@graphrefly/graphrefly 0.24.0 → 0.26.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/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
- package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
- package/dist/chunk-6LDQFTYS.js +102 -0
- package/dist/chunk-6LDQFTYS.js.map +1 -0
- package/dist/{chunk-5WGT55R4.js → chunk-AMCG74RZ.js} +195 -24
- package/dist/chunk-AMCG74RZ.js.map +1 -0
- package/dist/{chunk-AOCBDH4T.js → chunk-BVZYTZ5H.js} +76 -103
- package/dist/chunk-BVZYTZ5H.js.map +1 -0
- package/dist/chunk-FQMKGR6L.js +330 -0
- package/dist/chunk-FQMKGR6L.js.map +1 -0
- package/dist/chunk-HXZEYDUR.js +94 -0
- package/dist/chunk-HXZEYDUR.js.map +1 -0
- package/dist/{chunk-IPLKX3L2.js → chunk-IZYUSJC7.js} +16 -14
- package/dist/{chunk-IPLKX3L2.js.map → chunk-IZYUSJC7.js.map} +1 -1
- package/dist/chunk-J22W6HV3.js +107 -0
- package/dist/chunk-J22W6HV3.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-JYXEWPH4.js +62 -0
- package/dist/chunk-JYXEWPH4.js.map +1 -0
- package/dist/chunk-LCE3GF5P.js +866 -0
- package/dist/chunk-LCE3GF5P.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-OHISZPOJ.js +97 -0
- package/dist/chunk-OHISZPOJ.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-PY4XCDLR.js → chunk-RB6QPHJ7.js} +8 -6
- package/dist/{chunk-PY4XCDLR.js.map → chunk-RB6QPHJ7.js.map} +1 -1
- package/dist/chunk-SN4YWWYO.js +171 -0
- package/dist/chunk-SN4YWWYO.js.map +1 -0
- package/dist/chunk-SX52TAR4.js +110 -0
- package/dist/chunk-SX52TAR4.js.map +1 -0
- package/dist/{chunk-XOFWRC73.js → chunk-THTWHNU4.js} +319 -24
- package/dist/chunk-THTWHNU4.js.map +1 -0
- package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
- package/dist/chunk-XGPU467M.js +136 -0
- package/dist/chunk-XGPU467M.js.map +1 -0
- package/dist/{chunk-TDEXAMGO.js → chunk-ZQMEI34O.js} +206 -574
- package/dist/chunk-ZQMEI34O.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 +49 -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 +10 -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/core/index.cjs +53 -4
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +3 -3
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +26 -24
- package/dist/demo-shell-26p5fVxn.d.cts +102 -0
- package/dist/demo-shell-DEp-nMTl.d.ts +102 -0
- package/dist/extra/index.cjs +290 -110
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +5 -4
- package/dist/extra/index.d.ts +5 -4
- package/dist/extra/index.js +8 -5
- 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-6tZ5jEzr.d.cts} +195 -4
- package/dist/{graph-B6NFqv3z.d.ts → graph-DQ69XU0g.d.ts} +195 -4
- package/dist/index-B4MP_8V_.d.cts +37 -0
- package/dist/index-BEfE8H_G.d.cts +121 -0
- package/dist/{index-D7XgsUt7.d.ts → index-BW1z3BN9.d.ts} +169 -127
- package/dist/index-BYOHF0zP.d.ts +34 -0
- package/dist/index-B_IP40nB.d.cts +36 -0
- package/dist/index-Bd_fwmLf.d.cts +45 -0
- package/dist/{index-BysCTzJz.d.ts → index-BeIdBfcb.d.cts} +121 -547
- package/dist/index-BjI6ty9z.d.ts +121 -0
- package/dist/index-Bxb5ZYc9.d.cts +34 -0
- package/dist/{index-BJB7t9gg.d.cts → index-C0ZXMaXO.d.cts} +2 -2
- package/dist/{index-b5BYtczN.d.cts → index-C8mdwMXc.d.cts} +169 -127
- package/dist/index-CDAjUFIv.d.ts +36 -0
- package/dist/index-CPgZ5wRl.d.ts +44 -0
- package/dist/{index-AMWewNDe.d.cts → index-CUwyr1Kk.d.cts} +33 -4
- package/dist/index-CUyrtuOf.d.cts +127 -0
- package/dist/{index-C-TXEa7C.d.ts → index-CY2TljO4.d.ts} +2 -2
- package/dist/index-CmnuOibw.d.ts +37 -0
- package/dist/{index-DiobMNwE.d.ts → index-CuYwdKO-.d.ts} +3 -3
- package/dist/index-DFhjO4Gg.d.cts +44 -0
- package/dist/{index-1z8vRTCt.d.cts → index-DdD5MVDL.d.ts} +121 -547
- package/dist/index-DrISNAOm.d.ts +45 -0
- package/dist/index-QBpffFW-.d.cts +86 -0
- package/dist/{index-J7Kc0oIQ.d.cts → index-_oMEWlDq.d.cts} +3 -3
- package/dist/{index-CYkjxu3s.d.ts → index-eJ6T_qGM.d.ts} +33 -4
- package/dist/index-qldRdbQw.d.ts +86 -0
- package/dist/index-xdGjv0nO.d.ts +127 -0
- package/dist/index.cjs +2334 -195
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1007 -648
- package/dist/index.d.ts +1007 -648
- package/dist/index.js +1204 -1172
- package/dist/index.js.map +1 -1
- package/dist/{meta-CnkLA_43.d.ts → meta-BGqSZ7mt.d.ts} +1 -1
- package/dist/{meta-DWbkoq1s.d.cts → meta-C0-8XW6Q.d.cts} +1 -1
- package/dist/{node-B-f-Lu-k.d.cts → node-C_IBuvX2.d.cts} +26 -1
- package/dist/{node-B-f-Lu-k.d.ts → node-C_IBuvX2.d.ts} +26 -1
- package/dist/{observable-DBnrwcar.d.cts → observable-Crr1jgzx.d.cts} +1 -1
- package/dist/{observable-uP-wy_uK.d.ts → observable-DCk45RH5.d.ts} +1 -1
- 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/reactive-layout/index.cjs +843 -29
- 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 +25 -10
- package/dist/reactive-layout-BaOQefHu.d.cts +183 -0
- package/dist/reactive-layout-D9gejYXE.d.ts +183 -0
- package/dist/{storage-BuTdpCI1.d.cts → storage-BMycWEh2.d.ts} +9 -1
- package/dist/{storage-F2X1U1x0.d.ts → storage-DiqWHzVI.d.cts} +9 -1
- package/package.json +32 -2
- package/dist/chunk-5DJTTKX3.js.map +0 -1
- package/dist/chunk-5WGT55R4.js.map +0 -1
- package/dist/chunk-AOCBDH4T.js.map +0 -1
- package/dist/chunk-MW4VAKAO.js +0 -47
- package/dist/chunk-MW4VAKAO.js.map +0 -1
- package/dist/chunk-TDEXAMGO.js.map +0 -1
- package/dist/chunk-XOFWRC73.js.map +0 -1
- /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,
|
|
@@ -200,6 +209,7 @@ __export(index_exports, {
|
|
|
200
209
|
fromPromise: () => fromPromise,
|
|
201
210
|
fromPulsar: () => fromPulsar,
|
|
202
211
|
fromRabbitMQ: () => fromRabbitMQ,
|
|
212
|
+
fromRaf: () => fromRaf,
|
|
203
213
|
fromRedisStream: () => fromRedisStream,
|
|
204
214
|
fromSSE: () => fromSSE,
|
|
205
215
|
fromSqlite: () => fromSqlite,
|
|
@@ -214,6 +224,7 @@ __export(index_exports, {
|
|
|
214
224
|
graph: () => graph_exports,
|
|
215
225
|
graphProfile: () => graphProfile,
|
|
216
226
|
graphspec: () => graphspec_exports,
|
|
227
|
+
guarded: () => guarded_execution_exports,
|
|
217
228
|
harness: () => harness_exports,
|
|
218
229
|
indexedDbStorage: () => indexedDbStorage,
|
|
219
230
|
interval: () => interval,
|
|
@@ -223,7 +234,9 @@ __export(index_exports, {
|
|
|
223
234
|
keepalive: () => keepalive,
|
|
224
235
|
last: () => last,
|
|
225
236
|
layout: () => reactive_layout_exports,
|
|
237
|
+
lens: () => lens_exports,
|
|
226
238
|
linear: () => linear,
|
|
239
|
+
listSnapshots: () => listSnapshots,
|
|
227
240
|
lru: () => lru,
|
|
228
241
|
map: () => map2,
|
|
229
242
|
matchesAnyPattern: () => matchesAnyPattern,
|
|
@@ -273,11 +286,15 @@ __export(index_exports, {
|
|
|
273
286
|
replay: () => replay,
|
|
274
287
|
replayWAL: () => replayWAL,
|
|
275
288
|
rescue: () => rescue,
|
|
289
|
+
resilientPipeline: () => resilient_pipeline_exports,
|
|
276
290
|
resolveBackoffPreset: () => resolveBackoffPreset,
|
|
277
291
|
resolveDescribeFields: () => resolveDescribeFields,
|
|
292
|
+
restoreSnapshot: () => restoreSnapshot,
|
|
278
293
|
retry: () => retry,
|
|
279
294
|
retrySource: () => retrySource,
|
|
295
|
+
runReduction: () => runReduction,
|
|
280
296
|
sample: () => sample,
|
|
297
|
+
saveSnapshot: () => saveSnapshot,
|
|
281
298
|
scan: () => scan,
|
|
282
299
|
serializeError: () => serializeError,
|
|
283
300
|
share: () => share,
|
|
@@ -289,6 +306,7 @@ __export(index_exports, {
|
|
|
289
306
|
solid: () => solid_exports,
|
|
290
307
|
sqliteStorage: () => sqliteStorage,
|
|
291
308
|
state: () => state,
|
|
309
|
+
surface: () => surface_exports,
|
|
292
310
|
svelte: () => svelte_exports,
|
|
293
311
|
switchMap: () => switchMap,
|
|
294
312
|
take: () => take,
|
|
@@ -326,6 +344,7 @@ __export(index_exports, {
|
|
|
326
344
|
version: () => version,
|
|
327
345
|
vue: () => vue_exports,
|
|
328
346
|
wallClockNs: () => wallClockNs,
|
|
347
|
+
watchTopologyTree: () => watchTopologyTree,
|
|
329
348
|
window: () => window,
|
|
330
349
|
windowCount: () => windowCount,
|
|
331
350
|
windowTime: () => windowTime,
|
|
@@ -1337,6 +1356,12 @@ var NodeImpl = class _NodeImpl {
|
|
|
1337
1356
|
_autoError;
|
|
1338
1357
|
_pausable;
|
|
1339
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;
|
|
1340
1365
|
_hashFn;
|
|
1341
1366
|
_versioning;
|
|
1342
1367
|
/**
|
|
@@ -1510,18 +1535,61 @@ var NodeImpl = class _NodeImpl {
|
|
|
1510
1535
|
if (this._inspectorHooks?.size === 0) this._inspectorHooks = void 0;
|
|
1511
1536
|
};
|
|
1512
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
|
+
}
|
|
1513
1564
|
allowsObserve(actor) {
|
|
1514
|
-
if (this._guard == null) return true;
|
|
1515
|
-
|
|
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;
|
|
1516
1574
|
}
|
|
1517
1575
|
// --- Guard helper ---
|
|
1518
1576
|
_checkGuard(options) {
|
|
1519
|
-
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;
|
|
1520
1581
|
const actor = normalizeActor(options?.actor);
|
|
1521
1582
|
const action2 = options?.delivery === "signal" ? "signal" : "write";
|
|
1522
|
-
if (!this._guard(actor, action2)) {
|
|
1583
|
+
if (this._guard != null && !this._guard(actor, action2)) {
|
|
1523
1584
|
throw new GuardDenied({ actor, action: action2, nodeName: this.name });
|
|
1524
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
|
+
}
|
|
1525
1593
|
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
1526
1594
|
}
|
|
1527
1595
|
// --- Public transport ---
|
|
@@ -3045,10 +3113,6 @@ function SagaHandler(cqrsName, sagaName, eventNames) {
|
|
|
3045
3113
|
};
|
|
3046
3114
|
}
|
|
3047
3115
|
|
|
3048
|
-
// src/extra/sources.ts
|
|
3049
|
-
var import_node_fs = require("fs");
|
|
3050
|
-
var import_node_path = require("path");
|
|
3051
|
-
|
|
3052
3116
|
// src/extra/cron.ts
|
|
3053
3117
|
function parseField(field, min, max) {
|
|
3054
3118
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -3186,6 +3250,72 @@ function fromTimer(ms, opts) {
|
|
|
3186
3250
|
return cleanup;
|
|
3187
3251
|
}, sourceOpts(rest));
|
|
3188
3252
|
}
|
|
3253
|
+
function fromRaf(opts) {
|
|
3254
|
+
const { signal, ...rest } = opts ?? {};
|
|
3255
|
+
return producer((a) => {
|
|
3256
|
+
let done = false;
|
|
3257
|
+
let rafId;
|
|
3258
|
+
let fallbackTimer;
|
|
3259
|
+
let abortListenerAdded = false;
|
|
3260
|
+
let visibilityListenerAdded = false;
|
|
3261
|
+
const raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame : void 0;
|
|
3262
|
+
const caf = typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : void 0;
|
|
3263
|
+
const doc = typeof document !== "undefined" ? document : void 0;
|
|
3264
|
+
const clearPending = () => {
|
|
3265
|
+
if (rafId !== void 0 && caf) caf(rafId);
|
|
3266
|
+
if (fallbackTimer !== void 0) clearTimeout(fallbackTimer);
|
|
3267
|
+
rafId = void 0;
|
|
3268
|
+
fallbackTimer = void 0;
|
|
3269
|
+
};
|
|
3270
|
+
const cleanup = () => {
|
|
3271
|
+
done = true;
|
|
3272
|
+
clearPending();
|
|
3273
|
+
if (abortListenerAdded) {
|
|
3274
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3275
|
+
abortListenerAdded = false;
|
|
3276
|
+
}
|
|
3277
|
+
if (visibilityListenerAdded && doc) {
|
|
3278
|
+
doc.removeEventListener("visibilitychange", onVisibilityChange);
|
|
3279
|
+
visibilityListenerAdded = false;
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
const onAbort = () => {
|
|
3283
|
+
if (done) return;
|
|
3284
|
+
cleanup();
|
|
3285
|
+
a.down([[ERROR, signal.reason]]);
|
|
3286
|
+
};
|
|
3287
|
+
const tick = (now) => {
|
|
3288
|
+
if (done) return;
|
|
3289
|
+
a.emit(now);
|
|
3290
|
+
scheduleNext();
|
|
3291
|
+
};
|
|
3292
|
+
const scheduleNext = () => {
|
|
3293
|
+
if (done) return;
|
|
3294
|
+
if (raf && (!doc || doc.visibilityState !== "hidden")) {
|
|
3295
|
+
rafId = raf(tick);
|
|
3296
|
+
} else {
|
|
3297
|
+
fallbackTimer = setTimeout(() => tick(performance.now()), 16);
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
const onVisibilityChange = () => {
|
|
3301
|
+
if (done) return;
|
|
3302
|
+
clearPending();
|
|
3303
|
+
scheduleNext();
|
|
3304
|
+
};
|
|
3305
|
+
if (signal?.aborted) {
|
|
3306
|
+
onAbort();
|
|
3307
|
+
return cleanup;
|
|
3308
|
+
}
|
|
3309
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3310
|
+
abortListenerAdded = signal !== void 0;
|
|
3311
|
+
if (doc && raf) {
|
|
3312
|
+
doc.addEventListener("visibilitychange", onVisibilityChange);
|
|
3313
|
+
visibilityListenerAdded = true;
|
|
3314
|
+
}
|
|
3315
|
+
scheduleNext();
|
|
3316
|
+
return cleanup;
|
|
3317
|
+
}, sourceOpts(rest));
|
|
3318
|
+
}
|
|
3189
3319
|
function fromCron(expr, opts) {
|
|
3190
3320
|
const schedule = parseCron(expr);
|
|
3191
3321
|
const { tickMs: tickOpt, output, ...rest } = opts ?? {};
|
|
@@ -3220,99 +3350,6 @@ function fromEvent(target, type, opts) {
|
|
|
3220
3350
|
return () => target.removeEventListener(type, handler, options);
|
|
3221
3351
|
}, sourceOpts(rest));
|
|
3222
3352
|
}
|
|
3223
|
-
function fromFSWatch(paths, opts) {
|
|
3224
|
-
const list = Array.isArray(paths) ? paths : [paths];
|
|
3225
|
-
if (list.length === 0) {
|
|
3226
|
-
throw new RangeError("fromFSWatch expects at least one path");
|
|
3227
|
-
}
|
|
3228
|
-
const { recursive = true, debounce: debounce2 = 100, include, exclude, ...rest } = opts ?? {};
|
|
3229
|
-
const includePatterns = include?.map(globToRegExp) ?? [];
|
|
3230
|
-
const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
|
|
3231
|
-
globToRegExp
|
|
3232
|
-
);
|
|
3233
|
-
return producer((a) => {
|
|
3234
|
-
const pending = /* @__PURE__ */ new Map();
|
|
3235
|
-
const watchers = [];
|
|
3236
|
-
let stopped = false;
|
|
3237
|
-
let terminalEmitted = false;
|
|
3238
|
-
let generation = 0;
|
|
3239
|
-
const closeWatchers = () => {
|
|
3240
|
-
for (const watcher of watchers.splice(0)) watcher.close();
|
|
3241
|
-
};
|
|
3242
|
-
const emitError = (err) => {
|
|
3243
|
-
if (terminalEmitted) return;
|
|
3244
|
-
terminalEmitted = true;
|
|
3245
|
-
stopped = true;
|
|
3246
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3247
|
-
timer = void 0;
|
|
3248
|
-
pending.clear();
|
|
3249
|
-
closeWatchers();
|
|
3250
|
-
a.down([[ERROR, err]]);
|
|
3251
|
-
};
|
|
3252
|
-
let timer;
|
|
3253
|
-
const flush = (token) => {
|
|
3254
|
-
timer = void 0;
|
|
3255
|
-
if (stopped || terminalEmitted) return;
|
|
3256
|
-
if (pending.size === 0) return;
|
|
3257
|
-
const batchMessages = [];
|
|
3258
|
-
for (const evt of pending.values()) batchMessages.push([DATA, evt]);
|
|
3259
|
-
pending.clear();
|
|
3260
|
-
if (stopped || terminalEmitted || token !== generation) return;
|
|
3261
|
-
a.down(batchMessages);
|
|
3262
|
-
};
|
|
3263
|
-
try {
|
|
3264
|
-
for (const basePath of list) {
|
|
3265
|
-
const watcher = (0, import_node_fs.watch)(
|
|
3266
|
-
basePath,
|
|
3267
|
-
{ recursive },
|
|
3268
|
-
(eventType, fileName) => {
|
|
3269
|
-
if (stopped || terminalEmitted) return;
|
|
3270
|
-
if (fileName == null) return;
|
|
3271
|
-
const rel = String(fileName).replaceAll("\\", "/");
|
|
3272
|
-
const abs = (0, import_node_path.resolve)(basePath, String(fileName));
|
|
3273
|
-
const normalized = abs.replaceAll("\\", "/");
|
|
3274
|
-
const root = (0, import_node_path.resolve)(basePath).replaceAll("\\", "/");
|
|
3275
|
-
const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
|
|
3276
|
-
const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
|
|
3277
|
-
if (!included) return;
|
|
3278
|
-
const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
|
|
3279
|
-
if (excluded) return;
|
|
3280
|
-
let kind = "change";
|
|
3281
|
-
if (eventType === "rename") {
|
|
3282
|
-
try {
|
|
3283
|
-
kind = (0, import_node_fs.existsSync)(normalized) ? "create" : "delete";
|
|
3284
|
-
} catch {
|
|
3285
|
-
kind = "rename";
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
pending.set(normalized, {
|
|
3289
|
-
type: kind,
|
|
3290
|
-
path: normalized,
|
|
3291
|
-
root,
|
|
3292
|
-
relative_path: relForMatch,
|
|
3293
|
-
timestamp_ns: wallClockNs()
|
|
3294
|
-
});
|
|
3295
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3296
|
-
const token = generation;
|
|
3297
|
-
timer = setTimeout(() => flush(token), debounce2);
|
|
3298
|
-
}
|
|
3299
|
-
);
|
|
3300
|
-
watcher.on("error", (err) => emitError(err));
|
|
3301
|
-
watchers.push(watcher);
|
|
3302
|
-
}
|
|
3303
|
-
} catch (err) {
|
|
3304
|
-
emitError(err);
|
|
3305
|
-
}
|
|
3306
|
-
return () => {
|
|
3307
|
-
stopped = true;
|
|
3308
|
-
generation += 1;
|
|
3309
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3310
|
-
timer = void 0;
|
|
3311
|
-
closeWatchers();
|
|
3312
|
-
pending.clear();
|
|
3313
|
-
};
|
|
3314
|
-
}, sourceOpts(rest));
|
|
3315
|
-
}
|
|
3316
3353
|
function fromIter(iterable, opts) {
|
|
3317
3354
|
return producer((a) => {
|
|
3318
3355
|
let cancelled = false;
|
|
@@ -4527,6 +4564,200 @@ var RingBuffer = class {
|
|
|
4527
4564
|
}
|
|
4528
4565
|
};
|
|
4529
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
|
+
|
|
4530
4761
|
// src/extra/utils/sizeof.ts
|
|
4531
4762
|
var OVERHEAD = {
|
|
4532
4763
|
object: 56,
|
|
@@ -5160,6 +5391,20 @@ var Graph = class _Graph {
|
|
|
5160
5391
|
_parent = void 0;
|
|
5161
5392
|
_storageDisposers = /* @__PURE__ */ new Set();
|
|
5162
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();
|
|
5163
5408
|
/**
|
|
5164
5409
|
* @param name - Non-empty graph id (must not contain `::` and must not
|
|
5165
5410
|
* equal the reserved meta segment `__meta__`).
|
|
@@ -5199,6 +5444,55 @@ var Graph = class _Graph {
|
|
|
5199
5444
|
return out;
|
|
5200
5445
|
}
|
|
5201
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
|
+
// ——————————————————————————————————————————————————————————————
|
|
5202
5496
|
// Node registry
|
|
5203
5497
|
// ——————————————————————————————————————————————————————————————
|
|
5204
5498
|
/**
|
|
@@ -5228,6 +5522,7 @@ var Graph = class _Graph {
|
|
|
5228
5522
|
}
|
|
5229
5523
|
this._nodes.set(name, node2);
|
|
5230
5524
|
this._nodeToName.set(node2, name);
|
|
5525
|
+
this._emitTopology({ kind: "added", name, nodeKind: "node" });
|
|
5231
5526
|
return node2;
|
|
5232
5527
|
}
|
|
5233
5528
|
/**
|
|
@@ -5268,22 +5563,23 @@ var Graph = class _Graph {
|
|
|
5268
5563
|
assertRegisterableName(name, this.name, "remove");
|
|
5269
5564
|
const child = this._mounts.get(name);
|
|
5270
5565
|
if (child) {
|
|
5271
|
-
const
|
|
5566
|
+
const audit3 = { kind: "mount", nodes: [], mounts: [] };
|
|
5272
5567
|
const targets = [];
|
|
5273
5568
|
child._collectObserveTargets("", targets);
|
|
5274
5569
|
for (const [p, n] of targets) {
|
|
5275
5570
|
if (!p.includes(`${PATH_SEP}${GRAPH_META_SEGMENT}${PATH_SEP}`)) {
|
|
5276
|
-
|
|
5571
|
+
audit3.nodes.push(p);
|
|
5277
5572
|
}
|
|
5278
5573
|
void n;
|
|
5279
5574
|
}
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5575
|
+
audit3.nodes.sort();
|
|
5576
|
+
audit3.mounts.push(name);
|
|
5577
|
+
audit3.mounts.push(...child._collectSubgraphs(`${name}${PATH_SEP}`));
|
|
5283
5578
|
this._mounts.delete(name);
|
|
5284
5579
|
child._parent = void 0;
|
|
5285
5580
|
teardownMountedGraph(child);
|
|
5286
|
-
|
|
5581
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "mount", audit: audit3 });
|
|
5582
|
+
return audit3;
|
|
5287
5583
|
}
|
|
5288
5584
|
const node2 = this._nodes.get(name);
|
|
5289
5585
|
if (!node2) {
|
|
@@ -5292,7 +5588,9 @@ var Graph = class _Graph {
|
|
|
5292
5588
|
this._nodes.delete(name);
|
|
5293
5589
|
this._nodeToName.delete(node2);
|
|
5294
5590
|
node2.down([[TEARDOWN]], { internal: true });
|
|
5295
|
-
|
|
5591
|
+
const audit2 = { kind: "node", nodes: [name], mounts: [] };
|
|
5592
|
+
this._emitTopology({ kind: "removed", name, nodeKind: "node", audit: audit2 });
|
|
5593
|
+
return audit2;
|
|
5296
5594
|
}
|
|
5297
5595
|
/**
|
|
5298
5596
|
* Bulk remove — invokes {@link Graph.remove} for every local name matching
|
|
@@ -5521,6 +5819,7 @@ var Graph = class _Graph {
|
|
|
5521
5819
|
}
|
|
5522
5820
|
this._mounts.set(name, child);
|
|
5523
5821
|
child._parent = this;
|
|
5822
|
+
this._emitTopology({ kind: "added", name, nodeKind: "mount" });
|
|
5524
5823
|
return child;
|
|
5525
5824
|
}
|
|
5526
5825
|
/**
|
|
@@ -5811,6 +6110,33 @@ var Graph = class _Graph {
|
|
|
5811
6110
|
}
|
|
5812
6111
|
return reachable(this.describe(), from, direction, opts);
|
|
5813
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
|
+
}
|
|
5814
6140
|
/**
|
|
5815
6141
|
* @internal Collect all qualified paths in this graph tree matching a
|
|
5816
6142
|
* glob pattern. Used by scoped autoCheckpoint subscription.
|
|
@@ -6489,7 +6815,7 @@ var Graph = class _Graph {
|
|
|
6489
6815
|
return;
|
|
6490
6816
|
}
|
|
6491
6817
|
const nextSeq = s.seq + 1;
|
|
6492
|
-
const timestamp_ns =
|
|
6818
|
+
const timestamp_ns = wallClockNs();
|
|
6493
6819
|
const isFirst = s.lastSnapshot == null;
|
|
6494
6820
|
const shouldCompact = isFirst || nextSeq % s.compactEvery === 0;
|
|
6495
6821
|
const record = shouldCompact ? {
|
|
@@ -7263,18 +7589,67 @@ __export(graph_exports, {
|
|
|
7263
7589
|
JsonCodec: () => JsonCodec,
|
|
7264
7590
|
SIZEOF_OVERHEAD: () => OVERHEAD,
|
|
7265
7591
|
SIZEOF_SYMBOL: () => SIZEOF_SYMBOL,
|
|
7592
|
+
SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
|
|
7266
7593
|
createDagCborCodec: () => createDagCborCodec,
|
|
7267
7594
|
createDagCborZstdCodec: () => createDagCborZstdCodec,
|
|
7268
7595
|
decodeEnvelope: () => decodeEnvelope,
|
|
7269
7596
|
diffForWAL: () => diffForWAL,
|
|
7270
7597
|
encodeEnvelope: () => encodeEnvelope,
|
|
7598
|
+
explainPath: () => explainPath,
|
|
7271
7599
|
graphProfile: () => graphProfile,
|
|
7272
7600
|
reachable: () => reachable,
|
|
7273
7601
|
registerBuiltinCodecs: () => registerBuiltinCodecs,
|
|
7274
7602
|
replayWAL: () => replayWAL,
|
|
7275
|
-
sizeof: () => sizeof
|
|
7603
|
+
sizeof: () => sizeof,
|
|
7604
|
+
watchTopologyTree: () => watchTopologyTree
|
|
7276
7605
|
});
|
|
7277
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
|
+
|
|
7278
7653
|
// src/patterns/_internal.ts
|
|
7279
7654
|
function emitToMeta(metaNode, value) {
|
|
7280
7655
|
if (metaNode == null) return;
|
|
@@ -8368,6 +8743,7 @@ __export(extra_exports, {
|
|
|
8368
8743
|
fromPromise: () => fromPromise,
|
|
8369
8744
|
fromPulsar: () => fromPulsar,
|
|
8370
8745
|
fromRabbitMQ: () => fromRabbitMQ,
|
|
8746
|
+
fromRaf: () => fromRaf,
|
|
8371
8747
|
fromRedisStream: () => fromRedisStream,
|
|
8372
8748
|
fromSSE: () => fromSSE,
|
|
8373
8749
|
fromSqlite: () => fromSqlite,
|
|
@@ -14142,37 +14518,140 @@ function reactiveList(initial, options = {}) {
|
|
|
14142
14518
|
};
|
|
14143
14519
|
}
|
|
14144
14520
|
|
|
14145
|
-
// src/extra/
|
|
14146
|
-
var
|
|
14147
|
-
var
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
function sortJsonValue2(value) {
|
|
14151
|
-
if (value === null || typeof value !== "object") return value;
|
|
14152
|
-
if (Array.isArray(value)) return value.map(sortJsonValue2);
|
|
14153
|
-
const obj = value;
|
|
14154
|
-
const keys = Object.keys(obj).sort();
|
|
14155
|
-
const out = {};
|
|
14156
|
-
for (const k of keys) out[k] = sortJsonValue2(obj[k]);
|
|
14157
|
-
return out;
|
|
14158
|
-
}
|
|
14159
|
-
function stableJsonString(data) {
|
|
14160
|
-
return JSON.stringify(sortJsonValue2(data), void 0, 0);
|
|
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 };
|
|
14161
14526
|
}
|
|
14162
|
-
function
|
|
14163
|
-
const
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
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
|
+
};
|
|
14176
14655
|
}
|
|
14177
14656
|
function dictStorage(storage) {
|
|
14178
14657
|
return {
|
|
@@ -14185,16 +14664,54 @@ function dictStorage(storage) {
|
|
|
14185
14664
|
},
|
|
14186
14665
|
clear(key) {
|
|
14187
14666
|
delete storage[key];
|
|
14667
|
+
},
|
|
14668
|
+
list() {
|
|
14669
|
+
return Object.keys(storage).sort();
|
|
14188
14670
|
}
|
|
14189
14671
|
};
|
|
14190
14672
|
}
|
|
14191
14673
|
function fileStorage(dir) {
|
|
14674
|
+
const encoder = new TextEncoder();
|
|
14675
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
14192
14676
|
const pathFor = (key) => {
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
(
|
|
14196
|
-
|
|
14197
|
-
|
|
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
|
+
}
|
|
14198
14715
|
};
|
|
14199
14716
|
return {
|
|
14200
14717
|
save(key, record) {
|
|
@@ -14231,6 +14748,21 @@ function fileStorage(dir) {
|
|
|
14231
14748
|
} catch (e) {
|
|
14232
14749
|
if (e.code !== "ENOENT") throw e;
|
|
14233
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
|
+
}
|
|
14234
14766
|
}
|
|
14235
14767
|
};
|
|
14236
14768
|
}
|
|
@@ -14253,6 +14785,10 @@ function sqliteStorage(path) {
|
|
|
14253
14785
|
clear(key) {
|
|
14254
14786
|
db.prepare(`DELETE FROM graphrefly_checkpoint WHERE k = ?`).run(key);
|
|
14255
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
|
+
},
|
|
14256
14792
|
close() {
|
|
14257
14793
|
try {
|
|
14258
14794
|
db.close();
|
|
@@ -14902,17 +15438,32 @@ function workerSelf(target, opts) {
|
|
|
14902
15438
|
// src/patterns/index.ts
|
|
14903
15439
|
var patterns_exports = {};
|
|
14904
15440
|
__export(patterns_exports, {
|
|
15441
|
+
SNAPSHOT_WIRE_VERSION: () => SNAPSHOT_WIRE_VERSION,
|
|
15442
|
+
SurfaceError: () => SurfaceError,
|
|
15443
|
+
accountability: () => audit_exports,
|
|
14905
15444
|
ai: () => ai_exports,
|
|
15445
|
+
asSurfaceError: () => asSurfaceError,
|
|
14906
15446
|
cqrs: () => cqrs_exports,
|
|
15447
|
+
createGraph: () => createGraph,
|
|
15448
|
+
deleteSnapshot: () => deleteSnapshot,
|
|
14907
15449
|
demoShell: () => demo_shell_exports,
|
|
15450
|
+
diffSnapshots: () => diffSnapshots,
|
|
14908
15451
|
domainTemplates: () => domain_templates_exports,
|
|
14909
15452
|
graphspec: () => graphspec_exports,
|
|
15453
|
+
guarded: () => guarded_execution_exports,
|
|
14910
15454
|
harness: () => harness_exports,
|
|
14911
15455
|
layout: () => reactive_layout_exports,
|
|
15456
|
+
lens: () => lens_exports,
|
|
15457
|
+
listSnapshots: () => listSnapshots,
|
|
14912
15458
|
memory: () => memory_exports,
|
|
14913
15459
|
messaging: () => messaging_exports,
|
|
14914
15460
|
orchestration: () => orchestration_exports,
|
|
14915
|
-
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
|
|
14916
15467
|
});
|
|
14917
15468
|
|
|
14918
15469
|
// src/patterns/ai.ts
|
|
@@ -17950,44 +18501,458 @@ async function suggestStrategy(graph, problem, adapter, opts) {
|
|
|
17950
18501
|
};
|
|
17951
18502
|
}
|
|
17952
18503
|
|
|
17953
|
-
// src/patterns/
|
|
17954
|
-
var
|
|
17955
|
-
__export(
|
|
17956
|
-
|
|
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
|
|
17957
18513
|
});
|
|
17958
|
-
|
|
17959
|
-
|
|
17960
|
-
|
|
17961
|
-
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17965
|
-
|
|
17966
|
-
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
|
|
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;
|
|
17970
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
|
+
});
|
|
17971
18743
|
}
|
|
17972
18744
|
}
|
|
17973
|
-
|
|
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
|
+
};
|
|
17974
18799
|
}
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
17982
|
-
|
|
17983
|
-
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
|
|
17987
|
-
|
|
17988
|
-
|
|
17989
|
-
|
|
17990
|
-
|
|
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
|
+
|
|
18918
|
+
// src/patterns/demo-shell.ts
|
|
18919
|
+
var demo_shell_exports = {};
|
|
18920
|
+
__export(demo_shell_exports, {
|
|
18921
|
+
demoShell: () => demoShell
|
|
18922
|
+
});
|
|
18923
|
+
|
|
18924
|
+
// src/patterns/reactive-layout/reactive-layout.ts
|
|
18925
|
+
function isCJK(s) {
|
|
18926
|
+
for (const ch of s) {
|
|
18927
|
+
const c = ch.codePointAt(0);
|
|
18928
|
+
if (c >= 19968 && c <= 40959 || // CJK Unified Ideographs
|
|
18929
|
+
c >= 13312 && c <= 19903 || // CJK Extension A
|
|
18930
|
+
c >= 12288 && c <= 12351 || // CJK Symbols and Punctuation
|
|
18931
|
+
c >= 12352 && c <= 12447 || // Hiragana
|
|
18932
|
+
c >= 12448 && c <= 12543 || // Katakana
|
|
18933
|
+
c >= 44032 && c <= 55215 || // Hangul
|
|
18934
|
+
c >= 65280 && c <= 65519) {
|
|
18935
|
+
return true;
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18938
|
+
return false;
|
|
18939
|
+
}
|
|
18940
|
+
var kinsokuStart = /* @__PURE__ */ new Set([
|
|
18941
|
+
"\uFF0C",
|
|
18942
|
+
"\uFF0E",
|
|
18943
|
+
"\uFF01",
|
|
18944
|
+
"\uFF1A",
|
|
18945
|
+
"\uFF1B",
|
|
18946
|
+
"\uFF1F",
|
|
18947
|
+
"\u3001",
|
|
18948
|
+
"\u3002",
|
|
18949
|
+
"\u30FB",
|
|
18950
|
+
"\uFF09",
|
|
18951
|
+
"\u3015",
|
|
18952
|
+
"\u3009",
|
|
18953
|
+
"\u300B",
|
|
18954
|
+
"\u300D",
|
|
18955
|
+
"\u300F",
|
|
17991
18956
|
"\u3011"
|
|
17992
18957
|
]);
|
|
17993
18958
|
var leftStickyPunctuation = /* @__PURE__ */ new Set([
|
|
@@ -18054,7 +19019,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
18054
19019
|
const normalized = normalizeWhitespace(text);
|
|
18055
19020
|
if (normalized.length === 0) return [];
|
|
18056
19021
|
const pieces = segmentText(normalized);
|
|
18057
|
-
const
|
|
19022
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
18058
19023
|
granularity: "grapheme"
|
|
18059
19024
|
});
|
|
18060
19025
|
const rawTexts = [];
|
|
@@ -18098,7 +19063,8 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
18098
19063
|
let w = fontCache.get(seg);
|
|
18099
19064
|
if (w === void 0) {
|
|
18100
19065
|
if (stats) stats.misses += 1;
|
|
18101
|
-
|
|
19066
|
+
const raw = adapter.measureSegment(seg, font).width;
|
|
19067
|
+
w = Number.isFinite(raw) && raw >= 0 ? raw : 0;
|
|
18102
19068
|
fontCache.set(seg, w);
|
|
18103
19069
|
} else if (stats) {
|
|
18104
19070
|
stats.hits += 1;
|
|
@@ -18120,7 +19086,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
18120
19086
|
}
|
|
18121
19087
|
if (isCJK(t)) {
|
|
18122
19088
|
let unitText = "";
|
|
18123
|
-
for (const gs of
|
|
19089
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
18124
19090
|
const grapheme = gs.segment;
|
|
18125
19091
|
if (unitText.length > 0 && kinsokuStart.has(grapheme)) {
|
|
18126
19092
|
unitText += grapheme;
|
|
@@ -18152,7 +19118,7 @@ function analyzeAndMeasure(text, font, adapter, cache, stats) {
|
|
|
18152
19118
|
let graphemeWidths = null;
|
|
18153
19119
|
if (mergedWordLike[i] && t.length > 1) {
|
|
18154
19120
|
const gWidths = [];
|
|
18155
|
-
for (const gs of
|
|
19121
|
+
for (const gs of graphemeSegmenter2.segment(t)) {
|
|
18156
19122
|
gWidths.push(measureCached(gs.segment));
|
|
18157
19123
|
}
|
|
18158
19124
|
if (gWidths.length > 1) {
|
|
@@ -18192,10 +19158,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18192
19158
|
const seg = segments[i];
|
|
18193
19159
|
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
18194
19160
|
if (i === lineStartSeg && lineStartGrapheme > 0 && seg.graphemeWidths) {
|
|
18195
|
-
const
|
|
19161
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
18196
19162
|
granularity: "grapheme"
|
|
18197
19163
|
});
|
|
18198
|
-
const graphemes = [...
|
|
19164
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
18199
19165
|
text += graphemes.slice(lineStartGrapheme).join("");
|
|
18200
19166
|
} else {
|
|
18201
19167
|
text += seg.text;
|
|
@@ -18203,10 +19169,10 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18203
19169
|
}
|
|
18204
19170
|
if (endGrapheme > 0 && endSeg < segments.length) {
|
|
18205
19171
|
const seg = segments[endSeg];
|
|
18206
|
-
const
|
|
19172
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
18207
19173
|
granularity: "grapheme"
|
|
18208
19174
|
});
|
|
18209
|
-
const graphemes = [...
|
|
19175
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
18210
19176
|
const startG = lineStartSeg === endSeg ? lineStartGrapheme : 0;
|
|
18211
19177
|
text += graphemes.slice(startG, endGrapheme).join("");
|
|
18212
19178
|
}
|
|
@@ -18226,7 +19192,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18226
19192
|
pendingBreakSeg = -1;
|
|
18227
19193
|
pendingBreakWidth = 0;
|
|
18228
19194
|
}
|
|
18229
|
-
function
|
|
19195
|
+
function canBreakAfter2(kind) {
|
|
18230
19196
|
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
18231
19197
|
}
|
|
18232
19198
|
function startLine(segIdx, graphemeIdx, width) {
|
|
@@ -18271,7 +19237,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18271
19237
|
} else {
|
|
18272
19238
|
startLine(i, 0, w);
|
|
18273
19239
|
}
|
|
18274
|
-
if (
|
|
19240
|
+
if (canBreakAfter2(seg.kind)) {
|
|
18275
19241
|
pendingBreakSeg = i + 1;
|
|
18276
19242
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
18277
19243
|
}
|
|
@@ -18279,7 +19245,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18279
19245
|
}
|
|
18280
19246
|
const newW = lineW + w;
|
|
18281
19247
|
if (newW > maxWidth + 5e-3) {
|
|
18282
|
-
if (
|
|
19248
|
+
if (canBreakAfter2(seg.kind)) {
|
|
18283
19249
|
lineW += w;
|
|
18284
19250
|
lineEndSeg = i + 1;
|
|
18285
19251
|
lineEndGrapheme = 0;
|
|
@@ -18303,7 +19269,7 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18303
19269
|
lineW = newW;
|
|
18304
19270
|
lineEndSeg = i + 1;
|
|
18305
19271
|
lineEndGrapheme = 0;
|
|
18306
|
-
if (
|
|
19272
|
+
if (canBreakAfter2(seg.kind)) {
|
|
18307
19273
|
pendingBreakSeg = i + 1;
|
|
18308
19274
|
pendingBreakWidth = seg.kind === "space" ? lineW - w : lineW;
|
|
18309
19275
|
}
|
|
@@ -18334,9 +19300,289 @@ function computeLineBreaks(segments, maxWidth, adapter, font, cache) {
|
|
|
18334
19300
|
}
|
|
18335
19301
|
}
|
|
18336
19302
|
}
|
|
19303
|
+
function canBreakAfter(kind) {
|
|
19304
|
+
return kind === "space" || kind === "zero-width-break" || kind === "soft-hyphen";
|
|
19305
|
+
}
|
|
19306
|
+
var _graphemeSegmenter = null;
|
|
19307
|
+
function graphemeSegmenter() {
|
|
19308
|
+
if (_graphemeSegmenter === null) {
|
|
19309
|
+
_graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
19310
|
+
}
|
|
19311
|
+
return _graphemeSegmenter;
|
|
19312
|
+
}
|
|
19313
|
+
function sliceSegmentText(seg, startG, endG) {
|
|
19314
|
+
if (startG === 0 && endG < 0) return seg.text;
|
|
19315
|
+
const graphemes = [...graphemeSegmenter().segment(seg.text)].map((g) => g.segment);
|
|
19316
|
+
const stop = endG < 0 ? graphemes.length : endG;
|
|
19317
|
+
return graphemes.slice(startG, stop).join("");
|
|
19318
|
+
}
|
|
19319
|
+
function buildLineText(segments, startSeg, startG, endSeg, endG, appendHyphen) {
|
|
19320
|
+
let text = "";
|
|
19321
|
+
for (let i = startSeg; i < endSeg; i++) {
|
|
19322
|
+
const seg = segments[i];
|
|
19323
|
+
if (seg.kind === "soft-hyphen" || seg.kind === "hard-break") continue;
|
|
19324
|
+
if (i === startSeg && startG > 0) {
|
|
19325
|
+
text += sliceSegmentText(seg, startG, -1);
|
|
19326
|
+
} else {
|
|
19327
|
+
text += seg.text;
|
|
19328
|
+
}
|
|
19329
|
+
}
|
|
19330
|
+
if (endG > 0 && endSeg < segments.length) {
|
|
19331
|
+
const seg = segments[endSeg];
|
|
19332
|
+
const from = startSeg === endSeg ? startG : 0;
|
|
19333
|
+
text += sliceSegmentText(seg, from, endG);
|
|
19334
|
+
}
|
|
19335
|
+
if (appendHyphen) text += "-";
|
|
19336
|
+
return text;
|
|
19337
|
+
}
|
|
19338
|
+
function resolveHyphenWidth(ctx) {
|
|
19339
|
+
if (!ctx || !ctx.adapter || !ctx.font) return 0;
|
|
19340
|
+
const cache = ctx.cache;
|
|
19341
|
+
if (cache) {
|
|
19342
|
+
let fc = cache.get(ctx.font);
|
|
19343
|
+
if (!fc) {
|
|
19344
|
+
fc = /* @__PURE__ */ new Map();
|
|
19345
|
+
cache.set(ctx.font, fc);
|
|
19346
|
+
}
|
|
19347
|
+
let hw = fc.get("-");
|
|
19348
|
+
if (hw === void 0) {
|
|
19349
|
+
hw = ctx.adapter.measureSegment("-", ctx.font).width;
|
|
19350
|
+
fc.set("-", hw);
|
|
19351
|
+
}
|
|
19352
|
+
return hw;
|
|
19353
|
+
}
|
|
19354
|
+
return ctx.adapter.measureSegment("-", ctx.font).width;
|
|
19355
|
+
}
|
|
19356
|
+
function layoutNextLine(segments, cursor, slotWidth, ctx) {
|
|
19357
|
+
let i = cursor.segmentIndex;
|
|
19358
|
+
const initialG = cursor.graphemeIndex;
|
|
19359
|
+
if (i >= segments.length) return null;
|
|
19360
|
+
if (initialG === 0) {
|
|
19361
|
+
while (i < segments.length) {
|
|
19362
|
+
const seg = segments[i];
|
|
19363
|
+
if (seg.kind === "hard-break") {
|
|
19364
|
+
return {
|
|
19365
|
+
text: "",
|
|
19366
|
+
width: 0,
|
|
19367
|
+
start: { segmentIndex: cursor.segmentIndex, graphemeIndex: 0 },
|
|
19368
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
19369
|
+
};
|
|
19370
|
+
}
|
|
19371
|
+
if (seg.kind === "space" || seg.kind === "zero-width-break" || seg.kind === "soft-hyphen") {
|
|
19372
|
+
i += 1;
|
|
19373
|
+
continue;
|
|
19374
|
+
}
|
|
19375
|
+
break;
|
|
19376
|
+
}
|
|
19377
|
+
if (i >= segments.length) return null;
|
|
19378
|
+
}
|
|
19379
|
+
const hyphenWidth = resolveHyphenWidth(ctx);
|
|
19380
|
+
const startSeg = i;
|
|
19381
|
+
const startG = i === cursor.segmentIndex ? initialG : 0;
|
|
19382
|
+
let lineW = 0;
|
|
19383
|
+
let lineEndSeg = startSeg;
|
|
19384
|
+
let lineEndG = 0;
|
|
19385
|
+
let hasContent = false;
|
|
19386
|
+
let pendingBreakSeg = -1;
|
|
19387
|
+
let pendingBreakG = 0;
|
|
19388
|
+
let pendingBreakWidth = 0;
|
|
19389
|
+
let pendingBreakSoftHyphen = false;
|
|
19390
|
+
const recordPending = (sIdx, gIdx, widthAtBreak, kind) => {
|
|
19391
|
+
pendingBreakSeg = sIdx;
|
|
19392
|
+
pendingBreakG = gIdx;
|
|
19393
|
+
pendingBreakWidth = widthAtBreak;
|
|
19394
|
+
pendingBreakSoftHyphen = kind === "soft-hyphen";
|
|
19395
|
+
};
|
|
19396
|
+
const consumeBreakable = (segIdx, gStart, gWidths) => {
|
|
19397
|
+
for (let g = gStart; g < gWidths.length; g++) {
|
|
19398
|
+
const gw = gWidths[g];
|
|
19399
|
+
if (!hasContent) {
|
|
19400
|
+
lineW = gw;
|
|
19401
|
+
lineEndSeg = segIdx;
|
|
19402
|
+
lineEndG = g + 1;
|
|
19403
|
+
hasContent = true;
|
|
19404
|
+
continue;
|
|
19405
|
+
}
|
|
19406
|
+
if (lineW + gw > slotWidth + 5e-3) {
|
|
19407
|
+
return true;
|
|
19408
|
+
}
|
|
19409
|
+
lineW += gw;
|
|
19410
|
+
lineEndSeg = segIdx;
|
|
19411
|
+
lineEndG = g + 1;
|
|
19412
|
+
}
|
|
19413
|
+
if (lineEndSeg === segIdx && lineEndG === gWidths.length) {
|
|
19414
|
+
lineEndSeg = segIdx + 1;
|
|
19415
|
+
lineEndG = 0;
|
|
19416
|
+
}
|
|
19417
|
+
return false;
|
|
19418
|
+
};
|
|
19419
|
+
if (startG > 0 && startSeg < segments.length) {
|
|
19420
|
+
const seg = segments[startSeg];
|
|
19421
|
+
if (seg.graphemeWidths) {
|
|
19422
|
+
const overflowed = consumeBreakable(startSeg, startG, seg.graphemeWidths);
|
|
19423
|
+
if (overflowed) {
|
|
19424
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
19425
|
+
return {
|
|
19426
|
+
text: text2,
|
|
19427
|
+
width: lineW,
|
|
19428
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19429
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19430
|
+
};
|
|
19431
|
+
}
|
|
19432
|
+
i = lineEndSeg;
|
|
19433
|
+
} else {
|
|
19434
|
+
}
|
|
19435
|
+
}
|
|
19436
|
+
for (; i < segments.length; ) {
|
|
19437
|
+
const seg = segments[i];
|
|
19438
|
+
if (seg.kind === "hard-break") {
|
|
19439
|
+
if (hasContent) {
|
|
19440
|
+
const endsAtSoftHyphen2 = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
19441
|
+
const text2 = buildLineText(
|
|
19442
|
+
segments,
|
|
19443
|
+
startSeg,
|
|
19444
|
+
startG,
|
|
19445
|
+
lineEndSeg,
|
|
19446
|
+
lineEndG,
|
|
19447
|
+
endsAtSoftHyphen2
|
|
19448
|
+
);
|
|
19449
|
+
return {
|
|
19450
|
+
text: text2,
|
|
19451
|
+
width: lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0),
|
|
19452
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19453
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19454
|
+
};
|
|
19455
|
+
}
|
|
19456
|
+
return {
|
|
19457
|
+
text: "",
|
|
19458
|
+
width: 0,
|
|
19459
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19460
|
+
end: { segmentIndex: i + 1, graphemeIndex: 0 }
|
|
19461
|
+
};
|
|
19462
|
+
}
|
|
19463
|
+
const w = seg.width;
|
|
19464
|
+
if (!hasContent) {
|
|
19465
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
19466
|
+
const overflowed = consumeBreakable(i, 0, seg.graphemeWidths);
|
|
19467
|
+
if (overflowed) {
|
|
19468
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
19469
|
+
return {
|
|
19470
|
+
text: text2,
|
|
19471
|
+
width: lineW,
|
|
19472
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19473
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19474
|
+
};
|
|
19475
|
+
}
|
|
19476
|
+
i = lineEndSeg;
|
|
19477
|
+
continue;
|
|
19478
|
+
}
|
|
19479
|
+
lineW = w;
|
|
19480
|
+
lineEndSeg = i + 1;
|
|
19481
|
+
lineEndG = 0;
|
|
19482
|
+
hasContent = true;
|
|
19483
|
+
if (canBreakAfter(seg.kind)) {
|
|
19484
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
19485
|
+
}
|
|
19486
|
+
i += 1;
|
|
19487
|
+
continue;
|
|
19488
|
+
}
|
|
19489
|
+
const newW = lineW + w;
|
|
19490
|
+
if (newW > slotWidth + 5e-3) {
|
|
19491
|
+
if (canBreakAfter(seg.kind)) {
|
|
19492
|
+
lineEndSeg = i + 1;
|
|
19493
|
+
lineEndG = 0;
|
|
19494
|
+
const endsAtSoftHyphen2 = seg.kind === "soft-hyphen";
|
|
19495
|
+
const finalWidth = seg.kind === "space" ? lineW : lineW + (endsAtSoftHyphen2 ? hyphenWidth : 0);
|
|
19496
|
+
const text3 = buildLineText(
|
|
19497
|
+
segments,
|
|
19498
|
+
startSeg,
|
|
19499
|
+
startG,
|
|
19500
|
+
lineEndSeg,
|
|
19501
|
+
lineEndG,
|
|
19502
|
+
endsAtSoftHyphen2
|
|
19503
|
+
);
|
|
19504
|
+
return {
|
|
19505
|
+
text: text3,
|
|
19506
|
+
width: finalWidth,
|
|
19507
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19508
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19509
|
+
};
|
|
19510
|
+
}
|
|
19511
|
+
if (pendingBreakSeg >= 0) {
|
|
19512
|
+
const text3 = buildLineText(
|
|
19513
|
+
segments,
|
|
19514
|
+
startSeg,
|
|
19515
|
+
startG,
|
|
19516
|
+
pendingBreakSeg,
|
|
19517
|
+
pendingBreakG,
|
|
19518
|
+
pendingBreakSoftHyphen
|
|
19519
|
+
);
|
|
19520
|
+
return {
|
|
19521
|
+
text: text3,
|
|
19522
|
+
width: pendingBreakWidth + (pendingBreakSoftHyphen ? hyphenWidth : 0),
|
|
19523
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19524
|
+
end: { segmentIndex: pendingBreakSeg, graphemeIndex: pendingBreakG }
|
|
19525
|
+
};
|
|
19526
|
+
}
|
|
19527
|
+
if (w > slotWidth && seg.graphemeWidths) {
|
|
19528
|
+
const text3 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
19529
|
+
return {
|
|
19530
|
+
text: text3,
|
|
19531
|
+
width: lineW,
|
|
19532
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19533
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19534
|
+
};
|
|
19535
|
+
}
|
|
19536
|
+
const text2 = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, false);
|
|
19537
|
+
return {
|
|
19538
|
+
text: text2,
|
|
19539
|
+
width: lineW,
|
|
19540
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19541
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19542
|
+
};
|
|
19543
|
+
}
|
|
19544
|
+
lineW = newW;
|
|
19545
|
+
lineEndSeg = i + 1;
|
|
19546
|
+
lineEndG = 0;
|
|
19547
|
+
if (canBreakAfter(seg.kind)) {
|
|
19548
|
+
recordPending(i + 1, 0, seg.kind === "space" ? lineW - w : lineW, seg.kind);
|
|
19549
|
+
}
|
|
19550
|
+
i += 1;
|
|
19551
|
+
}
|
|
19552
|
+
if (!hasContent) return null;
|
|
19553
|
+
const endsAtSoftHyphen = lineEndSeg > 0 && segments[lineEndSeg - 1]?.kind === "soft-hyphen";
|
|
19554
|
+
const text = buildLineText(segments, startSeg, startG, lineEndSeg, lineEndG, endsAtSoftHyphen);
|
|
19555
|
+
return {
|
|
19556
|
+
text,
|
|
19557
|
+
width: lineW + (endsAtSoftHyphen ? hyphenWidth : 0),
|
|
19558
|
+
start: { segmentIndex: startSeg, graphemeIndex: startG },
|
|
19559
|
+
end: { segmentIndex: lineEndSeg, graphemeIndex: lineEndG }
|
|
19560
|
+
};
|
|
19561
|
+
}
|
|
19562
|
+
function carveTextLineSlots(base, blocked, minSlotWidth = 0) {
|
|
19563
|
+
let slots = [base];
|
|
19564
|
+
for (let bi = 0; bi < blocked.length; bi++) {
|
|
19565
|
+
const block = blocked[bi];
|
|
19566
|
+
const next = [];
|
|
19567
|
+
for (let si = 0; si < slots.length; si++) {
|
|
19568
|
+
const slot = slots[si];
|
|
19569
|
+
if (block.right <= slot.left || block.left >= slot.right) {
|
|
19570
|
+
next.push(slot);
|
|
19571
|
+
continue;
|
|
19572
|
+
}
|
|
19573
|
+
if (block.left > slot.left) next.push({ left: slot.left, right: block.left });
|
|
19574
|
+
if (block.right < slot.right) next.push({ left: block.right, right: slot.right });
|
|
19575
|
+
}
|
|
19576
|
+
slots = next;
|
|
19577
|
+
}
|
|
19578
|
+
if (minSlotWidth > 0) {
|
|
19579
|
+
return slots.filter((s) => s.right - s.left >= minSlotWidth);
|
|
19580
|
+
}
|
|
19581
|
+
return slots;
|
|
19582
|
+
}
|
|
18337
19583
|
function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
18338
19584
|
const positions = [];
|
|
18339
|
-
const
|
|
19585
|
+
const graphemeSegmenter2 = new Intl.Segmenter(void 0, {
|
|
18340
19586
|
granularity: "grapheme"
|
|
18341
19587
|
});
|
|
18342
19588
|
for (let lineIdx = 0; lineIdx < lineBreaks.lines.length; lineIdx++) {
|
|
@@ -18349,7 +19595,7 @@ function computeCharPositions(lineBreaks, segments, lineHeight) {
|
|
|
18349
19595
|
if (si >= line.endSegment && line.endGrapheme === 0) break;
|
|
18350
19596
|
continue;
|
|
18351
19597
|
}
|
|
18352
|
-
const graphemes = [...
|
|
19598
|
+
const graphemes = [...graphemeSegmenter2.segment(seg.text)].map((g) => g.segment);
|
|
18353
19599
|
if (graphemes.length === 0) continue;
|
|
18354
19600
|
const startG = si === line.startSegment ? line.startGrapheme : 0;
|
|
18355
19601
|
let endG;
|
|
@@ -20649,6 +21895,65 @@ ${validation.errors.join("\n")}`);
|
|
|
20649
21895
|
return parsed;
|
|
20650
21896
|
}
|
|
20651
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
|
+
|
|
20652
21957
|
// src/patterns/harness/index.ts
|
|
20653
21958
|
var harness_exports = {};
|
|
20654
21959
|
__export(harness_exports, {
|
|
@@ -21330,6 +22635,257 @@ function truncate(s, max) {
|
|
|
21330
22635
|
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
21331
22636
|
}
|
|
21332
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
|
+
|
|
21333
22889
|
// src/patterns/reactive-layout/index.ts
|
|
21334
22890
|
var reactive_layout_exports = {};
|
|
21335
22891
|
__export(reactive_layout_exports, {
|
|
@@ -21340,14 +22896,20 @@ __export(reactive_layout_exports, {
|
|
|
21340
22896
|
PrecomputedAdapter: () => PrecomputedAdapter,
|
|
21341
22897
|
SvgBoundsAdapter: () => SvgBoundsAdapter,
|
|
21342
22898
|
analyzeAndMeasure: () => analyzeAndMeasure,
|
|
22899
|
+
carveTextLineSlots: () => carveTextLineSlots,
|
|
22900
|
+
circleIntervalForBand: () => circleIntervalForBand,
|
|
21343
22901
|
computeBlockFlow: () => computeBlockFlow,
|
|
21344
22902
|
computeCharPositions: () => computeCharPositions,
|
|
22903
|
+
computeFlowLines: () => computeFlowLines,
|
|
21345
22904
|
computeLineBreaks: () => computeLineBreaks,
|
|
21346
22905
|
computeTotalHeight: () => computeTotalHeight,
|
|
22906
|
+
layoutNextLine: () => layoutNextLine,
|
|
21347
22907
|
measureBlock: () => measureBlock,
|
|
21348
22908
|
measureBlocks: () => measureBlocks,
|
|
21349
22909
|
reactiveBlockLayout: () => reactiveBlockLayout,
|
|
21350
|
-
|
|
22910
|
+
reactiveFlowLayout: () => reactiveFlowLayout,
|
|
22911
|
+
reactiveLayout: () => reactiveLayout,
|
|
22912
|
+
rectIntervalForBand: () => rectIntervalForBand
|
|
21351
22913
|
});
|
|
21352
22914
|
|
|
21353
22915
|
// src/patterns/reactive-layout/measurement-adapters.ts
|
|
@@ -21544,7 +23106,7 @@ var CanvasMeasureAdapter = class {
|
|
|
21544
23106
|
this.currentFont = font;
|
|
21545
23107
|
}
|
|
21546
23108
|
let width = ctx.measureText(text).width;
|
|
21547
|
-
if (this.emojiCorrection !== 1 &&
|
|
23109
|
+
if (this.emojiCorrection !== 1 && new RegExp("\\p{Emoji_Presentation}", "u").test(text)) {
|
|
21548
23110
|
width *= this.emojiCorrection;
|
|
21549
23111
|
}
|
|
21550
23112
|
return { width };
|
|
@@ -21815,6 +23377,564 @@ function reactiveBlockLayout(opts) {
|
|
|
21815
23377
|
};
|
|
21816
23378
|
}
|
|
21817
23379
|
|
|
23380
|
+
// src/patterns/reactive-layout/reactive-flow-layout.ts
|
|
23381
|
+
function circleIntervalForBand(o, bandTop, bandBottom) {
|
|
23382
|
+
const hPad = o.hPad ?? 0;
|
|
23383
|
+
const vPad = o.vPad ?? 0;
|
|
23384
|
+
const top = bandTop - vPad;
|
|
23385
|
+
const bottom = bandBottom + vPad;
|
|
23386
|
+
if (top >= o.cy + o.r || bottom <= o.cy - o.r) return null;
|
|
23387
|
+
const minDy = o.cy >= top && o.cy <= bottom ? 0 : o.cy < top ? top - o.cy : o.cy - bottom;
|
|
23388
|
+
if (minDy >= o.r) return null;
|
|
23389
|
+
const maxDx = Math.sqrt(o.r * o.r - minDy * minDy);
|
|
23390
|
+
return { left: o.cx - maxDx - hPad, right: o.cx + maxDx + hPad };
|
|
23391
|
+
}
|
|
23392
|
+
function rectIntervalForBand(o, bandTop, bandBottom) {
|
|
23393
|
+
const hPad = o.hPad ?? 0;
|
|
23394
|
+
const vPad = o.vPad ?? 0;
|
|
23395
|
+
if (bandBottom <= o.y - vPad) return null;
|
|
23396
|
+
if (bandTop >= o.y + o.h + vPad) return null;
|
|
23397
|
+
return { left: o.x - hPad, right: o.x + o.w + hPad };
|
|
23398
|
+
}
|
|
23399
|
+
function obstacleIntervalForBand(o, bandTop, bandBottom) {
|
|
23400
|
+
return o.kind === "circle" ? circleIntervalForBand(o, bandTop, bandBottom) : rectIntervalForBand(o, bandTop, bandBottom);
|
|
23401
|
+
}
|
|
23402
|
+
function computeFlowLines(segments, container, columns, obstacles, lineHeight, minSlotWidth) {
|
|
23403
|
+
const lines = [];
|
|
23404
|
+
let cursor = { segmentIndex: 0, graphemeIndex: 0 };
|
|
23405
|
+
if (segments.length === 0 || columns.count <= 0 || lineHeight <= 0) {
|
|
23406
|
+
return { lines, cursor };
|
|
23407
|
+
}
|
|
23408
|
+
const padX = container.paddingX ?? 0;
|
|
23409
|
+
const padY = container.paddingY ?? 0;
|
|
23410
|
+
const availWidth = Math.max(0, container.width - padX * 2);
|
|
23411
|
+
const availHeight = Math.max(0, container.height - padY * 2);
|
|
23412
|
+
const gapTotal = columns.gap * Math.max(0, columns.count - 1);
|
|
23413
|
+
const colWidth = Math.max(0, (availWidth - gapTotal) / columns.count);
|
|
23414
|
+
if (colWidth <= 0) return { lines, cursor };
|
|
23415
|
+
outerCol: for (let col = 0; col < columns.count; col++) {
|
|
23416
|
+
const colLeft = padX + col * (colWidth + columns.gap);
|
|
23417
|
+
const colRight = colLeft + colWidth;
|
|
23418
|
+
let bandTop = padY;
|
|
23419
|
+
while (bandTop + lineHeight <= padY + availHeight) {
|
|
23420
|
+
const bandBottom = bandTop + lineHeight;
|
|
23421
|
+
const blocked = [];
|
|
23422
|
+
for (let oi = 0; oi < obstacles.length; oi++) {
|
|
23423
|
+
const iv = obstacleIntervalForBand(obstacles[oi], bandTop, bandBottom);
|
|
23424
|
+
if (iv !== null) blocked.push(iv);
|
|
23425
|
+
}
|
|
23426
|
+
const slots = carveTextLineSlots({ left: colLeft, right: colRight }, blocked, minSlotWidth);
|
|
23427
|
+
if (slots.length === 0) {
|
|
23428
|
+
bandTop += lineHeight;
|
|
23429
|
+
continue;
|
|
23430
|
+
}
|
|
23431
|
+
let hardBreakThisBand = false;
|
|
23432
|
+
for (let si = 0; si < slots.length; si++) {
|
|
23433
|
+
const slot = slots[si];
|
|
23434
|
+
const slotW = slot.right - slot.left;
|
|
23435
|
+
const line = layoutNextLine(segments, cursor, slotW);
|
|
23436
|
+
if (line === null) {
|
|
23437
|
+
return { lines, cursor };
|
|
23438
|
+
}
|
|
23439
|
+
if (line.text.length === 0 && line.width === 0) {
|
|
23440
|
+
cursor = line.end;
|
|
23441
|
+
hardBreakThisBand = true;
|
|
23442
|
+
break;
|
|
23443
|
+
}
|
|
23444
|
+
lines.push({
|
|
23445
|
+
x: slot.left,
|
|
23446
|
+
y: bandTop,
|
|
23447
|
+
width: line.width,
|
|
23448
|
+
slotWidth: slotW,
|
|
23449
|
+
text: line.text,
|
|
23450
|
+
columnIndex: col,
|
|
23451
|
+
flushToRight: slot.right < colRight - 0.5
|
|
23452
|
+
});
|
|
23453
|
+
cursor = line.end;
|
|
23454
|
+
}
|
|
23455
|
+
bandTop += lineHeight;
|
|
23456
|
+
if (hardBreakThisBand) continue;
|
|
23457
|
+
if (cursor.segmentIndex >= segments.length) break outerCol;
|
|
23458
|
+
}
|
|
23459
|
+
if (cursor.segmentIndex >= segments.length) break;
|
|
23460
|
+
}
|
|
23461
|
+
return { lines, cursor };
|
|
23462
|
+
}
|
|
23463
|
+
function reactiveFlowLayout(opts) {
|
|
23464
|
+
const { adapter, name = "reactive-flow-layout", minSlotWidth = 20 } = opts;
|
|
23465
|
+
const g = new Graph(name);
|
|
23466
|
+
const measureCache = /* @__PURE__ */ new Map();
|
|
23467
|
+
const textNode = state(opts.text ?? "", { name: "text" });
|
|
23468
|
+
const fontNode = state(opts.font ?? "16px sans-serif", { name: "font" });
|
|
23469
|
+
const lineHeightNode = state(opts.lineHeight ?? 20, { name: "line-height" });
|
|
23470
|
+
const containerNode = state(
|
|
23471
|
+
opts.container ?? { width: 800, height: 600, paddingX: 0, paddingY: 0 },
|
|
23472
|
+
{ name: "container" }
|
|
23473
|
+
);
|
|
23474
|
+
const columnsNode = state(opts.columns ?? { count: 1, gap: 0 }, {
|
|
23475
|
+
name: "columns"
|
|
23476
|
+
});
|
|
23477
|
+
const obstaclesNode = state(opts.obstacles ?? [], { name: "obstacles" });
|
|
23478
|
+
const segmentsNode = node(
|
|
23479
|
+
[textNode, fontNode],
|
|
23480
|
+
(data, actions, ctx) => {
|
|
23481
|
+
const b0 = data[0];
|
|
23482
|
+
const textVal = b0 != null && b0.length > 0 ? b0.at(-1) : ctx.prevData[0];
|
|
23483
|
+
const b1 = data[1];
|
|
23484
|
+
const fontVal = b1 != null && b1.length > 0 ? b1.at(-1) : ctx.prevData[1];
|
|
23485
|
+
const result = analyzeAndMeasure(textVal, fontVal, adapter, measureCache);
|
|
23486
|
+
actions.emit(result);
|
|
23487
|
+
return () => {
|
|
23488
|
+
measureCache.clear();
|
|
23489
|
+
adapter.clearCache?.();
|
|
23490
|
+
};
|
|
23491
|
+
},
|
|
23492
|
+
{ name: "segments", describeKind: "derived" }
|
|
23493
|
+
);
|
|
23494
|
+
const flowLinesNode = derived(
|
|
23495
|
+
[segmentsNode, containerNode, columnsNode, obstaclesNode, lineHeightNode],
|
|
23496
|
+
([segs, cont, cols, obs, lh]) => {
|
|
23497
|
+
const segments = segs;
|
|
23498
|
+
const t0 = monotonicNs();
|
|
23499
|
+
const { lines: result, cursor } = computeFlowLines(
|
|
23500
|
+
segments,
|
|
23501
|
+
cont,
|
|
23502
|
+
cols,
|
|
23503
|
+
obs,
|
|
23504
|
+
lh,
|
|
23505
|
+
minSlotWidth
|
|
23506
|
+
);
|
|
23507
|
+
const elapsed = monotonicNs() - t0;
|
|
23508
|
+
const overflow = Math.max(0, segments.length - cursor.segmentIndex);
|
|
23509
|
+
const meta = flowLinesNode.meta;
|
|
23510
|
+
if (meta) {
|
|
23511
|
+
emitToMeta(meta["line-count"], result.length);
|
|
23512
|
+
emitToMeta(meta["layout-time-ns"], elapsed);
|
|
23513
|
+
emitToMeta(meta["overflow-segments"], overflow);
|
|
23514
|
+
}
|
|
23515
|
+
return result;
|
|
23516
|
+
},
|
|
23517
|
+
{
|
|
23518
|
+
name: "flow-lines",
|
|
23519
|
+
meta: {
|
|
23520
|
+
"line-count": 0,
|
|
23521
|
+
"layout-time-ns": 0,
|
|
23522
|
+
"overflow-segments": 0
|
|
23523
|
+
},
|
|
23524
|
+
equals: (a, b) => {
|
|
23525
|
+
const la = a;
|
|
23526
|
+
const lb = b;
|
|
23527
|
+
if (la.length !== lb.length) return false;
|
|
23528
|
+
for (let i = 0; i < la.length; i++) {
|
|
23529
|
+
const pa = la[i];
|
|
23530
|
+
const pb = lb[i];
|
|
23531
|
+
if (pa.x !== pb.x || pa.y !== pb.y || pa.width !== pb.width || pa.slotWidth !== pb.slotWidth || pa.text !== pb.text || pa.columnIndex !== pb.columnIndex || pa.flushToRight !== pb.flushToRight)
|
|
23532
|
+
return false;
|
|
23533
|
+
}
|
|
23534
|
+
return true;
|
|
23535
|
+
}
|
|
23536
|
+
}
|
|
23537
|
+
);
|
|
23538
|
+
g.add("text", textNode);
|
|
23539
|
+
g.add("font", fontNode);
|
|
23540
|
+
g.add("line-height", lineHeightNode);
|
|
23541
|
+
g.add("container", containerNode);
|
|
23542
|
+
g.add("columns", columnsNode);
|
|
23543
|
+
g.add("obstacles", obstaclesNode);
|
|
23544
|
+
g.add("segments", segmentsNode);
|
|
23545
|
+
g.add("flow-lines", flowLinesNode);
|
|
23546
|
+
return {
|
|
23547
|
+
graph: g,
|
|
23548
|
+
setText: (t) => g.set("text", t),
|
|
23549
|
+
setFont: (f) => g.set("font", f),
|
|
23550
|
+
setLineHeight: (lh) => g.set("line-height", lh),
|
|
23551
|
+
setContainer: (c) => g.set("container", c),
|
|
23552
|
+
setColumns: (c) => g.set("columns", c),
|
|
23553
|
+
setObstacles: (o) => g.set("obstacles", o),
|
|
23554
|
+
segments: segmentsNode,
|
|
23555
|
+
flowLines: flowLinesNode
|
|
23556
|
+
};
|
|
23557
|
+
}
|
|
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
|
+
|
|
21818
23938
|
// src/index.ts
|
|
21819
23939
|
var version = "0.0.0";
|
|
21820
23940
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -21855,15 +23975,20 @@ var version = "0.0.0";
|
|
|
21855
23975
|
ResettableTimer,
|
|
21856
23976
|
SIZEOF_OVERHEAD,
|
|
21857
23977
|
SIZEOF_SYMBOL,
|
|
23978
|
+
SNAPSHOT_VERSION,
|
|
23979
|
+
SNAPSHOT_WIRE_VERSION,
|
|
21858
23980
|
START,
|
|
21859
23981
|
START_MSG,
|
|
23982
|
+
SurfaceError,
|
|
21860
23983
|
TEARDOWN,
|
|
21861
23984
|
TEARDOWN_MSG,
|
|
21862
23985
|
TEARDOWN_ONLY_BATCH,
|
|
21863
23986
|
TimeoutError,
|
|
21864
23987
|
accessHintForGuard,
|
|
23988
|
+
accountability,
|
|
21865
23989
|
advanceVersion,
|
|
21866
23990
|
ai,
|
|
23991
|
+
asSurfaceError,
|
|
21867
23992
|
audit,
|
|
21868
23993
|
autoTrackNode,
|
|
21869
23994
|
batch,
|
|
@@ -21887,6 +24012,7 @@ var version = "0.0.0";
|
|
|
21887
24012
|
cqrs,
|
|
21888
24013
|
createDagCborCodec,
|
|
21889
24014
|
createDagCborZstdCodec,
|
|
24015
|
+
createGraph,
|
|
21890
24016
|
createTransport,
|
|
21891
24017
|
createVersioning,
|
|
21892
24018
|
createWatermarkController,
|
|
@@ -21898,11 +24024,13 @@ var version = "0.0.0";
|
|
|
21898
24024
|
defaultConfig,
|
|
21899
24025
|
defaultHash,
|
|
21900
24026
|
delay,
|
|
24027
|
+
deleteSnapshot,
|
|
21901
24028
|
demoShell,
|
|
21902
24029
|
derived,
|
|
21903
24030
|
deserializeError,
|
|
21904
24031
|
dictStorage,
|
|
21905
24032
|
diffForWAL,
|
|
24033
|
+
diffSnapshots,
|
|
21906
24034
|
distill,
|
|
21907
24035
|
distinctUntilChanged,
|
|
21908
24036
|
domainTemplates,
|
|
@@ -21914,6 +24042,7 @@ var version = "0.0.0";
|
|
|
21914
24042
|
encodeEnvelope,
|
|
21915
24043
|
escapeRegexChar,
|
|
21916
24044
|
exhaustMap,
|
|
24045
|
+
explainPath,
|
|
21917
24046
|
exponential,
|
|
21918
24047
|
externalBundle,
|
|
21919
24048
|
externalProducer,
|
|
@@ -21954,6 +24083,7 @@ var version = "0.0.0";
|
|
|
21954
24083
|
fromPromise,
|
|
21955
24084
|
fromPulsar,
|
|
21956
24085
|
fromRabbitMQ,
|
|
24086
|
+
fromRaf,
|
|
21957
24087
|
fromRedisStream,
|
|
21958
24088
|
fromSSE,
|
|
21959
24089
|
fromSqlite,
|
|
@@ -21968,6 +24098,7 @@ var version = "0.0.0";
|
|
|
21968
24098
|
graph,
|
|
21969
24099
|
graphProfile,
|
|
21970
24100
|
graphspec,
|
|
24101
|
+
guarded,
|
|
21971
24102
|
harness,
|
|
21972
24103
|
indexedDbStorage,
|
|
21973
24104
|
interval,
|
|
@@ -21977,7 +24108,9 @@ var version = "0.0.0";
|
|
|
21977
24108
|
keepalive,
|
|
21978
24109
|
last,
|
|
21979
24110
|
layout,
|
|
24111
|
+
lens,
|
|
21980
24112
|
linear,
|
|
24113
|
+
listSnapshots,
|
|
21981
24114
|
lru,
|
|
21982
24115
|
map,
|
|
21983
24116
|
matchesAnyPattern,
|
|
@@ -22027,11 +24160,15 @@ var version = "0.0.0";
|
|
|
22027
24160
|
replay,
|
|
22028
24161
|
replayWAL,
|
|
22029
24162
|
rescue,
|
|
24163
|
+
resilientPipeline,
|
|
22030
24164
|
resolveBackoffPreset,
|
|
22031
24165
|
resolveDescribeFields,
|
|
24166
|
+
restoreSnapshot,
|
|
22032
24167
|
retry,
|
|
22033
24168
|
retrySource,
|
|
24169
|
+
runReduction,
|
|
22034
24170
|
sample,
|
|
24171
|
+
saveSnapshot,
|
|
22035
24172
|
scan,
|
|
22036
24173
|
serializeError,
|
|
22037
24174
|
share,
|
|
@@ -22043,6 +24180,7 @@ var version = "0.0.0";
|
|
|
22043
24180
|
solid,
|
|
22044
24181
|
sqliteStorage,
|
|
22045
24182
|
state,
|
|
24183
|
+
surface,
|
|
22046
24184
|
svelte,
|
|
22047
24185
|
switchMap,
|
|
22048
24186
|
take,
|
|
@@ -22080,6 +24218,7 @@ var version = "0.0.0";
|
|
|
22080
24218
|
version,
|
|
22081
24219
|
vue,
|
|
22082
24220
|
wallClockNs,
|
|
24221
|
+
watchTopologyTree,
|
|
22083
24222
|
window,
|
|
22084
24223
|
windowCount,
|
|
22085
24224
|
windowTime,
|