effect 4.0.0-rc.113 → 4.0.0-rc.115

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 (76) hide show
  1. package/dist/Effect.d.ts +268 -7
  2. package/dist/Effect.d.ts.map +1 -1
  3. package/dist/Effect.js +33 -2
  4. package/dist/Effect.js.map +1 -1
  5. package/dist/Effectable.d.ts +1 -2
  6. package/dist/Effectable.d.ts.map +1 -1
  7. package/dist/Effectable.js.map +1 -1
  8. package/dist/FileSystem.d.ts +3 -4
  9. package/dist/FileSystem.d.ts.map +1 -1
  10. package/dist/FileSystem.js.map +1 -1
  11. package/dist/Match.d.ts +1 -14
  12. package/dist/Match.d.ts.map +1 -1
  13. package/dist/Schema.d.ts +4 -4
  14. package/dist/Schema.d.ts.map +1 -1
  15. package/dist/SchemaAST.d.ts.map +1 -1
  16. package/dist/SchemaAST.js +43 -23
  17. package/dist/SchemaAST.js.map +1 -1
  18. package/dist/SchemaParser.d.ts.map +1 -1
  19. package/dist/SchemaParser.js +3 -2
  20. package/dist/SchemaParser.js.map +1 -1
  21. package/dist/SchemaRepresentation.d.ts +39 -24
  22. package/dist/SchemaRepresentation.d.ts.map +1 -1
  23. package/dist/SchemaRepresentation.js +37 -23
  24. package/dist/SchemaRepresentation.js.map +1 -1
  25. package/dist/internal/arbitrary/schema.js.map +1 -1
  26. package/dist/internal/effect.js +16 -8
  27. package/dist/internal/effect.js.map +1 -1
  28. package/dist/internal/schema/fromJsonSchemaDocument.js +221 -230
  29. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  30. package/dist/internal/schema/make.js +8 -3
  31. package/dist/internal/schema/make.js.map +1 -1
  32. package/dist/internal/schema/toCodeDocument.js +4 -4
  33. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  34. package/dist/internal/schema/toEquivalence.js +2 -2
  35. package/dist/internal/schema/toEquivalence.js.map +1 -1
  36. package/dist/internal/schema/toFormatter.js +2 -2
  37. package/dist/internal/schema/toFormatter.js.map +1 -1
  38. package/dist/unstable/cli/Prompt.d.ts +6 -2
  39. package/dist/unstable/cli/Prompt.d.ts.map +1 -1
  40. package/dist/unstable/cli/Prompt.js +8 -7
  41. package/dist/unstable/cli/Prompt.js.map +1 -1
  42. package/dist/unstable/cli/internal/command.d.ts +1 -6
  43. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  44. package/dist/unstable/cli/internal/command.js +2 -0
  45. package/dist/unstable/cli/internal/command.js.map +1 -1
  46. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  47. package/dist/unstable/http/HttpEffect.js +6 -2
  48. package/dist/unstable/http/HttpEffect.js.map +1 -1
  49. package/dist/unstable/http/HttpServerResponse.d.ts +13 -2
  50. package/dist/unstable/http/HttpServerResponse.d.ts.map +1 -1
  51. package/dist/unstable/http/HttpServerResponse.js +19 -5
  52. package/dist/unstable/http/HttpServerResponse.js.map +1 -1
  53. package/dist/unstable/persistence/Persistence.d.ts.map +1 -1
  54. package/dist/unstable/persistence/Persistence.js +2 -10
  55. package/dist/unstable/persistence/Persistence.js.map +1 -1
  56. package/package.json +2 -2
  57. package/src/Effect.ts +271 -7
  58. package/src/Effectable.ts +2 -2
  59. package/src/FileSystem.ts +3 -4
  60. package/src/Match.ts +1 -1
  61. package/src/Schema.ts +4 -7
  62. package/src/SchemaAST.ts +48 -25
  63. package/src/SchemaParser.ts +3 -2
  64. package/src/SchemaRepresentation.ts +39 -24
  65. package/src/internal/arbitrary/schema.ts +16 -16
  66. package/src/internal/effect.ts +34 -11
  67. package/src/internal/schema/fromJsonSchemaDocument.ts +294 -268
  68. package/src/internal/schema/make.ts +9 -4
  69. package/src/internal/schema/toCodeDocument.ts +4 -4
  70. package/src/internal/schema/toEquivalence.ts +2 -2
  71. package/src/internal/schema/toFormatter.ts +2 -2
  72. package/src/unstable/cli/Prompt.ts +22 -11
  73. package/src/unstable/cli/internal/command.ts +2 -0
  74. package/src/unstable/http/HttpEffect.ts +5 -2
  75. package/src/unstable/http/HttpServerResponse.ts +21 -5
  76. package/src/unstable/persistence/Persistence.ts +6 -16
