@evolu/common 8.10.0 → 8.11.0
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/src/Config.d.ts +22 -22
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +92 -18
- package/dist/src/Fs.d.ts.map +1 -1
- package/dist/src/Fs.js +2 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +63 -9
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +106 -9
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +55 -4
- package/dist/src/Type.d.ts +1455 -1310
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1274 -517
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +6 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +336 -211
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +102 -15
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +94 -16
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +118 -38
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +335 -21
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +192 -14
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +81 -20
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +403 -81
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/Config.test.ts +2 -6
- package/src/Config.ts +133 -133
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.ts +92 -18
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Object.test.ts +27 -12
- package/src/Object.ts +5 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +137 -17
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.ts +106 -9
- package/src/Type.test.ts +946 -1028
- package/src/Type.ts +4195 -3136
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +15 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +686 -21
- package/src/local-first/Evolu.ts +450 -228
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +617 -10
- package/src/local-first/Protocol.ts +196 -72
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +363 -24
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +218 -32
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +434 -118
- package/src/local-first/index.ts +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- package/src/local-first/LocalAuth.ts +0 -457
package/dist/src/Type.js
CHANGED
|
@@ -460,7 +460,7 @@ import { createMutableRecord, getObjectKind, isPlainObject, } from "./Object.js"
|
|
|
460
460
|
import { hasNodeBuffer } from "./Platform.js";
|
|
461
461
|
import { err, flatMapResult, getOk, getOrNull, ok, trySync, } from "./Result.js";
|
|
462
462
|
import { safelyStringifyUnknownValue } from "./String.js";
|
|
463
|
-
import {
|
|
463
|
+
import { isInstance, } from "./Types.js";
|
|
464
464
|
/**
|
|
465
465
|
* Converts an error from a {@link Type} into formatted issues with paths.
|
|
466
466
|
*
|
|
@@ -497,7 +497,7 @@ import { instance, isInstance, } from "./Types.js";
|
|
|
497
497
|
*/
|
|
498
498
|
export const typeErrorToIssues = (type, error) => {
|
|
499
499
|
const runtimeType = type;
|
|
500
|
-
return runtimeType[getRuntimeTypeIssuesSymbol](error, "all").map((issue) => ({
|
|
500
|
+
return runtimeType[getRuntimeTypeIssuesSymbol](error, "all", []).map((issue) => ({
|
|
501
501
|
path: issue.path,
|
|
502
502
|
message: formatRuntimeTypeIssue(issue),
|
|
503
503
|
}));
|
|
@@ -506,50 +506,76 @@ const outputValidationSymbol =
|
|
|
506
506
|
/*#__PURE__*/ globalThis.Symbol();
|
|
507
507
|
const getRuntimeTypeIssuesSymbol =
|
|
508
508
|
/*#__PURE__*/ globalThis.Symbol();
|
|
509
|
-
|
|
509
|
+
// Copying a short path in a loop is about twice as fast as spreading it.
|
|
510
|
+
const appendIssuePath = (path, key) => {
|
|
511
|
+
const issuePath = createMutableArray(path.length + 1);
|
|
512
|
+
for (let index = 0; index < path.length; index++) {
|
|
513
|
+
issuePath[index] = path[index];
|
|
514
|
+
}
|
|
515
|
+
issuePath[path.length] = key;
|
|
516
|
+
return issuePath;
|
|
517
|
+
};
|
|
518
|
+
const singleRuntimeTypeIssue = (formatterName, error, defaultFormatter, path) => [
|
|
510
519
|
{ name: formatterName, error, path, formatError: defaultFormatter },
|
|
511
520
|
];
|
|
512
|
-
const
|
|
513
|
-
...issue,
|
|
514
|
-
path: [key, ...issue.path],
|
|
515
|
-
}));
|
|
516
|
-
const createCollectionRuntimeTypeIssues = (name, issuesKind, defaultFormatter, getNestedType) => (error, mode) => {
|
|
521
|
+
const createCollectionRuntimeTypeIssues = (name, issuesKind, defaultFormatter, getNestedType) => (error, mode, path) => {
|
|
517
522
|
const reason = error.reason;
|
|
518
523
|
if (reason.kind !== issuesKind) {
|
|
519
|
-
return singleRuntimeTypeIssue(name, error, defaultFormatter);
|
|
524
|
+
return singleRuntimeTypeIssue(name, error, defaultFormatter, path);
|
|
520
525
|
}
|
|
521
526
|
const allIssues = reason.issues;
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
+
const issueCount = mode === "first" ? 1 : allIssues.length;
|
|
528
|
+
const result = [];
|
|
529
|
+
for (let index = 0; index < issueCount; index++) {
|
|
530
|
+
const issue = allIssues[index];
|
|
531
|
+
const issuePath = appendIssuePath(path, "key" in issue ? issue.key : issue.index);
|
|
532
|
+
if (issue.error === undefined) {
|
|
533
|
+
result.push({
|
|
534
|
+
name,
|
|
535
|
+
error: mode === "first"
|
|
536
|
+
? error
|
|
537
|
+
: {
|
|
538
|
+
type: name,
|
|
539
|
+
reason: { kind: issuesKind, issues: [issue] },
|
|
540
|
+
},
|
|
541
|
+
path: issuePath,
|
|
542
|
+
formatError: defaultFormatter,
|
|
543
|
+
});
|
|
544
|
+
continue;
|
|
527
545
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
546
|
+
const nestedIssues = getNestedType(issue)[getRuntimeTypeIssuesSymbol](issue.error, mode, issuePath);
|
|
547
|
+
// Nested issues are a new array, so one visited issue returns them.
|
|
548
|
+
if (issueCount === 1)
|
|
549
|
+
return nestedIssues;
|
|
550
|
+
for (const nestedIssue of nestedIssues)
|
|
551
|
+
result.push(nestedIssue);
|
|
552
|
+
}
|
|
553
|
+
return result;
|
|
535
554
|
};
|
|
536
555
|
const formatDefaultRuntimeTypeIssue = (issue) => issue.formatError(issue.error);
|
|
537
556
|
const formatRuntimeTypeIssue = (issue, formatIssue = formatDefaultRuntimeTypeIssue) => {
|
|
538
|
-
|
|
557
|
+
let message = formatIssue(issue);
|
|
539
558
|
if (issue.alternatives === undefined)
|
|
540
|
-
return
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
559
|
+
return message;
|
|
560
|
+
// Each alternative issue starts a "- " line; continuation lines of a
|
|
561
|
+
// multi-line message are indented.
|
|
562
|
+
for (const { index, name, issues } of issue.alternatives) {
|
|
563
|
+
for (const alternativeIssue of issues) {
|
|
564
|
+
let path = "";
|
|
565
|
+
for (const key of alternativeIssue.path) {
|
|
566
|
+
path +=
|
|
567
|
+
typeof key === "string"
|
|
568
|
+
? `[${JSON.stringify(key)}]`
|
|
569
|
+
: `[${globalThis.String(key)}]`;
|
|
570
|
+
}
|
|
571
|
+
const alternativeMessage = formatRuntimeTypeIssue(alternativeIssue, formatIssue);
|
|
572
|
+
// replaceAll is slow even without a match.
|
|
573
|
+
message += `\n- ${index}: ${name}${path}: ${alternativeMessage.includes("\n")
|
|
574
|
+
? alternativeMessage.replaceAll("\n", "\n ")
|
|
575
|
+
: alternativeMessage}`;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return message;
|
|
553
579
|
};
|
|
554
580
|
export function assertType(type, value) {
|
|
555
581
|
if (type === undefined)
|
|
@@ -746,8 +772,12 @@ const withFormatError = (source, formatIssue, localizedTypeBySource) => {
|
|
|
746
772
|
})),
|
|
747
773
|
}),
|
|
748
774
|
});
|
|
749
|
-
const getTypeIssues = (error, mode) => source[getRuntimeTypeIssuesSymbol](error, mode).map(localizeIssue);
|
|
750
|
-
const derived = createTypeNode(source.name, parent, source.fromUnknown, source.is, source[outputValidationSymbol], source[fromSymbol], source[encoderSymbol].parent ?? source[encoderSymbol], getTypeIssues,
|
|
775
|
+
const getTypeIssues = (error, mode, path) => source[getRuntimeTypeIssuesSymbol](error, mode, path).map(localizeIssue);
|
|
776
|
+
const derived = createTypeNode(source.name, parent, source.fromUnknown, source.is, source[outputValidationSymbol], source[fromSymbol], source[encoderSymbol].parent ?? source[encoderSymbol], getTypeIssues, {
|
|
777
|
+
formatIssue,
|
|
778
|
+
check: source[checkSymbol],
|
|
779
|
+
refinements: source[refinementsSymbol],
|
|
780
|
+
});
|
|
751
781
|
// Localization changes error rendering, not conversion semantics. Preserve
|
|
752
782
|
// specialized public input operations such as json's single-parse Json
|
|
753
783
|
// boundary. Private operation chains on derived still support composition.
|
|
@@ -795,9 +825,17 @@ const localizeTypeReflection = (value, formatIssue, localizedTypeBySource) => {
|
|
|
795
825
|
return localized;
|
|
796
826
|
};
|
|
797
827
|
export function createType(name, fromUnknownOrParent, fromParentOrFormatError, formatError) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
828
|
+
if (typeof fromUnknownOrParent === "function") {
|
|
829
|
+
const fromUnknown = assertRefinementIdentity(fromUnknownOrParent);
|
|
830
|
+
return createRootType(name, fromUnknown, fromParentOrFormatError, {
|
|
831
|
+
// The asserted identity makes this an identity Type.
|
|
832
|
+
check: (value) => {
|
|
833
|
+
const result = fromUnknown(value);
|
|
834
|
+
return result.ok ? undefined : result.error;
|
|
835
|
+
},
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
return createChildType(name, fromUnknownOrParent, assertRefinementIdentity(fromParentOrFormatError), formatError);
|
|
801
839
|
}
|
|
802
840
|
const assertRefinementIdentity = (refinement) => (value) => {
|
|
803
841
|
const result = refinement(value);
|
|
@@ -847,15 +885,27 @@ const assertRefinementIdentity = (refinement) => (value) => {
|
|
|
847
885
|
*
|
|
848
886
|
* @group Construction
|
|
849
887
|
*/
|
|
850
|
-
export const createTypeWithError = (name, type, mapError, formatError) =>
|
|
851
|
-
const
|
|
852
|
-
return
|
|
853
|
-
|
|
854
|
-
|
|
888
|
+
export const createTypeWithError = (name, type, mapError, formatError) => {
|
|
889
|
+
const check = type[checkSymbol];
|
|
890
|
+
return createRootType(name, (value, options) => {
|
|
891
|
+
const result = type.fromUnknown(value, options);
|
|
892
|
+
return result.ok ? result : err(mapError(result.error, value));
|
|
893
|
+
}, formatError, {
|
|
894
|
+
check: check &&
|
|
895
|
+
((value, options) => {
|
|
896
|
+
const error = check(value, options);
|
|
897
|
+
return error === undefined ? undefined : mapError(error, value);
|
|
898
|
+
}),
|
|
899
|
+
});
|
|
900
|
+
};
|
|
901
|
+
const createRootType = (name, fromUnknown, formatError, { getTypeIssues, isOutput, check, } = {}) => {
|
|
855
902
|
const runtimeFormatError = formatError;
|
|
856
903
|
const runtimeGetTypeIssues = getTypeIssues ??
|
|
857
|
-
((error) => singleRuntimeTypeIssue(error.type === "TypeOf" ? name : error.type, error, runtimeFormatError));
|
|
858
|
-
const is =
|
|
904
|
+
((error, _mode, path) => singleRuntimeTypeIssue(error.type === "TypeOf" ? name : error.type, error, runtimeFormatError, path));
|
|
905
|
+
const is = isOutput ??
|
|
906
|
+
(check
|
|
907
|
+
? (value) => check(value, firstValidationOptions) === undefined
|
|
908
|
+
: (value) => fromUnknown(value).ok);
|
|
859
909
|
const from = (value, options = firstValidationOptions) => {
|
|
860
910
|
assertTypeOutput(name, is, fromUnknown, value, options);
|
|
861
911
|
return ok(value);
|
|
@@ -869,7 +919,7 @@ const createRootType = (name, fromUnknown, formatError, getTypeIssues) => {
|
|
|
869
919
|
return value;
|
|
870
920
|
};
|
|
871
921
|
return {
|
|
872
|
-
|
|
922
|
+
"~evolu/instance": "Type",
|
|
873
923
|
name,
|
|
874
924
|
parent: null,
|
|
875
925
|
fromUnknown,
|
|
@@ -879,32 +929,134 @@ const createRootType = (name, fromUnknown, formatError, getTypeIssues) => {
|
|
|
879
929
|
to,
|
|
880
930
|
orThrow,
|
|
881
931
|
orNull: to,
|
|
882
|
-
"~standard":
|
|
932
|
+
"~standard": {
|
|
933
|
+
version: 1,
|
|
934
|
+
vendor: "evolu",
|
|
935
|
+
validate: (value) => {
|
|
936
|
+
const result = fromUnknown(value, allValidationOptions);
|
|
937
|
+
return result.ok
|
|
938
|
+
? { value: result.value }
|
|
939
|
+
: {
|
|
940
|
+
issues: runtimeGetTypeIssues(result.error, "all", []).map((issue) => ({
|
|
941
|
+
message: formatRuntimeTypeIssue(issue),
|
|
942
|
+
path: issue.path,
|
|
943
|
+
})),
|
|
944
|
+
};
|
|
945
|
+
},
|
|
946
|
+
},
|
|
883
947
|
[outputValidationSymbol]: fromUnknown,
|
|
884
948
|
[fromSymbol]: ok,
|
|
885
949
|
[encoderSymbol]: identity,
|
|
886
950
|
[getRuntimeTypeIssuesSymbol]: runtimeGetTypeIssues,
|
|
951
|
+
[checkSymbol]: check,
|
|
952
|
+
[refinementsSymbol]: undefined,
|
|
953
|
+
[arrayTypeSymbol]: undefined,
|
|
954
|
+
[setTypeSymbol]: undefined,
|
|
887
955
|
};
|
|
888
956
|
};
|
|
889
957
|
const createChildType = (name, parent, fromParent, formatOwnError,
|
|
890
958
|
// Wrapped child errors can delegate their structured issues.
|
|
891
|
-
getTypeIssuesOverride
|
|
959
|
+
getTypeIssuesOverride,
|
|
960
|
+
// A refinement `fromParent` only checks the parent Output and returns
|
|
961
|
+
// `ok()`, so the child can return the parent Result unchanged.
|
|
962
|
+
isRefinement = false) => {
|
|
892
963
|
const typeParent = parent;
|
|
893
964
|
const defaultFormatter = formatOwnError;
|
|
894
965
|
const getTypeIssues = getTypeIssuesOverride ??
|
|
895
966
|
(formatOwnError
|
|
896
|
-
? (error, mode) => error.type === name
|
|
897
|
-
? singleRuntimeTypeIssue(name, error, defaultFormatter)
|
|
898
|
-
: typeParent[getRuntimeTypeIssuesSymbol](error, mode)
|
|
967
|
+
? (error, mode, path) => error.type === name
|
|
968
|
+
? singleRuntimeTypeIssue(name, error, defaultFormatter, path)
|
|
969
|
+
: typeParent[getRuntimeTypeIssuesSymbol](error, mode, path)
|
|
899
970
|
: typeParent[getRuntimeTypeIssuesSymbol]);
|
|
971
|
+
// Validators get only the value and no receiver (refinements are read into
|
|
972
|
+
// a local before a call), so default parameters, `arguments`, and `this`
|
|
973
|
+
// behave as in a direct call.
|
|
900
974
|
const validate = fromParent;
|
|
901
|
-
|
|
902
|
-
const
|
|
975
|
+
// Returning the parent Result avoids allocating an equal Ok per layer.
|
|
976
|
+
const mapFromParent = isRefinement
|
|
977
|
+
? (result) => {
|
|
978
|
+
if (!result.ok)
|
|
979
|
+
return result;
|
|
980
|
+
const refined = validate(result.value);
|
|
981
|
+
return refined.ok ? result : refined;
|
|
982
|
+
}
|
|
983
|
+
: (result) => result.ok ? validate(result.value) : result;
|
|
984
|
+
// Parent operations are read once, so validation skips repeated property
|
|
985
|
+
// lookups on heterogeneous Type nodes.
|
|
986
|
+
const parentFromUnknown = typeParent.fromUnknown;
|
|
987
|
+
const parentValidateOutput = typeParent[outputValidationSymbol];
|
|
988
|
+
const parentFrom = typeParent[fromSymbol];
|
|
989
|
+
const fromParentOperation = (value, options = firstValidationOptions) => mapFromParent(parentFrom(value, options));
|
|
990
|
+
if (parentFrom.parent) {
|
|
991
|
+
fromParentOperation.parent = mapRuntimeOperations(parentFrom.parent, (operation) => mapRuntimeResult(operation, mapFromParent));
|
|
992
|
+
}
|
|
903
993
|
const from = createFromOperation(fromParentOperation);
|
|
904
|
-
|
|
905
|
-
|
|
994
|
+
// Refinement children form a chain whose refinements run in order after
|
|
995
|
+
// its base, the nearest ancestor that is not a refinement, so nested
|
|
996
|
+
// refinements need no call per level.
|
|
997
|
+
const parentRefinements = isRefinement
|
|
998
|
+
? typeParent[refinementsSymbol]
|
|
999
|
+
: undefined;
|
|
1000
|
+
const refinements = parentRefinements
|
|
1001
|
+
? [...parentRefinements, validate]
|
|
1002
|
+
: [validate];
|
|
1003
|
+
const refinementCount = refinements.length;
|
|
1004
|
+
let base = typeParent;
|
|
1005
|
+
if (isRefinement) {
|
|
1006
|
+
while (base[refinementsSymbol])
|
|
1007
|
+
base = base.parent;
|
|
1008
|
+
}
|
|
1009
|
+
const isBase = base.is;
|
|
1010
|
+
const is = (value) => {
|
|
1011
|
+
if (!isBase(value))
|
|
1012
|
+
return false;
|
|
1013
|
+
for (let index = 0; index < refinementCount; index++) {
|
|
1014
|
+
const refine = refinements[index];
|
|
1015
|
+
if (!refine(value).ok)
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
return true;
|
|
1019
|
+
};
|
|
1020
|
+
const baseCheck = base[checkSymbol];
|
|
1021
|
+
// A child returns its parent Output unchanged, so a child of an identity
|
|
1022
|
+
// Type is one too.
|
|
1023
|
+
const check = baseCheck &&
|
|
1024
|
+
((value, options) => {
|
|
1025
|
+
const error = baseCheck(value, options);
|
|
1026
|
+
if (error !== undefined)
|
|
1027
|
+
return error;
|
|
1028
|
+
for (let index = 0; index < refinementCount; index++) {
|
|
1029
|
+
const refine = refinements[index];
|
|
1030
|
+
const result = refine(value);
|
|
1031
|
+
if (!result.ok)
|
|
1032
|
+
return result.error;
|
|
1033
|
+
}
|
|
1034
|
+
return undefined;
|
|
1035
|
+
});
|
|
1036
|
+
const validateBase = base.fromUnknown;
|
|
1037
|
+
// Like nested refinement children, it returns the base Result or the first
|
|
1038
|
+
// refinement Err. A single refinement is faster with its direct call.
|
|
1039
|
+
const fromUnknown = refinementCount > 1
|
|
1040
|
+
? (value, options = firstValidationOptions) => {
|
|
1041
|
+
const result = validateBase(value, options);
|
|
1042
|
+
if (!result.ok)
|
|
1043
|
+
return result;
|
|
1044
|
+
for (let index = 0; index < refinementCount; index++) {
|
|
1045
|
+
const refine = refinements[index];
|
|
1046
|
+
const refined = refine(result.value);
|
|
1047
|
+
if (!refined.ok)
|
|
1048
|
+
return refined;
|
|
1049
|
+
}
|
|
1050
|
+
return result;
|
|
1051
|
+
}
|
|
1052
|
+
: (value, options = firstValidationOptions) => mapFromParent(parentFromUnknown(value, options));
|
|
1053
|
+
// A root parent validates its Output with fromUnknown, and so does a child
|
|
1054
|
+
// of such a parent.
|
|
1055
|
+
const validateOutput = parentValidateOutput === parentFromUnknown
|
|
1056
|
+
? fromUnknown
|
|
1057
|
+
: (value, options = firstValidationOptions) => mapFromParent(parentValidateOutput(value, options));
|
|
906
1058
|
const to = identity;
|
|
907
|
-
return createTypeNode(name, typeParent, fromUnknown,
|
|
1059
|
+
return createTypeNode(name, typeParent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { check, refinements: isRefinement ? refinements : undefined });
|
|
908
1060
|
};
|
|
909
1061
|
export function transform(name, parent, output, { from: transformFrom, to: transformTo, }, formatOwnError) {
|
|
910
1062
|
const typeParent = parent;
|
|
@@ -925,20 +1077,20 @@ export function transform(name, parent, output, { from: transformFrom, to: trans
|
|
|
925
1077
|
return parentValue;
|
|
926
1078
|
};
|
|
927
1079
|
const defaultFormatter = formatOwnError;
|
|
928
|
-
const getTypeIssues = (error, mode) => {
|
|
1080
|
+
const getTypeIssues = (error, mode, path) => {
|
|
929
1081
|
if (error.type !== name) {
|
|
930
|
-
return typeParent[getRuntimeTypeIssuesSymbol](error, mode);
|
|
1082
|
+
return typeParent[getRuntimeTypeIssuesSymbol](error, mode, path);
|
|
931
1083
|
}
|
|
932
1084
|
if ("outputError" in error) {
|
|
933
|
-
return typeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode);
|
|
1085
|
+
return typeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode, path);
|
|
934
1086
|
}
|
|
935
|
-
return singleRuntimeTypeIssue(name, error, defaultFormatter);
|
|
1087
|
+
return singleRuntimeTypeIssue(name, error, defaultFormatter, path);
|
|
936
1088
|
};
|
|
937
1089
|
const validateOutput = (value, options) => {
|
|
938
1090
|
const result = typeOutput[outputValidationSymbol](value, options);
|
|
939
1091
|
return result.ok ? result : err({ type: name, outputError: result.error });
|
|
940
1092
|
};
|
|
941
|
-
return createTypeNode(name, typeParent, fromUnknown, typeOutput.is, validateOutput, from, to, getTypeIssues, { output: typeOutput });
|
|
1093
|
+
return createTypeNode(name, typeParent, fromUnknown, typeOutput.is, validateOutput, from, to, getTypeIssues, { additionalProperties: { output: typeOutput } });
|
|
942
1094
|
}
|
|
943
1095
|
const firstValidationOptions = { errors: "first" };
|
|
944
1096
|
const allValidationOptions = { errors: "all" };
|
|
@@ -948,6 +1100,33 @@ const fromSymbol =
|
|
|
948
1100
|
/*#__PURE__*/ globalThis.Symbol();
|
|
949
1101
|
const templateLiteralSyntaxSymbol =
|
|
950
1102
|
/*#__PURE__*/ globalThis.Symbol();
|
|
1103
|
+
// A Type node caches the Array and Set Types of itself as an element in these
|
|
1104
|
+
// own properties; a Type whose slot cannot be written, such as a frozen one,
|
|
1105
|
+
// uses the typeByElement WeakMaps. A WeakMap alone keeps each entry's value
|
|
1106
|
+
// alive through young-generation GCs, so every schema created at runtime would
|
|
1107
|
+
// be promoted.
|
|
1108
|
+
const arrayTypeSymbol = /*#__PURE__*/ globalThis.Symbol();
|
|
1109
|
+
const setTypeSymbol = /*#__PURE__*/ globalThis.Symbol();
|
|
1110
|
+
const checkSymbol =
|
|
1111
|
+
/*#__PURE__*/ globalThis.Symbol();
|
|
1112
|
+
const refinementsSymbol =
|
|
1113
|
+
/*#__PURE__*/ globalThis.Symbol();
|
|
1114
|
+
// The `fromUnknown` of an identity Type.
|
|
1115
|
+
const checkToFromUnknown = (check) => (value, options = firstValidationOptions) => {
|
|
1116
|
+
const error = check(value, options);
|
|
1117
|
+
return error === undefined ? ok(value) : err(error);
|
|
1118
|
+
};
|
|
1119
|
+
// The checks of identity Types, or `undefined` if any Type is not one.
|
|
1120
|
+
const getRuntimeChecks = (types) => {
|
|
1121
|
+
const checks = [];
|
|
1122
|
+
for (const type of types) {
|
|
1123
|
+
const check = type[checkSymbol];
|
|
1124
|
+
if (check === undefined)
|
|
1125
|
+
return undefined;
|
|
1126
|
+
checks.push(check);
|
|
1127
|
+
}
|
|
1128
|
+
return checks;
|
|
1129
|
+
};
|
|
951
1130
|
const mapRuntimeResult = (operation, map) => (value, options = firstValidationOptions) => map(operation(value, options), options);
|
|
952
1131
|
const createRuntimeOrThrow = (fromInput, formatError) => mapRuntimeResult(fromInput, (result) => {
|
|
953
1132
|
if (result.ok)
|
|
@@ -969,37 +1148,56 @@ const createFromOperation = (parent) => {
|
|
|
969
1148
|
from.parent = parent;
|
|
970
1149
|
return from;
|
|
971
1150
|
};
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
to.parent = toParent;
|
|
980
|
-
return to;
|
|
981
|
-
};
|
|
1151
|
+
// Composite Types read these operations of their Types on first use, so
|
|
1152
|
+
// construction allocates no arrays and validation skips property lookups on
|
|
1153
|
+
// heterogeneous Type nodes.
|
|
1154
|
+
const getFromUnknown = (type) => type.fromUnknown;
|
|
1155
|
+
const getValidateOutput = (type) => type[outputValidationSymbol];
|
|
1156
|
+
const getIs = (type) => type.is;
|
|
982
1157
|
function getTerminalRuntimeNode(node) {
|
|
983
1158
|
while (node.parent != null)
|
|
984
1159
|
node = node.parent;
|
|
985
1160
|
return node;
|
|
986
1161
|
}
|
|
987
|
-
const createTypeNode = (name, parent, fromUnknown, is, validateOutput, from, ownTo, getTypeIssues, additionalProperties, formatIssue = formatDefaultRuntimeTypeIssue) => {
|
|
1162
|
+
const createTypeNode = (name, parent, fromUnknown, is, validateOutput, from, ownTo, getTypeIssues, { additionalProperties, formatIssue = formatDefaultRuntimeTypeIssue, check, refinements, } = {}) => {
|
|
988
1163
|
const runtimeParent = parent;
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
1164
|
+
const parentTo = runtimeParent?.[encoderSymbol];
|
|
1165
|
+
// Operations defined directly here share this call's closure context, so a
|
|
1166
|
+
// Type node does not allocate one context per helper.
|
|
1167
|
+
let to = ownTo;
|
|
1168
|
+
if (parentTo) {
|
|
1169
|
+
to = (value) => parentTo(ownTo(value));
|
|
1170
|
+
const toParent = (value, options = firstValidationOptions) => ownTo(value, options);
|
|
1171
|
+
if (parentTo.parent) {
|
|
1172
|
+
toParent.parent = mapRuntimeOperations(parentTo.parent, (operation) => (value) => operation(ownTo(value)));
|
|
1173
|
+
}
|
|
1174
|
+
to.parent = toParent;
|
|
1175
|
+
}
|
|
992
1176
|
const runtimeFormatError = runtimeParent?.[getRuntimeTypeIssuesSymbol] === getTypeIssues
|
|
993
1177
|
? runtimeParent.formatError
|
|
994
|
-
: (error) => formatRuntimeTypeIssue(getTypeIssues(error, "first")[0], formatIssue);
|
|
995
|
-
const typedFrom =
|
|
1178
|
+
: (error) => formatRuntimeTypeIssue(getTypeIssues(error, "first", [])[0], formatIssue);
|
|
1179
|
+
const typedFrom = (value, options = firstValidationOptions) => {
|
|
1180
|
+
assertTypeOutput(name, is, validateOutput, value, options);
|
|
1181
|
+
return from(value, options);
|
|
1182
|
+
};
|
|
1183
|
+
if (from.parent) {
|
|
1184
|
+
typedFrom.parent = addRuntimeAssertions(runtimeParent, from.parent);
|
|
1185
|
+
}
|
|
996
1186
|
const fromInput = getTerminalRuntimeNode(typedFrom);
|
|
997
|
-
const typedTo =
|
|
1187
|
+
const typedTo = (value) => {
|
|
998
1188
|
assertTypeOutput(name, is, validateOutput, value);
|
|
999
|
-
return
|
|
1000
|
-
}
|
|
1189
|
+
return to(value);
|
|
1190
|
+
};
|
|
1191
|
+
if (to.parent) {
|
|
1192
|
+
typedTo.parent = mapRuntimeOperations(to.parent, (operation) => (value) => {
|
|
1193
|
+
assertTypeOutput(name, is, validateOutput, value);
|
|
1194
|
+
return operation(value);
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
// A spread-free literal gets a fixed shape; spreading in the middle made
|
|
1198
|
+
// every later property a slow generic definition.
|
|
1001
1199
|
const type = {
|
|
1002
|
-
|
|
1200
|
+
"~evolu/instance": "Type",
|
|
1003
1201
|
name,
|
|
1004
1202
|
parent,
|
|
1005
1203
|
fromUnknown,
|
|
@@ -1007,41 +1205,55 @@ const createTypeNode = (name, parent, fromUnknown, is, validateOutput, from, own
|
|
|
1007
1205
|
is,
|
|
1008
1206
|
from: typedFrom,
|
|
1009
1207
|
to: typedTo,
|
|
1010
|
-
orThrow:
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1208
|
+
orThrow: (value, options = firstValidationOptions) => {
|
|
1209
|
+
const result = fromInput(value, options);
|
|
1210
|
+
if (result.ok)
|
|
1211
|
+
return result.value;
|
|
1212
|
+
throw new Error(runtimeFormatError(result.error), {
|
|
1213
|
+
cause: result.error,
|
|
1214
|
+
});
|
|
1215
|
+
},
|
|
1216
|
+
orNull: (value, options = firstValidationOptions) => getOrNull(fromInput(value, options)),
|
|
1217
|
+
"~standard": {
|
|
1218
|
+
version: 1,
|
|
1219
|
+
vendor: "evolu",
|
|
1220
|
+
validate: (value) => {
|
|
1221
|
+
const result = fromUnknown(value, allValidationOptions);
|
|
1222
|
+
return result.ok
|
|
1223
|
+
? { value: result.value }
|
|
1224
|
+
: {
|
|
1225
|
+
issues: getTypeIssues(result.error, "all", []).map((issue) => ({
|
|
1226
|
+
message: formatRuntimeTypeIssue(issue, formatIssue),
|
|
1227
|
+
path: issue.path,
|
|
1228
|
+
})),
|
|
1229
|
+
};
|
|
1230
|
+
},
|
|
1231
|
+
},
|
|
1014
1232
|
[outputValidationSymbol]: validateOutput,
|
|
1015
1233
|
[fromSymbol]: from,
|
|
1016
1234
|
[encoderSymbol]: to,
|
|
1017
1235
|
[getRuntimeTypeIssuesSymbol]: getTypeIssues,
|
|
1236
|
+
[checkSymbol]: check,
|
|
1237
|
+
[refinementsSymbol]: refinements,
|
|
1238
|
+
[arrayTypeSymbol]: undefined,
|
|
1239
|
+
[setTypeSymbol]: undefined,
|
|
1018
1240
|
};
|
|
1241
|
+
if (additionalProperties !== undefined) {
|
|
1242
|
+
globalThis.Object.assign(type, additionalProperties);
|
|
1243
|
+
}
|
|
1019
1244
|
return type;
|
|
1020
1245
|
};
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
return result.ok
|
|
1027
|
-
? { value: result.value }
|
|
1028
|
-
: {
|
|
1029
|
-
issues: getTypeIssues(result.error, "all").map((issue) => ({
|
|
1030
|
-
message: formatRuntimeTypeIssue(issue, formatIssue),
|
|
1031
|
-
path: issue.path,
|
|
1032
|
-
})),
|
|
1033
|
-
};
|
|
1034
|
-
},
|
|
1035
|
-
});
|
|
1036
|
-
const addRuntimeAssertions = (name, is, validateOutput, parent, operation) => {
|
|
1246
|
+
// Each `from.parent` suffix asserts the Output of the next Type toward the
|
|
1247
|
+
// root before running the remaining pipeline.
|
|
1248
|
+
const addRuntimeAssertions = (boundary, operation) => {
|
|
1249
|
+
const { name, is } = boundary;
|
|
1250
|
+
const validateOutput = boundary[outputValidationSymbol];
|
|
1037
1251
|
const asserted = (value, options = firstValidationOptions) => {
|
|
1038
1252
|
assertTypeOutput(name, is, validateOutput, value, options);
|
|
1039
1253
|
return operation(value, options);
|
|
1040
1254
|
};
|
|
1041
1255
|
if (operation.parent) {
|
|
1042
|
-
|
|
1043
|
-
const runtimeParent = typeParent;
|
|
1044
|
-
asserted.parent = addRuntimeAssertions(runtimeParent.name, runtimeParent.is, runtimeParent[outputValidationSymbol], runtimeParent.parent, operation.parent);
|
|
1256
|
+
asserted.parent = addRuntimeAssertions(boundary.parent, operation.parent);
|
|
1045
1257
|
}
|
|
1046
1258
|
return asserted;
|
|
1047
1259
|
};
|
|
@@ -1050,18 +1262,26 @@ const addRuntimeAssertions = (name, is, validateOutput, parent, operation) => {
|
|
|
1050
1262
|
*
|
|
1051
1263
|
* @group Base
|
|
1052
1264
|
*/
|
|
1053
|
-
export const Unknown = /*#__PURE__*/ createRootType("Unknown", ok, identity);
|
|
1265
|
+
export const Unknown = /*#__PURE__*/ createRootType("Unknown", ok, identity, { isOutput: () => true, check: () => undefined });
|
|
1054
1266
|
/**
|
|
1055
1267
|
* A {@link Type} rejecting every value.
|
|
1056
1268
|
*
|
|
1057
1269
|
* @group Base
|
|
1058
1270
|
*/
|
|
1059
|
-
export const Never = /*#__PURE__*/ createRootType("Never", (value) => err({ type: "Never", value }), (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not valid for type Never
|
|
1271
|
+
export const Never = /*#__PURE__*/ createRootType("Never", (value) => err({ type: "Never", value }), (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not valid for type Never.`, {
|
|
1272
|
+
isOutput: () => false,
|
|
1273
|
+
check: (value) => ({ type: "Never", value }),
|
|
1274
|
+
});
|
|
1060
1275
|
const createTypeOfType = (name) => {
|
|
1061
1276
|
const typeOf = name.toLowerCase();
|
|
1062
1277
|
return createRootType(name, (value) => typeof value === typeOf
|
|
1063
1278
|
? ok(value)
|
|
1064
|
-
: err({ type: "TypeOf", expected: name, value }), (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not a ${typeOf}
|
|
1279
|
+
: err({ type: "TypeOf", expected: name, value }), (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not a ${typeOf}.`, {
|
|
1280
|
+
isOutput: (value) => typeof value === typeOf,
|
|
1281
|
+
check: (value) => typeof value === typeOf
|
|
1282
|
+
? undefined
|
|
1283
|
+
: { type: "TypeOf", expected: name, value },
|
|
1284
|
+
});
|
|
1065
1285
|
};
|
|
1066
1286
|
/**
|
|
1067
1287
|
* A JavaScript string {@link Type} without additional constraints.
|
|
@@ -1254,18 +1474,25 @@ export const EvoluType = /*#__PURE__*/ createType("EvoluType", (value) => isInst
|
|
|
1254
1474
|
: err({ type: "EvoluType", value }), (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not an Evolu Type.`);
|
|
1255
1475
|
export function objectTag(name, outputType) {
|
|
1256
1476
|
const formatError = (error) => `A value ${safelyStringifyUnknownValue(error.value)} does not have the expected object tag ${safelyStringifyUnknownValue(error.expected)}.`;
|
|
1477
|
+
const tag = `[object ${name}]`;
|
|
1257
1478
|
if (outputType === undefined) {
|
|
1258
|
-
return createRootType(name, (value) => hasObjectTag(value,
|
|
1479
|
+
return createRootType(name, (value) => hasObjectTag(value, tag)
|
|
1259
1480
|
? ok(value)
|
|
1260
|
-
: err({ type: "ObjectTag", expected: name, value }), formatError
|
|
1481
|
+
: err({ type: "ObjectTag", expected: name, value }), formatError, {
|
|
1482
|
+
isOutput: (value) => hasObjectTag(value, tag),
|
|
1483
|
+
check: (value) => hasObjectTag(value, tag)
|
|
1484
|
+
? undefined
|
|
1485
|
+
: { type: "ObjectTag", expected: name, value },
|
|
1486
|
+
});
|
|
1261
1487
|
}
|
|
1262
|
-
return globalThis.Object.assign(createChildType("ObjectTag", outputType, (value) => hasObjectTag(value,
|
|
1488
|
+
return globalThis.Object.assign(createChildType("ObjectTag", outputType, (value) => hasObjectTag(value, tag)
|
|
1263
1489
|
? ok(value)
|
|
1264
1490
|
: err({ type: "ObjectTag", expected: name, value }), formatError), { expected: name });
|
|
1265
1491
|
}
|
|
1266
|
-
|
|
1492
|
+
// Takes the complete `[object Name]` tag, so hot calls build no string.
|
|
1493
|
+
const hasObjectTag = (value, tag) => value !== null &&
|
|
1267
1494
|
(typeof value === "object" || typeof value === "function") &&
|
|
1268
|
-
globalThis.Object.prototype.toString.call(value) ===
|
|
1495
|
+
globalThis.Object.prototype.toString.call(value) === tag;
|
|
1269
1496
|
/**
|
|
1270
1497
|
* A realm-neutral JavaScript Date {@link Type} for trusted values.
|
|
1271
1498
|
*
|
|
@@ -1327,7 +1554,12 @@ export const instanceOf = (constructor) => {
|
|
|
1327
1554
|
const constructorName = concreteConstructor.name;
|
|
1328
1555
|
const is = (value) => globalThis.Function.prototype[globalThis.Symbol.hasInstance].call(concreteConstructor, value);
|
|
1329
1556
|
const fromUnknown = (value) => is(value) ? ok(value) : err({ type: "InstanceOf", constructorName, value });
|
|
1330
|
-
return globalThis.Object.assign(createRootType("InstanceOf", fromUnknown, (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not an instance of ${error.constructorName}
|
|
1557
|
+
return globalThis.Object.assign(createRootType("InstanceOf", fromUnknown, (error) => `A value ${safelyStringifyUnknownValue(error.value)} is not an instance of ${error.constructorName}.`, {
|
|
1558
|
+
isOutput: is,
|
|
1559
|
+
check: (value) => is(value)
|
|
1560
|
+
? undefined
|
|
1561
|
+
: { type: "InstanceOf", constructorName, value },
|
|
1562
|
+
}), { constructor: concreteConstructor });
|
|
1331
1563
|
};
|
|
1332
1564
|
/**
|
|
1333
1565
|
* Literal {@link Type}.
|
|
@@ -1372,9 +1604,6 @@ export const instanceOf = (constructor) => {
|
|
|
1372
1604
|
*/
|
|
1373
1605
|
export const literal = (expected) => {
|
|
1374
1606
|
const literalExpected = expected;
|
|
1375
|
-
const validate = (value) => value === literalExpected
|
|
1376
|
-
? ok(value)
|
|
1377
|
-
: err({ type: "Literal", expected: literalExpected, value });
|
|
1378
1607
|
const parent = typeof literalExpected === "string"
|
|
1379
1608
|
? String
|
|
1380
1609
|
: typeof literalExpected === "number"
|
|
@@ -1386,8 +1615,17 @@ export const literal = (expected) => {
|
|
|
1386
1615
|
: null;
|
|
1387
1616
|
const formatError = (error) => `The value ${safelyStringifyUnknownValue(error.value)} is not strictly equal to the expected literal: ${globalThis.String(error.expected)}.`;
|
|
1388
1617
|
return globalThis.Object.assign(parent
|
|
1389
|
-
? createChildType("Literal", parent,
|
|
1390
|
-
|
|
1618
|
+
? createChildType("Literal", parent, (value) => value === literalExpected
|
|
1619
|
+
? ok()
|
|
1620
|
+
: err({ type: "Literal", expected: literalExpected, value }), formatError, undefined, true)
|
|
1621
|
+
: createRootType("Literal", (value) => value === literalExpected
|
|
1622
|
+
? ok(value)
|
|
1623
|
+
: err({ type: "Literal", expected: literalExpected, value }), formatError, {
|
|
1624
|
+
isOutput: (value) => value === literalExpected,
|
|
1625
|
+
check: (value) => value === literalExpected
|
|
1626
|
+
? undefined
|
|
1627
|
+
: { type: "Literal", expected: literalExpected, value },
|
|
1628
|
+
}), {
|
|
1391
1629
|
expected: literalExpected,
|
|
1392
1630
|
[templateLiteralSyntaxSymbol]: true,
|
|
1393
1631
|
});
|
|
@@ -1409,16 +1647,35 @@ export function union(...typesOrLiterals) {
|
|
|
1409
1647
|
? typeOrLiteral
|
|
1410
1648
|
: literal(typeOrLiteral));
|
|
1411
1649
|
const inputMembers = members.map(getTerminalRuntimeNode);
|
|
1412
|
-
const
|
|
1413
|
-
const
|
|
1650
|
+
const inputCheck = createUnionCheck(inputMembers);
|
|
1651
|
+
const inputFrom = inputCheck
|
|
1652
|
+
? checkToFromUnknown(inputCheck)
|
|
1653
|
+
: createUnionValidation(inputMembers, getFromUnknown);
|
|
1414
1654
|
const getTypeIssues = createUnionRuntimeTypeIssues(members);
|
|
1415
|
-
const input = createTypeNode("Union", null, inputFrom, (
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1655
|
+
const input = createTypeNode("Union", null, inputFrom, createUnionIs(inputMembers), inputCheck
|
|
1656
|
+
? inputFrom
|
|
1657
|
+
: createUnionValidation(inputMembers, getValidateOutput), ok, identity, createUnionRuntimeTypeIssues(inputMembers), { check: inputCheck });
|
|
1658
|
+
// Literal members accept exactly their `===` equal values, so a match skips
|
|
1659
|
+
// them.
|
|
1660
|
+
const isLiteralUnion = typesOrLiterals.every((typeOrLiteral) => typeOrLiteral === null || typeof typeOrLiteral !== "object");
|
|
1661
|
+
const isLiteral = (value) =>
|
|
1662
|
+
// oxlint-disable-next-line typescript/prefer-includes -- Like Literal validation, indexOf uses ===, so NaN never matches.
|
|
1663
|
+
typesOrLiterals.indexOf(value) !== -1;
|
|
1664
|
+
const checkMembers = createUnionCheck(members);
|
|
1665
|
+
const check = checkMembers && isLiteralUnion
|
|
1666
|
+
? (value, options) => isLiteral(value) ? undefined : checkMembers(value, options)
|
|
1667
|
+
: checkMembers;
|
|
1668
|
+
const fromUnknown = check
|
|
1669
|
+
? checkToFromUnknown(check)
|
|
1670
|
+
: createUnionValidation(members, getFromUnknown);
|
|
1671
|
+
const validateOutput = check
|
|
1672
|
+
? fromUnknown
|
|
1673
|
+
: createUnionValidation(members, getValidateOutput);
|
|
1674
|
+
const fromParent = createUnionValidation(members, (member, index) => {
|
|
1675
|
+
const memberFromInput = getTerminalRuntimeNode(member[fromSymbol]);
|
|
1676
|
+
const inputIs = inputMembers[index].is;
|
|
1677
|
+
return (value, options) => inputIs(value) ? memberFromInput(value, options) : undefined;
|
|
1678
|
+
});
|
|
1422
1679
|
const getOutputMember = (value) => members.find((member) => member.is(value));
|
|
1423
1680
|
const from = createFromOperation(fromParent);
|
|
1424
1681
|
const to = (value) => {
|
|
@@ -1426,38 +1683,99 @@ export function union(...typesOrLiterals) {
|
|
|
1426
1683
|
assertNonNullable(member);
|
|
1427
1684
|
return member[encoderSymbol](value);
|
|
1428
1685
|
};
|
|
1429
|
-
return createTypeNode("Union", input, fromUnknown,
|
|
1686
|
+
return createTypeNode("Union", input, fromUnknown, isLiteralUnion ? isLiteral : createUnionIs(members), validateOutput, from, to, getTypeIssues, {
|
|
1687
|
+
additionalProperties: { members, [templateLiteralSyntaxSymbol]: true },
|
|
1688
|
+
check,
|
|
1689
|
+
});
|
|
1430
1690
|
}
|
|
1431
|
-
const createUnionRuntimeTypeIssues = (members) => (error) => [
|
|
1691
|
+
const createUnionRuntimeTypeIssues = (members) => (error, _mode, path) => [
|
|
1432
1692
|
{
|
|
1433
1693
|
name: "Union",
|
|
1434
1694
|
error,
|
|
1435
|
-
path
|
|
1695
|
+
path,
|
|
1436
1696
|
formatError: () => "A value does not match any allowed variant.",
|
|
1697
|
+
// Alternative issue paths are relative to the Union value.
|
|
1437
1698
|
alternatives: error.errors.map(({ index, error }) => ({
|
|
1438
1699
|
index,
|
|
1439
1700
|
name: members[index].name,
|
|
1440
|
-
issues: members[index][getRuntimeTypeIssuesSymbol](error, "all"),
|
|
1701
|
+
issues: members[index][getRuntimeTypeIssuesSymbol](error, "all", []),
|
|
1441
1702
|
})),
|
|
1442
1703
|
},
|
|
1443
1704
|
];
|
|
1444
|
-
const
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
if (result.ok)
|
|
1451
|
-
return result;
|
|
1452
|
-
if (errors === undefined || options.errors === "all") {
|
|
1453
|
-
(errors ??= []).push({ index, error: result.error });
|
|
1454
|
-
}
|
|
1705
|
+
const createUnionIs = (members) => {
|
|
1706
|
+
// Two members, as in nullOr, are checked without a loop.
|
|
1707
|
+
if (members.length === 2) {
|
|
1708
|
+
const firstIs = members[0].is;
|
|
1709
|
+
const secondIs = members[1].is;
|
|
1710
|
+
return (value) => firstIs(value) || secondIs(value);
|
|
1455
1711
|
}
|
|
1456
|
-
|
|
1457
|
-
return
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1712
|
+
let memberIs;
|
|
1713
|
+
return (value) => {
|
|
1714
|
+
const isMembers = (memberIs ??= members.map(getIs));
|
|
1715
|
+
for (let index = 0; index < isMembers.length; index++) {
|
|
1716
|
+
if (isMembers[index](value))
|
|
1717
|
+
return true;
|
|
1718
|
+
}
|
|
1719
|
+
return false;
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1722
|
+
// A Union of identity Types returns the Result of the first valid member, so
|
|
1723
|
+
// it is an identity Type too.
|
|
1724
|
+
const createUnionCheck = (members) => {
|
|
1725
|
+
const checks = getRuntimeChecks(members);
|
|
1726
|
+
if (checks === undefined)
|
|
1727
|
+
return undefined;
|
|
1728
|
+
return (value, options) => {
|
|
1729
|
+
// The same errors as createUnionValidation.
|
|
1730
|
+
let first;
|
|
1731
|
+
let errors;
|
|
1732
|
+
for (let index = 0; index < checks.length; index++) {
|
|
1733
|
+
const error = checks[index](value, options);
|
|
1734
|
+
if (error === undefined)
|
|
1735
|
+
return undefined;
|
|
1736
|
+
if (first === undefined) {
|
|
1737
|
+
first = { index, error };
|
|
1738
|
+
}
|
|
1739
|
+
else if (options.errors === "all") {
|
|
1740
|
+
(errors ??= [first]).push({ index, error });
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
assertNonNullable(first);
|
|
1744
|
+
return {
|
|
1745
|
+
type: "Union",
|
|
1746
|
+
errors: (errors ?? [first]),
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
};
|
|
1750
|
+
// A member validation returns `undefined` to skip its member.
|
|
1751
|
+
const createUnionValidation = (members, getMemberValidation) => {
|
|
1752
|
+
let memberValidations;
|
|
1753
|
+
return (value, options = firstValidationOptions) => {
|
|
1754
|
+
const validations = (memberValidations ??=
|
|
1755
|
+
members.map(getMemberValidation));
|
|
1756
|
+
// A later member often matches (nullOr), so the error array is created
|
|
1757
|
+
// only when a second member fails or the Union fails.
|
|
1758
|
+
let first;
|
|
1759
|
+
let errors;
|
|
1760
|
+
for (let index = 0; index < validations.length; index++) {
|
|
1761
|
+
const result = validations[index](value, options);
|
|
1762
|
+
if (result === undefined)
|
|
1763
|
+
continue;
|
|
1764
|
+
if (result.ok)
|
|
1765
|
+
return result;
|
|
1766
|
+
if (first === undefined) {
|
|
1767
|
+
first = { index, error: result.error };
|
|
1768
|
+
}
|
|
1769
|
+
else if (options.errors === "all") {
|
|
1770
|
+
(errors ??= [first]).push({ index, error: result.error });
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
assertNonNullable(first);
|
|
1774
|
+
return err({
|
|
1775
|
+
type: "Union",
|
|
1776
|
+
errors: (errors ?? [first]),
|
|
1777
|
+
});
|
|
1778
|
+
};
|
|
1461
1779
|
};
|
|
1462
1780
|
/**
|
|
1463
1781
|
* Union {@link Type} containing the supplied Type and `undefined`.
|
|
@@ -1751,14 +2069,14 @@ const createTemplateLiteralParserType = (templateParts) => {
|
|
|
1751
2069
|
return result;
|
|
1752
2070
|
return err({ type: "TemplateLiteral", value: stringResult.value });
|
|
1753
2071
|
};
|
|
1754
|
-
const getTypeIssues = (error, mode) => {
|
|
2072
|
+
const getTypeIssues = (error, mode, path) => {
|
|
1755
2073
|
if (error.type !== "TemplateLiteral") {
|
|
1756
|
-
return String[getRuntimeTypeIssuesSymbol](error, mode);
|
|
2074
|
+
return String[getRuntimeTypeIssuesSymbol](error, mode, path);
|
|
1757
2075
|
}
|
|
1758
2076
|
if ("outputError" in error) {
|
|
1759
|
-
return runtimeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode);
|
|
2077
|
+
return runtimeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode, path);
|
|
1760
2078
|
}
|
|
1761
|
-
return singleRuntimeTypeIssue("TemplateLiteral", error, ((error) => `The value ${safelyStringifyUnknownValue(error.value)} does not match the template literal.`));
|
|
2079
|
+
return singleRuntimeTypeIssue("TemplateLiteral", error, ((error) => `The value ${safelyStringifyUnknownValue(error.value)} does not match the template literal.`), path);
|
|
1762
2080
|
};
|
|
1763
2081
|
const canonicalStringFromUnknown = (value, options = firstValidationOptions) => {
|
|
1764
2082
|
const stringResult = String.fromUnknown(value, options);
|
|
@@ -1767,7 +2085,7 @@ const createTemplateLiteralParserType = (templateParts) => {
|
|
|
1767
2085
|
: stringResult;
|
|
1768
2086
|
};
|
|
1769
2087
|
const canonicalStringFrom = createFromOperation((value, options = firstValidationOptions) => canonicalizeString(value, options));
|
|
1770
|
-
const stringType = createTypeNode("TemplateLiteral", String, canonicalStringFromUnknown, (value) => validateCanonicalString(value, firstValidationOptions).ok, validateCanonicalString, canonicalStringFrom, identity, getTypeIssues, reflection);
|
|
2088
|
+
const stringType = createTypeNode("TemplateLiteral", String, canonicalStringFromUnknown, (value) => validateCanonicalString(value, firstValidationOptions).ok, validateCanonicalString, canonicalStringFrom, identity, getTypeIssues, { additionalProperties: reflection });
|
|
1771
2089
|
const fromUnknown = (value, options = firstValidationOptions) => {
|
|
1772
2090
|
const stringResult = String.fromUnknown(value, options);
|
|
1773
2091
|
if (!stringResult.ok)
|
|
@@ -1797,7 +2115,7 @@ const createTemplateLiteralParserType = (templateParts) => {
|
|
|
1797
2115
|
};
|
|
1798
2116
|
fromCanonicalString.parent = fromString;
|
|
1799
2117
|
const from = createFromOperation(fromCanonicalString);
|
|
1800
|
-
const type = createTypeNode("TemplateLiteral", stringType, fromUnknown, runtimeOutput.is, runtimeOutput[outputValidationSymbol], from, encodeCaptures, getTypeIssues, reflection);
|
|
2118
|
+
const type = createTypeNode("TemplateLiteral", stringType, fromUnknown, runtimeOutput.is, runtimeOutput[outputValidationSymbol], from, encodeCaptures, getTypeIssues, { additionalProperties: reflection });
|
|
1801
2119
|
type.from.parent =
|
|
1802
2120
|
fromCanonicalString;
|
|
1803
2121
|
return type;
|
|
@@ -1939,9 +2257,7 @@ const getTemplateLiteralPartFraming = (part) => {
|
|
|
1939
2257
|
return getTemplateLiteralPartFraming(type.parent);
|
|
1940
2258
|
};
|
|
1941
2259
|
export function brand(name, parent, validate, formatError) {
|
|
1942
|
-
return createChildType(name, parent, validate
|
|
1943
|
-
? (value) => flatMapResult(validate(value), () => ok(value))
|
|
1944
|
-
: ok, formatError);
|
|
2260
|
+
return createChildType(name, parent, validate ?? (() => ok()), formatError, undefined, true);
|
|
1945
2261
|
}
|
|
1946
2262
|
/**
|
|
1947
2263
|
* Canonical ISO date-time {@link String}.
|
|
@@ -4162,9 +4478,13 @@ const isArrayCollection = (value, isElement) => {
|
|
|
4162
4478
|
};
|
|
4163
4479
|
const arrayRuntimeConfig = {
|
|
4164
4480
|
name: "Array",
|
|
4481
|
+
typeSymbol: arrayTypeSymbol,
|
|
4165
4482
|
typeByElement: arrayTypeByElement,
|
|
4166
4483
|
validate: validateArrayCollection,
|
|
4167
4484
|
validateItems: (value, validateElement, options) => validateArrayItems(value, validateElement, options, false),
|
|
4485
|
+
check: (value, checkElement, options) => Array.isArray(value)
|
|
4486
|
+
? checkIndexedArrayItems("Array", value, checkElement, options)
|
|
4487
|
+
: { type: "Array", reason: { kind: "NotArray", value } },
|
|
4168
4488
|
encode: encodeArrayCollection,
|
|
4169
4489
|
is: isArrayCollection,
|
|
4170
4490
|
formatError: ((error) => {
|
|
@@ -4189,23 +4509,10 @@ const validateArrayItems = (value, validate, options, checkStructure) => validat
|
|
|
4189
4509
|
// Encoding and Output membership stay feature-local because sharing those
|
|
4190
4510
|
// shorter hot paths measurably increases standalone bundles.
|
|
4191
4511
|
const validateIndexedArrayItems = (name, value, validate, options, checkStructure) => {
|
|
4192
|
-
let issues
|
|
4512
|
+
let issues = checkStructure
|
|
4513
|
+
? getArrayExcessPropertyIssues(value, options)
|
|
4514
|
+
: undefined;
|
|
4193
4515
|
let output;
|
|
4194
|
-
if (checkStructure) {
|
|
4195
|
-
for (const key of Reflect.ownKeys(value)) {
|
|
4196
|
-
if (key === "length")
|
|
4197
|
-
continue;
|
|
4198
|
-
if (typeof key === "string") {
|
|
4199
|
-
const index = globalThis.Number(key) >>> 0;
|
|
4200
|
-
if (index < value.length && globalThis.String(index) === key) {
|
|
4201
|
-
continue;
|
|
4202
|
-
}
|
|
4203
|
-
}
|
|
4204
|
-
(issues ??= []).push({ kind: "ExcessProperty", key });
|
|
4205
|
-
if (options.errors === "first")
|
|
4206
|
-
break;
|
|
4207
|
-
}
|
|
4208
|
-
}
|
|
4209
4516
|
for (let index = 0; (issues === undefined || options.errors === "all") && index < value.length; index++) {
|
|
4210
4517
|
let item;
|
|
4211
4518
|
if (checkStructure) {
|
|
@@ -4252,6 +4559,66 @@ const validateIndexedArrayItems = (name, value, validate, options, checkStructur
|
|
|
4252
4559
|
},
|
|
4253
4560
|
});
|
|
4254
4561
|
};
|
|
4562
|
+
// Like validateIndexedArrayItems for identity elements, which leave the value
|
|
4563
|
+
// unchanged, so it returns only the error.
|
|
4564
|
+
const checkIndexedArrayItems = (name, value, check, options) => {
|
|
4565
|
+
let issues = getArrayExcessPropertyIssues(value, options);
|
|
4566
|
+
for (let index = 0; (issues === undefined || options.errors === "all") && index < value.length; index++) {
|
|
4567
|
+
const descriptor = globalThis.Object.getOwnPropertyDescriptor(value, index);
|
|
4568
|
+
if (descriptor === undefined) {
|
|
4569
|
+
(issues ??= []).push({ kind: "Hole", index });
|
|
4570
|
+
if (options.errors === "first")
|
|
4571
|
+
break;
|
|
4572
|
+
continue;
|
|
4573
|
+
}
|
|
4574
|
+
if (!("value" in descriptor)) {
|
|
4575
|
+
(issues ??= []).push({ kind: "Accessor", index });
|
|
4576
|
+
if (options.errors === "first")
|
|
4577
|
+
break;
|
|
4578
|
+
continue;
|
|
4579
|
+
}
|
|
4580
|
+
const error = check(descriptor.value, options, index);
|
|
4581
|
+
if (error === undefined)
|
|
4582
|
+
continue;
|
|
4583
|
+
(issues ??= []).push({ kind: "Element", index, error });
|
|
4584
|
+
if (options.errors === "first")
|
|
4585
|
+
break;
|
|
4586
|
+
}
|
|
4587
|
+
return issues === undefined
|
|
4588
|
+
? undefined
|
|
4589
|
+
: {
|
|
4590
|
+
type: name,
|
|
4591
|
+
reason: {
|
|
4592
|
+
kind: "Items",
|
|
4593
|
+
issues: issues,
|
|
4594
|
+
},
|
|
4595
|
+
};
|
|
4596
|
+
};
|
|
4597
|
+
// Own keys other than indexes and `length` are excess properties.
|
|
4598
|
+
const getArrayExcessPropertyIssues = (value, options) => {
|
|
4599
|
+
let issues;
|
|
4600
|
+
const keys = Reflect.ownKeys(value);
|
|
4601
|
+
for (let position = 0; position < keys.length; position++) {
|
|
4602
|
+
const key = keys[position];
|
|
4603
|
+
if (key === "length")
|
|
4604
|
+
continue;
|
|
4605
|
+
if (typeof key === "string") {
|
|
4606
|
+
// Ordinary arrays list their indexes first, in ascending order. Comparing
|
|
4607
|
+
// the key first reads `length` only for an index key.
|
|
4608
|
+
if (key === globalThis.String(position) && position < value.length) {
|
|
4609
|
+
continue;
|
|
4610
|
+
}
|
|
4611
|
+
const index = globalThis.Number(key) >>> 0;
|
|
4612
|
+
if (index < value.length && globalThis.String(index) === key) {
|
|
4613
|
+
continue;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
(issues ??= []).push({ kind: "ExcessProperty", key });
|
|
4617
|
+
if (options.errors === "first")
|
|
4618
|
+
break;
|
|
4619
|
+
}
|
|
4620
|
+
return issues;
|
|
4621
|
+
};
|
|
4255
4622
|
const copyArrayPrefix = (value, endIndex) => {
|
|
4256
4623
|
const output = createMutableArray(value.length);
|
|
4257
4624
|
for (let index = 0; index < endIndex; index++) {
|
|
@@ -4285,11 +4652,23 @@ export const set = (element) => createHomogeneousCollectionType(element, setRunt
|
|
|
4285
4652
|
// Array(String) +82/+63 B gzip, while Array(String)+Set(String) is 2501/2492 B
|
|
4286
4653
|
// and Set adds only +303/+304 B once Array is present. See TreeShaking.test.ts.
|
|
4287
4654
|
const createHomogeneousCollectionType = (typeElement, config) => {
|
|
4288
|
-
const
|
|
4655
|
+
const slot = typeElement[config.typeSymbol];
|
|
4656
|
+
// A copy of a Type, spread or with the Type as its prototype, has the slot
|
|
4657
|
+
// of the original.
|
|
4658
|
+
const cached = slot !== undefined && slot.element === typeElement
|
|
4659
|
+
? slot
|
|
4660
|
+
: config.typeByElement.get(typeElement);
|
|
4289
4661
|
if (cached)
|
|
4290
4662
|
return cached;
|
|
4291
|
-
const
|
|
4292
|
-
const
|
|
4663
|
+
const checkElement = typeElement[checkSymbol];
|
|
4664
|
+
const check = checkElement &&
|
|
4665
|
+
((value, options) => config.check(value, checkElement, options));
|
|
4666
|
+
const fromUnknown = check
|
|
4667
|
+
? checkToFromUnknown(check)
|
|
4668
|
+
: (value, options = firstValidationOptions) => config.validate(value, typeElement.fromUnknown, options);
|
|
4669
|
+
const validateOutput = check
|
|
4670
|
+
? fromUnknown
|
|
4671
|
+
: (value, options = firstValidationOptions) => config.validate(value, typeElement[outputValidationSymbol], options);
|
|
4293
4672
|
const parent = typeElement.parent
|
|
4294
4673
|
? createHomogeneousCollectionType(typeElement.parent, config)
|
|
4295
4674
|
: null;
|
|
@@ -4304,16 +4683,20 @@ const createHomogeneousCollectionType = (typeElement, config) => {
|
|
|
4304
4683
|
? identity
|
|
4305
4684
|
: (value) => config.encode(value, encodeElement);
|
|
4306
4685
|
const getTypeIssues = createCollectionRuntimeTypeIssues(config.name, "Items", config.formatError, () => typeElement);
|
|
4307
|
-
const type = createTypeNode(config.name, parent, fromUnknown, (value) => config.is(value, typeElement.is), validateOutput, from, to, getTypeIssues, { element: typeElement });
|
|
4308
|
-
|
|
4686
|
+
const type = createTypeNode(config.name, parent, fromUnknown, (value) => config.is(value, typeElement.is), validateOutput, from, to, getTypeIssues, { additionalProperties: { element: typeElement }, check });
|
|
4687
|
+
// A frozen element Type keeps its Collection Type in the WeakMap.
|
|
4688
|
+
if (!Reflect.set(typeElement, config.typeSymbol, type)) {
|
|
4689
|
+
config.typeByElement.set(typeElement, type);
|
|
4690
|
+
}
|
|
4309
4691
|
return type;
|
|
4310
4692
|
};
|
|
4311
4693
|
const setTypeByElement = /*#__PURE__*/ new WeakMap();
|
|
4312
4694
|
const setRuntimeConfig = {
|
|
4313
4695
|
name: "Set",
|
|
4696
|
+
typeSymbol: setTypeSymbol,
|
|
4314
4697
|
typeByElement: setTypeByElement,
|
|
4315
4698
|
validate: (value, validateElement, options) => {
|
|
4316
|
-
if (!hasObjectTag(value, "Set")) {
|
|
4699
|
+
if (!hasObjectTag(value, "[object Set]")) {
|
|
4317
4700
|
return err({
|
|
4318
4701
|
type: "Set",
|
|
4319
4702
|
reason: { kind: "NotSet", value },
|
|
@@ -4322,6 +4705,37 @@ const setRuntimeConfig = {
|
|
|
4322
4705
|
return validateSetItems(value, validateElement, options, true);
|
|
4323
4706
|
},
|
|
4324
4707
|
validateItems: (value, validateElement, options) => validateSetItems(value, validateElement, options, false),
|
|
4708
|
+
check: (value, checkElement, options) => {
|
|
4709
|
+
if (!hasObjectTag(value, "[object Set]")) {
|
|
4710
|
+
return { type: "Set", reason: { kind: "NotSet", value } };
|
|
4711
|
+
}
|
|
4712
|
+
// The same issues as validateSetItems, without building an Output.
|
|
4713
|
+
let issues;
|
|
4714
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
4715
|
+
(issues ??= []).push({ kind: "ExcessProperty", key });
|
|
4716
|
+
if (options.errors === "first")
|
|
4717
|
+
break;
|
|
4718
|
+
}
|
|
4719
|
+
let index = 0;
|
|
4720
|
+
for (const item of value) {
|
|
4721
|
+
if (issues !== undefined && options.errors === "first")
|
|
4722
|
+
break;
|
|
4723
|
+
const error = checkElement(item, options);
|
|
4724
|
+
if (error !== undefined) {
|
|
4725
|
+
(issues ??= []).push({ kind: "Element", index, error });
|
|
4726
|
+
}
|
|
4727
|
+
index++;
|
|
4728
|
+
}
|
|
4729
|
+
return issues === undefined
|
|
4730
|
+
? undefined
|
|
4731
|
+
: {
|
|
4732
|
+
type: "Set",
|
|
4733
|
+
reason: {
|
|
4734
|
+
kind: "Items",
|
|
4735
|
+
issues: issues,
|
|
4736
|
+
},
|
|
4737
|
+
};
|
|
4738
|
+
},
|
|
4325
4739
|
encode: (value, encodeElement) => {
|
|
4326
4740
|
let changed = false;
|
|
4327
4741
|
const output = new Set();
|
|
@@ -4334,7 +4748,7 @@ const setRuntimeConfig = {
|
|
|
4334
4748
|
return changed ? output : value;
|
|
4335
4749
|
},
|
|
4336
4750
|
is: (value, isElement) => {
|
|
4337
|
-
if (!hasObjectTag(value, "Set") ||
|
|
4751
|
+
if (!hasObjectTag(value, "[object Set]") ||
|
|
4338
4752
|
Reflect.ownKeys(value).length !== 0) {
|
|
4339
4753
|
return false;
|
|
4340
4754
|
}
|
|
@@ -4425,7 +4839,7 @@ export const map = (key, value) => {
|
|
|
4425
4839
|
if (cached)
|
|
4426
4840
|
return cached;
|
|
4427
4841
|
const validate = (input, validateKey, validateValue, options) => {
|
|
4428
|
-
if (!hasObjectTag(input, "Map")) {
|
|
4842
|
+
if (!hasObjectTag(input, "[object Map]")) {
|
|
4429
4843
|
return err({
|
|
4430
4844
|
type: "Map",
|
|
4431
4845
|
reason: { kind: "NotMap", value: input },
|
|
@@ -4433,8 +4847,90 @@ export const map = (key, value) => {
|
|
|
4433
4847
|
}
|
|
4434
4848
|
return validateMapEntries(input, validateKey, validateValue, options, true);
|
|
4435
4849
|
};
|
|
4436
|
-
const
|
|
4437
|
-
const
|
|
4850
|
+
const checkKey = typeKey[checkSymbol];
|
|
4851
|
+
const checkValue = typeValue[checkSymbol];
|
|
4852
|
+
// Like validate for identity keys and values, which leave the Map unchanged,
|
|
4853
|
+
// so it returns only the error. A custom iterator can repeat a key, so
|
|
4854
|
+
// collisions are still detected.
|
|
4855
|
+
const check = checkKey &&
|
|
4856
|
+
checkValue &&
|
|
4857
|
+
((input, options) => {
|
|
4858
|
+
if (!hasObjectTag(input, "[object Map]")) {
|
|
4859
|
+
return {
|
|
4860
|
+
type: "Map",
|
|
4861
|
+
reason: { kind: "NotMap", value: input },
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4864
|
+
const entries = input;
|
|
4865
|
+
let issues;
|
|
4866
|
+
const entryByKey = new Map();
|
|
4867
|
+
for (const key of Reflect.ownKeys(entries)) {
|
|
4868
|
+
(issues ??= []).push({ kind: "ExcessProperty", key });
|
|
4869
|
+
if (options.errors === "first")
|
|
4870
|
+
break;
|
|
4871
|
+
}
|
|
4872
|
+
let index = 0;
|
|
4873
|
+
for (const [inputKey, inputValue] of entries) {
|
|
4874
|
+
if (issues !== undefined && options.errors === "first")
|
|
4875
|
+
break;
|
|
4876
|
+
const keyError = checkKey(inputKey, options);
|
|
4877
|
+
if (keyError !== undefined) {
|
|
4878
|
+
(issues ??= []).push({
|
|
4879
|
+
kind: "Key",
|
|
4880
|
+
index,
|
|
4881
|
+
key: inputKey,
|
|
4882
|
+
error: keyError,
|
|
4883
|
+
});
|
|
4884
|
+
if (options.errors === "first")
|
|
4885
|
+
break;
|
|
4886
|
+
}
|
|
4887
|
+
const valueError = checkValue(inputValue, options);
|
|
4888
|
+
if (valueError !== undefined) {
|
|
4889
|
+
(issues ??= []).push({
|
|
4890
|
+
kind: "Value",
|
|
4891
|
+
index,
|
|
4892
|
+
key: inputKey,
|
|
4893
|
+
error: valueError,
|
|
4894
|
+
});
|
|
4895
|
+
if (options.errors === "first")
|
|
4896
|
+
break;
|
|
4897
|
+
}
|
|
4898
|
+
if (keyError === undefined) {
|
|
4899
|
+
const previous = entryByKey.get(inputKey);
|
|
4900
|
+
if (previous === undefined) {
|
|
4901
|
+
entryByKey.set(inputKey, { index, key: inputKey });
|
|
4902
|
+
}
|
|
4903
|
+
else {
|
|
4904
|
+
(issues ??= []).push({
|
|
4905
|
+
kind: "Collision",
|
|
4906
|
+
index,
|
|
4907
|
+
key: inputKey,
|
|
4908
|
+
previousIndex: previous.index,
|
|
4909
|
+
previousKey: previous.key,
|
|
4910
|
+
outputKey: inputKey,
|
|
4911
|
+
});
|
|
4912
|
+
if (options.errors === "first")
|
|
4913
|
+
break;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
index++;
|
|
4917
|
+
}
|
|
4918
|
+
return issues === undefined
|
|
4919
|
+
? undefined
|
|
4920
|
+
: {
|
|
4921
|
+
type: "Map",
|
|
4922
|
+
reason: {
|
|
4923
|
+
kind: "Entries",
|
|
4924
|
+
issues: issues,
|
|
4925
|
+
},
|
|
4926
|
+
};
|
|
4927
|
+
});
|
|
4928
|
+
const fromUnknown = check
|
|
4929
|
+
? checkToFromUnknown(check)
|
|
4930
|
+
: (input, options = firstValidationOptions) => validate(input, typeKey.fromUnknown, typeValue.fromUnknown, options);
|
|
4931
|
+
const validateOutput = check
|
|
4932
|
+
? fromUnknown
|
|
4933
|
+
: (input, options = firstValidationOptions) => validate(input, typeKey[outputValidationSymbol], typeValue[outputValidationSymbol], options);
|
|
4438
4934
|
const formatError = (error) => {
|
|
4439
4935
|
if (error.reason.kind === "NotMap")
|
|
4440
4936
|
return `A value ${safelyStringifyUnknownValue(error.reason.value)} is not a Map.`;
|
|
@@ -4475,7 +4971,7 @@ export const map = (key, value) => {
|
|
|
4475
4971
|
return changed ? output : input;
|
|
4476
4972
|
};
|
|
4477
4973
|
const is = (input) => {
|
|
4478
|
-
if (!hasObjectTag(input, "Map")) {
|
|
4974
|
+
if (!hasObjectTag(input, "[object Map]")) {
|
|
4479
4975
|
return false;
|
|
4480
4976
|
}
|
|
4481
4977
|
if (Reflect.ownKeys(input).length !== 0)
|
|
@@ -4486,26 +4982,36 @@ export const map = (key, value) => {
|
|
|
4486
4982
|
}
|
|
4487
4983
|
return true;
|
|
4488
4984
|
};
|
|
4489
|
-
const getTypeIssues = (error, mode) => {
|
|
4985
|
+
const getTypeIssues = (error, mode, path) => {
|
|
4490
4986
|
const mapError = error;
|
|
4491
4987
|
if (mapError.reason.kind !== "Entries") {
|
|
4492
|
-
return singleRuntimeTypeIssue("Map", error, formatError);
|
|
4988
|
+
return singleRuntimeTypeIssue("Map", error, formatError, path);
|
|
4493
4989
|
}
|
|
4494
4990
|
const allIssues = mapError.reason.issues;
|
|
4495
4991
|
const issues = mode === "first" ? [allIssues[0]] : allIssues;
|
|
4496
|
-
|
|
4992
|
+
const result = [];
|
|
4993
|
+
for (const issue of issues) {
|
|
4497
4994
|
if (issue.kind === "Key" || issue.kind === "Value") {
|
|
4498
|
-
|
|
4995
|
+
for (const nestedIssue of (issue.kind === "Key" ? typeKey : typeValue)[getRuntimeTypeIssuesSymbol](issue.error, mode, appendIssuePath(appendIssuePath(path, issue.index), issue.kind === "Key" ? "key" : "value"))) {
|
|
4996
|
+
result.push(nestedIssue);
|
|
4997
|
+
}
|
|
4998
|
+
continue;
|
|
4499
4999
|
}
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
:
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
5000
|
+
result.push({
|
|
5001
|
+
name: "Map",
|
|
5002
|
+
error: mode === "first"
|
|
5003
|
+
? error
|
|
5004
|
+
: {
|
|
5005
|
+
type: "Map",
|
|
5006
|
+
reason: { kind: "Entries", issues: [issue] },
|
|
5007
|
+
},
|
|
5008
|
+
path: appendIssuePath(path, issue.kind === "ExcessProperty" ? issue.key : issue.index),
|
|
5009
|
+
formatError: formatError,
|
|
5010
|
+
});
|
|
5011
|
+
}
|
|
5012
|
+
return result;
|
|
4507
5013
|
};
|
|
4508
|
-
const type = createTypeNode("Map", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { key: typeKey, value: typeValue });
|
|
5014
|
+
const type = createTypeNode("Map", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { additionalProperties: { key: typeKey, value: typeValue }, check });
|
|
4509
5015
|
if (typeByValue === undefined) {
|
|
4510
5016
|
typeByValue = new WeakMap();
|
|
4511
5017
|
mapTypeByValueByKey.set(typeKey, typeByValue);
|
|
@@ -4618,8 +5124,33 @@ const createTupleType = (typeElements) => {
|
|
|
4618
5124
|
}
|
|
4619
5125
|
return validateTupleItems(value, typeElements, validateElement, options, true);
|
|
4620
5126
|
};
|
|
4621
|
-
const
|
|
4622
|
-
const
|
|
5127
|
+
const elementChecks = getRuntimeChecks(typeElements);
|
|
5128
|
+
const checkElement = elementChecks &&
|
|
5129
|
+
((item, options, index) => elementChecks[index](item, options));
|
|
5130
|
+
// The same errors as validate, for identity elements.
|
|
5131
|
+
const check = checkElement &&
|
|
5132
|
+
((value, options) => {
|
|
5133
|
+
if (!Array.isArray(value)) {
|
|
5134
|
+
return { type: "Tuple", reason: { kind: "NotArray", value } };
|
|
5135
|
+
}
|
|
5136
|
+
if (value.length !== expectedLength) {
|
|
5137
|
+
return {
|
|
5138
|
+
type: "Tuple",
|
|
5139
|
+
reason: {
|
|
5140
|
+
kind: "InvalidLength",
|
|
5141
|
+
expected: expectedLength,
|
|
5142
|
+
actual: value.length,
|
|
5143
|
+
},
|
|
5144
|
+
};
|
|
5145
|
+
}
|
|
5146
|
+
return checkIndexedArrayItems("Tuple", value, checkElement, options);
|
|
5147
|
+
});
|
|
5148
|
+
const fromUnknown = check
|
|
5149
|
+
? checkToFromUnknown(check)
|
|
5150
|
+
: (value, options = firstValidationOptions) => validate(value, (element, item, elementOptions) => element.fromUnknown(item, elementOptions), options);
|
|
5151
|
+
const validateOutput = check
|
|
5152
|
+
? fromUnknown
|
|
5153
|
+
: (value, options = firstValidationOptions) => validate(value, (element, item, elementOptions) => element[outputValidationSymbol](item, elementOptions), options);
|
|
4623
5154
|
const formatError = (error) => {
|
|
4624
5155
|
if (error.reason.kind === "NotArray")
|
|
4625
5156
|
return `A value ${safelyStringifyUnknownValue(error.reason.value)} is not a tuple.`;
|
|
@@ -4678,7 +5209,7 @@ const createTupleType = (typeElements) => {
|
|
|
4678
5209
|
return true;
|
|
4679
5210
|
};
|
|
4680
5211
|
const getTypeIssues = createCollectionRuntimeTypeIssues("Tuple", "Items", formatError, (issue) => typeElements[issue.index]);
|
|
4681
|
-
return createTypeNode("Tuple", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { elements: typeElements });
|
|
5212
|
+
return createTypeNode("Tuple", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { additionalProperties: { elements: typeElements }, check });
|
|
4682
5213
|
};
|
|
4683
5214
|
const validateTupleItems = (value, elements, validate, options, checkStructure) => validateIndexedArrayItems("Tuple", value, (value, elementOptions, index) => validate(elements[index], value, elementOptions, index), options, checkStructure);
|
|
4684
5215
|
/**
|
|
@@ -4730,17 +5261,18 @@ export const Digit1To99 = /*#__PURE__*/ union(Digit1To9,
|
|
|
4730
5261
|
export const Digit1To59 = /*#__PURE__*/ union(Digit1To9,
|
|
4731
5262
|
/*#__PURE__*/ templateLiteral(
|
|
4732
5263
|
/*#__PURE__*/ union("1", "2", "3", "4", "5"), Digit));
|
|
4733
|
-
const createObjectRuntimeTypeIssues = (
|
|
5264
|
+
const createObjectRuntimeTypeIssues = (props, recordType) => (error, mode, path) => {
|
|
4734
5265
|
const objectError = error;
|
|
4735
5266
|
if (objectError.reason.kind !== "Properties") {
|
|
4736
|
-
return singleRuntimeTypeIssue("Object", error,
|
|
5267
|
+
return singleRuntimeTypeIssue("Object", error, formatObjectError, path);
|
|
4737
5268
|
}
|
|
4738
5269
|
const propertyErrors = objectError.reason.errors;
|
|
4739
5270
|
const keys = Reflect.ownKeys(propertyErrors);
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
const
|
|
4743
|
-
|
|
5271
|
+
assertNonNullable(keys[0]);
|
|
5272
|
+
const visitCount = mode === "first" ? 1 : keys.length;
|
|
5273
|
+
const result = [];
|
|
5274
|
+
for (let index = 0; index < visitCount; index++) {
|
|
5275
|
+
const key = keys[index];
|
|
4744
5276
|
const propertyError = propertyErrors[key];
|
|
4745
5277
|
const property = typeof key === "string" &&
|
|
4746
5278
|
props !== undefined &&
|
|
@@ -4760,51 +5292,86 @@ const createObjectRuntimeTypeIssues = (defaultFormatter, props, recordType) => (
|
|
|
4760
5292
|
reason: { kind: "Properties", errors },
|
|
4761
5293
|
};
|
|
4762
5294
|
}
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
5295
|
+
result.push({
|
|
5296
|
+
name: "Object",
|
|
5297
|
+
error: ownError,
|
|
5298
|
+
path: appendIssuePath(path, key),
|
|
5299
|
+
// The issue error has this first key, so its message needs no key
|
|
5300
|
+
// enumeration of the error record.
|
|
5301
|
+
formatError: () => formatObjectPropertyError(key, propertyError),
|
|
5302
|
+
});
|
|
5303
|
+
continue;
|
|
4766
5304
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
5305
|
+
// A record property error adds its own key.
|
|
5306
|
+
const nestedIssues = property === undefined
|
|
5307
|
+
? recordType[getRuntimeTypeIssuesSymbol](propertyError, mode, path.slice())
|
|
5308
|
+
: objectPropertyToType(property)[getRuntimeTypeIssuesSymbol](propertyError, mode, appendIssuePath(path, key));
|
|
5309
|
+
// Nested issues are a new array, so one visited issue returns them.
|
|
5310
|
+
if (visitCount === 1)
|
|
5311
|
+
return nestedIssues;
|
|
5312
|
+
for (const issue of nestedIssues)
|
|
5313
|
+
result.push(issue);
|
|
5314
|
+
}
|
|
5315
|
+
return result;
|
|
5316
|
+
};
|
|
5317
|
+
const formatObjectError = (error) => {
|
|
5318
|
+
if (error.reason.kind !== "Properties")
|
|
5319
|
+
return formatPlainObjectRootError(error.reason);
|
|
5320
|
+
const key = Reflect.ownKeys(error.reason.errors).at(0);
|
|
5321
|
+
assertNonNullable(key);
|
|
5322
|
+
return formatObjectPropertyError(key, error.reason.errors[key]);
|
|
5323
|
+
};
|
|
5324
|
+
const formatObjectPropertyError = (key, propertyError) => {
|
|
5325
|
+
assertNonNullable(propertyError);
|
|
5326
|
+
if (propertyError.type === "ObjectPropertyAccess") {
|
|
5327
|
+
switch (propertyError.reason) {
|
|
5328
|
+
case "Accessor":
|
|
5329
|
+
return "An Object property must be a data property. Materialize accessor values into plain data before using this Type or use a different Type.";
|
|
5330
|
+
case "NonEnumerable":
|
|
5331
|
+
return "An Object property must be enumerable. Make it enumerable or use a different Type.";
|
|
4769
5332
|
}
|
|
4770
|
-
|
|
4771
|
-
|
|
5333
|
+
}
|
|
5334
|
+
if (propertyError.type === "ObjectMissingProperty")
|
|
5335
|
+
return `The required property ${safelyStringifyUnknownValue(key)} is missing.`;
|
|
5336
|
+
if (typeof key === "symbol")
|
|
5337
|
+
return "An Object property key must be a string. Remove the symbol property or use a different Type.";
|
|
5338
|
+
if (propertyError.type === "ObjectExcessProperty")
|
|
5339
|
+
return `The property ${safelyStringifyUnknownValue(key)} is not allowed. Remove it or use a different Type.`;
|
|
5340
|
+
return `The property ${safelyStringifyUnknownValue(key)} is invalid.`;
|
|
4772
5341
|
};
|
|
4773
5342
|
const formatPlainObjectRootError = (reason) => reason.kind === "NotObject"
|
|
4774
5343
|
? `A value ${safelyStringifyUnknownValue(reason.value)} is not an object.`
|
|
4775
5344
|
: "The value is an object, but an Object Output must be a plain object or have a null prototype.";
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
5345
|
+
// Ordinary objects list string keys before symbols, so without symbols the
|
|
5346
|
+
// names are the own keys, and cheaper than Reflect.ownKeys. A Proxy may list
|
|
5347
|
+
// symbols first, and Reflect.ownKeys keeps that order.
|
|
5348
|
+
const getOwnKeys = (value) => {
|
|
5349
|
+
const names = globalThis.Object.getOwnPropertyNames(value);
|
|
5350
|
+
return globalThis.Object.getOwnPropertySymbols(value).length === 0
|
|
5351
|
+
? names
|
|
5352
|
+
: Reflect.ownKeys(value);
|
|
5353
|
+
};
|
|
5354
|
+
// An `is` walk in own-key order fails at the first symbol key, after checking
|
|
5355
|
+
// the names before it. Ordinary objects list symbols last, but a Proxy may
|
|
5356
|
+
// list one first. Walking names is faster than walking mixed own keys.
|
|
5357
|
+
const getNamesBeforeSymbol = (value) => {
|
|
5358
|
+
const names = [];
|
|
5359
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
5360
|
+
if (typeof key !== "string")
|
|
5361
|
+
break;
|
|
5362
|
+
names.push(key);
|
|
5363
|
+
}
|
|
5364
|
+
return names;
|
|
5365
|
+
};
|
|
5366
|
+
const checkPlainObject = (value, options) => {
|
|
4794
5367
|
if (value === null || typeof value !== "object") {
|
|
4795
|
-
return
|
|
4796
|
-
type: "Object",
|
|
4797
|
-
reason: { kind: "NotObject", value },
|
|
4798
|
-
});
|
|
5368
|
+
return { type: "Object", reason: { kind: "NotObject", value } };
|
|
4799
5369
|
}
|
|
4800
5370
|
if (!isPlainObject(value)) {
|
|
4801
|
-
return
|
|
4802
|
-
type: "Object",
|
|
4803
|
-
reason: { kind: "UnexpectedPrototype", value },
|
|
4804
|
-
});
|
|
5371
|
+
return { type: "Object", reason: { kind: "UnexpectedPrototype", value } };
|
|
4805
5372
|
}
|
|
4806
5373
|
let errors;
|
|
4807
|
-
for (const key of
|
|
5374
|
+
for (const key of getOwnKeys(value)) {
|
|
4808
5375
|
let propertyError;
|
|
4809
5376
|
if (typeof key !== "string") {
|
|
4810
5377
|
propertyError = { type: "ObjectExcessProperty" };
|
|
@@ -4833,57 +5400,38 @@ const _Object = /*#__PURE__*/ createRootType("Object", (value, options = firstVa
|
|
|
4833
5400
|
break;
|
|
4834
5401
|
}
|
|
4835
5402
|
return errors === undefined
|
|
4836
|
-
?
|
|
4837
|
-
:
|
|
5403
|
+
? undefined
|
|
5404
|
+
: {
|
|
4838
5405
|
type: "Object",
|
|
4839
5406
|
reason: { kind: "Properties", errors },
|
|
4840
|
-
}
|
|
4841
|
-
}
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
assertNonNullable(key);
|
|
4869
|
-
const propertyError = error.reason.errors[key];
|
|
4870
|
-
assertNonNullable(propertyError);
|
|
4871
|
-
if (propertyError.type === "ObjectPropertyAccess") {
|
|
4872
|
-
switch (propertyError.reason) {
|
|
4873
|
-
case "Accessor":
|
|
4874
|
-
return "An Object property must be a data property. Materialize accessor values into plain data before using this Type or use a different Type.";
|
|
4875
|
-
case "NonEnumerable":
|
|
4876
|
-
return "An Object property must be enumerable. Make it enumerable or use a different Type.";
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
if (propertyError.type === "ObjectMissingProperty")
|
|
4880
|
-
return `The required property ${safelyStringifyUnknownValue(key)} is missing.`;
|
|
4881
|
-
if (typeof key === "symbol")
|
|
4882
|
-
return "An Object property key must be a string. Remove the symbol property or use a different Type.";
|
|
4883
|
-
if (propertyError.type === "ObjectExcessProperty")
|
|
4884
|
-
return `The property ${safelyStringifyUnknownValue(key)} is not allowed. Remove it or use a different Type.`;
|
|
4885
|
-
return `The property ${safelyStringifyUnknownValue(key)} is invalid.`;
|
|
4886
|
-
})));
|
|
5407
|
+
};
|
|
5408
|
+
};
|
|
5409
|
+
/**
|
|
5410
|
+
* A {@link Type} for readonly plain objects with unknown property values.
|
|
5411
|
+
*
|
|
5412
|
+
* `Object` is the runtime counterpart of a `Readonly<Record<string, unknown>>`
|
|
5413
|
+
* data boundary. Its prototype rule uses the realm-neutral structural heuristic
|
|
5414
|
+
* described by {@link isPlainObject}. A matching custom root prototype can be
|
|
5415
|
+
* classified as plain; other custom prototypes and class instances are
|
|
5416
|
+
* rejected. Every own property must have a string key and be an enumerable data
|
|
5417
|
+
* property. Accessors, non-enumerable properties, and symbol properties are
|
|
5418
|
+
* rejected without reading their values.
|
|
5419
|
+
*
|
|
5420
|
+
* Use {@link object} when property names are fixed, {@link record} when keys and
|
|
5421
|
+
* values have their own Types, and {@link instanceOf} when an instance belongs
|
|
5422
|
+
* to the domain.
|
|
5423
|
+
*
|
|
5424
|
+
* @group Base
|
|
5425
|
+
*/
|
|
5426
|
+
const _Object = /*#__PURE__*/ createRootType("Object", (value, options = firstValidationOptions) => {
|
|
5427
|
+
const error = checkPlainObject(value, options);
|
|
5428
|
+
return error === undefined
|
|
5429
|
+
? ok(value)
|
|
5430
|
+
: err(error);
|
|
5431
|
+
}, formatObjectError, {
|
|
5432
|
+
getTypeIssues: /*#__PURE__*/ createObjectRuntimeTypeIssues(),
|
|
5433
|
+
check: checkPlainObject,
|
|
5434
|
+
});
|
|
4887
5435
|
// Avoid a local `Object` binding because Babel's CommonJS transform injects
|
|
4888
5436
|
// `Object.defineProperty` before it is initialized:
|
|
4889
5437
|
// https://github.com/babel/babel/issues/16943
|
|
@@ -5047,8 +5595,71 @@ export const record = (key, value) => {
|
|
|
5047
5595
|
}
|
|
5048
5596
|
return validateRecordEntries(input, validateKey, validateValue, options);
|
|
5049
5597
|
};
|
|
5050
|
-
const
|
|
5051
|
-
const
|
|
5598
|
+
const checkKey = typeKey[checkSymbol];
|
|
5599
|
+
const checkValue = typeValue[checkSymbol];
|
|
5600
|
+
// The same errors as validate for identity keys and values. Own keys are
|
|
5601
|
+
// unique, so identity keys never collide.
|
|
5602
|
+
const check = checkKey &&
|
|
5603
|
+
checkValue &&
|
|
5604
|
+
((input, options) => {
|
|
5605
|
+
if (input === null || typeof input !== "object") {
|
|
5606
|
+
return { type: "Record", reason: { kind: "NotRecord", value: input } };
|
|
5607
|
+
}
|
|
5608
|
+
if (!isPlainObject(input)) {
|
|
5609
|
+
return {
|
|
5610
|
+
type: "Record",
|
|
5611
|
+
reason: { kind: "NotPlainRecord", value: input },
|
|
5612
|
+
};
|
|
5613
|
+
}
|
|
5614
|
+
let issues;
|
|
5615
|
+
for (const inputKey of getOwnKeys(input)) {
|
|
5616
|
+
const keyError = checkKey(inputKey, options);
|
|
5617
|
+
if (keyError !== undefined) {
|
|
5618
|
+
(issues ??= []).push({ kind: "Key", key: inputKey, error: keyError });
|
|
5619
|
+
if (options.errors === "first")
|
|
5620
|
+
break;
|
|
5621
|
+
}
|
|
5622
|
+
const descriptor = globalThis.Object.getOwnPropertyDescriptor(input, inputKey);
|
|
5623
|
+
assert(descriptor !== undefined, "Record property descriptor is missing.");
|
|
5624
|
+
if (!("value" in descriptor)) {
|
|
5625
|
+
(issues ??= []).push({ kind: "Accessor", key: inputKey });
|
|
5626
|
+
if (options.errors === "first")
|
|
5627
|
+
break;
|
|
5628
|
+
}
|
|
5629
|
+
else if (!descriptor.enumerable) {
|
|
5630
|
+
(issues ??= []).push({ kind: "NonEnumerable", key: inputKey });
|
|
5631
|
+
if (options.errors === "first")
|
|
5632
|
+
break;
|
|
5633
|
+
}
|
|
5634
|
+
else {
|
|
5635
|
+
const valueError = checkValue(descriptor.value, options);
|
|
5636
|
+
if (valueError !== undefined) {
|
|
5637
|
+
(issues ??= []).push({
|
|
5638
|
+
kind: "Value",
|
|
5639
|
+
key: inputKey,
|
|
5640
|
+
error: valueError,
|
|
5641
|
+
});
|
|
5642
|
+
if (options.errors === "first")
|
|
5643
|
+
break;
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
return issues === undefined
|
|
5648
|
+
? undefined
|
|
5649
|
+
: {
|
|
5650
|
+
type: "Record",
|
|
5651
|
+
reason: {
|
|
5652
|
+
kind: "Entries",
|
|
5653
|
+
issues: issues,
|
|
5654
|
+
},
|
|
5655
|
+
};
|
|
5656
|
+
});
|
|
5657
|
+
const fromUnknown = check
|
|
5658
|
+
? checkToFromUnknown(check)
|
|
5659
|
+
: (input, options = firstValidationOptions) => validate(input, typeKey.fromUnknown, typeValue.fromUnknown, options);
|
|
5660
|
+
const validateOutput = check
|
|
5661
|
+
? fromUnknown
|
|
5662
|
+
: (input, options = firstValidationOptions) => validate(input, typeKey[outputValidationSymbol], typeValue[outputValidationSymbol], options);
|
|
5052
5663
|
const formatError = (error) => {
|
|
5053
5664
|
if (error.reason.kind === "NotRecord")
|
|
5054
5665
|
return `A value ${safelyStringifyUnknownValue(error.reason.value)} is not a Record.`;
|
|
@@ -5102,8 +5713,16 @@ export const record = (key, value) => {
|
|
|
5102
5713
|
return false;
|
|
5103
5714
|
if (!isPlainObject(input))
|
|
5104
5715
|
return false;
|
|
5105
|
-
|
|
5106
|
-
|
|
5716
|
+
// Separate name and symbol enumerations cost dictionary-mode inputs, such
|
|
5717
|
+
// as null-prototype records, about 10% over one Reflect.ownKeys, which is
|
|
5718
|
+
// much slower for ordinary objects.
|
|
5719
|
+
const hasSymbols = globalThis.Object.getOwnPropertySymbols(input).length !== 0;
|
|
5720
|
+
const names = hasSymbols
|
|
5721
|
+
? getNamesBeforeSymbol(input)
|
|
5722
|
+
: globalThis.Object.getOwnPropertyNames(input);
|
|
5723
|
+
for (let index = 0; index < names.length; index++) {
|
|
5724
|
+
const key = names[index];
|
|
5725
|
+
if (!typeKey.is(key))
|
|
5107
5726
|
return false;
|
|
5108
5727
|
const descriptor = globalThis.Object.getOwnPropertyDescriptor(input, key);
|
|
5109
5728
|
if (descriptor === undefined ||
|
|
@@ -5114,17 +5733,17 @@ export const record = (key, value) => {
|
|
|
5114
5733
|
if (!typeValue.is(descriptor.value))
|
|
5115
5734
|
return false;
|
|
5116
5735
|
}
|
|
5117
|
-
return
|
|
5736
|
+
return !hasSymbols;
|
|
5118
5737
|
};
|
|
5119
5738
|
const getTypeIssues = createCollectionRuntimeTypeIssues("Record", "Entries", formatError, (issue) => (issue.kind === "Key" ? typeKey : typeValue));
|
|
5120
|
-
return createTypeNode("Record", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { key: typeKey, value: typeValue });
|
|
5739
|
+
return createTypeNode("Record", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, { additionalProperties: { key: typeKey, value: typeValue }, check });
|
|
5121
5740
|
};
|
|
5122
5741
|
const validateRecordEntries = (input, validateKey, validateValue, options) => {
|
|
5123
5742
|
let issues;
|
|
5124
5743
|
const output = createMutableRecord();
|
|
5125
5744
|
const inputKeyByOutputKey = createMutableRecord();
|
|
5126
5745
|
let changed = false;
|
|
5127
|
-
for (const inputKey of
|
|
5746
|
+
for (const inputKey of getOwnKeys(input)) {
|
|
5128
5747
|
const keyResult = validateKey(inputKey, options);
|
|
5129
5748
|
if (!keyResult.ok) {
|
|
5130
5749
|
(issues ??= []).push({
|
|
@@ -5275,9 +5894,12 @@ export function withDefault(property, value, { strategy } = {}) {
|
|
|
5275
5894
|
? result
|
|
5276
5895
|
: err({ type: "WithDefault", outputError: result.error });
|
|
5277
5896
|
},
|
|
5278
|
-
[getRuntimeTypeIssuesSymbol]: (error, mode) => error.type === "WithDefault" && "outputError" in error
|
|
5279
|
-
? ownGetTypeIssues(error.outputError, mode)
|
|
5280
|
-
: type[getRuntimeTypeIssuesSymbol](error, mode),
|
|
5897
|
+
[getRuntimeTypeIssuesSymbol]: (error, mode, path) => error.type === "WithDefault" && "outputError" in error
|
|
5898
|
+
? ownGetTypeIssues(error.outputError, mode, path)
|
|
5899
|
+
: type[getRuntimeTypeIssuesSymbol](error, mode, path),
|
|
5900
|
+
// Output validation wraps errors, so a copied check would not match it.
|
|
5901
|
+
[checkSymbol]: undefined,
|
|
5902
|
+
[refinementsSymbol]: undefined,
|
|
5281
5903
|
};
|
|
5282
5904
|
const operations = {
|
|
5283
5905
|
output: outputWithIssues,
|
|
@@ -5336,13 +5958,32 @@ const missingDefaultValue = /*#__PURE__*/ globalThis.Symbol();
|
|
|
5336
5958
|
export function object(props, recordType) {
|
|
5337
5959
|
return createObjectType(snapshotObjectProps(props), recordType);
|
|
5338
5960
|
}
|
|
5339
|
-
const createObjectType = (props, recordType
|
|
5961
|
+
const createObjectType = (props, recordType,
|
|
5962
|
+
// A parent Object Type reuses the key order of its child.
|
|
5963
|
+
keys = globalThis.Object.keys(props)) => {
|
|
5340
5964
|
const runtimeProps = props;
|
|
5341
|
-
const
|
|
5342
|
-
|
|
5343
|
-
|
|
5965
|
+
const keyCount = keys.length;
|
|
5966
|
+
const propertyTypes = createMutableArray(keyCount);
|
|
5967
|
+
const optionalFlags = createMutableArray(keyCount);
|
|
5968
|
+
for (let index = 0; index < keyCount; index++) {
|
|
5969
|
+
const property = runtimeProps[keys[index]];
|
|
5970
|
+
if (isDefaultProperty(property)) {
|
|
5971
|
+
return property[defaultPropertySymbol].createObject(props, recordType);
|
|
5972
|
+
}
|
|
5973
|
+
const isOptional = isOptionalProperty(property);
|
|
5974
|
+
const type = isOptional ? property.type : property;
|
|
5975
|
+
propertyTypes[index] = type;
|
|
5976
|
+
optionalFlags[index] = isOptional;
|
|
5344
5977
|
}
|
|
5345
|
-
const
|
|
5978
|
+
const propertyChecks = propertyTypes.map((type) => type[checkSymbol]);
|
|
5979
|
+
const recordCheck = recordType?.value[checkSymbol];
|
|
5980
|
+
// When every property and Record value Type is an identity Type, validation
|
|
5981
|
+
// never changes a value.
|
|
5982
|
+
const isIdentity = !propertyChecks.includes(undefined) &&
|
|
5983
|
+
(recordType === undefined || recordCheck !== undefined);
|
|
5984
|
+
let isOutputs;
|
|
5985
|
+
// Returns `undefined` for a valid input it did not change, so a valid
|
|
5986
|
+
// identity Object allocates no Result.
|
|
5346
5987
|
const validate = (value, options, exactOutput) => {
|
|
5347
5988
|
if (value === null || typeof value !== "object") {
|
|
5348
5989
|
return err({
|
|
@@ -5357,116 +5998,158 @@ const createObjectType = (props, recordType) => {
|
|
|
5357
5998
|
reason: { kind: "UnexpectedPrototype", value: input },
|
|
5358
5999
|
});
|
|
5359
6000
|
}
|
|
6001
|
+
const ownKeys = getOwnKeys(input);
|
|
6002
|
+
// Descriptors by visit index let a copy-on-write Output reuse the values
|
|
6003
|
+
// already read. Only a property without a check can change its value.
|
|
6004
|
+
const descriptors = isIdentity
|
|
6005
|
+
? undefined
|
|
6006
|
+
: createMutableArray(keyCount + ownKeys.length);
|
|
5360
6007
|
let errors;
|
|
5361
6008
|
let output;
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
};
|
|
5372
|
-
for (const [key] of inputDescriptorByKey) {
|
|
5373
|
-
const property = typeof key === "string" && globalThis.Object.hasOwn(runtimeProps, key)
|
|
5374
|
-
? runtimeProps[key]
|
|
5375
|
-
: undefined;
|
|
6009
|
+
// Own keys that are exactly the declared keys in their order leave the
|
|
6010
|
+
// undeclared-key loop nothing to visit. A first-mode stop leaves the flag
|
|
6011
|
+
// unchecked, but then that loop does not run either.
|
|
6012
|
+
let ownKeysAreDeclared = ownKeys.length === keyCount;
|
|
6013
|
+
for (let index = 0; (errors === undefined || options.errors !== "first") && index < keyCount; index++) {
|
|
6014
|
+
const key = keys[index];
|
|
6015
|
+
if (ownKeysAreDeclared && ownKeys[index] !== key) {
|
|
6016
|
+
ownKeysAreDeclared = false;
|
|
6017
|
+
}
|
|
5376
6018
|
const descriptor = globalThis.Object.getOwnPropertyDescriptor(input, key);
|
|
5377
|
-
|
|
6019
|
+
if (descriptors !== undefined)
|
|
6020
|
+
descriptors[index] = descriptor;
|
|
6021
|
+
let error;
|
|
5378
6022
|
if (descriptor === undefined) {
|
|
5379
|
-
|
|
5380
|
-
if (isOptionalProperty(property)) {
|
|
6023
|
+
if (optionalFlags[index])
|
|
5381
6024
|
continue;
|
|
6025
|
+
error = { type: "ObjectMissingProperty" };
|
|
6026
|
+
}
|
|
6027
|
+
else if (!("value" in descriptor)) {
|
|
6028
|
+
error = {
|
|
6029
|
+
type: "ObjectPropertyAccess",
|
|
6030
|
+
reason: "Accessor",
|
|
6031
|
+
};
|
|
6032
|
+
}
|
|
6033
|
+
else if (!descriptor.enumerable) {
|
|
6034
|
+
error = {
|
|
6035
|
+
type: "ObjectPropertyAccess",
|
|
6036
|
+
reason: "NonEnumerable",
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
else {
|
|
6040
|
+
const propertyValue = descriptor.value;
|
|
6041
|
+
const check = propertyChecks[index];
|
|
6042
|
+
if (check !== undefined) {
|
|
6043
|
+
const propertyError = check(propertyValue, options);
|
|
6044
|
+
if (propertyError === undefined) {
|
|
6045
|
+
if (output !== undefined && errors === undefined) {
|
|
6046
|
+
output[key] = propertyValue;
|
|
6047
|
+
}
|
|
6048
|
+
continue;
|
|
6049
|
+
}
|
|
6050
|
+
error = propertyError;
|
|
5382
6051
|
}
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
6052
|
+
else {
|
|
6053
|
+
const result = exactOutput
|
|
6054
|
+
? propertyTypes[index][outputValidationSymbol](propertyValue, options)
|
|
6055
|
+
: propertyTypes[index].fromUnknown(propertyValue, options);
|
|
6056
|
+
if (result.ok) {
|
|
6057
|
+
if (errors !== undefined || exactOutput)
|
|
6058
|
+
continue;
|
|
6059
|
+
if (output === undefined) {
|
|
6060
|
+
if (globalThis.Object.is(result.value, propertyValue))
|
|
6061
|
+
continue;
|
|
6062
|
+
output = copyVisitedProperties(descriptors, ownKeys, index);
|
|
6063
|
+
}
|
|
6064
|
+
output[key] = result.value;
|
|
6065
|
+
continue;
|
|
6066
|
+
}
|
|
6067
|
+
error = result.error;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
errors ??= createMutableRecord();
|
|
6071
|
+
errors[key] = error;
|
|
6072
|
+
}
|
|
6073
|
+
const ownKeyEnd = ownKeysAreDeclared ? 0 : ownKeys.length;
|
|
6074
|
+
for (let position = 0; (errors === undefined || options.errors !== "first") &&
|
|
6075
|
+
position < ownKeyEnd; position++) {
|
|
6076
|
+
const key = ownKeys[position];
|
|
6077
|
+
const index = keyCount + position;
|
|
6078
|
+
if (typeof key === "string" &&
|
|
6079
|
+
// Own keys usually follow the declared order.
|
|
6080
|
+
((position < keyCount && key === keys[position]) ||
|
|
6081
|
+
globalThis.Object.hasOwn(runtimeProps, key))) {
|
|
6082
|
+
if (descriptors !== undefined)
|
|
6083
|
+
descriptors[index] = undefined;
|
|
5386
6084
|
continue;
|
|
5387
6085
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
6086
|
+
const descriptor = globalThis.Object.getOwnPropertyDescriptor(input, key);
|
|
6087
|
+
assert(descriptor !== undefined, "Object property is missing.");
|
|
6088
|
+
if (descriptors !== undefined)
|
|
6089
|
+
descriptors[index] = descriptor;
|
|
6090
|
+
let error;
|
|
6091
|
+
if (recordType === undefined) {
|
|
6092
|
+
error = {
|
|
5390
6093
|
type: "ObjectExcessProperty",
|
|
5391
|
-
}
|
|
5392
|
-
if (options.errors === "first")
|
|
5393
|
-
break;
|
|
5394
|
-
continue;
|
|
6094
|
+
};
|
|
5395
6095
|
}
|
|
5396
|
-
if (
|
|
5397
|
-
|
|
6096
|
+
else if (typeof key !== "string") {
|
|
6097
|
+
error = createRecordPropertyError({
|
|
5398
6098
|
kind: "Key",
|
|
5399
6099
|
key,
|
|
5400
|
-
error: {
|
|
5401
|
-
|
|
5402
|
-
expected: "String",
|
|
5403
|
-
value: key,
|
|
5404
|
-
},
|
|
5405
|
-
}));
|
|
5406
|
-
if (options.errors === "first")
|
|
5407
|
-
break;
|
|
5408
|
-
continue;
|
|
6100
|
+
error: { type: "TypeOf", expected: "String", value: key },
|
|
6101
|
+
});
|
|
5409
6102
|
}
|
|
5410
|
-
if (!("value" in descriptor)) {
|
|
5411
|
-
|
|
6103
|
+
else if (!("value" in descriptor)) {
|
|
6104
|
+
error = {
|
|
5412
6105
|
type: "ObjectPropertyAccess",
|
|
5413
6106
|
reason: "Accessor",
|
|
5414
6107
|
};
|
|
5415
|
-
setError(key, propertyError);
|
|
5416
|
-
if (options.errors === "first")
|
|
5417
|
-
break;
|
|
5418
|
-
continue;
|
|
5419
6108
|
}
|
|
5420
|
-
if (!descriptor.enumerable) {
|
|
5421
|
-
|
|
6109
|
+
else if (!descriptor.enumerable) {
|
|
6110
|
+
error = {
|
|
5422
6111
|
type: "ObjectPropertyAccess",
|
|
5423
6112
|
reason: "NonEnumerable",
|
|
5424
6113
|
};
|
|
5425
|
-
setError(key, propertyError);
|
|
5426
|
-
if (options.errors === "first")
|
|
5427
|
-
break;
|
|
5428
|
-
continue;
|
|
5429
|
-
}
|
|
5430
|
-
const propertyValue = descriptor.value;
|
|
5431
|
-
const propertyType = property === undefined
|
|
5432
|
-
? recordType.value
|
|
5433
|
-
: objectPropertyToType(property);
|
|
5434
|
-
const result = exactOutput
|
|
5435
|
-
? propertyType[outputValidationSymbol](propertyValue, options)
|
|
5436
|
-
: propertyType.fromUnknown(propertyValue, options);
|
|
5437
|
-
if (!result.ok) {
|
|
5438
|
-
setError(key, property === undefined
|
|
5439
|
-
? createRecordPropertyError({
|
|
5440
|
-
kind: "Value",
|
|
5441
|
-
key,
|
|
5442
|
-
error: result.error,
|
|
5443
|
-
})
|
|
5444
|
-
: result.error);
|
|
5445
|
-
if (options.errors === "first")
|
|
5446
|
-
break;
|
|
5447
|
-
continue;
|
|
5448
6114
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
for (const [previousKey, previousDescriptor] of inputDescriptorByKey) {
|
|
5460
|
-
if (previousKey === key)
|
|
5461
|
-
break;
|
|
5462
|
-
if (previousDescriptor !== undefined &&
|
|
5463
|
-
"value" in previousDescriptor &&
|
|
5464
|
-
previousDescriptor.enumerable) {
|
|
5465
|
-
output[previousKey] = previousDescriptor.value;
|
|
6115
|
+
else {
|
|
6116
|
+
const propertyValue = descriptor.value;
|
|
6117
|
+
let valueError;
|
|
6118
|
+
if (recordCheck !== undefined) {
|
|
6119
|
+
const checkError = recordCheck(propertyValue, options);
|
|
6120
|
+
if (checkError === undefined) {
|
|
6121
|
+
if (output !== undefined && errors === undefined) {
|
|
6122
|
+
output[key] = propertyValue;
|
|
6123
|
+
}
|
|
6124
|
+
continue;
|
|
5466
6125
|
}
|
|
6126
|
+
valueError = checkError;
|
|
5467
6127
|
}
|
|
6128
|
+
else {
|
|
6129
|
+
const result = exactOutput
|
|
6130
|
+
? recordType.value[outputValidationSymbol](propertyValue, options)
|
|
6131
|
+
: recordType.value.fromUnknown(propertyValue, options);
|
|
6132
|
+
if (result.ok) {
|
|
6133
|
+
if (errors !== undefined || exactOutput)
|
|
6134
|
+
continue;
|
|
6135
|
+
if (output === undefined) {
|
|
6136
|
+
if (globalThis.Object.is(result.value, propertyValue))
|
|
6137
|
+
continue;
|
|
6138
|
+
output = copyVisitedProperties(descriptors, ownKeys, index);
|
|
6139
|
+
}
|
|
6140
|
+
output[key] = result.value;
|
|
6141
|
+
continue;
|
|
6142
|
+
}
|
|
6143
|
+
valueError = result.error;
|
|
6144
|
+
}
|
|
6145
|
+
error = createRecordPropertyError({
|
|
6146
|
+
kind: "Value",
|
|
6147
|
+
key,
|
|
6148
|
+
error: valueError,
|
|
6149
|
+
});
|
|
5468
6150
|
}
|
|
5469
|
-
|
|
6151
|
+
errors ??= createMutableRecord();
|
|
6152
|
+
errors[key] = error;
|
|
5470
6153
|
}
|
|
5471
6154
|
if (errors !== undefined) {
|
|
5472
6155
|
return err({
|
|
@@ -5474,69 +6157,70 @@ const createObjectType = (props, recordType) => {
|
|
|
5474
6157
|
reason: { kind: "Properties", errors },
|
|
5475
6158
|
});
|
|
5476
6159
|
}
|
|
5477
|
-
|
|
5478
|
-
return ok(input);
|
|
5479
|
-
return ok(output);
|
|
6160
|
+
return output === undefined ? undefined : ok(output);
|
|
5480
6161
|
};
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
const
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
switch (propertyError.reason) {
|
|
5492
|
-
case "Accessor":
|
|
5493
|
-
return "An Object property must be a data property. Materialize accessor values into plain data before using this Type or use a different Type.";
|
|
5494
|
-
case "NonEnumerable":
|
|
5495
|
-
return "An Object property must be enumerable. Make it enumerable or use a different Type.";
|
|
5496
|
-
}
|
|
6162
|
+
// Properties visited before `end` are valid and unchanged, so a new Output
|
|
6163
|
+
// starts with their values.
|
|
6164
|
+
const copyVisitedProperties = (descriptors, ownKeys, end) => {
|
|
6165
|
+
const output = globalThis.Object.create(null);
|
|
6166
|
+
for (let index = 0; index < end; index++) {
|
|
6167
|
+
const descriptor = descriptors[index];
|
|
6168
|
+
if (descriptor === undefined)
|
|
6169
|
+
continue;
|
|
6170
|
+
output[index < keyCount ? keys[index] : ownKeys[index - keyCount]] =
|
|
6171
|
+
descriptor.value;
|
|
5497
6172
|
}
|
|
5498
|
-
|
|
5499
|
-
return `The required property ${safelyStringifyUnknownValue(key)} is missing.`;
|
|
5500
|
-
if (typeof key === "symbol")
|
|
5501
|
-
return "An Object property key must be a string. Remove the symbol property or use a different Type.";
|
|
5502
|
-
if (propertyError.type === "ObjectExcessProperty")
|
|
5503
|
-
return `The property ${safelyStringifyUnknownValue(key)} is not allowed. Remove it or use a different Type.`;
|
|
5504
|
-
return `The property ${safelyStringifyUnknownValue(key)} is invalid.`;
|
|
6173
|
+
return output;
|
|
5505
6174
|
};
|
|
5506
|
-
|
|
6175
|
+
// An identity Object builds no Output, so its validation returns an Err or
|
|
6176
|
+
// `undefined`.
|
|
6177
|
+
const check = isIdentity
|
|
6178
|
+
? (value, options) => {
|
|
6179
|
+
const result = validate(value, options, false);
|
|
6180
|
+
return result?.ok === false ? result.error : undefined;
|
|
6181
|
+
}
|
|
6182
|
+
: undefined;
|
|
6183
|
+
const fromUnknown = (value, options = firstValidationOptions) => validate(value, options, false) ??
|
|
6184
|
+
ok(value);
|
|
6185
|
+
const validateOutput = isIdentity
|
|
6186
|
+
? fromUnknown
|
|
6187
|
+
: (value, options = firstValidationOptions) => validate(value, options, true) ??
|
|
6188
|
+
ok(value);
|
|
5507
6189
|
let hasNonRootType = false;
|
|
5508
6190
|
let canSkipTo = recordType === undefined || recordType.value[encoderSymbol] === identity;
|
|
5509
|
-
for (
|
|
5510
|
-
const
|
|
5511
|
-
const type = objectPropertyToType(property);
|
|
6191
|
+
for (let index = 0; index < keyCount; index++) {
|
|
6192
|
+
const type = propertyTypes[index];
|
|
5512
6193
|
if (type[encoderSymbol] !== identity)
|
|
5513
6194
|
canSkipTo = false;
|
|
5514
|
-
|
|
5515
|
-
if (rootType !== type)
|
|
6195
|
+
if (type.parent != null)
|
|
5516
6196
|
hasNonRootType = true;
|
|
5517
|
-
rootProps[key] = isOptionalProperty(property)
|
|
5518
|
-
? optional(rootType)
|
|
5519
|
-
: rootType;
|
|
5520
6197
|
}
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
6198
|
+
let parent = null;
|
|
6199
|
+
if (hasNonRootType || recordType?.parent) {
|
|
6200
|
+
const rootProps = createMutableRecord();
|
|
6201
|
+
for (let index = 0; index < keyCount; index++) {
|
|
6202
|
+
const rootType = getTerminalRuntimeNode(propertyTypes[index]);
|
|
6203
|
+
rootProps[keys[index]] = optionalFlags[index]
|
|
6204
|
+
? optional(rootType)
|
|
6205
|
+
: rootType;
|
|
6206
|
+
}
|
|
6207
|
+
parent = createObjectType(rootProps, (recordType?.parent ?? recordType), keys);
|
|
5527
6208
|
}
|
|
6209
|
+
let inputFroms;
|
|
5528
6210
|
const recordValueFromInput = recordType
|
|
5529
6211
|
? getTerminalRuntimeNode(recordType.value[fromSymbol])
|
|
5530
6212
|
: undefined;
|
|
5531
6213
|
const fromParent = parent
|
|
5532
6214
|
? (value, options = firstValidationOptions) => {
|
|
6215
|
+
const propertyFroms = (inputFroms ??= propertyTypes.map((type) => getTerminalRuntimeNode(type[fromSymbol])));
|
|
5533
6216
|
let errors;
|
|
5534
6217
|
let output;
|
|
5535
|
-
for (
|
|
6218
|
+
for (let index = 0; index < keyCount; index++) {
|
|
6219
|
+
const key = keys[index];
|
|
5536
6220
|
if (!globalThis.Object.hasOwn(value, key))
|
|
5537
6221
|
continue;
|
|
5538
6222
|
const propertyValue = value[key];
|
|
5539
|
-
const result =
|
|
6223
|
+
const result = propertyFroms[index](propertyValue, options);
|
|
5540
6224
|
if (result.ok) {
|
|
5541
6225
|
if (!globalThis.Object.is(result.value, propertyValue)) {
|
|
5542
6226
|
(output ??= createMutableRecord(value))[key] = result.value;
|
|
@@ -5584,11 +6268,12 @@ const createObjectType = (props, recordType) => {
|
|
|
5584
6268
|
? identity
|
|
5585
6269
|
: (value) => {
|
|
5586
6270
|
let output;
|
|
5587
|
-
for (
|
|
6271
|
+
for (let index = 0; index < keyCount; index++) {
|
|
6272
|
+
const key = keys[index];
|
|
5588
6273
|
if (!globalThis.Object.hasOwn(value, key))
|
|
5589
6274
|
continue;
|
|
5590
6275
|
const propertyValue = value[key];
|
|
5591
|
-
const encoded =
|
|
6276
|
+
const encoded = propertyTypes[index][encoderSymbol](propertyValue);
|
|
5592
6277
|
if (!globalThis.Object.is(encoded, propertyValue)) {
|
|
5593
6278
|
(output ??= createMutableRecord(value))[key] = encoded;
|
|
5594
6279
|
}
|
|
@@ -5606,50 +6291,79 @@ const createObjectType = (props, recordType) => {
|
|
|
5606
6291
|
}
|
|
5607
6292
|
return output ?? value;
|
|
5608
6293
|
};
|
|
5609
|
-
const
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
return false;
|
|
5614
|
-
for (const key of keys) {
|
|
5615
|
-
const property = runtimeProps[key];
|
|
5616
|
-
const descriptor = globalThis.Object.getOwnPropertyDescriptor(value, key);
|
|
6294
|
+
const isDeclared = (value) => {
|
|
6295
|
+
const isProperties = (isOutputs ??= propertyTypes.map(getIs));
|
|
6296
|
+
for (let index = 0; index < keyCount; index++) {
|
|
6297
|
+
const descriptor = globalThis.Object.getOwnPropertyDescriptor(value, keys[index]);
|
|
5617
6298
|
if (descriptor === undefined) {
|
|
5618
|
-
if (!
|
|
6299
|
+
if (!optionalFlags[index])
|
|
5619
6300
|
return false;
|
|
5620
6301
|
continue;
|
|
5621
6302
|
}
|
|
5622
6303
|
if (!("value" in descriptor) ||
|
|
5623
6304
|
!descriptor.enumerable ||
|
|
5624
|
-
!
|
|
6305
|
+
!isProperties[index](descriptor.value)) {
|
|
5625
6306
|
return false;
|
|
5626
6307
|
}
|
|
5627
6308
|
}
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
6309
|
+
return true;
|
|
6310
|
+
};
|
|
6311
|
+
// Separate predicates keep each small; one shared by both cases was up to
|
|
6312
|
+
// 5% slower in either.
|
|
6313
|
+
const is = recordType === undefined
|
|
6314
|
+
? (value) => {
|
|
6315
|
+
if (value === null || typeof value !== "object")
|
|
5634
6316
|
return false;
|
|
5635
|
-
|
|
5636
|
-
if (descriptor === undefined ||
|
|
5637
|
-
!("value" in descriptor) ||
|
|
5638
|
-
!descriptor.enumerable) {
|
|
6317
|
+
if (!isPlainObject(value) || !isDeclared(value))
|
|
5639
6318
|
return false;
|
|
6319
|
+
// Checking names has no effects without a Record, so symbols, which
|
|
6320
|
+
// fail, are counted after them.
|
|
6321
|
+
const names = globalThis.Object.getOwnPropertyNames(value);
|
|
6322
|
+
for (let position = 0; position < names.length; position++) {
|
|
6323
|
+
const key = names[position];
|
|
6324
|
+
if (!(position < keyCount && key === keys[position]) &&
|
|
6325
|
+
!globalThis.Object.hasOwn(runtimeProps, key)) {
|
|
6326
|
+
return false;
|
|
6327
|
+
}
|
|
5640
6328
|
}
|
|
5641
|
-
|
|
5642
|
-
return false;
|
|
6329
|
+
return globalThis.Object.getOwnPropertySymbols(value).length === 0;
|
|
5643
6330
|
}
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
6331
|
+
: (value) => {
|
|
6332
|
+
if (value === null || typeof value !== "object")
|
|
6333
|
+
return false;
|
|
6334
|
+
if (!isPlainObject(value) || !isDeclared(value))
|
|
6335
|
+
return false;
|
|
6336
|
+
const hasSymbols = globalThis.Object.getOwnPropertySymbols(value).length !== 0;
|
|
6337
|
+
const names = hasSymbols
|
|
6338
|
+
? getNamesBeforeSymbol(value)
|
|
6339
|
+
: globalThis.Object.getOwnPropertyNames(value);
|
|
6340
|
+
for (let position = 0; position < names.length; position++) {
|
|
6341
|
+
const key = names[position];
|
|
6342
|
+
if ((position < keyCount && key === keys[position]) ||
|
|
6343
|
+
globalThis.Object.hasOwn(runtimeProps, key)) {
|
|
6344
|
+
continue;
|
|
6345
|
+
}
|
|
6346
|
+
const descriptor = globalThis.Object.getOwnPropertyDescriptor(value, key);
|
|
6347
|
+
if (descriptor === undefined ||
|
|
6348
|
+
!("value" in descriptor) ||
|
|
6349
|
+
!descriptor.enumerable) {
|
|
6350
|
+
return false;
|
|
6351
|
+
}
|
|
6352
|
+
if (!recordType.value.is(descriptor.value))
|
|
6353
|
+
return false;
|
|
6354
|
+
}
|
|
6355
|
+
return !hasSymbols;
|
|
6356
|
+
};
|
|
6357
|
+
const getTypeIssues = createObjectRuntimeTypeIssues(runtimeProps, recordType);
|
|
6358
|
+
return createTypeNode("Object", parent, fromUnknown, is, validateOutput, from, to, getTypeIssues, {
|
|
6359
|
+
additionalProperties: recordType
|
|
6360
|
+
? {
|
|
6361
|
+
props: runtimeProps,
|
|
6362
|
+
record: recordType,
|
|
6363
|
+
}
|
|
6364
|
+
: { props: runtimeProps },
|
|
6365
|
+
check,
|
|
6366
|
+
});
|
|
5653
6367
|
};
|
|
5654
6368
|
const createDefaultObjectType = (props, recordType) => {
|
|
5655
6369
|
const inputProps = createMutableRecord();
|
|
@@ -5665,7 +6379,7 @@ const createDefaultObjectType = (props, recordType) => {
|
|
|
5665
6379
|
else {
|
|
5666
6380
|
inputProps[key] = property;
|
|
5667
6381
|
const type = objectPropertyToType(property);
|
|
5668
|
-
const output = createRootType(type.name, type[outputValidationSymbol], type.formatError, type[getRuntimeTypeIssuesSymbol]);
|
|
6382
|
+
const output = createRootType(type.name, type[outputValidationSymbol], type.formatError, { getTypeIssues: type[getRuntimeTypeIssuesSymbol] });
|
|
5669
6383
|
outputProps[key] =
|
|
5670
6384
|
optionalPropertySymbol in property
|
|
5671
6385
|
? createOptionalProperty(output)
|
|
@@ -5698,16 +6412,18 @@ const createDefaultObjectType = (props, recordType) => {
|
|
|
5698
6412
|
}
|
|
5699
6413
|
return source[encoderSymbol](result);
|
|
5700
6414
|
};
|
|
5701
|
-
return createTypeNode("Object", parent, (value, options) => flatMapResult(source.fromUnknown(value, options), finish), output.is, output[outputValidationSymbol], createFromOperation(fromParent), to, createObjectRuntimeTypeIssues(
|
|
5702
|
-
|
|
5703
|
-
|
|
6415
|
+
return createTypeNode("Object", parent, (value, options) => flatMapResult(source.fromUnknown(value, options), finish), output.is, output[outputValidationSymbol], createFromOperation(fromParent), to, createObjectRuntimeTypeIssues(props, recordType), {
|
|
6416
|
+
additionalProperties: recordType
|
|
6417
|
+
? { props, record: recordType }
|
|
6418
|
+
: { props },
|
|
6419
|
+
});
|
|
5704
6420
|
};
|
|
5705
6421
|
// Read descriptors instead of spreading so accessors are not invoked,
|
|
5706
6422
|
// non-enumerable declarations are retained, and later mutations are isolated.
|
|
5707
6423
|
const snapshotObjectProps = (props, runtimeProps = createMutableRecord()) => {
|
|
5708
6424
|
const errorMessage = "Object schema properties must be own string-keyed data properties.";
|
|
5709
6425
|
assert(isPlainObject(props), errorMessage);
|
|
5710
|
-
for (const key of
|
|
6426
|
+
for (const key of getOwnKeys(props)) {
|
|
5711
6427
|
const descriptor = globalThis.Object.getOwnPropertyDescriptor(props, key);
|
|
5712
6428
|
assert(typeof key === "string" &&
|
|
5713
6429
|
descriptor !== undefined &&
|
|
@@ -5931,13 +6647,13 @@ export const objectKeys = (key) => (type) => {
|
|
|
5931
6647
|
? result
|
|
5932
6648
|
: err({ type: "ObjectKeys", outputError: result.error });
|
|
5933
6649
|
};
|
|
5934
|
-
const getTypeIssues = (error, mode) => {
|
|
6650
|
+
const getTypeIssues = (error, mode, path) => {
|
|
5935
6651
|
if ("outputError" in error) {
|
|
5936
|
-
return runtimeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode);
|
|
6652
|
+
return runtimeOutput[getRuntimeTypeIssuesSymbol](error.outputError, mode, path);
|
|
5937
6653
|
}
|
|
5938
|
-
return input[getRuntimeTypeIssuesSymbol](error.error, mode);
|
|
6654
|
+
return input[getRuntimeTypeIssuesSymbol](error.error, mode, path);
|
|
5939
6655
|
};
|
|
5940
|
-
return createTypeNode("ObjectKeys", Unknown, fromUnknown, runtimeOutput.is, validateOutput, createFromOperation(fromUnknown), to, getTypeIssues, { key, output: type });
|
|
6656
|
+
return createTypeNode("ObjectKeys", Unknown, fromUnknown, runtimeOutput.is, validateOutput, createFromOperation(fromUnknown), to, getTypeIssues, { additionalProperties: { key, output: type } });
|
|
5941
6657
|
};
|
|
5942
6658
|
export function result(okType, errorType) {
|
|
5943
6659
|
return discriminatedUnion("ok", createObjectType({
|
|
@@ -5995,21 +6711,28 @@ export function discriminatedUnion(...keyOrMembers) {
|
|
|
5995
6711
|
membersByDiscriminator.set(value, member);
|
|
5996
6712
|
}
|
|
5997
6713
|
const routeUnknown = (value) => {
|
|
5998
|
-
|
|
5999
|
-
? err({ type: "Object", reason: { kind: "NotObject", value } })
|
|
6000
|
-
: !isPlainObject(value)
|
|
6001
|
-
? err({
|
|
6002
|
-
type: "Object",
|
|
6003
|
-
reason: { kind: "UnexpectedPrototype", value },
|
|
6004
|
-
})
|
|
6005
|
-
: ok(value);
|
|
6006
|
-
if (!objectResult.ok) {
|
|
6714
|
+
if (value === null || typeof value !== "object") {
|
|
6007
6715
|
return err({
|
|
6008
6716
|
type: "DiscriminatedUnion",
|
|
6009
|
-
reason: {
|
|
6717
|
+
reason: {
|
|
6718
|
+
kind: "Object",
|
|
6719
|
+
error: { type: "Object", reason: { kind: "NotObject", value } },
|
|
6720
|
+
},
|
|
6010
6721
|
});
|
|
6011
6722
|
}
|
|
6012
|
-
|
|
6723
|
+
if (!isPlainObject(value)) {
|
|
6724
|
+
return err({
|
|
6725
|
+
type: "DiscriminatedUnion",
|
|
6726
|
+
reason: {
|
|
6727
|
+
kind: "Object",
|
|
6728
|
+
error: {
|
|
6729
|
+
type: "Object",
|
|
6730
|
+
reason: { kind: "UnexpectedPrototype", value },
|
|
6731
|
+
},
|
|
6732
|
+
},
|
|
6733
|
+
});
|
|
6734
|
+
}
|
|
6735
|
+
const input = value;
|
|
6013
6736
|
const descriptor = globalThis.Object.getOwnPropertyDescriptor(input, key);
|
|
6014
6737
|
let discriminator;
|
|
6015
6738
|
if (descriptor === undefined) {
|
|
@@ -6050,16 +6773,15 @@ export function discriminatedUnion(...keyOrMembers) {
|
|
|
6050
6773
|
}
|
|
6051
6774
|
return ok(member);
|
|
6052
6775
|
};
|
|
6053
|
-
const
|
|
6054
|
-
|
|
6055
|
-
:
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
});
|
|
6776
|
+
const createMemberError = (member, error) => ({
|
|
6777
|
+
type: "DiscriminatedUnion",
|
|
6778
|
+
reason: {
|
|
6779
|
+
kind: "Member",
|
|
6780
|
+
discriminator: member.props[key].expected,
|
|
6781
|
+
error,
|
|
6782
|
+
},
|
|
6783
|
+
});
|
|
6784
|
+
const wrapMemberResult = (member, result) => result.ok ? result : err(createMemberError(member, result.error));
|
|
6063
6785
|
const validateUnknown = (value, options = firstValidationOptions, useParent, exactOutput) => {
|
|
6064
6786
|
const routeResult = routeUnknown(value);
|
|
6065
6787
|
if (!routeResult.ok)
|
|
@@ -6098,24 +6820,34 @@ export function discriminatedUnion(...keyOrMembers) {
|
|
|
6098
6820
|
}
|
|
6099
6821
|
};
|
|
6100
6822
|
const defaultFormatter = formatError;
|
|
6101
|
-
const getTypeIssues = (error, mode) => {
|
|
6823
|
+
const getTypeIssues = (error, mode, path) => {
|
|
6102
6824
|
const discriminatedUnionError = error;
|
|
6103
6825
|
if (discriminatedUnionError.reason.kind === "Member") {
|
|
6104
6826
|
const reason = discriminatedUnionError.reason;
|
|
6105
6827
|
const member = membersByDiscriminator.get(reason.discriminator);
|
|
6106
6828
|
assertNonNullable(member);
|
|
6107
|
-
return member[getRuntimeTypeIssuesSymbol](reason.error, mode);
|
|
6829
|
+
return member[getRuntimeTypeIssuesSymbol](reason.error, mode, path);
|
|
6108
6830
|
}
|
|
6109
6831
|
if (discriminatedUnionError.reason.kind === "PropertyAccess" ||
|
|
6110
6832
|
discriminatedUnionError.reason.kind === "Discriminator") {
|
|
6111
|
-
return singleRuntimeTypeIssue("DiscriminatedUnion", discriminatedUnionError, defaultFormatter,
|
|
6833
|
+
return singleRuntimeTypeIssue("DiscriminatedUnion", discriminatedUnionError, defaultFormatter, appendIssuePath(path, discriminatedUnionError.reason.key));
|
|
6112
6834
|
}
|
|
6113
|
-
return singleRuntimeTypeIssue("DiscriminatedUnion", error, defaultFormatter);
|
|
6835
|
+
return singleRuntimeTypeIssue("DiscriminatedUnion", error, defaultFormatter, path);
|
|
6114
6836
|
};
|
|
6115
6837
|
const parentFromUnknown = (value, options) => validateUnknown(value, options, true, false);
|
|
6116
6838
|
const parentValidateOutput = (value, options) => validateUnknown(value, options, true, true);
|
|
6117
6839
|
const parentTo = (value) => route(value).parent[encoderSymbol](value);
|
|
6118
|
-
const parent = createTypeNode("DiscriminatedUnion", null, parentFromUnknown, (value) => getOutputMember(value)?.parent.is(value) ?? false, parentValidateOutput, ok, parentTo, getTypeIssues, { key, members });
|
|
6840
|
+
const parent = createTypeNode("DiscriminatedUnion", null, parentFromUnknown, (value) => getOutputMember(value)?.parent.is(value) ?? false, parentValidateOutput, ok, parentTo, getTypeIssues, { additionalProperties: { key, members } });
|
|
6841
|
+
// Identity members return the input itself, and so does routing.
|
|
6842
|
+
const check = getRuntimeChecks(members) &&
|
|
6843
|
+
((value, options) => {
|
|
6844
|
+
const routeResult = routeUnknown(value);
|
|
6845
|
+
if (!routeResult.ok)
|
|
6846
|
+
return routeResult.error;
|
|
6847
|
+
const member = routeResult.value;
|
|
6848
|
+
const error = member[checkSymbol](value, options);
|
|
6849
|
+
return error === undefined ? undefined : createMemberError(member, error);
|
|
6850
|
+
});
|
|
6119
6851
|
const fromUnknown = (value, options) => validateUnknown(value, options, false, false);
|
|
6120
6852
|
const validateOutput = (value, options) => validateUnknown(value, options, false, true);
|
|
6121
6853
|
const fromParent = (value, options = firstValidationOptions) => {
|
|
@@ -6125,7 +6857,7 @@ export function discriminatedUnion(...keyOrMembers) {
|
|
|
6125
6857
|
};
|
|
6126
6858
|
const from = createFromOperation(fromParent);
|
|
6127
6859
|
const to = (value) => route(value)[encoderSymbol](value);
|
|
6128
|
-
return createTypeNode("DiscriminatedUnion", parent, fromUnknown, (value) => getOutputMember(value)?.is(value) ?? false, validateOutput, from, to, getTypeIssues, { key, members });
|
|
6860
|
+
return createTypeNode("DiscriminatedUnion", parent, fromUnknown, (value) => getOutputMember(value)?.is(value) ?? false, validateOutput, from, to, getTypeIssues, { additionalProperties: { key, members }, check });
|
|
6129
6861
|
}
|
|
6130
6862
|
export function lazy(getType) {
|
|
6131
6863
|
let resolution = { state: "unresolved" };
|
|
@@ -6167,11 +6899,11 @@ export function lazy(getType) {
|
|
|
6167
6899
|
};
|
|
6168
6900
|
const parentFromUnknown = (value, options = firstValidationOptions) => resolve().root.fromUnknown(value, options);
|
|
6169
6901
|
const parentTo = (value) => resolve().root[encoderSymbol](value);
|
|
6170
|
-
const parent = createTypeNode("Lazy", null, parentFromUnknown, (value) => resolve().root.is(value), (value, options) => resolve().root[outputValidationSymbol](value, options), ok, parentTo, (error, mode) => resolve().root[getRuntimeTypeIssuesSymbol](error, mode));
|
|
6902
|
+
const parent = createTypeNode("Lazy", null, parentFromUnknown, (value) => resolve().root.is(value), (value, options) => resolve().root[outputValidationSymbol](value, options), ok, parentTo, (error, mode, path) => resolve().root[getRuntimeTypeIssuesSymbol](error, mode, path));
|
|
6171
6903
|
const fromUnknown = (value, options = firstValidationOptions) => resolve().target.fromUnknown(value, options);
|
|
6172
6904
|
const fromParent = (value, options = firstValidationOptions) => resolve().targetFromInput(value, options);
|
|
6173
6905
|
const from = createFromOperation(fromParent);
|
|
6174
|
-
const type = createTypeNode("Lazy", parent, fromUnknown, (value) => resolve().target.is(value), (value, options) => resolve().target[outputValidationSymbol](value, options), from, (value) => resolve().target[encoderSymbol](value), (error, mode) => resolve().target[getRuntimeTypeIssuesSymbol](error, mode));
|
|
6906
|
+
const type = createTypeNode("Lazy", parent, fromUnknown, (value) => resolve().target.is(value), (value, options) => resolve().target[outputValidationSymbol](value, options), from, (value) => resolve().target[encoderSymbol](value), (error, mode, path) => resolve().target[getRuntimeTypeIssuesSymbol](error, mode, path));
|
|
6175
6907
|
lazyTypeNodes.add(parent);
|
|
6176
6908
|
lazyTypeNodes.add(type);
|
|
6177
6909
|
return type;
|
|
@@ -6242,10 +6974,17 @@ const validateData = (value, options = firstValidationOptions) => {
|
|
|
6242
6974
|
const kind = getObjectKind(value);
|
|
6243
6975
|
if (kind === "Array") {
|
|
6244
6976
|
const array = value;
|
|
6245
|
-
|
|
6977
|
+
const keys = Reflect.ownKeys(array);
|
|
6978
|
+
for (let position = 0; position < keys.length; position++) {
|
|
6979
|
+
const key = keys[position];
|
|
6246
6980
|
if (key === "length")
|
|
6247
6981
|
continue;
|
|
6248
6982
|
if (typeof key === "string") {
|
|
6983
|
+
// Ordinary arrays list their indexes first, in ascending order.
|
|
6984
|
+
// Comparing the key first reads `length` only for an index key.
|
|
6985
|
+
if (key === globalThis.String(position) && position < array.length) {
|
|
6986
|
+
continue;
|
|
6987
|
+
}
|
|
6249
6988
|
const index = globalThis.Number(key) >>> 0;
|
|
6250
6989
|
if (index < array.length && globalThis.String(index) === key) {
|
|
6251
6990
|
continue;
|
|
@@ -6280,7 +7019,7 @@ const validateData = (value, options = firstValidationOptions) => {
|
|
|
6280
7019
|
}
|
|
6281
7020
|
}
|
|
6282
7021
|
else if (kind === "Object") {
|
|
6283
|
-
for (const key of
|
|
7022
|
+
for (const key of getOwnKeys(value)) {
|
|
6284
7023
|
const childPath = dataChildPath(path, key);
|
|
6285
7024
|
if (typeof key === "symbol") {
|
|
6286
7025
|
if (addIssue({
|
|
@@ -6394,7 +7133,7 @@ const validateData = (value, options = firstValidationOptions) => {
|
|
|
6394
7133
|
},
|
|
6395
7134
|
});
|
|
6396
7135
|
};
|
|
6397
|
-
const getDataRuntimeTypeIssues = (error, mode) => {
|
|
7136
|
+
const getDataRuntimeTypeIssues = (error, mode, path) => {
|
|
6398
7137
|
const dataError = error;
|
|
6399
7138
|
const issues = mode === "first"
|
|
6400
7139
|
? [dataError.reason.issues[0]]
|
|
@@ -6407,7 +7146,8 @@ const getDataRuntimeTypeIssues = (error, mode) => {
|
|
|
6407
7146
|
type: "Data",
|
|
6408
7147
|
reason: { kind: "Issues", issues: [issue] },
|
|
6409
7148
|
},
|
|
6410
|
-
path
|
|
7149
|
+
// A root issue shares the path array of its error.
|
|
7150
|
+
path: path.length === 0 ? issue.path : [...path, ...issue.path],
|
|
6411
7151
|
formatError: ((error) => {
|
|
6412
7152
|
const issue = error.reason.issues[0];
|
|
6413
7153
|
switch (issue.kind) {
|
|
@@ -6451,7 +7191,12 @@ const getDataRuntimeTypeIssues = (error, mode) => {
|
|
|
6451
7191
|
*
|
|
6452
7192
|
* @group Base
|
|
6453
7193
|
*/
|
|
6454
|
-
export const Data = /*#__PURE__*/ createTypeNode("Data", null, validateData, (value) => validateData(value).ok, validateData, ok, identity, getDataRuntimeTypeIssues
|
|
7194
|
+
export const Data = /*#__PURE__*/ createTypeNode("Data", null, validateData, (value) => validateData(value).ok, validateData, ok, identity, getDataRuntimeTypeIssues, {
|
|
7195
|
+
check: (value, options) => {
|
|
7196
|
+
const result = validateData(value, options);
|
|
7197
|
+
return result.ok ? undefined : result.error;
|
|
7198
|
+
},
|
|
7199
|
+
});
|
|
6455
7200
|
const emptyJsonValuePath =
|
|
6456
7201
|
/*#__PURE__*/ globalThis.Object.freeze([]);
|
|
6457
7202
|
const jsonValuePathToArray = (path) => {
|
|
@@ -6534,10 +7279,17 @@ const validateJsonValue = (value, options = firstValidationOptions) => {
|
|
|
6534
7279
|
}
|
|
6535
7280
|
const children = [];
|
|
6536
7281
|
if (isArray) {
|
|
6537
|
-
|
|
7282
|
+
const keys = Reflect.ownKeys(value);
|
|
7283
|
+
for (let position = 0; position < keys.length; position++) {
|
|
7284
|
+
const key = keys[position];
|
|
6538
7285
|
if (key === "length")
|
|
6539
7286
|
continue;
|
|
6540
7287
|
if (typeof key === "string") {
|
|
7288
|
+
// Ordinary arrays list their indexes first, in ascending order.
|
|
7289
|
+
// Comparing the key first reads `length` only for an index key.
|
|
7290
|
+
if (key === globalThis.String(position) && position < value.length) {
|
|
7291
|
+
continue;
|
|
7292
|
+
}
|
|
6541
7293
|
const index = globalThis.Number(key) >>> 0;
|
|
6542
7294
|
if (index < value.length && globalThis.String(index) === key) {
|
|
6543
7295
|
continue;
|
|
@@ -6581,7 +7333,7 @@ const validateJsonValue = (value, options = firstValidationOptions) => {
|
|
|
6581
7333
|
}
|
|
6582
7334
|
}
|
|
6583
7335
|
else {
|
|
6584
|
-
for (const key of
|
|
7336
|
+
for (const key of getOwnKeys(value)) {
|
|
6585
7337
|
const childPath = jsonValueChildPath(path, key);
|
|
6586
7338
|
if (typeof key === "symbol") {
|
|
6587
7339
|
if (addIssue({
|
|
@@ -6636,7 +7388,7 @@ const validateJsonValue = (value, options = firstValidationOptions) => {
|
|
|
6636
7388
|
},
|
|
6637
7389
|
});
|
|
6638
7390
|
};
|
|
6639
|
-
const getJsonValueRuntimeTypeIssues = (error, mode) => {
|
|
7391
|
+
const getJsonValueRuntimeTypeIssues = (error, mode, path) => {
|
|
6640
7392
|
const jsonValueError = error;
|
|
6641
7393
|
const issues = mode === "first"
|
|
6642
7394
|
? [jsonValueError.reason.issues[0]]
|
|
@@ -6649,7 +7401,7 @@ const getJsonValueRuntimeTypeIssues = (error, mode) => {
|
|
|
6649
7401
|
type: "JsonValue",
|
|
6650
7402
|
reason: { kind: "Issues", issues: [issue] },
|
|
6651
7403
|
},
|
|
6652
|
-
path: issue.path,
|
|
7404
|
+
path: path.length === 0 ? issue.path : [...path, ...issue.path],
|
|
6653
7405
|
formatError: ((error) => {
|
|
6654
7406
|
const issue = error.reason.issues[0];
|
|
6655
7407
|
switch (issue.kind) {
|
|
@@ -6766,7 +7518,12 @@ const stringifyJsonValue = (value) => {
|
|
|
6766
7518
|
* @group JSON
|
|
6767
7519
|
*/
|
|
6768
7520
|
export const JsonValue =
|
|
6769
|
-
/*#__PURE__*/ createTypeNode("JsonValue", null, validateJsonValue, (value) => validateJsonValue(value).ok, validateJsonValue, ok, identity, getJsonValueRuntimeTypeIssues
|
|
7521
|
+
/*#__PURE__*/ createTypeNode("JsonValue", null, validateJsonValue, (value) => validateJsonValue(value).ok, validateJsonValue, ok, identity, getJsonValueRuntimeTypeIssues, {
|
|
7522
|
+
check: (value, options) => {
|
|
7523
|
+
const result = validateJsonValue(value, options);
|
|
7524
|
+
return result.ok ? undefined : result.error;
|
|
7525
|
+
},
|
|
7526
|
+
});
|
|
6770
7527
|
/**
|
|
6771
7528
|
* Exact top-level JSON array {@link Type}.
|
|
6772
7529
|
*
|
|
@@ -6902,9 +7659,9 @@ export const JsonValueFromJson = /*#__PURE__*/ transform("JsonValueFromJson", Js
|
|
|
6902
7659
|
*/
|
|
6903
7660
|
export const json = (type, name, ..._validation) => {
|
|
6904
7661
|
const runtimeType = type;
|
|
6905
|
-
const getTypeIssues = (error, mode) => error.type === name
|
|
6906
|
-
? runtimeType[getRuntimeTypeIssuesSymbol](error.error, mode)
|
|
6907
|
-
: Json[getRuntimeTypeIssuesSymbol](error, mode);
|
|
7662
|
+
const getTypeIssues = (error, mode, path) => error.type === name
|
|
7663
|
+
? runtimeType[getRuntimeTypeIssuesSymbol](error.error, mode, path)
|
|
7664
|
+
: Json[getRuntimeTypeIssuesSymbol](error, mode, path);
|
|
6908
7665
|
const jsonToTypeOutput = (value, options = firstValidationOptions) => runtimeType.fromUnknown(parseJson(value), options);
|
|
6909
7666
|
const jsonStringToBrandedJson = (value, options) => {
|
|
6910
7667
|
const jsonValueResult = jsonToJsonValueResult(value);
|