effect 4.0.0-rc.118 → 4.0.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 (254) hide show
  1. package/README.md +17 -4
  2. package/dist/Array.d.ts +25 -25
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +16 -16
  5. package/dist/Array.js.map +1 -1
  6. package/dist/Cache.d.ts.map +1 -1
  7. package/dist/Cache.js +20 -16
  8. package/dist/Cache.js.map +1 -1
  9. package/dist/Cause.d.ts.map +1 -1
  10. package/dist/Cause.js +1 -1
  11. package/dist/Cause.js.map +1 -1
  12. package/dist/Chunk.d.ts +26 -26
  13. package/dist/Chunk.d.ts.map +1 -1
  14. package/dist/Chunk.js +15 -15
  15. package/dist/Chunk.js.map +1 -1
  16. package/dist/Effect.d.ts +167 -39
  17. package/dist/Effect.d.ts.map +1 -1
  18. package/dist/Effect.js +16 -10
  19. package/dist/Effect.js.map +1 -1
  20. package/dist/HashRing.d.ts.map +1 -1
  21. package/dist/HashRing.js +13 -5
  22. package/dist/HashRing.js.map +1 -1
  23. package/dist/MutableHashMap.d.ts.map +1 -1
  24. package/dist/MutableHashMap.js +4 -22
  25. package/dist/MutableHashMap.js.map +1 -1
  26. package/dist/Option.d.ts +17 -17
  27. package/dist/Option.d.ts.map +1 -1
  28. package/dist/Option.js +6 -6
  29. package/dist/Option.js.map +1 -1
  30. package/dist/Pool.d.ts +9 -5
  31. package/dist/Pool.d.ts.map +1 -1
  32. package/dist/Pool.js +34 -41
  33. package/dist/Pool.js.map +1 -1
  34. package/dist/Queue.d.ts +19 -2
  35. package/dist/Queue.d.ts.map +1 -1
  36. package/dist/Queue.js +9 -4
  37. package/dist/Queue.js.map +1 -1
  38. package/dist/RcMap.d.ts.map +1 -1
  39. package/dist/RcMap.js +16 -1
  40. package/dist/RcMap.js.map +1 -1
  41. package/dist/Record.d.ts +14 -14
  42. package/dist/Record.d.ts.map +1 -1
  43. package/dist/Record.js +10 -10
  44. package/dist/Record.js.map +1 -1
  45. package/dist/Schema.d.ts +37 -21
  46. package/dist/Schema.d.ts.map +1 -1
  47. package/dist/Schema.js +29 -10
  48. package/dist/Schema.js.map +1 -1
  49. package/dist/SchemaAST.d.ts.map +1 -1
  50. package/dist/SchemaAST.js +116 -97
  51. package/dist/SchemaAST.js.map +1 -1
  52. package/dist/SchemaRepresentation.d.ts +3 -1
  53. package/dist/SchemaRepresentation.d.ts.map +1 -1
  54. package/dist/SchemaRepresentation.js +3 -1
  55. package/dist/SchemaRepresentation.js.map +1 -1
  56. package/dist/ScopedCache.d.ts.map +1 -1
  57. package/dist/ScopedCache.js +24 -16
  58. package/dist/ScopedCache.js.map +1 -1
  59. package/dist/Stream.d.ts +45 -6
  60. package/dist/Stream.d.ts.map +1 -1
  61. package/dist/Stream.js +55 -44
  62. package/dist/Stream.js.map +1 -1
  63. package/dist/ai/LanguageModel.d.ts +8 -0
  64. package/dist/ai/LanguageModel.d.ts.map +1 -1
  65. package/dist/ai/LanguageModel.js +13 -2
  66. package/dist/ai/LanguageModel.js.map +1 -1
  67. package/dist/ai/McpSchema.d.ts +402 -402
  68. package/dist/ai/McpSchema.d.ts.map +1 -1
  69. package/dist/ai/McpSchema.js +1 -1
  70. package/dist/ai/McpSchema.js.map +1 -1
  71. package/dist/ai/McpServer.d.ts.map +1 -1
  72. package/dist/ai/McpServer.js +11 -3
  73. package/dist/ai/McpServer.js.map +1 -1
  74. package/dist/ai/Response.d.ts.map +1 -1
  75. package/dist/ai/Response.js +22 -12
  76. package/dist/ai/Response.js.map +1 -1
  77. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +1 -0
  78. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  79. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +1 -0
  80. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  81. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +1 -0
  82. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  83. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +1 -0
  84. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  85. package/dist/ai/internal/mcpSchema/v2024_11_05.js +3 -1
  86. package/dist/ai/internal/mcpSchema/v2024_11_05.js.map +1 -1
  87. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  88. package/dist/cluster/ClusterWorkflowEngine.js +16 -3
  89. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  90. package/dist/cluster/MessageStorage.d.ts +32 -8
  91. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  92. package/dist/cluster/MessageStorage.js +2 -1
  93. package/dist/cluster/MessageStorage.js.map +1 -1
  94. package/dist/cluster/Sharding.d.ts +5 -2
  95. package/dist/cluster/Sharding.d.ts.map +1 -1
  96. package/dist/cluster/Sharding.js +15 -8
  97. package/dist/cluster/Sharding.js.map +1 -1
  98. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  99. package/dist/cluster/SqlMessageStorage.js +36 -16
  100. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  101. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  102. package/dist/cluster/SqlRunnerStorage.js +30 -44
  103. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  104. package/dist/cluster/internal/entityManager.js +63 -62
  105. package/dist/cluster/internal/entityManager.js.map +1 -1
  106. package/dist/cluster/internal/interruptors.js +8 -0
  107. package/dist/cluster/internal/interruptors.js.map +1 -1
  108. package/dist/cluster/internal/resourceMap.js +39 -14
  109. package/dist/cluster/internal/resourceMap.js.map +1 -1
  110. package/dist/cluster/internal/resourceRef.js +168 -87
  111. package/dist/cluster/internal/resourceRef.js.map +1 -1
  112. package/dist/http/HttpEffect.d.ts +1 -0
  113. package/dist/http/HttpEffect.d.ts.map +1 -1
  114. package/dist/http/HttpEffect.js +29 -22
  115. package/dist/http/HttpEffect.js.map +1 -1
  116. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  117. package/dist/http/HttpMiddleware.js +3 -2
  118. package/dist/http/HttpMiddleware.js.map +1 -1
  119. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  120. package/dist/http/HttpStaticServer.js +14 -0
  121. package/dist/http/HttpStaticServer.js.map +1 -1
  122. package/dist/http/HttpTraceContext.d.ts +1 -1
  123. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  124. package/dist/http/HttpTraceContext.js +4 -4
  125. package/dist/http/HttpTraceContext.js.map +1 -1
  126. package/dist/http-api/HttpApiError.d.ts +1 -0
  127. package/dist/http-api/HttpApiError.d.ts.map +1 -1
  128. package/dist/http-api/HttpApiError.js +1 -0
  129. package/dist/http-api/HttpApiError.js.map +1 -1
  130. package/dist/internal/core.js +43 -2
  131. package/dist/internal/core.js.map +1 -1
  132. package/dist/internal/dateTime.js +1 -1
  133. package/dist/internal/dateTime.js.map +1 -1
  134. package/dist/internal/effect.js +138 -165
  135. package/dist/internal/effect.js.map +1 -1
  136. package/dist/internal/rcRef.js +59 -23
  137. package/dist/internal/rcRef.js.map +1 -1
  138. package/dist/internal/schema/annotations.js +1 -7
  139. package/dist/internal/schema/annotations.js.map +1 -1
  140. package/dist/internal/schema/codegen.js +56 -28
  141. package/dist/internal/schema/codegen.js.map +1 -1
  142. package/dist/internal/schema/fromJsonSchemaDocument.js +11 -8
  143. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  144. package/dist/internal/schema/toCodeDocument.js +10 -30
  145. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  146. package/dist/internal/schema/toCodec.js +1 -15
  147. package/dist/internal/schema/toCodec.js.map +1 -1
  148. package/dist/internal/schema/toEquivalence.js +4 -3
  149. package/dist/internal/schema/toEquivalence.js.map +1 -1
  150. package/dist/internal/schema/toFormatter.js +4 -4
  151. package/dist/internal/schema/toFormatter.js.map +1 -1
  152. package/dist/reactivity/Atom.d.ts.map +1 -1
  153. package/dist/reactivity/Atom.js +6 -2
  154. package/dist/reactivity/Atom.js.map +1 -1
  155. package/dist/reactivity/AtomRpc.d.ts +3 -3
  156. package/dist/reactivity/AtomRpc.d.ts.map +1 -1
  157. package/dist/rpc/Rpc.d.ts +2 -2
  158. package/dist/rpc/Rpc.d.ts.map +1 -1
  159. package/dist/rpc/RpcClient.d.ts +7 -7
  160. package/dist/rpc/RpcClient.d.ts.map +1 -1
  161. package/dist/rpc/RpcClient.js +21 -16
  162. package/dist/rpc/RpcClient.js.map +1 -1
  163. package/dist/rpc/RpcMessage.d.ts +1 -1
  164. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  165. package/dist/rpc/RpcServer.d.ts.map +1 -1
  166. package/dist/rpc/RpcServer.js +2 -2
  167. package/dist/rpc/RpcServer.js.map +1 -1
  168. package/dist/rpc/Utils.d.ts +2 -3
  169. package/dist/rpc/Utils.d.ts.map +1 -1
  170. package/dist/rpc/Utils.js +3 -4
  171. package/dist/rpc/Utils.js.map +1 -1
  172. package/dist/schema/SchemaCompiler/runtime.js +6 -1
  173. package/dist/schema/SchemaCompiler/runtime.js.map +1 -1
  174. package/dist/testing/TestSchema.d.ts +234 -6
  175. package/dist/testing/TestSchema.d.ts.map +1 -1
  176. package/dist/testing/TestSchema.js +244 -25
  177. package/dist/testing/TestSchema.js.map +1 -1
  178. package/dist/testing/index.d.ts +1 -0
  179. package/dist/testing/index.d.ts.map +1 -1
  180. package/dist/testing/index.js +1 -0
  181. package/dist/testing/index.js.map +1 -1
  182. package/dist/workflow/DurableDeferred.js +1 -1
  183. package/dist/workflow/DurableDeferred.js.map +1 -1
  184. package/dist/workflow/Workflow.js +1 -1
  185. package/dist/workflow/Workflow.js.map +1 -1
  186. package/dist/workflow/WorkflowEngine.d.ts +2 -0
  187. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  188. package/dist/workflow/WorkflowEngine.js +1 -0
  189. package/dist/workflow/WorkflowEngine.js.map +1 -1
  190. package/package.json +1 -1
  191. package/src/Array.ts +31 -31
  192. package/src/Cache.ts +22 -16
  193. package/src/Cause.ts +1 -1
  194. package/src/Chunk.ts +29 -29
  195. package/src/Effect.ts +170 -39
  196. package/src/HashRing.ts +14 -5
  197. package/src/MutableHashMap.ts +4 -24
  198. package/src/Option.ts +19 -19
  199. package/src/Pool.ts +28 -36
  200. package/src/Queue.ts +26 -6
  201. package/src/RcMap.ts +26 -2
  202. package/src/Record.ts +20 -20
  203. package/src/Schema.ts +48 -24
  204. package/src/SchemaAST.ts +135 -115
  205. package/src/SchemaRepresentation.ts +3 -1
  206. package/src/ScopedCache.ts +27 -16
  207. package/src/Stream.ts +158 -88
  208. package/src/ai/LanguageModel.ts +27 -2
  209. package/src/ai/McpSchema.ts +3 -3
  210. package/src/ai/McpServer.ts +12 -3
  211. package/src/ai/Response.ts +27 -12
  212. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +1 -0
  213. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +1 -0
  214. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +1 -0
  215. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +1 -0
  216. package/src/ai/internal/mcpSchema/v2024_11_05.ts +4 -2
  217. package/src/cluster/ClusterWorkflowEngine.ts +26 -5
  218. package/src/cluster/MessageStorage.ts +22 -5
  219. package/src/cluster/Sharding.ts +32 -28
  220. package/src/cluster/SqlMessageStorage.ts +45 -21
  221. package/src/cluster/SqlRunnerStorage.ts +85 -113
  222. package/src/cluster/internal/entityManager.ts +68 -80
  223. package/src/cluster/internal/interruptors.ts +13 -0
  224. package/src/cluster/internal/resourceMap.ts +55 -17
  225. package/src/cluster/internal/resourceRef.ts +189 -96
  226. package/src/http/HttpEffect.ts +15 -9
  227. package/src/http/HttpMiddleware.ts +8 -2
  228. package/src/http/HttpStaticServer.ts +16 -0
  229. package/src/http/HttpTraceContext.ts +8 -4
  230. package/src/http-api/HttpApiError.ts +1 -0
  231. package/src/internal/core.ts +56 -2
  232. package/src/internal/dateTime.ts +3 -1
  233. package/src/internal/effect.ts +146 -173
  234. package/src/internal/rcRef.ts +83 -33
  235. package/src/internal/schema/annotations.ts +0 -9
  236. package/src/internal/schema/codegen.ts +90 -43
  237. package/src/internal/schema/fromJsonSchemaDocument.ts +5 -3
  238. package/src/internal/schema/toCodeDocument.ts +10 -47
  239. package/src/internal/schema/toCodec.ts +3 -16
  240. package/src/internal/schema/toEquivalence.ts +4 -5
  241. package/src/internal/schema/toFormatter.ts +4 -6
  242. package/src/reactivity/Atom.ts +6 -2
  243. package/src/reactivity/AtomRpc.ts +5 -5
  244. package/src/rpc/Rpc.ts +2 -2
  245. package/src/rpc/RpcClient.ts +35 -32
  246. package/src/rpc/RpcMessage.ts +1 -1
  247. package/src/rpc/RpcServer.ts +2 -1
  248. package/src/rpc/Utils.ts +4 -10
  249. package/src/schema/SchemaCompiler/runtime.ts +17 -1
  250. package/src/testing/TestSchema.ts +315 -63
  251. package/src/testing/index.ts +1 -0
  252. package/src/workflow/DurableDeferred.ts +1 -1
  253. package/src/workflow/Workflow.ts +1 -1
  254. package/src/workflow/WorkflowEngine.ts +4 -0
