@repobit/dex-store-elements 1.2.2 → 1.2.4

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.
@@ -8,7 +8,7 @@ import { derivedContext } from "../contexts/context.derived.js";
8
8
  import { eventContext } from "../contexts/context.event.js";
9
9
  import { stateContext } from "../contexts/context.state.js";
10
10
  import { storeContext } from "../contexts/context.store.js";
11
- import { ActionEvent, CollectActionEvent, CollectOptionEvent, CollectUpdateByDeltaEvent, UpdateByDeltaEvent } from "../events/events.js";
11
+ import { ActionEvent, CollectActionEvent, CollectChildEvent, CollectChildRemovedEvent, CollectOptionEvent, CollectUpdateByDeltaEvent, UpdateByDeltaEvent } from "../events/events.js";
12
12
  import { toDSLContext } from "../renders/context.js";
13
13
  import eta from "../templating/eta.js";
14
14
  import { consume, provide } from "@lit/context";
@@ -32,6 +32,11 @@ export class StateNode extends LitElement {
32
32
  this.autoForward = true;
33
33
  this.noCollect = false;
34
34
  this.storeName = Symbol("bd-state");
35
+ // Configurable delay for mutation-triggered Eta coalescing (ms).
36
+ // - Use 0 for next-tick behavior (runs ASAP, more visits)
37
+ // - Use a higher value (e.g., 50–150) to batch SPA bursts
38
+ this.etaMutationDelay = 10;
39
+ this.etaMutationDelayMax = 100;
35
40
  // If true, ignore events coming from parent context; only react to
36
41
  // events originating inside this subtree (DOM-bubbled via listeners).
37
42
  this.ignoreEventsParent = false;
@@ -234,11 +239,54 @@ export class StateNode extends LitElement {
234
239
  if (!this.shouldRunEtaStateRender())
235
240
  return;
236
241
  // Provide 'it' derived from DSL context
237
- const it = await toDSLContext({ state, derived });
238
- await this._renderEtaTemplates(it);
242
+ // Mark cause (mutation-triggered vs state/derived-triggered)
243
+ this._etaRenderCurrentFromMutation = this._etaRenderTriggeredByMutation;
244
+ this._etaRenderTriggeredByMutation = false;
245
+ this._etaRenderInProgress = true;
246
+ try {
247
+ const it = await toDSLContext({ state, derived });
248
+ await this._renderEtaTemplates(it);
249
+ }
250
+ finally {
251
+ this._etaRenderInProgress = false;
252
+ this._etaRenderCurrentFromMutation = false;
253
+ // If mutations arrived while rendering, schedule one more pass
254
+ if (this._etaRenderNeedsRun) {
255
+ this._etaRenderNeedsRun = false;
256
+ this._scheduleEtaRenderFromMutation();
257
+ }
258
+ this._notifyEtaIdle();
259
+ }
239
260
  },
240
261
  args: () => [this.state, this._derived]
241
262
  });
263
+ // Track DOM changes in the light DOM (slot content) to rerun Eta when SPA rewrites
264
+ this._etaRenderInProgress = false;
265
+ this._etaRenderScheduled = false;
266
+ this._etaRenderNeedsRun = false;
267
+ this._etaRenderTriggeredByMutation = false;
268
+ this._etaRenderCurrentFromMutation = false;
269
+ this._etaIdleWaiters = [];
270
+ // Track nearest contextual children (register via CollectChildEvent)
271
+ this._contextualChildren = new Set();
272
+ this._onCollectChild = (e) => {
273
+ if (e.target === this)
274
+ return;
275
+ e.stopPropagation();
276
+ const { child } = e.detail;
277
+ if (child && child !== this) {
278
+ this._contextualChildren.add(child);
279
+ }
280
+ };
281
+ this._onCollectChildRemoved = (e) => {
282
+ if (e.target === this)
283
+ return;
284
+ e.stopPropagation();
285
+ const { child } = e.detail;
286
+ if (child && child !== this) {
287
+ this._contextualChildren.delete(child);
288
+ }
289
+ };
242
290
  }
