@fncts/schema 0.0.5 → 0.0.7

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 (157) hide show
  1. package/AST.d.ts +528 -0
  2. package/ASTAnnotation.d.ts +82 -0
  3. package/ASTAnnotationMap.d.ts +14 -0
  4. package/Gen.d.ts +15 -0
  5. package/InvalidInterpretationError.d.ts +5 -0
  6. package/ParseError.d.ts +107 -0
  7. package/ParseResult.d.ts +24 -0
  8. package/Parser/api.d.ts +42 -0
  9. package/Parser/definition.d.ts +22 -0
  10. package/Parser/interpreter.d.ts +6 -0
  11. package/Parser.d.ts +3 -0
  12. package/Schema/api/conc.d.ts +18 -0
  13. package/Schema/api/either.d.ts +19 -0
  14. package/Schema/api/hashMap.d.ts +21 -0
  15. package/Schema/api/immutableArray.d.ts +23 -0
  16. package/Schema/api/list.d.ts +23 -0
  17. package/Schema/api/maybe.d.ts +21 -0
  18. package/Schema/api.d.ts +243 -0
  19. package/Schema/definition.d.ts +29 -0
  20. package/Schema/derivations.d.ts +70 -0
  21. package/Schema.d.ts +9 -144
  22. package/_cjs/AST.cjs +1172 -0
  23. package/_cjs/AST.cjs.map +1 -0
  24. package/_cjs/ASTAnnotation.cjs +111 -0
  25. package/_cjs/ASTAnnotation.cjs.map +1 -0
  26. package/_cjs/ASTAnnotationMap.cjs +35 -0
  27. package/_cjs/ASTAnnotationMap.cjs.map +1 -0
  28. package/_cjs/Gen.cjs +182 -0
  29. package/_cjs/Gen.cjs.map +1 -0
  30. package/_cjs/InvalidInterpretationError.cjs +18 -0
  31. package/_cjs/InvalidInterpretationError.cjs.map +1 -0
  32. package/_cjs/ParseError.cjs +223 -0
  33. package/_cjs/ParseError.cjs.map +1 -0
  34. package/_cjs/{Decoder.cjs → ParseResult.cjs} +25 -22
  35. package/_cjs/ParseResult.cjs.map +1 -0
  36. package/_cjs/Parser/api.cjs +80 -0
  37. package/_cjs/Parser/api.cjs.map +1 -0
  38. package/_cjs/{Guard.cjs → Parser/definition.cjs} +17 -22
  39. package/_cjs/Parser/definition.cjs.map +1 -0
  40. package/_cjs/Parser/interpreter.cjs +410 -0
  41. package/_cjs/Parser/interpreter.cjs.map +1 -0
  42. package/_cjs/Parser.cjs +39 -0
  43. package/_cjs/Parser.cjs.map +1 -0
  44. package/_cjs/Schema/api/conc.cjs +84 -0
  45. package/_cjs/Schema/api/conc.cjs.map +1 -0
  46. package/_cjs/Schema/api/either.cjs +96 -0
  47. package/_cjs/Schema/api/either.cjs.map +1 -0
  48. package/_cjs/Schema/api/hashMap.cjs +161 -0
  49. package/_cjs/Schema/api/hashMap.cjs.map +1 -0
  50. package/_cjs/Schema/api/immutableArray.cjs +90 -0
  51. package/_cjs/Schema/api/immutableArray.cjs.map +1 -0
  52. package/_cjs/Schema/api/list.cjs +98 -0
  53. package/_cjs/Schema/api/list.cjs.map +1 -0
  54. package/_cjs/Schema/api/maybe.cjs +76 -0
  55. package/_cjs/Schema/api/maybe.cjs.map +1 -0
  56. package/_cjs/Schema/api.cjs +425 -0
  57. package/_cjs/Schema/api.cjs.map +1 -0
  58. package/_cjs/Schema/definition.cjs +26 -0
  59. package/_cjs/Schema/definition.cjs.map +1 -0
  60. package/_cjs/Schema/derivations.cjs +108 -0
  61. package/_cjs/Schema/derivations.cjs.map +1 -0
  62. package/_cjs/Schema.cjs +97 -237
  63. package/_cjs/Schema.cjs.map +1 -1
  64. package/_cjs/utils.cjs +52 -0
  65. package/_cjs/utils.cjs.map +1 -0
  66. package/_mjs/AST.mjs +1061 -0
  67. package/_mjs/AST.mjs.map +1 -0
  68. package/_mjs/ASTAnnotation.mjs +80 -0
  69. package/_mjs/ASTAnnotation.mjs.map +1 -0
  70. package/_mjs/ASTAnnotationMap.mjs +27 -0
  71. package/_mjs/ASTAnnotationMap.mjs.map +1 -0
  72. package/_mjs/Gen.mjs +173 -0
  73. package/_mjs/Gen.mjs.map +1 -0
  74. package/_mjs/InvalidInterpretationError.mjs +10 -0
  75. package/_mjs/InvalidInterpretationError.mjs.map +1 -0
  76. package/_mjs/ParseError.mjs +201 -0
  77. package/_mjs/ParseError.mjs.map +1 -0
  78. package/_mjs/ParseResult.mjs +22 -0
  79. package/_mjs/ParseResult.mjs.map +1 -0
  80. package/_mjs/Parser/api.mjs +67 -0
  81. package/_mjs/Parser/api.mjs.map +1 -0
  82. package/_mjs/Parser/definition.mjs +15 -0
  83. package/_mjs/Parser/definition.mjs.map +1 -0
  84. package/_mjs/Parser/interpreter.mjs +402 -0
  85. package/_mjs/Parser/interpreter.mjs.map +1 -0
  86. package/_mjs/Parser.mjs +5 -0
  87. package/_mjs/Parser.mjs.map +1 -0
  88. package/_mjs/Schema/api/conc.mjs +72 -0
  89. package/_mjs/Schema/api/conc.mjs.map +1 -0
  90. package/_mjs/Schema/api/either.mjs +85 -0
  91. package/_mjs/Schema/api/either.mjs.map +1 -0
  92. package/_mjs/Schema/api/hashMap.mjs +150 -0
  93. package/_mjs/Schema/api/hashMap.mjs.map +1 -0
  94. package/_mjs/Schema/api/immutableArray.mjs +79 -0
  95. package/_mjs/Schema/api/immutableArray.mjs.map +1 -0
  96. package/_mjs/Schema/api/list.mjs +87 -0
  97. package/_mjs/Schema/api/list.mjs.map +1 -0
  98. package/_mjs/Schema/api/maybe.mjs +65 -0
  99. package/_mjs/Schema/api/maybe.mjs.map +1 -0
  100. package/_mjs/Schema/api.mjs +368 -0
  101. package/_mjs/Schema/api.mjs.map +1 -0
  102. package/_mjs/Schema/definition.mjs +16 -0
  103. package/_mjs/Schema/definition.mjs.map +1 -0
  104. package/_mjs/Schema/derivations.mjs +94 -0
  105. package/_mjs/Schema/derivations.mjs.map +1 -0
  106. package/_mjs/Schema.mjs +12 -212
  107. package/_mjs/Schema.mjs.map +1 -1
  108. package/_mjs/utils.mjs +41 -0
  109. package/_mjs/utils.mjs.map +1 -0
  110. package/_src/AST.ts +1353 -0
  111. package/_src/ASTAnnotation.ts +98 -0
  112. package/_src/ASTAnnotationMap.ts +38 -0
  113. package/_src/Gen.ts +168 -0
  114. package/_src/InvalidInterpretationError.ts +6 -0
  115. package/_src/ParseError.ts +237 -0
  116. package/_src/ParseResult.ts +26 -0
  117. package/_src/Parser/api.ts +71 -0
  118. package/_src/Parser/definition.ts +24 -0
  119. package/_src/Parser/interpreter.ts +442 -0
  120. package/_src/Parser.ts +5 -0
  121. package/_src/Schema/api/conc.ts +78 -0
  122. package/_src/Schema/api/either.ts +96 -0
  123. package/_src/Schema/api/hashMap.ts +184 -0
  124. package/_src/Schema/api/immutableArray.ts +88 -0
  125. package/_src/Schema/api/list.ts +96 -0
  126. package/_src/Schema/api/maybe.ts +68 -0
  127. package/_src/Schema/api.ts +530 -0
  128. package/_src/Schema/definition.ts +32 -0
  129. package/_src/Schema/derivations.ts +185 -0
  130. package/_src/Schema.ts +14 -254
  131. package/_src/global.ts +53 -0
  132. package/_src/utils.ts +48 -0
  133. package/global.d.ts +52 -0
  134. package/package.json +2 -2
  135. package/utils.d.ts +8 -0
  136. package/Decoder.d.ts +0 -3
  137. package/Encoder.d.ts +0 -4
  138. package/Guard.d.ts +0 -3
  139. package/Schemable.d.ts +0 -39
  140. package/_cjs/Decoder.cjs.map +0 -1
  141. package/_cjs/Encoder.cjs +0 -45
  142. package/_cjs/Encoder.cjs.map +0 -1
  143. package/_cjs/Guard.cjs.map +0 -1
  144. package/_cjs/Schemable.cjs +0 -6
  145. package/_cjs/Schemable.cjs.map +0 -1
  146. package/_mjs/Decoder.mjs +0 -20
  147. package/_mjs/Decoder.mjs.map +0 -1
  148. package/_mjs/Encoder.mjs +0 -36
  149. package/_mjs/Encoder.mjs.map +0 -1
  150. package/_mjs/Guard.mjs +0 -20
  151. package/_mjs/Guard.mjs.map +0 -1
  152. package/_mjs/Schemable.mjs +0 -2
  153. package/_mjs/Schemable.mjs.map +0 -1
  154. package/_src/Decoder.ts +0 -20
  155. package/_src/Encoder.ts +0 -38
  156. package/_src/Guard.ts +0 -20
  157. package/_src/Schemable.ts +0 -46
