effect 3.3.5 → 3.4.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 (98) 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/Effect.js +236 -15
  7. package/dist/cjs/Effect.js.map +1 -1
  8. package/dist/cjs/Either.js +31 -1
  9. package/dist/cjs/Either.js.map +1 -1
  10. package/dist/cjs/ManagedRuntime.js.map +1 -1
  11. package/dist/cjs/Micro.js +2383 -0
  12. package/dist/cjs/Micro.js.map +1 -0
  13. package/dist/cjs/Option.js +1 -2
  14. package/dist/cjs/Option.js.map +1 -1
  15. package/dist/cjs/Schedule.js +2 -2
  16. package/dist/cjs/Stream.js.map +1 -1
  17. package/dist/cjs/Tuple.js +16 -9
  18. package/dist/cjs/Tuple.js.map +1 -1
  19. package/dist/cjs/index.js +4 -2
  20. package/dist/cjs/index.js.map +1 -1
  21. package/dist/cjs/internal/core-effect.js +4 -2
  22. package/dist/cjs/internal/core-effect.js.map +1 -1
  23. package/dist/cjs/internal/core.js +12 -2
  24. package/dist/cjs/internal/core.js.map +1 -1
  25. package/dist/cjs/internal/fiberRuntime.js +32 -0
  26. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  27. package/dist/cjs/internal/stream.js.map +1 -1
  28. package/dist/cjs/internal/version.js +1 -1
  29. package/dist/dts/Array.d.ts +14 -0
  30. package/dist/dts/Array.d.ts.map +1 -1
  31. package/dist/dts/Cause.d.ts +1 -1
  32. package/dist/dts/Chunk.d.ts +11 -0
  33. package/dist/dts/Chunk.d.ts.map +1 -1
  34. package/dist/dts/Effect.d.ts +235 -12
  35. package/dist/dts/Effect.d.ts.map +1 -1
  36. package/dist/dts/Either.d.ts +35 -0
  37. package/dist/dts/Either.d.ts.map +1 -1
  38. package/dist/dts/ManagedRuntime.d.ts +15 -0
  39. package/dist/dts/ManagedRuntime.d.ts.map +1 -1
  40. package/dist/dts/Micro.d.ts +2010 -0
  41. package/dist/dts/Micro.d.ts.map +1 -0
  42. package/dist/dts/Option.d.ts +2 -0
  43. package/dist/dts/Option.d.ts.map +1 -1
  44. package/dist/dts/Schedule.d.ts +2 -2
  45. package/dist/dts/Stream.d.ts +45 -6
  46. package/dist/dts/Stream.d.ts.map +1 -1
  47. package/dist/dts/Tuple.d.ts +18 -0
  48. package/dist/dts/Tuple.d.ts.map +1 -1
  49. package/dist/dts/index.d.ts +7 -0
  50. package/dist/dts/index.d.ts.map +1 -1
  51. package/dist/dts/internal/core-effect.d.ts.map +1 -1
  52. package/dist/dts/internal/core.d.ts.map +1 -1
  53. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  54. package/dist/esm/Array.js +14 -0
  55. package/dist/esm/Array.js.map +1 -1
  56. package/dist/esm/Chunk.js +11 -0
  57. package/dist/esm/Chunk.js.map +1 -1
  58. package/dist/esm/Effect.js +233 -12
  59. package/dist/esm/Effect.js.map +1 -1
  60. package/dist/esm/Either.js +30 -0
  61. package/dist/esm/Either.js.map +1 -1
  62. package/dist/esm/ManagedRuntime.js.map +1 -1
  63. package/dist/esm/Micro.js +2307 -0
  64. package/dist/esm/Micro.js.map +1 -0
  65. package/dist/esm/Option.js +1 -1
  66. package/dist/esm/Option.js.map +1 -1
  67. package/dist/esm/Schedule.js +2 -2
  68. package/dist/esm/Stream.js.map +1 -1
  69. package/dist/esm/Tuple.js +15 -8
  70. package/dist/esm/Tuple.js.map +1 -1
  71. package/dist/esm/index.js +7 -0
  72. package/dist/esm/index.js.map +1 -1
  73. package/dist/esm/internal/core-effect.js +2 -0
  74. package/dist/esm/internal/core-effect.js.map +1 -1
  75. package/dist/esm/internal/core.js +10 -1
  76. package/dist/esm/internal/core.js.map +1 -1
  77. package/dist/esm/internal/fiberRuntime.js +32 -0
  78. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  79. package/dist/esm/internal/stream.js.map +1 -1
  80. package/dist/esm/internal/version.js +1 -1
  81. package/package.json +9 -1
  82. package/src/Array.ts +15 -0
  83. package/src/Cause.ts +1 -1
  84. package/src/Chunk.ts +12 -0
  85. package/src/Effect.ts +243 -12
  86. package/src/Either.ts +51 -0
  87. package/src/ManagedRuntime.ts +16 -0
  88. package/src/Micro.ts +3835 -0
  89. package/src/Option.ts +12 -1
  90. package/src/Schedule.ts +2 -2
  91. package/src/Stream.ts +60 -8
  92. package/src/Tuple.ts +18 -8
  93. package/src/index.ts +8 -0
  94. package/src/internal/core-effect.ts +33 -0
  95. package/src/internal/core.ts +18 -1
  96. package/src/internal/fiberRuntime.ts +32 -0
  97. package/src/internal/stream.ts +8 -4
  98. package/src/internal/version.ts +1 -1
