@yoltra/core 0.6.0 → 0.8.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.6.0
2
+ * @yoltra/core v0.8.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 K = Object.defineProperty;
11
- var F = (c, e, t) => e in c ? K(c, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : c[e] = t;
12
- var p = (c, e, t) => F(c, typeof e != "symbol" ? e + "" : e, t);
13
- class U {
14
- 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 G {
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 U {
50
40
  *
51
41
  * @public
52
42
  */
53
- on(e, t, s) {
54
- let n = this.handlers.get(e);
55
- n || (n = /* @__PURE__ */ new Map(), this.handlers.set(e, n));
56
- let r = n.get(t);
57
- return r || (r = /* @__PURE__ */ new Set(), n.set(t, r)), r.add(s), () => this.off(e, t, s);
43
+ on(e, t, n) {
44
+ let r = this.handlers.get(e);
45
+ r || (r = /* @__PURE__ */ new Map(), this.handlers.set(e, r));
46
+ let i = r.get(t);
47
+ return i || (i = /* @__PURE__ */ new Set(), r.set(t, i)), i.add(n), () => this.off(e, t, n);
58
48
  }
59
49
  /**
60
50
  * Removes a specific handler previously added with {@link EventBus.on | `on`}.
@@ -76,11 +66,11 @@ class U {
76
66
  *
77
67
  * @public
78
68
  */
79
- off(e, t, s) {
80
- const n = this.handlers.get(e);
81
- if (!n) return;
82
- const r = n.get(t);
83
- r && (r.delete(s), r.size === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e));
69
+ off(e, t, n) {
70
+ const r = this.handlers.get(e);
71
+ if (!r) return;
72
+ const i = r.get(t);
73
+ i && (i.delete(n), i.size === 0 && r.delete(t), r.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 U {
104
94
  *
105
95
  * @public
106
96
  */
107
- emit(e, t, s, n) {
108
- const r = this.handlers.get(e);
109
- if (!r) return;
110
- const o = r.get(t);
111
- if (!(!o || o.size === 0))
112
- for (const l of [...o])
97
+ emit(e, t, n, r) {
98
+ const i = this.handlers.get(e);
99
+ if (!i) return;
100
+ const s = i.get(t);
101
+ if (!(!s || s.size === 0))
102
+ for (const l of [...s])
113
103
  try {
114
- l(s, n);
115
- } catch (i) {
116
- console.error("EventBus handler error:", i);
104
+ l(n, r);
105
+ } catch (c) {
106
+ console.error("EventBus handler error:", c);
117
107
  }
118
108
  }
119
109
  /**
@@ -133,37 +123,35 @@ class U {
133
123
  this.handlers.clear();
134
124
  }
135
125
  }
136
- class L {
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 J {
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 L {
196
184
  *
197
185
  * @public
198
186
  */
199
- on(e, t, s) {
200
- const n = String(t);
201
- if (this.isPattern(n)) {
202
- const r = n;
187
+ on(e, t, n) {
188
+ const r = String(t);
189
+ if (this.isPattern(r)) {
190
+ const i = r;
203
191
  this.patternHandlers.has(e) || this.patternHandlers.set(e, /* @__PURE__ */ new Map());
204
- const o = this.patternHandlers.get(e);
205
- return o.has(r) || (o.set(r, []), this.indexPattern(e, r)), o.get(r).push(s), () => this.offPattern(e, r, s);
192
+ const s = this.patternHandlers.get(e);
193
+ return s.has(i) || (s.set(i, []), this.indexPattern(e, i)), s.get(i).push(n), () => this.offPattern(e, i, n);
206
194
  } else {
207
- const r = this.normalizeTypeKey(n);
195
+ const i = this.normalizeTypeKey(r);
208
196
  this.handlers.has(e) || this.handlers.set(e, /* @__PURE__ */ new Map());
209
- const o = this.handlers.get(e);
210
- return o.has(r) || o.set(r, []), o.get(r).push(s), () => this.offExactNormalized(e, r, s);
197
+ const s = this.handlers.get(e);
198
+ return s.has(i) || s.set(i, []), s.get(i).push(n), () => this.offExactNormalized(e, i, n);
211
199
  }
212
200
  }
213
201
  /**
@@ -228,9 +216,9 @@ class L {
228
216
  *
229
217
  * @public
230
218
  */
231
- off(e, t, s) {
232
- const n = this.normalizeTypeKey(String(t));
233
- this.offExactNormalized(e, n, s);
219
+ off(e, t, n) {
220
+ const r = this.normalizeTypeKey(String(t));
221
+ this.offExactNormalized(e, r, n);
234
222
  }
235
223
  /**
236
224
  * Internal exact unsubscription using an already **normalized** type key.
@@ -240,13 +228,13 @@ class L {
240
228
  * @param handler - Handler to remove.
241
229
  * @internal
242
230
  */
243
- offExactNormalized(e, t, s) {
244
- const n = this.handlers.get(e);
245
- if (!n) return;
246
- const r = n.get(t);
231
+ offExactNormalized(e, t, n) {
232
+ const r = this.handlers.get(e);
247
233
  if (!r) return;
248
- const o = r.indexOf(s);
249
- o !== -1 && r.splice(o, 1), r.length === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e);
234
+ const i = r.get(t);
235
+ if (!i) return;
236
+ const s = i.indexOf(n);
237
+ s !== -1 && i.splice(s, 1), i.length === 0 && r.delete(t), r.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 L {
256
244
  * @param handler - Handler to remove.
257
245
  * @internal
258
246
  */
259
- offPattern(e, t, s) {
260
- const n = this.patternHandlers.get(e);
261
- if (!n) return;
262
- const r = n.get(t);
247
+ offPattern(e, t, n) {
248
+ const r = this.patternHandlers.get(e);
263
249
  if (!r) return;
264
- const o = r.indexOf(s);
265
- o !== -1 && r.splice(o, 1), r.length === 0 && (n.delete(t), this.unindexPattern(e, t)), n.size === 0 && (this.patternHandlers.delete(e), this.patternIndex.delete(e));
250
+ const i = r.get(t);
251
+ if (!i) return;
252
+ const s = i.indexOf(n);
253
+ s !== -1 && i.splice(s, 1), i.length === 0 && (r.delete(t), this.unindexPattern(e, t)), r.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,20 +272,20 @@ class L {
284
272
  *
285
273
  * @public
286
274
  */
287
- emit(e, t, s) {
288
- const n = String(t), r = this.normalizeTypeKey(n), o = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n), i = /* @__PURE__ */ new Set(), a = (d) => {
289
- for (const u of [...d])
290
- if (!i.has(u)) {
291
- i.add(u);
275
+ emit(e, t, n) {
276
+ const r = String(t), i = this.normalizeTypeKey(r), s = this.handlers.get(e)?.get(i) ?? [], l = this.matchingPatternHandlers(e, r), c = /* @__PURE__ */ new Set(), a = (d) => {
277
+ for (const f of [...d])
278
+ if (!c.has(f)) {
279
+ c.add(f);
292
280
  try {
293
- u(s);
294
- } catch (f) {
295
- console.error(f);
281
+ f(n);
282
+ } catch (u) {
283
+ console.error(u);
296
284
  continue;
297
285
  }
298
286
  }
299
287
  };
300
- a(o);
288
+ a(s);
301
289
  for (const d of l) a(d);
302
290
  }
303
291
  /**
@@ -316,23 +304,23 @@ class L {
316
304
  *
317
305
  * @public
318
306
  */
319
- emitWith(e, t, s) {
320
- const n = String(t), r = this.normalizeTypeKey(n), o = this.handlers.get(e)?.get(r) ?? [], l = this.matchingPatternHandlers(e, n);
321
- if (o.length === 0 && l.length === 0) return;
322
- const i = s(), a = /* @__PURE__ */ new Set(), d = (u) => {
323
- for (const f of [...u])
324
- if (!a.has(f)) {
325
- a.add(f);
307
+ emitWith(e, t, n) {
308
+ const r = String(t), i = this.normalizeTypeKey(r), s = this.handlers.get(e)?.get(i) ?? [], l = this.matchingPatternHandlers(e, r);
309
+ if (s.length === 0 && l.length === 0) return;
310
+ const c = n(), a = /* @__PURE__ */ new Set(), d = (f) => {
311
+ for (const u of [...f])
312
+ if (!a.has(u)) {
313
+ a.add(u);
326
314
  try {
327
- f(i);
328
- } catch (m) {
329
- console.error(m);
315
+ u(c);
316
+ } catch (p) {
317
+ console.error(p);
330
318
  continue;
331
319
  }
332
320
  }
333
321
  };
334
- d(o);
335
- for (const u of l) d(u);
322
+ d(s);
323
+ for (const f of l) d(f);
336
324
  }
337
325
  /**
338
326
  * Determines if a string is a **pattern** (contains `*`).
@@ -371,27 +359,27 @@ class L {
371
359
  * @internal
372
360
  */
373
361
  indexPattern(e, t) {
374
- let s = this.patternIndex.get(e);
375
- s === void 0 && (s = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, s));
376
- const n = this.splitPath(t), r = { pattern: t, segments: n }, o = n[0];
377
- if (o === void 0 || o === "*" || o === "**") {
378
- s.anyHead.push(r);
362
+ let n = this.patternIndex.get(e);
363
+ n === void 0 && (n = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, n));
364
+ const r = this.splitPath(t), i = { pattern: t, segments: r }, s = r[0];
365
+ if (s === void 0 || s === "*" || s === "**") {
366
+ n.anyHead.push(i);
379
367
  return;
380
368
  }
381
- const l = s.byHead.get(o);
382
- l === void 0 ? s.byHead.set(o, [r]) : l.push(r);
369
+ const l = n.byHead.get(s);
370
+ l === void 0 ? n.byHead.set(s, [i]) : l.push(i);
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 s = this.patternIndex.get(e);
390
- if (s === void 0) return;
391
- const n = this.splitPath(t)[0], r = n === void 0 || n === "*" || n === "**" ? s.anyHead : s.byHead.get(n);
392
- if (r === void 0) return;
393
- const o = r.findIndex((l) => l.pattern === t);
394
- o !== -1 && r.splice(o, 1), r.length === 0 && r !== s.anyHead && n !== void 0 && s.byHead.delete(n);
377
+ const n = this.patternIndex.get(e);
378
+ if (n === void 0) return;
379
+ const r = this.splitPath(t)[0], i = r === void 0 || r === "*" || r === "**" ? n.anyHead : n.byHead.get(r);
380
+ if (i === void 0) return;
381
+ const s = i.findIndex((l) => l.pattern === t);
382
+ s !== -1 && i.splice(s, 1), i.length === 0 && i !== n.anyHead && r !== void 0 && n.byHead.delete(r);
395
383
  }
396
384
  /**
397
385
  * The handler lists of every pattern matching this subject.
@@ -405,20 +393,20 @@ class L {
405
393
  * @internal
406
394
  */
407
395
  matchingPatternHandlers(e, t) {
408
- const s = this.patternHandlers.get(e), n = this.patternIndex.get(e);
409
- if (s === void 0 || s.size === 0 || n === void 0) return [];
410
- const r = this.splitPath(t), o = [], l = (a) => {
396
+ const n = this.patternHandlers.get(e), r = this.patternIndex.get(e);
397
+ if (n === void 0 || n.size === 0 || r === void 0) return [];
398
+ const i = this.splitPath(t), s = [], l = (a) => {
411
399
  for (const d of a) {
412
- if (!this.matchSegments(d.segments, r)) continue;
413
- const u = s.get(d.pattern);
414
- u !== void 0 && o.push(u);
400
+ if (!this.matchSegments(d.segments, i)) continue;
401
+ const f = n.get(d.pattern);
402
+ f !== void 0 && s.push(f);
415
403
  }
416
- }, i = r[0];
417
- if (i !== void 0) {
418
- const a = n.byHead.get(i);
404
+ }, c = i[0];
405
+ if (c !== void 0) {
406
+ const a = r.byHead.get(c);
419
407
  a !== void 0 && l(a);
420
408
  }
421
- return l(n.anyHead), o;
409
+ return l(r.anyHead), s;
422
410
  }
423
411
  /**
424
412
  * Pattern matcher over dot-separated segments, which arrive already split.
@@ -449,18 +437,18 @@ class L {
449
437
  * @internal
450
438
  */
451
439
  matchSegments(e, t) {
452
- let s = 0, n = 0, r = -1, o = 0;
453
- for (; n < t.length; )
454
- if (s < e.length && (e[s] === "*" || e[s] === t[n]))
455
- s++, n++;
456
- else if (s < e.length && e[s] === "**")
457
- r = s, o = n, s++;
458
- else if (r !== -1)
459
- s = r + 1, n = ++o;
440
+ let n = 0, r = 0, i = -1, s = 0;
441
+ for (; r < t.length; )
442
+ if (n < e.length && (e[n] === "*" || e[n] === t[r]))
443
+ n++, r++;
444
+ else if (n < e.length && e[n] === "**")
445
+ i = n, s = r, n++;
446
+ else if (i !== -1)
447
+ n = i + 1, r = ++s;
460
448
  else
461
449
  return !1;
462
- for (; s < e.length && e[s] === "**"; ) s++;
463
- return s === e.length;
450
+ for (; n < e.length && e[n] === "**"; ) n++;
451
+ return n === e.length;
464
452
  }
465
453
  /**
466
454
  * Removes **all** listeners (exact and pattern). Useful for tests/HMR teardown.
@@ -485,16 +473,21 @@ class L {
485
473
  */
486
474
  __introspect() {
487
475
  const e = [];
488
- for (const [t, s] of this.handlers)
489
- for (const [n, r] of s)
490
- r.length > 0 && e.push({ channel: t, type: n, count: r.length });
491
- for (const [t, s] of this.patternHandlers)
492
- for (const [n, r] of s)
493
- r.length > 0 && e.push({ channel: t, type: n, count: r.length });
476
+ for (const [t, n] of this.handlers)
477
+ for (const [r, i] of n)
478
+ i.length > 0 && e.push({ channel: t, type: r, count: i.length });
479
+ for (const [t, n] of this.patternHandlers)
480
+ for (const [r, i] of n)
481
+ i.length > 0 && e.push({ channel: t, type: r, count: i.length });
494
482
  return e;
495
483
  }
496
484
  }
497
- class V {
485
+ class X {
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 V {
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
  /**
@@ -536,138 +524,163 @@ class V {
536
524
  return this._reduce(e, t);
537
525
  }
538
526
  }
539
- const T = /* @__PURE__ */ new Set();
540
- function O(c, e) {
541
- const t = c ? `${c}.${e}` : e;
542
- T.has(t) || (T.add(t), console.warn(
543
- `[yoltra] State key "${e}"${c ? ` under "${c}"` : ""} contains a dot. Paths are dotted, so this key is indistinguishable from nested objects of the same name: a subscription to "${t}" may match the wrong value, and DevTools patches for it will address the wrong node. Rename the key, or nest it.`
527
+ const _ = /* @__PURE__ */ new Set();
528
+ function P(o, e) {
529
+ const t = o ? `${o}.${e}` : e;
530
+ _.has(t) || (_.add(t), console.warn(
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 I(c, e, t = "", s = /* @__PURE__ */ new Map()) {
547
- const n = [];
548
- return P(c, e, t, s, n), n;
534
+ function I(o, e, t = "", n = /* @__PURE__ */ new Map()) {
535
+ const r = [];
536
+ return M(o, e, t, n, r), r;
549
537
  }
550
- function P(c, e, t, s, n) {
551
- if (c === e) return;
552
- if (typeof c != "object" || typeof e != "object" || c === null || e === null) {
553
- if (typeof c == "number" && Number.isNaN(c) && Number.isNaN(e))
538
+ function M(o, e, t, n, r) {
539
+ if (o === e) return;
540
+ if (typeof o != "object" || typeof e != "object" || o === null || e === null) {
541
+ if (typeof o == "number" && Number.isNaN(o) && Number.isNaN(e))
554
542
  return;
555
- n.push(t);
543
+ r.push(t);
556
544
  return;
557
545
  }
558
- if (c instanceof Date && e instanceof Date) {
559
- c.getTime() !== e.getTime() && n.push(t);
546
+ if (o instanceof Date && e instanceof Date) {
547
+ o.getTime() !== e.getTime() && r.push(t);
560
548
  return;
561
549
  }
562
- if (c instanceof RegExp && e instanceof RegExp) {
563
- (c.source !== e.source || e.flags !== c.flags) && n.push(t);
550
+ if (o instanceof RegExp && e instanceof RegExp) {
551
+ (o.source !== e.source || e.flags !== o.flags) && r.push(t);
564
552
  return;
565
553
  }
566
- if (c instanceof Map || e instanceof Map) {
567
- n.push(t);
554
+ if (o instanceof Map || e instanceof Map) {
555
+ r.push(t);
568
556
  return;
569
557
  }
570
- if (c instanceof Set || e instanceof Set) {
571
- n.push(t);
558
+ if (o instanceof Set || e instanceof Set) {
559
+ r.push(t);
572
560
  return;
573
561
  }
574
- const r = c, o = e, l = s.get(r);
575
- if (l?.has(o)) return;
576
- const i = l ?? /* @__PURE__ */ new Set();
577
- i.add(o), l || s.set(r, i);
562
+ const i = o, s = e, l = n.get(i);
563
+ if (l?.has(s)) return;
564
+ const c = l ?? /* @__PURE__ */ new Set();
565
+ c.add(s), l || n.set(i, c);
578
566
  try {
579
- const a = Array.isArray(c), d = Array.isArray(e);
567
+ const a = Array.isArray(o), d = Array.isArray(e);
580
568
  if (a !== d) {
581
- n.push(t);
569
+ r.push(t);
582
570
  return;
583
571
  }
584
572
  if (a) {
585
- const h = c, y = e;
586
- h.length !== y.length && t && n.push(t);
587
- const g = Math.min(h.length, y.length);
588
- for (let w = 0; w < g; w++)
589
- h[w] !== y[w] && P(h[w], y[w], t ? `${t}.${w}` : `${w}`, s, n);
590
- for (let w = g; w < Math.max(h.length, y.length); w++)
591
- n.push(t ? `${t}.${w}` : `${w}`);
573
+ const h = o, y = e;
574
+ h.length !== y.length && t && r.push(t);
575
+ const w = Math.min(h.length, y.length);
576
+ for (let g = 0; g < w; g++)
577
+ h[g] !== y[g] && M(h[g], y[g], t ? `${t}.${g}` : `${g}`, n, r);
578
+ for (let g = w; g < Math.max(h.length, y.length); g++)
579
+ r.push(t ? `${t}.${g}` : `${g}`);
592
580
  return;
593
581
  }
594
- const u = Object.keys(c), f = Object.keys(e);
595
- if (u.length === 0 && f.length === 0) {
596
- n.push(t);
582
+ const f = Object.keys(o), u = Object.keys(e);
583
+ if (f.length === 0 && u.length === 0) {
584
+ r.push(t);
597
585
  return;
598
586
  }
599
- let m = u.length === f.length;
600
- if (m) {
601
- for (let h = 0; h < f.length; h++)
602
- if (!Object.prototype.hasOwnProperty.call(c, f[h])) {
603
- m = !1;
587
+ let p = f.length === u.length;
588
+ if (p) {
589
+ for (let h = 0; h < u.length; h++)
590
+ if (!Object.prototype.hasOwnProperty.call(o, u[h])) {
591
+ p = !1;
604
592
  break;
605
593
  }
606
594
  }
607
- if (m) {
608
- for (const h of f)
609
- c[h] !== e[h] && (process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h), P(c[h], e[h], t ? `${t}.${h}` : h, s, n));
595
+ if (p) {
596
+ for (const h of u)
597
+ o[h] !== e[h] && (process.env.NODE_ENV !== "production" && h.includes(".") && P(t, h), M(o[h], e[h], t ? `${t}.${h}` : h, n, r));
610
598
  return;
611
599
  }
612
- for (const h of f) {
613
- const y = Object.prototype.hasOwnProperty.call(c, h);
614
- if (y && c[h] === e[h]) continue;
615
- process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h);
616
- const g = t ? `${t}.${h}` : h;
600
+ for (const h of u) {
601
+ const y = Object.prototype.hasOwnProperty.call(o, h);
602
+ if (y && o[h] === e[h]) continue;
603
+ process.env.NODE_ENV !== "production" && h.includes(".") && P(t, h);
604
+ const w = t ? `${t}.${h}` : h;
617
605
  if (!y) {
618
- n.push(g);
606
+ r.push(w);
619
607
  continue;
620
608
  }
621
- P(c[h], e[h], g, s, n);
609
+ M(o[h], e[h], w, n, r);
622
610
  }
623
- for (const h of u)
624
- Object.prototype.hasOwnProperty.call(e, h) || (process.env.NODE_ENV !== "production" && h.includes(".") && O(t, h), n.push(t ? `${t}.${h}` : h));
611
+ for (const h of f)
612
+ Object.prototype.hasOwnProperty.call(e, h) || (process.env.NODE_ENV !== "production" && h.includes(".") && P(t, h), r.push(t ? `${t}.${h}` : h));
625
613
  } finally {
626
- i.delete(o), i.size === 0 && s.delete(r);
614
+ c.delete(s), c.size === 0 && n.delete(i);
627
615
  }
628
616
  }
629
- function M(c, e = /* @__PURE__ */ new WeakSet(), t) {
630
- if (c === null || typeof c != "object" || e.has(c) || (t !== void 0 && c === t.watch && t.onFound(), Object.isFrozen(c))) return c;
631
- if (e.add(c), Array.isArray(c)) {
632
- const s = c;
633
- for (let n = 0; n < s.length; n++)
634
- s[n] = M(s[n], e, t);
635
- return Object.freeze(s);
636
- }
637
- for (const s of Object.getOwnPropertyNames(c)) {
638
- const n = Object.getOwnPropertyDescriptor(c, s);
639
- !n || !("value" in n) || (c[s] = M(c[s], e, t));
640
- }
641
- for (const s of Object.getOwnPropertySymbols(c)) {
642
- const n = Object.getOwnPropertyDescriptor(c, s);
643
- !n || !("value" in n) || (c[s] = M(c[s], e, t));
644
- }
645
- return Object.freeze(c);
617
+ function C(o, e = /* @__PURE__ */ new WeakSet(), t) {
618
+ if (o === null || typeof o != "object" || e.has(o) || (t !== void 0 && o === t.watch && t.onFound(), Object.isFrozen(o)) || (e.add(o), ArrayBuffer.isView(o))) return o;
619
+ if (Array.isArray(o)) {
620
+ const n = o;
621
+ for (let r = 0; r < n.length; r++)
622
+ n[r] = C(n[r], e, t);
623
+ return Object.freeze(n);
624
+ }
625
+ for (const n of Object.getOwnPropertyNames(o)) {
626
+ const r = Object.getOwnPropertyDescriptor(o, n);
627
+ !r || !("value" in r) || (o[n] = C(o[n], e, t));
628
+ }
629
+ for (const n of Object.getOwnPropertySymbols(o)) {
630
+ const r = Object.getOwnPropertyDescriptor(o, n);
631
+ !r || !("value" in r) || (o[n] = C(o[n], e, t));
632
+ }
633
+ return Object.freeze(o);
646
634
  }
647
- const N = /* @__PURE__ */ Symbol.for("yoltra.rejected");
648
- function le(c) {
649
- return { [N]: !0, reason: c };
635
+ const q = /* @__PURE__ */ Symbol.for("yoltra.rejected");
636
+ function Re(o) {
637
+ return { [q]: !0, reason: o };
650
638
  }
651
- function Q(c) {
652
- return typeof c == "object" && c !== null && c[N] === !0;
639
+ function Y(o) {
640
+ return typeof o == "object" && o !== null && o[q] === !0;
653
641
  }
654
- class G {
642
+ class Z extends Error {
643
+ channel;
644
+ type;
645
+ idleMs;
646
+ constructor(e, t, n) {
647
+ super(
648
+ `[yoltra] call to "${e}/${t}" saw no correlated reply for ${n}ms. The timeout is idle rather than total, so this means the responder went quiet, not that it was slow. Check that something handles "${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 = n;
650
+ }
651
+ }
652
+ class B extends Error {
653
+ constructor(e) {
654
+ super(`[yoltra] call aborted: ${e}`), this.name = "CallAbortedError";
655
+ }
656
+ }
657
+ function ee(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: (r) => r === t };
661
+ const n = new Set(t);
662
+ return { channel: e, isTerminal: (r) => n.has(r) };
663
+ }
664
+ function te(o, e, t) {
665
+ return o.parentId === e ? !0 : t === void 0 ? !1 : o.meta?.correlationId === t;
666
+ }
667
+ class ne {
655
668
  constructor(e) {
656
- p(this, "highWaterMark");
657
- p(this, "buffer", []);
658
- /** Consumers parked in `take`, oldest first. */
659
- p(this, "takers", []);
660
- /** Producers parked in `put`, each with the item they are waiting to hand over. */
661
- p(this, "putters", []);
662
- p(this, "consuming", !1);
663
- /** No more items will be accepted, but what is already here is still owed to the consumer. */
664
- p(this, "ended", !1);
665
- /** Abandoned: nothing further is owed to anybody. */
666
- p(this, "closed", !1);
667
- /** Items discarded because nobody was iterating and the buffer was full. */
668
- p(this, "dropped", 0);
669
669
  this.highWaterMark = e;
670
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;
671
684
  /** How many items were discarded for want of a consumer. */
672
685
  get droppedCount() {
673
686
  return this.dropped;
@@ -686,8 +699,8 @@ class G {
686
699
  put(e) {
687
700
  if (this.closed || this.ended) return Promise.resolve();
688
701
  const t = this.takers.shift();
689
- return t !== void 0 ? (t({ value: e, done: !1 }), Promise.resolve()) : this.buffer.length < this.highWaterMark ? (this.buffer.push(e), Promise.resolve()) : this.consuming ? new Promise((s) => {
690
- this.putters.push({ item: e, release: s });
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((n) => {
703
+ this.putters.push({ item: e, release: n });
691
704
  }) : (this.dropped++, Promise.resolve());
692
705
  }
693
706
  /** Takes the next item, waiting if none is available. Resolves `done` once closed and drained. */
@@ -695,12 +708,12 @@ class G {
695
708
  this.consuming = !0;
696
709
  const e = this.buffer.shift();
697
710
  if (e !== void 0) {
698
- const s = this.putters.shift();
699
- return s !== void 0 && (this.buffer.push(s.item), s.release()), Promise.resolve({ value: e, done: !1 });
711
+ const n = this.putters.shift();
712
+ return n !== void 0 && (this.buffer.push(n.item), n.release()), Promise.resolve({ value: e, done: !1 });
700
713
  }
701
714
  const t = this.putters.shift();
702
- return t !== void 0 ? (t.release(), Promise.resolve({ value: t.item, done: !1 })) : this.closed || this.ended ? Promise.resolve({ value: void 0, done: !0 }) : new Promise((s) => {
703
- this.takers.push(s);
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((n) => {
716
+ this.takers.push(n);
704
717
  });
705
718
  }
706
719
  /**
@@ -721,9 +734,9 @@ class G {
721
734
  this.buffer.push(e.item), e.release(), e = this.putters.shift();
722
735
  let t = this.takers.shift();
723
736
  for (; t !== void 0; ) {
724
- const s = this.buffer.shift();
737
+ const n = this.buffer.shift();
725
738
  t(
726
- s !== void 0 ? { value: s, done: !1 } : { value: void 0, done: !0 }
739
+ n !== void 0 ? { value: n, done: !1 } : { value: void 0, done: !0 }
727
740
  ), t = this.takers.shift();
728
741
  }
729
742
  }
@@ -748,513 +761,881 @@ class G {
748
761
  t.release(), t = this.putters.shift();
749
762
  }
750
763
  }
751
- class J extends Error {
752
- constructor(t, s, n) {
753
- super(
754
- `[yoltra] call to "${t}/${s}" saw no correlated reply for ${n}ms. The timeout is idle rather than total, so this means the responder went quiet, not that it was slow. Check that something handles "${t}/${s}" and that its reply is emitted through the \`emit\` it was handed — a reply emitted from an unrelated context carries no causal link, and needs an explicit correlationId instead.`
755
- );
756
- p(this, "channel");
757
- p(this, "type");
758
- p(this, "idleMs");
759
- this.name = "CallTimeoutError", this.channel = t, this.type = s, this.idleMs = n;
764
+ const re = 3e4, ie = 16;
765
+ function se(o, e, t, n, r) {
766
+ const { channel: i, isTerminal: s } = ee(r.reply), l = r.timeoutMs ?? re, c = new ne(r.highWaterMark ?? ie), a = o.idFactory();
767
+ let d, f, u = !1;
768
+ const p = new Promise((E, R) => {
769
+ d = E, f = R;
770
+ });
771
+ p.catch(() => {
772
+ });
773
+ let h = null, y = null;
774
+ const w = (E, R = !1) => {
775
+ u || (u = !0, h !== null && clearTimeout(h), h = null, y?.(), y = null, R ? c.end() : c.close(), r.signal?.removeEventListener("abort", g), E());
776
+ };
777
+ function g() {
778
+ w(() => f(new B(String(r.signal?.reason ?? "signal aborted"))));
779
+ }
780
+ const b = () => {
781
+ h !== null && clearTimeout(h), h = setTimeout(() => {
782
+ w(() => f(new Z(e, t, l)));
783
+ }, l), h.unref?.();
784
+ };
785
+ return y = 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: i },
789
+ effect: async (E) => {
790
+ if (!u && te(E, a, r.correlationId)) {
791
+ if (b(), s(String(E.type))) {
792
+ w(() => d(E), !0);
793
+ return;
794
+ }
795
+ await c.put(E);
796
+ }
797
+ }
798
+ }), r.signal !== void 0 && (r.signal.aborted ? g() : r.signal.addEventListener("abort", g, { once: !0 })), b(), o.emit(e, t, n, {
799
+ id: a,
800
+ ...r.correlationId !== void 0 ? { meta: { correlationId: r.correlationId } } : {}
801
+ }), {
802
+ then: (E, R) => p.then(E, R),
803
+ catch: (E) => p.catch(E),
804
+ finally: (E) => p.finally(E),
805
+ get dropped() {
806
+ return c.droppedCount;
807
+ },
808
+ cancel: (E = "cancelled") => {
809
+ w(() => f(new B(E)));
810
+ },
811
+ [Symbol.asyncIterator]: () => (c.beginConsuming(), {
812
+ next: () => c.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 () => (c.close(), { value: void 0, done: !0 })
816
+ })
817
+ };
818
+ }
819
+ function oe(o, e) {
820
+ if (!e) return o;
821
+ const n = (e[0] === "." ? e.slice(1) : e).split(".");
822
+ let r = o;
823
+ for (const i of n) {
824
+ if (r == null) return;
825
+ r = r[i];
760
826
  }
827
+ return r;
761
828
  }
762
- class C extends Error {
763
- constructor(e) {
764
- super(`[yoltra] call aborted: ${e}`), this.name = "CallAbortedError";
829
+ function ce(o) {
830
+ if (!o) return [];
831
+ const t = (o[0] === "." ? o.slice(1) : o).split("."), n = [];
832
+ for (let r = 0; r < t.length; r++)
833
+ n.push(t.slice(0, r + 1).join("."));
834
+ return n;
835
+ }
836
+ const m = "$yoltra", O = Object.freeze(
837
+ Object.assign(/* @__PURE__ */ Object.create(null), {
838
+ Int8Array,
839
+ Uint8Array,
840
+ Uint8ClampedArray,
841
+ Int16Array,
842
+ Uint16Array,
843
+ Int32Array,
844
+ Uint32Array,
845
+ Float32Array,
846
+ Float64Array,
847
+ BigInt64Array,
848
+ BigUint64Array
849
+ })
850
+ );
851
+ function ae(o) {
852
+ const e = o.constructor?.name;
853
+ if (e !== void 0 && Object.prototype.hasOwnProperty.call(O, e))
854
+ return e;
855
+ if (o instanceof DataView) return "DataView";
856
+ for (const t of Object.keys(O)) {
857
+ const n = O[t];
858
+ if (n !== void 0 && o instanceof n) return t;
765
859
  }
766
860
  }
767
- function q(c) {
768
- const [e, t] = c;
769
- if (t === void 0) return { channel: e, isTerminal: () => !0 };
770
- if (typeof t == "string") return { channel: e, isTerminal: (n) => n === t };
771
- const s = new Set(t);
772
- return { channel: e, isTerminal: (n) => s.has(n) };
861
+ function N(o) {
862
+ const e = globalThis.Buffer;
863
+ if (e !== void 0) return e.from(o).toString("base64");
864
+ const t = 8192;
865
+ let n = "";
866
+ for (let r = 0; r < o.length; r += t)
867
+ n += String.fromCharCode(...o.subarray(r, r + t));
868
+ return btoa(n);
869
+ }
870
+ function de(o) {
871
+ const e = globalThis.Buffer;
872
+ if (e !== void 0) {
873
+ const r = e.from(o, "base64");
874
+ return new Uint8Array(r.subarray(0, r.length));
875
+ }
876
+ const t = atob(o), n = new Uint8Array(t.length);
877
+ for (let r = 0; r < t.length; r += 1) n[r] = t.charCodeAt(r);
878
+ return n;
879
+ }
880
+ function j(o, e = {}) {
881
+ const t = e.maxNodes ?? 1e5, n = e.sanitize, r = [], i = /* @__PURE__ */ new Map();
882
+ let s = 0, l = !1;
883
+ function c(d, f) {
884
+ if (n !== void 0 && (d = n(f, d)), s += 1, s > t)
885
+ return l = !0, { [m]: "unsupported", kind: "truncated" };
886
+ switch (typeof d) {
887
+ case "undefined":
888
+ return { [m]: "undefined" };
889
+ case "bigint":
890
+ return { [m]: "bigint", value: d.toString() };
891
+ case "number":
892
+ return Number.isNaN(d) ? { [m]: "nan" } : d === 1 / 0 ? { [m]: "infinity", sign: 1 } : d === -1 / 0 ? { [m]: "infinity", sign: -1 } : d;
893
+ case "function":
894
+ case "symbol":
895
+ return r.push(f), { [m]: "unsupported", kind: typeof d };
896
+ case "string":
897
+ case "boolean":
898
+ return d;
899
+ }
900
+ if (d === null) return null;
901
+ const u = d, p = i.get(u);
902
+ if (p !== void 0) return { [m]: "ref", path: p };
903
+ if (i.set(u, f), d instanceof Date)
904
+ return { [m]: "date", iso: d.toISOString() };
905
+ if (d instanceof RegExp)
906
+ return { [m]: "regexp", source: d.source, flags: d.flags };
907
+ if (d instanceof Error)
908
+ return { [m]: "error", name: d.name, message: d.message };
909
+ if (d instanceof Map) {
910
+ const g = [];
911
+ let b = 0;
912
+ for (const [v, E] of d)
913
+ g.push([c(v, `${f}/@k${b}`), c(E, `${f}/${b}`)]), b += 1;
914
+ return { [m]: "map", entries: g };
915
+ }
916
+ if (d instanceof Set) {
917
+ const g = [];
918
+ let b = 0;
919
+ for (const v of d)
920
+ g.push(c(v, `${f}/${b}`)), b += 1;
921
+ return { [m]: "set", values: g };
922
+ }
923
+ if (Array.isArray(d))
924
+ return d.map((g, b) => c(g, `${f}/${b}`));
925
+ if (d instanceof ArrayBuffer)
926
+ return s += Math.ceil(d.byteLength / 64), s > t ? (l = !0, { [m]: "unsupported", kind: "truncated" }) : {
927
+ [m]: "binary",
928
+ kind: "ArrayBuffer",
929
+ b64: N(new Uint8Array(d))
930
+ };
931
+ if (ArrayBuffer.isView(d)) {
932
+ const g = d;
933
+ s += Math.ceil(g.byteLength / 64);
934
+ const b = ae(g);
935
+ if (b === void 0)
936
+ return r.push(f), {
937
+ [m]: "unsupported",
938
+ kind: g.constructor?.name ?? "ArrayBufferView"
939
+ };
940
+ const v = new Uint8Array(g.buffer, g.byteOffset, g.byteLength), E = { [m]: "binary", kind: b, b64: N(v) };
941
+ return g.constructor?.name !== b && r.push(f), E;
942
+ }
943
+ const h = Object.getPrototypeOf(d), y = d.constructor?.name;
944
+ if (h !== null && h !== Object.prototype && y !== "Object") {
945
+ r.push(f);
946
+ const g = {};
947
+ for (const [b, v] of Object.entries(d))
948
+ g[b] = c(v, `${f}/${D(b)}`);
949
+ return {
950
+ [m]: "unsupported",
951
+ kind: y ?? "unknown",
952
+ value: g
953
+ };
954
+ }
955
+ const w = {};
956
+ for (const [g, b] of Object.entries(d))
957
+ w[g] = c(b, `${f}/${D(g)}`);
958
+ return m in w ? { [m]: "escaped", value: w } : w;
959
+ }
960
+ return { value: c(o, ""), report: { truncated: l, unsupported: r } };
961
+ }
962
+ function le(o) {
963
+ const e = /* @__PURE__ */ new Map(), t = [];
964
+ function n(s, l) {
965
+ if (s === null || typeof s != "object") return s;
966
+ if (Array.isArray(s)) {
967
+ const a = [];
968
+ return e.set(l, a), s.forEach((d, f) => {
969
+ if (z(d)) {
970
+ t.push({ target: a, key: f, path: d.path }), a[f] = void 0;
971
+ return;
972
+ }
973
+ a[f] = n(d, `${l}/${f}`);
974
+ }), a;
975
+ }
976
+ if (typeof s[m] == "string") {
977
+ const a = s;
978
+ switch (a[m]) {
979
+ case "undefined":
980
+ return;
981
+ case "nan":
982
+ return Number.NaN;
983
+ case "infinity":
984
+ return a.sign === 1 ? 1 / 0 : -1 / 0;
985
+ case "bigint":
986
+ return BigInt(a.value);
987
+ case "date":
988
+ return new Date(a.iso);
989
+ case "regexp":
990
+ return new RegExp(a.source, a.flags);
991
+ case "error": {
992
+ const d = new Error(a.message);
993
+ return d.name = a.name, d;
994
+ }
995
+ case "binary": {
996
+ const d = de(a.b64);
997
+ if (a.kind === "ArrayBuffer") {
998
+ const p = d.slice();
999
+ return e.set(l, p.buffer), p.buffer;
1000
+ }
1001
+ if (a.kind === "DataView") {
1002
+ const p = d.slice(), h = new DataView(p.buffer);
1003
+ return e.set(l, h), h;
1004
+ }
1005
+ const f = Object.prototype.hasOwnProperty.call(O, a.kind) ? O[a.kind] : void 0;
1006
+ if (f === void 0) return;
1007
+ const u = new f(d.slice().buffer);
1008
+ return e.set(l, u), u;
1009
+ }
1010
+ case "unsupported":
1011
+ return a.value !== void 0 ? r(a.value, l) : void 0;
1012
+ case "ref":
1013
+ return;
1014
+ case "map": {
1015
+ const d = /* @__PURE__ */ new Map();
1016
+ return e.set(l, d), a.entries.forEach(([f, u], p) => {
1017
+ d.set(n(f, `${l}/@k${p}`), n(u, `${l}/${p}`));
1018
+ }), d;
1019
+ }
1020
+ case "set": {
1021
+ const d = /* @__PURE__ */ new Set();
1022
+ return e.set(l, d), a.values.forEach((f, u) => d.add(n(f, `${l}/${u}`))), d;
1023
+ }
1024
+ case "escaped":
1025
+ return r(a.value, l);
1026
+ default:
1027
+ return;
1028
+ }
1029
+ }
1030
+ return r(s, l);
1031
+ }
1032
+ function r(s, l) {
1033
+ const c = {};
1034
+ e.set(l, c);
1035
+ for (const [a, d] of Object.entries(s)) {
1036
+ const f = `${l}/${D(a)}`;
1037
+ if (z(d)) {
1038
+ t.push({ target: c, key: a, path: d.path }), c[a] = void 0;
1039
+ continue;
1040
+ }
1041
+ c[a] = n(d, f);
1042
+ }
1043
+ return c;
1044
+ }
1045
+ const i = n(o, "");
1046
+ e.set("", i);
1047
+ for (const { target: s, key: l, path: c } of t)
1048
+ s[l] = e.get(c);
1049
+ return i;
1050
+ }
1051
+ function z(o) {
1052
+ return o !== null && typeof o == "object" && o[m] === "ref" && typeof o.path == "string";
1053
+ }
1054
+ function D(o) {
1055
+ return o.replace(/~/g, "~0").replace(/\//g, "~1");
1056
+ }
1057
+ function Oe(o, e, t = {}) {
1058
+ let n = t.maxNodes ?? 1e5;
1059
+ for (let r = 0; r < 8; r += 1) {
1060
+ const { value: i, report: s } = j(o, { ...t, maxNodes: n });
1061
+ let l;
1062
+ try {
1063
+ l = JSON.stringify(i)?.length ?? 0;
1064
+ } catch {
1065
+ l = Number.POSITIVE_INFINITY;
1066
+ }
1067
+ if (l <= e)
1068
+ return s.truncated ? {
1069
+ value: i,
1070
+ truncated: !0,
1071
+ note: `State was too large to send in full; parts beyond ${n} nodes are omitted.`
1072
+ } : { value: i, truncated: !1 };
1073
+ const c = Math.floor(n * e * 0.8 / l);
1074
+ if (n = Math.max(1, Math.min(c, n - 1)), n <= 1 && r > 0)
1075
+ break;
1076
+ }
1077
+ return {
1078
+ value: { [m]: "unsupported", kind: "truncated" },
1079
+ truncated: !0,
1080
+ note: `State exceeds the ${e}-byte transport limit and could not be reduced to fit.`
1081
+ };
1082
+ }
1083
+ const fe = 1e4;
1084
+ function x(o) {
1085
+ return o === null || typeof o != "object" ? JSON.stringify(o) ?? "null" : Array.isArray(o) ? `[${o.map(x).join(",")}]` : `{${Object.entries(o).sort(
1086
+ ([t], [n]) => t < n ? -1 : t > n ? 1 : 0
1087
+ ).map(([t, n]) => `${JSON.stringify(t)}:${x(n)}`).join(",")}}`;
1088
+ }
1089
+ function ue(o, e, t) {
1090
+ const n = `${o}::${e}`;
1091
+ if (t === null) return `${n}::null`;
1092
+ if (t === void 0) return `${n}::undefined`;
1093
+ if (typeof t != "object") return `${n}::${typeof t}:${String(t)}`;
1094
+ try {
1095
+ const { value: r, report: i } = j(t, { maxNodes: fe });
1096
+ return i.truncated ? `${n}::${Date.now()}::${Math.random()}` : `${n}::${x(r)}`;
1097
+ } catch {
1098
+ return `${n}::${Date.now()}::${Math.random()}`;
1099
+ }
1100
+ }
1101
+ function $(o, e) {
1102
+ return !o || "any" in o && o.any === !0 ? !0 : "keys" in o ? o.keys.some(
1103
+ ([t, n]) => e.channel === t && e.type === n
1104
+ ) : "channel" in o ? e.channel === o.channel : "channels" in o ? o.channels.includes(e.channel) : !1;
1105
+ }
1106
+ function he(o) {
1107
+ return typeof o == "function" ? o : o.middleware;
773
1108
  }
774
- function Y(c, e, t) {
775
- return c.parentId === e ? !0 : t === void 0 ? !1 : c.meta?.correlationId === t;
1109
+ function A(o) {
1110
+ if (typeof o != "function")
1111
+ return o.when;
776
1112
  }
777
- function X(c, e) {
1113
+ function U(o) {
1114
+ if (o.when) {
1115
+ const e = o.when;
1116
+ if ("keys" in e)
1117
+ return e.keys;
1118
+ }
1119
+ return [];
1120
+ }
1121
+ function pe(o, e) {
778
1122
  try {
779
1123
  return structuredClone(e);
780
1124
  } catch (t) {
781
1125
  throw new Error(
782
- `[yoltra] Initial state for slice "${String(c)}" could not be copied: ${t instanceof Error ? t.message : String(t)}. State must be structured-cloneable — functions, class instances and DOM nodes are not. Keep behaviour out of state and store plain data.`
1126
+ `[yoltra] Initial state for slice "${String(o)}" could not be copied: ${t instanceof Error ? t.message : String(t)}. State must be structured-cloneable — functions, class instances and DOM nodes are not. Keep behaviour out of state and store plain data.`
783
1127
  );
784
1128
  }
785
1129
  }
786
- function R(c, e) {
787
- return process.env.NODE_ENV === "production" ? c : M(c, /* @__PURE__ */ new WeakSet(), e);
1130
+ function T(o, e) {
1131
+ return process.env.NODE_ENV === "production" ? o : C(o, /* @__PURE__ */ new WeakSet(), e);
788
1132
  }
789
- const D = 100, Z = 64, z = 16, ee = 3e4, te = 16, $ = Object.freeze({ committed: !1, written: !1 }), ne = Object.freeze({ committed: !0, written: !1 }), se = Object.freeze({ committed: !0, written: !0 }), A = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
790
- class x {
791
- /**
792
- * Creates a store from a {@link StoreSpec}.
793
- *
794
- * @param spec - Store configuration (name, reducers, middleware, optional effects).
1133
+ const H = 100, V = 64, ge = 64, ye = 64, W = 16, me = Object.freeze({ committed: !1, written: !1 }), we = Object.freeze({
1134
+ committed: !1,
1135
+ written: !1,
1136
+ reason: "deduped"
1137
+ }), F = Object.freeze({
1138
+ committed: !1,
1139
+ written: !1,
1140
+ reason: "cascade"
1141
+ }), be = Object.freeze({ committed: !0, written: !1 }), Ee = Object.freeze({ committed: !0, written: !0 }), K = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
1142
+ class k {
1143
+ /**
1144
+ * Store name (used by DevTools & diagnostics).
795
1145
  *
796
1146
  * @public
797
1147
  */
798
- constructor(e) {
799
- /**
800
- * Store name (used by DevTools & diagnostics).
801
- *
802
- * @public
803
- */
804
- p(this, "name");
805
- /**
806
- * Registered middleware pipeline (run **before** reducers).
807
- * Stores either raw functions (legacy) or MiddlewareSpec objects.
808
- * Return `false` from the middleware function to stop propagation.
809
- *
810
- * @internal
811
- */
812
- p(this, "middleware");
813
- /**
814
- * Installed slice reducers keyed by slice name.
815
- *
816
- * @internal
817
- */
818
- p(this, "reducers");
819
- /**
820
- * Current immutable snapshot of the store state.
821
- * This reference changes whenever any slice changes (shallow immutability).
822
- *
823
- * @internal
824
- */
825
- p(this, "state");
826
- /**
827
- * Bus for reducer wiring (emit by `(channel, type)`).
828
- *
829
- * @internal
830
- */
831
- p(this, "reducerBus");
832
- /**
833
- * Bus for **granular** connector events (emit by **dotted path** inside a slice).
834
- *
835
- * @internal
836
- */
837
- p(this, "connectorBus");
838
- /**
839
- * Coarse-grained listeners (called once per committed event, only if state changed).
840
- *
841
- * @internal
842
- */
843
- p(this, "listeners", /* @__PURE__ */ new Set());
844
- /**
845
- * Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
846
- * Used for effects with explicit `keys` targeting.
847
- *
848
- * @internal
849
- */
850
- p(this, "effects", /* @__PURE__ */ new Map());
851
- /**
852
- * Pattern-based effects that need runtime matching.
853
- * Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
854
- * Stores tuples of [effect function, when matcher].
855
- *
856
- * @internal
857
- */
858
- p(this, "patternEffects", /* @__PURE__ */ new Set());
859
- /**
860
- * Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
861
- * Notified after reducers, before effects, for events that passed middleware.
862
- *
863
- * @internal
864
- */
865
- p(this, "committedEventSubscribers", /* @__PURE__ */ new Map());
866
- /**
867
- * Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
868
- * Notified when middleware rejects an event.
869
- *
870
- * @internal
871
- */
872
- p(this, "uncommittedEventSubscribers", /* @__PURE__ */ new Map());
873
- /**
874
- * All-events subscribers keyed by `"channel::type"` for O(1) lookup.
875
- * Notified for both committed and uncommitted events with phase parameter.
876
- *
877
- * @internal
878
- */
879
- /**
880
- * Subscribers to events that actually changed state, notified after the commit.
881
- *
882
- * @remarks
883
- * Separate from `committedEventSubscribers` rather than a filter over it, because the two
884
- * answer different questions and one of them is load bearing: `committed` means "not vetoed"
885
- * and fires for every event a store accepts, including every event in a store with no
886
- * reducers. Narrowing it would have silently stopped toasts and analytics firing.
887
- *
888
- * @internal
889
- */
890
- p(this, "writtenEventSubscribers", /* @__PURE__ */ new Map());
891
- p(this, "allEventSubscribers", /* @__PURE__ */ new Map());
892
- /**
893
- * Track reducerBus unsubs per slice for HMR/register/unregister.
894
- *
895
- * @internal
896
- */
897
- p(this, "sliceUnsubs", /* @__PURE__ */ new Map());
898
- /**
899
- * Pattern-based reducers that need runtime matching.
900
- * Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
901
- * Maps slice name to the `when` matcher.
902
- *
903
- * @internal
904
- */
905
- p(this, "patternReducers", /* @__PURE__ */ new Map());
906
- /**
907
- * Whether `__replayEvents()` is allowed.
908
- * Set from `spec.devtools.allowReplay`.
909
- *
910
- * @internal
911
- */
912
- p(this, "replayEnabled");
913
- /**
914
- * Produces the `id` for each emitted event. Defaults to `crypto.randomUUID()`; overridable
915
- * via {@link StoreSpec.idFactory} for runtimes lacking it or for deterministic tests.
916
- *
917
- * @internal
918
- */
919
- p(this, "idFactory");
920
- /**
921
- * Optional hook invoked when an effect throws/rejects. See
922
- * {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
923
- * failure — this is how callers observe effect errors.
924
- */
925
- p(this, "onEffectError");
926
- /**
927
- * Optional hook invoked when a reducer throws. See {@link StoreSpec.onReducerError}. The
928
- * failing slice is isolated rather than the event being rolled back, so this is the only
929
- * signal that a reducer misbehaved.
930
- */
931
- p(this, "onReducerError");
932
- /**
933
- * `slice:channel:type` combinations already warned about for payload aliasing.
934
- *
935
- * @remarks
936
- * Development-only diagnostics have to stay quiet enough to be read. One warning names the
937
- * pattern; repeating it once per event would bury it.
938
- */
939
- p(this, "warnedPayloadAliases", /* @__PURE__ */ new Set());
940
- /**
941
- * Pending events awaiting the **synchronous** reduce phase (middleware +
942
- * reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
943
- *
944
- * @internal
945
- */
946
- p(this, "reduceQueue", []);
947
- /**
948
- * Re-entrancy guard for the synchronous reduce phase.
949
- *
950
- * @internal
951
- */
952
- p(this, "isReducing", !1);
953
- /**
954
- * The event currently being reduced, or `null` outside the drain.
955
- *
956
- * @remarks
957
- * This is what makes causality exact rather than best-effort. The drain is synchronous — no
958
- * `await` can interleave — so any `emit` that arrives while it is set is, without ambiguity, a
959
- * consequence of this event. That catches the case a scoped `emit` closure cannot: a
960
- * middleware or subscriber that captured the store and calls `store.emit` directly instead of
961
- * using the injected one. Attribution should not depend on which reference a consumer reached
962
- * for.
963
- *
964
- * @internal
965
- */
966
- p(this, "currentEvent", null);
967
- /**
968
- * Events processed by the drain currently in progress. Compared against
969
- * `maxTransitionsPerDrain`, which is off unless configured.
970
- *
971
- * @internal
972
- */
973
- p(this, "transitionsThisDrain", 0);
974
- /**
975
- * Ceilings that stop a cascade from becoming a hung process. See {@link StoreSpec.maxReduceDepth}.
976
- *
977
- * @internal
978
- */
979
- p(this, "maxReduceDepth");
980
- p(this, "maxTransitionsPerDrain");
981
- p(this, "onCascade");
982
- p(this, "onRejected");
983
- /**
984
- * Registered instrumentation observers (DevTools seam). See {@link instrument}.
985
- *
986
- * @internal
987
- */
988
- p(this, "instrumentObservers", /* @__PURE__ */ new Set());
989
- /**
990
- * Scratch array collecting slice-prefixed changed leaf paths during an
991
- * instrumented reduce. Set by {@link drainReduce} while observers are active;
992
- * appended to by {@link commitStaged}. `null` when not instrumenting.
993
- *
994
- * @internal
995
- */
996
- p(this, "changedPathSink", null);
997
- /**
998
- * Where keyed reducers put their pending writes during a reduce, and the refusal one of them
999
- * returned.
1000
- *
1001
- * @remarks
1002
- * Keyed reducers are invoked through `reducerBus`, which delivers to handlers and has no way
1003
- * to hand a value back — the same reason `changedPathSink` exists. `null` outside a reduce.
1004
- *
1005
- * @internal
1006
- */
1007
- p(this, "stagingSink", null);
1008
- p(this, "stagedRejection", null);
1009
- p(this, "stagedRejectedBy", "");
1010
- /**
1011
- * Count of effect tasks currently in flight; surfaced as queue depth by
1012
- * {@link __devtoolsIntrospect}.
1013
- *
1014
- * @internal
1015
- */
1016
- p(this, "inFlightEffects", 0);
1017
- /**
1018
- * Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
1019
- *
1020
- * **Deduplication Behavior:**
1021
- * - Events are fingerprinted using `channel::type::JSON(payload)`
1022
- * - If an identical fingerprint is seen within the dedup window, it's skipped
1023
- * - The window is 50ms in development, 100ms in production
1024
- *
1025
- * **Limitations:**
1026
- * - Non-serializable payloads (functions, symbols, circular refs) get unique
1027
- * fingerprints and won't be deduplicated
1028
- * - Legitimate rapid-fire identical events may be incorrectly deduplicated
1029
- * - The cache is bounded to 1000 entries with lazy pruning
1030
- *
1031
- * @internal
1032
- */
1033
- p(this, "processedEvents", /* @__PURE__ */ new Map());
1034
- /**
1035
- * Lifetime count of events suppressed by the deduplication cache.
1036
- * Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
1037
- * surface it in the STORE_METRICS response without further core changes.
1038
- *
1039
- * @internal
1040
- */
1041
- p(this, "dedupCount", 0);
1042
- /**
1043
- * Store-owned metadata for registered effects, keyed by the effect function.
1044
- * Kept **off** the caller's function object: mutating a user-owned function
1045
- * (the old `fn.__quoMeta`) bled metadata across stores that share a handler
1046
- * and left it attached after unregister. Cleared on {@link dispose}.
1047
- *
1048
- * @internal
1049
- */
1050
- p(this, "effectMeta", /* @__PURE__ */ new WeakMap());
1051
- /**
1052
- * Configuration for event deduplication.
1053
- * @internal
1054
- */
1055
- p(this, "dedupConfig");
1056
- /**
1057
- * Timer for periodic cleanup of processed events.
1058
- *
1059
- * @internal
1060
- */
1061
- p(this, "eventCleanupTimer", null);
1062
- if (this.name = e.name ?? "yoltra Store", this.reducerBus = new U(), this.connectorBus = new L(), this.middleware = [...e.middleware ?? []], this.reducers = {}, this.state = {}, this.replayEnabled = e.devtools?.allowReplay ?? !1, this.idFactory = e.idFactory ?? (() => crypto.randomUUID()), this.onEffectError = e.onEffectError, this.onReducerError = e.onReducerError, this.maxReduceDepth = e.maxReduceDepth ?? Z, this.maxTransitionsPerDrain = e.maxTransitionsPerDrain ?? 1 / 0, this.onCascade = e.onCascade, this.onRejected = e.onRejected, this.dedupConfig = {
1063
- windowMs: e.dedupWindowMs ?? 0,
1064
- maxCacheSize: 1e3
1065
- }, Object.entries(e.reducer).forEach(([t, s]) => {
1066
- this.mountSlice(t, s, { preserveState: !1 });
1067
- }), e.effects?.length)
1068
- for (const t of e.effects)
1069
- this.registerEffect(t);
1070
- this.dispose = this.dispose.bind(this), this.notifyEffects = this.notifyEffects.bind(this), this.__applyExternalState = this.__applyExternalState.bind(this), this.__replayEvents = this.__replayEvents.bind(this), this.__devtoolsIntrospect = this.__devtoolsIntrospect.bind(this), this.mountSlice = this.mountSlice.bind(this), this.unmountSlice = this.unmountSlice.bind(this), this.getAtPath = this.getAtPath.bind(this), this.emit = this.emit.bind(this), this.subscribe = this.subscribe.bind(this), this.connect = this.connect.bind(this), this.onEffect = this.onEffect.bind(this), this.onEvent = this.onEvent.bind(this), this.getState = this.getState.bind(this), this.registerEffect = this.registerEffect.bind(this), this.registerMiddleware = this.registerMiddleware.bind(this), this.registerReducer = this.registerReducer.bind(this), this.replaceMiddleware = this.replaceMiddleware.bind(this), this.replaceEffects = this.replaceEffects.bind(this), this.replaceReducers = this.replaceReducers.bind(this), this.hotReplace = this.hotReplace.bind(this);
1071
- }
1148
+ name;
1072
1149
  /**
1073
- * Cleanup resources (timers, etc.) when disposing the store.
1074
- * Call this if you're dynamically creating/destroying stores.
1150
+ * Registered middleware pipeline (run **before** reducers).
1151
+ * Stores either raw functions (legacy) or MiddlewareSpec objects.
1075
1152
  *
1076
- * @example
1077
- * ```ts
1078
- * const store = createStore({ ... });
1079
- * // later
1080
- * store.dispose();
1081
- * ```
1153
+ * Only an explicit `false` stops propagation. Returning nothing allows the event, so
1154
+ * middleware that only logs or measures needs no `return` at all.
1082
1155
  *
1083
- * @public
1156
+ * @internal
1084
1157
  */
1085
- dispose() {
1086
- this.eventCleanupTimer && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null), this.processedEvents.clear(), this.effects.clear(), this.patternEffects.clear(), this.effectMeta = /* @__PURE__ */ new WeakMap(), this.warnedPayloadAliases.clear(), this.listeners.clear(), this.committedEventSubscribers.clear(), this.uncommittedEventSubscribers.clear(), this.writtenEventSubscribers.clear(), this.allEventSubscribers.clear(), this.instrumentObservers.clear(), this.connectorBus.clear(), this.reducerBus.clear(), this.patternReducers.clear(), this.sliceUnsubs.clear(), this.changedPathSink = null;
1087
- }
1158
+ middleware;
1088
1159
  /**
1089
- * Generates a fingerprint for an event for deduplication purposes.
1090
- * Falls back gracefully for non-serializable payloads.
1160
+ * Installed slice reducers keyed by slice name.
1091
1161
  *
1092
- * @param channel - Event channel.
1093
- * @param type - Event type.
1094
- * @param payload - Event payload.
1095
- * @returns A string fingerprint for the event.
1162
+ * @internal
1163
+ */
1164
+ reducers;
1165
+ /**
1166
+ * Current immutable snapshot of the store state.
1167
+ * This reference changes whenever any slice changes (shallow immutability).
1096
1168
  *
1097
1169
  * @internal
1098
1170
  */
1099
- fingerprint(e, t, s) {
1100
- const n = `${e}::${t}`;
1101
- try {
1102
- if (s == null)
1103
- return `${n}::null`;
1104
- if (typeof s != "object")
1105
- return `${n}::${String(s)}`;
1106
- const r = JSON.stringify(s);
1107
- return `${n}::${r}`;
1108
- } catch {
1109
- return `${n}::${Date.now()}::${Math.random()}`;
1110
- }
1111
- }
1171
+ state;
1112
1172
  /**
1113
- * Checks if an event should be deduplicated.
1114
- * Returns true if this is a duplicate that should be skipped.
1173
+ * Bus for reducer wiring (emit by `(channel, type)`).
1115
1174
  *
1116
- * @param fp - Event fingerprint.
1117
- * @returns `true` if duplicate (should skip), `false` otherwise.
1175
+ * @internal
1176
+ */
1177
+ reducerBus;
1178
+ /**
1179
+ * Bus for **granular** connector events (emit by **dotted path** inside a slice).
1118
1180
  *
1119
1181
  * @internal
1120
1182
  */
1121
- shouldDedupe(e, t) {
1122
- const s = Date.now(), n = this.processedEvents.get(e);
1123
- return n !== void 0 && s - n < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, s), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(s), !1);
1124
- }
1183
+ connectorBus;
1125
1184
  /**
1126
- * Starts the periodic prune interval if it isn't already running. Called when
1127
- * the first entry is cached so the timer's lifetime tracks actual dedup use
1128
- * (content window or identity `dedupKey`), independent of `dedupWindowMs`.
1185
+ * Coarse-grained listeners (called once per committed event, only if state changed).
1129
1186
  *
1130
1187
  * @internal
1131
1188
  */
1132
- ensureCleanupTimer() {
1133
- this.eventCleanupTimer === null && (this.eventCleanupTimer = setInterval(() => {
1134
- this.pruneProcessedEvents(Date.now());
1135
- }, 5e3), this.eventCleanupTimer.unref?.());
1136
- }
1189
+ listeners = /* @__PURE__ */ new Set();
1137
1190
  /**
1138
- * Removes expired entries from the processed events cache.
1191
+ * Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
1192
+ * Used for effects with explicit `keys` targeting.
1139
1193
  *
1140
- * @param now - Current timestamp.
1194
+ * @internal
1195
+ */
1196
+ effects = /* @__PURE__ */ new Map();
1197
+ /**
1198
+ * Pattern-based effects that need runtime matching.
1199
+ * Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
1200
+ * Stores tuples of [effect function, when matcher].
1141
1201
  *
1142
1202
  * @internal
1143
1203
  */
1144
- pruneProcessedEvents(e) {
1145
- const t = Math.max(this.dedupConfig.windowMs, D), s = e - t * 2;
1146
- for (const [n, r] of this.processedEvents)
1147
- r < s && this.processedEvents.delete(n);
1148
- this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
1149
- }
1204
+ patternEffects = /* @__PURE__ */ new Set();
1150
1205
  /**
1151
- * Reports a breached ceiling and refuses the emit.
1206
+ * Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
1207
+ * Notified after reducers, before effects, for events that passed middleware.
1152
1208
  *
1153
- * @remarks
1154
- * Console *and* hook, matching how reducer and effect errors are reported: a cascade is a
1155
- * wiring bug, and the console line is what a developer who has not registered a hook will
1156
- * actually see. Without one, refusing the emit would look exactly like the event never having
1157
- * been emitted at all — which is the invisibility this whole guard exists to end.
1209
+ * @internal
1210
+ */
1211
+ committedEventSubscribers = /* @__PURE__ */ new Map();
1212
+ /**
1213
+ * Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
1214
+ * Notified when middleware rejects an event.
1158
1215
  *
1159
1216
  * @internal
1160
1217
  */
1161
- reportCascade(e, t, s, n, r) {
1162
- console.error(
1163
- `[yoltra] Cascade stopped: "${s.channel}/${s.type}" would exceed ${e} (${t}). This event was refused and the chain ends here. A chain this long is almost always two consumers emitting into each other — check what reacts to "${s.channel}/${s.type}" and what that emits in turn.` + (r.length > 0 ? ` Recent causal chain: ${r.join(" → ")} → (refused).` : "")
1164
- );
1165
- try {
1166
- this.onCascade?.({ limit: e, limitValue: t, event: s, depth: n, chain: r });
1167
- } catch (o) {
1168
- console.error("onCascade handler error:", o);
1169
- }
1170
- }
1218
+ uncommittedEventSubscribers = /* @__PURE__ */ new Map();
1171
1219
  /**
1172
- * Checks if an event matches a `When` matcher.
1220
+ * All-events subscribers keyed by `"channel::type"` for O(1) lookup.
1221
+ * Notified for both committed and uncommitted events with phase parameter.
1173
1222
  *
1174
- * @param when - The When matcher (or undefined for "all events").
1175
- * @param event - The event to check.
1176
- * @returns `true` if the event matches, `false` otherwise.
1223
+ * @internal
1224
+ */
1225
+ /**
1226
+ * Subscribers to events that actually changed state, notified after the commit.
1177
1227
  *
1178
1228
  * @remarks
1179
- * - `undefined` or missing `when` matches ALL events.
1180
- * - `{ any: true }` matches ALL events.
1181
- * - `{ keys: [...] }` matches if event's `[channel, type]` is in the array.
1182
- * - `{ channel: 'x' }` matches if event's channel equals 'x'.
1183
- * - `{ channels: ['x', 'y'] }` matches if event's channel is in the array.
1229
+ * Separate from `committedEventSubscribers` rather than a filter over it, because the two
1230
+ * answer different questions and one of them is load bearing: `committed` means "not vetoed"
1231
+ * and fires for every event a store accepts, including every event in a store with no
1232
+ * reducers. Narrowing it would have silently stopped toasts and analytics firing.
1184
1233
  *
1185
1234
  * @internal
1186
1235
  */
1187
- matchesWhen(e, t) {
1188
- return !e || "any" in e && e.any === !0 ? !0 : "keys" in e ? e.keys.some(
1189
- ([s, n]) => t.channel === s && t.type === n
1190
- ) : "channel" in e ? t.channel === e.channel : "channels" in e ? e.channels.includes(t.channel) : !1;
1191
- }
1236
+ writtenEventSubscribers = /* @__PURE__ */ new Map();
1237
+ allEventSubscribers = /* @__PURE__ */ new Map();
1192
1238
  /**
1193
- * Extracts the middleware function from a MiddlewareInput.
1194
- * Handles both raw functions (legacy) and MiddlewareSpec objects.
1239
+ * True while a devtools time-travel is applying a snapshot or replaying events.
1195
1240
  *
1196
- * @param input - MiddlewareInput (function or spec).
1197
- * @returns The middleware function.
1241
+ * @remarks
1242
+ * Saved and restored rather than set and cleared to `false`: `__replayEvents` calls
1243
+ * `__applyExternalState` as its first step, so clearing on the inner call's way out would
1244
+ * unset the flag for the entire event loop that follows it.
1198
1245
  *
1199
1246
  * @internal
1200
1247
  */
1201
- getMiddlewareFunction(e) {
1202
- return typeof e == "function" ? e : e.middleware;
1203
- }
1248
+ replaying = !1;
1204
1249
  /**
1205
- * Gets the `when` matcher from a MiddlewareInput.
1206
- *
1207
- * @param input - MiddlewareInput (function or spec).
1208
- * @returns The `when` matcher, or `undefined` for raw functions (match all).
1250
+ * Track reducerBus unsubs per slice for HMR/register/unregister.
1209
1251
  *
1210
1252
  * @internal
1211
1253
  */
1212
- getMiddlewareWhen(e) {
1213
- if (typeof e != "function")
1214
- return e.when;
1215
- }
1254
+ sliceUnsubs = /* @__PURE__ */ new Map();
1216
1255
  /**
1217
- * Invokes all registered **effects** for a given event.
1218
- * Handles both key-based effects (O(1) lookup) and pattern-based effects (runtime matching).
1219
- * Errors are caught and logged.
1256
+ * Pattern-based reducers that need runtime matching.
1257
+ * Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
1258
+ * Maps slice name to the `when` matcher.
1220
1259
  *
1221
- * @param event - The event that was reduced.
1222
1260
  * @internal
1223
1261
  */
1224
- async notifyEffects(e) {
1225
- const t = this.scopedEmit(e), s = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(s);
1226
- if (n && n.size > 0)
1227
- for (const r of [...n])
1228
- try {
1229
- await r(e, this.getState, t);
1230
- } catch (o) {
1231
- console.error("Effect error:", o), this.onEffectError?.(o, e);
1232
- }
1233
- for (const { effect: r, when: o } of this.patternEffects)
1234
- if (this.matchesWhen(o, e))
1235
- try {
1236
- await r(e, this.getState, t);
1237
- } catch (l) {
1238
- console.error("Effect error:", l), this.onEffectError?.(l, e);
1239
- }
1240
- }
1262
+ patternReducers = /* @__PURE__ */ new Map();
1241
1263
  /**
1242
- * An `emit` that attributes whatever it sends to `cause`.
1264
+ * Where each mounted slice came from. See {@link Origin}.
1243
1265
  *
1244
1266
  * @remarks
1245
- * Built per event rather than per effect: every effect reacting to one event shares a cause,
1246
- * and one closure is cheaper than one per handler on a path that runs for every committed
1247
- * event.
1267
+ * This is what makes `replace*` mean "replace mine" rather than "replace everything". It
1268
+ * is written by `mountSlice` and never by a caller.
1248
1269
  *
1249
1270
  * @internal
1250
1271
  */
1251
- scopedEmit(e) {
1252
- const t = {
1253
- id: e.id,
1272
+ sliceOrigin = /* @__PURE__ */ new Map();
1273
+ /**
1274
+ * Who claims each dynamically mounted slice, for introspection only.
1275
+ *
1276
+ * @remarks
1277
+ * Surfaced in `__devtoolsIntrospect()` and named in the collision error. **Never read by
1278
+ * `replace*`.** Correctness comes from {@link Origin}, which nobody has to remember to
1279
+ * pass; if preservation depended on this string, forgetting it would silently delete a
1280
+ * library's state.
1281
+ *
1282
+ * @internal
1283
+ */
1284
+ sliceOwner = /* @__PURE__ */ new Map();
1285
+ /**
1286
+ * Slices unmounted by their owner, for a development-time diagnostic.
1287
+ *
1288
+ * @remarks
1289
+ * Decoration re-types the store, and after a disposer runs the widened type still claims
1290
+ * a slice that is gone. Reading it would hand a component `undefined` from a type that
1291
+ * promised a value, which is the silent failure this whole feature exists to remove.
1292
+ * Populated only for `dynamic` and `internal` slices: a `spec` slice removed by a
1293
+ * `replace*` was not promised by anyone's widened type.
1294
+ *
1295
+ * Bounded, because a long development session that mounts and disposes repeatedly would
1296
+ * otherwise accumulate an entry per cycle forever. The oldest is dropped: the diagnostic
1297
+ * exists for a slice someone has just stopped using, and a name disposed hundreds of
1298
+ * mounts ago is not the one being read by mistake. Maps to the owner, or `undefined`
1299
+ * when the library did not name itself.
1300
+ *
1301
+ * @internal
1302
+ */
1303
+ disposedSlices = /* @__PURE__ */ new Map();
1304
+ /** @internal */
1305
+ registrationObservers = /* @__PURE__ */ new Set();
1306
+ /**
1307
+ * Changes accumulated inside the current public call, flushed once at its end.
1308
+ *
1309
+ * @internal
1310
+ */
1311
+ pendingRegistrationChanges = null;
1312
+ /**
1313
+ * Depth of nested registration transactions.
1314
+ *
1315
+ * @remarks
1316
+ * `hotReplace` calls three `replace*` methods, and each of those registers repeatedly.
1317
+ * Only the outermost public entry point flushes, so one `hotReplace` produces one batch
1318
+ * spanning all three kinds rather than three batches of intermediate topology.
1319
+ *
1320
+ * @internal
1321
+ */
1322
+ registrationDepth = 0;
1323
+ /** True while observers are being notified, so a re-entrant change is queued. @internal */
1324
+ notifyingRegistrations = !1;
1325
+ /** Batches produced by an observer's own registrations, drained after the current one. @internal */
1326
+ queuedRegistrationBatches = [];
1327
+ /**
1328
+ * Whether `__replayEvents()` is allowed.
1329
+ * Set from `spec.devtools.allowReplay`.
1330
+ *
1331
+ * @internal
1332
+ */
1333
+ replayEnabled;
1334
+ /**
1335
+ * Produces the `id` for each emitted event. Defaults to `crypto.randomUUID()`; overridable
1336
+ * via {@link StoreSpec.idFactory} for runtimes lacking it or for deterministic tests.
1337
+ *
1338
+ * @internal
1339
+ */
1340
+ idFactory;
1341
+ /**
1342
+ * Optional hook invoked when an effect throws/rejects. See
1343
+ * {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
1344
+ * failure — this is how callers observe effect errors.
1345
+ */
1346
+ onEffectError;
1347
+ /**
1348
+ * Optional hook invoked when a reducer throws. See {@link StoreSpec.onReducerError}. The
1349
+ * failing slice is isolated rather than the event being rolled back, so this is the only
1350
+ * signal that a reducer misbehaved.
1351
+ */
1352
+ onReducerError;
1353
+ /** @internal */
1354
+ onSubscriberError;
1355
+ /**
1356
+ * `slice:channel:type` combinations already warned about for payload aliasing.
1357
+ *
1358
+ * @remarks
1359
+ * Development-only diagnostics have to stay quiet enough to be read. One warning names the
1360
+ * pattern; repeating it once per event would bury it.
1361
+ */
1362
+ warnedPayloadAliases = /* @__PURE__ */ new Set();
1363
+ /**
1364
+ * Pending events awaiting the **synchronous** reduce phase (middleware +
1365
+ * reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
1366
+ *
1367
+ * @internal
1368
+ */
1369
+ reduceQueue = [];
1370
+ /**
1371
+ * Re-entrancy guard for the synchronous reduce phase.
1372
+ *
1373
+ * @internal
1374
+ */
1375
+ isReducing = !1;
1376
+ /**
1377
+ * The event currently being reduced, or `null` outside the drain.
1378
+ *
1379
+ * @remarks
1380
+ * This is what makes causality exact rather than best-effort. The drain is synchronous — no
1381
+ * `await` can interleave — so any `emit` that arrives while it is set is, without ambiguity, a
1382
+ * consequence of this event. That catches the case a scoped `emit` closure cannot: a
1383
+ * middleware or subscriber that captured the store and calls `store.emit` directly instead of
1384
+ * using the injected one. Attribution should not depend on which reference a consumer reached
1385
+ * for.
1386
+ *
1387
+ * @internal
1388
+ */
1389
+ currentEvent = null;
1390
+ /**
1391
+ * Events processed by the drain currently in progress. Compared against
1392
+ * `maxTransitionsPerDrain`, which is off unless configured.
1393
+ *
1394
+ * @internal
1395
+ */
1396
+ transitionsThisDrain = 0;
1397
+ /**
1398
+ * Ceilings that stop a cascade from becoming a hung process. See {@link StoreSpec.maxReduceDepth}.
1399
+ *
1400
+ * @internal
1401
+ */
1402
+ maxReduceDepth;
1403
+ maxTransitionsPerDrain;
1404
+ onCascade;
1405
+ onRejected;
1406
+ /**
1407
+ * Registered instrumentation observers (DevTools seam). See {@link instrument}.
1408
+ *
1409
+ * @internal
1410
+ */
1411
+ instrumentObservers = /* @__PURE__ */ new Set();
1412
+ /**
1413
+ * Scratch array collecting slice-prefixed changed leaf paths during an
1414
+ * instrumented reduce. Set by {@link drainReduce} while observers are active;
1415
+ * appended to by {@link commitStaged}. `null` when not instrumenting.
1416
+ *
1417
+ * @internal
1418
+ */
1419
+ changedPathSink = null;
1420
+ /**
1421
+ * Where keyed reducers put their pending writes during a reduce, and the refusal one of them
1422
+ * returned.
1423
+ *
1424
+ * @remarks
1425
+ * Keyed reducers are invoked through `reducerBus`, which delivers to handlers and has no way
1426
+ * to hand a value back — the same reason `changedPathSink` exists. `null` outside a reduce.
1427
+ *
1428
+ * @internal
1429
+ */
1430
+ stagingSink = null;
1431
+ stagedRejection = null;
1432
+ stagedRejectedBy = "";
1433
+ /**
1434
+ * Count of effect tasks currently in flight; surfaced as queue depth by
1435
+ * {@link __devtoolsIntrospect}.
1436
+ *
1437
+ * @internal
1438
+ */
1439
+ inFlightEffects = 0;
1440
+ /**
1441
+ * Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
1442
+ *
1443
+ * **Deduplication Behavior:**
1444
+ * - Events are fingerprinted through the codec, so `Map`, `Set`, `Date`, `BigInt`, binary
1445
+ * and cyclic payloads all compare by content rather than collapsing to `{}`
1446
+ * - Plain-object keys are sorted, so key order is not content; array and `Map` order is
1447
+ * - If an identical fingerprint is seen within the dedup window, it's skipped
1448
+ * - The window is `dedupWindowMs`, which defaults to `0` (dedup off)
1449
+ *
1450
+ * **Limitations:**
1451
+ * - A payload larger than the fingerprint node budget is never deduplicated, which is the
1452
+ * safe direction: a missed dedup costs a duplicate, a false one drops a real event
1453
+ * - Legitimate rapid-fire identical events may be incorrectly deduplicated
1454
+ * - The cache is bounded to 1000 entries with lazy pruning
1455
+ *
1456
+ * @internal
1457
+ */
1458
+ processedEvents = /* @__PURE__ */ new Map();
1459
+ /**
1460
+ * Lifetime count of events suppressed by the deduplication cache.
1461
+ * Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
1462
+ * surface it in the STORE_METRICS response without further core changes.
1463
+ *
1464
+ * @internal
1465
+ */
1466
+ dedupCount = 0;
1467
+ /**
1468
+ * Store-owned metadata for registered effects, keyed by the effect function.
1469
+ * Kept **off** the caller's function object: mutating a user-owned function
1470
+ * (the old `fn.__quoMeta`) bled metadata across stores that share a handler
1471
+ * and left it attached after unregister. Cleared on {@link dispose}.
1472
+ *
1473
+ * @internal
1474
+ */
1475
+ effectMeta = /* @__PURE__ */ new WeakMap();
1476
+ /**
1477
+ * Configuration for event deduplication.
1478
+ * @internal
1479
+ */
1480
+ dedupConfig;
1481
+ /**
1482
+ * Timer for periodic cleanup of processed events.
1483
+ *
1484
+ * @internal
1485
+ */
1486
+ eventCleanupTimer = null;
1487
+ /**
1488
+ * Creates a store from a {@link StoreSpec}.
1489
+ *
1490
+ * @param spec - Store configuration (name, reducers, middleware, optional effects).
1491
+ *
1492
+ * @public
1493
+ */
1494
+ constructor(e) {
1495
+ if (this.name = e.name ?? "yoltra Store", this.reducerBus = new G(), this.connectorBus = new J(), this.middleware = (e.middleware ?? []).map((t) => ({
1496
+ input: t,
1497
+ origin: "spec"
1498
+ })), 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.onSubscriberError = e.onSubscriberError, this.maxReduceDepth = e.maxReduceDepth ?? ye, this.maxTransitionsPerDrain = e.maxTransitionsPerDrain ?? 1 / 0, this.onCascade = e.onCascade, this.onRejected = e.onRejected, this.dedupConfig = {
1499
+ windowMs: e.dedupWindowMs ?? 0,
1500
+ maxCacheSize: 1e3
1501
+ }, Object.entries(e.reducer).forEach(([t, n]) => {
1502
+ this.mountSlice(t, n, { preserveState: !1 });
1503
+ }), e.effects?.length)
1504
+ for (const t of e.effects)
1505
+ this.registerEffectWithOrigin(t, "spec");
1506
+ 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);
1507
+ }
1508
+ /**
1509
+ * Cleanup resources (timers, etc.) when disposing the store.
1510
+ * Call this if you're dynamically creating/destroying stores.
1511
+ *
1512
+ * @example
1513
+ * ```ts
1514
+ * const store = createStore({ ... });
1515
+ * // later
1516
+ * store.dispose();
1517
+ * ```
1518
+ *
1519
+ * @public
1520
+ */
1521
+ dispose() {
1522
+ 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.sliceOrigin.clear(), this.sliceOwner.clear(), this.disposedSlices.clear(), this.registrationObservers.clear(), this.pendingRegistrationChanges = null, this.queuedRegistrationBatches.length = 0, this.middleware.length = 0, this.changedPathSink = null;
1523
+ }
1524
+ /**
1525
+ * Generates a fingerprint for an event for deduplication purposes.
1526
+ * Falls back gracefully for non-serializable payloads.
1527
+ *
1528
+ * @param channel - Event channel.
1529
+ * @param type - Event type.
1530
+ * @param payload - Event payload.
1531
+ * @returns A string fingerprint for the event.
1532
+ *
1533
+ * @internal
1534
+ */
1535
+ fingerprint(e, t, n) {
1536
+ return ue(e, t, n);
1537
+ }
1538
+ /**
1539
+ * Checks if an event should be deduplicated.
1540
+ * Returns true if this is a duplicate that should be skipped.
1541
+ *
1542
+ * @param fp - Event fingerprint.
1543
+ * @returns `true` if duplicate (should skip), `false` otherwise.
1544
+ *
1545
+ * @internal
1546
+ */
1547
+ shouldDedupe(e, t) {
1548
+ const n = Date.now(), r = this.processedEvents.get(e);
1549
+ return r !== void 0 && n - r < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, n), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(n), !1);
1550
+ }
1551
+ /**
1552
+ * Starts the periodic prune interval if it isn't already running. Called when
1553
+ * the first entry is cached so the timer's lifetime tracks actual dedup use
1554
+ * (content window or identity `dedupKey`), independent of `dedupWindowMs`.
1555
+ *
1556
+ * @internal
1557
+ */
1558
+ ensureCleanupTimer() {
1559
+ this.eventCleanupTimer === null && (this.eventCleanupTimer = setInterval(() => {
1560
+ this.pruneProcessedEvents(Date.now());
1561
+ }, 5e3), this.eventCleanupTimer.unref?.());
1562
+ }
1563
+ /**
1564
+ * Removes expired entries from the processed events cache.
1565
+ *
1566
+ * @param now - Current timestamp.
1567
+ *
1568
+ * @internal
1569
+ */
1570
+ pruneProcessedEvents(e) {
1571
+ const t = Math.max(this.dedupConfig.windowMs, H), n = e - t * 2;
1572
+ for (const [r, i] of this.processedEvents)
1573
+ i < n && this.processedEvents.delete(r);
1574
+ this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
1575
+ }
1576
+ /**
1577
+ * Reports a breached ceiling and refuses the emit.
1578
+ *
1579
+ * @remarks
1580
+ * Console *and* hook, matching how reducer and effect errors are reported: a cascade is a
1581
+ * wiring bug, and the console line is what a developer who has not registered a hook will
1582
+ * actually see. Without one, refusing the emit would look exactly like the event never having
1583
+ * been emitted at all — which is the invisibility this whole guard exists to end.
1584
+ *
1585
+ * @internal
1586
+ */
1587
+ reportCascade(e, t, n, r, i) {
1588
+ console.error(
1589
+ `[yoltra] Cascade stopped: "${n.channel}/${n.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 "${n.channel}/${n.type}" and what that emits in turn.` + (i.length > 0 ? ` Recent causal chain: ${i.join(" → ")} → (refused).` : "")
1590
+ );
1591
+ try {
1592
+ this.onCascade?.({ limit: e, limitValue: t, event: n, depth: r, chain: i });
1593
+ } catch (s) {
1594
+ console.error("onCascade handler error:", s);
1595
+ }
1596
+ }
1597
+ /**
1598
+ * Invokes all registered **effects** for a given event.
1599
+ * Handles both key-based effects (O(1) lookup) and pattern-based effects (runtime matching).
1600
+ * Errors are caught and logged.
1601
+ *
1602
+ * @param event - The event that was reduced.
1603
+ * @internal
1604
+ */
1605
+ async notifyEffects(e) {
1606
+ const t = this.scopedEmit(e), n = `${String(e.channel)}::${String(e.type)}`, r = this.effects.get(n);
1607
+ if (r && r.size > 0)
1608
+ for (const i of [...r])
1609
+ try {
1610
+ await i.effect(e, this.getState, t);
1611
+ } catch (s) {
1612
+ console.error("Effect error:", s), this.onEffectError?.(s, e);
1613
+ }
1614
+ for (const { effect: i, when: s } of this.patternEffects)
1615
+ if ($(s, e))
1616
+ try {
1617
+ await i(e, this.getState, t);
1618
+ } catch (l) {
1619
+ console.error("Effect error:", l), this.onEffectError?.(l, e);
1620
+ }
1621
+ }
1622
+ /**
1623
+ * An `emit` that attributes whatever it sends to `cause`.
1624
+ *
1625
+ * @remarks
1626
+ * Built per event rather than per effect: every effect reacting to one event shares a cause,
1627
+ * and one closure is cheaper than one per handler on a path that runs for every committed
1628
+ * event.
1629
+ *
1630
+ * @internal
1631
+ */
1632
+ scopedEmit(e) {
1633
+ const t = {
1634
+ id: e.id,
1254
1635
  depth: e.depth ?? 0,
1255
- chain: [...this.currentEvent?.chain ?? [], e.id].slice(-z)
1636
+ chain: [...this.currentEvent?.chain ?? [], e.id].slice(-W)
1256
1637
  };
1257
- return ((s, n, r, o) => this.emitCaused(t, s, n, r, o));
1638
+ return ((n, r, i, s) => this.emitCaused(t, n, r, i, s));
1258
1639
  }
1259
1640
  /**
1260
1641
  * Notifies event subscribers for a specific phase.
@@ -1267,13 +1648,15 @@ class x {
1267
1648
  * @internal
1268
1649
  */
1269
1650
  notifyEventSubscribers(e, t) {
1270
- const s = `${String(e.channel)}::${String(e.type)}`, r = (t === "committed" ? this.committedEventSubscribers : t === "written" ? this.writtenEventSubscribers : this.uncommittedEventSubscribers).get(s);
1271
- if (r?.size)
1272
- for (const l of [...r]) this.invokeEventSubscriber(l, e, t);
1651
+ const n = `${String(e.channel)}::${String(e.type)}`, i = (t === "committed" ? this.committedEventSubscribers : t === "written" ? this.writtenEventSubscribers : this.uncommittedEventSubscribers).get(n);
1652
+ if (i?.size)
1653
+ for (const l of [...i])
1654
+ this.replaying && !l.duringReplay || this.invokeEventSubscriber(l.handler, e, t);
1273
1655
  if (t === "written") return;
1274
- const o = this.allEventSubscribers.get(s);
1275
- if (o?.size)
1276
- for (const l of [...o]) this.invokeEventSubscriber(l, e, t);
1656
+ const s = this.allEventSubscribers.get(n);
1657
+ if (s?.size)
1658
+ for (const l of [...s])
1659
+ this.replaying && !l.duringReplay || this.invokeEventSubscriber(l.handler, e, t);
1277
1660
  }
1278
1661
  /**
1279
1662
  * Invokes a single event-subscription handler **fire-and-forget**: synchronous
@@ -1282,12 +1665,15 @@ class x {
1282
1665
  *
1283
1666
  * @internal
1284
1667
  */
1285
- invokeEventSubscriber(e, t, s) {
1668
+ invokeEventSubscriber(e, t, n) {
1669
+ const r = (i) => {
1670
+ console.error("Event subscription error:", i), this.onSubscriberError?.(i, t, n);
1671
+ };
1286
1672
  try {
1287
- const n = e(t, this.getState, this.emit, s);
1288
- n && typeof n.then == "function" && n.catch((r) => console.error("Event subscription error:", r));
1289
- } catch (n) {
1290
- console.error("Event subscription error:", n);
1673
+ const i = e(t, this.getState, this.emit, n);
1674
+ i && typeof i.then == "function" && i.catch(r);
1675
+ } catch (i) {
1676
+ r(i);
1291
1677
  }
1292
1678
  }
1293
1679
  /**
@@ -1341,11 +1727,11 @@ class x {
1341
1727
  *
1342
1728
  * @internal
1343
1729
  */
1344
- stageSliceGuarded(e, t, s) {
1730
+ stageSliceGuarded(e, t, n) {
1345
1731
  try {
1346
- return this.stageSlice(e, t, s);
1347
- } catch (n) {
1348
- return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), null;
1732
+ return this.stageSlice(e, t, n);
1733
+ } catch (r) {
1734
+ return console.error(`Reducer error in slice "${e}":`, r), this.onReducerError?.(r, t, e), null;
1349
1735
  }
1350
1736
  }
1351
1737
  /**
@@ -1364,13 +1750,13 @@ class x {
1364
1750
  *
1365
1751
  * @internal
1366
1752
  */
1367
- stageSlice(e, t, s) {
1368
- const n = this.state[e], r = this.reducers[e].reduce(n, t);
1369
- if (Q(r)) return r;
1370
- if (n === r) return null;
1371
- const o = I(n, r);
1372
- if (o.length === 0) return null;
1373
- const l = t.payload, i = process.env.NODE_ENV !== "production" && l !== null && typeof l == "object" ? {
1753
+ stageSlice(e, t, n) {
1754
+ const r = this.state[e], i = this.reducers[e].reduce(r, t);
1755
+ if (Y(i)) return i;
1756
+ if (r === i) return null;
1757
+ const s = I(r, i);
1758
+ if (s.length === 0) return null;
1759
+ const l = t.payload, c = process.env.NODE_ENV !== "production" && l !== null && typeof l == "object" ? {
1374
1760
  watch: l,
1375
1761
  onFound: () => {
1376
1762
  const a = `${e}:${t.channel}:${t.type}`;
@@ -1379,11 +1765,11 @@ class x {
1379
1765
  ));
1380
1766
  }
1381
1767
  } : void 0;
1382
- return s.push({
1768
+ return n.push({
1383
1769
  name: e,
1384
- prev: n,
1385
- frozen: R(r, i),
1386
- leafPaths: o
1770
+ prev: r,
1771
+ frozen: T(i, c),
1772
+ leafPaths: s
1387
1773
  }), null;
1388
1774
  }
1389
1775
  /**
@@ -1407,26 +1793,26 @@ class x {
1407
1793
  */
1408
1794
  commitStaged(e, t) {
1409
1795
  if (e.length === 0) return !1;
1410
- const s = { ...this.state };
1411
- for (const n of e) s[n.name] = n.frozen;
1412
- if (this.state = s, this.changedPathSink)
1413
- for (const n of e)
1414
- for (const r of n.leafPaths)
1415
- this.changedPathSink.push(r ? `${n.name}.${r}` : n.name);
1416
- for (const n of e) {
1417
- const r = /* @__PURE__ */ new Set();
1418
- for (const o of n.leafPaths) {
1419
- if (o === "") {
1420
- r.add("");
1796
+ const n = { ...this.state };
1797
+ for (const r of e) n[r.name] = r.frozen;
1798
+ if (this.state = n, this.changedPathSink)
1799
+ for (const r of e)
1800
+ for (const i of r.leafPaths)
1801
+ this.changedPathSink.push(i ? `${r.name}.${i}` : r.name);
1802
+ for (const r of e) {
1803
+ const i = /* @__PURE__ */ new Set();
1804
+ for (const s of r.leafPaths) {
1805
+ if (s === "") {
1806
+ i.add("");
1421
1807
  continue;
1422
1808
  }
1423
- for (const l of x.buildAncestorPaths(o)) r.add(l);
1809
+ for (const l of k.buildAncestorPaths(s)) i.add(l);
1424
1810
  }
1425
- for (const o of r)
1426
- this.connectorBus.emitWith(n.name, o, () => ({
1427
- oldValue: this.getAtPath(n.prev, o),
1428
- newValue: this.getAtPath(n.frozen, o),
1429
- path: o,
1811
+ for (const s of i)
1812
+ this.connectorBus.emitWith(r.name, s, () => ({
1813
+ oldValue: this.getAtPath(r.prev, s),
1814
+ newValue: this.getAtPath(r.frozen, s),
1815
+ path: s,
1430
1816
  // Provenance, built inside the same lazy factory as the values: a subscriber that
1431
1817
  // needs to know why a value moved no longer has to mirror the cause into state and
1432
1818
  // keep it there twice.
@@ -1448,65 +1834,68 @@ class x {
1448
1834
  * @public
1449
1835
  */
1450
1836
  __devtoolsIntrospect() {
1451
- const e = Object.keys(this.reducers).map((l) => {
1452
- const i = this.patternReducers.get(l);
1453
- return { name: l, when: i };
1837
+ const e = Object.keys(this.reducers).map((c) => {
1838
+ const a = this.patternReducers.get(c);
1839
+ return {
1840
+ name: c,
1841
+ when: a,
1842
+ origin: this.sliceOrigin.get(c) ?? "spec",
1843
+ owner: this.sliceOwner.get(c)
1844
+ };
1454
1845
  }), t = [];
1455
- for (const [l, i] of this.effects) {
1456
- if (i.size === 0) continue;
1457
- const [a, d] = l.split("::");
1458
- for (const u of i) {
1459
- const f = this.effectMeta.get(u);
1460
- t.push({ channel: a, type: d, name: f?.name, description: f?.description });
1846
+ for (const [c, a] of this.effects) {
1847
+ if (a.size === 0) continue;
1848
+ const [d, f] = c.split("::");
1849
+ for (const u of a) {
1850
+ const p = this.effectMeta.get(u.effect);
1851
+ t.push({
1852
+ channel: d,
1853
+ type: f,
1854
+ name: p?.name,
1855
+ description: p?.description,
1856
+ origin: u.origin
1857
+ });
1461
1858
  }
1462
1859
  }
1463
- for (const l of this.patternEffects) {
1464
- const i = this.effectMeta.get(l.effect);
1860
+ for (const c of this.patternEffects) {
1861
+ const a = this.effectMeta.get(c.effect);
1465
1862
  t.push({
1466
1863
  channel: "*",
1467
1864
  type: "*",
1468
- name: i?.name,
1469
- description: i?.description
1865
+ name: a?.name,
1866
+ description: a?.description,
1867
+ origin: c.origin
1470
1868
  });
1471
1869
  }
1472
- const s = [];
1473
- for (const l of this.middleware)
1474
- typeof l == "function" ? s.push({ name: l.name || void 0 }) : s.push({
1475
- name: l.meta?.name,
1476
- description: l.meta?.description,
1477
- when: l.when
1478
- });
1479
1870
  const n = [];
1480
- for (const l of this.connectorBus.__introspect())
1481
- for (let i = 0; i < l.count; i++)
1482
- n.push({ reducer: l.channel, property: l.type });
1871
+ for (const { input: c, origin: a } of this.middleware)
1872
+ typeof c == "function" ? n.push({ name: c.name || void 0, origin: a }) : n.push({
1873
+ name: c.meta?.name,
1874
+ description: c.meta?.description,
1875
+ when: c.when,
1876
+ origin: a
1877
+ });
1483
1878
  const r = [];
1484
- for (const [l, i] of this.committedEventSubscribers) {
1485
- if (i.size === 0) continue;
1486
- const [a, d] = l.split("::");
1487
- for (let u = 0; u < i.size; u++)
1488
- r.push({ channel: a, type: d, phase: "committed" });
1489
- }
1490
- for (const [l, i] of this.uncommittedEventSubscribers) {
1491
- if (i.size === 0) continue;
1492
- const [a, d] = l.split("::");
1493
- for (let u = 0; u < i.size; u++)
1494
- r.push({ channel: a, type: d, phase: "uncommitted" });
1495
- }
1496
- for (const [l, i] of this.allEventSubscribers) {
1497
- if (i.size === 0) continue;
1498
- const [a, d] = l.split("::");
1499
- for (let u = 0; u < i.size; u++)
1500
- r.push({ channel: a, type: d, phase: "all" });
1501
- }
1502
- const o = this.listeners.size;
1879
+ for (const c of this.connectorBus.__introspect())
1880
+ for (let a = 0; a < c.count; a++)
1881
+ r.push({ reducer: c.channel, property: c.type });
1882
+ const i = [], s = (c, a) => {
1883
+ for (const [d, f] of c) {
1884
+ if (f.size === 0) continue;
1885
+ const [u, p] = d.split("::");
1886
+ for (const h of f)
1887
+ i.push({ channel: u, type: p, phase: a, duringReplay: h.duringReplay });
1888
+ }
1889
+ };
1890
+ s(this.committedEventSubscribers, "committed"), s(this.uncommittedEventSubscribers, "uncommitted"), s(this.writtenEventSubscribers, "written"), s(this.allEventSubscribers, "all");
1891
+ const l = this.listeners.size;
1503
1892
  return {
1504
1893
  reducers: e,
1505
1894
  effects: t,
1506
- middleware: s,
1507
- atomic: n,
1508
- event: r,
1509
- coarse: o,
1895
+ middleware: n,
1896
+ atomic: r,
1897
+ event: i,
1898
+ coarse: l,
1510
1899
  dedupHits: this.dedupCount,
1511
1900
  queueDepth: this.reduceQueue.length + this.inFlightEffects
1512
1901
  };
@@ -1531,36 +1920,50 @@ class x {
1531
1920
  throw new Error(
1532
1921
  "[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1533
1922
  );
1534
- const t = this.state, s = e, n = { ...this.state };
1535
- let r = !1;
1536
- Object.keys(this.reducers).forEach((o) => {
1537
- const l = t?.[o], i = s?.[o];
1538
- if (i === void 0) {
1539
- process.env.NODE_ENV !== "production" && console.warn(
1923
+ const t = this.replaying;
1924
+ this.replaying = !0;
1925
+ try {
1926
+ return this.applyExternalStateInner(e);
1927
+ } finally {
1928
+ this.replaying = t;
1929
+ }
1930
+ }
1931
+ /**
1932
+ * The body of {@link __applyExternalState}, with the replay flag already set.
1933
+ *
1934
+ * @internal
1935
+ */
1936
+ applyExternalStateInner(e) {
1937
+ const t = this.state, n = e, r = { ...this.state };
1938
+ let i = !1;
1939
+ Object.keys(this.reducers).forEach((s) => {
1940
+ const l = t?.[s], c = n?.[s];
1941
+ if (c === void 0) {
1942
+ process.env.NODE_ENV !== "production" && (this.sliceOrigin.get(s) ?? "spec") === "spec" && console.warn(
1540
1943
  `[yoltra] External state is missing slice "${String(
1541
- o
1944
+ s
1542
1945
  )}"; retaining its current value. Time-travel snapshots should contain all slices.`
1543
1946
  );
1544
1947
  return;
1545
1948
  }
1546
- if (l === i) return;
1547
- const a = R(i);
1548
- n[o] = a, r = !0;
1549
- const d = I(l, i);
1949
+ if (l === c) return;
1950
+ const a = T(c);
1951
+ r[s] = a, i = !0;
1952
+ const d = I(l, c);
1550
1953
  if (d.length === 0) return;
1551
- const u = /* @__PURE__ */ new Set();
1552
- for (const f of d) {
1553
- if (f === "") {
1554
- u.add("");
1954
+ const f = /* @__PURE__ */ new Set();
1955
+ for (const u of d) {
1956
+ if (u === "") {
1957
+ f.add("");
1555
1958
  continue;
1556
1959
  }
1557
- for (const m of x.buildAncestorPaths(f)) u.add(m);
1960
+ for (const p of k.buildAncestorPaths(u)) f.add(p);
1558
1961
  }
1559
- for (const f of u) {
1560
- const m = this.getAtPath(l, f), h = this.getAtPath(a, f);
1561
- this.connectorBus.emit(o, f, { oldValue: m, newValue: h, path: f });
1962
+ for (const u of f) {
1963
+ const p = this.getAtPath(l, u), h = this.getAtPath(a, u);
1964
+ this.connectorBus.emit(s, u, { oldValue: p, newValue: h, path: u });
1562
1965
  }
1563
- }), r && (this.state = n), r && this.listeners.forEach((o) => o());
1966
+ }), i && (this.state = r), i && this.listeners.forEach((s) => s());
1564
1967
  }
1565
1968
  /**
1566
1969
  * Replays a sequence of events from a snapshot through reducers and event
@@ -1579,25 +1982,39 @@ class x {
1579
1982
  throw new Error(
1580
1983
  "[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
1581
1984
  );
1985
+ const n = this.replaying;
1986
+ this.replaying = !0;
1987
+ try {
1988
+ this.replayEventsInner(e, t);
1989
+ } finally {
1990
+ this.replaying = n;
1991
+ }
1992
+ }
1993
+ /**
1994
+ * The body of {@link __replayEvents}, with the replay flag already set.
1995
+ *
1996
+ * @internal
1997
+ */
1998
+ replayEventsInner(e, t) {
1582
1999
  this.__applyExternalState(e);
1583
- for (const s of t) {
1584
- const n = s, r = [];
1585
- this.stagingSink = r;
1586
- let o = null;
2000
+ for (const n of t) {
2001
+ const r = n, i = [];
2002
+ this.stagingSink = i;
2003
+ let s = null;
1587
2004
  try {
1588
- this.reducerBus.emit(n.channel, n.type, n.payload, n), o = this.stagedRejection;
1589
- for (const [i, a] of this.patternReducers) {
1590
- if (o !== null) break;
1591
- if (this.matchesWhen(a, n)) {
1592
- const d = this.stageSliceGuarded(i, n, r);
1593
- d !== null && (o = d);
2005
+ this.reducerBus.emit(r.channel, r.type, r.payload, r), s = this.stagedRejection;
2006
+ for (const [c, a] of this.patternReducers) {
2007
+ if (s !== null) break;
2008
+ if ($(a, r)) {
2009
+ const d = this.stageSliceGuarded(c, r, i);
2010
+ d !== null && (s = d);
1594
2011
  }
1595
2012
  }
1596
2013
  } finally {
1597
2014
  this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1598
2015
  }
1599
- const l = o === null && this.commitStaged(r, n);
1600
- this.notifyEventSubscribers(n, "committed"), l && (this.notifyEventSubscribers(n, "written"), this.listeners.forEach((i) => i()));
2016
+ const l = s === null && this.commitStaged(i, r);
2017
+ this.notifyEventSubscribers(r, "committed"), l && (this.notifyEventSubscribers(r, "written"), this.listeners.forEach((c) => c()));
1601
2018
  }
1602
2019
  }
1603
2020
  /**
@@ -1643,8 +2060,8 @@ class x {
1643
2060
  *
1644
2061
  * @public
1645
2062
  */
1646
- async emit(e, t, s, n) {
1647
- return this.emitCaused(null, e, t, s, n);
2063
+ async emit(e, t, n, r) {
2064
+ return this.emitCaused(null, e, t, n, r);
1648
2065
  }
1649
2066
  /**
1650
2067
  * The real emit, with an explicitly supplied cause.
@@ -1658,45 +2075,45 @@ class x {
1658
2075
  *
1659
2076
  * @internal
1660
2077
  */
1661
- async emitCaused(e, t, s, n, r) {
1662
- const o = r?.dedupKey, l = this.dedupConfig.windowMs;
1663
- if (r?.skipDedup !== !0 && (l > 0 || o !== void 0)) {
1664
- const m = o !== void 0 && l <= 0 ? D : l, h = o !== void 0 ? `${t}::${s}::#${o}` : this.fingerprint(t, s, n);
1665
- if (this.shouldDedupe(h, m))
1666
- return $;
2078
+ async emitCaused(e, t, n, r, i) {
2079
+ const s = i?.dedupKey, l = this.dedupConfig.windowMs;
2080
+ if (i?.skipDedup !== !0 && (l > 0 || s !== void 0)) {
2081
+ const p = s !== void 0 && l <= 0 ? H : l, h = s !== void 0 ? `${t}::${n}::#${s}` : this.fingerprint(t, n, r);
2082
+ if (this.shouldDedupe(h, p))
2083
+ return we;
1667
2084
  }
1668
- const i = r?.id ?? this.idFactory(), a = this.currentEvent ?? e, d = a === null ? 0 : a.depth + 1;
2085
+ const c = i?.id ?? this.idFactory(), a = this.currentEvent ?? e, d = a === null ? 0 : a.depth + 1;
1669
2086
  if (a !== null && d > this.maxReduceDepth)
1670
2087
  return this.reportCascade(
1671
2088
  "maxReduceDepth",
1672
2089
  this.maxReduceDepth,
1673
2090
  {
1674
2091
  channel: t,
1675
- type: s,
1676
- payload: n,
1677
- id: i,
1678
- ...r?.meta !== void 0 ? { meta: r.meta } : {},
2092
+ type: n,
2093
+ payload: r,
2094
+ id: c,
2095
+ ...i?.meta !== void 0 ? { meta: i.meta } : {},
1679
2096
  parentId: a.id,
1680
2097
  depth: d
1681
2098
  },
1682
2099
  d,
1683
2100
  a.chain
1684
- ), $;
1685
- let u;
1686
- const f = new Promise((m) => {
1687
- u = m;
2101
+ ), F;
2102
+ let f;
2103
+ const u = new Promise((p) => {
2104
+ f = p;
1688
2105
  });
1689
2106
  return this.reduceQueue.push({
1690
2107
  channel: t,
1691
- type: s,
1692
- payload: n,
1693
- id: i,
1694
- meta: r?.meta,
1695
- resolve: u,
2108
+ type: n,
2109
+ payload: r,
2110
+ id: c,
2111
+ meta: i?.meta,
2112
+ resolve: f,
1696
2113
  // Only carried for caused events, so a root event's object stays byte-identical to one
1697
2114
  // built before causality existed — the same rule `meta` follows.
1698
2115
  ...a !== null ? { parentId: a.id, depth: d, chain: a.chain } : {}
1699
- }), this.drainReduce(), f;
2116
+ }), this.drainReduce(), u;
1700
2117
  }
1701
2118
  /**
1702
2119
  * Drains the reduce queue **synchronously**. For each event it runs middleware,
@@ -1713,47 +2130,47 @@ class x {
1713
2130
  this.isReducing = !0, this.transitionsThisDrain = 0;
1714
2131
  try {
1715
2132
  for (; this.reduceQueue.length > 0; ) {
1716
- const e = this.reduceQueue.shift(), { channel: t, type: s, payload: n, id: r, meta: o, resolve: l, parentId: i, depth: a, chain: d } = e, u = {
2133
+ const e = this.reduceQueue.shift(), { channel: t, type: n, payload: r, id: i, meta: s, resolve: l, parentId: c, depth: a, chain: d } = e, f = {
1717
2134
  channel: t,
1718
- type: s,
1719
- payload: n,
1720
- id: r,
1721
- ...o !== void 0 ? { meta: o } : {},
1722
- ...i !== void 0 ? { parentId: i, depth: a } : {}
2135
+ type: n,
2136
+ payload: r,
2137
+ id: i,
2138
+ ...s !== void 0 ? { meta: s } : {},
2139
+ ...c !== void 0 ? { parentId: c, depth: a } : {}
1723
2140
  };
1724
- if (i !== void 0 && ++this.transitionsThisDrain > this.maxTransitionsPerDrain) {
2141
+ if (c !== void 0 && ++this.transitionsThisDrain > this.maxTransitionsPerDrain) {
1725
2142
  this.reportCascade(
1726
2143
  "maxTransitionsPerDrain",
1727
2144
  this.maxTransitionsPerDrain,
1728
- u,
2145
+ f,
1729
2146
  a,
1730
2147
  d
1731
- ), l($);
2148
+ ), l(F);
1732
2149
  continue;
1733
2150
  }
1734
2151
  this.currentEvent = {
1735
- id: r,
2152
+ id: i,
1736
2153
  depth: a ?? 0,
1737
- chain: [...d ?? [], r].slice(-z)
2154
+ chain: [...d ?? [], i].slice(-W)
1738
2155
  };
1739
- const f = this.instrumentObservers.size > 0, m = f ? this.state : void 0, h = f ? [] : void 0;
2156
+ const u = this.instrumentObservers.size > 0, p = u ? this.state : void 0, h = u ? [] : void 0;
1740
2157
  h !== void 0 && (this.changedPathSink = h);
1741
- const y = f ? A() : 0;
1742
- let g = $;
2158
+ const y = u ? K() : 0;
2159
+ let w = me;
1743
2160
  try {
1744
- g = this.applyEventSync(u);
1745
- } catch (w) {
1746
- console.error("Emit reduce error:", w);
2161
+ w = this.applyEventSync(f);
2162
+ } catch (g) {
2163
+ console.error("Emit reduce error:", g);
1747
2164
  } finally {
1748
- f && (this.changedPathSink = null), this.currentEvent = null;
2165
+ u && (this.changedPathSink = null), this.currentEvent = null;
1749
2166
  }
1750
- f && this.emitInstrumentation(
1751
- u,
1752
- g,
2167
+ u && this.emitInstrumentation(
2168
+ f,
2169
+ w,
1753
2170
  h ?? [],
1754
- m,
1755
- A() - y
1756
- ), this.runEventEffects(u, g, l);
2171
+ p,
2172
+ K() - y
2173
+ ), this.runEventEffects(f, w, l);
1757
2174
  }
1758
2175
  } finally {
1759
2176
  this.isReducing = !1;
@@ -1771,45 +2188,56 @@ class x {
1771
2188
  * @internal
1772
2189
  */
1773
2190
  applyEventSync(e) {
1774
- for (const o of this.middleware) {
1775
- const l = this.getMiddlewareWhen(o);
1776
- if (!this.matchesWhen(l, e)) continue;
1777
- const i = this.getMiddlewareFunction(o);
2191
+ for (const { input: s } of this.middleware) {
2192
+ const l = A(s);
2193
+ if (!$(l, e)) continue;
2194
+ const c = he(s);
1778
2195
  let a;
1779
2196
  try {
1780
- a = i(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
2197
+ a = c(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
1781
2198
  `[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.`
1782
2199
  );
1783
2200
  } catch (d) {
1784
- console.error("Middleware error:", d), a = !1;
2201
+ console.error(
2202
+ `[yoltra] Middleware threw for "${e.channel}/${e.type}"; the event was vetoed and did not reach any reducer.`,
2203
+ d
2204
+ ), a = !1;
2205
+ }
2206
+ if (a === !1) {
2207
+ this.notifyEventSubscribers(e, "uncommitted");
2208
+ const d = typeof s == "function" ? s.name || void 0 : s.meta?.name;
2209
+ return Object.freeze({
2210
+ committed: !1,
2211
+ written: !1,
2212
+ reason: "vetoed",
2213
+ ...d !== void 0 && d !== "" ? { vetoedBy: d } : {}
2214
+ });
1785
2215
  }
1786
- if (!a)
1787
- return this.notifyEventSubscribers(e, "uncommitted"), $;
1788
2216
  }
1789
2217
  const t = [];
1790
2218
  this.stagingSink = t;
1791
- let s = null, n = "";
2219
+ let n = null, r = "";
1792
2220
  try {
1793
2221
  this.reducerBus.emit(
1794
2222
  e.channel,
1795
2223
  e.type,
1796
2224
  e.payload,
1797
2225
  e
1798
- ), s = this.stagedRejection, n = this.stagedRejectedBy;
1799
- for (const [o, l] of this.patternReducers) {
1800
- if (s !== null) break;
1801
- if (this.matchesWhen(l, e)) {
1802
- const i = this.stageSliceGuarded(o, e, t);
1803
- i !== null && (s = i, n = o);
2226
+ ), n = this.stagedRejection, r = this.stagedRejectedBy;
2227
+ for (const [s, l] of this.patternReducers) {
2228
+ if (n !== null) break;
2229
+ if ($(l, e)) {
2230
+ const c = this.stageSliceGuarded(s, e, t);
2231
+ c !== null && (n = c, r = s);
1804
2232
  }
1805
2233
  }
1806
2234
  } finally {
1807
2235
  this.stagingSink = null, this.stagedRejection = null, this.stagedRejectedBy = "";
1808
2236
  }
1809
- if (s !== null)
1810
- return this.onRejected?.(s, e, n), this.notifyEventSubscribers(e, "committed"), { committed: !0, written: !1, rejected: s };
1811
- const r = this.commitStaged(t, e);
1812
- return this.notifyEventSubscribers(e, "committed"), r && (this.notifyEventSubscribers(e, "written"), this.listeners.forEach((o) => o())), r ? se : ne;
2237
+ if (n !== null)
2238
+ return this.onRejected?.(n, e, r), this.notifyEventSubscribers(e, "committed"), { committed: !0, written: !1, rejected: n };
2239
+ const i = this.commitStaged(t, e);
2240
+ return this.notifyEventSubscribers(e, "committed"), i && (this.notifyEventSubscribers(e, "written"), this.listeners.forEach((s) => s())), i ? Ee : be;
1813
2241
  }
1814
2242
  /**
1815
2243
  * Runs a single committed event's effects as an **independent async task**,
@@ -1819,14 +2247,14 @@ class x {
1819
2247
  *
1820
2248
  * @internal
1821
2249
  */
1822
- async runEventEffects(e, t, s) {
2250
+ async runEventEffects(e, t, n) {
1823
2251
  this.inFlightEffects++;
1824
2252
  try {
1825
2253
  t.committed && await this.notifyEffects(e);
1826
- } catch (n) {
1827
- console.error("Effect error:", n);
2254
+ } catch (r) {
2255
+ console.error("Effect error:", r);
1828
2256
  } finally {
1829
- this.inFlightEffects--, s(t);
2257
+ this.inFlightEffects--, n(t);
1830
2258
  }
1831
2259
  }
1832
2260
  /**
@@ -1847,11 +2275,11 @@ class x {
1847
2275
  *
1848
2276
  * @internal
1849
2277
  */
1850
- emitInstrumentation(e, t, s, n, r) {
1851
- const o = {}, l = {};
1852
- for (const a of s)
1853
- o[a] = this.getAtPath(n, a), l[a] = this.getAtPath(this.state, a);
1854
- const i = {
2278
+ emitInstrumentation(e, t, n, r, i) {
2279
+ const s = {}, l = {};
2280
+ for (const a of n)
2281
+ s[a] = this.getAtPath(r, a), l[a] = this.getAtPath(this.state, a);
2282
+ const c = {
1855
2283
  event: {
1856
2284
  id: e.id,
1857
2285
  channel: e.channel,
@@ -1862,17 +2290,17 @@ class x {
1862
2290
  ...e.meta !== void 0 ? { meta: e.meta } : {}
1863
2291
  },
1864
2292
  committed: t.committed,
1865
- changedPaths: s,
1866
- prevValues: o,
2293
+ changedPaths: n,
2294
+ prevValues: s,
1867
2295
  nextValues: l,
1868
- reduceTimeMs: r,
2296
+ reduceTimeMs: i,
1869
2297
  // Present only when a reducer refused, so an observer can tell a refusal from a veto —
1870
2298
  // identical in state, entirely different in cause.
1871
2299
  ...t.rejected !== void 0 ? { rejected: t.rejected } : {}
1872
2300
  };
1873
2301
  for (const a of [...this.instrumentObservers])
1874
2302
  try {
1875
- a(i);
2303
+ a(c);
1876
2304
  } catch (d) {
1877
2305
  console.error("Instrumentation observer error:", d);
1878
2306
  }
@@ -1894,24 +2322,220 @@ class x {
1894
2322
  * off();
1895
2323
  * ```
1896
2324
  *
1897
- * @example Wildcard pattern
1898
- * ```ts
1899
- * // Listen to any item title change
1900
- * const off = store.connect(
1901
- * { reducer: 'todos', property: 'items.*.title' },
1902
- * (chg) => console.log('some title changed')
1903
- * );
1904
- * ```
2325
+ * @example Wildcard pattern
2326
+ * ```ts
2327
+ * // Listen to any item title change
2328
+ * const off = store.connect(
2329
+ * { reducer: 'todos', property: 'items.*.title' },
2330
+ * (chg) => console.log('some title changed')
2331
+ * );
2332
+ * ```
2333
+ *
2334
+ * @public
2335
+ */
2336
+ connect(e, t, n) {
2337
+ if (process.env.NODE_ENV !== "production" && this.disposedSlices.has(e.reducer)) {
2338
+ const i = this.disposedSlices.get(e.reducer);
2339
+ throw new Error(
2340
+ `[yoltra] Slice "${String(e.reducer)}" was unmounted by its owner${i === void 0 ? "" : ` (${i})`}. Hooks and subscriptions widened for it are no longer valid.`
2341
+ );
2342
+ }
2343
+ const r = this.connectorBus.on(e.reducer, e.property, t);
2344
+ if (n?.immediate === !0) {
2345
+ const i = this.state[e.reducer], s = e.property.includes("*") ? "" : e.property;
2346
+ t({ oldValue: void 0, newValue: this.getAtPath(i, s), path: s });
2347
+ }
2348
+ return r;
2349
+ }
2350
+ /**
2351
+ * Subscribe to reducer, middleware and effect registrations.
2352
+ *
2353
+ * Delivered as an array, one batch per public call: `replaceReducers` unmounts and then
2354
+ * remounts, and a per-change observer would see a spurious unmount of a slice that is only
2355
+ * being updated. Observers run after the state broadcast, and a registration made by an
2356
+ * observer is queued rather than delivered re-entrantly.
2357
+ *
2358
+ * Replay never produces a change, so there is no `duringReplay` option here. `dispose()`
2359
+ * fires nothing.
2360
+ *
2361
+ * @param observer - Receives one batch per registration change.
2362
+ * @param options - `emitCurrent` synthesizes a `'mounted'` batch for everything already
2363
+ * installed, delivered synchronously before this call returns, carrying each registration's
2364
+ * real origin rather than a synthetic marker.
2365
+ * @returns Unsubscribe function.
2366
+ *
2367
+ * @example
2368
+ * ```ts
2369
+ * const off = store.onRegistrationChange((changes) => {
2370
+ * for (const c of changes) {
2371
+ * console.log(c.op, c.kind, c.name, c.origin);
2372
+ * }
2373
+ * }, { emitCurrent: true });
2374
+ * off();
2375
+ * ```
2376
+ *
2377
+ * @public
2378
+ */
2379
+ onRegistrationChange(e, t) {
2380
+ if (this.registrationObservers.add(e), t?.emitCurrent === !0) {
2381
+ const n = this.describeCurrentRegistrations();
2382
+ if (n.length > 0) {
2383
+ const r = this.notifyingRegistrations;
2384
+ this.notifyingRegistrations = !0;
2385
+ try {
2386
+ this.invokeRegistrationObserver(e, n);
2387
+ } finally {
2388
+ this.notifyingRegistrations = r;
2389
+ }
2390
+ r || this.drainQueuedRegistrationBatches();
2391
+ }
2392
+ }
2393
+ return () => {
2394
+ this.registrationObservers.delete(e);
2395
+ };
2396
+ }
2397
+ /**
2398
+ * Everything currently installed, as `"mounted"` changes.
2399
+ *
2400
+ * @internal
2401
+ */
2402
+ describeCurrentRegistrations() {
2403
+ const e = [];
2404
+ for (const t of Object.keys(this.reducers))
2405
+ e.push({
2406
+ kind: "reducer",
2407
+ op: "mounted",
2408
+ name: t,
2409
+ // The real origin, never a synthetic "existing" marker. Filtering on provenance is
2410
+ // the main thing an observer does, and a snapshot that lied about it would break
2411
+ // exactly the registrations that were already there.
2412
+ origin: this.sliceOrigin.get(t) ?? "spec",
2413
+ owner: this.sliceOwner.get(t),
2414
+ when: this.patternReducers.get(t),
2415
+ // From this observer's point of view the state exists; it never saw a prior value.
2416
+ state: "initialized",
2417
+ dispatch: this.patternReducers.has(t) ? "pattern" : "keyed"
2418
+ });
2419
+ for (const t of this.middleware) {
2420
+ const n = typeof t.input == "function" ? void 0 : t.input.meta;
2421
+ e.push({
2422
+ kind: "middleware",
2423
+ op: "mounted",
2424
+ name: n?.name ?? (typeof t.input == "function" ? t.input.name : void 0),
2425
+ description: n?.description,
2426
+ origin: t.origin,
2427
+ when: A(t.input),
2428
+ dispatch: "pattern"
2429
+ });
2430
+ }
2431
+ for (const [t, n] of this.effects) {
2432
+ const [r, i] = t.split("::");
2433
+ for (const s of n)
2434
+ e.push({
2435
+ kind: "effect",
2436
+ op: "mounted",
2437
+ name: this.effectMeta.get(s.effect)?.name,
2438
+ description: this.effectMeta.get(s.effect)?.description,
2439
+ origin: s.origin,
2440
+ when: { keys: [[r, i]] },
2441
+ dispatch: "keyed"
2442
+ });
2443
+ }
2444
+ for (const t of this.patternEffects)
2445
+ e.push({
2446
+ kind: "effect",
2447
+ op: "mounted",
2448
+ name: this.effectMeta.get(t.effect)?.name,
2449
+ description: this.effectMeta.get(t.effect)?.description,
2450
+ origin: t.origin,
2451
+ when: t.when,
2452
+ dispatch: "pattern"
2453
+ });
2454
+ return e;
2455
+ }
2456
+ /**
2457
+ * Runs `fn` as one registration transaction, flushing a single batch at the outermost end.
2458
+ *
2459
+ * @internal
2460
+ */
2461
+ inRegistrationTransaction(e) {
2462
+ this.registrationDepth += 1;
2463
+ try {
2464
+ return e();
2465
+ } finally {
2466
+ this.registrationDepth -= 1, this.registrationDepth === 0 && this.flushRegistrationChanges();
2467
+ }
2468
+ }
2469
+ /**
2470
+ * Records a change, to be delivered when the current transaction ends.
2471
+ *
2472
+ * @remarks
2473
+ * Guarded on observer count **before** anything is allocated. These call sites run inside
2474
+ * `createStore`, so a twenty-slice store with nobody listening must build no objects and
2475
+ * no arrays at all - the same discipline `emitInstrumentation` already follows.
2476
+ *
2477
+ * @internal
2478
+ */
2479
+ recordRegistrationChange(e) {
2480
+ this.registrationObservers.size !== 0 && ((this.pendingRegistrationChanges ??= []).push(e()), this.registrationDepth === 0 && this.flushRegistrationChanges());
2481
+ }
2482
+ /** @internal */
2483
+ flushRegistrationChanges() {
2484
+ const e = this.pendingRegistrationChanges;
2485
+ if (this.pendingRegistrationChanges = null, !(e === null || e.length === 0)) {
2486
+ if (this.notifyingRegistrations) {
2487
+ this.queuedRegistrationBatches.push(e);
2488
+ return;
2489
+ }
2490
+ this.notifyingRegistrations = !0;
2491
+ try {
2492
+ this.deliverRegistrationBatch(e), this.drainQueuedRegistrationBatches();
2493
+ } finally {
2494
+ this.notifyingRegistrations = !1;
2495
+ }
2496
+ }
2497
+ }
2498
+ /**
2499
+ * Delivers batches an observer produced while being notified.
2500
+ *
2501
+ * @remarks
2502
+ * Bounded like the reduce depth, and for the same reason: two observers registering in
2503
+ * response to each other would otherwise loop forever. Logged rather than thrown - the
2504
+ * topology is correct at that point, and throwing would truncate the stream *and* unwind a
2505
+ * caller that did nothing wrong.
1905
2506
  *
1906
- * @public
2507
+ * @internal
1907
2508
  */
1908
- connect(e, t, s) {
1909
- const n = this.connectorBus.on(e.reducer, e.property, t);
1910
- if (s?.immediate === !0) {
1911
- const r = this.state[e.reducer], o = e.property.includes("*") ? "" : e.property;
1912
- t({ oldValue: void 0, newValue: this.getAtPath(r, o), path: o });
2509
+ drainQueuedRegistrationBatches() {
2510
+ let e = 0;
2511
+ for (; this.queuedRegistrationBatches.length > 0; ) {
2512
+ if (e >= V) {
2513
+ console.error(
2514
+ `[yoltra] Registration notifications exceeded ${V} rounds; dropping the rest. Two observers are most likely registering in response to each other.`
2515
+ ), this.queuedRegistrationBatches.length = 0;
2516
+ break;
2517
+ }
2518
+ e += 1, this.deliverRegistrationBatch(this.queuedRegistrationBatches.shift());
2519
+ }
2520
+ }
2521
+ /** @internal */
2522
+ deliverRegistrationBatch(e) {
2523
+ for (const t of [...this.registrationObservers])
2524
+ this.invokeRegistrationObserver(t, e);
2525
+ }
2526
+ /** @internal */
2527
+ invokeRegistrationObserver(e, t) {
2528
+ try {
2529
+ const n = e(t);
2530
+ process.env.NODE_ENV !== "production" && typeof n?.then == "function" && console.error(
2531
+ "[yoltra] A registration observer returned a Promise. Registration notifications are synchronous: the store has already moved on by the time it resolves. Do the work synchronously, or schedule it yourself and accept that the topology may have changed again."
2532
+ );
2533
+ } catch (n) {
2534
+ console.error("Registration observer error:", n);
1913
2535
  }
1914
- return n;
2536
+ }
2537
+ get isReplaying() {
2538
+ return this.replaying;
1915
2539
  }
1916
2540
  /**
1917
2541
  * Subscribe to events by channel and type.
@@ -1924,8 +2548,15 @@ class x {
1924
2548
  * - `'committed'` (default): Events that passed middleware and reached reducers.
1925
2549
  * Notified after reducers, before effects.
1926
2550
  * - `'uncommitted'`: Events rejected by middleware. Notified immediately after rejection.
2551
+ * - `'written'`: Events that actually changed state. Stricter than `committed`, which
2552
+ * fires for every event a store accepts including one with no reducers at all.
1927
2553
  * - `'all'`: Both committed and uncommitted events. Handler receives the phase parameter
1928
- * to distinguish between the two.
2554
+ * to distinguish between the two. Deliberately not `written` as well: an event that
2555
+ * writes is also committed, so folding it in would notify every existing `all`
2556
+ * subscriber twice for one event.
2557
+ *
2558
+ * **Replay:** a handler is not called while devtools is replaying, unless it opted in with
2559
+ * `{ duringReplay: true }`.
1929
2560
  *
1930
2561
  * @typeParam C - Channel key within `EM`.
1931
2562
  * @typeParam T - Event type key within channel `C`.
@@ -1959,11 +2590,16 @@ class x {
1959
2590
  *
1960
2591
  * @public
1961
2592
  */
1962
- onEvent(e, t, s, n = "committed") {
1963
- const r = `${e}::${String(t)}`, o = n === "committed" ? this.committedEventSubscribers : n === "uncommitted" ? this.uncommittedEventSubscribers : n === "written" ? this.writtenEventSubscribers : this.allEventSubscribers;
1964
- return o.has(r) || o.set(r, /* @__PURE__ */ new Set()), o.get(r).add(s), () => {
1965
- const l = o.get(r);
1966
- l && (l.delete(s), l.size === 0 && o.delete(r));
2593
+ onEvent(e, t, n, r = "committed", i) {
2594
+ const s = `${e}::${String(t)}`, l = r === "committed" ? this.committedEventSubscribers : r === "uncommitted" ? this.uncommittedEventSubscribers : r === "written" ? this.writtenEventSubscribers : this.allEventSubscribers;
2595
+ l.has(s) || l.set(s, /* @__PURE__ */ new Set());
2596
+ const c = {
2597
+ handler: n,
2598
+ duringReplay: i?.duringReplay === !0
2599
+ };
2600
+ return l.get(s).add(c), () => {
2601
+ const a = l.get(s);
2602
+ a && (a.delete(c), a.size === 0 && l.delete(s));
1967
2603
  };
1968
2604
  }
1969
2605
  /**
@@ -2037,10 +2673,86 @@ class x {
2037
2673
  * @public
2038
2674
  */
2039
2675
  registerMiddleware(e) {
2040
- return this.middleware.push(e), () => {
2041
- const t = this.middleware.indexOf(e);
2042
- t !== -1 && this.middleware.splice(t, 1);
2043
- };
2676
+ const t = { input: e, origin: "dynamic" };
2677
+ return this.middleware.push(t), this.recordMiddlewareChange(t, "mounted"), this.asRegistration(() => {
2678
+ const n = this.middleware.indexOf(t);
2679
+ n !== -1 && (this.middleware.splice(n, 1), this.recordMiddlewareChange(t, "unmounted"));
2680
+ });
2681
+ }
2682
+ /**
2683
+ * Turns a disposer into the callable object `register*` returns.
2684
+ *
2685
+ * @remarks
2686
+ * `Object.assign` onto the function rather than a new object, so every existing call site
2687
+ * keeps working verbatim: `const off = store.registerEffect(spec); off();` compiles and
2688
+ * runs exactly as before, while `.store` and `.dispose` become available to a library that
2689
+ * wants the widened type.
2690
+ *
2691
+ * A callable object rather than an overload or a second method: an overload cannot change
2692
+ * the return shape based on nothing, and a parallel `registerSliceX` family would leave
2693
+ * the originals permanently second class and force libraries to branch on the core version.
2694
+ *
2695
+ * @internal
2696
+ */
2697
+ recordMiddlewareChange(e, t) {
2698
+ this.recordRegistrationChange(() => {
2699
+ const n = typeof e.input == "function" ? void 0 : e.input.meta;
2700
+ return {
2701
+ kind: "middleware",
2702
+ op: t,
2703
+ name: n?.name ?? (typeof e.input == "function" ? e.input.name : void 0),
2704
+ description: n?.description,
2705
+ origin: e.origin,
2706
+ when: A(e.input),
2707
+ dispatch: "pattern"
2708
+ };
2709
+ });
2710
+ }
2711
+ /**
2712
+ * Drops an effect's metadata only once nothing is still registered with it.
2713
+ *
2714
+ * @remarks
2715
+ * `effectMeta` is keyed by the effect *function*, and the same function can legitimately
2716
+ * back several registrations. Deleting on the first disposal stripped the name and
2717
+ * description of the ones still live, which a devtools panel then showed as unnamed.
2718
+ *
2719
+ * @internal
2720
+ */
2721
+ releaseEffectMeta(e) {
2722
+ for (const t of this.effects.values())
2723
+ for (const n of t) if (n.effect === e) return;
2724
+ for (const t of this.patternEffects) if (t.effect === e) return;
2725
+ this.effectMeta.delete(e);
2726
+ }
2727
+ /** @internal */
2728
+ recordEffectChange(e, t, n, r, i) {
2729
+ this.recordRegistrationChange(() => ({
2730
+ kind: "effect",
2731
+ op: r,
2732
+ name: this.effectMeta.get(e)?.name,
2733
+ description: this.effectMeta.get(e)?.description,
2734
+ origin: n,
2735
+ when: t,
2736
+ dispatch: i
2737
+ }));
2738
+ }
2739
+ /** @internal */
2740
+ asRegistration(e) {
2741
+ return Object.assign(e, { store: this, dispose: e });
2742
+ }
2743
+ /**
2744
+ * The one place the widening cast lives.
2745
+ *
2746
+ * @remarks
2747
+ * Decoration is type-level only. This returns the **same runtime object**: subscriptions,
2748
+ * effects, middleware, the dedup cache, both buses and any in-flight `call()` are
2749
+ * untouched, and nothing re-subscribes. Keeping the cast here means no call site needs one,
2750
+ * which is the entire point of the feature.
2751
+ *
2752
+ * @internal
2753
+ */
2754
+ widened() {
2755
+ return this;
2044
2756
  }
2045
2757
  /**
2046
2758
  * Dynamically **adds** a named slice reducer at runtime.
@@ -2064,14 +2776,65 @@ class x {
2064
2776
  *
2065
2777
  * @public
2066
2778
  */
2067
- registerReducer(e, t) {
2779
+ registerReducer(e, t, n) {
2780
+ return this.registerSlice(e, t, n);
2781
+ }
2782
+ /**
2783
+ * Mounts a slice and hands back the widened store alongside a disposer.
2784
+ *
2785
+ * @remarks
2786
+ * The name `registerSlice` is what the guide uses; `registerReducer` keeps its broader
2787
+ * `name: string` signature and delegates here, so existing call sites are untouched.
2788
+ *
2789
+ * @public
2790
+ */
2791
+ registerSlice(e, t, n) {
2792
+ return this.inRegistrationTransaction(() => this.registerSliceInner(e, t, n));
2793
+ }
2794
+ /** @internal */
2795
+ registerSliceInner(e, t, n) {
2068
2796
  if (Object.prototype.hasOwnProperty.call(this.reducers, e))
2069
2797
  throw new Error(`Reducer ${e} already exists`);
2070
2798
  return this.mountSlice(e, t, {
2071
- preserveState: !1
2072
- }), this.listeners.forEach((s) => s()), () => {
2073
- this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((s) => s());
2074
- };
2799
+ preserveState: !1,
2800
+ origin: "dynamic",
2801
+ owner: n?.owner
2802
+ }), this.listeners.forEach((r) => r()), this.announceMountedSlice(e, this.state[e]), this.asRegistration(() => {
2803
+ Object.prototype.hasOwnProperty.call(this.reducers, e) && this.inRegistrationTransaction(() => {
2804
+ this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((r) => r());
2805
+ });
2806
+ });
2807
+ }
2808
+ /**
2809
+ * Mounts a slice and returns the widened store, for chaining.
2810
+ *
2811
+ * @remarks
2812
+ * No disposer, deliberately. After a disposer runs, the widened type still promises a
2813
+ * slice that is gone, and TypeScript cannot express "valid until that call". The chaining
2814
+ * API therefore does not hand one out, so the footgun does not exist on the path most
2815
+ * people take; {@link registerSlice} carries one for the library that owns the slice, and
2816
+ * the documented rule is that a disposer stays library-private.
2817
+ *
2818
+ * @public
2819
+ */
2820
+ withSlice(e, t, n) {
2821
+ return this.registerSlice(e, t, n), this.widened();
2822
+ }
2823
+ /**
2824
+ * Registers middleware and returns the widened store, for chaining.
2825
+ *
2826
+ * @public
2827
+ */
2828
+ withMiddleware(e) {
2829
+ return this.registerMiddleware(e), this.widened();
2830
+ }
2831
+ /**
2832
+ * Registers an effect and returns the widened store, for chaining.
2833
+ *
2834
+ * @public
2835
+ */
2836
+ withEffect(e) {
2837
+ return this.registerEffect(e), this.widened();
2075
2838
  }
2076
2839
  /**
2077
2840
  * Registers an **effect** (stateless async event consumer) that runs after reducers.
@@ -2168,13 +2931,7 @@ class x {
2168
2931
  * the terminal event from ever being sent. Un-iterated progress therefore buffers to
2169
2932
  * `highWaterMark` and is then counted on {@link CallHandle.dropped} rather than blocking.
2170
2933
  *
2171
- * **This is a local primitive.** A reply cannot reach it from a federated peer: the federation
2172
- * envelope carries neither `meta` nor `parentId`, and ingress namespaces the channel, so
2173
- * neither correlation nor the reply route survives the hop. That is not an oversight to route
2174
- * around — federation answers cross-node request/reply with typed peer *queries*, which are
2175
- * gated by a responder policy that may concede or deny. A call that federated silently would
2176
- * turn that access decision into an accident of which channel someone named. Ask a peer with a
2177
- * query; use `call` within a process.
2934
+ * **This is a local primitive.**
2178
2935
  *
2179
2936
  * @example Timeout is idle, not total
2180
2937
  * ```ts
@@ -2190,84 +2947,65 @@ class x {
2190
2947
  *
2191
2948
  * @public
2192
2949
  */
2193
- call(e, t, s, n) {
2194
- const { channel: r, isTerminal: o } = q(n.reply), l = n.timeoutMs ?? ee, i = new G(n.highWaterMark ?? te), a = this.idFactory();
2195
- let d, u, f = !1;
2196
- const m = new Promise((b, S) => {
2197
- d = b, u = S;
2198
- });
2199
- m.catch(() => {
2200
- });
2201
- let h = null, y = null;
2202
- const g = (b, S = !1) => {
2203
- f || (f = !0, h !== null && clearTimeout(h), h = null, y?.(), y = null, S ? i.end() : i.close(), n.signal?.removeEventListener("abort", w), b());
2204
- };
2205
- function w() {
2206
- g(() => u(new C(String(n.signal?.reason ?? "signal aborted"))));
2207
- }
2208
- const k = () => {
2209
- h !== null && clearTimeout(h), h = setTimeout(() => {
2210
- g(() => u(new J(e, t, l)));
2211
- }, l), h.unref?.();
2212
- };
2213
- return y = this.registerEffect({
2214
- // A pattern effect on the reply channel: which types are terminal is known, which are
2215
- // progress is not, so the filter cannot be a key list.
2216
- when: { channel: r },
2217
- effect: async (b) => {
2218
- if (!f && Y(b, a, n.correlationId)) {
2219
- if (k(), o(String(b.type))) {
2220
- g(() => d(b), !0);
2221
- return;
2222
- }
2223
- await i.put(b);
2224
- }
2225
- }
2226
- }), n.signal !== void 0 && (n.signal.aborted ? w() : n.signal.addEventListener("abort", w, { once: !0 })), k(), this.emit(e, t, s, {
2227
- id: a,
2228
- ...n.correlationId !== void 0 ? { meta: { correlationId: n.correlationId } } : {}
2229
- }), {
2230
- then: (b, S) => m.then(b, S),
2231
- catch: (b) => m.catch(b),
2232
- finally: (b) => m.finally(b),
2233
- get dropped() {
2234
- return i.droppedCount;
2950
+ call(e, t, n, r) {
2951
+ return se(
2952
+ {
2953
+ idFactory: this.idFactory,
2954
+ // `internal`, so an in-flight call survives even `replaceEffects(next, { scope: "all" })`.
2955
+ // A test harness resetting a store between cases never means "and abandon the call
2956
+ // that is currently awaiting a reply", and the symptom would be a hang to the idle
2957
+ // timeout with nothing pointing at the reset.
2958
+ registerEffect: (i) => this.registerEffectWithOrigin(i, "internal"),
2959
+ emit: this.emit
2235
2960
  },
2236
- cancel: (b = "cancelled") => {
2237
- g(() => u(new C(b)));
2238
- },
2239
- [Symbol.asyncIterator]: () => (i.beginConsuming(), {
2240
- next: () => i.take(),
2241
- // Called by `for await` on `break`, `return` or a throw. Without it, abandoning the
2242
- // loop would leave the effect registered and the producer parked for good.
2243
- return: async () => (i.close(), { value: void 0, done: !0 })
2244
- })
2245
- };
2961
+ e,
2962
+ t,
2963
+ n,
2964
+ r
2965
+ );
2246
2966
  }
2247
2967
  registerEffect(e) {
2248
- const { effect: t, meta: s, when: n } = e, r = [];
2249
- if (s && this.effectMeta.set(t, s), n && ("any" in n && n.any === !0 || "channel" in n || "channels" in n)) {
2250
- const i = { effect: t, when: n };
2251
- return this.patternEffects.add(i), () => {
2252
- this.patternEffects.delete(i);
2968
+ return this.asRegistration(this.registerEffectWithOrigin(e, "dynamic"));
2969
+ }
2970
+ /**
2971
+ * {@link registerEffect}, with the provenance the caller cannot set.
2972
+ *
2973
+ * @remarks
2974
+ * Kept private so no origin parameter leaks into `StoreInstance`. Three callers: the
2975
+ * constructor (`spec`), the public method (`dynamic`), and `store.call()` (`internal`).
2976
+ *
2977
+ * @internal
2978
+ */
2979
+ registerEffectWithOrigin(e, t) {
2980
+ const { effect: n, meta: r, when: i } = e, s = [];
2981
+ if (r && this.effectMeta.set(n, r), i && ("any" in i && i.any === !0 || "channel" in i || "channels" in i)) {
2982
+ const a = { effect: n, when: i, origin: t };
2983
+ return this.patternEffects.add(a), this.recordEffectChange(n, i, t, "mounted", "pattern"), () => {
2984
+ this.recordEffectChange(n, i, t, "unmounted", "pattern"), this.patternEffects.delete(a), this.releaseEffectMeta(n);
2253
2985
  };
2254
2986
  }
2255
- const l = this.normalizeEventKeys(e);
2256
- if (l.length === 0 && !n) {
2257
- const i = { effect: t, when: { any: !0 } };
2258
- return this.patternEffects.add(i), () => {
2259
- this.patternEffects.delete(i);
2987
+ const c = U(e);
2988
+ if (c.length === 0 && !i) {
2989
+ const a = { any: !0 }, d = { effect: n, when: a, origin: t };
2990
+ return this.patternEffects.add(d), this.recordEffectChange(n, a, t, "mounted", "pattern"), () => {
2991
+ this.recordEffectChange(n, a, t, "unmounted", "pattern"), this.patternEffects.delete(d), this.releaseEffectMeta(n);
2260
2992
  };
2261
2993
  }
2262
- for (const [i, a] of l) {
2263
- const d = `${String(i)}::${String(a)}`;
2264
- this.effects.has(d) || this.effects.set(d, /* @__PURE__ */ new Set()), this.effects.get(d).add(t), r.push(() => {
2265
- const u = this.effects.get(d);
2266
- u && (u.delete(t), u.size === 0 && this.effects.delete(d));
2994
+ for (const [a, d] of c) {
2995
+ const f = `${String(a)}::${String(d)}`;
2996
+ this.effects.has(f) || this.effects.set(f, /* @__PURE__ */ new Set());
2997
+ const u = { effect: n, origin: t };
2998
+ this.effects.get(f).add(u);
2999
+ const p = { keys: [[a, d]] };
3000
+ this.recordEffectChange(n, p, t, "mounted", "keyed"), s.push(() => {
3001
+ this.recordEffectChange(n, p, t, "unmounted", "keyed");
3002
+ const h = this.effects.get(f);
3003
+ h && (h.delete(u), h.size === 0 && this.effects.delete(f));
2267
3004
  });
2268
3005
  }
2269
3006
  return () => {
2270
- for (const i of r) i();
3007
+ for (const a of s) a();
3008
+ this.releaseEffectMeta(n);
2271
3009
  };
2272
3010
  }
2273
3011
  /**
@@ -2291,15 +3029,15 @@ class x {
2291
3029
  *
2292
3030
  * @public
2293
3031
  */
2294
- onEffect(e, t, s) {
2295
- const n = async (r, o, l) => {
2296
- if (r.channel !== e || r.type !== t) return;
2297
- const i = r;
2298
- return s(i.payload, o, l, i);
3032
+ onEffect(e, t, n) {
3033
+ const r = async (i, s, l) => {
3034
+ if (i.channel !== e || i.type !== t) return;
3035
+ const c = i;
3036
+ return n(c.payload, s, l, c);
2299
3037
  };
2300
3038
  return this.registerEffect({
2301
3039
  when: { keys: [[e, t]] },
2302
- effect: n
3040
+ effect: r
2303
3041
  });
2304
3042
  }
2305
3043
  /**
@@ -2318,9 +3056,23 @@ class x {
2318
3056
  *
2319
3057
  * @public
2320
3058
  */
2321
- replaceMiddleware(e) {
3059
+ replaceMiddleware(e, t = {}) {
3060
+ this.inRegistrationTransaction(() => this.replaceMiddlewareInner(e, t));
3061
+ }
3062
+ /** @internal */
3063
+ replaceMiddlewareInner(e, t = {}) {
3064
+ const n = t.scope ?? "spec", r = this.middleware.filter(
3065
+ (i) => n === "all" ? i.origin === "internal" : i.origin !== "spec"
3066
+ );
3067
+ for (const i of this.middleware)
3068
+ r.includes(i) || this.recordMiddlewareChange(i, "unmounted");
2322
3069
  this.middleware.length = 0;
2323
- for (const t of e) this.middleware.push(t);
3070
+ for (const i of e) {
3071
+ const s = { input: i, origin: "spec" };
3072
+ this.middleware.push(s), this.recordMiddlewareChange(s, "mounted");
3073
+ }
3074
+ for (const i of r) this.middleware.push(i);
3075
+ this.reportPreserved("replaceMiddleware", r.length, n);
2324
3076
  }
2325
3077
  /**
2326
3078
  * Replaces all registered **effects** (HMR-friendly).
@@ -2338,16 +3090,53 @@ class x {
2338
3090
  *
2339
3091
  * @public
2340
3092
  */
2341
- replaceEffects(e) {
2342
- this.effects.clear(), this.patternEffects.clear();
2343
- for (const t of e)
2344
- this.registerEffect(t);
3093
+ replaceEffects(e, t = {}) {
3094
+ this.inRegistrationTransaction(() => this.replaceEffectsInner(e, t));
3095
+ }
3096
+ /** @internal */
3097
+ replaceEffectsInner(e, t = {}) {
3098
+ const n = t.scope ?? "spec", r = (s) => n === "all" ? s === "internal" : s !== "spec";
3099
+ let i = 0;
3100
+ for (const [s, l] of this.effects) {
3101
+ for (const c of [...l]) {
3102
+ if (r(c.origin)) {
3103
+ i += 1;
3104
+ continue;
3105
+ }
3106
+ this.recordEffectChange(
3107
+ c.effect,
3108
+ { keys: [s.split("::")] },
3109
+ c.origin,
3110
+ "unmounted",
3111
+ "keyed"
3112
+ ), l.delete(c), this.releaseEffectMeta(c.effect);
3113
+ }
3114
+ l.size === 0 && this.effects.delete(s);
3115
+ }
3116
+ for (const s of [...this.patternEffects]) {
3117
+ if (r(s.origin)) {
3118
+ i += 1;
3119
+ continue;
3120
+ }
3121
+ this.recordEffectChange(s.effect, s.when, s.origin, "unmounted", "pattern"), this.patternEffects.delete(s), this.releaseEffectMeta(s.effect);
3122
+ }
3123
+ for (const s of e)
3124
+ this.registerEffectWithOrigin(s, "spec");
3125
+ this.reportPreserved("replaceEffects", i, n);
2345
3126
  }
2346
3127
  /**
2347
3128
  * Replaces the entire **reducer set** (HMR-friendly).
2348
3129
  *
2349
3130
  * @param next - Map of slice specs keyed by slice name.
2350
- * @param opts - `{ preserveState?: boolean }` (default `true`).
3131
+ * @param opts - `{ preserveState?: boolean }` (default `true`) and
3132
+ * `{ scope?: "spec" | "all" }` (default `"spec"`).
3133
+ *
3134
+ * @remarks
3135
+ * Replaces **spec-provenance slices only**. A slice mounted after construction with
3136
+ * `registerSlice` survives, along with its state: it was never part of the set this call
3137
+ * is replacing. Pass `{ scope: "all" }` for the pre-0.8.0 wholesale behaviour.
3138
+ *
3139
+ * Throws, before mutating anything, if `next` names a slice a library mounted at runtime.
2351
3140
  *
2352
3141
  * @example Hot module replacement
2353
3142
  * ```ts
@@ -2361,11 +3150,67 @@ class x {
2361
3150
  * @public
2362
3151
  */
2363
3152
  replaceReducers(e, t = {}) {
2364
- const s = t.preserveState !== !1, n = new Set(Object.keys(this.reducers)), r = Object.entries(e), o = new Set(r.map(([l]) => l));
2365
- for (const l of n)
2366
- o.has(l) || this.unmountSlice(l, { deleteState: !0 });
2367
- for (const [l, i] of r)
2368
- n.has(l) ? (this.unmountSlice(l, { deleteState: !1 }), this.mountSlice(l, i, { preserveState: s })) : this.mountSlice(l, i, { preserveState: !1 });
3153
+ this.inRegistrationTransaction(() => this.replaceReducersInner(e, t));
3154
+ }
3155
+ /** @internal */
3156
+ replaceReducersInner(e, t = {}) {
3157
+ const n = t.preserveState !== !1, r = t.scope ?? "spec", i = new Set(Object.keys(this.reducers)), s = Object.entries(e), l = new Set(s.map(([d]) => d));
3158
+ this.assertNoSliceCollision(e, r);
3159
+ const c = this.state;
3160
+ let a = 0;
3161
+ for (const d of i) {
3162
+ if (l.has(d)) continue;
3163
+ const f = this.sliceOrigin.get(d) ?? "spec";
3164
+ if (!(r === "all" ? f !== "internal" : f === "spec")) {
3165
+ a += 1;
3166
+ continue;
3167
+ }
3168
+ this.unmountSlice(d, { deleteState: !0 });
3169
+ }
3170
+ for (const [d, f] of s)
3171
+ i.has(d) ? (this.unmountSlice(d, { deleteState: !1 }), this.mountSlice(d, f, { preserveState: n, origin: "spec" })) : this.mountSlice(d, f, { preserveState: !1, origin: "spec" });
3172
+ this.state !== c && this.listeners.forEach((d) => d()), this.reportPreserved("replaceReducers", a, r);
3173
+ }
3174
+ /**
3175
+ * Refuses, before anything is mutated, to take over a slice mounted at runtime.
3176
+ *
3177
+ * @remarks
3178
+ * An application authoring a slice a library owns is a real mistake, and a silent takeover
3179
+ * is the worst available outcome: the library keeps a disposer for a slice that is no
3180
+ * longer its own. Throwing part-way through would be worse still, which is why this runs
3181
+ * as a pre-flight and why `hotReplace` calls it before swapping anything at all.
3182
+ *
3183
+ * @internal
3184
+ */
3185
+ assertNoSliceCollision(e, t) {
3186
+ if (t !== "spec") return;
3187
+ const n = Object.keys(e).filter(
3188
+ (i) => this.sliceOrigin.get(i) === "dynamic"
3189
+ );
3190
+ if (n.length === 0) return;
3191
+ const r = n.map((i) => {
3192
+ const s = this.sliceOwner.get(i);
3193
+ return s === void 0 ? `"${i}"` : `"${i}" (owner: ${s})`;
3194
+ }).join(", ");
3195
+ throw new Error(
3196
+ `[yoltra] replaceReducers would take over ${n.length === 1 ? "a slice" : "slices"} mounted at runtime: ${r}. Rename the slice, or pass { scope: "all" } to replace it deliberately.`
3197
+ );
3198
+ }
3199
+ /**
3200
+ * Says what a `replace*` call left alone, when it left anything alone.
3201
+ *
3202
+ * @remarks
3203
+ * Development only, and silent unless something was actually preserved, so the normal HMR
3204
+ * path stays quiet. `console.debug` rather than `warn`: this is correct operation, and
3205
+ * every existing `warn` in this file marks a genuine problem. It exists so "why is that
3206
+ * effect still firing after a reload" has an answer that does not require reading core.
3207
+ *
3208
+ * @internal
3209
+ */
3210
+ reportPreserved(e, t, n) {
3211
+ t !== 0 && process.env.NODE_ENV !== "production" && console.debug(
3212
+ `[yoltra] ${e} preserved ${t} registration${t === 1 ? "" : "s"} made after construction.${n === "spec" ? ' Pass { scope: "all" } to replace them too.' : ""}`
3213
+ );
2369
3214
  }
2370
3215
  /**
2371
3216
  * Convenience API to replace **any subset** of store parts (HMR patterns).
@@ -2385,7 +3230,10 @@ class x {
2385
3230
  * @public
2386
3231
  */
2387
3232
  hotReplace(e) {
2388
- e.middleware && this.replaceMiddleware(e.middleware), e.effects && this.replaceEffects(e.effects), e.reducer && this.replaceReducers(e.reducer, { preserveState: e.preserveState });
3233
+ const t = e.scope;
3234
+ e.reducer && this.assertNoSliceCollision(e.reducer, t ?? "spec"), this.inRegistrationTransaction(() => {
3235
+ e.middleware && this.replaceMiddleware(e.middleware, { scope: t }), e.effects && this.replaceEffects(e.effects, { scope: t }), e.reducer && this.replaceReducers(e.reducer, { preserveState: e.preserveState, scope: t });
3236
+ });
2389
3237
  }
2390
3238
  /**
2391
3239
  * Mounts a slice: installs reducer, initializes state (unless preserved),
@@ -2397,36 +3245,99 @@ class x {
2397
3245
  *
2398
3246
  * @internal
2399
3247
  */
2400
- mountSlice(e, t, s) {
2401
- const n = e, { reducer: r, state: o, when: l } = t;
2402
- if (this.reducers[e] = new V(r), (!s.preserveState || this.state[n] === void 0) && (this.state = {
3248
+ mountSlice(e, t, n) {
3249
+ this.mountSliceInner(e, t, n), this.recordSliceChange(
3250
+ e,
3251
+ "mounted",
3252
+ n.preserveState ? "preserved" : "initialized"
3253
+ );
3254
+ }
3255
+ /** @internal */
3256
+ mountSliceInner(e, t, n) {
3257
+ const r = e;
3258
+ this.sliceOrigin.set(r, n.origin ?? "spec"), n.owner !== void 0 && this.sliceOwner.set(r, n.owner), this.disposedSlices.delete(r);
3259
+ const { reducer: i, state: s, when: l } = t;
3260
+ if (this.reducers[e] = new X(i), (!n.preserveState || this.state[r] === void 0) && (this.state = {
2403
3261
  ...this.state,
2404
- [n]: R(X(n, o))
3262
+ [r]: T(pe(r, s))
2405
3263
  }), l && ("any" in l && l.any === !0 || "channel" in l || "channels" in l)) {
2406
- this.patternReducers.set(e, l), this.sliceUnsubs.set(n, []);
3264
+ this.patternReducers.set(e, l), this.sliceUnsubs.set(r, []);
2407
3265
  return;
2408
3266
  }
2409
- const a = this.normalizeEventKeys(t);
3267
+ const a = U(t);
2410
3268
  if (a.length === 0 && !l) {
2411
- this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(n, []);
3269
+ this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(r, []);
2412
3270
  return;
2413
3271
  }
2414
3272
  const d = [];
2415
- for (const [u, f] of a) {
2416
- const m = this.reducerBus.on(u, f, (h, y) => {
2417
- const g = y ?? {
2418
- channel: u,
2419
- type: f,
3273
+ for (const [f, u] of a) {
3274
+ const p = this.reducerBus.on(f, u, (h, y) => {
3275
+ const w = y ?? {
3276
+ channel: f,
3277
+ type: u,
2420
3278
  payload: h,
2421
3279
  id: this.idFactory()
2422
3280
  };
2423
3281
  if (this.stagingSink === null) return;
2424
- const w = this.stageSliceGuarded(e, g, this.stagingSink);
2425
- w !== null && this.stagedRejection === null && (this.stagedRejection = w, this.stagedRejectedBy = e);
3282
+ const g = this.stageSliceGuarded(e, w, this.stagingSink);
3283
+ g !== null && this.stagedRejection === null && (this.stagedRejection = g, this.stagedRejectedBy = e);
3284
+ });
3285
+ d.push(p);
3286
+ }
3287
+ this.sliceUnsubs.set(r, d);
3288
+ }
3289
+ /**
3290
+ * Records a slice mount or unmount for {@link onRegistrationChange}.
3291
+ *
3292
+ * @internal
3293
+ */
3294
+ recordSliceChange(e, t, n) {
3295
+ this.recordRegistrationChange(() => ({
3296
+ kind: "reducer",
3297
+ op: t,
3298
+ name: e,
3299
+ origin: this.sliceOrigin.get(e) ?? "spec",
3300
+ owner: this.sliceOwner.get(e),
3301
+ when: this.patternReducers.get(e),
3302
+ state: n,
3303
+ dispatch: this.patternReducers.has(e) ? "pattern" : "keyed"
3304
+ }));
3305
+ }
3306
+ /**
3307
+ * Announces a newly mounted slice on the connector bus.
3308
+ *
3309
+ * @remarks
3310
+ * `registerReducer` has always broadcast to `listeners`, which wakes `subscribe` and so
3311
+ * `useSelector`. It emitted **nothing** on `connectorBus`, which is what `connect` rides,
3312
+ * so `useAtomicProp`, `useAtomicProps` and the Suspense hooks never woke for a slice
3313
+ * mounted after creation: a component subscribed to a path inside it simply never
3314
+ * re-rendered. That makes the decoration story ship a documented-as-working path that does
3315
+ * not work, which is why this is here rather than filed as a follow-up.
3316
+ *
3317
+ * Scope, stated precisely because it is narrower than it looks: this emits the slice root
3318
+ * and its **top-level** keys, which is exactly what an ordinary commit emits when a
3319
+ * subtree first appears - `detectChangedProps` reports a newly-appearing branch at its
3320
+ * root, not leaf by leaf. So a `connect` on `"deep.n"` does not fire here, and does not
3321
+ * fire on a normal commit that first creates `deep` either. Consistent, not complete.
3322
+ *
3323
+ * Skipped when nothing is subscribed, and skipped entirely during construction, where no
3324
+ * subscriber can exist yet.
3325
+ *
3326
+ * @internal
3327
+ */
3328
+ announceMountedSlice(e, t) {
3329
+ const r = I(typeof t == "object" && t !== null ? {} : void 0, t), i = /* @__PURE__ */ new Set([""]);
3330
+ for (const s of r)
3331
+ if (s !== "")
3332
+ for (const l of k.buildAncestorPaths(s)) i.add(l);
3333
+ for (const s of i) {
3334
+ const l = this.getAtPath(this.state[e], s);
3335
+ this.connectorBus.emit(e, s, {
3336
+ oldValue: void 0,
3337
+ newValue: l,
3338
+ path: s
2426
3339
  });
2427
- d.push(m);
2428
3340
  }
2429
- this.sliceUnsubs.set(n, d);
2430
3341
  }
2431
3342
  /**
2432
3343
  * Unmounts a slice: disposes reducer-bus listeners, removes reducer,
@@ -2438,38 +3349,28 @@ class x {
2438
3349
  * @internal
2439
3350
  */
2440
3351
  unmountSlice(e, t) {
2441
- const s = e;
2442
- this.patternReducers.delete(e);
2443
- const n = this.sliceUnsubs.get(s);
2444
- if (n) {
2445
- for (const r of n)
3352
+ const n = e;
3353
+ if (this.recordSliceChange(n, "unmounted", t.deleteState ? "deleted" : "retained"), this.patternReducers.delete(e), process.env.NODE_ENV !== "production") {
3354
+ const i = this.sliceOrigin.get(n);
3355
+ if (i === "dynamic" || i === "internal")
3356
+ for (this.disposedSlices.set(n, this.sliceOwner.get(n)); this.disposedSlices.size > ge; )
3357
+ this.disposedSlices.delete(this.disposedSlices.keys().next().value);
3358
+ }
3359
+ this.sliceOrigin.delete(n), this.sliceOwner.delete(n);
3360
+ const r = this.sliceUnsubs.get(n);
3361
+ if (r) {
3362
+ for (const i of r)
2446
3363
  try {
2447
- r();
2448
- } catch (o) {
2449
- console.error(`[Store error]: ${o}`);
3364
+ i();
3365
+ } catch (s) {
3366
+ console.error(`[Store error]: ${s}`);
2450
3367
  }
2451
- this.sliceUnsubs.delete(s);
3368
+ this.sliceUnsubs.delete(n);
2452
3369
  }
2453
3370
  if (delete this.reducers[e], t.deleteState) {
2454
- const { [s]: r, ...o } = this.state;
2455
- this.state = o;
2456
- }
2457
- }
2458
- /**
2459
- * Normalizes event targeting from `when` to an array of EventKeys.
2460
- *
2461
- * @param spec - Object with an optional `when` matcher.
2462
- * @returns Array of `[channel, type]` pairs.
2463
- *
2464
- * @internal
2465
- */
2466
- normalizeEventKeys(e) {
2467
- if (e.when) {
2468
- const t = e.when;
2469
- if ("keys" in t)
2470
- return t.keys;
3371
+ const { [n]: i, ...s } = this.state;
3372
+ this.state = s;
2471
3373
  }
2472
- return [];
2473
3374
  }
2474
3375
  /**
2475
3376
  * Reads a dotted path from an object (supports numeric array indices via string keys).
@@ -2478,17 +3379,14 @@ class x {
2478
3379
  * @param path - Dotted path; leading dot is ignored.
2479
3380
  * @returns The value at the path, or `undefined`.
2480
3381
  *
3382
+ * @remarks
3383
+ * A member rather than a bare import: a test replaces this on the instance to count how many
3384
+ * walks describing a change costs, which only works while the callers go through `this`.
3385
+ *
2481
3386
  * @internal
2482
3387
  */
2483
3388
  getAtPath(e, t) {
2484
- if (!t) return e;
2485
- const n = (t[0] === "." ? t.slice(1) : t).split(".");
2486
- let r = e;
2487
- for (const o of n) {
2488
- if (r == null) return;
2489
- r = r[o];
2490
- }
2491
- return r;
3389
+ return oe(e, t);
2492
3390
  }
2493
3391
  /**
2494
3392
  * Builds ancestor paths for a dotted path.
@@ -2506,368 +3404,215 @@ class x {
2506
3404
  * @public
2507
3405
  */
2508
3406
  static buildAncestorPaths(e) {
2509
- if (!e) return [];
2510
- const s = (e[0] === "." ? e.slice(1) : e).split("."), n = [];
2511
- for (let r = 0; r < s.length; r++)
2512
- n.push(s.slice(0, r + 1).join("."));
2513
- return n;
3407
+ return ce(e);
2514
3408
  }
2515
3409
  }
2516
- function de(c) {
2517
- return new x({
2518
- ...c,
2519
- reducer: c.reducer ?? {},
2520
- middleware: c.middleware ?? [],
2521
- effects: c.effects ?? []
3410
+ function ke(o) {
3411
+ return new k({
3412
+ ...o,
3413
+ reducer: o.reducer ?? {},
3414
+ middleware: o.middleware ?? [],
3415
+ effects: o.effects ?? []
2522
3416
  });
2523
3417
  }
2524
- const ue = (c) => (e, t) => t.map((s) => [e, s]), fe = () => (c) => c, _ = /* @__PURE__ */ new Set();
2525
- function re(c) {
2526
- const e = String(c);
2527
- _.has(e) || (_.add(e), console.warn(
3418
+ const $e = (o) => (e, t) => t.map((n) => [e, n]), Me = () => (o) => o, Ce = () => (o) => o, Pe = () => (o) => o, Ie = () => (o) => o, L = /* @__PURE__ */ new Set();
3419
+ function ve(o) {
3420
+ const e = String(o);
3421
+ L.has(e) || (L.add(e), console.warn(
2528
3422
  `[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.`
2529
3423
  ));
2530
3424
  }
2531
- function ie(c, e) {
2532
- if (c.length !== e.length) return e;
2533
- for (let t = 0; t < c.length; t++)
2534
- if (c[t] !== e[t]) return e;
2535
- return c;
3425
+ function Se(o, e) {
3426
+ if (o.length !== e.length) return e;
3427
+ for (let t = 0; t < o.length; t++)
3428
+ if (o[t] !== e[t]) return e;
3429
+ return o;
2536
3430
  }
2537
- function he(c = {}) {
2538
- const e = c.selectId ?? ((i) => i.id), { sortComparer: t } = c, s = (i, a) => {
3431
+ function Ae(o = {}) {
3432
+ const e = o.selectId ?? ((c) => c.id), { sortComparer: t } = o, n = (c, a) => {
2539
3433
  if (t === void 0) return a;
2540
- const d = [...a].sort((u, f) => {
2541
- const m = i.entities[u], h = i.entities[f];
2542
- return m === void 0 || h === void 0 ? 0 : t(m, h);
3434
+ const d = [...a].sort((f, u) => {
3435
+ const p = c.entities[f], h = c.entities[u];
3436
+ return p === void 0 || h === void 0 ? 0 : t(p, h);
2543
3437
  });
2544
- return ie(a, d);
2545
- }, n = (i, a, d) => {
2546
- const u = { ...i, entities: a, ids: d };
2547
- return { ...u, ids: s(u, d) };
2548
- }, r = (i, a, d) => {
2549
- let u = null, f = null;
2550
- for (const m of a) {
2551
- const h = e(m);
2552
- process.env.NODE_ENV !== "production" && String(h).includes(".") && re(h);
2553
- const y = (u ?? i.entities)[h];
3438
+ return Se(a, d);
3439
+ }, r = (c, a, d) => {
3440
+ const f = { ...c, entities: a, ids: d };
3441
+ return { ...f, ids: n(f, d) };
3442
+ }, i = (c, a, d) => {
3443
+ let f = null, u = null;
3444
+ for (const p of a) {
3445
+ const h = e(p);
3446
+ process.env.NODE_ENV !== "production" && String(h).includes(".") && ve(h);
3447
+ const y = (f ?? c.entities)[h];
2554
3448
  if (y !== void 0 && d === "add") continue;
2555
- const g = y !== void 0 && d === "upsert" ? { ...y, ...m } : m;
2556
- u ?? (u = { ...i.entities }), u[h] = g, y === void 0 && (f ?? (f = [...i.ids]), f.push(h));
3449
+ const w = y !== void 0 && d === "upsert" ? { ...y, ...p } : p;
3450
+ f ??= { ...c.entities }, f[h] = w, y === void 0 && (u ??= [...c.ids], u.push(h));
2557
3451
  }
2558
- return u === null ? i : n(i, u, f ?? i.ids);
2559
- }, o = (i, a) => {
3452
+ return f === null ? c : r(c, f, u ?? c.ids);
3453
+ }, s = (c, a) => {
2560
3454
  let d = null;
2561
- for (const { id: u, changes: f } of a) {
2562
- const m = (d ?? i.entities)[u];
2563
- m !== void 0 && (d ?? (d = { ...i.entities }), d[u] = { ...m, ...f });
2564
- }
2565
- return d === null ? i : n(i, d, i.ids);
2566
- }, l = (i, a) => {
2567
- const d = new Set(a.filter((f) => i.entities[f] !== void 0));
2568
- if (d.size === 0) return i;
2569
- const u = { ...i.entities };
2570
- for (const f of d) delete u[f];
2571
- return n(
2572
- i,
2573
- u,
2574
- i.ids.filter((f) => !d.has(f))
3455
+ for (const { id: f, changes: u } of a) {
3456
+ const p = (d ?? c.entities)[f];
3457
+ p !== void 0 && (d ??= { ...c.entities }, d[f] = { ...p, ...u });
3458
+ }
3459
+ return d === null ? c : r(c, d, c.ids);
3460
+ }, l = (c, a) => {
3461
+ const d = new Set(a.filter((u) => c.entities[u] !== void 0));
3462
+ if (d.size === 0) return c;
3463
+ const f = { ...c.entities };
3464
+ for (const u of d) delete f[u];
3465
+ return r(
3466
+ c,
3467
+ f,
3468
+ c.ids.filter((u) => !d.has(u))
2575
3469
  );
2576
3470
  };
2577
3471
  return {
2578
- getInitialState(i) {
3472
+ getInitialState(c) {
2579
3473
  const a = { ids: [], entities: {} };
2580
- return i === void 0 ? a : { ...a, ...i };
3474
+ return c === void 0 ? a : { ...a, ...c };
2581
3475
  },
2582
- addOne: (i, a) => r(i, [a], "add"),
2583
- addMany: (i, a) => r(i, a, "add"),
2584
- setOne: (i, a) => r(i, [a], "set"),
2585
- setMany: (i, a) => r(i, a, "set"),
2586
- setAll: (i, a) => {
2587
- const d = {}, u = [];
2588
- for (const f of a) {
2589
- const m = e(f);
2590
- d[m] === void 0 && u.push(m), d[m] = f;
3476
+ addOne: (c, a) => i(c, [a], "add"),
3477
+ addMany: (c, a) => i(c, a, "add"),
3478
+ setOne: (c, a) => i(c, [a], "set"),
3479
+ setMany: (c, a) => i(c, a, "set"),
3480
+ setAll: (c, a) => {
3481
+ const d = {}, f = [];
3482
+ for (const u of a) {
3483
+ const p = e(u);
3484
+ d[p] === void 0 && f.push(p), d[p] = u;
2591
3485
  }
2592
- return n(i, d, u);
3486
+ return r(c, d, f);
2593
3487
  },
2594
- updateOne: (i, a) => o(i, [a]),
2595
- updateMany: (i, a) => o(i, a),
2596
- upsertOne: (i, a) => r(i, [a], "upsert"),
2597
- upsertMany: (i, a) => r(i, a, "upsert"),
2598
- removeOne: (i, a) => l(i, [a]),
2599
- removeMany: (i, a) => l(i, a),
2600
- removeAll: (i) => i.ids.length === 0 ? i : n(i, {}, []),
2601
- selectIds: (i) => i.ids,
2602
- selectEntities: (i) => i.entities,
2603
- selectAll: (i) => i.ids.map((a) => i.entities[a]),
2604
- selectById: (i, a) => i.entities[a],
2605
- selectTotal: (i) => i.ids.length,
3488
+ updateOne: (c, a) => s(c, [a]),
3489
+ updateMany: (c, a) => s(c, a),
3490
+ upsertOne: (c, a) => i(c, [a], "upsert"),
3491
+ upsertMany: (c, a) => i(c, a, "upsert"),
3492
+ removeOne: (c, a) => l(c, [a]),
3493
+ removeMany: (c, a) => l(c, a),
3494
+ removeAll: (c) => c.ids.length === 0 ? c : r(c, {}, []),
3495
+ selectIds: (c) => c.ids,
3496
+ selectEntities: (c) => c.entities,
3497
+ selectAll: (c) => c.ids.map((a) => c.entities[a]),
3498
+ selectById: (c, a) => c.entities[a],
3499
+ selectTotal: (c) => c.ids.length,
2606
3500
  idsPath: "ids",
2607
- pathTo: (i, a) => a === void 0 ? `entities.${i}` : `entities.${i}.${a}`,
2608
- anyField: (i) => `entities.*.${i}`
2609
- };
2610
- }
2611
- const E = "$yoltra";
2612
- function B(c, e = {}) {
2613
- const t = e.maxNodes ?? 1e5, s = e.sanitize, n = [], r = /* @__PURE__ */ new Map();
2614
- let o = 0, l = !1;
2615
- function i(d, u) {
2616
- if (s !== void 0 && (d = s(u, d)), o += 1, o > t)
2617
- return l = !0, { [E]: "unsupported", kind: "truncated" };
2618
- switch (typeof d) {
2619
- case "undefined":
2620
- return { [E]: "undefined" };
2621
- case "bigint":
2622
- return { [E]: "bigint", value: d.toString() };
2623
- case "number":
2624
- return Number.isNaN(d) ? { [E]: "nan" } : d === 1 / 0 ? { [E]: "infinity", sign: 1 } : d === -1 / 0 ? { [E]: "infinity", sign: -1 } : d;
2625
- case "function":
2626
- case "symbol":
2627
- return n.push(u), { [E]: "unsupported", kind: typeof d };
2628
- case "string":
2629
- case "boolean":
2630
- return d;
2631
- }
2632
- if (d === null) return null;
2633
- const f = d, m = r.get(f);
2634
- if (m !== void 0) return { [E]: "ref", path: m };
2635
- if (r.set(f, u), d instanceof Date)
2636
- return { [E]: "date", iso: d.toISOString() };
2637
- if (d instanceof RegExp)
2638
- return { [E]: "regexp", source: d.source, flags: d.flags };
2639
- if (d instanceof Error)
2640
- return { [E]: "error", name: d.name, message: d.message };
2641
- if (d instanceof Map) {
2642
- const y = [];
2643
- let g = 0;
2644
- for (const [w, k] of d)
2645
- y.push([i(w, `${u}/@k${g}`), i(k, `${u}/${g}`)]), g += 1;
2646
- return { [E]: "map", entries: y };
2647
- }
2648
- if (d instanceof Set) {
2649
- const y = [];
2650
- let g = 0;
2651
- for (const w of d)
2652
- y.push(i(w, `${u}/${g}`)), g += 1;
2653
- return { [E]: "set", values: y };
2654
- }
2655
- if (Array.isArray(d))
2656
- return d.map((y, g) => i(y, `${u}/${g}`));
2657
- const h = {};
2658
- for (const [y, g] of Object.entries(d))
2659
- h[y] = i(g, `${u}/${H(y)}`);
2660
- return E in h ? { [E]: "escaped", value: h } : h;
2661
- }
2662
- return { value: i(c, ""), report: { truncated: l, unsupported: n } };
2663
- }
2664
- function oe(c) {
2665
- const e = /* @__PURE__ */ new Map(), t = [];
2666
- function s(o, l) {
2667
- if (o === null || typeof o != "object") return o;
2668
- if (Array.isArray(o)) {
2669
- const a = [];
2670
- return e.set(l, a), o.forEach((d, u) => {
2671
- if (j(d)) {
2672
- t.push({ target: a, key: u, path: d.path }), a[u] = void 0;
2673
- return;
2674
- }
2675
- a[u] = s(d, `${l}/${u}`);
2676
- }), a;
2677
- }
2678
- if (typeof o[E] == "string") {
2679
- const a = o;
2680
- switch (a[E]) {
2681
- case "undefined":
2682
- return;
2683
- case "nan":
2684
- return Number.NaN;
2685
- case "infinity":
2686
- return a.sign === 1 ? 1 / 0 : -1 / 0;
2687
- case "bigint":
2688
- return BigInt(a.value);
2689
- case "date":
2690
- return new Date(a.iso);
2691
- case "regexp":
2692
- return new RegExp(a.source, a.flags);
2693
- case "error": {
2694
- const d = new Error(a.message);
2695
- return d.name = a.name, d;
2696
- }
2697
- case "unsupported":
2698
- return;
2699
- case "ref":
2700
- return;
2701
- case "map": {
2702
- const d = /* @__PURE__ */ new Map();
2703
- return e.set(l, d), a.entries.forEach(([u, f], m) => {
2704
- d.set(s(u, `${l}/@k${m}`), s(f, `${l}/${m}`));
2705
- }), d;
2706
- }
2707
- case "set": {
2708
- const d = /* @__PURE__ */ new Set();
2709
- return e.set(l, d), a.values.forEach((u, f) => d.add(s(u, `${l}/${f}`))), d;
2710
- }
2711
- case "escaped":
2712
- return n(a.value, l);
2713
- default:
2714
- return;
2715
- }
2716
- }
2717
- return n(o, l);
2718
- }
2719
- function n(o, l) {
2720
- const i = {};
2721
- e.set(l, i);
2722
- for (const [a, d] of Object.entries(o)) {
2723
- const u = `${l}/${H(a)}`;
2724
- if (j(d)) {
2725
- t.push({ target: i, key: a, path: d.path }), i[a] = void 0;
2726
- continue;
2727
- }
2728
- i[a] = s(d, u);
2729
- }
2730
- return i;
2731
- }
2732
- const r = s(c, "");
2733
- e.set("", r);
2734
- for (const { target: o, key: l, path: i } of t)
2735
- o[l] = e.get(i);
2736
- return r;
2737
- }
2738
- function j(c) {
2739
- return c !== null && typeof c == "object" && c[E] === "ref" && typeof c.path == "string";
2740
- }
2741
- function H(c) {
2742
- return c.replace(/~/g, "~0").replace(/\//g, "~1");
2743
- }
2744
- function pe(c, e, t = {}) {
2745
- let s = t.maxNodes ?? 1e5;
2746
- for (let n = 0; n < 8; n += 1) {
2747
- const { value: r, report: o } = B(c, { ...t, maxNodes: s });
2748
- let l;
2749
- try {
2750
- l = JSON.stringify(r)?.length ?? 0;
2751
- } catch {
2752
- l = Number.POSITIVE_INFINITY;
2753
- }
2754
- if (l <= e)
2755
- return o.truncated ? {
2756
- value: r,
2757
- truncated: !0,
2758
- note: `State was too large to send in full; parts beyond ${s} nodes are omitted.`
2759
- } : { value: r, truncated: !1 };
2760
- const i = Math.floor(s * e * 0.8 / l);
2761
- if (s = Math.max(1, Math.min(i, s - 1)), s <= 1 && n > 0)
2762
- break;
2763
- }
2764
- return {
2765
- value: { [E]: "unsupported", kind: "truncated" },
2766
- truncated: !0,
2767
- note: `State exceeds the ${e}-byte transport limit and could not be reduced to fit.`
3501
+ pathTo: (c, a) => a === void 0 ? `entities.${c}` : `entities.${c}.${a}`,
3502
+ anyField: (c) => `entities.*.${c}`
2768
3503
  };
2769
3504
  }
2770
- function v(c, e, t) {
2771
- c.onError?.(e, t);
3505
+ function S(o, e, t) {
3506
+ o.onError?.(e, t);
2772
3507
  }
2773
- async function me(c) {
3508
+ async function Te(o) {
2774
3509
  const e = { slices: {}, restored: !1 };
2775
3510
  let t;
2776
3511
  try {
2777
- t = c.source ?? await c.adapter.read(c.key);
2778
- } catch (n) {
2779
- return v(c, n, "read"), e;
3512
+ t = o.source ?? await o.adapter.read(o.key);
3513
+ } catch (r) {
3514
+ return S(o, r, "read"), e;
2780
3515
  }
2781
3516
  if (t == null || t === "") return e;
2782
- let s;
3517
+ let n;
2783
3518
  try {
2784
- s = oe(JSON.parse(t));
2785
- } catch (n) {
2786
- return v(c, n, "decode"), e;
2787
- }
2788
- if (s === null || typeof s != "object" || typeof s.version != "number")
2789
- return v(c, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
2790
- if (s.version !== c.version) {
2791
- if (c.migrate === void 0)
2792
- return v(
2793
- c,
3519
+ n = le(JSON.parse(t));
3520
+ } catch (r) {
3521
+ return S(o, r, "decode"), e;
3522
+ }
3523
+ if (n === null || typeof n != "object" || typeof n.version != "number")
3524
+ return S(o, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
3525
+ if (n.version !== o.version) {
3526
+ if (o.migrate === void 0)
3527
+ return S(
3528
+ o,
2794
3529
  new Error(
2795
- `persisted state is version ${s.version}, this build expects ${c.version}, and no migrate was supplied`
3530
+ `persisted state is version ${n.version}, this build expects ${o.version}, and no migrate was supplied`
2796
3531
  ),
2797
3532
  "migrate"
2798
3533
  ), e;
2799
3534
  try {
2800
- const n = c.migrate(s.slices, s.version);
2801
- return n === null ? e : { slices: n, restored: !0 };
2802
- } catch (n) {
2803
- return v(c, n, "migrate"), e;
3535
+ const r = o.migrate(n.slices, n.version);
3536
+ return r === null ? e : { slices: r, restored: !0 };
3537
+ } catch (r) {
3538
+ return S(o, r, "migrate"), e;
2804
3539
  }
2805
3540
  }
2806
- return { slices: s.slices ?? {}, restored: !0 };
3541
+ return { slices: n.slices ?? {}, restored: !0 };
2807
3542
  }
2808
- function ye(c, e) {
2809
- if (!e.restored) return c;
3543
+ function De(o, e) {
3544
+ if (!e.restored) return o;
2810
3545
  const t = {};
2811
- for (const [s, n] of Object.entries(c)) {
2812
- const r = e.slices[s];
2813
- t[s] = r === void 0 ? n : { ...n, state: r };
3546
+ for (const [n, r] of Object.entries(o)) {
3547
+ const i = e.slices[n];
3548
+ t[n] = i === void 0 ? r : { ...r, state: i };
2814
3549
  }
2815
3550
  return t;
2816
3551
  }
2817
- function W(c, e) {
2818
- const t = c ?? {}, s = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((n) => n in t).map((n) => [n, t[n]]));
2819
- return JSON.stringify(B({ version: e.version, slices: s }).value);
3552
+ function Q(o, e) {
3553
+ const t = o ?? {}, n = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((s) => s in t).map((s) => [s, t[s]])), { value: r, report: i } = j({ version: e.version, slices: n });
3554
+ if (i.truncated || i.unsupported.length > 0) {
3555
+ const s = [];
3556
+ i.truncated && s.push("state was too large to encode in full"), i.unsupported.length > 0 && s.push(`values with no faithful representation at: ${i.unsupported.join(", ")}`);
3557
+ const l = s.join("; ");
3558
+ S(
3559
+ e,
3560
+ new Error(`[yoltra] Persisted state is incomplete - ${l}.`),
3561
+ "encode"
3562
+ );
3563
+ }
3564
+ return JSON.stringify(r);
2820
3565
  }
2821
- function ge(c, e) {
2822
- const t = e.throttleMs ?? 250, s = e.slices;
2823
- let n = null, r = !1;
2824
- const o = () => {
2825
- if (r) {
2826
- r = !1;
3566
+ function xe(o, e) {
3567
+ const t = e.throttleMs ?? 250, n = e.slices;
3568
+ let r = null, i = !1;
3569
+ const s = () => {
3570
+ if (i) {
3571
+ i = !1;
2827
3572
  try {
2828
- const a = e.adapter.write(e.key, W(c.getState(), e));
2829
- a instanceof Promise && a.catch((d) => v(e, d, "write"));
3573
+ const a = e.adapter.write(e.key, Q(o.getState(), e));
3574
+ a instanceof Promise && a.catch((d) => S(e, d, "write"));
2830
3575
  } catch (a) {
2831
- v(e, a, "write");
3576
+ S(e, a, "write");
2832
3577
  }
2833
3578
  }
2834
3579
  }, l = () => {
2835
- if (r = !0, t <= 0) {
2836
- o();
3580
+ if (i = !0, t <= 0) {
3581
+ s();
2837
3582
  return;
2838
3583
  }
2839
- n === null && (n = setTimeout(() => {
2840
- n = null, o();
2841
- }, t), n.unref?.());
2842
- }, i = c.instrument((a) => {
2843
- if (s === void 0) {
3584
+ r === null && (r = setTimeout(() => {
3585
+ r = null, s();
3586
+ }, t), r.unref?.());
3587
+ }, c = o.instrument((a) => {
3588
+ if (n === void 0) {
2844
3589
  l();
2845
3590
  return;
2846
3591
  }
2847
3592
  (a.changedPaths ?? []).some(
2848
- (u) => s.some((f) => u === f || u.startsWith(`${f}.`))
3593
+ (f) => n.some((u) => f === u || f.startsWith(`${u}.`))
2849
3594
  ) && l();
2850
3595
  });
2851
3596
  return () => {
2852
- i(), n !== null && (clearTimeout(n), n = null), o();
3597
+ c(), r !== null && (clearTimeout(r), r = null), s();
2853
3598
  };
2854
3599
  }
2855
- function we(c, e) {
2856
- return W(c.getState(), e);
3600
+ function je(o, e) {
3601
+ return Q(o.getState(), e);
2857
3602
  }
2858
- function Ee(c) {
3603
+ function _e(o) {
2859
3604
  return {
2860
- read: (e) => c.getItem(e),
2861
- write: (e, t) => c.setItem(e, t),
2862
- remove: (e) => c.removeItem(e)
3605
+ read: (e) => o.getItem(e),
3606
+ write: (e, t) => o.setItem(e, t),
3607
+ remove: (e) => o.removeItem(e)
2863
3608
  };
2864
3609
  }
2865
- function be(c) {
2866
- const e = new Map(Object.entries(c ?? {}));
3610
+ function Be(o) {
3611
+ const e = new Map(Object.entries(o ?? {}));
2867
3612
  return {
2868
3613
  read: (t) => e.get(t) ?? null,
2869
- write: (t, s) => {
2870
- e.set(t, s);
3614
+ write: (t, n) => {
3615
+ e.set(t, n);
2871
3616
  },
2872
3617
  remove: (t) => {
2873
3618
  e.delete(t);
@@ -2875,28 +3620,31 @@ function be(c) {
2875
3620
  };
2876
3621
  }
2877
3622
  export {
2878
- C as CallAbortedError,
2879
- J as CallTimeoutError,
2880
- U as EventBus,
2881
- L as LooseEventBus,
2882
- V as Reducer,
2883
- le as Rejected,
2884
- x as Store,
2885
- he as createEntityAdapter,
2886
- be as createMemoryAdapter,
2887
- de as createStore,
2888
- Ee as createWebStorageAdapter,
2889
- oe as decodeState,
2890
- we as dehydrate,
3623
+ B as CallAbortedError,
3624
+ Z as CallTimeoutError,
3625
+ G as EventBus,
3626
+ J as LooseEventBus,
3627
+ X as Reducer,
3628
+ Re as Rejected,
3629
+ k as Store,
3630
+ Ae as createEntityAdapter,
3631
+ Be as createMemoryAdapter,
3632
+ ke as createStore,
3633
+ _e as createWebStorageAdapter,
3634
+ le as decodeState,
3635
+ Ie as defineEffect,
3636
+ Pe as defineMiddleware,
3637
+ Ce as defineSlice,
3638
+ je as dehydrate,
2891
3639
  I as detectChangedProps,
2892
- B as encodeState,
2893
- pe as encodeStateBounded,
2894
- fe as eventKeys,
2895
- M as freezeState,
2896
- me as hydrate,
2897
- Q as isRejected,
2898
- ge as persist,
2899
- ue as typedEvents,
2900
- ye as withHydration
3640
+ j as encodeState,
3641
+ Oe as encodeStateBounded,
3642
+ Me as eventKeys,
3643
+ C as freezeState,
3644
+ Te as hydrate,
3645
+ Y as isRejected,
3646
+ xe as persist,
3647
+ $e as typedEvents,
3648
+ De as withHydration
2901
3649
  };
2902
3650
  //# sourceMappingURL=yoltra.mjs.map