effect 3.6.7 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/cjs/Array.js +1 -1
  2. package/dist/cjs/Array.js.map +1 -1
  3. package/dist/cjs/Config.js +8 -1
  4. package/dist/cjs/Config.js.map +1 -1
  5. package/dist/cjs/Context.js +9 -1
  6. package/dist/cjs/Context.js.map +1 -1
  7. package/dist/cjs/Effect.js +26 -4
  8. package/dist/cjs/Effect.js.map +1 -1
  9. package/dist/cjs/Fiber.js.map +1 -1
  10. package/dist/cjs/FiberHandle.js +20 -8
  11. package/dist/cjs/FiberHandle.js.map +1 -1
  12. package/dist/cjs/FiberMap.js +22 -11
  13. package/dist/cjs/FiberMap.js.map +1 -1
  14. package/dist/cjs/FiberSet.js +23 -9
  15. package/dist/cjs/FiberSet.js.map +1 -1
  16. package/dist/cjs/Micro.js +9 -2
  17. package/dist/cjs/Micro.js.map +1 -1
  18. package/dist/cjs/Stream.js +30 -2
  19. package/dist/cjs/Stream.js.map +1 -1
  20. package/dist/cjs/internal/config.js +7 -1
  21. package/dist/cjs/internal/config.js.map +1 -1
  22. package/dist/cjs/internal/context.js +8 -1
  23. package/dist/cjs/internal/context.js.map +1 -1
  24. package/dist/cjs/internal/effect/circular.js +3 -1
  25. package/dist/cjs/internal/effect/circular.js.map +1 -1
  26. package/dist/cjs/internal/fiberRuntime.js +23 -15
  27. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  28. package/dist/cjs/internal/logger.js +4 -4
  29. package/dist/cjs/internal/logger.js.map +1 -1
  30. package/dist/cjs/internal/stream.js +6 -3
  31. package/dist/cjs/internal/stream.js.map +1 -1
  32. package/dist/cjs/internal/version.js +1 -1
  33. package/dist/dts/Array.d.ts +9 -9
  34. package/dist/dts/Array.d.ts.map +1 -1
  35. package/dist/dts/Config.d.ts +7 -0
  36. package/dist/dts/Config.d.ts.map +1 -1
  37. package/dist/dts/Context.d.ts +12 -0
  38. package/dist/dts/Context.d.ts.map +1 -1
  39. package/dist/dts/Effect.d.ts +84 -4
  40. package/dist/dts/Effect.d.ts.map +1 -1
  41. package/dist/dts/Fiber.d.ts +5 -1
  42. package/dist/dts/Fiber.d.ts.map +1 -1
  43. package/dist/dts/FiberHandle.d.ts +7 -0
  44. package/dist/dts/FiberHandle.d.ts.map +1 -1
  45. package/dist/dts/FiberMap.d.ts +7 -0
  46. package/dist/dts/FiberMap.d.ts.map +1 -1
  47. package/dist/dts/FiberSet.d.ts +17 -5
  48. package/dist/dts/FiberSet.d.ts.map +1 -1
  49. package/dist/dts/Micro.d.ts +6 -0
  50. package/dist/dts/Micro.d.ts.map +1 -1
  51. package/dist/dts/Stream.d.ts +30 -0
  52. package/dist/dts/Stream.d.ts.map +1 -1
  53. package/dist/dts/internal/stream.d.ts +0 -1
  54. package/dist/dts/internal/stream.d.ts.map +1 -1
  55. package/dist/esm/Array.js +1 -1
  56. package/dist/esm/Array.js.map +1 -1
  57. package/dist/esm/Config.js +7 -0
  58. package/dist/esm/Config.js.map +1 -1
  59. package/dist/esm/Context.js +8 -0
  60. package/dist/esm/Context.js.map +1 -1
  61. package/dist/esm/Effect.js +22 -0
  62. package/dist/esm/Effect.js.map +1 -1
  63. package/dist/esm/Fiber.js.map +1 -1
  64. package/dist/esm/FiberHandle.js +20 -8
  65. package/dist/esm/FiberHandle.js.map +1 -1
  66. package/dist/esm/FiberMap.js +22 -11
  67. package/dist/esm/FiberMap.js.map +1 -1
  68. package/dist/esm/FiberSet.js +23 -9
  69. package/dist/esm/FiberSet.js.map +1 -1
  70. package/dist/esm/Micro.js +7 -1
  71. package/dist/esm/Micro.js.map +1 -1
  72. package/dist/esm/Stream.js +27 -0
  73. package/dist/esm/Stream.js.map +1 -1
  74. package/dist/esm/internal/config.js +5 -0
  75. package/dist/esm/internal/config.js.map +1 -1
  76. package/dist/esm/internal/context.js +7 -0
  77. package/dist/esm/internal/context.js.map +1 -1
  78. package/dist/esm/internal/effect/circular.js +2 -0
  79. package/dist/esm/internal/effect/circular.js.map +1 -1
  80. package/dist/esm/internal/fiberRuntime.js +20 -13
  81. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  82. package/dist/esm/internal/logger.js +4 -4
  83. package/dist/esm/internal/logger.js.map +1 -1
  84. package/dist/esm/internal/stream.js +3 -0
  85. package/dist/esm/internal/stream.js.map +1 -1
  86. package/dist/esm/internal/version.js +1 -1
  87. package/package.json +1 -1
  88. package/src/Array.ts +39 -9
  89. package/src/Config.ts +8 -0
  90. package/src/Context.ts +13 -0
  91. package/src/Effect.ts +172 -19
  92. package/src/Fiber.ts +8 -1
  93. package/src/FiberHandle.ts +46 -10
  94. package/src/FiberMap.ts +48 -13
  95. package/src/FiberSet.ts +65 -24
  96. package/src/Micro.ts +8 -1
  97. package/src/Stream.ts +36 -0
  98. package/src/internal/config.ts +10 -0
  99. package/src/internal/context.ts +12 -0
  100. package/src/internal/effect/circular.ts +91 -0
  101. package/src/internal/fiberRuntime.ts +101 -30
  102. package/src/internal/logger.ts +5 -8
  103. package/src/internal/stream.ts +18 -0
  104. package/src/internal/version.ts +1 -1
