@repobit/dex-store-elements 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.0.3](https://github.com/bitdefender/dex-core/compare/@repobit/dex-store-elements@2.0.2...@repobit/dex-store-elements@2.0.3) (2026-07-24)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **DEX-1000:** restore v1 derived context and event filtering semantics
11
+
12
+
13
+ ## [2.0.2](https://github.com/bitdefender/dex-core/compare/@repobit/dex-store-elements@2.0.1...@repobit/dex-store-elements@2.0.2) (2026-07-24)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **DEX-1000:** add tests for price handling and late option resolution
18
+
19
+
6
20
  ## [2.0.1](https://github.com/bitdefender/dex-core/compare/@repobit/dex-store-elements@2.0.0...@repobit/dex-store-elements@2.0.1) (2026-07-23)
7
21
 
8
22
  **Note:** Version bump only for package @repobit/dex-store-elements
package/README.md CHANGED
@@ -6,7 +6,7 @@ Reactive HTML custom elements + declarative attribute renderers for building dyn
6
6
  - Declarative renderers via `data-store-*` attributes — no framework glue code
7
7
  - Eta templates for text and attributes with a unified DSL context
8
8
  - Compute layer (min/max over the reachable option space) feeds a flat state DTO into templates and the hide DSL
9
- - Composable event topology: ignore-list filtering via `data-store-event-id` + `ignore-events`
9
+ - Composable event topology: node-local ignore-list filtering via `data-store-event-id` + `ignore-events` (`data-store-id` remains a v1 alias)
10
10
 
11
11
  ## Requirements
12
12
 
@@ -173,24 +173,25 @@ The Eta context variable is `it` (Eta default). The available shape is identical
173
173
  - `it.option.*` (only inside `<bd-option>`)
174
174
  - `it.product.*` (only inside `<bd-product>`)
175
175
  - `it.state.*` / `it.ctx.*` (everywhere — `ctx` is an alias of `state`)
176
- - `it.derived.*` (whatever your `derived` factory returned — see below)
177
- - `it.store` (the raw `@repobit/dex-store` `Store`)
176
+ - any root-level keys returned by `derived` (see below)
177
+
178
+ The raw `Store`, `BdScope`, transitions, and controller flags are not exposed.
178
179
 
179
180
  Templates can appear in text content (`<p>…{{= it.option.price.discounted }}…</p>`) or implicitly in any attribute whose value contains `{{`.
180
181
 
181
182
  ## Derived variables / functions
182
183
 
183
184
  ```ts
184
- context.derived = async ({ store, product, option }) => ({
185
+ context.derived = async ({ product, option, state }) => ({
185
186
  mails: (p) => ((option?.getDevices?.() ?? 0) / p) * 100,
186
187
  // Nested overrides are merged into their slot:
187
188
  option: { someVar: 'hello' }
188
189
  });
189
190
  ```
190
191
 
191
- Read from templates as `{{= it.derived.mails(10) }}`. Read from the hide DSL as `it.derived.mails(10)`. The factory is async, called whenever the scope inputs change.
192
+ Read from templates as `{{= it.mails(10) }}`. Read from the hide DSL as `it.mails(10)`. The factory may be async and is called whenever the scope inputs change.
192
193
 
193
- `derived` lives in the **nested** `it.derived` slot (not merged into root) this keeps store-provided fields distinct from user-provided ones. v1's root-merge behaviour is gone.
194
+ As in v1, the returned object is deep-merged into the context root. This also allows a derived object to extend or override nested `option`, `product`, `state`, or `ctx` values.
194
195
 
195
196
  ## DSL context reference
196
197
 
@@ -198,7 +199,6 @@ The shape exposed to both Eta templates and the hide DSL:
198
199
 
199
200
  ```ts
200
201
  it = {
201
- store, // raw Store | undefined
202
202
  product: { // TemplateProductContext | undefined
203
203
  id : string,
204
204
  campaign: string | undefined,
@@ -244,11 +244,11 @@ it = {
244
244
  min: string, max: string,
245
245
  monthly: { min: string, max: string }
246
246
  }
247
- },
248
- scenarios: number
247
+ }
249
248
  },
250
249
  ctx, // alias of `state`
251
- derived: Record<string, unknown> // from `bd-context.derived`
250
+
251
+ // ...root-level values returned by `derived(...)`
252
252
  }
253
253
  ```
254
254
 
@@ -256,7 +256,7 @@ Notes
256
256
  - All price / discount values in the DSL are **formatted strings** (currency-aware). Don't perform numeric comparisons on them.
257
257
  - `it.ctx` is an alias of `it.state` for convenience.
258
258
  - `it.option.links.trial('30 days')` returns the trial URL for that duration, or `undefined` if no trial is configured.
259
- - `it.derived.*` is the user-defined overlay from `bd-context.derived`.
259
+ - `derived(...)` receives only `{ option, product, state }`; its result is deep-merged at the root.
260
260
 
261
261
  ## Data layer
262
262
 
@@ -301,18 +301,19 @@ context.dataLayer = ({ option, event }) => {
301
301
  | `data-store-set-min`, `data-store-set-max` | Bounds for delta updates. |
302
302
  | `data-store-set-use-as-value` (boolean) | Treat `delta` as an absolute value rather than an increment. |
303
303
  | `data-store-event-id="…"` | Tag the source element so `ignore-events` on `bd-*` nodes can drop its events. |
304
+ | `data-store-id="…"` | Legacy v1 alias for `data-store-event-id`. The canonical attribute wins when both are present. |
304
305
 
305
306
  For `<input type="number|text">` and `<select>`, the adapter reads the current input value and dispatches a delta update. For `<input type="checkbox|radio">` it fires on click when `checked === true`.
306
307
 
307
- ### Ignoring events on a subtree
308
+ ### Ignoring events on a node
308
309
 
309
310
  ```html
310
311
  <bd-option ignore-events="devicesBtn, subscriptionBtn">
311
- <!-- ignores events from any source tagged with one of those ids -->
312
+ <!-- this option ignores matching local or inherited transitions -->
312
313
  </bd-option>
313
314
  ```
314
315
 
315
- `ignore-events` cascades descendants inherit it. `data-store-event-id` is the matching tag on the source element. (`data-store-id` from v1 is gone.)
316
+ `ignore-events` is node-local, as in v1; it is not copied into `BdScope`. Each node independently decides whether to consume a local or inherited transition. If a parent drops a local event, that event is naturally not forwarded to its descendants. `data-store-event-id` is canonical; legacy `data-store-id` is accepted as an alias.
316
317
 
317
318
  ## Registration and initialization
318
319
 
@@ -1,13 +1,14 @@
1
1
  import type { BdActionCommand } from '../core/actions.js';
2
2
  export declare const DATA_ACTION_NODE: "[data-store-action]";
3
3
  export declare const DATA_EVENT_ID_ATTR = "data-store-event-id";
4
+ export declare const LEGACY_DATA_STORE_ID_ATTR = "data-store-id";
4
5
  /**
5
- * Reads the optional `data-store-event-id` attribute from an element.
6
+ * Reads the optional event-source id from an action element.
6
7
  *
7
- * The value pairs with the `ignore-events` attribute on `bd-*` provider
8
- * elements, so a node can opt out of reacting to events whose source
9
- * element carries this id. Returns `undefined` when the attribute is
10
- * absent.
8
+ * `data-store-event-id` is the canonical v2 spelling because it pairs
9
+ * directly with `ignore-events`. Legacy `data-store-id` is accepted as
10
+ * a fallback so v1 markup keeps working. When both are present, the
11
+ * canonical attribute wins.
11
12
  */
12
13
  export declare function readEventId(element: HTMLElement): string | undefined;
13
14
  export declare function isActionElement(element: HTMLElement): boolean;
@@ -1,15 +1,17 @@
1
1
  export const DATA_ACTION_NODE = '[data-store-action]';
2
2
  export const DATA_EVENT_ID_ATTR = 'data-store-event-id';
3
+ export const LEGACY_DATA_STORE_ID_ATTR = 'data-store-id';
3
4
  /**
4
- * Reads the optional `data-store-event-id` attribute from an element.
5
+ * Reads the optional event-source id from an action element.
5
6
  *
6
- * The value pairs with the `ignore-events` attribute on `bd-*` provider
7
- * elements, so a node can opt out of reacting to events whose source
8
- * element carries this id. Returns `undefined` when the attribute is
9
- * absent.
7
+ * `data-store-event-id` is the canonical v2 spelling because it pairs
8
+ * directly with `ignore-events`. Legacy `data-store-id` is accepted as
9
+ * a fallback so v1 markup keeps working. When both are present, the
10
+ * canonical attribute wins.
10
11
  */
11
12
  export function readEventId(element) {
12
- const value = element.dataset.storeEventId;
13
+ const value = element.dataset.storeEventId ||
14
+ element.dataset.storeId;
13
15
  if (value === undefined || value === '') {
14
16
  return undefined;
15
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"action.attributes.js","sourceRoot":"","sources":["../../../src/actions/action.attributes.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Action,\n BdActionCommand,\n UpdateByDelta\n} from '@/core/actions.js';\n\nexport const DATA_ACTION_NODE = '[data-store-action]' as const;\nexport const DATA_EVENT_ID_ATTR = 'data-store-event-id';\n\n/**\n * Reads the optional `data-store-event-id` attribute from an element.\n *\n * The value pairs with the `ignore-events` attribute on `bd-*` provider\n * elements, so a node can opt out of reacting to events whose source\n * element carries this id. Returns `undefined` when the attribute is\n * absent.\n */\nexport function readEventId(\n element: HTMLElement\n): string | undefined {\n const value = element.dataset.storeEventId;\n\n if (value === undefined || value === '') {\n return undefined;\n }\n\n return value;\n}\n\nexport function isActionElement(\n element: HTMLElement\n): boolean {\n return element.matches(DATA_ACTION_NODE);\n}\n\nexport function readBdActionCommand(\n element: HTMLElement\n): BdActionCommand | undefined {\n const dataset = element.dataset;\n\n if ('storeSetType' in dataset) {\n const update = readUpdateByDelta(element);\n\n if (!update) {\n return undefined;\n }\n\n return {\n kind: 'update-by-delta',\n update\n };\n }\n\n if (\n !isActionElement(element) &&\n !hasSetActionData(dataset)\n ) {\n return undefined;\n }\n\n return {\n kind : 'set-action',\n action: readSetAction(element)\n };\n}\n\nexport function readInputValueCommand(\n input: HTMLInputElement\n): BdActionCommand | undefined {\n const command = readBdActionCommand(input);\n\n if (command?.kind !== 'update-by-delta') {\n return command;\n }\n\n const delta = toOptionalNumber(input.value);\n\n if (delta === undefined) {\n return undefined;\n }\n\n return {\n kind : 'update-by-delta',\n update: {\n ...command.update,\n delta,\n min : toOptionalNumber(input.min) ?? command.update.min,\n max : toOptionalNumber(input.max) ?? command.update.max,\n useAsValue: true\n }\n };\n}\n\nfunction readUpdateByDelta(\n element: HTMLElement\n): UpdateByDelta | undefined {\n const dataset = element.dataset;\n const type = dataset.storeSetType;\n\n if (\n type !== 'devices' &&\n type !== 'subscription'\n ) {\n return undefined;\n }\n\n const delta = readDelta(dataset.storeSetDelta);\n\n if (delta === undefined) {\n return undefined;\n }\n\n return {\n type,\n delta,\n min : toOptionalNumber(dataset.storeSetMin),\n max : toOptionalNumber(dataset.storeSetMax),\n useAsValue: readBooleanAttribute(\n dataset,\n 'storeSetUseAsValue'\n ),\n eventId: readEventId(element)\n };\n}\n\nfunction readSetAction(\n element: HTMLElement\n): Action {\n const dataset = element.dataset;\n\n return {\n id : dataset.storeSetId,\n campaign: dataset.storeSetCampaign,\n bundle : readBooleanAttribute(\n dataset,\n 'storeSetBundle'\n ),\n devices : toOptionDimension(dataset.storeSetDevices),\n subscription: toOptionDimension(dataset.storeSetSubscription),\n eventId : readEventId(element)\n };\n}\n\nfunction hasSetActionData(\n dataset: DOMStringMap\n): boolean {\n return (\n 'storeSetId' in dataset ||\n 'storeSetCampaign' in dataset ||\n 'storeSetBundle' in dataset ||\n 'storeSetDevices' in dataset ||\n 'storeSetSubscription' in dataset\n );\n}\n\nfunction readDelta(\n value: string | undefined\n): UpdateByDelta['delta'] | undefined {\n if (\n value === 'next' ||\n value === 'prev'\n ) {\n return value;\n }\n\n return toOptionalNumber(value);\n}\n\nfunction readBooleanAttribute(\n dataset: DOMStringMap,\n key: string\n): boolean {\n if (!(key in dataset)) {\n return false;\n }\n\n const value = dataset[key];\n\n return value === '' || value === 'true';\n}\n\nfunction toOptionalNumber(\n value: string | undefined\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n const trimmed = value.trim();\n\n if (!trimmed) {\n return undefined;\n }\n\n const parsed = Number(trimmed);\n\n return Number.isNaN(parsed)\n ? undefined\n : parsed;\n}\n\nfunction toOptionDimension(\n value: string | undefined\n): number | undefined {\n const parsed = toOptionalNumber(value);\n\n if (\n parsed === undefined ||\n !Number.isFinite(parsed) ||\n parsed < 0 ||\n !Number.isInteger(parsed)\n ) {\n return undefined;\n }\n\n return parsed;\n}\n"]}
1
+ {"version":3,"file":"action.attributes.js","sourceRoot":"","sources":["../../../src/actions/action.attributes.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Action,\n BdActionCommand,\n UpdateByDelta\n} from '@/core/actions.js';\n\nexport const DATA_ACTION_NODE = '[data-store-action]' as const;\nexport const DATA_EVENT_ID_ATTR = 'data-store-event-id';\nexport const LEGACY_DATA_STORE_ID_ATTR = 'data-store-id';\n\n/**\n * Reads the optional event-source id from an action element.\n *\n * `data-store-event-id` is the canonical v2 spelling because it pairs\n * directly with `ignore-events`. Legacy `data-store-id` is accepted as\n * a fallback so v1 markup keeps working. When both are present, the\n * canonical attribute wins.\n */\nexport function readEventId(\n element: HTMLElement\n): string | undefined {\n const value =\n element.dataset.storeEventId ||\n element.dataset.storeId;\n\n if (value === undefined || value === '') {\n return undefined;\n }\n\n return value;\n}\n\nexport function isActionElement(\n element: HTMLElement\n): boolean {\n return element.matches(DATA_ACTION_NODE);\n}\n\nexport function readBdActionCommand(\n element: HTMLElement\n): BdActionCommand | undefined {\n const dataset = element.dataset;\n\n if ('storeSetType' in dataset) {\n const update = readUpdateByDelta(element);\n\n if (!update) {\n return undefined;\n }\n\n return {\n kind: 'update-by-delta',\n update\n };\n }\n\n if (\n !isActionElement(element) &&\n !hasSetActionData(dataset)\n ) {\n return undefined;\n }\n\n return {\n kind : 'set-action',\n action: readSetAction(element)\n };\n}\n\nexport function readInputValueCommand(\n input: HTMLInputElement\n): BdActionCommand | undefined {\n const command = readBdActionCommand(input);\n\n if (command?.kind !== 'update-by-delta') {\n return command;\n }\n\n const delta = toOptionalNumber(input.value);\n\n if (delta === undefined) {\n return undefined;\n }\n\n return {\n kind : 'update-by-delta',\n update: {\n ...command.update,\n delta,\n min : toOptionalNumber(input.min) ?? command.update.min,\n max : toOptionalNumber(input.max) ?? command.update.max,\n useAsValue: true\n }\n };\n}\n\nfunction readUpdateByDelta(\n element: HTMLElement\n): UpdateByDelta | undefined {\n const dataset = element.dataset;\n const type = dataset.storeSetType;\n\n if (\n type !== 'devices' &&\n type !== 'subscription'\n ) {\n return undefined;\n }\n\n const delta = readDelta(dataset.storeSetDelta);\n\n if (delta === undefined) {\n return undefined;\n }\n\n return {\n type,\n delta,\n min : toOptionalNumber(dataset.storeSetMin),\n max : toOptionalNumber(dataset.storeSetMax),\n useAsValue: readBooleanAttribute(\n dataset,\n 'storeSetUseAsValue'\n ),\n eventId: readEventId(element)\n };\n}\n\nfunction readSetAction(\n element: HTMLElement\n): Action {\n const dataset = element.dataset;\n\n return {\n id : dataset.storeSetId,\n campaign: dataset.storeSetCampaign,\n bundle : readBooleanAttribute(\n dataset,\n 'storeSetBundle'\n ),\n devices : toOptionDimension(dataset.storeSetDevices),\n subscription: toOptionDimension(dataset.storeSetSubscription),\n eventId : readEventId(element)\n };\n}\n\nfunction hasSetActionData(\n dataset: DOMStringMap\n): boolean {\n return (\n 'storeSetId' in dataset ||\n 'storeSetCampaign' in dataset ||\n 'storeSetBundle' in dataset ||\n 'storeSetDevices' in dataset ||\n 'storeSetSubscription' in dataset\n );\n}\n\nfunction readDelta(\n value: string | undefined\n): UpdateByDelta['delta'] | undefined {\n if (\n value === 'next' ||\n value === 'prev'\n ) {\n return value;\n }\n\n return toOptionalNumber(value);\n}\n\nfunction readBooleanAttribute(\n dataset: DOMStringMap,\n key: string\n): boolean {\n if (!(key in dataset)) {\n return false;\n }\n\n const value = dataset[key];\n\n return value === '' || value === 'true';\n}\n\nfunction toOptionalNumber(\n value: string | undefined\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n const trimmed = value.trim();\n\n if (!trimmed) {\n return undefined;\n }\n\n const parsed = Number(trimmed);\n\n return Number.isNaN(parsed)\n ? undefined\n : parsed;\n}\n\nfunction toOptionDimension(\n value: string | undefined\n): number | undefined {\n const parsed = toOptionalNumber(value);\n\n if (\n parsed === undefined ||\n !Number.isFinite(parsed) ||\n parsed < 0 ||\n !Number.isInteger(parsed)\n ) {\n return undefined;\n }\n\n return parsed;\n}\n"]}
@@ -3,51 +3,47 @@ import type { BdTransition } from '../core/actions.js';
3
3
  import type { EtaMutationBatchSchedule } from '../eta/types.js';
4
4
  import type { Product, ProductOption, Store } from '@repobit/dex-store';
5
5
  /**
6
- * Shape that `derived(scope, compute)` callbacks receive.
6
+ * Shape that v1-compatible `derived(...)` callbacks receive.
7
7
  *
8
- * The four fields mirror `BdScopedTemplateContext`. A `derived`
9
- * factory reads from this input and returns a new object whose
10
- * properties are exposed to templates under the `derived` key.
8
+ * This is deliberately narrower than `BdScope`: transition metadata,
9
+ * the raw store, controller flags, and callbacks are not template data.
10
+ * The returned object is merged into the root Eta/DSL context.
11
11
  */
12
12
  export type BdScopeDerivationInput = Readonly<{
13
- store: Store | undefined;
14
13
  product: Product | undefined;
15
14
  option: ProductOption | undefined;
15
+ state: BdComputeResult | undefined;
16
16
  }>;
17
17
  /**
18
18
  * Result shape of a `derived` factory.
19
19
  *
20
- * Returned object is shallow-merged into the Eta context under
21
- * the `derived` key, so templates can write `<%= it.derived.foo %>`
22
- * instead of pulling each derived value into the top-level scope.
20
+ * Returned values are deep-merged into the root Eta/DSL context,
21
+ * matching v1. A derived `{ canBuy: true }` is therefore read as
22
+ * `it.canBuy`, while nested values can extend or override the base
23
+ * `option`, `product`, `state`, and `ctx` objects.
23
24
  *
24
25
  * The default is `unknown` to allow flexible per-scope shapes; a
25
26
  * concrete node can narrow it locally via a typed wrapper.
26
27
  */
27
28
  export type BdScopeDerived = Readonly<Record<string, unknown>>;
28
29
  /**
29
- * Pure function that derives additional Eta template context
30
- * from the current scope and compute result.
31
- *
32
- * The function MUST be pure — it is invoked synchronously inside
33
- * `EtaTemplateController`'s `context` callback on every render.
34
- * The same `store` / `product` / `option` identity arguments
35
- * produce the same output (by reference) so the `@lit/task` args
36
- * tuple can short-circuit re-renders.
30
+ * Function that derives additional Eta/DSL template context.
31
+ * Async factories are supported, as they were in v1.
37
32
  *
38
33
  * @example
39
34
  *
40
35
  * ```ts
41
36
  * const scope: BdScope = {
42
37
  * ...createBdStoreScope(store),
43
- * derived: ({ store, product, option }) => ({
38
+ * derived: async ({ product, option, state }) => ({
44
39
  * hasProduct: Boolean(product),
45
- * canBuy : Boolean(store && product && option)
40
+ * canBuy : Boolean(product && option),
41
+ * hasState : Boolean(state)
46
42
  * })
47
43
  * };
48
44
  * ```
49
45
  */
50
- export type BdScopeDeriveFn = (scope: BdScopeDerivationInput) => BdScopeDerived;
46
+ export type BdScopeDeriveFn = (scope: BdScopeDerivationInput) => BdScopeDerived | null | undefined | PromiseLike<BdScopeDerived | null | undefined>;
51
47
  /**
52
48
  * Snapshot delivered to `BdScope.onScopeChange` callbacks.
53
49
  *
@@ -102,7 +98,7 @@ export type BdScope = Readonly<Partial<{
102
98
  dataLayer: BdDataLayerFn;
103
99
  }>>;
104
100
  /**
105
- * Result of running `scope.derived(scope)` — exposed by helpers
101
+ * Result of running `scope.derived(...)` — exposed by helpers
106
102
  * that want to apply the derivation outside the Eta context
107
103
  * builder (e.g. tests, diagnostics).
108
104
  */
@@ -117,12 +113,10 @@ export declare function extendBdOptionScope(parentScope: BdScope, option: Produc
117
113
  *
118
114
  * Exposed so non-Eta consumers (tests, diagnostics, future
119
115
  * controllers) can reuse the same derivation rule without going
120
- * through `EtaTemplateController`. Returning `undefined` when no
121
- * `derived` is configured keeps the caller in charge of deciding
122
- * what to do with the absence (omit the `derived` key vs.
123
- * publish an empty object).
116
+ * through `EtaTemplateController`. Returning `undefined` means
117
+ * there is no root overlay to merge into the base context.
124
118
  */
125
- export declare function runBdScopeDerived(scope: BdScope | undefined): BdDerivedResult | undefined;
119
+ export declare function runBdScopeDerived(scope: BdScope | undefined, state?: BdComputeResult): Promise<BdDerivedResult | undefined>;
126
120
  export declare const bdScopeContext: {
127
121
  __context__: Readonly<Partial<{
128
122
  store: Store;
@@ -32,17 +32,16 @@ export function extendBdOptionScope(parentScope, option, transition) {
32
32
  *
33
33
  * Exposed so non-Eta consumers (tests, diagnostics, future
34
34
  * controllers) can reuse the same derivation rule without going
35
- * through `EtaTemplateController`. Returning `undefined` when no
36
- * `derived` is configured keeps the caller in charge of deciding
37
- * what to do with the absence (omit the `derived` key vs.
38
- * publish an empty object).
35
+ * through `EtaTemplateController`. Returning `undefined` means
36
+ * there is no root overlay to merge into the base context.
39
37
  */
40
- export function runBdScopeDerived(scope) {
41
- return scope?.derived?.({
42
- store: scope.store,
38
+ export async function runBdScopeDerived(scope, state) {
39
+ const result = await scope?.derived?.({
43
40
  product: scope.product,
44
- option: scope.option
41
+ option: scope.option,
42
+ state
45
43
  });
44
+ return result ?? undefined;
46
45
  }
47
46
  export const bdScopeContext = createContext(Symbol.for('bd:scope'));
48
47
  //# sourceMappingURL=context.scope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.scope.js","sourceRoot":"","sources":["../../../src/contexts/context.scope.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgI7C,MAAM,UAAU,kBAAkB,CAChC,KAAY,EACZ,UAAyB;IAEzB,OAAO;QACL,KAAK;QACL,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,WAAoB,EACpB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,WAAoB,EACpB,OAAgB,EAChB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,OAAO;QACP,MAAM,EAAE,SAAS;QACjB,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,WAAoB,EACpB,MAAqB,EACrB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,MAAM;QACN,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAA0B;IAE1B,OAAO,KAAK,EAAE,OAAO,EAAE,CAAC;QACtB,KAAK,EAAI,KAAK,CAAC,KAAK;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAG,KAAK,CAAC,MAAM;KACtB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GACzB,aAAa,CACX,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CACvB,CAAC","sourcesContent":["import type { BdComputeResult } from '@/compute/compute.expand.js';\nimport type { BdTransition } from '@/core/actions.js';\nimport type { EtaMutationBatchSchedule } from '@/eta/types.js';\nimport { createContext } from '@lit/context';\nimport type { Product, ProductOption, Store } from '@repobit/dex-store';\n\n/**\n * Shape that `derived(scope, compute)` callbacks receive.\n *\n * The four fields mirror `BdScopedTemplateContext`. A `derived`\n * factory reads from this input and returns a new object whose\n * properties are exposed to templates under the `derived` key.\n */\nexport type BdScopeDerivationInput = Readonly<{\n store : Store | undefined;\n product: Product | undefined;\n option : ProductOption | undefined;\n}>;\n\n/**\n * Result shape of a `derived` factory.\n *\n * Returned object is shallow-merged into the Eta context under\n * the `derived` key, so templates can write `<%= it.derived.foo %>`\n * instead of pulling each derived value into the top-level scope.\n *\n * The default is `unknown` to allow flexible per-scope shapes; a\n * concrete node can narrow it locally via a typed wrapper.\n */\nexport type BdScopeDerived = Readonly<Record<string, unknown>>;\n\n/**\n * Pure function that derives additional Eta template context\n * from the current scope and compute result.\n *\n * The function MUST be pure it is invoked synchronously inside\n * `EtaTemplateController`'s `context` callback on every render.\n * The same `store` / `product` / `option` identity arguments\n * produce the same output (by reference) so the `@lit/task` args\n * tuple can short-circuit re-renders.\n *\n * @example\n *\n * ```ts\n * const scope: BdScope = {\n * ...createBdStoreScope(store),\n * derived: ({ store, product, option }) => ({\n * hasProduct: Boolean(product),\n * canBuy : Boolean(store && product && option)\n * })\n * };\n * ```\n */\nexport type BdScopeDeriveFn = (\n scope : BdScopeDerivationInput\n) => BdScopeDerived;\n\n/**\n * Snapshot delivered to `BdScope.onScopeChange` callbacks.\n *\n * Intentionally narrow — the hook fires when the published\n * scope of a `BdScopedElement` changes, but consumers should\n * only need the fields that drive template rendering. Adding a\n * new field here is a public surface change (every consumer can\n * read it), so prefer extending `BdScope` and rebinding this\n * snapshot rather than spreading the full scope.\n *\n * `state` carries the current compute result for the scope;\n * `undefined` when compute is disabled or has not run yet.\n */\nexport type BdScopeChangeSnapshot = Readonly<{\n store : Store | undefined;\n product: Product | undefined;\n option : ProductOption | undefined;\n state : BdComputeResult | undefined;\n}>;\n\n/**\n * Callback fired when the published scope of a\n * `BdScopedElement` changes.\n *\n * Invoked from `BdScopedElement.publishScope(...)` when the\n * normalized published scope reference actually changes, and\n * from `BdScopedElement.clearScope()` with all-undefined fields\n * when the scope is cleared. The hook receives the new\n * snapshot — it does NOT need to read the scope itself.\n *\n * The hook is stored on the published scope (`BdScope.onScopeChange`)\n * so it cascades to descendants via `parentScope.onScopeChange`.\n * Descendants may override the inherited hook by supplying\n * their own `onScopeChange` property.\n */\nexport type BdScopeChangeFn = (\n snapshot: BdScopeChangeSnapshot\n) => void;\n\nexport type BdDataLayerEvent =\n | 'all'\n | 'info'\n | 'comparison'\n | (string & {});\n\nexport type BdDataLayerPayload = Readonly<{\n option: ProductOption;\n event : BdDataLayerEvent;\n}>;\n\nexport type BdDataLayerFn = (\n payload: BdDataLayerPayload\n) => void;\n\nexport type BdScope = Readonly<Partial<{\n store : Store;\n product : Product;\n option : ProductOption;\n transition : BdTransition;\n computeDisabled : boolean;\n etaDisabled : boolean;\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n}>>\n\n/**\n * Result of running `scope.derived(scope)` — exposed by helpers\n * that want to apply the derivation outside the Eta context\n * builder (e.g. tests, diagnostics).\n */\nexport type BdDerivedResult = BdScopeDerived;\n\nexport function createBdStoreScope(\n store: Store,\n transition?: BdTransition\n): BdScope {\n return {\n store,\n transition\n };\n}\n\nexport function extendBdContextScope(\n parentScope: BdScope,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n transition\n };\n}\n\nexport function extendBdProductScope(\n parentScope: BdScope,\n product: Product,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n product,\n option: undefined,\n transition\n };\n}\n\nexport function extendBdOptionScope(\n parentScope: BdScope,\n option: ProductOption,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n option,\n transition\n };\n}\n\n/**\n * Invokes `scope.derived` when present, otherwise returns\n * `undefined`.\n *\n * Exposed so non-Eta consumers (tests, diagnostics, future\n * controllers) can reuse the same derivation rule without going\n * through `EtaTemplateController`. Returning `undefined` when no\n * `derived` is configured keeps the caller in charge of deciding\n * what to do with the absence (omit the `derived` key vs.\n * publish an empty object).\n */\nexport function runBdScopeDerived(\n scope: BdScope | undefined\n): BdDerivedResult | undefined {\n return scope?.derived?.({\n store : scope.store,\n product: scope.product,\n option : scope.option\n });\n}\n\nexport const bdScopeContext =\n createContext<BdScope | undefined>(\n Symbol.for('bd:scope')\n );\n"]}
1
+ {"version":3,"file":"context.scope.js","sourceRoot":"","sources":["../../../src/contexts/context.scope.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgI7C,MAAM,UAAU,kBAAkB,CAChC,KAAY,EACZ,UAAyB;IAEzB,OAAO;QACL,KAAK;QACL,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,WAAoB,EACpB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,WAAoB,EACpB,OAAgB,EAChB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,OAAO;QACP,MAAM,EAAE,SAAS;QACjB,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,WAAoB,EACpB,MAAqB,EACrB,UAAyB;IAEzB,OAAO;QACL,GAAG,WAAW;QACd,MAAM;QACN,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAA0B,EAC1B,KAAuB;IAEvB,MAAM,MAAM,GAAG,MAAM,KAAK,EAAE,OAAO,EAAE,CAAC;QACpC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAG,KAAK,CAAC,MAAM;QACrB,KAAK;KACN,CAAC,CAAC;IAEH,OAAO,MAAM,IAAI,SAAS,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GACzB,aAAa,CACX,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CACvB,CAAC","sourcesContent":["import type { BdComputeResult } from '@/compute/compute.expand.js';\nimport type { BdTransition } from '@/core/actions.js';\nimport type { EtaMutationBatchSchedule } from '@/eta/types.js';\nimport { createContext } from '@lit/context';\nimport type { Product, ProductOption, Store } from '@repobit/dex-store';\n\n/**\n * Shape that v1-compatible `derived(...)` callbacks receive.\n *\n * This is deliberately narrower than `BdScope`: transition metadata,\n * the raw store, controller flags, and callbacks are not template data.\n * The returned object is merged into the root Eta/DSL context.\n */\nexport type BdScopeDerivationInput = Readonly<{\n product: Product | undefined;\n option : ProductOption | undefined;\n state : BdComputeResult | undefined;\n}>;\n\n/**\n * Result shape of a `derived` factory.\n *\n * Returned values are deep-merged into the root Eta/DSL context,\n * matching v1. A derived `{ canBuy: true }` is therefore read as\n * `it.canBuy`, while nested values can extend or override the base\n * `option`, `product`, `state`, and `ctx` objects.\n *\n * The default is `unknown` to allow flexible per-scope shapes; a\n * concrete node can narrow it locally via a typed wrapper.\n */\nexport type BdScopeDerived = Readonly<Record<string, unknown>>;\n\n/**\n * Function that derives additional Eta/DSL template context.\n * Async factories are supported, as they were in v1.\n *\n * @example\n *\n * ```ts\n * const scope: BdScope = {\n * ...createBdStoreScope(store),\n * derived: async ({ product, option, state }) => ({\n * hasProduct: Boolean(product),\n * canBuy : Boolean(product && option),\n * hasState : Boolean(state)\n * })\n * };\n * ```\n */\nexport type BdScopeDeriveFn = (\n scope : BdScopeDerivationInput\n) =>\n | BdScopeDerived\n | null\n | undefined\n | PromiseLike<BdScopeDerived | null | undefined>;\n\n/**\n * Snapshot delivered to `BdScope.onScopeChange` callbacks.\n *\n * Intentionally narrow — the hook fires when the published\n * scope of a `BdScopedElement` changes, but consumers should\n * only need the fields that drive template rendering. Adding a\n * new field here is a public surface change (every consumer can\n * read it), so prefer extending `BdScope` and rebinding this\n * snapshot rather than spreading the full scope.\n *\n * `state` carries the current compute result for the scope;\n * `undefined` when compute is disabled or has not run yet.\n */\nexport type BdScopeChangeSnapshot = Readonly<{\n store : Store | undefined;\n product: Product | undefined;\n option : ProductOption | undefined;\n state : BdComputeResult | undefined;\n}>;\n\n/**\n * Callback fired when the published scope of a\n * `BdScopedElement` changes.\n *\n * Invoked from `BdScopedElement.publishScope(...)` when the\n * normalized published scope reference actually changes, and\n * from `BdScopedElement.clearScope()` with all-undefined fields\n * when the scope is cleared. The hook receives the new\n * snapshot — it does NOT need to read the scope itself.\n *\n * The hook is stored on the published scope (`BdScope.onScopeChange`)\n * so it cascades to descendants via `parentScope.onScopeChange`.\n * Descendants may override the inherited hook by supplying\n * their own `onScopeChange` property.\n */\nexport type BdScopeChangeFn = (\n snapshot: BdScopeChangeSnapshot\n) => void;\n\nexport type BdDataLayerEvent =\n | 'all'\n | 'info'\n | 'comparison'\n | (string & {});\n\nexport type BdDataLayerPayload = Readonly<{\n option: ProductOption;\n event : BdDataLayerEvent;\n}>;\n\nexport type BdDataLayerFn = (\n payload: BdDataLayerPayload\n) => void;\n\nexport type BdScope = Readonly<Partial<{\n store : Store;\n product : Product;\n option : ProductOption;\n transition : BdTransition;\n computeDisabled : boolean;\n etaDisabled : boolean;\n derived : BdScopeDeriveFn;\n mutationBatchSchedule: EtaMutationBatchSchedule;\n onScopeChange : BdScopeChangeFn;\n dataLayer : BdDataLayerFn;\n}>>\n\n/**\n * Result of running `scope.derived(...)` — exposed by helpers\n * that want to apply the derivation outside the Eta context\n * builder (e.g. tests, diagnostics).\n */\nexport type BdDerivedResult = BdScopeDerived;\n\nexport function createBdStoreScope(\n store: Store,\n transition?: BdTransition\n): BdScope {\n return {\n store,\n transition\n };\n}\n\nexport function extendBdContextScope(\n parentScope: BdScope,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n transition\n };\n}\n\nexport function extendBdProductScope(\n parentScope: BdScope,\n product: Product,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n product,\n option: undefined,\n transition\n };\n}\n\nexport function extendBdOptionScope(\n parentScope: BdScope,\n option: ProductOption,\n transition?: BdTransition\n): BdScope {\n return {\n ...parentScope,\n option,\n transition\n };\n}\n\n/**\n * Invokes `scope.derived` when present, otherwise returns\n * `undefined`.\n *\n * Exposed so non-Eta consumers (tests, diagnostics, future\n * controllers) can reuse the same derivation rule without going\n * through `EtaTemplateController`. Returning `undefined` means\n * there is no root overlay to merge into the base context.\n */\nexport async function runBdScopeDerived(\n scope: BdScope | undefined,\n state?: BdComputeResult\n): Promise<BdDerivedResult | undefined> {\n const result = await scope?.derived?.({\n product: scope.product,\n option : scope.option,\n state\n });\n\n return result ?? undefined;\n}\n\nexport const bdScopeContext =\n createContext<BdScope | undefined>(\n Symbol.for('bd:scope')\n );\n"]}
@@ -86,6 +86,7 @@ export declare class BdActionResolveController<TArgs extends readonly unknown[],
86
86
  private readonly _resolver;
87
87
  private _localTransition?;
88
88
  private _lastCommittedTransitionSeq;
89
+ private readonly _rejectedTransitionSeqs;
89
90
  private _listening;
90
91
  constructor(host: BdActionResolveHost, options: BdActionResolveOptions<TArgs, TDerivedArgs, TResult>);
91
92
  hostConnected(): void;
@@ -15,6 +15,7 @@ export class BdActionResolveController {
15
15
  this.host = host;
16
16
  this.options = options;
17
17
  this._lastCommittedTransitionSeq = 0;
18
+ this._rejectedTransitionSeqs = new Set();
18
19
  this._listening = false;
19
20
  this._onActionRequest = (event) => {
20
21
  if (!isBdActionRequestEvent(event)) {
@@ -105,6 +106,7 @@ export class BdActionResolveController {
105
106
  }
106
107
  abort(reason) {
107
108
  this._localTransition = undefined;
109
+ this._rejectedTransitionSeqs.clear();
108
110
  this._resolver.abort(reason);
109
111
  }
110
112
  _args() {
@@ -119,6 +121,7 @@ export class BdActionResolveController {
119
121
  _createActionContext(args) {
120
122
  const candidate = this._selectTransitionCandidate(args);
121
123
  if (!candidate ||
124
+ this._rejectedTransitionSeqs.has(candidate.transition.seq) ||
122
125
  candidate.transition.seq <= this._lastCommittedTransitionSeq) {
123
126
  return {
124
127
  freshTransition: false
@@ -150,9 +153,15 @@ export class BdActionResolveController {
150
153
  if (localCandidate && (!filter || filter(localCandidate))) {
151
154
  candidates.push(localCandidate);
152
155
  }
156
+ else if (localCandidate) {
157
+ this._rejectedTransitionSeqs.add(localCandidate.transition.seq);
158
+ }
153
159
  if (parentCandidate && (!filter || filter(parentCandidate))) {
154
160
  candidates.push(parentCandidate);
155
161
  }
162
+ else if (parentCandidate) {
163
+ this._rejectedTransitionSeqs.add(parentCandidate.transition.seq);
164
+ }
156
165
  if (candidates.length === 0) {
157
166
  return undefined;
158
167
  }
@@ -174,6 +183,11 @@ export class BdActionResolveController {
174
183
  return;
175
184
  }
176
185
  this._lastCommittedTransitionSeq = Math.max(this._lastCommittedTransitionSeq, transition.seq);
186
+ for (const seq of this._rejectedTransitionSeqs) {
187
+ if (seq <= this._lastCommittedTransitionSeq) {
188
+ this._rejectedTransitionSeqs.delete(seq);
189
+ }
190
+ }
177
191
  if (this._localTransition === transition) {
178
192
  this._localTransition = undefined;
179
193
  }
@@ -1 +1 @@
1
- {"version":3,"file":"controller.action-resolve.js","sourceRoot":"","sources":["../../../src/controllers/controller.action-resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\n\nimport {\n BD_ACTION_REQUEST_EVENT,\n createBdTransition,\n isBdActionRequestEvent,\n type BdActionRequestDetail,\n type BdTransition\n} from '@/core/actions.js';\nimport {\n BdResolveController,\n type BdResolveContext,\n type BdResolveHookPhase,\n type BdResolveHost,\n type BdResolveStatus,\n type BdWorkKey,\n type MaybePromise\n} from './controller.resolve.js';\n\nexport type BdActionResolveTransitionSource =\n | 'local'\n | 'parent';\n\nexport type BdActionResolveContext = Readonly<{\n /**\n * The transition passed to derive/resolve/commit.\n *\n * This is only set for a fresh transition. Once the controller marks a\n * transition committed, the same transition is suppressed from future args.\n */\n transition?: BdTransition;\n\n transitionSeq? : number;\n transitionSource?: BdActionResolveTransitionSource;\n freshTransition : boolean;\n}>;\n\nexport interface BdActionResolveHost extends BdResolveHost, EventTarget {}\n\nexport interface BdActionResolveOptions<\n TArgs extends readonly unknown[],\n TDerivedArgs extends readonly unknown[],\n TResult\n> {\n name? : string;\n workKey?: BdWorkKey;\n\n /**\n * Base node inputs. Keep this close to @lit/task naming.\n *\n * Examples:\n * () => [this.parentScope, this.devices, this.subscription] as const\n */\n args: () => TArgs;\n\n /**\n * Optional downward transition source, usually parentScope.transition.\n *\n * Local bd-action-request events captured by this controller are considered\n * alongside this transition, with the latest seq winning.\n */\n transition?: (args: TArgs) => BdTransition | undefined;\n\n /**\n * Converts base args + a fresh transition into the actual resolver args.\n *\n * This should be pure. It must not publish scope or mutate host props.\n */\n derive: (\n args: TArgs,\n context: BdActionResolveContext\n ) => TDerivedArgs;\n\n enabled?: (\n args: TDerivedArgs,\n context: BdActionResolveContext\n ) => boolean;\n\n argsEqual?: (\n oldArgs: TDerivedArgs,\n newArgs: TDerivedArgs\n ) => boolean;\n\n autoRun?: boolean | 'afterUpdate';\n\n resolve: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<TResult>;\n\n commit?: (\n result: TResult,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onPending?: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onDisabled?: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onComplete?: (\n result: TResult,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onError?: (\n error: unknown,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onHookError?: (\n error: unknown,\n phase: BdResolveHookPhase\n ) => void;\n\n abortOnDisconnect?: boolean;\n\n /**\n * If false, this controller only consumes transitions supplied by options.transition.\n */\n captureActions?: boolean;\n\n createTransition?: (\n detail: BdActionRequestDetail,\n event: CustomEvent<BdActionRequestDetail>\n ) => BdTransition;\n\n /**\n * Optional predicate consulted for every transition candidate (both\n * local `bd-action-request` events and parent cascades).\n *\n * Return `false` to drop the candidate; the controller will not feed it\n * into `derive` / `resolve` / `commit`. The host typically uses this to\n * honour the `ignore-events` and `ignore-events-parent` attributes on\n * `bd-*` provider elements.\n */\n filterTransition?: (\n candidate: TransitionCandidate\n ) => boolean;\n}\n\ntype BdActionResolveInternalArgs<\n TDerivedArgs extends readonly unknown[]\n> = readonly [\n args: TDerivedArgs,\n context: BdActionResolveContext\n];\n\ntype TransitionCandidate = Readonly<{\n transition: BdTransition;\n source : BdActionResolveTransitionSource;\n}>;\n\n/**\n * Node workflow controller for the common bd runtime pattern:\n *\n * base args + fresh transition -> derived resolver args\n * enabled -> resolve -> commit\n * mark transition committed only after successful commit\n *\n * It keeps raw DOM requests out of node code and keeps transition seq\n * bookkeeping out of every concrete resolver.\n */\nexport class BdActionResolveController<\n TArgs extends readonly unknown[],\n TDerivedArgs extends readonly unknown[],\n TResult\n> implements ReactiveController {\n private readonly _resolver: BdResolveController<\n BdActionResolveInternalArgs<TDerivedArgs>,\n TResult\n >;\n\n private _localTransition?: BdTransition;\n private _lastCommittedTransitionSeq = 0;\n private _listening = false;\n\n constructor(\n private readonly host: BdActionResolveHost,\n private readonly options:\n BdActionResolveOptions<TArgs, TDerivedArgs, TResult>\n ) {\n host.addController(this);\n\n this._resolver = new BdResolveController(host, {\n name : options.name,\n workKey : options.workKey,\n autoRun : options.autoRun,\n abortOnDisconnect: options.abortOnDisconnect,\n args : () => this._args(),\n argsEqual : (oldArgs, newArgs) =>\n this._argsEqual(\n oldArgs,\n newArgs\n ),\n enabled: ([args, context]) =>\n options.enabled?.(\n args,\n context\n ) ?? true,\n resolve: ([args, actionContext], resolveContext) =>\n options.resolve(\n args,\n resolveContext,\n actionContext\n ),\n commit: async (\n result,\n [args, actionContext],\n resolveContext\n ) => {\n const commitResult =\n options.commit?.(\n result,\n args,\n resolveContext,\n actionContext\n );\n\n if (isPromiseLike(commitResult)) {\n await commitResult;\n }\n\n this._markTransitionCommitted(actionContext);\n },\n onPending: ([args, actionContext], resolveContext) =>\n options.onPending?.(\n args,\n resolveContext,\n actionContext\n ),\n onDisabled: ([args, actionContext], resolveContext) =>\n options.onDisabled?.(\n args,\n resolveContext,\n actionContext\n ),\n onComplete: (\n result,\n [args, actionContext],\n resolveContext\n ) =>\n options.onComplete?.(\n result,\n args,\n resolveContext,\n actionContext\n ),\n onError: (\n error,\n [args, actionContext],\n resolveContext\n ) =>\n options.onError?.(\n error,\n args,\n resolveContext,\n actionContext\n ),\n onHookError: options.onHookError\n });\n }\n\n hostConnected(): void {\n if (this.options.captureActions === false) {\n return;\n }\n\n if (this._listening) {\n return;\n }\n\n this.host.addEventListener(\n BD_ACTION_REQUEST_EVENT,\n this._onActionRequest as EventListener\n );\n\n this._listening = true;\n }\n\n hostDisconnected(): void {\n if (!this._listening) {\n return;\n }\n\n this.host.removeEventListener(\n BD_ACTION_REQUEST_EVENT,\n this._onActionRequest as EventListener\n );\n\n this._listening = false;\n }\n\n get status(): BdResolveStatus {\n return this._resolver.status;\n }\n\n get value(): TResult | undefined {\n return this._resolver.value;\n }\n\n get error(): unknown {\n return this._resolver.error;\n }\n\n get pending(): boolean {\n return this._resolver.pending;\n }\n\n get complete(): boolean {\n return this._resolver.complete;\n }\n\n get disabled(): boolean {\n return this._resolver.disabled;\n }\n\n get failed(): boolean {\n return this._resolver.failed;\n }\n\n get localTransition(): BdTransition | undefined {\n return this._localTransition;\n }\n\n run(): Promise<TResult | undefined> {\n return this._resolver.run();\n }\n\n abort(reason?: unknown): void {\n this._localTransition = undefined;\n this._resolver.abort(reason);\n }\n\n private _args(): BdActionResolveInternalArgs<TDerivedArgs> {\n const baseArgs =\n this.options.args();\n\n const context =\n this._createActionContext(baseArgs);\n\n const derivedArgs =\n this.options.derive(\n baseArgs,\n context\n );\n\n return [\n derivedArgs,\n context\n ] as const;\n }\n\n private _createActionContext(\n args: TArgs\n ): BdActionResolveContext {\n const candidate =\n this._selectTransitionCandidate(args);\n\n if (\n !candidate ||\n candidate.transition.seq <= this._lastCommittedTransitionSeq\n ) {\n return {\n freshTransition: false\n };\n }\n\n return {\n transition : candidate.transition,\n transitionSeq : candidate.transition.seq,\n transitionSource: candidate.source,\n freshTransition : true\n };\n }\n\n private _selectTransitionCandidate(\n args: TArgs\n ): TransitionCandidate | undefined {\n const filter = this.options.filterTransition;\n\n const localCandidate =\n this._localTransition\n ? {\n transition: this._localTransition,\n source : 'local' as const\n }\n : undefined;\n\n const parentTransition =\n this.options.transition?.(args);\n\n const parentCandidate =\n parentTransition\n ? {\n transition: parentTransition,\n source : 'parent' as const\n }\n : undefined;\n\n const candidates: TransitionCandidate[] = [];\n\n if (localCandidate && (!filter || filter(localCandidate))) {\n candidates.push(localCandidate);\n }\n\n if (parentCandidate && (!filter || filter(parentCandidate))) {\n candidates.push(parentCandidate);\n }\n\n if (candidates.length === 0) {\n return undefined;\n }\n\n return candidates.reduce((latest, current) =>\n current.transition.seq >= latest.transition.seq ? current : latest\n );\n }\n\n private _argsEqual(\n oldArgs: BdActionResolveInternalArgs<TDerivedArgs>,\n newArgs: BdActionResolveInternalArgs<TDerivedArgs>\n ): boolean {\n const [oldDerivedArgs, oldContext] = oldArgs;\n const [newDerivedArgs, newContext] = newArgs;\n\n if (\n newContext.freshTransition &&\n newContext.transitionSeq !== oldContext.transitionSeq\n ) {\n return false;\n }\n\n return (\n this.options.argsEqual?.(\n oldDerivedArgs,\n newDerivedArgs\n ) ??\n shallowArrayEqual(\n oldDerivedArgs,\n newDerivedArgs\n )\n );\n }\n\n private _markTransitionCommitted(\n context: BdActionResolveContext\n ): void {\n const transition = context.transition;\n\n if (!transition || !context.freshTransition) {\n return;\n }\n\n this._lastCommittedTransitionSeq = Math.max(\n this._lastCommittedTransitionSeq,\n transition.seq\n );\n\n if (this._localTransition === transition) {\n this._localTransition = undefined;\n }\n\n this.host.requestUpdate();\n }\n\n private readonly _onActionRequest = (\n event: Event\n ): void => {\n if (!isBdActionRequestEvent(event)) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n const transition =\n this.options.createTransition?.(\n event.detail,\n event\n ) ??\n createBdTransition(\n event.detail,\n event.target\n );\n\n const filter = this.options.filterTransition;\n\n if (\n filter &&\n !filter({\n transition,\n source: 'local'\n })\n ) {\n return;\n }\n\n this._localTransition = transition;\n\n this.host.requestUpdate();\n };\n}\n\nfunction shallowArrayEqual(\n oldArgs: readonly unknown[],\n newArgs: readonly unknown[]\n): boolean {\n if (oldArgs === newArgs) {\n return true;\n }\n\n if (oldArgs.length !== newArgs.length) {\n return false;\n }\n\n for (let index = 0; index < oldArgs.length; index += 1) {\n if (!Object.is(\n oldArgs[index],\n newArgs[index]\n )) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isPromiseLike<T>(\n value: MaybePromise<T> | undefined\n): value is Promise<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n );\n}\n"]}
1
+ {"version":3,"file":"controller.action-resolve.js","sourceRoot":"","sources":["../../../src/controllers/controller.action-resolve.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EAGvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EAOpB,MAAM,yBAAyB,CAAC;AAsJjC;;;;;;;;;GASG;AACH,MAAM,OAAO,yBAAyB;IAepC,YACmB,IAAyB,EACzB,OACmC;QAFnC,SAAI,GAAJ,IAAI,CAAqB;QACzB,YAAO,GAAP,OAAO,CAC4B;QAP9C,gCAA2B,GAAG,CAAC,CAAC;QACvB,4BAAuB,GAAG,IAAI,GAAG,EAAU,CAAC;QACrD,eAAU,GAAG,KAAK,CAAC;QAmTV,qBAAgB,GAAG,CAClC,KAAY,EACN,EAAE;YACR,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnC,OAAO;YACT,CAAC;YAED,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,KAAK,CAAC,eAAe,EAAE,CAAC;YAExB,MAAM,UAAU,GACd,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAC7B,KAAK,CAAC,MAAM,EACZ,KAAK,CACN;gBACD,kBAAkB,CAChB,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,MAAM,CACb,CAAC;YAEJ,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAE7C,IACE,MAAM;gBACN,CAAC,MAAM,CAAC;oBACN,UAAU;oBACV,MAAM,EAAE,OAAO;iBAChB,CAAC,EACF,CAAC;gBACD,OAAO;YACT,CAAC;YAED,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC;YAEnC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC5B,CAAC,CAAC;QA/UA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAEzB,IAAI,CAAC,SAAS,GAAG,IAAI,mBAAmB,CAAC,IAAI,EAAE;YAC7C,IAAI,EAAe,OAAO,CAAC,IAAI;YAC/B,OAAO,EAAY,OAAO,CAAC,OAAO;YAClC,OAAO,EAAY,OAAO,CAAC,OAAO;YAClC,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,IAAI,EAAe,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;YACrC,SAAS,EAAU,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CACtC,IAAI,CAAC,UAAU,CACb,OAAO,EACP,OAAO,CACR;YACH,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAC3B,OAAO,CAAC,OAAO,EAAE,CACf,IAAI,EACJ,OAAO,CACR,IAAI,IAAI;YACX,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,CACjD,OAAO,CAAC,OAAO,CACb,IAAI,EACJ,cAAc,EACd,aAAa,CACd;YACH,MAAM,EAAE,KAAK,EACX,MAAM,EACN,CAAC,IAAI,EAAE,aAAa,CAAC,EACrB,cAAc,EACd,EAAE;gBACF,MAAM,YAAY,GAChB,OAAO,CAAC,MAAM,EAAE,CACd,MAAM,EACN,IAAI,EACJ,cAAc,EACd,aAAa,CACd,CAAC;gBAEJ,IAAI,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;oBAChC,MAAM,YAAY,CAAC;gBACrB,CAAC;gBAED,IAAI,CAAC,wBAAwB,CAAC,aAAa,CAAC,CAAC;YAC/C,CAAC;YACD,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,CACnD,OAAO,CAAC,SAAS,EAAE,CACjB,IAAI,EACJ,cAAc,EACd,aAAa,CACd;YACH,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,CACpD,OAAO,CAAC,UAAU,EAAE,CAClB,IAAI,EACJ,cAAc,EACd,aAAa,CACd;YACH,UAAU,EAAE,CACV,MAAM,EACN,CAAC,IAAI,EAAE,aAAa,CAAC,EACrB,cAAc,EACd,EAAE,CACF,OAAO,CAAC,UAAU,EAAE,CAClB,MAAM,EACN,IAAI,EACJ,cAAc,EACd,aAAa,CACd;YACH,OAAO,EAAE,CACP,KAAK,EACL,CAAC,IAAI,EAAE,aAAa,CAAC,EACrB,cAAc,EACd,EAAE,CACF,OAAO,CAAC,OAAO,EAAE,CACf,KAAK,EACL,IAAI,EACJ,cAAc,EACd,aAAa,CACd;YACH,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC,CAAC;IACL,CAAC;IAED,aAAa;QACX,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,KAAK,KAAK,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,gBAAgB,CACxB,uBAAuB,EACvB,IAAI,CAAC,gBAAiC,CACvC,CAAC;QAEF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAC3B,uBAAuB,EACvB,IAAI,CAAC,gBAAiC,CACvC,CAAC;QAEF,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;IAC/B,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;IAChC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;IAC/B,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,MAAgB;QACpB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAEO,KAAK;QACX,MAAM,QAAQ,GACZ,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAEtB,MAAM,OAAO,GACX,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAEtC,MAAM,WAAW,GACf,IAAI,CAAC,OAAO,CAAC,MAAM,CACjB,QAAQ,EACR,OAAO,CACR,CAAC;QAEJ,OAAO;YACL,WAAW;YACX,OAAO;SACC,CAAC;IACb,CAAC;IAEO,oBAAoB,CAC1B,IAAW;QAEX,MAAM,SAAS,GACb,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAExC,IACE,CAAC,SAAS;YACV,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;YAC1D,SAAS,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,2BAA2B,EAC5D,CAAC;YACD,OAAO;gBACL,eAAe,EAAE,KAAK;aACvB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,UAAU,EAAQ,SAAS,CAAC,UAAU;YACtC,aAAa,EAAK,SAAS,CAAC,UAAU,CAAC,GAAG;YAC1C,gBAAgB,EAAE,SAAS,CAAC,MAAM;YAClC,eAAe,EAAG,IAAI;SACvB,CAAC;IACJ,CAAC;IAEO,0BAA0B,CAChC,IAAW;QAEX,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC;QAE7C,MAAM,cAAc,GAClB,IAAI,CAAC,gBAAgB;YACnB,CAAC,CAAC;gBACA,UAAU,EAAE,IAAI,CAAC,gBAAgB;gBACjC,MAAM,EAAM,OAAgB;aAC7B;YACD,CAAC,CAAC,SAAS,CAAC;QAEhB,MAAM,gBAAgB,GACpB,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,eAAe,GACnB,gBAAgB;YACd,CAAC,CAAC;gBACA,UAAU,EAAE,gBAAgB;gBAC5B,MAAM,EAAM,QAAiB;aAC9B;YACD,CAAC,CAAC,SAAS,CAAC;QAEhB,MAAM,UAAU,GAA0B,EAAE,CAAC;QAE7C,IAAI,cAAc,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;YAC1D,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClC,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAC9B,cAAc,CAAC,UAAU,CAAC,GAAG,CAC9B,CAAC;QACJ,CAAC;QAED,IAAI,eAAe,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;YAC5D,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,eAAe,EAAE,CAAC;YAC3B,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAC9B,eAAe,CAAC,UAAU,CAAC,GAAG,CAC/B,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAC3C,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CACnE,CAAC;IACJ,CAAC;IAEO,UAAU,CAChB,OAAkD,EAClD,OAAkD;QAElD,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC;QAC7C,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC;QAE7C,IACE,UAAU,CAAC,eAAe;YAC1B,UAAU,CAAC,aAAa,KAAK,UAAU,CAAC,aAAa,EACrD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CACtB,cAAc,EACd,cAAc,CACf;YACD,iBAAiB,CACf,cAAc,EACd,cAAc,CACf,CACF,CAAC;IACJ,CAAC;IAEO,wBAAwB,CAC9B,OAA+B;QAE/B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,IAAI,CAAC,2BAA2B,GAAG,IAAI,CAAC,GAAG,CACzC,IAAI,CAAC,2BAA2B,EAChC,UAAU,CAAC,GAAG,CACf,CAAC;QAEF,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC/C,IAAI,GAAG,IAAI,IAAI,CAAC,2BAA2B,EAAE,CAAC;gBAC5C,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;YACzC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACpC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAC5B,CAAC;CAsCF;AAED,SAAS,iBAAiB,CACxB,OAA2B,EAC3B,OAA2B;IAE3B,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,EAAE,CACZ,OAAO,CAAC,KAAK,CAAC,EACd,OAAO,CAAC,KAAK,CAAC,CACf,EAAE,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CACpB,KAAkC;IAElC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,CACjC,CAAC;AACJ,CAAC","sourcesContent":["import type { ReactiveController } from 'lit';\n\nimport {\n BD_ACTION_REQUEST_EVENT,\n createBdTransition,\n isBdActionRequestEvent,\n type BdActionRequestDetail,\n type BdTransition\n} from '@/core/actions.js';\nimport {\n BdResolveController,\n type BdResolveContext,\n type BdResolveHookPhase,\n type BdResolveHost,\n type BdResolveStatus,\n type BdWorkKey,\n type MaybePromise\n} from './controller.resolve.js';\n\nexport type BdActionResolveTransitionSource =\n | 'local'\n | 'parent';\n\nexport type BdActionResolveContext = Readonly<{\n /**\n * The transition passed to derive/resolve/commit.\n *\n * This is only set for a fresh transition. Once the controller marks a\n * transition committed, the same transition is suppressed from future args.\n */\n transition?: BdTransition;\n\n transitionSeq? : number;\n transitionSource?: BdActionResolveTransitionSource;\n freshTransition : boolean;\n}>;\n\nexport interface BdActionResolveHost extends BdResolveHost, EventTarget {}\n\nexport interface BdActionResolveOptions<\n TArgs extends readonly unknown[],\n TDerivedArgs extends readonly unknown[],\n TResult\n> {\n name? : string;\n workKey?: BdWorkKey;\n\n /**\n * Base node inputs. Keep this close to @lit/task naming.\n *\n * Examples:\n * () => [this.parentScope, this.devices, this.subscription] as const\n */\n args: () => TArgs;\n\n /**\n * Optional downward transition source, usually parentScope.transition.\n *\n * Local bd-action-request events captured by this controller are considered\n * alongside this transition, with the latest seq winning.\n */\n transition?: (args: TArgs) => BdTransition | undefined;\n\n /**\n * Converts base args + a fresh transition into the actual resolver args.\n *\n * This should be pure. It must not publish scope or mutate host props.\n */\n derive: (\n args: TArgs,\n context: BdActionResolveContext\n ) => TDerivedArgs;\n\n enabled?: (\n args: TDerivedArgs,\n context: BdActionResolveContext\n ) => boolean;\n\n argsEqual?: (\n oldArgs: TDerivedArgs,\n newArgs: TDerivedArgs\n ) => boolean;\n\n autoRun?: boolean | 'afterUpdate';\n\n resolve: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<TResult>;\n\n commit?: (\n result: TResult,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onPending?: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onDisabled?: (\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onComplete?: (\n result: TResult,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onError?: (\n error: unknown,\n args: TDerivedArgs,\n resolveContext: BdResolveContext,\n actionContext: BdActionResolveContext\n ) => MaybePromise<void>;\n\n onHookError?: (\n error: unknown,\n phase: BdResolveHookPhase\n ) => void;\n\n abortOnDisconnect?: boolean;\n\n /**\n * If false, this controller only consumes transitions supplied by options.transition.\n */\n captureActions?: boolean;\n\n createTransition?: (\n detail: BdActionRequestDetail,\n event: CustomEvent<BdActionRequestDetail>\n ) => BdTransition;\n\n /**\n * Optional predicate consulted for every transition candidate (both\n * local `bd-action-request` events and parent cascades).\n *\n * Return `false` to drop the candidate; the controller will not feed it\n * into `derive` / `resolve` / `commit`. The host typically uses this to\n * honour the `ignore-events` and `ignore-events-parent` attributes on\n * `bd-*` provider elements.\n */\n filterTransition?: (\n candidate: TransitionCandidate\n ) => boolean;\n}\n\ntype BdActionResolveInternalArgs<\n TDerivedArgs extends readonly unknown[]\n> = readonly [\n args: TDerivedArgs,\n context: BdActionResolveContext\n];\n\ntype TransitionCandidate = Readonly<{\n transition: BdTransition;\n source : BdActionResolveTransitionSource;\n}>;\n\n/**\n * Node workflow controller for the common bd runtime pattern:\n *\n * base args + fresh transition -> derived resolver args\n * enabled -> resolve -> commit\n * mark transition committed only after successful commit\n *\n * It keeps raw DOM requests out of node code and keeps transition seq\n * bookkeeping out of every concrete resolver.\n */\nexport class BdActionResolveController<\n TArgs extends readonly unknown[],\n TDerivedArgs extends readonly unknown[],\n TResult\n> implements ReactiveController {\n private readonly _resolver: BdResolveController<\n BdActionResolveInternalArgs<TDerivedArgs>,\n TResult\n >;\n\n private _localTransition?: BdTransition;\n private _lastCommittedTransitionSeq = 0;\n private readonly _rejectedTransitionSeqs = new Set<number>();\n private _listening = false;\n\n constructor(\n private readonly host: BdActionResolveHost,\n private readonly options:\n BdActionResolveOptions<TArgs, TDerivedArgs, TResult>\n ) {\n host.addController(this);\n\n this._resolver = new BdResolveController(host, {\n name : options.name,\n workKey : options.workKey,\n autoRun : options.autoRun,\n abortOnDisconnect: options.abortOnDisconnect,\n args : () => this._args(),\n argsEqual : (oldArgs, newArgs) =>\n this._argsEqual(\n oldArgs,\n newArgs\n ),\n enabled: ([args, context]) =>\n options.enabled?.(\n args,\n context\n ) ?? true,\n resolve: ([args, actionContext], resolveContext) =>\n options.resolve(\n args,\n resolveContext,\n actionContext\n ),\n commit: async (\n result,\n [args, actionContext],\n resolveContext\n ) => {\n const commitResult =\n options.commit?.(\n result,\n args,\n resolveContext,\n actionContext\n );\n\n if (isPromiseLike(commitResult)) {\n await commitResult;\n }\n\n this._markTransitionCommitted(actionContext);\n },\n onPending: ([args, actionContext], resolveContext) =>\n options.onPending?.(\n args,\n resolveContext,\n actionContext\n ),\n onDisabled: ([args, actionContext], resolveContext) =>\n options.onDisabled?.(\n args,\n resolveContext,\n actionContext\n ),\n onComplete: (\n result,\n [args, actionContext],\n resolveContext\n ) =>\n options.onComplete?.(\n result,\n args,\n resolveContext,\n actionContext\n ),\n onError: (\n error,\n [args, actionContext],\n resolveContext\n ) =>\n options.onError?.(\n error,\n args,\n resolveContext,\n actionContext\n ),\n onHookError: options.onHookError\n });\n }\n\n hostConnected(): void {\n if (this.options.captureActions === false) {\n return;\n }\n\n if (this._listening) {\n return;\n }\n\n this.host.addEventListener(\n BD_ACTION_REQUEST_EVENT,\n this._onActionRequest as EventListener\n );\n\n this._listening = true;\n }\n\n hostDisconnected(): void {\n if (!this._listening) {\n return;\n }\n\n this.host.removeEventListener(\n BD_ACTION_REQUEST_EVENT,\n this._onActionRequest as EventListener\n );\n\n this._listening = false;\n }\n\n get status(): BdResolveStatus {\n return this._resolver.status;\n }\n\n get value(): TResult | undefined {\n return this._resolver.value;\n }\n\n get error(): unknown {\n return this._resolver.error;\n }\n\n get pending(): boolean {\n return this._resolver.pending;\n }\n\n get complete(): boolean {\n return this._resolver.complete;\n }\n\n get disabled(): boolean {\n return this._resolver.disabled;\n }\n\n get failed(): boolean {\n return this._resolver.failed;\n }\n\n get localTransition(): BdTransition | undefined {\n return this._localTransition;\n }\n\n run(): Promise<TResult | undefined> {\n return this._resolver.run();\n }\n\n abort(reason?: unknown): void {\n this._localTransition = undefined;\n this._rejectedTransitionSeqs.clear();\n this._resolver.abort(reason);\n }\n\n private _args(): BdActionResolveInternalArgs<TDerivedArgs> {\n const baseArgs =\n this.options.args();\n\n const context =\n this._createActionContext(baseArgs);\n\n const derivedArgs =\n this.options.derive(\n baseArgs,\n context\n );\n\n return [\n derivedArgs,\n context\n ] as const;\n }\n\n private _createActionContext(\n args: TArgs\n ): BdActionResolveContext {\n const candidate =\n this._selectTransitionCandidate(args);\n\n if (\n !candidate ||\n this._rejectedTransitionSeqs.has(candidate.transition.seq) ||\n candidate.transition.seq <= this._lastCommittedTransitionSeq\n ) {\n return {\n freshTransition: false\n };\n }\n\n return {\n transition : candidate.transition,\n transitionSeq : candidate.transition.seq,\n transitionSource: candidate.source,\n freshTransition : true\n };\n }\n\n private _selectTransitionCandidate(\n args: TArgs\n ): TransitionCandidate | undefined {\n const filter = this.options.filterTransition;\n\n const localCandidate =\n this._localTransition\n ? {\n transition: this._localTransition,\n source : 'local' as const\n }\n : undefined;\n\n const parentTransition =\n this.options.transition?.(args);\n\n const parentCandidate =\n parentTransition\n ? {\n transition: parentTransition,\n source : 'parent' as const\n }\n : undefined;\n\n const candidates: TransitionCandidate[] = [];\n\n if (localCandidate && (!filter || filter(localCandidate))) {\n candidates.push(localCandidate);\n } else if (localCandidate) {\n this._rejectedTransitionSeqs.add(\n localCandidate.transition.seq\n );\n }\n\n if (parentCandidate && (!filter || filter(parentCandidate))) {\n candidates.push(parentCandidate);\n } else if (parentCandidate) {\n this._rejectedTransitionSeqs.add(\n parentCandidate.transition.seq\n );\n }\n\n if (candidates.length === 0) {\n return undefined;\n }\n\n return candidates.reduce((latest, current) =>\n current.transition.seq >= latest.transition.seq ? current : latest\n );\n }\n\n private _argsEqual(\n oldArgs: BdActionResolveInternalArgs<TDerivedArgs>,\n newArgs: BdActionResolveInternalArgs<TDerivedArgs>\n ): boolean {\n const [oldDerivedArgs, oldContext] = oldArgs;\n const [newDerivedArgs, newContext] = newArgs;\n\n if (\n newContext.freshTransition &&\n newContext.transitionSeq !== oldContext.transitionSeq\n ) {\n return false;\n }\n\n return (\n this.options.argsEqual?.(\n oldDerivedArgs,\n newDerivedArgs\n ) ??\n shallowArrayEqual(\n oldDerivedArgs,\n newDerivedArgs\n )\n );\n }\n\n private _markTransitionCommitted(\n context: BdActionResolveContext\n ): void {\n const transition = context.transition;\n\n if (!transition || !context.freshTransition) {\n return;\n }\n\n this._lastCommittedTransitionSeq = Math.max(\n this._lastCommittedTransitionSeq,\n transition.seq\n );\n\n for (const seq of this._rejectedTransitionSeqs) {\n if (seq <= this._lastCommittedTransitionSeq) {\n this._rejectedTransitionSeqs.delete(seq);\n }\n }\n\n if (this._localTransition === transition) {\n this._localTransition = undefined;\n }\n\n this.host.requestUpdate();\n }\n\n private readonly _onActionRequest = (\n event: Event\n ): void => {\n if (!isBdActionRequestEvent(event)) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n const transition =\n this.options.createTransition?.(\n event.detail,\n event\n ) ??\n createBdTransition(\n event.detail,\n event.target\n );\n\n const filter = this.options.filterTransition;\n\n if (\n filter &&\n !filter({\n transition,\n source: 'local'\n })\n ) {\n return;\n }\n\n this._localTransition = transition;\n\n this.host.requestUpdate();\n };\n}\n\nfunction shallowArrayEqual(\n oldArgs: readonly unknown[],\n newArgs: readonly unknown[]\n): boolean {\n if (oldArgs === newArgs) {\n return true;\n }\n\n if (oldArgs.length !== newArgs.length) {\n return false;\n }\n\n for (let index = 0; index < oldArgs.length; index += 1) {\n if (!Object.is(\n oldArgs[index],\n newArgs[index]\n )) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isPromiseLike<T>(\n value: MaybePromise<T> | undefined\n): value is Promise<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n );\n}\n"]}
@@ -6,8 +6,8 @@ export type UpdateByDelta = Readonly<{
6
6
  useAsValue?: boolean;
7
7
  /**
8
8
  * Optional identifier for the action source element
9
- * (`data-store-event-id`). Consumers filter against this id via the
10
- * `ignore-events` attribute on `bd-*` provider elements.
9
+ * (`data-store-event-id`, or legacy `data-store-id`). Consumers filter
10
+ * against this id via `ignore-events` on `bd-*` provider elements.
11
11
  */
12
12
  eventId?: string;
13
13
  }>;
@@ -19,8 +19,8 @@ export type Action = Readonly<{
19
19
  subscription?: number;
20
20
  /**
21
21
  * Optional identifier for the action source element
22
- * (`data-store-event-id`). Consumers filter against this id via the
23
- * `ignore-events` attribute on `bd-*` provider elements.
22
+ * (`data-store-event-id`, or legacy `data-store-id`). Consumers filter
23
+ * against this id via `ignore-events` on `bd-*` provider elements.
24
24
  */
25
25
  eventId?: string;
26
26
  }>;
@@ -52,10 +52,10 @@ export declare function isBdActionRequestEvent(event: Event): event is CustomEve
52
52
  /**
53
53
  * Pulls the optional `eventId` off the command carried by a transition.
54
54
  *
55
- * `eventId` is set on the action element via `data-store-event-id="…"` and
56
- * is the matching key for the `ignore-events="…"` attribute on `bd-*`
57
- * provider elements. Returns `undefined` for commands that did not declare
58
- * one.
55
+ * `eventId` is set on the action element via `data-store-event-id="…"`
56
+ * (or legacy `data-store-id="…"`) and is the matching key for the
57
+ * `ignore-events="…"` attribute on `bd-*` provider elements. Returns
58
+ * `undefined` for commands that did not declare one.
59
59
  */
60
60
  export declare function getCommandEventId(command: BdActionCommand): string | undefined;
61
61
  /**
@@ -28,10 +28,10 @@ export function isBdActionRequestEvent(event) {
28
28
  /**
29
29
  * Pulls the optional `eventId` off the command carried by a transition.
30
30
  *
31
- * `eventId` is set on the action element via `data-store-event-id="…"` and
32
- * is the matching key for the `ignore-events="…"` attribute on `bd-*`
33
- * provider elements. Returns `undefined` for commands that did not declare
34
- * one.
31
+ * `eventId` is set on the action element via `data-store-event-id="…"`
32
+ * (or legacy `data-store-id="…"`) and is the matching key for the
33
+ * `ignore-events="…"` attribute on `bd-*` provider elements. Returns
34
+ * `undefined` for commands that did not declare one.
35
35
  */
36
36
  export function getCommandEventId(command) {
37
37
  return command.kind === 'set-action'
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../../src/core/actions.ts"],"names":[],"mappings":"AA6CA,MAAM,CAAC,MAAM,uBAAuB,GAClC,mBAA4B,CAAC;AAY/B,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB,MAAM,UAAU,kBAAkB,CAChC,MAA6B,EAC7B,UAA+B;IAE/B,OAAO;QACL,GAAG,EAAQ,EAAE,eAAe;QAC5B,OAAO,EAAI,MAAM,CAAC,OAAO;QACzB,UAAU;QACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,WAAwB,EACxB,MAA6B;IAE7B,OAAO,WAAW,CAAC,aAAa,CAC9B,IAAI,WAAW,CACb,uBAAuB,EACvB;QACE,OAAO,EAAK,IAAI;QAChB,QAAQ,EAAI,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,MAAM;KACP,CACF,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAY;IAEZ,IAAI,KAAK,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAI,KAA8B,CAAC,MAAM,CAAC;IAEtD,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,SAAS,IAAI,MAAM,CACpB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAwB;IAExB,OAAO,OAAO,CAAC,IAAI,KAAK,YAAY;QAClC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO;QACxB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAwB;IAExB,OAAO,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC","sourcesContent":["export type UpdateByDelta = Readonly<{\n type : 'devices' | 'subscription';\n delta : number | 'next' | 'prev';\n min? : number;\n max? : number;\n useAsValue?: boolean;\n /**\n * Optional identifier for the action source element\n * (`data-store-event-id`). Consumers filter against this id via the\n * `ignore-events` attribute on `bd-*` provider elements.\n */\n eventId? : string;\n}>;\n\nexport type Action = Readonly<{\n id? : string;\n campaign? : string;\n bundle? : boolean;\n devices? : number;\n subscription?: number;\n /**\n * Optional identifier for the action source element\n * (`data-store-event-id`). Consumers filter against this id via the\n * `ignore-events` attribute on `bd-*` provider elements.\n */\n eventId? : string;\n}>;\n\nexport type BdActionCommand =\n | Readonly<{\n kind : 'update-by-delta';\n update: UpdateByDelta;\n }>\n | Readonly<{\n kind : 'set-action';\n action: Action;\n }>;\n\nexport type BdTransition = Readonly<{\n seq : number;\n command : BdActionCommand;\n originNode?: EventTarget | null;\n createdAt : number;\n}>;\n\nexport const BD_ACTION_REQUEST_EVENT =\n 'bd-action-request' as const;\n\nexport type BdActionRequestDetail = Readonly<{\n command: BdActionCommand;\n}>;\n\ndeclare global {\n interface HTMLElementEventMap {\n [BD_ACTION_REQUEST_EVENT]: CustomEvent<BdActionRequestDetail>;\n }\n}\n\nlet bdTransitionSeq = 0;\n\nexport function createBdTransition(\n detail: BdActionRequestDetail,\n originNode?: EventTarget | null\n): BdTransition {\n return {\n seq : ++bdTransitionSeq,\n command : detail.command,\n originNode,\n createdAt: Date.now()\n };\n}\n\nexport function dispatchBdActionRequest(\n eventTarget: EventTarget,\n detail: BdActionRequestDetail\n): boolean {\n return eventTarget.dispatchEvent(\n new CustomEvent<BdActionRequestDetail>(\n BD_ACTION_REQUEST_EVENT,\n {\n bubbles : true,\n composed : true,\n cancelable: true,\n detail\n }\n )\n );\n}\n\nexport function isBdActionRequestEvent(\n event: Event\n): event is CustomEvent<BdActionRequestDetail> {\n if (event.type !== BD_ACTION_REQUEST_EVENT) {\n return false;\n }\n\n const detail = (event as CustomEvent<unknown>).detail;\n\n return (\n typeof detail === 'object' &&\n detail !== null &&\n 'command' in detail\n );\n}\n\n/**\n * Pulls the optional `eventId` off the command carried by a transition.\n *\n * `eventId` is set on the action element via `data-store-event-id=\"…\"` and\n * is the matching key for the `ignore-events=\"…\"` attribute on `bd-*`\n * provider elements. Returns `undefined` for commands that did not declare\n * one.\n */\nexport function getCommandEventId(\n command: BdActionCommand\n): string | undefined {\n return command.kind === 'set-action'\n ? command.action.eventId\n : command.update.eventId;\n}\n\n/**\n * Convenience wrapper around {@link getCommandEventId} for transitions.\n */\nexport function getTransitionEventId(\n transition: BdTransition\n): string | undefined {\n return getCommandEventId(transition.command);\n}\n"]}
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../../src/core/actions.ts"],"names":[],"mappings":"AA6CA,MAAM,CAAC,MAAM,uBAAuB,GAClC,mBAA4B,CAAC;AAY/B,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB,MAAM,UAAU,kBAAkB,CAChC,MAA6B,EAC7B,UAA+B;IAE/B,OAAO;QACL,GAAG,EAAQ,EAAE,eAAe;QAC5B,OAAO,EAAI,MAAM,CAAC,OAAO;QACzB,UAAU;QACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,WAAwB,EACxB,MAA6B;IAE7B,OAAO,WAAW,CAAC,aAAa,CAC9B,IAAI,WAAW,CACb,uBAAuB,EACvB;QACE,OAAO,EAAK,IAAI;QAChB,QAAQ,EAAI,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,MAAM;KACP,CACF,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAY;IAEZ,IAAI,KAAK,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAI,KAA8B,CAAC,MAAM,CAAC;IAEtD,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,SAAS,IAAI,MAAM,CACpB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAwB;IAExB,OAAO,OAAO,CAAC,IAAI,KAAK,YAAY;QAClC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO;QACxB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAwB;IAExB,OAAO,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC","sourcesContent":["export type UpdateByDelta = Readonly<{\n type : 'devices' | 'subscription';\n delta : number | 'next' | 'prev';\n min? : number;\n max? : number;\n useAsValue?: boolean;\n /**\n * Optional identifier for the action source element\n * (`data-store-event-id`, or legacy `data-store-id`). Consumers filter\n * against this id via `ignore-events` on `bd-*` provider elements.\n */\n eventId? : string;\n}>;\n\nexport type Action = Readonly<{\n id? : string;\n campaign? : string;\n bundle? : boolean;\n devices? : number;\n subscription?: number;\n /**\n * Optional identifier for the action source element\n * (`data-store-event-id`, or legacy `data-store-id`). Consumers filter\n * against this id via `ignore-events` on `bd-*` provider elements.\n */\n eventId? : string;\n}>;\n\nexport type BdActionCommand =\n | Readonly<{\n kind : 'update-by-delta';\n update: UpdateByDelta;\n }>\n | Readonly<{\n kind : 'set-action';\n action: Action;\n }>;\n\nexport type BdTransition = Readonly<{\n seq : number;\n command : BdActionCommand;\n originNode?: EventTarget | null;\n createdAt : number;\n}>;\n\nexport const BD_ACTION_REQUEST_EVENT =\n 'bd-action-request' as const;\n\nexport type BdActionRequestDetail = Readonly<{\n command: BdActionCommand;\n}>;\n\ndeclare global {\n interface HTMLElementEventMap {\n [BD_ACTION_REQUEST_EVENT]: CustomEvent<BdActionRequestDetail>;\n }\n}\n\nlet bdTransitionSeq = 0;\n\nexport function createBdTransition(\n detail: BdActionRequestDetail,\n originNode?: EventTarget | null\n): BdTransition {\n return {\n seq : ++bdTransitionSeq,\n command : detail.command,\n originNode,\n createdAt: Date.now()\n };\n}\n\nexport function dispatchBdActionRequest(\n eventTarget: EventTarget,\n detail: BdActionRequestDetail\n): boolean {\n return eventTarget.dispatchEvent(\n new CustomEvent<BdActionRequestDetail>(\n BD_ACTION_REQUEST_EVENT,\n {\n bubbles : true,\n composed : true,\n cancelable: true,\n detail\n }\n )\n );\n}\n\nexport function isBdActionRequestEvent(\n event: Event\n): event is CustomEvent<BdActionRequestDetail> {\n if (event.type !== BD_ACTION_REQUEST_EVENT) {\n return false;\n }\n\n const detail = (event as CustomEvent<unknown>).detail;\n\n return (\n typeof detail === 'object' &&\n detail !== null &&\n 'command' in detail\n );\n}\n\n/**\n * Pulls the optional `eventId` off the command carried by a transition.\n *\n * `eventId` is set on the action element via `data-store-event-id=\"…\"`\n * (or legacy `data-store-id=\"…\"`) and is the matching key for the\n * `ignore-events=\"…\"` attribute on `bd-*` provider elements. Returns\n * `undefined` for commands that did not declare one.\n */\nexport function getCommandEventId(\n command: BdActionCommand\n): string | undefined {\n return command.kind === 'set-action'\n ? command.action.eventId\n : command.update.eventId;\n}\n\n/**\n * Convenience wrapper around {@link getCommandEventId} for transitions.\n */\nexport function getTransitionEventId(\n transition: BdTransition\n): string | undefined {\n return getCommandEventId(transition.command);\n}\n"]}