@repobit/dex-store-elements 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/eta/controller.ts"],"names":[],"mappings":"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","sourcesContent":["// src/eta/controller.ts\n\nimport {\n Task,\n type TaskStatus\n} from '@lit/task';\n\nimport type {\n ReactiveController,\n ReactiveControllerHost\n} from 'lit';\n\nimport { EtaMutationBatcher } from './batcher.js';\nimport { createEtaTemplateCache } from './cache.js';\nimport { createDefaultTemplateOperationFactory } from './factory.js';\nimport { EtaMutationObserver } from './observer.js';\nimport { EtaTemplateIndex } from './template-index.js';\nimport type {\n EtaMutationBatchSchedule,\n EtaMutationBatchScheduleOption,\n EtaTemplateCache,\n EtaTemplateControllerOptions,\n EtaTemplateHost,\n EtaTemplateIndexLike,\n TemplateOperation,\n TemplateOperationFactory\n} from './types.js';\n\n/**\n * Tracks a per-root observer+batcher+index triple so the controller\n * can manage them as a unit during construction, connection, and\n * teardown.\n */\ninterface ShadowRootBinding<TContext extends object> {\n readonly shadow : ShadowRoot;\n readonly observer: EtaMutationObserver;\n readonly batcher : EtaMutationBatcher;\n readonly index : EtaTemplateIndexLike<TContext>;\n}\n\n/**\n * Default context type for the Eta template subsystem.\n *\n * **This is a recommended context shape, NOT something the controller\n * builds automatically.** The controller used to spread a default\n * `$scope` / `$compute` context internally; the current `args` /\n * `context` API delegates that to the host. Hosts that want the\n * documented shape should construct the context themselves:\n *\n * ```ts\n * context: ([scope]) => ({\n * ...(scope ?? {}),\n * $scope : scope,\n * $compute: this.compute\n * })\n * ```\n *\n * Templates may reference any property of the scope as a top-level\n * binding.\n */\nexport type DefaultEtaContext = {\n readonly $scope? : object;\n readonly $compute?: object;\n} & Record<string, unknown>;\n\n/**\n * Status of the underlying `@lit/task`. Re-exported so consumers do\n * not need a direct `@lit/task` dependency to read it.\n */\nexport type EtaTemplateStatus = TaskStatus;\n\n/**\n * Throws `signal.reason` when the signal has been aborted. No-op\n * otherwise.\n *\n * Prefers the native `signal.throwIfAborted()` when available (most\n * modern browsers) and falls back to a manual throw so the helper\n * still works in environments that pre-date `AbortSignal`'s\n * `throwIfAborted` method (notably some jsdom versions and older\n * test runners).\n */\nfunction throwIfAborted(signal: AbortSignal): void {\n if (typeof signal.throwIfAborted === 'function') {\n signal.throwIfAborted();\n return;\n }\n\n if (signal.aborted) {\n throw (\n signal.reason ??\n new DOMException(\n 'The operation was aborted.',\n 'AbortError'\n )\n );\n }\n}\n\n/**\n * Reactive controller that owns the lifecycle of the Eta template\n * subsystem for a single scoped boundary.\n *\n * Responsibilities:\n *\n * - Owns the `@lit/task` that drives rendering.\n * - Builds the rendering context from the host-supplied `args` /\n * `context` callbacks.\n * - Owns the `MutationObserver` that feeds the index.\n * - Owns a single `EtaTemplateIndex`.\n * - Iterates operations and calls `execute(context)` on each.\n * - Participates in `host.trackLocalWork` so render passes count\n * toward `BdNodeElement.updateComplete`.\n *\n * It does NOT:\n *\n * - Walk the DOM during render (the index is the source of truth).\n * - Discover templates during render (the factory runs once on\n * `add`).\n * - Know about products, options, prices, or transitions.\n *\n * **Skip-render semantics.** When `enabled(args)` returns `false`\n * or `context(args)` returns `undefined`, the controller skips the\n * render pass and returns `undefined`. Previously rendered DOM\n * output stays as-is — the controller does not blank the DOM or\n * remove rendered nodes. Hosts that want blank/fallback output\n * should either return a context that causes templates to render\n * blank values, or publish a fallback scope (e.g. an empty object\n * or a sentinel value) before requesting a render. This behaviour\n * is intentional: it lets the controller be safely driven from\n * the host's reactive state without flickering on intermediate\n * states where the scope is temporarily absent.\n */\nexport class EtaTemplateController<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = DefaultEtaContext\n> implements ReactiveController\n{\n readonly index: EtaTemplateIndexLike<TContext>;\n\n /**\n * The controller's internal template cache.\n *\n * **Internal — true private field.** The `#` ECMAScript private\n * field cannot be observed from outside the class at all\n * (neither via property lookup nor via casting). The TypeScript\n * `private` modifier above would be enough for the type checker\n * but JS property access would still find the field; using\n * `#cache` closes that gap at runtime too.\n *\n * **Per-controller isolation.** The controller always builds its\n * own cache; there is no public option to inject a different\n * one. The cache is shared with the controller's private\n * factory so `clearCache()` drains whatever the factory uses to\n * memoize compiled templates.\n *\n * Hosts invalidate the controller's own cache via\n * `controller.clearCache()`. The `clearCache()` call only clears\n * this controller's cache; no other state is affected.\n */\n readonly #cache: EtaTemplateCache;\n\n private readonly host: EtaTemplateHost &\n ReactiveControllerHost;\n private readonly argsFn : () => TArgs;\n private readonly enabledFn:\n | ((args: TArgs) => boolean)\n | undefined;\n private readonly contextFn: (args: TArgs) =>\n | TContext\n | undefined;\n private readonly onRenderError:\n | ((\n error: unknown,\n operation: TemplateOperation<TContext>\n ) => void)\n | undefined;\n private readonly mutationBatchScheduleOption:\n EtaMutationBatchScheduleOption;\n /**\n * Cached boundary-element predicate, captured once in the\n * constructor and forwarded to every shadow-root index through\n * `_shadowBoundaryPredicate`. The controller is the single\n * source of truth for the predicate — the host supplies it via\n * `options.isBoundaryElement` and the controller threads the\n * SAME predicate to every observed root (light + shadow).\n *\n * Captured up-front so `_shadowBoundaryPredicate` does not need\n * to reach back into `this.options` at every shadow-binding\n * creation. `undefined` when the host did not supply a\n * predicate — preserves the pre-fix behaviour for those hosts.\n */\n private readonly _boundaryElementPredicate:\n | ((element: Element) => boolean)\n | undefined;\n /**\n * Set of every shadow root that has already been wired into a\n * shadow binding.\n *\n * `WeakSet` because shadow roots can be GC'd when their host\n * disconnects — we never want to keep them alive through this\n * field. `_addShadowBinding` is the single writer; both\n * `_connectShadowRoots` (initial discovery) and\n * `_discoverShadowRootsFor` (light-DOM mutation-driven\n * discovery) gate registration through this set so the same\n * shadow root is never wired twice.\n */\n private readonly _seenShadows: WeakSet<ShadowRoot> =\n new WeakSet();\n private readonly observer: EtaMutationObserver;\n private readonly batcher : EtaMutationBatcher;\n private readonly task : Task<TArgs, TContext | undefined>;\n private readonly factory:\n TemplateOperationFactory<TContext>;\n /**\n * Per-shadow-root observer + batcher + index triples. Empty\n * when the host does not enumerate any shadow roots. Each\n * triple is wired in `hostConnected` and torn down in\n * `hostDisconnected` alongside the light-DOM observer.\n */\n private shadowBindings: ShadowRootBinding<TContext>[] = [];\n\n constructor(\n host: EtaTemplateHost & ReactiveControllerHost,\n options: EtaTemplateControllerOptions<TArgs, TContext>\n ) {\n this.host = host;\n\n // Runtime guard — `EtaTemplateHost.root` is non-optional in\n // the type system, but a host implementing the interface\n // could still hand back `undefined` at runtime. The index\n // uses `host.root` as its discovery boundary and silently\n // indexing the whole document if `root` is missing would\n // produce very confusing behaviour. Fail loudly instead.\n if (!host.root) {\n throw new Error(\n 'EtaTemplateController: host.root is required. ' +\n 'The host element must expose a `root` getter that ' +\n 'returns the Element the controller should index.'\n );\n }\n\n this.argsFn = options.args;\n this.enabledFn = options.enabled;\n this.contextFn = options.context;\n this.onRenderError = options.onRenderError;\n this.mutationBatchScheduleOption =\n options.mutationBatchSchedule ?? 'animation-frame';\n this._boundaryElementPredicate =\n options.isBoundaryElement;\n\n this.#cache = createEtaTemplateCache();\n\n // The controller owns its factory. There is no public option\n // to inject one — the factory is a private collaborator like\n // the cache. The controller builds a default\n // `DefaultTemplateOperationFactory` that shares its `#cache`,\n // so the cache and the factory stay in lockstep and\n // `clearCache()` drains the factory's compiled-template store\n // as well.\n const normalizedAttributeNames =\n this._observerAttributeNames(options);\n\n this._normalizedAttributeNames = normalizedAttributeNames;\n\n this.factory =\n createDefaultTemplateOperationFactory<TContext>(\n this.#cache,\n this._factoryOptions(options, normalizedAttributeNames)\n );\n\n this.index =\n options.createIndex?.(this.factory) ??\n new EtaTemplateIndex<TContext>(this.factory, {\n root : host.root,\n isBoundaryElement: options.isBoundaryElement\n });\n\n // Round-6 mutation-batching layer. The batcher sits between\n // the observer and the index: the observer forwards raw\n // MutationRecord batches via `onRecords`, the batcher\n // accumulates them, compacts them per the design rules, and\n // flushes the compacted mutations to the index on the\n // configured schedule (`animation-frame` by default).\n //\n // The `onFlush` callback kicks a render when the flush was\n // dirty — empty flushes never cause a render. We deliberately\n // call `task.run()` (not `requestRender()`) because the\n // batcher is the source of truth for \"dirty\" and we want the\n // Task's scheduling semantics (abort, dedupe, re-entry)\n // rather than the manual `requestRender()` path.\n this.batcher = new EtaMutationBatcher(this.index, {\n schedule: () => this.mutationBatchSchedule,\n onFlush : (dirty) => {\n if (dirty) {\n this.task.run();\n }\n }\n });\n\n this.observer = new EtaMutationObserver({\n attributeNames: normalizedAttributeNames,\n onRecords : (records) => {\n this.batcher.enqueue(records);\n // Discovery hook: a freshly-added element may carry a\n // shadow root that did not exist when `_connectShadowRoots`\n // ran. The hook is idempotent (gated by `_seenShadows`)\n // and only walks `childList` records so attribute / text\n // mutations stay cheap.\n this._discoverShadowRootsFor(records);\n }\n });\n\n this.task = new Task<TArgs, TContext | undefined>(host, {\n autoRun : true,\n argsEqual: options.argsEqual,\n task : async (args, { signal }) => {\n const work = this._runRenderPass(args, signal);\n\n const tracked =\n this.host.trackLocalWork?.(this, work) ?? work;\n\n return tracked;\n },\n args: () => this.argsFn()\n });\n\n host.addController(this);\n }\n\n /**\n * Invalidates every compiled template this controller owns.\n *\n * Drops all entries from the controller's internal cache. The\n * next render that touches a previously-compiled template will\n * re-compile it through the cache.\n *\n * **Per-controller scope.** `clearCache()` only clears this\n * controller's own cache. There is no public cache injection\n * option; hosts that want shared templates across controllers\n * should compile and store those templates at the application\n * layer instead of sharing the controller's internal Eta cache.\n *\n * Safe to call at any time, including between renders.\n */\n clearCache(): void {\n this.#cache.clear();\n }\n\n /**\n * Status of the underlying Task.\n *\n * Exposed so consumers (e.g. tests) can assert progress without\n * reaching into the `@lit/task` surface.\n */\n get status(): EtaTemplateStatus {\n return this.task.status;\n }\n\n /**\n * Latest context produced by a successful render pass.\n */\n get value(): TContext | undefined {\n return this.task.value as TContext | undefined;\n }\n\n /**\n * Most recent render error, or `undefined` when the last render\n * succeeded.\n *\n * Mirrors `@lit/task`'s `Task.error` getter. When a host passes\n * `onRenderError`, the controller swallows render errors and\n * records them here — read this getter to surface them in UI\n * state (e.g. an error banner). When `onRenderError` is omitted,\n * render errors reject `updateComplete`; both this getter and\n * the rejected promise expose the same error value.\n */\n get error(): unknown {\n return this.task.error;\n }\n\n /**\n * Promise that resolves when the current render pass completes.\n *\n * Mirrors `BdNodeElement.updateComplete`'s promise semantics for\n * the Eta subsystem. When the host supplies `trackLocalWork`,\n * this promise is tracked under the controller's instance so the\n * host's `updateComplete` waits for the render to settle before\n * resolving.\n *\n * Useful for tests and for downstream consumers that need to\n * observe render completion without poking at `@lit/task`.\n */\n get updateComplete(): Promise<TContext | undefined> {\n return this.task.taskComplete;\n }\n\n /**\n * Requests a fresh render pass through the underlying `@lit/task`.\n *\n * Most consumers never need to call this: the controller re-runs\n * whenever the host signals an update, the index mutates, or the\n * Task args change. The hook is provided for tests and for\n * consumers that want to force a re-render after an external cache\n * invalidation, scope mutation outside the normal reactive flow,\n * or other event that the Task's `argsEqual` check might miss.\n *\n * The name reflects what actually happens: `Task.run()` schedules\n * the task through Task's own scheduling semantics (which may\n * batch, dedupe, or abort against the current run id) rather than\n * synchronously executing the render loop.\n *\n * **Returns a promise.** The promise resolves to the latest\n * context (`TContext | undefined`) produced by the render pass\n * that `Task.run()` scheduled. Awaiting it lets tests and\n * downstream consumers observe render completion without\n * reaching into `@lit/task`. The promise resolves with the\n * controller's `value`, which is `undefined` when the render was\n * skipped (because `enabled()` returned `false` or `context()`\n * returned `undefined`).\n *\n * Because `Task.run()` returns `Promise<void>` in the installed\n * `@lit/task` version, this method kicks the run and then waits\n * on `taskComplete` (the same promise exposed as\n * `controller.updateComplete`) before reading `this.value`.\n * `Task.run()` resolves when the run is *scheduled*, not when it\n * *completes*; waiting on `taskComplete` is the only way to\n * guarantee the returned context reflects the render that this\n * call kicked off. The same `taskComplete` is also what the host\n * `updateComplete` waits on (through `trackLocalWork`), so\n * awaiting this method aligns the caller with the host's\n * subtree-completion promise.\n */\n async requestRender(): Promise<TContext | undefined> {\n // Kick the Task run. The returned promise resolves when the\n // run is scheduled, NOT when the render completes.\n this.task.run();\n\n // Wait for the render to actually finish so `this.value`\n // reflects this render's context rather than the previous\n // run's value. `taskComplete` is the same promise exposed\n // through the controller's `updateComplete` getter.\n await this.task.taskComplete;\n\n return this.value;\n }\n\n /**\n * Flushes the mutation batcher synchronously.\n *\n * Forwards any pending `MutationRecord`s to the index through\n * the batcher's compaction pass and returns whether the flush\n * made the index dirty. Returns `false` when there was nothing\n * to flush — an empty batch is a no-op.\n *\n * **Use cases:**\n *\n * - Hosts that drive the controller with\n * `mutationBatchSchedule: 'manual'` and want to apply a\n * known decoration phase's mutations before reading the\n * index. Pair with `flushAndRender()` when a render is\n * needed afterwards.\n * - Tests that want a deterministic, intent-revealing way to\n * drain the batcher without going through the observer's\n * scheduled tick. This is the recommended way to assert on\n * the post-flush index state from test bodies.\n * - Hot-path code that knows a render is about to be requested\n * via `requestRender()` and wants to guarantee the render\n * sees the latest mutations (the render task body already\n * calls `batcher.flush()` before executing, so this is\n * rarely needed in practice — but useful when callers want\n * the flush to happen synchronously at the call site rather\n * than inside the Task).\n *\n * Does NOT call `onFlush` on the batcher, so the flush never\n * re-enters `task.run()` on its own. Call `flushAndRender()`\n * when a render should follow the flush.\n *\n * Returns the batcher's `flush()` return value — `true` when\n * at least one compacted mutation made the index dirty,\n * `false` otherwise. The dirty flag mirrors the batcher's\n * dirty semantics (OR-accumulated across the four compaction\n * passes — see `EtaMutationBatcher.flush`).\n */\n /**\n * Flushes every observer queue into its matching batcher, then\n * flushes every batcher into its matching index.\n *\n * Forwards any pending `MutationRecord`s to the index through\n * the batcher's compaction pass and returns whether the flush\n * made the index dirty. Returns `false` when there was nothing\n * to flush — an empty batch is a no-op.\n *\n * **Manual schedule mode.** When `mutationBatchSchedule` is\n * `'manual'`, the batcher never auto-flushes; the host must\n * drain the queue itself. This method honours that contract\n * across every observed root (light + every shadow binding).\n *\n * **Drain order:**\n *\n * 1. Light observer → light batcher\n * (`observer.flushPendingRecords({ notify: false })`).\n * 2. Every shadow observer → its shadow batcher\n * (`binding.observer.flushPendingRecords({ notify: false })`).\n * Drain the observer BEFORE the batcher so records the\n * browser has produced but not yet delivered reach the\n * batcher before the synchronous flush.\n * 3. Light batcher → light index (`batcher.flush()`).\n * 4. Every shadow batcher → its shadow index\n * (`binding.batcher.flush()`), OR-accumulated into the\n * combined dirty flag.\n *\n * **Use cases:**\n *\n * - Hosts that drive the controller with\n * `mutationBatchSchedule: 'manual'` and want to apply a\n * known decoration phase's mutations before reading the\n * index. Pair with `flushAndRender()` when a render is\n * needed afterwards.\n * - Tests that want a deterministic, intent-revealing way to\n * drain the batcher without going through the observer's\n * scheduled tick. This is the recommended way to assert on\n * the post-flush index state from test bodies.\n * - Hot-path code that knows a render is about to be requested\n * via `requestRender()` and wants to guarantee the render\n * sees the latest mutations (the render task body already\n * calls `batcher.flush()` before executing, so this is\n * rarely needed in practice — but useful when callers want\n * the flush to happen synchronously at the call site rather\n * than inside the Task).\n *\n * Does NOT call `onFlush` on any batcher, so the flush never\n * re-enters `task.run()` on its own. Call `flushAndRender()`\n * when a render should follow the flush.\n *\n * Returns the combined dirty flag — `true` when at least one\n * batcher (light or shadow) made its index dirty, `false`\n * otherwise. The dirty flag mirrors each batcher's dirty\n * semantics (OR-accumulated across the four compaction passes\n * — see `EtaMutationBatcher.flush`).\n *\n * Idempotent — calling on an empty queue/batch still returns\n * `false` and never triggers a render. The `notify: false` flag\n * on every `flushPendingRecords` call keeps the observer's\n * own `onChange` callback dormant during a manual flush so a\n * host that drains and then calls `requestRender()` does not\n * double-schedule.\n */\n flushMutations(): boolean {\n this.observer.flushPendingRecords({ notify: false });\n\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n }\n\n let dirty = this.batcher.flush();\n\n for (const binding of this.shadowBindings) {\n if (binding.batcher.flush()) {\n dirty = true;\n }\n }\n\n return dirty;\n }\n\n /**\n * Flushes the mutation batcher and requests a fresh render\n * pass when the flush was dirty.\n *\n * Convenience wrapper for the \"manual\" schedule mode — hosts\n * that opt into `mutationBatchSchedule: 'manual'` must drain\n * the batcher themselves and then trigger a render. This\n * method collapses both steps into one call:\n *\n * ```ts\n * decorateBlock(block);\n * await decorateIcons(block);\n * eta.flushAndRender();\n * ```\n *\n * When the flush was a no-op (`false` return from the\n * batcher) the render is skipped — there is no point re-running\n * the Task against an unchanged index. When the flush was\n * dirty, the call forwards to `requestRender()` so the Task\n * runs with the same scheduling semantics as any other\n * render-triggering path.\n *\n * Does NOT wait for the render to complete; callers that need\n * to await the resulting context can follow up with\n * `await controller.updateComplete` (or\n * `await controller.requestRender()`). The return value of\n * the underlying `requestRender()` is intentionally discarded\n * to keep the API synchronous and easy to call from\n * imperative decoration flows.\n */\n flushAndRender(): void {\n const dirty = this.flushMutations();\n\n if (dirty) {\n // `requestRender()` returns a promise that resolves once\n // the Task completes; we intentionally discard it so the\n // host can call `flushAndRender()` from synchronous\n // decoration code. Callers that need to await the render\n // can use `await controller.updateComplete` afterwards.\n void this.requestRender();\n }\n }\n\n /**\n * Reads the current batch schedule as resolved by the\n * controller. Useful for diagnostics and for tests that want\n * to assert on the controller's effective schedule without\n * reaching into the private `batcher` field.\n */\n get mutationBatchSchedule(): EtaMutationBatchSchedule {\n return this._currentMutationBatchSchedule();\n }\n\n private _currentMutationBatchSchedule(): EtaMutationBatchSchedule {\n const schedule = this.mutationBatchScheduleOption;\n\n if (typeof schedule === 'function') {\n return schedule() ?? 'animation-frame';\n }\n\n return schedule ?? 'animation-frame';\n }\n\n /**\n * Runs a single render pass.\n *\n * The pass is gated by `enabled(args)` and `context(args)`:\n *\n * - `enabled` returning `false` skips the loop and returns\n * `undefined` (no context, no render).\n * - `context` returning `undefined` does the same.\n *\n * When both gates pass, the render loop runs in two phases:\n *\n * 1. **Pre-flush.** The mutation batcher applies any pending\n * `MutationRecord` batches to the index via\n * `observer.flushPendingRecords({ notify: false })` drains\n * records the browser has produced but not delivered yet into\n * the batcher, then `batcher.flush()` applies the compacted\n * batch so the index and source registry are accurate before\n * the render reads operations. Without this step, a render\n * triggered by a Task args change or a manual `requestRender()`\n * call could run against a stale index whenever the observer or\n * batcher had not yet flushed a pending burst of DOM changes.\n *\n * `batcher.flush()` cancels any pending scheduled flush\n * AND applies the current batch synchronously; it does NOT\n * call `onFlush`. That detail is important — calling\n * `onFlush` from inside an in-flight render pass would\n * re-enter `task.run()` and create an infinite render\n * loop. The Task's auto-run and the batcher's onFlush\n * callback are kept on separate scheduling paths for this\n * reason.\n *\n * The round-6 batcher sits between the observer and the index,\n * so both layers participate in the pre-flush: the observer\n * drains queued records into the batcher, and the batcher\n * drains its compacted mutations into the index.\n * 2. **Render.** The render loop runs in two phases so each\n * phase can suspend only the observer that would otherwise\n * treat the phase's self-induced mutations as fresh external\n * mutations:\n *\n * a. **Light-DOM phase.** Iterates `this.index` inside\n * `observer.suspendSync(...)` so DOM writes performed by\n * light-DOM operations are not fed back as fresh\n * mutations. The `finally` block drains whatever's queued\n * after the loop — at this point only self-induced\n * records should remain.\n * b. **Shadow phase.** Iterates each binding's `index`\n * inside `binding.observer.suspendSync(...)`. Wrapping\n * the shadow phase in `this.observer.suspendSync` would\n * discard legitimate external light-DOM mutations queued\n * in the meantime via `takeRecords`, and would not\n * actually suppress shadow observer feedback (the light\n * observer cannot see shadow-tree writes anyway). Each\n * binding therefore suspends its OWN observer so writes\n * inside its shadow root are not fed back as fresh\n * mutations.\n *\n * The pre-flush step above drained every shadow observer\n * BEFORE either phase, so the only records the shadow\n * observer will see during the shadow phase are writes from\n * the shadow operations themselves — exactly what each\n * shadow observer's `suspendSync` is there to suppress.\n *\n * Per-operation errors are routed to `onRenderError` when\n * supplied; otherwise they propagate to the Task and abort the\n * render pass.\n */\n private async _runRenderPass(\n args: TArgs,\n signal: AbortSignal\n ): Promise<TContext | undefined> {\n if (this.enabledFn && !this.enabledFn(args)) {\n return undefined;\n }\n\n const context = this.contextFn(args);\n\n if (context === undefined) {\n return undefined;\n }\n\n // Pre-flush: drain queued observer records into the batcher,\n // then apply any pending batch mutations to the index BEFORE\n // building the render loop. Without this step, the render\n // would run against a stale index whenever a burst of DOM\n // changes has been produced by the observer or accumulated by\n // the batcher but not yet flushed.\n //\n // `batcher.flush()` cancels any pending scheduled flush and\n // applies the current batch synchronously. It does NOT call\n // `onFlush` (no re-entrant `task.run()` from inside an\n // in-flight render pass — see the batcher's flush vs\n // flushAndNotify split).\n //\n // Draining the observer first matters because suspendSync's\n // final takeRecords() intentionally drops self-induced render\n // records. External records queued before the render must reach\n // the batcher before that drain happens. The same applies to\n // every shadow observer — drain those too so render passes\n // don't run against stale shadow indices.\n this.observer.flushPendingRecords({ notify: false });\n this.batcher.flush();\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n binding.batcher.flush();\n }\n\n this.observer.suspendSync(() => {\n for (const op of this.index) {\n if (signal.aborted) {\n break;\n }\n\n try {\n op.execute(context);\n } catch (error) {\n if (this.onRenderError) {\n this.onRenderError(error, op);\n } else {\n throw error;\n }\n }\n }\n });\n\n // Shadow operations run under their OWN observer suspension.\n // The render loop iterates light-DOM ops and shadow-binding\n // ops in separate phases so each phase can suspend only the\n // observer that would otherwise treat the phase's self-induced\n // mutations as fresh external mutations.\n //\n // Why two phases:\n //\n // - The light-DOM observer stays alive while a shadow\n // operation writes its rendered value into a shadow node —\n // that observer cannot see the write (different root), so\n // it cannot feed back into the light index. Wrapping the\n // shadow phase in `this.observer.suspendSync` would be a\n // no-op (and would discard legitimate external light-DOM\n // mutations queued in the meantime via `takeRecords`).\n // - The shadow observer for a binding sees only mutations\n // inside its own shadow root. Shadow operations writing\n // into that root must run under that observer's\n // suspension so the writes are not fed back as fresh\n // shadow mutations; otherwise the shadow batcher would\n // re-enqueue them on the next flush and the operation\n // would rebuild against no template and get dropped (the\n // shadow source registry has already persisted the\n // rendered plain string).\n //\n // Each phase reuses the existing abort-signal check and the\n // `onRenderError` routing so a failing operation only fails\n // its own phase.\n for (const binding of this.shadowBindings) {\n binding.observer.suspendSync(() => {\n for (const op of binding.index) {\n if (signal.aborted) {\n break;\n }\n\n try {\n op.execute(context);\n } catch (error) {\n if (this.onRenderError) {\n this.onRenderError(error, op);\n } else {\n throw error;\n }\n }\n }\n });\n }\n\n // Surface aborts that fired during the render loop as Task\n // errors so the task moves to the `ABORTED` status instead of\n // returning a stale context. The render loop itself is\n // synchronous, but external callers (e.g. host disconnection)\n // can flip the signal between the loop's `break` check and the\n // point where we return.\n throwIfAborted(signal);\n\n return context;\n }\n\n /**\n * Returns the effective attribute-name list that both the\n * observer and the factory must agree on.\n *\n * The single source of truth avoids contradictory\n * configurations where the observer is told to watch every\n * attribute while the factory is told to filter to a specific\n * list (or vice versa). The previous behaviour dropped records\n * on the floor silently when `observeAllAttributes: true` was\n * combined with a non-empty `attributeNames`.\n *\n * Resolution order:\n *\n * 1. `observeAllAttributes: true` → `undefined` (watch\n * everything).\n * 2. Non-empty `attributeNames` → that exact list.\n * 3. Anything else → `undefined` (watch everything).\n */\n private _observerAttributeNames(\n options: EtaTemplateControllerOptions<\n TArgs,\n TContext\n >\n ): readonly string[] | undefined {\n if (options.observeAllAttributes === true) {\n return undefined;\n }\n\n if (\n options.attributeNames &&\n options.attributeNames.length > 0\n ) {\n return options.attributeNames;\n }\n\n return undefined;\n }\n\n /**\n * Builds the per-factory option object passed to the default\n * factory constructor.\n *\n * `observeAllAttributes: true` (or no allowlist supplied) means\n * the factory should observe every attribute, so we omit\n * `attributeNames` entirely and let the factory default to\n * \"observe all\".\n *\n * When the controller was constructed with an explicit\n * `attributeNames` allowlist, we forward the **normalized** list\n * (computed by `_observerAttributeNames`) so the factory and the\n * observer agree on the same allowlist. Using the raw\n * `options.attributeNames` here would let `observeAllAttributes:\n * true` coexist with a non-empty list, contradicting the\n * observer's \"watch everything\" mode.\n */\n private _factoryOptions(\n _options: EtaTemplateControllerOptions<\n TArgs,\n TContext\n >,\n normalizedAttributeNames: readonly string[] | undefined\n ): {\n readonly attributeNames?: readonly string[];\n } {\n if (normalizedAttributeNames === undefined) {\n return {};\n }\n\n return { attributeNames: normalizedAttributeNames };\n }\n\n hostConnected(): void {\n // Initialize after the host is connected so the light DOM is available.\n // Nested boundaries are detected by the configured isBoundaryElement predicate.\n this.index.initialize(this.host.root);\n\n this.observer.observe(this.host.root);\n\n // Shadow roots are wired after the light-DOM observer so a\n // render pass covers both trees in stable order. The host's\n // `shadowRoots` callback runs once per connect; closed roots\n // are skipped with a one-time console.warn.\n this._connectShadowRoots();\n }\n\n /**\n * Discovers every open shadow root attached to a descendant of\n * the host's light subtree and wires one observer + batcher +\n * index triple per root.\n *\n * `MutationObserver` is scoped to a single root: an observer\n * attached to the host's light root does NOT see mutations\n * inside any nested shadow root. To render templates into\n * shadow trees the controller must therefore attach a separate\n * observer per shadow root. The controller discovers those roots\n * itself — walking the host's light subtree and reading\n * `element.shadowRoot` — so hosts do not have to plumb the\n * list through.\n *\n * Closed shadow roots are unreachable from outside (the\n * `shadowRoot` getter returns `null` for closed mode), so they\n * are silently skipped. Hosts that want to render into a\n * closed root should attach a controller from inside that root\n * instead.\n *\n * Idempotent — re-running on a second `hostConnected()` first\n * tears down any existing bindings, then rebuilds them.\n */\n private _connectShadowRoots(): void {\n if (this.shadowBindings.length > 0) {\n this._disconnectShadowRoots();\n }\n\n const root = this.host.root;\n\n if (root === undefined) {\n return;\n }\n\n // Iterative DFS over both the light DOM and any attached open\n // shadow roots. Each step pops a node from the stack; for\n // light-DOM elements we descend into `children` AND follow\n // `shadowRoot` so a shadow tree nested inside a child is\n // discovered. For shadow roots we descend into the root's\n // own children (which are the shadow-tree's top-level\n // nodes). Iterative (not recursive) so deeply nested\n // shadow trees cannot blow the stack.\n const stack: Node[] = [root];\n\n while (stack.length > 0) {\n const node = stack.pop()!;\n\n if (node instanceof ShadowRoot) {\n // Register the shadow root through the shared helper —\n // it gates against `_seenShadows` so the same shadow root\n // is never wired twice (the same guard also catches a\n // re-entrant `_addShadowBinding` from a nested element's\n // own upgrade path).\n this._addShadowBinding(node);\n // Descend into the shadow tree's top-level children.\n for (const child of Array.from(node.children)) {\n stack.push(child);\n }\n continue;\n }\n\n if (node instanceof Element) {\n // Descend into light-DOM children first.\n for (const child of Array.from(node.children)) {\n stack.push(child);\n }\n // Then follow any attached open shadow root. Order\n // matters for stable enumeration — children first,\n // then the shadow — so the outer widget's own shadow\n // tree appears before an inner widget's shadow tree.\n if (node.shadowRoot) {\n stack.push(node.shadowRoot);\n }\n }\n }\n }\n\n /**\n * Registers a single shadow root with the controller — builds\n * one observer + batcher + index triple per root and wires it\n * into the light-DOM observer's discovery callback.\n *\n * Gates registration through `_seenShadows` so the same shadow\n * root is never wired twice. Closed roots are skipped\n * silently (their `shadowRoot` getter returns `null` so the\n * caller should not pass them in).\n */\n private _addShadowBinding(shadow: ShadowRoot): void {\n if (this._seenShadows.has(shadow)) {\n return;\n }\n\n this._seenShadows.add(shadow);\n\n const index = new EtaTemplateIndex<TContext>(this.factory, {\n root: shadow,\n isBoundaryElement:\n this._shadowBoundaryPredicate(shadow)\n });\n\n const batcher = new EtaMutationBatcher(index, {\n schedule: () => this.mutationBatchSchedule,\n onFlush : (dirty) => {\n if (dirty) {\n this.task.run();\n }\n }\n });\n\n const observer = new EtaMutationObserver({\n attributeNames: this._normalizedAttributeNames,\n onRecords : (records) => {\n batcher.enqueue(records);\n }\n });\n\n index.initialize(shadow);\n observer.observe(shadow);\n\n this.shadowBindings.push({\n shadow,\n observer,\n batcher,\n index\n });\n }\n\n /**\n * Reacts to a light-DOM `MutationRecord` batch by registering\n * any newly-discovered open shadow roots.\n *\n * Lit `until` directives, `repeat` expansions, and custom\n * element upgrades can attach shadow roots after the\n * controller's `hostConnected()` pass has already walked the\n * subtree. The light-DOM observer's callback fires for every\n * mutation batch — we hook it here so a newly-inserted element\n * that carries an open shadow root gets wired into the\n * controller on the next batcher tick.\n *\n * Idempotent — `_addShadowBinding` gates against\n * `_seenShadows`, so re-running the discovery walk on the same\n * records (or running it during a `suspendSync` of the light\n * observer) is harmless.\n *\n * Only `childList` records matter: an `add` of an element is\n * what can carry a fresh shadow root, and skipping text /\n * attribute records keeps the discovery walk cheap.\n */\n private _discoverShadowRootsFor(\n records: readonly MutationRecord[]\n ): void {\n for (const record of records) {\n if (record.type !== 'childList') {\n continue;\n }\n\n for (const added of Array.from(record.addedNodes)) {\n this._walkForShadows(added);\n }\n }\n }\n\n /**\n * Iterative DFS over `node`'s subtree (light children + open\n * shadow roots) that registers every shadow root it encounters\n * via `_addShadowBinding`.\n *\n * Mirrors the discovery shape used inside `_connectShadowRoots`\n * but operates on a single subtree rooted at `node` rather\n * than the full host subtree. Skips detached roots and any\n * element/shadow already known to the controller — the\n * `_seenShadows` gate inside `_addShadowBinding` short-circuits\n * the second registration.\n */\n private _walkForShadows(node: Node): void {\n const stack: Node[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop()!;\n\n if (current instanceof ShadowRoot) {\n this._addShadowBinding(current);\n // Descend into the shadow tree's top-level children —\n // a nested host's own shadow must also be picked up.\n for (const child of Array.from(current.children)) {\n stack.push(child);\n }\n continue;\n }\n\n if (current instanceof Element) {\n for (const child of Array.from(current.children)) {\n stack.push(child);\n }\n\n const shadow = current.shadowRoot;\n\n // `shadowRoot` is `null` for closed-mode and for\n // elements that have not yet created a shadow root. Open\n // roots are reachable via the getter and have a\n // non-null return — `_addShadowBinding` then gates\n // against `_seenShadows`.\n if (shadow !== null) {\n stack.push(shadow);\n }\n }\n }\n }\n\n /**\n * Tears down every per-shadow-root observer, batcher, and index.\n * Mirrors the light-DOM teardown order in `hostDisconnected()`\n * so source-registry preservation behaves the same way on\n * shadow roots as it does on the light tree.\n *\n * Clears `_seenShadows` so a subsequent `hostConnected()` can\n * re-discover every shadow root from scratch — observers do\n * not survive disconnect, so previously-seen shadow roots are\n * no longer wired and should be re-discovered.\n */\n private _disconnectShadowRoots(): void {\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n binding.batcher.flush();\n binding.observer.disconnect();\n binding.index.disposeAll();\n }\n\n this.shadowBindings = [];\n\n // Rebuild the seen-set to match the cleared bindings list.\n // `WeakSet` cannot be cleared so we reassign the field —\n // any other code holding a reference to the previous set\n // would still see stale entries, but the controller is the\n // sole writer and the only other read site is\n // `_addShadowBinding`, which always reads the latest field\n // value. The `unknown` cast is needed because the field is\n // declared `private readonly` — the same pattern is used by\n // the existing `WeakMap` / `WeakSet` resets in\n // `EtaTemplateIndex.clear()`.\n (\n this as unknown as { _seenShadows: WeakSet<ShadowRoot> }\n )._seenShadows = new WeakSet();\n }\n\n /**\n * Returns the boundary predicate for a shadow-root index. The\n * controller currently reuses the host's\n * `isBoundaryElement` rule for every observed root — the host\n * option is the single source of truth. This helper exists so\n * per-root overrides can be added later without churning the\n * `_connectShadowRoots` call site.\n *\n * Returns `undefined` when the host did not supply an\n * `isBoundaryElement` predicate — preserves the pre-fix\n * behaviour for those hosts (the shadow index then has no\n * boundary rule and falls back to its own ownership checks).\n */\n private _shadowBoundaryPredicate(\n _shadow: ShadowRoot\n ): ((element: Element) => boolean) | undefined {\n return this._boundaryElementPredicate;\n }\n\n /**\n * Cached normalized attribute name list. The controller\n * computes this once in the constructor and reuses it for both\n * the light-DOM observer and every shadow observer so a single\n * allowlist applies across every observed root.\n */\n private readonly _normalizedAttributeNames:\n | readonly string[]\n | undefined;\n\n hostDisconnected(): void {\n // Round-6 teardown order. The order matters for source-\n // registry preservation:\n //\n // 1. `observer.flushPendingRecords({ notify: false })` —\n // drains any records the browser has already produced\n // for the underlying `MutationObserver` and forwards\n // them through `onRecords` into the batcher. This is\n // the round-6 equivalent of round 5's\n // `observer.disconnect({ flush: true, notify: false })`:\n // the batcher is now the destination, so we drain into\n // the batcher first and let the regular disconnect path\n // skip the flush. `notify: false` avoids scheduling a\n // render during teardown.\n // 2. `batcher.flush()` — applies the accumulated batch\n // synchronously so the index / source registry are\n // accurate through the next `disposeAll()`. Without\n // this step, an external mutation that landed just\n // before disconnect would survive only inside the\n // batcher's pending batch and be discarded together\n // with the batch on the next `flush()` (which won't\n // run because the observer is no longer attached).\n // 3. `observer.disconnect()` — stops the underlying\n // `MutationObserver` from delivering future records.\n // Defaults to drain-only (`flush: false`) because step 1\n // already forwarded the queue; the observer's `finally`\n // `takeRecords()` is defensive.\n // 4. `index.disposeAll()` — clears operations while\n // preserving the source registry for the next reconnect.\n //\n // No double-disconnect — step 3 is the only call to the\n // underlying `observer.disconnect()`.\n this.observer.flushPendingRecords({ notify: false });\n this.batcher.flush();\n this.observer.disconnect();\n this.index.disposeAll();\n\n // Shadow roots tear down in the same order: drain observer\n // → flush batcher → disconnect observer → dispose index.\n // `_disconnectShadowRoots()` clears `shadowBindings`, so a\n // subsequent `hostConnected()` rebuilds them from scratch.\n this._disconnectShadowRoots();\n }\n\n /**\n * Explicit-disposal alias for `hostDisconnected()`.\n *\n * Lit `ReactiveController`s are torn down automatically when\n * the host is disconnected, so most consumers never need this\n * method. It exists for two cases:\n *\n * - Tests that want a deterministic, intent-revealing way to\n * tear down the controller from the test body, without\n * reaching into the Lit host's `hostDisconnected()` hook\n * manually. This makes the teardown intent obvious in\n * tests that exercise the observer / index wiring in\n * isolation.\n * - Application code that owns a controller instance and wants\n * to mirror the Lit `hostConnected` / `hostDisconnected`\n * pair with a single, intent-revealing `dispose()` call —\n * for example, when a controller's lifetime is decoupled\n * from the host element (a controller that should be\n * disposed when a feature is unmounted, even if the host\n * stays connected for other reasons).\n *\n * The controller still requires a Lit `ReactiveControllerHost`\n * because the constructor calls `host.addController(this)` —\n * `dispose()` does not enable non-Lit integrations on its own,\n * it only offers a more direct teardown path for the\n * already-Lit-managed controller. If you need a true non-Lit\n * integration, wrap the controller in a minimal\n * `ReactiveControllerHost` adapter.\n *\n * The behaviour is identical to `hostDisconnected()`: the\n * observer is drained into the batcher via\n * `flushPendingRecords({ notify: false })`, the batcher is\n * flushed synchronously, the observer is disconnected, and the\n * index runs `disposeAll()`. Safe to call multiple times — the\n * underlying `MutationObserver.disconnect()` is idempotent and\n * the observer is one-shot, so a second call is a no-op.\n */\n dispose(): void {\n this.hostDisconnected();\n }\n}\n\n/**\n * Convenience constructor used by `BdScopedElement` (in a follow-up\n * task) and by tests that want a one-line setup.\n *\n * The controller owns its own cache and factory — neither is a\n * public option. This constructor simply forwards to\n * `new EtaTemplateController(host, options)` with no\n * transformations. It exists as a named export so callers do not\n * have to import the class directly.\n *\n * `args` and `context` are required by `EtaTemplateController` and\n * must be supplied by the caller. The convenience constructor does\n * not synthesise defaults for them.\n */\nexport function createEtaTemplateController<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = DefaultEtaContext\n>(\n host: EtaTemplateHost & ReactiveControllerHost,\n options: EtaTemplateControllerOptions<TArgs, TContext>\n): EtaTemplateController<TArgs, TContext> {\n return new EtaTemplateController<TArgs, TContext>(host, options);\n}\n"]}
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/eta/controller.ts"],"names":[],"mappings":"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","sourcesContent":["// src/eta/controller.ts\n\nimport {\n Task,\n type TaskStatus\n} from '@lit/task';\n\nimport type {\n ReactiveController,\n ReactiveControllerHost\n} from 'lit';\n\nimport { EtaMutationBatcher } from './batcher.js';\nimport { createEtaTemplateCache } from './cache.js';\nimport { createDefaultTemplateOperationFactory } from './factory.js';\nimport { EtaMutationObserver } from './observer.js';\nimport { EtaTemplateIndex } from './template-index.js';\nimport type {\n EtaMutationBatchSchedule,\n EtaMutationBatchScheduleOption,\n EtaTemplateCache,\n EtaTemplateControllerOptions,\n EtaTemplateHost,\n EtaTemplateIndexLike,\n TemplateOperation,\n TemplateOperationFactory\n} from './types.js';\n\n/**\n * Tracks a per-root observer+batcher+index triple so the controller\n * can manage them as a unit during construction, connection, and\n * teardown.\n */\ninterface ShadowRootBinding<TContext extends object> {\n readonly shadow : ShadowRoot;\n readonly observer: EtaMutationObserver;\n readonly batcher : EtaMutationBatcher;\n readonly index : EtaTemplateIndexLike<TContext>;\n}\n\n/**\n * Default context type for the Eta template subsystem.\n *\n * **This is a recommended context shape, NOT something the controller\n * builds automatically.** The controller used to spread a default\n * `$scope` / `$compute` context internally; the current `args` /\n * `context` API delegates that to the host. Hosts that want the\n * documented shape should construct the context themselves:\n *\n * ```ts\n * context: ([scope]) => ({\n * ...(scope ?? {}),\n * $scope : scope,\n * $compute: this.compute\n * })\n * ```\n *\n * Templates may reference any property of the scope as a top-level\n * binding.\n */\nexport type DefaultEtaContext = {\n readonly $scope? : object;\n readonly $compute?: object;\n} & Record<string, unknown>;\n\n/**\n * Status of the underlying `@lit/task`. Re-exported so consumers do\n * not need a direct `@lit/task` dependency to read it.\n */\nexport type EtaTemplateStatus = TaskStatus;\n\n/**\n * Throws `signal.reason` when the signal has been aborted. No-op\n * otherwise.\n *\n * Prefers the native `signal.throwIfAborted()` when available (most\n * modern browsers) and falls back to a manual throw so the helper\n * still works in environments that pre-date `AbortSignal`'s\n * `throwIfAborted` method (notably some jsdom versions and older\n * test runners).\n */\nfunction throwIfAborted(signal: AbortSignal): void {\n if (typeof signal.throwIfAborted === 'function') {\n signal.throwIfAborted();\n return;\n }\n\n if (signal.aborted) {\n throw (\n signal.reason ??\n new DOMException(\n 'The operation was aborted.',\n 'AbortError'\n )\n );\n }\n}\n\nfunction isPromiseLike<T>(\n value: T | PromiseLike<T>\n): value is PromiseLike<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n );\n}\n\n/**\n * Reactive controller that owns the lifecycle of the Eta template\n * subsystem for a single scoped boundary.\n *\n * Responsibilities:\n *\n * - Owns the `@lit/task` that drives rendering.\n * - Builds the rendering context from the host-supplied `args` /\n * `context` callbacks.\n * - Owns the `MutationObserver` that feeds the index.\n * - Owns a single `EtaTemplateIndex`.\n * - Iterates operations and calls `execute(context)` on each.\n * - Participates in `host.trackLocalWork` so render passes count\n * toward `BdNodeElement.updateComplete`.\n *\n * It does NOT:\n *\n * - Walk the DOM during render (the index is the source of truth).\n * - Discover templates during render (the factory runs once on\n * `add`).\n * - Know about products, options, prices, or transitions.\n *\n * **Skip-render semantics.** When `enabled(args)` returns `false`\n * or `context(args)` returns `undefined`, the controller skips the\n * render pass and returns `undefined`. Previously rendered DOM\n * output stays as-is — the controller does not blank the DOM or\n * remove rendered nodes. Hosts that want blank/fallback output\n * should either return a context that causes templates to render\n * blank values, or publish a fallback scope (e.g. an empty object\n * or a sentinel value) before requesting a render. This behaviour\n * is intentional: it lets the controller be safely driven from\n * the host's reactive state without flickering on intermediate\n * states where the scope is temporarily absent.\n */\nexport class EtaTemplateController<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = DefaultEtaContext\n> implements ReactiveController\n{\n readonly index: EtaTemplateIndexLike<TContext>;\n\n /**\n * The controller's internal template cache.\n *\n * **Internal — true private field.** The `#` ECMAScript private\n * field cannot be observed from outside the class at all\n * (neither via property lookup nor via casting). The TypeScript\n * `private` modifier above would be enough for the type checker\n * but JS property access would still find the field; using\n * `#cache` closes that gap at runtime too.\n *\n * **Per-controller isolation.** The controller always builds its\n * own cache; there is no public option to inject a different\n * one. The cache is shared with the controller's private\n * factory so `clearCache()` drains whatever the factory uses to\n * memoize compiled templates.\n *\n * Hosts invalidate the controller's own cache via\n * `controller.clearCache()`. The `clearCache()` call only clears\n * this controller's cache; no other state is affected.\n */\n readonly #cache: EtaTemplateCache;\n\n private readonly host: EtaTemplateHost &\n ReactiveControllerHost;\n private readonly argsFn : () => TArgs;\n private readonly enabledFn:\n | ((args: TArgs) => boolean)\n | undefined;\n private readonly contextFn: (args: TArgs) =>\n | TContext\n | undefined\n | PromiseLike<TContext | undefined>;\n private readonly onRenderError:\n | ((\n error: unknown,\n operation: TemplateOperation<TContext>\n ) => void)\n | undefined;\n private readonly mutationBatchScheduleOption:\n EtaMutationBatchScheduleOption;\n /**\n * Cached boundary-element predicate, captured once in the\n * constructor and forwarded to every shadow-root index through\n * `_shadowBoundaryPredicate`. The controller is the single\n * source of truth for the predicate — the host supplies it via\n * `options.isBoundaryElement` and the controller threads the\n * SAME predicate to every observed root (light + shadow).\n *\n * Captured up-front so `_shadowBoundaryPredicate` does not need\n * to reach back into `this.options` at every shadow-binding\n * creation. `undefined` when the host did not supply a\n * predicate — preserves the pre-fix behaviour for those hosts.\n */\n private readonly _boundaryElementPredicate:\n | ((element: Element) => boolean)\n | undefined;\n /**\n * Set of every shadow root that has already been wired into a\n * shadow binding.\n *\n * `WeakSet` because shadow roots can be GC'd when their host\n * disconnects — we never want to keep them alive through this\n * field. `_addShadowBinding` is the single writer; both\n * `_connectShadowRoots` (initial discovery) and\n * `_discoverShadowRootsFor` (light-DOM mutation-driven\n * discovery) gate registration through this set so the same\n * shadow root is never wired twice.\n */\n private readonly _seenShadows: WeakSet<ShadowRoot> =\n new WeakSet();\n private readonly observer: EtaMutationObserver;\n private readonly batcher : EtaMutationBatcher;\n private readonly task : Task<TArgs, TContext | undefined>;\n private readonly factory:\n TemplateOperationFactory<TContext>;\n /**\n * Per-shadow-root observer + batcher + index triples. Empty\n * when the host does not enumerate any shadow roots. Each\n * triple is wired in `hostConnected` and torn down in\n * `hostDisconnected` alongside the light-DOM observer.\n */\n private shadowBindings: ShadowRootBinding<TContext>[] = [];\n\n constructor(\n host: EtaTemplateHost & ReactiveControllerHost,\n options: EtaTemplateControllerOptions<TArgs, TContext>\n ) {\n this.host = host;\n\n // Runtime guard — `EtaTemplateHost.root` is non-optional in\n // the type system, but a host implementing the interface\n // could still hand back `undefined` at runtime. The index\n // uses `host.root` as its discovery boundary and silently\n // indexing the whole document if `root` is missing would\n // produce very confusing behaviour. Fail loudly instead.\n if (!host.root) {\n throw new Error(\n 'EtaTemplateController: host.root is required. ' +\n 'The host element must expose a `root` getter that ' +\n 'returns the Element the controller should index.'\n );\n }\n\n this.argsFn = options.args;\n this.enabledFn = options.enabled;\n this.contextFn = options.context;\n this.onRenderError = options.onRenderError;\n this.mutationBatchScheduleOption =\n options.mutationBatchSchedule ?? 'animation-frame';\n this._boundaryElementPredicate =\n options.isBoundaryElement;\n\n this.#cache = createEtaTemplateCache();\n\n // The controller owns its factory. There is no public option\n // to inject one — the factory is a private collaborator like\n // the cache. The controller builds a default\n // `DefaultTemplateOperationFactory` that shares its `#cache`,\n // so the cache and the factory stay in lockstep and\n // `clearCache()` drains the factory's compiled-template store\n // as well.\n const normalizedAttributeNames =\n this._observerAttributeNames(options);\n\n this._normalizedAttributeNames = normalizedAttributeNames;\n\n this.factory =\n createDefaultTemplateOperationFactory<TContext>(\n this.#cache,\n this._factoryOptions(options, normalizedAttributeNames)\n );\n\n this.index =\n options.createIndex?.(this.factory) ??\n new EtaTemplateIndex<TContext>(this.factory, {\n root : host.root,\n isBoundaryElement: options.isBoundaryElement\n });\n\n // Round-6 mutation-batching layer. The batcher sits between\n // the observer and the index: the observer forwards raw\n // MutationRecord batches via `onRecords`, the batcher\n // accumulates them, compacts them per the design rules, and\n // flushes the compacted mutations to the index on the\n // configured schedule (`animation-frame` by default).\n //\n // The `onFlush` callback kicks a render when the flush was\n // dirty — empty flushes never cause a render. We deliberately\n // call `task.run()` (not `requestRender()`) because the\n // batcher is the source of truth for \"dirty\" and we want the\n // Task's scheduling semantics (abort, dedupe, re-entry)\n // rather than the manual `requestRender()` path.\n this.batcher = new EtaMutationBatcher(this.index, {\n schedule: () => this.mutationBatchSchedule,\n onFlush : (dirty) => {\n if (dirty) {\n this.task.run();\n }\n }\n });\n\n this.observer = new EtaMutationObserver({\n attributeNames: normalizedAttributeNames,\n onRecords : (records) => {\n this.batcher.enqueue(records);\n // Discovery hook: a freshly-added element may carry a\n // shadow root that did not exist when `_connectShadowRoots`\n // ran. The hook is idempotent (gated by `_seenShadows`)\n // and only walks `childList` records so attribute / text\n // mutations stay cheap.\n this._discoverShadowRootsFor(records);\n }\n });\n\n this.task = new Task<TArgs, TContext | undefined>(host, {\n autoRun : true,\n argsEqual: options.argsEqual,\n task : async (args, { signal }) => {\n const work = this._runRenderPass(args, signal);\n\n const tracked =\n this.host.trackLocalWork?.(this, work) ?? work;\n\n return tracked;\n },\n args: () => this.argsFn()\n });\n\n host.addController(this);\n }\n\n /**\n * Invalidates every compiled template this controller owns.\n *\n * Drops all entries from the controller's internal cache. The\n * next render that touches a previously-compiled template will\n * re-compile it through the cache.\n *\n * **Per-controller scope.** `clearCache()` only clears this\n * controller's own cache. There is no public cache injection\n * option; hosts that want shared templates across controllers\n * should compile and store those templates at the application\n * layer instead of sharing the controller's internal Eta cache.\n *\n * Safe to call at any time, including between renders.\n */\n clearCache(): void {\n this.#cache.clear();\n }\n\n /**\n * Status of the underlying Task.\n *\n * Exposed so consumers (e.g. tests) can assert progress without\n * reaching into the `@lit/task` surface.\n */\n get status(): EtaTemplateStatus {\n return this.task.status;\n }\n\n /**\n * Latest context produced by a successful render pass.\n */\n get value(): TContext | undefined {\n return this.task.value as TContext | undefined;\n }\n\n /**\n * Most recent render error, or `undefined` when the last render\n * succeeded.\n *\n * Mirrors `@lit/task`'s `Task.error` getter. When a host passes\n * `onRenderError`, the controller swallows render errors and\n * records them here — read this getter to surface them in UI\n * state (e.g. an error banner). When `onRenderError` is omitted,\n * render errors reject `updateComplete`; both this getter and\n * the rejected promise expose the same error value.\n */\n get error(): unknown {\n return this.task.error;\n }\n\n /**\n * Promise that resolves when the current render pass completes.\n *\n * Mirrors `BdNodeElement.updateComplete`'s promise semantics for\n * the Eta subsystem. When the host supplies `trackLocalWork`,\n * this promise is tracked under the controller's instance so the\n * host's `updateComplete` waits for the render to settle before\n * resolving.\n *\n * Useful for tests and for downstream consumers that need to\n * observe render completion without poking at `@lit/task`.\n */\n get updateComplete(): Promise<TContext | undefined> {\n return this.task.taskComplete;\n }\n\n /**\n * Requests a fresh render pass through the underlying `@lit/task`.\n *\n * Most consumers never need to call this: the controller re-runs\n * whenever the host signals an update, the index mutates, or the\n * Task args change. The hook is provided for tests and for\n * consumers that want to force a re-render after an external cache\n * invalidation, scope mutation outside the normal reactive flow,\n * or other event that the Task's `argsEqual` check might miss.\n *\n * The name reflects what actually happens: `Task.run()` schedules\n * the task through Task's own scheduling semantics (which may\n * batch, dedupe, or abort against the current run id) rather than\n * synchronously executing the render loop.\n *\n * **Returns a promise.** The promise resolves to the latest\n * context (`TContext | undefined`) produced by the render pass\n * that `Task.run()` scheduled. Awaiting it lets tests and\n * downstream consumers observe render completion without\n * reaching into `@lit/task`. The promise resolves with the\n * controller's `value`, which is `undefined` when the render was\n * skipped (because `enabled()` returned `false` or `context()`\n * returned `undefined`).\n *\n * Because `Task.run()` returns `Promise<void>` in the installed\n * `@lit/task` version, this method kicks the run and then waits\n * on `taskComplete` (the same promise exposed as\n * `controller.updateComplete`) before reading `this.value`.\n * `Task.run()` resolves when the run is *scheduled*, not when it\n * *completes*; waiting on `taskComplete` is the only way to\n * guarantee the returned context reflects the render that this\n * call kicked off. The same `taskComplete` is also what the host\n * `updateComplete` waits on (through `trackLocalWork`), so\n * awaiting this method aligns the caller with the host's\n * subtree-completion promise.\n */\n async requestRender(): Promise<TContext | undefined> {\n // Kick the Task run. The returned promise resolves when the\n // run is scheduled, NOT when the render completes.\n this.task.run();\n\n // Wait for the render to actually finish so `this.value`\n // reflects this render's context rather than the previous\n // run's value. `taskComplete` is the same promise exposed\n // through the controller's `updateComplete` getter.\n await this.task.taskComplete;\n\n return this.value;\n }\n\n /**\n * Flushes the mutation batcher synchronously.\n *\n * Forwards any pending `MutationRecord`s to the index through\n * the batcher's compaction pass and returns whether the flush\n * made the index dirty. Returns `false` when there was nothing\n * to flush — an empty batch is a no-op.\n *\n * **Use cases:**\n *\n * - Hosts that drive the controller with\n * `mutationBatchSchedule: 'manual'` and want to apply a\n * known decoration phase's mutations before reading the\n * index. Pair with `flushAndRender()` when a render is\n * needed afterwards.\n * - Tests that want a deterministic, intent-revealing way to\n * drain the batcher without going through the observer's\n * scheduled tick. This is the recommended way to assert on\n * the post-flush index state from test bodies.\n * - Hot-path code that knows a render is about to be requested\n * via `requestRender()` and wants to guarantee the render\n * sees the latest mutations (the render task body already\n * calls `batcher.flush()` before executing, so this is\n * rarely needed in practice — but useful when callers want\n * the flush to happen synchronously at the call site rather\n * than inside the Task).\n *\n * Does NOT call `onFlush` on the batcher, so the flush never\n * re-enters `task.run()` on its own. Call `flushAndRender()`\n * when a render should follow the flush.\n *\n * Returns the batcher's `flush()` return value — `true` when\n * at least one compacted mutation made the index dirty,\n * `false` otherwise. The dirty flag mirrors the batcher's\n * dirty semantics (OR-accumulated across the four compaction\n * passes — see `EtaMutationBatcher.flush`).\n */\n /**\n * Flushes every observer queue into its matching batcher, then\n * flushes every batcher into its matching index.\n *\n * Forwards any pending `MutationRecord`s to the index through\n * the batcher's compaction pass and returns whether the flush\n * made the index dirty. Returns `false` when there was nothing\n * to flush — an empty batch is a no-op.\n *\n * **Manual schedule mode.** When `mutationBatchSchedule` is\n * `'manual'`, the batcher never auto-flushes; the host must\n * drain the queue itself. This method honours that contract\n * across every observed root (light + every shadow binding).\n *\n * **Drain order:**\n *\n * 1. Light observer → light batcher\n * (`observer.flushPendingRecords({ notify: false })`).\n * 2. Every shadow observer → its shadow batcher\n * (`binding.observer.flushPendingRecords({ notify: false })`).\n * Drain the observer BEFORE the batcher so records the\n * browser has produced but not yet delivered reach the\n * batcher before the synchronous flush.\n * 3. Light batcher → light index (`batcher.flush()`).\n * 4. Every shadow batcher → its shadow index\n * (`binding.batcher.flush()`), OR-accumulated into the\n * combined dirty flag.\n *\n * **Use cases:**\n *\n * - Hosts that drive the controller with\n * `mutationBatchSchedule: 'manual'` and want to apply a\n * known decoration phase's mutations before reading the\n * index. Pair with `flushAndRender()` when a render is\n * needed afterwards.\n * - Tests that want a deterministic, intent-revealing way to\n * drain the batcher without going through the observer's\n * scheduled tick. This is the recommended way to assert on\n * the post-flush index state from test bodies.\n * - Hot-path code that knows a render is about to be requested\n * via `requestRender()` and wants to guarantee the render\n * sees the latest mutations (the render task body already\n * calls `batcher.flush()` before executing, so this is\n * rarely needed in practice — but useful when callers want\n * the flush to happen synchronously at the call site rather\n * than inside the Task).\n *\n * Does NOT call `onFlush` on any batcher, so the flush never\n * re-enters `task.run()` on its own. Call `flushAndRender()`\n * when a render should follow the flush.\n *\n * Returns the combined dirty flag — `true` when at least one\n * batcher (light or shadow) made its index dirty, `false`\n * otherwise. The dirty flag mirrors each batcher's dirty\n * semantics (OR-accumulated across the four compaction passes\n * — see `EtaMutationBatcher.flush`).\n *\n * Idempotent — calling on an empty queue/batch still returns\n * `false` and never triggers a render. The `notify: false` flag\n * on every `flushPendingRecords` call keeps the observer's\n * own `onChange` callback dormant during a manual flush so a\n * host that drains and then calls `requestRender()` does not\n * double-schedule.\n */\n flushMutations(): boolean {\n this.observer.flushPendingRecords({ notify: false });\n\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n }\n\n let dirty = this.batcher.flush();\n\n for (const binding of this.shadowBindings) {\n if (binding.batcher.flush()) {\n dirty = true;\n }\n }\n\n return dirty;\n }\n\n /**\n * Flushes the mutation batcher and requests a fresh render\n * pass when the flush was dirty.\n *\n * Convenience wrapper for the \"manual\" schedule mode — hosts\n * that opt into `mutationBatchSchedule: 'manual'` must drain\n * the batcher themselves and then trigger a render. This\n * method collapses both steps into one call:\n *\n * ```ts\n * decorateBlock(block);\n * await decorateIcons(block);\n * eta.flushAndRender();\n * ```\n *\n * When the flush was a no-op (`false` return from the\n * batcher) the render is skipped — there is no point re-running\n * the Task against an unchanged index. When the flush was\n * dirty, the call forwards to `requestRender()` so the Task\n * runs with the same scheduling semantics as any other\n * render-triggering path.\n *\n * Does NOT wait for the render to complete; callers that need\n * to await the resulting context can follow up with\n * `await controller.updateComplete` (or\n * `await controller.requestRender()`). The return value of\n * the underlying `requestRender()` is intentionally discarded\n * to keep the API synchronous and easy to call from\n * imperative decoration flows.\n */\n flushAndRender(): void {\n const dirty = this.flushMutations();\n\n if (dirty) {\n // `requestRender()` returns a promise that resolves once\n // the Task completes; we intentionally discard it so the\n // host can call `flushAndRender()` from synchronous\n // decoration code. Callers that need to await the render\n // can use `await controller.updateComplete` afterwards.\n void this.requestRender();\n }\n }\n\n /**\n * Reads the current batch schedule as resolved by the\n * controller. Useful for diagnostics and for tests that want\n * to assert on the controller's effective schedule without\n * reaching into the private `batcher` field.\n */\n get mutationBatchSchedule(): EtaMutationBatchSchedule {\n return this._currentMutationBatchSchedule();\n }\n\n private _currentMutationBatchSchedule(): EtaMutationBatchSchedule {\n const schedule = this.mutationBatchScheduleOption;\n\n if (typeof schedule === 'function') {\n return schedule() ?? 'animation-frame';\n }\n\n return schedule ?? 'animation-frame';\n }\n\n /**\n * Runs a single render pass.\n *\n * The pass is gated by `enabled(args)` and `context(args)`:\n *\n * - `enabled` returning `false` skips the loop and returns\n * `undefined` (no context, no render).\n * - `context` returning `undefined` does the same.\n *\n * When both gates pass, the render loop runs in two phases:\n *\n * 1. **Pre-flush.** The mutation batcher applies any pending\n * `MutationRecord` batches to the index via\n * `observer.flushPendingRecords({ notify: false })` drains\n * records the browser has produced but not delivered yet into\n * the batcher, then `batcher.flush()` applies the compacted\n * batch so the index and source registry are accurate before\n * the render reads operations. Without this step, a render\n * triggered by a Task args change or a manual `requestRender()`\n * call could run against a stale index whenever the observer or\n * batcher had not yet flushed a pending burst of DOM changes.\n *\n * `batcher.flush()` cancels any pending scheduled flush\n * AND applies the current batch synchronously; it does NOT\n * call `onFlush`. That detail is important — calling\n * `onFlush` from inside an in-flight render pass would\n * re-enter `task.run()` and create an infinite render\n * loop. The Task's auto-run and the batcher's onFlush\n * callback are kept on separate scheduling paths for this\n * reason.\n *\n * The round-6 batcher sits between the observer and the index,\n * so both layers participate in the pre-flush: the observer\n * drains queued records into the batcher, and the batcher\n * drains its compacted mutations into the index.\n * 2. **Render.** The render loop runs in two phases so each\n * phase can suspend only the observer that would otherwise\n * treat the phase's self-induced mutations as fresh external\n * mutations:\n *\n * a. **Light-DOM phase.** Iterates `this.index` inside\n * `observer.suspendSync(...)` so DOM writes performed by\n * light-DOM operations are not fed back as fresh\n * mutations. The `finally` block drains whatever's queued\n * after the loop — at this point only self-induced\n * records should remain.\n * b. **Shadow phase.** Iterates each binding's `index`\n * inside `binding.observer.suspendSync(...)`. Wrapping\n * the shadow phase in `this.observer.suspendSync` would\n * discard legitimate external light-DOM mutations queued\n * in the meantime via `takeRecords`, and would not\n * actually suppress shadow observer feedback (the light\n * observer cannot see shadow-tree writes anyway). Each\n * binding therefore suspends its OWN observer so writes\n * inside its shadow root are not fed back as fresh\n * mutations.\n *\n * The pre-flush step above drained every shadow observer\n * BEFORE either phase, so the only records the shadow\n * observer will see during the shadow phase are writes from\n * the shadow operations themselves — exactly what each\n * shadow observer's `suspendSync` is there to suppress.\n *\n * Per-operation errors are routed to `onRenderError` when\n * supplied; otherwise they propagate to the Task and abort the\n * render pass.\n */\n private async _runRenderPass(\n args: TArgs,\n signal: AbortSignal\n ): Promise<TContext | undefined> {\n if (this.enabledFn && !this.enabledFn(args)) {\n return undefined;\n }\n\n const contextResult = this.contextFn(args);\n const context = isPromiseLike(contextResult)\n ? await contextResult\n : contextResult;\n\n throwIfAborted(signal);\n\n if (context === undefined) {\n return undefined;\n }\n\n // Pre-flush: drain queued observer records into the batcher,\n // then apply any pending batch mutations to the index BEFORE\n // building the render loop. Without this step, the render\n // would run against a stale index whenever a burst of DOM\n // changes has been produced by the observer or accumulated by\n // the batcher but not yet flushed.\n //\n // `batcher.flush()` cancels any pending scheduled flush and\n // applies the current batch synchronously. It does NOT call\n // `onFlush` (no re-entrant `task.run()` from inside an\n // in-flight render pass — see the batcher's flush vs\n // flushAndNotify split).\n //\n // Draining the observer first matters because suspendSync's\n // final takeRecords() intentionally drops self-induced render\n // records. External records queued before the render must reach\n // the batcher before that drain happens. The same applies to\n // every shadow observer — drain those too so render passes\n // don't run against stale shadow indices.\n this.observer.flushPendingRecords({ notify: false });\n this.batcher.flush();\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n binding.batcher.flush();\n }\n\n this.observer.suspendSync(() => {\n for (const op of this.index) {\n if (signal.aborted) {\n break;\n }\n\n try {\n op.execute(context);\n } catch (error) {\n if (this.onRenderError) {\n this.onRenderError(error, op);\n } else {\n throw error;\n }\n }\n }\n });\n\n // Shadow operations run under their OWN observer suspension.\n // The render loop iterates light-DOM ops and shadow-binding\n // ops in separate phases so each phase can suspend only the\n // observer that would otherwise treat the phase's self-induced\n // mutations as fresh external mutations.\n //\n // Why two phases:\n //\n // - The light-DOM observer stays alive while a shadow\n // operation writes its rendered value into a shadow node —\n // that observer cannot see the write (different root), so\n // it cannot feed back into the light index. Wrapping the\n // shadow phase in `this.observer.suspendSync` would be a\n // no-op (and would discard legitimate external light-DOM\n // mutations queued in the meantime via `takeRecords`).\n // - The shadow observer for a binding sees only mutations\n // inside its own shadow root. Shadow operations writing\n // into that root must run under that observer's\n // suspension so the writes are not fed back as fresh\n // shadow mutations; otherwise the shadow batcher would\n // re-enqueue them on the next flush and the operation\n // would rebuild against no template and get dropped (the\n // shadow source registry has already persisted the\n // rendered plain string).\n //\n // Each phase reuses the existing abort-signal check and the\n // `onRenderError` routing so a failing operation only fails\n // its own phase.\n for (const binding of this.shadowBindings) {\n binding.observer.suspendSync(() => {\n for (const op of binding.index) {\n if (signal.aborted) {\n break;\n }\n\n try {\n op.execute(context);\n } catch (error) {\n if (this.onRenderError) {\n this.onRenderError(error, op);\n } else {\n throw error;\n }\n }\n }\n });\n }\n\n // Surface aborts that fired during the render loop as Task\n // errors so the task moves to the `ABORTED` status instead of\n // returning a stale context. The render loop itself is\n // synchronous, but external callers (e.g. host disconnection)\n // can flip the signal between the loop's `break` check and the\n // point where we return.\n throwIfAborted(signal);\n\n return context;\n }\n\n /**\n * Returns the effective attribute-name list that both the\n * observer and the factory must agree on.\n *\n * The single source of truth avoids contradictory\n * configurations where the observer is told to watch every\n * attribute while the factory is told to filter to a specific\n * list (or vice versa). The previous behaviour dropped records\n * on the floor silently when `observeAllAttributes: true` was\n * combined with a non-empty `attributeNames`.\n *\n * Resolution order:\n *\n * 1. `observeAllAttributes: true` → `undefined` (watch\n * everything).\n * 2. Non-empty `attributeNames` → that exact list.\n * 3. Anything else → `undefined` (watch everything).\n */\n private _observerAttributeNames(\n options: EtaTemplateControllerOptions<\n TArgs,\n TContext\n >\n ): readonly string[] | undefined {\n if (options.observeAllAttributes === true) {\n return undefined;\n }\n\n if (\n options.attributeNames &&\n options.attributeNames.length > 0\n ) {\n return options.attributeNames;\n }\n\n return undefined;\n }\n\n /**\n * Builds the per-factory option object passed to the default\n * factory constructor.\n *\n * `observeAllAttributes: true` (or no allowlist supplied) means\n * the factory should observe every attribute, so we omit\n * `attributeNames` entirely and let the factory default to\n * \"observe all\".\n *\n * When the controller was constructed with an explicit\n * `attributeNames` allowlist, we forward the **normalized** list\n * (computed by `_observerAttributeNames`) so the factory and the\n * observer agree on the same allowlist. Using the raw\n * `options.attributeNames` here would let `observeAllAttributes:\n * true` coexist with a non-empty list, contradicting the\n * observer's \"watch everything\" mode.\n */\n private _factoryOptions(\n _options: EtaTemplateControllerOptions<\n TArgs,\n TContext\n >,\n normalizedAttributeNames: readonly string[] | undefined\n ): {\n readonly attributeNames?: readonly string[];\n } {\n if (normalizedAttributeNames === undefined) {\n return {};\n }\n\n return { attributeNames: normalizedAttributeNames };\n }\n\n hostConnected(): void {\n // Initialize after the host is connected so the light DOM is available.\n // Nested boundaries are detected by the configured isBoundaryElement predicate.\n this.index.initialize(this.host.root);\n\n this.observer.observe(this.host.root);\n\n // Shadow roots are wired after the light-DOM observer so a\n // render pass covers both trees in stable order. The host's\n // `shadowRoots` callback runs once per connect; closed roots\n // are skipped with a one-time console.warn.\n this._connectShadowRoots();\n }\n\n /**\n * Discovers every open shadow root attached to a descendant of\n * the host's light subtree and wires one observer + batcher +\n * index triple per root.\n *\n * `MutationObserver` is scoped to a single root: an observer\n * attached to the host's light root does NOT see mutations\n * inside any nested shadow root. To render templates into\n * shadow trees the controller must therefore attach a separate\n * observer per shadow root. The controller discovers those roots\n * itself — walking the host's light subtree and reading\n * `element.shadowRoot` — so hosts do not have to plumb the\n * list through.\n *\n * Closed shadow roots are unreachable from outside (the\n * `shadowRoot` getter returns `null` for closed mode), so they\n * are silently skipped. Hosts that want to render into a\n * closed root should attach a controller from inside that root\n * instead.\n *\n * Idempotent — re-running on a second `hostConnected()` first\n * tears down any existing bindings, then rebuilds them.\n */\n private _connectShadowRoots(): void {\n if (this.shadowBindings.length > 0) {\n this._disconnectShadowRoots();\n }\n\n const root = this.host.root;\n\n if (root === undefined) {\n return;\n }\n\n // Iterative DFS over both the light DOM and any attached open\n // shadow roots. Each step pops a node from the stack; for\n // light-DOM elements we descend into `children` AND follow\n // `shadowRoot` so a shadow tree nested inside a child is\n // discovered. For shadow roots we descend into the root's\n // own children (which are the shadow-tree's top-level\n // nodes). Iterative (not recursive) so deeply nested\n // shadow trees cannot blow the stack.\n const stack: Node[] = [root];\n\n while (stack.length > 0) {\n const node = stack.pop()!;\n\n if (node instanceof ShadowRoot) {\n // Register the shadow root through the shared helper —\n // it gates against `_seenShadows` so the same shadow root\n // is never wired twice (the same guard also catches a\n // re-entrant `_addShadowBinding` from a nested element's\n // own upgrade path).\n this._addShadowBinding(node);\n // Descend into the shadow tree's top-level children.\n for (const child of Array.from(node.children)) {\n stack.push(child);\n }\n continue;\n }\n\n if (node instanceof Element) {\n // Descend into light-DOM children first.\n for (const child of Array.from(node.children)) {\n stack.push(child);\n }\n // Then follow any attached open shadow root. Order\n // matters for stable enumeration — children first,\n // then the shadow — so the outer widget's own shadow\n // tree appears before an inner widget's shadow tree.\n if (node.shadowRoot) {\n stack.push(node.shadowRoot);\n }\n }\n }\n }\n\n /**\n * Registers a single shadow root with the controller — builds\n * one observer + batcher + index triple per root and wires it\n * into the light-DOM observer's discovery callback.\n *\n * Gates registration through `_seenShadows` so the same shadow\n * root is never wired twice. Closed roots are skipped\n * silently (their `shadowRoot` getter returns `null` so the\n * caller should not pass them in).\n */\n private _addShadowBinding(shadow: ShadowRoot): void {\n if (this._seenShadows.has(shadow)) {\n return;\n }\n\n this._seenShadows.add(shadow);\n\n const index = new EtaTemplateIndex<TContext>(this.factory, {\n root: shadow,\n isBoundaryElement:\n this._shadowBoundaryPredicate(shadow)\n });\n\n const batcher = new EtaMutationBatcher(index, {\n schedule: () => this.mutationBatchSchedule,\n onFlush : (dirty) => {\n if (dirty) {\n this.task.run();\n }\n }\n });\n\n const observer = new EtaMutationObserver({\n attributeNames: this._normalizedAttributeNames,\n onRecords : (records) => {\n batcher.enqueue(records);\n }\n });\n\n index.initialize(shadow);\n observer.observe(shadow);\n\n this.shadowBindings.push({\n shadow,\n observer,\n batcher,\n index\n });\n }\n\n /**\n * Reacts to a light-DOM `MutationRecord` batch by registering\n * any newly-discovered open shadow roots.\n *\n * Lit `until` directives, `repeat` expansions, and custom\n * element upgrades can attach shadow roots after the\n * controller's `hostConnected()` pass has already walked the\n * subtree. The light-DOM observer's callback fires for every\n * mutation batch — we hook it here so a newly-inserted element\n * that carries an open shadow root gets wired into the\n * controller on the next batcher tick.\n *\n * Idempotent — `_addShadowBinding` gates against\n * `_seenShadows`, so re-running the discovery walk on the same\n * records (or running it during a `suspendSync` of the light\n * observer) is harmless.\n *\n * Only `childList` records matter: an `add` of an element is\n * what can carry a fresh shadow root, and skipping text /\n * attribute records keeps the discovery walk cheap.\n */\n private _discoverShadowRootsFor(\n records: readonly MutationRecord[]\n ): void {\n for (const record of records) {\n if (record.type !== 'childList') {\n continue;\n }\n\n for (const added of Array.from(record.addedNodes)) {\n this._walkForShadows(added);\n }\n }\n }\n\n /**\n * Iterative DFS over `node`'s subtree (light children + open\n * shadow roots) that registers every shadow root it encounters\n * via `_addShadowBinding`.\n *\n * Mirrors the discovery shape used inside `_connectShadowRoots`\n * but operates on a single subtree rooted at `node` rather\n * than the full host subtree. Skips detached roots and any\n * element/shadow already known to the controller — the\n * `_seenShadows` gate inside `_addShadowBinding` short-circuits\n * the second registration.\n */\n private _walkForShadows(node: Node): void {\n const stack: Node[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop()!;\n\n if (current instanceof ShadowRoot) {\n this._addShadowBinding(current);\n // Descend into the shadow tree's top-level children —\n // a nested host's own shadow must also be picked up.\n for (const child of Array.from(current.children)) {\n stack.push(child);\n }\n continue;\n }\n\n if (current instanceof Element) {\n for (const child of Array.from(current.children)) {\n stack.push(child);\n }\n\n const shadow = current.shadowRoot;\n\n // `shadowRoot` is `null` for closed-mode and for\n // elements that have not yet created a shadow root. Open\n // roots are reachable via the getter and have a\n // non-null return — `_addShadowBinding` then gates\n // against `_seenShadows`.\n if (shadow !== null) {\n stack.push(shadow);\n }\n }\n }\n }\n\n /**\n * Tears down every per-shadow-root observer, batcher, and index.\n * Mirrors the light-DOM teardown order in `hostDisconnected()`\n * so source-registry preservation behaves the same way on\n * shadow roots as it does on the light tree.\n *\n * Clears `_seenShadows` so a subsequent `hostConnected()` can\n * re-discover every shadow root from scratch — observers do\n * not survive disconnect, so previously-seen shadow roots are\n * no longer wired and should be re-discovered.\n */\n private _disconnectShadowRoots(): void {\n for (const binding of this.shadowBindings) {\n binding.observer.flushPendingRecords({ notify: false });\n binding.batcher.flush();\n binding.observer.disconnect();\n binding.index.disposeAll();\n }\n\n this.shadowBindings = [];\n\n // Rebuild the seen-set to match the cleared bindings list.\n // `WeakSet` cannot be cleared so we reassign the field —\n // any other code holding a reference to the previous set\n // would still see stale entries, but the controller is the\n // sole writer and the only other read site is\n // `_addShadowBinding`, which always reads the latest field\n // value. The `unknown` cast is needed because the field is\n // declared `private readonly` — the same pattern is used by\n // the existing `WeakMap` / `WeakSet` resets in\n // `EtaTemplateIndex.clear()`.\n (\n this as unknown as { _seenShadows: WeakSet<ShadowRoot> }\n )._seenShadows = new WeakSet();\n }\n\n /**\n * Returns the boundary predicate for a shadow-root index. The\n * controller currently reuses the host's\n * `isBoundaryElement` rule for every observed root — the host\n * option is the single source of truth. This helper exists so\n * per-root overrides can be added later without churning the\n * `_connectShadowRoots` call site.\n *\n * Returns `undefined` when the host did not supply an\n * `isBoundaryElement` predicate — preserves the pre-fix\n * behaviour for those hosts (the shadow index then has no\n * boundary rule and falls back to its own ownership checks).\n */\n private _shadowBoundaryPredicate(\n _shadow: ShadowRoot\n ): ((element: Element) => boolean) | undefined {\n return this._boundaryElementPredicate;\n }\n\n /**\n * Cached normalized attribute name list. The controller\n * computes this once in the constructor and reuses it for both\n * the light-DOM observer and every shadow observer so a single\n * allowlist applies across every observed root.\n */\n private readonly _normalizedAttributeNames:\n | readonly string[]\n | undefined;\n\n hostDisconnected(): void {\n // Round-6 teardown order. The order matters for source-\n // registry preservation:\n //\n // 1. `observer.flushPendingRecords({ notify: false })` —\n // drains any records the browser has already produced\n // for the underlying `MutationObserver` and forwards\n // them through `onRecords` into the batcher. This is\n // the round-6 equivalent of round 5's\n // `observer.disconnect({ flush: true, notify: false })`:\n // the batcher is now the destination, so we drain into\n // the batcher first and let the regular disconnect path\n // skip the flush. `notify: false` avoids scheduling a\n // render during teardown.\n // 2. `batcher.flush()` — applies the accumulated batch\n // synchronously so the index / source registry are\n // accurate through the next `disposeAll()`. Without\n // this step, an external mutation that landed just\n // before disconnect would survive only inside the\n // batcher's pending batch and be discarded together\n // with the batch on the next `flush()` (which won't\n // run because the observer is no longer attached).\n // 3. `observer.disconnect()` — stops the underlying\n // `MutationObserver` from delivering future records.\n // Defaults to drain-only (`flush: false`) because step 1\n // already forwarded the queue; the observer's `finally`\n // `takeRecords()` is defensive.\n // 4. `index.disposeAll()` — clears operations while\n // preserving the source registry for the next reconnect.\n //\n // No double-disconnect — step 3 is the only call to the\n // underlying `observer.disconnect()`.\n this.observer.flushPendingRecords({ notify: false });\n this.batcher.flush();\n this.observer.disconnect();\n this.index.disposeAll();\n\n // Shadow roots tear down in the same order: drain observer\n // → flush batcher → disconnect observer → dispose index.\n // `_disconnectShadowRoots()` clears `shadowBindings`, so a\n // subsequent `hostConnected()` rebuilds them from scratch.\n this._disconnectShadowRoots();\n }\n\n /**\n * Explicit-disposal alias for `hostDisconnected()`.\n *\n * Lit `ReactiveController`s are torn down automatically when\n * the host is disconnected, so most consumers never need this\n * method. It exists for two cases:\n *\n * - Tests that want a deterministic, intent-revealing way to\n * tear down the controller from the test body, without\n * reaching into the Lit host's `hostDisconnected()` hook\n * manually. This makes the teardown intent obvious in\n * tests that exercise the observer / index wiring in\n * isolation.\n * - Application code that owns a controller instance and wants\n * to mirror the Lit `hostConnected` / `hostDisconnected`\n * pair with a single, intent-revealing `dispose()` call —\n * for example, when a controller's lifetime is decoupled\n * from the host element (a controller that should be\n * disposed when a feature is unmounted, even if the host\n * stays connected for other reasons).\n *\n * The controller still requires a Lit `ReactiveControllerHost`\n * because the constructor calls `host.addController(this)` —\n * `dispose()` does not enable non-Lit integrations on its own,\n * it only offers a more direct teardown path for the\n * already-Lit-managed controller. If you need a true non-Lit\n * integration, wrap the controller in a minimal\n * `ReactiveControllerHost` adapter.\n *\n * The behaviour is identical to `hostDisconnected()`: the\n * observer is drained into the batcher via\n * `flushPendingRecords({ notify: false })`, the batcher is\n * flushed synchronously, the observer is disconnected, and the\n * index runs `disposeAll()`. Safe to call multiple times — the\n * underlying `MutationObserver.disconnect()` is idempotent and\n * the observer is one-shot, so a second call is a no-op.\n */\n dispose(): void {\n this.hostDisconnected();\n }\n}\n\n/**\n * Convenience constructor used by `BdScopedElement` (in a follow-up\n * task) and by tests that want a one-line setup.\n *\n * The controller owns its own cache and factory — neither is a\n * public option. This constructor simply forwards to\n * `new EtaTemplateController(host, options)` with no\n * transformations. It exists as a named export so callers do not\n * have to import the class directly.\n *\n * `args` and `context` are required by `EtaTemplateController` and\n * must be supplied by the caller. The convenience constructor does\n * not synthesise defaults for them.\n */\nexport function createEtaTemplateController<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = DefaultEtaContext\n>(\n host: EtaTemplateHost & ReactiveControllerHost,\n options: EtaTemplateControllerOptions<TArgs, TContext>\n): EtaTemplateController<TArgs, TContext> {\n return new EtaTemplateController<TArgs, TContext>(host, options);\n}\n"]}
@@ -275,7 +275,7 @@ export interface EtaTemplateControllerOptions<TArgs extends readonly unknown[] =
275
275
  */
