effect 4.0.0-beta.105 → 4.0.0-beta.106

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 (134) hide show
  1. package/dist/Config.js +3 -3
  2. package/dist/Config.js.map +1 -1
  3. package/dist/ConfigProvider.d.ts +35 -3
  4. package/dist/ConfigProvider.d.ts.map +1 -1
  5. package/dist/ConfigProvider.js +41 -8
  6. package/dist/ConfigProvider.js.map +1 -1
  7. package/dist/Fiber.d.ts +1 -1
  8. package/dist/Fiber.d.ts.map +1 -1
  9. package/dist/Function.d.ts +26 -0
  10. package/dist/Function.d.ts.map +1 -1
  11. package/dist/Function.js +36 -0
  12. package/dist/Function.js.map +1 -1
  13. package/dist/JsonSchema.d.ts.map +1 -1
  14. package/dist/JsonSchema.js +34 -17
  15. package/dist/JsonSchema.js.map +1 -1
  16. package/dist/Schema.d.ts +32 -84
  17. package/dist/Schema.d.ts.map +1 -1
  18. package/dist/Schema.js +58 -54
  19. package/dist/Schema.js.map +1 -1
  20. package/dist/SchemaAST.d.ts +2 -17
  21. package/dist/SchemaAST.d.ts.map +1 -1
  22. package/dist/SchemaAST.js +39 -28
  23. package/dist/SchemaAST.js.map +1 -1
  24. package/dist/SchemaRepresentation.d.ts +15 -3
  25. package/dist/SchemaRepresentation.d.ts.map +1 -1
  26. package/dist/SchemaRepresentation.js +8 -2
  27. package/dist/SchemaRepresentation.js.map +1 -1
  28. package/dist/ScopedRef.d.ts.map +1 -1
  29. package/dist/ScopedRef.js +1 -1
  30. package/dist/ScopedRef.js.map +1 -1
  31. package/dist/TxQueue.js +1 -1
  32. package/dist/TxQueue.js.map +1 -1
  33. package/dist/internal/effect.js +2 -4
  34. package/dist/internal/effect.js.map +1 -1
  35. package/dist/internal/executionPlan.js +3 -2
  36. package/dist/internal/executionPlan.js.map +1 -1
  37. package/dist/internal/rcRef.js +18 -13
  38. package/dist/internal/rcRef.js.map +1 -1
  39. package/dist/internal/schema/fromJsonSchemaDocument.js +10 -6
  40. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  41. package/dist/internal/schema/toArbitrary.d.ts +1 -4
  42. package/dist/internal/schema/toArbitrary.d.ts.map +1 -1
  43. package/dist/internal/schema/toArbitrary.js +0 -74
  44. package/dist/internal/schema/toArbitrary.js.map +1 -1
  45. package/dist/internal/schema/toJsonSchemaDocument.js +18 -4
  46. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  47. package/dist/internal/schema/toRepresentation.js +45 -43
  48. package/dist/internal/schema/toRepresentation.js.map +1 -1
  49. package/dist/testing/TestSchema.js +2 -2
  50. package/dist/testing/TestSchema.js.map +1 -1
  51. package/dist/unstable/ai/McpSchema.d.ts +110 -110
  52. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  53. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  54. package/dist/unstable/ai/McpServer.js +23 -11
  55. package/dist/unstable/ai/McpServer.js.map +1 -1
  56. package/dist/unstable/ai/Prompt.d.ts +4 -3
  57. package/dist/unstable/ai/Prompt.d.ts.map +1 -1
  58. package/dist/unstable/ai/Prompt.js +38 -13
  59. package/dist/unstable/ai/Prompt.js.map +1 -1
  60. package/dist/unstable/cluster/Entity.d.ts +2 -2
  61. package/dist/unstable/cluster/Entity.d.ts.map +1 -1
  62. package/dist/unstable/cluster/Entity.js.map +1 -1
  63. package/dist/unstable/cluster/EntityProxy.d.ts +3 -3
  64. package/dist/unstable/cluster/EntityProxy.d.ts.map +1 -1
  65. package/dist/unstable/cluster/EntityProxy.js +4 -3
  66. package/dist/unstable/cluster/EntityProxy.js.map +1 -1
  67. package/dist/unstable/cluster/Sharding.d.ts +2 -2
  68. package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
  69. package/dist/unstable/cluster/Sharding.js +14 -5
  70. package/dist/unstable/cluster/Sharding.js.map +1 -1
  71. package/dist/unstable/eventlog/EventLogEncryption.d.ts +6 -5
  72. package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
  73. package/dist/unstable/eventlog/EventLogEncryption.js +14 -12
  74. package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
  75. package/dist/unstable/eventlog/EventLogMessage.d.ts +3 -3
  76. package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
  77. package/dist/unstable/eventlog/EventLogMessage.js +2 -3
  78. package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
  79. package/dist/unstable/eventlog/EventLogRemote.js +4 -4
  80. package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
  81. package/dist/unstable/eventlog/EventLogServerEncrypted.js +3 -2
  82. package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -1
  83. package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
  84. package/dist/unstable/http/HttpServerRequest.js +7 -1
  85. package/dist/unstable/http/HttpServerRequest.js.map +1 -1
  86. package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
  87. package/dist/unstable/http/MultipartParser/internal/multipart.js +18 -6
  88. package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
  89. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  90. package/dist/unstable/httpapi/OpenApi.js +1 -1
  91. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  92. package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
  93. package/dist/unstable/rpc/RpcClient.js +5 -0
  94. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  95. package/dist/unstable/sql/SqlResolver.d.ts.map +1 -1
  96. package/dist/unstable/sql/SqlResolver.js +20 -16
  97. package/dist/unstable/sql/SqlResolver.js.map +1 -1
  98. package/dist/unstable/workers/Worker.d.ts.map +1 -1
  99. package/dist/unstable/workers/Worker.js +11 -12
  100. package/dist/unstable/workers/Worker.js.map +1 -1
  101. package/package.json +1 -1
  102. package/src/Config.ts +3 -3
  103. package/src/ConfigProvider.ts +43 -8
  104. package/src/Fiber.ts +1 -1
  105. package/src/Function.ts +37 -0
  106. package/src/JsonSchema.ts +43 -42
  107. package/src/Schema.ts +68 -146
  108. package/src/SchemaAST.ts +50 -30
  109. package/src/SchemaRepresentation.ts +15 -3
  110. package/src/ScopedRef.ts +3 -1
  111. package/src/TxQueue.ts +1 -1
  112. package/src/internal/effect.ts +3 -6
  113. package/src/internal/executionPlan.ts +3 -2
  114. package/src/internal/rcRef.ts +24 -16
  115. package/src/internal/schema/fromJsonSchemaDocument.ts +19 -14
  116. package/src/internal/schema/toArbitrary.ts +0 -72
  117. package/src/internal/schema/toJsonSchemaDocument.ts +24 -4
  118. package/src/internal/schema/toRepresentation.ts +66 -48
  119. package/src/testing/TestSchema.ts +2 -2
  120. package/src/unstable/ai/McpServer.ts +23 -11
  121. package/src/unstable/ai/Prompt.ts +37 -15
  122. package/src/unstable/cluster/Entity.ts +7 -2
  123. package/src/unstable/cluster/EntityProxy.ts +12 -5
  124. package/src/unstable/cluster/Sharding.ts +29 -17
  125. package/src/unstable/eventlog/EventLogEncryption.ts +15 -16
  126. package/src/unstable/eventlog/EventLogMessage.ts +2 -3
  127. package/src/unstable/eventlog/EventLogRemote.ts +4 -4
  128. package/src/unstable/eventlog/EventLogServerEncrypted.ts +2 -2
  129. package/src/unstable/http/HttpServerRequest.ts +8 -3
  130. package/src/unstable/http/MultipartParser/internal/multipart.ts +19 -6
  131. package/src/unstable/httpapi/OpenApi.ts +4 -1
  132. package/src/unstable/rpc/RpcClient.ts +5 -0
  133. package/src/unstable/sql/SqlResolver.ts +28 -20
  134. package/src/unstable/workers/Worker.ts +13 -14
