@yoltra/core 0.5.0 → 0.7.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,23 +1,13 @@
1
1
  /*!
2
- * @yoltra/core v0.5.0
2
+ * @yoltra/core v0.7.0
3
3
  * (c) 2026 Manu Ramirez <@pixerael>
4
- * License: MIT
5
- * Homepage: https://yoltra.dev
6
- *
7
- * This source code is licensed under the MIT license found in the
8
- * LICENSE file in the root directory of this source tree
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 {
14
- constructor() {
15
- /**
16
- * Internal registry: `channel → type → Set<handler>`.
17
- * @internal
18
- */
19
- p(this, "handlers", /* @__PURE__ */ new Map());
20
- }
4
+ * License: MIT */
5
+ class W {
6
+ /**
7
+ * Internal registry: `channel → type → Set<handler>`.
8
+ * @internal
9
+ */
10
+ handlers = /* @__PURE__ */ new Map();
21
11
  /**
22
12
  * Subscribes a handler to an exact `(channel, type)`.
23
13
  *
@@ -50,11 +40,11 @@ class B {
50
40
  *
51
41
  * @public
52
42
  */
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);
43
+ on(e, t, s) {
44
+ let n = this.handlers.get(e);
45
+ n || (n = /* @__PURE__ */ new Map(), this.handlers.set(e, n));
46
+ let r = n.get(t);
47
+ return r || (r = /* @__PURE__ */ new Set(), n.set(t, r)), r.add(s), () => this.off(e, t, s);
58
48
  }
59
49
  /**
60
50
  * Removes a specific handler previously added with {@link EventBus.on | `on`}.
@@ -76,11 +66,11 @@ class B {
76
66
  *
77
67
  * @public
78
68
  */
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));
69
+ off(e, t, s) {
70
+ const n = this.handlers.get(e);
71
+ if (!n) return;
72
+ const r = n.get(t);
73
+ r && (r.delete(s), r.size === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e));
84
74
  }
85
75
  /**
86
76
  * Emits an event to all subscribers of the exact `(channel, type)`.
@@ -104,16 +94,16 @@ class B {
104
94
  *
105
95
  * @public
106
96
  */
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])
97
+ emit(e, t, s, n) {
98
+ const r = this.handlers.get(e);
99
+ if (!r) return;
100
+ const c = r.get(t);
101
+ if (!(!c || c.size === 0))
102
+ for (const l of [...c])
113
103
  try {
114
- a(n, s);
115
- } catch (r) {
116
- console.error("EventBus handler error:", r);
104
+ l(s, n);
105
+ } catch (i) {
106
+ console.error("EventBus handler error:", i);
117
107
  }
118
108
  }
119
109
  /**
@@ -133,37 +123,35 @@ class B {
133
123
  this.handlers.clear();
134
124
  }
135
125
  }
136
- class H {
137
- constructor() {
138
- /**
139
- * Exact handlers: `channel → type → [handlers]`.
140
- * @internal
141
- */
142
- p(this, "handlers", /* @__PURE__ */ new Map());
143
- /**
144
- * Pattern handlers with `*` and `**`: `channel → pattern(string) → [handlers]`.
145
- * @internal
146
- */
147
- p(this, "patternHandlers", /* @__PURE__ */ new Map());
148
- /**
149
- * Patterns bucketed by their first segment, so an emit tests only what could match.
150
- *
151
- * @remarks
152
- * Delivery used to walk every pattern registered on the channel and run the full segment
153
- * matcher against each. That is linear in the number of patterns rather than in the number
154
- * that match, and it re-split both the pattern and the subject on every test — for a thousand
155
- * patterns, two thousand string splits to deliver one event.
156
- *
157
- * A subject's first segment can only be matched by a pattern whose first segment is that same
158
- * literal, or is `*` or `**`. Bucketing on that turns the common shape — distinct event
159
- * families like `panel.*` and `order.**` — from a scan of everything into a map lookup plus
160
- * the handful that begin with a wildcard.
161
- *
162
- * It buys nothing for a channel where every pattern starts with `**`, since all of those must
163
- * still be tested. That is the honest worst case, and it is unchanged rather than worsened.
164
- */
165
- p(this, "patternIndex", /* @__PURE__ */ new Map());
166
- }
126
+ class U {
127
+ /**
128
+ * Exact handlers: `channel → type → [handlers]`.
129
+ * @internal
130
+ */
131
+ handlers = /* @__PURE__ */ new Map();
132
+ /**
133
+ * Pattern handlers with `*` and `**`: `channel → pattern(string) → [handlers]`.
134
+ * @internal
135
+ */
136
+ patternHandlers = /* @__PURE__ */ new Map();
137
+ /**
138
+ * Patterns bucketed by their first segment, so an emit tests only what could match.
139
+ *
140
+ * @remarks
141
+ * Delivery used to walk every pattern registered on the channel and run the full segment
142
+ * matcher against each. That is linear in the number of patterns rather than in the number
143
+ * that match, and it re-split both the pattern and the subject on every test — for a thousand
144
+ * patterns, two thousand string splits to deliver one event.
145
+ *
146
+ * A subject's first segment can only be matched by a pattern whose first segment is that same
147
+ * literal, or is `*` or `**`. Bucketing on that turns the common shape — distinct event
148
+ * families like `panel.*` and `order.**` — from a scan of everything into a map lookup plus
149
+ * the handful that begin with a wildcard.
150
+ *
151
+ * It buys nothing for a channel where every pattern starts with `**`, since all of those must
152
+ * still be tested. That is the honest worst case, and it is unchanged rather than worsened.
153
+ */
154
+ patternIndex = /* @__PURE__ */ new Map();
167
155
  /**
168
156
  * Subscribes a handler to either an **exact** type or a **pattern**.
169
157
  *
@@ -196,18 +184,18 @@ class H {
196
184
  *
197
185
  * @public
198
186
  */
199
- on(e, t, n) {
200
- const s = String(t);
201
- if (this.isPattern(s)) {
202
- const i = s;
187
+ on(e, t, s) {
188
+ const n = String(t);
189
+ if (this.isPattern(n)) {
190
+ const r = n;
203
191
  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);
192
+ const c = this.patternHandlers.get(e);
193
+ return c.has(r) || (c.set(r, []), this.indexPattern(e, r)), c.get(r).push(s), () => this.offPattern(e, r, s);
206
194
  } else {
207
- const i = this.normalizeTypeKey(s);
195
+ const r = this.normalizeTypeKey(n);
208
196
  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);
197
+ const c = this.handlers.get(e);
198
+ return c.has(r) || c.set(r, []), c.get(r).push(s), () => this.offExactNormalized(e, r, s);
211
199
  }
212
200
  }
213
201
  /**
@@ -228,9 +216,9 @@ class H {
228
216
  *
229
217
  * @public
230
218
  */
231
- off(e, t, n) {
232
- const s = this.normalizeTypeKey(String(t));
233
- this.offExactNormalized(e, s, n);
219
+ off(e, t, s) {
220
+ const n = this.normalizeTypeKey(String(t));
221
+ this.offExactNormalized(e, n, s);
234
222
  }
235
223
  /**
236
224
  * Internal exact unsubscription using an already **normalized** type key.
@@ -240,13 +228,13 @@ class H {
240
228
  * @param handler - Handler to remove.
241
229
  * @internal
242
230
  */
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);
231
+ offExactNormalized(e, t, s) {
232
+ const n = this.handlers.get(e);
233
+ if (!n) return;
234
+ const r = n.get(t);
235
+ if (!r) return;
236
+ const c = r.indexOf(s);
237
+ c !== -1 && r.splice(c, 1), r.length === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e);
250
238
  }
251
239
  /**
252
240
  * Internal removal for a **pattern** subscription. No-ops if missing.
@@ -256,13 +244,13 @@ class H {
256
244
  * @param handler - Handler to remove.
257
245
  * @internal
258
246
  */
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));
247
+ offPattern(e, t, s) {
248
+ const n = this.patternHandlers.get(e);
249
+ if (!n) return;
250
+ const r = n.get(t);
251
+ if (!r) return;
252
+ const c = r.indexOf(s);
253
+ c !== -1 && r.splice(c, 1), r.length === 0 && (n.delete(t), this.unindexPattern(e, t)), n.size === 0 && (this.patternHandlers.delete(e), this.patternIndex.delete(e));
266
254
  }
267
255
  /**
268
256
  * Emits an event to all exact subscribers first, then to **matching pattern** subscribers.
@@ -284,21 +272,21 @@ class H {
284
272
  *
285
273
  * @public
286
274
  */
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);
275
+ emit(e, t, s) {
276
+ const n = String(t), r = this.normalizeTypeKey(n), c = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n), i = /* @__PURE__ */ new Set(), a = (u) => {
277
+ for (const d of [...u])
278
+ if (!i.has(d)) {
279
+ i.add(d);
292
280
  try {
293
- u(n);
294
- } catch (l) {
295
- console.error(l);
281
+ d(s);
282
+ } catch (h) {
283
+ console.error(h);
296
284
  continue;
297
285
  }
298
286
  }
299
287
  };
300
- f(d);
301
- for (const c of a) f(c);
288
+ a(c);
289
+ for (const u of l) a(u);
302
290
  }
303
291
  /**
304
292
  * Emits a payload that is only built if somebody is listening.
@@ -316,23 +304,23 @@ class H {
316
304
  *
317
305
  * @public
318
306
  */
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);
307
+ emitWith(e, t, s) {
308
+ const n = String(t), r = this.normalizeTypeKey(n), c = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n);
309
+ if (c.length === 0 && l.length === 0) return;
310
+ const i = s(), a = /* @__PURE__ */ new Set(), u = (d) => {
311
+ for (const h of [...d])
312
+ if (!a.has(h)) {
313
+ a.add(h);
326
314
  try {
327
- l(r);
328
- } catch (y) {
329
- console.error(y);
315
+ h(i);
316
+ } catch (p) {
317
+ console.error(p);
330
318
  continue;
331
319
  }
332
320
  }
333
321
  };
334
- c(d);
335
- for (const u of a) c(u);
322
+ u(c);
323
+ for (const d of l) u(d);
336
324
  }
337
325
  /**
338
326
  * Determines if a string is a **pattern** (contains `*`).
@@ -371,27 +359,27 @@ class H {
371
359
  * @internal
372
360
  */
373
361
  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);
362
+ let s = this.patternIndex.get(e);
363
+ s === void 0 && (s = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, s));
364
+ const n = this.splitPath(t), r = { pattern: t, segments: n }, c = n[0];
365
+ if (c === void 0 || c === "*" || c === "**") {
366
+ s.anyHead.push(r);
379
367
  return;
380
368
  }
381
- const a = n.byHead.get(d);
382
- a === void 0 ? n.byHead.set(d, [i]) : a.push(i);
369
+ const l = s.byHead.get(c);
370
+ l === void 0 ? s.byHead.set(c, [r]) : l.push(r);
383
371
  }
384
372
  /**
385
373
  * Removes a pattern from the index. Paired with {@link LooseEventBus.offPattern}.
386
374
  * @internal
387
375
  */
388
376
  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);
377
+ const s = this.patternIndex.get(e);
378
+ if (s === void 0) return;
379
+ const n = this.splitPath(t)[0], r = n === void 0 || n === "*" || n === "**" ? s.anyHead : s.byHead.get(n);
380
+ if (r === void 0) return;
381
+ const c = r.findIndex((l) => l.pattern === t);
382
+ c !== -1 && r.splice(c, 1), r.length === 0 && r !== s.anyHead && n !== void 0 && s.byHead.delete(n);
395
383
  }
396
384
  /**
397
385
  * The handler lists of every pattern matching this subject.
@@ -405,20 +393,20 @@ class H {
405
393
  * @internal
406
394
  */
407
395
  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);
396
+ const s = this.patternHandlers.get(e), n = this.patternIndex.get(e);
397
+ if (s === void 0 || s.size === 0 || n === void 0) return [];
398
+ const r = this.splitPath(t), c = [], l = (a) => {
399
+ for (const u of a) {
400
+ if (!this.matchSegments(u.segments, r)) continue;
401
+ const d = s.get(u.pattern);
402
+ d !== void 0 && c.push(d);
415
403
  }
416
- }, r = i[0];
417
- if (r !== void 0) {
418
- const f = s.byHead.get(r);
419
- f !== void 0 && a(f);
404
+ }, i = r[0];
405
+ if (i !== void 0) {
406
+ const a = n.byHead.get(i);
407
+ a !== void 0 && l(a);
420
408
  }
421
- return a(s.anyHead), d;
409
+ return l(n.anyHead), c;
422
410
  }
423
411
  /**
424
412
  * Pattern matcher over dot-separated segments, which arrive already split.
@@ -449,18 +437,18 @@ class H {
449
437
  * @internal
450
438
  */
451
439
  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;
440
+ let s = 0, n = 0, r = -1, c = 0;
441
+ for (; n < t.length; )
442
+ if (s < e.length && (e[s] === "*" || e[s] === t[n]))
443
+ s++, n++;
444
+ else if (s < e.length && e[s] === "**")
445
+ r = s, c = n, s++;
446
+ else if (r !== -1)
447
+ s = r + 1, n = ++c;
460
448
  else
461
449
  return !1;
462
- for (; n < e.length && e[n] === "**"; ) n++;
463
- return n === e.length;
450
+ for (; s < e.length && e[s] === "**"; ) s++;
451
+ return s === e.length;
464
452
  }
465
453
  /**
466
454
  * Removes **all** listeners (exact and pattern). Useful for tests/HMR teardown.
@@ -485,16 +473,21 @@ class H {
485
473
  */
486
474
  __introspect() {
487
475
  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 });
476
+ for (const [t, s] of this.handlers)
477
+ for (const [n, r] of s)
478
+ r.length > 0 && e.push({ channel: t, type: n, count: r.length });
479
+ for (const [t, s] of this.patternHandlers)
480
+ for (const [n, r] of s)
481
+ r.length > 0 && e.push({ channel: t, type: n, count: r.length });
494
482
  return e;
495
483
  }
496
484
  }
