@repobit/dex-store-elements 1.7.2 → 2.0.0
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 +5 -905
- package/README.md +9 -7
- 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":"template-index.js","sourceRoot":"","sources":["../../../src/eta/template-index.ts"],"names":[],"mappings":"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","sourcesContent":["// src/eta/template-index.ts\n\nimport type {\n EtaTemplateSourceRegistry\n} from './source-registry.js';\nimport {\n createEtaTemplateSourceRegistry\n} from './source-registry.js';\nimport type {\n EtaTemplateIndexOptions,\n TemplateIndexMutation,\n TemplateIndexSink,\n TemplateOperation,\n TemplateOperationFactory\n} from './types.js';\n\n/**\n * The EtaTemplateIndex maintains the set of renderable operations for a\n * scoped boundary.\n *\n * It is Eta-agnostic. Discovery is delegated to a TemplateOperationFactory\n * which returns either an operation or `undefined`. The index simply stores\n * whatever the factory produces and looks it up by node for incremental\n * updates.\n *\n * Three views of the data are kept in sync:\n *\n * - `operations` — a flat Set of operations iterated during render.\n * - `nodeIndex` — a WeakMap from owning node (Element or Text) to the\n * operations that own it, used for O(1) removal.\n * - `attrOps` — a WeakMap from Element to a Map of `attributeName` to the\n * single operation that targets that attribute. Needed because Attr\n * identity is unstable across `setAttribute` calls — looking up by the\n * current Attr reference would never find the previous operation.\n *\n * Boundary handling is configurable through an injected predicate\n * (`EtaTemplateIndexOptions.isBoundaryElement`). The index itself does\n * not own boundary semantics — it asks the host/controller for a\n * decision. That makes boundary detection work consistently across\n * controllers that share the same root but own different indices.\n *\n * Source preservation is delegated to an\n * `EtaTemplateSourceRegistry` so the factory can re-read original\n * template sources after a render has overwritten the DOM value.\n *\n * **Cross-scope source registry limitation.** Each `EtaTemplateIndex`\n * owns the source registry it created in its constructor\n * (`createEtaTemplateSourceRegistry()`). Moving a rendered\n * template node from one scope to another loses the original\n * template source — the destination index's registry has no\n * record of it, so the factory falls back to the live DOM value,\n * which now holds the rendered string rather than the template\n * literal. The destination index cannot recreate the operation\n * without discovering the new template content externally.\n *\n * Applications that need to reparent rendered template nodes\n * across scopes should share a single\n * `EtaTemplateSourceRegistry` between the affected indices.\n * Because the factory is stateless at the call site (the\n * registry is threaded through every call via\n * `TemplateOperationContext`), two indices can share one factory\n * safely — each just passes the shared registry as the\n * `sources` field on every call. The v4 review recommends\n * keeping this limitation documented unless cross-scope DOM\n * moves become a real use case — at which point the\n * index/controller can grow a `sourceRegistry` option that\n * overrides the per-index `createEtaTemplateSourceRegistry()`\n * default.\n */\nexport class EtaTemplateIndex<TContext = object>\nimplements\n Iterable<TemplateOperation<TContext>>,\n TemplateIndexSink\n{\n private readonly _operations = new Set<\n TemplateOperation<TContext>\n >();\n\n /**\n * Per-node operation lookup.\n *\n * Reassignable so `clear()` can fully reset the index even though\n * `WeakMap` cannot be cleared. The old map is reclaimed by the GC\n * as soon as no caller holds it.\n */\n private _nodeIndex = new WeakMap<\n Node,\n Set<TemplateOperation<TContext>>\n >();\n\n /**\n * Per-element, per-attribute-name operation lookup.\n *\n * `Attr` identity is not stable across DOM mutations (`setAttribute`\n * removes the old Attr and inserts a new one), so we cannot key attribute\n * operations by their Attr reference. Instead we key by the owning\n * element + the attribute's qualified name. Each (element, name) pair\n * owns at most one operation because the factory only ever produces one\n * AttributeOperation per attribute.\n */\n private _attrOps = new WeakMap<\n Element,\n Map<string, TemplateOperation<TContext>>\n >();\n\n /**\n * Tracks nodes that have at least one operation associated with them.\n *\n * `WeakSet` cannot be iterated and cannot be cleared, so it is\n * reassigned by `clear()`. It only needs `add` / `has` / `delete`,\n * which `WeakSet` supports.\n */\n private _indexedNodes = new WeakSet<Node>();\n\n private readonly factory:\n TemplateOperationFactory<TContext>;\n private readonly options: EtaTemplateIndexOptions;\n private readonly sources: EtaTemplateSourceRegistry;\n\n constructor(\n factory: TemplateOperationFactory<TContext>,\n options: EtaTemplateIndexOptions = {}\n ) {\n this.factory = factory;\n this.options = options;\n this.sources = createEtaTemplateSourceRegistry();\n\n // The source registry is threaded through every factory call\n // via the `TemplateOperationContext` parameter, NOT wired into\n // the factory itself. That keeps the factory stateless at the\n // call site and makes factory reuse across indices safe — the\n // last constructor to call a registry setter no longer wins,\n // because there is no setter to call. See `_addNode`,\n // `_collectAttributes`, `_updateAttribute`, and `_updateText`\n // for the call sites that pass `{ sources: this.sources }`.\n }\n\n /**\n * Walks the subtree of `root` and indexes every node that contains a\n * template.\n *\n * The walk is implemented by `_addNode`, which descends into\n * descendants and respects the `isBoundaryElement` predicate. The\n * root element is added first so a root element's own attribute\n * templates are indexed even when it has no child nodes.\n *\n * Boundary semantics:\n *\n * - If `root` itself matches `isBoundaryElement`, it is still\n * indexed as the local boundary. Its own attribute templates and\n * its owned descendants are collected.\n * - If a nested element matches `isBoundaryElement`, the walk\n * neither collects the foreign boundary's own attributes nor\n * descends into the foreign boundary's subtree. Outer\n * controllers leave that subtree entirely to the boundary's\n * own controller.\n *\n * ⚠️ **Stable-on-raw-DOM invariant for nested boundaries.**\n * When `initialize` encounters a nested boundary element, the\n * `isBoundaryElement` predicate MUST classify it as a foreign\n * boundary **without** relying on prototype-side state such as\n * `instanceof BdScopedElement` or a runtime brand. The parent\n * index may scan a subtree before a nested custom element has\n * upgraded; in that window the nested element has not yet been\n * augmented with the brand, and a brand-only predicate would\n * fail to classify it as a boundary. No DOM mutation fires when\n * the upgrade completes, so the parent index would keep the\n * wrong operations for the lifetime of the page. Prefer\n * tag-name or marker-attribute checks so the classification is\n * stable from the moment the element is parsed. See\n * `EtaTemplateIndexOptions.isBoundaryElement` for full\n * rationale and example patterns.\n *\n * ⚠️ **Source preservation trust boundary.** `initialize()`\n * assumes external DOM edits have been observed while the\n * index was online. During normal operation the\n * `MutationObserver` keeps the `EtaTemplateSourceRegistry` in\n * sync via `syncTextSource()` / `syncAttributeSource()`. If\n * external code mutates already-rendered template nodes while\n * the controller is disconnected or the index has been cleared\n * (for example between two `clear()` / `initialize()` cycles),\n * the registry can hand back a stale preserved source and the\n * next render will re-emit the old template literal instead of\n * the new DOM value. Callers in that situation should `reset()`\n * (which also clears the source registry) before\n * re-initializing, or otherwise clear the registry themselves.\n */\n initialize(root: ParentNode): void {\n this.clear();\n\n this._addNode(root as Node);\n }\n\n /**\n * Applies a single mutation to the index.\n *\n * Returns `true` when the index actually changed as a result of\n * the mutation (an operation was added, replaced, or removed);\n * `false` when the mutation was a no-op for this index because\n * the node was outside the root, inside a foreign boundary, or\n * did not contain a template.\n *\n * Mutation semantics:\n *\n * - `add` walks the node and its descendants.\n * - `remove` drops every operation whose owner is the node or one of its\n * descendants.\n * - `attribute` re-runs attribute discovery on a single attribute of an\n * element that is already part of the index.\n * - `text` re-runs text discovery on a text node that is already part\n * of the index.\n */\n apply(mutation: TemplateIndexMutation): boolean {\n switch (mutation.type) {\n case 'add':\n return this._addNode(mutation.node);\n\n case 'remove':\n return this._removeNode(mutation.node);\n\n case 'attribute':\n return this._updateAttribute(\n mutation.element,\n mutation.attribute\n );\n\n case 'text':\n return this._updateText(mutation.node);\n\n // Exhaustive guard: if a new mutation variant is added,\n // TypeScript flags this `_exhaustive` assignment as a\n // type error until the new variant is handled here.\n default: {\n const _exhaustive: never = mutation;\n return _exhaustive;\n }\n }\n }\n\n /**\n * Drops every operation from the index and forgets every node.\n *\n * Every internal structure — including the `WeakMap` / `WeakSet`\n * instances — is reassigned so the index returns to a fully empty\n * state. The old `WeakMap` / `WeakSet` references become unreachable\n * as soon as `clear()` returns and the GC reclaims them.\n *\n * **Source registry is intentionally preserved.** The\n * `EtaTemplateSourceRegistry` keeps the original template source\n * strings for the live DOM nodes so the factory can re-render the\n * same nodes after a render has overwritten their text / attribute\n * values. Clearing the registry here would make\n * `clear()` + `initialize()` destructive on already-rendered\n * DOM — the next render would see plain text instead of the\n * original template literal. Callers that explicitly want a\n * fully destructive reset should use `reset()` instead, which\n * also clears the source registry.\n */\n clear(): void {\n for (const op of this._operations) {\n op.dispose();\n }\n\n this._operations.clear();\n this._nodeIndex = new WeakMap();\n this._attrOps = new WeakMap();\n this._indexedNodes = new WeakSet();\n }\n\n /**\n * Fully destructive reset — equivalent to `clear()` plus clearing\n * the `EtaTemplateSourceRegistry`.\n *\n * Use this when the live DOM no longer reflects the original\n * template sources (for example, because external code mutated\n * already-rendered nodes while the index was offline, or the\n * caller is switching to a completely different template set).\n *\n * After `reset()`, the next `initialize(root)` walks the subtree\n * from scratch using whatever is in the DOM at that moment —\n * the registry will not serve a stale preserved source.\n *\n * For normal `clear()` + `initialize()` cycles where the source\n * strings are still valid, prefer `clear()` so the registry\n * survives re-indexing.\n */\n reset(): void {\n this.clear();\n this.sources.clear();\n }\n\n [Symbol.iterator](): Iterator<\n TemplateOperation<TContext>\n > {\n return this._operations.values();\n }\n\n /**\n * Number of operations currently held by the index.\n *\n * Useful for tests and diagnostics. The renderer iterates operations\n * directly and does not need this.\n */\n get size(): number {\n return this._operations.size;\n }\n\n /**\n * Whether the given node has any operation associated with it.\n *\n * Used by the controller to decide if a mutation is interesting.\n *\n * For text nodes this checks whether any operation is keyed on the node.\n * For elements this returns true if the element owns any operation\n * directly (e.g. its attributes) — `hasNode(element)` does NOT imply\n * \"this element has been fully indexed\", only \"this element owns at\n * least one operation\".\n */\n hasNode(node: Node): boolean {\n if (this._indexedNodes.has(node)) {\n return true;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n return this._attrOps.has(node as Element);\n }\n\n return false;\n }\n\n /**\n * Disposes every operation currently held by the index.\n *\n * The default operations are no-ops for dispose, but custom\n * operations may need to release observers. Calling this from the\n * controller's teardown keeps the lifecycle clean.\n *\n * Delegates to `clear()` so disposal and reset happen through one\n * well-defined code path; this also avoids disposing each\n * operation twice.\n */\n disposeAll(): void {\n this.clear();\n }\n\n /**\n * Returns `true` when `element` matches the configured\n * `isBoundaryElement` predicate AND is a foreign boundary (i.e. is\n * not the index's own root).\n *\n * The root itself is always walked; only nested boundaries stop the\n * traversal.\n */\n private _isForeignBoundary(element: Element): boolean {\n if (\n this.options.root !== undefined &&\n element === this.options.root\n ) {\n return false;\n }\n\n return (\n this.options.isBoundaryElement?.(element) === true\n );\n }\n\n /**\n * Returns `true` when `node` belongs to this index's root.\n *\n * The walk climbs ancestors looking for either `root` or a foreign\n * boundary. If the walk reaches `root` without hitting a foreign\n * boundary, the node is owned. If the walk hits a foreign boundary\n * first, the node is not owned.\n *\n * When `root` is not configured, the index accepts every mutation\n * it receives — callers that wire a parent observer MUST set\n * `root` to prevent nested-scope leakage.\n *\n * Foreign-boundary element itself: when the target `node` is a\n * boundary element other than the index's own `root`, it is owned\n * by another controller. The check is performed up-front so the\n * ancestor loop only handles descendants and the `current !==\n * node` guard it used to need can go away.\n */\n private _ownsNode(node: Node): boolean {\n const root = this.options.root;\n\n if (root === undefined) {\n return true;\n }\n\n // Shadow-DOM index: a node belongs to this index when the\n // ancestor chain reaches the configured shadow root before\n // climbing out at the shadow host.\n //\n // `getRootNode()` alone is not sufficient because a foreign\n // boundary element nested deeper inside the shadow can sit\n // between `node` and the shadow root — the foreign boundary\n // belongs to a different controller, so this index must not\n // claim it. Walking `parentNode` upward also catches the\n // shadow-host case: the host's `parentNode` chain leaves the\n // shadow tree entirely, so the walk terminates at `null`\n // without ever reaching `root`.\n //\n // Foreign-boundary predicates are also consulted at each\n // step so a nested element whose `getRootNode()` is still\n // `root` but which `isBoundaryElement` classifies as a\n // foreign boundary is correctly excluded — matching the\n // light-DOM branch's behaviour below.\n if (root instanceof ShadowRoot) {\n let current: Node | null = node;\n\n while (current !== null) {\n if (current === root) {\n return true;\n }\n\n // Foreign boundary nested inside this shadow — owned by\n // another controller even though `getRootNode()` still\n // returns this shadow root. `current` is narrowed to\n // `Element` here and `root` is a `ShadowRoot` (we are in\n // the `root instanceof ShadowRoot` branch), so the two\n // are disjoint types — the explicit `current !== root`\n // check is unnecessary and would be flagged as\n // unintentional.\n if (\n current instanceof Element &&\n this.options.isBoundaryElement?.(current) === true\n ) {\n return false;\n }\n\n current = current.parentNode;\n }\n\n return false;\n }\n\n if (\n node instanceof Element &&\n node !== root &&\n this.options.isBoundaryElement?.(node) === true\n ) {\n return false;\n }\n\n let current: Node | null = node;\n\n while (current) {\n if (current === root) {\n return true;\n }\n\n if (\n current instanceof Element &&\n current !== root &&\n this.options.isBoundaryElement?.(current) === true\n ) {\n return false;\n }\n\n current = current.parentNode;\n }\n\n return false;\n }\n\n private _addNode(node: Node): boolean {\n // Refuse to walk into nodes that are not part of the root's owned\n // subtree. Observer feedback can deliver `add` mutations for\n // nested scope elements or sibling trees that the parent index\n // does not own — the index must ignore them.\n if (!this._ownsNode(node)) {\n return false;\n }\n\n if (this._isIndexed(node)) {\n // Already indexed. Avoid double-processing the same node.\n return false;\n }\n\n if (node.nodeType === Node.TEXT_NODE) {\n const op = this.factory.createTextOperation(\n node as Text,\n { sources: this.sources }\n );\n\n if (op) {\n this._addTextOperation(node as Text, op);\n return true;\n }\n\n return false;\n }\n\n // `ShadowRoot` (and any `DocumentFragment`-shaped root passed\n // to `initialize()`) is not itself a template target — it is\n // a container. Descend into its children and index each one.\n if (node instanceof DocumentFragment) {\n for (const child of Array.from(node.childNodes)) {\n this._addNode(child);\n }\n return false;\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return false;\n }\n\n const element = node as Element;\n\n // Foreign-boundary check happens BEFORE attribute collection so a\n // nested boundary element is fully skipped. The boundary's own\n // controller owns both its attributes and its descendants — a\n // parent index must not collect either.\n if (this._isForeignBoundary(element)) {\n return false;\n }\n\n const beforeSize = this._operations.size;\n\n this._collectAttributes(element);\n\n for (const child of Array.from(\n element.childNodes\n )) {\n this._addNode(child);\n }\n\n return this._operations.size !== beforeSize;\n }\n\n private _isIndexed(node: Node): boolean {\n if (this._indexedNodes.has(node)) {\n return true;\n }\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n return this._attrOps.has(node as Element);\n }\n\n return false;\n }\n\n private _collectAttributes(\n element: Element\n ): void {\n const context = { sources: this.sources };\n\n for (const attr of Array.from(element.attributes)) {\n const op =\n this.factory.createAttributeOperation(\n element,\n attr,\n context\n );\n\n if (!op) {\n continue;\n }\n\n this._addAttributeOperation(\n element,\n attr.name,\n op\n );\n }\n }\n\n /**\n * Removes every operation that targets `node` or one of its descendants.\n *\n * The walk mirrors `_addNode` but does not need to consult the factory.\n * Returns `true` when at least one operation was disposed by the\n * call.\n */\n private _removeNode(node: Node): boolean {\n let removed = false;\n const owned = this._nodeIndex.get(node);\n\n if (owned) {\n for (const op of owned) {\n if (this._deleteOperation(op)) {\n removed = true;\n }\n }\n\n this._nodeIndex.delete(node);\n }\n\n this._indexedNodes.delete(node);\n\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n\n // Drop any attribute operations owned by this element. The\n // per-attribute map is authoritative for attribute lookup, so we\n // must clear it here.\n const byName = this._attrOps.get(element);\n\n if (byName) {\n for (const op of byName.values()) {\n if (this._deleteOperation(op)) {\n removed = true;\n }\n }\n\n this._attrOps.delete(element);\n }\n\n for (const child of Array.from(\n element.childNodes\n )) {\n if (this._removeNode(child)) {\n removed = true;\n }\n }\n }\n\n return removed;\n }\n\n private _updateAttribute(\n element: Element,\n attribute: string\n ): boolean {\n if (!this._ownsNode(element)) {\n return false;\n }\n\n // Sync the registry with the current DOM value before re-running\n // discovery so an external mutation that turned a template into\n // plain text is reflected (the registry must not serve a stale\n // preserved source).\n this.sources.syncAttributeSource(element, attribute);\n\n // Drop the previous operation for this (element, name) pair. The\n // Attr reference is unstable across mutations, so we key by element\n // + name via `_attrOps` rather than by Attr. We capture the return\n // value because the contract for `apply()` requires this method\n // to report the index actually changed — even if no replacement\n // attribute is found, removing the old operation IS a change.\n const removed =\n this._removeAttributeOperation(element, attribute);\n\n // Resolve the current Attr for the new value. If the attribute was\n // removed entirely there is nothing to re-create.\n const attr = element.attributes.getNamedItem(\n attribute\n );\n\n if (!attr) {\n return removed;\n }\n\n const op =\n this.factory.createAttributeOperation(\n element,\n attr,\n { sources: this.sources }\n );\n\n if (!op) {\n // The new attribute value is not a template (e.g. external\n // code replaced `{{ url }}` with plain text). The old\n // operation, if any, was already removed above — return\n // whether that removal actually happened so callers see an\n // accurate dirty signal.\n return removed;\n }\n\n this._addAttributeOperation(\n element,\n attribute,\n op\n );\n return true;\n }\n\n private _updateText(node: Text): boolean {\n if (!this._ownsNode(node)) {\n return false;\n }\n\n // Sync the registry with the current DOM value before re-running\n // discovery. External mutations that turn a template into plain\n // text must not leave the registry holding a stale preserved\n // source.\n this.sources.syncTextSource(node);\n\n // Capture whether the previous text operation was actually\n // removed. The contract for `apply()` requires this method to\n // report the index actually changed — even if no replacement\n // text operation is created, dropping the old one IS a change.\n let removed = false;\n\n const owned = this._nodeIndex.get(node);\n\n if (owned) {\n for (const op of owned) {\n if (op.kind === 'text') {\n if (this._deleteOperation(op)) {\n removed = true;\n }\n }\n }\n\n const remaining = new Set<\n TemplateOperation<TContext>\n >();\n\n for (const op of owned) {\n if (this._operations.has(op)) {\n remaining.add(op);\n }\n }\n\n if (remaining.size === 0) {\n this._nodeIndex.delete(node);\n this._indexedNodes.delete(node);\n } else {\n this._nodeIndex.set(node, remaining);\n }\n }\n\n const op = this.factory.createTextOperation(\n node,\n { sources: this.sources }\n );\n\n if (!op) {\n // The new value is not a template (e.g. external code replaced\n // `{{ name }}` with plain text). The previous text operation,\n // if any, was already removed above — return whether that\n // removal actually happened so callers see an accurate dirty\n // signal.\n return removed;\n }\n\n this._addTextOperation(node, op);\n return true;\n }\n\n private _addTextOperation(\n node: Text,\n operation: TemplateOperation<TContext>\n ): void {\n this._operations.add(operation);\n\n const owned =\n this._nodeIndex.get(node) ??\n new Set<TemplateOperation<TContext>>();\n\n owned.add(operation);\n this._nodeIndex.set(node, owned);\n this._indexedNodes.add(node);\n }\n\n private _addAttributeOperation(\n element: Element,\n name: string,\n operation: TemplateOperation<TContext>\n ): void {\n this._operations.add(operation);\n\n // Register the operation under both the element and the\n // (element, name) pair. `_nodeIndex` lets `hasNode(element)` and\n // `_removeNode(element)` find the operation via element identity;\n // `_attrOps` lets `_updateAttribute` find the previous op by name\n // even after the Attr reference changes.\n const owned =\n this._nodeIndex.get(element) ??\n new Set<TemplateOperation<TContext>>();\n\n owned.add(operation);\n this._nodeIndex.set(element, owned);\n this._indexedNodes.add(element);\n\n let byName = this._attrOps.get(element);\n\n if (!byName) {\n byName = new Map<\n string,\n TemplateOperation<TContext>\n >();\n this._attrOps.set(element, byName);\n }\n\n byName.set(name, operation);\n }\n\n private _removeAttributeOperation(\n element: Element,\n name: string\n ): boolean {\n const byName = this._attrOps.get(element);\n\n if (!byName) {\n return false;\n }\n\n const op = byName.get(name);\n\n if (!op) {\n return false;\n }\n\n this._deleteOperation(op);\n\n byName.delete(name);\n\n if (byName.size === 0) {\n this._attrOps.delete(element);\n }\n\n const owned = this._nodeIndex.get(element);\n\n if (owned) {\n owned.delete(op);\n\n if (owned.size === 0) {\n this._nodeIndex.delete(element);\n this._indexedNodes.delete(element);\n }\n }\n\n return true;\n }\n\n /**\n * Removes `operation` from the live `_operations` set and calls its\n * `dispose()` hook.\n *\n * Returns `true` when the operation was actually present and\n * removed, `false` when it had already been removed by a previous\n * path (idempotent removal).\n *\n * Single entry point used by every removal path so the disposal\n * contract is honoured uniformly.\n */\n private _deleteOperation(\n operation: TemplateOperation<TContext>\n ): boolean {\n if (!this._operations.delete(operation)) {\n return false;\n }\n\n operation.dispose();\n return true;\n }\n}\n"]}
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for the Eta template subsystem.
|
|
3
|
+
*
|
|
4
|
+
* The subsystem is split into small, single-responsibility pieces:
|
|
5
|
+
*
|
|
6
|
+
* MutationObserver
|
|
7
|
+
* -> translates DOM mutations into TemplateIndexMutation values
|
|
8
|
+
*
|
|
9
|
+
* EtaTemplateIndex
|
|
10
|
+
* -> maintains the operation index for a scoped boundary
|
|
11
|
+
*
|
|
12
|
+
* TemplateOperationFactory
|
|
13
|
+
* -> discovers templates and compiles them via EtaTemplateCache
|
|
14
|
+
*
|
|
15
|
+
* TextOperation / AttributeOperation
|
|
16
|
+
* -> render a single DOM target against a context
|
|
17
|
+
*
|
|
18
|
+
* EtaTemplateController
|
|
19
|
+
* -> owns lifecycle, the Task schedule, and the context builder
|
|
20
|
+
*
|
|
21
|
+
* EtaMutationBatcher
|
|
22
|
+
* -> coalesces MutationRecord batches into a single
|
|
23
|
+
* index flush per scheduled tick (round 6)
|
|
24
|
+
*
|
|
25
|
+
* Most of these layers are Eta-agnostic. Only the cache and factory know how
|
|
26
|
+
* to talk to Eta. Everything else works on plain DOM nodes.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Schedule mode for `EtaMutationBatcher` flushes.
|
|
30
|
+
*
|
|
31
|
+
* Defined here (rather than in `batcher.ts`) so consumers can
|
|
32
|
+
* import the type without pulling the batcher module into a
|
|
33
|
+
* type-only import cycle. The batcher re-exports the type for
|
|
34
|
+
* direct importers.
|
|
35
|
+
*
|
|
36
|
+
* - `microtask` — flush via `queueMicrotask`. Lowest latency.
|
|
37
|
+
* - `animation-frame` — flush via `requestAnimationFrame`.
|
|
38
|
+
* Recommended default for browser hosts.
|
|
39
|
+
* - `idle` — flush via `requestIdleCallback` (with a `setTimeout`
|
|
40
|
+
* fallback). Lowest priority.
|
|
41
|
+
* - `manual` — never auto-flush; the host owns the lifecycle.
|
|
42
|
+
*/
|
|
43
|
+
export type EtaMutationBatchSchedule = 'microtask' | 'animation-frame' | 'idle' | 'manual';
|
|
44
|
+
export type EtaMutationBatchScheduleResolver = () => EtaMutationBatchSchedule | undefined;
|
|
45
|
+
export type EtaMutationBatchScheduleOption = EtaMutationBatchSchedule | EtaMutationBatchScheduleResolver;
|
|
46
|
+
/**
|
|
47
|
+
* A precompiled Eta template function.
|
|
48
|
+
*
|
|
49
|
+
* `data` is the rendering context. The function returns a string. Eta exposes
|
|
50
|
+
* `compile` as a member of the Eta class, so the function signature is
|
|
51
|
+
* `(data?) => string`.
|
|
52
|
+
*/
|
|
53
|
+
export type EtaCompiledTemplate = (data?: object) => string;
|
|
54
|
+
/**
|
|
55
|
+
* Discriminated union describing a single change that should be reflected
|
|
56
|
+
* in the template index.
|
|
57
|
+
*
|
|
58
|
+
* - `add` introduces a node that has not been seen before
|
|
59
|
+
* - `remove` drops a node and all of its descendants
|
|
60
|
+
* - `attribute` re-runs attribute discovery for a single attribute on an
|
|
61
|
+
* element that is already part of the index
|
|
62
|
+
* - `text` re-runs text discovery for a text node that is already part of
|
|
63
|
+
* the index
|
|
64
|
+
*/
|
|
65
|
+
export type TemplateIndexMutation = {
|
|
66
|
+
readonly type: 'add';
|
|
67
|
+
readonly node: Node;
|
|
68
|
+
} | {
|
|
69
|
+
readonly type: 'remove';
|
|
70
|
+
readonly node: Node;
|
|
71
|
+
} | {
|
|
72
|
+
readonly type: 'attribute';
|
|
73
|
+
readonly element: Element;
|
|
74
|
+
readonly attribute: string;
|
|
75
|
+
} | {
|
|
76
|
+
readonly type: 'text';
|
|
77
|
+
readonly node: Text;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Anything that can absorb a TemplateIndexMutation.
|
|
81
|
+
*
|
|
82
|
+
* EtaMutationObserver and EtaTemplateIndex both satisfy this interface so
|
|
83
|
+
* that they can be swapped in tests or composed by other layers.
|
|
84
|
+
*
|
|
85
|
+
* `apply` returns `true` only when the sink actually changed its
|
|
86
|
+
* index as a result of the mutation. Returning `false` signals that
|
|
87
|
+
* the mutation was ignored (the node was outside the index's root,
|
|
88
|
+
* the attribute had no template, the boundary skipped the subtree,
|
|
89
|
+
* etc.). The observer uses this signal to decide whether a render
|
|
90
|
+
* pass needs to be scheduled, so returning the wrong value directly
|
|
91
|
+
* causes either stale renders or unnecessary work.
|
|
92
|
+
*/
|
|
93
|
+
export interface TemplateIndexSink {
|
|
94
|
+
apply(mutation: TemplateIndexMutation): boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A single immutable rendering operation.
|
|
98
|
+
*
|
|
99
|
+
* Operations are owned by a single DOM target (a text node or an attribute
|
|
100
|
+
* of an element) and know nothing about template discovery, the index, or
|
|
101
|
+
* the controller. They simply render their target against a context.
|
|
102
|
+
*
|
|
103
|
+
* The `dispose` method is currently a no-op for the built-in operations but
|
|
104
|
+
* exists as an extension point for future implementations that may hold
|
|
105
|
+
* observers, event listeners, or other resources.
|
|
106
|
+
*/
|
|
107
|
+
export interface TemplateOperation<TContext = object> {
|
|
108
|
+
readonly owner: Node;
|
|
109
|
+
readonly kind: 'text' | 'attribute';
|
|
110
|
+
execute(context: TContext): void;
|
|
111
|
+
dispose(): void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Structural surface used by `TemplateOperationContext.sources`.
|
|
115
|
+
*
|
|
116
|
+
* Defined here (rather than imported from `source-registry.ts`) so
|
|
117
|
+
* `types.ts` stays a leaf module per the eta subsystem layout.
|
|
118
|
+
* Any object that satisfies these four methods can stand in for
|
|
119
|
+
* the real `EtaTemplateSourceRegistry` — tests can pass mocks
|
|
120
|
+
* without pulling the registry module into a type-only cycle.
|
|
121
|
+
*/
|
|
122
|
+
export interface TemplateOperationSourceRegistry {
|
|
123
|
+
readTextSource(node: Text): string | undefined;
|
|
124
|
+
replaceTextSource(node: Text, source: string | undefined): void;
|
|
125
|
+
readAttributeSource(element: Element, name: string): string | undefined;
|
|
126
|
+
setAttributeSource(element: Element, name: string, source: string): void;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Per-call context passed into `TemplateOperationFactory.create*`
|
|
130
|
+
* methods.
|
|
131
|
+
*
|
|
132
|
+
* Carries dependencies that the factory needs at discovery time
|
|
133
|
+
* but does not own. Today the only such dependency is the
|
|
134
|
+
* `EtaTemplateSourceRegistry`, which preserves original template
|
|
135
|
+
* sources across renders. By threading the registry through every
|
|
136
|
+
* factory call (rather than storing it on the factory instance)
|
|
137
|
+
* the factory stays stateless at the call site and can safely be
|
|
138
|
+
* shared across multiple indices — the registry is always the
|
|
139
|
+
* one owned by the calling index.
|
|
140
|
+
*
|
|
141
|
+
* The context is optional: factory implementations that do not
|
|
142
|
+
* need preserved sources can simply ignore it. Test fakes can
|
|
143
|
+
* pass `undefined`.
|
|
144
|
+
*/
|
|
145
|
+
export interface TemplateOperationContext {
|
|
146
|
+
/** Source registry for preserved original template sources. */
|
|
147
|
+
readonly sources?: TemplateOperationSourceRegistry;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Factory contract for producing TemplateOperation instances from DOM nodes.
|
|
151
|
+
*
|
|
152
|
+
* The factory is the only Eta-aware discovery layer. The collector simply
|
|
153
|
+
* forwards raw DOM nodes to the factory and stores whatever comes back.
|
|
154
|
+
*
|
|
155
|
+
* A factory returns `undefined` when a node does not contain a template,
|
|
156
|
+
* which lets the index skip irrelevant subtrees.
|
|
157
|
+
*
|
|
158
|
+
* The optional `context` parameter carries dependencies that the
|
|
159
|
+
* factory needs at discovery time but does not own — currently
|
|
160
|
+
* the source registry. The factory treats it as advisory: a
|
|
161
|
+
* missing context means the factory does not have access to
|
|
162
|
+
* preserved sources, so post-render reads fall back to the live
|
|
163
|
+
* DOM value.
|
|
164
|
+
*/
|
|
165
|
+
export interface TemplateOperationFactory<TContext = object> {
|
|
166
|
+
createTextOperation(node: Text, context?: TemplateOperationContext): TemplateOperation<TContext> | undefined;
|
|
167
|
+
createAttributeOperation(element: Element, attribute: Attr, context?: TemplateOperationContext): TemplateOperation<TContext> | undefined;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Cache for compiled Eta templates.
|
|
171
|
+
*
|
|
172
|
+
* Keys are the original template strings. The cache is the single owner of
|
|
173
|
+
* template compilation. Neither the factory nor operations compile templates
|
|
174
|
+
* directly.
|
|
175
|
+
*/
|
|
176
|
+
export interface EtaTemplateCache {
|
|
177
|
+
get(source: string): EtaCompiledTemplate;
|
|
178
|
+
clear(): void;
|
|
179
|
+
/**
|
|
180
|
+
* Number of compiled templates currently held in the cache.
|
|
181
|
+
*
|
|
182
|
+
* Optional because the structural minimum only describes what the
|
|
183
|
+
* controller and factory need to drive a render pass. The
|
|
184
|
+
* default `DefaultEtaTemplateCache` exposes `size`; tests and
|
|
185
|
+
* diagnostics that read `cache.size` rely on this hook being
|
|
186
|
+
* present, so the optional property documents the convention.
|
|
187
|
+
*
|
|
188
|
+
* `EtaTemplateController.#cache` is now a true ECMAScript
|
|
189
|
+
* private field and cannot be observed from outside the
|
|
190
|
+
* controller, but the host can still construct the cache
|
|
191
|
+
* directly and read `size` for diagnostics.
|
|
192
|
+
*/
|
|
193
|
+
readonly size?: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Result type returned by Eta when compiling a template source.
|
|
197
|
+
*
|
|
198
|
+
* Some Eta configurations (async, plugins, custom tags) can change the
|
|
199
|
+
* function shape; we keep the contract minimal and let EtaTemplateCache
|
|
200
|
+
* implementation choose the actual compiler.
|
|
201
|
+
*/
|
|
202
|
+
export type EtaCompileFn = (source: string) => EtaCompiledTemplate;
|
|
203
|
+
/**
|
|
204
|
+
* Minimal surface required by `EtaTemplateController` on its host.
|
|
205
|
+
*
|
|
206
|
+
* The controller reads the root element and may register local async
|
|
207
|
+
* work with the host so the render pass participates in the host's
|
|
208
|
+
* `updateComplete` contract.
|
|
209
|
+
*
|
|
210
|
+
* Anything else — the current scope, compute revision, the compute
|
|
211
|
+
* controller — is read by the host-supplied `args` / `context`
|
|
212
|
+
* callbacks inside `EtaTemplateControllerOptions`, NOT by the
|
|
213
|
+
* controller. That keeps the controller reusable outside
|
|
214
|
+
* `BdScopedElement`.
|
|
215
|
+
*/
|
|
216
|
+
export interface EtaTemplateHost {
|
|
217
|
+
/**
|
|
218
|
+
* Owning boundary. Must be an `Element` because the controller marks
|
|
219
|
+
* it as a traversal boundary (only elements own attributes and
|
|
220
|
+
* children — text/document roots cannot act as Eta boundaries).
|
|
221
|
+
*/
|
|
222
|
+
readonly root: Element;
|
|
223
|
+
/**
|
|
224
|
+
* Track local async work — when provided, the controller registers
|
|
225
|
+
* the render pass under its own instance as the key so the host's
|
|
226
|
+
* `updateComplete` waits for the render to finish before resolving.
|
|
227
|
+
*
|
|
228
|
+
* Mirrors the signature used by `BdNodeElement.trackLocalWork`.
|
|
229
|
+
*/
|
|
230
|
+
trackLocalWork?<T>(key: unknown, work: PromiseLike<T>): Promise<T>;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Options accepted by `EtaTemplateController`.
|
|
234
|
+
*
|
|
235
|
+
* The controller is scope-agnostic. Everything reactive is funnelled
|
|
236
|
+
* through `args`, `enabled`, and `context` callbacks so the controller
|
|
237
|
+
* stays usable outside `BdScopedElement`.
|
|
238
|
+
*/
|
|
239
|
+
export interface EtaTemplateControllerOptions<TArgs extends readonly unknown[] = readonly unknown[], TContext extends object = object> {
|
|
240
|
+
/**
|
|
241
|
+
* Args the controller reacts to. The Task re-runs whenever the
|
|
242
|
+
* returned tuple changes; the default comparison is `@lit/task`'s
|
|
243
|
+
* `shallowArrayEquals`. Override via `argsEqual` for custom
|
|
244
|
+
* equality (e.g. structural compare on a single object argument).
|
|
245
|
+
*
|
|
246
|
+
* Hosts typically return reactive properties of their own state:
|
|
247
|
+
*
|
|
248
|
+
* ```ts
|
|
249
|
+
* args: () => [
|
|
250
|
+
* this.currentScope,
|
|
251
|
+
* this.compute.revision,
|
|
252
|
+
* this.isComputeEnabled
|
|
253
|
+
* ] as const
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
args: () => TArgs;
|
|
257
|
+
/**
|
|
258
|
+
* Optional equality check for `args`. Forwarded to the underlying
|
|
259
|
+
* `@lit/task` so the controller re-runs only when the returned
|
|
260
|
+
* tuple actually changes by the host's definition.
|
|
261
|
+
*
|
|
262
|
+
* Return `true` when the old tuple and the new tuple should be
|
|
263
|
+
* treated as equal (no rerun); return `false` to force a rerun.
|
|
264
|
+
* When omitted, `@lit/task` uses its built-in
|
|
265
|
+
* `shallowArrayEquals` which compares tuple elements by reference
|
|
266
|
+
* (object identity for non-primitives).
|
|
267
|
+
*/
|
|
268
|
+
argsEqual?: (oldArgs: TArgs, newArgs: TArgs) => boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Optional gate. When `enabled` returns `false` the controller skips
|
|
271
|
+
* the render pass. When enabled returns false, the task completes
|
|
272
|
+
* with undefined.
|
|
273
|
+
*
|
|
274
|
+
* The default is "always enabled".
|
|
275
|
+
*/
|
|
276
|
+
enabled?: (args: TArgs) => boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Context builder. Returns the value handed to each
|
|
279
|
+
* `TemplateOperation.execute()` call.
|
|
280
|
+
*
|
|
281
|
+
* Returning `undefined` short-circuits the render pass for the
|
|
282
|
+
* current args — the index keeps its operations but the renderer
|
|
283
|
+
* does not invoke them. This is the right behaviour when the args
|
|
284
|
+
* describe a state where rendering would produce invalid output
|
|
285
|
+
* (for example, a scope that has not yet been published).
|
|
286
|
+
*
|
|
287
|
+
* Hosts that want the recommended `$scope` / `$compute` shape can
|
|
288
|
+
* spread the scope entry, then attach `$scope` and `$compute`
|
|
289
|
+
* fields to the returned context object. The controller does NOT
|
|
290
|
+
* build that shape for the host — see `DefaultEtaContext` for the
|
|
291
|
+
* documented shape.
|
|
292
|
+
*/
|
|
293
|
+
context: (args: TArgs) => TContext | undefined;
|
|
294
|
+
/**
|
|
295
|
+
* Optional override used by tests to inject a deterministic index.
|
|
296
|
+
*
|
|
297
|
+
* The callback receives the resolved factory so the index can be
|
|
298
|
+
* constructed with the same factory the controller will use. The
|
|
299
|
+
* returned object must:
|
|
300
|
+
*
|
|
301
|
+
* - implement `TemplateIndexSink` so the observer can feed it
|
|
302
|
+
* mutations;
|
|
303
|
+
* - be iterable so the controller can render every operation;
|
|
304
|
+
* - expose `disposeAll()` so the controller can clean up on
|
|
305
|
+
* disconnect.
|
|
306
|
+
*
|
|
307
|
+
* `EtaTemplateIndex` satisfies this shape; tests may return a fake.
|
|
308
|
+
*/
|
|
309
|
+
createIndex?: (factory: TemplateOperationFactory<TContext>) => EtaTemplateIndexLike<TContext>;
|
|
310
|
+
/**
|
|
311
|
+
* Optional allowlist of attribute names the factory should
|
|
312
|
+
* consider. When provided, attributes outside the list are skipped.
|
|
313
|
+
*
|
|
314
|
+
* When omitted, the factory defaults to observing every attribute.
|
|
315
|
+
*/
|
|
316
|
+
attributeNames?: readonly string[];
|
|
317
|
+
/**
|
|
318
|
+
* When `true`, the controller tells the factory to ignore any
|
|
319
|
+
* attribute allowlist and observe every attribute. Useful when
|
|
320
|
+
* templates use non-standard attribute names (e.g. plain `href`
|
|
321
|
+
* with a template literal).
|
|
322
|
+
*
|
|
323
|
+
* The controller owns its default factory privately; this option
|
|
324
|
+
* controls the factory and observer that the controller builds.
|
|
325
|
+
*/
|
|
326
|
+
observeAllAttributes?: boolean;
|
|
327
|
+
/**
|
|
328
|
+
* Optional predicate evaluated by the controller to decide whether
|
|
329
|
+
* an `Element` is a topology boundary (i.e. owned by a different
|
|
330
|
+
* controller).
|
|
331
|
+
*
|
|
332
|
+
* Threaded through to the index so the index's traversal and the
|
|
333
|
+
* host's own child accounting share a single rule. Without this
|
|
334
|
+
* predicate the index still walks the host's subtree but cannot
|
|
335
|
+
* tell apart foreign boundaries.
|
|
336
|
+
*
|
|
337
|
+
* ⚠️ **Stable-on-raw-DOM.** The predicate MUST work on raw DOM
|
|
338
|
+
* shape (tag names, marker attributes) rather than prototype-side
|
|
339
|
+
* state such as `instanceof BdScopedElement` or runtime brands
|
|
340
|
+
* set in custom-element classes. The parent index may scan a
|
|
341
|
+
* subtree before a nested custom element upgrades; without a
|
|
342
|
+
* stable raw-DOM check the parent index will silently absorb the
|
|
343
|
+
* nested scope's templates. See `EtaTemplateIndexOptions.
|
|
344
|
+
* isBoundaryElement` for full rationale and example patterns.
|
|
345
|
+
*
|
|
346
|
+
* Example pattern (tag-name based, stable before upgrade):
|
|
347
|
+
*
|
|
348
|
+
* ```ts
|
|
349
|
+
* isBoundaryElement: (element) => {
|
|
350
|
+
* const name = element.localName;
|
|
351
|
+
* return (
|
|
352
|
+
* name === 'bd-context' ||
|
|
353
|
+
* name === 'bd-product' ||
|
|
354
|
+
* name === 'bd-option'
|
|
355
|
+
* );
|
|
356
|
+
* }
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
isBoundaryElement?: (element: Element) => boolean;
|
|
360
|
+
/**
|
|
361
|
+
* Optional per-operation error handler.
|
|
362
|
+
*
|
|
363
|
+
* **Continue vs. throw behaviour:**
|
|
364
|
+
*
|
|
365
|
+
* - When this handler is provided, the render loop catches errors
|
|
366
|
+
* thrown by individual operations and continues with the next
|
|
367
|
+
* operation. The render pass is considered successful and the
|
|
368
|
+
* Task resolves normally with the most recent context. This is
|
|
369
|
+
* the right behaviour when callers want to observe partial
|
|
370
|
+
* failures (e.g. telemetry, fallback rendering) without aborting
|
|
371
|
+
* the whole render.
|
|
372
|
+
* - When omitted, the first error thrown by an operation propagates
|
|
373
|
+
* out of the render loop, the Task transitions to its error
|
|
374
|
+
* state, and `updateComplete` rejects. The previous successful
|
|
375
|
+
* render's output stays in the DOM because no later operation
|
|
376
|
+
* overwrites it.
|
|
377
|
+
*
|
|
378
|
+
* Mixing both modes is not supported — there is no way to observe
|
|
379
|
+
* the error AND propagate it in a single render pass. If both are
|
|
380
|
+
* needed, use the handler here and re-throw explicitly from inside
|
|
381
|
+
* it.
|
|
382
|
+
*/
|
|
383
|
+
onRenderError?: (error: unknown, operation: TemplateOperation<TContext>) => void;
|
|
384
|
+
/**
|
|
385
|
+
* Optional schedule for `EtaMutationBatcher` flushes (round 6).
|
|
386
|
+
*
|
|
387
|
+
* Controls how the controller coalesces DOM mutations before
|
|
388
|
+
* forwarding them to the index. Defaults to `'animation-frame'`
|
|
389
|
+
* when omitted, which matches the design doc's recommended
|
|
390
|
+
* production default for AEM / SPA-style bursty DOM updates.
|
|
391
|
+
*
|
|
392
|
+
* Schedules:
|
|
393
|
+
*
|
|
394
|
+
* - `'microtask'` — lowest latency. The flush runs on the next
|
|
395
|
+
* microtask. Useful for tests that want deterministic flushes
|
|
396
|
+
* via `await new Promise<void>(r => queueMicrotask(r))`.
|
|
397
|
+
* - `'animation-frame'` — flush via `requestAnimationFrame`.
|
|
398
|
+
* Recommended default; coalesces DOM churn before paint.
|
|
399
|
+
* - `'idle'` — flush via `requestIdleCallback` (with a
|
|
400
|
+
* `setTimeout` fallback). Lowest priority.
|
|
401
|
+
* - `'manual'` — never auto-flush. The host owns the lifecycle
|
|
402
|
+
* and must call `controller.flushMutations()` /
|
|
403
|
+
* `controller.flushAndRender()` itself. Intended for
|
|
404
|
+
* host-driven lifecycles (for example, AEM block decoration
|
|
405
|
+
* flows where the host knows exactly when a decoration phase
|
|
406
|
+
* ends).
|
|
407
|
+
*
|
|
408
|
+
* **Has no effect on tests that bypass the controller.** Tests
|
|
409
|
+
* that drive the observer directly still observe records through
|
|
410
|
+
* the legacy `TemplateIndexSink` path because they construct
|
|
411
|
+
* the observer with an explicit `sink` rather than relying on
|
|
412
|
+
* the controller's batcher wiring.
|
|
413
|
+
*
|
|
414
|
+
* **Has no effect when a custom observer is injected.** The
|
|
415
|
+
* controller owns the batcher; consumers that want a different
|
|
416
|
+
* forwarding destination should construct their own observer
|
|
417
|
+
* and skip the controller's batcher.
|
|
418
|
+
*/
|
|
419
|
+
mutationBatchSchedule?: EtaMutationBatchScheduleOption;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Options accepted by `EtaTemplateIndex`.
|
|
423
|
+
*
|
|
424
|
+
* The controller passes these through to the index it constructs. The
|
|
425
|
+
* index is otherwise opaque; boundary detection and ownership rules
|
|
426
|
+
* live here so the index does not need to know about products,
|
|
427
|
+
* options, or scoped elements.
|
|
428
|
+
*/
|
|
429
|
+
export interface EtaTemplateIndexOptions {
|
|
430
|
+
/**
|
|
431
|
+
* Predicate that decides whether an `Element` should be treated as a
|
|
432
|
+
* traversal boundary.
|
|
433
|
+
*
|
|
434
|
+
* When the predicate returns `true` for an element:
|
|
435
|
+
*
|
|
436
|
+
* - If the element is this index's own root, it is still indexed as
|
|
437
|
+
* the local boundary. Its own attributes and owned descendants are
|
|
438
|
+
* collected.
|
|
439
|
+
* - If the element is not this index's root, it is a foreign
|
|
440
|
+
* boundary. The index does not collect the foreign boundary's
|
|
441
|
+
* attributes and does not descend into its subtree.
|
|
442
|
+
*
|
|
443
|
+
* The predicate is also consulted during mutation entry points: an
|
|
444
|
+
* attribute or text mutation on a foreign boundary element or its
|
|
445
|
+
* descendants is ignored.
|
|
446
|
+
*
|
|
447
|
+
* ⚠️ **Stable-on-raw-DOM invariant.** The predicate MUST work on
|
|
448
|
+
* raw DOM shape (e.g. `element.localName`, `element.tagName`,
|
|
449
|
+
* marker attributes) rather than on prototype-side state such as
|
|
450
|
+
* `element instanceof BdScopedElement` or a runtime brand set in
|
|
451
|
+
* a custom-element class. The reason: the parent index may scan
|
|
452
|
+
* a subtree before a nested custom element upgrades, in which
|
|
453
|
+
* case the nested element has not yet been augmented with the
|
|
454
|
+
* brand and the parent index would accidentally collect its
|
|
455
|
+
* templates. No DOM mutation necessarily fires later when the
|
|
456
|
+
* upgrade completes, so the parent index may keep the wrong
|
|
457
|
+
* operations. Prefer tag-name or marker-attribute checks so the
|
|
458
|
+
* classification is stable from the moment the element is
|
|
459
|
+
* parsed.
|
|
460
|
+
*
|
|
461
|
+
* Example patterns:
|
|
462
|
+
*
|
|
463
|
+
* ```ts
|
|
464
|
+
* // Tag-name based — works on raw DOM, no upgrade required.
|
|
465
|
+
* isBoundaryElement: (element) => {
|
|
466
|
+
* const name = element.localName;
|
|
467
|
+
* return (
|
|
468
|
+
* name === 'bd-context' ||
|
|
469
|
+
* name === 'bd-product' ||
|
|
470
|
+
* name === 'bd-option'
|
|
471
|
+
* );
|
|
472
|
+
* }
|
|
473
|
+
* ```
|
|
474
|
+
*
|
|
475
|
+
* ```ts
|
|
476
|
+
* // Marker-attribute based — also stable before upgrade.
|
|
477
|
+
* isBoundaryElement: (element) =>
|
|
478
|
+
* element.hasAttribute('bd-scope') ||
|
|
479
|
+
* element.matches('bd-context,bd-product,bd-option')
|
|
480
|
+
* ```
|
|
481
|
+
*
|
|
482
|
+
* ```ts
|
|
483
|
+
* // Brand + tag-name fallback — keep the runtime brand as a fast
|
|
484
|
+
* path for already-upgraded hosts, but always fall back to a
|
|
485
|
+
// stable raw-DOM check so the parent index stays correct while
|
|
486
|
+
// the subtree is upgrading.
|
|
487
|
+
* isBoundaryElement: (element) =>
|
|
488
|
+
* isBdScopedElement(element) ||
|
|
489
|
+
* element.matches('bd-context,bd-product,bd-option')
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
readonly isBoundaryElement?: (element: Element) => boolean;
|
|
493
|
+
/**
|
|
494
|
+
* Root owned by this index.
|
|
495
|
+
*
|
|
496
|
+
* Mutations received by the index that target nodes outside the
|
|
497
|
+
* subtree rooted at `root` are dropped. Without `root`, the index
|
|
498
|
+
* accepts every mutation it receives — callers that wire a
|
|
499
|
+
* parent observer must set `root` to prevent nested-scope leakage.
|
|
500
|
+
*
|
|
501
|
+
* The root is an `Element` for light-DOM indices and a
|
|
502
|
+
* `ShadowRoot` for shadow-DOM indices. The controller walks
|
|
503
|
+
* the host's light subtree during `hostConnected` looking for
|
|
504
|
+
* attached open shadow roots and creates a per-root index for
|
|
505
|
+
* each one. Tests may pass either kind.
|
|
506
|
+
*/
|
|
507
|
+
readonly root?: Element | ShadowRoot;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Structural minimum required for an Eta template index.
|
|
511
|
+
*
|
|
512
|
+
* The real `EtaTemplateIndex` (in `./template-index.js`) implements this
|
|
513
|
+
* shape. Splitting the contract out lets `types.ts` stay free of the
|
|
514
|
+
* index implementation, avoiding a circular import.
|
|
515
|
+
*/
|
|
516
|
+
export interface EtaTemplateIndexLike<TContext = object> extends TemplateIndexSink, Iterable<TemplateOperation<TContext>> {
|
|
517
|
+
disposeAll(): void;
|
|
518
|
+
initialize(root: ParentNode): void;
|
|
519
|
+
/**
|
|
520
|
+
* Number of operations currently held by the index.
|
|
521
|
+
*
|
|
522
|
+
* The real `EtaTemplateIndex` (in `./template-index.js`) is the
|
|
523
|
+
* sole intended implementer; the field is required because
|
|
524
|
+
* diagnostics and tests read it directly without an
|
|
525
|
+
* `?? 0` fallback. Fake indices used in tests must provide
|
|
526
|
+
* `get size(): number { return 0; }` (or equivalent).
|
|
527
|
+
*/
|
|
528
|
+
readonly size: number;
|
|
529
|
+
}
|