effect 4.0.0-beta.33 → 4.0.0-beta.35

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 (132) hide show
  1. package/dist/Channel.d.ts +10 -1
  2. package/dist/Channel.d.ts.map +1 -1
  3. package/dist/Channel.js +5 -1
  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/Effect.d.ts +5 -3
  9. package/dist/Effect.d.ts.map +1 -1
  10. package/dist/Effect.js +4 -4
  11. package/dist/Effect.js.map +1 -1
  12. package/dist/FileSystem.d.ts.map +1 -1
  13. package/dist/FileSystem.js +2 -1
  14. package/dist/FileSystem.js.map +1 -1
  15. package/dist/Graph.d.ts.map +1 -1
  16. package/dist/Graph.js +2 -1
  17. package/dist/Graph.js.map +1 -1
  18. package/dist/Layer.d.ts +5 -2
  19. package/dist/Layer.d.ts.map +1 -1
  20. package/dist/Layer.js +14 -1
  21. package/dist/Layer.js.map +1 -1
  22. package/dist/MutableHashMap.d.ts +7 -0
  23. package/dist/MutableHashMap.d.ts.map +1 -1
  24. package/dist/MutableHashMap.js +8 -0
  25. package/dist/MutableHashMap.js.map +1 -1
  26. package/dist/MutableHashSet.d.ts +7 -0
  27. package/dist/MutableHashSet.d.ts.map +1 -1
  28. package/dist/MutableHashSet.js +8 -0
  29. package/dist/MutableHashSet.js.map +1 -1
  30. package/dist/Resource.d.ts.map +1 -1
  31. package/dist/Resource.js +2 -1
  32. package/dist/Resource.js.map +1 -1
  33. package/dist/Schema.d.ts +21 -0
  34. package/dist/Schema.d.ts.map +1 -1
  35. package/dist/Schema.js +20 -0
  36. package/dist/Schema.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/SubscriptionRef.d.ts.map +1 -1
  42. package/dist/SubscriptionRef.js +2 -1
  43. package/dist/SubscriptionRef.js.map +1 -1
  44. package/dist/TxDeferred.d.ts.map +1 -1
  45. package/dist/TxDeferred.js +2 -1
  46. package/dist/TxDeferred.js.map +1 -1
  47. package/dist/TxHashMap.d.ts.map +1 -1
  48. package/dist/TxHashMap.js +2 -1
  49. package/dist/TxHashMap.js.map +1 -1
  50. package/dist/TxHashSet.d.ts +1 -1
  51. package/dist/TxHashSet.d.ts.map +1 -1
  52. package/dist/TxHashSet.js +2 -1
  53. package/dist/TxHashSet.js.map +1 -1
  54. package/dist/TxPriorityQueue.d.ts +1 -1
  55. package/dist/TxPriorityQueue.d.ts.map +1 -1
  56. package/dist/TxPriorityQueue.js +2 -1
  57. package/dist/TxPriorityQueue.js.map +1 -1
  58. package/dist/TxSemaphore.d.ts.map +1 -1
  59. package/dist/TxSemaphore.js +2 -1
  60. package/dist/TxSemaphore.js.map +1 -1
  61. package/dist/internal/dateTime.js +8 -1
  62. package/dist/internal/dateTime.js.map +1 -1
  63. package/dist/internal/effect.js +1 -1
  64. package/dist/internal/effect.js.map +1 -1
  65. package/dist/unstable/ai/AiError.d.ts +2 -3
  66. package/dist/unstable/ai/AiError.d.ts.map +1 -1
  67. package/dist/unstable/ai/AiError.js +8 -6
  68. package/dist/unstable/ai/AiError.js.map +1 -1
  69. package/dist/unstable/ai/EmbeddingModel.d.ts +130 -0
  70. package/dist/unstable/ai/EmbeddingModel.d.ts.map +1 -0
  71. package/dist/unstable/ai/EmbeddingModel.js +127 -0
  72. package/dist/unstable/ai/EmbeddingModel.js.map +1 -0
  73. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  74. package/dist/unstable/ai/LanguageModel.js +34 -8
  75. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  76. package/dist/unstable/ai/ResponseIdTracker.js +3 -3
  77. package/dist/unstable/ai/ResponseIdTracker.js.map +1 -1
  78. package/dist/unstable/ai/Tool.d.ts +1 -1
  79. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  80. package/dist/unstable/ai/Tool.js +1 -1
  81. package/dist/unstable/ai/Tool.js.map +1 -1
  82. package/dist/unstable/ai/index.d.ts +17 -0
  83. package/dist/unstable/ai/index.d.ts.map +1 -1
  84. package/dist/unstable/ai/index.js +17 -0
  85. package/dist/unstable/ai/index.js.map +1 -1
  86. package/dist/unstable/cli/CliOutput.js +4 -3
  87. package/dist/unstable/cli/CliOutput.js.map +1 -1
  88. package/dist/unstable/http/Url.d.ts +604 -0
  89. package/dist/unstable/http/Url.d.ts.map +1 -0
  90. package/dist/unstable/http/Url.js +256 -0
  91. package/dist/unstable/http/Url.js.map +1 -0
  92. package/dist/unstable/http/index.d.ts +4 -0
  93. package/dist/unstable/http/index.d.ts.map +1 -1
  94. package/dist/unstable/http/index.js +4 -0
  95. package/dist/unstable/http/index.js.map +1 -1
  96. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts +30 -1
  97. package/dist/unstable/httpapi/HttpApiMiddleware.d.ts.map +1 -1
  98. package/dist/unstable/httpapi/HttpApiMiddleware.js +27 -0
  99. package/dist/unstable/httpapi/HttpApiMiddleware.js.map +1 -1
  100. package/dist/unstable/rpc/RpcSerialization.js +1 -1
  101. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  102. package/package.json +1 -1
  103. package/src/Channel.ts +11 -1
  104. package/src/Config.ts +5 -0
  105. package/src/Effect.ts +8 -7
  106. package/src/FileSystem.ts +2 -1
  107. package/src/Graph.ts +2 -1
  108. package/src/Layer.ts +27 -9
  109. package/src/MutableHashMap.ts +9 -0
  110. package/src/MutableHashSet.ts +9 -0
  111. package/src/Resource.ts +2 -1
  112. package/src/Schema.ts +30 -0
  113. package/src/Stream.ts +11 -1
  114. package/src/SubscriptionRef.ts +2 -1
  115. package/src/TxDeferred.ts +2 -2
  116. package/src/TxHashMap.ts +2 -1
  117. package/src/TxHashSet.ts +2 -2
  118. package/src/TxPriorityQueue.ts +2 -3
  119. package/src/TxSemaphore.ts +2 -1
  120. package/src/internal/dateTime.ts +8 -1
  121. package/src/internal/effect.ts +15 -6
  122. package/src/unstable/ai/AiError.ts +5 -5
  123. package/src/unstable/ai/EmbeddingModel.ts +209 -0
  124. package/src/unstable/ai/LanguageModel.ts +44 -8
  125. package/src/unstable/ai/ResponseIdTracker.ts +3 -3
  126. package/src/unstable/ai/Tool.ts +2 -2
  127. package/src/unstable/ai/index.ts +18 -0
  128. package/src/unstable/cli/CliOutput.ts +5 -4
  129. package/src/unstable/http/Url.ts +650 -0
  130. package/src/unstable/http/index.ts +5 -0
  131. package/src/unstable/httpapi/HttpApiMiddleware.ts +46 -1
  132. package/src/unstable/rpc/RpcSerialization.ts +1 -1
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
  // -----------------------------------------------------------------------------