497
- class K {
485
+ class L {
486
+ /**
487
+ * The underlying pure reducer function.
488
+ * @internal
489
+ */
490
+ _reduce;
498
491
  /**
499
492
  * Creates a new {@link Reducer} from a pure reducer function.
500
493
  *
@@ -511,11 +504,6 @@ class K {
511
504
  * @public
512
505
  */
513
506
  constructor(e) {
514
- /**
515
- * The underlying pure reducer function.
516
- * @internal
517
- */
518
- p(this, "_reduce");
519
507
  this._reduce = e;
520
508
  }
521
509
  /**
@@ -523,7 +511,7 @@ class K {
523
511
  *
524
512
  * @param state - Current state.
525
513
  * @param event - An event drawn from {@link EventUnion | `EventUnion<EM>`}.
526
- * @returns The next state produced by the underlying reducer function.
514
+ * @returns The next state, or a {@link Rejection} if the reducer refused the write.
527
515
  *
528
516
  * @example
529
517
  * ```ts
@@ -536,115 +524,336 @@ class K {
536
524
  return this._reduce(e, t);
537
525
  }
538
526
  }
539
- const k = /* @__PURE__ */ new Set();
540
- function M(o, e) {
527
+ const T = /* @__PURE__ */ new Set();
528
+ function O(o, e) {
541
529
  const t = o ? `${o}.${e}` : e;
542
- k.has(t) || (k.add(t), console.warn(
530
+ T.has(t) || (T.add(t), console.warn(
543
531
  `[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.`
544
532
  ));
545
533
  }
546
- function O(o, e, t = "", n = /* @__PURE__ */ new Map()) {
547
- const s = [];
548
- return v(o, e, t, n, s), s;
534
+ function I(o, e, t = "", s = /* @__PURE__ */ new Map()) {
535
+ const n = [];
536
+ return P(o, e, t, s, n), n;
549
537
  }
550
- function v(o, e, t, n, s) {
538
+ function P(o, e, t, s, n) {
551
539
  if (o === e) return;
552
540
  if (typeof o != "object" || typeof e != "object" || o === null || e === null) {
553
541
  if (typeof o == "number" && Number.isNaN(o) && Number.isNaN(e))
554
542
  return;
555
- s.push(t);
543
+ n.push(t);
556
544
  return;
557
545
  }
558
546
  if (o instanceof Date && e instanceof Date) {
559
- o.getTime() !== e.getTime() && s.push(t);
547
+ o.getTime() !== e.getTime() && n.push(t);
560
548
  return;
561
549
  }
562
550
  if (o instanceof RegExp && e instanceof RegExp) {
563
- (o.source !== e.source || e.flags !== o.flags) && s.push(t);
551
+ (o.source !== e.source || e.flags !== o.flags) && n.push(t);
564
552
  return;
565
553
  }
566
554
  if (o instanceof Map || e instanceof Map) {
567
- s.push(t);
555
+ n.push(t);
568
556
  return;
569
557
  }
570
558
  if (o instanceof Set || e instanceof Set) {
571
- s.push(t);
559
+ n.push(t);
572
560
  return;
573
561
  }
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);
562
+ const r = o, c = e, l = s.get(r);
563
+ if (l?.has(c)) return;
564
+ const i = l ?? /* @__PURE__ */ new Set();
565
+ i.add(c), l || s.set(r, i);
578
566
  try {
579
- const f = Array.isArray(o), c = Array.isArray(e);
580
- if (f !== c) {
581
- s.push(t);
567
+ const a = Array.isArray(o), u = Array.isArray(e);
568
+ if (a !== u) {
569
+ n.push(t);
582
570
  return;
583
571
  }
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);
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}`);
572
+ if (a) {
573
+ const f = o, m = e;
574
+ f.length !== m.length && t && n.push(t);
575
+ const y = Math.min(f.length, m.length);
576
+ for (let g = 0; g < y; g++)
577
+ f[g] !== m[g] && P(f[g], m[g], t ? `${t}.${g}` : `${g}`, s, n);
578
+ for (let g = y; g < Math.max(f.length, m.length); g++)
579
+ n.push(t ? `${t}.${g}` : `${g}`);
592
580
  return;
593
581
  }
594
- const u = Object.keys(o), l = Object.keys(e);
595
- if (u.length === 0 && l.length === 0) {
596
- s.push(t);
582
+ const d = Object.keys(o), h = Object.keys(e);
583
+ if (d.length === 0 && h.length === 0) {
584
+ n.push(t);
597
585
  return;
598
586
  }
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;
587
+ let p = d.length === h.length;
588
+ if (p) {
589
+ for (let f = 0; f < h.length; f++)
590
+ if (!Object.prototype.hasOwnProperty.call(o, h[f])) {
591
+ p = !1;
604
592
  break;
605
593
  }
606
594
  }
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));
595
+ if (p) {
596
+ for (const f of h)
597
+ o[f] !== e[f] && (process.env.NODE_ENV !== "production" && f.includes(".") && O(t, f), P(o[f], e[f], t ? `${t}.${f}` : f, s, n));
610
598
  return;
611
599
  }
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);
616
- const g = t ? `${t}.${h}` : h;
600
+ for (const f of h) {
601
+ const m = Object.prototype.hasOwnProperty.call(o, f);
602
+ if (m && o[f] === e[f]) continue;
603
+ process.env.NODE_ENV !== "production" && f.includes(".") && O(t, f);
604
+ const y = t ? `${t}.${f}` : f;
617
605
  if (!m) {
618
- s.push(g);
606
+ n.push(y);
619
607
  continue;
620
608
  }
621
- v(o[h], e[h], g, n, s);
609
+ P(o[f], e[f], y, s, n);
622
610
  }
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));
611
+ for (const f of d)
612
+ Object.prototype.hasOwnProperty.call(e, f) || (process.env.NODE_ENV !== "production" && f.includes(".") && O(t, f), n.push(t ? `${t}.${f}` : f));
625
613
  } finally {
626
- r.delete(d), r.size === 0 && n.delete(i);
614
+ i.delete(c), i.size === 0 && s.delete(r);
627
615
  }
628
616
  }
629
- function S(o, e = /* @__PURE__ */ new WeakSet(), t) {
617
+ function M(o, e = /* @__PURE__ */ new WeakSet(), t) {
630
618
  if (o === null || typeof o != "object" || e.has(o) || (t !== void 0 && o === t.watch && t.onFound(), Object.isFrozen(o))) return o;
631
619
  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);
620
+ const s = o;
621
+ for (let n = 0; n < s.length; n++)
622
+ s[n] = M(s[n], e, t);
623
+ return Object.freeze(s);
636
624
  }
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));
625
+ for (const s of Object.getOwnPropertyNames(o)) {
626
+ const n = Object.getOwnPropertyDescriptor(o, s);
627
+ !n || !("value" in n) || (o[s] = M(o[s], e, t));
640
628
  }
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));
629
+ for (const s of Object.getOwnPropertySymbols(o)) {
630
+ const n = Object.getOwnPropertyDescriptor(o, s);
631
+ !n || !("value" in n) || (o[s] = M(o[s], e, t));
644
632
  }
645
633
  return Object.freeze(o);
646
634
  }
647
- function W(o, e) {
635
+ const B = /* @__PURE__ */ Symbol.for("yoltra.rejected");
636
+ function fe(o) {
637
+ return { [B]: !0, reason: o };
638
+ }
639
+ function V(o) {
640
+ return typeof o == "object" && o !== null && o[B] === !0;
641
+ }
642
+ class Q extends Error {
643
+ channel;
644
+ type;
645
+ idleMs;
646
+ constructor(e, t, s) {
647
+ super(
648
+ `[yoltra] call to "${e}/${t}" saw no correlated reply for ${s}ms. The timeout is idle rather than total, so this means the responder went quiet, not that it was slow. Check that something handles "${e}/${t}" 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.`
649
+ ), this.name = "CallTimeoutError", this.channel = e, this.type = t, this.idleMs = s;
650
+ }
651
+ }
652
+ class C extends Error {
653
+ constructor(e) {
654
+ super(`[yoltra] call aborted: ${e}`), this.name = "CallAbortedError";
655
+ }
656
+ }
657
+ function G(o) {
658
+ const [e, t] = o;
659
+ if (t === void 0) return { channel: e, isTerminal: () => !0 };
660
+ if (typeof t == "string") return { channel: e, isTerminal: (n) => n === t };
661
+ const s = new Set(t);
662
+ return { channel: e, isTerminal: (n) => s.has(n) };
663
+ }
664
+ function J(o, e, t) {
665
+ return o.parentId === e ? !0 : t === void 0 ? !1 : o.meta?.correlationId === t;
666
+ }
667
+ class q {
668
+ constructor(e) {
669
+ this.highWaterMark = e;
670
+ }
671
+ highWaterMark;
672
+ buffer = [];
673
+ /** Consumers parked in `take`, oldest first. */
674
+ takers = [];
675
+ /** Producers parked in `put`, each with the item they are waiting to hand over. */
676
+ putters = [];
677
+ consuming = !1;
678
+ /** No more items will be accepted, but what is already here is still owed to the consumer. */
679
+ ended = !1;
680
+ /** Abandoned: nothing further is owed to anybody. */
681
+ closed = !1;
682
+ /** Items discarded because nobody was iterating and the buffer was full. */
683
+ dropped = 0;
684
+ /** How many items were discarded for want of a consumer. */
685
+ get droppedCount() {
686
+ return this.dropped;
687
+ }
688
+ /**
689
+ * Marks that a consumer has started pulling. From here on, a full buffer parks the producer
690
+ * rather than dropping.
691
+ */
692
+ beginConsuming() {
693
+ this.consuming = !0;
694
+ }
695
+ /**
696
+ * Offers an item. The returned promise settles when the item has been taken — or immediately,
697
+ * if it fit in the buffer or was dropped.
698
+ */
699
+ put(e) {
700
+ if (this.closed || this.ended) return Promise.resolve();
701
+ const t = this.takers.shift();
702
+ 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) => {
703
+ this.putters.push({ item: e, release: s });
704
+ }) : (this.dropped++, Promise.resolve());
705
+ }
706
+ /** Takes the next item, waiting if none is available. Resolves `done` once closed and drained. */
707
+ take() {
708
+ this.consuming = !0;
709
+ const e = this.buffer.shift();
710
+ if (e !== void 0) {
711
+ const s = this.putters.shift();
712
+ return s !== void 0 && (this.buffer.push(s.item), s.release()), Promise.resolve({ value: e, done: !1 });
713
+ }
714
+ const t = this.putters.shift();
715
+ 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) => {
716
+ this.takers.push(s);
717
+ });
718
+ }
719
+ /**
720
+ * Stops accepting items, but keeps owing the consumer everything already queued.
721
+ *
722
+ * @remarks
723
+ * What the terminal reply does. Closing outright at that moment would throw away progress the
724
+ * responder had already handed over and the consumer had not yet read — which is exactly what
725
+ * happened before this existed: a six-step job delivered five steps, because the sixth was in
726
+ * the buffer when `done` arrived and the buffer was cleared. The terminal event says "no more
727
+ * is coming", not "forget what you were given".
728
+ */
729
+ end() {
730
+ if (this.ended || this.closed) return;
731
+ this.ended = !0;
732
+ let e = this.putters.shift();
733
+ for (; e !== void 0; )
734
+ this.buffer.push(e.item), e.release(), e = this.putters.shift();
735
+ let t = this.takers.shift();
736
+ for (; t !== void 0; ) {
737
+ const s = this.buffer.shift();
738
+ t(
739
+ s !== void 0 ? { value: s, done: !1 } : { value: void 0, done: !0 }
740
+ ), t = this.takers.shift();
741
+ }
742
+ }
743
+ /**
744
+ * Closes the queue: waiting consumers are told `done`, and **every parked producer is
745
+ * released**.
746
+ *
747
+ * @remarks
748
+ * Releasing producers is not tidying up. A producer parked on `put` is a pending `await emit`
749
+ * somewhere; leaving it parked when the call has already settled would hang the responder for
750
+ * good — turning a timed-out call into a wedged process, which is worse than the problem
751
+ * backpressure was added to solve.
752
+ */
753
+ close() {
754
+ if (this.closed) return;
755
+ this.closed = !0, this.buffer.length = 0;
756
+ let e = this.takers.shift();
757
+ for (; e !== void 0; )
758
+ e({ value: void 0, done: !0 }), e = this.takers.shift();
759
+ let t = this.putters.shift();
760
+ for (; t !== void 0; )
761
+ t.release(), t = this.putters.shift();
762
+ }
763
+ }
764
+ const Y = 3e4, X = 16;
765
+ function Z(o, e, t, s, n) {
766
+ const { channel: r, isTerminal: c } = G(n.reply), l = n.timeoutMs ?? Y, i = new q(n.highWaterMark ?? X), a = o.idFactory();
767
+ let u, d, h = !1;
768
+ const p = new Promise((E, v) => {
769
+ u = E, d = v;
770
+ });
771
+ p.catch(() => {
772
+ });
773
+ let f = null, m = null;
774
+ const y = (E, v = !1) => {
775
+ h || (h = !0, f !== null && clearTimeout(f), f = null, m?.(), m = null, v ? i.end() : i.close(), n.signal?.removeEventListener("abort", g), E());
776
+ };
777
+ function g() {
778
+ y(() => d(new C(String(n.signal?.reason ?? "signal aborted"))));
779
+ }
780
+ const $ = () => {
781
+ f !== null && clearTimeout(f), f = setTimeout(() => {
782
+ y(() => d(new Q(e, t, l)));
783
+ }, l), f.unref?.();
784
+ };
785
+ return m = o.registerEffect({
786
+ // A pattern effect on the reply channel: which types are terminal is known, which are
787
+ // progress is not, so the filter cannot be a key list.
788
+ when: { channel: r },
789
+ effect: async (E) => {
790
+ if (!h && J(E, a, n.correlationId)) {
791
+ if ($(), c(String(E.type))) {
792
+ y(() => u(E), !0);
793
+ return;
794
+ }
795
+ await i.put(E);
796
+ }
797
+ }
798
+ }), n.signal !== void 0 && (n.signal.aborted ? g() : n.signal.addEventListener("abort", g, { once: !0 })), $(), o.emit(e, t, s, {
799
+ id: a,
800
+ ...n.correlationId !== void 0 ? { meta: { correlationId: n.correlationId } } : {}
801
+ }), {
802
+ then: (E, v) => p.then(E, v),
803
+ catch: (E) => p.catch(E),
804
+ finally: (E) => p.finally(E),
805
+ get dropped() {
806
+ return i.droppedCount;
807
+ },
808
+ cancel: (E = "cancelled") => {
809
+ y(() => d(new C(E)));
810
+ },
811
+ [Symbol.asyncIterator]: () => (i.beginConsuming(), {
812
+ next: () => i.take(),
813
+ // Called by `for await` on `break`, `return` or a throw. Without it, abandoning the
814
+ // loop would leave the effect registered and the producer parked for good.
815
+ return: async () => (i.close(), { value: void 0, done: !0 })
816
+ })
817
+ };
818
+ }
819
+ function ee(o, e) {
820
+ if (!e) return o;
821
+ const s = (e[0] === "." ? e.slice(1) : e).split(".");
822
+ let n = o;
823
+ for (const r of s) {
824
+ if (n == null) return;
825
+ n = n[r];
826
+ }
827
+ return n;
828
+ }
829
+ function te(o) {
830
+ if (!o) return [];
831
+ const t = (o[0] === "." ? o.slice(1) : o).split("."), s = [];
832
+ for (let n = 0; n < t.length; n++)
833
+ s.push(t.slice(0, n + 1).join("."));
834
+ return s;
835
+ }
836
+ function k(o, e) {
837
+ return !o || "any" in o && o.any === !0 ? !0 : "keys" in o ? o.keys.some(
838
+ ([t, s]) => e.channel === t && e.type === s
839
+ ) : "channel" in o ? e.channel === o.channel : "channels" in o ? o.channels.includes(e.channel) : !1;
840
+ }
841
+ function ne(o) {
842
+ return typeof o == "function" ? o : o.middleware;
843
+ }
844
+ function se(o) {
845
+ if (typeof o != "function")
846
+ return o.when;
847
+ }
848
+ function D(o) {
849
+ if (o.when) {
850
+ const e = o.when;
851
+ if ("keys" in e)
852
+ return e.keys;
853
+ }
854
+ return [];
855
+ }
856
+ function re(o, e) {
648
857
  try {
649
858
  return structuredClone(e);
650
859
  } catch (t) {
@@ -653,11 +862,274 @@ function W(o, e) {
653
862
  );
654
863
  }
655
864
  }
656
- function P(o, e) {
657
- return process.env.NODE_ENV === "production" ? o : S(o, /* @__PURE__ */ new WeakSet(), e);
865
+ function R(o, e) {
866
+ return process.env.NODE_ENV === "production" ? o : M(o, /* @__PURE__ */ new WeakSet(), e);
658
867
  }
659
- const x = 100, z = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
660
- class $ {
868
+ const A = 100, ie = 64, z = 16, S = Object.freeze({ committed: !1, written: !1 }), oe = Object.freeze({ committed: !0, written: !1 }), ce = Object.freeze({ committed: !0, written: !0 }), _ = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
869
+ class x {
870
+ /**
871
+ * Store name (used by DevTools & diagnostics).
872
+ *
873
+ * @public
874
+ */
875
+ name;
876
+ /**
877
+ * Registered middleware pipeline (run **before** reducers).
878
+ * Stores either raw functions (legacy) or MiddlewareSpec objects.
879
+ * Return `false` from the middleware function to stop propagation.
880
+ *
881
+ * @internal
882
+ */
883
+ middleware;
884
+ /**
885
+ * Installed slice reducers keyed by slice name.
886
+ *
887
+ * @internal
888
+ */
889
+ reducers;
890
+ /**
891
+ * Current immutable snapshot of the store state.
892
+ * This reference changes whenever any slice changes (shallow immutability).
893
+ *
894
+ * @internal
895
+ */
896
+ state;
897
+ /**
898
+ * Bus for reducer wiring (emit by `(channel, type)`).
899
+ *
900
+ * @internal
901
+ */
902
+ reducerBus;
903
+ /**
904
+ * Bus for **granular** connector events (emit by **dotted path** inside a slice).
905
+ *
906
+ * @internal
907
+ */
908
+ connectorBus;
909
+ /**
910
+ * Coarse-grained listeners (called once per committed event, only if state changed).
911
+ *
912
+ * @internal
913
+ */
914
+ listeners = /* @__PURE__ */ new Set();
915
+ /**
916
+ * Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
917
+ * Used for effects with explicit `keys` targeting.
918
+ *
919
+ * @internal
920
+ */
921
+ effects = /* @__PURE__ */ new Map();
922
+ /**
923
+ * Pattern-based effects that need runtime matching.
924
+ * Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
925
+ * Stores tuples of [effect function, when matcher].
926
+ *
927
+ * @internal
928
+ */
929
+ patternEffects = /* @__PURE__ */ new Set();
930
+ /**
931
+ * Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
932
+ * Notified after reducers, before effects, for events that passed middleware.
933
+ *
934
+ * @internal
935
+ */
936
+ committedEventSubscribers = /* @__PURE__ */ new Map();
937
+ /**
938
+ * Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
939
+ * Notified when middleware rejects an event.
940
+ *
941
+ * @internal
942
+ */
943
+ uncommittedEventSubscribers = /* @__PURE__ */ new Map();
944
+ /**
945
+ * All-events subscribers keyed by `"channel::type"` for O(1) lookup.
946
+ * Notified for both committed and uncommitted events with phase parameter.
947
+ *
948
+ * @internal
949
+ */
950
+ /**
951
+ * Subscribers to events that actually changed state, notified after the commit.
952
+ *
953
+ * @remarks
954
+ * Separate from `committedEventSubscribers` rather than a filter over it, because the two
955
+ * answer different questions and one of them is load bearing: `committed` means "not vetoed"
956
+ * and fires for every event a store accepts, including every event in a store with no
957
+ * reducers. Narrowing it would have silently stopped toasts and analytics firing.
958
+ *
959
+ * @internal
960
+ */
961
+ writtenEventSubscribers = /* @__PURE__ */ new Map();
962
+ allEventSubscribers = /* @__PURE__ */ new Map();
963
+ /**
964
+ * Track reducerBus unsubs per slice for HMR/register/unregister.
965
+ *
966
+ * @internal
967
+ */
968
+ sliceUnsubs = /* @__PURE__ */ new Map();
969
+ /**
970
+ * Pattern-based reducers that need runtime matching.
971
+ * Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
972
+ * Maps slice name to the `when` matcher.
973
+ *
974
+ * @internal
975
+ */
976
+ patternReducers = /* @__PURE__ */ new Map();
977
+ /**
978
+ * Whether `__replayEvents()` is allowed.
979
+ * Set from `spec.devtools.allowReplay`.
980
+ *
981
+ * @internal
982
+ */
983
+ replayEnabled;
984
+ /**
985
+ * Produces the `id` for each emitted event. Defaults to `crypto.randomUUID()`; overridable
986
+ * via {@link StoreSpec.idFactory} for runtimes lacking it or for deterministic tests.
987
+ *
988
+ * @internal
989
+ */
990
+ idFactory;
991
+ /**
992
+ * Optional hook invoked when an effect throws/rejects. See
993
+ * {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
994
+ * failure — this is how callers observe effect errors.
995
+ */
996
+ onEffectError;
997
+ /**
998
+ * Optional hook invoked when a reducer throws. See {@link StoreSpec.onReducerError}. The
999
+ * failing slice is isolated rather than the event being rolled back, so this is the only
1000
+ * signal that a reducer misbehaved.
1001
+ */
1002
+ onReducerError;
1003
+ /**
1004
+ * `slice:channel:type` combinations already warned about for payload aliasing.
1005
+ *
1006
+ * @remarks
1007
+ * Development-only diagnostics have to stay quiet enough to be read. One warning names the
1008
+ * pattern; repeating it once per event would bury it.
1009
+ */
1010
+ warnedPayloadAliases = /* @__PURE__ */ new Set();
1011
+ /**
1012
+ * Pending events awaiting the **synchronous** reduce phase (middleware +
1013
+ * reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
1014
+ *
1015
+ * @internal
1016
+ */
1017
+ reduceQueue = [];
1018
+ /**
1019
+ * Re-entrancy guard for the synchronous reduce phase.
1020
+ *
1021
+ * @internal
1022
+ */
1023
+ isReducing = !1;
1024
+ /**
1025
+ * The event currently being reduced, or `null` outside the drain.
1026
+ *
1027
+ * @remarks
1028
+ * This is what makes causality exact rather than best-effort. The drain is synchronous — no
1029
+ * `await` can interleave — so any `emit` that arrives while it is set is, without ambiguity, a
1030
+ * consequence of this event. That catches the case a scoped `emit` closure cannot: a
1031
+ * middleware or subscriber that captured the store and calls `store.emit` directly instead of
1032
+ * using the injected one. Attribution should not depend on which reference a consumer reached
1033
+ * for.
1034
+ *
1035
+ * @internal
1036
+ */
1037
+ currentEvent = null;
1038
+ /**
1039
+ * Events processed by the drain currently in progress. Compared against
1040
+ * `maxTransitionsPerDrain`, which is off unless configured.
1041
+ *
1042
+ * @internal
1043
+ */
1044
+ transitionsThisDrain = 0;
1045
+ /**
1046
+ * Ceilings that stop a cascade from becoming a hung process. See {@link StoreSpec.maxReduceDepth}.
1047
+ *
1048
+ * @internal
1049
+ */
1050
+ maxReduceDepth;
1051
+ maxTransitionsPerDrain;
1052
+ onCascade;
1053
+ onRejected;
1054
+ /**
1055
+ * Registered instrumentation observers (DevTools seam). See {@link instrument}.
1056
+ *
1057
+ * @internal
1058
+ */
1059
+ instrumentObservers = /* @__PURE__ */ new Set();
1060
+ /**
1061
+ * Scratch array collecting slice-prefixed changed leaf paths during an
1062
+ * instrumented reduce. Set by {@link drainReduce} while observers are active;
1063
+ * appended to by {@link commitStaged}. `null` when not instrumenting.
1064
+ *
1065
+ * @internal
1066
+ */
1067
+ changedPathSink = null;
1068
+ /**
1069
+ * Where keyed reducers put their pending writes during a reduce, and the refusal one of them
1070
+ * returned.
1071
+ *
1072
+ * @remarks
1073
+ * Keyed reducers are invoked through `reducerBus`, which delivers to handlers and has no way
1074
+ * to hand a value back — the same reason `changedPathSink` exists. `null` outside a reduce.
1075
+ *
1076
+ * @internal
1077
+ */
1078
+ stagingSink = null;
1079
+ stagedRejection = null;
1080
+ stagedRejectedBy = "";
1081
+ /**
1082
+ * Count of effect tasks currently in flight; surfaced as queue depth by
1083
+ * {@link __devtoolsIntrospect}.
1084
+ *
1085
+ * @internal
1086
+ */
1087
+ inFlightEffects = 0;
1088
+ /**
1089
+ * Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
1090
+ *
1091
+ * **Deduplication Behavior:**
1092
+ * - Events are fingerprinted using `channel::type::JSON(payload)`
1093
+ * - If an identical fingerprint is seen within the dedup window, it's skipped
1094
+ * - The window is 50ms in development, 100ms in production
1095
+ *
1096
+ * **Limitations:**
1097
+ * - Non-serializable payloads (functions, symbols, circular refs) get unique
1098
+ * fingerprints and won't be deduplicated
1099
+ * - Legitimate rapid-fire identical events may be incorrectly deduplicated
1100
+ * - The cache is bounded to 1000 entries with lazy pruning
1101
+ *
1102
+ * @internal
1103
+ */
1104
+ processedEvents = /* @__PURE__ */ new Map();
1105
+ /**
1106
+ * Lifetime count of events suppressed by the deduplication cache.
1107
+ * Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
1108
+ * surface it in the STORE_METRICS response without further core changes.
1109
+ *
1110
+ * @internal
1111
+ */
1112
+ dedupCount = 0;
1113
+ /**
1114
+ * Store-owned metadata for registered effects, keyed by the effect function.
1115
+ * Kept **off** the caller's function object: mutating a user-owned function
1116
+ * (the old `fn.__quoMeta`) bled metadata across stores that share a handler
1117
+ * and left it attached after unregister. Cleared on {@link dispose}.
1118
+ *
1119
+ * @internal
1120
+ */
1121
+ effectMeta = /* @__PURE__ */ new WeakMap();
1122
+ /**
1123
+ * Configuration for event deduplication.
1124
+ * @internal
1125
+ */
1126
+ dedupConfig;
1127
+ /**
1128
+ * Timer for periodic cleanup of processed events.
1129
+ *
1130
+ * @internal
1131
+ */
1132
+ eventCleanupTimer = null;
661
1133
  /**
662
1134
  * Creates a store from a {@link StoreSpec}.
663
1135
  *
@@ -666,223 +1138,15 @@ class $ {
666
1138
  * @public
667
1139
  */
668
1140
  constructor(e) {
669
- /**
670
- * Store name (used by DevTools & diagnostics).
671
- *
672
- * @public
673
- */
674
- p(this, "name");
675
- /**
676
- * Registered middleware pipeline (run **before** reducers).
677
- * Stores either raw functions (legacy) or MiddlewareSpec objects.
678
- * Return `false` from the middleware function to stop propagation.
679
- *
680
- * @internal
681
- */
682
- p(this, "middleware");
683
- /**
684
- * Installed slice reducers keyed by slice name.
685
- *
686
- * @internal
687
- */
688
- p(this, "reducers");
689
- /**
690
- * Current immutable snapshot of the store state.
691
- * This reference changes whenever any slice changes (shallow immutability).
692
- *
693
- * @internal
694
- */
695
- p(this, "state");
696
- /**
697
- * Bus for reducer wiring (emit by `(channel, type)`).
698
- *
699
- * @internal
700
- */
701
- p(this, "reducerBus");
702
- /**
703
- * Bus for **granular** connector events (emit by **dotted path** inside a slice).
704
- *
705
- * @internal
706
- */
707
- p(this, "connectorBus");
708
- /**
709
- * Coarse-grained listeners (called once per committed event, only if state changed).
710
- *
711
- * @internal
712
- */
713
- p(this, "listeners", /* @__PURE__ */ new Set());
714
- /**
715
- * Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
716
- * Used for effects with explicit `keys` targeting.
717
- *
718
- * @internal
719
- */
720
- p(this, "effects", /* @__PURE__ */ new Map());
721
- /**
722
- * Pattern-based effects that need runtime matching.
723
- * Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
724
- * Stores tuples of [effect function, when matcher].
725
- *
726
- * @internal
727
- */
728
- p(this, "patternEffects", /* @__PURE__ */ new Set());
729
- /**
730
- * Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
731
- * Notified after reducers, before effects, for events that passed middleware.
732
- *
733
- * @internal
734
- */
735
- p(this, "committedEventSubscribers", /* @__PURE__ */ new Map());
736
- /**
737
- * Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
738
- * Notified when middleware rejects an event.
739
- *
740
- * @internal
741
- */
742
- p(this, "uncommittedEventSubscribers", /* @__PURE__ */ new Map());
743
- /**
744
- * All-events subscribers keyed by `"channel::type"` for O(1) lookup.
745
- * Notified for both committed and uncommitted events with phase parameter.
746
- *
747
- * @internal
748
- */
749
- p(this, "allEventSubscribers", /* @__PURE__ */ new Map());
750
- /**
751
- * Track reducerBus unsubs per slice for HMR/register/unregister.
752
- *
753
- * @internal
754
- */
755
- p(this, "sliceUnsubs", /* @__PURE__ */ new Map());
756
- /**
757
- * Pattern-based reducers that need runtime matching.
758
- * Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
759
- * Maps slice name to the `when` matcher.
760
- *
761
- * @internal
762
- */
763
- p(this, "patternReducers", /* @__PURE__ */ new Map());
764
- /**
765
- * Whether `__replayEvents()` is allowed.
766
- * Set from `spec.devtools.allowReplay`.
767
- *
768
- * @internal
769
- */
770
- p(this, "replayEnabled");
771
- /**
772
- * Produces the `id` for each emitted event. Defaults to `crypto.randomUUID()`; overridable
773
- * via {@link StoreSpec.idFactory} for runtimes lacking it or for deterministic tests.
774
- *
775
- * @internal
776
- */
777
- p(this, "idFactory");
778
- /**
779
- * Optional hook invoked when an effect throws/rejects. See
780
- * {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
781
- * failure — this is how callers observe effect errors.
782
- */
783
- p(this, "onEffectError");
784
- /**
785
- * Optional hook invoked when a reducer throws. See {@link StoreSpec.onReducerError}. The
786
- * failing slice is isolated rather than the event being rolled back, so this is the only
787
- * signal that a reducer misbehaved.
788
- */
789
- p(this, "onReducerError");
790
- /**
791
- * `slice:channel:type` combinations already warned about for payload aliasing.
792
- *
793
- * @remarks
794
- * Development-only diagnostics have to stay quiet enough to be read. One warning names the
795
- * pattern; repeating it once per event would bury it.
796
- */
797
- p(this, "warnedPayloadAliases", /* @__PURE__ */ new Set());
798
- /**
799
- * Pending events awaiting the **synchronous** reduce phase (middleware +
800
- * reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
801
- *
802
- * @internal
803
- */
804
- p(this, "reduceQueue", []);
805
- /**
806
- * Re-entrancy guard for the synchronous reduce phase.
807
- *
808
- * @internal
809
- */
810
- p(this, "isReducing", !1);
811
- /**
812
- * Registered instrumentation observers (DevTools seam). See {@link instrument}.
813
- *
814
- * @internal
815
- */
816
- p(this, "instrumentObservers", /* @__PURE__ */ new Set());
817
- /**
818
- * Scratch array collecting slice-prefixed changed leaf paths during an
819
- * instrumented reduce. Set by {@link drainReduce} while observers are active;
820
- * appended to by {@link forwardEvent}. `null` when not instrumenting.
821
- *
822
- * @internal
823
- */
824
- p(this, "changedPathSink", null);
825
- /**
826
- * Count of effect tasks currently in flight; surfaced as queue depth by
827
- * {@link __devtoolsIntrospect}.
828
- *
829
- * @internal
830
- */
831
- p(this, "inFlightEffects", 0);
832
- /**
833
- * Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
834
- *
835
- * **Deduplication Behavior:**
836
- * - Events are fingerprinted using `channel::type::JSON(payload)`
837
- * - If an identical fingerprint is seen within the dedup window, it's skipped
838
- * - The window is 50ms in development, 100ms in production
839
- *
840
- * **Limitations:**
841
- * - Non-serializable payloads (functions, symbols, circular refs) get unique
842
- * fingerprints and won't be deduplicated
843
- * - Legitimate rapid-fire identical events may be incorrectly deduplicated
844
- * - The cache is bounded to 1000 entries with lazy pruning
845
- *
846
- * @internal
847
- */
848
- p(this, "processedEvents", /* @__PURE__ */ new Map());
849
- /**
850
- * Lifetime count of events suppressed by the deduplication cache.
851
- * Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
852
- * surface it in the STORE_METRICS response without further core changes.
853
- *
854
- * @internal
855
- */
856
- p(this, "dedupCount", 0);
857
- /**
858
- * Store-owned metadata for registered effects, keyed by the effect function.
859
- * Kept **off** the caller's function object: mutating a user-owned function
860
- * (the old `fn.__quoMeta`) bled metadata across stores that share a handler
861
- * and left it attached after unregister. Cleared on {@link dispose}.
862
- *
863
- * @internal
864
- */
865
- p(this, "effectMeta", /* @__PURE__ */ new WeakMap());
866
- /**
867
- * Configuration for event deduplication.
868
- * @internal
869
- */
870
- p(this, "dedupConfig");
871
- /**
872
- * Timer for periodic cleanup of processed events.
873
- *
874
- * @internal
875
- */
876
- 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 = {
1141
+ if (this.name = e.name ?? "yoltra Store", this.reducerBus = new W(), this.connectorBus = new U(), 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 ?? ie, this.maxTransitionsPerDrain = e.maxTransitionsPerDrain ?? 1 / 0, this.onCascade = e.onCascade, this.onRejected = e.onRejected, this.dedupConfig = {
878
1142
  windowMs: e.dedupWindowMs ?? 0,
879
1143
  maxCacheSize: 1e3
880
- }, Object.entries(e.reducer).forEach(([t, n]) => {
881
- this.mountSlice(t, n, { preserveState: !1 });
1144
+ }, Object.entries(e.reducer).forEach(([t, s]) => {
1145
+ this.mountSlice(t, s, { preserveState: !1 });
882
1146
  }), e.effects?.length)
883
1147
  for (const t of e.effects)
884
1148
  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);
1149
+ 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
1150
  }
887
1151
  /**
888
1152
  * Cleanup resources (timers, etc.) when disposing the store.
@@ -898,7 +1162,7 @@ class $ {
898
1162
  * @public
899
1163
  */
900
1164
  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;
1165
+ 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
1166
  }
903
1167
  /**
904
1168
  * Generates a fingerprint for an event for deduplication purposes.
@@ -911,17 +1175,17 @@ class $ {
911
1175
  *
912
1176
  * @internal
913
1177
  */
914
- fingerprint(e, t, n) {
915
- const s = `${e}::${t}`;
1178
+ fingerprint(e, t, s) {
1179
+ const n = `${e}::${t}`;
916
1180
  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}`;
1181
+ if (s == null)
1182
+ return `${n}::null`;
1183
+ if (typeof s != "object")
1184
+ return `${n}::${String(s)}`;
1185
+ const r = JSON.stringify(s);
1186
+ return `${n}::${r}`;
923
1187
  } catch {
924
- return `${s}::${Date.now()}::${Math.random()}`;
1188
+ return `${n}::${Date.now()}::${Math.random()}`;
925
1189
  }
926
1190
  }
927
1191
  /**
@@ -934,8 +1198,8 @@ class $ {
934
1198
  * @internal
935
1199
  */
936
1200
  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);
1201
+ const s = Date.now(), n = this.processedEvents.get(e);
1202
+ 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
1203
  }
940
1204
  /**
941
1205
  * Starts the periodic prune interval if it isn't already running. Called when
@@ -957,55 +1221,31 @@ class $ {
957
1221
  * @internal
958
1222
  */
959
1223
  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);
1224
+ const t = Math.max(this.dedupConfig.windowMs, A), s = e - t * 2;
1225
+ for (const [n, r] of this.processedEvents)
1226
+ r < s && this.processedEvents.delete(n);
963
1227
  this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
964
1228
  }
965
1229
  /**
966
- * Checks if an event matches a `When` matcher.
967
- *
968
- * @param when - The When matcher (or undefined for "all events").
969
- * @param event - The event to check.
970
- * @returns `true` if the event matches, `false` otherwise.
1230
+ * Reports a breached ceiling and refuses the emit.
971
1231
  *
972
1232
  * @remarks
973
- * - `undefined` or missing `when` matches ALL events.
974
- * - `{ any: true }` matches ALL events.
975
- * - `{ keys: [...] }` matches if event's `[channel, type]` is in the array.
976
- * - `{ channel: 'x' }` matches if event's channel equals 'x'.
977
- * - `{ channels: ['x', 'y'] }` matches if event's channel is in the array.
1233
+ * Console *and* hook, matching how reducer and effect errors are reported: a cascade is a
1234
+ * wiring bug, and the console line is what a developer who has not registered a hook will
1235
+ * actually see. Without one, refusing the emit would look exactly like the event never having
1236
+ * been emitted at all — which is the invisibility this whole guard exists to end.
978
1237
  *
979
1238
  * @internal
980
1239
  */
981
- matchesWhen(e, t) {
982
- return !e || "any" in e && e.any === !0 ? !0 : "keys" in e ? e.keys.some(
983
- ([n, s]) => t.channel === n && t.type === s
984
- ) : "channel" in e ? t.channel === e.channel : "channels" in e ? e.channels.includes(t.channel) : !1;
985
- }
986
- /**
987
- * Extracts the middleware function from a MiddlewareInput.
988
- * Handles both raw functions (legacy) and MiddlewareSpec objects.
989
- *
990
- * @param input - MiddlewareInput (function or spec).
991
- * @returns The middleware function.
992
- *
993
- * @internal
994
- */
995
- getMiddlewareFunction(e) {
996
- return typeof e == "function" ? e : e.middleware;
997
- }
998
- /**
999
- * Gets the `when` matcher from a MiddlewareInput.
1000
- *
1001
- * @param input - MiddlewareInput (function or spec).
1002
- * @returns The `when` matcher, or `undefined` for raw functions (match all).
1003
- *
1004
- * @internal
1005
- */
1006
- getMiddlewareWhen(e) {
1007
- if (typeof e != "function")
1008
- return e.when;
1240
+ reportCascade(e, t, s, n, r) {
1241
+ console.error(
1242
+ `[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).` : "")
1243
+ );
1244
+ try {
1245
+ this.onCascade?.({ limit: e, limitValue: t, event: s, depth: n, chain: r });
1246
+ } catch (c) {
1247
+ console.error("onCascade handler error:", c);
1248
+ }
1009
1249
  }
1010
1250
  /**
1011
1251
  * Invokes all registered **effects** for a given event.
@@ -1016,22 +1256,40 @@ class $ {
1016
1256
  * @internal
1017
1257
  */
1018
1258
  async notifyEffects(e) {
1019
- const t = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(t);
1259
+ const t = this.scopedEmit(e), s = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(s);
1020
1260
  if (n && n.size > 0)
1021
- for (const s of [...n])
1261
+ for (const r of [...n])
1022
1262
  try {
1023
- await s(e, this.getState, this.emit);
1024
- } catch (i) {
1025
- console.error("Effect error:", i), this.onEffectError?.(i, e);
1263
+ await r(e, this.getState, t);
1264
+ } catch (c) {
1265
+ console.error("Effect error:", c), this.onEffectError?.(c, e);
1026
1266
  }
1027
- for (const { effect: s, when: i } of this.patternEffects)
1028
- if (this.matchesWhen(i, e))
1267
+ for (const { effect: r, when: c } of this.patternEffects)
1268
+ if (k(c, e))
1029
1269
  try {
1030
- await s(e, this.getState, this.emit);
1031
- } catch (d) {
1032
- console.error("Effect error:", d), this.onEffectError?.(d, e);
1270
+ await r(e, this.getState, t);
1271
+ } catch (l) {
1272
+ console.error("Effect error:", l), this.onEffectError?.(l, e);
1033
1273
  }
1034
1274
  }
1275
+ /**
1276
+ * An `emit` that attributes whatever it sends to `cause`.
1277
+ *
1278
+ * @remarks
1279
+ * Built per event rather than per effect: every effect reacting to one event shares a cause,
1280
+ * and one closure is cheaper than one per handler on a path that runs for every committed
1281
+ * event.
1282
+ *
1283
+ * @internal
1284
+ */
1285
+ scopedEmit(e) {
1286
+ const t = {
1287
+ id: e.id,
1288
+ depth: e.depth ?? 0,
1289
+ chain: [...this.currentEvent?.chain ?? [], e.id].slice(-z)
1290
+ };
1291
+ return ((s, n, r, c) => this.emitCaused(t, s, n, r, c));
1292
+ }
1035
1293
  /**
1036
1294
  * Notifies event subscribers for a specific phase.
1037
1295
  *
@@ -1043,12 +1301,13 @@ class $ {
1043
1301
  * @internal
1044
1302
  */
1045
1303
  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);
