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