@@ -0,0 +1,530 @@
1
+ import type { Literal, ParseOptions, TemplateLiteral, TypeLiteral } from "../AST.js";
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
+
6
+ import { show } from "@fncts/base/data/Showable";
7
+
8
+ import { getParameter } from "../AST.js";
9
+ import { ASTTag, concrete, TemplateLiteralSpan } from "../AST.js";
10
+ import { ownKeys } from "../utils.js";
11
+
12
+ /**
13
+ * @tsplus static fncts.schema.SchemaOps fromAST
14
+ */
15
+ export function make<A>(ast: AST): Schema<A> {
16
+ return new Schema(ast);
17
+ }
18
+
19
+ /**
20
+ * @tsplus pipeable fncts.schema.Schema annotate
21
+ */
22
+ export function annotate<V>(annotation: ASTAnnotation<V>, value: V) {
23
+ return <A>(self: Schema<A>): Schema<A> => {
24
+ return Schema.fromAST(self.ast.clone({ annotations: self.ast.annotations.annotate(annotation, value) }));
25
+ };
26
+ }
27
+
28
+ /**
29
+ * @tsplus static fncts.schema.SchemaOps declaration
30
+ */
31
+ export function declaration(
32
+ typeParameters: Vector<Schema<any>>,
33
+ type: Schema<any>,
34
+ decode: (...typeParameters: ReadonlyArray<Schema<any>>) => Parser<any>,
35
+ annotations?: ASTAnnotationMap,
36
+ ): Schema<any> {
37
+ return Schema.fromAST(
38
+ AST.createDeclaration(
39
+ typeParameters.map((tp) => tp.ast),
40
+ type.ast,
41
+ (...typeParameters) => decode(...typeParameters.map(Schema.fromAST)),
42
+ annotations,
43
+ ),
44
+ );
45
+ }
46
+
47
+ /**
48
+ * @tsplus pipeable fncts.schema.Schema filter
49
+ */
50
+ export function filter<A, B extends A>(refinement: Refinement<A, B>): (self: Schema<A>) => Schema<B>;
51
+ export function filter<A>(predicate: Predicate<A>): (self: Schema<A>) => Schema<A>;
52
+ export function filter<A>(predicate: Predicate<A>) {
53
+ 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
+ );
59
+ return Schema.fromAST(ast);
60
+ };
61
+ }
62
+
63
+ /**
64
+ * @tsplus pipeable fncts.schema.Schema brand
65
+ */
66
+ export function brand<A, K extends string>(validation: Validation<A, K>) {
67
+ return (self: Schema<A>): Schema<A & Brand.Valid<A, K>> => {
68
+ const ast = AST.createRefinement(
69
+ self.ast,
70
+ (a: A) => (validation.validate(a) ? ParseResult.succeed(a) : ParseResult.fail(ParseError.TypeError(ast, a))),
71
+ false,
72
+ self.ast.annotations.annotate(ASTAnnotation.Brand, Vector(validation)),
73
+ );
74
+ return Schema.fromAST(ast);
75
+ };
76
+ }
77
+
78
+ function makeLiteral<Literal extends LiteralValue>(value: Literal): Schema<Literal> {
79
+ return Schema.fromAST(AST.createLiteral(value));
80
+ }
81
+
82
+ /**
83
+ * @tsplus static fncts.schema.SchemaOps literal
84
+ */
85
+ export function literal<Literals extends ReadonlyArray<LiteralValue>>(...literals: Literals): Schema<Literals[number]> {
86
+ return Schema.union(...literals.map(makeLiteral));
87
+ }
88
+
89
+ /**
90
+ * @tsplus static fncts.schema.SchemaOps never
91
+ * @tsplus implicit
92
+ */
93
+ export const never: Schema<never> = Schema.fromAST(AST.neverKeyword);
94
+
95
+ /**
96
+ * @tsplus static fncts.schema.SchemaOps unknown
97
+ * @tsplus implicit
98
+ */
99
+ export const unknown: Schema<unknown> = Schema.fromAST(AST.unknownKeyword);
100
+
101
+ /**
102
+ * @tsplus static fncts.schema.SchemaOps any
103
+ */
104
+ export const any: Schema<any> = Schema.fromAST(AST.anyKeyword);
105
+
106
+ /**
107
+ * @tsplus static fncts.schema.SchemaOps undefined
108
+ * @tsplus implicit
109
+ */
110
+ export const _undefined: Schema<undefined> = Schema.fromAST(AST.undefinedKeyword);
111
+
112
+ export { _undefined as undefined };
113
+
114
+ /**
115
+ * @tsplus static fncts.schema.SchemaOps null
116
+ * @tsplus implicit
117
+ */
118
+ export const _null: Schema<null> = Schema.fromAST(AST.createLiteral(null));
119
+
120
+ export { _null as null };
121
+
122
+ /**
123
+ * @tsplus static fncts.schema.SchemaOps void
124
+ * @tsplus implicit
125
+ */
126
+ export const _void: Schema<void> = Schema.fromAST(AST.voidKeyword);
127
+
128
+ export { _void as void };
129
+
130
+ /**
131
+ * @tsplus static fncts.schema.SchemaOps string
132
+ * @tsplus implicit
133
+ */
134
+ export const string: Schema<string> = Schema.fromAST(AST.stringKeyword);
135
+
136
+ /**
137
+ * @tsplus static fncts.schema.SchemaOps number
138
+ * @tsplus implicit
139
+ */
140
+ export const number: Schema<number> = Schema.fromAST(AST.numberKeyword);
141
+
142
+ /**
143
+ * @tsplus static fncts.schema.SchemaOps boolean
144
+ * @tsplus implicit
145
+ */
146
+ export const boolean: Schema<boolean> = Schema.fromAST(AST.booleanKeyword);
147
+
148
+ /**
149
+ * @tsplus static fncts.schema.SchemaOps bigint
150
+ * @tsplus implicit
151
+ */
152
+ export const bigint: Schema<bigint> = Schema.fromAST(AST.bigIntKeyword);
153
+
154
+ /**
155
+ * @tsplus static fncts.schema.SchemaOps symbol
156
+ * @tsplus implicit
157
+ */
158
+ export const symbol: Schema<symbol> = Schema.fromAST(AST.symbolKeyword);
159
+
160
+ /**
161
+ * @tsplus static fncts.schema.SchemaOps object
162
+ * @tsplus implicit
163
+ */
164
+ export const object: Schema<object> = Schema.fromAST(AST.objectKeyword);
165
+
166
+ /**
167
+ * @tsplus static fncts.schema.SchemaOps union
168
+ * @tsplus derive fncts.schema.Schema<|> 30
169
+ */
170
+ export function union<A extends ReadonlyArray<unknown>>(
171
+ ...members: {
172
+ [K in keyof A]: Schema<A[K]>;
173
+ }
174
+ ): Schema<A[number]> {
175
+ return Schema.fromAST(AST.createUnion(Vector.from(members.map((m) => m.ast))));
176
+ }
177
+
178
+ /**
179
+ * @tsplus getter fncts.schema.Schema nullable
180
+ */
181
+ export function nullable<A>(self: Schema<A>): Schema<A | null> {
182
+ return Schema.union(Schema.null, self);
183
+ }
184
+
185
+ /**
186
+ * @tsplus static fncts.schema.SchemaOps uniqueSymbol
187
+ */
188
+ export function uniqueSymbol<S extends symbol>(symbol: S, annotations?: ASTAnnotationMap): Schema<S> {
189
+ return Schema.fromAST(AST.createUniqueSymbol(symbol, annotations));
190
+ }
191
+
192
+ /**
193
+ * @tsplus getter fncts.schema.Schema optional
194
+ */
195
+ export function optional<A>(self: Schema<A>): OptionalSchema<A> {
196
+ return Schema.fromAST(
197
+ self.ast.clone({ annotations: self.ast.annotations.annotate(ASTAnnotation.Optional, true) }),
198
+ ) as OptionalSchema<A>;
199
+ }
200
+
201
+ /**
202
+ * @tsplus fluent fncts.schema.Schema isOptional
203
+ */
204
+ export function isOptional<A>(self: Schema<A>): self is OptionalSchema<A> {
205
+ return self.ast.annotations.get(ASTAnnotation.Optional).getOrElse(false);
206
+ }
207
+
208
+ export type OptionalSchemaKeys<T> = { [K in keyof T]: T[K] extends OptionalSchema<any> ? K : never }[keyof T];
209
+
210
+ /**
211
+ * @tsplus getter fncts.schema.Schema parseOptional
212
+ */
213
+ export function parseOptional<A>(self: Schema<A>): Schema<Maybe<A>> {
214
+ return Schema.fromAST(
215
+ self.ast.clone({ annotations: self.ast.annotations.annotate(ASTAnnotation.ParseOptional, true) }),
216
+ );
217
+ }
218
+
219
+ /**
220
+ * @tsplus fluent fncts.schema.Schema isParseOptional
221
+ */
222
+ export function isParseOptional<A>(self: Schema<A>): boolean {
223
+ return self.ast.annotations.get(ASTAnnotation.ParseOptional).getOrElse(false);
224
+ }
225
+
226
+ export type Spread<A> = {
227
+ [K in keyof A]: A[K];
228
+ } extends infer B
229
+ ? B
230
+ : never;
231
+
232
+ /**
233
+ * @tsplus static fncts.schema.SchemaOps struct
234
+ */
235
+ export function struct<Fields extends Record<PropertyKey, Schema<any>>>(
236
+ fields: Fields,
237
+ ): Schema<
238
+ Spread<
239
+ { readonly [K in Exclude<keyof Fields, OptionalSchemaKeys<Fields>>]: Schema.Infer<Fields[K]> } & {
240
+ readonly [K in OptionalSchemaKeys<Fields>]?: Schema.Infer<Fields[K]>;
241
+ }
242
+ >
243
+ > {
244
+ const parseOptionalKeys: Vector<PropertyKey> = ownKeys(fields).filter((key) => isParseOptional(fields[key]!));
245
+ const struct = Schema.fromAST(
246
+ AST.createTypeLiteral(
247
+ ownKeys(fields).map((key) => AST.createPropertySignature(key, fields[key]!.ast, isOptional(fields[key]!), true)),
248
+ Vector.empty(),
249
+ ),
250
+ );
251
+ if (parseOptionalKeys.isEmpty()) {
252
+ return struct as Schema<any>;
253
+ }
254
+ const propertySignatures = (struct.ast as TypeLiteral).propertySignatures;
255
+ const from = Schema.fromAST<any>(
256
+ AST.createTypeLiteral(
257
+ propertySignatures.map((p) =>
258
+ parseOptionalKeys.includes(p.name)
259
+ ? AST.createPropertySignature(
260
+ p.name,
261
+ AST.createUnion(Vector(AST.undefinedKeyword, AST.createLiteral(null), p.type)),
262
+ true,
263
+ p.isReadonly,
264
+ )
265
+ : p,
266
+ ),
267
+ Vector.empty(),
268
+ ),
269
+ );
270
+ const to = Schema.fromAST<any>(
271
+ AST.createTypeLiteral(
272
+ propertySignatures.map((p) => {
273
+ if (parseOptionalKeys.includes(p.name)) {
274
+ if (fields[p.name]!.ast.isLazy()) {
275
+ return AST.createPropertySignature(
276
+ p.name,
277
+ AST.createLazy(() => Schema.maybe(fields[p.name]!).ast),
278
+ true,
279
+ p.isReadonly,
280
+ );
281
+ }
282
+ return AST.createPropertySignature(p.name, Schema.maybe(fields[p.name]!).ast, true, p.isReadonly);
283
+ }
284
+ return p;
285
+ }),
286
+ Vector.empty(),
287
+ ),
288
+ );
289
+ return from.transform(
290
+ to,
291
+ (input) => {
292
+ const output = { ...input };
293
+ for (const key of parseOptionalKeys) {
294
+ output[key] = Maybe.fromNullable(input[key]);
295
+ }
296
+ return output;
297
+ },
298
+ (input) => {
299
+ const output = { ...input };
300
+ for (const key of parseOptionalKeys) {
301
+ const value: Maybe<any> = input[key];
302
+ if (value.isNothing()) {
303
+ delete output[key];
304
+ continue;
305
+ }
306
+ output[key] = value.value;
307
+ }
308
+ return output;
309
+ },
310
+ );
311
+ }
312
+
313
+ /**
314
+ * @tsplus static fncts.schema.SchemaOps tuple
315
+ */
316
+ export function tuple<Elements extends ReadonlyArray<Schema<any>>>(
317
+ ...elements: Elements
318
+ ): Schema<{ readonly [K in keyof Elements]: Schema.Infer<Elements[K]> }> {
319
+ return Schema.fromAST(
320
+ AST.createTuple(Vector.from(elements.map((schema) => AST.createElement(schema.ast, false))), Nothing(), true),
321
+ );
322
+ }
323
+
324
+ /**
325
+ * @tsplus static fncts.schema.SchemaOps lazy
326
+ */
327
+ export function lazy<A>(f: () => Schema<A>, annotations?: ASTAnnotationMap): Schema<A> {
328
+ return Schema.fromAST(AST.createLazy(() => f().ast, annotations));
329
+ }
330
+
331
+ /**
332
+ * @tsplus static fncts.schema.SchemaOps array
333
+ * @tsplus getter fncts.schema.Schema array
334
+ */
335
+ export function array<A>(item: Schema<A>): Schema<ReadonlyArray<A>> {
336
+ return Schema.fromAST(AST.createTuple(Vector.empty(), Just(Vector(item.ast)), true));
337
+ }
338
+
339
+ /**
340
+ * @tsplus static fncts.schema.SchemaOps mutableArray
341
+ * @tsplus getter fncts.schema.Schema mutableArray
342
+ */
343
+ export function mutableArray<A>(item: Schema<A>): Schema<Array<A>> {
344
+ return Schema.fromAST(AST.createTuple(Vector.empty(), Just(Vector(item.ast)), false));
345
+ }
346
+
347
+ /**
348
+ * @tsplus static fncts.schema.SchemaOps record
349
+ */
350
+ export function record<K extends string | symbol, V>(
351
+ key: Schema<K>,
352
+ value: Schema<V>,
353
+ ): Schema<{ readonly [k in K]: V }> {
354
+ return Schema.fromAST(AST.createRecord(key.ast, value.ast, true));
355
+ }
356
+
357
+ /**
358
+ * @tsplus static fncts.schema.SchemaOps enum
359
+ */
360
+ export function enum_<A extends { [x: string]: string | number }>(enums: A): Schema<A[keyof A]> {
361
+ return Schema.fromAST(
362
+ AST.createEnum(
363
+ Vector.from(
364
+ Object.keys(enums)
365
+ .filter((key) => typeof enums[enums[key]!] !== "number")
366
+ .map((key) => [key, enums[key]!]),
367
+ ),
368
+ ),
369
+ );
370
+ }
371
+
372
+ export { enum_ as enum };
373
+
374
+ type Join<T> = T extends [infer Head, ...infer Tail]
375
+ ? `${Head & (string | number | bigint | boolean | null | undefined)}${Tail extends [] ? "" : Join<Tail>}`
376
+ : never;
377
+
378
+ function getTemplateLiterals(ast: AST): Vector<TemplateLiteral | Literal> {
379
+ concrete(ast);
380
+ switch (ast._tag) {
381
+ case ASTTag.Literal:
382
+ return Vector(ast);
383
+ case ASTTag.NumberKeyword:
384
+ case ASTTag.StringKeyword:
385
+ return Vector(AST.createTemplateLiteral("", Vector(new TemplateLiteralSpan(ast, ""))));
386
+ case ASTTag.Union:
387
+ return ast.types.flatMap(getTemplateLiterals);
388
+ default:
389
+ throw new Error(`Unsupported template literal span ${show(ast)}`);
390
+ }
391
+ }
392
+
393
+ function combineTemplateLiterals(
394
+ a: TemplateLiteral | Literal,
395
+ b: TemplateLiteral | Literal,
396
+ ): TemplateLiteral | Literal {
397
+ if (a.isLiteral()) {
398
+ return b.isLiteral()
399
+ ? AST.createLiteral(String(a.literal) + String(b.literal))
400
+ : AST.createTemplateLiteral(String(a.literal) + b.head, b.spans);
401
+ }
402
+ if (b.isLiteral()) {
403
+ if (!a.spans.isNonEmpty()) {
404
+ throw new Error("Invalid template literal");
405
+ }
406
+ const last = a.spans.unsafeLast!;
407
+ return AST.createTemplateLiteral(
408
+ a.head,
409
+ a.spans.slice(0, -1).append(new TemplateLiteralSpan(last.type, last.literal + String(b.literal))),
410
+ );
411
+ }
412
+ if (!a.spans.isNonEmpty()) {
413
+ throw new Error("Invalid template literal");
414
+ }
415
+ const last = a.spans.unsafeLast!;
416
+ return AST.createTemplateLiteral(
417
+ a.head,
418
+ a.spans
419
+ .slice(0, -1)
420
+ .append(new TemplateLiteralSpan(last.type, last.literal + String(b.head)))
421
+ .concat(b.spans),
422
+ );
423
+ }
424
+
425
+ /**
426
+ * @tsplus static fncts.schema.SchemaOps templateLiteral
427
+ */
428
+ export function templateLiteral<T extends [Schema<any>, ...Array<Schema<any>>]>(
429
+ ...[head, ...tail]: T
430
+ ): Schema<Join<{ [K in keyof T]: Schema.Infer<T[K]> }>> {
431
+ let types: Vector<TemplateLiteral | Literal> = getTemplateLiterals(head.ast);
432
+ for (const span of tail) {
433
+ types = types.flatMap((a) => getTemplateLiterals(span.ast).map((b) => combineTemplateLiterals(a, b)));
434
+ }
435
+ return Schema.fromAST(AST.createUnion(types));
436
+ }
437
+
438
+ /**
439
+ * @tsplus static fncts.schema.SchemaOps keyof
440
+ * @tsplus getter fncts.schema.Schema keyof
441
+ */
442
+ export function keyof<A>(self: Schema<A>): Schema<keyof A> {
443
+ return Schema.fromAST(self.ast.keyof);
444
+ }
445
+
446
+ function isOverlappingPropertySignatures(x: TypeLiteral, y: TypeLiteral): boolean {
447
+ return x.propertySignatures.some((px) => y.propertySignatures.some((py) => px.name === py.name));
448
+ }
449
+
450
+ function isOverlappingIndexSignatures(x: TypeLiteral, y: TypeLiteral): boolean {
451
+ return x.indexSignatures.some((ix) =>
452
+ y.indexSignatures.some((iy) => {
453
+ const bx = getParameter(ix.parameter);
454
+ const by = getParameter(iy.parameter);
455
+ return (bx.isStringKeyword() && by.isStringKeyword()) || (bx.isSymbolKeyword() && by.isSymbolKeyword());
456
+ }),
457
+ );
458
+ }
459
+
460
+ /**
461
+ * @tsplus pipeable fncts.schema.Schema extend
462
+ */
463
+ export function extend<B>(that: Schema<B>) {
464
+ return <A>(self: Schema<A>): Schema<Spread<A & B>> => {
465
+ const selfTypes = self.ast.isUnion() ? self.ast.types : Vector(self.ast);
466
+ const thatTypes = that.ast.isUnion() ? that.ast.types : Vector(that.ast);
467
+ if (selfTypes.every(AST.isTypeLiteral) && thatTypes.every(AST.isTypeLiteral)) {
468
+ return Schema.fromAST(
469
+ AST.createUnion(
470
+ selfTypes.flatMap((x) =>
471
+ thatTypes.map((y) => {
472
+ if (isOverlappingPropertySignatures(x, y)) {
473
+ throw new Error("`extend` cannot handle overlapping property signatures");
474
+ }
475
+ if (isOverlappingIndexSignatures(x, y)) {
476
+ throw new Error("`extend` cannot handle overlapping index signatures");
477
+ }
478
+ return AST.createTypeLiteral(
479
+ x.propertySignatures.concat(y.propertySignatures),
480
+ x.indexSignatures.concat(y.indexSignatures),
481
+ );
482
+ }),
483
+ ),
484
+ ),
485
+ );
486
+ }
487
+ throw new Error("`extend can only handle type literals or unions of type literals`");
488
+ };
489
+ }
490
+
491
+ /**
492
+ * @tsplus pipeable fncts.schema.Schema instanceOf
493
+ */
494
+ export function instanceOf<A extends abstract new (...args: any) => any>(constructor: A) {
495
+ return (self: Schema<object>): Schema<InstanceType<A>> => {
496
+ return self
497
+ .filter((value): value is InstanceType<A> => value instanceof constructor)
498
+ .annotate(ASTAnnotation.Description, `an instance of ${constructor.name}`);
499
+ };
500
+ }
501
+
502
+ /**
503
+ * @tsplus pipeable fncts.schema.Schema transformOrFail
504
+ */
505
+ export function transformOrFail<A, B>(
506
+ to: Schema<B>,
507
+ decode: (input: A, options?: ParseOptions) => ParseResult<B>,
508
+ encode: (input: B, options?: ParseOptions) => ParseResult<A>,
509
+ ) {
510
+ return (from: Schema<A>): Schema<B> => {
511
+ return Schema.fromAST(AST.createTransform(from.ast, to.ast, decode, encode, false));
512
+ };
513
+ }
514
+
515
+ /**
516
+ * @tsplus pipeable fncts.schema.Schema transform
517
+ */
518
+ export function transform<A, B>(
519
+ to: Schema<B>,
520
+ decode: (input: A, options?: ParseOptions) => B,
521
+ encode: (input: B, options?: ParseOptions) => A,
522
+ ) {
523
+ return (from: Schema<A>): Schema<B> => {
524
+ return from.transformOrFail(
525
+ to,
526
+ (input, options) => ParseResult.succeed(decode(input, options)),
527
+ (input, options) => ParseResult.succeed(encode(input, options)),
528
+ );
529
+ };
530
+ }
@@ -0,0 +1,32 @@
1
+ export const SchemaVariance = Symbol.for("fncts.schema.Schema.Variance");
2
+ export type SchemaVariance = typeof SchemaVariance;
3
+
4
+ export const SchemaTypeId = Symbol.for("fncts.schema.Schema");
5
+ export type SchemaTypeId = typeof SchemaTypeId;
6
+
7
+ export const OptionalSchemaSymbol = Symbol.for("fncts.schema.Schema.OptionalSchema");
8
+ export type OptionalSchemaSymbol = typeof OptionalSchemaSymbol;
9
+
10
+ export interface SchemaF extends HKT {
11
+ readonly type: Schema<this["A"]>;
12
+ }
13
+
14
+ /**
15
+ * @tsplus type fncts.schema.Schema
16
+ * @tsplus companion fncts.schema.SchemaOps
17
+ */
18
+ export class Schema<in out A> {
19
+ readonly [SchemaTypeId]: SchemaTypeId = SchemaTypeId;
20
+ declare [SchemaVariance]: {
21
+ _A: (_: A) => A;
22
+ };
23
+ constructor(readonly ast: AST) {}
24
+ }
25
+
26
+ export interface OptionalSchema<in out A> extends Schema<A> {
27
+ [OptionalSchemaSymbol]: OptionalSchemaSymbol;
28
+ }
29
+
30
+ export declare namespace Schema {
31
+ export type Infer<S extends Schema<any>> = Parameters<S[SchemaVariance]["_A"]>[0];
32
+ }