@graphrefly/graphrefly 0.18.0 → 0.20.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/dist/{chunk-J7S54G7I.js → chunk-2L5J6RPM.js} +7 -2
- package/dist/chunk-2L5J6RPM.js.map +1 -0
- package/dist/{chunk-LB3RYLSC.js → chunk-3N2Y6PCR.js} +197 -42
- package/dist/chunk-3N2Y6PCR.js.map +1 -0
- package/dist/{chunk-KJGUP35I.js → chunk-5PSVTDNZ.js} +22 -9
- package/dist/chunk-5PSVTDNZ.js.map +1 -0
- package/dist/{chunk-76YPZQTW.js → chunk-BJAOEU4D.js} +34 -29
- package/dist/chunk-BJAOEU4D.js.map +1 -0
- package/dist/{chunk-UVWEKTYC.js → chunk-IAPLC4NR.js} +3 -3
- package/dist/{chunk-F6ORUNO7.js → chunk-OOA2UTXF.js} +58 -2
- package/dist/chunk-OOA2UTXF.js.map +1 -0
- package/dist/{chunk-TNKODJ6E.js → chunk-PGEU5MEH.js} +7 -3
- package/dist/{chunk-TNKODJ6E.js.map → chunk-PGEU5MEH.js.map} +1 -1
- package/dist/chunk-R2LPZIY2.js +111 -0
- package/dist/chunk-R2LPZIY2.js.map +1 -0
- package/dist/{chunk-BV3TPSBK.js → chunk-XYL3GLB3.js} +742 -757
- package/dist/chunk-XYL3GLB3.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +967 -811
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +4 -4
- package/dist/compat/nestjs/index.d.ts +4 -4
- package/dist/compat/nestjs/index.js +7 -7
- package/dist/core/index.cjs +653 -666
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +7 -3
- package/dist/extra/index.cjs +728 -688
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +9 -5
- package/dist/graph/index.cjs +836 -808
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +3 -3
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.js +8 -8
- package/dist/{graph-gISB9n3n.d.ts → graph-KsTe57nI.d.cts} +82 -8
- package/dist/{graph-BYFlyNpX.d.cts → graph-mILUUqW8.d.ts} +82 -8
- package/dist/{index-CgKPpiu8.d.ts → index-8a605sg9.d.ts} +2 -2
- package/dist/{index-DKaB2x0T.d.ts → index-B2SvPEbc.d.ts} +6 -65
- package/dist/{index-EmzYk-TG.d.cts → index-BHfg_Ez3.d.ts} +123 -77
- package/dist/{index-B80mMeuf.d.ts → index-Bc_diYYJ.d.cts} +123 -77
- package/dist/{index-B43mC7uY.d.cts → index-BjtlNirP.d.cts} +3 -3
- package/dist/{index-CEDaJaYE.d.ts → index-CgSiUouz.d.ts} +3 -3
- package/dist/{index-7WnwgjMu.d.ts → index-DuN3bhtm.d.ts} +82 -32
- package/dist/{index-D_tUMcpz.d.cts → index-SFzE_KTa.d.cts} +82 -32
- package/dist/{index-Ci_vPaVm.d.cts → index-UudxGnzc.d.cts} +6 -65
- package/dist/{index-BqOWSFhr.d.cts → index-VHA43cGP.d.cts} +2 -2
- package/dist/index.cjs +5936 -5522
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +644 -379
- package/dist/index.d.ts +644 -379
- package/dist/index.js +4388 -4058
- package/dist/index.js.map +1 -1
- package/dist/{meta-npl5b97j.d.cts → meta-BnG7XAaE.d.cts} +394 -236
- package/dist/{meta-npl5b97j.d.ts → meta-BnG7XAaE.d.ts} +394 -236
- package/dist/{observable-DFBCBELR.d.cts → observable-C8Kx_O6k.d.cts} +1 -1
- package/dist/{observable-oAGygKvc.d.ts → observable-DcBwQY7t.d.ts} +1 -1
- package/dist/patterns/reactive-layout/index.cjs +865 -718
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +3 -3
- package/dist/patterns/reactive-layout/index.d.ts +3 -3
- package/dist/patterns/reactive-layout/index.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-76YPZQTW.js.map +0 -1
- package/dist/chunk-BV3TPSBK.js.map +0 -1
- package/dist/chunk-F6ORUNO7.js.map +0 -1
- package/dist/chunk-FCLROC4Q.js +0 -231
- package/dist/chunk-FCLROC4Q.js.map +0 -1
- package/dist/chunk-J7S54G7I.js.map +0 -1
- package/dist/chunk-KJGUP35I.js.map +0 -1
- package/dist/chunk-LB3RYLSC.js.map +0 -1
- /package/dist/{chunk-UVWEKTYC.js.map → chunk-IAPLC4NR.js.map} +0 -0
|
@@ -105,6 +105,7 @@ module.exports = __toCommonJS(nestjs_exports);
|
|
|
105
105
|
var import_rxjs = require("rxjs");
|
|
106
106
|
|
|
107
107
|
// src/core/messages.ts
|
|
108
|
+
var START = /* @__PURE__ */ Symbol.for("graphrefly/START");
|
|
108
109
|
var DATA = /* @__PURE__ */ Symbol.for("graphrefly/DATA");
|
|
109
110
|
var DIRTY = /* @__PURE__ */ Symbol.for("graphrefly/DIRTY");
|
|
110
111
|
var RESOLVED = /* @__PURE__ */ Symbol.for("graphrefly/RESOLVED");
|
|
@@ -114,13 +115,27 @@ var RESUME = /* @__PURE__ */ Symbol.for("graphrefly/RESUME");
|
|
|
114
115
|
var TEARDOWN = /* @__PURE__ */ Symbol.for("graphrefly/TEARDOWN");
|
|
115
116
|
var COMPLETE = /* @__PURE__ */ Symbol.for("graphrefly/COMPLETE");
|
|
116
117
|
var ERROR = /* @__PURE__ */ Symbol.for("graphrefly/ERROR");
|
|
118
|
+
var knownMessageTypes = [
|
|
119
|
+
START,
|
|
120
|
+
DATA,
|
|
121
|
+
DIRTY,
|
|
122
|
+
RESOLVED,
|
|
123
|
+
INVALIDATE,
|
|
124
|
+
PAUSE,
|
|
125
|
+
RESUME,
|
|
126
|
+
TEARDOWN,
|
|
127
|
+
COMPLETE,
|
|
128
|
+
ERROR
|
|
129
|
+
];
|
|
130
|
+
var knownMessageSet = new Set(knownMessageTypes);
|
|
117
131
|
function messageTier(t) {
|
|
118
|
-
if (t ===
|
|
119
|
-
if (t ===
|
|
120
|
-
if (t ===
|
|
121
|
-
if (t ===
|
|
122
|
-
if (t ===
|
|
123
|
-
return
|
|
132
|
+
if (t === START) return 0;
|
|
133
|
+
if (t === DIRTY || t === INVALIDATE) return 1;
|
|
134
|
+
if (t === PAUSE || t === RESUME) return 2;
|
|
135
|
+
if (t === DATA || t === RESOLVED) return 3;
|
|
136
|
+
if (t === COMPLETE || t === ERROR) return 4;
|
|
137
|
+
if (t === TEARDOWN) return 5;
|
|
138
|
+
return 1;
|
|
124
139
|
}
|
|
125
140
|
function isPhase2Message(msg) {
|
|
126
141
|
const t = msg[0];
|
|
@@ -306,6 +321,82 @@ function normalizeActor(actor) {
|
|
|
306
321
|
};
|
|
307
322
|
}
|
|
308
323
|
|
|
324
|
+
// src/core/guard.ts
|
|
325
|
+
var GuardDenied = class extends Error {
|
|
326
|
+
actor;
|
|
327
|
+
action;
|
|
328
|
+
nodeName;
|
|
329
|
+
/**
|
|
330
|
+
* @param details - Actor, action, and optional node name for the denial.
|
|
331
|
+
* @param message - Optional override for the default error message.
|
|
332
|
+
*/
|
|
333
|
+
constructor(details, message) {
|
|
334
|
+
super(
|
|
335
|
+
message ?? `GuardDenied: action "${String(details.action)}" denied for actor type "${String(details.actor.type)}"`
|
|
336
|
+
);
|
|
337
|
+
this.name = "GuardDenied";
|
|
338
|
+
this.actor = details.actor;
|
|
339
|
+
this.action = details.action;
|
|
340
|
+
this.nodeName = details.nodeName;
|
|
341
|
+
}
|
|
342
|
+
/** Qualified registry path when known (roadmap diagnostics: same as {@link nodeName}). */
|
|
343
|
+
get node() {
|
|
344
|
+
return this.nodeName;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
function normalizeActions(action) {
|
|
348
|
+
if (Array.isArray(action)) {
|
|
349
|
+
return [...action];
|
|
350
|
+
}
|
|
351
|
+
return [action];
|
|
352
|
+
}
|
|
353
|
+
function matchesActions(set, action) {
|
|
354
|
+
return set.has(action) || set.has("*");
|
|
355
|
+
}
|
|
356
|
+
function policy(build) {
|
|
357
|
+
const rules = [];
|
|
358
|
+
const allow = (action, opts) => {
|
|
359
|
+
rules.push({
|
|
360
|
+
kind: "allow",
|
|
361
|
+
actions: new Set(normalizeActions(action)),
|
|
362
|
+
where: opts?.where ?? (() => true)
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
const deny = (action, opts) => {
|
|
366
|
+
rules.push({
|
|
367
|
+
kind: "deny",
|
|
368
|
+
actions: new Set(normalizeActions(action)),
|
|
369
|
+
where: opts?.where ?? (() => true)
|
|
370
|
+
});
|
|
371
|
+
};
|
|
372
|
+
build(allow, deny);
|
|
373
|
+
return (actor, action) => {
|
|
374
|
+
let denied = false;
|
|
375
|
+
let allowed = false;
|
|
376
|
+
for (const r of rules) {
|
|
377
|
+
if (!matchesActions(r.actions, action)) continue;
|
|
378
|
+
if (!r.where(actor)) continue;
|
|
379
|
+
if (r.kind === "deny") {
|
|
380
|
+
denied = true;
|
|
381
|
+
} else {
|
|
382
|
+
allowed = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (denied) return false;
|
|
386
|
+
return allowed;
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
var STANDARD_WRITE_TYPES = ["human", "llm", "wallet", "system"];
|
|
390
|
+
function accessHintForGuard(guard) {
|
|
391
|
+
const allowed = STANDARD_WRITE_TYPES.filter((t) => guard({ type: t, id: "" }, "write"));
|
|
392
|
+
if (allowed.length === 0) return "restricted";
|
|
393
|
+
if (allowed.includes("human") && allowed.includes("llm") && allowed.every((t) => t === "human" || t === "llm" || t === "system")) {
|
|
394
|
+
return "both";
|
|
395
|
+
}
|
|
396
|
+
if (allowed.length === 1) return allowed[0];
|
|
397
|
+
return allowed.join("+");
|
|
398
|
+
}
|
|
399
|
+
|
|
309
400
|
// src/core/batch.ts
|
|
310
401
|
var MAX_DRAIN_ITERATIONS = 1e3;
|
|
311
402
|
var batchDepth = 0;
|
|
@@ -462,14 +553,14 @@ function _downSequential(sink, messages, phase = 2) {
|
|
|
462
553
|
const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
|
|
463
554
|
for (const msg of messages) {
|
|
464
555
|
const tier = messageTier(msg[0]);
|
|
465
|
-
if (tier ===
|
|
556
|
+
if (tier === 3) {
|
|
466
557
|
if (isBatching()) {
|
|
467
558
|
const m = msg;
|
|
468
559
|
dataQueue.push(() => sink([m]));
|
|
469
560
|
} else {
|
|
470
561
|
sink([msg]);
|
|
471
562
|
}
|
|
472
|
-
} else if (tier >=
|
|
563
|
+
} else if (tier >= 4) {
|
|
473
564
|
if (isBatching()) {
|
|
474
565
|
const m = msg;
|
|
475
566
|
pendingPhase3.push(() => sink([m]));
|
|
@@ -482,82 +573,6 @@ function _downSequential(sink, messages, phase = 2) {
|
|
|
482
573
|
}
|
|
483
574
|
}
|
|
484
575
|
|
|
485
|
-
// src/core/guard.ts
|
|
486
|
-
var GuardDenied = class extends Error {
|
|
487
|
-
actor;
|
|
488
|
-
action;
|
|
489
|
-
nodeName;
|
|
490
|
-
/**
|
|
491
|
-
* @param details - Actor, action, and optional node name for the denial.
|
|
492
|
-
* @param message - Optional override for the default error message.
|
|
493
|
-
*/
|
|
494
|
-
constructor(details, message) {
|
|
495
|
-
super(
|
|
496
|
-
message ?? `GuardDenied: action "${String(details.action)}" denied for actor type "${String(details.actor.type)}"`
|
|
497
|
-
);
|
|
498
|
-
this.name = "GuardDenied";
|
|
499
|
-
this.actor = details.actor;
|
|
500
|
-
this.action = details.action;
|
|
501
|
-
this.nodeName = details.nodeName;
|
|
502
|
-
}
|
|
503
|
-
/** Qualified registry path when known (roadmap diagnostics: same as {@link nodeName}). */
|
|
504
|
-
get node() {
|
|
505
|
-
return this.nodeName;
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
function normalizeActions(action) {
|
|
509
|
-
if (Array.isArray(action)) {
|
|
510
|
-
return [...action];
|
|
511
|
-
}
|
|
512
|
-
return [action];
|
|
513
|
-
}
|
|
514
|
-
function matchesActions(set, action) {
|
|
515
|
-
return set.has(action) || set.has("*");
|
|
516
|
-
}
|
|
517
|
-
function policy(build) {
|
|
518
|
-
const rules = [];
|
|
519
|
-
const allow = (action, opts) => {
|
|
520
|
-
rules.push({
|
|
521
|
-
kind: "allow",
|
|
522
|
-
actions: new Set(normalizeActions(action)),
|
|
523
|
-
where: opts?.where ?? (() => true)
|
|
524
|
-
});
|
|
525
|
-
};
|
|
526
|
-
const deny = (action, opts) => {
|
|
527
|
-
rules.push({
|
|
528
|
-
kind: "deny",
|
|
529
|
-
actions: new Set(normalizeActions(action)),
|
|
530
|
-
where: opts?.where ?? (() => true)
|
|
531
|
-
});
|
|
532
|
-
};
|
|
533
|
-
build(allow, deny);
|
|
534
|
-
return (actor, action) => {
|
|
535
|
-
let denied = false;
|
|
536
|
-
let allowed = false;
|
|
537
|
-
for (const r of rules) {
|
|
538
|
-
if (!matchesActions(r.actions, action)) continue;
|
|
539
|
-
if (!r.where(actor)) continue;
|
|
540
|
-
if (r.kind === "deny") {
|
|
541
|
-
denied = true;
|
|
542
|
-
} else {
|
|
543
|
-
allowed = true;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if (denied) return false;
|
|
547
|
-
return allowed;
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
var STANDARD_WRITE_TYPES = ["human", "llm", "wallet", "system"];
|
|
551
|
-
function accessHintForGuard(guard) {
|
|
552
|
-
const allowed = STANDARD_WRITE_TYPES.filter((t) => guard({ type: t, id: "" }, "write"));
|
|
553
|
-
if (allowed.length === 0) return "restricted";
|
|
554
|
-
if (allowed.includes("human") && allowed.includes("llm") && allowed.every((t) => t === "human" || t === "llm" || t === "system")) {
|
|
555
|
-
return "both";
|
|
556
|
-
}
|
|
557
|
-
if (allowed.length === 1) return allowed[0];
|
|
558
|
-
return allowed.join("+");
|
|
559
|
-
}
|
|
560
|
-
|
|
561
576
|
// src/core/versioning.ts
|
|
562
577
|
var import_node_crypto = require("crypto");
|
|
563
578
|
function canonicalizeForHash(value) {
|
|
@@ -610,10 +625,24 @@ function advanceVersion(info, newValue, hashFn) {
|
|
|
610
625
|
}
|
|
611
626
|
}
|
|
612
627
|
|
|
613
|
-
// src/core/node.ts
|
|
628
|
+
// src/core/node-base.ts
|
|
614
629
|
var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
|
|
615
630
|
var CLEANUP_RESULT = /* @__PURE__ */ Symbol.for("graphrefly/CLEANUP_RESULT");
|
|
616
|
-
|
|
631
|
+
var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
|
|
632
|
+
var isCleanupFn = (value) => typeof value === "function";
|
|
633
|
+
function statusAfterMessage(status, msg) {
|
|
634
|
+
const t = msg[0];
|
|
635
|
+
if (t === DIRTY) return "dirty";
|
|
636
|
+
if (t === DATA) return "settled";
|
|
637
|
+
if (t === RESOLVED) return "resolved";
|
|
638
|
+
if (t === COMPLETE) return "completed";
|
|
639
|
+
if (t === ERROR) return "errored";
|
|
640
|
+
if (t === INVALIDATE) return "dirty";
|
|
641
|
+
if (t === TEARDOWN) return "disconnected";
|
|
642
|
+
return status;
|
|
643
|
+
}
|
|
644
|
+
function createIntBitSet(size) {
|
|
645
|
+
const fullMask = size >= 32 ? -1 : ~(-1 << size);
|
|
617
646
|
let bits = 0;
|
|
618
647
|
return {
|
|
619
648
|
set(i) {
|
|
@@ -626,7 +655,8 @@ function createIntBitSet() {
|
|
|
626
655
|
return (bits & 1 << i) !== 0;
|
|
627
656
|
},
|
|
628
657
|
covers(other) {
|
|
629
|
-
|
|
658
|
+
const otherBits = other._bits();
|
|
659
|
+
return (bits & otherBits) === otherBits;
|
|
630
660
|
},
|
|
631
661
|
any() {
|
|
632
662
|
return bits !== 0;
|
|
@@ -634,6 +664,9 @@ function createIntBitSet() {
|
|
|
634
664
|
reset() {
|
|
635
665
|
bits = 0;
|
|
636
666
|
},
|
|
667
|
+
setAll() {
|
|
668
|
+
bits = fullMask;
|
|
669
|
+
},
|
|
637
670
|
_bits() {
|
|
638
671
|
return bits;
|
|
639
672
|
}
|
|
@@ -641,6 +674,8 @@ function createIntBitSet() {
|
|
|
641
674
|
}
|
|
642
675
|
function createArrayBitSet(size) {
|
|
643
676
|
const words = new Uint32Array(Math.ceil(size / 32));
|
|
677
|
+
const lastBits = size % 32;
|
|
678
|
+
const lastWordMask = lastBits === 0 ? 4294967295 : (1 << lastBits) - 1 >>> 0;
|
|
644
679
|
return {
|
|
645
680
|
set(i) {
|
|
646
681
|
words[i >>> 5] |= 1 << (i & 31);
|
|
@@ -667,130 +702,103 @@ function createArrayBitSet(size) {
|
|
|
667
702
|
reset() {
|
|
668
703
|
words.fill(0);
|
|
669
704
|
},
|
|
705
|
+
setAll() {
|
|
706
|
+
for (let w = 0; w < words.length - 1; w++) words[w] = 4294967295;
|
|
707
|
+
if (words.length > 0) words[words.length - 1] = lastWordMask;
|
|
708
|
+
},
|
|
670
709
|
_words: words
|
|
671
710
|
};
|
|
672
711
|
}
|
|
673
712
|
function createBitSet(size) {
|
|
674
|
-
return size <= 31 ? createIntBitSet() : createArrayBitSet(size);
|
|
713
|
+
return size <= 31 ? createIntBitSet(size) : createArrayBitSet(size);
|
|
675
714
|
}
|
|
676
|
-
var
|
|
677
|
-
|
|
678
|
-
var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
|
|
679
|
-
var isCleanupFn = (value) => typeof value === "function";
|
|
680
|
-
var statusAfterMessage = (status, msg) => {
|
|
681
|
-
const t = msg[0];
|
|
682
|
-
if (t === DIRTY) return "dirty";
|
|
683
|
-
if (t === DATA) return "settled";
|
|
684
|
-
if (t === RESOLVED) return "resolved";
|
|
685
|
-
if (t === COMPLETE) return "completed";
|
|
686
|
-
if (t === ERROR) return "errored";
|
|
687
|
-
if (t === INVALIDATE) return "dirty";
|
|
688
|
-
if (t === TEARDOWN) return "disconnected";
|
|
689
|
-
return status;
|
|
690
|
-
};
|
|
691
|
-
var NodeImpl = class {
|
|
692
|
-
// --- Configuration (set once, never reassigned) ---
|
|
715
|
+
var NodeBase = class {
|
|
716
|
+
// --- Identity (set once) ---
|
|
693
717
|
_optsName;
|
|
694
718
|
_registryName;
|
|
695
|
-
/** @internal
|
|
719
|
+
/** @internal Read by `describeNode` before inference. */
|
|
696
720
|
_describeKind;
|
|
697
721
|
meta;
|
|
698
|
-
|
|
699
|
-
_fn;
|
|
700
|
-
_opts;
|
|
722
|
+
// --- Options ---
|
|
701
723
|
_equals;
|
|
724
|
+
_resubscribable;
|
|
725
|
+
_resetOnTeardown;
|
|
726
|
+
_onResubscribe;
|
|
702
727
|
_onMessage;
|
|
703
|
-
/** @internal
|
|
728
|
+
/** @internal Read by `describeNode` for `accessHintForGuard`. */
|
|
704
729
|
_guard;
|
|
730
|
+
/** @internal Subclasses update this through {@link _recordMutation}. */
|
|
705
731
|
_lastMutation;
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
// ---
|
|
732
|
+
// --- Versioning ---
|
|
733
|
+
_hashFn;
|
|
734
|
+
_versioning;
|
|
735
|
+
// --- Lifecycle state ---
|
|
736
|
+
/** @internal Read by `describeNode` and `graph.ts`. */
|
|
710
737
|
_cached;
|
|
738
|
+
/** @internal Read externally via `get status()`. */
|
|
711
739
|
_status;
|
|
712
740
|
_terminal = false;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
_manualEmitUsed = false;
|
|
741
|
+
_active = false;
|
|
742
|
+
// --- Sink storage ---
|
|
743
|
+
/** @internal Read by `graph/profile.ts` for subscriber counts. */
|
|
717
744
|
_sinkCount = 0;
|
|
718
745
|
_singleDepSinkCount = 0;
|
|
719
|
-
// --- Object/collection state ---
|
|
720
|
-
_depDirtyMask;
|
|
721
|
-
_depSettledMask;
|
|
722
|
-
_depCompleteMask;
|
|
723
|
-
_allDepsCompleteMask;
|
|
724
|
-
_lastDepValues;
|
|
725
|
-
_cleanup;
|
|
726
|
-
_sinks = null;
|
|
727
746
|
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
728
|
-
|
|
747
|
+
_sinks = null;
|
|
748
|
+
// --- Actions + bound helpers ---
|
|
729
749
|
_actions;
|
|
730
750
|
_boundDownToSinks;
|
|
751
|
+
// --- Inspector hook (Graph observability) ---
|
|
731
752
|
_inspectorHook;
|
|
732
|
-
|
|
733
|
-
_hashFn;
|
|
734
|
-
constructor(deps, fn, opts) {
|
|
735
|
-
this._deps = deps;
|
|
736
|
-
this._fn = fn;
|
|
737
|
-
this._opts = opts;
|
|
753
|
+
constructor(opts) {
|
|
738
754
|
this._optsName = opts.name;
|
|
739
755
|
this._describeKind = opts.describeKind;
|
|
740
756
|
this._equals = opts.equals ?? Object.is;
|
|
757
|
+
this._resubscribable = opts.resubscribable ?? false;
|
|
758
|
+
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
759
|
+
this._onResubscribe = opts.onResubscribe;
|
|
741
760
|
this._onMessage = opts.onMessage;
|
|
742
761
|
this._guard = opts.guard;
|
|
743
|
-
this._hasDeps = deps.length > 0;
|
|
744
|
-
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
745
|
-
this._isSingleDep = deps.length === 1 && fn != null;
|
|
746
762
|
this._cached = "initial" in opts ? opts.initial : NO_VALUE;
|
|
747
|
-
this._status =
|
|
763
|
+
this._status = "disconnected";
|
|
748
764
|
this._hashFn = opts.versioningHash ?? defaultHash;
|
|
749
765
|
this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
|
|
750
766
|
id: opts.versioningId,
|
|
751
767
|
hash: this._hashFn
|
|
752
768
|
}) : void 0;
|
|
753
|
-
this._depDirtyMask = createBitSet(deps.length);
|
|
754
|
-
this._depSettledMask = createBitSet(deps.length);
|
|
755
|
-
this._depCompleteMask = createBitSet(deps.length);
|
|
756
|
-
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
757
|
-
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
758
769
|
const meta = {};
|
|
759
770
|
for (const [k, v] of Object.entries(opts.meta ?? {})) {
|
|
760
|
-
meta[k] =
|
|
761
|
-
initial: v,
|
|
762
|
-
name: `${opts.name ?? "node"}:meta:${k}`,
|
|
763
|
-
describeKind: "state",
|
|
764
|
-
...opts.guard != null ? { guard: opts.guard } : {}
|
|
765
|
-
});
|
|
771
|
+
meta[k] = this._createMetaNode(k, v, opts);
|
|
766
772
|
}
|
|
767
773
|
Object.freeze(meta);
|
|
768
774
|
this.meta = meta;
|
|
769
775
|
const self = this;
|
|
770
776
|
this._actions = {
|
|
771
777
|
down(messages) {
|
|
772
|
-
self.
|
|
778
|
+
self._onManualEmit();
|
|
773
779
|
self._downInternal(messages);
|
|
774
780
|
},
|
|
775
781
|
emit(value) {
|
|
776
|
-
self.
|
|
782
|
+
self._onManualEmit();
|
|
777
783
|
self._downAutoValue(value);
|
|
778
784
|
},
|
|
779
785
|
up(messages) {
|
|
780
786
|
self._upInternal(messages);
|
|
781
787
|
}
|
|
782
788
|
};
|
|
783
|
-
this.down = this.down.bind(this);
|
|
784
|
-
this.up = this.up.bind(this);
|
|
785
789
|
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
786
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* Subclass hook invoked by `actions.down` / `actions.emit`. Default no-op;
|
|
793
|
+
* {@link NodeImpl} overrides to set `_manualEmitUsed`.
|
|
794
|
+
*/
|
|
795
|
+
_onManualEmit() {
|
|
796
|
+
}
|
|
797
|
+
// --- Identity getters ---
|
|
787
798
|
get name() {
|
|
788
799
|
return this._registryName ?? this._optsName;
|
|
789
800
|
}
|
|
790
|
-
/**
|
|
791
|
-
* When a node is registered with {@link Graph.add} without an options `name`,
|
|
792
|
-
* the graph assigns the registry local name for introspection (parity with graphrefly-py).
|
|
793
|
-
*/
|
|
801
|
+
/** @internal Assigned by `Graph.add` when registered without an options `name`. */
|
|
794
802
|
_assignRegistryName(localName) {
|
|
795
803
|
if (this._optsName !== void 0 || this._registryName !== void 0) return;
|
|
796
804
|
this._registryName = localName;
|
|
@@ -808,7 +816,10 @@ var NodeImpl = class {
|
|
|
808
816
|
}
|
|
809
817
|
};
|
|
810
818
|
}
|
|
811
|
-
|
|
819
|
+
/** @internal Used by subclasses to surface inspector events. */
|
|
820
|
+
_emitInspectorHook(event) {
|
|
821
|
+
this._inspectorHook?.(event);
|
|
822
|
+
}
|
|
812
823
|
get status() {
|
|
813
824
|
return this._status;
|
|
814
825
|
}
|
|
@@ -818,15 +829,7 @@ var NodeImpl = class {
|
|
|
818
829
|
get v() {
|
|
819
830
|
return this._versioning;
|
|
820
831
|
}
|
|
821
|
-
/**
|
|
822
|
-
* Retroactively apply versioning to a node that was created without it.
|
|
823
|
-
* No-op if versioning is already enabled.
|
|
824
|
-
*
|
|
825
|
-
* Version starts at 0 regardless of prior DATA emissions — it tracks
|
|
826
|
-
* changes from the moment versioning is enabled, not historical ones.
|
|
827
|
-
*
|
|
828
|
-
* @internal — used by {@link Graph.setVersioning}.
|
|
829
|
-
*/
|
|
832
|
+
/** @internal Used by `Graph.setVersioning` to retroactively apply versioning. */
|
|
830
833
|
_applyVersioning(level, opts) {
|
|
831
834
|
if (this._versioning != null) return;
|
|
832
835
|
this._hashFn = opts?.hash ?? this._hashFn;
|
|
@@ -846,6 +849,7 @@ var NodeImpl = class {
|
|
|
846
849
|
if (this._guard == null) return true;
|
|
847
850
|
return this._guard(normalizeActor(actor), "observe");
|
|
848
851
|
}
|
|
852
|
+
// --- Public transport ---
|
|
849
853
|
get() {
|
|
850
854
|
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
851
855
|
}
|
|
@@ -858,43 +862,25 @@ var NodeImpl = class {
|
|
|
858
862
|
if (!this._guard(actor, action)) {
|
|
859
863
|
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
860
864
|
}
|
|
861
|
-
this.
|
|
865
|
+
this._recordMutation(actor);
|
|
862
866
|
}
|
|
863
867
|
this._downInternal(messages);
|
|
864
868
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
this.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
879
|
-
const t = sinkMessages[i][0];
|
|
880
|
-
if (t === DATA || t === RESOLVED) {
|
|
881
|
-
hasPhase2 = true;
|
|
882
|
-
break;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
if (hasPhase2) {
|
|
886
|
-
const filtered = [];
|
|
887
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
888
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
889
|
-
}
|
|
890
|
-
if (filtered.length > 0) {
|
|
891
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
892
|
-
}
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
869
|
+
/** @internal Record a successful guarded mutation (called by `down` and subclass `up`). */
|
|
870
|
+
_recordMutation(actor) {
|
|
871
|
+
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* At-most-once deactivation guard. Both TEARDOWN (eager) and
|
|
875
|
+
* unsubscribe-body (lazy) call this. The `_active` flag ensures
|
|
876
|
+
* `_doDeactivate` runs exactly once per activation cycle.
|
|
877
|
+
*/
|
|
878
|
+
_onDeactivate() {
|
|
879
|
+
if (!this._active) return;
|
|
880
|
+
this._active = false;
|
|
881
|
+
this._doDeactivate();
|
|
897
882
|
}
|
|
883
|
+
// --- Subscribe (uniform across node shapes) ---
|
|
898
884
|
subscribe(sink, hints) {
|
|
899
885
|
if (hints?.actor != null && this._guard != null) {
|
|
900
886
|
const actor = normalizeActor(hints.actor);
|
|
@@ -902,17 +888,21 @@ var NodeImpl = class {
|
|
|
902
888
|
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
903
889
|
}
|
|
904
890
|
}
|
|
905
|
-
if (this._terminal && this.
|
|
891
|
+
if (this._terminal && this._resubscribable) {
|
|
906
892
|
this._terminal = false;
|
|
907
893
|
this._cached = NO_VALUE;
|
|
908
|
-
this._status =
|
|
909
|
-
this.
|
|
894
|
+
this._status = "disconnected";
|
|
895
|
+
this._onResubscribe?.();
|
|
910
896
|
}
|
|
911
897
|
this._sinkCount += 1;
|
|
912
898
|
if (hints?.singleDep) {
|
|
913
899
|
this._singleDepSinkCount += 1;
|
|
914
900
|
this._singleDepSinks.add(sink);
|
|
915
901
|
}
|
|
902
|
+
if (!this._terminal) {
|
|
903
|
+
const startMessages = this._cached === NO_VALUE ? [[START]] : [[START], [DATA, this._cached]];
|
|
904
|
+
downWithBatch(sink, startMessages);
|
|
905
|
+
}
|
|
916
906
|
if (this._sinks == null) {
|
|
917
907
|
this._sinks = sink;
|
|
918
908
|
} else if (typeof this._sinks === "function") {
|
|
@@ -920,10 +910,12 @@ var NodeImpl = class {
|
|
|
920
910
|
} else {
|
|
921
911
|
this._sinks.add(sink);
|
|
922
912
|
}
|
|
923
|
-
if (this.
|
|
924
|
-
this.
|
|
925
|
-
|
|
926
|
-
|
|
913
|
+
if (this._sinkCount === 1 && !this._terminal) {
|
|
914
|
+
this._active = true;
|
|
915
|
+
this._onActivate();
|
|
916
|
+
}
|
|
917
|
+
if (!this._terminal && this._status === "disconnected" && this._cached === NO_VALUE) {
|
|
918
|
+
this._status = "pending";
|
|
927
919
|
}
|
|
928
920
|
let removed = false;
|
|
929
921
|
return () => {
|
|
@@ -947,39 +939,49 @@ var NodeImpl = class {
|
|
|
947
939
|
}
|
|
948
940
|
}
|
|
949
941
|
if (this._sinks == null) {
|
|
950
|
-
this.
|
|
951
|
-
this._stopProducer();
|
|
942
|
+
this._onDeactivate();
|
|
952
943
|
}
|
|
953
944
|
};
|
|
954
945
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
946
|
+
// --- Down pipeline ---
|
|
947
|
+
/**
|
|
948
|
+
* Core outgoing dispatch. Applies terminal filter + local lifecycle
|
|
949
|
+
* update, then hands messages to `downWithBatch` for tier-aware delivery.
|
|
950
|
+
*/
|
|
951
|
+
_downInternal(messages) {
|
|
952
|
+
if (messages.length === 0) return;
|
|
953
|
+
let sinkMessages = messages;
|
|
954
|
+
if (this._terminal && !this._resubscribable) {
|
|
955
|
+
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
956
|
+
if (pass.length === 0) return;
|
|
957
|
+
sinkMessages = pass;
|
|
963
958
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
959
|
+
this._handleLocalLifecycle(sinkMessages);
|
|
960
|
+
if (this._canSkipDirty()) {
|
|
961
|
+
let hasPhase2 = false;
|
|
962
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
963
|
+
const t = sinkMessages[i][0];
|
|
964
|
+
if (t === DATA || t === RESOLVED) {
|
|
965
|
+
hasPhase2 = true;
|
|
966
|
+
break;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (hasPhase2) {
|
|
970
|
+
const filtered = [];
|
|
971
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
972
|
+
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
973
|
+
}
|
|
974
|
+
if (filtered.length > 0) {
|
|
975
|
+
downWithBatch(this._boundDownToSinks, filtered);
|
|
976
|
+
}
|
|
977
|
+
return;
|
|
969
978
|
}
|
|
970
979
|
}
|
|
980
|
+
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
971
981
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
for (const dep of this._deps) {
|
|
975
|
-
dep.up?.(messages, { internal: true });
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
unsubscribe() {
|
|
979
|
-
if (!this._hasDeps) return;
|
|
980
|
-
this._disconnectUpstream();
|
|
982
|
+
_canSkipDirty() {
|
|
983
|
+
return this._sinkCount === 1 && this._singleDepSinkCount === 1;
|
|
981
984
|
}
|
|
982
|
-
// --- Private methods (prototype, _ prefix) ---
|
|
983
985
|
_downToSinks(messages) {
|
|
984
986
|
if (this._sinks == null) return;
|
|
985
987
|
if (typeof this._sinks === "function") {
|
|
@@ -991,6 +993,11 @@ var NodeImpl = class {
|
|
|
991
993
|
sink(messages);
|
|
992
994
|
}
|
|
993
995
|
}
|
|
996
|
+
/**
|
|
997
|
+
* Update `_cached`, `_status`, `_terminal` from message batch before
|
|
998
|
+
* delivery. Subclass hooks `_onInvalidate` / `_onTeardown` let
|
|
999
|
+
* {@link NodeImpl} clear `_lastDepValues` and invoke cleanup fns.
|
|
1000
|
+
*/
|
|
994
1001
|
_handleLocalLifecycle(messages) {
|
|
995
1002
|
for (const m of messages) {
|
|
996
1003
|
const t = m[0];
|
|
@@ -1004,28 +1011,22 @@ var NodeImpl = class {
|
|
|
1004
1011
|
}
|
|
1005
1012
|
}
|
|
1006
1013
|
if (t === INVALIDATE) {
|
|
1007
|
-
|
|
1008
|
-
this._cleanup = void 0;
|
|
1009
|
-
cleanupFn?.();
|
|
1014
|
+
this._onInvalidate();
|
|
1010
1015
|
this._cached = NO_VALUE;
|
|
1011
|
-
this._lastDepValues = void 0;
|
|
1012
1016
|
}
|
|
1013
1017
|
this._status = statusAfterMessage(this._status, m);
|
|
1014
1018
|
if (t === COMPLETE || t === ERROR) {
|
|
1015
1019
|
this._terminal = true;
|
|
1016
1020
|
}
|
|
1017
1021
|
if (t === TEARDOWN) {
|
|
1018
|
-
if (this.
|
|
1022
|
+
if (this._resetOnTeardown) {
|
|
1019
1023
|
this._cached = NO_VALUE;
|
|
1020
1024
|
}
|
|
1021
|
-
|
|
1022
|
-
this._cleanup = void 0;
|
|
1023
|
-
teardownCleanup?.();
|
|
1025
|
+
this._onTeardown();
|
|
1024
1026
|
try {
|
|
1025
1027
|
this._propagateToMeta(t);
|
|
1026
1028
|
} finally {
|
|
1027
|
-
this.
|
|
1028
|
-
this._stopProducer();
|
|
1029
|
+
this._onDeactivate();
|
|
1029
1030
|
}
|
|
1030
1031
|
}
|
|
1031
1032
|
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
@@ -1033,7 +1034,20 @@ var NodeImpl = class {
|
|
|
1033
1034
|
}
|
|
1034
1035
|
}
|
|
1035
1036
|
}
|
|
1036
|
-
/**
|
|
1037
|
+
/**
|
|
1038
|
+
* Subclass hook: invoked when INVALIDATE arrives (before `_cached` is
|
|
1039
|
+
* cleared). {@link NodeImpl} uses this to run the fn cleanup fn and
|
|
1040
|
+
* drop `_lastDepValues` so the next wave re-runs fn.
|
|
1041
|
+
*/
|
|
1042
|
+
_onInvalidate() {
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Subclass hook: invoked when TEARDOWN arrives, before `_onDeactivate`.
|
|
1046
|
+
* {@link NodeImpl} uses this to run any pending cleanup fn.
|
|
1047
|
+
*/
|
|
1048
|
+
_onTeardown() {
|
|
1049
|
+
}
|
|
1050
|
+
/** Forward a signal to all companion meta nodes (best-effort). */
|
|
1037
1051
|
_propagateToMeta(t) {
|
|
1038
1052
|
for (const metaNode of Object.values(this.meta)) {
|
|
1039
1053
|
try {
|
|
@@ -1042,9 +1056,10 @@ var NodeImpl = class {
|
|
|
1042
1056
|
}
|
|
1043
1057
|
}
|
|
1044
1058
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1059
|
+
/**
|
|
1060
|
+
* Frame a computed value into the right protocol messages and dispatch
|
|
1061
|
+
* via `_downInternal`. Used by `_runFn` and `actions.emit`.
|
|
1062
|
+
*/
|
|
1048
1063
|
_downAutoValue(value) {
|
|
1049
1064
|
const wasDirty = this._status === "dirty";
|
|
1050
1065
|
let unchanged;
|
|
@@ -1052,7 +1067,9 @@ var NodeImpl = class {
|
|
|
1052
1067
|
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
1053
1068
|
} catch (eqErr) {
|
|
1054
1069
|
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
1055
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
1070
|
+
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
1071
|
+
cause: eqErr
|
|
1072
|
+
});
|
|
1056
1073
|
this._downInternal([[ERROR, wrapped]]);
|
|
1057
1074
|
return;
|
|
1058
1075
|
}
|
|
@@ -1062,89 +1079,173 @@ var NodeImpl = class {
|
|
|
1062
1079
|
}
|
|
1063
1080
|
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
1064
1081
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
// src/core/node.ts
|
|
1085
|
+
var NodeImpl = class extends NodeBase {
|
|
1086
|
+
// --- Dep configuration (set once) ---
|
|
1087
|
+
_deps;
|
|
1088
|
+
_fn;
|
|
1089
|
+
_opts;
|
|
1090
|
+
_hasDeps;
|
|
1091
|
+
_isSingleDep;
|
|
1092
|
+
_autoComplete;
|
|
1093
|
+
// --- Wave tracking masks ---
|
|
1094
|
+
_depDirtyMask;
|
|
1095
|
+
_depSettledMask;
|
|
1096
|
+
_depCompleteMask;
|
|
1097
|
+
_allDepsCompleteMask;
|
|
1098
|
+
// --- Identity-skip optimization ---
|
|
1099
|
+
_lastDepValues;
|
|
1100
|
+
_cleanup;
|
|
1101
|
+
// --- Upstream bookkeeping ---
|
|
1102
|
+
_upstreamUnsubs = [];
|
|
1103
|
+
// --- Fn behavior flag ---
|
|
1104
|
+
/** @internal Read by `describeNode` to infer `"operator"` label. */
|
|
1105
|
+
_manualEmitUsed = false;
|
|
1106
|
+
constructor(deps, fn, opts) {
|
|
1107
|
+
super(opts);
|
|
1108
|
+
this._deps = deps;
|
|
1109
|
+
this._fn = fn;
|
|
1110
|
+
this._opts = opts;
|
|
1111
|
+
this._hasDeps = deps.length > 0;
|
|
1112
|
+
this._isSingleDep = deps.length === 1 && fn != null;
|
|
1113
|
+
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
1114
|
+
if (!this._hasDeps && fn == null && this._cached !== NO_VALUE) {
|
|
1115
|
+
this._status = "settled";
|
|
1116
|
+
}
|
|
1117
|
+
this._depDirtyMask = createBitSet(deps.length);
|
|
1118
|
+
this._depSettledMask = createBitSet(deps.length);
|
|
1119
|
+
this._depCompleteMask = createBitSet(deps.length);
|
|
1120
|
+
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
1121
|
+
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
1122
|
+
this.down = this.down.bind(this);
|
|
1123
|
+
this.up = this.up.bind(this);
|
|
1124
|
+
}
|
|
1125
|
+
// --- Meta node factory (called from base constructor) ---
|
|
1126
|
+
_createMetaNode(key, initialValue, opts) {
|
|
1127
|
+
return node({
|
|
1128
|
+
initial: initialValue,
|
|
1129
|
+
name: `${opts.name ?? "node"}:meta:${key}`,
|
|
1130
|
+
describeKind: "state",
|
|
1131
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
// --- Manual emit tracker (set by actions.down / actions.emit) ---
|
|
1135
|
+
_onManualEmit() {
|
|
1136
|
+
this._manualEmitUsed = true;
|
|
1137
|
+
}
|
|
1138
|
+
// --- Up / unsubscribe ---
|
|
1139
|
+
up(messages, options) {
|
|
1140
|
+
if (!this._hasDeps) return;
|
|
1141
|
+
if (!options?.internal && this._guard != null) {
|
|
1142
|
+
const actor = normalizeActor(options?.actor);
|
|
1143
|
+
if (!this._guard(actor, "write")) {
|
|
1144
|
+
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
1103
1145
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1146
|
+
this._recordMutation(actor);
|
|
1147
|
+
}
|
|
1148
|
+
for (const dep of this._deps) {
|
|
1149
|
+
if (options === void 0) {
|
|
1150
|
+
dep.up?.(messages);
|
|
1151
|
+
} else {
|
|
1152
|
+
dep.up?.(messages, options);
|
|
1107
1153
|
}
|
|
1108
|
-
if (this._manualEmitUsed) return;
|
|
1109
|
-
if (out === void 0) return;
|
|
1110
|
-
this._downAutoValue(out);
|
|
1111
|
-
} catch (err) {
|
|
1112
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1113
|
-
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
1114
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
1115
1154
|
}
|
|
1116
1155
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
this.
|
|
1120
|
-
|
|
1121
|
-
if (!wasDirty) {
|
|
1122
|
-
this._downInternal([[DIRTY]]);
|
|
1156
|
+
_upInternal(messages) {
|
|
1157
|
+
if (!this._hasDeps) return;
|
|
1158
|
+
for (const dep of this._deps) {
|
|
1159
|
+
dep.up?.(messages, { internal: true });
|
|
1123
1160
|
}
|
|
1124
1161
|
}
|
|
1125
|
-
|
|
1126
|
-
if (!this.
|
|
1127
|
-
|
|
1162
|
+
unsubscribe() {
|
|
1163
|
+
if (!this._hasDeps) return;
|
|
1164
|
+
this._disconnectUpstream();
|
|
1165
|
+
}
|
|
1166
|
+
// --- Activation (first-subscriber / last-subscriber hooks) ---
|
|
1167
|
+
_onActivate() {
|
|
1168
|
+
if (this._hasDeps) {
|
|
1169
|
+
this._connectUpstream();
|
|
1170
|
+
return;
|
|
1128
1171
|
}
|
|
1129
|
-
this.
|
|
1130
|
-
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1131
|
-
this._depDirtyMask.reset();
|
|
1132
|
-
this._depSettledMask.reset();
|
|
1172
|
+
if (this._fn) {
|
|
1133
1173
|
this._runFn();
|
|
1174
|
+
return;
|
|
1134
1175
|
}
|
|
1135
1176
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1177
|
+
_doDeactivate() {
|
|
1178
|
+
this._disconnectUpstream();
|
|
1179
|
+
const cleanup = this._cleanup;
|
|
1180
|
+
this._cleanup = void 0;
|
|
1181
|
+
cleanup?.();
|
|
1182
|
+
if (this._fn != null) {
|
|
1183
|
+
this._cached = NO_VALUE;
|
|
1184
|
+
this._lastDepValues = void 0;
|
|
1185
|
+
}
|
|
1186
|
+
if (this._hasDeps || this._fn != null) {
|
|
1187
|
+
this._status = "disconnected";
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
// --- INVALIDATE / TEARDOWN hooks (clear fn state) ---
|
|
1191
|
+
_onInvalidate() {
|
|
1192
|
+
const cleanup = this._cleanup;
|
|
1193
|
+
this._cleanup = void 0;
|
|
1194
|
+
cleanup?.();
|
|
1195
|
+
this._lastDepValues = void 0;
|
|
1196
|
+
}
|
|
1197
|
+
_onTeardown() {
|
|
1198
|
+
const cleanup = this._cleanup;
|
|
1199
|
+
this._cleanup = void 0;
|
|
1200
|
+
cleanup?.();
|
|
1201
|
+
}
|
|
1202
|
+
// --- Upstream connect / disconnect ---
|
|
1203
|
+
_connectUpstream() {
|
|
1204
|
+
if (!this._hasDeps) return;
|
|
1205
|
+
if (this._upstreamUnsubs.length > 0) return;
|
|
1206
|
+
this._depDirtyMask.setAll();
|
|
1207
|
+
this._depSettledMask.reset();
|
|
1208
|
+
this._depCompleteMask.reset();
|
|
1209
|
+
const depValuesBefore = this._lastDepValues;
|
|
1210
|
+
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1211
|
+
for (let i = 0; i < this._deps.length; i += 1) {
|
|
1212
|
+
const dep = this._deps[i];
|
|
1213
|
+
this._upstreamUnsubs.push(
|
|
1214
|
+
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
if (this._fn && this._onMessage && !this._terminal && this._lastDepValues === depValuesBefore) {
|
|
1218
|
+
this._runFn();
|
|
1139
1219
|
}
|
|
1140
1220
|
}
|
|
1221
|
+
_disconnectUpstream() {
|
|
1222
|
+
if (this._upstreamUnsubs.length === 0) return;
|
|
1223
|
+
for (const unsub of this._upstreamUnsubs.splice(0)) {
|
|
1224
|
+
unsub();
|
|
1225
|
+
}
|
|
1226
|
+
this._depDirtyMask.reset();
|
|
1227
|
+
this._depSettledMask.reset();
|
|
1228
|
+
this._depCompleteMask.reset();
|
|
1229
|
+
}
|
|
1230
|
+
// --- Wave handling ---
|
|
1141
1231
|
_handleDepMessages(index, messages) {
|
|
1142
1232
|
for (const msg of messages) {
|
|
1143
|
-
this.
|
|
1233
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
1144
1234
|
const t = msg[0];
|
|
1145
1235
|
if (this._onMessage) {
|
|
1146
1236
|
try {
|
|
1147
|
-
|
|
1237
|
+
const consumed = this._onMessage(msg, index, this._actions);
|
|
1238
|
+
if (consumed) {
|
|
1239
|
+
if (t === START) {
|
|
1240
|
+
this._depDirtyMask.clear(index);
|
|
1241
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1242
|
+
this._depDirtyMask.reset();
|
|
1243
|
+
this._depSettledMask.reset();
|
|
1244
|
+
this._runFn();
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1148
1249
|
} catch (err) {
|
|
1149
1250
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1150
1251
|
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
@@ -1154,6 +1255,7 @@ var NodeImpl = class {
|
|
|
1154
1255
|
return;
|
|
1155
1256
|
}
|
|
1156
1257
|
}
|
|
1258
|
+
if (messageTier(t) < 1) continue;
|
|
1157
1259
|
if (!this._fn) {
|
|
1158
1260
|
if (t === COMPLETE && this._deps.length > 1) {
|
|
1159
1261
|
this._depCompleteMask.set(index);
|
|
@@ -1197,53 +1299,85 @@ var NodeImpl = class {
|
|
|
1197
1299
|
this._downInternal([msg]);
|
|
1198
1300
|
}
|
|
1199
1301
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
this.
|
|
1203
|
-
this.
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const dep = this._deps[i];
|
|
1212
|
-
this._upstreamUnsubs.push(
|
|
1213
|
-
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1214
|
-
);
|
|
1215
|
-
}
|
|
1216
|
-
} finally {
|
|
1217
|
-
this._connecting = false;
|
|
1302
|
+
_onDepDirty(index) {
|
|
1303
|
+
const wasDirty = this._depDirtyMask.has(index);
|
|
1304
|
+
this._depDirtyMask.set(index);
|
|
1305
|
+
this._depSettledMask.clear(index);
|
|
1306
|
+
if (!wasDirty) {
|
|
1307
|
+
this._downInternal([[DIRTY]]);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
_onDepSettled(index) {
|
|
1311
|
+
if (!this._depDirtyMask.has(index)) {
|
|
1312
|
+
this._onDepDirty(index);
|
|
1218
1313
|
}
|
|
1219
|
-
|
|
1314
|
+
this._depSettledMask.set(index);
|
|
1315
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1316
|
+
this._depDirtyMask.reset();
|
|
1317
|
+
this._depSettledMask.reset();
|
|
1220
1318
|
this._runFn();
|
|
1221
1319
|
}
|
|
1222
1320
|
}
|
|
1223
|
-
|
|
1224
|
-
if (
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
this._cleanup = void 0;
|
|
1228
|
-
producerCleanup?.();
|
|
1229
|
-
}
|
|
1230
|
-
_startProducer() {
|
|
1231
|
-
if (this._deps.length !== 0 || !this._fn || this._producerStarted) return;
|
|
1232
|
-
this._producerStarted = true;
|
|
1233
|
-
this._runFn();
|
|
1321
|
+
_maybeCompleteFromDeps() {
|
|
1322
|
+
if (this._autoComplete && this._deps.length > 0 && this._depCompleteMask.covers(this._allDepsCompleteMask)) {
|
|
1323
|
+
this._downInternal([[COMPLETE]]);
|
|
1324
|
+
}
|
|
1234
1325
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1326
|
+
// --- Fn execution ---
|
|
1327
|
+
_runFn() {
|
|
1328
|
+
if (!this._fn) return;
|
|
1329
|
+
if (this._terminal && !this._resubscribable) return;
|
|
1330
|
+
try {
|
|
1331
|
+
const n = this._deps.length;
|
|
1332
|
+
const depValues = new Array(n);
|
|
1333
|
+
for (let i = 0; i < n; i++) depValues[i] = this._deps[i].get();
|
|
1334
|
+
const prev = this._lastDepValues;
|
|
1335
|
+
if (n > 0 && prev != null && prev.length === n) {
|
|
1336
|
+
let allSame = true;
|
|
1337
|
+
for (let i = 0; i < n; i++) {
|
|
1338
|
+
if (!Object.is(depValues[i], prev[i])) {
|
|
1339
|
+
allSame = false;
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (allSame) {
|
|
1344
|
+
if (this._status === "dirty") {
|
|
1345
|
+
this._downInternal([[RESOLVED]]);
|
|
1346
|
+
}
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
const prevCleanup = this._cleanup;
|
|
1351
|
+
this._cleanup = void 0;
|
|
1352
|
+
prevCleanup?.();
|
|
1353
|
+
this._manualEmitUsed = false;
|
|
1354
|
+
this._lastDepValues = depValues;
|
|
1355
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
1356
|
+
const out = this._fn(depValues, this._actions);
|
|
1357
|
+
if (isCleanupResult(out)) {
|
|
1358
|
+
this._cleanup = out.cleanup;
|
|
1359
|
+
if (this._manualEmitUsed) return;
|
|
1360
|
+
if ("value" in out) {
|
|
1361
|
+
this._downAutoValue(out.value);
|
|
1362
|
+
}
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (isCleanupFn(out)) {
|
|
1366
|
+
this._cleanup = out;
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
if (this._manualEmitUsed) return;
|
|
1370
|
+
if (out === void 0) return;
|
|
1371
|
+
this._downAutoValue(out);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1374
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
1375
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
1239
1376
|
}
|
|
1240
|
-
this._connected = false;
|
|
1241
|
-
this._depDirtyMask.reset();
|
|
1242
|
-
this._depSettledMask.reset();
|
|
1243
|
-
this._depCompleteMask.reset();
|
|
1244
|
-
this._status = "disconnected";
|
|
1245
1377
|
}
|
|
1246
1378
|
};
|
|
1379
|
+
var isNodeArray = (value) => Array.isArray(value);
|
|
1380
|
+
var isNodeOptions = (value) => typeof value === "object" && value != null && !Array.isArray(value);
|
|
1247
1381
|
function node(depsOrFn, fnOrOpts, optsArg) {
|
|
1248
1382
|
const deps = isNodeArray(depsOrFn) ? depsOrFn : [];
|
|
1249
1383
|
const fn = typeof depsOrFn === "function" ? depsOrFn : typeof fnOrOpts === "function" ? fnOrOpts : void 0;
|
|
@@ -1380,6 +1514,10 @@ function fromCron(expr, opts) {
|
|
|
1380
1514
|
{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` }
|
|
1381
1515
|
);
|
|
1382
1516
|
}
|
|
1517
|
+
function keepalive(n) {
|
|
1518
|
+
return n.subscribe(() => {
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1383
1521
|
|
|
1384
1522
|
// src/compat/nestjs/explorer.ts
|
|
1385
1523
|
var scheduleSeq = 0;
|
|
@@ -2058,261 +2196,78 @@ function fromHeader(headerName = "x-graphrefly-actor") {
|
|
|
2058
2196
|
return (context) => {
|
|
2059
2197
|
const req = context.switchToHttp().getRequest();
|
|
2060
2198
|
const raw = req?.headers?.[headerName.toLowerCase()];
|
|
2061
|
-
if (typeof raw !== "string" || raw.length === 0) return void 0;
|
|
2062
|
-
try {
|
|
2063
|
-
return JSON.parse(raw);
|
|
2064
|
-
} catch {
|
|
2065
|
-
return void 0;
|
|
2066
|
-
}
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
function getActor(req) {
|
|
2070
|
-
const actor = req?.[ACTOR_KEY];
|
|
2071
|
-
return actor != null ? normalizeActor(actor) : DEFAULT_ACTOR;
|
|
2072
|
-
}
|
|
2073
|
-
var GraphReflyGuardImpl = class {
|
|
2074
|
-
constructor(extractor) {
|
|
2075
|
-
this.extractor = extractor;
|
|
2076
|
-
}
|
|
2077
|
-
canActivate(context) {
|
|
2078
|
-
const actor = normalizeActor(this.extractor(context));
|
|
2079
|
-
const req = context.switchToHttp().getRequest();
|
|
2080
|
-
if (req != null) {
|
|
2081
|
-
req[ACTOR_KEY] = actor;
|
|
2082
|
-
}
|
|
2083
|
-
return true;
|
|
2084
|
-
}
|
|
2085
|
-
};
|
|
2086
|
-
function GraphReflyGuard(extractor) {
|
|
2087
|
-
return new GraphReflyGuardImpl(extractor ?? fromJwtPayload());
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
// src/compat/nestjs/module.ts
|
|
2091
|
-
var import_common2 = require("@nestjs/common");
|
|
2092
|
-
var import_core2 = require("@nestjs/core");
|
|
2093
|
-
|
|
2094
|
-
// src/core/dynamic-node.ts
|
|
2095
|
-
var DynamicNodeImpl = class {
|
|
2096
|
-
_optsName;
|
|
2097
|
-
_registryName;
|
|
2098
|
-
_describeKind;
|
|
2099
|
-
meta;
|
|
2100
|
-
_fn;
|
|
2101
|
-
_equals;
|
|
2102
|
-
_resubscribable;
|
|
2103
|
-
_resetOnTeardown;
|
|
2104
|
-
_autoComplete;
|
|
2105
|
-
_onMessage;
|
|
2106
|
-
_onResubscribe;
|
|
2107
|
-
/** @internal — read by {@link describeNode} for `accessHintForGuard`. */
|
|
2108
|
-
_guard;
|
|
2109
|
-
_lastMutation;
|
|
2110
|
-
_inspectorHook;
|
|
2111
|
-
// Sink tracking
|
|
2112
|
-
_sinkCount = 0;
|
|
2113
|
-
_singleDepSinkCount = 0;
|
|
2114
|
-
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
2115
|
-
// Actions object (for onMessage handler)
|
|
2116
|
-
_actions;
|
|
2117
|
-
_boundDownToSinks;
|
|
2118
|
-
// Mutable state
|
|
2119
|
-
_cached = NO_VALUE;
|
|
2120
|
-
_status = "disconnected";
|
|
2121
|
-
_terminal = false;
|
|
2122
|
-
_connected = false;
|
|
2123
|
-
_rewiring = false;
|
|
2124
|
-
// re-entrancy guard
|
|
2125
|
-
// Dynamic deps tracking
|
|
2126
|
-
_deps = [];
|
|
2127
|
-
_depUnsubs = [];
|
|
2128
|
-
_depIndexMap = /* @__PURE__ */ new Map();
|
|
2129
|
-
// node → index in _deps
|
|
2130
|
-
_dirtyBits = /* @__PURE__ */ new Set();
|
|
2131
|
-
_settledBits = /* @__PURE__ */ new Set();
|
|
2132
|
-
_completeBits = /* @__PURE__ */ new Set();
|
|
2133
|
-
// Sinks
|
|
2134
|
-
_sinks = null;
|
|
2135
|
-
constructor(fn, opts) {
|
|
2136
|
-
this._fn = fn;
|
|
2137
|
-
this._optsName = opts.name;
|
|
2138
|
-
this._describeKind = opts.describeKind;
|
|
2139
|
-
this._equals = opts.equals ?? Object.is;
|
|
2140
|
-
this._resubscribable = opts.resubscribable ?? false;
|
|
2141
|
-
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
2142
|
-
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
2143
|
-
this._onMessage = opts.onMessage;
|
|
2144
|
-
this._onResubscribe = opts.onResubscribe;
|
|
2145
|
-
this._guard = opts.guard;
|
|
2146
|
-
this._inspectorHook = void 0;
|
|
2147
|
-
const meta = {};
|
|
2148
|
-
for (const [k, v] of Object.entries(opts.meta ?? {})) {
|
|
2149
|
-
meta[k] = node({
|
|
2150
|
-
initial: v,
|
|
2151
|
-
name: `${opts.name ?? "dynamicNode"}:meta:${k}`,
|
|
2152
|
-
describeKind: "state",
|
|
2153
|
-
...opts.guard != null ? { guard: opts.guard } : {}
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
Object.freeze(meta);
|
|
2157
|
-
this.meta = meta;
|
|
2158
|
-
const self = this;
|
|
2159
|
-
this._actions = {
|
|
2160
|
-
down(messages) {
|
|
2161
|
-
self._downInternal(messages);
|
|
2162
|
-
},
|
|
2163
|
-
emit(value) {
|
|
2164
|
-
self._downAutoValue(value);
|
|
2165
|
-
},
|
|
2166
|
-
up(messages) {
|
|
2167
|
-
for (const dep of self._deps) {
|
|
2168
|
-
dep.up?.(messages, { internal: true });
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
};
|
|
2172
|
-
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
2173
|
-
}
|
|
2174
|
-
get name() {
|
|
2175
|
-
return this._registryName ?? this._optsName;
|
|
2176
|
-
}
|
|
2177
|
-
/** @internal */
|
|
2178
|
-
_assignRegistryName(localName) {
|
|
2179
|
-
if (this._optsName !== void 0 || this._registryName !== void 0) return;
|
|
2180
|
-
this._registryName = localName;
|
|
2181
|
-
}
|
|
2182
|
-
/**
|
|
2183
|
-
* @internal Attach/remove inspector hook for graph-level observability.
|
|
2184
|
-
* Returns a disposer that restores the previous hook.
|
|
2185
|
-
*/
|
|
2186
|
-
_setInspectorHook(hook) {
|
|
2187
|
-
const prev = this._inspectorHook;
|
|
2188
|
-
this._inspectorHook = hook;
|
|
2189
|
-
return () => {
|
|
2190
|
-
if (this._inspectorHook === hook) {
|
|
2191
|
-
this._inspectorHook = prev;
|
|
2192
|
-
}
|
|
2193
|
-
};
|
|
2194
|
-
}
|
|
2195
|
-
get status() {
|
|
2196
|
-
return this._status;
|
|
2197
|
-
}
|
|
2198
|
-
get lastMutation() {
|
|
2199
|
-
return this._lastMutation;
|
|
2200
|
-
}
|
|
2201
|
-
/** Versioning not yet supported on DynamicNodeImpl. */
|
|
2202
|
-
get v() {
|
|
2203
|
-
return void 0;
|
|
2204
|
-
}
|
|
2205
|
-
hasGuard() {
|
|
2206
|
-
return this._guard != null;
|
|
2207
|
-
}
|
|
2208
|
-
allowsObserve(actor) {
|
|
2209
|
-
if (this._guard == null) return true;
|
|
2210
|
-
return this._guard(normalizeActor(actor), "observe");
|
|
2211
|
-
}
|
|
2212
|
-
get() {
|
|
2213
|
-
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
2214
|
-
}
|
|
2215
|
-
down(messages, options) {
|
|
2216
|
-
if (messages.length === 0) return;
|
|
2217
|
-
if (!options?.internal && this._guard != null) {
|
|
2218
|
-
const actor = normalizeActor(options?.actor);
|
|
2219
|
-
const delivery = options?.delivery ?? "write";
|
|
2220
|
-
const action = delivery === "signal" ? "signal" : "write";
|
|
2221
|
-
if (!this._guard(actor, action)) {
|
|
2222
|
-
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
2223
|
-
}
|
|
2224
|
-
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
2225
|
-
}
|
|
2226
|
-
this._downInternal(messages);
|
|
2227
|
-
}
|
|
2228
|
-
_downInternal(messages) {
|
|
2229
|
-
if (messages.length === 0) return;
|
|
2230
|
-
let sinkMessages = messages;
|
|
2231
|
-
if (this._terminal && !this._resubscribable) {
|
|
2232
|
-
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
2233
|
-
if (pass.length === 0) return;
|
|
2234
|
-
sinkMessages = pass;
|
|
2235
|
-
}
|
|
2236
|
-
this._handleLocalLifecycle(sinkMessages);
|
|
2237
|
-
if (this._canSkipDirty()) {
|
|
2238
|
-
let hasPhase2 = false;
|
|
2239
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
2240
|
-
const t = sinkMessages[i][0];
|
|
2241
|
-
if (t === DATA || t === RESOLVED) {
|
|
2242
|
-
hasPhase2 = true;
|
|
2243
|
-
break;
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
if (hasPhase2) {
|
|
2247
|
-
const filtered = [];
|
|
2248
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
2249
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
2250
|
-
}
|
|
2251
|
-
if (filtered.length > 0) {
|
|
2252
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
2253
|
-
}
|
|
2254
|
-
return;
|
|
2255
|
-
}
|
|
2199
|
+
if (typeof raw !== "string" || raw.length === 0) return void 0;
|
|
2200
|
+
try {
|
|
2201
|
+
return JSON.parse(raw);
|
|
2202
|
+
} catch {
|
|
2203
|
+
return void 0;
|
|
2256
2204
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
function getActor(req) {
|
|
2208
|
+
const actor = req?.[ACTOR_KEY];
|
|
2209
|
+
return actor != null ? normalizeActor(actor) : DEFAULT_ACTOR;
|
|
2210
|
+
}
|
|
2211
|
+
var GraphReflyGuardImpl = class {
|
|
2212
|
+
constructor(extractor) {
|
|
2213
|
+
this.extractor = extractor;
|
|
2261
2214
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
if (this._terminal && this._resubscribable) {
|
|
2270
|
-
this._terminal = false;
|
|
2271
|
-
this._cached = NO_VALUE;
|
|
2272
|
-
this._status = "disconnected";
|
|
2273
|
-
this._onResubscribe?.();
|
|
2274
|
-
}
|
|
2275
|
-
this._sinkCount += 1;
|
|
2276
|
-
if (hints?.singleDep) {
|
|
2277
|
-
this._singleDepSinkCount += 1;
|
|
2278
|
-
this._singleDepSinks.add(sink);
|
|
2279
|
-
}
|
|
2280
|
-
if (this._sinks == null) {
|
|
2281
|
-
this._sinks = sink;
|
|
2282
|
-
} else if (typeof this._sinks === "function") {
|
|
2283
|
-
this._sinks = /* @__PURE__ */ new Set([this._sinks, sink]);
|
|
2284
|
-
} else {
|
|
2285
|
-
this._sinks.add(sink);
|
|
2286
|
-
}
|
|
2287
|
-
if (!this._connected) {
|
|
2288
|
-
this._connect();
|
|
2215
|
+
canActivate(context) {
|
|
2216
|
+
const actor = normalizeActor(this.extractor(context));
|
|
2217
|
+
const req = context.switchToHttp().getRequest();
|
|
2218
|
+
if (req != null) {
|
|
2219
|
+
req[ACTOR_KEY] = actor;
|
|
2289
2220
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2221
|
+
return true;
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
function GraphReflyGuard(extractor) {
|
|
2225
|
+
return new GraphReflyGuardImpl(extractor ?? fromJwtPayload());
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
// src/compat/nestjs/module.ts
|
|
2229
|
+
var import_common2 = require("@nestjs/common");
|
|
2230
|
+
var import_core2 = require("@nestjs/core");
|
|
2231
|
+
|
|
2232
|
+
// src/core/dynamic-node.ts
|
|
2233
|
+
var MAX_RERUN = 16;
|
|
2234
|
+
var DynamicNodeImpl = class extends NodeBase {
|
|
2235
|
+
_fn;
|
|
2236
|
+
_autoComplete;
|
|
2237
|
+
// Dynamic deps tracking
|
|
2238
|
+
/** @internal Read by `describeNode`. */
|
|
2239
|
+
_deps = [];
|
|
2240
|
+
_depUnsubs = [];
|
|
2241
|
+
_depIndexMap = /* @__PURE__ */ new Map();
|
|
2242
|
+
_depDirtyBits = /* @__PURE__ */ new Set();
|
|
2243
|
+
_depSettledBits = /* @__PURE__ */ new Set();
|
|
2244
|
+
_depCompleteBits = /* @__PURE__ */ new Set();
|
|
2245
|
+
// Execution state
|
|
2246
|
+
_running = false;
|
|
2247
|
+
_rewiring = false;
|
|
2248
|
+
_bufferedDepMessages = [];
|
|
2249
|
+
_trackedValues = /* @__PURE__ */ new Map();
|
|
2250
|
+
_rerunCount = 0;
|
|
2251
|
+
constructor(fn, opts) {
|
|
2252
|
+
super(opts);
|
|
2253
|
+
this._fn = fn;
|
|
2254
|
+
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
2255
|
+
this.down = this.down.bind(this);
|
|
2256
|
+
this.up = this.up.bind(this);
|
|
2315
2257
|
}
|
|
2258
|
+
_createMetaNode(key, initialValue, opts) {
|
|
2259
|
+
return node({
|
|
2260
|
+
initial: initialValue,
|
|
2261
|
+
name: `${opts.name ?? "dynamicNode"}:meta:${key}`,
|
|
2262
|
+
describeKind: "state",
|
|
2263
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
/** Versioning not supported on DynamicNodeImpl (override base). */
|
|
2267
|
+
get v() {
|
|
2268
|
+
return void 0;
|
|
2269
|
+
}
|
|
2270
|
+
// --- Up / unsubscribe ---
|
|
2316
2271
|
up(messages, options) {
|
|
2317
2272
|
if (this._deps.length === 0) return;
|
|
2318
2273
|
if (!options?.internal && this._guard != null) {
|
|
@@ -2320,221 +2275,227 @@ var DynamicNodeImpl = class {
|
|
|
2320
2275
|
if (!this._guard(actor, "write")) {
|
|
2321
2276
|
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
2322
2277
|
}
|
|
2323
|
-
this.
|
|
2278
|
+
this._recordMutation(actor);
|
|
2324
2279
|
}
|
|
2325
2280
|
for (const dep of this._deps) {
|
|
2326
2281
|
dep.up?.(messages, options);
|
|
2327
2282
|
}
|
|
2328
2283
|
}
|
|
2329
|
-
|
|
2330
|
-
this.
|
|
2331
|
-
|
|
2332
|
-
// --- Private methods ---
|
|
2333
|
-
_downToSinks(messages) {
|
|
2334
|
-
if (this._sinks == null) return;
|
|
2335
|
-
if (typeof this._sinks === "function") {
|
|
2336
|
-
this._sinks(messages);
|
|
2337
|
-
return;
|
|
2338
|
-
}
|
|
2339
|
-
const snapshot = [...this._sinks];
|
|
2340
|
-
for (const sink of snapshot) {
|
|
2341
|
-
sink(messages);
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
_handleLocalLifecycle(messages) {
|
|
2345
|
-
for (const m of messages) {
|
|
2346
|
-
const t = m[0];
|
|
2347
|
-
if (t === DATA) this._cached = m[1];
|
|
2348
|
-
if (t === INVALIDATE) {
|
|
2349
|
-
this._cached = NO_VALUE;
|
|
2350
|
-
this._status = "dirty";
|
|
2351
|
-
}
|
|
2352
|
-
if (t === DATA) {
|
|
2353
|
-
this._status = "settled";
|
|
2354
|
-
} else if (t === RESOLVED) {
|
|
2355
|
-
this._status = "resolved";
|
|
2356
|
-
} else if (t === DIRTY) {
|
|
2357
|
-
this._status = "dirty";
|
|
2358
|
-
} else if (t === COMPLETE) {
|
|
2359
|
-
this._status = "completed";
|
|
2360
|
-
this._terminal = true;
|
|
2361
|
-
} else if (t === ERROR) {
|
|
2362
|
-
this._status = "errored";
|
|
2363
|
-
this._terminal = true;
|
|
2364
|
-
}
|
|
2365
|
-
if (t === TEARDOWN) {
|
|
2366
|
-
if (this._resetOnTeardown) this._cached = NO_VALUE;
|
|
2367
|
-
try {
|
|
2368
|
-
this._propagateToMeta(t);
|
|
2369
|
-
} finally {
|
|
2370
|
-
this._disconnect();
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
2374
|
-
this._propagateToMeta(t);
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
/** Propagate a signal to all companion meta nodes (best-effort). */
|
|
2379
|
-
_propagateToMeta(t) {
|
|
2380
|
-
for (const metaNode of Object.values(this.meta)) {
|
|
2381
|
-
try {
|
|
2382
|
-
metaNode.down([[t]], { internal: true });
|
|
2383
|
-
} catch {
|
|
2384
|
-
}
|
|
2284
|
+
_upInternal(messages) {
|
|
2285
|
+
for (const dep of this._deps) {
|
|
2286
|
+
dep.up?.(messages, { internal: true });
|
|
2385
2287
|
}
|
|
2386
2288
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
let unchanged;
|
|
2390
|
-
try {
|
|
2391
|
-
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
2392
|
-
} catch (eqErr) {
|
|
2393
|
-
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
2394
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
|
|
2395
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
2396
|
-
return;
|
|
2397
|
-
}
|
|
2398
|
-
if (unchanged) {
|
|
2399
|
-
this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
|
|
2400
|
-
return;
|
|
2401
|
-
}
|
|
2402
|
-
this._cached = value;
|
|
2403
|
-
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
2289
|
+
unsubscribe() {
|
|
2290
|
+
this._disconnect();
|
|
2404
2291
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
this._connected = true;
|
|
2408
|
-
this._status = "settled";
|
|
2409
|
-
this._dirtyBits.clear();
|
|
2410
|
-
this._settledBits.clear();
|
|
2411
|
-
this._completeBits.clear();
|
|
2292
|
+
// --- Activation hooks ---
|
|
2293
|
+
_onActivate() {
|
|
2412
2294
|
this._runFn();
|
|
2413
2295
|
}
|
|
2296
|
+
_doDeactivate() {
|
|
2297
|
+
this._disconnect();
|
|
2298
|
+
}
|
|
2414
2299
|
_disconnect() {
|
|
2415
|
-
if (!this._connected) return;
|
|
2416
2300
|
for (const unsub of this._depUnsubs) unsub();
|
|
2417
2301
|
this._depUnsubs = [];
|
|
2418
2302
|
this._deps = [];
|
|
2419
2303
|
this._depIndexMap.clear();
|
|
2420
|
-
this.
|
|
2421
|
-
this.
|
|
2422
|
-
this.
|
|
2423
|
-
this.
|
|
2304
|
+
this._depDirtyBits.clear();
|
|
2305
|
+
this._depSettledBits.clear();
|
|
2306
|
+
this._depCompleteBits.clear();
|
|
2307
|
+
this._cached = NO_VALUE;
|
|
2424
2308
|
this._status = "disconnected";
|
|
2425
2309
|
}
|
|
2310
|
+
// --- Fn execution with rewire buffer ---
|
|
2426
2311
|
_runFn() {
|
|
2427
2312
|
if (this._terminal && !this._resubscribable) return;
|
|
2428
|
-
if (this.
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
if (!trackedSet.has(dep)) {
|
|
2433
|
-
trackedSet.add(dep);
|
|
2434
|
-
trackedDeps.push(dep);
|
|
2435
|
-
}
|
|
2436
|
-
return dep.get();
|
|
2437
|
-
};
|
|
2313
|
+
if (this._running) return;
|
|
2314
|
+
this._running = true;
|
|
2315
|
+
this._rerunCount = 0;
|
|
2316
|
+
let result;
|
|
2438
2317
|
try {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2318
|
+
for (; ; ) {
|
|
2319
|
+
const trackedDeps = [];
|
|
2320
|
+
const trackedValuesMap = /* @__PURE__ */ new Map();
|
|
2321
|
+
const trackedSet = /* @__PURE__ */ new Set();
|
|
2322
|
+
const get = (dep) => {
|
|
2323
|
+
if (!trackedSet.has(dep)) {
|
|
2324
|
+
trackedSet.add(dep);
|
|
2325
|
+
trackedDeps.push(dep);
|
|
2326
|
+
trackedValuesMap.set(dep, dep.get());
|
|
2327
|
+
}
|
|
2328
|
+
return dep.get();
|
|
2329
|
+
};
|
|
2330
|
+
this._trackedValues = trackedValuesMap;
|
|
2331
|
+
const depValues = [];
|
|
2332
|
+
for (const dep of this._deps) depValues.push(dep.get());
|
|
2333
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
2334
|
+
try {
|
|
2335
|
+
result = this._fn(get);
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2338
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, {
|
|
2339
|
+
cause: err
|
|
2340
|
+
});
|
|
2341
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
this._rewiring = true;
|
|
2345
|
+
this._bufferedDepMessages = [];
|
|
2346
|
+
try {
|
|
2347
|
+
this._rewire(trackedDeps);
|
|
2348
|
+
} finally {
|
|
2349
|
+
this._rewiring = false;
|
|
2350
|
+
}
|
|
2351
|
+
let needsRerun = false;
|
|
2352
|
+
for (const entry of this._bufferedDepMessages) {
|
|
2353
|
+
for (const msg of entry.msgs) {
|
|
2354
|
+
if (msg[0] === DATA) {
|
|
2355
|
+
const dep = this._deps[entry.index];
|
|
2356
|
+
const trackedValue = dep != null ? this._trackedValues.get(dep) : void 0;
|
|
2357
|
+
const actualValue = msg[1];
|
|
2358
|
+
if (!this._equals(trackedValue, actualValue)) {
|
|
2359
|
+
needsRerun = true;
|
|
2360
|
+
break;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
if (needsRerun) break;
|
|
2365
|
+
}
|
|
2366
|
+
if (needsRerun) {
|
|
2367
|
+
this._rerunCount += 1;
|
|
2368
|
+
if (this._rerunCount > MAX_RERUN) {
|
|
2369
|
+
this._bufferedDepMessages = [];
|
|
2370
|
+
this._downInternal([
|
|
2371
|
+
[
|
|
2372
|
+
ERROR,
|
|
2373
|
+
new Error(
|
|
2374
|
+
`dynamicNode "${this.name ?? "anonymous"}": rewire did not stabilize within ${MAX_RERUN} iterations`
|
|
2375
|
+
)
|
|
2376
|
+
]
|
|
2377
|
+
]);
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
this._bufferedDepMessages = [];
|
|
2381
|
+
continue;
|
|
2382
|
+
}
|
|
2383
|
+
const drain = this._bufferedDepMessages;
|
|
2384
|
+
this._bufferedDepMessages = [];
|
|
2385
|
+
for (const entry of drain) {
|
|
2386
|
+
for (const msg of entry.msgs) {
|
|
2387
|
+
this._updateMasksForMessage(entry.index, msg);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
this._depDirtyBits.clear();
|
|
2391
|
+
this._depSettledBits.clear();
|
|
2392
|
+
break;
|
|
2393
|
+
}
|
|
2394
|
+
} finally {
|
|
2395
|
+
this._running = false;
|
|
2450
2396
|
}
|
|
2397
|
+
this._downAutoValue(result);
|
|
2451
2398
|
}
|
|
2452
2399
|
_rewire(newDeps) {
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
const
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
2469
|
-
newUnsubs.push(unsub);
|
|
2470
|
-
}
|
|
2400
|
+
const oldMap = this._depIndexMap;
|
|
2401
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
2402
|
+
const newUnsubs = [];
|
|
2403
|
+
for (let i = 0; i < newDeps.length; i++) {
|
|
2404
|
+
const dep = newDeps[i];
|
|
2405
|
+
newMap.set(dep, i);
|
|
2406
|
+
const oldIdx = oldMap.get(dep);
|
|
2407
|
+
if (oldIdx !== void 0) {
|
|
2408
|
+
newUnsubs.push(this._depUnsubs[oldIdx]);
|
|
2409
|
+
this._depUnsubs[oldIdx] = () => {
|
|
2410
|
+
};
|
|
2411
|
+
} else {
|
|
2412
|
+
const idx = i;
|
|
2413
|
+
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
2414
|
+
newUnsubs.push(unsub);
|
|
2471
2415
|
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2416
|
+
}
|
|
2417
|
+
for (const [dep, oldIdx] of oldMap) {
|
|
2418
|
+
if (!newMap.has(dep)) {
|
|
2419
|
+
this._depUnsubs[oldIdx]();
|
|
2476
2420
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2421
|
+
}
|
|
2422
|
+
this._deps = newDeps;
|
|
2423
|
+
this._depUnsubs = newUnsubs;
|
|
2424
|
+
this._depIndexMap = newMap;
|
|
2425
|
+
this._depDirtyBits.clear();
|
|
2426
|
+
this._depSettledBits.clear();
|
|
2427
|
+
const newCompleteBits = /* @__PURE__ */ new Set();
|
|
2428
|
+
for (const oldIdx of this._depCompleteBits) {
|
|
2429
|
+
for (const [dep, idx] of oldMap) {
|
|
2430
|
+
if (idx === oldIdx && newMap.has(dep)) {
|
|
2486
2431
|
newCompleteBits.add(newMap.get(dep));
|
|
2432
|
+
break;
|
|
2487
2433
|
}
|
|
2488
2434
|
}
|
|
2489
|
-
this._completeBits = newCompleteBits;
|
|
2490
|
-
} finally {
|
|
2491
|
-
this._rewiring = false;
|
|
2492
2435
|
}
|
|
2436
|
+
this._depCompleteBits = newCompleteBits;
|
|
2493
2437
|
}
|
|
2438
|
+
// --- Dep message handling ---
|
|
2494
2439
|
_handleDepMessages(index, messages) {
|
|
2495
|
-
if (this._rewiring)
|
|
2440
|
+
if (this._rewiring) {
|
|
2441
|
+
this._bufferedDepMessages.push({ index, msgs: messages });
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2496
2444
|
for (const msg of messages) {
|
|
2497
|
-
this.
|
|
2445
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
2498
2446
|
const t = msg[0];
|
|
2499
2447
|
if (this._onMessage) {
|
|
2500
2448
|
try {
|
|
2501
2449
|
if (this._onMessage(msg, index, this._actions)) continue;
|
|
2502
2450
|
} catch (err) {
|
|
2503
|
-
|
|
2451
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2452
|
+
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
2453
|
+
cause: err
|
|
2454
|
+
});
|
|
2455
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
2504
2456
|
return;
|
|
2505
2457
|
}
|
|
2506
2458
|
}
|
|
2459
|
+
if (messageTier(t) < 1) continue;
|
|
2507
2460
|
if (t === DIRTY) {
|
|
2508
|
-
this.
|
|
2509
|
-
this.
|
|
2510
|
-
|
|
2511
|
-
|
|
2461
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
2462
|
+
this._depDirtyBits.add(index);
|
|
2463
|
+
this._depSettledBits.delete(index);
|
|
2464
|
+
if (wasEmpty) {
|
|
2465
|
+
this._downInternal([[DIRTY]]);
|
|
2512
2466
|
}
|
|
2513
2467
|
continue;
|
|
2514
2468
|
}
|
|
2515
2469
|
if (t === DATA || t === RESOLVED) {
|
|
2516
|
-
if (!this.
|
|
2517
|
-
this.
|
|
2518
|
-
|
|
2470
|
+
if (!this._depDirtyBits.has(index)) {
|
|
2471
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
2472
|
+
this._depDirtyBits.add(index);
|
|
2473
|
+
if (wasEmpty) {
|
|
2474
|
+
this._downInternal([[DIRTY]]);
|
|
2475
|
+
}
|
|
2519
2476
|
}
|
|
2520
|
-
this.
|
|
2477
|
+
this._depSettledBits.add(index);
|
|
2521
2478
|
if (this._allDirtySettled()) {
|
|
2522
|
-
this.
|
|
2523
|
-
this.
|
|
2524
|
-
this.
|
|
2479
|
+
this._depDirtyBits.clear();
|
|
2480
|
+
this._depSettledBits.clear();
|
|
2481
|
+
if (!this._running) {
|
|
2482
|
+
if (this._depValuesDifferFromTracked()) {
|
|
2483
|
+
this._runFn();
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2525
2486
|
}
|
|
2526
2487
|
continue;
|
|
2527
2488
|
}
|
|
2528
2489
|
if (t === COMPLETE) {
|
|
2529
|
-
this.
|
|
2530
|
-
this.
|
|
2531
|
-
this.
|
|
2490
|
+
this._depCompleteBits.add(index);
|
|
2491
|
+
this._depDirtyBits.delete(index);
|
|
2492
|
+
this._depSettledBits.delete(index);
|
|
2532
2493
|
if (this._allDirtySettled()) {
|
|
2533
|
-
this.
|
|
2534
|
-
this.
|
|
2535
|
-
this._runFn();
|
|
2494
|
+
this._depDirtyBits.clear();
|
|
2495
|
+
this._depSettledBits.clear();
|
|
2496
|
+
if (!this._running) this._runFn();
|
|
2536
2497
|
}
|
|
2537
|
-
if (this._autoComplete && this.
|
|
2498
|
+
if (this._autoComplete && this._depCompleteBits.size >= this._deps.length && this._deps.length > 0) {
|
|
2538
2499
|
this._downInternal([[COMPLETE]]);
|
|
2539
2500
|
}
|
|
2540
2501
|
continue;
|
|
@@ -2550,13 +2511,46 @@ var DynamicNodeImpl = class {
|
|
|
2550
2511
|
this._downInternal([msg]);
|
|
2551
2512
|
}
|
|
2552
2513
|
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Update dep masks for a message without triggering `_runFn` — used
|
|
2516
|
+
* during post-rewire drain so the wave state is consistent with the
|
|
2517
|
+
* buffered activation cascade without recursing.
|
|
2518
|
+
*/
|
|
2519
|
+
_updateMasksForMessage(index, msg) {
|
|
2520
|
+
const t = msg[0];
|
|
2521
|
+
if (t === DIRTY) {
|
|
2522
|
+
this._depDirtyBits.add(index);
|
|
2523
|
+
this._depSettledBits.delete(index);
|
|
2524
|
+
} else if (t === DATA || t === RESOLVED) {
|
|
2525
|
+
this._depDirtyBits.add(index);
|
|
2526
|
+
this._depSettledBits.add(index);
|
|
2527
|
+
} else if (t === COMPLETE) {
|
|
2528
|
+
this._depCompleteBits.add(index);
|
|
2529
|
+
this._depDirtyBits.delete(index);
|
|
2530
|
+
this._depSettledBits.delete(index);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2553
2533
|
_allDirtySettled() {
|
|
2554
|
-
if (this.
|
|
2555
|
-
for (const idx of this.
|
|
2556
|
-
if (!this.
|
|
2534
|
+
if (this._depDirtyBits.size === 0) return false;
|
|
2535
|
+
for (const idx of this._depDirtyBits) {
|
|
2536
|
+
if (!this._depSettledBits.has(idx)) return false;
|
|
2557
2537
|
}
|
|
2558
2538
|
return true;
|
|
2559
2539
|
}
|
|
2540
|
+
/**
|
|
2541
|
+
* True if any current dep value differs from what the last `_runFn`
|
|
2542
|
+
* saw via `get()`. Used to suppress redundant re-runs when deferred
|
|
2543
|
+
* handshake messages arrive after `_rewire` for a dep whose value
|
|
2544
|
+
* already matches `_trackedValues`.
|
|
2545
|
+
*/
|
|
2546
|
+
_depValuesDifferFromTracked() {
|
|
2547
|
+
for (const dep of this._deps) {
|
|
2548
|
+
const current = dep.get();
|
|
2549
|
+
const tracked = this._trackedValues.get(dep);
|
|
2550
|
+
if (!this._equals(current, tracked)) return true;
|
|
2551
|
+
}
|
|
2552
|
+
return false;
|
|
2553
|
+
}
|
|
2560
2554
|
};
|
|
2561
2555
|
|
|
2562
2556
|
// src/core/meta.ts
|
|
@@ -2626,6 +2620,10 @@ function describeNode(node2, includeFields) {
|
|
|
2626
2620
|
out.name = node2.name;
|
|
2627
2621
|
}
|
|
2628
2622
|
if (all || includeFields.has("value")) {
|
|
2623
|
+
const isSentinel = node2 instanceof NodeImpl && node2._cached === NO_VALUE || node2 instanceof DynamicNodeImpl && node2._cached === NO_VALUE;
|
|
2624
|
+
if (isSentinel) {
|
|
2625
|
+
out.sentinel = true;
|
|
2626
|
+
}
|
|
2629
2627
|
try {
|
|
2630
2628
|
out.value = node2.get();
|
|
2631
2629
|
} catch {
|
|
@@ -2652,6 +2650,129 @@ function describeNode(node2, includeFields) {
|
|
|
2652
2650
|
return out;
|
|
2653
2651
|
}
|
|
2654
2652
|
|
|
2653
|
+
// src/graph/sizeof.ts
|
|
2654
|
+
var OVERHEAD = {
|
|
2655
|
+
object: 56,
|
|
2656
|
+
array: 64,
|
|
2657
|
+
string: 40,
|
|
2658
|
+
// header; content added separately
|
|
2659
|
+
number: 8,
|
|
2660
|
+
boolean: 4,
|
|
2661
|
+
null: 0,
|
|
2662
|
+
undefined: 0,
|
|
2663
|
+
symbol: 40,
|
|
2664
|
+
bigint: 16,
|
|
2665
|
+
function: 120,
|
|
2666
|
+
map: 72,
|
|
2667
|
+
set: 72,
|
|
2668
|
+
mapEntry: 40,
|
|
2669
|
+
setEntry: 24
|
|
2670
|
+
};
|
|
2671
|
+
function sizeof(value) {
|
|
2672
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
2673
|
+
return _sizeof(value, seen);
|
|
2674
|
+
}
|
|
2675
|
+
function _sizeof(value, seen) {
|
|
2676
|
+
if (value == null) return 0;
|
|
2677
|
+
const t = typeof value;
|
|
2678
|
+
switch (t) {
|
|
2679
|
+
case "number":
|
|
2680
|
+
return OVERHEAD.number;
|
|
2681
|
+
case "boolean":
|
|
2682
|
+
return OVERHEAD.boolean;
|
|
2683
|
+
case "string":
|
|
2684
|
+
return OVERHEAD.string + value.length * 2;
|
|
2685
|
+
// UTF-16
|
|
2686
|
+
case "bigint":
|
|
2687
|
+
return OVERHEAD.bigint;
|
|
2688
|
+
case "symbol":
|
|
2689
|
+
return OVERHEAD.symbol;
|
|
2690
|
+
case "function":
|
|
2691
|
+
if (seen.has(value)) return 0;
|
|
2692
|
+
seen.add(value);
|
|
2693
|
+
return OVERHEAD.function;
|
|
2694
|
+
case "undefined":
|
|
2695
|
+
return 0;
|
|
2696
|
+
}
|
|
2697
|
+
const obj = value;
|
|
2698
|
+
if (seen.has(obj)) return 0;
|
|
2699
|
+
seen.add(obj);
|
|
2700
|
+
if (obj instanceof Map) {
|
|
2701
|
+
let size2 = OVERHEAD.map;
|
|
2702
|
+
for (const [k, v] of obj) {
|
|
2703
|
+
size2 += OVERHEAD.mapEntry + _sizeof(k, seen) + _sizeof(v, seen);
|
|
2704
|
+
}
|
|
2705
|
+
return size2;
|
|
2706
|
+
}
|
|
2707
|
+
if (obj instanceof Set) {
|
|
2708
|
+
let size2 = OVERHEAD.set;
|
|
2709
|
+
for (const v of obj) {
|
|
2710
|
+
size2 += OVERHEAD.setEntry + _sizeof(v, seen);
|
|
2711
|
+
}
|
|
2712
|
+
return size2;
|
|
2713
|
+
}
|
|
2714
|
+
if (Array.isArray(obj)) {
|
|
2715
|
+
let size2 = OVERHEAD.array + obj.length * 8;
|
|
2716
|
+
for (const item of obj) {
|
|
2717
|
+
size2 += _sizeof(item, seen);
|
|
2718
|
+
}
|
|
2719
|
+
return size2;
|
|
2720
|
+
}
|
|
2721
|
+
if (obj instanceof ArrayBuffer) return obj.byteLength;
|
|
2722
|
+
if (ArrayBuffer.isView(obj)) return obj.byteLength;
|
|
2723
|
+
let size = OVERHEAD.object;
|
|
2724
|
+
const keys = Object.keys(obj);
|
|
2725
|
+
for (const key of keys) {
|
|
2726
|
+
size += OVERHEAD.string + key.length * 2;
|
|
2727
|
+
size += _sizeof(obj[key], seen);
|
|
2728
|
+
}
|
|
2729
|
+
return size;
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
// src/graph/profile.ts
|
|
2733
|
+
function graphProfile(graph, opts) {
|
|
2734
|
+
const topN = opts?.topN ?? 10;
|
|
2735
|
+
const desc = graph.describe({ detail: "standard" });
|
|
2736
|
+
const targets = [];
|
|
2737
|
+
if (typeof graph._collectObserveTargets === "function") {
|
|
2738
|
+
graph._collectObserveTargets("", targets);
|
|
2739
|
+
}
|
|
2740
|
+
const pathToNode = /* @__PURE__ */ new Map();
|
|
2741
|
+
for (const [p, n] of targets) {
|
|
2742
|
+
pathToNode.set(p, n);
|
|
2743
|
+
}
|
|
2744
|
+
const profiles = [];
|
|
2745
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
2746
|
+
const nd = pathToNode.get(path);
|
|
2747
|
+
const impl = nd instanceof NodeImpl ? nd : null;
|
|
2748
|
+
const valueSizeBytes = impl ? sizeof(impl.get()) : 0;
|
|
2749
|
+
const subscriberCount = impl ? impl._sinkCount : 0;
|
|
2750
|
+
const depCount = nodeDesc.deps?.length ?? 0;
|
|
2751
|
+
const isOrphanEffect = nodeDesc.type === "effect" && subscriberCount === 0;
|
|
2752
|
+
profiles.push({
|
|
2753
|
+
path,
|
|
2754
|
+
type: nodeDesc.type,
|
|
2755
|
+
status: nodeDesc.status ?? "unknown",
|
|
2756
|
+
valueSizeBytes,
|
|
2757
|
+
subscriberCount,
|
|
2758
|
+
depCount,
|
|
2759
|
+
isOrphanEffect
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
const totalValueSizeBytes = profiles.reduce((sum, p) => sum + p.valueSizeBytes, 0);
|
|
2763
|
+
const hotspots = [...profiles].sort((a, b) => b.valueSizeBytes - a.valueSizeBytes).slice(0, topN);
|
|
2764
|
+
const orphanEffects = profiles.filter((p) => p.isOrphanEffect);
|
|
2765
|
+
return {
|
|
2766
|
+
nodeCount: profiles.length,
|
|
2767
|
+
edgeCount: desc.edges.length,
|
|
2768
|
+
subgraphCount: desc.subgraphs.length,
|
|
2769
|
+
nodes: profiles,
|
|
2770
|
+
totalValueSizeBytes,
|
|
2771
|
+
hotspots,
|
|
2772
|
+
orphanEffects
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2655
2776
|
// src/graph/graph.ts
|
|
2656
2777
|
var PATH_SEP = "::";
|
|
2657
2778
|
var GRAPH_META_SEGMENT = "__meta__";
|
|
@@ -3564,6 +3685,16 @@ var Graph = class _Graph {
|
|
|
3564
3685
|
}
|
|
3565
3686
|
return out;
|
|
3566
3687
|
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Snapshot-based resource profile: per-node stats, orphan effect detection,
|
|
3690
|
+
* memory hotspots. Zero runtime overhead — walks nodes on demand.
|
|
3691
|
+
*
|
|
3692
|
+
* @param opts - Optional `topN` for hotspot limit (default 10).
|
|
3693
|
+
* @returns Aggregate profile with per-node details, hotspots, and orphan effects.
|
|
3694
|
+
*/
|
|
3695
|
+
resourceProfile(opts) {
|
|
3696
|
+
return graphProfile(this, opts);
|
|
3697
|
+
}
|
|
3567
3698
|
_qualifyEdgeEndpoint(part, prefix) {
|
|
3568
3699
|
if (part.includes(PATH_SEP)) return part;
|
|
3569
3700
|
return prefix === "" ? part : `${prefix}${PATH_SEP}${part}`;
|
|
@@ -3665,8 +3796,8 @@ var Graph = class _Graph {
|
|
|
3665
3796
|
dirtyCount: 0,
|
|
3666
3797
|
resolvedCount: 0,
|
|
3667
3798
|
events: [],
|
|
3668
|
-
|
|
3669
|
-
|
|
3799
|
+
anyCompletedCleanly: false,
|
|
3800
|
+
anyErrored: false
|
|
3670
3801
|
};
|
|
3671
3802
|
let lastTriggerDepIndex;
|
|
3672
3803
|
let lastRunDepValues;
|
|
@@ -3710,8 +3841,8 @@ var Graph = class _Graph {
|
|
|
3710
3841
|
} else if (minimal) {
|
|
3711
3842
|
if (t === DIRTY) result.dirtyCount++;
|
|
3712
3843
|
else if (t === RESOLVED) result.resolvedCount++;
|
|
3713
|
-
else if (t === COMPLETE && !result.
|
|
3714
|
-
else if (t === ERROR) result.
|
|
3844
|
+
else if (t === COMPLETE && !result.anyErrored) result.anyCompletedCleanly = true;
|
|
3845
|
+
else if (t === ERROR) result.anyErrored = true;
|
|
3715
3846
|
} else if (t === DIRTY) {
|
|
3716
3847
|
result.dirtyCount++;
|
|
3717
3848
|
result.events.push({ type: "dirty", path, ...base });
|
|
@@ -3719,10 +3850,10 @@ var Graph = class _Graph {
|
|
|
3719
3850
|
result.resolvedCount++;
|
|
3720
3851
|
result.events.push({ type: "resolved", path, ...base, ...withCausal });
|
|
3721
3852
|
} else if (t === COMPLETE) {
|
|
3722
|
-
if (!result.
|
|
3853
|
+
if (!result.anyErrored) result.anyCompletedCleanly = true;
|
|
3723
3854
|
result.events.push({ type: "complete", path, ...base });
|
|
3724
3855
|
} else if (t === ERROR) {
|
|
3725
|
-
result.
|
|
3856
|
+
result.anyErrored = true;
|
|
3726
3857
|
result.events.push({ type: "error", path, data: m[1], ...base });
|
|
3727
3858
|
}
|
|
3728
3859
|
}
|
|
@@ -3742,11 +3873,14 @@ var Graph = class _Graph {
|
|
|
3742
3873
|
get events() {
|
|
3743
3874
|
return result.events;
|
|
3744
3875
|
},
|
|
3745
|
-
get
|
|
3746
|
-
return result.
|
|
3876
|
+
get anyCompletedCleanly() {
|
|
3877
|
+
return result.anyCompletedCleanly;
|
|
3878
|
+
},
|
|
3879
|
+
get anyErrored() {
|
|
3880
|
+
return result.anyErrored;
|
|
3747
3881
|
},
|
|
3748
|
-
get
|
|
3749
|
-
return result.
|
|
3882
|
+
get completedWithoutErrors() {
|
|
3883
|
+
return result.anyCompletedCleanly && !result.anyErrored;
|
|
3750
3884
|
},
|
|
3751
3885
|
dispose() {
|
|
3752
3886
|
unsub();
|
|
@@ -3782,9 +3916,10 @@ var Graph = class _Graph {
|
|
|
3782
3916
|
dirtyCount: 0,
|
|
3783
3917
|
resolvedCount: 0,
|
|
3784
3918
|
events: [],
|
|
3785
|
-
|
|
3786
|
-
|
|
3919
|
+
anyCompletedCleanly: false,
|
|
3920
|
+
anyErrored: false
|
|
3787
3921
|
};
|
|
3922
|
+
const nodeErrored = /* @__PURE__ */ new Set();
|
|
3788
3923
|
const actor = options.actor;
|
|
3789
3924
|
const targets = [];
|
|
3790
3925
|
this._collectObserveTargets("", targets);
|
|
@@ -3803,8 +3938,11 @@ var Graph = class _Graph {
|
|
|
3803
3938
|
} else if (minimal) {
|
|
3804
3939
|
if (t === DIRTY) result.dirtyCount++;
|
|
3805
3940
|
else if (t === RESOLVED) result.resolvedCount++;
|
|
3806
|
-
else if (t === COMPLETE && !
|
|
3807
|
-
else if (t === ERROR)
|
|
3941
|
+
else if (t === COMPLETE && !nodeErrored.has(path)) result.anyCompletedCleanly = true;
|
|
3942
|
+
else if (t === ERROR) {
|
|
3943
|
+
result.anyErrored = true;
|
|
3944
|
+
nodeErrored.add(path);
|
|
3945
|
+
}
|
|
3808
3946
|
} else if (t === DIRTY) {
|
|
3809
3947
|
result.dirtyCount++;
|
|
3810
3948
|
result.events.push({ type: "dirty", path, ...base });
|
|
@@ -3812,10 +3950,11 @@ var Graph = class _Graph {
|
|
|
3812
3950
|
result.resolvedCount++;
|
|
3813
3951
|
result.events.push({ type: "resolved", path, ...base });
|
|
3814
3952
|
} else if (t === COMPLETE) {
|
|
3815
|
-
if (!
|
|
3953
|
+
if (!nodeErrored.has(path)) result.anyCompletedCleanly = true;
|
|
3816
3954
|
result.events.push({ type: "complete", path, ...base });
|
|
3817
3955
|
} else if (t === ERROR) {
|
|
3818
|
-
result.
|
|
3956
|
+
result.anyErrored = true;
|
|
3957
|
+
nodeErrored.add(path);
|
|
3819
3958
|
result.events.push({ type: "error", path, data: m[1], ...base });
|
|
3820
3959
|
}
|
|
3821
3960
|
}
|
|
@@ -3835,11 +3974,14 @@ var Graph = class _Graph {
|
|
|
3835
3974
|
get events() {
|
|
3836
3975
|
return result.events;
|
|
3837
3976
|
},
|
|
3838
|
-
get
|
|
3839
|
-
return result.
|
|
3977
|
+
get anyCompletedCleanly() {
|
|
3978
|
+
return result.anyCompletedCleanly;
|
|
3979
|
+
},
|
|
3980
|
+
get anyErrored() {
|
|
3981
|
+
return result.anyErrored;
|
|
3840
3982
|
},
|
|
3841
|
-
get
|
|
3842
|
-
return result.
|
|
3983
|
+
get completedWithoutErrors() {
|
|
3984
|
+
return result.anyCompletedCleanly && !result.anyErrored;
|
|
3843
3985
|
},
|
|
3844
3986
|
dispose() {
|
|
3845
3987
|
for (const u of unsubs) u();
|
|
@@ -3871,8 +4013,8 @@ var Graph = class _Graph {
|
|
|
3871
4013
|
dirtyCount: 0,
|
|
3872
4014
|
resolvedCount: 0,
|
|
3873
4015
|
events: [],
|
|
3874
|
-
|
|
3875
|
-
|
|
4016
|
+
anyCompletedCleanly: false,
|
|
4017
|
+
anyErrored: false
|
|
3876
4018
|
};
|
|
3877
4019
|
const target = this.resolve(path);
|
|
3878
4020
|
let batchSeq = 0;
|
|
@@ -3891,10 +4033,10 @@ var Graph = class _Graph {
|
|
|
3891
4033
|
acc.resolvedCount++;
|
|
3892
4034
|
acc.events.push({ type: "resolved", path, ...base });
|
|
3893
4035
|
} else if (t === COMPLETE) {
|
|
3894
|
-
if (!acc.
|
|
4036
|
+
if (!acc.anyErrored) acc.anyCompletedCleanly = true;
|
|
3895
4037
|
acc.events.push({ type: "complete", path, ...base });
|
|
3896
4038
|
} else if (t === ERROR) {
|
|
3897
|
-
acc.
|
|
4039
|
+
acc.anyErrored = true;
|
|
3898
4040
|
acc.events.push({ type: "error", path, data: m[1], ...base });
|
|
3899
4041
|
}
|
|
3900
4042
|
}
|
|
@@ -3912,11 +4054,14 @@ var Graph = class _Graph {
|
|
|
3912
4054
|
get events() {
|
|
3913
4055
|
return acc.events;
|
|
3914
4056
|
},
|
|
3915
|
-
get
|
|
3916
|
-
return acc.
|
|
4057
|
+
get anyCompletedCleanly() {
|
|
4058
|
+
return acc.anyCompletedCleanly;
|
|
3917
4059
|
},
|
|
3918
|
-
get
|
|
3919
|
-
return acc.
|
|
4060
|
+
get anyErrored() {
|
|
4061
|
+
return acc.anyErrored;
|
|
4062
|
+
},
|
|
4063
|
+
get completedWithoutErrors() {
|
|
4064
|
+
return acc.anyCompletedCleanly && !acc.anyErrored;
|
|
3920
4065
|
},
|
|
3921
4066
|
dispose() {
|
|
3922
4067
|
unsub();
|
|
@@ -3937,9 +4082,10 @@ var Graph = class _Graph {
|
|
|
3937
4082
|
dirtyCount: 0,
|
|
3938
4083
|
resolvedCount: 0,
|
|
3939
4084
|
events: [],
|
|
3940
|
-
|
|
3941
|
-
|
|
4085
|
+
anyCompletedCleanly: false,
|
|
4086
|
+
anyErrored: false
|
|
3942
4087
|
};
|
|
4088
|
+
const nodeErrored = /* @__PURE__ */ new Set();
|
|
3943
4089
|
const targets = [];
|
|
3944
4090
|
this._collectObserveTargets("", targets);
|
|
3945
4091
|
targets.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
@@ -3961,10 +4107,11 @@ var Graph = class _Graph {
|
|
|
3961
4107
|
acc.resolvedCount++;
|
|
3962
4108
|
acc.events.push({ type: "resolved", path, ...base });
|
|
3963
4109
|
} else if (t === COMPLETE) {
|
|
3964
|
-
if (!
|
|
4110
|
+
if (!nodeErrored.has(path)) acc.anyCompletedCleanly = true;
|
|
3965
4111
|
acc.events.push({ type: "complete", path, ...base });
|
|
3966
4112
|
} else if (t === ERROR) {
|
|
3967
|
-
acc.
|
|
4113
|
+
acc.anyErrored = true;
|
|
4114
|
+
nodeErrored.add(path);
|
|
3968
4115
|
acc.events.push({ type: "error", path, data: m[1], ...base });
|
|
3969
4116
|
}
|
|
3970
4117
|
}
|
|
@@ -3983,11 +4130,14 @@ var Graph = class _Graph {
|
|
|
3983
4130
|
get events() {
|
|
3984
4131
|
return acc.events;
|
|
3985
4132
|
},
|
|
3986
|
-
get
|
|
3987
|
-
return acc.
|
|
4133
|
+
get anyCompletedCleanly() {
|
|
4134
|
+
return acc.anyCompletedCleanly;
|
|
4135
|
+
},
|
|
4136
|
+
get anyErrored() {
|
|
4137
|
+
return acc.anyErrored;
|
|
3988
4138
|
},
|
|
3989
|
-
get
|
|
3990
|
-
return acc.
|
|
4139
|
+
get completedWithoutErrors() {
|
|
4140
|
+
return acc.anyCompletedCleanly && !acc.anyErrored;
|
|
3991
4141
|
},
|
|
3992
4142
|
dispose() {
|
|
3993
4143
|
for (const u of unsubs) u();
|
|
@@ -4313,8 +4463,9 @@ var Graph = class _Graph {
|
|
|
4313
4463
|
/**
|
|
4314
4464
|
* Debounced persistence wired to graph-wide observe stream (spec §3.8 auto-checkpoint).
|
|
4315
4465
|
*
|
|
4316
|
-
* Checkpoint trigger uses {@link messageTier}: only batches containing tier >=
|
|
4317
|
-
* schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1 control
|
|
4466
|
+
* Checkpoint trigger uses {@link messageTier}: only batches containing tier >= 3 messages
|
|
4467
|
+
* schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1/2 control
|
|
4468
|
+
* waves (`START`/`DIRTY`/`INVALIDATE`/`PAUSE`/`RESUME`).
|
|
4318
4469
|
*/
|
|
4319
4470
|
autoCheckpoint(adapter, options = {}) {
|
|
4320
4471
|
const debounceMs = Math.max(0, options.debounceMs ?? 500);
|
|
@@ -4361,7 +4512,7 @@ var Graph = class _Graph {
|
|
|
4361
4512
|
timer = setTimeout(flush, debounceMs);
|
|
4362
4513
|
};
|
|
4363
4514
|
const off = this.observe().subscribe((path, messages) => {
|
|
4364
|
-
const triggeredByTier = messages.some((m) => messageTier(m[0]) >=
|
|
4515
|
+
const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 3);
|
|
4365
4516
|
if (!triggeredByTier) return;
|
|
4366
4517
|
if (options.filter) {
|
|
4367
4518
|
const nd = this.resolve(path);
|
|
@@ -4604,6 +4755,15 @@ function reactiveLog(initial, options = {}) {
|
|
|
4604
4755
|
return bundle;
|
|
4605
4756
|
}
|
|
4606
4757
|
|
|
4758
|
+
// src/patterns/_internal.ts
|
|
4759
|
+
function domainMeta(domain, kind, extra) {
|
|
4760
|
+
return {
|
|
4761
|
+
[domain]: true,
|
|
4762
|
+
[`${domain}_type`]: kind,
|
|
4763
|
+
...extra ?? {}
|
|
4764
|
+
};
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4607
4767
|
// src/patterns/cqrs.ts
|
|
4608
4768
|
var COMMAND_GUARD = policy((allow, deny) => {
|
|
4609
4769
|
allow("write");
|
|
@@ -4621,11 +4781,7 @@ var EVENT_GUARD = policy((allow, deny) => {
|
|
|
4621
4781
|
deny("write");
|
|
4622
4782
|
});
|
|
4623
4783
|
function cqrsMeta(kind, extra) {
|
|
4624
|
-
return
|
|
4625
|
-
}
|
|
4626
|
-
function keepalive(n) {
|
|
4627
|
-
return n.subscribe(() => {
|
|
4628
|
-
});
|
|
4784
|
+
return domainMeta("cqrs", kind, extra);
|
|
4629
4785
|
}
|
|
4630
4786
|
var CqrsGraph = class extends Graph {
|
|
4631
4787
|
_eventLogs = /* @__PURE__ */ new Map();
|