276
276
  enabled?: (args: TArgs) => boolean;
277
277
  /**
278
- * Context builder. Returns the value handed to each
278
+ * Context builder. Returns (or resolves to) the value handed to each
279
279
  * `TemplateOperation.execute()` call.
280
280
  *
281
281
  * Returning `undefined` short-circuits the render pass for the
@@ -290,7 +290,7 @@ export interface EtaTemplateControllerOptions<TArgs extends readonly unknown[] =
290
290
  * build that shape for the host — see `DefaultEtaContext` for the
291
291
  * documented shape.
292
292
  */
293
- context: (args: TArgs) => TContext | undefined;
293
+ context: (args: TArgs) => TContext | undefined | PromiseLike<TContext | undefined>;
294
294
  /**
295
295
  * Optional override used by tests to inject a deterministic index.
296
296
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/eta/types.ts"],"names":[],"mappings":"AAAA,mBAAmB","sourcesContent":["// src/eta/types.ts\n\n/**\n * Shared type definitions for the Eta template subsystem.\n *\n * The subsystem is split into small, single-responsibility pieces:\n *\n * MutationObserver\n * -> translates DOM mutations into TemplateIndexMutation values\n *\n * EtaTemplateIndex\n * -> maintains the operation index for a scoped boundary\n *\n * TemplateOperationFactory\n * -> discovers templates and compiles them via EtaTemplateCache\n *\n * TextOperation / AttributeOperation\n * -> render a single DOM target against a context\n *\n * EtaTemplateController\n * -> owns lifecycle, the Task schedule, and the context builder\n *\n * EtaMutationBatcher\n * -> coalesces MutationRecord batches into a single\n * index flush per scheduled tick (round 6)\n *\n * Most of these layers are Eta-agnostic. Only the cache and factory know how\n * to talk to Eta. Everything else works on plain DOM nodes.\n */\n\n/**\n * Schedule mode for `EtaMutationBatcher` flushes.\n *\n * Defined here (rather than in `batcher.ts`) so consumers can\n * import the type without pulling the batcher module into a\n * type-only import cycle. The batcher re-exports the type for\n * direct importers.\n *\n * - `microtask` — flush via `queueMicrotask`. Lowest latency.\n * - `animation-frame` — flush via `requestAnimationFrame`.\n * Recommended default for browser hosts.\n * - `idle` — flush via `requestIdleCallback` (with a `setTimeout`\n * fallback). Lowest priority.\n * - `manual` — never auto-flush; the host owns the lifecycle.\n */\nexport type EtaMutationBatchSchedule =\n | 'microtask'\n | 'animation-frame'\n | 'idle'\n | 'manual';\n\nexport type EtaMutationBatchScheduleResolver = () =>\n | EtaMutationBatchSchedule\n | undefined;\n\nexport type EtaMutationBatchScheduleOption =\n | EtaMutationBatchSchedule\n | EtaMutationBatchScheduleResolver;\n\n/**\n * A precompiled Eta template function.\n *\n * `data` is the rendering context. The function returns a string. Eta exposes\n * `compile` as a member of the Eta class, so the function signature is\n * `(data?) => string`.\n */\nexport type EtaCompiledTemplate = (\n data?: object\n) => string;\n\n/**\n * Discriminated union describing a single change that should be reflected\n * in the template index.\n *\n * - `add` introduces a node that has not been seen before\n * - `remove` drops a node and all of its descendants\n * - `attribute` re-runs attribute discovery for a single attribute on an\n * element that is already part of the index\n * - `text` re-runs text discovery for a text node that is already part of\n * the index\n */\nexport type TemplateIndexMutation =\n | {\n readonly type: 'add';\n readonly node: Node;\n }\n | {\n readonly type: 'remove';\n readonly node: Node;\n }\n | {\n readonly type : 'attribute';\n readonly element : Element;\n readonly attribute: string;\n }\n | {\n readonly type: 'text';\n readonly node: Text;\n };\n\n/**\n * Anything that can absorb a TemplateIndexMutation.\n *\n * EtaMutationObserver and EtaTemplateIndex both satisfy this interface so\n * that they can be swapped in tests or composed by other layers.\n *\n * `apply` returns `true` only when the sink actually changed its\n * index as a result of the mutation. Returning `false` signals that\n * the mutation was ignored (the node was outside the index's root,\n * the attribute had no template, the boundary skipped the subtree,\n * etc.). The observer uses this signal to decide whether a render\n * pass needs to be scheduled, so returning the wrong value directly\n * causes either stale renders or unnecessary work.\n */\nexport interface TemplateIndexSink {\n apply(mutation: TemplateIndexMutation): boolean;\n}\n\n/**\n * A single immutable rendering operation.\n *\n * Operations are owned by a single DOM target (a text node or an attribute\n * of an element) and know nothing about template discovery, the index, or\n * the controller. They simply render their target against a context.\n *\n * The `dispose` method is currently a no-op for the built-in operations but\n * exists as an extension point for future implementations that may hold\n * observers, event listeners, or other resources.\n */\nexport interface TemplateOperation<\n TContext = object\n> {\n readonly owner: Node;\n\n readonly kind: 'text' | 'attribute';\n\n execute(context: TContext): void;\n\n dispose(): void;\n}\n\n/**\n * Structural surface used by `TemplateOperationContext.sources`.\n *\n * Defined here (rather than imported from `source-registry.ts`) so\n * `types.ts` stays a leaf module per the eta subsystem layout.\n * Any object that satisfies these four methods can stand in for\n * the real `EtaTemplateSourceRegistry` — tests can pass mocks\n * without pulling the registry module into a type-only cycle.\n */\nexport interface TemplateOperationSourceRegistry {\n readTextSource(node: Text): string | undefined;\n replaceTextSource(\n node: Text,\n source: string | undefined\n ): void;\n readAttributeSource(\n element: Element,\n name: string\n ): string | undefined;\n setAttributeSource(\n element: Element,\n name: string,\n source: string\n ): void;\n}\n\n/**\n * Per-call context passed into `TemplateOperationFactory.create*`\n * methods.\n *\n * Carries dependencies that the factory needs at discovery time\n * but does not own. Today the only such dependency is the\n * `EtaTemplateSourceRegistry`, which preserves original template\n * sources across renders. By threading the registry through every\n * factory call (rather than storing it on the factory instance)\n * the factory stays stateless at the call site and can safely be\n * shared across multiple indices — the registry is always the\n * one owned by the calling index.\n *\n * The context is optional: factory implementations that do not\n * need preserved sources can simply ignore it. Test fakes can\n * pass `undefined`.\n */\nexport interface TemplateOperationContext {\n /** Source registry for preserved original template sources. */\n readonly sources?: TemplateOperationSourceRegistry;\n}\n\n/**\n * Factory contract for producing TemplateOperation instances from DOM nodes.\n *\n * The factory is the only Eta-aware discovery layer. The collector simply\n * forwards raw DOM nodes to the factory and stores whatever comes back.\n *\n * A factory returns `undefined` when a node does not contain a template,\n * which lets the index skip irrelevant subtrees.\n *\n * The optional `context` parameter carries dependencies that the\n * factory needs at discovery time but does not own — currently\n * the source registry. The factory treats it as advisory: a\n * missing context means the factory does not have access to\n * preserved sources, so post-render reads fall back to the live\n * DOM value.\n */\nexport interface TemplateOperationFactory<\n TContext = object\n> {\n createTextOperation(\n node: Text,\n context?: TemplateOperationContext\n ): TemplateOperation<TContext> | undefined;\n\n createAttributeOperation(\n element: Element,\n attribute: Attr,\n context?: TemplateOperationContext\n ): TemplateOperation<TContext> | undefined;\n}\n\n/**\n * Cache for compiled Eta templates.\n *\n * Keys are the original template strings. The cache is the single owner of\n * template compilation. Neither the factory nor operations compile templates\n * directly.\n */\nexport interface EtaTemplateCache {\n get(source: string): EtaCompiledTemplate;\n\n clear(): void;\n\n /**\n * Number of compiled templates currently held in the cache.\n *\n * Optional because the structural minimum only describes what the\n * controller and factory need to drive a render pass. The\n * default `DefaultEtaTemplateCache` exposes `size`; tests and\n * diagnostics that read `cache.size` rely on this hook being\n * present, so the optional property documents the convention.\n *\n * `EtaTemplateController.#cache` is now a true ECMAScript\n * private field and cannot be observed from outside the\n * controller, but the host can still construct the cache\n * directly and read `size` for diagnostics.\n */\n readonly size?: number;\n}\n\n/**\n * Result type returned by Eta when compiling a template source.\n *\n * Some Eta configurations (async, plugins, custom tags) can change the\n * function shape; we keep the contract minimal and let EtaTemplateCache\n * implementation choose the actual compiler.\n */\nexport type EtaCompileFn = (\n source: string\n) => EtaCompiledTemplate;\n\n/**\n * Minimal surface required by `EtaTemplateController` on its host.\n *\n * The controller reads the root element and may register local async\n * work with the host so the render pass participates in the host's\n * `updateComplete` contract.\n *\n * Anything else — the current scope, compute revision, the compute\n * controller — is read by the host-supplied `args` / `context`\n * callbacks inside `EtaTemplateControllerOptions`, NOT by the\n * controller. That keeps the controller reusable outside\n * `BdScopedElement`.\n */\nexport interface EtaTemplateHost {\n /**\n * Owning boundary. Must be an `Element` because the controller marks\n * it as a traversal boundary (only elements own attributes and\n * children — text/document roots cannot act as Eta boundaries).\n */\n readonly root: Element;\n\n /**\n * Track local async work — when provided, the controller registers\n * the render pass under its own instance as the key so the host's\n * `updateComplete` waits for the render to finish before resolving.\n *\n * Mirrors the signature used by `BdNodeElement.trackLocalWork`.\n */\n trackLocalWork?<T>(\n key: unknown,\n work: PromiseLike<T>\n ): Promise<T>;\n}\n\n/**\n * Options accepted by `EtaTemplateController`.\n *\n * The controller is scope-agnostic. Everything reactive is funnelled\n * through `args`, `enabled`, and `context` callbacks so the controller\n * stays usable outside `BdScopedElement`.\n */\nexport interface EtaTemplateControllerOptions<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = object\n> {\n /**\n * Args the controller reacts to. The Task re-runs whenever the\n * returned tuple changes; the default comparison is `@lit/task`'s\n * `shallowArrayEquals`. Override via `argsEqual` for custom\n * equality (e.g. structural compare on a single object argument).\n *\n * Hosts typically return reactive properties of their own state:\n *\n * ```ts\n * args: () => [\n * this.currentScope,\n * this.compute.revision,\n * this.isComputeEnabled\n * ] as const\n * ```\n */\n args: () => TArgs;\n\n /**\n * Optional equality check for `args`. Forwarded to the underlying\n * `@lit/task` so the controller re-runs only when the returned\n * tuple actually changes by the host's definition.\n *\n * Return `true` when the old tuple and the new tuple should be\n * treated as equal (no rerun); return `false` to force a rerun.\n * When omitted, `@lit/task` uses its built-in\n * `shallowArrayEquals` which compares tuple elements by reference\n * (object identity for non-primitives).\n */\n argsEqual?: (\n oldArgs: TArgs,\n newArgs: TArgs\n ) => boolean;\n\n /**\n * Optional gate. When `enabled` returns `false` the controller skips\n * the render pass. When enabled returns false, the task completes\n * with undefined.\n *\n * The default is \"always enabled\".\n */\n enabled?: (args: TArgs) => boolean;\n\n /**\n * Context builder. Returns the value handed to each\n * `TemplateOperation.execute()` call.\n *\n * Returning `undefined` short-circuits the render pass for the\n * current args — the index keeps its operations but the renderer\n * does not invoke them. This is the right behaviour when the args\n * describe a state where rendering would produce invalid output\n * (for example, a scope that has not yet been published).\n *\n * Hosts that want the recommended `$scope` / `$compute` shape can\n * spread the scope entry, then attach `$scope` and `$compute`\n * fields to the returned context object. The controller does NOT\n * build that shape for the host — see `DefaultEtaContext` for the\n * documented shape.\n */\n context: (args: TArgs) => TContext | undefined;\n\n /**\n * Optional override used by tests to inject a deterministic index.\n *\n * The callback receives the resolved factory so the index can be\n * constructed with the same factory the controller will use. The\n * returned object must:\n *\n * - implement `TemplateIndexSink` so the observer can feed it\n * mutations;\n * - be iterable so the controller can render every operation;\n * - expose `disposeAll()` so the controller can clean up on\n * disconnect.\n *\n * `EtaTemplateIndex` satisfies this shape; tests may return a fake.\n */\n createIndex?: (\n factory: TemplateOperationFactory<TContext>\n ) => EtaTemplateIndexLike<TContext>;\n\n /**\n * Optional allowlist of attribute names the factory should\n * consider. When provided, attributes outside the list are skipped.\n *\n * When omitted, the factory defaults to observing every attribute.\n */\n attributeNames?: readonly string[];\n\n /**\n * When `true`, the controller tells the factory to ignore any\n * attribute allowlist and observe every attribute. Useful when\n * templates use non-standard attribute names (e.g. plain `href`\n * with a template literal).\n *\n * The controller owns its default factory privately; this option\n * controls the factory and observer that the controller builds.\n */\n observeAllAttributes?: boolean;\n\n /**\n * Optional predicate evaluated by the controller to decide whether\n * an `Element` is a topology boundary (i.e. owned by a different\n * controller).\n *\n * Threaded through to the index so the index's traversal and the\n * host's own child accounting share a single rule. Without this\n * predicate the index still walks the host's subtree but cannot\n * tell apart foreign boundaries.\n *\n * ⚠️ **Stable-on-raw-DOM.** The predicate MUST work on raw DOM\n * shape (tag names, marker attributes) rather than prototype-side\n * state such as `instanceof BdScopedElement` or runtime brands\n * set in custom-element classes. The parent index may scan a\n * subtree before a nested custom element upgrades; without a\n * stable raw-DOM check the parent index will silently absorb the\n * nested scope's templates. See `EtaTemplateIndexOptions.\n * isBoundaryElement` for full rationale and example patterns.\n *\n * Example pattern (tag-name based, stable before upgrade):\n *\n * ```ts\n * isBoundaryElement: (element) => {\n * const name = element.localName;\n * return (\n * name === 'bd-context' ||\n * name === 'bd-product' ||\n * name === 'bd-option'\n * );\n * }\n * ```\n */\n isBoundaryElement?: (element: Element) => boolean;\n\n /**\n * Optional per-operation error handler.\n *\n * **Continue vs. throw behaviour:**\n *\n * - When this handler is provided, the render loop catches errors\n * thrown by individual operations and continues with the next\n * operation. The render pass is considered successful and the\n * Task resolves normally with the most recent context. This is\n * the right behaviour when callers want to observe partial\n * failures (e.g. telemetry, fallback rendering) without aborting\n * the whole render.\n * - When omitted, the first error thrown by an operation propagates\n * out of the render loop, the Task transitions to its error\n * state, and `updateComplete` rejects. The previous successful\n * render's output stays in the DOM because no later operation\n * overwrites it.\n *\n * Mixing both modes is not supported — there is no way to observe\n * the error AND propagate it in a single render pass. If both are\n * needed, use the handler here and re-throw explicitly from inside\n * it.\n */\n onRenderError?: (\n error: unknown,\n operation: TemplateOperation<TContext>\n ) => void;\n\n /**\n * Optional schedule for `EtaMutationBatcher` flushes (round 6).\n *\n * Controls how the controller coalesces DOM mutations before\n * forwarding them to the index. Defaults to `'animation-frame'`\n * when omitted, which matches the design doc's recommended\n * production default for AEM / SPA-style bursty DOM updates.\n *\n * Schedules:\n *\n * - `'microtask'` — lowest latency. The flush runs on the next\n * microtask. Useful for tests that want deterministic flushes\n * via `await new Promise<void>(r => queueMicrotask(r))`.\n * - `'animation-frame'` — flush via `requestAnimationFrame`.\n * Recommended default; coalesces DOM churn before paint.\n * - `'idle'` — flush via `requestIdleCallback` (with a\n * `setTimeout` fallback). Lowest priority.\n * - `'manual'` — never auto-flush. The host owns the lifecycle\n * and must call `controller.flushMutations()` /\n * `controller.flushAndRender()` itself. Intended for\n * host-driven lifecycles (for example, AEM block decoration\n * flows where the host knows exactly when a decoration phase\n * ends).\n *\n * **Has no effect on tests that bypass the controller.** Tests\n * that drive the observer directly still observe records through\n * the legacy `TemplateIndexSink` path because they construct\n * the observer with an explicit `sink` rather than relying on\n * the controller's batcher wiring.\n *\n * **Has no effect when a custom observer is injected.** The\n * controller owns the batcher; consumers that want a different\n * forwarding destination should construct their own observer\n * and skip the controller's batcher.\n */\n mutationBatchSchedule?: EtaMutationBatchScheduleOption;\n}\n\n/**\n * Options accepted by `EtaTemplateIndex`.\n *\n * The controller passes these through to the index it constructs. The\n * index is otherwise opaque; boundary detection and ownership rules\n * live here so the index does not need to know about products,\n * options, or scoped elements.\n */\nexport interface EtaTemplateIndexOptions {\n /**\n * Predicate that decides whether an `Element` should be treated as a\n * traversal boundary.\n *\n * When the predicate returns `true` for an element:\n *\n * - If the element is this index's own root, it is still indexed as\n * the local boundary. Its own attributes and owned descendants are\n * collected.\n * - If the element is not this index's root, it is a foreign\n * boundary. The index does not collect the foreign boundary's\n * attributes and does not descend into its subtree.\n *\n * The predicate is also consulted during mutation entry points: an\n * attribute or text mutation on a foreign boundary element or its\n * descendants is ignored.\n *\n * ⚠️ **Stable-on-raw-DOM invariant.** The predicate MUST work on\n * raw DOM shape (e.g. `element.localName`, `element.tagName`,\n * marker attributes) rather than on prototype-side state such as\n * `element instanceof BdScopedElement` or a runtime brand set in\n * a custom-element class. The reason: the parent index may scan\n * a subtree before a nested custom element upgrades, in which\n * case the nested element has not yet been augmented with the\n * brand and the parent index would accidentally collect its\n * templates. No DOM mutation necessarily fires later when the\n * upgrade completes, so the parent index may keep the wrong\n * operations. Prefer tag-name or marker-attribute checks so the\n * classification is stable from the moment the element is\n * parsed.\n *\n * Example patterns:\n *\n * ```ts\n * // Tag-name based — works on raw DOM, no upgrade required.\n * isBoundaryElement: (element) => {\n * const name = element.localName;\n * return (\n * name === 'bd-context' ||\n * name === 'bd-product' ||\n * name === 'bd-option'\n * );\n * }\n * ```\n *\n * ```ts\n * // Marker-attribute based — also stable before upgrade.\n * isBoundaryElement: (element) =>\n * element.hasAttribute('bd-scope') ||\n * element.matches('bd-context,bd-product,bd-option')\n * ```\n *\n * ```ts\n * // Brand + tag-name fallback — keep the runtime brand as a fast\n * path for already-upgraded hosts, but always fall back to a\n // stable raw-DOM check so the parent index stays correct while\n // the subtree is upgrading.\n * isBoundaryElement: (element) =>\n * isBdScopedElement(element) ||\n * element.matches('bd-context,bd-product,bd-option')\n * ```\n */\n readonly isBoundaryElement?: (\n element: Element\n ) => boolean;\n\n /**\n * Root owned by this index.\n *\n * Mutations received by the index that target nodes outside the\n * subtree rooted at `root` are dropped. Without `root`, the index\n * accepts every mutation it receives — callers that wire a\n * parent observer must set `root` to prevent nested-scope leakage.\n *\n * The root is an `Element` for light-DOM indices and a\n * `ShadowRoot` for shadow-DOM indices. The controller walks\n * the host's light subtree during `hostConnected` looking for\n * attached open shadow roots and creates a per-root index for\n * each one. Tests may pass either kind.\n */\n readonly root?: Element | ShadowRoot;\n}\n\n/**\n * Structural minimum required for an Eta template index.\n *\n * The real `EtaTemplateIndex` (in `./template-index.js`) implements this\n * shape. Splitting the contract out lets `types.ts` stay free of the\n * index implementation, avoiding a circular import.\n */\nexport interface EtaTemplateIndexLike<\n TContext = object\n> extends TemplateIndexSink,\n Iterable<TemplateOperation<TContext>> {\n disposeAll(): void;\n\n initialize(root: ParentNode): void;\n\n /**\n * Number of operations currently held by the index.\n *\n * The real `EtaTemplateIndex` (in `./template-index.js`) is the\n * sole intended implementer; the field is required because\n * diagnostics and tests read it directly without an\n * `?? 0` fallback. Fake indices used in tests must provide\n * `get size(): number { return 0; }` (or equivalent).\n */\n readonly size: number;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/eta/types.ts"],"names":[],"mappings":"AAAA,mBAAmB","sourcesContent":["// src/eta/types.ts\n\n/**\n * Shared type definitions for the Eta template subsystem.\n *\n * The subsystem is split into small, single-responsibility pieces:\n *\n * MutationObserver\n * -> translates DOM mutations into TemplateIndexMutation values\n *\n * EtaTemplateIndex\n * -> maintains the operation index for a scoped boundary\n *\n * TemplateOperationFactory\n * -> discovers templates and compiles them via EtaTemplateCache\n *\n * TextOperation / AttributeOperation\n * -> render a single DOM target against a context\n *\n * EtaTemplateController\n * -> owns lifecycle, the Task schedule, and the context builder\n *\n * EtaMutationBatcher\n * -> coalesces MutationRecord batches into a single\n * index flush per scheduled tick (round 6)\n *\n * Most of these layers are Eta-agnostic. Only the cache and factory know how\n * to talk to Eta. Everything else works on plain DOM nodes.\n */\n\n/**\n * Schedule mode for `EtaMutationBatcher` flushes.\n *\n * Defined here (rather than in `batcher.ts`) so consumers can\n * import the type without pulling the batcher module into a\n * type-only import cycle. The batcher re-exports the type for\n * direct importers.\n *\n * - `microtask` — flush via `queueMicrotask`. Lowest latency.\n * - `animation-frame` — flush via `requestAnimationFrame`.\n * Recommended default for browser hosts.\n * - `idle` — flush via `requestIdleCallback` (with a `setTimeout`\n * fallback). Lowest priority.\n * - `manual` — never auto-flush; the host owns the lifecycle.\n */\nexport type EtaMutationBatchSchedule =\n | 'microtask'\n | 'animation-frame'\n | 'idle'\n | 'manual';\n\nexport type EtaMutationBatchScheduleResolver = () =>\n | EtaMutationBatchSchedule\n | undefined;\n\nexport type EtaMutationBatchScheduleOption =\n | EtaMutationBatchSchedule\n | EtaMutationBatchScheduleResolver;\n\n/**\n * A precompiled Eta template function.\n *\n * `data` is the rendering context. The function returns a string. Eta exposes\n * `compile` as a member of the Eta class, so the function signature is\n * `(data?) => string`.\n */\nexport type EtaCompiledTemplate = (\n data?: object\n) => string;\n\n/**\n * Discriminated union describing a single change that should be reflected\n * in the template index.\n *\n * - `add` introduces a node that has not been seen before\n * - `remove` drops a node and all of its descendants\n * - `attribute` re-runs attribute discovery for a single attribute on an\n * element that is already part of the index\n * - `text` re-runs text discovery for a text node that is already part of\n * the index\n */\nexport type TemplateIndexMutation =\n | {\n readonly type: 'add';\n readonly node: Node;\n }\n | {\n readonly type: 'remove';\n readonly node: Node;\n }\n | {\n readonly type : 'attribute';\n readonly element : Element;\n readonly attribute: string;\n }\n | {\n readonly type: 'text';\n readonly node: Text;\n };\n\n/**\n * Anything that can absorb a TemplateIndexMutation.\n *\n * EtaMutationObserver and EtaTemplateIndex both satisfy this interface so\n * that they can be swapped in tests or composed by other layers.\n *\n * `apply` returns `true` only when the sink actually changed its\n * index as a result of the mutation. Returning `false` signals that\n * the mutation was ignored (the node was outside the index's root,\n * the attribute had no template, the boundary skipped the subtree,\n * etc.). The observer uses this signal to decide whether a render\n * pass needs to be scheduled, so returning the wrong value directly\n * causes either stale renders or unnecessary work.\n */\nexport interface TemplateIndexSink {\n apply(mutation: TemplateIndexMutation): boolean;\n}\n\n/**\n * A single immutable rendering operation.\n *\n * Operations are owned by a single DOM target (a text node or an attribute\n * of an element) and know nothing about template discovery, the index, or\n * the controller. They simply render their target against a context.\n *\n * The `dispose` method is currently a no-op for the built-in operations but\n * exists as an extension point for future implementations that may hold\n * observers, event listeners, or other resources.\n */\nexport interface TemplateOperation<\n TContext = object\n> {\n readonly owner: Node;\n\n readonly kind: 'text' | 'attribute';\n\n execute(context: TContext): void;\n\n dispose(): void;\n}\n\n/**\n * Structural surface used by `TemplateOperationContext.sources`.\n *\n * Defined here (rather than imported from `source-registry.ts`) so\n * `types.ts` stays a leaf module per the eta subsystem layout.\n * Any object that satisfies these four methods can stand in for\n * the real `EtaTemplateSourceRegistry` — tests can pass mocks\n * without pulling the registry module into a type-only cycle.\n */\nexport interface TemplateOperationSourceRegistry {\n readTextSource(node: Text): string | undefined;\n replaceTextSource(\n node: Text,\n source: string | undefined\n ): void;\n readAttributeSource(\n element: Element,\n name: string\n ): string | undefined;\n setAttributeSource(\n element: Element,\n name: string,\n source: string\n ): void;\n}\n\n/**\n * Per-call context passed into `TemplateOperationFactory.create*`\n * methods.\n *\n * Carries dependencies that the factory needs at discovery time\n * but does not own. Today the only such dependency is the\n * `EtaTemplateSourceRegistry`, which preserves original template\n * sources across renders. By threading the registry through every\n * factory call (rather than storing it on the factory instance)\n * the factory stays stateless at the call site and can safely be\n * shared across multiple indices — the registry is always the\n * one owned by the calling index.\n *\n * The context is optional: factory implementations that do not\n * need preserved sources can simply ignore it. Test fakes can\n * pass `undefined`.\n */\nexport interface TemplateOperationContext {\n /** Source registry for preserved original template sources. */\n readonly sources?: TemplateOperationSourceRegistry;\n}\n\n/**\n * Factory contract for producing TemplateOperation instances from DOM nodes.\n *\n * The factory is the only Eta-aware discovery layer. The collector simply\n * forwards raw DOM nodes to the factory and stores whatever comes back.\n *\n * A factory returns `undefined` when a node does not contain a template,\n * which lets the index skip irrelevant subtrees.\n *\n * The optional `context` parameter carries dependencies that the\n * factory needs at discovery time but does not own — currently\n * the source registry. The factory treats it as advisory: a\n * missing context means the factory does not have access to\n * preserved sources, so post-render reads fall back to the live\n * DOM value.\n */\nexport interface TemplateOperationFactory<\n TContext = object\n> {\n createTextOperation(\n node: Text,\n context?: TemplateOperationContext\n ): TemplateOperation<TContext> | undefined;\n\n createAttributeOperation(\n element: Element,\n attribute: Attr,\n context?: TemplateOperationContext\n ): TemplateOperation<TContext> | undefined;\n}\n\n/**\n * Cache for compiled Eta templates.\n *\n * Keys are the original template strings. The cache is the single owner of\n * template compilation. Neither the factory nor operations compile templates\n * directly.\n */\nexport interface EtaTemplateCache {\n get(source: string): EtaCompiledTemplate;\n\n clear(): void;\n\n /**\n * Number of compiled templates currently held in the cache.\n *\n * Optional because the structural minimum only describes what the\n * controller and factory need to drive a render pass. The\n * default `DefaultEtaTemplateCache` exposes `size`; tests and\n * diagnostics that read `cache.size` rely on this hook being\n * present, so the optional property documents the convention.\n *\n * `EtaTemplateController.#cache` is now a true ECMAScript\n * private field and cannot be observed from outside the\n * controller, but the host can still construct the cache\n * directly and read `size` for diagnostics.\n */\n readonly size?: number;\n}\n\n/**\n * Result type returned by Eta when compiling a template source.\n *\n * Some Eta configurations (async, plugins, custom tags) can change the\n * function shape; we keep the contract minimal and let EtaTemplateCache\n * implementation choose the actual compiler.\n */\nexport type EtaCompileFn = (\n source: string\n) => EtaCompiledTemplate;\n\n/**\n * Minimal surface required by `EtaTemplateController` on its host.\n *\n * The controller reads the root element and may register local async\n * work with the host so the render pass participates in the host's\n * `updateComplete` contract.\n *\n * Anything else — the current scope, compute revision, the compute\n * controller — is read by the host-supplied `args` / `context`\n * callbacks inside `EtaTemplateControllerOptions`, NOT by the\n * controller. That keeps the controller reusable outside\n * `BdScopedElement`.\n */\nexport interface EtaTemplateHost {\n /**\n * Owning boundary. Must be an `Element` because the controller marks\n * it as a traversal boundary (only elements own attributes and\n * children — text/document roots cannot act as Eta boundaries).\n */\n readonly root: Element;\n\n /**\n * Track local async work — when provided, the controller registers\n * the render pass under its own instance as the key so the host's\n * `updateComplete` waits for the render to finish before resolving.\n *\n * Mirrors the signature used by `BdNodeElement.trackLocalWork`.\n */\n trackLocalWork?<T>(\n key: unknown,\n work: PromiseLike<T>\n ): Promise<T>;\n}\n\n/**\n * Options accepted by `EtaTemplateController`.\n *\n * The controller is scope-agnostic. Everything reactive is funnelled\n * through `args`, `enabled`, and `context` callbacks so the controller\n * stays usable outside `BdScopedElement`.\n */\nexport interface EtaTemplateControllerOptions<\n TArgs extends readonly unknown[] = readonly unknown[],\n TContext extends object = object\n> {\n /**\n * Args the controller reacts to. The Task re-runs whenever the\n * returned tuple changes; the default comparison is `@lit/task`'s\n * `shallowArrayEquals`. Override via `argsEqual` for custom\n * equality (e.g. structural compare on a single object argument).\n *\n * Hosts typically return reactive properties of their own state:\n *\n * ```ts\n * args: () => [\n * this.currentScope,\n * this.compute.revision,\n * this.isComputeEnabled\n * ] as const\n * ```\n */\n args: () => TArgs;\n\n /**\n * Optional equality check for `args`. Forwarded to the underlying\n * `@lit/task` so the controller re-runs only when the returned\n * tuple actually changes by the host's definition.\n *\n * Return `true` when the old tuple and the new tuple should be\n * treated as equal (no rerun); return `false` to force a rerun.\n * When omitted, `@lit/task` uses its built-in\n * `shallowArrayEquals` which compares tuple elements by reference\n * (object identity for non-primitives).\n */\n argsEqual?: (\n oldArgs: TArgs,\n newArgs: TArgs\n ) => boolean;\n\n /**\n * Optional gate. When `enabled` returns `false` the controller skips\n * the render pass. When enabled returns false, the task completes\n * with undefined.\n *\n * The default is \"always enabled\".\n */\n enabled?: (args: TArgs) => boolean;\n\n /**\n * Context builder. Returns (or resolves to) the value handed to each\n * `TemplateOperation.execute()` call.\n *\n * Returning `undefined` short-circuits the render pass for the\n * current args — the index keeps its operations but the renderer\n * does not invoke them. This is the right behaviour when the args\n * describe a state where rendering would produce invalid output\n * (for example, a scope that has not yet been published).\n *\n * Hosts that want the recommended `$scope` / `$compute` shape can\n * spread the scope entry, then attach `$scope` and `$compute`\n * fields to the returned context object. The controller does NOT\n * build that shape for the host — see `DefaultEtaContext` for the\n * documented shape.\n */\n context: (args: TArgs) =>\n | TContext\n | undefined\n | PromiseLike<TContext | undefined>;\n\n /**\n * Optional override used by tests to inject a deterministic index.\n *\n * The callback receives the resolved factory so the index can be\n * constructed with the same factory the controller will use. The\n * returned object must:\n *\n * - implement `TemplateIndexSink` so the observer can feed it\n * mutations;\n * - be iterable so the controller can render every operation;\n * - expose `disposeAll()` so the controller can clean up on\n * disconnect.\n *\n * `EtaTemplateIndex` satisfies this shape; tests may return a fake.\n */\n createIndex?: (\n factory: TemplateOperationFactory<TContext>\n ) => EtaTemplateIndexLike<TContext>;\n\n /**\n * Optional allowlist of attribute names the factory should\n * consider. When provided, attributes outside the list are skipped.\n *\n * When omitted, the factory defaults to observing every attribute.\n */\n attributeNames?: readonly string[];\n\n /**\n * When `true`, the controller tells the factory to ignore any\n * attribute allowlist and observe every attribute. Useful when\n * templates use non-standard attribute names (e.g. plain `href`\n * with a template literal).\n *\n * The controller owns its default factory privately; this option\n * controls the factory and observer that the controller builds.\n */\n observeAllAttributes?: boolean;\n\n /**\n * Optional predicate evaluated by the controller to decide whether\n * an `Element` is a topology boundary (i.e. owned by a different\n * controller).\n *\n * Threaded through to the index so the index's traversal and the\n * host's own child accounting share a single rule. Without this\n * predicate the index still walks the host's subtree but cannot\n * tell apart foreign boundaries.\n *\n * ⚠️ **Stable-on-raw-DOM.** The predicate MUST work on raw DOM\n * shape (tag names, marker attributes) rather than prototype-side\n * state such as `instanceof BdScopedElement` or runtime brands\n * set in custom-element classes. The parent index may scan a\n * subtree before a nested custom element upgrades; without a\n * stable raw-DOM check the parent index will silently absorb the\n * nested scope's templates. See `EtaTemplateIndexOptions.\n * isBoundaryElement` for full rationale and example patterns.\n *\n * Example pattern (tag-name based, stable before upgrade):\n *\n * ```ts\n * isBoundaryElement: (element) => {\n * const name = element.localName;\n * return (\n * name === 'bd-context' ||\n * name === 'bd-product' ||\n * name === 'bd-option'\n * );\n * }\n * ```\n */\n isBoundaryElement?: (element: Element) => boolean;\n\n /**\n * Optional per-operation error handler.\n *\n * **Continue vs. throw behaviour:**\n *\n * - When this handler is provided, the render loop catches errors\n * thrown by individual operations and continues with the next\n * operation. The render pass is considered successful and the\n * Task resolves normally with the most recent context. This is\n * the right behaviour when callers want to observe partial\n * failures (e.g. telemetry, fallback rendering) without aborting\n * the whole render.\n * - When omitted, the first error thrown by an operation propagates\n * out of the render loop, the Task transitions to its error\n * state, and `updateComplete` rejects. The previous successful\n * render's output stays in the DOM because no later operation\n * overwrites it.\n *\n * Mixing both modes is not supported — there is no way to observe\n * the error AND propagate it in a single render pass. If both are\n * needed, use the handler here and re-throw explicitly from inside\n * it.\n */\n onRenderError?: (\n error: unknown,\n operation: TemplateOperation<TContext>\n ) => void;\n\n /**\n * Optional schedule for `EtaMutationBatcher` flushes (round 6).\n *\n * Controls how the controller coalesces DOM mutations before\n * forwarding them to the index. Defaults to `'animation-frame'`\n * when omitted, which matches the design doc's recommended\n * production default for AEM / SPA-style bursty DOM updates.\n *\n * Schedules:\n *\n * - `'microtask'` — lowest latency. The flush runs on the next\n * microtask. Useful for tests that want deterministic flushes\n * via `await new Promise<void>(r => queueMicrotask(r))`.\n * - `'animation-frame'` — flush via `requestAnimationFrame`.\n * Recommended default; coalesces DOM churn before paint.\n * - `'idle'` — flush via `requestIdleCallback` (with a\n * `setTimeout` fallback). Lowest priority.\n * - `'manual'` — never auto-flush. The host owns the lifecycle\n * and must call `controller.flushMutations()` /\n * `controller.flushAndRender()` itself. Intended for\n * host-driven lifecycles (for example, AEM block decoration\n * flows where the host knows exactly when a decoration phase\n * ends).\n *\n * **Has no effect on tests that bypass the controller.** Tests\n * that drive the observer directly still observe records through\n * the legacy `TemplateIndexSink` path because they construct\n * the observer with an explicit `sink` rather than relying on\n * the controller's batcher wiring.\n *\n * **Has no effect when a custom observer is injected.** The\n * controller owns the batcher; consumers that want a different\n * forwarding destination should construct their own observer\n * and skip the controller's batcher.\n */\n mutationBatchSchedule?: EtaMutationBatchScheduleOption;\n}\n\n/**\n * Options accepted by `EtaTemplateIndex`.\n *\n * The controller passes these through to the index it constructs. The\n * index is otherwise opaque; boundary detection and ownership rules\n * live here so the index does not need to know about products,\n * options, or scoped elements.\n */\nexport interface EtaTemplateIndexOptions {\n /**\n * Predicate that decides whether an `Element` should be treated as a\n * traversal boundary.\n *\n * When the predicate returns `true` for an element:\n *\n * - If the element is this index's own root, it is still indexed as\n * the local boundary. Its own attributes and owned descendants are\n * collected.\n * - If the element is not this index's root, it is a foreign\n * boundary. The index does not collect the foreign boundary's\n * attributes and does not descend into its subtree.\n *\n * The predicate is also consulted during mutation entry points: an\n * attribute or text mutation on a foreign boundary element or its\n * descendants is ignored.\n *\n * ⚠️ **Stable-on-raw-DOM invariant.** The predicate MUST work on\n * raw DOM shape (e.g. `element.localName`, `element.tagName`,\n * marker attributes) rather than on prototype-side state such as\n * `element instanceof BdScopedElement` or a runtime brand set in\n * a custom-element class. The reason: the parent index may scan\n * a subtree before a nested custom element upgrades, in which\n * case the nested element has not yet been augmented with the\n * brand and the parent index would accidentally collect its\n * templates. No DOM mutation necessarily fires later when the\n * upgrade completes, so the parent index may keep the wrong\n * operations. Prefer tag-name or marker-attribute checks so the\n * classification is stable from the moment the element is\n * parsed.\n *\n * Example patterns:\n *\n * ```ts\n * // Tag-name based — works on raw DOM, no upgrade required.\n * isBoundaryElement: (element) => {\n * const name = element.localName;\n * return (\n * name === 'bd-context' ||\n * name === 'bd-product' ||\n * name === 'bd-option'\n * );\n * }\n * ```\n *\n * ```ts\n * // Marker-attribute based — also stable before upgrade.\n * isBoundaryElement: (element) =>\n * element.hasAttribute('bd-scope') ||\n * element.matches('bd-context,bd-product,bd-option')\n * ```\n *\n * ```ts\n * // Brand + tag-name fallback — keep the runtime brand as a fast\n * path for already-upgraded hosts, but always fall back to a\n // stable raw-DOM check so the parent index stays correct while\n // the subtree is upgrading.\n * isBoundaryElement: (element) =>\n * isBdScopedElement(element) ||\n * element.matches('bd-context,bd-product,bd-option')\n * ```\n */\n readonly isBoundaryElement?: (\n element: Element\n ) => boolean;\n\n /**\n * Root owned by this index.\n *\n * Mutations received by the index that target nodes outside the\n * subtree rooted at `root` are dropped. Without `root`, the index\n * accepts every mutation it receives — callers that wire a\n * parent observer must set `root` to prevent nested-scope leakage.\n *\n * The root is an `Element` for light-DOM indices and a\n * `ShadowRoot` for shadow-DOM indices. The controller walks\n * the host's light subtree during `hostConnected` looking for\n * attached open shadow roots and creates a per-root index for\n * each one. Tests may pass either kind.\n */\n readonly root?: Element | ShadowRoot;\n}\n\n/**\n * Structural minimum required for an Eta template index.\n *\n * The real `EtaTemplateIndex` (in `./template-index.js`) implements this\n * shape. Splitting the contract out lets `types.ts` stay free of the\n * index implementation, avoiding a circular import.\n */\nexport interface EtaTemplateIndexLike<\n TContext = object\n> extends TemplateIndexSink,\n Iterable<TemplateOperation<TContext>> {\n disposeAll(): void;\n\n initialize(root: ParentNode): void;\n\n /**\n * Number of operations currently held by the index.\n *\n * The real `EtaTemplateIndex` (in `./template-index.js`) is the\n * sole intended implementer; the field is required because\n * diagnostics and tests read it directly without an\n * `?? 0` fallback. Fake indices used in tests must provide\n * `get size(): number { return 0; }` (or equivalent).\n */\n readonly size: number;\n}\n"]}
@@ -1,2 +1,2 @@
1
1
  import type { RenderContext } from '../context.js';
2
- export declare const handleHide: (el: HTMLElement, ctx: RenderContext) => void;
2
+ export declare const handleHide: (el: HTMLElement, ctx: RenderContext) => Promise<void>;
@@ -49,7 +49,7 @@ function resetAppliedHideMode(el) {
49
49
  resetHideMode(el, previousMode);
50
50
  HIDE_MODES.delete(el);
51
51
  }
52
- export const handleHide = (el, ctx) => {
52
+ export const handleHide = async (el, ctx) => {
53
53
  const { storeHide, storeHideType } = el.dataset;
54
54
  if (!storeHide) {
55
55
  resetAppliedHideMode(el);
@@ -57,7 +57,7 @@ export const handleHide = (el, ctx) => {
57
57
  }
58
58
  applyHideMode(el, normalizeHideMode(storeHideType), Compiler.boolean({
59
59
  expr: storeHide,
60
- ctx: toDSLContext(ctx)
60
+ ctx: await toDSLContext(ctx)
61
61
  }));
62
62
  };
63
63
  //# sourceMappingURL=hide.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hide.js","sourceRoot":"","sources":["../../../../src/renders/attributes/hide.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,UAAU,GAAG,IAAI,OAAO,EAAyB,CAAC;AAExD,SAAS,iBAAiB,CACxB,IAAa;IAEb,IACE,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,YAAY,EACrB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,aAAa,CACpB,EAAe,EACf,IAAc,EACd,IAAa;IAEb,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAExC,IACE,YAAY;QACZ,YAAY,KAAK,IAAI,EACrB,CAAC;QACD,aAAa,CACX,EAAE,EACF,YAAY,CACb,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,aAAa,CACX,EAAE,EACF,IAAI,CACL,CAAC;QACF,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtB,OAAO;IACT,CAAC;IAED,UAAU,CAAC,GAAG,CACZ,EAAE,EACF,IAAI,CACL,CAAC;IAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;QACvB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;AAC5B,CAAC;AAED,SAAS,aAAa,CACpB,EAAe,EACf,IAAc;IAEd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IAED,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,oBAAoB,CAC3B,EAAe;IAEf,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAExC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;IACT,CAAC;IAED,aAAa,CACX,EAAE,EACF,YAAY,CACb,CAAC;IACF,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,EAAe,EACf,GAAkB,EACZ,EAAE;IACR,MAAM,EACJ,SAAS,EACT,aAAa,EACd,GAAG,EAAE,CAAC,OAAO,CAAC;IAEf,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,oBAAoB,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,aAAa,CACX,EAAE,EACF,iBAAiB,CAAC,aAAa,CAAC,EAChC,QAAQ,CAAC,OAAO,CAAC;QACf,IAAI,EAAE,SAAS;QACf,GAAG,EAAG,YAAY,CAAC,GAAG,CAAC;KACxB,CAAC,CACH,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { Compiler } from '@/dsl/compilers/index.js';\nimport type { RenderContext } from '../context.js';\nimport { toDSLContext } from '../context.js';\n\ntype HideMode = 'display' | 'opacity' | 'visibility';\n\nconst HIDE_MODES = new WeakMap<HTMLElement, HideMode>();\n\nfunction normalizeHideMode(\n mode?: string\n): HideMode {\n if (\n mode === 'opacity' ||\n mode === 'visibility'\n ) {\n return mode;\n }\n\n return 'display';\n}\n\nfunction applyHideMode(\n el: HTMLElement,\n mode: HideMode,\n hide: boolean\n): void {\n const previousMode = HIDE_MODES.get(el);\n\n if (\n previousMode &&\n previousMode !== mode\n ) {\n resetHideMode(\n el,\n previousMode\n );\n }\n\n if (!hide) {\n resetHideMode(\n el,\n mode\n );\n HIDE_MODES.delete(el);\n return;\n }\n\n HIDE_MODES.set(\n el,\n mode\n );\n\n if (mode === 'opacity') {\n el.style.opacity = '0';\n return;\n }\n\n if (mode === 'visibility') {\n el.style.visibility = 'hidden';\n return;\n }\n\n el.style.display = 'none';\n}\n\nfunction resetHideMode(\n el: HTMLElement,\n mode: HideMode\n): void {\n if (mode === 'opacity') {\n el.style.opacity = '';\n return;\n }\n\n if (mode === 'visibility') {\n el.style.visibility = '';\n return;\n }\n\n el.style.display = '';\n}\n\nfunction resetAppliedHideMode(\n el: HTMLElement\n): void {\n const previousMode = HIDE_MODES.get(el);\n\n if (!previousMode) {\n return;\n }\n\n resetHideMode(\n el,\n previousMode\n );\n HIDE_MODES.delete(el);\n}\n\nexport const handleHide = (\n el: HTMLElement,\n ctx: RenderContext\n): void => {\n const {\n storeHide,\n storeHideType\n } = el.dataset;\n\n if (!storeHide) {\n resetAppliedHideMode(el);\n return;\n }\n\n applyHideMode(\n el,\n normalizeHideMode(storeHideType),\n Compiler.boolean({\n expr: storeHide,\n ctx : toDSLContext(ctx)\n })\n );\n};\n"]}
1
+ {"version":3,"file":"hide.js","sourceRoot":"","sources":["../../../../src/renders/attributes/hide.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,UAAU,GAAG,IAAI,OAAO,EAAyB,CAAC;AAExD,SAAS,iBAAiB,CACxB,IAAa;IAEb,IACE,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,YAAY,EACrB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,aAAa,CACpB,EAAe,EACf,IAAc,EACd,IAAa;IAEb,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAExC,IACE,YAAY;QACZ,YAAY,KAAK,IAAI,EACrB,CAAC;QACD,aAAa,CACX,EAAE,EACF,YAAY,CACb,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,aAAa,CACX,EAAE,EACF,IAAI,CACL,CAAC;QACF,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtB,OAAO;IACT,CAAC;IAED,UAAU,CAAC,GAAG,CACZ,EAAE,EACF,IAAI,CACL,CAAC;IAEF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;QACvB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;AAC5B,CAAC;AAED,SAAS,aAAa,CACpB,EAAe,EACf,IAAc;IAEd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IAED,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,oBAAoB,CAC3B,EAAe;IAEf,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAExC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;IACT,CAAC;IAED,aAAa,CACX,EAAE,EACF,YAAY,CACb,CAAC;IACF,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,EAAe,EACf,GAAkB,EACH,EAAE;IACjB,MAAM,EACJ,SAAS,EACT,aAAa,EACd,GAAG,EAAE,CAAC,OAAO,CAAC;IAEf,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,oBAAoB,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,aAAa,CACX,EAAE,EACF,iBAAiB,CAAC,aAAa,CAAC,EAChC,QAAQ,CAAC,OAAO,CAAC;QACf,IAAI,EAAE,SAAS;QACf,GAAG,EAAG,MAAM,YAAY,CAAC,GAAG,CAAC;KAC9B,CAAC,CACH,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { Compiler } from '@/dsl/compilers/index.js';\nimport type { RenderContext } from '../context.js';\nimport { toDSLContext } from '../context.js';\n\ntype HideMode = 'display' | 'opacity' | 'visibility';\n\nconst HIDE_MODES = new WeakMap<HTMLElement, HideMode>();\n\nfunction normalizeHideMode(\n mode?: string\n): HideMode {\n if (\n mode === 'opacity' ||\n mode === 'visibility'\n ) {\n return mode;\n }\n\n return 'display';\n}\n\nfunction applyHideMode(\n el: HTMLElement,\n mode: HideMode,\n hide: boolean\n): void {\n const previousMode = HIDE_MODES.get(el);\n\n if (\n previousMode &&\n previousMode !== mode\n ) {\n resetHideMode(\n el,\n previousMode\n );\n }\n\n if (!hide) {\n resetHideMode(\n el,\n mode\n );\n HIDE_MODES.delete(el);\n return;\n }\n\n HIDE_MODES.set(\n el,\n mode\n );\n\n if (mode === 'opacity') {\n el.style.opacity = '0';\n return;\n }\n\n if (mode === 'visibility') {\n el.style.visibility = 'hidden';\n return;\n }\n\n el.style.display = 'none';\n}\n\nfunction resetHideMode(\n el: HTMLElement,\n mode: HideMode\n): void {\n if (mode === 'opacity') {\n el.style.opacity = '';\n return;\n }\n\n if (mode === 'visibility') {\n el.style.visibility = '';\n return;\n }\n\n el.style.display = '';\n}\n\nfunction resetAppliedHideMode(\n el: HTMLElement\n): void {\n const previousMode = HIDE_MODES.get(el);\n\n if (!previousMode) {\n return;\n }\n\n resetHideMode(\n el,\n previousMode\n );\n HIDE_MODES.delete(el);\n}\n\nexport const handleHide = async (\n el: HTMLElement,\n ctx: RenderContext\n): Promise<void> => {\n const {\n storeHide,\n storeHideType\n } = el.dataset;\n\n if (!storeHide) {\n resetAppliedHideMode(el);\n return;\n }\n\n applyHideMode(\n el,\n normalizeHideMode(storeHideType),\n Compiler.boolean({\n expr: storeHide,\n ctx : await toDSLContext(ctx)\n })\n );\n};\n"]}
@@ -1,5 +1,6 @@
1
1
  export const isPresent = (value) => value !== null &&
2
2
  value !== undefined &&
3
+ (typeof value !== 'string' || value.trim() !== '') &&
3
4
  (typeof value !== 'number' || Number.isFinite(value));
4
5
  export const resolveAnchor = (el) => el instanceof HTMLAnchorElement
5
6
  ? el
@@ -1 +1 @@
1
- {"version":3,"file":"utilty.js","sourceRoot":"","sources":["../../../../src/renders/attributes/utilty.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,KAAc,EACL,EAAE,CACX,KAAK,KAAK,IAAI;IACd,KAAK,KAAK,SAAS;IACnB,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAExD,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,EAAe,EACW,EAAE,CAC5B,EAAE,YAAY,iBAAiB;IAC7B,CAAC,CAAC,EAAE;IACJ,CAAC,CAAC,EAAE,CAAC,aAAa,CAAoB,GAAG,CAAC,CAAC;AAQ/C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAyB,EACzB,MAAoB,EACpB,aAAgB,EAChB,WAAqC,EACrC,gBAAwB,EAClB,EAAE;IACR,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAE1B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,KAAK,KAAK,aAAa,CAAC;QACzC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,KAAK,EACL,MAAM,CAAC,KAAK,CAAC,EACb,QAAQ,EACR,QAAQ,CACT,CAAC;QAEF,MAAM,CAAC,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC","sourcesContent":["export const isPresent = (\n value: unknown\n): boolean =>\n value !== null &&\n value !== undefined &&\n (typeof value !== 'number' || Number.isFinite(value));\n\nexport const resolveAnchor = (\n el: HTMLElement\n): HTMLAnchorElement | null =>\n el instanceof HTMLAnchorElement\n ? el\n : el.querySelector<HTMLAnchorElement>('a');\n\nexport type SelectOptionFormatter<T> = (\n value: T,\n index: number,\n values: readonly T[]\n) => string;\n\nexport const buildSelectOptions = <T>(\n select: HTMLSelectElement,\n values: readonly T[],\n selectedValue: T,\n formatLabel: SelectOptionFormatter<T>,\n setAttributeName: string\n): void => {\n select.options.length = 0;\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n const selected = value === selectedValue;\n const label = formatLabel(value, index, values);\n const option = new Option(\n label,\n String(value),\n selected,\n selected\n );\n\n option.setAttribute(setAttributeName, String(value));\n select.add(option);\n }\n};\n"]}
1
+ {"version":3,"file":"utilty.js","sourceRoot":"","sources":["../../../../src/renders/attributes/utilty.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,KAAc,EACL,EAAE,CACX,KAAK,KAAK,IAAI;IACd,KAAK,KAAK,SAAS;IACnB,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAClD,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAExD,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,EAAe,EACW,EAAE,CAC5B,EAAE,YAAY,iBAAiB;IAC7B,CAAC,CAAC,EAAE;IACJ,CAAC,CAAC,EAAE,CAAC,aAAa,CAAoB,GAAG,CAAC,CAAC;AAQ/C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAyB,EACzB,MAAoB,EACpB,aAAgB,EAChB,WAAqC,EACrC,gBAAwB,EAClB,EAAE;IACR,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAE1B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,KAAK,KAAK,aAAa,CAAC;QACzC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,KAAK,EACL,MAAM,CAAC,KAAK,CAAC,EACb,QAAQ,EACR,QAAQ,CACT,CAAC;QAEF,MAAM,CAAC,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC","sourcesContent":["export const isPresent = (\n value: unknown\n): boolean =>\n value !== null &&\n value !== undefined &&\n (typeof value !== 'string' || value.trim() !== '') &&\n (typeof value !== 'number' || Number.isFinite(value));\n\nexport const resolveAnchor = (\n el: HTMLElement\n): HTMLAnchorElement | null =>\n el instanceof HTMLAnchorElement\n ? el\n : el.querySelector<HTMLAnchorElement>('a');\n\nexport type SelectOptionFormatter<T> = (\n value: T,\n index: number,\n values: readonly T[]\n) => string;\n\nexport const buildSelectOptions = <T>(\n select: HTMLSelectElement,\n values: readonly T[],\n selectedValue: T,\n formatLabel: SelectOptionFormatter<T>,\n setAttributeName: string\n): void => {\n select.options.length = 0;\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n const selected = value === selectedValue;\n const label = formatLabel(value, index, values);\n const option = new Option(\n label,\n String(value),\n selected,\n selected\n );\n\n option.setAttribute(setAttributeName, String(value));\n select.add(option);\n }\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { BdComputeResult } from '../compute/compute.expand.js';
2
- import type { BdScope } from '../contexts/context.scope.js';
3
- import type { Product, ProductOption, Store } from '@repobit/dex-store';
2
+ import { type BdScope, type BdScopeDeriveFn } from '../contexts/context.scope.js';
3
+ import type { Product, ProductOption } from '@repobit/dex-store';
4
4
  export type RenderContext = Readonly<{
5
5
  scope?: BdScope;
6
6
  store?: BdScope['store'];
@@ -79,26 +79,23 @@ export type TemplateStateContext = Readonly<{
79
79
  };
80
80
  };
81
81
  };
82
- scenarios: number;
83
82
  }>;
84
83
  /**
85
84
  * Shape consumed by Eta templates and the hide DSL.
86
85
  *
87
- * Mirrors the v1 `it.*` layout: `option` / `product` / `state` are flattened
88
- * DTOs, and `ctx` is an alias of `state`. `derived` is intentionally kept
89
- * nested (see {@link BdScopedTemplateContext}) v1 merged derived into the
90
- * root via `deepmerge`; v2 exposes it explicitly so consumers can tell
91
- * user-provided values apart from store-provided ones.
86
+ * Mirrors the v1 `it.*` layout. The base surface contains only
87
+ * `option`, `product`, `state`, and the `ctx` alias. User-derived values
88
+ * are deep-merged into this object at the root; the raw store and the full
89
+ * internal scope are intentionally not exposed.
92
90
  */
93
- export type TemplateDslContext = Readonly<{
94
- store: Store | undefined;
91
+ export type TemplateDslContext = Readonly<Record<string, unknown> & {
95
92
  product: TemplateProductContext | undefined;
96
93
  option: TemplateOptionContext | undefined;
97
94
  state: TemplateStateContext | undefined;
98
95
  ctx: TemplateStateContext | undefined;
99
96
  }>;
100
97
  export declare function createRenderContext(scope: BdScope | undefined, compute: BdComputeResult | undefined): RenderContext;
101
- export declare function toDSLContext(ctx: RenderContext): DSLContext;
98
+ export declare function toDSLContext(ctx: RenderContext): Promise<DSLContext>;
102
99
  /**
103
100
  * Build the flattened, v1-shaped DSL context.
104
101
  *
@@ -107,11 +104,11 @@ export declare function toDSLContext(ctx: RenderContext): DSLContext;
107
104
  * on `it.option.*` / `it.product.*` / `it.state.*` / `it.ctx.*`.
108
105
  */
109
106
  export declare function buildTemplateDslContext(input: {
110
- store: Store | undefined;
111
- product: Product | undefined;
112
- option: ProductOption | undefined;
113
- state: BdComputeResult | undefined;
114
- }): TemplateDslContext;
107
+ product?: Product | null;
108
+ option?: ProductOption | null;
109
+ state?: BdComputeResult;
110
+ derived?: BdScopeDeriveFn;
111
+ }): Promise<TemplateDslContext>;
115
112
  export declare function getDiscountedPrice(option: ProductOption, params?: {
116
113
  monthly?: boolean;
117
114
  }): string | undefined;