package/src/Effect.ts CHANGED
@@ -13941,8 +13941,8 @@ export const onExitFilter: {
13941
13941
  export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>> = internal.cached
13942
13942
 
13943
13943
  /**
13944
- * Returns an effect that caches its result for a specified `Duration`,
13945
- * known as "timeToLive" (TTL).
13944
+ * Returns an effect that caches its result for a fixed duration or a duration
13945
+ * computed from its `Exit`, known as "timeToLive" (TTL).
13946
13946
  *
13947
13947
  * **When to use**
13948
13948
  *
@@ -13961,6 +13961,17 @@ export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
13961
13961
  * After the specified duration has passed, the cache expires, and the effect
13962
13962
  * will be recomputed upon the next evaluation.
13963
13963
  *
13964
+ * `timeToLive` accepts a `Duration.Input` or a function from `Exit<A, E>` to
13965
+ * `Duration.Input`. The function runs once after each fresh computation,
13966
+ * including failures, so successes and failures can have different TTLs. It
13967
+ * does not run when the cache is created or when a cached result is reused.
13968
+ * The callback also receives interruption exits, which are cached for the
13969
+ * returned duration.
13970
+ *
13971
+ * The TTL starts when the computation completes. Concurrent callers share the
13972
+ * pending computation. A zero TTL expires immediately, and an infinite TTL
13973
+ * keeps the result indefinitely.
13974
+ *
13964
13975
  * **Example** (Memoizing an effect with TTL)
13965
13976
  *
13966
13977
  * ```ts import.meta.vitest
@@ -13985,6 +13996,26 @@ export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
13985
13996
  * output // => ["expensive task...", "result 1", "result 1", "result 1"]
13986
13997
  * ```
13987
13998
  *
13999
+ * **Example** (Caching successes while retrying failures)
14000
+ *
14001
+ * ```ts import.meta.vitest
14002
+ * import { Effect, Exit } from "effect"
14003
+ *
14004
+ * let attempts = 0
14005
+ * const task = Effect.suspend(() =>
14006
+ * ++attempts === 1 ? Effect.fail("temporary failure") : Effect.succeed(42)
14007
+ * )
14008
+ * const program = Effect.gen(function*() {
14009
+ * const cached = yield* task.pipe(
14010
+ * Effect.cachedWithTTL((exit) => Exit.isSuccess(exit) ? "1 hour" : 0)
14011
+ * )
14012
+ * yield* Effect.exit(cached)
14013
+ * return yield* cached
14014
+ * })
14015
+ *
14016
+ * Effect.runSync(program) // => 42
14017
+ * ```
14018
+ *
13988
14019
  * @see {@link cached} for a similar function that caches the result
13989
14020
  * indefinitely.
13990
14021
  * @see {@link cachedInvalidateWithTTL} for a similar function that includes an
@@ -13994,8 +14025,8 @@ export const cached: <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
13994
14025
  */
13995
14026
  export const cachedWithTTL: {
13996
14027
  /**
13997
- * Returns an effect that caches its result for a specified `Duration`,
13998
- * known as "timeToLive" (TTL).
14028
+ * Returns an effect that caches its result for a fixed duration or a duration
14029
+ * computed from its `Exit`, known as "timeToLive" (TTL).
13999
14030
  *
14000
14031
  * **When to use**
14001
14032
  *
@@ -14014,6 +14045,17 @@ export const cachedWithTTL: {
14014
14045
  * After the specified duration has passed, the cache expires, and the effect
14015
14046
  * will be recomputed upon the next evaluation.
14016
14047
  *
14048
+ * `timeToLive` accepts a `Duration.Input` or a function from `Exit<A, E>` to
14049
+ * `Duration.Input`. The function runs once after each fresh computation,
14050
+ * including failures, so successes and failures can have different TTLs. It
14051
+ * does not run when the cache is created or when a cached result is reused.
14052
+ * The callback also receives interruption exits, which are cached for the
14053
+ * returned duration.
14054
+ *
14055
+ * The TTL starts when the computation completes. Concurrent callers share the
14056
+ * pending computation. A zero TTL expires immediately, and an infinite TTL
14057
+ * keeps the result indefinitely.
14058
+ *
14017
14059
  * **Example** (Memoizing an effect with TTL)
14018
14060
  *
14019
14061
  * ```ts import.meta.vitest
@@ -14038,6 +14080,110 @@ export const cachedWithTTL: {
14038
14080
  * output // => ["expensive task...", "result 1", "result 1", "result 1"]
14039
14081
  * ```
14040
14082
  *
14083
+ * **Example** (Caching successes while retrying failures)
14084
+ *
14085
+ * ```ts import.meta.vitest
14086
+ * import { Effect, Exit } from "effect"
14087
+ *
14088
+ * let attempts = 0
14089
+ * const task = Effect.suspend(() =>
14090
+ * ++attempts === 1 ? Effect.fail("temporary failure") : Effect.succeed(42)
14091
+ * )
14092
+ * const program = Effect.gen(function*() {
14093
+ * const cached = yield* task.pipe(
14094
+ * Effect.cachedWithTTL((exit) => Exit.isSuccess(exit) ? "1 hour" : 0)
14095
+ * )
14096
+ * yield* Effect.exit(cached)
14097
+ * return yield* cached
14098
+ * })
14099
+ *
14100
+ * Effect.runSync(program) // => 42
14101
+ * ```
14102
+ *
14103
+ * @see {@link cached} for a similar function that caches the result
14104
+ * indefinitely.
14105
+ * @see {@link cachedInvalidateWithTTL} for a similar function that includes an
14106
+ * additional effect for manually invalidating the cached value.
14107
+ * @category caching
14108
+ * @since 2.0.0
14109
+ */
14110
+ <A, E>(timeToLive: (exit: Exit.Exit<A, E>) => Duration.Input): <R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
14111
+ /**
14112
+ * Returns an effect that caches its result for a fixed duration or a duration
14113
+ * computed from its `Exit`, known as "timeToLive" (TTL).
14114
+ *
14115
+ * **When to use**
14116
+ *
14117
+ * Use when you need a costly effect result to be reused for a bounded duration
14118
+ * before being recomputed.
14119
+ *
14120
+ * **Details**
14121
+ *
14122
+ * This function is used to cache the result of an effect for a specified amount
14123
+ * of time. This means that the first time the effect is evaluated, its result
14124
+ * is computed and stored.
14125
+ *
14126
+ * If the effect is evaluated again within the specified `timeToLive`, the
14127
+ * cached result will be used, avoiding recomputation.
14128
+ *
14129
+ * After the specified duration has passed, the cache expires, and the effect
14130
+ * will be recomputed upon the next evaluation.
14131
+ *
14132
+ * `timeToLive` accepts a `Duration.Input` or a function from `Exit<A, E>` to
14133
+ * `Duration.Input`. The function runs once after each fresh computation,
14134
+ * including failures, so successes and failures can have different TTLs. It
14135
+ * does not run when the cache is created or when a cached result is reused.
14136
+ * The callback also receives interruption exits, which are cached for the
14137
+ * returned duration.
14138
+ *
14139
+ * The TTL starts when the computation completes. Concurrent callers share the
14140
+ * pending computation. A zero TTL expires immediately, and an infinite TTL
14141
+ * keeps the result indefinitely.
14142
+ *
14143
+ * **Example** (Memoizing an effect with TTL)
14144
+ *
14145
+ * ```ts import.meta.vitest
14146
+ * import { Effect } from "effect"
14147
+ * const output: Array<unknown> = []
14148
+ * const record = (value: unknown) => Effect.sync(() => { output.push(value) })
14149
+ *
14150
+ * let i = 1
14151
+ * const expensiveTask = Effect.sync(() => {
14152
+ * void output.push("expensive task...")
14153
+ * return `result ${i++}`
14154
+ * })
14155
+ *
14156
+ * const program = Effect.gen(function*() {
14157
+ * const cached = yield* Effect.cachedWithTTL(expensiveTask, "1 hour")
14158
+ * yield* cached.pipe(Effect.andThen(record))
14159
+ * yield* cached.pipe(Effect.andThen(record))
14160
+ * yield* cached.pipe(Effect.andThen(record))
14161
+ * })
14162
+ *
14163
+ * Effect.runSync(program)
14164
+ * output // => ["expensive task...", "result 1", "result 1", "result 1"]
14165
+ * ```
14166
+ *
14167
+ * **Example** (Caching successes while retrying failures)
14168
+ *
14169
+ * ```ts import.meta.vitest
14170
+ * import { Effect, Exit } from "effect"
14171
+ *
14172
+ * let attempts = 0
14173
+ * const task = Effect.suspend(() =>
14174
+ * ++attempts === 1 ? Effect.fail("temporary failure") : Effect.succeed(42)
14175
+ * )
14176
+ * const program = Effect.gen(function*() {
14177
+ * const cached = yield* task.pipe(
14178
+ * Effect.cachedWithTTL((exit) => Exit.isSuccess(exit) ? "1 hour" : 0)
14179
+ * )
14180
+ * yield* Effect.exit(cached)
14181
+ * return yield* cached
14182
+ * })
14183
+ *
14184
+ * Effect.runSync(program) // => 42
14185
+ * ```
14186
+ *
14041
14187
  * @see {@link cached} for a similar function that caches the result
14042
14188
  * indefinitely.
14043
14189
  * @see {@link cachedInvalidateWithTTL} for a similar function that includes an
@@ -14047,8 +14193,92 @@ export const cachedWithTTL: {
14047
14193
  */
14048
14194
  (timeToLive: Duration.Input): <A, E, R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
14049
14195
  /**
14050
- * Returns an effect that caches its result for a specified `Duration`,
14051
- * known as "timeToLive" (TTL).
14196
+ * Returns an effect that caches its result for a fixed duration or a duration
14197
+ * computed from its `Exit`, known as "timeToLive" (TTL).
14198
+ *
14199
+ * **When to use**
14200
+ *
14201
+ * Use when you need a costly effect result to be reused for a bounded duration
14202
+ * before being recomputed.
14203
+ *
14204
+ * **Details**
14205
+ *
14206
+ * This function is used to cache the result of an effect for a specified amount
14207
+ * of time. This means that the first time the effect is evaluated, its result
14208
+ * is computed and stored.
14209
+ *
14210
+ * If the effect is evaluated again within the specified `timeToLive`, the
14211
+ * cached result will be used, avoiding recomputation.
14212
+ *
14213
+ * After the specified duration has passed, the cache expires, and the effect
14214
+ * will be recomputed upon the next evaluation.
14215
+ *
14216
+ * `timeToLive` accepts a `Duration.Input` or a function from `Exit<A, E>` to
14217
+ * `Duration.Input`. The function runs once after each fresh computation,
14218
+ * including failures, so successes and failures can have different TTLs. It
14219
+ * does not run when the cache is created or when a cached result is reused.
14220
+ * The callback also receives interruption exits, which are cached for the
14221
+ * returned duration.
14222
+ *
14223
+ * The TTL starts when the computation completes. Concurrent callers share the
14224
+ * pending computation. A zero TTL expires immediately, and an infinite TTL
14225
+ * keeps the result indefinitely.
14226
+ *
14227
+ * **Example** (Memoizing an effect with TTL)
14228
+ *
14229
+ * ```ts import.meta.vitest
14230
+ * import { Effect } from "effect"
14231
+ * const output: Array<unknown> = []
14232
+ * const record = (value: unknown) => Effect.sync(() => { output.push(value) })
14233
+ *
14234
+ * let i = 1
14235
+ * const expensiveTask = Effect.sync(() => {
14236
+ * void output.push("expensive task...")
14237
+ * return `result ${i++}`
14238
+ * })
14239
+ *
14240
+ * const program = Effect.gen(function*() {
14241
+ * const cached = yield* Effect.cachedWithTTL(expensiveTask, "1 hour")
14242
+ * yield* cached.pipe(Effect.andThen(record))
14243
+ * yield* cached.pipe(Effect.andThen(record))
14244
+ * yield* cached.pipe(Effect.andThen(record))
14245
+ * })
14246
+ *
14247
+ * Effect.runSync(program)
14248
+ * output // => ["expensive task...", "result 1", "result 1", "result 1"]
14249
+ * ```
14250
+ *
14251
+ * **Example** (Caching successes while retrying failures)
14252
+ *
14253
+ * ```ts import.meta.vitest
14254
+ * import { Effect, Exit } from "effect"
14255
+ *
14256
+ * let attempts = 0
14257
+ * const task = Effect.suspend(() =>
14258
+ * ++attempts === 1 ? Effect.fail("temporary failure") : Effect.succeed(42)
14259
+ * )
14260
+ * const program = Effect.gen(function*() {
14261
+ * const cached = yield* task.pipe(
14262
+ * Effect.cachedWithTTL((exit) => Exit.isSuccess(exit) ? "1 hour" : 0)
14263
+ * )
14264
+ * yield* Effect.exit(cached)
14265
+ * return yield* cached
14266
+ * })
14267
+ *
14268
+ * Effect.runSync(program) // => 42
14269
+ * ```
14270
+ *
14271
+ * @see {@link cached} for a similar function that caches the result
14272
+ * indefinitely.
14273
+ * @see {@link cachedInvalidateWithTTL} for a similar function that includes an
14274
+ * additional effect for manually invalidating the cached value.
14275
+ * @category caching
14276
+ * @since 2.0.0
14277
+ */
14278
+ <A, E>(timeToLive: Duration.Input | ((exit: Exit.Exit<A, E>) => Duration.Input)): <R>(self: Effect<A, E, R>) => Effect<Effect<A, E, R>>
14279
+ /**
14280
+ * Returns an effect that caches its result for a fixed duration or a duration
14281
+ * computed from its `Exit`, known as "timeToLive" (TTL).
14052
14282
  *
14053
14283
  * **When to use**
14054
14284
  *
@@ -14067,6 +14297,17 @@ export const cachedWithTTL: {
14067
14297
  * After the specified duration has passed, the cache expires, and the effect
14068
14298
  * will be recomputed upon the next evaluation.
14069
14299
  *
14300
+ * `timeToLive` accepts a `Duration.Input` or a function from `Exit<A, E>` to
14301
+ * `Duration.Input`. The function runs once after each fresh computation,
14302
+ * including failures, so successes and failures can have different TTLs. It
14303
+ * does not run when the cache is created or when a cached result is reused.
14304
+ * The callback also receives interruption exits, which are cached for the
14305
+ * returned duration.
14306
+ *
14307
+ * The TTL starts when the computation completes. Concurrent callers share the
14308
+ * pending computation. A zero TTL expires immediately, and an infinite TTL
14309
+ * keeps the result indefinitely.
14310
+ *
14070
14311
  * **Example** (Memoizing an effect with TTL)
14071
14312
  *
14072
14313
  * ```ts import.meta.vitest
@@ -14091,6 +14332,26 @@ export const cachedWithTTL: {
14091
14332
  * output // => ["expensive task...", "result 1", "result 1", "result 1"]
14092
14333
  * ```
14093
14334
  *
14335
+ * **Example** (Caching successes while retrying failures)
14336
+ *
14337
+ * ```ts import.meta.vitest
14338
+ * import { Effect, Exit } from "effect"
14339
+ *
14340
+ * let attempts = 0
14341
+ * const task = Effect.suspend(() =>
14342
+ * ++attempts === 1 ? Effect.fail("temporary failure") : Effect.succeed(42)
14343
+ * )
14344
+ * const program = Effect.gen(function*() {
14345
+ * const cached = yield* task.pipe(
14346
+ * Effect.cachedWithTTL((exit) => Exit.isSuccess(exit) ? "1 hour" : 0)
14347
+ * )
14348
+ * yield* Effect.exit(cached)
14349
+ * return yield* cached
14350
+ * })
14351
+ *
14352
+ * Effect.runSync(program) // => 42
14353
+ * ```
14354
+ *
14094
14355
  * @see {@link cached} for a similar function that caches the result
14095
14356
  * indefinitely.
14096
14357
  * @see {@link cachedInvalidateWithTTL} for a similar function that includes an
@@ -14098,7 +14359,10 @@ export const cachedWithTTL: {
14098
14359
  * @category caching
14099
14360
  * @since 2.0.0
14100
14361
  */
14101
- <A, E, R>(self: Effect<A, E, R>, timeToLive: Duration.Input): Effect<Effect<A, E, R>>
14362
+ <A, E, R>(
14363
+ self: Effect<A, E, R>,
14364
+ timeToLive: Duration.Input | ((exit: Exit.Exit<A, E>) => Duration.Input)
14365
+ ): Effect<Effect<A, E, R>>
14102
14366
  } = internal.cachedWithTTL
14103
14367
 
14104
14368
  /**
package/src/Effectable.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import type * as Effect from "./Effect.ts"
10
10
  import type * as Fiber from "./Fiber.ts"
11
- import { type EffectTypeId, evaluate, makePrimitiveProto } from "./internal/core.ts"
11
+ import { evaluate, makePrimitiveProto } from "./internal/core.ts"
12
12
 
13
13
  /**
14
14
  * Create a low-level `Effect` prototype.
@@ -78,7 +78,7 @@ type AsEffectReturn<Self> = Self extends {
78
78
 
79
79
  declare abstract class MixinBase extends Class<any, any, any> {
80
80
  constructor(...args: ReadonlyArray<any>)
81
- override readonly [EffectTypeId]: AsEffectReturn<this>[typeof EffectTypeId]
81
+ override readonly [Effect.TypeId]: AsEffectReturn<this>[Effect.TypeId]
82
82
  override [Symbol.iterator](): Effect.EffectIterator<AsEffectReturn<this>>
83
83
  }
84
84
 
package/src/FileSystem.ts CHANGED
@@ -906,10 +906,9 @@ export declare namespace File {
906
906
  * permissions, and size information. This structure is returned by file
907
907
  * stat operations.
908
908
  *
909
- * Node and Bun preserve `size` and `blksize` exactly. Unsafe numeric metadata
910
- * (such as `ino` or `dev`) fails the entire stat operation with `BadArgument`,
911
- * including optional fields. Inode values above `Number.MAX_SAFE_INTEGER`
912
- * can therefore prevent stat and HTTP file serving even for small files.
909
+ * Node and Bun preserve `size` and `blksize` exactly. Optional `number`
910
+ * metadata is `Option.none()` when absent or outside the safe integer range.
911
+ * Unsafe `dev` or `mode` values fail the stat operation with `BadArgument`.
913
912
  *
914
913
  * **Example** (Inspecting file information)
915
914
  *
package/src/Match.ts CHANGED
@@ -22,7 +22,7 @@ const TypeId = internal.TypeId
22
22
 
23
23
  // The conditional must stay deferred until P is inferred. Replacing it with an
24
24
  // intersection loses contextual typing for nested generic calls (microsoft/TypeScript#52864).
25
- type Contextual<P, Fallback> = internal.Contextual<P, Fallback>
25
+ type Contextual<P, Fallback> = [P] extends [never] ? Fallback : P
26
26
 
27
27
  type TagHandlers<D extends string, R, Ret> = {
28
28
  readonly [Tag in Types.Tags<D, R> & string]: (_: Extract<R, Record<D, Tag>>) => Ret
package/src/Schema.ts CHANGED
@@ -15512,9 +15512,6 @@ export function resolveAnnotations<S extends Constraint>(
15512
15512
  export function resolveAnnotationsKey<S extends Constraint>(schema: S): Annotations.Key<S["Type"]> | undefined {
15513
15513
  return schema.ast.context?.annotations
15514
15514
  }
15515
- /** @internal */
15516
- type AnnotationSchemaConstraint = Constraint
15517
-
15518
15515
  /**
15519
15516
  * The `Annotations` namespace groups all annotation interfaces used to attach
15520
15517
  * metadata to schemas. Annotations control documentation, validation messages,
@@ -15833,7 +15830,7 @@ export declare namespace Annotations {
15833
15830
  *
15834
15831
  * @since 4.0.0
15835
15832
  */
15836
- readonly arbitraryConstraint?: ToArbitrary.Constraint<any> | undefined
15833
+ readonly arbitraryConstraint?: ToArbitrary.FilterConstraint<any> | undefined
15837
15834
  /**
15838
15835
  * Marks the filter as *structural*, meaning it applies to the shape or
15839
15836
  * structure of the container (e.g., array length, object keys) rather than
@@ -15899,7 +15896,7 @@ export declare namespace Annotations {
15899
15896
  * @category models
15900
15897
  * @since 4.0.0
15901
15898
  */
15902
- interface Constraint<T = unknown> extends GenerationConstraint<T> {
15899
+ interface FilterConstraint<T = unknown> extends GenerationConstraint<T> {
15903
15900
  readonly order?: Order.Order<T> | undefined
15904
15901
  }
15905
15902
  /**
@@ -15908,7 +15905,7 @@ export declare namespace Annotations {
15908
15905
  * @category models
15909
15906
  * @since 4.0.0
15910
15907
  */
15911
- interface DeclarationInput<T, Parameters extends ReadonlyArray<AnnotationSchemaConstraint>> {
15908
+ interface DeclarationInput<T, Parameters extends ReadonlyArray<Constraint>> {
15912
15909
  readonly typeParameters: TypeParameters.Type<Parameters>
15913
15910
  readonly constraint: GenerationConstraint<T> | undefined
15914
15911
  }
@@ -15918,7 +15915,7 @@ export declare namespace Annotations {
15918
15915
  * @category models
15919
15916
  * @since 4.0.0
15920
15917
  */
15921
- interface Declaration<T, Parameters extends ReadonlyArray<AnnotationSchemaConstraint>> {
15918
+ interface Declaration<T, Parameters extends ReadonlyArray<Constraint>> {
15922
15919
  (input: DeclarationInput<T, Parameters>): SchemaAST.Link
15923
15920
  }
15924
15921
  }
package/src/SchemaAST.ts CHANGED
@@ -1377,10 +1377,18 @@ type TemplateLiteralPart =
1377
1377
  | TemplateLiteral
1378
1378
  | Union<TemplateLiteralPart>
1379
1379
 
1380
- function isTemplateLiteralPart(ast: AST, path: string, validated: WeakSet<AST>): ast is TemplateLiteralPart {
1380
+ function formatTemplateLiteralPath(path: string | number): string {
1381
+ return typeof path === "number" ? `parts[${path}]` : path
1382
+ }
1383
+
1384
+ function isTemplateLiteralPart(
1385
+ ast: AST,
1386
+ path: string | number,
1387
+ validated: Set<AST>
1388
+ ): ast is TemplateLiteralPart {
1381
1389
  if (validated.has(ast)) return true
1382
1390
  if (ast.encoding) {
1383
- throw new Error(`TemplateLiteral parts cannot have an encoding at ${path}`)
1391
+ throw new Error(`TemplateLiteral parts cannot have an encoding at ${formatTemplateLiteralPath(path)}`)
1384
1392
  }
1385
1393
  let valid: boolean
1386
1394
  switch (ast._tag) {
@@ -1392,14 +1400,18 @@ function isTemplateLiteralPart(ast: AST, path: string, validated: WeakSet<AST>):
1392
1400
  case "Literal":
1393
1401
  valid = !ast.checks
1394
1402
  break
1395
- case "TemplateLiteral":
1403
+ case "TemplateLiteral": {
1404
+ const currentPath = formatTemplateLiteralPath(path)
1396
1405
  valid = !ast.checks &&
1397
- ast.parts.every((part, index) => isTemplateLiteralPart(part, `${path}.parts[${index}]`, validated))
1406
+ ast.parts.every((part, index) => isTemplateLiteralPart(part, `${currentPath}.parts[${index}]`, validated))
1398
1407
  break
1399
- case "Union":
1408
+ }
1409
+ case "Union": {
1410
+ const currentPath = formatTemplateLiteralPath(path)
1400
1411
  valid = !ast.checks &&
1401
- ast.types.every((part, index) => isTemplateLiteralPart(part, `${path}.types[${index}]`, validated))
1412
+ ast.types.every((part, index) => isTemplateLiteralPart(part, `${currentPath}.types[${index}]`, validated))
1402
1413
  break
1414
+ }
1403
1415
  default:
1404
1416
  return false
1405
1417
  }
@@ -1477,10 +1489,10 @@ export const TemplateLiteral: new(
1477
1489
  super(annotations, checks, encoding, context)
1478
1490
  const encodedParts: Array<TemplateLiteralPart> = []
1479
1491
  const literals: Array<string | undefined> = []
1480
- const validated = new WeakSet<AST>()
1492
+ const validated = new Set<AST>()
1481
1493
  for (let index = 0; index < parts.length; index++) {
1482
1494
  const part = parts[index]
1483
- if (!isTemplateLiteralPart(part, `parts[${index}]`, validated)) {
1495
+ if (!isTemplateLiteralPart(part, index, validated)) {
1484
1496
  throw new Error(`Invalid TemplateLiteral part ${part._tag}`)
1485
1497
  }
1486
1498
  encodedParts.push(part)
@@ -2573,8 +2585,23 @@ function isIndexSignatureParameterSide(ast: AST): ast is IndexSignatureParameter
2573
2585
  }
2574
2586
  }
2575
2587
 
2588
+ function isIndexSignatureParameterEncodedSide(ast: AST): boolean {
2589
+ const encoded = getLastEncoding(ast)
2590
+ switch (encoded._tag) {
2591
+ case "String":
2592
+ case "Number":
2593
+ case "Symbol":
2594
+ case "TemplateLiteral":
2595
+ return true
2596
+ case "Union":
2597
+ return encoded.types.every(isIndexSignatureParameterEncodedSide)
2598
+ default:
2599
+ return false
2600
+ }
2601
+ }
2602
+
2576
2603
  function isIndexSignatureParameter(ast: AST): ast is IndexSignatureParameter {
2577
- return isIndexSignatureParameterSide(ast) && isIndexSignatureParameterSide(toEncoded(ast))
2604
+ return isIndexSignatureParameterSide(ast) && isIndexSignatureParameterEncodedSide(ast)
2578
2605
  }
2579
2606
 
2580
2607
  /**
@@ -2727,7 +2754,16 @@ export const Objects: new(
2727
2754
  this.encodingChecks = encodingChecks
2728
2755
 
2729
2756
  // Duplicate property signatures
2730
- const duplicates = propertySignatures.map((ps) => ps.name).filter((name, i, arr) => arr.indexOf(name) !== i)
2757
+ const seen = new Set<PropertyKey>()
2758
+ const duplicates: Array<PropertyKey> = []
2759
+ for (const propertySignature of propertySignatures) {
2760
+ const name = propertySignature.name
2761
+ if (seen.has(name)) {
2762
+ duplicates.push(name)
2763
+ } else {
2764
+ seen.add(name)
2765
+ }
2766
+ }
2731
2767
  if (duplicates.length > 0) {
2732
2768
  throw new Error(`Duplicate identifiers: ${JSON.stringify(duplicates)}. ts(2300)`)
2733
2769
  }
@@ -3780,20 +3816,6 @@ function formatIsOptional(isOptional: boolean | undefined): string {
3780
3816
  return isOptional ? "?" : ""
3781
3817
  }
3782
3818
 
3783
- /** @internal */
3784
- export function memoizeThunk<A>(f: () => A): () => A {
3785
- let done = false
3786
- let a: A
3787
- return () => {
3788
- if (done) {
3789
- return a
3790
- }
3791
- a = f()
3792
- done = true
3793
- return a
3794
- }
3795
- }
3796
-
3797
3819
  /**
3798
3820
  * AST node for lazy/recursive schemas.
3799
3821
  *
@@ -3866,7 +3888,8 @@ export const Suspend: new(
3866
3888
  throw new Error("Cannot add checks to Suspend")
3867
3889
  }
3868
3890
  super(annotations, undefined, encoding, context)
3869
- this.thunk = memoizeThunk(thunk)
3891
+ let ast: AST
3892
+ this.thunk = () => ast ??= thunk()
3870
3893
  }
3871
3894
  /** @internal */
3872
3895
  getParser(compile: SchemaParser.Compiler): SchemaParser.Parser {
@@ -41,9 +41,10 @@ import * as SchemaIssue from "./SchemaIssue.ts"
41
41
  * @since 4.0.0
42
42
  */
43
43
  export function makeEffect<S extends Schema.Constraint>(schema: S) {
44
- const parser = runWithCompiler<S["Type"], never>(constructorCompiler, SchemaAST.toType(schema.ast))
44
+ const ast = schema.ast
45
+ let parser: ReturnType<typeof runWithCompiler<S["Type"], never>>
45
46
  return (input: S["~type.make.in"], options?: Schema.MakeOptions): Effect.Effect<S["Type"], SchemaIssue.Issue> => {
46
- return parser(
47
+ return (parser ??= runWithCompiler<S["Type"], never>(constructorCompiler, SchemaAST.toType(ast)))(
47
48
  input,
48
49
  options?.disableChecks
49
50
  ? options?.parseOptions ? { ...options.parseOptions, disableChecks: true } : { disableChecks: true }