1304
+ const s = `${String(e.channel)}::${String(e.type)}`, r = (t === "committed" ? this.committedEventSubscribers : t === "written" ? this.writtenEventSubscribers : this.uncommittedEventSubscribers).get(s);
1305
+ if (r?.size)
1306
+ for (const l of [...r]) this.invokeEventSubscriber(l, e, t);
1307
+ if (t === "written") return;
1308
+ const c = this.allEventSubscribers.get(s);
1309
+ if (c?.size)
1310
+ for (const l of [...c]) this.invokeEventSubscriber(l, e, t);
1052
1311
  }
1053
1312
  /**
1054
1313
  * Invokes a single event-subscription handler **fire-and-forget**: synchronous
@@ -1057,12 +1316,12 @@ class $ {
1057
1316
  *
1058
1317
  * @internal
1059
1318
  */
1060
- invokeEventSubscriber(e, t, n) {
1319
+ invokeEventSubscriber(e, t, s) {
1061
1320
  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);
1321
+ const n = e(t, this.getState, this.emit, s);
1322
+ n && typeof n.then == "function" && n.catch((r) => console.error("Event subscription error:", r));
1323
+ } catch (n) {
1324
+ console.error("Event subscription error:", n);
1066
1325
  }
1067
1326
  }
1068
1327
  /**
@@ -1100,53 +1359,116 @@ class $ {
1100
1359
  * pattern reducer's throw aborted the commit and notified nobody, not even the uncommitted
1101
1360
  * subscribers a veto would have reached.
1102
1361
  *
1103
- * The semantics are now the same either way: **the failing slice is isolated.** Its state is
1362
+ * The semantics are the same either way: **the failing slice is isolated.** Its state is
1104
1363
  * 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.
1364
+ * changed.
1365
+ *
1366
+ * That is deliberately *not* what a {@link Rejected} refusal does, which discards the whole
1367
+ * event. A crash and a refusal are different acts: a reducer that throws has a bug and should
1368
+ * not be able to veto its neighbours' work, while a reducer that refuses has made a decision
1369
+ * and must be able to.
1370
+ *
1371
+ * This once argued that rolling back was untenable, because subscribers were notified as each
1372
+ * slice committed and a later revert would have told them about a value that no longer
1373
+ * existed. Staging removed that obstacle — nothing is notified until every slice is written —
1374
+ * which is what made refusal possible at all.
1109
1375
  *
1110
1376
  * @internal
1111
1377
  */