243
291
  /**
244
292
  * all options computed by this node
@@ -250,12 +298,78 @@ export class StateNode extends LitElement {
250
298
  // Allow subclasses (e.g., OptionNode) to disable the base Eta render
251
299
  // when they provide their own option-specific rendering pipeline.
252
300
  shouldRunEtaStateRender() { return true; }
301
+ _isEtaIdle() {
302
+ return !this._etaRenderInProgress && !this._etaRenderDebounce && !this._etaRenderNeedsRun;
303
+ }
304
+ _waitEtaSettled() {
305
+ if (this._isEtaIdle())
306
+ return Promise.resolve();
307
+ return new Promise((resolve) => {
308
+ this._etaIdleWaiters.push(resolve);
309
+ });
310
+ }
311
+ _notifyEtaIdle() {
312
+ if (!this._isEtaIdle())
313
+ return;
314
+ if (this._etaIdleWaiters.length === 0)
315
+ return;
316
+ const waiters = this._etaIdleWaiters.splice(0);
317
+ for (const w of waiters)
318
+ w();
319
+ }
320
+ _scheduleEtaRenderFromMutation() {
321
+ // If a render is already in progress, remember to run again once it finishes.
322
+ if (this._etaRenderInProgress) {
323
+ this._etaRenderNeedsRun = true;
324
+ return;
325
+ }
326
+ if (!this.shouldRunEtaStateRender())
327
+ return;
328
+ // Adaptive coalescing: restart timer on every mutation and, if bursts continue,
329
+ // increase delay up to a configured max to batch work.
330
+ const base = Math.max(0, Number(this.etaMutationDelay) || 0);
331
+ const max = Math.max(base, Number(this.etaMutationDelayMax) || 0);
332
+ let delay = base;
333
+ if (this._etaRenderDebounce) {
334
+ clearTimeout(this._etaRenderDebounce);
335
+ const prev = this._etaCoalesceDelay ?? base;
336
+ delay = Math.min(prev > 0 ? prev * 2 : base, max);
337
+ }
338
+ this._etaCoalesceDelay = delay;
339
+ this._etaRenderTriggeredByMutation = true;
340
+ this._etaRenderDebounce = window.setTimeout(() => {
341
+ this._etaRenderDebounce = undefined;
342
+ this._etaCoalesceDelay = base;
343
+ this._etaRenderTask.run();
344
+ // If nothing else is scheduled and not in-progress, resolve any idle waiters
345
+ queueMicrotask(() => this._notifyEtaIdle());
346
+ }, delay);
347
+ }
253
348
  _isStateNodeElement(el) {
254
349
  if (el instanceof StateNode)
255
350
  return true;
256
351
  const t = el.tagName;
257
352
  return t === 'BD-STATE' || t === 'BD-PRODUCT' || t === 'BD-OPTION' || t === 'BD-CONTEXT';
258
353
  }
354
+ // Find the nearest eligible element under this provider to use as a refresh root
355
+ _findEligibleRoot(n) {
356
+ let cur = n;
357
+ while (cur && cur !== this) {
358
+ if (!(cur instanceof HTMLElement)) {
359
+ cur = cur.parentNode;
360
+ continue;
361
+ }
362
+ if (this._isStateNodeElement(cur) && cur !== this)
363
+ return null; // nested provider boundary
364
+ if (cur.hasAttribute('data-store-render'))
365
+ return null; // render-managed boundary
366
+ const parentProvider = cur.parentElement?.closest('bd-state,bd-product,bd-option,bd-context');
367
+ if (parentProvider === this || cur.parentElement === this)
368
+ return cur;
369
+ cur = cur.parentNode;
370
+ }
371
+ return this;
372
+ }
259
373
  _hasNestedStateNode(el) {
260
374
  return !!el.querySelector?.('bd-state,bd-product,bd-option,bd-context');
261
375
  }
@@ -280,11 +394,16 @@ export class StateNode extends LitElement {
280
394
  cache = new Map();
281
395
  this._tplAttrTemplates.set(el, cache);
282
396
  }
397
+ // Fast path: if we've previously seen no templated attributes, skip scan
398
+ if (this._noEtaAttrs && this._noEtaAttrs.has(el))
399
+ return;
400
+ let foundTemplateAttr = false;
283
401
  // Implicit any-attribute templates (heuristic: contains '{{')
284
402
  for (const a of names) {
285
403
  const raw = el.getAttribute(a);
286
404
  if (!raw || !raw.includes('{{'))
287
405
  continue;
406
+ foundTemplateAttr = true;
288
407
  const key = `imp:${a}`;
289
408
  let entry = cache.get(key);
290
409
  if (!entry || entry.src !== raw) {
@@ -299,11 +418,27 @@ export class StateNode extends LitElement {
299
418
  catch { /* ignore */ }
