@graphrefly/graphrefly 0.18.0 → 0.19.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-TNKODJ6E.js → chunk-AHRKWMNI.js} +7 -3
- package/dist/{chunk-TNKODJ6E.js.map → chunk-AHRKWMNI.js.map} +1 -1
- package/dist/{chunk-76YPZQTW.js → chunk-BER7UYLM.js} +27 -26
- package/dist/chunk-BER7UYLM.js.map +1 -0
- package/dist/{chunk-F6ORUNO7.js → chunk-IRZAGZUB.js} +34 -2
- package/dist/{chunk-F6ORUNO7.js.map → chunk-IRZAGZUB.js.map} +1 -1
- package/dist/{chunk-LB3RYLSC.js → chunk-JC2SN46B.js} +197 -42
- package/dist/chunk-JC2SN46B.js.map +1 -0
- package/dist/{chunk-KJGUP35I.js → chunk-OO5QOAXI.js} +4 -4
- package/dist/{chunk-UVWEKTYC.js → chunk-UW77D7SP.js} +3 -3
- package/dist/{chunk-J7S54G7I.js → chunk-XUOY3YKN.js} +7 -2
- package/dist/chunk-XUOY3YKN.js.map +1 -0
- package/dist/chunk-YLR5JUJZ.js +111 -0
- package/dist/chunk-YLR5JUJZ.js.map +1 -0
- package/dist/{chunk-BV3TPSBK.js → chunk-YXR3WW3Q.js} +740 -755
- package/dist/chunk-YXR3WW3Q.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +931 -784
- 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 +651 -664
- 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 +686 -672
- 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 +5 -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-B80mMeuf.d.ts → index-BBUYZfJ1.d.cts} +122 -76
- package/dist/{index-D_tUMcpz.d.cts → index-Bjh5C1Tp.d.cts} +37 -32
- package/dist/{index-B43mC7uY.d.cts → index-BjtlNirP.d.cts} +3 -3
- package/dist/{index-7WnwgjMu.d.ts → index-BnkMgNNa.d.ts} +37 -32
- package/dist/{index-CEDaJaYE.d.ts → index-CgSiUouz.d.ts} +3 -3
- package/dist/{index-EmzYk-TG.d.cts → index-CvKzv0AW.d.ts} +122 -76
- 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 +5920 -5572
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +595 -399
- package/dist/index.d.ts +595 -399
- package/dist/index.js +4357 -4063
- 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 +1 -1
- package/dist/chunk-76YPZQTW.js.map +0 -1
- package/dist/chunk-BV3TPSBK.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-LB3RYLSC.js.map +0 -1
- /package/dist/{chunk-KJGUP35I.js.map → chunk-OO5QOAXI.js.map} +0 -0
- /package/dist/{chunk-UVWEKTYC.js.map → chunk-UW77D7SP.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
|
-
let sinkMessages = messages;
|
|
869
|
-
if (this._terminal && !this._opts.resubscribable) {
|
|
870
|
-
const terminalPassthrough = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
871
|
-
if (terminalPassthrough.length === 0) return;
|
|
872
|
-
lifecycleMessages = terminalPassthrough;
|
|
873
|
-
sinkMessages = terminalPassthrough;
|
|
874
|
-
}
|
|
875
|
-
this._handleLocalLifecycle(lifecycleMessages);
|
|
876
|
-
if (this._canSkipDirty()) {
|
|
877
|
-
let hasPhase2 = false;
|
|
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() };
|
|
897
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();
|
|
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
|
-
|
|
1122
|
-
|
|
1156
|
+
_upInternal(messages) {
|
|
1157
|
+
if (!this._hasDeps) return;
|
|
1158
|
+
for (const dep of this._deps) {
|
|
1159
|
+
dep.up?.(messages, { internal: true });
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
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;
|
|
1171
|
+
}
|
|
1172
|
+
if (this._fn) {
|
|
1173
|
+
this._runFn();
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
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";
|
|
1123
1188
|
}
|
|
1124
1189
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
+
);
|
|
1128
1216
|
}
|
|
1129
|
-
this.
|
|
1130
|
-
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1131
|
-
this._depDirtyMask.reset();
|
|
1132
|
-
this._depSettledMask.reset();
|
|
1217
|
+
if (this._fn && this._onMessage && !this._terminal && this._lastDepValues === depValuesBefore) {
|
|
1133
1218
|
this._runFn();
|
|
1134
1219
|
}
|
|
1135
1220
|
}
|
|
1136
|
-
|
|
1137
|
-
if (this.
|
|
1138
|
-
|
|
1221
|
+
_disconnectUpstream() {
|
|
1222
|
+
if (this._upstreamUnsubs.length === 0) return;
|
|
1223
|
+
for (const unsub of this._upstreamUnsubs.splice(0)) {
|
|
1224
|
+
unsub();
|
|
1139
1225
|
}
|
|
1226
|
+
this._depDirtyMask.reset();
|
|
1227
|
+
this._depSettledMask.reset();
|
|
1228
|
+
this._depCompleteMask.reset();
|
|
1140
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
|
-
this._status = "settled";
|
|
1207
|
-
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1208
|
-
this._connecting = true;
|
|
1209
|
-
try {
|
|
1210
|
-
for (let i = 0; i < this._deps.length; i += 1) {
|
|
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]]);
|
|
1218
1308
|
}
|
|
1219
|
-
|
|
1309
|
+
}
|
|
1310
|
+
_onDepSettled(index) {
|
|
1311
|
+
if (!this._depDirtyMask.has(index)) {
|
|
1312
|
+
this._onDepDirty(index);
|
|
1313
|
+
}
|
|
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;
|
|
@@ -2080,239 +2214,56 @@ var GraphReflyGuardImpl = class {
|
|
|
2080
2214
|
if (req != null) {
|
|
2081
2215
|
req[ACTOR_KEY] = actor;
|
|
2082
2216
|
}
|
|
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);
|
|
2217
|
+
return true;
|
|
2227
2218
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2219
|
+
};
|
|
2220
|
+
function GraphReflyGuard(extractor) {
|
|
2221
|
+
return new GraphReflyGuardImpl(extractor ?? fromJwtPayload());
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// src/compat/nestjs/module.ts
|
|
2225
|
+
var import_common2 = require("@nestjs/common");
|
|
2226
|
+
var import_core2 = require("@nestjs/core");
|
|
2227
|
+
|
|
2228
|
+
// src/core/dynamic-node.ts
|
|
2229
|
+
var MAX_RERUN = 16;
|
|
2230
|
+
var DynamicNodeImpl = class extends NodeBase {
|
|
2231
|
+
_fn;
|
|
2232
|
+
_autoComplete;
|
|
2233
|
+
// Dynamic deps tracking
|
|
2234
|
+
/** @internal Read by `describeNode`. */
|
|
2235
|
+
_deps = [];
|
|
2236
|
+
_depUnsubs = [];
|
|
2237
|
+
_depIndexMap = /* @__PURE__ */ new Map();
|
|
2238
|
+
_depDirtyBits = /* @__PURE__ */ new Set();
|
|
2239
|
+
_depSettledBits = /* @__PURE__ */ new Set();
|
|
2240
|
+
_depCompleteBits = /* @__PURE__ */ new Set();
|
|
2241
|
+
// Execution state
|
|
2242
|
+
_running = false;
|
|
2243
|
+
_rewiring = false;
|
|
2244
|
+
_bufferedDepMessages = [];
|
|
2245
|
+
_trackedValues = /* @__PURE__ */ new Map();
|
|
2246
|
+
_rerunCount = 0;
|
|
2247
|
+
constructor(fn, opts) {
|
|
2248
|
+
super(opts);
|
|
2249
|
+
this._fn = fn;
|
|
2250
|
+
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
2251
|
+
this.down = this.down.bind(this);
|
|
2252
|
+
this.up = this.up.bind(this);
|
|
2258
2253
|
}
|
|
2259
|
-
|
|
2260
|
-
return
|
|
2254
|
+
_createMetaNode(key, initialValue, opts) {
|
|
2255
|
+
return node({
|
|
2256
|
+
initial: initialValue,
|
|
2257
|
+
name: `${opts.name ?? "dynamicNode"}:meta:${key}`,
|
|
2258
|
+
describeKind: "state",
|
|
2259
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
2260
|
+
});
|
|
2261
2261
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
if (!this._guard(actor, "observe")) {
|
|
2266
|
-
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
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();
|
|
2289
|
-
}
|
|
2290
|
-
let removed = false;
|
|
2291
|
-
return () => {
|
|
2292
|
-
if (removed) return;
|
|
2293
|
-
removed = true;
|
|
2294
|
-
this._sinkCount -= 1;
|
|
2295
|
-
if (this._singleDepSinks.has(sink)) {
|
|
2296
|
-
this._singleDepSinkCount -= 1;
|
|
2297
|
-
this._singleDepSinks.delete(sink);
|
|
2298
|
-
}
|
|
2299
|
-
if (this._sinks == null) return;
|
|
2300
|
-
if (typeof this._sinks === "function") {
|
|
2301
|
-
if (this._sinks === sink) this._sinks = null;
|
|
2302
|
-
} else {
|
|
2303
|
-
this._sinks.delete(sink);
|
|
2304
|
-
if (this._sinks.size === 1) {
|
|
2305
|
-
const [only] = this._sinks;
|
|
2306
|
-
this._sinks = only;
|
|
2307
|
-
} else if (this._sinks.size === 0) {
|
|
2308
|
-
this._sinks = null;
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
if (this._sinks == null) {
|
|
2312
|
-
this._disconnect();
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
2262
|
+
/** Versioning not supported on DynamicNodeImpl (override base). */
|
|
2263
|
+
get v() {
|
|
2264
|
+
return void 0;
|
|
2315
2265
|
}
|
|
2266
|
+
// --- Up / unsubscribe ---
|
|
2316
2267
|
up(messages, options) {
|
|
2317
2268
|
if (this._deps.length === 0) return;
|
|
2318
2269
|
if (!options?.internal && this._guard != null) {
|
|
@@ -2320,221 +2271,227 @@ var DynamicNodeImpl = class {
|
|
|
2320
2271
|
if (!this._guard(actor, "write")) {
|
|
2321
2272
|
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
2322
2273
|
}
|
|
2323
|
-
this.
|
|
2274
|
+
this._recordMutation(actor);
|
|
2324
2275
|
}
|
|
2325
2276
|
for (const dep of this._deps) {
|
|
2326
2277
|
dep.up?.(messages, options);
|
|
2327
2278
|
}
|
|
2328
2279
|
}
|
|
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
|
-
}
|
|
2280
|
+
_upInternal(messages) {
|
|
2281
|
+
for (const dep of this._deps) {
|
|
2282
|
+
dep.up?.(messages, { internal: true });
|
|
2385
2283
|
}
|
|
2386
2284
|
}
|
|
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]]);
|
|
2285
|
+
unsubscribe() {
|
|
2286
|
+
this._disconnect();
|
|
2404
2287
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
this._connected = true;
|
|
2408
|
-
this._status = "settled";
|
|
2409
|
-
this._dirtyBits.clear();
|
|
2410
|
-
this._settledBits.clear();
|
|
2411
|
-
this._completeBits.clear();
|
|
2288
|
+
// --- Activation hooks ---
|
|
2289
|
+
_onActivate() {
|
|
2412
2290
|
this._runFn();
|
|
2413
2291
|
}
|
|
2292
|
+
_doDeactivate() {
|
|
2293
|
+
this._disconnect();
|
|
2294
|
+
}
|
|
2414
2295
|
_disconnect() {
|
|
2415
|
-
if (!this._connected) return;
|
|
2416
2296
|
for (const unsub of this._depUnsubs) unsub();
|
|
2417
2297
|
this._depUnsubs = [];
|
|
2418
2298
|
this._deps = [];
|
|
2419
2299
|
this._depIndexMap.clear();
|
|
2420
|
-
this.
|
|
2421
|
-
this.
|
|
2422
|
-
this.
|
|
2423
|
-
this.
|
|
2300
|
+
this._depDirtyBits.clear();
|
|
2301
|
+
this._depSettledBits.clear();
|
|
2302
|
+
this._depCompleteBits.clear();
|
|
2303
|
+
this._cached = NO_VALUE;
|
|
2424
2304
|
this._status = "disconnected";
|
|
2425
2305
|
}
|
|
2306
|
+
// --- Fn execution with rewire buffer ---
|
|
2426
2307
|
_runFn() {
|
|
2427
2308
|
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
|
-
};
|
|
2309
|
+
if (this._running) return;
|
|
2310
|
+
this._running = true;
|
|
2311
|
+
this._rerunCount = 0;
|
|
2312
|
+
let result;
|
|
2438
2313
|
try {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2314
|
+
for (; ; ) {
|
|
2315
|
+
const trackedDeps = [];
|
|
2316
|
+
const trackedValuesMap = /* @__PURE__ */ new Map();
|
|
2317
|
+
const trackedSet = /* @__PURE__ */ new Set();
|
|
2318
|
+
const get = (dep) => {
|
|
2319
|
+
if (!trackedSet.has(dep)) {
|
|
2320
|
+
trackedSet.add(dep);
|
|
2321
|
+
trackedDeps.push(dep);
|
|
2322
|
+
trackedValuesMap.set(dep, dep.get());
|
|
2323
|
+
}
|
|
2324
|
+
return dep.get();
|
|
2325
|
+
};
|
|
2326
|
+
this._trackedValues = trackedValuesMap;
|
|
2327
|
+
const depValues = [];
|
|
2328
|
+
for (const dep of this._deps) depValues.push(dep.get());
|
|
2329
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
2330
|
+
try {
|
|
2331
|
+
result = this._fn(get);
|
|
2332
|
+
} catch (err) {
|
|
2333
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2334
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, {
|
|
2335
|
+
cause: err
|
|
2336
|
+
});
|
|
2337
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
this._rewiring = true;
|
|
2341
|
+
this._bufferedDepMessages = [];
|
|
2342
|
+
try {
|
|
2343
|
+
this._rewire(trackedDeps);
|
|
2344
|
+
} finally {
|
|
2345
|
+
this._rewiring = false;
|
|
2346
|
+
}
|
|
2347
|
+
let needsRerun = false;
|
|
2348
|
+
for (const entry of this._bufferedDepMessages) {
|
|
2349
|
+
for (const msg of entry.msgs) {
|
|
2350
|
+
if (msg[0] === DATA) {
|
|
2351
|
+
const dep = this._deps[entry.index];
|
|
2352
|
+
const trackedValue = dep != null ? this._trackedValues.get(dep) : void 0;
|
|
2353
|
+
const actualValue = msg[1];
|
|
2354
|
+
if (!this._equals(trackedValue, actualValue)) {
|
|
2355
|
+
needsRerun = true;
|
|
2356
|
+
break;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
if (needsRerun) break;
|
|
2361
|
+
}
|
|
2362
|
+
if (needsRerun) {
|
|
2363
|
+
this._rerunCount += 1;
|
|
2364
|
+
if (this._rerunCount > MAX_RERUN) {
|
|
2365
|
+
this._bufferedDepMessages = [];
|
|
2366
|
+
this._downInternal([
|
|
2367
|
+
[
|
|
2368
|
+
ERROR,
|
|
2369
|
+
new Error(
|
|
2370
|
+
`dynamicNode "${this.name ?? "anonymous"}": rewire did not stabilize within ${MAX_RERUN} iterations`
|
|
2371
|
+
)
|
|
2372
|
+
]
|
|
2373
|
+
]);
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
this._bufferedDepMessages = [];
|
|
2377
|
+
continue;
|
|
2378
|
+
}
|
|
2379
|
+
const drain = this._bufferedDepMessages;
|
|
2380
|
+
this._bufferedDepMessages = [];
|
|
2381
|
+
for (const entry of drain) {
|
|
2382
|
+
for (const msg of entry.msgs) {
|
|
2383
|
+
this._updateMasksForMessage(entry.index, msg);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
this._depDirtyBits.clear();
|
|
2387
|
+
this._depSettledBits.clear();
|
|
2388
|
+
break;
|
|
2389
|
+
}
|
|
2390
|
+
} finally {
|
|
2391
|
+
this._running = false;
|
|
2450
2392
|
}
|
|
2393
|
+
this._downAutoValue(result);
|
|
2451
2394
|
}
|
|
2452
2395
|
_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
|
-
}
|
|
2396
|
+
const oldMap = this._depIndexMap;
|
|
2397
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
2398
|
+
const newUnsubs = [];
|
|
2399
|
+
for (let i = 0; i < newDeps.length; i++) {
|
|
2400
|
+
const dep = newDeps[i];
|
|
2401
|
+
newMap.set(dep, i);
|
|
2402
|
+
const oldIdx = oldMap.get(dep);
|
|
2403
|
+
if (oldIdx !== void 0) {
|
|
2404
|
+
newUnsubs.push(this._depUnsubs[oldIdx]);
|
|
2405
|
+
this._depUnsubs[oldIdx] = () => {
|
|
2406
|
+
};
|
|
2407
|
+
} else {
|
|
2408
|
+
const idx = i;
|
|
2409
|
+
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
2410
|
+
newUnsubs.push(unsub);
|
|
2471
2411
|
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2412
|
+
}
|
|
2413
|
+
for (const [dep, oldIdx] of oldMap) {
|
|
2414
|
+
if (!newMap.has(dep)) {
|
|
2415
|
+
this._depUnsubs[oldIdx]();
|
|
2476
2416
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2417
|
+
}
|
|
2418
|
+
this._deps = newDeps;
|
|
2419
|
+
this._depUnsubs = newUnsubs;
|
|
2420
|
+
this._depIndexMap = newMap;
|
|
2421
|
+
this._depDirtyBits.clear();
|
|
2422
|
+
this._depSettledBits.clear();
|
|
2423
|
+
const newCompleteBits = /* @__PURE__ */ new Set();
|
|
2424
|
+
for (const oldIdx of this._depCompleteBits) {
|
|
2425
|
+
for (const [dep, idx] of oldMap) {
|
|
2426
|
+
if (idx === oldIdx && newMap.has(dep)) {
|
|
2486
2427
|
newCompleteBits.add(newMap.get(dep));
|
|
2428
|
+
break;
|
|
2487
2429
|
}
|
|
2488
2430
|
}
|
|
2489
|
-
this._completeBits = newCompleteBits;
|
|
2490
|
-
} finally {
|
|
2491
|
-
this._rewiring = false;
|
|
2492
2431
|
}
|
|
2432
|
+
this._depCompleteBits = newCompleteBits;
|
|
2493
2433
|
}
|
|
2434
|
+
// --- Dep message handling ---
|
|
2494
2435
|
_handleDepMessages(index, messages) {
|
|
2495
|
-
if (this._rewiring)
|
|
2436
|
+
if (this._rewiring) {
|
|
2437
|
+
this._bufferedDepMessages.push({ index, msgs: messages });
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2496
2440
|
for (const msg of messages) {
|
|
2497
|
-
this.
|
|
2441
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
2498
2442
|
const t = msg[0];
|
|
2499
2443
|
if (this._onMessage) {
|
|
2500
2444
|
try {
|
|
2501
2445
|
if (this._onMessage(msg, index, this._actions)) continue;
|
|
2502
2446
|
} catch (err) {
|
|
2503
|
-
|
|
2447
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2448
|
+
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
2449
|
+
cause: err
|
|
2450
|
+
});
|
|
2451
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
2504
2452
|
return;
|
|
2505
2453
|
}
|
|
2506
2454
|
}
|
|
2455
|
+
if (messageTier(t) < 1) continue;
|
|
2507
2456
|
if (t === DIRTY) {
|
|
2508
|
-
this.
|
|
2509
|
-
this.
|
|
2510
|
-
|
|
2511
|
-
|
|
2457
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
2458
|
+
this._depDirtyBits.add(index);
|
|
2459
|
+
this._depSettledBits.delete(index);
|
|
2460
|
+
if (wasEmpty) {
|
|
2461
|
+
this._downInternal([[DIRTY]]);
|
|
2512
2462
|
}
|
|
2513
2463
|
continue;
|
|
2514
2464
|
}
|
|
2515
2465
|
if (t === DATA || t === RESOLVED) {
|
|
2516
|
-
if (!this.
|
|
2517
|
-
this.
|
|
2518
|
-
|
|
2466
|
+
if (!this._depDirtyBits.has(index)) {
|
|
2467
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
2468
|
+
this._depDirtyBits.add(index);
|
|
2469
|
+
if (wasEmpty) {
|
|
2470
|
+
this._downInternal([[DIRTY]]);
|
|
2471
|
+
}
|
|
2519
2472
|
}
|
|
2520
|
-
this.
|
|
2473
|
+
this._depSettledBits.add(index);
|
|
2521
2474
|
if (this._allDirtySettled()) {
|
|
2522
|
-
this.
|
|
2523
|
-
this.
|
|
2524
|
-
this.
|
|
2475
|
+
this._depDirtyBits.clear();
|
|
2476
|
+
this._depSettledBits.clear();
|
|
2477
|
+
if (!this._running) {
|
|
2478
|
+
if (this._depValuesDifferFromTracked()) {
|
|
2479
|
+
this._runFn();
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2525
2482
|
}
|
|
2526
2483
|
continue;
|
|
2527
2484
|
}
|
|
2528
2485
|
if (t === COMPLETE) {
|
|
2529
|
-
this.
|
|
2530
|
-
this.
|
|
2531
|
-
this.
|
|
2486
|
+
this._depCompleteBits.add(index);
|
|
2487
|
+
this._depDirtyBits.delete(index);
|
|
2488
|
+
this._depSettledBits.delete(index);
|
|
2532
2489
|
if (this._allDirtySettled()) {
|
|
2533
|
-
this.
|
|
2534
|
-
this.
|
|
2535
|
-
this._runFn();
|
|
2490
|
+
this._depDirtyBits.clear();
|
|
2491
|
+
this._depSettledBits.clear();
|
|
2492
|
+
if (!this._running) this._runFn();
|
|
2536
2493
|
}
|
|
2537
|
-
if (this._autoComplete && this.
|
|
2494
|
+
if (this._autoComplete && this._depCompleteBits.size >= this._deps.length && this._deps.length > 0) {
|
|
2538
2495
|
this._downInternal([[COMPLETE]]);
|
|
2539
2496
|
}
|
|
2540
2497
|
continue;
|
|
@@ -2550,13 +2507,46 @@ var DynamicNodeImpl = class {
|
|
|
2550
2507
|
this._downInternal([msg]);
|
|
2551
2508
|
}
|
|
2552
2509
|
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Update dep masks for a message without triggering `_runFn` — used
|
|
2512
|
+
* during post-rewire drain so the wave state is consistent with the
|
|
2513
|
+
* buffered activation cascade without recursing.
|
|
2514
|
+
*/
|
|
2515
|
+
_updateMasksForMessage(index, msg) {
|
|
2516
|
+
const t = msg[0];
|
|
2517
|
+
if (t === DIRTY) {
|
|
2518
|
+
this._depDirtyBits.add(index);
|
|
2519
|
+
this._depSettledBits.delete(index);
|
|
2520
|
+
} else if (t === DATA || t === RESOLVED) {
|
|
2521
|
+
this._depDirtyBits.add(index);
|
|
2522
|
+
this._depSettledBits.add(index);
|
|
2523
|
+
} else if (t === COMPLETE) {
|
|
2524
|
+
this._depCompleteBits.add(index);
|
|
2525
|
+
this._depDirtyBits.delete(index);
|
|
2526
|
+
this._depSettledBits.delete(index);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2553
2529
|
_allDirtySettled() {
|
|
2554
|
-
if (this.
|
|
2555
|
-
for (const idx of this.
|
|
2556
|
-
if (!this.
|
|
2530
|
+
if (this._depDirtyBits.size === 0) return false;
|
|
2531
|
+
for (const idx of this._depDirtyBits) {
|
|
2532
|
+
if (!this._depSettledBits.has(idx)) return false;
|
|
2557
2533
|
}
|
|
2558
2534
|
return true;
|
|
2559
2535
|
}
|
|
2536
|
+
/**
|
|
2537
|
+
* True if any current dep value differs from what the last `_runFn`
|
|
2538
|
+
* saw via `get()`. Used to suppress redundant re-runs when deferred
|
|
2539
|
+
* handshake messages arrive after `_rewire` for a dep whose value
|
|
2540
|
+
* already matches `_trackedValues`.
|
|
2541
|
+
*/
|
|
2542
|
+
_depValuesDifferFromTracked() {
|
|
2543
|
+
for (const dep of this._deps) {
|
|
2544
|
+
const current = dep.get();
|
|
2545
|
+
const tracked = this._trackedValues.get(dep);
|
|
2546
|
+
if (!this._equals(current, tracked)) return true;
|
|
2547
|
+
}
|
|
2548
|
+
return false;
|
|
2549
|
+
}
|
|
2560
2550
|
};
|
|
2561
2551
|
|
|
2562
2552
|
// src/core/meta.ts
|
|
@@ -2626,6 +2616,10 @@ function describeNode(node2, includeFields) {
|
|
|
2626
2616
|
out.name = node2.name;
|
|
2627
2617
|
}
|
|
2628
2618
|
if (all || includeFields.has("value")) {
|
|
2619
|
+
const isSentinel = node2 instanceof NodeImpl && node2._cached === NO_VALUE || node2 instanceof DynamicNodeImpl && node2._cached === NO_VALUE;
|
|
2620
|
+
if (isSentinel) {
|
|
2621
|
+
out.sentinel = true;
|
|
2622
|
+
}
|
|
2629
2623
|
try {
|
|
2630
2624
|
out.value = node2.get();
|
|
2631
2625
|
} catch {
|
|
@@ -2652,6 +2646,129 @@ function describeNode(node2, includeFields) {
|
|
|
2652
2646
|
return out;
|
|
2653
2647
|
}
|
|
2654
2648
|
|
|
2649
|
+
// src/graph/sizeof.ts
|
|
2650
|
+
var OVERHEAD = {
|
|
2651
|
+
object: 56,
|
|
2652
|
+
array: 64,
|
|
2653
|
+
string: 40,
|
|
2654
|
+
// header; content added separately
|
|
2655
|
+
number: 8,
|
|
2656
|
+
boolean: 4,
|
|
2657
|
+
null: 0,
|
|
2658
|
+
undefined: 0,
|
|
2659
|
+
symbol: 40,
|
|
2660
|
+
bigint: 16,
|
|
2661
|
+
function: 120,
|
|
2662
|
+
map: 72,
|
|
2663
|
+
set: 72,
|
|
2664
|
+
mapEntry: 40,
|
|
2665
|
+
setEntry: 24
|
|
2666
|
+
};
|
|
2667
|
+
function sizeof(value) {
|
|
2668
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
2669
|
+
return _sizeof(value, seen);
|
|
2670
|
+
}
|
|
2671
|
+
function _sizeof(value, seen) {
|
|
2672
|
+
if (value == null) return 0;
|
|
2673
|
+
const t = typeof value;
|
|
2674
|
+
switch (t) {
|
|
2675
|
+
case "number":
|
|
2676
|
+
return OVERHEAD.number;
|
|
2677
|
+
case "boolean":
|
|
2678
|
+
return OVERHEAD.boolean;
|
|
2679
|
+
case "string":
|
|
2680
|
+
return OVERHEAD.string + value.length * 2;
|
|
2681
|
+
// UTF-16
|
|
2682
|
+
case "bigint":
|
|
2683
|
+
return OVERHEAD.bigint;
|
|
2684
|
+
case "symbol":
|
|
2685
|
+
return OVERHEAD.symbol;
|
|
2686
|
+
case "function":
|
|
2687
|
+
if (seen.has(value)) return 0;
|
|
2688
|
+
seen.add(value);
|
|
2689
|
+
return OVERHEAD.function;
|
|
2690
|
+
case "undefined":
|
|
2691
|
+
return 0;
|
|
2692
|
+
}
|
|
2693
|
+
const obj = value;
|
|
2694
|
+
if (seen.has(obj)) return 0;
|
|
2695
|
+
seen.add(obj);
|
|
2696
|
+
if (obj instanceof Map) {
|
|
2697
|
+
let size2 = OVERHEAD.map;
|
|
2698
|
+
for (const [k, v] of obj) {
|
|
2699
|
+
size2 += OVERHEAD.mapEntry + _sizeof(k, seen) + _sizeof(v, seen);
|
|
2700
|
+
}
|
|
2701
|
+
return size2;
|
|
2702
|
+
}
|
|
2703
|
+
if (obj instanceof Set) {
|
|
2704
|
+
let size2 = OVERHEAD.set;
|
|
2705
|
+
for (const v of obj) {
|
|
2706
|
+
size2 += OVERHEAD.setEntry + _sizeof(v, seen);
|
|
2707
|
+
}
|
|
2708
|
+
return size2;
|
|
2709
|
+
}
|
|
2710
|
+
if (Array.isArray(obj)) {
|
|
2711
|
+
let size2 = OVERHEAD.array + obj.length * 8;
|
|
2712
|
+
for (const item of obj) {
|
|
2713
|
+
size2 += _sizeof(item, seen);
|
|
2714
|
+
}
|
|
2715
|
+
return size2;
|
|
2716
|
+
}
|
|
2717
|
+
if (obj instanceof ArrayBuffer) return obj.byteLength;
|
|
2718
|
+
if (ArrayBuffer.isView(obj)) return obj.byteLength;
|
|
2719
|
+
let size = OVERHEAD.object;
|
|
2720
|
+
const keys = Object.keys(obj);
|
|
2721
|
+
for (const key of keys) {
|
|
2722
|
+
size += OVERHEAD.string + key.length * 2;
|
|
2723
|
+
size += _sizeof(obj[key], seen);
|
|
2724
|
+
}
|
|
2725
|
+
return size;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
// src/graph/profile.ts
|
|
2729
|
+
function graphProfile(graph, opts) {
|
|
2730
|
+
const topN = opts?.topN ?? 10;
|
|
2731
|
+
const desc = graph.describe({ detail: "standard" });
|
|
2732
|
+
const targets = [];
|
|
2733
|
+
if (typeof graph._collectObserveTargets === "function") {
|
|
2734
|
+
graph._collectObserveTargets("", targets);
|
|
2735
|
+
}
|
|
2736
|
+
const pathToNode = /* @__PURE__ */ new Map();
|
|
2737
|
+
for (const [p, n] of targets) {
|
|
2738
|
+
pathToNode.set(p, n);
|
|
2739
|
+
}
|
|
2740
|
+
const profiles = [];
|
|
2741
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
2742
|
+
const nd = pathToNode.get(path);
|
|
2743
|
+
const impl = nd instanceof NodeImpl ? nd : null;
|
|
2744
|
+
const valueSizeBytes = impl ? sizeof(impl.get()) : 0;
|
|
2745
|
+
const subscriberCount = impl ? impl._sinkCount : 0;
|
|
2746
|
+
const depCount = nodeDesc.deps?.length ?? 0;
|
|
2747
|
+
const isOrphanEffect = nodeDesc.type === "effect" && subscriberCount === 0;
|
|
2748
|
+
profiles.push({
|
|
2749
|
+
path,
|
|
2750
|
+
type: nodeDesc.type,
|
|
2751
|
+
status: nodeDesc.status ?? "unknown",
|
|
2752
|
+
valueSizeBytes,
|
|
2753
|
+
subscriberCount,
|
|
2754
|
+
depCount,
|
|
2755
|
+
isOrphanEffect
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
const totalValueSizeBytes = profiles.reduce((sum, p) => sum + p.valueSizeBytes, 0);
|
|
2759
|
+
const hotspots = [...profiles].sort((a, b) => b.valueSizeBytes - a.valueSizeBytes).slice(0, topN);
|
|
2760
|
+
const orphanEffects = profiles.filter((p) => p.isOrphanEffect);
|
|
2761
|
+
return {
|
|
2762
|
+
nodeCount: profiles.length,
|
|
2763
|
+
edgeCount: desc.edges.length,
|
|
2764
|
+
subgraphCount: desc.subgraphs.length,
|
|
2765
|
+
nodes: profiles,
|
|
2766
|
+
totalValueSizeBytes,
|
|
2767
|
+
hotspots,
|
|
2768
|
+
orphanEffects
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2655
2772
|
// src/graph/graph.ts
|
|
2656
2773
|
var PATH_SEP = "::";
|
|
2657
2774
|
var GRAPH_META_SEGMENT = "__meta__";
|
|
@@ -3564,6 +3681,16 @@ var Graph = class _Graph {
|
|
|
3564
3681
|
}
|
|
3565
3682
|
return out;
|
|
3566
3683
|
}
|
|
3684
|
+
/**
|
|
3685
|
+
* Snapshot-based resource profile: per-node stats, orphan effect detection,
|
|
3686
|
+
* memory hotspots. Zero runtime overhead — walks nodes on demand.
|
|
3687
|
+
*
|
|
3688
|
+
* @param opts - Optional `topN` for hotspot limit (default 10).
|
|
3689
|
+
* @returns Aggregate profile with per-node details, hotspots, and orphan effects.
|
|
3690
|
+
*/
|
|
3691
|
+
resourceProfile(opts) {
|
|
3692
|
+
return graphProfile(this, opts);
|
|
3693
|
+
}
|
|
3567
3694
|
_qualifyEdgeEndpoint(part, prefix) {
|
|
3568
3695
|
if (part.includes(PATH_SEP)) return part;
|
|
3569
3696
|
return prefix === "" ? part : `${prefix}${PATH_SEP}${part}`;
|
|
@@ -3665,8 +3792,8 @@ var Graph = class _Graph {
|
|
|
3665
3792
|
dirtyCount: 0,
|
|
3666
3793
|
resolvedCount: 0,
|
|
3667
3794
|
events: [],
|
|
3668
|
-
|
|
3669
|
-
|
|
3795
|
+
anyCompletedCleanly: false,
|
|
3796
|
+
anyErrored: false
|
|
3670
3797
|
};
|
|
3671
3798
|
let lastTriggerDepIndex;
|
|
3672
3799
|
let lastRunDepValues;
|
|
@@ -3710,8 +3837,8 @@ var Graph = class _Graph {
|
|
|
3710
3837
|
} else if (minimal) {
|
|
3711
3838
|
if (t === DIRTY) result.dirtyCount++;
|
|
3712
3839
|
else if (t === RESOLVED) result.resolvedCount++;
|
|
3713
|
-
else if (t === COMPLETE && !result.
|
|
3714
|
-
else if (t === ERROR) result.
|
|
3840
|
+
else if (t === COMPLETE && !result.anyErrored) result.anyCompletedCleanly = true;
|
|
3841
|
+
else if (t === ERROR) result.anyErrored = true;
|
|
3715
3842
|
} else if (t === DIRTY) {
|
|
3716
3843
|
result.dirtyCount++;
|
|
3717
3844
|
result.events.push({ type: "dirty", path, ...base });
|
|
@@ -3719,10 +3846,10 @@ var Graph = class _Graph {
|
|
|
3719
3846
|
result.resolvedCount++;
|
|
3720
3847
|
result.events.push({ type: "resolved", path, ...base, ...withCausal });
|
|
3721
3848
|
} else if (t === COMPLETE) {
|
|
3722
|
-
if (!result.
|
|
3849
|
+
if (!result.anyErrored) result.anyCompletedCleanly = true;
|
|
3723
3850
|
result.events.push({ type: "complete", path, ...base });
|
|
3724
3851
|
} else if (t === ERROR) {
|
|
3725
|
-
result.
|
|
3852
|
+
result.anyErrored = true;
|
|
3726
3853
|
result.events.push({ type: "error", path, data: m[1], ...base });
|
|
3727
3854
|
}
|
|
3728
3855
|
}
|
|
@@ -3742,11 +3869,14 @@ var Graph = class _Graph {
|
|
|
3742
3869
|
get events() {
|
|
3743
3870
|
return result.events;
|
|
3744
3871
|
},
|
|
3745
|
-
get
|
|
3746
|
-
return result.
|
|
3872
|
+
get anyCompletedCleanly() {
|
|
3873
|
+
return result.anyCompletedCleanly;
|
|
3874
|
+
},
|
|
3875
|
+
get anyErrored() {
|
|
3876
|
+
return result.anyErrored;
|
|
3747
3877
|
},
|
|
3748
|
-
get
|
|
3749
|
-
return result.
|
|
3878
|
+
get completedWithoutErrors() {
|
|
3879
|
+
return result.anyCompletedCleanly && !result.anyErrored;
|
|
3750
3880
|
},
|
|
3751
3881
|
dispose() {
|
|
3752
3882
|
unsub();
|
|
@@ -3782,9 +3912,10 @@ var Graph = class _Graph {
|
|
|
3782
3912
|
dirtyCount: 0,
|
|
3783
3913
|
resolvedCount: 0,
|
|
3784
3914
|
events: [],
|
|
3785
|
-
|
|
3786
|
-
|
|
3915
|
+
anyCompletedCleanly: false,
|
|
3916
|
+
anyErrored: false
|
|
3787
3917
|
};
|
|
3918
|
+
const nodeErrored = /* @__PURE__ */ new Set();
|
|
3788
3919
|
const actor = options.actor;
|
|
3789
3920
|
const targets = [];
|
|
3790
3921
|
this._collectObserveTargets("", targets);
|
|
@@ -3803,8 +3934,11 @@ var Graph = class _Graph {
|
|
|
3803
3934
|
} else if (minimal) {
|
|
3804
3935
|
if (t === DIRTY) result.dirtyCount++;
|
|
3805
3936
|
else if (t === RESOLVED) result.resolvedCount++;
|
|
3806
|
-
else if (t === COMPLETE && !
|
|
3807
|
-
else if (t === ERROR)
|
|
3937
|
+
else if (t === COMPLETE && !nodeErrored.has(path)) result.anyCompletedCleanly = true;
|
|
3938
|
+
else if (t === ERROR) {
|
|
3939
|
+
result.anyErrored = true;
|
|
3940
|
+
nodeErrored.add(path);
|
|
3941
|
+
}
|
|
3808
3942
|
} else if (t === DIRTY) {
|
|
3809
3943
|
result.dirtyCount++;
|
|
3810
3944
|
result.events.push({ type: "dirty", path, ...base });
|
|
@@ -3812,10 +3946,11 @@ var Graph = class _Graph {
|
|
|
3812
3946
|
result.resolvedCount++;
|
|
3813
3947
|
result.events.push({ type: "resolved", path, ...base });
|
|
3814
3948
|
} else if (t === COMPLETE) {
|
|
3815
|
-
if (!
|
|
3949
|
+
if (!nodeErrored.has(path)) result.anyCompletedCleanly = true;
|
|
3816
3950
|
result.events.push({ type: "complete", path, ...base });
|
|
3817
3951
|
} else if (t === ERROR) {
|
|
3818
|
-
result.
|
|
3952
|
+
result.anyErrored = true;
|
|
3953
|
+
nodeErrored.add(path);
|
|
3819
3954
|
result.events.push({ type: "error", path, data: m[1], ...base });
|
|
3820
3955
|
}
|
|
3821
3956
|
}
|
|
@@ -3835,11 +3970,14 @@ var Graph = class _Graph {
|
|
|
3835
3970
|
get events() {
|
|
3836
3971
|
return result.events;
|
|
3837
3972
|
},
|
|
3838
|
-
get
|
|
3839
|
-
return result.
|
|
3973
|
+
get anyCompletedCleanly() {
|
|
3974
|
+
return result.anyCompletedCleanly;
|
|
3975
|
+
},
|
|
3976
|
+
get anyErrored() {
|
|
3977
|
+
return result.anyErrored;
|
|
3840
3978
|
},
|
|
3841
|
-
get
|
|
3842
|
-
return result.
|
|
3979
|
+
get completedWithoutErrors() {
|
|
3980
|
+
return result.anyCompletedCleanly && !result.anyErrored;
|
|
3843
3981
|
},
|
|
3844
3982
|
dispose() {
|
|
3845
3983
|
for (const u of unsubs) u();
|
|
@@ -3871,8 +4009,8 @@ var Graph = class _Graph {
|
|
|
3871
4009
|
dirtyCount: 0,
|
|
3872
4010
|
resolvedCount: 0,
|
|
3873
4011
|
events: [],
|
|
3874
|
-
|
|
3875
|
-
|
|
4012
|
+
anyCompletedCleanly: false,
|
|
4013
|
+
anyErrored: false
|
|
3876
4014
|
};
|
|
3877
4015
|
const target = this.resolve(path);
|
|
3878
4016
|
let batchSeq = 0;
|
|
@@ -3891,10 +4029,10 @@ var Graph = class _Graph {
|
|
|
3891
4029
|
acc.resolvedCount++;
|
|
3892
4030
|
acc.events.push({ type: "resolved", path, ...base });
|
|
3893
4031
|
} else if (t === COMPLETE) {
|
|
3894
|
-
if (!acc.
|
|
4032
|
+
if (!acc.anyErrored) acc.anyCompletedCleanly = true;
|
|
3895
4033
|
acc.events.push({ type: "complete", path, ...base });
|
|
3896
4034
|
} else if (t === ERROR) {
|
|
3897
|
-
acc.
|
|
4035
|
+
acc.anyErrored = true;
|
|
3898
4036
|
acc.events.push({ type: "error", path, data: m[1], ...base });
|
|
3899
4037
|
}
|
|
3900
4038
|
}
|
|
@@ -3912,11 +4050,14 @@ var Graph = class _Graph {
|
|
|
3912
4050
|
get events() {
|
|
3913
4051
|
return acc.events;
|
|
3914
4052
|
},
|
|
3915
|
-
get
|
|
3916
|
-
return acc.
|
|
4053
|
+
get anyCompletedCleanly() {
|
|
4054
|
+
return acc.anyCompletedCleanly;
|
|
3917
4055
|
},
|
|
3918
|
-
get
|
|
3919
|
-
return acc.
|
|
4056
|
+
get anyErrored() {
|
|
4057
|
+
return acc.anyErrored;
|
|
4058
|
+
},
|
|
4059
|
+
get completedWithoutErrors() {
|
|
4060
|
+
return acc.anyCompletedCleanly && !acc.anyErrored;
|
|
3920
4061
|
},
|
|
3921
4062
|
dispose() {
|
|
3922
4063
|
unsub();
|
|
@@ -3937,9 +4078,10 @@ var Graph = class _Graph {
|
|
|
3937
4078
|
dirtyCount: 0,
|
|
3938
4079
|
resolvedCount: 0,
|
|
3939
4080
|
events: [],
|
|
3940
|
-
|
|
3941
|
-
|
|
4081
|
+
anyCompletedCleanly: false,
|
|
4082
|
+
anyErrored: false
|
|
3942
4083
|
};
|
|
4084
|
+
const nodeErrored = /* @__PURE__ */ new Set();
|
|
3943
4085
|
const targets = [];
|
|
3944
4086
|
this._collectObserveTargets("", targets);
|
|
3945
4087
|
targets.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
@@ -3961,10 +4103,11 @@ var Graph = class _Graph {
|
|
|
3961
4103
|
acc.resolvedCount++;
|
|
3962
4104
|
acc.events.push({ type: "resolved", path, ...base });
|
|
3963
4105
|
} else if (t === COMPLETE) {
|
|
3964
|
-
if (!
|
|
4106
|
+
if (!nodeErrored.has(path)) acc.anyCompletedCleanly = true;
|
|
3965
4107
|
acc.events.push({ type: "complete", path, ...base });
|
|
3966
4108
|
} else if (t === ERROR) {
|
|
3967
|
-
acc.
|
|
4109
|
+
acc.anyErrored = true;
|
|
4110
|
+
nodeErrored.add(path);
|
|
3968
4111
|
acc.events.push({ type: "error", path, data: m[1], ...base });
|
|
3969
4112
|
}
|
|
3970
4113
|
}
|
|
@@ -3983,11 +4126,14 @@ var Graph = class _Graph {
|
|
|
3983
4126
|
get events() {
|
|
3984
4127
|
return acc.events;
|
|
3985
4128
|
},
|
|
3986
|
-
get
|
|
3987
|
-
return acc.
|
|
4129
|
+
get anyCompletedCleanly() {
|
|
4130
|
+
return acc.anyCompletedCleanly;
|
|
4131
|
+
},
|
|
4132
|
+
get anyErrored() {
|
|
4133
|
+
return acc.anyErrored;
|
|
3988
4134
|
},
|
|
3989
|
-
get
|
|
3990
|
-
return acc.
|
|
4135
|
+
get completedWithoutErrors() {
|
|
4136
|
+
return acc.anyCompletedCleanly && !acc.anyErrored;
|
|
3991
4137
|
},
|
|
3992
4138
|
dispose() {
|
|
3993
4139
|
for (const u of unsubs) u();
|
|
@@ -4313,8 +4459,9 @@ var Graph = class _Graph {
|
|
|
4313
4459
|
/**
|
|
4314
4460
|
* Debounced persistence wired to graph-wide observe stream (spec §3.8 auto-checkpoint).
|
|
4315
4461
|
*
|
|
4316
|
-
* Checkpoint trigger uses {@link messageTier}: only batches containing tier >=
|
|
4317
|
-
* schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1 control
|
|
4462
|
+
* Checkpoint trigger uses {@link messageTier}: only batches containing tier >= 3 messages
|
|
4463
|
+
* schedule a save (`DATA`/`RESOLVED`/terminal/destruction), never pure tier-0/1/2 control
|
|
4464
|
+
* waves (`START`/`DIRTY`/`INVALIDATE`/`PAUSE`/`RESUME`).
|
|
4318
4465
|
*/
|
|
4319
4466
|
autoCheckpoint(adapter, options = {}) {
|
|
4320
4467
|
const debounceMs = Math.max(0, options.debounceMs ?? 500);
|
|
@@ -4361,7 +4508,7 @@ var Graph = class _Graph {
|
|
|
4361
4508
|
timer = setTimeout(flush, debounceMs);
|
|
4362
4509
|
};
|
|
4363
4510
|
const off = this.observe().subscribe((path, messages) => {
|
|
4364
|
-
const triggeredByTier = messages.some((m) => messageTier(m[0]) >=
|
|
4511
|
+
const triggeredByTier = messages.some((m) => messageTier(m[0]) >= 3);
|
|
4365
4512
|
if (!triggeredByTier) return;
|
|
4366
4513
|
if (options.filter) {
|
|
4367
4514
|
const nd = this.resolve(path);
|