1112
- forwardEventGuarded(e, t) {
1378
+ stageSliceGuarded(e, t, s) {
1113
1379
  try {
1114
- return this.forwardEvent(e, t);
1380
+ return this.stageSlice(e, t, s);
1115
1381
  } catch (n) {
1116
- return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), !1;
1382
+ return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), null;
1117
1383
  }
1118
1384
  }
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,
1385
+ /**
1386
+ * Runs one slice's reducer and records what it *would* write. Writes nothing.
1387
+ *
1388
+ * @returns The reducer's {@link Rejection} if it refused, otherwise `null`.
1389
+ *
1390
+ * @remarks
1391
+ * The staging half of the write path. Nothing here touches `this.state` or notifies anybody,
1392
+ * which is what lets the event be refused after every reducer has had its say — a decision
1393
+ * that has to see the whole diff cannot be made one slice at a time.
1394
+ *
1395
+ * Freezing happens here rather than at commit because it is where the new value is built, and
1396
+ * the freeze is a no-op on anything already frozen; a staged slice that never commits is
1397
+ * discarded frozen, which costs nothing and keeps the committed path free of a second walk.
1398
+ *
1399
+ * @internal
1400
+ */
1401
+ stageSlice(e, t, s) {
1402
+ const n = this.state[e], r = this.reducers[e].reduce(n, t);
1403
+ if (V(r)) return r;
1404
+ if (n === r) return null;
1405
+ const c = I(n, r);
1406
+ if (c.length === 0) return null;
1407
+ const l = t.payload, i = process.env.NODE_ENV !== "production" && l !== null && typeof l == "object" ? {
1408
+ watch: l,
1126
1409
  onFound: () => {
1127
- const c = `${e}:${t.channel}:${t.type}`;
1128
- this.warnedPayloadAliases.has(c) || (this.warnedPayloadAliases.add(c), console.warn(
1410
+ const a = `${e}:${t.channel}:${t.type}`;
1411
+ this.warnedPayloadAliases.has(a) || (this.warnedPayloadAliases.add(a), console.warn(
1129
1412
  `[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
1413
  ));
1131
1414
  }
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;
1415
+ } : void 0;
1416
+ return s.push({
1417
+ name: e,
1418
+ prev: n,
1419
+ frozen: R(r, i),
1420
+ leafPaths: c
1421
+ }), null;
1422
+ }
1423
+ /**
1424
+ * Writes every staged slice, then tells the world — in that order.
1425
+ *
1426
+ * @remarks
1427
+ * The commit half. Assigning all slices under a single new root before any notification goes
1428
+ * out is what closes the window this used to leave open: notifications fired per slice as each
1429
+ * committed, so a subscriber to slice A that read `getState()` could observe slice B of the
1430
+ * *same event* not yet applied. In React that window is real, because the atomic hooks use a
1431
+ * change as a bare signal and then re-read the whole store.
1432
+ *
1433
+ * It is also what makes refusal possible at all. The previous code documented rollback as
1434
+ * untenable precisely because "an event that reverted afterwards would have already told
1435
+ * components about a value that no longer exists" — true when notification and commit were the
1436
+ * same step, and no longer true now that they are not.
1437
+ *
1438
+ * @returns `true` if anything was written.
1439
+ *
1440
+ * @internal
1441
+ */
1442
+ commitStaged(e, t) {
1443
+ if (e.length === 0) return !1;
1444
+ const s = { ...this.state };
1445
+ for (const n of e) s[n.name] = n.frozen;
1446
+ if (this.state = s, this.changedPathSink)
1447
+ for (const n of e)
1448
+ for (const r of n.leafPaths)
1449
+ this.changedPathSink.push(r ? `${n.name}.${r}` : n.name);
1450
+ for (const n of e) {
1451
+ const r = /* @__PURE__ */ new Set();
1452
+ for (const c of n.leafPaths) {
1453
+ if (c === "") {
1454
+ r.add("");
1455
+ continue;
1456
+ }
1457
+ for (const l of x.buildAncestorPaths(c)) r.add(l);
1141
1458
  }
1142
- for (const u of $.buildAncestorPaths(c)) f.add(u);
1459
+ for (const c of r)
1460
+ this.connectorBus.emitWith(n.name, c, () => ({
1461
+ oldValue: this.getAtPath(n.prev, c),
1462
+ newValue: this.getAtPath(n.frozen, c),
1463
+ path: c,
1464
+ // Provenance, built inside the same lazy factory as the values: a subscriber that
1465
+ // needs to know why a value moved no longer has to mirror the cause into state and
1466
+ // keep it there twice.
1467
+ eventId: t.id,
1468
+ channel: t.channel,
1469
+ type: t.type
1470
+ }));
1143
1471
  }
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
1472
  return !0;
1151
1473
  }
1152
1474
  /**
@@ -1160,65 +1482,65 @@ class $ {
1160
1482
  * @public
1161
1483
  */
1162
1484
  __devtoolsIntrospect() {
1163
- const e = Object.keys(this.reducers).map((a) => {
1164
- const r = this.patternReducers.get(a);
1165
- return { name: a, when: r };
1485
+ const e = Object.keys(this.reducers).map((l) => {
1486
+ const i = this.patternReducers.get(l);
1487
+ return { name: l, when: i };
1166
1488
  }), 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 });
1489
+ for (const [l, i] of this.effects) {
1490
+ if (i.size === 0) continue;
1491
+ const [a, u] = l.split("::");
1492
+ for (const d of i) {
1493
+ const h = this.effectMeta.get(d);
1494
+ t.push({ channel: a, type: u, name: h?.name, description: h?.description });
1173
1495
  }
1174
1496
  }
1175
- for (const a of this.patternEffects) {
1176
- const r = this.effectMeta.get(a.effect);
1497
+ for (const l of this.patternEffects) {
1498
+ const i = this.effectMeta.get(l.effect);
1177
1499
  t.push({
1178
1500
  channel: "*",
1179
1501
  type: "*",
1180
- name: r?.name,
1181
- description: r?.description
1502
+ name: i?.name,
1503
+ description: i?.description
1182
1504
  });
1183
1505
  }
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
1506
  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" });
1507
+ for (const l of this.middleware)
1508
+ typeof l == "function" ? s.push({ name: l.name || void 0 }) : s.push({
1509
+ name: l.meta?.name,
1510
+ description: l.meta?.description,
1511
+ when: l.when
1512
+ });
1513
+ const n = [];
1514
+ for (const l of this.connectorBus.__introspect())
1515
+ for (let i = 0; i < l.count; i++)
1516
+ n.push({ reducer: l.channel, property: l.type });
1517
+ const r = [];
1518
+ for (const [l, i] of this.committedEventSubscribers) {
1519
+ if (i.size === 0) continue;
1520
+ const [a, u] = l.split("::");
1521
+ for (let d = 0; d < i.size; d++)
1522
+ r.push({ channel: a, type: u, phase: "committed" });
1201
1523
  }
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" });
1524
+ for (const [l, i] of this.uncommittedEventSubscribers) {
1525
+ if (i.size === 0) continue;
1526
+ const [a, u] = l.split("::");
1527
+ for (let d = 0; d < i.size; d++)
1528
+ r.push({ channel: a, type: u, phase: "uncommitted" });
1207
1529
  }
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" });
1530
+ for (const [l, i] of this.allEventSubscribers) {
1531
+ if (i.size === 0) continue;
1532
+ const [a, u] = l.split("::");
1533
+ for (let d = 0; d < i.size; d++)
1534
+ r.push({ channel: a, type: u, phase: "all" });
1213
1535
  }
1214
- const d = this.listeners.size;
1536
+ const c = this.listeners.size;
1215
1537
  return {
1216
1538
  reducers: e,
1217
1539
  effects: t,
1218
- middleware: n,
1219
- atomic: s,
1220
- event: i,
1221
- coarse: d,
1540
+ middleware: s,
1541
+ atomic: n,
1542
+ event: r,
1543
+ coarse: c,
1222
1544
  dedupHits: this.dedupCount,
1223
1545
  queueDepth: this.reduceQueue.length + this.inFlightEffects
1224
1546
  };
@@ -1228,7 +1550,7 @@ class $ {
1228
1550
  * fine-grained path changes for each slice.
1229
1551
  *
1230
1552
  * **State Immutability**: If any slices change, a new state object is created via
1231
- * shallow spread. This ensures consistent immutability with {@link forwardEvent}.
1553
+ * shallow spread. This ensures consistent immutability with {@link commitStaged}.
1232
1554
  *
1233
1555
  * **Missing slices**: the snapshot should contain every slice. A slice absent
1234
1556
  * from `nextPlain` is **retained at its current value** (not blanked to
@@ -1243,36 +1565,36 @@ class $ {
1243
1565
  throw new Error(
1244
1566
  "[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1245
1567
  );
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) {
1568
+ const t = this.state, s = e, n = { ...this.state };
1569
+ let r = !1;
1570
+ Object.keys(this.reducers).forEach((c) => {
1571
+ const l = t?.[c], i = s?.[c];
1572
+ if (i === void 0) {
1251
1573
  process.env.NODE_ENV !== "production" && console.warn(
1252
1574
  `[yoltra] External state is missing slice "${String(
1253
- d
1575
+ c
1254
1576
  )}"; retaining its current value. Time-travel snapshots should contain all slices.`
1255
1577
  );
1256
1578
  return;
1257
1579
  }
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;
1263
- const u = /* @__PURE__ */ new Set();
1264
- for (const l of c) {
1265
- if (l === "") {
1266
- u.add("");
1580
+ if (l === i) return;
1581
+ const a = R(i);
1582
+ n[c] = a, r = !0;
1583
+ const u = I(l, i);
1584
+ if (u.length === 0) return;
1585
+ const d = /* @__PURE__ */ new Set();
1586
+ for (const h of u) {
1587
+ if (h === "") {
1588
+ d.add("");
1267
1589
  continue;
1268
1590
  }
1269
- for (const y of $.buildAncestorPaths(l)) u.add(y);
1591
+ for (const p of x.buildAncestorPaths(h)) d.add(p);
1270
1592
  }
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 });
1593
+ for (const h of d) {
1594
+ const p = this.getAtPath(l, h), f = this.getAtPath(a, h);
1595
+ this.connectorBus.emit(c, h, { oldValue: p, newValue: f, path: h });
1274
1596
  }
1275
- }), i && (this.state = s), i && this.listeners.forEach((d) => d());
1597
+ }), r && (this.state = n), r && this.listeners.forEach((c) => c());
1276
1598
  }
