@graphrefly/graphrefly 0.25.0 → 0.27.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/README.md +8 -0
- package/dist/ai-CaR_912Q.d.cts +1033 -0
- package/dist/ai-WlRltJV7.d.ts +1033 -0
- package/dist/audit-ClmqGOCx.d.cts +245 -0
- package/dist/audit-DRlSzBu9.d.ts +245 -0
- package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
- package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
- package/dist/chunk-APFNLIRG.js +62 -0
- package/dist/chunk-APFNLIRG.js.map +1 -0
- package/dist/chunk-AT5LKYNL.js +395 -0
- package/dist/chunk-AT5LKYNL.js.map +1 -0
- package/dist/{chunk-IAHGTNOZ.js → chunk-BQ6RQQFF.js} +351 -2095
- package/dist/chunk-BQ6RQQFF.js.map +1 -0
- package/dist/{chunk-L2GLW2U7.js → chunk-BVZYTZ5H.js} +9 -103
- package/dist/chunk-BVZYTZ5H.js.map +1 -0
- package/dist/{chunk-EVR6UFUV.js → chunk-DST5DKZS.js} +19 -15
- package/dist/{chunk-EVR6UFUV.js.map → chunk-DST5DKZS.js.map} +1 -1
- package/dist/{chunk-TKE3JGOH.js → chunk-GTE6PWRZ.js} +5 -692
- package/dist/chunk-GTE6PWRZ.js.map +1 -0
- package/dist/chunk-HXZEYDUR.js +94 -0
- package/dist/chunk-HXZEYDUR.js.map +1 -0
- package/dist/chunk-J22W6HV3.js +107 -0
- package/dist/chunk-J22W6HV3.js.map +1 -0
- package/dist/{chunk-PY4XCDLR.js → chunk-J2VBW3DZ.js} +6 -95
- package/dist/chunk-J2VBW3DZ.js.map +1 -0
- package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
- package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
- package/dist/chunk-JWBCY4NC.js +330 -0
- package/dist/chunk-JWBCY4NC.js.map +1 -0
- package/dist/chunk-K2AUJHVP.js +2251 -0
- package/dist/chunk-K2AUJHVP.js.map +1 -0
- package/dist/chunk-MJ2NKQQL.js +119 -0
- package/dist/chunk-MJ2NKQQL.js.map +1 -0
- package/dist/chunk-N6UR7YVY.js +198 -0
- package/dist/chunk-N6UR7YVY.js.map +1 -0
- package/dist/chunk-NC6S43JJ.js +456 -0
- package/dist/chunk-NC6S43JJ.js.map +1 -0
- package/dist/chunk-OFVJBJXR.js +98 -0
- package/dist/chunk-OFVJBJXR.js.map +1 -0
- package/dist/chunk-OHISZPOJ.js +97 -0
- package/dist/chunk-OHISZPOJ.js.map +1 -0
- package/dist/chunk-OU5CQKNW.js +102 -0
- package/dist/chunk-OU5CQKNW.js.map +1 -0
- package/dist/{chunk-XOFWRC73.js → chunk-PF7GRZMW.js} +316 -21
- package/dist/chunk-PF7GRZMW.js.map +1 -0
- package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
- package/dist/chunk-PHOUUNK7.js.map +1 -0
- package/dist/chunk-RNHBMHKA.js +1665 -0
- package/dist/chunk-RNHBMHKA.js.map +1 -0
- package/dist/chunk-SX52TAR4.js +110 -0
- package/dist/chunk-SX52TAR4.js.map +1 -0
- package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
- package/dist/chunk-WBZOVTYK.js +171 -0
- package/dist/chunk-WBZOVTYK.js.map +1 -0
- package/dist/chunk-WKNUIZOY.js +354 -0
- package/dist/chunk-WKNUIZOY.js.map +1 -0
- package/dist/chunk-X3VMZYBT.js +713 -0
- package/dist/chunk-X3VMZYBT.js.map +1 -0
- package/dist/chunk-X5R3GL6H.js +525 -0
- package/dist/chunk-X5R3GL6H.js.map +1 -0
- package/dist/chunk-XGPU467M.js +136 -0
- package/dist/chunk-XGPU467M.js.map +1 -0
- package/dist/compat/index.cjs +7656 -0
- package/dist/compat/index.cjs.map +1 -0
- package/dist/compat/index.d.cts +18 -0
- package/dist/compat/index.d.ts +18 -0
- package/dist/compat/index.js +50 -0
- package/dist/compat/index.js.map +1 -0
- package/dist/compat/jotai/index.cjs +2048 -0
- package/dist/compat/jotai/index.cjs.map +1 -0
- package/dist/compat/jotai/index.d.cts +2 -0
- package/dist/compat/jotai/index.d.ts +2 -0
- package/dist/compat/jotai/index.js +9 -0
- package/dist/compat/jotai/index.js.map +1 -0
- package/dist/compat/nanostores/index.cjs +2175 -0
- package/dist/compat/nanostores/index.cjs.map +1 -0
- package/dist/compat/nanostores/index.d.cts +2 -0
- package/dist/compat/nanostores/index.d.ts +2 -0
- package/dist/compat/nanostores/index.js +23 -0
- package/dist/compat/nanostores/index.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +350 -16
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +6 -6
- package/dist/compat/nestjs/index.d.ts +6 -6
- package/dist/compat/nestjs/index.js +11 -9
- package/dist/compat/react/index.cjs +141 -0
- package/dist/compat/react/index.cjs.map +1 -0
- package/dist/compat/react/index.d.cts +2 -0
- package/dist/compat/react/index.d.ts +2 -0
- package/dist/compat/react/index.js +12 -0
- package/dist/compat/react/index.js.map +1 -0
- package/dist/compat/solid/index.cjs +128 -0
- package/dist/compat/solid/index.cjs.map +1 -0
- package/dist/compat/solid/index.d.cts +2 -0
- package/dist/compat/solid/index.d.ts +2 -0
- package/dist/compat/solid/index.js +12 -0
- package/dist/compat/solid/index.js.map +1 -0
- package/dist/compat/svelte/index.cjs +131 -0
- package/dist/compat/svelte/index.cjs.map +1 -0
- package/dist/compat/svelte/index.d.cts +2 -0
- package/dist/compat/svelte/index.d.ts +2 -0
- package/dist/compat/svelte/index.js +12 -0
- package/dist/compat/svelte/index.js.map +1 -0
- package/dist/compat/vue/index.cjs +146 -0
- package/dist/compat/vue/index.cjs.map +1 -0
- package/dist/compat/vue/index.d.cts +3 -0
- package/dist/compat/vue/index.d.ts +3 -0
- package/dist/compat/vue/index.js +12 -0
- package/dist/compat/vue/index.js.map +1 -0
- package/dist/compat/zustand/index.cjs +4931 -0
- package/dist/compat/zustand/index.cjs.map +1 -0
- package/dist/compat/zustand/index.d.cts +5 -0
- package/dist/compat/zustand/index.d.ts +5 -0
- package/dist/compat/zustand/index.js +12 -0
- package/dist/compat/zustand/index.js.map +1 -0
- package/dist/composite-C7PcQvcs.d.cts +303 -0
- package/dist/composite-aUCvjZVR.d.ts +303 -0
- package/dist/core/index.cjs +53 -4
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +4 -3
- package/dist/core/index.d.ts +4 -3
- package/dist/core/index.js +26 -24
- package/dist/demo-shell-BDkOptd6.d.ts +102 -0
- package/dist/demo-shell-Crid1WdR.d.cts +102 -0
- package/dist/extra/index.cjs +222 -110
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +6 -4
- package/dist/extra/index.d.ts +6 -4
- package/dist/extra/index.js +72 -65
- package/dist/extra/sources.cjs +2486 -0
- package/dist/extra/sources.cjs.map +1 -0
- package/dist/extra/sources.d.cts +465 -0
- package/dist/extra/sources.d.ts +465 -0
- package/dist/extra/sources.js +57 -0
- package/dist/extra/sources.js.map +1 -0
- package/dist/graph/index.cjs +408 -14
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +5 -5
- package/dist/graph/index.d.ts +5 -5
- package/dist/graph/index.js +13 -5
- package/dist/{graph-D-3JIQme.d.cts → graph-CCwGKLCm.d.ts} +195 -4
- package/dist/{graph-B6NFqv3z.d.ts → graph-DNCrvZSn.d.cts} +195 -4
- package/dist/index-3lsddbbS.d.ts +86 -0
- package/dist/index-B1tloyhO.d.cts +34 -0
- package/dist/{index-CYkjxu3s.d.ts → index-B6D3QNSA.d.ts} +33 -4
- package/dist/index-B6EhDnjH.d.cts +37 -0
- package/dist/index-B9B7_HEY.d.ts +37 -0
- package/dist/{index-Ds23Wvou.d.ts → index-BHlKbUwO.d.cts} +131 -883
- package/dist/{index-DiobMNwE.d.ts → index-BPVt8kqc.d.ts} +3 -3
- package/dist/index-BaSM3aYt.d.ts +195 -0
- package/dist/index-BuEoe-Qu.d.ts +121 -0
- package/dist/{index-Ch0IpIO0.d.cts → index-BwfLUNw4.d.ts} +131 -883
- package/dist/index-ByQxazQJ.d.cts +86 -0
- package/dist/index-C0svESO4.d.ts +127 -0
- package/dist/{index-OXImXMq6.d.ts → index-C8oil6M6.d.ts} +18 -196
- package/dist/{index-DKE1EATr.d.cts → index-CI3DprxP.d.cts} +18 -196
- package/dist/{index-AMWewNDe.d.cts → index-CO8uBlUh.d.cts} +33 -4
- package/dist/index-CxFrXH4m.d.ts +45 -0
- package/dist/index-D8wS_PeY.d.cts +121 -0
- package/dist/index-DO_6JN9Z.d.cts +127 -0
- package/dist/index-DVGiGFGT.d.cts +195 -0
- package/dist/index-DYme44FM.d.cts +44 -0
- package/dist/{index-J7Kc0oIQ.d.cts → index-DlLp-2Xn.d.cts} +3 -3
- package/dist/index-Dzk2hrlR.d.ts +44 -0
- package/dist/index-VHqptjhu.d.cts +45 -0
- package/dist/index-VdHQMPy1.d.ts +36 -0
- package/dist/index-Xi3u0HCQ.d.cts +36 -0
- package/dist/index-wEn0eFe8.d.ts +34 -0
- package/dist/index.cjs +1780 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +784 -2082
- package/dist/index.d.ts +784 -2082
- package/dist/index.js +955 -4349
- package/dist/index.js.map +1 -1
- package/dist/memory-C6Z2tGpC.d.cts +139 -0
- package/dist/memory-li6FL5RM.d.ts +139 -0
- package/dist/messaging-Gt4LPbyA.d.cts +269 -0
- package/dist/messaging-XDoYablx.d.ts +269 -0
- package/dist/{meta-DWbkoq1s.d.cts → meta-BxCA7rcr.d.cts} +1 -1
- package/dist/{meta-CnkLA_43.d.ts → meta-CbznRPYJ.d.ts} +1 -1
- package/dist/{node-B-f-Lu-k.d.cts → node-BmerH3kS.d.cts} +26 -1
- package/dist/{node-B-f-Lu-k.d.ts → node-BmerH3kS.d.ts} +26 -1
- package/dist/{observable-uP-wy_uK.d.ts → observable-BgGUwcqp.d.ts} +1 -1
- package/dist/{observable-DBnrwcar.d.cts → observable-DJt_AxzQ.d.cts} +1 -1
- package/dist/patterns/ai.cjs +7930 -0
- package/dist/patterns/ai.cjs.map +1 -0
- package/dist/patterns/ai.d.cts +10 -0
- package/dist/patterns/ai.d.ts +10 -0
- package/dist/patterns/ai.js +71 -0
- package/dist/patterns/ai.js.map +1 -0
- package/dist/patterns/audit.cjs +5805 -0
- package/dist/patterns/audit.cjs.map +1 -0
- package/dist/patterns/audit.d.cts +6 -0
- package/dist/patterns/audit.d.ts +6 -0
- package/dist/patterns/audit.js +29 -0
- package/dist/patterns/audit.js.map +1 -0
- package/dist/patterns/demo-shell.cjs +5604 -0
- package/dist/patterns/demo-shell.cjs.map +1 -0
- package/dist/patterns/demo-shell.d.cts +6 -0
- package/dist/patterns/demo-shell.d.ts +6 -0
- package/dist/patterns/demo-shell.js +15 -0
- package/dist/patterns/demo-shell.js.map +1 -0
- package/dist/patterns/memory.cjs +5283 -0
- package/dist/patterns/memory.cjs.map +1 -0
- package/dist/patterns/memory.d.cts +5 -0
- package/dist/patterns/memory.d.ts +5 -0
- package/dist/patterns/memory.js +20 -0
- package/dist/patterns/memory.js.map +1 -0
- package/dist/patterns/reactive-layout/index.cjs +355 -13
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +6 -5
- package/dist/patterns/reactive-layout/index.d.ts +6 -5
- package/dist/patterns/reactive-layout/index.js +15 -12
- package/dist/reactive-layout-MQP--J3F.d.cts +183 -0
- package/dist/reactive-layout-u5Ulnqag.d.ts +183 -0
- package/dist/{storage-BuTdpCI1.d.cts → storage-CMjUUuxn.d.ts} +10 -2
- package/dist/{storage-F2X1U1x0.d.ts → storage-DdWlZo6U.d.cts} +10 -2
- package/dist/sugar-CCOxXK1e.d.ts +201 -0
- package/dist/sugar-D02n5JjF.d.cts +201 -0
- package/package.json +63 -3
- package/dist/chunk-5DJTTKX3.js.map +0 -1
- package/dist/chunk-IAHGTNOZ.js.map +0 -1
- package/dist/chunk-L2GLW2U7.js.map +0 -1
- package/dist/chunk-MW4VAKAO.js +0 -47
- package/dist/chunk-MW4VAKAO.js.map +0 -1
- package/dist/chunk-PY4XCDLR.js.map +0 -1
- package/dist/chunk-TKE3JGOH.js.map +0 -1
- package/dist/chunk-XOFWRC73.js.map +0 -1
- package/dist/index-BJB7t9gg.d.cts +0 -392
- package/dist/index-C-TXEa7C.d.ts +0 -392
- /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.js.map} +0 -0
package/dist/graph/index.cjs
CHANGED
|
@@ -26,16 +26,19 @@ __export(graph_exports, {
|
|
|
26
26
|
JsonCodec: () => JsonCodec,
|
|
27
27
|
SIZEOF_OVERHEAD: () => OVERHEAD,
|
|
28
28
|
SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
|
|
29
|
+
SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
|
|
29
30
|
createDagCborCodec: () => createDagCborCodec,
|
|
30
31
|
createDagCborZstdCodec: () => createDagCborZstdCodec,
|
|
31
32
|
decodeEnvelope: () => decodeEnvelope,
|
|
32
33
|
diffForWAL: () => diffForWAL,
|
|
33
34
|
encodeEnvelope: () => encodeEnvelope,
|
|
35
|
+
explainPath: () => explainPath,
|
|
34
36
|
graphProfile: () => graphProfile,
|
|
35
37
|
reachable: () => reachable,
|
|
36
38
|
registerBuiltinCodecs: () => registerBuiltinCodecs,
|
|
37
39
|
replayWAL: () => replayWAL,
|
|
38
|
-
sizeof: () => sizeof
|
|
40
|
+
sizeof: () => sizeof,
|
|
41
|
+
watchTopologyTree: () => watchTopologyTree
|
|
39
42
|
});
|
|
40
43
|
module.exports = __toCommonJS(graph_exports);
|
|
41
44
|
|
|
@@ -317,6 +320,200 @@ function replayWAL(entries) {
|
|
|
317
320
|
return result;
|
|
318
321
|
}
|
|
319
322
|
|
|
323
|
+
// src/graph/explain.ts
|
|
324
|
+
function explainPath(described, from, to, opts = {}) {
|
|
325
|
+
const fromExists = from in described.nodes;
|
|
326
|
+
const toExists = to in described.nodes;
|
|
327
|
+
if (!fromExists) return makeFailure(from, to, "no-such-from");
|
|
328
|
+
if (!toExists) return makeFailure(from, to, "no-such-to");
|
|
329
|
+
const maxDepth = opts.maxDepth;
|
|
330
|
+
if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
|
|
331
|
+
throw new Error(`explainPath: maxDepth must be an integer >= 0`);
|
|
332
|
+
}
|
|
333
|
+
if (from === to) {
|
|
334
|
+
if (opts.findCycle === true) {
|
|
335
|
+
const cycle = findShortestCycle(described, from, opts);
|
|
336
|
+
if (cycle != null) return cycle;
|
|
337
|
+
}
|
|
338
|
+
const step = buildStep(from, described.nodes[from], 0, opts);
|
|
339
|
+
return makeSuccess(from, to, [step]);
|
|
340
|
+
}
|
|
341
|
+
if (maxDepth === 0) return makeFailure(from, to, "no-path");
|
|
342
|
+
const result = bfsShortestPath(described, from, to, maxDepth);
|
|
343
|
+
if (!result.found) {
|
|
344
|
+
return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
|
|
345
|
+
}
|
|
346
|
+
return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
|
|
347
|
+
}
|
|
348
|
+
function bfsShortestPath(described, from, to, maxDepth) {
|
|
349
|
+
const pred = /* @__PURE__ */ new Map();
|
|
350
|
+
const queue = [{ path: to, depth: 0 }];
|
|
351
|
+
const visited = /* @__PURE__ */ new Set([to]);
|
|
352
|
+
let head = 0;
|
|
353
|
+
let truncated = false;
|
|
354
|
+
while (head < queue.length) {
|
|
355
|
+
const cur = queue[head++];
|
|
356
|
+
if (cur.path === from) break;
|
|
357
|
+
if (maxDepth != null && cur.depth >= maxDepth) {
|
|
358
|
+
const node3 = described.nodes[cur.path];
|
|
359
|
+
if (node3?.deps && node3.deps.length > 0) truncated = true;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const node2 = described.nodes[cur.path];
|
|
363
|
+
if (node2 == null) continue;
|
|
364
|
+
const deps = node2.deps ?? [];
|
|
365
|
+
const slots = /* @__PURE__ */ new Map();
|
|
366
|
+
for (let i = 0; i < deps.length; i++) {
|
|
367
|
+
const dep = deps[i];
|
|
368
|
+
if (!dep) continue;
|
|
369
|
+
let arr = slots.get(dep);
|
|
370
|
+
if (arr == null) {
|
|
371
|
+
arr = [];
|
|
372
|
+
slots.set(dep, arr);
|
|
373
|
+
}
|
|
374
|
+
arr.push(i);
|
|
375
|
+
}
|
|
376
|
+
for (const [dep, indices] of slots) {
|
|
377
|
+
if (visited.has(dep)) continue;
|
|
378
|
+
visited.add(dep);
|
|
379
|
+
pred.set(dep, { from: cur.path, depIndices: indices });
|
|
380
|
+
queue.push({ path: dep, depth: cur.depth + 1 });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (!pred.has(from)) {
|
|
384
|
+
return { found: false, pathOrder: [], truncated };
|
|
385
|
+
}
|
|
386
|
+
const pathOrder = [{ path: from }];
|
|
387
|
+
let cursor = from;
|
|
388
|
+
while (cursor !== to) {
|
|
389
|
+
const p = pred.get(cursor);
|
|
390
|
+
if (p == null) return { found: false, pathOrder: [], truncated: false };
|
|
391
|
+
pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
|
|
392
|
+
pathOrder.push({ path: p.from });
|
|
393
|
+
cursor = p.from;
|
|
394
|
+
}
|
|
395
|
+
return { found: true, pathOrder, truncated: false };
|
|
396
|
+
}
|
|
397
|
+
function findShortestCycle(described, start, opts) {
|
|
398
|
+
const startNode = described.nodes[start];
|
|
399
|
+
if (startNode == null) return null;
|
|
400
|
+
const startDeps = startNode.deps ?? [];
|
|
401
|
+
const selfSlots = [];
|
|
402
|
+
for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
|
|
403
|
+
if (selfSlots.length > 0) {
|
|
404
|
+
const step0 = buildStep(start, startNode, 0, opts);
|
|
405
|
+
step0.dep_index = selfSlots[0];
|
|
406
|
+
const step1 = buildStep(start, startNode, 1, opts);
|
|
407
|
+
return makeSuccess(start, start, [step0, step1]);
|
|
408
|
+
}
|
|
409
|
+
let best = null;
|
|
410
|
+
for (let i = 0; i < startDeps.length; i++) {
|
|
411
|
+
const dep = startDeps[i];
|
|
412
|
+
if (!dep || dep === start) continue;
|
|
413
|
+
const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
|
|
414
|
+
if (!sub.found) continue;
|
|
415
|
+
if (best == null || sub.pathOrder.length < best.pathOrder.length) {
|
|
416
|
+
best = sub;
|
|
417
|
+
best = {
|
|
418
|
+
found: true,
|
|
419
|
+
pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
|
|
420
|
+
truncated: false
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (best == null) return null;
|
|
425
|
+
return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
|
|
426
|
+
}
|
|
427
|
+
function materializeSteps(described, pathOrder, opts) {
|
|
428
|
+
return pathOrder.map((entry, i) => {
|
|
429
|
+
const node2 = described.nodes[entry.path];
|
|
430
|
+
const step = buildStep(entry.path, node2, i, opts);
|
|
431
|
+
if (entry.depIndices != null && entry.depIndices.length > 0) {
|
|
432
|
+
step.dep_index = entry.depIndices[0];
|
|
433
|
+
if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
|
|
434
|
+
}
|
|
435
|
+
return step;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function buildStep(path, node2, hop, opts) {
|
|
439
|
+
const step = {
|
|
440
|
+
path,
|
|
441
|
+
type: node2.type,
|
|
442
|
+
hop
|
|
443
|
+
};
|
|
444
|
+
if (node2.status !== void 0) step.status = node2.status;
|
|
445
|
+
if ("value" in node2) step.value = node2.value;
|
|
446
|
+
if (node2.v != null) step.v = node2.v;
|
|
447
|
+
const annotation = opts.annotations?.get(path) ?? node2.reason;
|
|
448
|
+
if (annotation != null) step.reason = annotation;
|
|
449
|
+
const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
|
|
450
|
+
if (lastMutation != null) step.lastMutation = lastMutation;
|
|
451
|
+
return step;
|
|
452
|
+
}
|
|
453
|
+
function makeSuccess(from, to, steps) {
|
|
454
|
+
return finalize(from, to, true, "ok", steps);
|
|
455
|
+
}
|
|
456
|
+
function makeFailure(from, to, reason) {
|
|
457
|
+
return finalize(from, to, false, reason, []);
|
|
458
|
+
}
|
|
459
|
+
function finalize(from, to, found, reason, steps) {
|
|
460
|
+
const text = renderChain(from, to, found, reason, steps);
|
|
461
|
+
return {
|
|
462
|
+
from,
|
|
463
|
+
to,
|
|
464
|
+
found,
|
|
465
|
+
reason,
|
|
466
|
+
steps,
|
|
467
|
+
text,
|
|
468
|
+
toJSON() {
|
|
469
|
+
return { from, to, found, reason, steps };
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function renderChain(from, to, found, reason, steps) {
|
|
474
|
+
if (!found) {
|
|
475
|
+
switch (reason) {
|
|
476
|
+
case "no-such-from":
|
|
477
|
+
return `explainPath: no node named "${from}"`;
|
|
478
|
+
case "no-such-to":
|
|
479
|
+
return `explainPath: no node named "${to}"`;
|
|
480
|
+
case "max-depth-exceeded":
|
|
481
|
+
return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
|
|
482
|
+
default:
|
|
483
|
+
return `explainPath: no path from "${from}" to "${to}"`;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
|
|
487
|
+
for (const step of steps) {
|
|
488
|
+
const arrow = step.hop === 0 ? "\xB7" : "\u2193";
|
|
489
|
+
const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
|
|
490
|
+
lines.push(head);
|
|
491
|
+
if ("value" in step) {
|
|
492
|
+
lines.push(` value: ${formatValue(step.value)}`);
|
|
493
|
+
}
|
|
494
|
+
if (step.reason != null) {
|
|
495
|
+
lines.push(` reason: ${step.reason}`);
|
|
496
|
+
}
|
|
497
|
+
if (step.lastMutation != null) {
|
|
498
|
+
const a = step.lastMutation.actor;
|
|
499
|
+
lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return lines.join("\n");
|
|
503
|
+
}
|
|
504
|
+
function formatValue(v) {
|
|
505
|
+
if (v === void 0) return "<sentinel>";
|
|
506
|
+
if (v === null) return "null";
|
|
507
|
+
if (typeof v === "string") return JSON.stringify(v);
|
|
508
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
|
509
|
+
try {
|
|
510
|
+
const s = JSON.stringify(v);
|
|
511
|
+
return s.length > 80 ? `${s.slice(0, 77)}...` : s;
|
|
512
|
+
} catch {
|
|
513
|
+
return String(v);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
320
517
|
// src/core/batch.ts
|
|
321
518
|
var MAX_DRAIN_ITERATIONS = 1e3;
|
|
322
519
|
var batchDepth = 0;
|
|
@@ -1077,6 +1274,12 @@ var NodeImpl = class _NodeImpl {
|
|
|
1077
1274
|
_autoError;
|
|
1078
1275
|
_pausable;
|
|
1079
1276
|
_guard;
|
|
1277
|
+
/**
|
|
1278
|
+
* @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
|
|
1279
|
+
* (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
|
|
1280
|
+
* write/signal/observe checks AND the original `_guard` with every entry here.
|
|
1281
|
+
*/
|
|
1282
|
+
_extraGuards;
|
|
1080
1283
|
_hashFn;
|
|
1081
1284
|
_versioning;
|
|
1082
1285
|
/**
|
|
@@ -1250,18 +1453,61 @@ var NodeImpl = class _NodeImpl {
|
|
|
1250
1453
|
if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
|
|
1251
1454
|
};
|
|
1252
1455
|
}
|
|
1456
|
+
/**
|
|
1457
|
+
* @internal Push an additional guard onto this node. Effective enforcement
|
|
1458
|
+
* is the AND of `_guard` and every guard pushed via this hook — any one
|
|
1459
|
+
* rejecting throws {@link GuardDenied}. Returns a disposer that removes
|
|
1460
|
+
* the pushed guard. Multiple guards may be stacked simultaneously.
|
|
1461
|
+
*
|
|
1462
|
+
* Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
|
|
1463
|
+
* runtime constraint enforcement onto an existing graph without rebuilding
|
|
1464
|
+
* its nodes. Pre-1.0 internal API; not part of the public surface.
|
|
1465
|
+
*
|
|
1466
|
+
* **Identity semantics:** guards are tracked in a `Set`, so pushing the
|
|
1467
|
+
* same `NodeGuard` reference twice is a single registration. Wrap each
|
|
1468
|
+
* push in a unique closure if independent stacking is needed.
|
|
1469
|
+
*
|
|
1470
|
+
* **Iteration order:** insertion-ordered (`Set` semantics). Determinism
|
|
1471
|
+
* follows from single-threaded JS execution; nested re-entry from inside
|
|
1472
|
+
* a guard body (push/pop while iterating) is undefined-but-survivable.
|
|
1473
|
+
*/
|
|
1474
|
+
_pushGuard(guard) {
|
|
1475
|
+
if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
|
|
1476
|
+
this._extraGuards.add(guard);
|
|
1477
|
+
return () => {
|
|
1478
|
+
this._extraGuards?.delete(guard);
|
|
1479
|
+
if (this._extraGuards?.size === 0) this._extraGuards = void 0;
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1253
1482
|
allowsObserve(actor) {
|
|
1254
|
-
if (this._guard == null) return true;
|
|
1255
|
-
|
|
1483
|
+
if (this._guard == null && this._extraGuards == null) return true;
|
|
1484
|
+
const a = normalizeActor(actor);
|
|
1485
|
+
if (this._guard != null && !this._guard(a, "observe")) return false;
|
|
1486
|
+
if (this._extraGuards != null) {
|
|
1487
|
+
for (const eg of this._extraGuards) {
|
|
1488
|
+
if (!eg(a, "observe")) return false;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
return true;
|
|
1256
1492
|
}
|
|
1257
1493
|
// --- Guard helper ---
|
|
1258
1494
|
_checkGuard(options) {
|
|
1259
|
-
if (options?.internal
|
|
1495
|
+
if (options?.internal) return;
|
|
1496
|
+
const hasGuard = this._guard != null || this._extraGuards != null;
|
|
1497
|
+
const hasActor = options?.actor != null;
|
|
1498
|
+
if (!hasGuard && !hasActor) return;
|
|
1260
1499
|
const actor = normalizeActor(options?.actor);
|
|
1261
1500
|
const action = options?.delivery === "signal" ? "signal" : "write";
|
|
1262
|
-
if (!this._guard(actor, action)) {
|
|
1501
|
+
if (this._guard != null && !this._guard(actor, action)) {
|
|
1263
1502
|
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
1264
1503
|
}
|
|
1504
|
+
if (this._extraGuards != null) {
|
|
1505
|
+
for (const eg of this._extraGuards) {
|
|
1506
|
+
if (!eg(actor, action)) {
|
|
1507
|
+
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1265
1511
|
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
1266
1512
|
}
|
|
1267
1513
|
// --- Public transport ---
|
|
@@ -2275,6 +2521,10 @@ function describeNode(node2, includeFields) {
|
|
|
2275
2521
|
function state(initial, opts) {
|
|
2276
2522
|
return node([], { ...opts, initial });
|
|
2277
2523
|
}
|
|
2524
|
+
function producer(fn, opts) {
|
|
2525
|
+
const wrapped = (_data, actions, ctx) => fn(actions, ctx) ?? void 0;
|
|
2526
|
+
return node(wrapped, { describeKind: "producer", ...opts });
|
|
2527
|
+
}
|
|
2278
2528
|
|
|
2279
2529
|
// src/extra/timer.ts
|
|
2280
2530
|
var ResettableTimer = class {
|
|
@@ -2871,6 +3121,20 @@ var Graph = class _Graph {
|
|
|
2871
3121
|
_parent = void 0;
|
|
2872
3122
|
_storageDisposers = /* @__PURE__ */ new Set();
|
|
2873
3123
|
_disposers = /* @__PURE__ */ new Set();
|
|
3124
|
+
/**
|
|
3125
|
+
* @internal Lazy `TopologyEvent` producer. Created on first `.topology`
|
|
3126
|
+
* access. Zero cost until something subscribes — producer fn only runs when
|
|
3127
|
+
* the first sink attaches, registering one handler into
|
|
3128
|
+
* {@link Graph._topologyEmitters}.
|
|
3129
|
+
*/
|
|
3130
|
+
_topology;
|
|
3131
|
+
/**
|
|
3132
|
+
* @internal Active emit handlers for the topology producer. Each entry is
|
|
3133
|
+
* the closure registered by the producer fn on activation; cleared on
|
|
3134
|
+
* deactivation. `_emitTopology` broadcasts through every entry (there is at
|
|
3135
|
+
* most one per activation cycle of the producer).
|
|
3136
|
+
*/
|
|
3137
|
+
_topologyEmitters = /* @__PURE__ */ new Set();
|
|
2874
3138
|
/**
|
|
2875
3139
|
* @param name - Non-empty graph id (must not contain `::` and must not
|
|
2876
3140
|
* equal the reserved meta segment `__meta__`).
|
|
@@ -2910,6 +3174,55 @@ var Graph = class _Graph {
|
|
|
2910
3174
|
return out;
|
|
2911
3175
|
}
|
|
2912
3176
|
// ——————————————————————————————————————————————————————————————
|
|
3177
|
+
// Topology companion (structural-change event stream)
|
|
3178
|
+
// ——————————————————————————————————————————————————————————————
|
|
3179
|
+
/**
|
|
3180
|
+
* Reactive stream of structural changes to this graph's own registry
|
|
3181
|
+
* (add / mount / remove). Value mutations live on `observe()`; this
|
|
3182
|
+
* companion only fires when the topology shape changes.
|
|
3183
|
+
*
|
|
3184
|
+
* Lazy: the underlying node is created on first access and activates when
|
|
3185
|
+
* something subscribes. No emission replay — late subscribers do not
|
|
3186
|
+
* receive historical events and should snapshot via {@link Graph.describe}
|
|
3187
|
+
* before listening for incremental changes. Events that fire while the
|
|
3188
|
+
* producer has zero subscribers are dropped (no retention).
|
|
3189
|
+
*
|
|
3190
|
+
* Own-graph only: a parent's `topology` does NOT emit for structural
|
|
3191
|
+
* changes inside a mounted child. Transitive consumers subscribe to each
|
|
3192
|
+
* child's topology separately (recurse through `topology`'s own "added"
|
|
3193
|
+
* events with `nodeKind: "mount"` to discover new children).
|
|
3194
|
+
*
|
|
3195
|
+
* See {@link TopologyEvent} for payload shape.
|
|
3196
|
+
*
|
|
3197
|
+
* @category observability
|
|
3198
|
+
*/
|
|
3199
|
+
get topology() {
|
|
3200
|
+
if (this._topology == null) {
|
|
3201
|
+
this._topology = producer(
|
|
3202
|
+
(actions) => {
|
|
3203
|
+
const handler = (event) => {
|
|
3204
|
+
actions.emit(event);
|
|
3205
|
+
};
|
|
3206
|
+
this._topologyEmitters.add(handler);
|
|
3207
|
+
return () => {
|
|
3208
|
+
this._topologyEmitters.delete(handler);
|
|
3209
|
+
};
|
|
3210
|
+
},
|
|
3211
|
+
{ name: `${this.name}_topology` }
|
|
3212
|
+
);
|
|
3213
|
+
}
|
|
3214
|
+
return this._topology;
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* @internal Fire a {@link TopologyEvent} to every active subscriber of
|
|
3218
|
+
* `this.topology`. No-op when the topology node has never been accessed or
|
|
3219
|
+
* currently has no sinks — zero cost for graphs nobody observes.
|
|
3220
|
+
*/
|
|
3221
|
+
_emitTopology(event) {
|
|
3222
|
+
if (this._topology == null || this._topologyEmitters.size === 0) return;
|
|
3223
|
+
for (const h of this._topologyEmitters) h(event);
|
|
3224
|
+
}
|
|
3225
|
+
// ——————————————————————————————————————————————————————————————
|
|
2913
3226
|
// Node registry
|
|
2914
3227
|
// ——————————————————————————————————————————————————————————————
|
|
2915
3228
|
/**
|
|
@@ -2939,6 +3252,7 @@ var Graph = class _Graph {
|
|
|
2939
3252
|
}
|
|
2940
3253
|
this._nodes.set(name, node2);
|
|
2941
3254
|
this._nodeToName.set(node2, name);
|
|
3255
|
+
this._emitTopology({ kind: "added", name, nodeKind: "node" });
|
|
2942
3256
|
return node2;
|
|
2943
3257
|
}
|
|
2944
3258
|
/**
|
|
@@ -2979,22 +3293,23 @@ var Graph = class _Graph {
|
|
|
2979
3293
|
assertRegisterableName(name, this.name, "remove");
|
|
2980
3294
|
const child = this._mounts.get(name);
|
|
2981
3295
|
if (child) {
|
|
2982
|
-
const
|
|
3296
|
+
const audit2 = { kind: "mount", nodes: [], mounts: [] };
|
|
2983
3297
|
const targets = [];
|
|
2984
3298
|
child._collectObserveTargets("", targets);
|
|
2985
3299
|
for (const [p, n] of targets) {
|
|
2986
3300
|
if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
|
|
2987
|
-
|
|
3301
|
+
audit2.nodes.push(p);
|
|
2988
3302
|
}
|
|
2989
3303
|
void n;
|
|
2990
3304
|
}
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3305
|
+
audit2.nodes.sort();
|
|
3306
|
+
audit2.mounts.push(name);
|
|
3307
|
+
audit2.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
|
|
2994
3308
|
this._mounts.delete(name);
|
|
2995
3309
|
child._parent = void 0;
|
|
2996
3310
|
teardownMountedGraph(child);
|
|
2997
|
-
|
|
3311
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit2 });
|
|
3312
|
+
return audit2;
|
|
2998
3313
|
}
|
|
2999
3314
|
const node2 = this._nodes.get(name);
|
|
3000
3315
|
if (!node2) {
|
|
@@ -3003,7 +3318,9 @@ var Graph = class _Graph {
|
|
|
3003
3318
|
this._nodes.delete(name);
|
|
3004
3319
|
this._nodeToName.delete(node2);
|
|
3005
3320
|
node2.down([[TEARDOWN]], { internal: true });
|
|
3006
|
-
|
|
3321
|
+
const audit = { kind: "node", nodes: [name], mounts: [] };
|
|
3322
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "node", audit });
|
|
3323
|
+
return audit;
|
|
3007
3324
|
}
|
|
3008
3325
|
/**
|
|
3009
3326
|
* Bulk remove — invokes {@link Graph.remove} for every local name matching
|
|
@@ -3232,6 +3549,7 @@ var Graph = class _Graph {
|
|
|
3232
3549
|
}
|
|
3233
3550
|
this._mounts.set(name, child);
|
|
3234
3551
|
child._parent = this;
|
|
3552
|
+
this._emitTopology({ kind: "added", name, nodeKind: "mount" });
|
|
3235
3553
|
return child;
|
|
3236
3554
|
}
|
|
3237
3555
|
/**
|
|
@@ -3522,6 +3840,33 @@ var Graph = class _Graph {
|
|
|
3522
3840
|
}
|
|
3523
3841
|
return reachable(this.describe(), from, direction, opts);
|
|
3524
3842
|
}
|
|
3843
|
+
/**
|
|
3844
|
+
* Causal walkback: shortest dep-chain from `from` to `to`, enriched with
|
|
3845
|
+
* each node's value, status, last-mutation actor, and reasoning annotation
|
|
3846
|
+
* from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
|
|
3847
|
+
*
|
|
3848
|
+
* @param from - Upstream node (the cause).
|
|
3849
|
+
* @param to - Downstream node (the effect).
|
|
3850
|
+
* @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
|
|
3851
|
+
* and `from === to`, returns the shortest cycle through other nodes
|
|
3852
|
+
* (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
|
|
3853
|
+
* Annotations and lastMutations are collected automatically from the
|
|
3854
|
+
* live graph.
|
|
3855
|
+
*/
|
|
3856
|
+
explain(from, to, opts) {
|
|
3857
|
+
const described = this.describe({ detail: "full" });
|
|
3858
|
+
const annotations = new Map(this._annotations);
|
|
3859
|
+
const lastMutations = /* @__PURE__ */ new Map();
|
|
3860
|
+
for (const [path, n] of Object.entries(described.nodes)) {
|
|
3861
|
+
if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
|
|
3862
|
+
}
|
|
3863
|
+
return explainPath(described, from, to, {
|
|
3864
|
+
...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
|
|
3865
|
+
...opts?.findCycle === true ? { findCycle: true } : {},
|
|
3866
|
+
annotations,
|
|
3867
|
+
lastMutations
|
|
3868
|
+
});
|
|
3869
|
+
}
|
|
3525
3870
|
/**
|
|
3526
3871
|
* @internal Collect all qualified paths in this graph tree matching a
|
|
3527
3872
|
* glob pattern. Used by scoped autoCheckpoint subscription.
|
|
@@ -4200,7 +4545,7 @@ var Graph = class _Graph {
|
|
|
4200
4545
|
return;
|
|
4201
4546
|
}
|
|
4202
4547
|
const nextSeq = s.seq + 1;
|
|
4203
|
-
const timestamp_ns =
|
|
4548
|
+
const timestamp_ns = wallClockNs();
|
|
4204
4549
|
const isFirst = s.lastSnapshot == null;
|
|
4205
4550
|
const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
|
|
4206
4551
|
const record = shouldCompact ? {
|
|
@@ -4614,6 +4959,52 @@ function reachable(described, from, direction, options = {}) {
|
|
|
4614
4959
|
if (options.withDetail) return { paths, depths, truncated };
|
|
4615
4960
|
return paths;
|
|
4616
4961
|
}
|
|
4962
|
+
|
|
4963
|
+
// src/graph/topology-tree.ts
|
|
4964
|
+
function watchTopologyTree(graph, cb) {
|
|
4965
|
+
const subs = /* @__PURE__ */ new Map();
|
|
4966
|
+
const wire = (g, prefix) => {
|
|
4967
|
+
if (subs.has(g)) return;
|
|
4968
|
+
const placeholder = { off: () => {
|
|
4969
|
+
}, prefix };
|
|
4970
|
+
subs.set(g, placeholder);
|
|
4971
|
+
const off = g.topology.subscribe((msgs) => {
|
|
4972
|
+
for (const m of msgs) {
|
|
4973
|
+
if (m[0] !== DATA) continue;
|
|
4974
|
+
const event = m[1];
|
|
4975
|
+
cb(event, g, prefix);
|
|
4976
|
+
if (event.kind === "added" && event.nodeKind === "mount") {
|
|
4977
|
+
const child = g._mounts.get(event.name);
|
|
4978
|
+
if (child instanceof Graph) {
|
|
4979
|
+
const childPrefix = `${prefix}${event.name}::`;
|
|
4980
|
+
wire(child, childPrefix);
|
|
4981
|
+
}
|
|
4982
|
+
} else if (event.kind === "removed" && event.nodeKind === "mount") {
|
|
4983
|
+
const removedPrefix = `${prefix}${event.name}::`;
|
|
4984
|
+
for (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {
|
|
4985
|
+
if (trackedGraph === graph) continue;
|
|
4986
|
+
if (trackedEntry.prefix.startsWith(removedPrefix)) {
|
|
4987
|
+
trackedEntry.off();
|
|
4988
|
+
subs.delete(trackedGraph);
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
});
|
|
4994
|
+
placeholder.off = off;
|
|
4995
|
+
for (const [mountName, child] of g._mounts) {
|
|
4996
|
+
if (child instanceof Graph) {
|
|
4997
|
+
const childPrefix = `${prefix}${mountName}::`;
|
|
4998
|
+
wire(child, childPrefix);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
};
|
|
5002
|
+
wire(graph, "");
|
|
5003
|
+
return () => {
|
|
5004
|
+
for (const entry of subs.values()) entry.off();
|
|
5005
|
+
subs.clear();
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
4617
5008
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4618
5009
|
0 && (module.exports = {
|
|
4619
5010
|
ENVELOPE_VERSION,
|
|
@@ -4622,15 +5013,18 @@ function reachable(described, from, direction, options = {}) {
|
|
|
4622
5013
|
JsonCodec,
|
|
4623
5014
|
SIZEOF_OVERHEAD,
|
|
4624
5015
|
SIZEOF_SYMBOL,
|
|
5016
|
+
SNAPSHOT_VERSION,
|
|
4625
5017
|
createDagCborCodec,
|
|
4626
5018
|
createDagCborZstdCodec,
|
|
4627
5019
|
decodeEnvelope,
|
|
4628
5020
|
diffForWAL,
|
|
4629
5021
|
encodeEnvelope,
|
|
5022
|
+
explainPath,
|
|
4630
5023
|
graphProfile,
|
|
4631
5024
|
reachable,
|
|
4632
5025
|
registerBuiltinCodecs,
|
|
4633
5026
|
replayWAL,
|
|
4634
|
-
sizeof
|
|
5027
|
+
sizeof,
|
|
5028
|
+
watchTopologyTree
|
|
4635
5029
|
});
|
|
4636
5030
|
//# sourceMappingURL=index.cjs.map
|