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.
Files changed (38) hide show
  1. package/README.md +115 -89
  2. package/dist/{TimeTravelOverlay-DxqJL0Zk.d.ts → TimeTravelOverlay-Dglcwpg-.d.ts} +9 -8
  3. package/dist/{TimeTravelOverlay-CJv-S_Km.d.cts → TimeTravelOverlay-OjklzuCD.d.cts} +9 -8
  4. package/dist/adapters/react.cjs +74 -91
  5. package/dist/adapters/react.d.cts +23 -22
  6. package/dist/adapters/react.d.ts +23 -22
  7. package/dist/adapters/react.js +3 -3
  8. package/dist/adapters/vanilla.cjs +74 -91
  9. package/dist/adapters/vanilla.d.cts +4 -4
  10. package/dist/adapters/vanilla.d.ts +4 -4
  11. package/dist/adapters/vanilla.js +3 -3
  12. package/dist/{chunk-TFLYCXK4.js → chunk-5JNWC7Z4.js} +14 -13
  13. package/dist/{chunk-DP74DVRT.js → chunk-MKL3JUPO.js} +55 -15
  14. package/dist/{chunk-2WBPGSRL.js → chunk-MSTHQVNK.js} +61 -78
  15. package/dist/{index-Oie9hhE8.d.cts → index-m0aAWxhX.d.cts} +330 -218
  16. package/dist/{index-Oie9hhE8.d.ts → index-m0aAWxhX.d.ts} +330 -218
  17. package/dist/index.cjs +69 -89
  18. package/dist/index.d.cts +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +2 -4
  21. package/dist/{plugins.cjs → modules.cjs} +464 -195
  22. package/dist/modules.d.cts +52 -0
  23. package/dist/modules.d.ts +52 -0
  24. package/dist/modules.js +619 -0
  25. package/dist/super.d.ts +642 -298
  26. package/dist/super.global.js +481 -210
  27. package/dist/timeTravel-DExvNb04.d.ts +352 -0
  28. package/dist/timeTravel-DctvcHVt.d.cts +352 -0
  29. package/dist/utils.cjs +59 -14
  30. package/dist/utils.d.cts +1 -1
  31. package/dist/utils.d.ts +1 -1
  32. package/dist/utils.js +7 -1
  33. package/package.json +6 -6
  34. package/dist/plugins.d.cts +0 -112
  35. package/dist/plugins.d.ts +0 -112
  36. package/dist/plugins.js +0 -370
  37. package/dist/timeTravel-B1vedDQc.d.ts +0 -76
  38. package/dist/timeTravel-WpgWmKu-.d.cts +0 -76
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  reactive
3
- } from "./chunk-2WBPGSRL.js";
3
+ } from "./chunk-MSTHQVNK.js";
4
4
  import {
5
5
  createEl,
6
6
  formatKeyForDisplay,
@@ -13,7 +13,7 @@ import {
13
13
  RAW,
14
14
  canHandle,
15
15
  nuke
16
- } from "./chunk-DP74DVRT.js";
16
+ } from "./chunk-MKL3JUPO.js";
17
17
 
18
18
  // src/ts/adapters/autotracker.ts
