effect 4.0.0-beta.34 → 4.0.0-beta.36

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 (84) hide show
  1. package/dist/Channel.d.ts +10 -1
  2. package/dist/Channel.d.ts.map +1 -1
  3. package/dist/Channel.js +14 -8
  4. package/dist/Channel.js.map +1 -1
  5. package/dist/Config.d.ts +4 -0
  6. package/dist/Config.d.ts.map +1 -1
  7. package/dist/Config.js.map +1 -1
  8. package/dist/Cron.d.ts +7 -0
  9. package/dist/Cron.d.ts.map +1 -1
  10. package/dist/Cron.js +109 -45
  11. package/dist/Cron.js.map +1 -1
  12. package/dist/Effect.d.ts +2 -2
  13. package/dist/Effect.d.ts.map +1 -1
  14. package/dist/Effect.js.map +1 -1
  15. package/dist/Equivalence.d.ts +52 -0
  16. package/dist/Equivalence.d.ts.map +1 -1
  17. package/dist/Equivalence.js +52 -0
  18. package/dist/Equivalence.js.map +1 -1
  19. package/dist/Layer.d.ts +5 -2
  20. package/dist/Layer.d.ts.map +1 -1
  21. package/dist/Layer.js +14 -1
  22. package/dist/Layer.js.map +1 -1
  23. package/dist/LayerMap.d.ts +5 -4
  24. package/dist/LayerMap.d.ts.map +1 -1
  25. package/dist/LayerMap.js.map +1 -1
  26. package/dist/PubSub.d.ts.map +1 -1
  27. package/dist/PubSub.js +9 -3
  28. package/dist/PubSub.js.map +1 -1
  29. package/dist/Schema.d.ts +50 -5
  30. package/dist/Schema.d.ts.map +1 -1
  31. package/dist/Schema.js +40 -0
  32. package/dist/Schema.js.map +1 -1
  33. package/dist/SchemaParser.d.ts +29 -0
  34. package/dist/SchemaParser.d.ts.map +1 -1
  35. package/dist/SchemaParser.js +38 -0
  36. package/dist/SchemaParser.js.map +1 -1
  37. package/dist/Stream.d.ts +10 -1
  38. package/dist/Stream.d.ts.map +1 -1
  39. package/dist/Stream.js +5 -1
  40. package/dist/Stream.js.map +1 -1
  41. package/dist/internal/dateTime.js +8 -1
  42. package/dist/internal/dateTime.js.map +1 -1
  43. package/dist/internal/effect.js +4 -3
  44. package/dist/internal/effect.js.map +1 -1
  45. package/dist/unstable/ai/AiError.d.ts +1 -0
  46. package/dist/unstable/ai/AiError.d.ts.map +1 -1
  47. package/dist/unstable/ai/AiError.js +7 -3
  48. package/dist/unstable/ai/AiError.js.map +1 -1
  49. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  50. package/dist/unstable/ai/LanguageModel.js +49 -21
  51. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  52. package/dist/unstable/cli/CliOutput.js +4 -3
  53. package/dist/unstable/cli/CliOutput.js.map +1 -1
  54. package/dist/unstable/cli/Prompt.js +146 -13
  55. package/dist/unstable/cli/Prompt.js.map +1 -1
  56. package/dist/unstable/httpapi/HttpApiEndpoint.js +2 -2
  57. package/dist/unstable/httpapi/HttpApiEndpoint.js.map +1 -1
  58. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts +15 -13
  59. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
  60. package/dist/unstable/httpapi/HttpApiMiddleware.js +5 -3
  61. package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
  62. package/dist/unstable/rpc/RpcSerialization.js +1 -1
  63. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  64. package/package.json +1 -1
  65. package/src/Channel.ts +23 -11
  66. package/src/Config.ts +5 -0
  67. package/src/Cron.ts +142 -45
  68. package/src/Effect.ts +3 -3
  69. package/src/Equivalence.ts +56 -0
  70. package/src/Layer.ts +27 -9
  71. package/src/LayerMap.ts +7 -5
  72. package/src/PubSub.ts +13 -5
  73. package/src/Schema.ts +62 -8
  74. package/src/SchemaParser.ts +53 -0
  75. package/src/Stream.ts +11 -1
  76. package/src/internal/dateTime.ts +8 -1
  77. package/src/internal/effect.ts +16 -13
  78. package/src/unstable/ai/AiError.ts +4 -2
  79. package/src/unstable/ai/LanguageModel.ts +59 -40
  80. package/src/unstable/cli/CliOutput.ts +5 -4
  81. package/src/unstable/cli/Prompt.ts +147 -14
  82. package/src/unstable/httpapi/HttpApiEndpoint.ts +2 -2
  83. package/src/unstable/httpapi/HttpApiMiddleware.ts +36 -20
  84. package/src/unstable/rpc/RpcSerialization.ts +1 -1