1277
1599
  /**
1278
1600
  * Replays a sequence of events from a snapshot through reducers and event
@@ -1292,13 +1614,24 @@ class $ {
1292
1614
  "[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1293
1615
  );
1294
1616
  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());
1617
+ for (const s of t) {
1618
+ const n = s, r = [];
1619
+ this.stagingSink = r;
1620
+ let c = null;
1621
+ try {
1622
+ this.reducerBus.emit(n.channel, n.type, n.payload, n), c = this.stagedRejection;
1623
+ for (const [i, a] of this.patternReducers) {
1624
+ if (c !== null) break;
1625
+ if (k(a, n)) {
1626
+ const u = this.stageSliceGuarded(i, n, r);
1627
+ u !== null && (c = u);
1628
+ }
1629
+ }
1630
+ } finally {
1631
+ this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1632
+ }
1633
+ const l = c === null && this.commitStaged(r, n);
1634
+ this.notifyEventSubscribers(n, "committed"), l && (this.notifyEventSubscribers(n, "written"), this.listeners.forEach((i) => i()));
1302
1635
  }
1303
1636
  }
1304
1637
  /**
@@ -1310,13 +1643,13 @@ class $ {
1310
1643
  * phase* (step 5) runs afterwards, asynchronously.
1311
1644
  * 1. **Deduplication** (opt-in) - Skip when content-dedup is enabled (`dedupWindowMs > 0`) or a matching `dedupKey` recurs; off by default
1312
1645
  * 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)
1646
+ * 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
1647
+ * 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
1648
  * 5. **Effects** (async) - side-effects keyed by `(channel, type)`; the returned promise resolves once they complete
1316
1649
  *
1317
1650
  * **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.
1651
+ * if any slice changed. Works because the commit builds a new state reference via
1652
+ * shallow spread when any slice changes.
1320
1653
  *
1321
1654
  * @typeParam C - Channel key in `EM`.
1322
1655
  * @typeParam T - Type key within channel `C`.
@@ -1344,26 +1677,60 @@ class $ {
1344
1677
  *
1345
1678
  * @public
1346
1679
  */
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;
1680
+ async emit(e, t, s, n) {
1681
+ return this.emitCaused(null, e, t, s, n);
1682
+ }
1683
+ /**
1684
+ * The real emit, with an explicitly supplied cause.
1685
+ *
1686
+ * @remarks
1687
+ * Exists so the parent can be passed without a pseudo-private field on the public
1688
+ * {@link EmitOptions}. Two callers supply one: the public {@link emit} passes `null` and lets
1689
+ * `currentEvent` speak for the synchronous case, and the scoped `emit` handed to effects passes
1690
+ * the event that triggered them — effects resume after the drain has ended, so nothing else
1691
+ * could still know what caused them.
1692
+ *
1693
+ * @internal
1694
+ */
1695
+ async emitCaused(e, t, s, n, r) {
1696
+ const c = r?.dedupKey, l = this.dedupConfig.windowMs;
1697
+ if (r?.skipDedup !== !0 && (l > 0 || c !== void 0)) {
1698
+ const p = c !== void 0 && l <= 0 ? A : l, f = c !== void 0 ? `${t}::${s}::#${c}` : this.fingerprint(t, s, n);
1699
+ if (this.shouldDedupe(f, p))
1700
+ return S;
1353
1701
  }
1354
- const a = s?.id ?? this.idFactory();
1355
- let r;
1356
- const f = new Promise((c) => {
1357
- r = c;
1702
+ const i = r?.id ?? this.idFactory(), a = this.currentEvent ?? e, u = a === null ? 0 : a.depth + 1;
1703
+ if (a !== null && u > this.maxReduceDepth)
1704
+ return this.reportCascade(
1705
+ "maxReduceDepth",
1706
+ this.maxReduceDepth,
1707
+ {
1708
+ channel: t,
1709
+ type: s,
1710
+ payload: n,
1711
+ id: i,
1712
+ ...r?.meta !== void 0 ? { meta: r.meta } : {},
1713
+ parentId: a.id,
1714
+ depth: u
1715
+ },
1716
+ u,
1717
+ a.chain
1718
+ ), S;
1719
+ let d;
1720
+ const h = new Promise((p) => {
1721
+ d = p;
1358
1722
  });
1359
1723
  return this.reduceQueue.push({
1360
- channel: e,
1361
- type: t,
1724
+ channel: t,
1725
+ type: s,
1362
1726
  payload: n,
1363
- id: a,
1364
- meta: s?.meta,
1365
- resolve: r
1366
- }), this.drainReduce(), f;
1727
+ id: i,
1728
+ meta: r?.meta,
1729
+ resolve: d,
1730
+ // Only carried for caused events, so a root event's object stays byte-identical to one
1731
+ // built before causality existed — the same rule `meta` follows.
1732
+ ...a !== null ? { parentId: a.id, depth: u, chain: a.chain } : {}
1733
+ }), this.drainReduce(), h;
1367
1734
  }