@@ -10,7 +10,8 @@ import * as Exit from "./Exit.js"
10
10
  import * as Fiber from "./Fiber.js"
11
11
  import * as FiberId from "./FiberId.js"
12
12
  import * as FiberRef from "./FiberRef.js"
13
- import { dual } from "./Function.js"
13
+ import { constFalse, dual } from "./Function.js"
14
+ import * as HashSet from "./HashSet.js"
14
15
  import * as Inspectable from "./Inspectable.js"
15
16
  import type { FiberRuntime } from "./internal/fiberRuntime.js"
16
17
  import * as Option from "./Option.js"
@@ -142,6 +143,17 @@ export const makeRuntime = <R, E = unknown, A = unknown>(): Effect.Effect<
142
143
  (self) => runtime(self)<R>()
143
144
  )
144
145
 
146
+ const internalFiberIdId = -1
147
+ const internalFiberId = FiberId.make(internalFiberIdId, 0)
148
+ const isInternalInterruption = Cause.reduceWithContext(undefined, {
149
+ emptyCase: constFalse,
150
+ failCase: constFalse,
151
+ dieCase: constFalse,
152
+ interruptCase: (_, fiberId) => HashSet.has(FiberId.ids(fiberId), internalFiberIdId),
153
+ sequentialCase: (_, left, right) => left || right,
154
+ parallelCase: (_, left, right) => left || right
155
+ })
156
+
145
157
  /**
146
158
  * Set the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle.
147
159
  * If a fiber is already running, it will be interrupted unless `options.onlyIfMissing` is set.
@@ -155,6 +167,7 @@ export const unsafeSet: {
155
167
  options?: {
156
168
  readonly interruptAs?: FiberId.FiberId | undefined
157
169
  readonly onlyIfMissing?: boolean | undefined
170
+ readonly propagateInterruption?: boolean | undefined
158
171
  }
159
172
  ): (self: FiberHandle<A, E>) => void
160
173
  <A, E, XE extends E, XA extends A>(
@@ -163,6 +176,7 @@ export const unsafeSet: {
163
176
  options?: {
164
177
  readonly interruptAs?: FiberId.FiberId | undefined
165
178
  readonly onlyIfMissing?: boolean | undefined
179
+ readonly propagateInterruption?: boolean | undefined
166
180
  }
167
181
  ): void
168
182
  } = dual((args) => isFiberHandle(args[0]), <A, E, XE extends E, XA extends A>(
@@ -171,19 +185,20 @@ export const unsafeSet: {
171
185
  options?: {
172
186
  readonly interruptAs?: FiberId.FiberId | undefined
173
187
  readonly onlyIfMissing?: boolean | undefined
188
+ readonly propagateInterruption?: boolean | undefined
174
189
  }
175
190
  ): void => {
176
191
  if (self.state._tag === "Closed") {
177
- fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
192
+ fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
178
193
  return
179
194
  } else if (self.state.fiber !== undefined) {
180
195
  if (options?.onlyIfMissing === true) {
181
- fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
196
+ fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
182
197
  return
183
198
  } else if (self.state.fiber === fiber) {
184
199
  return
185
200
  }
186
- self.state.fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
201
+ self.state.fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
187
202
  self.state.fiber = undefined
188
203
  }
189
204
 
@@ -193,7 +208,14 @@ export const unsafeSet: {
193
208
  if (self.state._tag === "Open" && fiber === self.state.fiber) {
194
209
  self.state.fiber = undefined
195
210
  }
196
- if (Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause)) {
211
+ if (
212
+ Exit.isFailure(exit) &&
213
+ (
214
+ options?.propagateInterruption === true ?
215
+ !isInternalInterruption(exit.cause) :
216
+ !Cause.isInterruptedOnly(exit.cause)
217
+ )
218
+ ) {
197
219
  Deferred.unsafeDone(self.deferred, exit as any)
198
220
  }
199
221
  })
@@ -211,6 +233,7 @@ export const set: {
211
233
  fiber: Fiber.RuntimeFiber<XA, XE>,
212
234
  options?: {
213
235
  readonly onlyIfMissing?: boolean
236
+ readonly propagateInterruption?: boolean | undefined
214
237
  }
215
238
  ): (self: FiberHandle<A, E>) => Effect.Effect<void>
216
239
  <A, E, XE extends E, XA extends A>(
@@ -218,6 +241,7 @@ export const set: {
218
241
  fiber: Fiber.RuntimeFiber<XA, XE>,
219
242
  options?: {
220
243
  readonly onlyIfMissing?: boolean
244
+ readonly propagateInterruption?: boolean | undefined
221
245
  }
222
246
  ): Effect.Effect<void>
223
247
  } = dual((args) => isFiberHandle(args[0]), <A, E, XE extends E, XA extends A>(
@@ -225,6 +249,7 @@ export const set: {
225
249
  fiber: Fiber.RuntimeFiber<XA, XE>,
226
250
  options?: {
227
251
  readonly onlyIfMissing?: boolean
252
+ readonly propagateInterruption?: boolean | undefined
228
253
  }
229
254
  ): Effect.Effect<void> =>
230
255
  Effect.fiberIdWith(
@@ -232,7 +257,8 @@ export const set: {
232
257
  Effect.sync(() =>
233
258
  unsafeSet(self, fiber, {
234
259
  interruptAs: fiberId,
235
- onlyIfMissing: options?.onlyIfMissing
260
+ onlyIfMissing: options?.onlyIfMissing,
261
+ propagateInterruption: options?.propagateInterruption
236
262
  })
237
263
  )
238
264
  ))
@@ -261,12 +287,12 @@ export const get = <A, E>(self: FiberHandle<A, E>): Effect.Effect<Fiber.RuntimeF
261
287
  */
