@yoltra/core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @module @yoltra/core
3
+ */
4
+ /**
5
+ * Brand identifying a {@link Rejection}.
6
+ *
7
+ * @remarks
8
+ * `Symbol.for` rather than `Symbol()`, so the brand survives two copies of this package meeting
9
+ * at runtime — a duplicated dependency, a federated bundle, a consumer that pinned an older
10
+ * minor. With a unique symbol the check would silently answer `false` across that boundary and a
11
+ * refusal would read as ordinary state, which is the failure this whole feature exists to end.
12
+ *
13
+ * @internal
14
+ */
15
+ declare const REJECTED: unique symbol;
16
+ /**
17
+ * A reducer's refusal to apply a write, carrying the reason.
18
+ *
19
+ * @remarks
20
+ * Distinct from a reducer returning its state unchanged, which is indistinguishable from "the
21
+ * event did not concern me". A `Rejection` says *this write was considered and declined*, and it
22
+ * says why — which is what a contended store needs and what a lost update otherwise costs.
23
+ *
24
+ * @public
25
+ */
26
+ export interface Rejection {
27
+ readonly [REJECTED]: true;
28
+ /** Why the write was refused. Surfaced to the caller and to `onRejected`. */
29
+ readonly reason: string;
30
+ }
31
+ /**
32
+ * Builds a {@link Rejection} for a reducer to return instead of state.
33
+ *
34
+ * @param reason - Why the write is refused; surfaced verbatim to the caller.
35
+ *
36
+ * @remarks
37
+ * Rejecting is a whole-event act: no slice commits, no change notifications fire, and the
38
+ * caller's `emit` resolves reporting the refusal. A reducer that merely has nothing to do should
39
+ * return its state, not this.
40
+ *
41
+ * @example Compare-and-swap on a contended slice
42
+ * ```ts
43
+ * reducer: (state, event) =>
44
+ * event.payload.expectedVersion === state.version
45
+ * ? { ...state, ...event.payload.patch, version: state.version + 1 }
46
+ * : Rejected(`stale write: expected v${event.payload.expectedVersion}, have v${state.version}`)
47
+ * ```
48
+ *
49
+ * @public
50
+ */
51
+ export declare function Rejected(reason: string): Rejection;
52
+ /**
53
+ * Whether a reducer returned a {@link Rejection} rather than state.
54
+ *
55
+ * @public
56
+ */
57
+ export declare function isRejected(value: unknown): value is Rejection;
58
+ export {};
@@ -1,6 +1,5 @@
1
- /**
2
- * @module @yoltra/core
3
- */
1
+ import { Rejection } from './store/rejection.js';
2
+ import { CallHandle, CallOptions } from './store/call.js';
4
3
  /**
5
4
  * A minimal "record of record" constraint for EventMaps.
6
5
  *
@@ -104,6 +103,29 @@ export interface Event<EM extends EventMapBase = EventMapBase, C extends keyof E
104
103
  * Absent entirely unless {@link EmitOptions.meta} was supplied. See {@link EventMeta}.
105
104
  */
106
105
  readonly meta?: EventMeta;
106
+ /**
107
+ * The `id` of the event whose handling caused this one, when there was one.
108
+ *
109
+ * @remarks
110
+ * Absent on a **root** event — one emitted by application code rather than by a middleware,
111
+ * subscriber or effect reacting to another event. Together with {@link Event.depth} this makes
112
+ * a cascade legible after the fact: without it, a runaway chain is a pile of unrelated events
113
+ * with no way to tell which caused which.
114
+ */
115
+ readonly parentId?: string;
116
+ /**
117
+ * How many events deep in a causal chain this one is. A root event is depth `0`; an event
118
+ * emitted while handling it is `1`, and so on.
119
+ *
120
+ * @remarks
121
+ * Absent on a root event rather than present as `0`, so an event emitted by application code
122
+ * stays byte-identical to one built before causality tracking existed — the same treatment
123
+ * {@link Event.meta} gets, and for the same reason: `Object.keys` and `toStrictEqual` are load
124
+ * bearing in consumer tests.
125
+ *
126
+ * This is the value {@link StoreSpec.maxReduceDepth} bounds.
127
+ */
128
+ readonly depth?: number;
107
129
  }
108
130
  /**
109
131
  * Generic "old → new" wrapper for fine-grained change notifications.
@@ -127,6 +149,20 @@ export interface Change<V = any> {
127
149
  newValue: V;
128
150
  /** Dotted path for fine-grained listeners; e.g., "data.items.0.title" */
129
151
  path?: string;
152
+ /**
153
+ * The `id` of the event that caused this change.
154
+ *
155
+ * @remarks
156
+ * A change used to be anonymous, so a subscriber that needed to know *why* a value moved had
157
+ * to mirror the cause into state and store it twice. Absent when the change did not come from
158
+ * an event — a DevTools time-travel snapshot, for instance — which is itself the signal that
159
+ * no event caused it.
160
+ */
161
+ eventId?: string;
162
+ /** Channel of the causing event. Absent for the same reason as {@link Change.eventId}. */
163
+ channel?: string;
164
+ /** Type of the causing event. Absent for the same reason as {@link Change.eventId}. */
165
+ type?: string;
130
166
  }
131
167
  /**
132
168
  * Emit function narrowed to the developer's EventMap.
@@ -143,6 +179,58 @@ export interface Change<V = any> {
143
179
  *
144
180
  * @public
145
181
  */
182
+ /**
183
+ * What an `emit` resolves to once its effects have run.
184
+ *
185
+ * @remarks
186
+ * `emit` used to resolve to `void`, so a caller could not tell "the reducer applied my write"
187
+ * from "the reducer looked at my write and returned the state unchanged". On a single-writer
188
+ * store that distinction is academic; on a contended one it is a lost update the API could not
189
+ * report.
190
+ *
191
+ * Deliberately does **not** carry the changed paths. Building that list costs a string
192
+ * concatenation per changed path on every emit, and almost no caller reads it — the same reason
193
+ * change notifications are built lazily. Instrumentation already provides them to the observers
194
+ * that do want them.
195
+ *
196
+ * @public
197
+ */
198
+ export interface EmitResult {
199
+ /**
200
+ * The event was not vetoed by middleware.
201
+ *
202
+ * @remarks
203
+ * Unchanged in meaning, and deliberately not narrowed to "state changed" — an event-only store
204
+ * commits every event and writes nothing, by construction.
205
+ */
206
+ readonly committed: boolean;
207
+ /** A reducer actually changed state. */
208
+ readonly written: boolean;
209
+ /** Present when a reducer refused the write. See {@link Rejection}. */
210
+ readonly rejected?: Rejection;
211
+ }
212
+ /**
213
+ * Options for {@link StoreInstance.connect}.
214
+ *
215
+ * @public
216
+ */
217
+ export interface ConnectOptions {
218
+ /**
219
+ * Deliver the current value once, immediately, before any change arrives.
220
+ *
221
+ * @remarks
222
+ * A subscription otherwise starts at "from now on", so a subscriber's first render has to read
223
+ * the path separately — the same path, spelled twice, which is one place for them to drift.
224
+ *
225
+ * The synthetic change has `oldValue: undefined` and no `eventId`, `channel` or `type`: no
226
+ * event caused it, and claiming one would be a lie a subscriber could act on.
227
+ *
228
+ * For a wildcard pattern the "current value" of a match set is not a thing, so the slice root
229
+ * is delivered with `path: ""`. React's hooks do not need this at all — `useSyncExternalStore`
230
+ * already reads a snapshot on mount — so it is aimed at imperative subscribers.
231
+ */
232
+ readonly immediate?: boolean;
233
+ }
146
234
  /**
147
235
  * Per-emit options.
148
236
  *
@@ -195,7 +283,7 @@ export interface EmitOptions {
195
283
  */
196
284
  skipDedup?: boolean;
197
285
  }
198
- export type Emit<EM extends EventMapBase> = <C extends keyof EM & string, T extends keyof EM[C] & string>(channel: C, type: T, payload: EM[C][T], opts?: EmitOptions) => Promise<void>;
286
+ export type Emit<EM extends EventMapBase> = <C extends keyof EM & string, T extends keyof EM[C] & string>(channel: C, type: T, payload: EM[C][T], opts?: EmitOptions) => Promise<EmitResult>;
199
287
  /**
200
288
  * Basic unsubscribe handle.
201
289
  *
@@ -235,6 +323,15 @@ export interface InstrumentedEvent<EM extends EventMapBase = EventMapBase> {
235
323
  nextValues: Record<string, unknown>;
236
324
  /** Wall-clock milliseconds spent in the synchronous reduce phase for this event. */
237
325
  reduceTimeMs: number;
326
+ /**
327
+ * Present when a reducer refused the write, carrying its reason.
328
+ *
329
+ * @remarks
330
+ * Distinct from `committed: false`, which means middleware vetoed the event before any reducer
331
+ * saw it. This is a reducer having considered the write and declined it — the two look
332
+ * identical in state and are entirely different in cause.
333
+ */
334
+ rejected?: Rejection;
238
335
  }
239
336
  /**
240
337
  * Observer for {@link StoreInstance.instrument}. Called once per emitted event
@@ -432,7 +529,104 @@ export type StoreSpec<R extends string, S extends Record<R, any>, EM extends Eve
432
529
  * @param slice - Name of the slice whose reducer threw.
433
530
  */
