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