effect 4.0.0-beta.50 → 4.0.0-beta.51
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/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +18 -14
- package/dist/BigDecimal.js.map +1 -1
- package/dist/BigInt.d.ts.map +1 -1
- package/dist/BigInt.js +4 -4
- package/dist/BigInt.js.map +1 -1
- package/dist/Brand.d.ts +2 -4
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js.map +1 -1
- package/dist/Duration.js +1 -1
- package/dist/Duration.js.map +1 -1
- package/dist/Schema.d.ts +77 -9
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +37 -7
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +6 -0
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +216 -229
- package/dist/SchemaAST.js.map +1 -1
- package/dist/SchemaGetter.d.ts +3 -5
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +3 -2
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/SchemaIssue.d.ts.map +1 -1
- package/dist/SchemaIssue.js +29 -11
- package/dist/SchemaIssue.js.map +1 -1
- package/dist/SchemaParser.js +14 -2
- package/dist/SchemaParser.js.map +1 -1
- package/dist/internal/effect.js +142 -65
- package/dist/internal/effect.js.map +1 -1
- package/dist/unstable/cluster/Runners.d.ts.map +1 -1
- package/dist/unstable/cluster/Runners.js +3 -2
- package/dist/unstable/cluster/Runners.js.map +1 -1
- package/dist/unstable/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/unstable/cluster/SqlMessageStorage.js +1 -0
- package/dist/unstable/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/unstable/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/unstable/cluster/SqlRunnerStorage.js +6 -6
- package/dist/unstable/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -1
- package/dist/unstable/eventlog/SqlEventJournal.js +9 -8
- package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerEncrypted.js +6 -5
- package/dist/unstable/eventlog/SqlEventLogServerEncrypted.js.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -1
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +6 -5
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
- package/dist/unstable/httpapi/OpenApi.d.ts +1 -10
- package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
- package/dist/unstable/httpapi/OpenApi.js +2 -11
- package/dist/unstable/httpapi/OpenApi.js.map +1 -1
- package/dist/unstable/observability/OtlpMetrics.js +1 -1
- package/dist/unstable/observability/OtlpMetrics.js.map +1 -1
- package/dist/unstable/observability/internal/protobuf.js +4 -4
- package/dist/unstable/observability/internal/protobuf.js.map +1 -1
- package/dist/unstable/rpc/RpcSerialization.d.ts +11 -0
- package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcSerialization.js +14 -9
- package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
- package/package.json +1 -1
- package/src/BigDecimal.ts +20 -16
- package/src/BigInt.ts +4 -4
- package/src/Brand.ts +2 -4
- package/src/Duration.ts +1 -1
- package/src/Schema.ts +87 -11
- package/src/SchemaAST.ts +315 -267
- package/src/SchemaGetter.ts +4 -6
- package/src/SchemaIssue.ts +28 -15
- package/src/SchemaParser.ts +8 -2
- package/src/internal/effect.ts +196 -69
- package/src/unstable/cluster/Runners.ts +8 -5
- package/src/unstable/cluster/SqlMessageStorage.ts +1 -0
- package/src/unstable/cluster/SqlRunnerStorage.ts +12 -6
- package/src/unstable/eventlog/SqlEventJournal.ts +10 -2
- package/src/unstable/eventlog/SqlEventLogServerEncrypted.ts +8 -3
- package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +9 -3
- package/src/unstable/httpapi/OpenApi.ts +2 -14
- package/src/unstable/observability/OtlpMetrics.ts +1 -1
- package/src/unstable/observability/internal/protobuf.ts +4 -4
- package/src/unstable/rpc/RpcSerialization.ts +41 -36
package/src/SchemaAST.ts
CHANGED
|
@@ -77,10 +77,10 @@ import * as Arr from "./Array.ts"
|
|
|
77
77
|
import * as Cause from "./Cause.ts"
|
|
78
78
|
import type * as Combiner from "./Combiner.ts"
|
|
79
79
|
import * as Effect from "./Effect.ts"
|
|
80
|
-
import
|
|
80
|
+
import * as Exit from "./Exit.ts"
|
|
81
81
|
import { format, formatPropertyKey } from "./Formatter.ts"
|
|
82
82
|
import { memoize } from "./Function.ts"
|
|
83
|
-
import { effectIsExit } from "./internal/effect.ts"
|
|
83
|
+
import { effectIsExit, iterateEager } from "./internal/effect.ts"
|
|
84
84
|
import * as internalRecord from "./internal/record.ts"
|
|
85
85
|
import * as InternalAnnotations from "./internal/schema/annotations.ts"
|
|
86
86
|
import * as Option from "./Option.ts"
|
|
@@ -440,6 +440,13 @@ export interface ParseOptions {
|
|
|
440
440
|
* transformations.
|
|
441
441
|
*/
|
|
442
442
|
readonly disableChecks?: boolean | undefined
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* The maximum number of async effects to run concurrently.
|
|
446
|
+
*
|
|
447
|
+
* Defaults to 1.
|
|
448
|
+
*/
|
|
449
|
+
readonly concurrency?: number | "unbounded" | undefined
|
|
443
450
|
}
|
|
444
451
|
|
|
445
452
|
/** @internal */
|
|
@@ -1375,6 +1382,19 @@ export class Arrays extends Base {
|
|
|
1375
1382
|
const elements = ast.elements.map((ast) => ({ ast, parser: recur(ast) }))
|
|
1376
1383
|
const rest = ast.rest.map((ast) => ({ ast, parser: recur(ast) }))
|
|
1377
1384
|
const elementLen = elements.length
|
|
1385
|
+
|
|
1386
|
+
const [head, ...tail] = rest
|
|
1387
|
+
const tailLen = tail.length
|
|
1388
|
+
|
|
1389
|
+
function getParser(tailThreshold: number, index: number): { readonly ast: AST; readonly parser: Parser.Parser } {
|
|
1390
|
+
if (index < elementLen) {
|
|
1391
|
+
return elements[index]
|
|
1392
|
+
} else if (index >= tailThreshold) {
|
|
1393
|
+
return tail[index - tailThreshold]
|
|
1394
|
+
}
|
|
1395
|
+
return head
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1378
1398
|
return Effect.fnUntracedEager(function*(oinput, options) {
|
|
1379
1399
|
if (oinput._tag === "None") {
|
|
1380
1400
|
return oinput
|
|
@@ -1386,132 +1406,42 @@ export class Arrays extends Base {
|
|
|
1386
1406
|
return yield* Effect.fail(new Issue.InvalidType(ast, oinput))
|
|
1387
1407
|
}
|
|
1388
1408
|
|
|
1389
|
-
const
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
const value = i < input.length ? Option.some(input[i]) : Option.none()
|
|
1400
|
-
const eff = e.parser(value, options)
|
|
1401
|
-
const exit = effectIsExit(eff) ? eff : yield* Effect.exit(eff)
|
|
1402
|
-
if (exit._tag === "Failure") {
|
|
1403
|
-
const issueElement = Cause.findError(exit.cause)
|
|
1404
|
-
if (Result.isFailure(issueElement)) {
|
|
1405
|
-
return yield* exit
|
|
1406
|
-
}
|
|
1407
|
-
const issue = new Issue.Pointer([i], issueElement.success)
|
|
1408
|
-
if (errorsAllOption) {
|
|
1409
|
-
if (issues) issues.push(issue)
|
|
1410
|
-
else issues = [issue]
|
|
1411
|
-
} else {
|
|
1412
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1413
|
-
}
|
|
1414
|
-
} else if (exit.value._tag === "Some") {
|
|
1415
|
-
output[i] = exit.value.value
|
|
1416
|
-
} else if (!isOptional(e.ast)) {
|
|
1417
|
-
const issue = new Issue.Pointer([i], new Issue.MissingKey(e.ast.context?.annotations))
|
|
1418
|
-
if (errorsAllOption) {
|
|
1419
|
-
if (issues) issues.push(issue)
|
|
1420
|
-
else issues = [issue]
|
|
1421
|
-
} else {
|
|
1422
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1409
|
+
const len = input.length
|
|
1410
|
+
const state = {
|
|
1411
|
+
ast,
|
|
1412
|
+
getParser,
|
|
1413
|
+
oinput,
|
|
1414
|
+
len,
|
|
1415
|
+
tailThreshold: resolveTailThreshold(len, elementLen, tailLen),
|
|
1416
|
+
output: new globalThis.Array(len),
|
|
1417
|
+
issues: undefined as Arr.NonEmptyArray<Issue.Issue> | undefined,
|
|
1418
|
+
options
|
|
1425
1419
|
}
|
|
1420
|
+
const concurrency = resolveConcurrency(options?.concurrency)
|
|
1421
|
+
const eff = parseArray(state, input, {
|
|
1422
|
+
concurrency: concurrency?.concurrency,
|
|
1423
|
+
end: ast.rest.length === 0 ? elementLen : Math.max(len, elementLen + tailLen)
|
|
1424
|
+
})
|
|
1425
|
+
if (eff) yield* eff
|
|
1426
|
+
|
|
1426
1427
|
// ---------------------------------------------
|
|
1427
|
-
// handle
|
|
1428
|
+
// handle excess indexes
|
|
1428
1429
|
// ---------------------------------------------
|
|
1429
|
-
|
|
1430
|
-
if (ast.rest.length > 0) {
|
|
1431
|
-
const [head, ...tail] = rest
|
|
1432
|
-
const keyAnnotations = head.ast.context?.annotations
|
|
1433
|
-
for (; i < len - tail.length; i++) {
|
|
1434
|
-
const eff = head.parser(Option.some(input[i]), options)
|
|
1435
|
-
const exit = effectIsExit(eff) ? eff : yield* Effect.exit(eff)
|
|
1436
|
-
if (exit._tag === "Failure") {
|
|
1437
|
-
const issueRest = Cause.findError(exit.cause)
|
|
1438
|
-
if (Result.isFailure(issueRest)) {
|
|
1439
|
-
return yield* exit
|
|
1440
|
-
}
|
|
1441
|
-
const issue = new Issue.Pointer([i], issueRest.success)
|
|
1442
|
-
if (errorsAllOption) {
|
|
1443
|
-
if (issues) issues.push(issue)
|
|
1444
|
-
else issues = [issue]
|
|
1445
|
-
} else {
|
|
1446
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1447
|
-
}
|
|
1448
|
-
} else if (exit.value._tag === "Some") {
|
|
1449
|
-
output[i] = exit.value.value
|
|
1450
|
-
} else {
|
|
1451
|
-
const issue = new Issue.Pointer([i], new Issue.MissingKey(keyAnnotations))
|
|
1452
|
-
if (errorsAllOption) {
|
|
1453
|
-
if (issues) issues.push(issue)
|
|
1454
|
-
else issues = [issue]
|
|
1455
|
-
} else {
|
|
1456
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
// ---------------------------------------------
|
|
1461
|
-
// handle post rest elements
|
|
1462
|
-
// ---------------------------------------------
|
|
1463
|
-
for (let j = 0; j < tail.length; j++) {
|
|
1464
|
-
const index = i + j
|
|
1465
|
-
if (len < index) {
|
|
1466
|
-
continue
|
|
1467
|
-
} else {
|
|
1468
|
-
const tailj = tail[j]
|
|
1469
|
-
const keyAnnotations = tailj.ast.context?.annotations
|
|
1470
|
-
const eff = tailj.parser(Option.some(input[index]), options)
|
|
1471
|
-
const exit = effectIsExit(eff) ? eff : yield* Effect.exit(eff)
|
|
1472
|
-
if (exit._tag === "Failure") {
|
|
1473
|
-
const issueRest = Cause.findError(exit.cause)
|
|
1474
|
-
if (Result.isFailure(issueRest)) {
|
|
1475
|
-
return yield* exit
|
|
1476
|
-
}
|
|
1477
|
-
const issue = new Issue.Pointer([index], issueRest.success)
|
|
1478
|
-
if (errorsAllOption) {
|
|
1479
|
-
if (issues) issues.push(issue)
|
|
1480
|
-
else issues = [issue]
|
|
1481
|
-
} else {
|
|
1482
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1483
|
-
}
|
|
1484
|
-
} else if (exit.value._tag === "Some") {
|
|
1485
|
-
output[index] = exit.value.value
|
|
1486
|
-
} else {
|
|
1487
|
-
const issue = new Issue.Pointer([index], new Issue.MissingKey(keyAnnotations))
|
|
1488
|
-
if (errorsAllOption) {
|
|
1489
|
-
if (issues) issues.push(issue)
|
|
1490
|
-
else issues = [issue]
|
|
1491
|
-
} else {
|
|
1492
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
} else {
|
|
1498
|
-
// ---------------------------------------------
|
|
1499
|
-
// handle excess indexes
|
|
1500
|
-
// ---------------------------------------------
|
|
1430
|
+
if (ast.rest.length === 0 && len > elementLen) {
|
|
1501
1431
|
for (let i = elementLen; i <= len - 1; i++) {
|
|
1502
1432
|
const issue = new Issue.Pointer([i], new Issue.UnexpectedKey(ast, input[i]))
|
|
1503
|
-
if (
|
|
1504
|
-
if (issues) issues.push(issue)
|
|
1505
|
-
else issues = [issue]
|
|
1433
|
+
if (options.errors === "all") {
|
|
1434
|
+
if (state.issues) state.issues.push(issue)
|
|
1435
|
+
else state.issues = [issue]
|
|
1506
1436
|
} else {
|
|
1507
1437
|
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1508
1438
|
}
|
|
1509
1439
|
}
|
|
1510
1440
|
}
|
|
1511
|
-
if (issues) {
|
|
1512
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, issues))
|
|
1441
|
+
if (state.issues) {
|
|
1442
|
+
return yield* Effect.fail(new Issue.Composite(ast, oinput, state.issues))
|
|
1513
1443
|
}
|
|
1514
|
-
return Option.some(output)
|
|
1444
|
+
return Option.some(state.output)
|
|
1515
1445
|
})
|
|
1516
1446
|
}
|
|
1517
1447
|
/** @internal */
|
|
@@ -1527,6 +1457,74 @@ export class Arrays extends Base {
|
|
|
1527
1457
|
return "array"
|
|
1528
1458
|
}
|
|
1529
1459
|
}
|
|
1460
|
+
const parseArray = iterateEager<{
|
|
1461
|
+
readonly ast: AST
|
|
1462
|
+
readonly oinput: Option.Option<unknown>
|
|
1463
|
+
readonly len: number
|
|
1464
|
+
readonly getParser: (tailThreshold: number, index: number) => { readonly ast: AST; readonly parser: Parser.Parser }
|
|
1465
|
+
readonly tailThreshold: number
|
|
1466
|
+
readonly options: ParseOptions
|
|
1467
|
+
readonly output: Array<unknown>
|
|
1468
|
+
issues: Array<Issue.Issue> | undefined
|
|
1469
|
+
}, unknown>()({
|
|
1470
|
+
onItem(s, item, i) {
|
|
1471
|
+
const value = i < s.len ? Option.some(item) : Option.none()
|
|
1472
|
+
return s.getParser(s.tailThreshold, i).parser(value, s.options)
|
|
1473
|
+
},
|
|
1474
|
+
step(s, _, exit, i) {
|
|
1475
|
+
if (exit._tag === "Failure") {
|
|
1476
|
+
return wrapPropertyKeyIssue(s, s.ast, i, exit)
|
|
1477
|
+
} else if (exit.value._tag === "Some") {
|
|
1478
|
+
s.output[i] = exit.value.value
|
|
1479
|
+
} else {
|
|
1480
|
+
const p = s.getParser(s.tailThreshold, i)
|
|
1481
|
+
if (isOptional(p.ast)) return
|
|
1482
|
+
const issue = new Issue.Pointer([i], new Issue.MissingKey(p.ast.context?.annotations))
|
|
1483
|
+
if (s.options.errors === "all") {
|
|
1484
|
+
if (s.issues) s.issues.push(issue)
|
|
1485
|
+
else s.issues = [issue]
|
|
1486
|
+
} else {
|
|
1487
|
+
return Exit.fail(new Issue.Composite(s.ast, s.oinput, [issue]))
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
})
|
|
1492
|
+
|
|
1493
|
+
function resolveTailThreshold(
|
|
1494
|
+
inputLen: number,
|
|
1495
|
+
elementLen: number,
|
|
1496
|
+
tailLen: number
|
|
1497
|
+
) {
|
|
1498
|
+
return Math.max(elementLen, inputLen - tailLen)
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const resolveConcurrency = (value: number | "unbounded" | undefined) => {
|
|
1502
|
+
value = value === "unbounded" ? Infinity : value ?? 1
|
|
1503
|
+
return value > 1 ? { concurrency: value } : undefined
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
const wrapPropertyKeyIssue = (
|
|
1507
|
+
s: {
|
|
1508
|
+
readonly oinput: Option.Option<unknown>
|
|
1509
|
+
readonly options: ParseOptions
|
|
1510
|
+
issues: Array<Issue.Issue> | undefined
|
|
1511
|
+
},
|
|
1512
|
+
ast: AST,
|
|
1513
|
+
key: PropertyKey,
|
|
1514
|
+
exit: Exit.Failure<any, Issue.Issue>
|
|
1515
|
+
) => {
|
|
1516
|
+
const issueResult = Cause.findError(exit.cause)
|
|
1517
|
+
if (Result.isFailure(issueResult)) {
|
|
1518
|
+
return exit
|
|
1519
|
+
}
|
|
1520
|
+
const issue = new Issue.Pointer([key], issueResult.success)
|
|
1521
|
+
if (s.options.errors === "all") {
|
|
1522
|
+
if (s.issues) s.issues.push(issue)
|
|
1523
|
+
else s.issues = [issue]
|
|
1524
|
+
} else {
|
|
1525
|
+
return Exit.fail(new Issue.Composite(ast, s.oinput, [issue]))
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1530
1528
|
|
|
1531
1529
|
/**
|
|
1532
1530
|
* floating point or integer, with optional exponent
|
|
@@ -1720,12 +1718,11 @@ export class Objects extends Base {
|
|
|
1720
1718
|
const expectedKeys: Array<PropertyKey> = []
|
|
1721
1719
|
const expectedKeysSet = new Set<PropertyKey>()
|
|
1722
1720
|
const properties: Array<{
|
|
1723
|
-
readonly ps: PropertySignature
|
|
1721
|
+
readonly ps: PropertySignature | IndexSignature
|
|
1724
1722
|
readonly parser: Parser.Parser
|
|
1725
1723
|
readonly name: PropertyKey
|
|
1726
1724
|
readonly type: AST
|
|
1727
1725
|
}> = []
|
|
1728
|
-
const propertyCount = ast.propertySignatures.length
|
|
1729
1726
|
for (const ps of ast.propertySignatures) {
|
|
1730
1727
|
expectedKeys.push(ps.name)
|
|
1731
1728
|
expectedKeysSet.add(ps.name)
|
|
@@ -1743,6 +1740,54 @@ export class Objects extends Base {
|
|
|
1743
1740
|
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) {
|
|
1744
1741
|
return fromRefinement(ast, Predicate.isNotNullish)
|
|
1745
1742
|
}
|
|
1743
|
+
|
|
1744
|
+
const parseIndexes = indexCount > 0 ?
|
|
1745
|
+
iterateEager<{
|
|
1746
|
+
readonly oinput: Option.Option<unknown>
|
|
1747
|
+
readonly input: Record<PropertyKey, unknown>
|
|
1748
|
+
readonly options: ParseOptions
|
|
1749
|
+
readonly out: Record<PropertyKey, unknown>
|
|
1750
|
+
issues: Array<Issue.Issue> | undefined
|
|
1751
|
+
}, [key: PropertyKey, is: IndexSignature]>()({
|
|
1752
|
+
onItem: Effect.fnUntracedEager(function*(
|
|
1753
|
+
s,
|
|
1754
|
+
[key, is]
|
|
1755
|
+
) {
|
|
1756
|
+
const parserKey = recur(indexSignatureParameterFromString(is.parameter))
|
|
1757
|
+
const effKey = parserKey(Option.some(key), s.options)
|
|
1758
|
+
const exitKey = (effectIsExit(effKey) ? effKey : yield* Effect.exit(effKey)) as Exit.Exit<
|
|
1759
|
+
Option.Option<PropertyKey>,
|
|
1760
|
+
Issue.Issue
|
|
1761
|
+
>
|
|
1762
|
+
if (exitKey._tag === "Failure") {
|
|
1763
|
+
const eff = wrapPropertyKeyIssue(s, ast, key, exitKey)
|
|
1764
|
+
if (eff) yield* eff
|
|
1765
|
+
return
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
const value: Option.Option<unknown> = Option.some(s.input[key])
|
|
1769
|
+
const parserValue = recur(is.type)
|
|
1770
|
+
const effValue = parserValue(value, s.options)
|
|
1771
|
+
const exitValue = effectIsExit(effValue) ? effValue : yield* Effect.exit(effValue)
|
|
1772
|
+
if (exitValue._tag === "Failure") {
|
|
1773
|
+
const eff = wrapPropertyKeyIssue(s, ast, key, exitValue)
|
|
1774
|
+
if (eff) yield* eff
|
|
1775
|
+
return
|
|
1776
|
+
} else if (exitKey.value._tag === "Some" && exitValue.value._tag === "Some") {
|
|
1777
|
+
const k2 = exitKey.value.value
|
|
1778
|
+
const v2 = exitValue.value.value
|
|
1779
|
+
if (is.merge && is.merge.decode && Object.hasOwn(s.out, k2)) {
|
|
1780
|
+
const [k, v] = is.merge.decode.combine([k2, s.out[k2]], [k2, v2])
|
|
1781
|
+
internalRecord.set(s.out, k, v)
|
|
1782
|
+
} else {
|
|
1783
|
+
internalRecord.set(s.out, k2, v2)
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}),
|
|
1787
|
+
step: (_s, _, exit: Exit.Exit<void, Issue.Issue>) => exit._tag === "Failure" ? exit : undefined
|
|
1788
|
+
}) :
|
|
1789
|
+
undefined
|
|
1790
|
+
|
|
1746
1791
|
return Effect.fnUntracedEager(function*(oinput, options) {
|
|
1747
1792
|
if (oinput._tag === "None") {
|
|
1748
1793
|
return oinput
|
|
@@ -1755,7 +1800,14 @@ export class Objects extends Base {
|
|
|
1755
1800
|
}
|
|
1756
1801
|
|
|
1757
1802
|
const out: Record<PropertyKey, unknown> = {}
|
|
1758
|
-
|
|
1803
|
+
const state = {
|
|
1804
|
+
ast,
|
|
1805
|
+
oinput,
|
|
1806
|
+
input,
|
|
1807
|
+
out,
|
|
1808
|
+
issues: undefined as Arr.NonEmptyArray<Issue.Issue> | undefined,
|
|
1809
|
+
options
|
|
1810
|
+
}
|
|
1759
1811
|
const errorsAllOption = options.errors === "all"
|
|
1760
1812
|
const onExcessPropertyError = options.onExcessProperty === "error"
|
|
1761
1813
|
const onExcessPropertyPreserve = options.onExcessProperty === "preserve"
|
|
@@ -1773,10 +1825,10 @@ export class Objects extends Base {
|
|
|
1773
1825
|
if (onExcessPropertyError) {
|
|
1774
1826
|
const issue = new Issue.Pointer([key], new Issue.UnexpectedKey(ast, input[key]))
|
|
1775
1827
|
if (errorsAllOption) {
|
|
1776
|
-
if (issues) {
|
|
1777
|
-
issues.push(issue)
|
|
1828
|
+
if (state.issues) {
|
|
1829
|
+
state.issues.push(issue)
|
|
1778
1830
|
} else {
|
|
1779
|
-
issues = [issue]
|
|
1831
|
+
state.issues = [issue]
|
|
1780
1832
|
}
|
|
1781
1833
|
continue
|
|
1782
1834
|
} else {
|
|
@@ -1790,111 +1842,33 @@ export class Objects extends Base {
|
|
|
1790
1842
|
}
|
|
1791
1843
|
}
|
|
1792
1844
|
|
|
1845
|
+
const concurrency = resolveConcurrency(options?.concurrency)
|
|
1846
|
+
|
|
1793
1847
|
// ---------------------------------------------
|
|
1794
1848
|
// handle property signatures
|
|
1795
1849
|
// ---------------------------------------------
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
const value: Option.Option<unknown> = Object.hasOwn(input, p.name)
|
|
1799
|
-
? Option.some(input[p.name])
|
|
1800
|
-
: Option.none()
|
|
1801
|
-
const eff = p.parser(value, options)
|
|
1802
|
-
const exit = effectIsExit(eff) ? eff : yield* Effect.exit(eff)
|
|
1803
|
-
if (exit._tag === "Failure") {
|
|
1804
|
-
const issueProp = Cause.findError(exit.cause)
|
|
1805
|
-
if (Result.isFailure(issueProp)) {
|
|
1806
|
-
return yield* exit
|
|
1807
|
-
}
|
|
1808
|
-
const issue = new Issue.Pointer([p.name], issueProp.success)
|
|
1809
|
-
if (errorsAllOption) {
|
|
1810
|
-
if (issues) issues.push(issue)
|
|
1811
|
-
else issues = [issue]
|
|
1812
|
-
continue
|
|
1813
|
-
} else {
|
|
1814
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, [issue]))
|
|
1815
|
-
}
|
|
1816
|
-
} else if (exit.value._tag === "Some") {
|
|
1817
|
-
internalRecord.set(out, p.name, exit.value.value)
|
|
1818
|
-
} else if (!isOptional(p.type)) {
|
|
1819
|
-
const issue = new Issue.Pointer([p.name], new Issue.MissingKey(p.type.context?.annotations))
|
|
1820
|
-
if (errorsAllOption) {
|
|
1821
|
-
if (issues) issues.push(issue)
|
|
1822
|
-
else issues = [issue]
|
|
1823
|
-
continue
|
|
1824
|
-
} else {
|
|
1825
|
-
return yield* Effect.fail(
|
|
1826
|
-
new Issue.Composite(ast, oinput, [issue])
|
|
1827
|
-
)
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1850
|
+
const eff = parseProperties(state, properties, concurrency)
|
|
1851
|
+
if (eff) yield* eff
|
|
1831
1852
|
|
|
1832
1853
|
// ---------------------------------------------
|
|
1833
1854
|
// handle index signatures
|
|
1834
1855
|
// ---------------------------------------------
|
|
1835
|
-
if (
|
|
1856
|
+
if (parseIndexes) {
|
|
1857
|
+
const keyPairs = Arr.empty<[PropertyKey, IndexSignature]>()
|
|
1836
1858
|
for (let i = 0; i < indexCount; i++) {
|
|
1837
1859
|
const is = ast.indexSignatures[i]
|
|
1838
1860
|
const keys = getIndexSignatureKeys(input, is.parameter)
|
|
1839
1861
|
for (let j = 0; j < keys.length; j++) {
|
|
1840
1862
|
const key = keys[j]
|
|
1841
|
-
|
|
1842
|
-
const effKey = parserKey(Option.some(key), options)
|
|
1843
|
-
const exitKey = (effectIsExit(effKey) ? effKey : yield* Effect.exit(effKey)) as Exit.Exit<
|
|
1844
|
-
Option.Option<PropertyKey>,
|
|
1845
|
-
Issue.Issue
|
|
1846
|
-
>
|
|
1847
|
-
if (exitKey._tag === "Failure") {
|
|
1848
|
-
const issueKey = Cause.findError(exitKey.cause)
|
|
1849
|
-
if (Result.isFailure(issueKey)) {
|
|
1850
|
-
return yield* exitKey
|
|
1851
|
-
}
|
|
1852
|
-
const issue = new Issue.Pointer([key], issueKey.success)
|
|
1853
|
-
if (errorsAllOption) {
|
|
1854
|
-
if (issues) issues.push(issue)
|
|
1855
|
-
else issues = [issue]
|
|
1856
|
-
continue
|
|
1857
|
-
}
|
|
1858
|
-
return yield* Effect.fail(
|
|
1859
|
-
new Issue.Composite(ast, oinput, [issue])
|
|
1860
|
-
)
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
const value: Option.Option<unknown> = Option.some(input[key])
|
|
1864
|
-
const parserValue = recur(is.type)
|
|
1865
|
-
const effValue = parserValue(value, options)
|
|
1866
|
-
const exitValue = effectIsExit(effValue) ? effValue : yield* Effect.exit(effValue)
|
|
1867
|
-
if (exitValue._tag === "Failure") {
|
|
1868
|
-
const issueValue = Cause.findError(exitValue.cause)
|
|
1869
|
-
if (Result.isFailure(issueValue)) {
|
|
1870
|
-
return yield* exitValue
|
|
1871
|
-
}
|
|
1872
|
-
const issue = new Issue.Pointer([key], issueValue.success)
|
|
1873
|
-
if (errorsAllOption) {
|
|
1874
|
-
if (issues) issues.push(issue)
|
|
1875
|
-
else issues = [issue]
|
|
1876
|
-
continue
|
|
1877
|
-
} else {
|
|
1878
|
-
return yield* Effect.fail(
|
|
1879
|
-
new Issue.Composite(ast, oinput, [issue])
|
|
1880
|
-
)
|
|
1881
|
-
}
|
|
1882
|
-
} else if (exitKey.value._tag === "Some" && exitValue.value._tag === "Some") {
|
|
1883
|
-
const k2 = exitKey.value.value
|
|
1884
|
-
const v2 = exitValue.value.value
|
|
1885
|
-
if (is.merge && is.merge.decode && Object.hasOwn(out, k2)) {
|
|
1886
|
-
const [k, v] = is.merge.decode.combine([k2, out[k2]], [k2, v2])
|
|
1887
|
-
internalRecord.set(out, k, v)
|
|
1888
|
-
} else {
|
|
1889
|
-
internalRecord.set(out, k2, v2)
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1863
|
+
keyPairs.push([key, is])
|
|
1892
1864
|
}
|
|
1893
1865
|
}
|
|
1866
|
+
const eff = parseIndexes(state, keyPairs, concurrency)
|
|
1867
|
+
if (eff) yield* eff
|
|
1894
1868
|
}
|
|
1895
1869
|
|
|
1896
|
-
if (issues) {
|
|
1897
|
-
return yield* Effect.fail(new Issue.Composite(ast, oinput, issues))
|
|
1870
|
+
if (state.issues) {
|
|
1871
|
+
return yield* Effect.fail(new Issue.Composite(ast, oinput, state.issues))
|
|
1898
1872
|
}
|
|
1899
1873
|
if (options.propertyOrder === "original") {
|
|
1900
1874
|
// preserve input keys order
|
|
@@ -1947,6 +1921,56 @@ export class Objects extends Base {
|
|
|
1947
1921
|
}
|
|
1948
1922
|
}
|
|
1949
1923
|
|
|
1924
|
+
type ParsedProperty = {
|
|
1925
|
+
readonly ps: PropertySignature | IndexSignature
|
|
1926
|
+
readonly parser: Parser.Parser
|
|
1927
|
+
readonly name: PropertyKey
|
|
1928
|
+
readonly type: AST
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
const parseProperties = iterateEager<{
|
|
1932
|
+
readonly ast: AST
|
|
1933
|
+
readonly oinput: Option.Option<unknown>
|
|
1934
|
+
readonly input: Record<PropertyKey, unknown>
|
|
1935
|
+
readonly options: ParseOptions
|
|
1936
|
+
readonly out: Record<PropertyKey, unknown>
|
|
1937
|
+
issues: Array<Issue.Issue> | undefined
|
|
1938
|
+
}, ParsedProperty>()({
|
|
1939
|
+
onItem(
|
|
1940
|
+
s: {
|
|
1941
|
+
readonly oinput: Option.Option<unknown>
|
|
1942
|
+
readonly input: Record<PropertyKey, unknown>
|
|
1943
|
+
readonly options: ParseOptions
|
|
1944
|
+
readonly out: Record<PropertyKey, unknown>
|
|
1945
|
+
issues: Array<Issue.Issue> | undefined
|
|
1946
|
+
},
|
|
1947
|
+
p
|
|
1948
|
+
) {
|
|
1949
|
+
const value: Option.Option<unknown> = Object.hasOwn(s.input, p.name)
|
|
1950
|
+
? Option.some(s.input[p.name])
|
|
1951
|
+
: Option.none()
|
|
1952
|
+
return p.parser(value, s.options)
|
|
1953
|
+
},
|
|
1954
|
+
step(s, p, exit) {
|
|
1955
|
+
if (exit._tag === "Failure") {
|
|
1956
|
+
return wrapPropertyKeyIssue(s, s.ast, p.name, exit)
|
|
1957
|
+
} else if (exit.value._tag === "Some") {
|
|
1958
|
+
internalRecord.set(s.out, p.name, exit.value.value)
|
|
1959
|
+
} else if (!isOptional(p.type)) {
|
|
1960
|
+
const issue = new Issue.Pointer([p.name], new Issue.MissingKey(p.type.context?.annotations))
|
|
1961
|
+
if (s.options.errors === "all") {
|
|
1962
|
+
if (s.issues) s.issues.push(issue)
|
|
1963
|
+
else s.issues = [issue]
|
|
1964
|
+
return
|
|
1965
|
+
} else {
|
|
1966
|
+
return Exit.fail(
|
|
1967
|
+
new Issue.Composite(s.ast, s.oinput, [issue])
|
|
1968
|
+
)
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
})
|
|
1973
|
+
|
|
1950
1974
|
function mergeChecks(checks: Checks | undefined, b: AST): Checks | undefined {
|
|
1951
1975
|
if (!checks) {
|
|
1952
1976
|
return b.checks
|
|
@@ -2252,54 +2276,33 @@ export class Union<A extends AST = AST> extends Base {
|
|
|
2252
2276
|
getParser(recur: (ast: AST) => Parser.Parser): Parser.Parser {
|
|
2253
2277
|
// oxlint-disable-next-line @typescript-eslint/no-this-alias
|
|
2254
2278
|
const ast = this
|
|
2255
|
-
|
|
2279
|
+
|
|
2280
|
+
return (oinput, options) => {
|
|
2256
2281
|
if (oinput._tag === "None") {
|
|
2257
|
-
return oinput
|
|
2282
|
+
return Effect.succeed(oinput)
|
|
2258
2283
|
}
|
|
2259
2284
|
const input = oinput.value
|
|
2260
|
-
const oneOf = ast.mode === "oneOf"
|
|
2261
2285
|
const candidates = getCandidates(input, ast.types)
|
|
2262
|
-
let issues: Arr.NonEmptyArray<Issue.Issue> | undefined
|
|
2263
2286
|
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2287
|
+
const state = {
|
|
2288
|
+
ast,
|
|
2289
|
+
recur,
|
|
2290
|
+
oinput,
|
|
2291
|
+
input,
|
|
2268
2292
|
out: undefined,
|
|
2269
|
-
successes: []
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
const candidate = candidates[i]
|
|
2273
|
-
const parser = recur(candidate)
|
|
2274
|
-
const eff = parser(oinput, options)
|
|
2275
|
-
const exit = effectIsExit(eff) ? eff : yield* Effect.exit(eff)
|
|
2276
|
-
if (exit._tag === "Failure") {
|
|
2277
|
-
const issueResult = Cause.findError(exit.cause)
|
|
2278
|
-
if (Result.isFailure(issueResult)) {
|
|
2279
|
-
return yield* exit
|
|
2280
|
-
}
|
|
2281
|
-
if (issues) issues.push(issueResult.success)
|
|
2282
|
-
else issues = [issueResult.success]
|
|
2283
|
-
continue
|
|
2284
|
-
} else {
|
|
2285
|
-
if (tracking.out && oneOf) {
|
|
2286
|
-
tracking.successes.push(candidate)
|
|
2287
|
-
return yield* Effect.fail(new Issue.OneOf(ast, input, tracking.successes))
|
|
2288
|
-
}
|
|
2289
|
-
tracking.out = exit.value
|
|
2290
|
-
tracking.successes.push(candidate)
|
|
2291
|
-
if (!oneOf) {
|
|
2292
|
-
break
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2293
|
+
successes: [],
|
|
2294
|
+
issues: undefined as Arr.NonEmptyArray<Issue.Issue> | undefined,
|
|
2295
|
+
options
|
|
2295
2296
|
}
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
return yield* Effect.fail(new Issue.AnyOf(ast, input, issues ?? []))
|
|
2297
|
+
const concurrency = resolveConcurrency(options?.concurrency)
|
|
2298
|
+
const eff = parseUnion(state, candidates, concurrency)
|
|
2299
|
+
if (!eff) {
|
|
2300
|
+
return state.out ? Effect.succeed(state.out) : Effect.fail(new Issue.AnyOf(ast, input, state.issues ?? []))
|
|
2301
2301
|
}
|
|
2302
|
-
|
|
2302
|
+
return Effect.flatMap(eff, (_) => {
|
|
2303
|
+
return state.out ? Effect.succeed(state.out) : Effect.fail(new Issue.AnyOf(ast, input, state.issues ?? []))
|
|
2304
|
+
})
|
|
2305
|
+
}
|
|
2303
2306
|
}
|
|
2304
2307
|
/** @internal */
|
|
2305
2308
|
recur(recur: (ast: AST) => AST) {
|
|
@@ -2347,6 +2350,42 @@ export class Union<A extends AST = AST> extends Base {
|
|
|
2347
2350
|
}
|
|
2348
2351
|
}
|
|
2349
2352
|
|
|
2353
|
+
const parseUnion = iterateEager<{
|
|
2354
|
+
readonly recur: (ast: AST) => Parser.Parser
|
|
2355
|
+
readonly ast: Union
|
|
2356
|
+
readonly oinput: Option.Option<unknown>
|
|
2357
|
+
readonly input: unknown
|
|
2358
|
+
readonly options: ParseOptions
|
|
2359
|
+
out: Option.Option<unknown> | undefined
|
|
2360
|
+
successes: Array<AST>
|
|
2361
|
+
issues: Array<Issue.Issue> | undefined
|
|
2362
|
+
}, AST>()({
|
|
2363
|
+
onItem(s, ast) {
|
|
2364
|
+
const parser = s.recur(ast)
|
|
2365
|
+
return parser(s.oinput, s.options)
|
|
2366
|
+
},
|
|
2367
|
+
step(s, candidate, exit) {
|
|
2368
|
+
if (exit._tag === "Failure") {
|
|
2369
|
+
const issueResult = Cause.findError(exit.cause)
|
|
2370
|
+
if (Result.isFailure(issueResult)) {
|
|
2371
|
+
return exit
|
|
2372
|
+
}
|
|
2373
|
+
if (s.issues) s.issues.push(issueResult.success)
|
|
2374
|
+
else s.issues = [issueResult.success]
|
|
2375
|
+
} else {
|
|
2376
|
+
if (s.out && s.ast.mode === "oneOf") {
|
|
2377
|
+
s.successes.push(candidate)
|
|
2378
|
+
return Exit.fail(new Issue.OneOf(s.ast, s.input, s.successes))
|
|
2379
|
+
}
|
|
2380
|
+
s.out = exit.value
|
|
2381
|
+
s.successes.push(candidate)
|
|
2382
|
+
if (s.ast.mode === "anyOf") {
|
|
2383
|
+
return Exit.void
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
})
|
|
2388
|
+
|
|
2350
2389
|
const nonFiniteLiterals = new Union([
|
|
2351
2390
|
new Literal("Infinity"),
|
|
2352
2391
|
new Literal("-Infinity"),
|
|
@@ -2550,19 +2589,12 @@ export type Check<T> = Filter<T> | FilterGroup<T>
|
|
|
2550
2589
|
|
|
2551
2590
|
/** @internal */
|
|
2552
2591
|
export function makeFilter<T>(
|
|
2553
|
-
filter: (
|
|
2554
|
-
input: T,
|
|
2555
|
-
ast: AST,
|
|
2556
|
-
options: ParseOptions
|
|
2557
|
-
) => undefined | boolean | string | Issue.Issue | {
|
|
2558
|
-
readonly path: ReadonlyArray<PropertyKey>
|
|
2559
|
-
readonly message: string
|
|
2560
|
-
},
|
|
2592
|
+
filter: (input: T, ast: AST, options: ParseOptions) => Schema.FilterOutput,
|
|
2561
2593
|
annotations?: Schema.Annotations.Filter | undefined,
|
|
2562
2594
|
aborted: boolean = false
|
|
2563
2595
|
): Filter<T> {
|
|
2564
2596
|
return new Filter(
|
|
2565
|
-
(input, ast, options) => Issue.make(input, filter(input, ast, options)),
|
|
2597
|
+
(input, ast, options) => Issue.make(input, ast, filter(input, ast, options)),
|
|
2566
2598
|
annotations,
|
|
2567
2599
|
aborted
|
|
2568
2600
|
)
|
|
@@ -3372,7 +3404,14 @@ export const resolveDescription: (ast: AST) => string | undefined = InternalAnno
|
|
|
3372
3404
|
* @internal
|
|
3373
3405
|
*/
|
|
3374
3406
|
export function isJson(u: unknown): u is Schema.Json {
|
|
3375
|
-
|
|
3407
|
+
// `onPath` is the current recursion stack: nodes between the root and the
|
|
3408
|
+
// one being visited. A hit here means we looped back to an ancestor — a
|
|
3409
|
+
// real cycle, not a DAG — so the value is not JSON.
|
|
3410
|
+
const onPath = new Set<unknown>()
|
|
3411
|
+
// `validated` memoizes subtrees we've already fully checked. Without it, a
|
|
3412
|
+
// diamond-shaped DAG (same node reached through multiple parents) would be
|
|
3413
|
+
// re-traversed once per parent, which is exponential in the nesting depth.
|
|
3414
|
+
const validated = new Set<unknown>()
|
|
3376
3415
|
return recur(u)
|
|
3377
3416
|
|
|
3378
3417
|
function recur(u: unknown): boolean {
|
|
@@ -3385,14 +3424,23 @@ export function isJson(u: unknown): u is Schema.Json {
|
|
|
3385
3424
|
if (typeof u !== "object" || u === undefined) {
|
|
3386
3425
|
return false
|
|
3387
3426
|
}
|
|
3388
|
-
if (
|
|
3427
|
+
if (onPath.has(u)) {
|
|
3389
3428
|
return false
|
|
3390
3429
|
}
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
return u.every(recur)
|
|
3430
|
+
if (validated.has(u)) {
|
|
3431
|
+
return true
|
|
3394
3432
|
}
|
|
3395
|
-
|
|
3433
|
+
onPath.add(u)
|
|
3434
|
+
const ok = Array.isArray(u)
|
|
3435
|
+
? u.every(recur)
|
|
3436
|
+
: Object.keys(u).every((key) => recur((u as Record<string, unknown>)[key]))
|
|
3437
|
+
// Pop on exit so siblings reaching the same node via a different path
|
|
3438
|
+
// don't see it as an ancestor (that would reject valid DAGs).
|
|
3439
|
+
onPath.delete(u)
|
|
3440
|
+
if (ok) {
|
|
3441
|
+
validated.add(u)
|
|
3442
|
+
}
|
|
3443
|
+
return ok
|
|
3396
3444
|
}
|
|
3397
3445
|
}
|
|
3398
3446
|
|