@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.
Files changed (76) hide show
  1. package/AST.d.ts +29 -12
  2. package/Guard.d.ts +9 -0
  3. package/ParseError.d.ts +40 -3
  4. package/ParseFailure.d.ts +18 -0
  5. package/ParseResult.d.ts +2 -1
  6. package/Parser/api.d.ts +0 -6
  7. package/Parser/interpreter.d.ts +1 -2
  8. package/Show.d.ts +1 -1
  9. package/_cjs/AST.cjs +115 -65
  10. package/_cjs/AST.cjs.map +1 -1
  11. package/_cjs/Guard.cjs +278 -0
  12. package/_cjs/Guard.cjs.map +1 -0
  13. package/_cjs/ParseError.cjs +77 -3
  14. package/_cjs/ParseError.cjs.map +1 -1
  15. package/_cjs/ParseFailure.cjs +28 -0
  16. package/_cjs/ParseFailure.cjs.map +1 -0
  17. package/_cjs/ParseResult.cjs +4 -3
  18. package/_cjs/ParseResult.cjs.map +1 -1
  19. package/_cjs/Parser/api.cjs +11 -23
  20. package/_cjs/Parser/api.cjs.map +1 -1
  21. package/_cjs/Parser/interpreter.cjs +93 -141
  22. package/_cjs/Parser/interpreter.cjs.map +1 -1
  23. package/_cjs/Schema/api/conc.cjs +1 -1
  24. package/_cjs/Schema/api/conc.cjs.map +1 -1
  25. package/_cjs/Schema/api/hashMap.cjs +1 -1
  26. package/_cjs/Schema/api/hashMap.cjs.map +1 -1
  27. package/_cjs/Schema/api/immutableArray.cjs +1 -1
  28. package/_cjs/Schema/api/immutableArray.cjs.map +1 -1
  29. package/_cjs/Schema/api/list.cjs +1 -1
  30. package/_cjs/Schema/api/list.cjs.map +1 -1
  31. package/_cjs/Schema/api.cjs +27 -28
  32. package/_cjs/Schema/api.cjs.map +1 -1
  33. package/_cjs/Show.cjs +23 -12
  34. package/_cjs/Show.cjs.map +1 -1
  35. package/_mjs/AST.mjs +107 -61
  36. package/_mjs/AST.mjs.map +1 -1
  37. package/_mjs/Guard.mjs +269 -0
  38. package/_mjs/Guard.mjs.map +1 -0
  39. package/_mjs/ParseError.mjs +72 -2
  40. package/_mjs/ParseError.mjs.map +1 -1
  41. package/_mjs/ParseFailure.mjs +20 -0
  42. package/_mjs/ParseFailure.mjs.map +1 -0
  43. package/_mjs/ParseResult.mjs +4 -3
  44. package/_mjs/ParseResult.mjs.map +1 -1
  45. package/_mjs/Parser/api.mjs +10 -21
  46. package/_mjs/Parser/api.mjs.map +1 -1
  47. package/_mjs/Parser/interpreter.mjs +94 -142
  48. package/_mjs/Parser/interpreter.mjs.map +1 -1
  49. package/_mjs/Schema/api/conc.mjs +1 -1
  50. package/_mjs/Schema/api/conc.mjs.map +1 -1
  51. package/_mjs/Schema/api/hashMap.mjs +1 -1
  52. package/_mjs/Schema/api/hashMap.mjs.map +1 -1
  53. package/_mjs/Schema/api/immutableArray.mjs +1 -1
  54. package/_mjs/Schema/api/immutableArray.mjs.map +1 -1
  55. package/_mjs/Schema/api/list.mjs +1 -1
  56. package/_mjs/Schema/api/list.mjs.map +1 -1
  57. package/_mjs/Schema/api.mjs +27 -28
  58. package/_mjs/Schema/api.mjs.map +1 -1
  59. package/_mjs/Show.mjs +23 -12
  60. package/_mjs/Show.mjs.map +1 -1
  61. package/_src/AST.ts +96 -47
  62. package/_src/Guard.ts +268 -0
  63. package/_src/ParseError.ts +88 -4
  64. package/_src/ParseFailure.ts +18 -0
  65. package/_src/ParseResult.ts +3 -3
  66. package/_src/Parser/api.ts +8 -21
  67. package/_src/Parser/interpreter.ts +94 -128
  68. package/_src/Schema/api/conc.ts +1 -1
  69. package/_src/Schema/api/hashMap.ts +1 -1
  70. package/_src/Schema/api/immutableArray.ts +1 -1
  71. package/_src/Schema/api/list.ts +1 -1
  72. package/_src/Schema/api.ts +3 -10
  73. package/_src/Show.ts +28 -15
  74. package/_src/global.ts +4 -0
  75. package/global.d.ts +4 -0
  76. package/package.json +3 -3
