@repobit/dex-store-elements 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { BdScope, type BdDataLayerFn, type BdScopeChangeFn, type BdScopeDeriveFn
3
3
  import { BdComputeController } from '../controllers/controller.compute.js';
4
4
  import { type BdTransition } from '../core/actions.js';
5
5
  import type { EtaMutationBatchSchedule } from '../eta/types.js';
6
- import { type TemplateOptionContext, type TemplateProductContext, type TemplateStateContext } from '../renders/context.js';
6
+ import { type TemplateDslContext } from '../renders/context.js';
7
7
  import type { Store } from '@repobit/dex-store';
8
8
  import type { PropertyValues } from 'lit';
9
9
  import { BdNodeElement } from './node.element.js';
@@ -16,20 +16,12 @@ import { BdNodeElement } from './node.element.js';
16
16
  * so prefer extending `TemplateDslContext` and rebinding this snapshot
17
17
  * rather than spreading the raw `BdScope` object.
18
18
  *
19
- * `derived` carries whatever object `scope.derived({...})` returned for the
20
- * current scope templates read derived properties as `<%= it.derived.foo
21
- * %>`. `undefined` when the scope has no `derived` factory.
19
+ * Values returned by `scope.derived({...})` are deep-merged into this
20
+ * context at the root, matching v1. The raw store and full scope are not
21
+ * part of the public template surface.
22
22
  */
23
- export type BdScopedTemplateContext = {
24
- store: Store | undefined;
25
- product: TemplateProductContext | undefined;
26
- option: TemplateOptionContext | undefined;
27
- state: TemplateStateContext | undefined;
28
- ctx: TemplateStateContext | undefined;
29
- derived: Readonly<Record<string, unknown>> | undefined;
30
- };
23
+ export type BdScopedTemplateContext = TemplateDslContext;
31
24
  export declare abstract class BdScopedElement extends BdNodeElement {
32
- #private;
33
25
  private readonly scopedValueKeys;
34
26
  protected computeResult?: BdComputeResult;
35
27
  readonly compute: BdComputeController;
@@ -50,12 +42,11 @@ export declare abstract class BdScopedElement extends BdNodeElement {
50
42
  * `isEtaEnabled` so a disabled scope does not flicker
51
43
  * intermediate state.
52
44
  *
53
- * The context exposes **only** `store`, `product`, `option`,
54
- * and `state` exactly the fields templates are expected to
55
- * reference. We do not spread the full scope, do not surface
56
- * `$scope` / `$state` handles, and do not leak the transition
57
- * object. New fields belong on `BdScope` plus an explicit
58
- * binding here so the public template surface stays auditable.
45
+ * The base context exposes only `product`, `option`, `state`,
46
+ * and the `ctx` alias. A user `derived` factory may deep-merge
47
+ * additional root values. We do not spread the full scope,
48
+ * expose the raw store, surface `$scope` / `$state` handles, or
49
+ * leak transitions and controller flags.
59
50
  *
60
51
  * The boundary predicate uses raw tag names so nested
61
52
  * `bd-context` / `bd-product` / `bd-option` nodes
@@ -84,7 +75,12 @@ export declare abstract class BdScopedElement extends BdNodeElement {
84
75
  price: import("../compute/compute.expand.js").BdComputeMonthlyRange;
85
76
  discountedPrice: import("../compute/compute.expand.js").BdComputeMonthlyRange;
86
77
  discount: import("../compute/compute.expand.js").BdComputeDiscountRange;
87
- }> | undefined], BdScopedTemplateContext>;
78
+ }> | undefined], Readonly<Record<string, unknown> & {
79
+ product: import("../renders/context.js").TemplateProductContext | undefined;
80
+ option: import("../renders/context.js").TemplateOptionContext | undefined;
81
+ state: import("../renders/context.js").TemplateStateContext | undefined;
82
+ ctx: import("../renders/context.js").TemplateStateContext | undefined;
83
+ }>>;
88
84
  etaDisabled: boolean;
89
85
  protected get isEtaEnabled(): boolean;
90
86
  computeDisabled: boolean;
