effect 4.0.0-rc.113 → 4.0.0-rc.114
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/Effect.d.ts +268 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +33 -2
- package/dist/Effect.js.map +1 -1
- package/dist/Effectable.d.ts +1 -2
- package/dist/Effectable.d.ts.map +1 -1
- package/dist/Effectable.js.map +1 -1
- package/dist/FileSystem.d.ts +3 -4
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js.map +1 -1
- package/dist/Match.d.ts +1 -14
- package/dist/Match.d.ts.map +1 -1
- package/dist/Schema.d.ts +4 -4
- package/dist/Schema.d.ts.map +1 -1
- package/dist/SchemaRepresentation.d.ts +39 -24
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +37 -23
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/internal/arbitrary/schema.js.map +1 -1
- package/dist/internal/effect.js +16 -8
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +221 -230
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toCodeDocument.js +4 -4
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/unstable/cli/Prompt.d.ts +6 -2
- package/dist/unstable/cli/Prompt.d.ts.map +1 -1
- package/dist/unstable/cli/Prompt.js +8 -7
- package/dist/unstable/cli/Prompt.js.map +1 -1
- package/dist/unstable/cli/internal/command.d.ts +1 -6
- package/dist/unstable/cli/internal/command.d.ts.map +1 -1
- package/dist/unstable/cli/internal/command.js +2 -0
- package/dist/unstable/cli/internal/command.js.map +1 -1
- package/package.json +1 -1
- package/src/Effect.ts +271 -7
- package/src/Effectable.ts +2 -2
- package/src/FileSystem.ts +3 -4
- package/src/Match.ts +1 -1
- package/src/Schema.ts +4 -7
- package/src/SchemaRepresentation.ts +39 -24
- package/src/internal/arbitrary/schema.ts +16 -16
- package/src/internal/effect.ts +34 -11
- package/src/internal/schema/fromJsonSchemaDocument.ts +294 -268
- package/src/internal/schema/toCodeDocument.ts +4 -4
- package/src/unstable/cli/Prompt.ts +22 -11
- package/src/unstable/cli/internal/command.ts +2 -0
|
@@ -11,6 +11,11 @@ type Path = ReadonlyArray<string | number>
|
|
|
11
11
|
type Representation = SchemaRepresentation.Representation
|
|
12
12
|
type Check = SchemaRepresentation.Check
|
|
13
13
|
|
|
14
|
+
interface Checked {
|
|
15
|
+
readonly checks: ReadonlyArray<Check>
|
|
16
|
+
readonly annotations?: Schema.Annotations.Annotations | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
type ImportedJsonSchemaRepresentation = Extract<Representation, {
|
|
15
20
|
readonly _tag:
|
|
16
21
|
| "Reference"
|
|
@@ -48,12 +53,18 @@ interface ImportedObjectScope {
|
|
|
48
53
|
const never: ImportedJsonSchemaRepresentation = { _tag: "Never", checks: [] }
|
|
49
54
|
const unknown: ImportedJsonSchemaRepresentation = { _tag: "Unknown", checks: [] }
|
|
50
55
|
const string: ImportedJsonSchemaRepresentation = { _tag: "String", checks: [] }
|
|
56
|
+
const unsupportedStructuredValue = "Only primitive values are supported in \"const\" and \"enum\"."
|
|
57
|
+
|
|
58
|
+
function isObject(input: unknown): input is Record<string, unknown> {
|
|
59
|
+
return typeof input === "object" && input !== null && !Array.isArray(input)
|
|
60
|
+
}
|
|
51
61
|
|
|
52
|
-
function makeJsonLiteral(input: unknown): ImportedJsonSchemaRepresentation | undefined {
|
|
62
|
+
function makeJsonLiteral(input: unknown, path: Path): ImportedJsonSchemaRepresentation | undefined {
|
|
53
63
|
if (input === null) return { _tag: "Null", checks: [] }
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
if (typeof input === "string" || typeof input === "number" || typeof input === "boolean") {
|
|
65
|
+
return { _tag: "Literal", literal: input, checks: [] }
|
|
66
|
+
}
|
|
67
|
+
if (typeof input === "object") throw errorWithPath(unsupportedStructuredValue, path)
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
const jsonSchemaValueTypes = ["null", "string", "number", "boolean", "object", "array"] as const
|
|
@@ -150,56 +161,6 @@ function jsonDeclaration(
|
|
|
150
161
|
}
|
|
151
162
|
}
|
|
152
163
|
|
|
153
|
-
function unknownJsonSchemas(representation: Representation): Representation {
|
|
154
|
-
switch (representation._tag) {
|
|
155
|
-
case "Unknown":
|
|
156
|
-
return jsonDeclaration(representation.annotations)
|
|
157
|
-
case "Suspend":
|
|
158
|
-
return { ...representation, thunk: unknownJsonSchemas(representation.thunk) }
|
|
159
|
-
case "Arrays":
|
|
160
|
-
return {
|
|
161
|
-
...representation,
|
|
162
|
-
elements: representation.elements.map((element) => ({
|
|
163
|
-
...element,
|
|
164
|
-
type: unknownJsonSchemas(element.type)
|
|
165
|
-
})),
|
|
166
|
-
rest: representation.rest.map(unknownJsonSchemas)
|
|
167
|
-
}
|
|
168
|
-
case "Objects":
|
|
169
|
-
return {
|
|
170
|
-
...representation,
|
|
171
|
-
propertySignatures: representation.propertySignatures.map((property) => ({
|
|
172
|
-
...property,
|
|
173
|
-
type: unknownJsonSchemas(property.type)
|
|
174
|
-
})),
|
|
175
|
-
indexSignatures: representation.indexSignatures.map((indexSignature) => ({
|
|
176
|
-
parameter: unknownJsonSchemas(indexSignature.parameter),
|
|
177
|
-
type: unknownJsonSchemas(indexSignature.type)
|
|
178
|
-
})),
|
|
179
|
-
checks: representation.checks.map(unknownJsonSchemaCheck)
|
|
180
|
-
}
|
|
181
|
-
case "Union":
|
|
182
|
-
return { ...representation, types: representation.types.map(unknownJsonSchemas) }
|
|
183
|
-
default:
|
|
184
|
-
return representation
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function unknownJsonSchemaCheck(check: Check): Check {
|
|
189
|
-
const representation = check.representation
|
|
190
|
-
const schemas = representation?.schemas
|
|
191
|
-
if (representation === undefined || schemas === undefined) {
|
|
192
|
-
return check
|
|
193
|
-
}
|
|
194
|
-
return {
|
|
195
|
-
...check,
|
|
196
|
-
representation: {
|
|
197
|
-
...representation,
|
|
198
|
-
schemas: schemas.map(unknownJsonSchemas)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
164
|
function translateJsonSchemaMultiDocument(
|
|
204
165
|
document: JsonSchema.MultiDocument<"draft-2020-12">,
|
|
205
166
|
options?: SchemaRepresentation.FromJsonSchemaOptions,
|
|
@@ -209,13 +170,80 @@ function translateJsonSchemaMultiDocument(
|
|
|
209
170
|
const reachableDefinitions = new Map<string, Path>()
|
|
210
171
|
const objectScopesByProperties = new WeakMap<
|
|
211
172
|
SchemaRepresentation.Objects["propertySignatures"],
|
|
212
|
-
ReadonlyArray<ImportedObjectScope
|
|
173
|
+
readonly [ReadonlyArray<ImportedObjectScope>, Path]
|
|
213
174
|
>()
|
|
214
175
|
const annotatedReferences: Array<{
|
|
215
176
|
readonly reference: SchemaRepresentation.Reference
|
|
216
177
|
readonly path: Path
|
|
217
178
|
}> = []
|
|
218
179
|
const resolvingChoices = new Set<string>()
|
|
180
|
+
const finalReferences = new Set<string>()
|
|
181
|
+
let inNestedResource = false
|
|
182
|
+
|
|
183
|
+
function finalize(representation: Representation): Representation {
|
|
184
|
+
switch (representation._tag) {
|
|
185
|
+
case "Unknown":
|
|
186
|
+
return jsonDeclaration(representation.annotations)
|
|
187
|
+
case "Reference":
|
|
188
|
+
finalReferences.add(representation.$ref)
|
|
189
|
+
return representation
|
|
190
|
+
case "Suspend":
|
|
191
|
+
return { ...representation, thunk: finalize(representation.thunk) }
|
|
192
|
+
case "Arrays":
|
|
193
|
+
return {
|
|
194
|
+
...representation,
|
|
195
|
+
elements: representation.elements.map((element) => ({
|
|
196
|
+
...element,
|
|
197
|
+
type: finalize(element.type)
|
|
198
|
+
})),
|
|
199
|
+
rest: representation.rest.map(finalize)
|
|
200
|
+
}
|
|
201
|
+
case "Objects":
|
|
202
|
+
// Intersections may reduce open patterns to fixed properties, so reject only
|
|
203
|
+
// if an open pattern remains in the final representation.
|
|
204
|
+
const [scopes, path] = objectScopesByProperties.get(representation.propertySignatures)!
|
|
205
|
+
if (
|
|
206
|
+
representation.indexSignatures[0]?.parameter === string &&
|
|
207
|
+
scopes.some((scope) => scope.patterns.length > 0)
|
|
208
|
+
) {
|
|
209
|
+
throw errorWithPath(
|
|
210
|
+
"Cannot import open \"patternProperties\": unmatched keys cannot be typed correctly.",
|
|
211
|
+
path
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
...representation,
|
|
216
|
+
propertySignatures: representation.propertySignatures.map((property) => ({
|
|
217
|
+
...property,
|
|
218
|
+
type: finalize(property.type)
|
|
219
|
+
})),
|
|
220
|
+
indexSignatures: representation.indexSignatures.map((indexSignature) => ({
|
|
221
|
+
parameter: finalize(indexSignature.parameter),
|
|
222
|
+
type: finalize(indexSignature.type)
|
|
223
|
+
})),
|
|
224
|
+
checks: representation.checks.map(finalizeCheck)
|
|
225
|
+
}
|
|
226
|
+
case "Union":
|
|
227
|
+
return { ...representation, types: representation.types.map(finalize) }
|
|
228
|
+
default:
|
|
229
|
+
return representation
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function finalizeCheck(check: Check): Check {
|
|
234
|
+
const representation = check.representation
|
|
235
|
+
const schemas = representation?.schemas
|
|
236
|
+
if (representation === undefined || schemas === undefined) {
|
|
237
|
+
return check
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
...check,
|
|
241
|
+
representation: {
|
|
242
|
+
...representation,
|
|
243
|
+
schemas: schemas.map(finalize)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
219
247
|
|
|
220
248
|
function annotate(
|
|
221
249
|
representation: ImportedJsonSchemaRepresentation,
|
|
@@ -239,15 +267,16 @@ function translateJsonSchemaMultiDocument(
|
|
|
239
267
|
const cached = definitionCache.get(key)
|
|
240
268
|
if (cached !== undefined) {
|
|
241
269
|
if (cached === null) {
|
|
242
|
-
throw errorWithPath(
|
|
270
|
+
throw errorWithPath(
|
|
271
|
+
recursiveReferenceError ??
|
|
272
|
+
`Definition ${JSON.stringify(key)} is a circular alias.`,
|
|
273
|
+
[...path, "$ref"]
|
|
274
|
+
)
|
|
243
275
|
}
|
|
244
276
|
return cached
|
|
245
277
|
}
|
|
246
|
-
if (!Object.hasOwn(document.definitions, key)) {
|
|
247
|
-
throw errorWithPath(`Invalid reference ${key}`, [...path, "$ref"])
|
|
248
|
-
}
|
|
249
278
|
definitionCache.set(key, null)
|
|
250
|
-
const representation =
|
|
279
|
+
const representation = translateSchema(document.definitions[key], ["definitions", key])
|
|
251
280
|
definitionCache.set(key, representation)
|
|
252
281
|
return representation
|
|
253
282
|
}
|
|
@@ -255,20 +284,23 @@ function translateJsonSchemaMultiDocument(
|
|
|
255
284
|
function resolveReference(
|
|
256
285
|
reference: SchemaRepresentation.Reference,
|
|
257
286
|
path: Path,
|
|
258
|
-
|
|
287
|
+
recursiveReferenceError?: string,
|
|
259
288
|
seen: Set<string> = new Set()
|
|
260
289
|
): ImportedJsonSchemaRepresentation {
|
|
261
290
|
if (seen.has(reference.$ref)) {
|
|
262
|
-
throw errorWithPath(
|
|
291
|
+
throw errorWithPath(
|
|
292
|
+
`Definition ${JSON.stringify(reference.$ref)} is a circular alias.`,
|
|
293
|
+
[...path, "$ref"]
|
|
294
|
+
)
|
|
263
295
|
}
|
|
264
296
|
seen.add(reference.$ref)
|
|
265
|
-
const representation = translateDefinition(reference.$ref, path,
|
|
297
|
+
const representation = translateDefinition(reference.$ref, path, recursiveReferenceError)
|
|
266
298
|
if (representation._tag === "Reference") {
|
|
267
|
-
return resolveReference(representation, path,
|
|
299
|
+
return resolveReference(representation, path, recursiveReferenceError, seen)
|
|
268
300
|
}
|
|
269
301
|
if (representation._tag === "Suspend" && representation.thunk._tag === "Reference") {
|
|
270
302
|
return annotate(
|
|
271
|
-
resolveReference(representation.thunk, path,
|
|
303
|
+
resolveReference(representation.thunk, path, recursiveReferenceError, seen),
|
|
272
304
|
representation.annotations
|
|
273
305
|
)
|
|
274
306
|
}
|
|
@@ -284,7 +316,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
284
316
|
return { ...left, ...right }
|
|
285
317
|
}
|
|
286
318
|
|
|
287
|
-
function
|
|
319
|
+
function annotateIntersection(
|
|
288
320
|
representation: ImportedJsonSchemaRepresentation,
|
|
289
321
|
left: ImportedJsonSchemaRepresentation,
|
|
290
322
|
right: ImportedJsonSchemaRepresentation
|
|
@@ -298,42 +330,31 @@ function translateJsonSchemaMultiDocument(
|
|
|
298
330
|
)
|
|
299
331
|
}
|
|
300
332
|
|
|
301
|
-
function
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
): ReadonlyArray<Check> | undefined {
|
|
305
|
-
if (checks.length === 0) return undefined
|
|
306
|
-
if (annotations === undefined) return checks
|
|
307
|
-
if (checks.length === 1 && checks[0].annotations === undefined) {
|
|
308
|
-
return [{
|
|
309
|
-
...checks[0],
|
|
310
|
-
annotations
|
|
311
|
-
}]
|
|
312
|
-
}
|
|
313
|
-
return [{
|
|
314
|
-
_tag: "FilterGroup",
|
|
315
|
-
checks: checks as [Check, ...Array<Check>],
|
|
316
|
-
annotations
|
|
317
|
-
}]
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function combineChecks(
|
|
321
|
-
left: ReadonlyArray<Check>,
|
|
322
|
-
right: ReadonlyArray<Check>,
|
|
323
|
-
annotations: Schema.Annotations.Annotations | undefined,
|
|
333
|
+
function intersectChecks(
|
|
334
|
+
left: Checked,
|
|
335
|
+
right: Checked,
|
|
324
336
|
deduplicate: ReadonlyArray<string> = []
|
|
325
|
-
): ReadonlyArray<Check
|
|
337
|
+
): readonly [ReadonlyArray<Check>, Schema.Annotations.Annotations | undefined] {
|
|
338
|
+
let checks = right.checks
|
|
326
339
|
for (const id of deduplicate) {
|
|
327
|
-
if (left.some((check) =>
|
|
328
|
-
|
|
340
|
+
if (left.checks.some((check) => check._tag === "Filter" && check.representation?.id === id)) {
|
|
341
|
+
checks = checks.filter((check) => check._tag !== "Filter" || check.representation?.id !== id)
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
344
|
+
if (checks.length === 0) {
|
|
345
|
+
const annotations = mergeAnnotations(left.annotations, right.annotations)
|
|
346
|
+
return [left.checks, annotations]
|
|
347
|
+
}
|
|
348
|
+
if (right.annotations !== undefined) {
|
|
349
|
+
checks = checks.length === 1 && checks[0].annotations === undefined
|
|
350
|
+
? [{ ...checks[0], annotations: right.annotations }]
|
|
351
|
+
: [{
|
|
352
|
+
_tag: "FilterGroup",
|
|
353
|
+
checks: checks as [Check, ...Array<Check>],
|
|
354
|
+
annotations: right.annotations
|
|
355
|
+
}]
|
|
356
|
+
}
|
|
357
|
+
return [[...left.checks, ...checks], left.annotations]
|
|
337
358
|
}
|
|
338
359
|
|
|
339
360
|
function satisfiesPrimitiveCheck(check: Check, value: string | number): boolean | undefined {
|
|
@@ -379,7 +400,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
379
400
|
return representation.checks.every((check) => satisfiesPrimitiveCheck(check, value as string | number))
|
|
380
401
|
}
|
|
381
402
|
|
|
382
|
-
function
|
|
403
|
+
function intersectPrimitiveWithLiteral(
|
|
383
404
|
primitive:
|
|
384
405
|
| SchemaRepresentation.String
|
|
385
406
|
| SchemaRepresentation.Number
|
|
@@ -389,10 +410,10 @@ function translateJsonSchemaMultiDocument(
|
|
|
389
410
|
const satisfies = primitive._tag === "Boolean"
|
|
390
411
|
? typeof literal.literal === "boolean"
|
|
391
412
|
: satisfiesLiteral(primitive, literal)
|
|
392
|
-
return satisfies ?
|
|
413
|
+
return satisfies ? annotateIntersection(literal, primitive, literal) : never
|
|
393
414
|
}
|
|
394
415
|
|
|
395
|
-
function
|
|
416
|
+
function intersectArrays(
|
|
396
417
|
left: SchemaRepresentation.Arrays,
|
|
397
418
|
right: SchemaRepresentation.Arrays,
|
|
398
419
|
path: Path
|
|
@@ -408,7 +429,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
408
429
|
if (leftType === undefined || rightType === undefined) {
|
|
409
430
|
return isOptional ? { elements, rest: [] } : undefined
|
|
410
431
|
}
|
|
411
|
-
const type =
|
|
432
|
+
const type = intersect(
|
|
412
433
|
leftType as ImportedJsonSchemaRepresentation,
|
|
413
434
|
rightType as ImportedJsonSchemaRepresentation,
|
|
414
435
|
[...path, "elements", index, "type"]
|
|
@@ -427,7 +448,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
427
448
|
if (leftRest === undefined || rightRest === undefined) {
|
|
428
449
|
return { elements, rest: [] }
|
|
429
450
|
}
|
|
430
|
-
const rest =
|
|
451
|
+
const rest = intersect(
|
|
431
452
|
leftRest as ImportedJsonSchemaRepresentation,
|
|
432
453
|
rightRest as ImportedJsonSchemaRepresentation,
|
|
433
454
|
[...path, "rest", 0]
|
|
@@ -435,13 +456,13 @@ function translateJsonSchemaMultiDocument(
|
|
|
435
456
|
return { elements, rest: rest._tag === "Never" ? [] : [rest] }
|
|
436
457
|
}
|
|
437
458
|
|
|
438
|
-
function
|
|
459
|
+
function intersectAll(
|
|
439
460
|
types: ReadonlyArray<ImportedJsonSchemaRepresentation>,
|
|
440
461
|
path: Path
|
|
441
462
|
): ImportedJsonSchemaRepresentation {
|
|
442
463
|
let out = types[0] ?? unknown
|
|
443
464
|
for (let index = 1; index < types.length; index++) {
|
|
444
|
-
out =
|
|
465
|
+
out = intersect(out, types[index], [...path, index])
|
|
445
466
|
}
|
|
446
467
|
return out
|
|
447
468
|
}
|
|
@@ -461,8 +482,17 @@ function translateJsonSchemaMultiDocument(
|
|
|
461
482
|
requiresFiniteKeyDomain ||= scope.additionalProperties._tag === "Never" ||
|
|
462
483
|
scope.additionalProperties._tag !== "Unknown" && (scope.hasProperties || scope.patterns.length > 0)
|
|
463
484
|
}
|
|
464
|
-
|
|
465
|
-
|
|
485
|
+
const closedPattern = scopes.length === 1 && names.size === 0 &&
|
|
486
|
+
scopes[0].additionalProperties._tag === "Never" && scopes[0].patterns.length === 1
|
|
487
|
+
? scopes[0].patterns[0]
|
|
488
|
+
: undefined
|
|
489
|
+
if (!hasFiniteKeyDomain && requiresFiniteKeyDomain && closedPattern === undefined) {
|
|
490
|
+
throw errorWithPath(
|
|
491
|
+
scopes.some((scope) => scope.additionalProperties._tag === "Never")
|
|
492
|
+
? "Cannot import this closed patterned object: only one pattern without properties is supported."
|
|
493
|
+
: "Cannot combine typed \"additionalProperties\" with other property schemas: Effect index signatures also check excluded keys.",
|
|
494
|
+
path
|
|
495
|
+
)
|
|
466
496
|
}
|
|
467
497
|
|
|
468
498
|
const properties: Array<SchemaRepresentation.PropertySignature> = []
|
|
@@ -484,55 +514,33 @@ function translateJsonSchemaMultiDocument(
|
|
|
484
514
|
}
|
|
485
515
|
if (property?.type === undefined && !matches) types.push(scope.additionalProperties)
|
|
486
516
|
}
|
|
487
|
-
const type =
|
|
517
|
+
const type = intersectAll(types, [...path, "properties", name])
|
|
488
518
|
if (!isOptional && type._tag === "Never") return never
|
|
489
519
|
properties.push({ name, type, isOptional, isMutable: false })
|
|
490
520
|
}
|
|
491
521
|
|
|
492
|
-
const indexSignatures:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
: {
|
|
507
|
-
...pattern,
|
|
508
|
-
type: combine(
|
|
509
|
-
previous.type,
|
|
510
|
-
pattern.type,
|
|
511
|
-
[...path, "patternProperties", pattern.source]
|
|
512
|
-
)
|
|
513
|
-
}
|
|
514
|
-
)
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
for (const { parameter, type } of patterns.values()) {
|
|
518
|
-
indexSignatures.push({
|
|
519
|
-
parameter,
|
|
520
|
-
type: combine(type, additionalProperties, [...path, "indexSignatures"])
|
|
521
|
-
})
|
|
522
|
-
}
|
|
523
|
-
indexSignatures.push({ parameter: string, type: additionalProperties })
|
|
524
|
-
} else if (properties.length === 0) {
|
|
525
|
-
indexSignatures.push({ parameter: string, type: never })
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
objectScopesByProperties.set(properties, scopes)
|
|
529
|
-
return {
|
|
522
|
+
const indexSignatures: ReadonlyArray<SchemaRepresentation.IndexSignature> = closedPattern !== undefined
|
|
523
|
+
? [{ parameter: closedPattern.parameter, type: closedPattern.type }]
|
|
524
|
+
: !hasFiniteKeyDomain
|
|
525
|
+
? [{
|
|
526
|
+
parameter: string,
|
|
527
|
+
type: intersectAll(
|
|
528
|
+
scopes.map((scope) => scope.additionalProperties),
|
|
529
|
+
[...path, "additionalProperties"]
|
|
530
|
+
)
|
|
531
|
+
}]
|
|
532
|
+
: properties.length === 0
|
|
533
|
+
? [{ parameter: string, type: never }]
|
|
534
|
+
: []
|
|
535
|
+
const representation: SchemaRepresentation.Objects = {
|
|
530
536
|
_tag: "Objects",
|
|
531
537
|
propertySignatures: properties,
|
|
532
538
|
indexSignatures,
|
|
533
539
|
checks,
|
|
534
540
|
annotations
|
|
535
541
|
}
|
|
542
|
+
objectScopesByProperties.set(properties, [scopes, path])
|
|
543
|
+
return representation
|
|
536
544
|
}
|
|
537
545
|
|
|
538
546
|
const jsonRootTags = ["Null", "String", "Number", "Boolean", "Objects", "Arrays"] as const
|
|
@@ -597,7 +605,11 @@ function translateJsonSchemaMultiDocument(
|
|
|
597
605
|
representation.indexSignatures.some((indexSignature) =>
|
|
598
606
|
hasChoices(indexSignature.parameter as ImportedJsonSchemaRepresentation) ||
|
|
599
607
|
hasChoices(indexSignature.type as ImportedJsonSchemaRepresentation)
|
|
600
|
-
) ||
|
|
608
|
+
) ||
|
|
609
|
+
objectScopesByProperties.get(representation.propertySignatures)![0].some((scope) =>
|
|
610
|
+
scope.patterns.some((pattern) => hasChoices(pattern.type))
|
|
611
|
+
) ||
|
|
612
|
+
representation.checks.some(checkHasChoices)
|
|
601
613
|
default:
|
|
602
614
|
return false
|
|
603
615
|
}
|
|
@@ -629,7 +641,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
629
641
|
: undefined
|
|
630
642
|
}
|
|
631
643
|
|
|
632
|
-
function
|
|
644
|
+
function intersectLiteralUnions(
|
|
633
645
|
left: SchemaRepresentation.Union,
|
|
634
646
|
right: SchemaRepresentation.Union,
|
|
635
647
|
path: Path
|
|
@@ -650,12 +662,12 @@ function translateJsonSchemaMultiDocument(
|
|
|
650
662
|
const value = literalValue(representation)
|
|
651
663
|
if (value === undefined) return undefined
|
|
652
664
|
const match = rightByValue.get(value)
|
|
653
|
-
if (match !== undefined) types.push(
|
|
665
|
+
if (match !== undefined) types.push(intersect(representation, annotate(match, right.annotations), path))
|
|
654
666
|
}
|
|
655
667
|
return makeUnion(left, types)
|
|
656
668
|
}
|
|
657
669
|
|
|
658
|
-
function
|
|
670
|
+
function intersectUnionWithType(
|
|
659
671
|
union: SchemaRepresentation.Union,
|
|
660
672
|
type: ImportedJsonSchemaRepresentation,
|
|
661
673
|
path: Path,
|
|
@@ -674,16 +686,16 @@ function translateJsonSchemaMultiDocument(
|
|
|
674
686
|
if (members.length === 1 && union.checks.length === 0) {
|
|
675
687
|
const member = annotate(members[0].type, union.annotations)
|
|
676
688
|
return unionOnLeft
|
|
677
|
-
?
|
|
678
|
-
:
|
|
689
|
+
? intersect(member, type, [...path, "types", members[0].index])
|
|
690
|
+
: intersect(type, member, [...path, "types", members[0].index])
|
|
679
691
|
}
|
|
680
692
|
return makeUnion(
|
|
681
693
|
union,
|
|
682
694
|
members
|
|
683
695
|
.map((member) =>
|
|
684
696
|
unionOnLeft
|
|
685
|
-
?
|
|
686
|
-
:
|
|
697
|
+
? intersect(member.type, type, [...path, "types", member.index])
|
|
698
|
+
: intersect(type, member.type, [...path, "types", member.index])
|
|
687
699
|
)
|
|
688
700
|
.filter((type) => type._tag !== "Never")
|
|
689
701
|
)
|
|
@@ -700,10 +712,13 @@ function translateJsonSchemaMultiDocument(
|
|
|
700
712
|
}
|
|
701
713
|
|
|
702
714
|
function unsupportedIntersection(path: Path): never {
|
|
703
|
-
throw errorWithPath(
|
|
715
|
+
throw errorWithPath(
|
|
716
|
+
"Cannot intersect these \"anyOf\" or \"oneOf\" alternatives without expanding their branches.",
|
|
717
|
+
path
|
|
718
|
+
)
|
|
704
719
|
}
|
|
705
720
|
|
|
706
|
-
function
|
|
721
|
+
function intersectPartition(
|
|
707
722
|
partition: SchemaRepresentation.Union,
|
|
708
723
|
other: SchemaRepresentation.Union,
|
|
709
724
|
path: Path,
|
|
@@ -720,91 +735,79 @@ function translateJsonSchemaMultiDocument(
|
|
|
720
735
|
const types: Array<ImportedJsonSchemaRepresentation> = []
|
|
721
736
|
for (let index = 0; index < partitionTypes.length; index++) {
|
|
722
737
|
const member = partitionTypes[index]
|
|
723
|
-
const type =
|
|
738
|
+
const type = intersectUnionWithType(other, member, [...path, "types", index], !partitionOnLeft)
|
|
724
739
|
if (type._tag !== "Never") types.push(type)
|
|
725
740
|
}
|
|
726
741
|
return makeUnion(partition, types)
|
|
727
742
|
}
|
|
728
743
|
|
|
729
|
-
function
|
|
744
|
+
function intersectUnions(
|
|
730
745
|
left: SchemaRepresentation.Union,
|
|
731
746
|
right: SchemaRepresentation.Union,
|
|
732
747
|
path: Path
|
|
733
748
|
): ImportedJsonSchemaRepresentation {
|
|
734
|
-
const literals =
|
|
749
|
+
const literals = intersectLiteralUnions(left, right, path)
|
|
735
750
|
if (literals !== undefined) return literals
|
|
736
|
-
if (isTypePartition(left)) return
|
|
737
|
-
if (isTypePartition(right)) return
|
|
751
|
+
if (isTypePartition(left)) return intersectPartition(left, right, path, true)
|
|
752
|
+
if (isTypePartition(right)) return intersectPartition(right, left, path, false)
|
|
738
753
|
return unsupportedIntersection(path)
|
|
739
754
|
}
|
|
740
755
|
|
|
741
|
-
function
|
|
756
|
+
function intersect(
|
|
742
757
|
left: ImportedJsonSchemaRepresentation,
|
|
743
758
|
right: ImportedJsonSchemaRepresentation,
|
|
744
759
|
path: Path
|
|
745
760
|
): ImportedJsonSchemaRepresentation {
|
|
746
761
|
if (left._tag === "Never") return left
|
|
747
762
|
if (right._tag === "Never") return right
|
|
748
|
-
if (left._tag === "Unknown") return
|
|
749
|
-
if (right._tag === "Unknown") return
|
|
750
|
-
if (left._tag === "Reference") return
|
|
751
|
-
if (right._tag === "Reference") return
|
|
763
|
+
if (left._tag === "Unknown") return annotateIntersection(right, left, right)
|
|
764
|
+
if (right._tag === "Unknown") return annotateIntersection(left, left, right)
|
|
765
|
+
if (left._tag === "Reference") return intersect(resolveReference(left, path), right, path)
|
|
766
|
+
if (right._tag === "Reference") return intersect(left, resolveReference(right, path), path)
|
|
752
767
|
if (left._tag === "Suspend") {
|
|
753
768
|
return annotate(
|
|
754
|
-
|
|
769
|
+
intersect(left.thunk as ImportedJsonSchemaRepresentation, right, path),
|
|
755
770
|
left.annotations
|
|
756
771
|
)
|
|
757
772
|
}
|
|
758
773
|
if (right._tag === "Suspend") {
|
|
759
774
|
return annotate(
|
|
760
|
-
|
|
775
|
+
intersect(left, right.thunk as ImportedJsonSchemaRepresentation, path),
|
|
761
776
|
right.annotations
|
|
762
777
|
)
|
|
763
778
|
}
|
|
764
|
-
if (left._tag === "Union" && right._tag === "Union") return
|
|
765
|
-
if (left._tag === "Union") return
|
|
766
|
-
if (right._tag === "Union") return
|
|
779
|
+
if (left._tag === "Union" && right._tag === "Union") return intersectUnions(left, right, path)
|
|
780
|
+
if (left._tag === "Union") return intersectUnionWithType(left, right, path, true)
|
|
781
|
+
if (right._tag === "Union") return intersectUnionWithType(right, left, path, false)
|
|
767
782
|
|
|
768
783
|
switch (left._tag) {
|
|
769
784
|
case "Null":
|
|
770
785
|
return right._tag === "Null"
|
|
771
|
-
?
|
|
786
|
+
? annotateIntersection({ _tag: "Null", checks: [...left.checks, ...right.checks] }, left, right)
|
|
772
787
|
: never
|
|
773
788
|
case "String":
|
|
774
789
|
if (right._tag === "Literal") {
|
|
775
|
-
return
|
|
790
|
+
return intersectPrimitiveWithLiteral(left, right)
|
|
776
791
|
}
|
|
777
792
|
if (right._tag !== "String") return never
|
|
778
|
-
const stringChecks =
|
|
779
|
-
return annotate(
|
|
780
|
-
{
|
|
781
|
-
_tag: "String",
|
|
782
|
-
checks: stringChecks ?? left.checks
|
|
783
|
-
},
|
|
784
|
-
mergeAnnotations(left.annotations, stringChecks === undefined ? right.annotations : undefined)
|
|
785
|
-
)
|
|
793
|
+
const [stringChecks, stringAnnotations] = intersectChecks(left, right)
|
|
794
|
+
return annotate({ _tag: "String", checks: stringChecks }, stringAnnotations)
|
|
786
795
|
case "Number":
|
|
787
796
|
if (right._tag === "Literal") {
|
|
788
|
-
return
|
|
797
|
+
return intersectPrimitiveWithLiteral(left, right)
|
|
789
798
|
}
|
|
790
799
|
if (right._tag !== "Number") return never
|
|
791
|
-
const numberChecks =
|
|
800
|
+
const [numberChecks, numberAnnotations] = intersectChecks(left, right, [
|
|
792
801
|
"effect/schema/isFinite",
|
|
793
802
|
"effect/schema/isInt"
|
|
794
803
|
])
|
|
795
|
-
return annotate(
|
|
796
|
-
{
|
|
797
|
-
_tag: "Number",
|
|
798
|
-
checks: numberChecks ?? left.checks
|
|
799
|
-
},
|
|
800
|
-
mergeAnnotations(left.annotations, numberChecks === undefined ? right.annotations : undefined)
|
|
801
|
-
)
|
|
804
|
+
return annotate({ _tag: "Number", checks: numberChecks }, numberAnnotations)
|
|
802
805
|
case "Boolean":
|
|
803
806
|
if (right._tag === "Literal") {
|
|
804
|
-
return
|
|
807
|
+
return intersectPrimitiveWithLiteral(left, right)
|
|
805
808
|
}
|
|
806
809
|
return right._tag === "Boolean"
|
|
807
|
-
?
|
|
810
|
+
? annotateIntersection(
|
|
808
811
|
{
|
|
809
812
|
_tag: "Boolean",
|
|
810
813
|
checks: [...left.checks, ...right.checks]
|
|
@@ -816,7 +819,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
816
819
|
case "Literal":
|
|
817
820
|
if (right._tag === "Literal") {
|
|
818
821
|
return left.literal === right.literal
|
|
819
|
-
?
|
|
822
|
+
? annotateIntersection(
|
|
820
823
|
{
|
|
821
824
|
...left,
|
|
822
825
|
checks: [...left.checks, ...right.checks]
|
|
@@ -830,58 +833,54 @@ function translateJsonSchemaMultiDocument(
|
|
|
830
833
|
(right._tag === "String" || right._tag === "Number") && satisfiesLiteral(right, left) ||
|
|
831
834
|
right._tag === "Boolean" && typeof left.literal === "boolean"
|
|
832
835
|
) {
|
|
833
|
-
return
|
|
836
|
+
return annotateIntersection(left, left, right)
|
|
834
837
|
}
|
|
835
838
|
return never
|
|
836
839
|
case "Arrays": {
|
|
837
840
|
if (right._tag !== "Arrays") return never
|
|
838
|
-
const arrays =
|
|
841
|
+
const arrays = intersectArrays(left, right, path)
|
|
839
842
|
if (arrays === undefined) return never
|
|
840
|
-
const
|
|
841
|
-
return annotate(
|
|
842
|
-
{
|
|
843
|
-
_tag: "Arrays",
|
|
844
|
-
elements: arrays.elements,
|
|
845
|
-
rest: arrays.rest,
|
|
846
|
-
checks: arrayChecks ?? left.checks
|
|
847
|
-
},
|
|
848
|
-
mergeAnnotations(left.annotations, arrayChecks === undefined ? right.annotations : undefined)
|
|
849
|
-
)
|
|
843
|
+
const [checks, annotations] = intersectChecks(left, right, ["effect/schema/isUnique"])
|
|
844
|
+
return annotate({ _tag: "Arrays", ...arrays, checks }, annotations)
|
|
850
845
|
}
|
|
851
846
|
case "Objects": {
|
|
852
847
|
if (right._tag !== "Objects") return never
|
|
853
|
-
const
|
|
848
|
+
const [checks, annotations] = intersectChecks(left, right)
|
|
854
849
|
const scopes = [
|
|
855
|
-
...objectScopesByProperties.get(left.propertySignatures)
|
|
856
|
-
...objectScopesByProperties.get(right.propertySignatures)!
|
|
850
|
+
...objectScopesByProperties.get(left.propertySignatures)![0],
|
|
851
|
+
...objectScopesByProperties.get(right.propertySignatures)![0]
|
|
857
852
|
]
|
|
858
853
|
return lowerObject(
|
|
859
854
|
scopes,
|
|
860
|
-
|
|
861
|
-
|
|
855
|
+
checks,
|
|
856
|
+
annotations,
|
|
862
857
|
path
|
|
863
858
|
)
|
|
864
859
|
}
|
|
865
860
|
}
|
|
866
861
|
}
|
|
867
862
|
|
|
868
|
-
function
|
|
863
|
+
function translateSchema(
|
|
864
|
+
input: unknown,
|
|
865
|
+
path: Path,
|
|
866
|
+
isRoot = false
|
|
867
|
+
): ImportedJsonSchemaRepresentation {
|
|
869
868
|
if (input === false) {
|
|
870
869
|
return never
|
|
871
870
|
}
|
|
872
|
-
if (
|
|
871
|
+
if (!isObject(input)) return unknown
|
|
873
872
|
const schema = options?.onEnter === undefined
|
|
874
873
|
? input as JsonSchema.JsonSchema
|
|
875
874
|
: options.onEnter(input as JsonSchema.JsonSchema)
|
|
876
875
|
if (schema === undefined) {
|
|
877
876
|
return unknown
|
|
878
877
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
878
|
+
if (
|
|
879
|
+
isObject(schema.not) &&
|
|
880
|
+
Object.keys(schema.not).length === 0
|
|
881
|
+
) return annotate(never, jsonSchemaAnnotations(schema))
|
|
882
|
+
const wasInNestedResource = inNestedResource
|
|
883
|
+
inNestedResource ||= !isRoot && typeof schema.$id === "string"
|
|
885
884
|
for (const keyword of Object.keys(schema)) {
|
|
886
885
|
if (keyword === "if" && !Object.hasOwn(schema, "then") && !Object.hasOwn(schema, "else")) continue
|
|
887
886
|
switch (keyword) {
|
|
@@ -893,21 +892,22 @@ function translateJsonSchemaMultiDocument(
|
|
|
893
892
|
case "not":
|
|
894
893
|
case "unevaluatedItems":
|
|
895
894
|
case "unevaluatedProperties":
|
|
896
|
-
throw errorWithPath(
|
|
895
|
+
throw errorWithPath(
|
|
896
|
+
`Cannot import JSON Schema keyword "${keyword}": Effect has no equivalent constraint.`,
|
|
897
|
+
[...path, keyword]
|
|
898
|
+
)
|
|
897
899
|
}
|
|
898
900
|
}
|
|
899
901
|
|
|
900
|
-
let representation =
|
|
902
|
+
let representation = translateType(schema, path)
|
|
901
903
|
if (Object.hasOwn(schema, "const")) {
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
906
|
-
if (literal !== undefined) representation = combine(representation, literal, [...path, "const"])
|
|
904
|
+
const constPath = [...path, "const"]
|
|
905
|
+
const literal = makeJsonLiteral(schema.const, constPath)
|
|
906
|
+
if (literal !== undefined) representation = intersect(representation, literal, constPath)
|
|
907
907
|
}
|
|
908
908
|
if (Array.isArray(schema.enum)) {
|
|
909
|
-
const types = schema.enum.map((value) => makeJsonLiteral(value) ?? unknown)
|
|
910
|
-
representation =
|
|
909
|
+
const types = schema.enum.map((value, index) => makeJsonLiteral(value, [...path, "enum", index]) ?? unknown)
|
|
910
|
+
representation = intersect(
|
|
911
911
|
representation,
|
|
912
912
|
types.length === 1
|
|
913
913
|
? types[0]
|
|
@@ -916,12 +916,24 @@ function translateJsonSchemaMultiDocument(
|
|
|
916
916
|
)
|
|
917
917
|
}
|
|
918
918
|
if (typeof schema.$ref === "string") {
|
|
919
|
+
if (inNestedResource) {
|
|
920
|
+
throw errorWithPath(
|
|
921
|
+
"Cannot resolve $ref under a nested \"$id\". Resolve or flatten it first.",
|
|
922
|
+
[...path, "$ref"]
|
|
923
|
+
)
|
|
924
|
+
}
|
|
919
925
|
const $ref = JsonSchema.getReferenceKey(schema.$ref)
|
|
920
926
|
if ($ref === undefined) {
|
|
921
|
-
throw errorWithPath(
|
|
927
|
+
throw errorWithPath(
|
|
928
|
+
`Unsupported $ref ${JSON.stringify(schema.$ref)}. Use "#/$defs/Name".`,
|
|
929
|
+
[...path, "$ref"]
|
|
930
|
+
)
|
|
922
931
|
}
|
|
923
932
|
if (!Object.hasOwn(document.definitions, $ref)) {
|
|
924
|
-
throw errorWithPath(
|
|
933
|
+
throw errorWithPath(
|
|
934
|
+
`Missing definition ${JSON.stringify($ref)} for $ref ${JSON.stringify(schema.$ref)}.`,
|
|
935
|
+
[...path, "$ref"]
|
|
936
|
+
)
|
|
925
937
|
}
|
|
926
938
|
if (!reachableDefinitions.has($ref)) reachableDefinitions.set($ref, path)
|
|
927
939
|
const reference: SchemaRepresentation.Reference = { _tag: "Reference", $ref }
|
|
@@ -929,10 +941,12 @@ function translateJsonSchemaMultiDocument(
|
|
|
929
941
|
? reference
|
|
930
942
|
: representation._tag === "Never"
|
|
931
943
|
? never
|
|
932
|
-
:
|
|
933
|
-
resolveReference(
|
|
934
|
-
|
|
935
|
-
|
|
944
|
+
: intersect(
|
|
945
|
+
resolveReference(
|
|
946
|
+
reference,
|
|
947
|
+
path,
|
|
948
|
+
`Recursive $ref ${JSON.stringify(reference.$ref)} cannot have sibling constraints.`
|
|
949
|
+
),
|
|
936
950
|
representation,
|
|
937
951
|
path
|
|
938
952
|
)
|
|
@@ -945,9 +959,9 @@ function translateJsonSchemaMultiDocument(
|
|
|
945
959
|
|
|
946
960
|
if (Array.isArray(schema.allOf)) {
|
|
947
961
|
for (let index = 0; index < schema.allOf.length; index++) {
|
|
948
|
-
representation =
|
|
962
|
+
representation = intersect(
|
|
949
963
|
representation,
|
|
950
|
-
|
|
964
|
+
translateSchema(schema.allOf[index], [...path, "allOf", index]),
|
|
951
965
|
[...path, "allOf", index]
|
|
952
966
|
)
|
|
953
967
|
}
|
|
@@ -958,17 +972,21 @@ function translateJsonSchemaMultiDocument(
|
|
|
958
972
|
if (Array.isArray(members)) {
|
|
959
973
|
const union: ImportedJsonSchemaRepresentation = {
|
|
960
974
|
_tag: "Union",
|
|
961
|
-
types: members.map((member, index) =>
|
|
975
|
+
types: members.map((member, index) => translateSchema(member, [...path, mode, index])),
|
|
962
976
|
...(mode === "oneOf" ? { options: { mode } } : {}),
|
|
963
977
|
checks: []
|
|
964
978
|
}
|
|
965
|
-
representation =
|
|
979
|
+
representation = intersect(union, representation, [...path, mode])
|
|
966
980
|
}
|
|
967
981
|
}
|
|
982
|
+
inNestedResource = wasInNestedResource
|
|
968
983
|
return representation
|
|
969
984
|
}
|
|
970
985
|
|
|
971
|
-
function
|
|
986
|
+
function translateType(
|
|
987
|
+
schema: JsonSchema.JsonSchema,
|
|
988
|
+
path: Path
|
|
989
|
+
): ImportedJsonSchemaRepresentation {
|
|
972
990
|
const types = Array.isArray(schema.type) && schema.type.every(isImportedJsonSchemaType)
|
|
973
991
|
? schema.type
|
|
974
992
|
: !isImportedJsonSchemaType(schema.type) && hasTypeSpecificKeywords(schema)
|
|
@@ -977,7 +995,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
977
995
|
if (types !== undefined) {
|
|
978
996
|
return {
|
|
979
997
|
_tag: "Union",
|
|
980
|
-
types: types.map((type) =>
|
|
998
|
+
types: types.map((type) => translateType({ ...schema, type }, path)),
|
|
981
999
|
checks: []
|
|
982
1000
|
}
|
|
983
1001
|
}
|
|
@@ -1007,7 +1025,7 @@ function translateJsonSchemaMultiDocument(
|
|
|
1007
1025
|
const minItems = typeof schema.minItems === "number" ? schema.minItems : 0
|
|
1008
1026
|
const elements = prefixItems?.map((element, index) => ({
|
|
1009
1027
|
isOptional: index + 1 > minItems,
|
|
1010
|
-
type:
|
|
1028
|
+
type: translateSchema(element, [...path, "prefixItems", index])
|
|
1011
1029
|
})) ?? []
|
|
1012
1030
|
const isTupleClosed = schema.items === false ||
|
|
1013
1031
|
(schema.items === undefined &&
|
|
@@ -1021,7 +1039,9 @@ function translateJsonSchemaMultiDocument(
|
|
|
1021
1039
|
elements,
|
|
1022
1040
|
rest: isTupleClosed
|
|
1023
1041
|
? []
|
|
1024
|
-
: [
|
|
1042
|
+
: [
|
|
1043
|
+
schema.items === undefined ? unknown : translateSchema(schema.items, [...path, "items"])
|
|
1044
|
+
],
|
|
1025
1045
|
checks: collectArrayChecks(schema, isMaxItemsRedundant)
|
|
1026
1046
|
}
|
|
1027
1047
|
}
|
|
@@ -1034,14 +1054,17 @@ function translateJsonSchemaMultiDocument(
|
|
|
1034
1054
|
}
|
|
1035
1055
|
}
|
|
1036
1056
|
|
|
1037
|
-
function
|
|
1057
|
+
function importPatternCheck(pattern: string, path: Path): Check | undefined {
|
|
1038
1058
|
switch (options?.patterns ?? "error") {
|
|
1039
1059
|
case "error":
|
|
1040
|
-
throw errorWithPath(
|
|
1060
|
+
throw errorWithPath(
|
|
1061
|
+
"Patterns may block validation and are disabled. Use patterns: \"apply\" for trusted schemas or \"ignore\" to discard them.",
|
|
1062
|
+
path
|
|
1063
|
+
)
|
|
1041
1064
|
case "ignore":
|
|
1042
|
-
return
|
|
1065
|
+
return undefined
|
|
1043
1066
|
case "apply":
|
|
1044
|
-
return
|
|
1067
|
+
return jsonSchemaFilter("effect/schema/isPattern", { source: pattern, flags: "" })
|
|
1045
1068
|
}
|
|
1046
1069
|
}
|
|
1047
1070
|
|
|
@@ -1050,7 +1073,8 @@ function translateJsonSchemaMultiDocument(
|
|
|
1050
1073
|
addNumberCheck(checks, schema.minLength, "effect/schema/isMinLength", "minLength")
|
|
1051
1074
|
addNumberCheck(checks, schema.maxLength, "effect/schema/isMaxLength", "maxLength")
|
|
1052
1075
|
if (typeof schema.pattern === "string") {
|
|
1053
|
-
|
|
1076
|
+
const check = importPatternCheck(schema.pattern, [...path, "pattern"])
|
|
1077
|
+
if (check !== undefined) checks.push(check)
|
|
1054
1078
|
}
|
|
1055
1079
|
return checks
|
|
1056
1080
|
}
|
|
@@ -1086,45 +1110,42 @@ function translateJsonSchemaMultiDocument(
|
|
|
1086
1110
|
schema: JsonSchema.JsonSchema,
|
|
1087
1111
|
path: Path
|
|
1088
1112
|
): ImportedObjectScope {
|
|
1089
|
-
const sourceProperties =
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
:
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
: []
|
|
1113
|
+
const sourceProperties = isObject(schema.properties) ? schema.properties : {}
|
|
1114
|
+
const required = new Set(
|
|
1115
|
+
Array.isArray(schema.required)
|
|
1116
|
+
? schema.required.filter((key): key is string => typeof key === "string")
|
|
1117
|
+
: []
|
|
1118
|
+
)
|
|
1096
1119
|
const propertyNames = Object.keys(sourceProperties)
|
|
1097
1120
|
const keys = new Set([...propertyNames, ...required])
|
|
1098
1121
|
const properties = new Map(Array.from(keys, (name) => [name, {
|
|
1099
1122
|
type: Object.hasOwn(sourceProperties, name)
|
|
1100
|
-
?
|
|
1123
|
+
? translateSchema(sourceProperties[name], [...path, "properties", name])
|
|
1101
1124
|
: undefined,
|
|
1102
|
-
isOptional: !required.
|
|
1125
|
+
isOptional: !required.has(name)
|
|
1103
1126
|
}]))
|
|
1104
1127
|
const hasProperties = propertyNames.length > 0
|
|
1105
1128
|
const patterns: Array<ImportedObjectPattern> = []
|
|
1106
1129
|
if (
|
|
1107
|
-
|
|
1108
|
-
schema.patternProperties !== null &&
|
|
1109
|
-
!Array.isArray(schema.patternProperties)
|
|
1130
|
+
isObject(schema.patternProperties)
|
|
1110
1131
|
) {
|
|
1111
1132
|
for (const [pattern, value] of Object.entries(schema.patternProperties)) {
|
|
1112
|
-
const
|
|
1113
|
-
if (
|
|
1133
|
+
const check = importPatternCheck(pattern, [...path, "patternProperties", pattern])
|
|
1134
|
+
if (check === undefined) return { properties, hasProperties, patterns: [], additionalProperties: unknown }
|
|
1114
1135
|
patterns.push({
|
|
1115
1136
|
source: pattern,
|
|
1116
1137
|
parameter: {
|
|
1117
1138
|
_tag: "String",
|
|
1118
|
-
checks
|
|
1139
|
+
checks: [check]
|
|
1119
1140
|
},
|
|
1120
|
-
type:
|
|
1141
|
+
type: translateSchema(value, [...path, "patternProperties", pattern])
|
|
1121
1142
|
})
|
|
1122
1143
|
}
|
|
1123
1144
|
}
|
|
1124
1145
|
const additionalProperties = schema.additionalProperties === false
|
|
1125
1146
|
? never
|
|
1126
|
-
:
|
|
1127
|
-
?
|
|
1147
|
+
: isObject(schema.additionalProperties)
|
|
1148
|
+
? translateSchema(schema.additionalProperties, [...path, "additionalProperties"])
|
|
1128
1149
|
: unknown
|
|
1129
1150
|
return { properties, hasProperties, patterns, additionalProperties }
|
|
1130
1151
|
}
|
|
@@ -1141,7 +1162,11 @@ function translateJsonSchemaMultiDocument(
|
|
|
1141
1162
|
checks.push(jsonSchemaFilter(
|
|
1142
1163
|
"effect/schema/isPropertyNames",
|
|
1143
1164
|
null,
|
|
1144
|
-
[
|
|
1165
|
+
[intersect(
|
|
1166
|
+
string,
|
|
1167
|
+
translateSchema(schema.propertyNames, propertyNamesPath),
|
|
1168
|
+
propertyNamesPath
|
|
1169
|
+
)]
|
|
1145
1170
|
))
|
|
1146
1171
|
}
|
|
1147
1172
|
return checks
|
|
@@ -1149,10 +1174,11 @@ function translateJsonSchemaMultiDocument(
|
|
|
1149
1174
|
|
|
1150
1175
|
const references: Record<string, Representation> = {}
|
|
1151
1176
|
const representations = document.schemas.map((schema, index) =>
|
|
1152
|
-
|
|
1177
|
+
finalize(translateSchema(schema, singleRoot ? ["schema"] : ["schemas", index], true))
|
|
1153
1178
|
) as [Representation, ...Array<Representation>]
|
|
1154
|
-
|
|
1155
|
-
|
|
1179
|
+
// Definitions expanded away by intersections do not need a standalone output schema.
|
|
1180
|
+
for (const key of finalReferences) {
|
|
1181
|
+
InternalRecord.assignProperty(references, key, finalize(translateDefinition(key, reachableDefinitions.get(key)!)))
|
|
1156
1182
|
}
|
|
1157
1183
|
for (const { reference, path } of annotatedReferences) {
|
|
1158
1184
|
resolveReference(reference, path)
|