@graphrefly/graphrefly 0.17.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-R6OHUUYB.js → chunk-AHRKWMNI.js} +7 -7
- package/dist/chunk-AHRKWMNI.js.map +1 -0
- package/dist/{chunk-2PORF4RP.js → chunk-BER7UYLM.js} +27 -32
- package/dist/chunk-BER7UYLM.js.map +1 -0
- package/dist/{chunk-646OG3PO.js → chunk-IRZAGZUB.js} +51 -52
- package/dist/chunk-IRZAGZUB.js.map +1 -0
- package/dist/{chunk-IHJHBADD.js → chunk-JC2SN46B.js} +385 -197
- package/dist/chunk-JC2SN46B.js.map +1 -0
- package/dist/{chunk-XJ6EMQ22.js → chunk-OO5QOAXI.js} +4 -10
- package/dist/chunk-OO5QOAXI.js.map +1 -0
- package/dist/{chunk-YXROQFXZ.js → chunk-UW77D7SP.js} +3 -3
- package/dist/{chunk-F2ULI3Q3.js → chunk-XUOY3YKN.js} +7 -3
- 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 +1127 -983
- 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 -13
- package/dist/core/index.cjs +653 -749
- 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 -7
- package/dist/extra/index.cjs +773 -795
- 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 -11
- package/dist/graph/index.cjs +1036 -975
- 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-fCsaaVIa.d.cts → graph-KsTe57nI.d.cts} +127 -51
- package/dist/{graph-Dc-P9BVm.d.ts → graph-mILUUqW8.d.ts} +127 -51
- package/dist/{index-DhXznWyH.d.ts → index-8a605sg9.d.ts} +2 -2
- package/dist/{index-D7y9Q8W4.d.ts → index-B2SvPEbc.d.ts} +8 -69
- package/dist/{index-YlOH1Gw6.d.cts → index-BBUYZfJ1.d.cts} +122 -78
- package/dist/{index-ClaKZFPl.d.cts → index-Bjh5C1Tp.d.cts} +38 -35
- package/dist/{index-DWq0P9T6.d.ts → index-BjtlNirP.d.cts} +5 -7
- package/dist/{index-N704txAA.d.ts → index-BnkMgNNa.d.ts} +38 -35
- package/dist/{index-BBVBYPxr.d.cts → index-CgSiUouz.d.ts} +5 -7
- package/dist/{index-BmoUvOGN.d.ts → index-CvKzv0AW.d.ts} +122 -78
- package/dist/{index-4OIX-q0C.d.cts → index-UudxGnzc.d.cts} +8 -69
- package/dist/{index-DlGMf_Qe.d.cts → index-VHA43cGP.d.cts} +2 -2
- package/dist/index.cjs +6146 -5725
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +617 -383
- package/dist/index.d.ts +617 -383
- package/dist/index.js +4401 -4028
- package/dist/index.js.map +1 -1
- package/dist/{meta-BV4pj9ML.d.cts → meta-BnG7XAaE.d.cts} +395 -289
- package/dist/{meta-BV4pj9ML.d.ts → meta-BnG7XAaE.d.ts} +395 -289
- package/dist/observable-C8Kx_O6k.d.cts +36 -0
- package/dist/observable-DcBwQY7t.d.ts +36 -0
- package/dist/patterns/reactive-layout/index.cjs +1037 -857
- 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-2PORF4RP.js.map +0 -1
- package/dist/chunk-646OG3PO.js.map +0 -1
- package/dist/chunk-BV3TPSBK.js.map +0 -1
- package/dist/chunk-EBNKJULL.js +0 -231
- package/dist/chunk-EBNKJULL.js.map +0 -1
- package/dist/chunk-F2ULI3Q3.js.map +0 -1
- package/dist/chunk-IHJHBADD.js.map +0 -1
- package/dist/chunk-R6OHUUYB.js.map +0 -1
- package/dist/chunk-XJ6EMQ22.js.map +0 -1
- package/dist/observable-Cz-AWhwR.d.cts +0 -42
- package/dist/observable-DCqlwGyl.d.ts +0 -42
- /package/dist/{chunk-YXROQFXZ.js.map → chunk-UW77D7SP.js.map} +0 -0
package/dist/core/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(core_exports, {
|
|
|
34
34
|
RESOLVED: () => RESOLVED,
|
|
35
35
|
RESUME: () => RESUME,
|
|
36
36
|
ResettableTimer: () => ResettableTimer,
|
|
37
|
+
START: () => START,
|
|
37
38
|
TEARDOWN: () => TEARDOWN,
|
|
38
39
|
accessHintForGuard: () => accessHintForGuard,
|
|
39
40
|
advanceVersion: () => advanceVersion,
|
|
@@ -43,18 +44,17 @@ __export(core_exports, {
|
|
|
43
44
|
createVersioning: () => createVersioning,
|
|
44
45
|
defaultHash: () => defaultHash,
|
|
45
46
|
derived: () => derived,
|
|
46
|
-
describeNode: () => describeNode,
|
|
47
47
|
downWithBatch: () => downWithBatch,
|
|
48
48
|
dynamicNode: () => dynamicNode,
|
|
49
49
|
effect: () => effect,
|
|
50
50
|
isBatching: () => isBatching,
|
|
51
51
|
isKnownMessageType: () => isKnownMessageType,
|
|
52
|
+
isLocalOnly: () => isLocalOnly,
|
|
52
53
|
isPhase2Message: () => isPhase2Message,
|
|
53
54
|
isTerminalMessage: () => isTerminalMessage,
|
|
54
55
|
isV1: () => isV1,
|
|
55
56
|
knownMessageTypes: () => knownMessageTypes,
|
|
56
57
|
messageTier: () => messageTier,
|
|
57
|
-
metaSnapshot: () => metaSnapshot,
|
|
58
58
|
monotonicNs: () => monotonicNs,
|
|
59
59
|
node: () => node,
|
|
60
60
|
normalizeActor: () => normalizeActor,
|
|
@@ -83,6 +83,7 @@ function normalizeActor(actor) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// src/core/messages.ts
|
|
86
|
+
var START = /* @__PURE__ */ Symbol.for("graphrefly/START");
|
|
86
87
|
var DATA = /* @__PURE__ */ Symbol.for("graphrefly/DATA");
|
|
87
88
|
var DIRTY = /* @__PURE__ */ Symbol.for("graphrefly/DIRTY");
|
|
88
89
|
var RESOLVED = /* @__PURE__ */ Symbol.for("graphrefly/RESOLVED");
|
|
@@ -93,6 +94,7 @@ var TEARDOWN = /* @__PURE__ */ Symbol.for("graphrefly/TEARDOWN");
|
|
|
93
94
|
var COMPLETE = /* @__PURE__ */ Symbol.for("graphrefly/COMPLETE");
|
|
94
95
|
var ERROR = /* @__PURE__ */ Symbol.for("graphrefly/ERROR");
|
|
95
96
|
var knownMessageTypes = [
|
|
97
|
+
START,
|
|
96
98
|
DATA,
|
|
97
99
|
DIRTY,
|
|
98
100
|
RESOLVED,
|
|
@@ -103,16 +105,18 @@ var knownMessageTypes = [
|
|
|
103
105
|
COMPLETE,
|
|
104
106
|
ERROR
|
|
105
107
|
];
|
|
108
|
+
var knownMessageSet = new Set(knownMessageTypes);
|
|
106
109
|
function isKnownMessageType(t) {
|
|
107
|
-
return
|
|
110
|
+
return knownMessageSet.has(t);
|
|
108
111
|
}
|
|
109
112
|
function messageTier(t) {
|
|
110
|
-
if (t ===
|
|
111
|
-
if (t ===
|
|
112
|
-
if (t ===
|
|
113
|
-
if (t ===
|
|
114
|
-
if (t ===
|
|
115
|
-
return
|
|
113
|
+
if (t === START) return 0;
|
|
114
|
+
if (t === DIRTY || t === INVALIDATE) return 1;
|
|
115
|
+
if (t === PAUSE || t === RESUME) return 2;
|
|
116
|
+
if (t === DATA || t === RESOLVED) return 3;
|
|
117
|
+
if (t === COMPLETE || t === ERROR) return 4;
|
|
118
|
+
if (t === TEARDOWN) return 5;
|
|
119
|
+
return 1;
|
|
116
120
|
}
|
|
117
121
|
function isPhase2Message(msg) {
|
|
118
122
|
const t = msg[0];
|
|
@@ -121,6 +125,10 @@ function isPhase2Message(msg) {
|
|
|
121
125
|
function isTerminalMessage(t) {
|
|
122
126
|
return t === COMPLETE || t === ERROR;
|
|
123
127
|
}
|
|
128
|
+
function isLocalOnly(t) {
|
|
129
|
+
if (!knownMessageSet.has(t)) return false;
|
|
130
|
+
return messageTier(t) < 3;
|
|
131
|
+
}
|
|
124
132
|
function propagatesToMeta(t) {
|
|
125
133
|
return t === TEARDOWN;
|
|
126
134
|
}
|
|
@@ -281,14 +289,14 @@ function _downSequential(sink, messages, phase = 2) {
|
|
|
281
289
|
const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
|
|
282
290
|
for (const msg of messages) {
|
|
283
291
|
const tier = messageTier(msg[0]);
|
|
284
|
-
if (tier ===
|
|
292
|
+
if (tier === 3) {
|
|
285
293
|
if (isBatching()) {
|
|
286
294
|
const m = msg;
|
|
287
295
|
dataQueue.push(() => sink([m]));
|
|
288
296
|
} else {
|
|
289
297
|
sink([msg]);
|
|
290
298
|
}
|
|
291
|
-
} else if (tier >=
|
|
299
|
+
} else if (tier >= 4) {
|
|
292
300
|
if (isBatching()) {
|
|
293
301
|
const m = msg;
|
|
294
302
|
pendingPhase3.push(() => sink([m]));
|
|
@@ -301,14 +309,6 @@ function _downSequential(sink, messages, phase = 2) {
|
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
311
|
|
|
304
|
-
// src/core/clock.ts
|
|
305
|
-
function monotonicNs() {
|
|
306
|
-
return Math.trunc(performance.now() * 1e6);
|
|
307
|
-
}
|
|
308
|
-
function wallClockNs() {
|
|
309
|
-
return Date.now() * 1e6;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
312
|
// src/core/guard.ts
|
|
313
313
|
var GuardDenied = class extends Error {
|
|
314
314
|
actor;
|
|
@@ -407,6 +407,14 @@ function accessHintForGuard(guard) {
|
|
|
407
407
|
return allowed.join("+");
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
// src/core/clock.ts
|
|
411
|
+
function monotonicNs() {
|
|
412
|
+
return Math.trunc(performance.now() * 1e6);
|
|
413
|
+
}
|
|
414
|
+
function wallClockNs() {
|
|
415
|
+
return Date.now() * 1e6;
|
|
416
|
+
}
|
|
417
|
+
|
|
410
418
|
// src/core/versioning.ts
|
|
411
419
|
var import_node_crypto = require("crypto");
|
|
412
420
|
function canonicalizeForHash(value) {
|
|
@@ -462,10 +470,29 @@ function isV1(info) {
|
|
|
462
470
|
return "cid" in info;
|
|
463
471
|
}
|
|
464
472
|
|
|
465
|
-
// src/core/node.ts
|
|
473
|
+
// src/core/node-base.ts
|
|
466
474
|
var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
|
|
467
475
|
var CLEANUP_RESULT = /* @__PURE__ */ Symbol.for("graphrefly/CLEANUP_RESULT");
|
|
468
|
-
function
|
|
476
|
+
function cleanupResult(cleanup, ...args) {
|
|
477
|
+
const r = { [CLEANUP_RESULT]: true, cleanup };
|
|
478
|
+
if (args.length > 0) r.value = args[0];
|
|
479
|
+
return r;
|
|
480
|
+
}
|
|
481
|
+
var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
|
|
482
|
+
var isCleanupFn = (value) => typeof value === "function";
|
|
483
|
+
function statusAfterMessage(status, msg) {
|
|
484
|
+
const t = msg[0];
|
|
485
|
+
if (t === DIRTY) return "dirty";
|
|
486
|
+
if (t === DATA) return "settled";
|
|
487
|
+
if (t === RESOLVED) return "resolved";
|
|
488
|
+
if (t === COMPLETE) return "completed";
|
|
489
|
+
if (t === ERROR) return "errored";
|
|
490
|
+
if (t === INVALIDATE) return "dirty";
|
|
491
|
+
if (t === TEARDOWN) return "disconnected";
|
|
492
|
+
return status;
|
|
493
|
+
}
|
|
494
|
+
function createIntBitSet(size) {
|
|
495
|
+
const fullMask = size >= 32 ? -1 : ~(-1 << size);
|
|
469
496
|
let bits = 0;
|
|
470
497
|
return {
|
|
471
498
|
set(i) {
|
|
@@ -478,7 +505,8 @@ function createIntBitSet() {
|
|
|
478
505
|
return (bits & 1 << i) !== 0;
|
|
479
506
|
},
|
|
480
507
|
covers(other) {
|
|
481
|
-
|
|
508
|
+
const otherBits = other._bits();
|
|
509
|
+
return (bits & otherBits) === otherBits;
|
|
482
510
|
},
|
|
483
511
|
any() {
|
|
484
512
|
return bits !== 0;
|
|
@@ -486,6 +514,9 @@ function createIntBitSet() {
|
|
|
486
514
|
reset() {
|
|
487
515
|
bits = 0;
|
|
488
516
|
},
|
|
517
|
+
setAll() {
|
|
518
|
+
bits = fullMask;
|
|
519
|
+
},
|
|
489
520
|
_bits() {
|
|
490
521
|
return bits;
|
|
491
522
|
}
|
|
@@ -493,6 +524,8 @@ function createIntBitSet() {
|
|
|
493
524
|
}
|
|
494
525
|
function createArrayBitSet(size) {
|
|
495
526
|
const words = new Uint32Array(Math.ceil(size / 32));
|
|
527
|
+
const lastBits = size % 32;
|
|
528
|
+
const lastWordMask = lastBits === 0 ? 4294967295 : (1 << lastBits) - 1 >>> 0;
|
|
496
529
|
return {
|
|
497
530
|
set(i) {
|
|
498
531
|
words[i >>> 5] |= 1 << (i & 31);
|
|
@@ -519,135 +552,103 @@ function createArrayBitSet(size) {
|
|
|
519
552
|
reset() {
|
|
520
553
|
words.fill(0);
|
|
521
554
|
},
|
|
555
|
+
setAll() {
|
|
556
|
+
for (let w = 0; w < words.length - 1; w++) words[w] = 4294967295;
|
|
557
|
+
if (words.length > 0) words[words.length - 1] = lastWordMask;
|
|
558
|
+
},
|
|
522
559
|
_words: words
|
|
523
560
|
};
|
|
524
561
|
}
|
|
525
562
|
function createBitSet(size) {
|
|
526
|
-
return size <= 31 ? createIntBitSet() : createArrayBitSet(size);
|
|
527
|
-
}
|
|
528
|
-
var isNodeArray = (value) => Array.isArray(value);
|
|
529
|
-
var isNodeOptions = (value) => typeof value === "object" && value != null && !Array.isArray(value);
|
|
530
|
-
function cleanupResult(cleanup, ...args) {
|
|
531
|
-
const r = { [CLEANUP_RESULT]: true, cleanup };
|
|
532
|
-
if (args.length > 0) r.value = args[0];
|
|
533
|
-
return r;
|
|
563
|
+
return size <= 31 ? createIntBitSet(size) : createArrayBitSet(size);
|
|
534
564
|
}
|
|
535
|
-
var
|
|
536
|
-
|
|
537
|
-
var statusAfterMessage = (status, msg) => {
|
|
538
|
-
const t = msg[0];
|
|
539
|
-
if (t === DIRTY) return "dirty";
|
|
540
|
-
if (t === DATA) return "settled";
|
|
541
|
-
if (t === RESOLVED) return "resolved";
|
|
542
|
-
if (t === COMPLETE) return "completed";
|
|
543
|
-
if (t === ERROR) return "errored";
|
|
544
|
-
if (t === INVALIDATE) return "dirty";
|
|
545
|
-
if (t === TEARDOWN) return "disconnected";
|
|
546
|
-
return status;
|
|
547
|
-
};
|
|
548
|
-
var NodeImpl = class {
|
|
549
|
-
// --- Configuration (set once, never reassigned) ---
|
|
565
|
+
var NodeBase = class {
|
|
566
|
+
// --- Identity (set once) ---
|
|
550
567
|
_optsName;
|
|
551
568
|
_registryName;
|
|
552
|
-
/** @internal
|
|
569
|
+
/** @internal Read by `describeNode` before inference. */
|
|
553
570
|
_describeKind;
|
|
554
571
|
meta;
|
|
555
|
-
|
|
556
|
-
_fn;
|
|
557
|
-
_opts;
|
|
572
|
+
// --- Options ---
|
|
558
573
|
_equals;
|
|
574
|
+
_resubscribable;
|
|
575
|
+
_resetOnTeardown;
|
|
576
|
+
_onResubscribe;
|
|
559
577
|
_onMessage;
|
|
560
|
-
/** @internal
|
|
578
|
+
/** @internal Read by `describeNode` for `accessHintForGuard`. */
|
|
561
579
|
_guard;
|
|
580
|
+
/** @internal Subclasses update this through {@link _recordMutation}. */
|
|
562
581
|
_lastMutation;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// ---
|
|
582
|
+
// --- Versioning ---
|
|
583
|
+
_hashFn;
|
|
584
|
+
_versioning;
|
|
585
|
+
// --- Lifecycle state ---
|
|
586
|
+
/** @internal Read by `describeNode` and `graph.ts`. */
|
|
567
587
|
_cached;
|
|
588
|
+
/** @internal Read externally via `get status()`. */
|
|
568
589
|
_status;
|
|
569
590
|
_terminal = false;
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
_manualEmitUsed = false;
|
|
591
|
+
_active = false;
|
|
592
|
+
// --- Sink storage ---
|
|
593
|
+
/** @internal Read by `graph/profile.ts` for subscriber counts. */
|
|
574
594
|
_sinkCount = 0;
|
|
575
595
|
_singleDepSinkCount = 0;
|
|
576
|
-
// --- Object/collection state ---
|
|
577
|
-
_depDirtyMask;
|
|
578
|
-
_depSettledMask;
|
|
579
|
-
_depCompleteMask;
|
|
580
|
-
_allDepsCompleteMask;
|
|
581
|
-
_lastDepValues;
|
|
582
|
-
_cleanup;
|
|
583
|
-
_sinks = null;
|
|
584
596
|
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
585
|
-
|
|
597
|
+
_sinks = null;
|
|
598
|
+
// --- Actions + bound helpers ---
|
|
586
599
|
_actions;
|
|
587
600
|
_boundDownToSinks;
|
|
601
|
+
// --- Inspector hook (Graph observability) ---
|
|
588
602
|
_inspectorHook;
|
|
589
|
-
|
|
590
|
-
_hashFn;
|
|
591
|
-
constructor(deps, fn, opts) {
|
|
592
|
-
this._deps = deps;
|
|
593
|
-
this._fn = fn;
|
|
594
|
-
this._opts = opts;
|
|
603
|
+
constructor(opts) {
|
|
595
604
|
this._optsName = opts.name;
|
|
596
605
|
this._describeKind = opts.describeKind;
|
|
597
606
|
this._equals = opts.equals ?? Object.is;
|
|
607
|
+
this._resubscribable = opts.resubscribable ?? false;
|
|
608
|
+
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
609
|
+
this._onResubscribe = opts.onResubscribe;
|
|
598
610
|
this._onMessage = opts.onMessage;
|
|
599
611
|
this._guard = opts.guard;
|
|
600
|
-
this._hasDeps = deps.length > 0;
|
|
601
|
-
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
602
|
-
this._isSingleDep = deps.length === 1 && fn != null;
|
|
603
612
|
this._cached = "initial" in opts ? opts.initial : NO_VALUE;
|
|
604
|
-
this._status =
|
|
613
|
+
this._status = "disconnected";
|
|
605
614
|
this._hashFn = opts.versioningHash ?? defaultHash;
|
|
606
615
|
this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
|
|
607
616
|
id: opts.versioningId,
|
|
608
617
|
hash: this._hashFn
|
|
609
618
|
}) : void 0;
|
|
610
|
-
this._depDirtyMask = createBitSet(deps.length);
|
|
611
|
-
this._depSettledMask = createBitSet(deps.length);
|
|
612
|
-
this._depCompleteMask = createBitSet(deps.length);
|
|
613
|
-
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
614
|
-
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
615
619
|
const meta = {};
|
|
616
620
|
for (const [k, v] of Object.entries(opts.meta ?? {})) {
|
|
617
|
-
meta[k] =
|
|
618
|
-
initial: v,
|
|
619
|
-
name: `${opts.name ?? "node"}:meta:${k}`,
|
|
620
|
-
describeKind: "state",
|
|
621
|
-
...opts.guard != null ? { guard: opts.guard } : {}
|
|
622
|
-
});
|
|
621
|
+
meta[k] = this._createMetaNode(k, v, opts);
|
|
623
622
|
}
|
|
624
623
|
Object.freeze(meta);
|
|
625
624
|
this.meta = meta;
|
|
626
625
|
const self = this;
|
|
627
626
|
this._actions = {
|
|
628
627
|
down(messages) {
|
|
629
|
-
self.
|
|
628
|
+
self._onManualEmit();
|
|
630
629
|
self._downInternal(messages);
|
|
631
630
|
},
|
|
632
631
|
emit(value) {
|
|
633
|
-
self.
|
|
632
|
+
self._onManualEmit();
|
|
634
633
|
self._downAutoValue(value);
|
|
635
634
|
},
|
|
636
635
|
up(messages) {
|
|
637
636
|
self._upInternal(messages);
|
|
638
637
|
}
|
|
639
638
|
};
|
|
640
|
-
this.down = this.down.bind(this);
|
|
641
|
-
this.up = this.up.bind(this);
|
|
642
639
|
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
643
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Subclass hook invoked by `actions.down` / `actions.emit`. Default no-op;
|
|
643
|
+
* {@link NodeImpl} overrides to set `_manualEmitUsed`.
|
|
644
|
+
*/
|
|
645
|
+
_onManualEmit() {
|
|
646
|
+
}
|
|
647
|
+
// --- Identity getters ---
|
|
644
648
|
get name() {
|
|
645
649
|
return this._registryName ?? this._optsName;
|
|
646
650
|
}
|
|
647
|
-
/**
|
|
648
|
-
* When a node is registered with {@link Graph.add} without an options `name`,
|
|
649
|
-
* the graph assigns the registry local name for introspection (parity with graphrefly-py).
|
|
650
|
-
*/
|
|
651
|
+
/** @internal Assigned by `Graph.add` when registered without an options `name`. */
|
|
651
652
|
_assignRegistryName(localName) {
|
|
652
653
|
if (this._optsName !== void 0 || this._registryName !== void 0) return;
|
|
653
654
|
this._registryName = localName;
|
|
@@ -665,7 +666,10 @@ var NodeImpl = class {
|
|
|
665
666
|
}
|
|
666
667
|
};
|
|
667
668
|
}
|
|
668
|
-
|
|
669
|
+
/** @internal Used by subclasses to surface inspector events. */
|
|
670
|
+
_emitInspectorHook(event) {
|
|
671
|
+
this._inspectorHook?.(event);
|
|
672
|
+
}
|
|
669
673
|
get status() {
|
|
670
674
|
return this._status;
|
|
671
675
|
}
|
|
@@ -675,15 +679,7 @@ var NodeImpl = class {
|
|
|
675
679
|
get v() {
|
|
676
680
|
return this._versioning;
|
|
677
681
|
}
|
|
678
|
-
/**
|
|
679
|
-
* Retroactively apply versioning to a node that was created without it.
|
|
680
|
-
* No-op if versioning is already enabled.
|
|
681
|
-
*
|
|
682
|
-
* Version starts at 0 regardless of prior DATA emissions — it tracks
|
|
683
|
-
* changes from the moment versioning is enabled, not historical ones.
|
|
684
|
-
*
|
|
685
|
-
* @internal — used by {@link Graph.setVersioning}.
|
|
686
|
-
*/
|
|
682
|
+
/** @internal Used by `Graph.setVersioning` to retroactively apply versioning. */
|
|
687
683
|
_applyVersioning(level, opts) {
|
|
688
684
|
if (this._versioning != null) return;
|
|
689
685
|
this._hashFn = opts?.hash ?? this._hashFn;
|
|
@@ -703,6 +699,7 @@ var NodeImpl = class {
|
|
|
703
699
|
if (this._guard == null) return true;
|
|
704
700
|
return this._guard(normalizeActor(actor), "observe");
|
|
705
701
|
}
|
|
702
|
+
// --- Public transport ---
|
|
706
703
|
get() {
|
|
707
704
|
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
708
705
|
}
|
|
@@ -715,43 +712,25 @@ var NodeImpl = class {
|
|
|
715
712
|
if (!this._guard(actor, action)) {
|
|
716
713
|
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
717
714
|
}
|
|
718
|
-
this.
|
|
715
|
+
this._recordMutation(actor);
|
|
719
716
|
}
|
|
720
717
|
this._downInternal(messages);
|
|
721
718
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
this.
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
736
|
-
const t = sinkMessages[i][0];
|
|
737
|
-
if (t === DATA || t === RESOLVED) {
|
|
738
|
-
hasPhase2 = true;
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
if (hasPhase2) {
|
|
743
|
-
const filtered = [];
|
|
744
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
745
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
746
|
-
}
|
|
747
|
-
if (filtered.length > 0) {
|
|
748
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
749
|
-
}
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
719
|
+
/** @internal Record a successful guarded mutation (called by `down` and subclass `up`). */
|
|
720
|
+
_recordMutation(actor) {
|
|
721
|
+
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* At-most-once deactivation guard. Both TEARDOWN (eager) and
|
|
725
|
+
* unsubscribe-body (lazy) call this. The `_active` flag ensures
|
|
726
|
+
* `_doDeactivate` runs exactly once per activation cycle.
|
|
727
|
+
*/
|
|
728
|
+
_onDeactivate() {
|
|
729
|
+
if (!this._active) return;
|
|
730
|
+
this._active = false;
|
|
731
|
+
this._doDeactivate();
|
|
754
732
|
}
|
|
733
|
+
// --- Subscribe (uniform across node shapes) ---
|
|
755
734
|
subscribe(sink, hints) {
|
|
756
735
|
if (hints?.actor != null && this._guard != null) {
|
|
757
736
|
const actor = normalizeActor(hints.actor);
|
|
@@ -759,17 +738,21 @@ var NodeImpl = class {
|
|
|
759
738
|
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
760
739
|
}
|
|
761
740
|
}
|
|
762
|
-
if (this._terminal && this.
|
|
741
|
+
if (this._terminal && this._resubscribable) {
|
|
763
742
|
this._terminal = false;
|
|
764
743
|
this._cached = NO_VALUE;
|
|
765
|
-
this._status =
|
|
766
|
-
this.
|
|
744
|
+
this._status = "disconnected";
|
|
745
|
+
this._onResubscribe?.();
|
|
767
746
|
}
|
|
768
747
|
this._sinkCount += 1;
|
|
769
748
|
if (hints?.singleDep) {
|
|
770
749
|
this._singleDepSinkCount += 1;
|
|
771
750
|
this._singleDepSinks.add(sink);
|
|
772
751
|
}
|
|
752
|
+
if (!this._terminal) {
|
|
753
|
+
const startMessages = this._cached === NO_VALUE ? [[START]] : [[START], [DATA, this._cached]];
|
|
754
|
+
downWithBatch(sink, startMessages);
|
|
755
|
+
}
|
|
773
756
|
if (this._sinks == null) {
|
|
774
757
|
this._sinks = sink;
|
|
775
758
|
} else if (typeof this._sinks === "function") {
|
|
@@ -777,10 +760,12 @@ var NodeImpl = class {
|
|
|
777
760
|
} else {
|
|
778
761
|
this._sinks.add(sink);
|
|
779
762
|
}
|
|
780
|
-
if (this.
|
|
781
|
-
this.
|
|
782
|
-
|
|
783
|
-
|
|
763
|
+
if (this._sinkCount === 1 && !this._terminal) {
|
|
764
|
+
this._active = true;
|
|
765
|
+
this._onActivate();
|
|
766
|
+
}
|
|
767
|
+
if (!this._terminal && this._status === "disconnected" && this._cached === NO_VALUE) {
|
|
768
|
+
this._status = "pending";
|
|
784
769
|
}
|
|
785
770
|
let removed = false;
|
|
786
771
|
return () => {
|
|
@@ -804,39 +789,49 @@ var NodeImpl = class {
|
|
|
804
789
|
}
|
|
805
790
|
}
|
|
806
791
|
if (this._sinks == null) {
|
|
807
|
-
this.
|
|
808
|
-
this._stopProducer();
|
|
792
|
+
this._onDeactivate();
|
|
809
793
|
}
|
|
810
794
|
};
|
|
811
795
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
796
|
+
// --- Down pipeline ---
|
|
797
|
+
/**
|
|
798
|
+
* Core outgoing dispatch. Applies terminal filter + local lifecycle
|
|
799
|
+
* update, then hands messages to `downWithBatch` for tier-aware delivery.
|
|
800
|
+
*/
|
|
801
|
+
_downInternal(messages) {
|
|
802
|
+
if (messages.length === 0) return;
|
|
803
|
+
let sinkMessages = messages;
|
|
804
|
+
if (this._terminal && !this._resubscribable) {
|
|
805
|
+
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
806
|
+
if (pass.length === 0) return;
|
|
807
|
+
sinkMessages = pass;
|
|
820
808
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
809
|
+
this._handleLocalLifecycle(sinkMessages);
|
|
810
|
+
if (this._canSkipDirty()) {
|
|
811
|
+
let hasPhase2 = false;
|
|
812
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
813
|
+
const t = sinkMessages[i][0];
|
|
814
|
+
if (t === DATA || t === RESOLVED) {
|
|
815
|
+
hasPhase2 = true;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (hasPhase2) {
|
|
820
|
+
const filtered = [];
|
|
821
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
822
|
+
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
823
|
+
}
|
|
824
|
+
if (filtered.length > 0) {
|
|
825
|
+
downWithBatch(this._boundDownToSinks, filtered);
|
|
826
|
+
}
|
|
827
|
+
return;
|
|
826
828
|
}
|
|
827
829
|
}
|
|
830
|
+
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
828
831
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
for (const dep of this._deps) {
|
|
832
|
-
dep.up?.(messages, { internal: true });
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
unsubscribe() {
|
|
836
|
-
if (!this._hasDeps) return;
|
|
837
|
-
this._disconnectUpstream();
|
|
832
|
+
_canSkipDirty() {
|
|
833
|
+
return this._sinkCount === 1 && this._singleDepSinkCount === 1;
|
|
838
834
|
}
|
|
839
|
-
// --- Private methods (prototype, _ prefix) ---
|
|
840
835
|
_downToSinks(messages) {
|
|
841
836
|
if (this._sinks == null) return;
|
|
842
837
|
if (typeof this._sinks === "function") {
|
|
@@ -848,6 +843,11 @@ var NodeImpl = class {
|
|
|
848
843
|
sink(messages);
|
|
849
844
|
}
|
|
850
845
|
}
|
|
846
|
+
/**
|
|
847
|
+
* Update `_cached`, `_status`, `_terminal` from message batch before
|
|
848
|
+
* delivery. Subclass hooks `_onInvalidate` / `_onTeardown` let
|
|
849
|
+
* {@link NodeImpl} clear `_lastDepValues` and invoke cleanup fns.
|
|
850
|
+
*/
|
|
851
851
|
_handleLocalLifecycle(messages) {
|
|
852
852
|
for (const m of messages) {
|
|
853
853
|
const t = m[0];
|
|
@@ -861,28 +861,22 @@ var NodeImpl = class {
|
|
|
861
861
|
}
|
|
862
862
|
}
|
|
863
863
|
if (t === INVALIDATE) {
|
|
864
|
-
|
|
865
|
-
this._cleanup = void 0;
|
|
866
|
-
cleanupFn?.();
|
|
864
|
+
this._onInvalidate();
|
|
867
865
|
this._cached = NO_VALUE;
|
|
868
|
-
this._lastDepValues = void 0;
|
|
869
866
|
}
|
|
870
867
|
this._status = statusAfterMessage(this._status, m);
|
|
871
868
|
if (t === COMPLETE || t === ERROR) {
|
|
872
869
|
this._terminal = true;
|
|
873
870
|
}
|
|
874
871
|
if (t === TEARDOWN) {
|
|
875
|
-
if (this.
|
|
872
|
+
if (this._resetOnTeardown) {
|
|
876
873
|
this._cached = NO_VALUE;
|
|
877
874
|
}
|
|
878
|
-
|
|
879
|
-
this._cleanup = void 0;
|
|
880
|
-
teardownCleanup?.();
|
|
875
|
+
this._onTeardown();
|
|
881
876
|
try {
|
|
882
877
|
this._propagateToMeta(t);
|
|
883
878
|
} finally {
|
|
884
|
-
this.
|
|
885
|
-
this._stopProducer();
|
|
879
|
+
this._onDeactivate();
|
|
886
880
|
}
|
|
887
881
|
}
|
|
888
882
|
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
@@ -890,7 +884,20 @@ var NodeImpl = class {
|
|
|
890
884
|
}
|
|
891
885
|
}
|
|
892
886
|
}
|
|
893
|
-
/**
|
|
887
|
+
/**
|
|
888
|
+
* Subclass hook: invoked when INVALIDATE arrives (before `_cached` is
|
|
889
|
+
* cleared). {@link NodeImpl} uses this to run the fn cleanup fn and
|
|
890
|
+
* drop `_lastDepValues` so the next wave re-runs fn.
|
|
891
|
+
*/
|
|
892
|
+
_onInvalidate() {
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Subclass hook: invoked when TEARDOWN arrives, before `_onDeactivate`.
|
|
896
|
+
* {@link NodeImpl} uses this to run any pending cleanup fn.
|
|
897
|
+
*/
|
|
898
|
+
_onTeardown() {
|
|
899
|
+
}
|
|
900
|
+
/** Forward a signal to all companion meta nodes (best-effort). */
|
|
894
901
|
_propagateToMeta(t) {
|
|
895
902
|
for (const metaNode of Object.values(this.meta)) {
|
|
896
903
|
try {
|
|
@@ -899,9 +906,10 @@ var NodeImpl = class {
|
|
|
899
906
|
}
|
|
900
907
|
}
|
|
901
908
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
909
|
+
/**
|
|
910
|
+
* Frame a computed value into the right protocol messages and dispatch
|
|
911
|
+
* via `_downInternal`. Used by `_runFn` and `actions.emit`.
|
|
912
|
+
*/
|
|
905
913
|
_downAutoValue(value) {
|
|
906
914
|
const wasDirty = this._status === "dirty";
|
|
907
915
|
let unchanged;
|
|
@@ -909,7 +917,9 @@ var NodeImpl = class {
|
|
|
909
917
|
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
910
918
|
} catch (eqErr) {
|
|
911
919
|
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
912
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
920
|
+
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
921
|
+
cause: eqErr
|
|
922
|
+
});
|
|
913
923
|
this._downInternal([[ERROR, wrapped]]);
|
|
914
924
|
return;
|
|
915
925
|
}
|
|
@@ -919,89 +929,173 @@ var NodeImpl = class {
|
|
|
919
929
|
}
|
|
920
930
|
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
921
931
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if ("value" in out) {
|
|
957
|
-
this._downAutoValue(out.value);
|
|
958
|
-
}
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
if (isCleanupFn(out)) {
|
|
962
|
-
this._cleanup = out;
|
|
963
|
-
return;
|
|
964
|
-
}
|
|
965
|
-
if (this._manualEmitUsed) return;
|
|
966
|
-
if (out === void 0) return;
|
|
967
|
-
this._downAutoValue(out);
|
|
968
|
-
} catch (err) {
|
|
969
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
970
|
-
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
971
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
_onDepDirty(index) {
|
|
975
|
-
const wasDirty = this._depDirtyMask.has(index);
|
|
976
|
-
this._depDirtyMask.set(index);
|
|
977
|
-
this._depSettledMask.clear(index);
|
|
978
|
-
if (!wasDirty) {
|
|
979
|
-
this._downInternal([[DIRTY]]);
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// src/core/node.ts
|
|
935
|
+
var NodeImpl = class extends NodeBase {
|
|
936
|
+
// --- Dep configuration (set once) ---
|
|
937
|
+
_deps;
|
|
938
|
+
_fn;
|
|
939
|
+
_opts;
|
|
940
|
+
_hasDeps;
|
|
941
|
+
_isSingleDep;
|
|
942
|
+
_autoComplete;
|
|
943
|
+
// --- Wave tracking masks ---
|
|
944
|
+
_depDirtyMask;
|
|
945
|
+
_depSettledMask;
|
|
946
|
+
_depCompleteMask;
|
|
947
|
+
_allDepsCompleteMask;
|
|
948
|
+
// --- Identity-skip optimization ---
|
|
949
|
+
_lastDepValues;
|
|
950
|
+
_cleanup;
|
|
951
|
+
// --- Upstream bookkeeping ---
|
|
952
|
+
_upstreamUnsubs = [];
|
|
953
|
+
// --- Fn behavior flag ---
|
|
954
|
+
/** @internal Read by `describeNode` to infer `"operator"` label. */
|
|
955
|
+
_manualEmitUsed = false;
|
|
956
|
+
constructor(deps, fn, opts) {
|
|
957
|
+
super(opts);
|
|
958
|
+
this._deps = deps;
|
|
959
|
+
this._fn = fn;
|
|
960
|
+
this._opts = opts;
|
|
961
|
+
this._hasDeps = deps.length > 0;
|
|
962
|
+
this._isSingleDep = deps.length === 1 && fn != null;
|
|
963
|
+
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
964
|
+
if (!this._hasDeps && fn == null && this._cached !== NO_VALUE) {
|
|
965
|
+
this._status = "settled";
|
|
980
966
|
}
|
|
967
|
+
this._depDirtyMask = createBitSet(deps.length);
|
|
968
|
+
this._depSettledMask = createBitSet(deps.length);
|
|
969
|
+
this._depCompleteMask = createBitSet(deps.length);
|
|
970
|
+
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
971
|
+
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
972
|
+
this.down = this.down.bind(this);
|
|
973
|
+
this.up = this.up.bind(this);
|
|
981
974
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
975
|
+
// --- Meta node factory (called from base constructor) ---
|
|
976
|
+
_createMetaNode(key, initialValue, opts) {
|
|
977
|
+
return node({
|
|
978
|
+
initial: initialValue,
|
|
979
|
+
name: `${opts.name ?? "node"}:meta:${key}`,
|
|
980
|
+
describeKind: "state",
|
|
981
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
// --- Manual emit tracker (set by actions.down / actions.emit) ---
|
|
985
|
+
_onManualEmit() {
|
|
986
|
+
this._manualEmitUsed = true;
|
|
987
|
+
}
|
|
988
|
+
// --- Up / unsubscribe ---
|
|
989
|
+
up(messages, options) {
|
|
990
|
+
if (!this._hasDeps) return;
|
|
991
|
+
if (!options?.internal && this._guard != null) {
|
|
992
|
+
const actor = normalizeActor(options?.actor);
|
|
993
|
+
if (!this._guard(actor, "write")) {
|
|
994
|
+
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
995
|
+
}
|
|
996
|
+
this._recordMutation(actor);
|
|
985
997
|
}
|
|
986
|
-
this.
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
998
|
+
for (const dep of this._deps) {
|
|
999
|
+
if (options === void 0) {
|
|
1000
|
+
dep.up?.(messages);
|
|
1001
|
+
} else {
|
|
1002
|
+
dep.up?.(messages, options);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
_upInternal(messages) {
|
|
1007
|
+
if (!this._hasDeps) return;
|
|
1008
|
+
for (const dep of this._deps) {
|
|
1009
|
+
dep.up?.(messages, { internal: true });
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
unsubscribe() {
|
|
1013
|
+
if (!this._hasDeps) return;
|
|
1014
|
+
this._disconnectUpstream();
|
|
1015
|
+
}
|
|
1016
|
+
// --- Activation (first-subscriber / last-subscriber hooks) ---
|
|
1017
|
+
_onActivate() {
|
|
1018
|
+
if (this._hasDeps) {
|
|
1019
|
+
this._connectUpstream();
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (this._fn) {
|
|
990
1023
|
this._runFn();
|
|
1024
|
+
return;
|
|
991
1025
|
}
|
|
992
1026
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1027
|
+
_doDeactivate() {
|
|
1028
|
+
this._disconnectUpstream();
|
|
1029
|
+
const cleanup = this._cleanup;
|
|
1030
|
+
this._cleanup = void 0;
|
|
1031
|
+
cleanup?.();
|
|
1032
|
+
if (this._fn != null) {
|
|
1033
|
+
this._cached = NO_VALUE;
|
|
1034
|
+
this._lastDepValues = void 0;
|
|
1035
|
+
}
|
|
1036
|
+
if (this._hasDeps || this._fn != null) {
|
|
1037
|
+
this._status = "disconnected";
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// --- INVALIDATE / TEARDOWN hooks (clear fn state) ---
|
|
1041
|
+
_onInvalidate() {
|
|
1042
|
+
const cleanup = this._cleanup;
|
|
1043
|
+
this._cleanup = void 0;
|
|
1044
|
+
cleanup?.();
|
|
1045
|
+
this._lastDepValues = void 0;
|
|
1046
|
+
}
|
|
1047
|
+
_onTeardown() {
|
|
1048
|
+
const cleanup = this._cleanup;
|
|
1049
|
+
this._cleanup = void 0;
|
|
1050
|
+
cleanup?.();
|
|
1051
|
+
}
|
|
1052
|
+
// --- Upstream connect / disconnect ---
|
|
1053
|
+
_connectUpstream() {
|
|
1054
|
+
if (!this._hasDeps) return;
|
|
1055
|
+
if (this._upstreamUnsubs.length > 0) return;
|
|
1056
|
+
this._depDirtyMask.setAll();
|
|
1057
|
+
this._depSettledMask.reset();
|
|
1058
|
+
this._depCompleteMask.reset();
|
|
1059
|
+
const depValuesBefore = this._lastDepValues;
|
|
1060
|
+
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1061
|
+
for (let i = 0; i < this._deps.length; i += 1) {
|
|
1062
|
+
const dep = this._deps[i];
|
|
1063
|
+
this._upstreamUnsubs.push(
|
|
1064
|
+
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
if (this._fn && this._onMessage && !this._terminal && this._lastDepValues === depValuesBefore) {
|
|
1068
|
+
this._runFn();
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
_disconnectUpstream() {
|
|
1072
|
+
if (this._upstreamUnsubs.length === 0) return;
|
|
1073
|
+
for (const unsub of this._upstreamUnsubs.splice(0)) {
|
|
1074
|
+
unsub();
|
|
996
1075
|
}
|
|
1076
|
+
this._depDirtyMask.reset();
|
|
1077
|
+
this._depSettledMask.reset();
|
|
1078
|
+
this._depCompleteMask.reset();
|
|
997
1079
|
}
|
|
1080
|
+
// --- Wave handling ---
|
|
998
1081
|
_handleDepMessages(index, messages) {
|
|
999
1082
|
for (const msg of messages) {
|
|
1000
|
-
this.
|
|
1083
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
1001
1084
|
const t = msg[0];
|
|
1002
1085
|
if (this._onMessage) {
|
|
1003
1086
|
try {
|
|
1004
|
-
|
|
1087
|
+
const consumed = this._onMessage(msg, index, this._actions);
|
|
1088
|
+
if (consumed) {
|
|
1089
|
+
if (t === START) {
|
|
1090
|
+
this._depDirtyMask.clear(index);
|
|
1091
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1092
|
+
this._depDirtyMask.reset();
|
|
1093
|
+
this._depSettledMask.reset();
|
|
1094
|
+
this._runFn();
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1005
1099
|
} catch (err) {
|
|
1006
1100
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1007
1101
|
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
@@ -1011,6 +1105,7 @@ var NodeImpl = class {
|
|
|
1011
1105
|
return;
|
|
1012
1106
|
}
|
|
1013
1107
|
}
|
|
1108
|
+
if (messageTier(t) < 1) continue;
|
|
1014
1109
|
if (!this._fn) {
|
|
1015
1110
|
if (t === COMPLETE && this._deps.length > 1) {
|
|
1016
1111
|
this._depCompleteMask.set(index);
|
|
@@ -1054,53 +1149,85 @@ var NodeImpl = class {
|
|
|
1054
1149
|
this._downInternal([msg]);
|
|
1055
1150
|
}
|
|
1056
1151
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
this.
|
|
1060
|
-
this.
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
this._status = "settled";
|
|
1064
|
-
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1065
|
-
this._connecting = true;
|
|
1066
|
-
try {
|
|
1067
|
-
for (let i = 0; i < this._deps.length; i += 1) {
|
|
1068
|
-
const dep = this._deps[i];
|
|
1069
|
-
this._upstreamUnsubs.push(
|
|
1070
|
-
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
} finally {
|
|
1074
|
-
this._connecting = false;
|
|
1152
|
+
_onDepDirty(index) {
|
|
1153
|
+
const wasDirty = this._depDirtyMask.has(index);
|
|
1154
|
+
this._depDirtyMask.set(index);
|
|
1155
|
+
this._depSettledMask.clear(index);
|
|
1156
|
+
if (!wasDirty) {
|
|
1157
|
+
this._downInternal([[DIRTY]]);
|
|
1075
1158
|
}
|
|
1076
|
-
|
|
1159
|
+
}
|
|
1160
|
+
_onDepSettled(index) {
|
|
1161
|
+
if (!this._depDirtyMask.has(index)) {
|
|
1162
|
+
this._onDepDirty(index);
|
|
1163
|
+
}
|
|
1164
|
+
this._depSettledMask.set(index);
|
|
1165
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1166
|
+
this._depDirtyMask.reset();
|
|
1167
|
+
this._depSettledMask.reset();
|
|
1077
1168
|
this._runFn();
|
|
1078
1169
|
}
|
|
1079
1170
|
}
|
|
1080
|
-
|
|
1081
|
-
if (
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
this._cleanup = void 0;
|
|
1085
|
-
producerCleanup?.();
|
|
1086
|
-
}
|
|
1087
|
-
_startProducer() {
|
|
1088
|
-
if (this._deps.length !== 0 || !this._fn || this._producerStarted) return;
|
|
1089
|
-
this._producerStarted = true;
|
|
1090
|
-
this._runFn();
|
|
1171
|
+
_maybeCompleteFromDeps() {
|
|
1172
|
+
if (this._autoComplete && this._deps.length > 0 && this._depCompleteMask.covers(this._allDepsCompleteMask)) {
|
|
1173
|
+
this._downInternal([[COMPLETE]]);
|
|
1174
|
+
}
|
|
1091
1175
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1176
|
+
// --- Fn execution ---
|
|
1177
|
+
_runFn() {
|
|
1178
|
+
if (!this._fn) return;
|
|
1179
|
+
if (this._terminal && !this._resubscribable) return;
|
|
1180
|
+
try {
|
|
1181
|
+
const n = this._deps.length;
|
|
1182
|
+
const depValues = new Array(n);
|
|
1183
|
+
for (let i = 0; i < n; i++) depValues[i] = this._deps[i].get();
|
|
1184
|
+
const prev = this._lastDepValues;
|
|
1185
|
+
if (n > 0 && prev != null && prev.length === n) {
|
|
1186
|
+
let allSame = true;
|
|
1187
|
+
for (let i = 0; i < n; i++) {
|
|
1188
|
+
if (!Object.is(depValues[i], prev[i])) {
|
|
1189
|
+
allSame = false;
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (allSame) {
|
|
1194
|
+
if (this._status === "dirty") {
|
|
1195
|
+
this._downInternal([[RESOLVED]]);
|
|
1196
|
+
}
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
const prevCleanup = this._cleanup;
|
|
1201
|
+
this._cleanup = void 0;
|
|
1202
|
+
prevCleanup?.();
|
|
1203
|
+
this._manualEmitUsed = false;
|
|
1204
|
+
this._lastDepValues = depValues;
|
|
1205
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
1206
|
+
const out = this._fn(depValues, this._actions);
|
|
1207
|
+
if (isCleanupResult(out)) {
|
|
1208
|
+
this._cleanup = out.cleanup;
|
|
1209
|
+
if (this._manualEmitUsed) return;
|
|
1210
|
+
if ("value" in out) {
|
|
1211
|
+
this._downAutoValue(out.value);
|
|
1212
|
+
}
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
if (isCleanupFn(out)) {
|
|
1216
|
+
this._cleanup = out;
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
if (this._manualEmitUsed) return;
|
|
1220
|
+
if (out === void 0) return;
|
|
1221
|
+
this._downAutoValue(out);
|
|
1222
|
+
} catch (err) {
|
|
1223
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1224
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
1225
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
1096
1226
|
}
|
|
1097
|
-
this._connected = false;
|
|
1098
|
-
this._depDirtyMask.reset();
|
|
1099
|
-
this._depSettledMask.reset();
|
|
1100
|
-
this._depCompleteMask.reset();
|
|
1101
|
-
this._status = "disconnected";
|
|
1102
1227
|
}
|
|
1103
1228
|
};
|
|
1229
|
+
var isNodeArray = (value) => Array.isArray(value);
|
|
1230
|
+
var isNodeOptions = (value) => typeof value === "object" && value != null && !Array.isArray(value);
|
|
1104
1231
|
function node(depsOrFn, fnOrOpts, optsArg) {
|
|
1105
1232
|
const deps = isNodeArray(depsOrFn) ? depsOrFn : [];
|
|
1106
1233
|
const fn = typeof depsOrFn === "function" ? depsOrFn : typeof fnOrOpts === "function" ? fnOrOpts : void 0;
|
|
@@ -1167,230 +1294,47 @@ function bridge(from, to, opts) {
|
|
|
1167
1294
|
}
|
|
1168
1295
|
|
|
1169
1296
|
// src/core/dynamic-node.ts
|
|
1297
|
+
var MAX_RERUN = 16;
|
|
1170
1298
|
function dynamicNode(fn, opts) {
|
|
1171
1299
|
return new DynamicNodeImpl(fn, opts ?? {});
|
|
1172
1300
|
}
|
|
1173
|
-
var DynamicNodeImpl = class {
|
|
1174
|
-
_optsName;
|
|
1175
|
-
_registryName;
|
|
1176
|
-
_describeKind;
|
|
1177
|
-
meta;
|
|
1301
|
+
var DynamicNodeImpl = class extends NodeBase {
|
|
1178
1302
|
_fn;
|
|
1179
|
-
_equals;
|
|
1180
|
-
_resubscribable;
|
|
1181
|
-
_resetOnTeardown;
|
|
1182
1303
|
_autoComplete;
|
|
1183
|
-
_onMessage;
|
|
1184
|
-
_onResubscribe;
|
|
1185
|
-
/** @internal — read by {@link describeNode} for `accessHintForGuard`. */
|
|
1186
|
-
_guard;
|
|
1187
|
-
_lastMutation;
|
|
1188
|
-
_inspectorHook;
|
|
1189
|
-
// Sink tracking
|
|
1190
|
-
_sinkCount = 0;
|
|
1191
|
-
_singleDepSinkCount = 0;
|
|
1192
|
-
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
1193
|
-
// Actions object (for onMessage handler)
|
|
1194
|
-
_actions;
|
|
1195
|
-
_boundDownToSinks;
|
|
1196
|
-
// Mutable state
|
|
1197
|
-
_cached = NO_VALUE;
|
|
1198
|
-
_status = "disconnected";
|
|
1199
|
-
_terminal = false;
|
|
1200
|
-
_connected = false;
|
|
1201
|
-
_rewiring = false;
|
|
1202
|
-
// re-entrancy guard
|
|
1203
1304
|
// Dynamic deps tracking
|
|
1305
|
+
/** @internal Read by `describeNode`. */
|
|
1204
1306
|
_deps = [];
|
|
1205
1307
|
_depUnsubs = [];
|
|
1206
1308
|
_depIndexMap = /* @__PURE__ */ new Map();
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1309
|
+
_depDirtyBits = /* @__PURE__ */ new Set();
|
|
1310
|
+
_depSettledBits = /* @__PURE__ */ new Set();
|
|
1311
|
+
_depCompleteBits = /* @__PURE__ */ new Set();
|
|
1312
|
+
// Execution state
|
|
1313
|
+
_running = false;
|
|
1314
|
+
_rewiring = false;
|
|
1315
|
+
_bufferedDepMessages = [];
|
|
1316
|
+
_trackedValues = /* @__PURE__ */ new Map();
|
|
1317
|
+
_rerunCount = 0;
|
|
1213
1318
|
constructor(fn, opts) {
|
|
1319
|
+
super(opts);
|
|
1214
1320
|
this._fn = fn;
|
|
1215
|
-
this._optsName = opts.name;
|
|
1216
|
-
this._describeKind = opts.describeKind;
|
|
1217
|
-
this._equals = opts.equals ?? Object.is;
|
|
1218
|
-
this._resubscribable = opts.resubscribable ?? false;
|
|
1219
|
-
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
1220
1321
|
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
1221
|
-
this.
|
|
1222
|
-
this.
|
|
1223
|
-
this._guard = opts.guard;
|
|
1224
|
-
this._inspectorHook = void 0;
|
|
1225
|
-
const meta = {};
|
|
1226
|
-
for (const [k, v] of Object.entries(opts.meta ?? {})) {
|
|
1227
|
-
meta[k] = node({
|
|
1228
|
-
initial: v,
|
|
1229
|
-
name: `${opts.name ?? "dynamicNode"}:meta:${k}`,
|
|
1230
|
-
describeKind: "state",
|
|
1231
|
-
...opts.guard != null ? { guard: opts.guard } : {}
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
|
-
Object.freeze(meta);
|
|
1235
|
-
this.meta = meta;
|
|
1236
|
-
const self = this;
|
|
1237
|
-
this._actions = {
|
|
1238
|
-
down(messages) {
|
|
1239
|
-
self._downInternal(messages);
|
|
1240
|
-
},
|
|
1241
|
-
emit(value) {
|
|
1242
|
-
self._downAutoValue(value);
|
|
1243
|
-
},
|
|
1244
|
-
up(messages) {
|
|
1245
|
-
for (const dep of self._deps) {
|
|
1246
|
-
dep.up?.(messages, { internal: true });
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
};
|
|
1250
|
-
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
1322
|
+
this.down = this.down.bind(this);
|
|
1323
|
+
this.up = this.up.bind(this);
|
|
1251
1324
|
}
|
|
1252
|
-
|
|
1253
|
-
return
|
|
1325
|
+
_createMetaNode(key, initialValue, opts) {
|
|
1326
|
+
return node({
|
|
1327
|
+
initial: initialValue,
|
|
1328
|
+
name: `${opts.name ?? "dynamicNode"}:meta:${key}`,
|
|
1329
|
+
describeKind: "state",
|
|
1330
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
1331
|
+
});
|
|
1254
1332
|
}
|
|
1255
|
-
/**
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
this._registryName = localName;
|
|
1259
|
-
}
|
|
1260
|
-
/**
|
|
1261
|
-
* @internal Attach/remove inspector hook for graph-level observability.
|
|
1262
|
-
* Returns a disposer that restores the previous hook.
|
|
1263
|
-
*/
|
|
1264
|
-
_setInspectorHook(hook) {
|
|
1265
|
-
const prev = this._inspectorHook;
|
|
1266
|
-
this._inspectorHook = hook;
|
|
1267
|
-
return () => {
|
|
1268
|
-
if (this._inspectorHook === hook) {
|
|
1269
|
-
this._inspectorHook = prev;
|
|
1270
|
-
}
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
get status() {
|
|
1274
|
-
return this._status;
|
|
1275
|
-
}
|
|
1276
|
-
get lastMutation() {
|
|
1277
|
-
return this._lastMutation;
|
|
1278
|
-
}
|
|
1279
|
-
/** Versioning not yet supported on DynamicNodeImpl. */
|
|
1280
|
-
get v() {
|
|
1281
|
-
return void 0;
|
|
1282
|
-
}
|
|
1283
|
-
hasGuard() {
|
|
1284
|
-
return this._guard != null;
|
|
1285
|
-
}
|
|
1286
|
-
allowsObserve(actor) {
|
|
1287
|
-
if (this._guard == null) return true;
|
|
1288
|
-
return this._guard(normalizeActor(actor), "observe");
|
|
1289
|
-
}
|
|
1290
|
-
get() {
|
|
1291
|
-
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
1292
|
-
}
|
|
1293
|
-
down(messages, options) {
|
|
1294
|
-
if (messages.length === 0) return;
|
|
1295
|
-
if (!options?.internal && this._guard != null) {
|
|
1296
|
-
const actor = normalizeActor(options?.actor);
|
|
1297
|
-
const delivery = options?.delivery ?? "write";
|
|
1298
|
-
const action = delivery === "signal" ? "signal" : "write";
|
|
1299
|
-
if (!this._guard(actor, action)) {
|
|
1300
|
-
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
1301
|
-
}
|
|
1302
|
-
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
1303
|
-
}
|
|
1304
|
-
this._downInternal(messages);
|
|
1305
|
-
}
|
|
1306
|
-
_downInternal(messages) {
|
|
1307
|
-
if (messages.length === 0) return;
|
|
1308
|
-
let sinkMessages = messages;
|
|
1309
|
-
if (this._terminal && !this._resubscribable) {
|
|
1310
|
-
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
1311
|
-
if (pass.length === 0) return;
|
|
1312
|
-
sinkMessages = pass;
|
|
1313
|
-
}
|
|
1314
|
-
this._handleLocalLifecycle(sinkMessages);
|
|
1315
|
-
if (this._canSkipDirty()) {
|
|
1316
|
-
let hasPhase2 = false;
|
|
1317
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
1318
|
-
const t = sinkMessages[i][0];
|
|
1319
|
-
if (t === DATA || t === RESOLVED) {
|
|
1320
|
-
hasPhase2 = true;
|
|
1321
|
-
break;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
if (hasPhase2) {
|
|
1325
|
-
const filtered = [];
|
|
1326
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
1327
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
1328
|
-
}
|
|
1329
|
-
if (filtered.length > 0) {
|
|
1330
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
1331
|
-
}
|
|
1332
|
-
return;
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
1336
|
-
}
|
|
1337
|
-
_canSkipDirty() {
|
|
1338
|
-
return this._sinkCount === 1 && this._singleDepSinkCount === 1;
|
|
1339
|
-
}
|
|
1340
|
-
subscribe(sink, hints) {
|
|
1341
|
-
if (hints?.actor != null && this._guard != null) {
|
|
1342
|
-
const actor = normalizeActor(hints.actor);
|
|
1343
|
-
if (!this._guard(actor, "observe")) {
|
|
1344
|
-
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
if (this._terminal && this._resubscribable) {
|
|
1348
|
-
this._terminal = false;
|
|
1349
|
-
this._cached = NO_VALUE;
|
|
1350
|
-
this._status = "disconnected";
|
|
1351
|
-
this._onResubscribe?.();
|
|
1352
|
-
}
|
|
1353
|
-
this._sinkCount += 1;
|
|
1354
|
-
if (hints?.singleDep) {
|
|
1355
|
-
this._singleDepSinkCount += 1;
|
|
1356
|
-
this._singleDepSinks.add(sink);
|
|
1357
|
-
}
|
|
1358
|
-
if (this._sinks == null) {
|
|
1359
|
-
this._sinks = sink;
|
|
1360
|
-
} else if (typeof this._sinks === "function") {
|
|
1361
|
-
this._sinks = /* @__PURE__ */ new Set([this._sinks, sink]);
|
|
1362
|
-
} else {
|
|
1363
|
-
this._sinks.add(sink);
|
|
1364
|
-
}
|
|
1365
|
-
if (!this._connected) {
|
|
1366
|
-
this._connect();
|
|
1367
|
-
}
|
|
1368
|
-
let removed = false;
|
|
1369
|
-
return () => {
|
|
1370
|
-
if (removed) return;
|
|
1371
|
-
removed = true;
|
|
1372
|
-
this._sinkCount -= 1;
|
|
1373
|
-
if (this._singleDepSinks.has(sink)) {
|
|
1374
|
-
this._singleDepSinkCount -= 1;
|
|
1375
|
-
this._singleDepSinks.delete(sink);
|
|
1376
|
-
}
|
|
1377
|
-
if (this._sinks == null) return;
|
|
1378
|
-
if (typeof this._sinks === "function") {
|
|
1379
|
-
if (this._sinks === sink) this._sinks = null;
|
|
1380
|
-
} else {
|
|
1381
|
-
this._sinks.delete(sink);
|
|
1382
|
-
if (this._sinks.size === 1) {
|
|
1383
|
-
const [only] = this._sinks;
|
|
1384
|
-
this._sinks = only;
|
|
1385
|
-
} else if (this._sinks.size === 0) {
|
|
1386
|
-
this._sinks = null;
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
if (this._sinks == null) {
|
|
1390
|
-
this._disconnect();
|
|
1391
|
-
}
|
|
1392
|
-
};
|
|
1333
|
+
/** Versioning not supported on DynamicNodeImpl (override base). */
|
|
1334
|
+
get v() {
|
|
1335
|
+
return void 0;
|
|
1393
1336
|
}
|
|
1337
|
+
// --- Up / unsubscribe ---
|
|
1394
1338
|
up(messages, options) {
|
|
1395
1339
|
if (this._deps.length === 0) return;
|
|
1396
1340
|
if (!options?.internal && this._guard != null) {
|
|
@@ -1398,221 +1342,227 @@ var DynamicNodeImpl = class {
|
|
|
1398
1342
|
if (!this._guard(actor, "write")) {
|
|
1399
1343
|
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
1400
1344
|
}
|
|
1401
|
-
this.
|
|
1345
|
+
this._recordMutation(actor);
|
|
1402
1346
|
}
|
|
1403
1347
|
for (const dep of this._deps) {
|
|
1404
1348
|
dep.up?.(messages, options);
|
|
1405
1349
|
}
|
|
1406
1350
|
}
|
|
1407
|
-
|
|
1408
|
-
this.
|
|
1409
|
-
|
|
1410
|
-
// --- Private methods ---
|
|
1411
|
-
_downToSinks(messages) {
|
|
1412
|
-
if (this._sinks == null) return;
|
|
1413
|
-
if (typeof this._sinks === "function") {
|
|
1414
|
-
this._sinks(messages);
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
const snapshot = [...this._sinks];
|
|
1418
|
-
for (const sink of snapshot) {
|
|
1419
|
-
sink(messages);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
_handleLocalLifecycle(messages) {
|
|
1423
|
-
for (const m of messages) {
|
|
1424
|
-
const t = m[0];
|
|
1425
|
-
if (t === DATA) this._cached = m[1];
|
|
1426
|
-
if (t === INVALIDATE) {
|
|
1427
|
-
this._cached = NO_VALUE;
|
|
1428
|
-
this._status = "dirty";
|
|
1429
|
-
}
|
|
1430
|
-
if (t === DATA) {
|
|
1431
|
-
this._status = "settled";
|
|
1432
|
-
} else if (t === RESOLVED) {
|
|
1433
|
-
this._status = "resolved";
|
|
1434
|
-
} else if (t === DIRTY) {
|
|
1435
|
-
this._status = "dirty";
|
|
1436
|
-
} else if (t === COMPLETE) {
|
|
1437
|
-
this._status = "completed";
|
|
1438
|
-
this._terminal = true;
|
|
1439
|
-
} else if (t === ERROR) {
|
|
1440
|
-
this._status = "errored";
|
|
1441
|
-
this._terminal = true;
|
|
1442
|
-
}
|
|
1443
|
-
if (t === TEARDOWN) {
|
|
1444
|
-
if (this._resetOnTeardown) this._cached = NO_VALUE;
|
|
1445
|
-
try {
|
|
1446
|
-
this._propagateToMeta(t);
|
|
1447
|
-
} finally {
|
|
1448
|
-
this._disconnect();
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
1452
|
-
this._propagateToMeta(t);
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
/** Propagate a signal to all companion meta nodes (best-effort). */
|
|
1457
|
-
_propagateToMeta(t) {
|
|
1458
|
-
for (const metaNode of Object.values(this.meta)) {
|
|
1459
|
-
try {
|
|
1460
|
-
metaNode.down([[t]], { internal: true });
|
|
1461
|
-
} catch {
|
|
1462
|
-
}
|
|
1351
|
+
_upInternal(messages) {
|
|
1352
|
+
for (const dep of this._deps) {
|
|
1353
|
+
dep.up?.(messages, { internal: true });
|
|
1463
1354
|
}
|
|
1464
1355
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
let unchanged;
|
|
1468
|
-
try {
|
|
1469
|
-
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
1470
|
-
} catch (eqErr) {
|
|
1471
|
-
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
1472
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
|
|
1473
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
if (unchanged) {
|
|
1477
|
-
this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
this._cached = value;
|
|
1481
|
-
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
1356
|
+
unsubscribe() {
|
|
1357
|
+
this._disconnect();
|
|
1482
1358
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
this._connected = true;
|
|
1486
|
-
this._status = "settled";
|
|
1487
|
-
this._dirtyBits.clear();
|
|
1488
|
-
this._settledBits.clear();
|
|
1489
|
-
this._completeBits.clear();
|
|
1359
|
+
// --- Activation hooks ---
|
|
1360
|
+
_onActivate() {
|
|
1490
1361
|
this._runFn();
|
|
1491
1362
|
}
|
|
1363
|
+
_doDeactivate() {
|
|
1364
|
+
this._disconnect();
|
|
1365
|
+
}
|
|
1492
1366
|
_disconnect() {
|
|
1493
|
-
if (!this._connected) return;
|
|
1494
1367
|
for (const unsub of this._depUnsubs) unsub();
|
|
1495
1368
|
this._depUnsubs = [];
|
|
1496
1369
|
this._deps = [];
|
|
1497
1370
|
this._depIndexMap.clear();
|
|
1498
|
-
this.
|
|
1499
|
-
this.
|
|
1500
|
-
this.
|
|
1501
|
-
this.
|
|
1371
|
+
this._depDirtyBits.clear();
|
|
1372
|
+
this._depSettledBits.clear();
|
|
1373
|
+
this._depCompleteBits.clear();
|
|
1374
|
+
this._cached = NO_VALUE;
|
|
1502
1375
|
this._status = "disconnected";
|
|
1503
1376
|
}
|
|
1377
|
+
// --- Fn execution with rewire buffer ---
|
|
1504
1378
|
_runFn() {
|
|
1505
1379
|
if (this._terminal && !this._resubscribable) return;
|
|
1506
|
-
if (this.
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if (!trackedSet.has(dep)) {
|
|
1511
|
-
trackedSet.add(dep);
|
|
1512
|
-
trackedDeps.push(dep);
|
|
1513
|
-
}
|
|
1514
|
-
return dep.get();
|
|
1515
|
-
};
|
|
1380
|
+
if (this._running) return;
|
|
1381
|
+
this._running = true;
|
|
1382
|
+
this._rerunCount = 0;
|
|
1383
|
+
let result;
|
|
1516
1384
|
try {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1385
|
+
for (; ; ) {
|
|
1386
|
+
const trackedDeps = [];
|
|
1387
|
+
const trackedValuesMap = /* @__PURE__ */ new Map();
|
|
1388
|
+
const trackedSet = /* @__PURE__ */ new Set();
|
|
1389
|
+
const get = (dep) => {
|
|
1390
|
+
if (!trackedSet.has(dep)) {
|
|
1391
|
+
trackedSet.add(dep);
|
|
1392
|
+
trackedDeps.push(dep);
|
|
1393
|
+
trackedValuesMap.set(dep, dep.get());
|
|
1394
|
+
}
|
|
1395
|
+
return dep.get();
|
|
1396
|
+
};
|
|
1397
|
+
this._trackedValues = trackedValuesMap;
|
|
1398
|
+
const depValues = [];
|
|
1399
|
+
for (const dep of this._deps) depValues.push(dep.get());
|
|
1400
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
1401
|
+
try {
|
|
1402
|
+
result = this._fn(get);
|
|
1403
|
+
} catch (err) {
|
|
1404
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1405
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, {
|
|
1406
|
+
cause: err
|
|
1407
|
+
});
|
|
1408
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
this._rewiring = true;
|
|
1412
|
+
this._bufferedDepMessages = [];
|
|
1413
|
+
try {
|
|
1414
|
+
this._rewire(trackedDeps);
|
|
1415
|
+
} finally {
|
|
1416
|
+
this._rewiring = false;
|
|
1417
|
+
}
|
|
1418
|
+
let needsRerun = false;
|
|
1419
|
+
for (const entry of this._bufferedDepMessages) {
|
|
1420
|
+
for (const msg of entry.msgs) {
|
|
1421
|
+
if (msg[0] === DATA) {
|
|
1422
|
+
const dep = this._deps[entry.index];
|
|
1423
|
+
const trackedValue = dep != null ? this._trackedValues.get(dep) : void 0;
|
|
1424
|
+
const actualValue = msg[1];
|
|
1425
|
+
if (!this._equals(trackedValue, actualValue)) {
|
|
1426
|
+
needsRerun = true;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
if (needsRerun) break;
|
|
1432
|
+
}
|
|
1433
|
+
if (needsRerun) {
|
|
1434
|
+
this._rerunCount += 1;
|
|
1435
|
+
if (this._rerunCount > MAX_RERUN) {
|
|
1436
|
+
this._bufferedDepMessages = [];
|
|
1437
|
+
this._downInternal([
|
|
1438
|
+
[
|
|
1439
|
+
ERROR,
|
|
1440
|
+
new Error(
|
|
1441
|
+
`dynamicNode "${this.name ?? "anonymous"}": rewire did not stabilize within ${MAX_RERUN} iterations`
|
|
1442
|
+
)
|
|
1443
|
+
]
|
|
1444
|
+
]);
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
this._bufferedDepMessages = [];
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
const drain = this._bufferedDepMessages;
|
|
1451
|
+
this._bufferedDepMessages = [];
|
|
1452
|
+
for (const entry of drain) {
|
|
1453
|
+
for (const msg of entry.msgs) {
|
|
1454
|
+
this._updateMasksForMessage(entry.index, msg);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
this._depDirtyBits.clear();
|
|
1458
|
+
this._depSettledBits.clear();
|
|
1459
|
+
break;
|
|
1520
1460
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
this._rewire(trackedDeps);
|
|
1524
|
-
if (result === void 0) return;
|
|
1525
|
-
this._downAutoValue(result);
|
|
1526
|
-
} catch (err) {
|
|
1527
|
-
this._downInternal([[ERROR, err]]);
|
|
1461
|
+
} finally {
|
|
1462
|
+
this._running = false;
|
|
1528
1463
|
}
|
|
1464
|
+
this._downAutoValue(result);
|
|
1529
1465
|
}
|
|
1530
1466
|
_rewire(newDeps) {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
1547
|
-
newUnsubs.push(unsub);
|
|
1548
|
-
}
|
|
1467
|
+
const oldMap = this._depIndexMap;
|
|
1468
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
1469
|
+
const newUnsubs = [];
|
|
1470
|
+
for (let i = 0; i < newDeps.length; i++) {
|
|
1471
|
+
const dep = newDeps[i];
|
|
1472
|
+
newMap.set(dep, i);
|
|
1473
|
+
const oldIdx = oldMap.get(dep);
|
|
1474
|
+
if (oldIdx !== void 0) {
|
|
1475
|
+
newUnsubs.push(this._depUnsubs[oldIdx]);
|
|
1476
|
+
this._depUnsubs[oldIdx] = () => {
|
|
1477
|
+
};
|
|
1478
|
+
} else {
|
|
1479
|
+
const idx = i;
|
|
1480
|
+
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
1481
|
+
newUnsubs.push(unsub);
|
|
1549
1482
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1483
|
+
}
|
|
1484
|
+
for (const [dep, oldIdx] of oldMap) {
|
|
1485
|
+
if (!newMap.has(dep)) {
|
|
1486
|
+
this._depUnsubs[oldIdx]();
|
|
1554
1487
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1488
|
+
}
|
|
1489
|
+
this._deps = newDeps;
|
|
1490
|
+
this._depUnsubs = newUnsubs;
|
|
1491
|
+
this._depIndexMap = newMap;
|
|
1492
|
+
this._depDirtyBits.clear();
|
|
1493
|
+
this._depSettledBits.clear();
|
|
1494
|
+
const newCompleteBits = /* @__PURE__ */ new Set();
|
|
1495
|
+
for (const oldIdx of this._depCompleteBits) {
|
|
1496
|
+
for (const [dep, idx] of oldMap) {
|
|
1497
|
+
if (idx === oldIdx && newMap.has(dep)) {
|
|
1564
1498
|
newCompleteBits.add(newMap.get(dep));
|
|
1499
|
+
break;
|
|
1565
1500
|
}
|
|
1566
1501
|
}
|
|
1567
|
-
this._completeBits = newCompleteBits;
|
|
1568
|
-
} finally {
|
|
1569
|
-
this._rewiring = false;
|
|
1570
1502
|
}
|
|
1503
|
+
this._depCompleteBits = newCompleteBits;
|
|
1571
1504
|
}
|
|
1505
|
+
// --- Dep message handling ---
|
|
1572
1506
|
_handleDepMessages(index, messages) {
|
|
1573
|
-
if (this._rewiring)
|
|
1507
|
+
if (this._rewiring) {
|
|
1508
|
+
this._bufferedDepMessages.push({ index, msgs: messages });
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1574
1511
|
for (const msg of messages) {
|
|
1575
|
-
this.
|
|
1512
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
1576
1513
|
const t = msg[0];
|
|
1577
1514
|
if (this._onMessage) {
|
|
1578
1515
|
try {
|
|
1579
1516
|
if (this._onMessage(msg, index, this._actions)) continue;
|
|
1580
1517
|
} catch (err) {
|
|
1581
|
-
|
|
1518
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1519
|
+
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
1520
|
+
cause: err
|
|
1521
|
+
});
|
|
1522
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
1582
1523
|
return;
|
|
1583
1524
|
}
|
|
1584
1525
|
}
|
|
1526
|
+
if (messageTier(t) < 1) continue;
|
|
1585
1527
|
if (t === DIRTY) {
|
|
1586
|
-
this.
|
|
1587
|
-
this.
|
|
1588
|
-
|
|
1589
|
-
|
|
1528
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
1529
|
+
this._depDirtyBits.add(index);
|
|
1530
|
+
this._depSettledBits.delete(index);
|
|
1531
|
+
if (wasEmpty) {
|
|
1532
|
+
this._downInternal([[DIRTY]]);
|
|
1590
1533
|
}
|
|
1591
1534
|
continue;
|
|
1592
1535
|
}
|
|
1593
1536
|
if (t === DATA || t === RESOLVED) {
|
|
1594
|
-
if (!this.
|
|
1595
|
-
this.
|
|
1596
|
-
|
|
1537
|
+
if (!this._depDirtyBits.has(index)) {
|
|
1538
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
1539
|
+
this._depDirtyBits.add(index);
|
|
1540
|
+
if (wasEmpty) {
|
|
1541
|
+
this._downInternal([[DIRTY]]);
|
|
1542
|
+
}
|
|
1597
1543
|
}
|
|
1598
|
-
this.
|
|
1544
|
+
this._depSettledBits.add(index);
|
|
1599
1545
|
if (this._allDirtySettled()) {
|
|
1600
|
-
this.
|
|
1601
|
-
this.
|
|
1602
|
-
this.
|
|
1546
|
+
this._depDirtyBits.clear();
|
|
1547
|
+
this._depSettledBits.clear();
|
|
1548
|
+
if (!this._running) {
|
|
1549
|
+
if (this._depValuesDifferFromTracked()) {
|
|
1550
|
+
this._runFn();
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1603
1553
|
}
|
|
1604
1554
|
continue;
|
|
1605
1555
|
}
|
|
1606
1556
|
if (t === COMPLETE) {
|
|
1607
|
-
this.
|
|
1608
|
-
this.
|
|
1609
|
-
this.
|
|
1557
|
+
this._depCompleteBits.add(index);
|
|
1558
|
+
this._depDirtyBits.delete(index);
|
|
1559
|
+
this._depSettledBits.delete(index);
|
|
1610
1560
|
if (this._allDirtySettled()) {
|
|
1611
|
-
this.
|
|
1612
|
-
this.
|
|
1613
|
-
this._runFn();
|
|
1561
|
+
this._depDirtyBits.clear();
|
|
1562
|
+
this._depSettledBits.clear();
|
|
1563
|
+
if (!this._running) this._runFn();
|
|
1614
1564
|
}
|
|
1615
|
-
if (this._autoComplete && this.
|
|
1565
|
+
if (this._autoComplete && this._depCompleteBits.size >= this._deps.length && this._deps.length > 0) {
|
|
1616
1566
|
this._downInternal([[COMPLETE]]);
|
|
1617
1567
|
}
|
|
1618
1568
|
continue;
|
|
@@ -1628,13 +1578,46 @@ var DynamicNodeImpl = class {
|
|
|
1628
1578
|
this._downInternal([msg]);
|
|
1629
1579
|
}
|
|
1630
1580
|
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Update dep masks for a message without triggering `_runFn` — used
|
|
1583
|
+
* during post-rewire drain so the wave state is consistent with the
|
|
1584
|
+
* buffered activation cascade without recursing.
|
|
1585
|
+
*/
|
|
1586
|
+
_updateMasksForMessage(index, msg) {
|
|
1587
|
+
const t = msg[0];
|
|
1588
|
+
if (t === DIRTY) {
|
|
1589
|
+
this._depDirtyBits.add(index);
|
|
1590
|
+
this._depSettledBits.delete(index);
|
|
1591
|
+
} else if (t === DATA || t === RESOLVED) {
|
|
1592
|
+
this._depDirtyBits.add(index);
|
|
1593
|
+
this._depSettledBits.add(index);
|
|
1594
|
+
} else if (t === COMPLETE) {
|
|
1595
|
+
this._depCompleteBits.add(index);
|
|
1596
|
+
this._depDirtyBits.delete(index);
|
|
1597
|
+
this._depSettledBits.delete(index);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1631
1600
|
_allDirtySettled() {
|
|
1632
|
-
if (this.
|
|
1633
|
-
for (const idx of this.
|
|
1634
|
-
if (!this.
|
|
1601
|
+
if (this._depDirtyBits.size === 0) return false;
|
|
1602
|
+
for (const idx of this._depDirtyBits) {
|
|
1603
|
+
if (!this._depSettledBits.has(idx)) return false;
|
|
1635
1604
|
}
|
|
1636
1605
|
return true;
|
|
1637
1606
|
}
|
|
1607
|
+
/**
|
|
1608
|
+
* True if any current dep value differs from what the last `_runFn`
|
|
1609
|
+
* saw via `get()`. Used to suppress redundant re-runs when deferred
|
|
1610
|
+
* handshake messages arrive after `_rewire` for a dep whose value
|
|
1611
|
+
* already matches `_trackedValues`.
|
|
1612
|
+
*/
|
|
1613
|
+
_depValuesDifferFromTracked() {
|
|
1614
|
+
for (const dep of this._deps) {
|
|
1615
|
+
const current = dep.get();
|
|
1616
|
+
const tracked = this._trackedValues.get(dep);
|
|
1617
|
+
if (!this._equals(current, tracked)) return true;
|
|
1618
|
+
}
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1638
1621
|
};
|
|
1639
1622
|
|
|
1640
1623
|
// src/core/meta.ts
|
|
@@ -1650,85 +1633,6 @@ function resolveDescribeFields(detail, fields) {
|
|
|
1650
1633
|
return /* @__PURE__ */ new Set(["type", "deps"]);
|
|
1651
1634
|
}
|
|
1652
1635
|
}
|
|
1653
|
-
function inferDescribeType(n) {
|
|
1654
|
-
if (n._describeKind != null) return n._describeKind;
|
|
1655
|
-
if (!n._hasDeps) return n._fn != null ? "producer" : "state";
|
|
1656
|
-
if (n._fn == null) return "derived";
|
|
1657
|
-
if (n._manualEmitUsed) return "operator";
|
|
1658
|
-
return "derived";
|
|
1659
|
-
}
|
|
1660
|
-
function metaSnapshot(node2) {
|
|
1661
|
-
const out = {};
|
|
1662
|
-
for (const [key, child] of Object.entries(node2.meta)) {
|
|
1663
|
-
try {
|
|
1664
|
-
out[key] = child.get();
|
|
1665
|
-
} catch {
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
return out;
|
|
1669
|
-
}
|
|
1670
|
-
function describeNode(node2, includeFields) {
|
|
1671
|
-
const all = includeFields == null;
|
|
1672
|
-
const metaKeys = !all && includeFields != null ? [...includeFields].filter((f) => f.startsWith("meta.")).map((f) => f.slice(5)) : null;
|
|
1673
|
-
const wantsMeta = all || includeFields.has("meta") || metaKeys != null && metaKeys.length > 0;
|
|
1674
|
-
let type = "state";
|
|
1675
|
-
let deps = [];
|
|
1676
|
-
if (node2 instanceof NodeImpl) {
|
|
1677
|
-
type = inferDescribeType(node2);
|
|
1678
|
-
deps = node2._deps.map((d) => d.name ?? "");
|
|
1679
|
-
} else if (node2 instanceof DynamicNodeImpl) {
|
|
1680
|
-
type = node2._describeKind ?? "derived";
|
|
1681
|
-
deps = [];
|
|
1682
|
-
}
|
|
1683
|
-
const out = { type, deps };
|
|
1684
|
-
if (all || includeFields.has("status")) {
|
|
1685
|
-
out.status = node2.status;
|
|
1686
|
-
}
|
|
1687
|
-
const guard = node2 instanceof NodeImpl && node2._guard || node2 instanceof DynamicNodeImpl && node2._guard || void 0;
|
|
1688
|
-
if (wantsMeta) {
|
|
1689
|
-
const rawMeta = { ...metaSnapshot(node2) };
|
|
1690
|
-
if (guard != null && rawMeta.access === void 0) {
|
|
1691
|
-
rawMeta.access = accessHintForGuard(guard);
|
|
1692
|
-
}
|
|
1693
|
-
if (metaKeys != null && metaKeys.length > 0 && !includeFields.has("meta")) {
|
|
1694
|
-
const filtered = {};
|
|
1695
|
-
for (const k of metaKeys) {
|
|
1696
|
-
if (k in rawMeta) filtered[k] = rawMeta[k];
|
|
1697
|
-
}
|
|
1698
|
-
out.meta = filtered;
|
|
1699
|
-
} else {
|
|
1700
|
-
out.meta = rawMeta;
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
if (node2.name != null) {
|
|
1704
|
-
out.name = node2.name;
|
|
1705
|
-
}
|
|
1706
|
-
if (all || includeFields.has("value")) {
|
|
1707
|
-
try {
|
|
1708
|
-
out.value = node2.get();
|
|
1709
|
-
} catch {
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
if ((all || includeFields.has("v")) && node2.v != null) {
|
|
1713
|
-
const vInfo = { id: node2.v.id, version: node2.v.version };
|
|
1714
|
-
if ("cid" in node2.v) {
|
|
1715
|
-
vInfo.cid = node2.v.cid;
|
|
1716
|
-
vInfo.prev = node2.v.prev;
|
|
1717
|
-
}
|
|
1718
|
-
out.v = vInfo;
|
|
1719
|
-
}
|
|
1720
|
-
if (all || includeFields.has("guard")) {
|
|
1721
|
-
if (guard != null) {
|
|
1722
|
-
out.guard = accessHintForGuard(guard);
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
if (all || includeFields.has("lastMutation")) {
|
|
1726
|
-
if (node2.lastMutation != null) {
|
|
1727
|
-
out.lastMutation = node2.lastMutation;
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
return out;
|
|
1731
|
-
}
|
|
1732
1636
|
|
|
1733
1637
|
// src/core/sugar.ts
|
|
1734
1638
|
function state(initial, opts) {
|
|
@@ -1794,6 +1698,7 @@ var ResettableTimer = class {
|
|
|
1794
1698
|
RESOLVED,
|
|
1795
1699
|
RESUME,
|
|
1796
1700
|
ResettableTimer,
|
|
1701
|
+
START,
|
|
1797
1702
|
TEARDOWN,
|
|
1798
1703
|
accessHintForGuard,
|
|
1799
1704
|
advanceVersion,
|
|
@@ -1803,18 +1708,17 @@ var ResettableTimer = class {
|
|
|
1803
1708
|
createVersioning,
|
|
1804
1709
|
defaultHash,
|
|
1805
1710
|
derived,
|
|
1806
|
-
describeNode,
|
|
1807
1711
|
downWithBatch,
|
|
1808
1712
|
dynamicNode,
|
|
1809
1713
|
effect,
|
|
1810
1714
|
isBatching,
|
|
1811
1715
|
isKnownMessageType,
|
|
1716
|
+
isLocalOnly,
|
|
1812
1717
|
isPhase2Message,
|
|
1813
1718
|
isTerminalMessage,
|
|
1814
1719
|
isV1,
|
|
1815
1720
|
knownMessageTypes,
|
|
1816
1721
|
messageTier,
|
|
1817
|
-
metaSnapshot,
|
|
1818
1722
|
monotonicNs,
|
|
1819
1723
|
node,
|
|
1820
1724
|
normalizeActor,
|