@fncts/schema 0.0.13 → 0.0.15
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/AST.d.ts +29 -12
- package/Guard.d.ts +9 -0
- package/ParseError.d.ts +40 -3
- package/ParseFailure.d.ts +18 -0
- package/ParseResult.d.ts +2 -1
- package/Parser/api.d.ts +0 -6
- package/Parser/interpreter.d.ts +1 -2
- package/Show.d.ts +1 -1
- package/_cjs/AST.cjs +115 -65
- package/_cjs/AST.cjs.map +1 -1
- package/_cjs/Guard.cjs +278 -0
- package/_cjs/Guard.cjs.map +1 -0
- package/_cjs/ParseError.cjs +77 -3
- package/_cjs/ParseError.cjs.map +1 -1
- package/_cjs/ParseFailure.cjs +28 -0
- package/_cjs/ParseFailure.cjs.map +1 -0
- package/_cjs/ParseResult.cjs +4 -3
- package/_cjs/ParseResult.cjs.map +1 -1
- package/_cjs/Parser/api.cjs +11 -23
- package/_cjs/Parser/api.cjs.map +1 -1
- package/_cjs/Parser/interpreter.cjs +93 -141
- package/_cjs/Parser/interpreter.cjs.map +1 -1
- package/_cjs/Schema/api/conc.cjs +1 -1
- package/_cjs/Schema/api/conc.cjs.map +1 -1
- package/_cjs/Schema/api/hashMap.cjs +1 -1
- package/_cjs/Schema/api/hashMap.cjs.map +1 -1
- package/_cjs/Schema/api/immutableArray.cjs +1 -1
- package/_cjs/Schema/api/immutableArray.cjs.map +1 -1
- package/_cjs/Schema/api/list.cjs +1 -1
- package/_cjs/Schema/api/list.cjs.map +1 -1
- package/_cjs/Schema/api.cjs +27 -28
- package/_cjs/Schema/api.cjs.map +1 -1
- package/_cjs/Show.cjs +23 -12
- package/_cjs/Show.cjs.map +1 -1
- package/_mjs/AST.mjs +107 -61
- package/_mjs/AST.mjs.map +1 -1
- package/_mjs/Guard.mjs +269 -0
- package/_mjs/Guard.mjs.map +1 -0
- package/_mjs/ParseError.mjs +72 -2
- package/_mjs/ParseError.mjs.map +1 -1
- package/_mjs/ParseFailure.mjs +20 -0
- package/_mjs/ParseFailure.mjs.map +1 -0
- package/_mjs/ParseResult.mjs +4 -3
- package/_mjs/ParseResult.mjs.map +1 -1
- package/_mjs/Parser/api.mjs +10 -21
- package/_mjs/Parser/api.mjs.map +1 -1
- package/_mjs/Parser/interpreter.mjs +94 -142
- package/_mjs/Parser/interpreter.mjs.map +1 -1
- package/_mjs/Schema/api/conc.mjs +1 -1
- package/_mjs/Schema/api/conc.mjs.map +1 -1
- package/_mjs/Schema/api/hashMap.mjs +1 -1
- package/_mjs/Schema/api/hashMap.mjs.map +1 -1
- package/_mjs/Schema/api/immutableArray.mjs +1 -1
- package/_mjs/Schema/api/immutableArray.mjs.map +1 -1
- package/_mjs/Schema/api/list.mjs +1 -1
- package/_mjs/Schema/api/list.mjs.map +1 -1
- package/_mjs/Schema/api.mjs +27 -28
- package/_mjs/Schema/api.mjs.map +1 -1
- package/_mjs/Show.mjs +23 -12
- package/_mjs/Show.mjs.map +1 -1
- package/_src/AST.ts +96 -47
- package/_src/Guard.ts +268 -0
- package/_src/ParseError.ts +88 -4
- package/_src/ParseFailure.ts +18 -0
- package/_src/ParseResult.ts +3 -3
- package/_src/Parser/api.ts +8 -21
- package/_src/Parser/interpreter.ts +94 -128
- package/_src/Schema/api/conc.ts +1 -1
- package/_src/Schema/api/hashMap.ts +1 -1
- package/_src/Schema/api/immutableArray.ts +1 -1
- package/_src/Schema/api/list.ts +1 -1
- package/_src/Schema/api.ts +3 -10
- package/_src/Show.ts +28 -15
- package/_src/global.ts +4 -0
- package/global.d.ts +4 -0
- package/package.json +3 -3
package/_src/Parser/api.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { parserFor } from "./interpreter.js";
|
|
|
7
7
|
* @tsplus getter fncts.schema.Parser decode
|
|
8
8
|
*/
|
|
9
9
|
export function decode<A>(schema: Schema<A>): Parser<A> {
|
|
10
|
-
return parserFor(schema.ast);
|
|
10
|
+
return parserFor(schema.ast, true);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -23,7 +23,7 @@ export function decodeMaybe<A>(schema: Schema<A>): <A>(input: A, options?: Parse
|
|
|
23
23
|
* @tsplus getter fncts.schema.Parser encode
|
|
24
24
|
*/
|
|
25
25
|
export function encode<A>(schema: Schema<A>): <A>(input: A, options?: ParseOptions) => ParseResult<unknown> {
|
|
26
|
-
return parserFor(schema.ast
|
|
26
|
+
return parserFor(schema.ast, false);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -31,35 +31,22 @@ export function encode<A>(schema: Schema<A>): <A>(input: A, options?: ParseOptio
|
|
|
31
31
|
* @tsplus getter fncts.schema.Parser encodeMaybe
|
|
32
32
|
*/
|
|
33
33
|
export function encodeMaybe<A>(schema: Schema<A>): <A>(input: A, options?: ParseOptions) => Maybe<unknown> {
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @tsplus getter fncts.schema.Schema is
|
|
39
|
-
* @tsplus getter fncts.schema.Parser is
|
|
40
|
-
*/
|
|
41
|
-
export function is<A>(schema: Schema<A>) {
|
|
42
|
-
return (input: unknown, options?: ParseOptions): input is A => {
|
|
43
|
-
return parserFor(schema.ast)(input, options).isRight();
|
|
44
|
-
};
|
|
34
|
+
return (input, options) => encode(schema)(input, options).toMaybe;
|
|
45
35
|
}
|
|
46
36
|
|
|
47
37
|
function parseMaybe(ast: AST) {
|
|
48
|
-
const parse = parserFor(ast);
|
|
38
|
+
const parse = parserFor(ast, true);
|
|
49
39
|
return (input: unknown, options?: ParseOptions): Maybe<any> => {
|
|
50
40
|
return parse(input, options).toMaybe;
|
|
51
41
|
};
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
function parseOrThrow(ast: AST) {
|
|
55
|
-
const parser = parserFor(ast);
|
|
45
|
+
const parser = parserFor(ast, true);
|
|
56
46
|
return (input: unknown, options?: ParseOptions) => {
|
|
57
|
-
return parser(input, options).match({
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
Right: Function.identity,
|
|
62
|
-
});
|
|
47
|
+
return parser(input, options).match((failure) => {
|
|
48
|
+
throw new Error(ParseError.format(failure.errors));
|
|
49
|
+
}, Function.identity);
|
|
63
50
|
};
|
|
64
51
|
}
|
|
65
52
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { Literal } from "../AST.js";
|
|
2
|
-
import type { Hook } from "../ASTAnnotation.js";
|
|
3
1
|
import type { MutableVector } from "@fncts/base/collection/immutable/Vector";
|
|
4
2
|
import type { Validation } from "@fncts/base/data/Branded";
|
|
5
3
|
|
|
4
|
+
import { globalValue } from "@fncts/base/data/Global";
|
|
6
5
|
import {
|
|
7
6
|
isBigInt,
|
|
8
7
|
isBoolean,
|
|
@@ -14,10 +13,32 @@ import {
|
|
|
14
13
|
isUndefined,
|
|
15
14
|
} from "@fncts/base/util/predicates";
|
|
16
15
|
|
|
17
|
-
import { ASTTag, concrete } from "../AST.js";
|
|
16
|
+
import { ASTTag, concrete, getSearchTree } from "../AST.js";
|
|
17
|
+
import { RefinementError, TransformationError } from "../ParseError.js";
|
|
18
18
|
import { getKeysForIndexSignature, getTemplateLiteralRegex, memoize, ownKeys } from "../utils.js";
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const decodeMemoMap = globalValue(
|
|
21
|
+
Symbol.for("fncts.schema.Parser.decodeMemoMap"),
|
|
22
|
+
() => new WeakMap<AST, Parser<any>>(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const encodeMemoMap = globalValue(
|
|
26
|
+
Symbol.for("fncts.schema.Parser.encodeMemoMap"),
|
|
27
|
+
() => new WeakMap<AST, Parser<any>>(),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function goMemo(ast: AST, isDecoding: boolean): Parser<any> {
|
|
31
|
+
const memoMap = isDecoding ? decodeMemoMap : encodeMemoMap;
|
|
32
|
+
const memo = memoMap.get(ast);
|
|
33
|
+
if (memo) {
|
|
34
|
+
return memo;
|
|
35
|
+
}
|
|
36
|
+
const parser = go(ast, isDecoding);
|
|
37
|
+
memoMap.set(ast, parser);
|
|
38
|
+
return parser;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function go(ast: AST, isDecoding: boolean): Parser<any> {
|
|
21
42
|
concrete(ast);
|
|
22
43
|
switch (ast._tag) {
|
|
23
44
|
case ASTTag.Declaration:
|
|
@@ -53,18 +74,18 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
53
74
|
return Parser.fromRefinement(ast, (u): u is any => isString(u) && regex.test(u));
|
|
54
75
|
}
|
|
55
76
|
case ASTTag.Tuple: {
|
|
56
|
-
const elements = ast.elements.map((e) =>
|
|
57
|
-
const rest = ast.rest.map((rest) => rest.map(
|
|
77
|
+
const elements = ast.elements.map((e) => goMemo(e.type, isDecoding));
|
|
78
|
+
const rest = ast.rest.map((rest) => rest.map((ast) => goMemo(ast, isDecoding)));
|
|
58
79
|
return Parser.make((input, options) => {
|
|
59
80
|
if (!Array.isArray(input)) {
|
|
60
|
-
return ParseResult.fail(ParseError.TypeError(unknownArray, input));
|
|
81
|
+
return ParseResult.fail(ParseError.TypeError(AST.unknownArray, input));
|
|
61
82
|
}
|
|
62
83
|
const output: Array<any> = [];
|
|
63
84
|
const errors: MutableVector<ParseError> = Vector.emptyPushable();
|
|
64
85
|
const allErrors = options?.allErrors;
|
|
65
86
|
let i = 0;
|
|
66
87
|
for (; i < elements.length; i++) {
|
|
67
|
-
if (input.length < i +
|
|
88
|
+
if (input.length < i + 1) {
|
|
68
89
|
if (!ast.elements[i]!.isOptional) {
|
|
69
90
|
const e = ParseError.IndexError(i, Vector(ParseError.MissingError));
|
|
70
91
|
errors.push(e);
|
|
@@ -73,23 +94,24 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
73
94
|
} else {
|
|
74
95
|
return ParseResult.failures(errors);
|
|
75
96
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
const parser = elements[i]!;
|
|
100
|
+
const t = parser(input[i], options);
|
|
101
|
+
Either.concrete(t);
|
|
102
|
+
if (t.isLeft()) {
|
|
103
|
+
const e = ParseError.IndexError(i, t.left.errors);
|
|
104
|
+
errors.push(e);
|
|
105
|
+
if (allErrors) {
|
|
106
|
+
continue;
|
|
107
|
+
} else {
|
|
108
|
+
return ParseResult.failures(errors);
|
|
88
109
|
}
|
|
89
|
-
output.push(t.right);
|
|
90
110
|
}
|
|
111
|
+
output.push(t.right);
|
|
91
112
|
}
|
|
92
113
|
}
|
|
114
|
+
|
|
93
115
|
if (rest.isJust()) {
|
|
94
116
|
const head = rest.value.unsafeHead!;
|
|
95
117
|
const tail = rest.value.tail;
|
|
@@ -97,7 +119,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
97
119
|
const t = head(input[i], options);
|
|
98
120
|
Either.concrete(t);
|
|
99
121
|
if (t.isLeft()) {
|
|
100
|
-
const e = ParseError.IndexError(i, t.left);
|
|
122
|
+
const e = ParseError.IndexError(i, t.left.errors);
|
|
101
123
|
errors.push(e);
|
|
102
124
|
if (allErrors) {
|
|
103
125
|
continue;
|
|
@@ -116,7 +138,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
116
138
|
const t = tail[j]!(input[i], options);
|
|
117
139
|
Either.concrete(t);
|
|
118
140
|
if (t.isLeft()) {
|
|
119
|
-
const e = ParseError.IndexError(i, t.left);
|
|
141
|
+
const e = ParseError.IndexError(i, t.left.errors);
|
|
120
142
|
errors.push(e);
|
|
121
143
|
if (allErrors) {
|
|
122
144
|
continue;
|
|
@@ -148,11 +170,13 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
148
170
|
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) {
|
|
149
171
|
return Parser.fromRefinement(ast, (u): u is Exclude<typeof u, null> => u !== null);
|
|
150
172
|
}
|
|
151
|
-
const propertySignatureTypes = ast.propertySignatures.map((f) =>
|
|
152
|
-
const indexSignatures = ast.indexSignatures.map(
|
|
173
|
+
const propertySignatureTypes = ast.propertySignatures.map((f) => goMemo(f.type, isDecoding));
|
|
174
|
+
const indexSignatures = ast.indexSignatures.map(
|
|
175
|
+
(is) => [goMemo(is.parameter, isDecoding), goMemo(is.type, isDecoding)] as const,
|
|
176
|
+
);
|
|
153
177
|
return Parser.make((input, options) => {
|
|
154
178
|
if (!isRecord(input)) {
|
|
155
|
-
return ParseResult.fail(ParseError.TypeError(unknownRecord, input));
|
|
179
|
+
return ParseResult.fail(ParseError.TypeError(AST.unknownRecord, input));
|
|
156
180
|
}
|
|
157
181
|
const output: any = {};
|
|
158
182
|
const expectedKeys: any = {};
|
|
@@ -178,7 +202,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
178
202
|
const t: ParseResult<unknown> = parser(input[name], options);
|
|
179
203
|
Either.concrete(t);
|
|
180
204
|
if (t.isLeft()) {
|
|
181
|
-
const e = ParseError.KeyError(AST.createKey(name), name, t.left);
|
|
205
|
+
const e = ParseError.KeyError(AST.createKey(name), name, t.left.errors);
|
|
182
206
|
errors.push(e);
|
|
183
207
|
if (allErrors) {
|
|
184
208
|
continue;
|
|
@@ -202,7 +226,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
202
226
|
let t = parameter(key, options);
|
|
203
227
|
Either.concrete(t);
|
|
204
228
|
if (t.isLeft()) {
|
|
205
|
-
const e = ParseError.KeyError(AST.createKey(key), key, t.left);
|
|
229
|
+
const e = ParseError.KeyError(AST.createKey(key), key, t.left.errors);
|
|
206
230
|
errors.push(e);
|
|
207
231
|
if (allErrors) {
|
|
208
232
|
continue;
|
|
@@ -214,7 +238,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
214
238
|
t = type(input[key], options);
|
|
215
239
|
Either.concrete(t);
|
|
216
240
|
if (t.isLeft()) {
|
|
217
|
-
const e = ParseError.KeyError(AST.createKey(key), key, t.left);
|
|
241
|
+
const e = ParseError.KeyError(AST.createKey(key), key, t.left.errors);
|
|
218
242
|
errors.push(e);
|
|
219
243
|
if (allErrors) {
|
|
220
244
|
continue;
|
|
@@ -247,13 +271,13 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
247
271
|
});
|
|
248
272
|
}
|
|
249
273
|
case ASTTag.Union: {
|
|
250
|
-
const searchTree = getSearchTree(ast.types);
|
|
274
|
+
const searchTree = getSearchTree(ast.types, isDecoding);
|
|
251
275
|
const ownKeys = Reflect.ownKeys(searchTree.keys);
|
|
252
276
|
const len = ownKeys.length;
|
|
253
277
|
const otherwise = searchTree.otherwise;
|
|
254
278
|
const map = new Map<any, Parser<any>>();
|
|
255
279
|
ast.types.forEach((ast) => {
|
|
256
|
-
map.set(ast,
|
|
280
|
+
map.set(ast, goMemo(ast, isDecoding));
|
|
257
281
|
});
|
|
258
282
|
return Parser.make((input, options) => {
|
|
259
283
|
const errors = Vector.emptyPushable<ParseError>();
|
|
@@ -272,7 +296,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
272
296
|
if (t.isRight()) {
|
|
273
297
|
return t;
|
|
274
298
|
} else {
|
|
275
|
-
errors.push(ParseError.UnionMemberError(t.left));
|
|
299
|
+
errors.push(ParseError.UnionMemberError(t.left.errors));
|
|
276
300
|
}
|
|
277
301
|
}
|
|
278
302
|
} else {
|
|
@@ -289,7 +313,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
289
313
|
}
|
|
290
314
|
}
|
|
291
315
|
} else {
|
|
292
|
-
errors.push(ParseError.TypeError(unknownRecord, input));
|
|
316
|
+
errors.push(ParseError.TypeError(AST.unknownRecord, input));
|
|
293
317
|
}
|
|
294
318
|
}
|
|
295
319
|
for (let i = 0; i < otherwise.length; i++) {
|
|
@@ -298,7 +322,7 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
298
322
|
if (t.isRight()) {
|
|
299
323
|
return t;
|
|
300
324
|
} else {
|
|
301
|
-
errors.push(ParseError.UnionMemberError(t.left));
|
|
325
|
+
errors.push(ParseError.UnionMemberError(t.left.errors));
|
|
302
326
|
}
|
|
303
327
|
}
|
|
304
328
|
|
|
@@ -308,33 +332,52 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
308
332
|
});
|
|
309
333
|
}
|
|
310
334
|
case ASTTag.Lazy: {
|
|
311
|
-
const f = () =>
|
|
335
|
+
const f = () => goMemo(ast.getAST(), isDecoding);
|
|
312
336
|
const get = memoize<void, Parser<any>>(f);
|
|
313
337
|
return Parser.make((a, options) => get()(a, options));
|
|
314
338
|
}
|
|
315
339
|
case ASTTag.Refinement: {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
340
|
+
if (isDecoding) {
|
|
341
|
+
const from = goMemo(ast.from, isDecoding);
|
|
342
|
+
return Parser.make((input, options) =>
|
|
343
|
+
from(input, options)
|
|
344
|
+
.mapLeft((failure) => ParseFailure(Vector(RefinementError(ast, input, "From", failure.errors))))
|
|
345
|
+
.flatMap((a) =>
|
|
346
|
+
ast
|
|
347
|
+
.decode(a, options)
|
|
348
|
+
.mapLeft((failure) => ParseFailure(Vector(RefinementError(ast, input, "Predicate", failure.errors)))),
|
|
349
|
+
),
|
|
350
|
+
);
|
|
351
|
+
} else {
|
|
352
|
+
const from = goMemo(ast.from, true);
|
|
353
|
+
const to = goMemo(ast.from.getTo, false);
|
|
319
354
|
return Parser.make((input, options) =>
|
|
320
355
|
to(input, options)
|
|
321
356
|
.flatMap((a) => ast.decode(a, options))
|
|
322
357
|
.flatMap((a) => from(a, options)),
|
|
323
358
|
);
|
|
324
359
|
}
|
|
325
|
-
return Parser.make((input, options) => from(input, options).flatMap((a) => ast.decode(a, options)));
|
|
326
360
|
}
|
|
327
361
|
case ASTTag.Transform: {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
362
|
+
const transformation = isDecoding ? ast.decode : ast.encode;
|
|
363
|
+
const from = isDecoding ? goMemo(ast.from, true) : goMemo(ast.to, false);
|
|
364
|
+
const to = isDecoding ? goMemo(ast.to, true) : goMemo(ast.from, false);
|
|
365
|
+
return Parser.make((input, options) =>
|
|
366
|
+
from(input, options)
|
|
367
|
+
.mapLeft((failure) =>
|
|
368
|
+
ParseFailure(Vector(TransformationError(ast, input, isDecoding ? "Encoded" : "Type", failure.errors))),
|
|
369
|
+
)
|
|
370
|
+
.flatMap((a) =>
|
|
371
|
+
transformation(a, options).mapLeft((failure) =>
|
|
372
|
+
ParseFailure(Vector(TransformationError(ast, input, "Transformation", failure.errors))),
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
.flatMap((a) =>
|
|
376
|
+
to(a, options).mapLeft((failure) =>
|
|
377
|
+
ParseFailure(Vector(TransformationError(ast, input, isDecoding ? "Type" : "Encoded", failure.errors))),
|
|
378
|
+
),
|
|
379
|
+
),
|
|
380
|
+
);
|
|
338
381
|
}
|
|
339
382
|
case ASTTag.Validation: {
|
|
340
383
|
return Parser.make((u, options) => {
|
|
@@ -358,85 +401,8 @@ const go = memoize(function go(ast: AST): Parser<any> {
|
|
|
358
401
|
});
|
|
359
402
|
}
|
|
360
403
|
}
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
export function parserFor(ast: AST): Parser<any> {
|
|
364
|
-
return go(ast);
|
|
365
404
|
}
|
|
366
405
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const unknownRecord = AST.createTypeLiteral(
|
|
370
|
-
Vector.empty(),
|
|
371
|
-
Vector(
|
|
372
|
-
AST.createIndexSignature(AST.stringKeyword, AST.unknownKeyword, true),
|
|
373
|
-
AST.createIndexSignature(AST.symbolKeyword, AST.unknownKeyword, true),
|
|
374
|
-
),
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
function getLiterals(ast: AST): ReadonlyArray<[PropertyKey, Literal]> {
|
|
378
|
-
AST.concrete(ast);
|
|
379
|
-
switch (ast._tag) {
|
|
380
|
-
case ASTTag.Declaration:
|
|
381
|
-
return getLiterals(ast.type);
|
|
382
|
-
case ASTTag.TypeLiteral: {
|
|
383
|
-
const out: Array<[PropertyKey, Literal]> = [];
|
|
384
|
-
for (let i = 0; i < ast.propertySignatures.length; i++) {
|
|
385
|
-
const propertySignature = ast.propertySignatures[i]!;
|
|
386
|
-
if (propertySignature.type.isLiteral() && !propertySignature.isOptional) {
|
|
387
|
-
out.push([propertySignature.name, propertySignature.type]);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return out;
|
|
391
|
-
}
|
|
392
|
-
case ASTTag.Refinement:
|
|
393
|
-
return getLiterals(ast.from);
|
|
394
|
-
case ASTTag.Transform:
|
|
395
|
-
return ast.isReversed ? getLiterals(ast.to) : getLiterals(ast.from.getFrom);
|
|
396
|
-
}
|
|
397
|
-
return [];
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function getSearchTree(members: Vector<AST>): {
|
|
401
|
-
keys: {
|
|
402
|
-
readonly [key: PropertyKey]: {
|
|
403
|
-
buckets: { [literal: string]: ReadonlyArray<AST> };
|
|
404
|
-
ast: AST;
|
|
405
|
-
};
|
|
406
|
-
};
|
|
407
|
-
otherwise: ReadonlyArray<AST>;
|
|
408
|
-
} {
|
|
409
|
-
const keys: {
|
|
410
|
-
[key: PropertyKey]: {
|
|
411
|
-
buckets: { [literal: string]: Array<AST> };
|
|
412
|
-
ast: AST;
|
|
413
|
-
};
|
|
414
|
-
} = {};
|
|
415
|
-
const otherwise: Array<AST> = [];
|
|
416
|
-
for (let i = 0; i < members.length; i++) {
|
|
417
|
-
const member = members[i]!;
|
|
418
|
-
const tags = getLiterals(member);
|
|
419
|
-
if (tags.length > 0) {
|
|
420
|
-
for (let j = 0; j < tags.length; j++) {
|
|
421
|
-
const [key, literal] = tags[j]!;
|
|
422
|
-
const hash = String(literal.literal);
|
|
423
|
-
keys[key]! ||= { buckets: {}, ast: AST.neverKeyword };
|
|
424
|
-
const buckets = keys[key]!.buckets;
|
|
425
|
-
if (Object.prototype.hasOwnProperty.call(buckets, hash)) {
|
|
426
|
-
if (j < tags.length - 1) {
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
buckets[hash]!.push(member);
|
|
430
|
-
keys[key]!.ast = AST.createUnion(Vector(keys[key]!.ast, literal));
|
|
431
|
-
} else {
|
|
432
|
-
buckets[hash]! = [member];
|
|
433
|
-
keys[key]!.ast = AST.createUnion(Vector(keys[key]!.ast, literal));
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
} else {
|
|
438
|
-
otherwise.push(member);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return { keys, otherwise };
|
|
406
|
+
export function parserFor(ast: AST, isDecoding: boolean): Parser<any> {
|
|
407
|
+
return goMemo(ast, isDecoding);
|
|
442
408
|
}
|
package/_src/Schema/api/conc.ts
CHANGED
|
@@ -49,7 +49,7 @@ export function concParser<A>(value: Schema<A>): Parser<Conc<A>> {
|
|
|
49
49
|
const t = value.decode(v);
|
|
50
50
|
Either.concrete(t);
|
|
51
51
|
if (t.isLeft()) {
|
|
52
|
-
errors.push(ParseError.IndexError(i, t.left));
|
|
52
|
+
errors.push(ParseError.IndexError(i, t.left.errors));
|
|
53
53
|
if (!allErrors) {
|
|
54
54
|
return ParseResult.failures(errors);
|
|
55
55
|
}
|
|
@@ -78,7 +78,7 @@ function hashMapParser<K, V>(key: Schema<K>, value: Schema<V>): Parser<HashMap<K
|
|
|
78
78
|
const tk = key.decode(k, options);
|
|
79
79
|
Either.concrete(tk);
|
|
80
80
|
if (tk.isLeft()) {
|
|
81
|
-
errors.push(ParseError.KeyError(key.ast, k, tk.left));
|
|
81
|
+
errors.push(ParseError.KeyError(key.ast, k, tk.left.errors));
|
|
82
82
|
if (!allErrors) {
|
|
83
83
|
return ParseResult.failures(errors);
|
|
84
84
|
}
|
|
@@ -59,7 +59,7 @@ function parser<A>(value: Schema<A>): Parser<ImmutableArray<A>> {
|
|
|
59
59
|
const t = value.decode(v, options);
|
|
60
60
|
Either.concrete(t);
|
|
61
61
|
if (t.isLeft()) {
|
|
62
|
-
errors.push(ParseError.IndexError(index, t.left));
|
|
62
|
+
errors.push(ParseError.IndexError(index, t.left.errors));
|
|
63
63
|
if (allErrors) {
|
|
64
64
|
continue;
|
|
65
65
|
}
|
package/_src/Schema/api/list.ts
CHANGED
|
@@ -54,7 +54,7 @@ function parser<A>(value: Schema<A>): Parser<List<A>> {
|
|
|
54
54
|
const t = value.decode(v, options);
|
|
55
55
|
Either.concrete(t);
|
|
56
56
|
if (t.isLeft()) {
|
|
57
|
-
errors.push(ParseError.IndexError(index, t.left));
|
|
57
|
+
errors.push(ParseError.IndexError(index, t.left.errors));
|
|
58
58
|
if (allErrors) {
|
|
59
59
|
continue;
|
|
60
60
|
}
|
package/_src/Schema/api.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { Literal, ParseOptions, TemplateLiteral, TypeLiteral } from "../AST.js";
|
|
2
2
|
import type { Brand, Validation } from "@fncts/base/data/Branded";
|
|
3
|
-
import type { Check } from "@fncts/typelevel";
|
|
4
|
-
import type { OptionalKeys, RequiredKeys } from "@fncts/typelevel/Object";
|
|
5
3
|
|
|
6
4
|
import { show } from "@fncts/base/data/Showable";
|
|
7
5
|
|
|
@@ -51,11 +49,7 @@ export function filter<A, B extends A>(refinement: Refinement<A, B>): (self: Sch
|
|
|
51
49
|
export function filter<A>(predicate: Predicate<A>): (self: Schema<A>) => Schema<A>;
|
|
52
50
|
export function filter<A>(predicate: Predicate<A>) {
|
|
53
51
|
return (self: Schema<A>): Schema<A> => {
|
|
54
|
-
const ast: AST = AST.createRefinement(
|
|
55
|
-
self.ast,
|
|
56
|
-
(a: A) => (predicate(a) ? ParseResult.succeed(a) : ParseResult.fail(ParseError.TypeError(ast, a))),
|
|
57
|
-
false,
|
|
58
|
-
);
|
|
52
|
+
const ast: AST = AST.createRefinement(self.ast, predicate);
|
|
59
53
|
return Schema.fromAST(ast);
|
|
60
54
|
};
|
|
61
55
|
}
|
|
@@ -67,8 +61,7 @@ export function brand<A, K extends string>(validation: Validation<A, K>) {
|
|
|
67
61
|
return (self: Schema<A>): Schema<A & Brand.Valid<A, K>> => {
|
|
68
62
|
const ast = AST.createRefinement(
|
|
69
63
|
self.ast,
|
|
70
|
-
|
|
71
|
-
false,
|
|
64
|
+
validation.validate,
|
|
72
65
|
self.ast.annotations.annotate(ASTAnnotation.Brand, Vector(validation)),
|
|
73
66
|
);
|
|
74
67
|
return Schema.fromAST(ast);
|
|
@@ -528,7 +521,7 @@ export function transformOrFail<A, B>(
|
|
|
528
521
|
encode: (input: B, options?: ParseOptions) => ParseResult<A>,
|
|
529
522
|
) {
|
|
530
523
|
return (from: Schema<A>): Schema<B> => {
|
|
531
|
-
return Schema.fromAST(AST.createTransform(from.ast, to.ast, decode, encode
|
|
524
|
+
return Schema.fromAST(AST.createTransform(from.ast, to.ast, decode, encode));
|
|
532
525
|
};
|
|
533
526
|
}
|
|
534
527
|
|
package/_src/Show.ts
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import type { TemplateLiteral, TemplateLiteralSpan } from "@fncts/schema/AST";
|
|
2
2
|
|
|
3
|
+
import { globalValue } from "@fncts/base/data/Global";
|
|
3
4
|
import { ASTTag } from "@fncts/schema/AST";
|
|
4
5
|
import { ASTAnnotation } from "@fncts/schema/ASTAnnotation";
|
|
5
6
|
import { memoize } from "@fncts/schema/utils";
|
|
6
7
|
|
|
8
|
+
const showMemoMap = globalValue(Symbol.for("fncts.schema.Guard.showMemoMap"), () => new WeakMap<AST, Eval<string>>());
|
|
9
|
+
|
|
10
|
+
function goMemo(ast: AST): Eval<string> {
|
|
11
|
+
const memo = showMemoMap.get(ast);
|
|
12
|
+
if (memo) {
|
|
13
|
+
return memo;
|
|
14
|
+
}
|
|
15
|
+
const s = go(ast);
|
|
16
|
+
showMemoMap.set(ast, s);
|
|
17
|
+
return s;
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
/**
|
|
8
21
|
* @tsplus getter fncts.schema.Schema show
|
|
9
22
|
*/
|
|
10
23
|
export function show<A>(self: Schema<A>): string {
|
|
11
|
-
const ev =
|
|
24
|
+
const ev = goMemo(self.ast);
|
|
12
25
|
return ev.run;
|
|
13
26
|
}
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
function go(ast: AST): Eval<string> {
|
|
16
29
|
AST.concrete(ast);
|
|
17
30
|
switch (ast._tag) {
|
|
18
31
|
case ASTTag.Declaration: {
|
|
@@ -20,7 +33,7 @@ const go = memoize(function go(ast: AST): Eval<string> {
|
|
|
20
33
|
() => Eval.now("Unknown Type"),
|
|
21
34
|
(id) => {
|
|
22
35
|
return ast.typeParameters
|
|
23
|
-
.traverse(Eval.Applicative)(
|
|
36
|
+
.traverse(Eval.Applicative)(goMemo)
|
|
24
37
|
.map((ts) => {
|
|
25
38
|
if (ts.length <= 0) {
|
|
26
39
|
return id;
|
|
@@ -66,11 +79,11 @@ const go = memoize(function go(ast: AST): Eval<string> {
|
|
|
66
79
|
return Eval.now("`" + formatTemplateLiteral(ast) + "`");
|
|
67
80
|
case ASTTag.Tuple:
|
|
68
81
|
return Do((Δ) => {
|
|
69
|
-
const elements = Δ(ast.elements.
|
|
82
|
+
const elements = Δ(ast.elements.traverse(Eval.Applicative)((element) => goMemo(element.type)));
|
|
70
83
|
const restElements = Δ(
|
|
71
84
|
ast.rest.match(
|
|
72
85
|
() => Eval.now(Vector.empty<string>()),
|
|
73
|
-
(rest) => rest.traverse(Eval.Applicative)(
|
|
86
|
+
(rest) => rest.traverse(Eval.Applicative)(goMemo),
|
|
74
87
|
),
|
|
75
88
|
);
|
|
76
89
|
|
|
@@ -93,9 +106,9 @@ const go = memoize(function go(ast: AST): Eval<string> {
|
|
|
93
106
|
});
|
|
94
107
|
case ASTTag.TypeLiteral:
|
|
95
108
|
return Do((Δ) => {
|
|
96
|
-
const propertySignatures = Δ(ast.propertySignatures.traverse(Eval.Applicative)((ps) =>
|
|
109
|
+
const propertySignatures = Δ(ast.propertySignatures.traverse(Eval.Applicative)((ps) => goMemo(ps.type)));
|
|
97
110
|
const indexSignatures = Δ(
|
|
98
|
-
ast.indexSignatures.traverse(Eval.Applicative)((is) =>
|
|
111
|
+
ast.indexSignatures.traverse(Eval.Applicative)((is) => goMemo(is.parameter).zip(goMemo(is.type))),
|
|
99
112
|
);
|
|
100
113
|
|
|
101
114
|
const required: Array<[PropertyKey, string]> = [];
|
|
@@ -123,26 +136,26 @@ const go = memoize(function go(ast: AST): Eval<string> {
|
|
|
123
136
|
});
|
|
124
137
|
case ASTTag.Union:
|
|
125
138
|
return ast.types
|
|
126
|
-
.traverse(Eval.Applicative)(
|
|
139
|
+
.traverse(Eval.Applicative)(goMemo)
|
|
127
140
|
.map((ts) => ts.join(" | "));
|
|
128
141
|
case ASTTag.Lazy: {
|
|
129
|
-
const f = () =>
|
|
130
|
-
const get = memoize<
|
|
131
|
-
return Eval.defer(() => get(
|
|
142
|
+
const f = () => goMemo(ast.getAST());
|
|
143
|
+
const get = memoize<void, Eval<string>>(f);
|
|
144
|
+
return Eval.defer(() => get());
|
|
132
145
|
}
|
|
133
146
|
case ASTTag.Enum: {
|
|
134
147
|
return Eval.now(ast.enums.map(([name]) => name).join(" | "));
|
|
135
148
|
}
|
|
136
149
|
case ASTTag.Refinement: {
|
|
137
150
|
return ast.annotations.get(ASTAnnotation.Identifier).match(
|
|
138
|
-
() =>
|
|
151
|
+
() => goMemo(ast.from).map((from) => `Refined<${from}>`),
|
|
139
152
|
(id) => Eval.now(id),
|
|
140
153
|
);
|
|
141
154
|
}
|
|
142
155
|
case ASTTag.Transform:
|
|
143
|
-
return
|
|
156
|
+
return goMemo(ast.to);
|
|
144
157
|
case ASTTag.Validation: {
|
|
145
|
-
return
|
|
158
|
+
return goMemo(ast.from).map((from) => {
|
|
146
159
|
const validationNames = ast.validation.map((v) => v.name).join(" & ");
|
|
147
160
|
|
|
148
161
|
if (validationNames.length <= 0) {
|
|
@@ -153,7 +166,7 @@ const go = memoize(function go(ast: AST): Eval<string> {
|
|
|
153
166
|
});
|
|
154
167
|
}
|
|
155
168
|
}
|
|
156
|
-
}
|
|
169
|
+
}
|
|
157
170
|
|
|
158
171
|
function formatTemplateLiteralSpan(span: TemplateLiteralSpan): string {
|
|
159
172
|
switch (span.type._tag) {
|
package/_src/global.ts
CHANGED
|
@@ -39,6 +39,10 @@ import { ASTAnnotationMap } from "@fncts/schema/ASTAnnotationMap";
|
|
|
39
39
|
* @tsplus global
|
|
40
40
|
*/
|
|
41
41
|
import { ParseError } from "@fncts/schema/ParseError";
|
|
42
|
+
/**
|
|
43
|
+
* @tsplus global
|
|
44
|
+
*/
|
|
45
|
+
import { ParseFailure } from "@fncts/schema/ParseFailure";
|
|
42
46
|
/**
|
|
43
47
|
* @tsplus global
|
|
44
48
|
*/
|
package/global.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ import { ASTAnnotationMap } from "@fncts/schema/ASTAnnotationMap";
|
|
|
38
38
|
* @tsplus global
|
|
39
39
|
*/
|
|
40
40
|
import { ParseError } from "@fncts/schema/ParseError";
|
|
41
|
+
/**
|
|
42
|
+
* @tsplus global
|
|
43
|
+
*/
|
|
44
|
+
import { ParseFailure } from "@fncts/schema/ParseFailure";
|
|
41
45
|
/**
|
|
42
46
|
* @tsplus global
|
|
43
47
|
*/
|
package/package.json
CHANGED