434
531
  onReducerError?: (error: unknown, event: EventUnion<EM>, slice: string) => void;
532
+ /**
533
+ * Maximum causal depth of an event chain before the store refuses to extend it.
534
+ *
535
+ * @remarks
536
+ * An event emitted while handling another is one deeper than its cause. Two reducers wired to
537
+ * each other, or an effect that emits the event its own reducer answers, climb this without
538
+ * bound — and the reduce queue drains synchronously, so in a browser that is a frozen tab with
539
+ * no error and no stack, and on a server a pinned core.
540
+ *
541
+ * **On by default**, because the whole point is that the failure mode does not require
542
+ * configuration to avoid. The default is far past any legitimate chain: an event caused by an
543
+ * event caused by an event is normal, sixty-four deep is a bug. Raise it if an application
544
+ * genuinely nests deeper, or set `Infinity` to opt out entirely and own the consequences.
545
+ *
546
+ * Breaching does not throw — see {@link StoreSpec.onCascade}.
547
+ *
548
+ * @default 64
549
+ */
550
+ maxReduceDepth?: number;
551
+ /**
552
+ * Maximum number of events one synchronous drain will process before refusing more.
553
+ *
554
+ * @remarks
555
+ * A drain processes one root event plus every event emitted *while it runs* — so this counts a
556
+ * single causal burst, not application traffic. A plain loop is unaffected: `emit` drains to
557
+ * completion before it returns, so `for (const row of rows) store.emit(…)` is a thousand drains
558
+ * of one event each, never one drain of a thousand.
559
+ *
560
+ * **Off by default** because a wide burst is not by itself a bug. One `sync` event whose
561
+ * subscriber fans out to five hundred `upsert`s is a legitimate shape, and a default low enough
562
+ * to catch a runaway would refuse it. Depth is what separates a cascade from a fan-out — a
563
+ * fan-out is wide and shallow, a cascade is narrow and deep — which is why
564
+ * {@link StoreSpec.maxReduceDepth} carries the default and this does not.
565
+ *
566
+ * Set it when a store's bursts are known to be bounded and an unexpectedly wide one is itself
567
+ * the symptom worth catching.
568
+ *
569
+ * @default undefined (no limit)
570
+ */
571
+ maxTransitionsPerDrain?: number;
572
+ /**
573
+ * Called when a ceiling is breached, instead of throwing.
574
+ *
575
+ * @remarks
576
+ * The offending emit is refused and the chain stops there; everything already committed
577
+ * stands. It does not throw, because the throw would surface in whichever frame happened to be
578
+ * emitting — a subscriber, an effect, a middleware — which is the same species of
579
+ * hard-to-attribute failure the ceiling exists to prevent. A cascade is a wiring bug, and this
580
+ * is where the wiring gets named.
581
+ *
582
+ * @param info - Which ceiling, the event that would have extended the chain, and its causal
583
+ * chain of ids, newest last.
584
+ */
585
+ onCascade?: (info: CascadeInfo<EM>) => void;
586
+ /**
587
+ * Called when a reducer refuses a write by returning {@link Rejected}.
588
+ *
589
+ * @remarks
590
+ * The caller learns of its own refusal from the `emit` result; this is for everyone else —
591
+ * logging, metrics, alerting on a rate of rejected writes. Shaped as a callback rather than a
592
+ * subscription for the same reason {@link StoreSpec.onReducerError} is: it is a rare global
593
+ * signal, not something several independent parties register and unregister for.
594
+ *
595
+ * A refusal is a normal outcome, not an error. It means a reducer considered the write and
596
+ * declined it — a stale compare-and-swap, an unmet precondition — and the event is rejected
597
+ * whole, so no slice writes.
598
+ *
599
+ * @param rejection - The refusal and its reason.
600
+ * @param event - The event that was refused.
601
+ * @param slice - Name of the slice whose reducer refused.
602
+ */
603
+ onRejected?: (rejection: Rejection, event: EventUnion<EM>, slice: string) => void;
435
604
  };
605
+ /**
606
+ * What {@link StoreSpec.onCascade} receives when a ceiling is breached.
607
+ *
608
+ * @typeParam EM - Event map.
609
+ *
610
+ * @public
611
+ */
612
+ export interface CascadeInfo<EM extends EventMapBase = EventMapBase> {
613
+ /** Which ceiling was hit. */
614
+ readonly limit: "maxReduceDepth" | "maxTransitionsPerDrain";
615
+ /** The configured value that was exceeded. */
616
+ readonly limitValue: number;
617
+ /** The event that was refused — the one that would have extended the chain. */
618
+ readonly event: EventUnion<EM>;
619
+ /** Causal depth the refused event would have had. */
620
+ readonly depth: number;
621
+ /**
622
+ * Ids from the root of the chain to the refused event's parent, newest last.
623
+ *
624
+ * @remarks
625
+ * Bounded to the most recent entries: a cascade is long by definition, and the useful part is
626
+ * the cycle at the end rather than the thousand identical hops before it.
627
+ */
628
+ readonly chain: readonly string[];
629
+ }
436
630
  /**
437
631
  * Public Store surface.
438
632
  *
@@ -474,7 +668,16 @@ export interface StoreInstance<R extends string = string, S extends Record<R, an
474
668
  connect(spec: {
475
669
  reducer: R;
476
670
  property: string;
477
- }, handler: (change: Change) => void): Unsubscribe;
671
+ }, handler: (change: Change) => void, options?: ConnectOptions): Unsubscribe;
672
+ /**
673
+ * Sends a request and waits for the reply, correlating the two automatically.
674
+ *
675
+ * @remarks
676
+ * Awaitable for the terminal reply, async-iterable for progress. See the implementation on
677
+ * {@link Store.call} for the full contract — correlation, backpressure, timeouts, and why it
678
+ * is a local primitive rather than something that federates.
679
+ */
680
+ call<C extends keyof EM & string, T extends keyof EM[C] & string>(channel: C, type: T, payload: EM[C][T], opts: CallOptions<EM>): CallHandle<EventUnion<EM>, EventUnion<EM>>;
478
681
  /**
479
682
  * Convenience helper to register an **effect** filtered by a single `(channel, type)` pair.
480
683
  *
@@ -664,7 +867,8 @@ export interface StoreInstance<R extends string = string, S extends Record<R, an
664
867
  * Use `when` for event targeting (preferred). The `events` property is
665
868
  * kept for backward compatibility but `when` is recommended for new code.
666
869
  *
667
- * @example Using `when` (recommended)
870
+ * @example
871
+ * Using `when` (recommended)
668
872
  * ```ts
669
873
  * const counterSpec: ReducerSpec<{ value: number }, MyEM> = {
670
874
  * state: { value: 0 },
@@ -706,7 +910,7 @@ export interface ReducerSpec<S = any, EM extends EventMapBase = EventMapBase> {
706
910
  *
707
911
  * @public
708
912
  */
709
- export type ReducerFunction<S = any, EM extends EventMapBase = EventMapBase> = (state: S, event: EventUnion<EM>) => S;
913
+ export type ReducerFunction<S = any, EM extends EventMapBase = EventMapBase> = (state: S, event: EventUnion<EM>) => S | Rejection;
710
914
  /**
711
915
  * Effect specification (stateless async event consumer).
712
916
  *
@@ -719,7 +923,8 @@ export type ReducerFunction<S = any, EM extends EventMapBase = EventMapBase> = (
719
923
  * - Effects are keyed by event for O(1) lookup (no scanning).
720
924
  * - Use `when` for event targeting (preferred over `events`).
721
925
  *
722
- * @example Using `when` (recommended)
926
+ * @example
927
+ * Using `when` (recommended)
723
928
  * ```ts
724
929
  * const logEffect: EffectSpec<AppState, MyEM> = {
725
930
  * when: { keys: eventKeys<MyEM>()([['ui', 'increment']]) },
@@ -994,9 +1199,14 @@ export type EventFromWhen<EM extends EventMapBase, W extends When<EM>> = W exten
994
1199
  * type T3 = PathValue<S, 'todos'>; // Array<{ title: string; done: boolean }>
995
1200
  * ```
996
1201
  *
1202
+ * @remarks
1203
+ * The empty path resolves to `T` itself, matching what the code has always done: both the
1204
+ * store's internal path reader and the React one return the object unchanged for `""`. The type
1205
+ * used to say `never`, so a subscription to a root-value slice was typed as nothing at all.
1206
+ *
997
1207
  * @public
998
1208
  */
999
- export type PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : K extends `${number}` ? T extends readonly (infer E)[] ? PathValue<E, Rest> : never : never : P extends keyof T ? T[P] : P extends `${number}` ? T extends readonly (infer E)[] ? E : never : never;
1209
+ export type PathValue<T, P extends string> = P extends "" ? T : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : K extends `${number}` ? T extends readonly (infer E)[] ? PathValue<E, Rest> : never : never : P extends keyof T ? T[P] : P extends `${number}` ? T extends readonly (infer E)[] ? E : never : never;
1000
1210
  /**
1001
1211
  * Type discriminator for event consumers.
1002
1212
  *
@@ -1045,6 +1255,23 @@ export type DeepRO<T> = DeepReadonly<T>;
1045
1255
  * @public
1046
1256
  */