package/src/SchemaAST.ts CHANGED
@@ -3394,7 +3394,8 @@ export function collectSentinels(ast: AST): ReadonlyArray<Sentinel> {
3394
3394
  }
3395
3395
  }
3396
3396
 
3397
- type CandidateIndex = (input: any, isConstructor: boolean) => ReadonlyArray<AST>
3397
+ /** @internal */
3398
+ export type CandidateIndex = (input: any, isConstructor: boolean) => ReadonlyArray<number>
3398
3399
  type SentinelEntry = readonly [
3399
3400
  byValue: Map<LiteralValue | symbol, Set<number>>,
3400
3401
  all: Set<number>
@@ -3409,15 +3410,17 @@ const getRuntimeType = (input: unknown): Type => input === null ? "null" : Array
3409
3410
  const hasPropertySignature = (input: object, key: PropertyKey): boolean =>
3410
3411
  key === "__proto__" ? Object.hasOwn(input, key) : key in input
3411
3412
 
3412
- function getIndex(types: ReadonlyArray<AST>): CandidateIndex {
3413
+ /** @internal */
3414
+ export function getCandidateIndex(types: ReadonlyArray<AST>): CandidateIndex {
3413
3415
  let index = candidateIndexCache.get(types)
3414
3416
  if (index) return index
3415
3417
 
3416
3418
  let bySentinel: SentinelIndex | undefined
3417
3419
  let sentinelCandidateCount = 0
3418
3420
  let otherwise: { [K in Type]?: Array<number> } | undefined
3419
- let literalCandidates: Map<LiteralValue | symbol, Array<AST>> | undefined
3421
+ let literalCandidates: Map<LiteralValue | symbol, Array<number>> | undefined
3420
3422
  let onlyLiterals = true
3423
+ const literalOf: Array<LiteralValue | symbol | undefined> = []
3421
3424
  for (let i = 0; i < types.length; i++) {
3422
3425
  const a = types[i]
3423
3426
  const encoded = toCandidate(a)
@@ -3426,9 +3429,10 @@ function getIndex(types: ReadonlyArray<AST>): CandidateIndex {
3426
3429
  if (isLiteral(encoded) || isUniqueSymbol(encoded)) {
3427
3430
  literalCandidates ??= new Map()
3428
3431
  const literal = isLiteral(encoded) ? encoded.literal : encoded.symbol
3432
+ literalOf[i] = literal
3429
3433
  let arr = literalCandidates.get(literal)
3430
3434
  if (!arr) literalCandidates.set(literal, arr = [])
3431
- arr.push(a)
3435
+ arr.push(i)
3432
3436
  } else {
3433
3437
  onlyLiterals = false
3434
3438
  }
@@ -3454,24 +3458,25 @@ function getIndex(types: ReadonlyArray<AST>): CandidateIndex {
3454
3458
  }
3455
3459
 
3456
3460
  // Non-discriminated members are grouped by runtime type once and reused by every decode.
3457
- const fallbacks: { [K in Type]?: ReadonlyArray<AST> } = {}
3458
- const getFallback = (type: Type): ReadonlyArray<AST> =>
3459
- fallbacks[type] ??= Object.freeze((otherwise?.[type] ?? emptyCandidates).map((i) => types[i]))
3461
+ const fallbacks: { [K in Type]?: ReadonlyArray<number> } = {}
3462
+ const getFallback = (type: Type): ReadonlyArray<number> =>
3463
+ fallbacks[type] ??= Object.freeze(otherwise?.[type] ?? emptyCandidates)
3460
3464
 
3461
3465
  if (onlyLiterals && literalCandidates) {
3462
3466
  literalCandidates.forEach(Object.freeze)
3463
3467
  index = (input) => literalCandidates.get(input) ?? emptyCandidates
3464
3468
  } else if (bySentinel?.size === 1 && !otherwise) {
3465
3469
  const [key, [byValue]] = bySentinel.entries().next().value!
3466
- const candidates = byValue as unknown as Map<LiteralValue | symbol, ReadonlyArray<AST>>
3470
+ const candidates = new Map<LiteralValue | symbol, ReadonlyArray<number>>()
3467
3471
  for (const [literal, indexes] of byValue) {
3468
- candidates.set(literal, Object.freeze(Array.from(indexes, (index) => types[index])))
3472
+ candidates.set(literal, Object.freeze(Array.from(indexes)))
3469
3473
  }
3474
+ const all = Object.freeze(types.map((_, i) => i))
3470
3475
  index = (input, isConstructor) => {
3471
3476
  if (Predicate.isObjectKeyword(input)) {
3472
3477
  const value = hasPropertySignature(input, key) ? (input as any)[key] : undefined
3473
3478
  if (value !== undefined) return candidates.get(value) ?? emptyCandidates
3474
- if (isConstructor) return types
3479
+ if (isConstructor) return all
3475
3480
  }
3476
3481
  return emptyCandidates
3477
3482
  }
@@ -3538,12 +3543,12 @@ function getIndex(types: ReadonlyArray<AST>): CandidateIndex {
3538
3543
  }
3539
3544
  }
3540
3545
  }
3541
- return Array.from(selected).sort((a, b) => a - b).map((i) => types[i])
3546
+ return Array.from(selected).sort((a, b) => a - b)
3542
3547
  }
3543
3548
  } else {
3544
3549
  index = (input) => {
3545
3550
  const fallback = getFallback(getRuntimeType(input))
3546
- return literalCandidates ? fallback.filter(filterLiterals(input)) : fallback
3551
+ return literalCandidates ? fallback.filter((i) => literalOf[i] === undefined || literalOf[i] === input) : fallback
3547
3552
  }
3548
3553
  }
3549
3554
 
@@ -3551,31 +3556,6 @@ function getIndex(types: ReadonlyArray<AST>): CandidateIndex {
3551
3556
  return index
3552
3557
  }
3553
3558
 
3554
- function filterLiterals(input: any) {
3555
- return (ast: AST) => {
3556
- const encoded = toCandidate(ast)
3557
- return encoded._tag === "Literal" ?
3558
- encoded.literal === input
3559
- : encoded._tag === "UniqueSymbol" ?
3560
- encoded.symbol === input
3561
- : true
3562
- }
3563
- }
3564
-
3565
- /**
3566
- * The goal is to reduce the number of a union members that will be checked.
3567
- * This is useful to reduce the number of issues that will be returned.
3568
- *
3569
- * @internal
3570
- */
3571
- export function getCandidates(
3572
- input: any,
3573
- types: ReadonlyArray<AST>,
3574
- isConstructor = false
3575
- ): ReadonlyArray<AST> {
3576
- return getIndex(types)(input, isConstructor)
3577
- }
3578
-
3579
3559
  /**
3580
3560
  * AST node representing a union of schemas.
3581
3561
  *
@@ -3680,43 +3660,28 @@ export const Union: new<A extends AST = AST>(
3680
3660
  ): SchemaParser.Parser {
3681
3661
  // oxlint-disable-next-line @typescript-eslint/no-this-alias
3682
3662
  const ast = this
3663
+ const isConstructor = compileField !== undefined
3664
+ const parsers: Array<SchemaParser.Parser> = []
3665
+ const parser = (i: number): SchemaParser.Parser => parsers[i] ??= compile(ast.types[i])
3666
+ let index: CandidateIndex | undefined
3683
3667
 
3684
3668
  return (input, options) => {
3685
3669
  if (input === InternalParser.missing) {
3686
3670
  return InternalParser.missingExit
3687
3671
  }
3688
- const candidates = getCandidates(input, ast.types, compileField !== undefined)
3672
+ const candidates = (index ??= getCandidateIndex(ast.types))(input, isConstructor)
3689
3673
 
3690
3674
  if (candidates.length === 0) {
3691
3675
  return Effect.fail(new SchemaIssue.AnyOf(ast, [], input, options))
3692
3676
  }
3693
3677
  if (candidates.length === 1) {
3694
- const result = compile(candidates[0])(input, options)
3678
+ const result = parser(candidates[0])(input, options)
3695
3679
  if ((result as Exit.Exit<unknown, SchemaIssue.Issue>)._tag === "Success") return result
3696
3680
  return effectIsExit(result)
3697
3681
  ? failSingleUnionCandidate(ast, (result as Exit.Failure<unknown, SchemaIssue.Issue>).cause, input, options)
3698
- : Effect.catchCause(result, (cause) => failSingleUnionCandidate(ast, cause, input, options))
3699
- }
3700
-
3701
- const state = {
3702
- ast,
3703
- compile,
3704
- input,
3705
- out: undefined,
3706
- successes: ast.options?.mode === "oneOf" ? [] : undefined,
3707
- issues: undefined as Arr.NonEmptyArray<SchemaIssue.Issue> | undefined,
3708
- options
3709
- }
3710
- const eff = parseUnion(state, candidates)
3711
- if (!eff) {
3712
- if (state.out) return state.out
3713
- return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options))
3682
+ : catchSingleUnionCandidate(ast, result, input, options)
3714
3683
  }
3715
- return Effect.flatMapEager(eff, (_) => {
3716
- if (state.out === InternalParser.sameExit) return Effect.succeed(input)
3717
- if (state.out) return state.out
3718
- return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options))
3719
- })
3684
+ return parseUnionCandidates(ast, parser, candidates, input, options)
3720
3685
  }
3721
3686
  }
3722
3687
  private _rebuild(
@@ -3795,20 +3760,65 @@ function failSingleUnionCandidate(
3795
3760
  return Exit.fail(new SchemaIssue.AnyOf(ast, [issue], input, options))
3796
3761
  }
3797
3762
 
3798
- const parseUnion = iterateEager<{
3799
- readonly compile: (ast: AST) => SchemaParser.Parser
3763
+ function catchSingleUnionCandidate(
3764
+ ast: Union,
3765
+ result: Effect.Effect<unknown, SchemaIssue.Issue, unknown>,
3766
+ input: unknown,
3767
+ options: ParseOptions
3768
+ ) {
3769
+ return Effect.catchCause(result, (cause) => failSingleUnionCandidate(ast, cause, input, options))
3770
+ }
3771
+
3772
+ type UnionParserState = {
3773
+ readonly parser: (i: number) => SchemaParser.Parser
3800
3774
  readonly ast: Union
3801
3775
  readonly input: unknown
3802
3776
  readonly options: ParseOptions
3803
3777
  out: Exit.Success<unknown, SchemaIssue.Issue> | undefined
3804
3778
  readonly successes: Array<AST> | undefined
3805
3779
  issues: Array<SchemaIssue.Issue> | undefined
3806
- }, AST>()({
3807
- onItem(s, ast) {
3808
- const parser = s.compile(ast)
3809
- return parser(s.input, s.options)
3780
+ }
3781
+
3782
+ function parseUnionCandidates(
3783
+ ast: Union,
3784
+ parser: (i: number) => SchemaParser.Parser,
3785
+ candidates: ReadonlyArray<number>,
3786
+ input: unknown,
3787
+ options: ParseOptions
3788
+ ): Effect.Effect<unknown, SchemaIssue.Issue, any> {
3789
+ const state: UnionParserState = {
3790
+ ast,
3791
+ parser,
3792
+ input,
3793
+ out: undefined,
3794
+ successes: ast.options?.mode === "oneOf" ? [] : undefined,
3795
+ issues: undefined,
3796
+ options
3797
+ }
3798
+ const eff = parseUnion(state, candidates)
3799
+ if (!eff) {
3800
+ if (state.out) return state.out
3801
+ return Effect.fail(new SchemaIssue.AnyOf(ast, state.issues ?? [], input, options))
3802
+ }
3803
+ return resumeUnion(eff, state)
3804
+ }
3805
+
3806
+ function resumeUnion(
3807
+ eff: Effect.Effect<void, SchemaIssue.Issue, any>,
3808
+ state: UnionParserState
3809
+ ): Effect.Effect<unknown, SchemaIssue.Issue, any> {
3810
+ return Effect.flatMapEager(eff, (_) => {
3811
+ if (state.out === InternalParser.sameExit) return Effect.succeed(state.input)
3812
+ if (state.out) return state.out
3813
+ return Effect.fail(new SchemaIssue.AnyOf(state.ast, state.issues ?? [], state.input, state.options))
3814
+ })
3815
+ }
3816
+
3817
+ const parseUnion = iterateEager<UnionParserState, number>()({
3818
+ onItem(s, i) {
3819
+ return s.parser(i)(s.input, s.options)
3810
3820
  },
3811
- step(s, candidate, exit) {
3821
+ step(s, i, exit) {
3812
3822
  if (exit._tag === "Failure") {
3813
3823
  const issue = InternalSchemaCause.getSchemaIssue(exit.cause)
3814
3824
  if (issue === undefined) {
@@ -3818,12 +3828,12 @@ const parseUnion = iterateEager<{
3818
3828
  else s.issues = [issue]
3819
3829
  } else {
3820
3830
  if (s.out && s.successes) {
3821
- s.successes.push(candidate)
3831
+ s.successes.push(s.ast.types[i])
3822
3832
  return Exit.fail(new SchemaIssue.OneOf(s.ast, s.successes, s.input, s.options))
3823
3833
  }
3824
3834
  s.out = exit
3825
3835
  if (s.successes) {
3826
- s.successes.push(candidate)
3836
+ s.successes.push(s.ast.types[i])
3827
3837
  } else {
3828
3838
  return Exit.void
3829
3839
  }
@@ -4220,26 +4230,33 @@ export function isPattern(regExp: globalThis.RegExp, annotations?: Schema.Annota
4220
4230
  )
4221
4231
  }
4222
4232
 
4223
- function copy<A extends AST>(ast: A): Types.Mutable<A> {
4233
+ // Copies that update only context or encoding share a body.
4234
+ // All other fields describe the body itself, including child contexts.
4235
+ const bodyOwners = new WeakMap<AST, AST>()
4236
+
4237
+ function copy<A extends AST>(ast: A, changes: Partial<AST>): A {
4224
4238
  // AST copies preserve the prototype and enumerable values, not property descriptors.
4225
- return Object.assign(Object.create(Object.getPrototypeOf(ast)), ast)
4239
+ const out = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, changes) as A
4240
+ if (Reflect.ownKeys(changes).every((key) => key === "context" || key === "encoding")) {
4241
+ bodyOwners.set(out, getContextOwner(ast))
4242
+ }
4243
+ return out
4226
4244
  }
4227
4245
 
4228
- const contextOwners = new WeakMap<AST, AST>()
4229
-
4230
4246
  /** @internal */
4231
- export function getContextOwner(ast: AST): AST {
4232
- return contextOwners.get(ast) ?? ast
4247
+ export function getContextOwner<A extends AST>(ast: A): A {
4248
+ const existing = bodyOwners.get(ast)
4249
+ if (existing !== undefined) return existing as A
4250
+ if (ast.encoding === undefined) return ast
4251
+ // A body representative must not expose or retain the node's own codec.
4252
+ const owner = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, { encoding: undefined }) as A
4253
+ bodyOwners.set(ast, owner)
4254
+ return owner
4233
4255
  }
4234
4256
 
4235
4257
  /** @internal */
4236
4258
  export function replaceEncoding<A extends AST>(ast: A, encoding: Encoding | undefined): A {
4237
- if (ast.encoding === encoding) {
4238
- return ast
4239
- }
4240
- const out = copy(ast)
4241
- out.encoding = encoding
4242
- return out
4259
+ return ast.encoding === encoding ? ast : copy(ast, { encoding })
4243
4260
  }
4244
4261
 
4245
4262
  /** @internal */
@@ -4248,13 +4265,10 @@ export function replaceContext<A extends AST>(ast: A, context: Context | undefin
4248
4265
  return ast
4249
4266
  }
4250
4267
  const owner = getContextOwner(ast)
4251
- if (owner.context === context) {
4252
- return owner as A
4268
+ if (owner.context === context && owner.encoding === ast.encoding) {
4269
+ return owner
4253
4270
  }
4254
- const out = copy(ast)
4255
- out.context = context
4256
- contextOwners.set(out as A, owner)
4257
- return out
4271
+ return copy(ast, { context })
4258
4272
  }
4259
4273
 
4260
4274
  /** @internal */
@@ -4268,9 +4282,7 @@ export function annotate<A extends AST>(ast: A, annotations: Schema.Annotations.
4268
4282
  const last = ast.checks[ast.checks.length - 1]
4269
4283
  return replaceChecks(ast, Arr.append(ast.checks.slice(0, -1), last.annotate(annotations)))
4270
4284
  }
4271
- const out = copy(ast)
4272
- out.annotations = { ...ast.annotations, ...annotations }
4273
- return out
4285
+ return copy(ast, { annotations: { ...ast.annotations, ...annotations } })
4274
4286
  }
4275
4287
 
4276
4288
  /** @internal */
@@ -4281,9 +4293,7 @@ export function replaceChecks<A extends AST>(ast: A, checks: Checks | undefined)
4281
4293
  if (ast.checks === checks) {
4282
4294
  return ast
4283
4295
  }
4284
- const out = copy(ast)
4285
- out.checks = checks
4286
- return out
4296
+ return copy(ast, { checks })
4287
4297
  }
