effect 4.0.0-rc.118 → 4.0.1

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.
Files changed (254) hide show
  1. package/README.md +17 -4
  2. package/dist/Array.d.ts +25 -25
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +16 -16
  5. package/dist/Array.js.map +1 -1
  6. package/dist/Cache.d.ts.map +1 -1
  7. package/dist/Cache.js +20 -16
  8. package/dist/Cache.js.map +1 -1
  9. package/dist/Cause.d.ts.map +1 -1
  10. package/dist/Cause.js +1 -1
  11. package/dist/Cause.js.map +1 -1
  12. package/dist/Chunk.d.ts +26 -26
  13. package/dist/Chunk.d.ts.map +1 -1
  14. package/dist/Chunk.js +15 -15
  15. package/dist/Chunk.js.map +1 -1
  16. package/dist/Effect.d.ts +167 -39
  17. package/dist/Effect.d.ts.map +1 -1
  18. package/dist/Effect.js +16 -10
  19. package/dist/Effect.js.map +1 -1
  20. package/dist/HashRing.d.ts.map +1 -1
  21. package/dist/HashRing.js +13 -5
  22. package/dist/HashRing.js.map +1 -1
  23. package/dist/MutableHashMap.d.ts.map +1 -1
  24. package/dist/MutableHashMap.js +4 -22
  25. package/dist/MutableHashMap.js.map +1 -1
  26. package/dist/Option.d.ts +17 -17
  27. package/dist/Option.d.ts.map +1 -1
  28. package/dist/Option.js +6 -6
  29. package/dist/Option.js.map +1 -1
  30. package/dist/Pool.d.ts +9 -5
  31. package/dist/Pool.d.ts.map +1 -1
  32. package/dist/Pool.js +34 -41
  33. package/dist/Pool.js.map +1 -1
  34. package/dist/Queue.d.ts +19 -2
  35. package/dist/Queue.d.ts.map +1 -1
  36. package/dist/Queue.js +9 -4
  37. package/dist/Queue.js.map +1 -1
  38. package/dist/RcMap.d.ts.map +1 -1
  39. package/dist/RcMap.js +16 -1
  40. package/dist/RcMap.js.map +1 -1
  41. package/dist/Record.d.ts +14 -14
  42. package/dist/Record.d.ts.map +1 -1
  43. package/dist/Record.js +10 -10
  44. package/dist/Record.js.map +1 -1
  45. package/dist/Schema.d.ts +37 -21
  46. package/dist/Schema.d.ts.map +1 -1
  47. package/dist/Schema.js +29 -10
  48. package/dist/Schema.js.map +1 -1
  49. package/dist/SchemaAST.d.ts.map +1 -1
  50. package/dist/SchemaAST.js +116 -97
  51. package/dist/SchemaAST.js.map +1 -1
  52. package/dist/SchemaRepresentation.d.ts +3 -1
  53. package/dist/SchemaRepresentation.d.ts.map +1 -1
  54. package/dist/SchemaRepresentation.js +3 -1
  55. package/dist/SchemaRepresentation.js.map +1 -1
  56. package/dist/ScopedCache.d.ts.map +1 -1
  57. package/dist/ScopedCache.js +24 -16
  58. package/dist/ScopedCache.js.map +1 -1
  59. package/dist/Stream.d.ts +45 -6
  60. package/dist/Stream.d.ts.map +1 -1
  61. package/dist/Stream.js +55 -44
  62. package/dist/Stream.js.map +1 -1
  63. package/dist/ai/LanguageModel.d.ts +8 -0
  64. package/dist/ai/LanguageModel.d.ts.map +1 -1
  65. package/dist/ai/LanguageModel.js +13 -2
  66. package/dist/ai/LanguageModel.js.map +1 -1
  67. package/dist/ai/McpSchema.d.ts +402 -402
  68. package/dist/ai/McpSchema.d.ts.map +1 -1
  69. package/dist/ai/McpSchema.js +1 -1
  70. package/dist/ai/McpSchema.js.map +1 -1
  71. package/dist/ai/McpServer.d.ts.map +1 -1
  72. package/dist/ai/McpServer.js +11 -3
  73. package/dist/ai/McpServer.js.map +1 -1
  74. package/dist/ai/Response.d.ts.map +1 -1
  75. package/dist/ai/Response.js +22 -12
  76. package/dist/ai/Response.js.map +1 -1
  77. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +1 -0
  78. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  79. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +1 -0
  80. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  81. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +1 -0
  82. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  83. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +1 -0
  84. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  85. package/dist/ai/internal/mcpSchema/v2024_11_05.js +3 -1
  86. package/dist/ai/internal/mcpSchema/v2024_11_05.js.map +1 -1
  87. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  88. package/dist/cluster/ClusterWorkflowEngine.js +16 -3
  89. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  90. package/dist/cluster/MessageStorage.d.ts +32 -8
  91. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  92. package/dist/cluster/MessageStorage.js +2 -1
  93. package/dist/cluster/MessageStorage.js.map +1 -1
  94. package/dist/cluster/Sharding.d.ts +5 -2
  95. package/dist/cluster/Sharding.d.ts.map +1 -1
  96. package/dist/cluster/Sharding.js +15 -8
  97. package/dist/cluster/Sharding.js.map +1 -1
  98. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  99. package/dist/cluster/SqlMessageStorage.js +36 -16
  100. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  101. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  102. package/dist/cluster/SqlRunnerStorage.js +30 -44
  103. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  104. package/dist/cluster/internal/entityManager.js +63 -62
  105. package/dist/cluster/internal/entityManager.js.map +1 -1
  106. package/dist/cluster/internal/interruptors.js +8 -0
  107. package/dist/cluster/internal/interruptors.js.map +1 -1
  108. package/dist/cluster/internal/resourceMap.js +39 -14
  109. package/dist/cluster/internal/resourceMap.js.map +1 -1
  110. package/dist/cluster/internal/resourceRef.js +168 -87
  111. package/dist/cluster/internal/resourceRef.js.map +1 -1
  112. package/dist/http/HttpEffect.d.ts +1 -0
  113. package/dist/http/HttpEffect.d.ts.map +1 -1
  114. package/dist/http/HttpEffect.js +29 -22
  115. package/dist/http/HttpEffect.js.map +1 -1
  116. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  117. package/dist/http/HttpMiddleware.js +3 -2
  118. package/dist/http/HttpMiddleware.js.map +1 -1
  119. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  120. package/dist/http/HttpStaticServer.js +14 -0
  121. package/dist/http/HttpStaticServer.js.map +1 -1
  122. package/dist/http/HttpTraceContext.d.ts +1 -1
  123. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  124. package/dist/http/HttpTraceContext.js +4 -4
  125. package/dist/http/HttpTraceContext.js.map +1 -1
  126. package/dist/http-api/HttpApiError.d.ts +1 -0
  127. package/dist/http-api/HttpApiError.d.ts.map +1 -1
  128. package/dist/http-api/HttpApiError.js +1 -0
  129. package/dist/http-api/HttpApiError.js.map +1 -1
  130. package/dist/internal/core.js +43 -2
  131. package/dist/internal/core.js.map +1 -1
  132. package/dist/internal/dateTime.js +1 -1
  133. package/dist/internal/dateTime.js.map +1 -1
  134. package/dist/internal/effect.js +138 -165
  135. package/dist/internal/effect.js.map +1 -1
  136. package/dist/internal/rcRef.js +59 -23
  137. package/dist/internal/rcRef.js.map +1 -1
  138. package/dist/internal/schema/annotations.js +1 -7
  139. package/dist/internal/schema/annotations.js.map +1 -1
  140. package/dist/internal/schema/codegen.js +56 -28
  141. package/dist/internal/schema/codegen.js.map +1 -1
  142. package/dist/internal/schema/fromJsonSchemaDocument.js +11 -8
  143. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  144. package/dist/internal/schema/toCodeDocument.js +10 -30
  145. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  146. package/dist/internal/schema/toCodec.js +1 -15
  147. package/dist/internal/schema/toCodec.js.map +1 -1
  148. package/dist/internal/schema/toEquivalence.js +4 -3
  149. package/dist/internal/schema/toEquivalence.js.map +1 -1
  150. package/dist/internal/schema/toFormatter.js +4 -4
  151. package/dist/internal/schema/toFormatter.js.map +1 -1
  152. package/dist/reactivity/Atom.d.ts.map +1 -1
  153. package/dist/reactivity/Atom.js +6 -2
  154. package/dist/reactivity/Atom.js.map +1 -1
  155. package/dist/reactivity/AtomRpc.d.ts +3 -3
  156. package/dist/reactivity/AtomRpc.d.ts.map +1 -1
  157. package/dist/rpc/Rpc.d.ts +2 -2
  158. package/dist/rpc/Rpc.d.ts.map +1 -1
  159. package/dist/rpc/RpcClient.d.ts +7 -7
  160. package/dist/rpc/RpcClient.d.ts.map +1 -1
  161. package/dist/rpc/RpcClient.js +21 -16
  162. package/dist/rpc/RpcClient.js.map +1 -1
  163. package/dist/rpc/RpcMessage.d.ts +1 -1
  164. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  165. package/dist/rpc/RpcServer.d.ts.map +1 -1
  166. package/dist/rpc/RpcServer.js +2 -2
  167. package/dist/rpc/RpcServer.js.map +1 -1
  168. package/dist/rpc/Utils.d.ts +2 -3
  169. package/dist/rpc/Utils.d.ts.map +1 -1
  170. package/dist/rpc/Utils.js +3 -4
  171. package/dist/rpc/Utils.js.map +1 -1
  172. package/dist/schema/SchemaCompiler/runtime.js +6 -1
  173. package/dist/schema/SchemaCompiler/runtime.js.map +1 -1
  174. package/dist/testing/TestSchema.d.ts +234 -6
  175. package/dist/testing/TestSchema.d.ts.map +1 -1
  176. package/dist/testing/TestSchema.js +244 -25
  177. package/dist/testing/TestSchema.js.map +1 -1
  178. package/dist/testing/index.d.ts +1 -0
  179. package/dist/testing/index.d.ts.map +1 -1
  180. package/dist/testing/index.js +1 -0
  181. package/dist/testing/index.js.map +1 -1
  182. package/dist/workflow/DurableDeferred.js +1 -1
  183. package/dist/workflow/DurableDeferred.js.map +1 -1
  184. package/dist/workflow/Workflow.js +1 -1
  185. package/dist/workflow/Workflow.js.map +1 -1
  186. package/dist/workflow/WorkflowEngine.d.ts +2 -0
  187. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  188. package/dist/workflow/WorkflowEngine.js +1 -0
  189. package/dist/workflow/WorkflowEngine.js.map +1 -1
  190. package/package.json +1 -1
  191. package/src/Array.ts +31 -31
  192. package/src/Cache.ts +22 -16
  193. package/src/Cause.ts +1 -1
  194. package/src/Chunk.ts +29 -29
  195. package/src/Effect.ts +170 -39
  196. package/src/HashRing.ts +14 -5
  197. package/src/MutableHashMap.ts +4 -24
  198. package/src/Option.ts +19 -19
  199. package/src/Pool.ts +28 -36
  200. package/src/Queue.ts +26 -6
  201. package/src/RcMap.ts +26 -2
  202. package/src/Record.ts +20 -20
  203. package/src/Schema.ts +48 -24
  204. package/src/SchemaAST.ts +135 -115
  205. package/src/SchemaRepresentation.ts +3 -1
  206. package/src/ScopedCache.ts +27 -16
  207. package/src/Stream.ts +158 -88
  208. package/src/ai/LanguageModel.ts +27 -2
  209. package/src/ai/McpSchema.ts +3 -3
  210. package/src/ai/McpServer.ts +12 -3
  211. package/src/ai/Response.ts +27 -12
  212. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +1 -0
  213. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +1 -0
  214. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +1 -0
  215. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +1 -0
  216. package/src/ai/internal/mcpSchema/v2024_11_05.ts +4 -2
  217. package/src/cluster/ClusterWorkflowEngine.ts +26 -5
  218. package/src/cluster/MessageStorage.ts +22 -5
  219. package/src/cluster/Sharding.ts +32 -28
  220. package/src/cluster/SqlMessageStorage.ts +45 -21
  221. package/src/cluster/SqlRunnerStorage.ts +85 -113
  222. package/src/cluster/internal/entityManager.ts +68 -80
  223. package/src/cluster/internal/interruptors.ts +13 -0
  224. package/src/cluster/internal/resourceMap.ts +55 -17
  225. package/src/cluster/internal/resourceRef.ts +189 -96
  226. package/src/http/HttpEffect.ts +15 -9
  227. package/src/http/HttpMiddleware.ts +8 -2
  228. package/src/http/HttpStaticServer.ts +16 -0
  229. package/src/http/HttpTraceContext.ts +8 -4
  230. package/src/http-api/HttpApiError.ts +1 -0
  231. package/src/internal/core.ts +56 -2
  232. package/src/internal/dateTime.ts +3 -1
  233. package/src/internal/effect.ts +146 -173
  234. package/src/internal/rcRef.ts +83 -33
  235. package/src/internal/schema/annotations.ts +0 -9
  236. package/src/internal/schema/codegen.ts +90 -43
  237. package/src/internal/schema/fromJsonSchemaDocument.ts +5 -3
  238. package/src/internal/schema/toCodeDocument.ts +10 -47
  239. package/src/internal/schema/toCodec.ts +3 -16
  240. package/src/internal/schema/toEquivalence.ts +4 -5
  241. package/src/internal/schema/toFormatter.ts +4 -6
  242. package/src/reactivity/Atom.ts +6 -2
  243. package/src/reactivity/AtomRpc.ts +5 -5
  244. package/src/rpc/Rpc.ts +2 -2
  245. package/src/rpc/RpcClient.ts +35 -32
  246. package/src/rpc/RpcMessage.ts +1 -1
  247. package/src/rpc/RpcServer.ts +2 -1
  248. package/src/rpc/Utils.ts +4 -10
  249. package/src/schema/SchemaCompiler/runtime.ts +17 -1
  250. package/src/testing/TestSchema.ts +315 -63
  251. package/src/testing/index.ts +1 -0
  252. package/src/workflow/DurableDeferred.ts +1 -1
  253. package/src/workflow/Workflow.ts +1 -1
  254. package/src/workflow/WorkflowEngine.ts +4 -0