1047
1257
  export type Primitive = string | number | boolean | bigint | symbol | null | undefined | Date | RegExp;
1258
+ /**
1259
+ * A value with **no addressable interior**: its changes are reported at the slice root rather
1260
+ * than at a path beneath it.
1261
+ *
1262
+ * @remarks
1263
+ * The distinction the path types were missing. `Map` and `Set` keep their contents outside own
1264
+ * enumerable keys, so walking them with `keyof` yields the names of their *methods* — which is
1265
+ * how `"byId.get"` and `"byId.size"` came to be offered as subscribable paths, and why a slice
1266
+ * holding a plain number autocompleted `"toFixed"`. Neither ever notified anything, because
1267
+ * `detectChangedProps` reports such a value at its own path and never descends into it.
1268
+ *
1269
+ * This is the type-level counterpart of that runtime rule: what the diff reports at the root,
1270
+ * the types address at the root, with the empty path.
1271
+ *
1272
+ * @public
1273
+ */
1274
+ export type RootValue = Primitive | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>;
1048
1275
  /**
1049
1276
  * Compute dotted paths of T, including nested objects and arrays.
1050
1277
  *
@@ -1052,7 +1279,7 @@ export type Primitive = string | number | boolean | bigint | symbol | null | und
1052
1279
  *
1053
1280
  * @public
1054
1281
  */