19
19
  var Autotracker = class {
@@ -119,9 +119,9 @@ var Autotracker = class {
119
119
  * const stop = atrkr.callback(() => console.log("changed")); // re-run after when ".user.name" changes
120
120
  * @example Packaged Customization
121
121
  * const atrkr = new Autotracker(); // no reactor passed
122
- * withTracker(atrkr, () => state.user.name); // import `withTracker` first
122
+ * withTracker(atrkr, () => state.user.name); // import `withTracker` too
123
123
  * 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
124
- * @example Extensive customization
124
+ * @example Extensive Customization
125
125
  * atrkr.unblock();
126
126
  * const prev = CTX.autotracker;
127
127
  * CTX.autotracker = atrkr; // import CTX first
@@ -169,8 +169,8 @@ function effect(callback, options) {
169
169
 
170
170
  // src/ts/adapters/vanilla/TimeTravelOverlay.ts
171
171
  var keys = {
172
- overrides: ["Ctrl+z", "Cmd+z", "Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z", "Ctrl+g", "Cmd+g", ",", ".", "ArrowLeft", "ArrowRight", "Space", "Alt+Space", "Escape", "Delete", "e", "i", "c"],
173
- shortcuts: { undo: ["Ctrl+z", "Cmd+z"], redo: ["Ctrl+y", "Cmd+y", "Ctrl+Shift+z", "Cmd+Shift+z"], genesis: ["Ctrl+g", "Cmd+g"], prevFrame: ",", nextFrame: ".", skipBwd: "ArrowLeft", skipFwd: "ArrowRight", playPause: "Space", rewind: "Alt+Space", closeOverlay: "Escape", clrHistory: "Delete", export: "e", import: "i", clear: "c" }
172
+ 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"],
173
+ 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" }
174
174
  };
175
175
  var TimeTravelOverlay = class _TimeTravelOverlay {
176
176
  static count = 0;
@@ -181,22 +181,22 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
181
181
  els;
182
182
  clups = [];
183
183
  keyup;
184
- /** Creates a docked TimeTravel overlay bound to a plugin instance.
185
- * @param time TimeTravel plugin instance that owns timeline operations.
184
+ /** Creates a docked TimeTravel overlay bound to a module instance.
185
+ * @param time TimeTravel module instance that owns timeline operations.
186
186
  * @param build Optional initial overlay config overrides.
187
187
  */
188
188
  constructor(time, build = {}) {
189
189
  this.time = time;
190
190
  this.config = reactive({ title: `Time Travel Overlay ${this.index = ++_TimeTravelOverlay.count}`, ...build });
191
191
  this.state.open = !!this.config.startOpen;
192
- 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[0])}`, 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" });
192
+ 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" });
193
193
  status.append((box.append(frame), box), clrHistory);
194
194
  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));
195
195
  host.append(toggle, panel);
196
196
  this.els = { host, toggle, panel, title, frame, clrHistory, undo, redo, genesis, playPause, rewind, range, exp, imp, clr, payload, io };
197
197
  this.keyup = (e) => {
198
- const a = this.state.open && keyEventAllowed(e, keys);
199
- 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 = "");
198
+ const a = this.state.open && (this.config.devOnly ? CTX.isDevEnv : true) && keyEventAllowed(e, keys);
199
+ 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 = "");
200
200
  };
201
201
  window.addEventListener("keydown", this.keyup);
202
202
  const sync = [
@@ -211,7 +211,8 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
211
211
  effect(() => {
212
212
  frame.textContent = `Frame: ${s.currentFrame} / ${s.history.length}`;
213
213
  range.disabled = clrHistory.disabled = !s.history.length;
214
- genesis.disabled = rewind.disabled = undo.disabled = !s.currentFrame;
214
+ genesis.disabled = undo.disabled = !s.currentFrame;
215
+ rewind.disabled = !s.paused || !s.currentFrame;
215
216
  playPause.disabled = redo.disabled = s.currentFrame >= s.history.length;
216
217
  range.max = String(s.history.length);
217
218
  range.value = String(Math.min(s.currentFrame, s.history.length));
@@ -225,7 +226,7 @@ var TimeTravelOverlay = class _TimeTravelOverlay {
225
226
  this.clups.push(...sync);
226
227
  }
227
228
  destroy() {
228
- this.clups.forEach((fn) => fn());
229
+ for (const clup of this.clups) clup();
229
230
  this.keyup && window.removeEventListener("keydown", this.keyup);
230
231
  this.els.host.remove();
231
232
  nuke(this), --_TimeTravelOverlay.count;
@@ -2,7 +2,9 @@
2
2
  var CTX = {
3
3
  /** Flag indicating whether the application is running in development mode. */
4
4
  isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
5
- /** active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
5
+ /** Flag indicating whether a cascade is currently ongoing so reactors can allow all writes. */
6
+ isCascading: false,
7
+ /** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
6
8
  autotracker: null
7
9
  };
8
10
  var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
@@ -14,14 +16,13 @@ var VERSION = /* @__PURE__ */ Symbol.for("S.I.A_VERSION");
14
16
  var SSVERSION = /* @__PURE__ */ Symbol.for("S.I.A_SNAPSHOT_VERSION");
15
17
  var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
16
18
  var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
17
- var EVT_WARN = console.warn.bind(console, "[S.I.A Event]");
18
19
  var EVT_OPTS = { LISTENER: ["capture", "depth", "once", "signal", "immediate"], MEDIATOR: ["lazy", "signal", "immediate"] };
19
20
  var NIL = Object.freeze({});
20
21
  var NOOP = () => {
21
22
  };
22
23
 
23
24
  // src/ts/utils/obj.ts
24
- var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
25
+ var arrRegex = /^([^\[\]]+)\[(\d+)\]$/;
25
26
  function isObj(obj, arraycheck = true) {
26
27
  return "object" === typeof obj && obj !== null && (arraycheck ? !Array.isArray(obj) : true);
27
28
  }
@@ -29,7 +30,7 @@ function isPOJO(obj, config = NIL, typecheck = true) {
29
30
  return (typecheck ? isObj(obj, false) : true) && (config.crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
30
31
  }
31
32
  function canHandle(obj, config = NIL, typecheck = true) {
32
- if (typecheck && !isObj(obj, false)) return false;
33
+ if (typecheck && !isObj(obj, false) || obj[INERTIA]) return false;
33
34
  if (Array.isArray(obj) || !config.preserveContext && isPOJO(obj, config, false)) return true;
34
35
  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);
35
36
  return false;
@@ -40,7 +41,7 @@ function getAny(source, key, separator = ".", keyFunc) {
40
41
  const keys = key.split(separator);
41
42
  let currObj = source;
42
43
  for (let i = 0, len = keys.length; i < len; i++) {
43
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
44
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRegex);
44
45
  if (match) {
45
46
  const [, key3, iStr] = match;
46
47
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return void 0;
@@ -57,7 +58,7 @@ function setAny(target, key, value, separator = ".", keyFunc) {
57
58
  if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
58
59
  const keys = key.split(separator);
59
60
  for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
60
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
61
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRegex);
61
62
  if (match) {
62
63
  const [, key3, iStr] = match;
63
64
  if (!Array.isArray(currObj[key3])) currObj[key3] = [];
@@ -78,7 +79,7 @@ function deleteAny(target, key, separator = ".", keyFunc) {
78
79
  if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
79
80
  const keys = key.split(separator);
80
81
  for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
81
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
82
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRegex);
82
83
  if (match) {
83
84
  const [, key3, iStr] = match;
84
85
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return;
@@ -96,7 +97,7 @@ function inAny(source, key, separator = ".", keyFunc) {
96
97
  if (!key.includes(separator)) return key in source;
97
98
  const keys = key.split(separator);
98
99
  for (let currObj = source, i = 0, len = keys.length; i < len; i++) {
99
- const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
100
+ const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRegex);
100
101
  if (match) {
101
102
  const [, key3, iStr] = match;
102
103
  if (!Array.isArray(currObj[key3]) || !(key3 in currObj)) return false;
@@ -113,16 +114,48 @@ function inAny(source, key, separator = ".", keyFunc) {
113
114
  function parseAnyObj(obj, separator = ".", keyFunc = (p) => p, seen = /* @__PURE__ */ new WeakSet()) {
114
115
  if (!isObj(obj) || seen.has(obj)) return obj;
115
116
  seen.add(obj);
116
- const result = {};
117
- Object.keys(obj).forEach((k) => 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]);
117
+ const result = {}, keys = Object.keys(obj);
118
+ for (let i = 0, len = keys.length; i < len; i++) {
119
+ const k = keys[i];
120
+ 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];
121
+ }
118
122
  return result;
119
123
  }
120
124
  function parseEvtOpts(options, opts, boolOpt = opts[0], result = {}) {
121
125
  return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
122
126
  }
123
- function mergeObjs(o1 = {}, o2 = {}) {
124
- const merged = { ...o1 || {}, ...o2 || {} };
125
- return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
127
+ function fanout(a, b, c, d) {
128
+ const isEvPd = !!a?.target, isPath = !isEvPd && "string" === typeof b, [state, 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(state, path) : olds;
129
+ if (isEvPd && type !== "set" && type !== "delete" || !target || !canHandle(news, opts)) return;
130
+ const prev = CTX.isCascading;
131
+ CTX.isCascading = isEvPd;
132
+ try {
133
+ const walk = (target2, obj, depth = isEvPd ? 1 : Infinity, keys = Object.keys(obj)) => {
134
+ for (let i = 0, len = keys.length; i < len; i++) {
135
+ const val = obj[keys[i]];
136
+ try {
137
+ if ((opts.atomic ?? true) && Array.isArray(val)) target2[keys[i]] = val, target2[keys[i]].length = target2[keys[i]].length;
138
+ else depth > 1 && canHandle(val, opts) ? walk(target2[keys[i]] ||= {}, val, depth - 1) : target2[keys[i]] = val;
139
+ } catch (e) {
140
+ if (e instanceof RangeError) throw e;
141
+ }
142
+ }
143
+ };
144
+ if ((opts.atomic ?? true) && Array.isArray(news) && isPath) setAny(state, path, news), getAny(state, path).length = news.length;
145
+ else walk(target, opts.merge ? mergeObjs(olds, news, opts) : news, opts.depth === true ? Infinity : opts.depth);
146
+ } finally {
147
+ CTX.isCascading = prev;
148
+ }
149
+ }
150
+ var fanoutOptsArr = ["merge", "depth", "atomic"];
151
+ function mergeObjs(o1, o2, config, pojocheck = true) {
152
+ if (pojocheck && (!isPOJO(o1 || NIL, config) || !isPOJO(o2 || NIL, config))) return o2;
153
+ const merged = { ...o1 ||= {}, ...o2 ||= {} }, keys = Object.keys(merged);
154
+ for (let i = 0, len = keys.length; i < len; i++) {
155
+ const o1C = o1[keys[i]], o2C = o2[keys[i]];
156
+ if (isPOJO(o1C, config) && isPOJO(o2C, config)) merged[keys[i]] = mergeObjs(o1C, o2C, config, false);
157
+ }
158
+ return merged;
126
159
  }
127
160
  function getTrailRecords(obj, path, reverse = false) {
128
161
  const parts = path.split("."), chain = [["*", obj, obj]];
@@ -137,7 +170,12 @@ function deepClone(obj, config = NIL, seen = /* @__PURE__ */ new WeakMap()) {
137
170
  const clone = config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {};
138
171
  seen.set(obj, clone);
139
172
  const keys = config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
140
- for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = deepClone(obj[keys[i]], config, seen);
173
+ for (let i = 0, len = keys.length; i < len; i++)
174
+ try {
175
+ clone[keys[i]] = deepClone(obj[keys[i]], config, seen);
176
+ } catch (e) {
177
+ if (e instanceof RangeError) throw e;
178
+ }
141
179
  return clone;
142
180
  }
143
181
  function nuke(target) {
@@ -165,10 +203,10 @@ export {
165
203
  SSVERSION,
166
204
  RTR_BATCH,
167
205
  RTR_LOG,
168
- EVT_WARN,
169
206
  EVT_OPTS,
170
207
  NIL,
171
208
  NOOP,
209
+ arrRegex,
172
210
  isObj,
173
211
  isPOJO,
174
212
  canHandle,
@@ -178,6 +216,8 @@ export {
178
216
  inAny,
179
217
  parseAnyObj,
180
218
  parseEvtOpts,
219
+ fanout,
220
+ fanoutOptsArr,
181
221
  mergeObjs,
182
222
  getTrailRecords,
183
223
  deepClone,
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  CTX,
3
3
  EVT_OPTS,
4
- EVT_WARN,
5
4
  INDIFFABLE,
6
5
  INERTIA,
7
6
  NIL,
@@ -18,11 +17,10 @@ import {
18
17
  getAny,
19
18
  getTrailRecords,
20
19
  inAny,
21
- mergeObjs,
22
20
  nuke,
23
21
  parseEvtOpts,
24
22
  setAny
25
- } from "./chunk-DP74DVRT.js";
23
+ } from "./chunk-MKL3JUPO.js";
26
24
 
27
25
  // src/ts/core/event.ts
28
26
  var ReactorEvent = class _ReactorEvent {
@@ -47,35 +45,34 @@ var ReactorEvent = class _ReactorEvent {
47
45
  staticType;
48
46
  /** Original event target context. */
49
47
  target;
50
- /** Root reactive object for this event wave. */
48
+ /** Root reactive object for this event instance wave. */
51
49
  root;
52
- /** Original target path for this event wave. */
50
+ /** Original target path for this event instance wave. */
53
51
  path;
54
52
  /** Current value at the event target path. */
55
53
  value;
56
54
  /** Previous value at the event target path. */
57
55
  oldValue;
58
- /** Whether resolve/reject intent semantics are allowed for this event. */
56
+ /** Whether resolve/reject intent semantics are allowed for this event instance. */
59
57
  rejectable;
60
- /** Whether this event wave can bubble back up to ancestors or just capture down. */
58
+ /** Whether this event instance wave can bubble back up to ancestors or just capture down. */
61
59
  bubbles;
62
60
  /**
63
- * `DOMHighResTimeStamp` for this event payload for native event parity and accuracy.
61
+ * `DOMHighResTimeStamp` for this event instance payload for native event parity and accuracy.
64
62
  * Enable `eventTimeStamps` option, then use this over custom timestamps in listeners for accuracy.
65
63
  * */
66
64
  timestamp;
67
- _warn = NOOP;
65
+ /** The `Reactor` instance that dispatched this event instance. */
66
+ reactor;
68
67
  _resolved = "";
69
68
  _rejected = "";
70
69
  _propagationStopped = false;
71
70
  _immediatePropagationStopped = false;
72
71
  /**
73
72
  * @param payload Source payload for this event instance.
74
- * @param bubbles Whether bubbling is enabled for this wave.
75
- * @param canWarn Whether warning output is enabled.
76
- * @param canStamp Whether timestamping is enabled.
73
+ * @param reactor The `Reactor` instance creating this event instance.
77
74
  */
78
- constructor(payload, bubbles = false, canStamp = false, canWarn = true) {
75
+ constructor(payload, reactor) {
79
76
  this.staticType = this.type = payload.type;
80
77
  this.target = payload.target;
81
78
  this.currentTarget = payload.currentTarget;
@@ -84,9 +81,9 @@ var ReactorEvent = class _ReactorEvent {
84
81
  this.value = payload.target.value;
85
82
  this.oldValue = payload.target.oldValue;
86
83
  this.rejectable = payload.rejectable;
87
- this.bubbles = bubbles;
88
- if (canStamp) this.timestamp = performance.now();
89
- if (canWarn) this._warn = EVT_WARN;
84
+ this.bubbles = !!reactor.config.eventBubbling;
85
+ if (reactor.config.eventTimeStamps) this.timestamp = performance.now();
86
+ this.reactor = reactor;
90
87
  }
91
88
  /** Whether propagation has been stopped. */
92
89
  get propagationStopped() {
@@ -116,9 +113,9 @@ var ReactorEvent = class _ReactorEvent {
116
113
  * @example e.resolve("API Load successful"); // message
117
114
  */
118
115
  resolve(message) {
119
- if (!this.rejectable) return this._warn(`[ReactorEvent] Ignored resolve() call on a non-rejectable ${this.staticType} at "${this.path}"`);
120
- if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`[ReactorEvent] Resolving an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
121
- if (this.rejectable) this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`;
116
+ if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`resolve()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
117
+ 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.`);
118
+ if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._resolved = message || `Could ${this.staticType} intended value at "${this.path}"`}`);
122
119
  }
123
120
  /** Rejection reason for rejectable events. */
124
121
  get rejected() {
@@ -131,9 +128,9 @@ var ReactorEvent = class _ReactorEvent {
131
128
  * @example e.resolve("User is not logged in"); // reason
132
129
  */
133
130
  reject(reason) {
134
- if (!this.rejectable) return this._warn(`[ReactorEvent] Ignored reject() call on a non-rejectable ${this.staticType} at "${this.path}"`);
135
- if (this.eventPhase !== _ReactorEvent.CAPTURING_PHASE) this._warn(`[ReactorEvent] Rejecting an intent on ${this.staticType} at "${this.path}" outside of the capture phase is unadvised.`);
136
- if (this.rejectable) this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`;
131
+ if (!this.rejectable) return this.reactor.log(`[ReactorEvent] Ignored \`reject()\` call on a non-rejectable ${this.staticType} at "${this.path}"`);
132
+ 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.`);
133
+ if (this.rejectable) this.reactor.log(`[ReactorEvent] ${this._rejected = reason || `Couldn't ${this.staticType} intended value at "${this.path}"`}`);
137
134
  }
138
135
  /**
139
136
  * Returns event path values from target to root.
@@ -142,24 +139,22 @@ var ReactorEvent = class _ReactorEvent {
142
139
  composedPath() {
143
140
  return getTrailRecords(this.root, this.path, true).map((r) => r[2]);
144
141
  }
145
- get canWarn() {
146
- return this._warn !== NOOP;
147
- }
148
142
  };
149
143
 
150
144
  // src/ts/core/reactor.ts
151
145
  var Reactor = class {
146
+ /** Logger function for this reactor instance, override if desired, `this.canLog = false` resets. */
152
147
  log = NOOP;
148
+ /** The core state object for this reactor instance. */
153
149
  core;
154
150
  // `?:`s | pay the ~800 byte price upfront for what u might never use
155
- plugins;
151
+ /** The modules being used by this reactor. */
152
+ modules;
153
+ /** Configuration options for this reactor instance. */
156
154
  config;
155
+ /** Whether this reactor instance is currently batching updates, a window view into the engine timing */
157
156
  isBatching = false;
158
157
  // Async Batching
159
- isCascading = false;
160
- // Setter Cascading
161
- isLogging = false;
162
- // keeping track so API getter doesn't slow down internal iterations in any way
163
158
  queue;
164
159
  // Tasks to run after flush
165
160
  batch;
@@ -192,7 +187,7 @@ var Reactor = class {
192
187
  if (this.config.referenceTracking && parent && key && !this.link(target, parent, key, false)) return target;
193
188
  const cached = this.proxyCache.get(target);
194
189
  if (cached) return cached;
195
- if (target[INERTIA] || !canHandle(target, this.config, false)) return target;
190
+ if (!canHandle(target, this.config, false)) return target;
196
191
  rejectable ||= target[REJECTABLE];
197
192
  indiffable ||= target[INDIFFABLE];
198
193
  const proxy = new Proxy(target, {
@@ -226,7 +221,7 @@ var Reactor = class {
226
221
  safeValue = value?.[RAW] || value;
227
222
  unchanged = this.config.equalityFunction(safeValue, safeOldValue);
228
223
  }
229
- if (!indiffable && unchanged && !this.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
224
+ if (!indiffable && unchanged && !CTX.isCascading) return this.log(`\u{1F504} [Reactor \`set\` Trap] Unchanged for "${keyStr}" on "${paths}"`), true;
230
225
  if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
231
226
  if (this.setters) {
232
227
  const wildcords = this.setters.get("*");
@@ -380,7 +375,7 @@ var Reactor = class {
380
375
  if (this.queue?.size) for (const task of this.queue) task(), this.queue.delete(task);
381
376
  }
382
377
  wave(path, payload) {
383
- const e = new ReactorEvent(payload, this.config.eventBubbling, this.config.eventTimeStamps, this.isLogging), chain = getTrailRecords(this.core, path);
378
+ const e = new ReactorEvent(payload, this), chain = getTrailRecords(this.core, path);
384
379
  e.eventPhase = ReactorEvent.CAPTURING_PHASE;
385
380
  for (let i = 0; i <= chain.length - 2; i++) {
386
381
  if (e.propagationStopped) break;
@@ -454,8 +449,8 @@ var Reactor = class {
454
449
  return depth;
455
450
  }
456
451
  getContext(path) {
457
- const lastDot = path.lastIndexOf("."), value = getAny(this.core, path), object = lastDot === -1 ? this.core : getAny(this.core, path.slice(0, lastDot));
458
- return { path, value, key: path.slice(lastDot + 1) || "", hadKey: true, object };
452
+ const last = path.lastIndexOf("."), value = getAny(this.core, path), object = last === -1 ? this.core : getAny(this.core, path.slice(0, last));
453
+ return { path, value, key: path.slice(last + 1) || "", hadKey: true, object };
459
454
  }
460
455
  bindSignal(cord, sig) {
461
456
  if (sig) sig.aborted ? cord.clup() : sig.addEventListener("abort", cord.clup, { once: true });
@@ -471,7 +466,12 @@ var Reactor = class {
471
466
  const clone = !raw ? this.config.preserveContext ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {} : obj;
472
467
  seen.set(obj, clone);
473
468
  const keys = this.config.preserveContext ? Reflect.ownKeys(obj) : Object.keys(obj);
474
- for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = this.cloned(obj[keys[i]], raw, seen);
469
+ for (let i = 0, len = keys.length; i < len; i++)
470
+ try {
471
+ clone[keys[i]] = this.cloned(obj[keys[i]], raw, seen);
472
+ } catch (e) {
473
+ if (e instanceof RangeError) throw e;
474
+ }
475
475
  if (!raw && this.config.smartCloning) this.snapCache.set(obj, clone), obj[SSVERSION] = version;
476
476
  return clone;
477
477
  }
@@ -559,7 +559,7 @@ var Reactor = class {
559
559
  * rtr.delete("cache.temp", () => TERMINATOR);
560
560
  */
561
561
  delete(path, callback, options) {
562
- return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path, void 0));
562
+ return this.syncAdd("delete", path, callback, options, (imm) => (imm !== "auto" || inAny(this.core, path)) && deleteAny(this.core, path));
563
563
  }
564
564
  /** Registers a delete mediator for a path that only triggers once. */
565
565
  donce(path, callback, options) {
@@ -576,7 +576,7 @@ var Reactor = class {
576
576
  }
577
577
  /**
578
578
  * Registers a watcher for a path.
579
- * Watch callbacks run synchronously with the operation.
579
+ * Watch callbacks run synchronously with the operation, use leaf paths for reliability as it sees exact sets; no bubbling here.
580
580
  * @param path Path or wildcard path.
581
581
  * @param callback Watch callback.
582
582
  * @param options Sync options.
@@ -625,7 +625,7 @@ var Reactor = class {
625
625
  cord = { cb: callback, capture, depth, once, clup: () => this.off(path, callback, options), lDepth: depth !== void 0 ? this.getDepth(path) : depth };
626
626
  if (immediate && (immediate !== "auto" || inAny(this.core, path))) {
627
627
  const target = this.getContext(path);
628
- callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this.config.eventBubbling, this.isLogging));
628
+ callback(new ReactorEvent({ type: "init", target, currentTarget: target, root: this.core, rejectable: false }, this));
629
629
  }
630
630
  (cords ?? (this.listeners.set(path, cords = []), cords)).push(cord);
631
631
  return this.bindSignal(cord, signal);
@@ -656,56 +656,39 @@ var Reactor = class {
656
656
  return this.cloned(arguments.length < 2 ? this.core : branch, raw);
657
657
  }
658
658
  /**
659
- * Cascades object updates into direct child paths.
660
- * @param payload Event or payload source.
661
- * @param objectSafe Merge old/new object values before cascading, don't set for arrays; merger doesn't play nice
662
- * @example
663
- * rtr.on("user", (event) => rtr.cascade(event));
664
- * @example
665
- * rtr.watch("user", (_value, payload) => rtr.cascade(payload));
666
- */
667
- cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objectSafe = true) {
668
- if (type !== "set" && type !== "delete" || !canHandle(news, this.config) || (objectSafe ? !canHandle(olds, this.config) : false)) return;
669
- const obj = objectSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
670
- this.isCascading = true;
671
- for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path === "*" ? keys[i] : path + "." + keys[i], obj[keys[i]]);
672
- this.isCascading = false;
673
- }
674
- /**
675
- * Installs a plugin instance.
676
- * @param plugin Plugin instance.
677
- * @returns Current reactor for fluent chaining.
659
+ * Installs a module instance.
660
+ * @param target Module instance.
661
+ * @param id Optional identification tag for this instance in the module.
662
+ * @returns Current `Reactor` instance for fluent chaining.
678
663
  */
679
- plugIn(plugin) {
680
- const name = plugin.constructor.plugName;
681
- this.plugins?.get(name)?.destroy();
682
- return (this.plugins ??= /* @__PURE__ */ new Map()).set(name, (plugin.setup(this), plugin)), this;
664
+ use(target, id) {
665
+ return (this.modules ??= /* @__PURE__ */ new Set()).add(target.setup(this, id)), this;
683
666
  }
684
- /** Resets the reactor to its initial state. */
667
+ /** Resets this reactor instance to its initial state. */
685
668
  reset() {
686
669
  this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear();
687
670
  this.batch?.clear(), this.queue?.clear(), this.isBatching = false;
688
671
  }
689
672
  destroy() {
690
- if (this.plugins) for (const plug of this.plugins.values()) plug.destroy();
673
+ if (this.modules) for (const mdle of this.modules) mdle.destroy();
691
674
  this.reset(), nuke(this);
692
675
  }
693
676
  get canLog() {
694
- return this.isLogging = this.log !== NOOP;
677
+ return this.log !== NOOP;
695
678
  }
696
679
  set canLog(value) {
697
- this.log = (this.isLogging = value) ? RTR_LOG : NOOP;
680
+ this.log = value ? RTR_LOG : NOOP;
698
681
  }
699
- get canTraceLineage() {
700
- return this.config.referenceTracking && !!this.config.lineageTracing;
682
+ get canLineageTrace() {
683
+ return this.config.lineageTracing && this.config.referenceTracking;
701
684
  }
702
685
  get canSmartClone() {
703
- return this.config.referenceTracking && !!this.config.smartCloning;
686
+ return this.config.smartCloning && this.config.referenceTracking;
704
687
  }
705
688
  };
706
689
 
707
690
  // src/ts/core/mixins.ts
708
- var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "cascade", "plugIn", "reset", "destroy"];
691
+ var methods = ["tick", "stall", "nostall", "get", "gonce", "noget", "set", "sonce", "noset", "delete", "donce", "nodelete", "watch", "wonce", "nowatch", "on", "once", "off", "snapshot", "use", "reset", "destroy"];
709
692
  function reactive(target, build, preferences = NIL) {
710
693
  if ("__Reactor__" in target) return target;
711
694
  const descriptors = {}, rtr = getReactor(target, true, build), locks = { enumerable: false, configurable: true, writable: false }, hasAffix = !!(preferences.prefix || preferences.suffix);
@@ -713,7 +696,7 @@ function reactive(target, build, preferences = NIL) {
713
696
  let key = methods[i];
714
697
  if (hasAffix) (preferences.whitelist?.includes(key) ?? true) && (key = `${preferences.prefix || ""}${key}${preferences.suffix || ""}`);
715
698
  else if (preferences.whitelist?.includes(key)) continue;
716
- descriptors[key] = { value: rtr[key].bind(rtr), ...locks };
699
+ descriptors[key] = { value: rtr[methods[i]].bind(rtr), ...locks };
717
700
  }
718
701
  descriptors["__Reactor__"] = { value: rtr, ...locks };
719
702
  return Object.defineProperties(rtr.core, descriptors), rtr.core;
@@ -724,7 +707,7 @@ function inert(target) {
724
707
  function live(target) {
725
708
  return delete getRaw(target)[INERTIA], target;
726
709
  }
727
- function isInert(target) {
710
+ function isInert(target = NIL) {
728
711
  return !!getRaw(target)[INERTIA];
729
712
  }
730
713
  function intent(target) {
@@ -733,7 +716,7 @@ function intent(target) {
733
716
  function state(target) {
734
717
  return delete getRaw(target)[REJECTABLE], target;
735
718
  }
736
- function isIntent(target) {
719
+ function isIntent(target = NIL) {
737
720
  return !!getRaw(target)[REJECTABLE];
738
721
  }
739
722
  function volatile(target) {
@@ -742,19 +725,19 @@ function volatile(target) {
742
725
  function stable(target) {
743
726
  return delete getRaw(target)[INDIFFABLE], target;
744
727
  }
745
- function isVolatile(target) {
728
+ function isVolatile(target = NIL) {
746
729
  return !!getRaw(target)[INDIFFABLE];
747
730
  }
748
731
  function getReactor(target, create = false, build) {
749
732
  return (target instanceof Reactor ? target : target.__Reactor__) || (create ? new Reactor(target, build) : void 0);
750
733
  }
751
- function getRaw(target) {
752
- return target?.[RAW] || target;
734
+ function getRaw(target = NIL) {
735
+ return target[RAW] || target;
753
736
  }
754
- function getVersion(target) {
737
+ function getVersion(target = NIL) {
755
738
  return getRaw(target)[VERSION] || 0;
756
739
  }
757
- function getSnapshotVersion(target) {
740
+ function getSnapshotVersion(target = NIL) {
758
741
  return getRaw(target)[SSVERSION] || 0;
759
742
  }
760
743