@@ -1,4 +1,4 @@
1
- let moduleVersion = "3.3.5";
1
+ let moduleVersion = "3.4.1";
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.5",
3
+ "version": "3.4.1",
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/Effect.ts CHANGED
@@ -291,7 +291,7 @@ export const isEffect: (u: unknown) => u is Effect<unknown, unknown, unknown> =
291
291
  * yield* cached.pipe(Effect.andThen(Console.log))
292
292
  * })
293
293
  *
294
- * // Effect.runFork(program)
294
+ * Effect.runFork(program)
295
295
  * // Output:
296
296
  * // expensive task...
297
297
  * // result 1
@@ -336,7 +336,7 @@ export const cachedWithTTL: {
336
336
  * yield* cached.pipe(Effect.andThen(Console.log))
337
337
  * })
338
338
  *
339
- * // Effect.runFork(program)
339
+ * Effect.runFork(program)
340
340
  * // Output:
341
341
  * // expensive task...
342
342
  * // result 1
@@ -385,7 +385,7 @@ export const cachedInvalidateWithTTL: {
385
385
  * yield* cached.pipe(Effect.andThen(Console.log))
386
386
  * })
387
387
  *
388
- * // Effect.runFork(program)
388
+ * Effect.runFork(program)
389
389
  * // Output:
390
390
  * // non-cached version:
391
391
  * // expensive task...
@@ -422,7 +422,7 @@ export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
422
422
  * console.log(yield* memoized(10))
423
423
  * })
424
424
  *
425
- * // Effect.runFork(program)
425
+ * Effect.runFork(program)
426
426
  * // Example Output:
427
427
  * // non-memoized version:
428
428
  * // 2
@@ -453,7 +453,7 @@ export const cachedFunction: <A, B, E, R>(
453
453
  * yield* Effect.repeatN(task2, 2)
454
454
  * })
455
455
  *
456
- * // Effect.runFork(program)
456
+ * Effect.runFork(program)
457
457
  * // Output:
458
458
  * // task1
459
459
  * // task1
@@ -2159,6 +2159,40 @@ export const uninterruptibleMask: <A, E, R>(
2159
2159
  f: (restore: <AX, EX, RX>(effect: Effect<AX, EX, RX>) => Effect<AX, EX, RX>) => Effect<A, E, R>
2160
2160
  ) => Effect<A, E, R> = core.uninterruptibleMask
2161
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
+
2162
2196
  // -------------------------------------------------------------------------------------
2163
2197
  // mapping
2164
2198
  // -------------------------------------------------------------------------------------
@@ -4017,8 +4051,27 @@ export const tap: {
4017
4051
  } = core.tap
4018
4052
 
