@pyreon/reactivity 0.23.0 → 0.24.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/lib/_chunks/reactive-devtools-BCpGoGZ5.js +280 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +16 -173
- package/lib/lpih.js +177 -0
- package/lib/types/index.d.ts +116 -2
- package/lib/types/lpih.d.ts +111 -0
- package/package.json +6 -1
- package/src/computed.ts +47 -6
- package/src/effect.ts +33 -4
- package/src/index.ts +8 -0
- package/src/lpih.ts +227 -0
- package/src/reactive-devtools.ts +213 -0
- package/src/signal.ts +23 -3
- package/src/tests/lpih-source-location.test.ts +277 -0
- package/src/tests/lpih.test.ts +351 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
//#region src/reactive-devtools.ts
|
|
2
|
+
/**
|
|
3
|
+
* Time constant for the rate1s EWMA (milliseconds). Tuned for the "hot
|
|
4
|
+
* path debugging" use case: a 1-second time constant means a burst of
|
|
5
|
+
* fires shows up immediately, then decays to 1/e (~0.37×) after one
|
|
6
|
+
* second of silence, ~5% after 3 seconds, ~0.7% after 5 seconds.
|
|
7
|
+
*
|
|
8
|
+
* @internal — exported for tests + tunability.
|
|
9
|
+
*/
|
|
10
|
+
const LPIH_RATE_TAU_MS = 1e3;
|
|
11
|
+
let _active = false;
|
|
12
|
+
let _nextId = 1;
|
|
13
|
+
const _byId = /* @__PURE__ */ new Map();
|
|
14
|
+
const _subId = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
/** @internal — finalizer callback; prunes the record when a node is GC'd. */
|
|
16
|
+
function _rdPrune(id) {
|
|
17
|
+
_byId.delete(id);
|
|
18
|
+
}
|
|
19
|
+
const _finalizer = new FinalizationRegistry(_rdPrune);
|
|
20
|
+
const FIRE_CAP = 512;
|
|
21
|
+
let _fireBuf = null;
|
|
22
|
+
let _fireCount = 0;
|
|
23
|
+
const PREVIEW_MAX = 60;
|
|
24
|
+
function preview(v) {
|
|
25
|
+
let s;
|
|
26
|
+
try {
|
|
27
|
+
if (v === null) return "null";
|
|
28
|
+
if (v === void 0) return "undefined";
|
|
29
|
+
const t = typeof v;
|
|
30
|
+
if (t === "string") s = JSON.stringify(v);
|
|
31
|
+
else if (t === "number" || t === "boolean" || t === "bigint") s = String(v);
|
|
32
|
+
else if (t === "function") s = `[Function ${v.name || "anonymous"}]`;
|
|
33
|
+
else if (t === "symbol") s = v.toString();
|
|
34
|
+
else if (Array.isArray(v)) s = `Array(${v.length})`;
|
|
35
|
+
else {
|
|
36
|
+
const ctor = v.constructor?.name;
|
|
37
|
+
let keys = [];
|
|
38
|
+
try {
|
|
39
|
+
keys = Object.keys(v).slice(0, 3);
|
|
40
|
+
} catch {
|
|
41
|
+
keys = [];
|
|
42
|
+
}
|
|
43
|
+
s = `${ctor && ctor !== "Object" ? `${ctor} ` : ""}{${keys.join(", ")}${keys.length === 3 ? ", …" : ""}}`;
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
s = "[unstringifiable]";
|
|
47
|
+
}
|
|
48
|
+
return s.length > PREVIEW_MAX ? `${s.slice(0, PREVIEW_MAX)}…` : s;
|
|
49
|
+
}
|
|
50
|
+
/** Activate the bridge. Idempotent. Called when a devtools client attaches. */
|
|
51
|
+
function activateReactiveDevtools() {
|
|
52
|
+
_active = true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Deactivate + drop all retained state. Called when the devtools client
|
|
56
|
+
* disconnects so a closed panel leaves zero residue.
|
|
57
|
+
*/
|
|
58
|
+
function deactivateReactiveDevtools() {
|
|
59
|
+
_active = false;
|
|
60
|
+
_byId.clear();
|
|
61
|
+
_fireBuf = null;
|
|
62
|
+
_fireCount = 0;
|
|
63
|
+
}
|
|
64
|
+
function isReactiveDevtoolsActive() {
|
|
65
|
+
return _active;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse the user's call site from `new Error().stack`. Returns undefined
|
|
69
|
+
* when devtools isn't active (zero-cost early-return — no Error allocated)
|
|
70
|
+
* OR when the stack format isn't recognized.
|
|
71
|
+
*
|
|
72
|
+
* `skipFrames` is the number of caller-frames to skip past _captureCallerLocation
|
|
73
|
+
* itself. The framework's hot-path callers (signal / computedLazy / effect)
|
|
74
|
+
* pass their own depth so the captured frame is the USER's call to
|
|
75
|
+
* `signal()` / `computed()` / `effect()`, not the framework's internals.
|
|
76
|
+
*
|
|
77
|
+
* Recognized stack formats:
|
|
78
|
+
* - V8 (Chrome / Node / Bun): ` at fn (file:line:col)`
|
|
79
|
+
* - V8 (anonymous): ` at file:line:col`
|
|
80
|
+
* - JSC (Safari) + SpiderMonkey: `fn@file:line:col`
|
|
81
|
+
*
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
function _captureCallerLocation(skipFrames) {
|
|
85
|
+
if (!_active) return void 0;
|
|
86
|
+
const raw = (/* @__PURE__ */ new Error()).stack;
|
|
87
|
+
if (!raw) return void 0;
|
|
88
|
+
const lines = raw.split("\n");
|
|
89
|
+
const target = lines[(lines[0] && lines[0].trim().startsWith("Error") ? 1 : 0) + 1 + skipFrames];
|
|
90
|
+
if (!target) return void 0;
|
|
91
|
+
return parseStackLine(target);
|
|
92
|
+
}
|
|
93
|
+
function parseStackLine(line) {
|
|
94
|
+
const v8Paren = line.match(/\(([^()]+):(\d+):(\d+)\)\s*$/);
|
|
95
|
+
if (v8Paren && v8Paren[1] && v8Paren[2] && v8Paren[3]) {
|
|
96
|
+
const file = v8Paren[1];
|
|
97
|
+
const lineN = Number.parseInt(v8Paren[2], 10);
|
|
98
|
+
const col = Number.parseInt(v8Paren[3], 10);
|
|
99
|
+
if (Number.isFinite(lineN) && Number.isFinite(col)) return {
|
|
100
|
+
file,
|
|
101
|
+
line: lineN,
|
|
102
|
+
col
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const v8Bare = line.match(/at\s+([^\s()]+):(\d+):(\d+)\s*$/);
|
|
106
|
+
if (v8Bare && v8Bare[1] && v8Bare[2] && v8Bare[3]) {
|
|
107
|
+
const file = v8Bare[1];
|
|
108
|
+
const lineN = Number.parseInt(v8Bare[2], 10);
|
|
109
|
+
const col = Number.parseInt(v8Bare[3], 10);
|
|
110
|
+
if (Number.isFinite(lineN) && Number.isFinite(col)) return {
|
|
111
|
+
file,
|
|
112
|
+
line: lineN,
|
|
113
|
+
col
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const jsc = line.match(/@([^@\s]+):(\d+):(\d+)\s*$/);
|
|
117
|
+
if (jsc && jsc[1] && jsc[2] && jsc[3]) {
|
|
118
|
+
const file = jsc[1];
|
|
119
|
+
const lineN = Number.parseInt(jsc[2], 10);
|
|
120
|
+
const col = Number.parseInt(jsc[3], 10);
|
|
121
|
+
if (Number.isFinite(lineN) && Number.isFinite(col)) return {
|
|
122
|
+
file,
|
|
123
|
+
line: lineN,
|
|
124
|
+
col
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Register a signal/computed/effect node. `host` is the object carrying
|
|
130
|
+
* the `_s` subscriber Set (the signal read fn itself, or a computed's
|
|
131
|
+
* internal host). `sub` is the notify closure (`recompute`/`run`) whose
|
|
132
|
+
* identity appears in upstream `_s` Sets — used to resolve edges.
|
|
133
|
+
*
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
function _rdRegister(node, kind, host, sub, label, loc) {
|
|
137
|
+
if (!_active) return void 0;
|
|
138
|
+
const id = _nextId++;
|
|
139
|
+
_byId.set(id, {
|
|
140
|
+
id,
|
|
141
|
+
kind,
|
|
142
|
+
name: label ?? `${kind === "signal" ? "signal" : kind}#${id}`,
|
|
143
|
+
ref: new WeakRef(node),
|
|
144
|
+
hostRef: host ? new WeakRef(host) : null,
|
|
145
|
+
fires: 0,
|
|
146
|
+
lastFire: null,
|
|
147
|
+
loc,
|
|
148
|
+
rate1s: 0
|
|
149
|
+
});
|
|
150
|
+
if (sub) _subId.set(sub, id);
|
|
151
|
+
_finalizer.register(node, id);
|
|
152
|
+
Object.defineProperty(node, "__pxRdId", {
|
|
153
|
+
value: id,
|
|
154
|
+
enumerable: false,
|
|
155
|
+
configurable: true
|
|
156
|
+
});
|
|
157
|
+
return id;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Record that a node fired (signal write / computed recompute / effect
|
|
161
|
+
* run). Bumps counters + appends to the bounded fire buffer.
|
|
162
|
+
*
|
|
163
|
+
* @internal
|
|
164
|
+
*/
|
|
165
|
+
function _rdRecordFire(node) {
|
|
166
|
+
if (!_active) return;
|
|
167
|
+
const id = node.__pxRdId;
|
|
168
|
+
if (id === void 0) return;
|
|
169
|
+
const rec = _byId.get(id);
|
|
170
|
+
const ts = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
171
|
+
if (rec) {
|
|
172
|
+
rec.fires++;
|
|
173
|
+
if (rec.lastFire !== null) {
|
|
174
|
+
const dt = ts - rec.lastFire;
|
|
175
|
+
const decay = Math.exp(-dt / LPIH_RATE_TAU_MS);
|
|
176
|
+
rec.rate1s = rec.rate1s * decay + 1;
|
|
177
|
+
} else rec.rate1s = 1;
|
|
178
|
+
rec.lastFire = ts;
|
|
179
|
+
}
|
|
180
|
+
if (_fireBuf === null) _fireBuf = new Array(FIRE_CAP);
|
|
181
|
+
_fireBuf[_fireCount % FIRE_CAP] = {
|
|
182
|
+
id,
|
|
183
|
+
ts
|
|
184
|
+
};
|
|
185
|
+
_fireCount++;
|
|
186
|
+
}
|
|
187
|
+
function resolveSubId(sub) {
|
|
188
|
+
const direct = sub.__pxRdId;
|
|
189
|
+
if (direct !== void 0) return direct;
|
|
190
|
+
return _subId.get(sub);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Fresh snapshot of the live reactive graph. Edges are recomputed from
|
|
194
|
+
* each live node's current subscriber Set — always consistent with the
|
|
195
|
+
* framework's real subscription state, no incremental drift.
|
|
196
|
+
*/
|
|
197
|
+
function getReactiveGraph() {
|
|
198
|
+
const nodes = [];
|
|
199
|
+
const edges = [];
|
|
200
|
+
for (const rec of _byId.values()) {
|
|
201
|
+
const node = rec.ref.deref();
|
|
202
|
+
if (!node) continue;
|
|
203
|
+
const subs = (rec.hostRef?.deref() ?? null)?._s ?? null;
|
|
204
|
+
const valueStr = rec.kind === "effect" ? "" : preview(node._v);
|
|
205
|
+
nodes.push({
|
|
206
|
+
id: rec.id,
|
|
207
|
+
kind: rec.kind,
|
|
208
|
+
name: rec.name,
|
|
209
|
+
value: valueStr,
|
|
210
|
+
subscribers: subs?.size ?? 0,
|
|
211
|
+
fires: rec.fires,
|
|
212
|
+
lastFire: rec.lastFire,
|
|
213
|
+
...rec.loc ? { loc: rec.loc } : {}
|
|
214
|
+
});
|
|
215
|
+
if (subs) for (const cb of subs) {
|
|
216
|
+
const to = resolveSubId(cb);
|
|
217
|
+
if (to !== void 0) edges.push({
|
|
218
|
+
from: rec.id,
|
|
219
|
+
to
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
nodes,
|
|
225
|
+
edges
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Aggregate fire counts by source-location — powers Live Program Inlay
|
|
230
|
+
* Hints. Walks the live node registry, keys each node by its captured
|
|
231
|
+
* `loc`, and returns one summary per unique `file:line:col`. Nodes
|
|
232
|
+
* without a captured location are skipped (their fires are still
|
|
233
|
+
* visible via `getReactiveGraph()` and `getReactiveFires()` for the
|
|
234
|
+
* existing graph / timeline surfaces).
|
|
235
|
+
*
|
|
236
|
+
* Returns a fresh array, JSON-serializable, safe to ship across the
|
|
237
|
+
* devtools-host bridge or to write into an LSP cache file.
|
|
238
|
+
*/
|
|
239
|
+
function getFireSummaries() {
|
|
240
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
241
|
+
const nowTs = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
242
|
+
for (const rec of _byId.values()) {
|
|
243
|
+
if (!rec.loc) continue;
|
|
244
|
+
if (!rec.ref.deref()) continue;
|
|
245
|
+
const k = `${rec.loc.file}:${rec.loc.line}:${rec.loc.col}`;
|
|
246
|
+
const decayedRate = rec.lastFire !== null ? rec.rate1s * Math.exp(-(nowTs - rec.lastFire) / LPIH_RATE_TAU_MS) : 0;
|
|
247
|
+
const existing = byKey.get(k);
|
|
248
|
+
if (existing) {
|
|
249
|
+
existing.count += rec.fires;
|
|
250
|
+
existing.rate1s += decayedRate;
|
|
251
|
+
if (rec.lastFire !== null && (existing.lastFire === null || rec.lastFire > existing.lastFire)) {
|
|
252
|
+
existing.lastFire = rec.lastFire;
|
|
253
|
+
existing.kind = rec.kind;
|
|
254
|
+
}
|
|
255
|
+
} else byKey.set(k, {
|
|
256
|
+
loc: rec.loc,
|
|
257
|
+
count: rec.fires,
|
|
258
|
+
lastFire: rec.lastFire,
|
|
259
|
+
kind: rec.kind,
|
|
260
|
+
rate1s: decayedRate
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return [...byKey.values()];
|
|
264
|
+
}
|
|
265
|
+
/** Bounded recent-fire timeline (oldest → newest). Fresh copy. */
|
|
266
|
+
function getReactiveFires() {
|
|
267
|
+
if (_fireBuf === null || _fireCount === 0) return [];
|
|
268
|
+
if (_fireCount <= FIRE_CAP) return _fireBuf.slice(0, _fireCount);
|
|
269
|
+
const start = _fireCount % FIRE_CAP;
|
|
270
|
+
const out = [];
|
|
271
|
+
for (let i = 0; i < FIRE_CAP; i++) {
|
|
272
|
+
const e = _fireBuf[(start + i) % FIRE_CAP];
|
|
273
|
+
if (e) out.push(e);
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
//#endregion
|
|
279
|
+
export { deactivateReactiveDevtools as a, getReactiveGraph as c, activateReactiveDevtools as i, isReactiveDevtoolsActive as l, _rdRecordFire as n, getFireSummaries as o, _rdRegister as r, getReactiveFires as s, _captureCallerLocation as t };
|
|
280
|
+
//# sourceMappingURL=reactive-devtools-BCpGoGZ5.js.map
|
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"8f0a5281-1","name":"batch.ts"},{"uid":"8f0a5281-3","name":"cell.ts"},{"uid":"8f0a5281-5","name":"scope.ts"},{"uid":"8f0a5281-7","name":"tracking.ts"},{"uid":"8f0a5281-9","name":"effect.ts"},{"uid":"8f0a5281-11","name":"computed.ts"},{"uid":"8f0a5281-13","name":"createSelector.ts"},{"uid":"8f0a5281-15","name":"debug.ts"},{"uid":"8f0a5281-17","name":"reactive-trace.ts"},{"uid":"8f0a5281-19","name":"signal.ts"},{"uid":"8f0a5281-21","name":"store.ts"},{"uid":"8f0a5281-23","name":"reconcile.ts"},{"uid":"8f0a5281-25","name":"resource.ts"},{"uid":"8f0a5281-27","name":"watch.ts"},{"uid":"8f0a5281-29","name":"index.ts"}]}]},{"name":"lpih.js","children":[{"name":"src/lpih.ts","uid":"8f0a5281-31"}]},{"name":"_chunks/reactive-devtools-BCpGoGZ5.js","children":[{"name":"src/reactive-devtools.ts","uid":"8f0a5281-33"}]}],"isRoot":true},"nodeParts":{"8f0a5281-1":{"renderedLength":3016,"gzipLength":1167,"brotliLength":0,"metaUid":"8f0a5281-0"},"8f0a5281-3":{"renderedLength":1636,"gzipLength":786,"brotliLength":0,"metaUid":"8f0a5281-2"},"8f0a5281-5":{"renderedLength":3026,"gzipLength":1226,"brotliLength":0,"metaUid":"8f0a5281-4"},"8f0a5281-7":{"renderedLength":2227,"gzipLength":858,"brotliLength":0,"metaUid":"8f0a5281-6"},"8f0a5281-9":{"renderedLength":7697,"gzipLength":2469,"brotliLength":0,"metaUid":"8f0a5281-8"},"8f0a5281-11":{"renderedLength":5143,"gzipLength":1570,"brotliLength":0,"metaUid":"8f0a5281-10"},"8f0a5281-13":{"renderedLength":2244,"gzipLength":981,"brotliLength":0,"metaUid":"8f0a5281-12"},"8f0a5281-15":{"renderedLength":2469,"gzipLength":1092,"brotliLength":0,"metaUid":"8f0a5281-14"},"8f0a5281-17":{"renderedLength":2721,"gzipLength":1363,"brotliLength":0,"metaUid":"8f0a5281-16"},"8f0a5281-19":{"renderedLength":3643,"gzipLength":1554,"brotliLength":0,"metaUid":"8f0a5281-18"},"8f0a5281-21":{"renderedLength":5232,"gzipLength":1867,"brotliLength":0,"metaUid":"8f0a5281-20"},"8f0a5281-23":{"renderedLength":2278,"gzipLength":940,"brotliLength":0,"metaUid":"8f0a5281-22"},"8f0a5281-25":{"renderedLength":1205,"gzipLength":524,"brotliLength":0,"metaUid":"8f0a5281-24"},"8f0a5281-27":{"renderedLength":1249,"gzipLength":582,"brotliLength":0,"metaUid":"8f0a5281-26"},"8f0a5281-29":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"8f0a5281-28"},"8f0a5281-31":{"renderedLength":6380,"gzipLength":2579,"brotliLength":0,"metaUid":"8f0a5281-30"},"8f0a5281-33":{"renderedLength":8588,"gzipLength":3409,"brotliLength":0,"metaUid":"8f0a5281-32"}},"nodeMetas":{"8f0a5281-0":{"id":"/src/batch.ts","moduleParts":{"index.js":"8f0a5281-1"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-18"},{"uid":"8f0a5281-6"}]},"8f0a5281-2":{"id":"/src/cell.ts","moduleParts":{"index.js":"8f0a5281-3"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-4":{"id":"/src/scope.ts","moduleParts":{"index.js":"8f0a5281-5"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-8"}]},"8f0a5281-6":{"id":"/src/tracking.ts","moduleParts":{"index.js":"8f0a5281-7"},"imported":[{"uid":"8f0a5281-0"}],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-12"},{"uid":"8f0a5281-8"},{"uid":"8f0a5281-24"},{"uid":"8f0a5281-18"}]},"8f0a5281-8":{"id":"/src/effect.ts","moduleParts":{"index.js":"8f0a5281-9"},"imported":[{"uid":"8f0a5281-32"},{"uid":"8f0a5281-4"},{"uid":"8f0a5281-6"}],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-12"},{"uid":"8f0a5281-24"},{"uid":"8f0a5281-26"}]},"8f0a5281-10":{"id":"/src/computed.ts","moduleParts":{"index.js":"8f0a5281-11"},"imported":[{"uid":"8f0a5281-0"},{"uid":"8f0a5281-8"},{"uid":"8f0a5281-32"},{"uid":"8f0a5281-4"},{"uid":"8f0a5281-6"}],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-12":{"id":"/src/createSelector.ts","moduleParts":{"index.js":"8f0a5281-13"},"imported":[{"uid":"8f0a5281-8"},{"uid":"8f0a5281-6"}],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-14":{"id":"/src/debug.ts","moduleParts":{"index.js":"8f0a5281-15"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-18"}]},"8f0a5281-16":{"id":"/src/reactive-trace.ts","moduleParts":{"index.js":"8f0a5281-17"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-18"}]},"8f0a5281-18":{"id":"/src/signal.ts","moduleParts":{"index.js":"8f0a5281-19"},"imported":[{"uid":"8f0a5281-0"},{"uid":"8f0a5281-14"},{"uid":"8f0a5281-32"},{"uid":"8f0a5281-16"},{"uid":"8f0a5281-6"}],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-24"},{"uid":"8f0a5281-20"}]},"8f0a5281-20":{"id":"/src/store.ts","moduleParts":{"index.js":"8f0a5281-21"},"imported":[{"uid":"8f0a5281-18"}],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-22"}]},"8f0a5281-22":{"id":"/src/reconcile.ts","moduleParts":{"index.js":"8f0a5281-23"},"imported":[{"uid":"8f0a5281-20"}],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-24":{"id":"/src/resource.ts","moduleParts":{"index.js":"8f0a5281-25"},"imported":[{"uid":"8f0a5281-8"},{"uid":"8f0a5281-18"},{"uid":"8f0a5281-6"}],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-26":{"id":"/src/watch.ts","moduleParts":{"index.js":"8f0a5281-27"},"imported":[{"uid":"8f0a5281-8"}],"importedBy":[{"uid":"8f0a5281-28"}]},"8f0a5281-28":{"id":"/src/index.ts","moduleParts":{"index.js":"8f0a5281-29"},"imported":[{"uid":"8f0a5281-0"},{"uid":"8f0a5281-2"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-12"},{"uid":"8f0a5281-14"},{"uid":"8f0a5281-32"},{"uid":"8f0a5281-16"},{"uid":"8f0a5281-8"},{"uid":"8f0a5281-22"},{"uid":"8f0a5281-24"},{"uid":"8f0a5281-4"},{"uid":"8f0a5281-18"},{"uid":"8f0a5281-20"},{"uid":"8f0a5281-6"},{"uid":"8f0a5281-26"}],"importedBy":[],"isEntry":true},"8f0a5281-30":{"id":"/src/lpih.ts","moduleParts":{"lpih.js":"8f0a5281-31"},"imported":[{"uid":"8f0a5281-32"},{"uid":"8f0a5281-34","dynamic":true}],"importedBy":[],"isEntry":true},"8f0a5281-32":{"id":"/src/reactive-devtools.ts","moduleParts":{"_chunks/reactive-devtools-BCpGoGZ5.js":"8f0a5281-33"},"imported":[],"importedBy":[{"uid":"8f0a5281-28"},{"uid":"8f0a5281-10"},{"uid":"8f0a5281-8"},{"uid":"8f0a5281-18"},{"uid":"8f0a5281-30"}]},"8f0a5281-34":{"id":"node:fs/promises","moduleParts":{},"imported":[],"importedBy":[{"uid":"8f0a5281-30"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { a as deactivateReactiveDevtools, c as getReactiveGraph, i as activateReactiveDevtools, l as isReactiveDevtoolsActive, n as _rdRecordFire, o as getFireSummaries, r as _rdRegister, s as getReactiveFires, t as _captureCallerLocation } from "./_chunks/reactive-devtools-BCpGoGZ5.js";
|
|
2
|
+
|
|
1
3
|
//#region src/batch.ts
|
|
2
4
|
const __DEV__ = process.env.NODE_ENV !== "production";
|
|
3
5
|
let batchDepth = 0;
|
|
@@ -149,169 +151,6 @@ function cell(value) {
|
|
|
149
151
|
return new Cell(value);
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
//#endregion
|
|
153
|
-
//#region src/reactive-devtools.ts
|
|
154
|
-
let _active = false;
|
|
155
|
-
let _nextId = 1;
|
|
156
|
-
const _byId = /* @__PURE__ */ new Map();
|
|
157
|
-
const _subId = /* @__PURE__ */ new WeakMap();
|
|
158
|
-
/** @internal — finalizer callback; prunes the record when a node is GC'd. */
|
|
159
|
-
function _rdPrune(id) {
|
|
160
|
-
_byId.delete(id);
|
|
161
|
-
}
|
|
162
|
-
const _finalizer = new FinalizationRegistry(_rdPrune);
|
|
163
|
-
const FIRE_CAP = 512;
|
|
164
|
-
let _fireBuf = null;
|
|
165
|
-
let _fireCount = 0;
|
|
166
|
-
const PREVIEW_MAX$1 = 60;
|
|
167
|
-
function preview$1(v) {
|
|
168
|
-
let s;
|
|
169
|
-
try {
|
|
170
|
-
if (v === null) return "null";
|
|
171
|
-
if (v === void 0) return "undefined";
|
|
172
|
-
const t = typeof v;
|
|
173
|
-
if (t === "string") s = JSON.stringify(v);
|
|
174
|
-
else if (t === "number" || t === "boolean" || t === "bigint") s = String(v);
|
|
175
|
-
else if (t === "function") s = `[Function ${v.name || "anonymous"}]`;
|
|
176
|
-
else if (t === "symbol") s = v.toString();
|
|
177
|
-
else if (Array.isArray(v)) s = `Array(${v.length})`;
|
|
178
|
-
else {
|
|
179
|
-
const ctor = v.constructor?.name;
|
|
180
|
-
let keys = [];
|
|
181
|
-
try {
|
|
182
|
-
keys = Object.keys(v).slice(0, 3);
|
|
183
|
-
} catch {
|
|
184
|
-
keys = [];
|
|
185
|
-
}
|
|
186
|
-
s = `${ctor && ctor !== "Object" ? `${ctor} ` : ""}{${keys.join(", ")}${keys.length === 3 ? ", …" : ""}}`;
|
|
187
|
-
}
|
|
188
|
-
} catch {
|
|
189
|
-
s = "[unstringifiable]";
|
|
190
|
-
}
|
|
191
|
-
return s.length > PREVIEW_MAX$1 ? `${s.slice(0, PREVIEW_MAX$1)}…` : s;
|
|
192
|
-
}
|
|
193
|
-
/** Activate the bridge. Idempotent. Called when a devtools client attaches. */
|
|
194
|
-
function activateReactiveDevtools() {
|
|
195
|
-
_active = true;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Deactivate + drop all retained state. Called when the devtools client
|
|
199
|
-
* disconnects so a closed panel leaves zero residue.
|
|
200
|
-
*/
|
|
201
|
-
function deactivateReactiveDevtools() {
|
|
202
|
-
_active = false;
|
|
203
|
-
_byId.clear();
|
|
204
|
-
_fireBuf = null;
|
|
205
|
-
_fireCount = 0;
|
|
206
|
-
}
|
|
207
|
-
function isReactiveDevtoolsActive() {
|
|
208
|
-
return _active;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Register a signal/computed/effect node. `host` is the object carrying
|
|
212
|
-
* the `_s` subscriber Set (the signal read fn itself, or a computed's
|
|
213
|
-
* internal host). `sub` is the notify closure (`recompute`/`run`) whose
|
|
214
|
-
* identity appears in upstream `_s` Sets — used to resolve edges.
|
|
215
|
-
*
|
|
216
|
-
* @internal
|
|
217
|
-
*/
|
|
218
|
-
function _rdRegister(node, kind, host, sub, label) {
|
|
219
|
-
if (!_active) return void 0;
|
|
220
|
-
const id = _nextId++;
|
|
221
|
-
_byId.set(id, {
|
|
222
|
-
id,
|
|
223
|
-
kind,
|
|
224
|
-
name: label ?? `${kind === "signal" ? "signal" : kind}#${id}`,
|
|
225
|
-
ref: new WeakRef(node),
|
|
226
|
-
hostRef: host ? new WeakRef(host) : null,
|
|
227
|
-
fires: 0,
|
|
228
|
-
lastFire: null
|
|
229
|
-
});
|
|
230
|
-
if (sub) _subId.set(sub, id);
|
|
231
|
-
_finalizer.register(node, id);
|
|
232
|
-
Object.defineProperty(node, "__pxRdId", {
|
|
233
|
-
value: id,
|
|
234
|
-
enumerable: false,
|
|
235
|
-
configurable: true
|
|
236
|
-
});
|
|
237
|
-
return id;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Record that a node fired (signal write / computed recompute / effect
|
|
241
|
-
* run). Bumps counters + appends to the bounded fire buffer.
|
|
242
|
-
*
|
|
243
|
-
* @internal
|
|
244
|
-
*/
|
|
245
|
-
function _rdRecordFire(node) {
|
|
246
|
-
if (!_active) return;
|
|
247
|
-
const id = node.__pxRdId;
|
|
248
|
-
if (id === void 0) return;
|
|
249
|
-
const rec = _byId.get(id);
|
|
250
|
-
const ts = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
251
|
-
if (rec) {
|
|
252
|
-
rec.fires++;
|
|
253
|
-
rec.lastFire = ts;
|
|
254
|
-
}
|
|
255
|
-
if (_fireBuf === null) _fireBuf = new Array(FIRE_CAP);
|
|
256
|
-
_fireBuf[_fireCount % FIRE_CAP] = {
|
|
257
|
-
id,
|
|
258
|
-
ts
|
|
259
|
-
};
|
|
260
|
-
_fireCount++;
|
|
261
|
-
}
|
|
262
|
-
function resolveSubId(sub) {
|
|
263
|
-
const direct = sub.__pxRdId;
|
|
264
|
-
if (direct !== void 0) return direct;
|
|
265
|
-
return _subId.get(sub);
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Fresh snapshot of the live reactive graph. Edges are recomputed from
|
|
269
|
-
* each live node's current subscriber Set — always consistent with the
|
|
270
|
-
* framework's real subscription state, no incremental drift.
|
|
271
|
-
*/
|
|
272
|
-
function getReactiveGraph() {
|
|
273
|
-
const nodes = [];
|
|
274
|
-
const edges = [];
|
|
275
|
-
for (const rec of _byId.values()) {
|
|
276
|
-
const node = rec.ref.deref();
|
|
277
|
-
if (!node) continue;
|
|
278
|
-
const subs = (rec.hostRef?.deref() ?? null)?._s ?? null;
|
|
279
|
-
const valueStr = rec.kind === "effect" ? "" : preview$1(node._v);
|
|
280
|
-
nodes.push({
|
|
281
|
-
id: rec.id,
|
|
282
|
-
kind: rec.kind,
|
|
283
|
-
name: rec.name,
|
|
284
|
-
value: valueStr,
|
|
285
|
-
subscribers: subs?.size ?? 0,
|
|
286
|
-
fires: rec.fires,
|
|
287
|
-
lastFire: rec.lastFire
|
|
288
|
-
});
|
|
289
|
-
if (subs) for (const cb of subs) {
|
|
290
|
-
const to = resolveSubId(cb);
|
|
291
|
-
if (to !== void 0) edges.push({
|
|
292
|
-
from: rec.id,
|
|
293
|
-
to
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
nodes,
|
|
299
|
-
edges
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
/** Bounded recent-fire timeline (oldest → newest). Fresh copy. */
|
|
303
|
-
function getReactiveFires() {
|
|
304
|
-
if (_fireBuf === null || _fireCount === 0) return [];
|
|
305
|
-
if (_fireCount <= FIRE_CAP) return _fireBuf.slice(0, _fireCount);
|
|
306
|
-
const start = _fireCount % FIRE_CAP;
|
|
307
|
-
const out = [];
|
|
308
|
-
for (let i = 0; i < FIRE_CAP; i++) {
|
|
309
|
-
const e = _fireBuf[(start + i) % FIRE_CAP];
|
|
310
|
-
if (e) out.push(e);
|
|
311
|
-
}
|
|
312
|
-
return out;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
154
|
//#endregion
|
|
316
155
|
//#region src/scope.ts
|
|
317
156
|
var EffectScope = class {
|
|
@@ -560,7 +399,7 @@ function cleanupLocalDeps$1(deps, fn) {
|
|
|
560
399
|
deps.length = 0;
|
|
561
400
|
}
|
|
562
401
|
}
|
|
563
|
-
function effect(fn) {
|
|
402
|
+
function effect(fn, options) {
|
|
564
403
|
if (process.env.NODE_ENV !== "production") {
|
|
565
404
|
if (fn.constructor && fn.constructor.name === "AsyncFunction") console.warn("[pyreon] effect() received an async function. Signal reads after the first `await` are NOT tracked — only the synchronous prefix is. Read every tracked signal BEFORE any await, or split into separate effects, or use `watch(source, asyncCb)` for async-in-callback patterns.");
|
|
566
405
|
}
|
|
@@ -628,7 +467,7 @@ function effect(fn) {
|
|
|
628
467
|
if (!isFirstRun) scope?.notifyEffectRan();
|
|
629
468
|
isFirstRun = false;
|
|
630
469
|
};
|
|
631
|
-
if (process.env.NODE_ENV !== "production") _rdRegister(run, "effect", null, run, void 0);
|
|
470
|
+
if (process.env.NODE_ENV !== "production") _rdRegister(run, "effect", null, run, void 0, options?.__sourceLocation ?? _captureCallerLocation(1));
|
|
632
471
|
run();
|
|
633
472
|
const e = { dispose() {
|
|
634
473
|
runCleanup();
|
|
@@ -727,7 +566,7 @@ function renderEffect(fn) {
|
|
|
727
566
|
}
|
|
728
567
|
} else renderEffectFullTrack(deps, run, trackedFn);
|
|
729
568
|
};
|
|
730
|
-
if (process.env.NODE_ENV !== "production") _rdRegister(run, "effect", null, run, void 0);
|
|
569
|
+
if (process.env.NODE_ENV !== "production") _rdRegister(run, "effect", null, run, void 0, _captureCallerLocation(1));
|
|
731
570
|
run();
|
|
732
571
|
const dispose = () => {
|
|
733
572
|
if (disposed) return;
|
|
@@ -759,7 +598,8 @@ function computed(fn, options) {
|
|
|
759
598
|
if (process.env.NODE_ENV !== "production") {
|
|
760
599
|
if (fn.constructor && fn.constructor.name === "AsyncFunction") console.warn("[pyreon] computed() received an async function. The result type becomes `Computed<Promise<T>>`, and signal reads after the first `await` are NOT tracked. Use `createResource` for async-derived state, or compute synchronously over a signal that holds the awaited value.");
|
|
761
600
|
}
|
|
762
|
-
|
|
601
|
+
const loc = options?.__sourceLocation;
|
|
602
|
+
return options?.equals ? computedWithEquals(fn, options.equals, loc) : computedLazy(fn, loc);
|
|
763
603
|
}
|
|
764
604
|
/**
|
|
765
605
|
* Default computed — lazy evaluation with deferred cleanup.
|
|
@@ -771,7 +611,7 @@ function computed(fn, options) {
|
|
|
771
611
|
* in diamond patterns (a→b,c→d: b notifies d, c tries to notify d again —
|
|
772
612
|
* skipped because d is already dirty).
|
|
773
613
|
*/
|
|
774
|
-
function computedLazy(fn) {
|
|
614
|
+
function computedLazy(fn, injectedLoc) {
|
|
775
615
|
let value;
|
|
776
616
|
let dirty = true;
|
|
777
617
|
let disposed = false;
|
|
@@ -832,7 +672,7 @@ function computedLazy(fn) {
|
|
|
832
672
|
set.delete(updater);
|
|
833
673
|
};
|
|
834
674
|
};
|
|
835
|
-
if (process.env.NODE_ENV !== "production") _rdRegister(read, "derived", host, recompute, void 0);
|
|
675
|
+
if (process.env.NODE_ENV !== "production") _rdRegister(read, "derived", host, recompute, void 0, injectedLoc ?? _captureCallerLocation(2));
|
|
836
676
|
getCurrentScope()?.add({ dispose: read.dispose });
|
|
837
677
|
return read;
|
|
838
678
|
}
|
|
@@ -842,7 +682,7 @@ function computedLazy(fn) {
|
|
|
842
682
|
* Re-evaluates immediately when deps change and only notifies downstream
|
|
843
683
|
* if `equals(prev, next)` returns false.
|
|
844
684
|
*/
|
|
845
|
-
function computedWithEquals(fn, equals) {
|
|
685
|
+
function computedWithEquals(fn, equals, injectedLoc) {
|
|
846
686
|
let value;
|
|
847
687
|
let dirty = true;
|
|
848
688
|
let initialized = false;
|
|
@@ -910,7 +750,7 @@ function computedWithEquals(fn, equals) {
|
|
|
910
750
|
set.delete(updater);
|
|
911
751
|
};
|
|
912
752
|
};
|
|
913
|
-
if (process.env.NODE_ENV !== "production") _rdRegister(read, "derived", host, recompute, void 0);
|
|
753
|
+
if (process.env.NODE_ENV !== "production") _rdRegister(read, "derived", host, recompute, void 0, injectedLoc ?? _captureCallerLocation(2));
|
|
914
754
|
getCurrentScope()?.add({ dispose: read.dispose });
|
|
915
755
|
return read;
|
|
916
756
|
}
|
|
@@ -1275,7 +1115,10 @@ function signal(initialValue, options) {
|
|
|
1275
1115
|
read.direct = _directFn;
|
|
1276
1116
|
read.debug = _debug;
|
|
1277
1117
|
read.label = options?.name;
|
|
1278
|
-
if (process.env.NODE_ENV !== "production")
|
|
1118
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1119
|
+
const loc = options?.__sourceLocation ? options.__sourceLocation : _captureCallerLocation(1);
|
|
1120
|
+
_rdRegister(read, "signal", read, null, read.label, loc);
|
|
1121
|
+
}
|
|
1279
1122
|
return read;
|
|
1280
1123
|
}
|
|
1281
1124
|
|
|
@@ -1596,5 +1439,5 @@ function watch(source, callback, opts = {}) {
|
|
|
1596
1439
|
}
|
|
1597
1440
|
|
|
1598
1441
|
//#endregion
|
|
1599
|
-
export { Cell, EffectScope, _bind, activateReactiveDevtools, batch, cell, clearReactiveTrace, computed, createResource, createSelector, createStore, deactivateReactiveDevtools, effect, effectScope, getCurrentScope, getReactiveFires, getReactiveGraph, getReactiveTrace, inspectSignal, isReactiveDevtoolsActive, isStore, markRaw, nextTick, onCleanup, onScopeDispose, onSignalUpdate, reconcile, renderEffect, runUntracked, runUntracked as untrack, setCurrentScope, setErrorHandler, setSnapshotCapture, shallowReactive, signal, watch, why };
|
|
1442
|
+
export { Cell, EffectScope, _bind, activateReactiveDevtools, batch, cell, clearReactiveTrace, computed, createResource, createSelector, createStore, deactivateReactiveDevtools, effect, effectScope, getCurrentScope, getFireSummaries, getReactiveFires, getReactiveGraph, getReactiveTrace, inspectSignal, isReactiveDevtoolsActive, isStore, markRaw, nextTick, onCleanup, onScopeDispose, onSignalUpdate, reconcile, renderEffect, runUntracked, runUntracked as untrack, setCurrentScope, setErrorHandler, setSnapshotCapture, shallowReactive, signal, watch, why };
|
|
1600
1443
|
//# sourceMappingURL=index.js.map
|