sia-reactor 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,812 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Reactor: () => Reactor,
24
+ ReactorEvent: () => ReactorEvent,
25
+ getSnapshotVersion: () => getSnapshotVersion,
26
+ getVersion: () => getVersion,
27
+ inert: () => inert,
28
+ intent: () => intent,
29
+ isInert: () => isInert,
30
+ isIntent: () => isIntent,
31
+ isVolatile: () => isVolatile,
32
+ live: () => live,
33
+ methods: () => methods,
34
+ reactive: () => reactive,
35
+ stable: () => stable,
36
+ state: () => state,
37
+ volatile: () => volatile
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+
41
+ // src/utils/obj.ts
42
+ var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
43
+ function isArr(obj) {
44
+ return Array.isArray(obj);
45
+ }
46
+ function isObj(obj, checkArr = true) {
47
+ return "object" === typeof obj && obj !== null && (checkArr ? !Array.isArray(obj) : true);
48
+ }
49
+ function isStrictObj(obj, crossRealms = false, typecheck = true) {
50
+ return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
51
+ }
52
+ function setAny(target, key, value, separator = ".", keyFunc) {
53
+ var _a, _b;
54
+ if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
55
+ const keys = key.split(separator);
56
+ let currObj = target;
57
+ for (let i = 0; i < keys.length; i++) {
58
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
59
+ if (match) {
60
+ const [, key3, iStr] = match;
61
+ if (!isArr(currObj[key3])) currObj[key3] = [];
62
+ if (i === keys.length - 1) currObj[key3][Number(iStr)] = value;
63
+ else (_a = currObj[key3])[_b = Number(iStr)] || (_a[_b] = {}), currObj = currObj[key3][Number(iStr)];
64
+ } else {
65
+ if (i === keys.length - 1) currObj[key2] = value;
66
+ else currObj[key2] || (currObj[key2] = {}), currObj = currObj[key2];
67
+ }
68
+ }
69
+ }
70
+ function getAny(source, key, separator = ".", keyFunc) {
71
+ if (!key.includes(separator)) return source[keyFunc ? keyFunc(key) : key];
72
+ const keys = key.split(separator);
73
+ let currObj = source;
74
+ for (let i = 0; i < keys.length; i++) {
75
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
76
+ if (match) {
77
+ const [, key3, iStr] = match;
78
+ if (!isArr(currObj[key3]) || !(key3 in currObj)) return void 0;
79
+ currObj = currObj[key3][Number(iStr)];
80
+ } else {
81
+ if (!isObj(currObj) || !(key2 in currObj)) return void 0;
82
+ currObj = currObj[key2];
83
+ }
84
+ }
85
+ return currObj;
86
+ }
87
+ function deleteAny(target, key, separator = ".", keyFunc) {
88
+ if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
89
+ const keys = key.split(separator);
90
+ let currObj = target;
91
+ for (let i = 0; i < keys.length; i++) {
92
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
93
+ if (match) {
94
+ const [, key3, iStr] = match;
95
+ if (!isArr(currObj[key3]) || !(key3 in currObj)) return;
96
+ if (i === keys.length - 1) delete currObj[key3][Number(iStr)];
97
+ else currObj = currObj[key3][Number(iStr)];
98
+ } else {
99
+ if (!isObj(currObj) || !(key2 in currObj)) return;
100
+ if (i === keys.length - 1) delete currObj[key2];
101
+ else currObj = currObj[key2];
102
+ }
103
+ }
104
+ }
105
+ function inAny(source, key, separator = ".", keyFunc) {
106
+ if (!key.includes(separator)) return key in source;
107
+ const keys = key.split(separator);
108
+ let currObj = source;
109
+ for (let i = 0; i < keys.length; i++) {
110
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
111
+ if (match) {
112
+ const [, key3, iStr] = match;
113
+ if (!isArr(currObj[key3]) || !(key3 in currObj)) return false;
114
+ if (i === keys.length - 1) return true;
115
+ currObj = currObj[key3][Number(iStr)];
116
+ } else {
117
+ if (!isObj(currObj) || !(key2 in currObj)) return false;
118
+ if (i === keys.length - 1) return true;
119
+ currObj = currObj[key2];
120
+ }
121
+ }
122
+ return true;
123
+ }
124
+ function parseEvOpts(options, opts, boolOpt = opts[0], result = {}) {
125
+ return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
126
+ }
127
+ function mergeObjs(o1 = {}, o2 = {}) {
128
+ const merged = { ...o1 || {}, ...o2 || {} };
129
+ return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
130
+ }
131
+ function getTrailPaths(path, reverse = true) {
132
+ const parts = path.split("."), chain = ["*"];
133
+ let acc = "";
134
+ for (let i = 0; i < parts.length; i++) {
135
+ acc += (i === 0 ? "" : ".") + parts[i];
136
+ chain.push(acc);
137
+ }
138
+ return reverse ? chain.reverse() : chain;
139
+ }
140
+ function getTrailRecords(obj, path) {
141
+ const parts = path.split("."), record = [["*", obj, obj]];
142
+ let acc = "", currObj = obj;
143
+ for (let i = 0; i < parts.length; i++) {
144
+ acc += (i === 0 ? "" : ".") + parts[i];
145
+ record.push([acc, currObj, currObj = currObj?.[parts[i]]]);
146
+ }
147
+ return record;
148
+ }
149
+ function nuke(target) {
150
+ let proto = target;
151
+ while (proto && proto !== Object.prototype) {
152
+ for (const key of Object.getOwnPropertyNames(proto)) {
153
+ if (key === "constructor") continue;
154
+ const desc = Object.getOwnPropertyDescriptor(proto, key);
155
+ if ("function" === typeof desc?.value) continue;
156
+ if (desc?.get || desc?.set) continue;
157
+ proto[key] = null;
158
+ }
159
+ proto = Object.getPrototypeOf(proto);
160
+ }
161
+ }
162
+
163
+ // src/core/reactor.ts
164
+ var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
165
+ var INERTIA = /* @__PURE__ */ Symbol.for("S.I.A_INERTIA");
166
+ var REJECTABLE = /* @__PURE__ */ Symbol.for("S.I.A_REJECTABLE");
167
+ var INDIFFABLE = /* @__PURE__ */ Symbol.for("S.I.A_INDIFFABLE");
168
+ var TERMINATOR = /* @__PURE__ */ Symbol.for("S.I.A_TERMINATOR");
169
+ var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
170
+ var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
171
+ var NOOP = () => {
172
+ };
173
+ var R_BATCH = ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window);
174
+ var R_LOG = console.log.bind(console, "[S.I.A Reactor]");
175
+ var EV_WARN = console.warn.bind(console, "[S.I.A Event]");
176
+ var EV_OPTS = {
177
+ LISTENER: ["capture", "depth", "once", "signal", "immediate"],
178
+ MEDIATOR: ["lazy", "signal", "immediate"]
179
+ };
180
+ var _ReactorEvent = class _ReactorEvent {
181
+ constructor(payload, bubbles = false, canWarn = true) {
182
+ this.eventPhase = _ReactorEvent.NONE;
183
+ this._propagationStopped = false;
184
+ this._immediatePropagationStopped = false;
185
+ this._resolved = "";
186
+ this._rejected = "";
187
+ this._warn = NOOP;
188
+ this.type = this.staticType = payload.type;
189
+ this.target = payload.target;
190
+ this.currentTarget = payload.currentTarget;
191
+ this.root = payload.root;
192
+ this.value = payload.target.value;
193
+ this.oldValue = payload.target.oldValue;
194
+ this.path = payload.target.path;
195
+ this.rejectable = payload.rejectable;
196
+ this.bubbles = bubbles;
197
+ if (canWarn) this._warn = EV_WARN;
198
+ }
199
+ get propagationStopped() {
200
+ return this._propagationStopped;
201
+ }
202
+ stopPropagation() {
203
+ this._propagationStopped = true;
204
+ }
205
+ get immediatePropagationStopped() {
206
+ return this._immediatePropagationStopped;
207
+ }
208
+ stopImmediatePropagation() {
209
+ this._propagationStopped = true;
210
+ this._immediatePropagationStopped = true;
211
+ }
212
+ get resolved() {
213
+ return this._resolved;
214
+ }
215
+ resolve(message) {
216
+ if (!this.rejectable) return this._warn(`Ignored resolve() call on a non-rejectable ${this.staticType} at "${this.path}"`);
217
+ if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`Resolving an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
218
+ if (this.rejectable) this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`;
219
+ }
220
+ get rejected() {
221
+ return this._rejected;
222
+ }
223
+ reject(reason) {
224
+ if (!this.rejectable) return this._warn(`Ignored reject() call on a non-rejectable ${this.staticType} at "${this.path}"`);
225
+ if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`Rejecting an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
226
+ if (this.rejectable) this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`;
227
+ }
228
+ composedPath() {
229
+ return getTrailPaths(this.path);
230
+ }
231
+ get canWarn() {
232
+ return this._warn === EV_WARN;
233
+ }
234
+ };
235
+ _ReactorEvent.NONE = 0;
236
+ _ReactorEvent.CAPTURING_PHASE = 1;
237
+ _ReactorEvent.AT_TARGET = 2;
238
+ _ReactorEvent.BUBBLING_PHASE = 3;
239
+ var ReactorEvent = _ReactorEvent;
240
+ var Reactor = class {
241
+ constructor(obj = {}, options) {
242
+ this.proxyCache = /* @__PURE__ */ new WeakMap();
243
+ this.log = NOOP;
244
+ // `?:`s | pay the ~800 byte price upfront for what u might never use
245
+ this.isLogging = false;
246
+ // keeping track so API getter doesn't slow down internal iterations in any way
247
+ this.isTracing = false;
248
+ this.isTracking = false;
249
+ this.isSCloning = false;
250
+ // Smart Cloning
251
+ this.isBatching = false;
252
+ this[INERTIA] = true;
253
+ this.config = {
254
+ crossRealms: false,
255
+ eventBubbling: true,
256
+ batchingFunction: R_BATCH,
257
+ ...options
258
+ };
259
+ this.core = this.proxied(obj);
260
+ if (!options) return;
261
+ this.canLog = !!options.debug;
262
+ if (this.isTracking = !!options.referenceTracking) this.lineage = /* @__PURE__ */ new WeakMap();
263
+ if (this.isSCloning = this.isTracking && !!options.smartCloning) this.snapshotCache = /* @__PURE__ */ new WeakMap();
264
+ this.isTracing = this.isTracking && !!options.lineageTracing;
265
+ }
266
+ proxied(obj, rejectable = false, indiffable = false, parent, key, path) {
267
+ if (!obj || typeof obj !== "object") return obj;
268
+ if (!(isStrictObj(obj, this.config.crossRealms, false) || Array.isArray(obj)) || obj[INERTIA]) return obj;
269
+ obj = obj[RAW] || obj;
270
+ if (this.isTracking && parent && key) this.link(obj, parent, key, false);
271
+ if (this.proxyCache.has(obj)) return this.proxyCache.get(obj);
272
+ rejectable || (rejectable = obj[REJECTABLE]);
273
+ indiffable || (indiffable = obj[INDIFFABLE]);
274
+ const proxy = new Proxy(obj, {
275
+ // Robust Proxy handler
276
+ get: (object, key2, receiver) => {
277
+ if (key2 === RAW) return object;
278
+ let value = object[key2];
279
+ const safeKey = String(key2), fullPath = this.isTracing ? void 0 : path ? path + "." + safeKey : safeKey, paths = this.isTracing ? this.trace(object, safeKey) : fullPath;
280
+ this.log(`\u{1F440} [GET Trap] Initiated for "${safeKey}" on "${paths}"`);
281
+ if (this.config.get) value = this.config.get(object, key2, value, receiver, paths);
282
+ if (this.getters) {
283
+ const wildcords = this.getters.get("*");
284
+ for (let i = 0, len = this.isTracing ? paths.length : 1; i < len; i++) {
285
+ const currPath = this.isTracing ? paths[i] : fullPath, cords = this.getters.get(currPath);
286
+ if (!cords && !wildcords) continue;
287
+ const target = {
288
+ path: currPath,
289
+ value,
290
+ key: safeKey,
291
+ object: receiver
292
+ }, payload = {
293
+ type: "get",
294
+ target,
295
+ currentTarget: target,
296
+ root: this.core,
297
+ rejectable
298
+ };
299
+ if (cords) value = this.mediate(currPath, payload, "get", cords);
300
+ if (!wildcords) continue;
301
+ target.value = value;
302
+ value = this.mediate("*", payload, "get", wildcords);
303
+ }
304
+ }
305
+ return this.proxied(value, rejectable, indiffable, object, safeKey, fullPath);
306
+ },
307
+ set: (object, key2, value, receiver) => {
308
+ let unchanged, safeValue, safeOldValue, terminated = false;
309
+ const safeKey = String(key2), fullPath = this.isTracing ? void 0 : path ? path + "." + safeKey : safeKey, paths = this.isTracing ? this.trace(object, safeKey) : fullPath, loopLen = this.isTracing ? paths.length : 1, oldValue = object[key2];
310
+ if (this.isTracking || !indiffable) {
311
+ safeOldValue = oldValue?.[RAW] || oldValue;
312
+ safeValue = value?.[RAW] || value;
313
+ unchanged = Object.is(safeValue, safeOldValue);
314
+ }
315
+ if (!indiffable && unchanged) return true;
316
+ this.log(`\u270F\uFE0F [SET Trap] Initiated for "${safeKey}" on "${paths}"`);
317
+ if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
318
+ if (this.setters) {
319
+ const wildcords = this.setters.get("*");
320
+ for (let i = 0; i < loopLen; i++) {
321
+ const currPath = this.isTracking ? paths[i] : fullPath, cords = this.setters.get(currPath);
322
+ if (!cords && !wildcords) continue;
323
+ const target = {
324
+ path: currPath,
325
+ value,
326
+ oldValue,
327
+ key: safeKey,
328
+ object: receiver
329
+ }, payload = {
330
+ type: "set",
331
+ target,
332
+ currentTarget: target,
333
+ root: this.core,
334
+ terminated,
335
+ rejectable
336
+ };
337
+ if (cords) {
338
+ const result2 = this.mediate(currPath, payload, "set", cords);
339
+ if (!(terminated || (terminated = payload.terminated))) value = result2;
340
+ }
341
+ if (!wildcords) continue;
342
+ target.value = value;
343
+ const result = this.mediate("*", payload, "set", wildcords);
344
+ if (!(terminated || (terminated = payload.terminated))) value = result;
345
+ }
346
+ }
347
+ if (terminated) return this.log(`\u{1F6E1}\uFE0F [SET Mediator] Terminated on "${paths}"`), true;
348
+ object[key2] = value;
349
+ if (this.isTracking && !unchanged) this.isSCloning && this.stamp(object), this.unlink(safeOldValue, object, safeKey), this.link(safeValue, object, safeKey);
350
+ if (this.watchers || this.listeners)
351
+ for (let i = 0; i < loopLen; i++) {
352
+ const currPath = this.isTracking ? paths[i] : fullPath, target = {
353
+ path: currPath,
354
+ value,
355
+ oldValue,
356
+ key: safeKey,
357
+ object: receiver
358
+ };
359
+ this.notify(currPath, {
360
+ type: "set",
361
+ target,
362
+ currentTarget: target,
363
+ root: this.core,
364
+ terminated,
365
+ rejectable
366
+ });
367
+ }
368
+ return true;
369
+ },
370
+ deleteProperty: (object, key2) => {
371
+ let value, receiver = this.proxyCache.get(object), terminated = false;
372
+ const safeKey = String(key2), fullPath = this.isTracing ? void 0 : path ? path + "." + safeKey : safeKey, paths = this.isTracing ? this.trace(object, safeKey) : fullPath, loopLen = this.isTracing ? paths.length : 1, oldValue = object[key2];
373
+ this.log(`\u{1F5D1}\uFE0F [DELETE Trap] Initiated for "${safeKey}" on "${paths}"`);
374
+ if (this.config.delete) terminated = (value = this.config.delete(object, key2, oldValue, receiver, paths)) === TERMINATOR;
375
+ if (this.deleters) {
376
+ const wildcords = this.deleters.get("*");
377
+ for (let i = 0; i < loopLen; i++) {
378
+ const currPath = this.isTracking ? paths[i] : fullPath, cords = this.deleters.get(currPath);
379
+ if (!cords && !wildcords) continue;
380
+ const target = {
381
+ path: currPath,
382
+ value,
383
+ oldValue,
384
+ key: safeKey,
385
+ object: receiver
386
+ }, payload = {
387
+ type: "delete",
388
+ target,
389
+ currentTarget: target,
390
+ root: this.core,
391
+ rejectable
392
+ };
393
+ if (cords) {
394
+ const result2 = this.mediate(currPath, payload, "delete", cords);
395
+ if (!(terminated || (terminated = payload.terminated))) value = result2;
396
+ }
397
+ if (!wildcords) continue;
398
+ const result = this.mediate("*", payload, "delete", wildcords);
399
+ if (!(terminated || (terminated = payload.terminated))) value = result;
400
+ }
401
+ }
402
+ if (terminated) return this.log(`\u{1F6E1}\uFE0F [DELETE Mediator] Terminated on "${paths}"`), true;
403
+ delete object[key2];
404
+ if (this.isTracking) this.isSCloning && this.stamp(object), this.unlink(oldValue?.[RAW] || oldValue, object, safeKey);
405
+ if (this.watchers || this.listeners)
406
+ for (let i = 0; i < loopLen; i++) {
407
+ const currPath = this.isTracking ? paths[i] : fullPath, target = {
408
+ path: currPath,
409
+ value,
410
+ oldValue,
411
+ key: safeKey,
412
+ object: receiver
413
+ };
414
+ this.notify(currPath, {
415
+ type: "delete",
416
+ target,
417
+ currentTarget: target,
418
+ root: this.core,
419
+ rejectable
420
+ });
421
+ }
422
+ return true;
423
+ }
424
+ });
425
+ return this.proxyCache.set(obj, proxy), proxy;
426
+ }
427
+ trace(target, path, paths = [], visited = /* @__PURE__ */ new WeakSet()) {
428
+ if (Object.is(target, this.core[RAW] || this.core)) return paths.push(path), paths;
429
+ if (visited.has(target)) return paths;
430
+ visited.add(target);
431
+ const es = this.lineage.get(target);
432
+ if (!es) return paths;
433
+ for (let i = 0, len = es.length; i < len; i += 2) this.trace(es[i], es[i + 1] ? es[i + 1] + "." + path : path, paths, visited);
434
+ return paths;
435
+ }
436
+ // won't be called without `.isTracking` so internal guard avoided
437
+ link(target, parent, key, typecheck = true, es) {
438
+ if (typecheck && !(isStrictObj(target, this.config.crossRealms) || Array.isArray(target))) return;
439
+ es = this.lineage.get(target) ?? (this.lineage.set(target, es = []), es);
440
+ for (let i = 0, len = es.length; i < len; i += 2) if (Object.is(es[i], parent) && es[i + 1] === key) return;
441
+ es.push(parent, key);
442
+ }
443
+ unlink(target, parent, key) {
444
+ if (!(isStrictObj(target, this.config.crossRealms) || Array.isArray(target))) return;
445
+ const es = this.lineage.get(target);
446
+ if (es) {
447
+ for (let i = 0, len = es.length; i < len; i += 2) if (Object.is(es[i], parent) && es[i + 1] === key) return void es.splice(i, 2);
448
+ }
449
+ }
450
+ stamp(target, typecheck = true) {
451
+ if (typecheck && "object" !== typeof target) return;
452
+ target[VERSION] = (target[VERSION] || 0) + 1;
453
+ const es = this.lineage?.get(target);
454
+ if (es) for (let i = 0, len = es.length; i < len; i += 2) this.stamp(es[i]);
455
+ }
456
+ mediate(path, payload, type, cords) {
457
+ let terminated = false, value = payload.target.value;
458
+ const isGet = type === "get", isSet = type === "set", mediators = isGet ? this.getters : isSet ? this.setters : this.deleters;
459
+ for (let i = !isGet ? 0 : cords.length - 1, len = !isGet ? cords.length : -1; i !== len; i += !isGet ? 1 : -1) {
460
+ const response = isGet ? cords[i].cb(value, payload) : isSet ? cords[i].cb(value, terminated, payload) : cords[i].cb(terminated, payload);
461
+ if (isGet || !(terminated || (terminated = payload.terminated = response === TERMINATOR))) value = response;
462
+ if (cords[i].once) cords.splice(i--, 1), !cords.length && mediators.delete(path);
463
+ }
464
+ return value;
465
+ }
466
+ notify(path, payload) {
467
+ if (this.watchers) {
468
+ const wildcords = this.watchers.get("*"), cords = this.watchers.get(path);
469
+ if (cords)
470
+ for (let i = 0, len = cords.length; i < len; i++) {
471
+ cords[i].cb(payload.target.value, payload);
472
+ if (cords[i].once) cords.splice(i--, 1), !cords.length && this.watchers.delete(path);
473
+ }
474
+ if (wildcords)
475
+ for (let i = 0, len = wildcords.length; i < len; i++) {
476
+ wildcords[i].cb(payload.target.value, payload);
477
+ if (wildcords[i].once) wildcords.splice(i--, 1), !wildcords.length && this.watchers.delete("*");
478
+ }
479
+ }
480
+ this.listeners && this.schedule(path, payload);
481
+ }
482
+ schedule(path, payload) {
483
+ this.batch ?? (this.batch = /* @__PURE__ */ new Map());
484
+ this.batch.set(path, payload), !this.isBatching && this.initBatching();
485
+ }
486
+ initBatching() {
487
+ this.isBatching = true, this.config.batchingFunction(() => this.flush());
488
+ }
489
+ flush() {
490
+ this.isBatching = false, this.batch && this.tick(this.batch.keys());
491
+ if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
492
+ }
493
+ wave(path, payload) {
494
+ const e = new ReactorEvent(payload, this.config.eventBubbling, this.isLogging), chain = getTrailRecords(this.core, path);
495
+ e.eventPhase = ReactorEvent.CAPTURING_PHASE;
496
+ for (let i = 0; i <= chain.length - 2; i++) {
497
+ if (e.propagationStopped) break;
498
+ this.fire(chain[i], e, true);
499
+ }
500
+ if (e.propagationStopped) return;
501
+ e.eventPhase = ReactorEvent.AT_TARGET;
502
+ this.fire(chain[chain.length - 1], e, true);
503
+ !e.immediatePropagationStopped && this.fire(chain[chain.length - 1], e, false);
504
+ if (!e.bubbles) return;
505
+ e.eventPhase = ReactorEvent.BUBBLING_PHASE;
506
+ for (let i = chain.length - 2; i >= 0; i--) {
507
+ if (e.propagationStopped) break;
508
+ this.fire(chain[i], e, false);
509
+ }
510
+ }
511
+ fire([path, object, value], e, isCapture, cords = this.listeners.get(path)) {
512
+ if (!cords) return;
513
+ e.type = path !== e.target.path ? "update" : e.staticType;
514
+ e.currentTarget = {
515
+ path,
516
+ value,
517
+ oldValue: e.type !== "update" ? e.target.oldValue : void 0,
518
+ key: e.type !== "update" ? path : path.slice(path.lastIndexOf(".") + 1) || "",
519
+ object
520
+ };
521
+ let tDepth, lDepth;
522
+ for (let i = 0, len = cords.length; i < len; i++) {
523
+ if (e.immediatePropagationStopped) break;
524
+ if (cords[i].capture !== isCapture) continue;
525
+ if (cords[i].depth !== void 0) {
526
+ tDepth ?? (tDepth = this.getDepth(e.target.path)), lDepth ?? (lDepth = this.getDepth(path));
527
+ if (tDepth > lDepth + cords[i].depth) continue;
528
+ }
529
+ cords[i].cb(e);
530
+ if (cords[i].once) cords.splice(i--, 1), !cords.length && this.listeners.delete(path);
531
+ }
532
+ }
533
+ tick(paths) {
534
+ if (!paths) return this.flush();
535
+ if ("string" === typeof paths) {
536
+ const task = this.batch?.get(paths);
537
+ task && (this.wave(paths, task), this.batch.delete(paths));
538
+ } else
539
+ for (const path of paths) {
540
+ const task = this.batch.get(path);
541
+ task && (this.wave(path, task), this.batch.delete(path));
542
+ }
543
+ }
544
+ stall(task) {
545
+ this.queue ?? (this.queue = /* @__PURE__ */ new Set());
546
+ this.queue.add(task), !this.isBatching && this.initBatching();
547
+ }
548
+ nostall(task) {
549
+ return this.queue?.delete(task);
550
+ }
551
+ bind(cord, signal) {
552
+ signal?.aborted ? cord.clup() : signal?.addEventListener("abort", cord.clup, { once: true });
553
+ if (signal && !signal.aborted) cord.sclup = () => signal.removeEventListener("abort", cord.clup);
554
+ return cord.clup;
555
+ }
556
+ clone(obj, raw, visited = /* @__PURE__ */ new WeakMap()) {
557
+ if (!(isStrictObj(obj, this.config.crossRealms) || Array.isArray(obj)) || visited.has(obj)) return obj;
558
+ const version = obj[VERSION] || 0, cached = !raw && this.isSCloning && this.snapshotCache.get(obj);
559
+ if (cached && obj[SSVERSION] === version) return cached;
560
+ const clone = !raw ? Array.isArray(obj) ? [] : {} : obj[RAW] || obj;
561
+ visited.set(obj, clone);
562
+ const keys = Object.keys(obj);
563
+ for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = this.clone(obj[keys[i]], raw, visited);
564
+ if (!raw && this.isSCloning) {
565
+ this.snapshotCache.set(obj, clone);
566
+ obj[SSVERSION] = version;
567
+ }
568
+ return clone;
569
+ }
570
+ getDepth(p, d = !p ? 0 : 1) {
571
+ for (let i = 0, len = p.length; i < len; i++) if (p.charCodeAt(i) === 46) d++;
572
+ return d;
573
+ }
574
+ getContext(path) {
575
+ const lastDot = path.lastIndexOf("."), value = path === "*" ? this.core : getAny(this.core, path), object = lastDot === -1 ? this.core : getAny(this.core, path.slice(0, lastDot));
576
+ return {
577
+ path,
578
+ value,
579
+ key: path.slice(lastDot + 1) || "",
580
+ object
581
+ };
582
+ }
583
+ syncAdd(key, path, cb, opts, onImmediate) {
584
+ var _a;
585
+ const { lazy = false, once = false, signal, immediate = false } = parseEvOpts(opts, EV_OPTS.MEDIATOR), store = this[_a = `${key}${key.endsWith("t") ? "t" : ""}ers`] ?? (this[_a] = /* @__PURE__ */ new Map());
586
+ let cords = store.get(path), cord;
587
+ if (cords) {
588
+ for (let i = 0, len = cords.length; i < len; i++)
589
+ if (Object.is(cords[i].cb, cb)) {
590
+ cord = cords[i];
591
+ break;
592
+ }
593
+ }
594
+ if (cord) return cord.clup;
595
+ let task;
596
+ cord = {
597
+ cb,
598
+ once,
599
+ clup: () => (lazy && this.nostall(task), this[`no${key}`](path, cb))
600
+ };
601
+ immediate && onImmediate?.(immediate);
602
+ task = () => (cords ?? (store.set(path, cords = []), cords)).push(cord);
603
+ lazy ? this.stall(task) : task();
604
+ return this.bind(cord, signal);
605
+ }
606
+ syncDrop(store, path, cb) {
607
+ const cords = store?.get(path);
608
+ if (!cords) return void 0;
609
+ for (let i = 0, len = cords.length; i < len; i++) if (Object.is(cords[i].cb, cb)) return cords[i].sclup?.(), cords.splice(i--, 1), !cords.length && store.delete(path), true;
610
+ return false;
611
+ }
612
+ get(path, cb, opts) {
613
+ return this.syncAdd("get", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && getAny(this.core, path));
614
+ }
615
+ gonce(path, cb, opts) {
616
+ return this.get(path, cb, {
617
+ ...parseEvOpts(opts, EV_OPTS.MEDIATOR),
618
+ once: true
619
+ });
620
+ }
621
+ noget(path, cb) {
622
+ return this.syncDrop(this.getters, path, cb);
623
+ }
624
+ set(path, cb, opts) {
625
+ return this.syncAdd("set", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && setAny(this.core, path, getAny(this.core, path)));
626
+ }
627
+ sonce(path, cb, opts) {
628
+ return this.set(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
629
+ }
630
+ noset(path, cb) {
631
+ return this.syncDrop(this.setters, path, cb);
632
+ }
633
+ delete(path, cb, opts) {
634
+ return this.syncAdd("delete", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path, void 0));
635
+ }
636
+ donce(path, cb, opts) {
637
+ return this.delete(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
638
+ }
639
+ nodelete(path, cb) {
640
+ return this.syncDrop(this.deleters, path, cb);
641
+ }
642
+ watch(path, cb, opts) {
643
+ return this.syncAdd(
644
+ "watch",
645
+ path,
646
+ cb,
647
+ opts,
648
+ (imm) => imm !== "auto" && inAny(this.core, path) && ((target) => cb(target.value, {
649
+ type: "init",
650
+ target,
651
+ currentTarget: target,
652
+ root: this.core,
653
+ rejectable: false
654
+ }))(this.getContext(path))
655
+ );
656
+ }
657
+ wonce(path, cb, opts) {
658
+ return this.watch(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
659
+ }
660
+ nowatch(path, cb) {
661
+ return this.syncDrop(this.watchers, path, cb);
662
+ }
663
+ on(path, cb, options) {
664
+ this.listeners ?? (this.listeners = /* @__PURE__ */ new Map());
665
+ const { capture = false, once = false, signal, immediate = false, depth } = parseEvOpts(options, EV_OPTS.LISTENER);
666
+ let cords = this.listeners.get(path), cord;
667
+ if (cords) {
668
+ for (let i = 0, len = cords.length; i < len; i++)
669
+ if (Object.is(cords[i].cb, cb) && capture === cords[i].capture) {
670
+ cord = cords[i];
671
+ break;
672
+ }
673
+ }
674
+ if (cord) return cord.clup;
675
+ cord = {
676
+ cb,
677
+ capture,
678
+ depth,
679
+ once,
680
+ clup: () => this.off(path, cb, options)
681
+ };
682
+ if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
683
+ const target = this.getContext(path);
684
+ cb(
685
+ new ReactorEvent(
686
+ {
687
+ type: "init",
688
+ target,
689
+ currentTarget: target,
690
+ root: this.core,
691
+ rejectable: false
692
+ },
693
+ this.config.eventBubbling,
694
+ this.isLogging
695
+ )
696
+ );
697
+ }
698
+ (cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
699
+ return this.bind(cord, signal);
700
+ }
701
+ once(path, cb, options) {
702
+ return this.on(path, cb, Object.assign(parseEvOpts(options, EV_OPTS.LISTENER), { once: true }));
703
+ }
704
+ off(path, cb, options) {
705
+ const cords = this.listeners?.get(path);
706
+ if (!cords) return void 0;
707
+ const { capture } = parseEvOpts(options, EV_OPTS.LISTENER);
708
+ for (let i = 0, len = cords.length; i < len; i++) if (Object.is(cords[i].cb, cb) && cords[i].capture === capture) return cords[i].sclup?.(), cords.splice(i--, 1), !cords.length && this.listeners.delete(path), true;
709
+ return false;
710
+ }
711
+ snapshot(raw = !this.isSCloning, branch = this.core) {
712
+ return this.clone(branch, raw);
713
+ }
714
+ cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objSafe = true) {
715
+ if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || Array.isArray(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || Array.isArray(olds)) : false)) return;
716
+ const obj = objSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
717
+ for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path + "." + keys[i], obj[keys[i]]);
718
+ }
719
+ reset() {
720
+ this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
721
+ this.queue?.clear(), this.batch?.clear(), this.isBatching = false;
722
+ this.proxyCache = /* @__PURE__ */ new WeakMap();
723
+ }
724
+ destroy() {
725
+ this.reset(), nuke(this);
726
+ }
727
+ get canLog() {
728
+ return this.log === R_LOG;
729
+ }
730
+ set canLog(value) {
731
+ this.log = (this.isLogging = value) ? R_LOG : NOOP;
732
+ }
733
+ get canTrackReferences() {
734
+ return this.isTracking;
735
+ }
736
+ get canTraceLineage() {
737
+ return this.isTracing;
738
+ }
739
+ get canSmartClone() {
740
+ return this.isSCloning;
741
+ }
742
+ };
743
+
744
+ // src/tools/mixins.ts
745
+ var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "cascade", "snapshot", "reset", "destroy"];
746
+ function reactive(target, options, prefs) {
747
+ const descriptors = {}, rtr = target instanceof Reactor ? target : new Reactor(target, options), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(prefs?.prefix || prefs?.suffix);
748
+ for (let key of methods) {
749
+ if (hasAffix) (prefs?.whitelist?.includes(key) ?? true) && (key = `${prefs?.prefix || ""}${key}${prefs?.suffix || ""}`);
750
+ else if (prefs?.whitelist?.includes(key)) continue;
751
+ descriptors[key] = { value: rtr[key].bind(rtr), ...locks };
752
+ }
753
+ descriptors["__Reactor__"] = { value: rtr, ...locks };
754
+ return Object.defineProperties(rtr.core, descriptors), rtr.core;
755
+ }
756
+ function inert(target) {
757
+ target[INERTIA] = true;
758
+ return target;
759
+ }
760
+ function live(target) {
761
+ delete target[INERTIA];
762
+ return target;
763
+ }
764
+ function isInert(target) {
765
+ return !!target[INERTIA];
766
+ }
767
+ function intent(target) {
768
+ target[REJECTABLE] = true;
769
+ return target;
770
+ }
771
+ function state(target) {
772
+ delete target[REJECTABLE];
773
+ return target;
774
+ }
775
+ function isIntent(target) {
776
+ return !!target[REJECTABLE];
777
+ }
778
+ function volatile(target) {
779
+ target[INDIFFABLE] = true;
780
+ return target;
781
+ }
782
+ function stable(target) {
783
+ delete target[INDIFFABLE];
784
+ return target;
785
+ }
786
+ function isVolatile(target) {
787
+ return !!target[INDIFFABLE];
788
+ }
789
+ function getVersion(target) {
790
+ return target[VERSION] || 0;
791
+ }
792
+ function getSnapshotVersion(target) {
793
+ return target[SSVERSION] || 0;
794
+ }
795
+ // Annotate the CommonJS export names for ESM import in node:
796
+ 0 && (module.exports = {
797
+ Reactor,
798
+ ReactorEvent,
799
+ getSnapshotVersion,
800
+ getVersion,
801
+ inert,
802
+ intent,
803
+ isInert,
804
+ isIntent,
805
+ isVolatile,
806
+ live,
807
+ methods,
808
+ reactive,
809
+ stable,
810
+ state,
811
+ volatile
812
+ });