4288
4298
 
4289
4299
  /** @internal */
@@ -4364,13 +4374,6 @@ function appendTransformation<A extends AST>(
4364
4374
  return replaceEncoding(to, to.encoding ? [...to.encoding, link] : [link])
4365
4375
  }
4366
4376
 
4367
- /** @internal */
4368
- export function brand(ast: AST, brand: string): AST {
4369
- const existing = InternalAnnotations.resolveBrands(ast)
4370
- const brands = existing ? [...existing, brand] : [brand]
4371
- return annotate(ast, { brands })
4372
- }
4373
-
4374
4377
  /**
4375
4378
  * Maps over the array but will return the original array if no changes occur.
4376
4379
  * @internal
@@ -4555,6 +4558,17 @@ function extractStructuralChecks(checks: Checks): Checks | undefined {
4555
4558
  return Arr.isArrayNonEmpty(out) ? out : undefined
4556
4559
  }
4557
4560
 
4561
+ function canPreserveEncodingChecks(ast: AST): boolean {
4562
+ let preserve = true
4563
+ function visit(child: AST): AST {
4564
+ preserve = preserve && !child.encoding && !isSuspend(child)
4565
+ if (preserve && "recur" in child) child.recur(visit)
4566
+ return child
4567
+ }
4568
+ if ("recur" in ast) ast.recur(visit)
4569
+ return preserve
4570
+ }
4571
+
4558
4572
  /**
4559
4573
  * Strips all encoding transformations from an AST, returning the decoded
4560
4574
  * (type-level) representation.
@@ -4581,22 +4595,24 @@ function extractStructuralChecks(checks: Checks): Checks | undefined {
4581
4595
  * @since 4.0.0
4582
4596
  */