300
419
  }
301
420
  }
421
+ // If no templated attributes were found on this element, mark it to skip next time
422
+ if (!foundTemplateAttr) {
423
+ if (!this._noEtaAttrs)
424
+ this._noEtaAttrs = new WeakSet();
425
+ this._noEtaAttrs.add(el);
426
+ }
302
427
  }
303
428
  _hasRenderNodes(el) {
304
- return !!el.querySelector?.('[data-store-render]');
429
+ const h = el;
430
+ if (!h)
431
+ return false;
432
+ if (h.hasAttribute && h.hasAttribute('data-store-render'))
433
+ return true;
434
+ // Heuristic: check innerHTML for render markers to avoid expensive queries
435
+ const html = h.innerHTML;
436
+ return typeof html === 'string' && html.includes('data-store-render');
305
437
  }
306
438
  async _morphElementFromHTML(el, html) {
439
+ // Fast path: nothing changed
440
+ if (el.innerHTML === html)
441
+ return;
307
442
  try {
308
443
  const wrapper = el.cloneNode(false);
309
444
  wrapper.innerHTML = html;
@@ -316,10 +451,19 @@ export class StateNode extends LitElement {
316
451
  async _renderEtaTemplates(context) {
317
452
  // Traverse descendants; for any element that does NOT contain a nested state node,
318
453
  // treat its innerHTML as a single Eta template and render/morph the entire subtree.
454
+ let _processed = 0;
319
455
  const visit = async (root) => {
456
+ // If a follow-up Eta run is already requested (DOM still changing)
457
+ // and this pass was triggered by mutation, bail out to avoid unstable writes.
458
+ if (this._etaRenderCurrentFromMutation && this._etaRenderNeedsRun)
459
+ return;
320
460
  for (const child of Array.from(root.children)) {
461
+ if (this._etaRenderCurrentFromMutation && this._etaRenderNeedsRun)
462
+ return;
321
463
  if (!(child instanceof HTMLElement))
322
464
  continue;
465
+ // Read current HTML source once
466
+ const currentSrc = child.innerHTML ?? '';
323
467
  // Always allow attribute-level Eta on any node in this subtree
324
468
  await this._renderEtaAttributes(child, context);
325
469
  if (this._isStateNodeElement(child) && child !== this) {
@@ -330,6 +474,7 @@ export class StateNode extends LitElement {
330
474
  await visit(child);
331
475
  continue;
332
476
  }
477
+ // Cheap render-node detection using current HTML source
333
478
  // If subtree contains render nodes, recurse into it but don't morph at this level
334
479
  if (this._hasRenderNodes(child)) {
335
480
  await visit(child);
@@ -339,30 +484,118 @@ export class StateNode extends LitElement {
339
484
  if (child.hasAttribute('data-store-render')) {
340
485
  continue;
341
486
  }
487
+ // Reuse cached template, but refresh if DOM source changed (e.g., SPA mutated innerHTML)
342
488
  let entry = this._tplElementTemplates.get(child);
489
+ const hadTemplate = Boolean(entry && entry.src && entry.src.includes('{{'));
490
+ // If this element and its attributes contain no templates, it is not a
491
+ // special provider/render container, and it never had a template cached,
492
+ // skip further work.
493
+ const noAttrs = this._noEtaAttrs?.has(child) ?? false;
494
+ if (!currentSrc.includes('{{') && noAttrs && !hadTemplate && !this._hasNestedStateNode(child)) {
495
+ continue;
496
+ }
343
497
  if (!entry) {
344
- const src = child.innerHTML ?? '';
345
- entry = { src };
498
+ entry = { src: currentSrc };
346
499
  this._tplElementTemplates.set(child, entry);
347
500
  }
501
+ else if (entry.src !== currentSrc) {
502
+ const isTemplateLike = currentSrc.includes('{{');
503
+ // Accept SPA-authored changes (even without templates) only when this pass is mutation-triggered.
504
+ if (isTemplateLike || this._etaRenderCurrentFromMutation) {
505
+ entry.src = currentSrc;
506
+ delete entry.fn;
507
+ }
508
+ }
348
509
  const out = this._safeEtaRender(entry, context, { onErrorReturnInput: true });
510
+ if (this._etaRenderCurrentFromMutation) {
511
+ // During mutation-triggered passes, avoid writing to DOM to prevent
512
+ // racing with SPA changes. We refreshed caches above so subsequent
513
+ // non-mutation renders have the latest source.
514
+ continue;
515
+ }
516
+ if (this._etaRenderCurrentFromMutation && this._etaRenderNeedsRun)
517
+ return;
349
518
  await this._morphElementFromHTML(child, out);
519
+ // Periodically yield to avoid long tasks
520
+ if ((++_processed % 50) === 0) {
521
+ await Promise.resolve();
522
+ }
350
523
  }
351
524
  };
352
- await visit(this);
525
+ // If this is a mutation-triggered pass and we have specific dirty roots, only refresh those
526
+ if (this._etaRenderCurrentFromMutation && this._etaDirtyRoots && this._etaDirtyRoots.size) {
527
+ for (const r of this._etaDirtyRoots) {
528
+ await visit(r);
529
+ }
530
+ this._etaDirtyRoots.clear();
531
+ }
532
+ else {
533
+ await visit(this);
534
+ }
353
535
  }
354
536
  connectedCallback() {
355
537
  super.connectedCallback();
538
+ // Listen for child registration/unregistration
539
+ this.addEventListener(CollectChildEvent.eventName, this._onCollectChild);
540
+ this.addEventListener(CollectChildRemovedEvent.eventName, this._onCollectChildRemoved);
356
541
  this.addEventListener(CollectActionEvent.eventName, this._collectActionEvent);
357
542
  this.addEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
358
543
  this.addEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
359
544
  [ActionEvent, UpdateByDeltaEvent].forEach(e => this.addEventListener(e.eventName, this._eventChange));
545
+ // Observe slot/light DOM changes to trigger Eta re-rendering in SPA scenarios
546
+ if (!this._slotMo && this.shouldRunEtaStateRender()) {
547
+ this._slotMo = new MutationObserver((muts) => {
548
+ for (const m of muts) {
549
+ // Consider only targets that aren't within nested-provider or render-managed subtrees
550
+ const targets = [];
551
+ if (m.type === 'characterData' || m.type === 'attributes') {
552
+ targets.push(m.target);
553
+ // If attributes changed on an element previously marked as having no Eta attrs,
554
+ // clear the cache so it can be rescanned on the next pass.
555
+ if (m.type === 'attributes' && this._noEtaAttrs && m.target instanceof HTMLElement) {
556
+ this._noEtaAttrs.delete(m.target);
557
+ }
558
+ }
559
+ else {
560
+ targets.push(...Array.from(m.addedNodes));
561
+ }
562
+ const eligibles = [];
563
+ for (const t of targets) {
564
+ const root = this._findEligibleRoot(t);
565
+ if (root)
566
+ eligibles.push(root);
567
+ }
568
+ if (!eligibles.length)
569
+ continue;
570
+ // Track dirty roots for this cycle
571
+ if (!this._etaDirtyRoots)
572
+ this._etaDirtyRoots = new Set();
573
+ eligibles.forEach(r => this._etaDirtyRoots.add(r));
574
+ this._scheduleEtaRenderFromMutation();
575
+ break; // one schedule is enough per batch
576
+ }
577
+ });
578
+ this._slotMo.observe(this, {
579
+ childList: true,
580
+ subtree: true,
581
+ characterData: true,
582
+ attributes: true
583
+ });
584
+ }
585
+ // Announce this node as a contextual child of its nearest parent StateNode
586
+ // so parents can await our updateComplete as part of theirs.
587
+ this._announceAsContextualChild();
360
588
  }
361
589
  remove() {
590
+ // Announce removal to parent before DOM detaches, so it can unregister us
591
+ this._announceContextualChildRemoved();
362
592
  this.dispatchEvent(new CollectOptionEvent({ name: this.storeName, options: null }));
363
593
  super.remove();
364
594
  }
365
595
  disconnectedCallback() {
596
+ // Stop listening for contextual child events
597
+ this.removeEventListener(CollectChildEvent.eventName, this._onCollectChild);
598
+ this.removeEventListener(CollectChildRemovedEvent.eventName, this._onCollectChildRemoved);
366
599
  this.removeEventListener(CollectActionEvent.eventName, this._collectActionEvent);
367
600
  this.removeEventListener(CollectUpdateByDeltaEvent.eventName, this._collectUpdateByDeltaEvent);
368
601
  this.removeEventListener(CollectOptionEvent.eventName, this._collectOptionEvent);
@@ -370,8 +603,21 @@ export class StateNode extends LitElement {
370
603
  this._options.clear();
371
604
  this._actions.clear();
372
605
  this._partialOptions.clear();
606
+ this._contextualChildren.clear();
607
+ if (this._etaRenderDebounce) {
608
+ clearTimeout(this._etaRenderDebounce);
609
+ this._etaRenderDebounce = undefined;
610
+ }
611
+ this._slotMo?.disconnect();
612
+ this._slotMo = undefined;
373
613
  super.disconnectedCallback();
374
614
  }
615
+ _announceAsContextualChild() {
616
+ this.dispatchEvent(new CollectChildEvent({ child: this }));
617
+ }
618
+ _announceContextualChildRemoved() {
619
+ this.dispatchEvent(new CollectChildRemovedEvent({ child: this }));
620
+ }
375
621
  _eventChange(e) {
376
622
  e.stopPropagation();
377
623
  // ignore if source matches ignore list
@@ -446,28 +692,41 @@ export class StateNode extends LitElement {
446
692
  this._notifyParent();
447
693
  }
448
694
  async _computeState(isActive) {
695
+ // We'll iteratively expand the set of options using actions and delta updates
696
+ // until we reach a fixed point (no new options discovered). This ensures
697
+ // combinations across different dimensions are included (e.g., 10-24).
449
698
  const computed = new Set();
450
- // 1) collect base options + partialOptions
699
+ const queue = [];
700
+ const enqueue = (opt) => {
701
+ if (!computed.has(opt)) {
702
+ computed.add(opt);
703
+ queue.push(opt);
704
+ }
705
+ };
706
+ // 1) collect base options + partialOptions and seed the queue
451
707
  for (const optsPromise of this._options.values()) {
452
708
  isActive();
453
709
  const opts = await optsPromise;
454
710
  for (const opt of opts) {
455
711
  isActive();
456
- computed.add(opt);
457
- await this._applyPartials(opt, computed, isActive);
712
+ enqueue(opt);
713
+ const partials = await this._applyPartials(opt, isActive);
714
+ for (const p of partials)
715
+ enqueue(p);
458
716
  }
459
717
  }
460
- // 2) apply full “actions”
461
- for (const opt of [...computed]) {
462
- isActive();
463
- await this._applyActions(opt, computed, isActive);
464
- }
465
- // 3) apply full "delta updates"
466
- for (const opt of [...computed]) {
718
+ // 2) Repeatedly apply actions and delta updates to closure
719
+ while (queue.length) {
467
720
  isActive();
468
- await this._applyDeltaUpdates(opt, computed, isActive);
721
+ const current = queue.shift();
722
+ const actionResults = await this._applyActions(current, isActive);
723
+ for (const a of actionResults)
724
+ enqueue(a);
725
+ const deltaResults = await this._applyDeltaUpdates(current, isActive);
726
+ for (const d of deltaResults)
727
+ enqueue(d);
469
728
  }
470
- // 3) combine bundles
729
+ // 3) combine bundles on the full set
471
730
  for (const opt of [...computed]) {
472
731
  isActive();
473
732
  const bundles = [...this._partialBundleOptions.values()];
@@ -476,7 +735,8 @@ export class StateNode extends LitElement {
476
735
  }
477
736
  return [...computed];
478
737
  }
479
- async _applyPartials(baseOpt, computed, isActive) {
738
+ async _applyPartials(baseOpt, isActive) {
739
+ const results = [];
480
740
  for (const partial of this._partialOptions.values()) {
481
741
  isActive();
482
742
  if (!partial.id) {
@@ -485,27 +745,37 @@ export class StateNode extends LitElement {
485
745
  const newProduct = await baseOpt.switchProduct({ id: partial.id, campaign: partial.campaign });
486
746
  const newOpt = await newProduct?.getOption({ devices: partial.devices, subscription: partial.subscription });
487
747
  if (newOpt)
488
- computed.add(newOpt);
748
+ results.push(newOpt);
489
749
  }
750
+ return results;
490
751
  }
491
- async _applyActions(baseOpt, computed, isActive) {
752
+ async _applyActions(baseOpt, isActive) {
753
+ const results = [];
492
754
  for (const action of this._actions.values()) {
493
755
  isActive();
756
+ const targetDevices = action.devices ?? baseOpt.getDevices();
757
+ const targetSubscription = action.subscription ?? baseOpt.getSubscription();
758
+ // Skip no-op actions that don't change anything
759
+ if (targetDevices === baseOpt.getDevices() && targetSubscription === baseOpt.getSubscription()) {
760
+ continue;
761
+ }
494
762
  const newOpt = await baseOpt.getOption({
495
- devices: action.devices ?? baseOpt.getDevices(),
496
- subscription: action.subscription ?? baseOpt.getSubscription()
763
+ devices: targetDevices,
764
+ subscription: targetSubscription
497
765
  });
498
766
  if (newOpt)
499
- computed.add(newOpt);
767
+ results.push(newOpt);
500
768
  }
769
+ return results;
501
770
  }
502
- async _applyDeltaUpdates(baseOpt, computed, isActive) {
771
+ async _applyDeltaUpdates(baseOpt, isActive) {
772
+ const results = [];
503
773
  const product = baseOpt.getProduct();
504
774
  const baseDevices = product.getDevices().values;
505
775
  const baseSubscriptions = product.getSubscriptions().values;
506
776
  let devices = baseOpt.getDevices();
507
777
  let subscription = baseOpt.getSubscription();
508
- //corner case for input types that define an interval by themselves
778
+ // corner case for input types that define an interval by themselves
509
779
  const getValue = (action, startValue, minValue) => {
510
780
  if (action.useAsValue) {
511
781
  action.delta = 1;
@@ -520,9 +790,7 @@ export class StateNode extends LitElement {
520
790
  // Loop until we can no longer apply the delta
521
791
  while (true) {
522
792
  isActive();
523
- const values = action.type === "devices"
524
- ? baseDevices
525
- : baseSubscriptions;
793
+ const values = action.type === "devices" ? baseDevices : baseSubscriptions;
526
794
  const current = action.type === "devices"
527
795
  ? getValue(action, devices, baseDevices[0])
528
796
  : getValue(action, subscription, baseSubscriptions[0]);
@@ -536,13 +804,13 @@ export class StateNode extends LitElement {
536
804
  else {
537
805
  subscription = newValue;
538
806
  }
539
- // Fetch the new option and add it if exists
807
+ // Fetch the new option and collect it if exists
540
808
  const newOpt = await baseOpt.getOption({ devices, subscription });
541
- if (newOpt) {
542
- computed.add(newOpt);
543
- }
809
+ if (newOpt)
810
+ results.push(newOpt);
544
811
  }
545
812
  }
813
+ return results;
546
814
  }
547
815
  /**
548
816
  * Attempts to apply the given delta action to the current value.
@@ -653,6 +921,18 @@ export class StateNode extends LitElement {
653
921
  await this._syncEventTask.taskComplete;
654
922
  await this._collectToggleTask.taskComplete;
655
923
  await this._etaRenderTask.taskComplete;
924
+ // Ensure any debounced/mutation-triggered Eta runs have settled
925
+ await this._waitEtaSettled();
926
+ // Await all registered contextual children (nearest descendants only)
927
+ try {
928
+ const children = Array.from(this._contextualChildren);
929
+ const waits = children
930
+ .map((c) => c.updateComplete)
931
+ .filter((p) => !!p);
932
+ if (waits.length > 0)
933
+ await Promise.allSettled(waits);
934
+ }
935
+ catch { /* ignore */ }
656
936
  return result;
657
937
  }
658
938
  isDeviceAndSubscriptionChange(evt) {
@@ -708,6 +988,12 @@ __decorate([
708
988
  __decorate([
709
989
  property({ attribute: 'store-name' })
710
990
  ], StateNode.prototype, "storeName", void 0);
991
+ __decorate([
992
+ property({ attribute: 'eta-mutation-delay', type: Number })
993
+ ], StateNode.prototype, "etaMutationDelay", void 0);
994
+ __decorate([
995
+ property({ attribute: 'eta-mutation-delay-max', type: Number })
996
+ ], StateNode.prototype, "etaMutationDelayMax", void 0);
711
997
  __decorate([
712
998
  property({ type: Boolean, attribute: 'ignore-events-parent' })
713
999
  ], StateNode.prototype, "ignoreEventsParent", void 0);