package/src/Effect.ts CHANGED
@@ -11999,11 +11999,11 @@ export const scopedWith: <A, E, R>(
11999
11999
  * @since 2.0.0
12000
12000
  * @category Resource Management & Finalization
12001
12001
  */
12002
- export const acquireRelease: <A, E, R>(
12002
+ export const acquireRelease: <A, E, R, R2>(
12003
12003
  acquire: Effect<A, E, R>,
12004
- release: (a: A, exit: Exit.Exit<unknown, unknown>) => Effect<unknown>,
12004
+ release: (a: A, exit: Exit.Exit<unknown, unknown>) => Effect<unknown, never, R2>,
12005
12005
  options?: { readonly interruptible?: boolean }
12006
- ) => Effect<A, E, Scope | R> = internal.acquireRelease
12006
+ ) => Effect<A, E, R | R2 | Scope> = internal.acquireRelease
12007
12007
 
12008
12008
  /**
12009
12009
  * This function is used to ensure that an `Effect` value that represents the
@@ -1060,3 +1060,59 @@ export function makeReducer<A>() {
1060
1060
  combineAll
1061
1061
  )
1062
1062
  }
1063
+
1064
+ /**
1065
+ * An `Equivalence` instance for `Date` objects.
1066
+ *
1067
+ * Dates are compared by their time value (milliseconds since the Unix epoch),
1068
+ * using {@link Date.prototype.getTime}.
1069
+ *
1070
+ * When to use this:
1071
+ * - When comparing `Date` objects by their exact point in time
1072
+ * - When you need value-based equality instead of reference equality
1073
+ * - When working with collections that contain `Date` values
1074
+ *
1075
+ * Behavior:
1076
+ * - Does not mutate inputs
1077
+ * - Two dates are equivalent if `self.getTime() === that.getTime()`
1078
+ * - Internally uses {@link Number} equivalence
1079
+ * - Different `Date` instances representing the same time are considered equivalent
1080
+ *
1081
+ * **Example**
1082
+ *
1083
+ * ```ts
1084
+ * import { Equivalence } from "effect"
1085
+ *
1086
+ * const d1 = new Date("2020-01-01T00:00:00.000Z")
1087
+ * const d2 = new Date("2020-01-01T00:00:00.000Z")
1088
+ * const d3 = new Date("2021-01-01T00:00:00.000Z")
1089
+ * const invalidDate1 = new Date("foo")
1090
+ * const invalidDate2 = new Date("bar")
1091
+ *
1092
+ * console.log(Equivalence.Date(d1, d2)) // true
1093
+ * console.log(Equivalence.Date(d1, d3)) // false
1094
+ * console.log(Equivalence.Date(invalidDate1, invalidDate2)) // true
1095
+ * console.log(Equivalence.Date(invalidDate1, d1)) // false
1096
+ * ```
1097
+ *
1098
+ * **Example** (Reference vs value equality)
1099
+ *
1100
+ * ```ts
1101
+ * import { Equivalence } from "effect"
1102
+ *
1103
+ * const d1 = new Date(0)
1104
+ * const d2 = new Date(0)
1105
+ *
1106
+ * console.log(d1 === d2) // false (different references)
1107
+ * console.log(Equivalence.Date(d1, d2)) // true (same time value)
1108
+ * ```
1109
+ *
1110
+ * See also: {@link Number}, {@link mapInput}, {@link strictEqual}
1111
+ *
1112
+ * @category instances
1113
+ * @since 4.0.0
1114
+ */
1115
+ export const Date: Equivalence<Date> = mapInput(
1116
+ Number,
1117
+ (d: Date) => d.getTime()
1118
+ )
package/src/Layer.ts CHANGED
@@ -19,6 +19,7 @@
19
19
  */
20
20
  import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts"
21
21
  import type * as Cause from "./Cause.ts"
22
+ import type * as Channel from "./Channel.ts"
22
23
  import * as Deferred from "./Deferred.ts"
23
24
  import type { Effect } from "./Effect.ts"
24
25
  import type * as Exit from "./Exit.ts"
@@ -33,6 +34,7 @@ import { hasProperty } from "./Predicate.ts"
33
34
  import { CurrentStackFrame } from "./References.ts"
34
35
  import * as Scope from "./Scope.ts"
35
36
  import * as ServiceMap from "./ServiceMap.ts"
37
+ import type * as Stream from "./Stream.ts"
36
38
  import * as Tracer from "./Tracer.ts"
37
39
  import type * as Types from "./Types.ts"
38
40
 
@@ -3148,19 +3150,21 @@ export const launch = <RIn, E, ROut>(self: Layer<ROut, E, RIn>): Effect<never, E
3148
3150
  */
3149
3151
  export type PartialEffectful<A extends object> = Types.Simplify<
3150
3152
  & {
3151
- [
3152
- K in keyof A as A[K] extends Effect<any, any, any> | ((...args: any) => Effect<any, any, any>) ? K
3153
- : never
3154
- ]?: A[K]
3153
+ [K in keyof A as A[K] extends AnyEffectOrStream ? K : never]?: A[K]
3155
3154
  }
3156
3155
  & {
3157
- [
3158
- K in keyof A as A[K] extends Effect<any, any, any> | ((...args: any) => Effect<any, any, any>) ? never
3159
- : K
3160
- ]: A[K]
3156
+ [K in keyof A as A[K] extends AnyEffectOrStream ? never : K]: A[K]
3161
3157
  }
3162
3158
  >
3163
3159
 
3160
+ type AnyEffectOrStream =
3161
+ | Effect<any, any, any>
3162
+ | Stream.Stream<any, any, any>
3163
+ | Channel.Channel<any, any, any, any, any, any, any>
3164
+ | ((...args: any) => Effect<any, any, any>)
3165
+ | ((...args: any) => Stream.Stream<any, any, any>)
3166
+ | ((...args: any) => Channel.Channel<any, any, any, any, any, any, any>)
3167
+
3164
3168
  /**
3165
3169
  * Creates a mock layer for testing purposes. You can provide a partial
3166
3170
  * implementation of the service, and any methods not provided will
@@ -3331,7 +3335,18 @@ const mockImpl = <I, S extends object>(service: ServiceMap.Key<I, S>, implementa
3331
3335
  )
3332
3336
 
3333
3337
  const makeUnimplemented = (error: globalThis.Error) => {
3334
- const dead = internalEffect.die(error)
3338
+ const dead = Object.assign(internalEffect.die(error), {
3339
+ [StreamTypeId]: StreamTypeId,
3340
+ channel: {
3341
+ [ChannelTypeId]: ChannelTypeId,
3342
+ transform: () => internalEffect.succeed(dead),
3343
+ pipe() {
3344
+ return pipeArguments(this, arguments)
3345
+ }
3346
+ },
3347
+ [ChannelTypeId]: ChannelTypeId,
3348
+ transform: () => internalEffect.succeed(dead)
3349
+ })
3335
3350
  function unimplemented() {
3336
3351
  return dead
3337
3352
  }
@@ -3341,6 +3356,9 @@ const makeUnimplemented = (error: globalThis.Error) => {
3341
3356
  return unimplemented
3342
3357
  }
3343
3358
 
3359
+ const StreamTypeId: Stream.TypeId = "~effect/Stream"
3360
+ const ChannelTypeId: Channel.TypeId = "~effect/Channel"
3361
+
3344
3362
  // -----------------------------------------------------------------------------
3345
3363
  // Type constraints
3346
3364
  // -----------------------------------------------------------------------------
package/src/LayerMap.ts CHANGED
@@ -12,6 +12,8 @@ import type { Mutable, NoExcessProperties } from "./Types.ts"
12
12
 
13
13
  const TypeId = "~effect/LayerMap"
14
14
 
15
+ type IdleTimeToLiveInput<K> = Duration.Input | ((key: K) => Duration.Input)
16
+
15
17
  /**
16
18
  * @since 3.14.0
17
19
  * @category Models
@@ -119,7 +121,7 @@ export const make: <
119
121
  >(
120
122
  lookup: (key: K) => L,
121
123
  options?: {
122
- readonly idleTimeToLive?: Duration.Input | undefined
124
+ readonly idleTimeToLive?: IdleTimeToLiveInput<K> | undefined
123
125
  readonly preloadKeys?: PreloadKeys
124
126
  } | undefined
125
127
  ) => Effect.Effect<
@@ -129,7 +131,7 @@ export const make: <
129
131
  > = Effect.fnUntraced(function*<I, K, EL, RL>(
130
132
  lookup: (key: K) => Layer.Layer<I, EL, RL>,
131
133
  options?: {
132
- readonly idleTimeToLive?: Duration.Input | undefined
134
+ readonly idleTimeToLive?: IdleTimeToLiveInput<K> | undefined
133
135
  } | undefined
134
136
  ) {
135
137
  const services = yield* Effect.services<never>()
@@ -198,7 +200,7 @@ export const fromRecord = <
198
200
  >(
199
201
  layers: Layers,
200
202
  options?: {
201
- readonly idleTimeToLive?: Duration.Input | undefined
203
+ readonly idleTimeToLive?: IdleTimeToLiveInput<keyof Layers> | undefined
202
204
  readonly preload?: Preload | undefined
203
205
  } | undefined
204
206
  ): Effect.Effect<
@@ -309,7 +311,7 @@ export const Service = <Self>() =>
309
311
  | NoExcessProperties<{
310
312
  readonly lookup: (key: any) => Layer.Layer<any, any, any>
311
313
  readonly dependencies?: ReadonlyArray<Layer.Layer<any, any, any>> | undefined
312
- readonly idleTimeToLive?: Duration.Input | undefined
314
+ readonly idleTimeToLive?: IdleTimeToLiveInput<any> | undefined
313
315
  readonly preloadKeys?:
314
316
  | Iterable<Options extends { readonly lookup: (key: infer K) => any } ? K : never>
315
317
  | undefined
@@ -317,7 +319,7 @@ export const Service = <Self>() =>
317
319
  | NoExcessProperties<{
318
320
  readonly layers: Record<string, Layer.Layer<any, any, any>>
319
321
  readonly dependencies?: ReadonlyArray<Layer.Layer<any, any, any>> | undefined
320
- readonly idleTimeToLive?: Duration.Input | undefined
322
+ readonly idleTimeToLive?: IdleTimeToLiveInput<any> | undefined
321
323
  readonly preload?: boolean | undefined
322
324
  }, Options>
323
325
  >(
package/src/PubSub.ts CHANGED
@@ -41,6 +41,7 @@ import { nextPow2 } from "./Number.ts"
41
41
  import * as Option from "./Option.ts"
42
42
  import { type Pipeable, pipeArguments } from "./Pipeable.ts"
43
43
  import * as Scope from "./Scope.ts"
44
+ import * as ServiceMap from "./ServiceMap.ts"
44
45
  import type { Covariant, Invariant } from "./Types.ts"
45
46
 
46
47
  const TypeId = "~effect/PubSub"
@@ -881,7 +882,7 @@ export const publish: {
881
882
  } = dual(2, <A>(self: PubSub<A>, value: A): Effect.Effect<boolean> =>
882
883
  Effect.suspend(() => {
883
884
  if (self.shutdownFlag.current) {
884
- return Effect.interrupt
885
+ return Effect.succeed(false)
885
886
  }
886
887
 
887
888
  if (self.pubsub.publish(value)) {
@@ -1110,7 +1111,7 @@ export const publishAll: {
1110
1111
  } = dual(2, <A>(self: PubSub<A>, elements: Iterable<A>): Effect.Effect<boolean> =>
1111
1112
  Effect.suspend(() => {
1112
1113
  if (self.shutdownFlag.current) {
1113
- return Effect.interrupt
1114
+ return Effect.succeed(false)
1114
1115
  }
1115
1116
  const surplus = self.pubsub.publishAll(elements)
1116
1117
  self.strategy.completeSubscribersUnsafe(self.pubsub, self.subscribers)
@@ -1173,9 +1174,16 @@ export const publishAll: {
1173
1174
  * @category subscription
1174
1175
  */
1175
1176
  export const subscribe = <A>(self: PubSub<A>): Effect.Effect<Subscription<A>, never, Scope.Scope> =>
1176
- Effect.acquireRelease(
1177
- Effect.sync(() => makeSubscriptionUnsafe(self.pubsub, self.subscribers, self.strategy)),
1178
- unsubscribe
1177
+ Effect.uninterruptible(
1178
+ Effect.servicesWith((services) => {
1179
+ const localScope = ServiceMap.get(services, Scope.Scope)
1180
+ const scope = Scope.forkUnsafe(self.scope)
1181
+ const subscription = makeSubscriptionUnsafe(self.pubsub, self.subscribers, self.strategy)
1182
+ return Scope.addFinalizer(scope, unsubscribe(subscription)).pipe(
1183
+ Effect.andThen(Scope.addFinalizerExit(localScope, (exit) => Scope.close(scope, exit))),
1184
+ Effect.as(subscription)
1185
+ )
1186
+ })
1179
1187
  )
1180
1188
 
1181
1189
  const unsubscribe = <A>(self: Subscription<A>): Effect.Effect<void> =>
package/src/Schema.ts CHANGED
@@ -1209,6 +1209,18 @@ export const decodeOption = Parser.decodeOption
1209
1209
  * @category Decoding
1210
1210
  * @since 4.0.0
1211
1211
  */
1212
+ export const decodeUnknownResult = Parser.decodeUnknownResult
1213
+
1214
+ /**
1215
+ * @category Decoding
1216
+ * @since 4.0.0
1217
+ */
1218
+ export const decodeResult = Parser.decodeResult
1219
+
1220
+ /**
1221
+ * @category Decoding
1222
+ * @since 4.0.0
1223
+ */
1212
1224
  export const decodeUnknownPromise = Parser.decodeUnknownPromise
1213
1225
 
1214
1226
  /**
@@ -1367,6 +1379,18 @@ export const encodeOption = Parser.encodeOption
1367
1379
  * @category Encoding
1368
1380
  * @since 4.0.0
1369
1381
  */
1382
+ export const encodeUnknownResult = Parser.encodeUnknownResult
1383
+
1384
+ /**
1385
+ * @category Encoding
1386
+ * @since 4.0.0
1387
+ */
1388
+ export const encodeResult = Parser.encodeResult
1389
+
1390
+ /**
1391
+ * @category Encoding
1392
+ * @since 4.0.0
1393
+ */
1370
1394
  export const encodeUnknownPromise = Parser.encodeUnknownPromise
1371
1395
 
1372
1396
  /**
@@ -2276,7 +2300,7 @@ export declare namespace Struct {
2276
2300
  /**
2277
2301
  * @since 4.0.0
2278
2302
  */
2279
- export type Type<F extends Fields> = Type_<F>
2303
+ export type Type<F extends Fields> = Simplify<Type_<F>>
2280
2304
 
2281
2305
  type Iso_<
2282
2306
  F extends Fields,
@@ -2291,7 +2315,7 @@ export declare namespace Struct {
2291
2315
  /**
2292
2316
  * @since 4.0.0
2293
2317
  */
2294
- export type Iso<F extends Fields> = Iso_<F>
2318
+ export type Iso<F extends Fields> = Simplify<Iso_<F>>
2295
2319
 
2296
2320
  type EncodedOptionalKeys<Fields extends Struct.Fields> = {
2297
2321
  [K in keyof Fields]: Fields[K] extends { readonly "~encoded.optionality": "optional" } ? K
@@ -2316,7 +2340,7 @@ export declare namespace Struct {
2316
2340
  /**
2317
2341
  * @since 4.0.0
2318
2342
  */
2319
- export type Encoded<F extends Fields> = Encoded_<F>
2343
+ export type Encoded<F extends Fields> = Simplify<Encoded_<F>>
2320
2344
 
2321
2345
  /**
2322
2346
  * @since 4.0.0
@@ -2343,7 +2367,7 @@ export declare namespace Struct {
2343
2367
  /**
2344
2368
  * @since 4.0.0
2345
2369
  */
2346
- export type MakeIn<F extends Fields> = MakeIn_<F>
2370
+ export type MakeIn<F extends Fields> = Simplify<MakeIn_<F>>
2347
2371
  }
2348
2372
 
2349
2373
  /**
@@ -2351,14 +2375,14 @@ export declare namespace Struct {
2351
2375
  */
2352
2376
  export interface Struct<Fields extends Struct.Fields> extends
2353
2377
  Bottom<
2354
- Simplify<Struct.Type<Fields>>,
2355
- Simplify<Struct.Encoded<Fields>>,
2378
+ Struct.Type<Fields>,
2379
+ Struct.Encoded<Fields>,
2356
2380
  Struct.DecodingServices<Fields>,
2357
2381
  Struct.EncodingServices<Fields>,
2358
2382
  AST.Objects,
2359
2383
  Struct<Fields>,
2360
- Simplify<Struct.MakeIn<Fields>>,
2361
- Simplify<Struct.Iso<Fields>>
2384
+ Struct.MakeIn<Fields>,
2385
+ Struct.Iso<Fields>
2362
2386
  >
2363
2387
  {
2364
2388
  readonly "~rebuild.out": this
@@ -3262,6 +3286,36 @@ export const NonEmptyArray = Struct_.lambda<NonEmptyArrayLambda>((schema) =>
3262
3286
  make(new AST.Arrays(false, [schema.ast], [schema.ast]), { schema })
3263
3287
  )
3264
3288
 
3289
+ /**
3290
+ * @category Arrays
3291
+ * @since 4.0.0
3292
+ */
3293
+ export interface ArrayEnsure<S extends Top> extends decodeTo<$Array<toType<S>>, Union<readonly [S, $Array<S>]>> {}
3294
+
3295
+ /**
3296
+ * Decodes a single value or an array of values into an array.
3297
+ *
3298
+ * Decoding:
3299
+ * - a single value is decoded as a one-element array
3300
+ * - an array is decoded as-is
3301
+ *
3302
+ * Encoding:
3303
+ * - a one-element array is encoded as a single value
3304
+ * - arrays with more than one element are encoded as arrays
3305
+ *
3306
+ * @category Arrays
3307
+ * @since 4.0.0
3308
+ */
3309
+ export function ArrayEnsure<S extends Top>(schema: S): ArrayEnsure<S> {
3310
+ return Union([schema, Array(schema)]).pipe(decodeTo(
3311
+ Array(toType(schema)),
3312
+ Transformation.transform({
3313
+ decode: Arr.ensure,
3314
+ encode: (array) => array.length === 1 ? array[0] : array
3315
+ })
3316
+ ))
3317
+ }
3318
+
3265
3319
  /**
3266
3320
  * Schema type for an array with unique elements. Produced by {@link UniqueArray}.
3267
3321
  *
@@ -204,6 +204,24 @@ export const decodeOption: <S extends Schema.Top & { readonly DecodingServices:
204
204
  schema: S
205
205
  ) => (input: S["Encoded"], options?: AST.ParseOptions) => Option.Option<S["Type"]> = decodeUnknownOption
206
206
 
207
+ /**
208
+ * @category Decoding
209
+ * @since 4.0.0
210
+ */
211
+ export function decodeUnknownResult<S extends Schema.Top & { readonly DecodingServices: never }>(
212
+ schema: S
213
+ ): (input: unknown, options?: AST.ParseOptions) => Result.Result<S["Type"], Issue.Issue> {
214
+ return asResult(decodeUnknownEffect(schema))
215
+ }
216
+
217
+ /**
218
+ * @category Decoding
219
+ * @since 4.0.0
220
+ */
221
+ export const decodeResult: <S extends Schema.Top & { readonly DecodingServices: never }>(
222
+ schema: S
223
+ ) => (input: S["Encoded"], options?: AST.ParseOptions) => Result.Result<S["Type"], Issue.Issue> = decodeUnknownResult
224
+
207
225
  /**
208
226
  * @category Decoding
209
227
  * @since 4.0.0
@@ -293,6 +311,24 @@ export const encodeOption: <S extends Schema.Top & { readonly EncodingServices:
293
311
  schema: S
294
312
  ) => (input: S["Type"], options?: AST.ParseOptions) => Option.Option<S["Encoded"]> = encodeUnknownOption
295
313
 
314
+ /**
315
+ * @category Encoding
316
+ * @since 4.0.0
317
+ */
318
+ export function encodeUnknownResult<S extends Schema.Top & { readonly EncodingServices: never }>(
319
+ schema: S
320
+ ): (input: unknown, options?: AST.ParseOptions) => Result.Result<S["Encoded"], Issue.Issue> {
321
+ return asResult(encodeUnknownEffect(schema))
322
+ }
323
+
324
+ /**
325
+ * @category Encoding
326
+ * @since 4.0.0
327
+ */
328
+ export const encodeResult: <S extends Schema.Top & { readonly EncodingServices: never }>(
329
+ schema: S
330
+ ) => (input: S["Type"], options?: AST.ParseOptions) => Result.Result<S["Encoded"], Issue.Issue> = encodeUnknownResult
331
+
296
332
  /**
297
333
  * @category Encoding
298
334
  * @since 4.0.0
@@ -343,6 +379,23 @@ export function asOption<T, E, R>(
343
379
  return (input: E, options?: AST.ParseOptions) => Exit.getSuccess(parserExit(input, options))
344
380
  }
345
381
 
382
+ function asResult<T, E, R>(
383
+ parser: (input: E, options?: AST.ParseOptions) => Effect.Effect<T, Issue.Issue, R>
384
+ ): (input: E, options?: AST.ParseOptions) => Result.Result<T, Issue.Issue> {
385
+ const parserExit = asExit(parser)
386
+ return (input: E, options?: AST.ParseOptions) => {
387
+ const exit = parserExit(input, options)
388
+ if (Exit.isSuccess(exit)) {
389
+ return Result.succeed(exit.value)
390
+ }
391
+ const error = Cause.findError(exit.cause)
392
+ if (Result.isFailure(error)) {
393
+ throw Cause.squash(error.failure)
394
+ }
395
+ return Result.fail(error.success)
396
+ }
397
+ }
398
+
346
399
  function asSync<T, E, R>(
347
400
  parser: (input: E, options?: AST.ParseOptions) => Effect.Effect<T, Issue.Issue, R>
348
401
  ): (input: E, options?: AST.ParseOptions) => T {
package/src/Stream.ts CHANGED
@@ -57,7 +57,17 @@ import type {
57
57
  } from "./Types.ts"
58
58
  import type * as Unify from "./Unify.ts"
59
59
 
60
- const TypeId = "~effect/Stream"
60
+ /**
61
+ * @since 4.0.0
62
+ * @category Type Identifiers
63
+ */
64
+ export type TypeId = "~effect/Stream"
65
+
66
+ /**
67
+ * @since 4.0.0
68
+ * @category Type Identifiers
69
+ */
70
+ export const TypeId: TypeId = "~effect/Stream"
61
71
 
62
72
  /**
63
73
  * A `Stream<A, E, R>` describes a program that can emit many `A` values, fail
@@ -232,7 +232,14 @@ export const makeUnsafe = <A extends DateTime.DateTime.Input>(input: A): DateTim
232
232
  return fromDateUnsafe(new Date(input)) as DateTime.DateTime.PreserveZone<A>
233
233
  }
234
234
 
235
- const hasZone = (input: string): boolean => /Z|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input)
235
+ /**
236
+ * Detects whether a date string already contains timezone info.
237
+ * Without a zone, `new Date("2024-01-01T12:00:00")` is parsed as local time,
238
+ * so `makeUnsafe` appends "Z" to force UTC interpretation.
239
+ * This check prevents appending "Z" to strings that already have a zone
240
+ * (e.g. "2024-01-01T12:00:00Z", "...+05:30", "...GMT"), which would produce invalid dates.
241
+ */
242
+ const hasZone = (input: string): boolean => /Z|GMT|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input)
236
243
 
237
244
  const minEpochMillis = -8640000000000000 + (12 * 60 * 60 * 1000)
238
245
  const maxEpochMillis = 8640000000000000 - (14 * 60 * 60 * 1000)
@@ -3792,19 +3792,21 @@ export const scopedWith = <A, E, R>(
3792
3792
  })
3793
3793
 
3794
3794
  /** @internal */
3795
- export const acquireRelease = <A, E, R>(
3795
+ export const acquireRelease = <A, E, R, R2>(
3796
3796
  acquire: Effect.Effect<A, E, R>,
3797
- release: (a: A, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown>,
3797
+ release: (a: A, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown, never, R2>,
3798
3798
  options?: { readonly interruptible?: boolean }
3799
- ): Effect.Effect<A, E, R | Scope.Scope> =>
3800
- uninterruptibleMask((restore) =>
3801
- flatMap(
3802
- scope,
3803
- (scope) =>
3804
- tap(
3805
- options?.interruptible ? restore(acquire) : acquire,
3806
- (a) => scopeAddFinalizerExit(scope, (exit) => release(a, exit))
3807
- )
3799
+ ): Effect.Effect<A, E, R | R2 | Scope.Scope> =>
3800
+ servicesWith((services: ServiceMap.ServiceMap<R2>) =>
3801
+ uninterruptibleMask((restore) =>
3802
+ flatMap(
3803
+ scope,
3804
+ (scope) =>
3805
+ tap(
3806
+ options?.interruptible ? restore(acquire) : acquire,
3807
+ (a) => scopeAddFinalizerExit(scope, (exit) => provideServices(release(a, exit), services))
3808
+ )
3809
+ )
3808
3810
  )
3809
3811
  )
3810
3812
 
@@ -4061,7 +4063,8 @@ export const cachedInvalidateWithTTL: {
4061
4063
  const wait = flatMap(latch.await, () => exit!)
4062
4064
  return [
4063
4065
  withFiber((fiber) => {
4064
- const now = isFinite ? fiber.getRef(ClockRef).currentTimeMillisUnsafe() : 0
4066
+ const clock = fiber.getRef(ClockRef)
4067
+ const now = isFinite ? clock.currentTimeMillisUnsafe() : 0
4065
4068
  if (running || now < expiresAt) return exit ?? wait
4066
4069
  running = true
4067
4070
  latch.closeUnsafe()
@@ -4069,7 +4072,7 @@ export const cachedInvalidateWithTTL: {
4069
4072
  return onExit(self, (exit_) =>
4070
4073
  sync(() => {
4071
4074
  running = false
4072
- expiresAt = now + ttlMillis
4075
+ expiresAt = clock.currentTimeMillisUnsafe() + ttlMillis
4073
4076
  exit = exit_
4074
4077
  latch.openUnsafe()
4075
4078
  }))
@@ -1532,11 +1532,13 @@ export const reasonFromHttpStatus = (params: {
1532
1532
  readonly body?: unknown
1533
1533
  readonly http?: typeof HttpContext.Type
1534
1534
  readonly metadata?: typeof ProviderMetadata.Type
1535
+ readonly description?: string | undefined
1535
1536
  }): AiErrorReason => {
1536
- const { status, http, metadata } = params
1537
+ const { status, http, metadata, description } = params
1537
1538
  const common = {
1538
1539
  http,
1539
- ...(Predicate.isNotUndefined(metadata) ? { metadata } : {})
1540
+ ...(metadata ? { metadata } : undefined),
1541
+ ...(description ? { description } : undefined)
1540
1542
  }
1541
1543
  switch (status) {
1542
1544
  case 400: