@yoltra/core 0.3.0 → 0.4.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 +1 -1
- package/README.md +112 -7
- package/dist/types/entity/entityAdapter.d.ts +118 -0
- package/dist/types/eventBus/EventBus.d.ts +18 -5
- package/dist/types/eventBus/LooseEventBus.d.ts +72 -49
- package/dist/types/index.d.ts +9 -1
- package/dist/types/persistence/adapters.d.ts +33 -0
- package/dist/types/persistence/persist.d.ts +126 -0
- package/dist/types/serialize/codec.d.ts +119 -0
- package/dist/types/store/Store.d.ts +73 -15
- package/dist/types/types.d.ts +129 -29
- package/dist/types/utils/immutability.d.ts +24 -1
- package/dist/yoltra.cjs.js +2 -2
- package/dist/yoltra.esm.js +1090 -460
- package/dist/yoltra.umd.js +2 -2
- package/package.json +23 -3
package/dist/yoltra.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @yoltra/core v0.
|
|
2
|
+
* @yoltra/core v0.4.0
|
|
3
3
|
* (c) 2026 Manu Ramirez <@pixerael>
|
|
4
4
|
* License: MIT
|
|
5
5
|
* Homepage: https://yoltra.dev
|
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
* This source code is licensed under the MIT license found in the
|
|
8
8
|
* LICENSE file in the root directory of this source tree
|
|
9
9
|
*/
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
class
|
|
10
|
+
var T = Object.defineProperty;
|
|
11
|
+
var C = (o, e, t) => e in o ? T(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
|
|
12
|
+
var p = (o, e, t) => C(o, typeof e != "symbol" ? e + "" : e, t);
|
|
13
|
+
class B {
|
|
14
14
|
constructor() {
|
|
15
15
|
/**
|
|
16
16
|
* Internal registry: `channel → type → Set<handler>`.
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
p(this, "handlers", /* @__PURE__ */ new Map());
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Subscribes a handler to an exact `(channel, type)`.
|
|
@@ -25,7 +25,10 @@ class O {
|
|
|
25
25
|
* @typeParam T - Type key within channel `C` (must be a string key of `EM[C]`).
|
|
26
26
|
* @param channel - Channel name to subscribe to.
|
|
27
27
|
* @param type - Event type within the channel.
|
|
28
|
-
* @param handler - Function invoked with the payload type `EM[C][T]`.
|
|
28
|
+
* @param handler - Function invoked with the payload type `EM[C][T]`. It optionally
|
|
29
|
+
* receives the **source event** as a second argument when the emitter supplies one, so
|
|
30
|
+
* subscribers can read the true `id` (and any `meta`) instead of reconstructing an event
|
|
31
|
+
* from the payload alone. Handlers that declare only `payload` remain valid.
|
|
29
32
|
* @returns An **unsubscribe** function that removes this handler.
|
|
30
33
|
*
|
|
31
34
|
* @example
|
|
@@ -38,13 +41,20 @@ class O {
|
|
|
38
41
|
* off();
|
|
39
42
|
* ```
|
|
40
43
|
*
|
|
44
|
+
* @example Reading the source event
|
|
45
|
+
* ```ts
|
|
46
|
+
* bus.on('data', 'loaded', (payload, event) => {
|
|
47
|
+
* console.log('event id:', event?.id);
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
41
51
|
* @public
|
|
42
52
|
*/
|
|
43
|
-
on(e, t,
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
let
|
|
47
|
-
return
|
|
53
|
+
on(e, t, n) {
|
|
54
|
+
let s = this.handlers.get(e);
|
|
55
|
+
s || (s = /* @__PURE__ */ new Map(), this.handlers.set(e, s));
|
|
56
|
+
let i = s.get(t);
|
|
57
|
+
return i || (i = /* @__PURE__ */ new Set(), s.set(t, i)), i.add(n), () => this.off(e, t, n);
|
|
48
58
|
}
|
|
49
59
|
/**
|
|
50
60
|
* Removes a specific handler previously added with {@link EventBus.on | `on`}.
|
|
@@ -66,11 +76,11 @@ class O {
|
|
|
66
76
|
*
|
|
67
77
|
* @public
|
|
68
78
|
*/
|
|
69
|
-
off(e, t,
|
|
70
|
-
const
|
|
71
|
-
if (!
|
|
72
|
-
const
|
|
73
|
-
|
|
79
|
+
off(e, t, n) {
|
|
80
|
+
const s = this.handlers.get(e);
|
|
81
|
+
if (!s) return;
|
|
82
|
+
const i = s.get(t);
|
|
83
|
+
i && (i.delete(n), i.size === 0 && s.delete(t), s.size === 0 && this.handlers.delete(e));
|
|
74
84
|
}
|
|
75
85
|
/**
|
|
76
86
|
* Emits an event to all subscribers of the exact `(channel, type)`.
|
|
@@ -83,6 +93,9 @@ class O {
|
|
|
83
93
|
* @param channel - Channel name to emit on.
|
|
84
94
|
* @param type - Event type to emit.
|
|
85
95
|
* @param payload - Payload matching `EM[C][T]`.
|
|
96
|
+
* @param event - Optional **source event**, forwarded to handlers as a second argument.
|
|
97
|
+
* Supply it whenever the caller already holds the real event so subscribers observe its
|
|
98
|
+
* true `id` rather than reconstructing one; omitting it keeps the original behaviour.
|
|
86
99
|
*
|
|
87
100
|
* @example
|
|
88
101
|
* ```ts
|
|
@@ -91,16 +104,16 @@ class O {
|
|
|
91
104
|
*
|
|
92
105
|
* @public
|
|
93
106
|
*/
|
|
94
|
-
emit(e, t,
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
97
|
-
const
|
|
98
|
-
if (!(!
|
|
99
|
-
for (const
|
|
107
|
+
emit(e, t, n, s) {
|
|
108
|
+
const i = this.handlers.get(e);
|
|
109
|
+
if (!i) return;
|
|
110
|
+
const d = i.get(t);
|
|
111
|
+
if (!(!d || d.size === 0))
|
|
112
|
+
for (const a of [...d])
|
|
100
113
|
try {
|
|
101
|
-
|
|
102
|
-
} catch (
|
|
103
|
-
console.error("EventBus handler error:",
|
|
114
|
+
a(n, s);
|
|
115
|
+
} catch (r) {
|
|
116
|
+
console.error("EventBus handler error:", r);
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
119
|
/**
|
|
@@ -120,18 +133,36 @@ class O {
|
|
|
120
133
|
this.handlers.clear();
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
|
-
class
|
|
136
|
+
class H {
|
|
124
137
|
constructor() {
|
|
125
138
|
/**
|
|
126
139
|
* Exact handlers: `channel → type → [handlers]`.
|
|
127
140
|
* @internal
|
|
128
141
|
*/
|
|
129
|
-
|
|
142
|
+
p(this, "handlers", /* @__PURE__ */ new Map());
|
|
130
143
|
/**
|
|
131
144
|
* Pattern handlers with `*` and `**`: `channel → pattern(string) → [handlers]`.
|
|
132
145
|
* @internal
|
|
133
146
|
*/
|
|
134
|
-
|
|
147
|
+
p(this, "patternHandlers", /* @__PURE__ */ new Map());
|
|
148
|
+
/**
|
|
149
|
+
* Patterns bucketed by their first segment, so an emit tests only what could match.
|
|
150
|
+
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* Delivery used to walk every pattern registered on the channel and run the full segment
|
|
153
|
+
* matcher against each. That is linear in the number of patterns rather than in the number
|
|
154
|
+
* that match, and it re-split both the pattern and the subject on every test — for a thousand
|
|
155
|
+
* patterns, two thousand string splits to deliver one event.
|
|
156
|
+
*
|
|
157
|
+
* A subject's first segment can only be matched by a pattern whose first segment is that same
|
|
158
|
+
* literal, or is `*` or `**`. Bucketing on that turns the common shape — distinct event
|
|
159
|
+
* families like `panel.*` and `order.**` — from a scan of everything into a map lookup plus
|
|
160
|
+
* the handful that begin with a wildcard.
|
|
161
|
+
*
|
|
162
|
+
* It buys nothing for a channel where every pattern starts with `**`, since all of those must
|
|
163
|
+
* still be tested. That is the honest worst case, and it is unchanged rather than worsened.
|
|
164
|
+
*/
|
|
165
|
+
p(this, "patternIndex", /* @__PURE__ */ new Map());
|
|
135
166
|
}
|
|
136
167
|
/**
|
|
137
168
|
* Subscribes a handler to either an **exact** type or a **pattern**.
|
|
@@ -165,18 +196,18 @@ class k {
|
|
|
165
196
|
*
|
|
166
197
|
* @public
|
|
167
198
|
*/
|
|
168
|
-
on(e, t,
|
|
169
|
-
const
|
|
170
|
-
if (this.isPattern(
|
|
171
|
-
const
|
|
199
|
+
on(e, t, n) {
|
|
200
|
+
const s = String(t);
|
|
201
|
+
if (this.isPattern(s)) {
|
|
202
|
+
const i = s;
|
|
172
203
|
this.patternHandlers.has(e) || this.patternHandlers.set(e, /* @__PURE__ */ new Map());
|
|
173
|
-
const
|
|
174
|
-
return
|
|
204
|
+
const d = this.patternHandlers.get(e);
|
|
205
|
+
return d.has(i) || (d.set(i, []), this.indexPattern(e, i)), d.get(i).push(n), () => this.offPattern(e, i, n);
|
|
175
206
|
} else {
|
|
176
|
-
const
|
|
207
|
+
const i = this.normalizeTypeKey(s);
|
|
177
208
|
this.handlers.has(e) || this.handlers.set(e, /* @__PURE__ */ new Map());
|
|
178
|
-
const
|
|
179
|
-
return
|
|
209
|
+
const d = this.handlers.get(e);
|
|
210
|
+
return d.has(i) || d.set(i, []), d.get(i).push(n), () => this.offExactNormalized(e, i, n);
|
|
180
211
|
}
|
|
181
212
|
}
|
|
182
213
|
/**
|
|
@@ -197,9 +228,9 @@ class k {
|
|
|
197
228
|
*
|
|
198
229
|
* @public
|
|
199
230
|
*/
|
|
200
|
-
off(e, t,
|
|
201
|
-
const
|
|
202
|
-
this.offExactNormalized(e,
|
|
231
|
+
off(e, t, n) {
|
|
232
|
+
const s = this.normalizeTypeKey(String(t));
|
|
233
|
+
this.offExactNormalized(e, s, n);
|
|
203
234
|
}
|
|
204
235
|
/**
|
|
205
236
|
* Internal exact unsubscription using an already **normalized** type key.
|
|
@@ -209,13 +240,13 @@ class k {
|
|
|
209
240
|
* @param handler - Handler to remove.
|
|
210
241
|
* @internal
|
|
211
242
|
*/
|
|
212
|
-
offExactNormalized(e, t,
|
|
213
|
-
const
|
|
214
|
-
if (!n) return;
|
|
215
|
-
const s = n.get(t);
|
|
243
|
+
offExactNormalized(e, t, n) {
|
|
244
|
+
const s = this.handlers.get(e);
|
|
216
245
|
if (!s) return;
|
|
217
|
-
const
|
|
218
|
-
|
|
246
|
+
const i = s.get(t);
|
|
247
|
+
if (!i) return;
|
|
248
|
+
const d = i.indexOf(n);
|
|
249
|
+
d !== -1 && i.splice(d, 1), i.length === 0 && s.delete(t), s.size === 0 && this.handlers.delete(e);
|
|
219
250
|
}
|
|
220
251
|
/**
|
|
221
252
|
* Internal removal for a **pattern** subscription. No-ops if missing.
|
|
@@ -225,13 +256,13 @@ class k {
|
|
|
225
256
|
* @param handler - Handler to remove.
|
|
226
257
|
* @internal
|
|
227
258
|
*/
|
|
228
|
-
offPattern(e, t,
|
|
229
|
-
const
|
|
230
|
-
if (!n) return;
|
|
231
|
-
const s = n.get(t);
|
|
259
|
+
offPattern(e, t, n) {
|
|
260
|
+
const s = this.patternHandlers.get(e);
|
|
232
261
|
if (!s) return;
|
|
233
|
-
const
|
|
234
|
-
|
|
262
|
+
const i = s.get(t);
|
|
263
|
+
if (!i) return;
|
|
264
|
+
const d = i.indexOf(n);
|
|
265
|
+
d !== -1 && i.splice(d, 1), i.length === 0 && (s.delete(t), this.unindexPattern(e, t)), s.size === 0 && (this.patternHandlers.delete(e), this.patternIndex.delete(e));
|
|
235
266
|
}
|
|
236
267
|
/**
|
|
237
268
|
* Emits an event to all exact subscribers first, then to **matching pattern** subscribers.
|
|
@@ -253,27 +284,55 @@ class k {
|
|
|
253
284
|
*
|
|
254
285
|
* @public
|
|
255
286
|
*/
|
|
256
|
-
emit(e, t,
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
287
|
+
emit(e, t, n) {
|
|
288
|
+
const s = String(t), i = this.normalizeTypeKey(s), d = this.handlers.get(e)?.get(i) ?? [], a = this.matchingPatternHandlers(e, s), r = /* @__PURE__ */ new Set(), f = (c) => {
|
|
289
|
+
for (const u of [...c])
|
|
290
|
+
if (!r.has(u)) {
|
|
291
|
+
r.add(u);
|
|
292
|
+
try {
|
|
293
|
+
u(n);
|
|
294
|
+
} catch (l) {
|
|
295
|
+
console.error(l);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
f(d);
|
|
301
|
+
for (const c of a) f(c);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Emits a payload that is only built if somebody is listening.
|
|
305
|
+
*
|
|
306
|
+
* @param channel - Channel to emit on.
|
|
307
|
+
* @param type - Concrete event type.
|
|
308
|
+
* @param make - Builds the payload. Called at most once, and only when a handler matched.
|
|
309
|
+
*
|
|
310
|
+
* @remarks
|
|
311
|
+
* Same matching as {@link LooseEventBus.emit}; the difference is *when* the payload exists.
|
|
312
|
+
* The store's change notification carries the old and new value at a path, and reading those
|
|
313
|
+
* means walking the state tree twice per path. Doing that eagerly meant a slice nobody had
|
|
314
|
+
* subscribed to paid the full cost of describing changes to an audience of nobody — the
|
|
315
|
+
* matching work was already being done to discover there were no handlers.
|
|
316
|
+
*
|
|
317
|
+
* @public
|
|
318
|
+
*/
|
|
319
|
+
emitWith(e, t, n) {
|
|
320
|
+
const s = String(t), i = this.normalizeTypeKey(s), d = this.handlers.get(e)?.get(i) ?? [], a = this.matchingPatternHandlers(e, s);
|
|
321
|
+
if (d.length === 0 && a.length === 0) return;
|
|
322
|
+
const r = n(), f = /* @__PURE__ */ new Set(), c = (u) => {
|
|
323
|
+
for (const l of [...u])
|
|
324
|
+
if (!f.has(l)) {
|
|
325
|
+
f.add(l);
|
|
267
326
|
try {
|
|
268
|
-
|
|
269
|
-
} catch (
|
|
270
|
-
console.error(
|
|
327
|
+
l(r);
|
|
328
|
+
} catch (y) {
|
|
329
|
+
console.error(y);
|
|
271
330
|
continue;
|
|
272
331
|
}
|
|
273
332
|
}
|
|
274
333
|
};
|
|
275
|
-
|
|
276
|
-
for (const
|
|
334
|
+
c(d);
|
|
335
|
+
for (const u of a) c(u);
|
|
277
336
|
}
|
|
278
337
|
/**
|
|
279
338
|
* Determines if a string is a **pattern** (contains `*`).
|
|
@@ -308,42 +367,100 @@ class k {
|
|
|
308
367
|
return this.normalizeTypeKey(e).split(".").filter(Boolean);
|
|
309
368
|
}
|
|
310
369
|
/**
|
|
311
|
-
*
|
|
370
|
+
* Files a pattern under the first segment that could select it.
|
|
371
|
+
* @internal
|
|
372
|
+
*/
|
|
373
|
+
indexPattern(e, t) {
|
|
374
|
+
let n = this.patternIndex.get(e);
|
|
375
|
+
n === void 0 && (n = { byHead: /* @__PURE__ */ new Map(), anyHead: [] }, this.patternIndex.set(e, n));
|
|
376
|
+
const s = this.splitPath(t), i = { pattern: t, segments: s }, d = s[0];
|
|
377
|
+
if (d === void 0 || d === "*" || d === "**") {
|
|
378
|
+
n.anyHead.push(i);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const a = n.byHead.get(d);
|
|
382
|
+
a === void 0 ? n.byHead.set(d, [i]) : a.push(i);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Removes a pattern from the index. Paired with {@link LooseEventBus.offPattern}.
|
|
386
|
+
* @internal
|
|
387
|
+
*/
|
|
388
|
+
unindexPattern(e, t) {
|
|
389
|
+
const n = this.patternIndex.get(e);
|
|
390
|
+
if (n === void 0) return;
|
|
391
|
+
const s = this.splitPath(t)[0], i = s === void 0 || s === "*" || s === "**" ? n.anyHead : n.byHead.get(s);
|
|
392
|
+
if (i === void 0) return;
|
|
393
|
+
const d = i.findIndex((a) => a.pattern === t);
|
|
394
|
+
d !== -1 && i.splice(d, 1), i.length === 0 && i !== n.anyHead && s !== void 0 && n.byHead.delete(s);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* The handler lists of every pattern matching this subject.
|
|
398
|
+
*
|
|
399
|
+
* @remarks
|
|
400
|
+
* Shared by `emit` and `emitWith` so the two cannot drift on what "matching" means — which
|
|
401
|
+
* they could, being two copies of the same walk before.
|
|
402
|
+
*
|
|
403
|
+
* The subject is split once here rather than once per pattern tested.
|
|
404
|
+
*
|
|
405
|
+
* @internal
|
|
406
|
+
*/
|
|
407
|
+
matchingPatternHandlers(e, t) {
|
|
408
|
+
const n = this.patternHandlers.get(e), s = this.patternIndex.get(e);
|
|
409
|
+
if (n === void 0 || n.size === 0 || s === void 0) return [];
|
|
410
|
+
const i = this.splitPath(t), d = [], a = (f) => {
|
|
411
|
+
for (const c of f) {
|
|
412
|
+
if (!this.matchSegments(c.segments, i)) continue;
|
|
413
|
+
const u = n.get(c.pattern);
|
|
414
|
+
u !== void 0 && d.push(u);
|
|
415
|
+
}
|
|
416
|
+
}, r = i[0];
|
|
417
|
+
if (r !== void 0) {
|
|
418
|
+
const f = s.byHead.get(r);
|
|
419
|
+
f !== void 0 && a(f);
|
|
420
|
+
}
|
|
421
|
+
return a(s.anyHead), d;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Pattern matcher over dot-separated segments, which arrive already split.
|
|
312
425
|
*
|
|
313
426
|
* Rules:
|
|
314
427
|
* - **literal**: exact match.
|
|
315
428
|
* - `*` : matches exactly **one** segment.
|
|
316
429
|
* - `**` : matches **zero or more** remaining segments (including empty).
|
|
317
430
|
*
|
|
318
|
-
* @
|
|
319
|
-
*
|
|
320
|
-
*
|
|
431
|
+
* @remarks
|
|
432
|
+
* Takes segments rather than strings so delivery can split each pattern once at registration
|
|
433
|
+
* and the subject once per emit, instead of both once per test. Re-splitting per test was most
|
|
434
|
+
* of what made wildcard delivery expensive: a thousand patterns meant two thousand string
|
|
435
|
+
* splits to deliver one event.
|
|
436
|
+
*
|
|
437
|
+
* @param pSegs - Pattern segments (may include `*`/`**`).
|
|
438
|
+
* @param sSegs - Subject segments to test.
|
|
439
|
+
* @returns `true` if the pattern matches; otherwise `false`.
|
|
321
440
|
*
|
|
322
441
|
* @example
|
|
323
442
|
* ```ts
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* matchPattern('**.end', 'x.y.end') // true
|
|
443
|
+
* matchSegments(['a', '*'], ['a', 'b']) // true
|
|
444
|
+
* matchSegments(['a', '*'], ['a', 'b', 'c']) // false
|
|
445
|
+
* matchSegments(['a', '**'], ['a']) // true
|
|
446
|
+
* matchSegments(['**', 'end'], ['x', 'y', 'end']) // true
|
|
329
447
|
* ```
|
|
330
448
|
*
|
|
331
449
|
* @internal
|
|
332
450
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
i = s, o = c, s++;
|
|
451
|
+
matchSegments(e, t) {
|
|
452
|
+
let n = 0, s = 0, i = -1, d = 0;
|
|
453
|
+
for (; s < t.length; )
|
|
454
|
+
if (n < e.length && (e[n] === "*" || e[n] === t[s]))
|
|
455
|
+
n++, s++;
|
|
456
|
+
else if (n < e.length && e[n] === "**")
|
|
457
|
+
i = n, d = s, n++;
|
|
341
458
|
else if (i !== -1)
|
|
342
|
-
|
|
459
|
+
n = i + 1, s = ++d;
|
|
343
460
|
else
|
|
344
461
|
return !1;
|
|
345
|
-
for (;
|
|
346
|
-
return
|
|
462
|
+
for (; n < e.length && e[n] === "**"; ) n++;
|
|
463
|
+
return n === e.length;
|
|
347
464
|
}
|
|
348
465
|
/**
|
|
349
466
|
* Removes **all** listeners (exact and pattern). Useful for tests/HMR teardown.
|
|
@@ -356,7 +473,7 @@ class k {
|
|
|
356
473
|
* @public
|
|
357
474
|
*/
|
|
358
475
|
clear() {
|
|
359
|
-
this.handlers.clear(), this.patternHandlers.clear();
|
|
476
|
+
this.handlers.clear(), this.patternHandlers.clear(), this.patternIndex.clear();
|
|
360
477
|
}
|
|
361
478
|
/**
|
|
362
479
|
* Returns a snapshot of all registered subscriptions for DevTools introspection.
|
|
@@ -368,16 +485,16 @@ class k {
|
|
|
368
485
|
*/
|
|
369
486
|
__introspect() {
|
|
370
487
|
const e = [];
|
|
371
|
-
for (const [t,
|
|
372
|
-
for (const [
|
|
373
|
-
|
|
374
|
-
for (const [t,
|
|
375
|
-
for (const [
|
|
376
|
-
|
|
488
|
+
for (const [t, n] of this.handlers)
|
|
489
|
+
for (const [s, i] of n)
|
|
490
|
+
i.length > 0 && e.push({ channel: t, type: s, count: i.length });
|
|
491
|
+
for (const [t, n] of this.patternHandlers)
|
|
492
|
+
for (const [s, i] of n)
|
|
493
|
+
i.length > 0 && e.push({ channel: t, type: s, count: i.length });
|
|
377
494
|
return e;
|
|
378
495
|
}
|
|
379
496
|
}
|
|
380
|
-
class
|
|
497
|
+
class K {
|
|
381
498
|
/**
|
|
382
499
|
* Creates a new {@link Reducer} from a pure reducer function.
|
|
383
500
|
*
|
|
@@ -398,7 +515,7 @@ class _ {
|
|
|
398
515
|
* The underlying pure reducer function.
|
|
399
516
|
* @internal
|
|
400
517
|
*/
|
|
401
|
-
|
|
518
|
+
p(this, "_reduce");
|
|
402
519
|
this._reduce = e;
|
|
403
520
|
}
|
|
404
521
|
/**
|
|
@@ -419,68 +536,128 @@ class _ {
|
|
|
419
536
|
return this._reduce(e, t);
|
|
420
537
|
}
|
|
421
538
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
539
|
+
const P = /* @__PURE__ */ new Set();
|
|
540
|
+
function M(o, e) {
|
|
541
|
+
const t = o ? `${o}.${e}` : e;
|
|
542
|
+
P.has(t) || (P.add(t), console.warn(
|
|
543
|
+
`[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
|
+
));
|
|
545
|
+
}
|
|
546
|
+
function O(o, e, t = "", n = /* @__PURE__ */ new Map()) {
|
|
547
|
+
const s = [];
|
|
548
|
+
return v(o, e, t, n, s), s;
|
|
549
|
+
}
|
|
550
|
+
function v(o, e, t, n, s) {
|
|
551
|
+
if (o === e) return;
|
|
552
|
+
if (typeof o != "object" || typeof e != "object" || o === null || e === null) {
|
|
553
|
+
if (typeof o == "number" && Number.isNaN(o) && Number.isNaN(e))
|
|
554
|
+
return;
|
|
555
|
+
s.push(t);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (o instanceof Date && e instanceof Date) {
|
|
559
|
+
o.getTime() !== e.getTime() && s.push(t);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (o instanceof RegExp && e instanceof RegExp) {
|
|
563
|
+
(o.source !== e.source || e.flags !== o.flags) && s.push(t);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (o instanceof Map || e instanceof Map) {
|
|
567
|
+
s.push(t);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (o instanceof Set || e instanceof Set) {
|
|
571
|
+
s.push(t);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const i = o, d = e, a = n.get(i);
|
|
575
|
+
if (a?.has(d)) return;
|
|
576
|
+
const r = a ?? /* @__PURE__ */ new Set();
|
|
577
|
+
r.add(d), a || n.set(i, r);
|
|
434
578
|
try {
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
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);
|
|
579
|
+
const f = Array.isArray(o), c = Array.isArray(e);
|
|
580
|
+
if (f !== c) {
|
|
581
|
+
s.push(t);
|
|
582
|
+
return;
|
|
446
583
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
584
|
+
if (f) {
|
|
585
|
+
const h = o, m = e;
|
|
586
|
+
h.length !== m.length && t && s.push(t);
|
|
587
|
+
const g = Math.min(h.length, m.length);
|
|
588
|
+
for (let w = 0; w < g; w++)
|
|
589
|
+
h[w] !== m[w] && v(h[w], m[w], t ? `${t}.${w}` : `${w}`, n, s);
|
|
590
|
+
for (let w = g; w < Math.max(h.length, m.length); w++)
|
|
591
|
+
s.push(t ? `${t}.${w}` : `${w}`);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const u = Object.keys(o), l = Object.keys(e);
|
|
595
|
+
if (u.length === 0 && l.length === 0) {
|
|
596
|
+
s.push(t);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
let y = u.length === l.length;
|
|
600
|
+
if (y) {
|
|
601
|
+
for (let h = 0; h < l.length; h++)
|
|
602
|
+
if (!Object.prototype.hasOwnProperty.call(o, l[h])) {
|
|
603
|
+
y = !1;
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (y) {
|
|
608
|
+
for (const h of l)
|
|
609
|
+
o[h] !== e[h] && (process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h), v(o[h], e[h], t ? `${t}.${h}` : h, n, s));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
for (const h of l) {
|
|
613
|
+
const m = Object.prototype.hasOwnProperty.call(o, h);
|
|
614
|
+
if (m && o[h] === e[h]) continue;
|
|
615
|
+
process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h);
|
|
616
|
+
const g = t ? `${t}.${h}` : h;
|
|
617
|
+
if (!m) {
|
|
618
|
+
s.push(g);
|
|
452
619
|
continue;
|
|
453
620
|
}
|
|
454
|
-
|
|
621
|
+
v(o[h], e[h], g, n, s);
|
|
455
622
|
}
|
|
456
|
-
|
|
623
|
+
for (const h of u)
|
|
624
|
+
Object.prototype.hasOwnProperty.call(e, h) || (process.env.NODE_ENV !== "production" && h.includes(".") && M(t, h), s.push(t ? `${t}.${h}` : h));
|
|
457
625
|
} finally {
|
|
458
|
-
|
|
626
|
+
r.delete(d), r.size === 0 && n.delete(i);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function S(o, e = /* @__PURE__ */ new WeakSet(), t) {
|
|
630
|
+
if (o === null || typeof o != "object" || e.has(o) || (t !== void 0 && o === t.watch && t.onFound(), Object.isFrozen(o))) return o;
|
|
631
|
+
if (e.add(o), Array.isArray(o)) {
|
|
632
|
+
const n = o;
|
|
633
|
+
for (let s = 0; s < n.length; s++)
|
|
634
|
+
n[s] = S(n[s], e, t);
|
|
635
|
+
return Object.freeze(n);
|
|
459
636
|
}
|
|
637
|
+
for (const n of Object.getOwnPropertyNames(o)) {
|
|
638
|
+
const s = Object.getOwnPropertyDescriptor(o, n);
|
|
639
|
+
!s || !("value" in s) || (o[n] = S(o[n], e, t));
|
|
640
|
+
}
|
|
641
|
+
for (const n of Object.getOwnPropertySymbols(o)) {
|
|
642
|
+
const s = Object.getOwnPropertyDescriptor(o, n);
|
|
643
|
+
!s || !("value" in s) || (o[n] = S(o[n], e, t));
|
|
644
|
+
}
|
|
645
|
+
return Object.freeze(o);
|
|
460
646
|
}
|
|
461
|
-
function
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
for (const t of Object.getOwnPropertyNames(a)) {
|
|
470
|
-
const r = Object.getOwnPropertyDescriptor(a, t);
|
|
471
|
-
!r || !("value" in r) || (a[t] = E(a[t], e));
|
|
472
|
-
}
|
|
473
|
-
for (const t of Object.getOwnPropertySymbols(a)) {
|
|
474
|
-
const r = Object.getOwnPropertyDescriptor(a, t);
|
|
475
|
-
!r || !("value" in r) || (a[t] = E(a[t], e));
|
|
476
|
-
}
|
|
477
|
-
return Object.freeze(a);
|
|
647
|
+
function W(o, e) {
|
|
648
|
+
try {
|
|
649
|
+
return structuredClone(e);
|
|
650
|
+
} catch (t) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
`[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.`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
478
655
|
}
|
|
479
|
-
function
|
|
480
|
-
return process.env.NODE_ENV === "production" ?
|
|
656
|
+
function k(o, e) {
|
|
657
|
+
return process.env.NODE_ENV === "production" ? o : S(o, /* @__PURE__ */ new WeakSet(), e);
|
|
481
658
|
}
|
|
482
|
-
const
|
|
483
|
-
class
|
|
659
|
+
const x = 100, z = () => typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
|
|
660
|
+
class $ {
|
|
484
661
|
/**
|
|
485
662
|
* Creates a store from a {@link StoreSpec}.
|
|
486
663
|
*
|
|
@@ -494,7 +671,7 @@ class v {
|
|
|
494
671
|
*
|
|
495
672
|
* @public
|
|
496
673
|
*/
|
|
497
|
-
|
|
674
|
+
p(this, "name");
|
|
498
675
|
/**
|
|
499
676
|
* Registered middleware pipeline (run **before** reducers).
|
|
500
677
|
* Stores either raw functions (legacy) or MiddlewareSpec objects.
|
|
@@ -502,45 +679,45 @@ class v {
|
|
|
502
679
|
*
|
|
503
680
|
* @internal
|
|
504
681
|
*/
|
|
505
|
-
|
|
682
|
+
p(this, "middleware");
|
|
506
683
|
/**
|
|
507
684
|
* Installed slice reducers keyed by slice name.
|
|
508
685
|
*
|
|
509
686
|
* @internal
|
|
510
687
|
*/
|
|
511
|
-
|
|
688
|
+
p(this, "reducers");
|
|
512
689
|
/**
|
|
513
690
|
* Current immutable snapshot of the store state.
|
|
514
691
|
* This reference changes whenever any slice changes (shallow immutability).
|
|
515
692
|
*
|
|
516
693
|
* @internal
|
|
517
694
|
*/
|
|
518
|
-
|
|
695
|
+
p(this, "state");
|
|
519
696
|
/**
|
|
520
697
|
* Bus for reducer wiring (emit by `(channel, type)`).
|
|
521
698
|
*
|
|
522
699
|
* @internal
|
|
523
700
|
*/
|
|
524
|
-
|
|
701
|
+
p(this, "reducerBus");
|
|
525
702
|
/**
|
|
526
703
|
* Bus for **granular** connector events (emit by **dotted path** inside a slice).
|
|
527
704
|
*
|
|
528
705
|
* @internal
|
|
529
706
|
*/
|
|
530
|
-
|
|
707
|
+
p(this, "connectorBus");
|
|
531
708
|
/**
|
|
532
709
|
* Coarse-grained listeners (called once per committed event, only if state changed).
|
|
533
710
|
*
|
|
534
711
|
* @internal
|
|
535
712
|
*/
|
|
536
|
-
|
|
713
|
+
p(this, "listeners", /* @__PURE__ */ new Set());
|
|
537
714
|
/**
|
|
538
715
|
* Registered effect handlers keyed by `"channel::type"` for O(1) lookup.
|
|
539
|
-
* Used for effects with explicit `keys`
|
|
716
|
+
* Used for effects with explicit `keys` targeting.
|
|
540
717
|
*
|
|
541
718
|
* @internal
|
|
542
719
|
*/
|
|
543
|
-
|
|
720
|
+
p(this, "effects", /* @__PURE__ */ new Map());
|
|
544
721
|
/**
|
|
545
722
|
* Pattern-based effects that need runtime matching.
|
|
546
723
|
* Used for effects with `when: { any }`, `{ channel }`, or `{ channels }`.
|
|
@@ -548,34 +725,34 @@ class v {
|
|
|
548
725
|
*
|
|
549
726
|
* @internal
|
|
550
727
|
*/
|
|
551
|
-
|
|
728
|
+
p(this, "patternEffects", /* @__PURE__ */ new Set());
|
|
552
729
|
/**
|
|
553
730
|
* Committed event subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
554
731
|
* Notified after reducers, before effects, for events that passed middleware.
|
|
555
732
|
*
|
|
556
733
|
* @internal
|
|
557
734
|
*/
|
|
558
|
-
|
|
735
|
+
p(this, "committedEventSubscribers", /* @__PURE__ */ new Map());
|
|
559
736
|
/**
|
|
560
737
|
* Uncommitted event subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
561
738
|
* Notified when middleware rejects an event.
|
|
562
739
|
*
|
|
563
740
|
* @internal
|
|
564
741
|
*/
|
|
565
|
-
|
|
742
|
+
p(this, "uncommittedEventSubscribers", /* @__PURE__ */ new Map());
|
|
566
743
|
/**
|
|
567
744
|
* All-events subscribers keyed by `"channel::type"` for O(1) lookup.
|
|
568
745
|
* Notified for both committed and uncommitted events with phase parameter.
|
|
569
746
|
*
|
|
570
747
|
* @internal
|
|
571
748
|
*/
|
|
572
|
-
|
|
749
|
+
p(this, "allEventSubscribers", /* @__PURE__ */ new Map());
|
|
573
750
|
/**
|
|
574
751
|
* Track reducerBus unsubs per slice for HMR/register/unregister.
|
|
575
752
|
*
|
|
576
753
|
* @internal
|
|
577
754
|
*/
|
|
578
|
-
|
|
755
|
+
p(this, "sliceUnsubs", /* @__PURE__ */ new Map());
|
|
579
756
|
/**
|
|
580
757
|
* Pattern-based reducers that need runtime matching.
|
|
581
758
|
* Used for reducers with `when: { any }`, `{ channel }`, or `{ channels }`.
|
|
@@ -583,39 +760,60 @@ class v {
|
|
|
583
760
|
*
|
|
584
761
|
* @internal
|
|
585
762
|
*/
|
|
586
|
-
|
|
763
|
+
p(this, "patternReducers", /* @__PURE__ */ new Map());
|
|
587
764
|
/**
|
|
588
765
|
* Whether `__replayEvents()` is allowed.
|
|
589
766
|
* Set from `spec.devtools.allowReplay`.
|
|
590
767
|
*
|
|
591
768
|
* @internal
|
|
592
769
|
*/
|
|
593
|
-
|
|
770
|
+
p(this, "replayEnabled");
|
|
771
|
+
/**
|
|
772
|
+
* Produces the `id` for each emitted event. Defaults to `crypto.randomUUID()`; overridable
|
|
773
|
+
* via {@link StoreSpec.idFactory} for runtimes lacking it or for deterministic tests.
|
|
774
|
+
*
|
|
775
|
+
* @internal
|
|
776
|
+
*/
|
|
777
|
+
p(this, "idFactory");
|
|
594
778
|
/**
|
|
595
779
|
* Optional hook invoked when an effect throws/rejects. See
|
|
596
780
|
* {@link StoreSpec.onEffectError}. `await emit()` never rejects on effect
|
|
597
781
|
* failure — this is how callers observe effect errors.
|
|
598
782
|
*/
|
|
599
|
-
|
|
783
|
+
p(this, "onEffectError");
|
|
784
|
+
/**
|
|
785
|
+
* Optional hook invoked when a reducer throws. See {@link StoreSpec.onReducerError}. The
|
|
786
|
+
* failing slice is isolated rather than the event being rolled back, so this is the only
|
|
787
|
+
* signal that a reducer misbehaved.
|
|
788
|
+
*/
|
|
789
|
+
p(this, "onReducerError");
|
|
790
|
+
/**
|
|
791
|
+
* `slice:channel:type` combinations already warned about for payload aliasing.
|
|
792
|
+
*
|
|
793
|
+
* @remarks
|
|
794
|
+
* Development-only diagnostics have to stay quiet enough to be read. One warning names the
|
|
795
|
+
* pattern; repeating it once per event would bury it.
|
|
796
|
+
*/
|
|
797
|
+
p(this, "warnedPayloadAliases", /* @__PURE__ */ new Set());
|
|
600
798
|
/**
|
|
601
799
|
* Pending events awaiting the **synchronous** reduce phase (middleware +
|
|
602
800
|
* reducers + subscribers + coarse listeners). Drained by {@link drainReduce}.
|
|
603
801
|
*
|
|
604
802
|
* @internal
|
|
605
803
|
*/
|
|
606
|
-
|
|
804
|
+
p(this, "reduceQueue", []);
|
|
607
805
|
/**
|
|
608
806
|
* Re-entrancy guard for the synchronous reduce phase.
|
|
609
807
|
*
|
|
610
808
|
* @internal
|
|
611
809
|
*/
|
|
612
|
-
|
|
810
|
+
p(this, "isReducing", !1);
|
|
613
811
|
/**
|
|
614
812
|
* Registered instrumentation observers (DevTools seam). See {@link instrument}.
|
|
615
813
|
*
|
|
616
814
|
* @internal
|
|
617
815
|
*/
|
|
618
|
-
|
|
816
|
+
p(this, "instrumentObservers", /* @__PURE__ */ new Set());
|
|
619
817
|
/**
|
|
620
818
|
* Scratch array collecting slice-prefixed changed leaf paths during an
|
|
621
819
|
* instrumented reduce. Set by {@link drainReduce} while observers are active;
|
|
@@ -623,14 +821,14 @@ class v {
|
|
|
623
821
|
*
|
|
624
822
|
* @internal
|
|
625
823
|
*/
|
|
626
|
-
|
|
824
|
+
p(this, "changedPathSink", null);
|
|
627
825
|
/**
|
|
628
826
|
* Count of effect tasks currently in flight; surfaced as queue depth by
|
|
629
827
|
* {@link __devtoolsIntrospect}.
|
|
630
828
|
*
|
|
631
829
|
* @internal
|
|
632
830
|
*/
|
|
633
|
-
|
|
831
|
+
p(this, "inFlightEffects", 0);
|
|
634
832
|
/**
|
|
635
833
|
* Tracks processed events by fingerprint with timestamps for TTL-based deduplication.
|
|
636
834
|
*
|
|
@@ -647,7 +845,7 @@ class v {
|
|
|
647
845
|
*
|
|
648
846
|
* @internal
|
|
649
847
|
*/
|
|
650
|
-
|
|
848
|
+
p(this, "processedEvents", /* @__PURE__ */ new Map());
|
|
651
849
|
/**
|
|
652
850
|
* Lifetime count of events suppressed by the deduplication cache.
|
|
653
851
|
* Exposed via {@link __devtoolsIntrospect} so the DevTools agent can
|
|
@@ -655,7 +853,7 @@ class v {
|
|
|
655
853
|
*
|
|
656
854
|
* @internal
|
|
657
855
|
*/
|
|
658
|
-
|
|
856
|
+
p(this, "dedupCount", 0);
|
|
659
857
|
/**
|
|
660
858
|
* Store-owned metadata for registered effects, keyed by the effect function.
|
|
661
859
|
* Kept **off** the caller's function object: mutating a user-owned function
|
|
@@ -664,23 +862,23 @@ class v {
|
|
|
664
862
|
*
|
|
665
863
|
* @internal
|
|
666
864
|
*/
|
|
667
|
-
|
|
865
|
+
p(this, "effectMeta", /* @__PURE__ */ new WeakMap());
|
|
668
866
|
/**
|
|
669
867
|
* Configuration for event deduplication.
|
|
670
868
|
* @internal
|
|
671
869
|
*/
|
|
672
|
-
|
|
870
|
+
p(this, "dedupConfig");
|
|
673
871
|
/**
|
|
674
872
|
* Timer for periodic cleanup of processed events.
|
|
675
873
|
*
|
|
676
874
|
* @internal
|
|
677
875
|
*/
|
|
678
|
-
|
|
679
|
-
if (this.name = e.name ?? "yoltra Store", this.reducerBus = new
|
|
876
|
+
p(this, "eventCleanupTimer", null);
|
|
877
|
+
if (this.name = e.name ?? "yoltra Store", this.reducerBus = new B(), this.connectorBus = new H(), this.middleware = [...e.middleware ?? []], this.reducers = {}, this.state = {}, this.replayEnabled = e.devtools?.allowReplay ?? !1, this.idFactory = e.idFactory ?? (() => crypto.randomUUID()), this.onEffectError = e.onEffectError, this.onReducerError = e.onReducerError, this.dedupConfig = {
|
|
680
878
|
windowMs: e.dedupWindowMs ?? 0,
|
|
681
879
|
maxCacheSize: 1e3
|
|
682
|
-
}, Object.entries(e.reducer).forEach(([t,
|
|
683
|
-
this.mountSlice(t,
|
|
880
|
+
}, Object.entries(e.reducer).forEach(([t, n]) => {
|
|
881
|
+
this.mountSlice(t, n, { preserveState: !1 });
|
|
684
882
|
}), e.effects?.length)
|
|
685
883
|
for (const t of e.effects)
|
|
686
884
|
this.registerEffect(t);
|
|
@@ -713,17 +911,17 @@ class v {
|
|
|
713
911
|
*
|
|
714
912
|
* @internal
|
|
715
913
|
*/
|
|
716
|
-
fingerprint(e, t,
|
|
717
|
-
const
|
|
914
|
+
fingerprint(e, t, n) {
|
|
915
|
+
const s = `${e}::${t}`;
|
|
718
916
|
try {
|
|
719
|
-
if (
|
|
720
|
-
return `${
|
|
721
|
-
if (typeof
|
|
722
|
-
return `${
|
|
723
|
-
const
|
|
724
|
-
return `${
|
|
917
|
+
if (n == null)
|
|
918
|
+
return `${s}::null`;
|
|
919
|
+
if (typeof n != "object")
|
|
920
|
+
return `${s}::${String(n)}`;
|
|
921
|
+
const i = JSON.stringify(n);
|
|
922
|
+
return `${s}::${i}`;
|
|
725
923
|
} catch {
|
|
726
|
-
return `${
|
|
924
|
+
return `${s}::${Date.now()}::${Math.random()}`;
|
|
727
925
|
}
|
|
728
926
|
}
|
|
729
927
|
/**
|
|
@@ -736,8 +934,8 @@ class v {
|
|
|
736
934
|
* @internal
|
|
737
935
|
*/
|
|
738
936
|
shouldDedupe(e, t) {
|
|
739
|
-
const
|
|
740
|
-
return
|
|
937
|
+
const n = Date.now(), s = this.processedEvents.get(e);
|
|
938
|
+
return s !== void 0 && n - s < t ? (this.dedupCount++, !0) : (this.processedEvents.set(e, n), this.ensureCleanupTimer(), this.processedEvents.size > this.dedupConfig.maxCacheSize && this.pruneProcessedEvents(n), !1);
|
|
741
939
|
}
|
|
742
940
|
/**
|
|
743
941
|
* Starts the periodic prune interval if it isn't already running. Called when
|
|
@@ -759,9 +957,9 @@ class v {
|
|
|
759
957
|
* @internal
|
|
760
958
|
*/
|
|
761
959
|
pruneProcessedEvents(e) {
|
|
762
|
-
const t = Math.max(this.dedupConfig.windowMs,
|
|
763
|
-
for (const [
|
|
764
|
-
|
|
960
|
+
const t = Math.max(this.dedupConfig.windowMs, x), n = e - t * 2;
|
|
961
|
+
for (const [s, i] of this.processedEvents)
|
|
962
|
+
i < n && this.processedEvents.delete(s);
|
|
765
963
|
this.processedEvents.size === 0 && this.eventCleanupTimer !== null && (clearInterval(this.eventCleanupTimer), this.eventCleanupTimer = null);
|
|
766
964
|
}
|
|
767
965
|
/**
|
|
@@ -782,7 +980,7 @@ class v {
|
|
|
782
980
|
*/
|
|
783
981
|
matchesWhen(e, t) {
|
|
784
982
|
return !e || "any" in e && e.any === !0 ? !0 : "keys" in e ? e.keys.some(
|
|
785
|
-
([
|
|
983
|
+
([n, s]) => t.channel === n && t.type === s
|
|
786
984
|
) : "channel" in e ? t.channel === e.channel : "channels" in e ? e.channels.includes(t.channel) : !1;
|
|
787
985
|
}
|
|
788
986
|
/**
|
|
@@ -818,20 +1016,20 @@ class v {
|
|
|
818
1016
|
* @internal
|
|
819
1017
|
*/
|
|
820
1018
|
async notifyEffects(e) {
|
|
821
|
-
const t = `${String(e.channel)}::${String(e.type)}`,
|
|
822
|
-
if (
|
|
823
|
-
for (const
|
|
1019
|
+
const t = `${String(e.channel)}::${String(e.type)}`, n = this.effects.get(t);
|
|
1020
|
+
if (n && n.size > 0)
|
|
1021
|
+
for (const s of [...n])
|
|
824
1022
|
try {
|
|
825
|
-
await
|
|
826
|
-
} catch (
|
|
827
|
-
console.error("Effect error:",
|
|
1023
|
+
await s(e, this.getState, this.emit);
|
|
1024
|
+
} catch (i) {
|
|
1025
|
+
console.error("Effect error:", i), this.onEffectError?.(i, e);
|
|
828
1026
|
}
|
|
829
|
-
for (const { effect:
|
|
830
|
-
if (this.matchesWhen(
|
|
1027
|
+
for (const { effect: s, when: i } of this.patternEffects)
|
|
1028
|
+
if (this.matchesWhen(i, e))
|
|
831
1029
|
try {
|
|
832
|
-
await
|
|
833
|
-
} catch (
|
|
834
|
-
console.error("Effect error:",
|
|
1030
|
+
await s(e, this.getState, this.emit);
|
|
1031
|
+
} catch (d) {
|
|
1032
|
+
console.error("Effect error:", d), this.onEffectError?.(d, e);
|
|
835
1033
|
}
|
|
836
1034
|
}
|
|
837
1035
|
/**
|
|
@@ -845,12 +1043,12 @@ class v {
|
|
|
845
1043
|
* @internal
|
|
846
1044
|
*/
|
|
847
1045
|
notifyEventSubscribers(e, t) {
|
|
848
|
-
const
|
|
849
|
-
if (
|
|
850
|
-
for (const
|
|
851
|
-
const
|
|
852
|
-
if (
|
|
853
|
-
for (const
|
|
1046
|
+
const n = `${String(e.channel)}::${String(e.type)}`, i = (t === "committed" ? this.committedEventSubscribers : this.uncommittedEventSubscribers).get(n);
|
|
1047
|
+
if (i?.size)
|
|
1048
|
+
for (const a of [...i]) this.invokeEventSubscriber(a, e, t);
|
|
1049
|
+
const d = this.allEventSubscribers.get(n);
|
|
1050
|
+
if (d?.size)
|
|
1051
|
+
for (const a of [...d]) this.invokeEventSubscriber(a, e, t);
|
|
854
1052
|
}
|
|
855
1053
|
/**
|
|
856
1054
|
* Invokes a single event-subscription handler **fire-and-forget**: synchronous
|
|
@@ -859,12 +1057,12 @@ class v {
|
|
|
859
1057
|
*
|
|
860
1058
|
* @internal
|
|
861
1059
|
*/
|
|
862
|
-
invokeEventSubscriber(e, t,
|
|
1060
|
+
invokeEventSubscriber(e, t, n) {
|
|
863
1061
|
try {
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
} catch (
|
|
867
|
-
console.error("Event subscription error:",
|
|
1062
|
+
const s = e(t, this.getState, this.emit, n);
|
|
1063
|
+
s && typeof s.then == "function" && s.catch((i) => console.error("Event subscription error:", i));
|
|
1064
|
+
} catch (s) {
|
|
1065
|
+
console.error("Event subscription error:", s);
|
|
868
1066
|
}
|
|
869
1067
|
}
|
|
870
1068
|
/**
|
|
@@ -883,22 +1081,62 @@ class v {
|
|
|
883
1081
|
*
|
|
884
1082
|
* @internal
|
|
885
1083
|
*/
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1084
|
+
/**
|
|
1085
|
+
* Reduces one slice and contains any error it raises.
|
|
1086
|
+
*
|
|
1087
|
+
* @returns `true` when the slice changed.
|
|
1088
|
+
*
|
|
1089
|
+
* @remarks
|
|
1090
|
+
* The single funnel both dispatch paths go through, which is the point. Keyed reducers run
|
|
1091
|
+
* through `reducerBus`, whose handler loop caught and logged; pattern reducers were called
|
|
1092
|
+
* straight from the drain, so their errors escaped to the caller instead. The same bug in the
|
|
1093
|
+
* same reducer therefore produced two different outcomes depending on how the slice happened
|
|
1094
|
+
* to be targeted — a keyed reducer's throw let the event commit and its effects run, while a
|
|
1095
|
+
* pattern reducer's throw aborted the commit and notified nobody, not even the uncommitted
|
|
1096
|
+
* subscribers a veto would have reached.
|
|
1097
|
+
*
|
|
1098
|
+
* The semantics are now the same either way: **the failing slice is isolated.** Its state is
|
|
1099
|
+
* unchanged, every other slice still reduces, and the event still commits if anything else
|
|
1100
|
+
* changed. Rolling the whole event back would be tidier in principle, but fine-grained
|
|
1101
|
+
* subscribers are notified inside `forwardEvent` as each slice commits, so an event that
|
|
1102
|
+
* reverted afterwards would have already told components about a value that no longer exists.
|
|
1103
|
+
* Isolation keeps every notification truthful.
|
|
1104
|
+
*
|
|
1105
|
+
* @internal
|
|
1106
|
+
*/
|
|
1107
|
+
forwardEventGuarded(e, t) {
|
|
1108
|
+
try {
|
|
1109
|
+
return this.forwardEvent(e, t);
|
|
1110
|
+
} catch (n) {
|
|
1111
|
+
return console.error(`Reducer error in slice "${e}":`, n), this.onReducerError?.(n, t, e), !1;
|
|
901
1112
|
}
|
|
1113
|
+
}
|
|
1114
|
+
forwardEvent(e, t) {
|
|
1115
|
+
const n = this.state[e], s = this.reducers[e].reduce(n, t);
|
|
1116
|
+
if (n === s) return !1;
|
|
1117
|
+
const i = O(n, s).filter(Boolean);
|
|
1118
|
+
if (i.length === 0) return !1;
|
|
1119
|
+
const d = t.payload, a = process.env.NODE_ENV !== "production" && d !== null && typeof d == "object" ? {
|
|
1120
|
+
watch: d,
|
|
1121
|
+
onFound: () => {
|
|
1122
|
+
const c = `${e}:${t.channel}:${t.type}`;
|
|
1123
|
+
this.warnedPayloadAliases.has(c) || (this.warnedPayloadAliases.add(c), console.warn(
|
|
1124
|
+
`[yoltra] Slice "${e}" stored the payload of "${t.channel}/${t.type}" by reference. It is now frozen along with the rest of the state, so the emitter mutating it later will throw in development and silently corrupt state in production. Copy the payload in the reducer instead.`
|
|
1125
|
+
));
|
|
1126
|
+
}
|
|
1127
|
+
} : void 0, r = k(s, a);
|
|
1128
|
+
if (this.state = { ...this.state, [e]: r }, this.changedPathSink)
|
|
1129
|
+
for (const c of i)
|
|
1130
|
+
this.changedPathSink.push(c ? `${e}.${c}` : e);
|
|
1131
|
+
const f = /* @__PURE__ */ new Set();
|
|
1132
|
+
for (const c of i)
|
|
1133
|
+
for (const u of $.buildAncestorPaths(c)) f.add(u);
|
|
1134
|
+
for (const c of f)
|
|
1135
|
+
this.connectorBus.emitWith(e, c, () => ({
|
|
1136
|
+
oldValue: this.getAtPath(n, c),
|
|
1137
|
+
newValue: this.getAtPath(r, c),
|
|
1138
|
+
path: c
|
|
1139
|
+
}));
|
|
902
1140
|
return !0;
|
|
903
1141
|
}
|
|
904
1142
|
/**
|
|
@@ -912,65 +1150,65 @@ class v {
|
|
|
912
1150
|
* @public
|
|
913
1151
|
*/
|
|
914
1152
|
__devtoolsIntrospect() {
|
|
915
|
-
const e = Object.keys(this.reducers).map((
|
|
916
|
-
const
|
|
917
|
-
return { name:
|
|
1153
|
+
const e = Object.keys(this.reducers).map((a) => {
|
|
1154
|
+
const r = this.patternReducers.get(a);
|
|
1155
|
+
return { name: a, when: r };
|
|
918
1156
|
}), t = [];
|
|
919
|
-
for (const [
|
|
920
|
-
if (
|
|
921
|
-
const [f,
|
|
922
|
-
for (const
|
|
923
|
-
const
|
|
924
|
-
t.push({ channel: f, type:
|
|
1157
|
+
for (const [a, r] of this.effects) {
|
|
1158
|
+
if (r.size === 0) continue;
|
|
1159
|
+
const [f, c] = a.split("::");
|
|
1160
|
+
for (const u of r) {
|
|
1161
|
+
const l = this.effectMeta.get(u);
|
|
1162
|
+
t.push({ channel: f, type: c, name: l?.name, description: l?.description });
|
|
925
1163
|
}
|
|
926
1164
|
}
|
|
927
|
-
for (const
|
|
928
|
-
const
|
|
1165
|
+
for (const a of this.patternEffects) {
|
|
1166
|
+
const r = this.effectMeta.get(a.effect);
|
|
929
1167
|
t.push({
|
|
930
1168
|
channel: "*",
|
|
931
1169
|
type: "*",
|
|
932
|
-
name:
|
|
933
|
-
description:
|
|
1170
|
+
name: r?.name,
|
|
1171
|
+
description: r?.description
|
|
934
1172
|
});
|
|
935
1173
|
}
|
|
936
|
-
const r = [];
|
|
937
|
-
for (const i of this.middleware)
|
|
938
|
-
typeof i == "function" ? r.push({ name: i.name || void 0 }) : r.push({
|
|
939
|
-
name: i.meta?.name,
|
|
940
|
-
description: i.meta?.description,
|
|
941
|
-
when: i.when
|
|
942
|
-
});
|
|
943
1174
|
const n = [];
|
|
944
|
-
for (const
|
|
945
|
-
|
|
946
|
-
|
|
1175
|
+
for (const a of this.middleware)
|
|
1176
|
+
typeof a == "function" ? n.push({ name: a.name || void 0 }) : n.push({
|
|
1177
|
+
name: a.meta?.name,
|
|
1178
|
+
description: a.meta?.description,
|
|
1179
|
+
when: a.when
|
|
1180
|
+
});
|
|
947
1181
|
const s = [];
|
|
948
|
-
for (const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1182
|
+
for (const a of this.connectorBus.__introspect())
|
|
1183
|
+
for (let r = 0; r < a.count; r++)
|
|
1184
|
+
s.push({ reducer: a.channel, property: a.type });
|
|
1185
|
+
const i = [];
|
|
1186
|
+
for (const [a, r] of this.committedEventSubscribers) {
|
|
1187
|
+
if (r.size === 0) continue;
|
|
1188
|
+
const [f, c] = a.split("::");
|
|
1189
|
+
for (let u = 0; u < r.size; u++)
|
|
1190
|
+
i.push({ channel: f, type: c, phase: "committed" });
|
|
953
1191
|
}
|
|
954
|
-
for (const [
|
|
955
|
-
if (
|
|
956
|
-
const [f,
|
|
957
|
-
for (let
|
|
958
|
-
|
|
1192
|
+
for (const [a, r] of this.uncommittedEventSubscribers) {
|
|
1193
|
+
if (r.size === 0) continue;
|
|
1194
|
+
const [f, c] = a.split("::");
|
|
1195
|
+
for (let u = 0; u < r.size; u++)
|
|
1196
|
+
i.push({ channel: f, type: c, phase: "uncommitted" });
|
|
959
1197
|
}
|
|
960
|
-
for (const [
|
|
961
|
-
if (
|
|
962
|
-
const [f,
|
|
963
|
-
for (let
|
|
964
|
-
|
|
1198
|
+
for (const [a, r] of this.allEventSubscribers) {
|
|
1199
|
+
if (r.size === 0) continue;
|
|
1200
|
+
const [f, c] = a.split("::");
|
|
1201
|
+
for (let u = 0; u < r.size; u++)
|
|
1202
|
+
i.push({ channel: f, type: c, phase: "all" });
|
|
965
1203
|
}
|
|
966
|
-
const
|
|
1204
|
+
const d = this.listeners.size;
|
|
967
1205
|
return {
|
|
968
1206
|
reducers: e,
|
|
969
1207
|
effects: t,
|
|
970
|
-
middleware:
|
|
971
|
-
atomic:
|
|
972
|
-
event:
|
|
973
|
-
coarse:
|
|
1208
|
+
middleware: n,
|
|
1209
|
+
atomic: s,
|
|
1210
|
+
event: i,
|
|
1211
|
+
coarse: d,
|
|
974
1212
|
dedupHits: this.dedupCount,
|
|
975
1213
|
queueDepth: this.reduceQueue.length + this.inFlightEffects
|
|
976
1214
|
};
|
|
@@ -991,36 +1229,34 @@ class v {
|
|
|
991
1229
|
* @internal
|
|
992
1230
|
*/
|
|
993
1231
|
__applyExternalState(e) {
|
|
994
|
-
if (!this.replayEnabled)
|
|
995
|
-
|
|
1232
|
+
if (!this.replayEnabled)
|
|
1233
|
+
throw new Error(
|
|
996
1234
|
"[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
|
|
997
1235
|
);
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const i = t?.[c], o = r?.[c];
|
|
1004
|
-
if (o === void 0) {
|
|
1236
|
+
const t = this.state, n = e, s = { ...this.state };
|
|
1237
|
+
let i = !1;
|
|
1238
|
+
Object.keys(this.reducers).forEach((d) => {
|
|
1239
|
+
const a = t?.[d], r = n?.[d];
|
|
1240
|
+
if (r === void 0) {
|
|
1005
1241
|
process.env.NODE_ENV !== "production" && console.warn(
|
|
1006
1242
|
`[yoltra] External state is missing slice "${String(
|
|
1007
|
-
|
|
1243
|
+
d
|
|
1008
1244
|
)}"; retaining its current value. Time-travel snapshots should contain all slices.`
|
|
1009
1245
|
);
|
|
1010
1246
|
return;
|
|
1011
1247
|
}
|
|
1012
|
-
if (
|
|
1013
|
-
const f =
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
if (
|
|
1017
|
-
const
|
|
1018
|
-
for (const
|
|
1019
|
-
for (const
|
|
1020
|
-
const
|
|
1021
|
-
this.connectorBus.emit(
|
|
1248
|
+
if (a === r) return;
|
|
1249
|
+
const f = k(r);
|
|
1250
|
+
s[d] = f, i = !0;
|
|
1251
|
+
const c = O(a, r).filter(Boolean);
|
|
1252
|
+
if (c.length === 0) return;
|
|
1253
|
+
const u = /* @__PURE__ */ new Set();
|
|
1254
|
+
for (const l of c) for (const y of $.buildAncestorPaths(l)) u.add(y);
|
|
1255
|
+
for (const l of u) {
|
|
1256
|
+
const y = this.getAtPath(a, l), h = this.getAtPath(f, l);
|
|
1257
|
+
this.connectorBus.emit(d, l, { oldValue: y, newValue: h, path: l });
|
|
1022
1258
|
}
|
|
1023
|
-
}),
|
|
1259
|
+
}), i && (this.state = s), i && this.listeners.forEach((d) => d());
|
|
1024
1260
|
}
|
|
1025
1261
|
/**
|
|
1026
1262
|
* Replays a sequence of events from a snapshot through reducers and event
|
|
@@ -1040,13 +1276,13 @@ class v {
|
|
|
1040
1276
|
"[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })"
|
|
1041
1277
|
);
|
|
1042
1278
|
this.__applyExternalState(e);
|
|
1043
|
-
for (const
|
|
1044
|
-
const
|
|
1045
|
-
this.reducerBus.emit(
|
|
1046
|
-
for (const [
|
|
1047
|
-
this.matchesWhen(f,
|
|
1048
|
-
const
|
|
1049
|
-
this.notifyEventSubscribers(
|
|
1279
|
+
for (const n of t) {
|
|
1280
|
+
const s = n, i = this.state;
|
|
1281
|
+
this.reducerBus.emit(s.channel, s.type, s.payload, s);
|
|
1282
|
+
for (const [r, f] of this.patternReducers)
|
|
1283
|
+
this.matchesWhen(f, s) && this.forwardEventGuarded(r, s);
|
|
1284
|
+
const d = this.state, a = i !== d;
|
|
1285
|
+
this.notifyEventSubscribers(s, "committed"), a && this.listeners.forEach((r) => r());
|
|
1050
1286
|
}
|
|
1051
1287
|
}
|
|
1052
1288
|
/**
|
|
@@ -1092,24 +1328,25 @@ class v {
|
|
|
1092
1328
|
*
|
|
1093
1329
|
* @public
|
|
1094
1330
|
*/
|
|
1095
|
-
async emit(e, t,
|
|
1096
|
-
const
|
|
1097
|
-
if (
|
|
1098
|
-
const
|
|
1099
|
-
if (this.shouldDedupe(
|
|
1331
|
+
async emit(e, t, n, s) {
|
|
1332
|
+
const i = s?.dedupKey, d = this.dedupConfig.windowMs;
|
|
1333
|
+
if (s?.skipDedup !== !0 && (d > 0 || i !== void 0)) {
|
|
1334
|
+
const c = i !== void 0 && d <= 0 ? x : d, u = i !== void 0 ? `${e}::${t}::#${i}` : this.fingerprint(e, t, n);
|
|
1335
|
+
if (this.shouldDedupe(u, c))
|
|
1100
1336
|
return;
|
|
1101
1337
|
}
|
|
1102
|
-
const
|
|
1103
|
-
let
|
|
1104
|
-
const f = new Promise((
|
|
1105
|
-
|
|
1338
|
+
const a = s?.id ?? this.idFactory();
|
|
1339
|
+
let r;
|
|
1340
|
+
const f = new Promise((c) => {
|
|
1341
|
+
r = c;
|
|
1106
1342
|
});
|
|
1107
1343
|
return this.reduceQueue.push({
|
|
1108
1344
|
channel: e,
|
|
1109
1345
|
type: t,
|
|
1110
|
-
payload:
|
|
1111
|
-
id:
|
|
1112
|
-
|
|
1346
|
+
payload: n,
|
|
1347
|
+
id: a,
|
|
1348
|
+
meta: s?.meta,
|
|
1349
|
+
resolve: r
|
|
1113
1350
|
}), this.drainReduce(), f;
|
|
1114
1351
|
}
|
|
1115
1352
|
/**
|
|
@@ -1127,24 +1364,30 @@ class v {
|
|
|
1127
1364
|
this.isReducing = !0;
|
|
1128
1365
|
try {
|
|
1129
1366
|
for (; this.reduceQueue.length > 0; ) {
|
|
1130
|
-
const { channel: e, type: t, payload:
|
|
1131
|
-
|
|
1132
|
-
|
|
1367
|
+
const { channel: e, type: t, payload: n, id: s, meta: i, resolve: d } = this.reduceQueue.shift(), a = {
|
|
1368
|
+
channel: e,
|
|
1369
|
+
type: t,
|
|
1370
|
+
payload: n,
|
|
1371
|
+
id: s,
|
|
1372
|
+
...i !== void 0 ? { meta: i } : {}
|
|
1373
|
+
}, r = this.instrumentObservers.size > 0, f = r ? this.state : void 0, c = r ? [] : void 0;
|
|
1374
|
+
c !== void 0 && (this.changedPathSink = c);
|
|
1375
|
+
const u = r ? z() : 0;
|
|
1133
1376
|
let l = !1;
|
|
1134
1377
|
try {
|
|
1135
|
-
l = this.applyEventSync(
|
|
1136
|
-
} catch (
|
|
1137
|
-
console.error("Emit reduce error:",
|
|
1378
|
+
l = this.applyEventSync(a);
|
|
1379
|
+
} catch (y) {
|
|
1380
|
+
console.error("Emit reduce error:", y);
|
|
1138
1381
|
} finally {
|
|
1139
|
-
|
|
1382
|
+
r && (this.changedPathSink = null);
|
|
1140
1383
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1384
|
+
r && this.emitInstrumentation(
|
|
1385
|
+
a,
|
|
1143
1386
|
l,
|
|
1387
|
+
c ?? [],
|
|
1144
1388
|
f,
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
), this.runEventEffects(c, l, s);
|
|
1389
|
+
z() - u
|
|
1390
|
+
), this.runEventEffects(a, l, d);
|
|
1148
1391
|
}
|
|
1149
1392
|
} finally {
|
|
1150
1393
|
this.isReducing = !1;
|
|
@@ -1162,25 +1405,32 @@ class v {
|
|
|
1162
1405
|
* @internal
|
|
1163
1406
|
*/
|
|
1164
1407
|
applyEventSync(e) {
|
|
1165
|
-
for (const
|
|
1166
|
-
const
|
|
1167
|
-
if (!this.matchesWhen(
|
|
1168
|
-
const
|
|
1169
|
-
let
|
|
1408
|
+
for (const s of this.middleware) {
|
|
1409
|
+
const i = this.getMiddlewareWhen(s);
|
|
1410
|
+
if (!this.matchesWhen(i, e)) continue;
|
|
1411
|
+
const d = this.getMiddlewareFunction(s);
|
|
1412
|
+
let a;
|
|
1170
1413
|
try {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1414
|
+
a = d(this.state, e, this.emit), process.env.NODE_ENV !== "production" && typeof a?.then == "function" && console.error(
|
|
1415
|
+
`[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.`
|
|
1416
|
+
);
|
|
1417
|
+
} catch (r) {
|
|
1418
|
+
console.error("Middleware error:", r), a = !1;
|
|
1174
1419
|
}
|
|
1175
|
-
if (!
|
|
1420
|
+
if (!a)
|
|
1176
1421
|
return this.notifyEventSubscribers(e, "uncommitted"), !1;
|
|
1177
1422
|
}
|
|
1178
1423
|
const t = this.state;
|
|
1179
|
-
this.reducerBus.emit(
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1424
|
+
this.reducerBus.emit(
|
|
1425
|
+
e.channel,
|
|
1426
|
+
e.type,
|
|
1427
|
+
e.payload,
|
|
1428
|
+
e
|
|
1429
|
+
);
|
|
1430
|
+
for (const [s, i] of this.patternReducers)
|
|
1431
|
+
this.matchesWhen(i, e) && this.forwardEventGuarded(s, e);
|
|
1432
|
+
const n = t !== this.state;
|
|
1433
|
+
return this.notifyEventSubscribers(e, "committed"), n && this.listeners.forEach((s) => s()), !0;
|
|
1184
1434
|
}
|
|
1185
1435
|
/**
|
|
1186
1436
|
* Runs a single committed event's effects as an **independent async task**,
|
|
@@ -1190,14 +1440,14 @@ class v {
|
|
|
1190
1440
|
*
|
|
1191
1441
|
* @internal
|
|
1192
1442
|
*/
|
|
1193
|
-
async runEventEffects(e, t,
|
|
1443
|
+
async runEventEffects(e, t, n) {
|
|
1194
1444
|
this.inFlightEffects++;
|
|
1195
1445
|
try {
|
|
1196
1446
|
t && await this.notifyEffects(e);
|
|
1197
|
-
} catch (
|
|
1198
|
-
console.error("Effect error:",
|
|
1447
|
+
} catch (s) {
|
|
1448
|
+
console.error("Effect error:", s);
|
|
1199
1449
|
} finally {
|
|
1200
|
-
this.inFlightEffects--,
|
|
1450
|
+
this.inFlightEffects--, n();
|
|
1201
1451
|
}
|
|
1202
1452
|
}
|
|
1203
1453
|
/**
|
|
@@ -1218,28 +1468,31 @@ class v {
|
|
|
1218
1468
|
*
|
|
1219
1469
|
* @internal
|
|
1220
1470
|
*/
|
|
1221
|
-
emitInstrumentation(e, t,
|
|
1222
|
-
const
|
|
1223
|
-
for (const f of
|
|
1224
|
-
|
|
1225
|
-
const
|
|
1471
|
+
emitInstrumentation(e, t, n, s, i) {
|
|
1472
|
+
const d = {}, a = {};
|
|
1473
|
+
for (const f of n)
|
|
1474
|
+
d[f] = this.getAtPath(s, f), a[f] = this.getAtPath(this.state, f);
|
|
1475
|
+
const r = {
|
|
1226
1476
|
event: {
|
|
1227
1477
|
id: e.id,
|
|
1228
1478
|
channel: e.channel,
|
|
1229
1479
|
type: e.type,
|
|
1230
|
-
payload: e.payload
|
|
1480
|
+
payload: e.payload,
|
|
1481
|
+
// Conditional, so an event without metadata produces an observer payload
|
|
1482
|
+
// byte-identical to the pre-`meta` shape.
|
|
1483
|
+
...e.meta !== void 0 ? { meta: e.meta } : {}
|
|
1231
1484
|
},
|
|
1232
1485
|
committed: t,
|
|
1233
|
-
changedPaths:
|
|
1234
|
-
prevValues:
|
|
1235
|
-
nextValues:
|
|
1236
|
-
reduceTimeMs:
|
|
1486
|
+
changedPaths: n,
|
|
1487
|
+
prevValues: d,
|
|
1488
|
+
nextValues: a,
|
|
1489
|
+
reduceTimeMs: i
|
|
1237
1490
|
};
|
|
1238
1491
|
for (const f of [...this.instrumentObservers])
|
|
1239
1492
|
try {
|
|
1240
|
-
f(
|
|
1241
|
-
} catch (
|
|
1242
|
-
console.error("Instrumentation observer error:",
|
|
1493
|
+
f(r);
|
|
1494
|
+
} catch (c) {
|
|
1495
|
+
console.error("Instrumentation observer error:", c);
|
|
1243
1496
|
}
|
|
1244
1497
|
}
|
|
1245
1498
|
/**
|
|
@@ -1319,11 +1572,11 @@ class v {
|
|
|
1319
1572
|
*
|
|
1320
1573
|
* @public
|
|
1321
1574
|
*/
|
|
1322
|
-
onEvent(e, t,
|
|
1323
|
-
const
|
|
1324
|
-
return
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1575
|
+
onEvent(e, t, n, s = "committed") {
|
|
1576
|
+
const i = `${e}::${String(t)}`, d = s === "committed" ? this.committedEventSubscribers : s === "uncommitted" ? this.uncommittedEventSubscribers : this.allEventSubscribers;
|
|
1577
|
+
return d.has(i) || d.set(i, /* @__PURE__ */ new Set()), d.get(i).add(n), () => {
|
|
1578
|
+
const a = d.get(i);
|
|
1579
|
+
a && (a.delete(n), a.size === 0 && d.delete(i));
|
|
1327
1580
|
};
|
|
1328
1581
|
}
|
|
1329
1582
|
/**
|
|
@@ -1365,13 +1618,21 @@ class v {
|
|
|
1365
1618
|
/**
|
|
1366
1619
|
* Registers a middleware (runs **before** reducers).
|
|
1367
1620
|
*
|
|
1368
|
-
* @param mw - Middleware `(state, event, emit) => boolean
|
|
1369
|
-
*
|
|
1621
|
+
* @param mw - Middleware `(state, event, emit) => boolean`. Return `false` to cancel event
|
|
1622
|
+
* propagation.
|
|
1370
1623
|
* @returns Unsubscribe function that removes this middleware.
|
|
1371
1624
|
*
|
|
1625
|
+
* @remarks
|
|
1626
|
+
* **Synchronous, and that is the contract.** The reduce phase completes before `emit()`
|
|
1627
|
+
* returns, so the commit decision has to be available in the same tick. An `async` middleware
|
|
1628
|
+
* returns a Promise, every Promise is truthy, and the veto would therefore never fire — the
|
|
1629
|
+
* event would commit while the middleware was still deciding. The type rejects it; this note
|
|
1630
|
+
* exists because the examples here used to teach it. Do authorization and validation here, and
|
|
1631
|
+
* anything that needs to await in an effect.
|
|
1632
|
+
*
|
|
1372
1633
|
* @example Logging middleware
|
|
1373
1634
|
* ```ts
|
|
1374
|
-
* const off = store.registerMiddleware(
|
|
1635
|
+
* const off = store.registerMiddleware((state, event) => {
|
|
1375
1636
|
* console.log('Event:', event.channel, event.type, event.payload);
|
|
1376
1637
|
* return true; // allow
|
|
1377
1638
|
* });
|
|
@@ -1398,7 +1659,7 @@ class v {
|
|
|
1398
1659
|
* Dynamically **adds** a named slice reducer at runtime.
|
|
1399
1660
|
*
|
|
1400
1661
|
* @param name - New slice name (must not already exist).
|
|
1401
|
-
* @param spec - Reducer spec (state,
|
|
1662
|
+
* @param spec - Reducer spec (state, when, reducer).
|
|
1402
1663
|
* @returns Disposer function that **removes** the slice (and its state).
|
|
1403
1664
|
*
|
|
1404
1665
|
* @example
|
|
@@ -1420,8 +1681,8 @@ class v {
|
|
|
1420
1681
|
if (e in this.reducers) throw new Error(`Reducer ${e} already exists`);
|
|
1421
1682
|
return this.mountSlice(e, t, {
|
|
1422
1683
|
preserveState: !1
|
|
1423
|
-
}), this.listeners.forEach((
|
|
1424
|
-
this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((
|
|
1684
|
+
}), this.listeners.forEach((n) => n()), () => {
|
|
1685
|
+
this.unmountSlice(e, { deleteState: !0 }), this.listeners.forEach((n) => n());
|
|
1425
1686
|
};
|
|
1426
1687
|
}
|
|
1427
1688
|
/**
|
|
@@ -1429,7 +1690,7 @@ class v {
|
|
|
1429
1690
|
*
|
|
1430
1691
|
* Effects are **keyed** by `(channel, type)` for O(1) lookup (no scanning all effects).
|
|
1431
1692
|
*
|
|
1432
|
-
* @param spec - Effect specification with `
|
|
1693
|
+
* @param spec - Effect specification with `when` targeting and `effect` (handler).
|
|
1433
1694
|
* @returns Unsubscribe function.
|
|
1434
1695
|
*
|
|
1435
1696
|
* @example Logging effect
|
|
@@ -1457,29 +1718,29 @@ class v {
|
|
|
1457
1718
|
* @public
|
|
1458
1719
|
*/
|
|
1459
1720
|
registerEffect(e) {
|
|
1460
|
-
const { effect: t, meta:
|
|
1461
|
-
if (
|
|
1462
|
-
const
|
|
1463
|
-
return this.patternEffects.add(
|
|
1464
|
-
this.patternEffects.delete(
|
|
1721
|
+
const { effect: t, meta: n, when: s } = e, i = [];
|
|
1722
|
+
if (n && this.effectMeta.set(t, n), s && ("any" in s && s.any === !0 || "channel" in s || "channels" in s)) {
|
|
1723
|
+
const r = { effect: t, when: s };
|
|
1724
|
+
return this.patternEffects.add(r), () => {
|
|
1725
|
+
this.patternEffects.delete(r);
|
|
1465
1726
|
};
|
|
1466
1727
|
}
|
|
1467
|
-
const
|
|
1468
|
-
if (
|
|
1469
|
-
const
|
|
1470
|
-
return this.patternEffects.add(
|
|
1471
|
-
this.patternEffects.delete(
|
|
1728
|
+
const a = this.normalizeEventKeys(e);
|
|
1729
|
+
if (a.length === 0 && !s) {
|
|
1730
|
+
const r = { effect: t, when: { any: !0 } };
|
|
1731
|
+
return this.patternEffects.add(r), () => {
|
|
1732
|
+
this.patternEffects.delete(r);
|
|
1472
1733
|
};
|
|
1473
1734
|
}
|
|
1474
|
-
for (const [
|
|
1475
|
-
const
|
|
1476
|
-
this.effects.has(
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1735
|
+
for (const [r, f] of a) {
|
|
1736
|
+
const c = `${String(r)}::${String(f)}`;
|
|
1737
|
+
this.effects.has(c) || this.effects.set(c, /* @__PURE__ */ new Set()), this.effects.get(c).add(t), i.push(() => {
|
|
1738
|
+
const u = this.effects.get(c);
|
|
1739
|
+
u && (u.delete(t), u.size === 0 && this.effects.delete(c));
|
|
1479
1740
|
});
|
|
1480
1741
|
}
|
|
1481
1742
|
return () => {
|
|
1482
|
-
for (const
|
|
1743
|
+
for (const r of i) r();
|
|
1483
1744
|
};
|
|
1484
1745
|
}
|
|
1485
1746
|
/**
|
|
@@ -1503,15 +1764,15 @@ class v {
|
|
|
1503
1764
|
*
|
|
1504
1765
|
* @public
|
|
1505
1766
|
*/
|
|
1506
|
-
onEffect(e, t,
|
|
1507
|
-
const
|
|
1508
|
-
if (
|
|
1509
|
-
const
|
|
1510
|
-
return r
|
|
1767
|
+
onEffect(e, t, n) {
|
|
1768
|
+
const s = async (i, d, a) => {
|
|
1769
|
+
if (i.channel !== e || i.type !== t) return;
|
|
1770
|
+
const r = i;
|
|
1771
|
+
return n(r.payload, d, a, r);
|
|
1511
1772
|
};
|
|
1512
1773
|
return this.registerEffect({
|
|
1513
|
-
|
|
1514
|
-
effect:
|
|
1774
|
+
when: { keys: [[e, t]] },
|
|
1775
|
+
effect: s
|
|
1515
1776
|
});
|
|
1516
1777
|
}
|
|
1517
1778
|
/**
|
|
@@ -1573,11 +1834,11 @@ class v {
|
|
|
1573
1834
|
* @public
|
|
1574
1835
|
*/
|
|
1575
1836
|
replaceReducers(e, t = {}) {
|
|
1576
|
-
const
|
|
1577
|
-
for (const
|
|
1578
|
-
|
|
1579
|
-
for (const [
|
|
1580
|
-
|
|
1837
|
+
const n = t.preserveState !== !1, s = new Set(Object.keys(this.reducers)), i = Object.entries(e), d = new Set(i.map(([a]) => a));
|
|
1838
|
+
for (const a of s)
|
|
1839
|
+
d.has(a) || this.unmountSlice(a, { deleteState: !0 });
|
|
1840
|
+
for (const [a, r] of i)
|
|
1841
|
+
s.has(a) ? (this.unmountSlice(a, { deleteState: !1 }), this.mountSlice(a, r, { preserveState: n })) : this.mountSlice(a, r, { preserveState: !1 });
|
|
1581
1842
|
}
|
|
1582
1843
|
/**
|
|
1583
1844
|
* Convenience API to replace **any subset** of store parts (HMR patterns).
|
|
@@ -1604,31 +1865,39 @@ class v {
|
|
|
1604
1865
|
* and wires `(channel, type)` listeners on the reducer bus.
|
|
1605
1866
|
*
|
|
1606
1867
|
* @param name - Slice name.
|
|
1607
|
-
* @param rSpec - Reducer spec (state,
|
|
1868
|
+
* @param rSpec - Reducer spec (state, when, reducer).
|
|
1608
1869
|
* @param opts - `{ preserveState: boolean }` whether to keep existing state.
|
|
1609
1870
|
*
|
|
1610
1871
|
* @internal
|
|
1611
1872
|
*/
|
|
1612
|
-
mountSlice(e, t,
|
|
1613
|
-
const
|
|
1614
|
-
if (this.reducers[e] = new
|
|
1615
|
-
this.
|
|
1873
|
+
mountSlice(e, t, n) {
|
|
1874
|
+
const s = e, { reducer: i, state: d, when: a } = t;
|
|
1875
|
+
if (this.reducers[e] = new K(i), (!n.preserveState || this.state[s] === void 0) && (this.state = {
|
|
1876
|
+
...this.state,
|
|
1877
|
+
[s]: k(W(s, d))
|
|
1878
|
+
}), a && ("any" in a && a.any === !0 || "channel" in a || "channels" in a)) {
|
|
1879
|
+
this.patternReducers.set(e, a), this.sliceUnsubs.set(s, []);
|
|
1616
1880
|
return;
|
|
1617
1881
|
}
|
|
1618
|
-
const
|
|
1619
|
-
if (
|
|
1620
|
-
this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(
|
|
1882
|
+
const f = this.normalizeEventKeys(t);
|
|
1883
|
+
if (f.length === 0 && !a) {
|
|
1884
|
+
this.patternReducers.set(e, { any: !0 }), this.sliceUnsubs.set(s, []);
|
|
1621
1885
|
return;
|
|
1622
1886
|
}
|
|
1623
|
-
const
|
|
1624
|
-
for (const [u,
|
|
1625
|
-
const y = this.reducerBus.on(u,
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1887
|
+
const c = [];
|
|
1888
|
+
for (const [u, l] of f) {
|
|
1889
|
+
const y = this.reducerBus.on(u, l, (h, m) => {
|
|
1890
|
+
const g = m ?? {
|
|
1891
|
+
channel: u,
|
|
1892
|
+
type: l,
|
|
1893
|
+
payload: h,
|
|
1894
|
+
id: this.idFactory()
|
|
1895
|
+
};
|
|
1896
|
+
this.forwardEventGuarded(e, g);
|
|
1628
1897
|
});
|
|
1629
|
-
|
|
1898
|
+
c.push(y);
|
|
1630
1899
|
}
|
|
1631
|
-
this.sliceUnsubs.set(
|
|
1900
|
+
this.sliceUnsubs.set(s, c);
|
|
1632
1901
|
}
|
|
1633
1902
|
/**
|
|
1634
1903
|
* Unmounts a slice: disposes reducer-bus listeners, removes reducer,
|
|
@@ -1640,24 +1909,27 @@ class v {
|
|
|
1640
1909
|
* @internal
|
|
1641
1910
|
*/
|
|
1642
1911
|
unmountSlice(e, t) {
|
|
1643
|
-
const
|
|
1912
|
+
const n = e;
|
|
1644
1913
|
this.patternReducers.delete(e);
|
|
1645
|
-
const
|
|
1646
|
-
if (
|
|
1647
|
-
for (const
|
|
1914
|
+
const s = this.sliceUnsubs.get(n);
|
|
1915
|
+
if (s) {
|
|
1916
|
+
for (const i of s)
|
|
1648
1917
|
try {
|
|
1649
|
-
|
|
1650
|
-
} catch (
|
|
1651
|
-
console.error(`[Store error]: ${
|
|
1918
|
+
i();
|
|
1919
|
+
} catch (d) {
|
|
1920
|
+
console.error(`[Store error]: ${d}`);
|
|
1652
1921
|
}
|
|
1653
|
-
this.sliceUnsubs.delete(
|
|
1922
|
+
this.sliceUnsubs.delete(n);
|
|
1923
|
+
}
|
|
1924
|
+
if (delete this.reducers[e], t.deleteState) {
|
|
1925
|
+
const { [n]: i, ...d } = this.state;
|
|
1926
|
+
this.state = d;
|
|
1654
1927
|
}
|
|
1655
|
-
delete this.reducers[e], t.deleteState && delete this.state[r];
|
|
1656
1928
|
}
|
|
1657
1929
|
/**
|
|
1658
|
-
* Normalizes event targeting from `when`
|
|
1930
|
+
* Normalizes event targeting from `when` to an array of EventKeys.
|
|
1659
1931
|
*
|
|
1660
|
-
* @param spec - Object with optional `when`
|
|
1932
|
+
* @param spec - Object with an optional `when` matcher.
|
|
1661
1933
|
* @returns Array of `[channel, type]` pairs.
|
|
1662
1934
|
*
|
|
1663
1935
|
* @internal
|
|
@@ -1665,16 +1937,10 @@ class v {
|
|
|
1665
1937
|
normalizeEventKeys(e) {
|
|
1666
1938
|
if (e.when) {
|
|
1667
1939
|
const t = e.when;
|
|
1668
|
-
if ("any" in t && t.any === !0)
|
|
1669
|
-
return [];
|
|
1670
1940
|
if ("keys" in t)
|
|
1671
1941
|
return t.keys;
|
|
1672
|
-
if ("channel" in t)
|
|
1673
|
-
return [];
|
|
1674
|
-
if ("channels" in t)
|
|
1675
|
-
return [];
|
|
1676
1942
|
}
|
|
1677
|
-
return
|
|
1943
|
+
return [];
|
|
1678
1944
|
}
|
|
1679
1945
|
/**
|
|
1680
1946
|
* Reads a dotted path from an object (supports numeric array indices via string keys).
|
|
@@ -1687,13 +1953,13 @@ class v {
|
|
|
1687
1953
|
*/
|
|
1688
1954
|
getAtPath(e, t) {
|
|
1689
1955
|
if (!t) return e;
|
|
1690
|
-
const
|
|
1691
|
-
let
|
|
1692
|
-
for (const
|
|
1693
|
-
if (
|
|
1694
|
-
|
|
1956
|
+
const s = (t[0] === "." ? t.slice(1) : t).split(".");
|
|
1957
|
+
let i = e;
|
|
1958
|
+
for (const d of s) {
|
|
1959
|
+
if (i == null) return;
|
|
1960
|
+
i = i[d];
|
|
1695
1961
|
}
|
|
1696
|
-
return
|
|
1962
|
+
return i;
|
|
1697
1963
|
}
|
|
1698
1964
|
/**
|
|
1699
1965
|
* Builds ancestor paths for a dotted path.
|
|
@@ -1712,33 +1978,397 @@ class v {
|
|
|
1712
1978
|
*/
|
|
1713
1979
|
static buildAncestorPaths(e) {
|
|
1714
1980
|
if (!e) return [];
|
|
1715
|
-
const
|
|
1716
|
-
for (let
|
|
1717
|
-
|
|
1718
|
-
return
|
|
1981
|
+
const n = (e[0] === "." ? e.slice(1) : e).split("."), s = [];
|
|
1982
|
+
for (let i = 0; i < n.length; i++)
|
|
1983
|
+
s.push(n.slice(0, i + 1).join("."));
|
|
1984
|
+
return s;
|
|
1719
1985
|
}
|
|
1720
1986
|
}
|
|
1721
|
-
function
|
|
1722
|
-
return new
|
|
1723
|
-
name:
|
|
1724
|
-
reducer:
|
|
1725
|
-
middleware:
|
|
1726
|
-
effects:
|
|
1727
|
-
dedupWindowMs:
|
|
1728
|
-
|
|
1729
|
-
|
|
1987
|
+
function L(o) {
|
|
1988
|
+
return new $({
|
|
1989
|
+
name: o.name,
|
|
1990
|
+
reducer: o.reducer ?? {},
|
|
1991
|
+
middleware: o.middleware ?? [],
|
|
1992
|
+
effects: o.effects ?? [],
|
|
1993
|
+
dedupWindowMs: o.dedupWindowMs,
|
|
1994
|
+
idFactory: o.idFactory,
|
|
1995
|
+
devtools: o.devtools,
|
|
1996
|
+
onEffectError: o.onEffectError,
|
|
1997
|
+
onReducerError: o.onReducerError
|
|
1730
1998
|
});
|
|
1731
1999
|
}
|
|
1732
|
-
const
|
|
2000
|
+
const G = (o) => (e, t) => t.map((n) => [e, n]), Q = () => (o) => o, I = /* @__PURE__ */ new Set();
|
|
2001
|
+
function j(o) {
|
|
2002
|
+
const e = String(o);
|
|
2003
|
+
I.has(e) || (I.add(e), console.warn(
|
|
2004
|
+
`[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.`
|
|
2005
|
+
));
|
|
2006
|
+
}
|
|
2007
|
+
function F(o, e) {
|
|
2008
|
+
if (o.length !== e.length) return e;
|
|
2009
|
+
for (let t = 0; t < o.length; t++)
|
|
2010
|
+
if (o[t] !== e[t]) return e;
|
|
2011
|
+
return o;
|
|
2012
|
+
}
|
|
2013
|
+
function J(o = {}) {
|
|
2014
|
+
const e = o.selectId ?? ((r) => r.id), { sortComparer: t } = o, n = (r, f) => {
|
|
2015
|
+
if (t === void 0) return f;
|
|
2016
|
+
const c = [...f].sort((u, l) => {
|
|
2017
|
+
const y = r.entities[u], h = r.entities[l];
|
|
2018
|
+
return y === void 0 || h === void 0 ? 0 : t(y, h);
|
|
2019
|
+
});
|
|
2020
|
+
return F(f, c);
|
|
2021
|
+
}, s = (r, f, c) => {
|
|
2022
|
+
const u = { ...r, entities: f, ids: c };
|
|
2023
|
+
return { ...u, ids: n(u, c) };
|
|
2024
|
+
}, i = (r, f, c) => {
|
|
2025
|
+
let u = null, l = null;
|
|
2026
|
+
for (const y of f) {
|
|
2027
|
+
const h = e(y);
|
|
2028
|
+
process.env.NODE_ENV !== "production" && String(h).includes(".") && j(h);
|
|
2029
|
+
const m = (u ?? r.entities)[h];
|
|
2030
|
+
if (m !== void 0 && c === "add") continue;
|
|
2031
|
+
const g = m !== void 0 && c === "upsert" ? { ...m, ...y } : y;
|
|
2032
|
+
u ?? (u = { ...r.entities }), u[h] = g, m === void 0 && (l ?? (l = [...r.ids]), l.push(h));
|
|
2033
|
+
}
|
|
2034
|
+
return u === null ? r : s(r, u, l ?? r.ids);
|
|
2035
|
+
}, d = (r, f) => {
|
|
2036
|
+
let c = null;
|
|
2037
|
+
for (const { id: u, changes: l } of f) {
|
|
2038
|
+
const y = (c ?? r.entities)[u];
|
|
2039
|
+
y !== void 0 && (c ?? (c = { ...r.entities }), c[u] = { ...y, ...l });
|
|
2040
|
+
}
|
|
2041
|
+
return c === null ? r : s(r, c, r.ids);
|
|
2042
|
+
}, a = (r, f) => {
|
|
2043
|
+
const c = new Set(f.filter((l) => r.entities[l] !== void 0));
|
|
2044
|
+
if (c.size === 0) return r;
|
|
2045
|
+
const u = { ...r.entities };
|
|
2046
|
+
for (const l of c) delete u[l];
|
|
2047
|
+
return s(
|
|
2048
|
+
r,
|
|
2049
|
+
u,
|
|
2050
|
+
r.ids.filter((l) => !c.has(l))
|
|
2051
|
+
);
|
|
2052
|
+
};
|
|
2053
|
+
return {
|
|
2054
|
+
getInitialState(r) {
|
|
2055
|
+
const f = { ids: [], entities: {} };
|
|
2056
|
+
return r === void 0 ? f : { ...f, ...r };
|
|
2057
|
+
},
|
|
2058
|
+
addOne: (r, f) => i(r, [f], "add"),
|
|
2059
|
+
addMany: (r, f) => i(r, f, "add"),
|
|
2060
|
+
setOne: (r, f) => i(r, [f], "set"),
|
|
2061
|
+
setMany: (r, f) => i(r, f, "set"),
|
|
2062
|
+
setAll: (r, f) => {
|
|
2063
|
+
const c = {}, u = [];
|
|
2064
|
+
for (const l of f) {
|
|
2065
|
+
const y = e(l);
|
|
2066
|
+
c[y] === void 0 && u.push(y), c[y] = l;
|
|
2067
|
+
}
|
|
2068
|
+
return s(r, c, u);
|
|
2069
|
+
},
|
|
2070
|
+
updateOne: (r, f) => d(r, [f]),
|
|
2071
|
+
updateMany: (r, f) => d(r, f),
|
|
2072
|
+
upsertOne: (r, f) => i(r, [f], "upsert"),
|
|
2073
|
+
upsertMany: (r, f) => i(r, f, "upsert"),
|
|
2074
|
+
removeOne: (r, f) => a(r, [f]),
|
|
2075
|
+
removeMany: (r, f) => a(r, f),
|
|
2076
|
+
removeAll: (r) => r.ids.length === 0 ? r : s(r, {}, []),
|
|
2077
|
+
selectIds: (r) => r.ids,
|
|
2078
|
+
selectEntities: (r) => r.entities,
|
|
2079
|
+
selectAll: (r) => r.ids.map((f) => r.entities[f]),
|
|
2080
|
+
selectById: (r, f) => r.entities[f],
|
|
2081
|
+
selectTotal: (r) => r.ids.length,
|
|
2082
|
+
idsPath: "ids",
|
|
2083
|
+
pathTo: (r, f) => f === void 0 ? `entities.${r}` : `entities.${r}.${f}`,
|
|
2084
|
+
anyField: (r) => `entities.*.${r}`
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
const E = "$yoltra";
|
|
2088
|
+
function _(o, e = {}) {
|
|
2089
|
+
const t = e.maxNodes ?? 1e5, n = e.sanitize, s = [], i = /* @__PURE__ */ new Map();
|
|
2090
|
+
let d = 0, a = !1;
|
|
2091
|
+
function r(c, u) {
|
|
2092
|
+
if (n !== void 0 && (c = n(u, c)), d += 1, d > t)
|
|
2093
|
+
return a = !0, { [E]: "unsupported", kind: "truncated" };
|
|
2094
|
+
switch (typeof c) {
|
|
2095
|
+
case "undefined":
|
|
2096
|
+
return { [E]: "undefined" };
|
|
2097
|
+
case "bigint":
|
|
2098
|
+
return { [E]: "bigint", value: c.toString() };
|
|
2099
|
+
case "number":
|
|
2100
|
+
return Number.isNaN(c) ? { [E]: "nan" } : c === 1 / 0 ? { [E]: "infinity", sign: 1 } : c === -1 / 0 ? { [E]: "infinity", sign: -1 } : c;
|
|
2101
|
+
case "function":
|
|
2102
|
+
case "symbol":
|
|
2103
|
+
return s.push(u), { [E]: "unsupported", kind: typeof c };
|
|
2104
|
+
case "string":
|
|
2105
|
+
case "boolean":
|
|
2106
|
+
return c;
|
|
2107
|
+
}
|
|
2108
|
+
if (c === null) return null;
|
|
2109
|
+
const l = c, y = i.get(l);
|
|
2110
|
+
if (y !== void 0) return { [E]: "ref", path: y };
|
|
2111
|
+
if (i.set(l, u), c instanceof Date)
|
|
2112
|
+
return { [E]: "date", iso: c.toISOString() };
|
|
2113
|
+
if (c instanceof RegExp)
|
|
2114
|
+
return { [E]: "regexp", source: c.source, flags: c.flags };
|
|
2115
|
+
if (c instanceof Error)
|
|
2116
|
+
return { [E]: "error", name: c.name, message: c.message };
|
|
2117
|
+
if (c instanceof Map) {
|
|
2118
|
+
const m = [];
|
|
2119
|
+
let g = 0;
|
|
2120
|
+
for (const [w, D] of c)
|
|
2121
|
+
m.push([r(w, `${u}/@k${g}`), r(D, `${u}/${g}`)]), g += 1;
|
|
2122
|
+
return { [E]: "map", entries: m };
|
|
2123
|
+
}
|
|
2124
|
+
if (c instanceof Set) {
|
|
2125
|
+
const m = [];
|
|
2126
|
+
let g = 0;
|
|
2127
|
+
for (const w of c)
|
|
2128
|
+
m.push(r(w, `${u}/${g}`)), g += 1;
|
|
2129
|
+
return { [E]: "set", values: m };
|
|
2130
|
+
}
|
|
2131
|
+
if (Array.isArray(c))
|
|
2132
|
+
return c.map((m, g) => r(m, `${u}/${g}`));
|
|
2133
|
+
const h = {};
|
|
2134
|
+
for (const [m, g] of Object.entries(c))
|
|
2135
|
+
h[m] = r(g, `${u}/${N(m)}`);
|
|
2136
|
+
return E in h ? { [E]: "escaped", value: h } : h;
|
|
2137
|
+
}
|
|
2138
|
+
return { value: r(o, ""), report: { truncated: a, unsupported: s } };
|
|
2139
|
+
}
|
|
2140
|
+
function V(o) {
|
|
2141
|
+
const e = /* @__PURE__ */ new Map(), t = [];
|
|
2142
|
+
function n(d, a) {
|
|
2143
|
+
if (d === null || typeof d != "object") return d;
|
|
2144
|
+
if (Array.isArray(d)) {
|
|
2145
|
+
const f = [];
|
|
2146
|
+
return e.set(a, f), d.forEach((c, u) => {
|
|
2147
|
+
if (R(c)) {
|
|
2148
|
+
t.push({ target: f, key: u, path: c.path }), f[u] = void 0;
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
f[u] = n(c, `${a}/${u}`);
|
|
2152
|
+
}), f;
|
|
2153
|
+
}
|
|
2154
|
+
if (typeof d[E] == "string") {
|
|
2155
|
+
const f = d;
|
|
2156
|
+
switch (f[E]) {
|
|
2157
|
+
case "undefined":
|
|
2158
|
+
return;
|
|
2159
|
+
case "nan":
|
|
2160
|
+
return Number.NaN;
|
|
2161
|
+
case "infinity":
|
|
2162
|
+
return f.sign === 1 ? 1 / 0 : -1 / 0;
|
|
2163
|
+
case "bigint":
|
|
2164
|
+
return BigInt(f.value);
|
|
2165
|
+
case "date":
|
|
2166
|
+
return new Date(f.iso);
|
|
2167
|
+
case "regexp":
|
|
2168
|
+
return new RegExp(f.source, f.flags);
|
|
2169
|
+
case "error": {
|
|
2170
|
+
const c = new Error(f.message);
|
|
2171
|
+
return c.name = f.name, c;
|
|
2172
|
+
}
|
|
2173
|
+
case "unsupported":
|
|
2174
|
+
return;
|
|
2175
|
+
case "ref":
|
|
2176
|
+
return;
|
|
2177
|
+
case "map": {
|
|
2178
|
+
const c = /* @__PURE__ */ new Map();
|
|
2179
|
+
return e.set(a, c), f.entries.forEach(([u, l], y) => {
|
|
2180
|
+
c.set(n(u, `${a}/@k${y}`), n(l, `${a}/${y}`));
|
|
2181
|
+
}), c;
|
|
2182
|
+
}
|
|
2183
|
+
case "set": {
|
|
2184
|
+
const c = /* @__PURE__ */ new Set();
|
|
2185
|
+
return e.set(a, c), f.values.forEach((u, l) => c.add(n(u, `${a}/${l}`))), c;
|
|
2186
|
+
}
|
|
2187
|
+
case "escaped":
|
|
2188
|
+
return s(f.value, a);
|
|
2189
|
+
default:
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
return s(d, a);
|
|
2194
|
+
}
|
|
2195
|
+
function s(d, a) {
|
|
2196
|
+
const r = {};
|
|
2197
|
+
e.set(a, r);
|
|
2198
|
+
for (const [f, c] of Object.entries(d)) {
|
|
2199
|
+
const u = `${a}/${N(f)}`;
|
|
2200
|
+
if (R(c)) {
|
|
2201
|
+
t.push({ target: r, key: f, path: c.path }), r[f] = void 0;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
r[f] = n(c, u);
|
|
2205
|
+
}
|
|
2206
|
+
return r;
|
|
2207
|
+
}
|
|
2208
|
+
const i = n(o, "");
|
|
2209
|
+
e.set("", i);
|
|
2210
|
+
for (const { target: d, key: a, path: r } of t)
|
|
2211
|
+
d[a] = e.get(r);
|
|
2212
|
+
return i;
|
|
2213
|
+
}
|
|
2214
|
+
function R(o) {
|
|
2215
|
+
return o !== null && typeof o == "object" && o[E] === "ref" && typeof o.path == "string";
|
|
2216
|
+
}
|
|
2217
|
+
function N(o) {
|
|
2218
|
+
return o.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2219
|
+
}
|
|
2220
|
+
function Y(o, e, t = {}) {
|
|
2221
|
+
let n = t.maxNodes ?? 1e5;
|
|
2222
|
+
for (let s = 0; s < 8; s += 1) {
|
|
2223
|
+
const { value: i, report: d } = _(o, { ...t, maxNodes: n });
|
|
2224
|
+
let a;
|
|
2225
|
+
try {
|
|
2226
|
+
a = JSON.stringify(i)?.length ?? 0;
|
|
2227
|
+
} catch {
|
|
2228
|
+
a = Number.POSITIVE_INFINITY;
|
|
2229
|
+
}
|
|
2230
|
+
if (a <= e)
|
|
2231
|
+
return d.truncated ? {
|
|
2232
|
+
value: i,
|
|
2233
|
+
truncated: !0,
|
|
2234
|
+
note: `State was too large to send in full; parts beyond ${n} nodes are omitted.`
|
|
2235
|
+
} : { value: i, truncated: !1 };
|
|
2236
|
+
const r = Math.floor(n * e * 0.8 / a);
|
|
2237
|
+
if (n = Math.max(1, Math.min(r, n - 1)), n <= 1 && s > 0)
|
|
2238
|
+
break;
|
|
2239
|
+
}
|
|
2240
|
+
return {
|
|
2241
|
+
value: { [E]: "unsupported", kind: "truncated" },
|
|
2242
|
+
truncated: !0,
|
|
2243
|
+
note: `State exceeds the ${e}-byte transport limit and could not be reduced to fit.`
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function b(o, e, t) {
|
|
2247
|
+
o.onError?.(e, t);
|
|
2248
|
+
}
|
|
2249
|
+
async function q(o) {
|
|
2250
|
+
const e = { slices: {}, restored: !1 };
|
|
2251
|
+
let t;
|
|
2252
|
+
try {
|
|
2253
|
+
t = o.source ?? await o.adapter.read(o.key);
|
|
2254
|
+
} catch (s) {
|
|
2255
|
+
return b(o, s, "read"), e;
|
|
2256
|
+
}
|
|
2257
|
+
if (t == null || t === "") return e;
|
|
2258
|
+
let n;
|
|
2259
|
+
try {
|
|
2260
|
+
n = V(JSON.parse(t));
|
|
2261
|
+
} catch (s) {
|
|
2262
|
+
return b(o, s, "decode"), e;
|
|
2263
|
+
}
|
|
2264
|
+
if (n === null || typeof n != "object" || typeof n.version != "number")
|
|
2265
|
+
return b(o, new Error("persisted payload is not a recognisable envelope"), "decode"), e;
|
|
2266
|
+
if (n.version !== o.version) {
|
|
2267
|
+
if (o.migrate === void 0)
|
|
2268
|
+
return b(
|
|
2269
|
+
o,
|
|
2270
|
+
new Error(
|
|
2271
|
+
`persisted state is version ${n.version}, this build expects ${o.version}, and no migrate was supplied`
|
|
2272
|
+
),
|
|
2273
|
+
"migrate"
|
|
2274
|
+
), e;
|
|
2275
|
+
try {
|
|
2276
|
+
const s = o.migrate(n.slices, n.version);
|
|
2277
|
+
return s === null ? e : { slices: s, restored: !0 };
|
|
2278
|
+
} catch (s) {
|
|
2279
|
+
return b(o, s, "migrate"), e;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return { slices: n.slices ?? {}, restored: !0 };
|
|
2283
|
+
}
|
|
2284
|
+
function X(o, e) {
|
|
2285
|
+
if (!e.restored) return o;
|
|
2286
|
+
const t = {};
|
|
2287
|
+
for (const [n, s] of Object.entries(o)) {
|
|
2288
|
+
const i = e.slices[n];
|
|
2289
|
+
t[n] = i === void 0 ? s : { ...s, state: i };
|
|
2290
|
+
}
|
|
2291
|
+
return t;
|
|
2292
|
+
}
|
|
2293
|
+
function A(o, e) {
|
|
2294
|
+
const t = o ?? {}, n = e.slices === void 0 ? t : Object.fromEntries(e.slices.filter((s) => s in t).map((s) => [s, t[s]]));
|
|
2295
|
+
return JSON.stringify(_({ version: e.version, slices: n }).value);
|
|
2296
|
+
}
|
|
2297
|
+
function Z(o, e) {
|
|
2298
|
+
const t = e.throttleMs ?? 250, n = e.slices;
|
|
2299
|
+
let s = null, i = !1;
|
|
2300
|
+
const d = () => {
|
|
2301
|
+
if (i) {
|
|
2302
|
+
i = !1;
|
|
2303
|
+
try {
|
|
2304
|
+
const f = e.adapter.write(e.key, A(o.getState(), e));
|
|
2305
|
+
f instanceof Promise && f.catch((c) => b(e, c, "write"));
|
|
2306
|
+
} catch (f) {
|
|
2307
|
+
b(e, f, "write");
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}, a = () => {
|
|
2311
|
+
if (i = !0, t <= 0) {
|
|
2312
|
+
d();
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
s === null && (s = setTimeout(() => {
|
|
2316
|
+
s = null, d();
|
|
2317
|
+
}, t), s.unref?.());
|
|
2318
|
+
}, r = o.instrument((f) => {
|
|
2319
|
+
if (n === void 0) {
|
|
2320
|
+
a();
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
(f.changedPaths ?? []).some(
|
|
2324
|
+
(u) => n.some((l) => u === l || u.startsWith(`${l}.`))
|
|
2325
|
+
) && a();
|
|
2326
|
+
});
|
|
2327
|
+
return () => {
|
|
2328
|
+
r(), s !== null && (clearTimeout(s), s = null), d();
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
function ee(o, e) {
|
|
2332
|
+
return A(o.getState(), e);
|
|
2333
|
+
}
|
|
2334
|
+
function te(o) {
|
|
2335
|
+
return {
|
|
2336
|
+
read: (e) => o.getItem(e),
|
|
2337
|
+
write: (e, t) => o.setItem(e, t),
|
|
2338
|
+
remove: (e) => o.removeItem(e)
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
function se(o) {
|
|
2342
|
+
const e = new Map(Object.entries(o ?? {}));
|
|
2343
|
+
return {
|
|
2344
|
+
read: (t) => e.get(t) ?? null,
|
|
2345
|
+
write: (t, n) => {
|
|
2346
|
+
e.set(t, n);
|
|
2347
|
+
},
|
|
2348
|
+
remove: (t) => {
|
|
2349
|
+
e.delete(t);
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
1733
2353
|
export {
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2354
|
+
B as EventBus,
|
|
2355
|
+
H as LooseEventBus,
|
|
2356
|
+
K as Reducer,
|
|
2357
|
+
$ as Store,
|
|
2358
|
+
J as createEntityAdapter,
|
|
2359
|
+
se as createMemoryAdapter,
|
|
2360
|
+
L as createStore,
|
|
2361
|
+
te as createWebStorageAdapter,
|
|
2362
|
+
V as decodeState,
|
|
2363
|
+
ee as dehydrate,
|
|
2364
|
+
O as detectChangedProps,
|
|
2365
|
+
_ as encodeState,
|
|
2366
|
+
Y as encodeStateBounded,
|
|
2367
|
+
Q as eventKeys,
|
|
2368
|
+
S as freezeState,
|
|
2369
|
+
q as hydrate,
|
|
2370
|
+
Z as persist,
|
|
2371
|
+
G as typedEvents,
|
|
2372
|
+
X as withHydration
|
|
1743
2373
|
};
|
|
1744
2374
|
//# sourceMappingURL=yoltra.esm.js.map
|