@fluixi/devtools 0.2.0-alpha.7 → 1.0.0-alpha.101
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 +1 -1
- package/README.md +12 -12
- package/dist/callsite.cjs +8 -1
- package/dist/callsite.d.ts.map +1 -1
- package/dist/callsite.js +13 -2
- package/dist/callsite.mjs +8 -1
- package/dist/diff.cjs +2 -0
- package/dist/diff.js +1 -1
- package/dist/diff.mjs +2 -0
- package/dist/export.cjs +2 -0
- package/dist/export.mjs +2 -0
- package/dist/format.cjs +2 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +6 -1
- package/dist/format.mjs +2 -0
- package/dist/hook.cjs +31 -14
- package/dist/hook.mjs +31 -14
- package/dist/index.cjs +32 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -17
- package/dist/index.mjs +32 -15
- package/dist/inspector.cjs +2 -0
- package/dist/inspector.mjs +2 -0
- package/dist/instrument.cjs +2 -0
- package/dist/instrument.mjs +2 -0
- package/dist/observe.cjs +32 -14
- package/dist/observe.d.ts +7 -0
- package/dist/observe.d.ts.map +1 -1
- package/dist/observe.js +33 -17
- package/dist/observe.mjs +32 -14
- package/dist/path.js +1 -1
- package/dist/plugins/index.cjs +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/wire.cjs +1 -1
- package/dist/wire.mjs +1 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @fluixi/devtools
|
|
2
2
|
|
|
3
3
|
Live inspector for the Fluixi reactive graph. Instrument `signal` / `memo` / `effect`, then render
|
|
4
|
-
the dependency graph and watch propagation animate
|
|
4
|
+
the dependency graph and watch propagation animate: State → Memo → Effect, glitch-free, lazy.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
7
|
import { createReactor, createGraphView, DEVTOOLS_CSS } from '@fluixi/devtools';
|
|
@@ -23,13 +23,13 @@ tick();
|
|
|
23
23
|
|
|
24
24
|
The instrumented `createSignal`/`createMemo`/`createEffect` wrap the real `@fluixi/reactive`
|
|
25
25
|
primitives. A read is a
|
|
26
|
-
subscription, so edges are recorded from actual reads
|
|
26
|
+
subscription, so edges are recorded from actual reads: it's the genuine dependency graph, not a
|
|
27
27
|
reconstruction. Values are re-read untracked each frame and effects count their own re-runs, so the
|
|
28
28
|
inspector animates exactly as the core propagates.
|
|
29
29
|
|
|
30
30
|
## Watching an app you did not write
|
|
31
31
|
|
|
32
|
-
createReactor hands out its own primitives, so it only sees code written against them. observeGraph() sees an ordinary app: the runtime reports each node as it is made, and the graph fills from plain `import { signal } from '@fluixi/core'`. Call it before the app's modules load
|
|
32
|
+
createReactor hands out its own primitives, so it only sees code written against them. observeGraph() sees an ordinary app: the runtime reports each node as it is made, and the graph fills from plain `import { signal } from '@fluixi/core'`. Call it before the app's modules load: a node made earlier is not in the graph.
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
import { observeGraph, createGraphView } from '@fluixi/devtools';
|
|
@@ -42,13 +42,13 @@ tick();
|
|
|
42
42
|
|
|
43
43
|
The same Map<number, RNode> the reactor produces, so createGraphView and anything else reading the graph does not care which made it. Dependencies are read off the core node, which already keeps its own sources and observers, so nothing is wrapped and no read is intercepted.
|
|
44
44
|
|
|
45
|
-
No write edges, because nothing records which computation was running when a setter fired. Render effects are left out
|
|
45
|
+
No write edges, because nothing records which computation was running when a setter fired. Render effects are left out: the dom layer makes one per binding. An unwatched runtime pays one null check per node creation and per recompute.
|
|
46
46
|
|
|
47
47
|
## Getting the graph somewhere else
|
|
48
48
|
|
|
49
|
-
serializeGraph flattens the graph to json
|
|
49
|
+
serializeGraph flattens the graph to json, Sets become arrays, the live core node becomes version and state, the reader is dropped, and snapshotToRenderMap turns it back into something the view draws. sample() returns what moved, so a delta is the same call with those ids, and applySnapshot folds one into a graph already on screen.
|
|
50
50
|
|
|
51
|
-
installDevtoolsHook() is for a tool that cannot be imported by the app. It leaves a global behind; when the application's runtime loads it finds it and hands over its own registration function, so the graph is built from the app's copy of the framework rather than the tool's. It has to be installed before the app runs
|
|
51
|
+
installDevtoolsHook() is for a tool that cannot be imported by the app. It leaves a global behind; when the application's runtime loads it finds it and hands over its own registration function, so the graph is built from the app's copy of the framework rather than the tool's. It has to be installed before the app runs, a browser agent at document_start, because the runtime offers once. It works against a production build too, without names or locations.
|
|
52
52
|
|
|
53
53
|
```js
|
|
54
54
|
import { installDevtoolsHook } from '@fluixi/devtools';
|
|
@@ -72,7 +72,7 @@ globalThis.__fx_source?.("src/App.ts", 10, 15, "count");
|
|
|
72
72
|
const count = signal(0);
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
A renderer needs nothing else
|
|
75
|
+
A renderer needs nothing else, no compiler, no source file, no ast, and file, line and column are enough for a host to decide how to open it. For example:
|
|
76
76
|
|
|
77
77
|
```ts
|
|
78
78
|
for (const node of reactor.graph.values()) {
|
|
@@ -82,9 +82,9 @@ for (const node of reactor.graph.values()) {
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
The compiler wraps every component in a memo, so the graph sees the wrapper rather than the component. A node whose value carries a component's name reports its kind as `component` and takes that name as its label, so the graph reads `App` rather than `m5`. Nothing about the node changes
|
|
85
|
+
The compiler wraps every component in a memo, so the graph sees the wrapper rather than the component. A node whose value carries a component's name reports its kind as `component` and takes that name as its label, so the graph reads `App` rather than `m5`. Nothing about the node changes: it is the same memo, with the same dependencies.
|
|
86
86
|
|
|
87
|
-
A component node's `source` is where its tag was written, which is where the node was made
|
|
87
|
+
A component node's `source` is where its tag was written, which is where the node was made: the same meaning `source` has for every other kind. `declaredAt` is where the component itself was declared, which is usually another file. It stands in for `source` when nothing marked the tag, and two components sharing a name are reported without a location rather than with the wrong one.
|
|
88
88
|
|
|
89
89
|
### Making a node the compiler cannot see
|
|
90
90
|
|
|
@@ -105,7 +105,7 @@ A production build should leave `sourceLocations` off and then carries none of i
|
|
|
105
105
|
- `observeGraph()` → `{ graph, sample(), detach() }`
|
|
106
106
|
- `installDevtoolsHook()` → the global a tool attaches through
|
|
107
107
|
- `serializeGraph(graph, now?, { ids, removed }?)` / `snapshotToRenderMap(snapshot)`
|
|
108
|
-
- `applySnapshot(graph, snapshot)
|
|
109
|
-
- `formatValue(value, limit?)
|
|
108
|
+
- `applySnapshot(graph, snapshot)`: fold a full snapshot or a delta into what is drawn
|
|
109
|
+
- `formatValue(value, limit?)`: a printable rendering of any value; never throws
|
|
110
110
|
- `createGraphView(svg)` → `{ update(graph, now?), clear() }`
|
|
111
|
-
- `DEVTOOLS_CSS
|
|
111
|
+
- `DEVTOOLS_CSS`: default theme string (override the `fx-*` classes to restyle)
|
package/dist/callsite.cjs
CHANGED
|
@@ -77,5 +77,12 @@ function callSite(stack, origin) {
|
|
|
77
77
|
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
78
78
|
}
|
|
79
79
|
function currentCallSite(origin) {
|
|
80
|
-
|
|
80
|
+
const errors = Error;
|
|
81
|
+
const limit = errors.stackTraceLimit;
|
|
82
|
+
errors.stackTraceLimit = 50;
|
|
83
|
+
try {
|
|
84
|
+
return callSite(new Error().stack, origin);
|
|
85
|
+
} finally {
|
|
86
|
+
errors.stackTraceLimit = limit;
|
|
87
|
+
}
|
|
81
88
|
}
|
package/dist/callsite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callsite.d.ts","sourceRoot":"","sources":["../src/callsite.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AA8D1D;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAgBrE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"callsite.d.ts","sourceRoot":"","sources":["../src/callsite.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AA8D1D;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAgBrE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAYrE"}
|
package/dist/callsite.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* A browser extension's own bundle.
|
|
3
3
|
*
|
|
4
4
|
* The devtools agent carries a copy of this package, so every frame inside it looked like
|
|
5
|
-
* application code and every node reported `chrome-extension
|
|
5
|
+
* application code and every node reported `chrome-extension://.../agent.js` as its origin.
|
|
6
6
|
* Nothing under these schemes is ever the page's own source.
|
|
7
7
|
*/
|
|
8
8
|
const EXTENSION = ['chrome-extension://', 'moz-extension://', 'safari-web-extension://', 'extension://'];
|
|
@@ -87,5 +87,16 @@ export function callSite(stack, origin) {
|
|
|
87
87
|
* the frames of the creating call are still on the stack.
|
|
88
88
|
*/
|
|
89
89
|
export function currentCallSite(origin) {
|
|
90
|
-
|
|
90
|
+
// Deeper than the default ten frames: the reporting machinery and the framework's own calls
|
|
91
|
+
// can fill those, and the application's frame is the one past them.
|
|
92
|
+
// A V8 extension, absent from the DOM types; elsewhere it is an ignored property.
|
|
93
|
+
const errors = Error;
|
|
94
|
+
const limit = errors.stackTraceLimit;
|
|
95
|
+
errors.stackTraceLimit = 50;
|
|
96
|
+
try {
|
|
97
|
+
return callSite(new Error().stack, origin);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
errors.stackTraceLimit = limit;
|
|
101
|
+
}
|
|
91
102
|
}
|
package/dist/callsite.mjs
CHANGED
|
@@ -52,7 +52,14 @@ function callSite(stack, origin) {
|
|
|
52
52
|
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
53
53
|
}
|
|
54
54
|
function currentCallSite(origin) {
|
|
55
|
-
|
|
55
|
+
const errors = Error;
|
|
56
|
+
const limit = errors.stackTraceLimit;
|
|
57
|
+
errors.stackTraceLimit = 50;
|
|
58
|
+
try {
|
|
59
|
+
return callSite(new Error().stack, origin);
|
|
60
|
+
} finally {
|
|
61
|
+
errors.stackTraceLimit = limit;
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
export {
|
|
58
65
|
callSite,
|
package/dist/diff.cjs
CHANGED
|
@@ -106,6 +106,8 @@ function walk(value, depth, seen) {
|
|
|
106
106
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
107
107
|
let read;
|
|
108
108
|
try {
|
|
109
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
110
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
109
111
|
read = walk(object[key], depth + 1, seen);
|
|
110
112
|
} catch {
|
|
111
113
|
read = "[unreadable]";
|
package/dist/diff.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// What changed, rather than what the value is now.
|
|
2
2
|
//
|
|
3
|
-
// `todos [{
|
|
3
|
+
// `todos [{...}] → [{...}, {...}]` says nothing the graph did not. "One row added at index 2, here
|
|
4
4
|
// it is" does. Computed at the write, while both sides are still in hand, afterwards only
|
|
5
5
|
// the new value exists.
|
|
6
6
|
import { formatValue } from './format.js';
|
package/dist/diff.mjs
CHANGED
|
@@ -80,6 +80,8 @@ function walk(value, depth, seen) {
|
|
|
80
80
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
81
81
|
let read;
|
|
82
82
|
try {
|
|
83
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
84
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
83
85
|
read = walk(object[key], depth + 1, seen);
|
|
84
86
|
} catch {
|
|
85
87
|
read = "[unreadable]";
|
package/dist/export.cjs
CHANGED
|
@@ -122,6 +122,8 @@ function walk(value, depth, seen) {
|
|
|
122
122
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
123
123
|
let read;
|
|
124
124
|
try {
|
|
125
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
126
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
125
127
|
read = walk(object[key], depth + 1, seen);
|
|
126
128
|
} catch {
|
|
127
129
|
read = "[unreadable]";
|
package/dist/export.mjs
CHANGED
|
@@ -106,6 +106,8 @@ function walk(value, depth, seen) {
|
|
|
106
106
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
107
107
|
let read;
|
|
108
108
|
try {
|
|
109
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
110
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
109
111
|
read = walk(object[key], depth + 1, seen);
|
|
110
112
|
} catch {
|
|
111
113
|
read = "[unreadable]";
|
package/dist/format.cjs
CHANGED
|
@@ -106,6 +106,8 @@ function walk(value, depth, seen) {
|
|
|
106
106
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
107
107
|
let read;
|
|
108
108
|
try {
|
|
109
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
110
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
109
111
|
read = walk(object[key], depth + 1, seen);
|
|
110
112
|
} catch {
|
|
111
113
|
read = "[unreadable]";
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAwHA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAM,GAAG,MAAM,CAQ/D;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAO,GAAG,MAAM,CA4C5D"}
|
package/dist/format.js
CHANGED
|
@@ -98,9 +98,14 @@ function walk(value, depth, seen) {
|
|
|
98
98
|
}
|
|
99
99
|
const keys = Object.keys(object);
|
|
100
100
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
101
|
-
// A getter
|
|
101
|
+
// A getter is shown, never called: on a component's props it builds the content
|
|
102
|
+
// (a `children` getter makes fresh nodes per read) and reads signals, so printing it
|
|
103
|
+
// ran the component's work again on every sample. A proxy can still refuse.
|
|
102
104
|
let read;
|
|
103
105
|
try {
|
|
106
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
107
|
+
if (descriptor?.get)
|
|
108
|
+
return `${key}: [getter]`;
|
|
104
109
|
read = walk(object[key], depth + 1, seen);
|
|
105
110
|
}
|
|
106
111
|
catch {
|
package/dist/format.mjs
CHANGED
|
@@ -80,6 +80,8 @@ function walk(value, depth, seen) {
|
|
|
80
80
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
81
81
|
let read;
|
|
82
82
|
try {
|
|
83
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
84
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
83
85
|
read = walk(object[key], depth + 1, seen);
|
|
84
86
|
} catch {
|
|
85
87
|
read = "[unreadable]";
|
package/dist/hook.cjs
CHANGED
|
@@ -352,7 +352,14 @@ function callSite(stack, origin) {
|
|
|
352
352
|
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
353
353
|
}
|
|
354
354
|
function currentCallSite(origin) {
|
|
355
|
-
|
|
355
|
+
const errors = Error;
|
|
356
|
+
const limit = errors.stackTraceLimit;
|
|
357
|
+
errors.stackTraceLimit = 50;
|
|
358
|
+
try {
|
|
359
|
+
return callSite(new Error().stack, origin);
|
|
360
|
+
} finally {
|
|
361
|
+
errors.stackTraceLimit = limit;
|
|
362
|
+
}
|
|
356
363
|
}
|
|
357
364
|
|
|
358
365
|
// src/instrument.ts
|
|
@@ -441,6 +448,8 @@ function walk(value, depth, seen) {
|
|
|
441
448
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
442
449
|
let read;
|
|
443
450
|
try {
|
|
451
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
452
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
444
453
|
read = walk(object[key], depth + 1, seen);
|
|
445
454
|
} catch {
|
|
446
455
|
read = "[unreadable]";
|
|
@@ -877,7 +886,7 @@ function observeGraph(options = {}) {
|
|
|
877
886
|
const claims = (node, value) => {
|
|
878
887
|
for (const dep of node.deps) {
|
|
879
888
|
const other = graph.get(dep);
|
|
880
|
-
if (other?.read && other
|
|
889
|
+
if (other?.read && readRaw(other) === value) return false;
|
|
881
890
|
}
|
|
882
891
|
return true;
|
|
883
892
|
};
|
|
@@ -895,6 +904,7 @@ function observeGraph(options = {}) {
|
|
|
895
904
|
const scopeOfTarget = /* @__PURE__ */ new WeakMap();
|
|
896
905
|
const fieldsOf = /* @__PURE__ */ new WeakMap();
|
|
897
906
|
let described = null;
|
|
907
|
+
let descendants = null;
|
|
898
908
|
const rendered = /* @__PURE__ */ new Map();
|
|
899
909
|
const elementsIn = (value) => {
|
|
900
910
|
if (value instanceof Element) return [value];
|
|
@@ -1256,6 +1266,7 @@ function observeGraph(options = {}) {
|
|
|
1256
1266
|
graph,
|
|
1257
1267
|
sample() {
|
|
1258
1268
|
described = null;
|
|
1269
|
+
descendants = null;
|
|
1259
1270
|
const changed = [];
|
|
1260
1271
|
const moved = (id) => {
|
|
1261
1272
|
changed.push(id);
|
|
@@ -1311,7 +1322,7 @@ function observeGraph(options = {}) {
|
|
|
1311
1322
|
runsAtLastSample.set(node.id, node.runs);
|
|
1312
1323
|
continue;
|
|
1313
1324
|
}
|
|
1314
|
-
const raw = node
|
|
1325
|
+
const raw = readRaw(node);
|
|
1315
1326
|
if (recording) raws.set(node.id, raw);
|
|
1316
1327
|
edges(node.id, node);
|
|
1317
1328
|
const stamped = raw?.$name;
|
|
@@ -1386,17 +1397,8 @@ function observeGraph(options = {}) {
|
|
|
1386
1397
|
if (live.length) return live;
|
|
1387
1398
|
collect(id);
|
|
1388
1399
|
if (!live.length) {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
let at = node.createdIn;
|
|
1392
|
-
while (at !== void 0 && !seen.has(at)) {
|
|
1393
|
-
if (at === id) return true;
|
|
1394
|
-
seen.add(at);
|
|
1395
|
-
at = graph.get(at)?.createdIn;
|
|
1396
|
-
}
|
|
1397
|
-
return false;
|
|
1398
|
-
};
|
|
1399
|
-
for (const node of graph.values()) if (inside(node)) collect(node.id);
|
|
1400
|
+
descendants ??= descendantsByCreator(graph);
|
|
1401
|
+
for (const below of descendants.get(id) ?? []) collect(below);
|
|
1400
1402
|
}
|
|
1401
1403
|
return live;
|
|
1402
1404
|
},
|
|
@@ -1509,6 +1511,21 @@ function observeGraph(options = {}) {
|
|
|
1509
1511
|
}
|
|
1510
1512
|
return api;
|
|
1511
1513
|
}
|
|
1514
|
+
function descendantsByCreator(graph) {
|
|
1515
|
+
const out = /* @__PURE__ */ new Map();
|
|
1516
|
+
for (const node of graph.values()) {
|
|
1517
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1518
|
+
let at = node.createdIn;
|
|
1519
|
+
while (at !== void 0 && !seen.has(at)) {
|
|
1520
|
+
seen.add(at);
|
|
1521
|
+
let list = out.get(at);
|
|
1522
|
+
if (!list) out.set(at, list = []);
|
|
1523
|
+
list.push(node.id);
|
|
1524
|
+
at = graph.get(at)?.createdIn;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return out;
|
|
1528
|
+
}
|
|
1512
1529
|
|
|
1513
1530
|
// src/highlight.ts
|
|
1514
1531
|
var LAYER = "fx-highlight-layer";
|
package/dist/hook.mjs
CHANGED
|
@@ -330,7 +330,14 @@ function callSite(stack, origin) {
|
|
|
330
330
|
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
331
331
|
}
|
|
332
332
|
function currentCallSite(origin) {
|
|
333
|
-
|
|
333
|
+
const errors = Error;
|
|
334
|
+
const limit = errors.stackTraceLimit;
|
|
335
|
+
errors.stackTraceLimit = 50;
|
|
336
|
+
try {
|
|
337
|
+
return callSite(new Error().stack, origin);
|
|
338
|
+
} finally {
|
|
339
|
+
errors.stackTraceLimit = limit;
|
|
340
|
+
}
|
|
334
341
|
}
|
|
335
342
|
|
|
336
343
|
// src/instrument.ts
|
|
@@ -430,6 +437,8 @@ function walk(value, depth, seen) {
|
|
|
430
437
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
431
438
|
let read;
|
|
432
439
|
try {
|
|
440
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
441
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
433
442
|
read = walk(object[key], depth + 1, seen);
|
|
434
443
|
} catch {
|
|
435
444
|
read = "[unreadable]";
|
|
@@ -866,7 +875,7 @@ function observeGraph(options = {}) {
|
|
|
866
875
|
const claims = (node, value) => {
|
|
867
876
|
for (const dep of node.deps) {
|
|
868
877
|
const other = graph.get(dep);
|
|
869
|
-
if (other?.read && other
|
|
878
|
+
if (other?.read && readRaw(other) === value) return false;
|
|
870
879
|
}
|
|
871
880
|
return true;
|
|
872
881
|
};
|
|
@@ -884,6 +893,7 @@ function observeGraph(options = {}) {
|
|
|
884
893
|
const scopeOfTarget = /* @__PURE__ */ new WeakMap();
|
|
885
894
|
const fieldsOf = /* @__PURE__ */ new WeakMap();
|
|
886
895
|
let described = null;
|
|
896
|
+
let descendants = null;
|
|
887
897
|
const rendered = /* @__PURE__ */ new Map();
|
|
888
898
|
const elementsIn = (value) => {
|
|
889
899
|
if (value instanceof Element) return [value];
|
|
@@ -1245,6 +1255,7 @@ function observeGraph(options = {}) {
|
|
|
1245
1255
|
graph,
|
|
1246
1256
|
sample() {
|
|
1247
1257
|
described = null;
|
|
1258
|
+
descendants = null;
|
|
1248
1259
|
const changed = [];
|
|
1249
1260
|
const moved = (id) => {
|
|
1250
1261
|
changed.push(id);
|
|
@@ -1300,7 +1311,7 @@ function observeGraph(options = {}) {
|
|
|
1300
1311
|
runsAtLastSample.set(node.id, node.runs);
|
|
1301
1312
|
continue;
|
|
1302
1313
|
}
|
|
1303
|
-
const raw = node
|
|
1314
|
+
const raw = readRaw(node);
|
|
1304
1315
|
if (recording) raws.set(node.id, raw);
|
|
1305
1316
|
edges(node.id, node);
|
|
1306
1317
|
const stamped = raw?.$name;
|
|
@@ -1375,17 +1386,8 @@ function observeGraph(options = {}) {
|
|
|
1375
1386
|
if (live.length) return live;
|
|
1376
1387
|
collect(id);
|
|
1377
1388
|
if (!live.length) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
let at = node.createdIn;
|
|
1381
|
-
while (at !== void 0 && !seen.has(at)) {
|
|
1382
|
-
if (at === id) return true;
|
|
1383
|
-
seen.add(at);
|
|
1384
|
-
at = graph.get(at)?.createdIn;
|
|
1385
|
-
}
|
|
1386
|
-
return false;
|
|
1387
|
-
};
|
|
1388
|
-
for (const node of graph.values()) if (inside(node)) collect(node.id);
|
|
1389
|
+
descendants ??= descendantsByCreator(graph);
|
|
1390
|
+
for (const below of descendants.get(id) ?? []) collect(below);
|
|
1389
1391
|
}
|
|
1390
1392
|
return live;
|
|
1391
1393
|
},
|
|
@@ -1498,6 +1500,21 @@ function observeGraph(options = {}) {
|
|
|
1498
1500
|
}
|
|
1499
1501
|
return api;
|
|
1500
1502
|
}
|
|
1503
|
+
function descendantsByCreator(graph) {
|
|
1504
|
+
const out = /* @__PURE__ */ new Map();
|
|
1505
|
+
for (const node of graph.values()) {
|
|
1506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1507
|
+
let at = node.createdIn;
|
|
1508
|
+
while (at !== void 0 && !seen.has(at)) {
|
|
1509
|
+
seen.add(at);
|
|
1510
|
+
let list = out.get(at);
|
|
1511
|
+
if (!list) out.set(at, list = []);
|
|
1512
|
+
list.push(node.id);
|
|
1513
|
+
at = graph.get(at)?.createdIn;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return out;
|
|
1517
|
+
}
|
|
1501
1518
|
|
|
1502
1519
|
// src/highlight.ts
|
|
1503
1520
|
var LAYER = "fx-highlight-layer";
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/devtools
|
|
1
|
+
/*! @fluixi/devtools v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -259,6 +259,8 @@ function walk(value, depth, seen) {
|
|
|
259
259
|
const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
|
|
260
260
|
let read;
|
|
261
261
|
try {
|
|
262
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
263
|
+
if (descriptor?.get) return `${key}: [getter]`;
|
|
262
264
|
read = walk(object[key], depth + 1, seen);
|
|
263
265
|
} catch {
|
|
264
266
|
read = "[unreadable]";
|
|
@@ -665,7 +667,14 @@ function callSite(stack, origin) {
|
|
|
665
667
|
return fallback2 ? { location: { ...fallback2, external: true }, external: true } : void 0;
|
|
666
668
|
}
|
|
667
669
|
function currentCallSite(origin) {
|
|
668
|
-
|
|
670
|
+
const errors = Error;
|
|
671
|
+
const limit = errors.stackTraceLimit;
|
|
672
|
+
errors.stackTraceLimit = 50;
|
|
673
|
+
try {
|
|
674
|
+
return callSite(new Error().stack, origin);
|
|
675
|
+
} finally {
|
|
676
|
+
errors.stackTraceLimit = limit;
|
|
677
|
+
}
|
|
669
678
|
}
|
|
670
679
|
|
|
671
680
|
// src/history.ts
|
|
@@ -1709,7 +1718,7 @@ function observeGraph(options = {}) {
|
|
|
1709
1718
|
const claims = (node, value) => {
|
|
1710
1719
|
for (const dep of node.deps) {
|
|
1711
1720
|
const other = graph.get(dep);
|
|
1712
|
-
if (other?.read && other
|
|
1721
|
+
if (other?.read && readRaw(other) === value) return false;
|
|
1713
1722
|
}
|
|
1714
1723
|
return true;
|
|
1715
1724
|
};
|
|
@@ -1727,6 +1736,7 @@ function observeGraph(options = {}) {
|
|
|
1727
1736
|
const scopeOfTarget = /* @__PURE__ */ new WeakMap();
|
|
1728
1737
|
const fieldsOf = /* @__PURE__ */ new WeakMap();
|
|
1729
1738
|
let described = null;
|
|
1739
|
+
let descendants = null;
|
|
1730
1740
|
const rendered = /* @__PURE__ */ new Map();
|
|
1731
1741
|
const elementsIn = (value) => {
|
|
1732
1742
|
if (value instanceof Element) return [value];
|
|
@@ -2088,6 +2098,7 @@ function observeGraph(options = {}) {
|
|
|
2088
2098
|
graph,
|
|
2089
2099
|
sample() {
|
|
2090
2100
|
described = null;
|
|
2101
|
+
descendants = null;
|
|
2091
2102
|
const changed = [];
|
|
2092
2103
|
const moved = (id) => {
|
|
2093
2104
|
changed.push(id);
|
|
@@ -2143,7 +2154,7 @@ function observeGraph(options = {}) {
|
|
|
2143
2154
|
runsAtLastSample.set(node.id, node.runs);
|
|
2144
2155
|
continue;
|
|
2145
2156
|
}
|
|
2146
|
-
const raw = node
|
|
2157
|
+
const raw = readRaw(node);
|
|
2147
2158
|
if (recording) raws.set(node.id, raw);
|
|
2148
2159
|
edges(node.id, node);
|
|
2149
2160
|
const stamped = raw?.$name;
|
|
@@ -2218,17 +2229,8 @@ function observeGraph(options = {}) {
|
|
|
2218
2229
|
if (live.length) return live;
|
|
2219
2230
|
collect(id);
|
|
2220
2231
|
if (!live.length) {
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
let at3 = node.createdIn;
|
|
2224
|
-
while (at3 !== void 0 && !seen.has(at3)) {
|
|
2225
|
-
if (at3 === id) return true;
|
|
2226
|
-
seen.add(at3);
|
|
2227
|
-
at3 = graph.get(at3)?.createdIn;
|
|
2228
|
-
}
|
|
2229
|
-
return false;
|
|
2230
|
-
};
|
|
2231
|
-
for (const node of graph.values()) if (inside(node)) collect(node.id);
|
|
2232
|
+
descendants ??= descendantsByCreator(graph);
|
|
2233
|
+
for (const below of descendants.get(id) ?? []) collect(below);
|
|
2232
2234
|
}
|
|
2233
2235
|
return live;
|
|
2234
2236
|
},
|
|
@@ -2341,6 +2343,21 @@ function observeGraph(options = {}) {
|
|
|
2341
2343
|
}
|
|
2342
2344
|
return api;
|
|
2343
2345
|
}
|
|
2346
|
+
function descendantsByCreator(graph) {
|
|
2347
|
+
const out = /* @__PURE__ */ new Map();
|
|
2348
|
+
for (const node of graph.values()) {
|
|
2349
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2350
|
+
let at3 = node.createdIn;
|
|
2351
|
+
while (at3 !== void 0 && !seen.has(at3)) {
|
|
2352
|
+
seen.add(at3);
|
|
2353
|
+
let list = out.get(at3);
|
|
2354
|
+
if (!list) out.set(at3, list = []);
|
|
2355
|
+
list.push(node.id);
|
|
2356
|
+
at3 = graph.get(at3)?.createdIn;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return out;
|
|
2360
|
+
}
|
|
2344
2361
|
|
|
2345
2362
|
// src/highlight.ts
|
|
2346
2363
|
var LAYER = "fx-highlight-layer";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
// @fluixi/devtools: live inspector for the Fluixi reactive graph.
|
|
2
|
-
// Instrumented signal/memo/effect register into a graph; createGraphView renders + animates the
|
|
3
|
-
// State → Memo → Effect propagation.
|
|
4
|
-
//
|
|
5
|
-
// import { createReactor, createGraphView } from '@fluixi/devtools';
|
|
6
|
-
//
|
|
7
|
-
// const reactor = createReactor();
|
|
8
|
-
// reactor.run(({ createSignal, createMemo, createEffect }) => {
|
|
9
|
-
// const [count, setCount] = createSignal(0, { name: 'count' });
|
|
10
|
-
// const double = createMemo(() => count() * 2, { name: 'double' });
|
|
11
|
-
// createEffect(() => console.log(double()), { name: 'log' });
|
|
12
|
-
// setInterval(() => setCount((n) => n + 1), 1000);
|
|
13
|
-
// });
|
|
14
|
-
//
|
|
15
|
-
// const view = createGraphView(svgEl);
|
|
16
|
-
// const tick = () => { reactor.sample(); view.update(reactor.graph); requestAnimationFrame(tick); };
|
|
17
|
-
// tick();
|
|
18
1
|
export { createReactor, nodeVersion, nodeState } from './instrument.js';
|
|
19
2
|
export { observeGraph } from './observe.js';
|
|
20
3
|
export { installDevtoolsHook } from './hook.js';
|