package/src/SchemaAST.ts CHANGED
@@ -16,7 +16,7 @@ import * as Cause from "./Cause.ts"
16
16
  import * as Effect from "./Effect.ts"
17
17
  import * as Exit from "./Exit.ts"
18
18
  import { format, formatPropertyKey } from "./Formatter.ts"
19
- import { identity, memoize } from "./Function.ts"
19
+ import { identity, memoize, memoizeIdempotent } from "./Function.ts"
20
20
  import { effectIsExit, iterateEager } from "./internal/effect.ts"
21
21
  import * as InternalRecord from "./internal/record.ts"
22
22
  import * as InternalAnnotations from "./internal/schema/annotations.ts"
@@ -2517,7 +2517,7 @@ export type Sentinel = {
2517
2517
  readonly literal: LiteralValue | symbol
2518
2518
  }
2519
2519
 
2520
- const toCandidate = memoize((ast: AST): AST => {
2520
+ const toCandidate = memoizeIdempotent((ast: AST): AST => {
2521
2521
  while (true) {
2522
2522
  if (isSuspend(ast)) return unknown
2523
2523
  const encoding = ast.encoding
@@ -2581,7 +2581,7 @@ function getCandidateTypes(ast: AST): ReadonlyArray<Type> {
2581
2581
  }
2582
2582
 
2583
2583
  /** @internal */
2584
- export function collectSentinels(ast: AST): Array<Sentinel> {
2584
+ export function collectSentinels(ast: AST): ReadonlyArray<Sentinel> {
2585
2585
  switch (ast._tag) {
2586
2586
  default:
2587
2587
  return []
@@ -3280,6 +3280,13 @@ function modifyOwnPropertyDescriptors<A extends AST>(
3280
3280
  return Object.create(Object.getPrototypeOf(ast), d)
3281
3281
  }
3282
3282
 
3283
+ const contextOwners = new WeakMap<AST, AST>()
3284
+
3285
+ /** @internal */
3286
+ export function getContextOwner(ast: AST): AST {
3287
+ return contextOwners.get(ast) ?? ast
3288
+ }
3289
+
3283
3290
  /** @internal */
3284
3291
  export function replaceEncoding<A extends AST>(ast: A, encoding: Encoding | undefined): A {
3285
3292
  if (ast.encoding === encoding) {
@@ -3295,9 +3302,15 @@ export function replaceContext<A extends AST>(ast: A, context: Context | undefin
3295
3302
  if (ast.context === context) {
3296
3303
  return ast
3297
3304
  }
3298
- return modifyOwnPropertyDescriptors(ast, (d) => {
3305
+ const owner = getContextOwner(ast)
3306
+ if (owner.context === context) {
3307
+ return owner as A
3308
+ }
3309
+ const out = modifyOwnPropertyDescriptors(ast, (d) => {
3299
3310
  d.context.value = context
3300
3311
  })
3312
+ contextOwners.set(out, owner)
3313
+ return out
3301
3314
  }
3302
3315
 
3303
3316
  /** @internal */
@@ -3365,6 +3378,21 @@ export function applyToSelfOrLastLinkEncoding(f: (ast: AST) => AST) {
3365
3378
  return memoize(out)
3366
3379
  }
3367
3380
 
3381
+ /** @internal */
3382
+ export function applyToSelfOrLastLinkEncodingIdempotent(
3383
+ f: (ast: AST) => AST,
3384
+ options?: { readonly stopAt?: (link: Link) => boolean }
3385
+ ) {
3386
+ function out(ast: AST): AST {
3387
+ if (ast.encoding) {
3388
+ const last = ast.encoding[ast.encoding.length - 1]
3389
+ return options?.stopAt?.(last) ? ast : replaceEncoding(ast, updateLastLink(ast.encoding, out))
3390
+ }
3391
+ return f(ast)
3392
+ }
3393
+ return memoizeIdempotent(out)
3394
+ }
3395
+
3368
3396
  /** @internal */
3369
3397
  export function middlewareDecoding(
3370
3398
  ast: AST,
@@ -3432,42 +3460,34 @@ export function annotateKey<A extends AST>(ast: A, annotations: Schema.Annotatio
3432
3460
  return replaceContext(ast, context)
3433
3461
  }
3434
3462
 
3435
- const optionalKeyLastLink = applyToLastLink(optionalKey)
3436
-
3437
- /**
3438
- * Marks an AST node's property key as optional by setting
3439
- * {@link Context.isOptional} to `true`.
3440
- *
3441
- * **Details**
3442
- *
3443
- * Also propagates the optional flag through the last link of the encoding
3444
- * chain if present.
3445
- *
3446
- * @see {@link isOptional}
3447
- * @see {@link Context}
3448
- * @category transforming
3449
- * @since 4.0.0
3450
- */
3451
- export function optionalKey<A extends AST>(ast: A): A {
3463
+ /** @internal */
3464
+ export const optionalKey: <A extends AST>(ast: A) => A = memoizeIdempotent(<A extends AST>(ast: A): A => {
3452
3465
  const context = ast.context ?
3453
3466
  ast.context.isOptional === false ?
3454
3467
  new Context(true, ast.context.isMutable, ast.context.constructorDefault, ast.context.annotations) :
3455
3468
  ast.context :
3456
3469
  new Context(true, false)
3457
3470
  return optionalKeyLastLink(replaceContext(ast, context))
3458
- }
3471
+ })
3459
3472
 
3460
- const mutableKeyLastLink = applyToLastLink(mutableKey)
3473
+ const optionalKeyLastLink = applyToLastLink(optionalKey)
3474
+
3475
+ /** @internal */
3476
+ export const optional = memoize(<A extends AST>(ast: A): Union<A | Undefined> =>
3477
+ optionalKey(new Union([ast, undefined_], "anyOf"))
3478
+ )
3461
3479
 
3462
3480
  /** @internal */
3463
- export function mutableKey<A extends AST>(ast: A): A {
3481
+ export const mutableKey = memoizeIdempotent(<A extends AST>(ast: A): A => {
3464
3482
  const context = ast.context ?
3465
3483
  ast.context.isMutable === false ?
3466
3484
  new Context(ast.context.isOptional, true, ast.context.constructorDefault, ast.context.annotations) :
3467
3485
  ast.context :
3468
3486
  new Context(false, true)
3469
3487
  return mutableKeyLastLink(replaceContext(ast, context))
3470
- }
3488
+ })
3489
+
3490
+ const mutableKeyLastLink = applyToLastLink(mutableKey)
3471
3491
 
3472
3492
  /** @internal */
3473
3493
  export function withConstructorDefault<A extends AST>(
@@ -3616,7 +3636,7 @@ function extractStructuralChecks(checks: Checks): Checks | undefined {
3616
3636
  * @category transforming
3617
3637
  * @since 4.0.0
3618
3638
  */
3619
- export const toType = memoize(<A extends AST>(ast: A): A => {
3639
+ export const toType = memoizeIdempotent(<A extends AST>(ast: A): A => {
3620
3640
  if (ast.encoding) {
3621
3641
  return toType(replaceEncoding(ast, undefined))
3622
3642
  }
@@ -3663,7 +3683,7 @@ export const toType = memoize(<A extends AST>(ast: A): A => {
3663
3683
  * @category transforming
3664
3684
  * @since 4.0.0
3665
3685
  */
3666
- export const toEncoded = memoize((ast: AST): AST => {
3686
+ export const toEncoded = memoizeIdempotent((ast: AST): AST => {
3667
3687
  return toType(flip(ast))
3668
3688
  })
3669
3689
 
@@ -3807,7 +3827,7 @@ export const enumsToLiterals = memoize((ast: Enum): Union<Literal> => {
3807
3827
  )
3808
3828
  })
3809
3829
 
3810
- const parameterFromPropertyKey = applyToSelfOrLastLinkEncoding((ast) => {
3830
+ const parameterFromPropertyKey = applyToSelfOrLastLinkEncodingIdempotent((ast) => {
3811
3831
  switch (ast._tag) {
3812
3832
  default:
3813
3833
  return ast
@@ -3819,7 +3839,7 @@ const parameterFromPropertyKey = applyToSelfOrLastLinkEncoding((ast) => {
3819
3839
  })
3820
3840
 
3821
3841
  /** @internal */
3822
- export const parameterFromString = applyToSelfOrLastLinkEncoding((ast) => {
3842
+ export const parameterFromString = applyToSelfOrLastLinkEncodingIdempotent((ast) => {
3823
3843
  switch (ast._tag) {
3824
3844
  default:
3825
3845
  return ast
@@ -3831,7 +3851,7 @@ export const parameterFromString = applyToSelfOrLastLinkEncoding((ast) => {
3831
3851
  }
3832
3852
  })
3833
3853
 
3834
- const partFromString = applyToSelfOrLastLinkEncoding((ast) => {
3854
+ const partFromString = applyToSelfOrLastLinkEncodingIdempotent((ast) => {
3835
3855
  switch (ast._tag) {
3836
3856
  default:
3837
3857
  return ast
@@ -55,7 +55,13 @@ export declare namespace ToJsonSchema {
55
55
  }
56
56
 
57
57
  /**
58
- * JSON Schema compiler for a check.
58
+ * Compiles a check to a JSON Schema fragment.
59
+ *
60
+ * **Gotchas**
61
+ *
62
+ * Treat the input schemas as immutable. The returned value must be a valid JSON Schema object graph and must not be
63
+ * mutated after this function returns. Return a new object graph to produce different output during a later
64
+ * compilation.
59
65
  *
60
66
  * @category models
61
67
  * @since 4.0.0
@@ -728,7 +734,10 @@ export function toMultiDocument(document: Document): MultiDocument {
728
734
  *
729
735
  * **Gotchas**
730
736
  *
731
- * Opaque declarations are represented by an unconstrained JSON Schema. Check callback results are used directly, and exceptions raised by a callback pass through unchanged.
737
+ * Opaque declarations are represented by an unconstrained JSON Schema. Check callback results are used directly, and
738
+ * exceptions raised by a callback pass through unchanged. Callbacks must treat their input schemas as immutable. Each
739
+ * returned value must be a valid JSON Schema object graph and must not be mutated after the callback returns. Local
740
+ * definition references returned by callbacks are resolved together with compiler-generated references.
732
741
  *
733
742
  * @see {@link toJsonSchemaMultiDocument} for multiple roots sharing definitions
734
743
  *
@@ -751,7 +760,10 @@ export function toJsonSchemaDocument(
751
760
  *
752
761
  * **Gotchas**
753
762
  *
754
- * Every definition is compiled, including definitions that are not reachable from a root.
763
+ * Every definition is compiled, including definitions that are not reachable from a root. Check callbacks must treat
764
+ * their input schemas as immutable. Each returned value must be a valid JSON Schema object graph and must not be
765
+ * mutated after the callback returns. Local definition references returned by callbacks are resolved together with
766
+ * compiler-generated references.
755
767
  *
756
768
  * @see {@link toJsonSchemaDocument} for a single root
757
769
  *
package/src/ScopedRef.ts CHANGED
@@ -219,7 +219,9 @@ export const set: {
219
219
  Scope.provide(scope),
220
220
  Effect.tapCause((cause) => Scope.close(scope, Exit.failCause(cause)))
221
221
  )
222
- yield* Scope.close(self.backing.backing.ref.current[0], Exit.void)
222
+ yield* Scope.close(self.backing.backing.ref.current[0], Exit.void).pipe(
223
+ Effect.tapCause((cause) => Scope.close(scope, Exit.failCause(cause)))
224
+ )
223
225
  self.backing.backing.ref.current = [scope, value]
224
226
  },
225
227
  Effect.uninterruptible,
package/src/TxQueue.ts CHANGED
@@ -1700,7 +1700,7 @@ export const clear = <A, E>(self: TxEnqueue<A, E>): Effect.Effect<Array<A>, Excl
1700
1700
  */
1701
1701
  export const shutdown = <A, E>(self: TxEnqueue<A, E>): Effect.Effect<boolean> =>
1702
1702
  Effect.gen(function*() {
1703
- yield* Effect.ignore(clear(self))
1703
+ yield* Effect.ignoreCause(clear(self))
1704
1704
  return yield* interrupt(self)
1705
1705
  }).pipe(Effect.tx)
1706
1706
 
@@ -822,7 +822,7 @@ export const fiberJoin = <A, E>(self: Fiber.Fiber<A, E>): Effect.Effect<A, E> =>
822
822
  /** @internal */
823
823
  export const fiberJoinAll = <A extends Iterable<Fiber.Fiber<any, any>>>(self: A): Effect.Effect<
824
824
  Arr.ReadonlyArray.With<A, A extends Iterable<Fiber.Fiber<infer _A, infer _E>> ? _A : never>,
825
- A extends Fiber.Fiber<infer _A, infer _E> ? _E : never
825
+ A extends Iterable<Fiber.Fiber<infer _A, infer _E>> ? _E : never
826
826
  > =>
827
827
  callback((resume) => {
828
828
  const fibers = Array.from(self)
@@ -5862,11 +5862,8 @@ export const useSpan: {
5862
5862
  return withFiber((fiber) => {
5863
5863
  const span = makeSpanUnsafe(fiber, name, options)
5864
5864
  const clock = fiber.getRef(ClockRef)
5865
- return onExit(internalCall(() => evaluate(span)), (exit) =>
5866
- sync(() => {
5867
- if (span.status._tag === "Ended") return
5868
- span.end(clock.currentTimeNanosUnsafe(), exit)
5869
- }))
5865
+ const timingEnabled = fiber.getRef(TracerTimingEnabled)
5866
+ return onExit(internalCall(() => evaluate(span)), (exit) => endSpan(span, exit, clock, timingEnabled))
5870
5867
  })
5871
5868
  }
5872
5869
 
@@ -31,6 +31,7 @@ export const makeEventEmitter = (
31
31
  ): EventEmitter => {
32
32
  let lastStepIndex = -1
33
33
  let stepAttempt = 0
34
+ const emit = (event: Api.Event<any>) => effect.ignoreCause(onEvent(event))
34
35
  return {
35
36
  begin: effect.clockWith((clock) =>
36
37
  effect.suspend(() => {
@@ -47,7 +48,7 @@ export const makeEventEmitter = (
47
48
  startNanos: clock.monotonicTimeNanosUnsafe()
48
49
  }
49
50
  return effect.as(
50
- onEvent({
51
+ emit({
51
52
  _tag: "AttemptStart",
52
53
  attempt: state.attempt,
53
54
  stepAttempt: state.stepAttempt,
@@ -60,7 +61,7 @@ export const makeEventEmitter = (
60
61
  end: (state, exit) =>
61
62
  effect.clockWith((clock) => {
62
63
  const duration = Duration.nanos(clock.monotonicTimeNanosUnsafe() - state.startNanos)
63
- return onEvent(
64
+ return emit(
64
65
  exit._tag === "Success"
65
66
  ? {
66
67
  _tag: "AttemptSuccess",
@@ -94,7 +94,7 @@ export const make = <A, E, R>(options: {
94
94
  })
95
95
 
96
96
  const getState = <A, E>(self: RcRefImpl<A, E>) =>
97
- Effect.uninterruptibleMask((restore) => {
97
+ Effect.uninterruptibleMask(function loop(restore): Effect.Effect<State.Acquired<A>, E> {
98
98
  switch (self.state._tag) {
99
99
  case "Closed": {
100
100
  return Effect.interrupt
@@ -107,22 +107,30 @@ const getState = <A, E>(self: RcRefImpl<A, E>) =>
107
107
  }
108
108
  case "Empty": {
109
109
  const scope = Scope.makeUnsafe()
110
- return self.semaphore.withPermits(1)(
111
- restore(Effect.provideContext(
112
- self.acquire as Effect.Effect<A, E>,
113
- Context.add(self.context, Scope.Scope, scope)
114
- )).pipe(Effect.map((value) => {
115
- const state: State.Acquired<A> = {
116
- _tag: "Acquired",
117
- value,
118
- scope,
119
- fiber: undefined,
120
- refCount: 1,
121
- invalidated: false
110
+ return self.semaphore.withPermit(
111
+ Effect.suspend(() => {
112
+ if (self.state._tag !== "Empty") {
113
+ return loop(restore)
122
114
  }
123
- self.state = state
124
- return state
125
- }))
115
+ return restore(Effect.provideContext(
116
+ self.acquire as Effect.Effect<A, E>,
117
+ Context.add(self.context, Scope.Scope, scope)
118
+ )).pipe(
119
+ Effect.map((value) => {
120
+ const state: State.Acquired<A> = {
121
+ _tag: "Acquired",
122
+ value,
123
+ scope,
124
+ fiber: undefined,
125
+ refCount: 1,
126
+ invalidated: false
127
+ }
128
+ self.state = state
129
+ return state
130
+ }),
131
+ Effect.onExit((exit) => Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void)
132
+ )
133
+ })
126
134
  )
127
135
  }
128
136
  }
@@ -757,23 +757,26 @@ function translateJsonSchemaMultiDocument(
757
757
  case "boolean":
758
758
  return { _tag: "Boolean", checks: [] }
759
759
  case "array": {
760
+ const prefixItems = Array.isArray(schema.prefixItems) ? schema.prefixItems : undefined
760
761
  const minItems = typeof schema.minItems === "number" ? schema.minItems : 0
761
- const elements = Array.isArray(schema.prefixItems)
762
- ? schema.prefixItems.map((element, index) => ({
763
- isOptional: index + 1 > minItems,
764
- type: recur(element, [...path, "prefixItems", index])
765
- }))
766
- : []
767
- const rest = schema.items !== undefined
768
- ? [recur(schema.items, [...path, "items"])]
769
- : schema.prefixItems !== undefined && typeof schema.maxItems === "number"
770
- ? []
771
- : [unknown]
762
+ const elements = prefixItems?.map((element, index) => ({
763
+ isOptional: index + 1 > minItems,
764
+ type: recur(element, [...path, "prefixItems", index])
765
+ })) ?? []
766
+ const isTupleClosed = schema.items === false ||
767
+ (schema.items === undefined &&
768
+ prefixItems !== undefined &&
769
+ schema.maxItems === prefixItems.length)
770
+ const isMaxItemsRedundant = isTupleClosed &&
771
+ typeof schema.maxItems === "number" &&
772
+ schema.maxItems >= elements.length
772
773
  return {
773
774
  _tag: "Arrays",
774
775
  elements,
775
- rest,
776
- checks: collectArrayChecks(schema)
776
+ rest: isTupleClosed
777
+ ? []
778
+ : [schema.items === undefined ? unknown : recur(schema.items, [...path, "items"])],
779
+ checks: collectArrayChecks(schema, isMaxItemsRedundant)
777
780
  }
778
781
  }
779
782
  case "object":
@@ -808,10 +811,12 @@ function translateJsonSchemaMultiDocument(
808
811
  return checks
809
812
  }
810
813
 
811
- function collectArrayChecks(schema: JsonSchema.JsonSchema): Array<Check> {
814
+ function collectArrayChecks(schema: JsonSchema.JsonSchema, isMaxItemsRedundant: boolean): Array<Check> {
812
815
  const checks: Array<Check> = []
813
816
  if (schema.prefixItems === undefined) {
814
817
  addNumberCheck(checks, schema.minItems, "effect/schema/isMinLength", "minLength")
818
+ }
819
+ if (!isMaxItemsRedundant) {
815
820
  addNumberCheck(checks, schema.maxItems, "effect/schema/isMaxLength", "maxLength")
816
821
  }
817
822
  if (schema.uniqueItems === true) {
@@ -34,20 +34,6 @@ type LazyOption<T> = (
34
34
  recursionStack: RecursionStack
35
35
  ) => FastCheck.Arbitrary<T> | undefined
36
36
 
37
- export interface MutableReport {
38
- readonly warnings: Array<Schema.Annotations.ToArbitrary.Warning>
39
- }
40
-
41
- /** @internal */
42
- export function makeReport(): MutableReport {
43
- return { warnings: [] }
44
- }
45
-
46
- /** @internal */
47
- export function toReport(report: MutableReport): Schema.Annotations.ToArbitrary.Report {
48
- return { warnings: report.warnings.slice() }
49
- }
50
-
51
37
  function arbitraryError(what: string) {
52
38
  return new Error(`Unable to derive an arbitrary for ${what}`)
53
39
  }
@@ -394,64 +380,6 @@ function finiteNumberContext(ctx: Context): Context {
394
380
  }
395
381
  }
396
382
 
397
- function reportChecks(report: MutableReport, checks: SchemaAST.Checks | undefined, path: ReadonlyArray<PropertyKey>) {
398
- function visit(check: SchemaAST.Check<any>, covered: boolean) {
399
- const arbitrary = check.annotations?.arbitrary
400
- const nextCovered = covered || arbitrary?.constraint !== undefined || arbitrary?.candidate !== undefined
401
- if (check._tag !== "Filter") {
402
- for (const child of check.checks) {
403
- visit(child, nextCovered)
404
- }
405
- } else if (!nextCovered) {
406
- const description = check.annotations?.representation?.id ?? check.annotations?.identifier ??
407
- check.annotations?.expected
408
- report.warnings.push({ _tag: "OpaqueFilter", path, ...(description === undefined ? {} : { description }) })
409
- }
410
- }
411
- checks?.forEach((check) => visit(check, false))
412
- }
413
-
414
- /** @internal */
415
- export function collectReport(ast: SchemaAST.AST, report: MutableReport) {
416
- const stack = new WeakSet<SchemaAST.AST>()
417
- function visit(ast: SchemaAST.AST, path: ReadonlyArray<PropertyKey>) {
418
- if (stack.has(ast)) {
419
- return
420
- }
421
- stack.add(ast)
422
- reportChecks(report, ast.checks, path)
423
- switch (ast._tag) {
424
- case "Declaration":
425
- ast.typeParameters.forEach((tp) => visit(tp, path))
426
- break
427
- case "Arrays": {
428
- for (const [i, type] of [...ast.elements, ...ast.rest].entries()) {
429
- visit(type, [...path, i])
430
- }
431
- break
432
- }
433
- case "Objects":
434
- ast.propertySignatures.forEach((ps) => visit(ps.type, [...path, ps.name]))
435
- ast.indexSignatures.forEach((is) => {
436
- visit(is.parameter, path)
437
- visit(is.type, path)
438
- })
439
- break
440
- case "Union":
441
- ast.types.forEach((type) => visit(type, path))
442
- break
443
- case "TemplateLiteral":
444
- ast.parts.forEach((part, i) => visit(SchemaAST.toEncoded(part), [...path, i]))
445
- break
446
- case "Suspend":
447
- visit(ast.thunk(), path)
448
- break
449
- }
450
- stack.delete(ast)
451
- }
452
- visit(ast, [])
453
- }
454
-
455
383
  function applyCandidates(
456
384
  fc: typeof FastCheck,
457
385
  ctx: Context,
@@ -1,7 +1,8 @@
1
1
  import * as Arr from "../../Array.ts"
2
2
  import * as Equal from "../../Equal.ts"
3
- import { escapeToken } from "../../JsonPointer.ts"
3
+ import { escapeToken, unescapeToken } from "../../JsonPointer.ts"
4
4
  import type * as JsonSchema from "../../JsonSchema.ts"
5
+ import { rewriteRefs } from "../../JsonSchema.ts"
5
6
  import * as RegEx from "../../RegExp.ts"
6
7
  import type * as Schema from "../../Schema.ts"
7
8
  import * as SchemaAST from "../../SchemaAST.ts"
@@ -21,6 +22,11 @@ const jsonSchemaAnnotationExcludedKeys = new Set([
21
22
  ...InternalAnnotations.jsonSchemaAnnotationKeys
22
23
  ])
23
24
 
25
+ /** @internal */
26
+ export const toRepresentationOptions = {
27
+ isAnonymousReferenceAllowed: (ast: SchemaAST.AST): boolean => !SchemaAST.isDeclaration(ast)
28
+ }
29
+
24
30
  function collectJsonSchemaAnnotations(
25
31
  annotations: Schema.Annotations.Annotations | undefined,
26
32
  options: Schema.ToJsonSchemaOptions | undefined
@@ -142,22 +148,26 @@ function compileJsonSchema(
142
148
  references: SchemaRepresentation.References,
143
149
  options: Schema.ToJsonSchemaOptions | undefined
144
150
  ): JsonSchema.MultiDocument<"draft-2020-12"> {
145
- const definitions: Record<string, JsonSchema.JsonSchema> = {}
146
151
  // null = compiling, string = canonical key, object = compiled schema
147
152
  const definitionStates = new Map<string, JsonSchema.JsonSchema | string | null>()
148
153
  const compiledRepresentations = new WeakMap<SchemaRepresentation.Representation, JsonSchema.JsonSchema>()
149
154
  const fallbackDefinitions = new Map<string, Array<string>>()
155
+ let hasAliases = false
150
156
  const referenceKeys = Object.keys(references)
151
157
  for (const key of referenceKeys) {
152
158
  compileDefinition(key, ["references", key])
153
159
  }
160
+ const schemas = Arr.map(
161
+ representations,
162
+ (representation, index) => finalizeJsonSchema(recur(representation, rootPaths[index]))
163
+ )
164
+ const definitions: Record<string, JsonSchema.JsonSchema> = {}
154
165
  for (const key of referenceKeys) {
155
166
  const compiled = definitionStates.get(key)!
156
167
  if (typeof compiled !== "string") {
157
- InternalRecord.assignProperty(definitions, key, compiled)
168
+ InternalRecord.assignProperty(definitions, key, finalizeJsonSchema(compiled))
158
169
  }
159
170
  }
160
- const schemas = Arr.map(representations, (representation, index) => recur(representation, rootPaths[index]))
161
171
  return { dialect: "draft-2020-12", schemas, definitions }
162
172
 
163
173
  function compileDefinition(key: string, path: Path): string {
@@ -179,6 +189,7 @@ function compileJsonSchema(
179
189
  if (candidates === undefined) fallbackDefinitions.set(fallback, [key])
180
190
  else candidates.push(key)
181
191
  } else {
192
+ hasAliases = true
182
193
  definitionStates.set(key, match)
183
194
  return match
184
195
  }
@@ -187,6 +198,15 @@ function compileJsonSchema(
187
198
  return key
188
199
  }
189
200
 
201
+ function finalizeJsonSchema(schema: JsonSchema.JsonSchema): JsonSchema.JsonSchema {
202
+ if (!hasAliases) return schema
203
+ return rewriteRefs(schema, ($ref) =>
204
+ $ref.replace(/^#\/\$defs\/([^/]*)/, (match, token) => {
205
+ const canonical = definitionStates.get(unescapeToken(token))
206
+ return typeof canonical === "string" ? `#/$defs/${escapeToken(canonical)}` : match
207
+ }))
208
+ }
209
+
190
210
  function getIdentifierFallback(
191
211
  representation: SchemaRepresentation.Representation
192
212
  ): string | undefined {