@repobit/dex-store-elements 1.7.3 → 2.0.1
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.
- package/CHANGELOG.md +6 -906
- package/README.md +308 -283
- package/dist/src/actions/action.adapter.d.ts +36 -0
- package/dist/src/actions/action.adapter.js +183 -0
- package/dist/src/actions/action.adapter.js.map +1 -0
- package/dist/src/actions/action.attributes.d.ts +15 -0
- package/dist/src/actions/action.attributes.js +137 -0
- package/dist/src/actions/action.attributes.js.map +1 -0
- package/dist/src/actions/action.registry.d.ts +4 -0
- package/dist/src/actions/action.registry.js +119 -0
- package/dist/src/actions/action.registry.js.map +1 -0
- package/dist/src/actions/index.d.ts +3 -2
- package/dist/src/actions/index.js +3 -80
- package/dist/src/actions/index.js.map +1 -1
- package/dist/src/compute/compute.expand.d.ts +70 -0
- package/dist/src/compute/compute.expand.js +319 -0
- package/dist/src/compute/compute.expand.js.map +1 -0
- package/dist/src/compute/compute.scan.d.ts +54 -0
- package/dist/src/compute/compute.scan.js +160 -0
- package/dist/src/compute/compute.scan.js.map +1 -0
- package/dist/src/compute/option-resolver.d.ts +31 -0
- package/dist/src/compute/option-resolver.js +147 -0
- package/dist/src/compute/option-resolver.js.map +1 -0
- package/dist/src/contexts/context.compute.d.ts +12 -0
- package/dist/src/contexts/context.compute.js +4 -0
- package/dist/src/contexts/context.compute.js.map +1 -0
- package/dist/src/contexts/context.scope.d.ts +139 -0
- package/dist/src/contexts/context.scope.js +48 -0
- package/dist/src/contexts/context.scope.js.map +1 -0
- package/dist/src/controllers/controller.action-resolve.d.ts +110 -0
- package/dist/src/controllers/controller.action-resolve.js +203 -0
- package/dist/src/controllers/controller.action-resolve.js.map +1 -0
- package/dist/src/controllers/controller.action-topology.d.ts +59 -0
- package/dist/src/controllers/controller.action-topology.js +130 -0
- package/dist/src/controllers/controller.action-topology.js.map +1 -0
- package/dist/src/controllers/controller.compute.d.ts +72 -0
- package/dist/src/controllers/controller.compute.js +110 -0
- package/dist/src/controllers/controller.compute.js.map +1 -0
- package/dist/src/controllers/controller.resolve.d.ts +78 -0
- package/dist/src/controllers/controller.resolve.js +205 -0
- package/dist/src/controllers/controller.resolve.js.map +1 -0
- package/dist/src/core/action-topology.d.ts +62 -0
- package/dist/src/core/action-topology.js +67 -0
- package/dist/src/core/action-topology.js.map +1 -0
- package/dist/src/core/actions.d.ts +64 -0
- package/dist/src/core/actions.js +47 -0
- package/dist/src/core/actions.js.map +1 -0
- package/dist/src/core/node.element.d.ts +179 -0
- package/dist/src/core/node.element.js +573 -0
- package/dist/src/core/node.element.js.map +1 -0
- package/dist/src/core/node.scope.d.ts +374 -0
- package/dist/src/core/node.scope.js +558 -0
- package/dist/src/core/node.scope.js.map +1 -0
- package/dist/src/core/option-update.d.ts +11 -0
- package/dist/src/core/option-update.js +59 -0
- package/dist/src/core/option-update.js.map +1 -0
- package/dist/src/dsl/compilers/array/compiler.js +13 -14
- package/dist/src/dsl/compilers/array/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/boolean/compiler.d.ts +1 -1
- package/dist/src/dsl/compilers/boolean/compiler.js +14 -27
- package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/enum/compiler.js +7 -10
- package/dist/src/dsl/compilers/enum/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/index.d.ts +1 -1
- package/dist/src/dsl/compilers/index.js +5 -5
- package/dist/src/dsl/compilers/index.js.map +1 -1
- package/dist/src/dsl/utilty.d.ts +0 -4
- package/dist/src/dsl/utilty.js +3 -30
- package/dist/src/dsl/utilty.js.map +1 -1
- package/dist/src/eta/batch.d.ts +123 -0
- package/dist/src/eta/batch.js +162 -0
- package/dist/src/eta/batch.js.map +1 -0
- package/dist/src/eta/batcher.d.ts +184 -0
- package/dist/src/eta/batcher.js +335 -0
- package/dist/src/eta/batcher.js.map +1 -0
- package/dist/src/eta/cache.d.ts +36 -0
- package/dist/src/eta/cache.js +56 -0
- package/dist/src/eta/cache.js.map +1 -0
- package/dist/src/eta/controller.d.ts +608 -0
- package/dist/src/eta/controller.js +1036 -0
- package/dist/src/eta/controller.js.map +1 -0
- package/dist/src/eta/factory.d.ts +74 -0
- package/dist/src/eta/factory.js +121 -0
- package/dist/src/eta/factory.js.map +1 -0
- package/dist/src/eta/idle.d.ts +39 -0
- package/dist/src/eta/idle.js +52 -0
- package/dist/src/eta/idle.js.map +1 -0
- package/dist/src/eta/index.d.ts +52 -0
- package/dist/src/eta/index.js +13 -0
- package/dist/src/eta/index.js.map +1 -0
- package/dist/src/eta/observer.d.ts +263 -0
- package/dist/src/eta/observer.js +263 -0
- package/dist/src/eta/observer.js.map +1 -0
- package/dist/src/eta/operations.d.ts +41 -0
- package/dist/src/eta/operations.js +59 -0
- package/dist/src/eta/operations.js.map +1 -0
- package/dist/src/eta/source-registry.d.ts +198 -0
- package/dist/src/eta/source-registry.js +267 -0
- package/dist/src/eta/source-registry.js.map +1 -0
- package/dist/src/eta/template-index.d.ts +285 -0
- package/dist/src/eta/template-index.js +642 -0
- package/dist/src/eta/template-index.js.map +1 -0
- package/dist/src/eta/types.d.ts +529 -0
- package/dist/src/eta/types.js +2 -0
- package/dist/src/eta/types.js.map +1 -0
- package/dist/src/index.d.ts +21 -9
- package/dist/src/index.js +22 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/nodes/node.context.d.ts +5 -3
- package/dist/src/nodes/node.context.js +48 -3
- package/dist/src/nodes/node.context.js.map +1 -1
- package/dist/src/nodes/node.option.d.ts +11 -31
- package/dist/src/nodes/node.option.js +158 -222
- package/dist/src/nodes/node.option.js.map +1 -1
- package/dist/src/nodes/node.product.d.ts +7 -11
- package/dist/src/nodes/node.product.js +85 -64
- package/dist/src/nodes/node.product.js.map +1 -1
- package/dist/src/renders/attributes/buyLink.d.ts +2 -2
- package/dist/src/renders/attributes/buyLink.js +14 -11
- package/dist/src/renders/attributes/buyLink.js.map +1 -1
- package/dist/src/renders/attributes/devices.d.ts +2 -2
- package/dist/src/renders/attributes/devices.js +9 -19
- package/dist/src/renders/attributes/devices.js.map +1 -1
- package/dist/src/renders/attributes/discount.d.ts +2 -2
- package/dist/src/renders/attributes/discount.js +56 -44
- package/dist/src/renders/attributes/discount.js.map +1 -1
- package/dist/src/renders/attributes/hide.d.ts +2 -2
- package/dist/src/renders/attributes/hide.js +49 -14
- package/dist/src/renders/attributes/hide.js.map +1 -1
- package/dist/src/renders/attributes/index.d.ts +3 -3
- package/dist/src/renders/attributes/index.js +2 -4
- package/dist/src/renders/attributes/index.js.map +1 -1
- package/dist/src/renders/attributes/price.d.ts +2 -2
- package/dist/src/renders/attributes/price.js +30 -44
- package/dist/src/renders/attributes/price.js.map +1 -1
- package/dist/src/renders/attributes/subscription.d.ts +2 -2
- package/dist/src/renders/attributes/subscription.js +14 -20
- package/dist/src/renders/attributes/subscription.js.map +1 -1
- package/dist/src/renders/attributes/utilty.d.ts +2 -0
- package/dist/src/renders/attributes/utilty.js +17 -4
- package/dist/src/renders/attributes/utilty.js.map +1 -1
- package/dist/src/renders/context.d.ts +122 -10
- package/dist/src/renders/context.js +140 -80
- package/dist/src/renders/context.js.map +1 -1
- package/dist/src/renders/format.js +2 -2
- package/dist/src/renders/format.js.map +1 -1
- package/dist/src/renders/index.d.ts +6 -2
- package/dist/src/renders/index.js +67 -29
- package/dist/src/renders/index.js.map +1 -1
- package/dist/src/renders/observe.d.ts +1 -4
- package/dist/src/renders/observe.js +10 -24
- package/dist/src/renders/observe.js.map +1 -1
- package/dist/src/renders/render.wrapper.d.ts +37 -0
- package/dist/src/renders/render.wrapper.js +147 -0
- package/dist/src/renders/render.wrapper.js.map +1 -0
- package/dist/src/templating/eta.js +1 -4
- package/dist/src/templating/eta.js.map +1 -1
- package/package.json +4 -5
- package/dist/src/actions/action.button.d.ts +0 -2
- package/dist/src/actions/action.button.js +0 -8
- package/dist/src/actions/action.button.js.map +0 -1
- package/dist/src/actions/action.input.d.ts +0 -2
- package/dist/src/actions/action.input.js +0 -43
- package/dist/src/actions/action.input.js.map +0 -1
- package/dist/src/actions/action.select.d.ts +0 -2
- package/dist/src/actions/action.select.js +0 -14
- package/dist/src/actions/action.select.js.map +0 -1
- package/dist/src/actions/utilty.d.ts +0 -16
- package/dist/src/actions/utilty.js +0 -149
- package/dist/src/actions/utilty.js.map +0 -1
- package/dist/src/contexts/context.datalayer.d.ts +0 -9
- package/dist/src/contexts/context.datalayer.js +0 -3
- package/dist/src/contexts/context.datalayer.js.map +0 -1
- package/dist/src/contexts/context.derived.d.ts +0 -12
- package/dist/src/contexts/context.derived.js +0 -3
- package/dist/src/contexts/context.derived.js.map +0 -1
- package/dist/src/contexts/context.option.d.ts +0 -5
- package/dist/src/contexts/context.option.js +0 -3
- package/dist/src/contexts/context.option.js.map +0 -1
- package/dist/src/contexts/context.product.d.ts +0 -5
- package/dist/src/contexts/context.product.js +0 -3
- package/dist/src/contexts/context.product.js.map +0 -1
- package/dist/src/contexts/context.state.d.ts +0 -28
- package/dist/src/contexts/context.state.js +0 -3
- package/dist/src/contexts/context.state.js.map +0 -1
- package/dist/src/contexts/context.store.d.ts +0 -5
- package/dist/src/contexts/context.store.js +0 -3
- package/dist/src/contexts/context.store.js.map +0 -1
- package/dist/src/controllers/collect-controller.d.ts +0 -39
- package/dist/src/controllers/collect-controller.js +0 -161
- package/dist/src/controllers/collect-controller.js.map +0 -1
- package/dist/src/controllers/compute-controller.d.ts +0 -55
- package/dist/src/controllers/compute-controller.js +0 -350
- package/dist/src/controllers/compute-controller.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.d.ts +0 -25
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.js +0 -192
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.d.ts +0 -28
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.js +0 -206
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-renderer.d.ts +0 -21
- package/dist/src/controllers/eta-renderer/eta-renderer.js +0 -77
- package/dist/src/controllers/eta-renderer/eta-renderer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-template-cache.d.ts +0 -24
- package/dist/src/controllers/eta-renderer/eta-template-cache.js +0 -61
- package/dist/src/controllers/eta-renderer/eta-template-cache.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-types.d.ts +0 -11
- package/dist/src/controllers/eta-renderer/eta-types.js +0 -1
- package/dist/src/controllers/eta-renderer/eta-types.js.map +0 -1
- package/dist/src/controllers/eta-renderer/index.d.ts +0 -2
- package/dist/src/controllers/eta-renderer/index.js +0 -2
- package/dist/src/controllers/eta-renderer/index.js.map +0 -1
- package/dist/src/controllers/event-pipeline-controller.d.ts +0 -28
- package/dist/src/controllers/event-pipeline-controller.js +0 -72
- package/dist/src/controllers/event-pipeline-controller.js.map +0 -1
- package/dist/src/events/events.d.ts +0 -75
- package/dist/src/events/events.js +0 -78
- package/dist/src/events/events.js.map +0 -1
- package/dist/src/nodes/node.root.d.ts +0 -16
- package/dist/src/nodes/node.root.js +0 -49
- package/dist/src/nodes/node.root.js.map +0 -1
- package/dist/src/nodes/node.state.d.ts +0 -88
- package/dist/src/nodes/node.state.js +0 -330
- package/dist/src/nodes/node.state.js.map +0 -1
- package/dist/src/renders/utility.d.ts +0 -32
- package/dist/src/renders/utility.js +0 -191
- package/dist/src/renders/utility.js.map +0 -1
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { defaultContainsEtaTemplate as defaultIsTemplateText } from './source-registry.js';
|
|
2
|
+
import type { EtaTemplateCache, TemplateOperation, TemplateOperationContext, TemplateOperationFactory } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Re-export of the canonical default template predicate from
|
|
5
|
+
* `source-registry.ts`. The two names point at the same
|
|
6
|
+
* function so the registry and the factory can never drift
|
|
7
|
+
* apart (round-10 audit item B1).
|
|
8
|
+
*/
|
|
9
|
+
export { defaultIsTemplateText };
|
|
10
|
+
/**
|
|
11
|
+
* Default factory for the Eta template subsystem.
|
|
12
|
+
*
|
|
13
|
+
* The factory is the only place that knows about Eta. Everything
|
|
14
|
+
* else in the subsystem deals with plain DOM nodes and immutable
|
|
15
|
+
* operations.
|
|
16
|
+
*
|
|
17
|
+
* Discovery rules:
|
|
18
|
+
*
|
|
19
|
+
* - Text nodes become `TextOperation` when their text matches the
|
|
20
|
+
* template predicate (default: contains both `{{` and `}}`).
|
|
21
|
+
* - Element attributes become `AttributeOperation` when their
|
|
22
|
+
* qualified name passes the attribute-allowlist filter (default:
|
|
23
|
+
* every attribute is allowed) AND the value matches the template
|
|
24
|
+
* predicate.
|
|
25
|
+
*
|
|
26
|
+
* The factory never reads or writes the DOM beyond looking at the
|
|
27
|
+
* supplied node/attribute values. When a source registry is
|
|
28
|
+
* supplied through the per-call `TemplateOperationContext`, the
|
|
29
|
+
* factory consults the registry first so the original template
|
|
30
|
+
* source is reused after a render has overwritten the DOM value.
|
|
31
|
+
*
|
|
32
|
+
* The factory also writes the resolved source back to the registry
|
|
33
|
+
* on each successful read so that subsequent renders and external
|
|
34
|
+
* mutation handlers see the same source.
|
|
35
|
+
*
|
|
36
|
+
* **Stateless at the call site.** The factory does NOT hold a
|
|
37
|
+
* reference to a source registry — every `create*` call receives
|
|
38
|
+
* its registry through the `context` parameter. The index that
|
|
39
|
+
* owns the registry passes it in on every call, so a single
|
|
40
|
+
* factory instance can be safely shared across multiple indices
|
|
41
|
+
* without one index's registry leaking into another.
|
|
42
|
+
*/
|
|
43
|
+
export declare class DefaultTemplateOperationFactory<TContext = object> implements TemplateOperationFactory<TContext> {
|
|
44
|
+
private readonly cache;
|
|
45
|
+
private readonly attributeNames?;
|
|
46
|
+
private readonly isTemplateText;
|
|
47
|
+
constructor(cache: EtaTemplateCache, options?: {
|
|
48
|
+
readonly attributeNames?: readonly string[];
|
|
49
|
+
readonly isTemplateText?: (text: string) => boolean;
|
|
50
|
+
});
|
|
51
|
+
createTextOperation(node: Text, context?: TemplateOperationContext): TemplateOperation<TContext> | undefined;
|
|
52
|
+
createAttributeOperation(element: Element, attribute: Attr, context?: TemplateOperationContext): TemplateOperation<TContext> | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Returns `true` when `name` should be considered by the factory.
|
|
55
|
+
*
|
|
56
|
+
* The factory uses an explicit allowlist when one is supplied via
|
|
57
|
+
* the constructor. When no allowlist is supplied, every attribute
|
|
58
|
+
* is observed and the factory decides per-attribute whether it
|
|
59
|
+
* contains a template. This default matches the architecture
|
|
60
|
+
* "automatic text and attribute template discovery" rather than
|
|
61
|
+
* the legacy `OBS_ATTRS_RENDER` allowlist.
|
|
62
|
+
*/
|
|
63
|
+
private isObservedAttribute;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convenience constructor used by the default controller and by
|
|
67
|
+
* tests that want a one-line setup.
|
|
68
|
+
*
|
|
69
|
+
* The `cache` argument is REQUIRED — the factory does not own or
|
|
70
|
+
* auto-create a cache. Every call site that constructs a factory
|
|
71
|
+
* must own the cache it hands in (typically the controller's own
|
|
72
|
+
* `#cache` or a cache the host created and manages).
|
|
73
|
+
*/
|
|
74
|
+
export declare function createDefaultTemplateOperationFactory<TContext = object>(cache: EtaTemplateCache, options?: ConstructorParameters<typeof DefaultTemplateOperationFactory<TContext>>[1]): TemplateOperationFactory<TContext>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// src/eta/factory.ts
|
|
2
|
+
import { AttributeOperation, TextOperation } from './operations.js';
|
|
3
|
+
import { defaultContainsEtaTemplate as defaultIsTemplateText } from './source-registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Re-export of the canonical default template predicate from
|
|
6
|
+
* `source-registry.ts`. The two names point at the same
|
|
7
|
+
* function so the registry and the factory can never drift
|
|
8
|
+
* apart (round-10 audit item B1).
|
|
9
|
+
*/
|
|
10
|
+
export { defaultIsTemplateText };
|
|
11
|
+
/**
|
|
12
|
+
* Default factory for the Eta template subsystem.
|
|
13
|
+
*
|
|
14
|
+
* The factory is the only place that knows about Eta. Everything
|
|
15
|
+
* else in the subsystem deals with plain DOM nodes and immutable
|
|
16
|
+
* operations.
|
|
17
|
+
*
|
|
18
|
+
* Discovery rules:
|
|
19
|
+
*
|
|
20
|
+
* - Text nodes become `TextOperation` when their text matches the
|
|
21
|
+
* template predicate (default: contains both `{{` and `}}`).
|
|
22
|
+
* - Element attributes become `AttributeOperation` when their
|
|
23
|
+
* qualified name passes the attribute-allowlist filter (default:
|
|
24
|
+
* every attribute is allowed) AND the value matches the template
|
|
25
|
+
* predicate.
|
|
26
|
+
*
|
|
27
|
+
* The factory never reads or writes the DOM beyond looking at the
|
|
28
|
+
* supplied node/attribute values. When a source registry is
|
|
29
|
+
* supplied through the per-call `TemplateOperationContext`, the
|
|
30
|
+
* factory consults the registry first so the original template
|
|
31
|
+
* source is reused after a render has overwritten the DOM value.
|
|
32
|
+
*
|
|
33
|
+
* The factory also writes the resolved source back to the registry
|
|
34
|
+
* on each successful read so that subsequent renders and external
|
|
35
|
+
* mutation handlers see the same source.
|
|
36
|
+
*
|
|
37
|
+
* **Stateless at the call site.** The factory does NOT hold a
|
|
38
|
+
* reference to a source registry — every `create*` call receives
|
|
39
|
+
* its registry through the `context` parameter. The index that
|
|
40
|
+
* owns the registry passes it in on every call, so a single
|
|
41
|
+
* factory instance can be safely shared across multiple indices
|
|
42
|
+
* without one index's registry leaking into another.
|
|
43
|
+
*/
|
|
44
|
+
export class DefaultTemplateOperationFactory {
|
|
45
|
+
constructor(cache, options) {
|
|
46
|
+
this.cache = cache;
|
|
47
|
+
this.attributeNames = options?.attributeNames;
|
|
48
|
+
this.isTemplateText =
|
|
49
|
+
options?.isTemplateText ?? defaultIsTemplateText;
|
|
50
|
+
}
|
|
51
|
+
createTextOperation(node, context = {}) {
|
|
52
|
+
const sources = context.sources;
|
|
53
|
+
// Try preserved source first so post-render reads still see the
|
|
54
|
+
// original template (P0 #1 in the implementation review).
|
|
55
|
+
let source = sources?.readTextSource(node);
|
|
56
|
+
if (source === undefined) {
|
|
57
|
+
source = node.nodeValue ?? '';
|
|
58
|
+
}
|
|
59
|
+
if (!source || !this.isTemplateText(source)) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
// Persist the resolved source so future reads see the same value
|
|
63
|
+
// even after a render overwrites the DOM. No-op when the registry
|
|
64
|
+
// already has the entry.
|
|
65
|
+
sources?.replaceTextSource(node, source);
|
|
66
|
+
const template = this.cache.get(source);
|
|
67
|
+
return new TextOperation(node, template);
|
|
68
|
+
}
|
|
69
|
+
createAttributeOperation(element, attribute, context = {}) {
|
|
70
|
+
const name = attribute.name;
|
|
71
|
+
if (name === '') {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
if (!this.isObservedAttribute(name)) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const sources = context.sources;
|
|
78
|
+
// Try preserved source first; the registry is authoritative for
|
|
79
|
+
// what the attribute's source was when the index first saw it.
|
|
80
|
+
let source = sources?.readAttributeSource(element, name);
|
|
81
|
+
if (source === undefined) {
|
|
82
|
+
source = attribute.value ?? '';
|
|
83
|
+
}
|
|
84
|
+
if (source === '' || !this.isTemplateText(source)) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
sources?.setAttributeSource(element, name, source);
|
|
88
|
+
const template = this.cache.get(source);
|
|
89
|
+
return new AttributeOperation(element, name, template);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns `true` when `name` should be considered by the factory.
|
|
93
|
+
*
|
|
94
|
+
* The factory uses an explicit allowlist when one is supplied via
|
|
95
|
+
* the constructor. When no allowlist is supplied, every attribute
|
|
96
|
+
* is observed and the factory decides per-attribute whether it
|
|
97
|
+
* contains a template. This default matches the architecture
|
|
98
|
+
* "automatic text and attribute template discovery" rather than
|
|
99
|
+
* the legacy `OBS_ATTRS_RENDER` allowlist.
|
|
100
|
+
*/
|
|
101
|
+
isObservedAttribute(name) {
|
|
102
|
+
if (this.attributeNames === undefined ||
|
|
103
|
+
this.attributeNames.length === 0) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return this.attributeNames.indexOf(name) !== -1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Convenience constructor used by the default controller and by
|
|
111
|
+
* tests that want a one-line setup.
|
|
112
|
+
*
|
|
113
|
+
* The `cache` argument is REQUIRED — the factory does not own or
|
|
114
|
+
* auto-create a cache. Every call site that constructs a factory
|
|
115
|
+
* must own the cache it hands in (typically the controller's own
|
|
116
|
+
* `#cache` or a cache the host created and manages).
|
|
117
|
+
*/
|
|
118
|
+
export function createDefaultTemplateOperationFactory(cache, options) {
|
|
119
|
+
return new DefaultTemplateOperationFactory(cache, options);
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/eta/factory.ts"],"names":[],"mappings":"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","sourcesContent":["// src/eta/factory.ts\n\nimport {\n AttributeOperation,\n TextOperation\n} from './operations.js';\nimport {\n defaultContainsEtaTemplate as defaultIsTemplateText\n} from './source-registry.js';\nimport type {\n EtaTemplateCache,\n TemplateOperation,\n TemplateOperationContext,\n TemplateOperationFactory\n} from './types.js';\n\n/**\n * Re-export of the canonical default template predicate from\n * `source-registry.ts`. The two names point at the same\n * function so the registry and the factory can never drift\n * apart (round-10 audit item B1).\n */\nexport { defaultIsTemplateText };\n\n/**\n * Default factory for the Eta template subsystem.\n *\n * The factory is the only place that knows about Eta. Everything\n * else in the subsystem deals with plain DOM nodes and immutable\n * operations.\n *\n * Discovery rules:\n *\n * - Text nodes become `TextOperation` when their text matches the\n * template predicate (default: contains both `{{` and `}}`).\n * - Element attributes become `AttributeOperation` when their\n * qualified name passes the attribute-allowlist filter (default:\n * every attribute is allowed) AND the value matches the template\n * predicate.\n *\n * The factory never reads or writes the DOM beyond looking at the\n * supplied node/attribute values. When a source registry is\n * supplied through the per-call `TemplateOperationContext`, the\n * factory consults the registry first so the original template\n * source is reused after a render has overwritten the DOM value.\n *\n * The factory also writes the resolved source back to the registry\n * on each successful read so that subsequent renders and external\n * mutation handlers see the same source.\n *\n * **Stateless at the call site.** The factory does NOT hold a\n * reference to a source registry — every `create*` call receives\n * its registry through the `context` parameter. The index that\n * owns the registry passes it in on every call, so a single\n * factory instance can be safely shared across multiple indices\n * without one index's registry leaking into another.\n */\nexport class DefaultTemplateOperationFactory<\n TContext = object\n>\nimplements TemplateOperationFactory<TContext>\n{\n private readonly cache : EtaTemplateCache;\n private readonly attributeNames?: readonly string[];\n private readonly isTemplateText: (\n text: string\n ) => boolean;\n\n constructor(\n cache: EtaTemplateCache,\n options?: {\n readonly attributeNames?: readonly string[];\n readonly isTemplateText?: (\n text: string\n ) => boolean;\n }\n ) {\n this.cache = cache;\n this.attributeNames = options?.attributeNames;\n this.isTemplateText =\n options?.isTemplateText ?? defaultIsTemplateText;\n }\n\n createTextOperation(\n node: Text,\n context: TemplateOperationContext = {}\n ): TemplateOperation<TContext> | undefined {\n const sources = context.sources;\n\n // Try preserved source first so post-render reads still see the\n // original template (P0 #1 in the implementation review).\n let source = sources?.readTextSource(node);\n\n if (source === undefined) {\n source = node.nodeValue ?? '';\n }\n\n if (!source || !this.isTemplateText(source)) {\n return undefined;\n }\n\n // Persist the resolved source so future reads see the same value\n // even after a render overwrites the DOM. No-op when the registry\n // already has the entry.\n sources?.replaceTextSource(node, source);\n\n const template = this.cache.get(source);\n\n return new TextOperation<TContext>(node, template);\n }\n\n createAttributeOperation(\n element: Element,\n attribute: Attr,\n context: TemplateOperationContext = {}\n ): TemplateOperation<TContext> | undefined {\n const name = attribute.name;\n\n if (name === '') {\n return undefined;\n }\n\n if (!this.isObservedAttribute(name)) {\n return undefined;\n }\n\n const sources = context.sources;\n\n // Try preserved source first; the registry is authoritative for\n // what the attribute's source was when the index first saw it.\n let source = sources?.readAttributeSource(\n element,\n name\n );\n\n if (source === undefined) {\n source = attribute.value ?? '';\n }\n\n if (source === '' || !this.isTemplateText(source)) {\n return undefined;\n }\n\n sources?.setAttributeSource(\n element,\n name,\n source\n );\n\n const template = this.cache.get(source);\n\n return new AttributeOperation<TContext>(\n element,\n name,\n template\n );\n }\n\n /**\n * Returns `true` when `name` should be considered by the factory.\n *\n * The factory uses an explicit allowlist when one is supplied via\n * the constructor. When no allowlist is supplied, every attribute\n * is observed and the factory decides per-attribute whether it\n * contains a template. This default matches the architecture\n * \"automatic text and attribute template discovery\" rather than\n * the legacy `OBS_ATTRS_RENDER` allowlist.\n */\n private isObservedAttribute(name: string): boolean {\n if (\n this.attributeNames === undefined ||\n this.attributeNames.length === 0\n ) {\n return true;\n }\n\n return this.attributeNames.indexOf(name) !== -1;\n }\n}\n\n/**\n * Convenience constructor used by the default controller and by\n * tests that want a one-line setup.\n *\n * The `cache` argument is REQUIRED — the factory does not own or\n * auto-create a cache. Every call site that constructs a factory\n * must own the cache it hands in (typically the controller's own\n * `#cache` or a cache the host created and manages).\n */\nexport function createDefaultTemplateOperationFactory<\n TContext = object\n>(\n cache: EtaTemplateCache,\n options?: ConstructorParameters<\n typeof DefaultTemplateOperationFactory<TContext>\n >[1]\n): TemplateOperationFactory<TContext> {\n return new DefaultTemplateOperationFactory<TContext>(\n cache,\n options\n );\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny shim for the browser's `requestIdleCallback` / `cancelIdleCallback`
|
|
3
|
+
* pair, with a `setTimeout` / `clearTimeout` fallback for environments
|
|
4
|
+
* (older Safari, jsdom, web workers in some configurations, server-side
|
|
5
|
+
* tests) where the idle-callback API is not available.
|
|
6
|
+
*
|
|
7
|
+
* The fallback uses a 16 ms `setTimeout` because that roughly matches
|
|
8
|
+
* the per-frame budget the browser would normally allocate for idle
|
|
9
|
+
* work. Tests should not assume the exact fallback timing — the goal
|
|
10
|
+
* is "later, not now", not a specific delay.
|
|
11
|
+
*
|
|
12
|
+
* `globalThis` is used instead of `window` so the helpers also work
|
|
13
|
+
* in workers (where `window` is undefined) and in Node-based test
|
|
14
|
+
* runners that polyfill the global scope.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Schedule `callback` to run when the browser is idle.
|
|
18
|
+
*
|
|
19
|
+
* Returns a numeric handle that can be passed to `cancelIdle`. The
|
|
20
|
+
* handle is opaque — callers MUST NOT interpret it as anything other
|
|
21
|
+
* than an identifier for cancellation.
|
|
22
|
+
*
|
|
23
|
+
* When `requestIdleCallback` is unavailable the callback is deferred
|
|
24
|
+
* via `setTimeout(callback, 16)`. The 16 ms delay is a heuristic that
|
|
25
|
+
* matches a single animation-frame budget; it is intentionally not
|
|
26
|
+
* configurable because the fallback is only meant to provide
|
|
27
|
+
* scheduling-after-now semantics, not precise timing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function requestIdle(callback: () => void): number;
|
|
30
|
+
/**
|
|
31
|
+
* Cancel a previously scheduled idle callback.
|
|
32
|
+
*
|
|
33
|
+
* Uses `cancelIdleCallback` when the host supports it; falls back to
|
|
34
|
+
* `clearTimeout` for the `setTimeout`-based fallback path. Callers
|
|
35
|
+
* MUST pass the same handle that `requestIdle` returned; passing a
|
|
36
|
+
* foreign handle (for example, one from `setTimeout` rather than
|
|
37
|
+
* `requestIdle`) is undefined behavior.
|
|
38
|
+
*/
|
|
39
|
+
export declare function cancelIdle(id: number): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/eta/idle.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tiny shim for the browser's `requestIdleCallback` / `cancelIdleCallback`
|
|
4
|
+
* pair, with a `setTimeout` / `clearTimeout` fallback for environments
|
|
5
|
+
* (older Safari, jsdom, web workers in some configurations, server-side
|
|
6
|
+
* tests) where the idle-callback API is not available.
|
|
7
|
+
*
|
|
8
|
+
* The fallback uses a 16 ms `setTimeout` because that roughly matches
|
|
9
|
+
* the per-frame budget the browser would normally allocate for idle
|
|
10
|
+
* work. Tests should not assume the exact fallback timing — the goal
|
|
11
|
+
* is "later, not now", not a specific delay.
|
|
12
|
+
*
|
|
13
|
+
* `globalThis` is used instead of `window` so the helpers also work
|
|
14
|
+
* in workers (where `window` is undefined) and in Node-based test
|
|
15
|
+
* runners that polyfill the global scope.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Schedule `callback` to run when the browser is idle.
|
|
19
|
+
*
|
|
20
|
+
* Returns a numeric handle that can be passed to `cancelIdle`. The
|
|
21
|
+
* handle is opaque — callers MUST NOT interpret it as anything other
|
|
22
|
+
* than an identifier for cancellation.
|
|
23
|
+
*
|
|
24
|
+
* When `requestIdleCallback` is unavailable the callback is deferred
|
|
25
|
+
* via `setTimeout(callback, 16)`. The 16 ms delay is a heuristic that
|
|
26
|
+
* matches a single animation-frame budget; it is intentionally not
|
|
27
|
+
* configurable because the fallback is only meant to provide
|
|
28
|
+
* scheduling-after-now semantics, not precise timing.
|
|
29
|
+
*/
|
|
30
|
+
export function requestIdle(callback) {
|
|
31
|
+
if (typeof globalThis.requestIdleCallback === 'function') {
|
|
32
|
+
return globalThis.requestIdleCallback(callback);
|
|
33
|
+
}
|
|
34
|
+
return globalThis.setTimeout(callback, 16);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Cancel a previously scheduled idle callback.
|
|
38
|
+
*
|
|
39
|
+
* Uses `cancelIdleCallback` when the host supports it; falls back to
|
|
40
|
+
* `clearTimeout` for the `setTimeout`-based fallback path. Callers
|
|
41
|
+
* MUST pass the same handle that `requestIdle` returned; passing a
|
|
42
|
+
* foreign handle (for example, one from `setTimeout` rather than
|
|
43
|
+
* `requestIdle`) is undefined behavior.
|
|
44
|
+
*/
|
|
45
|
+
export function cancelIdle(id) {
|
|
46
|
+
if (typeof globalThis.cancelIdleCallback === 'function') {
|
|
47
|
+
globalThis.cancelIdleCallback(id);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
globalThis.clearTimeout(id);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=idle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idle.js","sourceRoot":"","sources":["../../../src/eta/idle.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAElB;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,QAAoB;IAC9C,IAAI,OAAO,UAAU,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;QACzD,OAAO,UAAU,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,UAAU,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAsB,CAAC;AAClE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,IAAI,OAAO,UAAU,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;QACxD,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IACD,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["// src/eta/idle.ts\n\n/**\n * Tiny shim for the browser's `requestIdleCallback` / `cancelIdleCallback`\n * pair, with a `setTimeout` / `clearTimeout` fallback for environments\n * (older Safari, jsdom, web workers in some configurations, server-side\n * tests) where the idle-callback API is not available.\n *\n * The fallback uses a 16 ms `setTimeout` because that roughly matches\n * the per-frame budget the browser would normally allocate for idle\n * work. Tests should not assume the exact fallback timing — the goal\n * is \"later, not now\", not a specific delay.\n *\n * `globalThis` is used instead of `window` so the helpers also work\n * in workers (where `window` is undefined) and in Node-based test\n * runners that polyfill the global scope.\n */\n\n/**\n * Schedule `callback` to run when the browser is idle.\n *\n * Returns a numeric handle that can be passed to `cancelIdle`. The\n * handle is opaque — callers MUST NOT interpret it as anything other\n * than an identifier for cancellation.\n *\n * When `requestIdleCallback` is unavailable the callback is deferred\n * via `setTimeout(callback, 16)`. The 16 ms delay is a heuristic that\n * matches a single animation-frame budget; it is intentionally not\n * configurable because the fallback is only meant to provide\n * scheduling-after-now semantics, not precise timing.\n */\nexport function requestIdle(callback: () => void): number {\n if (typeof globalThis.requestIdleCallback === 'function') {\n return globalThis.requestIdleCallback(callback);\n }\n return globalThis.setTimeout(callback, 16) as unknown as number;\n}\n\n/**\n * Cancel a previously scheduled idle callback.\n *\n * Uses `cancelIdleCallback` when the host supports it; falls back to\n * `clearTimeout` for the `setTimeout`-based fallback path. Callers\n * MUST pass the same handle that `requestIdle` returned; passing a\n * foreign handle (for example, one from `setTimeout` rather than\n * `requestIdle`) is undefined behavior.\n */\nexport function cancelIdle(id: number): void {\n if (typeof globalThis.cancelIdleCallback === 'function') {\n globalThis.cancelIdleCallback(id);\n return;\n }\n globalThis.clearTimeout(id);\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public surface of the Eta template subsystem.
|
|
3
|
+
*
|
|
4
|
+
* The subsystem is split into small, focused pieces. Consumers should
|
|
5
|
+
* prefer the high-level `EtaTemplateController`; the lower-level types are
|
|
6
|
+
* exported for tests and for advanced consumers that want to compose
|
|
7
|
+
* custom observers, factories, or indices.
|
|
8
|
+
*
|
|
9
|
+
* Layer map:
|
|
10
|
+
*
|
|
11
|
+
* EtaMutationObserver
|
|
12
|
+
* -> translates browser MutationRecords into TemplateIndexMutation
|
|
13
|
+
* values and forwards them to a TemplateIndexSink (round 5)
|
|
14
|
+
* or to an onRecords callback (round 6).
|
|
15
|
+
*
|
|
16
|
+
* EtaMutationBatcher
|
|
17
|
+
* -> coalesces MutationRecord batches into a single index flush
|
|
18
|
+
* per scheduled tick (round 6).
|
|
19
|
+
*
|
|
20
|
+
* EtaTemplateIndex
|
|
21
|
+
* -> maintains the operation index for a scoped boundary; iterable.
|
|
22
|
+
*
|
|
23
|
+
* EtaTemplateSourceRegistry
|
|
24
|
+
* -> sidecar storage for original template sources so the factory
|
|
25
|
+
* can re-read sources after a render has overwritten the DOM.
|
|
26
|
+
*
|
|
27
|
+
* TemplateOperationFactory
|
|
28
|
+
* -> discovers templates on DOM nodes and creates TextOperation /
|
|
29
|
+
* AttributeOperation instances.
|
|
30
|
+
*
|
|
31
|
+
* TextOperation / AttributeOperation
|
|
32
|
+
* -> render a single DOM target against a context.
|
|
33
|
+
*
|
|
34
|
+
* EtaTemplateCache
|
|
35
|
+
* -> owns the mapping from template source string to a compiled
|
|
36
|
+
* Eta template function.
|
|
37
|
+
*
|
|
38
|
+
* EtaTemplateController
|
|
39
|
+
* -> owns lifecycle, the @lit/task schedule, and the context builder.
|
|
40
|
+
* Brings the pieces above together for a single scoped boundary.
|
|
41
|
+
*/
|
|
42
|
+
export type { EtaCompiledTemplate, EtaCompileFn, EtaMutationBatchSchedule, EtaTemplateCache, EtaTemplateControllerOptions, EtaTemplateHost, EtaTemplateIndexLike, EtaTemplateIndexOptions, TemplateIndexMutation, TemplateIndexSink, TemplateOperation, TemplateOperationContext, TemplateOperationFactory } from './types.js';
|
|
43
|
+
export { createDefaultTemplateOperationFactory, defaultIsTemplateText, DefaultTemplateOperationFactory } from './factory.js';
|
|
44
|
+
export { AttributeOperation, TextOperation } from './operations.js';
|
|
45
|
+
export { createEtaTemplateCache, DefaultEtaTemplateCache } from './cache.js';
|
|
46
|
+
export { EtaTemplateIndex } from './template-index.js';
|
|
47
|
+
export { createEtaMutationObserver, EtaMutationObserver } from './observer.js';
|
|
48
|
+
export { createEtaTemplateSourceRegistry, defaultContainsEtaTemplate, EtaTemplateSourceRegistry, type EtaTemplatePredicate, type EtaTemplateSourceRegistryOptions } from './source-registry.js';
|
|
49
|
+
export { createEtaTemplateController, EtaTemplateController, type DefaultEtaContext, type EtaTemplateStatus } from './controller.js';
|
|
50
|
+
export { EtaMutationBatch, enqueueRecord } from './batch.js';
|
|
51
|
+
export { EtaMutationBatcher, type EtaMutationBatcherOptions } from './batcher.js';
|
|
52
|
+
export { cancelIdle, requestIdle } from './idle.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/eta/index.ts
|
|
2
|
+
export { createDefaultTemplateOperationFactory, defaultIsTemplateText, DefaultTemplateOperationFactory } from './factory.js';
|
|
3
|
+
export { AttributeOperation, TextOperation } from './operations.js';
|
|
4
|
+
export { createEtaTemplateCache, DefaultEtaTemplateCache } from './cache.js';
|
|
5
|
+
export { EtaTemplateIndex } from './template-index.js';
|
|
6
|
+
export { createEtaMutationObserver, EtaMutationObserver } from './observer.js';
|
|
7
|
+
export { createEtaTemplateSourceRegistry, defaultContainsEtaTemplate, EtaTemplateSourceRegistry } from './source-registry.js';
|
|
8
|
+
export { createEtaTemplateController, EtaTemplateController } from './controller.js';
|
|
9
|
+
// Round-6 mutation-batching layer.
|
|
10
|
+
export { EtaMutationBatch, enqueueRecord } from './batch.js';
|
|
11
|
+
export { EtaMutationBatcher } from './batcher.js';
|
|
12
|
+
export { cancelIdle, requestIdle } from './idle.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/eta/index.ts"],"names":[],"mappings":"AAAA,mBAAmB;AA4DnB,OAAO,EACL,qCAAqC,EAAE,qBAAqB,EAC5D,+BAA+B,EAChC,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,sBAAsB,EAAE,uBAAuB,EAChD,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EACL,yBAAyB,EAAE,mBAAmB,EAC/C,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAAE,yBAAyB,EAGtD,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,2BAA2B,EAAE,qBAAqB,EAEnD,MAAM,iBAAiB,CAAC;AAEzB,mCAAmC;AACnC,OAAO,EACL,gBAAgB,EAAE,aAAa,EAChC,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,UAAU,EAAE,WAAW,EACxB,MAAM,WAAW,CAAC","sourcesContent":["// src/eta/index.ts\n\n/**\n * Public surface of the Eta template subsystem.\n *\n * The subsystem is split into small, focused pieces. Consumers should\n * prefer the high-level `EtaTemplateController`; the lower-level types are\n * exported for tests and for advanced consumers that want to compose\n * custom observers, factories, or indices.\n *\n * Layer map:\n *\n * EtaMutationObserver\n * -> translates browser MutationRecords into TemplateIndexMutation\n * values and forwards them to a TemplateIndexSink (round 5)\n * or to an onRecords callback (round 6).\n *\n * EtaMutationBatcher\n * -> coalesces MutationRecord batches into a single index flush\n * per scheduled tick (round 6).\n *\n * EtaTemplateIndex\n * -> maintains the operation index for a scoped boundary; iterable.\n *\n * EtaTemplateSourceRegistry\n * -> sidecar storage for original template sources so the factory\n * can re-read sources after a render has overwritten the DOM.\n *\n * TemplateOperationFactory\n * -> discovers templates on DOM nodes and creates TextOperation /\n * AttributeOperation instances.\n *\n * TextOperation / AttributeOperation\n * -> render a single DOM target against a context.\n *\n * EtaTemplateCache\n * -> owns the mapping from template source string to a compiled\n * Eta template function.\n *\n * EtaTemplateController\n * -> owns lifecycle, the @lit/task schedule, and the context builder.\n * Brings the pieces above together for a single scoped boundary.\n */\n\nexport type {\n EtaCompiledTemplate,\n EtaCompileFn,\n EtaMutationBatchSchedule,\n EtaTemplateCache,\n EtaTemplateControllerOptions,\n EtaTemplateHost,\n EtaTemplateIndexLike,\n EtaTemplateIndexOptions,\n TemplateIndexMutation,\n TemplateIndexSink,\n TemplateOperation,\n TemplateOperationContext,\n TemplateOperationFactory\n} from './types.js';\n\nexport {\n createDefaultTemplateOperationFactory, defaultIsTemplateText,\n DefaultTemplateOperationFactory\n} from './factory.js';\n\nexport {\n AttributeOperation,\n TextOperation\n} from './operations.js';\n\nexport {\n createEtaTemplateCache, DefaultEtaTemplateCache\n} from './cache.js';\n\nexport { EtaTemplateIndex } from './template-index.js';\n\nexport {\n createEtaMutationObserver, EtaMutationObserver\n} from './observer.js';\n\nexport {\n createEtaTemplateSourceRegistry,\n defaultContainsEtaTemplate, EtaTemplateSourceRegistry,\n type EtaTemplatePredicate,\n type EtaTemplateSourceRegistryOptions\n} from './source-registry.js';\n\nexport {\n createEtaTemplateController, EtaTemplateController, type DefaultEtaContext,\n type EtaTemplateStatus\n} from './controller.js';\n\n// Round-6 mutation-batching layer.\nexport {\n EtaMutationBatch, enqueueRecord\n} from './batch.js';\n\nexport {\n EtaMutationBatcher, type EtaMutationBatcherOptions\n} from './batcher.js';\n\nexport {\n cancelIdle, requestIdle\n} from './idle.js';\n\n"]}
|