@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/index.cjs
CHANGED
|
@@ -101,15 +101,20 @@ __export(index_exports, {
|
|
|
101
101
|
ResettableTimer: () => ResettableTimer,
|
|
102
102
|
SIZEOF_OVERHEAD: () => OVERHEAD,
|
|
103
103
|
SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
|
|
104
|
+
SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
|
|
105
|
+
SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
|
|
104
106
|
START: () => START,
|
|
105
107
|
START_MSG: () => START_MSG,
|
|
108
|
+
SurfaceError: () => SurfaceError,
|
|
106
109
|
TEARDOWN: () => TEARDOWN,
|
|
107
110
|
TEARDOWN_MSG: () => TEARDOWN_MSG,
|
|
108
111
|
TEARDOWN_ONLY_BATCH: () => TEARDOWN_ONLY_BATCH,
|
|
109
112
|
TimeoutError: () => TimeoutError,
|
|
110
113
|
accessHintForGuard: () => accessHintForGuard,
|
|
114
|
+
accountability: () => audit_exports,
|
|
111
115
|
advanceVersion: () => advanceVersion,
|
|
112
116
|
ai: () => ai_exports,
|
|
117
|
+
asSurfaceError: () => asSurfaceError,
|
|
113
118
|
audit: () => audit,
|
|
114
119
|
autoTrackNode: () => autoTrackNode,
|
|
115
120
|
batch: () => batch,
|
|
@@ -133,6 +138,7 @@ __export(index_exports, {
|
|
|
133
138
|
cqrs: () => cqrs_exports,
|
|
134
139
|
createDagCborCodec: () => createDagCborCodec,
|
|
135
140
|
createDagCborZstdCodec: () => createDagCborZstdCodec,
|
|
141
|
+
createGraph: () => createGraph,
|
|
136
142
|
createTransport: () => createTransport,
|
|
137
143
|
createVersioning: () => createVersioning,
|
|
138
144
|
createWatermarkController: () => createWatermarkController,
|
|
@@ -144,11 +150,13 @@ __export(index_exports, {
|
|
|
144
150
|
defaultConfig: () => defaultConfig,
|
|
145
151
|
defaultHash: () => defaultHash,
|
|
146
152
|
delay: () => delay,
|
|
153
|
+
deleteSnapshot: () => deleteSnapshot,
|
|
147
154
|
demoShell: () => demo_shell_exports,
|
|
148
155
|
derived: () => derived,
|
|
149
156
|
deserializeError: () => deserializeError,
|
|
150
157
|
dictStorage: () => dictStorage,
|
|
151
158
|
diffForWAL: () => diffForWAL,
|
|
159
|
+
diffSnapshots: () => diffSnapshots,
|
|
152
160
|
distill: () => distill,
|
|
153
161
|
distinctUntilChanged: () => distinctUntilChanged,
|
|
154
162
|
domainTemplates: () => domain_templates_exports,
|
|
@@ -160,6 +168,7 @@ __export(index_exports, {
|
|
|
160
168
|
encodeEnvelope: () => encodeEnvelope,
|
|
161
169
|
escapeRegexChar: () => escapeRegexChar,
|
|
162
170
|
exhaustMap: () => exhaustMap,
|
|
171
|
+
explainPath: () => explainPath,
|
|
163
172
|
exponential: () => exponential,
|
|
164
173
|
externalBundle: () => externalBundle,
|
|
165
174
|
externalProducer: () => externalProducer,
|
|
@@ -215,6 +224,7 @@ __export(index_exports, {
|
|
|
215
224
|
graph: () => graph_exports,
|
|
216
225
|
graphProfile: () => graphProfile,
|
|
217
226
|
graphspec: () => graphspec_exports,
|
|
227
|
+
guarded: () => guarded_execution_exports,
|
|
218
228
|
harness: () => harness_exports,
|
|
219
229
|
indexedDbStorage: () => indexedDbStorage,
|
|
220
230
|
interval: () => interval,
|
|
@@ -224,7 +234,9 @@ __export(index_exports, {
|
|
|
224
234
|
keepalive: () => keepalive,
|
|
225
235
|
last: () => last,
|
|
226
236
|
layout: () => reactive_layout_exports,
|
|
237
|
+
lens: () => lens_exports,
|
|
227
238
|
linear: () => linear,
|
|
239
|
+
listSnapshots: () => listSnapshots,
|
|
228
240
|
lru: () => lru,
|
|
229
241
|
map: () => map2,
|
|
230
242
|
matchesAnyPattern: () => matchesAnyPattern,
|
|
@@ -274,11 +286,15 @@ __export(index_exports, {
|
|
|
274
286
|
replay: () => replay,
|
|
275
287
|
replayWAL: () => replayWAL,
|
|
276
288
|
rescue: () => rescue,
|
|
289
|
+
resilientPipeline: () => resilient_pipeline_exports,
|
|
277
290
|
resolveBackoffPreset: () => resolveBackoffPreset,
|
|
278
291
|
resolveDescribeFields: () => resolveDescribeFields,
|
|
292
|
+
restoreSnapshot: () => restoreSnapshot,
|
|
279
293
|
retry: () => retry,
|
|
280
294
|
retrySource: () => retrySource,
|
|
295
|
+
runReduction: () => runReduction,
|
|
281
296
|
sample: () => sample,
|
|
297
|
+
saveSnapshot: () => saveSnapshot,
|
|
282
298
|
scan: () => scan,
|
|
283
299
|
serializeError: () => serializeError,
|
|
284
300
|
share: () => share,
|
|
@@ -290,6 +306,7 @@ __export(index_exports, {
|
|
|
290
306
|
solid: () => solid_exports,
|
|
291
307
|
sqliteStorage: () => sqliteStorage,
|
|
292
308
|
state: () => state,
|
|
309
|
+
surface: () => surface_exports,
|
|
293
310
|
svelte: () => svelte_exports,
|
|
294
311
|
switchMap: () => switchMap,
|
|
295
312
|
take: () => take,
|
|
@@ -327,6 +344,7 @@ __export(index_exports, {
|
|
|
327
344
|
version: () => version,
|
|
328
345
|
vue: () => vue_exports,
|
|
329
346
|
wallClockNs: () => wallClockNs,
|
|
347
|
+
watchTopologyTree: () => watchTopologyTree,
|
|
330
348
|
window: () => window,
|
|
331
349
|
windowCount: () => windowCount,
|
|
332
350
|
windowTime: () => windowTime,
|
|
@@ -1338,6 +1356,12 @@ var NodeImpl = class _NodeImpl {
|
|
|
1338
1356
|
_autoError;
|
|
1339
1357
|
_pausable;
|
|
1340
1358
|
_guard;
|
|
1359
|
+
/**
|
|
1360
|
+
* @internal Additional guards stacked at runtime via {@link NodeImpl._pushGuard}
|
|
1361
|
+
* (e.g. by `policyEnforcer({ mode: "enforce" })`, roadmap §9.2). Effective
|
|
1362
|
+
* write/signal/observe checks AND the original `_guard` with every entry here.
|
|
1363
|
+
*/
|
|
1364
|
+
_extraGuards;
|
|
1341
1365
|
_hashFn;
|
|
1342
1366
|
_versioning;
|
|
1343
1367
|
/**
|
|
@@ -1511,18 +1535,61 @@ var NodeImpl = class _NodeImpl {
|
|
|
1511
1535
|
if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
|
|
1512
1536
|
};
|
|
1513
1537
|
}
|
|
1538
|
+
/**
|
|
1539
|
+
* @internal Push an additional guard onto this node. Effective enforcement
|
|
1540
|
+
* is the AND of `_guard` and every guard pushed via this hook — any one
|
|
1541
|
+
* rejecting throws {@link GuardDenied}. Returns a disposer that removes
|
|
1542
|
+
* the pushed guard. Multiple guards may be stacked simultaneously.
|
|
1543
|
+
*
|
|
1544
|
+
* Used by `policyEnforcer({ mode: "enforce" })` (roadmap §9.2) to overlay
|
|
1545
|
+
* runtime constraint enforcement onto an existing graph without rebuilding
|
|
1546
|
+
* its nodes. Pre-1.0 internal API; not part of the public surface.
|
|
1547
|
+
*
|
|
1548
|
+
* **Identity semantics:** guards are tracked in a `Set`, so pushing the
|
|
1549
|
+
* same `NodeGuard` reference twice is a single registration. Wrap each
|
|
1550
|
+
* push in a unique closure if independent stacking is needed.
|
|
1551
|
+
*
|
|
1552
|
+
* **Iteration order:** insertion-ordered (`Set` semantics). Determinism
|
|
1553
|
+
* follows from single-threaded JS execution; nested re-entry from inside
|
|
1554
|
+
* a guard body (push/pop while iterating) is undefined-but-survivable.
|
|
1555
|
+
*/
|
|
1556
|
+
_pushGuard(guard) {
|
|
1557
|
+
if (this._extraGuards == null) this._extraGuards = /* @__PURE__ */ new Set();
|
|
1558
|
+
this._extraGuards.add(guard);
|
|
1559
|
+
return () => {
|
|
1560
|
+
this._extraGuards?.delete(guard);
|
|
1561
|
+
if (this._extraGuards?.size === 0) this._extraGuards = void 0;
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1514
1564
|
allowsObserve(actor) {
|
|
1515
|
-
if (this._guard == null) return true;
|
|
1516
|
-
|
|
1565
|
+
if (this._guard == null && this._extraGuards == null) return true;
|
|
1566
|
+
const a = normalizeActor(actor);
|
|
1567
|
+
if (this._guard != null && !this._guard(a, "observe")) return false;
|
|
1568
|
+
if (this._extraGuards != null) {
|
|
1569
|
+
for (const eg of this._extraGuards) {
|
|
1570
|
+
if (!eg(a, "observe")) return false;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
return true;
|
|
1517
1574
|
}
|
|
1518
1575
|
// --- Guard helper ---
|
|
1519
1576
|
_checkGuard(options) {
|
|
1520
|
-
if (options?.internal
|
|
1577
|
+
if (options?.internal) return;
|
|
1578
|
+
const hasGuard = this._guard != null || this._extraGuards != null;
|
|
1579
|
+
const hasActor = options?.actor != null;
|
|
1580
|
+
if (!hasGuard && !hasActor) return;
|
|
1521
1581
|
const actor = normalizeActor(options?.actor);
|
|
1522
1582
|
const action2 = options?.delivery === "signal" ? "signal" : "write";
|
|
1523
|
-
if (!this._guard(actor, action2)) {
|
|
1583
|
+
if (this._guard != null && !this._guard(actor, action2)) {
|
|
1524
1584
|
throw new GuardDenied({ actor, action: action2, nodeName: this.name });
|
|
1525
1585
|
}
|
|
1586
|
+
if (this._extraGuards != null) {
|
|
1587
|
+
for (const eg of this._extraGuards) {
|
|
1588
|
+
if (!eg(actor, action2)) {
|
|
1589
|
+
throw new GuardDenied({ actor, action: action2, nodeName: this.name });
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1526
1593
|
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
1527
1594
|
}
|
|
1528
1595
|
// --- Public transport ---
|
|
@@ -3046,10 +3113,6 @@ function SagaHandler(cqrsName, sagaName, eventNames) {
|
|
|
3046
3113
|
};
|
|
3047
3114
|
}
|
|
3048
3115
|
|
|
3049
|
-
// src/extra/sources.ts
|
|
3050
|
-
var import_node_fs = require("fs");
|
|
3051
|
-
var import_node_path = require("path");
|
|
3052
|
-
|
|
3053
3116
|
// src/extra/cron.ts
|
|
3054
3117
|
function parseField(field, min, max) {
|
|
3055
3118
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -3287,99 +3350,6 @@ function fromEvent(target, type, opts) {
|
|
|
3287
3350
|
return () => target.removeEventListener(type, handler, options);
|
|
3288
3351
|
}, sourceOpts(rest));
|
|
3289
3352
|
}
|
|
3290
|
-
function fromFSWatch(paths, opts) {
|
|
3291
|
-
const list = Array.isArray(paths) ? paths : [paths];
|
|
3292
|
-
if (list.length === 0) {
|
|
3293
|
-
throw new RangeError("fromFSWatch expects at least one path");
|
|
3294
|
-
}
|
|
3295
|
-
const { recursive = true, debounce: debounce2 = 100, include, exclude, ...rest } = opts ?? {};
|
|
3296
|
-
const includePatterns = include?.map(globToRegExp) ?? [];
|
|
3297
|
-
const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
|
|
3298
|
-
globToRegExp
|
|
3299
|
-
);
|
|
3300
|
-
return producer((a) => {
|
|
3301
|
-
const pending = /* @__PURE__ */ new Map();
|
|
3302
|
-
const watchers = [];
|
|
3303
|
-
let stopped = false;
|
|
3304
|
-
let terminalEmitted = false;
|
|
3305
|
-
let generation = 0;
|
|
3306
|
-
const closeWatchers = () => {
|
|
3307
|
-
for (const watcher of watchers.splice(0)) watcher.close();
|
|
3308
|
-
};
|
|
3309
|
-
const emitError = (err) => {
|
|
3310
|
-
if (terminalEmitted) return;
|
|
3311
|
-
terminalEmitted = true;
|
|
3312
|
-
stopped = true;
|
|
3313
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3314
|
-
timer = void 0;
|
|
3315
|
-
pending.clear();
|
|
3316
|
-
closeWatchers();
|
|
3317
|
-
a.down([[ERROR, err]]);
|
|
3318
|
-
};
|
|
3319
|
-
let timer;
|
|
3320
|
-
const flush = (token) => {
|
|
3321
|
-
timer = void 0;
|
|
3322
|
-
if (stopped || terminalEmitted) return;
|
|
3323
|
-
if (pending.size === 0) return;
|
|
3324
|
-
const batchMessages = [];
|
|
3325
|
-
for (const evt of pending.values()) batchMessages.push([DATA, evt]);
|
|
3326
|
-
pending.clear();
|
|
3327
|
-
if (stopped || terminalEmitted || token !== generation) return;
|
|
3328
|
-
a.down(batchMessages);
|
|
3329
|
-
};
|
|
3330
|
-
try {
|
|
3331
|
-
for (const basePath of list) {
|
|
3332
|
-
const watcher = (0, import_node_fs.watch)(
|
|
3333
|
-
basePath,
|
|
3334
|
-
{ recursive },
|
|
3335
|
-
(eventType, fileName) => {
|
|
3336
|
-
if (stopped || terminalEmitted) return;
|
|
3337
|
-
if (fileName == null) return;
|
|
3338
|
-
const rel = String(fileName).replaceAll("\\", "/");
|
|
3339
|
-
const abs = (0, import_node_path.resolve)(basePath, String(fileName));
|
|
3340
|
-
const normalized = abs.replaceAll("\\", "/");
|
|
3341
|
-
const root = (0, import_node_path.resolve)(basePath).replaceAll("\\", "/");
|
|
3342
|
-
const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
|
|
3343
|
-
const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
|
|
3344
|
-
if (!included) return;
|
|
3345
|
-
const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
|
|
3346
|
-
if (excluded) return;
|
|
3347
|
-
let kind = "change";
|
|
3348
|
-
if (eventType === "rename") {
|
|
3349
|
-
try {
|
|
3350
|
-
kind = (0, import_node_fs.existsSync)(normalized) ? "create" : "delete";
|
|
3351
|
-
} catch {
|
|
3352
|
-
kind = "rename";
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
pending.set(normalized, {
|
|
3356
|
-
type: kind,
|
|
3357
|
-
path: normalized,
|
|
3358
|
-
root,
|
|
3359
|
-
relative_path: relForMatch,
|
|
3360
|
-
timestamp_ns: wallClockNs()
|
|
3361
|
-
});
|
|
3362
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3363
|
-
const token = generation;
|
|
3364
|
-
timer = setTimeout(() => flush(token), debounce2);
|
|
3365
|
-
}
|
|
3366
|
-
);
|
|
3367
|
-
watcher.on("error", (err) => emitError(err));
|
|
3368
|
-
watchers.push(watcher);
|
|
3369
|
-
}
|
|
3370
|
-
} catch (err) {
|
|
3371
|
-
emitError(err);
|
|
3372
|
-
}
|
|
3373
|
-
return () => {
|
|
3374
|
-
stopped = true;
|
|
3375
|
-
generation += 1;
|
|
3376
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3377
|
-
timer = void 0;
|
|
3378
|
-
closeWatchers();
|
|
3379
|
-
pending.clear();
|
|
3380
|
-
};
|
|
3381
|
-
}, sourceOpts(rest));
|
|
3382
|
-
}
|
|
3383
3353
|
function fromIter(iterable, opts) {
|
|
3384
3354
|
return producer((a) => {
|
|
3385
3355
|
let cancelled = false;
|
|
@@ -4594,6 +4564,200 @@ var RingBuffer = class {
|
|
|
4594
4564
|
}
|
|
4595
4565
|
};
|
|
4596
4566
|
|
|
4567
|
+
// src/graph/explain.ts
|
|
4568
|
+
function explainPath(described, from, to, opts = {}) {
|
|
4569
|
+
const fromExists = from in described.nodes;
|
|
4570
|
+
const toExists = to in described.nodes;
|
|
4571
|
+
if (!fromExists) return makeFailure(from, to, "no-such-from");
|
|
4572
|
+
if (!toExists) return makeFailure(from, to, "no-such-to");
|
|
4573
|
+
const maxDepth = opts.maxDepth;
|
|
4574
|
+
if (maxDepth != null && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
|
|
4575
|
+
throw new Error(`explainPath: maxDepth must be an integer >= 0`);
|
|
4576
|
+
}
|
|
4577
|
+
if (from === to) {
|
|
4578
|
+
if (opts.findCycle === true) {
|
|
4579
|
+
const cycle = findShortestCycle(described, from, opts);
|
|
4580
|
+
if (cycle != null) return cycle;
|
|
4581
|
+
}
|
|
4582
|
+
const step = buildStep(from, described.nodes[from], 0, opts);
|
|
4583
|
+
return makeSuccess(from, to, [step]);
|
|
4584
|
+
}
|
|
4585
|
+
if (maxDepth === 0) return makeFailure(from, to, "no-path");
|
|
4586
|
+
const result = bfsShortestPath(described, from, to, maxDepth);
|
|
4587
|
+
if (!result.found) {
|
|
4588
|
+
return makeFailure(from, to, result.truncated ? "max-depth-exceeded" : "no-path");
|
|
4589
|
+
}
|
|
4590
|
+
return makeSuccess(from, to, materializeSteps(described, result.pathOrder, opts));
|
|
4591
|
+
}
|
|
4592
|
+
function bfsShortestPath(described, from, to, maxDepth) {
|
|
4593
|
+
const pred = /* @__PURE__ */ new Map();
|
|
4594
|
+
const queue = [{ path: to, depth: 0 }];
|
|
4595
|
+
const visited = /* @__PURE__ */ new Set([to]);
|
|
4596
|
+
let head = 0;
|
|
4597
|
+
let truncated = false;
|
|
4598
|
+
while (head < queue.length) {
|
|
4599
|
+
const cur = queue[head++];
|
|
4600
|
+
if (cur.path === from) break;
|
|
4601
|
+
if (maxDepth != null && cur.depth >= maxDepth) {
|
|
4602
|
+
const node3 = described.nodes[cur.path];
|
|
4603
|
+
if (node3?.deps && node3.deps.length > 0) truncated = true;
|
|
4604
|
+
continue;
|
|
4605
|
+
}
|
|
4606
|
+
const node2 = described.nodes[cur.path];
|
|
4607
|
+
if (node2 == null) continue;
|
|
4608
|
+
const deps = node2.deps ?? [];
|
|
4609
|
+
const slots = /* @__PURE__ */ new Map();
|
|
4610
|
+
for (let i = 0; i < deps.length; i++) {
|
|
4611
|
+
const dep = deps[i];
|
|
4612
|
+
if (!dep) continue;
|
|
4613
|
+
let arr = slots.get(dep);
|
|
4614
|
+
if (arr == null) {
|
|
4615
|
+
arr = [];
|
|
4616
|
+
slots.set(dep, arr);
|
|
4617
|
+
}
|
|
4618
|
+
arr.push(i);
|
|
4619
|
+
}
|
|
4620
|
+
for (const [dep, indices] of slots) {
|
|
4621
|
+
if (visited.has(dep)) continue;
|
|
4622
|
+
visited.add(dep);
|
|
4623
|
+
pred.set(dep, { from: cur.path, depIndices: indices });
|
|
4624
|
+
queue.push({ path: dep, depth: cur.depth + 1 });
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
if (!pred.has(from)) {
|
|
4628
|
+
return { found: false, pathOrder: [], truncated };
|
|
4629
|
+
}
|
|
4630
|
+
const pathOrder = [{ path: from }];
|
|
4631
|
+
let cursor = from;
|
|
4632
|
+
while (cursor !== to) {
|
|
4633
|
+
const p = pred.get(cursor);
|
|
4634
|
+
if (p == null) return { found: false, pathOrder: [], truncated: false };
|
|
4635
|
+
pathOrder[pathOrder.length - 1].depIndices = p.depIndices;
|
|
4636
|
+
pathOrder.push({ path: p.from });
|
|
4637
|
+
cursor = p.from;
|
|
4638
|
+
}
|
|
4639
|
+
return { found: true, pathOrder, truncated: false };
|
|
4640
|
+
}
|
|
4641
|
+
function findShortestCycle(described, start, opts) {
|
|
4642
|
+
const startNode = described.nodes[start];
|
|
4643
|
+
if (startNode == null) return null;
|
|
4644
|
+
const startDeps = startNode.deps ?? [];
|
|
4645
|
+
const selfSlots = [];
|
|
4646
|
+
for (let i = 0; i < startDeps.length; i++) if (startDeps[i] === start) selfSlots.push(i);
|
|
4647
|
+
if (selfSlots.length > 0) {
|
|
4648
|
+
const step0 = buildStep(start, startNode, 0, opts);
|
|
4649
|
+
step0.dep_index = selfSlots[0];
|
|
4650
|
+
const step1 = buildStep(start, startNode, 1, opts);
|
|
4651
|
+
return makeSuccess(start, start, [step0, step1]);
|
|
4652
|
+
}
|
|
4653
|
+
let best = null;
|
|
4654
|
+
for (let i = 0; i < startDeps.length; i++) {
|
|
4655
|
+
const dep = startDeps[i];
|
|
4656
|
+
if (!dep || dep === start) continue;
|
|
4657
|
+
const sub = bfsShortestPath(described, dep, start, opts.maxDepth);
|
|
4658
|
+
if (!sub.found) continue;
|
|
4659
|
+
if (best == null || sub.pathOrder.length < best.pathOrder.length) {
|
|
4660
|
+
best = sub;
|
|
4661
|
+
best = {
|
|
4662
|
+
found: true,
|
|
4663
|
+
pathOrder: [{ path: start, depIndices: [i] }, ...sub.pathOrder],
|
|
4664
|
+
truncated: false
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
if (best == null) return null;
|
|
4669
|
+
return makeSuccess(start, start, materializeSteps(described, best.pathOrder, opts));
|
|
4670
|
+
}
|
|
4671
|
+
function materializeSteps(described, pathOrder, opts) {
|
|
4672
|
+
return pathOrder.map((entry, i) => {
|
|
4673
|
+
const node2 = described.nodes[entry.path];
|
|
4674
|
+
const step = buildStep(entry.path, node2, i, opts);
|
|
4675
|
+
if (entry.depIndices != null && entry.depIndices.length > 0) {
|
|
4676
|
+
step.dep_index = entry.depIndices[0];
|
|
4677
|
+
if (entry.depIndices.length > 1) step.dep_indices = [...entry.depIndices];
|
|
4678
|
+
}
|
|
4679
|
+
return step;
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
function buildStep(path, node2, hop, opts) {
|
|
4683
|
+
const step = {
|
|
4684
|
+
path,
|
|
4685
|
+
type: node2.type,
|
|
4686
|
+
hop
|
|
4687
|
+
};
|
|
4688
|
+
if (node2.status !== void 0) step.status = node2.status;
|
|
4689
|
+
if ("value" in node2) step.value = node2.value;
|
|
4690
|
+
if (node2.v != null) step.v = node2.v;
|
|
4691
|
+
const annotation = opts.annotations?.get(path) ?? node2.reason;
|
|
4692
|
+
if (annotation != null) step.reason = annotation;
|
|
4693
|
+
const lastMutation = opts.lastMutations?.get(path) ?? node2.lastMutation;
|
|
4694
|
+
if (lastMutation != null) step.lastMutation = lastMutation;
|
|
4695
|
+
return step;
|
|
4696
|
+
}
|
|
4697
|
+
function makeSuccess(from, to, steps) {
|
|
4698
|
+
return finalize(from, to, true, "ok", steps);
|
|
4699
|
+
}
|
|
4700
|
+
function makeFailure(from, to, reason) {
|
|
4701
|
+
return finalize(from, to, false, reason, []);
|
|
4702
|
+
}
|
|
4703
|
+
function finalize(from, to, found, reason, steps) {
|
|
4704
|
+
const text = renderChain(from, to, found, reason, steps);
|
|
4705
|
+
return {
|
|
4706
|
+
from,
|
|
4707
|
+
to,
|
|
4708
|
+
found,
|
|
4709
|
+
reason,
|
|
4710
|
+
steps,
|
|
4711
|
+
text,
|
|
4712
|
+
toJSON() {
|
|
4713
|
+
return { from, to, found, reason, steps };
|
|
4714
|
+
}
|
|
4715
|
+
};
|
|
4716
|
+
}
|
|
4717
|
+
function renderChain(from, to, found, reason, steps) {
|
|
4718
|
+
if (!found) {
|
|
4719
|
+
switch (reason) {
|
|
4720
|
+
case "no-such-from":
|
|
4721
|
+
return `explainPath: no node named "${from}"`;
|
|
4722
|
+
case "no-such-to":
|
|
4723
|
+
return `explainPath: no node named "${to}"`;
|
|
4724
|
+
case "max-depth-exceeded":
|
|
4725
|
+
return `explainPath: no path from "${from}" to "${to}" within maxDepth`;
|
|
4726
|
+
default:
|
|
4727
|
+
return `explainPath: no path from "${from}" to "${to}"`;
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
const lines = [`Causal path: ${from} \u2192 ${to} (${steps.length} step(s))`];
|
|
4731
|
+
for (const step of steps) {
|
|
4732
|
+
const arrow = step.hop === 0 ? "\xB7" : "\u2193";
|
|
4733
|
+
const head = ` ${arrow} ${step.path} (${step.type}${step.status ? `/${step.status}` : ""})`;
|
|
4734
|
+
lines.push(head);
|
|
4735
|
+
if ("value" in step) {
|
|
4736
|
+
lines.push(` value: ${formatValue(step.value)}`);
|
|
4737
|
+
}
|
|
4738
|
+
if (step.reason != null) {
|
|
4739
|
+
lines.push(` reason: ${step.reason}`);
|
|
4740
|
+
}
|
|
4741
|
+
if (step.lastMutation != null) {
|
|
4742
|
+
const a = step.lastMutation.actor;
|
|
4743
|
+
lines.push(` actor: ${a.type}${a.id ? `:${a.id}` : ""}`);
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
return lines.join("\n");
|
|
4747
|
+
}
|
|
4748
|
+
function formatValue(v) {
|
|
4749
|
+
if (v === void 0) return "<sentinel>";
|
|
4750
|
+
if (v === null) return "null";
|
|
4751
|
+
if (typeof v === "string") return JSON.stringify(v);
|
|
4752
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
|
4753
|
+
try {
|
|
4754
|
+
const s = JSON.stringify(v);
|
|
4755
|
+
return s.length > 80 ? `${s.slice(0, 77)}...` : s;
|
|
4756
|
+
} catch {
|
|
4757
|
+
return String(v);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4597
4761
|
// src/extra/utils/sizeof.ts
|
|
4598
4762
|
var OVERHEAD = {
|
|
4599
4763
|
object: 56,
|
|
@@ -5227,6 +5391,20 @@ var Graph = class _Graph {
|
|
|
5227
5391
|
_parent = void 0;
|
|
5228
5392
|
_storageDisposers = /* @__PURE__ */ new Set();
|
|
5229
5393
|
_disposers = /* @__PURE__ */ new Set();
|
|
5394
|
+
/**
|
|
5395
|
+
* @internal Lazy `TopologyEvent` producer. Created on first `.topology`
|
|
5396
|
+
* access. Zero cost until something subscribes — producer fn only runs when
|
|
5397
|
+
* the first sink attaches, registering one handler into
|
|
5398
|
+
* {@link Graph._topologyEmitters}.
|
|
5399
|
+
*/
|
|
5400
|
+
_topology;
|
|
5401
|
+
/**
|
|
5402
|
+
* @internal Active emit handlers for the topology producer. Each entry is
|
|
5403
|
+
* the closure registered by the producer fn on activation; cleared on
|
|
5404
|
+
* deactivation. `_emitTopology` broadcasts through every entry (there is at
|
|
5405
|
+
* most one per activation cycle of the producer).
|
|
5406
|
+
*/
|
|
5407
|
+
_topologyEmitters = /* @__PURE__ */ new Set();
|
|
5230
5408
|
/**
|
|
5231
5409
|
* @param name - Non-empty graph id (must not contain `::` and must not
|
|
5232
5410
|
* equal the reserved meta segment `__meta__`).
|
|
@@ -5266,6 +5444,55 @@ var Graph = class _Graph {
|
|
|
5266
5444
|
return out;
|
|
5267
5445
|
}
|
|
5268
5446
|
// ——————————————————————————————————————————————————————————————
|
|
5447
|
+
// Topology companion (structural-change event stream)
|
|
5448
|
+
// ——————————————————————————————————————————————————————————————
|
|
5449
|
+
/**
|
|
5450
|
+
* Reactive stream of structural changes to this graph's own registry
|
|
5451
|
+
* (add / mount / remove). Value mutations live on `observe()`; this
|
|
5452
|
+
* companion only fires when the topology shape changes.
|
|
5453
|
+
*
|
|
5454
|
+
* Lazy: the underlying node is created on first access and activates when
|
|
5455
|
+
* something subscribes. No emission replay — late subscribers do not
|
|
5456
|
+
* receive historical events and should snapshot via {@link Graph.describe}
|
|
5457
|
+
* before listening for incremental changes. Events that fire while the
|
|
5458
|
+
* producer has zero subscribers are dropped (no retention).
|
|
5459
|
+
*
|
|
5460
|
+
* Own-graph only: a parent's `topology` does NOT emit for structural
|
|
5461
|
+
* changes inside a mounted child. Transitive consumers subscribe to each
|
|
5462
|
+
* child's topology separately (recurse through `topology`'s own "added"
|
|
5463
|
+
* events with `nodeKind: "mount"` to discover new children).
|
|
5464
|
+
*
|
|
5465
|
+
* See {@link TopologyEvent} for payload shape.
|
|
5466
|
+
*
|
|
5467
|
+
* @category observability
|
|
5468
|
+
*/
|
|
5469
|
+
get topology() {
|
|
5470
|
+
if (this._topology == null) {
|
|
5471
|
+
this._topology = producer(
|
|
5472
|
+
(actions) => {
|
|
5473
|
+
const handler = (event) => {
|
|
5474
|
+
actions.emit(event);
|
|
5475
|
+
};
|
|
5476
|
+
this._topologyEmitters.add(handler);
|
|
5477
|
+
return () => {
|
|
5478
|
+
this._topologyEmitters.delete(handler);
|
|
5479
|
+
};
|
|
5480
|
+
},
|
|
5481
|
+
{ name: `${this.name}_topology` }
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5484
|
+
return this._topology;
|
|
5485
|
+
}
|
|
5486
|
+
/**
|
|
5487
|
+
* @internal Fire a {@link TopologyEvent} to every active subscriber of
|
|
5488
|
+
* `this.topology`. No-op when the topology node has never been accessed or
|
|
5489
|
+
* currently has no sinks — zero cost for graphs nobody observes.
|
|
5490
|
+
*/
|
|
5491
|
+
_emitTopology(event) {
|
|
5492
|
+
if (this._topology == null || this._topologyEmitters.size === 0) return;
|
|
5493
|
+
for (const h of this._topologyEmitters) h(event);
|
|
5494
|
+
}
|
|
5495
|
+
// ——————————————————————————————————————————————————————————————
|
|
5269
5496
|
// Node registry
|
|
5270
5497
|
// ——————————————————————————————————————————————————————————————
|
|
5271
5498
|
/**
|
|
@@ -5295,6 +5522,7 @@ var Graph = class _Graph {
|
|
|
5295
5522
|
}
|
|
5296
5523
|
this._nodes.set(name, node2);
|
|
5297
5524
|
this._nodeToName.set(node2, name);
|
|
5525
|
+
this._emitTopology({ kind: "added", name, nodeKind: "node" });
|
|
5298
5526
|
return node2;
|
|
5299
5527
|
}
|
|
5300
5528
|
/**
|
|
@@ -5335,22 +5563,23 @@ var Graph = class _Graph {
|
|
|
5335
5563
|
assertRegisterableName(name, this.name, "remove");
|
|
5336
5564
|
const child = this._mounts.get(name);
|
|
5337
5565
|
if (child) {
|
|
5338
|
-
const
|
|
5566
|
+
const audit3 = { kind: "mount", nodes: [], mounts: [] };
|
|
5339
5567
|
const targets = [];
|
|
5340
5568
|
child._collectObserveTargets("", targets);
|
|
5341
5569
|
for (const [p, n] of targets) {
|
|
5342
5570
|
if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
|
|
5343
|
-
|
|
5571
|
+
audit3.nodes.push(p);
|
|
5344
5572
|
}
|
|
5345
5573
|
void n;
|
|
5346
5574
|
}
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5575
|
+
audit3.nodes.sort();
|
|
5576
|
+
audit3.mounts.push(name);
|
|
5577
|
+
audit3.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
|
|
5350
5578
|
this._mounts.delete(name);
|
|
5351
5579
|
child._parent = void 0;
|
|
5352
5580
|
teardownMountedGraph(child);
|
|
5353
|
-
|
|
5581
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit3 });
|
|
5582
|
+
return audit3;
|
|
5354
5583
|
}
|
|
5355
5584
|
const node2 = this._nodes.get(name);
|
|
5356
5585
|
if (!node2) {
|
|
@@ -5359,7 +5588,9 @@ var Graph = class _Graph {
|
|
|
5359
5588
|
this._nodes.delete(name);
|
|
5360
5589
|
this._nodeToName.delete(node2);
|
|
5361
5590
|
node2.down([[TEARDOWN]], { internal: true });
|
|
5362
|
-
|
|
5591
|
+
const audit2 = { kind: "node", nodes: [name], mounts: [] };
|
|
5592
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "node", audit: audit2 });
|
|
5593
|
+
return audit2;
|
|
5363
5594
|
}
|
|
5364
5595
|
/**
|
|
5365
5596
|
* Bulk remove — invokes {@link Graph.remove} for every local name matching
|
|
@@ -5588,6 +5819,7 @@ var Graph = class _Graph {
|
|
|
5588
5819
|
}
|
|
5589
5820
|
this._mounts.set(name, child);
|
|
5590
5821
|
child._parent = this;
|
|
5822
|
+
this._emitTopology({ kind: "added", name, nodeKind: "mount" });
|
|
5591
5823
|
return child;
|
|
5592
5824
|
}
|
|
5593
5825
|
/**
|
|
@@ -5878,6 +6110,33 @@ var Graph = class _Graph {
|
|
|
5878
6110
|
}
|
|
5879
6111
|
return reachable(this.describe(), from, direction, opts);
|
|
5880
6112
|
}
|
|
6113
|
+
/**
|
|
6114
|
+
* Causal walkback: shortest dep-chain from `from` to `to`, enriched with
|
|
6115
|
+
* each node's value, status, last-mutation actor, and reasoning annotation
|
|
6116
|
+
* from {@link Graph.trace}. Wraps {@link explainPath} (roadmap §9.2).
|
|
6117
|
+
*
|
|
6118
|
+
* @param from - Upstream node (the cause).
|
|
6119
|
+
* @param to - Downstream node (the effect).
|
|
6120
|
+
* @param opts - Optional `maxDepth` and `findCycle`. When `findCycle:true`
|
|
6121
|
+
* and `from === to`, returns the shortest cycle through other nodes
|
|
6122
|
+
* (useful for diagnosing feedback loops, COMPOSITION-GUIDE §7).
|
|
6123
|
+
* Annotations and lastMutations are collected automatically from the
|
|
6124
|
+
* live graph.
|
|
6125
|
+
*/
|
|
6126
|
+
explain(from, to, opts) {
|
|
6127
|
+
const described = this.describe({ detail: "full" });
|
|
6128
|
+
const annotations = new Map(this._annotations);
|
|
6129
|
+
const lastMutations = /* @__PURE__ */ new Map();
|
|
6130
|
+
for (const [path, n] of Object.entries(described.nodes)) {
|
|
6131
|
+
if (n.lastMutation != null) lastMutations.set(path, n.lastMutation);
|
|
6132
|
+
}
|
|
6133
|
+
return explainPath(described, from, to, {
|
|
6134
|
+
...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
|
|
6135
|
+
...opts?.findCycle === true ? { findCycle: true } : {},
|
|
6136
|
+
annotations,
|
|
6137
|
+
lastMutations
|
|
6138
|
+
});
|
|
6139
|
+
}
|
|
5881
6140
|
/**
|
|
5882
6141
|
* @internal Collect all qualified paths in this graph tree matching a
|
|
5883
6142
|
* glob pattern. Used by scoped autoCheckpoint subscription.
|
|
@@ -6556,7 +6815,7 @@ var Graph = class _Graph {
|
|
|
6556
6815
|
return;
|
|
6557
6816
|
}
|
|
6558
6817
|
const nextSeq = s.seq + 1;
|
|
6559
|
-
const timestamp_ns =
|
|
6818
|
+
const timestamp_ns = wallClockNs();
|
|
6560
6819
|
const isFirst = s.lastSnapshot == null;
|
|
6561
6820
|
const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
|
|
6562
6821
|
const record = shouldCompact ? {
|
|
@@ -7330,18 +7589,67 @@ __export(graph_exports, {
|
|
|
7330
7589
|
JsonCodec: () => JsonCodec,
|
|
7331
7590
|
SIZEOF_OVERHEAD: () => OVERHEAD,
|
|
7332
7591
|
SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
|
|
7592
|
+
SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
|
|
7333
7593
|
createDagCborCodec: () => createDagCborCodec,
|
|
7334
7594
|
createDagCborZstdCodec: () => createDagCborZstdCodec,
|
|
7335
7595
|
decodeEnvelope: () => decodeEnvelope,
|
|
7336
7596
|
diffForWAL: () => diffForWAL,
|
|
7337
7597
|
encodeEnvelope: () => encodeEnvelope,
|
|
7598
|
+
explainPath: () => explainPath,
|
|
7338
7599
|
graphProfile: () => graphProfile,
|
|
7339
7600
|
reachable: () => reachable,
|
|
7340
7601
|
registerBuiltinCodecs: () => registerBuiltinCodecs,
|
|
7341
7602
|
replayWAL: () => replayWAL,
|
|
7342
|
-
sizeof: () => sizeof
|
|
7603
|
+
sizeof: () => sizeof,
|
|
7604
|
+
watchTopologyTree: () => watchTopologyTree
|
|
7343
7605
|
});
|
|
7344
7606
|
|
|
7607
|
+
// src/graph/topology-tree.ts
|
|
7608
|
+
function watchTopologyTree(graph, cb) {
|
|
7609
|
+
const subs = /* @__PURE__ */ new Map();
|
|
7610
|
+
const wire = (g, prefix) => {
|
|
7611
|
+
if (subs.has(g)) return;
|
|
7612
|
+
const placeholder = { off: () => {
|
|
7613
|
+
}, prefix };
|
|
7614
|
+
subs.set(g, placeholder);
|
|
7615
|
+
const off = g.topology.subscribe((msgs) => {
|
|
7616
|
+
for (const m of msgs) {
|
|
7617
|
+
if (m[0] !== DATA) continue;
|
|
7618
|
+
const event = m[1];
|
|
7619
|
+
cb(event, g, prefix);
|
|
7620
|
+
if (event.kind === "added" && event.nodeKind === "mount") {
|
|
7621
|
+
const child = g._mounts.get(event.name);
|
|
7622
|
+
if (child instanceof Graph) {
|
|
7623
|
+
const childPrefix = `${prefix}${event.name}::`;
|
|
7624
|
+
wire(child, childPrefix);
|
|
7625
|
+
}
|
|
7626
|
+
} else if (event.kind === "removed" && event.nodeKind === "mount") {
|
|
7627
|
+
const removedPrefix = `${prefix}${event.name}::`;
|
|
7628
|
+
for (const [trackedGraph, trackedEntry] of Array.from(subs.entries())) {
|
|
7629
|
+
if (trackedGraph === graph) continue;
|
|
7630
|
+
if (trackedEntry.prefix.startsWith(removedPrefix)) {
|
|
7631
|
+
trackedEntry.off();
|
|
7632
|
+
subs.delete(trackedGraph);
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
});
|
|
7638
|
+
placeholder.off = off;
|
|
7639
|
+
for (const [mountName, child] of g._mounts) {
|
|
7640
|
+
if (child instanceof Graph) {
|
|
7641
|
+
const childPrefix = `${prefix}${mountName}::`;
|
|
7642
|
+
wire(child, childPrefix);
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7645
|
+
};
|
|
7646
|
+
wire(graph, "");
|
|
7647
|
+
return () => {
|
|
7648
|
+
for (const entry of subs.values()) entry.off();
|
|
7649
|
+
subs.clear();
|
|
7650
|
+
};
|
|
7651
|
+
}
|
|
7652
|
+
|
|
7345
7653
|
// src/patterns/_internal.ts
|
|
7346
7654
|
function emitToMeta(metaNode, value) {
|
|
7347
7655
|
if (metaNode == null) return;
|
|
@@ -14210,71 +14518,212 @@ function reactiveList(initial, options = {}) {
|
|
|
14210
14518
|
};
|
|
14211
14519
|
}
|
|
14212
14520
|
|
|
14213
|
-
// src/extra/
|
|
14214
|
-
var
|
|
14215
|
-
var
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
function sortJsonValue2(value) {
|
|
14219
|
-
if (value === null || typeof value !== "object") return value;
|
|
14220
|
-
if (Array.isArray(value)) return value.map(sortJsonValue2);
|
|
14221
|
-
const obj = value;
|
|
14222
|
-
const keys = Object.keys(obj).sort();
|
|
14223
|
-
const out = {};
|
|
14224
|
-
for (const k of keys) out[k] = sortJsonValue2(obj[k]);
|
|
14225
|
-
return out;
|
|
14226
|
-
}
|
|
14227
|
-
function stableJsonString(data) {
|
|
14228
|
-
return JSON.stringify(sortJsonValue2(data), void 0, 0);
|
|
14229
|
-
}
|
|
14230
|
-
function memoryStorage() {
|
|
14231
|
-
const data = /* @__PURE__ */ new Map();
|
|
14232
|
-
return {
|
|
14233
|
-
save(key, record) {
|
|
14234
|
-
data.set(key, JSON.parse(JSON.stringify(record)));
|
|
14235
|
-
},
|
|
14236
|
-
load(key) {
|
|
14237
|
-
const v = data.get(key);
|
|
14238
|
-
return v === void 0 ? null : JSON.parse(JSON.stringify(v));
|
|
14239
|
-
},
|
|
14240
|
-
clear(key) {
|
|
14241
|
-
data.delete(key);
|
|
14242
|
-
}
|
|
14243
|
-
};
|
|
14244
|
-
}
|
|
14245
|
-
function dictStorage(storage) {
|
|
14246
|
-
return {
|
|
14247
|
-
save(key, record) {
|
|
14248
|
-
storage[key] = JSON.parse(JSON.stringify(record));
|
|
14249
|
-
},
|
|
14250
|
-
load(key) {
|
|
14251
|
-
const raw = storage[key];
|
|
14252
|
-
return raw === void 0 ? null : JSON.parse(JSON.stringify(raw));
|
|
14253
|
-
},
|
|
14254
|
-
clear(key) {
|
|
14255
|
-
delete storage[key];
|
|
14256
|
-
}
|
|
14257
|
-
};
|
|
14521
|
+
// src/extra/sources-fs.ts
|
|
14522
|
+
var import_node_fs = require("node:fs");
|
|
14523
|
+
var import_node_path = require("node:path");
|
|
14524
|
+
function sourceOpts4(opts) {
|
|
14525
|
+
return { describeKind: "producer", ...opts };
|
|
14258
14526
|
}
|
|
14259
|
-
function
|
|
14260
|
-
const
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
|
|
14277
|
-
|
|
14527
|
+
function fromFSWatch(paths, opts) {
|
|
14528
|
+
const list = Array.isArray(paths) ? paths : [paths];
|
|
14529
|
+
if (list.length === 0) {
|
|
14530
|
+
throw new RangeError("fromFSWatch expects at least one path");
|
|
14531
|
+
}
|
|
14532
|
+
const { recursive = true, debounce: debounce2 = 100, include, exclude, ...rest } = opts ?? {};
|
|
14533
|
+
const includePatterns = include?.map(globToRegExp) ?? [];
|
|
14534
|
+
const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
|
|
14535
|
+
globToRegExp
|
|
14536
|
+
);
|
|
14537
|
+
return producer((a) => {
|
|
14538
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14539
|
+
const watchers = [];
|
|
14540
|
+
let stopped = false;
|
|
14541
|
+
let terminalEmitted = false;
|
|
14542
|
+
let generation = 0;
|
|
14543
|
+
const closeWatchers = () => {
|
|
14544
|
+
for (const watcher of watchers.splice(0)) watcher.close();
|
|
14545
|
+
};
|
|
14546
|
+
const emitError = (err) => {
|
|
14547
|
+
if (terminalEmitted) return;
|
|
14548
|
+
terminalEmitted = true;
|
|
14549
|
+
stopped = true;
|
|
14550
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
14551
|
+
timer = void 0;
|
|
14552
|
+
pending.clear();
|
|
14553
|
+
closeWatchers();
|
|
14554
|
+
a.down([[ERROR, err]]);
|
|
14555
|
+
};
|
|
14556
|
+
let timer;
|
|
14557
|
+
const flush = (token) => {
|
|
14558
|
+
timer = void 0;
|
|
14559
|
+
if (stopped || terminalEmitted) return;
|
|
14560
|
+
if (pending.size === 0) return;
|
|
14561
|
+
const batchMessages = [];
|
|
14562
|
+
for (const evt of pending.values()) batchMessages.push([DATA, evt]);
|
|
14563
|
+
pending.clear();
|
|
14564
|
+
if (stopped || terminalEmitted || token !== generation) return;
|
|
14565
|
+
a.down(batchMessages);
|
|
14566
|
+
};
|
|
14567
|
+
try {
|
|
14568
|
+
for (const basePath of list) {
|
|
14569
|
+
const watcher = (0, import_node_fs.watch)(
|
|
14570
|
+
basePath,
|
|
14571
|
+
{ recursive },
|
|
14572
|
+
(eventType, fileName) => {
|
|
14573
|
+
if (stopped || terminalEmitted) return;
|
|
14574
|
+
if (fileName == null) return;
|
|
14575
|
+
const rel = String(fileName).replaceAll("\\", "/");
|
|
14576
|
+
const abs = (0, import_node_path.resolve)(basePath, String(fileName));
|
|
14577
|
+
const normalized = abs.replaceAll("\\", "/");
|
|
14578
|
+
const root = (0, import_node_path.resolve)(basePath).replaceAll("\\", "/");
|
|
14579
|
+
const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
|
|
14580
|
+
const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
|
|
14581
|
+
if (!included) return;
|
|
14582
|
+
const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
|
|
14583
|
+
if (excluded) return;
|
|
14584
|
+
let kind = "change";
|
|
14585
|
+
if (eventType === "rename") {
|
|
14586
|
+
try {
|
|
14587
|
+
kind = (0, import_node_fs.existsSync)(normalized) ? "create" : "delete";
|
|
14588
|
+
} catch {
|
|
14589
|
+
kind = "rename";
|
|
14590
|
+
}
|
|
14591
|
+
}
|
|
14592
|
+
pending.set(normalized, {
|
|
14593
|
+
type: kind,
|
|
14594
|
+
path: normalized,
|
|
14595
|
+
root,
|
|
14596
|
+
relative_path: relForMatch,
|
|
14597
|
+
timestamp_ns: wallClockNs()
|
|
14598
|
+
});
|
|
14599
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
14600
|
+
const token = generation;
|
|
14601
|
+
timer = setTimeout(() => flush(token), debounce2);
|
|
14602
|
+
}
|
|
14603
|
+
);
|
|
14604
|
+
watcher.on("error", (err) => emitError(err));
|
|
14605
|
+
watchers.push(watcher);
|
|
14606
|
+
}
|
|
14607
|
+
} catch (err) {
|
|
14608
|
+
emitError(err);
|
|
14609
|
+
}
|
|
14610
|
+
return () => {
|
|
14611
|
+
stopped = true;
|
|
14612
|
+
generation += 1;
|
|
14613
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
14614
|
+
timer = void 0;
|
|
14615
|
+
closeWatchers();
|
|
14616
|
+
pending.clear();
|
|
14617
|
+
};
|
|
14618
|
+
}, sourceOpts4(rest));
|
|
14619
|
+
}
|
|
14620
|
+
|
|
14621
|
+
// src/extra/storage.ts
|
|
14622
|
+
var import_node_crypto = require("node:crypto");
|
|
14623
|
+
var import_node_fs2 = require("node:fs");
|
|
14624
|
+
var import_node_path2 = require("node:path");
|
|
14625
|
+
var import_node_sqlite = require("node:sqlite");
|
|
14626
|
+
function sortJsonValue2(value) {
|
|
14627
|
+
if (value === null || typeof value !== "object") return value;
|
|
14628
|
+
if (Array.isArray(value)) return value.map(sortJsonValue2);
|
|
14629
|
+
const obj = value;
|
|
14630
|
+
const keys = Object.keys(obj).sort();
|
|
14631
|
+
const out = {};
|
|
14632
|
+
for (const k of keys) out[k] = sortJsonValue2(obj[k]);
|
|
14633
|
+
return out;
|
|
14634
|
+
}
|
|
14635
|
+
function stableJsonString(data) {
|
|
14636
|
+
return JSON.stringify(sortJsonValue2(data), void 0, 0);
|
|
14637
|
+
}
|
|
14638
|
+
function memoryStorage() {
|
|
14639
|
+
const data = /* @__PURE__ */ new Map();
|
|
14640
|
+
return {
|
|
14641
|
+
save(key, record) {
|
|
14642
|
+
data.set(key, JSON.parse(JSON.stringify(record)));
|
|
14643
|
+
},
|
|
14644
|
+
load(key) {
|
|
14645
|
+
const v = data.get(key);
|
|
14646
|
+
return v === void 0 ? null : JSON.parse(JSON.stringify(v));
|
|
14647
|
+
},
|
|
14648
|
+
clear(key) {
|
|
14649
|
+
data.delete(key);
|
|
14650
|
+
},
|
|
14651
|
+
list() {
|
|
14652
|
+
return [...data.keys()].sort();
|
|
14653
|
+
}
|
|
14654
|
+
};
|
|
14655
|
+
}
|
|
14656
|
+
function dictStorage(storage) {
|
|
14657
|
+
return {
|
|
14658
|
+
save(key, record) {
|
|
14659
|
+
storage[key] = JSON.parse(JSON.stringify(record));
|
|
14660
|
+
},
|
|
14661
|
+
load(key) {
|
|
14662
|
+
const raw = storage[key];
|
|
14663
|
+
return raw === void 0 ? null : JSON.parse(JSON.stringify(raw));
|
|
14664
|
+
},
|
|
14665
|
+
clear(key) {
|
|
14666
|
+
delete storage[key];
|
|
14667
|
+
},
|
|
14668
|
+
list() {
|
|
14669
|
+
return Object.keys(storage).sort();
|
|
14670
|
+
}
|
|
14671
|
+
};
|
|
14672
|
+
}
|
|
14673
|
+
function fileStorage(dir) {
|
|
14674
|
+
const encoder = new TextEncoder();
|
|
14675
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
14676
|
+
const pathFor = (key) => {
|
|
14677
|
+
let out = "";
|
|
14678
|
+
for (const ch of key) {
|
|
14679
|
+
if (ch.length === 1 && /[a-zA-Z0-9_-]/.test(ch)) {
|
|
14680
|
+
out += ch;
|
|
14681
|
+
continue;
|
|
14682
|
+
}
|
|
14683
|
+
for (const byte of encoder.encode(ch)) {
|
|
14684
|
+
out += `%${byte.toString(16).padStart(2, "0")}`;
|
|
14685
|
+
}
|
|
14686
|
+
}
|
|
14687
|
+
return (0, import_node_path2.join)(dir, `${out}.json`);
|
|
14688
|
+
};
|
|
14689
|
+
const keyFromFilename = (filename) => {
|
|
14690
|
+
if (!filename.endsWith(".json")) return null;
|
|
14691
|
+
const stem = filename.slice(0, -".json".length);
|
|
14692
|
+
const bytes = [];
|
|
14693
|
+
const encodeAscii = (s) => {
|
|
14694
|
+
for (let i2 = 0; i2 < s.length; i2++) bytes.push(s.charCodeAt(i2));
|
|
14695
|
+
};
|
|
14696
|
+
let i = 0;
|
|
14697
|
+
while (i < stem.length) {
|
|
14698
|
+
const ch = stem[i];
|
|
14699
|
+
if (ch === "%" && i + 2 < stem.length) {
|
|
14700
|
+
const hex = stem.slice(i + 1, i + 3);
|
|
14701
|
+
if (/^[0-9a-f]{2}$/i.test(hex)) {
|
|
14702
|
+
bytes.push(Number.parseInt(hex, 16));
|
|
14703
|
+
i += 3;
|
|
14704
|
+
continue;
|
|
14705
|
+
}
|
|
14706
|
+
}
|
|
14707
|
+
encodeAscii(ch);
|
|
14708
|
+
i += 1;
|
|
14709
|
+
}
|
|
14710
|
+
try {
|
|
14711
|
+
return decoder.decode(new Uint8Array(bytes));
|
|
14712
|
+
} catch {
|
|
14713
|
+
return null;
|
|
14714
|
+
}
|
|
14715
|
+
};
|
|
14716
|
+
return {
|
|
14717
|
+
save(key, record) {
|
|
14718
|
+
(0, import_node_fs2.mkdirSync)(dir, { recursive: true });
|
|
14719
|
+
const filePath = pathFor(key);
|
|
14720
|
+
const payload = `${stableJsonString(record)}
|
|
14721
|
+
`;
|
|
14722
|
+
const base = (0, import_node_path2.basename)(filePath);
|
|
14723
|
+
const d = (0, import_node_path2.dirname)(filePath);
|
|
14724
|
+
const tmp = (0, import_node_path2.join)(d, `.${base}.${(0, import_node_crypto.randomBytes)(8).toString("hex")}.tmp`);
|
|
14725
|
+
try {
|
|
14726
|
+
(0, import_node_fs2.writeFileSync)(tmp, payload, "utf8");
|
|
14278
14727
|
(0, import_node_fs2.renameSync)(tmp, filePath);
|
|
14279
14728
|
} catch (e) {
|
|
14280
14729
|
try {
|
|
@@ -14299,6 +14748,21 @@ function fileStorage(dir) {
|
|
|
14299
14748
|
} catch (e) {
|
|
14300
14749
|
if (e.code !== "ENOENT") throw e;
|
|
14301
14750
|
}
|
|
14751
|
+
},
|
|
14752
|
+
list() {
|
|
14753
|
+
try {
|
|
14754
|
+
const entries = (0, import_node_fs2.readdirSync)(dir);
|
|
14755
|
+
const keys = [];
|
|
14756
|
+
for (const entry of entries) {
|
|
14757
|
+
if (entry.startsWith(".")) continue;
|
|
14758
|
+
const k = keyFromFilename(entry);
|
|
14759
|
+
if (k !== null) keys.push(k);
|
|
14760
|
+
}
|
|
14761
|
+
return keys.sort();
|
|
14762
|
+
} catch (e) {
|
|
14763
|
+
if (e.code === "ENOENT") return [];
|
|
14764
|
+
throw e;
|
|
14765
|
+
}
|
|
14302
14766
|
}
|
|
14303
14767
|
};
|
|
14304
14768
|
}
|
|
@@ -14321,6 +14785,10 @@ function sqliteStorage(path) {
|
|
|
14321
14785
|
clear(key) {
|
|
14322
14786
|
db.prepare(`DELETE FROM graphrefly_checkpoint WHERE k = ?`).run(key);
|
|
14323
14787
|
},
|
|
14788
|
+
list() {
|
|
14789
|
+
const rows = db.prepare(`SELECT k FROM graphrefly_checkpoint ORDER BY k`).all();
|
|
14790
|
+
return rows.map((r) => r.k);
|
|
14791
|
+
},
|
|
14324
14792
|
close() {
|
|
14325
14793
|
try {
|
|
14326
14794
|
db.close();
|
|
@@ -14970,17 +15438,32 @@ function workerSelf(target, opts) {
|
|
|
14970
15438
|
// src/patterns/index.ts
|
|
14971
15439
|
var patterns_exports = {};
|
|
14972
15440
|
__export(patterns_exports, {
|
|
15441
|
+
SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
|
|
15442
|
+
SurfaceError: () => SurfaceError,
|
|
15443
|
+
accountability: () => audit_exports,
|
|
14973
15444
|
ai: () => ai_exports,
|
|
15445
|
+
asSurfaceError: () => asSurfaceError,
|
|
14974
15446
|
cqrs: () => cqrs_exports,
|
|
15447
|
+
createGraph: () => createGraph,
|
|
15448
|
+
deleteSnapshot: () => deleteSnapshot,
|
|
14975
15449
|
demoShell: () => demo_shell_exports,
|
|
15450
|
+
diffSnapshots: () => diffSnapshots,
|
|
14976
15451
|
domainTemplates: () => domain_templates_exports,
|
|
14977
15452
|
graphspec: () => graphspec_exports,
|
|
15453
|
+
guarded: () => guarded_execution_exports,
|
|
14978
15454
|
harness: () => harness_exports,
|
|
14979
15455
|
layout: () => reactive_layout_exports,
|
|
15456
|
+
lens: () => lens_exports,
|
|
15457
|
+
listSnapshots: () => listSnapshots,
|
|
14980
15458
|
memory: () => memory_exports,
|
|
14981
15459
|
messaging: () => messaging_exports,
|
|
14982
15460
|
orchestration: () => orchestration_exports,
|
|
14983
|
-
reduction: () => reduction_exports
|
|
15461
|
+
reduction: () => reduction_exports,
|
|
15462
|
+
resilientPipeline: () => resilient_pipeline_exports,
|
|
15463
|
+
restoreSnapshot: () => restoreSnapshot,
|
|
15464
|
+
runReduction: () => runReduction,
|
|
15465
|
+
saveSnapshot: () => saveSnapshot,
|
|
15466
|
+
surface: () => surface_exports
|
|
14984
15467
|
});
|
|
14985
15468
|
|
|
14986
15469
|
// src/patterns/ai.ts
|
|
@@ -18018,6 +18501,420 @@ async function suggestStrategy(graph, problem, adapter, opts) {
|
|
|
18018
18501
|
};
|
|
18019
18502
|
}
|
|
18020
18503
|
|
|
18504
|
+
// src/patterns/audit.ts
|
|
18505
|
+
var audit_exports = {};
|
|
18506
|
+
__export(audit_exports, {
|
|
18507
|
+
AuditTrailGraph: () => AuditTrailGraph,
|
|
18508
|
+
PolicyEnforcerGraph: () => PolicyEnforcerGraph,
|
|
18509
|
+
auditTrail: () => auditTrail,
|
|
18510
|
+
complianceSnapshot: () => complianceSnapshot,
|
|
18511
|
+
policyEnforcer: () => policyEnforcer,
|
|
18512
|
+
reactiveExplainPath: () => reactiveExplainPath
|
|
18513
|
+
});
|
|
18514
|
+
function auditMeta(kind, extra) {
|
|
18515
|
+
return domainMeta("audit", kind, extra);
|
|
18516
|
+
}
|
|
18517
|
+
var DEFAULT_INCLUDE_TYPES = /* @__PURE__ */ new Set([
|
|
18518
|
+
"data",
|
|
18519
|
+
"error",
|
|
18520
|
+
"complete",
|
|
18521
|
+
"teardown"
|
|
18522
|
+
]);
|
|
18523
|
+
var AuditTrailGraph = class extends Graph {
|
|
18524
|
+
entries;
|
|
18525
|
+
count;
|
|
18526
|
+
_log;
|
|
18527
|
+
_target;
|
|
18528
|
+
constructor(target, opts) {
|
|
18529
|
+
super(opts.name ?? `${target.name}_audit`, opts.graph);
|
|
18530
|
+
this._target = target;
|
|
18531
|
+
this._log = reactiveLog([], {
|
|
18532
|
+
name: "entries",
|
|
18533
|
+
...opts.maxSize != null ? { maxSize: opts.maxSize } : {}
|
|
18534
|
+
});
|
|
18535
|
+
this.entries = this._log.entries;
|
|
18536
|
+
this.add("entries", this.entries);
|
|
18537
|
+
this.count = derived(
|
|
18538
|
+
[this.entries],
|
|
18539
|
+
([snapshot]) => snapshot.length,
|
|
18540
|
+
{ name: "count", describeKind: "derived", meta: auditMeta("count") }
|
|
18541
|
+
);
|
|
18542
|
+
this.add("count", this.count);
|
|
18543
|
+
this.addDisposer(keepalive(this.count));
|
|
18544
|
+
const includeTypes = opts.includeTypes != null ? new Set(opts.includeTypes) : DEFAULT_INCLUDE_TYPES;
|
|
18545
|
+
const filter2 = opts.filter;
|
|
18546
|
+
let seq = 0;
|
|
18547
|
+
const handle = target.observe({ timeline: true, structured: true });
|
|
18548
|
+
const offEvent = handle.onEvent((event) => {
|
|
18549
|
+
if (event.type === "derived") return;
|
|
18550
|
+
const type = event.type;
|
|
18551
|
+
if (!includeTypes.has(type)) return;
|
|
18552
|
+
const path = event.path ?? "";
|
|
18553
|
+
const entry = {
|
|
18554
|
+
seq: seq++,
|
|
18555
|
+
timestamp_ns: event.timestamp_ns ?? monotonicNs(),
|
|
18556
|
+
wall_clock_ns: wallClockNs(),
|
|
18557
|
+
path,
|
|
18558
|
+
type
|
|
18559
|
+
};
|
|
18560
|
+
const node2 = path ? safeNode(target, path) : void 0;
|
|
18561
|
+
const lastMutation = node2?.lastMutation;
|
|
18562
|
+
if (lastMutation != null) entry.actor = lastMutation.actor;
|
|
18563
|
+
if (type === "data") entry.value = event.data;
|
|
18564
|
+
if (type === "error") entry.error = event.data;
|
|
18565
|
+
const reason = path ? safeAnnotation(target, path) : void 0;
|
|
18566
|
+
if (reason != null) entry.reason = reason;
|
|
18567
|
+
if (filter2 != null && !filter2(entry)) return;
|
|
18568
|
+
this._log.append(entry);
|
|
18569
|
+
});
|
|
18570
|
+
this.addDisposer(() => {
|
|
18571
|
+
offEvent();
|
|
18572
|
+
handle.dispose();
|
|
18573
|
+
});
|
|
18574
|
+
this.addDisposer(() => this._log.disposeAllViews());
|
|
18575
|
+
}
|
|
18576
|
+
/** All entries currently in the ring (snapshot). */
|
|
18577
|
+
all() {
|
|
18578
|
+
return this.entries.cache ?? [];
|
|
18579
|
+
}
|
|
18580
|
+
/** Entries matching `path`. Order preserved. */
|
|
18581
|
+
byNode(path) {
|
|
18582
|
+
return this.all().filter((e) => e.path === path);
|
|
18583
|
+
}
|
|
18584
|
+
/** Entries whose `actor.id` matches. Use `byActorType` for type filtering. */
|
|
18585
|
+
byActor(actorId) {
|
|
18586
|
+
return this.all().filter((e) => e.actor?.id === actorId);
|
|
18587
|
+
}
|
|
18588
|
+
/** Entries whose `actor.type` matches (e.g. `"llm"`, `"human"`). */
|
|
18589
|
+
byActorType(type) {
|
|
18590
|
+
return this.all().filter((e) => e.actor?.type === type);
|
|
18591
|
+
}
|
|
18592
|
+
/**
|
|
18593
|
+
* Entries with `timestamp_ns` in `[start_ns, end_ns)` (end exclusive).
|
|
18594
|
+
* Omit `end_ns` to query open-ended.
|
|
18595
|
+
*/
|
|
18596
|
+
byTimeRange(start_ns, end_ns) {
|
|
18597
|
+
return this.all().filter((e) => {
|
|
18598
|
+
if (e.timestamp_ns < start_ns) return false;
|
|
18599
|
+
if (end_ns != null && e.timestamp_ns >= end_ns) return false;
|
|
18600
|
+
return true;
|
|
18601
|
+
});
|
|
18602
|
+
}
|
|
18603
|
+
/** Reference to the audited graph (escape hatch for tooling). */
|
|
18604
|
+
get target() {
|
|
18605
|
+
return this._target;
|
|
18606
|
+
}
|
|
18607
|
+
};
|
|
18608
|
+
function auditTrail(target, opts = {}) {
|
|
18609
|
+
return new AuditTrailGraph(target, opts);
|
|
18610
|
+
}
|
|
18611
|
+
var PolicyEnforcerGraph = class extends Graph {
|
|
18612
|
+
policies;
|
|
18613
|
+
violations;
|
|
18614
|
+
violationCount;
|
|
18615
|
+
_target;
|
|
18616
|
+
_mode;
|
|
18617
|
+
_currentGuard;
|
|
18618
|
+
constructor(target, policies, opts) {
|
|
18619
|
+
super(opts.name ?? `${target.name}_policy`, opts.graph);
|
|
18620
|
+
this._target = target;
|
|
18621
|
+
this._mode = opts.mode ?? "audit";
|
|
18622
|
+
const policiesNode = isNode3(policies) ? policies : state(policies, { name: "policies" });
|
|
18623
|
+
this.policies = policiesNode;
|
|
18624
|
+
this.add("policies", this.policies);
|
|
18625
|
+
this.violations = new TopicGraph("violations", {
|
|
18626
|
+
retainedLimit: opts.violationsLimit ?? 1e3
|
|
18627
|
+
});
|
|
18628
|
+
this.mount("violations", this.violations);
|
|
18629
|
+
this.violationCount = derived(
|
|
18630
|
+
[this.violations.events],
|
|
18631
|
+
([snapshot]) => snapshot.length,
|
|
18632
|
+
{
|
|
18633
|
+
name: "violationCount",
|
|
18634
|
+
describeKind: "derived",
|
|
18635
|
+
meta: auditMeta("policy_violation_count")
|
|
18636
|
+
}
|
|
18637
|
+
);
|
|
18638
|
+
this.add("violationCount", this.violationCount);
|
|
18639
|
+
this.addDisposer(keepalive(this.violationCount));
|
|
18640
|
+
const initialRules = policiesNode.cache ?? [];
|
|
18641
|
+
let latestRules = initialRules;
|
|
18642
|
+
this._currentGuard = policyFromRules(latestRules);
|
|
18643
|
+
const offPolicies = policiesNode.subscribe((msgs) => {
|
|
18644
|
+
for (const m of msgs) {
|
|
18645
|
+
if (m[0] === DATA) {
|
|
18646
|
+
latestRules = m[1] ?? [];
|
|
18647
|
+
this._currentGuard = policyFromRules(latestRules);
|
|
18648
|
+
}
|
|
18649
|
+
}
|
|
18650
|
+
});
|
|
18651
|
+
this.addDisposer(offPolicies);
|
|
18652
|
+
const paths = opts.paths != null ? [...opts.paths] : collectPaths(target);
|
|
18653
|
+
if (this._mode === "enforce") {
|
|
18654
|
+
const restorers = /* @__PURE__ */ new Map();
|
|
18655
|
+
const wrapAndPush = (path) => {
|
|
18656
|
+
if (restorers.has(path)) return;
|
|
18657
|
+
const node2 = safeNode(target, path);
|
|
18658
|
+
if (!(node2 instanceof NodeImpl)) return;
|
|
18659
|
+
const pathGuard = (actor, action2) => {
|
|
18660
|
+
const ok = this._currentGuard(actor, action2);
|
|
18661
|
+
if (!ok) {
|
|
18662
|
+
this._publishViolation(actor, action2, path, "blocked");
|
|
18663
|
+
}
|
|
18664
|
+
return ok;
|
|
18665
|
+
};
|
|
18666
|
+
restorers.set(path, node2._pushGuard(pathGuard));
|
|
18667
|
+
};
|
|
18668
|
+
for (const path of paths) wrapAndPush(path);
|
|
18669
|
+
if (opts.paths == null) {
|
|
18670
|
+
const offTopology = watchTopologyTree(target, (event, emitter, prefix) => {
|
|
18671
|
+
if (event.kind === "added") {
|
|
18672
|
+
if (event.nodeKind === "node") {
|
|
18673
|
+
wrapAndPush(`${prefix}${event.name}`);
|
|
18674
|
+
} else {
|
|
18675
|
+
const child = emitter._mounts.get(event.name);
|
|
18676
|
+
if (!(child instanceof Graph)) return;
|
|
18677
|
+
const mountPrefix = `${prefix}${event.name}::`;
|
|
18678
|
+
const localPaths = collectPaths(child);
|
|
18679
|
+
for (const localPath of localPaths) {
|
|
18680
|
+
wrapAndPush(
|
|
18681
|
+
localPath === "" ? `${prefix}${event.name}` : `${mountPrefix}${localPath}`
|
|
18682
|
+
);
|
|
18683
|
+
}
|
|
18684
|
+
}
|
|
18685
|
+
} else if (event.kind === "removed") {
|
|
18686
|
+
if (event.nodeKind === "node") {
|
|
18687
|
+
const qp = `${prefix}${event.name}`;
|
|
18688
|
+
const r = restorers.get(qp);
|
|
18689
|
+
if (r != null) {
|
|
18690
|
+
r();
|
|
18691
|
+
restorers.delete(qp);
|
|
18692
|
+
}
|
|
18693
|
+
} else {
|
|
18694
|
+
const mountQp = `${prefix}${event.name}`;
|
|
18695
|
+
const mountPrefix = `${mountQp}::`;
|
|
18696
|
+
for (const [p, r] of restorers) {
|
|
18697
|
+
if (p === mountQp || p.startsWith(mountPrefix)) {
|
|
18698
|
+
r();
|
|
18699
|
+
restorers.delete(p);
|
|
18700
|
+
}
|
|
18701
|
+
}
|
|
18702
|
+
}
|
|
18703
|
+
}
|
|
18704
|
+
});
|
|
18705
|
+
this.addDisposer(offTopology);
|
|
18706
|
+
} else {
|
|
18707
|
+
const offCleanup = target.topology.subscribe((msgs) => {
|
|
18708
|
+
for (const m of msgs) {
|
|
18709
|
+
if (m[0] !== DATA) continue;
|
|
18710
|
+
const event = m[1];
|
|
18711
|
+
if (event.kind !== "removed" || event.nodeKind !== "node") continue;
|
|
18712
|
+
const r = restorers.get(event.name);
|
|
18713
|
+
if (r != null) {
|
|
18714
|
+
r();
|
|
18715
|
+
restorers.delete(event.name);
|
|
18716
|
+
}
|
|
18717
|
+
}
|
|
18718
|
+
});
|
|
18719
|
+
this.addDisposer(offCleanup);
|
|
18720
|
+
}
|
|
18721
|
+
this.addDisposer(() => {
|
|
18722
|
+
for (const r of restorers.values()) r();
|
|
18723
|
+
restorers.clear();
|
|
18724
|
+
});
|
|
18725
|
+
} else {
|
|
18726
|
+
const handle = target.observe({ timeline: true, structured: true });
|
|
18727
|
+
const off = handle.onEvent((event) => {
|
|
18728
|
+
if (event.type !== "data" && event.type !== "error") return;
|
|
18729
|
+
const path = event.path ?? "";
|
|
18730
|
+
if (!path) return;
|
|
18731
|
+
if (opts.paths != null && !opts.paths.includes(path)) return;
|
|
18732
|
+
const node2 = safeNode(target, path);
|
|
18733
|
+
const lastMutation = node2?.lastMutation;
|
|
18734
|
+
if (lastMutation == null) return;
|
|
18735
|
+
const action2 = "write";
|
|
18736
|
+
if (this._currentGuard(lastMutation.actor, action2)) return;
|
|
18737
|
+
this._publishViolation(lastMutation.actor, action2, path, "observed");
|
|
18738
|
+
});
|
|
18739
|
+
this.addDisposer(() => {
|
|
18740
|
+
off();
|
|
18741
|
+
handle.dispose();
|
|
18742
|
+
});
|
|
18743
|
+
}
|
|
18744
|
+
}
|
|
18745
|
+
_publishViolation(actor, action2, path, result) {
|
|
18746
|
+
this.violations.publish({
|
|
18747
|
+
timestamp_ns: monotonicNs(),
|
|
18748
|
+
wall_clock_ns: wallClockNs(),
|
|
18749
|
+
path,
|
|
18750
|
+
actor,
|
|
18751
|
+
action: action2,
|
|
18752
|
+
mode: this._mode,
|
|
18753
|
+
result
|
|
18754
|
+
});
|
|
18755
|
+
}
|
|
18756
|
+
/** Snapshot of recorded violations. */
|
|
18757
|
+
all() {
|
|
18758
|
+
return this.violations.retained();
|
|
18759
|
+
}
|
|
18760
|
+
get mode() {
|
|
18761
|
+
return this._mode;
|
|
18762
|
+
}
|
|
18763
|
+
get target() {
|
|
18764
|
+
return this._target;
|
|
18765
|
+
}
|
|
18766
|
+
};
|
|
18767
|
+
function policyEnforcer(target, policies, opts = {}) {
|
|
18768
|
+
return new PolicyEnforcerGraph(target, policies, opts);
|
|
18769
|
+
}
|
|
18770
|
+
function reactiveExplainPath(target, from, to, opts) {
|
|
18771
|
+
let v = 0;
|
|
18772
|
+
const version2 = state(v, { name: "explain_version" });
|
|
18773
|
+
const handle = target.observe({ timeline: true, structured: true });
|
|
18774
|
+
const off = handle.onEvent((event) => {
|
|
18775
|
+
const t = event.type;
|
|
18776
|
+
if (t !== "data" && t !== "error" && t !== "complete" && t !== "teardown") return;
|
|
18777
|
+
v += 1;
|
|
18778
|
+
version2.emit(v);
|
|
18779
|
+
});
|
|
18780
|
+
const explainOpts = {
|
|
18781
|
+
...opts?.maxDepth != null ? { maxDepth: opts.maxDepth } : {},
|
|
18782
|
+
...opts?.findCycle === true ? { findCycle: true } : {}
|
|
18783
|
+
};
|
|
18784
|
+
const node2 = derived([version2], () => target.explain(from, to, explainOpts), {
|
|
18785
|
+
name: opts?.name ?? "explain",
|
|
18786
|
+
describeKind: "derived",
|
|
18787
|
+
equals: (a, b) => a.found === b.found && a.reason === b.reason && a.steps.length === b.steps.length && causalStepsEqual(a.steps, b.steps),
|
|
18788
|
+
meta: auditMeta("explain_path", { from, to })
|
|
18789
|
+
});
|
|
18790
|
+
const stopKeepalive = keepalive(node2);
|
|
18791
|
+
return {
|
|
18792
|
+
node: node2,
|
|
18793
|
+
dispose() {
|
|
18794
|
+
off();
|
|
18795
|
+
handle.dispose();
|
|
18796
|
+
stopKeepalive();
|
|
18797
|
+
}
|
|
18798
|
+
};
|
|
18799
|
+
}
|
|
18800
|
+
function causalStepsEqual(a, b) {
|
|
18801
|
+
for (let i = 0; i < a.length; i++) {
|
|
18802
|
+
const x = a[i];
|
|
18803
|
+
const y = b[i];
|
|
18804
|
+
if (x.path !== y.path) return false;
|
|
18805
|
+
if (x.type !== y.type) return false;
|
|
18806
|
+
if (x.status !== y.status) return false;
|
|
18807
|
+
if (x.hop !== y.hop) return false;
|
|
18808
|
+
if (x.dep_index !== y.dep_index) return false;
|
|
18809
|
+
if (x.reason !== y.reason) return false;
|
|
18810
|
+
if (x.value !== y.value) return false;
|
|
18811
|
+
if (x.lastMutation !== y.lastMutation) return false;
|
|
18812
|
+
const xv = x.v;
|
|
18813
|
+
const yv = y.v;
|
|
18814
|
+
if (xv !== yv) {
|
|
18815
|
+
if (xv == null || yv == null) return false;
|
|
18816
|
+
if (xv.id !== yv.id || xv.version !== yv.version) return false;
|
|
18817
|
+
}
|
|
18818
|
+
}
|
|
18819
|
+
return true;
|
|
18820
|
+
}
|
|
18821
|
+
function complianceSnapshot(target, opts = {}) {
|
|
18822
|
+
const result = {
|
|
18823
|
+
format_version: 1,
|
|
18824
|
+
timestamp_ns: monotonicNs(),
|
|
18825
|
+
wall_clock_ns: wallClockNs(),
|
|
18826
|
+
graph: target.snapshot()
|
|
18827
|
+
};
|
|
18828
|
+
if (opts.actor != null) result.actor = opts.actor;
|
|
18829
|
+
if (opts.audit != null) {
|
|
18830
|
+
const entries = [...opts.audit.all()];
|
|
18831
|
+
result.audit = { count: entries.length, entries };
|
|
18832
|
+
}
|
|
18833
|
+
if (opts.policies != null) {
|
|
18834
|
+
const rules = opts.policies.policies.cache ?? [];
|
|
18835
|
+
result.policies = {
|
|
18836
|
+
mode: opts.policies.mode,
|
|
18837
|
+
rules,
|
|
18838
|
+
violations: [...opts.policies.all()]
|
|
18839
|
+
};
|
|
18840
|
+
}
|
|
18841
|
+
const fingerprint = computeFingerprint(result);
|
|
18842
|
+
return { ...result, fingerprint };
|
|
18843
|
+
}
|
|
18844
|
+
function isNode3(x) {
|
|
18845
|
+
return typeof x === "object" && x !== null && "subscribe" in x;
|
|
18846
|
+
}
|
|
18847
|
+
function safeNode(target, path) {
|
|
18848
|
+
try {
|
|
18849
|
+
return target.node(path);
|
|
18850
|
+
} catch {
|
|
18851
|
+
return void 0;
|
|
18852
|
+
}
|
|
18853
|
+
}
|
|
18854
|
+
function safeAnnotation(target, path) {
|
|
18855
|
+
try {
|
|
18856
|
+
return target.annotation(path);
|
|
18857
|
+
} catch {
|
|
18858
|
+
return void 0;
|
|
18859
|
+
}
|
|
18860
|
+
}
|
|
18861
|
+
function collectPaths(target) {
|
|
18862
|
+
const described = target.describe({ detail: "minimal" });
|
|
18863
|
+
return Object.keys(described.nodes);
|
|
18864
|
+
}
|
|
18865
|
+
function computeFingerprint(value) {
|
|
18866
|
+
return defaultHash(JSON.stringify(canonicalize(value)));
|
|
18867
|
+
}
|
|
18868
|
+
function canonicalize(value) {
|
|
18869
|
+
const stack = /* @__PURE__ */ new Set();
|
|
18870
|
+
const walk = (v) => {
|
|
18871
|
+
if (v === void 0) return { __undefined: true };
|
|
18872
|
+
if (v === null) return null;
|
|
18873
|
+
const t = typeof v;
|
|
18874
|
+
if (t === "bigint") return { __bigint: v.toString() };
|
|
18875
|
+
if (t !== "object") return v;
|
|
18876
|
+
const obj = v;
|
|
18877
|
+
if (stack.has(obj)) return { __circular: true };
|
|
18878
|
+
stack.add(obj);
|
|
18879
|
+
try {
|
|
18880
|
+
if (Array.isArray(obj)) {
|
|
18881
|
+
return obj.map(walk);
|
|
18882
|
+
}
|
|
18883
|
+
if (obj instanceof Date) {
|
|
18884
|
+
return { __date: obj.toISOString() };
|
|
18885
|
+
}
|
|
18886
|
+
if (obj instanceof RegExp) {
|
|
18887
|
+
return { __regexp: { source: obj.source, flags: obj.flags } };
|
|
18888
|
+
}
|
|
18889
|
+
if (obj instanceof Map) {
|
|
18890
|
+
const entries = [...obj.entries()].map(([k, mv]) => [
|
|
18891
|
+
walk(k),
|
|
18892
|
+
walk(mv)
|
|
18893
|
+
]);
|
|
18894
|
+
return { __map: entries };
|
|
18895
|
+
}
|
|
18896
|
+
if (obj instanceof Set) {
|
|
18897
|
+
const items = [...obj].map(walk);
|
|
18898
|
+
return { __set: items };
|
|
18899
|
+
}
|
|
18900
|
+
if (ArrayBuffer.isView(obj)) {
|
|
18901
|
+
const ta = obj;
|
|
18902
|
+
const arr = new Array(ta.length);
|
|
18903
|
+
for (let i = 0; i < ta.length; i++) arr[i] = ta[i] ?? 0;
|
|
18904
|
+
return { __typed_array: { ctor: obj.constructor.name, data: arr } };
|
|
18905
|
+
}
|
|
18906
|
+
const out = {};
|
|
18907
|
+
for (const k of Object.keys(obj).sort()) {
|
|
18908
|
+
out[k] = walk(obj[k]);
|
|
18909
|
+
}
|
|
18910
|
+
return out;
|
|
18911
|
+
} finally {
|
|
18912
|
+
stack.delete(obj);
|
|
18913
|
+
}
|
|
18914
|
+
};
|
|
18915
|
+
return walk(value);
|
|
18916
|
+
}
|
|
18917
|
+
|
|
18021
18918
|
// src/patterns/demo-shell.ts
|
|
18022
18919
|
var demo_shell_exports = {};
|
|
18023
18920
|
__export(demo_shell_exports, {
|
|
@@ -20998,6 +21895,65 @@ ${validation.errors.join("\n")}`);
|
|
|
20998
21895
|
return parsed;
|
|
20999
21896
|
}
|
|
21000
21897
|
|
|
21898
|
+
// src/patterns/guarded-execution.ts
|
|
21899
|
+
var guarded_execution_exports = {};
|
|
21900
|
+
__export(guarded_execution_exports, {
|
|
21901
|
+
GuardedExecutionGraph: () => GuardedExecutionGraph,
|
|
21902
|
+
guardedExecution: () => guardedExecution
|
|
21903
|
+
});
|
|
21904
|
+
var GuardedExecutionGraph = class extends Graph {
|
|
21905
|
+
enforcer;
|
|
21906
|
+
violations;
|
|
21907
|
+
_target;
|
|
21908
|
+
_defaultActor;
|
|
21909
|
+
constructor(target, opts) {
|
|
21910
|
+
super(opts.name ?? `${target.name}_guarded`, opts.graph);
|
|
21911
|
+
this._target = target;
|
|
21912
|
+
this._defaultActor = opts.actor;
|
|
21913
|
+
const enforcerOpts = {
|
|
21914
|
+
mode: opts.mode ?? "enforce",
|
|
21915
|
+
name: "enforcer"
|
|
21916
|
+
};
|
|
21917
|
+
if (opts.violationsLimit != null) enforcerOpts.violationsLimit = opts.violationsLimit;
|
|
21918
|
+
this.enforcer = policyEnforcer(target, opts.policies, enforcerOpts);
|
|
21919
|
+
this.violations = this.enforcer.violations;
|
|
21920
|
+
this.mount("enforcer", this.enforcer);
|
|
21921
|
+
}
|
|
21922
|
+
/**
|
|
21923
|
+
* Describe the **target** graph scoped to the configured actor. Returns
|
|
21924
|
+
* only nodes the actor is permitted to see (via the target's node guards
|
|
21925
|
+
* filtering `describe()` via `actor`).
|
|
21926
|
+
*
|
|
21927
|
+
* Pass `{actor}` in opts to override the configured actor for this call.
|
|
21928
|
+
* Pass any standard {@link GraphDescribeOptions} fields (`detail`,
|
|
21929
|
+
* `fields`, `filter`) — they apply to the target's describe.
|
|
21930
|
+
*
|
|
21931
|
+
* **Mode interaction:**
|
|
21932
|
+
* - In `mode: "enforce"` (default), the enforcer stacks a policy-derived
|
|
21933
|
+
* guard on every target node. `scopedDescribe({actor})` then filters by
|
|
21934
|
+
* the AND of per-node guards AND the stacked policy guard.
|
|
21935
|
+
* - In `mode: "audit"`, NO guards are stacked — `scopedDescribe` filters
|
|
21936
|
+
* purely by the target's pre-existing per-node guards. If a target has
|
|
21937
|
+
* no node-level guards, the policy rules you pass have no effect on
|
|
21938
|
+
* visibility (they only populate the `violations` topic on writes).
|
|
21939
|
+
*/
|
|
21940
|
+
scopedDescribe(opts) {
|
|
21941
|
+
const actor = opts?.actor ?? this._defaultActor;
|
|
21942
|
+
const describeOpts = {
|
|
21943
|
+
...opts,
|
|
21944
|
+
...actor != null ? { actor } : {}
|
|
21945
|
+
};
|
|
21946
|
+
return this._target.describe(describeOpts);
|
|
21947
|
+
}
|
|
21948
|
+
/** The wrapped graph (escape hatch for tooling). */
|
|
21949
|
+
get target() {
|
|
21950
|
+
return this._target;
|
|
21951
|
+
}
|
|
21952
|
+
};
|
|
21953
|
+
function guardedExecution(target, opts) {
|
|
21954
|
+
return new GuardedExecutionGraph(target, opts);
|
|
21955
|
+
}
|
|
21956
|
+
|
|
21001
21957
|
// src/patterns/harness/index.ts
|
|
21002
21958
|
var harness_exports = {};
|
|
21003
21959
|
__export(harness_exports, {
|
|
@@ -21679,6 +22635,257 @@ function truncate(s, max) {
|
|
|
21679
22635
|
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
21680
22636
|
}
|
|
21681
22637
|
|
|
22638
|
+
// src/patterns/lens.ts
|
|
22639
|
+
var lens_exports = {};
|
|
22640
|
+
__export(lens_exports, {
|
|
22641
|
+
LensGraph: () => LensGraph,
|
|
22642
|
+
graphLens: () => graphLens,
|
|
22643
|
+
watchTopologyTree: () => watchTopologyTree
|
|
22644
|
+
});
|
|
22645
|
+
function lensMeta(kind) {
|
|
22646
|
+
return domainMeta("lens", kind);
|
|
22647
|
+
}
|
|
22648
|
+
function computeTopologyStats(described) {
|
|
22649
|
+
const paths = Object.keys(described.nodes);
|
|
22650
|
+
const depsByPath = /* @__PURE__ */ new Map();
|
|
22651
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
22652
|
+
for (const path of paths) {
|
|
22653
|
+
const deps = described.nodes[path]?.deps ?? [];
|
|
22654
|
+
depsByPath.set(path, deps);
|
|
22655
|
+
for (const dep of deps) {
|
|
22656
|
+
if (!dependents.has(dep)) dependents.set(dep, /* @__PURE__ */ new Set());
|
|
22657
|
+
dependents.get(dep).add(path);
|
|
22658
|
+
}
|
|
22659
|
+
}
|
|
22660
|
+
const sources = [];
|
|
22661
|
+
const sinks = [];
|
|
22662
|
+
for (const path of paths) {
|
|
22663
|
+
if ((depsByPath.get(path) ?? []).length === 0) sources.push(path);
|
|
22664
|
+
if (!dependents.has(path)) sinks.push(path);
|
|
22665
|
+
}
|
|
22666
|
+
sources.sort();
|
|
22667
|
+
sinks.sort();
|
|
22668
|
+
const edgeCount = described.edges.length;
|
|
22669
|
+
const WHITE = 0;
|
|
22670
|
+
const GRAY = 1;
|
|
22671
|
+
const BLACK = 2;
|
|
22672
|
+
const color = /* @__PURE__ */ new Map();
|
|
22673
|
+
for (const p of paths) color.set(p, WHITE);
|
|
22674
|
+
let hasCycles = false;
|
|
22675
|
+
const longestFrom = /* @__PURE__ */ new Map();
|
|
22676
|
+
const dfs = (path) => {
|
|
22677
|
+
const c = color.get(path) ?? WHITE;
|
|
22678
|
+
if (c === GRAY) {
|
|
22679
|
+
hasCycles = true;
|
|
22680
|
+
return 0;
|
|
22681
|
+
}
|
|
22682
|
+
if (c === BLACK) return longestFrom.get(path) ?? 0;
|
|
22683
|
+
color.set(path, GRAY);
|
|
22684
|
+
let best = 0;
|
|
22685
|
+
const kids = dependents.get(path);
|
|
22686
|
+
if (kids != null) {
|
|
22687
|
+
for (const k of kids) {
|
|
22688
|
+
const childLongest = dfs(k);
|
|
22689
|
+
if (childLongest + 1 > best) best = childLongest + 1;
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
color.set(path, BLACK);
|
|
22693
|
+
longestFrom.set(path, best);
|
|
22694
|
+
return best;
|
|
22695
|
+
};
|
|
22696
|
+
let depth = 0;
|
|
22697
|
+
for (const src of sources) {
|
|
22698
|
+
const d = dfs(src);
|
|
22699
|
+
if (d > depth) depth = d;
|
|
22700
|
+
}
|
|
22701
|
+
for (const p of paths) {
|
|
22702
|
+
if (color.get(p) === WHITE) dfs(p);
|
|
22703
|
+
}
|
|
22704
|
+
return {
|
|
22705
|
+
nodeCount: paths.length,
|
|
22706
|
+
edgeCount,
|
|
22707
|
+
subgraphCount: described.subgraphs.length,
|
|
22708
|
+
sources,
|
|
22709
|
+
sinks,
|
|
22710
|
+
depth,
|
|
22711
|
+
hasCycles
|
|
22712
|
+
};
|
|
22713
|
+
}
|
|
22714
|
+
function computeHealthReport(described) {
|
|
22715
|
+
const problems = [];
|
|
22716
|
+
for (const [path, desc] of Object.entries(described.nodes)) {
|
|
22717
|
+
if (desc.status !== "errored") continue;
|
|
22718
|
+
const entry = { path, status: "errored" };
|
|
22719
|
+
const upstream = reachable(described, path, "upstream", {});
|
|
22720
|
+
for (const p of upstream) {
|
|
22721
|
+
if (p === path) continue;
|
|
22722
|
+
if (described.nodes[p]?.status === "errored") {
|
|
22723
|
+
entry.upstreamCause = p;
|
|
22724
|
+
break;
|
|
22725
|
+
}
|
|
22726
|
+
}
|
|
22727
|
+
problems.push(entry);
|
|
22728
|
+
}
|
|
22729
|
+
problems.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
22730
|
+
return { ok: problems.length === 0, problems };
|
|
22731
|
+
}
|
|
22732
|
+
function topologyStatsEqual(a, b) {
|
|
22733
|
+
return a.nodeCount === b.nodeCount && a.edgeCount === b.edgeCount && a.subgraphCount === b.subgraphCount && a.depth === b.depth && a.hasCycles === b.hasCycles && stringArrayEqual(a.sources, b.sources) && stringArrayEqual(a.sinks, b.sinks);
|
|
22734
|
+
}
|
|
22735
|
+
function healthReportEqual(a, b) {
|
|
22736
|
+
if (a.ok !== b.ok) return false;
|
|
22737
|
+
if (a.problems.length !== b.problems.length) return false;
|
|
22738
|
+
for (let i = 0; i < a.problems.length; i++) {
|
|
22739
|
+
const x = a.problems[i];
|
|
22740
|
+
const y = b.problems[i];
|
|
22741
|
+
if (x.path !== y.path) return false;
|
|
22742
|
+
if (x.status !== y.status) return false;
|
|
22743
|
+
if (x.upstreamCause !== y.upstreamCause) return false;
|
|
22744
|
+
}
|
|
22745
|
+
return true;
|
|
22746
|
+
}
|
|
22747
|
+
function stringArrayEqual(a, b) {
|
|
22748
|
+
if (a.length !== b.length) return false;
|
|
22749
|
+
for (let i = 0; i < a.length; i++) {
|
|
22750
|
+
if (a[i] !== b[i]) return false;
|
|
22751
|
+
}
|
|
22752
|
+
return true;
|
|
22753
|
+
}
|
|
22754
|
+
var LensGraph = class extends Graph {
|
|
22755
|
+
/**
|
|
22756
|
+
* Aggregate structural stats — `nodeCount`, `edgeCount`, `sources`,
|
|
22757
|
+
* `sinks`, `depth`, `hasCycles`, `subgraphCount`. Recomputes on every
|
|
22758
|
+
* structural change via {@link watchTopologyTree} (transitive).
|
|
22759
|
+
*
|
|
22760
|
+
* Named `stats` (not `topology`) because `Graph.topology` already names
|
|
22761
|
+
* the raw `TopologyEvent` stream on every graph including `LensGraph`;
|
|
22762
|
+
* giving the lens its own `topology` accessor with an incompatible
|
|
22763
|
+
* `Node<TopologyStats>` type would break Liskov substitutability.
|
|
22764
|
+
*/
|
|
22765
|
+
stats;
|
|
22766
|
+
health;
|
|
22767
|
+
/**
|
|
22768
|
+
* Per-path flow tracker — a live {@link ReactiveMapBundle} keyed by
|
|
22769
|
+
* qualified path. Use `.get(path)` / `.has(path)` / `.size` for O(1)
|
|
22770
|
+
* sync queries; subscribe to `.entries` for a reactive snapshot of the
|
|
22771
|
+
* whole map. Lazy — the snapshot is materialized only while `.entries`
|
|
22772
|
+
* has subscribers.
|
|
22773
|
+
*
|
|
22774
|
+
* Shape intentionally differs from `stats` / `health` (which are plain
|
|
22775
|
+
* `Node<Report>`) because `flow` is a keyed collection, not a single
|
|
22776
|
+
* aggregate value. The map shape exposes cheaper queries than any
|
|
22777
|
+
* snapshot-based design.
|
|
22778
|
+
*/
|
|
22779
|
+
flow;
|
|
22780
|
+
_target;
|
|
22781
|
+
constructor(target, opts = {}) {
|
|
22782
|
+
super(opts.name ?? `${target.name}_lens`, opts.graph);
|
|
22783
|
+
this._target = target;
|
|
22784
|
+
let statsVersion = 0;
|
|
22785
|
+
let healthVersion = 0;
|
|
22786
|
+
const statsTick = state(0, { name: "stats_tick" });
|
|
22787
|
+
const healthTick = state(0, { name: "health_tick" });
|
|
22788
|
+
this.add("stats_tick", statsTick);
|
|
22789
|
+
this.add("health_tick", healthTick);
|
|
22790
|
+
const mapOpts = { name: "flow" };
|
|
22791
|
+
if (opts.maxFlowPaths != null) mapOpts.maxSize = opts.maxFlowPaths;
|
|
22792
|
+
this.flow = reactiveMap(mapOpts);
|
|
22793
|
+
this.add("flow", this.flow.entries);
|
|
22794
|
+
const pathFilter = opts.pathFilter;
|
|
22795
|
+
const offTopology = watchTopologyTree(target, (event, _emitter, prefix) => {
|
|
22796
|
+
statsVersion += 1;
|
|
22797
|
+
statsTick.emit(statsVersion);
|
|
22798
|
+
healthVersion += 1;
|
|
22799
|
+
healthTick.emit(healthVersion);
|
|
22800
|
+
if (event.kind === "removed") {
|
|
22801
|
+
if (event.nodeKind === "node") {
|
|
22802
|
+
const qp = `${prefix}${event.name}`;
|
|
22803
|
+
this.flow.delete(qp);
|
|
22804
|
+
} else {
|
|
22805
|
+
const mountPrefix = `${prefix}${event.name}::`;
|
|
22806
|
+
const keysToDelete = [];
|
|
22807
|
+
for (const relativePath of event.audit.nodes) {
|
|
22808
|
+
const qualified = relativePath === "" ? `${prefix}${event.name}` : `${mountPrefix}${relativePath}`;
|
|
22809
|
+
keysToDelete.push(qualified);
|
|
22810
|
+
}
|
|
22811
|
+
if (keysToDelete.length > 0) this.flow.deleteMany(keysToDelete);
|
|
22812
|
+
}
|
|
22813
|
+
}
|
|
22814
|
+
});
|
|
22815
|
+
this.addDisposer(offTopology);
|
|
22816
|
+
const observeHandle = target.observe({ timeline: true, structured: true });
|
|
22817
|
+
const offObserve = observeHandle.onEvent((event) => {
|
|
22818
|
+
const t = event.type;
|
|
22819
|
+
if (t === "error" || t === "complete" || t === "data" || t === "teardown") {
|
|
22820
|
+
healthVersion += 1;
|
|
22821
|
+
healthTick.emit(healthVersion);
|
|
22822
|
+
}
|
|
22823
|
+
if (t === "data") {
|
|
22824
|
+
const path = event.path ?? "";
|
|
22825
|
+
if (!path) return;
|
|
22826
|
+
if (pathFilter != null && !pathFilter(path)) return;
|
|
22827
|
+
const existing = this.flow.get(path);
|
|
22828
|
+
const count = existing != null ? existing.count + 1 : 1;
|
|
22829
|
+
this.flow.set(path, { path, count, lastUpdate_ns: monotonicNs() });
|
|
22830
|
+
}
|
|
22831
|
+
});
|
|
22832
|
+
this.addDisposer(() => {
|
|
22833
|
+
offObserve();
|
|
22834
|
+
observeHandle.dispose();
|
|
22835
|
+
});
|
|
22836
|
+
this.stats = derived(
|
|
22837
|
+
[statsTick],
|
|
22838
|
+
() => computeTopologyStats(target.describe({ detail: "minimal" })),
|
|
22839
|
+
{
|
|
22840
|
+
name: "stats",
|
|
22841
|
+
describeKind: "derived",
|
|
22842
|
+
equals: topologyStatsEqual,
|
|
22843
|
+
meta: lensMeta("stats")
|
|
22844
|
+
}
|
|
22845
|
+
);
|
|
22846
|
+
this.add("stats", this.stats);
|
|
22847
|
+
this.addDisposer(keepalive(this.stats));
|
|
22848
|
+
this.health = derived(
|
|
22849
|
+
[healthTick],
|
|
22850
|
+
() => computeHealthReport(target.describe({ detail: "standard" })),
|
|
22851
|
+
{
|
|
22852
|
+
name: "health",
|
|
22853
|
+
describeKind: "derived",
|
|
22854
|
+
equals: healthReportEqual,
|
|
22855
|
+
meta: lensMeta("health")
|
|
22856
|
+
}
|
|
22857
|
+
);
|
|
22858
|
+
this.add("health", this.health);
|
|
22859
|
+
this.addDisposer(keepalive(this.health));
|
|
22860
|
+
}
|
|
22861
|
+
/**
|
|
22862
|
+
* Live causal chain from `from` to `to`. Recomputes whenever the target
|
|
22863
|
+
* mutates. Disposed automatically when the lens is destroyed.
|
|
22864
|
+
*
|
|
22865
|
+
* **Lifetime note:** every call to `why()` registers a lens-owned disposer
|
|
22866
|
+
* that runs on `lens.destroy()`. The returned `dispose` function releases
|
|
22867
|
+
* the internal subscription but does NOT remove the lens-owned disposer —
|
|
22868
|
+
* so heavy calling (e.g. per render frame) accumulates no-op disposers
|
|
22869
|
+
* until lens teardown. Cache the returned handle for long-lived queries.
|
|
22870
|
+
*
|
|
22871
|
+
* @param from - Qualified path of the upstream endpoint.
|
|
22872
|
+
* @param to - Qualified path of the downstream endpoint.
|
|
22873
|
+
* @param opts - See {@link reactiveExplainPath}.
|
|
22874
|
+
*/
|
|
22875
|
+
why(from, to, opts) {
|
|
22876
|
+
const handle = reactiveExplainPath(this._target, from, to, opts);
|
|
22877
|
+
this.addDisposer(handle.dispose);
|
|
22878
|
+
return handle;
|
|
22879
|
+
}
|
|
22880
|
+
/** Reference to the lensed graph. */
|
|
22881
|
+
get target() {
|
|
22882
|
+
return this._target;
|
|
22883
|
+
}
|
|
22884
|
+
};
|
|
22885
|
+
function graphLens(target, opts) {
|
|
22886
|
+
return new LensGraph(target, opts);
|
|
22887
|
+
}
|
|
22888
|
+
|
|
21682
22889
|
// src/patterns/reactive-layout/index.ts
|
|
21683
22890
|
var reactive_layout_exports = {};
|
|
21684
22891
|
__export(reactive_layout_exports, {
|
|
@@ -21899,7 +23106,7 @@ var CanvasMeasureAdapter = class {
|
|
|
21899
23106
|
this.currentFont = font;
|
|
21900
23107
|
}
|
|
21901
23108
|
let width = ctx.measureText(text).width;
|
|
21902
|
-
if (this.emojiCorrection !== 1 &&
|
|
23109
|
+
if (this.emojiCorrection !== 1 && new RegExp("\\p{Emoji_Presentation}", "u").test(text)) {
|
|
21903
23110
|
width *= this.emojiCorrection;
|
|
21904
23111
|
}
|
|
21905
23112
|
return { width };
|
|
@@ -22349,6 +23556,385 @@ function reactiveFlowLayout(opts) {
|
|
|
22349
23556
|
};
|
|
22350
23557
|
}
|
|
22351
23558
|
|
|
23559
|
+
// src/patterns/resilient-pipeline.ts
|
|
23560
|
+
var resilient_pipeline_exports = {};
|
|
23561
|
+
__export(resilient_pipeline_exports, {
|
|
23562
|
+
NS_PER_MS: () => NS_PER_MS,
|
|
23563
|
+
NS_PER_SEC: () => NS_PER_SEC,
|
|
23564
|
+
resilientPipeline: () => resilientPipeline
|
|
23565
|
+
});
|
|
23566
|
+
function resilientPipeline(source, opts = {}) {
|
|
23567
|
+
let current = source;
|
|
23568
|
+
if (opts.rateLimit != null) {
|
|
23569
|
+
current = rateLimiter(current, opts.rateLimit);
|
|
23570
|
+
}
|
|
23571
|
+
if (opts.budget != null && opts.budget.length > 0) {
|
|
23572
|
+
current = budgetGate(current, opts.budget);
|
|
23573
|
+
}
|
|
23574
|
+
let breakerState;
|
|
23575
|
+
if (opts.breaker != null) {
|
|
23576
|
+
const breaker = circuitBreaker(opts.breaker);
|
|
23577
|
+
const onOpen = opts.breakerOnOpen ?? "skip";
|
|
23578
|
+
const wrapped = withBreaker(breaker, { onOpen })(current);
|
|
23579
|
+
current = wrapped.node;
|
|
23580
|
+
breakerState = wrapped.breakerState;
|
|
23581
|
+
}
|
|
23582
|
+
if (opts.timeoutMs != null) {
|
|
23583
|
+
if (opts.timeoutMs <= 0) throw new RangeError("timeoutMs must be > 0");
|
|
23584
|
+
if (opts.timeoutMs > 9e6) {
|
|
23585
|
+
throw new RangeError(
|
|
23586
|
+
"timeoutMs must be <= 9_000_000 (\u22482.5h) to stay within safe ns arithmetic"
|
|
23587
|
+
);
|
|
23588
|
+
}
|
|
23589
|
+
current = timeout(current, opts.timeoutMs * NS_PER_MS);
|
|
23590
|
+
}
|
|
23591
|
+
if (opts.retry != null) {
|
|
23592
|
+
current = retry(current, opts.retry);
|
|
23593
|
+
}
|
|
23594
|
+
if (opts.fallback !== void 0) {
|
|
23595
|
+
current = fallback(current, opts.fallback);
|
|
23596
|
+
}
|
|
23597
|
+
const withStatusBundle = withStatus(current, { initialStatus: opts.initialStatus ?? "pending" });
|
|
23598
|
+
return {
|
|
23599
|
+
node: withStatusBundle.node,
|
|
23600
|
+
status: withStatusBundle.status,
|
|
23601
|
+
error: withStatusBundle.error,
|
|
23602
|
+
breakerState
|
|
23603
|
+
};
|
|
23604
|
+
}
|
|
23605
|
+
|
|
23606
|
+
// src/patterns/surface/index.ts
|
|
23607
|
+
var surface_exports = {};
|
|
23608
|
+
__export(surface_exports, {
|
|
23609
|
+
SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
|
|
23610
|
+
SurfaceError: () => SurfaceError,
|
|
23611
|
+
asSurfaceError: () => asSurfaceError,
|
|
23612
|
+
createGraph: () => createGraph,
|
|
23613
|
+
deleteSnapshot: () => deleteSnapshot,
|
|
23614
|
+
diffSnapshots: () => diffSnapshots,
|
|
23615
|
+
listSnapshots: () => listSnapshots,
|
|
23616
|
+
restoreSnapshot: () => restoreSnapshot,
|
|
23617
|
+
runReduction: () => runReduction,
|
|
23618
|
+
saveSnapshot: () => saveSnapshot
|
|
23619
|
+
});
|
|
23620
|
+
|
|
23621
|
+
// src/patterns/surface/errors.ts
|
|
23622
|
+
var SurfaceError = class extends Error {
|
|
23623
|
+
code;
|
|
23624
|
+
details;
|
|
23625
|
+
constructor(code, message, details) {
|
|
23626
|
+
super(message);
|
|
23627
|
+
this.name = "SurfaceError";
|
|
23628
|
+
this.code = code;
|
|
23629
|
+
if (details !== void 0) this.details = details;
|
|
23630
|
+
}
|
|
23631
|
+
/**
|
|
23632
|
+
* JSON-safe payload for wire serialization. Defensively validates
|
|
23633
|
+
* `details` — if it can't be round-tripped through `JSON.stringify`
|
|
23634
|
+
* (cyclic refs, `BigInt`, `Error` instance not pre-toJSON'd), the
|
|
23635
|
+
* payload falls back to `{code, message}` only rather than crashing
|
|
23636
|
+
* the MCP/CLI wrapper when it serializes this error onto the wire.
|
|
23637
|
+
*/
|
|
23638
|
+
toJSON() {
|
|
23639
|
+
const out = { code: this.code, message: this.message };
|
|
23640
|
+
if (this.details !== void 0) {
|
|
23641
|
+
const safe = safeDetails(this.details);
|
|
23642
|
+
if (safe !== void 0) out.details = safe;
|
|
23643
|
+
}
|
|
23644
|
+
return out;
|
|
23645
|
+
}
|
|
23646
|
+
};
|
|
23647
|
+
function safeDetails(details) {
|
|
23648
|
+
try {
|
|
23649
|
+
return JSON.parse(JSON.stringify(details));
|
|
23650
|
+
} catch {
|
|
23651
|
+
return void 0;
|
|
23652
|
+
}
|
|
23653
|
+
}
|
|
23654
|
+
function asSurfaceError(err, fallbackCode = "internal-error") {
|
|
23655
|
+
if (err instanceof SurfaceError) return err;
|
|
23656
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23657
|
+
return new SurfaceError(fallbackCode, message);
|
|
23658
|
+
}
|
|
23659
|
+
|
|
23660
|
+
// src/patterns/surface/create.ts
|
|
23661
|
+
function createGraph(spec, opts) {
|
|
23662
|
+
const structural = validateSpec(spec);
|
|
23663
|
+
if (!structural.valid) {
|
|
23664
|
+
throw new SurfaceError(
|
|
23665
|
+
"invalid-spec",
|
|
23666
|
+
`GraphSpec validation failed:
|
|
23667
|
+
${structural.errors.join("\n")}`,
|
|
23668
|
+
{ errors: structural.errors }
|
|
23669
|
+
);
|
|
23670
|
+
}
|
|
23671
|
+
const catalog = opts?.catalog ?? {};
|
|
23672
|
+
const catalogValidation = validateSpecAgainstCatalog(spec, catalog);
|
|
23673
|
+
if (!catalogValidation.valid) {
|
|
23674
|
+
throw new SurfaceError(
|
|
23675
|
+
"catalog-error",
|
|
23676
|
+
`Catalog validation failed:
|
|
23677
|
+
${catalogValidation.errors.join("\n")}`,
|
|
23678
|
+
{ errors: catalogValidation.errors }
|
|
23679
|
+
);
|
|
23680
|
+
}
|
|
23681
|
+
try {
|
|
23682
|
+
return compileSpec(spec, opts);
|
|
23683
|
+
} catch (err) {
|
|
23684
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23685
|
+
throw new SurfaceError("catalog-error", message);
|
|
23686
|
+
}
|
|
23687
|
+
}
|
|
23688
|
+
|
|
23689
|
+
// src/patterns/surface/reduce.ts
|
|
23690
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
23691
|
+
async function runReduction(spec, input, opts) {
|
|
23692
|
+
const inputPath = opts?.inputPath ?? "input";
|
|
23693
|
+
const outputPath = opts?.outputPath ?? "output";
|
|
23694
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
23695
|
+
const graph = createGraph(spec, { catalog: opts?.catalog });
|
|
23696
|
+
let outputNode;
|
|
23697
|
+
try {
|
|
23698
|
+
outputNode = graph.resolve(outputPath);
|
|
23699
|
+
} catch {
|
|
23700
|
+
graph.destroy();
|
|
23701
|
+
throw new SurfaceError(
|
|
23702
|
+
"node-not-found",
|
|
23703
|
+
`reduce: output path "${outputPath}" is not registered`,
|
|
23704
|
+
{ path: outputPath }
|
|
23705
|
+
);
|
|
23706
|
+
}
|
|
23707
|
+
try {
|
|
23708
|
+
graph.resolve(inputPath);
|
|
23709
|
+
} catch {
|
|
23710
|
+
graph.destroy();
|
|
23711
|
+
throw new SurfaceError(
|
|
23712
|
+
"node-not-found",
|
|
23713
|
+
`reduce: input path "${inputPath}" is not registered`,
|
|
23714
|
+
{ path: inputPath }
|
|
23715
|
+
);
|
|
23716
|
+
}
|
|
23717
|
+
try {
|
|
23718
|
+
return await new Promise((resolve, reject) => {
|
|
23719
|
+
let primed = false;
|
|
23720
|
+
let settled = false;
|
|
23721
|
+
let timer;
|
|
23722
|
+
let unsub;
|
|
23723
|
+
let shouldUnsub = false;
|
|
23724
|
+
const finish = (action2) => {
|
|
23725
|
+
if (settled) return;
|
|
23726
|
+
settled = true;
|
|
23727
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
23728
|
+
if (unsub !== void 0) {
|
|
23729
|
+
unsub();
|
|
23730
|
+
unsub = void 0;
|
|
23731
|
+
} else {
|
|
23732
|
+
shouldUnsub = true;
|
|
23733
|
+
}
|
|
23734
|
+
action2();
|
|
23735
|
+
};
|
|
23736
|
+
unsub = outputNode.subscribe((msgs) => {
|
|
23737
|
+
for (const m of msgs) {
|
|
23738
|
+
if (settled) return;
|
|
23739
|
+
if (!primed) continue;
|
|
23740
|
+
if (m[0] === DATA) {
|
|
23741
|
+
finish(() => resolve(m[1]));
|
|
23742
|
+
return;
|
|
23743
|
+
}
|
|
23744
|
+
if (m[0] === RESOLVED) {
|
|
23745
|
+
const cached2 = outputNode.cache;
|
|
23746
|
+
finish(() => resolve(cached2));
|
|
23747
|
+
return;
|
|
23748
|
+
}
|
|
23749
|
+
if (m[0] === ERROR) {
|
|
23750
|
+
const payload = m[1];
|
|
23751
|
+
const message = payload instanceof Error ? payload.message : String(payload);
|
|
23752
|
+
const cause = payload instanceof Error ? payload : void 0;
|
|
23753
|
+
finish(
|
|
23754
|
+
() => reject(
|
|
23755
|
+
new SurfaceError(
|
|
23756
|
+
"internal-error",
|
|
23757
|
+
`reduce: output emitted ERROR: ${message}`,
|
|
23758
|
+
cause != null ? { cause } : void 0
|
|
23759
|
+
)
|
|
23760
|
+
)
|
|
23761
|
+
);
|
|
23762
|
+
return;
|
|
23763
|
+
}
|
|
23764
|
+
if (m[0] === COMPLETE) {
|
|
23765
|
+
finish(
|
|
23766
|
+
() => reject(
|
|
23767
|
+
new SurfaceError(
|
|
23768
|
+
"internal-error",
|
|
23769
|
+
`reduce: output COMPLETEd without a post-push DATA`
|
|
23770
|
+
)
|
|
23771
|
+
)
|
|
23772
|
+
);
|
|
23773
|
+
return;
|
|
23774
|
+
}
|
|
23775
|
+
}
|
|
23776
|
+
});
|
|
23777
|
+
if (shouldUnsub) {
|
|
23778
|
+
unsub?.();
|
|
23779
|
+
unsub = void 0;
|
|
23780
|
+
}
|
|
23781
|
+
primed = true;
|
|
23782
|
+
try {
|
|
23783
|
+
graph.set(inputPath, input);
|
|
23784
|
+
} catch (err) {
|
|
23785
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23786
|
+
const cause = err instanceof Error ? err : void 0;
|
|
23787
|
+
finish(
|
|
23788
|
+
() => reject(
|
|
23789
|
+
new SurfaceError(
|
|
23790
|
+
"internal-error",
|
|
23791
|
+
`reduce: failed to set input on "${inputPath}": ${message}`,
|
|
23792
|
+
cause != null ? { path: inputPath, cause } : { path: inputPath }
|
|
23793
|
+
)
|
|
23794
|
+
)
|
|
23795
|
+
);
|
|
23796
|
+
return;
|
|
23797
|
+
}
|
|
23798
|
+
if (!settled && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
23799
|
+
timer = setTimeout(() => {
|
|
23800
|
+
finish(
|
|
23801
|
+
() => reject(
|
|
23802
|
+
new SurfaceError(
|
|
23803
|
+
"reduce-timeout",
|
|
23804
|
+
`reduce: no output emitted within ${timeoutMs}ms`,
|
|
23805
|
+
{ timeoutMs, outputPath }
|
|
23806
|
+
)
|
|
23807
|
+
)
|
|
23808
|
+
);
|
|
23809
|
+
}, timeoutMs);
|
|
23810
|
+
timer.unref?.();
|
|
23811
|
+
}
|
|
23812
|
+
});
|
|
23813
|
+
} finally {
|
|
23814
|
+
graph.destroy();
|
|
23815
|
+
}
|
|
23816
|
+
}
|
|
23817
|
+
|
|
23818
|
+
// src/patterns/surface/snapshot.ts
|
|
23819
|
+
var SNAPSHOT_WIRE_VERSION = SNAPSHOT_VERSION;
|
|
23820
|
+
function unwrapCheckpoint(raw, snapshotId) {
|
|
23821
|
+
if (raw == null || typeof raw !== "object") {
|
|
23822
|
+
throw new SurfaceError("snapshot-not-found", `snapshot "${snapshotId}" not found in tier`, {
|
|
23823
|
+
snapshotId
|
|
23824
|
+
});
|
|
23825
|
+
}
|
|
23826
|
+
const record = raw;
|
|
23827
|
+
if ("mode" in record) {
|
|
23828
|
+
if (record.mode === "full" && "snapshot" in record) {
|
|
23829
|
+
return record.snapshot;
|
|
23830
|
+
}
|
|
23831
|
+
if (record.mode === "diff") {
|
|
23832
|
+
throw new SurfaceError(
|
|
23833
|
+
"restore-failed",
|
|
23834
|
+
`snapshot "${snapshotId}" is a diff record; restore the baseline and replay WAL instead`,
|
|
23835
|
+
{ snapshotId, mode: "diff" }
|
|
23836
|
+
);
|
|
23837
|
+
}
|
|
23838
|
+
throw new SurfaceError(
|
|
23839
|
+
"restore-failed",
|
|
23840
|
+
`snapshot "${snapshotId}" has unknown mode "${String(record.mode)}"`,
|
|
23841
|
+
{ snapshotId, mode: String(record.mode) }
|
|
23842
|
+
);
|
|
23843
|
+
}
|
|
23844
|
+
if ("nodes" in record && "edges" in record && "subgraphs" in record && "name" in record) {
|
|
23845
|
+
return record;
|
|
23846
|
+
}
|
|
23847
|
+
throw new SurfaceError(
|
|
23848
|
+
"restore-failed",
|
|
23849
|
+
`snapshot "${snapshotId}" payload is not a GraphCheckpointRecord or GraphPersistSnapshot`,
|
|
23850
|
+
{ snapshotId }
|
|
23851
|
+
);
|
|
23852
|
+
}
|
|
23853
|
+
async function saveSnapshot(graph, snapshotId, tier) {
|
|
23854
|
+
let snapshot;
|
|
23855
|
+
try {
|
|
23856
|
+
snapshot = graph.snapshot();
|
|
23857
|
+
} catch (err) {
|
|
23858
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23859
|
+
throw new SurfaceError(
|
|
23860
|
+
"snapshot-failed",
|
|
23861
|
+
`snapshot "${snapshotId}" serialization failed: ${message}`,
|
|
23862
|
+
{ snapshotId }
|
|
23863
|
+
);
|
|
23864
|
+
}
|
|
23865
|
+
const record = {
|
|
23866
|
+
mode: "full",
|
|
23867
|
+
seq: 0,
|
|
23868
|
+
timestamp_ns: wallClockNs(),
|
|
23869
|
+
format_version: SNAPSHOT_WIRE_VERSION,
|
|
23870
|
+
snapshot
|
|
23871
|
+
};
|
|
23872
|
+
try {
|
|
23873
|
+
await tier.save(snapshotId, record);
|
|
23874
|
+
} catch (err) {
|
|
23875
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23876
|
+
throw new SurfaceError("snapshot-failed", `snapshot "${snapshotId}" save failed: ${message}`, {
|
|
23877
|
+
snapshotId
|
|
23878
|
+
});
|
|
23879
|
+
}
|
|
23880
|
+
return { snapshotId, timestamp_ns: record.timestamp_ns };
|
|
23881
|
+
}
|
|
23882
|
+
async function restoreSnapshot(snapshotId, tier, opts) {
|
|
23883
|
+
const raw = await tier.load(snapshotId);
|
|
23884
|
+
const snapshot = unwrapCheckpoint(raw, snapshotId);
|
|
23885
|
+
try {
|
|
23886
|
+
return Graph.fromSnapshot(
|
|
23887
|
+
snapshot,
|
|
23888
|
+
opts?.factories ? { factories: opts.factories } : void 0
|
|
23889
|
+
);
|
|
23890
|
+
} catch (err) {
|
|
23891
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23892
|
+
throw new SurfaceError(
|
|
23893
|
+
"restore-failed",
|
|
23894
|
+
`snapshot "${snapshotId}" restore failed: ${message}`,
|
|
23895
|
+
{
|
|
23896
|
+
snapshotId
|
|
23897
|
+
}
|
|
23898
|
+
);
|
|
23899
|
+
}
|
|
23900
|
+
}
|
|
23901
|
+
async function diffSnapshots(snapshotIdA, snapshotIdB, tier) {
|
|
23902
|
+
const [rawA, rawB] = await Promise.all([tier.load(snapshotIdA), tier.load(snapshotIdB)]);
|
|
23903
|
+
const snapshotA = unwrapCheckpoint(rawA, snapshotIdA);
|
|
23904
|
+
const snapshotB = unwrapCheckpoint(rawB, snapshotIdB);
|
|
23905
|
+
return Graph.diff(snapshotA, snapshotB);
|
|
23906
|
+
}
|
|
23907
|
+
async function listSnapshots(tier) {
|
|
23908
|
+
if (typeof tier.list !== "function") {
|
|
23909
|
+
throw new SurfaceError(
|
|
23910
|
+
"tier-no-list",
|
|
23911
|
+
"StorageTier does not implement list(); wrap the tier with an enumerator or use a different backend"
|
|
23912
|
+
);
|
|
23913
|
+
}
|
|
23914
|
+
return tier.list();
|
|
23915
|
+
}
|
|
23916
|
+
async function deleteSnapshot(snapshotId, tier) {
|
|
23917
|
+
if (typeof tier.clear !== "function") {
|
|
23918
|
+
throw new SurfaceError(
|
|
23919
|
+
"snapshot-failed",
|
|
23920
|
+
`StorageTier is append-only (no clear()); cannot delete "${snapshotId}"`,
|
|
23921
|
+
{ snapshotId }
|
|
23922
|
+
);
|
|
23923
|
+
}
|
|
23924
|
+
try {
|
|
23925
|
+
await tier.clear(snapshotId);
|
|
23926
|
+
} catch (err) {
|
|
23927
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
23928
|
+
throw new SurfaceError(
|
|
23929
|
+
"snapshot-failed",
|
|
23930
|
+
`snapshot "${snapshotId}" delete failed: ${message}`,
|
|
23931
|
+
{
|
|
23932
|
+
snapshotId
|
|
23933
|
+
}
|
|
23934
|
+
);
|
|
23935
|
+
}
|
|
23936
|
+
}
|
|
23937
|
+
|
|
22352
23938
|
// src/index.ts
|
|
22353
23939
|
var version = "0.0.0";
|
|
22354
23940
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -22389,15 +23975,20 @@ var version = "0.0.0";
|
|
|
22389
23975
|
ResettableTimer,
|
|
22390
23976
|
SIZEOF_OVERHEAD,
|
|
22391
23977
|
SIZEOF_SYMBOL,
|
|
23978
|
+
SNAPSHOT_VERSION,
|
|
23979
|
+
SNAPSHOT_WIRE_VERSION,
|
|
22392
23980
|
START,
|
|
22393
23981
|
START_MSG,
|
|
23982
|
+
SurfaceError,
|
|
22394
23983
|
TEARDOWN,
|
|
22395
23984
|
TEARDOWN_MSG,
|
|
22396
23985
|
TEARDOWN_ONLY_BATCH,
|
|
22397
23986
|
TimeoutError,
|
|
22398
23987
|
accessHintForGuard,
|
|
23988
|
+
accountability,
|
|
22399
23989
|
advanceVersion,
|
|
22400
23990
|
ai,
|
|
23991
|
+
asSurfaceError,
|
|
22401
23992
|
audit,
|
|
22402
23993
|
autoTrackNode,
|
|
22403
23994
|
batch,
|
|
@@ -22421,6 +24012,7 @@ var version = "0.0.0";
|
|
|
22421
24012
|
cqrs,
|
|
22422
24013
|
createDagCborCodec,
|
|
22423
24014
|
createDagCborZstdCodec,
|
|
24015
|
+
createGraph,
|
|
22424
24016
|
createTransport,
|
|
22425
24017
|
createVersioning,
|
|
22426
24018
|
createWatermarkController,
|
|
@@ -22432,11 +24024,13 @@ var version = "0.0.0";
|
|
|
22432
24024
|
defaultConfig,
|
|
22433
24025
|
defaultHash,
|
|
22434
24026
|
delay,
|
|
24027
|
+
deleteSnapshot,
|
|
22435
24028
|
demoShell,
|
|
22436
24029
|
derived,
|
|
22437
24030
|
deserializeError,
|
|
22438
24031
|
dictStorage,
|
|
22439
24032
|
diffForWAL,
|
|
24033
|
+
diffSnapshots,
|
|
22440
24034
|
distill,
|
|
22441
24035
|
distinctUntilChanged,
|
|
22442
24036
|
domainTemplates,
|
|
@@ -22448,6 +24042,7 @@ var version = "0.0.0";
|
|
|
22448
24042
|
encodeEnvelope,
|
|
22449
24043
|
escapeRegexChar,
|
|
22450
24044
|
exhaustMap,
|
|
24045
|
+
explainPath,
|
|
22451
24046
|
exponential,
|
|
22452
24047
|
externalBundle,
|
|
22453
24048
|
externalProducer,
|
|
@@ -22503,6 +24098,7 @@ var version = "0.0.0";
|
|
|
22503
24098
|
graph,
|
|
22504
24099
|
graphProfile,
|
|
22505
24100
|
graphspec,
|
|
24101
|
+
guarded,
|
|
22506
24102
|
harness,
|
|
22507
24103
|
indexedDbStorage,
|
|
22508
24104
|
interval,
|
|
@@ -22512,7 +24108,9 @@ var version = "0.0.0";
|
|
|
22512
24108
|
keepalive,
|
|
22513
24109
|
last,
|
|
22514
24110
|
layout,
|
|
24111
|
+
lens,
|
|
22515
24112
|
linear,
|
|
24113
|
+
listSnapshots,
|
|
22516
24114
|
lru,
|
|
22517
24115
|
map,
|
|
22518
24116
|
matchesAnyPattern,
|
|
@@ -22562,11 +24160,15 @@ var version = "0.0.0";
|
|
|
22562
24160
|
replay,
|
|
22563
24161
|
replayWAL,
|
|
22564
24162
|
rescue,
|
|
24163
|
+
resilientPipeline,
|
|
22565
24164
|
resolveBackoffPreset,
|
|
22566
24165
|
resolveDescribeFields,
|
|
24166
|
+
restoreSnapshot,
|
|
22567
24167
|
retry,
|
|
22568
24168
|
retrySource,
|
|
24169
|
+
runReduction,
|
|
22569
24170
|
sample,
|
|
24171
|
+
saveSnapshot,
|
|
22570
24172
|
scan,
|
|
22571
24173
|
serializeError,
|
|
22572
24174
|
share,
|
|
@@ -22578,6 +24180,7 @@ var version = "0.0.0";
|
|
|
22578
24180
|
solid,
|
|
22579
24181
|
sqliteStorage,
|
|
22580
24182
|
state,
|
|
24183
|
+
surface,
|
|
22581
24184
|
svelte,
|
|
22582
24185
|
switchMap,
|
|
22583
24186
|
take,
|
|
@@ -22615,6 +24218,7 @@ var version = "0.0.0";
|
|
|
22615
24218
|
version,
|
|
22616
24219
|
vue,
|
|
22617
24220
|
wallClockNs,
|
|
24221
|
+
watchTopologyTree,
|
|
22618
24222
|
window,
|
|
22619
24223
|
windowCount,
|
|
22620
24224
|
windowTime,
|