@repobit/dex-store-elements 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +211 -0
  3. package/dist/src/actions/action.button.d.ts +1 -0
  4. package/dist/src/actions/action.button.js +14 -0
  5. package/dist/src/actions/action.button.js.map +1 -0
  6. package/dist/src/actions/action.input.d.ts +1 -0
  7. package/dist/src/actions/action.input.js +37 -0
  8. package/dist/src/actions/action.input.js.map +1 -0
  9. package/dist/src/actions/action.select.d.ts +1 -0
  10. package/dist/src/actions/action.select.js +12 -0
  11. package/dist/src/actions/action.select.js.map +1 -0
  12. package/dist/src/actions/index.d.ts +2 -0
  13. package/dist/src/actions/index.js +30 -0
  14. package/dist/src/actions/index.js.map +1 -0
  15. package/dist/src/actions/utilty.d.ts +9 -0
  16. package/dist/src/actions/utilty.js +88 -0
  17. package/dist/src/actions/utilty.js.map +1 -0
  18. package/dist/src/contexts/context.datalayer.d.ts +9 -0
  19. package/dist/src/contexts/context.datalayer.js +3 -0
  20. package/dist/src/contexts/context.datalayer.js.map +1 -0
  21. package/dist/src/contexts/context.derived.d.ts +12 -0
  22. package/dist/src/contexts/context.derived.js +3 -0
  23. package/dist/src/contexts/context.derived.js.map +1 -0
  24. package/dist/src/contexts/context.event.d.ts +5 -0
  25. package/dist/src/contexts/context.event.js +3 -0
  26. package/dist/src/contexts/context.event.js.map +1 -0
  27. package/dist/src/contexts/context.option.d.ts +5 -0
  28. package/dist/src/contexts/context.option.js +3 -0
  29. package/dist/src/contexts/context.option.js.map +1 -0
  30. package/dist/src/contexts/context.product.d.ts +5 -0
  31. package/dist/src/contexts/context.product.js +3 -0
  32. package/dist/src/contexts/context.product.js.map +1 -0
  33. package/dist/src/contexts/context.state.d.ts +28 -0
  34. package/dist/src/contexts/context.state.js +3 -0
  35. package/dist/src/contexts/context.state.js.map +1 -0
  36. package/dist/src/contexts/context.store.d.ts +5 -0
  37. package/dist/src/contexts/context.store.js +3 -0
  38. package/dist/src/contexts/context.store.js.map +1 -0
  39. package/dist/src/dsl/compilers/array/compiler.d.ts +1 -0
  40. package/dist/src/dsl/compilers/array/compiler.js +30 -0
  41. package/dist/src/dsl/compilers/array/compiler.js.map +1 -0
  42. package/dist/src/dsl/compilers/boolean/compiler.d.ts +7 -0
  43. package/dist/src/dsl/compilers/boolean/compiler.js +53 -0
  44. package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -0
  45. package/dist/src/dsl/compilers/enum/compiler.d.ts +7 -0
  46. package/dist/src/dsl/compilers/enum/compiler.js +16 -0
  47. package/dist/src/dsl/compilers/enum/compiler.js.map +1 -0
  48. package/dist/src/dsl/compilers/index.d.ts +12 -0
  49. package/dist/src/dsl/compilers/index.js +9 -0
  50. package/dist/src/dsl/compilers/index.js.map +1 -0
  51. package/dist/src/dsl/utilty.d.ts +6 -0
  52. package/dist/src/dsl/utilty.js +32 -0
  53. package/dist/src/dsl/utilty.js.map +1 -0
  54. package/dist/src/events/events.d.ts +60 -0
  55. package/dist/src/events/events.js +56 -0
  56. package/dist/src/events/events.js.map +1 -0
  57. package/dist/src/index.d.ts +9 -0
  58. package/dist/src/index.js +10 -0
  59. package/dist/src/index.js.map +1 -0
  60. package/dist/src/nodes/node.context.d.ts +3 -0
  61. package/dist/src/nodes/node.context.js +15 -0
  62. package/dist/src/nodes/node.context.js.map +1 -0
  63. package/dist/src/nodes/node.option.d.ts +32 -0
  64. package/dist/src/nodes/node.option.js +247 -0
  65. package/dist/src/nodes/node.option.js.map +1 -0
  66. package/dist/src/nodes/node.product.d.ts +14 -0
  67. package/dist/src/nodes/node.product.js +99 -0
  68. package/dist/src/nodes/node.product.js.map +1 -0
  69. package/dist/src/nodes/node.root.d.ts +16 -0
  70. package/dist/src/nodes/node.root.js +53 -0
  71. package/dist/src/nodes/node.root.js.map +1 -0
  72. package/dist/src/nodes/node.state.d.ts +112 -0
  73. package/dist/src/nodes/node.state.js +742 -0
  74. package/dist/src/nodes/node.state.js.map +1 -0
  75. package/dist/src/renders/attributes/buyLink.d.ts +2 -0
  76. package/dist/src/renders/attributes/buyLink.js +18 -0
  77. package/dist/src/renders/attributes/buyLink.js.map +1 -0
  78. package/dist/src/renders/attributes/devices.d.ts +2 -0
  79. package/dist/src/renders/attributes/devices.js +32 -0
  80. package/dist/src/renders/attributes/devices.js.map +1 -0
  81. package/dist/src/renders/attributes/discount.d.ts +2 -0
  82. package/dist/src/renders/attributes/discount.js +96 -0
  83. package/dist/src/renders/attributes/discount.js.map +1 -0
  84. package/dist/src/renders/attributes/hide.d.ts +2 -0
  85. package/dist/src/renders/attributes/hide.js +33 -0
  86. package/dist/src/renders/attributes/hide.js.map +1 -0
  87. package/dist/src/renders/attributes/index.d.ts +3 -0
  88. package/dist/src/renders/attributes/index.js +24 -0
  89. package/dist/src/renders/attributes/index.js.map +1 -0
  90. package/dist/src/renders/attributes/price.d.ts +2 -0
  91. package/dist/src/renders/attributes/price.js +96 -0
  92. package/dist/src/renders/attributes/price.js.map +1 -0
  93. package/dist/src/renders/attributes/subscription.d.ts +2 -0
  94. package/dist/src/renders/attributes/subscription.js +34 -0
  95. package/dist/src/renders/attributes/subscription.js.map +1 -0
  96. package/dist/src/renders/attributes/trialLink.d.ts +2 -0
  97. package/dist/src/renders/attributes/trialLink.js +13 -0
  98. package/dist/src/renders/attributes/trialLink.js.map +1 -0
  99. package/dist/src/renders/context.d.ts +11 -0
  100. package/dist/src/renders/context.js +88 -0
  101. package/dist/src/renders/context.js.map +1 -0
  102. package/dist/src/renders/format.d.ts +2 -0
  103. package/dist/src/renders/format.js +21 -0
  104. package/dist/src/renders/format.js.map +1 -0
  105. package/dist/src/renders/index.d.ts +2 -0
  106. package/dist/src/renders/index.js +30 -0
  107. package/dist/src/renders/index.js.map +1 -0
  108. package/dist/src/renders/observe.d.ts +4 -0
  109. package/dist/src/renders/observe.js +30 -0
  110. package/dist/src/renders/observe.js.map +1 -0
  111. package/dist/src/renders/utility.d.ts +28 -0
  112. package/dist/src/renders/utility.js +132 -0
  113. package/dist/src/renders/utility.js.map +1 -0
  114. package/dist/src/templating/eta.d.ts +3 -0
  115. package/dist/src/templating/eta.js +9 -0
  116. package/dist/src/templating/eta.js.map +1 -0
  117. package/package.json +64 -0
