@pumped-fn/lite 2.1.4 → 2.1.5

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/index.mjs CHANGED
@@ -586,9 +586,8 @@ var ControllerImpl = class {
586
586
  get() {
587
587
  const entry = this.scope.getEntry(this.atom);
588
588
  if (!entry || entry.state === "idle") throw new Error("Atom not resolved");
589
- if (entry.state === "failed" && entry.error) throw entry.error;
590
- if (entry.state === "resolving" && entry.hasValue) return entry.value;
591
- if (entry.state === "resolved" && entry.hasValue) return entry.value;
589
+ if (entry.state === "failed") throw entry.error;
590
+ if (entry.hasValue) return entry.value;
592
591
  throw new Error("Atom not resolved");
593
592
  }
594
593
  async resolve() {
@@ -616,8 +615,7 @@ var ScopeImpl = class {
616
615
  resolving = /* @__PURE__ */ new Set();
617
616
  pending = /* @__PURE__ */ new Map();
618
617
  stateListeners = /* @__PURE__ */ new Map();
619
- invalidationQueue = /* @__PURE__ */ new Set();
620
- invalidationScheduled = false;
618
+ invalidationQueue = [];
621
619
  invalidationChain = null;
622
620
  chainPromise = null;
623
621
  chainError = null;
@@ -630,48 +628,33 @@ var ScopeImpl = class {
630
628
  resolveExts;
631
629
  execExts;
632
630
  ready;
633
- scheduleInvalidation(atom$1) {
634
- const entry = this.cache.get(atom$1);
635
- if (!entry || entry.state === "idle") return;
631
+ scheduleInvalidation(atom$1, entry) {
632
+ if (!entry) {
633
+ entry = this.cache.get(atom$1);
634
+ if (!entry || entry.state === "idle") return;
635
+ }
636
636
  if (entry.state === "resolving") {
637
637
  entry.pendingInvalidate = true;
638
638
  return;
639
639
  }
640
- this.invalidationQueue.add(atom$1);
640
+ if (!this.invalidationQueue.includes(atom$1)) this.invalidationQueue.push(atom$1);
641
641
  if (!this.chainPromise) {
642
- this.invalidationChain = /* @__PURE__ */ new Set();
643
- this.invalidationScheduled = true;
644
642
  this.chainError = null;
645
- this.chainPromise = (async () => {
646
- await new Promise((resolve) => {
647
- queueMicrotask(resolve);
648
- });
649
- try {
650
- await this.processInvalidationChain();
651
- } catch (error) {
652
- if (this.chainError === null) this.chainError = error;
653
- }
654
- })();
643
+ this.chainPromise = Promise.resolve().then(() => this.processInvalidationChain().catch((error) => {
644
+ if (this.chainError === null) this.chainError = error;
645
+ }));
655
646
  }
656
647
  }
657
648
  async processInvalidationChain() {
658
649
  try {
659
- while (this.invalidationQueue.size > 0 && !this.disposed) {
660
- const atom$1 = this.invalidationQueue.values().next().value;
661
- this.invalidationQueue.delete(atom$1);
662
- if (this.invalidationChain.has(atom$1)) {
663
- const chainAtoms = Array.from(this.invalidationChain);
664
- chainAtoms.push(atom$1);
665
- const path = chainAtoms.map((a) => a.factory?.name || "<anonymous>").join(" → ");
666
- throw new Error(`Infinite invalidation loop detected: ${path}`);
667
- }
668
- this.invalidationChain.add(atom$1);
669
- await this.doInvalidateSequential(atom$1);
650
+ while (this.invalidationQueue.length > 0 && !this.disposed) {
651
+ const atom$1 = this.invalidationQueue.shift();
652
+ const result = this.doInvalidateSequential(atom$1);
653
+ if (result) await result;
670
654
  }
671
655
  } finally {
672
656
  this.invalidationChain = null;
673
657
  this.chainPromise = null;
674
- this.invalidationScheduled = false;
675
658
  }
676
659
  }
677
660
  constructor(options) {
@@ -714,79 +697,68 @@ var ScopeImpl = class {
714
697
  return entry;
715
698
  }
716
699
  addListener(atom$1, event, listener) {
717
- this.cancelScheduledGC(atom$1);
718
- const listeners = this.getOrCreateEntry(atom$1).listeners.get(event);
700
+ const entry = this.getOrCreateEntry(atom$1);
701
+ if (entry.gcScheduled) {
702
+ clearTimeout(entry.gcScheduled);
703
+ entry.gcScheduled = null;
704
+ }
705
+ const listeners = entry.listeners.get(event);
719
706
  listeners.add(listener);
720
707
  return () => {
721
708
  listeners.delete(listener);
722
- this.maybeScheduleGC(atom$1);
709
+ this.maybeScheduleGCEntry(atom$1, entry);
723
710
  };
724
711
  }
725
- getSubscriberCount(atom$1) {
726
- const entry = this.cache.get(atom$1);
727
- if (!entry) return 0;
728
- let count = 0;
729
- for (const listeners of entry.listeners.values()) count += listeners.size;
730
- return count;
712
+ hasSubscribers(entry) {
713
+ for (const listeners of entry.listeners.values()) if (listeners.size > 0) return true;
714
+ return false;
731
715
  }
732
- maybeScheduleGC(atom$1) {
716
+ maybeScheduleGCEntry(atom$1, entry) {
733
717
  if (!this.gcOptions.enabled) return;
734
718
  if (atom$1.keepAlive) return;
735
- const entry = this.cache.get(atom$1);
736
- if (!entry) return;
737
719
  if (entry.state === "idle") return;
738
- if (this.getSubscriberCount(atom$1) > 0) return;
720
+ if (this.hasSubscribers(entry)) return;
739
721
  if (entry.dependents.size > 0) return;
740
722
  if (entry.gcScheduled) return;
741
723
  entry.gcScheduled = setTimeout(() => {
742
724
  this.executeGC(atom$1);
743
725
  }, this.gcOptions.graceMs);
744
726
  }
745
- cancelScheduledGC(atom$1) {
746
- const entry = this.cache.get(atom$1);
747
- if (entry?.gcScheduled) {
748
- clearTimeout(entry.gcScheduled);
749
- entry.gcScheduled = null;
750
- }
751
- }
752
727
  async executeGC(atom$1) {
753
728
  const entry = this.cache.get(atom$1);
754
729
  if (!entry) return;
755
730
  entry.gcScheduled = null;
756
- if (this.getSubscriberCount(atom$1) > 0) return;
731
+ if (this.hasSubscribers(entry)) return;
757
732
  if (entry.dependents.size > 0) return;
758
733
  if (atom$1.keepAlive) return;
759
734
  await this.release(atom$1);
760
- if (atom$1.deps) for (const dep of Object.values(atom$1.deps)) {
735
+ if (atom$1.deps) for (const key in atom$1.deps) {
736
+ const dep = atom$1.deps[key];
761
737
  const depAtom = isAtom(dep) ? dep : isControllerDep(dep) ? dep.atom : null;
762
738
  if (!depAtom) continue;
763
739
  const depEntry = this.cache.get(depAtom);
764
740
  if (depEntry) {
765
741
  depEntry.dependents.delete(atom$1);
766
- this.maybeScheduleGC(depAtom);
742
+ this.maybeScheduleGCEntry(depAtom, depEntry);
767
743
  }
768
744
  }
769
745
  }
770
- notifyListeners(atom$1, event) {
771
- const entry = this.cache.get(atom$1);
772
- if (!entry) return;
746
+ notifyEntry(entry, event) {
773
747
  const eventListeners = entry.listeners.get(event);
774
748
  if (eventListeners?.size) for (const listener of [...eventListeners]) listener();
775
749
  const allListeners = entry.listeners.get("*");
776
- if (allListeners?.size) for (const listener of [...allListeners]) listener();
750
+ if (allListeners?.size) for (const listener of allListeners) listener();
777
751
  }
778
- notifyAllListeners(atom$1) {
779
- const entry = this.cache.get(atom$1);
780
- if (!entry) return;
752
+ notifyEntryAll(entry) {
781
753
  const allListeners = entry.listeners.get("*");
782
- if (allListeners?.size) for (const listener of [...allListeners]) listener();
754
+ if (allListeners?.size) for (const listener of allListeners) listener();
783
755
  }
784
756
  emitStateChange(state, atom$1) {
757
+ if (this.stateListeners.size === 0) return;
785
758
  const stateMap = this.stateListeners.get(state);
786
- if (stateMap) {
787
- const listeners = stateMap.get(atom$1);
788
- if (listeners?.size) for (const listener of [...listeners]) listener();
789
- }
759
+ if (!stateMap) return;
760
+ const listeners = stateMap.get(atom$1);
761
+ if (listeners?.size) for (const listener of [...listeners]) listener();
790
762
  }
791
763
  on(event, atom$1, listener) {
792
764
  let stateMap = this.stateListeners.get(event);
@@ -810,14 +782,17 @@ var ScopeImpl = class {
810
782
  }
811
783
  };
812
784
  }
813
- async resolve(atom$1) {
814
- if (this.disposed) throw new Error("Scope is disposed");
815
- if (!this.initialized) await this.ready;
785
+ resolve(atom$1) {
786
+ if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("Scope is disposed"));
787
+ if (!this.initialized) {
788
+ if (!this.ready) return this.resolveAndTrack(atom$1);
789
+ return this.ready.then(() => this.resolve(atom$1));
790
+ }
816
791
  const entry = this.cache.get(atom$1);
817
- if (entry?.state === "resolved") return entry.value;
792
+ if (entry?.state === "resolved") return entry.resolvedPromise ?? (entry.resolvedPromise = Promise.resolve(entry.value));
818
793
  const pendingPromise = this.pending.get(atom$1);
819
794
  if (pendingPromise) return pendingPromise;
820
- if (this.resolving.has(atom$1)) throw new Error("Circular dependency detected");
795
+ if (this.resolving.has(atom$1)) return Promise.reject(/* @__PURE__ */ new Error("Circular dependency detected"));
821
796
  if (this.presets.has(atom$1)) {
822
797
  const presetValue = this.presets.get(atom$1);
823
798
  if (isAtom(presetValue)) return this.resolve(presetValue);
@@ -826,9 +801,12 @@ var ScopeImpl = class {
826
801
  newEntry.value = presetValue;
827
802
  newEntry.hasValue = true;
828
803
  this.emitStateChange("resolved", atom$1);
829
- this.notifyListeners(atom$1, "resolved");
830
- return newEntry.value;
804
+ this.notifyEntry(newEntry, "resolved");
805
+ return Promise.resolve(newEntry.value);
831
806
  }
807
+ return this.resolveAndTrack(atom$1);
808
+ }
809
+ async resolveAndTrack(atom$1) {
832
810
  this.resolving.add(atom$1);
833
811
  const promise = this.doResolve(atom$1);
834
812
  this.pending.set(atom$1, promise);
@@ -848,7 +826,7 @@ var ScopeImpl = class {
848
826
  entry.cleanups = [];
849
827
  entry.state = "resolving";
850
828
  this.emitStateChange("resolving", atom$1);
851
- this.notifyListeners(atom$1, "resolving");
829
+ this.notifyEntry(entry, "resolving");
852
830
  }
853
831
  const resolvedDeps = await this.resolveDeps(atom$1.deps, void 0, atom$1);
854
832
  const ctx = {
@@ -863,23 +841,25 @@ var ScopeImpl = class {
863
841
  }
864
842
  };
865
843
  const factory = atom$1.factory;
866
- const doResolve = async () => {
867
- if (atom$1.deps) return factory(ctx, resolvedDeps);
868
- else return factory(ctx);
869
- };
870
844
  try {
871
- const event = {
872
- kind: "atom",
873
- target: atom$1,
874
- scope: this
875
- };
876
- const value = await this.applyResolveExtensions(event, doResolve);
845
+ let value;
846
+ if (this.resolveExts.length === 0) value = atom$1.deps ? await factory(ctx, resolvedDeps) : await factory(ctx);
847
+ else {
848
+ const doResolve = async () => atom$1.deps ? factory(ctx, resolvedDeps) : factory(ctx);
849
+ const event = {
850
+ kind: "atom",
851
+ target: atom$1,
852
+ scope: this
853
+ };
854
+ value = await this.applyResolveExtensions(event, doResolve);
855
+ }
877
856
  entry.state = "resolved";
878
857
  entry.value = value;
879
858
  entry.hasValue = true;
880
859
  entry.error = void 0;
860
+ entry.resolvedPromise = Promise.resolve(value);
881
861
  this.emitStateChange("resolved", atom$1);
882
- this.notifyListeners(atom$1, "resolved");
862
+ this.notifyEntry(entry, "resolved");
883
863
  if (entry.pendingInvalidate) {
884
864
  entry.pendingInvalidate = false;
885
865
  this.invalidationChain?.delete(atom$1);
@@ -895,7 +875,7 @@ var ScopeImpl = class {
895
875
  entry.value = void 0;
896
876
  entry.hasValue = false;
897
877
  this.emitStateChange("failed", atom$1);
898
- this.notifyAllListeners(atom$1);
878
+ this.notifyEntryAll(entry);
899
879
  if (entry.pendingInvalidate) {
900
880
  entry.pendingInvalidate = false;
901
881
  this.invalidationChain?.delete(atom$1);
@@ -923,39 +903,61 @@ var ScopeImpl = class {
923
903
  const deferredResources = [];
924
904
  for (const key in deps) {
925
905
  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)) {
906
+ if (isAtom(dep)) {
907
+ const cachedEntry = this.cache.get(dep);
908
+ if (cachedEntry?.state === "resolved") {
909
+ result[key] = cachedEntry.value;
910
+ if (dependentAtom) cachedEntry.dependents.add(dependentAtom);
911
+ } else parallel.push(this.resolve(dep).then((value) => {
912
+ result[key] = value;
913
+ if (dependentAtom) {
914
+ const depEntry = this.getEntry(dep);
915
+ if (depEntry) depEntry.dependents.add(dependentAtom);
916
+ }
917
+ }));
918
+ } else if (isControllerDep(dep)) {
934
919
  if (dep.watch) {
935
920
  if (!dependentAtom) throw new Error("controller({ watch: true }) is only supported in atom dependencies");
936
921
  if (!dep.resolve) throw new Error("controller({ watch: true }) requires resolve: true");
937
922
  }
938
923
  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 {
924
+ if (dep.resolve) {
925
+ const cachedCtrlEntry = this.cache.get(dep.atom);
926
+ if (cachedCtrlEntry?.state === "resolved") {
927
+ result[key] = ctrl;
928
+ if (dependentAtom) cachedCtrlEntry.dependents.add(dependentAtom);
929
+ if (dep.watch) {
930
+ const eq = dep.eq ?? shallowEqual;
931
+ let prev = ctrl.get();
932
+ const unsub = this.on("resolved", dep.atom, () => {
933
+ const next = ctrl.get();
934
+ if (!eq(prev, next)) this.scheduleInvalidation(dependentAtom);
935
+ prev = next;
936
+ });
937
+ const depEntry = this.getEntry(dependentAtom);
938
+ if (depEntry) depEntry.cleanups.push(unsub);
939
+ else unsub();
940
+ }
941
+ } else parallel.push(ctrl.resolve().then(() => {
942
+ result[key] = ctrl;
943
+ if (dependentAtom) {
944
+ const depEntry = this.getEntry(dep.atom);
945
+ if (depEntry) depEntry.dependents.add(dependentAtom);
946
+ }
947
+ if (dep.watch) {
948
+ const eq = dep.eq ?? shallowEqual;
949
+ let prev = ctrl.get();
950
+ const unsub = this.on("resolved", dep.atom, () => {
951
+ const next = ctrl.get();
952
+ if (!eq(prev, next)) this.scheduleInvalidation(dependentAtom);
953
+ prev = next;
954
+ });
955
+ const depEntry = this.getEntry(dependentAtom);
956
+ if (depEntry) depEntry.cleanups.push(unsub);
957
+ else unsub();
958
+ }
959
+ }));
960
+ } else {
959
961
  result[key] = ctrl;
960
962
  if (dependentAtom) {
961
963
  const depEntry = this.getEntry(dep.atom);
@@ -1015,17 +1017,18 @@ var ScopeImpl = class {
1015
1017
  localResolvingResources.add(resourceKey);
1016
1018
  try {
1017
1019
  const resourceDeps = await this.resolveDeps(resource$1.deps, ctx);
1018
- const event = {
1019
- kind: "resource",
1020
- target: resource$1,
1021
- ctx: storeCtx
1022
- };
1023
- const doResolve = async () => {
1024
- const factory = resource$1.factory;
1025
- if (resource$1.deps) return factory(storeCtx, resourceDeps);
1026
- return factory(storeCtx);
1027
- };
1028
- const value = await this.applyResolveExtensions(event, doResolve);
1020
+ const factory = resource$1.factory;
1021
+ let value;
1022
+ if (this.resolveExts.length === 0) value = resource$1.deps ? await factory(storeCtx, resourceDeps) : await factory(storeCtx);
1023
+ else {
1024
+ const event = {
1025
+ kind: "resource",
1026
+ target: resource$1,
1027
+ ctx: storeCtx
1028
+ };
1029
+ const doResolve = async () => resource$1.deps ? factory(storeCtx, resourceDeps) : factory(storeCtx);
1030
+ value = await this.applyResolveExtensions(event, doResolve);
1031
+ }
1029
1032
  storeCtx.data.set(resourceKey, value);
1030
1033
  return value;
1031
1034
  } finally {
@@ -1087,7 +1090,7 @@ var ScopeImpl = class {
1087
1090
  return;
1088
1091
  }
1089
1092
  entry.pendingSet = { value };
1090
- this.scheduleInvalidation(atom$1);
1093
+ this.scheduleInvalidation(atom$1, entry);
1091
1094
  }
1092
1095
  scheduleUpdate(atom$1, fn) {
1093
1096
  const entry = this.cache.get(atom$1);
@@ -1098,9 +1101,9 @@ var ScopeImpl = class {
1098
1101
  return;
1099
1102
  }
1100
1103
  entry.pendingSet = { fn };
1101
- this.scheduleInvalidation(atom$1);
1104
+ this.scheduleInvalidation(atom$1, entry);
1102
1105
  }
1103
- async doInvalidateSequential(atom$1) {
1106
+ doInvalidateSequential(atom$1) {
1104
1107
  const entry = this.cache.get(atom$1);
1105
1108
  if (!entry) return;
1106
1109
  if (entry.state === "idle") return;
@@ -1108,23 +1111,27 @@ var ScopeImpl = class {
1108
1111
  const pendingSet = entry.pendingSet;
1109
1112
  entry.pendingSet = void 0;
1110
1113
  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);
1114
+ entry.value = "value" in pendingSet ? pendingSet.value : pendingSet.fn(previousValue);
1121
1115
  entry.state = "resolved";
1122
1116
  entry.hasValue = true;
1123
- this.emitStateChange("resolved", atom$1);
1124
- this.notifyListeners(atom$1, "resolved");
1125
- this.invalidationChain?.delete(atom$1);
1117
+ entry.error = void 0;
1118
+ entry.pendingInvalidate = false;
1119
+ entry.resolvedPromise = void 0;
1120
+ if (this.stateListeners.size) this.emitStateChange("resolved", atom$1);
1121
+ this.notifyEntry(entry, "resolved");
1126
1122
  return;
1127
1123
  }
1124
+ if (!this.invalidationChain) this.invalidationChain = /* @__PURE__ */ new Set();
1125
+ if (this.invalidationChain.has(atom$1)) {
1126
+ const chainAtoms = Array.from(this.invalidationChain);
1127
+ chainAtoms.push(atom$1);
1128
+ const path = chainAtoms.map((a) => a.factory?.name || "<anonymous>").join(" → ");
1129
+ throw new Error(`Infinite invalidation loop detected: ${path}`);
1130
+ }
1131
+ this.invalidationChain.add(atom$1);
1132
+ return this.doInvalidateAsync(atom$1, entry, previousValue);
1133
+ }
1134
+ async doInvalidateAsync(atom$1, entry, previousValue) {
1128
1135
  for (let i = entry.cleanups.length - 1; i >= 0; i--) try {
1129
1136
  await entry.cleanups[i]?.();
1130
1137
  } catch {}
@@ -1136,7 +1143,7 @@ var ScopeImpl = class {
1136
1143
  this.pending.delete(atom$1);
1137
1144
  this.resolving.delete(atom$1);
1138
1145
  this.emitStateChange("resolving", atom$1);
1139
- this.notifyListeners(atom$1, "resolving");
1146
+ this.notifyEntry(entry, "resolving");
1140
1147
  try {
1141
1148
  await this.resolve(atom$1);
1142
1149
  } catch (e) {
@@ -1153,15 +1160,17 @@ var ScopeImpl = class {
1153
1160
  for (let i = entry.cleanups.length - 1; i >= 0; i--) try {
1154
1161
  await entry.cleanups[i]?.();
1155
1162
  } catch {}
1156
- if (atom$1.deps) for (const dep of Object.values(atom$1.deps)) {
1163
+ if (atom$1.deps) for (const key in atom$1.deps) {
1164
+ const dep = atom$1.deps[key];
1157
1165
  const depAtom = isAtom(dep) ? dep : isControllerDep(dep) ? dep.atom : null;
1158
1166
  if (!depAtom) continue;
1159
1167
  const depEntry = this.cache.get(depAtom);
1160
1168
  if (depEntry) {
1161
1169
  depEntry.dependents.delete(atom$1);
1162
- this.maybeScheduleGC(depAtom);
1170
+ this.maybeScheduleGCEntry(depAtom, depEntry);
1163
1171
  }
1164
1172
  }
1173
+ this.notifyEntryAll(entry);
1165
1174
  this.cache.delete(atom$1);
1166
1175
  this.controllers.delete(atom$1);
1167
1176
  for (const [state, stateMap] of this.stateListeners) {
@@ -1174,7 +1183,7 @@ var ScopeImpl = class {
1174
1183
  await this.chainPromise;
1175
1184
  } catch {}
1176
1185
  this.disposed = true;
1177
- this.invalidationQueue.clear();
1186
+ this.invalidationQueue.length = 0;
1178
1187
  this.invalidationChain = null;
1179
1188
  this.chainPromise = null;
1180
1189
  for (const ext of this.extensions) if (ext.dispose) await ext.dispose(this);
@@ -1196,8 +1205,11 @@ var ScopeImpl = class {
1196
1205
  createContext(options) {
1197
1206
  if (this.disposed) throw new Error("Scope is disposed");
1198
1207
  const ctx = new ExecutionContextImpl(this, options);
1199
- for (const tagged of options?.tags ?? []) ctx.data.set(tagged.key, tagged.value);
1200
- for (const tagged of this.tags) if (!ctx.data.has(tagged.key)) ctx.data.set(tagged.key, tagged.value);
1208
+ const ctxTags = options?.tags;
1209
+ if (ctxTags && ctxTags.length > 0) for (let i = 0; i < ctxTags.length; i++) ctx.data.set(ctxTags[i].key, ctxTags[i].value);
1210
+ if (this.tags.length > 0) {
1211
+ for (let i = 0; i < this.tags.length; i++) if (!ctx.data.has(this.tags[i].key)) ctx.data.set(this.tags[i].key, this.tags[i].value);
1212
+ }
1201
1213
  return ctx;
1202
1214
  }
1203
1215
  };
@@ -1251,8 +1263,11 @@ var ExecutionContextImpl = class ExecutionContextImpl {
1251
1263
  execName,
1252
1264
  flowName: flow$1.name
1253
1265
  });
1254
- for (const tagged of execTags ?? []) childCtx.data.set(tagged.key, tagged.value);
1255
- for (const tagged of flow$1.tags ?? []) if (!childCtx.data.has(tagged.key)) childCtx.data.set(tagged.key, tagged.value);
1266
+ if (execTags && execTags.length > 0) for (let i = 0; i < execTags.length; i++) childCtx.data.set(execTags[i].key, execTags[i].value);
1267
+ const flowTags = flow$1.tags;
1268
+ if (flowTags && flowTags.length > 0) {
1269
+ for (let i = 0; i < flowTags.length; i++) if (!childCtx.data.has(flowTags[i].key)) childCtx.data.set(flowTags[i].key, flowTags[i].value);
1270
+ }
1256
1271
  try {
1257
1272
  const result = presetValue !== void 0 && typeof presetValue === "function" ? await childCtx.execPresetFn(flow$1, presetValue) : await childCtx.execFlowInternal(flow$1);
1258
1273
  await childCtx.close({ ok: true });
@@ -1287,18 +1302,18 @@ var ExecutionContextImpl = class ExecutionContextImpl {
1287
1302
  async execFlowInternal(flow$1) {
1288
1303
  const resolvedDeps = await this.scope.resolveDeps(flow$1.deps, this);
1289
1304
  const factory = flow$1.factory;
1290
- const doExec = async () => {
1291
- if (flow$1.deps) return factory(this, resolvedDeps);
1292
- else return factory(this);
1293
- };
1305
+ if (this.scope.execExts.length === 0) return flow$1.deps ? factory(this, resolvedDeps) : factory(this);
1306
+ const doExec = async () => flow$1.deps ? factory(this, resolvedDeps) : factory(this);
1294
1307
  return this.applyExecExtensions(flow$1, doExec);
1295
1308
  }
1296
1309
  async execFnInternal(options) {
1297
1310
  const { fn, params } = options;
1311
+ if (this.scope.execExts.length === 0) return fn(this, ...params);
1298
1312
  const doExec = () => Promise.resolve(fn(this, ...params));
1299
1313
  return this.applyExecExtensions(fn, doExec);
1300
1314
  }
1301
1315
  async execPresetFn(flow$1, fn) {
1316
+ if (this.scope.execExts.length === 0) return fn(this);
1302
1317
  const doExec = () => Promise.resolve(fn(this));
1303
1318
  return this.applyExecExtensions(flow$1, doExec);
1304
1319
  }
@@ -1314,9 +1329,13 @@ var ExecutionContextImpl = class ExecutionContextImpl {
1314
1329
  onClose(fn) {
1315
1330
  this.cleanups.push(fn);
1316
1331
  }
1317
- async close(result = { ok: true }) {
1318
- if (this.closed) return;
1332
+ close(result = { ok: true }) {
1333
+ if (this.closed) return Promise.resolve();
1319
1334
  this.closed = true;
1335
+ if (this.cleanups.length === 0) return Promise.resolve();
1336
+ return this.runCleanups(result);
1337
+ }
1338
+ async runCleanups(result) {
1320
1339
  for (let i = this.cleanups.length - 1; i >= 0; i--) try {
1321
1340
  await this.cleanups[i]?.(result);
1322
1341
  } catch {}