sia-reactor 0.0.21 → 0.0.23
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-DxqJL0Zk.d.ts → TimeTravelOverlay-Dglcwpg-.d.ts} +9 -8
- package/dist/{TimeTravelOverlay-CJv-S_Km.d.cts → TimeTravelOverlay-OjklzuCD.d.cts} +9 -8
- package/dist/adapters/react.cjs +74 -91
- 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 +74 -91
- 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-TFLYCXK4.js → chunk-5JNWC7Z4.js} +14 -13
- package/dist/{chunk-DP74DVRT.js → chunk-MKL3JUPO.js} +55 -15
- package/dist/{chunk-2WBPGSRL.js → chunk-MSTHQVNK.js} +61 -78
- package/dist/{index-Oie9hhE8.d.cts → index-m0aAWxhX.d.cts} +330 -218
- package/dist/{index-Oie9hhE8.d.ts → index-m0aAWxhX.d.ts} +330 -218
- package/dist/index.cjs +69 -89
- 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} +464 -195
- package/dist/modules.d.cts +52 -0
- package/dist/modules.d.ts +52 -0
- package/dist/modules.js +619 -0
- package/dist/super.d.ts +642 -298
- package/dist/super.global.js +481 -210
- package/dist/timeTravel-DExvNb04.d.ts +352 -0
- package/dist/timeTravel-DctvcHVt.d.cts +352 -0
- package/dist/utils.cjs +59 -14
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +7 -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,14 +73,13 @@ 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 = () => {
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
// src/ts/utils/obj.ts
|
|
82
|
-
var
|
|
82
|
+
var arrRegex = /^([^\[\]]+)\[(\d+)\]$/;
|
|
83
83
|
function isObj(obj, arraycheck = true) {
|
|
84
84
|
return "object" === typeof obj && obj !== null && (arraycheck ? !Array.isArray(obj) : true);
|
|
85
85
|
}
|
|
@@ -87,7 +87,7 @@ var sia = (() => {
|
|
|
87
87
|
return (typecheck ? isObj(obj, false) : true) && (config.crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
|
|
88
88
|
}
|
|
89
89
|
function canHandle(obj, config = NIL, typecheck = true) {
|
|
90
|
-
if (typecheck && !isObj(obj, false)) return false;
|
|
90
|
+
if (typecheck && !isObj(obj, false) || obj[INERTIA]) return false;
|
|
91
91
|
if (Array.isArray(obj) || !config.preserveContext && isPOJO(obj, config, false)) return true;
|
|
92
92
|
if (config.preserveContext) return !(obj instanceof Map) && !(obj instanceof Set) && !(obj instanceof WeakMap) && !(obj instanceof WeakSet) && !(obj instanceof Error) && !(obj instanceof Number) && !(obj instanceof Date) && !(obj instanceof String) && !(obj instanceof RegExp) && !(obj instanceof ArrayBuffer) && !(obj instanceof Promise);
|
|
93
93
|
return false;
|
|
@@ -98,7 +98,7 @@ var sia = (() => {
|
|
|
98
98
|
const keys2 = key.split(separator);
|
|
99
99
|
let currObj = source;
|
|
100
100
|
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
101
|
-
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(
|
|
101
|
+
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRegex);
|
|
102
102
|
if (match) {
|
|
103
103
|
const [, key3, iStr] = match;
|
|
104
104
|
if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return void 0;
|
|
@@ -115,7 +115,7 @@ var sia = (() => {
|
|
|
115
115
|
if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
|
|
116
116
|
const keys2 = key.split(separator);
|
|
117
117
|
for (let currObj = target, i = 0, len = keys2.length; i < len; i++) {
|
|
118
|
-
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(
|
|
118
|
+
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRegex);
|
|
119
119
|
if (match) {
|
|
120
120
|
const [, key3, iStr] = match;
|
|
121
121
|
if (!Array.isArray(currObj[key3])) currObj[key3] = [];
|
|
@@ -136,7 +136,7 @@ var sia = (() => {
|
|
|
136
136
|
if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
|
|
137
137
|
const keys2 = key.split(separator);
|
|
138
138
|
for (let currObj = target, i = 0, len = keys2.length; i < len; i++) {
|
|
139
|
-
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(
|
|
139
|
+
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRegex);
|
|
140
140
|
if (match) {
|
|
141
141
|
const [, key3, iStr] = match;
|
|
142
142
|
if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return;
|
|
@@ -154,7 +154,7 @@ var sia = (() => {
|
|
|
154
154
|
if (!key.includes(separator)) return key in source;
|
|
155
155
|
const keys2 = key.split(separator);
|
|
156
156
|
for (let currObj = source, i = 0, len = keys2.length; i < len; i++) {
|
|
157
|
-
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(
|
|
157
|
+
const key2 = keyFunc ? keyFunc(keys2[i]) : keys2[i], match = key2.includes("[") && key2.match(arrRegex);
|
|
158
158
|
if (match) {
|
|
159
159
|
const [, key3, iStr] = match;
|
|
160
160
|
if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return false;
|
|
@@ -171,16 +171,48 @@ 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
|
}
|
|
181
|
-
function
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
+
function fanout(a, b, c, d) {
|
|
185
|
+
const isEvPd = !!a?.target, isPath = !isEvPd && "string" === typeof b, [state2, path, olds, news, opts, type] = isEvPd ? [a.root, a.currentTarget.path, a.currentTarget.oldValue, a.currentTarget.value, b || NIL, a.type] : isPath ? [a, b, getAny(a, b), c, d || NIL, void 0] : [void 0, void 0, a, b, c || NIL, void 0], target = isEvPd ? getAny(a.root, a.currentTarget.path) : isPath ? getAny(state2, path) : olds;
|
|
186
|
+
if (isEvPd && type !== "set" && type !== "delete" || !target || !canHandle(news, opts)) return;
|
|
187
|
+
const prev = CTX.isCascading;
|
|
188
|
+
CTX.isCascading = isEvPd;
|
|
189
|
+
try {
|
|
190
|
+
const walk = (target2, obj, depth = isEvPd ? 1 : Infinity, keys2 = Object.keys(obj)) => {
|
|
191
|
+
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
192
|
+
const val = obj[keys2[i]];
|
|
193
|
+
try {
|
|
194
|
+
if ((opts.atomic ?? true) && Array.isArray(val)) target2[keys2[i]] = val, target2[keys2[i]].length = target2[keys2[i]].length;
|
|
195
|
+
else depth > 1 && canHandle(val, opts) ? walk(target2[keys2[i]] ||= {}, val, depth - 1) : target2[keys2[i]] = val;
|
|
196
|
+
} catch (e) {
|
|
197
|
+
if (e instanceof RangeError) throw e;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
if ((opts.atomic ?? true) && Array.isArray(news) && isPath) setAny(state2, path, news), getAny(state2, path).length = news.length;
|
|
202
|
+
else walk(target, opts.merge ? mergeObjs(olds, news, opts) : news, opts.depth === true ? Infinity : opts.depth);
|
|
203
|
+
} finally {
|
|
204
|
+
CTX.isCascading = prev;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
var fanoutOptsArr = ["merge", "depth", "atomic"];
|
|
208
|
+
function mergeObjs(o1, o2, config, pojocheck = true) {
|
|
209
|
+
if (pojocheck && (!isPOJO(o1 || NIL, config) || !isPOJO(o2 || NIL, config))) return o2;
|
|
210
|
+
const merged = { ...o1 ||= {}, ...o2 ||= {} }, keys2 = Object.keys(merged);
|
|
211
|
+
for (let i = 0, len = keys2.length; i < len; i++) {
|
|
212
|
+
const o1C = o1[keys2[i]], o2C = o2[keys2[i]];
|
|
213
|
+
if (isPOJO(o1C, config) && isPOJO(o2C, config)) merged[keys2[i]] = mergeObjs(o1C, o2C, config, false);
|
|
214
|
+
}
|
|
215
|
+
return merged;
|
|
184
216
|
}
|
|
185
217
|
function getTrailRecords(obj, path, reverse = false) {
|
|
186
218
|
const parts = path.split("."), chain = [["*", obj, obj]];
|
|
@@ -195,7 +227,12 @@ var sia = (() => {
|
|
|
195
227
|
const clone = config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {};
|
|
196
228
|
seen.set(obj, clone);
|
|
197
229
|
const keys2 = config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
198
|
-
for (let i = 0, len = keys2.length; i < len; i++)
|
|
230
|
+
for (let i = 0, len = keys2.length; i < len; i++)
|
|
231
|
+
try {
|
|
232
|
+
clone[keys2[i]] = deepClone(obj[keys2[i]], config, seen);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
if (e instanceof RangeError) throw e;
|
|
235
|
+
}
|
|
199
236
|
return clone;
|
|
200
237
|
}
|
|
201
238
|
function nuke(target) {
|
|
@@ -235,35 +272,34 @@ var sia = (() => {
|
|
|
235
272
|
staticType;
|
|
236
273
|
/** Original event target context. */
|
|
237
274
|
target;
|
|
238
|
-
/** Root reactive object for this event wave. */
|
|
275
|
+
/** Root reactive object for this event instance wave. */
|
|
239
276
|
root;
|
|
240
|
-
/** Original target path for this event wave. */
|
|
277
|
+
/** Original target path for this event instance wave. */
|
|
241
278
|
path;
|
|
242
279
|
/** Current value at the event target path. */
|
|
243
280
|
value;
|
|
244
281
|
/** Previous value at the event target path. */
|
|
245
282
|
oldValue;
|
|
246
|
-
/** Whether resolve/reject intent semantics are allowed for this event. */
|
|
283
|
+
/** Whether resolve/reject intent semantics are allowed for this event instance. */
|
|
247
284
|
rejectable;
|
|
248
|
-
/** Whether this event wave can bubble back up to ancestors or just capture down. */
|
|
285
|
+
/** Whether this event instance wave can bubble back up to ancestors or just capture down. */
|
|
249
286
|
bubbles;
|
|
250
287
|
/**
|
|
251
|
-
* `DOMHighResTimeStamp` for this event payload for native event parity and accuracy.
|
|
288
|
+
* `DOMHighResTimeStamp` for this event instance payload for native event parity and accuracy.
|
|
252
289
|
* Enable `eventTimeStamps` option, then use this over custom timestamps in listeners for accuracy.
|
|
253
290
|
* */
|
|
254
291
|
timestamp;
|
|
255
|
-
|
|
292
|
+
/** The `Reactor` instance that dispatched this event instance. */
|
|
293
|
+
reactor;
|
|
256
294
|
_resolved = "";
|
|
257
295
|
_rejected = "";
|
|
258
296
|
_propagationStopped = false;
|
|
259
297
|
_immediatePropagationStopped = false;
|
|
260
298
|
/**
|
|
261
299
|
* @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.
|
|
300
|
+
* @param reactor The `Reactor` instance creating this event instance.
|
|
265
301
|
*/
|
|
266
|
-
constructor(payload,
|
|
302
|
+
constructor(payload, reactor) {
|
|
267
303
|
this.staticType = this.type = payload.type;
|
|
268
304
|
this.target = payload.target;
|
|
269
305
|
this.currentTarget = payload.currentTarget;
|
|
@@ -272,9 +308,9 @@ var sia = (() => {
|
|
|
272
308
|
this.value = payload.target.value;
|
|
273
309
|
this.oldValue = payload.target.oldValue;
|
|
274
310
|
this.rejectable = payload.rejectable;
|
|
275
|
-
this.bubbles =
|
|
276
|
-
if (
|
|
277
|
-
|
|
311
|
+
this.bubbles = !!reactor.config.eventBubbling;
|
|
312
|
+
if (reactor.config.eventTimeStamps) this.timestamp = performance.now();
|
|
313
|
+
this.reactor = reactor;
|
|
278
314
|
}
|
|
279
315
|
/** Whether propagation has been stopped. */
|
|
280
316
|
get propagationStopped() {
|
|
@@ -304,9 +340,9 @@ var sia = (() => {
|
|
|
304
340
|
* @example e.resolve("API Load successful"); // message
|
|
305
341
|
*/
|
|
306
342
|
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}"
|
|
343
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`resolve()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
344
|
+
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.`);
|
|
345
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`}`);
|
|
310
346
|
}
|
|
311
347
|
/** Rejection reason for rejectable events. */
|
|
312
348
|
get rejected() {
|
|
@@ -319,9 +355,9 @@ var sia = (() => {
|
|
|
319
355
|
* @example e.resolve("User is not logged in"); // reason
|
|
320
356
|
*/
|
|
321
357
|
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}"
|
|
358
|
+
if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`reject()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
|
|
359
|
+
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.`);
|
|
360
|
+
if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`}`);
|
|
325
361
|
}
|
|
326
362
|
/**
|
|
327
363
|
* Returns event path values from target to root.
|
|
@@ -330,24 +366,22 @@ var sia = (() => {
|
|
|
330
366
|
composedPath() {
|
|
331
367
|
return getTrailRecords(this.root, this.path, true).map((r) => r[2]);
|
|
332
368
|
}
|
|
333
|
-
get canWarn() {
|
|
334
|
-
return this._warn !== NOOP;
|
|
335
|
-
}
|
|
336
369
|
};
|
|
337
370
|
|
|
338
371
|
// src/ts/core/reactor.ts
|
|
339
372
|
var Reactor = class {
|
|
373
|
+
/** Logger function for this reactor instance, override if desired, `this.canLog = false` resets. */
|
|
340
374
|
log = NOOP;
|
|
375
|
+
/** The core state object for this reactor instance. */
|
|
341
376
|
core;
|
|
342
377
|
// `?:`s | pay the ~800 byte price upfront for what u might never use
|
|
343
|
-
|
|
378
|
+
/** The modules being used by this reactor. */
|
|
379
|
+
modules;
|
|
380
|
+
/** Configuration options for this reactor instance. */
|
|
344
381
|
config;
|
|
382
|
+
/** Whether this reactor instance is currently batching updates, a window view into the engine timing */
|
|
345
383
|
isBatching = false;
|
|
346
384
|
// 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
385
|
queue;
|
|
352
386
|
// Tasks to run after flush
|
|
353
387
|
batch;
|
|
@@ -380,7 +414,7 @@ var sia = (() => {
|
|
|
380
414
|
if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
|
|
381
415
|
const cached = this.proxyCache.get(target);
|
|
382
416
|
if (cached) return cached;
|
|
383
|
-
if (
|
|
417
|
+
if (!canHandle(target, this.config, false)) return target;
|
|
384
418
|
rejectable ||= target[REJECTABLE];
|
|
385
419
|
indiffable ||= target[INDIFFABLE];
|
|
386
420
|
const proxy = new Proxy(target, {
|
|
@@ -414,7 +448,7 @@ var sia = (() => {
|
|
|
414
448
|
safeValue = value?.[RAW] || value;
|
|
415
449
|
unchanged = this.config.equalityFunction(safeValue, safeOldValue);
|
|
416
450
|
}
|
|
417
|
-
if (!indiffable && unchanged && !
|
|
451
|
+
if (!indiffable && unchanged && !CTX.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
|
|
418
452
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
419
453
|
if (this.setters) {
|
|
420
454
|
const wildcords = this.setters.get("*");
|
|
@@ -568,7 +602,7 @@ var sia = (() => {
|
|
|
568
602
|
if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
|
|
569
603
|
}
|
|
570
604
|
wave(path, payload) {
|
|
571
|
-
const e = new ReactorEvent(payload, this
|
|
605
|
+
const e = new ReactorEvent(payload, this), chain = getTrailRecords(this.core, path);
|
|
572
606
|
e.eventPhase = ReactorEvent.CAPTURING_PHASE;
|
|
573
607
|
for (let i = 0; i <= chain.length - 2; i++) {
|
|
574
608
|
if (e.propagationStopped) break;
|
|
@@ -642,8 +676,8 @@ var sia = (() => {
|
|
|
642
676
|
return depth;
|
|
643
677
|
}
|
|
644
678
|
getContext(path) {
|
|
645
|
-
const
|
|
646
|
-
return { path, value, key: path.slice(
|
|
679
|
+
const last = path.lastIndexOf("."), value = getAny(this.core, path), object = last === -1 ? this.core : getAny(this.core, path.slice(0, last));
|
|
680
|
+
return { path, value, key: path.slice(last + 1) || "", hadKey: true, object };
|
|
647
681
|
}
|
|
648
682
|
bindSignal(cord, sig) {
|
|
649
683
|
if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
|
|
@@ -659,7 +693,12 @@ var sia = (() => {
|
|
|
659
693
|
const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
|
|
660
694
|
seen.set(obj, clone);
|
|
661
695
|
const keys2 = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
|
|
662
|
-
for (let i = 0, len = keys2.length; i < len; i++)
|
|
696
|
+
for (let i = 0, len = keys2.length; i < len; i++)
|
|
697
|
+
try {
|
|
698
|
+
clone[keys2[i]] = this.cloned(obj[keys2[i]], raw, seen);
|
|
699
|
+
} catch (e) {
|
|
700
|
+
if (e instanceof RangeError) throw e;
|
|
701
|
+
}
|
|
663
702
|
if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
|
|
664
703
|
return clone;
|
|
665
704
|
}
|
|
@@ -747,7 +786,7 @@ var sia = (() => {
|
|
|
747
786
|
* rtr.delete("cache.temp", () => TERMINATOR);
|
|
748
787
|
*/
|
|
749
788
|
delete(path, callback, options) {
|
|
750
|
-
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path
|
|
789
|
+
return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path));
|
|
751
790
|
}
|
|
752
791
|
/** Registers a delete mediator for a path that only triggers once. */
|
|
753
792
|
donce(path, callback, options) {
|
|
@@ -764,7 +803,7 @@ var sia = (() => {
|
|
|
764
803
|
}
|
|
765
804
|
/**
|
|
766
805
|
* Registers a watcher for a path.
|
|
767
|
-
* Watch callbacks run synchronously with the operation.
|
|
806
|
+
* Watch callbacks run synchronously with the operation, use leaf paths for reliability as it sees exact sets; no bubbling here.
|
|
768
807
|
* @param path Path or wildcard path.
|
|
769
808
|
* @param callback Watch callback.
|
|
770
809
|
* @param options Sync options.
|
|
@@ -813,7 +852,7 @@ var sia = (() => {
|
|
|
813
852
|
cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options), lDepth: depth !== void 0 ? this.getDepth(path) : depth };
|
|
814
853
|
if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
|
|
815
854
|
const target = this.getContext(path);
|
|
816
|
-
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this
|
|
855
|
+
callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this));
|
|
817
856
|
}
|
|
818
857
|
(cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
|
|
819
858
|
return this.bindSignal(cord, signal);
|
|
@@ -844,56 +883,39 @@ var sia = (() => {
|
|
|
844
883
|
return this.cloned(arguments.length < 2 ? this.core : branch, raw);
|
|
845
884
|
}
|
|
846
885
|
/**
|
|
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.
|
|
886
|
+
* Installs a module instance.
|
|
887
|
+
* @param target Module instance.
|
|
888
|
+
* @param id Optional identification tag for this instance in the module.
|
|
889
|
+
* @returns Current `Reactor` instance for fluent chaining.
|
|
866
890
|
*/
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
this.plugins?.get(name)?.destroy();
|
|
870
|
-
return (this.plugins ??= /* @__PURE__ */ new Map()).set(name, (plugin.setup(this), plugin)), this;
|
|
891
|
+
use(target, id) {
|
|
892
|
+
return (this.modules ??= /* @__PURE__ */ new Set()).add(target.setup(this, id)), this;
|
|
871
893
|
}
|
|
872
|
-
/** Resets
|
|
894
|
+
/** Resets this reactor instance to its initial state. */
|
|
873
895
|
reset() {
|
|
874
896
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
|
|
875
897
|
this.batch?.clear(), this.queue?.clear(), this.isBatching = false;
|
|
876
898
|
}
|
|
877
899
|
destroy() {
|
|
878
|
-
if (this.
|
|
900
|
+
if (this.modules) for (const mdle of this.modules) mdle.destroy();
|
|
879
901
|
this.reset(), nuke(this);
|
|
880
902
|
}
|
|
881
903
|
get canLog() {
|
|
882
|
-
return this.
|
|
904
|
+
return this.log !== NOOP;
|
|
883
905
|
}
|
|
884
906
|
set canLog(value) {
|
|
885
|
-
this.log =
|
|
907
|
+
this.log = value ? RTR_LOG : NOOP;
|
|
886
908
|
}
|
|
887
|
-
get
|
|
888
|
-
return this.config.
|
|
909
|
+
get canLineageTrace() {
|
|
910
|
+
return this.config.lineageTracing && this.config.referenceTracking;
|
|
889
911
|
}
|
|
890
912
|
get canSmartClone() {
|
|
891
|
-
return this.config.
|
|
913
|
+
return this.config.smartCloning && this.config.referenceTracking;
|
|
892
914
|
}
|
|
893
915
|
};
|
|
894
916
|
|
|
895
917
|
// 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", "
|
|
918
|
+
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
919
|
function reactive(target, build, preferences = NIL) {
|
|
898
920
|
if ("__Reactor__" in target) return target;
|
|
899
921
|
const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
|
|
@@ -901,7 +923,7 @@ var sia = (() => {
|
|
|
901
923
|
let key = methods[i];
|
|
902
924
|
if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
|
|
903
925
|
else if (preferences.whitelist?.includes(key)) continue;
|
|
904
|
-
descriptors[key] = { value: rtr[
|
|
926
|
+
descriptors[key] = { value: rtr[methods[i]].bind(rtr), ...locks };
|
|
905
927
|
}
|
|
906
928
|
descriptors["__Reactor__"] = { value: rtr, ...locks };
|
|
907
929
|
return Object.defineProperties(rtr.core, descriptors), rtr.core;
|
|
@@ -912,7 +934,7 @@ var sia = (() => {
|
|
|
912
934
|
function live(target) {
|
|
913
935
|
return delete getRaw(target)[INERTIA], target;
|
|
914
936
|
}
|
|
915
|
-
function isInert(target) {
|
|
937
|
+
function isInert(target = NIL) {
|
|
916
938
|
return !!getRaw(target)[INERTIA];
|
|
917
939
|
}
|
|
918
940
|
function intent(target) {
|
|
@@ -921,7 +943,7 @@ var sia = (() => {
|
|
|
921
943
|
function state(target) {
|
|
922
944
|
return delete getRaw(target)[REJECTABLE], target;
|
|
923
945
|
}
|
|
924
|
-
function isIntent(target) {
|
|
946
|
+
function isIntent(target = NIL) {
|
|
925
947
|
return !!getRaw(target)[REJECTABLE];
|
|
926
948
|
}
|
|
927
949
|
function volatile(target) {
|
|
@@ -930,25 +952,26 @@ var sia = (() => {
|
|
|
930
952
|
function stable(target) {
|
|
931
953
|
return delete getRaw(target)[INDIFFABLE], target;
|
|
932
954
|
}
|
|
933
|
-
function isVolatile(target) {
|
|
955
|
+
function isVolatile(target = NIL) {
|
|
934
956
|
return !!getRaw(target)[INDIFFABLE];
|
|
935
957
|
}
|
|
936
958
|
function getReactor(target, create = false, build) {
|
|
937
959
|
return (target instanceof Reactor ? target : target.__Reactor__) || (create ? new Reactor(target, build) : void 0);
|
|
938
960
|
}
|
|
939
|
-
function getRaw(target) {
|
|
940
|
-
return target
|
|
961
|
+
function getRaw(target = NIL) {
|
|
962
|
+
return target[RAW] || target;
|
|
941
963
|
}
|
|
942
|
-
function getVersion(target) {
|
|
964
|
+
function getVersion(target = NIL) {
|
|
943
965
|
return getRaw(target)[VERSION] || 0;
|
|
944
966
|
}
|
|
945
|
-
function getSnapshotVersion(target) {
|
|
967
|
+
function getSnapshotVersion(target = NIL) {
|
|
946
968
|
return getRaw(target)[SSVERSION] || 0;
|
|
947
969
|
}
|
|
948
970
|
|
|
949
971
|
// src/ts/utils.ts
|
|
950
972
|
var utils_exports = {};
|
|
951
973
|
__export(utils_exports, {
|
|
974
|
+
arrRegex: () => arrRegex,
|
|
952
975
|
assignEl: () => assignEl,
|
|
953
976
|
bindAllMethods: () => bindAllMethods,
|
|
954
977
|
canHandle: () => canHandle,
|
|
@@ -957,6 +980,8 @@ var sia = (() => {
|
|
|
957
980
|
createEl: () => createEl,
|
|
958
981
|
deepClone: () => deepClone,
|
|
959
982
|
deleteAny: () => deleteAny,
|
|
983
|
+
fanout: () => fanout,
|
|
984
|
+
fanoutOptsArr: () => fanoutOptsArr,
|
|
960
985
|
formatKeyForDisplay: () => formatKeyForDisplay,
|
|
961
986
|
formatKeyShortcutsForDisplay: () => formatKeyShortcutsForDisplay,
|
|
962
987
|
getAny: () => getAny,
|
|
@@ -1123,63 +1148,95 @@ var sia = (() => {
|
|
|
1123
1148
|
}
|
|
1124
1149
|
}
|
|
1125
1150
|
|
|
1126
|
-
// src/ts/
|
|
1127
|
-
var
|
|
1128
|
-
__export(
|
|
1129
|
-
|
|
1151
|
+
// src/ts/modules.ts
|
|
1152
|
+
var modules_exports = {};
|
|
1153
|
+
__export(modules_exports, {
|
|
1154
|
+
AsyncStorageAdapter: () => AsyncStorageAdapter,
|
|
1155
|
+
BaseReactorModule: () => BaseReactorModule,
|
|
1130
1156
|
BaseStorageAdapter: () => BaseStorageAdapter,
|
|
1157
|
+
COOKIE_ADAPTER_BUILD: () => COOKIE_ADAPTER_BUILD,
|
|
1158
|
+
CookieAdapter: () => CookieAdapter,
|
|
1131
1159
|
INDEXED_DB_ADAPTER_BUILD: () => INDEXED_DB_ADAPTER_BUILD,
|
|
1132
1160
|
IndexedDBAdapter: () => IndexedDBAdapter,
|
|
1133
1161
|
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1162
|
+
MemoryAdapter: () => MemoryAdapter,
|
|
1163
|
+
PERSIST_MODULE_BUILD: () => PERSIST_MODULE_BUILD,
|
|
1164
|
+
PersistModule: () => PersistModule,
|
|
1165
|
+
SessionStorageAdapter: () => SessionStorageAdapter,
|
|
1137
1166
|
StorageAdapter: () => StorageAdapter,
|
|
1138
|
-
|
|
1139
|
-
|
|
1167
|
+
TIME_TRAVEL_MODULE_BUILD: () => TIME_TRAVEL_MODULE_BUILD,
|
|
1168
|
+
TimeTravelModule: () => TimeTravelModule
|
|
1140
1169
|
});
|
|
1141
1170
|
|
|
1142
|
-
// src/ts/
|
|
1143
|
-
var
|
|
1144
|
-
|
|
1171
|
+
// src/ts/modules/base.ts
|
|
1172
|
+
var wpArr = ["*"];
|
|
1173
|
+
var BaseReactorModule = class {
|
|
1174
|
+
static moduleName;
|
|
1145
1175
|
get name() {
|
|
1146
|
-
return this.constructor.
|
|
1176
|
+
return this.constructor.moduleName;
|
|
1147
1177
|
}
|
|
1148
1178
|
ac = new AbortController();
|
|
1149
1179
|
signal = this.ac.signal;
|
|
1150
|
-
|
|
1180
|
+
rtrs = /* @__PURE__ */ new Map();
|
|
1181
|
+
rids = /* @__PURE__ */ new WeakMap();
|
|
1182
|
+
// for quick 0(1) lookups over iteration
|
|
1183
|
+
wired = false;
|
|
1151
1184
|
config;
|
|
1152
1185
|
state;
|
|
1153
1186
|
constructor(config, rtr, state2) {
|
|
1154
1187
|
guardAllMethods(this, this.guard);
|
|
1155
|
-
this.rtr = rtr;
|
|
1156
1188
|
this.config = isObj(config) ? reactive(config) : config;
|
|
1157
1189
|
this.state = isObj(state2) ? reactive(state2) : state2;
|
|
1190
|
+
rtr && this.attach(rtr);
|
|
1158
1191
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1192
|
+
/**
|
|
1193
|
+
* Connect to a `Reactor` instance, allows managing multiple reactors if needed.
|
|
1194
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
1195
|
+
* @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.
|
|
1196
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
1197
|
+
* @example
|
|
1198
|
+
* const mod = new MyModule().attach(state1).attach(state2); // implicit index-based ids by default, add a .setup() or `Reactor.use()` when ready for init.
|
|
1199
|
+
* @example
|
|
1200
|
+
* const persist = new PersistModule(config).attach(sessState, "session").attach(adminState, "session.admin"); // don't use "*", causes de-serialization issues.
|
|
1201
|
+
*/
|
|
1202
|
+
attach(target, id = this.rtrs.size) {
|
|
1203
|
+
const rtr = getReactor(target);
|
|
1204
|
+
if (!rtr || this.rtrs.has(id)) return this;
|
|
1205
|
+
return this.rids.set((this.rtrs.set(id, rtr), rtr), id), this.onAttach(rtr, id), this;
|
|
1206
|
+
}
|
|
1207
|
+
onAttach(_rtr, _rid) {
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Entry point called to initialize module wiring, calls `.attach(target, id)` first, `Reactor.use()` calls this internally.
|
|
1211
|
+
* Should run as last in `.attach()` chain or after all desired reactors if using multiple; so wiring is done safely after.
|
|
1212
|
+
* @param target `Reactor` instance or `reactive()` object to connect to.
|
|
1213
|
+
* @param id Optional id for the reactor, prefer over default implicit index id when managing multiple reactors.
|
|
1214
|
+
* @returns Current `ReactorModule` instance for fluent chaining.
|
|
1215
|
+
* @example
|
|
1216
|
+
* const mod = new MyModule().attach(state1).setup(state2); // if using multiple, this should run last; with same params as `.attach()` for a shorter chain
|
|
1217
|
+
*/
|
|
1218
|
+
setup(target, id) {
|
|
1219
|
+
return this.attach(target, id), !this.wired && (this.wire(), this.wired = true), this;
|
|
1163
1220
|
}
|
|
1164
1221
|
destroy() {
|
|
1165
1222
|
this.ac.abort();
|
|
1166
1223
|
this.onDestroy?.();
|
|
1167
1224
|
}
|
|
1168
1225
|
/**
|
|
1169
|
-
* Wraps a function with
|
|
1226
|
+
* Wraps a function with module-scoped error logging.
|
|
1170
1227
|
* Use this when creating functions dynamically (for example, before attaching an anonymous listener on the fly).
|
|
1171
1228
|
* @example
|
|
1172
1229
|
* window.addEventListener("resize", this.guard(() => this.syncLayout(true)), { signal: this.signal });
|
|
1173
1230
|
*/
|
|
1174
1231
|
guard = (fn) => {
|
|
1175
|
-
return guardMethod(fn, (e) => this.
|
|
1232
|
+
return guardMethod(fn, (e) => this.rtrs.values().next().value?.log(`[Reactor "${this.name}" Module] Error: ${e}`));
|
|
1176
1233
|
};
|
|
1177
1234
|
// `()=>{}`: needs to be bounded even before initialization
|
|
1178
1235
|
};
|
|
1179
1236
|
|
|
1180
1237
|
// src/ts/utils/store.ts
|
|
1181
1238
|
var BaseStorageAdapter = class {
|
|
1182
|
-
|
|
1239
|
+
name = "StorageAdapter";
|
|
1183
1240
|
config;
|
|
1184
1241
|
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
1242
|
constructor(config) {
|
|
@@ -1187,26 +1244,44 @@ var sia = (() => {
|
|
|
1187
1244
|
}
|
|
1188
1245
|
};
|
|
1189
1246
|
var StorageAdapter = class extends BaseStorageAdapter {
|
|
1247
|
+
name = "SyncStorageAdapter";
|
|
1190
1248
|
};
|
|
1191
1249
|
var AsyncStorageAdapter = class extends BaseStorageAdapter {
|
|
1250
|
+
name = "AsyncStorageAdapter";
|
|
1192
1251
|
};
|
|
1193
1252
|
var LocalStorageAdapter = class extends StorageAdapter {
|
|
1194
|
-
|
|
1195
|
-
|
|
1253
|
+
name = "LocalStorage";
|
|
1254
|
+
/**
|
|
1255
|
+
* Reads and parses a value from localStorage.
|
|
1256
|
+
* @param key Storage key.
|
|
1257
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1258
|
+
*/
|
|
1259
|
+
get(key, reviver = this.config.reviver) {
|
|
1196
1260
|
try {
|
|
1197
1261
|
const v = localStorage.getItem(key);
|
|
1198
|
-
return v ? JSON.parse(v) : void 0;
|
|
1262
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1199
1263
|
} catch {
|
|
1200
1264
|
return void 0;
|
|
1201
1265
|
}
|
|
1202
1266
|
}
|
|
1203
|
-
|
|
1267
|
+
/**
|
|
1268
|
+
* Serializes and writes a value to localStorage.
|
|
1269
|
+
* @param key Storage key.
|
|
1270
|
+
* @param value Value to serialize.
|
|
1271
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1272
|
+
*/
|
|
1273
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1204
1274
|
try {
|
|
1205
|
-
return localStorage.setItem(key, JSON.stringify(value)), true;
|
|
1275
|
+
return localStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
1206
1276
|
} catch (e) {
|
|
1207
1277
|
return this.warn("setItem", void 0, key), false;
|
|
1208
1278
|
}
|
|
1209
1279
|
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Removes a single key from localStorage.
|
|
1282
|
+
* @param key Storage key.
|
|
1283
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1284
|
+
*/
|
|
1210
1285
|
remove(key) {
|
|
1211
1286
|
try {
|
|
1212
1287
|
return localStorage.removeItem(key), true;
|
|
@@ -1214,6 +1289,10 @@ var sia = (() => {
|
|
|
1214
1289
|
return this.warn("removeItem", void 0, key), false;
|
|
1215
1290
|
}
|
|
1216
1291
|
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Clears all localStorage entries for the current origin.
|
|
1294
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1295
|
+
*/
|
|
1217
1296
|
clear() {
|
|
1218
1297
|
try {
|
|
1219
1298
|
return localStorage.clear(), true;
|
|
@@ -1222,25 +1301,94 @@ var sia = (() => {
|
|
|
1222
1301
|
}
|
|
1223
1302
|
}
|
|
1224
1303
|
};
|
|
1225
|
-
var
|
|
1304
|
+
var SessionStorageAdapter = class extends StorageAdapter {
|
|
1305
|
+
name = "SessionStorage";
|
|
1306
|
+
/**
|
|
1307
|
+
* Reads and parses a value from sessionStorage.
|
|
1308
|
+
* @param key Storage key.
|
|
1309
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1310
|
+
*/
|
|
1311
|
+
get(key, reviver = this.config.reviver) {
|
|
1312
|
+
try {
|
|
1313
|
+
const v = sessionStorage.getItem(key);
|
|
1314
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1315
|
+
} catch {
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Serializes and writes a value to sessionStorage.
|
|
1321
|
+
* @param key Storage key.
|
|
1322
|
+
* @param value Value to serialize.
|
|
1323
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1324
|
+
*/
|
|
1325
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1326
|
+
try {
|
|
1327
|
+
return sessionStorage.setItem(key, JSON.stringify(value, replacer)), true;
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
return this.warn("setItem", void 0, key), false;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Removes a single key from sessionStorage.
|
|
1334
|
+
* @param key Storage key.
|
|
1335
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1336
|
+
*/
|
|
1337
|
+
remove(key) {
|
|
1338
|
+
try {
|
|
1339
|
+
return sessionStorage.removeItem(key), true;
|
|
1340
|
+
} catch (e) {
|
|
1341
|
+
return this.warn("removeItem", void 0, key), false;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Clears all sessionStorage entries for the current tab session.
|
|
1346
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1347
|
+
*/
|
|
1348
|
+
clear() {
|
|
1349
|
+
try {
|
|
1350
|
+
return sessionStorage.clear(), true;
|
|
1351
|
+
} catch (e) {
|
|
1352
|
+
return this.warn("clear", void 0), false;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
var MemoryAdapter = class extends StorageAdapter {
|
|
1357
|
+
name = "Memory";
|
|
1226
1358
|
constructor(build) {
|
|
1227
1359
|
super({ store: /* @__PURE__ */ new Map(), ...build });
|
|
1228
1360
|
}
|
|
1229
|
-
|
|
1361
|
+
/**
|
|
1362
|
+
* Reads and parses a value from memory storage.
|
|
1363
|
+
* @param key Storage key.
|
|
1364
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1365
|
+
*/
|
|
1366
|
+
get(key, reviver = this.config.reviver) {
|
|
1230
1367
|
try {
|
|
1231
1368
|
const v = this.config.store.get(key);
|
|
1232
|
-
return v ? JSON.parse(v) : void 0;
|
|
1369
|
+
return v ? JSON.parse(v, reviver) : void 0;
|
|
1233
1370
|
} catch {
|
|
1234
1371
|
return void 0;
|
|
1235
1372
|
}
|
|
1236
1373
|
}
|
|
1237
|
-
|
|
1374
|
+
/**
|
|
1375
|
+
* Serializes and writes a value to memory storage.
|
|
1376
|
+
* @param key Storage key.
|
|
1377
|
+
* @param value Value to serialize.
|
|
1378
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1379
|
+
*/
|
|
1380
|
+
set(key, value, replacer = this.config.replacer) {
|
|
1238
1381
|
try {
|
|
1239
|
-
return this.config.store.set(key, JSON.stringify(value)), true;
|
|
1382
|
+
return this.config.store.set(key, JSON.stringify(value, replacer)), true;
|
|
1240
1383
|
} catch (e) {
|
|
1241
1384
|
return this.warn("set", void 0, key), false;
|
|
1242
1385
|
}
|
|
1243
1386
|
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Removes a single key from memory storage.
|
|
1389
|
+
* @param key Storage key.
|
|
1390
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1391
|
+
*/
|
|
1244
1392
|
remove(key) {
|
|
1245
1393
|
try {
|
|
1246
1394
|
return this.config.store.delete(key), true;
|
|
@@ -1248,6 +1396,10 @@ var sia = (() => {
|
|
|
1248
1396
|
return this.warn("remove", void 0, key), false;
|
|
1249
1397
|
}
|
|
1250
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Clears all entries from memory storage.
|
|
1401
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1402
|
+
*/
|
|
1251
1403
|
clear() {
|
|
1252
1404
|
try {
|
|
1253
1405
|
return this.config.store.clear(), true;
|
|
@@ -1256,52 +1408,147 @@ var sia = (() => {
|
|
|
1256
1408
|
}
|
|
1257
1409
|
}
|
|
1258
1410
|
};
|
|
1411
|
+
var CookieAdapter = class extends StorageAdapter {
|
|
1412
|
+
name = "Cookie";
|
|
1413
|
+
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}` : ""}`;
|
|
1414
|
+
constructor(build) {
|
|
1415
|
+
super({ secure: "undefined" !== typeof window && location.protocol === "https:", ...COOKIE_ADAPTER_BUILD, ...build });
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Reads and parses a cookie visible to the current page scope.
|
|
1419
|
+
* @param key Cookie key.
|
|
1420
|
+
* @returns Parsed value, or `undefined` when missing/unreadable.
|
|
1421
|
+
*/
|
|
1422
|
+
get(key, reviver = this.config.reviver) {
|
|
1423
|
+
try {
|
|
1424
|
+
const k = encodeURIComponent(key) + "=";
|
|
1425
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1426
|
+
if (!pair.startsWith(k)) continue;
|
|
1427
|
+
return JSON.parse(decodeURIComponent(pair.slice(k.length)), reviver);
|
|
1428
|
+
}
|
|
1429
|
+
return void 0;
|
|
1430
|
+
} catch {
|
|
1431
|
+
return void 0;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Writes a cookie with optional per-call scope/lifetime overrides.
|
|
1436
|
+
* @param key Cookie key.
|
|
1437
|
+
* @param value Value to serialize.
|
|
1438
|
+
* @param opts Optional per-call cookie options.
|
|
1439
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1440
|
+
*/
|
|
1441
|
+
set(key, value, opts, replacer = this.config.replacer) {
|
|
1442
|
+
try {
|
|
1443
|
+
return document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value, replacer))}; ${this.deets(opts)}`, true;
|
|
1444
|
+
} catch {
|
|
1445
|
+
return this.warn("set", void 0, key), false;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Removes a cookie key using matching scope attributes.
|
|
1450
|
+
* @param key Cookie key.
|
|
1451
|
+
* @param opts Optional per-call scope overrides.
|
|
1452
|
+
* @returns `true` when removal succeeds, else `false`.
|
|
1453
|
+
*/
|
|
1454
|
+
remove(key, opts) {
|
|
1455
|
+
try {
|
|
1456
|
+
return document.cookie = `${encodeURIComponent(key)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`, true;
|
|
1457
|
+
} catch {
|
|
1458
|
+
return this.warn("remove", void 0, key), false;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Attempts to remove all visible cookie keys for the given scope.
|
|
1463
|
+
* @param opts Optional per-call scope overrides.
|
|
1464
|
+
* @returns `true` when clear succeeds, else `false`.
|
|
1465
|
+
*/
|
|
1466
|
+
clear(opts) {
|
|
1467
|
+
try {
|
|
1468
|
+
for (const pair of document.cookie ? document.cookie.split("; ") : []) {
|
|
1469
|
+
const idx = pair.indexOf("=");
|
|
1470
|
+
document.cookie = `${idx === -1 ? pair : pair.slice(0, idx)}=; ${this.deets({ ...opts, maxAge: 0, expires: /* @__PURE__ */ new Date(0) })}`;
|
|
1471
|
+
}
|
|
1472
|
+
return true;
|
|
1473
|
+
} catch {
|
|
1474
|
+
return this.warn("clear"), false;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1259
1478
|
var IndexedDBAdapter = class extends AsyncStorageAdapter {
|
|
1479
|
+
name = "IndexedDB";
|
|
1260
1480
|
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
1481
|
constructor(build) {
|
|
1263
1482
|
super({ ...INDEXED_DB_ADAPTER_BUILD, ...build });
|
|
1264
1483
|
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Returns a connected IndexedDB instance, opening it when needed.
|
|
1486
|
+
* @returns Connected database handle.
|
|
1487
|
+
*/
|
|
1265
1488
|
async idb() {
|
|
1266
1489
|
const idb = this.config.onidb();
|
|
1267
1490
|
if (idb || this.db) return Promise.resolve(idb || this.db);
|
|
1268
1491
|
return new Promise((res, rej) => {
|
|
1269
1492
|
const req = indexedDB.open(this.config.dbName, this.config.version);
|
|
1270
1493
|
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"),
|
|
1494
|
+
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
1495
|
req.onerror = (e) => (this.config.onerror(req.error, e), this.warn("open", "Something went wrong"), rej(req.error));
|
|
1273
1496
|
req.onblocked = (e) => (this.config.onblocked(e), this.warn("open", "Close other tabs for updates"));
|
|
1274
1497
|
});
|
|
1275
1498
|
}
|
|
1276
|
-
|
|
1499
|
+
/**
|
|
1500
|
+
* Reads a value by key from an object store.
|
|
1501
|
+
* @param key Record key.
|
|
1502
|
+
* @param store Optional object-store override.
|
|
1503
|
+
* @returns Stored value, or `undefined` when missing/unreadable.
|
|
1504
|
+
*/
|
|
1505
|
+
async get(key, store = this.config.stores[0], options = this.config) {
|
|
1277
1506
|
try {
|
|
1278
|
-
const req = (await this.idb()).transaction(store).objectStore(store).get(key);
|
|
1507
|
+
const req = (await this.idb()).transaction(store, "readonly", options).objectStore(store).get(key);
|
|
1279
1508
|
return new Promise((res) => req.onsuccess = () => res(req.result));
|
|
1280
1509
|
} catch {
|
|
1281
1510
|
return this.warn("get", void 0, store), void 0;
|
|
1282
1511
|
}
|
|
1283
1512
|
}
|
|
1284
|
-
|
|
1513
|
+
/**
|
|
1514
|
+
* Writes a value by key into an object store.
|
|
1515
|
+
* @param key Record key.
|
|
1516
|
+
* @param value Value to store.
|
|
1517
|
+
* @param store Optional object-store override.
|
|
1518
|
+
* @returns `true` when write succeeds, else `false`.
|
|
1519
|
+
*/
|
|
1520
|
+
async set(key, value, store = this.config.stores[0], options = this.config) {
|
|
1285
1521
|
try {
|
|
1286
|
-
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).put(value, key);
|
|
1522
|
+
const req = (await this.idb()).transaction(store, "readwrite", options).objectStore(store).put(value, key);
|
|
1287
1523
|
return new Promise((res) => req.onsuccess = () => res(true));
|
|
1288
1524
|
} catch (e) {
|
|
1289
1525
|
return this.warn("put", void 0, store), false;
|
|
1290
1526
|
}
|
|
1291
1527
|
}
|
|
1292
|
-
|
|
1528
|
+
/**
|
|
1529
|
+
* Deletes a value by key from an object store.
|
|
1530
|
+
* @param key Record key.
|
|
1531
|
+
* @param store Optional object-store override.
|
|
1532
|
+
* @returns `true` when delete succeeds, else `false`.
|
|
1533
|
+
*/
|
|
1534
|
+
async remove(key, store = this.config.stores[0], options = this.config) {
|
|
1293
1535
|
try {
|
|
1294
|
-
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).delete(key);
|
|
1536
|
+
const req = (await this.idb()).transaction(store, "readwrite", options).objectStore(store).delete(key);
|
|
1295
1537
|
return new Promise((res) => req.onsuccess = () => res(true));
|
|
1296
1538
|
} catch (e) {
|
|
1297
1539
|
return this.warn("delete", void 0, store), false;
|
|
1298
1540
|
}
|
|
1299
1541
|
}
|
|
1300
|
-
|
|
1542
|
+
/**
|
|
1543
|
+
* Clears one or more object stores.
|
|
1544
|
+
* @param stores Store name or list of store names to clear.
|
|
1545
|
+
* @returns `true` when all clears succeed, else `false`.
|
|
1546
|
+
*/
|
|
1547
|
+
async clear(stores = this.config.stores, options = this.config) {
|
|
1301
1548
|
let success = true;
|
|
1302
1549
|
for (const store of Array.isArray(stores) ? stores : [stores])
|
|
1303
1550
|
try {
|
|
1304
|
-
const req = (await this.idb()).transaction(store, "readwrite").objectStore(store).clear();
|
|
1551
|
+
const req = (await this.idb()).transaction(store, "readwrite", options).objectStore(store).clear();
|
|
1305
1552
|
await new Promise((res) => req.onsuccess = () => res(true));
|
|
1306
1553
|
} catch (e) {
|
|
1307
1554
|
this.warn("clear", void 0, store), success = false;
|
|
@@ -1309,90 +1556,120 @@ var sia = (() => {
|
|
|
1309
1556
|
return success;
|
|
1310
1557
|
}
|
|
1311
1558
|
};
|
|
1559
|
+
var COOKIE_ADAPTER_BUILD = { path: "/", sameSite: "Lax", domain: void 0, debug: false };
|
|
1312
1560
|
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
1561
|
|
|
1314
|
-
// src/ts/
|
|
1315
|
-
var
|
|
1316
|
-
static
|
|
1562
|
+
// src/ts/modules/persist.ts
|
|
1563
|
+
var PersistModule = class extends BaseReactorModule {
|
|
1564
|
+
static moduleName = "persist";
|
|
1317
1565
|
adapter;
|
|
1566
|
+
hydrateSeq = 0;
|
|
1318
1567
|
saveTimeoutId = 0;
|
|
1319
1568
|
get payload() {
|
|
1320
|
-
|
|
1321
|
-
|
|
1569
|
+
let res = this.rtrs.size > 1 ? {} : void 0;
|
|
1570
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1571
|
+
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;
|
|
1572
|
+
this.rtrs.size > 1 ? setAny(res, rid, val) : res = val;
|
|
1573
|
+
}
|
|
1574
|
+
return res;
|
|
1322
1575
|
}
|
|
1323
1576
|
constructor(config, rtr) {
|
|
1324
|
-
super(
|
|
1577
|
+
super(mergeObjs(PERSIST_MODULE_BUILD, config), rtr, { hydrated: false });
|
|
1325
1578
|
}
|
|
1326
1579
|
wire() {
|
|
1327
1580
|
"undefined" !== typeof window && window.addEventListener("pagehide", this.onDestroy, { signal: this.signal });
|
|
1328
1581
|
"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.
|
|
1582
|
+
this.config.on("adapter", this.handleAdapter, { signal: this.signal, immediate: true });
|
|
1583
|
+
this.config.on("disabled", this.handleDisabled, { signal: this.signal, immediate: true });
|
|
1584
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1585
|
+
}
|
|
1586
|
+
onAttach(rtr) {
|
|
1587
|
+
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
1588
|
}
|
|
1333
|
-
async
|
|
1589
|
+
async handleAdapter({ value = LocalStorageAdapter }) {
|
|
1590
|
+
const seq = ++this.hydrateSeq;
|
|
1334
1591
|
if (this.adapter && value === this.adapter.constructor) return;
|
|
1592
|
+
this.state.hydrated = false;
|
|
1335
1593
|
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
|
-
|
|
1594
|
+
this.adapter = "function" === typeof value ? new value({ debug: !!this.rtrs.values().next().value?.canLog }) : value;
|
|
1595
|
+
try {
|
|
1596
|
+
let saved = this.adapter.get(this.config.key);
|
|
1597
|
+
const isAsync = saved instanceof Promise, { depth, merge = true } = parseEvtOpts(this.config.fanout ?? isAsync, fanoutOptsArr, "depth");
|
|
1598
|
+
saved = !isAsync ? saved : await saved;
|
|
1599
|
+
if (seq !== this.hydrateSeq || !saved) return;
|
|
1600
|
+
for (const [rid, rtr] of this.rtrs) {
|
|
1601
|
+
const entry = this.rtrs.size > 1 ? getAny(saved, rid) : saved;
|
|
1602
|
+
if (!entry) continue;
|
|
1603
|
+
const set = (p, news, olds) => (depth ? fanout : setAny)(rtr.core, p, merge ? mergeObjs(news, olds) : olds, depth ? { depth, crossRealms: rtr.config.crossRealms } : void 0);
|
|
1604
|
+
for (const p of this.config.paths ?? wpArr) set(p, getAny(rtr.core, p), getAny(entry, p));
|
|
1605
|
+
}
|
|
1606
|
+
for (const rtr of this.rtrs.values()) rtr.tick(depth ? "*" : this.config.paths ?? "*");
|
|
1607
|
+
} finally {
|
|
1608
|
+
if (seq === this.hydrateSeq) this.state.hydrated = true;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
handleDisabled({ value }) {
|
|
1612
|
+
for (const rtr of this.rtrs.values()) this.onAttach(rtr);
|
|
1346
1613
|
value && this.adapter?.remove(this.config.key);
|
|
1347
1614
|
}
|
|
1348
|
-
|
|
1349
|
-
for (const
|
|
1350
|
-
|
|
1615
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1616
|
+
for (const rtr of this.rtrs.values()) {
|
|
1617
|
+
for (const p of prevs) rtr.off(p, this.handleSave);
|
|
1618
|
+
for (const p of paths) rtr.off(p, this.handleSave), !this.config.disabled && rtr.on(p, this.handleSave, { signal: this.signal, immediate: true });
|
|
1619
|
+
}
|
|
1351
1620
|
}
|
|
1352
|
-
|
|
1621
|
+
handleSave(e) {
|
|
1622
|
+
if (!this.state.hydrated) return e.stopImmediatePropagation();
|
|
1353
1623
|
if (!this.saveTimeoutId) this.saveTimeoutId = setTimeout2(() => (this.adapter.set(this.config.key, this.payload), this.saveTimeoutId = 0), this.config.throttle, this.signal);
|
|
1354
1624
|
}
|
|
1355
|
-
/** Clears persisted payload for this
|
|
1625
|
+
/** Clears persisted payload for this module instance and drops any pending save. */
|
|
1356
1626
|
clear() {
|
|
1357
1627
|
clearTimeout(this.saveTimeoutId);
|
|
1358
|
-
this.saveTimeoutId = -1
|
|
1628
|
+
this.saveTimeoutId = -1;
|
|
1629
|
+
for (const rtr of this.rtrs.values()) rtr.stall(() => this.saveTimeoutId = 0);
|
|
1359
1630
|
this.adapter?.remove(this.config.key);
|
|
1360
1631
|
}
|
|
1361
1632
|
onDestroy() {
|
|
1362
|
-
!this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1633
|
+
this.state.hydrated && !this.config.disabled && this.adapter?.set(this.config.key, this.payload);
|
|
1363
1634
|
}
|
|
1364
1635
|
};
|
|
1365
|
-
var
|
|
1636
|
+
var PERSIST_MODULE_BUILD = { disabled: false, key: "REACTOR_STORE", throttle: 2500, useSnapshot: false };
|
|
1366
1637
|
|
|
1367
|
-
// src/ts/
|
|
1368
|
-
var
|
|
1369
|
-
static
|
|
1638
|
+
// src/ts/modules/timeTravel.ts
|
|
1639
|
+
var TimeTravelModule = class extends BaseReactorModule {
|
|
1640
|
+
static moduleName = "timeTravel";
|
|
1370
1641
|
lastTimestamp = 0;
|
|
1371
1642
|
playbackTimeoutId = -1;
|
|
1372
1643
|
constructor(config, rtr) {
|
|
1373
|
-
super({ ...
|
|
1644
|
+
super({ ...TIME_TRAVEL_MODULE_BUILD, ...config }, rtr, { initialState: {}, history: [], currentFrame: 0, paused: true });
|
|
1374
1645
|
}
|
|
1375
1646
|
// ===========================================================================
|
|
1376
1647
|
// THE FOUNDATION & WIRETAP (Passive Recording)
|
|
1377
1648
|
// ===========================================================================
|
|
1378
1649
|
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
1650
|
this.lastTimestamp = performance.now();
|
|
1382
1651
|
this.state.set("currentFrame", (v = 0) => clamp(0, v, this.state.history.length), { signal: this.signal, immediate: true });
|
|
1383
|
-
this.config.on("paths", this.
|
|
1652
|
+
this.config.on("paths", this.handlePaths, { signal: this.signal, immediate: true });
|
|
1384
1653
|
!this.state.paused && this.play();
|
|
1385
1654
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1655
|
+
onAttach(rtr, rid) {
|
|
1656
|
+
rtr.config.referenceTracking = rtr.config.smartCloning = rtr.config.eventTimeStamps = true;
|
|
1657
|
+
if (!this.state.history.length || !this.state.initialState[rid]) this.state.initialState[rid] = rtr.snapshot();
|
|
1658
|
+
for (const p of this.config.paths ?? wpArr) rtr.on(p, this.record, { signal: this.signal });
|
|
1659
|
+
}
|
|
1660
|
+
handlePaths({ value: paths = wpArr, oldValue: prevs = wpArr }) {
|
|
1661
|
+
for (const rtr of this.rtrs.values()) {
|
|
1662
|
+
for (const p of prevs) rtr.off(p, this.record);
|
|
1663
|
+
for (const p of paths) rtr.off(p, this.record), rtr.on(p, this.record, { signal: this.signal });
|
|
1664
|
+
}
|
|
1389
1665
|
}
|
|
1390
1666
|
/** Chronicling the lifecycle of the system, Captures the essence of every mutation wave that bubbles up. */
|
|
1391
|
-
record(e) {
|
|
1667
|
+
record(e, rid = this.rids.get(e.reactor)) {
|
|
1392
1668
|
if (!this.state.paused) return;
|
|
1393
|
-
if (this.state.currentFrame < this.state.history.length) this.state
|
|
1394
|
-
if (this.state.history.length >= this.config.maxHistoryLength) this.state
|
|
1395
|
-
|
|
1669
|
+
if (this.state.currentFrame < this.state.history.length) fanout(this.state, "history", this.state.history.slice(0, this.state.currentFrame), { atomic: true });
|
|
1670
|
+
if (this.state.history.length >= this.config.maxHistoryLength) fanout(this.state, "history", this.state.history.slice(1), { atomic: true });
|
|
1671
|
+
const en = { path: e.target.path, value: e.reactor.snapshot(false, e.target.value), oldValue: e.reactor.snapshot(false, e.target.oldValue), type: e.staticType, deltat: e.timestamp - this.lastTimestamp, rid };
|
|
1672
|
+
e.rejected && (en.rejected = e.rejected), !e.target.hadKey && (en.hadKey = false), this.state.history.push(en);
|
|
1396
1673
|
this.state.currentFrame = this.state.history.length;
|
|
1397
1674
|
this.lastTimestamp = e.timestamp;
|
|
1398
1675
|
}
|
|
@@ -1401,7 +1678,7 @@ var sia = (() => {
|
|
|
1401
1678
|
this.pause();
|
|
1402
1679
|
this.playbackTimeoutId = -1;
|
|
1403
1680
|
this.state.history.length = this.state.currentFrame = 0;
|
|
1404
|
-
this.state.initialState = this.rtr.snapshot();
|
|
1681
|
+
this.state.initialState = Object.fromEntries(this.rtrs.entries().map(([rid, rtr]) => [rid, rtr.snapshot()]));
|
|
1405
1682
|
this.lastTimestamp = performance.now();
|
|
1406
1683
|
}
|
|
1407
1684
|
// ===========================================================================
|
|
@@ -1414,12 +1691,13 @@ var sia = (() => {
|
|
|
1414
1691
|
while (this.state.currentFrame !== target) {
|
|
1415
1692
|
const e = this.state.history[forward ? this.state.currentFrame : this.state.currentFrame - 1];
|
|
1416
1693
|
if (!e) break;
|
|
1417
|
-
|
|
1418
|
-
|
|
1694
|
+
const rtr = this.rtrs.get(e.rid) || this.rtrs.values().next().value;
|
|
1695
|
+
if (forward) e.type === "delete" ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.value, rtr.config));
|
|
1696
|
+
else e.hadKey === false ? deleteAny(rtr.core, e.path) : setAny(rtr.core, e.path, deepClone(e.oldValue, rtr.config));
|
|
1419
1697
|
forward ? this.state.currentFrame++ : this.state.currentFrame--;
|
|
1420
|
-
if (e.rejected)
|
|
1698
|
+
if (e.rejected) rtr.log(`[Reactor ${this.name} Module] ${forward ? "Replaying" : "Reversing"} REJECTED intent at "${e.path}"`);
|
|
1421
1699
|
}
|
|
1422
|
-
this.rtr.tick();
|
|
1700
|
+
for (const rtr of this.rtrs.values()) rtr.tick();
|
|
1423
1701
|
if (!keepShield) this.state.paused = true;
|
|
1424
1702
|
}
|
|
1425
1703
|
/** Step through time, Moves the playhead and teleports the state. */
|
|
@@ -1438,9 +1716,9 @@ var sia = (() => {
|
|
|
1438
1716
|
async automove(forward = true) {
|
|
1439
1717
|
this.state.paused = false;
|
|
1440
1718
|
while ((forward ? this.state.currentFrame < this.state.history.length : this.state.currentFrame > 0) && !this.state.paused) {
|
|
1441
|
-
const
|
|
1719
|
+
const idx = forward ? this.state.currentFrame : this.state.currentFrame - 1, e = this.state.history[forward ? idx + 1 : idx - 1];
|
|
1442
1720
|
this.jumpTo(this.state.currentFrame + (forward ? 1 : -1), true);
|
|
1443
|
-
if (e?.
|
|
1721
|
+
if (e?.deltat > 0) await new Promise((res) => this.playbackTimeoutId = setTimeout2(() => res(0), Math.min(e.deltat, this.config.maxPlaybackDelay), this.signal));
|
|
1444
1722
|
}
|
|
1445
1723
|
this.state.paused = true;
|
|
1446
1724
|
}
|
|
@@ -1455,27 +1733,19 @@ var sia = (() => {
|
|
|
1455
1733
|
// ===========================================================================
|
|
1456
1734
|
/** Exports the current session as a JSON string. */
|
|
1457
1735
|
export(replacer, space) {
|
|
1458
|
-
|
|
1459
|
-
return JSON.stringify(this.state, replacer, space);
|
|
1460
|
-
} catch (e) {
|
|
1461
|
-
return this.rtr.log(`[Reactor ${this.name} Plug] Failed to export session`), "";
|
|
1462
|
-
}
|
|
1736
|
+
return JSON.stringify(this.state, replacer, space);
|
|
1463
1737
|
}
|
|
1464
1738
|
/** Imports a session from a JSON string, allowing you to replay or analyze past states. */
|
|
1465
|
-
import(json) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
1473
|
-
} catch (e) {
|
|
1474
|
-
this.rtr.log(`[Reactor ${this.name} Plug] Failed to load session`);
|
|
1475
|
-
}
|
|
1739
|
+
import(json, reviver) {
|
|
1740
|
+
setAny(this.state, "*", JSON.parse(json, reviver));
|
|
1741
|
+
this.lastTimestamp = performance.now();
|
|
1742
|
+
const resume = !this.state.paused, target = this.state.currentFrame;
|
|
1743
|
+
this.state.paused = false;
|
|
1744
|
+
for (const [rid, rtr] of this.rtrs) setAny(rtr.core, "*", deepClone(this.state.initialState[rid], rtr.config)), rtr.tick();
|
|
1745
|
+
this.state.currentFrame = 0, this.jumpTo(target), resume && this.play();
|
|
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;
|