@@ -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.reverse);
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 parseMaybe(schema.ast.reverse);
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
- Left: (errors) => {
59
- throw new Error(ParseError.format(errors));
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 go = memoize(function go(ast: AST): Parser<any> {
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) => go(e.type));
57
- const rest = ast.rest.map((rest) => rest.map(go));
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 + 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
- } else {
77
- const parser = elements[i]!;
78
- const t = parser(input[i], options);
79
- Either.concrete(t);
80
- if (t.isLeft()) {
81
- const e = ParseError.IndexError(i, t.left);
82
- errors.push(e);
83
- if (allErrors) {
84
- continue;
85
- } else {
86
- return ParseResult.failures(errors);
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) => go(f.type));
152
- const indexSignatures = ast.indexSignatures.map((is) => [go(is.parameter), go(is.type)] as const);
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, go(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 = () => go(ast.getAST());
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
- const from = go(ast.from);
317
- if (ast.isReversed) {
318
- const to = go(ast.from.getTo);
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 from = go(ast.from);
329
- if (ast.isReversed) {
330
- const to = go(ast.to);
331
- return Parser.make((input, options) =>
332
- to(input, options)
333
- .flatMap((a) => ast.encode(a, options))
334
- .flatMap((a) => from(a, options)),
335
- );
336
- }
337
- return Parser.make((input, options) => from(input, options).flatMap((a) => ast.decode(a, options)));
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
- const unknownArray = AST.createTuple(Vector.empty(), Just(Vector(AST.unknownKeyword)), true);
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
  }
@@ -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
  }
@@ -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
  }
@@ -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
- (a: A) => (validation.validate(a) ? ParseResult.succeed(a) : ParseResult.fail(ParseError.TypeError(ast, a))),
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, false));
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 = go(self.ast);
24
+ const ev = goMemo(self.ast);
12
25
  return ev.run;
13
26
  }
14
27
 
15
- const go = memoize(function go(ast: AST): Eval<string> {
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)(go)
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.map((element) => element.type).traverse(Eval.Applicative)(go));
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)(go),
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) => go(ps.type)));
109
+ const propertySignatures = Δ(ast.propertySignatures.traverse(Eval.Applicative)((ps) => goMemo(ps.type)));
97
110
  const indexSignatures = Δ(
98
- ast.indexSignatures.traverse(Eval.Applicative)((is) => go(is.parameter).zip(go(is.type))),
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)(go)
139
+ .traverse(Eval.Applicative)(goMemo)
127
140
  .map((ts) => ts.join(" | "));
128
141
  case ASTTag.Lazy: {
129
- const f = () => go(ast.getAST());
130
- const get = memoize<typeof f, Eval<string>>(f);
131
- return Eval.defer(() => get(f));
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
- () => go(ast.from).map((from) => `Refined<${from}>`),
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 go(ast.to);
156
+ return goMemo(ast.to);
144
157
  case ASTTag.Validation: {
145
- return go(ast.from).map((from) => {
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
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@fncts/schema",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "dependencies": {
5
- "@fncts/base": "0.0.33",
6
- "@fncts/typelevel": "0.0.16"
5
+ "@fncts/base": "0.0.35",
6
+ "@fncts/typelevel": "0.0.18"
7
7
  },
8
8
  "exports": {
9
9
  "./*": {