262
288
  export const clear = <A, E>(self: FiberHandle<A, E>): Effect.Effect<void> =>
263
289
  Effect.uninterruptibleMask((restore) =>
264
- Effect.suspend(() => {
290
+ Effect.withFiberRuntime((fiber) => {
265
291
  if (self.state._tag === "Closed" || self.state.fiber === undefined) {
266
292
  return Effect.void
267
293
  }
268
294
  return Effect.zipRight(
269
- restore(Fiber.interrupt(self.state.fiber)),
295
+ restore(Fiber.interruptAs(self.state.fiber, FiberId.combine(fiber.id(), internalFiberId))),
270
296
  Effect.sync(() => {
271
297
  if (self.state._tag === "Open") {
272
298
  self.state.fiber = undefined
@@ -298,6 +324,7 @@ export const run: {
298
324
  self: FiberHandle<A, E>,
299
325
  options?: {
300
326
  readonly onlyIfMissing?: boolean
327
+ readonly propagateInterruption?: boolean | undefined
301
328
  }
302
329
  ): <R, XE extends E, XA extends A>(
303
330
  effect: Effect.Effect<XA, XE, R>
@@ -307,13 +334,17 @@ export const run: {
307
334
  effect: Effect.Effect<XA, XE, R>,
308
335
  options?: {
309
336
  readonly onlyIfMissing?: boolean
337
+ readonly propagateInterruption?: boolean | undefined
310
338
  }
311
339
  ): Effect.Effect<Fiber.RuntimeFiber<XA, XE>, never, R>
312
340
  } = function() {
313
341
  const self = arguments[0] as FiberHandle
314
342
  if (Effect.isEffect(arguments[1])) {
315
343
  const effect = arguments[1]
316
- const options = arguments[2] as { readonly onlyIfMissing?: boolean } | undefined
344
+ const options = arguments[2] as {
345
+ readonly onlyIfMissing?: boolean
346
+ readonly propagateInterruption?: boolean | undefined
347
+ } | undefined
317
348
  return Effect.suspend(() => {
318
349
  if (self.state._tag === "Closed") {
319
350
  return Effect.interrupt
@@ -328,7 +359,10 @@ export const run: {
328
359
  )
329
360
  }) as any
330
361
  }
331
- const options = arguments[1] as { readonly onlyIfMissing?: boolean } | undefined
362
+ const options = arguments[1] as {
363
+ readonly onlyIfMissing?: boolean
364
+ readonly propagateInterruption?: boolean | undefined
365
+ } | undefined
332
366
  return (effect: Effect.Effect<unknown, unknown, any>) =>
333
367
  Effect.suspend(() => {
334
368
  if (self.state._tag === "Closed") {
@@ -382,6 +416,7 @@ export const runtime: <A, E>(
382
416
  options?:
383
417
  | Runtime.RunForkOptions & {
384
418
  readonly onlyIfMissing?: boolean | undefined
419
+ readonly propagateInterruption?: boolean | undefined
385
420
  }
386
421
  | undefined
387
422
  ) => Fiber.RuntimeFiber<XA, XE>,
@@ -397,6 +432,7 @@ export const runtime: <A, E>(
397
432
  options?:
398
433
  | Runtime.RunForkOptions & {
399
434
  readonly onlyIfMissing?: boolean | undefined
435
+ readonly propagateInterruption?: boolean | undefined
400
436
  }
401
437
  | undefined
402
438
  ) => {
package/src/FiberMap.ts CHANGED
@@ -10,7 +10,8 @@ import * as Exit from "./Exit.js"
10
10
  import * as Fiber from "./Fiber.js"
11
11
  import * as FiberId from "./FiberId.js"
12
12
  import * as FiberRef from "./FiberRef.js"
13
- import { dual } from "./Function.js"
13
+ import { constFalse, dual } from "./Function.js"
14
+ import * as HashSet from "./HashSet.js"
14
15
  import * as Inspectable from "./Inspectable.js"
15
16
  import type { FiberRuntime } from "./internal/fiberRuntime.js"
16
17
  import * as Iterable from "./Iterable.js"
@@ -157,6 +158,17 @@ export const makeRuntime = <R, K, E = unknown, A = unknown>(): Effect.Effect<
157
158
  (self) => runtime(self)<R>()
158
159
  )
159
160
 
161
+ const internalFiberIdId = -1
162
+ const internalFiberId = FiberId.make(internalFiberIdId, 0)
163
+ const isInternalInterruption = Cause.reduceWithContext(undefined, {
164
+ emptyCase: constFalse,
165
+ failCase: constFalse,
166
+ dieCase: constFalse,
167
+ interruptCase: (_, fiberId) => HashSet.has(FiberId.ids(fiberId), internalFiberIdId),
168
+ sequentialCase: (_, left, right) => left || right,
169
+ parallelCase: (_, left, right) => left || right
170
+ })
171
+
160
172
  /**
161
173
  * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap.
162
174
  * If the key already exists in the FiberMap, the previous fiber will be interrupted.
@@ -171,6 +183,7 @@ export const unsafeSet: {
171
183
  options?: {
172
184
  readonly interruptAs?: FiberId.FiberId | undefined
173
185
  readonly onlyIfMissing?: boolean | undefined
186
+ readonly propagateInterruption?: boolean | undefined
174
187
  } | undefined
175
188
  ): (self: FiberMap<K, A, E>) => void
176
189
  <K, A, E, XE extends E, XA extends A>(
@@ -180,6 +193,7 @@ export const unsafeSet: {
180
193
  options?: {
181
194
  readonly interruptAs?: FiberId.FiberId | undefined
182
195
  readonly onlyIfMissing?: boolean | undefined
196
+ readonly propagateInterruption?: boolean | undefined
183
197
  } | undefined
184
198
  ): void
185
199
  } = dual((args) => isFiberMap(args[0]), <K, A, E, XE extends E, XA extends A>(
@@ -189,22 +203,23 @@ export const unsafeSet: {
189
203
  options?: {
190
204
  readonly interruptAs?: FiberId.FiberId | undefined
191
205
  readonly onlyIfMissing?: boolean | undefined
206
+ readonly propagateInterruption?: boolean | undefined
192
207
  } | undefined
193
208
  ): void => {
194
209
  if (self.state._tag === "Closed") {
195
- fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
210
+ fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
196
211
  return
197
212
  }
198
213
 
199
214
  const previous = MutableHashMap.get(self.state.backing, key)
200
215
  if (previous._tag === "Some") {
201
216
  if (options?.onlyIfMissing === true) {
202
- fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
217
+ fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
203
218
  return
204
219
  } else if (previous.value === fiber) {
205
220
  return
206
221
  }
207
- previous.value.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
222
+ previous.value.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
208
223
  }
209
224
 
210
225
  ;(fiber as FiberRuntime<unknown, unknown>).setFiberRef(FiberRef.unhandledErrorLogLevel, Option.none())
@@ -217,7 +232,14 @@ export const unsafeSet: {
217
232
  if (Option.isSome(current) && fiber === current.value) {
218
233
  MutableHashMap.remove(self.state.backing, key)
219
234
  }
220
- if (Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause)) {
235
+ if (
236
+ Exit.isFailure(exit) &&
237
+ (
238
+ options?.propagateInterruption === true ?
239
+ !isInternalInterruption(exit.cause) :
240
+ !Cause.isInterruptedOnly(exit.cause)
241
+ )
242
+ ) {
221
243
  Deferred.unsafeDone(self.deferred, exit as any)
222
244
  }
223
245
  })
@@ -236,6 +258,7 @@ export const set: {
236
258
  fiber: Fiber.RuntimeFiber<XA, XE>,
237
259
  options?: {
238
260
  readonly onlyIfMissing?: boolean | undefined
261
+ readonly propagateInterruption?: boolean | undefined
239
262
  } | undefined
240
263
  ): (self: FiberMap<K, A, E>) => Effect.Effect<void>
241
264
  <K, A, E, XE extends E, XA extends A>(
@@ -244,6 +267,7 @@ export const set: {
244
267
  fiber: Fiber.RuntimeFiber<XA, XE>,
245
268
  options?: {
246
269
  readonly onlyIfMissing?: boolean | undefined
270
+ readonly propagateInterruption?: boolean | undefined
247
271
  } | undefined
248
272
  ): Effect.Effect<void>
249
273
  } = dual((args) => isFiberMap(args[0]), <K, A, E, XE extends E, XA extends A>(
@@ -252,14 +276,15 @@ export const set: {
252
276
  fiber: Fiber.RuntimeFiber<XA, XE>,
253
277
  options?: {
254
278
  readonly onlyIfMissing?: boolean | undefined
279
+ readonly propagateInterruption?: boolean | undefined
255
280
  } | undefined
256
281
  ): Effect.Effect<void> =>
257
282
  Effect.fiberIdWith(
258
283
  (fiberId) =>
259
284
  Effect.sync(() =>
260
285
  unsafeSet(self, key, fiber, {
261
- interruptAs: fiberId,
262
- onlyIfMissing: options?.onlyIfMissing
286
+ ...options,
287
+ interruptAs: fiberId
263
288
  })
264
289
  )
265
290
  ))
@@ -349,7 +374,7 @@ export const remove: {
349
374
  key: K
350
375
  ) => Effect.Effect<void>
351
376
  >(2, (self, key) =>
352
- Effect.suspend(() => {
377
+ Effect.withFiberRuntime((removeFiber) => {
353
378
  if (self.state._tag === "Closed") {
354
379
  return Effect.void
355
380
  }
@@ -358,7 +383,7 @@ export const remove: {
358
383
  return Effect.void
359
384
  }
360
385
  // will be removed by the observer
361
- return Fiber.interrupt(fiber.value)
386
+ return Fiber.interruptAs(fiber.value, FiberId.combine(removeFiber.id(), internalFiberId))
362
387
  }))
363
388
 
364
389
  /**
@@ -366,14 +391,14 @@ export const remove: {
366
391
  * @categories combinators
367
392
  */
368
393
  export const clear = <K, A, E>(self: FiberMap<K, A, E>): Effect.Effect<void> =>
369
- Effect.suspend(() => {
394
+ Effect.withFiberRuntime((clearFiber) => {
370
395
  if (self.state._tag === "Closed") {
371
396
  return Effect.void
372
397
  }
373
398
 
374
399
  return Effect.forEach(self.state.backing, ([, fiber]) =>
375
400
  // will be removed by the observer
376
- Fiber.interrupt(fiber))
401
+ Fiber.interruptAs(fiber, FiberId.combine(clearFiber.id(), internalFiberId)))
377
402
  })
378
403
 
379
404
  const constInterruptedFiber = (function() {
@@ -399,6 +424,7 @@ export const run: {
399
424
  key: K,
400
425
  options?: {
401
426
  readonly onlyIfMissing?: boolean | undefined
427
+ readonly propagateInterruption?: boolean | undefined
402
428
  } | undefined
403
429
  ): <R, XE extends E, XA extends A>(
404
430
  effect: Effect.Effect<XA, XE, R>
@@ -409,6 +435,7 @@ export const run: {
409
435
  effect: Effect.Effect<XA, XE, R>,
410
436
  options?: {
411
437
  readonly onlyIfMissing?: boolean | undefined
438
+ readonly propagateInterruption?: boolean | undefined
412
439
  } | undefined
413
440
  ): Effect.Effect<Fiber.RuntimeFiber<XA, XE>, never, R>
414
441
  } = function() {
@@ -416,7 +443,10 @@ export const run: {
416
443
  const self = arguments[0] as FiberMap<any>
417
444
  const key = arguments[1]
418
445
  const effect = arguments[2] as Effect.Effect<any, any, any>
419
- const options = arguments[3] as { readonly onlyIfMissing?: boolean } | undefined
446
+ const options = arguments[3] as {
447
+ readonly onlyIfMissing?: boolean
448
+ readonly propagateInterruption?: boolean | undefined
449
+ } | undefined
420
450
  return Effect.suspend(() => {
421
451
  if (self.state._tag === "Closed") {
422
452
  return Effect.interrupt
@@ -433,7 +463,10 @@ export const run: {
433
463
  }
434
464
  const self = arguments[0] as FiberMap<any>
435
465
  const key = arguments[1]
436
- const options = arguments[2] as { readonly onlyIfMissing?: boolean } | undefined
466
+ const options = arguments[2] as {
467
+ readonly onlyIfMissing?: boolean
468
+ readonly propagateInterruption?: boolean | undefined
469
+ } | undefined
437
470
  return (effect: Effect.Effect<any, any, any>) =>
438
471
  Effect.suspend(() => {
439
472
  if (self.state._tag === "Closed") {
@@ -486,6 +519,7 @@ export const runtime: <K, A, E>(
486
519
  options?:
487
520
  | Runtime.RunForkOptions & {
488
521
  readonly onlyIfMissing?: boolean | undefined
522
+ readonly propagateInterruption?: boolean | undefined
489
523
  }
490
524
  | undefined
491
525
  ) => Fiber.RuntimeFiber<XA, XE>,
@@ -502,6 +536,7 @@ export const runtime: <K, A, E>(
502
536
  options?:
503
537
  | Runtime.RunForkOptions & {
504
538
  readonly onlyIfMissing?: boolean | undefined
539
+ readonly propagateInterruption?: boolean | undefined
505
540
  }
506
541
  | undefined
507
542
  ) => {
package/src/FiberSet.ts CHANGED
@@ -9,7 +9,8 @@ import * as Deferred from "./Deferred.js"
9
9
  import * as Exit from "./Exit.js"
10
10
  import * as Fiber from "./Fiber.js"
11
11
  import * as FiberRef from "./FiberRef.js"
12
- import { dual } from "./Function.js"
12
+ import { constFalse, dual } from "./Function.js"
13
+ import * as HashSet from "./HashSet.js"
13
14
  import * as Inspectable from "./Inspectable.js"
14
15
  import type { FiberRuntime } from "./internal/fiberRuntime.js"
15
16
  import * as Iterable from "./Iterable.js"
@@ -146,6 +147,17 @@ export const makeRuntime = <R = never, A = unknown, E = unknown>(): Effect.Effec
146
147
  (self) => runtime(self)<R>()
147
148
  )
148
149
 
150
+ const internalFiberIdId = -1
151
+ const internalFiberId = FiberId.make(internalFiberIdId, 0)
152
+ const isInternalInterruption = Cause.reduceWithContext(undefined, {
153
+ emptyCase: constFalse,
154
+ failCase: constFalse,
155
+ dieCase: constFalse,
156
+ interruptCase: (_, fiberId) => HashSet.has(FiberId.ids(fiberId), internalFiberIdId),
157
+ sequentialCase: (_, left, right) => left || right,
158
+ parallelCase: (_, left, right) => left || right
159
+ })
160
+
149
161
  /**
150
162
  * Add a fiber to the FiberSet. When the fiber completes, it will be removed.
151
163
  *
@@ -157,6 +169,7 @@ export const unsafeAdd: {
157
169
  fiber: Fiber.RuntimeFiber<XA, XE>,
158
170
  options?: {
159
171
  readonly interruptAs?: FiberId.FiberId | undefined
172
+ readonly propagateInterruption?: boolean | undefined
160
173
  } | undefined
161
174
  ): (self: FiberSet<A, E>) => void
162
175
  <A, E, XE extends E, XA extends A>(
@@ -164,6 +177,7 @@ export const unsafeAdd: {
164
177
  fiber: Fiber.RuntimeFiber<XA, XE>,
165
178
  options?: {
166
179
  readonly interruptAs?: FiberId.FiberId | undefined
180
+ readonly propagateInterruption?: boolean | undefined
167
181
  } | undefined
168
182
  ): void
169
183
  } = dual((args) => isFiberSet(args[0]), <A, E, XE extends E, XA extends A>(
@@ -171,10 +185,11 @@ export const unsafeAdd: {
171
185
  fiber: Fiber.RuntimeFiber<XA, XE>,
172
186
  options?: {
173
187
  readonly interruptAs?: FiberId.FiberId | undefined
188
+ readonly propagateInterruption?: boolean | undefined
174
189
  } | undefined
175
190
  ): void => {
176
191
  if (self.state._tag === "Closed") {
177
- fiber.unsafeInterruptAsFork(options?.interruptAs ?? FiberId.none)
192
+ fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId))
178
193
  return
179
194
  } else if (self.state.backing.has(fiber)) {
180
195
  return
@@ -186,7 +201,14 @@ export const unsafeAdd: {
186
201
  return
187
202
  }
188
203
  self.state.backing.delete(fiber)
189
- if (Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause)) {
204
+ if (
205
+ Exit.isFailure(exit) &&
206
+ (
207
+ options?.propagateInterruption === true ?
208
+ !isInternalInterruption(exit.cause) :
209
+ !Cause.isInterruptedOnly(exit.cause)
210
+ )
211
+ ) {
190
212
  Deferred.unsafeDone(self.deferred, exit as any)
191
213
  }
192
214
  })
@@ -200,26 +222,31 @@ export const unsafeAdd: {
200
222
  */
201
223
  export const add: {
202
224
  <A, E, XE extends E, XA extends A>(
203
- fiber: Fiber.RuntimeFiber<XA, XE>
225
+ fiber: Fiber.RuntimeFiber<XA, XE>,
226
+ options?: {
227
+ readonly propagateInterruption?: boolean | undefined
228
+ } | undefined
204
229
  ): (self: FiberSet<A, E>) => Effect.Effect<void>
205
230
  <A, E, XE extends E, XA extends A>(
206
231
  self: FiberSet<A, E>,
207
- fiber: Fiber.RuntimeFiber<XA, XE>
232
+ fiber: Fiber.RuntimeFiber<XA, XE>,
233
+ options?: {
234
+ readonly propagateInterruption?: boolean | undefined
235
+ } | undefined
208
236
  ): Effect.Effect<void>
209
- } = dual<
210
- <A, E, XE extends E, XA extends A>(
211
- fiber: Fiber.RuntimeFiber<XA, XE>
212
- ) => (self: FiberSet<A, E>) => Effect.Effect<void>,
237
+ } = dual(
238
+ (args) => isFiberSet(args[0]),
213
239
  <A, E, XE extends E, XA extends A>(
214
240
  self: FiberSet<A, E>,
215
- fiber: Fiber.RuntimeFiber<XA, XE>
216
- ) => Effect.Effect<void>
217
- >(
218
- 2,
219
- (self, fiber) =>
241
+ fiber: Fiber.RuntimeFiber<XA, XE>,
242
+ options?: {
243
+ readonly propagateInterruption?: boolean | undefined
244
+ } | undefined
245
+ ): Effect.Effect<void> =>
220
246
  Effect.fiberIdWith((fiberId) =>
221
247
  Effect.sync(() =>
222
248
  unsafeAdd(self, fiber, {
249
+ ...options,
223
250
  interruptAs: fiberId
224
251
  })
225
252
  )
@@ -231,13 +258,13 @@ export const add: {
231
258
  * @categories combinators
232
259
  */
233
260
  export const clear = <A, E>(self: FiberSet<A, E>): Effect.Effect<void> =>
234
- Effect.suspend(() => {
261
+ Effect.withFiberRuntime((clearFiber) => {
235
262
  if (self.state._tag === "Closed") {
236
263
  return Effect.void
237
264
  }
238
265
  return Effect.forEach(self.state.backing, (fiber) =>
239
266
  // will be removed by the observer
240
- Fiber.interrupt(fiber))
267
+ Fiber.interruptAs(fiber, FiberId.combine(clearFiber.id(), internalFiberId)))
241
268
  })
242
269
 
243
270
  /**
@@ -248,16 +275,25 @@ export const clear = <A, E>(self: FiberSet<A, E>): Effect.Effect<void> =>
248
275
  * @categories combinators
249
276
  */
250
277
  export const run: {
251
- <A, E>(self: FiberSet<A, E>): <R, XE extends E, XA extends A>(
278
+ <A, E>(
279
+ self: FiberSet<A, E>,
280
+ options?: {
281
+ readonly propagateInterruption?: boolean | undefined
282
+ } | undefined
283
+ ): <R, XE extends E, XA extends A>(
252
284
  effect: Effect.Effect<XA, XE, R>
253
285
  ) => Effect.Effect<Fiber.RuntimeFiber<XA, XE>, never, R>
254
286
  <A, E, R, XE extends E, XA extends A>(
255
287
  self: FiberSet<A, E>,
256
- effect: Effect.Effect<XA, XE, R>
288
+ effect: Effect.Effect<XA, XE, R>,
289
+ options?: {
290
+ readonly propagateInterruption?: boolean | undefined
291
+ } | undefined
257
292
  ): Effect.Effect<Fiber.RuntimeFiber<XA, XE>, never, R>
258
293
  } = function() {
259
294
  const self = arguments[0] as FiberSet<any, any>
260
- if (arguments.length === 1) {
295
+ if (!Effect.isEffect(arguments[1])) {
296
+ const options = arguments[1] as { readonly propagateInterruption?: boolean | undefined } | undefined
261
297
  return (effect: Effect.Effect<any, any, any>) =>
262
298
  Effect.suspend(() => {
263
299
  if (self.state._tag === "Closed") {
@@ -266,12 +302,13 @@ export const run: {
266
302
  return Effect.uninterruptibleMask((restore) =>
267
303
  Effect.tap(
268
304
  restore(Effect.forkDaemon(effect)),
269
- (fiber) => add(self, fiber)
305
+ (fiber) => add(self, fiber, options)
270
306
  )
271
307
  )
272
308
  })
273
309
  }
274
- const effect = arguments[1] as Effect.Effect<any, any, any>
310
+ const effect = arguments[1]
311
+ const options = arguments[2] as { readonly propagateInterruption?: boolean | undefined } | undefined
275
312
  return Effect.suspend(() => {
276
313
  if (self.state._tag === "Closed") {
277
314
  return Effect.interrupt
@@ -279,7 +316,7 @@ export const run: {
279
316
  return Effect.uninterruptibleMask((restore) =>
280
317
  Effect.tap(
281
318
  restore(Effect.forkDaemon(effect)),
282
- (fiber) => add(self, fiber)
319
+ (fiber) => add(self, fiber, options)
283
320
  )
284
321
  )
285
322
  }) as any
@@ -316,7 +353,9 @@ export const runtime: <A, E>(
316
353
  ) => <R = never>() => Effect.Effect<
317
354
  <XE extends E, XA extends A>(
318
355
  effect: Effect.Effect<XA, XE, R>,
319
- options?: Runtime.RunForkOptions | undefined
356
+ options?:
357
+ | Runtime.RunForkOptions & { readonly propagateInterruption?: boolean | undefined }
358
+ | undefined
320
359
  ) => Fiber.RuntimeFiber<XA, XE>,
321
360
  never,
322
361
  R
@@ -327,7 +366,9 @@ export const runtime: <A, E>(
327
366
  const runFork = Runtime.runFork(runtime)
328
367
  return <XE extends E, XA extends A>(
329
368
  effect: Effect.Effect<XA, XE, R>,
330
- options?: Runtime.RunForkOptions | undefined
369
+ options?:
370
+ | Runtime.RunForkOptions & { readonly propagateInterruption?: boolean | undefined }
371
+ | undefined
331
372
  ) => {
332
373
  const fiber = runFork(effect, options)
333
374
  unsafeAdd(self, fiber)
package/src/Micro.ts CHANGED
@@ -19,7 +19,7 @@ import { StructuralPrototype } from "./internal/effectable.js"
19
19
  import { SingleShotGen } from "./internal/singleShotGen.js"
20
20
  import * as Option from "./Option.js"
21
21
  import { type Pipeable, pipeArguments } from "./Pipeable.js"
22
- import { isIterable, isTagged, type Predicate, type Refinement } from "./Predicate.js"
22
+ import { hasProperty, isIterable, isTagged, type Predicate, type Refinement } from "./Predicate.js"
23
23
  import type { ReadonlyRecord } from "./Record.js"
24
24
  import type { Sink, SinkTypeId } from "./Sink.js"
25
25
  import type { Stream, StreamTypeId } from "./Stream.js"
@@ -171,6 +171,13 @@ export type MicroCauseTypeId = typeof MicroCauseTypeId
171
171
  */
172
172
  export type MicroCause<E> = MicroCause.Die | MicroCause.Fail<E> | MicroCause.Interrupt
173
173
 
174
+ /**
175
+ * @since 3.6.6
176
+ * @experimental
177
+ * @category guards
178
+ */
179
+ export const isMicroCause = (self: unknown): self is MicroCause<unknown> => hasProperty(self, MicroCauseTypeId)
180
+
174
181
  /**
175
182
  * @since 3.4.6
176
183
  * @experimental
package/src/Stream.ts CHANGED
@@ -3472,6 +3472,42 @@ export const provideSomeLayer: {
3472
3472
  ): Stream<A, E | E2, RIn | Exclude<R, ROut>>
3473
3473
  } = internal.provideSomeLayer
3474
3474
 
3475
+ /**
3476
+ * Returns a stream that mirrors the first upstream to emit an item.
3477
+ * As soon as one of the upstream emits a first value, the other is interrupted.
3478
+ * The resulting stream will forward all items from the "winning" source stream.
3479
+ * Any upstream failures will cause the returned stream to fail.
3480
+ *
3481
+ * @example
3482
+ * import { Stream, Schedule, Console, Effect } from "effect"
3483
+ *
3484
+ * const stream = Stream.fromSchedule(Schedule.spaced('2 millis')).pipe(
3485
+ * Stream.race(Stream.fromSchedule(Schedule.spaced('1 millis'))),
3486
+ * Stream.take(6),
3487
+ * Stream.tap(Console.log)
3488
+ * )
3489
+ *
3490
+ * Effect.runPromise(Stream.runDrain(stream))
3491
+ * // Output each millisecond from the first stream, the rest streams are interrupted
3492
+ * // 0
3493
+ * // 1
3494
+ * // 2
3495
+ * // 3
3496
+ * // 4
3497
+ * // 5
3498
+ * @since 3.7.0
3499
+ * @category racing
3500
+ */
3501
+ export const race: {
3502
+ <AR, ER, RR>(
3503
+ right: Stream<AR, ER, RR>
3504
+ ): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AL | AR, EL | ER, RL | RR>
3505
+ <AL, EL, RL, AR, ER, RR>(
3506
+ left: Stream<AL, EL, RL>,
3507
+ right: Stream<AR, ER, RR>
3508
+ ): Stream<AL | AR, EL | ER, RL | RR>
3509
+ } = internal.race
3510
+
3475
3511
  /**
3476
3512
  * Returns a stream that mirrors the first upstream to emit an item.
3477
3513
  * As soon as one of the upstream emits a first value, all the others are interrupted.
@@ -447,6 +447,16 @@ export const string = (name?: string): Config.Config<string> => {
447
447
  return name === undefined ? config : nested(config, name)
448
448
  }
449
449
 
450
+ /** @internal */
451
+ export const nonEmptyString = (name?: string): Config.Config<string> => {
452
+ const config = primitive(
453
+ "a non-empty text property",
454
+ Either.liftPredicate((text) => text.length > 0, () => configError.MissingData([], "Expected a non-empty string"))
455
+ )
456
+
457
+ return name === undefined ? config : nested(config, name)
458
+ }
459
+
450
460
  /** @internal */
451
461
  export const all = <const Arg extends Iterable<Config.Config<any>> | Record<string, Config.Config<any>>>(
452
462
  arg: Arg