effect 4.0.0-beta.105 → 4.0.0-beta.107
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.
- package/dist/Channel.d.ts +26 -0
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +47 -0
- package/dist/Channel.js.map +1 -1
- package/dist/Config.js +3 -3
- package/dist/Config.js.map +1 -1
- package/dist/ConfigProvider.d.ts +35 -3
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +41 -8
- package/dist/ConfigProvider.js.map +1 -1
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +13 -1
- package/dist/Duration.js.map +1 -1
- package/dist/Fiber.d.ts +1 -1
- package/dist/Fiber.d.ts.map +1 -1
- package/dist/Function.d.ts +26 -0
- package/dist/Function.d.ts.map +1 -1
- package/dist/Function.js +36 -0
- package/dist/Function.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +34 -17
- package/dist/JsonSchema.js.map +1 -1
- package/dist/Schema.d.ts +32 -84
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +58 -54
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +2 -17
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +39 -28
- package/dist/SchemaAST.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +44 -7
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +15 -4
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/ScopedRef.d.ts.map +1 -1
- package/dist/ScopedRef.js +1 -1
- package/dist/ScopedRef.js.map +1 -1
- package/dist/Stream.d.ts +10 -0
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +12 -23
- package/dist/Stream.js.map +1 -1
- package/dist/TxQueue.js +1 -1
- package/dist/TxQueue.js.map +1 -1
- package/dist/internal/effect.js +2 -4
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/executionPlan.js +3 -2
- package/dist/internal/executionPlan.js.map +1 -1
- package/dist/internal/rcRef.js +18 -13
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +32 -16
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toArbitrary.d.ts +1 -4
- package/dist/internal/schema/toArbitrary.d.ts.map +1 -1
- package/dist/internal/schema/toArbitrary.js +0 -74
- package/dist/internal/schema/toArbitrary.js.map +1 -1
- package/dist/internal/schema/toJsonSchemaDocument.js +18 -4
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toRepresentation.js +45 -43
- package/dist/internal/schema/toRepresentation.js.map +1 -1
- package/dist/testing/TestSchema.js +2 -2
- package/dist/testing/TestSchema.js.map +1 -1
- package/dist/unstable/ai/McpSchema.d.ts +128 -128
- package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.js +23 -11
- package/dist/unstable/ai/McpServer.js.map +1 -1
- package/dist/unstable/ai/Prompt.d.ts +4 -3
- package/dist/unstable/ai/Prompt.d.ts.map +1 -1
- package/dist/unstable/ai/Prompt.js +38 -13
- package/dist/unstable/ai/Prompt.js.map +1 -1
- package/dist/unstable/cli/Argument.d.ts +6 -3
- package/dist/unstable/cli/Argument.d.ts.map +1 -1
- package/dist/unstable/cli/Argument.js +2 -1
- package/dist/unstable/cli/Argument.js.map +1 -1
- package/dist/unstable/cli/CliError.d.ts +20 -1
- package/dist/unstable/cli/CliError.d.ts.map +1 -1
- package/dist/unstable/cli/CliError.js +26 -2
- package/dist/unstable/cli/CliError.js.map +1 -1
- package/dist/unstable/cli/Command.d.ts +23 -0
- package/dist/unstable/cli/Command.d.ts.map +1 -1
- package/dist/unstable/cli/Command.js +23 -4
- package/dist/unstable/cli/Command.js.map +1 -1
- package/dist/unstable/cluster/Entity.d.ts +2 -2
- package/dist/unstable/cluster/Entity.d.ts.map +1 -1
- package/dist/unstable/cluster/Entity.js.map +1 -1
- package/dist/unstable/cluster/EntityProxy.d.ts +3 -3
- package/dist/unstable/cluster/EntityProxy.d.ts.map +1 -1
- package/dist/unstable/cluster/EntityProxy.js +4 -3
- package/dist/unstable/cluster/EntityProxy.js.map +1 -1
- package/dist/unstable/cluster/Sharding.d.ts +2 -2
- package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
- package/dist/unstable/cluster/Sharding.js +14 -5
- package/dist/unstable/cluster/Sharding.js.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.d.ts +6 -5
- package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.js +14 -12
- package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.d.ts +3 -3
- package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.js +2 -3
- package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
- package/dist/unstable/eventlog/EventLogRemote.js +4 -4
- package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
- package/dist/unstable/eventlog/EventLogServerEncrypted.js +3 -2
- package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/unstable/http/HttpServerRequest.js +7 -1
- package/dist/unstable/http/HttpServerRequest.js.map +1 -1
- package/dist/unstable/http/Multipart.d.ts +2 -0
- package/dist/unstable/http/Multipart.d.ts.map +1 -1
- package/dist/unstable/http/Multipart.js +4 -12
- package/dist/unstable/http/Multipart.js.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/headers.d.ts.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/headers.js +16 -2
- package/dist/unstable/http/MultipartParser/internal/headers.js.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.js +23 -8
- package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
- package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
- package/dist/unstable/httpapi/OpenApi.js +1 -1
- package/dist/unstable/httpapi/OpenApi.js.map +1 -1
- package/dist/unstable/process/ChildProcess.d.ts +8 -0
- package/dist/unstable/process/ChildProcess.d.ts.map +1 -1
- package/dist/unstable/process/ChildProcess.js.map +1 -1
- package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcClient.js +5 -0
- package/dist/unstable/rpc/RpcClient.js.map +1 -1
- package/dist/unstable/sql/Migrator.d.ts +9 -1
- package/dist/unstable/sql/Migrator.d.ts.map +1 -1
- package/dist/unstable/sql/Migrator.js +15 -2
- package/dist/unstable/sql/Migrator.js.map +1 -1
- package/dist/unstable/sql/SqlResolver.d.ts.map +1 -1
- package/dist/unstable/sql/SqlResolver.js +20 -16
- package/dist/unstable/sql/SqlResolver.js.map +1 -1
- package/dist/unstable/workers/Worker.d.ts.map +1 -1
- package/dist/unstable/workers/Worker.js +11 -12
- package/dist/unstable/workers/Worker.js.map +1 -1
- package/package.json +1 -1
- package/src/Channel.ts +58 -0
- package/src/Config.ts +3 -3
- package/src/ConfigProvider.ts +43 -8
- package/src/Duration.ts +12 -1
- package/src/Fiber.ts +1 -1
- package/src/Function.ts +37 -0
- package/src/JsonSchema.ts +43 -42
- package/src/Schema.ts +68 -146
- package/src/SchemaAST.ts +50 -30
- package/src/SchemaRepresentation.ts +44 -7
- package/src/ScopedRef.ts +3 -1
- package/src/Stream.ts +12 -30
- package/src/TxQueue.ts +1 -1
- package/src/internal/effect.ts +3 -6
- package/src/internal/executionPlan.ts +3 -2
- package/src/internal/rcRef.ts +24 -16
- package/src/internal/schema/fromJsonSchemaDocument.ts +36 -18
- package/src/internal/schema/toArbitrary.ts +0 -72
- package/src/internal/schema/toJsonSchemaDocument.ts +24 -4
- package/src/internal/schema/toRepresentation.ts +66 -48
- package/src/testing/TestSchema.ts +2 -2
- package/src/unstable/ai/McpServer.ts +23 -11
- package/src/unstable/ai/Prompt.ts +37 -15
- package/src/unstable/cli/Argument.ts +6 -3
- package/src/unstable/cli/CliError.ts +28 -2
- package/src/unstable/cli/Command.ts +44 -4
- package/src/unstable/cluster/Entity.ts +7 -2
- package/src/unstable/cluster/EntityProxy.ts +12 -5
- package/src/unstable/cluster/Sharding.ts +29 -17
- package/src/unstable/eventlog/EventLogEncryption.ts +15 -16
- package/src/unstable/eventlog/EventLogMessage.ts +2 -3
- package/src/unstable/eventlog/EventLogRemote.ts +4 -4
- package/src/unstable/eventlog/EventLogServerEncrypted.ts +2 -2
- package/src/unstable/http/HttpServerRequest.ts +8 -3
- package/src/unstable/http/Multipart.ts +4 -13
- package/src/unstable/http/MultipartParser/internal/headers.ts +16 -387
- package/src/unstable/http/MultipartParser/internal/multipart.ts +25 -8
- package/src/unstable/httpapi/OpenApi.ts +4 -1
- package/src/unstable/process/ChildProcess.ts +8 -0
- package/src/unstable/rpc/RpcClient.ts +5 -0
- package/src/unstable/sql/Migrator.ts +22 -9
- package/src/unstable/sql/SqlResolver.ts +28 -20
- package/src/unstable/workers/Worker.ts +13 -14
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/Stream.ts
CHANGED
|
@@ -19270,39 +19270,16 @@ export const mkString = <E, R>(self: Stream<string, E, R>): Effect.Effect<string
|
|
|
19270
19270
|
* await Effect.runPromise(program) // => [1, 2, 3, 4]
|
|
19271
19271
|
* ```
|
|
19272
19272
|
*
|
|
19273
|
+
* **Gotchas**
|
|
19274
|
+
*
|
|
19275
|
+
* This materializes the full content in memory. The source stream must not
|
|
19276
|
+
* reuse or mutate emitted buffers, which are retained until collection completes.
|
|
19277
|
+
*
|
|
19273
19278
|
* @category destructors
|
|
19274
19279
|
* @since 4.0.0
|
|
19275
19280
|
*/
|
|
19276
19281
|
export const mkArrayBuffer = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effect<ArrayBuffer, E, R> =>
|
|
19277
|
-
Effect.map(
|
|
19278
|
-
Channel.runFold(
|
|
19279
|
-
self.channel,
|
|
19280
|
-
(): {
|
|
19281
|
-
bytes: number
|
|
19282
|
-
readonly arrays: Array<Uint8Array>
|
|
19283
|
-
} => ({
|
|
19284
|
-
bytes: 0,
|
|
19285
|
-
arrays: []
|
|
19286
|
-
}),
|
|
19287
|
-
(acc, chunk) => {
|
|
19288
|
-
for (let i = 0; i < chunk.length; i++) {
|
|
19289
|
-
acc.bytes += chunk[i].length
|
|
19290
|
-
acc.arrays.push(chunk[i])
|
|
19291
|
-
}
|
|
19292
|
-
return acc
|
|
19293
|
-
}
|
|
19294
|
-
),
|
|
19295
|
-
({ arrays, bytes }) => {
|
|
19296
|
-
const result = new Uint8Array(bytes)
|
|
19297
|
-
let offset = 0
|
|
19298
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
19299
|
-
const array = arrays[i]
|
|
19300
|
-
result.set(array, offset)
|
|
19301
|
-
offset += array.length
|
|
19302
|
-
}
|
|
19303
|
-
return result.buffer
|
|
19304
|
-
}
|
|
19305
|
-
)
|
|
19282
|
+
Effect.map(Channel.mkUint8Array(self.channel), (bytes) => bytes.buffer)
|
|
19306
19283
|
|
|
19307
19284
|
/**
|
|
19308
19285
|
* Concatenates the stream's `Uint8Array` chunks into a single `Uint8Array`.
|
|
@@ -19321,11 +19298,16 @@ export const mkArrayBuffer = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effe
|
|
|
19321
19298
|
* await Effect.runPromise(program)
|
|
19322
19299
|
* ```
|
|
19323
19300
|
*
|
|
19301
|
+
* **Gotchas**
|
|
19302
|
+
*
|
|
19303
|
+
* This materializes the full content in memory. The source stream must not
|
|
19304
|
+
* reuse or mutate emitted buffers, which are retained until collection completes.
|
|
19305
|
+
*
|
|
19324
19306
|
* @category destructors
|
|
19325
19307
|
* @since 4.0.0
|
|
19326
19308
|
*/
|
|
19327
19309
|
export const mkUint8Array = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effect<Uint8Array, E, R> =>
|
|
19328
|
-
|
|
19310
|
+
Channel.mkUint8Array(self.channel)
|
|
19329
19311
|
|
|
19330
19312
|
/**
|
|
19331
19313
|
* Converts the stream to a `ReadableStream` using the provided services.
|
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.
|
|
1703
|
+
yield* Effect.ignoreCause(clear(self))
|
|
1704
1704
|
return yield* interrupt(self)
|
|
1705
1705
|
}).pipe(Effect.tx)
|
|
1706
1706
|
|
package/src/internal/effect.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
5866
|
-
|
|
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
|
-
|
|
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
|
|
64
|
+
return emit(
|
|
64
65
|
exit._tag === "Success"
|
|
65
66
|
? {
|
|
66
67
|
_tag: "AttemptSuccess",
|
package/src/internal/rcRef.ts
CHANGED
|
@@ -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.
|
|
111
|
-
|
|
112
|
-
self.
|
|
113
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
}
|
|
@@ -743,7 +743,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
743
743
|
case "string":
|
|
744
744
|
return {
|
|
745
745
|
_tag: "String",
|
|
746
|
-
checks: collectStringChecks(schema)
|
|
746
|
+
checks: collectStringChecks(schema, path)
|
|
747
747
|
}
|
|
748
748
|
case "number":
|
|
749
749
|
case "integer":
|
|
@@ -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 =
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
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":
|
|
@@ -788,12 +791,23 @@ function translateJsonSchemaMultiDocument(
|
|
|
788
791
|
}
|
|
789
792
|
}
|
|
790
793
|
|
|
791
|
-
function
|
|
794
|
+
function importPatternChecks(pattern: string, path: Path): Array<Check> {
|
|
795
|
+
switch (options?.patterns ?? "error") {
|
|
796
|
+
case "error":
|
|
797
|
+
throw errorWithPath(`Pattern encountered while patterns is set to "error"`, path)
|
|
798
|
+
case "ignore":
|
|
799
|
+
return []
|
|
800
|
+
case "apply":
|
|
801
|
+
return [jsonSchemaFilter("effect/schema/isPattern", { source: pattern, flags: "" })]
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function collectStringChecks(schema: JsonSchema.JsonSchema, path: Path): Array<Check> {
|
|
792
806
|
const checks: Array<Check> = []
|
|
793
807
|
addNumberCheck(checks, schema.minLength, "effect/schema/isMinLength", "minLength")
|
|
794
808
|
addNumberCheck(checks, schema.maxLength, "effect/schema/isMaxLength", "maxLength")
|
|
795
809
|
if (typeof schema.pattern === "string") {
|
|
796
|
-
checks.push(
|
|
810
|
+
checks.push(...importPatternChecks(schema.pattern, [...path, "pattern"]))
|
|
797
811
|
}
|
|
798
812
|
return checks
|
|
799
813
|
}
|
|
@@ -808,10 +822,12 @@ function translateJsonSchemaMultiDocument(
|
|
|
808
822
|
return checks
|
|
809
823
|
}
|
|
810
824
|
|
|
811
|
-
function collectArrayChecks(schema: JsonSchema.JsonSchema): Array<Check> {
|
|
825
|
+
function collectArrayChecks(schema: JsonSchema.JsonSchema, isMaxItemsRedundant: boolean): Array<Check> {
|
|
812
826
|
const checks: Array<Check> = []
|
|
813
827
|
if (schema.prefixItems === undefined) {
|
|
814
828
|
addNumberCheck(checks, schema.minItems, "effect/schema/isMinLength", "minLength")
|
|
829
|
+
}
|
|
830
|
+
if (!isMaxItemsRedundant) {
|
|
815
831
|
addNumberCheck(checks, schema.maxItems, "effect/schema/isMaxLength", "maxLength")
|
|
816
832
|
}
|
|
817
833
|
if (schema.uniqueItems === true) {
|
|
@@ -851,10 +867,12 @@ function translateJsonSchemaMultiDocument(
|
|
|
851
867
|
!Array.isArray(schema.patternProperties)
|
|
852
868
|
) {
|
|
853
869
|
for (const [pattern, value] of Object.entries(schema.patternProperties)) {
|
|
870
|
+
const checks = importPatternChecks(pattern, [...path, "patternProperties", pattern])
|
|
871
|
+
if (checks.length === 0) return [{ parameter: string, type: unknown }]
|
|
854
872
|
signatures.push({
|
|
855
873
|
parameter: {
|
|
856
874
|
_tag: "String",
|
|
857
|
-
checks
|
|
875
|
+
checks
|
|
858
876
|
},
|
|
859
877
|
type: recur(value, [...path, "patternProperties", pattern])
|
|
860
878
|
})
|
|
@@ -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 {
|
|
@@ -7,17 +7,24 @@ import * as InternalAnnotations from "./annotations.ts"
|
|
|
7
7
|
|
|
8
8
|
/** @internal */
|
|
9
9
|
export function toRepresentation(
|
|
10
|
-
ast: SchemaAST.AST
|
|
10
|
+
ast: SchemaAST.AST,
|
|
11
|
+
options?: Options
|
|
11
12
|
): SchemaRepresentation.Document {
|
|
12
|
-
const { references, representations } = toRepresentations([ast])
|
|
13
|
+
const { references, representations } = toRepresentations([ast], options)
|
|
13
14
|
return { representation: representations[0], references }
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/** @internal */
|
|
17
18
|
export function toRepresentations(
|
|
18
|
-
asts: readonly [SchemaAST.AST, ...Array<SchemaAST.AST>]
|
|
19
|
+
asts: readonly [SchemaAST.AST, ...Array<SchemaAST.AST>],
|
|
20
|
+
options?: Options
|
|
19
21
|
): SchemaRepresentation.MultiDocument {
|
|
20
|
-
return fromASTs(asts)
|
|
22
|
+
return fromASTs(asts, options)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
export interface Options {
|
|
27
|
+
readonly isAnonymousReferenceAllowed?: ((ast: SchemaAST.AST) => boolean) | undefined
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
type CheckRepresentationAnnotation = SchemaRepresentation.CheckRepresentationAnnotation<
|
|
@@ -28,11 +35,49 @@ function annotationsField<A>(annotations: A | undefined): { readonly annotations
|
|
|
28
35
|
return annotations === undefined ? undefined : { annotations }
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
function hasShareableStructure(
|
|
39
|
+
ast: SchemaAST.AST,
|
|
40
|
+
isAnonymousReferenceAllowed: Options["isAnonymousReferenceAllowed"]
|
|
41
|
+
): boolean {
|
|
42
|
+
if (isAnonymousReferenceAllowed?.(ast) === false) return false
|
|
43
|
+
switch (ast._tag) {
|
|
44
|
+
case "Arrays":
|
|
45
|
+
case "Objects":
|
|
46
|
+
case "Suspend":
|
|
47
|
+
return true
|
|
48
|
+
case "Declaration":
|
|
49
|
+
return true
|
|
50
|
+
case "Union":
|
|
51
|
+
return ast.types.some((ast) => hasShareableStructure(ast, isAnonymousReferenceAllowed))
|
|
52
|
+
default:
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isWorthReferencing(bodyCost: number, occurrences: number): boolean {
|
|
58
|
+
return occurrences * bodyCost > bodyCost + occurrences + 1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isAnonymousReferenceEligible(
|
|
62
|
+
ast: SchemaAST.AST,
|
|
63
|
+
occurrences: number,
|
|
64
|
+
isAnonymousReferenceAllowed: Options["isAnonymousReferenceAllowed"]
|
|
65
|
+
): boolean {
|
|
66
|
+
if (isAnonymousReferenceAllowed?.(ast) === false) return false
|
|
67
|
+
if (hasShareableStructure(ast, isAnonymousReferenceAllowed)) return true
|
|
68
|
+
switch (ast._tag) {
|
|
69
|
+
case "Union":
|
|
70
|
+
return isWorthReferencing(ast.types.length + 1, occurrences)
|
|
71
|
+
case "Enum":
|
|
72
|
+
return isWorthReferencing(ast.enums.length + 1, occurrences)
|
|
73
|
+
case "TemplateLiteral":
|
|
74
|
+
return isWorthReferencing(ast.parts.length + 1, occurrences)
|
|
75
|
+
case "Literal":
|
|
76
|
+
return typeof ast.literal === "string" &&
|
|
77
|
+
isWorthReferencing(ast.literal.length / 32 + 1, occurrences)
|
|
78
|
+
default:
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
36
81
|
}
|
|
37
82
|
|
|
38
83
|
interface ReferenceIdentifier {
|
|
@@ -54,17 +99,15 @@ function resolveReferenceIdentifier(
|
|
|
54
99
|
}
|
|
55
100
|
|
|
56
101
|
function fromASTs(
|
|
57
|
-
asts: readonly [SchemaAST.AST, ...Array<SchemaAST.AST>]
|
|
102
|
+
asts: readonly [SchemaAST.AST, ...Array<SchemaAST.AST>],
|
|
103
|
+
options: Options | undefined
|
|
58
104
|
): SchemaRepresentation.MultiDocument {
|
|
59
105
|
const references: Record<string, SchemaRepresentation.Representation> = {}
|
|
60
106
|
const anonymousReferences = new Map<SchemaAST.AST, string>()
|
|
61
107
|
const referenceOwners = new Map<string, SchemaAST.AST>()
|
|
62
|
-
const valueIds = new Map<unknown, number>()
|
|
63
|
-
const canonicalByKey = new Map<string, SchemaAST.AST>()
|
|
64
|
-
let nextValueId = 0
|
|
65
108
|
const buildingReferences = new Set<string>()
|
|
66
109
|
const visiting = new Set<SchemaAST.AST>()
|
|
67
|
-
const
|
|
110
|
+
const occurrences = new Map<SchemaAST.AST, number>()
|
|
68
111
|
const shared = new Set<SchemaAST.AST>()
|
|
69
112
|
|
|
70
113
|
for (const ast of asts) visit(ast)
|
|
@@ -84,35 +127,6 @@ function fromASTs(
|
|
|
84
127
|
return candidate
|
|
85
128
|
}
|
|
86
129
|
|
|
87
|
-
function getValueId(value: unknown): number {
|
|
88
|
-
if (typeof value === "number" && globalThis.Number.isNaN(value)) {
|
|
89
|
-
return nextValueId++
|
|
90
|
-
}
|
|
91
|
-
const found = valueIds.get(value)
|
|
92
|
-
if (found !== undefined) return found
|
|
93
|
-
const id = nextValueId++
|
|
94
|
-
valueIds.set(value, id)
|
|
95
|
-
return id
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function getIdentityKey(ast: SchemaAST.AST): string {
|
|
99
|
-
let identity = ast._tag
|
|
100
|
-
for (const [key, value] of Object.entries(ast)) {
|
|
101
|
-
if (key !== "_tag" && key !== "context") identity += `:${getValueId(value)}`
|
|
102
|
-
}
|
|
103
|
-
return identity
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function getCanonicalAST(ast: SchemaAST.AST): SchemaAST.AST {
|
|
107
|
-
const key = getIdentityKey(ast)
|
|
108
|
-
const canonical = canonicalByKey.get(key)
|
|
109
|
-
if (canonical === undefined) {
|
|
110
|
-
canonicalByKey.set(key, ast)
|
|
111
|
-
return ast
|
|
112
|
-
}
|
|
113
|
-
return canonical
|
|
114
|
-
}
|
|
115
|
-
|
|
116
130
|
function annotateReference(
|
|
117
131
|
ast: SchemaAST.AST,
|
|
118
132
|
referenceIdentifier: ReferenceIdentifier,
|
|
@@ -143,12 +157,16 @@ function fromASTs(
|
|
|
143
157
|
|
|
144
158
|
function visit(input: SchemaAST.AST): void {
|
|
145
159
|
const ast = SchemaAST.getLastEncoding(input)
|
|
146
|
-
const owner =
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
const owner = SchemaAST.getContextOwner(ast)
|
|
161
|
+
const count = (occurrences.get(owner) ?? 0) + 1
|
|
162
|
+
occurrences.set(owner, count)
|
|
163
|
+
if (count > 1) {
|
|
164
|
+
if (
|
|
165
|
+
!shared.has(owner) &&
|
|
166
|
+
isAnonymousReferenceEligible(owner, count, options?.isAnonymousReferenceAllowed)
|
|
167
|
+
) shared.add(owner)
|
|
149
168
|
return
|
|
150
169
|
}
|
|
151
|
-
visited.add(owner)
|
|
152
170
|
visitChecks(ast.checks)
|
|
153
171
|
switch (ast._tag) {
|
|
154
172
|
case "Declaration":
|
|
@@ -178,7 +196,7 @@ function fromASTs(
|
|
|
178
196
|
|
|
179
197
|
function recur(input: SchemaAST.AST): SchemaRepresentation.Representation {
|
|
180
198
|
const ast = SchemaAST.getLastEncoding(input)
|
|
181
|
-
const owner =
|
|
199
|
+
const owner = SchemaAST.getContextOwner(ast)
|
|
182
200
|
const referenceIdentifier = resolveReferenceIdentifier(input, ast)
|
|
183
201
|
if (referenceIdentifier !== undefined) {
|
|
184
202
|
const reference = getReference(referenceIdentifier.identifier, owner)
|
|
@@ -172,7 +172,7 @@ export class Asserts<S extends Schema.Constraint> {
|
|
|
172
172
|
}) {
|
|
173
173
|
const decodeUnknownEffect = SchemaParser.decodeUnknownEffect(this.schema)
|
|
174
174
|
const encodeEffect = SchemaParser.encodeEffect(this.schema)
|
|
175
|
-
const arbitrary = Schema.toArbitrary(this.schema)
|
|
175
|
+
const arbitrary = Schema.toArbitrary(this.schema)(FastCheck)
|
|
176
176
|
return FastCheck.assert(
|
|
177
177
|
FastCheck.asyncProperty(arbitrary, async (t) => {
|
|
178
178
|
const r = await Effect.runPromise(
|
|
@@ -280,7 +280,7 @@ export class Asserts<S extends Schema.Constraint> {
|
|
|
280
280
|
}) {
|
|
281
281
|
const params = options?.params
|
|
282
282
|
const is = Schema.is(schema)
|
|
283
|
-
const arb = Schema.toArbitrary(schema)
|
|
283
|
+
const arb = Schema.toArbitrary(schema)(FastCheck)
|
|
284
284
|
FastCheck.assert(FastCheck.property(arb, (a) => is(a)), { numRuns: 20, ...params })
|
|
285
285
|
}
|
|
286
286
|
}
|