4019
4053
  /**
4020
- * Returns an effect that effectfully "peeks" at the failure or success of
4021
- * this effect.
4054
+ * Inspects both success and failure outcomes of an effect, performing different actions based on the result.
4055
+ *
4056
+ * @example
4057
+ * import { Effect, Random, Console } from "effect"
4058
+ *
4059
+ * // Simulate an effect that might fail
4060
+ * const task = Effect.filterOrFail(
4061
+ * Random.nextRange(-1, 1),
4062
+ * (n) => n >= 0,
4063
+ * () => "random number is negative"
4064
+ * )
4065
+ *
4066
+ * // Define an effect that logs both success and failure outcomes of the 'task'
4067
+ * const tapping = Effect.tapBoth(task, {
4068
+ * onFailure: (error) => Console.log(`failure: ${error}`),
4069
+ * onSuccess: (randomNumber) => Console.log(`random number: ${randomNumber}`)
4070
+ * })
4071
+ *
4072
+ * Effect.runFork(tapping)
4073
+ * // Example Output:
4074
+ * // failure: random number is negative
4022
4075
  *
4023
4076
  * @since 2.0.0
4024
4077
  * @category sequencing
@@ -4040,7 +4093,36 @@ export const tapBoth: {
4040
4093
  } = effect.tapBoth
4041
4094
 
4042
4095
  /**
4043
- * Returns an effect that effectually "peeks" at the defect of this effect.
4096
+ * Specifically inspects non-recoverable failures or defects in an effect (i.e., one or more `Die` causes).
4097
+ *
4098
+ * @example
4099
+ * import { Effect, Console } from "effect"
4100
+ *
4101
+ * // Create an effect that is designed to fail, simulating an occurrence of a network error
4102
+ * const task1: Effect.Effect<number, string> = Effect.fail("NetworkError")
4103
+ *
4104
+ * // this won't log anything because is not a defect
4105
+ * const tapping1 = Effect.tapDefect(task1, (cause) =>
4106
+ * Console.log(`defect: ${cause}`)
4107
+ * )
4108
+ *
4109
+ * Effect.runFork(tapping1)
4110
+ * // No Output
4111
+ *
4112
+ * // Simulate a severe failure in the system by causing a defect with a specific message.
4113
+ * const task2: Effect.Effect<number, string> = Effect.dieMessage(
4114
+ * "Something went wrong"
4115
+ * )
4116
+ *
4117
+ * // This will only log defects, not errors
4118
+ * const tapping2 = Effect.tapDefect(task2, (cause) =>
4119
+ * Console.log(`defect: ${cause}`)
4120
+ * )
4121
+ *
4122
+ * Effect.runFork(tapping2)
4123
+ * // Output:
4124
+ * // defect: RuntimeException: Something went wrong
4125
+ * // ... stack trace ...
4044
4126
  *
4045
4127
  * @since 2.0.0
4046
4128
  * @category sequencing
@@ -4056,7 +4138,23 @@ export const tapDefect: {
4056
4138
  } = effect.tapDefect
4057
4139
 
4058
4140
  /**
4059
- * Returns an effect that effectfully "peeks" at the failure of this effect.
4141
+ * Executes an effectful operation to inspect the failure of an effect without altering it.
4142
+ *
4143
+ * @example
4144
+ * import { Effect, Console } from "effect"
4145
+ *
4146
+ * // Create an effect that is designed to fail, simulating an occurrence of a network error
4147
+ * const task: Effect.Effect<number, string> = Effect.fail("NetworkError")
4148
+ *
4149
+ * // Log the error message if the task fails. This function only executes if there is an error,
4150
+ * // providing a method to handle or inspect errors without altering the outcome of the original effect.
4151
+ * const tapping = Effect.tapError(task, (error) =>
4152
+ * Console.log(`expected error: ${error}`)
4153
+ * )
4154
+ *
4155
+ * Effect.runFork(tapping)
4156
+ * // Output:
4157
+ * // expected error: NetworkError
4060
4158
  *
4061
4159
  * @since 2.0.0
4062
4160
  * @category sequencing
@@ -4069,7 +4167,33 @@ export const tapError: {
4069
4167
  } = effect.tapError
4070
4168
 
4071
4169
  /**
4072
- * Returns an effect that effectfully "peeks" at the specific tagged failure of this effect.
4170
+ * Specifically inspects a failure with a particular tag, allowing focused error handling.
4171
+ *
4172
+ * @example
4173
+ * import { Effect, Console } from "effect"
4174
+ *
4175
+ * class NetworkError {
4176
+ * readonly _tag = "NetworkError"
4177
+ * constructor(readonly statusCode: number) {}
4178
+ * }
4179
+ * class ValidationError {
4180
+ * readonly _tag = "ValidationError"
4181
+ * constructor(readonly field: string) {}
4182
+ * }
4183
+ *
4184
+ * // Create an effect that is designed to fail, simulating an occurrence of a network error
4185
+ * const task: Effect.Effect<number, NetworkError | ValidationError> =
4186
+ * Effect.fail(new NetworkError(504))
4187
+ *
4188
+ * // Apply an error handling function only to errors tagged as "NetworkError",
4189
+ * // and log the corresponding status code of the error.
4190
+ * const tapping = Effect.tapErrorTag(task, "NetworkError", (error) =>
4191
+ * Console.log(`expected error: ${error.statusCode}`)
4192
+ * )
4193
+ *
4194
+ * Effect.runFork(tapping)
4195
+ * // Output:
4196
+ * // expected error: 504
4073
4197
  *
4074
4198
  * @since 2.0.0
4075
4199
  * @category sequencing
@@ -4087,8 +4211,37 @@ export const tapErrorTag: {
4087
4211
  } = effect.tapErrorTag
4088
4212
 
4089
4213
  /**
4090
- * Returns an effect that effectually "peeks" at the cause of the failure of
4091
- * this effect.
4214
+ * Inspects the underlying cause of an effect's failure.
4215
+ *
4216
+ * @example
4217
+ * import { Effect, Console } from "effect"
4218
+ *
4219
+ * // Create an effect that is designed to fail, simulating an occurrence of a network error
4220
+ * const task1: Effect.Effect<number, string> = Effect.fail("NetworkError")
4221
+ *
4222
+ * // This will log the cause of any expected error or defect
4223
+ * const tapping1 = Effect.tapErrorCause(task1, (cause) =>
4224
+ * Console.log(`error cause: ${cause}`)
4225
+ * )
4226
+ *
4227
+ * Effect.runFork(tapping1)
4228
+ * // Output:
4229
+ * // error cause: Error: NetworkError
4230
+ *
4231
+ * // Simulate a severe failure in the system by causing a defect with a specific message.
4232
+ * const task2: Effect.Effect<number, string> = Effect.dieMessage(
4233
+ * "Something went wrong"
4234
+ * )
4235
+ *
4236
+ * // This will log the cause of any expected error or defect
4237
+ * const tapping2 = Effect.tapErrorCause(task2, (cause) =>
4238
+ * Console.log(`error cause: ${cause}`)
4239
+ * )
4240
+ *
4241
+ * Effect.runFork(tapping2)
4242
+ * // Output:
4243
+ * // error cause: RuntimeException: Something went wrong
4244
+ * // ... stack trace ...
4092
4245
  *
4093
4246
  * @since 2.0.0
4094
4247
  * @category sequencing
@@ -5152,6 +5305,55 @@ export const validateWith: {
5152
5305
  } = fiberRuntime.validateWith
5153
5306
 
5154
5307
  /**
5308
+ * The `Effect.zip` function allows you to combine two effects into a single
5309
+ * effect. This combined effect yields a tuple containing the results of both
5310
+ * input effects once they succeed.
5311
+ *
5312
+ * Note that `Effect.zip` processes effects sequentially: it first completes the
5313
+ * effect on the left and then the effect on the right.
5314
+ *
5315
+ * If you want to run the effects concurrently, you can use the `concurrent` option.
5316
+ *
5317
+ * @example
5318
+ * import { Effect } from "effect"
5319
+ *
5320
+ * const task1 = Effect.succeed(1).pipe(
5321
+ * Effect.delay("200 millis"),
5322
+ * Effect.tap(Effect.log("task1 done"))
5323
+ * )
5324
+ * const task2 = Effect.succeed("hello").pipe(
5325
+ * Effect.delay("100 millis"),
5326
+ * Effect.tap(Effect.log("task2 done"))
5327
+ * )
5328
+ *
5329
+ * const task3 = Effect.zip(task1, task2)
5330
+ *
5331
+ * Effect.runPromise(task3).then(console.log)
5332
+ * // Output:
5333
+ * // timestamp=... level=INFO fiber=#0 message="task1 done"
5334
+ * // timestamp=... level=INFO fiber=#0 message="task2 done"
5335
+ * // [ 1, 'hello' ]
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.zip(task1, task2, { concurrent: true })
5350
+ *
5351
+ * Effect.runPromise(task3).then(console.log)
5352
+ * // Output:
5353
+ * // timestamp=... level=INFO fiber=#0 message="task2 done"
5354
+ * // timestamp=... level=INFO fiber=#0 message="task1 done"
5355
+ * // [ 1, 'hello' ]
5356
+ *
5155
5357
  * @since 2.0.0
5156
5358
  * @category zipping
5157
5359
  */
