@yoltra/core 0.5.0 → 0.6.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.
package/dist/yoltra.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @yoltra/core v0.5.0
2
+ * @yoltra/core v0.6.0
3
3
  * (c) 2026 Manu Ramirez <@pixerael>
4
4
  * License: MIT
5
5
  * Homepage: https://yoltra.dev
@@ -7,10 +7,10 @@
7
7
  * This source code is licensed under the MIT license found in the
8
8
  * LICENSE file in the root directory of this source tree
9
9
  */
10
- var T = Object.defineProperty;
11
- var C = (o, e, t) => e in o ? T(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
12
- var p = (o, e, t) => C(o, typeof e != "symbol" ? e + "" : e, t);
13
- class B {
10
+ var K = Object.defineProperty;
11
+ var F = (c, e, t) => e in c ? K(c, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : c[e] = t;
12
+ var p = (c, e, t) => F(c, typeof e != "symbol" ? e + "" : e, t);
13
+ class U {
14
14
  constructor() {
15
15
  /**
16
16
  * Internal registry: `channel → type → Set<handler>`.
@@ -50,11 +50,11 @@ class B {
50
50
  *
51
51
  * @public
52
52
  */
53
- on(e, t, n) {
54
- let s = this.handlers.get(e);
55
- s || (s = /* @__PURE__ */ new Map(), this.handlers.set(e, s));
56
- let i = s.get(t);
57
- return i || (i = /* @__PURE__ */ new Set(), s.set(t, i)), i.add(n), () => this.off(e, t, n);
53
+ on(e, t, s) {
54
+ let n = this.handlers.get(e);
55
+ n || (n = /* @__PURE__ */ new Map(), this.handlers.set(e, n));
56
+ let r = n.get(t);
57
+ return r || (r = /* @__PURE__ */ new Set(), n.set(t, r)), r.add(s), () => this.off(e, t, s);
58
58
  }
59
59
  /**
60
60
  * Removes a specific handler previously added with {@link EventBus.on | `on`}.
@@ -76,11 +76,11 @@ class B {
76
76
  *
77
77
  * @public
78
78
  */
79
- off(e, t, n) {
80
- const s = this.handlers.get(e);
81
- if (!s) return;
82
- const i = s.get(t);
83
- i && (i.delete(n), i.size === 0 && s.delete(t), s.size === 0 && this.handlers.delete(e));
79
+ off(e, t, s) {
80
+ const n = this.handlers.get(e);
81
+ if (!n) return;
82
+ const r = n.get(t);
83
+ r && (r.delete(s), r.size === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e));
84
84
  }
85
85
  /**
86
86
  * Emits an event to all subscribers of the exact `(channel, type)`.
@@ -104,16 +104,16 @@ class B {
104
104
  *
105
105
  * @public
106
106
  */
107
- emit(e, t, n, s) {
108
- const i = this.handlers.get(e);
109
- if (!i) return;
110
- const d = i.get(t);
111
- if (!(!d || d.size === 0))
112
- for (const a of [...d])
107
+ emit(e, t, s, n) {
108
+ const r = this.handlers.get(e);
109
+ if (!r) return;
110
+ const o = r.get(t);
111
+ if (!(!o || o.size === 0))
112
+ for (const l of [...o])
113
113
  try {
114
- a(n, s);
115
- } catch (r) {
116
- console.error("EventBus handler error:", r);
114
+ l(s, n);
115
+ } catch (i) {
116
+ console.error("EventBus handler error:", i);
117
117
  }
118
118
  }
119
119
  /**
@@ -133,7 +133,7 @@ class B {
133
133
  this.handlers.clear();
134
134
  }
135
135
  }
136
- class H {
136
+ class L {
137
137
  constructor() {
138
138
  /**
139
139
  * Exact handlers: `channel → type → [handlers]`.
@@ -196,18 +196,18 @@ class H {
196
196
  *
197
197
  * @public
198
198
  */
199
- on(e, t, n) {
200
- const s = String(t);
201
- if (this.isPattern(s)) {
202
- const i = s;
199
+ on(e, t, s) {
200
+ const n = String(t);
201
+ if (this.isPattern(n)) {
202
+ const r = n;
203
203
  this.patternHandlers.has(e) || this.patternHandlers.set(e, /* @__PURE__ */ new Map());
204
- const d = this.patternHandlers.get(e);
205
- return d.has(i) || (d.set(i, []), this.indexPattern(e, i)), d.get(i).push(n), () => this.offPattern(e, i, n);
204
+ const o = this.patternHandlers.get(e);
205
+ return o.has(r) || (o.set(r, []), this.indexPattern(e, r)), o.get(r).push(s), () => this.offPattern(e, r, s);
206
206
  } else {
207
- const i = this.normalizeTypeKey(s);
207
+ const r = this.normalizeTypeKey(n);
208
208
  this.handlers.has(e) || this.handlers.set(e, /* @__PURE__ */ new Map());
209
- const d = this.handlers.get(e);
210
- return d.has(i) || d.set(i, []), d.get(i).push(n), () => this.offExactNormalized(e, i, n);
209
+ const o = this.handlers.get(e);
210
+ return o.has(r) || o.set(r, []), o.get(r).push(s), () => this.offExactNormalized(e, r, s);
211
211
  }
212
212
  }
213
213
  /**
@@ -228,9 +228,9 @@ class H {
228
228
  *
229
229
  * @public
230
230
  */
231
- off(e, t, n) {
232
- const s = this.normalizeTypeKey(String(t));
233
- this.offExactNormalized(e, s, n);
231
+ off(e, t, s) {
232
+ const n = this.normalizeTypeKey(String(t));
233
+ this.offExactNormalized(e, n, s);
234
234
  }
235
235
  /**
236
236
  * Internal exact unsubscription using an already **normalized** type key.
@@ -240,13 +240,13 @@ class H {
240
240
  * @param handler - Handler to remove.
241
241
  * @internal
242
242
  */
243
- offExactNormalized(e, t, n) {
244
- const s = this.handlers.get(e);
245
- if (!s) return;
246
- const i = s.get(t);
247
- if (!i) return;
248
- const d = i.indexOf(n);
249
- d !== -1 && i.splice(d, 1), i.length === 0 && s.delete(t), s.size === 0 && this.handlers.delete(e);
243
+ offExactNormalized(e, t, s) {
244
+ const n = this.handlers.get(e);
245
+ if (!n) return;
246
+ const r = n.get(t);
247
+ if (!r) return;
248
+ const o = r.indexOf(s);
249
+ o !== -1 && r.splice(o, 1), r.length === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e);
250
250
  }
251
251
  /**
252
252
  * Internal removal for a **pattern** subscription. No-ops if missing.
@@ -256,13 +256,13 @@ class H {
256
256
  * @param handler - Handler to remove.
257
257
  * @internal
258
258
  */
259
- offPattern(e, t, n) {
260
- const s = this.patternHandlers.get(e);
261
- if (!s) return;
262
- const i = s.get(t);
263
- if (!i) return;
264
- const d = i.indexOf(n);
265
- d !== -1 && i.splice(d, 1), i.length === 0 && (s.delete(t), this.unindexPattern(e, t)), s.size === 0 && (this.patternHandlers.delete(e), this.patternIndex.delete(e));
259
+ offPattern(e, t, s) {
260
+ const n = this.patternHandlers.get(e);
261
+ if (!n) return;
262
+ const r = n.get(t);
263
+ if (!r) return;
264
+ const o = r.indexOf(s);
265
+ o !== -1 && r.splice(o, 1), r.length === 0 && (n.delete(t), this.unindexPattern(e, t)), n.size === 0 && (this.patternHandlers.delete(e), this.patternIndex.delete(e));
266
266
  }
267
267
  /**
268
268
  * Emits an event to all exact subscribers first, then to **matching pattern** subscribers.
@@ -284,21 +284,21 @@ class H {
284
284
  *
285
285
  * @public
286
286
  */
287
- emit(e, t, n) {
288
- const s = String(t), i = this.normalizeTypeKey(s), d = this.handlers.get(e)?.get(i) ?? [], a = this.matchingPatternHandlers(e, s), r = /* @__PURE__ */ new Set(), f = (c) => {
289
- for (const u of [...c])
290
- if (!r.has(u)) {
291
- r.add(u);
287
+ emit(e, t, s) {
288
+ const n = String(t), r = this.normalizeTypeKey(n), o = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n), i = /* @__PURE__ */ new Set(), a = (d) => {
289
+ for (const u of [...d])
290
+ if (!i.has(u)) {
291
+ i.add(u);
292
292
  try {
293
- u(n);
294
- } catch (l) {
295
- console.error(l);
293
+ u(s);
294
+ } catch (f) {
295
+ console.error(f);
296
296
  continue;
297
297
  }
298
298
  }
299
299
  };
300
- f(d);
301
- for (const c of a) f(c);
300
+ a(o);
301
+ for (const d of l) a(d);
302
302
  }
303
303
  /**
304
304
  * Emits a payload that is only built if somebody is listening.
@@ -316,23 +316,23 @@ class H {
316
316
  *
317
317
  * @public
318
318
  */
319
- emitWith(e, t, n) {
320
- const s = String(t), i = this.normalizeTypeKey(s), d = this.handlers.get(e)?.get(i) ?? [], a = this.matchingPatternHandlers(e, s);
321
- if (d.length === 0 && a.length === 0) return;
322
- const r = n(), f = /* @__PURE__ */ new Set(), c = (u) => {
323
- for (const l of [...u])
324
- if (!f.has(l)) {
325
- f.add(l);
319
+ emitWith(e, t, s) {
320
+ const n = String(t), r = this.normalizeTypeKey(n), o = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n);
321
+ if (o.length === 0 && l.length === 0) return;
322
+ const i = s(), a = /* @__PURE__ */ new Set(), d = (u) => {
323
+ for (const f of [...u])
324
+ if (!a.has(f)) {
325
+ a.add(f);
326
326
  try {
327
- l(r);
328
- } catch (y) {
329
- console.error(y);
327
+ f(i);
328
+ } catch (m) {
329
+ console.error(m);
330
330
  continue;
331
331
  }
332
332
  }
333
333
  };
334
- c(d);
335
- for (const u of a) c(u);
334
+ d(o);
335
+ for (const u of l) d(u);
336
336
  }
337
337
  /**
338
338
  * Determines if a string is a **pattern** (contains `*`).
@@ -371,27 +371,27 @@ class H {
371
371
  * @internal
372
372
  */
373
373
  indexPattern(e, t) {
374
- let n = this.patternIndex.get(e);
375
- n === void 0 && (n = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, n));
376
- const s = this.splitPath(t), i = { pattern: t, segments: s }, d = s[0];
377
- if (d === void 0 || d === "*" || d === "**") {
378
- n.anyHead.push(i);
374
+ let s = this.patternIndex.get(e);
375
+ s === void 0 && (s = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, s));
376
+ const n = this.splitPath(t), r = { pattern: t, segments: n }, o = n[0];
377
+ if (o === void 0 || o === "*" || o === "**") {
378
+ s.anyHead.push(r);
379
379
  return;
380
380
  }
381
- const a = n.byHead.get(d);
382
- a === void 0 ? n.byHead.set(d, [i]) : a.push(i);
381
+ const l = s.byHead.get(o);
382
+ l === void 0 ? s.byHead.set(o, [r]) : l.push(r);
383
383
  }
384
384
  /**
385
385
  * Removes a pattern from the index. Paired with {@link LooseEventBus.offPattern}.
386
386
  * @internal
387
387
  */
388
388
  unindexPattern(e, t) {
389
- const n = this.patternIndex.get(e);
390
- if (n === void 0) return;
391
- const s = this.splitPath(t)[0], i = s === void 0 || s === "*" || s === "**" ? n.anyHead : n.byHead.get(s);
392
- if (i === void 0) return;
393
- const d = i.findIndex((a) => a.pattern === t);
394
- d !== -1 && i.splice(d, 1), i.length === 0 && i !== n.anyHead && s !== void 0 && n.byHead.delete(s);
389
+ const s = this.patternIndex.get(e);
390
+ if (s === void 0) return;
391
+ const n = this.splitPath(t)[0], r = n === void 0 || n === "*" || n === "**" ? s.anyHead : s.byHead.get(n);
392
+ if (r === void 0) return;
393
+ const o = r.findIndex((l) => l.pattern === t);
394
+ o !== -1 && r.splice(o, 1), r.length === 0 && r !== s.anyHead && n !== void 0 && s.byHead.delete(n);
395
395
  }
396
396
  /**
397
397
  * The handler lists of every pattern matching this subject.
@@ -405,20 +405,20 @@ class H {
405
405
  * @internal
406
406
  */
407
407
  matchingPatternHandlers(e, t) {
408
- const n = this.patternHandlers.get(e), s = this.patternIndex.get(e);
409
- if (n === void 0 || n.size === 0 || s === void 0) return [];
410
- const i = this.splitPath(t), d = [], a = (f) => {
411
- for (const c of f) {
412
- if (!this.matchSegments(c.segments, i)) continue;
413
- const u = n.get(c.pattern);
414
- u !== void 0 && d.push(u);
408
+ const s = this.patternHandlers.get(e), n = this.patternIndex.get(e);
409
+ if (s === void 0 || s.size === 0 || n === void 0) return [];
410
+ const r = this.splitPath(t), o = [], l = (a) => {
411
+ for (const d of a) {
412
+ if (!this.matchSegments(d.segments, r)) continue;
413
+ const u = s.get(d.pattern);
414
+ u !== void 0 && o.push(u);
415
415
  }
416
- }, r = i[0];
417
- if (r !== void 0) {
418
- const f = s.byHead.get(r);
419
- f !== void 0 && a(f);
416
+ }, i = r[0];
417
+ if (i !== void 0) {
418
+ const a = n.byHead.get(i);
419
+ a !== void 0 && l(a);
420
420
  }
421
- return a(s.anyHead), d;
421
+ return l(n.anyHead), o;
422
422
  }
423
423
  /**
424
424
  * Pattern matcher over dot-separated segments, which arrive already split.
@@ -449,18 +449,18 @@ class H {
449
449
  * @internal
450
450
  */
451
451
  matchSegments(e, t) {
452
- let n = 0, s = 0, i = -1, d = 0;
453
- for (; s < t.length; )
454
- if (n < e.length && (e[n] === "*" || e[n] === t[s]))
455
- n++, s++;
456
- else if (n < e.length && e[n] === "**")
457
- i = n, d = s, n++;
458
- else if (i !== -1)
459
- n = i + 1, s = ++d;
452
+ let s = 0, n = 0, r = -1, o = 0;
453
+ for (; n < t.length; )
454
+ if (s < e.length && (e[s] === "*" || e[s] === t[n]))
455
+ s++, n++;
456
+ else if (s < e.length && e[s] === "**")
457
+ r = s, o = n, s++;
458
+ else if (r !== -1)
459
+ s = r + 1, n = ++o;
460
460
  else
461
461
  return !1;
462
- for (; n < e.length && e[n] === "**"; ) n++;
463
- return n === e.length;
462
+ for (; s < e.length && e[s] === "**"; ) s++;
463
+ return s === e.length;
464
464
  }
465
465
  /**
466
466
  * Removes **all** listeners (exact and pattern). Useful for tests/HMR teardown.
@@ -485,16 +485,16 @@ class H {
485
485
  */
486
486
  __introspect() {
487
487
  const e = [];
488
- for (const [t, n] of this.handlers)
489
- for (const [s, i] of n)
490
- i.length > 0 && e.push({ channel: t, type: s, count: i.length });
491
- for (const [t, n] of this.patternHandlers)
492
- for (const [s, i] of n)
493
- i.length > 0 && e.push({ channel: t, type: s, count: i.length });
488
+ for (const [t, s] of this.handlers)
489
+ for (const [n, r] of s)
490
+ r.length > 0 && e.push({ channel: t, type: n, count: r.length });
491
+ for (const [t, s] of this.patternHandlers)
492
+ for (const [n, r] of s)
493
+ r.length > 0 && e.push({ channel: t, type: n, count: r.length });
494
494
  return e;
495
495
  }
496
496
  }
497
- class K {
497
+ class V {
498
498
  /**
499
499
  * Creates a new {@link Reducer} from a pure reducer function.
500
500
  *
@@ -523,7 +523,7 @@ class K {
523
523
  *
524
524
  * @param state - Current state.
525
525
  * @param event - An event drawn from {@link EventUnion | `EventUnion<EM>`}.
526
- * @returns The next state produced by the underlying reducer function.
526
+ * @returns The next state, or a {@link Rejection} if the reducer refused the write.
527
527
  *
528
528
  * @example
529
529
  * ```ts
@@ -536,128 +536,258 @@ class K {
536
536
  return this._reduce(e, t);
537
537
  }
538
538
  }
539
- const k = /* @__PURE__ */ new Set();
540
- function M(o, e) {
541
- const t = o ? `${o}.${e}` : e;
542
- k.has(t) || (k.add(t), console.warn(
543
- `[yoltra] State key "${e}"${o ? ` under "${o}"` : ""} contains a dot. Paths are dotted, so this key is indistinguishable from nested objects of the same name: a subscription to "${t}" may match the wrong value, and DevTools patches for it will address the wrong node. Rename the key, or nest it.`
539
+ const T = /* @__PURE__ */ new Set();
540
+ function O(c, e) {
541
+ const t = c ? `${c}.${e}` : e;
542
+ T.has(t) || (T.add(t), console.warn(
543
+ `[yoltra] State key "${e}"${c ? ` under "${c}"` : ""} contains a dot. Paths are dotted, so this key is indistinguishable from nested objects of the same name: a subscription to "${t}" may match the wrong value, and DevTools patches for it will address the wrong node. Rename the key, or nest it.`
544
544
  ));
545
545
  }
546
- function O(o, e, t = "", n = /* @__PURE__ */ new Map()) {
547
- const s = [];
548
- return v(o, e, t, n, s), s;
546
+ function I(c, e, t = "", s = /* @__PURE__ */ new Map()) {
547
+ const n = [];
548
+ return P(c, e, t, s, n), n;
549
549
  }
550
- function v(o, e, t, n, s) {
551
- if (o === e) return;
552
- if (typeof o != "object" || typeof e != "object" || o === null || e === null) {
553
- if (typeof o == "number" && Number.isNaN(o) && Number.isNaN(e))
550
+ function P(c, e, t, s, n) {
551
+ if (c === e) return;
552
+ if (typeof c != "object" || typeof e != "object" || c === null || e === null) {
553
+ if (typeof c == "number" && Number.isNaN(c) && Number.isNaN(e))
554
554
  return;
555
- s.push(t);
555
+ n.push(t);
556
556
  return;
557
557
  }
558
- if (o instanceof Date && e instanceof Date) {
559
- o.getTime() !== e.getTime() && s.push(t);
558
+ if (c instanceof Date && e instanceof Date) {
559
+ c.getTime() !== e.getTime() && n.push(t);
560
560
  return;
561
561
  }
562
- if (o instanceof RegExp && e instanceof RegExp) {
563
- (o.source !== e.source || e.flags !== o.flags) && s.push(t);
562
+ if (c instanceof RegExp && e instanceof RegExp) {
563
+ (c.source !== e.source || e.flags !== c.flags) && n.push(t);
564
564
  return;
565
565
  }
566
- if (o instanceof Map || e instanceof Map) {
567
- s.push(t);
566
+ if (c instanceof Map || e instanceof Map) {
567
+ n.push(t);
568
568
  return;
569
569
  }
570
- if (o instanceof Set || e instanceof Set) {
571
- s.push(t);
570
+ if (c instanceof Set || e instanceof Set) {
571
+ n.push(t);
572
572
  return;
573
573
  }
574
- const i = o, d = e, a = n.get(i);
575
- if (a?.has(d)) return;
576
- const r = a ?? /* @__PURE__ */ new Set();
577
- r.add(d), a || n.set(i, r);
574
+ const r = c, o = e, l = s.get(r);
575
+ if (l?.has(o)) return;
576
+ const i = l ?? /* @__PURE__ */ new Set();
577
+ i.add(o), l || s.set(r, i);
578
578
  try {
579
- const f = Array.isArray(o), c = Array.isArray(e);
580
- if (f !== c) {
581
- s.push(t);
579
+ const a = Array.isArray(c), d = Array.isArray(e);
580
+ if (a !== d) {
581
+ n.push(t);
582
582
  return;
583
583
  }
584
- if (f) {
585
- const h = o, m = e;
586
- h.length !== m.length && t && s.push(t);
587
- const g = Math.min(h.length, m.length);
584
+ if (a) {
585
+ const h = c, y = e;
586
+ h.length !== y.length && t && n.push(t);
587
+ const g = Math.min(h.length, y.length);
588
588
  for (let w = 0; w < g; w++)
589
- h[w] !== m[w] && v(h[w], m[w], t ? `${t}.${w}` : `${w}`, n, s);
590
- for (let w = g; w < Math.max(h.length, m.length); w++)
591
- s.push(t ? `${t}.${w}` : `${w}`);
589
+ h[w] !== y[w] && P(h[w], y[w], t ? `${t}.${w}` : `${w}`, s, n);
590
+ for (let w = g; w < Math.max(h.length, y.length); w++)
591
+ n.push(t ? `${t}.${w}` : `${w}`);
592
592
  return;
593
593
  }
594
- const u = Object.keys(o), l = Object.keys(e);
595
- if (u.length === 0 && l.length === 0) {
596
- s.push(t);
594
+ const u = Object.keys(c), f = Object.keys(e);
595
+ if (u.length === 0 && f.length === 0) {
596
+ n.push(t);
597
597
  return;
598
598
  }
599
- let y = u.length === l.length;
600
- if (y) {
601
- for (let h = 0; h < l.length; h++)
602
- if (!Object.prototype.hasOwnProperty.call(o, l[h])) {
603
- y = !1;
599
+ let m = u.length === f.length;
600
+ if (m) {
601
+ for (let h = 0; h < f.length; h++)
602
+ if (!Object.prototype.hasOwnProperty.call(c, f[h])) {
603
+ m = !1;
604
604
  break;
605
605
  }
606
606
  }
607
- if (y) {
608
- for (const h of l)
609
- o[h] !== e[h] && (process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h), v(o[h], e[h], t ? `${t}.${h}` : h, n, s));
607
+ if (m) {
608
+ for (const h of f)
609
+ c[h] !== e[h] && (process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h), P(c[h], e[h], t ? `${t}.${h}` : h, s, n));
610
610
  return;
611
611
  }
612
- for (const h of l) {
613
- const m = Object.prototype.hasOwnProperty.call(o, h);
614
- if (m && o[h] === e[h]) continue;
615
- process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h);
612
+ for (const h of f) {
613
+ const y = Object.prototype.hasOwnProperty.call(c, h);
614
+ if (y && c[h] === e[h]) continue;
615
+ process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h);
616
616
  const g = t ? `${t}.${h}` : h;
617
- if (!m) {
618
- s.push(g);
617
+ if (!y) {
618
+ n.push(g);
619
619
  continue;
620
620
  }
621
- v(o[h], e[h], g, n, s);
621
+ P(c[h], e[h], g, s, n);
622
622
  }
623
623
  for (const h of u)
624
- Object.prototype.hasOwnProperty.call(e, h) || (process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h), s.push(t ? `${t}.${h}` : h));
624
+ Object.prototype.hasOwnProperty.call(e, h) || (process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h), n.push(t ? `${t}.${h}` : h));
625
625
  } finally {
626
- r.delete(d), r.size === 0 && n.delete(i);
626
+ i.delete(o), i.size === 0 && s.delete(r);
627
627
  }
628
628
  }
629
- function S(o, e = /* @__PURE__ */ new WeakSet(), t) {
630
- if (o === null || typeof o != "object" || e.has(o) || (t !== void 0 && o === t.watch && t.onFound(), Object.isFrozen(o))) return o;
631
- if (e.add(o), Array.isArray(o)) {
632
- const n = o;
633
- for (let s = 0; s < n.length; s++)
634
- n[s] = S(n[s], e, t);
635
- return Object.freeze(n);
636
- }
637
- for (const n of Object.getOwnPropertyNames(o)) {
638
- const s = Object.getOwnPropertyDescriptor(o, n);
639
- !s || !("value" in s) || (o[n] = S(o[n], e, t));
640
- }
641
- for (const n of Object.getOwnPropertySymbols(o)) {
642
- const s = Object.getOwnPropertyDescriptor(o, n);
643
- !s || !("value" in s) || (o[n] = S(o[n], e, t));
644
- }
645
- return Object.freeze(o);
629
+ function M(c, e = /* @__PURE__ */ new WeakSet(), t) {
630
+ if (c === null || typeof c != "object" || e.has(c) || (t !== void 0 && c === t.watch && t.onFound(), Object.isFrozen(c))) return c;
631
+ if (e.add(c), Array.isArray(c)) {
632
+ const s = c;
633
+ for (let n = 0; n < s.length; n++)
634
+ s[n] = M(s[n], e, t);
635
+ return Object.freeze(s);
636
+ }
637
+ for (const s of Object.getOwnPropertyNames(c)) {
638
+ const n = Object.getOwnPropertyDescriptor(c, s);
639
+ !n || !("value" in n) || (c[s] = M(c[s], e, t));
640
+ }
641
+ for (const s of Object.getOwnPropertySymbols(c)) {
642
+ const n = Object.getOwnPropertyDescriptor(c, s);
643
+ !n || !("value" in n) || (c[s] = M(c[s], e, t));
644
+ }
645
+ return Object.freeze(c);
646
+ }
647
+ const N = /* @__PURE__ */ Symbol.for("yoltra.rejected");
648
+ function le(c) {
649
+ return { [N]: !0, reason: c };
650
+ }
651
+ function Q(c) {
652
+ return typeof c == "object" && c !== null && c[N] === !0;
646
653
  }
647
- function W(o, e) {
654
+ class G {
655
+ constructor(e) {
656
+ p(this, "highWaterMark");
657
+ p(this, "buffer", []);
658
+ /** Consumers parked in `take`, oldest first. */
659
+ p(this, "takers", []);
660
+ /** Producers parked in `put`, each with the item they are waiting to hand over. */
661
+ p(this, "putters", []);
662
+ p(this, "consuming", !1);
663
+ /** No more items will be accepted, but what is already here is still owed to the consumer. */
664
+ p(this, "ended", !1);
665
+ /** Abandoned: nothing further is owed to anybody. */
666
+ p(this, "closed", !1);
667
+ /** Items discarded because nobody was iterating and the buffer was full. */
668
+ p(this, "dropped", 0);
669
+ this.highWaterMark = e;
670
+ }
671
+ /** How many items were discarded for want of a consumer. */
672
+ get droppedCount() {
673
+ return this.dropped;
674
+ }
675
+ /**
676
+ * Marks that a consumer has started pulling. From here on, a full buffer parks the producer
677
+ * rather than dropping.
678
+ */
679
+ beginConsuming() {
680
+ this.consuming = !0;
681
+ }
682
+ /**
683
+ * Offers an item. The returned promise settles when the item has been taken — or immediately,
684
+ * if it fit in the buffer or was dropped.
685
+ */
686
+ put(e) {
687
+ if (this.closed || this.ended) return Promise.resolve();
688
+ const t = this.takers.shift();
689
+ return t !== void 0 ? (t({ value: e, done: !1 }), Promise.resolve()) : this.buffer.length < this.highWaterMark ? (this.buffer.push(e), Promise.resolve()) : this.consuming ? new Promise((s) => {
690
+ this.putters.push({ item: e, release: s });
691
+ }) : (this.dropped++, Promise.resolve());
692
+ }
693
+ /** Takes the next item, waiting if none is available. Resolves `done` once closed and drained. */
694
+ take() {
695
+ this.consuming = !0;
696
+ const e = this.buffer.shift();
697
+ if (e !== void 0) {
698
+ const s = this.putters.shift();
699
+ return s !== void 0 && (this.buffer.push(s.item), s.release()), Promise.resolve({ value: e, done: !1 });
700
+ }
701
+ const t = this.putters.shift();
702
+ return t !== void 0 ? (t.release(), Promise.resolve({ value: t.item, done: !1 })) : this.closed || this.ended ? Promise.resolve({ value: void 0, done: !0 }) : new Promise((s) => {
703
+ this.takers.push(s);
704
+ });
705
+ }
706
+ /**
707
+ * Stops accepting items, but keeps owing the consumer everything already queued.
708
+ *
709
+ * @remarks
710
+ * What the terminal reply does. Closing outright at that moment would throw away progress the
711
+ * responder had already handed over and the consumer had not yet read — which is exactly what
712
+ * happened before this existed: a six-step job delivered five steps, because the sixth was in
713
+ * the buffer when `done` arrived and the buffer was cleared. The terminal event says "no more
714
+ * is coming", not "forget what you were given".
715
+ */
716
+ end() {
717
+ if (this.ended || this.closed) return;
718
+ this.ended = !0;
719
+ let e = this.putters.shift();
720
+ for (; e !== void 0; )
721
+ this.buffer.push(e.item), e.release(), e = this.putters.shift();
722
+ let t = this.takers.shift();
723
+ for (; t !== void 0; ) {
724
+ const s = this.buffer.shift();
725
+ t(
726
+ s !== void 0 ? { value: s, done: !1 } : { value: void 0, done: !0 }
727
+ ), t = this.takers.shift();
728
+ }
729
+ }
730
+ /**
731
+ * Closes the queue: waiting consumers are told `done`, and **every parked producer is
732
+ * released**.
733
+ *
734
+ * @remarks
735
+ * Releasing producers is not tidying up. A producer parked on `put` is a pending `await emit`
736
+ * somewhere; leaving it parked when the call has already settled would hang the responder for
737
+ * good — turning a timed-out call into a wedged process, which is worse than the problem
738
+ * backpressure was added to solve.
739
+ */
740
+ close() {
741
+ if (this.closed) return;
742
+ this.closed = !0, this.buffer.length = 0;
743
+ let e = this.takers.shift();
744
+ for (; e !== void 0; )
745
+ e({ value: void 0, done: !0 }), e = this.takers.shift();
746
+ let t = this.putters.shift();
747
+ for (; t !== void 0; )
748
+ t.release(), t = this.putters.shift();
749
+ }
750
+ }
751
+ class J extends Error {
752
+ constructor(t, s, n) {
753
+ super(
754
+ `[yoltra] call to "${t}/${s}" saw no correlated reply for ${n}ms. The timeout is idle rather than total, so this means the responder went quiet, not that it was slow. Check that something handles "${t}/${s}" and that its reply is emitted through the \`emit\` it was handed — a reply emitted from an unrelated context carries no causal link, and needs an explicit correlationId instead.`
755
+ );
756
+ p(this, "channel");
757
+ p(this, "type");
758
+ p(this, "idleMs");
759
+ this.name = "CallTimeoutError", this.channel = t, this.type = s, this.idleMs = n;
760
+ }
761
+ }
762
+ class C extends Error {
763
+ constructor(e) {
764
+ super(`[yoltra] call aborted: ${e}`), this.name = "CallAbortedError";
765
+ }
766
+ }
767
+ function q(c) {
768
+ const [e, t] = c;
769
+ if (t === void 0) return { channel: e, isTerminal: () => !0 };
770
+ if (typeof t == "string") return { channel: e, isTerminal: (n) => n === t };
771
+ const s = new Set(t);
772
+ return { channel: e, isTerminal: (n) => s.has(n) };
773
+ }
774
+ function Y(c, e, t) {
775
+ return c.parentId === e ? !0 : t === void 0 ? !1 : c.meta?.correlationId === t;
776
+ }
777
+ function X(c, e) {
648
778
  try {
649
779
  return structuredClone(e);
650
780
  } catch (t) {
651
781
  throw new Error(
652
- `[yoltra] Initial state for slice "${String(o)}" could not be copied: ${t instanceof Error ? t.message : String(t)}. State must be structured-cloneable — functions, class instances and DOM nodes are not. Keep behaviour out of state and store plain data.`
782
+ `[yoltra] Initial state for slice "${String(c)}" could not be copied: ${t instanceof Error ? t.message : String(t)}. State must be structured-cloneable — functions, class instances and DOM nodes are not. Keep behaviour out of state and store plain data.`
653
783
  );
654
784
  }
655
785
  }
656
- function P(o, e) {
657
- return process.env.NODE_ENV === "production" ? o : S(o, /* @__PURE__ */ new WeakSet(), e);
786
+ function R(c, e) {
787
+ return process.env.NODE_ENV === "production" ? c : M(c, /* @__PURE__ */ new WeakSet(), e);
658
788
  }
659
- const x = 100, z = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
660
- class $ {
789
+ const D = 100, Z = 64, z = 16, ee = 3e4, te = 16, $ = Object.freeze({ committed: !1, written: !1 }), ne = Object.freeze({ committed: !0, written: !1 }), se = Object.freeze({ committed: !0, written: !0 }), A = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
790
+ class x {
661
791
  /**
662
792
  * Creates a store from a {@link StoreSpec}.
663
793
  *
@@ -746,6 +876,18 @@ class $ {
746
876
  *
747
877
  * @internal
748
878
  */
879
+ /**
880
+ * Subscribers to events that actually changed state, notified after the commit.
881
+ *
882
+ * @remarks
883
+ * Separate from `committedEventSubscribers` rather than a filter over it, because the two
884
+ * answer different questions and one of them is load bearing: `committed` means "not vetoed"
885
+ * and fires for every event a store accepts, including every event in a store with no
886
+ * reducers. Narrowing it would have silently stopped toasts and analytics firing.
887
+ *
888
+ * @internal
889
+ */
890
+ p(this, "writtenEventSubscribers", /* @__PURE__ */ new Map());
749
891
  p(this, "allEventSubscribers", /* @__PURE__ */ new Map());
750
892
  /**
751
893
  * Track reducerBus unsubs per slice for HMR/register/unregister.
@@ -808,6 +950,36 @@ class $ {
808
950
  * @internal
809
951
  */
810
952
  p(this, "isReducing", !1);
953
+ /**
954
+ * The event currently being reduced, or `null` outside the drain.
955
+ *
956
+ * @remarks
957
+ * This is what makes causality exact rather than best-effort. The drain is synchronous — no
958
+ * `await` can interleave — so any `emit` that arrives while it is set is, without ambiguity, a
959
+ * consequence of this event. That catches the case a scoped `emit` closure cannot: a
960
+ * middleware or subscriber that captured the store and calls `store.emit` directly instead of
961
+ * using the injected one. Attribution should not depend on which reference a consumer reached
962
+ * for.
963
+ *
964
+ * @internal
965
+ */
966
+ p(this, "currentEvent", null);
967
+ /**
968
+ * Events processed by the drain currently in progress. Compared against
969
+ * `maxTransitionsPerDrain`, which is off unless configured.
970
+ *
971
+ * @internal
972
+ */
973
+ p(this, "transitionsThisDrain", 0);
974
+ /**
975
+ * Ceilings that stop a cascade from becoming a hung process. See {@link StoreSpec.maxReduceDepth}.
976
+ *
977
+ * @internal
978
+ */
979
+ p(this, "maxReduceDepth");
980
+ p(this, "maxTransitionsPerDrain");
981
+ p(this, "onCascade");
982
+ p(this, "onRejected");
811
983
  /**
812
984
  * Registered instrumentation observers (DevTools seam). See {@link instrument}.
813
985
  *
@@ -817,11 +989,24 @@ class $ {
817
989
  /**
818
990
  * Scratch array collecting slice-prefixed changed leaf paths during an
819
991
  * instrumented reduce. Set by {@link drainReduce} while observers are active;
820
- * appended to by {@link forwardEvent}. `null` when not instrumenting.
992
+ * appended to by {@link commitStaged}. `null` when not instrumenting.
821
993
  *
822
994
  * @internal
823
995
  */
824
996
  p(this, "changedPathSink", null);
997
+ /**
998
+ * Where keyed reducers put their pending writes during a reduce, and the refusal one of them
999
+ * returned.
1000
+ *
1001
+ * @remarks
1002
+ * Keyed reducers are invoked through `reducerBus`, which delivers to handlers and has no way
1003
+ * to hand a value back — the same reason `changedPathSink` exists. `null` outside a reduce.
1004
+ *
1005
+ * @internal
1006
+ */
1007
+ p(this, "stagingSink", null);
1008
+ p(this, "stagedRejection", null);
1009
+ p(this, "stagedRejectedBy", "");
825
1010
  /**
826
1011
  * Count of effect tasks currently in flight; surfaced as queue depth by
827
1012
  * {@link __devtoolsIntrospect}.
@@ -874,15 +1059,15 @@ class $ {
874
1059
  * @internal
875
1060
  */
876
1061
  p(this, "eventCleanupTimer", null);
877
- if (this.name = e.name ?? "yoltra Store", this.reducerBus = new B(), this.connectorBus = new H(), this.middleware = [...e.middleware ?? []], this.reducers = {}, this.state = {}, this.replayEnabled = e.devtools?.allowReplay ?? !1, this.idFactory = e.idFactory ?? (() => crypto.randomUUID()), this.onEffectError = e.onEffectError, this.onReducerError = e.onReducerError, this.dedupConfig = {
1062
+ if (this.name = e.name ?? "yoltra Store", this.reducerBus = new U(), this.connectorBus = new L(), this.middleware = [...e.middleware ?? []], this.reducers = {}, this.state = {}, this.replayEnabled = e.devtools?.allowReplay ?? !1, this.idFactory = e.idFactory ?? (() => crypto.randomUUID()), this.onEffectError = e.onEffectError, this.onReducerError = e.onReducerError, this.maxReduceDepth = e.maxReduceDepth ?? Z, this.maxTransitionsPerDrain = e.maxTransitionsPerDrain ?? 1 / 0, this.onCascade = e.onCascade, this.onRejected = e.onRejected, this.dedupConfig = {
878
1063
  windowMs: e.dedupWindowMs ?? 0,
879
1064
  maxCacheSize: 1e3
880
- }, Object.entries(e.reducer).forEach(([t, n]) => {
881
- this.mountSlice(t, n, { preserveState: !1 });
1065
+ }, Object.entries(e.reducer).forEach(([t, s]) => {
1066
+ this.mountSlice(t, s, { preserveState: !1 });
882
1067
  }), e.effects?.length)
883
1068
  for (const t of e.effects)
884
1069
  this.registerEffect(t);
885
- this.dispose = this.dispose.bind(this), this.notifyEffects = this.notifyEffects.bind(this), this.forwardEvent = this.forwardEvent.bind(this), this.__applyExternalState = this.__applyExternalState.bind(this), this.__replayEvents = this.__replayEvents.bind(this), this.__devtoolsIntrospect = this.__devtoolsIntrospect.bind(this), this.mountSlice = this.mountSlice.bind(this), this.unmountSlice = this.unmountSlice.bind(this), this.getAtPath = this.getAtPath.bind(this), this.emit = this.emit.bind(this), this.subscribe = this.subscribe.bind(this), this.connect = this.connect.bind(this), this.onEffect = this.onEffect.bind(this), this.onEvent = this.onEvent.bind(this), this.getState = this.getState.bind(this), this.registerEffect = this.registerEffect.bind(this), this.registerMiddleware = this.registerMiddleware.bind(this), this.registerReducer = this.registerReducer.bind(this), this.replaceMiddleware = this.replaceMiddleware.bind(this), this.replaceEffects = this.replaceEffects.bind(this), this.replaceReducers = this.replaceReducers.bind(this), this.hotReplace = this.hotReplace.bind(this);
1070
+ this.dispose = this.dispose.bind(this), this.notifyEffects = this.notifyEffects.bind(this), this.__applyExternalState = this.__applyExternalState.bind(this), this.__replayEvents = this.__replayEvents.bind(this), this.__devtoolsIntrospect = this.__devtoolsIntrospect.bind(this), this.mountSlice = this.mountSlice.bind(this), this.unmountSlice = this.unmountSlice.bind(this), this.getAtPath = this.getAtPath.bind(this), this.emit = this.emit.bind(this), this.subscribe = this.subscribe.bind(this), this.connect = this.connect.bind(this), this.onEffect = this.onEffect.bind(this), this.onEvent = this.onEvent.bind(this), this.getState = this.getState.bind(this), this.registerEffect = this.registerEffect.bind(this), this.registerMiddleware = this.registerMiddleware.bind(this), this.registerReducer = this.registerReducer.bind(this), this.replaceMiddleware = this.replaceMiddleware.bind(this), this.replaceEffects = this.replaceEffects.bind(this), this.replaceReducers = this.replaceReducers.bind(this), this.hotReplace = this.hotReplace.bind(this);
886
1071
  }
887
1072
  /**
888
1073
  * Cleanup resources (timers, etc.) when disposing the store.
@@ -898,7 +1083,7 @@ class $ {
898
1083
  * @public
899
1084
  */
900
1085
  dispose() {
901
- this.eventCleanupTimer && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null), this.processedEvents.clear(), this.effects.clear(), this.patternEffects.clear(), this.effectMeta = /* @__PURE__ */ new WeakMap(), this.warnedPayloadAliases.clear(), this.listeners.clear(), this.committedEventSubscribers.clear(), this.uncommittedEventSubscribers.clear(), this.allEventSubscribers.clear(), this.instrumentObservers.clear(), this.connectorBus.clear(), this.reducerBus.clear(), this.patternReducers.clear(), this.sliceUnsubs.clear(), this.changedPathSink = null;
1086
+ this.eventCleanupTimer && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null), this.processedEvents.clear(), this.effects.clear(), this.patternEffects.clear(), this.effectMeta = /* @__PURE__ */ new WeakMap(), this.warnedPayloadAliases.clear(), this.listeners.clear(), this.committedEventSubscribers.clear(), this.uncommittedEventSubscribers.clear(), this.writtenEventSubscribers.clear(), this.allEventSubscribers.clear(), this.instrumentObservers.clear(), this.connectorBus.clear(), this.reducerBus.clear(), this.patternReducers.clear(), this.sliceUnsubs.clear(), this.changedPathSink = null;
902
1087
  }
903
1088
  /**
904
1089
  * Generates a fingerprint for an event for deduplication purposes.
@@ -911,17 +1096,17 @@ class $ {
911
1096
  *
912
1097
  * @internal
913
1098
  */
914
- fingerprint(e, t, n) {
915
- const s = `${e}::${t}`;
1099
+ fingerprint(e, t, s) {
1100
+ const n = `${e}::${t}`;
916
1101
  try {
917
- if (n == null)
918
- return `${s}::null`;
919
- if (typeof n != "object")
920
- return `${s}::${String(n)}`;
921
- const i = JSON.stringify(n);
922
- return `${s}::${i}`;
1102
+ if (s == null)
1103
+ return `${n}::null`;
1104
+ if (typeof s != "object")
1105
+ return `${n}::${String(s)}`;
1106
+ const r = JSON.stringify(s);
1107
+ return `${n}::${r}`;
923
1108
  } catch {
924
- return `${s}::${Date.now()}::${Math.random()}`;
1109
+ return `${n}::${Date.now()}::${Math.random()}`;
925
1110
  }
926
1111
  }
927
1112
  /**
@@ -934,8 +1119,8 @@ class $ {
934
1119
  * @internal
935
1120
  */
936
1121
  shouldDedupe(e, t) {
937
- const n = Date.now(), s = this.processedEvents.get(e);
938
- return s !== void 0 && n - s < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, n), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(n), !1);
1122
+ const s = Date.now(), n = this.processedEvents.get(e);
1123
+ return n !== void 0 && s - n < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, s), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(s), !1);
939
1124
  }
940
1125
  /**
941
1126
  * Starts the periodic prune interval if it isn't already running. Called when
@@ -957,11 +1142,32 @@ class $ {
957
1142
  * @internal
958
1143
  */
959
1144
  pruneProcessedEvents(e) {
960
- const t = Math.max(this.dedupConfig.windowMs, x), n = e - t * 2;
961
- for (const [s, i] of this.processedEvents)
962
- i < n && this.processedEvents.delete(s);
1145
+ const t = Math.max(this.dedupConfig.windowMs, D), s = e - t * 2;
1146
+ for (const [n, r] of this.processedEvents)
1147
+ r < s && this.processedEvents.delete(n);
963
1148
  this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
964
1149
  }
1150
+ /**
1151
+ * Reports a breached ceiling and refuses the emit.
1152
+ *
1153
+ * @remarks
1154
+ * Console *and* hook, matching how reducer and effect errors are reported: a cascade is a
1155
+ * wiring bug, and the console line is what a developer who has not registered a hook will
1156
+ * actually see. Without one, refusing the emit would look exactly like the event never having
1157
+ * been emitted at all — which is the invisibility this whole guard exists to end.
1158
+ *
1159
+ * @internal
1160
+ */
1161
+ reportCascade(e, t, s, n, r) {
1162
+ console.error(
1163
+ `[yoltra] Cascade stopped: "${s.channel}/${s.type}" would exceed ${e} (${t}). This event was refused and the chain ends here. A chain this long is almost always two consumers emitting into each other — check what reacts to "${s.channel}/${s.type}" and what that emits in turn.` + (r.length > 0 ? ` Recent causal chain: ${r.join(" → ")} → (refused).` : "")
1164
+ );
1165
+ try {
1166
+ this.onCascade?.({ limit: e, limitValue: t, event: s, depth: n, chain: r });
1167
+ } catch (o) {
1168
+ console.error("onCascade handler error:", o);
1169
+ }
1170
+ }
965
1171
  /**
966
1172
  * Checks if an event matches a `When` matcher.
967
1173
  *
@@ -980,7 +1186,7 @@ class $ {
980
1186
  */
981
1187
  matchesWhen(e, t) {
982
1188
  return !e || "any" in e && e.any === !0 ? !0 : "keys" in e ? e.keys.some(
983
- ([n, s]) => t.channel === n && t.type === s
1189
+ ([s, n]) => t.channel === s && t.type === n
984
1190
  ) : "channel" in e ? t.channel === e.channel : "channels" in e ? e.channels.includes(t.channel) : !1;
985
1191
  }
986
1192
  /**
@@ -1016,22 +1222,40 @@ class $ {
1016
1222
  * @internal
1017
1223
  */
1018
1224
  async notifyEffects(e) {
1019
- const t = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(t);
1225
+ const t = this.scopedEmit(e), s = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(s);
1020
1226
  if (n && n.size > 0)
1021
- for (const s of [...n])
1227
+ for (const r of [...n])
1022
1228
  try {
1023
- await s(e, this.getState, this.emit);
1024
- } catch (i) {
1025
- console.error("Effect error:", i), this.onEffectError?.(i, e);
1229
+ await r(e, this.getState, t);
1230
+ } catch (o) {
1231
+ console.error("Effect error:", o), this.onEffectError?.(o, e);
1026
1232
  }
1027
- for (const { effect: s, when: i } of this.patternEffects)
1028
- if (this.matchesWhen(i, e))
1233
+ for (const { effect: r, when: o } of this.patternEffects)
1234
+ if (this.matchesWhen(o, e))
1029
1235
  try {
1030
- await s(e, this.getState, this.emit);
1031
- } catch (d) {
1032
- console.error("Effect error:", d), this.onEffectError?.(d, e);
1236
+ await r(e, this.getState, t);
1237
+ } catch (l) {
1238
+ console.error("Effect error:", l), this.onEffectError?.(l, e);
1033
1239
  }
1034
1240
  }
1241
+ /**
1242
+ * An `emit` that attributes whatever it sends to `cause`.
1243
+ *
1244
+ * @remarks
1245
+ * Built per event rather than per effect: every effect reacting to one event shares a cause,
1246
+ * and one closure is cheaper than one per handler on a path that runs for every committed
1247
+ * event.
1248
+ *
1249
+ * @internal
1250
+ */
1251
+ scopedEmit(e) {
1252
+ const t = {
1253
+ id: e.id,
1254
+ depth: e.depth ?? 0,
1255
+ chain: [...this.currentEvent?.chain ?? [], e.id].slice(-z)
1256
+ };
1257
+ return ((s, n, r, o) => this.emitCaused(t, s, n, r, o));
1258
+ }
1035
1259
  /**
1036
1260
  * Notifies event subscribers for a specific phase.
1037
1261
  *
@@ -1043,12 +1267,13 @@ class $ {
1043
1267
  * @internal
1044
1268
  */
1045
1269
  notifyEventSubscribers(e, t) {
1046
- const n = `${String(e.channel)}::${String(e.type)}`, i = (t === "committed" ? this.committedEventSubscribers : this.uncommittedEventSubscribers).get(n);
1047
- if (i?.size)
1048
- for (const a of [...i]) this.invokeEventSubscriber(a, e, t);
1049
- const d = this.allEventSubscribers.get(n);
1050
- if (d?.size)
1051
- for (const a of [...d]) this.invokeEventSubscriber(a, e, t);
1270
+ const s = `${String(e.channel)}::${String(e.type)}`, r = (t === "committed" ? this.committedEventSubscribers : t === "written" ? this.writtenEventSubscribers : this.uncommittedEventSubscribers).get(s);
1271
+ if (r?.size)
1272
+ for (const l of [...r]) this.invokeEventSubscriber(l, e, t);
1273
+ if (t === "written") return;
1274
+ const o = this.allEventSubscribers.get(s);
1275
+ if (o?.size)
1276
+ for (const l of [...o]) this.invokeEventSubscriber(l, e, t);
1052
1277
  }
1053
1278
  /**
1054
1279
  * Invokes a single event-subscription handler **fire-and-forget**: synchronous
@@ -1057,12 +1282,12 @@ class $ {
1057
1282
  *
1058
1283
  * @internal
1059
1284
  */
1060
- invokeEventSubscriber(e, t, n) {
1285
+ invokeEventSubscriber(e, t, s) {
1061
1286
  try {
1062
- const s = e(t, this.getState, this.emit, n);
1063
- s && typeof s.then == "function" && s.catch((i) => console.error("Event subscription error:", i));
1064
- } catch (s) {
1065
- console.error("Event subscription error:", s);
1287
+ const n = e(t, this.getState, this.emit, s);
1288
+ n && typeof n.then == "function" && n.catch((r) => console.error("Event subscription error:", r));
1289
+ } catch (n) {
1290
+ console.error("Event subscription error:", n);
1066
1291
  }
1067
1292
  }
1068
1293
  /**
@@ -1100,53 +1325,116 @@ class $ {
1100
1325
  * pattern reducer's throw aborted the commit and notified nobody, not even the uncommitted
1101
1326
  * subscribers a veto would have reached.
1102
1327
  *
1103
- * The semantics are now the same either way: **the failing slice is isolated.** Its state is
1328
+ * The semantics are the same either way: **the failing slice is isolated.** Its state is
1104
1329
  * unchanged, every other slice still reduces, and the event still commits if anything else
1105
- * changed. Rolling the whole event back would be tidier in principle, but fine-grained
1106
- * subscribers are notified inside `forwardEvent` as each slice commits, so an event that
1107
- * reverted afterwards would have already told components about a value that no longer exists.
1108
- * Isolation keeps every notification truthful.
1330
+ * changed.
1331
+ *
1332
+ * That is deliberately *not* what a {@link Rejected} refusal does, which discards the whole
1333
+ * event. A crash and a refusal are different acts: a reducer that throws has a bug and should
1334
+ * not be able to veto its neighbours' work, while a reducer that refuses has made a decision
1335
+ * and must be able to.
1336
+ *
1337
+ * This once argued that rolling back was untenable, because subscribers were notified as each
1338
+ * slice committed and a later revert would have told them about a value that no longer
1339
+ * existed. Staging removed that obstacle — nothing is notified until every slice is written —
1340
+ * which is what made refusal possible at all.
1109
1341
  *
1110
1342
  * @internal
1111
1343
  */
1112
- forwardEventGuarded(e, t) {
1344
+ stageSliceGuarded(e, t, s) {
1113
1345
  try {
1114
- return this.forwardEvent(e, t);
1346
+ return this.stageSlice(e, t, s);
1115
1347
  } catch (n) {
1116
- return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), !1;
1348
+ return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), null;
1117
1349
  }
1118
1350
  }
1119
- forwardEvent(e, t) {
1120
- const n = this.state[e], s = this.reducers[e].reduce(n, t);
1121
- if (n === s) return !1;
1122
- const i = O(n, s);
1123
- if (i.length === 0) return !1;
1124
- const d = t.payload, a = process.env.NODE_ENV !== "production" && d !== null && typeof d == "object" ? {
1125
- watch: d,
1351
+ /**
1352
+ * Runs one slice's reducer and records what it *would* write. Writes nothing.
1353
+ *
1354
+ * @returns The reducer's {@link Rejection} if it refused, otherwise `null`.
1355
+ *
1356
+ * @remarks
1357
+ * The staging half of the write path. Nothing here touches `this.state` or notifies anybody,
1358
+ * which is what lets the event be refused after every reducer has had its say — a decision
1359
+ * that has to see the whole diff cannot be made one slice at a time.
1360
+ *
1361
+ * Freezing happens here rather than at commit because it is where the new value is built, and
1362
+ * the freeze is a no-op on anything already frozen; a staged slice that never commits is
1363
+ * discarded frozen, which costs nothing and keeps the committed path free of a second walk.
1364
+ *
1365
+ * @internal
1366
+ */
1367
+ stageSlice(e, t, s) {
1368
+ const n = this.state[e], r = this.reducers[e].reduce(n, t);
1369
+ if (Q(r)) return r;
1370
+ if (n === r) return null;
1371
+ const o = I(n, r);
1372
+ if (o.length === 0) return null;
1373
+ const l = t.payload, i = process.env.NODE_ENV !== "production" && l !== null && typeof l == "object" ? {
1374
+ watch: l,
1126
1375
  onFound: () => {
1127
- const c = `${e}:${t.channel}:${t.type}`;
1128
- this.warnedPayloadAliases.has(c) || (this.warnedPayloadAliases.add(c), console.warn(
1376
+ const a = `${e}:${t.channel}:${t.type}`;
1377
+ this.warnedPayloadAliases.has(a) || (this.warnedPayloadAliases.add(a), console.warn(
1129
1378
  `[yoltra] Slice "${e}" stored the payload of "${t.channel}/${t.type}" by reference. It is now frozen along with the rest of the state, so the emitter mutating it later will throw in development and silently corrupt state in production. Copy the payload in the reducer instead.`
1130
1379
  ));
1131
1380
  }
1132
- } : void 0, r = P(s, a);
1133
- if (this.state = { ...this.state, [e]: r }, this.changedPathSink)
1134
- for (const c of i)
1135
- this.changedPathSink.push(c ? `${e}.${c}` : e);
1136
- const f = /* @__PURE__ */ new Set();
1137
- for (const c of i) {
1138
- if (c === "") {
1139
- f.add("");
1140
- continue;
1381
+ } : void 0;
1382
+ return s.push({
1383
+ name: e,
1384
+ prev: n,
1385
+ frozen: R(r, i),
1386
+ leafPaths: o
1387
+ }), null;
1388
+ }
1389
+ /**
1390
+ * Writes every staged slice, then tells the world — in that order.
1391
+ *
1392
+ * @remarks
1393
+ * The commit half. Assigning all slices under a single new root before any notification goes
1394
+ * out is what closes the window this used to leave open: notifications fired per slice as each
1395
+ * committed, so a subscriber to slice A that read `getState()` could observe slice B of the
1396
+ * *same event* not yet applied. In React that window is real, because the atomic hooks use a
1397
+ * change as a bare signal and then re-read the whole store.
1398
+ *
1399
+ * It is also what makes refusal possible at all. The previous code documented rollback as
1400
+ * untenable precisely because "an event that reverted afterwards would have already told
1401
+ * components about a value that no longer exists" — true when notification and commit were the
1402
+ * same step, and no longer true now that they are not.
1403
+ *
1404
+ * @returns `true` if anything was written.
1405
+ *
1406
+ * @internal
1407
+ */
1408
+ commitStaged(e, t) {
1409
+ if (e.length === 0) return !1;
1410
+ const s = { ...this.state };
1411
+ for (const n of e) s[n.name] = n.frozen;
1412
+ if (this.state = s, this.changedPathSink)
1413
+ for (const n of e)
1414
+ for (const r of n.leafPaths)
1415
+ this.changedPathSink.push(r ? `${n.name}.${r}` : n.name);
1416
+ for (const n of e) {
1417
+ const r = /* @__PURE__ */ new Set();
1418
+ for (const o of n.leafPaths) {
1419
+ if (o === "") {
1420
+ r.add("");
1421
+ continue;
1422
+ }
1423
+ for (const l of x.buildAncestorPaths(o)) r.add(l);
1141
1424
  }
1142
- for (const u of $.buildAncestorPaths(c)) f.add(u);
1425
+ for (const o of r)
1426
+ this.connectorBus.emitWith(n.name, o, () => ({
1427
+ oldValue: this.getAtPath(n.prev, o),
1428
+ newValue: this.getAtPath(n.frozen, o),
1429
+ path: o,
1430
+ // Provenance, built inside the same lazy factory as the values: a subscriber that
1431
+ // needs to know why a value moved no longer has to mirror the cause into state and
1432
+ // keep it there twice.
1433
+ eventId: t.id,
1434
+ channel: t.channel,
1435
+ type: t.type
1436
+ }));
1143
1437
  }
1144
- for (const c of f)
1145
- this.connectorBus.emitWith(e, c, () => ({
1146
- oldValue: this.getAtPath(n, c),
1147
- newValue: this.getAtPath(r, c),
1148
- path: c
1149
- }));
1150
1438
  return !0;
1151
1439
  }
1152
1440
  /**
@@ -1160,65 +1448,65 @@ class $ {
1160
1448
  * @public
1161
1449
  */
1162
1450
  __devtoolsIntrospect() {
1163
- const e = Object.keys(this.reducers).map((a) => {
1164
- const r = this.patternReducers.get(a);
1165
- return { name: a, when: r };
1451
+ const e = Object.keys(this.reducers).map((l) => {
1452
+ const i = this.patternReducers.get(l);
1453
+ return { name: l, when: i };
1166
1454
  }), t = [];
1167
- for (const [a, r] of this.effects) {
1168
- if (r.size === 0) continue;
1169
- const [f, c] = a.split("::");
1170
- for (const u of r) {
1171
- const l = this.effectMeta.get(u);
1172
- t.push({ channel: f, type: c, name: l?.name, description: l?.description });
1455
+ for (const [l, i] of this.effects) {
1456
+ if (i.size === 0) continue;
1457
+ const [a, d] = l.split("::");
1458
+ for (const u of i) {
1459
+ const f = this.effectMeta.get(u);
1460
+ t.push({ channel: a, type: d, name: f?.name, description: f?.description });
1173
1461
  }
1174
1462
  }
1175
- for (const a of this.patternEffects) {
1176
- const r = this.effectMeta.get(a.effect);
1463
+ for (const l of this.patternEffects) {
1464
+ const i = this.effectMeta.get(l.effect);
1177
1465
  t.push({
1178
1466
  channel: "*",
1179
1467
  type: "*",
1180
- name: r?.name,
1181
- description: r?.description
1468
+ name: i?.name,
1469
+ description: i?.description
1182
1470
  });
1183
1471
  }
1184
- const n = [];
1185
- for (const a of this.middleware)
1186
- typeof a == "function" ? n.push({ name: a.name || void 0 }) : n.push({
1187
- name: a.meta?.name,
1188
- description: a.meta?.description,
1189
- when: a.when
1190
- });
1191
1472
  const s = [];
1192
- for (const a of this.connectorBus.__introspect())
1193
- for (let r = 0; r < a.count; r++)
1194
- s.push({ reducer: a.channel, property: a.type });
1195
- const i = [];
1196
- for (const [a, r] of this.committedEventSubscribers) {
1197
- if (r.size === 0) continue;
1198
- const [f, c] = a.split("::");
1199
- for (let u = 0; u < r.size; u++)
1200
- i.push({ channel: f, type: c, phase: "committed" });
1473
+ for (const l of this.middleware)
1474
+ typeof l == "function" ? s.push({ name: l.name || void 0 }) : s.push({
1475
+ name: l.meta?.name,
1476
+ description: l.meta?.description,
1477
+ when: l.when
1478
+ });
1479
+ const n = [];
1480
+ for (const l of this.connectorBus.__introspect())
1481
+ for (let i = 0; i < l.count; i++)
1482
+ n.push({ reducer: l.channel, property: l.type });
1483
+ const r = [];
1484
+ for (const [l, i] of this.committedEventSubscribers) {
1485
+ if (i.size === 0) continue;
1486
+ const [a, d] = l.split("::");
1487
+ for (let u = 0; u < i.size; u++)
1488
+ r.push({ channel: a, type: d, phase: "committed" });
1201
1489
  }
1202
- for (const [a, r] of this.uncommittedEventSubscribers) {
1203
- if (r.size === 0) continue;
1204
- const [f, c] = a.split("::");
1205
- for (let u = 0; u < r.size; u++)
1206
- i.push({ channel: f, type: c, phase: "uncommitted" });
1490
+ for (const [l, i] of this.uncommittedEventSubscribers) {
1491
+ if (i.size === 0) continue;
1492
+ const [a, d] = l.split("::");
1493
+ for (let u = 0; u < i.size; u++)
1494
+ r.push({ channel: a, type: d, phase: "uncommitted" });
1207
1495
  }
1208
- for (const [a, r] of this.allEventSubscribers) {
1209
- if (r.size === 0) continue;
1210
- const [f, c] = a.split("::");
1211
- for (let u = 0; u < r.size; u++)
1212
- i.push({ channel: f, type: c, phase: "all" });
1496
+ for (const [l, i] of this.allEventSubscribers) {
1497
+ if (i.size === 0) continue;
1498
+ const [a, d] = l.split("::");
1499
+ for (let u = 0; u < i.size; u++)
1500
+ r.push({ channel: a, type: d, phase: "all" });
1213
1501
  }
1214
- const d = this.listeners.size;
1502
+ const o = this.listeners.size;
1215
1503
  return {
1216
1504
  reducers: e,
1217
1505
  effects: t,
1218
- middleware: n,
1219
- atomic: s,
1220
- event: i,
1221
- coarse: d,
1506
+ middleware: s,
1507
+ atomic: n,
1508
+ event: r,
1509
+ coarse: o,
1222
1510
  dedupHits: this.dedupCount,
1223
1511
  queueDepth: this.reduceQueue.length + this.inFlightEffects
1224
1512
  };
@@ -1228,7 +1516,7 @@ class $ {
1228
1516
  * fine-grained path changes for each slice.
1229
1517
  *
1230
1518
  * **State Immutability**: If any slices change, a new state object is created via
1231
- * shallow spread. This ensures consistent immutability with {@link forwardEvent}.
1519
+ * shallow spread. This ensures consistent immutability with {@link commitStaged}.
1232
1520
  *
1233
1521
  * **Missing slices**: the snapshot should contain every slice. A slice absent
1234
1522
  * from `nextPlain` is **retained at its current value** (not blanked to
@@ -1243,36 +1531,36 @@ class $ {
1243
1531
  throw new Error(
1244
1532
  "[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1245
1533
  );
1246
- const t = this.state, n = e, s = { ...this.state };
1247
- let i = !1;
1248
- Object.keys(this.reducers).forEach((d) => {
1249
- const a = t?.[d], r = n?.[d];
1250
- if (r === void 0) {
1534
+ const t = this.state, s = e, n = { ...this.state };
1535
+ let r = !1;
1536
+ Object.keys(this.reducers).forEach((o) => {
1537
+ const l = t?.[o], i = s?.[o];
1538
+ if (i === void 0) {
1251
1539
  process.env.NODE_ENV !== "production" && console.warn(
1252
1540
  `[yoltra] External state is missing slice "${String(
1253
- d
1541
+ o
1254
1542
  )}"; retaining its current value. Time-travel snapshots should contain all slices.`
1255
1543
  );
1256
1544
  return;
1257
1545
  }
1258
- if (a === r) return;
1259
- const f = P(r);
1260
- s[d] = f, i = !0;
1261
- const c = O(a, r);
1262
- if (c.length === 0) return;
1546
+ if (l === i) return;
1547
+ const a = R(i);
1548
+ n[o] = a, r = !0;
1549
+ const d = I(l, i);
1550
+ if (d.length === 0) return;
1263
1551
  const u = /* @__PURE__ */ new Set();
1264
- for (const l of c) {
1265
- if (l === "") {
1552
+ for (const f of d) {
1553
+ if (f === "") {
1266
1554
  u.add("");
1267
1555
  continue;
1268
1556
  }
1269
- for (const y of $.buildAncestorPaths(l)) u.add(y);
1557
+ for (const m of x.buildAncestorPaths(f)) u.add(m);
1270
1558
  }
1271
- for (const l of u) {
1272
- const y = this.getAtPath(a, l), h = this.getAtPath(f, l);
1273
- this.connectorBus.emit(d, l, { oldValue: y, newValue: h, path: l });
1559
+ for (const f of u) {
1560
+ const m = this.getAtPath(l, f), h = this.getAtPath(a, f);
1561
+ this.connectorBus.emit(o, f, { oldValue: m, newValue: h, path: f });
1274
1562
  }
1275
- }), i && (this.state = s), i && this.listeners.forEach((d) => d());
1563
+ }), r && (this.state = n), r && this.listeners.forEach((o) => o());
1276
1564
  }
1277
1565
  /**
1278
1566
  * Replays a sequence of events from a snapshot through reducers and event
@@ -1292,13 +1580,24 @@ class $ {
1292
1580
  "[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1293
1581
  );
1294
1582
  this.__applyExternalState(e);
1295
- for (const n of t) {
1296
- const s = n, i = this.state;
1297
- this.reducerBus.emit(s.channel, s.type, s.payload, s);
1298
- for (const [r, f] of this.patternReducers)
1299
- this.matchesWhen(f, s) && this.forwardEventGuarded(r, s);
1300
- const d = this.state, a = i !== d;
1301
- this.notifyEventSubscribers(s, "committed"), a && this.listeners.forEach((r) => r());
1583
+ for (const s of t) {
1584
+ const n = s, r = [];
1585
+ this.stagingSink = r;
1586
+ let o = null;
1587
+ try {
1588
+ this.reducerBus.emit(n.channel, n.type, n.payload, n), o = this.stagedRejection;
1589
+ for (const [i, a] of this.patternReducers) {
1590
+ if (o !== null) break;
1591
+ if (this.matchesWhen(a, n)) {
1592
+ const d = this.stageSliceGuarded(i, n, r);
1593
+ d !== null && (o = d);
1594
+ }
1595
+ }
1596
+ } finally {
1597
+ this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1598
+ }
1599
+ const l = o === null && this.commitStaged(r, n);
1600
+ this.notifyEventSubscribers(n, "committed"), l && (this.notifyEventSubscribers(n, "written"), this.listeners.forEach((i) => i()));
1302
1601
  }
1303
1602
  }
1304
1603
  /**
@@ -1310,13 +1609,13 @@ class $ {
1310
1609
  * phase* (step 5) runs afterwards, asynchronously.
1311
1610
  * 1. **Deduplication** (opt-in) - Skip when content-dedup is enabled (`dedupWindowMs > 0`) or a matching `dedupKey` recurs; off by default
1312
1611
  * 2. **Middleware** (sync) - Pre-reducer hooks; may cancel by returning `false`
1313
- * 3. **Reducers** (sync) - state updates + fine-grained path notifications
1314
- * 4. **Subscribers + coarse** (sync) - event subscribers (fire-and-forget) then coarse listeners (only if state changed)
1612
+ * 3. **Reducers** (sync) - every matching slice is *staged*; nothing is written yet, so a refusal from the last reducer still stops the first one's write
1613
+ * 4. **Commit + subscribers** (sync) - all staged slices are assigned under one new root, then event subscribers (`committed`, then `written` when state actually changed), then coarse listeners
1315
1614
  * 5. **Effects** (async) - side-effects keyed by `(channel, type)`; the returned promise resolves once they complete
1316
1615
  *
1317
1616
  * **Change Detection**: Uses reference equality (`===`) on `this.state` to determine
1318
- * if any slice changed. Works because {@link forwardEvent} creates a new state reference
1319
- * via shallow spread when any slice changes.
1617
+ * if any slice changed. Works because the commit builds a new state reference via
1618
+ * shallow spread when any slice changes.
1320
1619
  *
1321
1620
  * @typeParam C - Channel key in `EM`.
1322
1621
  * @typeParam T - Type key within channel `C`.
@@ -1344,25 +1643,59 @@ class $ {
1344
1643
  *
1345
1644
  * @public
1346
1645
  */
1347
- async emit(e, t, n, s) {
1348
- const i = s?.dedupKey, d = this.dedupConfig.windowMs;
1349
- if (s?.skipDedup !== !0 && (d > 0 || i !== void 0)) {
1350
- const c = i !== void 0 && d <= 0 ? x : d, u = i !== void 0 ? `${e}::${t}::#${i}` : this.fingerprint(e, t, n);
1351
- if (this.shouldDedupe(u, c))
1352
- return;
1646
+ async emit(e, t, s, n) {
1647
+ return this.emitCaused(null, e, t, s, n);
1648
+ }
1649
+ /**
1650
+ * The real emit, with an explicitly supplied cause.
1651
+ *
1652
+ * @remarks
1653
+ * Exists so the parent can be passed without a pseudo-private field on the public
1654
+ * {@link EmitOptions}. Two callers supply one: the public {@link emit} passes `null` and lets
1655
+ * `currentEvent` speak for the synchronous case, and the scoped `emit` handed to effects passes
1656
+ * the event that triggered them — effects resume after the drain has ended, so nothing else
1657
+ * could still know what caused them.
1658
+ *
1659
+ * @internal
1660
+ */
1661
+ async emitCaused(e, t, s, n, r) {
1662
+ const o = r?.dedupKey, l = this.dedupConfig.windowMs;
1663
+ if (r?.skipDedup !== !0 && (l > 0 || o !== void 0)) {
1664
+ const m = o !== void 0 && l <= 0 ? D : l, h = o !== void 0 ? `${t}::${s}::#${o}` : this.fingerprint(t, s, n);
1665
+ if (this.shouldDedupe(h, m))
1666
+ return $;
1353
1667
  }
1354
- const a = s?.id ?? this.idFactory();
1355
- let r;
1356
- const f = new Promise((c) => {
1357
- r = c;
1668
+ const i = r?.id ?? this.idFactory(), a = this.currentEvent ?? e, d = a === null ? 0 : a.depth + 1;
1669
+ if (a !== null && d > this.maxReduceDepth)
1670
+ return this.reportCascade(
1671
+ "maxReduceDepth",
1672
+ this.maxReduceDepth,
1673
+ {
1674
+ channel: t,
1675
+ type: s,
1676
+ payload: n,
1677
+ id: i,
1678
+ ...r?.meta !== void 0 ? { meta: r.meta } : {},
1679
+ parentId: a.id,
1680
+ depth: d
1681
+ },
1682
+ d,
1683
+ a.chain
1684
+ ), $;
1685
+ let u;
1686
+ const f = new Promise((m) => {
1687
+ u = m;
1358
1688
  });
1359
1689
  return this.reduceQueue.push({
1360
- channel: e,
1361
- type: t,
1690
+ channel: t,
1691
+ type: s,
1362
1692
  payload: n,
1363
- id: a,
1364
- meta: s?.meta,
1365
- resolve: r
1693
+ id: i,
1694
+ meta: r?.meta,
1695
+ resolve: u,
1696
+ // Only carried for caused events, so a root event's object stays byte-identical to one
1697
+ // built before causality existed — the same rule `meta` follows.
1698
+ ...a !== null ? { parentId: a.id, depth: d, chain: a.chain } : {}
1366
1699
  }), this.drainReduce(), f;
1367
1700
  }
1368
1701
  /**
@@ -1377,33 +1710,50 @@ class $ {
1377
1710
  */
1378
1711
  drainReduce() {
1379
1712
  if (!this.isReducing) {
1380
- this.isReducing = !0;
1713
+ this.isReducing = !0, this.transitionsThisDrain = 0;
1381
1714
  try {
1382
1715
  for (; this.reduceQueue.length > 0; ) {
1383
- const { channel: e, type: t, payload: n, id: s, meta: i, resolve: d } = this.reduceQueue.shift(), a = {
1384
- channel: e,
1385
- type: t,
1716
+ const e = this.reduceQueue.shift(), { channel: t, type: s, payload: n, id: r, meta: o, resolve: l, parentId: i, depth: a, chain: d } = e, u = {
1717
+ channel: t,
1718
+ type: s,
1386
1719
  payload: n,
1387
- id: s,
1388
- ...i !== void 0 ? { meta: i } : {}
1389
- }, r = this.instrumentObservers.size > 0, f = r ? this.state : void 0, c = r ? [] : void 0;
1390
- c !== void 0 && (this.changedPathSink = c);
1391
- const u = r ? z() : 0;
1392
- let l = !1;
1720
+ id: r,
1721
+ ...o !== void 0 ? { meta: o } : {},
1722
+ ...i !== void 0 ? { parentId: i, depth: a } : {}
1723
+ };
1724
+ if (i !== void 0 && ++this.transitionsThisDrain > this.maxTransitionsPerDrain) {
1725
+ this.reportCascade(
1726
+ "maxTransitionsPerDrain",
1727
+ this.maxTransitionsPerDrain,
1728
+ u,
1729
+ a,
1730
+ d
1731
+ ), l($);
1732
+ continue;
1733
+ }
1734
+ this.currentEvent = {
1735
+ id: r,
1736
+ depth: a ?? 0,
1737
+ chain: [...d ?? [], r].slice(-z)
1738
+ };
1739
+ const f = this.instrumentObservers.size > 0, m = f ? this.state : void 0, h = f ? [] : void 0;
1740
+ h !== void 0 && (this.changedPathSink = h);
1741
+ const y = f ? A() : 0;
1742
+ let g = $;
1393
1743
  try {
1394
- l = this.applyEventSync(a);
1395
- } catch (y) {
1396
- console.error("Emit reduce error:", y);
1744
+ g = this.applyEventSync(u);
1745
+ } catch (w) {
1746
+ console.error("Emit reduce error:", w);
1397
1747
  } finally {
1398
- r && (this.changedPathSink = null);
1748
+ f && (this.changedPathSink = null), this.currentEvent = null;
1399
1749
  }
1400
- r && this.emitInstrumentation(
1401
- a,
1402
- l,
1403
- c ?? [],
1404
- f,
1405
- z() - u
1406
- ), this.runEventEffects(a, l, d);
1750
+ f && this.emitInstrumentation(
1751
+ u,
1752
+ g,
1753
+ h ?? [],
1754
+ m,
1755
+ A() - y
1756
+ ), this.runEventEffects(u, g, l);
1407
1757
  }
1408
1758
  } finally {
1409
1759
  this.isReducing = !1;
@@ -1421,32 +1771,45 @@ class $ {
1421
1771
  * @internal
1422
1772
  */
1423
1773
  applyEventSync(e) {
1424
- for (const s of this.middleware) {
1425
- const i = this.getMiddlewareWhen(s);
1426
- if (!this.matchesWhen(i, e)) continue;
1427
- const d = this.getMiddlewareFunction(s);
1774
+ for (const o of this.middleware) {
1775
+ const l = this.getMiddlewareWhen(o);
1776
+ if (!this.matchesWhen(l, e)) continue;
1777
+ const i = this.getMiddlewareFunction(o);
1428
1778
  let a;
1429
1779
  try {
1430
- a = d(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
1780
+ a = i(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
1431
1781
  `[yoltra] Middleware for "${e.channel}/${e.type}" returned a Promise. Middleware is synchronous: a Promise is truthy, so this event was allowed without waiting and a "return false" inside it can never veto. Do the check synchronously, and put anything that must await in an effect.`
1432
1782
  );
1433
- } catch (r) {
1434
- console.error("Middleware error:", r), a = !1;
1783
+ } catch (d) {
1784
+ console.error("Middleware error:", d), a = !1;
1435
1785
  }
1436
1786
  if (!a)
1437
- return this.notifyEventSubscribers(e, "uncommitted"), !1;
1787
+ return this.notifyEventSubscribers(e, "uncommitted"), $;
1438
1788
  }
1439
- const t = this.state;
1440
- this.reducerBus.emit(
1441
- e.channel,
1442
- e.type,
1443
- e.payload,
1444
- e
1445
- );
1446
- for (const [s, i] of this.patternReducers)
1447
- this.matchesWhen(i, e) && this.forwardEventGuarded(s, e);
1448
- const n = t !== this.state;
1449
- return this.notifyEventSubscribers(e, "committed"), n && this.listeners.forEach((s) => s()), !0;
1789
+ const t = [];
1790
+ this.stagingSink = t;
1791
+ let s = null, n = "";
1792
+ try {
1793
+ this.reducerBus.emit(
1794
+ e.channel,
1795
+ e.type,
1796
+ e.payload,
1797
+ e
1798
+ ), s = this.stagedRejection, n = this.stagedRejectedBy;
1799
+ for (const [o, l] of this.patternReducers) {
1800
+ if (s !== null) break;
1801
+ if (this.matchesWhen(l, e)) {
1802
+ const i = this.stageSliceGuarded(o, e, t);
1803
+ i !== null && (s = i, n = o);
1804
+ }
1805
+ }
1806
+ } finally {
1807
+ this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1808
+ }
1809
+ if (s !== null)
1810
+ return this.onRejected?.(s, e, n), this.notifyEventSubscribers(e, "committed"), { committed: !0, written: !1, rejected: s };
1811
+ const r = this.commitStaged(t, e);
1812
+ return this.notifyEventSubscribers(e, "committed"), r && (this.notifyEventSubscribers(e, "written"), this.listeners.forEach((o) => o())), r ? se : ne;
1450
1813
  }
1451
1814
  /**
1452
1815
  * Runs a single committed event's effects as an **independent async task**,
@@ -1456,14 +1819,14 @@ class $ {
1456
1819
  *
1457
1820
  * @internal
1458
1821
  */
1459
- async runEventEffects(e, t, n) {
1822
+ async runEventEffects(e, t, s) {
1460
1823
  this.inFlightEffects++;
1461
1824
  try {
1462
- t && await this.notifyEffects(e);
1463
- } catch (s) {
1464
- console.error("Effect error:", s);
1825
+ t.committed && await this.notifyEffects(e);
1826
+ } catch (n) {
1827
+ console.error("Effect error:", n);
1465
1828
  } finally {
1466
- this.inFlightEffects--, n();
1829
+ this.inFlightEffects--, s(t);
1467
1830
  }
1468
1831
  }
1469
1832
  /**
@@ -1479,16 +1842,16 @@ class $ {
1479
1842
  /**
1480
1843
  * Builds an {@link InstrumentedEvent} from the reduce result and notifies
1481
1844
  * observers. `changedPaths` are the exact slice-prefixed leaf paths recorded
1482
- * by {@link forwardEvent} during this reduce, so DevTools patches need no
1845
+ * by {@link commitStaged} during this reduce, so DevTools patches need no
1483
1846
  * re-diff.
1484
1847
  *
1485
1848
  * @internal
1486
1849
  */
1487
- emitInstrumentation(e, t, n, s, i) {
1488
- const d = {}, a = {};
1489
- for (const f of n)
1490
- d[f] = this.getAtPath(s, f), a[f] = this.getAtPath(this.state, f);
1491
- const r = {
1850
+ emitInstrumentation(e, t, s, n, r) {
1851
+ const o = {}, l = {};
1852
+ for (const a of s)
1853
+ o[a] = this.getAtPath(n, a), l[a] = this.getAtPath(this.state, a);
1854
+ const i = {
1492
1855
  event: {
1493
1856
  id: e.id,
1494
1857
  channel: e.channel,
@@ -1498,17 +1861,20 @@ class $ {
1498
1861
  // byte-identical to the pre-`meta` shape.
1499
1862
  ...e.meta !== void 0 ? { meta: e.meta } : {}
1500
1863
  },
1501
- committed: t,
1502
- changedPaths: n,
1503
- prevValues: d,
1504
- nextValues: a,
1505
- reduceTimeMs: i
1864
+ committed: t.committed,
1865
+ changedPaths: s,
1866
+ prevValues: o,
1867
+ nextValues: l,
1868
+ reduceTimeMs: r,
1869
+ // Present only when a reducer refused, so an observer can tell a refusal from a veto —
1870
+ // identical in state, entirely different in cause.
1871
+ ...t.rejected !== void 0 ? { rejected: t.rejected } : {}
1506
1872
  };
1507
- for (const f of [...this.instrumentObservers])
1873
+ for (const a of [...this.instrumentObservers])
1508
1874
  try {
1509
- f(r);
1510
- } catch (c) {
1511
- console.error("Instrumentation observer error:", c);
1875
+ a(i);
1876
+ } catch (d) {
1877
+ console.error("Instrumentation observer error:", d);
1512
1878
  }
1513
1879
  }
1514
1880
  /**
@@ -1539,8 +1905,13 @@ class $ {
1539
1905
  *
1540
1906
  * @public
1541
1907
  */
1542
- connect(e, t) {
1543
- return this.connectorBus.on(e.reducer, e.property, t);
1908
+ connect(e, t, s) {
1909
+ const n = this.connectorBus.on(e.reducer, e.property, t);
1910
+ if (s?.immediate === !0) {
1911
+ const r = this.state[e.reducer], o = e.property.includes("*") ? "" : e.property;
1912
+ t({ oldValue: void 0, newValue: this.getAtPath(r, o), path: o });
1913
+ }
1914
+ return n;
1544
1915
  }
1545
1916
  /**
1546
1917
  * Subscribe to events by channel and type.
@@ -1588,11 +1959,11 @@ class $ {
1588
1959
  *
1589
1960
  * @public
1590
1961
  */
1591
- onEvent(e, t, n, s = "committed") {
1592
- const i = `${e}::${String(t)}`, d = s === "committed" ? this.committedEventSubscribers : s === "uncommitted" ? this.uncommittedEventSubscribers : this.allEventSubscribers;
1593
- return d.has(i) || d.set(i, /* @__PURE__ */ new Set()), d.get(i).add(n), () => {
1594
- const a = d.get(i);
1595
- a && (a.delete(n), a.size === 0 && d.delete(i));
1962
+ onEvent(e, t, s, n = "committed") {
1963
+ const r = `${e}::${String(t)}`, o = n === "committed" ? this.committedEventSubscribers : n === "uncommitted" ? this.uncommittedEventSubscribers : n === "written" ? this.writtenEventSubscribers : this.allEventSubscribers;
1964
+ return o.has(r) || o.set(r, /* @__PURE__ */ new Set()), o.get(r).add(s), () => {
1965
+ const l = o.get(r);
1966
+ l && (l.delete(s), l.size === 0 && o.delete(r));
1596
1967
  };
1597
1968
  }
1598
1969
  /**
@@ -1698,8 +2069,8 @@ class $ {
1698
2069
  throw new Error(`Reducer ${e} already exists`);
1699
2070
  return this.mountSlice(e, t, {
1700
2071
  preserveState: !1
1701
- }), this.listeners.forEach((n) => n()), () => {
1702
- this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((n) => n());
2072
+ }), this.listeners.forEach((s) => s()), () => {
2073
+ this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((s) => s());
1703
2074
  };
1704
2075
  }
1705
2076
  /**
@@ -1734,30 +2105,169 @@ class $ {
1734
2105
  *
1735
2106
  * @public
1736
2107
  */
2108
+ /**
2109
+ * Sends a request and waits for the reply, correlating the two automatically.
2110
+ *
2111
+ * @typeParam C - Request channel.
2112
+ * @typeParam T - Request type within `C`.
2113
+ * @param channel - Channel to send on.
2114
+ * @param type - Event type to send.
2115
+ * @param payload - The **request** payload. This is what you are sending; what comes back is
2116
+ * described by {@link CallOptions.reply}, not by this.
2117
+ * @param opts - Which replies end the call, and how long to wait. See {@link CallOptions}.
2118
+ * @returns A {@link CallHandle}: `await` it for the terminal reply, or `for await` it for
2119
+ * progress events as they arrive.
2120
+ *
2121
+ * @remarks
2122
+ * Every consumer of an event bus eventually writes request/reply by hand — mint an id,
2123
+ * subscribe, match, time out, unsubscribe — and every one of them writes the same eighty lines
2124
+ * with the same two bugs: the subscription outlives the call, and a responder that forgets to
2125
+ * echo the id produces a timeout with nothing to point at. This is that, once.
2126
+ *
2127
+ * **Correlation is causal.** The store stamps `parentId` on anything emitted while an event is
2128
+ * being handled, so a responder that replies through the `emit` it was handed is already
2129
+ * correlated. There is no id to mint, echo, or forget:
2130
+ *
2131
+ * ```ts
2132
+ * store.registerEffect({
2133
+ * when: { keys: [["rpc", "ask"]] },
2134
+ * effect: async (event, _get, emit) => {
2135
+ * await emit("rpc", "answer", await lookup(event.payload.q));
2136
+ * },
2137
+ * });
2138
+ * ```
2139
+ *
2140
+ * **The reply carries its own discriminant.** A call resolves to the *event*, not the payload,
2141
+ * because a caller often cannot know which kind of reply it will get:
2142
+ *
2143
+ * ```ts
2144
+ * const res = await store.call("rpc", "ask", { q }, { reply: ["rpc", ["answer", "error"]] });
2145
+ * switch (res.type) {
2146
+ * case "answer": return res.payload;
2147
+ * case "error": throw new Error(res.payload.reason);
2148
+ * }
2149
+ * ```
2150
+ *
2151
+ * **Progress streams, with backpressure.** Any correlated event that is not terminal is
2152
+ * progress, and iterating the call consumes it. The producer genuinely waits: `emit` resolves
2153
+ * only once its effects have run, and the collector is an effect that does not return until the
2154
+ * consumer has taken the item. A responder writing `await emit("rpc", "progress", chunk)` is
2155
+ * therefore paced by the reader, with nothing buffering without bound.
2156
+ *
2157
+ * ```ts
2158
+ * const call = store.call("job", "start", { id }, {
2159
+ * reply: ["job", "done"],
2160
+ * highWaterMark: 4,
2161
+ * });
2162
+ * for await (const step of call) await render(step.payload); // producer waits on this
2163
+ * const { payload } = await call;
2164
+ * ```
2165
+ *
2166
+ * Backpressure engages **once you begin iterating**. A call that is only awaited never pulls,
2167
+ * so blocking its producer would deadlock the call itself — progress nobody reads would stop
2168
+ * the terminal event from ever being sent. Un-iterated progress therefore buffers to
2169
+ * `highWaterMark` and is then counted on {@link CallHandle.dropped} rather than blocking.
2170
+ *
2171
+ * **This is a local primitive.** A reply cannot reach it from a federated peer: the federation
2172
+ * envelope carries neither `meta` nor `parentId`, and ingress namespaces the channel, so
2173
+ * neither correlation nor the reply route survives the hop. That is not an oversight to route
2174
+ * around — federation answers cross-node request/reply with typed peer *queries*, which are
2175
+ * gated by a responder policy that may concede or deny. A call that federated silently would
2176
+ * turn that access decision into an accident of which channel someone named. Ask a peer with a
2177
+ * query; use `call` within a process.
2178
+ *
2179
+ * @example Timeout is idle, not total
2180
+ * ```ts
2181
+ * // Survives a job that streams for minutes; fails a responder that goes quiet for 5s.
2182
+ * await store.call("job", "start", { id }, { reply: ["job", "done"], timeoutMs: 5_000 });
2183
+ * ```
2184
+ *
2185
+ * @example Cancelling
2186
+ * ```ts
2187
+ * const call = store.call("rpc", "ask", { q }, { reply: ["rpc", "answer"] });
2188
+ * useEffect(() => () => call.cancel("unmounted"), [call]);
2189
+ * ```
2190
+ *
2191
+ * @public
2192
+ */
2193
+ call(e, t, s, n) {
2194
+ const { channel: r, isTerminal: o } = q(n.reply), l = n.timeoutMs ?? ee, i = new G(n.highWaterMark ?? te), a = this.idFactory();
2195
+ let d, u, f = !1;
2196
+ const m = new Promise((b, S) => {
2197
+ d = b, u = S;
2198
+ });
2199
+ m.catch(() => {
2200
+ });
2201
+ let h = null, y = null;
2202
+ const g = (b, S = !1) => {
2203
+ f || (f = !0, h !== null && clearTimeout(h), h = null, y?.(), y = null, S ? i.end() : i.close(), n.signal?.removeEventListener("abort", w), b());
2204
+ };
2205
+ function w() {
2206
+ g(() => u(new C(String(n.signal?.reason ?? "signal aborted"))));
2207
+ }
2208
+ const k = () => {
2209
+ h !== null && clearTimeout(h), h = setTimeout(() => {
2210
+ g(() => u(new J(e, t, l)));
2211
+ }, l), h.unref?.();
2212
+ };
2213
+ return y = this.registerEffect({
2214
+ // A pattern effect on the reply channel: which types are terminal is known, which are
2215
+ // progress is not, so the filter cannot be a key list.
2216
+ when: { channel: r },
2217
+ effect: async (b) => {
2218
+ if (!f && Y(b, a, n.correlationId)) {
2219
+ if (k(), o(String(b.type))) {
2220
+ g(() => d(b), !0);
2221
+ return;
2222
+ }
2223
+ await i.put(b);
2224
+ }
2225
+ }
2226
+ }), n.signal !== void 0 && (n.signal.aborted ? w() : n.signal.addEventListener("abort", w, { once: !0 })), k(), this.emit(e, t, s, {
2227
+ id: a,
2228
+ ...n.correlationId !== void 0 ? { meta: { correlationId: n.correlationId } } : {}
2229
+ }), {
2230
+ then: (b, S) => m.then(b, S),
2231
+ catch: (b) => m.catch(b),
2232
+ finally: (b) => m.finally(b),
2233
+ get dropped() {
2234
+ return i.droppedCount;
2235
+ },
2236
+ cancel: (b = "cancelled") => {
2237
+ g(() => u(new C(b)));
2238
+ },
2239
+ [Symbol.asyncIterator]: () => (i.beginConsuming(), {
2240
+ next: () => i.take(),
2241
+ // Called by `for await` on `break`, `return` or a throw. Without it, abandoning the
2242
+ // loop would leave the effect registered and the producer parked for good.
2243
+ return: async () => (i.close(), { value: void 0, done: !0 })
2244
+ })
2245
+ };
2246
+ }
1737
2247
  registerEffect(e) {
1738
- const { effect: t, meta: n, when: s } = e, i = [];
1739
- if (n && this.effectMeta.set(t, n), s && ("any" in s && s.any === !0 || "channel" in s || "channels" in s)) {
1740
- const r = { effect: t, when: s };
1741
- return this.patternEffects.add(r), () => {
1742
- this.patternEffects.delete(r);
2248
+ const { effect: t, meta: s, when: n } = e, r = [];
2249
+ if (s && this.effectMeta.set(t, s), n && ("any" in n && n.any === !0 || "channel" in n || "channels" in n)) {
2250
+ const i = { effect: t, when: n };
2251
+ return this.patternEffects.add(i), () => {
2252
+ this.patternEffects.delete(i);
1743
2253
  };
1744
2254
  }
1745
- const a = this.normalizeEventKeys(e);
1746
- if (a.length === 0 && !s) {
1747
- const r = { effect: t, when: { any: !0 } };
1748
- return this.patternEffects.add(r), () => {
1749
- this.patternEffects.delete(r);
2255
+ const l = this.normalizeEventKeys(e);
2256
+ if (l.length === 0 && !n) {
2257
+ const i = { effect: t, when: { any: !0 } };
2258
+ return this.patternEffects.add(i), () => {
2259
+ this.patternEffects.delete(i);
1750
2260
  };
1751
2261
  }
1752
- for (const [r, f] of a) {
1753
- const c = `${String(r)}::${String(f)}`;
1754
- this.effects.has(c) || this.effects.set(c, /* @__PURE__ */ new Set()), this.effects.get(c).add(t), i.push(() => {
1755
- const u = this.effects.get(c);
1756
- u && (u.delete(t), u.size === 0 && this.effects.delete(c));
2262
+ for (const [i, a] of l) {
2263
+ const d = `${String(i)}::${String(a)}`;
2264
+ this.effects.has(d) || this.effects.set(d, /* @__PURE__ */ new Set()), this.effects.get(d).add(t), r.push(() => {
2265
+ const u = this.effects.get(d);
2266
+ u && (u.delete(t), u.size === 0 && this.effects.delete(d));
1757
2267
  });
1758
2268
  }
1759
2269
  return () => {
1760
- for (const r of i) r();
2270
+ for (const i of r) i();
1761
2271
  };
1762
2272
  }
1763
2273
  /**
@@ -1781,15 +2291,15 @@ class $ {
1781
2291
  *
1782
2292
  * @public
1783
2293
  */
1784
- onEffect(e, t, n) {
1785
- const s = async (i, d, a) => {
1786
- if (i.channel !== e || i.type !== t) return;
1787
- const r = i;
1788
- return n(r.payload, d, a, r);
2294
+ onEffect(e, t, s) {
2295
+ const n = async (r, o, l) => {
2296
+ if (r.channel !== e || r.type !== t) return;
2297
+ const i = r;
2298
+ return s(i.payload, o, l, i);
1789
2299
  };
1790
2300
  return this.registerEffect({
1791
2301
  when: { keys: [[e, t]] },
1792
- effect: s
2302
+ effect: n
1793
2303
  });
1794
2304
  }
1795
2305
  /**
@@ -1851,11 +2361,11 @@ class $ {
1851
2361
  * @public
1852
2362
  */
1853
2363
  replaceReducers(e, t = {}) {
1854
- const n = t.preserveState !== !1, s = new Set(Object.keys(this.reducers)), i = Object.entries(e), d = new Set(i.map(([a]) => a));
1855
- for (const a of s)
1856
- d.has(a) || this.unmountSlice(a, { deleteState: !0 });
1857
- for (const [a, r] of i)
1858
- s.has(a) ? (this.unmountSlice(a, { deleteState: !1 }), this.mountSlice(a, r, { preserveState: n })) : this.mountSlice(a, r, { preserveState: !1 });
2364
+ const s = t.preserveState !== !1, n = new Set(Object.keys(this.reducers)), r = Object.entries(e), o = new Set(r.map(([l]) => l));
2365
+ for (const l of n)
2366
+ o.has(l) || this.unmountSlice(l, { deleteState: !0 });
2367
+ for (const [l, i] of r)
2368
+ n.has(l) ? (this.unmountSlice(l, { deleteState: !1 }), this.mountSlice(l, i, { preserveState: s })) : this.mountSlice(l, i, { preserveState: !1 });
1859
2369
  }
1860
2370
  /**
1861
2371
  * Convenience API to replace **any subset** of store parts (HMR patterns).
@@ -1887,34 +2397,36 @@ class $ {
1887
2397
  *
1888
2398
  * @internal
1889
2399
  */
1890
- mountSlice(e, t, n) {
1891
- const s = e, { reducer: i, state: d, when: a } = t;
1892
- if (this.reducers[e] = new K(i), (!n.preserveState || this.state[s] === void 0) && (this.state = {
2400
+ mountSlice(e, t, s) {
2401
+ const n = e, { reducer: r, state: o, when: l } = t;
2402
+ if (this.reducers[e] = new V(r), (!s.preserveState || this.state[n] === void 0) && (this.state = {
1893
2403
  ...this.state,
1894
- [s]: P(W(s, d))
1895
- }), a && ("any" in a && a.any === !0 || "channel" in a || "channels" in a)) {
1896
- this.patternReducers.set(e, a), this.sliceUnsubs.set(s, []);
2404
+ [n]: R(X(n, o))
2405
+ }), l && ("any" in l && l.any === !0 || "channel" in l || "channels" in l)) {
2406
+ this.patternReducers.set(e, l), this.sliceUnsubs.set(n, []);
1897
2407
  return;
1898
2408
  }
1899
- const f = this.normalizeEventKeys(t);
1900
- if (f.length === 0 && !a) {
1901
- this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(s, []);
2409
+ const a = this.normalizeEventKeys(t);
2410
+ if (a.length === 0 && !l) {
2411
+ this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(n, []);
1902
2412
  return;
1903
2413
  }
1904
- const c = [];
1905
- for (const [u, l] of f) {
1906
- const y = this.reducerBus.on(u, l, (h, m) => {
1907
- const g = m ?? {
2414
+ const d = [];
2415
+ for (const [u, f] of a) {
2416
+ const m = this.reducerBus.on(u, f, (h, y) => {
2417
+ const g = y ?? {
1908
2418
  channel: u,
1909
- type: l,
2419
+ type: f,
1910
2420
  payload: h,
1911
2421
  id: this.idFactory()
1912
2422
  };
1913
- this.forwardEventGuarded(e, g);
2423
+ if (this.stagingSink === null) return;
2424
+ const w = this.stageSliceGuarded(e, g, this.stagingSink);
2425
+ w !== null && this.stagedRejection === null && (this.stagedRejection = w, this.stagedRejectedBy = e);
1914
2426
  });
1915
- c.push(y);
2427
+ d.push(m);
1916
2428
  }
1917
- this.sliceUnsubs.set(s, c);
2429
+ this.sliceUnsubs.set(n, d);
1918
2430
  }
1919
2431
  /**
1920
2432
  * Unmounts a slice: disposes reducer-bus listeners, removes reducer,
@@ -1926,21 +2438,21 @@ class $ {
1926
2438
  * @internal
1927
2439
  */
1928
2440
  unmountSlice(e, t) {
1929
- const n = e;
2441
+ const s = e;
1930
2442
  this.patternReducers.delete(e);
1931
- const s = this.sliceUnsubs.get(n);
1932
- if (s) {
1933
- for (const i of s)
2443
+ const n = this.sliceUnsubs.get(s);
2444
+ if (n) {
2445
+ for (const r of n)
1934
2446
  try {
1935
- i();
1936
- } catch (d) {
1937
- console.error(`[Store error]: ${d}`);
2447
+ r();
2448
+ } catch (o) {
2449
+ console.error(`[Store error]: ${o}`);
1938
2450
  }
1939
- this.sliceUnsubs.delete(n);
2451
+ this.sliceUnsubs.delete(s);
1940
2452
  }
1941
2453
  if (delete this.reducers[e], t.deleteState) {
1942
- const { [n]: i, ...d } = this.state;
1943
- this.state = d;
2454
+ const { [s]: r, ...o } = this.state;
2455
+ this.state = o;
1944
2456
  }
1945
2457
  }
1946
2458
  /**
@@ -1970,13 +2482,13 @@ class $ {
1970
2482
  */
1971
2483
  getAtPath(e, t) {
1972
2484
  if (!t) return e;
1973
- const s = (t[0] === "." ? t.slice(1) : t).split(".");
1974
- let i = e;
1975
- for (const d of s) {
1976
- if (i == null) return;
1977
- i = i[d];
2485
+ const n = (t[0] === "." ? t.slice(1) : t).split(".");
2486
+ let r = e;
2487
+ for (const o of n) {
2488
+ if (r == null) return;
2489
+ r = r[o];
1978
2490
  }
1979
- return i;
2491
+ return r;
1980
2492
  }
1981
2493
  /**
1982
2494
  * Builds ancestor paths for a dotted path.
@@ -1995,263 +2507,258 @@ class $ {
1995
2507
  */
1996
2508
  static buildAncestorPaths(e) {
1997
2509
  if (!e) return [];
1998
- const n = (e[0] === "." ? e.slice(1) : e).split("."), s = [];
1999
- for (let i = 0; i < n.length; i++)
2000
- s.push(n.slice(0, i + 1).join("."));
2001
- return s;
2510
+ const s = (e[0] === "." ? e.slice(1) : e).split("."), n = [];
2511
+ for (let r = 0; r < s.length; r++)
2512
+ n.push(s.slice(0, r + 1).join("."));
2513
+ return n;
2002
2514
  }
2003
2515
  }
2004
- function L(o) {
2005
- return new $({
2006
- name: o.name,
2007
- reducer: o.reducer ?? {},
2008
- middleware: o.middleware ?? [],
2009
- effects: o.effects ?? [],
2010
- dedupWindowMs: o.dedupWindowMs,
2011
- idFactory: o.idFactory,
2012
- devtools: o.devtools,
2013
- onEffectError: o.onEffectError,
2014
- onReducerError: o.onReducerError
2516
+ function de(c) {
2517
+ return new x({
2518
+ ...c,
2519
+ reducer: c.reducer ?? {},
2520
+ middleware: c.middleware ?? [],
2521
+ effects: c.effects ?? []
2015
2522
  });
2016
2523
  }
2017
- const G = (o) => (e, t) => t.map((n) => [e, n]), Q = () => (o) => o, I = /* @__PURE__ */ new Set();
2018
- function j(o) {
2019
- const e = String(o);
2020
- I.has(e) || (I.add(e), console.warn(
2524
+ const ue = (c) => (e, t) => t.map((s) => [e, s]), fe = () => (c) => c, _ = /* @__PURE__ */ new Set();
2525
+ function re(c) {
2526
+ const e = String(c);
2527
+ _.has(e) || (_.add(e), console.warn(
2021
2528
  `[yoltra] Entity id "${e}" contains a dot. Paths are dotted, so a subscription to "entities.${e}" is indistinguishable from one to a nested object of the same name. Use ids without dots.`
2022
2529
  ));
2023
2530
  }
2024
- function F(o, e) {
2025
- if (o.length !== e.length) return e;
2026
- for (let t = 0; t < o.length; t++)
2027
- if (o[t] !== e[t]) return e;
2028
- return o;
2531
+ function ie(c, e) {
2532
+ if (c.length !== e.length) return e;
2533
+ for (let t = 0; t < c.length; t++)
2534
+ if (c[t] !== e[t]) return e;
2535
+ return c;
2029
2536
  }
2030
- function J(o = {}) {
2031
- const e = o.selectId ?? ((r) => r.id), { sortComparer: t } = o, n = (r, f) => {
2032
- if (t === void 0) return f;
2033
- const c = [...f].sort((u, l) => {
2034
- const y = r.entities[u], h = r.entities[l];
2035
- return y === void 0 || h === void 0 ? 0 : t(y, h);
2537
+ function he(c = {}) {
2538
+ const e = c.selectId ?? ((i) => i.id), { sortComparer: t } = c, s = (i, a) => {
2539
+ if (t === void 0) return a;
2540
+ const d = [...a].sort((u, f) => {
2541
+ const m = i.entities[u], h = i.entities[f];
2542
+ return m === void 0 || h === void 0 ? 0 : t(m, h);
2036
2543
  });
2037
- return F(f, c);
2038
- }, s = (r, f, c) => {
2039
- const u = { ...r, entities: f, ids: c };
2040
- return { ...u, ids: n(u, c) };
2041
- }, i = (r, f, c) => {
2042
- let u = null, l = null;
2043
- for (const y of f) {
2044
- const h = e(y);
2045
- process.env.NODE_ENV !== "production" && String(h).includes(".") && j(h);
2046
- const m = (u ?? r.entities)[h];
2047
- if (m !== void 0 && c === "add") continue;
2048
- const g = m !== void 0 && c === "upsert" ? { ...m, ...y } : y;
2049
- u ?? (u = { ...r.entities }), u[h] = g, m === void 0 && (l ?? (l = [...r.ids]), l.push(h));
2544
+ return ie(a, d);
2545
+ }, n = (i, a, d) => {
2546
+ const u = { ...i, entities: a, ids: d };
2547
+ return { ...u, ids: s(u, d) };
2548
+ }, r = (i, a, d) => {
2549
+ let u = null, f = null;
2550
+ for (const m of a) {
2551
+ const h = e(m);
2552
+ process.env.NODE_ENV !== "production" && String(h).includes(".") && re(h);
2553
+ const y = (u ?? i.entities)[h];
2554
+ if (y !== void 0 && d === "add") continue;
2555
+ const g = y !== void 0 && d === "upsert" ? { ...y, ...m } : m;
2556
+ u ?? (u = { ...i.entities }), u[h] = g, y === void 0 && (f ?? (f = [...i.ids]), f.push(h));
2050
2557
  }
2051
- return u === null ? r : s(r, u, l ?? r.ids);
2052
- }, d = (r, f) => {
2053
- let c = null;
2054
- for (const { id: u, changes: l } of f) {
2055
- const y = (c ?? r.entities)[u];
2056
- y !== void 0 && (c ?? (c = { ...r.entities }), c[u] = { ...y, ...l });
2558
+ return u === null ? i : n(i, u, f ?? i.ids);
2559
+ }, o = (i, a) => {
2560
+ let d = null;
2561
+ for (const { id: u, changes: f } of a) {
2562
+ const m = (d ?? i.entities)[u];
2563
+ m !== void 0 && (d ?? (d = { ...i.entities }), d[u] = { ...m, ...f });
2057
2564
  }
2058
- return c === null ? r : s(r, c, r.ids);
2059
- }, a = (r, f) => {
2060
- const c = new Set(f.filter((l) => r.entities[l] !== void 0));
2061
- if (c.size === 0) return r;
2062
- const u = { ...r.entities };
2063
- for (const l of c) delete u[l];
2064
- return s(
2065
- r,
2565
+ return d === null ? i : n(i, d, i.ids);
2566
+ }, l = (i, a) => {
2567
+ const d = new Set(a.filter((f) => i.entities[f] !== void 0));
2568
+ if (d.size === 0) return i;
2569
+ const u = { ...i.entities };
2570
+ for (const f of d) delete u[f];
2571
+ return n(
2572
+ i,
2066
2573
  u,
2067
- r.ids.filter((l) => !c.has(l))
2574
+ i.ids.filter((f) => !d.has(f))
2068
2575
  );
2069
2576
  };
2070
2577
  return {
2071
- getInitialState(r) {
2072
- const f = { ids: [], entities: {} };
2073
- return r === void 0 ? f : { ...f, ...r };
2578
+ getInitialState(i) {
2579
+ const a = { ids: [], entities: {} };
2580
+ return i === void 0 ? a : { ...a, ...i };
2074
2581
  },
2075
- addOne: (r, f) => i(r, [f], "add"),
2076
- addMany: (r, f) => i(r, f, "add"),
2077
- setOne: (r, f) => i(r, [f], "set"),
2078
- setMany: (r, f) => i(r, f, "set"),
2079
- setAll: (r, f) => {
2080
- const c = {}, u = [];
2081
- for (const l of f) {
2082
- const y = e(l);
2083
- c[y] === void 0 && u.push(y), c[y] = l;
2582
+ addOne: (i, a) => r(i, [a], "add"),
2583
+ addMany: (i, a) => r(i, a, "add"),
2584
+ setOne: (i, a) => r(i, [a], "set"),
2585
+ setMany: (i, a) => r(i, a, "set"),
2586
+ setAll: (i, a) => {
2587
+ const d = {}, u = [];
2588
+ for (const f of a) {
2589
+ const m = e(f);
2590
+ d[m] === void 0 && u.push(m), d[m] = f;
2084
2591
  }
2085
- return s(r, c, u);
2592
+ return n(i, d, u);
2086
2593
  },
2087
- updateOne: (r, f) => d(r, [f]),
2088
- updateMany: (r, f) => d(r, f),
2089
- upsertOne: (r, f) => i(r, [f], "upsert"),
2090
- upsertMany: (r, f) => i(r, f, "upsert"),
2091
- removeOne: (r, f) => a(r, [f]),
2092
- removeMany: (r, f) => a(r, f),
2093
- removeAll: (r) => r.ids.length === 0 ? r : s(r, {}, []),
2094
- selectIds: (r) => r.ids,
2095
- selectEntities: (r) => r.entities,
2096
- selectAll: (r) => r.ids.map((f) => r.entities[f]),
2097
- selectById: (r, f) => r.entities[f],
2098
- selectTotal: (r) => r.ids.length,
2594
+ updateOne: (i, a) => o(i, [a]),
2595
+ updateMany: (i, a) => o(i, a),
2596
+ upsertOne: (i, a) => r(i, [a], "upsert"),
2597
+ upsertMany: (i, a) => r(i, a, "upsert"),
2598
+ removeOne: (i, a) => l(i, [a]),
2599
+ removeMany: (i, a) => l(i, a),
2600
+ removeAll: (i) => i.ids.length === 0 ? i : n(i, {}, []),
2601
+ selectIds: (i) => i.ids,
2602
+ selectEntities: (i) => i.entities,
2603
+ selectAll: (i) => i.ids.map((a) => i.entities[a]),
2604
+ selectById: (i, a) => i.entities[a],
2605
+ selectTotal: (i) => i.ids.length,
2099
2606
  idsPath: "ids",
2100
- pathTo: (r, f) => f === void 0 ? `entities.${r}` : `entities.${r}.${f}`,
2101
- anyField: (r) => `entities.*.${r}`
2607
+ pathTo: (i, a) => a === void 0 ? `entities.${i}` : `entities.${i}.${a}`,
2608
+ anyField: (i) => `entities.*.${i}`
2102
2609
  };
2103
2610
  }
2104
2611
  const E = "$yoltra";
2105
- function _(o, e = {}) {
2106
- const t = e.maxNodes ?? 1e5, n = e.sanitize, s = [], i = /* @__PURE__ */ new Map();
2107
- let d = 0, a = !1;
2108
- function r(c, u) {
2109
- if (n !== void 0 && (c = n(u, c)), d += 1, d > t)
2110
- return a = !0, { [E]: "unsupported", kind: "truncated" };
2111
- switch (typeof c) {
2612
+ function B(c, e = {}) {
2613
+ const t = e.maxNodes ?? 1e5, s = e.sanitize, n = [], r = /* @__PURE__ */ new Map();
2614
+ let o = 0, l = !1;
2615
+ function i(d, u) {
2616
+ if (s !== void 0 && (d = s(u, d)), o += 1, o > t)
2617
+ return l = !0, { [E]: "unsupported", kind: "truncated" };
2618
+ switch (typeof d) {
2112
2619
  case "undefined":
2113
2620
  return { [E]: "undefined" };
2114
2621
  case "bigint":
2115
- return { [E]: "bigint", value: c.toString() };
2622
+ return { [E]: "bigint", value: d.toString() };
2116
2623
  case "number":
2117
- return Number.isNaN(c) ? { [E]: "nan" } : c === 1 / 0 ? { [E]: "infinity", sign: 1 } : c === -1 / 0 ? { [E]: "infinity", sign: -1 } : c;
2624
+ return Number.isNaN(d) ? { [E]: "nan" } : d === 1 / 0 ? { [E]: "infinity", sign: 1 } : d === -1 / 0 ? { [E]: "infinity", sign: -1 } : d;
2118
2625
  case "function":
2119
2626
  case "symbol":
2120
- return s.push(u), { [E]: "unsupported", kind: typeof c };
2627
+ return n.push(u), { [E]: "unsupported", kind: typeof d };
2121
2628
  case "string":
2122
2629
  case "boolean":
2123
- return c;
2630
+ return d;
2124
2631
  }
2125
- if (c === null) return null;
2126
- const l = c, y = i.get(l);
2127
- if (y !== void 0) return { [E]: "ref", path: y };
2128
- if (i.set(l, u), c instanceof Date)
2129
- return { [E]: "date", iso: c.toISOString() };
2130
- if (c instanceof RegExp)
2131
- return { [E]: "regexp", source: c.source, flags: c.flags };
2132
- if (c instanceof Error)
2133
- return { [E]: "error", name: c.name, message: c.message };
2134
- if (c instanceof Map) {
2135
- const m = [];
2632
+ if (d === null) return null;
2633
+ const f = d, m = r.get(f);
2634
+ if (m !== void 0) return { [E]: "ref", path: m };
2635
+ if (r.set(f, u), d instanceof Date)
2636
+ return { [E]: "date", iso: d.toISOString() };
2637
+ if (d instanceof RegExp)
2638
+ return { [E]: "regexp", source: d.source, flags: d.flags };
2639
+ if (d instanceof Error)
2640
+ return { [E]: "error", name: d.name, message: d.message };
2641
+ if (d instanceof Map) {
2642
+ const y = [];
2136
2643
  let g = 0;
2137
- for (const [w, D] of c)
2138
- m.push([r(w, `${u}/@k${g}`), r(D, `${u}/${g}`)]), g += 1;
2139
- return { [E]: "map", entries: m };
2644
+ for (const [w, k] of d)
2645
+ y.push([i(w, `${u}/@k${g}`), i(k, `${u}/${g}`)]), g += 1;
2646
+ return { [E]: "map", entries: y };
2140
2647
  }
2141
- if (c instanceof Set) {
2142
- const m = [];
2648
+ if (d instanceof Set) {
2649
+ const y = [];
2143
2650
  let g = 0;
2144
- for (const w of c)
2145
- m.push(r(w, `${u}/${g}`)), g += 1;
2146
- return { [E]: "set", values: m };
2651
+ for (const w of d)
2652
+ y.push(i(w, `${u}/${g}`)), g += 1;
2653
+ return { [E]: "set", values: y };
2147
2654
  }
2148
- if (Array.isArray(c))
2149
- return c.map((m, g) => r(m, `${u}/${g}`));
2655
+ if (Array.isArray(d))
2656
+ return d.map((y, g) => i(y, `${u}/${g}`));
2150
2657
  const h = {};
2151
- for (const [m, g] of Object.entries(c))
2152
- h[m] = r(g, `${u}/${A(m)}`);
2658
+ for (const [y, g] of Object.entries(d))
2659
+ h[y] = i(g, `${u}/${H(y)}`);
2153
2660
  return E in h ? { [E]: "escaped", value: h } : h;
2154
2661
  }
2155
- return { value: r(o, ""), report: { truncated: a, unsupported: s } };
2662
+ return { value: i(c, ""), report: { truncated: l, unsupported: n } };
2156
2663
  }
2157
- function V(o) {
2664
+ function oe(c) {
2158
2665
  const e = /* @__PURE__ */ new Map(), t = [];
2159
- function n(d, a) {
2160
- if (d === null || typeof d != "object") return d;
2161
- if (Array.isArray(d)) {
2162
- const f = [];
2163
- return e.set(a, f), d.forEach((c, u) => {
2164
- if (R(c)) {
2165
- t.push({ target: f, key: u, path: c.path }), f[u] = void 0;
2666
+ function s(o, l) {
2667
+ if (o === null || typeof o != "object") return o;
2668
+ if (Array.isArray(o)) {
2669
+ const a = [];
2670
+ return e.set(l, a), o.forEach((d, u) => {
2671
+ if (j(d)) {
2672
+ t.push({ target: a, key: u, path: d.path }), a[u] = void 0;
2166
2673
  return;
2167
2674
  }
2168
- f[u] = n(c, `${a}/${u}`);
2169
- }), f;
2675
+ a[u] = s(d, `${l}/${u}`);
2676
+ }), a;
2170
2677
  }
2171
- if (typeof d[E] == "string") {
2172
- const f = d;
2173
- switch (f[E]) {
2678
+ if (typeof o[E] == "string") {
2679
+ const a = o;
2680
+ switch (a[E]) {
2174
2681
  case "undefined":
2175
2682
  return;
2176
2683
  case "nan":
2177
2684
  return Number.NaN;
2178
2685
  case "infinity":
2179
- return f.sign === 1 ? 1 / 0 : -1 / 0;
2686
+ return a.sign === 1 ? 1 / 0 : -1 / 0;
2180
2687
  case "bigint":
2181
- return BigInt(f.value);
2688
+ return BigInt(a.value);
2182
2689
  case "date":
2183
- return new Date(f.iso);
2690
+ return new Date(a.iso);
2184
2691
  case "regexp":
2185
- return new RegExp(f.source, f.flags);
2692
+ return new RegExp(a.source, a.flags);
2186
2693
  case "error": {
2187
- const c = new Error(f.message);
2188
- return c.name = f.name, c;
2694
+ const d = new Error(a.message);
2695
+ return d.name = a.name, d;
2189
2696
  }
2190
2697
  case "unsupported":
2191
2698
  return;
2192
2699
  case "ref":
2193
2700
  return;
2194
2701
  case "map": {
2195
- const c = /* @__PURE__ */ new Map();
2196
- return e.set(a, c), f.entries.forEach(([u, l], y) => {
2197
- c.set(n(u, `${a}/@k${y}`), n(l, `${a}/${y}`));
2198
- }), c;
2702
+ const d = /* @__PURE__ */ new Map();
2703
+ return e.set(l, d), a.entries.forEach(([u, f], m) => {
2704
+ d.set(s(u, `${l}/@k${m}`), s(f, `${l}/${m}`));
2705
+ }), d;
2199
2706
  }
2200
2707
  case "set": {
2201
- const c = /* @__PURE__ */ new Set();
2202
- return e.set(a, c), f.values.forEach((u, l) => c.add(n(u, `${a}/${l}`))), c;
2708
+ const d = /* @__PURE__ */ new Set();
2709
+ return e.set(l, d), a.values.forEach((u, f) => d.add(s(u, `${l}/${f}`))), d;
2203
2710
  }
2204
2711
  case "escaped":
2205
- return s(f.value, a);
2712
+ return n(a.value, l);
2206
2713
  default:
2207
2714
  return;
2208
2715
  }
2209
2716
  }
2210
- return s(d, a);
2211
- }
2212
- function s(d, a) {
2213
- const r = {};
2214
- e.set(a, r);
2215
- for (const [f, c] of Object.entries(d)) {
2216
- const u = `${a}/${A(f)}`;
2217
- if (R(c)) {
2218
- t.push({ target: r, key: f, path: c.path }), r[f] = void 0;
2717
+ return n(o, l);
2718
+ }
2719
+ function n(o, l) {
2720
+ const i = {};
2721
+ e.set(l, i);
2722
+ for (const [a, d] of Object.entries(o)) {
2723
+ const u = `${l}/${H(a)}`;
2724
+ if (j(d)) {
2725
+ t.push({ target: i, key: a, path: d.path }), i[a] = void 0;
2219
2726
  continue;
2220
2727
  }
2221
- r[f] = n(c, u);
2728
+ i[a] = s(d, u);
2222
2729
  }
2223
- return r;
2730
+ return i;
2224
2731
  }
2225
- const i = n(o, "");
2226
- e.set("", i);
2227
- for (const { target: d, key: a, path: r } of t)
2228
- d[a] = e.get(r);
2229
- return i;
2732
+ const r = s(c, "");
2733
+ e.set("", r);
2734
+ for (const { target: o, key: l, path: i } of t)
2735
+ o[l] = e.get(i);
2736
+ return r;
2230
2737
  }
2231
- function R(o) {
2232
- return o !== null && typeof o == "object" && o[E] === "ref" && typeof o.path == "string";
2738
+ function j(c) {
2739
+ return c !== null && typeof c == "object" && c[E] === "ref" && typeof c.path == "string";
2233
2740
  }
2234
- function A(o) {
2235
- return o.replace(/~/g, "~0").replace(/\//g, "~1");
2741
+ function H(c) {
2742
+ return c.replace(/~/g, "~0").replace(/\//g, "~1");
2236
2743
  }
2237
- function Y(o, e, t = {}) {
2238
- let n = t.maxNodes ?? 1e5;
2239
- for (let s = 0; s < 8; s += 1) {
2240
- const { value: i, report: d } = _(o, { ...t, maxNodes: n });
2241
- let a;
2744
+ function pe(c, e, t = {}) {
2745
+ let s = t.maxNodes ?? 1e5;
2746
+ for (let n = 0; n < 8; n += 1) {
2747
+ const { value: r, report: o } = B(c, { ...t, maxNodes: s });
2748
+ let l;
2242
2749
  try {
2243
- a = JSON.stringify(i)?.length ?? 0;
2750
+ l = JSON.stringify(r)?.length ?? 0;
2244
2751
  } catch {
2245
- a = Number.POSITIVE_INFINITY;
2752
+ l = Number.POSITIVE_INFINITY;
2246
2753
  }
2247
- if (a <= e)
2248
- return d.truncated ? {
2249
- value: i,
2754
+ if (l <= e)
2755
+ return o.truncated ? {
2756
+ value: r,
2250
2757
  truncated: !0,
2251
- note: `State was too large to send in full; parts beyond ${n} nodes are omitted.`
2252
- } : { value: i, truncated: !1 };
2253
- const r = Math.floor(n * e * 0.8 / a);
2254
- if (n = Math.max(1, Math.min(r, n - 1)), n <= 1 && s > 0)
2758
+ note: `State was too large to send in full; parts beyond ${s} nodes are omitted.`
2759
+ } : { value: r, truncated: !1 };
2760
+ const i = Math.floor(s * e * 0.8 / l);
2761
+ if (s = Math.max(1, Math.min(i, s - 1)), s <= 1 && n > 0)
2255
2762
  break;
2256
2763
  }
2257
2764
  return {
@@ -2260,107 +2767,107 @@ function Y(o, e, t = {}) {
2260
2767
  note: `State exceeds the ${e}-byte transport limit and could not be reduced to fit.`
2261
2768
  };
2262
2769
  }
2263
- function b(o, e, t) {
2264
- o.onError?.(e, t);
2770
+ function v(c, e, t) {
2771
+ c.onError?.(e, t);
2265
2772
  }
2266
- async function q(o) {
2773
+ async function me(c) {
2267
2774
  const e = { slices: {}, restored: !1 };
2268
2775
  let t;
2269
2776
  try {
2270
- t = o.source ?? await o.adapter.read(o.key);
2271
- } catch (s) {
2272
- return b(o, s, "read"), e;
2777
+ t = c.source ?? await c.adapter.read(c.key);
2778
+ } catch (n) {
2779
+ return v(c, n, "read"), e;
2273
2780
  }
2274
2781
  if (t == null || t === "") return e;
2275
- let n;
2782
+ let s;
2276
2783
  try {
2277
- n = V(JSON.parse(t));
2278
- } catch (s) {
2279
- return b(o, s, "decode"), e;
2280
- }
2281
- if (n === null || typeof n != "object" || typeof n.version != "number")
2282
- return b(o, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
2283
- if (n.version !== o.version) {
2284
- if (o.migrate === void 0)
2285
- return b(
2286
- o,
2784
+ s = oe(JSON.parse(t));
2785
+ } catch (n) {
2786
+ return v(c, n, "decode"), e;
2787
+ }
2788
+ if (s === null || typeof s != "object" || typeof s.version != "number")
2789
+ return v(c, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
2790
+ if (s.version !== c.version) {
2791
+ if (c.migrate === void 0)
2792
+ return v(
2793
+ c,
2287
2794
  new Error(
2288
- `persisted state is version ${n.version}, this build expects ${o.version}, and no migrate was supplied`
2795
+ `persisted state is version ${s.version}, this build expects ${c.version}, and no migrate was supplied`
2289
2796
  ),
2290
2797
  "migrate"
2291
2798
  ), e;
2292
2799
  try {
2293
- const s = o.migrate(n.slices, n.version);
2294
- return s === null ? e : { slices: s, restored: !0 };
2295
- } catch (s) {
2296
- return b(o, s, "migrate"), e;
2800
+ const n = c.migrate(s.slices, s.version);
2801
+ return n === null ? e : { slices: n, restored: !0 };
2802
+ } catch (n) {
2803
+ return v(c, n, "migrate"), e;
2297
2804
  }
2298
2805
  }
2299
- return { slices: n.slices ?? {}, restored: !0 };
2806
+ return { slices: s.slices ?? {}, restored: !0 };
2300
2807
  }
2301
- function X(o, e) {
2302
- if (!e.restored) return o;
2808
+ function ye(c, e) {
2809
+ if (!e.restored) return c;
2303
2810
  const t = {};
2304
- for (const [n, s] of Object.entries(o)) {
2305
- const i = e.slices[n];
2306
- t[n] = i === void 0 ? s : { ...s, state: i };
2811
+ for (const [s, n] of Object.entries(c)) {
2812
+ const r = e.slices[s];
2813
+ t[s] = r === void 0 ? n : { ...n, state: r };
2307
2814
  }
2308
2815
  return t;
2309
2816
  }
2310
- function N(o, e) {
2311
- const t = o ?? {}, n = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((s) => s in t).map((s) => [s, t[s]]));
2312
- return JSON.stringify(_({ version: e.version, slices: n }).value);
2817
+ function W(c, e) {
2818
+ const t = c ?? {}, s = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((n) => n in t).map((n) => [n, t[n]]));
2819
+ return JSON.stringify(B({ version: e.version, slices: s }).value);
2313
2820
  }
2314
- function Z(o, e) {
2315
- const t = e.throttleMs ?? 250, n = e.slices;
2316
- let s = null, i = !1;
2317
- const d = () => {
2318
- if (i) {
2319
- i = !1;
2821
+ function ge(c, e) {
2822
+ const t = e.throttleMs ?? 250, s = e.slices;
2823
+ let n = null, r = !1;
2824
+ const o = () => {
2825
+ if (r) {
2826
+ r = !1;
2320
2827
  try {
2321
- const f = e.adapter.write(e.key, N(o.getState(), e));
2322
- f instanceof Promise && f.catch((c) => b(e, c, "write"));
2323
- } catch (f) {
2324
- b(e, f, "write");
2828
+ const a = e.adapter.write(e.key, W(c.getState(), e));
2829
+ a instanceof Promise && a.catch((d) => v(e, d, "write"));
2830
+ } catch (a) {
2831
+ v(e, a, "write");
2325
2832
  }
2326
2833
  }
2327
- }, a = () => {
2328
- if (i = !0, t <= 0) {
2329
- d();
2834
+ }, l = () => {
2835
+ if (r = !0, t <= 0) {
2836
+ o();
2330
2837
  return;
2331
2838
  }
2332
- s === null && (s = setTimeout(() => {
2333
- s = null, d();
2334
- }, t), s.unref?.());
2335
- }, r = o.instrument((f) => {
2336
- if (n === void 0) {
2337
- a();
2839
+ n === null && (n = setTimeout(() => {
2840
+ n = null, o();
2841
+ }, t), n.unref?.());
2842
+ }, i = c.instrument((a) => {
2843
+ if (s === void 0) {
2844
+ l();
2338
2845
  return;
2339
2846
  }
2340
- (f.changedPaths ?? []).some(
2341
- (u) => n.some((l) => u === l || u.startsWith(`${l}.`))
2342
- ) && a();
2847
+ (a.changedPaths ?? []).some(
2848
+ (u) => s.some((f) => u === f || u.startsWith(`${f}.`))
2849
+ ) && l();
2343
2850
  });
2344
2851
  return () => {
2345
- r(), s !== null && (clearTimeout(s), s = null), d();
2852
+ i(), n !== null && (clearTimeout(n), n = null), o();
2346
2853
  };
2347
2854
  }
2348
- function ee(o, e) {
2349
- return N(o.getState(), e);
2855
+ function we(c, e) {
2856
+ return W(c.getState(), e);
2350
2857
  }
2351
- function te(o) {
2858
+ function Ee(c) {
2352
2859
  return {
2353
- read: (e) => o.getItem(e),
2354
- write: (e, t) => o.setItem(e, t),
2355
- remove: (e) => o.removeItem(e)
2860
+ read: (e) => c.getItem(e),
2861
+ write: (e, t) => c.setItem(e, t),
2862
+ remove: (e) => c.removeItem(e)
2356
2863
  };
2357
2864
  }
2358
- function se(o) {
2359
- const e = new Map(Object.entries(o ?? {}));
2865
+ function be(c) {
2866
+ const e = new Map(Object.entries(c ?? {}));
2360
2867
  return {
2361
2868
  read: (t) => e.get(t) ?? null,
2362
- write: (t, n) => {
2363
- e.set(t, n);
2869
+ write: (t, s) => {
2870
+ e.set(t, s);
2364
2871
  },
2365
2872
  remove: (t) => {
2366
2873
  e.delete(t);
@@ -2368,24 +2875,28 @@ function se(o) {
2368
2875
  };
2369
2876
  }
2370
2877
  export {
2371
- B as EventBus,
2372
- H as LooseEventBus,
2373
- K as Reducer,
2374
- $ as Store,
2375
- J as createEntityAdapter,
2376
- se as createMemoryAdapter,
2377
- L as createStore,
2378
- te as createWebStorageAdapter,
2379
- V as decodeState,
2380
- ee as dehydrate,
2381
- O as detectChangedProps,
2382
- _ as encodeState,
2383
- Y as encodeStateBounded,
2384
- Q as eventKeys,
2385
- S as freezeState,
2386
- q as hydrate,
2387
- Z as persist,
2388
- G as typedEvents,
2389
- X as withHydration
2878
+ C as CallAbortedError,
2879
+ J as CallTimeoutError,
2880
+ U as EventBus,
2881
+ L as LooseEventBus,
2882
+ V as Reducer,
2883
+ le as Rejected,
2884
+ x as Store,
2885
+ he as createEntityAdapter,
2886
+ be as createMemoryAdapter,
2887
+ de as createStore,
2888
+ Ee as createWebStorageAdapter,
2889
+ oe as decodeState,
2890
+ we as dehydrate,
2891
+ I as detectChangedProps,
2892
+ B as encodeState,
2893
+ pe as encodeStateBounded,
2894
+ fe as eventKeys,
2895
+ M as freezeState,
2896
+ me as hydrate,
2897
+ Q as isRejected,
2898
+ ge as persist,
2899
+ ue as typedEvents,
2900
+ ye as withHydration
2390
2901
  };
2391
2902
  //# sourceMappingURL=yoltra.mjs.map