sia-reactor 0.0.21 → 0.0.22
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 +115 -89
- package/dist/{TimeTravelOverlay-CJv-S_Km.d.cts → TimeTravelOverlay-DiXUgbUU.d.cts} +9 -8
- package/dist/{TimeTravelOverlay-DxqJL0Zk.d.ts → TimeTravelOverlay-eWjAy0yr.d.ts} +9 -8
- package/dist/adapters/react.cjs +66 -84
- package/dist/adapters/react.d.cts +23 -22
- package/dist/adapters/react.d.ts +23 -22
- package/dist/adapters/react.js +3 -3
- package/dist/adapters/vanilla.cjs +66 -84
- package/dist/adapters/vanilla.d.cts +4 -4
- package/dist/adapters/vanilla.d.ts +4 -4
- package/dist/adapters/vanilla.js +3 -3
- package/dist/{chunk-2WBPGSRL.js → chunk-3SKLWTEA.js} +52 -70
- package/dist/{chunk-DP74DVRT.js → chunk-BTA6MIQ6.js} +40 -8
- package/dist/{chunk-TFLYCXK4.js → chunk-CS3FOV6J.js} +14 -13
- package/dist/{index-Oie9hhE8.d.cts → index-BgbbNXTW.d.cts} +306 -207
- package/dist/{index-Oie9hhE8.d.ts → index-BgbbNXTW.d.ts} +306 -207
- package/dist/index.cjs +54 -75
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -4
- package/dist/{plugins.cjs → modules.cjs} +437 -166
- package/dist/modules.d.cts +53 -0
- package/dist/modules.d.ts +53 -0
- package/dist/modules.js +627 -0
- package/dist/super.d.ts +610 -281
- package/dist/super.global.js +445 -174
- package/dist/timeTravel-CraHdbXZ.d.cts +352 -0
- package/dist/timeTravel-YUxRHRgh.d.ts +352 -0
- package/dist/utils.cjs +41 -7
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -1
- package/package.json +6 -6
- package/dist/plugins.d.cts +0 -112
- package/dist/plugins.d.ts +0 -112
- package/dist/plugins.js +0 -370
- package/dist/timeTravel-B1vedDQc.d.ts +0 -76
- package/dist/timeTravel-WpgWmKu-.d.cts +0 -76
|
@@ -17,28 +17,34 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
|
-
// src/ts/
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
23
|
-
|
|
20
|
+
// src/ts/modules.ts
|
|
21
|
+
var modules_exports = {};
|
|
22
|
+
__export(modules_exports, {
|
|
23
|
+
AsyncStorageAdapter: () => AsyncStorageAdapter,
|
|
24
|
+
BaseReactorModule: () => BaseReactorModule,
|
|
24
25
|
BaseStorageAdapter: () => BaseStorageAdapter,
|
|
26
|
+
COOKIE_ADAPTER_BUILD: () => COOKIE_ADAPTER_BUILD,
|
|
27
|
+
CookieAdapter: () => CookieAdapter,
|
|
25
28
|
INDEXED_DB_ADAPTER_BUILD: () => INDEXED_DB_ADAPTER_BUILD,
|
|
26
29
|
IndexedDBAdapter: () => IndexedDBAdapter,
|
|
27
30
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
MemoryAdapter: () => MemoryAdapter,
|
|
32
|
+
PERSIST_MODULE_BUILD: () => PERSIST_MODULE_BUILD,
|
|
33
|
+
PersistModule: () => PersistModule,
|
|
34
|
+
SessionStorageAdapter: () => SessionStorageAdapter,
|
|
31
35
|
StorageAdapter: () => StorageAdapter,
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
TIME_TRAVEL_MODULE_BUILD: () => TIME_TRAVEL_MODULE_BUILD,
|
|
37
|
+
TimeTravelModule: () => TimeTravelModule
|
|
34
38
|
});
|
|
35
|
-
module.exports = __toCommonJS(
|
|
39
|
+
module.exports = __toCommonJS(modules_exports);
|
|
36
40
|
|
|
37
41
|
// src/ts/core/consts.ts
|
|
38
42
|
var CTX = {
|
|
39
43
|
/** Flag indicating whether the application is running in development mode. */
|
|
40
44
|
isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
|
|
41
|
-
/**
|
|
45
|
+
/** Flag indicating whether a cascade is currently ongoing so reactors can allow all writes. */
|
|
46
|
+
isCascading: false,
|
|
47
|
+
/** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
|
|
42
48
|
autotracker: null
|
|
43
49
|
};
|
|
44
50
|
var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
|
|
@@ -50,7 +56,6 @@ var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
|
|
|
50
56
|
var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
|
|
51
57
|
var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
|
|
52
58
|
var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
|
|
53
|
-
var EVT_WARN = console.warn.bind(console, "[S.I.A Event]");
|
|
54
59
|
var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
|
|
55
60
|
var NIL = Object.freeze({});
|
|
56
61
|
var NOOP = () => {
|
|
@@ -149,9 +154,33 @@ function inAny(source, key, separator = ".", keyFunc) {
|
|
|
149
154
|
function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
|
|
150
155
|
return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
|
|
151
156
|
}
|
|
157
|
+
function fanout(a, b, c, d) {
|
|
158
|
+
const isEvtPld = !!a?.target, [state, path, news, olds, opts, type] = isEvtPld ? [a.root, a.currentTarget.path, a.currentTarget.value, a.currentTarget.oldValue, b || NIL, a.type] : [a, b, c, (d || NIL).merge ? getAny(a, b) : NIL, d || NIL], target = path === "*" ? state : getAny(state, path);
|
|
159
|
+
if (isEvtPld && type !== "set" && type !== "delete" || !target || !canHandle(news, opts)) return;
|
|
160
|
+
const prev = CTX.isCascading;
|
|
161
|
+
CTX.isCascading = isEvtPld;
|
|
162
|
+
try {
|
|
163
|
+
const walk = (target2, obj, depth = 1, keys = Object.keys(obj)) => {
|
|
164
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
165
|
+
const val = obj[keys[i]];
|
|
166
|
+
try {
|
|
167
|
+
depth > 1 && canHandle(val, opts) ? walk(target2[keys[i]] ||= {}, val, depth - 1) : target2[keys[i]] = val;
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
walk(target, opts.merge && canHandle(olds, opts) ? mergeObjs(olds, news) : news, opts.depth === true ? Infinity : +opts.depth);
|
|
173
|
+
} finally {
|
|
174
|
+
CTX.isCascading = prev;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
152
177
|
function mergeObjs(o1 = {}, o2 = {}) {
|
|
153
|
-
const merged = { ...o1
|
|
154
|
-
|
|
178
|
+
const merged = { ...o1 ||= {}, ...o2 ||= {} }, keys = Object.keys(merged);
|
|
179
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
180
|
+
const k = keys[i];
|
|
181
|
+
if (isObj(o1[k]) && isObj(o2[k])) merged[k] = mergeObjs(o1[k], o2[k]);
|
|
182
|
+
}
|
|
183
|
+
return merged;
|
|
155
184
|
}
|
|
156
185
|
function getTrailRecords(obj, path, reverse = false) {
|
|
157
186
|
const parts = path.split("."), chain = [["*", obj, obj]];
|
|
@@ -166,7 +195,11 @@ function deepClone(obj, config = NIL, seen = /* @__PURE__ */ new WeakMap()) {
|
|
|
166
195
|
const clone = config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {};
|
|
167
196
|
seen.set(obj, clone);
|
|
168
197
|
const keys = config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
169
|
-
for (let i = 0, len = keys.length; i < len; i++)
|
|
198
|
+
for (let i = 0, len = keys.length; i < len; i++)
|
|
199
|
+
try {
|
|
200
|
+
clone[keys[i]] = deepClone(obj[keys[i]], config, seen);
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
170
203
|
return clone;
|
|
171
204
|
}
|
|
172
205
|
function nuke(target) {
|
|
@@ -206,35 +239,34 @@ var ReactorEvent = class _ReactorEvent {
|
|
|
206
239
|
staticType;
|
|
207
240
|
/** Original event target context. */
|
|
208
241
|
target;
|
|
209
|
-
/** Root reactive object for this event wave. */
|
|
242
|
+
/** Root reactive object for this event instance wave. */
|
|
210
243
|
root;
|
|
211
|
-
/** Original target path for this event wave. */
|
|
244
|
+
/** Original target path for this event instance wave. */
|
|
212
245
|
path;
|
|
213
246
|
/** Current value at the event target path. */
|
|
214
247
|
value;
|
|
215
248
|
/** Previous value at the event target path. */
|
|
216
249
|
oldValue;
|
|
217
|
-
/** Whether resolve/reject intent semantics are allowed for this event. */
|
|
250
|
+
/** Whether resolve/reject intent semantics are allowed for this event instance. */
|
|
218
251
|
rejectable;
|
|
219
|
-
/** Whether this event wave can bubble back up to ancestors or just capture down. */
|
|
252
|
+
/** Whether this event instance wave can bubble back up to ancestors or just capture down. */
|
|
220
253
|
bubbles;
|
|
221
254
|
/**
|
|
222
|
-
* `DOMHighResTimeStamp` for this event payload for native event parity and accuracy.
|
|
255
|
+
* `DOMHighResTimeStamp` for this event instance payload for native event parity and accuracy.
|
|
223
256
|
* Enable `eventTimeStamps` option, then use this over custom timestamps in listeners for accuracy.
|
|
224
257
|
* */
|
|
225
258
|
timestamp;
|
|
226
|
-
|
|
259
|
+
/** The `Reactor` instance that dispatched this event instance. */
|
|
260
|
+
reactor;
|
|
227
261
|
_resolved = "";
|
|
228
262
|
_rejected = "";
|
|
229
263
|
_propagationStopped = false;
|
|
230
264
|
_immediatePropagationStopped = false;
|
|
231
265
|
/**
|
|
232
266
|
* @param payload Source payload for this event instance.
|
|
233
|
-
* @param
|
|
234
|
-
* @param canWarn Whether warning output is enabled.
|
|
235
|
-
* @param canStamp Whether timestamping is enabled.
|
|
267
|
+
* @param reactor The `Reactor` instance creating this event instance.
|
|
236
268
|
*/
|
|
237
|
-
constructor(payload,
|
|
269
|
+
constructor(payload, reactor) {
|
|
238
270
|
this.staticType = this.type = payload.type;
|
|
239
271
|
this.target = payload.target;
|
|
240
272
|
this.currentTarget = payload.currentTarget;
|
|
@@ -243,9 +275,9 @@ var ReactorEvent = class _ReactorEvent {
|
|
|
243
275
|
this.value = payload.target.value;
|
|
244
276
|
this.oldValue = payload.target.oldValue;
|
|
245
277
|
this.rejectable = payload.rejectable;
|
|
246
|
-
this.bubbles =
|
|
247
|
-
if (
|
|
248
|
-
|
|
278
|
+
this.bubbles = !!reactor.config.eventBubbling;
|
|
279
|
+
if (reactor.config.eventTimeStamps) this.timestamp = performance.now();
|
|
280
|
+
this.reactor = reactor;
|
|
249
281
|
}
|
|
250
282
|
/** Whether propagation has been stopped. */
|
|
251
283
|
get propagationStopped() {
|
|
@@ -275,9 +307,9 @@ var ReactorEvent = class _ReactorEvent {
|
|
|
275
307
|
* @example e.resolve("API Load successful"); // message
|
|
276
308
|
*/
|
|
277
309
|
resolve(message) {
|
|
278
|
-
if (!this.rejectable) return this.
|
|
279
|
-
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.
|
|
280
|
-
if (this.rejectable) this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"
|
|
310
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`resolve()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
311
|
+
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.reactor.log(`[ReactorEvent] Resolving an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
|
|
312
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`}`);
|
|
281
313
|
}
|
|
282
314
|
/** Rejection reason for rejectable events. */
|
|
283
315
|
get rejected() {
|
|
@@ -290,9 +322,9 @@ var ReactorEvent = class _ReactorEvent {
|
|
|
290
322
|
* @example e.resolve("User is not logged in"); // reason
|
|
291
323
|
*/
|
|
292
324
|
reject(reason) {
|
|
293
|
-
if (!this.rejectable) return this.
|
|
294
|
-
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.
|
|
295
|
-
if (this.rejectable) this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"
|
|
325
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`reject()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
326
|
+
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.reactor.log(`[ReactorEvent] Rejecting an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
|
|
327
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`}`);
|
|
296
328
|
}
|
|
297
329
|
/**
|
|
298
330
|
* Returns event path values from target to root.
|
|
@@ -301,24 +333,22 @@ var ReactorEvent = class _ReactorEvent {
|
|
|
301
333
|
composedPath() {
|
|
302
334
|
return getTrailRecords(this.root, this.path, true).map((r) => r[2]);
|
|
303
335
|
}
|
|
304
|
-
get canWarn() {
|
|
305
|
-
return this._warn !== NOOP;
|
|
306
|
-
}
|
|
307
336
|
};
|
|
308
337
|
|
|
309
338
|
// src/ts/core/reactor.ts
|
|
310
339
|
var Reactor = class {
|
|
340
|
+
/** Logger function for this reactor instance, override if desired, `this.canLog = false` resets. */
|
|
311
341
|
log = NOOP;
|
|
342
|
+
/** The core state object for this reactor instance. */
|
|
312
343
|
core;
|
|
313
344
|
// `?:`s | pay the ~800 byte price upfront for what u might never use
|
|
314
|
-
|
|
345
|
+
/** The modules being used by this reactor. */
|
|
346
|
+
modules;
|
|
347
|
+
/** Configuration options for this reactor instance. */
|
|
315
348
|
config;
|
|
349
|
+
/** Whether this reactor instance is currently batching updates, a window view into the engine timing */
|
|
316
350
|
isBatching = false;
|
|
317
351
|
// Async Batching
|
|
318
|
-
isCascading = false;
|
|
319
|
-
// Setter Cascading
|
|
320
|
-
isLogging = false;
|
|
321
|
-
// keeping track so API getter doesn't slow down internal iterations in any way
|
|
322
352
|
queue;
|
|
323
353
|
// Tasks to run after flush
|
|
324
354
|
batch;
|
|
@@ -385,7 +415,7 @@ var Reactor = class {
|
|
|
385
415
|
safeValue = value?.[RAW] || value;
|
|
386
416
|
unchanged = this.config.equalityFunction(safeValue, safeOldValue);
|
|
387
417
|
}
|
|
388
|
-
if (!indiffable && unchanged && !
|
|
418
|
+
if (!indiffable && unchanged && !CTX.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
|
|
389
419
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
390
420
|
if (this.setters) {
|
|
391
421
|
const wildcords = this.setters.get("*");
|
|
@@ -539,7 +569,7 @@ var Reactor = class {
|
|
|
539
569
|
if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
|
|
540
570
|
}
|
|
541
571
|
wave(path, payload) {
|
|
542
|
-
const e = new ReactorEvent(payload, this
|
|
572
|
+
const e = new ReactorEvent(payload, this), chain = getTrailRecords(this.core, path);
|
|
543
573
|
e.eventPhase = ReactorEvent.CAPTURING_PHASE;
|
|
544
574
|
for (let i = 0; i <= chain.length - 2; i++) {
|
|
545
575
|
if (e.propagationStopped) break;
|
|
@@ -613,8 +643,8 @@ var Reactor = class {
|
|
|
613
643
|
return depth;
|
|
614
644
|
}
|
|
615
645
|
getContext(path) {
|
|
616
|
-
const
|
|
617
|
-
return { path, value, key: path.slice(
|
|
646
|
+
const last = path.lastIndexOf("."), value = getAny(this.core, path), object = last === -1 ? this.core : getAny(this.core, path.slice(0, last));
|
|
647
|
+
return { path, value, key: path.slice(last + 1) || "", hadKey: true, object };
|
|
618
648
|
}
|
|
619
649
|
bindSignal(cord, sig) {
|
|
620
650
|
if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
|
|
@@ -630,7 +660,11 @@ var Reactor = class {
|
|
|
630
660
|
const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
|
|
631
661
|
seen.set(obj, clone);
|
|
632
662
|
const keys = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
633
|
-
for (let i = 0, len = keys.length; i < len; i++)
|
|
663
|
+
for (let i = 0, len = keys.length; i < len; i++)
|
|
664
|
+
try {
|
|
665
|
+
clone[keys[i]] = this.cloned(obj[keys[i]], raw, seen);
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
634
668
|
if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
|
|
635
669
|
return clone;
|
|
636
670
|
}
|
|
@@ -718,7 +752,7 @@ var Reactor = class {
|
|
|
718
752
|
* rtr.delete("cache.temp", () => TERMINATOR);
|
|
719
753
|
*/
|
|
720
754
|
delete(path, callback, options) {
|
|
721
|
-
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path
|
|
755
|
+
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path));
|
|
722
756
|
}
|
|
723
757
|
/** Registers a delete mediator for a path that only triggers once. */
|
|
724
758
|
donce(path, callback, options) {
|
|
@@ -735,7 +769,7 @@ var Reactor = class {
|
|
|
735
769
|
}
|
|
736
770
|
/**
|
|
737
771
|
* Registers a watcher for a path.
|
|
738
|
-
* Watch callbacks run synchronously with the operation.
|
|
772
|
+
* Watch callbacks run synchronously with the operation, use leaf paths for reliability as it sees exact sets; no bubbling here.
|
|
739
773
|
* @param path Path or wildcard path.
|
|
740
774
|
* @param callback Watch callback.
|
|
741
775
|
* @param options Sync options.
|
|
@@ -784,7 +818,7 @@ var Reactor = class {
|
|
|
784
818
|
cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options), lDepth: depth !== void 0 ? this.getDepth(path) : depth };
|
|
785
819
|
if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
|
|
786
820
|
const target = this.getContext(path);
|
|
787
|
-
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this
|
|
821
|
+
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this));
|
|
788
822
|
}
|
|
789
823
|
(cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
|
|
790
824
|
return this.bindSignal(cord, signal);
|
|
@@ -815,56 +849,39 @@ var Reactor = class {
|
|
|
815
849
|
return this.cloned(arguments.length < 2 ? this.core : branch, raw);
|
|
816
850
|
}
|
|
817
851
|
/**
|
|
818
|
-
*
|
|
819
|
-
* @param
|
|
820
|
-
* @param
|
|
821
|
-
* @
|
|
822
|
-
* rtr.on("user", (event) => rtr.cascade(event));
|
|
823
|
-
* @example
|
|
824
|
-
* rtr.watch("user", (_value, payload) => rtr.cascade(payload));
|
|
825
|
-
*/
|
|
826
|
-
cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objectSafe = true) {
|
|
827
|
-
if (type !== "set" && type !== "delete" || !canHandle(news, this.config) || (objectSafe ? !canHandle(olds, this.config) : false)) return;
|
|
828
|
-
const obj = objectSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
|
|
829
|
-
this.isCascading = true;
|
|
830
|
-
for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path === "*" ? keys[i] : path + "." + keys[i], obj[keys[i]]);
|
|
831
|
-
this.isCascading = false;
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Installs a plugin instance.
|
|
835
|
-
* @param plugin Plugin instance.
|
|
836
|
-
* @returns Current reactor for fluent chaining.
|
|
852
|
+
* Installs a module instance.
|
|
853
|
+
* @param target Module instance.
|
|
854
|
+
* @param id Optional identification tag for this instance in the module.
|
|
855
|
+
* @returns Current `Reactor` instance for fluent chaining.
|
|
837
856
|
*/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
this.plugins?.get(name)?.destroy();
|
|
841
|
-
return (this.plugins ??= /* @__PURE__ */ new Map()).set(name, (plugin.setup(this), plugin)), this;
|
|
857
|
+
use(target, id) {
|
|
858
|
+
return (this.modules ??= /* @__PURE__ */ new Set()).add(target.setup(this, id)), this;
|
|
842
859
|
}
|
|
843
|
-
/** Resets
|
|
860
|
+
/** Resets this reactor instance to its initial state. */
|
|
844
861
|
reset() {
|
|
845
862
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
|
|
846
863
|
this.batch?.clear(), this.queue?.clear(), this.isBatching = false;
|
|
847
864
|
}
|
|
848
865
|
destroy() {
|
|
849
|
-
if (this.
|
|
866
|
+
if (this.modules) for (const mdle of this.modules) mdle.destroy();
|
|
850
867
|
this.reset(), nuke(this);
|
|
851
868
|
}
|
|
852
869
|
get canLog() {
|
|
853
|
-
return this.
|
|
870
|
+
return this.log !== NOOP;
|
|
854
871
|
}
|
|
855
872
|
set canLog(value) {
|
|
856
|
-
this.log =
|
|
873
|
+
this.log = value ? RTR_LOG : NOOP;
|
|
857
874
|
}
|
|
858
|
-
get
|
|
859
|
-
return this.config.
|
|
875
|
+
get canLineageTrace() {
|
|
876
|
+
return this.config.lineageTracing && this.config.referenceTracking;
|
|
860
877
|
}
|
|
861
878
|
get canSmartClone() {
|
|
862
|
-
return this.config.
|
|
879
|
+
return this.config.smartCloning && this.config.referenceTracking;
|
|
863
880
|
}
|
|
864
881
|
};
|
|
865
882
|
|
|
866
883
|
// src/ts/core/mixins.ts
|
|
867
|
-
var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "
|
|
884
|
+
var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
|
|
868
885
|
function reactive(target, build, preferences = NIL) {
|
|
869
886
|
if ("__Reactor__" in target) return target;
|
|
870
887
|
const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
|
|
@@ -872,7 +889,7 @@ function reactive(target, build, preferences = NIL) {
|
|
|
872
889
|
let key = methods[i];
|
|
873
890
|
if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
|
|
874
891
|
else if (preferences.whitelist?.includes(key)) continue;
|
|
875
|
-
descriptors[key] = { value: rtr[
|
|
892
|
+
descriptors[key] = { value: rtr[methods[i]].bind(rtr), ...locks };
|
|
876
893
|
}
|
|
877
894
|
descriptors["__Reactor__"] = { value: rtr, ...locks };
|
|
878
895
|
return Object.defineProperties(rtr.core, descriptors), rtr.core;
|
|
@@ -909,47 +926,75 @@ function guardMethod(fn, onError = (e) => console.error(e)) {
|
|
|
909
926
|
});
|
|
910
927
|
}
|
|
911
928
|
|
|
912
|
-
// src/ts/
|
|
913
|
-
var
|
|
914
|
-
|
|
929
|
+
// src/ts/modules/base.ts
|
|
930
|
+
var wpArr = ["*"];
|
|
931
|
+
var BaseReactorModule = class {
|
|
932
|
+
static moduleName;
|
|
915
933
|
get name() {
|
|
916
|
-
return this.constructor.
|
|
934
|
+
return this.constructor.moduleName;
|
|
917
935
|
}
|
|
918
936
|
ac = new AbortController();
|
|
919
937
|
signal = this.ac.signal;
|
|
920
|
-
|
|
938
|
+
rtrs = /* @__PURE__ */ new Map();
|
|
939
|
+
rids = /* @__PURE__ */ new WeakMap();
|
|
940
|
+
// for quick 0(1) lookups over iteration
|
|
941
|
+
wired = false;
|
|
921
942
|
config;
|
|
922
943
|
state;
|
|
923
944
|
constructor(config, rtr, state) {
|
|
924
945
|
guardAllMethods(this, this.guard);
|
|
925
|
-
this.rtr = rtr;
|
|
926
946
|
this.config = isObj(config) ? reactive(config) : config;
|
|
927
947
|
this.state = isObj(state) ? reactive(state) : state;
|
|
948
|
+
rtr && this.attach(rtr);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Connect to a `Reactor` instance, allows managing multiple reactors if needed.
|
|
952
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
953
|
+
* @param id Optional custom id for the reactor, prefer over default implicit index id when managing multiple reactors, supports paths to merge into a single tree.
|
|
954
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
955
|
+
* @example
|
|
956
|
+
* const mod = new MyModule().attach(state1).attach(state2); // implicit index-based ids by default, add a .setup() or `Reactor.use()` when ready for init.
|
|
957
|
+
* @example
|
|
958
|
+
* const persist = new PersistModule(config).attach(sessState, "session").attach(adminState, "session.admin"); // don't use "*", causes de-serialization issues.
|
|
959
|
+
*/
|
|
960
|
+
attach(target, id = this.rtrs.size) {
|
|
961
|
+
const rtr = getReactor(target);
|
|
962
|
+
if (!rtr || this.rtrs.has(id)) return this;
|
|
963
|
+
return this.rids.set((this.rtrs.set(id, rtr), rtr), id), this.onAttach(rtr, id), this;
|
|
928
964
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
965
|
+
onAttach(_rtr, _rid) {
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Entry point called to initialize module wiring, calls `.attach(target, id)` first, `Reactor.use()` calls this internally.
|
|
969
|
+
* Should run as last in `.attach()` chain or after all desired reactors if using multiple; so wiring is done safely after.
|
|
970
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
971
|
+
* @param id Optional id for the reactor, prefer over default implicit index id when managing multiple reactors.
|
|
972
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
973
|
+
* @example
|
|
974
|
+
* const mod = new MyModule().attach(state1).setup(state2); // if using multiple, this should run last; with same params as `.attach()` for a shorter chain
|
|
975
|
+
*/
|
|
976
|
+
setup(target, id) {
|
|
977
|
+
return this.attach(target, id), !this.wired && (this.wire(), this.wired = true), this;
|
|
933
978
|
}
|
|
934
979
|
destroy() {
|
|
935
980
|
this.ac.abort();
|
|
936
981
|
this.onDestroy?.();
|
|
937
982
|
}
|
|
938
983
|
/**
|
|
939
|
-
* Wraps a function with
|
|
984
|
+
* Wraps a function with module-scoped error logging.
|
|
940
985
|
* Use this when creating functions dynamically (for example, before attaching an anonymous listener on the fly).
|
|
941
986
|
* @example
|
|
942
987
|
* window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
|
|
943
988
|
*/
|
|
944
989
|
guard = (fn) => {
|
|
945
|
-
return guardMethod(fn, (e) => this.
|
|
990
|
+
return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
|
|
946
991
|
};
|
|
947
992
|
// `()=>{}`: needs to be bounded even before initialization
|
|
948
993
|
};
|
|
949
994
|
|
|
950
995
|
// src/ts/utils/store.ts
|
|
951
996
|
var BaseStorageAdapter = class {
|
|
952
|
-
|
|
997
|
+
name = "StorageAdapter";
|
|
953
998
|
config;
|
|
954
999
|
warn = (act = "", mssg = "Support issue or Private Mode", key = "", store = "") => this.config.debug && console.warn(`[${this.constructor.name} \`${act}\`] Failed${key ? `for ${key}` : ""} ${store ? ` on "${store}"` : ""} ${this.config.dbName ? ` at ${this.config.dbName}` : ""} (${mssg})`);
|
|
955
1000
|
constructor(config) {
|
|
@@ -957,26 +1002,44 @@ var BaseStorageAdapter = class {
|
|
|
957
1002
|
}
|
|
958
1003
|
};
|
|
959
1004
|
var StorageAdapter = class extends BaseStorageAdapter {
|
|
1005
|
+
name = "SyncStorageAdapter";
|
|
960
1006
|
};
|
|
961
1007
|
var AsyncStorageAdapter = class extends BaseStorageAdapter {
|
|
1008
|
+
name = "AsyncStorageAdapter";
|
|
962
1009
|
};
|
|
963
1010
|
var LocalStorageAdapter = class extends StorageAdapter {
|
|
964
|
-
|
|
965
|
-
|
|
1011
|
+
name = "LocalStorage";
|
|
1012
|
+
/**
|
|
1013
|
+
* Reads and parses a value from localStorage.
|
|
1014
|
+
* @param key Storage key.
|
|
1015
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1016
|
+
*/
|
|
1017
|
+
get(key, reviver = this.config.reviver) {
|
|
966
1018
|
try {
|
|
967
1019
|
const v = localStorage.getItem(key);
|
|
968
|
-
return v ? JSON.parse(v) : void 0;
|
|
1020
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
969
1021
|
} catch {
|
|
970
1022
|
return void 0;
|
|
971
1023
|
}
|
|
972
1024
|
}
|
|
973
|
-
|
|
1025
|
+
/**
|
|
1026
|
+
* Serializes and writes a value to localStorage.
|
|
1027
|
+
* @param key Storage key.
|
|
1028
|
+
* @param value Value to serialize.
|
|
1029
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1030
|
+
*/
|
|
1031
|
+
set(key, value, replacer = this.config.replacer) {
|
|
974
1032
|
try {
|
|
975
|
-
return localStorage.setItem(key, JSON.stringify(value)), true;
|
|
1033
|
+
return localStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
976
1034
|
} catch (e) {
|
|
977
1035
|
return this.warn("setItem", void 0, key), false;
|
|
978
1036
|
}
|
|
979
1037
|
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Removes a single key from localStorage.
|
|
1040
|
+
* @param key Storage key.
|
|
1041
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1042
|
+
*/
|
|
980
1043
|
remove(key) {
|
|
981
1044
|
try {
|
|
982
1045
|
return localStorage.removeItem(key), true;
|
|
@@ -984,6 +1047,10 @@ var LocalStorageAdapter = class extends StorageAdapter {
|
|
|
984
1047
|
return this.warn("removeItem", void 0, key), false;
|
|
985
1048
|
}
|
|
986
1049
|
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Clears all localStorage entries for the current origin.
|
|
1052
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1053
|
+
*/
|
|
987
1054
|
clear() {
|
|
988
1055
|
try {
|
|
989
1056
|
return localStorage.clear(), true;
|
|
@@ -992,25 +1059,94 @@ var LocalStorageAdapter = class extends StorageAdapter {
|
|
|
992
1059
|
}
|
|
993
1060
|
}
|
|
994
1061
|
};
|
|
995
|
-
var
|
|
1062
|
+
var SessionStorageAdapter = class extends StorageAdapter {
|
|
1063
|
+
name = "SessionStorage";
|
|
1064
|
+
/**
|
|
1065
|
+
* Reads and parses a value from sessionStorage.
|
|
1066
|
+
* @param key Storage key.
|
|
1067
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1068
|
+
*/
|
|
1069
|
+
get(key, reviver = this.config.reviver) {
|
|
1070
|
+
try {
|
|
1071
|
+
const v = sessionStorage.getItem(key);
|
|
1072
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1073
|
+
} catch {
|
|
1074
|
+
return void 0;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Serializes and writes a value to sessionStorage.
|
|
1079
|
+
* @param key Storage key.
|
|
1080
|
+
* @param value Value to serialize.
|
|
1081
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1082
|
+
*/
|
|
1083
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1084
|
+
try {
|
|
1085
|
+
return sessionStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
1086
|
+
} catch (e) {
|
|
1087
|
+
return this.warn("setItem", void 0, key), false;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Removes a single key from sessionStorage.
|
|
1092
|
+
* @param key Storage key.
|
|
1093
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1094
|
+
*/
|
|
1095
|
+
remove(key) {
|
|
1096
|
+
try {
|
|
1097
|
+
return sessionStorage.removeItem(key), true;
|
|
1098
|
+
} catch (e) {
|
|
1099
|
+
return this.warn("removeItem", void 0, key), false;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Clears all sessionStorage entries for the current tab session.
|
|
1104
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1105
|
+
*/
|
|
1106
|
+
clear() {
|
|
1107
|
+
try {
|
|
1108
|
+
return sessionStorage.clear(), true;
|
|
1109
|
+
} catch (e) {
|
|
1110
|
+
return this.warn("clear", void 0), false;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
var MemoryAdapter = class extends StorageAdapter {
|
|
1115
|
+
name = "Memory";
|
|
996
1116
|
constructor(build) {
|
|
997
1117
|
super({ store: /* @__PURE__ */ new Map(), ...build });
|
|
998
1118
|
}
|
|
999
|
-
|
|
1119
|
+
/**
|
|
1120
|
+
* Reads and parses a value from memory storage.
|
|
1121
|
+
* @param key Storage key.
|
|
1122
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1123
|
+
*/
|
|
1124
|
+
get(key, reviver = this.config.reviver) {
|
|
1000
1125
|
try {
|
|
1001
1126
|
const v = this.config.store.get(key);
|
|
1002
|
-
return v ? JSON.parse(v) : void 0;
|
|
1127
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1003
1128
|
} catch {
|
|
1004
1129
|
return void 0;
|
|
1005
1130
|
}
|
|
1006
1131
|
}
|
|
1007
|
-
|
|
1132
|
+
/**
|
|
1133
|
+
* Serializes and writes a value to memory storage.
|
|
1134
|
+
* @param key Storage key.
|
|
1135
|
+
* @param value Value to serialize.
|
|
1136
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1137
|
+
*/
|
|
1138
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1008
1139
|
try {
|
|
1009
|
-
return this.config.store.set(key, JSON.stringify(value)), true;
|
|
1140
|
+
return this.config.store.set(key, JSON.stringify(value, replacer)), true;
|
|
1010
1141
|
} catch (e) {
|
|
1011
1142
|
return this.warn("set", void 0, key), false;
|
|
1012
1143
|
}
|
|
1013
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Removes a single key from memory storage.
|
|
1147
|
+
* @param key Storage key.
|
|
1148
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1149
|
+
*/
|
|
1014
1150
|
remove(key) {
|
|
1015
1151
|
try {
|
|
1016
1152
|
return this.config.store.delete(key), true;
|
|
@@ -1018,6 +1154,10 @@ var MemoryStorageAdapter = class extends StorageAdapter {
|
|
|
1018
1154
|
return this.warn("remove", void 0, key), false;
|
|
1019
1155
|
}
|
|
1020
1156
|
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Clears all entries from memory storage.
|
|
1159
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1160
|
+
*/
|
|
1021
1161
|
clear() {
|
|
1022
1162
|
try {
|
|
1023
1163
|
return this.config.store.clear(), true;
|
|
@@ -1026,23 +1166,100 @@ var MemoryStorageAdapter = class extends StorageAdapter {
|
|
|
1026
1166
|
}
|
|
1027
1167
|
}
|
|
1028
1168
|
};
|
|
1169
|
+
var CookieAdapter = class extends StorageAdapter {
|
|
1170
|
+
name = "Cookie";
|
|
1171
|
+
deets = (opts = NIL, _d = opts.domain ?? this.config.domain, _m = opts.maxAge ?? this.config.maxAge, _e = opts.expires ?? this.config.expires) => `Path=${opts.path ?? this.config.path}; SameSite=${opts.sameSite ?? this.config.sameSite}${_d ? `; Domain=${_d}` : ""}${opts.secure ?? this.config.secure ? "; Secure" : ""}${_m !== void 0 ? `; Max-Age=${_m}` : ""}${_e !== void 0 ? `; Expires=${_e instanceof Date ? _e.toUTCString() : _e}` : ""}`;
|
|
1172
|
+
constructor(build) {
|
|
1173
|
+
super({ secure: "undefined" !== typeof window && location.protocol === "https:", ...COOKIE_ADAPTER_BUILD, ...build });
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Reads and parses a cookie visible to the current page scope.
|
|
1177
|
+
* @param key Cookie key.
|
|
1178
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1179
|
+
*/
|
|
1180
|
+
get(key, reviver = this.config.reviver) {
|
|
1181
|
+
try {
|
|
1182
|
+
const k = encodeURIComponent(key) + "=";
|
|
1183
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1184
|
+
if (!pair.startsWith(k)) continue;
|
|
1185
|
+
return JSON.parse(decodeURIComponent(pair.slice(k.length)), reviver);
|
|
1186
|
+
}
|
|
1187
|
+
return void 0;
|
|
1188
|
+
} catch {
|
|
1189
|
+
return void 0;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Writes a cookie with optional per-call scope/lifetime overrides.
|
|
1194
|
+
* @param key Cookie key.
|
|
1195
|
+
* @param value Value to serialize.
|
|
1196
|
+
* @param opts Optional per-call cookie options.
|
|
1197
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1198
|
+
*/
|
|
1199
|
+
set(key, value, opts = NIL, replacer = this.config.replacer) {
|
|
1200
|
+
try {
|
|
1201
|
+
return document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value, replacer))}; ${this.deets(opts)}`, true;
|
|
1202
|
+
} catch {
|
|
1203
|
+
return this.warn("set", void 0, key), false;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Removes a cookie key using matching scope attributes.
|
|
1208
|
+
* @param key Cookie key.
|
|
1209
|
+
* @param opts Optional per-call scope overrides.
|
|
1210
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1211
|
+
*/
|
|
1212
|
+
remove(key, opts = NIL) {
|
|
1213
|
+
try {
|
|
1214
|
+
return document.cookie = `${encodeURIComponent(key)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`, true;
|
|
1215
|
+
} catch {
|
|
1216
|
+
return this.warn("remove", void 0, key), false;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* Attempts to remove all visible cookie keys for the given scope.
|
|
1221
|
+
* @param opts Optional per-call scope overrides.
|
|
1222
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1223
|
+
*/
|
|
1224
|
+
clear(opts = NIL) {
|
|
1225
|
+
try {
|
|
1226
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1227
|
+
const idx = pair.indexOf("=");
|
|
1228
|
+
document.cookie = `${idx === -1 ? pair : pair.slice(0, idx)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`;
|
|
1229
|
+
}
|
|
1230
|
+
return true;
|
|
1231
|
+
} catch {
|
|
1232
|
+
return this.warn("clear"), false;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1029
1236
|
var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
1237
|
+
name = "IndexedDB";
|
|
1030
1238
|
db;
|
|
1031
|
-
warn = (act = "", mssg = "Support issue or Private Mode", store = "", key = "") => console.warn(`[IndexedDB \`${act}\`] Failed${store ? ` for ${key} on "${store}"` : ""} at ${this.config.dbName} (${mssg})`);
|
|
1032
1239
|
constructor(build) {
|
|
1033
1240
|
super({ ...INDEXED_DB_ADAPTER_BUILD, ...build });
|
|
1034
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Returns a connected IndexedDB instance, opening it when needed.
|
|
1244
|
+
* @returns Connected database handle.
|
|
1245
|
+
*/
|
|
1035
1246
|
async idb() {
|
|
1036
1247
|
const idb = this.config.onidb();
|
|
1037
1248
|
if (idb || this.db) return Promise.resolve(idb || this.db);
|
|
1038
1249
|
return new Promise((res, rej) => {
|
|
1039
1250
|
const req = indexedDB.open(this.config.dbName, this.config.version);
|
|
1040
1251
|
req.onupgradeneeded = (e) => (this.config.onupgradeneeded(req.result, e), this.config.stores.forEach((s) => !req.result.objectStoreNames.contains(s) && req.result.createObjectStore(s)));
|
|
1041
|
-
req.onsuccess = (e) => (this.config.onsuccess(req.result, e), req.result.onversionchange = (e2) => (this.warn("update", "Updated in another tab"),
|
|
1252
|
+
req.onsuccess = (e) => (this.config.onsuccess(req.result, e), req.result.onversionchange = (e2) => (this.config.onversionchange(req.result, e2), this.warn("update", "Updated in another tab"), req.result.close()), res(this.db = req.result));
|
|
1042
1253
|
req.onerror = (e) => (this.config.onerror(req.error, e), this.warn("open", "Something went wrong"), rej(req.error));
|
|
1043
1254
|
req.onblocked = (e) => (this.config.onblocked(e), this.warn("open", "Close other tabs for updates"));
|
|
1044
1255
|
});
|
|
1045
1256
|
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Reads a value by key from an object store.
|
|
1259
|
+
* @param key Record key.
|
|
1260
|
+
* @param store Optional object-store override.
|
|
1261
|
+
* @returns Stored value, or `undefined` when missing/unreadable.
|
|
1262
|
+
*/
|
|
1046
1263
|
async get(key, store = this.config.stores[0]) {
|
|
1047
1264
|
try {
|
|
1048
1265
|
const req = (await this.idb()).transaction(store).objectStore(store).get(key);
|
|
@@ -1051,6 +1268,13 @@ var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
|
1051
1268
|
return this.warn("get", void 0, store), void 0;
|
|
1052
1269
|
}
|
|
1053
1270
|
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Writes a value by key into an object store.
|
|
1273
|
+
* @param key Record key.
|
|
1274
|
+
* @param value Value to store.
|
|
1275
|
+
* @param store Optional object-store override.
|
|
1276
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1277
|
+
*/
|
|
1054
1278
|
async set(key, value, store = this.config.stores[0]) {
|
|
1055
1279
|
try {
|
|
1056
1280
|
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).put(value, key);
|
|
@@ -1059,6 +1283,12 @@ var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
|
1059
1283
|
return this.warn("put", void 0, store), false;
|
|
1060
1284
|
}
|
|
1061
1285
|
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Deletes a value by key from an object store.
|
|
1288
|
+
* @param key Record key.
|
|
1289
|
+
* @param store Optional object-store override.
|
|
1290
|
+
* @returns `true` when delete succeeds, else `false`.
|
|
1291
|
+
*/
|
|
1062
1292
|
async remove(key, store = this.config.stores[0]) {
|
|
1063
1293
|
try {
|
|
1064
1294
|
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).delete(key);
|
|
@@ -1067,6 +1297,11 @@ var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
|
1067
1297
|
return this.warn("delete", void 0, store), false;
|
|
1068
1298
|
}
|
|
1069
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Clears one or more object stores.
|
|
1302
|
+
* @param stores Store name or list of store names to clear.
|
|
1303
|
+
* @returns `true` when all clears succeed, else `false`.
|
|
1304
|
+
*/
|
|
1070
1305
|
async clear(stores = this.config.stores) {
|
|
1071
1306
|
let success = true;
|
|
1072
1307
|
for (const store of Array.isArray(stores) ? stores : [stores])
|
|
@@ -1079,6 +1314,7 @@ var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
|
1079
1314
|
return success;
|
|
1080
1315
|
}
|
|
1081
1316
|
};
|
|
1317
|
+
var COOKIE_ADAPTER_BUILD = { path: "/", sameSite: "Lax", domain: void 0, debug: false };
|
|
1082
1318
|
var INDEXED_DB_ADAPTER_BUILD = { dbName: "REACTOR_IDB", stores: ["VAULT"], version: 1, onidb: NOOP, onupgradeneeded: NOOP, onversionchange: NOOP, onsuccess: NOOP, onerror: NOOP, onblocked: NOOP };
|
|
1083
1319
|
|
|
1084
1320
|
// src/ts/utils/fn.ts
|
|
@@ -1091,93 +1327,123 @@ function setTimeout2(handler, timeout, ...args) {
|
|
|
1091
1327
|
return sig.addEventListener("abort", kill, { once: true }), id;
|
|
1092
1328
|
}
|
|
1093
1329
|
|
|
1094
|
-
// src/ts/
|
|
1095
|
-
var
|
|
1096
|
-
static
|
|
1330
|
+
// src/ts/modules/persist.ts
|
|
1331
|
+
var PersistModule = class extends BaseReactorModule {
|
|
1332
|
+
static moduleName = "persist";
|
|
1097
1333
|
adapter;
|
|
1334
|
+
hydrateSeq = 0;
|
|
1098
1335
|
saveTimeoutId = 0;
|
|
1099
1336
|
get payload() {
|
|
1100
|
-
|
|
1101
|
-
|
|
1337
|
+
let res = this.rtrs.size > 1 ? {} : void 0;
|
|
1338
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1339
|
+
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;
|
|
1340
|
+
this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
|
|
1341
|
+
}
|
|
1342
|
+
return res;
|
|
1102
1343
|
}
|
|
1103
1344
|
constructor(config, rtr) {
|
|
1104
|
-
super({ ...
|
|
1345
|
+
super({ ...PERSIST_MODULE_BUILD, ...config }, rtr, { hydrated: false });
|
|
1105
1346
|
}
|
|
1106
1347
|
wire() {
|
|
1107
1348
|
"undefined" !== typeof window && window.addEventListener("pagehide", this.onDestroy, { signal: this.signal });
|
|
1108
1349
|
"undefined" !== typeof document && document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && this.onDestroy(), { signal: this.signal });
|
|
1109
|
-
this.config.on("adapter", this.
|
|
1110
|
-
this.config.on("disabled", this.
|
|
1111
|
-
this.config.on("paths", this.
|
|
1350
|
+
this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
|
|
1351
|
+
this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
|
|
1352
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1112
1353
|
}
|
|
1113
|
-
|
|
1354
|
+
onAttach(rtr) {
|
|
1355
|
+
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);
|
|
1356
|
+
}
|
|
1357
|
+
async handleAdapter({ value = LocalStorageAdapter }) {
|
|
1358
|
+
const seq = ++this.hydrateSeq;
|
|
1114
1359
|
if (this.adapter && value === this.adapter.constructor) return;
|
|
1360
|
+
this.state.hydrated = false;
|
|
1115
1361
|
this.adapter?.remove(this.config.key);
|
|
1116
|
-
this.adapter = "function" === typeof value ? new value({ debug: this.
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1362
|
+
this.adapter = "function" === typeof value ? new value({ debug: !!this.rtrs.values().next().value?.canLog }) : value;
|
|
1363
|
+
try {
|
|
1364
|
+
let saved = this.adapter.get(this.config.key);
|
|
1365
|
+
const isAsync = saved instanceof Promise, depth = this.config.fanout ?? isAsync;
|
|
1366
|
+
saved = !isAsync ? saved : await saved;
|
|
1367
|
+
if (seq !== this.hydrateSeq || !saved) return;
|
|
1368
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1369
|
+
const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
|
|
1370
|
+
if (!entry) continue;
|
|
1371
|
+
const set = (p, news, olds) => (depth ? fanout : setAny)(rtr.core, p, isPOJO(news, rtr.config) && isPOJO(olds, rtr.config) ? mergeObjs(news, olds) : olds, depth ? { depth } : void 0);
|
|
1372
|
+
for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
|
|
1373
|
+
}
|
|
1374
|
+
for (const rtr of this.rtrs.values()) rtr.tick(!depth ? this.config.paths ?? "*" : "*");
|
|
1375
|
+
} catch {
|
|
1376
|
+
} finally {
|
|
1377
|
+
if (seq === this.hydrateSeq) this.state.hydrated = true;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
handleDisabled({ value }) {
|
|
1381
|
+
for (const rtr of this.rtrs.values()) this.onAttach(rtr);
|
|
1126
1382
|
value && this.adapter?.remove(this.config.key);
|
|
1127
1383
|
}
|
|
1128
|
-
|
|
1129
|
-
for (const
|
|
1130
|
-
|
|
1384
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1385
|
+
for (const rtr of this.rtrs.values()) {
|
|
1386
|
+
for (const p of prevs) rtr.off(p, this.handleSave);
|
|
1387
|
+
for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
|
|
1388
|
+
}
|
|
1131
1389
|
}
|
|
1132
|
-
|
|
1390
|
+
handleSave(e) {
|
|
1391
|
+
if (!this.state.hydrated) return e.stopImmediatePropagation();
|
|
1133
1392
|
if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout2(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
|
|
1134
1393
|
}
|
|
1135
|
-
/** Clears persisted payload for this
|
|
1394
|
+
/** Clears persisted payload for this module instance and drops any pending save. */
|
|
1136
1395
|
clear() {
|
|
1137
1396
|
clearTimeout(this.saveTimeoutId);
|
|
1138
|
-
this.saveTimeoutId = -1
|
|
1397
|
+
this.saveTimeoutId = -1;
|
|
1398
|
+
for (const rtr of this.rtrs.values()) rtr.stall(() => this.saveTimeoutId = 0);
|
|
1139
1399
|
this.adapter?.remove(this.config.key);
|
|
1140
1400
|
}
|
|
1141
1401
|
onDestroy() {
|
|
1142
|
-
!this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1402
|
+
this.state.hydrated && !this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1143
1403
|
}
|
|
1144
1404
|
};
|
|
1145
|
-
var
|
|
1405
|
+
var PERSIST_MODULE_BUILD = { disabled: false, key: "REACTOR_STORE", throttle: 2500, useSnapshot: false };
|
|
1146
1406
|
|
|
1147
1407
|
// src/ts/utils/num.ts
|
|
1148
1408
|
function clamp(min = 0, val, max = Infinity) {
|
|
1149
1409
|
return Math.min(Math.max(val, min), max);
|
|
1150
1410
|
}
|
|
1151
1411
|
|
|
1152
|
-
// src/ts/
|
|
1153
|
-
var
|
|
1154
|
-
static
|
|
1412
|
+
// src/ts/modules/timeTravel.ts
|
|
1413
|
+
var TimeTravelModule = class extends BaseReactorModule {
|
|
1414
|
+
static moduleName = "timeTravel";
|
|
1155
1415
|
lastTimestamp = 0;
|
|
1156
1416
|
playbackTimeoutId = -1;
|
|
1157
1417
|
constructor(config, rtr) {
|
|
1158
|
-
super({ ...
|
|
1418
|
+
super({ ...TIME_TRAVEL_MODULE_BUILD, ...config }, rtr, { initialState: {}, history: [], currentFrame: 0, paused: true });
|
|
1159
1419
|
}
|
|
1160
1420
|
// ===========================================================================
|
|
1161
1421
|
// THE FOUNDATION & WIRETAP (Passive Recording)
|
|
1162
1422
|
// ===========================================================================
|
|
1163
1423
|
wire() {
|
|
1164
|
-
this.rtr.config.referenceTracking = this.rtr.config.smartCloning = this.rtr.config.eventTimeStamps = true;
|
|
1165
|
-
if (!this.state.history.length || this.state.initialState == null) this.state.initialState = this.rtr.snapshot();
|
|
1166
1424
|
this.lastTimestamp = performance.now();
|
|
1167
1425
|
this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
|
|
1168
|
-
this.config.on("paths", this.
|
|
1426
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1169
1427
|
!this.state.paused && this.play();
|
|
1170
1428
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1429
|
+
onAttach(rtr, rid) {
|
|
1430
|
+
rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
|
|
1431
|
+
if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
|
|
1432
|
+
for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
|
|
1433
|
+
}
|
|
1434
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1435
|
+
for (const rtr of this.rtrs.values()) {
|
|
1436
|
+
for (const p of prevs) rtr.off(p, this.record);
|
|
1437
|
+
for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
|
|
1438
|
+
}
|
|
1174
1439
|
}
|
|
1175
1440
|
/** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
|
|
1176
|
-
record(e) {
|
|
1441
|
+
record(e, rid = this.rids.get(e.reactor)) {
|
|
1177
1442
|
if (!this.state.paused) return;
|
|
1178
1443
|
if (this.state.currentFrame < this.state.history.length) this.state.history = this.state.history.slice(0, this.state.currentFrame);
|
|
1179
1444
|
if (this.state.history.length >= this.config.maxHistoryLength) this.state.history = this.state.history.slice(1);
|
|
1180
|
-
this.state.history.push({ path: e.target.path, value:
|
|
1445
|
+
this.state.history.push({ path: e.target.path, value: e.reactor.snapshot(false, e.target.value), oldValue: e.reactor.snapshot(false, e.target.oldValue), type: e.staticType, hadKey: e.target.hadKey, deltat: e.timestamp - this.lastTimestamp, rid });
|
|
1446
|
+
if (e.rejected) this.state.history[this.state.history.length - 1].rejected = e.rejected;
|
|
1181
1447
|
this.state.currentFrame = this.state.history.length;
|
|
1182
1448
|
this.lastTimestamp = e.timestamp;
|
|
1183
1449
|
}
|
|
@@ -1186,7 +1452,7 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
1186
1452
|
this.pause();
|
|
1187
1453
|
this.playbackTimeoutId = -1;
|
|
1188
1454
|
this.state.history.length = this.state.currentFrame = 0;
|
|
1189
|
-
this.state.initialState = this.rtr.snapshot();
|
|
1455
|
+
this.state.initialState = Object.fromEntries(this.rtrs.entries().map(([rid, rtr]) => [rid, rtr.snapshot()]));
|
|
1190
1456
|
this.lastTimestamp = performance.now();
|
|
1191
1457
|
}
|
|
1192
1458
|
// ===========================================================================
|
|
@@ -1199,12 +1465,13 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
1199
1465
|
while (this.state.currentFrame !== target) {
|
|
1200
1466
|
const e = this.state.history[forward ? this.state.currentFrame : this.state.currentFrame - 1];
|
|
1201
1467
|
if (!e) break;
|
|
1202
|
-
|
|
1203
|
-
|
|
1468
|
+
const rtr = this.rtrs.get(e.rid) || this.rtrs.values().next().value;
|
|
1469
|
+
if (forward) e.type === "delete" ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.value, rtr.config));
|
|
1470
|
+
else !e.hadKey ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.oldValue, rtr.config));
|
|
1204
1471
|
forward ? this.state.currentFrame++ : this.state.currentFrame--;
|
|
1205
|
-
if (e.rejected)
|
|
1472
|
+
if (e.rejected) rtr.log(`[Reactor ${this.name} Module] ${forward ? "Replaying" : "Reversing"} REJECTED intent at "${e.path}"`);
|
|
1206
1473
|
}
|
|
1207
|
-
this.rtr.tick();
|
|
1474
|
+
for (const rtr of this.rtrs.values()) rtr.tick();
|
|
1208
1475
|
if (!keepShield) this.state.paused = true;
|
|
1209
1476
|
}
|
|
1210
1477
|
/** Step through time, Moves the playhead and teleports the state. */
|
|
@@ -1223,9 +1490,9 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
1223
1490
|
async automove(forward = true) {
|
|
1224
1491
|
this.state.paused = false;
|
|
1225
1492
|
while ((forward ? this.state.currentFrame < this.state.history.length : this.state.currentFrame > 0) && !this.state.paused) {
|
|
1226
|
-
const
|
|
1493
|
+
const idx = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[forward ? idx + 1 : idx - 1];
|
|
1227
1494
|
this.jumpTo(this.state.currentFrame + (forward ? 1 : -1), true);
|
|
1228
|
-
if (e?.
|
|
1495
|
+
if (e?.deltat > 0) await new Promise((res) => this.playbackTimeoutId = setTimeout2(() => res(0), Math.min(e.deltat, this.config.maxPlaybackDelay), this.signal));
|
|
1229
1496
|
}
|
|
1230
1497
|
this.state.paused = true;
|
|
1231
1498
|
}
|
|
@@ -1243,35 +1510,39 @@ var TimeTravelPlugin = class extends BaseReactorPlugin {
|
|
|
1243
1510
|
try {
|
|
1244
1511
|
return JSON.stringify(this.state, replacer, space);
|
|
1245
1512
|
} catch (e) {
|
|
1246
|
-
return this.
|
|
1513
|
+
return this.rtrs.values().next().value?.log(`[Reactor ${this.name} Module] Failed to export session`), "";
|
|
1247
1514
|
}
|
|
1248
1515
|
}
|
|
1249
1516
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
1250
|
-
import(json) {
|
|
1517
|
+
import(json, reviver) {
|
|
1251
1518
|
try {
|
|
1252
|
-
setAny(this.state, "*", JSON.parse(json));
|
|
1519
|
+
setAny(this.state, "*", JSON.parse(json, reviver));
|
|
1253
1520
|
this.lastTimestamp = performance.now();
|
|
1254
1521
|
const resume = !this.state.paused, target = this.state.currentFrame;
|
|
1255
1522
|
this.state.paused = false;
|
|
1256
|
-
|
|
1523
|
+
for (const [rid, rtr] of this.rtrs) setAny(rtr.core, "*", deepClone(this.state.initialState[rid], rtr.config)), rtr.tick();
|
|
1257
1524
|
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
1258
1525
|
} catch (e) {
|
|
1259
|
-
this.
|
|
1526
|
+
this.rtrs.values().next().value?.log(`[Reactor ${this.name} Module] Failed to load session`);
|
|
1260
1527
|
}
|
|
1261
1528
|
}
|
|
1262
1529
|
};
|
|
1263
|
-
var
|
|
1530
|
+
var TIME_TRAVEL_MODULE_BUILD = { maxPlaybackDelay: 2e3 };
|
|
1264
1531
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1265
1532
|
0 && (module.exports = {
|
|
1266
|
-
|
|
1533
|
+
AsyncStorageAdapter,
|
|
1534
|
+
BaseReactorModule,
|
|
1267
1535
|
BaseStorageAdapter,
|
|
1536
|
+
COOKIE_ADAPTER_BUILD,
|
|
1537
|
+
CookieAdapter,
|
|
1268
1538
|
INDEXED_DB_ADAPTER_BUILD,
|
|
1269
1539
|
IndexedDBAdapter,
|
|
1270
1540
|
LocalStorageAdapter,
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1541
|
+
MemoryAdapter,
|
|
1542
|
+
PERSIST_MODULE_BUILD,
|
|
1543
|
+
PersistModule,
|
|
1544
|
+
SessionStorageAdapter,
|
|
1274
1545
|
StorageAdapter,
|
|
1275
|
-
|
|
1276
|
-
|
|
1546
|
+
TIME_TRAVEL_MODULE_BUILD,
|
|
1547
|
+
TimeTravelModule
|
|
1277
1548
|
});
|