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
package/dist/super.global.js
CHANGED
|
@@ -23,7 +23,6 @@ var sia = (() => {
|
|
|
23
23
|
__export(super_exports, {
|
|
24
24
|
CTX: () => CTX,
|
|
25
25
|
EVT_OPTS: () => EVT_OPTS,
|
|
26
|
-
EVT_WARN: () => EVT_WARN,
|
|
27
26
|
INDIFFABLE: () => INDIFFABLE,
|
|
28
27
|
INERTIA: () => INERTIA,
|
|
29
28
|
NIL: () => NIL,
|
|
@@ -48,7 +47,7 @@ var sia = (() => {
|
|
|
48
47
|
isVolatile: () => isVolatile,
|
|
49
48
|
live: () => live,
|
|
50
49
|
methods: () => methods,
|
|
51
|
-
|
|
50
|
+
modules: () => modules_exports,
|
|
52
51
|
reactive: () => reactive,
|
|
53
52
|
stable: () => stable,
|
|
54
53
|
state: () => state,
|
|
@@ -60,7 +59,9 @@ var sia = (() => {
|
|
|
60
59
|
var CTX = {
|
|
61
60
|
/** Flag indicating whether the application is running in development mode. */
|
|
62
61
|
isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
|
|
63
|
-
/**
|
|
62
|
+
/** Flag indicating whether a cascade is currently ongoing so reactors can allow all writes. */
|
|
63
|
+
isCascading: false,
|
|
64
|
+
/** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
|
|
64
65
|
autotracker: null
|
|
65
66
|
};
|
|
66
67
|
var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
|
|
@@ -72,7 +73,6 @@ var sia = (() => {
|
|
|
72
73
|
var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
|
|
73
74
|
var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
|
|
74
75
|
var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
|
|
75
|
-
var EVT_WARN = console.warn.bind(console, "[S.I.A Event]");
|
|
76
76
|
var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
|
|
77
77
|
var NIL = Object.freeze({});
|
|
78
78
|
var NOOP = () => {
|
|
@@ -171,16 +171,43 @@ var sia = (() => {
|
|
|
171
171
|
function parseAnyObj(obj, separator = ".", keyFunc = (p) => p, seen = /* @__PURE__ */ new WeakSet()) {
|
|
172
172
|
if (!isObj(obj) || seen.has(obj)) return obj;
|
|
173
173
|
seen.add(obj);
|
|
174
|
-
const result = {};
|
|
175
|
-
|
|
174
|
+
const result = {}, keys2 = Object.keys(obj);
|
|
175
|
+
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
176
|
+
const k = keys2[i];
|
|
177
|
+
k === "*" || k.includes(separator) ? setAny(result, k, parseAnyObj(obj[k], separator, keyFunc, seen), separator, keyFunc) : result[k] = isObj(obj[k]) ? parseAnyObj(obj[k], separator, keyFunc, seen) : obj[k];
|
|
178
|
+
}
|
|
176
179
|
return result;
|
|
177
180
|
}
|
|
178
181
|
function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
|
|
179
182
|
return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
|
|
180
183
|
}
|
|
184
|
+
function fanout(a, b, c, d) {
|
|
185
|
+
const isEvtPld = !!a?.target, [state2, 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 === "*" ? state2 : getAny(state2, path);
|
|
186
|
+
if (isEvtPld && type !== "set" && type !== "delete" || !target || !canHandle(news, opts)) return;
|
|
187
|
+
const prev = CTX.isCascading;
|
|
188
|
+
CTX.isCascading = isEvtPld;
|
|
189
|
+
try {
|
|
190
|
+
const walk = (target2, obj, depth = 1, keys2 = Object.keys(obj)) => {
|
|
191
|
+
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
192
|
+
const val = obj[keys2[i]];
|
|
193
|
+
try {
|
|
194
|
+
depth > 1 && canHandle(val, opts) ? walk(target2[keys2[i]] ||= {}, val, depth - 1) : target2[keys2[i]] = val;
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
walk(target, opts.merge && canHandle(olds, opts) ? mergeObjs(olds, news) : news, opts.depth === true ? Infinity : +opts.depth);
|
|
200
|
+
} finally {
|
|
201
|
+
CTX.isCascading = prev;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
181
204
|
function mergeObjs(o1 = {}, o2 = {}) {
|
|
182
|
-
const merged = { ...o1
|
|
183
|
-
|
|
205
|
+
const merged = { ...o1 ||= {}, ...o2 ||= {} }, keys2 = Object.keys(merged);
|
|
206
|
+
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
207
|
+
const k = keys2[i];
|
|
208
|
+
if (isObj(o1[k]) && isObj(o2[k])) merged[k] = mergeObjs(o1[k], o2[k]);
|
|
209
|
+
}
|
|
210
|
+
return merged;
|
|
184
211
|
}
|
|
185
212
|
function getTrailRecords(obj, path, reverse = false) {
|
|
186
213
|
const parts = path.split("."), chain = [["*", obj, obj]];
|
|
@@ -195,7 +222,11 @@ var sia = (() => {
|
|
|
195
222
|
const clone = config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {};
|
|
196
223
|
seen.set(obj, clone);
|
|
197
224
|
const keys2 = config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
198
|
-
for (let i = 0, len = keys2.length; i < len; i++)
|
|
225
|
+
for (let i = 0, len = keys2.length; i < len; i++)
|
|
226
|
+
try {
|
|
227
|
+
clone[keys2[i]] = deepClone(obj[keys2[i]], config, seen);
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
199
230
|
return clone;
|
|
200
231
|
}
|
|
201
232
|
function nuke(target) {
|
|
@@ -235,35 +266,34 @@ var sia = (() => {
|
|
|
235
266
|
staticType;
|
|
236
267
|
/** Original event target context. */
|
|
237
268
|
target;
|
|
238
|
-
/** Root reactive object for this event wave. */
|
|
269
|
+
/** Root reactive object for this event instance wave. */
|
|
239
270
|
root;
|
|
240
|
-
/** Original target path for this event wave. */
|
|
271
|
+
/** Original target path for this event instance wave. */
|
|
241
272
|
path;
|
|
242
273
|
/** Current value at the event target path. */
|
|
243
274
|
value;
|
|
244
275
|
/** Previous value at the event target path. */
|
|
245
276
|
oldValue;
|
|
246
|
-
/** Whether resolve/reject intent semantics are allowed for this event. */
|
|
277
|
+
/** Whether resolve/reject intent semantics are allowed for this event instance. */
|
|
247
278
|
rejectable;
|
|
248
|
-
/** Whether this event wave can bubble back up to ancestors or just capture down. */
|
|
279
|
+
/** Whether this event instance wave can bubble back up to ancestors or just capture down. */
|
|
249
280
|
bubbles;
|
|
250
281
|
/**
|
|
251
|
-
* `DOMHighResTimeStamp` for this event payload for native event parity and accuracy.
|
|
282
|
+
* `DOMHighResTimeStamp` for this event instance payload for native event parity and accuracy.
|
|
252
283
|
* Enable `eventTimeStamps` option, then use this over custom timestamps in listeners for accuracy.
|
|
253
284
|
* */
|
|
254
285
|
timestamp;
|
|
255
|
-
|
|
286
|
+
/** The `Reactor` instance that dispatched this event instance. */
|
|
287
|
+
reactor;
|
|
256
288
|
_resolved = "";
|
|
257
289
|
_rejected = "";
|
|
258
290
|
_propagationStopped = false;
|
|
259
291
|
_immediatePropagationStopped = false;
|
|
260
292
|
/**
|
|
261
293
|
* @param payload Source payload for this event instance.
|
|
262
|
-
* @param
|
|
263
|
-
* @param canWarn Whether warning output is enabled.
|
|
264
|
-
* @param canStamp Whether timestamping is enabled.
|
|
294
|
+
* @param reactor The `Reactor` instance creating this event instance.
|
|
265
295
|
*/
|
|
266
|
-
constructor(payload,
|
|
296
|
+
constructor(payload, reactor) {
|
|
267
297
|
this.staticType = this.type = payload.type;
|
|
268
298
|
this.target = payload.target;
|
|
269
299
|
this.currentTarget = payload.currentTarget;
|
|
@@ -272,9 +302,9 @@ var sia = (() => {
|
|
|
272
302
|
this.value = payload.target.value;
|
|
273
303
|
this.oldValue = payload.target.oldValue;
|
|
274
304
|
this.rejectable = payload.rejectable;
|
|
275
|
-
this.bubbles =
|
|
276
|
-
if (
|
|
277
|
-
|
|
305
|
+
this.bubbles = !!reactor.config.eventBubbling;
|
|
306
|
+
if (reactor.config.eventTimeStamps) this.timestamp = performance.now();
|
|
307
|
+
this.reactor = reactor;
|
|
278
308
|
}
|
|
279
309
|
/** Whether propagation has been stopped. */
|
|
280
310
|
get propagationStopped() {
|
|
@@ -304,9 +334,9 @@ var sia = (() => {
|
|
|
304
334
|
* @example e.resolve("API Load successful"); // message
|
|
305
335
|
*/
|
|
306
336
|
resolve(message) {
|
|
307
|
-
if (!this.rejectable) return this.
|
|
308
|
-
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.
|
|
309
|
-
if (this.rejectable) this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"
|
|
337
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`resolve()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
338
|
+
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.`);
|
|
339
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`}`);
|
|
310
340
|
}
|
|
311
341
|
/** Rejection reason for rejectable events. */
|
|
312
342
|
get rejected() {
|
|
@@ -319,9 +349,9 @@ var sia = (() => {
|
|
|
319
349
|
* @example e.resolve("User is not logged in"); // reason
|
|
320
350
|
*/
|
|
321
351
|
reject(reason) {
|
|
322
|
-
if (!this.rejectable) return this.
|
|
323
|
-
if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this.
|
|
324
|
-
if (this.rejectable) this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"
|
|
352
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`reject()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
353
|
+
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.`);
|
|
354
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`}`);
|
|
325
355
|
}
|
|
326
356
|
/**
|
|
327
357
|
* Returns event path values from target to root.
|
|
@@ -330,24 +360,22 @@ var sia = (() => {
|
|
|
330
360
|
composedPath() {
|
|
331
361
|
return getTrailRecords(this.root, this.path, true).map((r) => r[2]);
|
|
332
362
|
}
|
|
333
|
-
get canWarn() {
|
|
334
|
-
return this._warn !== NOOP;
|
|
335
|
-
}
|
|
336
363
|
};
|
|
337
364
|
|
|
338
365
|
// src/ts/core/reactor.ts
|
|
339
366
|
var Reactor = class {
|
|
367
|
+
/** Logger function for this reactor instance, override if desired, `this.canLog = false` resets. */
|
|
340
368
|
log = NOOP;
|
|
369
|
+
/** The core state object for this reactor instance. */
|
|
341
370
|
core;
|
|
342
371
|
// `?:`s | pay the ~800 byte price upfront for what u might never use
|
|
343
|
-
|
|
372
|
+
/** The modules being used by this reactor. */
|
|
373
|
+
modules;
|
|
374
|
+
/** Configuration options for this reactor instance. */
|
|
344
375
|
config;
|
|
376
|
+
/** Whether this reactor instance is currently batching updates, a window view into the engine timing */
|
|
345
377
|
isBatching = false;
|
|
346
378
|
// Async Batching
|
|
347
|
-
isCascading = false;
|
|
348
|
-
// Setter Cascading
|
|
349
|
-
isLogging = false;
|
|
350
|
-
// keeping track so API getter doesn't slow down internal iterations in any way
|
|
351
379
|
queue;
|
|
352
380
|
// Tasks to run after flush
|
|
353
381
|
batch;
|
|
@@ -414,7 +442,7 @@ var sia = (() => {
|
|
|
414
442
|
safeValue = value?.[RAW] || value;
|
|
415
443
|
unchanged = this.config.equalityFunction(safeValue, safeOldValue);
|
|
416
444
|
}
|
|
417
|
-
if (!indiffable && unchanged && !
|
|
445
|
+
if (!indiffable && unchanged && !CTX.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
|
|
418
446
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
419
447
|
if (this.setters) {
|
|
420
448
|
const wildcords = this.setters.get("*");
|
|
@@ -568,7 +596,7 @@ var sia = (() => {
|
|
|
568
596
|
if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
|
|
569
597
|
}
|
|
570
598
|
wave(path, payload) {
|
|
571
|
-
const e = new ReactorEvent(payload, this
|
|
599
|
+
const e = new ReactorEvent(payload, this), chain = getTrailRecords(this.core, path);
|
|
572
600
|
e.eventPhase = ReactorEvent.CAPTURING_PHASE;
|
|
573
601
|
for (let i = 0; i <= chain.length - 2; i++) {
|
|
574
602
|
if (e.propagationStopped) break;
|
|
@@ -642,8 +670,8 @@ var sia = (() => {
|
|
|
642
670
|
return depth;
|
|
643
671
|
}
|
|
644
672
|
getContext(path) {
|
|
645
|
-
const
|
|
646
|
-
return { path, value, key: path.slice(
|
|
673
|
+
const last = path.lastIndexOf("."), value = getAny(this.core, path), object = last === -1 ? this.core : getAny(this.core, path.slice(0, last));
|
|
674
|
+
return { path, value, key: path.slice(last + 1) || "", hadKey: true, object };
|
|
647
675
|
}
|
|
648
676
|
bindSignal(cord, sig) {
|
|
649
677
|
if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
|
|
@@ -659,7 +687,11 @@ var sia = (() => {
|
|
|
659
687
|
const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
|
|
660
688
|
seen.set(obj, clone);
|
|
661
689
|
const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
662
|
-
for (let i = 0, len = keys2.length; i < len; i++)
|
|
690
|
+
for (let i = 0, len = keys2.length; i < len; i++)
|
|
691
|
+
try {
|
|
692
|
+
clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
663
695
|
if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
|
|
664
696
|
return clone;
|
|
665
697
|
}
|
|
@@ -747,7 +779,7 @@ var sia = (() => {
|
|
|
747
779
|
* rtr.delete("cache.temp", () => TERMINATOR);
|
|
748
780
|
*/
|
|
749
781
|
delete(path, callback, options) {
|
|
750
|
-
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path
|
|
782
|
+
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path));
|
|
751
783
|
}
|
|
752
784
|
/** Registers a delete mediator for a path that only triggers once. */
|
|
753
785
|
donce(path, callback, options) {
|
|
@@ -764,7 +796,7 @@ var sia = (() => {
|
|
|
764
796
|
}
|
|
765
797
|
/**
|
|
766
798
|
* Registers a watcher for a path.
|
|
767
|
-
* Watch callbacks run synchronously with the operation.
|
|
799
|
+
* Watch callbacks run synchronously with the operation, use leaf paths for reliability as it sees exact sets; no bubbling here.
|
|
768
800
|
* @param path Path or wildcard path.
|
|
769
801
|
* @param callback Watch callback.
|
|
770
802
|
* @param options Sync options.
|
|
@@ -813,7 +845,7 @@ var sia = (() => {
|
|
|
813
845
|
cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options), lDepth: depth !== void 0 ? this.getDepth(path) : depth };
|
|
814
846
|
if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
|
|
815
847
|
const target = this.getContext(path);
|
|
816
|
-
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this
|
|
848
|
+
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this));
|
|
817
849
|
}
|
|
818
850
|
(cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
|
|
819
851
|
return this.bindSignal(cord, signal);
|
|
@@ -844,56 +876,39 @@ var sia = (() => {
|
|
|
844
876
|
return this.cloned(arguments.length < 2 ? this.core : branch, raw);
|
|
845
877
|
}
|
|
846
878
|
/**
|
|
847
|
-
*
|
|
848
|
-
* @param
|
|
849
|
-
* @param
|
|
850
|
-
* @
|
|
851
|
-
* rtr.on("user", (event) => rtr.cascade(event));
|
|
852
|
-
* @example
|
|
853
|
-
* rtr.watch("user", (_value, payload) => rtr.cascade(payload));
|
|
854
|
-
*/
|
|
855
|
-
cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objectSafe = true) {
|
|
856
|
-
if (type !== "set" && type !== "delete" || !canHandle(news, this.config) || (objectSafe ? !canHandle(olds, this.config) : false)) return;
|
|
857
|
-
const obj = objectSafe ? mergeObjs(olds, news) : news, keys2 = Object.keys(obj);
|
|
858
|
-
this.isCascading = true;
|
|
859
|
-
for (let i = 0, len = keys2.length; i < len; i++) setAny(this.core, path === "*" ? keys2[i] : path + "." + keys2[i], obj[keys2[i]]);
|
|
860
|
-
this.isCascading = false;
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* Installs a plugin instance.
|
|
864
|
-
* @param plugin Plugin instance.
|
|
865
|
-
* @returns Current reactor for fluent chaining.
|
|
879
|
+
* Installs a module instance.
|
|
880
|
+
* @param target Module instance.
|
|
881
|
+
* @param id Optional identification tag for this instance in the module.
|
|
882
|
+
* @returns Current `Reactor` instance for fluent chaining.
|
|
866
883
|
*/
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
this.plugins?.get(name)?.destroy();
|
|
870
|
-
return (this.plugins ??= /* @__PURE__ */ new Map()).set(name, (plugin.setup(this), plugin)), this;
|
|
884
|
+
use(target, id) {
|
|
885
|
+
return (this.modules ??= /* @__PURE__ */ new Set()).add(target.setup(this, id)), this;
|
|
871
886
|
}
|
|
872
|
-
/** Resets
|
|
887
|
+
/** Resets this reactor instance to its initial state. */
|
|
873
888
|
reset() {
|
|
874
889
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
|
|
875
890
|
this.batch?.clear(), this.queue?.clear(), this.isBatching = false;
|
|
876
891
|
}
|
|
877
892
|
destroy() {
|
|
878
|
-
if (this.
|
|
893
|
+
if (this.modules) for (const mdle of this.modules) mdle.destroy();
|
|
879
894
|
this.reset(), nuke(this);
|
|
880
895
|
}
|
|
881
896
|
get canLog() {
|
|
882
|
-
return this.
|
|
897
|
+
return this.log !== NOOP;
|
|
883
898
|
}
|
|
884
899
|
set canLog(value) {
|
|
885
|
-
this.log =
|
|
900
|
+
this.log = value ? RTR_LOG : NOOP;
|
|
886
901
|
}
|
|
887
|
-
get
|
|
888
|
-
return this.config.
|
|
902
|
+
get canLineageTrace() {
|
|
903
|
+
return this.config.lineageTracing && this.config.referenceTracking;
|
|
889
904
|
}
|
|
890
905
|
get canSmartClone() {
|
|
891
|
-
return this.config.
|
|
906
|
+
return this.config.smartCloning && this.config.referenceTracking;
|
|
892
907
|
}
|
|
893
908
|
};
|
|
894
909
|
|
|
895
910
|
// src/ts/core/mixins.ts
|
|
896
|
-
var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "
|
|
911
|
+
var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
|
|
897
912
|
function reactive(target, build, preferences = NIL) {
|
|
898
913
|
if ("__Reactor__" in target) return target;
|
|
899
914
|
const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
|
|
@@ -901,7 +916,7 @@ var sia = (() => {
|
|
|
901
916
|
let key = methods[i];
|
|
902
917
|
if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
|
|
903
918
|
else if (preferences.whitelist?.includes(key)) continue;
|
|
904
|
-
descriptors[key] = { value: rtr[
|
|
919
|
+
descriptors[key] = { value: rtr[methods[i]].bind(rtr), ...locks };
|
|
905
920
|
}
|
|
906
921
|
descriptors["__Reactor__"] = { value: rtr, ...locks };
|
|
907
922
|
return Object.defineProperties(rtr.core, descriptors), rtr.core;
|
|
@@ -957,6 +972,7 @@ var sia = (() => {
|
|
|
957
972
|
createEl: () => createEl,
|
|
958
973
|
deepClone: () => deepClone,
|
|
959
974
|
deleteAny: () => deleteAny,
|
|
975
|
+
fanout: () => fanout,
|
|
960
976
|
formatKeyForDisplay: () => formatKeyForDisplay,
|
|
961
977
|
formatKeyShortcutsForDisplay: () => formatKeyShortcutsForDisplay,
|
|
962
978
|
getAny: () => getAny,
|
|
@@ -1123,63 +1139,95 @@ var sia = (() => {
|
|
|
1123
1139
|
}
|
|
1124
1140
|
}
|
|
1125
1141
|
|
|
1126
|
-
// src/ts/
|
|
1127
|
-
var
|
|
1128
|
-
__export(
|
|
1129
|
-
|
|
1142
|
+
// src/ts/modules.ts
|
|
1143
|
+
var modules_exports = {};
|
|
1144
|
+
__export(modules_exports, {
|
|
1145
|
+
AsyncStorageAdapter: () => AsyncStorageAdapter,
|
|
1146
|
+
BaseReactorModule: () => BaseReactorModule,
|
|
1130
1147
|
BaseStorageAdapter: () => BaseStorageAdapter,
|
|
1148
|
+
COOKIE_ADAPTER_BUILD: () => COOKIE_ADAPTER_BUILD,
|
|
1149
|
+
CookieAdapter: () => CookieAdapter,
|
|
1131
1150
|
INDEXED_DB_ADAPTER_BUILD: () => INDEXED_DB_ADAPTER_BUILD,
|
|
1132
1151
|
IndexedDBAdapter: () => IndexedDBAdapter,
|
|
1133
1152
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1153
|
+
MemoryAdapter: () => MemoryAdapter,
|
|
1154
|
+
PERSIST_MODULE_BUILD: () => PERSIST_MODULE_BUILD,
|
|
1155
|
+
PersistModule: () => PersistModule,
|
|
1156
|
+
SessionStorageAdapter: () => SessionStorageAdapter,
|
|
1137
1157
|
StorageAdapter: () => StorageAdapter,
|
|
1138
|
-
|
|
1139
|
-
|
|
1158
|
+
TIME_TRAVEL_MODULE_BUILD: () => TIME_TRAVEL_MODULE_BUILD,
|
|
1159
|
+
TimeTravelModule: () => TimeTravelModule
|
|
1140
1160
|
});
|
|
1141
1161
|
|
|
1142
|
-
// src/ts/
|
|
1143
|
-
var
|
|
1144
|
-
|
|
1162
|
+
// src/ts/modules/base.ts
|
|
1163
|
+
var wpArr = ["*"];
|
|
1164
|
+
var BaseReactorModule = class {
|
|
1165
|
+
static moduleName;
|
|
1145
1166
|
get name() {
|
|
1146
|
-
return this.constructor.
|
|
1167
|
+
return this.constructor.moduleName;
|
|
1147
1168
|
}
|
|
1148
1169
|
ac = new AbortController();
|
|
1149
1170
|
signal = this.ac.signal;
|
|
1150
|
-
|
|
1171
|
+
rtrs = /* @__PURE__ */ new Map();
|
|
1172
|
+
rids = /* @__PURE__ */ new WeakMap();
|
|
1173
|
+
// for quick 0(1) lookups over iteration
|
|
1174
|
+
wired = false;
|
|
1151
1175
|
config;
|
|
1152
1176
|
state;
|
|
1153
1177
|
constructor(config, rtr, state2) {
|
|
1154
1178
|
guardAllMethods(this, this.guard);
|
|
1155
|
-
this.rtr = rtr;
|
|
1156
1179
|
this.config = isObj(config) ? reactive(config) : config;
|
|
1157
1180
|
this.state = isObj(state2) ? reactive(state2) : state2;
|
|
1181
|
+
rtr && this.attach(rtr);
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Connect to a `Reactor` instance, allows managing multiple reactors if needed.
|
|
1185
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
1186
|
+
* @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.
|
|
1187
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
1188
|
+
* @example
|
|
1189
|
+
* const mod = new MyModule().attach(state1).attach(state2); // implicit index-based ids by default, add a .setup() or `Reactor.use()` when ready for init.
|
|
1190
|
+
* @example
|
|
1191
|
+
* const persist = new PersistModule(config).attach(sessState, "session").attach(adminState, "session.admin"); // don't use "*", causes de-serialization issues.
|
|
1192
|
+
*/
|
|
1193
|
+
attach(target, id = this.rtrs.size) {
|
|
1194
|
+
const rtr = getReactor(target);
|
|
1195
|
+
if (!rtr || this.rtrs.has(id)) return this;
|
|
1196
|
+
return this.rids.set((this.rtrs.set(id, rtr), rtr), id), this.onAttach(rtr, id), this;
|
|
1197
|
+
}
|
|
1198
|
+
onAttach(_rtr, _rid) {
|
|
1158
1199
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1200
|
+
/**
|
|
1201
|
+
* Entry point called to initialize module wiring, calls `.attach(target, id)` first, `Reactor.use()` calls this internally.
|
|
1202
|
+
* Should run as last in `.attach()` chain or after all desired reactors if using multiple; so wiring is done safely after.
|
|
1203
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
1204
|
+
* @param id Optional id for the reactor, prefer over default implicit index id when managing multiple reactors.
|
|
1205
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
1206
|
+
* @example
|
|
1207
|
+
* const mod = new MyModule().attach(state1).setup(state2); // if using multiple, this should run last; with same params as `.attach()` for a shorter chain
|
|
1208
|
+
*/
|
|
1209
|
+
setup(target, id) {
|
|
1210
|
+
return this.attach(target, id), !this.wired && (this.wire(), this.wired = true), this;
|
|
1163
1211
|
}
|
|
1164
1212
|
destroy() {
|
|
1165
1213
|
this.ac.abort();
|
|
1166
1214
|
this.onDestroy?.();
|
|
1167
1215
|
}
|
|
1168
1216
|
/**
|
|
1169
|
-
* Wraps a function with
|
|
1217
|
+
* Wraps a function with module-scoped error logging.
|
|
1170
1218
|
* Use this when creating functions dynamically (for example, before attaching an anonymous listener on the fly).
|
|
1171
1219
|
* @example
|
|
1172
1220
|
* window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
|
|
1173
1221
|
*/
|
|
1174
1222
|
guard = (fn) => {
|
|
1175
|
-
return guardMethod(fn, (e) => this.
|
|
1223
|
+
return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
|
|
1176
1224
|
};
|
|
1177
1225
|
// `()=>{}`: needs to be bounded even before initialization
|
|
1178
1226
|
};
|
|
1179
1227
|
|
|
1180
1228
|
// src/ts/utils/store.ts
|
|
1181
1229
|
var BaseStorageAdapter = class {
|
|
1182
|
-
|
|
1230
|
+
name = "StorageAdapter";
|
|
1183
1231
|
config;
|
|
1184
1232
|
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})`);
|
|
1185
1233
|
constructor(config) {
|
|
@@ -1187,26 +1235,44 @@ var sia = (() => {
|
|
|
1187
1235
|
}
|
|
1188
1236
|
};
|
|
1189
1237
|
var StorageAdapter = class extends BaseStorageAdapter {
|
|
1238
|
+
name = "SyncStorageAdapter";
|
|
1190
1239
|
};
|
|
1191
1240
|
var AsyncStorageAdapter = class extends BaseStorageAdapter {
|
|
1241
|
+
name = "AsyncStorageAdapter";
|
|
1192
1242
|
};
|
|
1193
1243
|
var LocalStorageAdapter = class extends StorageAdapter {
|
|
1194
|
-
|
|
1195
|
-
|
|
1244
|
+
name = "LocalStorage";
|
|
1245
|
+
/**
|
|
1246
|
+
* Reads and parses a value from localStorage.
|
|
1247
|
+
* @param key Storage key.
|
|
1248
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1249
|
+
*/
|
|
1250
|
+
get(key, reviver = this.config.reviver) {
|
|
1196
1251
|
try {
|
|
1197
1252
|
const v = localStorage.getItem(key);
|
|
1198
|
-
return v ? JSON.parse(v) : void 0;
|
|
1253
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1199
1254
|
} catch {
|
|
1200
1255
|
return void 0;
|
|
1201
1256
|
}
|
|
1202
1257
|
}
|
|
1203
|
-
|
|
1258
|
+
/**
|
|
1259
|
+
* Serializes and writes a value to localStorage.
|
|
1260
|
+
* @param key Storage key.
|
|
1261
|
+
* @param value Value to serialize.
|
|
1262
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1263
|
+
*/
|
|
1264
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1204
1265
|
try {
|
|
1205
|
-
return localStorage.setItem(key, JSON.stringify(value)), true;
|
|
1266
|
+
return localStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
1206
1267
|
} catch (e) {
|
|
1207
1268
|
return this.warn("setItem", void 0, key), false;
|
|
1208
1269
|
}
|
|
1209
1270
|
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Removes a single key from localStorage.
|
|
1273
|
+
* @param key Storage key.
|
|
1274
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1275
|
+
*/
|
|
1210
1276
|
remove(key) {
|
|
1211
1277
|
try {
|
|
1212
1278
|
return localStorage.removeItem(key), true;
|
|
@@ -1214,6 +1280,10 @@ var sia = (() => {
|
|
|
1214
1280
|
return this.warn("removeItem", void 0, key), false;
|
|
1215
1281
|
}
|
|
1216
1282
|
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Clears all localStorage entries for the current origin.
|
|
1285
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1286
|
+
*/
|
|
1217
1287
|
clear() {
|
|
1218
1288
|
try {
|
|
1219
1289
|
return localStorage.clear(), true;
|
|
@@ -1222,25 +1292,94 @@ var sia = (() => {
|
|
|
1222
1292
|
}
|
|
1223
1293
|
}
|
|
1224
1294
|
};
|
|
1225
|
-
var
|
|
1295
|
+
var SessionStorageAdapter = class extends StorageAdapter {
|
|
1296
|
+
name = "SessionStorage";
|
|
1297
|
+
/**
|
|
1298
|
+
* Reads and parses a value from sessionStorage.
|
|
1299
|
+
* @param key Storage key.
|
|
1300
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1301
|
+
*/
|
|
1302
|
+
get(key, reviver = this.config.reviver) {
|
|
1303
|
+
try {
|
|
1304
|
+
const v = sessionStorage.getItem(key);
|
|
1305
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1306
|
+
} catch {
|
|
1307
|
+
return void 0;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Serializes and writes a value to sessionStorage.
|
|
1312
|
+
* @param key Storage key.
|
|
1313
|
+
* @param value Value to serialize.
|
|
1314
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1315
|
+
*/
|
|
1316
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1317
|
+
try {
|
|
1318
|
+
return sessionStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
1319
|
+
} catch (e) {
|
|
1320
|
+
return this.warn("setItem", void 0, key), false;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Removes a single key from sessionStorage.
|
|
1325
|
+
* @param key Storage key.
|
|
1326
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1327
|
+
*/
|
|
1328
|
+
remove(key) {
|
|
1329
|
+
try {
|
|
1330
|
+
return sessionStorage.removeItem(key), true;
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
return this.warn("removeItem", void 0, key), false;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Clears all sessionStorage entries for the current tab session.
|
|
1337
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1338
|
+
*/
|
|
1339
|
+
clear() {
|
|
1340
|
+
try {
|
|
1341
|
+
return sessionStorage.clear(), true;
|
|
1342
|
+
} catch (e) {
|
|
1343
|
+
return this.warn("clear", void 0), false;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
var MemoryAdapter = class extends StorageAdapter {
|
|
1348
|
+
name = "Memory";
|
|
1226
1349
|
constructor(build) {
|
|
1227
1350
|
super({ store: /* @__PURE__ */ new Map(), ...build });
|
|
1228
1351
|
}
|
|
1229
|
-
|
|
1352
|
+
/**
|
|
1353
|
+
* Reads and parses a value from memory storage.
|
|
1354
|
+
* @param key Storage key.
|
|
1355
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1356
|
+
*/
|
|
1357
|
+
get(key, reviver = this.config.reviver) {
|
|
1230
1358
|
try {
|
|
1231
1359
|
const v = this.config.store.get(key);
|
|
1232
|
-
return v ? JSON.parse(v) : void 0;
|
|
1360
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1233
1361
|
} catch {
|
|
1234
1362
|
return void 0;
|
|
1235
1363
|
}
|
|
1236
1364
|
}
|
|
1237
|
-
|
|
1365
|
+
/**
|
|
1366
|
+
* Serializes and writes a value to memory storage.
|
|
1367
|
+
* @param key Storage key.
|
|
1368
|
+
* @param value Value to serialize.
|
|
1369
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1370
|
+
*/
|
|
1371
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1238
1372
|
try {
|
|
1239
|
-
return this.config.store.set(key, JSON.stringify(value)), true;
|
|
1373
|
+
return this.config.store.set(key, JSON.stringify(value, replacer)), true;
|
|
1240
1374
|
} catch (e) {
|
|
1241
1375
|
return this.warn("set", void 0, key), false;
|
|
1242
1376
|
}
|
|
1243
1377
|
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Removes a single key from memory storage.
|
|
1380
|
+
* @param key Storage key.
|
|
1381
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1382
|
+
*/
|
|
1244
1383
|
remove(key) {
|
|
1245
1384
|
try {
|
|
1246
1385
|
return this.config.store.delete(key), true;
|
|
@@ -1248,6 +1387,10 @@ var sia = (() => {
|
|
|
1248
1387
|
return this.warn("remove", void 0, key), false;
|
|
1249
1388
|
}
|
|
1250
1389
|
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Clears all entries from memory storage.
|
|
1392
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1393
|
+
*/
|
|
1251
1394
|
clear() {
|
|
1252
1395
|
try {
|
|
1253
1396
|
return this.config.store.clear(), true;
|
|
@@ -1256,23 +1399,100 @@ var sia = (() => {
|
|
|
1256
1399
|
}
|
|
1257
1400
|
}
|
|
1258
1401
|
};
|
|
1402
|
+
var CookieAdapter = class extends StorageAdapter {
|
|
1403
|
+
name = "Cookie";
|
|
1404
|
+
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}` : ""}`;
|
|
1405
|
+
constructor(build) {
|
|
1406
|
+
super({ secure: "undefined" !== typeof window && location.protocol === "https:", ...COOKIE_ADAPTER_BUILD, ...build });
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Reads and parses a cookie visible to the current page scope.
|
|
1410
|
+
* @param key Cookie key.
|
|
1411
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1412
|
+
*/
|
|
1413
|
+
get(key, reviver = this.config.reviver) {
|
|
1414
|
+
try {
|
|
1415
|
+
const k = encodeURIComponent(key) + "=";
|
|
1416
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1417
|
+
if (!pair.startsWith(k)) continue;
|
|
1418
|
+
return JSON.parse(decodeURIComponent(pair.slice(k.length)), reviver);
|
|
1419
|
+
}
|
|
1420
|
+
return void 0;
|
|
1421
|
+
} catch {
|
|
1422
|
+
return void 0;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Writes a cookie with optional per-call scope/lifetime overrides.
|
|
1427
|
+
* @param key Cookie key.
|
|
1428
|
+
* @param value Value to serialize.
|
|
1429
|
+
* @param opts Optional per-call cookie options.
|
|
1430
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1431
|
+
*/
|
|
1432
|
+
set(key, value, opts = NIL, replacer = this.config.replacer) {
|
|
1433
|
+
try {
|
|
1434
|
+
return document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value, replacer))}; ${this.deets(opts)}`, true;
|
|
1435
|
+
} catch {
|
|
1436
|
+
return this.warn("set", void 0, key), false;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Removes a cookie key using matching scope attributes.
|
|
1441
|
+
* @param key Cookie key.
|
|
1442
|
+
* @param opts Optional per-call scope overrides.
|
|
1443
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1444
|
+
*/
|
|
1445
|
+
remove(key, opts = NIL) {
|
|
1446
|
+
try {
|
|
1447
|
+
return document.cookie = `${encodeURIComponent(key)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`, true;
|
|
1448
|
+
} catch {
|
|
1449
|
+
return this.warn("remove", void 0, key), false;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Attempts to remove all visible cookie keys for the given scope.
|
|
1454
|
+
* @param opts Optional per-call scope overrides.
|
|
1455
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1456
|
+
*/
|
|
1457
|
+
clear(opts = NIL) {
|
|
1458
|
+
try {
|
|
1459
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1460
|
+
const idx = pair.indexOf("=");
|
|
1461
|
+
document.cookie = `${idx === -1 ? pair : pair.slice(0, idx)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`;
|
|
1462
|
+
}
|
|
1463
|
+
return true;
|
|
1464
|
+
} catch {
|
|
1465
|
+
return this.warn("clear"), false;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1259
1469
|
var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
1470
|
+
name = "IndexedDB";
|
|
1260
1471
|
db;
|
|
1261
|
-
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})`);
|
|
1262
1472
|
constructor(build) {
|
|
1263
1473
|
super({ ...INDEXED_DB_ADAPTER_BUILD, ...build });
|
|
1264
1474
|
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Returns a connected IndexedDB instance, opening it when needed.
|
|
1477
|
+
* @returns Connected database handle.
|
|
1478
|
+
*/
|
|
1265
1479
|
async idb() {
|
|
1266
1480
|
const idb = this.config.onidb();
|
|
1267
1481
|
if (idb || this.db) return Promise.resolve(idb || this.db);
|
|
1268
1482
|
return new Promise((res, rej) => {
|
|
1269
1483
|
const req = indexedDB.open(this.config.dbName, this.config.version);
|
|
1270
1484
|
req.onupgradeneeded = (e) => (this.config.onupgradeneeded(req.result, e), this.config.stores.forEach((s) => !req.result.objectStoreNames.contains(s) && req.result.createObjectStore(s)));
|
|
1271
|
-
req.onsuccess = (e) => (this.config.onsuccess(req.result, e), req.result.onversionchange = (e2) => (this.warn("update", "Updated in another tab"),
|
|
1485
|
+
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));
|
|
1272
1486
|
req.onerror = (e) => (this.config.onerror(req.error, e), this.warn("open", "Something went wrong"), rej(req.error));
|
|
1273
1487
|
req.onblocked = (e) => (this.config.onblocked(e), this.warn("open", "Close other tabs for updates"));
|
|
1274
1488
|
});
|
|
1275
1489
|
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Reads a value by key from an object store.
|
|
1492
|
+
* @param key Record key.
|
|
1493
|
+
* @param store Optional object-store override.
|
|
1494
|
+
* @returns Stored value, or `undefined` when missing/unreadable.
|
|
1495
|
+
*/
|
|
1276
1496
|
async get(key, store = this.config.stores[0]) {
|
|
1277
1497
|
try {
|
|
1278
1498
|
const req = (await this.idb()).transaction(store).objectStore(store).get(key);
|
|
@@ -1281,6 +1501,13 @@ var sia = (() => {
|
|
|
1281
1501
|
return this.warn("get", void 0, store), void 0;
|
|
1282
1502
|
}
|
|
1283
1503
|
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Writes a value by key into an object store.
|
|
1506
|
+
* @param key Record key.
|
|
1507
|
+
* @param value Value to store.
|
|
1508
|
+
* @param store Optional object-store override.
|
|
1509
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1510
|
+
*/
|
|
1284
1511
|
async set(key, value, store = this.config.stores[0]) {
|
|
1285
1512
|
try {
|
|
1286
1513
|
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).put(value, key);
|
|
@@ -1289,6 +1516,12 @@ var sia = (() => {
|
|
|
1289
1516
|
return this.warn("put", void 0, store), false;
|
|
1290
1517
|
}
|
|
1291
1518
|
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Deletes a value by key from an object store.
|
|
1521
|
+
* @param key Record key.
|
|
1522
|
+
* @param store Optional object-store override.
|
|
1523
|
+
* @returns `true` when delete succeeds, else `false`.
|
|
1524
|
+
*/
|
|
1292
1525
|
async remove(key, store = this.config.stores[0]) {
|
|
1293
1526
|
try {
|
|
1294
1527
|
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).delete(key);
|
|
@@ -1297,6 +1530,11 @@ var sia = (() => {
|
|
|
1297
1530
|
return this.warn("delete", void 0, store), false;
|
|
1298
1531
|
}
|
|
1299
1532
|
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Clears one or more object stores.
|
|
1535
|
+
* @param stores Store name or list of store names to clear.
|
|
1536
|
+
* @returns `true` when all clears succeed, else `false`.
|
|
1537
|
+
*/
|
|
1300
1538
|
async clear(stores = this.config.stores) {
|
|
1301
1539
|
let success = true;
|
|
1302
1540
|
for (const store of Array.isArray(stores) ? stores : [stores])
|
|
@@ -1309,90 +1547,121 @@ var sia = (() => {
|
|
|
1309
1547
|
return success;
|
|
1310
1548
|
}
|
|
1311
1549
|
};
|
|
1550
|
+
var COOKIE_ADAPTER_BUILD = { path: "/", sameSite: "Lax", domain: void 0, debug: false };
|
|
1312
1551
|
var INDEXED_DB_ADAPTER_BUILD = { dbName: "REACTOR_IDB", stores: ["VAULT"], version: 1, onidb: NOOP, onupgradeneeded: NOOP, onversionchange: NOOP, onsuccess: NOOP, onerror: NOOP, onblocked: NOOP };
|
|
1313
1552
|
|
|
1314
|
-
// src/ts/
|
|
1315
|
-
var
|
|
1316
|
-
static
|
|
1553
|
+
// src/ts/modules/persist.ts
|
|
1554
|
+
var PersistModule = class extends BaseReactorModule {
|
|
1555
|
+
static moduleName = "persist";
|
|
1317
1556
|
adapter;
|
|
1557
|
+
hydrateSeq = 0;
|
|
1318
1558
|
saveTimeoutId = 0;
|
|
1319
1559
|
get payload() {
|
|
1320
|
-
|
|
1321
|
-
|
|
1560
|
+
let res = this.rtrs.size > 1 ? {} : void 0;
|
|
1561
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1562
|
+
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;
|
|
1563
|
+
this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
|
|
1564
|
+
}
|
|
1565
|
+
return res;
|
|
1322
1566
|
}
|
|
1323
1567
|
constructor(config, rtr) {
|
|
1324
|
-
super({ ...
|
|
1568
|
+
super({ ...PERSIST_MODULE_BUILD, ...config }, rtr, { hydrated: false });
|
|
1325
1569
|
}
|
|
1326
1570
|
wire() {
|
|
1327
1571
|
"undefined" !== typeof window && window.addEventListener("pagehide", this.onDestroy, { signal: this.signal });
|
|
1328
1572
|
"undefined" !== typeof document && document.addEventListener("visibilitychange", () => document.visibilityState === "hidden" && this.onDestroy(), { signal: this.signal });
|
|
1329
|
-
this.config.on("adapter", this.
|
|
1330
|
-
this.config.on("disabled", this.
|
|
1331
|
-
this.config.on("paths", this.
|
|
1573
|
+
this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
|
|
1574
|
+
this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
|
|
1575
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1576
|
+
}
|
|
1577
|
+
onAttach(rtr) {
|
|
1578
|
+
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);
|
|
1332
1579
|
}
|
|
1333
|
-
async
|
|
1580
|
+
async handleAdapter({ value = LocalStorageAdapter }) {
|
|
1581
|
+
const seq = ++this.hydrateSeq;
|
|
1334
1582
|
if (this.adapter && value === this.adapter.constructor) return;
|
|
1583
|
+
this.state.hydrated = false;
|
|
1335
1584
|
this.adapter?.remove(this.config.key);
|
|
1336
|
-
this.adapter = "function" === typeof value ? new value({ debug: this.
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1585
|
+
this.adapter = "function" === typeof value ? new value({ debug: !!this.rtrs.values().next().value?.canLog }) : value;
|
|
1586
|
+
try {
|
|
1587
|
+
let saved = this.adapter.get(this.config.key);
|
|
1588
|
+
const isAsync = saved instanceof Promise, depth = this.config.fanout ?? isAsync;
|
|
1589
|
+
saved = !isAsync ? saved : await saved;
|
|
1590
|
+
if (seq !== this.hydrateSeq || !saved) return;
|
|
1591
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1592
|
+
const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
|
|
1593
|
+
if (!entry) continue;
|
|
1594
|
+
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);
|
|
1595
|
+
for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
|
|
1596
|
+
}
|
|
1597
|
+
for (const rtr of this.rtrs.values()) rtr.tick(!depth ? this.config.paths ?? "*" : "*");
|
|
1598
|
+
} catch {
|
|
1599
|
+
} finally {
|
|
1600
|
+
if (seq === this.hydrateSeq) this.state.hydrated = true;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
handleDisabled({ value }) {
|
|
1604
|
+
for (const rtr of this.rtrs.values()) this.onAttach(rtr);
|
|
1346
1605
|
value && this.adapter?.remove(this.config.key);
|
|
1347
1606
|
}
|
|
1348
|
-
|
|
1349
|
-
for (const
|
|
1350
|
-
|
|
1607
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1608
|
+
for (const rtr of this.rtrs.values()) {
|
|
1609
|
+
for (const p of prevs) rtr.off(p, this.handleSave);
|
|
1610
|
+
for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
|
|
1611
|
+
}
|
|
1351
1612
|
}
|
|
1352
|
-
|
|
1613
|
+
handleSave(e) {
|
|
1614
|
+
if (!this.state.hydrated) return e.stopImmediatePropagation();
|
|
1353
1615
|
if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout2(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
|
|
1354
1616
|
}
|
|
1355
|
-
/** Clears persisted payload for this
|
|
1617
|
+
/** Clears persisted payload for this module instance and drops any pending save. */
|
|
1356
1618
|
clear() {
|
|
1357
1619
|
clearTimeout(this.saveTimeoutId);
|
|
1358
|
-
this.saveTimeoutId = -1
|
|
1620
|
+
this.saveTimeoutId = -1;
|
|
1621
|
+
for (const rtr of this.rtrs.values()) rtr.stall(() => this.saveTimeoutId = 0);
|
|
1359
1622
|
this.adapter?.remove(this.config.key);
|
|
1360
1623
|
}
|
|
1361
1624
|
onDestroy() {
|
|
1362
|
-
!this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1625
|
+
this.state.hydrated && !this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1363
1626
|
}
|
|
1364
1627
|
};
|
|
1365
|
-
var
|
|
1628
|
+
var PERSIST_MODULE_BUILD = { disabled: false, key: "REACTOR_STORE", throttle: 2500, useSnapshot: false };
|
|
1366
1629
|
|
|
1367
|
-
// src/ts/
|
|
1368
|
-
var
|
|
1369
|
-
static
|
|
1630
|
+
// src/ts/modules/timeTravel.ts
|
|
1631
|
+
var TimeTravelModule = class extends BaseReactorModule {
|
|
1632
|
+
static moduleName = "timeTravel";
|
|
1370
1633
|
lastTimestamp = 0;
|
|
1371
1634
|
playbackTimeoutId = -1;
|
|
1372
1635
|
constructor(config, rtr) {
|
|
1373
|
-
super({ ...
|
|
1636
|
+
super({ ...TIME_TRAVEL_MODULE_BUILD, ...config }, rtr, { initialState: {}, history: [], currentFrame: 0, paused: true });
|
|
1374
1637
|
}
|
|
1375
1638
|
// ===========================================================================
|
|
1376
1639
|
// THE FOUNDATION & WIRETAP (Passive Recording)
|
|
1377
1640
|
// ===========================================================================
|
|
1378
1641
|
wire() {
|
|
1379
|
-
this.rtr.config.referenceTracking = this.rtr.config.smartCloning = this.rtr.config.eventTimeStamps = true;
|
|
1380
|
-
if (!this.state.history.length || this.state.initialState == null) this.state.initialState = this.rtr.snapshot();
|
|
1381
1642
|
this.lastTimestamp = performance.now();
|
|
1382
1643
|
this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
|
|
1383
|
-
this.config.on("paths", this.
|
|
1644
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1384
1645
|
!this.state.paused && this.play();
|
|
1385
1646
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1647
|
+
onAttach(rtr, rid) {
|
|
1648
|
+
rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
|
|
1649
|
+
if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
|
|
1650
|
+
for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
|
|
1651
|
+
}
|
|
1652
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1653
|
+
for (const rtr of this.rtrs.values()) {
|
|
1654
|
+
for (const p of prevs) rtr.off(p, this.record);
|
|
1655
|
+
for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
|
|
1656
|
+
}
|
|
1389
1657
|
}
|
|
1390
1658
|
/** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
|
|
1391
|
-
record(e) {
|
|
1659
|
+
record(e, rid = this.rids.get(e.reactor)) {
|
|
1392
1660
|
if (!this.state.paused) return;
|
|
1393
1661
|
if (this.state.currentFrame < this.state.history.length) this.state.history = this.state.history.slice(0, this.state.currentFrame);
|
|
1394
1662
|
if (this.state.history.length >= this.config.maxHistoryLength) this.state.history = this.state.history.slice(1);
|
|
1395
|
-
this.state.history.push({ path: e.target.path, value:
|
|
1663
|
+
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 });
|
|
1664
|
+
if (e.rejected) this.state.history[this.state.history.length - 1].rejected = e.rejected;
|
|
1396
1665
|
this.state.currentFrame = this.state.history.length;
|
|
1397
1666
|
this.lastTimestamp = e.timestamp;
|
|
1398
1667
|
}
|
|
@@ -1401,7 +1670,7 @@ var sia = (() => {
|
|
|
1401
1670
|
this.pause();
|
|
1402
1671
|
this.playbackTimeoutId = -1;
|
|
1403
1672
|
this.state.history.length = this.state.currentFrame = 0;
|
|
1404
|
-
this.state.initialState = this.rtr.snapshot();
|
|
1673
|
+
this.state.initialState = Object.fromEntries(this.rtrs.entries().map(([rid, rtr]) => [rid, rtr.snapshot()]));
|
|
1405
1674
|
this.lastTimestamp = performance.now();
|
|
1406
1675
|
}
|
|
1407
1676
|
// ===========================================================================
|
|
@@ -1414,12 +1683,13 @@ var sia = (() => {
|
|
|
1414
1683
|
while (this.state.currentFrame !== target) {
|
|
1415
1684
|
const e = this.state.history[forward ? this.state.currentFrame : this.state.currentFrame - 1];
|
|
1416
1685
|
if (!e) break;
|
|
1417
|
-
|
|
1418
|
-
|
|
1686
|
+
const rtr = this.rtrs.get(e.rid) || this.rtrs.values().next().value;
|
|
1687
|
+
if (forward) e.type === "delete" ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.value, rtr.config));
|
|
1688
|
+
else !e.hadKey ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.oldValue, rtr.config));
|
|
1419
1689
|
forward ? this.state.currentFrame++ : this.state.currentFrame--;
|
|
1420
|
-
if (e.rejected)
|
|
1690
|
+
if (e.rejected) rtr.log(`[Reactor ${this.name} Module] ${forward ? "Replaying" : "Reversing"} REJECTED intent at "${e.path}"`);
|
|
1421
1691
|
}
|
|
1422
|
-
this.rtr.tick();
|
|
1692
|
+
for (const rtr of this.rtrs.values()) rtr.tick();
|
|
1423
1693
|
if (!keepShield) this.state.paused = true;
|
|
1424
1694
|
}
|
|
1425
1695
|
/** Step through time, Moves the playhead and teleports the state. */
|
|
@@ -1438,9 +1708,9 @@ var sia = (() => {
|
|
|
1438
1708
|
async automove(forward = true) {
|
|
1439
1709
|
this.state.paused = false;
|
|
1440
1710
|
while ((forward ? this.state.currentFrame < this.state.history.length : this.state.currentFrame > 0) && !this.state.paused) {
|
|
1441
|
-
const
|
|
1711
|
+
const idx = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[forward ? idx + 1 : idx - 1];
|
|
1442
1712
|
this.jumpTo(this.state.currentFrame + (forward ? 1 : -1), true);
|
|
1443
|
-
if (e?.
|
|
1713
|
+
if (e?.deltat > 0) await new Promise((res) => this.playbackTimeoutId = setTimeout2(() => res(0), Math.min(e.deltat, this.config.maxPlaybackDelay), this.signal));
|
|
1444
1714
|
}
|
|
1445
1715
|
this.state.paused = true;
|
|
1446
1716
|
}
|
|
@@ -1458,24 +1728,24 @@ var sia = (() => {
|
|
|
1458
1728
|
try {
|
|
1459
1729
|
return JSON.stringify(this.state, replacer, space);
|
|
1460
1730
|
} catch (e) {
|
|
1461
|
-
return this.
|
|
1731
|
+
return this.rtrs.values().next().value?.log(`[Reactor ${this.name} Module] Failed to export session`), "";
|
|
1462
1732
|
}
|
|
1463
1733
|
}
|
|
1464
1734
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
1465
|
-
import(json) {
|
|
1735
|
+
import(json, reviver) {
|
|
1466
1736
|
try {
|
|
1467
|
-
setAny(this.state, "*", JSON.parse(json));
|
|
1737
|
+
setAny(this.state, "*", JSON.parse(json, reviver));
|
|
1468
1738
|
this.lastTimestamp = performance.now();
|
|
1469
1739
|
const resume = !this.state.paused, target = this.state.currentFrame;
|
|
1470
1740
|
this.state.paused = false;
|
|
1471
|
-
|
|
1741
|
+
for (const [rid, rtr] of this.rtrs) setAny(rtr.core, "*", deepClone(this.state.initialState[rid], rtr.config)), rtr.tick();
|
|
1472
1742
|
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
1473
1743
|
} catch (e) {
|
|
1474
|
-
this.
|
|
1744
|
+
this.rtrs.values().next().value?.log(`[Reactor ${this.name} Module] Failed to load session`);
|
|
1475
1745
|
}
|
|
1476
1746
|
}
|
|
1477
1747
|
};
|
|
1478
|
-
var
|
|
1748
|
+
var TIME_TRAVEL_MODULE_BUILD = { maxPlaybackDelay: 2e3 };
|
|
1479
1749
|
|
|
1480
1750
|
// src/ts/adapters/vanilla.ts
|
|
1481
1751
|
var vanilla_exports = {};
|
|
@@ -1590,9 +1860,9 @@ var sia = (() => {
|
|
|
1590
1860
|
* const stop = atrkr.callback(() => console.log("changed")); // re-run after when ".user.name" changes
|
|
1591
1861
|
* @example Packaged Customization
|
|
1592
1862
|
* const atrkr = new Autotracker(); // no reactor passed
|
|
1593
|
-
* withTracker(atrkr, () => state.user.name); // import `withTracker`
|
|
1863
|
+
* withTracker(atrkr, () => state.user.name); // import `withTracker` too
|
|
1594
1864
|
* const stop = atrkr.callback(() => console.log("sync"), { sync: true }); // re-run immediately when ".user.name" changes, works on any path used from any reactor state
|
|
1595
|
-
* @example Extensive
|
|
1865
|
+
* @example Extensive Customization
|
|
1596
1866
|
* atrkr.unblock();
|
|
1597
1867
|
* const prev = CTX.autotracker;
|
|
1598
1868
|
* CTX.autotracker = atrkr; // import CTX first
|
|
@@ -1640,8 +1910,8 @@ var sia = (() => {
|
|
|
1640
1910
|
|
|
1641
1911
|
// src/ts/adapters/vanilla/TimeTravelOverlay.ts
|
|
1642
1912
|
var keys = {
|
|
1643
|
-
overrides: ["Ctrl+z", "Cmd+z", "Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z", "
|
|
1644
|
-
shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis:
|
|
1913
|
+
overrides: ["Ctrl+z", "Cmd+z", "Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z", "Home", "End", ",", ".", "ArrowLeft", "ArrowRight", "Space", "Alt+Space", "Escape", "Delete", "e", "i", "c"],
|
|
1914
|
+
shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: "Home", ending: "End", prevFrame: ",", nextFrame: ".", skipBwd: "ArrowLeft", skipFwd: "ArrowRight", playPause: "Space", rewind: "Alt+Space", closeOverlay: "Escape", clrHistory: "Delete", export: "e", import: "i", clear: "c" }
|
|
1645
1915
|
};
|
|
1646
1916
|
var TimeTravelOverlay = class _TimeTravelOverlay {
|
|
1647
1917
|
static count = 0;
|
|
@@ -1652,22 +1922,22 @@ var sia = (() => {
|
|
|
1652
1922
|
els;
|
|
1653
1923
|
clups = [];
|
|
1654
1924
|
keyup;
|
|
1655
|
-
/** Creates a docked TimeTravel overlay bound to a
|
|
1656
|
-
* @param time TimeTravel
|
|
1925
|
+
/** Creates a docked TimeTravel overlay bound to a module instance.
|
|
1926
|
+
* @param time TimeTravel module instance that owns timeline operations.
|
|
1657
1927
|
* @param build Optional initial overlay config overrides.
|
|
1658
1928
|
*/
|
|
1659
1929
|
constructor(time, build = {}) {
|
|
1660
1930
|
this.time = time;
|
|
1661
1931
|
this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
|
|
1662
1932
|
this.state.open = !!this.config.startOpen;
|
|
1663
|
-
const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis
|
|
1933
|
+
const s = this.time.state, host = createEl("div", { className: "tt-overlay-host" }), toggle = createEl("button", { className: "tt-overlay-toggle", type: "button", onclick: () => this.state.open = !this.state.open }), panel = createEl("aside", { className: "tt-overlay", ariaLabel: "time travel overlay" }), title = createEl("div", { className: "title" }), frame = createEl("span", { className: "muted" }), clrHistory = createEl("button", { textContent: `Clear History${formatKeyForDisplay(keys.shortcuts.clrHistory)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clrHistory, false), onclick: () => (this.time.clear(), this.state.import = "") }), undo = createEl("button", { textContent: `Undo${formatKeyForDisplay(keys.shortcuts.undo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.undo, false), onclick: this.time.undo }), redo = createEl("button", { textContent: `Redo${formatKeyForDisplay(keys.shortcuts.redo[0])}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.redo, false), onclick: this.time.redo }), genesis = createEl("button", { textContent: `Genesis${formatKeyForDisplay(keys.shortcuts.genesis)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.genesis, false), onclick: () => this.time.jumpTo(0) }), playPause = createEl("button", { onclick: () => this.time[s.paused ? "play" : "pause"](), ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.playPause, false) }), rewind = createEl("button", { textContent: `Rewind${formatKeyForDisplay(keys.shortcuts.rewind)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.rewind, false), onclick: this.time.rewind }), range = createEl("input", { type: "range", min: "0", max: "0", value: "0", title: "time travel frame", ariaLabel: "time travel frame", oninput: () => this.time.jumpTo(Number(range.value)) }), exp = createEl("button", { textContent: `Export${formatKeyForDisplay(keys.shortcuts.export)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.export, false), onclick: () => this.state.import = this.time.export(null, 2) }), imp = createEl("button", { textContent: `Import${formatKeyForDisplay(keys.shortcuts.import)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.import, false), onclick: () => this.state.import.trim().length && this.time.import(this.state.import) }), clr = createEl("button", { textContent: `Clear${formatKeyForDisplay(keys.shortcuts.clear)}`, ariaKeyShortcuts: parseForARIAKS(keys.shortcuts.clear, false), onclick: () => this.state.import = "" }), payload = createEl("textarea", { className: "tt-io", readOnly: true, placeholder: "current payload json", title: "current payload" }), io = createEl("textarea", { className: "tt-io", placeholder: "timeline payload json", oninput: () => this.state.import = io.value }), foot = createEl("p", { className: "tt-footnote", textContent: "Want this in your app? " }), link = createEl("a", { target: "_blank", rel: "noreferrer noopener", textContent: "sia-reactor", href: "https://www.npmjs.com/package/sia-reactor" }), box = createEl("div", { className: "tt-status-box" }), status = createEl("div", { className: "tt-status-row" }), row1 = createEl("div", { className: "tt-row" }), row2 = createEl("div", { className: "tt-row" }), row3 = createEl("div", { className: "tt-row" });
|
|
1664
1934
|
status.append((box.append(frame), box), clrHistory);
|
|
1665
1935
|
panel.append(title, status, (row1.append(undo, redo, genesis), row1), (row2.append(playPause, rewind), row2), payload, range, (row3.append(exp, imp, clr), row3), io, (foot.appendChild(link), foot));
|
|
1666
1936
|
host.append(toggle, panel);
|
|
1667
1937
|
this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
|
|
1668
1938
|
this.keyup = (e) => {
|
|
1669
|
-
const a = this.state.open && keyEventAllowed(e, keys);
|
|
1670
|
-
a === "undo" ? this.time.undo() : a === "redo" ? this.time.redo() : a === "genesis" ? this.time.jumpTo(0) : a === "prevFrame" ? this.time.step(1, false) : a === "nextFrame" ? this.time.step(1, true) : a === "skipBwd" ? this.time.step(5, false) : a === "skipFwd" ? this.time.step(5, true) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "clrHistory" ? this.time.clear() : a === "closeOverlay" ? this.state.open = false : a === "export" ? this.state.import = this.time.export() : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = "");
|
|
1939
|
+
const a = this.state.open && (this.config.devOnly ? CTX.isDevEnv : true) && keyEventAllowed(e, keys);
|
|
1940
|
+
a === "undo" ? this.time.undo() : a === "redo" ? this.time.redo() : a === "genesis" ? this.time.jumpTo(0) : a === "ending" ? this.time.jumpTo(s.history.length) : a === "prevFrame" ? this.time.step(1, false) : a === "nextFrame" ? this.time.step(1, true) : a === "skipBwd" ? this.time.step(5, false) : a === "skipFwd" ? this.time.step(5, true) : a === "rewind" ? this.time.rewind() : a === "playPause" ? this.time[s.paused ? "play" : "pause"]() : a === "clrHistory" ? this.time.clear() : a === "closeOverlay" ? this.state.open = false : a === "export" ? this.state.import = this.time.export() : a === "import" ? this.state.import.trim().length && this.time.import(this.state.import) : a === "clear" && (this.state.import = "");
|
|
1671
1941
|
};
|
|
1672
1942
|
window.addEventListener("keydown", this.keyup);
|
|
1673
1943
|
const sync = [
|
|
@@ -1682,7 +1952,8 @@ var sia = (() => {
|
|
|
1682
1952
|
effect(() => {
|
|
1683
1953
|
frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
|
|
1684
1954
|
range.disabled = clrHistory.disabled = !s.history.length;
|
|
1685
|
-
genesis.disabled =
|
|
1955
|
+
genesis.disabled = undo.disabled = !s.currentFrame;
|
|
1956
|
+
rewind.disabled = !s.paused || !s.currentFrame;
|
|
1686
1957
|
playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
|
|
1687
1958
|
range.max = String(s.history.length);
|
|
1688
1959
|
range.value = String(Math.min(s.currentFrame, s.history.length));
|
|
@@ -1696,7 +1967,7 @@ var sia = (() => {
|
|
|
1696
1967
|
this.clups.push(...sync);
|
|
1697
1968
|
}
|
|
1698
1969
|
destroy() {
|
|
1699
|
-
this.clups
|
|
1970
|
+
for (const clup of this.clups) clup();
|
|
1700
1971
|
this.keyup && window.removeEventListener("keydown", this.keyup);
|
|
1701
1972
|
this.els.host.remove();
|
|
1702
1973
|
nuke(this), --_TimeTravelOverlay.count;
|