1368
1735
  /**
1369
1736
  * Drains the reduce queue **synchronously**. For each event it runs middleware,
@@ -1377,33 +1744,50 @@ class $ {
1377
1744
  */
1378
1745
  drainReduce() {
1379
1746
  if (!this.isReducing) {
1380
- this.isReducing = !0;
1747
+ this.isReducing = !0, this.transitionsThisDrain = 0;
1381
1748
  try {
1382
1749
  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,
1750
+ const e = this.reduceQueue.shift(), { channel: t, type: s, payload: n, id: r, meta: c, resolve: l, parentId: i, depth: a, chain: u } = e, d = {
1751
+ channel: t,
1752
+ type: s,
1386
1753
  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;
1754
+ id: r,
1755
+ ...c !== void 0 ? { meta: c } : {},
1756
+ ...i !== void 0 ? { parentId: i, depth: a } : {}
1757
+ };
1758
+ if (i !== void 0 && ++this.transitionsThisDrain > this.maxTransitionsPerDrain) {
1759
+ this.reportCascade(
1760
+ "maxTransitionsPerDrain",
1761
+ this.maxTransitionsPerDrain,
1762
+ d,
1763
+ a,
1764
+ u
1765
+ ), l(S);
1766
+ continue;
1767
+ }
1768
+ this.currentEvent = {
1769
+ id: r,
1770
+ depth: a ?? 0,
1771
+ chain: [...u ?? [], r].slice(-z)
1772
+ };
1773
+ const h = this.instrumentObservers.size > 0, p = h ? this.state : void 0, f = h ? [] : void 0;
1774
+ f !== void 0 && (this.changedPathSink = f);
1775
+ const m = h ? _() : 0;
1776
+ let y = S;
1393
1777
  try {
1394
- l = this.applyEventSync(a);
1395
- } catch (y) {
1396
- console.error("Emit reduce error:", y);
1778
+ y = this.applyEventSync(d);
1779
+ } catch (g) {
1780
+ console.error("Emit reduce error:", g);
1397
1781
  } finally {
1398
- r && (this.changedPathSink = null);
1782
+ h && (this.changedPathSink = null), this.currentEvent = null;
1399
1783
  }
1400
- r && this.emitInstrumentation(
1401
- a,
1402
- l,
1403
- c ?? [],
1404
- f,
1405
- z() - u
1406
- ), this.runEventEffects(a, l, d);
1784
+ h && this.emitInstrumentation(
1785
+ d,
1786
+ y,
1787
+ f ?? [],
1788
+ p,
1789
+ _() - m
1790
+ ), this.runEventEffects(d, y, l);
1407
1791
  }
1408
1792
  } finally {
1409
1793
  this.isReducing = !1;
@@ -1421,32 +1805,45 @@ class $ {
1421
1805
  * @internal
1422
1806
  */
1423
1807
  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);
1808
+ for (const c of this.middleware) {
1809
+ const l = se(c);
1810
+ if (!k(l, e)) continue;
1811
+ const i = ne(c);
1428
1812
  let a;
1429
1813
  try {
1430
- a = d(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
1814
+ a = i(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
1431
1815
  `[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
1816
  );
1433
- } catch (r) {
1434
- console.error("Middleware error:", r), a = !1;
1817
+ } catch (u) {
1818
+ console.error("Middleware error:", u), a = !1;
1435
1819
  }
1436
1820
  if (!a)
1437
- return this.notifyEventSubscribers(e, "uncommitted"), !1;
1821
+ return this.notifyEventSubscribers(e, "uncommitted"), S;
1438
1822
  }
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;
1823
+ const t = [];
1824
+ this.stagingSink = t;
1825
+ let s = null, n = "";
1826
+ try {
1827
+ this.reducerBus.emit(
1828
+ e.channel,
1829
+ e.type,
1830
+ e.payload,
1831
+ e
1832
+ ), s = this.stagedRejection, n = this.stagedRejectedBy;
1833
+ for (const [c, l] of this.patternReducers) {
1834
+ if (s !== null) break;
1835
+ if (k(l, e)) {
1836
+ const i = this.stageSliceGuarded(c, e, t);
1837
+ i !== null && (s = i, n = c);
1838
+ }
1839
+ }
1840
+ } finally {
1841
+ this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1842
+ }
1843
+ if (s !== null)
1844
+ return this.onRejected?.(s, e, n), this.notifyEventSubscribers(e, "committed"), { committed: !0, written: !1, rejected: s };
1845
+ const r = this.commitStaged(t, e);
1846
+ return this.notifyEventSubscribers(e, "committed"), r && (this.notifyEventSubscribers(e, "written"), this.listeners.forEach((c) => c())), r ? ce : oe;
1450
1847
  }
1451
1848
  /**
1452
1849
  * Runs a single committed event's effects as an **independent async task**,
@@ -1456,14 +1853,14 @@ class $ {
1456
1853
  *
1457
1854
  * @internal
1458
1855
  */
1459
- async runEventEffects(e, t, n) {
1856
+ async runEventEffects(e, t, s) {
1460
1857
  this.inFlightEffects++;
1461
1858
  try {
1462
- t && await this.notifyEffects(e);
1463
- } catch (s) {
1464
- console.error("Effect error:", s);
1859
+ t.committed && await this.notifyEffects(e);
1860
+ } catch (n) {
1861
+ console.error("Effect error:", n);
1465
1862
  } finally {
1466
- this.inFlightEffects--, n();
1863
+ this.inFlightEffects--, s(t);
1467
1864
  }
1468
1865
  }
1469
1866
  /**
@@ -1479,16 +1876,16 @@ class $ {
1479
1876
  /**
1480
1877
  * Builds an {@link InstrumentedEvent} from the reduce result and notifies
1481
1878
  * observers. `changedPaths` are the exact slice-prefixed leaf paths recorded
1482
- * by {@link forwardEvent} during this reduce, so DevTools patches need no
1879
+ * by {@link commitStaged} during this reduce, so DevTools patches need no
1483
1880
  * re-diff.
1484
1881
  *
1485
1882
  * @internal
1486
1883
  */
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 = {
1884
+ emitInstrumentation(e, t, s, n, r) {
1885
+ const c = {}, l = {};
1886
+ for (const a of s)
1887
+ c[a] = this.getAtPath(n, a), l[a] = this.getAtPath(this.state, a);
1888
+ const i = {
1492
1889
  event: {
1493
1890
  id: e.id,
1494
1891
  channel: e.channel,
@@ -1498,17 +1895,20 @@ class $ {
1498
1895
  // byte-identical to the pre-`meta` shape.
1499
1896
  ...e.meta !== void 0 ? { meta: e.meta } : {}
1500
1897
  },
1501
- committed: t,
1502
- changedPaths: n,
1503
- prevValues: d,
1504
- nextValues: a,
1505
- reduceTimeMs: i
1898
+ committed: t.committed,
1899
+ changedPaths: s,
1900
+ prevValues: c,
1901
+ nextValues: l,
1902
+ reduceTimeMs: r,
1903
+ // Present only when a reducer refused, so an observer can tell a refusal from a veto —
1904
+ // identical in state, entirely different in cause.
1905
+ ...t.rejected !== void 0 ? { rejected: t.rejected } : {}
1506
1906
  };
1507
- for (const f of [...this.instrumentObservers])
1907
+ for (const a of [...this.instrumentObservers])
1508
1908
  try {
1509
- f(r);
1510
- } catch (c) {
1511
- console.error("Instrumentation observer error:", c);
1909
+ a(i);
1910
+ } catch (u) {
1911
+ console.error("Instrumentation observer error:", u);
1512
1912
  }
1513
1913
  }
1514
1914
  /**
@@ -1539,8 +1939,13 @@ class $ {
1539
1939
  *
1540
1940
  * @public
1541
1941
  */
1542
- connect(e, t) {
1543
- return this.connectorBus.on(e.reducer, e.property, t);
1942
+ connect(e, t, s) {
1943
+ const n = this.connectorBus.on(e.reducer, e.property, t);
1944
+ if (s?.immediate === !0) {
1945
+ const r = this.state[e.reducer], c = e.property.includes("*") ? "" : e.property;
1946
+ t({ oldValue: void 0, newValue: this.getAtPath(r, c), path: c });
1947
+ }
1948
+ return n;
1544
1949
  }
1545
1950
  /**
1546
1951
  * Subscribe to events by channel and type.
@@ -1588,11 +1993,11 @@ class $ {
1588
1993
  *
1589
1994
  * @public
1590
1995
  */
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));
1996
+ onEvent(e, t, s, n = "committed") {
1997
+ const r = `${e}::${String(t)}`, c = n === "committed" ? this.committedEventSubscribers : n === "uncommitted" ? this.uncommittedEventSubscribers : n === "written" ? this.writtenEventSubscribers : this.allEventSubscribers;
1998
+ return c.has(r) || c.set(r, /* @__PURE__ */ new Set()), c.get(r).add(s), () => {
1999
+ const l = c.get(r);
2000
+ l && (l.delete(s), l.size === 0 && c.delete(r));
1596
2001
  };
1597
2002
  }
1598
2003
  /**
@@ -1698,8 +2103,8 @@ class $ {
1698
2103
  throw new Error(`Reducer ${e} already exists`);
1699
2104
  return this.mountSlice(e, t, {
1700
2105
  preserveState: !1
1701
- }), this.listeners.forEach((n) => n()), () => {
1702
- this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((n) => n());
2106
+ }), this.listeners.forEach((s) => s()), () => {
2107
+ this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((s) => s());
1703
2108
  };
1704
2109
  }
1705
2110
  /**
@@ -1734,30 +2139,118 @@ class $ {
1734
2139
  *
1735
2140
  * @public
1736
2141
  */
2142
+ /**
2143
+ * Sends a request and waits for the reply, correlating the two automatically.
2144
+ *
2145
+ * @typeParam C - Request channel.
2146
+ * @typeParam T - Request type within `C`.
2147
+ * @param channel - Channel to send on.
2148
+ * @param type - Event type to send.
2149
+ * @param payload - The **request** payload. This is what you are sending; what comes back is
2150
+ * described by {@link CallOptions.reply}, not by this.
2151
+ * @param opts - Which replies end the call, and how long to wait. See {@link CallOptions}.
2152
+ * @returns A {@link CallHandle}: `await` it for the terminal reply, or `for await` it for
2153
+ * progress events as they arrive.
2154
+ *
2155
+ * @remarks
2156
+ * Every consumer of an event bus eventually writes request/reply by hand — mint an id,
2157
+ * subscribe, match, time out, unsubscribe — and every one of them writes the same eighty lines
2158
+ * with the same two bugs: the subscription outlives the call, and a responder that forgets to
2159
+ * echo the id produces a timeout with nothing to point at. This is that, once.
2160
+ *
2161
+ * **Correlation is causal.** The store stamps `parentId` on anything emitted while an event is
2162
+ * being handled, so a responder that replies through the `emit` it was handed is already
2163
+ * correlated. There is no id to mint, echo, or forget:
2164
+ *
2165
+ * ```ts
2166
+ * store.registerEffect({
2167
+ * when: { keys: [["rpc", "ask"]] },
2168
+ * effect: async (event, _get, emit) => {
2169
+ * await emit("rpc", "answer", await lookup(event.payload.q));
2170
+ * },
2171
+ * });
2172
+ * ```
2173
+ *
2174
+ * **The reply carries its own discriminant.** A call resolves to the *event*, not the payload,
2175
+ * because a caller often cannot know which kind of reply it will get:
2176
+ *
2177
+ * ```ts
2178
+ * const res = await store.call("rpc", "ask", { q }, { reply: ["rpc", ["answer", "error"]] });
2179
+ * switch (res.type) {
2180
+ * case "answer": return res.payload;
2181
+ * case "error": throw new Error(res.payload.reason);
2182
+ * }
2183
+ * ```
2184
+ *
2185
+ * **Progress streams, with backpressure.** Any correlated event that is not terminal is
2186
+ * progress, and iterating the call consumes it. The producer genuinely waits: `emit` resolves
2187
+ * only once its effects have run, and the collector is an effect that does not return until the
2188
+ * consumer has taken the item. A responder writing `await emit("rpc", "progress", chunk)` is
2189
+ * therefore paced by the reader, with nothing buffering without bound.
2190
+ *
2191
+ * ```ts
2192
+ * const call = store.call("job", "start", { id }, {
2193
+ * reply: ["job", "done"],
2194
+ * highWaterMark: 4,
2195
+ * });
2196
+ * for await (const step of call) await render(step.payload); // producer waits on this
2197
+ * const { payload } = await call;
2198
+ * ```
2199
+ *
2200
+ * Backpressure engages **once you begin iterating**. A call that is only awaited never pulls,
2201
+ * so blocking its producer would deadlock the call itself — progress nobody reads would stop
2202
+ * the terminal event from ever being sent. Un-iterated progress therefore buffers to
2203
+ * `highWaterMark` and is then counted on {@link CallHandle.dropped} rather than blocking.
2204
+ *
2205
+ * **This is a local primitive.**
2206
+ *
2207
+ * @example Timeout is idle, not total
2208
+ * ```ts
2209
+ * // Survives a job that streams for minutes; fails a responder that goes quiet for 5s.
2210
+ * await store.call("job", "start", { id }, { reply: ["job", "done"], timeoutMs: 5_000 });
2211
+ * ```
2212
+ *
2213
+ * @example Cancelling
2214
+ * ```ts
2215
+ * const call = store.call("rpc", "ask", { q }, { reply: ["rpc", "answer"] });
2216
+ * useEffect(() => () => call.cancel("unmounted"), [call]);
2217
+ * ```
2218
+ *
2219
+ * @public
2220
+ */
2221
+ call(e, t, s, n) {
2222
+ return Z(
2223
+ { idFactory: this.idFactory, registerEffect: this.registerEffect, emit: this.emit },
2224
+ e,
2225
+ t,
2226
+ s,
2227
+ n
2228
+ );
2229
+ }
1737
2230
  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);
2231
+ const { effect: t, meta: s, when: n } = e, r = [];
2232
+ if (s && this.effectMeta.set(t, s), n && ("any" in n && n.any === !0 || "channel" in n || "channels" in n)) {
2233
+ const i = { effect: t, when: n };
2234
+ return this.patternEffects.add(i), () => {
2235
+ this.patternEffects.delete(i);
1743
2236
  };
1744
2237
  }
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);
2238
+ const l = D(e);
2239
+ if (l.length === 0 && !n) {
2240
+ const i = { effect: t, when: { any: !0 } };
2241
+ return this.patternEffects.add(i), () => {
2242
+ this.patternEffects.delete(i);
1750
2243
  };
1751
2244
  }
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));
2245
+ for (const [i, a] of l) {
2246
+ const u = `${String(i)}::${String(a)}`;
2247
+ this.effects.has(u) || this.effects.set(u, /* @__PURE__ */ new Set()), this.effects.get(u).add(t), r.push(() => {
2248
+ const d = this.effects.get(u);
2249
+ d && (d.delete(t), d.size === 0 && this.effects.delete(u));
1757
2250
  });
1758
2251
  }
1759
2252
  return () => {
1760
- for (const r of i) r();
2253
+ for (const i of r) i();
1761
2254
  };
1762
2255
  }
1763
2256
  /**
@@ -1781,15 +2274,15 @@ class $ {
1781
2274
  *
1782
2275
  * @public
1783
2276
  */
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);
2277
+ onEffect(e, t, s) {
2278
+ const n = async (r, c, l) => {
2279
+ if (r.channel !== e || r.type !== t) return;
2280
+ const i = r;
2281
+ return s(i.payload, c, l, i);
1789
2282
  };
1790
2283
  return this.registerEffect({
1791
2284
  when: { keys: [[e, t]] },
1792
- effect: s
2285
+ effect: n
1793
2286
  });
1794
2287
  }
1795
2288
  /**
@@ -1851,11 +2344,11 @@ class $ {
1851
2344
  * @public
1852
2345
  */
1853
2346
  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 });
2347
+ const s = t.preserveState !== !1, n = new Set(Object.keys(this.reducers)), r = Object.entries(e), c = new Set(r.map(([l]) => l));
2348
+ for (const l of n)
2349
+ c.has(l) || this.unmountSlice(l, { deleteState: !0 });
2350
+ for (const [l, i] of r)
2351
+ n.has(l) ? (this.unmountSlice(l, { deleteState: !1 }), this.mountSlice(l, i, { preserveState: s })) : this.mountSlice(l, i, { preserveState: !1 });
1859
2352
  }
1860
2353
  /**
1861
2354
  * Convenience API to replace **any subset** of store parts (HMR patterns).
@@ -1887,34 +2380,36 @@ class $ {
1887
2380
  *
1888
2381
  * @internal
1889
2382
  */
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 = {
2383
+ mountSlice(e, t, s) {
2384
+ const n = e, { reducer: r, state: c, when: l } = t;
2385
+ if (this.reducers[e] = new L(r), (!s.preserveState || this.state[n] === void 0) && (this.state = {
1893
2386
  ...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, []);
2387
+ [n]: R(re(n, c))
2388
+ }), l && ("any" in l && l.any === !0 || "channel" in l || "channels" in l)) {
2389
+ this.patternReducers.set(e, l), this.sliceUnsubs.set(n, []);
1897
2390
  return;
1898
2391
  }
1899
- const f = this.normalizeEventKeys(t);
1900
- if (f.length === 0 && !a) {
1901
- this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(s, []);
2392
+ const a = D(t);
2393
+ if (a.length === 0 && !l) {
2394
+ this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(n, []);
1902
2395
  return;
1903
2396
  }
1904
- const c = [];
1905
- for (const [u, l] of f) {
1906
- const y = this.reducerBus.on(u, l, (h, m) => {
1907
- const g = m ?? {
1908
- channel: u,
1909
- type: l,
1910
- payload: h,
2397
+ const u = [];
2398
+ for (const [d, h] of a) {
2399
+ const p = this.reducerBus.on(d, h, (f, m) => {
2400
+ const y = m ?? {
2401
+ channel: d,
2402
+ type: h,
2403
+ payload: f,
1911
2404
  id: this.idFactory()
1912
2405
  };
1913
- this.forwardEventGuarded(e, g);
2406
+ if (this.stagingSink === null) return;
2407
+ const g = this.stageSliceGuarded(e, y, this.stagingSink);
2408
+ g !== null && this.stagedRejection === null && (this.stagedRejection = g, this.stagedRejectedBy = e);
1914
2409
  });
1915
- c.push(y);
2410
+ u.push(p);
1916
2411
  }
1917
- this.sliceUnsubs.set(s, c);
2412
+ this.sliceUnsubs.set(n, u);
1918
2413
  }
1919
2414
  /**
1920
2415
  * Unmounts a slice: disposes reducer-bus listeners, removes reducer,
@@ -1926,38 +2421,22 @@ class $ {
1926
2421
  * @internal
1927
2422
  */
1928
2423
  unmountSlice(e, t) {
1929
- const n = e;
2424
+ const s = e;
1930
2425
  this.patternReducers.delete(e);
1931
- const s = this.sliceUnsubs.get(n);
1932
- if (s) {
1933
- for (const i of s)
2426
+ const n = this.sliceUnsubs.get(s);
2427
+ if (n) {
2428
+ for (const r of n)
1934
2429
  try {
1935
- i();
1936
- } catch (d) {
1937
- console.error(`[Store error]: ${d}`);
2430
+ r();
2431
+ } catch (c) {
2432
+ console.error(`[Store error]: ${c}`);
1938
2433
  }
1939
- this.sliceUnsubs.delete(n);
2434
+ this.sliceUnsubs.delete(s);
1940
2435
  }
1941
2436
  if (delete this.reducers[e], t.deleteState) {
1942
- const { [n]: i, ...d } = this.state;
1943
- this.state = d;
1944
- }
1945
- }
1946
- /**
1947
- * Normalizes event targeting from `when` to an array of EventKeys.
1948
- *
1949
- * @param spec - Object with an optional `when` matcher.
1950
- * @returns Array of `[channel, type]` pairs.
1951
- *
1952
- * @internal
1953
- */
1954
- normalizeEventKeys(e) {
1955
- if (e.when) {
1956
- const t = e.when;
1957
- if ("keys" in t)
1958
- return t.keys;
2437
+ const { [s]: r, ...c } = this.state;
2438
+ this.state = c;
1959
2439
  }
1960
- return [];
1961
2440
  }
1962
2441
  /**
1963
2442
  * Reads a dotted path from an object (supports numeric array indices via string keys).
@@ -1966,17 +2445,14 @@ class $ {
1966
2445
  * @param path - Dotted path; leading dot is ignored.
1967
2446
  * @returns The value at the path, or `undefined`.
1968
2447
  *
2448
+ * @remarks
2449
+ * A member rather than a bare import: a test replaces this on the instance to count how many
2450
+ * walks describing a change costs, which only works while the callers go through `this`.
2451
+ *
1969
2452
  * @internal
1970
2453
  */
1971
2454
  getAtPath(e, t) {
1972
- 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];
1978
- }
1979
- return i;
2455
+ return ee(e, t);
1980
2456
  }
1981
2457
  /**
1982
2458
  * Builds ancestor paths for a dotted path.
@@ -1994,268 +2470,259 @@ class $ {
1994
2470
  * @public
1995
2471
  */
1996
2472
  static buildAncestorPaths(e) {
1997
- 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;
2473
+ return te(e);
2002
2474
  }
2003
2475
  }
2004
- function L(o) {
2005
- return new $({
2006
- name: o.name,
2476
+ function he(o) {
2477
+ return new x({
2478
+ ...o,
2007
2479
  reducer: o.reducer ?? {},
2008
2480
  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
2481
+ effects: o.effects ?? []
2015
2482
  });
2016
2483
  }
2017
- const G = (o) => (e, t) => t.map((n) => [e, n]), Q = () => (o) => o, I = /* @__PURE__ */ new Set();
2018
- function j(o) {
2484
+ const pe = (o) => (e, t) => t.map((s) => [e, s]), me = () => (o) => o, j = /* @__PURE__ */ new Set();
2485
+ function ae(o) {
2019
2486
  const e = String(o);
2020
- I.has(e) || (I.add(e), console.warn(
2487
+ j.has(e) || (j.add(e), console.warn(
2021
2488
  `[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
2489
  ));
2023
2490
  }
2024
- function F(o, e) {
2491
+ function le(o, e) {
2025
2492
  if (o.length !== e.length) return e;
2026
2493
  for (let t = 0; t < o.length; t++)
2027
2494
  if (o[t] !== e[t]) return e;
2028
2495
  return o;
2029
2496
  }
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);
2497
+ function ye(o = {}) {
2498
+ const e = o.selectId ?? ((i) => i.id), { sortComparer: t } = o, s = (i, a) => {
2499
+ if (t === void 0) return a;
2500
+ const u = [...a].sort((d, h) => {
2501
+ const p = i.entities[d], f = i.entities[h];
2502
+ return p === void 0 || f === void 0 ? 0 : t(p, f);
2036
2503
  });
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));
2504
+ return le(a, u);
2505
+ }, n = (i, a, u) => {
2506
+ const d = { ...i, entities: a, ids: u };
2507
+ return { ...d, ids: s(d, u) };
2508
+ }, r = (i, a, u) => {
2509
+ let d = null, h = null;
2510
+ for (const p of a) {
2511
+ const f = e(p);
2512
+ process.env.NODE_ENV !== "production" && String(f).includes(".") && ae(f);
2513
+ const m = (d ?? i.entities)[f];
2514
+ if (m !== void 0 && u === "add") continue;
2515
+ const y = m !== void 0 && u === "upsert" ? { ...m, ...p } : p;
2516
+ d ??= { ...i.entities }, d[f] = y, m === void 0 && (h ??= [...i.ids], h.push(f));
2050
2517
  }
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 });
2518
+ return d === null ? i : n(i, d, h ?? i.ids);
2519
+ }, c = (i, a) => {
2520
+ let u = null;
2521
+ for (const { id: d, changes: h } of a) {
2522
+ const p = (u ?? i.entities)[d];
2523
+ p !== void 0 && (u ??= { ...i.entities }, u[d] = { ...p, ...h });
2057
2524
  }
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,
2066
- u,
2067
- r.ids.filter((l) => !c.has(l))
2525
+ return u === null ? i : n(i, u, i.ids);
2526
+ }, l = (i, a) => {
2527
+ const u = new Set(a.filter((h) => i.entities[h] !== void 0));
2528
+ if (u.size === 0) return i;
2529
+ const d = { ...i.entities };
2530
+ for (const h of u) delete d[h];
2531
+ return n(
2532
+ i,
2533
+ d,
2534
+ i.ids.filter((h) => !u.has(h))
2068
2535
  );
2069
2536
  };
2070
2537
  return {
2071
- getInitialState(r) {
2072
- const f = { ids: [], entities: {} };
2073
- return r === void 0 ? f : { ...f, ...r };
2538
+ getInitialState(i) {
2539
+ const a = { ids: [], entities: {} };
2540
+ return i === void 0 ? a : { ...a, ...i };
2074
2541
  },
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;
2542
+ addOne: (i, a) => r(i, [a], "add"),
2543
+ addMany: (i, a) => r(i, a, "add"),
2544
+ setOne: (i, a) => r(i, [a], "set"),
2545
+ setMany: (i, a) => r(i, a, "set"),
2546
+ setAll: (i, a) => {
2547
+ const u = {}, d = [];
2548
+ for (const h of a) {
2549
+ const p = e(h);
2550
+ u[p] === void 0 && d.push(p), u[p] = h;
2084
2551
  }
2085
- return s(r, c, u);
2552
+ return n(i, u, d);
2086
2553
  },
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,
2554
+ updateOne: (i, a) => c(i, [a]),
2555
+ updateMany: (i, a) => c(i, a),
2556
+ upsertOne: (i, a) => r(i, [a], "upsert"),
2557
+ upsertMany: (i, a) => r(i, a, "upsert"),
2558
+ removeOne: (i, a) => l(i, [a]),
2559
+ removeMany: (i, a) => l(i, a),
2560
+ removeAll: (i) => i.ids.length === 0 ? i : n(i, {}, []),
2561
+ selectIds: (i) => i.ids,
2562
+ selectEntities: (i) => i.entities,
2563
+ selectAll: (i) => i.ids.map((a) => i.entities[a]),
2564
+ selectById: (i, a) => i.entities[a],
2565
+ selectTotal: (i) => i.ids.length,
2099
2566
  idsPath: "ids",
2100
- pathTo: (r, f) => f === void 0 ? `entities.${r}` : `entities.${r}.${f}`,
2101
- anyField: (r) => `entities.*.${r}`
2567
+ pathTo: (i, a) => a === void 0 ? `entities.${i}` : `entities.${i}.${a}`,
2568
+ anyField: (i) => `entities.*.${i}`
2102
2569
  };
2103
2570
  }
2104
- 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) {
2571
+ const w = "$yoltra";
2572
+ function H(o, e = {}) {
2573
+ const t = e.maxNodes ?? 1e5, s = e.sanitize, n = [], r = /* @__PURE__ */ new Map();
2574
+ let c = 0, l = !1;
2575
+ function i(u, d) {
2576
+ if (s !== void 0 && (u = s(d, u)), c += 1, c > t)
2577
+ return l = !0, { [w]: "unsupported", kind: "truncated" };
2578
+ switch (typeof u) {
2112
2579
  case "undefined":
2113
- return { [E]: "undefined" };
2580
+ return { [w]: "undefined" };
2114
2581
  case "bigint":
2115
- return { [E]: "bigint", value: c.toString() };
2582
+ return { [w]: "bigint", value: u.toString() };
2116
2583
  case "number":
2117
- return Number.isNaN(c) ? { [E]: "nan" } : c === 1 / 0 ? { [E]: "infinity", sign: 1 } : c === -1 / 0 ? { [E]: "infinity", sign: -1 } : c;
2584
+ return Number.isNaN(u) ? { [w]: "nan" } : u === 1 / 0 ? { [w]: "infinity", sign: 1 } : u === -1 / 0 ? { [w]: "infinity", sign: -1 } : u;
2118
2585
  case "function":
2119
2586
  case "symbol":
2120
- return s.push(u), { [E]: "unsupported", kind: typeof c };
2587
+ return n.push(d), { [w]: "unsupported", kind: typeof u };
2121
2588
  case "string":
2122
2589
  case "boolean":
2123
- return c;
2590
+ return u;
2124
2591
  }
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) {
2592
+ if (u === null) return null;
2593
+ const h = u, p = r.get(h);
2594
+ if (p !== void 0) return { [w]: "ref", path: p };
2595
+ if (r.set(h, d), u instanceof Date)
2596
+ return { [w]: "date", iso: u.toISOString() };
2597
+ if (u instanceof RegExp)
2598
+ return { [w]: "regexp", source: u.source, flags: u.flags };
2599
+ if (u instanceof Error)
2600
+ return { [w]: "error", name: u.name, message: u.message };
2601
+ if (u instanceof Map) {
2135
2602
  const m = [];
2136
- 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 };
2603
+ let y = 0;
2604
+ for (const [g, $] of u)
2605
+ m.push([i(g, `${d}/@k${y}`), i($, `${d}/${y}`)]), y += 1;
2606
+ return { [w]: "map", entries: m };
2140
2607
  }
2141
- if (c instanceof Set) {
2608
+ if (u instanceof Set) {
2142
2609
  const m = [];
2143
- let g = 0;
2144
- for (const w of c)
2145
- m.push(r(w, `${u}/${g}`)), g += 1;
2146
- return { [E]: "set", values: m };
2610
+ let y = 0;
2611
+ for (const g of u)
2612
+ m.push(i(g, `${d}/${y}`)), y += 1;
2613
+ return { [w]: "set", values: m };
2147
2614
  }
2148
- if (Array.isArray(c))
2149
- return c.map((m, g) => r(m, `${u}/${g}`));
2150
- const h = {};
2151
- for (const [m, g] of Object.entries(c))
2152
- h[m] = r(g, `${u}/${A(m)}`);
2153
- return E in h ? { [E]: "escaped", value: h } : h;
2154
- }
2155
- return { value: r(o, ""), report: { truncated: a, unsupported: s } };
2615
+ if (Array.isArray(u))
2616
+ return u.map((m, y) => i(m, `${d}/${y}`));
2617
+ const f = {};
2618
+ for (const [m, y] of Object.entries(u))
2619
+ f[m] = i(y, `${d}/${F(m)}`);
2620
+ return w in f ? { [w]: "escaped", value: f } : f;
2621
+ }
2622
+ return { value: i(o, ""), report: { truncated: l, unsupported: n } };
2156
2623
  }
