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