sia-reactor 0.0.9 → 0.0.11

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.
@@ -1,10 +1,7 @@
1
1
  // src/utils/obj.ts
2
2
  var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
3
3
  function isDef(val) {
4
- return val !== void 0;
5
- }
6
- function isArr(obj) {
7
- return Array.isArray(obj);
4
+ return "undefined" !== typeof val;
8
5
  }
9
6
  function isObj(obj, checkArr = true) {
10
7
  return "object" === typeof obj && obj !== null && (checkArr ? !Array.isArray(obj) : true);
@@ -12,12 +9,6 @@ function isObj(obj, checkArr = true) {
12
9
  function isStrictObj(obj, crossRealms = false, typecheck = true) {
13
10
  return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
14
11
  }
15
- function isIter(obj) {
16
- return obj != null && "function" === typeof obj[Symbol.iterator];
17
- }
18
- function inBoolArrOpt(opt, str) {
19
- return opt?.includes?.(str) ?? opt;
20
- }
21
12
  function setAny(target, key, value, separator = ".", keyFunc) {
22
13
  if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
23
14
  const keys = key.split(separator);
@@ -25,7 +16,7 @@ function setAny(target, key, value, separator = ".", keyFunc) {
25
16
  const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
26
17
  if (match) {
27
18
  const [, key3, iStr] = match;
28
- if (!isArr(currObj[key3])) currObj[key3] = [];
19
+ if (!Array.isArray(currObj[key3])) currObj[key3] = [];
29
20
  if (i === len - 1) currObj[key3][Number(iStr)] = value;
30
21
  else currObj[key3][Number(iStr)] ||= {}, currObj = currObj[key3][Number(iStr)];
31
22
  } else {
@@ -42,7 +33,7 @@ function getAny(source, key, separator = ".", keyFunc) {
42
33
  const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
43
34
  if (match) {
44
35
  const [, key3, iStr] = match;
45
- if (!isArr(currObj[key3]) || !(key3 in currObj)) return void 0;
36
+ if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return void 0;
46
37
  currObj = currObj[key3][Number(iStr)];
47
38
  } else {
48
39
  if (!isObj(currObj) || !(key2 in currObj)) return void 0;
@@ -58,7 +49,7 @@ function deleteAny(target, key, separator = ".", keyFunc) {
58
49
  const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
59
50
  if (match) {
60
51
  const [, key3, iStr] = match;
61
- if (!isArr(currObj[key3]) || !(key3 in currObj)) return;
52
+ if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return;
62
53
  if (i === len - 1) delete currObj[key3][Number(iStr)];
63
54
  else currObj = currObj[key3][Number(iStr)];
64
55
  } else {
@@ -75,7 +66,7 @@ function inAny(source, key, separator = ".", keyFunc) {
75
66
  const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
76
67
  if (match) {
77
68
  const [, key3, iStr] = match;
78
- if (!isArr(currObj[key3]) || !(key3 in currObj)) return false;
69
+ if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return false;
79
70
  if (i === len - 1) return true;
80
71
  currObj = currObj[key3][Number(iStr)];
81
72
  } else {
@@ -93,7 +84,7 @@ function parseAnyObj(obj, separator = ".", keyFunc = (p) => p, visited = /* @__P
93
84
  Object.keys(obj).forEach((k) => k.includes(separator) ? setAny(result, k, parseAnyObj(obj[k], separator, keyFunc, visited), separator, keyFunc) : result[k] = isObj(obj[k]) ? parseAnyObj(obj[k], separator, keyFunc, visited) : obj[k]);
94
85
  return result;
95
86
  }
96
- function parseEvOpts(options, opts, boolOpt = opts[0], result = {}) {
87
+ function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
97
88
  return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
98
89
  }
99
90
  function mergeObjs(o1 = {}, o2 = {}) {
@@ -113,8 +104,8 @@ function getTrailRecords(obj, path) {
113
104
  return record;
114
105
  }
115
106
  function deepClone(obj, crossRealms, visited = /* @__PURE__ */ new WeakMap()) {
116
- if (!(isStrictObj(obj, crossRealms) || isArr(obj)) || visited.has(obj)) return obj;
117
- const clone = isArr(obj) ? [] : {};
107
+ if (!(isStrictObj(obj, crossRealms) || Array.isArray(obj)) || visited.has(obj)) return obj;
108
+ const clone = Array.isArray(obj) ? [] : {};
118
109
  visited.set(obj, clone);
119
110
  const keys = Object.keys(obj);
120
111
  for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = deepClone(obj[keys[i]], crossRealms, visited);
@@ -136,17 +127,14 @@ function nuke(target) {
136
127
 
137
128
  export {
138
129
  isDef,
139
- isArr,
140
130
  isObj,
141
131
  isStrictObj,
142
- isIter,
143
- inBoolArrOpt,
144
132
  setAny,
145
133
  getAny,
146
134
  deleteAny,
147
135
  inAny,
148
136
  parseAnyObj,
149
- parseEvOpts,
137
+ parseEvtOpts,
150
138
  mergeObjs,
151
139
  getTrailPaths,
152
140
  getTrailRecords,
@@ -4,13 +4,13 @@ import {
4
4
  getTrailPaths,
5
5
  getTrailRecords,
6
6
  inAny,
7
- isArr,
7
+ isDef,
8
8
  isStrictObj,
9
9
  mergeObjs,
10
10
  nuke,
11
- parseEvOpts,
11
+ parseEvtOpts,
12
12
  setAny
13
- } from "./chunk-JWR2Y4OV.js";
13
+ } from "./chunk-DW7Z7YEA.js";
14
14
 
15
15
  // src/core/reactor.ts
16
16
  var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
@@ -20,12 +20,13 @@ var INDIFFABLE = /* @__PURE__ */ Symbol.for("S.I.A_INDIFFABLE");
20
20
  var TERMINATOR = /* @__PURE__ */ Symbol.for("S.I.A_TERMINATOR");
21
21
  var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
22
22
  var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
23
+ var NIL = Object.freeze({});
23
24
  var NOOP = () => {
24
25
  };
25
- var R_BATCH = ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window);
26
- var R_LOG = console.log.bind(console, "[S.I.A Reactor]");
27
- var EV_WARN = console.warn.bind(console, "[S.I.A Event]");
28
- var EV_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
26
+ var RTR_BATCH = (isDef(queueMicrotask) ? queueMicrotask : setTimeout).bind(window);
27
+ var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
28
+ var EVT_WARN = console.warn.bind(console, "[S.I.A Event]");
29
+ var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
29
30
  var ReactorEvent = class _ReactorEvent {
30
31
  static NONE = 0;
31
32
  static CAPTURING_PHASE = 1;
@@ -41,24 +42,24 @@ var ReactorEvent = class _ReactorEvent {
41
42
  value;
42
43
  oldValue;
43
44
  rejectable;
44
- // readonly timestamp: number;
45
45
  bubbles;
46
- _propagationStopped = false;
47
- _immediatePropagationStopped = false;
46
+ // readonly timestamp: number;
47
+ _warn = NOOP;
48
48
  _resolved = "";
49
49
  _rejected = "";
50
- _warn = NOOP;
50
+ _propagationStopped = false;
51
+ _immediatePropagationStopped = false;
51
52
  constructor(payload, bubbles = false, canWarn = true) {
52
53
  this.type = this.staticType = payload.type;
53
54
  this.target = payload.target;
54
55
  this.currentTarget = payload.currentTarget;
55
56
  this.root = payload.root;
57
+ this.path = payload.target.path;
56
58
  this.value = payload.target.value;
57
59
  this.oldValue = payload.target.oldValue;
58
- this.path = payload.target.path;
59
60
  this.rejectable = payload.rejectable;
60
61
  this.bubbles = bubbles;
61
- if (canWarn) this._warn = EV_WARN;
62
+ if (canWarn) this._warn = EVT_WARN;
62
63
  }
63
64
  get propagationStopped() {
64
65
  return this._propagationStopped;
@@ -93,26 +94,14 @@ var ReactorEvent = class _ReactorEvent {
93
94
  return getTrailPaths(this.path);
94
95
  }
95
96
  get canWarn() {
96
- return this._warn === EV_WARN;
97
+ return this._warn !== NOOP;
97
98
  }
98
99
  };
99
100
  var Reactor = class {
100
- getters;
101
- setters;
102
- deleters;
103
- watchers;
104
- listeners;
105
- lineage;
106
- // { parent, key }: uses maths to avoid extra allocations for pairs
107
- queue;
108
- // Tasks to run after flush
109
- batch;
110
- proxyCache = /* @__PURE__ */ new WeakMap();
111
- snapshotCache;
112
101
  log = NOOP;
113
- config;
114
102
  core;
115
103
  // `?:`s | pay the ~800 byte price upfront for what u might never use
104
+ config;
116
105
  isLogging = false;
117
106
  // keeping track so API getter doesn't slow down internal iterations in any way
118
107
  isTracing = false;
@@ -125,19 +114,32 @@ var Reactor = class {
125
114
  // Async Batching
126
115
  isCascading = false;
127
116
  // Setter Cascading
117
+ queue;
118
+ // Tasks to run after flush
119
+ batch;
120
+ // Batched payloads to flush async
121
+ lineage;
122
+ // { parent, key }: uses maths to avoid extra allocations for pairs
123
+ snapCache;
124
+ proxyCache = /* @__PURE__ */ new WeakMap();
125
+ getters;
126
+ setters;
127
+ deleters;
128
+ watchers;
129
+ listeners;
128
130
  constructor(obj = {}, options) {
129
131
  this[INERTIA] = true;
130
- this.config = { crossRealms: false, eventBubbling: true, preserveContext: false, equalityFunction: Object.is, batchingFunction: R_BATCH, ...options };
132
+ this.config = { crossRealms: false, eventBubbling: true, preserveContext: false, equalityFunction: Object.is, batchingFunction: RTR_BATCH, ...options };
131
133
  this.core = this.proxied(obj);
132
134
  if (!options) return;
133
135
  this.canLog = !!options.debug;
134
136
  if (this.isTracking = !!options.referenceTracking) this.lineage = /* @__PURE__ */ new WeakMap();
135
- if (this.isSCloning = this.isTracking && !!options.smartCloning) this.snapshotCache = /* @__PURE__ */ new WeakMap();
137
+ if (this.isSCloning = this.isTracking && !!options.smartCloning) this.snapCache = /* @__PURE__ */ new WeakMap();
136
138
  this.isTracing = this.isTracking && !!options.lineageTracing;
137
139
  }
138
140
  proxied(obj, rejectable = false, indiffable = false, parent, key, path) {
139
- if (!obj || typeof obj !== "object") return obj;
140
- if (!(isStrictObj(obj, this.config.crossRealms, false) || isArr(obj)) || obj[INERTIA]) return obj;
141
+ if (!obj || "object" !== typeof obj) return obj;
142
+ if (!(isStrictObj(obj, this.config.crossRealms, false) || Array.isArray(obj)) || obj[INERTIA]) return obj;
141
143
  obj = obj[RAW] || obj;
142
144
  if (this.isTracking && parent && key) this.link(obj, parent, key, false);
143
145
  if (this.proxyCache.has(obj)) return this.proxyCache.get(obj);
@@ -246,13 +248,13 @@ var Reactor = class {
246
248
  }
247
249
  // won't be called without `.isTracking` so internal guard avoided
248
250
  link(target, parent, key, typecheck = true, es) {
249
- if (typecheck && !(isStrictObj(target, this.config.crossRealms) || isArr(target))) return;
251
+ if (typecheck && !(isStrictObj(target, this.config.crossRealms) || Array.isArray(target))) return;
250
252
  es = this.lineage.get(target) ?? (this.lineage.set(target, es = []), es);
251
253
  for (let i = 0, len = es.length; i < len; i += 2) if (Object.is(es[i], parent) && es[i + 1] === key) return;
252
254
  es.push(parent, key);
253
255
  }
254
256
  unlink(target, parent, key) {
255
- if (!(isStrictObj(target, this.config.crossRealms) || isArr(target))) return;
257
+ if (!(isStrictObj(target, this.config.crossRealms) || Array.isArray(target))) return;
256
258
  const es = this.lineage.get(target);
257
259
  if (es) {
258
260
  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);
@@ -351,32 +353,31 @@ var Reactor = class {
351
353
  nostall(task) {
352
354
  return this.queue?.delete(task);
353
355
  }
354
- bind(cord, signal) {
355
- signal?.aborted ? cord.clup() : signal?.addEventListener("abort", cord.clup, { once: true });
356
- if (signal && !signal.aborted) cord.sclup = () => signal.removeEventListener("abort", cord.clup);
357
- return cord.clup;
356
+ bind(cord, sig) {
357
+ if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
358
+ return cord.sclup = !sig || sig.aborted ? NOOP : () => sig.removeEventListener("abort", cord.clup), cord.clup;
358
359
  }
359
360
  clone(obj, raw, visited = /* @__PURE__ */ new WeakMap()) {
360
- if (!(isStrictObj(obj, this.config.crossRealms) || isArr(obj)) || visited.has(obj)) return obj;
361
- const version = obj[VERSION] || 0, cached = !raw && this.isSCloning && this.snapshotCache.get(obj);
361
+ if (!(isStrictObj(obj, this.config.crossRealms) || Array.isArray(obj)) || visited.has(obj)) return obj;
362
+ const version = obj[VERSION] || 0, cached = !raw && this.isSCloning && this.snapCache.get(obj);
362
363
  if (cached && obj[SSVERSION] === version) return cached;
363
- const clone = !raw ? isArr(obj) ? [] : {} : obj[RAW] || obj;
364
+ const clone = !raw ? Array.isArray(obj) ? [] : {} : obj[RAW] || obj;
364
365
  visited.set(obj, clone);
365
366
  const keys = Object.keys(obj);
366
367
  for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = this.clone(obj[keys[i]], raw, visited);
367
- if (!raw && this.isSCloning) this.snapshotCache.set(obj, clone), obj[SSVERSION] = version;
368
+ if (!raw && this.isSCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
368
369
  return clone;
369
370
  }
370
- getDepth(p, d = !p ? 0 : 1) {
371
- for (let i = 0, len = p.length; i < len; i++) if (p.charCodeAt(i) === 46) d++;
371
+ getDepth(path, d = !path ? 0 : 1) {
372
+ for (let i = 0, len = path.length; i < len; i++) if (path.charCodeAt(i) === 46) d++;
372
373
  return d;
373
374
  }
374
375
  getContext(path) {
375
376
  const lastDot = path.lastIndexOf("."), value = path === "*" ? this.core : getAny(this.core, path), object = lastDot === -1 ? this.core : getAny(this.core, path.slice(0, lastDot));
376
377
  return { path, value, key: path.slice(lastDot + 1) || "", object };
377
378
  }
378
- syncAdd(key, path, cb, opts, onImmediate) {
379
- const { lazy = false, once = false, signal, immediate = false } = parseEvOpts(opts, EV_OPTS.MEDIATOR), store = this[`${key}${key.endsWith("t") ? "t" : ""}ers`] ??= /* @__PURE__ */ new Map();
379
+ syncAdd(key, path, cb, opts, onImmediate = NOOP) {
380
+ const { lazy = false, once = false, signal, immediate = false } = parseEvtOpts(opts, EVT_OPTS.MEDIATOR), store = this[`${key}${key.endsWith("t") ? "t" : ""}ers`] ??= /* @__PURE__ */ new Map();
380
381
  let cords = store.get(path), cord;
381
382
  if (cords) {
382
383
  for (let i = 0, len = cords.length; i < len; i++)
@@ -388,7 +389,7 @@ var Reactor = class {
388
389
  if (cord) return cord.clup;
389
390
  let task;
390
391
  cord = { cb, once, clup: () => (lazy && this.nostall(task), this[`no${key}`](path, cb)) };
391
- immediate && onImmediate?.(immediate);
392
+ immediate && onImmediate(immediate);
392
393
  task = () => (cords ?? (store.set(path, cords = []), cords)).push(cord);
393
394
  lazy ? this.stall(task) : task();
394
395
  return this.bind(cord, signal);
@@ -396,80 +397,80 @@ var Reactor = class {
396
397
  syncDrop(store, path, cb) {
397
398
  const cords = store?.get(path);
398
399
  if (!cords) return void 0;
399
- 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;
400
+ 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;
400
401
  return false;
401
402
  }
402
- get(path, cb, opts) {
403
- return this.syncAdd("get", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && getAny(this.core, path));
403
+ get(path, callback, options) {
404
+ return this.syncAdd("get", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && getAny(this.core, path));
404
405
  }
405
- gonce(path, cb, opts) {
406
- return this.get(path, cb, { ...parseEvOpts(opts, EV_OPTS.MEDIATOR), once: true });
406
+ gonce(path, callback, options) {
407
+ return this.get(path, callback, { ...parseEvtOpts(options, EVT_OPTS.MEDIATOR), once: true });
407
408
  }
408
- noget(path, cb) {
409
- return this.syncDrop(this.getters, path, cb);
409
+ noget(path, callback) {
410
+ return this.syncDrop(this.getters, path, callback);
410
411
  }
411
- set(path, cb, opts) {
412
- return this.syncAdd("set", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && setAny(this.core, path, getAny(this.core, path)));
412
+ set(path, callback, options) {
413
+ return this.syncAdd("set", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && setAny(this.core, path, getAny(this.core, path)));
413
414
  }
414
- sonce(path, cb, opts) {
415
- return this.set(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
415
+ sonce(path, callback, options) {
416
+ return this.set(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.MEDIATOR), { once: true }));
416
417
  }
417
- noset(path, cb) {
418
- return this.syncDrop(this.setters, path, cb);
418
+ noset(path, callback) {
419
+ return this.syncDrop(this.setters, path, callback);
419
420
  }
420
- delete(path, cb, opts) {
421
- return this.syncAdd("delete", path, cb, opts, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path, void 0));
421
+ delete(path, callback, options) {
422
+ return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path, void 0));
422
423
  }
423
- donce(path, cb, opts) {
424
- return this.delete(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
424
+ donce(path, callback, options) {
425
+ return this.delete(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.MEDIATOR), { once: true }));
425
426
  }
426
- nodelete(path, cb) {
427
- return this.syncDrop(this.deleters, path, cb);
427
+ nodelete(path, callback) {
428
+ return this.syncDrop(this.deleters, path, callback);
428
429
  }
429
- watch(path, cb, opts) {
430
- return this.syncAdd("watch", path, cb, opts, (imm) => imm !== "auto" && inAny(this.core, path) && ((target) => cb(target.value, { type: "init", target, currentTarget: target, root: this.core, rejectable: false }))(this.getContext(path)));
430
+ watch(path, callback, options) {
431
+ return this.syncAdd("watch", path, callback, options, (imm) => imm !== "auto" && inAny(this.core, path) && ((target) => callback(target.value, { type: "init", target, currentTarget: target, root: this.core, rejectable: false }))(this.getContext(path)));
431
432
  }
432
- wonce(path, cb, opts) {
433
- return this.watch(path, cb, Object.assign(parseEvOpts(opts, EV_OPTS.MEDIATOR), { once: true }));
433
+ wonce(path, callback, options) {
434
+ return this.watch(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.MEDIATOR), { once: true }));
434
435
  }
435
- nowatch(path, cb) {
436
- return this.syncDrop(this.watchers, path, cb);
436
+ nowatch(path, callback) {
437
+ return this.syncDrop(this.watchers, path, callback);
437
438
  }
438
- on(path, cb, options) {
439
+ on(path, callback, options) {
439
440
  this.listeners ??= /* @__PURE__ */ new Map();
440
- const { capture = false, once = false, signal, immediate = false, depth } = parseEvOpts(options, EV_OPTS.LISTENER);
441
+ const { capture = false, once = false, signal, immediate = false, depth } = parseEvtOpts(options, EVT_OPTS.LISTENER);
441
442
  let cords = this.listeners.get(path), cord;
442
443
  if (cords) {
443
444
  for (let i = 0, len = cords.length; i < len; i++)
444
- if (Object.is(cords[i].cb, cb) && capture === cords[i].capture) {
445
+ if (Object.is(cords[i].cb, callback) && capture === cords[i].capture) {
445
446
  cord = cords[i];
446
447
  break;
447
448
  }
448
449
  }
449
450
  if (cord) return cord.clup;
450
- cord = { cb, capture, depth, once, clup: () => this.off(path, cb, options) };
451
+ cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options) };
451
452
  if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
452
453
  const target = this.getContext(path);
453
- cb(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this.config.eventBubbling, this.isLogging));
454
+ callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this.config.eventBubbling, this.isLogging));
454
455
  }
455
456
  (cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
456
457
  return this.bind(cord, signal);
457
458
  }
458
- once(path, cb, options) {
459
- return this.on(path, cb, Object.assign(parseEvOpts(options, EV_OPTS.LISTENER), { once: true }));
459
+ once(path, callback, options) {
460
+ return this.on(path, callback, Object.assign(parseEvtOpts(options, EVT_OPTS.LISTENER), { once: true }));
460
461
  }
461
- off(path, cb, options) {
462
+ off(path, callback, options) {
462
463
  const cords = this.listeners?.get(path);
463
464
  if (!cords) return void 0;
464
- const { capture } = parseEvOpts(options, EV_OPTS.LISTENER);
465
- 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;
465
+ const { capture } = parseEvtOpts(options, EVT_OPTS.LISTENER);
466
+ for (let i = 0, len = cords.length; i < len; i++) if (Object.is(cords[i].cb, callback) && cords[i].capture === capture) return cords[i].sclup(), cords.splice(i--, 1), !cords.length && this.listeners.delete(path), true;
466
467
  return false;
467
468
  }
468
469
  snapshot(raw = !this.isSCloning, branch = this.core) {
469
470
  return this.clone(branch, raw);
470
471
  }
471
472
  cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objSafe = true) {
472
- if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || isArr(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || isArr(olds)) : false)) return;
473
+ if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || Array.isArray(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || Array.isArray(olds)) : false)) return;
473
474
  const obj = objSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
474
475
  this.isCascading = true;
475
476
  for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path + "." + keys[i], obj[keys[i]]);
@@ -483,10 +484,10 @@ var Reactor = class {
483
484
  this.reset(), nuke(this);
484
485
  }
485
486
  get canLog() {
486
- return this.isLogging;
487
+ return this.isLogging = this.log !== NOOP;
487
488
  }
488
489
  set canLog(value) {
489
- this.log = (this.isLogging = value) ? R_LOG : NOOP;
490
+ this.log = (this.isLogging = value) ? RTR_LOG : NOOP;
490
491
  }
491
492
  get canTrackReferences() {
492
493
  return this.isTracking;
@@ -501,11 +502,13 @@ var Reactor = class {
501
502
 
502
503
  // src/utils/mixins.ts
503
504
  var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "cascade", "snapshot", "reset", "destroy"];
504
- function reactive(target, options, prefs) {
505
- const descriptors = {}, rtr = target instanceof Reactor ? target : new Reactor(target, options), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(prefs?.prefix || prefs?.suffix);
506
- for (let key of methods) {
507
- if (hasAffix) (prefs?.whitelist?.includes(key) ?? true) && (key = `${prefs?.prefix || ""}${key}${prefs?.suffix || ""}`);
508
- else if (prefs?.whitelist?.includes(key)) continue;
505
+ function reactive(target, options, prefs = NIL) {
506
+ if ("__Reactor__" in target) return target;
507
+ const descriptors = {}, rtr = target instanceof Reactor ? target : new Reactor(target, options), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(prefs.prefix || prefs.suffix);
508
+ for (let i = 0, len = methods.length; i < len; i++) {
509
+ let key = methods[i];
510
+ if (hasAffix) (prefs.whitelist?.includes(key) ?? true) && (key = `${prefs.prefix || ""}${key}${prefs.suffix || ""}`);
511
+ else if (prefs.whitelist?.includes(key)) continue;
509
512
  descriptors[key] = { value: rtr[key].bind(rtr), ...locks };
510
513
  }
511
514
  descriptors["__Reactor__"] = { value: rtr, ...locks };
@@ -553,6 +556,8 @@ function getSnapshotVersion(target) {
553
556
 
554
557
  export {
555
558
  TERMINATOR,
559
+ NIL,
560
+ NOOP,
556
561
  ReactorEvent,
557
562
  Reactor,
558
563
  methods,