sia-reactor 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/dist/chunk-JTRN6O5Y.js +165 -0
- package/dist/index-DBfVdpnb.d.cts +498 -0
- package/dist/index-DBfVdpnb.d.ts +498 -0
- package/dist/index.cjs +812 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +662 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +24 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +0 -0
- package/dist/utils.cjs +224 -0
- package/dist/utils.d.cts +11 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +52 -0
- package/package.json +67 -0
package/dist/index.d.cts
ADDED
|
@@ -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';
|
package/dist/index.d.ts
ADDED
|
@@ -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);
|