2157
- function V(o) {
2624
+ function ue(o) {
2158
2625
  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;
2626
+ function s(c, l) {
2627
+ if (c === null || typeof c != "object") return c;
2628
+ if (Array.isArray(c)) {
2629
+ const a = [];
2630
+ return e.set(l, a), c.forEach((u, d) => {
2631
+ if (N(u)) {
2632
+ t.push({ target: a, key: d, path: u.path }), a[d] = void 0;
2166
2633
  return;
2167
2634
  }
2168
- f[u] = n(c, `${a}/${u}`);
2169
- }), f;
2635
+ a[d] = s(u, `${l}/${d}`);
2636
+ }), a;
2170
2637
  }
2171
- if (typeof d[E] == "string") {
2172
- const f = d;
2173
- switch (f[E]) {
2638
+ if (typeof c[w] == "string") {
2639
+ const a = c;
2640
+ switch (a[w]) {
2174
2641
  case "undefined":
2175
2642
  return;
2176
2643
  case "nan":
2177
2644
  return Number.NaN;
2178
2645
  case "infinity":
2179
- return f.sign === 1 ? 1 / 0 : -1 / 0;
2646
+ return a.sign === 1 ? 1 / 0 : -1 / 0;
2180
2647
  case "bigint":
2181
- return BigInt(f.value);
2648
+ return BigInt(a.value);
2182
2649
  case "date":
2183
- return new Date(f.iso);
2650
+ return new Date(a.iso);
2184
2651
  case "regexp":
2185
- return new RegExp(f.source, f.flags);
2652
+ return new RegExp(a.source, a.flags);
2186
2653
  case "error": {
2187
- const c = new Error(f.message);
2188
- return c.name = f.name, c;
2654
+ const u = new Error(a.message);
2655
+ return u.name = a.name, u;
2189
2656
  }
2190
2657
  case "unsupported":
2191
2658
  return;
2192
2659
  case "ref":
2193
2660
  return;
2194
2661
  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;
2662
+ const u = /* @__PURE__ */ new Map();
2663
+ return e.set(l, u), a.entries.forEach(([d, h], p) => {
2664
+ u.set(s(d, `${l}/@k${p}`), s(h, `${l}/${p}`));
2665
+ }), u;
2199
2666
  }
2200
2667
  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;
2668
+ const u = /* @__PURE__ */ new Set();
2669
+ return e.set(l, u), a.values.forEach((d, h) => u.add(s(d, `${l}/${h}`))), u;
2203
2670
  }
2204
2671
  case "escaped":
2205
- return s(f.value, a);
2672
+ return n(a.value, l);
2206
2673
  default:
2207
2674
  return;
2208
2675
  }
2209
2676
  }
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;
2677
+ return n(c, l);
2678
+ }
2679
+ function n(c, l) {
2680
+ const i = {};
2681
+ e.set(l, i);
2682
+ for (const [a, u] of Object.entries(c)) {
2683
+ const d = `${l}/${F(a)}`;
2684
+ if (N(u)) {
2685
+ t.push({ target: i, key: a, path: u.path }), i[a] = void 0;
2219
2686
  continue;
2220
2687
  }
2221
- r[f] = n(c, u);
2688
+ i[a] = s(u, d);
2222
2689
  }
2223
- return r;
2690
+ return i;
2224
2691
  }
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;
2692
+ const r = s(o, "");
2693
+ e.set("", r);
2694
+ for (const { target: c, key: l, path: i } of t)
2695
+ c[l] = e.get(i);
2696
+ return r;
2230
2697
  }
