effect 3.3.4 → 3.4.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 (103) hide show
  1. package/Micro/package.json +6 -0
  2. package/dist/cjs/Array.js +18 -3
  3. package/dist/cjs/Array.js.map +1 -1
  4. package/dist/cjs/Chunk.js +13 -2
  5. package/dist/cjs/Chunk.js.map +1 -1
  6. package/dist/cjs/Config.js.map +1 -1
  7. package/dist/cjs/Effect.js +249 -23
  8. package/dist/cjs/Effect.js.map +1 -1
  9. package/dist/cjs/Either.js +31 -1
  10. package/dist/cjs/Either.js.map +1 -1
  11. package/dist/cjs/ManagedRuntime.js.map +1 -1
  12. package/dist/cjs/Micro.js +2383 -0
  13. package/dist/cjs/Micro.js.map +1 -0
  14. package/dist/cjs/Option.js +1 -2
  15. package/dist/cjs/Option.js.map +1 -1
  16. package/dist/cjs/Schedule.js +2 -2
  17. package/dist/cjs/Stream.js.map +1 -1
  18. package/dist/cjs/Tuple.js +16 -9
  19. package/dist/cjs/Tuple.js.map +1 -1
  20. package/dist/cjs/index.js +4 -2
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/internal/core-effect.js +4 -2
  23. package/dist/cjs/internal/core-effect.js.map +1 -1
  24. package/dist/cjs/internal/core.js +12 -2
  25. package/dist/cjs/internal/core.js.map +1 -1
  26. package/dist/cjs/internal/fiberRuntime.js +32 -0
  27. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  28. package/dist/cjs/internal/stream.js.map +1 -1
  29. package/dist/cjs/internal/version.js +1 -1
  30. package/dist/dts/Array.d.ts +14 -0
  31. package/dist/dts/Array.d.ts.map +1 -1
  32. package/dist/dts/Cause.d.ts +1 -1
  33. package/dist/dts/Chunk.d.ts +11 -0
  34. package/dist/dts/Chunk.d.ts.map +1 -1
  35. package/dist/dts/Config.d.ts +3 -4
  36. package/dist/dts/Config.d.ts.map +1 -1
  37. package/dist/dts/Effect.d.ts +248 -20
  38. package/dist/dts/Effect.d.ts.map +1 -1
  39. package/dist/dts/Either.d.ts +35 -0
  40. package/dist/dts/Either.d.ts.map +1 -1
  41. package/dist/dts/ManagedRuntime.d.ts +15 -0
  42. package/dist/dts/ManagedRuntime.d.ts.map +1 -1
  43. package/dist/dts/Micro.d.ts +2002 -0
  44. package/dist/dts/Micro.d.ts.map +1 -0
  45. package/dist/dts/Option.d.ts +2 -0
  46. package/dist/dts/Option.d.ts.map +1 -1
  47. package/dist/dts/Schedule.d.ts +2 -2
  48. package/dist/dts/Stream.d.ts +45 -6
  49. package/dist/dts/Stream.d.ts.map +1 -1
  50. package/dist/dts/Tuple.d.ts +18 -0
  51. package/dist/dts/Tuple.d.ts.map +1 -1
  52. package/dist/dts/index.d.ts +7 -0
  53. package/dist/dts/index.d.ts.map +1 -1
  54. package/dist/dts/internal/core-effect.d.ts.map +1 -1
  55. package/dist/dts/internal/core.d.ts.map +1 -1
  56. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  57. package/dist/esm/Array.js +14 -0
  58. package/dist/esm/Array.js.map +1 -1
  59. package/dist/esm/Chunk.js +11 -0
  60. package/dist/esm/Chunk.js.map +1 -1
  61. package/dist/esm/Config.js.map +1 -1
  62. package/dist/esm/Effect.js +246 -20
  63. package/dist/esm/Effect.js.map +1 -1
  64. package/dist/esm/Either.js +30 -0
  65. package/dist/esm/Either.js.map +1 -1
  66. package/dist/esm/ManagedRuntime.js.map +1 -1
  67. package/dist/esm/Micro.js +2307 -0
  68. package/dist/esm/Micro.js.map +1 -0
  69. package/dist/esm/Option.js +1 -1
  70. package/dist/esm/Option.js.map +1 -1
  71. package/dist/esm/Schedule.js +2 -2
  72. package/dist/esm/Stream.js.map +1 -1
  73. package/dist/esm/Tuple.js +15 -8
  74. package/dist/esm/Tuple.js.map +1 -1
  75. package/dist/esm/index.js +7 -0
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/internal/core-effect.js +2 -0
  78. package/dist/esm/internal/core-effect.js.map +1 -1
  79. package/dist/esm/internal/core.js +10 -1
  80. package/dist/esm/internal/core.js.map +1 -1
  81. package/dist/esm/internal/fiberRuntime.js +32 -0
  82. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  83. package/dist/esm/internal/stream.js.map +1 -1
  84. package/dist/esm/internal/version.js +1 -1
  85. package/package.json +9 -1
  86. package/src/Array.ts +15 -0
  87. package/src/Cause.ts +1 -1
  88. package/src/Chunk.ts +12 -0
  89. package/src/Config.ts +7 -5
  90. package/src/Effect.ts +256 -20
  91. package/src/Either.ts +51 -0
  92. package/src/ManagedRuntime.ts +16 -0
  93. package/src/Micro.ts +3826 -0
  94. package/src/Option.ts +12 -1
  95. package/src/Schedule.ts +2 -2
  96. package/src/Stream.ts +60 -8
  97. package/src/Tuple.ts +18 -8
  98. package/src/index.ts +8 -0
  99. package/src/internal/core-effect.ts +33 -0
  100. package/src/internal/core.ts +18 -1
  101. package/src/internal/fiberRuntime.ts +32 -0
  102. package/src/internal/stream.ts +8 -4
  103. package/src/internal/version.ts +1 -1
