@yoltra/core 0.1.0 → 0.3.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/LICENSE +0 -0
- package/README.es.md +49 -34
- package/README.md +45 -32
- package/dist/types/index.d.ts +1 -1
- package/dist/types/store/Store.d.ts +133 -14
- package/dist/types/types.d.ts +134 -14
- package/dist/types/utils/detectChangedProps.d.ts +9 -6
- package/dist/yoltra.cjs.js +5 -2
- package/dist/yoltra.esm.js +547 -360
- package/dist/yoltra.umd.js +5 -2
- package/package.json +15 -16
package/dist/yoltra.esm.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @yoltra/core v0.
|
|
2
|
+
* @yoltra/core v0.3.0
|
|
3
3
|
* (c) 2026 Manu Ramirez <@pixerael>
|
|
4
4
|
* License: MIT
|
|
5
5
|
* Homepage: https://yoltra.dev
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree
|
|
6
9
|
*/
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
class
|
|
10
|
+
var P = Object.defineProperty;
|
|
11
|
+
var z = (a, e, t) => e in a ? P(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
|
|
12
|
+
var d = (a, e, t) => z(a, typeof e != "symbol" ? e + "" : e, t);
|
|
13
|
+
class O {
|
|
11
14
|
constructor() {
|
|
12
15
|
/**
|
|
13
16
|
* Internal registry: `channel → type → Set<handler>`.
|
|
14
17
|
* @internal
|
|
15
18
|
*/
|
|
16
|
-
|
|
19
|
+
d(this, "handlers", /* @__PURE__ */ new Map());
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* Subscribes a handler to an exact `(channel, type)`.
|
|
@@ -37,11 +40,11 @@ class z {
|
|
|
37
40
|
*
|
|
38
41
|
* @public
|
|
39
42
|
*/
|
|
40
|
-
on(e, t,
|
|
43
|
+
on(e, t, r) {
|
|
41
44
|
let n = this.handlers.get(e);
|
|
42
45
|
n || (n = /* @__PURE__ */ new Map(), this.handlers.set(e, n));
|
|
43
|
-
let
|
|
44
|
-
return
|
|
46
|
+
let s = n.get(t);
|
|
47
|
+
return s || (s = /* @__PURE__ */ new Set(), n.set(t, s)), s.add(r), () => this.off(e, t, r);
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* Removes a specific handler previously added with {@link EventBus.on | `on`}.
|
|
@@ -63,11 +66,11 @@ class z {
|
|
|
63
66
|
*
|
|
64
67
|
* @public
|
|
65
68
|
*/
|
|
66
|
-
off(e, t,
|
|
69
|
+
off(e, t, r) {
|
|
67
70
|
const n = this.handlers.get(e);
|
|
68
71
|
if (!n) return;
|
|
69
|
-
const
|
|
70
|
-
|
|
72
|
+
const s = n.get(t);
|
|
73
|
+
s && (s.delete(r), s.size === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e));
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
76
|
* Emits an event to all subscribers of the exact `(channel, type)`.
|
|
@@ -88,14 +91,14 @@ class z {
|
|
|
88
91
|
*
|
|
89
92
|
* @public
|
|
90
93
|
*/
|
|
91
|
-
emit(e, t,
|
|
94
|
+
emit(e, t, r) {
|
|
92
95
|
const n = this.handlers.get(e);
|
|
93
96
|
if (!n) return;
|
|
94
|
-
const
|
|
95
|
-
if (!(!
|
|
96
|
-
for (const
|
|
97
|
+
const s = n.get(t);
|
|
98
|
+
if (!(!s || s.size === 0))
|
|
99
|
+
for (const c of [...s])
|
|
97
100
|
try {
|
|
98
|
-
|
|
101
|
+
c(r);
|
|
99
102
|
} catch (i) {
|
|
100
103
|
console.error("EventBus handler error:", i);
|
|
101
104
|
}
|
|
@@ -123,12 +126,12 @@ class k {
|
|
|
123
126
|
* Exact handlers: `channel → type → [handlers]`.
|
|
124
127
|
* @internal
|
|
125
128
|
*/
|
|
126
|
-
|
|
129
|
+
d(this, "handlers", /* @__PURE__ */ new Map());
|
|
127
130
|
/**
|
|
128
131
|
* Pattern handlers with `*` and `**`: `channel → pattern(string) → [handlers]`.
|
|
129
132
|
* @internal
|
|
130
133
|
*/
|
|
131
|
-
|
|
134
|
+
d(this, "patternHandlers", /* @__PURE__ */ new Map());
|
|
132
135
|
}
|
|
133
136
|
/**
|
|
134
137
|
* Subscribes a handler to either an **exact** type or a **pattern**.
|
|
@@ -162,18 +165,18 @@ class k {
|
|
|
162
165
|
*
|
|
163
166
|
* @public
|
|
164
167
|
*/
|
|
165
|
-
on(e, t,
|
|
168
|
+
on(e, t, r) {
|
|
166
169
|
const n = String(t);
|
|
167
170
|
if (this.isPattern(n)) {
|
|
168
|
-
const
|
|
171
|
+
const s = n;
|
|
169
172
|
this.patternHandlers.has(e) || this.patternHandlers.set(e, /* @__PURE__ */ new Map());
|
|
170
|
-
const
|
|
171
|
-
return
|
|
173
|
+
const c = this.patternHandlers.get(e);
|
|
174
|
+
return c.has(s) || c.set(s, []), c.get(s).push(r), () => this.offPattern(e, s, r);
|
|
172
175
|
} else {
|
|
173
|
-
const
|
|
176
|
+
const s = this.normalizeTypeKey(n);
|
|
174
177
|
this.handlers.has(e) || this.handlers.set(e, /* @__PURE__ */ new Map());
|
|
175
|
-
const
|
|
176
|
-
return
|
|
178
|
+
const c = this.handlers.get(e);
|
|
179
|
+
return c.has(s) || c.set(s, []), c.get(s).push(r), () => this.offExactNormalized(e, s, r);
|
|
177
180
|
}
|
|
178
181
|
}
|
|
179
182
|
/**
|
|
@@ -194,9 +197,9 @@ class k {
|
|
|
194
197
|
*
|
|
195
198
|
* @public
|
|
196
199
|
*/
|
|
197
|
-
off(e, t,
|
|
200
|
+
off(e, t, r) {
|
|
198
201
|
const n = this.normalizeTypeKey(String(t));
|
|
199
|
-
this.offExactNormalized(e, n,
|
|
202
|
+
this.offExactNormalized(e, n, r);
|
|
200
203
|
}
|
|
201
204
|
/**
|
|
202
205
|
* Internal exact unsubscription using an already **normalized** type key.
|
|
@@ -206,13 +209,13 @@ class k {
|
|
|
206
209
|
* @param handler - Handler to remove.
|
|
207
210
|
* @internal
|
|
208
211
|
*/
|
|
209
|
-
offExactNormalized(e, t,
|
|
212
|
+
offExactNormalized(e, t, r) {
|
|
210
213
|
const n = this.handlers.get(e);
|
|
211
214
|
if (!n) return;
|
|
212
|
-
const
|
|
213
|
-
if (!
|
|
214
|
-
const
|
|
215
|
-
|
|
215
|
+
const s = n.get(t);
|
|
216
|
+
if (!s) return;
|
|
217
|
+
const c = s.indexOf(r);
|
|
218
|
+
c !== -1 && s.splice(c, 1), s.length === 0 && n.delete(t), n.size === 0 && this.handlers.delete(e);
|
|
216
219
|
}
|
|
217
220
|
/**
|
|
218
221
|
* Internal removal for a **pattern** subscription. No-ops if missing.
|
|
@@ -222,13 +225,13 @@ class k {
|
|
|
222
225
|
* @param handler - Handler to remove.
|
|
223
226
|
* @internal
|
|
224
227
|
*/
|
|
225
|
-
offPattern(e, t,
|
|
228
|
+
offPattern(e, t, r) {
|
|
226
229
|
const n = this.patternHandlers.get(e);
|
|
227
230
|
if (!n) return;
|
|
228
|
-
const
|
|
229
|
-
if (!
|
|
230
|
-
const
|
|
231
|
-
|
|
231
|
+
const s = n.get(t);
|
|
232
|
+
if (!s) return;
|
|
233
|
+
const c = s.indexOf(r);
|
|
234
|
+
c !== -1 && s.splice(c, 1), s.length === 0 && n.delete(t), n.size === 0 && this.patternHandlers.delete(e);
|
|
232
235
|
}
|
|
233
236
|
/**
|
|
234
237
|
* Emits an event to all exact subscribers first, then to **matching pattern** subscribers.
|
|
@@ -250,27 +253,27 @@ class k {
|
|
|
250
253
|
*
|
|
251
254
|
* @public
|
|
252
255
|
*/
|
|
253
|
-
emit(e, t,
|
|
254
|
-
const n = String(t),
|
|
256
|
+
emit(e, t, r) {
|
|
257
|
+
const n = String(t), s = this.normalizeTypeKey(n), c = this.handlers.get(e)?.get(s) ?? [], i = this.patternHandlers.get(e), o = [];
|
|
255
258
|
if (i && i.size) {
|
|
256
259
|
const l = this.normalizeTypeKey(n);
|
|
257
|
-
for (const [
|
|
258
|
-
this.matchPattern(
|
|
260
|
+
for (const [u, p] of i.entries())
|
|
261
|
+
this.matchPattern(u, l) && o.push(p);
|
|
259
262
|
}
|
|
260
263
|
const f = /* @__PURE__ */ new Set(), h = (l) => {
|
|
261
|
-
for (const
|
|
262
|
-
if (!f.has(
|
|
263
|
-
f.add(
|
|
264
|
+
for (const u of [...l])
|
|
265
|
+
if (!f.has(u)) {
|
|
266
|
+
f.add(u);
|
|
264
267
|
try {
|
|
265
|
-
|
|
266
|
-
} catch (
|
|
267
|
-
console.error(
|
|
268
|
+
u(r);
|
|
269
|
+
} catch (p) {
|
|
270
|
+
console.error(p);
|
|
268
271
|
continue;
|
|
269
272
|
}
|
|
270
273
|
}
|
|
271
274
|
};
|
|
272
|
-
h(
|
|
273
|
-
for (const l of
|
|
275
|
+
h(c);
|
|
276
|
+
for (const l of o) h(l);
|
|
274
277
|
}
|
|
275
278
|
/**
|
|
276
279
|
* Determines if a string is a **pattern** (contains `*`).
|
|
@@ -328,29 +331,19 @@ class k {
|
|
|
328
331
|
* @internal
|
|
329
332
|
*/
|
|
330
333
|
matchPattern(e, t) {
|
|
331
|
-
const
|
|
332
|
-
let
|
|
333
|
-
for (;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
334
|
+
const r = this.splitPath(e), n = this.splitPath(t);
|
|
335
|
+
let s = 0, c = 0, i = -1, o = 0;
|
|
336
|
+
for (; c < n.length; )
|
|
337
|
+
if (s < r.length && (r[s] === "*" || r[s] === n[c]))
|
|
338
|
+
s++, c++;
|
|
339
|
+
else if (s < r.length && r[s] === "**")
|
|
340
|
+
i = s, o = c, s++;
|
|
341
|
+
else if (i !== -1)
|
|
342
|
+
s = i + 1, c = ++o;
|
|
343
|
+
else
|
|
340
344
|
return !1;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
r++, o++;
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
return !1;
|
|
347
|
-
}
|
|
348
|
-
if (o === n.length) {
|
|
349
|
-
for (; r < s.length; r++)
|
|
350
|
-
if (s[r] !== "**") return !1;
|
|
351
|
-
return !0;
|
|
352
|
-
}
|
|
353
|
-
return !1;
|
|
345
|
+
for (; s < r.length && r[s] === "**"; ) s++;
|
|
346
|
+
return s === r.length;
|
|
354
347
|
}
|
|
355
348
|
/**
|
|
356
349
|
* Removes **all** listeners (exact and pattern). Useful for tests/HMR teardown.
|
|
@@ -375,12 +368,12 @@ class k {
|
|
|
375
368
|
*/
|
|
376
369
|
__introspect() {
|
|
377
370
|
const e = [];
|
|
378
|
-
for (const [t,
|
|
379
|
-
for (const [n,
|
|
380
|
-
|
|
381
|
-
for (const [t,
|
|
382
|
-
for (const [n,
|
|
383
|
-
|
|
371
|
+
for (const [t, r] of this.handlers)
|
|
372
|
+
for (const [n, s] of r)
|
|
373
|
+
s.length > 0 && e.push({ channel: t, type: n, count: s.length });
|
|
374
|
+
for (const [t, r] of this.patternHandlers)
|
|
375
|
+
for (const [n, s] of r)
|
|
376
|
+
s.length > 0 && e.push({ channel: t, type: n, count: s.length });
|
|
384
377
|
return e;
|
|
385
378
|
}
|
|
386
379
|
}
|
|
@@ -405,7 +398,7 @@ class _ {
|
|
|
405
398
|
* The underlying pure reducer function.
|
|
406
399
|
* @internal
|
|
407
400
|
*/
|
|
408
|
-
|
|
401
|
+
d(this, "_reduce");
|
|
409
402
|
this._reduce = e;
|
|
410
403
|
}
|
|
411
404
|
/**
|
|
@@ -426,67 +419,67 @@ class _ {
|
|
|
426
419
|
return this._reduce(e, t);
|
|
427
420
|
}
|
|
428
421
|
}
|
|
429
|
-
function
|
|
422
|
+
function g(a, e, t = "", r = /* @__PURE__ */ new Map()) {
|
|
430
423
|
if (a === e) return [];
|
|
431
424
|
if (typeof a != "object" || typeof e != "object" || a === null || e === null)
|
|
432
|
-
return [t];
|
|
425
|
+
return typeof a == "number" && Number.isNaN(a) && Number.isNaN(e) ? [] : [t];
|
|
433
426
|
if (a instanceof Date && e instanceof Date)
|
|
434
427
|
return a.getTime() === e.getTime() ? [] : [t];
|
|
435
428
|
if (a instanceof RegExp && e instanceof RegExp)
|
|
436
429
|
return a.source === e.source && e.flags === a.flags ? [] : [t];
|
|
437
|
-
const n = a,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
o
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
return f.filter(Boolean);
|
|
455
|
-
}
|
|
456
|
-
const h = Object.keys(a), l = Object.keys(e), p = /* @__PURE__ */ new Set([...h, ...l]);
|
|
457
|
-
for (const d of p) {
|
|
458
|
-
const m = t ? `${t}.${d}` : d, y = Object.prototype.hasOwnProperty.call(a, d), g = Object.prototype.hasOwnProperty.call(e, d);
|
|
459
|
-
if (!y) {
|
|
460
|
-
f.push(m);
|
|
461
|
-
continue;
|
|
430
|
+
const n = a, s = e, c = r.get(n);
|
|
431
|
+
if (c?.has(s)) return [];
|
|
432
|
+
const i = c ?? /* @__PURE__ */ new Set();
|
|
433
|
+
i.add(s), c || r.set(n, i);
|
|
434
|
+
try {
|
|
435
|
+
const o = Array.isArray(a), f = Array.isArray(e);
|
|
436
|
+
if (o !== f) return [t];
|
|
437
|
+
const h = [];
|
|
438
|
+
if (o) {
|
|
439
|
+
const u = a, p = e;
|
|
440
|
+
if (u.length !== p.length) return [t];
|
|
441
|
+
for (let y = 0; y < u.length; y++) {
|
|
442
|
+
const m = t ? `${t}.${y}` : `${y}`;
|
|
443
|
+
h.push(...g(u[y], p[y], m, r));
|
|
444
|
+
}
|
|
445
|
+
return h.filter(Boolean);
|
|
462
446
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
447
|
+
const l = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(e)]);
|
|
448
|
+
for (const u of l) {
|
|
449
|
+
const p = t ? `${t}.${u}` : u, y = Object.prototype.hasOwnProperty.call(a, u), m = Object.prototype.hasOwnProperty.call(e, u);
|
|
450
|
+
if (!y || !m) {
|
|
451
|
+
h.push(p);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
h.push(...g(a[u], e[u], p, r));
|
|
466
455
|
}
|
|
467
|
-
|
|
468
|
-
|
|
456
|
+
return h.filter(Boolean);
|
|
457
|
+
} finally {
|
|
458
|
+
i.delete(s), i.size === 0 && r.delete(n);
|
|
469
459
|
}
|
|
470
|
-
return f.filter(Boolean);
|
|
471
460
|
}
|
|
472
|
-
function
|
|
461
|
+
function E(a, e = /* @__PURE__ */ new WeakSet()) {
|
|
473
462
|
if (a === null || typeof a != "object" || e.has(a) || Object.isFrozen(a)) return a;
|
|
474
463
|
if (e.add(a), Array.isArray(a)) {
|
|
475
464
|
const t = a;
|
|
476
|
-
for (let
|
|
477
|
-
t[
|
|
465
|
+
for (let r = 0; r < t.length; r++)
|
|
466
|
+
t[r] = E(t[r], e);
|
|
478
467
|
return Object.freeze(t);
|
|
479
468
|
}
|
|
480
469
|
for (const t of Object.getOwnPropertyNames(a)) {
|
|
481
|
-
const
|
|
482
|
-
!
|
|
470
|
+
const r = Object.getOwnPropertyDescriptor(a, t);
|
|
471
|
+
!r || !("value" in r) || (a[t] = E(a[t], e));
|
|
483
472
|
}
|
|
484
473
|
for (const t of Object.getOwnPropertySymbols(a)) {
|
|
485
|
-
const
|
|
486
|
-
!
|
|
474
|
+
const r = Object.getOwnPropertyDescriptor(a, t);
|
|
475
|
+
!r || !("value" in r) || (a[t] = E(a[t], e));
|
|
487
476
|
}
|
|
488
477
|
return Object.freeze(a);
|
|
489
478
|
}
|
|
479
|
+
function b(a) {
|
|
480
|
+
return process.env.NODE_ENV === "production" ? a : E(a);
|
|
481
|
+
}
|
|
482
|
+
const w = 100, S = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
|
|
490
483
|
class v {
|
|
491
484
|
/**
|
|
492
485
|
* Creates a store from a {@link StoreSpec}.
|
|
@@ -501,7 +494,7 @@ class v {
|
|
|
501
494
|
*
|
|
502
495
|
* @public
|
|
503
496
|
*/
|
|
504
|
-
|
|
497
|
+
d(this, "name");
|
|
505
498
|
/**
|
|
506
499
|
* Registered middleware pipeline (run **before** reducers).
|
|
507
500
|
* Stores either raw functions (legacy) or MiddlewareSpec objects.
|
|
@@ -509,45 +502,45 @@ class v {
|
|
|
509
502
|
*
|
|
510
503
|
* @internal
|
|
511
504
|
*/
|
|
512
|
-
|
|
505
|
+
d(this, "middleware");
|
|
513
506
|
/**
|
|
514
507
|
* Installed slice reducers keyed by slice name.
|
|
515
508
|
*
|
|
516
509
|
* @internal
|
|
517
510
|
*/
|
|
518
|
-
|
|
511
|
+
d(this, "reducers");
|
|
519
512
|
/**
|
|
520
513
|
* Current immutable snapshot of the store state.
|
|
521
514
|
* This reference changes whenever any slice changes (shallow immutability).
|
|
522
515
|
*
|
|
523
516
|
* @internal
|
|
524
517
|
*/
|
|
525
|
-
|
|
518
|
+
d(this, "state");
|
|
526
519
|
/**
|
|
527
520
|
* Bus for reducer wiring (emit by `(channel, type)`).
|
|
528
521
|
*
|
|
529
522
|
* @internal
|
|
530
523
|
*/
|
|
531
|
-
|
|
524
|
+
d(this, "reducerBus");
|
|
532
525
|
/**
|
|
533
526
|
* Bus for **granular** connector events (emit by **dotted path** inside a slice).
|
|
534
527
|
*
|
|
535
528
|
* @internal
|
|
536
529
|
*/
|
|
537
|
-
|
|
530
|
+
d(this, "connectorBus");
|
|
538
531
|
/**
|
|
539
532
|
* Coarse-grained listeners (called once per committed event, only if state changed).
|
|
540
533
|
*
|
|
541
534
|
* @internal
|
|
542
535
|
*/
|
|
543
|
-
|
|
536
|
+
d(this, "listeners", /* @__PURE__ */ new Set());
|
|
544
537
|
/**
|
|
545
538
|
* Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
|
|
546
539
|
* Used for effects with explicit `keys` or legacy `events` targeting.
|
|
547
540
|
*
|
|
548
541
|
* @internal
|
|
549
542
|
*/
|
|
550
|
-
|
|
543
|
+
d(this, "effects", /* @__PURE__ */ new Map());
|
|
551
544
|
/**
|
|
552
545
|
* Pattern-based effects that need runtime matching.
|
|
553
546
|
* Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
|
|
@@ -555,34 +548,34 @@ class v {
|
|
|
555
548
|
*
|
|
556
549
|
* @internal
|
|
557
550
|
*/
|
|
558
|
-
|
|
551
|
+
d(this, "patternEffects", /* @__PURE__ */ new Set());
|
|
559
552
|
/**
|
|
560
553
|
* Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
561
554
|
* Notified after reducers, before effects, for events that passed middleware.
|
|
562
555
|
*
|
|
563
556
|
* @internal
|
|
564
557
|
*/
|
|
565
|
-
|
|
558
|
+
d(this, "committedEventSubscribers", /* @__PURE__ */ new Map());
|
|
566
559
|
/**
|
|
567
560
|
* Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
568
561
|
* Notified when middleware rejects an event.
|
|
569
562
|
*
|
|
570
563
|
* @internal
|
|
571
564
|
*/
|
|
572
|
-
|
|
565
|
+
d(this, "uncommittedEventSubscribers", /* @__PURE__ */ new Map());
|
|
573
566
|
/**
|
|
574
567
|
* All-events subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
575
568
|
* Notified for both committed and uncommitted events with phase parameter.
|
|
576
569
|
*
|
|
577
570
|
* @internal
|
|
578
571
|
*/
|
|
579
|
-
|
|
572
|
+
d(this, "allEventSubscribers", /* @__PURE__ */ new Map());
|
|
580
573
|
/**
|
|
581
574
|
* Track reducerBus unsubs per slice for HMR/register/unregister.
|
|
582
575
|
*
|
|
583
576
|
* @internal
|
|
584
577
|
*/
|
|
585
|
-
|
|
578
|
+
d(this, "sliceUnsubs", /* @__PURE__ */ new Map());
|
|
586
579
|
/**
|
|
587
580
|
* Pattern-based reducers that need runtime matching.
|
|
588
581
|
* Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
|
|
@@ -590,26 +583,54 @@ class v {
|
|
|
590
583
|
*
|
|
591
584
|
* @internal
|
|
592
585
|
*/
|
|
593
|
-
|
|
586
|
+
d(this, "patternReducers", /* @__PURE__ */ new Map());
|
|
594
587
|
/**
|
|
595
588
|
* Whether `__replayEvents()` is allowed.
|
|
596
589
|
* Set from `spec.devtools.allowReplay`.
|
|
597
590
|
*
|
|
598
591
|
* @internal
|
|
599
592
|
*/
|
|
600
|
-
|
|
593
|
+
d(this, "replayEnabled");
|
|
601
594
|
/**
|
|
602
|
-
*
|
|
595
|
+
* Optional hook invoked when an effect throws/rejects. See
|
|
596
|
+
* {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
|
|
597
|
+
* failure — this is how callers observe effect errors.
|
|
598
|
+
*/
|
|
599
|
+
d(this, "onEffectError");
|
|
600
|
+
/**
|
|
601
|
+
* Pending events awaiting the **synchronous** reduce phase (middleware +
|
|
602
|
+
* reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
|
|
603
603
|
*
|
|
604
604
|
* @internal
|
|
605
605
|
*/
|
|
606
|
-
|
|
606
|
+
d(this, "reduceQueue", []);
|
|
607
607
|
/**
|
|
608
|
-
* Re-entrancy guard
|
|
608
|
+
* Re-entrancy guard for the synchronous reduce phase.
|
|
609
609
|
*
|
|
610
610
|
* @internal
|
|
611
611
|
*/
|
|
612
|
-
|
|
612
|
+
d(this, "isReducing", !1);
|
|
613
|
+
/**
|
|
614
|
+
* Registered instrumentation observers (DevTools seam). See {@link instrument}.
|
|
615
|
+
*
|
|
616
|
+
* @internal
|
|
617
|
+
*/
|
|
618
|
+
d(this, "instrumentObservers", /* @__PURE__ */ new Set());
|
|
619
|
+
/**
|
|
620
|
+
* Scratch array collecting slice-prefixed changed leaf paths during an
|
|
621
|
+
* instrumented reduce. Set by {@link drainReduce} while observers are active;
|
|
622
|
+
* appended to by {@link forwardEvent}. `null` when not instrumenting.
|
|
623
|
+
*
|
|
624
|
+
* @internal
|
|
625
|
+
*/
|
|
626
|
+
d(this, "changedPathSink", null);
|
|
627
|
+
/**
|
|
628
|
+
* Count of effect tasks currently in flight; surfaced as queue depth by
|
|
629
|
+
* {@link __devtoolsIntrospect}.
|
|
630
|
+
*
|
|
631
|
+
* @internal
|
|
632
|
+
*/
|
|
633
|
+
d(this, "inFlightEffects", 0);
|
|
613
634
|
/**
|
|
614
635
|
* Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
|
|
615
636
|
*
|
|
@@ -626,31 +647,44 @@ class v {
|
|
|
626
647
|
*
|
|
627
648
|
* @internal
|
|
628
649
|
*/
|
|
629
|
-
|
|
650
|
+
d(this, "processedEvents", /* @__PURE__ */ new Map());
|
|
651
|
+
/**
|
|
652
|
+
* Lifetime count of events suppressed by the deduplication cache.
|
|
653
|
+
* Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
|
|
654
|
+
* surface it in the STORE_METRICS response without further core changes.
|
|
655
|
+
*
|
|
656
|
+
* @internal
|
|
657
|
+
*/
|
|
658
|
+
d(this, "dedupCount", 0);
|
|
659
|
+
/**
|
|
660
|
+
* Store-owned metadata for registered effects, keyed by the effect function.
|
|
661
|
+
* Kept **off** the caller's function object: mutating a user-owned function
|
|
662
|
+
* (the old `fn.__quoMeta`) bled metadata across stores that share a handler
|
|
663
|
+
* and left it attached after unregister. Cleared on {@link dispose}.
|
|
664
|
+
*
|
|
665
|
+
* @internal
|
|
666
|
+
*/
|
|
667
|
+
d(this, "effectMeta", /* @__PURE__ */ new WeakMap());
|
|
630
668
|
/**
|
|
631
669
|
* Configuration for event deduplication.
|
|
632
670
|
* @internal
|
|
633
671
|
*/
|
|
634
|
-
|
|
672
|
+
d(this, "dedupConfig");
|
|
635
673
|
/**
|
|
636
674
|
* Timer for periodic cleanup of processed events.
|
|
637
675
|
*
|
|
638
676
|
* @internal
|
|
639
677
|
*/
|
|
640
|
-
|
|
641
|
-
this.name = e.name ?? "yoltra Store", this.reducerBus = new
|
|
642
|
-
|
|
643
|
-
if (this.dedupConfig = {
|
|
644
|
-
windowMs: e.dedupWindowMs ?? t,
|
|
678
|
+
d(this, "eventCleanupTimer", null);
|
|
679
|
+
if (this.name = e.name ?? "yoltra Store", this.reducerBus = new O(), this.connectorBus = new k(), this.middleware = [...e.middleware ?? []], this.reducers = {}, this.state = {}, this.replayEnabled = e.devtools?.allowReplay ?? !1, this.onEffectError = e.onEffectError, this.dedupConfig = {
|
|
680
|
+
windowMs: e.dedupWindowMs ?? 0,
|
|
645
681
|
maxCacheSize: 1e3
|
|
646
|
-
}, Object.entries(e.reducer).forEach(([
|
|
647
|
-
this.mountSlice(
|
|
682
|
+
}, Object.entries(e.reducer).forEach(([t, r]) => {
|
|
683
|
+
this.mountSlice(t, r, { preserveState: !1 });
|
|
648
684
|
}), e.effects?.length)
|
|
649
|
-
for (const
|
|
650
|
-
this.registerEffect(
|
|
651
|
-
this.
|
|
652
|
-
this.pruneProcessedEvents(Date.now());
|
|
653
|
-
}, 5e3), this.dispose = this.dispose.bind(this), this.notifyEffects = this.notifyEffects.bind(this), this.forwardEvent = this.forwardEvent.bind(this), this.__applyExternalState = this.__applyExternalState.bind(this), this.__replayEvents = this.__replayEvents.bind(this), this.__devtoolsIntrospect = this.__devtoolsIntrospect.bind(this), this.mountSlice = this.mountSlice.bind(this), this.unmountSlice = this.unmountSlice.bind(this), this.getAtPath = this.getAtPath.bind(this), this.emit = this.emit.bind(this), this.subscribe = this.subscribe.bind(this), this.connect = this.connect.bind(this), this.onEffect = this.onEffect.bind(this), this.onEvent = this.onEvent.bind(this), this.getState = this.getState.bind(this), this.registerEffect = this.registerEffect.bind(this), this.registerMiddleware = this.registerMiddleware.bind(this), this.registerReducer = this.registerReducer.bind(this), this.replaceMiddleware = this.replaceMiddleware.bind(this), this.replaceEffects = this.replaceEffects.bind(this), this.replaceReducers = this.replaceReducers.bind(this), this.hotReplace = this.hotReplace.bind(this);
|
|
685
|
+
for (const t of e.effects)
|
|
686
|
+
this.registerEffect(t);
|
|
687
|
+
this.dispose = this.dispose.bind(this), this.notifyEffects = this.notifyEffects.bind(this), this.forwardEvent = this.forwardEvent.bind(this), this.__applyExternalState = this.__applyExternalState.bind(this), this.__replayEvents = this.__replayEvents.bind(this), this.__devtoolsIntrospect = this.__devtoolsIntrospect.bind(this), this.mountSlice = this.mountSlice.bind(this), this.unmountSlice = this.unmountSlice.bind(this), this.getAtPath = this.getAtPath.bind(this), this.emit = this.emit.bind(this), this.subscribe = this.subscribe.bind(this), this.connect = this.connect.bind(this), this.onEffect = this.onEffect.bind(this), this.onEvent = this.onEvent.bind(this), this.getState = this.getState.bind(this), this.registerEffect = this.registerEffect.bind(this), this.registerMiddleware = this.registerMiddleware.bind(this), this.registerReducer = this.registerReducer.bind(this), this.replaceMiddleware = this.replaceMiddleware.bind(this), this.replaceEffects = this.replaceEffects.bind(this), this.replaceReducers = this.replaceReducers.bind(this), this.hotReplace = this.hotReplace.bind(this);
|
|
654
688
|
}
|
|
655
689
|
/**
|
|
656
690
|
* Cleanup resources (timers, etc.) when disposing the store.
|
|
@@ -666,7 +700,7 @@ class v {
|
|
|
666
700
|
* @public
|
|
667
701
|
*/
|
|
668
702
|
dispose() {
|
|
669
|
-
this.eventCleanupTimer && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null), this.processedEvents.clear(), this.effects.clear(), this.patternEffects.clear();
|
|
703
|
+
this.eventCleanupTimer && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null), this.processedEvents.clear(), this.effects.clear(), this.patternEffects.clear(), this.effectMeta = /* @__PURE__ */ new WeakMap(), this.listeners.clear(), this.committedEventSubscribers.clear(), this.uncommittedEventSubscribers.clear(), this.allEventSubscribers.clear(), this.instrumentObservers.clear(), this.connectorBus.clear(), this.reducerBus.clear(), this.patternReducers.clear(), this.sliceUnsubs.clear(), this.changedPathSink = null;
|
|
670
704
|
}
|
|
671
705
|
/**
|
|
672
706
|
* Generates a fingerprint for an event for deduplication purposes.
|
|
@@ -679,15 +713,15 @@ class v {
|
|
|
679
713
|
*
|
|
680
714
|
* @internal
|
|
681
715
|
*/
|
|
682
|
-
fingerprint(e, t,
|
|
716
|
+
fingerprint(e, t, r) {
|
|
683
717
|
const n = `${e}::${t}`;
|
|
684
718
|
try {
|
|
685
|
-
if (
|
|
719
|
+
if (r == null)
|
|
686
720
|
return `${n}::null`;
|
|
687
|
-
if (typeof
|
|
688
|
-
return `${n}::${String(
|
|
689
|
-
const
|
|
690
|
-
return `${n}::${
|
|
721
|
+
if (typeof r != "object")
|
|
722
|
+
return `${n}::${String(r)}`;
|
|
723
|
+
const s = JSON.stringify(r);
|
|
724
|
+
return `${n}::${s}`;
|
|
691
725
|
} catch {
|
|
692
726
|
return `${n}::${Date.now()}::${Math.random()}`;
|
|
693
727
|
}
|
|
@@ -701,9 +735,21 @@ class v {
|
|
|
701
735
|
*
|
|
702
736
|
* @internal
|
|
703
737
|
*/
|
|
704
|
-
shouldDedupe(e) {
|
|
705
|
-
const
|
|
706
|
-
return
|
|
738
|
+
shouldDedupe(e, t) {
|
|
739
|
+
const r = Date.now(), n = this.processedEvents.get(e);
|
|
740
|
+
return n !== void 0 && r - n < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, r), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(r), !1);
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Starts the periodic prune interval if it isn't already running. Called when
|
|
744
|
+
* the first entry is cached so the timer's lifetime tracks actual dedup use
|
|
745
|
+
* (content window or identity `dedupKey`), independent of `dedupWindowMs`.
|
|
746
|
+
*
|
|
747
|
+
* @internal
|
|
748
|
+
*/
|
|
749
|
+
ensureCleanupTimer() {
|
|
750
|
+
this.eventCleanupTimer === null && (this.eventCleanupTimer = setInterval(() => {
|
|
751
|
+
this.pruneProcessedEvents(Date.now());
|
|
752
|
+
}, 5e3), this.eventCleanupTimer.unref?.());
|
|
707
753
|
}
|
|
708
754
|
/**
|
|
709
755
|
* Removes expired entries from the processed events cache.
|
|
@@ -713,9 +759,10 @@ class v {
|
|
|
713
759
|
* @internal
|
|
714
760
|
*/
|
|
715
761
|
pruneProcessedEvents(e) {
|
|
716
|
-
const t =
|
|
717
|
-
for (const [
|
|
718
|
-
|
|
762
|
+
const t = Math.max(this.dedupConfig.windowMs, w), r = e - t * 2;
|
|
763
|
+
for (const [n, s] of this.processedEvents)
|
|
764
|
+
s < r && this.processedEvents.delete(n);
|
|
765
|
+
this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
|
|
719
766
|
}
|
|
720
767
|
/**
|
|
721
768
|
* Checks if an event matches a `When` matcher.
|
|
@@ -735,7 +782,7 @@ class v {
|
|
|
735
782
|
*/
|
|
736
783
|
matchesWhen(e, t) {
|
|
737
784
|
return !e || "any" in e && e.any === !0 ? !0 : "keys" in e ? e.keys.some(
|
|
738
|
-
([
|
|
785
|
+
([r, n]) => t.channel === r && t.type === n
|
|
739
786
|
) : "channel" in e ? t.channel === e.channel : "channels" in e ? e.channels.includes(t.channel) : !1;
|
|
740
787
|
}
|
|
741
788
|
/**
|
|
@@ -771,20 +818,20 @@ class v {
|
|
|
771
818
|
* @internal
|
|
772
819
|
*/
|
|
773
820
|
async notifyEffects(e) {
|
|
774
|
-
const t = `${String(e.channel)}::${String(e.type)}`,
|
|
775
|
-
if (
|
|
776
|
-
for (const n of [...
|
|
821
|
+
const t = `${String(e.channel)}::${String(e.type)}`, r = this.effects.get(t);
|
|
822
|
+
if (r && r.size > 0)
|
|
823
|
+
for (const n of [...r])
|
|
777
824
|
try {
|
|
778
825
|
await n(e, this.getState, this.emit);
|
|
779
|
-
} catch (
|
|
780
|
-
console.error("Effect error:",
|
|
826
|
+
} catch (s) {
|
|
827
|
+
console.error("Effect error:", s), this.onEffectError?.(s, e);
|
|
781
828
|
}
|
|
782
|
-
for (const { effect: n, when:
|
|
783
|
-
if (this.matchesWhen(
|
|
829
|
+
for (const { effect: n, when: s } of this.patternEffects)
|
|
830
|
+
if (this.matchesWhen(s, e))
|
|
784
831
|
try {
|
|
785
832
|
await n(e, this.getState, this.emit);
|
|
786
|
-
} catch (
|
|
787
|
-
console.error("Effect error:",
|
|
833
|
+
} catch (c) {
|
|
834
|
+
console.error("Effect error:", c), this.onEffectError?.(c, e);
|
|
788
835
|
}
|
|
789
836
|
}
|
|
790
837
|
/**
|
|
@@ -797,23 +844,28 @@ class v {
|
|
|
797
844
|
* @param phase - The phase ('committed' or 'uncommitted').
|
|
798
845
|
* @internal
|
|
799
846
|
*/
|
|
800
|
-
|
|
801
|
-
const
|
|
802
|
-
if (
|
|
803
|
-
for (const i of [...
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
847
|
+
notifyEventSubscribers(e, t) {
|
|
848
|
+
const r = `${String(e.channel)}::${String(e.type)}`, s = (t === "committed" ? this.committedEventSubscribers : this.uncommittedEventSubscribers).get(r);
|
|
849
|
+
if (s?.size)
|
|
850
|
+
for (const i of [...s]) this.invokeEventSubscriber(i, e, t);
|
|
851
|
+
const c = this.allEventSubscribers.get(r);
|
|
852
|
+
if (c?.size)
|
|
853
|
+
for (const i of [...c]) this.invokeEventSubscriber(i, e, t);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Invokes a single event-subscription handler **fire-and-forget**: synchronous
|
|
857
|
+
* throws and async rejections are logged but never block the emit pipeline.
|
|
858
|
+
* Event subscribers are notifications, not part of the committed reduce result.
|
|
859
|
+
*
|
|
860
|
+
* @internal
|
|
861
|
+
*/
|
|
862
|
+
invokeEventSubscriber(e, t, r) {
|
|
863
|
+
try {
|
|
864
|
+
const n = e(t, this.getState, this.emit, r);
|
|
865
|
+
n && typeof n.then == "function" && n.catch((s) => console.error("Event subscription error:", s));
|
|
866
|
+
} catch (n) {
|
|
867
|
+
console.error("Event subscription error:", n);
|
|
868
|
+
}
|
|
817
869
|
}
|
|
818
870
|
/**
|
|
819
871
|
* Applies a reduced event to a slice and emits **precise** connector events.
|
|
@@ -832,18 +884,20 @@ class v {
|
|
|
832
884
|
* @internal
|
|
833
885
|
*/
|
|
834
886
|
forwardEvent(e, t) {
|
|
835
|
-
const
|
|
836
|
-
if (
|
|
837
|
-
const
|
|
838
|
-
if (
|
|
839
|
-
const
|
|
840
|
-
this.state = { ...this.state, [e]:
|
|
887
|
+
const r = this.state[e], n = this.reducers[e].reduce(r, t);
|
|
888
|
+
if (r === n) return !1;
|
|
889
|
+
const s = g(r, n).filter(Boolean);
|
|
890
|
+
if (s.length === 0) return !1;
|
|
891
|
+
const c = b(n);
|
|
892
|
+
if (this.state = { ...this.state, [e]: c }, this.changedPathSink)
|
|
893
|
+
for (const o of s)
|
|
894
|
+
this.changedPathSink.push(o ? `${e}.${o}` : e);
|
|
841
895
|
const i = /* @__PURE__ */ new Set();
|
|
842
|
-
for (const
|
|
843
|
-
for (const f of v.buildAncestorPaths(
|
|
844
|
-
for (const
|
|
845
|
-
const f = this.getAtPath(
|
|
846
|
-
this.connectorBus.emit(e,
|
|
896
|
+
for (const o of s)
|
|
897
|
+
for (const f of v.buildAncestorPaths(o)) i.add(f);
|
|
898
|
+
for (const o of i) {
|
|
899
|
+
const f = this.getAtPath(r, o), h = this.getAtPath(c, o);
|
|
900
|
+
this.connectorBus.emit(e, o, { oldValue: f, newValue: h, path: o });
|
|
847
901
|
}
|
|
848
902
|
return !0;
|
|
849
903
|
}
|
|
@@ -859,58 +913,67 @@ class v {
|
|
|
859
913
|
*/
|
|
860
914
|
__devtoolsIntrospect() {
|
|
861
915
|
const e = Object.keys(this.reducers).map((i) => {
|
|
862
|
-
const
|
|
863
|
-
return { name: i, when:
|
|
916
|
+
const o = this.patternReducers.get(i);
|
|
917
|
+
return { name: i, when: o };
|
|
864
918
|
}), t = [];
|
|
865
|
-
for (const [i,
|
|
866
|
-
if (
|
|
919
|
+
for (const [i, o] of this.effects) {
|
|
920
|
+
if (o.size === 0) continue;
|
|
867
921
|
const [f, h] = i.split("::");
|
|
868
|
-
for (const l of
|
|
869
|
-
const
|
|
870
|
-
t.push({ channel: f, type: h, name:
|
|
922
|
+
for (const l of o) {
|
|
923
|
+
const u = this.effectMeta.get(l);
|
|
924
|
+
t.push({ channel: f, type: h, name: u?.name, description: u?.description });
|
|
871
925
|
}
|
|
872
926
|
}
|
|
873
927
|
for (const i of this.patternEffects) {
|
|
874
|
-
const
|
|
928
|
+
const o = this.effectMeta.get(i.effect);
|
|
875
929
|
t.push({
|
|
876
930
|
channel: "*",
|
|
877
931
|
type: "*",
|
|
878
|
-
name:
|
|
879
|
-
description:
|
|
932
|
+
name: o?.name,
|
|
933
|
+
description: o?.description
|
|
880
934
|
});
|
|
881
935
|
}
|
|
882
|
-
const
|
|
936
|
+
const r = [];
|
|
883
937
|
for (const i of this.middleware)
|
|
884
|
-
typeof i == "function" ?
|
|
938
|
+
typeof i == "function" ? r.push({ name: i.name || void 0 }) : r.push({
|
|
885
939
|
name: i.meta?.name,
|
|
886
940
|
description: i.meta?.description,
|
|
887
941
|
when: i.when
|
|
888
942
|
});
|
|
889
943
|
const n = [];
|
|
890
944
|
for (const i of this.connectorBus.__introspect())
|
|
891
|
-
for (let
|
|
945
|
+
for (let o = 0; o < i.count; o++)
|
|
892
946
|
n.push({ reducer: i.channel, property: i.type });
|
|
893
|
-
const
|
|
894
|
-
for (const [i,
|
|
895
|
-
if (
|
|
947
|
+
const s = [];
|
|
948
|
+
for (const [i, o] of this.committedEventSubscribers) {
|
|
949
|
+
if (o.size === 0) continue;
|
|
896
950
|
const [f, h] = i.split("::");
|
|
897
|
-
for (let l = 0; l <
|
|
898
|
-
|
|
951
|
+
for (let l = 0; l < o.size; l++)
|
|
952
|
+
s.push({ channel: f, type: h, phase: "committed" });
|
|
899
953
|
}
|
|
900
|
-
for (const [i,
|
|
901
|
-
if (
|
|
954
|
+
for (const [i, o] of this.uncommittedEventSubscribers) {
|
|
955
|
+
if (o.size === 0) continue;
|
|
902
956
|
const [f, h] = i.split("::");
|
|
903
|
-
for (let l = 0; l <
|
|
904
|
-
|
|
957
|
+
for (let l = 0; l < o.size; l++)
|
|
958
|
+
s.push({ channel: f, type: h, phase: "uncommitted" });
|
|
905
959
|
}
|
|
906
|
-
for (const [i,
|
|
907
|
-
if (
|
|
960
|
+
for (const [i, o] of this.allEventSubscribers) {
|
|
961
|
+
if (o.size === 0) continue;
|
|
908
962
|
const [f, h] = i.split("::");
|
|
909
|
-
for (let l = 0; l <
|
|
910
|
-
|
|
963
|
+
for (let l = 0; l < o.size; l++)
|
|
964
|
+
s.push({ channel: f, type: h, phase: "all" });
|
|
911
965
|
}
|
|
912
|
-
const
|
|
913
|
-
return {
|
|
966
|
+
const c = this.listeners.size;
|
|
967
|
+
return {
|
|
968
|
+
reducers: e,
|
|
969
|
+
effects: t,
|
|
970
|
+
middleware: r,
|
|
971
|
+
atomic: n,
|
|
972
|
+
event: s,
|
|
973
|
+
coarse: c,
|
|
974
|
+
dedupHits: this.dedupCount,
|
|
975
|
+
queueDepth: this.reduceQueue.length + this.inFlightEffects
|
|
976
|
+
};
|
|
914
977
|
}
|
|
915
978
|
/**
|
|
916
979
|
* Applies an externally provided **whole-state** (e.g., DevTools time travel) and emits
|
|
@@ -919,27 +982,45 @@ class v {
|
|
|
919
982
|
* **State Immutability**: If any slices change, a new state object is created via
|
|
920
983
|
* shallow spread. This ensures consistent immutability with {@link forwardEvent}.
|
|
921
984
|
*
|
|
985
|
+
* **Missing slices**: the snapshot should contain every slice. A slice absent
|
|
986
|
+
* from `nextPlain` is **retained at its current value** (not blanked to
|
|
987
|
+
* `undefined`, which would make `getState().<slice>` throw on next access).
|
|
988
|
+
*
|
|
922
989
|
* @param nextPlain - Plain JS object to become the new state.
|
|
923
990
|
*
|
|
924
991
|
* @internal
|
|
925
992
|
*/
|
|
926
993
|
__applyExternalState(e) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
994
|
+
if (!this.replayEnabled) {
|
|
995
|
+
console.warn(
|
|
996
|
+
"[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
|
|
997
|
+
);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const t = this.state, r = e, n = { ...this.state };
|
|
1001
|
+
let s = !1;
|
|
1002
|
+
Object.keys(this.reducers).forEach((c) => {
|
|
1003
|
+
const i = t?.[c], o = r?.[c];
|
|
1004
|
+
if (o === void 0) {
|
|
1005
|
+
process.env.NODE_ENV !== "production" && console.warn(
|
|
1006
|
+
`[yoltra] External state is missing slice "${String(
|
|
1007
|
+
c
|
|
1008
|
+
)}"; retaining its current value. Time-travel snapshots should contain all slices.`
|
|
1009
|
+
);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (i === o) return;
|
|
1013
|
+
const f = b(o);
|
|
1014
|
+
n[c] = f, s = !0;
|
|
1015
|
+
const h = g(i, o).filter(Boolean);
|
|
935
1016
|
if (h.length === 0) return;
|
|
936
1017
|
const l = /* @__PURE__ */ new Set();
|
|
937
|
-
for (const
|
|
938
|
-
for (const
|
|
939
|
-
const
|
|
940
|
-
this.connectorBus.emit(
|
|
1018
|
+
for (const u of h) for (const p of v.buildAncestorPaths(u)) l.add(p);
|
|
1019
|
+
for (const u of l) {
|
|
1020
|
+
const p = this.getAtPath(i, u), y = this.getAtPath(f, u);
|
|
1021
|
+
this.connectorBus.emit(c, u, { oldValue: p, newValue: y, path: u });
|
|
941
1022
|
}
|
|
942
|
-
}),
|
|
1023
|
+
}), s && (this.state = n), s && this.listeners.forEach((c) => c());
|
|
943
1024
|
}
|
|
944
1025
|
/**
|
|
945
1026
|
* Replays a sequence of events from a snapshot through reducers and event
|
|
@@ -959,25 +1040,27 @@ class v {
|
|
|
959
1040
|
"[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
|
|
960
1041
|
);
|
|
961
1042
|
this.__applyExternalState(e);
|
|
962
|
-
for (const
|
|
963
|
-
const n =
|
|
1043
|
+
for (const r of t) {
|
|
1044
|
+
const n = r, s = this.state;
|
|
964
1045
|
this.reducerBus.emit(n.channel, n.type, n.payload);
|
|
965
|
-
for (const [
|
|
966
|
-
this.matchesWhen(f, n) && this.forwardEvent(
|
|
967
|
-
const
|
|
968
|
-
this.notifyEventSubscribers(n, "committed"), i && this.listeners.forEach((
|
|
1046
|
+
for (const [o, f] of this.patternReducers)
|
|
1047
|
+
this.matchesWhen(f, n) && this.forwardEvent(o, n);
|
|
1048
|
+
const c = this.state, i = s !== c;
|
|
1049
|
+
this.notifyEventSubscribers(n, "committed"), i && this.listeners.forEach((o) => o());
|
|
969
1050
|
}
|
|
970
1051
|
}
|
|
971
1052
|
/**
|
|
972
1053
|
* Emits a typed event `(channel, type, payload)`.
|
|
973
1054
|
* Events are queued and processed **sequentially** (FIFO).
|
|
974
1055
|
*
|
|
975
|
-
* **Pipeline per event:**
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
1056
|
+
* **Pipeline per event:** the *reduce phase* (steps 1-4) runs **synchronously**,
|
|
1057
|
+
* so `getState()` reflects the change as soon as `emit()` returns; the *effect
|
|
1058
|
+
* phase* (step 5) runs afterwards, asynchronously.
|
|
1059
|
+
* 1. **Deduplication** (opt-in) - Skip when content-dedup is enabled (`dedupWindowMs > 0`) or a matching `dedupKey` recurs; off by default
|
|
1060
|
+
* 2. **Middleware** (sync) - Pre-reducer hooks; may cancel by returning `false`
|
|
1061
|
+
* 3. **Reducers** (sync) - state updates + fine-grained path notifications
|
|
1062
|
+
* 4. **Subscribers + coarse** (sync) - event subscribers (fire-and-forget) then coarse listeners (only if state changed)
|
|
1063
|
+
* 5. **Effects** (async) - side-effects keyed by `(channel, type)`; the returned promise resolves once they complete
|
|
981
1064
|
*
|
|
982
1065
|
* **Change Detection**: Uses reference equality (`===`) on `this.state` to determine
|
|
983
1066
|
* if any slice changed. Works because {@link forwardEvent} creates a new state reference
|
|
@@ -988,7 +1071,9 @@ class v {
|
|
|
988
1071
|
* @param channel - Channel name.
|
|
989
1072
|
* @param type - Event type name.
|
|
990
1073
|
* @param payload - Payload typed as `EM[C][T]`.
|
|
991
|
-
* @
|
|
1074
|
+
* @param opts - Optional per-emit options (e.g. `dedupKey` for identity-based dedup).
|
|
1075
|
+
* @returns A promise that resolves once this event's effects have finished.
|
|
1076
|
+
* State is already updated synchronously before `emit()` returns.
|
|
992
1077
|
*
|
|
993
1078
|
* @example Basic usage
|
|
994
1079
|
* ```ts
|
|
@@ -1007,55 +1092,156 @@ class v {
|
|
|
1007
1092
|
*
|
|
1008
1093
|
* @public
|
|
1009
1094
|
*/
|
|
1010
|
-
async emit(e, t,
|
|
1011
|
-
const n = this.
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1095
|
+
async emit(e, t, r, n) {
|
|
1096
|
+
const s = n?.dedupKey, c = this.dedupConfig.windowMs;
|
|
1097
|
+
if (c > 0 || s !== void 0) {
|
|
1098
|
+
const h = s !== void 0 && c <= 0 ? w : c, l = s !== void 0 ? `${e}::${t}::#${s}` : this.fingerprint(e, t, r);
|
|
1099
|
+
if (this.shouldDedupe(l, h))
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const i = crypto.randomUUID();
|
|
1103
|
+
let o;
|
|
1104
|
+
const f = new Promise((h) => {
|
|
1105
|
+
o = h;
|
|
1106
|
+
});
|
|
1107
|
+
return this.reduceQueue.push({
|
|
1016
1108
|
channel: e,
|
|
1017
1109
|
type: t,
|
|
1018
|
-
payload:
|
|
1019
|
-
id:
|
|
1020
|
-
|
|
1021
|
-
|
|
1110
|
+
payload: r,
|
|
1111
|
+
id: i,
|
|
1112
|
+
resolve: o
|
|
1113
|
+
}), this.drainReduce(), f;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Drains the reduce queue **synchronously**. For each event it runs middleware,
|
|
1117
|
+
* reducers, event subscribers, and coarse listeners in the same tick, so
|
|
1118
|
+
* `getState()` reflects the change the moment {@link emit} returns. Re-entrant
|
|
1119
|
+
* emits (from middleware or subscribers) are appended and drained in the same
|
|
1120
|
+
* pass — preserving FIFO order without interleaving reducers. Each committed
|
|
1121
|
+
* event's effects then run in an independent task (see {@link runEventEffects}).
|
|
1122
|
+
*
|
|
1123
|
+
* @internal
|
|
1124
|
+
*/
|
|
1125
|
+
drainReduce() {
|
|
1126
|
+
if (!this.isReducing) {
|
|
1127
|
+
this.isReducing = !0;
|
|
1022
1128
|
try {
|
|
1023
|
-
for (; this.
|
|
1024
|
-
const { channel:
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
break;
|
|
1035
|
-
}
|
|
1036
|
-
} catch (S) {
|
|
1037
|
-
console.error("Middleware error:", S), l = !1;
|
|
1038
|
-
break;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
if (!l) {
|
|
1042
|
-
await this.notifyEventSubscribers(h, "uncommitted");
|
|
1043
|
-
continue;
|
|
1129
|
+
for (; this.reduceQueue.length > 0; ) {
|
|
1130
|
+
const { channel: e, type: t, payload: r, id: n, resolve: s } = this.reduceQueue.shift(), c = { channel: e, type: t, payload: r, id: n }, i = this.instrumentObservers.size > 0, o = i ? this.state : void 0, f = [];
|
|
1131
|
+
i && (this.changedPathSink = f);
|
|
1132
|
+
const h = i ? S() : 0;
|
|
1133
|
+
let l = !1;
|
|
1134
|
+
try {
|
|
1135
|
+
l = this.applyEventSync(c);
|
|
1136
|
+
} catch (u) {
|
|
1137
|
+
console.error("Emit reduce error:", u);
|
|
1138
|
+
} finally {
|
|
1139
|
+
i && (this.changedPathSink = null);
|
|
1044
1140
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1141
|
+
i && this.emitInstrumentation(
|
|
1142
|
+
c,
|
|
1143
|
+
l,
|
|
1144
|
+
f,
|
|
1145
|
+
o,
|
|
1146
|
+
S() - h
|
|
1147
|
+
), this.runEventEffects(c, l, s);
|
|
1051
1148
|
}
|
|
1052
|
-
} catch (o) {
|
|
1053
|
-
console.error("Emit queue error:", o);
|
|
1054
1149
|
} finally {
|
|
1055
|
-
this.
|
|
1150
|
+
this.isReducing = !1;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Runs the **synchronous** part of the pipeline for a single event: middleware
|
|
1156
|
+
* (may veto), key- and pattern-based reducers, committed/uncommitted event
|
|
1157
|
+
* subscribers (fire-and-forget), and coarse listeners.
|
|
1158
|
+
*
|
|
1159
|
+
* @returns `true` if the event was committed (passed middleware), `false` if a
|
|
1160
|
+
* middleware vetoed it.
|
|
1161
|
+
*
|
|
1162
|
+
* @internal
|
|
1163
|
+
*/
|
|
1164
|
+
applyEventSync(e) {
|
|
1165
|
+
for (const n of this.middleware) {
|
|
1166
|
+
const s = this.getMiddlewareWhen(n);
|
|
1167
|
+
if (!this.matchesWhen(s, e)) continue;
|
|
1168
|
+
const c = this.getMiddlewareFunction(n);
|
|
1169
|
+
let i;
|
|
1170
|
+
try {
|
|
1171
|
+
i = c(this.state, e, this.emit);
|
|
1172
|
+
} catch (o) {
|
|
1173
|
+
console.error("Middleware error:", o), i = !1;
|
|
1056
1174
|
}
|
|
1175
|
+
if (!i)
|
|
1176
|
+
return this.notifyEventSubscribers(e, "uncommitted"), !1;
|
|
1177
|
+
}
|
|
1178
|
+
const t = this.state;
|
|
1179
|
+
this.reducerBus.emit(e.channel, e.type, e.payload);
|
|
1180
|
+
for (const [n, s] of this.patternReducers)
|
|
1181
|
+
this.matchesWhen(s, e) && this.forwardEvent(n, e);
|
|
1182
|
+
const r = t !== this.state;
|
|
1183
|
+
return this.notifyEventSubscribers(e, "committed"), r && this.listeners.forEach((n) => n()), !0;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Runs a single committed event's effects as an **independent async task**,
|
|
1187
|
+
* then resolves that event's completion deferred so `await emit(...)` settles
|
|
1188
|
+
* once its effects finish. Per-event tasks (rather than one shared serialized
|
|
1189
|
+
* loop) let an effect `await` a re-entrant emit without deadlocking.
|
|
1190
|
+
*
|
|
1191
|
+
* @internal
|
|
1192
|
+
*/
|
|
1193
|
+
async runEventEffects(e, t, r) {
|
|
1194
|
+
this.inFlightEffects++;
|
|
1195
|
+
try {
|
|
1196
|
+
t && await this.notifyEffects(e);
|
|
1197
|
+
} catch (n) {
|
|
1198
|
+
console.error("Effect error:", n);
|
|
1199
|
+
} finally {
|
|
1200
|
+
this.inFlightEffects--, r();
|
|
1057
1201
|
}
|
|
1058
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Registers an instrumentation observer. See {@link StoreInstance.instrument}.
|
|
1205
|
+
*
|
|
1206
|
+
* @public
|
|
1207
|
+
*/
|
|
1208
|
+
instrument(e) {
|
|
1209
|
+
return this.instrumentObservers.add(e), () => {
|
|
1210
|
+
this.instrumentObservers.delete(e);
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Builds an {@link InstrumentedEvent} from the reduce result and notifies
|
|
1215
|
+
* observers. `changedPaths` are the exact slice-prefixed leaf paths recorded
|
|
1216
|
+
* by {@link forwardEvent} during this reduce, so DevTools patches need no
|
|
1217
|
+
* re-diff.
|
|
1218
|
+
*
|
|
1219
|
+
* @internal
|
|
1220
|
+
*/
|
|
1221
|
+
emitInstrumentation(e, t, r, n, s) {
|
|
1222
|
+
const c = {}, i = {};
|
|
1223
|
+
for (const f of r)
|
|
1224
|
+
c[f] = this.getAtPath(n, f), i[f] = this.getAtPath(this.state, f);
|
|
1225
|
+
const o = {
|
|
1226
|
+
event: {
|
|
1227
|
+
id: e.id,
|
|
1228
|
+
channel: e.channel,
|
|
1229
|
+
type: e.type,
|
|
1230
|
+
payload: e.payload
|
|
1231
|
+
},
|
|
1232
|
+
committed: t,
|
|
1233
|
+
changedPaths: r,
|
|
1234
|
+
prevValues: c,
|
|
1235
|
+
nextValues: i,
|
|
1236
|
+
reduceTimeMs: s
|
|
1237
|
+
};
|
|
1238
|
+
for (const f of [...this.instrumentObservers])
|
|
1239
|
+
try {
|
|
1240
|
+
f(o);
|
|
1241
|
+
} catch (h) {
|
|
1242
|
+
console.error("Instrumentation observer error:", h);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1059
1245
|
/**
|
|
1060
1246
|
* Connects a **fine-grained** listener to a dotted path under a slice.
|
|
1061
1247
|
*
|
|
@@ -1133,11 +1319,11 @@ class v {
|
|
|
1133
1319
|
*
|
|
1134
1320
|
* @public
|
|
1135
1321
|
*/
|
|
1136
|
-
onEvent(e, t,
|
|
1137
|
-
const
|
|
1138
|
-
return
|
|
1139
|
-
const i =
|
|
1140
|
-
i && (i.delete(
|
|
1322
|
+
onEvent(e, t, r, n = "committed") {
|
|
1323
|
+
const s = `${e}::${String(t)}`, c = n === "committed" ? this.committedEventSubscribers : n === "uncommitted" ? this.uncommittedEventSubscribers : this.allEventSubscribers;
|
|
1324
|
+
return c.has(s) || c.set(s, /* @__PURE__ */ new Set()), c.get(s).add(r), () => {
|
|
1325
|
+
const i = c.get(s);
|
|
1326
|
+
i && (i.delete(r), i.size === 0 && c.delete(s));
|
|
1141
1327
|
};
|
|
1142
1328
|
}
|
|
1143
1329
|
/**
|
|
@@ -1234,8 +1420,8 @@ class v {
|
|
|
1234
1420
|
if (e in this.reducers) throw new Error(`Reducer ${e} already exists`);
|
|
1235
1421
|
return this.mountSlice(e, t, {
|
|
1236
1422
|
preserveState: !1
|
|
1237
|
-
}), this.listeners.forEach((
|
|
1238
|
-
this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((
|
|
1423
|
+
}), this.listeners.forEach((r) => r()), () => {
|
|
1424
|
+
this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((r) => r());
|
|
1239
1425
|
};
|
|
1240
1426
|
}
|
|
1241
1427
|
/**
|
|
@@ -1271,29 +1457,29 @@ class v {
|
|
|
1271
1457
|
* @public
|
|
1272
1458
|
*/
|
|
1273
1459
|
registerEffect(e) {
|
|
1274
|
-
const { effect: t, meta:
|
|
1275
|
-
if (
|
|
1276
|
-
const
|
|
1277
|
-
return this.patternEffects.add(
|
|
1278
|
-
this.patternEffects.delete(
|
|
1460
|
+
const { effect: t, meta: r, when: n } = e, s = [];
|
|
1461
|
+
if (r && this.effectMeta.set(t, r), n && ("any" in n && n.any === !0 || "channel" in n || "channels" in n)) {
|
|
1462
|
+
const o = { effect: t, when: n };
|
|
1463
|
+
return this.patternEffects.add(o), () => {
|
|
1464
|
+
this.patternEffects.delete(o);
|
|
1279
1465
|
};
|
|
1280
1466
|
}
|
|
1281
1467
|
const i = this.normalizeEventKeys(e);
|
|
1282
1468
|
if (i.length === 0 && !n && !e.events) {
|
|
1283
|
-
const
|
|
1284
|
-
return this.patternEffects.add(
|
|
1285
|
-
this.patternEffects.delete(
|
|
1469
|
+
const o = { effect: t, when: { any: !0 } };
|
|
1470
|
+
return this.patternEffects.add(o), () => {
|
|
1471
|
+
this.patternEffects.delete(o);
|
|
1286
1472
|
};
|
|
1287
1473
|
}
|
|
1288
|
-
for (const [
|
|
1289
|
-
const h = `${String(
|
|
1290
|
-
this.effects.has(h) || this.effects.set(h, /* @__PURE__ */ new Set()), this.effects.get(h).add(t),
|
|
1474
|
+
for (const [o, f] of i) {
|
|
1475
|
+
const h = `${String(o)}::${String(f)}`;
|
|
1476
|
+
this.effects.has(h) || this.effects.set(h, /* @__PURE__ */ new Set()), this.effects.get(h).add(t), s.push(() => {
|
|
1291
1477
|
const l = this.effects.get(h);
|
|
1292
1478
|
l && (l.delete(t), l.size === 0 && this.effects.delete(h));
|
|
1293
1479
|
});
|
|
1294
1480
|
}
|
|
1295
1481
|
return () => {
|
|
1296
|
-
for (const
|
|
1482
|
+
for (const o of s) o();
|
|
1297
1483
|
};
|
|
1298
1484
|
}
|
|
1299
1485
|
/**
|
|
@@ -1317,11 +1503,11 @@ class v {
|
|
|
1317
1503
|
*
|
|
1318
1504
|
* @public
|
|
1319
1505
|
*/
|
|
1320
|
-
onEffect(e, t,
|
|
1321
|
-
const n = async (
|
|
1322
|
-
if (
|
|
1323
|
-
const
|
|
1324
|
-
return
|
|
1506
|
+
onEffect(e, t, r) {
|
|
1507
|
+
const n = async (s, c, i) => {
|
|
1508
|
+
if (s.channel !== e || s.type !== t) return;
|
|
1509
|
+
const o = s;
|
|
1510
|
+
return r(o.payload, c, i, o);
|
|
1325
1511
|
};
|
|
1326
1512
|
return this.registerEffect({
|
|
1327
1513
|
events: [[e, t]],
|
|
@@ -1387,11 +1573,11 @@ class v {
|
|
|
1387
1573
|
* @public
|
|
1388
1574
|
*/
|
|
1389
1575
|
replaceReducers(e, t = {}) {
|
|
1390
|
-
const
|
|
1576
|
+
const r = t.preserveState !== !1, n = new Set(Object.keys(this.reducers)), s = Object.entries(e), c = new Set(s.map(([i]) => i));
|
|
1391
1577
|
for (const i of n)
|
|
1392
|
-
|
|
1393
|
-
for (const [i,
|
|
1394
|
-
n.has(i) ? (this.unmountSlice(i, { deleteState: !1 }), this.mountSlice(i,
|
|
1578
|
+
c.has(i) || this.unmountSlice(i, { deleteState: !0 });
|
|
1579
|
+
for (const [i, o] of s)
|
|
1580
|
+
n.has(i) ? (this.unmountSlice(i, { deleteState: !1 }), this.mountSlice(i, o, { preserveState: r })) : this.mountSlice(i, o, { preserveState: !1 });
|
|
1395
1581
|
}
|
|
1396
1582
|
/**
|
|
1397
1583
|
* Convenience API to replace **any subset** of store parts (HMR patterns).
|
|
@@ -1423,24 +1609,24 @@ class v {
|
|
|
1423
1609
|
*
|
|
1424
1610
|
* @internal
|
|
1425
1611
|
*/
|
|
1426
|
-
mountSlice(e, t,
|
|
1427
|
-
const n = e, { events:
|
|
1428
|
-
if (this.reducers[e] = new _(
|
|
1429
|
-
this.patternReducers.set(e,
|
|
1612
|
+
mountSlice(e, t, r) {
|
|
1613
|
+
const n = e, { events: s, reducer: c, state: i, when: o } = t;
|
|
1614
|
+
if (this.reducers[e] = new _(c), (!r.preserveState || this.state[n] === void 0) && (this.state[n] = b(structuredClone(i))), o && ("any" in o && o.any === !0 || "channel" in o || "channels" in o)) {
|
|
1615
|
+
this.patternReducers.set(e, o), this.sliceUnsubs.set(n, []);
|
|
1430
1616
|
return;
|
|
1431
1617
|
}
|
|
1432
1618
|
const h = this.normalizeEventKeys(t);
|
|
1433
|
-
if (h.length === 0 && !
|
|
1619
|
+
if (h.length === 0 && !o && !s) {
|
|
1434
1620
|
this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(n, []);
|
|
1435
1621
|
return;
|
|
1436
1622
|
}
|
|
1437
1623
|
const l = [];
|
|
1438
|
-
for (const [
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
this.forwardEvent(e,
|
|
1624
|
+
for (const [u, p] of h) {
|
|
1625
|
+
const y = this.reducerBus.on(u, p, (m) => {
|
|
1626
|
+
const M = { channel: u, type: p, payload: m, id: crypto.randomUUID() };
|
|
1627
|
+
this.forwardEvent(e, M);
|
|
1442
1628
|
});
|
|
1443
|
-
l.push(
|
|
1629
|
+
l.push(y);
|
|
1444
1630
|
}
|
|
1445
1631
|
this.sliceUnsubs.set(n, l);
|
|
1446
1632
|
}
|
|
@@ -1454,19 +1640,19 @@ class v {
|
|
|
1454
1640
|
* @internal
|
|
1455
1641
|
*/
|
|
1456
1642
|
unmountSlice(e, t) {
|
|
1457
|
-
const
|
|
1643
|
+
const r = e;
|
|
1458
1644
|
this.patternReducers.delete(e);
|
|
1459
|
-
const n = this.sliceUnsubs.get(
|
|
1645
|
+
const n = this.sliceUnsubs.get(r);
|
|
1460
1646
|
if (n) {
|
|
1461
|
-
for (const
|
|
1647
|
+
for (const s of n)
|
|
1462
1648
|
try {
|
|
1463
|
-
|
|
1464
|
-
} catch (
|
|
1465
|
-
console.error(`[Store error]: ${
|
|
1649
|
+
s();
|
|
1650
|
+
} catch (c) {
|
|
1651
|
+
console.error(`[Store error]: ${c}`);
|
|
1466
1652
|
}
|
|
1467
|
-
this.sliceUnsubs.delete(
|
|
1653
|
+
this.sliceUnsubs.delete(r);
|
|
1468
1654
|
}
|
|
1469
|
-
delete this.reducers[e], t.deleteState && delete this.state[
|
|
1655
|
+
delete this.reducers[e], t.deleteState && delete this.state[r];
|
|
1470
1656
|
}
|
|
1471
1657
|
/**
|
|
1472
1658
|
* Normalizes event targeting from `when` or legacy `events` to an array of EventKeys.
|
|
@@ -1502,12 +1688,12 @@ class v {
|
|
|
1502
1688
|
getAtPath(e, t) {
|
|
1503
1689
|
if (!t) return e;
|
|
1504
1690
|
const n = (t[0] === "." ? t.slice(1) : t).split(".");
|
|
1505
|
-
let
|
|
1506
|
-
for (const
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1691
|
+
let s = e;
|
|
1692
|
+
for (const c of n) {
|
|
1693
|
+
if (s == null) return;
|
|
1694
|
+
s = s[c];
|
|
1509
1695
|
}
|
|
1510
|
-
return
|
|
1696
|
+
return s;
|
|
1511
1697
|
}
|
|
1512
1698
|
/**
|
|
1513
1699
|
* Builds ancestor paths for a dotted path.
|
|
@@ -1526,32 +1712,33 @@ class v {
|
|
|
1526
1712
|
*/
|
|
1527
1713
|
static buildAncestorPaths(e) {
|
|
1528
1714
|
if (!e) return [];
|
|
1529
|
-
const
|
|
1530
|
-
for (let
|
|
1531
|
-
n.push(
|
|
1715
|
+
const r = (e[0] === "." ? e.slice(1) : e).split("."), n = [];
|
|
1716
|
+
for (let s = 0; s < r.length; s++)
|
|
1717
|
+
n.push(r.slice(0, s + 1).join("."));
|
|
1532
1718
|
return n;
|
|
1533
1719
|
}
|
|
1534
1720
|
}
|
|
1535
|
-
function
|
|
1721
|
+
function R(a) {
|
|
1536
1722
|
return new v({
|
|
1537
1723
|
name: a.name,
|
|
1538
1724
|
reducer: a.reducer ?? {},
|
|
1539
1725
|
middleware: a.middleware ?? [],
|
|
1540
1726
|
effects: a.effects ?? [],
|
|
1541
1727
|
dedupWindowMs: a.dedupWindowMs,
|
|
1542
|
-
devtools: a.devtools
|
|
1728
|
+
devtools: a.devtools,
|
|
1729
|
+
onEffectError: a.onEffectError
|
|
1543
1730
|
});
|
|
1544
1731
|
}
|
|
1545
|
-
const
|
|
1732
|
+
const $ = (a) => (e, t) => t.map((r) => [e, r]), B = () => (a) => a;
|
|
1546
1733
|
export {
|
|
1547
|
-
|
|
1734
|
+
O as EventBus,
|
|
1548
1735
|
k as LooseEventBus,
|
|
1549
1736
|
_ as Reducer,
|
|
1550
1737
|
v as Store,
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1738
|
+
R as createStore,
|
|
1739
|
+
g as detectChangedProps,
|
|
1740
|
+
B as eventKeys,
|
|
1741
|
+
E as freezeState,
|
|
1742
|
+
$ as typedEvents
|
|
1556
1743
|
};
|
|
1557
1744
|
//# sourceMappingURL=yoltra.esm.js.map
|