1055
- export type Path<T> = T extends Primitive ? never : T extends readonly (infer U)[] ? `${number}` | (Path<U> extends never ? never : `${number}.${Path<U>}`) : {
1282
+ export type Path<T> = T extends RootValue ? never : T extends readonly (infer U)[] ? `${number}` | (Path<U> extends never ? never : `${number}.${Path<U>}`) : {
1056
1283
  [K in keyof T & string]: T[K] extends Primitive ? K : K | (Path<T[K]> extends never ? never : `${K}.${Path<T[K]>}`);
1057
1284
  }[keyof T & string];
1058
1285
  /**
@@ -1068,9 +1295,19 @@ export type WithGlob<T extends string> = T | `${string}*${string}`;
1068
1295
  *
1069
1296
  * @typeParam Slice - Slice state type.
1070
1297
  *
1298
+ * @remarks
1299
+ * A slice that **is** one value — a primitive, a `Map`, a `Set`, a `Date` — has no key to
1300
+ * address, and its only subscribable path is the empty one. Saying so is what makes
1301
+ * `{ reducer, property: "" }` type-check where it can actually fire, instead of falling through
1302
+ * to the untyped `property: string` overload and returning `unknown`.
1303
+ *
1304
+ * The conditional distributes over unions, which is why a nullable object slice gets both:
1305
+ * `Dotted<{ a: number } | null>` is `"" | "a"`. That is exactly right — such a slice really does
1306
+ * change at its root when it becomes `null`, and at `"a"` otherwise.
1307
+ *
1071
1308
  * @public
1072
1309
  */
1073
- export type Dotted<Slice> = (keyof Slice & string) | Path<Slice>;
1310
+ export type Dotted<Slice> = Slice extends RootValue ? "" : (keyof Slice & string) | Path<Slice>;
1074
1311
  /**
1075
1312
  * Deep readonly type: recursively makes all properties readonly.
1076
1313
  *
@@ -1098,11 +1335,39 @@ export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T ex
1098
1335
  *
1099
1336
  * - `'committed'`: Events that passed middleware and reached reducers (default)
1100
1337
  * - `'uncommitted'`: Events rejected by middleware
1338
+ * - `'written'`: Events that actually changed state
1101
1339
  * - `'all'`: Both committed and uncommitted events
1102
1340
  *
1341
+ * @remarks
1342
+ * `'committed'` means **not vetoed**, and always has. It fires for an event that passed
1343
+ * middleware whether or not any reducer wrote anything — including every event in a store with
1344
+ * no reducers at all, which is the shape a notification or analytics bus takes. Toasts,
1345
+ * animations and tracking depend on that, so it is not narrowed.
1346
+ *
1347
+ * `'written'` is the stricter fact, added rather than substituted: state changed. It fires
1348
+ * **after** the commit, so a subscriber reading `getState()` from it sees the new value — which
1349
+ * is what people tend to assume `'committed'` does.
1350
+ *
1351
+ * `'all'` deliberately stays `committed | uncommitted`. Folding `'written'` into it would hand
1352
+ * every existing `'all'` subscriber a second notification per written event and quietly double
1353
+ * their counts.
1354
+ *
1355
+ * @public
1356
+ */
1357
+ export type EventPhase = "committed" | "uncommitted" | "written" | "all";
1358
+ /**
1359
+ * The phases a handler is actually *told about*.
1360
+ *
1361
+ * @remarks
1362
+ * `'all'` is a subscription selector, not an outcome — nothing is ever delivered "in the all
1363
+ * phase". Naming the difference keeps the two from being conflated in a handler signature, which
1364
+ * is where they were previously spelled out by hand and drifted: adding `'written'` to
1365
+ * {@link EventPhase} left three copies in `@yoltra/react` still claiming a handler could only
1366
+ * ever see two phases, and the build failed on the mismatch.
1367
+ *
1103
1368
  * @public
1104
1369
  */
1105
- export type EventPhase = "committed" | "uncommitted" | "all";
1370
+ export type NotifiedPhase = Exclude<EventPhase, "all">;
1106
1371
  /**
1107
1372
  * Handler function for event subscriptions (receives full event union).
1108
1373
  *
@@ -1131,7 +1396,7 @@ export type EventPhase = "committed" | "uncommitted" | "all";
1131
1396
  *
1132
1397
  * @public
1133
1398
  */
1134
- export type EventSubscriptionHandler<S = any, EM extends EventMapBase = EventMapBase> = (event: EventUnion<EM>, getState: () => S, emit: Emit<EM>, phase: "committed" | "uncommitted") => void | Promise<void>;
1399
+ export type EventSubscriptionHandler<S = any, EM extends EventMapBase = EventMapBase> = (event: EventUnion<EM>, getState: () => S, emit: Emit<EM>, phase: NotifiedPhase) => void | Promise<void>;
1135
1400
  /**
1136
1401
  * Narrowed event subscription handler for specific `(channel, type)` pairs.
1137
1402
  * Provides better type inference when subscribing to a single event type.
@@ -1156,4 +1421,4 @@ export type EventSubscriptionHandler<S = any, EM extends EventMapBase = EventMap
1156
1421
  *
1157
1422
  * @public
1158
1423
  */
1159
- export type NarrowedEventHandler<S, EM extends EventMapBase, C extends keyof EM & string, T extends keyof EM[C] & string> = (event: Event<EM, C, T>, getState: () => S, emit: Emit<EM>, phase: "committed" | "uncommitted") => void | Promise<void>;
1424
+ export type NarrowedEventHandler<S, EM extends EventMapBase, C extends keyof EM & string, T extends keyof EM[C] & string> = (event: Event<EM, C, T>, getState: () => S, emit: Emit<EM>, phase: NotifiedPhase) => void | Promise<void>;
@@ -61,6 +61,12 @@
61
61
  *
62
62
  * @remarks
63
63
  * - If `oldState === newState` (same reference), returns `[]` immediately.
64
+ * - A change at the **root** — the values themselves differ and neither is a walkable object,
65
+ * as for a primitive, a `Map`/`Set`, or two `Date`s — is reported at the `path` given, which
66
+ * is `""` for the default root call. `[""]` therefore means *"the whole value changed"*, and
67
+ * is emphatically **not** the same as `[]`. Callers must not filter it out for falsiness:
68
+ * doing so is indistinguishable from "nothing changed", which is how a store slice holding a
69
+ * primitive once silently refused every update it was given.
64
70
  * - For objects, only **own enumerable** keys are compared (via `Object.keys`).
65
71
  * - Returned paths are **leaf paths** where a primitive/terminal difference was detected; for arrays,
66
72
  * a length change is treated as a leaf change at the array path.
@@ -1,4 +1,4 @@
1
- import { DeepReadonly } from '../types';
1
+ import { DeepReadonly } from '../types.js';
2
2
  /**
3
3
  * Deep-freezes a value **in place** and returns it as {@link DeepReadonly | `DeepReadonly<T>`}.
4
4
  *
@@ -1,2 +1,2 @@
1
- export * from './detectChangedProps';
2
- export * from './immutability';
1
+ export * from './detectChangedProps.js';
2
+ export * from './immutability.js';
@@ -0,0 +1,11 @@
1
+ /*!
2
+ * @yoltra/core v0.6.0
3
+ * (c) 2026 Manu Ramirez <@pixerael>
4
+ * License: MIT
5
+ * Homepage: https://yoltra.dev
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree
9
+ */
10
+ "use strict";var G=Object.defineProperty;var J=(c,e,t)=>e in c?G(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var p=(c,e,t)=>J(c,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class B{constructor(){p(this,"handlers",new Map)}on(e,t,s){let n=this.handlers.get(e);n||(n=new Map,this.handlers.set(e,n));let r=n.get(t);return r||(r=new Set,n.set(t,r)),r.add(s),()=>this.off(e,t,s)}off(e,t,s){const n=this.handlers.get(e);if(!n)return;const r=n.get(t);r&&(r.delete(s),r.size===0&&n.delete(t),n.size===0&&this.handlers.delete(e))}emit(e,t,s,n){const r=this.handlers.get(e);if(!r)return;const o=r.get(t);if(!(!o||o.size===0))for(const d of[...o])try{d(s,n)}catch(i){console.error("EventBus handler error:",i)}}clear(){this.handlers.clear()}}class H{constructor(){p(this,"handlers",new Map);p(this,"patternHandlers",new Map);p(this,"patternIndex",new Map)}on(e,t,s){const n=String(t);if(this.isPattern(n)){const r=n;this.patternHandlers.has(e)||this.patternHandlers.set(e,new Map);const o=this.patternHandlers.get(e);return o.has(r)||(o.set(r,[]),this.indexPattern(e,r)),o.get(r).push(s),()=>this.offPattern(e,r,s)}else{const r=this.normalizeTypeKey(n);this.handlers.has(e)||this.handlers.set(e,new Map);const o=this.handlers.get(e);return o.has(r)||o.set(r,[]),o.get(r).push(s),()=>this.offExactNormalized(e,r,s)}}off(e,t,s){const n=this.normalizeTypeKey(String(t));this.offExactNormalized(e,n,s)}offExactNormalized(e,t,s){const n=this.handlers.get(e);if(!n)return;const r=n.get(t);if(!r)return;const o=r.indexOf(s);o!==-1&&r.splice(o,1),r.length===0&&n.delete(t),n.size===0&&this.handlers.delete(e)}offPattern(e,t,s){const n=this.patternHandlers.get(e);if(!n)return;const r=n.get(t);if(!r)return;const o=r.indexOf(s);o!==-1&&r.splice(o,1),r.length===0&&(n.delete(t),this.unindexPattern(e,t)),n.size===0&&(this.patternHandlers.delete(e),this.patternIndex.delete(e))}emit(e,t,s){const n=String(t),r=this.normalizeTypeKey(n),o=this.handlers.get(e)?.get(r)??[],d=this.matchingPatternHandlers(e,n),i=new Set,a=l=>{for(const u of[...l])if(!i.has(u)){i.add(u);try{u(s)}catch(f){console.error(f);continue}}};a(o);for(const l of d)a(l)}emitWith(e,t,s){const n=String(t),r=this.normalizeTypeKey(n),o=this.handlers.get(e)?.get(r)??[],d=this.matchingPatternHandlers(e,n);if(o.length===0&&d.length===0)return;const i=s(),a=new Set,l=u=>{for(const f of[...u])if(!a.has(f)){a.add(f);try{f(i)}catch(m){console.error(m);continue}}};l(o);for(const u of d)l(u)}isPattern(e){return e.includes("*")}normalizeTypeKey(e){return e.replace(/^\./,"")}splitPath(e){return this.normalizeTypeKey(e).split(".").filter(Boolean)}indexPattern(e,t){let s=this.patternIndex.get(e);s===void 0&&(s={byHead:new Map,anyHead:[]},this.patternIndex.set(e,s));const n=this.splitPath(t),r={pattern:t,segments:n},o=n[0];if(o===void 0||o==="*"||o==="**"){s.anyHead.push(r);return}const d=s.byHead.get(o);d===void 0?s.byHead.set(o,[r]):d.push(r)}unindexPattern(e,t){const s=this.patternIndex.get(e);if(s===void 0)return;const n=this.splitPath(t)[0],r=n===void 0||n==="*"||n==="**"?s.anyHead:s.byHead.get(n);if(r===void 0)return;const o=r.findIndex(d=>d.pattern===t);o!==-1&&r.splice(o,1),r.length===0&&r!==s.anyHead&&n!==void 0&&s.byHead.delete(n)}matchingPatternHandlers(e,t){const s=this.patternHandlers.get(e),n=this.patternIndex.get(e);if(s===void 0||s.size===0||n===void 0)return[];const r=this.splitPath(t),o=[],d=a=>{for(const l of a){if(!this.matchSegments(l.segments,r))continue;const u=s.get(l.pattern);u!==void 0&&o.push(u)}},i=r[0];if(i!==void 0){const a=n.byHead.get(i);a!==void 0&&d(a)}return d(n.anyHead),o}matchSegments(e,t){let s=0,n=0,r=-1,o=0;for(;n<t.length;)if(s<e.length&&(e[s]==="*"||e[s]===t[n]))s++,n++;else if(s<e.length&&e[s]==="**")r=s,o=n,s++;else if(r!==-1)s=r+1,n=++o;else return!1;for(;s<e.length&&e[s]==="**";)s++;return s===e.length}clear(){this.handlers.clear(),this.patternHandlers.clear(),this.patternIndex.clear()}__introspect(){const e=[];for(const[t,s]of this.handlers)for(const[n,r]of s)r.length>0&&e.push({channel:t,type:n,count:r.length});for(const[t,s]of this.patternHandlers)for(const[n,r]of s)r.length>0&&e.push({channel:t,type:n,count:r.length});return e}}class W{constructor(e){p(this,"_reduce");this._reduce=e}reduce(e,t){return this._reduce(e,t)}}const D=new Set;function x(c,e){const t=c?`${c}.${e}`:e;D.has(t)||(D.add(t),console.warn(`[yoltra] State key "${e}"${c?` under "${c}"`:""} contains a dot. Paths are dotted, so this key is indistinguishable from nested objects of the same name: a subscription to "${t}" may match the wrong value, and DevTools patches for it will address the wrong node. Rename the key, or nest it.`))}function T(c,e,t="",s=new Map){const n=[];return R(c,e,t,s,n),n}function R(c,e,t,s,n){if(c===e)return;if(typeof c!="object"||typeof e!="object"||c===null||e===null){if(typeof c=="number"&&Number.isNaN(c)&&Number.isNaN(e))return;n.push(t);return}if(c instanceof Date&&e instanceof Date){c.getTime()!==e.getTime()&&n.push(t);return}if(c instanceof RegExp&&e instanceof RegExp){(c.source!==e.source||e.flags!==c.flags)&&n.push(t);return}if(c instanceof Map||e instanceof Map){n.push(t);return}if(c instanceof Set||e instanceof Set){n.push(t);return}const r=c,o=e,d=s.get(r);if(d?.has(o))return;const i=d??new Set;i.add(o),d||s.set(r,i);try{const a=Array.isArray(c),l=Array.isArray(e);if(a!==l){n.push(t);return}if(a){const h=c,y=e;h.length!==y.length&&t&&n.push(t);const g=Math.min(h.length,y.length);for(let E=0;E<g;E++)h[E]!==y[E]&&R(h[E],y[E],t?`${t}.${E}`:`${E}`,s,n);for(let E=g;E<Math.max(h.length,y.length);E++)n.push(t?`${t}.${E}`:`${E}`);return}const u=Object.keys(c),f=Object.keys(e);if(u.length===0&&f.length===0){n.push(t);return}let m=u.length===f.length;if(m){for(let h=0;h<f.length;h++)if(!Object.prototype.hasOwnProperty.call(c,f[h])){m=!1;break}}if(m){for(const h of f)c[h]!==e[h]&&(process.env.NODE_ENV!=="production"&&h.includes(".")&&x(t,h),R(c[h],e[h],t?`${t}.${h}`:h,s,n));return}for(const h of f){const y=Object.prototype.hasOwnProperty.call(c,h);if(y&&c[h]===e[h])continue;process.env.NODE_ENV!=="production"&&h.includes(".")&&x(t,h);const g=t?`${t}.${h}`:h;if(!y){n.push(g);continue}R(c[h],e[h],g,s,n)}for(const h of u)Object.prototype.hasOwnProperty.call(e,h)||(process.env.NODE_ENV!=="production"&&h.includes(".")&&x(t,h),n.push(t?`${t}.${h}`:h))}finally{i.delete(o),i.size===0&&s.delete(r)}}function k(c,e=new WeakSet,t){if(c===null||typeof c!="object"||e.has(c)||(t!==void 0&&c===t.watch&&t.onFound(),Object.isFrozen(c)))return c;if(e.add(c),Array.isArray(c)){const s=c;for(let n=0;n<s.length;n++)s[n]=k(s[n],e,t);return Object.freeze(s)}for(const s of Object.getOwnPropertyNames(c)){const n=Object.getOwnPropertyDescriptor(c,s);!n||!("value"in n)||(c[s]=k(c[s],e,t))}for(const s of Object.getOwnPropertySymbols(c)){const n=Object.getOwnPropertyDescriptor(c,s);!n||!("value"in n)||(c[s]=k(c[s],e,t))}return Object.freeze(c)}const K=Symbol.for("yoltra.rejected");function q(c){return{[K]:!0,reason:c}}function F(c){return typeof c=="object"&&c!==null&&c[K]===!0}class Y{constructor(e){p(this,"highWaterMark");p(this,"buffer",[]);p(this,"takers",[]);p(this,"putters",[]);p(this,"consuming",!1);p(this,"ended",!1);p(this,"closed",!1);p(this,"dropped",0);this.highWaterMark=e}get droppedCount(){return this.dropped}beginConsuming(){this.consuming=!0}put(e){if(this.closed||this.ended)return Promise.resolve();const t=this.takers.shift();return t!==void 0?(t({value:e,done:!1}),Promise.resolve()):this.buffer.length<this.highWaterMark?(this.buffer.push(e),Promise.resolve()):this.consuming?new Promise(s=>{this.putters.push({item:e,release:s})}):(this.dropped++,Promise.resolve())}take(){this.consuming=!0;const e=this.buffer.shift();if(e!==void 0){const s=this.putters.shift();return s!==void 0&&(this.buffer.push(s.item),s.release()),Promise.resolve({value:e,done:!1})}const t=this.putters.shift();return t!==void 0?(t.release(),Promise.resolve({value:t.item,done:!1})):this.closed||this.ended?Promise.resolve({value:void 0,done:!0}):new Promise(s=>{this.takers.push(s)})}end(){if(this.ended||this.closed)return;this.ended=!0;let e=this.putters.shift();for(;e!==void 0;)this.buffer.push(e.item),e.release(),e=this.putters.shift();let t=this.takers.shift();for(;t!==void 0;){const s=this.buffer.shift();t(s!==void 0?{value:s,done:!1}:{value:void 0,done:!0}),t=this.takers.shift()}}close(){if(this.closed)return;this.closed=!0,this.buffer.length=0;let e=this.takers.shift();for(;e!==void 0;)e({value:void 0,done:!0}),e=this.takers.shift();let t=this.putters.shift();for(;t!==void 0;)t.release(),t=this.putters.shift()}}class U extends Error{constructor(t,s,n){super(`[yoltra] call to "${t}/${s}" saw no correlated reply for ${n}ms. The timeout is idle rather than total, so this means the responder went quiet, not that it was slow. Check that something handles "${t}/${s}" and that its reply is emitted through the \`emit\` it was handed — a reply emitted from an unrelated context carries no causal link, and needs an explicit correlationId instead.`);p(this,"channel");p(this,"type");p(this,"idleMs");this.name="CallTimeoutError",this.channel=t,this.type=s,this.idleMs=n}}class I extends Error{constructor(e){super(`[yoltra] call aborted: ${e}`),this.name="CallAbortedError"}}function X(c){const[e,t]=c;if(t===void 0)return{channel:e,isTerminal:()=>!0};if(typeof t=="string")return{channel:e,isTerminal:n=>n===t};const s=new Set(t);return{channel:e,isTerminal:n=>s.has(n)}}function Z(c,e,t){return c.parentId===e?!0:t===void 0?!1:c.meta?.correlationId===t}function ee(c,e){try{return structuredClone(e)}catch(t){throw new Error(`[yoltra] Initial state for slice "${String(c)}" could not be copied: ${t instanceof Error?t.message:String(t)}. State must be structured-cloneable — functions, class instances and DOM nodes are not. Keep behaviour out of state and store plain data.`)}}function O(c,e){return process.env.NODE_ENV==="production"?c:k(c,new WeakSet,e)}const z=100,te=64,A=16,ne=3e4,se=16,$=Object.freeze({committed:!1,written:!1}),re=Object.freeze({committed:!0,written:!1}),ie=Object.freeze({committed:!0,written:!0}),j=()=>typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now();class P{constructor(e){p(this,"name");p(this,"middleware");p(this,"reducers");p(this,"state");p(this,"reducerBus");p(this,"connectorBus");p(this,"listeners",new Set);p(this,"effects",new Map);p(this,"patternEffects",new Set);p(this,"committedEventSubscribers",new Map);p(this,"uncommittedEventSubscribers",new Map);p(this,"writtenEventSubscribers",new Map);p(this,"allEventSubscribers",new Map);p(this,"sliceUnsubs",new Map);p(this,"patternReducers",new Map);p(this,"replayEnabled");p(this,"idFactory");p(this,"onEffectError");p(this,"onReducerError");p(this,"warnedPayloadAliases",new Set);p(this,"reduceQueue",[]);p(this,"isReducing",!1);p(this,"currentEvent",null);p(this,"transitionsThisDrain",0);p(this,"maxReduceDepth");p(this,"maxTransitionsPerDrain");p(this,"onCascade");p(this,"onRejected");p(this,"instrumentObservers",new Set);p(this,"changedPathSink",null);p(this,"stagingSink",null);p(this,"stagedRejection",null);p(this,"stagedRejectedBy","");p(this,"inFlightEffects",0);p(this,"processedEvents",new Map);p(this,"dedupCount",0);p(this,"effectMeta",new WeakMap);p(this,"dedupConfig");p(this,"eventCleanupTimer",null);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.maxReduceDepth=e.maxReduceDepth??te,this.maxTransitionsPerDrain=e.maxTransitionsPerDrain??1/0,this.onCascade=e.onCascade,this.onRejected=e.onRejected,this.dedupConfig={windowMs:e.dedupWindowMs??0,maxCacheSize:1e3},Object.entries(e.reducer).forEach(([t,s])=>{this.mountSlice(t,s,{preserveState:!1})}),e.effects?.length)for(const t of e.effects)this.registerEffect(t);this.dispose=this.dispose.bind(this),this.notifyEffects=this.notifyEffects.bind(this),this.__applyExternalState=this.__applyExternalState.bind(this),this.__replayEvents=this.__replayEvents.bind(this),this.__devtoolsIntrospect=this.__devtoolsIntrospect.bind(this),this.mountSlice=this.mountSlice.bind(this),this.unmountSlice=this.unmountSlice.bind(this),this.getAtPath=this.getAtPath.bind(this),this.emit=this.emit.bind(this),this.subscribe=this.subscribe.bind(this),this.connect=this.connect.bind(this),this.onEffect=this.onEffect.bind(this),this.onEvent=this.onEvent.bind(this),this.getState=this.getState.bind(this),this.registerEffect=this.registerEffect.bind(this),this.registerMiddleware=this.registerMiddleware.bind(this),this.registerReducer=this.registerReducer.bind(this),this.replaceMiddleware=this.replaceMiddleware.bind(this),this.replaceEffects=this.replaceEffects.bind(this),this.replaceReducers=this.replaceReducers.bind(this),this.hotReplace=this.hotReplace.bind(this)}dispose(){this.eventCleanupTimer&&(clearInterval(this.eventCleanupTimer),this.eventCleanupTimer=null),this.processedEvents.clear(),this.effects.clear(),this.patternEffects.clear(),this.effectMeta=new WeakMap,this.warnedPayloadAliases.clear(),this.listeners.clear(),this.committedEventSubscribers.clear(),this.uncommittedEventSubscribers.clear(),this.writtenEventSubscribers.clear(),this.allEventSubscribers.clear(),this.instrumentObservers.clear(),this.connectorBus.clear(),this.reducerBus.clear(),this.patternReducers.clear(),this.sliceUnsubs.clear(),this.changedPathSink=null}fingerprint(e,t,s){const n=`${e}::${t}`;try{if(s==null)return`${n}::null`;if(typeof s!="object")return`${n}::${String(s)}`;const r=JSON.stringify(s);return`${n}::${r}`}catch{return`${n}::${Date.now()}::${Math.random()}`}}shouldDedupe(e,t){const s=Date.now(),n=this.processedEvents.get(e);return n!==void 0&&s-n<t?(this.dedupCount++,!0):(this.processedEvents.set(e,s),this.ensureCleanupTimer(),this.processedEvents.size>this.dedupConfig.maxCacheSize&&this.pruneProcessedEvents(s),!1)}ensureCleanupTimer(){this.eventCleanupTimer===null&&(this.eventCleanupTimer=setInterval(()=>{this.pruneProcessedEvents(Date.now())},5e3),this.eventCleanupTimer.unref?.())}pruneProcessedEvents(e){const t=Math.max(this.dedupConfig.windowMs,z),s=e-t*2;for(const[n,r]of this.processedEvents)r<s&&this.processedEvents.delete(n);this.processedEvents.size===0&&this.eventCleanupTimer!==null&&(clearInterval(this.eventCleanupTimer),this.eventCleanupTimer=null)}reportCascade(e,t,s,n,r){console.error(`[yoltra] Cascade stopped: "${s.channel}/${s.type}" would exceed ${e} (${t}). This event was refused and the chain ends here. A chain this long is almost always two consumers emitting into each other — check what reacts to "${s.channel}/${s.type}" and what that emits in turn.`+(r.length>0?` Recent causal chain: ${r.join(" → ")} → (refused).`:""));try{this.onCascade?.({limit:e,limitValue:t,event:s,depth:n,chain:r})}catch(o){console.error("onCascade handler error:",o)}}matchesWhen(e,t){return!e||"any"in e&&e.any===!0?!0:"keys"in e?e.keys.some(([s,n])=>t.channel===s&&t.type===n):"channel"in e?t.channel===e.channel:"channels"in e?e.channels.includes(t.channel):!1}getMiddlewareFunction(e){return typeof e=="function"?e:e.middleware}getMiddlewareWhen(e){if(typeof e!="function")return e.when}async notifyEffects(e){const t=this.scopedEmit(e),s=`${String(e.channel)}::${String(e.type)}`,n=this.effects.get(s);if(n&&n.size>0)for(const r of[...n])try{await r(e,this.getState,t)}catch(o){console.error("Effect error:",o),this.onEffectError?.(o,e)}for(const{effect:r,when:o}of this.patternEffects)if(this.matchesWhen(o,e))try{await r(e,this.getState,t)}catch(d){console.error("Effect error:",d),this.onEffectError?.(d,e)}}scopedEmit(e){const t={id:e.id,depth:e.depth??0,chain:[...this.currentEvent?.chain??[],e.id].slice(-A)};return((s,n,r,o)=>this.emitCaused(t,s,n,r,o))}notifyEventSubscribers(e,t){const s=`${String(e.channel)}::${String(e.type)}`,r=(t==="committed"?this.committedEventSubscribers:t==="written"?this.writtenEventSubscribers:this.uncommittedEventSubscribers).get(s);if(r?.size)for(const d of[...r])this.invokeEventSubscriber(d,e,t);if(t==="written")return;const o=this.allEventSubscribers.get(s);if(o?.size)for(const d of[...o])this.invokeEventSubscriber(d,e,t)}invokeEventSubscriber(e,t,s){try{const n=e(t,this.getState,this.emit,s);n&&typeof n.then=="function"&&n.catch(r=>console.error("Event subscription error:",r))}catch(n){console.error("Event subscription error:",n)}}stageSliceGuarded(e,t,s){try{return this.stageSlice(e,t,s)}catch(n){return console.error(`Reducer error in slice "${e}":`,n),this.onReducerError?.(n,t,e),null}}stageSlice(e,t,s){const n=this.state[e],r=this.reducers[e].reduce(n,t);if(F(r))return r;if(n===r)return null;const o=T(n,r);if(o.length===0)return null;const d=t.payload,i=process.env.NODE_ENV!=="production"&&d!==null&&typeof d=="object"?{watch:d,onFound:()=>{const a=`${e}:${t.channel}:${t.type}`;this.warnedPayloadAliases.has(a)||(this.warnedPayloadAliases.add(a),console.warn(`[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.`))}}:void 0;return s.push({name:e,prev:n,frozen:O(r,i),leafPaths:o}),null}commitStaged(e,t){if(e.length===0)return!1;const s={...this.state};for(const n of e)s[n.name]=n.frozen;if(this.state=s,this.changedPathSink)for(const n of e)for(const r of n.leafPaths)this.changedPathSink.push(r?`${n.name}.${r}`:n.name);for(const n of e){const r=new Set;for(const o of n.leafPaths){if(o===""){r.add("");continue}for(const d of P.buildAncestorPaths(o))r.add(d)}for(const o of r)this.connectorBus.emitWith(n.name,o,()=>({oldValue:this.getAtPath(n.prev,o),newValue:this.getAtPath(n.frozen,o),path:o,eventId:t.id,channel:t.channel,type:t.type}))}return!0}__devtoolsIntrospect(){const e=Object.keys(this.reducers).map(d=>{const i=this.patternReducers.get(d);return{name:d,when:i}}),t=[];for(const[d,i]of this.effects){if(i.size===0)continue;const[a,l]=d.split("::");for(const u of i){const f=this.effectMeta.get(u);t.push({channel:a,type:l,name:f?.name,description:f?.description})}}for(const d of this.patternEffects){const i=this.effectMeta.get(d.effect);t.push({channel:"*",type:"*",name:i?.name,description:i?.description})}const s=[];for(const d of this.middleware)typeof d=="function"?s.push({name:d.name||void 0}):s.push({name:d.meta?.name,description:d.meta?.description,when:d.when});const n=[];for(const d of this.connectorBus.__introspect())for(let i=0;i<d.count;i++)n.push({reducer:d.channel,property:d.type});const r=[];for(const[d,i]of this.committedEventSubscribers){if(i.size===0)continue;const[a,l]=d.split("::");for(let u=0;u<i.size;u++)r.push({channel:a,type:l,phase:"committed"})}for(const[d,i]of this.uncommittedEventSubscribers){if(i.size===0)continue;const[a,l]=d.split("::");for(let u=0;u<i.size;u++)r.push({channel:a,type:l,phase:"uncommitted"})}for(const[d,i]of this.allEventSubscribers){if(i.size===0)continue;const[a,l]=d.split("::");for(let u=0;u<i.size;u++)r.push({channel:a,type:l,phase:"all"})}const o=this.listeners.size;return{reducers:e,effects:t,middleware:s,atomic:n,event:r,coarse:o,dedupHits:this.dedupCount,queueDepth:this.reduceQueue.length+this.inFlightEffects}}__applyExternalState(e){if(!this.replayEnabled)throw new Error("[yoltra] External state apply (time-travel) is disabled. Enable it with createStore({ devtools: { allowReplay: true } })");const t=this.state,s=e,n={...this.state};let r=!1;Object.keys(this.reducers).forEach(o=>{const d=t?.[o],i=s?.[o];if(i===void 0){process.env.NODE_ENV!=="production"&&console.warn(`[yoltra] External state is missing slice "${String(o)}"; retaining its current value. Time-travel snapshots should contain all slices.`);return}if(d===i)return;const a=O(i);n[o]=a,r=!0;const l=T(d,i);if(l.length===0)return;const u=new Set;for(const f of l){if(f===""){u.add("");continue}for(const m of P.buildAncestorPaths(f))u.add(m)}for(const f of u){const m=this.getAtPath(d,f),h=this.getAtPath(a,f);this.connectorBus.emit(o,f,{oldValue:m,newValue:h,path:f})}}),r&&(this.state=n),r&&this.listeners.forEach(o=>o())}__replayEvents(e,t){if(!this.replayEnabled)throw new Error("[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })");this.__applyExternalState(e);for(const s of t){const n=s,r=[];this.stagingSink=r;let o=null;try{this.reducerBus.emit(n.channel,n.type,n.payload,n),o=this.stagedRejection;for(const[i,a]of this.patternReducers){if(o!==null)break;if(this.matchesWhen(a,n)){const l=this.stageSliceGuarded(i,n,r);l!==null&&(o=l)}}}finally{this.stagingSink=null,this.stagedRejection=null,this.stagedRejectedBy=""}const d=o===null&&this.commitStaged(r,n);this.notifyEventSubscribers(n,"committed"),d&&(this.notifyEventSubscribers(n,"written"),this.listeners.forEach(i=>i()))}}async emit(e,t,s,n){return this.emitCaused(null,e,t,s,n)}async emitCaused(e,t,s,n,r){const o=r?.dedupKey,d=this.dedupConfig.windowMs;if(r?.skipDedup!==!0&&(d>0||o!==void 0)){const m=o!==void 0&&d<=0?z:d,h=o!==void 0?`${t}::${s}::#${o}`:this.fingerprint(t,s,n);if(this.shouldDedupe(h,m))return $}const i=r?.id??this.idFactory(),a=this.currentEvent??e,l=a===null?0:a.depth+1;if(a!==null&&l>this.maxReduceDepth)return this.reportCascade("maxReduceDepth",this.maxReduceDepth,{channel:t,type:s,payload:n,id:i,...r?.meta!==void 0?{meta:r.meta}:{},parentId:a.id,depth:l},l,a.chain),$;let u;const f=new Promise(m=>{u=m});return this.reduceQueue.push({channel:t,type:s,payload:n,id:i,meta:r?.meta,resolve:u,...a!==null?{parentId:a.id,depth:l,chain:a.chain}:{}}),this.drainReduce(),f}drainReduce(){if(!this.isReducing){this.isReducing=!0,this.transitionsThisDrain=0;try{for(;this.reduceQueue.length>0;){const e=this.reduceQueue.shift(),{channel:t,type:s,payload:n,id:r,meta:o,resolve:d,parentId:i,depth:a,chain:l}=e,u={channel:t,type:s,payload:n,id:r,...o!==void 0?{meta:o}:{},...i!==void 0?{parentId:i,depth:a}:{}};if(i!==void 0&&++this.transitionsThisDrain>this.maxTransitionsPerDrain){this.reportCascade("maxTransitionsPerDrain",this.maxTransitionsPerDrain,u,a,l),d($);continue}this.currentEvent={id:r,depth:a??0,chain:[...l??[],r].slice(-A)};const f=this.instrumentObservers.size>0,m=f?this.state:void 0,h=f?[]:void 0;h!==void 0&&(this.changedPathSink=h);const y=f?j():0;let g=$;try{g=this.applyEventSync(u)}catch(E){console.error("Emit reduce error:",E)}finally{f&&(this.changedPathSink=null),this.currentEvent=null}f&&this.emitInstrumentation(u,g,h??[],m,j()-y),this.runEventEffects(u,g,d)}}finally{this.isReducing=!1}}}applyEventSync(e){for(const o of this.middleware){const d=this.getMiddlewareWhen(o);if(!this.matchesWhen(d,e))continue;const i=this.getMiddlewareFunction(o);let a;try{a=i(this.state,e,this.emit),process.env.NODE_ENV!=="production"&&typeof a?.then=="function"&&console.error(`[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.`)}catch(l){console.error("Middleware error:",l),a=!1}if(!a)return this.notifyEventSubscribers(e,"uncommitted"),$}const t=[];this.stagingSink=t;let s=null,n="";try{this.reducerBus.emit(e.channel,e.type,e.payload,e),s=this.stagedRejection,n=this.stagedRejectedBy;for(const[o,d]of this.patternReducers){if(s!==null)break;if(this.matchesWhen(d,e)){const i=this.stageSliceGuarded(o,e,t);i!==null&&(s=i,n=o)}}}finally{this.stagingSink=null,this.stagedRejection=null,this.stagedRejectedBy=""}if(s!==null)return this.onRejected?.(s,e,n),this.notifyEventSubscribers(e,"committed"),{committed:!0,written:!1,rejected:s};const r=this.commitStaged(t,e);return this.notifyEventSubscribers(e,"committed"),r&&(this.notifyEventSubscribers(e,"written"),this.listeners.forEach(o=>o())),r?ie:re}async runEventEffects(e,t,s){this.inFlightEffects++;try{t.committed&&await this.notifyEffects(e)}catch(n){console.error("Effect error:",n)}finally{this.inFlightEffects--,s(t)}}instrument(e){return this.instrumentObservers.add(e),()=>{this.instrumentObservers.delete(e)}}emitInstrumentation(e,t,s,n,r){const o={},d={};for(const a of s)o[a]=this.getAtPath(n,a),d[a]=this.getAtPath(this.state,a);const i={event:{id:e.id,channel:e.channel,type:e.type,payload:e.payload,...e.meta!==void 0?{meta:e.meta}:{}},committed:t.committed,changedPaths:s,prevValues:o,nextValues:d,reduceTimeMs:r,...t.rejected!==void 0?{rejected:t.rejected}:{}};for(const a of[...this.instrumentObservers])try{a(i)}catch(l){console.error("Instrumentation observer error:",l)}}connect(e,t,s){const n=this.connectorBus.on(e.reducer,e.property,t);if(s?.immediate===!0){const r=this.state[e.reducer],o=e.property.includes("*")?"":e.property;t({oldValue:void 0,newValue:this.getAtPath(r,o),path:o})}return n}onEvent(e,t,s,n="committed"){const r=`${e}::${String(t)}`,o=n==="committed"?this.committedEventSubscribers:n==="uncommitted"?this.uncommittedEventSubscribers:n==="written"?this.writtenEventSubscribers:this.allEventSubscribers;return o.has(r)||o.set(r,new Set),o.get(r).add(s),()=>{const d=o.get(r);d&&(d.delete(s),d.size===0&&o.delete(r))}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}getState(){return this.state}registerMiddleware(e){return this.middleware.push(e),()=>{const t=this.middleware.indexOf(e);t!==-1&&this.middleware.splice(t,1)}}registerReducer(e,t){if(Object.prototype.hasOwnProperty.call(this.reducers,e))throw new Error(`Reducer ${e} already exists`);return this.mountSlice(e,t,{preserveState:!1}),this.listeners.forEach(s=>s()),()=>{this.unmountSlice(e,{deleteState:!0}),this.listeners.forEach(s=>s())}}call(e,t,s,n){const{channel:r,isTerminal:o}=X(n.reply),d=n.timeoutMs??ne,i=new Y(n.highWaterMark??se),a=this.idFactory();let l,u,f=!1;const m=new Promise((b,S)=>{l=b,u=S});m.catch(()=>{});let h=null,y=null;const g=(b,S=!1)=>{f||(f=!0,h!==null&&clearTimeout(h),h=null,y?.(),y=null,S?i.end():i.close(),n.signal?.removeEventListener("abort",E),b())};function E(){g(()=>u(new I(String(n.signal?.reason??"signal aborted"))))}const M=()=>{h!==null&&clearTimeout(h),h=setTimeout(()=>{g(()=>u(new U(e,t,d)))},d),h.unref?.()};return y=this.registerEffect({when:{channel:r},effect:async b=>{if(!f&&Z(b,a,n.correlationId)){if(M(),o(String(b.type))){g(()=>l(b),!0);return}await i.put(b)}}}),n.signal!==void 0&&(n.signal.aborted?E():n.signal.addEventListener("abort",E,{once:!0})),M(),this.emit(e,t,s,{id:a,...n.correlationId!==void 0?{meta:{correlationId:n.correlationId}}:{}}),{then:(b,S)=>m.then(b,S),catch:b=>m.catch(b),finally:b=>m.finally(b),get dropped(){return i.droppedCount},cancel:(b="cancelled")=>{g(()=>u(new I(b)))},[Symbol.asyncIterator]:()=>(i.beginConsuming(),{next:()=>i.take(),return:async()=>(i.close(),{value:void 0,done:!0})})}}registerEffect(e){const{effect:t,meta:s,when:n}=e,r=[];if(s&&this.effectMeta.set(t,s),n&&("any"in n&&n.any===!0||"channel"in n||"channels"in n)){const i={effect:t,when:n};return this.patternEffects.add(i),()=>{this.patternEffects.delete(i)}}const d=this.normalizeEventKeys(e);if(d.length===0&&!n){const i={effect:t,when:{any:!0}};return this.patternEffects.add(i),()=>{this.patternEffects.delete(i)}}for(const[i,a]of d){const l=`${String(i)}::${String(a)}`;this.effects.has(l)||this.effects.set(l,new Set),this.effects.get(l).add(t),r.push(()=>{const u=this.effects.get(l);u&&(u.delete(t),u.size===0&&this.effects.delete(l))})}return()=>{for(const i of r)i()}}onEffect(e,t,s){const n=async(r,o,d)=>{if(r.channel!==e||r.type!==t)return;const i=r;return s(i.payload,o,d,i)};return this.registerEffect({when:{keys:[[e,t]]},effect:n})}replaceMiddleware(e){this.middleware.length=0;for(const t of e)this.middleware.push(t)}replaceEffects(e){this.effects.clear(),this.patternEffects.clear();for(const t of e)this.registerEffect(t)}replaceReducers(e,t={}){const s=t.preserveState!==!1,n=new Set(Object.keys(this.reducers)),r=Object.entries(e),o=new Set(r.map(([d])=>d));for(const d of n)o.has(d)||this.unmountSlice(d,{deleteState:!0});for(const[d,i]of r)n.has(d)?(this.unmountSlice(d,{deleteState:!1}),this.mountSlice(d,i,{preserveState:s})):this.mountSlice(d,i,{preserveState:!1})}hotReplace(e){e.middleware&&this.replaceMiddleware(e.middleware),e.effects&&this.replaceEffects(e.effects),e.reducer&&this.replaceReducers(e.reducer,{preserveState:e.preserveState})}mountSlice(e,t,s){const n=e,{reducer:r,state:o,when:d}=t;if(this.reducers[e]=new W(r),(!s.preserveState||this.state[n]===void 0)&&(this.state={...this.state,[n]:O(ee(n,o))}),d&&("any"in d&&d.any===!0||"channel"in d||"channels"in d)){this.patternReducers.set(e,d),this.sliceUnsubs.set(n,[]);return}const a=this.normalizeEventKeys(t);if(a.length===0&&!d){this.patternReducers.set(e,{any:!0}),this.sliceUnsubs.set(n,[]);return}const l=[];for(const[u,f]of a){const m=this.reducerBus.on(u,f,(h,y)=>{const g=y??{channel:u,type:f,payload:h,id:this.idFactory()};if(this.stagingSink===null)return;const E=this.stageSliceGuarded(e,g,this.stagingSink);E!==null&&this.stagedRejection===null&&(this.stagedRejection=E,this.stagedRejectedBy=e)});l.push(m)}this.sliceUnsubs.set(n,l)}unmountSlice(e,t){const s=e;this.patternReducers.delete(e);const n=this.sliceUnsubs.get(s);if(n){for(const r of n)try{r()}catch(o){console.error(`[Store error]: ${o}`)}this.sliceUnsubs.delete(s)}if(delete this.reducers[e],t.deleteState){const{[s]:r,...o}=this.state;this.state=o}}normalizeEventKeys(e){if(e.when){const t=e.when;if("keys"in t)return t.keys}return[]}getAtPath(e,t){if(!t)return e;const n=(t[0]==="."?t.slice(1):t).split(".");let r=e;for(const o of n){if(r==null)return;r=r[o]}return r}static buildAncestorPaths(e){if(!e)return[];const s=(e[0]==="."?e.slice(1):e).split("."),n=[];for(let r=0;r<s.length;r++)n.push(s.slice(0,r+1).join("."));return n}}function oe(c){return new P({...c,reducer:c.reducer??{},middleware:c.middleware??[],effects:c.effects??[]})}const ce=c=>(e,t)=>t.map(s=>[e,s]),ae=()=>c=>c,_=new Set;function de(c){const e=String(c);_.has(e)||(_.add(e),console.warn(`[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.`))}function le(c,e){if(c.length!==e.length)return e;for(let t=0;t<c.length;t++)if(c[t]!==e[t])return e;return c}function ue(c={}){const e=c.selectId??(i=>i.id),{sortComparer:t}=c,s=(i,a)=>{if(t===void 0)return a;const l=[...a].sort((u,f)=>{const m=i.entities[u],h=i.entities[f];return m===void 0||h===void 0?0:t(m,h)});return le(a,l)},n=(i,a,l)=>{const u={...i,entities:a,ids:l};return{...u,ids:s(u,l)}},r=(i,a,l)=>{let u=null,f=null;for(const m of a){const h=e(m);process.env.NODE_ENV!=="production"&&String(h).includes(".")&&de(h);const y=(u??i.entities)[h];if(y!==void 0&&l==="add")continue;const g=y!==void 0&&l==="upsert"?{...y,...m}:m;u??(u={...i.entities}),u[h]=g,y===void 0&&(f??(f=[...i.ids]),f.push(h))}return u===null?i:n(i,u,f??i.ids)},o=(i,a)=>{let l=null;for(const{id:u,changes:f}of a){const m=(l??i.entities)[u];m!==void 0&&(l??(l={...i.entities}),l[u]={...m,...f})}return l===null?i:n(i,l,i.ids)},d=(i,a)=>{const l=new Set(a.filter(f=>i.entities[f]!==void 0));if(l.size===0)return i;const u={...i.entities};for(const f of l)delete u[f];return n(i,u,i.ids.filter(f=>!l.has(f)))};return{getInitialState(i){const a={ids:[],entities:{}};return i===void 0?a:{...a,...i}},addOne:(i,a)=>r(i,[a],"add"),addMany:(i,a)=>r(i,a,"add"),setOne:(i,a)=>r(i,[a],"set"),setMany:(i,a)=>r(i,a,"set"),setAll:(i,a)=>{const l={},u=[];for(const f of a){const m=e(f);l[m]===void 0&&u.push(m),l[m]=f}return n(i,l,u)},updateOne:(i,a)=>o(i,[a]),updateMany:(i,a)=>o(i,a),upsertOne:(i,a)=>r(i,[a],"upsert"),upsertMany:(i,a)=>r(i,a,"upsert"),removeOne:(i,a)=>d(i,[a]),removeMany:(i,a)=>d(i,a),removeAll:i=>i.ids.length===0?i:n(i,{},[]),selectIds:i=>i.ids,selectEntities:i=>i.entities,selectAll:i=>i.ids.map(a=>i.entities[a]),selectById:(i,a)=>i.entities[a],selectTotal:i=>i.ids.length,idsPath:"ids",pathTo:(i,a)=>a===void 0?`entities.${i}`:`entities.${i}.${a}`,anyField:i=>`entities.*.${i}`}}const w="$yoltra";function C(c,e={}){const t=e.maxNodes??1e5,s=e.sanitize,n=[],r=new Map;let o=0,d=!1;function i(l,u){if(s!==void 0&&(l=s(u,l)),o+=1,o>t)return d=!0,{[w]:"unsupported",kind:"truncated"};switch(typeof l){case"undefined":return{[w]:"undefined"};case"bigint":return{[w]:"bigint",value:l.toString()};case"number":return Number.isNaN(l)?{[w]:"nan"}:l===1/0?{[w]:"infinity",sign:1}:l===-1/0?{[w]:"infinity",sign:-1}:l;case"function":case"symbol":return n.push(u),{[w]:"unsupported",kind:typeof l};case"string":case"boolean":return l}if(l===null)return null;const f=l,m=r.get(f);if(m!==void 0)return{[w]:"ref",path:m};if(r.set(f,u),l instanceof Date)return{[w]:"date",iso:l.toISOString()};if(l instanceof RegExp)return{[w]:"regexp",source:l.source,flags:l.flags};if(l instanceof Error)return{[w]:"error",name:l.name,message:l.message};if(l instanceof Map){const y=[];let g=0;for(const[E,M]of l)y.push([i(E,`${u}/@k${g}`),i(M,`${u}/${g}`)]),g+=1;return{[w]:"map",entries:y}}if(l instanceof Set){const y=[];let g=0;for(const E of l)y.push(i(E,`${u}/${g}`)),g+=1;return{[w]:"set",values:y}}if(Array.isArray(l))return l.map((y,g)=>i(y,`${u}/${g}`));const h={};for(const[y,g]of Object.entries(l))h[y]=i(g,`${u}/${V(y)}`);return w in h?{[w]:"escaped",value:h}:h}return{value:i(c,""),report:{truncated:d,unsupported:n}}}function L(c){const e=new Map,t=[];function s(o,d){if(o===null||typeof o!="object")return o;if(Array.isArray(o)){const a=[];return e.set(d,a),o.forEach((l,u)=>{if(N(l)){t.push({target:a,key:u,path:l.path}),a[u]=void 0;return}a[u]=s(l,`${d}/${u}`)}),a}if(typeof o[w]=="string"){const a=o;switch(a[w]){case"undefined":return;case"nan":return Number.NaN;case"infinity":return a.sign===1?1/0:-1/0;case"bigint":return BigInt(a.value);case"date":return new Date(a.iso);case"regexp":return new RegExp(a.source,a.flags);case"error":{const l=new Error(a.message);return l.name=a.name,l}case"unsupported":return;case"ref":return;case"map":{const l=new Map;return e.set(d,l),a.entries.forEach(([u,f],m)=>{l.set(s(u,`${d}/@k${m}`),s(f,`${d}/${m}`))}),l}case"set":{const l=new Set;return e.set(d,l),a.values.forEach((u,f)=>l.add(s(u,`${d}/${f}`))),l}case"escaped":return n(a.value,d);default:return}}return n(o,d)}function n(o,d){const i={};e.set(d,i);for(const[a,l]of Object.entries(o)){const u=`${d}/${V(a)}`;if(N(l)){t.push({target:i,key:a,path:l.path}),i[a]=void 0;continue}i[a]=s(l,u)}return i}const r=s(c,"");e.set("",r);for(const{target:o,key:d,path:i}of t)o[d]=e.get(i);return r}function N(c){return c!==null&&typeof c=="object"&&c[w]==="ref"&&typeof c.path=="string"}function V(c){return c.replace(/~/g,"~0").replace(/\//g,"~1")}function fe(c,e,t={}){let s=t.maxNodes??1e5;for(let n=0;n<8;n+=1){const{value:r,report:o}=C(c,{...t,maxNodes:s});let d;try{d=JSON.stringify(r)?.length??0}catch{d=Number.POSITIVE_INFINITY}if(d<=e)return o.truncated?{value:r,truncated:!0,note:`State was too large to send in full; parts beyond ${s} nodes are omitted.`}:{value:r,truncated:!1};const i=Math.floor(s*e*.8/d);if(s=Math.max(1,Math.min(i,s-1)),s<=1&&n>0)break}return{value:{[w]:"unsupported",kind:"truncated"},truncated:!0,note:`State exceeds the ${e}-byte transport limit and could not be reduced to fit.`}}function v(c,e,t){c.onError?.(e,t)}async function he(c){const e={slices:{},restored:!1};let t;try{t=c.source??await c.adapter.read(c.key)}catch(n){return v(c,n,"read"),e}if(t==null||t==="")return e;let s;try{s=L(JSON.parse(t))}catch(n){return v(c,n,"decode"),e}if(s===null||typeof s!="object"||typeof s.version!="number")return v(c,new Error("persisted payload is not a recognisable envelope"),"decode"),e;if(s.version!==c.version){if(c.migrate===void 0)return v(c,new Error(`persisted state is version ${s.version}, this build expects ${c.version}, and no migrate was supplied`),"migrate"),e;try{const n=c.migrate(s.slices,s.version);return n===null?e:{slices:n,restored:!0}}catch(n){return v(c,n,"migrate"),e}}return{slices:s.slices??{},restored:!0}}function pe(c,e){if(!e.restored)return c;const t={};for(const[s,n]of Object.entries(c)){const r=e.slices[s];t[s]=r===void 0?n:{...n,state:r}}return t}function Q(c,e){const t=c??{},s=e.slices===void 0?t:Object.fromEntries(e.slices.filter(n=>n in t).map(n=>[n,t[n]]));return JSON.stringify(C({version:e.version,slices:s}).value)}function me(c,e){const t=e.throttleMs??250,s=e.slices;let n=null,r=!1;const o=()=>{if(r){r=!1;try{const a=e.adapter.write(e.key,Q(c.getState(),e));a instanceof Promise&&a.catch(l=>v(e,l,"write"))}catch(a){v(e,a,"write")}}},d=()=>{if(r=!0,t<=0){o();return}n===null&&(n=setTimeout(()=>{n=null,o()},t),n.unref?.())},i=c.instrument(a=>{if(s===void 0){d();return}(a.changedPaths??[]).some(u=>s.some(f=>u===f||u.startsWith(`${f}.`)))&&d()});return()=>{i(),n!==null&&(clearTimeout(n),n=null),o()}}function ye(c,e){return Q(c.getState(),e)}function ge(c){return{read:e=>c.getItem(e),write:(e,t)=>c.setItem(e,t),remove:e=>c.removeItem(e)}}function Ee(c){const e=new Map(Object.entries(c??{}));return{read:t=>e.get(t)??null,write:(t,s)=>{e.set(t,s)},remove:t=>{e.delete(t)}}}exports.CallAbortedError=I;exports.CallTimeoutError=U;exports.EventBus=B;exports.LooseEventBus=H;exports.Reducer=W;exports.Rejected=q;exports.Store=P;exports.createEntityAdapter=ue;exports.createMemoryAdapter=Ee;exports.createStore=oe;exports.createWebStorageAdapter=ge;exports.decodeState=L;exports.dehydrate=ye;exports.detectChangedProps=T;exports.encodeState=C;exports.encodeStateBounded=fe;exports.eventKeys=ae;exports.freezeState=k;exports.hydrate=he;exports.isRejected=F;exports.persist=me;exports.typedEvents=ce;exports.withHydration=pe;
11
+ //# sourceMappingURL=yoltra.cjs.map