import { isTag } from './shared'; export declare function hasAsyncOpcodes(): boolean; export declare function markOpcodeAsync(op: tagOp): void; export declare const opsForTag: Map>; export declare const tagsToRevalidate: Set; export declare const relatedTags: Map>; export declare function releaseOpArray(arr: Array): void; export declare const DEBUG_MERGED_CELLS: Set; export declare const DEBUG_CELLS: Set>; export declare function currentGlobalRevision(): number; export declare function bumpGlobalRevision(): number; export declare const cellsMap: WeakMap>>; export declare function getCells(): Cell[]; export declare function getMergedCells(): MergedCell[]; export declare function tracked(klass: any, key: string, descriptor?: PropertyDescriptor & { initializer?: () => any; }): void; export type AnyCell = Cell | MergedCell; export declare function isRendering(): boolean; export declare function setIsRendering(value: boolean): void; export declare class Cell { private __value; id: number; _revision: number; toHTML: () => string; _relatedObj?: object; _relatedKey?: string | number | symbol; [Symbol.toPrimitive](): T; _debugName?: string | undefined; [isTag]: boolean; constructor(value: T, debugName?: string); get _value(): T; set _value(v: T); get value(): T; set value(value: T); update(value: T): void; } export declare class LazyCell extends Cell { private __isResolved; private __lazyValue; private __fn; constructor(fn: () => T, debugName?: string); get _value(): T; set _value(v: T); } export declare function listDependentCells(cells: Array, cell: MergedCell): string; export declare function opsFor(cell: AnyCell): tagOp[]; export declare function relatedTagsForCell(cell: Cell): Set; /** * Synchronously run a cell's own opcodes and the opcodes of every formula that * depends on it. Intended for hosts (Ember's fine-grained sync) that update a * cell from INSIDE the current `syncDomSync` drain — at that point the drain * has already snapshotted its work list and its terminal `tagsToRevalidate` * clear would otherwise discard the just-dirtied cell, so its bound DOM (e.g. a * `{{this.salutation}}` text node) would never re-render this tick. This flushes * those opcodes immediately. The related-tag set for the cell is consumed * (deleted) the same way the normal drain consumes it, so a subsequent drain * does not double-execute. A single bad binding can't abort the flush, but its * error is surfaced to the host via the opcode-error reporter (NOT swallowed). */ export declare function flushCellOpcodes(cell: Cell | MergedCell): void; /** * Apply a cell update synchronously and deliver it to subscribers NOW. * * The drain-safe sibling of `Cell.update()`: a plain `update()` issued from * inside an active `syncDomSync` lands in `tagsToRevalidate` after the drain * snapshotted its work list, so the terminal clear silently drops it. This * helper instead: * - mutates `_value`/`_revision` directly, BYPASSING the host * `_cellUpdateDeferralHook` (a deferred apply would leave `_value` stale * while the caller immediately re-executes subscribers against it); * - removes the cell from `tagsToRevalidate` so an in-flight drain doesn't * double-execute it; * - flushes its opcodes + subscriber formulas via `flushCellOpcodes` under * `_isRendering` — REQUIRED: `MergedCell.value` only re-collects deps on * the tracking path, so flushing while not rendering would permanently * unsubscribe every re-executed formula. * * Used by `keyedSelector` key-cell flips and the recycle-mode holder swap in * the list control flow. No-op when the value is reference-equal. */ export declare function applyCellUpdateSync(cell: Cell, value: T): void; export declare function getTagId(): number; export declare function tagsFromRange(start: number, end?: number): Cell[]; export declare class MergedCell { fn: Fn | Function; toHTML: () => string; isConst: boolean; isDestroyed: boolean; id: number; [Symbol.toPrimitive](): any; _debugName?: string | undefined; relatedCells: Set | null; [isTag]: boolean; constructor(fn: Fn | Function, debugName?: string); destroy(): void; get value(): any; } export type tagOp = (...values: unknown[]) => Promise | void; export type OpcodeErrorReporter = (error: unknown, context: { tag: Cell | MergedCell; opcode: tagOp | null; }) => void; export declare function setOpcodeErrorReporter(reporter: OpcodeErrorReporter | null): void; export type CellUpdateDeferralHook = (cell: Cell, newValue: unknown) => boolean; export declare function setCellUpdateDeferralHook(hook: CellUpdateDeferralHook | null): void; export type DevtoolsCellNotifier = (cell: Cell, oldValue: unknown) => void; export declare function setDevtoolsCellNotifier(notifier: DevtoolsCellNotifier | null): void; /** * Apply a deferred cell update from the host's drain phase. * * Mirrors the synchronous body of `Cell.update` (mutate value + bump revision * when changed + enqueue for revalidation + schedule), but bypasses the * deferral-hook check so it can be called from inside the hook's queue * flusher without re-entering the hook (which would loop). * * The host owns the queue of `(cell, value)` pairs. This function exposes the * primitive that applies a single pair. Typical host pattern: * * setCellUpdateDeferralHook((cell, value) => { * hostQueue.push([cell, value]); * scheduleHostDrain(); * return true; * }); * * function drain() { * while (hostQueue.length) { * const [cell, value] = hostQueue.shift()!; * applyDeferredCellUpdate(cell, value); * } * } */ export declare function applyDeferredCellUpdate(cell: Cell, value: unknown): void; export declare function reportOpcodeError(e: any, tag: Cell | MergedCell): void; /** * Executes all opcodes for a tag. * * `awaitAsync = false` is the synchronous fast path with no Promise allocation. * `awaitAsync = true` preserves async opcode semantics and awaits marked async ops. */ export declare function executeTag(tag: Cell | MergedCell, awaitAsync: true): Promise; export declare function executeTag(tag: Cell | MergedCell, awaitAsync: false): void; export declare function executeTag(tag: Cell | MergedCell, awaitAsync?: boolean): Promise | void; export declare function executeTagSync(tag: Cell | MergedCell): void; export declare function lazyRawCellFor(obj: T, key: K, init?: () => T[K]): Cell; export declare function rawCellFor(obj: T, key: K): Cell; export declare function cellFor(obj: T, key: K, skipDefine?: boolean): Cell; type Fn = () => unknown; export declare function formula(fn: Function | Fn, debugName?: string): MergedCell; /** * `cached(fn)` — memoizing derivation. * * Like `formula(fn)` but records the last computed value and the set of tracked * cells observed during that computation. A subsequent `value` read returns the * cached value as long as none of those cells has bumped its `_revision` since * the last compute. When the observed deps are dirty, the underlying fn() is * re-executed exactly once, freshly collecting deps. * * Both `cached.value` (public) and the inner MergedCell's `value` (invoked by * `executeTag` during DOM sync) route through the same memoized path, so the * user getter runs at most once per dep-revision epoch even when the sync * pipeline re-executes the tag directly. * * The returned object is tag-like: it participates in GXT's tracker frames, so * a parent formula that reads `cached.value` still records a dependency on the * underlying cells (we re-add them to the ambient tracker on every read). */ export interface CachedCell { readonly value: T; readonly tag: MergedCell; invalidate(): void; [isTag]: true; } export declare function cached(fn: () => T, debugName?: string): CachedCell; /** * `cachedHelper(factory)` — identity-stable memoization for the `(hash)` and * `(array)` keyword helpers. * * Classic Glimmer memoizes `(hash)`/`(array)` (a `createComputeRef`-style stable * reference that only changes when an input changes). GXT compiles a `(hash)` / * `(array)` value into a getter (`() => $__hash({...})` / `() => $__array(...)`) * that the arg-access layer (`$_args`) RE-INVOKES on every read, so every read * produced a FRESH object/array identity. Reference-comparing consumers (Ember * child components, modifiers) then saw the arg as perpetually changed and * over-fired `didUpdateAttrs` / `didReceiveAttrs` even on unrelated re-renders. * * This wraps the helper factory so the produced identity is memoized: * - the value is computed once, capturing the tracked cells read during the * computation; * - subsequent reads return the SAME reference while those cells are unchanged * — and FOREVER when the factory read no tracked cell (e.g. `(hash)`, whose * properties are live getters, or a constant `(array 1 2)`); * - the reference is recomputed (yielding a fresh identity) only when a * captured cell actually changes (e.g. `(array this.x)` when `this.x` * changes); * - the captured deps are replayed into the ambient tracker on every read, so * a consumer formula still depends on the underlying cells and re-runs when * they change — value-correctness is preserved. * * Invalidation is gated on captured gxt-cell revisions AND a global-revision * fallback: a zero-dep capture is NOT treated as stable-forever (that pinned * stale values when the factory read classic/plain-object host props that don't * entangle a gxt Cell). Instead, when the global revision moves the memo * recomputes and a value-equality gate decides whether the served identity * actually changes — identity stays stable while the value is unchanged (no * over-invalidation) and turns over only on a real value change. The memo * deliberately does NOT subscribe a tag to the dep cells (no `bindAllCellsToTag`) * — the consumer's own tracker does the subscribing — which keeps it * allocation-light and leak-free (no tag lingers in a destroyed component's cell * subscriber sets). * * WHY NOT reuse `createCache`/`getValue` (core/glimmer/caching-primitives.ts)? * Empirically they cannot do this job: gxt's `createCache` is a PUSH/opcode-based, * lifecycle-owned memo — it installs a persistent `formula`+`opcodeFor` * subscription into each dep cell's `relatedTags` (released only by * `cache.destroy()`), `getValue` reads a `calcVersion` counter WITHOUT entangling * the caller, and recompute is driven ASYNCHRONOUSLY by the `scheduleRevalidate` * microtask drain. For this fire-and-forget arg getter (no destroy hook) that * would (1) LEAK — subscriptions pile up on long-lived parent cells across * keyed-each churn (probed: 50 caches over one cell → relatedTags 0→100, none * released); (2) fail to re-render `(array this.x)` consumers — `getValue` * entangles nothing (probed: 0 cells); (3) flip identity only after a flush, * breaking the synchronous-within-a-render-tick contract the identity tests * assert. This helper is the PULL-based, entangling, synchronous, leak-free * counterpart — faithful to the `@glimmer/tracking/primitives/cache` RFC for the * arg-getter use. Do NOT collapse it into `createCache` without first making * `createCache` itself RFC-faithful (a shared-primitive change, separate PR). * * Returns a getter so the existing arg-getter calling convention is preserved * (`$_args` invokes the arg as `args[key]()`). */ export declare function cachedHelper(factory: () => T): () => T; export declare function deepFnValue(fn: Function | Fn): any; export declare function cell(value: T, debugName?: string): Cell; export declare function registerLeafOwnersForFormula(f: MergedCell): void; export declare function materializeAbsentPathCell(child: Function): boolean; export declare function inNewTrackingFrame(callback: () => void): void; export declare function getTracker(): Set> | null; export declare function setTracker(tracker: Set | null): void; export {};