@graphrefly/graphrefly 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-J7S54G7I.js → chunk-2L5J6RPM.js} +7 -2
- package/dist/chunk-2L5J6RPM.js.map +1 -0
- package/dist/{chunk-LB3RYLSC.js → chunk-3N2Y6PCR.js} +197 -42
- package/dist/chunk-3N2Y6PCR.js.map +1 -0
- package/dist/{chunk-KJGUP35I.js → chunk-5PSVTDNZ.js} +22 -9
- package/dist/chunk-5PSVTDNZ.js.map +1 -0
- package/dist/{chunk-76YPZQTW.js → chunk-BJAOEU4D.js} +34 -29
- package/dist/chunk-BJAOEU4D.js.map +1 -0
- package/dist/{chunk-UVWEKTYC.js → chunk-IAPLC4NR.js} +3 -3
- package/dist/{chunk-F6ORUNO7.js → chunk-OOA2UTXF.js} +58 -2
- package/dist/chunk-OOA2UTXF.js.map +1 -0
- package/dist/{chunk-TNKODJ6E.js → chunk-PGEU5MEH.js} +7 -3
- package/dist/{chunk-TNKODJ6E.js.map → chunk-PGEU5MEH.js.map} +1 -1
- package/dist/chunk-R2LPZIY2.js +111 -0
- package/dist/chunk-R2LPZIY2.js.map +1 -0
- package/dist/{chunk-BV3TPSBK.js → chunk-XYL3GLB3.js} +742 -757
- package/dist/chunk-XYL3GLB3.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +967 -811
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +4 -4
- package/dist/compat/nestjs/index.d.ts +4 -4
- package/dist/compat/nestjs/index.js +7 -7
- package/dist/core/index.cjs +653 -666
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +7 -3
- package/dist/extra/index.cjs +728 -688
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +9 -5
- package/dist/graph/index.cjs +836 -808
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +3 -3
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.js +8 -8
- package/dist/{graph-gISB9n3n.d.ts → graph-KsTe57nI.d.cts} +82 -8
- package/dist/{graph-BYFlyNpX.d.cts → graph-mILUUqW8.d.ts} +82 -8
- package/dist/{index-CgKPpiu8.d.ts → index-8a605sg9.d.ts} +2 -2
- package/dist/{index-DKaB2x0T.d.ts → index-B2SvPEbc.d.ts} +6 -65
- package/dist/{index-EmzYk-TG.d.cts → index-BHfg_Ez3.d.ts} +123 -77
- package/dist/{index-B80mMeuf.d.ts → index-Bc_diYYJ.d.cts} +123 -77
- package/dist/{index-B43mC7uY.d.cts → index-BjtlNirP.d.cts} +3 -3
- package/dist/{index-CEDaJaYE.d.ts → index-CgSiUouz.d.ts} +3 -3
- package/dist/{index-7WnwgjMu.d.ts → index-DuN3bhtm.d.ts} +82 -32
- package/dist/{index-D_tUMcpz.d.cts → index-SFzE_KTa.d.cts} +82 -32
- package/dist/{index-Ci_vPaVm.d.cts → index-UudxGnzc.d.cts} +6 -65
- package/dist/{index-BqOWSFhr.d.cts → index-VHA43cGP.d.cts} +2 -2
- package/dist/index.cjs +5936 -5522
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +644 -379
- package/dist/index.d.ts +644 -379
- package/dist/index.js +4388 -4058
- package/dist/index.js.map +1 -1
- package/dist/{meta-npl5b97j.d.cts → meta-BnG7XAaE.d.cts} +394 -236
- package/dist/{meta-npl5b97j.d.ts → meta-BnG7XAaE.d.ts} +394 -236
- package/dist/{observable-DFBCBELR.d.cts → observable-C8Kx_O6k.d.cts} +1 -1
- package/dist/{observable-oAGygKvc.d.ts → observable-DcBwQY7t.d.ts} +1 -1
- package/dist/patterns/reactive-layout/index.cjs +865 -718
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +3 -3
- package/dist/patterns/reactive-layout/index.d.ts +3 -3
- package/dist/patterns/reactive-layout/index.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-76YPZQTW.js.map +0 -1
- package/dist/chunk-BV3TPSBK.js.map +0 -1
- package/dist/chunk-F6ORUNO7.js.map +0 -1
- package/dist/chunk-FCLROC4Q.js +0 -231
- package/dist/chunk-FCLROC4Q.js.map +0 -1
- package/dist/chunk-J7S54G7I.js.map +0 -1
- package/dist/chunk-KJGUP35I.js.map +0 -1
- package/dist/chunk-LB3RYLSC.js.map +0 -1
- /package/dist/{chunk-UVWEKTYC.js.map → chunk-IAPLC4NR.js.map} +0 -0
package/dist/extra/index.cjs
CHANGED
|
@@ -65,6 +65,7 @@ __export(extra_exports, {
|
|
|
65
65
|
find: () => find,
|
|
66
66
|
first: () => first,
|
|
67
67
|
firstValueFrom: () => firstValueFrom,
|
|
68
|
+
firstWhere: () => firstWhere,
|
|
68
69
|
flatMap: () => flatMap,
|
|
69
70
|
forEach: () => forEach,
|
|
70
71
|
fromAny: () => fromAny,
|
|
@@ -100,6 +101,7 @@ __export(extra_exports, {
|
|
|
100
101
|
fromWebhook: () => fromWebhook,
|
|
101
102
|
globToRegExp: () => globToRegExp,
|
|
102
103
|
interval: () => interval,
|
|
104
|
+
keepalive: () => keepalive,
|
|
103
105
|
last: () => last,
|
|
104
106
|
linear: () => linear,
|
|
105
107
|
logSlice: () => logSlice,
|
|
@@ -121,6 +123,7 @@ __export(extra_exports, {
|
|
|
121
123
|
pubsub: () => pubsub,
|
|
122
124
|
race: () => race,
|
|
123
125
|
rateLimiter: () => rateLimiter,
|
|
126
|
+
reactiveCounter: () => reactiveCounter,
|
|
124
127
|
reactiveIndex: () => reactiveIndex,
|
|
125
128
|
reactiveList: () => reactiveList,
|
|
126
129
|
reactiveLog: () => reactiveLog,
|
|
@@ -142,7 +145,6 @@ __export(extra_exports, {
|
|
|
142
145
|
shareReplay: () => shareReplay,
|
|
143
146
|
signalToName: () => signalToName,
|
|
144
147
|
skip: () => skip,
|
|
145
|
-
startWith: () => startWith,
|
|
146
148
|
switchMap: () => switchMap,
|
|
147
149
|
take: () => take,
|
|
148
150
|
takeUntil: () => takeUntil,
|
|
@@ -189,6 +191,7 @@ __export(extra_exports, {
|
|
|
189
191
|
module.exports = __toCommonJS(extra_exports);
|
|
190
192
|
|
|
191
193
|
// src/core/messages.ts
|
|
194
|
+
var START = /* @__PURE__ */ Symbol.for("graphrefly/START");
|
|
192
195
|
var DATA = /* @__PURE__ */ Symbol.for("graphrefly/DATA");
|
|
193
196
|
var DIRTY = /* @__PURE__ */ Symbol.for("graphrefly/DIRTY");
|
|
194
197
|
var RESOLVED = /* @__PURE__ */ Symbol.for("graphrefly/RESOLVED");
|
|
@@ -199,6 +202,7 @@ var TEARDOWN = /* @__PURE__ */ Symbol.for("graphrefly/TEARDOWN");
|
|
|
199
202
|
var COMPLETE = /* @__PURE__ */ Symbol.for("graphrefly/COMPLETE");
|
|
200
203
|
var ERROR = /* @__PURE__ */ Symbol.for("graphrefly/ERROR");
|
|
201
204
|
var knownMessageTypes = [
|
|
205
|
+
START,
|
|
202
206
|
DATA,
|
|
203
207
|
DIRTY,
|
|
204
208
|
RESOLVED,
|
|
@@ -209,13 +213,15 @@ var knownMessageTypes = [
|
|
|
209
213
|
COMPLETE,
|
|
210
214
|
ERROR
|
|
211
215
|
];
|
|
216
|
+
var knownMessageSet = new Set(knownMessageTypes);
|
|
212
217
|
function messageTier(t) {
|
|
213
|
-
if (t ===
|
|
214
|
-
if (t ===
|
|
215
|
-
if (t ===
|
|
216
|
-
if (t ===
|
|
217
|
-
if (t ===
|
|
218
|
-
return
|
|
218
|
+
if (t === START) return 0;
|
|
219
|
+
if (t === DIRTY || t === INVALIDATE) return 1;
|
|
220
|
+
if (t === PAUSE || t === RESUME) return 2;
|
|
221
|
+
if (t === DATA || t === RESOLVED) return 3;
|
|
222
|
+
if (t === COMPLETE || t === ERROR) return 4;
|
|
223
|
+
if (t === TEARDOWN) return 5;
|
|
224
|
+
return 1;
|
|
219
225
|
}
|
|
220
226
|
function isPhase2Message(msg) {
|
|
221
227
|
const t = msg[0];
|
|
@@ -224,6 +230,10 @@ function isPhase2Message(msg) {
|
|
|
224
230
|
function isTerminalMessage(t) {
|
|
225
231
|
return t === COMPLETE || t === ERROR;
|
|
226
232
|
}
|
|
233
|
+
function isLocalOnly(t) {
|
|
234
|
+
if (!knownMessageSet.has(t)) return false;
|
|
235
|
+
return messageTier(t) < 3;
|
|
236
|
+
}
|
|
227
237
|
function propagatesToMeta(t) {
|
|
228
238
|
return t === TEARDOWN;
|
|
229
239
|
}
|
|
@@ -384,14 +394,14 @@ function _downSequential(sink, messages, phase = 2) {
|
|
|
384
394
|
const dataQueue = phase === 3 ? pendingPhase3 : pendingPhase2;
|
|
385
395
|
for (const msg of messages) {
|
|
386
396
|
const tier = messageTier(msg[0]);
|
|
387
|
-
if (tier ===
|
|
397
|
+
if (tier === 3) {
|
|
388
398
|
if (isBatching()) {
|
|
389
399
|
const m = msg;
|
|
390
400
|
dataQueue.push(() => sink([m]));
|
|
391
401
|
} else {
|
|
392
402
|
sink([msg]);
|
|
393
403
|
}
|
|
394
|
-
} else if (tier >=
|
|
404
|
+
} else if (tier >= 4) {
|
|
395
405
|
if (isBatching()) {
|
|
396
406
|
const m = msg;
|
|
397
407
|
pendingPhase3.push(() => sink([m]));
|
|
@@ -500,10 +510,24 @@ function advanceVersion(info, newValue, hashFn) {
|
|
|
500
510
|
}
|
|
501
511
|
}
|
|
502
512
|
|
|
503
|
-
// src/core/node.ts
|
|
513
|
+
// src/core/node-base.ts
|
|
504
514
|
var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
|
|
505
515
|
var CLEANUP_RESULT = /* @__PURE__ */ Symbol.for("graphrefly/CLEANUP_RESULT");
|
|
506
|
-
|
|
516
|
+
var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
|
|
517
|
+
var isCleanupFn = (value) => typeof value === "function";
|
|
518
|
+
function statusAfterMessage(status, msg) {
|
|
519
|
+
const t = msg[0];
|
|
520
|
+
if (t === DIRTY) return "dirty";
|
|
521
|
+
if (t === DATA) return "settled";
|
|
522
|
+
if (t === RESOLVED) return "resolved";
|
|
523
|
+
if (t === COMPLETE) return "completed";
|
|
524
|
+
if (t === ERROR) return "errored";
|
|
525
|
+
if (t === INVALIDATE) return "dirty";
|
|
526
|
+
if (t === TEARDOWN) return "disconnected";
|
|
527
|
+
return status;
|
|
528
|
+
}
|
|
529
|
+
function createIntBitSet(size) {
|
|
530
|
+
const fullMask = size >= 32 ? -1 : ~(-1 << size);
|
|
507
531
|
let bits = 0;
|
|
508
532
|
return {
|
|
509
533
|
set(i) {
|
|
@@ -516,7 +540,8 @@ function createIntBitSet() {
|
|
|
516
540
|
return (bits & 1 << i) !== 0;
|
|
517
541
|
},
|
|
518
542
|
covers(other) {
|
|
519
|
-
|
|
543
|
+
const otherBits = other._bits();
|
|
544
|
+
return (bits & otherBits) === otherBits;
|
|
520
545
|
},
|
|
521
546
|
any() {
|
|
522
547
|
return bits !== 0;
|
|
@@ -524,6 +549,9 @@ function createIntBitSet() {
|
|
|
524
549
|
reset() {
|
|
525
550
|
bits = 0;
|
|
526
551
|
},
|
|
552
|
+
setAll() {
|
|
553
|
+
bits = fullMask;
|
|
554
|
+
},
|
|
527
555
|
_bits() {
|
|
528
556
|
return bits;
|
|
529
557
|
}
|
|
@@ -531,6 +559,8 @@ function createIntBitSet() {
|
|
|
531
559
|
}
|
|
532
560
|
function createArrayBitSet(size) {
|
|
533
561
|
const words = new Uint32Array(Math.ceil(size / 32));
|
|
562
|
+
const lastBits = size % 32;
|
|
563
|
+
const lastWordMask = lastBits === 0 ? 4294967295 : (1 << lastBits) - 1 >>> 0;
|
|
534
564
|
return {
|
|
535
565
|
set(i) {
|
|
536
566
|
words[i >>> 5] |= 1 << (i & 31);
|
|
@@ -557,130 +587,103 @@ function createArrayBitSet(size) {
|
|
|
557
587
|
reset() {
|
|
558
588
|
words.fill(0);
|
|
559
589
|
},
|
|
590
|
+
setAll() {
|
|
591
|
+
for (let w = 0; w < words.length - 1; w++) words[w] = 4294967295;
|
|
592
|
+
if (words.length > 0) words[words.length - 1] = lastWordMask;
|
|
593
|
+
},
|
|
560
594
|
_words: words
|
|
561
595
|
};
|
|
562
596
|
}
|
|
563
597
|
function createBitSet(size) {
|
|
564
|
-
return size <= 31 ? createIntBitSet() : createArrayBitSet(size);
|
|
598
|
+
return size <= 31 ? createIntBitSet(size) : createArrayBitSet(size);
|
|
565
599
|
}
|
|
566
|
-
var
|
|
567
|
-
|
|
568
|
-
var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
|
|
569
|
-
var isCleanupFn = (value) => typeof value === "function";
|
|
570
|
-
var statusAfterMessage = (status, msg) => {
|
|
571
|
-
const t = msg[0];
|
|
572
|
-
if (t === DIRTY) return "dirty";
|
|
573
|
-
if (t === DATA) return "settled";
|
|
574
|
-
if (t === RESOLVED) return "resolved";
|
|
575
|
-
if (t === COMPLETE) return "completed";
|
|
576
|
-
if (t === ERROR) return "errored";
|
|
577
|
-
if (t === INVALIDATE) return "dirty";
|
|
578
|
-
if (t === TEARDOWN) return "disconnected";
|
|
579
|
-
return status;
|
|
580
|
-
};
|
|
581
|
-
var NodeImpl = class {
|
|
582
|
-
// --- Configuration (set once, never reassigned) ---
|
|
600
|
+
var NodeBase = class {
|
|
601
|
+
// --- Identity (set once) ---
|
|
583
602
|
_optsName;
|
|
584
603
|
_registryName;
|
|
585
|
-
/** @internal
|
|
604
|
+
/** @internal Read by `describeNode` before inference. */
|
|
586
605
|
_describeKind;
|
|
587
606
|
meta;
|
|
588
|
-
|
|
589
|
-
_fn;
|
|
590
|
-
_opts;
|
|
607
|
+
// --- Options ---
|
|
591
608
|
_equals;
|
|
609
|
+
_resubscribable;
|
|
610
|
+
_resetOnTeardown;
|
|
611
|
+
_onResubscribe;
|
|
592
612
|
_onMessage;
|
|
593
|
-
/** @internal
|
|
613
|
+
/** @internal Read by `describeNode` for `accessHintForGuard`. */
|
|
594
614
|
_guard;
|
|
615
|
+
/** @internal Subclasses update this through {@link _recordMutation}. */
|
|
595
616
|
_lastMutation;
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// ---
|
|
617
|
+
// --- Versioning ---
|
|
618
|
+
_hashFn;
|
|
619
|
+
_versioning;
|
|
620
|
+
// --- Lifecycle state ---
|
|
621
|
+
/** @internal Read by `describeNode` and `graph.ts`. */
|
|
600
622
|
_cached;
|
|
623
|
+
/** @internal Read externally via `get status()`. */
|
|
601
624
|
_status;
|
|
602
625
|
_terminal = false;
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
_manualEmitUsed = false;
|
|
626
|
+
_active = false;
|
|
627
|
+
// --- Sink storage ---
|
|
628
|
+
/** @internal Read by `graph/profile.ts` for subscriber counts. */
|
|
607
629
|
_sinkCount = 0;
|
|
608
630
|
_singleDepSinkCount = 0;
|
|
609
|
-
// --- Object/collection state ---
|
|
610
|
-
_depDirtyMask;
|
|
611
|
-
_depSettledMask;
|
|
612
|
-
_depCompleteMask;
|
|
613
|
-
_allDepsCompleteMask;
|
|
614
|
-
_lastDepValues;
|
|
615
|
-
_cleanup;
|
|
616
|
-
_sinks = null;
|
|
617
631
|
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
618
|
-
|
|
632
|
+
_sinks = null;
|
|
633
|
+
// --- Actions + bound helpers ---
|
|
619
634
|
_actions;
|
|
620
635
|
_boundDownToSinks;
|
|
636
|
+
// --- Inspector hook (Graph observability) ---
|
|
621
637
|
_inspectorHook;
|
|
622
|
-
|
|
623
|
-
_hashFn;
|
|
624
|
-
constructor(deps, fn, opts) {
|
|
625
|
-
this._deps = deps;
|
|
626
|
-
this._fn = fn;
|
|
627
|
-
this._opts = opts;
|
|
638
|
+
constructor(opts) {
|
|
628
639
|
this._optsName = opts.name;
|
|
629
640
|
this._describeKind = opts.describeKind;
|
|
630
641
|
this._equals = opts.equals ?? Object.is;
|
|
642
|
+
this._resubscribable = opts.resubscribable ?? false;
|
|
643
|
+
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
644
|
+
this._onResubscribe = opts.onResubscribe;
|
|
631
645
|
this._onMessage = opts.onMessage;
|
|
632
646
|
this._guard = opts.guard;
|
|
633
|
-
this._hasDeps = deps.length > 0;
|
|
634
|
-
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
635
|
-
this._isSingleDep = deps.length === 1 && fn != null;
|
|
636
647
|
this._cached = "initial" in opts ? opts.initial : NO_VALUE;
|
|
637
|
-
this._status =
|
|
648
|
+
this._status = "disconnected";
|
|
638
649
|
this._hashFn = opts.versioningHash ?? defaultHash;
|
|
639
650
|
this._versioning = opts.versioning != null ? createVersioning(opts.versioning, this._cached === NO_VALUE ? void 0 : this._cached, {
|
|
640
651
|
id: opts.versioningId,
|
|
641
652
|
hash: this._hashFn
|
|
642
653
|
}) : void 0;
|
|
643
|
-
this._depDirtyMask = createBitSet(deps.length);
|
|
644
|
-
this._depSettledMask = createBitSet(deps.length);
|
|
645
|
-
this._depCompleteMask = createBitSet(deps.length);
|
|
646
|
-
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
647
|
-
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
648
654
|
const meta = {};
|
|
649
655
|
for (const [k, v] of Object.entries(opts.meta ?? {})) {
|
|
650
|
-
meta[k] =
|
|
651
|
-
initial: v,
|
|
652
|
-
name: `${opts.name ?? "node"}:meta:${k}`,
|
|
653
|
-
describeKind: "state",
|
|
654
|
-
...opts.guard != null ? { guard: opts.guard } : {}
|
|
655
|
-
});
|
|
656
|
+
meta[k] = this._createMetaNode(k, v, opts);
|
|
656
657
|
}
|
|
657
658
|
Object.freeze(meta);
|
|
658
659
|
this.meta = meta;
|
|
659
660
|
const self = this;
|
|
660
661
|
this._actions = {
|
|
661
662
|
down(messages) {
|
|
662
|
-
self.
|
|
663
|
+
self._onManualEmit();
|
|
663
664
|
self._downInternal(messages);
|
|
664
665
|
},
|
|
665
666
|
emit(value) {
|
|
666
|
-
self.
|
|
667
|
+
self._onManualEmit();
|
|
667
668
|
self._downAutoValue(value);
|
|
668
669
|
},
|
|
669
670
|
up(messages) {
|
|
670
671
|
self._upInternal(messages);
|
|
671
672
|
}
|
|
672
673
|
};
|
|
673
|
-
this.down = this.down.bind(this);
|
|
674
|
-
this.up = this.up.bind(this);
|
|
675
674
|
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
676
675
|
}
|
|
676
|
+
/**
|
|
677
|
+
* Subclass hook invoked by `actions.down` / `actions.emit`. Default no-op;
|
|
678
|
+
* {@link NodeImpl} overrides to set `_manualEmitUsed`.
|
|
679
|
+
*/
|
|
680
|
+
_onManualEmit() {
|
|
681
|
+
}
|
|
682
|
+
// --- Identity getters ---
|
|
677
683
|
get name() {
|
|
678
684
|
return this._registryName ?? this._optsName;
|
|
679
685
|
}
|
|
680
|
-
/**
|
|
681
|
-
* When a node is registered with {@link Graph.add} without an options `name`,
|
|
682
|
-
* the graph assigns the registry local name for introspection (parity with graphrefly-py).
|
|
683
|
-
*/
|
|
686
|
+
/** @internal Assigned by `Graph.add` when registered without an options `name`. */
|
|
684
687
|
_assignRegistryName(localName) {
|
|
685
688
|
if (this._optsName !== void 0 || this._registryName !== void 0) return;
|
|
686
689
|
this._registryName = localName;
|
|
@@ -698,7 +701,10 @@ var NodeImpl = class {
|
|
|
698
701
|
}
|
|
699
702
|
};
|
|
700
703
|
}
|
|
701
|
-
|
|
704
|
+
/** @internal Used by subclasses to surface inspector events. */
|
|
705
|
+
_emitInspectorHook(event) {
|
|
706
|
+
this._inspectorHook?.(event);
|
|
707
|
+
}
|
|
702
708
|
get status() {
|
|
703
709
|
return this._status;
|
|
704
710
|
}
|
|
@@ -708,15 +714,7 @@ var NodeImpl = class {
|
|
|
708
714
|
get v() {
|
|
709
715
|
return this._versioning;
|
|
710
716
|
}
|
|
711
|
-
/**
|
|
712
|
-
* Retroactively apply versioning to a node that was created without it.
|
|
713
|
-
* No-op if versioning is already enabled.
|
|
714
|
-
*
|
|
715
|
-
* Version starts at 0 regardless of prior DATA emissions — it tracks
|
|
716
|
-
* changes from the moment versioning is enabled, not historical ones.
|
|
717
|
-
*
|
|
718
|
-
* @internal — used by {@link Graph.setVersioning}.
|
|
719
|
-
*/
|
|
717
|
+
/** @internal Used by `Graph.setVersioning` to retroactively apply versioning. */
|
|
720
718
|
_applyVersioning(level, opts) {
|
|
721
719
|
if (this._versioning != null) return;
|
|
722
720
|
this._hashFn = opts?.hash ?? this._hashFn;
|
|
@@ -736,6 +734,7 @@ var NodeImpl = class {
|
|
|
736
734
|
if (this._guard == null) return true;
|
|
737
735
|
return this._guard(normalizeActor(actor), "observe");
|
|
738
736
|
}
|
|
737
|
+
// --- Public transport ---
|
|
739
738
|
get() {
|
|
740
739
|
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
741
740
|
}
|
|
@@ -748,43 +747,25 @@ var NodeImpl = class {
|
|
|
748
747
|
if (!this._guard(actor, action)) {
|
|
749
748
|
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
750
749
|
}
|
|
751
|
-
this.
|
|
750
|
+
this._recordMutation(actor);
|
|
752
751
|
}
|
|
753
752
|
this._downInternal(messages);
|
|
754
753
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
this.
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
769
|
-
const t = sinkMessages[i][0];
|
|
770
|
-
if (t === DATA || t === RESOLVED) {
|
|
771
|
-
hasPhase2 = true;
|
|
772
|
-
break;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
if (hasPhase2) {
|
|
776
|
-
const filtered = [];
|
|
777
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
778
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
779
|
-
}
|
|
780
|
-
if (filtered.length > 0) {
|
|
781
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
782
|
-
}
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
754
|
+
/** @internal Record a successful guarded mutation (called by `down` and subclass `up`). */
|
|
755
|
+
_recordMutation(actor) {
|
|
756
|
+
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* At-most-once deactivation guard. Both TEARDOWN (eager) and
|
|
760
|
+
* unsubscribe-body (lazy) call this. The `_active` flag ensures
|
|
761
|
+
* `_doDeactivate` runs exactly once per activation cycle.
|
|
762
|
+
*/
|
|
763
|
+
_onDeactivate() {
|
|
764
|
+
if (!this._active) return;
|
|
765
|
+
this._active = false;
|
|
766
|
+
this._doDeactivate();
|
|
787
767
|
}
|
|
768
|
+
// --- Subscribe (uniform across node shapes) ---
|
|
788
769
|
subscribe(sink, hints) {
|
|
789
770
|
if (hints?.actor != null && this._guard != null) {
|
|
790
771
|
const actor = normalizeActor(hints.actor);
|
|
@@ -792,17 +773,21 @@ var NodeImpl = class {
|
|
|
792
773
|
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
793
774
|
}
|
|
794
775
|
}
|
|
795
|
-
if (this._terminal && this.
|
|
776
|
+
if (this._terminal && this._resubscribable) {
|
|
796
777
|
this._terminal = false;
|
|
797
778
|
this._cached = NO_VALUE;
|
|
798
|
-
this._status =
|
|
799
|
-
this.
|
|
779
|
+
this._status = "disconnected";
|
|
780
|
+
this._onResubscribe?.();
|
|
800
781
|
}
|
|
801
782
|
this._sinkCount += 1;
|
|
802
783
|
if (hints?.singleDep) {
|
|
803
784
|
this._singleDepSinkCount += 1;
|
|
804
785
|
this._singleDepSinks.add(sink);
|
|
805
786
|
}
|
|
787
|
+
if (!this._terminal) {
|
|
788
|
+
const startMessages = this._cached === NO_VALUE ? [[START]] : [[START], [DATA, this._cached]];
|
|
789
|
+
downWithBatch(sink, startMessages);
|
|
790
|
+
}
|
|
806
791
|
if (this._sinks == null) {
|
|
807
792
|
this._sinks = sink;
|
|
808
793
|
} else if (typeof this._sinks === "function") {
|
|
@@ -810,10 +795,12 @@ var NodeImpl = class {
|
|
|
810
795
|
} else {
|
|
811
796
|
this._sinks.add(sink);
|
|
812
797
|
}
|
|
813
|
-
if (this.
|
|
814
|
-
this.
|
|
815
|
-
|
|
816
|
-
|
|
798
|
+
if (this._sinkCount === 1 && !this._terminal) {
|
|
799
|
+
this._active = true;
|
|
800
|
+
this._onActivate();
|
|
801
|
+
}
|
|
802
|
+
if (!this._terminal && this._status === "disconnected" && this._cached === NO_VALUE) {
|
|
803
|
+
this._status = "pending";
|
|
817
804
|
}
|
|
818
805
|
let removed = false;
|
|
819
806
|
return () => {
|
|
@@ -837,39 +824,49 @@ var NodeImpl = class {
|
|
|
837
824
|
}
|
|
838
825
|
}
|
|
839
826
|
if (this._sinks == null) {
|
|
840
|
-
this.
|
|
841
|
-
this._stopProducer();
|
|
827
|
+
this._onDeactivate();
|
|
842
828
|
}
|
|
843
829
|
};
|
|
844
830
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
831
|
+
// --- Down pipeline ---
|
|
832
|
+
/**
|
|
833
|
+
* Core outgoing dispatch. Applies terminal filter + local lifecycle
|
|
834
|
+
* update, then hands messages to `downWithBatch` for tier-aware delivery.
|
|
835
|
+
*/
|
|
836
|
+
_downInternal(messages) {
|
|
837
|
+
if (messages.length === 0) return;
|
|
838
|
+
let sinkMessages = messages;
|
|
839
|
+
if (this._terminal && !this._resubscribable) {
|
|
840
|
+
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
841
|
+
if (pass.length === 0) return;
|
|
842
|
+
sinkMessages = pass;
|
|
853
843
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
844
|
+
this._handleLocalLifecycle(sinkMessages);
|
|
845
|
+
if (this._canSkipDirty()) {
|
|
846
|
+
let hasPhase2 = false;
|
|
847
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
848
|
+
const t = sinkMessages[i][0];
|
|
849
|
+
if (t === DATA || t === RESOLVED) {
|
|
850
|
+
hasPhase2 = true;
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (hasPhase2) {
|
|
855
|
+
const filtered = [];
|
|
856
|
+
for (let i = 0; i < sinkMessages.length; i++) {
|
|
857
|
+
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
858
|
+
}
|
|
859
|
+
if (filtered.length > 0) {
|
|
860
|
+
downWithBatch(this._boundDownToSinks, filtered);
|
|
861
|
+
}
|
|
862
|
+
return;
|
|
859
863
|
}
|
|
860
864
|
}
|
|
865
|
+
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
861
866
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
for (const dep of this._deps) {
|
|
865
|
-
dep.up?.(messages, { internal: true });
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
unsubscribe() {
|
|
869
|
-
if (!this._hasDeps) return;
|
|
870
|
-
this._disconnectUpstream();
|
|
867
|
+
_canSkipDirty() {
|
|
868
|
+
return this._sinkCount === 1 && this._singleDepSinkCount === 1;
|
|
871
869
|
}
|
|
872
|
-
// --- Private methods (prototype, _ prefix) ---
|
|
873
870
|
_downToSinks(messages) {
|
|
874
871
|
if (this._sinks == null) return;
|
|
875
872
|
if (typeof this._sinks === "function") {
|
|
@@ -881,6 +878,11 @@ var NodeImpl = class {
|
|
|
881
878
|
sink(messages);
|
|
882
879
|
}
|
|
883
880
|
}
|
|
881
|
+
/**
|
|
882
|
+
* Update `_cached`, `_status`, `_terminal` from message batch before
|
|
883
|
+
* delivery. Subclass hooks `_onInvalidate` / `_onTeardown` let
|
|
884
|
+
* {@link NodeImpl} clear `_lastDepValues` and invoke cleanup fns.
|
|
885
|
+
*/
|
|
884
886
|
_handleLocalLifecycle(messages) {
|
|
885
887
|
for (const m of messages) {
|
|
886
888
|
const t = m[0];
|
|
@@ -894,28 +896,22 @@ var NodeImpl = class {
|
|
|
894
896
|
}
|
|
895
897
|
}
|
|
896
898
|
if (t === INVALIDATE) {
|
|
897
|
-
|
|
898
|
-
this._cleanup = void 0;
|
|
899
|
-
cleanupFn?.();
|
|
899
|
+
this._onInvalidate();
|
|
900
900
|
this._cached = NO_VALUE;
|
|
901
|
-
this._lastDepValues = void 0;
|
|
902
901
|
}
|
|
903
902
|
this._status = statusAfterMessage(this._status, m);
|
|
904
903
|
if (t === COMPLETE || t === ERROR) {
|
|
905
904
|
this._terminal = true;
|
|
906
905
|
}
|
|
907
906
|
if (t === TEARDOWN) {
|
|
908
|
-
if (this.
|
|
907
|
+
if (this._resetOnTeardown) {
|
|
909
908
|
this._cached = NO_VALUE;
|
|
910
909
|
}
|
|
911
|
-
|
|
912
|
-
this._cleanup = void 0;
|
|
913
|
-
teardownCleanup?.();
|
|
910
|
+
this._onTeardown();
|
|
914
911
|
try {
|
|
915
912
|
this._propagateToMeta(t);
|
|
916
913
|
} finally {
|
|
917
|
-
this.
|
|
918
|
-
this._stopProducer();
|
|
914
|
+
this._onDeactivate();
|
|
919
915
|
}
|
|
920
916
|
}
|
|
921
917
|
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
@@ -923,7 +919,20 @@ var NodeImpl = class {
|
|
|
923
919
|
}
|
|
924
920
|
}
|
|
925
921
|
}
|
|
926
|
-
/**
|
|
922
|
+
/**
|
|
923
|
+
* Subclass hook: invoked when INVALIDATE arrives (before `_cached` is
|
|
924
|
+
* cleared). {@link NodeImpl} uses this to run the fn cleanup fn and
|
|
925
|
+
* drop `_lastDepValues` so the next wave re-runs fn.
|
|
926
|
+
*/
|
|
927
|
+
_onInvalidate() {
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Subclass hook: invoked when TEARDOWN arrives, before `_onDeactivate`.
|
|
931
|
+
* {@link NodeImpl} uses this to run any pending cleanup fn.
|
|
932
|
+
*/
|
|
933
|
+
_onTeardown() {
|
|
934
|
+
}
|
|
935
|
+
/** Forward a signal to all companion meta nodes (best-effort). */
|
|
927
936
|
_propagateToMeta(t) {
|
|
928
937
|
for (const metaNode of Object.values(this.meta)) {
|
|
929
938
|
try {
|
|
@@ -932,9 +941,10 @@ var NodeImpl = class {
|
|
|
932
941
|
}
|
|
933
942
|
}
|
|
934
943
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
944
|
+
/**
|
|
945
|
+
* Frame a computed value into the right protocol messages and dispatch
|
|
946
|
+
* via `_downInternal`. Used by `_runFn` and `actions.emit`.
|
|
947
|
+
*/
|
|
938
948
|
_downAutoValue(value) {
|
|
939
949
|
const wasDirty = this._status === "dirty";
|
|
940
950
|
let unchanged;
|
|
@@ -942,7 +952,9 @@ var NodeImpl = class {
|
|
|
942
952
|
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
943
953
|
} catch (eqErr) {
|
|
944
954
|
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
945
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
955
|
+
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, {
|
|
956
|
+
cause: eqErr
|
|
957
|
+
});
|
|
946
958
|
this._downInternal([[ERROR, wrapped]]);
|
|
947
959
|
return;
|
|
948
960
|
}
|
|
@@ -952,89 +964,173 @@ var NodeImpl = class {
|
|
|
952
964
|
}
|
|
953
965
|
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
954
966
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if ("value" in out) {
|
|
990
|
-
this._downAutoValue(out.value);
|
|
991
|
-
}
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
if (isCleanupFn(out)) {
|
|
995
|
-
this._cleanup = out;
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
if (this._manualEmitUsed) return;
|
|
999
|
-
if (out === void 0) return;
|
|
1000
|
-
this._downAutoValue(out);
|
|
1001
|
-
} catch (err) {
|
|
1002
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1003
|
-
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
1004
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
// src/core/node.ts
|
|
970
|
+
var NodeImpl = class extends NodeBase {
|
|
971
|
+
// --- Dep configuration (set once) ---
|
|
972
|
+
_deps;
|
|
973
|
+
_fn;
|
|
974
|
+
_opts;
|
|
975
|
+
_hasDeps;
|
|
976
|
+
_isSingleDep;
|
|
977
|
+
_autoComplete;
|
|
978
|
+
// --- Wave tracking masks ---
|
|
979
|
+
_depDirtyMask;
|
|
980
|
+
_depSettledMask;
|
|
981
|
+
_depCompleteMask;
|
|
982
|
+
_allDepsCompleteMask;
|
|
983
|
+
// --- Identity-skip optimization ---
|
|
984
|
+
_lastDepValues;
|
|
985
|
+
_cleanup;
|
|
986
|
+
// --- Upstream bookkeeping ---
|
|
987
|
+
_upstreamUnsubs = [];
|
|
988
|
+
// --- Fn behavior flag ---
|
|
989
|
+
/** @internal Read by `describeNode` to infer `"operator"` label. */
|
|
990
|
+
_manualEmitUsed = false;
|
|
991
|
+
constructor(deps, fn, opts) {
|
|
992
|
+
super(opts);
|
|
993
|
+
this._deps = deps;
|
|
994
|
+
this._fn = fn;
|
|
995
|
+
this._opts = opts;
|
|
996
|
+
this._hasDeps = deps.length > 0;
|
|
997
|
+
this._isSingleDep = deps.length === 1 && fn != null;
|
|
998
|
+
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
999
|
+
if (!this._hasDeps && fn == null && this._cached !== NO_VALUE) {
|
|
1000
|
+
this._status = "settled";
|
|
1005
1001
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
this.
|
|
1010
|
-
this.
|
|
1011
|
-
|
|
1012
|
-
|
|
1002
|
+
this._depDirtyMask = createBitSet(deps.length);
|
|
1003
|
+
this._depSettledMask = createBitSet(deps.length);
|
|
1004
|
+
this._depCompleteMask = createBitSet(deps.length);
|
|
1005
|
+
this._allDepsCompleteMask = createBitSet(deps.length);
|
|
1006
|
+
for (let i = 0; i < deps.length; i++) this._allDepsCompleteMask.set(i);
|
|
1007
|
+
this.down = this.down.bind(this);
|
|
1008
|
+
this.up = this.up.bind(this);
|
|
1009
|
+
}
|
|
1010
|
+
// --- Meta node factory (called from base constructor) ---
|
|
1011
|
+
_createMetaNode(key, initialValue, opts) {
|
|
1012
|
+
return node({
|
|
1013
|
+
initial: initialValue,
|
|
1014
|
+
name: `${opts.name ?? "node"}:meta:${key}`,
|
|
1015
|
+
describeKind: "state",
|
|
1016
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
// --- Manual emit tracker (set by actions.down / actions.emit) ---
|
|
1020
|
+
_onManualEmit() {
|
|
1021
|
+
this._manualEmitUsed = true;
|
|
1022
|
+
}
|
|
1023
|
+
// --- Up / unsubscribe ---
|
|
1024
|
+
up(messages, options) {
|
|
1025
|
+
if (!this._hasDeps) return;
|
|
1026
|
+
if (!options?.internal && this._guard != null) {
|
|
1027
|
+
const actor = normalizeActor(options?.actor);
|
|
1028
|
+
if (!this._guard(actor, "write")) {
|
|
1029
|
+
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
1030
|
+
}
|
|
1031
|
+
this._recordMutation(actor);
|
|
1032
|
+
}
|
|
1033
|
+
for (const dep of this._deps) {
|
|
1034
|
+
if (options === void 0) {
|
|
1035
|
+
dep.up?.(messages);
|
|
1036
|
+
} else {
|
|
1037
|
+
dep.up?.(messages, options);
|
|
1038
|
+
}
|
|
1013
1039
|
}
|
|
1014
1040
|
}
|
|
1015
|
-
|
|
1016
|
-
if (!this.
|
|
1017
|
-
|
|
1041
|
+
_upInternal(messages) {
|
|
1042
|
+
if (!this._hasDeps) return;
|
|
1043
|
+
for (const dep of this._deps) {
|
|
1044
|
+
dep.up?.(messages, { internal: true });
|
|
1018
1045
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1046
|
+
}
|
|
1047
|
+
unsubscribe() {
|
|
1048
|
+
if (!this._hasDeps) return;
|
|
1049
|
+
this._disconnectUpstream();
|
|
1050
|
+
}
|
|
1051
|
+
// --- Activation (first-subscriber / last-subscriber hooks) ---
|
|
1052
|
+
_onActivate() {
|
|
1053
|
+
if (this._hasDeps) {
|
|
1054
|
+
this._connectUpstream();
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (this._fn) {
|
|
1023
1058
|
this._runFn();
|
|
1059
|
+
return;
|
|
1024
1060
|
}
|
|
1025
1061
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1062
|
+
_doDeactivate() {
|
|
1063
|
+
this._disconnectUpstream();
|
|
1064
|
+
const cleanup = this._cleanup;
|
|
1065
|
+
this._cleanup = void 0;
|
|
1066
|
+
cleanup?.();
|
|
1067
|
+
if (this._fn != null) {
|
|
1068
|
+
this._cached = NO_VALUE;
|
|
1069
|
+
this._lastDepValues = void 0;
|
|
1070
|
+
}
|
|
1071
|
+
if (this._hasDeps || this._fn != null) {
|
|
1072
|
+
this._status = "disconnected";
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
// --- INVALIDATE / TEARDOWN hooks (clear fn state) ---
|
|
1076
|
+
_onInvalidate() {
|
|
1077
|
+
const cleanup = this._cleanup;
|
|
1078
|
+
this._cleanup = void 0;
|
|
1079
|
+
cleanup?.();
|
|
1080
|
+
this._lastDepValues = void 0;
|
|
1081
|
+
}
|
|
1082
|
+
_onTeardown() {
|
|
1083
|
+
const cleanup = this._cleanup;
|
|
1084
|
+
this._cleanup = void 0;
|
|
1085
|
+
cleanup?.();
|
|
1086
|
+
}
|
|
1087
|
+
// --- Upstream connect / disconnect ---
|
|
1088
|
+
_connectUpstream() {
|
|
1089
|
+
if (!this._hasDeps) return;
|
|
1090
|
+
if (this._upstreamUnsubs.length > 0) return;
|
|
1091
|
+
this._depDirtyMask.setAll();
|
|
1092
|
+
this._depSettledMask.reset();
|
|
1093
|
+
this._depCompleteMask.reset();
|
|
1094
|
+
const depValuesBefore = this._lastDepValues;
|
|
1095
|
+
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1096
|
+
for (let i = 0; i < this._deps.length; i += 1) {
|
|
1097
|
+
const dep = this._deps[i];
|
|
1098
|
+
this._upstreamUnsubs.push(
|
|
1099
|
+
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
if (this._fn && this._onMessage && !this._terminal && this._lastDepValues === depValuesBefore) {
|
|
1103
|
+
this._runFn();
|
|
1029
1104
|
}
|
|
1030
1105
|
}
|
|
1106
|
+
_disconnectUpstream() {
|
|
1107
|
+
if (this._upstreamUnsubs.length === 0) return;
|
|
1108
|
+
for (const unsub of this._upstreamUnsubs.splice(0)) {
|
|
1109
|
+
unsub();
|
|
1110
|
+
}
|
|
1111
|
+
this._depDirtyMask.reset();
|
|
1112
|
+
this._depSettledMask.reset();
|
|
1113
|
+
this._depCompleteMask.reset();
|
|
1114
|
+
}
|
|
1115
|
+
// --- Wave handling ---
|
|
1031
1116
|
_handleDepMessages(index, messages) {
|
|
1032
1117
|
for (const msg of messages) {
|
|
1033
|
-
this.
|
|
1118
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
1034
1119
|
const t = msg[0];
|
|
1035
1120
|
if (this._onMessage) {
|
|
1036
1121
|
try {
|
|
1037
|
-
|
|
1122
|
+
const consumed = this._onMessage(msg, index, this._actions);
|
|
1123
|
+
if (consumed) {
|
|
1124
|
+
if (t === START) {
|
|
1125
|
+
this._depDirtyMask.clear(index);
|
|
1126
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1127
|
+
this._depDirtyMask.reset();
|
|
1128
|
+
this._depSettledMask.reset();
|
|
1129
|
+
this._runFn();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1038
1134
|
} catch (err) {
|
|
1039
1135
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1040
1136
|
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
@@ -1044,6 +1140,7 @@ var NodeImpl = class {
|
|
|
1044
1140
|
return;
|
|
1045
1141
|
}
|
|
1046
1142
|
}
|
|
1143
|
+
if (messageTier(t) < 1) continue;
|
|
1047
1144
|
if (!this._fn) {
|
|
1048
1145
|
if (t === COMPLETE && this._deps.length > 1) {
|
|
1049
1146
|
this._depCompleteMask.set(index);
|
|
@@ -1087,53 +1184,85 @@ var NodeImpl = class {
|
|
|
1087
1184
|
this._downInternal([msg]);
|
|
1088
1185
|
}
|
|
1089
1186
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
this.
|
|
1093
|
-
this.
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
this._status = "settled";
|
|
1097
|
-
const subHints = this._isSingleDep ? { singleDep: true } : void 0;
|
|
1098
|
-
this._connecting = true;
|
|
1099
|
-
try {
|
|
1100
|
-
for (let i = 0; i < this._deps.length; i += 1) {
|
|
1101
|
-
const dep = this._deps[i];
|
|
1102
|
-
this._upstreamUnsubs.push(
|
|
1103
|
-
dep.subscribe((msgs) => this._handleDepMessages(i, msgs), subHints)
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
} finally {
|
|
1107
|
-
this._connecting = false;
|
|
1187
|
+
_onDepDirty(index) {
|
|
1188
|
+
const wasDirty = this._depDirtyMask.has(index);
|
|
1189
|
+
this._depDirtyMask.set(index);
|
|
1190
|
+
this._depSettledMask.clear(index);
|
|
1191
|
+
if (!wasDirty) {
|
|
1192
|
+
this._downInternal([[DIRTY]]);
|
|
1108
1193
|
}
|
|
1109
|
-
|
|
1194
|
+
}
|
|
1195
|
+
_onDepSettled(index) {
|
|
1196
|
+
if (!this._depDirtyMask.has(index)) {
|
|
1197
|
+
this._onDepDirty(index);
|
|
1198
|
+
}
|
|
1199
|
+
this._depSettledMask.set(index);
|
|
1200
|
+
if (this._depDirtyMask.any() && this._depSettledMask.covers(this._depDirtyMask)) {
|
|
1201
|
+
this._depDirtyMask.reset();
|
|
1202
|
+
this._depSettledMask.reset();
|
|
1110
1203
|
this._runFn();
|
|
1111
1204
|
}
|
|
1112
1205
|
}
|
|
1113
|
-
|
|
1114
|
-
if (
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
this._cleanup = void 0;
|
|
1118
|
-
producerCleanup?.();
|
|
1119
|
-
}
|
|
1120
|
-
_startProducer() {
|
|
1121
|
-
if (this._deps.length !== 0 || !this._fn || this._producerStarted) return;
|
|
1122
|
-
this._producerStarted = true;
|
|
1123
|
-
this._runFn();
|
|
1206
|
+
_maybeCompleteFromDeps() {
|
|
1207
|
+
if (this._autoComplete && this._deps.length > 0 && this._depCompleteMask.covers(this._allDepsCompleteMask)) {
|
|
1208
|
+
this._downInternal([[COMPLETE]]);
|
|
1209
|
+
}
|
|
1124
1210
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1211
|
+
// --- Fn execution ---
|
|
1212
|
+
_runFn() {
|
|
1213
|
+
if (!this._fn) return;
|
|
1214
|
+
if (this._terminal && !this._resubscribable) return;
|
|
1215
|
+
try {
|
|
1216
|
+
const n = this._deps.length;
|
|
1217
|
+
const depValues = new Array(n);
|
|
1218
|
+
for (let i = 0; i < n; i++) depValues[i] = this._deps[i].get();
|
|
1219
|
+
const prev = this._lastDepValues;
|
|
1220
|
+
if (n > 0 && prev != null && prev.length === n) {
|
|
1221
|
+
let allSame = true;
|
|
1222
|
+
for (let i = 0; i < n; i++) {
|
|
1223
|
+
if (!Object.is(depValues[i], prev[i])) {
|
|
1224
|
+
allSame = false;
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (allSame) {
|
|
1229
|
+
if (this._status === "dirty") {
|
|
1230
|
+
this._downInternal([[RESOLVED]]);
|
|
1231
|
+
}
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
const prevCleanup = this._cleanup;
|
|
1236
|
+
this._cleanup = void 0;
|
|
1237
|
+
prevCleanup?.();
|
|
1238
|
+
this._manualEmitUsed = false;
|
|
1239
|
+
this._lastDepValues = depValues;
|
|
1240
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
1241
|
+
const out = this._fn(depValues, this._actions);
|
|
1242
|
+
if (isCleanupResult(out)) {
|
|
1243
|
+
this._cleanup = out.cleanup;
|
|
1244
|
+
if (this._manualEmitUsed) return;
|
|
1245
|
+
if ("value" in out) {
|
|
1246
|
+
this._downAutoValue(out.value);
|
|
1247
|
+
}
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
if (isCleanupFn(out)) {
|
|
1251
|
+
this._cleanup = out;
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (this._manualEmitUsed) return;
|
|
1255
|
+
if (out === void 0) return;
|
|
1256
|
+
this._downAutoValue(out);
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1259
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, { cause: err });
|
|
1260
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
1129
1261
|
}
|
|
1130
|
-
this._connected = false;
|
|
1131
|
-
this._depDirtyMask.reset();
|
|
1132
|
-
this._depSettledMask.reset();
|
|
1133
|
-
this._depCompleteMask.reset();
|
|
1134
|
-
this._status = "disconnected";
|
|
1135
1262
|
}
|
|
1136
1263
|
};
|
|
1264
|
+
var isNodeArray = (value) => Array.isArray(value);
|
|
1265
|
+
var isNodeOptions = (value) => typeof value === "object" && value != null && !Array.isArray(value);
|
|
1137
1266
|
function node(depsOrFn, fnOrOpts, optsArg) {
|
|
1138
1267
|
const deps = isNodeArray(depsOrFn) ? depsOrFn : [];
|
|
1139
1268
|
const fn = typeof depsOrFn === "function" ? depsOrFn : typeof fnOrOpts === "function" ? fnOrOpts : void 0;
|
|
@@ -1158,8 +1287,8 @@ function producer(fn, opts) {
|
|
|
1158
1287
|
function derived(deps, fn, opts) {
|
|
1159
1288
|
return node(deps, fn, { describeKind: "derived", ...opts });
|
|
1160
1289
|
}
|
|
1161
|
-
function effect(deps, fn) {
|
|
1162
|
-
return node(deps, fn, { describeKind: "effect" });
|
|
1290
|
+
function effect(deps, fn, opts) {
|
|
1291
|
+
return node(deps, fn, { describeKind: "effect", ...opts });
|
|
1163
1292
|
}
|
|
1164
1293
|
|
|
1165
1294
|
// src/extra/backoff.ts
|
|
@@ -2277,7 +2406,60 @@ function firstValueFrom(source) {
|
|
|
2277
2406
|
});
|
|
2278
2407
|
});
|
|
2279
2408
|
}
|
|
2409
|
+
function firstWhere(source, predicate) {
|
|
2410
|
+
return new Promise((resolve, reject) => {
|
|
2411
|
+
let settled = false;
|
|
2412
|
+
const unsub = source.subscribe((msgs) => {
|
|
2413
|
+
for (const m of msgs) {
|
|
2414
|
+
if (settled) return;
|
|
2415
|
+
if (m[0] === DATA) {
|
|
2416
|
+
const v = m[1];
|
|
2417
|
+
if (predicate(v)) {
|
|
2418
|
+
settled = true;
|
|
2419
|
+
resolve(v);
|
|
2420
|
+
queueMicrotask(() => unsub());
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
if (m[0] === ERROR) {
|
|
2425
|
+
settled = true;
|
|
2426
|
+
reject(m[1]);
|
|
2427
|
+
queueMicrotask(() => unsub());
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
if (m[0] === COMPLETE) {
|
|
2431
|
+
settled = true;
|
|
2432
|
+
reject(new Error("completed without matching value"));
|
|
2433
|
+
queueMicrotask(() => unsub());
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
});
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2280
2440
|
var shareReplay = replay;
|
|
2441
|
+
function keepalive(n) {
|
|
2442
|
+
return n.subscribe(() => {
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
function reactiveCounter(cap) {
|
|
2446
|
+
const counter = state(0);
|
|
2447
|
+
return {
|
|
2448
|
+
node: counter,
|
|
2449
|
+
increment() {
|
|
2450
|
+
const current = counter.get() ?? 0;
|
|
2451
|
+
if (current >= cap) return false;
|
|
2452
|
+
counter.down([[DIRTY], [DATA, current + 1]]);
|
|
2453
|
+
return true;
|
|
2454
|
+
},
|
|
2455
|
+
get() {
|
|
2456
|
+
return counter.get() ?? 0;
|
|
2457
|
+
},
|
|
2458
|
+
atCap() {
|
|
2459
|
+
return (counter.get() ?? 0) >= cap;
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2281
2463
|
|
|
2282
2464
|
// src/extra/adapters.ts
|
|
2283
2465
|
function createSinkErrorHandler(userHandler) {
|
|
@@ -2519,6 +2701,10 @@ function toSSE(source, opts) {
|
|
|
2519
2701
|
unsub = source.subscribe((msgs) => {
|
|
2520
2702
|
for (const msg of msgs) {
|
|
2521
2703
|
const t = msg[0];
|
|
2704
|
+
if (isLocalOnly(t)) {
|
|
2705
|
+
if (t === DIRTY && includeDirty) {
|
|
2706
|
+
} else continue;
|
|
2707
|
+
}
|
|
2522
2708
|
if (t === DATA) {
|
|
2523
2709
|
write(dataEvent, serializeSseData(msg[1], serialize));
|
|
2524
2710
|
continue;
|
|
@@ -2534,7 +2720,6 @@ function toSSE(source, opts) {
|
|
|
2534
2720
|
return;
|
|
2535
2721
|
}
|
|
2536
2722
|
if (!includeResolved && t === RESOLVED) continue;
|
|
2537
|
-
if (!includeDirty && t === DIRTY) continue;
|
|
2538
2723
|
write(
|
|
2539
2724
|
eventNameResolver(t),
|
|
2540
2725
|
msg.length > 1 ? serializeSseData(msg[1], serialize) : void 0
|
|
@@ -5121,452 +5306,275 @@ function restoreGraphCheckpointIndexedDb(graph, spec) {
|
|
|
5121
5306
|
}
|
|
5122
5307
|
|
|
5123
5308
|
// src/core/dynamic-node.ts
|
|
5309
|
+
var MAX_RERUN = 16;
|
|
5124
5310
|
function dynamicNode(fn, opts) {
|
|
5125
5311
|
return new DynamicNodeImpl(fn, opts ?? {});
|
|
5126
5312
|
}
|
|
5127
|
-
var DynamicNodeImpl = class {
|
|
5128
|
-
_optsName;
|
|
5129
|
-
_registryName;
|
|
5130
|
-
_describeKind;
|
|
5131
|
-
meta;
|
|
5313
|
+
var DynamicNodeImpl = class extends NodeBase {
|
|
5132
5314
|
_fn;
|
|
5133
|
-
_equals;
|
|
5134
|
-
_resubscribable;
|
|
5135
|
-
_resetOnTeardown;
|
|
5136
5315
|
_autoComplete;
|
|
5137
|
-
_onMessage;
|
|
5138
|
-
_onResubscribe;
|
|
5139
|
-
/** @internal — read by {@link describeNode} for `accessHintForGuard`. */
|
|
5140
|
-
_guard;
|
|
5141
|
-
_lastMutation;
|
|
5142
|
-
_inspectorHook;
|
|
5143
|
-
// Sink tracking
|
|
5144
|
-
_sinkCount = 0;
|
|
5145
|
-
_singleDepSinkCount = 0;
|
|
5146
|
-
_singleDepSinks = /* @__PURE__ */ new WeakSet();
|
|
5147
|
-
// Actions object (for onMessage handler)
|
|
5148
|
-
_actions;
|
|
5149
|
-
_boundDownToSinks;
|
|
5150
|
-
// Mutable state
|
|
5151
|
-
_cached = NO_VALUE;
|
|
5152
|
-
_status = "disconnected";
|
|
5153
|
-
_terminal = false;
|
|
5154
|
-
_connected = false;
|
|
5155
|
-
_rewiring = false;
|
|
5156
|
-
// re-entrancy guard
|
|
5157
5316
|
// Dynamic deps tracking
|
|
5317
|
+
/** @internal Read by `describeNode`. */
|
|
5158
5318
|
_deps = [];
|
|
5159
5319
|
_depUnsubs = [];
|
|
5160
5320
|
_depIndexMap = /* @__PURE__ */ new Map();
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5321
|
+
_depDirtyBits = /* @__PURE__ */ new Set();
|
|
5322
|
+
_depSettledBits = /* @__PURE__ */ new Set();
|
|
5323
|
+
_depCompleteBits = /* @__PURE__ */ new Set();
|
|
5324
|
+
// Execution state
|
|
5325
|
+
_running = false;
|
|
5326
|
+
_rewiring = false;
|
|
5327
|
+
_bufferedDepMessages = [];
|
|
5328
|
+
_trackedValues = /* @__PURE__ */ new Map();
|
|
5329
|
+
_rerunCount = 0;
|
|
5167
5330
|
constructor(fn, opts) {
|
|
5331
|
+
super(opts);
|
|
5168
5332
|
this._fn = fn;
|
|
5169
|
-
this._optsName = opts.name;
|
|
5170
|
-
this._describeKind = opts.describeKind;
|
|
5171
|
-
this._equals = opts.equals ?? Object.is;
|
|
5172
|
-
this._resubscribable = opts.resubscribable ?? false;
|
|
5173
|
-
this._resetOnTeardown = opts.resetOnTeardown ?? false;
|
|
5174
5333
|
this._autoComplete = opts.completeWhenDepsComplete ?? true;
|
|
5175
|
-
this.
|
|
5176
|
-
this.
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5334
|
+
this.down = this.down.bind(this);
|
|
5335
|
+
this.up = this.up.bind(this);
|
|
5336
|
+
}
|
|
5337
|
+
_createMetaNode(key, initialValue, opts) {
|
|
5338
|
+
return node({
|
|
5339
|
+
initial: initialValue,
|
|
5340
|
+
name: `${opts.name ?? "dynamicNode"}:meta:${key}`,
|
|
5341
|
+
describeKind: "state",
|
|
5342
|
+
...opts.guard != null ? { guard: opts.guard } : {}
|
|
5343
|
+
});
|
|
5344
|
+
}
|
|
5345
|
+
/** Versioning not supported on DynamicNodeImpl (override base). */
|
|
5346
|
+
get v() {
|
|
5347
|
+
return void 0;
|
|
5348
|
+
}
|
|
5349
|
+
// --- Up / unsubscribe ---
|
|
5350
|
+
up(messages, options) {
|
|
5351
|
+
if (this._deps.length === 0) return;
|
|
5352
|
+
if (!options?.internal && this._guard != null) {
|
|
5353
|
+
const actor = normalizeActor(options?.actor);
|
|
5354
|
+
if (!this._guard(actor, "write")) {
|
|
5355
|
+
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
5356
|
+
}
|
|
5357
|
+
this._recordMutation(actor);
|
|
5187
5358
|
}
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
const self = this;
|
|
5191
|
-
this._actions = {
|
|
5192
|
-
down(messages) {
|
|
5193
|
-
self._downInternal(messages);
|
|
5194
|
-
},
|
|
5195
|
-
emit(value) {
|
|
5196
|
-
self._downAutoValue(value);
|
|
5197
|
-
},
|
|
5198
|
-
up(messages) {
|
|
5199
|
-
for (const dep of self._deps) {
|
|
5200
|
-
dep.up?.(messages, { internal: true });
|
|
5201
|
-
}
|
|
5202
|
-
}
|
|
5203
|
-
};
|
|
5204
|
-
this._boundDownToSinks = this._downToSinks.bind(this);
|
|
5205
|
-
}
|
|
5206
|
-
get name() {
|
|
5207
|
-
return this._registryName ?? this._optsName;
|
|
5208
|
-
}
|
|
5209
|
-
/** @internal */
|
|
5210
|
-
_assignRegistryName(localName) {
|
|
5211
|
-
if (this._optsName !== void 0 || this._registryName !== void 0) return;
|
|
5212
|
-
this._registryName = localName;
|
|
5213
|
-
}
|
|
5214
|
-
/**
|
|
5215
|
-
* @internal Attach/remove inspector hook for graph-level observability.
|
|
5216
|
-
* Returns a disposer that restores the previous hook.
|
|
5217
|
-
*/
|
|
5218
|
-
_setInspectorHook(hook) {
|
|
5219
|
-
const prev = this._inspectorHook;
|
|
5220
|
-
this._inspectorHook = hook;
|
|
5221
|
-
return () => {
|
|
5222
|
-
if (this._inspectorHook === hook) {
|
|
5223
|
-
this._inspectorHook = prev;
|
|
5224
|
-
}
|
|
5225
|
-
};
|
|
5226
|
-
}
|
|
5227
|
-
get status() {
|
|
5228
|
-
return this._status;
|
|
5229
|
-
}
|
|
5230
|
-
get lastMutation() {
|
|
5231
|
-
return this._lastMutation;
|
|
5232
|
-
}
|
|
5233
|
-
/** Versioning not yet supported on DynamicNodeImpl. */
|
|
5234
|
-
get v() {
|
|
5235
|
-
return void 0;
|
|
5236
|
-
}
|
|
5237
|
-
hasGuard() {
|
|
5238
|
-
return this._guard != null;
|
|
5239
|
-
}
|
|
5240
|
-
allowsObserve(actor) {
|
|
5241
|
-
if (this._guard == null) return true;
|
|
5242
|
-
return this._guard(normalizeActor(actor), "observe");
|
|
5243
|
-
}
|
|
5244
|
-
get() {
|
|
5245
|
-
return this._cached === NO_VALUE ? void 0 : this._cached;
|
|
5246
|
-
}
|
|
5247
|
-
down(messages, options) {
|
|
5248
|
-
if (messages.length === 0) return;
|
|
5249
|
-
if (!options?.internal && this._guard != null) {
|
|
5250
|
-
const actor = normalizeActor(options?.actor);
|
|
5251
|
-
const delivery = options?.delivery ?? "write";
|
|
5252
|
-
const action = delivery === "signal" ? "signal" : "write";
|
|
5253
|
-
if (!this._guard(actor, action)) {
|
|
5254
|
-
throw new GuardDenied({ actor, action, nodeName: this.name });
|
|
5255
|
-
}
|
|
5256
|
-
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
5257
|
-
}
|
|
5258
|
-
this._downInternal(messages);
|
|
5259
|
-
}
|
|
5260
|
-
_downInternal(messages) {
|
|
5261
|
-
if (messages.length === 0) return;
|
|
5262
|
-
let sinkMessages = messages;
|
|
5263
|
-
if (this._terminal && !this._resubscribable) {
|
|
5264
|
-
const pass = messages.filter((m) => m[0] === TEARDOWN || m[0] === INVALIDATE);
|
|
5265
|
-
if (pass.length === 0) return;
|
|
5266
|
-
sinkMessages = pass;
|
|
5267
|
-
}
|
|
5268
|
-
this._handleLocalLifecycle(sinkMessages);
|
|
5269
|
-
if (this._canSkipDirty()) {
|
|
5270
|
-
let hasPhase2 = false;
|
|
5271
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
5272
|
-
const t = sinkMessages[i][0];
|
|
5273
|
-
if (t === DATA || t === RESOLVED) {
|
|
5274
|
-
hasPhase2 = true;
|
|
5275
|
-
break;
|
|
5276
|
-
}
|
|
5277
|
-
}
|
|
5278
|
-
if (hasPhase2) {
|
|
5279
|
-
const filtered = [];
|
|
5280
|
-
for (let i = 0; i < sinkMessages.length; i++) {
|
|
5281
|
-
if (sinkMessages[i][0] !== DIRTY) filtered.push(sinkMessages[i]);
|
|
5282
|
-
}
|
|
5283
|
-
if (filtered.length > 0) {
|
|
5284
|
-
downWithBatch(this._boundDownToSinks, filtered);
|
|
5285
|
-
}
|
|
5286
|
-
return;
|
|
5287
|
-
}
|
|
5359
|
+
for (const dep of this._deps) {
|
|
5360
|
+
dep.up?.(messages, options);
|
|
5288
5361
|
}
|
|
5289
|
-
downWithBatch(this._boundDownToSinks, sinkMessages);
|
|
5290
5362
|
}
|
|
5291
|
-
|
|
5292
|
-
return this._sinkCount === 1 && this._singleDepSinkCount === 1;
|
|
5293
|
-
}
|
|
5294
|
-
subscribe(sink, hints) {
|
|
5295
|
-
if (hints?.actor != null && this._guard != null) {
|
|
5296
|
-
const actor = normalizeActor(hints.actor);
|
|
5297
|
-
if (!this._guard(actor, "observe")) {
|
|
5298
|
-
throw new GuardDenied({ actor, action: "observe", nodeName: this.name });
|
|
5299
|
-
}
|
|
5300
|
-
}
|
|
5301
|
-
if (this._terminal && this._resubscribable) {
|
|
5302
|
-
this._terminal = false;
|
|
5303
|
-
this._cached = NO_VALUE;
|
|
5304
|
-
this._status = "disconnected";
|
|
5305
|
-
this._onResubscribe?.();
|
|
5306
|
-
}
|
|
5307
|
-
this._sinkCount += 1;
|
|
5308
|
-
if (hints?.singleDep) {
|
|
5309
|
-
this._singleDepSinkCount += 1;
|
|
5310
|
-
this._singleDepSinks.add(sink);
|
|
5311
|
-
}
|
|
5312
|
-
if (this._sinks == null) {
|
|
5313
|
-
this._sinks = sink;
|
|
5314
|
-
} else if (typeof this._sinks === "function") {
|
|
5315
|
-
this._sinks = /* @__PURE__ */ new Set([this._sinks, sink]);
|
|
5316
|
-
} else {
|
|
5317
|
-
this._sinks.add(sink);
|
|
5318
|
-
}
|
|
5319
|
-
if (!this._connected) {
|
|
5320
|
-
this._connect();
|
|
5321
|
-
}
|
|
5322
|
-
let removed = false;
|
|
5323
|
-
return () => {
|
|
5324
|
-
if (removed) return;
|
|
5325
|
-
removed = true;
|
|
5326
|
-
this._sinkCount -= 1;
|
|
5327
|
-
if (this._singleDepSinks.has(sink)) {
|
|
5328
|
-
this._singleDepSinkCount -= 1;
|
|
5329
|
-
this._singleDepSinks.delete(sink);
|
|
5330
|
-
}
|
|
5331
|
-
if (this._sinks == null) return;
|
|
5332
|
-
if (typeof this._sinks === "function") {
|
|
5333
|
-
if (this._sinks === sink) this._sinks = null;
|
|
5334
|
-
} else {
|
|
5335
|
-
this._sinks.delete(sink);
|
|
5336
|
-
if (this._sinks.size === 1) {
|
|
5337
|
-
const [only] = this._sinks;
|
|
5338
|
-
this._sinks = only;
|
|
5339
|
-
} else if (this._sinks.size === 0) {
|
|
5340
|
-
this._sinks = null;
|
|
5341
|
-
}
|
|
5342
|
-
}
|
|
5343
|
-
if (this._sinks == null) {
|
|
5344
|
-
this._disconnect();
|
|
5345
|
-
}
|
|
5346
|
-
};
|
|
5347
|
-
}
|
|
5348
|
-
up(messages, options) {
|
|
5349
|
-
if (this._deps.length === 0) return;
|
|
5350
|
-
if (!options?.internal && this._guard != null) {
|
|
5351
|
-
const actor = normalizeActor(options?.actor);
|
|
5352
|
-
if (!this._guard(actor, "write")) {
|
|
5353
|
-
throw new GuardDenied({ actor, action: "write", nodeName: this.name });
|
|
5354
|
-
}
|
|
5355
|
-
this._lastMutation = { actor, timestamp_ns: wallClockNs() };
|
|
5356
|
-
}
|
|
5363
|
+
_upInternal(messages) {
|
|
5357
5364
|
for (const dep of this._deps) {
|
|
5358
|
-
dep.up?.(messages,
|
|
5365
|
+
dep.up?.(messages, { internal: true });
|
|
5359
5366
|
}
|
|
5360
5367
|
}
|
|
5361
5368
|
unsubscribe() {
|
|
5362
5369
|
this._disconnect();
|
|
5363
5370
|
}
|
|
5364
|
-
// ---
|
|
5365
|
-
|
|
5366
|
-
if (this._sinks == null) return;
|
|
5367
|
-
if (typeof this._sinks === "function") {
|
|
5368
|
-
this._sinks(messages);
|
|
5369
|
-
return;
|
|
5370
|
-
}
|
|
5371
|
-
const snapshot = [...this._sinks];
|
|
5372
|
-
for (const sink of snapshot) {
|
|
5373
|
-
sink(messages);
|
|
5374
|
-
}
|
|
5375
|
-
}
|
|
5376
|
-
_handleLocalLifecycle(messages) {
|
|
5377
|
-
for (const m of messages) {
|
|
5378
|
-
const t = m[0];
|
|
5379
|
-
if (t === DATA) this._cached = m[1];
|
|
5380
|
-
if (t === INVALIDATE) {
|
|
5381
|
-
this._cached = NO_VALUE;
|
|
5382
|
-
this._status = "dirty";
|
|
5383
|
-
}
|
|
5384
|
-
if (t === DATA) {
|
|
5385
|
-
this._status = "settled";
|
|
5386
|
-
} else if (t === RESOLVED) {
|
|
5387
|
-
this._status = "resolved";
|
|
5388
|
-
} else if (t === DIRTY) {
|
|
5389
|
-
this._status = "dirty";
|
|
5390
|
-
} else if (t === COMPLETE) {
|
|
5391
|
-
this._status = "completed";
|
|
5392
|
-
this._terminal = true;
|
|
5393
|
-
} else if (t === ERROR) {
|
|
5394
|
-
this._status = "errored";
|
|
5395
|
-
this._terminal = true;
|
|
5396
|
-
}
|
|
5397
|
-
if (t === TEARDOWN) {
|
|
5398
|
-
if (this._resetOnTeardown) this._cached = NO_VALUE;
|
|
5399
|
-
try {
|
|
5400
|
-
this._propagateToMeta(t);
|
|
5401
|
-
} finally {
|
|
5402
|
-
this._disconnect();
|
|
5403
|
-
}
|
|
5404
|
-
}
|
|
5405
|
-
if (t !== TEARDOWN && propagatesToMeta(t)) {
|
|
5406
|
-
this._propagateToMeta(t);
|
|
5407
|
-
}
|
|
5408
|
-
}
|
|
5409
|
-
}
|
|
5410
|
-
/** Propagate a signal to all companion meta nodes (best-effort). */
|
|
5411
|
-
_propagateToMeta(t) {
|
|
5412
|
-
for (const metaNode of Object.values(this.meta)) {
|
|
5413
|
-
try {
|
|
5414
|
-
metaNode.down([[t]], { internal: true });
|
|
5415
|
-
} catch {
|
|
5416
|
-
}
|
|
5417
|
-
}
|
|
5418
|
-
}
|
|
5419
|
-
_downAutoValue(value) {
|
|
5420
|
-
const wasDirty = this._status === "dirty";
|
|
5421
|
-
let unchanged;
|
|
5422
|
-
try {
|
|
5423
|
-
unchanged = this._cached !== NO_VALUE && this._equals(this._cached, value);
|
|
5424
|
-
} catch (eqErr) {
|
|
5425
|
-
const eqMsg = eqErr instanceof Error ? eqErr.message : String(eqErr);
|
|
5426
|
-
const wrapped = new Error(`Node "${this.name}": equals threw: ${eqMsg}`, { cause: eqErr });
|
|
5427
|
-
this._downInternal([[ERROR, wrapped]]);
|
|
5428
|
-
return;
|
|
5429
|
-
}
|
|
5430
|
-
if (unchanged) {
|
|
5431
|
-
this._downInternal(wasDirty ? [[RESOLVED]] : [[DIRTY], [RESOLVED]]);
|
|
5432
|
-
return;
|
|
5433
|
-
}
|
|
5434
|
-
this._cached = value;
|
|
5435
|
-
this._downInternal(wasDirty ? [[DATA, value]] : [[DIRTY], [DATA, value]]);
|
|
5436
|
-
}
|
|
5437
|
-
_connect() {
|
|
5438
|
-
if (this._connected) return;
|
|
5439
|
-
this._connected = true;
|
|
5440
|
-
this._status = "settled";
|
|
5441
|
-
this._dirtyBits.clear();
|
|
5442
|
-
this._settledBits.clear();
|
|
5443
|
-
this._completeBits.clear();
|
|
5371
|
+
// --- Activation hooks ---
|
|
5372
|
+
_onActivate() {
|
|
5444
5373
|
this._runFn();
|
|
5445
5374
|
}
|
|
5375
|
+
_doDeactivate() {
|
|
5376
|
+
this._disconnect();
|
|
5377
|
+
}
|
|
5446
5378
|
_disconnect() {
|
|
5447
|
-
if (!this._connected) return;
|
|
5448
5379
|
for (const unsub of this._depUnsubs) unsub();
|
|
5449
5380
|
this._depUnsubs = [];
|
|
5450
5381
|
this._deps = [];
|
|
5451
5382
|
this._depIndexMap.clear();
|
|
5452
|
-
this.
|
|
5453
|
-
this.
|
|
5454
|
-
this.
|
|
5455
|
-
this.
|
|
5383
|
+
this._depDirtyBits.clear();
|
|
5384
|
+
this._depSettledBits.clear();
|
|
5385
|
+
this._depCompleteBits.clear();
|
|
5386
|
+
this._cached = NO_VALUE;
|
|
5456
5387
|
this._status = "disconnected";
|
|
5457
5388
|
}
|
|
5389
|
+
// --- Fn execution with rewire buffer ---
|
|
5458
5390
|
_runFn() {
|
|
5459
5391
|
if (this._terminal && !this._resubscribable) return;
|
|
5460
|
-
if (this.
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
if (!trackedSet.has(dep)) {
|
|
5465
|
-
trackedSet.add(dep);
|
|
5466
|
-
trackedDeps.push(dep);
|
|
5467
|
-
}
|
|
5468
|
-
return dep.get();
|
|
5469
|
-
};
|
|
5392
|
+
if (this._running) return;
|
|
5393
|
+
this._running = true;
|
|
5394
|
+
this._rerunCount = 0;
|
|
5395
|
+
let result;
|
|
5470
5396
|
try {
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5397
|
+
for (; ; ) {
|
|
5398
|
+
const trackedDeps = [];
|
|
5399
|
+
const trackedValuesMap = /* @__PURE__ */ new Map();
|
|
5400
|
+
const trackedSet = /* @__PURE__ */ new Set();
|
|
5401
|
+
const get = (dep) => {
|
|
5402
|
+
if (!trackedSet.has(dep)) {
|
|
5403
|
+
trackedSet.add(dep);
|
|
5404
|
+
trackedDeps.push(dep);
|
|
5405
|
+
trackedValuesMap.set(dep, dep.get());
|
|
5406
|
+
}
|
|
5407
|
+
return dep.get();
|
|
5408
|
+
};
|
|
5409
|
+
this._trackedValues = trackedValuesMap;
|
|
5410
|
+
const depValues = [];
|
|
5411
|
+
for (const dep of this._deps) depValues.push(dep.get());
|
|
5412
|
+
this._emitInspectorHook({ kind: "run", depValues });
|
|
5413
|
+
try {
|
|
5414
|
+
result = this._fn(get);
|
|
5415
|
+
} catch (err) {
|
|
5416
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5417
|
+
const wrapped = new Error(`Node "${this.name}": fn threw: ${errMsg}`, {
|
|
5418
|
+
cause: err
|
|
5419
|
+
});
|
|
5420
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
5421
|
+
return;
|
|
5422
|
+
}
|
|
5423
|
+
this._rewiring = true;
|
|
5424
|
+
this._bufferedDepMessages = [];
|
|
5425
|
+
try {
|
|
5426
|
+
this._rewire(trackedDeps);
|
|
5427
|
+
} finally {
|
|
5428
|
+
this._rewiring = false;
|
|
5429
|
+
}
|
|
5430
|
+
let needsRerun = false;
|
|
5431
|
+
for (const entry of this._bufferedDepMessages) {
|
|
5432
|
+
for (const msg of entry.msgs) {
|
|
5433
|
+
if (msg[0] === DATA) {
|
|
5434
|
+
const dep = this._deps[entry.index];
|
|
5435
|
+
const trackedValue = dep != null ? this._trackedValues.get(dep) : void 0;
|
|
5436
|
+
const actualValue = msg[1];
|
|
5437
|
+
if (!this._equals(trackedValue, actualValue)) {
|
|
5438
|
+
needsRerun = true;
|
|
5439
|
+
break;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
if (needsRerun) break;
|
|
5444
|
+
}
|
|
5445
|
+
if (needsRerun) {
|
|
5446
|
+
this._rerunCount += 1;
|
|
5447
|
+
if (this._rerunCount > MAX_RERUN) {
|
|
5448
|
+
this._bufferedDepMessages = [];
|
|
5449
|
+
this._downInternal([
|
|
5450
|
+
[
|
|
5451
|
+
ERROR,
|
|
5452
|
+
new Error(
|
|
5453
|
+
`dynamicNode "${this.name ?? "anonymous"}": rewire did not stabilize within ${MAX_RERUN} iterations`
|
|
5454
|
+
)
|
|
5455
|
+
]
|
|
5456
|
+
]);
|
|
5457
|
+
return;
|
|
5458
|
+
}
|
|
5459
|
+
this._bufferedDepMessages = [];
|
|
5460
|
+
continue;
|
|
5461
|
+
}
|
|
5462
|
+
const drain = this._bufferedDepMessages;
|
|
5463
|
+
this._bufferedDepMessages = [];
|
|
5464
|
+
for (const entry of drain) {
|
|
5465
|
+
for (const msg of entry.msgs) {
|
|
5466
|
+
this._updateMasksForMessage(entry.index, msg);
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
this._depDirtyBits.clear();
|
|
5470
|
+
this._depSettledBits.clear();
|
|
5471
|
+
break;
|
|
5472
|
+
}
|
|
5473
|
+
} finally {
|
|
5474
|
+
this._running = false;
|
|
5482
5475
|
}
|
|
5476
|
+
this._downAutoValue(result);
|
|
5483
5477
|
}
|
|
5484
5478
|
_rewire(newDeps) {
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
const
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
5501
|
-
newUnsubs.push(unsub);
|
|
5502
|
-
}
|
|
5479
|
+
const oldMap = this._depIndexMap;
|
|
5480
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
5481
|
+
const newUnsubs = [];
|
|
5482
|
+
for (let i = 0; i < newDeps.length; i++) {
|
|
5483
|
+
const dep = newDeps[i];
|
|
5484
|
+
newMap.set(dep, i);
|
|
5485
|
+
const oldIdx = oldMap.get(dep);
|
|
5486
|
+
if (oldIdx !== void 0) {
|
|
5487
|
+
newUnsubs.push(this._depUnsubs[oldIdx]);
|
|
5488
|
+
this._depUnsubs[oldIdx] = () => {
|
|
5489
|
+
};
|
|
5490
|
+
} else {
|
|
5491
|
+
const idx = i;
|
|
5492
|
+
const unsub = dep.subscribe((msgs) => this._handleDepMessages(idx, msgs));
|
|
5493
|
+
newUnsubs.push(unsub);
|
|
5503
5494
|
}
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5495
|
+
}
|
|
5496
|
+
for (const [dep, oldIdx] of oldMap) {
|
|
5497
|
+
if (!newMap.has(dep)) {
|
|
5498
|
+
this._depUnsubs[oldIdx]();
|
|
5508
5499
|
}
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5500
|
+
}
|
|
5501
|
+
this._deps = newDeps;
|
|
5502
|
+
this._depUnsubs = newUnsubs;
|
|
5503
|
+
this._depIndexMap = newMap;
|
|
5504
|
+
this._depDirtyBits.clear();
|
|
5505
|
+
this._depSettledBits.clear();
|
|
5506
|
+
const newCompleteBits = /* @__PURE__ */ new Set();
|
|
5507
|
+
for (const oldIdx of this._depCompleteBits) {
|
|
5508
|
+
for (const [dep, idx] of oldMap) {
|
|
5509
|
+
if (idx === oldIdx && newMap.has(dep)) {
|
|
5518
5510
|
newCompleteBits.add(newMap.get(dep));
|
|
5511
|
+
break;
|
|
5519
5512
|
}
|
|
5520
5513
|
}
|
|
5521
|
-
this._completeBits = newCompleteBits;
|
|
5522
|
-
} finally {
|
|
5523
|
-
this._rewiring = false;
|
|
5524
5514
|
}
|
|
5515
|
+
this._depCompleteBits = newCompleteBits;
|
|
5525
5516
|
}
|
|
5517
|
+
// --- Dep message handling ---
|
|
5526
5518
|
_handleDepMessages(index, messages) {
|
|
5527
|
-
if (this._rewiring)
|
|
5519
|
+
if (this._rewiring) {
|
|
5520
|
+
this._bufferedDepMessages.push({ index, msgs: messages });
|
|
5521
|
+
return;
|
|
5522
|
+
}
|
|
5528
5523
|
for (const msg of messages) {
|
|
5529
|
-
this.
|
|
5524
|
+
this._emitInspectorHook({ kind: "dep_message", depIndex: index, message: msg });
|
|
5530
5525
|
const t = msg[0];
|
|
5531
5526
|
if (this._onMessage) {
|
|
5532
5527
|
try {
|
|
5533
5528
|
if (this._onMessage(msg, index, this._actions)) continue;
|
|
5534
5529
|
} catch (err) {
|
|
5535
|
-
|
|
5530
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5531
|
+
const wrapped = new Error(`Node "${this.name}": onMessage threw: ${errMsg}`, {
|
|
5532
|
+
cause: err
|
|
5533
|
+
});
|
|
5534
|
+
this._downInternal([[ERROR, wrapped]]);
|
|
5536
5535
|
return;
|
|
5537
5536
|
}
|
|
5538
5537
|
}
|
|
5538
|
+
if (messageTier(t) < 1) continue;
|
|
5539
5539
|
if (t === DIRTY) {
|
|
5540
|
-
this.
|
|
5541
|
-
this.
|
|
5542
|
-
|
|
5543
|
-
|
|
5540
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
5541
|
+
this._depDirtyBits.add(index);
|
|
5542
|
+
this._depSettledBits.delete(index);
|
|
5543
|
+
if (wasEmpty) {
|
|
5544
|
+
this._downInternal([[DIRTY]]);
|
|
5544
5545
|
}
|
|
5545
5546
|
continue;
|
|
5546
5547
|
}
|
|
5547
5548
|
if (t === DATA || t === RESOLVED) {
|
|
5548
|
-
if (!this.
|
|
5549
|
-
this.
|
|
5550
|
-
|
|
5549
|
+
if (!this._depDirtyBits.has(index)) {
|
|
5550
|
+
const wasEmpty = this._depDirtyBits.size === 0;
|
|
5551
|
+
this._depDirtyBits.add(index);
|
|
5552
|
+
if (wasEmpty) {
|
|
5553
|
+
this._downInternal([[DIRTY]]);
|
|
5554
|
+
}
|
|
5551
5555
|
}
|
|
5552
|
-
this.
|
|
5556
|
+
this._depSettledBits.add(index);
|
|
5553
5557
|
if (this._allDirtySettled()) {
|
|
5554
|
-
this.
|
|
5555
|
-
this.
|
|
5556
|
-
this.
|
|
5558
|
+
this._depDirtyBits.clear();
|
|
5559
|
+
this._depSettledBits.clear();
|
|
5560
|
+
if (!this._running) {
|
|
5561
|
+
if (this._depValuesDifferFromTracked()) {
|
|
5562
|
+
this._runFn();
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5557
5565
|
}
|
|
5558
5566
|
continue;
|
|
5559
5567
|
}
|
|
5560
5568
|
if (t === COMPLETE) {
|
|
5561
|
-
this.
|
|
5562
|
-
this.
|
|
5563
|
-
this.
|
|
5569
|
+
this._depCompleteBits.add(index);
|
|
5570
|
+
this._depDirtyBits.delete(index);
|
|
5571
|
+
this._depSettledBits.delete(index);
|
|
5564
5572
|
if (this._allDirtySettled()) {
|
|
5565
|
-
this.
|
|
5566
|
-
this.
|
|
5567
|
-
this._runFn();
|
|
5573
|
+
this._depDirtyBits.clear();
|
|
5574
|
+
this._depSettledBits.clear();
|
|
5575
|
+
if (!this._running) this._runFn();
|
|
5568
5576
|
}
|
|
5569
|
-
if (this._autoComplete && this.
|
|
5577
|
+
if (this._autoComplete && this._depCompleteBits.size >= this._deps.length && this._deps.length > 0) {
|
|
5570
5578
|
this._downInternal([[COMPLETE]]);
|
|
5571
5579
|
}
|
|
5572
5580
|
continue;
|
|
@@ -5582,13 +5590,46 @@ var DynamicNodeImpl = class {
|
|
|
5582
5590
|
this._downInternal([msg]);
|
|
5583
5591
|
}
|
|
5584
5592
|
}
|
|
5593
|
+
/**
|
|
5594
|
+
* Update dep masks for a message without triggering `_runFn` — used
|
|
5595
|
+
* during post-rewire drain so the wave state is consistent with the
|
|
5596
|
+
* buffered activation cascade without recursing.
|
|
5597
|
+
*/
|
|
5598
|
+
_updateMasksForMessage(index, msg) {
|
|
5599
|
+
const t = msg[0];
|
|
5600
|
+
if (t === DIRTY) {
|
|
5601
|
+
this._depDirtyBits.add(index);
|
|
5602
|
+
this._depSettledBits.delete(index);
|
|
5603
|
+
} else if (t === DATA || t === RESOLVED) {
|
|
5604
|
+
this._depDirtyBits.add(index);
|
|
5605
|
+
this._depSettledBits.add(index);
|
|
5606
|
+
} else if (t === COMPLETE) {
|
|
5607
|
+
this._depCompleteBits.add(index);
|
|
5608
|
+
this._depDirtyBits.delete(index);
|
|
5609
|
+
this._depSettledBits.delete(index);
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5585
5612
|
_allDirtySettled() {
|
|
5586
|
-
if (this.
|
|
5587
|
-
for (const idx of this.
|
|
5588
|
-
if (!this.
|
|
5613
|
+
if (this._depDirtyBits.size === 0) return false;
|
|
5614
|
+
for (const idx of this._depDirtyBits) {
|
|
5615
|
+
if (!this._depSettledBits.has(idx)) return false;
|
|
5589
5616
|
}
|
|
5590
5617
|
return true;
|
|
5591
5618
|
}
|
|
5619
|
+
/**
|
|
5620
|
+
* True if any current dep value differs from what the last `_runFn`
|
|
5621
|
+
* saw via `get()`. Used to suppress redundant re-runs when deferred
|
|
5622
|
+
* handshake messages arrive after `_rewire` for a dep whose value
|
|
5623
|
+
* already matches `_trackedValues`.
|
|
5624
|
+
*/
|
|
5625
|
+
_depValuesDifferFromTracked() {
|
|
5626
|
+
for (const dep of this._deps) {
|
|
5627
|
+
const current = dep.get();
|
|
5628
|
+
const tracked = this._trackedValues.get(dep);
|
|
5629
|
+
if (!this._equals(current, tracked)) return true;
|
|
5630
|
+
}
|
|
5631
|
+
return false;
|
|
5632
|
+
}
|
|
5592
5633
|
};
|
|
5593
5634
|
|
|
5594
5635
|
// src/extra/operators.ts
|
|
@@ -5651,9 +5692,12 @@ function reduce(source, reducer, seed, opts) {
|
|
|
5651
5692
|
}
|
|
5652
5693
|
function take(source, count, opts) {
|
|
5653
5694
|
if (count <= 0) {
|
|
5695
|
+
let completed = false;
|
|
5654
5696
|
return node(
|
|
5655
5697
|
[source],
|
|
5656
5698
|
(_d, a) => {
|
|
5699
|
+
if (completed) return void 0;
|
|
5700
|
+
completed = true;
|
|
5657
5701
|
a.down([[COMPLETE]]);
|
|
5658
5702
|
return void 0;
|
|
5659
5703
|
},
|
|
@@ -5661,8 +5705,15 @@ function take(source, count, opts) {
|
|
|
5661
5705
|
...operatorOpts3(opts),
|
|
5662
5706
|
completeWhenDepsComplete: false,
|
|
5663
5707
|
onMessage(msg, _i, a) {
|
|
5664
|
-
if (msg[0] ===
|
|
5708
|
+
if (msg[0] === START && !completed) {
|
|
5709
|
+
completed = true;
|
|
5665
5710
|
a.down([[COMPLETE]]);
|
|
5711
|
+
return true;
|
|
5712
|
+
}
|
|
5713
|
+
if (msg[0] === COMPLETE && !completed) {
|
|
5714
|
+
completed = true;
|
|
5715
|
+
a.down([[COMPLETE]]);
|
|
5716
|
+
return true;
|
|
5666
5717
|
}
|
|
5667
5718
|
return true;
|
|
5668
5719
|
}
|
|
@@ -5844,21 +5895,6 @@ function find(source, predicate, opts) {
|
|
|
5844
5895
|
function elementAt(source, index, opts) {
|
|
5845
5896
|
return take(skip(source, index, opts), 1, opts);
|
|
5846
5897
|
}
|
|
5847
|
-
function startWith(source, initial, opts) {
|
|
5848
|
-
let prepended = false;
|
|
5849
|
-
return node(
|
|
5850
|
-
[source],
|
|
5851
|
-
([v], a) => {
|
|
5852
|
-
if (!prepended) {
|
|
5853
|
-
prepended = true;
|
|
5854
|
-
a.emit(initial);
|
|
5855
|
-
}
|
|
5856
|
-
a.emit(v);
|
|
5857
|
-
return void 0;
|
|
5858
|
-
},
|
|
5859
|
-
operatorOpts3(opts)
|
|
5860
|
-
);
|
|
5861
|
-
}
|
|
5862
5898
|
function tap(source, fnOrObserver, opts) {
|
|
5863
5899
|
if (typeof fnOrObserver === "function") {
|
|
5864
5900
|
return derived(
|
|
@@ -6194,6 +6230,7 @@ function forwardInner(inner, a, onInnerComplete) {
|
|
|
6194
6230
|
let sawError = false;
|
|
6195
6231
|
const out = [];
|
|
6196
6232
|
for (const m of msgs) {
|
|
6233
|
+
if (messageTier(m[0]) < 1) continue;
|
|
6197
6234
|
if (m[0] === DATA) emitted = true;
|
|
6198
6235
|
if (m[0] === COMPLETE) sawComplete = true;
|
|
6199
6236
|
else {
|
|
@@ -6678,7 +6715,7 @@ function sample(source, notifier, opts) {
|
|
|
6678
6715
|
if (terminated) return true;
|
|
6679
6716
|
const t = msg[0];
|
|
6680
6717
|
const tier = messageTier(t);
|
|
6681
|
-
if (tier >=
|
|
6718
|
+
if (tier >= 4) {
|
|
6682
6719
|
if (t === ERROR) {
|
|
6683
6720
|
terminated = true;
|
|
6684
6721
|
a.down([msg]);
|
|
@@ -6694,6 +6731,7 @@ function sample(source, notifier, opts) {
|
|
|
6694
6731
|
a.down([msg]);
|
|
6695
6732
|
return true;
|
|
6696
6733
|
}
|
|
6734
|
+
terminated = true;
|
|
6697
6735
|
a.down([msg]);
|
|
6698
6736
|
return true;
|
|
6699
6737
|
}
|
|
@@ -7350,7 +7388,7 @@ function verifiable(source, verifyFn, opts) {
|
|
|
7350
7388
|
}
|
|
7351
7389
|
return { node: sourceNode, verified, trigger: triggerNode };
|
|
7352
7390
|
}
|
|
7353
|
-
function
|
|
7391
|
+
function keepalive2(node2) {
|
|
7354
7392
|
node2.subscribe(() => void 0);
|
|
7355
7393
|
}
|
|
7356
7394
|
function mapFromSnapshot(snapshot) {
|
|
@@ -7435,8 +7473,8 @@ function distill(source, extractFn, opts) {
|
|
|
7435
7473
|
return packed;
|
|
7436
7474
|
});
|
|
7437
7475
|
const size = derived([store.entries], ([snapshot]) => mapFromSnapshot(snapshot).size);
|
|
7438
|
-
|
|
7439
|
-
|
|
7476
|
+
keepalive2(compact);
|
|
7477
|
+
keepalive2(size);
|
|
7440
7478
|
return { store, compact, size };
|
|
7441
7479
|
}
|
|
7442
7480
|
|
|
@@ -8007,7 +8045,7 @@ function workerBridge(target, opts) {
|
|
|
8007
8045
|
for (const m of msgs) {
|
|
8008
8046
|
const type = m[0];
|
|
8009
8047
|
if (type === DATA) continue;
|
|
8010
|
-
if (
|
|
8048
|
+
if (isLocalOnly(type)) continue;
|
|
8011
8049
|
if (type === ERROR) {
|
|
8012
8050
|
transport.post({
|
|
8013
8051
|
t: "e",
|
|
@@ -8133,7 +8171,7 @@ function workerSelf(target, opts) {
|
|
|
8133
8171
|
for (const m of msgs) {
|
|
8134
8172
|
const type = m[0];
|
|
8135
8173
|
if (type === DATA) continue;
|
|
8136
|
-
if (
|
|
8174
|
+
if (isLocalOnly(type)) continue;
|
|
8137
8175
|
if (type === ERROR) {
|
|
8138
8176
|
transport.post({
|
|
8139
8177
|
t: "e",
|
|
@@ -8277,6 +8315,7 @@ function workerSelf(target, opts) {
|
|
|
8277
8315
|
find,
|
|
8278
8316
|
first,
|
|
8279
8317
|
firstValueFrom,
|
|
8318
|
+
firstWhere,
|
|
8280
8319
|
flatMap,
|
|
8281
8320
|
forEach,
|
|
8282
8321
|
fromAny,
|
|
@@ -8312,6 +8351,7 @@ function workerSelf(target, opts) {
|
|
|
8312
8351
|
fromWebhook,
|
|
8313
8352
|
globToRegExp,
|
|
8314
8353
|
interval,
|
|
8354
|
+
keepalive,
|
|
8315
8355
|
last,
|
|
8316
8356
|
linear,
|
|
8317
8357
|
logSlice,
|
|
@@ -8333,6 +8373,7 @@ function workerSelf(target, opts) {
|
|
|
8333
8373
|
pubsub,
|
|
8334
8374
|
race,
|
|
8335
8375
|
rateLimiter,
|
|
8376
|
+
reactiveCounter,
|
|
8336
8377
|
reactiveIndex,
|
|
8337
8378
|
reactiveList,
|
|
8338
8379
|
reactiveLog,
|
|
@@ -8354,7 +8395,6 @@ function workerSelf(target, opts) {
|
|
|
8354
8395
|
shareReplay,
|
|
8355
8396
|
signalToName,
|
|
8356
8397
|
skip,
|
|
8357
|
-
startWith,
|
|
8358
8398
|
switchMap,
|
|
8359
8399
|
take,
|
|
8360
8400
|
takeUntil,
|