4583
4597
  export const toType = memoizeIdempotent(<A extends AST>(ast: A): A => {
4584
- const out: any = ast
4585
- const type = out.recur?.(toType) ?? out
4586
- const encodingChecks: Checks | undefined = type.encodingChecks
4587
- if (encodingChecks) {
4588
- const checks = type === ast
4589
- ? encodingChecks
4598
+ const owner = getContextOwner(ast)
4599
+ if (owner !== ast) {
4600
+ const type = toType(owner)
4601
+ return type === owner && ast.encoding === undefined ? ast : replaceContext(type, ast.context)
4602
+ }
4603
+ const type = ("recur" in ast ? ast.recur(toType) : ast) as A
4604
+ if ("encodingChecks" in type && type.encodingChecks) {
4605
+ const checks = canPreserveEncodingChecks(ast)
4606
+ ? type.encodingChecks
4590
4607
  : isArrays(type) || isObjects(type) || isDeclaration(type) && type.typeParameters.length > 0
4591
- ? extractStructuralChecks(encodingChecks)
4608
+ ? extractStructuralChecks(type.encodingChecks)
4592
4609
  : undefined
4593
- const copyOfType = copy(type)
4594
- copyOfType.encoding = undefined
4595
- copyOfType.encodingChecks = undefined
4596
- copyOfType.checks = combineChecks(type.checks, checks)
4597
- return copyOfType
4610
+ return copy(type, {
4611
+ encodingChecks: undefined,
4612
+ checks: combineChecks(type.checks, checks)
4613
+ })
4598
4614
  }
4599
- return type.encoding ? replaceEncoding(type, undefined) : type
4615
+ return type
4600
4616
  })
4601
4617
 
4602
4618
  /**
@@ -4668,8 +4684,12 @@ export const flip = memoize((ast: AST): AST => {
4668
4684
  if (ast.encoding) {
4669
4685
  return flipEncoding(ast, ast.encoding)
4670
4686
  }
4671
- const out: any = ast
4672
- return out.flip?.(flip) ?? out.recur?.(flip) ?? out
4687
+ const owner = getContextOwner(ast)
4688
+ if (owner !== ast) {
4689
+ const flipped = flip(owner)
4690
+ return flipped === owner ? ast : replaceContext(flipped, ast.context)
4691
+ }
4692
+ return "flip" in ast ? ast.flip(flip) : "recur" in ast ? ast.recur(flip) : ast
4673
4693
  })
4674
4694
 
4675
4695
  /** @internal */
@@ -2432,7 +2432,9 @@ export interface ToRepresentationOptions {
2432
2432
  * **Details**
2433
2433
  *
2434
2434
  * Apply `SchemaAST.toType` to the AST first to lower its type side instead. The optional reference policy controls which
2435
- * candidates are moved into the document's shared reference table.
2435
+ * candidates are moved into the document's shared reference table. TypeScript-only
2436
+ * distinctions such as those added by `Schema.brand` are absent from the AST
2437
+ * and cannot be reconstructed from the resulting document.
2436
2438
  *
2437
2439
  * @see {@link toRepresentations} for multiple roots sharing one reference table
2438
2440
  *
@@ -317,7 +317,7 @@ export const get: {
317
317
  // Move the entry to the end of the map to keep it fresh
318
318
  MutableHashMap.remove(state.map, key)
319
319
  MutableHashMap.set(state.map, key, oentry.value)
320
- return awaitEntry(oentry.value, restore)
320
+ return awaitEntry(self, key, oentry.value, restore)
321
321
  }
322
322
  const scope = Scope.makeUnsafe()
323
323
  const deferred = Deferred.makeUnsafe</**
@@ -380,41 +380,46 @@ export const get: {
380
380
  entry.fiber = effect.forkUnsafe(
381
381
  fiber,
382
382
  effect.onExit(effect.suspend(() => Scope.provide(self.lookup(key), scope)), (exit) => {
383
- Deferred.doneUnsafe(deferred, exit)
384
383
  if (effect.exitHasInterrupts(exit)) {
385
- if (self.state._tag === "Open") {
386
- const current = MutableHashMap.get(self.state.map, key)
387
- if (Option.isSome(current) && current.value === entry) MutableHashMap.remove(self.state.map, key)
388
- }
384
+ removeEntry(self, key, entry)
385
+ Deferred.doneUnsafe(deferred, exit)
389
386
  return Scope.close(scope, exit)
390
387
  }
391
- const ttl = self.timeToLive(exit, key)
392
- if (Duration.isFinite(ttl)) {
393
- entry.expiresAt = fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl)
388
+ try {
389
+ const ttl = self.timeToLive(exit, key)
390
+ if (Duration.isFinite(ttl)) {
391
+ entry.expiresAt = fiber.getRef(effect.ClockRef).currentTimeMillisUnsafe() + Duration.toMillis(ttl)
392
+ }
393
+ } finally {
394
+ Deferred.doneUnsafe(deferred, exit)
394
395
  }
395
396
  return effect.void
396
397
  }),
397
398
  true,
398
399
  true
399
400
  )
400
- return awaitEntry(entry, restore)
401
+ return awaitEntry(self, key, entry, restore)
401
402
  })
402
403
  )
403
404
  })
404
405
  )
405
406
  )
406
407
 
407
- const awaitEntry = <A, E>(
408
+ const awaitEntry = <Key, A, E, R>(
409
+ self: ScopedCache<Key, A, E, R>,
410
+ key: Key,
408
411
  entry: Entry<A, E>,
409
412
  restore: <X, Y, Z>(effect: Effect.Effect<X, Y, Z>) => Effect.Effect<X, Y, Z>
410
413
  ): Effect.Effect<A, E> => {
411
- const fiber = entry.fiber
412
- if (fiber === undefined || Deferred.isDoneUnsafe(entry.deferred)) return restore(Deferred.await(entry.deferred))
414
+ if (Deferred.isDoneUnsafe(entry.deferred)) return restore(Deferred.await(entry.deferred))
413
415
  entry.awaiters++
414
416
  // Install cleanup before restore so a pending interrupt cannot skip the decrement.
415
417
  return effect.onExit(restore(Deferred.await(entry.deferred)), () => {
416
418
  entry.awaiters--
417
- if (entry.awaiters > 0 || Deferred.isDoneUnsafe(entry.deferred)) return effect.void
419
+ const fiber = entry.fiber
420
+ if (entry.awaiters > 0 || fiber === undefined || Deferred.isDoneUnsafe(entry.deferred)) return effect.void
421
+ // Detach before interruption so callers arriving during finalization start fresh.
422
+ removeEntry(self, key, entry)
418
423
  return effect.flatMap(effect.fiberInterrupt(fiber), () => {
419
424
  const exit = fiber.pollUnsafe()!
420
425
  return Exit.isFailure(exit) && Cause.hasDies(exit.cause) ? effect.failCause(exit.cause) : effect.void
@@ -422,6 +427,12 @@ const awaitEntry = <A, E>(
422
427
  })
423
428
  }
424
429
 
430
+ const removeEntry = <Key, A, E, R>(self: ScopedCache<Key, A, E, R>, key: Key, entry: Entry<A, E>): void => {
431
+ if (self.state._tag !== "Open") return
432
+ const current = MutableHashMap.get(self.state.map, key)
433
+ if (Option.isSome(current) && current.value === entry) MutableHashMap.remove(self.state.map, key)
434
+ }
435
+
425
436
  const hasExpired = <A, E>(entry: Entry<A, E>, fiber: Fiber.Fiber<unknown, unknown>): boolean => {
426
437
  if (entry.expiresAt === undefined) {
427
438
  return false
@@ -518,7 +529,7 @@ export const getOption: {
518
529
  core.withFiber((fiber) =>
519
530
  effect.flatMap(
520
531
  getImpl(self, key, fiber),
521
- (entry) => entry ? effect.asSome(awaitEntry(entry, restore)) : effect.succeedNone
532
+ (entry) => entry ? effect.asSome(awaitEntry(self, key, entry, restore)) : effect.succeedNone
522
533
  )
523
534
  )
524
535
  )
@@ -1004,7 +1015,7 @@ export const invalidateWhen: {
1004
1015
  if (entry === undefined) {
1005
1016
  return effect.succeed(false)
1006
1017
  }
1007
- return awaitEntry(entry, restore).pipe(
1018
+ return awaitEntry(self, key, entry, restore).pipe(
1008
1019
  effect.flatMap((value) => {
1009
1020
  if (self.state._tag === "Closed") {
1010
1021
  return effect.succeed(false)