@@ -5246,6 +5448,35 @@ export const zipRight: {
5246
5448
  } = fiberRuntime.zipRightOptions
5247
5449
 
5248
5450
  /**
5451
+ * The `Effect.zipWith` function operates similarly to {@link zip} by combining
5452
+ * two effects. However, instead of returning a tuple, it allows you to apply a
5453
+ * function to the results of the combined effects, transforming them into a
5454
+ * single value
5455
+ *
5456
+ * @example
5457
+ * import { Effect } from "effect"
5458
+ *
5459
+ * const task1 = Effect.succeed(1).pipe(
5460
+ * Effect.delay("200 millis"),
5461
+ * Effect.tap(Effect.log("task1 done"))
5462
+ * )
5463
+ * const task2 = Effect.succeed("hello").pipe(
5464
+ * Effect.delay("100 millis"),
5465
+ * Effect.tap(Effect.log("task2 done"))
5466
+ * )
5467
+ *
5468
+ * const task3 = Effect.zipWith(
5469
+ * task1,
5470
+ * task2,
5471
+ * (number, string) => number + string.length
5472
+ * )
5473
+ *
5474
+ * Effect.runPromise(task3).then(console.log)
5475
+ * // Output:
5476
+ * // timestamp=... level=INFO fiber=#3 message="task1 done"
5477
+ * // timestamp=... level=INFO fiber=#2 message="task2 done"
5478
+ * // 6
5479
+ *
5249
5480
  * @since 2.0.0
5250
5481
  * @category zipping
5251
5482
  */
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