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