@repobit/dex-store-elements 1.7.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +6 -906
  2. package/README.md +308 -283
  3. package/dist/src/actions/action.adapter.d.ts +36 -0
  4. package/dist/src/actions/action.adapter.js +183 -0
  5. package/dist/src/actions/action.adapter.js.map +1 -0
  6. package/dist/src/actions/action.attributes.d.ts +15 -0
  7. package/dist/src/actions/action.attributes.js +137 -0
  8. package/dist/src/actions/action.attributes.js.map +1 -0
  9. package/dist/src/actions/action.registry.d.ts +4 -0
  10. package/dist/src/actions/action.registry.js +119 -0
  11. package/dist/src/actions/action.registry.js.map +1 -0
  12. package/dist/src/actions/index.d.ts +3 -2
  13. package/dist/src/actions/index.js +3 -80
  14. package/dist/src/actions/index.js.map +1 -1
  15. package/dist/src/compute/compute.expand.d.ts +70 -0
  16. package/dist/src/compute/compute.expand.js +319 -0
  17. package/dist/src/compute/compute.expand.js.map +1 -0
  18. package/dist/src/compute/compute.scan.d.ts +54 -0
  19. package/dist/src/compute/compute.scan.js +160 -0
  20. package/dist/src/compute/compute.scan.js.map +1 -0
  21. package/dist/src/compute/option-resolver.d.ts +31 -0
  22. package/dist/src/compute/option-resolver.js +147 -0
  23. package/dist/src/compute/option-resolver.js.map +1 -0
  24. package/dist/src/contexts/context.compute.d.ts +12 -0
  25. package/dist/src/contexts/context.compute.js +4 -0
  26. package/dist/src/contexts/context.compute.js.map +1 -0
  27. package/dist/src/contexts/context.scope.d.ts +139 -0
  28. package/dist/src/contexts/context.scope.js +48 -0
  29. package/dist/src/contexts/context.scope.js.map +1 -0
  30. package/dist/src/controllers/controller.action-resolve.d.ts +110 -0
  31. package/dist/src/controllers/controller.action-resolve.js +203 -0
  32. package/dist/src/controllers/controller.action-resolve.js.map +1 -0
  33. package/dist/src/controllers/controller.action-topology.d.ts +59 -0
  34. package/dist/src/controllers/controller.action-topology.js +130 -0
  35. package/dist/src/controllers/controller.action-topology.js.map +1 -0
  36. package/dist/src/controllers/controller.compute.d.ts +72 -0
  37. package/dist/src/controllers/controller.compute.js +110 -0
  38. package/dist/src/controllers/controller.compute.js.map +1 -0
  39. package/dist/src/controllers/controller.resolve.d.ts +78 -0
  40. package/dist/src/controllers/controller.resolve.js +205 -0
  41. package/dist/src/controllers/controller.resolve.js.map +1 -0
  42. package/dist/src/core/action-topology.d.ts +62 -0
  43. package/dist/src/core/action-topology.js +67 -0
  44. package/dist/src/core/action-topology.js.map +1 -0
  45. package/dist/src/core/actions.d.ts +64 -0
  46. package/dist/src/core/actions.js +47 -0
  47. package/dist/src/core/actions.js.map +1 -0
  48. package/dist/src/core/node.element.d.ts +179 -0
  49. package/dist/src/core/node.element.js +573 -0
  50. package/dist/src/core/node.element.js.map +1 -0
  51. package/dist/src/core/node.scope.d.ts +374 -0
  52. package/dist/src/core/node.scope.js +558 -0
  53. package/dist/src/core/node.scope.js.map +1 -0
  54. package/dist/src/core/option-update.d.ts +11 -0
  55. package/dist/src/core/option-update.js +59 -0
  56. package/dist/src/core/option-update.js.map +1 -0
  57. package/dist/src/dsl/compilers/array/compiler.js +13 -14
  58. package/dist/src/dsl/compilers/array/compiler.js.map +1 -1
  59. package/dist/src/dsl/compilers/boolean/compiler.d.ts +1 -1
  60. package/dist/src/dsl/compilers/boolean/compiler.js +14 -27
  61. package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -1
  62. package/dist/src/dsl/compilers/enum/compiler.js +7 -10
  63. package/dist/src/dsl/compilers/enum/compiler.js.map +1 -1
  64. package/dist/src/dsl/compilers/index.d.ts +1 -1
  65. package/dist/src/dsl/compilers/index.js +5 -5
  66. package/dist/src/dsl/compilers/index.js.map +1 -1
  67. package/dist/src/dsl/utilty.d.ts +0 -4
  68. package/dist/src/dsl/utilty.js +3 -30
  69. package/dist/src/dsl/utilty.js.map +1 -1
  70. package/dist/src/eta/batch.d.ts +123 -0
  71. package/dist/src/eta/batch.js +162 -0
  72. package/dist/src/eta/batch.js.map +1 -0
  73. package/dist/src/eta/batcher.d.ts +184 -0
  74. package/dist/src/eta/batcher.js +335 -0
  75. package/dist/src/eta/batcher.js.map +1 -0
  76. package/dist/src/eta/cache.d.ts +36 -0
  77. package/dist/src/eta/cache.js +56 -0
  78. package/dist/src/eta/cache.js.map +1 -0
  79. package/dist/src/eta/controller.d.ts +608 -0
  80. package/dist/src/eta/controller.js +1036 -0
  81. package/dist/src/eta/controller.js.map +1 -0
  82. package/dist/src/eta/factory.d.ts +74 -0
  83. package/dist/src/eta/factory.js +121 -0
  84. package/dist/src/eta/factory.js.map +1 -0
  85. package/dist/src/eta/idle.d.ts +39 -0
  86. package/dist/src/eta/idle.js +52 -0
  87. package/dist/src/eta/idle.js.map +1 -0
  88. package/dist/src/eta/index.d.ts +52 -0
  89. package/dist/src/eta/index.js +13 -0
  90. package/dist/src/eta/index.js.map +1 -0
  91. package/dist/src/eta/observer.d.ts +263 -0
  92. package/dist/src/eta/observer.js +263 -0
  93. package/dist/src/eta/observer.js.map +1 -0
  94. package/dist/src/eta/operations.d.ts +41 -0
  95. package/dist/src/eta/operations.js +59 -0
  96. package/dist/src/eta/operations.js.map +1 -0
  97. package/dist/src/eta/source-registry.d.ts +198 -0
  98. package/dist/src/eta/source-registry.js +267 -0
  99. package/dist/src/eta/source-registry.js.map +1 -0
  100. package/dist/src/eta/template-index.d.ts +285 -0
  101. package/dist/src/eta/template-index.js +642 -0
  102. package/dist/src/eta/template-index.js.map +1 -0
  103. package/dist/src/eta/types.d.ts +529 -0
  104. package/dist/src/eta/types.js +2 -0
  105. package/dist/src/eta/types.js.map +1 -0
  106. package/dist/src/index.d.ts +21 -9
  107. package/dist/src/index.js +22 -11
  108. package/dist/src/index.js.map +1 -1
  109. package/dist/src/nodes/node.context.d.ts +5 -3
  110. package/dist/src/nodes/node.context.js +48 -3
  111. package/dist/src/nodes/node.context.js.map +1 -1
  112. package/dist/src/nodes/node.option.d.ts +11 -31
  113. package/dist/src/nodes/node.option.js +158 -222
  114. package/dist/src/nodes/node.option.js.map +1 -1
  115. package/dist/src/nodes/node.product.d.ts +7 -11
  116. package/dist/src/nodes/node.product.js +85 -64
  117. package/dist/src/nodes/node.product.js.map +1 -1
  118. package/dist/src/renders/attributes/buyLink.d.ts +2 -2
  119. package/dist/src/renders/attributes/buyLink.js +14 -11
  120. package/dist/src/renders/attributes/buyLink.js.map +1 -1
  121. package/dist/src/renders/attributes/devices.d.ts +2 -2
  122. package/dist/src/renders/attributes/devices.js +9 -19
  123. package/dist/src/renders/attributes/devices.js.map +1 -1
  124. package/dist/src/renders/attributes/discount.d.ts +2 -2
  125. package/dist/src/renders/attributes/discount.js +56 -44
  126. package/dist/src/renders/attributes/discount.js.map +1 -1
  127. package/dist/src/renders/attributes/hide.d.ts +2 -2
  128. package/dist/src/renders/attributes/hide.js +49 -14
  129. package/dist/src/renders/attributes/hide.js.map +1 -1
  130. package/dist/src/renders/attributes/index.d.ts +3 -3
  131. package/dist/src/renders/attributes/index.js +2 -4
  132. package/dist/src/renders/attributes/index.js.map +1 -1
  133. package/dist/src/renders/attributes/price.d.ts +2 -2
  134. package/dist/src/renders/attributes/price.js +30 -44
  135. package/dist/src/renders/attributes/price.js.map +1 -1
  136. package/dist/src/renders/attributes/subscription.d.ts +2 -2
  137. package/dist/src/renders/attributes/subscription.js +14 -20
  138. package/dist/src/renders/attributes/subscription.js.map +1 -1
  139. package/dist/src/renders/attributes/utilty.d.ts +2 -0
  140. package/dist/src/renders/attributes/utilty.js +17 -4
  141. package/dist/src/renders/attributes/utilty.js.map +1 -1
  142. package/dist/src/renders/context.d.ts +122 -10
  143. package/dist/src/renders/context.js +140 -80
  144. package/dist/src/renders/context.js.map +1 -1
  145. package/dist/src/renders/format.js +2 -2
  146. package/dist/src/renders/format.js.map +1 -1
  147. package/dist/src/renders/index.d.ts +6 -2
  148. package/dist/src/renders/index.js +67 -29
  149. package/dist/src/renders/index.js.map +1 -1
  150. package/dist/src/renders/observe.d.ts +1 -4
  151. package/dist/src/renders/observe.js +10 -24
  152. package/dist/src/renders/observe.js.map +1 -1
  153. package/dist/src/renders/render.wrapper.d.ts +37 -0
  154. package/dist/src/renders/render.wrapper.js +147 -0
  155. package/dist/src/renders/render.wrapper.js.map +1 -0
  156. package/dist/src/templating/eta.js +1 -4
  157. package/dist/src/templating/eta.js.map +1 -1
  158. package/package.json +4 -5
  159. package/dist/src/actions/action.button.d.ts +0 -2
  160. package/dist/src/actions/action.button.js +0 -8
  161. package/dist/src/actions/action.button.js.map +0 -1
  162. package/dist/src/actions/action.input.d.ts +0 -2
  163. package/dist/src/actions/action.input.js +0 -43
  164. package/dist/src/actions/action.input.js.map +0 -1
  165. package/dist/src/actions/action.select.d.ts +0 -2
  166. package/dist/src/actions/action.select.js +0 -14
  167. package/dist/src/actions/action.select.js.map +0 -1
  168. package/dist/src/actions/utilty.d.ts +0 -16
  169. package/dist/src/actions/utilty.js +0 -149
  170. package/dist/src/actions/utilty.js.map +0 -1
  171. package/dist/src/contexts/context.datalayer.d.ts +0 -9
  172. package/dist/src/contexts/context.datalayer.js +0 -3
  173. package/dist/src/contexts/context.datalayer.js.map +0 -1
  174. package/dist/src/contexts/context.derived.d.ts +0 -12
  175. package/dist/src/contexts/context.derived.js +0 -3
  176. package/dist/src/contexts/context.derived.js.map +0 -1
  177. package/dist/src/contexts/context.option.d.ts +0 -5
  178. package/dist/src/contexts/context.option.js +0 -3
  179. package/dist/src/contexts/context.option.js.map +0 -1
  180. package/dist/src/contexts/context.product.d.ts +0 -5
  181. package/dist/src/contexts/context.product.js +0 -3
  182. package/dist/src/contexts/context.product.js.map +0 -1
  183. package/dist/src/contexts/context.state.d.ts +0 -28
  184. package/dist/src/contexts/context.state.js +0 -3
  185. package/dist/src/contexts/context.state.js.map +0 -1
  186. package/dist/src/contexts/context.store.d.ts +0 -5
  187. package/dist/src/contexts/context.store.js +0 -3
  188. package/dist/src/contexts/context.store.js.map +0 -1
  189. package/dist/src/controllers/collect-controller.d.ts +0 -39
  190. package/dist/src/controllers/collect-controller.js +0 -161
  191. package/dist/src/controllers/collect-controller.js.map +0 -1
  192. package/dist/src/controllers/compute-controller.d.ts +0 -55
  193. package/dist/src/controllers/compute-controller.js +0 -350
  194. package/dist/src/controllers/compute-controller.js.map +0 -1
  195. package/dist/src/controllers/eta-renderer/eta-dom-renderer.d.ts +0 -25
  196. package/dist/src/controllers/eta-renderer/eta-dom-renderer.js +0 -192
  197. package/dist/src/controllers/eta-renderer/eta-dom-renderer.js.map +0 -1
  198. package/dist/src/controllers/eta-renderer/eta-mutation-observer.d.ts +0 -28
  199. package/dist/src/controllers/eta-renderer/eta-mutation-observer.js +0 -206
  200. package/dist/src/controllers/eta-renderer/eta-mutation-observer.js.map +0 -1
  201. package/dist/src/controllers/eta-renderer/eta-renderer.d.ts +0 -21
  202. package/dist/src/controllers/eta-renderer/eta-renderer.js +0 -77
  203. package/dist/src/controllers/eta-renderer/eta-renderer.js.map +0 -1
  204. package/dist/src/controllers/eta-renderer/eta-template-cache.d.ts +0 -24
  205. package/dist/src/controllers/eta-renderer/eta-template-cache.js +0 -61
  206. package/dist/src/controllers/eta-renderer/eta-template-cache.js.map +0 -1
  207. package/dist/src/controllers/eta-renderer/eta-types.d.ts +0 -11
  208. package/dist/src/controllers/eta-renderer/eta-types.js +0 -1
  209. package/dist/src/controllers/eta-renderer/eta-types.js.map +0 -1
  210. package/dist/src/controllers/eta-renderer/index.d.ts +0 -2
  211. package/dist/src/controllers/eta-renderer/index.js +0 -2
  212. package/dist/src/controllers/eta-renderer/index.js.map +0 -1
  213. package/dist/src/controllers/event-pipeline-controller.d.ts +0 -28
  214. package/dist/src/controllers/event-pipeline-controller.js +0 -72
  215. package/dist/src/controllers/event-pipeline-controller.js.map +0 -1
  216. package/dist/src/events/events.d.ts +0 -75
  217. package/dist/src/events/events.js +0 -78
  218. package/dist/src/events/events.js.map +0 -1
  219. package/dist/src/nodes/node.root.d.ts +0 -16
  220. package/dist/src/nodes/node.root.js +0 -49
  221. package/dist/src/nodes/node.root.js.map +0 -1
  222. package/dist/src/nodes/node.state.d.ts +0 -88
  223. package/dist/src/nodes/node.state.js +0 -330
  224. package/dist/src/nodes/node.state.js.map +0 -1
  225. package/dist/src/renders/utility.d.ts +0 -32
  226. package/dist/src/renders/utility.js +0 -191
  227. package/dist/src/renders/utility.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/actions/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActionNode, cleanupActionNode, DATA_ACTION_NODE, handleActionNode } from \"./utilty\";\n\nconst SHADOW_HOST_SELECTOR = '[shadow]';\n\nconst isActionNode = (node: HTMLElement): node is ActionNode => node.matches(DATA_ACTION_NODE);\n\nconst forEachActionNode = (root: HTMLElement | ShadowRoot, fn: (node: ActionNode) => void) => {\n if (root instanceof HTMLElement && isActionNode(root)) {\n fn(root);\n }\n\n root\n .querySelectorAll<ActionNode>(DATA_ACTION_NODE)\n .forEach(fn);\n};\n\nconst forEachShadowRoot = (root: HTMLElement | ShadowRoot, fn: (shadowRoot: ShadowRoot) => void) => {\n if (root instanceof HTMLElement && root.hasAttribute('shadow') && root.shadowRoot) {\n fn(root.shadowRoot);\n }\n\n root\n .querySelectorAll<HTMLElement>(SHADOW_HOST_SELECTOR)\n .forEach((host) => host.shadowRoot && fn(host.shadowRoot));\n};\n\nconst registerActionNodes = (root: HTMLElement | ShadowRoot) => {\n const observed = new WeakSet<Node>();\n const observers = new WeakMap<Node, MutationObserver>();\n const activeObservers = new Set<MutationObserver>();\n\n const disconnect = (node: Node) => {\n const observer = observers.get(node);\n if (!observer) return;\n observer.disconnect();\n observers.delete(node);\n activeObservers.delete(observer);\n };\n\n const cleanupShadowTree = (shadowRoot: ShadowRoot) => {\n forEachActionNode(shadowRoot, cleanupActionNode);\n forEachShadowRoot(shadowRoot, cleanupShadowTree);\n disconnect(shadowRoot);\n };\n\n const handleAdded = (node: HTMLElement) => {\n forEachActionNode(node, handleActionNode);\n forEachShadowRoot(node, watch);\n };\n\n const handleRemoved = (node: HTMLElement) => {\n forEachActionNode(node, cleanupActionNode);\n forEachShadowRoot(node, cleanupShadowTree);\n };\n\n const watch = (node: HTMLElement | ShadowRoot) => {\n if (observed.has(node)) return;\n observed.add(node);\n\n forEachActionNode(node, handleActionNode);\n forEachShadowRoot(node, watch);\n\n const mo = new MutationObserver(muts => {\n for (const m of muts) {\n for (const added of Array.from(m.addedNodes)) {\n if (!(added instanceof HTMLElement)) continue;\n handleAdded(added);\n }\n for (const removed of Array.from(m.removedNodes)) {\n if (!(removed instanceof HTMLElement)) continue;\n handleRemoved(removed);\n }\n }\n });\n\n mo.observe(node, {\n childList: true,\n subtree : true\n });\n observers.set(node, mo);\n activeObservers.add(mo);\n };\n\n watch(root);\n return () => {\n for (const observer of activeObservers) {\n observer.disconnect();\n }\n activeObservers.clear();\n };\n}\n\nexport { registerActionNodes };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC","sourcesContent":["export * from './action.adapter.js';\nexport * from './action.attributes.js';\nexport * from './action.registry.js';\n\n"]}