@@ -33,6 +33,7 @@ import { type Inspectable, NodeInspectSymbol, toJson } from "./Inspectable.ts"
33
33
  import * as Option from "./Option.ts"
34
34
  import type { Pipeable } from "./Pipeable.ts"
35
35
  import { pipeArguments } from "./Pipeable.ts"
36
+ import { hasProperty } from "./Predicate.ts"
36
37
 
37
38
  const TypeId = "~effect/collections/MutableHashMap"
38
39
 
@@ -71,6 +72,14 @@ export interface MutableHashMap<out K, out V> extends Iterable<[K, V]>, Pipeable
71
72
  readonly buckets: Map<number, NonEmptyArray<K>>
72
73
  }
73
74
 
75
+ /**
76
+ * Checks if the specified value is a `MutableHashMap`, `false` otherwise.
77
+ *
78
+ * @category refinements
79
+ * @since 4.0.0
80
+ */
81
+ export const isMutableHashMap = <K, V>(value: unknown): value is MutableHashMap<K, V> => hasProperty(value, TypeId)
82
+
74
83
  const MutableHashMapProto: Omit<MutableHashMap<unknown, unknown>, "backing" | "buckets" | "bucketsSize"> = {
75
84
  [TypeId]: TypeId,
76
85
  [Symbol.iterator](this: MutableHashMap<unknown, unknown>): Iterator<[unknown, unknown]> {
@@ -30,6 +30,7 @@ import { type Inspectable, NodeInspectSymbol, toJson } from "./Inspectable.ts"
30
30
  import * as MutableHashMap from "./MutableHashMap.ts"
31
31
  import type { Pipeable } from "./Pipeable.ts"
32
32
  import { pipeArguments } from "./Pipeable.ts"
33
+ import { hasProperty } from "./Predicate.ts"
33
34
 
34
35
  const TypeId = "~effect/collections/MutableHashSet"
35
36
 
@@ -68,6 +69,14 @@ export interface MutableHashSet<out V> extends Iterable<V>, Pipeable, Inspectabl
68
69
  readonly keyMap: MutableHashMap.MutableHashMap<V, boolean>
69
70
  }
70
71
 
72
+ /**
73
+ * Checks if the specified value is a `MutableHashSet`, `false` otherwise.
74
+ *
75
+ * @category refinements
76
+ * @since 4.0.0
77
+ */
78
+ export const isMutableHashSet = <V>(value: unknown): value is MutableHashSet<V> => hasProperty(value, TypeId)
79
+
71
80
  const MutableHashSetProto: Omit<MutableHashSet<unknown>, "keyMap"> = {
72
81
  [TypeId]: TypeId,
73
82
  [Symbol.iterator](this: MutableHashSet<unknown>): Iterator<unknown> {
package/src/Resource.ts CHANGED
@@ -6,6 +6,7 @@ import * as Exit from "./Exit.ts"
6
6
  import { identity } from "./Function.ts"
7
7
  import { PipeInspectableProto } from "./internal/core.ts"
8
8
  import type { Pipeable } from "./Pipeable.ts"
9
+ import { hasProperty } from "./Predicate.ts"
9
10
  import type * as Schedule from "./Schedule.ts"
10
11
  import type * as Scope from "./Scope.ts"
11
12
  import * as ScopedRef from "./ScopedRef.ts"
@@ -32,7 +33,7 @@ export interface Resource<in out A, in out E = never> extends Pipeable {
32
33
  */
33
34
  export const isResource: (u: unknown) => u is Resource<unknown, unknown> = (
34
35
  u: unknown
35
- ): u is Resource<unknown, unknown> => typeof u === "object" && u !== null && TypeId in u
36
+ ): u is Resource<unknown, unknown> => hasProperty(u, TypeId)
36
37
 
37
38
  const Proto = {
38
39
  ...PipeInspectableProto,
package/src/Schema.ts CHANGED
@@ -3262,6 +3262,36 @@ export const NonEmptyArray = Struct_.lambda<NonEmptyArrayLambda>((schema) =>
3262
3262
  make(new AST.Arrays(false, [schema.ast], [schema.ast]), { schema })
3263
3263
  )
3264
3264
 
3265
+ /**
3266
+ * @category Arrays
3267
+ * @since 4.0.0
3268
+ */
3269
+ export interface ArrayEnsure<S extends Top> extends decodeTo<$Array<toType<S>>, Union<readonly [S, $Array<S>]>> {}
3270
+
3271
+ /**
3272
+ * Decodes a single value or an array of values into an array.
3273
+ *
3274
+ * Decoding:
3275
+ * - a single value is decoded as a one-element array
3276
+ * - an array is decoded as-is
3277
+ *
3278
+ * Encoding:
3279
+ * - a one-element array is encoded as a single value
3280
+ * - arrays with more than one element are encoded as arrays
3281
+ *
3282
+ * @category Arrays
3283
+ * @since 4.0.0
3284
+ */
3285
+ export function ArrayEnsure<S extends Top>(schema: S): ArrayEnsure<S> {
3286
+ return Union([schema, Array(schema)]).pipe(decodeTo(
3287
+ Array(toType(schema)),
3288
+ Transformation.transform({
3289
+ decode: Arr.ensure,
3290
+ encode: (array) => array.length === 1 ? array[0] : array
3291
+ })
3292
+ ))
3293
+ }
3294
+
3265
3295
  /**
3266
3296
  * Schema type for an array with unique elements. Produced by {@link UniqueArray}.
3267
3297
  *
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
@@ -6,6 +6,7 @@ import { dual, identity } from "./Function.ts"
6
6
  import { PipeInspectableProto } from "./internal/core.ts"
7
7
  import * as Option from "./Option.ts"
8
8
  import type { Pipeable } from "./Pipeable.ts"
9
+ import { hasProperty } from "./Predicate.ts"
9
10
  import * as PubSub from "./PubSub.ts"
10
11
  import * as Semaphore from "./Semaphore.ts"
11
12
  import * as Stream from "./Stream.ts"
@@ -29,7 +30,7 @@ export interface SubscriptionRef<in out A> extends SubscriptionRef.Variance<A>,
29
30
  */
30
31
  export const isSubscriptionRef: (u: unknown) => u is SubscriptionRef<unknown> = (
31
32
  u: unknown
32
- ): u is SubscriptionRef<unknown> => typeof u === "object" && u != null && TypeId in u
33
+ ): u is SubscriptionRef<unknown> => hasProperty(u, TypeId)
33
34
 
34
35
  /**
35
36
  * The `SynchronizedRef` namespace containing type definitions and utilities.
package/src/TxDeferred.ts CHANGED
@@ -13,6 +13,7 @@ import type { Option } from "./Option.ts"
13
13
  import * as O from "./Option.ts"
14
14
  import type { Pipeable } from "./Pipeable.ts"
15
15
  import { pipeArguments } from "./Pipeable.ts"
16
+ import { hasProperty } from "./Predicate.ts"
16
17
  import type { Result } from "./Result.ts"
17
18
  import * as Res from "./Result.ts"
18
19
  import * as TxRef from "./TxRef.ts"
@@ -390,5 +391,4 @@ export const fail: {
390
391
  * @since 4.0.0
391
392
  * @category guards
392
393
  */
393
- export const isTxDeferred = (u: unknown): u is TxDeferred<unknown, unknown> =>
394
- typeof u === "object" && u !== null && TypeId in u
394
+ export const isTxDeferred = (u: unknown): u is TxDeferred<unknown, unknown> => hasProperty(u, TypeId)
package/src/TxHashMap.ts CHANGED
@@ -11,6 +11,7 @@ import { NodeInspectSymbol, toJson } from "./Inspectable.ts"
11
11
  import * as Option from "./Option.ts"
12
12
  import type { Pipeable } from "./Pipeable.ts"
13
13
  import { pipeArguments } from "./Pipeable.ts"
14
+ import { hasProperty } from "./Predicate.ts"
14
15
  import type { Result } from "./Result.ts"
15
16
  import * as TxRef from "./TxRef.ts"
16
17
 
@@ -1882,7 +1883,7 @@ export const setMany: {
1882
1883
  * @category guards
1883
1884
  */
1884
1885
  export const isTxHashMap = <K, V>(value: unknown): value is TxHashMap<K, V> => {
1885
- return typeof value === "object" && value !== null && TypeId in value
1886
+ return hasProperty(value, TypeId)
1886
1887
  }
1887
1888
 
1888
1889
  /**
package/src/TxHashSet.ts CHANGED
@@ -10,7 +10,7 @@ import type { Inspectable } from "./Inspectable.ts"
10
10
  import { NodeInspectSymbol, toJson } from "./Inspectable.ts"
11
11
  import type { Pipeable } from "./Pipeable.ts"
12
12
  import { pipeArguments } from "./Pipeable.ts"
13
- import type { Predicate, Refinement } from "./Predicate.ts"
13
+ import { hasProperty, type Predicate, type Refinement } from "./Predicate.ts"
14
14
  import * as TxRef from "./TxRef.ts"
15
15
  import type { NoInfer } from "./Types.ts"
16
16
 
@@ -286,7 +286,7 @@ export const fromHashSet = <V>(hashSet: HashSet.HashSet<V>): Effect.Effect<TxHas
286
286
  * @since 2.0.0
287
287
  * @category guards
288
288
  */
289
- export const isTxHashSet = (u: unknown): u is TxHashSet<unknown> => typeof u === "object" && u !== null && TypeId in u
289
+ export const isTxHashSet = (u: unknown): u is TxHashSet<unknown> => hasProperty(u, TypeId)
290
290
 
291
291
  /**
292
292
  * Adds a value to the TxHashSet. If the value already exists, the operation has no effect.
@@ -16,7 +16,7 @@ import * as O from "./Option.ts"
16
16
  import type { Order } from "./Order.ts"
17
17
  import type { Pipeable } from "./Pipeable.ts"
18
18
  import { pipeArguments } from "./Pipeable.ts"
19
- import type { Predicate } from "./Predicate.ts"
19
+ import { hasProperty, type Predicate } from "./Predicate.ts"
20
20
  import * as TxRef from "./TxRef.ts"
21
21
 
22
22
  const TypeId = "~effect/transactions/TxPriorityQueue"
@@ -763,5 +763,4 @@ export const toArray = <A>(self: TxPriorityQueue<A>): Effect.Effect<Array<A>, ne
763
763
  * @since 4.0.0
764
764
  * @category guards
765
765
  */
766
- export const isTxPriorityQueue = (u: unknown): u is TxPriorityQueue<unknown> =>
767
- typeof u === "object" && u !== null && TypeId in u
766
+ export const isTxPriorityQueue = (u: unknown): u is TxPriorityQueue<unknown> => hasProperty(u, TypeId)
@@ -7,6 +7,7 @@ import type { Inspectable } from "./Inspectable.ts"
7
7
  import { NodeInspectSymbol, toJson } from "./Inspectable.ts"
8
8
  import type { Pipeable } from "./Pipeable.ts"
9
9
  import { pipeArguments } from "./Pipeable.ts"
10
+ import { hasProperty } from "./Predicate.ts"
10
11
  import type * as Scope from "./Scope.ts"
11
12
  import * as TxRef from "./TxRef.ts"
12
13
 
@@ -738,4 +739,4 @@ export const withPermitScoped = (self: TxSemaphore): Effect.Effect<void, never,
738
739
  * @since 4.0.0
739
740
  * @category guards
740
741
  */
741
- export const isTxSemaphore = (u: unknown): u is TxSemaphore => typeof u === "object" && u !== null && TypeId in u
742
+ export const isTxSemaphore = (u: unknown): u is TxSemaphore => hasProperty(u, TypeId)
@@ -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,13 +3792,22 @@ 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>
3798
- ): Effect.Effect<A, E, R | Scope.Scope> =>
3799
- uninterruptible(
3800
- flatMap(scope, (scope) =>
3801
- tap(acquire, (a) => scopeAddFinalizerExit(scope, (exit) => internalCall(() => release(a, exit)))))
3797
+ release: (a: A, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown, never, R2>,
3798
+ options?: { readonly interruptible?: boolean }
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
+ )
3810
+ )
3802
3811
  )
3803
3812
 
3804
3813
  /** @internal */
@@ -1396,10 +1396,8 @@ export class AiError extends Schema.ErrorClass<AiError>(
1396
1396
  method: Schema.String,
1397
1397
  reason: AiErrorReason
1398
1398
  }) {
1399
- /**
1400
- * @since 1.0.0
1401
- */
1402
1399
  readonly [TypeId] = TypeId
1400
+ override readonly cause = this.reason
1403
1401
 
1404
1402
  /**
1405
1403
  * Delegates to the underlying reason's `isRetryable` getter.
@@ -1534,11 +1532,13 @@ export const reasonFromHttpStatus = (params: {
1534
1532
  readonly body?: unknown
1535
1533
  readonly http?: typeof HttpContext.Type
1536
1534
  readonly metadata?: typeof ProviderMetadata.Type
1535
+ readonly description?: string | undefined
1537
1536
  }): AiErrorReason => {
1538
- const { status, http, metadata } = params
1537
+ const { status, http, metadata, description } = params
1539
1538
  const common = {
1540
1539
  http,
1541
- ...(Predicate.isNotUndefined(metadata) ? { metadata } : {})
1540
+ ...(metadata ? { metadata } : undefined),
1541
+ ...(description ? { description } : undefined)
1542
1542
  }
1543
1543
  switch (status) {
1544
1544
  case 400:
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The `EmbeddingModel` module provides provider-agnostic text embedding capabilities.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { Effect } from "effect"
7
+ * import { EmbeddingModel } from "effect/unstable/ai"
8
+ *
9
+ * const program = Effect.gen(function*() {
10
+ * const model = yield* EmbeddingModel.EmbeddingModel
11
+ * return yield* model.embed("hello world")
12
+ * })
13
+ * ```
14
+ *
15
+ * @since 4.0.0
16
+ */
17
+ import * as Effect from "../../Effect.ts"
18
+ import * as Exit from "../../Exit.ts"
19
+ import * as Request from "../../Request.ts"
20
+ import * as RequestResolver from "../../RequestResolver.ts"
21
+ import * as Schema from "../../Schema.ts"
22
+ import * as ServiceMap from "../../ServiceMap.ts"
23
+ import * as AiError from "./AiError.ts"
24
+
25
+ /**
26
+ * Service tag for embedding model operations.
27
+ *
28
+ * @since 4.0.0
29
+ * @category services
30
+ */
31
+ export class EmbeddingModel extends ServiceMap.Service<EmbeddingModel, Service>()(
32
+ "effect/unstable/ai/EmbeddingModel"
33
+ ) {}
34
+
35
+ /**
36
+ * Service tag that provides the current embedding dimensions.
37
+ *
38
+ * @since 4.0.0
39
+ * @category services
40
+ */
41
+ export class Dimensions extends ServiceMap.Service<Dimensions, number>()(
42
+ "effect/unstable/ai/EmbeddingModel/Dimensions"
43
+ ) {}
44
+
45
+ /**
46
+ * Token usage metadata for embedding operations.
47
+ *
48
+ * @since 4.0.0
49
+ * @category models
50
+ */
51
+ export class EmbeddingUsage extends Schema.Class<EmbeddingUsage>(
52
+ "effect/ai/EmbeddingModel/EmbeddingUsage"
53
+ )({
54
+ inputTokens: Schema.UndefinedOr(Schema.Finite)
55
+ }) {}
56
+
57
+ /**
58
+ * Response for a single embedding request.
59
+ *
60
+ * @since 4.0.0
61
+ * @category models
62
+ */
63
+ export class EmbedResponse extends Schema.Class<EmbedResponse>(
64
+ "effect/ai/EmbeddingModel/EmbedResponse"
65
+ )({
66
+ vector: Schema.Array(Schema.Finite)
67
+ }) {}
68
+
69
+ /**
70
+ * Response for multiple embeddings.
71
+ *
72
+ * @since 4.0.0
73
+ * @category models
74
+ */
75
+ export class EmbedManyResponse extends Schema.Class<EmbedManyResponse>(
76
+ "effect/ai/EmbeddingModel/EmbedManyResponse"
77
+ )({
78
+ embeddings: Schema.Array(EmbedResponse),
79
+ usage: EmbeddingUsage
80
+ }) {}
81
+
82
+ /**
83
+ * Provider input options for embedding requests.
84
+ *
85
+ * @since 4.0.0
86
+ * @category models
87
+ */
88
+ export interface ProviderOptions {
89
+ readonly inputs: ReadonlyArray<string>
90
+ }
91
+
92
+ /**
93
+ * Provider response for batch embedding requests.
94
+ *
95
+ * @since 4.0.0
96
+ * @category models
97
+ */
98
+ export interface ProviderResponse {
99
+ readonly results: Array<Array<number>>
100
+ readonly usage: {
101
+ readonly inputTokens: number | undefined
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Tagged request used by request resolvers for embedding operations.
107
+ *
108
+ * @since 4.0.0
109
+ * @category constructors
110
+ */
111
+ export class EmbeddingRequest extends Request.TaggedClass("EmbeddingRequest")<
112
+ { readonly input: string },
113
+ EmbedResponse,
114
+ AiError.AiError
115
+ > {}
116
+
117
+ /**
118
+ * Service interface for embedding operations.
119
+ *
120
+ * @since 4.0.0
121
+ * @category models
122
+ */
123
+ export interface Service {
124
+ readonly resolver: RequestResolver.RequestResolver<EmbeddingRequest>
125
+ readonly embed: (input: string) => Effect.Effect<EmbedResponse, AiError.AiError>
126
+ readonly embedMany: (input: ReadonlyArray<string>) => Effect.Effect<EmbedManyResponse, AiError.AiError>
127
+ }
128
+
129
+ const invalidProviderResponse = (description: string): AiError.AiError =>
130
+ AiError.make({
131
+ module: "EmbeddingModel",
132
+ method: "embedMany",
133
+ reason: new AiError.InvalidOutputError({ description })
134
+ })
135
+
136
+ /**
137
+ * Creates an EmbeddingModel service from a provider embedMany implementation.
138
+ *
139
+ * @since 4.0.0
140
+ * @category constructors
141
+ */
142
+ export const make: (params: {
143
+ readonly embedMany: (options: ProviderOptions) => Effect.Effect<ProviderResponse, AiError.AiError>
144
+ }) => Effect.Effect<Service> = Effect.fnUntraced(function*(params) {
145
+ const resolver = RequestResolver.make<EmbeddingRequest>((entries) =>
146
+ Effect.flatMap(
147
+ params.embedMany({
148
+ inputs: entries.map((entry) => entry.request.input)
149
+ }),
150
+ (response) =>
151
+ Effect.map(mapProviderResults(entries.length, response.results), (embeddings) => {
152
+ for (let i = 0; i < entries.length; i++) {
153
+ entries[i].completeUnsafe(Exit.succeed(embeddings[i]))
154
+ }
155
+ })
156
+ )
157
+ ).pipe(
158
+ RequestResolver.withSpan("EmbeddingModel.resolver")
159
+ )
160
+
161
+ return EmbeddingModel.of({
162
+ resolver,
163
+ embed: (input) =>
164
+ Effect.request(new EmbeddingRequest({ input }), resolver).pipe(
165
+ Effect.withSpan("EmbeddingModel.embed")
166
+ ),
167
+ embedMany: (input) =>
168
+ (input.length === 0
169
+ ? Effect.succeed(
170
+ new EmbedManyResponse({
171
+ embeddings: [],
172
+ usage: new EmbeddingUsage({ inputTokens: undefined })
173
+ })
174
+ )
175
+ : params.embedMany({ inputs: input }).pipe(
176
+ Effect.flatMap((response) =>
177
+ mapProviderResults(input.length, response.results).pipe(
178
+ Effect.map((embeddings) =>
179
+ new EmbedManyResponse({
180
+ embeddings,
181
+ usage: new EmbeddingUsage({
182
+ inputTokens: response.usage.inputTokens
183
+ })
184
+ })
185
+ )
186
+ )
187
+ )
188
+ )).pipe(Effect.withSpan("EmbeddingModel.embedMany"))
189
+ })
190
+ })
191
+
192
+ const mapProviderResults = (
193
+ inputLength: number,
194
+ results: Array<Array<number>>
195
+ ): Effect.Effect<Array<EmbedResponse>, AiError.AiError> => {
196
+ const embeddings = new Array<EmbedResponse>(inputLength)
197
+ if (results.length !== inputLength) {
198
+ return Effect.fail(
199
+ invalidProviderResponse(
200
+ `Provider returned ${results.length} embeddings but expected ${inputLength}`
201
+ )
202
+ )
203
+ }
204
+ for (let i = 0; i < results.length; i++) {
205
+ const vector = results[i]
206
+ embeddings[i] = new EmbedResponse({ vector })
207
+ }
208
+ return Effect.succeed(embeddings)
209
+ }