@@ -17,7 +17,7 @@ import * as Result from "../Result.js";
17
17
  import * as Scheduler from "../Scheduler.js";
18
18
  import * as Tracer from "../Tracer.js";
19
19
  import { internalCall } from "../Utils.js";
20
- import { args, causeAnnotate, causeDie, causeEmpty, causeFromReasons, CauseImpl, constEmptyAnnotations, contA, contAll, contE, evaluate, exitDie, exitFail, exitFailCause, exitSucceed, ExitTypeId, Fail, InterruptorStackTrace, isCause, isDieReason, isEffect, isFailReason, isInterruptReason, isNoSuchElementError, makePrimitive, makePrimitiveProto, NoSuchElementError, ReasonBase, StackTraceKey as CauseStackTrace, TaggedError, withFiber, Yield } from "./core.js";
20
+ import { args, causeAnnotate, causeCombine, causeEmpty, causeFromReasons, CauseImpl, constEmptyAnnotations, contA, contAll, contE, evaluate, exitDie, exitFail, exitFailCause, exitSucceed, ExitTypeId, Fail, InterruptorStackTrace, isCause, isDieReason, isEffect, isFailReason, isInterruptReason, isNoSuchElementError, makePrimitive, makePrimitiveProto, NoSuchElementError, ReasonBase, StackTraceKey as CauseStackTrace, TaggedError, withFiber, Yield } from "./core.js";
21
21
  import * as doNotation from "./doNotation.js";
22
22
  import * as InternalMetric from "./metric.js";
23
23
  import * as InternalRecord from "./record.js";
@@ -126,35 +126,6 @@ export const causeAnnotations = self => {
126
126
  }
127
127
  return Context.makeUnsafe(map);
128
128
  };
129
- const dedupeReasons = (self, that) => {
130
- // Keep deduplication local so causeCombine does not retain Array.ts in the core bundle.
131
- // Snapshot both arrays before invoking user-defined hash or equality methods.
132
- const buckets = new Map();
133
- const out = [];
134
- for (const reason of self.concat(that)) {
135
- const hash = Hash.hash(reason);
136
- const bucket = buckets.get(hash);
137
- if (bucket === undefined) {
138
- buckets.set(hash, [reason]);
139
- } else if (bucket.some(previous => Equal.equals(previous, reason))) {
140
- continue;
141
- } else {
142
- bucket.push(reason);
143
- }
144
- out.push(reason);
145
- }
146
- return out;
147
- };
148
- /** @internal */
149
- export const causeCombine = /*#__PURE__*/dual(2, (self, that) => {
150
- if (self.reasons.length === 0) {
151
- return that;
152
- } else if (that.reasons.length === 0) {
153
- return self;
154
- }
155
- const newCause = new CauseImpl(dedupeReasons(self.reasons, that.reasons));
156
- return Equal.equals(self, newCause) ? self : newCause;
157
- });
158
129
  /** @internal */
159
130
  export const causeMap = /*#__PURE__*/dual(2, (self, f) => {
160
131
  let hasFail = false;
@@ -332,6 +303,14 @@ const fiberVariance = {
332
303
  const fiberIdStore = {
333
304
  id: 0
334
305
  };
306
+ const AsyncResource = /*#__PURE__*/(() => {
307
+ try {
308
+ return globalThis.process?.getBuiltinModule?.("node:async_hooks")?.AsyncResource;
309
+ } catch {
310
+ return undefined;
311
+ }
312
+ })();
313
+ const captureAsyncContext = () => AsyncResource === undefined ? undefined : new AsyncResource("effect/Fiber");
335
314
  /** @internal */
336
315
  export const getCurrentFiber = () => globalThis[currentFiberTypeId];
337
316
  /** @internal */
@@ -350,6 +329,7 @@ export class FiberImpl {
350
329
  this._running = false;
351
330
  this._deferredInterrupt = false;
352
331
  this._parent = undefined;
332
+ this._asyncContext = undefined;
353
333
  this.cache.runtimeMetrics?.recordFiberStart(this.context);
354
334
  }
355
335
  get [FiberTypeId]() {
@@ -406,6 +386,10 @@ export class FiberImpl {
406
386
  evaluate(effect) {
407
387
  if (this._exit) {
408
388
  return;
389
+ } else if (this._asyncContext !== undefined) {
390
+ const asyncContext = this._asyncContext;
391
+ this._asyncContext = undefined;
392
+ return asyncContext.runInAsyncScope(this.evaluate, this, effect);
409
393
  } else if (this._yielded !== undefined) {
410
394
  const yielded = this._yielded;
411
395
  this._yielded = undefined;
@@ -413,6 +397,7 @@ export class FiberImpl {
413
397
  }
414
398
  const exit = this.runLoop(effect);
415
399
  if (exit === Yield) {
400
+ this._asyncContext = captureAsyncContext();
416
401
  return;
417
402
  }
418
403
  // the interruptChildren middleware is added in Effect.forkChild, so it can be
@@ -782,17 +767,18 @@ export const tryPromise = options => {
782
767
  const f = typeof options === "function" ? options : options.try;
783
768
  const catcher = typeof options === "function" ? cause => new UnknownError(cause, "An error occurred in Effect.tryPromise") : options.catch;
784
769
  return callbackOptions(function (resume, signal) {
785
- const failWithCatch = cause => {
770
+ // Defer the catcher until the fiber restores its async context.
771
+ const failWithCatch = cause => suspend(() => {
786
772
  try {
787
- resume(fail(internalCall(() => catcher(cause))));
773
+ return fail(internalCall(() => catcher(cause)));
788
774
  } catch (err) {
789
- resume(die(err));
775
+ return die(err);
790
776
  }
791
- };
777
+ });
792
778
  try {
793
- f(signal).then(a => resume(succeed(a)), failWithCatch);
779
+ f(signal).then(a => resume(succeed(a)), e => resume(failWithCatch(e)));
794
780
  } catch (err) {
795
- failWithCatch(err);
781
+ resume(failWithCatch(err));
796
782
  }
797
783
  }, f.length !== 0);
798
784
  };
@@ -852,7 +838,7 @@ const asyncFinalizer = /*#__PURE__*/makePrimitive({
852
838
  }
853
839
  },
854
840
  [contE](cause, _fiber) {
855
- return hasInterrupts(cause) ? flatMap(this[args](), () => failCause(cause)) : failCause(cause);
841
+ return hasInterrupts(cause) ? flatMap(combineFinalizerCause(exitFailCause(cause), this[args]()), () => failCause(cause)) : failCause(cause);
856
842
  }
857
843
  });
858
844
  /** @internal */
@@ -1079,73 +1065,75 @@ export const asVoid = self => new ContImpl(self, returnPayload, exitVoid);
1079
1065
  /** @internal */
1080
1066
  export const sandbox = self => catchCause(self, fail);
1081
1067
  /** @internal */
1082
- export const raceAll = (all, options) => withFiber(parent => callback(resume => {
1083
- const effects = Arr.fromIterable(all);
1084
- const len = effects.length;
1085
- let doneCount = 0;
1086
- let done = false;
1068
+ export const raceAll = (all, options) => withFiber(parent => {
1087
1069
  const fibers = new Set();
1088
- const failures = [];
1089
- const onExit = (exit, fiber, i) => {
1090
- doneCount++;
1091
- if (exit._tag === "Failure") {
1092
- failures.push(...exit.cause.reasons);
1093
- if (doneCount >= len) {
1094
- resume(failCause(causeFromReasons(failures)));
1070
+ // Read fibers on exit to include losers forked after the race settles.
1071
+ onExitUnsafe(parent, () => fibers.size === 0 ? undefined : fiberInterruptAll(fibers));
1072
+ return callback(resume => {
1073
+ const effects = Arr.fromIterable(all);
1074
+ const len = effects.length;
1075
+ let doneCount = 0;
1076
+ let done = false;
1077
+ const failures = [];
1078
+ const onExit = (exit, fiber, i) => {
1079
+ doneCount++;
1080
+ if (exit._tag === "Failure") {
1081
+ failures.push(...exit.cause.reasons);
1082
+ if (doneCount >= len) {
1083
+ resume(failCause(causeFromReasons(failures)));
1084
+ }
1085
+ return;
1095
1086
  }
1096
- return;
1097
- }
1098
- const isWinner = !done;
1099
- done = true;
1100
- resume(fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit));
1101
- if (isWinner && options?.onWinner) {
1102
- options.onWinner({
1103
- fiber,
1104
- index: i,
1105
- parentFiber: parent
1106
- });
1107
- }
1108
- };
1109
- for (let i = 0; i < len; i++) {
1110
- const fiber = forkUnsafe(parent, effects[i], true, true, false);
1111
- fibers.add(fiber);
1112
- fiber.addObserver(exit => {
1113
- fibers.delete(fiber);
1114
- onExit(exit, fiber, i);
1115
- });
1116
- if (done) break;
1117
- }
1118
- return fiberInterruptAll(fibers);
1119
- }));
1120
- /** @internal */
1121
- export const raceAllFirst = (all, options) => withFiber(parent => callback(resume => {
1122
- let done = false;
1123
- const fibers = new Set();
1124
- const onExit = exit => {
1125
- done = true;
1126
- resume(fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit));
1127
- };
1128
- let i = 0;
1129
- for (const effect of all) {
1130
- if (done) break;
1131
- const index = i++;
1132
- const fiber = forkUnsafe(parent, effect, true, true, false);
1133
- fibers.add(fiber);
1134
- fiber.addObserver(exit => {
1135
- fibers.delete(fiber);
1136
1087
  const isWinner = !done;
1137
- onExit(exit);
1088
+ done = true;
1089
+ resume(exit);
1138
1090
  if (isWinner && options?.onWinner) {
1139
1091
  options.onWinner({
1140
1092
  fiber,
1141
- index,
1093
+ index: i,
1142
1094
  parentFiber: parent
1143
1095
  });
1144
1096
  }
1145
- });
1146
- }
1147
- return fiberInterruptAll(fibers);
1148
- }));
1097
+ };
1098
+ for (let i = 0; i < len; i++) {
1099
+ const fiber = forkUnsafe(parent, effects[i], true, true, false);
1100
+ fibers.add(fiber);
1101
+ fiber.addObserver(exit => {
1102
+ fibers.delete(fiber);
1103
+ onExit(exit, fiber, i);
1104
+ });
1105
+ if (done) break;
1106
+ }
1107
+ });
1108
+ });
1109
+ /** @internal */
1110
+ export const raceAllFirst = (all, options) => withFiber(parent => {
1111
+ const fibers = new Set();
1112
+ onExitUnsafe(parent, () => fibers.size === 0 ? undefined : fiberInterruptAll(fibers));
1113
+ return callback(resume => {
1114
+ let done = false;
1115
+ let i = 0;
1116
+ for (const effect of all) {
1117
+ if (done) break;
1118
+ const index = i++;
1119
+ const fiber = forkUnsafe(parent, effect, true, true, false);
1120
+ fibers.add(fiber);
1121
+ fiber.addObserver(exit => {
1122
+ fibers.delete(fiber);
1123
+ const isWinner = !done;
1124
+ done = true;
1125
+ resume(exit);
1126
+ if (isWinner && options?.onWinner) {
1127
+ options.onWinner({
1128
+ fiber,
1129
+ index,
1130
+ parentFiber: parent
1131
+ });
1132
+ }
1133
+ });
1134
+ }
1135
+ });
1136
+ });
1149
1137
  /** @internal */
1150
1138
  export const race = /*#__PURE__*/dual(args => isEffect(args[1]), (self, that, options) => raceAll([self, that], options));
1151
1139
  /** @internal */
@@ -1930,61 +1918,55 @@ export const acquireDisposable = acquire => acquireRelease(acquire, resource =>
1930
1918
  // Caching
1931
1919
  // ----------------------------------------------------------------------------
1932
1920
  /** @internal */
1933
- export const cachedInvalidateWithTTL = /*#__PURE__*/dual(2, (self, ttl) => sync(() => {
1934
- const ttlMillis = typeof ttl === "function" ? exit => Duration.toMillis(Duration.fromInputUnsafe(ttl(exit))) : constant(Duration.toMillis(Duration.fromInputUnsafe(ttl)));
1935
- const latch = makeLatchUnsafe(false);
1921
+ export const cachedInvalidateWithTTL = /*#__PURE__*/dual(2, (self, ttl) => sync(() => makeCachedUnsafe(self, typeof ttl === "function" ? exit => Duration.toMillis(Duration.fromInputUnsafe(ttl(exit))) : constant(Duration.toMillis(Duration.fromInputUnsafe(ttl))))));
1922
+ const infiniteTTL = /*#__PURE__*/constant(Infinity);
1923
+ const makeCachedUnsafe = (self, ttlMillis) => {
1936
1924
  let expiresAt = 0;
1937
- let running = false;
1938
1925
  let exit;
1939
- const wait = flatMap(latch.await, () => exit);
1926
+ let current;
1927
+ const join = (fiber, run) => {
1928
+ run.awaiters++;
1929
+ onExitUnsafe(fiber, () => {
1930
+ // Abandon the run once every caller has left, unless it already finished.
1931
+ if (--run.awaiters > 0 || current !== run) return;
1932
+ // Detach it first so new callers start a fresh run instead of joining
1933
+ // one that is being interrupted.
1934
+ current = undefined;
1935
+ return fiberInterrupt(run.fiber);
1936
+ });
1937
+ return fiberJoin(run.fiber);
1938
+ };
1940
1939
  return [withFiber(fiber => {
1941
- const clock = fiber.getRef(ClockRef);
1942
- const now = expiresAt === Infinity ? 0 : clock.currentTimeMillisUnsafe();
1943
- if (running || now < expiresAt) return exit ?? wait;
1944
- running = true;
1945
- latch.closeUnsafe();
1940
+ if (current !== undefined) return join(fiber, current);
1941
+ if (exit !== undefined && (expiresAt === Infinity || fiber.getRef(ClockRef).currentTimeMillisUnsafe() < expiresAt)) {
1942
+ return exit;
1943
+ }
1946
1944
  exit = undefined;
1947
- onExitUnsafe(fiber, exit_ => sync(() => {
1948
- try {
1949
- const duration = ttlMillis(exit_);
1950
- expiresAt = clock.currentTimeMillisUnsafe() + duration;
1951
- exit = exit_;
1952
- } catch (error) {
1953
- const cause = causeDie(error);
1954
- // Publish the same combined cause that onExit returns to the owner.
1955
- exit = exitFailCause(exitIsFailure(exit_) ? causeCombine(exit_.cause, cause) : cause);
1956
- throw error;
1957
- } finally {
1958
- running = false;
1959
- latch.openUnsafe();
1960
- }
1961
- }));
1962
- return self;
1945
+ const clock = fiber.getRef(ClockRef);
1946
+ const run = {
1947
+ fiber: undefined,
1948
+ awaiters: 0
1949
+ };
1950
+ current = run;
1951
+ run.fiber = forkUnsafe(fiber, onExitPrimitive(self, exit_ => {
1952
+ // An abandoned run must not overwrite or clear a replacement run.
1953
+ if (current !== run) return;
1954
+ current = undefined;
1955
+ // Interruption is abandonment, so it is never cached.
1956
+ if (exitHasInterrupts(exit_)) return;
1957
+ const duration = ttlMillis(exit_);
1958
+ expiresAt = duration === Infinity ? Infinity : clock.currentTimeMillisUnsafe() + duration;
1959
+ exit = exit_;
1960
+ }), true, true);
1961
+ return run.fiber._exit ?? join(fiber, run);
1963
1962
  }), sync(() => {
1964
- expiresAt = 0;
1965
- latch.closeUnsafe();
1966
1963
  exit = undefined;
1967
1964
  })];
1968
- }));
1965
+ };
1969
1966
  /** @internal */
1970
1967
  export const cachedWithTTL = /*#__PURE__*/dual(2, (self, timeToLive) => map(cachedInvalidateWithTTL(self, timeToLive), tuple => tuple[0]));
1971
1968
  /** @internal */
1972
- export const cached = self => sync(() => {
1973
- const latch = makeLatchUnsafe(false);
1974
- let started = false;
1975
- let exit;
1976
- const wait = flatMap(latch.await, () => exit);
1977
- return withFiber(fiber => {
1978
- if (exit !== undefined) return exit;
1979
- if (started) return wait;
1980
- started = true;
1981
- onExitUnsafe(fiber, result => sync(() => {
1982
- exit = result;
1983
- latch.openUnsafe();
1984
- }));
1985
- return self;
1986
- });
1987
- });
1969
+ export const cached = self => sync(() => makeCachedUnsafe(self, infiniteTTL)[0]);
1988
1970
  // ----------------------------------------------------------------------------
1989
1971
  // interruption
1990
1972
  // ----------------------------------------------------------------------------
@@ -2100,11 +2082,11 @@ export const reduce = /*#__PURE__*/dual(3, (elements, zero, f) => {
2100
2082
  /** @internal */
2101
2083
  export const validate = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, f, options) => flatMap(partition(elements, f, {
2102
2084
  concurrency: options?.concurrency
2103
- }), ([excluded, satisfying]) => {
2104
- if (Arr.isArrayNonEmpty(excluded)) {
2105
- return fail(excluded);
2085
+ }), ([passes, fails]) => {
2086
+ if (Arr.isArrayNonEmpty(fails)) {
2087
+ return fail(fails);
2106
2088
  }
2107
- return options?.discard ? void_ : succeed(satisfying);
2089
+ return options?.discard ? void_ : succeed(passes);
2108
2090
  }));
2109
2091
  /** @internal */
2110
2092
  export const findFirst = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, predicate) => suspend(() => {
@@ -2233,26 +2215,22 @@ const iterateConcurrentImpl = options => {
2233
2215
  let parentFiber;
2234
2216
  let fibers;
2235
2217
  let resume;
2236
- let interrupted = false;
2237
2218
  let terminal;
2238
2219
  let effect;
2239
2220
  const failDefect = error => {
2240
2221
  const defect = exitDie(error);
2241
2222
  terminal = defect;
2242
2223
  done = true;
2243
- interrupted = true;
2244
- return fibers && fibers.size > 0 ? flatMap(uninterruptible(fiberInterruptAll(Array.from(fibers))), () => defect) : defect;
2224
+ return fibers && fibers.size > 0 ? flatMap(uninterruptible(fiberInterruptAll(Array.from(fibers))), () => terminal ?? defect) : defect;
2245
2225
  };
2246
2226
  const go = () => {
2247
2227
  let paused = false;
2248
2228
  for (; !terminal && index < end; index++) {
2249
2229
  const item = items[index];
2250
2230
  const eff = effect ?? onItem(state, item, index);
2251
- // fast case (already an exit)
2252
2231
  if (effectIsExit(eff)) {
2253
2232
  terminal = step(state, item, eff, index);
2254
2233
  if (terminal) break;
2255
- // We have an effect, so enter "async" mode
2256
2234
  } else if (!parentFiber) {
2257
2235
  return callback(cb => {
2258
2236
  parentFiber = getCurrentFiber();
@@ -2267,14 +2245,12 @@ const iterateConcurrentImpl = options => {
2267
2245
  }
2268
2246
  if (result) return cb(result);
2269
2247
  return suspend(() => {
2270
- terminal = exitVoid;
2271
- interrupted = true;
2272
- return fibers ? fiberInterruptAll(fibers) : void_;
2248
+ terminal ??= exitVoid;
2249
+ return flatMap(fibers ? fiberInterruptAll(fibers) : void_, () => terminal?._tag === "Failure" ? terminal : void_);
2273
2250
  });
2274
2251
  });
2275
- // Fork the effect with concurrency > 1
2276
2252
  } else {
2277
- // Clear the temporary effect from capturing the parentFiber
2253
+ // Clear the effect cached before the parent fiber was available.
2278
2254
  effect = undefined;
2279
2255
  const fiber = forkUnsafe(parentFiber, eff, true, true, "inherit");
2280
2256
  if (fiber._exit) {
@@ -2282,27 +2258,23 @@ const iterateConcurrentImpl = options => {
2282
2258
  if (terminal) break;
2283
2259
  continue;
2284
2260
  }
2285
- // Add the fiber to the Set
2286
2261
  fibers.add(fiber);
2287
2262
  const currentIndex = index;
2288
2263
  fiber.addObserver(exit => {
2289
2264
  fibers.delete(fiber);
2290
2265
  try {
2291
2266
  if (terminal) {
2292
- if (!interrupted && exit._tag === "Failure") {
2293
- for (const reason of exit.cause.reasons) {
2294
- if (reason._tag === "Interrupt") continue;else if (terminal._tag === "Failure") {
2295
- ;
2296
- terminal.cause.reasons.push(reason);
2297
- } else {
2298
- terminal = exitFailCause(causeFromReasons([reason]));
2299
- }
2267
+ if (exit._tag === "Failure") {
2268
+ const reasons = exit.cause.reasons.filter(reason => reason._tag !== "Interrupt");
2269
+ if (reasons.length > 0) {
2270
+ const cause = causeFromReasons(reasons);
2271
+ terminal = exitFailCause(terminal._tag === "Failure" ? causeCombine(terminal.cause, cause) : cause);
2300
2272
  }
2301
2273
  }
2302
2274
  } else {
2303
2275
  const result = step(state, item, exit, currentIndex);
2304
2276
  if (result) {
2305
- terminal = result._tag === "Failure" ? exitFailCause(causeFromReasons(result.cause.reasons.slice())) : result;
2277
+ terminal = result;
2306
2278
  go();
2307
2279
  }
2308
2280
  }
@@ -2316,7 +2288,6 @@ const iterateConcurrentImpl = options => {
2316
2288
  resume(failDefect(error));
2317
2289
  }
2318
2290
  });
2319
- // Check if we have reached the concurrency limit
2320
2291
  if (fibers.size < concurrency) continue;
2321
2292
  paused = true;
2322
2293
  index++;
@@ -2436,6 +2407,8 @@ export const forkUnsafe = (parent, effect, immediate = false, daemon = false, un
2436
2407
  if (immediate) {
2437
2408
  child.evaluate(effect);
2438
2409
  } else {
2410
+ // Preserve the fork context rather than the dispatcher's context.
2411
+ child._asyncContext = captureAsyncContext();
2439
2412
  parentRuntime.currentDispatcher.scheduleTask(() => child.evaluate(effect), 0);
2440
2413
  }
2441
2414
  if (!daemon && !child._exit) {