@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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @fluixi/devtools v0.2.0-alpha.7 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/devtools v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/instrument.ts
4
4
  import {
@@ -191,6 +191,8 @@ function walk(value, depth, seen) {
191
191
  const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
192
192
  let read;
193
193
  try {
194
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
195
+ if (descriptor?.get) return `${key}: [getter]`;
194
196
  read = walk(object[key], depth + 1, seen);
195
197
  } catch {
196
198
  read = "[unreadable]";
@@ -601,7 +603,14 @@ function callSite(stack, origin) {
601
603
  return fallback2 ? { location: { ...fallback2, external: true }, external: true } : void 0;
602
604
  }
603
605
  function currentCallSite(origin) {
604
- return callSite(new Error().stack, origin);
606
+ const errors = Error;
607
+ const limit = errors.stackTraceLimit;
608
+ errors.stackTraceLimit = 50;
609
+ try {
610
+ return callSite(new Error().stack, origin);
611
+ } finally {
612
+ errors.stackTraceLimit = limit;
613
+ }
605
614
  }
606
615
 
607
616
  // src/history.ts
@@ -1645,7 +1654,7 @@ function observeGraph(options = {}) {
1645
1654
  const claims = (node, value) => {
1646
1655
  for (const dep of node.deps) {
1647
1656
  const other = graph.get(dep);
1648
- if (other?.read && other.read() === value) return false;
1657
+ if (other?.read && readRaw(other) === value) return false;
1649
1658
  }
1650
1659
  return true;
1651
1660
  };
@@ -1663,6 +1672,7 @@ function observeGraph(options = {}) {
1663
1672
  const scopeOfTarget = /* @__PURE__ */ new WeakMap();
1664
1673
  const fieldsOf = /* @__PURE__ */ new WeakMap();
1665
1674
  let described = null;
1675
+ let descendants = null;
1666
1676
  const rendered = /* @__PURE__ */ new Map();
1667
1677
  const elementsIn = (value) => {
1668
1678
  if (value instanceof Element) return [value];
@@ -2024,6 +2034,7 @@ function observeGraph(options = {}) {
2024
2034
  graph,
2025
2035
  sample() {
2026
2036
  described = null;
2037
+ descendants = null;
2027
2038
  const changed = [];
2028
2039
  const moved = (id) => {
2029
2040
  changed.push(id);
@@ -2079,7 +2090,7 @@ function observeGraph(options = {}) {
2079
2090
  runsAtLastSample.set(node.id, node.runs);
2080
2091
  continue;
2081
2092
  }
2082
- const raw = node.read();
2093
+ const raw = readRaw(node);
2083
2094
  if (recording) raws.set(node.id, raw);
2084
2095
  edges(node.id, node);
2085
2096
  const stamped = raw?.$name;
@@ -2154,17 +2165,8 @@ function observeGraph(options = {}) {
2154
2165
  if (live.length) return live;
2155
2166
  collect(id);
2156
2167
  if (!live.length) {
2157
- const inside = (node) => {
2158
- const seen = /* @__PURE__ */ new Set();
2159
- let at3 = node.createdIn;
2160
- while (at3 !== void 0 && !seen.has(at3)) {
2161
- if (at3 === id) return true;
2162
- seen.add(at3);
2163
- at3 = graph.get(at3)?.createdIn;
2164
- }
2165
- return false;
2166
- };
2167
- for (const node of graph.values()) if (inside(node)) collect(node.id);
2168
+ descendants ??= descendantsByCreator(graph);
2169
+ for (const below of descendants.get(id) ?? []) collect(below);
2168
2170
  }
2169
2171
  return live;
2170
2172
  },
@@ -2277,6 +2279,21 @@ function observeGraph(options = {}) {
2277
2279
  }
2278
2280
  return api;
2279
2281
  }
2282
+ function descendantsByCreator(graph) {
2283
+ const out = /* @__PURE__ */ new Map();
2284
+ for (const node of graph.values()) {
2285
+ const seen = /* @__PURE__ */ new Set();
2286
+ let at3 = node.createdIn;
2287
+ while (at3 !== void 0 && !seen.has(at3)) {
2288
+ seen.add(at3);
2289
+ let list = out.get(at3);
2290
+ if (!list) out.set(at3, list = []);
2291
+ list.push(node.id);
2292
+ at3 = graph.get(at3)?.createdIn;
2293
+ }
2294
+ }
2295
+ return out;
2296
+ }
2280
2297
 
2281
2298
  // src/highlight.ts
2282
2299
  var LAYER = "fx-highlight-layer";
@@ -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]";
@@ -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]";
@@ -194,6 +194,8 @@ function walk(value, depth, seen) {
194
194
  const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
195
195
  let read;
196
196
  try {
197
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
198
+ if (descriptor?.get) return `${key}: [getter]`;
197
199
  read = walk(object[key], depth + 1, seen);
198
200
  } catch {
199
201
  read = "[unreadable]";
@@ -176,6 +176,8 @@ function walk(value, depth, seen) {
176
176
  const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
177
177
  let read;
178
178
  try {
179
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
180
+ if (descriptor?.get) return `${key}: [getter]`;
179
181
  read = walk(object[key], depth + 1, seen);
180
182
  } catch {
181
183
  read = "[unreadable]";
package/dist/observe.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/observe.ts
21
21
  var observe_exports = {};
22
22
  __export(observe_exports, {
23
+ descendantsByCreator: () => descendantsByCreator,
23
24
  observeGraph: () => observeGraph
24
25
  });
25
26
  module.exports = __toCommonJS(observe_exports);
@@ -173,7 +174,14 @@ function callSite(stack, origin) {
173
174
  return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
174
175
  }
175
176
  function currentCallSite(origin) {
176
- return callSite(new Error().stack, origin);
177
+ const errors = Error;
178
+ const limit = errors.stackTraceLimit;
179
+ errors.stackTraceLimit = 50;
180
+ try {
181
+ return callSite(new Error().stack, origin);
182
+ } finally {
183
+ errors.stackTraceLimit = limit;
184
+ }
177
185
  }
178
186
 
179
187
  // src/instrument.ts
@@ -262,6 +270,8 @@ function walk(value, depth, seen) {
262
270
  const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
263
271
  let read;
264
272
  try {
273
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
274
+ if (descriptor?.get) return `${key}: [getter]`;
265
275
  read = walk(object[key], depth + 1, seen);
266
276
  } catch {
267
277
  read = "[unreadable]";
@@ -869,7 +879,7 @@ function observeGraph(options = {}) {
869
879
  const claims = (node, value) => {
870
880
  for (const dep of node.deps) {
871
881
  const other = graph.get(dep);
872
- if (other?.read && other.read() === value) return false;
882
+ if (other?.read && readRaw(other) === value) return false;
873
883
  }
874
884
  return true;
875
885
  };
@@ -887,6 +897,7 @@ function observeGraph(options = {}) {
887
897
  const scopeOfTarget = /* @__PURE__ */ new WeakMap();
888
898
  const fieldsOf = /* @__PURE__ */ new WeakMap();
889
899
  let described = null;
900
+ let descendants = null;
890
901
  const rendered = /* @__PURE__ */ new Map();
891
902
  const elementsIn = (value) => {
892
903
  if (value instanceof Element) return [value];
@@ -1248,6 +1259,7 @@ function observeGraph(options = {}) {
1248
1259
  graph,
1249
1260
  sample() {
1250
1261
  described = null;
1262
+ descendants = null;
1251
1263
  const changed = [];
1252
1264
  const moved = (id) => {
1253
1265
  changed.push(id);
@@ -1303,7 +1315,7 @@ function observeGraph(options = {}) {
1303
1315
  runsAtLastSample.set(node.id, node.runs);
1304
1316
  continue;
1305
1317
  }
1306
- const raw = node.read();
1318
+ const raw = readRaw(node);
1307
1319
  if (recording) raws.set(node.id, raw);
1308
1320
  edges(node.id, node);
1309
1321
  const stamped = raw?.$name;
@@ -1378,17 +1390,8 @@ function observeGraph(options = {}) {
1378
1390
  if (live.length) return live;
1379
1391
  collect(id);
1380
1392
  if (!live.length) {
1381
- const inside = (node) => {
1382
- const seen = /* @__PURE__ */ new Set();
1383
- let at = node.createdIn;
1384
- while (at !== void 0 && !seen.has(at)) {
1385
- if (at === id) return true;
1386
- seen.add(at);
1387
- at = graph.get(at)?.createdIn;
1388
- }
1389
- return false;
1390
- };
1391
- for (const node of graph.values()) if (inside(node)) collect(node.id);
1393
+ descendants ??= descendantsByCreator(graph);
1394
+ for (const below of descendants.get(id) ?? []) collect(below);
1392
1395
  }
1393
1396
  return live;
1394
1397
  },
@@ -1501,3 +1504,18 @@ function observeGraph(options = {}) {
1501
1504
  }
1502
1505
  return api;
1503
1506
  }
1507
+ function descendantsByCreator(graph) {
1508
+ const out = /* @__PURE__ */ new Map();
1509
+ for (const node of graph.values()) {
1510
+ const seen = /* @__PURE__ */ new Set();
1511
+ let at = node.createdIn;
1512
+ while (at !== void 0 && !seen.has(at)) {
1513
+ seen.add(at);
1514
+ let list = out.get(at);
1515
+ if (!list) out.set(at, list = []);
1516
+ list.push(node.id);
1517
+ at = graph.get(at)?.createdIn;
1518
+ }
1519
+ }
1520
+ return out;
1521
+ }
package/dist/observe.d.ts CHANGED
@@ -77,4 +77,11 @@ export interface ObserveOptions {
77
77
  mapSource?: (location: ReactiveSourceLocation) => ReactiveSourceLocation | undefined;
78
78
  }
79
79
  export declare function observeGraph(options?: ObserveOptions): ObservedGraph;
80
+ /**
81
+ * What every node created, at any depth, in graph order: the nodes whose `createdIn` chain
82
+ * passes through it. Built in one pass over the graph, so asking it for each tag of a page is
83
+ * linear; walking every node's chain once per tag made a sample quadratic, which on a page of a
84
+ * few thousand nodes took seconds on every keystroke.
85
+ */
86
+ export declare function descendantsByCreator(graph: Map<number, RNode>): Map<number, number[]>;
80
87
  //# sourceMappingURL=observe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAmE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG9D,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAmDhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnC,gFAAgF;IAChF,MAAM,IAAI,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,6FAA6F;IAC7F,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,4EAA4E;IAC5E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B;;;;;;OAMG;IACH,WAAW,IAAI,MAAM,EAAE,CAAC;IACxB,8EAA8E;IAC9E,UAAU,IAAI,UAAU,EAAE,CAAC;IAC3B,qEAAqE;IACrE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IACnC,wEAAwE;IACxE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD;;;OAGG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7C,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IACrD,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,sBAAsB,GAAG,SAAS,CAAC;CACtF;AAgGD,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAk2BxE"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAmE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG9D,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAmDhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnC,gFAAgF;IAChF,MAAM,IAAI,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,6FAA6F;IAC7F,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,4EAA4E;IAC5E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B;;;;;;OAMG;IACH,WAAW,IAAI,MAAM,EAAE,CAAC;IACxB,8EAA8E;IAC9E,UAAU,IAAI,UAAU,EAAE,CAAC;IAC3B,qEAAqE;IACrE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IACnC,wEAAwE;IACxE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD;;;OAGG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7C,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IACrD,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,sBAAsB,GAAG,SAAS,CAAC;CACtF;AAgGD,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CA81BxE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAcrF"}
package/dist/observe.js CHANGED
@@ -154,7 +154,7 @@ export function observeGraph(options = {}) {
154
154
  const claims = (node, value) => {
155
155
  for (const dep of node.deps) {
156
156
  const other = graph.get(dep);
157
- if (other?.read && other.read() === value)
157
+ if (other?.read && readRaw(other) === value)
158
158
  return false;
159
159
  }
160
160
  return true;
@@ -209,6 +209,8 @@ export function observeGraph(options = {}) {
209
209
  const fieldsOf = new WeakMap();
210
210
  /** Cleared on each sample: the page has moved on. */
211
211
  let described = null;
212
+ /** Per sample, like `described`: what each node created, at any depth. */
213
+ let descendants = null;
212
214
  /**
213
215
  * What each tag rendered, weakly.
214
216
  *
@@ -614,7 +616,7 @@ export function observeGraph(options = {}) {
614
616
  const parentId = parent ? scopeIds.get(parent) : undefined;
615
617
  // Where it was opened: the line that defined the module, built the service, or, for
616
618
  // an element, the one that put it on the page. A bundler's virtual module (the
617
- // generated app config, `\0virtual:…`) is nowhere a reader can go, so it is left out.
619
+ // generated app config, `\0virtual:...`) is nowhere a reader can go, so it is left out.
618
620
  const found = currentCallSite(origin);
619
621
  const site = found && !/\0|__x00__|^virtual:/.test(found.location.file ?? '') ? found : undefined;
620
622
  const node = {
@@ -724,6 +726,7 @@ export function observeGraph(options = {}) {
724
726
  graph,
725
727
  sample() {
726
728
  described = null;
729
+ descendants = null;
727
730
  const changed = [];
728
731
  /** Recorded once here, so every consumer can be told about it at its own pace. */
729
732
  const moved = (id) => {
@@ -800,7 +803,9 @@ export function observeGraph(options = {}) {
800
803
  runsAtLastSample.set(node.id, node.runs);
801
804
  continue;
802
805
  }
803
- const raw = node.read();
806
+ // Guarded: a memo holding an error rethrows it on every read, and this runs on a
807
+ // timer, so an unguarded read threw the page's error ten times a second.
808
+ const raw = readRaw(node);
804
809
  // For the next write to diff against; without it the first change has no entries.
805
810
  if (recording)
806
811
  raws.set(node.id, raw);
@@ -917,20 +922,9 @@ export function observeGraph(options = {}) {
917
922
  // A value drives the element its binding was recorded against. A tag that returned
918
923
  // nothing usable falls back to where the values inside it reach.
919
924
  if (!live.length) {
920
- const inside = (node) => {
921
- const seen = new Set();
922
- let at = node.createdIn;
923
- while (at !== undefined && !seen.has(at)) {
924
- if (at === id)
925
- return true;
926
- seen.add(at);
927
- at = graph.get(at)?.createdIn;
928
- }
929
- return false;
930
- };
931
- for (const node of graph.values())
932
- if (inside(node))
933
- collect(node.id);
925
+ descendants ??= descendantsByCreator(graph);
926
+ for (const below of descendants.get(id) ?? [])
927
+ collect(below);
934
928
  }
935
929
  return live;
936
930
  },
@@ -1060,3 +1054,25 @@ export function observeGraph(options = {}) {
1060
1054
  }
1061
1055
  return api;
1062
1056
  }
1057
+ /**
1058
+ * What every node created, at any depth, in graph order: the nodes whose `createdIn` chain
1059
+ * passes through it. Built in one pass over the graph, so asking it for each tag of a page is
1060
+ * linear; walking every node's chain once per tag made a sample quadratic, which on a page of a
1061
+ * few thousand nodes took seconds on every keystroke.
1062
+ */
1063
+ export function descendantsByCreator(graph) {
1064
+ const out = new Map();
1065
+ for (const node of graph.values()) {
1066
+ const seen = new Set();
1067
+ let at = node.createdIn;
1068
+ while (at !== undefined && !seen.has(at)) {
1069
+ seen.add(at);
1070
+ let list = out.get(at);
1071
+ if (!list)
1072
+ out.set(at, (list = []));
1073
+ list.push(node.id);
1074
+ at = graph.get(at)?.createdIn;
1075
+ }
1076
+ }
1077
+ return out;
1078
+ }
package/dist/observe.mjs CHANGED
@@ -153,7 +153,14 @@ function callSite(stack, origin) {
153
153
  return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
154
154
  }
155
155
  function currentCallSite(origin) {
156
- return callSite(new Error().stack, origin);
156
+ const errors = Error;
157
+ const limit = errors.stackTraceLimit;
158
+ errors.stackTraceLimit = 50;
159
+ try {
160
+ return callSite(new Error().stack, origin);
161
+ } finally {
162
+ errors.stackTraceLimit = limit;
163
+ }
157
164
  }
158
165
 
159
166
  // src/instrument.ts
@@ -253,6 +260,8 @@ function walk(value, depth, seen) {
253
260
  const shown = keys.slice(0, MAX_ENTRIES).map((key) => {
254
261
  let read;
255
262
  try {
263
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
264
+ if (descriptor?.get) return `${key}: [getter]`;
256
265
  read = walk(object[key], depth + 1, seen);
257
266
  } catch {
258
267
  read = "[unreadable]";
@@ -860,7 +869,7 @@ function observeGraph(options = {}) {
860
869
  const claims = (node, value) => {
861
870
  for (const dep of node.deps) {
862
871
  const other = graph.get(dep);
863
- if (other?.read && other.read() === value) return false;
872
+ if (other?.read && readRaw(other) === value) return false;
864
873
  }
865
874
  return true;
866
875
  };
@@ -878,6 +887,7 @@ function observeGraph(options = {}) {
878
887
  const scopeOfTarget = /* @__PURE__ */ new WeakMap();
879
888
  const fieldsOf = /* @__PURE__ */ new WeakMap();
880
889
  let described = null;
890
+ let descendants = null;
881
891
  const rendered = /* @__PURE__ */ new Map();
882
892
  const elementsIn = (value) => {
883
893
  if (value instanceof Element) return [value];
@@ -1239,6 +1249,7 @@ function observeGraph(options = {}) {
1239
1249
  graph,
1240
1250
  sample() {
1241
1251
  described = null;
1252
+ descendants = null;
1242
1253
  const changed = [];
1243
1254
  const moved = (id) => {
1244
1255
  changed.push(id);
@@ -1294,7 +1305,7 @@ function observeGraph(options = {}) {
1294
1305
  runsAtLastSample.set(node.id, node.runs);
1295
1306
  continue;
1296
1307
  }
1297
- const raw = node.read();
1308
+ const raw = readRaw(node);
1298
1309
  if (recording) raws.set(node.id, raw);
1299
1310
  edges(node.id, node);
1300
1311
  const stamped = raw?.$name;
@@ -1369,17 +1380,8 @@ function observeGraph(options = {}) {
1369
1380
  if (live.length) return live;
1370
1381
  collect(id);
1371
1382
  if (!live.length) {
1372
- const inside = (node) => {
1373
- const seen = /* @__PURE__ */ new Set();
1374
- let at = node.createdIn;
1375
- while (at !== void 0 && !seen.has(at)) {
1376
- if (at === id) return true;
1377
- seen.add(at);
1378
- at = graph.get(at)?.createdIn;
1379
- }
1380
- return false;
1381
- };
1382
- for (const node of graph.values()) if (inside(node)) collect(node.id);
1383
+ descendants ??= descendantsByCreator(graph);
1384
+ for (const below of descendants.get(id) ?? []) collect(below);
1383
1385
  }
1384
1386
  return live;
1385
1387
  },
@@ -1492,6 +1494,22 @@ function observeGraph(options = {}) {
1492
1494
  }
1493
1495
  return api;
1494
1496
  }
1497
+ function descendantsByCreator(graph) {
1498
+ const out = /* @__PURE__ */ new Map();
1499
+ for (const node of graph.values()) {
1500
+ const seen = /* @__PURE__ */ new Set();
1501
+ let at = node.createdIn;
1502
+ while (at !== void 0 && !seen.has(at)) {
1503
+ seen.add(at);
1504
+ let list = out.get(at);
1505
+ if (!list) out.set(at, list = []);
1506
+ list.push(node.id);
1507
+ at = graph.get(at)?.createdIn;
1508
+ }
1509
+ }
1510
+ return out;
1511
+ }
1495
1512
  export {
1513
+ descendantsByCreator,
1496
1514
  observeGraph
1497
1515
  };
package/dist/path.js CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  /** Windows separators, and the leading slash an origin-stripped path can lose. */
11
11
  const normalize = (file) => file.replace(/\\/g, '/');
12
- /** `Users/…`, `home/…`: an absolute path whose leading slash was trimmed off. */
12
+ /** `Users/...`, `home/...`: an absolute path whose leading slash was trimmed off. */
13
13
  const ROOTED = /^\/|^(Users|home|var|opt|private|tmp|mnt|srv)\//;
14
14
  /** Where a monorepo keeps the things it builds. */
15
15
  const WORKSPACE_DIRS = new Set(['packages', 'apps', 'libs', 'examples']);
@@ -1,4 +1,4 @@
1
- /*! @fluixi/devtools v0.2.0-alpha.7 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
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;
@@ -1,4 +1,4 @@
1
- /*! @fluixi/devtools v0.2.0-alpha.7 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/devtools v1.0.0-alpha.101 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/plugin.ts
4
4
  var plugins = [];