@pumped-fn/lite 2.1.0 → 2.1.3
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/CHANGELOG.md +47 -0
- package/PATTERNS.md +1 -1
- package/dist/index.cjs +223 -109
- package/dist/index.d.cts +28 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +28 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +223 -110
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -87,7 +87,7 @@ function getAtomsForTag(tag$1) {
|
|
|
87
87
|
return live;
|
|
88
88
|
}
|
|
89
89
|
function tag(options) {
|
|
90
|
-
const key = Symbol
|
|
90
|
+
const key = Symbol(`@pumped-fn/lite/tag/${options.label}`);
|
|
91
91
|
const hasDefault = "default" in options;
|
|
92
92
|
const defaultValue = hasDefault ? options.default : void 0;
|
|
93
93
|
const parse = options.parse;
|
|
@@ -265,7 +265,8 @@ function isAtom(value) {
|
|
|
265
265
|
* @param options - Optional configuration:
|
|
266
266
|
* - `resolve: true` — auto-resolves the dep before the parent factory runs; `config.get()` is safe.
|
|
267
267
|
* - `watch: true` — atom deps only; requires `resolve: true`; automatically re-runs the parent factory
|
|
268
|
-
* when the dep resolves to a new value (value-equality gated via `
|
|
268
|
+
* when the dep resolves to a new value (value-equality gated via plain-object `shallowEqual` by default,
|
|
269
|
+
* otherwise `Object.is`). Replaces
|
|
269
270
|
* manual `ctx.cleanup(ctx.scope.on('resolved', dep, () => ctx.invalidate()))` wiring. Watch
|
|
270
271
|
* listeners are auto-cleaned on re-resolve, release, and dispose.
|
|
271
272
|
* - `eq` — custom equality function `(a: T, b: T) => boolean`; only used with `watch: true`.
|
|
@@ -424,12 +425,38 @@ function isResource(value) {
|
|
|
424
425
|
//#endregion
|
|
425
426
|
//#region src/service.ts
|
|
426
427
|
function service(config) {
|
|
427
|
-
|
|
428
|
+
const atomInstance = {
|
|
428
429
|
[atomSymbol]: true,
|
|
429
430
|
factory: config.factory,
|
|
430
431
|
deps: config.deps,
|
|
431
432
|
tags: config.tags
|
|
432
433
|
};
|
|
434
|
+
if (config.tags?.length) registerAtomToTags(atomInstance, config.tags);
|
|
435
|
+
return atomInstance;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/equality.ts
|
|
440
|
+
function isPlainObject(value) {
|
|
441
|
+
const prototype = Object.getPrototypeOf(value);
|
|
442
|
+
return prototype === Object.prototype || prototype === null;
|
|
443
|
+
}
|
|
444
|
+
function enumerableOwnKeys(value) {
|
|
445
|
+
return Reflect.ownKeys(value).filter((key) => Object.prototype.propertyIsEnumerable.call(value, key));
|
|
446
|
+
}
|
|
447
|
+
function shallowEqual(a, b) {
|
|
448
|
+
if (Object.is(a, b)) return true;
|
|
449
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
450
|
+
if (!isPlainObject(a) || !isPlainObject(b)) return false;
|
|
451
|
+
const objA = a;
|
|
452
|
+
const objB = b;
|
|
453
|
+
const keysA = enumerableOwnKeys(objA);
|
|
454
|
+
if (keysA.length !== enumerableOwnKeys(objB).length) return false;
|
|
455
|
+
for (const key of keysA) {
|
|
456
|
+
if (!Object.hasOwn(objB, key)) return false;
|
|
457
|
+
if (!Object.is(objA[key], objB[key])) return false;
|
|
458
|
+
}
|
|
459
|
+
return true;
|
|
433
460
|
}
|
|
434
461
|
|
|
435
462
|
//#endregion
|
|
@@ -445,7 +472,7 @@ function getResourceKey(resource$1) {
|
|
|
445
472
|
return key;
|
|
446
473
|
}
|
|
447
474
|
const inflightResources = /* @__PURE__ */ new WeakMap();
|
|
448
|
-
const
|
|
475
|
+
const resolvingResourcesMap = /* @__PURE__ */ new WeakMap();
|
|
449
476
|
var ContextDataImpl = class {
|
|
450
477
|
map = /* @__PURE__ */ new Map();
|
|
451
478
|
constructor(parentData) {
|
|
@@ -470,6 +497,10 @@ var ContextDataImpl = class {
|
|
|
470
497
|
if (this.map.has(key)) return this.map.get(key);
|
|
471
498
|
return this.parentData?.seek(key);
|
|
472
499
|
}
|
|
500
|
+
seekHas(key) {
|
|
501
|
+
if (this.map.has(key)) return true;
|
|
502
|
+
return this.parentData?.seekHas(key) ?? false;
|
|
503
|
+
}
|
|
473
504
|
getTag(tag$1) {
|
|
474
505
|
return this.map.get(tag$1.key);
|
|
475
506
|
}
|
|
@@ -515,6 +546,16 @@ var SelectHandleImpl = class {
|
|
|
515
546
|
return this.currentValue;
|
|
516
547
|
}
|
|
517
548
|
subscribe(listener) {
|
|
549
|
+
if (!this.ctrlUnsub) {
|
|
550
|
+
this.currentValue = this.selector(this.ctrl.get());
|
|
551
|
+
this.ctrlUnsub = this.ctrl.on("resolved", () => {
|
|
552
|
+
const nextValue = this.selector(this.ctrl.get());
|
|
553
|
+
if (!this.eq(this.currentValue, nextValue)) {
|
|
554
|
+
this.currentValue = nextValue;
|
|
555
|
+
this.notifyListeners();
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
518
559
|
this.listeners.add(listener);
|
|
519
560
|
return () => {
|
|
520
561
|
this.listeners.delete(listener);
|
|
@@ -522,12 +563,15 @@ var SelectHandleImpl = class {
|
|
|
522
563
|
};
|
|
523
564
|
}
|
|
524
565
|
notifyListeners() {
|
|
525
|
-
for (const listener of this.listeners) listener();
|
|
566
|
+
for (const listener of [...this.listeners]) listener();
|
|
567
|
+
}
|
|
568
|
+
dispose() {
|
|
569
|
+
this.listeners.clear();
|
|
570
|
+
this.cleanup();
|
|
526
571
|
}
|
|
527
572
|
cleanup() {
|
|
528
573
|
this.ctrlUnsub?.();
|
|
529
574
|
this.ctrlUnsub = null;
|
|
530
|
-
this.listeners.clear();
|
|
531
575
|
}
|
|
532
576
|
};
|
|
533
577
|
var ControllerImpl = class {
|
|
@@ -576,11 +620,15 @@ var ScopeImpl = class {
|
|
|
576
620
|
invalidationScheduled = false;
|
|
577
621
|
invalidationChain = null;
|
|
578
622
|
chainPromise = null;
|
|
623
|
+
chainError = null;
|
|
579
624
|
initialized = false;
|
|
625
|
+
disposed = false;
|
|
580
626
|
controllers = /* @__PURE__ */ new Map();
|
|
581
627
|
gcOptions;
|
|
582
628
|
extensions;
|
|
583
629
|
tags;
|
|
630
|
+
resolveExts;
|
|
631
|
+
execExts;
|
|
584
632
|
ready;
|
|
585
633
|
scheduleInvalidation(atom$1) {
|
|
586
634
|
const entry = this.cache.get(atom$1);
|
|
@@ -593,16 +641,22 @@ var ScopeImpl = class {
|
|
|
593
641
|
if (!this.chainPromise) {
|
|
594
642
|
this.invalidationChain = /* @__PURE__ */ new Set();
|
|
595
643
|
this.invalidationScheduled = true;
|
|
596
|
-
this.
|
|
597
|
-
|
|
598
|
-
|
|
644
|
+
this.chainError = null;
|
|
645
|
+
this.chainPromise = (async () => {
|
|
646
|
+
await new Promise((resolve) => {
|
|
647
|
+
queueMicrotask(resolve);
|
|
599
648
|
});
|
|
600
|
-
|
|
649
|
+
try {
|
|
650
|
+
await this.processInvalidationChain();
|
|
651
|
+
} catch (error) {
|
|
652
|
+
if (this.chainError === null) this.chainError = error;
|
|
653
|
+
}
|
|
654
|
+
})();
|
|
601
655
|
}
|
|
602
656
|
}
|
|
603
657
|
async processInvalidationChain() {
|
|
604
658
|
try {
|
|
605
|
-
while (this.invalidationQueue.size > 0) {
|
|
659
|
+
while (this.invalidationQueue.size > 0 && !this.disposed) {
|
|
606
660
|
const atom$1 = this.invalidationQueue.values().next().value;
|
|
607
661
|
this.invalidationQueue.delete(atom$1);
|
|
608
662
|
if (this.invalidationChain.has(atom$1)) {
|
|
@@ -623,6 +677,8 @@ var ScopeImpl = class {
|
|
|
623
677
|
constructor(options) {
|
|
624
678
|
this.extensions = options?.extensions ?? [];
|
|
625
679
|
this.tags = options?.tags ?? [];
|
|
680
|
+
this.resolveExts = this.extensions.filter((e) => e.wrapResolve);
|
|
681
|
+
this.execExts = this.extensions.filter((e) => e.wrapExec);
|
|
626
682
|
for (const p of options?.presets ?? []) this.presets.set(p.target, p.value);
|
|
627
683
|
this.gcOptions = {
|
|
628
684
|
enabled: options?.gc?.enabled ?? true,
|
|
@@ -715,21 +771,21 @@ var ScopeImpl = class {
|
|
|
715
771
|
const entry = this.cache.get(atom$1);
|
|
716
772
|
if (!entry) return;
|
|
717
773
|
const eventListeners = entry.listeners.get(event);
|
|
718
|
-
if (eventListeners) for (const listener of eventListeners) listener();
|
|
774
|
+
if (eventListeners?.size) for (const listener of [...eventListeners]) listener();
|
|
719
775
|
const allListeners = entry.listeners.get("*");
|
|
720
|
-
if (allListeners) for (const listener of allListeners) listener();
|
|
776
|
+
if (allListeners?.size) for (const listener of [...allListeners]) listener();
|
|
721
777
|
}
|
|
722
778
|
notifyAllListeners(atom$1) {
|
|
723
779
|
const entry = this.cache.get(atom$1);
|
|
724
780
|
if (!entry) return;
|
|
725
781
|
const allListeners = entry.listeners.get("*");
|
|
726
|
-
if (allListeners) for (const listener of allListeners) listener();
|
|
782
|
+
if (allListeners?.size) for (const listener of [...allListeners]) listener();
|
|
727
783
|
}
|
|
728
784
|
emitStateChange(state, atom$1) {
|
|
729
785
|
const stateMap = this.stateListeners.get(state);
|
|
730
786
|
if (stateMap) {
|
|
731
787
|
const listeners = stateMap.get(atom$1);
|
|
732
|
-
if (listeners) for (const listener of listeners) listener();
|
|
788
|
+
if (listeners?.size) for (const listener of [...listeners]) listener();
|
|
733
789
|
}
|
|
734
790
|
}
|
|
735
791
|
on(event, atom$1, listener) {
|
|
@@ -755,14 +811,15 @@ var ScopeImpl = class {
|
|
|
755
811
|
};
|
|
756
812
|
}
|
|
757
813
|
async resolve(atom$1) {
|
|
814
|
+
if (this.disposed) throw new Error("Scope is disposed");
|
|
758
815
|
if (!this.initialized) await this.ready;
|
|
759
816
|
const entry = this.cache.get(atom$1);
|
|
760
817
|
if (entry?.state === "resolved") return entry.value;
|
|
761
818
|
const pendingPromise = this.pending.get(atom$1);
|
|
762
819
|
if (pendingPromise) return pendingPromise;
|
|
763
820
|
if (this.resolving.has(atom$1)) throw new Error("Circular dependency detected");
|
|
764
|
-
|
|
765
|
-
|
|
821
|
+
if (this.presets.has(atom$1)) {
|
|
822
|
+
const presetValue = this.presets.get(atom$1);
|
|
766
823
|
if (isAtom(presetValue)) return this.resolve(presetValue);
|
|
767
824
|
const newEntry = this.getOrCreateEntry(atom$1);
|
|
768
825
|
newEntry.state = "resolved";
|
|
@@ -785,7 +842,9 @@ var ScopeImpl = class {
|
|
|
785
842
|
async doResolve(atom$1) {
|
|
786
843
|
const entry = this.getOrCreateEntry(atom$1);
|
|
787
844
|
if (!(entry.state === "resolving")) {
|
|
788
|
-
for (let i = entry.cleanups.length - 1; i >= 0; i--)
|
|
845
|
+
for (let i = entry.cleanups.length - 1; i >= 0; i--) try {
|
|
846
|
+
await entry.cleanups[i]?.();
|
|
847
|
+
} catch {}
|
|
789
848
|
entry.cleanups = [];
|
|
790
849
|
entry.state = "resolving";
|
|
791
850
|
this.emitStateChange("resolving", atom$1);
|
|
@@ -805,7 +864,7 @@ var ScopeImpl = class {
|
|
|
805
864
|
};
|
|
806
865
|
const factory = atom$1.factory;
|
|
807
866
|
const doResolve = async () => {
|
|
808
|
-
if (atom$1.deps
|
|
867
|
+
if (atom$1.deps) return factory(ctx, resolvedDeps);
|
|
809
868
|
else return factory(ctx);
|
|
810
869
|
};
|
|
811
870
|
try {
|
|
@@ -841,88 +900,101 @@ var ScopeImpl = class {
|
|
|
841
900
|
entry.pendingInvalidate = false;
|
|
842
901
|
this.invalidationChain?.delete(atom$1);
|
|
843
902
|
this.scheduleInvalidation(atom$1);
|
|
844
|
-
}
|
|
903
|
+
} else if (entry.pendingSet && "value" in entry.pendingSet) {
|
|
904
|
+
this.invalidationChain?.delete(atom$1);
|
|
905
|
+
this.scheduleInvalidation(atom$1);
|
|
906
|
+
} else entry.pendingSet = void 0;
|
|
845
907
|
throw entry.error;
|
|
846
908
|
}
|
|
847
909
|
}
|
|
848
910
|
async applyResolveExtensions(event, doResolve) {
|
|
849
911
|
let next = doResolve;
|
|
850
|
-
for (let i = this.
|
|
851
|
-
const ext = this.
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
next = ext.wrapResolve.bind(ext, currentNext, event);
|
|
855
|
-
}
|
|
912
|
+
for (let i = this.resolveExts.length - 1; i >= 0; i--) {
|
|
913
|
+
const ext = this.resolveExts[i];
|
|
914
|
+
const currentNext = next;
|
|
915
|
+
next = ext.wrapResolve.bind(ext, currentNext, event);
|
|
856
916
|
}
|
|
857
917
|
return next();
|
|
858
918
|
}
|
|
859
919
|
async resolveDeps(deps, ctx, dependentAtom) {
|
|
860
920
|
if (!deps) return {};
|
|
861
921
|
const result = {};
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
|
|
922
|
+
const parallel = [];
|
|
923
|
+
const deferredResources = [];
|
|
924
|
+
for (const key in deps) {
|
|
925
|
+
const dep = deps[key];
|
|
926
|
+
if (isAtom(dep)) parallel.push(this.resolve(dep).then((value) => {
|
|
927
|
+
result[key] = value;
|
|
928
|
+
if (dependentAtom) {
|
|
929
|
+
const depEntry = this.getEntry(dep);
|
|
930
|
+
if (depEntry) depEntry.dependents.add(dependentAtom);
|
|
931
|
+
}
|
|
932
|
+
}));
|
|
933
|
+
else if (isControllerDep(dep)) {
|
|
934
|
+
if (dep.watch) {
|
|
935
|
+
if (!dependentAtom) throw new Error("controller({ watch: true }) is only supported in atom dependencies");
|
|
936
|
+
if (!dep.resolve) throw new Error("controller({ watch: true }) requires resolve: true");
|
|
937
|
+
}
|
|
938
|
+
const ctrl = this.controller(dep.atom);
|
|
939
|
+
if (dep.resolve) parallel.push(ctrl.resolve().then(() => {
|
|
940
|
+
result[key] = ctrl;
|
|
941
|
+
if (dependentAtom) {
|
|
942
|
+
const depEntry = this.getEntry(dep.atom);
|
|
943
|
+
if (depEntry) depEntry.dependents.add(dependentAtom);
|
|
944
|
+
}
|
|
945
|
+
if (dep.watch) {
|
|
946
|
+
const eq = dep.eq ?? shallowEqual;
|
|
947
|
+
let prev = ctrl.get();
|
|
948
|
+
const unsub = this.on("resolved", dep.atom, () => {
|
|
949
|
+
const next = ctrl.get();
|
|
950
|
+
if (!eq(prev, next)) this.scheduleInvalidation(dependentAtom);
|
|
951
|
+
prev = next;
|
|
952
|
+
});
|
|
953
|
+
const depEntry = this.getEntry(dependentAtom);
|
|
954
|
+
if (depEntry) depEntry.cleanups.push(unsub);
|
|
955
|
+
else unsub();
|
|
956
|
+
}
|
|
957
|
+
}));
|
|
958
|
+
else {
|
|
959
|
+
result[key] = ctrl;
|
|
960
|
+
if (dependentAtom) {
|
|
961
|
+
const depEntry = this.getEntry(dep.atom);
|
|
962
|
+
if (depEntry) depEntry.dependents.add(dependentAtom);
|
|
888
963
|
}
|
|
889
|
-
});
|
|
890
|
-
const depEntry = this.getEntry(dependentAtom);
|
|
891
|
-
if (depEntry) depEntry.cleanups.push(unsub);
|
|
892
|
-
else unsub();
|
|
893
|
-
}
|
|
894
|
-
} else if (tagExecutorSymbol in dep) {
|
|
895
|
-
const tagExecutor = dep;
|
|
896
|
-
switch (tagExecutor.mode) {
|
|
897
|
-
case "required": {
|
|
898
|
-
const value = ctx ? ctx.data.seekTag(tagExecutor.tag) : tagExecutor.tag.find(this.tags);
|
|
899
|
-
if (value !== void 0) result[key] = value;
|
|
900
|
-
else if (tagExecutor.tag.hasDefault) result[key] = tagExecutor.tag.defaultValue;
|
|
901
|
-
else throw new Error(`Tag "${tagExecutor.tag.label}" not found`);
|
|
902
|
-
break;
|
|
903
964
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
965
|
+
} else if (tagExecutorSymbol in dep) {
|
|
966
|
+
const tagExecutor = dep;
|
|
967
|
+
switch (tagExecutor.mode) {
|
|
968
|
+
case "required": {
|
|
969
|
+
const value = ctx ? ctx.data.seekTag(tagExecutor.tag) : tagExecutor.tag.find(this.tags);
|
|
970
|
+
if (value !== void 0) result[key] = value;
|
|
971
|
+
else if (tagExecutor.tag.hasDefault) result[key] = tagExecutor.tag.defaultValue;
|
|
972
|
+
else throw new Error(`Tag "${tagExecutor.tag.label}" not found`);
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
case "optional":
|
|
976
|
+
result[key] = (ctx ? ctx.data.seekTag(tagExecutor.tag) : tagExecutor.tag.find(this.tags)) ?? tagExecutor.tag.defaultValue;
|
|
977
|
+
break;
|
|
978
|
+
case "all":
|
|
979
|
+
result[key] = ctx ? this.collectFromHierarchy(ctx, tagExecutor.tag) : tagExecutor.tag.collect(this.tags);
|
|
980
|
+
break;
|
|
981
|
+
}
|
|
982
|
+
} else if (isResource(dep)) deferredResources.push([key, dep]);
|
|
983
|
+
}
|
|
984
|
+
if (parallel.length === 1) await parallel[0];
|
|
985
|
+
else if (parallel.length > 1) await Promise.all(parallel);
|
|
986
|
+
for (const [key, resource$1] of deferredResources) {
|
|
912
987
|
if (!ctx) throw new Error("Resource deps require an ExecutionContext");
|
|
913
|
-
const resource$1 = dep;
|
|
914
988
|
const resourceKey = getResourceKey(resource$1);
|
|
915
989
|
const storeCtx = ctx.parent ?? ctx;
|
|
916
990
|
if (storeCtx.data.has(resourceKey)) {
|
|
917
991
|
result[key] = storeCtx.data.get(resourceKey);
|
|
918
992
|
continue;
|
|
919
993
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
result[key] = existingSeek;
|
|
994
|
+
if (ctx.data.seekHas(resourceKey)) {
|
|
995
|
+
result[key] = ctx.data.seek(resourceKey);
|
|
923
996
|
continue;
|
|
924
997
|
}
|
|
925
|
-
if (resolvingResources.has(resourceKey)) throw new Error(`Circular resource dependency detected: ${resource$1.name ?? "anonymous"}`);
|
|
926
998
|
let flights = inflightResources.get(storeCtx.data);
|
|
927
999
|
if (!flights) {
|
|
928
1000
|
flights = /* @__PURE__ */ new Map();
|
|
@@ -933,8 +1005,14 @@ var ScopeImpl = class {
|
|
|
933
1005
|
result[key] = await inflight;
|
|
934
1006
|
continue;
|
|
935
1007
|
}
|
|
1008
|
+
let localResolvingResources = resolvingResourcesMap.get(storeCtx.data);
|
|
1009
|
+
if (!localResolvingResources) {
|
|
1010
|
+
localResolvingResources = /* @__PURE__ */ new Set();
|
|
1011
|
+
resolvingResourcesMap.set(storeCtx.data, localResolvingResources);
|
|
1012
|
+
}
|
|
1013
|
+
if (localResolvingResources.has(resourceKey)) throw new Error(`Circular resource dependency detected: ${resource$1.name ?? "anonymous"}`);
|
|
936
1014
|
const resolve = async () => {
|
|
937
|
-
|
|
1015
|
+
localResolvingResources.add(resourceKey);
|
|
938
1016
|
try {
|
|
939
1017
|
const resourceDeps = await this.resolveDeps(resource$1.deps, ctx);
|
|
940
1018
|
const event = {
|
|
@@ -944,14 +1022,14 @@ var ScopeImpl = class {
|
|
|
944
1022
|
};
|
|
945
1023
|
const doResolve = async () => {
|
|
946
1024
|
const factory = resource$1.factory;
|
|
947
|
-
if (resource$1.deps
|
|
1025
|
+
if (resource$1.deps) return factory(storeCtx, resourceDeps);
|
|
948
1026
|
return factory(storeCtx);
|
|
949
1027
|
};
|
|
950
1028
|
const value = await this.applyResolveExtensions(event, doResolve);
|
|
951
1029
|
storeCtx.data.set(resourceKey, value);
|
|
952
1030
|
return value;
|
|
953
1031
|
} finally {
|
|
954
|
-
|
|
1032
|
+
localResolvingResources.delete(resourceKey);
|
|
955
1033
|
}
|
|
956
1034
|
};
|
|
957
1035
|
const promise = resolve();
|
|
@@ -975,6 +1053,7 @@ var ScopeImpl = class {
|
|
|
975
1053
|
return results;
|
|
976
1054
|
}
|
|
977
1055
|
controller(atom$1, options) {
|
|
1056
|
+
if (this.disposed) throw new Error("Scope is disposed");
|
|
978
1057
|
let ctrl = this.controllers.get(atom$1);
|
|
979
1058
|
if (!ctrl) {
|
|
980
1059
|
ctrl = new ControllerImpl(atom$1, this);
|
|
@@ -984,7 +1063,7 @@ var ScopeImpl = class {
|
|
|
984
1063
|
return ctrl;
|
|
985
1064
|
}
|
|
986
1065
|
select(atom$1, selector, options) {
|
|
987
|
-
return new SelectHandleImpl(this.controller(atom$1), selector, options?.eq ??
|
|
1066
|
+
return new SelectHandleImpl(this.controller(atom$1), selector, options?.eq ?? Object.is);
|
|
988
1067
|
}
|
|
989
1068
|
getFlowPreset(flow$1) {
|
|
990
1069
|
return this.presets.get(flow$1);
|
|
@@ -1028,10 +1107,27 @@ var ScopeImpl = class {
|
|
|
1028
1107
|
const previousValue = entry.value;
|
|
1029
1108
|
const pendingSet = entry.pendingSet;
|
|
1030
1109
|
entry.pendingSet = void 0;
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1110
|
+
if (pendingSet) {
|
|
1111
|
+
entry.state = "resolving";
|
|
1112
|
+
entry.value = previousValue;
|
|
1113
|
+
entry.error = void 0;
|
|
1114
|
+
entry.pendingInvalidate = false;
|
|
1115
|
+
this.pending.delete(atom$1);
|
|
1116
|
+
this.resolving.delete(atom$1);
|
|
1117
|
+
this.emitStateChange("resolving", atom$1);
|
|
1118
|
+
this.notifyListeners(atom$1, "resolving");
|
|
1119
|
+
if ("value" in pendingSet) entry.value = pendingSet.value;
|
|
1120
|
+
else entry.value = pendingSet.fn(previousValue);
|
|
1121
|
+
entry.state = "resolved";
|
|
1122
|
+
entry.hasValue = true;
|
|
1123
|
+
this.emitStateChange("resolved", atom$1);
|
|
1124
|
+
this.notifyListeners(atom$1, "resolved");
|
|
1125
|
+
this.invalidationChain?.delete(atom$1);
|
|
1126
|
+
return;
|
|
1034
1127
|
}
|
|
1128
|
+
for (let i = entry.cleanups.length - 1; i >= 0; i--) try {
|
|
1129
|
+
await entry.cleanups[i]?.();
|
|
1130
|
+
} catch {}
|
|
1035
1131
|
entry.cleanups = [];
|
|
1036
1132
|
entry.state = "resolving";
|
|
1037
1133
|
entry.value = previousValue;
|
|
@@ -1041,16 +1137,11 @@ var ScopeImpl = class {
|
|
|
1041
1137
|
this.resolving.delete(atom$1);
|
|
1042
1138
|
this.emitStateChange("resolving", atom$1);
|
|
1043
1139
|
this.notifyListeners(atom$1, "resolving");
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
entry.
|
|
1048
|
-
entry.hasValue = true;
|
|
1049
|
-
this.emitStateChange("resolved", atom$1);
|
|
1050
|
-
this.notifyListeners(atom$1, "resolved");
|
|
1051
|
-
return;
|
|
1140
|
+
try {
|
|
1141
|
+
await this.resolve(atom$1);
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
if (!entry.pendingSet && !entry.pendingInvalidate) throw e;
|
|
1052
1144
|
}
|
|
1053
|
-
await this.resolve(atom$1);
|
|
1054
1145
|
}
|
|
1055
1146
|
async release(atom$1) {
|
|
1056
1147
|
const entry = this.cache.get(atom$1);
|
|
@@ -1059,14 +1150,33 @@ var ScopeImpl = class {
|
|
|
1059
1150
|
clearTimeout(entry.gcScheduled);
|
|
1060
1151
|
entry.gcScheduled = null;
|
|
1061
1152
|
}
|
|
1062
|
-
for (let i = entry.cleanups.length - 1; i >= 0; i--) {
|
|
1063
|
-
|
|
1064
|
-
|
|
1153
|
+
for (let i = entry.cleanups.length - 1; i >= 0; i--) try {
|
|
1154
|
+
await entry.cleanups[i]?.();
|
|
1155
|
+
} catch {}
|
|
1156
|
+
if (atom$1.deps) for (const dep of Object.values(atom$1.deps)) {
|
|
1157
|
+
const depAtom = isAtom(dep) ? dep : isControllerDep(dep) ? dep.atom : null;
|
|
1158
|
+
if (!depAtom) continue;
|
|
1159
|
+
const depEntry = this.cache.get(depAtom);
|
|
1160
|
+
if (depEntry) {
|
|
1161
|
+
depEntry.dependents.delete(atom$1);
|
|
1162
|
+
this.maybeScheduleGC(depAtom);
|
|
1163
|
+
}
|
|
1065
1164
|
}
|
|
1066
1165
|
this.cache.delete(atom$1);
|
|
1067
1166
|
this.controllers.delete(atom$1);
|
|
1167
|
+
for (const [state, stateMap] of this.stateListeners) {
|
|
1168
|
+
stateMap.delete(atom$1);
|
|
1169
|
+
if (stateMap.size === 0) this.stateListeners.delete(state);
|
|
1170
|
+
}
|
|
1068
1171
|
}
|
|
1069
1172
|
async dispose() {
|
|
1173
|
+
if (this.chainPromise) try {
|
|
1174
|
+
await this.chainPromise;
|
|
1175
|
+
} catch {}
|
|
1176
|
+
this.disposed = true;
|
|
1177
|
+
this.invalidationQueue.clear();
|
|
1178
|
+
this.invalidationChain = null;
|
|
1179
|
+
this.chainPromise = null;
|
|
1070
1180
|
for (const ext of this.extensions) if (ext.dispose) await ext.dispose(this);
|
|
1071
1181
|
for (const entry of this.cache.values()) if (entry.gcScheduled) {
|
|
1072
1182
|
clearTimeout(entry.gcScheduled);
|
|
@@ -1077,8 +1187,14 @@ var ScopeImpl = class {
|
|
|
1077
1187
|
}
|
|
1078
1188
|
async flush() {
|
|
1079
1189
|
if (this.chainPromise) await this.chainPromise;
|
|
1190
|
+
if (this.chainError !== null) {
|
|
1191
|
+
const error = this.chainError;
|
|
1192
|
+
this.chainError = null;
|
|
1193
|
+
throw error;
|
|
1194
|
+
}
|
|
1080
1195
|
}
|
|
1081
1196
|
createContext(options) {
|
|
1197
|
+
if (this.disposed) throw new Error("Scope is disposed");
|
|
1082
1198
|
const ctx = new ExecutionContextImpl(this, options);
|
|
1083
1199
|
for (const tagged of options?.tags ?? []) ctx.data.set(tagged.key, tagged.value);
|
|
1084
1200
|
for (const tagged of this.tags) if (!ctx.data.has(tagged.key)) ctx.data.set(tagged.key, tagged.value);
|
|
@@ -1172,7 +1288,7 @@ var ExecutionContextImpl = class ExecutionContextImpl {
|
|
|
1172
1288
|
const resolvedDeps = await this.scope.resolveDeps(flow$1.deps, this);
|
|
1173
1289
|
const factory = flow$1.factory;
|
|
1174
1290
|
const doExec = async () => {
|
|
1175
|
-
if (flow$1.deps
|
|
1291
|
+
if (flow$1.deps) return factory(this, resolvedDeps);
|
|
1176
1292
|
else return factory(this);
|
|
1177
1293
|
};
|
|
1178
1294
|
return this.applyExecExtensions(flow$1, doExec);
|
|
@@ -1188,12 +1304,10 @@ var ExecutionContextImpl = class ExecutionContextImpl {
|
|
|
1188
1304
|
}
|
|
1189
1305
|
async applyExecExtensions(target, doExec) {
|
|
1190
1306
|
let next = doExec;
|
|
1191
|
-
for (let i = this.scope.
|
|
1192
|
-
const ext = this.scope.
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
next = ext.wrapExec.bind(ext, currentNext, target, this);
|
|
1196
|
-
}
|
|
1307
|
+
for (let i = this.scope.execExts.length - 1; i >= 0; i--) {
|
|
1308
|
+
const ext = this.scope.execExts[i];
|
|
1309
|
+
const currentNext = next;
|
|
1310
|
+
next = ext.wrapExec.bind(ext, currentNext, target, this);
|
|
1197
1311
|
}
|
|
1198
1312
|
return next();
|
|
1199
1313
|
}
|
|
@@ -1203,10 +1317,9 @@ var ExecutionContextImpl = class ExecutionContextImpl {
|
|
|
1203
1317
|
async close(result = { ok: true }) {
|
|
1204
1318
|
if (this.closed) return;
|
|
1205
1319
|
this.closed = true;
|
|
1206
|
-
for (let i = this.cleanups.length - 1; i >= 0; i--) {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1320
|
+
for (let i = this.cleanups.length - 1; i >= 0; i--) try {
|
|
1321
|
+
await this.cleanups[i]?.(result);
|
|
1322
|
+
} catch {}
|
|
1210
1323
|
}
|
|
1211
1324
|
};
|
|
1212
1325
|
/**
|
|
@@ -1243,5 +1356,5 @@ function createScope(options) {
|
|
|
1243
1356
|
const VERSION = "0.0.1";
|
|
1244
1357
|
|
|
1245
1358
|
//#endregion
|
|
1246
|
-
export { ParseError, VERSION, atom, atomSymbol, controller, controllerDepSymbol, controllerSymbol, createScope, flow, flowSymbol, getAllTags, isAtom, isControllerDep, isFlow, isPreset, isResource, isTag, isTagExecutor, isTagged, preset, presetSymbol, resource, resourceSymbol, service, tag, tagExecutorSymbol, tagSymbol, taggedSymbol, tags, typed, typedSymbol };
|
|
1359
|
+
export { ParseError, VERSION, atom, atomSymbol, controller, controllerDepSymbol, controllerSymbol, createScope, flow, flowSymbol, getAllTags, isAtom, isControllerDep, isFlow, isPreset, isResource, isTag, isTagExecutor, isTagged, preset, presetSymbol, resource, resourceSymbol, service, shallowEqual, tag, tagExecutorSymbol, tagSymbol, taggedSymbol, tags, typed, typedSymbol };
|
|
1247
1360
|
//# sourceMappingURL=index.mjs.map
|