2231
- function R(o) {
2232
- return o !== null && typeof o == "object" && o[E] === "ref" && typeof o.path == "string";
2698
+ function N(o) {
2699
+ return o !== null && typeof o == "object" && o[w] === "ref" && typeof o.path == "string";
2233
2700
  }
2234
- function A(o) {
2701
+ function F(o) {
2235
2702
  return o.replace(/~/g, "~0").replace(/\//g, "~1");
2236
2703
  }
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;
2704
+ function ge(o, e, t = {}) {
2705
+ let s = t.maxNodes ?? 1e5;
2706
+ for (let n = 0; n < 8; n += 1) {
2707
+ const { value: r, report: c } = H(o, { ...t, maxNodes: s });
2708
+ let l;
2242
2709
  try {
2243
- a = JSON.stringify(i)?.length ?? 0;
2710
+ l = JSON.stringify(r)?.length ?? 0;
2244
2711
  } catch {
2245
- a = Number.POSITIVE_INFINITY;
2712
+ l = Number.POSITIVE_INFINITY;
2246
2713
  }
2247
- if (a <= e)
2248
- return d.truncated ? {
2249
- value: i,
2714
+ if (l <= e)
2715
+ return c.truncated ? {
2716
+ value: r,
2250
2717
  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)
2718
+ note: `State was too large to send in full; parts beyond ${s} nodes are omitted.`
2719
+ } : { value: r, truncated: !1 };
2720
+ const i = Math.floor(s * e * 0.8 / l);
2721
+ if (s = Math.max(1, Math.min(i, s - 1)), s <= 1 && n > 0)
2255
2722
  break;
2256
2723
  }
2257
2724
  return {
2258
- value: { [E]: "unsupported", kind: "truncated" },
2725
+ value: { [w]: "unsupported", kind: "truncated" },
2259
2726
  truncated: !0,
2260
2727
  note: `State exceeds the ${e}-byte transport limit and could not be reduced to fit.`
2261
2728
  };
@@ -2263,104 +2730,104 @@ function Y(o, e, t = {}) {
2263
2730
  function b(o, e, t) {
2264
2731
  o.onError?.(e, t);
2265
2732
  }
2266
- async function q(o) {
2733
+ async function we(o) {
2267
2734
  const e = { slices: {}, restored: !1 };
2268
2735
  let t;
2269
2736
  try {
2270
2737
  t = o.source ?? await o.adapter.read(o.key);
2271
- } catch (s) {
2272
- return b(o, s, "read"), e;
2738
+ } catch (n) {
2739
+ return b(o, n, "read"), e;
2273
2740
  }
2274
2741
  if (t == null || t === "") return e;
2275
- let n;
2742
+ let s;
2276
2743
  try {
2277
- n = V(JSON.parse(t));
2278
- } catch (s) {
2279
- return b(o, s, "decode"), e;
2744
+ s = ue(JSON.parse(t));
2745
+ } catch (n) {
2746
+ return b(o, n, "decode"), e;
2280
2747
  }
2281
- if (n === null || typeof n != "object" || typeof n.version != "number")
2748
+ if (s === null || typeof s != "object" || typeof s.version != "number")
2282
2749
  return b(o, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
2283
- if (n.version !== o.version) {
2750
+ if (s.version !== o.version) {
2284
2751
  if (o.migrate === void 0)
2285
2752
  return b(
2286
2753
  o,
2287
2754
  new Error(
2288
- `persisted state is version ${n.version}, this build expects ${o.version}, and no migrate was supplied`
2755
+ `persisted state is version ${s.version}, this build expects ${o.version}, and no migrate was supplied`
2289
2756
  ),
2290
2757
  "migrate"
2291
2758
  ), e;
2292
2759
  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;
2760
+ const n = o.migrate(s.slices, s.version);
2761
+ return n === null ? e : { slices: n, restored: !0 };
2762
+ } catch (n) {
2763
+ return b(o, n, "migrate"), e;
2297
2764
  }
2298
2765
  }
2299
- return { slices: n.slices ?? {}, restored: !0 };
2766
+ return { slices: s.slices ?? {}, restored: !0 };
2300
2767
  }
2301
- function X(o, e) {
2768
+ function Ee(o, e) {
2302
2769
  if (!e.restored) return o;
2303
2770
  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 };
2771
+ for (const [s, n] of Object.entries(o)) {
2772
+ const r = e.slices[s];
2773
+ t[s] = r === void 0 ? n : { ...n, state: r };
2307
2774
  }
2308
2775
  return t;
2309
2776
  }
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);
2777
+ function K(o, e) {
2778
+ const t = o ?? {}, s = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((n) => n in t).map((n) => [n, t[n]]));
2779
+ return JSON.stringify(H({ version: e.version, slices: s }).value);
2313
2780
  }
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;
2781
+ function be(o, e) {
2782
+ const t = e.throttleMs ?? 250, s = e.slices;
2783
+ let n = null, r = !1;
2784
+ const c = () => {
2785
+ if (r) {
2786
+ r = !1;
2320
2787
  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");
2788
+ const a = e.adapter.write(e.key, K(o.getState(), e));
2789
+ a instanceof Promise && a.catch((u) => b(e, u, "write"));
2790
+ } catch (a) {
2791
+ b(e, a, "write");
2325
2792
  }
2326
2793
  }
2327
- }, a = () => {
2328
- if (i = !0, t <= 0) {
2329
- d();
2794
+ }, l = () => {
2795
+ if (r = !0, t <= 0) {
2796
+ c();
2330
2797
  return;
2331
2798
  }
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();
2799
+ n === null && (n = setTimeout(() => {
2800
+ n = null, c();
2801
+ }, t), n.unref?.());
2802
+ }, i = o.instrument((a) => {
2803
+ if (s === void 0) {
2804
+ l();
2338
2805
  return;
2339
2806
  }
2340
- (f.changedPaths ?? []).some(
2341
- (u) => n.some((l) => u === l || u.startsWith(`${l}.`))
2342
- ) && a();
2807
+ (a.changedPaths ?? []).some(
2808
+ (d) => s.some((h) => d === h || d.startsWith(`${h}.`))
2809
+ ) && l();
2343
2810
  });
2344
2811
  return () => {
2345
- r(), s !== null && (clearTimeout(s), s = null), d();
2812
+ i(), n !== null && (clearTimeout(n), n = null), c();
2346
2813
  };
2347
2814
  }
2348
- function ee(o, e) {
2349
- return N(o.getState(), e);
2815
+ function ve(o, e) {
2816
+ return K(o.getState(), e);
2350
2817
  }
2351
- function te(o) {
2818
+ function Se(o) {
2352
2819
  return {
2353
2820
  read: (e) => o.getItem(e),
2354
2821
  write: (e, t) => o.setItem(e, t),
2355
2822
  remove: (e) => o.removeItem(e)
2356
2823
  };
2357
2824
  }
2358
- function se(o) {
2825
+ function $e(o) {
2359
2826
  const e = new Map(Object.entries(o ?? {}));
2360
2827
  return {
2361
2828
  read: (t) => e.get(t) ?? null,
2362
- write: (t, n) => {
2363
- e.set(t, n);
2829
+ write: (t, s) => {
2830
+ e.set(t, s);
2364
2831
  },
2365
2832
  remove: (t) => {
2366
2833
  e.delete(t);
@@ -2368,24 +2835,28 @@ function se(o) {
2368
2835
  };
2369
2836
  }
2370
2837
  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
2838
+ C as CallAbortedError,
2839
+ Q as CallTimeoutError,
2840
+ W as EventBus,
2841
+ U as LooseEventBus,
2842
+ L as Reducer,
2843
+ fe as Rejected,
2844
+ x as Store,
2845
+ ye as createEntityAdapter,
2846
+ $e as createMemoryAdapter,
2847
+ he as createStore,
2848
+ Se as createWebStorageAdapter,
2849
+ ue as decodeState,
2850
+ ve as dehydrate,
2851
+ I as detectChangedProps,
2852
+ H as encodeState,
2853
+ ge as encodeStateBounded,
2854
+ me as eventKeys,
2855
+ M as freezeState,
2856
+ we as hydrate,
2857
+ V as isRejected,
2858
+ be as persist,
2859
+ pe as typedEvents,
2860
+ Ee as withHydration
2390
2861
  };
2391
2862
  //# sourceMappingURL=yoltra.mjs.map