@@ -0,0 +1,70 @@
1
+ import type { BdActionCommand } from '../core/actions.js';
2
+ import type { Product, Store } from '@repobit/dex-store';
3
+ import { type BdActionDomain, type BdSeedDeclaration } from './compute.scan.js';
4
+ import { type BdOptionScenario } from './option-resolver.js';
5
+ /**
6
+ * Min/max reduction over every option reachable from a compute boundary.
7
+ */
8
+ export type BdComputeValue = Readonly<{
9
+ value: number;
10
+ fmt: string;
11
+ }>;
12
+ export type BdComputeRange = Readonly<{
13
+ min: BdComputeValue;
14
+ max: BdComputeValue;
15
+ }>;
16
+ export type BdComputeMonthlyRange = Readonly<BdComputeRange & {
17
+ monthly: BdComputeRange;
18
+ }>;
19
+ export type BdComputeDiscountRange = Readonly<BdComputeMonthlyRange & {
20
+ percentage: BdComputeMonthlyRange;
21
+ }>;
22
+ export type BdComputeResult = Readonly<{
23
+ min: number;
24
+ max: number;
25
+ minFormatted: string;
26
+ maxFormatted: string;
27
+ scenarios: number;
28
+ price: BdComputeMonthlyRange;
29
+ discountedPrice: BdComputeMonthlyRange;
30
+ discount: BdComputeDiscountRange;
31
+ }>;
32
+ /**
33
+ * Resolve declarative seeds into concrete product/option scenarios. Seeds that
34
+ * cannot be resolved (unknown product, missing option) are dropped.
35
+ */
36
+ export declare function resolveSeedScenarios(store: Store, seeds: readonly BdSeedDeclaration[], signal?: AbortSignal): Promise<BdOptionScenario[]>;
37
+ /**
38
+ * Fixed-point expansion of the reachable option space.
39
+ *
40
+ * Starting from the seeds, every static command and every domain-expanded
41
+ * command is applied to every newly discovered scenario, until no new scenario
42
+ * appears. Domains expand against the scenario's own product, which is what
43
+ * makes a product-switching action correctly explore the new product's
44
+ * device/subscription dimensions rather than the originally rendered ones.
45
+ */
46
+ export declare function expandScenarios(params: Readonly<{
47
+ store?: Store;
48
+ seeds: readonly BdOptionScenario[];
49
+ staticCommands: readonly BdActionCommand[];
50
+ domains: readonly BdActionDomain[];
51
+ signal?: AbortSignal;
52
+ maxScenarios?: number;
53
+ }>): Promise<BdOptionScenario[]>;
54
+ /**
55
+ * Expand a domain into per-value set commands against a specific product. A
56
+ * devices domain becomes one useAsValue update per device value the product
57
+ * offers, and likewise for subscriptions.
58
+ */
59
+ export declare function commandsFromDomains(domains: readonly BdActionDomain[], product: Product): BdActionCommand[];
60
+ /**
61
+ * Reduce a set of scenarios to a min/max price range. Prices are read with the
62
+ * same ProductOption.getPrice() the runtime uses, so formatting and currency
63
+ * match exactly.
64
+ */
65
+ export declare function reducePriceRange(scenarios: readonly BdOptionScenario[]): BdComputeResult | undefined;
66
+ /**
67
+ * Convenience: a stable signature for a compute result so consumers can cheaply
68
+ * detect a meaningful change.
69
+ */
70
+ export declare function computeResultKey(result: BdComputeResult | undefined): string;
@@ -0,0 +1,319 @@
1
+ // src/compute/compute.expand.ts
2
+ import { resolveScenarioCommand, scenarioKey } from './option-resolver.js';
3
+ const DEFAULT_MAX_SCENARIOS = 10000;
4
+ /**
5
+ * Resolve declarative seeds into concrete product/option scenarios. Seeds that
6
+ * cannot be resolved (unknown product, missing option) are dropped.
7
+ */
8
+ export async function resolveSeedScenarios(store, seeds, signal) {
9
+ const resolved = await Promise.all(seeds.map(async (seed) => {
10
+ const product = await store.getProduct({
11
+ id: seed.productId,
12
+ campaign: seed.campaign
13
+ });
14
+ throwIfAborted(signal);
15
+ if (!product) {
16
+ return undefined;
17
+ }
18
+ const option = await product.getOption({
19
+ devices: seed.devices,
20
+ subscription: seed.subscription
21
+ });
22
+ if (!option) {
23
+ return undefined;
24
+ }
25
+ return {
26
+ product,
27
+ option
28
+ };
29
+ }));
30
+ return resolved.filter((scenario) => scenario !== undefined);
31
+ }
32
+ /**
33
+ * Fixed-point expansion of the reachable option space.
34
+ *
35
+ * Starting from the seeds, every static command and every domain-expanded
36
+ * command is applied to every newly discovered scenario, until no new scenario
37
+ * appears. Domains expand against the scenario's own product, which is what
38
+ * makes a product-switching action correctly explore the new product's
39
+ * device/subscription dimensions rather than the originally rendered ones.
40
+ */
41
+ export async function expandScenarios(params) {
42
+ const limit = params.maxScenarios ?? DEFAULT_MAX_SCENARIOS;
43
+ const visited = new Map();
44
+ let frontier = [];
45
+ for (const seed of params.seeds) {
46
+ const key = scenarioKey(seed);
47
+ if (!visited.has(key)) {
48
+ visited.set(key, seed);
49
+ frontier.push(seed);
50
+ }
51
+ }
52
+ while (frontier.length > 0 && visited.size < limit) {
53
+ const next = [];
54
+ for (const scenario of frontier) {
55
+ const commands = [
56
+ ...params.staticCommands,
57
+ ...commandsFromDomains(params.domains, scenario.product)
58
+ ];
59
+ for (const command of commands) {
60
+ throwIfAborted(params.signal);
61
+ const result = await resolveScenarioCommand({
62
+ store: params.store,
63
+ scenario,
64
+ command
65
+ });
66
+ if (!result) {
67
+ continue;
68
+ }
69
+ const key = scenarioKey(result);
70
+ if (visited.has(key)) {
71
+ continue;
72
+ }
73
+ visited.set(key, result);
74
+ next.push(result);
75
+ if (visited.size >= limit) {
76
+ break;
77
+ }
78
+ }
79
+ if (visited.size >= limit) {
80
+ break;
81
+ }
82
+ }
83
+ frontier = next;
84
+ }
85
+ return [...visited.values()];
86
+ }
87
+ /**
88
+ * Expand a domain into per-value set commands against a specific product. A
89
+ * devices domain becomes one useAsValue update per device value the product
90
+ * offers, and likewise for subscriptions.
91
+ */
92
+ export function commandsFromDomains(domains, product) {
93
+ const commands = [];
94
+ for (const domain of domains) {
95
+ const values = domain.type === 'devices'
96
+ ? product.getDevices().values
97
+ : product.getSubscriptions().values;
98
+ for (const value of values) {
99
+ if (!isValueInDomain(value, domain)) {
100
+ continue;
101
+ }
102
+ commands.push(domainValueCommand(domain.type, value));
103
+ }
104
+ }
105
+ return commands;
106
+ }
107
+ function isValueInDomain(value, domain) {
108
+ if (domain.min !== undefined &&
109
+ value < domain.min) {
110
+ return false;
111
+ }
112
+ if (domain.max !== undefined &&
113
+ value > domain.max) {
114
+ return false;
115
+ }
116
+ return true;
117
+ }
118
+ function domainValueCommand(type, value) {
119
+ return {
120
+ kind: 'update-by-delta',
121
+ update: {
122
+ type,
123
+ delta: value,
124
+ useAsValue: true
125
+ }
126
+ };
127
+ }
128
+ /**
129
+ * Reduce a set of scenarios to a min/max price range. Prices are read with the
130
+ * same ProductOption.getPrice() the runtime uses, so formatting and currency
131
+ * match exactly.
132
+ */
133
+ export function reducePriceRange(scenarios) {
134
+ const price = createMonthlyAccumulator();
135
+ const discountedPrice = createMonthlyAccumulator();
136
+ const discount = createDiscountAccumulator();
137
+ for (const scenario of scenarios) {
138
+ collectOptionCompute(scenario.option, {
139
+ price,
140
+ discountedPrice,
141
+ discount
142
+ });
143
+ }
144
+ if (!price.base.hasValue) {
145
+ return undefined;
146
+ }
147
+ const priceRange = finalizeMonthlyAccumulator(price);
148
+ return {
149
+ min: priceRange.min.value,
150
+ max: priceRange.max.value,
151
+ minFormatted: priceRange.min.fmt,
152
+ maxFormatted: priceRange.max.fmt,
153
+ scenarios: scenarios.length,
154
+ price: priceRange,
155
+ discountedPrice: finalizeMonthlyAccumulator(discountedPrice),
156
+ discount: finalizeDiscountAccumulator(discount)
157
+ };
158
+ }
159
+ /**
160
+ * Convenience: a stable signature for a compute result so consumers can cheaply
161
+ * detect a meaningful change.
162
+ */
163
+ export function computeResultKey(result) {
164
+ if (!result) {
165
+ return '';
166
+ }
167
+ return [
168
+ result.scenarios,
169
+ rangeKey(result.price),
170
+ rangeKey(result.price.monthly),
171
+ rangeKey(result.discountedPrice),
172
+ rangeKey(result.discountedPrice.monthly),
173
+ rangeKey(result.discount),
174
+ rangeKey(result.discount.monthly),
175
+ rangeKey(result.discount.percentage),
176
+ rangeKey(result.discount.percentage.monthly)
177
+ ].join('|');
178
+ }
179
+ function collectOptionCompute(option, buckets) {
180
+ collectMetric(buckets.price.base, readPriceMetric(option));
181
+ collectMetric(buckets.price.monthly, readPriceMetric(option, { monthly: true }));
182
+ collectMetric(buckets.discountedPrice.base, readOptionalPriceMetric(option, 'getDiscountedPrice'));
183
+ collectMetric(buckets.discountedPrice.monthly, readOptionalPriceMetric(option, 'getDiscountedPrice', { monthly: true }));
184
+ collectMetric(buckets.discount.base, readOptionalPriceMetric(option, 'getDiscount', {
185
+ percentage: false,
186
+ symbol: false
187
+ }, {
188
+ percentage: false
189
+ }));
190
+ collectMetric(buckets.discount.monthly, readOptionalPriceMetric(option, 'getDiscount', {
191
+ percentage: false,
192
+ monthly: true,
193
+ symbol: false
194
+ }, {
195
+ percentage: false,
196
+ monthly: true
197
+ }));
198
+ collectMetric(buckets.discount.percentage.base, readOptionalPriceMetric(option, 'getDiscount', {
199
+ percentage: true,
200
+ symbol: false
201
+ }, {
202
+ percentage: true
203
+ }));
204
+ collectMetric(buckets.discount.percentage.monthly, readOptionalPriceMetric(option, 'getDiscount', {
205
+ percentage: true,
206
+ monthly: true,
207
+ symbol: false
208
+ }, {
209
+ percentage: true,
210
+ monthly: true
211
+ }));
212
+ }
213
+ function readPriceMetric(option, params = {}) {
214
+ return {
215
+ value: option.getPrice({
216
+ ...params,
217
+ currency: false
218
+ }),
219
+ fmt: String(option.getPrice({
220
+ ...params,
221
+ currency: true
222
+ }))
223
+ };
224
+ }
225
+ function readOptionalPriceMetric(option, method, numericParams = {}, formattedParams = numericParams) {
226
+ const withOptional = option;
227
+ const fn = withOptional[method];
228
+ if (!fn) {
229
+ return undefined;
230
+ }
231
+ const value = fn.call(option, {
232
+ ...numericParams,
233
+ currency: false
234
+ });
235
+ if (typeof value !== 'number' ||
236
+ !Number.isFinite(value)) {
237
+ return undefined;
238
+ }
239
+ const fmt = fn.call(option, formattedParams);
240
+ return {
241
+ value,
242
+ fmt: fmt === undefined ? '' : String(fmt)
243
+ };
244
+ }
245
+ function collectMetric(accumulator, metric) {
246
+ if (!metric) {
247
+ return;
248
+ }
249
+ if (!accumulator.hasValue ||
250
+ metric.value < accumulator.min.value) {
251
+ accumulator.min = metric;
252
+ }
253
+ if (!accumulator.hasValue ||
254
+ metric.value > accumulator.max.value) {
255
+ accumulator.max = metric;
256
+ }
257
+ accumulator.hasValue = true;
258
+ }
259
+ function createAccumulator() {
260
+ return {
261
+ min: emptyComputeValue(Number.MAX_SAFE_INTEGER),
262
+ max: emptyComputeValue(Number.MIN_SAFE_INTEGER),
263
+ hasValue: false
264
+ };
265
+ }
266
+ function createMonthlyAccumulator() {
267
+ return {
268
+ base: createAccumulator(),
269
+ monthly: createAccumulator()
270
+ };
271
+ }
272
+ function createDiscountAccumulator() {
273
+ return {
274
+ ...createMonthlyAccumulator(),
275
+ percentage: createMonthlyAccumulator()
276
+ };
277
+ }
278
+ function finalizeAccumulator(accumulator) {
279
+ return {
280
+ min: accumulator.hasValue
281
+ ? accumulator.min
282
+ : emptyComputeValue(Number.MAX_SAFE_INTEGER),
283
+ max: accumulator.hasValue
284
+ ? accumulator.max
285
+ : emptyComputeValue(Number.MIN_SAFE_INTEGER)
286
+ };
287
+ }
288
+ function finalizeMonthlyAccumulator(accumulator) {
289
+ return {
290
+ ...finalizeAccumulator(accumulator.base),
291
+ monthly: finalizeAccumulator(accumulator.monthly)
292
+ };
293
+ }
294
+ function finalizeDiscountAccumulator(accumulator) {
295
+ return {
296
+ ...finalizeMonthlyAccumulator(accumulator),
297
+ percentage: finalizeMonthlyAccumulator(accumulator.percentage)
298
+ };
299
+ }
300
+ function emptyComputeValue(value) {
301
+ return {
302
+ value,
303
+ fmt: ''
304
+ };
305
+ }
306
+ function rangeKey(range) {
307
+ return [
308
+ range.min.value,
309
+ range.min.fmt,
310
+ range.max.value,
311
+ range.max.fmt
312
+ ].join(':');
313
+ }
314
+ function throwIfAborted(signal) {
315
+ if (signal?.aborted) {
316
+ throw signal.reason ?? new DOMException('Aborted', 'AbortError');
317
+ }
318
+ }
319
+ //# sourceMappingURL=compute.expand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute.expand.js","sourceRoot":"","sources":["../../../src/compute/compute.expand.ts"],"names":[],"mappings":"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","sourcesContent":["// src/compute/compute.expand.ts\n\nimport type {\n BdActionCommand,\n UpdateByDelta\n} from '@/core/actions.js';\nimport type {\n Product,\n ProductOption,\n Store\n} from '@repobit/dex-store';\nimport {\n type BdActionDomain,\n type BdSeedDeclaration\n} from './compute.scan.js';\nimport {\n resolveScenarioCommand,\n scenarioKey,\n type BdOptionScenario\n} from './option-resolver.js';\n\n/**\n * Min/max reduction over every option reachable from a compute boundary.\n */\nexport type BdComputeValue = Readonly<{\n value: number;\n fmt : string;\n}>;\n\nexport type BdComputeRange = Readonly<{\n min: BdComputeValue;\n max: BdComputeValue;\n}>;\n\nexport type BdComputeMonthlyRange = Readonly<BdComputeRange & {\n monthly: BdComputeRange;\n}>;\n\nexport type BdComputeDiscountRange = Readonly<BdComputeMonthlyRange & {\n percentage: BdComputeMonthlyRange;\n}>;\n\nexport type BdComputeResult = Readonly<{\n min : number;\n max : number;\n minFormatted: string;\n maxFormatted: string;\n scenarios : number;\n\n price : BdComputeMonthlyRange;\n discountedPrice: BdComputeMonthlyRange;\n discount : BdComputeDiscountRange;\n}>;\n\nconst DEFAULT_MAX_SCENARIOS = 10000;\n\n/**\n * Resolve declarative seeds into concrete product/option scenarios. Seeds that\n * cannot be resolved (unknown product, missing option) are dropped.\n */\nexport async function resolveSeedScenarios(\n store: Store,\n seeds: readonly BdSeedDeclaration[],\n signal?: AbortSignal\n): Promise<BdOptionScenario[]> {\n const resolved = await Promise.all(\n seeds.map(async (seed) => {\n const product =\n await store.getProduct({\n id : seed.productId,\n campaign: seed.campaign\n });\n\n throwIfAborted(signal);\n\n if (!product) {\n return undefined;\n }\n\n const option =\n await product.getOption({\n devices : seed.devices,\n subscription: seed.subscription\n });\n\n if (!option) {\n return undefined;\n }\n\n return {\n product,\n option\n } satisfies BdOptionScenario;\n })\n );\n\n return resolved.filter(\n (scenario): scenario is BdOptionScenario => scenario !== undefined\n );\n}\n\n/**\n * Fixed-point expansion of the reachable option space.\n *\n * Starting from the seeds, every static command and every domain-expanded\n * command is applied to every newly discovered scenario, until no new scenario\n * appears. Domains expand against the scenario's own product, which is what\n * makes a product-switching action correctly explore the new product's\n * device/subscription dimensions rather than the originally rendered ones.\n */\nexport async function expandScenarios(\n params: Readonly<{\n store? : Store;\n seeds : readonly BdOptionScenario[];\n staticCommands: readonly BdActionCommand[];\n domains : readonly BdActionDomain[];\n signal? : AbortSignal;\n maxScenarios? : number;\n }>\n): Promise<BdOptionScenario[]> {\n const limit =\n params.maxScenarios ?? DEFAULT_MAX_SCENARIOS;\n\n const visited = new Map<string, BdOptionScenario>();\n let frontier: BdOptionScenario[] = [];\n\n for (const seed of params.seeds) {\n const key = scenarioKey(seed);\n\n if (!visited.has(key)) {\n visited.set(key, seed);\n frontier.push(seed);\n }\n }\n\n while (frontier.length > 0 && visited.size < limit) {\n const next: BdOptionScenario[] = [];\n\n for (const scenario of frontier) {\n const commands = [\n ...params.staticCommands,\n ...commandsFromDomains(params.domains, scenario.product)\n ];\n\n for (const command of commands) {\n throwIfAborted(params.signal);\n\n const result =\n await resolveScenarioCommand({\n store: params.store,\n scenario,\n command\n });\n\n if (!result) {\n continue;\n }\n\n const key = scenarioKey(result);\n\n if (visited.has(key)) {\n continue;\n }\n\n visited.set(key, result);\n next.push(result);\n\n if (visited.size >= limit) {\n break;\n }\n }\n\n if (visited.size >= limit) {\n break;\n }\n }\n\n frontier = next;\n }\n\n return [...visited.values()];\n}\n\n/**\n * Expand a domain into per-value set commands against a specific product. A\n * devices domain becomes one useAsValue update per device value the product\n * offers, and likewise for subscriptions.\n */\nexport function commandsFromDomains(\n domains: readonly BdActionDomain[],\n product: Product\n): BdActionCommand[] {\n const commands: BdActionCommand[] = [];\n\n for (const domain of domains) {\n const values =\n domain.type === 'devices'\n ? product.getDevices().values\n : product.getSubscriptions().values;\n\n for (const value of values) {\n if (!isValueInDomain(value, domain)) {\n continue;\n }\n\n commands.push(\n domainValueCommand(domain.type, value)\n );\n }\n }\n\n return commands;\n}\n\nfunction isValueInDomain(\n value: number,\n domain: BdActionDomain\n): boolean {\n if (\n domain.min !== undefined &&\n value < domain.min\n ) {\n return false;\n }\n\n if (\n domain.max !== undefined &&\n value > domain.max\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction domainValueCommand(\n type: UpdateByDelta['type'],\n value: number\n): BdActionCommand {\n return {\n kind : 'update-by-delta',\n update: {\n type,\n delta : value,\n useAsValue: true\n }\n };\n}\n\n/**\n * Reduce a set of scenarios to a min/max price range. Prices are read with the\n * same ProductOption.getPrice() the runtime uses, so formatting and currency\n * match exactly.\n */\nexport function reducePriceRange(\n scenarios: readonly BdOptionScenario[]\n): BdComputeResult | undefined {\n const price = createMonthlyAccumulator();\n const discountedPrice = createMonthlyAccumulator();\n const discount = createDiscountAccumulator();\n\n for (const scenario of scenarios) {\n collectOptionCompute(\n scenario.option,\n {\n price,\n discountedPrice,\n discount\n }\n );\n }\n\n if (!price.base.hasValue) {\n return undefined;\n }\n\n const priceRange = finalizeMonthlyAccumulator(price);\n\n return {\n min : priceRange.min.value,\n max : priceRange.max.value,\n minFormatted: priceRange.min.fmt,\n maxFormatted: priceRange.max.fmt,\n scenarios : scenarios.length,\n\n price: priceRange,\n discountedPrice:\n finalizeMonthlyAccumulator(discountedPrice),\n discount:\n finalizeDiscountAccumulator(discount)\n };\n}\n\n/**\n * Convenience: a stable signature for a compute result so consumers can cheaply\n * detect a meaningful change.\n */\nexport function computeResultKey(\n result: BdComputeResult | undefined\n): string {\n if (!result) {\n return '';\n }\n\n return [\n result.scenarios,\n rangeKey(result.price),\n rangeKey(result.price.monthly),\n rangeKey(result.discountedPrice),\n rangeKey(result.discountedPrice.monthly),\n rangeKey(result.discount),\n rangeKey(result.discount.monthly),\n rangeKey(result.discount.percentage),\n rangeKey(result.discount.percentage.monthly)\n ].join('|');\n}\n\ntype ComputeBuckets = {\n price : MonthlyAccumulator;\n discountedPrice: MonthlyAccumulator;\n discount : DiscountAccumulator;\n};\n\ntype Accumulator = {\n min : BdComputeValue;\n max : BdComputeValue;\n hasValue: boolean;\n};\n\ntype MonthlyAccumulator = {\n base : Accumulator;\n monthly: Accumulator;\n};\n\ntype DiscountAccumulator = MonthlyAccumulator & {\n percentage: MonthlyAccumulator;\n};\n\ntype OptionalPriceMethod =\n | 'getDiscountedPrice'\n | 'getDiscount';\n\ntype OptionWithOptionalPrices = ProductOption & Partial<Record<\n OptionalPriceMethod,\n (params?: Record<string, unknown>) => string | number | undefined\n>>;\n\nfunction collectOptionCompute(\n option: ProductOption,\n buckets: ComputeBuckets\n): void {\n collectMetric(\n buckets.price.base,\n readPriceMetric(option)\n );\n collectMetric(\n buckets.price.monthly,\n readPriceMetric(\n option,\n { monthly: true }\n )\n );\n\n collectMetric(\n buckets.discountedPrice.base,\n readOptionalPriceMetric(\n option,\n 'getDiscountedPrice'\n )\n );\n collectMetric(\n buckets.discountedPrice.monthly,\n readOptionalPriceMetric(\n option,\n 'getDiscountedPrice',\n { monthly: true }\n )\n );\n\n collectMetric(\n buckets.discount.base,\n readOptionalPriceMetric(\n option,\n 'getDiscount',\n {\n percentage: false,\n symbol : false\n },\n {\n percentage: false\n }\n )\n );\n collectMetric(\n buckets.discount.monthly,\n readOptionalPriceMetric(\n option,\n 'getDiscount',\n {\n percentage: false,\n monthly : true,\n symbol : false\n },\n {\n percentage: false,\n monthly : true\n }\n )\n );\n collectMetric(\n buckets.discount.percentage.base,\n readOptionalPriceMetric(\n option,\n 'getDiscount',\n {\n percentage: true,\n symbol : false\n },\n {\n percentage: true\n }\n )\n );\n collectMetric(\n buckets.discount.percentage.monthly,\n readOptionalPriceMetric(\n option,\n 'getDiscount',\n {\n percentage: true,\n monthly : true,\n symbol : false\n },\n {\n percentage: true,\n monthly : true\n }\n )\n );\n}\n\nfunction readPriceMetric(\n option: ProductOption,\n params: {\n monthly?: boolean;\n } = {}\n): BdComputeValue {\n return {\n value: option.getPrice({\n ...params,\n currency: false\n }),\n fmt: String(\n option.getPrice({\n ...params,\n currency: true\n })\n )\n };\n}\n\nfunction readOptionalPriceMetric(\n option: ProductOption,\n method: OptionalPriceMethod,\n numericParams: Record<string, unknown> = {},\n formattedParams: Record<string, unknown> = numericParams\n): BdComputeValue | undefined {\n const withOptional = option as OptionWithOptionalPrices;\n const fn = withOptional[method];\n\n if (!fn) {\n return undefined;\n }\n\n const value = fn.call(\n option,\n {\n ...numericParams,\n currency: false\n }\n );\n\n if (\n typeof value !== 'number' ||\n !Number.isFinite(value)\n ) {\n return undefined;\n }\n\n const fmt = fn.call(\n option,\n formattedParams\n );\n\n return {\n value,\n fmt: fmt === undefined ? '' : String(fmt)\n };\n}\n\nfunction collectMetric(\n accumulator: Accumulator,\n metric: BdComputeValue | undefined\n): void {\n if (!metric) {\n return;\n }\n\n if (\n !accumulator.hasValue ||\n metric.value < accumulator.min.value\n ) {\n accumulator.min = metric;\n }\n\n if (\n !accumulator.hasValue ||\n metric.value > accumulator.max.value\n ) {\n accumulator.max = metric;\n }\n\n accumulator.hasValue = true;\n}\n\nfunction createAccumulator(): Accumulator {\n return {\n min : emptyComputeValue(Number.MAX_SAFE_INTEGER),\n max : emptyComputeValue(Number.MIN_SAFE_INTEGER),\n hasValue: false\n };\n}\n\nfunction createMonthlyAccumulator(): MonthlyAccumulator {\n return {\n base : createAccumulator(),\n monthly: createAccumulator()\n };\n}\n\nfunction createDiscountAccumulator(): DiscountAccumulator {\n return {\n ...createMonthlyAccumulator(),\n percentage: createMonthlyAccumulator()\n };\n}\n\nfunction finalizeAccumulator(\n accumulator: Accumulator\n): BdComputeRange {\n return {\n min: accumulator.hasValue\n ? accumulator.min\n : emptyComputeValue(Number.MAX_SAFE_INTEGER),\n max: accumulator.hasValue\n ? accumulator.max\n : emptyComputeValue(Number.MIN_SAFE_INTEGER)\n };\n}\n\nfunction finalizeMonthlyAccumulator(\n accumulator: MonthlyAccumulator\n): BdComputeMonthlyRange {\n return {\n ...finalizeAccumulator(accumulator.base),\n monthly: finalizeAccumulator(accumulator.monthly)\n };\n}\n\nfunction finalizeDiscountAccumulator(\n accumulator: DiscountAccumulator\n): BdComputeDiscountRange {\n return {\n ...finalizeMonthlyAccumulator(accumulator),\n percentage:\n finalizeMonthlyAccumulator(accumulator.percentage)\n };\n}\n\nfunction emptyComputeValue(\n value: number\n): BdComputeValue {\n return {\n value,\n fmt: ''\n };\n}\n\nfunction rangeKey(\n range: BdComputeRange\n): string {\n return [\n range.min.value,\n range.min.fmt,\n range.max.value,\n range.max.fmt\n ].join(':');\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import type { BdActionCommand } from '../core/actions.js';
2
+ export declare const DATA_DEVICES_DOMAIN: "data-store-devices";
3
+ export declare const DATA_SUBSCRIPTION_DOMAIN: "data-store-subscription";
4
+ export type BdActionDomainKind = 'devices' | 'subscription';
5
+ export type BdActionDomain = Readonly<{
6
+ type: BdActionDomainKind;
7
+ min?: number;
8
+ max?: number;
9
+ }>;
10
+ /**
11
+ * A declarative seed scenario used by standalone compute helpers. Live node
12
+ * compute uses resolved ProductOption seeds collected from option nodes.
13
+ */
14
+ export type BdSeedDeclaration = Readonly<{
15
+ productId: string;
16
+ campaign?: string;
17
+ bundle: boolean;
18
+ devices: number;
19
+ subscription: number;
20
+ }>;
21
+ /**
22
+ * Result of scanning the action elements owned by a compute boundary.
23
+ *
24
+ * - staticCommands: concrete jumps (button says product-b, option says
25
+ * subscription=24, select option children, etc).
26
+ * - domains: controls that represent a devices / subscription space of
27
+ * whatever product a scenario lands on. Inputs may bound that space by min/max.
28
+ */
29
+ export type BdComputeScan = Readonly<{
30
+ staticCommands: readonly BdActionCommand[];
31
+ domains: readonly BdActionDomain[];
32
+ }>;
33
+ export declare const EMPTY_COMPUTE_SCAN: BdComputeScan;
34
+ /**
35
+ * Stable fingerprint of the declared seeds. Used as a compute task arg so the
36
+ * controller only reruns when the declared seed set actually changes, not on
37
+ * every host update.
38
+ */
39
+ export declare function seedDeclarationsKey(seeds: readonly BdSeedDeclaration[]): string;
40
+ /**
41
+ * Turn the action elements owned by a compute boundary into static commands and
42
+ * dynamic domains. Reuses the same parser as the runtime action layer; the only
43
+ * difference is breadth: a select contributes all of its option children here,
44
+ * not just the selected one.
45
+ */
46
+ export declare function scanActionElements(elements: Iterable<HTMLElement>): BdComputeScan;
47
+ export declare function collectActionElementCompute(element: HTMLElement): BdComputeScan;
48
+ export declare function mergeComputeScans(scans: Iterable<BdComputeScan>): BdComputeScan;
49
+ /**
50
+ * A select / control is a dynamic domain only when it is actionable AND tagged
51
+ * as a devices or subscription dimension. A plain `data-store-devices` span just
52
+ * renders the current value and contributes nothing to compute.
53
+ */
54
+ export declare function readComputeDomain(element: HTMLElement): BdActionDomain | undefined;
@@ -0,0 +1,160 @@
1
+ // src/compute/compute.scan.ts
2
+ import { isActionElement, readBdActionCommand, readInputValueCommand } from '../actions/action.attributes.js';
3
+ export const DATA_DEVICES_DOMAIN = 'data-store-devices';
4
+ export const DATA_SUBSCRIPTION_DOMAIN = 'data-store-subscription';
5
+ export const EMPTY_COMPUTE_SCAN = Object.freeze({
6
+ staticCommands: Object.freeze([]),
7
+ domains: Object.freeze([])
8
+ });
9
+ /**
10
+ * Stable fingerprint of the declared seeds. Used as a compute task arg so the
11
+ * controller only reruns when the declared seed set actually changes, not on
12
+ * every host update.
13
+ */
14
+ export function seedDeclarationsKey(seeds) {
15
+ return seeds
16
+ .map((seed) => [
17
+ seed.productId,
18
+ seed.campaign ?? '',
19
+ seed.bundle ? '1' : '0',
20
+ seed.devices,
21
+ seed.subscription
22
+ ].join('|'))
23
+ .sort()
24
+ .join(';');
25
+ }
26
+ /**
27
+ * Turn the action elements owned by a compute boundary into static commands and
28
+ * dynamic domains. Reuses the same parser as the runtime action layer; the only
29
+ * difference is breadth: a select contributes all of its option children here,
30
+ * not just the selected one.
31
+ */
32
+ export function scanActionElements(elements) {
33
+ const staticCommands = [];
34
+ const domains = [];
35
+ for (const element of elements) {
36
+ const scan = collectActionElementCompute(element);
37
+ staticCommands.push(...scan.staticCommands);
38
+ domains.push(...scan.domains);
39
+ }
40
+ return {
41
+ staticCommands,
42
+ domains
43
+ };
44
+ }
45
+ export function collectActionElementCompute(element) {
46
+ const domain = readComputeDomain(element);
47
+ if (domain) {
48
+ return {
49
+ staticCommands: [],
50
+ domains: [domain]
51
+ };
52
+ }
53
+ if (element instanceof HTMLSelectElement) {
54
+ const staticCommands = [];
55
+ collectSelectCommands(element, staticCommands);
56
+ return {
57
+ staticCommands,
58
+ domains: []
59
+ };
60
+ }
61
+ const command = element instanceof HTMLInputElement
62
+ ? readInputActionCommand(element)
63
+ : readBdActionCommand(element);
64
+ if (!command) {
65
+ return EMPTY_COMPUTE_SCAN;
66
+ }
67
+ return {
68
+ staticCommands: [command],
69
+ domains: []
70
+ };
71
+ }
72
+ export function mergeComputeScans(scans) {
73
+ const staticCommands = [];
74
+ const domains = [];
75
+ for (const scan of scans) {
76
+ staticCommands.push(...scan.staticCommands);
77
+ domains.push(...scan.domains);
78
+ }
79
+ return {
80
+ staticCommands,
81
+ domains
82
+ };
83
+ }
84
+ /**
85
+ * A select / control is a dynamic domain only when it is actionable AND tagged
86
+ * as a devices or subscription dimension. A plain `data-store-devices` span just
87
+ * renders the current value and contributes nothing to compute.
88
+ */
89
+ export function readComputeDomain(element) {
90
+ if (!isActionElement(element)) {
91
+ return undefined;
92
+ }
93
+ if (element instanceof HTMLInputElement &&
94
+ (element.type === 'number' ||
95
+ element.type === 'text')) {
96
+ return readInputComputeDomain(element);
97
+ }
98
+ if (element.hasAttribute(DATA_DEVICES_DOMAIN)) {
99
+ return {
100
+ type: 'devices'
101
+ };
102
+ }
103
+ if (element.hasAttribute(DATA_SUBSCRIPTION_DOMAIN)) {
104
+ return {
105
+ type: 'subscription'
106
+ };
107
+ }
108
+ return undefined;
109
+ }
110
+ function readInputComputeDomain(input) {
111
+ const command = readBdActionCommand(input);
112
+ if (command?.kind !== 'update-by-delta') {
113
+ return undefined;
114
+ }
115
+ return {
116
+ type: command.update.type,
117
+ min: toOptionalNumber(input.min) ?? command.update.min,
118
+ max: toOptionalNumber(input.max) ?? command.update.max
119
+ };
120
+ }
121
+ function readInputActionCommand(input) {
122
+ switch (input.type) {
123
+ case 'number':
124
+ case 'text':
125
+ return readInputValueCommand(input);
126
+ default:
127
+ return readBdActionCommand(input);
128
+ }
129
+ }
130
+ function collectSelectCommands(select, out) {
131
+ let collected = false;
132
+ for (const option of Array.from(select.options)) {
133
+ const command = readBdActionCommand(option);
134
+ if (command) {
135
+ out.push(command);
136
+ collected = true;
137
+ }
138
+ }
139
+ if (collected) {
140
+ return;
141
+ }
142
+ const command = readBdActionCommand(select);
143
+ if (command?.kind === 'set-action') {
144
+ out.push(command);
145
+ }
146
+ }
147
+ function toOptionalNumber(value) {
148
+ if (value === undefined) {
149
+ return undefined;
150
+ }
151
+ const trimmed = value.trim();
152
+ if (!trimmed) {
153
+ return undefined;
154
+ }
155
+ const parsed = Number(trimmed);
156
+ return Number.isNaN(parsed)
157
+ ? undefined
158
+ : parsed;
159
+ }
160
+ //# sourceMappingURL=compute.scan.js.map