@@ -95,10 +91,9 @@ export declare abstract class BdScopedElement extends BdNodeElement {
95
91
  /**
96
92
  * Comma-separated list of event ids to ignore.
97
93
  *
98
- * Pair with `data-store-event-id` on action elements: any transition
99
- * whose command carries a matching `eventId` is dropped before it can
100
- * drive this node (or any of its descendants, since the cascade is
101
- * gated here).
94
+ * Pair with `data-store-event-id` on action elements (legacy
95
+ * `data-store-id` is also accepted): any transition whose command
96
+ * carries a matching `eventId` is dropped before it can drive this node.
102
97
  *
103
98
  * Example:
104
99
  * ```html
@@ -185,10 +180,10 @@ export declare abstract class BdScopedElement extends BdNodeElement {
185
180
  *
186
181
  * Users supply this on any bd-* node (via constructor,
187
182
  * attribute, or `instance.derived = fn`) to expose additional
188
- * template bindings under the `derived` key. The factory
189
- * receives `{ store, product, option }` and returns an object
190
- * whose properties become available to templates as
191
- * `<%= it.derived.<key> %>`.
183
+ * template bindings at the context root. The factory receives
184
+ * `{ product, option, state }`; its result is deep-merged with
185
+ * the base v1 context, so a returned `foo` is read as
186
+ * `<%= it.foo %>`.
192
187
  *
193
188
  * `publishScope` merges this property into the published scope
194
189
  * automatically when the caller did not supply an explicit
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { bdComputeContext } from '../contexts/context.compute.js';
8
- import { bdScopeContext, createBdStoreScope, runBdScopeDerived } from '../contexts/context.scope.js';
8
+ import { bdScopeContext, createBdStoreScope } from '../contexts/context.scope.js';
9
9
  import { BdComputeController } from '../controllers/controller.compute.js';
10
10
  import { getTransitionEventId } from '../core/actions.js';
11
11
  import { createEtaTemplateController } from '../eta/controller.js';
@@ -100,12 +100,11 @@ export class BdScopedElement extends BdNodeElement {
100
100
  * `isEtaEnabled` so a disabled scope does not flicker
101
101
  * intermediate state.
102
102
  *
103
- * The context exposes **only** `store`, `product`, `option`,
104
- * and `state` exactly the fields templates are expected to
105
- * reference. We do not spread the full scope, do not surface
106
- * `$scope` / `$state` handles, and do not leak the transition
107
- * object. New fields belong on `BdScope` plus an explicit
108
- * binding here so the public template surface stays auditable.
103
+ * The base context exposes only `product`, `option`, `state`,
104
+ * and the `ctx` alias. A user `derived` factory may deep-merge
105
+ * additional root values. We do not spread the full scope,
106
+ * expose the raw store, surface `$scope` / `$state` handles, or
107
+ * leak transitions and controller flags.
109
108
  *
110
109
  * The boundary predicate uses raw tag names so nested
111
110
  * `bd-context` / `bd-product` / `bd-option` nodes
@@ -120,21 +119,13 @@ export class BdScopedElement extends BdNodeElement {
120
119
  this.computeResult
121
120
  ],
122
121
  enabled: () => this.isEtaEnabled,
123
- context: ([scope, compute]) => {
124
- const dsl = buildTemplateDslContext({
125
- store: scope?.store,
122
+ context: async ([scope, compute]) => {
123
+ return await buildTemplateDslContext({
126
124
  product: scope?.product,
127
125
  option: scope?.option,
128
- state: compute
126
+ state: compute,
127
+ derived: scope?.derived
129
128
  });
130
- return {
131
- store: dsl.store,
132
- product: dsl.product,
133
- option: dsl.option,
134
- state: dsl.state,
135
- ctx: dsl.ctx,
136
- derived: runBdScopeDerived(scope)
137
- };
138
129
  },
139
130
  /**
140
131
  * Mutation batch schedule is resolved from the current
@@ -201,19 +192,8 @@ export class BdScopedElement extends BdNodeElement {
201
192
  return (this.computeDisabled !== true &&
202
193
  this.parentScope?.computeDisabled !== true);
203
194
  }
204
- /**
205
- * Lazy cache of the parsed `ignore-events` attribute.
206
- *
207
- * Re-parsed whenever the attribute changes; consumers should access
208
- * `this.ignoredEventIds` rather than calling `parseIgnoreEvents` themselves.
209
- */
210
- #ignoredEventIds;
211
195
  get ignoredEventIds() {
212
- if (this.#ignoredEventIds !== undefined) {
213
- return this.#ignoredEventIds;
214
- }
215
- this.#ignoredEventIds = parseIgnoreEvents(this.ignoreEvents);
216
- return this.#ignoredEventIds;
196
+ return parseIgnoreEvents(this.ignoreEvents);
217
197
  }
218
198
  /**
219
199
  * Filter callback handed to each node's `BdActionResolveController`.
@@ -448,9 +428,6 @@ export class BdScopedElement extends BdNodeElement {
448
428
  changedProperties.has('onScopeChange') ||
449
429
  changedProperties.has('dataLayer');
450
430
  const storeChanged = changedProperties.has('store');
451
- if (changedProperties.has('ignoreEvents')) {
452
- this.#ignoredEventIds = undefined;
453
- }
454
431
  if (!overlayPropsChanged && !storeChanged) {
455
432
  return;
456
433
  }
@@ -534,6 +511,7 @@ __decorate([
534
511
  attribute: false
535
512
  })
536
513
  ], BdScopedElement.prototype, "store", void 0);
514
+ const EMPTY_EVENT_IDS = new Set();
537
515
  /**
538
516
  * Parses the comma-separated `ignore-events` attribute into a set of ids.
539
517
  *
@@ -554,5 +532,4 @@ export function parseIgnoreEvents(value) {
554
532
  }
555
533
  return ids.size === 0 ? EMPTY_EVENT_IDS : ids;
556
534
  }
557
- const EMPTY_EVENT_IDS = new Set();
558
535
  //# sourceMappingURL=node.scope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.scope.js","sourceRoot":"","sources":["../../../src/core/node.scope.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BdComputeResult } from '@/compute/compute.expand.js';\nimport { bdComputeContext } from '@/contexts/context.compute.js';\nimport {\n BdScope,\n bdScopeContext,\n createBdStoreScope,\n runBdScopeDerived,\n type BdDataLayerFn,\n type BdScopeChangeFn,\n type BdScopeChangeSnapshot,\n type BdScopeDeriveFn\n} from '@/contexts/context.scope.js';\nimport { BdComputeController } from '@/controllers/controller.compute.js';\nimport {\n getTransitionEventId,\n type BdTransition\n} from '@/core/actions.js';\nimport {\n createEtaTemplateController\n} from '@/eta/controller.js';\nimport type { EtaMutationBatchSchedule } from '@/eta/types.js';\nimport {\n buildTemplateDslContext,\n type TemplateOptionContext,\n type TemplateProductContext,\n type TemplateStateContext\n} from '@/renders/context.js';\nimport { consume, provide } from '@lit/context';\nimport type {\n Store\n} from '@repobit/dex-store';\nimport type { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { BdNodeElement } from './node.element.js';\n/**\n * The shape templates see under a `BdScopedElement`.\n *\n * Mirrors the v1 `it.*` layout: `option` / `product` / `state` are flattened\n * DTOs with pre-formatted strings, and `ctx` is an alias of `state`. Adding\n * a new field here is a public surface change (every consumer can read it),\n * so prefer extending `TemplateDslContext` and rebinding this snapshot\n * rather than spreading the raw `BdScope` object.\n *\n * `derived` carries whatever object `scope.derived({...})` returned for the\n * current scope — templates read derived properties as `<%= it.derived.foo\n * %>`. `undefined` when the scope has no `derived` factory.\n */\nexport type BdScopedTemplateContext = {\n store : Store | undefined;\n product: TemplateProductContext | undefined;\n option : TemplateOptionContext | undefined;\n state : TemplateStateContext | undefined;\n ctx : TemplateStateContext | undefined;\n derived: Readonly<Record<string, unknown>> | undefined;\n};\n\nfunction hasOwn(\n object: object,\n key: PropertyKey\n): boolean {\n return Object.prototype.hasOwnProperty.call(\n object,\n key\n );\n}\n\n/**\n * Applies sparse boolean flags to a scope object.\n *\n * Flags are represented by presence, not by true/false values:\n * - `computeDisabled: true` means compute is disabled;\n * - missing `computeDisabled` means compute is enabled.\n *\n * The function preserves the original scope reference when the flags already\n * match the requested state. Otherwise, it returns a shallow clone with the\n * required flags added or removed.\n */\n/**\n * Overlay shape consumed by `applyScopedFlags`.\n *\n * `flags` is the sparse-boolean phase (`computeDisabled`,\n * `etaDisabled`). `values` is the value-merge phase (`derived`,\n * `mutationBatchSchedule`). Both phases run in the same lazy\n * allocation slot, so any write in either phase causes the\n * scope to be cloned exactly once.\n */\ntype BdScopeOverlay = Readonly<{\n flags : Partial<Record<\n 'computeDisabled' | 'etaDisabled',\n boolean\n >>;\n values: Partial<{\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n }>;\n}>;\n\ntype BdScopeValueKey =\n | 'derived'\n | 'mutationBatchSchedule'\n | 'onScopeChange'\n | 'dataLayer';\n\n/**\n * Sparse flag application.\n *\n * `applyScopedFlags` is the single pass that merges per-node\n * overlays into the published scope:\n *\n * - **sparse boolean flags** — `computeDisabled` and\n * `etaDisabled`. Represented by presence, not by `true` /\n * `false`: missing means enabled, present and `true` means\n * disabled. Setting `enabled=false` deletes the flag rather\n * than storing `false`.\n *\n * - **value overlays** — `derived` and\n * `mutationBatchSchedule`. Caller-supplied values win; the\n * node's own property fills in when the caller did not supply\n * one. Missing on both sides means the field is absent from\n * the published scope.\n *\n * The function preserves the original `object` reference when\n * nothing actually changes. The `nextObject` slot is only\n * allocated the first time a write happens, so a scope with\n * nothing to merge returns the same reference it was given —\n * important because `publishScope` uses `Object.is` to short-\n * circuit downstream `@provide` notifications.\n */\nfunction applyScopedFlags(\n scope: BdScope,\n overlays: Readonly<{\n flags: Partial<Record<\n 'computeDisabled' | 'etaDisabled',\n boolean\n >>;\n values: Partial<{\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n }>;\n }>\n): BdScope {\n let nextObject: BdScope | undefined;\n\n const getNextObject = (): BdScope => {\n if (nextObject !== undefined) {\n return nextObject;\n }\n\n nextObject = {\n ...scope\n };\n\n return nextObject;\n };\n\n for (\n const [key, enabled] of Object.entries(overlays.flags) as [\n 'computeDisabled' | 'etaDisabled',\n boolean\n ][]\n ) {\n const currentValue = scope[key];\n\n if (enabled) {\n if (currentValue !== true) {\n (getNextObject() as Record<string, unknown>)[key] = true;\n }\n\n continue;\n }\n\n if (hasOwn(scope, key)) {\n delete (getNextObject() as Record<string, unknown>)[key];\n }\n }\n\n for (\n const [key, value] of Object.entries(overlays.values) as [\n 'derived' | 'mutationBatchSchedule' | 'onScopeChange' | 'dataLayer',\n BdScopeDeriveFn |\n EtaMutationBatchSchedule |\n BdScopeChangeFn |\n BdDataLayerFn\n ][]\n ) {\n if (value === undefined) {\n continue;\n }\n\n if (Object.is(scope[key], value)) {\n continue;\n }\n\n (getNextObject() as Record<string, unknown>)[key] = value;\n }\n\n return nextObject ?? scope;\n}\n\nexport abstract class BdScopedElement extends BdNodeElement {\n private readonly scopedValueKeys = new Set<BdScopeValueKey>();\n\n @provide({\n context: bdComputeContext\n })\n @state()\n protected computeResult?: BdComputeResult;\n\n readonly compute =\n new BdComputeController(this, {\n store: () => this.currentScope?.store,\n\n enabled: () => this.isComputeEnabled,\n\n onResult: (result) => {\n const previous = this.computeResult;\n\n this.computeResult = result;\n\n if (Object.is(previous, result)) {\n return;\n }\n\n this._fireOnScopeChangeForCompute();\n }\n });\n\n /**\n * Owning boundary for the Eta template index.\n *\n * `BdScopedElement` IS the index root — every nested template\n * is owned by the closest enclosing bd-* node, and that node\n * in turn owns the controller that renders its subtree.\n */\n get root(): Element {\n return this;\n }\n\n /**\n * Eta template controller that renders the subtree against the\n * current scope and compute result.\n *\n * Args trigger a re-render when the scope, the compute result,\n * or the eta-enabled flag change. The render is gated on\n * `isEtaEnabled` so a disabled scope does not flicker\n * intermediate state.\n *\n * The context exposes **only** `store`, `product`, `option`,\n * and `state` — exactly the fields templates are expected to\n * reference. We do not spread the full scope, do not surface\n * `$scope` / `$state` handles, and do not leak the transition\n * object. New fields belong on `BdScope` plus an explicit\n * binding here so the public template surface stays auditable.\n *\n * The boundary predicate uses raw tag names so nested\n * `bd-context` / `bd-product` / `bd-option` nodes\n * keep their own controllers and templates do not leak across\n * scope boundaries. Tag-name checks are stable before custom\n * element upgrade — `EtaTemplateControllerOptions.isBoundaryElement`\n * MUST work on raw DOM shape.\n */\n readonly eta =\n createEtaTemplateController<\n readonly [\n scope : BdScope | undefined,\n compute: BdComputeResult | undefined\n ],\n BdScopedTemplateContext\n >(this, {\n args: () => [\n this.currentScope,\n this.computeResult\n ] as const,\n\n enabled: () => this.isEtaEnabled,\n\n context: ([scope, compute]) => {\n const dsl = buildTemplateDslContext({\n store : scope?.store,\n product: scope?.product,\n option : scope?.option,\n state : compute\n });\n\n return {\n store : dsl.store,\n product: dsl.product,\n option : dsl.option,\n state : dsl.state,\n ctx : dsl.ctx,\n derived: runBdScopeDerived(scope)\n };\n },\n\n /**\n * Mutation batch schedule is resolved from the current\n * published scope whenever the batcher schedules a future\n * flush. The value flows through `BdScope` like `derived`\n * does, so a parent that republishes a new\n * `mutationBatchSchedule` is observed by already-constructed\n * descendants on subsequent mutation bursts.\n *\n * Resolution order at scheduling time:\n *\n * 1. `this.currentScope?.mutationBatchSchedule` — this\n * element's normalized published scope, when it has one.\n * It already includes inherited values plus this node's\n * own override.\n * 2. `this.parentScope?.mutationBatchSchedule` — inherited\n * schedule for scoped consumers that do not publish a\n * scope of their own.\n * 3. The Eta controller's own default\n * (`'animation-frame'`).\n *\n * The cascade mirrors `derived`, but the Eta controller gets\n * a resolver callback rather than the current value so the\n * batcher can read the latest published scope.\n */\n mutationBatchSchedule: () =>\n this.currentScope?.mutationBatchSchedule ??\n this.parentScope?.mutationBatchSchedule,\n\n isBoundaryElement: (element) => {\n const name = element.localName;\n\n return (\n name === 'bd-context' ||\n name === 'bd-product' ||\n name === 'bd-option'\n );\n }\n });\n\n @property({\n attribute: 'eta-disabled',\n type : Boolean,\n reflect : true\n })\n etaDisabled = false;\n\n protected get isEtaEnabled(): boolean {\n return (\n this.etaDisabled !== true &&\n this.parentScope?.etaDisabled !== true\n );\n }\n\n @property({\n attribute: 'compute-disabled',\n type : Boolean,\n reflect : true\n })\n computeDisabled = false;\n\n @consume({\n context : bdScopeContext,\n subscribe: true\n })\n @property({\n attribute: false\n })\n protected parentScope?: BdScope;\n\n @provide({\n context: bdScopeContext\n })\n @state()\n protected providedScope?: BdScope;\n\n protected get currentScope(): BdScope | undefined {\n return this.providedScope;\n }\n\n protected get isComputeEnabled(): boolean {\n return (\n this.computeDisabled !== true &&\n this.parentScope?.computeDisabled !== true\n );\n }\n\n /**\n * Comma-separated list of event ids to ignore.\n *\n * Pair with `data-store-event-id` on action elements: any transition\n * whose command carries a matching `eventId` is dropped before it can\n * drive this node (or any of its descendants, since the cascade is\n * gated here).\n *\n * Example:\n * ```html\n * <button data-store-action\n * data-store-event-id=\"devicesBtn\"\n * data-store-set-devices=\"25\">25</button>\n *\n * <bd-option ignore-events=\"devicesBtn, subscriptionBtn\">\n * ...\n * </bd-option>\n * ```\n */\n @property({\n attribute: 'ignore-events'\n })\n ignoreEvents?: string;\n\n /**\n * When set, this node only reacts to events originating inside its own\n * subtree. Cascading transitions from `parentScope.transition` are\n * dropped, mirroring v1's `<bd-state ignore-events-parent>` semantics.\n *\n * `<bd-context>` enables this by default (it is a hard event boundary).\n */\n @property({\n attribute: 'ignore-events-parent',\n type : Boolean,\n reflect : true\n })\n ignoreEventsParent = false;\n\n /**\n * Lazy cache of the parsed `ignore-events` attribute.\n *\n * Re-parsed whenever the attribute changes; consumers should access\n * `this.ignoredEventIds` rather than calling `parseIgnoreEvents` themselves.\n */\n #ignoredEventIds?: ReadonlySet<string>;\n\n protected get ignoredEventIds(): ReadonlySet<string> {\n if (this.#ignoredEventIds !== undefined) {\n return this.#ignoredEventIds;\n }\n\n this.#ignoredEventIds = parseIgnoreEvents(this.ignoreEvents);\n\n return this.#ignoredEventIds;\n }\n\n /**\n * Filter callback handed to each node's `BdActionResolveController`.\n *\n * Drops transitions whose `eventId` matches an entry in `ignore-events`,\n * and — when `ignore-events-parent` is set — drops any cascade from a\n * parent scope. Concrete nodes pass `filterTransition: (c) =>\n * this._filterTransition(c)` into their controller config; the\n * controller invokes this for both local `bd-action-request` events\n * and inherited transitions.\n */\n protected _filterTransition(candidate: {\n transition: BdTransition;\n source : 'local' | 'parent';\n }): boolean {\n if (\n this.ignoreEventsParent &&\n candidate.source === 'parent'\n ) {\n return false;\n }\n\n const ignored = this.ignoredEventIds;\n\n if (ignored.size === 0) {\n return true;\n }\n\n const eventId = getTransitionEventId(candidate.transition);\n\n if (eventId === undefined) {\n return true;\n }\n\n return !ignored.has(eventId);\n }\n\n protected publishScope(nextScope: BdScope): BdScope {\n const sourceScope = Object.is(nextScope, this.providedScope)\n ? this.scopeWithoutOwnedValues(nextScope)\n : nextScope;\n const normalizedScope = this.applyScopedFlags(sourceScope);\n\n this.updateScopedValueKeys(sourceScope, normalizedScope);\n\n if (Object.is(this.providedScope, normalizedScope)) {\n return normalizedScope;\n }\n\n const previousScope = this.providedScope;\n const hadPreviousScope = previousScope !== undefined;\n const previousSnapshot = this.buildScopeChangeSnapshot(\n previousScope\n );\n const nextSnapshot = this.buildScopeChangeSnapshot(\n normalizedScope\n );\n\n this.providedScope = normalizedScope;\n\n // Fire the hook when the observable snapshot changed OR\n // when the scope transitioned in or out of existence. The\n // latter covers the \"first publish\" and \"clear\" lifecycle\n // events even when the snapshot itself is all-undefined.\n const existenceChanged = !hadPreviousScope;\n const observableChanged = !this.snapshotsEqual(\n previousSnapshot,\n nextSnapshot\n );\n\n if (existenceChanged || observableChanged) {\n const onScopeChange =\n normalizedScope.onScopeChange ??\n previousScope?.onScopeChange;\n\n onScopeChange?.(nextSnapshot);\n }\n\n return normalizedScope;\n }\n\n private buildScopeChangeSnapshot(\n scope: BdScope | undefined\n ): BdScopeChangeSnapshot {\n return {\n store : scope?.store,\n product: scope?.product,\n option : scope?.option,\n state : this.computeResult\n };\n }\n\n /**\n * Fires `onScopeChange` for compute-result-only transitions.\n *\n * `BdComputeController.onResult` writes `computeResult`, which\n * is part of the published snapshot's `state`. Without this\n * trigger, consumers watching the snapshot would miss compute\n * state changes that are not accompanied by a scope publish\n * or clear. Targeted invocation keeps the contract in one\n * place: `publishScope()` and `clearScope()` keep their own\n * paths; this helper handles the compute-only path.\n *\n * Idempotent: no-op when no scope has been published yet\n * (the hook is not on any scope) or when the snapshot's\n * `state` did not actually change (caller short-circuits via\n * `Object.is` before calling).\n */\n private _fireOnScopeChangeForCompute(): void {\n if (!this.providedScope) {\n return;\n }\n\n const snapshot = this.buildScopeChangeSnapshot(\n this.providedScope\n );\n\n const onScopeChange =\n this.providedScope.onScopeChange;\n\n onScopeChange?.(snapshot);\n }\n\n private snapshotsEqual(\n a: BdScopeChangeSnapshot,\n b: BdScopeChangeSnapshot\n ): boolean {\n return (\n a.store === b.store &&\n a.product === b.product &&\n a.option === b.option &&\n a.state === b.state\n );\n }\n\n private scopeWithoutOwnedValues(scope: BdScope): BdScope {\n let nextScope: BdScope | undefined;\n\n for (const key of this.scopedValueKeys) {\n if (!hasOwn(scope, key)) {\n continue;\n }\n\n if (nextScope === undefined) {\n nextScope = { ...scope };\n }\n\n delete (nextScope as Record<string, unknown>)[key];\n }\n\n return nextScope ?? scope;\n }\n\n private updateScopedValueKeys(\n sourceScope: BdScope,\n normalizedScope: BdScope\n ): void {\n this.updateScopedValueKey(\n 'derived',\n sourceScope,\n normalizedScope,\n this.derived\n );\n this.updateScopedValueKey(\n 'mutationBatchSchedule',\n sourceScope,\n normalizedScope,\n this.mutationBatchSchedule\n );\n this.updateScopedValueKey(\n 'onScopeChange',\n sourceScope,\n normalizedScope,\n this.onScopeChange\n );\n this.updateScopedValueKey(\n 'dataLayer',\n sourceScope,\n normalizedScope,\n this.dataLayer\n );\n }\n\n private updateScopedValueKey<T>(\n key: BdScopeValueKey,\n sourceScope: BdScope,\n normalizedScope: BdScope,\n ownValue: T | undefined\n ): void {\n if (\n ownValue !== undefined &&\n sourceScope[key] === undefined &&\n Object.is(normalizedScope[key], ownValue)\n ) {\n this.scopedValueKeys.add(key);\n return;\n }\n\n this.scopedValueKeys.delete(key);\n }\n\n protected clearScope(): void {\n if (this.store !== undefined) {\n this.publishScope(createBdStoreScope(this.store));\n\n return;\n }\n\n const previousScope = this.providedScope;\n\n if (previousScope === undefined) {\n return;\n }\n\n const onScopeChange = previousScope.onScopeChange;\n\n this.providedScope = undefined;\n\n if (onScopeChange) {\n onScopeChange({\n store : undefined,\n product: undefined,\n option : undefined,\n state : undefined\n });\n }\n }\n\n protected requireParentScope(): BdScope {\n if (!this.parentScope) {\n throw new Error(\n `${this.localName} requires a parent BdScope.`\n );\n }\n\n return this.parentScope;\n }\n\n /**\n * Single-pass overlay applied to every published scope.\n *\n * The pass runs in two phases:\n *\n * 1. **Sparse boolean flags** — `computeDisabled` and\n * `etaDisabled`. Reflected from `isComputeEnabled` /\n * `isEtaEnabled` (the inverse of \"is enabled\").\n * 2. **Value overlays** — `derived` and\n * `mutationBatchSchedule`. Caller-supplied values win; the\n * per-instance property fills in when the caller did not\n * supply one.\n *\n * Both phases share the same lazy-allocation strategy: a new\n * scope object is only created when at least one phase has a\n * write to perform. A scope with nothing to merge is returned\n * by reference, which lets `publishScope`'s `Object.is` check\n * short-circuit the downstream `@provide` notification.\n */\n private applyScopedFlags(scope: BdScope): BdScope {\n const overlay: BdScopeOverlay = {\n flags: {\n computeDisabled: !this.isComputeEnabled,\n etaDisabled : !this.isEtaEnabled\n },\n values: {}\n };\n\n if (scope.derived === undefined && this.derived !== undefined) {\n overlay.values.derived = this.derived;\n }\n\n if (\n scope.mutationBatchSchedule === undefined &&\n this.mutationBatchSchedule !== undefined\n ) {\n overlay.values.mutationBatchSchedule =\n this.mutationBatchSchedule;\n }\n\n if (\n scope.onScopeChange === undefined &&\n this.onScopeChange !== undefined\n ) {\n overlay.values.onScopeChange = this.onScopeChange;\n }\n\n if (\n scope.dataLayer === undefined &&\n this.dataLayer !== undefined\n ) {\n overlay.values.dataLayer = this.dataLayer;\n }\n\n return applyScopedFlags(scope, overlay);\n }\n\n /**\n * Per-instance derived Eta context factory.\n *\n * Users supply this on any bd-* node (via constructor,\n * attribute, or `instance.derived = fn`) to expose additional\n * template bindings under the `derived` key. The factory\n * receives `{ store, product, option }` and returns an object\n * whose properties become available to templates as\n * `<%= it.derived.<key> %>`.\n *\n * `publishScope` merges this property into the published scope\n * automatically when the caller did not supply an explicit\n * `derived` of its own — so a user just needs to assign\n * `node.derived = fn` (or set the `derived` attribute) and\n * the factory becomes part of the scope for free.\n *\n * Reactivity: the field is a Lit `@property`, so reassigning\n * `this.derived = newFactory` triggers `requestUpdate()` and a\n * re-publish via `willUpdate`. Method references declared\n * once per instance are stable, so the `@lit/task` args\n * comparison short-circuits re-renders for unrelated changes.\n *\n * `derived` is intended to be supplied by users at the call\n * site, not declared on concrete node subclasses. Subclasses\n * that need a static default can still assign it in their\n * constructor; the merge step lets callers override.\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.derived = ({ product, option }) => ({\n * canBuy : Boolean(product && option),\n * hasDiscount: Boolean(option?.discounted)\n * });\n * ```\n *\n * Or via plain JS (recommended — functions cannot be authored\n * as attribute strings):\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.derived = ({ product, option }) => ({\n * canBuy : Boolean(product && option),\n * hasDiscount: Boolean(option?.discounted)\n * });\n * ```\n *\n * The attribute mapping is intentionally omitted — Lit's\n * default converter cannot turn a string into a typed\n * function, so the property is JS-only. Hosts that want\n * attribute-driven configuration should expose a wrapper\n * attribute on their own subclass and assign `derived` from\n * the wrapper's converter.\n */\n @property({\n attribute: false\n })\n derived?: BdScopeDeriveFn;\n\n /**\n * Per-instance mutation batch schedule.\n *\n * Wired through the scope so descendants can read the same\n * value via `parentScope.mutationBatchSchedule`. Existing Eta\n * controllers receive a resolver callback, so flipping the\n * schedule after connection affects future mutation flushes\n * once the parent republishes its scope.\n *\n * Accepted via constructor / `instance.mutationBatchSchedule =\n * 'idle'`. The attribute mapping is intentionally omitted\n * because Lit's default converter is happy with the string\n * union, but exposing it as an attribute would invite the\n * same authoring pitfalls we deliberately avoid for\n * `derived` (callers writing `schedule=\"animation-frame\"`\n * without realising the value is scope-driven rather than an\n * imperative one-shot switch).\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.mutationBatchSchedule = 'microtask';\n * ```\n */\n @property({\n attribute: false\n })\n mutationBatchSchedule?: EtaMutationBatchSchedule;\n\n /**\n * Per-instance scope-change hook.\n *\n * Fired whenever this node's effective published scope changes\n * — both when `publishScope(...)` produces a new normalized\n * scope reference, and when `clearScope()` removes the\n * scope. The hook receives a narrow snapshot exposing only\n * `store`, `product`, `option`, and `state` — the four fields\n * that drive template rendering. It does NOT receive the\n * transition, the disabled flags, the derived factory, or the\n * mutation batch schedule.\n *\n * Wired through `BdScope.onScopeChange` like `derived` and\n * `mutationBatchSchedule`, so descendants can read the\n * inherited hook from `parentScope.onScopeChange` and a child\n * can override its ancestor's hook by assigning its own.\n *\n * Attribute mapping is intentionally omitted — functions\n * cannot be represented as attribute strings, and a stale or\n * stale-shaped `on-scope-change=\"...\"` attribute would invite\n * the same authoring pitfalls we deliberately avoid for\n * `derived`. Configure via JS instead:\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.onScopeChange = ({ store, product, option, state }) => {\n * console.log('scope changed', { store, product, option, state });\n * };\n * ```\n */\n @property({\n attribute: false\n })\n onScopeChange?: BdScopeChangeFn;\n\n /**\n * Inherited callback for option-load data-layer events.\n * Configure it as a JavaScript property; functions are not\n * represented as attributes.\n */\n @property({\n attribute: false\n })\n dataLayer?: BdDataLayerFn;\n\n /**\n * Per-instance store reference.\n *\n * When set on any `BdScopedElement` subclass, the value is published\n * into the scope context via `createBdStoreScope(this.store)` so\n * descendants can consume it from `parentScope.store`. Setting it back\n * to `undefined` calls `clearScope()` to remove the published scope;\n * flipping from one store to another re-publishes the existing scope\n * with the new reference.\n *\n * The store-property publish is intentionally additive — it does not\n * replace the existing `BdActionResolveController` flow used by\n * `bd-context`, `bd-product`, and `bd-option`. Those controllers\n * capture their own transition for the action-request path; the store\n * scope is purely a value-publish surface.\n *\n * @example\n *\n * ```ts\n * const root = document.createElement('bd-context');\n * root.store = myStore;\n * // descendants under <bd-context> now see { store: myStore }\n * // via parentScope.store.\n *\n * // Clear the published scope\n * root.store = undefined;\n * ```\n *\n * The attribute mapping is intentionally omitted — Lit's default\n * converter cannot turn a string into a `Store` instance, and\n * serializing a store to an attribute would defeat the point of\n * holding it as a typed reference. Configure via JS instead, the\n * same way `derived` and `onScopeChange` are configured.\n */\n @property({\n attribute: false\n })\n store?: Store;\n\n /**\n * Publishes or clears the scope for a `store` property change.\n *\n * Single entry point for the three transitions `store` can drive:\n *\n * 1. `store` is `undefined` → `clearScope()` removes the published\n * scope and fires the `onScopeChange` hook with all-undefined\n * fields.\n * 2. `store` is defined and a scope is already published →\n * re-publishes the existing scope with the new store reference\n * (the rest of the scope — `derived`, `mutationBatchSchedule`,\n * `onScopeChange`, flags — is preserved).\n * 3. `store` is defined and no scope is published yet → publishes a\n * fresh `createBdStoreScope(this.store)` so the first descendant\n * read sees a non-undefined `parentScope.store`.\n *\n * Called from `willUpdate` whenever `changedProperties.has('store')`.\n * Idempotent: re-publishing the same scope reference is a no-op for\n * the `@provide` notification because `publishScope` already short-\n * circuits on `Object.is(this.providedScope, normalizedScope)`.\n */\n protected publishStoreScope(): void {\n if (this.store === undefined) {\n this.clearScope();\n\n return;\n }\n\n if (this.providedScope) {\n this.publishScope({\n ...this.providedScope,\n store: this.store\n });\n\n return;\n }\n\n this.publishScope(createBdStoreScope(this.store));\n }\n\n protected override willUpdate(\n changedProperties: PropertyValues<this>\n ): void {\n super.willUpdate(changedProperties);\n\n const overlayPropsChanged =\n changedProperties.has('computeDisabled') ||\n changedProperties.has('etaDisabled') ||\n changedProperties.has('derived') ||\n changedProperties.has('mutationBatchSchedule') ||\n changedProperties.has('onScopeChange') ||\n changedProperties.has('dataLayer');\n const storeChanged = changedProperties.has('store');\n\n if (changedProperties.has('ignoreEvents')) {\n this.#ignoredEventIds = undefined;\n }\n\n if (!overlayPropsChanged && !storeChanged) {\n return;\n }\n\n if (storeChanged) {\n this.publishStoreScope();\n }\n\n if (overlayPropsChanged && this.providedScope) {\n this.publishScope(this.providedScope);\n }\n }\n}\n\n/**\n * Parses the comma-separated `ignore-events` attribute into a set of ids.\n *\n * Empty entries are skipped so `\"foo, ,bar\"` produces `{ foo, bar }`.\n * The function is exported so test code can compare against the same\n * parsing rules the runtime uses.\n */\nexport function parseIgnoreEvents(\n value: string | undefined\n): ReadonlySet<string> {\n if (value === undefined || value === '') {\n return EMPTY_EVENT_IDS;\n }\n\n const ids = new Set<string>();\n\n for (const part of value.split(',')) {\n const trimmed = part.trim();\n\n if (trimmed !== '') {\n ids.add(trimmed);\n }\n }\n\n return ids.size === 0 ? EMPTY_EVENT_IDS : ids;\n}\n\nconst EMPTY_EVENT_IDS: ReadonlySet<string> = new Set<string>();\n"]}
1
+ {"version":3,"file":"node.scope.js","sourceRoot":"","sources":["../../../src/core/node.scope.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BdComputeResult } from '@/compute/compute.expand.js';\nimport { bdComputeContext } from '@/contexts/context.compute.js';\nimport {\n BdScope,\n bdScopeContext,\n createBdStoreScope,\n type BdDataLayerFn,\n type BdScopeChangeFn,\n type BdScopeChangeSnapshot,\n type BdScopeDeriveFn\n} from '@/contexts/context.scope.js';\nimport { BdComputeController } from '@/controllers/controller.compute.js';\nimport {\n getTransitionEventId,\n type BdTransition\n} from '@/core/actions.js';\nimport {\n createEtaTemplateController\n} from '@/eta/controller.js';\nimport type { EtaMutationBatchSchedule } from '@/eta/types.js';\nimport {\n buildTemplateDslContext,\n type TemplateDslContext\n} from '@/renders/context.js';\nimport { consume, provide } from '@lit/context';\nimport type {\n Store\n} from '@repobit/dex-store';\nimport type { PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { BdNodeElement } from './node.element.js';\n/**\n * The shape templates see under a `BdScopedElement`.\n *\n * Mirrors the v1 `it.*` layout: `option` / `product` / `state` are flattened\n * DTOs with pre-formatted strings, and `ctx` is an alias of `state`. Adding\n * a new field here is a public surface change (every consumer can read it),\n * so prefer extending `TemplateDslContext` and rebinding this snapshot\n * rather than spreading the raw `BdScope` object.\n *\n * Values returned by `scope.derived({...})` are deep-merged into this\n * context at the root, matching v1. The raw store and full scope are not\n * part of the public template surface.\n */\nexport type BdScopedTemplateContext = TemplateDslContext;\n\nfunction hasOwn(\n object: object,\n key: PropertyKey\n): boolean {\n return Object.prototype.hasOwnProperty.call(\n object,\n key\n );\n}\n\n/**\n * Applies sparse boolean flags to a scope object.\n *\n * Flags are represented by presence, not by true/false values:\n * - `computeDisabled: true` means compute is disabled;\n * - missing `computeDisabled` means compute is enabled.\n *\n * The function preserves the original scope reference when the flags already\n * match the requested state. Otherwise, it returns a shallow clone with the\n * required flags added or removed.\n */\n/**\n * Overlay shape consumed by `applyScopedFlags`.\n *\n * `flags` is the sparse-boolean phase (`computeDisabled`,\n * `etaDisabled`). `values` is the value-merge phase (`derived`,\n * `mutationBatchSchedule`). Both phases run in the same lazy\n * allocation slot, so any write in either phase causes the\n * scope to be cloned exactly once.\n */\ntype BdScopeOverlay = Readonly<{\n flags : Partial<Record<\n 'computeDisabled' | 'etaDisabled',\n boolean\n >>;\n values: Partial<{\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n }>;\n}>;\n\ntype BdScopeValueKey =\n | 'derived'\n | 'mutationBatchSchedule'\n | 'onScopeChange'\n | 'dataLayer';\n\n/**\n * Sparse flag application.\n *\n * `applyScopedFlags` is the single pass that merges per-node\n * overlays into the published scope:\n *\n * - **sparse boolean flags** — `computeDisabled` and\n * `etaDisabled`. Represented by presence, not by `true` /\n * `false`: missing means enabled, present and `true` means\n * disabled. Setting `enabled=false` deletes the flag rather\n * than storing `false`.\n *\n * - **value overlays** — `derived` and\n * `mutationBatchSchedule`. Caller-supplied values win; the\n * node's own property fills in when the caller did not supply\n * one. Missing on both sides means the field is absent from\n * the published scope.\n *\n * The function preserves the original `object` reference when\n * nothing actually changes. The `nextObject` slot is only\n * allocated the first time a write happens, so a scope with\n * nothing to merge returns the same reference it was given —\n * important because `publishScope` uses `Object.is` to short-\n * circuit downstream `@provide` notifications.\n */\nfunction applyScopedFlags(\n scope: BdScope,\n overlays: Readonly<{\n flags: Partial<Record<\n 'computeDisabled' | 'etaDisabled',\n boolean\n >>;\n values: Partial<{\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n }>;\n }>\n): BdScope {\n let nextObject: BdScope | undefined;\n\n const getNextObject = (): BdScope => {\n if (nextObject !== undefined) {\n return nextObject;\n }\n\n nextObject = {\n ...scope\n };\n\n return nextObject;\n };\n\n for (\n const [key, enabled] of Object.entries(overlays.flags) as [\n 'computeDisabled' | 'etaDisabled',\n boolean\n ][]\n ) {\n const currentValue = scope[key];\n\n if (enabled) {\n if (currentValue !== true) {\n (getNextObject() as Record<string, unknown>)[key] = true;\n }\n\n continue;\n }\n\n if (hasOwn(scope, key)) {\n delete (getNextObject() as Record<string, unknown>)[key];\n }\n }\n\n for (\n const [key, value] of Object.entries(overlays.values) as [\n 'derived' | 'mutationBatchSchedule' | 'onScopeChange' | 'dataLayer',\n BdScopeDeriveFn |\n EtaMutationBatchSchedule |\n BdScopeChangeFn |\n BdDataLayerFn\n ][]\n ) {\n if (value === undefined) {\n continue;\n }\n\n if (Object.is(scope[key], value)) {\n continue;\n }\n\n (getNextObject() as Record<string, unknown>)[key] = value;\n }\n\n return nextObject ?? scope;\n}\n\nexport abstract class BdScopedElement extends BdNodeElement {\n private readonly scopedValueKeys = new Set<BdScopeValueKey>();\n\n @provide({\n context: bdComputeContext\n })\n @state()\n protected computeResult?: BdComputeResult;\n\n readonly compute =\n new BdComputeController(this, {\n store: () => this.currentScope?.store,\n\n enabled: () => this.isComputeEnabled,\n\n onResult: (result) => {\n const previous = this.computeResult;\n\n this.computeResult = result;\n\n if (Object.is(previous, result)) {\n return;\n }\n\n this._fireOnScopeChangeForCompute();\n }\n });\n\n /**\n * Owning boundary for the Eta template index.\n *\n * `BdScopedElement` IS the index root — every nested template\n * is owned by the closest enclosing bd-* node, and that node\n * in turn owns the controller that renders its subtree.\n */\n get root(): Element {\n return this;\n }\n\n /**\n * Eta template controller that renders the subtree against the\n * current scope and compute result.\n *\n * Args trigger a re-render when the scope, the compute result,\n * or the eta-enabled flag change. The render is gated on\n * `isEtaEnabled` so a disabled scope does not flicker\n * intermediate state.\n *\n * The base context exposes only `product`, `option`, `state`,\n * and the `ctx` alias. A user `derived` factory may deep-merge\n * additional root values. We do not spread the full scope,\n * expose the raw store, surface `$scope` / `$state` handles, or\n * leak transitions and controller flags.\n *\n * The boundary predicate uses raw tag names so nested\n * `bd-context` / `bd-product` / `bd-option` nodes\n * keep their own controllers and templates do not leak across\n * scope boundaries. Tag-name checks are stable before custom\n * element upgrade — `EtaTemplateControllerOptions.isBoundaryElement`\n * MUST work on raw DOM shape.\n */\n readonly eta =\n createEtaTemplateController<\n readonly [\n scope : BdScope | undefined,\n compute: BdComputeResult | undefined\n ],\n BdScopedTemplateContext\n >(this, {\n args: () => [\n this.currentScope,\n this.computeResult\n ] as const,\n\n enabled: () => this.isEtaEnabled,\n\n context: async ([scope, compute]) => {\n return await buildTemplateDslContext({\n product: scope?.product,\n option : scope?.option,\n state : compute,\n derived: scope?.derived\n });\n },\n\n /**\n * Mutation batch schedule is resolved from the current\n * published scope whenever the batcher schedules a future\n * flush. The value flows through `BdScope` like `derived`\n * does, so a parent that republishes a new\n * `mutationBatchSchedule` is observed by already-constructed\n * descendants on subsequent mutation bursts.\n *\n * Resolution order at scheduling time:\n *\n * 1. `this.currentScope?.mutationBatchSchedule` — this\n * element's normalized published scope, when it has one.\n * It already includes inherited values plus this node's\n * own override.\n * 2. `this.parentScope?.mutationBatchSchedule` — inherited\n * schedule for scoped consumers that do not publish a\n * scope of their own.\n * 3. The Eta controller's own default\n * (`'animation-frame'`).\n *\n * The cascade mirrors `derived`, but the Eta controller gets\n * a resolver callback rather than the current value so the\n * batcher can read the latest published scope.\n */\n mutationBatchSchedule: () =>\n this.currentScope?.mutationBatchSchedule ??\n this.parentScope?.mutationBatchSchedule,\n\n isBoundaryElement: (element) => {\n const name = element.localName;\n\n return (\n name === 'bd-context' ||\n name === 'bd-product' ||\n name === 'bd-option'\n );\n }\n });\n\n @property({\n attribute: 'eta-disabled',\n type : Boolean,\n reflect : true\n })\n etaDisabled = false;\n\n protected get isEtaEnabled(): boolean {\n return (\n this.etaDisabled !== true &&\n this.parentScope?.etaDisabled !== true\n );\n }\n\n @property({\n attribute: 'compute-disabled',\n type : Boolean,\n reflect : true\n })\n computeDisabled = false;\n\n @consume({\n context : bdScopeContext,\n subscribe: true\n })\n @property({\n attribute: false\n })\n protected parentScope?: BdScope;\n\n @provide({\n context: bdScopeContext\n })\n @state()\n protected providedScope?: BdScope;\n\n protected get currentScope(): BdScope | undefined {\n return this.providedScope;\n }\n\n protected get isComputeEnabled(): boolean {\n return (\n this.computeDisabled !== true &&\n this.parentScope?.computeDisabled !== true\n );\n }\n\n /**\n * Comma-separated list of event ids to ignore.\n *\n * Pair with `data-store-event-id` on action elements (legacy\n * `data-store-id` is also accepted): any transition whose command\n * carries a matching `eventId` is dropped before it can drive this node.\n *\n * Example:\n * ```html\n * <button data-store-action\n * data-store-event-id=\"devicesBtn\"\n * data-store-set-devices=\"25\">25</button>\n *\n * <bd-option ignore-events=\"devicesBtn, subscriptionBtn\">\n * ...\n * </bd-option>\n * ```\n */\n @property({\n attribute: 'ignore-events'\n })\n ignoreEvents?: string;\n\n /**\n * When set, this node only reacts to events originating inside its own\n * subtree. Cascading transitions from `parentScope.transition` are\n * dropped, mirroring v1's `<bd-state ignore-events-parent>` semantics.\n *\n * `<bd-context>` enables this by default (it is a hard event boundary).\n */\n @property({\n attribute: 'ignore-events-parent',\n type : Boolean,\n reflect : true\n })\n ignoreEventsParent = false;\n\n protected get ignoredEventIds(): ReadonlySet<string> {\n return parseIgnoreEvents(this.ignoreEvents);\n }\n\n /**\n * Filter callback handed to each node's `BdActionResolveController`.\n *\n * Drops transitions whose `eventId` matches an entry in `ignore-events`,\n * and — when `ignore-events-parent` is set — drops any cascade from a\n * parent scope. Concrete nodes pass `filterTransition: (c) =>\n * this._filterTransition(c)` into their controller config; the\n * controller invokes this for both local `bd-action-request` events\n * and inherited transitions.\n */\n protected _filterTransition(candidate: {\n transition: BdTransition;\n source : 'local' | 'parent';\n }): boolean {\n if (\n this.ignoreEventsParent &&\n candidate.source === 'parent'\n ) {\n return false;\n }\n\n const ignored = this.ignoredEventIds;\n\n if (ignored.size === 0) {\n return true;\n }\n\n const eventId = getTransitionEventId(candidate.transition);\n\n if (eventId === undefined) {\n return true;\n }\n\n return !ignored.has(eventId);\n }\n\n protected publishScope(nextScope: BdScope): BdScope {\n const sourceScope = Object.is(nextScope, this.providedScope)\n ? this.scopeWithoutOwnedValues(nextScope)\n : nextScope;\n const normalizedScope = this.applyScopedFlags(sourceScope);\n\n this.updateScopedValueKeys(sourceScope, normalizedScope);\n\n if (Object.is(this.providedScope, normalizedScope)) {\n return normalizedScope;\n }\n\n const previousScope = this.providedScope;\n const hadPreviousScope = previousScope !== undefined;\n const previousSnapshot = this.buildScopeChangeSnapshot(\n previousScope\n );\n const nextSnapshot = this.buildScopeChangeSnapshot(\n normalizedScope\n );\n\n this.providedScope = normalizedScope;\n\n // Fire the hook when the observable snapshot changed OR\n // when the scope transitioned in or out of existence. The\n // latter covers the \"first publish\" and \"clear\" lifecycle\n // events even when the snapshot itself is all-undefined.\n const existenceChanged = !hadPreviousScope;\n const observableChanged = !this.snapshotsEqual(\n previousSnapshot,\n nextSnapshot\n );\n\n if (existenceChanged || observableChanged) {\n const onScopeChange =\n normalizedScope.onScopeChange ??\n previousScope?.onScopeChange;\n\n onScopeChange?.(nextSnapshot);\n }\n\n return normalizedScope;\n }\n\n private buildScopeChangeSnapshot(\n scope: BdScope | undefined\n ): BdScopeChangeSnapshot {\n return {\n store : scope?.store,\n product: scope?.product,\n option : scope?.option,\n state : this.computeResult\n };\n }\n\n /**\n * Fires `onScopeChange` for compute-result-only transitions.\n *\n * `BdComputeController.onResult` writes `computeResult`, which\n * is part of the published snapshot's `state`. Without this\n * trigger, consumers watching the snapshot would miss compute\n * state changes that are not accompanied by a scope publish\n * or clear. Targeted invocation keeps the contract in one\n * place: `publishScope()` and `clearScope()` keep their own\n * paths; this helper handles the compute-only path.\n *\n * Idempotent: no-op when no scope has been published yet\n * (the hook is not on any scope) or when the snapshot's\n * `state` did not actually change (caller short-circuits via\n * `Object.is` before calling).\n */\n private _fireOnScopeChangeForCompute(): void {\n if (!this.providedScope) {\n return;\n }\n\n const snapshot = this.buildScopeChangeSnapshot(\n this.providedScope\n );\n\n const onScopeChange =\n this.providedScope.onScopeChange;\n\n onScopeChange?.(snapshot);\n }\n\n private snapshotsEqual(\n a: BdScopeChangeSnapshot,\n b: BdScopeChangeSnapshot\n ): boolean {\n return (\n a.store === b.store &&\n a.product === b.product &&\n a.option === b.option &&\n a.state === b.state\n );\n }\n\n private scopeWithoutOwnedValues(scope: BdScope): BdScope {\n let nextScope: BdScope | undefined;\n\n for (const key of this.scopedValueKeys) {\n if (!hasOwn(scope, key)) {\n continue;\n }\n\n if (nextScope === undefined) {\n nextScope = { ...scope };\n }\n\n delete (nextScope as Record<string, unknown>)[key];\n }\n\n return nextScope ?? scope;\n }\n\n private updateScopedValueKeys(\n sourceScope: BdScope,\n normalizedScope: BdScope\n ): void {\n this.updateScopedValueKey(\n 'derived',\n sourceScope,\n normalizedScope,\n this.derived\n );\n this.updateScopedValueKey(\n 'mutationBatchSchedule',\n sourceScope,\n normalizedScope,\n this.mutationBatchSchedule\n );\n this.updateScopedValueKey(\n 'onScopeChange',\n sourceScope,\n normalizedScope,\n this.onScopeChange\n );\n this.updateScopedValueKey(\n 'dataLayer',\n sourceScope,\n normalizedScope,\n this.dataLayer\n );\n }\n\n private updateScopedValueKey<T>(\n key: BdScopeValueKey,\n sourceScope: BdScope,\n normalizedScope: BdScope,\n ownValue: T | undefined\n ): void {\n if (\n ownValue !== undefined &&\n sourceScope[key] === undefined &&\n Object.is(normalizedScope[key], ownValue)\n ) {\n this.scopedValueKeys.add(key);\n return;\n }\n\n this.scopedValueKeys.delete(key);\n }\n\n protected clearScope(): void {\n if (this.store !== undefined) {\n this.publishScope(createBdStoreScope(this.store));\n\n return;\n }\n\n const previousScope = this.providedScope;\n\n if (previousScope === undefined) {\n return;\n }\n\n const onScopeChange = previousScope.onScopeChange;\n\n this.providedScope = undefined;\n\n if (onScopeChange) {\n onScopeChange({\n store : undefined,\n product: undefined,\n option : undefined,\n state : undefined\n });\n }\n }\n\n protected requireParentScope(): BdScope {\n if (!this.parentScope) {\n throw new Error(\n `${this.localName} requires a parent BdScope.`\n );\n }\n\n return this.parentScope;\n }\n\n /**\n * Single-pass overlay applied to every published scope.\n *\n * The pass runs in two phases:\n *\n * 1. **Sparse boolean flags** — `computeDisabled` and\n * `etaDisabled`. Reflected from `isComputeEnabled` /\n * `isEtaEnabled` (the inverse of \"is enabled\").\n * 2. **Value overlays** — `derived` and\n * `mutationBatchSchedule`. Caller-supplied values win; the\n * per-instance property fills in when the caller did not\n * supply one.\n *\n * Both phases share the same lazy-allocation strategy: a new\n * scope object is only created when at least one phase has a\n * write to perform. A scope with nothing to merge is returned\n * by reference, which lets `publishScope`'s `Object.is` check\n * short-circuit the downstream `@provide` notification.\n */\n private applyScopedFlags(scope: BdScope): BdScope {\n const overlay: BdScopeOverlay = {\n flags: {\n computeDisabled: !this.isComputeEnabled,\n etaDisabled : !this.isEtaEnabled\n },\n values: {}\n };\n\n if (scope.derived === undefined && this.derived !== undefined) {\n overlay.values.derived = this.derived;\n }\n\n if (\n scope.mutationBatchSchedule === undefined &&\n this.mutationBatchSchedule !== undefined\n ) {\n overlay.values.mutationBatchSchedule =\n this.mutationBatchSchedule;\n }\n\n if (\n scope.onScopeChange === undefined &&\n this.onScopeChange !== undefined\n ) {\n overlay.values.onScopeChange = this.onScopeChange;\n }\n\n if (\n scope.dataLayer === undefined &&\n this.dataLayer !== undefined\n ) {\n overlay.values.dataLayer = this.dataLayer;\n }\n\n return applyScopedFlags(scope, overlay);\n }\n\n /**\n * Per-instance derived Eta context factory.\n *\n * Users supply this on any bd-* node (via constructor,\n * attribute, or `instance.derived = fn`) to expose additional\n * template bindings at the context root. The factory receives\n * `{ product, option, state }`; its result is deep-merged with\n * the base v1 context, so a returned `foo` is read as\n * `<%= it.foo %>`.\n *\n * `publishScope` merges this property into the published scope\n * automatically when the caller did not supply an explicit\n * `derived` of its own — so a user just needs to assign\n * `node.derived = fn` (or set the `derived` attribute) and\n * the factory becomes part of the scope for free.\n *\n * Reactivity: the field is a Lit `@property`, so reassigning\n * `this.derived = newFactory` triggers `requestUpdate()` and a\n * re-publish via `willUpdate`. Method references declared\n * once per instance are stable, so the `@lit/task` args\n * comparison short-circuits re-renders for unrelated changes.\n *\n * `derived` is intended to be supplied by users at the call\n * site, not declared on concrete node subclasses. Subclasses\n * that need a static default can still assign it in their\n * constructor; the merge step lets callers override.\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.derived = ({ product, option }) => ({\n * canBuy : Boolean(product && option),\n * hasDiscount: Boolean(option?.discounted)\n * });\n * ```\n *\n * Or via plain JS (recommended — functions cannot be authored\n * as attribute strings):\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.derived = ({ product, option }) => ({\n * canBuy : Boolean(product && option),\n * hasDiscount: Boolean(option?.discounted)\n * });\n * ```\n *\n * The attribute mapping is intentionally omitted — Lit's\n * default converter cannot turn a string into a typed\n * function, so the property is JS-only. Hosts that want\n * attribute-driven configuration should expose a wrapper\n * attribute on their own subclass and assign `derived` from\n * the wrapper's converter.\n */\n @property({\n attribute: false\n })\n derived?: BdScopeDeriveFn;\n\n /**\n * Per-instance mutation batch schedule.\n *\n * Wired through the scope so descendants can read the same\n * value via `parentScope.mutationBatchSchedule`. Existing Eta\n * controllers receive a resolver callback, so flipping the\n * schedule after connection affects future mutation flushes\n * once the parent republishes its scope.\n *\n * Accepted via constructor / `instance.mutationBatchSchedule =\n * 'idle'`. The attribute mapping is intentionally omitted\n * because Lit's default converter is happy with the string\n * union, but exposing it as an attribute would invite the\n * same authoring pitfalls we deliberately avoid for\n * `derived` (callers writing `schedule=\"animation-frame\"`\n * without realising the value is scope-driven rather than an\n * imperative one-shot switch).\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.mutationBatchSchedule = 'microtask';\n * ```\n */\n @property({\n attribute: false\n })\n mutationBatchSchedule?: EtaMutationBatchSchedule;\n\n /**\n * Per-instance scope-change hook.\n *\n * Fired whenever this node's effective published scope changes\n * — both when `publishScope(...)` produces a new normalized\n * scope reference, and when `clearScope()` removes the\n * scope. The hook receives a narrow snapshot exposing only\n * `store`, `product`, `option`, and `state` — the four fields\n * that drive template rendering. It does NOT receive the\n * transition, the disabled flags, the derived factory, or the\n * mutation batch schedule.\n *\n * Wired through `BdScope.onScopeChange` like `derived` and\n * `mutationBatchSchedule`, so descendants can read the\n * inherited hook from `parentScope.onScopeChange` and a child\n * can override its ancestor's hook by assigning its own.\n *\n * Attribute mapping is intentionally omitted — functions\n * cannot be represented as attribute strings, and a stale or\n * stale-shaped `on-scope-change=\"...\"` attribute would invite\n * the same authoring pitfalls we deliberately avoid for\n * `derived`. Configure via JS instead:\n *\n * @example\n *\n * ```ts\n * const product = document.createElement('bd-product');\n * product.productId = 'acrobat-pro';\n * product.onScopeChange = ({ store, product, option, state }) => {\n * console.log('scope changed', { store, product, option, state });\n * };\n * ```\n */\n @property({\n attribute: false\n })\n onScopeChange?: BdScopeChangeFn;\n\n /**\n * Inherited callback for option-load data-layer events.\n * Configure it as a JavaScript property; functions are not\n * represented as attributes.\n */\n @property({\n attribute: false\n })\n dataLayer?: BdDataLayerFn;\n\n /**\n * Per-instance store reference.\n *\n * When set on any `BdScopedElement` subclass, the value is published\n * into the scope context via `createBdStoreScope(this.store)` so\n * descendants can consume it from `parentScope.store`. Setting it back\n * to `undefined` calls `clearScope()` to remove the published scope;\n * flipping from one store to another re-publishes the existing scope\n * with the new reference.\n *\n * The store-property publish is intentionally additive — it does not\n * replace the existing `BdActionResolveController` flow used by\n * `bd-context`, `bd-product`, and `bd-option`. Those controllers\n * capture their own transition for the action-request path; the store\n * scope is purely a value-publish surface.\n *\n * @example\n *\n * ```ts\n * const root = document.createElement('bd-context');\n * root.store = myStore;\n * // descendants under <bd-context> now see { store: myStore }\n * // via parentScope.store.\n *\n * // Clear the published scope\n * root.store = undefined;\n * ```\n *\n * The attribute mapping is intentionally omitted — Lit's default\n * converter cannot turn a string into a `Store` instance, and\n * serializing a store to an attribute would defeat the point of\n * holding it as a typed reference. Configure via JS instead, the\n * same way `derived` and `onScopeChange` are configured.\n */\n @property({\n attribute: false\n })\n store?: Store;\n\n /**\n * Publishes or clears the scope for a `store` property change.\n *\n * Single entry point for the three transitions `store` can drive:\n *\n * 1. `store` is `undefined` → `clearScope()` removes the published\n * scope and fires the `onScopeChange` hook with all-undefined\n * fields.\n * 2. `store` is defined and a scope is already published →\n * re-publishes the existing scope with the new store reference\n * (the rest of the scope — `derived`, `mutationBatchSchedule`,\n * `onScopeChange`, flags — is preserved).\n * 3. `store` is defined and no scope is published yet → publishes a\n * fresh `createBdStoreScope(this.store)` so the first descendant\n * read sees a non-undefined `parentScope.store`.\n *\n * Called from `willUpdate` whenever `changedProperties.has('store')`.\n * Idempotent: re-publishing the same scope reference is a no-op for\n * the `@provide` notification because `publishScope` already short-\n * circuits on `Object.is(this.providedScope, normalizedScope)`.\n */\n protected publishStoreScope(): void {\n if (this.store === undefined) {\n this.clearScope();\n\n return;\n }\n\n if (this.providedScope) {\n this.publishScope({\n ...this.providedScope,\n store: this.store\n });\n\n return;\n }\n\n this.publishScope(createBdStoreScope(this.store));\n }\n\n protected override willUpdate(\n changedProperties: PropertyValues<this>\n ): void {\n super.willUpdate(changedProperties);\n\n const overlayPropsChanged =\n changedProperties.has('computeDisabled') ||\n changedProperties.has('etaDisabled') ||\n changedProperties.has('derived') ||\n changedProperties.has('mutationBatchSchedule') ||\n changedProperties.has('onScopeChange') ||\n changedProperties.has('dataLayer');\n const storeChanged = changedProperties.has('store');\n\n if (!overlayPropsChanged && !storeChanged) {\n return;\n }\n\n if (storeChanged) {\n this.publishStoreScope();\n }\n\n if (overlayPropsChanged && this.providedScope) {\n this.publishScope(this.providedScope);\n }\n }\n}\n\nconst EMPTY_EVENT_IDS: ReadonlySet<string> = new Set<string>();\n\n/**\n * Parses the comma-separated `ignore-events` attribute into a set of ids.\n *\n * Empty entries are skipped so `\"foo, ,bar\"` produces `{ foo, bar }`.\n * The function is exported so test code can compare against the same\n * parsing rules the runtime uses.\n */\nexport function parseIgnoreEvents(\n value: string | undefined\n): ReadonlySet<string> {\n if (value === undefined || value === '') {\n return EMPTY_EVENT_IDS;\n }\n\n const ids = new Set<string>();\n\n for (const part of value.split(',')) {\n const trimmed = part.trim();\n\n if (trimmed !== '') {\n ids.add(trimmed);\n }\n }\n\n return ids.size === 0 ? EMPTY_EVENT_IDS : ids;\n}\n"]}
@@ -25,6 +25,12 @@ function throwIfAborted(signal) {
25
25
  new DOMException('The operation was aborted.', 'AbortError'));
26
26
  }
27
27
  }
28
+ function isPromiseLike(value) {
29
+ return (typeof value === 'object' &&
30
+ value !== null &&
31
+ 'then' in value &&
32
+ typeof value.then === 'function');
33
+ }
28
34
  /**
29
35
  * Reactive controller that owns the lifecycle of the Eta template
30
36
  * subsystem for a single scoped boundary.
@@ -536,7 +542,11 @@ export class EtaTemplateController {
536
542
  if (this.enabledFn && !this.enabledFn(args)) {
537
543
  return undefined;
538
544
  }
539
- const context = this.contextFn(args);
545
+ const contextResult = this.contextFn(args);
546
+ const context = isPromiseLike(contextResult)
547
+ ? await contextResult
548
+ : contextResult;
549
+ throwIfAborted(signal);
540
550
  if (context === undefined) {
541
551
  return undefined;
542
552
  }