@@ -0,0 +1,742 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var StateNode_1;
8
+ import { derivedContext } from "../contexts/context.derived.js";
9
+ import { eventContext } from "../contexts/context.event.js";
10
+ import { stateContext } from "../contexts/context.state.js";
11
+ import { storeContext } from "../contexts/context.store.js";
12
+ import { ActionEvent, CollectActionEvent, CollectOptionEvent, CollectUpdateByDeltaEvent, UpdateByDeltaEvent } from "../events/events.js";
13
+ import { toDSLContext } from "../renders/context.js";
14
+ import eta from "../templating/eta.js";
15
+ import { consume, provide } from "@lit/context";
16
+ import { Task } from "@lit/task";
17
+ import { LitElement } from "lit";
18
+ import { customElement, property } from "lit/decorators.js";
19
+ import morph from 'nanomorph';
20
+ if (!window.Promise.withResolvers) {
21
+ window.Promise.withResolvers = function () {
22
+ let resolve, reject;
23
+ const promise = new Promise((res, rej) => {
24
+ resolve = res;
25
+ reject = rej;
26
+ });
27
+ return { resolve, reject, promise };
28
+ };
29
+ }
30
+ let StateNode = StateNode_1 = class StateNode extends LitElement {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.autoForward = true;
34
+ this.noCollect = false;
35
+ this.storeName = Symbol("bd-state");
36
+ // If true, ignore events coming from parent context; only react to
37
+ // events originating inside this subtree (DOM-bubbled via listeners).
38
+ this.ignoreEventsParent = false;
39
+ /**
40
+ * all options as seens by this node
41
+ * options + partialOptions
42
+ */
43
+ this._options = new Map();
44
+ /**
45
+ * all actions as seens by this node
46
+ */
47
+ this._actions = new Map();
48
+ /**
49
+ * all delta updates as seens by this node
50
+ * changes that affect only devices and subscription
51
+ */
52
+ this._deltaUpdates = new Map();
53
+ /**
54
+ * all product changes as seens by this node
55
+ */
56
+ this._partialOptions = new Map();
57
+ /**
58
+ * all product changes as seens by this node
59
+ */
60
+ this._partialBundleOptions = new Map();
61
+ /**
62
+ * Local state
63
+ */
64
+ this.state = {
65
+ price: {
66
+ min: {
67
+ value: Number.MAX_SAFE_INTEGER,
68
+ fmt: ""
69
+ },
70
+ max: {
71
+ value: Number.MIN_SAFE_INTEGER,
72
+ fmt: ""
73
+ },
74
+ monthly: {
75
+ min: {
76
+ value: Number.MAX_SAFE_INTEGER,
77
+ fmt: ""
78
+ },
79
+ max: {
80
+ value: Number.MIN_SAFE_INTEGER,
81
+ fmt: ""
82
+ }
83
+ }
84
+ },
85
+ discountedPrice: {
86
+ min: {
87
+ value: Number.MAX_SAFE_INTEGER,
88
+ fmt: ""
89
+ },
90
+ max: {
91
+ value: Number.MIN_SAFE_INTEGER,
92
+ fmt: ""
93
+ },
94
+ monthly: {
95
+ min: {
96
+ value: Number.MAX_SAFE_INTEGER,
97
+ fmt: ""
98
+ },
99
+ max: {
100
+ value: Number.MIN_SAFE_INTEGER,
101
+ fmt: ""
102
+ }
103
+ }
104
+ },
105
+ discount: {
106
+ min: {
107
+ value: Number.MAX_SAFE_INTEGER,
108
+ fmt: ""
109
+ },
110
+ max: {
111
+ value: Number.MIN_SAFE_INTEGER,
112
+ fmt: ""
113
+ },
114
+ monthly: {
115
+ min: {
116
+ value: Number.MAX_SAFE_INTEGER,
117
+ fmt: ""
118
+ },
119
+ max: {
120
+ value: Number.MIN_SAFE_INTEGER,
121
+ fmt: ""
122
+ }
123
+ },
124
+ percentage: {
125
+ min: {
126
+ value: Number.MAX_SAFE_INTEGER,
127
+ fmt: ""
128
+ },
129
+ max: {
130
+ value: Number.MIN_SAFE_INTEGER,
131
+ fmt: ""
132
+ },
133
+ monthly: {
134
+ min: {
135
+ value: Number.MAX_SAFE_INTEGER,
136
+ fmt: ""
137
+ },
138
+ max: {
139
+ value: Number.MIN_SAFE_INTEGER,
140
+ fmt: ""
141
+ }
142
+ }
143
+ }
144
+ }
145
+ };
146
+ this._computeTask = new Task(this, {
147
+ task: async (_, { signal }) => {
148
+ const isActive = () => {
149
+ if (signal.aborted) {
150
+ throw new DOMException('Task aborted', `AbortError ${this.storeName.toString()}`);
151
+ }
152
+ };
153
+ // runs whenever _store or version changes
154
+ try {
155
+ const computed = await this._computeState(isActive);
156
+ await this._computeContext(computed, isActive);
157
+ return computed ?? [];
158
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
159
+ }
160
+ catch (e) { /* empty */ }
161
+ },
162
+ args: () => [this.store]
163
+ });
164
+ // Default event forwarder for simple pass-through nodes (e.g., root/context)
165
+ this._defaultForwardEventTask = new Task(this, {
166
+ task: async ([evt, auto]) => {
167
+ if (!evt || !auto)
168
+ return;
169
+ this._forwardEvent(evt);
170
+ this._notifyParent();
171
+ },
172
+ args: () => [this._event, this.autoForward]
173
+ });
174
+ // Keep `_event` in sync with either DOM events or parent context,
175
+ // depending on `ignoreEventsParent`.
176
+ this._syncEventTask = new Task(this, {
177
+ task: async ([fromParent, fromDom, ignoreParent]) => {
178
+ // Track last-seen refs so we can choose the freshest source
179
+ const domChanged = fromDom !== this._prevDomEventRef;
180
+ const parentChanged = fromParent !== this._prevParentEventRef;
181
+ this._prevDomEventRef = fromDom;
182
+ this._prevParentEventRef = fromParent;
183
+ if (ignoreParent) {
184
+ this._event = fromDom ?? undefined;
185
+ return;
186
+ }
187
+ if (domChanged && !parentChanged) {
188
+ this._event = fromDom ?? undefined;
189
+ return;
190
+ }
191
+ if (parentChanged && !domChanged) {
192
+ this._event = fromParent ?? undefined;
193
+ return;
194
+ }
195
+ if (domChanged && parentChanged) {
196
+ // If both changed in the same microtask, prefer DOM-originated
197
+ this._event = fromDom ?? fromParent ?? undefined;
198
+ return;
199
+ }
200
+ // Neither changed; keep current, but if nothing set yet, fall back
201
+ if (this._event === undefined) {
202
+ this._event = fromDom ?? fromParent ?? undefined;
203
+ }
204
+ },
205
+ args: () => [this._eventParent, this._eventDom, this.ignoreEventsParent]
206
+ });
207
+ this._collectToggleTask = new Task(this, {
208
+ task: async ([noCollect]) => {
209
+ const prev = this._prevCollect;
210
+ this._prevCollect = !noCollect; // track last 'collect' state for edge detection
211
+ if (prev === undefined) {
212
+ // Initial mount: register only if collecting (noCollect=false)
213
+ if (!noCollect)
214
+ this._notifyParent();
215
+ return;
216
+ }
217
+ // prev represents prior collect state
218
+ const currentCollect = !noCollect;
219
+ if (currentCollect && !prev) {
220
+ this._notifyParent();
221
+ }
222
+ else if (!currentCollect && prev) {
223
+ this.dispatchEvent(new CollectOptionEvent({ name: this.storeName, options: null }));
224
+ }
225
+ },
226
+ args: () => [this.noCollect]
227
+ });
228
+ // Cache original templates per element so we can re-render from the original innerHTML
229
+ this._tplElementTemplates = new WeakMap();
230
+ this._tplAttrTemplates = new WeakMap();
231
+ this._etaRenderTask = new Task(this, {
232
+ task: async ([state, derived]) => {
233
+ if (!state)
234
+ return;
235
+ if (!this.shouldRunEtaStateRender())
236
+ return;
237
+ // Provide 'it' derived from DSL context
238
+ const it = await toDSLContext({ state, derived });
239
+ await this._renderEtaTemplates(it);
240
+ },
241
+ args: () => [this.state, this._derived]
242
+ });
243
+ }
244
+ /**
245
+ * all options computed by this node
246
+ * options * actions
247
+ */
248
+ get computedOptions() {
249
+ return this._computeTask.value;
250
+ }
251
+ // Allow subclasses (e.g., OptionNode) to disable the base Eta render
252
+ // when they provide their own option-specific rendering pipeline.
253
+ shouldRunEtaStateRender() { return true; }
254
+ _isStateNodeElement(el) {
255
+ if (el instanceof StateNode_1)
256
+ return true;
257
+ const t = el.tagName;
258
+ return t === 'BD-STATE' || t === 'BD-PRODUCT' || t === 'BD-OPTION' || t === 'BD-CONTEXT';
259
+ }
260
+ _hasNestedStateNode(el) {
261
+ return !!el.querySelector?.('bd-state,bd-product,bd-option,bd-context');
262
+ }
263
+ _safeEtaRender(entry, data, { onErrorReturnInput = false } = {}) {
264
+ try {
265
+ if (!entry.fn) {
266
+ entry.fn = eta.compile(entry.src);
267
+ }
268
+ const out = eta.render(entry.fn, data);
269
+ return typeof out === 'string' ? out : String(out ?? '');
270
+ }
271
+ catch (err) {
272
+ console.error('Eta render error:', err);
273
+ return onErrorReturnInput ? entry.src : '';
274
+ }
275
+ }
276
+ async _renderEtaAttributes(el, data) {
277
+ // any attribute whose value contains '{{' is treated as an Eta template
278
+ const names = el.getAttributeNames();
279
+ let cache = this._tplAttrTemplates.get(el);
280
+ if (!cache) {
281
+ cache = new Map();
282
+ this._tplAttrTemplates.set(el, cache);
283
+ }
284
+ // Implicit any-attribute templates (heuristic: contains '{{')
285
+ for (const a of names) {
286
+ const raw = el.getAttribute(a);
287
+ if (!raw || !raw.includes('{{'))
288
+ continue;
289
+ const key = `imp:${a}`;
290
+ let entry = cache.get(key);
291
+ if (!entry || entry.src !== raw) {
292
+ entry = { src: raw };
293
+ cache.set(key, entry);
294
+ }
295
+ const rendered = this._safeEtaRender(entry, data, { onErrorReturnInput: true });
296
+ if (rendered !== raw) {
297
+ try {
298
+ el.setAttribute(a, rendered);
299
+ }
300
+ catch { /* ignore */ }
301
+ }
302
+ }
303
+ }
304
+ _hasRenderNodes(el) {
305
+ return !!el.querySelector?.('[data-store-render]');
306
+ }
307
+ async _morphElementFromHTML(el, html) {
308
+ try {
309
+ const wrapper = el.cloneNode(false);
310
+ wrapper.innerHTML = html;
311
+ morph(el, wrapper);
312
+ }
313
+ catch {
314
+ el.innerHTML = html;
315
+ }
316
+ }
317
+ async _renderEtaTemplates(context) {
318
+ // Traverse descendants; for any element that does NOT contain a nested state node,
319
+ // treat its innerHTML as a single Eta template and render/morph the entire subtree.
320
+ const visit = async (root) => {
321
+ for (const child of Array.from(root.children)) {
322
+ if (!(child instanceof HTMLElement))
323
+ continue;
324
+ // Always allow attribute-level Eta on any node in this subtree
325
+ await this._renderEtaAttributes(child, context);
326
+ if (this._isStateNodeElement(child) && child !== this) {
327
+ continue; // nested provider; let it handle its subtree
328
+ }
329
+ if (this._hasNestedStateNode(child)) {
330
+ // drill down until we reach leaves without nested state nodes
331
+ await visit(child);
332
+ continue;
333
+ }
334
+ // If subtree contains render nodes, recurse into it but don't morph at this level
335
+ if (this._hasRenderNodes(child)) {
336
+ await visit(child);
337
+ continue;
338
+ }
339
+ // Skip elements managed by the render pipeline
340
+ if (child.hasAttribute('data-store-render')) {
341
+ continue;
342
+ }
343
+ let entry = this._tplElementTemplates.get(child);
344
+ if (!entry) {
345
+ const src = child.innerHTML ?? '';
346
+ entry = { src };
347
+ this._tplElementTemplates.set(child, entry);
348
+ }
349
+ const out = this._safeEtaRender(entry, context, { onErrorReturnInput: true });
350
+ await this._morphElementFromHTML(child, out);
351
+ }
352
+ };
353
+ await visit(this);
354
+ }
355
+ connectedCallback() {
356
+ super.connectedCallback();
357
+ this.addEventListener(CollectActionEvent.eventName, this._collectActionEvent);
358
+ this.addEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
359
+ this.addEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
360
+ [ActionEvent, UpdateByDeltaEvent].forEach(e => this.addEventListener(e.eventName, this._eventChange));
361
+ }
362
+ remove() {
363
+ this.dispatchEvent(new CollectOptionEvent({ name: this.storeName, options: null }));
364
+ super.remove();
365
+ }
366
+ disconnectedCallback() {
367
+ this.removeEventListener(CollectActionEvent.eventName, this._collectActionEvent);
368
+ this.removeEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
369
+ this.removeEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
370
+ [ActionEvent, UpdateByDeltaEvent].forEach(e => this.removeEventListener(e.eventName, this._eventChange));
371
+ this._options.clear();
372
+ this._actions.clear();
373
+ this._partialOptions.clear();
374
+ super.disconnectedCallback();
375
+ }
376
+ _eventChange(e) {
377
+ e.stopPropagation();
378
+ // ignore if source matches ignore list
379
+ if (this._isIgnoredSource(e)) {
380
+ return;
381
+ }
382
+ this._eventDom = e;
383
+ }
384
+ _forwardEvent(e = this._event) {
385
+ this._fEvent = e;
386
+ }
387
+ _collectOptionEvent(e) {
388
+ if (e.target === this) {
389
+ return;
390
+ }
391
+ e.stopPropagation();
392
+ this.collectOption(e.detail);
393
+ }
394
+ collectOption({ name, options }) {
395
+ //node has disconected
396
+ if (options === null) {
397
+ this._options.delete(name);
398
+ }
399
+ else {
400
+ this._options.set(name, options);
401
+ }
402
+ this._computeTask.abort();
403
+ this._computeTask.run();
404
+ this._notifyParent();
405
+ }
406
+ _collectActionEvent(e) {
407
+ if (e.target === this) {
408
+ return;
409
+ }
410
+ e.stopPropagation();
411
+ const { name, action } = e.detail;
412
+ //node has disconected
413
+ if (action === null) {
414
+ this._actions.delete(name);
415
+ this._partialOptions.delete(name);
416
+ this._partialBundleOptions.delete(name);
417
+ }
418
+ else if (!action.id && (action.devices || action.subscription)) {
419
+ this._actions.set(name, action);
420
+ }
421
+ else {
422
+ if (action.bundle) {
423
+ this._partialBundleOptions.set(name, action);
424
+ }
425
+ else {
426
+ this._partialOptions.set(name, action);
427
+ }
428
+ }
429
+ this._computeTask.abort();
430
+ this._computeTask.run();
431
+ this._notifyParent();
432
+ }
433
+ _collectUpdateByDeltaEvent(e) {
434
+ if (e.target === this) {
435
+ return;
436
+ }
437
+ const { name, update } = e.detail;
438
+ //node has disconected
439
+ if (update === null) {
440
+ this._deltaUpdates.delete(name);
441
+ }
442
+ else {
443
+ this._deltaUpdates.set(name, update);
444
+ }
445
+ this._computeTask.abort();
446
+ this._computeTask.run();
447
+ this._notifyParent();
448
+ }
449
+ async _computeState(isActive) {
450
+ const computed = new Set();
451
+ // 1) collect base options + partialOptions
452
+ for (const optsPromise of this._options.values()) {
453
+ isActive();
454
+ const opts = await optsPromise;
455
+ for (const opt of opts) {
456
+ isActive();
457
+ computed.add(opt);
458
+ await this._applyPartials(opt, computed, isActive);
459
+ }
460
+ }
461
+ // 2) apply full “actions”
462
+ for (const opt of [...computed]) {
463
+ isActive();
464
+ await this._applyActions(opt, computed, isActive);
465
+ }
466
+ // 3) apply full "delta updates"
467
+ for (const opt of [...computed]) {
468
+ isActive();
469
+ await this._applyDeltaUpdates(opt, computed, isActive);
470
+ }
471
+ // 3) combine bundles
472
+ for (const opt of [...computed]) {
473
+ isActive();
474
+ const bundles = [...this._partialBundleOptions.values()];
475
+ const combos = await this._applyBundles(opt, bundles);
476
+ combos.forEach(cb => cb && computed.add(cb));
477
+ }
478
+ return [...computed];
479
+ }
480
+ async _applyPartials(baseOpt, computed, isActive) {
481
+ for (const partial of this._partialOptions.values()) {
482
+ isActive();
483
+ if (!partial.id) {
484
+ continue;
485
+ }
486
+ const newProduct = await baseOpt.switchProduct({ id: partial.id, campaign: partial.campaign });
487
+ const newOpt = await newProduct?.getOption({ devices: partial.devices, subscription: partial.subscription });
488
+ if (newOpt)
489
+ computed.add(newOpt);
490
+ }
491
+ }
492
+ async _applyActions(baseOpt, computed, isActive) {
493
+ for (const action of this._actions.values()) {
494
+ isActive();
495
+ const newOpt = await baseOpt.getOption({
496
+ devices: action.devices ?? baseOpt.getDevices(),
497
+ subscription: action.subscription ?? baseOpt.getSubscription()
498
+ });
499
+ if (newOpt)
500
+ computed.add(newOpt);
501
+ }
502
+ }
503
+ async _applyDeltaUpdates(baseOpt, computed, isActive) {
504
+ const product = baseOpt.getProduct();
505
+ const baseDevices = product.getDevices().values;
506
+ const baseSubscriptions = product.getSubscriptions().values;
507
+ let devices = baseOpt.getDevices();
508
+ let subscription = baseOpt.getSubscription();
509
+ //corner case for input types that define an interval by themselves
510
+ const getValue = (action, startValue, minValue) => {
511
+ if (action.useAsValue) {
512
+ action.delta = 1;
513
+ return Number(action.min) || minValue;
514
+ }
515
+ else {
516
+ return startValue;
517
+ }
518
+ };
519
+ for (const action of this._deltaUpdates.values()) {
520
+ isActive();
521
+ // Loop until we can no longer apply the delta
522
+ while (true) {
523
+ isActive();
524
+ const values = action.type === "devices"
525
+ ? baseDevices
526
+ : baseSubscriptions;
527
+ const current = action.type === "devices"
528
+ ? getValue(action, devices, baseDevices[0])
529
+ : getValue(action, subscription, baseSubscriptions[0]);
530
+ const { newValue, done } = this._computeDelta(values, current, action);
531
+ if (done)
532
+ break;
533
+ // Update the appropriate variable
534
+ if (action.type === "devices") {
535
+ devices = newValue;
536
+ }
537
+ else {
538
+ subscription = newValue;
539
+ }
540
+ // Fetch the new option and add it if exists
541
+ const newOpt = await baseOpt.getOption({ devices, subscription });
542
+ if (newOpt) {
543
+ computed.add(newOpt);
544
+ }
545
+ }
546
+ }
547
+ }
548
+ /**
549
+ * Attempts to apply the given delta action to the current value.
550
+ * Returns the updated value and whether we've exhausted variants.
551
+ */
552
+ _computeDelta(values, current, action) {
553
+ const min = Number(action.min) || Number.MAX_SAFE_INTEGER;
554
+ const max = Number(action.max) || Number.MIN_SAFE_INTEGER;
555
+ // Handle "next" / "prev" stepping through a discrete list
556
+ if (action.delta === "next" || action.delta === "prev") {
557
+ const idx = values.findIndex(v => v === current);
558
+ const step = action.delta === "next" ? 1 : -1;
559
+ const candidate = values.at(idx + step);
560
+ if (candidate && (action.delta === "next" ? candidate <= min : candidate >= max)) {
561
+ return { newValue: candidate, done: false };
562
+ }
563
+ return { newValue: current, done: true };
564
+ }
565
+ // Handle numeric delta
566
+ const candidate = current + action.delta;
567
+ const isValid = action.delta > 0 ? candidate <= min : candidate >= max;
568
+ if (isValid) {
569
+ return { newValue: candidate, done: false };
570
+ }
571
+ return { newValue: current, done: true };
572
+ }
573
+ async _computeContext(options, isActive) {
574
+ function updateMinMax(range, value, formatted) {
575
+ if (range.min.value == null || value < range.min.value) {
576
+ range.min.value = value;
577
+ range.min.fmt = formatted;
578
+ }
579
+ if (range.max.value == null || value > range.max.value) {
580
+ range.max.value = value;
581
+ range.max.fmt = formatted;
582
+ }
583
+ }
584
+ for (const option of options) {
585
+ isActive();
586
+ updateMinMax(this.state.price, option.getPrice({ currency: false }), option.getPrice());
587
+ updateMinMax(this.state.price.monthly, option.getPrice({ monthly: true, currency: false }), option.getPrice({ monthly: true }));
588
+ updateMinMax(this.state.discountedPrice, option.getDiscountedPrice({ currency: false }), option.getDiscountedPrice());
589
+ updateMinMax(this.state.discountedPrice.monthly, option.getDiscountedPrice({ monthly: true, currency: false }), option.getDiscountedPrice({ monthly: true }));
590
+ updateMinMax(this.state.discount, option.getDiscount({ symbol: false }), option.getDiscount());
591
+ updateMinMax(this.state.discount.monthly, option.getDiscount({ monthly: true, symbol: false }), option.getDiscount({ monthly: true }));
592
+ updateMinMax(this.state.discount.percentage, option.getDiscount({ percentage: true, symbol: false }), option.getDiscount({ percentage: true }));
593
+ updateMinMax(this.state.discount.percentage.monthly, option.getDiscount({ monthly: true, percentage: true, symbol: false }), option.getDiscount({ monthly: true, percentage: true }));
594
+ }
595
+ this.state = { ...this.state };
596
+ }
597
+ async _getOption({ id, campaign, devices, subscription }, bundle = []) {
598
+ if (id && devices && subscription) {
599
+ const product = await this.store?.getProduct({ id, campaign });
600
+ return await product?.getOption({ devices, subscription, bundle });
601
+ }
602
+ return null;
603
+ }
604
+ async _applyBundles(baseOpt, bundles) {
605
+ const result = [];
606
+ const recurse = async (prefix, start) => {
607
+ for (let i = start; i < bundles.length; i++) {
608
+ const bundleOption = await this._getOption({
609
+ id: bundles[i].id,
610
+ campaign: bundles[i].campaign,
611
+ devices: bundles[i].devices ?? baseOpt.getDevices(),
612
+ subscription: bundles[i].subscription ?? baseOpt.getSubscription()
613
+ }, []);
614
+ if (!bundleOption) {
615
+ continue;
616
+ }
617
+ const next = prefix.concat({
618
+ devicesFixed: Boolean(bundles[i].devices),
619
+ subscriptionFixed: Boolean(bundles[i].subscription),
620
+ option: bundleOption
621
+ });
622
+ result.push(next);
623
+ await recurse(next, i + 1);
624
+ }
625
+ };
626
+ const bundleOption = async (option, bundles = []) => {
627
+ for (const bundle of bundles) {
628
+ const newOpt = await option.toogleBundle(bundle);
629
+ if (newOpt) {
630
+ option = newOpt;
631
+ }
632
+ else {
633
+ return null;
634
+ }
635
+ }
636
+ return option;
637
+ };
638
+ await recurse([], 0);
639
+ const x = await Promise.all(result.map(bundles => bundleOption(baseOpt, bundles)));
640
+ return x;
641
+ }
642
+ _notifyParent() {
643
+ if (this.noCollect)
644
+ return;
645
+ this.dispatchEvent(new CollectOptionEvent({
646
+ name: this.storeName,
647
+ options: this._computeTask.taskComplete
648
+ }));
649
+ }
650
+ async getUpdateComplete() {
651
+ const result = await super.getUpdateComplete();
652
+ await this._computeTask.taskComplete;
653
+ await this._defaultForwardEventTask.taskComplete;
654
+ await this._syncEventTask.taskComplete;
655
+ await this._collectToggleTask.taskComplete;
656
+ await this._etaRenderTask.taskComplete;
657
+ return result;
658
+ }
659
+ isDeviceAndSubscriptionChange(evt) {
660
+ return evt instanceof ActionEvent
661
+ && Boolean(evt.detail?.devices || evt.detail?.subscription)
662
+ && !evt.detail.bundle;
663
+ }
664
+ isBundleToogle(evt) {
665
+ return evt instanceof ActionEvent
666
+ && Boolean(evt.detail?.id)
667
+ && Boolean(evt.detail.bundle);
668
+ }
669
+ isProductChange(evt) {
670
+ return evt instanceof ActionEvent
671
+ && Boolean(evt.detail?.id)
672
+ && !evt.detail.bundle;
673
+ }
674
+ isActionEvent(evt) {
675
+ return this.isProductChange(evt) || this.isDeviceAndSubscriptionChange(evt);
676
+ }
677
+ isDeltaUpdate(evt) {
678
+ return evt instanceof UpdateByDeltaEvent;
679
+ }
680
+ _isIgnoredSource(e) {
681
+ const set = this._getIgnoreSet();
682
+ if (set.size === 0)
683
+ return false;
684
+ const id = e.detail?.storeId || "";
685
+ return set.has(id);
686
+ }
687
+ _getIgnoreSet() {
688
+ const raw = this.ignoreEvents;
689
+ if (!raw)
690
+ return new Set();
691
+ return new Set(raw
692
+ .split(',')
693
+ .map(s => s.trim())
694
+ .filter(Boolean));
695
+ }
696
+ };
697
+ __decorate([
698
+ property({ attribute: false })
699
+ ], StateNode.prototype, "autoForward", void 0);
700
+ __decorate([
701
+ property({ type: Boolean, attribute: 'nocollect' })
702
+ ], StateNode.prototype, "noCollect", void 0);
703
+ __decorate([
704
+ property({ attribute: 'ignoreevents' })
705
+ ], StateNode.prototype, "ignoreEvents", void 0);
706
+ __decorate([
707
+ property()
708
+ ], StateNode.prototype, "storeName", void 0);
709
+ __decorate([
710
+ property({ type: Boolean, attribute: 'ignore-events-parent' })
711
+ ], StateNode.prototype, "ignoreEventsParent", void 0);
712
+ __decorate([
713
+ consume({ context: storeContext, subscribe: true }),
714
+ property({ attribute: false })
715
+ ], StateNode.prototype, "store", void 0);
716
+ __decorate([
717
+ consume({ context: eventContext, subscribe: true }),
718
+ property({ attribute: false })
719
+ ], StateNode.prototype, "_eventParent", void 0);
720
+ __decorate([
721
+ consume({ context: derivedContext, subscribe: true }),
722
+ property({ attribute: false })
723
+ ], StateNode.prototype, "_derived", void 0);
724
+ __decorate([
725
+ property({ attribute: false })
726
+ ], StateNode.prototype, "_event", void 0);
727
+ __decorate([
728
+ property({ attribute: false })
729
+ ], StateNode.prototype, "_eventDom", void 0);
730
+ __decorate([
731
+ provide({ context: eventContext }),
732
+ property({ attribute: false })
733
+ ], StateNode.prototype, "_fEvent", void 0);
734
+ __decorate([
735
+ provide({ context: stateContext }),
736
+ property({ attribute: false })
737
+ ], StateNode.prototype, "state", void 0);
738
+ StateNode = StateNode_1 = __decorate([
739
+ customElement('bd-state')
740
+ ], StateNode);
741
+ export { StateNode };
742
+ //# sourceMappingURL=node.state.js.map