@@ -1,4 +1,4 @@
1
- let moduleVersion = "3.3.4";
1
+ let moduleVersion = "3.4.0";
2
2
  export const getCurrentVersion = () => moduleVersion;
3
3
  export const setCurrentVersion = version => {
4
4
  moduleVersion = version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect",
3
- "version": "3.3.4",
3
+ "version": "3.4.0",
4
4
  "description": "The missing standard library for TypeScript, for writing production-grade software.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -372,6 +372,11 @@
372
372
  "import": "./dist/esm/MetricState.js",
373
373
  "default": "./dist/cjs/MetricState.js"
374
374
  },
375
+ "./Micro": {
376
+ "types": "./dist/dts/Micro.d.ts",
377
+ "import": "./dist/esm/Micro.js",
378
+ "default": "./dist/cjs/Micro.js"
379
+ },
375
380
  "./ModuleVersion": {
376
381
  "types": "./dist/dts/ModuleVersion.d.ts",
377
382
  "import": "./dist/esm/ModuleVersion.js",
@@ -1020,6 +1025,9 @@
1020
1025
  "MetricState": [
1021
1026
  "./dist/dts/MetricState.d.ts"
1022
1027
  ],
1028
+ "Micro": [
1029
+ "./dist/dts/Micro.d.ts"
1030
+ ],
1023
1031
  "ModuleVersion": [
1024
1032
  "./dist/dts/ModuleVersion.d.ts"
1025
1033
  ],
package/src/Array.ts CHANGED
@@ -145,6 +145,21 @@ export const replicate: {
145
145
  export const fromIterable = <A>(collection: Iterable<A>): Array<A> =>
146
146
  Array.isArray(collection) ? collection : Array.from(collection)
147
147
 
148
+ /**
149
+ * Creates a new `Array` from a value that might not be an iterable.
150
+ *
151
+ * @example
152
+ * import { Array } from "effect"
153
+ *
154
+ * assert.deepStrictEqual(Array.ensure("a"), ["a"])
155
+ * assert.deepStrictEqual(Array.ensure(["a"]), ["a"])
156
+ * assert.deepStrictEqual(Array.ensure(["a", "b", "c"]), ["a", "b", "c"])
157
+ *
158
+ * @category constructors
159
+ * @since 3.3.0
160
+ */
161
+ export const ensure = <A>(self: ReadonlyArray<A> | A): Array<A> => Array.isArray(self) ? self : [self as A]
162
+
148
163
  /**
149
164
  * Takes a record and returns an array of tuples containing its keys and values.
150
165
  *
package/src/Cause.ts CHANGED
@@ -192,7 +192,7 @@ export interface CauseReducer<in C, in E, in out Z> {
192
192
  */
193
193
  export interface YieldableError extends Pipeable, Inspectable, Readonly<Error> {
194
194
  readonly [Effect.EffectTypeId]: Effect.Effect.VarianceStruct<never, this, never>
195
- readonly [Stream.StreamTypeId]: Effect.Effect.VarianceStruct<never, this, never>
195
+ readonly [Stream.StreamTypeId]: Stream.Stream.VarianceStruct<never, this, never>
196
196
  readonly [Sink.SinkTypeId]: Sink.Sink.VarianceStruct<never, unknown, never, this, never>
197
197
  readonly [Channel.ChannelTypeId]: Channel.Channel.VarianceStruct<never, unknown, this, unknown, never, unknown, never>
198
198
  [Symbol.iterator](): Effect.EffectGenerator<Effect.Effect<never, this, never>>
package/src/Chunk.ts CHANGED
@@ -819,6 +819,8 @@ export const head: <A>(self: Chunk<A>) => Option<A> = get(0)
819
819
  /**
820
820
  * Returns the first element of this chunk.
821
821
  *
822
+ * It will throw an error if the chunk is empty.
823
+ *
822
824
  * @since 2.0.0
823
825
  * @category unsafe
824
826
  */
@@ -843,11 +845,21 @@ export const last = <A>(self: Chunk<A>): Option<A> => get(self, self.length - 1)
843
845
  /**
844
846
  * Returns the last element of this chunk.
845
847
  *
848
+ * It will throw an error if the chunk is empty.
849
+ *
846
850
  * @since 2.0.0
847
851
  * @category unsafe
848
852
  */
849
853
  export const unsafeLast = <A>(self: Chunk<A>): A => unsafeGet(self, self.length - 1)
850
854
 
855
+ /**
856
+ * Returns the last element of this non empty chunk.
857
+ *
858
+ * @since 3.4.0
859
+ * @category elements
860
+ */
861
+ export const lastNonEmpty: <A>(self: NonEmptyChunk<A>) => A = unsafeLast
862
+
851
863
  /**
852
864
  * @since 2.0.0
853
865
  */
package/src/Config.ts CHANGED
@@ -70,13 +70,15 @@ export declare namespace Config {
70
70
  * @since 2.0.0
71
71
  * @category models
72
72
  */
73
- export type Wrap<A> = [NonNullable<A>] extends [infer T] ?
74
- [T] extends [Record<string, any>] ? [Exclude<keyof T, string>] extends [never] ?
75
- | { readonly [K in keyof A]: Wrap<A[K]> }
76
- | Config<A>
77
- : Config<A>
73
+ export type Wrap<A> = [NonNullable<A>] extends [infer T] ? [IsPlainObject<T>] extends [true] ?
74
+ | { readonly [K in keyof A]: Wrap<A[K]> }
75
+ | Config<A>
78
76
  : Config<A>
79
77
  : Config<A>
78
+
79
+ type IsPlainObject<A> = [A] extends [Record<string, any>]
80
+ ? [keyof A] extends [never] ? false : [keyof A] extends [string] ? true : false
81
+ : false
80
82
  }
81
83
 
82
84
  /**
package/src/Effect.ts CHANGED
@@ -266,8 +266,38 @@ export const isEffect: (u: unknown) => u is Effect<unknown, unknown, unknown> =
266
266
  // -------------------------------------------------------------------------------------
267
267
 
268
268
  /**
269
- * Returns an effect that, if evaluated, will return the cached result of this
270
- * effect. Cached results will expire after `timeToLive` duration.
269
+ * Returns an effect that caches its result for a specified duration, known as
270
+ * the `timeToLive`. When the cache expires after the duration, the effect will be
271
+ * recomputed upon next evaluation.
272
+ *
273
+ * @example
274
+ * import { Effect, Console } from "effect"
275
+ *
276
+ * let i = 1
277
+ * const expensiveTask = Effect.promise<string>(() => {
278
+ * console.log("expensive task...")
279
+ * return new Promise((resolve) => {
280
+ * setTimeout(() => {
281
+ * resolve(`result ${i++}`)
282
+ * }, 100)
283
+ * })
284
+ * })
285
+ *
286
+ * const program = Effect.gen(function* () {
287
+ * const cached = yield* Effect.cachedWithTTL(expensiveTask, "150 millis")
288
+ * yield* cached.pipe(Effect.andThen(Console.log))
289
+ * yield* cached.pipe(Effect.andThen(Console.log))
290
+ * yield* Effect.sleep("100 millis")
291
+ * yield* cached.pipe(Effect.andThen(Console.log))
292
+ * })
293
+ *
294
+ * Effect.runFork(program)
295
+ * // Output:
296
+ * // expensive task...
297
+ * // result 1
298
+ * // result 1
299
+ * // expensive task...
300
+ * // result 2
271
301
  *
272
302
  * @since 2.0.0
273
303
  * @category caching
@@ -278,10 +308,41 @@ export const cachedWithTTL: {
278
308
  } = circular.cached
279
309
 
280
310
  /**
281
- * Returns an effect that, if evaluated, will return the cached result of this
282
- * effect. Cached results will expire after `timeToLive` duration. In
283
- * addition, returns an effect that can be used to invalidate the current
284
- * cached value before the `timeToLive` duration expires.
311
+ * Similar to {@link cachedWithTTL}, this function caches an effect's result for a
312
+ * specified duration. It also includes an additional effect for manually
313
+ * invalidating the cached value before it naturally expires.
314
+ *
315
+ * @example
316
+ * import { Effect, Console } from "effect"
317
+ *
318
+ * let i = 1
319
+ * const expensiveTask = Effect.promise<string>(() => {
320
+ * console.log("expensive task...")
321
+ * return new Promise((resolve) => {
322
+ * setTimeout(() => {
323
+ * resolve(`result ${i++}`)
324
+ * }, 100)
325
+ * })
326
+ * })
327
+ *
328
+ * const program = Effect.gen(function* () {
329
+ * const [cached, invalidate] = yield* Effect.cachedInvalidateWithTTL(
330
+ * expensiveTask,
331
+ * "1 hour"
332
+ * )
333
+ * yield* cached.pipe(Effect.andThen(Console.log))
334
+ * yield* cached.pipe(Effect.andThen(Console.log))
335
+ * yield* invalidate
336
+ * yield* cached.pipe(Effect.andThen(Console.log))
337
+ * })
338
+ *
339
+ * Effect.runFork(program)
340
+ * // Output:
341
+ * // expensive task...
342
+ * // result 1
343
+ * // result 1
344
+ * // expensive task...
345
+ * // result 2
285
346
  *
286
347
  * @since 2.0.0
287
348
  * @category caching
@@ -297,8 +358,44 @@ export const cachedInvalidateWithTTL: {
297
358
  } = circular.cachedInvalidateWithTTL
298
359
 
299
360
  /**
300
- * Returns an effect that, if evaluated, will return the lazily computed
301
- * result of this effect.
361
+ * Returns an effect that computes a result lazily and caches it. Subsequent
362
+ * evaluations of this effect will return the cached result without re-executing
363
+ * the logic.
364
+ *
365
+ * @example
366
+ * import { Effect, Console } from "effect"
367
+ *
368
+ * let i = 1
369
+ * const expensiveTask = Effect.promise<string>(() => {
370
+ * console.log("expensive task...")
371
+ * return new Promise((resolve) => {
372
+ * setTimeout(() => {
373
+ * resolve(`result ${i++}`)
374
+ * }, 100)
375
+ * })
376
+ * })
377
+ *
378
+ * const program = Effect.gen(function* () {
379
+ * console.log("non-cached version:")
380
+ * yield* expensiveTask.pipe(Effect.andThen(Console.log))
381
+ * yield* expensiveTask.pipe(Effect.andThen(Console.log))
382
+ * console.log("cached version:")
383
+ * const cached = yield* Effect.cached(expensiveTask)
384
+ * yield* cached.pipe(Effect.andThen(Console.log))
385
+ * yield* cached.pipe(Effect.andThen(Console.log))
386
+ * })
387
+ *
388
+ * Effect.runFork(program)
389
+ * // Output:
390
+ * // non-cached version:
391
+ * // expensive task...
392
+ * // result 1
393
+ * // expensive task...
394
+ * // result 2
395
+ * // cached version:
396
+ * // expensive task...
397
+ * // result 3
398
+ * // result 3
302
399
  *
303
400
  * @since 2.0.0
304
401
  * @category caching
@@ -306,7 +403,33 @@ export const cachedInvalidateWithTTL: {
306
403
  export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>> = effect.memoize
307
404
 
308
405
  /**
309
- * Returns a memoized version of the specified effectual function.
406
+ * Returns a memoized version of a function with effects. Memoization ensures
407
+ * that results are stored and reused for the same inputs, reducing the need to
408
+ * recompute them.
409
+ *
410
+ * @example
411
+ * import { Effect, Random } from "effect"
412
+ *
413
+ * const program = Effect.gen(function* () {
414
+ * const randomNumber = (n: number) => Random.nextIntBetween(1, n)
415
+ * console.log("non-memoized version:")
416
+ * console.log(yield* randomNumber(10))
417
+ * console.log(yield* randomNumber(10))
418
+ *
419
+ * console.log("memoized version:")
420
+ * const memoized = yield* Effect.cachedFunction(randomNumber)
421
+ * console.log(yield* memoized(10))
422
+ * console.log(yield* memoized(10))
423
+ * })
424
+ *
425
+ * Effect.runFork(program)
426
+ * // Example Output:
427
+ * // non-memoized version:
428
+ * // 2
429
+ * // 8
430
+ * // memoized version:
431
+ * // 5
432
+ * // 5
310
433
  *
311
434
  * @since 2.0.0
312
435
  * @category caching
@@ -317,24 +440,25 @@ export const cachedFunction: <A, B, E, R>(
317
440
  ) => Effect<(a: A) => Effect<B, E, R>> = circular.cachedFunction
318
441
 
319
442
  /**
320
- * Returns an effect that will be executed at most once, even if it is
321
- * evaluated multiple times.
443
+ * Returns an effect that executes only once, regardless of how many times it's
444
+ * called.
322
445
  *
323
446
  * @example
324
447
  * import { Effect, Console } from "effect"
325
448
  *
326
- * const program = Effect.gen(function* (_) {
327
- * const twice = Console.log("twice")
328
- * yield* _(twice, Effect.repeatN(1))
329
- * const once = yield* _(Console.log("once"), Effect.once)
330
- * yield* _(once, Effect.repeatN(1))
449
+ * const program = Effect.gen(function* () {
450
+ * const task1 = Console.log("task1")
451
+ * yield* Effect.repeatN(task1, 2)
452
+ * const task2 = yield* Effect.once(Console.log("task2"))
453
+ * yield* Effect.repeatN(task2, 2)
331
454
  * })
332
455
  *
333
456
  * Effect.runFork(program)
334
457
  * // Output:
335
- * // twice
336
- * // twice
337
- * // once
458
+ * // task1
459
+ * // task1
460
+ * // task1
461
+ * // task2
338
462
  *
339
463
  * @since 2.0.0
340
464
  * @category caching
@@ -2035,6 +2159,40 @@ export const uninterruptibleMask: <A, E, R>(
2035
2159
  f: (restore: <AX, EX, RX>(effect: Effect<AX, EX, RX>) => Effect<AX, EX, RX>) => Effect<A, E, R>
2036
2160
  ) => Effect<A, E, R> = core.uninterruptibleMask
2037
2161
 
2162
+ // -------------------------------------------------------------------------------------
2163
+ // lifting
2164
+ // -------------------------------------------------------------------------------------
2165
+
2166
+ /**
2167
+ * Transforms a `Predicate` function into an `Effect` returning the input value if the predicate returns `true`
2168
+ * or failing with specified error if the predicate fails
2169
+ *
2170
+ * @param predicate - A `Predicate` function that takes in a value of type `A` and returns a boolean.
2171
+ *
2172
+ * @example
2173
+ * import { Effect } from "effect"
2174
+ *
2175
+ * const isPositive = (n: number): boolean => n > 0
2176
+ *
2177
+ * // succeeds with `1`
2178
+ * Effect.liftPredicate(1, isPositive, n => `${n} is not positive`)
2179
+ *
2180
+ * // fails with `"0 is not positive"`
2181
+ * Effect.liftPredicate(0, isPositive, n => `${n} is not positive`)
2182
+ *
2183
+ * @category lifting
2184
+ * @since 3.4.0
2185
+ */
2186
+ export const liftPredicate: {
2187
+ <A, B extends A, E>(
2188
+ refinement: Refinement<NoInfer<A>, B>,
2189
+ orFailWith: (a: NoInfer<A>) => E
2190
+ ): (a: A) => Effect<B, E>
2191
+ <A, E>(predicate: Predicate<NoInfer<A>>, orFailWith: (a: NoInfer<A>) => E): (a: A) => Effect<A, E>
2192
+ <A, E, B extends A>(self: A, refinement: Refinement<A, B>, orFailWith: (a: A) => E): Effect<B, E>
2193
+ <A, E>(self: A, predicate: Predicate<NoInfer<A>>, orFailWith: (a: NoInfer<A>) => E): Effect<A, E>
2194
+ } = effect.liftPredicate
2195
+
2038
2196
  // -------------------------------------------------------------------------------------
2039
2197
  // mapping
2040
2198
  // -------------------------------------------------------------------------------------
@@ -2457,7 +2615,7 @@ export const scopeWith: <A, E, R>(f: (scope: Scope.Scope) => Effect<A, E, R>) =>
2457
2615
  fiberRuntime.scopeWith
2458
2616
 
2459
2617
  /**
2460
- * Scopes all resources uses in this workflow to the lifetime of the workflow,
2618
+ * Scopes all resources used in this workflow to the lifetime of the workflow,
2461
2619
  * ensuring that their finalizers are run as soon as this workflow completes
2462
2620
  * execution, whether by success, failure, or interruption.
2463
2621
  *
@@ -5028,6 +5186,55 @@ export const validateWith: {
5028
5186
  } = fiberRuntime.validateWith
5029
5187
 
5030
5188
  /**
5189
+ * The `Effect.zip` function allows you to combine two effects into a single
5190
+ * effect. This combined effect yields a tuple containing the results of both
5191
+ * input effects once they succeed.
5192
+ *
5193
+ * Note that `Effect.zip` processes effects sequentially: it first completes the
5194
+ * effect on the left and then the effect on the right.
5195
+ *
5196
+ * If you want to run the effects concurrently, you can use the `concurrent` option.
5197
+ *
5198
+ * @example
5199
+ * import { Effect } from "effect"
5200
+ *
5201
+ * const task1 = Effect.succeed(1).pipe(
5202
+ * Effect.delay("200 millis"),
5203
+ * Effect.tap(Effect.log("task1 done"))
5204
+ * )
5205
+ * const task2 = Effect.succeed("hello").pipe(
5206
+ * Effect.delay("100 millis"),
5207
+ * Effect.tap(Effect.log("task2 done"))
5208
+ * )
5209
+ *
5210
+ * const task3 = Effect.zip(task1, task2)
5211
+ *
5212
+ * Effect.runPromise(task3).then(console.log)
5213
+ * // Output:
5214
+ * // timestamp=... level=INFO fiber=#0 message="task1 done"
5215
+ * // timestamp=... level=INFO fiber=#0 message="task2 done"
5216
+ * // [ 1, 'hello' ]
5217
+ *
5218
+ * @example
5219
+ * import { Effect } from "effect"
5220
+ *
5221
+ * const task1 = Effect.succeed(1).pipe(
5222
+ * Effect.delay("200 millis"),
5223
+ * Effect.tap(Effect.log("task1 done"))
5224
+ * )
5225
+ * const task2 = Effect.succeed("hello").pipe(
5226
+ * Effect.delay("100 millis"),
5227
+ * Effect.tap(Effect.log("task2 done"))
5228
+ * )
5229
+ *
5230
+ * const task3 = Effect.zip(task1, task2, { concurrent: true })
5231
+ *
5232
+ * Effect.runPromise(task3).then(console.log)
5233
+ * // Output:
5234
+ * // timestamp=... level=INFO fiber=#0 message="task2 done"
5235
+ * // timestamp=... level=INFO fiber=#0 message="task1 done"
5236
+ * // [ 1, 'hello' ]
5237
+ *
5031
5238
  * @since 2.0.0
5032
5239
  * @category zipping
5033
5240
  */
@@ -5122,6 +5329,35 @@ export const zipRight: {
5122
5329
  } = fiberRuntime.zipRightOptions
5123
5330
 
5124
5331
  /**
5332
+ * The `Effect.zipWith` function operates similarly to {@link zip} by combining
5333
+ * two effects. However, instead of returning a tuple, it allows you to apply a
5334
+ * function to the results of the combined effects, transforming them into a
5335
+ * single value
5336
+ *
5337
+ * @example
5338
+ * import { Effect } from "effect"
5339
+ *
5340
+ * const task1 = Effect.succeed(1).pipe(
5341
+ * Effect.delay("200 millis"),
5342
+ * Effect.tap(Effect.log("task1 done"))
5343
+ * )
5344
+ * const task2 = Effect.succeed("hello").pipe(
5345
+ * Effect.delay("100 millis"),
5346
+ * Effect.tap(Effect.log("task2 done"))
5347
+ * )
5348
+ *
5349
+ * const task3 = Effect.zipWith(
5350
+ * task1,
5351
+ * task2,
5352
+ * (number, string) => number + string.length
5353
+ * )
5354
+ *
5355
+ * Effect.runPromise(task3).then(console.log)
5356
+ * // Output:
5357
+ * // timestamp=... level=INFO fiber=#3 message="task1 done"
5358
+ * // timestamp=... level=INFO fiber=#2 message="task2 done"
5359
+ * // 6
5360
+ *
5125
5361
  * @since 2.0.0
5126
5362
  * @category zipping
5127
5363
  */
package/src/Either.ts CHANGED
@@ -389,6 +389,57 @@ export const match: {
389
389
  }): B | C => isLeft(self) ? onLeft(self.left) : onRight(self.right)
390
390
  )
391
391
 
392
+ /**
393
+ * Transforms a `Predicate` function into a `Right` of the input value if the predicate returns `true`
394
+ * or `Left` of the result of the provided function if the predicate returns false
395
+ *
396
+ * @param predicate - A `Predicate` function that takes in a value of type `A` and returns a boolean.
397
+ *
398
+ * @example
399
+ * import { pipe, Either } from "effect"
400
+ *
401
+ * const isPositive = (n: number): boolean => n > 0
402
+ *
403
+ * assert.deepStrictEqual(
404
+ * pipe(
405
+ * 1,
406
+ * Either.liftPredicate(isPositive, n => `${n} is not positive`)
407
+ * ),
408
+ * Either.right(1)
409
+ * )
410
+ * assert.deepStrictEqual(
411
+ * pipe(
412
+ * 0,
413
+ * Either.liftPredicate(isPositive, n => `${n} is not positive`)
414
+ * ),
415
+ * Either.left("0 is not positive")
416
+ * )
417
+ *
418
+ * @category lifting
419
+ * @since 3.4.0
420
+ */
421
+ export const liftPredicate: {
422
+ <A, B extends A, E>(refinement: Refinement<NoInfer<A>, B>, orLeftWith: (a: NoInfer<A>) => E): (a: A) => Either<B, E>
423
+ <A, E>(
424
+ predicate: Predicate<NoInfer<A>>,
425
+ orLeftWith: (a: NoInfer<A>) => E
426
+ ): (a: A) => Either<A, E>
427
+ <A, E, B extends A>(
428
+ self: A,
429
+ refinement: Refinement<A, B>,
430
+ orLeftWith: (a: A) => E
431
+ ): Either<B, E>
432
+ <A, E>(
433
+ self: A,
434
+ predicate: Predicate<NoInfer<A>>,
435
+ orLeftWith: (a: NoInfer<A>) => E
436
+ ): Either<A, E>
437
+ } = dual(
438
+ 3,
439
+ <A, E>(a: A, predicate: Predicate<A>, orLeftWith: (a: A) => E): Either<A, E> =>
440
+ predicate(a) ? right(a) : left(orLeftWith(a))
441
+ )
442
+
392
443
  /**
393
444
  * Filter the right value with the provided function.
394
445
  * If the predicate fails, set the left value with the result of the provided function.
@@ -9,6 +9,22 @@ import type * as Layer from "./Layer.js"
9
9
  import type { Pipeable } from "./Pipeable.js"
10
10
  import type * as Runtime from "./Runtime.js"
11
11
 
12
+ /**
13
+ * @since 3.4.0
14
+ */
15
+ export declare namespace ManagedRuntime {
16
+ /**
17
+ * @category type-level
18
+ * @since 3.4.0
19
+ */
20
+ export type Context<T extends ManagedRuntime<any, any>> = [T] extends [ManagedRuntime<infer R, infer _E>] ? R : never
21
+ /**
22
+ * @category type-level
23
+ * @since 3.4.0
24
+ */
25
+ export type Error<T extends ManagedRuntime<any, any>> = [T] extends [ManagedRuntime<infer _R, infer E>] ? E : never
26
+ }
27
+
12
28
  /**
13
29
  * @since 2.0.0
14
30
  * @category models