sia-reactor 0.0.25 → 0.0.26

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.md CHANGED
@@ -5,8 +5,9 @@
5
5
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  [![NPM Version](https://img.shields.io/npm/v/sia-reactor.svg)](https://www.npmjs.com/package/sia-reactor)
7
7
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/sia-reactor)](https://bundlephobia.com/package/sia-reactor)
8
+ [![Github](https://img.shields.io/badge/github-100000?style=for-the-badge&logo=github)](https://github.com/Tobi007-del/sia-reactor)
8
9
 
9
- [Live Demo & Benchmarks](https://tobi007-del.github.io/t007-tools/packages/sia-reactor/src/index.html) | [Report Bug](https://github.com/Tobi007-del/t007-tools/issues)
10
+ [Live Demo & Benchmarks](https://tobi007-del.github.io/sia-reactor/src/index.html) | [Report Bug](https://github.com/Tobi007-del/sia-reactor/issues)
10
11
 
11
12
  [Chronicles](https://github.com/Tobi007-del/tmg-media-player/blob/main/CHRONICLES.md) | [Interaction Folklore](https://github.com/Tobi007-del/tmg-media-player/blob/main/FOLKLORE.md)
12
13
 
@@ -195,7 +196,7 @@ const data = reactive({
195
196
 
196
197
  ### React Hooks & Effects
197
198
 
198
- The engine provides native React bindings utilizing `useSyncExternalStore` and an internal `Autotracker` for concurrent-safe, surgically precise component re-renders. All hooks natively accept a `Reactor` instance, a `reactive()` proxy, or a plain object (which will be auto-wrapped on the fly). Just import, your editor will reveal more details.
199
+ The engine provides native React bindings utilizing `useSyncExternalStore` and an internal `Autotracker` for concurrent-safe, surgically precise component re-renders. All hooks natively accept a `Reactor` instance, a `reactive()` proxy, or a plain object (which will be auto-wrapped on the fly). Just import, your editor will reveal more details all round.
199
200
 
200
201
  ```javascript
201
202
  import { reactive } from "sia-reactor";
@@ -230,7 +231,7 @@ const stopTracking = effect(() => console.log("User name changed to:", state.use
230
231
 
231
232
  ### Modules: The Extension Port
232
233
 
233
- The `Reactor` is designed to be a lightweight core. Extended capabilities are attached via Modules. It ships with a suite of powerful modules for common architectural needs.
234
+ The `Reactor` is designed to be a lightweight core. Extended capabilities are attached via Modules. Use `.attach(target: Reactor | Reactive<T>, id)` to chain reactors then `.setup(target, id)` which the `Reactor.use()` also calls to init, the `id` param will be direct keys on the final object, pass dotted paths to manipulate the shape.
234
235
 
235
236
  #### The Persistence Module
236
237
  Automatically syncs your State to LocalStorage, SessionStorage, Memory or IndexedDB. Always use this module first to avoid re-initialization issues.
@@ -240,14 +241,20 @@ import { reactive, Reactor, getReactor } from "sia-reactor";
240
241
  import { PersistModule, LocalStorageAdapter, IndexedDBAdapter, SessionStorageAdapter, CookieAdapter, MemoryAdapter } from "sia-reactor/modules";
241
242
 
242
243
  const state = reactive({ theme: "dark", settings: { volume: 50, brightness: 30 } });
243
- const persist = new PersistModule({ // Plug it in. State is now automatically hydrated and throttled-saved.
244
- key: "APP_PREFS",
245
- paths: ["theme", "settings.brightness"],
246
- throttle: 5000,
247
- fanout: true, // async hydration should use leaf writes to sync already initialized listeners.
244
+ const persist = new PersistModule({
245
+ key: "APP_GLOBAL_STATE",
246
+ whitelist: ["theme", "settings.brightness"], // all paths if omitted, use object if multiple reactors
247
+ blacklist: ["settings.debug"], // optional excluded paths
248
+ throttle: 2500, // ms between saves
249
+ fanout: true, // async hydration use leaf writes to sync initialized listeners.
248
250
  adapter: new IndexedDBAdapter({ dbName: "Session", version: 1, onversionchange: () => location.reload(), useSnapshot: true }) // or `LocalStorageAdapter` (instance or signature)
249
- };
250
- state.use(persist, getReactor(state))); // Put `Reactor` as second constructor arg if you want type inference, e.g. for the paths in the array.
251
+ }, getReactor(state)); // `Reactor` in second arg for path inference
252
+ state.use(persist); // calls `.setup()`, use after all attachments, `id` is the second param too.
253
+
254
+ // Seperate attach sample if multiple reactors desired
255
+ persist.attach(uiState, "ui").setup(appState, "app"); // or paths: "app.ui"
256
+ persist.config.whitelist = { ui: ["settings.theme"], app: ["settings.volume"] }; // Multi-reactor filtering by id key. If you didn't pass ids, use implicit index keys: { "0": [...], "1": [...] }
257
+
251
258
  ```
252
259
 
253
260
  #### The Time Travel Module
@@ -258,7 +265,7 @@ import { TimeTravelModule } from "sia-reactor/modules";
258
265
  import { effect, TimeTravelOverlay } from "sia-reactor/adapters/vanilla";
259
266
  import "sia-reactor/css/time-travel-overlay.css";
260
267
 
261
- const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150 });
268
+ const time = new TimeTravelModule({ maxHistory: 300, loop: false, rate: 150, whitelist: ["state.playing", "state.currentTime"] });
262
269
  state.use(time);
263
270
 
264
271
  // If persist uses an async adapter (e.g. IndexedDB), wait till after hydration:
@@ -531,7 +538,7 @@ S.I.A. Reactor synthesizes core concepts from the heavyweights of web and media
531
538
 
532
539
  No fancy screenshots here. True engineers look at performance metrics.
533
540
 
534
- To see the Reactor handle deep DAG mutations, DOM-style event routing, and microtask batching in real-time, visit the **[Live Demo](https://tobi007-del.github.io/t007-tools/packages/sia-reactor/src/index.html)**, open your DevTools console, and run the built-in Grand Master Stress Suite directly on your own CPU.
541
+ To see the Reactor handle deep DAG mutations, DOM-style event routing, and microtask batching in real-time, visit the **[Live Demo](https://tobi007-del.github.io/sia-reactor/src/index.html)**, open your DevTools console, and run the built-in Grand Master Stress Suite directly on your own CPU.
535
542
 
536
543
  *NOTE: The reactor is progressively enhanced so it's performance depends on how you use it and the options you toggle, it's base form is incredibly light.*
537
544
 
@@ -540,7 +547,7 @@ To see the Reactor handle deep DAG mutations, DOM-style event routing, and micro
540
547
  ## Author
541
548
 
542
549
  - Architect & Developer - [Oketade Oluwatobiloba (Tobi007-del)](https://github.com/Tobi007-del)
543
- - Project - [t007-tools](https://github.com/Tobi007-del/t007-tools)
550
+ - Project - [t007-tools](https://github.com/Tobi007-del/sia-reactor)
544
551
 
545
552
  ## Acknowledgments
546
553
 
@@ -550,6 +557,6 @@ Designed to bring absolute architectural dominance and rendering efficiency to c
550
557
 
551
558
  If you find this project useful, please consider giving it a star! ⭐
552
559
 
553
- [![Star History Chart](https://api.star-history.com/svg?repos=Tobi007-del/t007-tools&type=Date)](https://github.com/Tobi007-del/t007-tools)
560
+ [![Star History Chart](https://api.star-history.com/svg?repos=Tobi007-del/sia-reactor&type=Date)](https://github.com/Tobi007-del/sia-reactor)
554
561
 
555
562
  **[⬆ Back to Top](#sia-reactor)**
@@ -1,5 +1,5 @@
1
- import { d as Reactive } from './index-CB-IiZIB.cjs';
2
- import { m as TimeTravelModule } from './timeTravel-CEc3grUE.cjs';
1
+ import { d as Reactive } from './index-2jKy98op.cjs';
2
+ import { m as TimeTravelModule } from './timeTravel-DzgX8BKQ.cjs';
3
3
 
4
4
  /** Reactive options for the TimeTravel overlay instance. */
5
5
  interface TimeTravelOverlayConfig {
@@ -1,5 +1,5 @@
1
- import { d as Reactive } from './index-CB-IiZIB.js';
2
- import { m as TimeTravelModule } from './timeTravel-D7NiYqdD.js';
1
+ import { d as Reactive } from './index-2jKy98op.js';
2
+ import { m as TimeTravelModule } from './timeTravel-CsbQ8qhP.js';
3
3
 
4
4
  /** Reactive options for the TimeTravel overlay instance. */
5
5
  interface TimeTravelOverlayConfig {
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions, R as Reactor, d as Reactive, v as ReactorBuild, D as DeepReadonly, W as WildPaths, e as PathValue } from '../index-CB-IiZIB.cjs';
1
+ import { E as EffectOptions, R as Reactor, d as Reactive, v as ReactorBuild, D as DeepReadonly, W as WildPaths, e as PathValue } from '../index-2jKy98op.cjs';
2
2
  import { useLayoutEffect } from 'react';
3
- import { m as TimeTravelModule } from '../timeTravel-CEc3grUE.cjs';
4
- import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-Bz2v9hov.cjs';
3
+ import { m as TimeTravelModule } from '../timeTravel-DzgX8BKQ.cjs';
4
+ import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-_6k5wu1I.cjs';
5
5
 
6
6
  /**
7
7
  * Subscribes a component to desired Reactor state and returns it.
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions, R as Reactor, d as Reactive, v as ReactorBuild, D as DeepReadonly, W as WildPaths, e as PathValue } from '../index-CB-IiZIB.js';
1
+ import { E as EffectOptions, R as Reactor, d as Reactive, v as ReactorBuild, D as DeepReadonly, W as WildPaths, e as PathValue } from '../index-2jKy98op.js';
2
2
  import { useLayoutEffect } from 'react';
3
- import { m as TimeTravelModule } from '../timeTravel-D7NiYqdD.js';
4
- import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-CvTDJWpP.js';
3
+ import { m as TimeTravelModule } from '../timeTravel-CsbQ8qhP.js';
4
+ import { a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-fun5VLIo.js';
5
5
 
6
6
  /**
7
7
  * Subscribes a component to desired Reactor state and returns it.
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-CB-IiZIB.cjs';
2
- export { A as Autotracker, w as withTracker } from '../index-CB-IiZIB.cjs';
3
- export { T as TimeTravelOverlay, a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-Bz2v9hov.cjs';
4
- import '../timeTravel-CEc3grUE.cjs';
1
+ import { E as EffectOptions } from '../index-2jKy98op.cjs';
2
+ export { A as Autotracker, w as withTracker } from '../index-2jKy98op.cjs';
3
+ export { T as TimeTravelOverlay, a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-_6k5wu1I.cjs';
4
+ import '../timeTravel-DzgX8BKQ.cjs';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -1,7 +1,7 @@
1
- import { E as EffectOptions } from '../index-CB-IiZIB.js';
2
- export { A as Autotracker, w as withTracker } from '../index-CB-IiZIB.js';
3
- export { T as TimeTravelOverlay, a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-CvTDJWpP.js';
4
- import '../timeTravel-D7NiYqdD.js';
1
+ import { E as EffectOptions } from '../index-2jKy98op.js';
2
+ export { A as Autotracker, w as withTracker } from '../index-2jKy98op.js';
3
+ export { T as TimeTravelOverlay, a as TimeTravelOverlayConfig } from '../TimeTravelOverlay-fun5VLIo.js';
4
+ import '../timeTravel-CsbQ8qhP.js';
5
5
 
6
6
  /**
7
7
  * Runs a reactive side effect in vanilla JavaScript.
@@ -816,6 +816,7 @@ declare function deepClone<T>(obj: T, config?: {
816
816
  declare function nuke(target: any): void;
817
817
 
818
818
  type ReactorModuleId = string | number;
819
+ type ModulePaths<P extends string = string> = P[] | Partial<Record<string, P[]>>;
819
820
  interface ReactorModuleConstructor<P extends BaseReactorModule = BaseReactorModule, T extends object = any> {
820
821
  new (rtr: Reactor<T>, config: any): P;
821
822
  moduleName: string;
@@ -873,6 +874,13 @@ declare abstract class BaseReactorModule<T extends object = any, Config = any, S
873
874
  * window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
874
875
  */
875
876
  guard: <Fn extends Function>(fn: Fn) => Fn;
877
+ /**
878
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
879
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
880
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
881
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
882
+ */
883
+ protected getPaths<P extends string = string>(paths?: ModulePaths<P>, target?: Reactor<any> | ReactorModuleId): P[];
876
884
  }
877
885
 
878
886
  /**
@@ -1195,4 +1203,4 @@ declare const NIL: any;
1195
1203
  /** Shared no-operation function. */
1196
1204
  declare const NOOP: () => void;
1197
1205
 
1198
- export { type Live as $, Autotracker as A, BaseReactorModule as B, CTX as C, type DeepReadonly as D, type EffectOptions as E, type FanoutTuple as F, type DeepMerge as G, type DeepPartial as H, type Inert as I, type DeepRequired as J, type Deleter as K, type DeleterRecord as L, type DepthConfig as M, type DirectPayload as N, EVT_OPTS as O, type Paths as P, type Getter as Q, Reactor as R, type GetterRecord as S, INDIFFABLE as T, INERTIA as U, type Intent as V, type WildPaths as W, type Listener as X, type ListenerOptions as Y, type ListenerOptionsTuple as Z, type ListenerRecord as _, type REvent as a, type MaxDepth as a0, NIL as a1, NOOP as a2, type NextDepth as a3, type NoTraverse as a4, type PathBranch as a5, type PathBranchValue as a6, type PathDepth as a7, type PathKey as a8, type PathLeaf as a9, type WatcherRecord as aA, getRaw as aB, getSnapshotVersion as aC, getVersion as aD, inert as aE, intent as aF, isInert as aG, isIntent as aH, isVolatile as aI, live as aJ, methods as aK, reactive as aL, stable as aM, state as aN, volatile as aO, type Payload as aa, type PrevDepth as ab, type Primitive as ac, RAW as ad, REJECTABLE as ae, RTR_BATCH as af, RTR_LOG as ag, type ReactivePreferences as ah, ReactorEvent as ai, SSVERSION as aj, type Setter as ak, type SetterRecord as al, type Stable as am, type State as an, type StrictPathKey as ao, type SubtractDepth as ap, type SyncOptions as aq, type SyncOptionsTuple as ar, TERMINATOR as as, type Target as at, type Unflatten as au, type UnionToIntersection as av, type UpdatePayload as aw, VERSION as ax, type Volatile as ay, type Watcher as az, type ReactorModuleConstructor as b, type ReactorModuleId as c, type Reactive as d, type PathValue as e, arrRegex as f, canHandle as g, deepClone as h, deleteAny as i, fanout as j, fanoutOptsArr as k, getAny as l, getTrailRecords as m, inAny as n, isObj as o, isPOJO as p, mergeObjs as q, nuke as r, parseAnyObj as s, parseEvtOpts as t, setAny as u, type ReactorBuild as v, withTracker as w, type AddDepth as x, type ChildPaths as y, type DeepKeys as z };
1206
+ export { type ListenerRecord as $, Autotracker as A, BaseReactorModule as B, CTX as C, type DeepReadonly as D, type EffectOptions as E, type FanoutTuple as F, type DeepMerge as G, type DeepPartial as H, type Inert as I, type DeepRequired as J, type Deleter as K, type DeleterRecord as L, type ModulePaths as M, type DepthConfig as N, type DirectPayload as O, type Paths as P, EVT_OPTS as Q, Reactor as R, type Getter as S, type GetterRecord as T, INDIFFABLE as U, INERTIA as V, type WildPaths as W, type Intent as X, type Listener as Y, type ListenerOptions as Z, type ListenerOptionsTuple as _, type ReactorModuleId as a, type Live as a0, type MaxDepth as a1, NIL as a2, NOOP as a3, type NextDepth as a4, type NoTraverse as a5, type PathBranch as a6, type PathBranchValue as a7, type PathDepth as a8, type PathKey as a9, type Watcher as aA, type WatcherRecord as aB, getRaw as aC, getSnapshotVersion as aD, getVersion as aE, inert as aF, intent as aG, isInert as aH, isIntent as aI, isVolatile as aJ, live as aK, methods as aL, reactive as aM, stable as aN, state as aO, volatile as aP, type PathLeaf as aa, type Payload as ab, type PrevDepth as ac, type Primitive as ad, RAW as ae, REJECTABLE as af, RTR_BATCH as ag, RTR_LOG as ah, type ReactivePreferences as ai, ReactorEvent as aj, SSVERSION as ak, type Setter as al, type SetterRecord as am, type Stable as an, type State as ao, type StrictPathKey as ap, type SubtractDepth as aq, type SyncOptions as ar, type SyncOptionsTuple as as, TERMINATOR as at, type Target as au, type Unflatten as av, type UnionToIntersection as aw, type UpdatePayload as ax, VERSION as ay, type Volatile as az, type REvent as b, type ReactorModuleConstructor as c, type Reactive as d, type PathValue as e, arrRegex as f, canHandle as g, deepClone as h, deleteAny as i, fanout as j, fanoutOptsArr as k, getAny as l, getTrailRecords as m, inAny as n, isObj as o, isPOJO as p, mergeObjs as q, nuke as r, parseAnyObj as s, parseEvtOpts as t, setAny as u, type ReactorBuild as v, withTracker as w, type AddDepth as x, type ChildPaths as y, type DeepKeys as z };
@@ -816,6 +816,7 @@ declare function deepClone<T>(obj: T, config?: {
816
816
  declare function nuke(target: any): void;
817
817
 
818
818
  type ReactorModuleId = string | number;
819
+ type ModulePaths<P extends string = string> = P[] | Partial<Record<string, P[]>>;
819
820
  interface ReactorModuleConstructor<P extends BaseReactorModule = BaseReactorModule, T extends object = any> {
820
821
  new (rtr: Reactor<T>, config: any): P;
821
822
  moduleName: string;
@@ -873,6 +874,13 @@ declare abstract class BaseReactorModule<T extends object = any, Config = any, S
873
874
  * window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
874
875
  */
875
876
  guard: <Fn extends Function>(fn: Fn) => Fn;
877
+ /**
878
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
879
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
880
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
881
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
882
+ */
883
+ protected getPaths<P extends string = string>(paths?: ModulePaths<P>, target?: Reactor<any> | ReactorModuleId): P[];
876
884
  }
877
885
 
878
886
  /**
@@ -1195,4 +1203,4 @@ declare const NIL: any;
1195
1203
  /** Shared no-operation function. */
1196
1204
  declare const NOOP: () => void;
1197
1205
 
1198
- export { type Live as $, Autotracker as A, BaseReactorModule as B, CTX as C, type DeepReadonly as D, type EffectOptions as E, type FanoutTuple as F, type DeepMerge as G, type DeepPartial as H, type Inert as I, type DeepRequired as J, type Deleter as K, type DeleterRecord as L, type DepthConfig as M, type DirectPayload as N, EVT_OPTS as O, type Paths as P, type Getter as Q, Reactor as R, type GetterRecord as S, INDIFFABLE as T, INERTIA as U, type Intent as V, type WildPaths as W, type Listener as X, type ListenerOptions as Y, type ListenerOptionsTuple as Z, type ListenerRecord as _, type REvent as a, type MaxDepth as a0, NIL as a1, NOOP as a2, type NextDepth as a3, type NoTraverse as a4, type PathBranch as a5, type PathBranchValue as a6, type PathDepth as a7, type PathKey as a8, type PathLeaf as a9, type WatcherRecord as aA, getRaw as aB, getSnapshotVersion as aC, getVersion as aD, inert as aE, intent as aF, isInert as aG, isIntent as aH, isVolatile as aI, live as aJ, methods as aK, reactive as aL, stable as aM, state as aN, volatile as aO, type Payload as aa, type PrevDepth as ab, type Primitive as ac, RAW as ad, REJECTABLE as ae, RTR_BATCH as af, RTR_LOG as ag, type ReactivePreferences as ah, ReactorEvent as ai, SSVERSION as aj, type Setter as ak, type SetterRecord as al, type Stable as am, type State as an, type StrictPathKey as ao, type SubtractDepth as ap, type SyncOptions as aq, type SyncOptionsTuple as ar, TERMINATOR as as, type Target as at, type Unflatten as au, type UnionToIntersection as av, type UpdatePayload as aw, VERSION as ax, type Volatile as ay, type Watcher as az, type ReactorModuleConstructor as b, type ReactorModuleId as c, type Reactive as d, type PathValue as e, arrRegex as f, canHandle as g, deepClone as h, deleteAny as i, fanout as j, fanoutOptsArr as k, getAny as l, getTrailRecords as m, inAny as n, isObj as o, isPOJO as p, mergeObjs as q, nuke as r, parseAnyObj as s, parseEvtOpts as t, setAny as u, type ReactorBuild as v, withTracker as w, type AddDepth as x, type ChildPaths as y, type DeepKeys as z };
1206
+ export { type ListenerRecord as $, Autotracker as A, BaseReactorModule as B, CTX as C, type DeepReadonly as D, type EffectOptions as E, type FanoutTuple as F, type DeepMerge as G, type DeepPartial as H, type Inert as I, type DeepRequired as J, type Deleter as K, type DeleterRecord as L, type ModulePaths as M, type DepthConfig as N, type DirectPayload as O, type Paths as P, EVT_OPTS as Q, Reactor as R, type Getter as S, type GetterRecord as T, INDIFFABLE as U, INERTIA as V, type WildPaths as W, type Intent as X, type Listener as Y, type ListenerOptions as Z, type ListenerOptionsTuple as _, type ReactorModuleId as a, type Live as a0, type MaxDepth as a1, NIL as a2, NOOP as a3, type NextDepth as a4, type NoTraverse as a5, type PathBranch as a6, type PathBranchValue as a7, type PathDepth as a8, type PathKey as a9, type Watcher as aA, type WatcherRecord as aB, getRaw as aC, getSnapshotVersion as aD, getVersion as aE, inert as aF, intent as aG, isInert as aH, isIntent as aI, isVolatile as aJ, live as aK, methods as aL, reactive as aM, stable as aN, state as aO, volatile as aP, type PathLeaf as aa, type Payload as ab, type PrevDepth as ac, type Primitive as ad, RAW as ae, REJECTABLE as af, RTR_BATCH as ag, RTR_LOG as ah, type ReactivePreferences as ai, ReactorEvent as aj, SSVERSION as ak, type Setter as al, type SetterRecord as am, type Stable as an, type State as ao, type StrictPathKey as ap, type SubtractDepth as aq, type SyncOptions as ar, type SyncOptionsTuple as as, TERMINATOR as at, type Target as au, type Unflatten as av, type UnionToIntersection as aw, type UpdatePayload as ax, VERSION as ay, type Volatile as az, type REvent as b, type ReactorModuleConstructor as c, type Reactive as d, type PathValue as e, arrRegex as f, canHandle as g, deepClone as h, deleteAny as i, fanout as j, fanoutOptsArr as k, getAny as l, getTrailRecords as m, inAny as n, isObj as o, isPOJO as p, mergeObjs as q, nuke as r, parseAnyObj as s, parseEvtOpts as t, setAny as u, type ReactorBuild as v, withTracker as w, type AddDepth as x, type ChildPaths as y, type DeepKeys as z };
package/dist/index.d.cts CHANGED
@@ -1 +1 @@
1
- export { x as AddDepth, C as CTX, y as ChildPaths, z as DeepKeys, G as DeepMerge, H as DeepPartial, D as DeepReadonly, J as DeepRequired, K as Deleter, L as DeleterRecord, M as DepthConfig, N as DirectPayload, O as EVT_OPTS, E as EffectOptions, Q as Getter, S as GetterRecord, T as INDIFFABLE, U as INERTIA, I as Inert, V as Intent, X as Listener, Y as ListenerOptions, Z as ListenerOptionsTuple, _ as ListenerRecord, $ as Live, a0 as MaxDepth, a1 as NIL, a2 as NOOP, a3 as NextDepth, a4 as NoTraverse, a5 as PathBranch, a6 as PathBranchValue, a7 as PathDepth, a8 as PathKey, a9 as PathLeaf, e as PathValue, P as Paths, aa as Payload, ab as PrevDepth, ac as Primitive, ad as RAW, ae as REJECTABLE, a as REvent, af as RTR_BATCH, ag as RTR_LOG, d as Reactive, ah as ReactivePreferences, R as Reactor, v as ReactorBuild, ai as ReactorEvent, aj as SSVERSION, ak as Setter, al as SetterRecord, am as Stable, an as State, ao as StrictPathKey, ap as SubtractDepth, aq as SyncOptions, ar as SyncOptionsTuple, as as TERMINATOR, at as Target, au as Unflatten, av as UnionToIntersection, aw as UpdatePayload, ax as VERSION, ay as Volatile, az as Watcher, aA as WatcherRecord, W as WildPaths, aB as getRaw, aC as getSnapshotVersion, aD as getVersion, aE as inert, aF as intent, aG as isInert, aH as isIntent, aI as isVolatile, aJ as live, aK as methods, aL as reactive, aM as stable, aN as state, aO as volatile } from './index-CB-IiZIB.cjs';
1
+ export { x as AddDepth, C as CTX, y as ChildPaths, z as DeepKeys, G as DeepMerge, H as DeepPartial, D as DeepReadonly, J as DeepRequired, K as Deleter, L as DeleterRecord, N as DepthConfig, O as DirectPayload, Q as EVT_OPTS, E as EffectOptions, S as Getter, T as GetterRecord, U as INDIFFABLE, V as INERTIA, I as Inert, X as Intent, Y as Listener, Z as ListenerOptions, _ as ListenerOptionsTuple, $ as ListenerRecord, a0 as Live, a1 as MaxDepth, a2 as NIL, a3 as NOOP, a4 as NextDepth, a5 as NoTraverse, a6 as PathBranch, a7 as PathBranchValue, a8 as PathDepth, a9 as PathKey, aa as PathLeaf, e as PathValue, P as Paths, ab as Payload, ac as PrevDepth, ad as Primitive, ae as RAW, af as REJECTABLE, b as REvent, ag as RTR_BATCH, ah as RTR_LOG, d as Reactive, ai as ReactivePreferences, R as Reactor, v as ReactorBuild, aj as ReactorEvent, ak as SSVERSION, al as Setter, am as SetterRecord, an as Stable, ao as State, ap as StrictPathKey, aq as SubtractDepth, ar as SyncOptions, as as SyncOptionsTuple, at as TERMINATOR, au as Target, av as Unflatten, aw as UnionToIntersection, ax as UpdatePayload, ay as VERSION, az as Volatile, aA as Watcher, aB as WatcherRecord, W as WildPaths, aC as getRaw, aD as getSnapshotVersion, aE as getVersion, aF as inert, aG as intent, aH as isInert, aI as isIntent, aJ as isVolatile, aK as live, aL as methods, aM as reactive, aN as stable, aO as state, aP as volatile } from './index-2jKy98op.cjs';
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { x as AddDepth, C as CTX, y as ChildPaths, z as DeepKeys, G as DeepMerge, H as DeepPartial, D as DeepReadonly, J as DeepRequired, K as Deleter, L as DeleterRecord, M as DepthConfig, N as DirectPayload, O as EVT_OPTS, E as EffectOptions, Q as Getter, S as GetterRecord, T as INDIFFABLE, U as INERTIA, I as Inert, V as Intent, X as Listener, Y as ListenerOptions, Z as ListenerOptionsTuple, _ as ListenerRecord, $ as Live, a0 as MaxDepth, a1 as NIL, a2 as NOOP, a3 as NextDepth, a4 as NoTraverse, a5 as PathBranch, a6 as PathBranchValue, a7 as PathDepth, a8 as PathKey, a9 as PathLeaf, e as PathValue, P as Paths, aa as Payload, ab as PrevDepth, ac as Primitive, ad as RAW, ae as REJECTABLE, a as REvent, af as RTR_BATCH, ag as RTR_LOG, d as Reactive, ah as ReactivePreferences, R as Reactor, v as ReactorBuild, ai as ReactorEvent, aj as SSVERSION, ak as Setter, al as SetterRecord, am as Stable, an as State, ao as StrictPathKey, ap as SubtractDepth, aq as SyncOptions, ar as SyncOptionsTuple, as as TERMINATOR, at as Target, au as Unflatten, av as UnionToIntersection, aw as UpdatePayload, ax as VERSION, ay as Volatile, az as Watcher, aA as WatcherRecord, W as WildPaths, aB as getRaw, aC as getSnapshotVersion, aD as getVersion, aE as inert, aF as intent, aG as isInert, aH as isIntent, aI as isVolatile, aJ as live, aK as methods, aL as reactive, aM as stable, aN as state, aO as volatile } from './index-CB-IiZIB.js';
1
+ export { x as AddDepth, C as CTX, y as ChildPaths, z as DeepKeys, G as DeepMerge, H as DeepPartial, D as DeepReadonly, J as DeepRequired, K as Deleter, L as DeleterRecord, N as DepthConfig, O as DirectPayload, Q as EVT_OPTS, E as EffectOptions, S as Getter, T as GetterRecord, U as INDIFFABLE, V as INERTIA, I as Inert, X as Intent, Y as Listener, Z as ListenerOptions, _ as ListenerOptionsTuple, $ as ListenerRecord, a0 as Live, a1 as MaxDepth, a2 as NIL, a3 as NOOP, a4 as NextDepth, a5 as NoTraverse, a6 as PathBranch, a7 as PathBranchValue, a8 as PathDepth, a9 as PathKey, aa as PathLeaf, e as PathValue, P as Paths, ab as Payload, ac as PrevDepth, ad as Primitive, ae as RAW, af as REJECTABLE, b as REvent, ag as RTR_BATCH, ah as RTR_LOG, d as Reactive, ai as ReactivePreferences, R as Reactor, v as ReactorBuild, aj as ReactorEvent, ak as SSVERSION, al as Setter, am as SetterRecord, an as Stable, ao as State, ap as StrictPathKey, aq as SubtractDepth, ar as SyncOptions, as as SyncOptionsTuple, at as TERMINATOR, au as Target, av as Unflatten, aw as UnionToIntersection, ax as UpdatePayload, ay as VERSION, az as Volatile, aA as Watcher, aB as WatcherRecord, W as WildPaths, aC as getRaw, aD as getSnapshotVersion, aE as getVersion, aF as inert, aG as intent, aH as isInert, aI as isIntent, aJ as isVolatile, aK as live, aL as methods, aM as reactive, aN as stable, aO as state, aP as volatile } from './index-2jKy98op.js';
package/dist/modules.cjs CHANGED
@@ -1021,6 +1021,16 @@ var BaseReactorModule = class {
1021
1021
  return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
1022
1022
  };
1023
1023
  // `()=>{}`: needs to be bounded even before initialization
1024
+ /**
1025
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
1026
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
1027
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
1028
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
1029
+ */
1030
+ getPaths(paths, target) {
1031
+ const rid = "object" === typeof target ? this.rids.get(target) : target;
1032
+ return (paths && (Array.isArray(paths) ? paths : paths[String(rid)])) ?? wpArr;
1033
+ }
1024
1034
  };
1025
1035
 
1026
1036
  // src/ts/utils/store.ts
@@ -1367,7 +1377,7 @@ var PersistModule = class extends BaseReactorModule {
1367
1377
  get payload() {
1368
1378
  let res = this.rtrs.size > 1 ? {} : void 0;
1369
1379
  for (const [rid, rtr] of this.rtrs) {
1370
- const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, val = this.config.paths ? this.config.paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
1380
+ const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, paths = this.getPaths(this.config.whitelist, rid), val = this.config.whitelist ? paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
1371
1381
  this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
1372
1382
  }
1373
1383
  return res;
@@ -1380,10 +1390,10 @@ var PersistModule = class extends BaseReactorModule {
1380
1390
  "undefined" !== typeof document && document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && this.onDestroy(), { signal: this.signal });
1381
1391
  this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
1382
1392
  this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
1383
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
1393
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
1384
1394
  }
1385
- onAttach(rtr) {
1386
- for (const p of this.config.paths ?? wpArr) !this.config.disabled ? rtr.on(p, this.handleSave, { signal: this.signal, immediate: true }) : rtr.off(p, this.handleSave);
1395
+ onAttach(rtr, rid) {
1396
+ for (const p of this.getPaths(this.config.whitelist, rid)) !this.config.disabled ? rtr.on(p, this.save, { signal: this.signal, immediate: true }) : rtr.off(p, this.save);
1387
1397
  }
1388
1398
  async handleAdapter({ value = LocalStorageAdapter }) {
1389
1399
  const seq = ++this.hydrateSeq;
@@ -1397,27 +1407,29 @@ var PersistModule = class extends BaseReactorModule {
1397
1407
  saved = !isAsync ? saved : await saved;
1398
1408
  if (seq !== this.hydrateSeq || !saved) return;
1399
1409
  for (const [rid, rtr] of this.rtrs) {
1410
+ const paths = this.getPaths(this.config.whitelist, rid);
1400
1411
  const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
1401
1412
  if (!entry) continue;
1402
1413
  const set = (p, news, olds) => (depth ? fanout : setAny)(rtr.core, p, merge ? mergeObjs(news, olds) : olds, depth ? { depth, crossRealms: rtr.config.crossRealms } : void 0);
1403
- for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
1414
+ for (const p of this.config.whitelist ? paths : wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
1404
1415
  }
1405
- for (const rtr of this.rtrs.values()) rtr.tick(depth ? "*" : this.config.paths ?? "*");
1416
+ for (const [rid, rtr] of this.rtrs) rtr.tick(depth ? "*" : this.config.whitelist ? this.getPaths(this.config.whitelist, rid) : "*");
1406
1417
  } finally {
1407
1418
  if (seq === this.hydrateSeq) this.state.hydrated = true;
1408
1419
  }
1409
1420
  }
1410
1421
  handleDisabled({ value }) {
1411
- for (const rtr of this.rtrs.values()) this.onAttach(rtr);
1422
+ for (const [rid, rtr] of this.rtrs) this.onAttach(rtr, rid);
1412
1423
  value && this.adapter?.remove(this.config.key);
1413
1424
  }
1414
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
1415
- for (const rtr of this.rtrs.values()) {
1416
- for (const p of prevs) rtr.off(p, this.handleSave);
1417
- for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
1425
+ handleWhitelist({ value: paths, oldValue: prevs }) {
1426
+ for (const [rid, rtr] of this.rtrs) {
1427
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.save);
1428
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.save), !this.config.disabled && rtr.on(p, this.save, { signal: this.signal, immediate: true });
1418
1429
  }
1419
1430
  }
1420
- handleSave(e) {
1431
+ save(e) {
1432
+ if (this.config.blacklist && this.getPaths(this.config.blacklist, e.reactor).includes(e.path)) return;
1421
1433
  if (!this.state.hydrated) return e.stopImmediatePropagation();
1422
1434
  if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout2(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
1423
1435
  }
@@ -1453,22 +1465,23 @@ var TimeTravelModule = class extends BaseReactorModule {
1453
1465
  wire() {
1454
1466
  this.lastTimestamp = performance.now();
1455
1467
  this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
1456
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
1468
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
1457
1469
  !this.state.paused && this.play();
1458
1470
  }
1459
1471
  onAttach(rtr, rid) {
1460
1472
  rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
1461
1473
  if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
1462
- for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
1474
+ for (const p of this.getPaths(this.config.whitelist, rid)) rtr.on(p, this.record, { signal: this.signal });
1463
1475
  }
1464
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
1465
- for (const rtr of this.rtrs.values()) {
1466
- for (const p of prevs) rtr.off(p, this.record);
1467
- for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
1476
+ handleWhitelist({ value: paths, oldValue: prevs }) {
1477
+ for (const [rid, rtr] of this.rtrs) {
1478
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.record);
1479
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
1468
1480
  }
1469
1481
  }
1470
1482
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
1471
1483
  record(e, rid = this.rids.get(e.reactor)) {
1484
+ if (this.getPaths(this.config.blacklist, rid).includes(e.path)) return;
1472
1485
  if (!this.state.paused) return;
1473
1486
  if (this.state.currentFrame < this.state.history.length) fanout(this.state, "history", this.state.history.slice(0, this.state.currentFrame), { atomic: true });
1474
1487
  if (this.state.history.length >= this.config.maxHistoryLength) fanout(this.state, "history", this.state.history.slice(1), { atomic: true });
@@ -1,15 +1,19 @@
1
- import { P as Paths, I as Inert, F as FanoutTuple, B as BaseReactorModule, R as Reactor, a as REvent } from './index-CB-IiZIB.cjs';
2
- export { b as ReactorModuleConstructor, c as ReactorModuleId } from './index-CB-IiZIB.cjs';
3
- import { S as StorageAdapter, A as AsyncStorageAdapter, a as StorageAdapterConstructor, b as AsyncStorageAdapterConstructor } from './timeTravel-CEc3grUE.cjs';
4
- export { B as BaseStorageAdapter, C as COOKIE_ADAPTER_BUILD, c as CookieAdapter, d as CookieAdapterConfig, e as CookieOptions, H as HistoryEntry, I as INDEXED_DB_ADAPTER_BUILD, f as IndexedDBAdapter, g as IndexedDBAdapterConfig, J as JSONReplacer, h as JSONReviver, L as LocalStorageAdapter, M as MemoryAdapter, i as MemoryAdapterConfig, j as SessionStorageAdapter, k as StorageAdapterConfig, T as TIME_TRAVEL_MODULE_BUILD, l as TimeTravelConfig, m as TimeTravelModule, n as TimeTravelState } from './timeTravel-CEc3grUE.cjs';
1
+ import { P as Paths, M as ModulePaths, I as Inert, F as FanoutTuple, B as BaseReactorModule, R as Reactor, a as ReactorModuleId, b as REvent } from './index-2jKy98op.cjs';
2
+ export { c as ReactorModuleConstructor } from './index-2jKy98op.cjs';
3
+ import { S as StorageAdapter, A as AsyncStorageAdapter, a as StorageAdapterConstructor, b as AsyncStorageAdapterConstructor } from './timeTravel-DzgX8BKQ.cjs';
4
+ export { B as BaseStorageAdapter, C as COOKIE_ADAPTER_BUILD, c as CookieAdapter, d as CookieAdapterConfig, e as CookieOptions, H as HistoryEntry, I as INDEXED_DB_ADAPTER_BUILD, f as IndexedDBAdapter, g as IndexedDBAdapterConfig, J as JSONReplacer, h as JSONReviver, L as LocalStorageAdapter, M as MemoryAdapter, i as MemoryAdapterConfig, j as SessionStorageAdapter, k as StorageAdapterConfig, T as TIME_TRAVEL_MODULE_BUILD, l as TimeTravelConfig, m as TimeTravelModule, n as TimeTravelState } from './timeTravel-DzgX8BKQ.cjs';
5
5
 
6
6
  interface PersistConfig<T extends object, P extends Paths<T> = Paths<T>> {
7
7
  /** Whether the persistence is disabled and cleared */
8
8
  disabled: boolean;
9
9
  /** The key under which to store the persisted data */
10
10
  key: string;
11
- /** Specific paths only, no need for "*"; instead don't pass anything */
12
- paths: P[];
11
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
12
+ * - `P[]`: one shared path list for all attached reactors.
13
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
14
+ whitelist: ModulePaths<P>;
15
+ /** Exclude filter for save-trigger paths. Checked only during save events. */
16
+ blacklist?: ModulePaths<P>;
13
17
  /** Storage adapter class or instance to use, can satisfy `instanceof` or just definition, cast to `any` if the latter */
14
18
  adapter: Inert<StorageAdapter> | Inert<AsyncStorageAdapter> | Inert<StorageAdapterConstructor> | Inert<AsyncStorageAdapterConstructor>;
15
19
  /** Throttle time for saving changes */
@@ -38,15 +42,15 @@ declare class PersistModule<T extends object = any, P extends Paths<T> = Paths<T
38
42
  get payload(): Record<string, any> | undefined;
39
43
  constructor(config?: Partial<PersistConfig<T, P>>, rtr?: Reactor<T>);
40
44
  wire(): void;
41
- protected onAttach(rtr: Reactor<any>): void;
45
+ protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
42
46
  protected handleAdapter({ value }: REvent<PersistConfig<T, P>, "adapter">): Promise<void>;
43
47
  protected handleDisabled({ value }: REvent<PersistConfig<T, P>, "disabled">): void;
44
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "paths">): void;
45
- protected handleSave(e: REvent<any, P>): void;
48
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "whitelist">): void;
49
+ protected save(e: REvent<any, P>): void;
46
50
  /** Clears persisted payload for this module instance and drops any pending save. */
47
51
  clear(): void;
48
52
  protected onDestroy(): void;
49
53
  }
50
54
  declare const PERSIST_MODULE_BUILD: Partial<PersistConfig<any>>;
51
55
 
52
- export { AsyncStorageAdapter, AsyncStorageAdapterConstructor, BaseReactorModule, PERSIST_MODULE_BUILD, type PersistConfig, PersistModule, StorageAdapter, StorageAdapterConstructor };
56
+ export { AsyncStorageAdapter, AsyncStorageAdapterConstructor, BaseReactorModule, ModulePaths, PERSIST_MODULE_BUILD, type PersistConfig, PersistModule, ReactorModuleId, StorageAdapter, StorageAdapterConstructor };
package/dist/modules.d.ts CHANGED
@@ -1,15 +1,19 @@
1
- import { P as Paths, I as Inert, F as FanoutTuple, B as BaseReactorModule, R as Reactor, a as REvent } from './index-CB-IiZIB.js';
2
- export { b as ReactorModuleConstructor, c as ReactorModuleId } from './index-CB-IiZIB.js';
3
- import { S as StorageAdapter, A as AsyncStorageAdapter, a as StorageAdapterConstructor, b as AsyncStorageAdapterConstructor } from './timeTravel-D7NiYqdD.js';
4
- export { B as BaseStorageAdapter, C as COOKIE_ADAPTER_BUILD, c as CookieAdapter, d as CookieAdapterConfig, e as CookieOptions, H as HistoryEntry, I as INDEXED_DB_ADAPTER_BUILD, f as IndexedDBAdapter, g as IndexedDBAdapterConfig, J as JSONReplacer, h as JSONReviver, L as LocalStorageAdapter, M as MemoryAdapter, i as MemoryAdapterConfig, j as SessionStorageAdapter, k as StorageAdapterConfig, T as TIME_TRAVEL_MODULE_BUILD, l as TimeTravelConfig, m as TimeTravelModule, n as TimeTravelState } from './timeTravel-D7NiYqdD.js';
1
+ import { P as Paths, M as ModulePaths, I as Inert, F as FanoutTuple, B as BaseReactorModule, R as Reactor, a as ReactorModuleId, b as REvent } from './index-2jKy98op.js';
2
+ export { c as ReactorModuleConstructor } from './index-2jKy98op.js';
3
+ import { S as StorageAdapter, A as AsyncStorageAdapter, a as StorageAdapterConstructor, b as AsyncStorageAdapterConstructor } from './timeTravel-CsbQ8qhP.js';
4
+ export { B as BaseStorageAdapter, C as COOKIE_ADAPTER_BUILD, c as CookieAdapter, d as CookieAdapterConfig, e as CookieOptions, H as HistoryEntry, I as INDEXED_DB_ADAPTER_BUILD, f as IndexedDBAdapter, g as IndexedDBAdapterConfig, J as JSONReplacer, h as JSONReviver, L as LocalStorageAdapter, M as MemoryAdapter, i as MemoryAdapterConfig, j as SessionStorageAdapter, k as StorageAdapterConfig, T as TIME_TRAVEL_MODULE_BUILD, l as TimeTravelConfig, m as TimeTravelModule, n as TimeTravelState } from './timeTravel-CsbQ8qhP.js';
5
5
 
6
6
  interface PersistConfig<T extends object, P extends Paths<T> = Paths<T>> {
7
7
  /** Whether the persistence is disabled and cleared */
8
8
  disabled: boolean;
9
9
  /** The key under which to store the persisted data */
10
10
  key: string;
11
- /** Specific paths only, no need for "*"; instead don't pass anything */
12
- paths: P[];
11
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
12
+ * - `P[]`: one shared path list for all attached reactors.
13
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
14
+ whitelist: ModulePaths<P>;
15
+ /** Exclude filter for save-trigger paths. Checked only during save events. */
16
+ blacklist?: ModulePaths<P>;
13
17
  /** Storage adapter class or instance to use, can satisfy `instanceof` or just definition, cast to `any` if the latter */
14
18
  adapter: Inert<StorageAdapter> | Inert<AsyncStorageAdapter> | Inert<StorageAdapterConstructor> | Inert<AsyncStorageAdapterConstructor>;
15
19
  /** Throttle time for saving changes */
@@ -38,15 +42,15 @@ declare class PersistModule<T extends object = any, P extends Paths<T> = Paths<T
38
42
  get payload(): Record<string, any> | undefined;
39
43
  constructor(config?: Partial<PersistConfig<T, P>>, rtr?: Reactor<T>);
40
44
  wire(): void;
41
- protected onAttach(rtr: Reactor<any>): void;
45
+ protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
42
46
  protected handleAdapter({ value }: REvent<PersistConfig<T, P>, "adapter">): Promise<void>;
43
47
  protected handleDisabled({ value }: REvent<PersistConfig<T, P>, "disabled">): void;
44
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "paths">): void;
45
- protected handleSave(e: REvent<any, P>): void;
48
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "whitelist">): void;
49
+ protected save(e: REvent<any, P>): void;
46
50
  /** Clears persisted payload for this module instance and drops any pending save. */
47
51
  clear(): void;
48
52
  protected onDestroy(): void;
49
53
  }
50
54
  declare const PERSIST_MODULE_BUILD: Partial<PersistConfig<any>>;
51
55
 
52
- export { AsyncStorageAdapter, AsyncStorageAdapterConstructor, BaseReactorModule, PERSIST_MODULE_BUILD, type PersistConfig, PersistModule, StorageAdapter, StorageAdapterConstructor };
56
+ export { AsyncStorageAdapter, AsyncStorageAdapterConstructor, BaseReactorModule, ModulePaths, PERSIST_MODULE_BUILD, type PersistConfig, PersistModule, ReactorModuleId, StorageAdapter, StorageAdapterConstructor };
package/dist/modules.js CHANGED
@@ -88,6 +88,16 @@ var BaseReactorModule = class {
88
88
  return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
89
89
  };
90
90
  // `()=>{}`: needs to be bounded even before initialization
91
+ /**
92
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
93
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
94
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
95
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
96
+ */
97
+ getPaths(paths, target) {
98
+ const rid = "object" === typeof target ? this.rids.get(target) : target;
99
+ return (paths && (Array.isArray(paths) ? paths : paths[String(rid)])) ?? wpArr;
100
+ }
91
101
  };
92
102
 
93
103
  // src/ts/utils/store.ts
@@ -424,7 +434,7 @@ var PersistModule = class extends BaseReactorModule {
424
434
  get payload() {
425
435
  let res = this.rtrs.size > 1 ? {} : void 0;
426
436
  for (const [rid, rtr] of this.rtrs) {
427
- const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, val = this.config.paths ? this.config.paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
437
+ const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, paths = this.getPaths(this.config.whitelist, rid), val = this.config.whitelist ? paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
428
438
  this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
429
439
  }
430
440
  return res;
@@ -437,10 +447,10 @@ var PersistModule = class extends BaseReactorModule {
437
447
  "undefined" !== typeof document && document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && this.onDestroy(), { signal: this.signal });
438
448
  this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
439
449
  this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
440
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
450
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
441
451
  }
442
- onAttach(rtr) {
443
- for (const p of this.config.paths ?? wpArr) !this.config.disabled ? rtr.on(p, this.handleSave, { signal: this.signal, immediate: true }) : rtr.off(p, this.handleSave);
452
+ onAttach(rtr, rid) {
453
+ for (const p of this.getPaths(this.config.whitelist, rid)) !this.config.disabled ? rtr.on(p, this.save, { signal: this.signal, immediate: true }) : rtr.off(p, this.save);
444
454
  }
445
455
  async handleAdapter({ value = LocalStorageAdapter }) {
446
456
  const seq = ++this.hydrateSeq;
@@ -454,27 +464,29 @@ var PersistModule = class extends BaseReactorModule {
454
464
  saved = !isAsync ? saved : await saved;
455
465
  if (seq !== this.hydrateSeq || !saved) return;
456
466
  for (const [rid, rtr] of this.rtrs) {
467
+ const paths = this.getPaths(this.config.whitelist, rid);
457
468
  const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
458
469
  if (!entry) continue;
459
470
  const set = (p, news, olds) => (depth ? fanout : setAny)(rtr.core, p, merge ? mergeObjs(news, olds) : olds, depth ? { depth, crossRealms: rtr.config.crossRealms } : void 0);
460
- for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
471
+ for (const p of this.config.whitelist ? paths : wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
461
472
  }
462
- for (const rtr of this.rtrs.values()) rtr.tick(depth ? "*" : this.config.paths ?? "*");
473
+ for (const [rid, rtr] of this.rtrs) rtr.tick(depth ? "*" : this.config.whitelist ? this.getPaths(this.config.whitelist, rid) : "*");
463
474
  } finally {
464
475
  if (seq === this.hydrateSeq) this.state.hydrated = true;
465
476
  }
466
477
  }
467
478
  handleDisabled({ value }) {
468
- for (const rtr of this.rtrs.values()) this.onAttach(rtr);
479
+ for (const [rid, rtr] of this.rtrs) this.onAttach(rtr, rid);
469
480
  value && this.adapter?.remove(this.config.key);
470
481
  }
471
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
472
- for (const rtr of this.rtrs.values()) {
473
- for (const p of prevs) rtr.off(p, this.handleSave);
474
- for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
482
+ handleWhitelist({ value: paths, oldValue: prevs }) {
483
+ for (const [rid, rtr] of this.rtrs) {
484
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.save);
485
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.save), !this.config.disabled && rtr.on(p, this.save, { signal: this.signal, immediate: true });
475
486
  }
476
487
  }
477
- handleSave(e) {
488
+ save(e) {
489
+ if (this.config.blacklist && this.getPaths(this.config.blacklist, e.reactor).includes(e.path)) return;
478
490
  if (!this.state.hydrated) return e.stopImmediatePropagation();
479
491
  if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
480
492
  }
@@ -505,22 +517,23 @@ var TimeTravelModule = class extends BaseReactorModule {
505
517
  wire() {
506
518
  this.lastTimestamp = performance.now();
507
519
  this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
508
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
520
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
509
521
  !this.state.paused && this.play();
510
522
  }
511
523
  onAttach(rtr, rid) {
512
524
  rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
513
525
  if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
514
- for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
526
+ for (const p of this.getPaths(this.config.whitelist, rid)) rtr.on(p, this.record, { signal: this.signal });
515
527
  }
516
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
517
- for (const rtr of this.rtrs.values()) {
518
- for (const p of prevs) rtr.off(p, this.record);
519
- for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
528
+ handleWhitelist({ value: paths, oldValue: prevs }) {
529
+ for (const [rid, rtr] of this.rtrs) {
530
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.record);
531
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
520
532
  }
521
533
  }
522
534
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
523
535
  record(e, rid = this.rids.get(e.reactor)) {
536
+ if (this.getPaths(this.config.blacklist, rid).includes(e.path)) return;
524
537
  if (!this.state.paused) return;
525
538
  if (this.state.currentFrame < this.state.history.length) fanout(this.state, "history", this.state.history.slice(0, this.state.currentFrame), { atomic: true });
526
539
  if (this.state.history.length >= this.config.maxHistoryLength) fanout(this.state, "history", this.state.history.slice(1), { atomic: true });
package/dist/super.d.ts CHANGED
@@ -816,6 +816,7 @@ declare function deepClone<T>(obj: T, config?: {
816
816
  declare function nuke(target: any): void;
817
817
 
818
818
  type ReactorModuleId = string | number;
819
+ type ModulePaths<P extends string = string> = P[] | Partial<Record<string, P[]>>;
819
820
  interface ReactorModuleConstructor<P extends BaseReactorModule = BaseReactorModule, T extends object = any> {
820
821
  new (rtr: Reactor<T>, config: any): P;
821
822
  moduleName: string;
@@ -873,6 +874,13 @@ declare abstract class BaseReactorModule<T extends object = any, Config = any, S
873
874
  * window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
874
875
  */
875
876
  guard: <Fn extends Function>(fn: Fn) => Fn;
877
+ /**
878
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
879
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
880
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
881
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
882
+ */
883
+ protected getPaths<P extends string = string>(paths?: ModulePaths<P>, target?: Reactor<any> | ReactorModuleId): P[];
876
884
  }
877
885
 
878
886
  /**
@@ -1686,8 +1694,12 @@ interface PersistConfig<T extends object, P extends Paths<T> = Paths<T>> {
1686
1694
  disabled: boolean;
1687
1695
  /** The key under which to store the persisted data */
1688
1696
  key: string;
1689
- /** Specific paths only, no need for "*"; instead don't pass anything */
1690
- paths: P[];
1697
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
1698
+ * - `P[]`: one shared path list for all attached reactors.
1699
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
1700
+ whitelist: ModulePaths<P>;
1701
+ /** Exclude filter for save-trigger paths. Checked only during save events. */
1702
+ blacklist?: ModulePaths<P>;
1691
1703
  /** Storage adapter class or instance to use, can satisfy `instanceof` or just definition, cast to `any` if the latter */
1692
1704
  adapter: Inert<StorageAdapter> | Inert<AsyncStorageAdapter> | Inert<StorageAdapterConstructor> | Inert<AsyncStorageAdapterConstructor>;
1693
1705
  /** Throttle time for saving changes */
@@ -1716,11 +1728,11 @@ declare class PersistModule<T extends object = any, P extends Paths<T> = Paths<T
1716
1728
  get payload(): Record<string, any> | undefined;
1717
1729
  constructor(config?: Partial<PersistConfig<T, P>>, rtr?: Reactor<T>);
1718
1730
  wire(): void;
1719
- protected onAttach(rtr: Reactor<any>): void;
1731
+ protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
1720
1732
  protected handleAdapter({ value }: REvent<PersistConfig<T, P>, "adapter">): Promise<void>;
1721
1733
  protected handleDisabled({ value }: REvent<PersistConfig<T, P>, "disabled">): void;
1722
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "paths">): void;
1723
- protected handleSave(e: REvent<any, P>): void;
1734
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<PersistConfig<T, P>, "whitelist">): void;
1735
+ protected save(e: REvent<any, P>): void;
1724
1736
  /** Clears persisted payload for this module instance and drops any pending save. */
1725
1737
  clear(): void;
1726
1738
  protected onDestroy(): void;
@@ -1747,8 +1759,12 @@ interface HistoryEntry<T extends object = any, P extends Paths<T> = Paths<T>> {
1747
1759
  rid: ReactorModuleId;
1748
1760
  }
1749
1761
  interface TimeTravelConfig<T extends object, P extends Paths<T> = Paths<T>> {
1750
- /** Specific paths only, no "*"; instead don't pass anything */
1751
- paths: P[];
1762
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
1763
+ * - `P[]`: one shared path list for all attached reactors.
1764
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
1765
+ whitelist: ModulePaths<P>;
1766
+ /** Exclude filter for recorded paths. Checked only during record events. */
1767
+ blacklist?: ModulePaths<P>;
1752
1768
  /** Maximum number of history entries to keep (Memory Cap), you lose replaying Sessions or the Genesis */
1753
1769
  maxHistoryLength: number;
1754
1770
  /** Max delay between events during playback (ms) */
@@ -1779,7 +1795,7 @@ declare class TimeTravelModule<T extends object = any, P extends Paths<T> = Path
1779
1795
  constructor(config?: Partial<TimeTravelConfig<T, P>>, rtr?: Reactor<T>);
1780
1796
  wire(): void;
1781
1797
  protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
1782
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "paths">): void;
1798
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "whitelist">): void;
1783
1799
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
1784
1800
  protected record(e: REvent<any, P>, rid?: ReactorModuleId): void;
1785
1801
  /** Clears timeline history and resets playhead/genesis to the current reactor state. */
@@ -1831,6 +1847,7 @@ declare const modules_LocalStorageAdapter: typeof LocalStorageAdapter;
1831
1847
  type modules_MemoryAdapter = MemoryAdapter;
1832
1848
  declare const modules_MemoryAdapter: typeof MemoryAdapter;
1833
1849
  type modules_MemoryAdapterConfig = MemoryAdapterConfig;
1850
+ type modules_ModulePaths<P extends string = string> = ModulePaths<P>;
1834
1851
  declare const modules_PERSIST_MODULE_BUILD: typeof PERSIST_MODULE_BUILD;
1835
1852
  type modules_PersistConfig<T extends object, P extends Paths<T> = Paths<T>> = PersistConfig<T, P>;
1836
1853
  type modules_PersistModule<T extends object = any, P extends Paths<T> = Paths<T>> = PersistModule<T, P>;
@@ -1849,7 +1866,7 @@ type modules_TimeTravelModule<T extends object = any, P extends Paths<T> = Paths
1849
1866
  declare const modules_TimeTravelModule: typeof TimeTravelModule;
1850
1867
  type modules_TimeTravelState<T extends object, P extends Paths<T> = Paths<T>> = TimeTravelState<T, P>;
1851
1868
  declare namespace modules {
1852
- export { modules_AsyncStorageAdapter as AsyncStorageAdapter, type modules_AsyncStorageAdapterConstructor as AsyncStorageAdapterConstructor, modules_BaseReactorModule as BaseReactorModule, modules_BaseStorageAdapter as BaseStorageAdapter, modules_COOKIE_ADAPTER_BUILD as COOKIE_ADAPTER_BUILD, modules_CookieAdapter as CookieAdapter, type modules_CookieAdapterConfig as CookieAdapterConfig, type modules_CookieOptions as CookieOptions, type modules_HistoryEntry as HistoryEntry, modules_INDEXED_DB_ADAPTER_BUILD as INDEXED_DB_ADAPTER_BUILD, modules_IndexedDBAdapter as IndexedDBAdapter, type modules_IndexedDBAdapterConfig as IndexedDBAdapterConfig, type modules_JSONReplacer as JSONReplacer, type modules_JSONReviver as JSONReviver, modules_LocalStorageAdapter as LocalStorageAdapter, modules_MemoryAdapter as MemoryAdapter, type modules_MemoryAdapterConfig as MemoryAdapterConfig, modules_PERSIST_MODULE_BUILD as PERSIST_MODULE_BUILD, type modules_PersistConfig as PersistConfig, modules_PersistModule as PersistModule, type modules_ReactorModuleConstructor as ReactorModuleConstructor, type modules_ReactorModuleId as ReactorModuleId, modules_SessionStorageAdapter as SessionStorageAdapter, modules_StorageAdapter as StorageAdapter, type modules_StorageAdapterConfig as StorageAdapterConfig, type modules_StorageAdapterConstructor as StorageAdapterConstructor, modules_TIME_TRAVEL_MODULE_BUILD as TIME_TRAVEL_MODULE_BUILD, type modules_TimeTravelConfig as TimeTravelConfig, modules_TimeTravelModule as TimeTravelModule, type modules_TimeTravelState as TimeTravelState };
1869
+ export { modules_AsyncStorageAdapter as AsyncStorageAdapter, type modules_AsyncStorageAdapterConstructor as AsyncStorageAdapterConstructor, modules_BaseReactorModule as BaseReactorModule, modules_BaseStorageAdapter as BaseStorageAdapter, modules_COOKIE_ADAPTER_BUILD as COOKIE_ADAPTER_BUILD, modules_CookieAdapter as CookieAdapter, type modules_CookieAdapterConfig as CookieAdapterConfig, type modules_CookieOptions as CookieOptions, type modules_HistoryEntry as HistoryEntry, modules_INDEXED_DB_ADAPTER_BUILD as INDEXED_DB_ADAPTER_BUILD, modules_IndexedDBAdapter as IndexedDBAdapter, type modules_IndexedDBAdapterConfig as IndexedDBAdapterConfig, type modules_JSONReplacer as JSONReplacer, type modules_JSONReviver as JSONReviver, modules_LocalStorageAdapter as LocalStorageAdapter, modules_MemoryAdapter as MemoryAdapter, type modules_MemoryAdapterConfig as MemoryAdapterConfig, type modules_ModulePaths as ModulePaths, modules_PERSIST_MODULE_BUILD as PERSIST_MODULE_BUILD, type modules_PersistConfig as PersistConfig, modules_PersistModule as PersistModule, type modules_ReactorModuleConstructor as ReactorModuleConstructor, type modules_ReactorModuleId as ReactorModuleId, modules_SessionStorageAdapter as SessionStorageAdapter, modules_StorageAdapter as StorageAdapter, type modules_StorageAdapterConfig as StorageAdapterConfig, type modules_StorageAdapterConstructor as StorageAdapterConstructor, modules_TIME_TRAVEL_MODULE_BUILD as TIME_TRAVEL_MODULE_BUILD, type modules_TimeTravelConfig as TimeTravelConfig, modules_TimeTravelModule as TimeTravelModule, type modules_TimeTravelState as TimeTravelState };
1853
1870
  }
1854
1871
 
1855
1872
  /**
@@ -1256,6 +1256,16 @@ var sia = (() => {
1256
1256
  return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
1257
1257
  };
1258
1258
  // `()=>{}`: needs to be bounded even before initialization
1259
+ /**
1260
+ * Path resolution utility for modules, provides automatic reactor id resolution for multi-reactor setups.
1261
+ * @param paths Paths to filter by, supports same formats as `ModulePaths`, will be resolved with the module's reactor id if applicable.
1262
+ * @param target Reactor or reactor id to resolve paths for when using per-reactor path lists`.
1263
+ * @returns Resolved paths array, defaults to `["*"]` if no paths are found using search criteria.
1264
+ */
1265
+ getPaths(paths, target) {
1266
+ const rid = "object" === typeof target ? this.rids.get(target) : target;
1267
+ return (paths && (Array.isArray(paths) ? paths : paths[String(rid)])) ?? wpArr;
1268
+ }
1259
1269
  };
1260
1270
 
1261
1271
  // src/ts/utils/store.ts
@@ -1592,7 +1602,7 @@ var sia = (() => {
1592
1602
  get payload() {
1593
1603
  let res = this.rtrs.size > 1 ? {} : void 0;
1594
1604
  for (const [rid, rtr] of this.rtrs) {
1595
- const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, val = this.config.paths ? this.config.paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
1605
+ const snap = this.config.useSnapshot ? (this.config.useSnapshot === true && (rtr.config.referenceTracking = rtr.config.smartCloning = true), rtr.snapshot()) : rtr.core, paths = this.getPaths(this.config.whitelist, rid), val = this.config.whitelist ? paths.reduce((acc, p) => (setAny(acc, p, getAny(snap, p)), acc), {}) : snap;
1596
1606
  this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
1597
1607
  }
1598
1608
  return res;
@@ -1605,10 +1615,10 @@ var sia = (() => {
1605
1615
  "undefined" !== typeof document && document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && this.onDestroy(), { signal: this.signal });
1606
1616
  this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
1607
1617
  this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
1608
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
1618
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
1609
1619
  }
1610
- onAttach(rtr) {
1611
- for (const p of this.config.paths ?? wpArr) !this.config.disabled ? rtr.on(p, this.handleSave, { signal: this.signal, immediate: true }) : rtr.off(p, this.handleSave);
1620
+ onAttach(rtr, rid) {
1621
+ for (const p of this.getPaths(this.config.whitelist, rid)) !this.config.disabled ? rtr.on(p, this.save, { signal: this.signal, immediate: true }) : rtr.off(p, this.save);
1612
1622
  }
1613
1623
  async handleAdapter({ value = LocalStorageAdapter }) {
1614
1624
  const seq = ++this.hydrateSeq;
@@ -1622,27 +1632,29 @@ var sia = (() => {
1622
1632
  saved = !isAsync ? saved : await saved;
1623
1633
  if (seq !== this.hydrateSeq || !saved) return;
1624
1634
  for (const [rid, rtr] of this.rtrs) {
1635
+ const paths = this.getPaths(this.config.whitelist, rid);
1625
1636
  const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
1626
1637
  if (!entry) continue;
1627
1638
  const set = (p, news, olds) => (depth ? fanout : setAny)(rtr.core, p, merge ? mergeObjs(news, olds) : olds, depth ? { depth, crossRealms: rtr.config.crossRealms } : void 0);
1628
- for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
1639
+ for (const p of this.config.whitelist ? paths : wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
1629
1640
  }
1630
- for (const rtr of this.rtrs.values()) rtr.tick(depth ? "*" : this.config.paths ?? "*");
1641
+ for (const [rid, rtr] of this.rtrs) rtr.tick(depth ? "*" : this.config.whitelist ? this.getPaths(this.config.whitelist, rid) : "*");
1631
1642
  } finally {
1632
1643
  if (seq === this.hydrateSeq) this.state.hydrated = true;
1633
1644
  }
1634
1645
  }
1635
1646
  handleDisabled({ value }) {
1636
- for (const rtr of this.rtrs.values()) this.onAttach(rtr);
1647
+ for (const [rid, rtr] of this.rtrs) this.onAttach(rtr, rid);
1637
1648
  value && this.adapter?.remove(this.config.key);
1638
1649
  }
1639
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
1640
- for (const rtr of this.rtrs.values()) {
1641
- for (const p of prevs) rtr.off(p, this.handleSave);
1642
- for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
1650
+ handleWhitelist({ value: paths, oldValue: prevs }) {
1651
+ for (const [rid, rtr] of this.rtrs) {
1652
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.save);
1653
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.save), !this.config.disabled && rtr.on(p, this.save, { signal: this.signal, immediate: true });
1643
1654
  }
1644
1655
  }
1645
- handleSave(e) {
1656
+ save(e) {
1657
+ if (this.config.blacklist && this.getPaths(this.config.blacklist, e.reactor).includes(e.path)) return;
1646
1658
  if (!this.state.hydrated) return e.stopImmediatePropagation();
1647
1659
  if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout2(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
1648
1660
  }
@@ -1673,22 +1685,23 @@ var sia = (() => {
1673
1685
  wire() {
1674
1686
  this.lastTimestamp = performance.now();
1675
1687
  this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
1676
- this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
1688
+ this.config.on("whitelist", this.handleWhitelist, { signal: this.signal, immediate: true });
1677
1689
  !this.state.paused && this.play();
1678
1690
  }
1679
1691
  onAttach(rtr, rid) {
1680
1692
  rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
1681
1693
  if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
1682
- for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
1694
+ for (const p of this.getPaths(this.config.whitelist, rid)) rtr.on(p, this.record, { signal: this.signal });
1683
1695
  }
1684
- handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
1685
- for (const rtr of this.rtrs.values()) {
1686
- for (const p of prevs) rtr.off(p, this.record);
1687
- for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
1696
+ handleWhitelist({ value: paths, oldValue: prevs }) {
1697
+ for (const [rid, rtr] of this.rtrs) {
1698
+ for (const p of this.getPaths(prevs, rid)) rtr.off(p, this.record);
1699
+ for (const p of this.getPaths(paths, rid)) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
1688
1700
  }
1689
1701
  }
1690
1702
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
1691
1703
  record(e, rid = this.rids.get(e.reactor)) {
1704
+ if (this.getPaths(this.config.blacklist, rid).includes(e.path)) return;
1692
1705
  if (!this.state.paused) return;
1693
1706
  if (this.state.currentFrame < this.state.history.length) fanout(this.state, "history", this.state.history.slice(0, this.state.currentFrame), { atomic: true });
1694
1707
  if (this.state.history.length >= this.config.maxHistoryLength) fanout(this.state, "history", this.state.history.slice(1), { atomic: true });
@@ -1,4 +1,4 @@
1
- import { P as Paths, e as PathValue, a as REvent, c as ReactorModuleId, B as BaseReactorModule, R as Reactor } from './index-CB-IiZIB.cjs';
1
+ import { P as Paths, e as PathValue, b as REvent, a as ReactorModuleId, M as ModulePaths, B as BaseReactorModule, R as Reactor } from './index-2jKy98op.js';
2
2
 
3
3
  type JSONReplacer = ((this: any, key: string, value: any) => any) | (number | string)[] | null;
4
4
  type JSONReviver = ((this: any, key: string, value: any) => any) | undefined;
@@ -289,8 +289,12 @@ interface HistoryEntry<T extends object = any, P extends Paths<T> = Paths<T>> {
289
289
  rid: ReactorModuleId;
290
290
  }
291
291
  interface TimeTravelConfig<T extends object, P extends Paths<T> = Paths<T>> {
292
- /** Specific paths only, no "*"; instead don't pass anything */
293
- paths: P[];
292
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
293
+ * - `P[]`: one shared path list for all attached reactors.
294
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
295
+ whitelist: ModulePaths<P>;
296
+ /** Exclude filter for recorded paths. Checked only during record events. */
297
+ blacklist?: ModulePaths<P>;
294
298
  /** Maximum number of history entries to keep (Memory Cap), you lose replaying Sessions or the Genesis */
295
299
  maxHistoryLength: number;
296
300
  /** Max delay between events during playback (ms) */
@@ -321,7 +325,7 @@ declare class TimeTravelModule<T extends object = any, P extends Paths<T> = Path
321
325
  constructor(config?: Partial<TimeTravelConfig<T, P>>, rtr?: Reactor<T>);
322
326
  wire(): void;
323
327
  protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
324
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "paths">): void;
328
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "whitelist">): void;
325
329
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
326
330
  protected record(e: REvent<any, P>, rid?: ReactorModuleId): void;
327
331
  /** Clears timeline history and resets playhead/genesis to the current reactor state. */
@@ -1,4 +1,4 @@
1
- import { P as Paths, e as PathValue, a as REvent, c as ReactorModuleId, B as BaseReactorModule, R as Reactor } from './index-CB-IiZIB.js';
1
+ import { P as Paths, e as PathValue, b as REvent, a as ReactorModuleId, M as ModulePaths, B as BaseReactorModule, R as Reactor } from './index-2jKy98op.cjs';
2
2
 
3
3
  type JSONReplacer = ((this: any, key: string, value: any) => any) | (number | string)[] | null;
4
4
  type JSONReviver = ((this: any, key: string, value: any) => any) | undefined;
@@ -289,8 +289,12 @@ interface HistoryEntry<T extends object = any, P extends Paths<T> = Paths<T>> {
289
289
  rid: ReactorModuleId;
290
290
  }
291
291
  interface TimeTravelConfig<T extends object, P extends Paths<T> = Paths<T>> {
292
- /** Specific paths only, no "*"; instead don't pass anything */
293
- paths: P[];
292
+ /** Whitelist paths only, no need for "*"; instead don't pass anything.
293
+ * - `P[]`: one shared path list for all attached reactors.
294
+ * - `Record<string, P[]>`: per-reactor path lists keyed by module reactor id. If you don't pass ids in `.attach()`, use implicit index keys (`"0"`, `"1"`, ...). */
295
+ whitelist: ModulePaths<P>;
296
+ /** Exclude filter for recorded paths. Checked only during record events. */
297
+ blacklist?: ModulePaths<P>;
294
298
  /** Maximum number of history entries to keep (Memory Cap), you lose replaying Sessions or the Genesis */
295
299
  maxHistoryLength: number;
296
300
  /** Max delay between events during playback (ms) */
@@ -321,7 +325,7 @@ declare class TimeTravelModule<T extends object = any, P extends Paths<T> = Path
321
325
  constructor(config?: Partial<TimeTravelConfig<T, P>>, rtr?: Reactor<T>);
322
326
  wire(): void;
323
327
  protected onAttach(rtr: Reactor<any>, rid: ReactorModuleId): void;
324
- protected handlePaths({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "paths">): void;
328
+ protected handleWhitelist({ value: paths, oldValue: prevs }: REvent<TimeTravelConfig<T, P>, "whitelist">): void;
325
329
  /** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
326
330
  protected record(e: REvent<any, P>, rid?: ReactorModuleId): void;
327
331
  /** Clears timeline history and resets playhead/genesis to the current reactor state. */
package/dist/utils.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { F as FanoutTuple, f as arrRegex, g as canHandle, h as deepClone, i as deleteAny, j as fanout, k as fanoutOptsArr, l as getAny, m as getTrailRecords, n as inAny, o as isObj, p as isPOJO, q as mergeObjs, r as nuke, s as parseAnyObj, t as parseEvtOpts, u as setAny } from './index-CB-IiZIB.cjs';
1
+ export { F as FanoutTuple, f as arrRegex, g as canHandle, h as deepClone, i as deleteAny, j as fanout, k as fanoutOptsArr, l as getAny, m as getTrailRecords, n as inAny, o as isObj, p as isPOJO, q as mergeObjs, r as nuke, s as parseAnyObj, t as parseEvtOpts, u as setAny } from './index-2jKy98op.cjs';
2
2
 
3
3
  declare function clamp(min: number | undefined, val: number, max?: number): number;
4
4
 
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { F as FanoutTuple, f as arrRegex, g as canHandle, h as deepClone, i as deleteAny, j as fanout, k as fanoutOptsArr, l as getAny, m as getTrailRecords, n as inAny, o as isObj, p as isPOJO, q as mergeObjs, r as nuke, s as parseAnyObj, t as parseEvtOpts, u as setAny } from './index-CB-IiZIB.js';
1
+ export { F as FanoutTuple, f as arrRegex, g as canHandle, h as deepClone, i as deleteAny, j as fanout, k as fanoutOptsArr, l as getAny, m as getTrailRecords, n as inAny, o as isObj, p as isPOJO, q as mergeObjs, r as nuke, s as parseAnyObj, t as parseEvtOpts, u as setAny } from './index-2jKy98op.js';
2
2
 
3
3
  declare function clamp(min: number | undefined, val: number, max?: number): number;
4
4
 
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "sia-reactor",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "description": "The Programmable Data DOM. A high-performance State Intent Architecture (S.I.A.) Engine with zero-allocation loops, event propagation, and structural sharing.",
5
5
  "author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/Tobi007-del/t007-tools.git",
10
- "directory": "packages/sia-reactor"
9
+ "url": "git+https://github.com/Tobi007-del/sia-reactor.git"
11
10
  },
12
- "homepage": "https://github.com/Tobi007-del/tree/main/packages/sia-reactor#readme",
11
+ "homepage": "https://github.com/Tobi007-del/sia-reactor#readme",
13
12
  "bugs": {
14
13
  "url": "https://github.com/Tobi007-del/sia-reactor/issues"
15
14
  },
@@ -58,12 +57,14 @@
58
57
  },
59
58
  "scripts": {
60
59
  "build": "tsup --config tsup.config.ts",
61
- "prepublishOnly": "shx cp ../../LICENSE .",
62
60
  "test": "vitest --reporter=verbose",
63
- "test:run": "vitest run",
61
+ "test:run": "vitest run --reporter=verbose",
64
62
  "demo": "vite --config demo/vite.config.ts",
65
63
  "demo:build": "vite build --config demo/vite.config.ts",
66
- "demo:preview": "vite preview --config demo/vite.config.ts"
64
+ "demo:preview": "vite preview --config demo/vite.config.ts",
65
+ "change": "changeset",
66
+ "bump": "changeset version",
67
+ "release": "changeset publish"
67
68
  },
68
69
  "files": [
69
70
  "dist"
@@ -89,6 +90,8 @@
89
90
  "zero-dependency"
90
91
  ],
91
92
  "devDependencies": {
93
+ "@changesets/cli": "^2.30.0",
94
+ "@types/node": "^25.2.2",
92
95
  "@types/react-dom": "^18.0.0",
93
96
  "@vitejs/plugin-react": "^5.0.4",
94
97
  "@types/react": "^18.0.0",