schematox 1.2.1 → 1.2.2-alpha

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 (63) hide show
  1. package/dist/constants.d.ts +20 -0
  2. package/dist/constants.d.ts.map +1 -0
  3. package/dist/constants.js +22 -0
  4. package/dist/constants.js.map +1 -0
  5. package/dist/index.d.ts +10 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +9 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/parse.d.ts +5 -0
  10. package/dist/parse.d.ts.map +1 -0
  11. package/dist/parse.js +328 -0
  12. package/dist/parse.js.map +1 -0
  13. package/dist/struct.d.ts +48 -0
  14. package/dist/struct.d.ts.map +1 -0
  15. package/dist/struct.js +111 -0
  16. package/dist/struct.js.map +1 -0
  17. package/dist/types/extensions.d.ts +13 -0
  18. package/dist/types/extensions.d.ts.map +1 -0
  19. package/dist/types/extensions.js +2 -0
  20. package/dist/types/extensions.js.map +1 -0
  21. package/dist/types/infer.d.ts +35 -0
  22. package/dist/types/infer.d.ts.map +1 -0
  23. package/dist/types/infer.js +2 -0
  24. package/dist/types/infer.js.map +1 -0
  25. package/dist/types/schema.d.ts +93 -0
  26. package/dist/types/schema.d.ts.map +1 -0
  27. package/dist/types/schema.js +2 -0
  28. package/dist/types/schema.js.map +1 -0
  29. package/dist/types/standard-schema.d.ts +35 -0
  30. package/dist/types/standard-schema.d.ts.map +1 -0
  31. package/dist/types/standard-schema.js +2 -0
  32. package/dist/types/standard-schema.js.map +1 -0
  33. package/dist/types/struct.d.ts +52 -0
  34. package/dist/types/struct.d.ts.map +1 -0
  35. package/dist/types/struct.js +2 -0
  36. package/dist/types/struct.js.map +1 -0
  37. package/dist/types/utils.d.ts +41 -0
  38. package/dist/types/utils.d.ts.map +1 -0
  39. package/dist/types/utils.js +2 -0
  40. package/dist/types/utils.js.map +1 -0
  41. package/dist/utils.d.ts +9 -0
  42. package/dist/utils.d.ts.map +1 -0
  43. package/dist/utils.js +14 -0
  44. package/dist/utils.js.map +1 -0
  45. package/package.json +15 -4
  46. package/src/tests/README.md +390 -0
  47. package/src/tests/by-struct/array.test.ts +1684 -0
  48. package/src/tests/by-struct/boolean.test.ts +741 -0
  49. package/src/tests/by-struct/literal.test.ts +755 -0
  50. package/src/tests/by-struct/number.test.ts +1234 -0
  51. package/src/tests/by-struct/object.test.ts +1484 -0
  52. package/src/tests/by-struct/record.test.ts +1802 -0
  53. package/src/tests/by-struct/string.test.ts +1252 -0
  54. package/src/tests/by-struct/tuple.test.ts +1341 -0
  55. package/src/tests/by-struct/union.test.ts +1284 -0
  56. package/src/tests/fixtures.ts +52 -0
  57. package/src/tests/fold-constants.ts +247 -0
  58. package/src/tests/fold-morph.ts +49 -0
  59. package/src/tests/type.ts +1 -0
  60. package/src/tests/types/extensions.test.ts +117 -0
  61. package/src/tests/types/infer.test.ts +1410 -0
  62. package/src/tests/utils.test.ts +191 -0
  63. package/CHANGELOG.md +0 -52
@@ -0,0 +1,1802 @@
1
+ import * as x from '../../'
2
+ import * as fixture from '../fixtures'
3
+
4
+ import type { StructSharedKeys } from '../type'
5
+
6
+ describe('Type inference and parse by schema/construct/struct (foldA)', () => {
7
+ it('branded key', () => {
8
+ const schema = {
9
+ type: 'record',
10
+ key: { type: 'string', brand: ['x', 'y'] },
11
+ of: { type: 'boolean' },
12
+ } as const satisfies x.Schema
13
+
14
+ const struct = x.record(x.boolean(), x.string().brand('x', 'y'))
15
+
16
+ type Key = string & { __x: 'y' }
17
+ type ExpectedSubj = Record<Key, boolean>
18
+
19
+ const subjects: Array<ExpectedSubj> = [
20
+ {},
21
+ // @ts-expect-error must not allow not branded string key declaration
22
+ { ['x']: true },
23
+ { ['x' as Key]: false },
24
+ ]
25
+
26
+ // @ts-expect-error must not allow not branded string property access
27
+ subjects[1]!['x'] = false
28
+
29
+ foldA: {
30
+ const construct = x.makeStruct(schema)
31
+
32
+ /* ensure that schema/construct/struct/~standard subject types are identical */
33
+
34
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
35
+
36
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
37
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
38
+
39
+ type SchemaSubj = x.Infer<typeof schema>
40
+
41
+ x.tCh<SchemaSubj, ExpectedSubj>()
42
+ x.tCh<ExpectedSubj, SchemaSubj>()
43
+
44
+ type StructSubj = x.Infer<typeof struct.__schema>
45
+
46
+ x.tCh<StructSubj, ExpectedSubj>()
47
+ x.tCh<ExpectedSubj, StructSubj>()
48
+
49
+ type StandardSubj = NonNullable<
50
+ (typeof struct)['~standard']['types']
51
+ >['output']
52
+
53
+ x.tCh<StandardSubj, ExpectedSubj>()
54
+ x.tCh<ExpectedSubj, StandardSubj>()
55
+
56
+ /* parsed either type check */
57
+
58
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
59
+
60
+ const parsed = x.parse(schema, undefined)
61
+
62
+ type SchemaParsed = typeof parsed
63
+
64
+ x.tCh<SchemaParsed, ExpectedParsed>()
65
+ x.tCh<ExpectedParsed, SchemaParsed>()
66
+
67
+ type ConstructParsed = ReturnType<typeof construct.parse>
68
+
69
+ x.tCh<ConstructParsed, ExpectedParsed>()
70
+ x.tCh<ExpectedParsed, ConstructParsed>()
71
+
72
+ type StructParsed = ReturnType<typeof struct.parse>
73
+
74
+ x.tCh<StructParsed, ExpectedParsed>()
75
+ x.tCh<ExpectedParsed, StructParsed>()
76
+
77
+ type StandardParsed = Extract<
78
+ ReturnType<(typeof struct)['~standard']['validate']>,
79
+ { value: unknown }
80
+ >['value']
81
+
82
+ x.tCh<StandardParsed, ExpectedSubj>()
83
+ x.tCh<ExpectedSubj, StandardParsed>()
84
+
85
+ /* runtime schema check */
86
+
87
+ expect(struct.__schema).toStrictEqual(schema)
88
+ expect(construct.__schema).toStrictEqual(schema)
89
+ expect(construct.__schema === schema).toBe(false)
90
+
91
+ /* parse result check */
92
+
93
+ for (const subj of subjects) {
94
+ const schemaParsed = x.parse(schema, subj)
95
+
96
+ expect(schemaParsed.error).toBe(undefined)
97
+ expect(schemaParsed.data).toStrictEqual(subj)
98
+
99
+ const constructParsed = construct.parse(subj)
100
+
101
+ expect(constructParsed.error).toBe(undefined)
102
+ expect(constructParsed.data).toStrictEqual(subj)
103
+
104
+ const structParsed = struct.parse(subj)
105
+
106
+ expect(structParsed.error).toBe(undefined)
107
+ expect(structParsed.data).toStrictEqual(subj)
108
+
109
+ const standardParsed = struct['~standard'].validate(subj)
110
+
111
+ if (standardParsed instanceof Promise) {
112
+ throw Error('Not expected')
113
+ }
114
+
115
+ if (standardParsed.issues !== undefined) {
116
+ throw Error('not expected')
117
+ }
118
+
119
+ expect(standardParsed.value).toStrictEqual(subj)
120
+ }
121
+ }
122
+ })
123
+
124
+ it('required', () => {
125
+ const schema = {
126
+ type: 'record',
127
+ of: { type: 'boolean' },
128
+ } as const satisfies x.Schema
129
+
130
+ const struct = x.record(x.boolean())
131
+
132
+ type ExpectedSubj = Record<string, boolean>
133
+
134
+ const subjects: Array<ExpectedSubj> = [{}, { x: true }, { x: false }]
135
+
136
+ foldA: {
137
+ const construct = x.makeStruct(schema)
138
+
139
+ /* ensure that schema/construct/struct/~standard subject types are identical */
140
+
141
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
142
+
143
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
144
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
145
+
146
+ type SchemaSubj = x.Infer<typeof schema>
147
+
148
+ x.tCh<SchemaSubj, ExpectedSubj>()
149
+ x.tCh<ExpectedSubj, SchemaSubj>()
150
+
151
+ type StructSubj = x.Infer<typeof struct.__schema>
152
+
153
+ x.tCh<StructSubj, ExpectedSubj>()
154
+ x.tCh<ExpectedSubj, StructSubj>()
155
+
156
+ type StandardSubj = NonNullable<
157
+ (typeof struct)['~standard']['types']
158
+ >['output']
159
+
160
+ x.tCh<StandardSubj, ExpectedSubj>()
161
+ x.tCh<ExpectedSubj, StandardSubj>()
162
+
163
+ /* parsed either type check */
164
+
165
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
166
+
167
+ const parsed = x.parse(schema, undefined)
168
+
169
+ type SchemaParsed = typeof parsed
170
+
171
+ x.tCh<SchemaParsed, ExpectedParsed>()
172
+ x.tCh<ExpectedParsed, SchemaParsed>()
173
+
174
+ type ConstructParsed = ReturnType<typeof construct.parse>
175
+
176
+ x.tCh<ConstructParsed, ExpectedParsed>()
177
+ x.tCh<ExpectedParsed, ConstructParsed>()
178
+
179
+ type StructParsed = ReturnType<typeof struct.parse>
180
+
181
+ x.tCh<StructParsed, ExpectedParsed>()
182
+ x.tCh<ExpectedParsed, StructParsed>()
183
+
184
+ type StandardParsed = Extract<
185
+ ReturnType<(typeof struct)['~standard']['validate']>,
186
+ { value: unknown }
187
+ >['value']
188
+
189
+ x.tCh<StandardParsed, ExpectedSubj>()
190
+ x.tCh<ExpectedSubj, StandardParsed>()
191
+
192
+ /* runtime schema check */
193
+
194
+ expect(struct.__schema).toStrictEqual(schema)
195
+ expect(construct.__schema).toStrictEqual(schema)
196
+ expect(construct.__schema === schema).toBe(false)
197
+
198
+ /* parse result check */
199
+
200
+ for (const subj of subjects) {
201
+ const schemaParsed = x.parse(schema, subj)
202
+
203
+ expect(schemaParsed.error).toBe(undefined)
204
+ expect(schemaParsed.data).toStrictEqual(subj)
205
+
206
+ const constructParsed = construct.parse(subj)
207
+
208
+ expect(constructParsed.error).toBe(undefined)
209
+ expect(constructParsed.data).toStrictEqual(subj)
210
+
211
+ const structParsed = struct.parse(subj)
212
+
213
+ expect(structParsed.error).toBe(undefined)
214
+ expect(structParsed.data).toStrictEqual(subj)
215
+
216
+ const standardParsed = struct['~standard'].validate(subj)
217
+
218
+ if (standardParsed instanceof Promise) {
219
+ throw Error('Not expected')
220
+ }
221
+
222
+ if (standardParsed.issues !== undefined) {
223
+ throw Error('not expected')
224
+ }
225
+
226
+ expect(standardParsed.value).toStrictEqual(subj)
227
+ }
228
+ }
229
+ })
230
+
231
+ it('optional', () => {
232
+ const schema = {
233
+ type: 'record',
234
+ of: { type: 'boolean' },
235
+ optional: true,
236
+ } as const satisfies x.Schema
237
+
238
+ const struct = x.record(x.boolean()).optional()
239
+
240
+ type ExpectedSubj = Record<string, boolean> | undefined
241
+
242
+ const subjects: Array<ExpectedSubj> = [{}, { x: true }]
243
+
244
+ foldA: {
245
+ const construct = x.makeStruct(schema)
246
+
247
+ /* ensure that schema/construct/struct/~standard subject types are identical */
248
+
249
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
250
+
251
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
252
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
253
+
254
+ type SchemaSubj = x.Infer<typeof schema>
255
+
256
+ x.tCh<SchemaSubj, ExpectedSubj>()
257
+ x.tCh<ExpectedSubj, SchemaSubj>()
258
+
259
+ type StructSubj = x.Infer<typeof struct.__schema>
260
+
261
+ x.tCh<StructSubj, ExpectedSubj>()
262
+ x.tCh<ExpectedSubj, StructSubj>()
263
+
264
+ type StandardSubj = NonNullable<
265
+ (typeof struct)['~standard']['types']
266
+ >['output']
267
+
268
+ x.tCh<StandardSubj, ExpectedSubj>()
269
+ x.tCh<ExpectedSubj, StandardSubj>()
270
+
271
+ /* parsed either type check */
272
+
273
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
274
+
275
+ const parsed = x.parse(schema, undefined)
276
+
277
+ type SchemaParsed = typeof parsed
278
+
279
+ x.tCh<SchemaParsed, ExpectedParsed>()
280
+ x.tCh<ExpectedParsed, SchemaParsed>()
281
+
282
+ type ConstructParsed = ReturnType<typeof construct.parse>
283
+
284
+ x.tCh<ConstructParsed, ExpectedParsed>()
285
+ x.tCh<ExpectedParsed, ConstructParsed>()
286
+
287
+ type StructParsed = ReturnType<typeof struct.parse>
288
+
289
+ x.tCh<StructParsed, ExpectedParsed>()
290
+ x.tCh<ExpectedParsed, StructParsed>()
291
+
292
+ type StandardParsed = Extract<
293
+ ReturnType<(typeof struct)['~standard']['validate']>,
294
+ { value: unknown }
295
+ >['value']
296
+
297
+ x.tCh<StandardParsed, ExpectedSubj>()
298
+ x.tCh<ExpectedSubj, StandardParsed>()
299
+
300
+ /* runtime schema check */
301
+
302
+ expect(struct.__schema).toStrictEqual(schema)
303
+ expect(construct.__schema).toStrictEqual(schema)
304
+ expect(construct.__schema === schema).toBe(false)
305
+
306
+ /* parse result check */
307
+
308
+ for (const subj of subjects) {
309
+ const schemaParsed = x.parse(schema, subj)
310
+
311
+ expect(schemaParsed.error).toBe(undefined)
312
+ expect(schemaParsed.data).toStrictEqual(subj)
313
+
314
+ const constructParsed = construct.parse(subj)
315
+
316
+ expect(constructParsed.error).toBe(undefined)
317
+ expect(constructParsed.data).toStrictEqual(subj)
318
+
319
+ const structParsed = struct.parse(subj)
320
+
321
+ expect(structParsed.error).toBe(undefined)
322
+ expect(structParsed.data).toStrictEqual(subj)
323
+
324
+ const standardParsed = struct['~standard'].validate(subj)
325
+
326
+ if (standardParsed instanceof Promise) {
327
+ throw Error('Not expected')
328
+ }
329
+
330
+ if (standardParsed.issues !== undefined) {
331
+ throw Error('not expected')
332
+ }
333
+
334
+ expect(standardParsed.value).toStrictEqual(subj)
335
+ }
336
+ }
337
+ })
338
+
339
+ it('nullable', () => {
340
+ const schema = {
341
+ type: 'record',
342
+ of: { type: 'boolean' },
343
+ nullable: true,
344
+ } as const satisfies x.Schema
345
+
346
+ const struct = x.record(x.boolean()).nullable()
347
+
348
+ type ExpectedSubj = Record<string, boolean> | null
349
+
350
+ const subjects: Array<ExpectedSubj> = [{ x: true }, null]
351
+
352
+ foldA: {
353
+ const construct = x.makeStruct(schema)
354
+
355
+ /* ensure that schema/construct/struct/~standard subject types are identical */
356
+
357
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
358
+
359
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
360
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
361
+
362
+ type SchemaSubj = x.Infer<typeof schema>
363
+
364
+ x.tCh<SchemaSubj, ExpectedSubj>()
365
+ x.tCh<ExpectedSubj, SchemaSubj>()
366
+
367
+ type StructSubj = x.Infer<typeof struct.__schema>
368
+
369
+ x.tCh<StructSubj, ExpectedSubj>()
370
+ x.tCh<ExpectedSubj, StructSubj>()
371
+
372
+ type StandardSubj = NonNullable<
373
+ (typeof struct)['~standard']['types']
374
+ >['output']
375
+
376
+ x.tCh<StandardSubj, ExpectedSubj>()
377
+ x.tCh<ExpectedSubj, StandardSubj>()
378
+
379
+ /* parsed either type check */
380
+
381
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
382
+
383
+ const parsed = x.parse(schema, undefined)
384
+
385
+ type SchemaParsed = typeof parsed
386
+
387
+ x.tCh<SchemaParsed, ExpectedParsed>()
388
+ x.tCh<ExpectedParsed, SchemaParsed>()
389
+
390
+ type ConstructParsed = ReturnType<typeof construct.parse>
391
+
392
+ x.tCh<ConstructParsed, ExpectedParsed>()
393
+ x.tCh<ExpectedParsed, ConstructParsed>()
394
+
395
+ type StructParsed = ReturnType<typeof struct.parse>
396
+
397
+ x.tCh<StructParsed, ExpectedParsed>()
398
+ x.tCh<ExpectedParsed, StructParsed>()
399
+
400
+ type StandardParsed = Extract<
401
+ ReturnType<(typeof struct)['~standard']['validate']>,
402
+ { value: unknown }
403
+ >['value']
404
+
405
+ x.tCh<StandardParsed, ExpectedSubj>()
406
+ x.tCh<ExpectedSubj, StandardParsed>()
407
+
408
+ /* runtime schema check */
409
+
410
+ expect(struct.__schema).toStrictEqual(schema)
411
+ expect(construct.__schema).toStrictEqual(schema)
412
+ expect(construct.__schema === schema).toBe(false)
413
+
414
+ /* parse result check */
415
+
416
+ for (const subj of subjects) {
417
+ const schemaParsed = x.parse(schema, subj)
418
+
419
+ expect(schemaParsed.error).toBe(undefined)
420
+ expect(schemaParsed.data).toStrictEqual(subj)
421
+
422
+ const constructParsed = construct.parse(subj)
423
+
424
+ expect(constructParsed.error).toBe(undefined)
425
+ expect(constructParsed.data).toStrictEqual(subj)
426
+
427
+ const structParsed = struct.parse(subj)
428
+
429
+ expect(structParsed.error).toBe(undefined)
430
+ expect(structParsed.data).toStrictEqual(subj)
431
+
432
+ const standardParsed = struct['~standard'].validate(subj)
433
+
434
+ if (standardParsed instanceof Promise) {
435
+ throw Error('Not expected')
436
+ }
437
+
438
+ if (standardParsed.issues !== undefined) {
439
+ throw Error('not expected')
440
+ }
441
+
442
+ expect(standardParsed.value).toStrictEqual(subj)
443
+ }
444
+ }
445
+ })
446
+
447
+ it('minLength', () => {
448
+ const schema = {
449
+ type: 'record',
450
+ of: { type: 'boolean' },
451
+ minLength: 2,
452
+ } as const satisfies x.Schema
453
+
454
+ const struct = x.record(x.boolean()).minLength(schema.minLength)
455
+
456
+ type ExpectedSubj = Record<string, boolean>
457
+
458
+ const subjects: Array<ExpectedSubj> = [
459
+ { 0: true, 1: false },
460
+ { 0: true, 1: false, 3: true },
461
+ ]
462
+
463
+ foldA: {
464
+ const construct = x.makeStruct(schema)
465
+
466
+ /* ensure that schema/construct/struct/~standard subject types are identical */
467
+
468
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
469
+
470
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
471
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
472
+
473
+ type SchemaSubj = x.Infer<typeof schema>
474
+
475
+ x.tCh<SchemaSubj, ExpectedSubj>()
476
+ x.tCh<ExpectedSubj, SchemaSubj>()
477
+
478
+ type StructSubj = x.Infer<typeof struct.__schema>
479
+
480
+ x.tCh<StructSubj, ExpectedSubj>()
481
+ x.tCh<ExpectedSubj, StructSubj>()
482
+
483
+ type StandardSubj = NonNullable<
484
+ (typeof struct)['~standard']['types']
485
+ >['output']
486
+
487
+ x.tCh<StandardSubj, ExpectedSubj>()
488
+ x.tCh<ExpectedSubj, StandardSubj>()
489
+
490
+ /* parsed either type check */
491
+
492
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
493
+
494
+ const parsed = x.parse(schema, undefined)
495
+
496
+ type SchemaParsed = typeof parsed
497
+
498
+ x.tCh<SchemaParsed, ExpectedParsed>()
499
+ x.tCh<ExpectedParsed, SchemaParsed>()
500
+
501
+ type ConstructParsed = ReturnType<typeof construct.parse>
502
+
503
+ x.tCh<ConstructParsed, ExpectedParsed>()
504
+ x.tCh<ExpectedParsed, ConstructParsed>()
505
+
506
+ type StructParsed = ReturnType<typeof struct.parse>
507
+
508
+ x.tCh<StructParsed, ExpectedParsed>()
509
+ x.tCh<ExpectedParsed, StructParsed>()
510
+
511
+ type StandardParsed = Extract<
512
+ ReturnType<(typeof struct)['~standard']['validate']>,
513
+ { value: unknown }
514
+ >['value']
515
+
516
+ x.tCh<StandardParsed, ExpectedSubj>()
517
+ x.tCh<ExpectedSubj, StandardParsed>()
518
+
519
+ /* runtime schema check */
520
+
521
+ expect(struct.__schema).toStrictEqual(schema)
522
+ expect(construct.__schema).toStrictEqual(schema)
523
+ expect(construct.__schema === schema).toBe(false)
524
+
525
+ /* parse result check */
526
+
527
+ for (const subj of subjects) {
528
+ const schemaParsed = x.parse(schema, subj)
529
+
530
+ expect(schemaParsed.error).toBe(undefined)
531
+ expect(schemaParsed.data).toStrictEqual(subj)
532
+
533
+ const constructParsed = construct.parse(subj)
534
+
535
+ expect(constructParsed.error).toBe(undefined)
536
+ expect(constructParsed.data).toStrictEqual(subj)
537
+
538
+ const structParsed = struct.parse(subj)
539
+
540
+ expect(structParsed.error).toBe(undefined)
541
+ expect(structParsed.data).toStrictEqual(subj)
542
+
543
+ const standardParsed = struct['~standard'].validate(subj)
544
+
545
+ if (standardParsed instanceof Promise) {
546
+ throw Error('Not expected')
547
+ }
548
+
549
+ if (standardParsed.issues !== undefined) {
550
+ throw Error('not expected')
551
+ }
552
+
553
+ expect(standardParsed.value).toStrictEqual(subj)
554
+ }
555
+ }
556
+ })
557
+
558
+ it('maxLength', () => {
559
+ const schema = {
560
+ type: 'record',
561
+ of: { type: 'boolean' },
562
+ maxLength: 3,
563
+ } as const satisfies x.Schema
564
+
565
+ const struct = x.record(x.boolean()).maxLength(schema.maxLength)
566
+
567
+ type ExpectedSubj = Record<string, boolean>
568
+
569
+ const subjects: Array<ExpectedSubj> = [
570
+ {},
571
+ { 0: true },
572
+ { 0: true, 1: false },
573
+ { 0: true, 1: false, 2: true },
574
+ ]
575
+
576
+ foldA: {
577
+ const construct = x.makeStruct(schema)
578
+
579
+ /* ensure that schema/construct/struct/~standard subject types are identical */
580
+
581
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
582
+
583
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
584
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
585
+
586
+ type SchemaSubj = x.Infer<typeof schema>
587
+
588
+ x.tCh<SchemaSubj, ExpectedSubj>()
589
+ x.tCh<ExpectedSubj, SchemaSubj>()
590
+
591
+ type StructSubj = x.Infer<typeof struct.__schema>
592
+
593
+ x.tCh<StructSubj, ExpectedSubj>()
594
+ x.tCh<ExpectedSubj, StructSubj>()
595
+
596
+ type StandardSubj = NonNullable<
597
+ (typeof struct)['~standard']['types']
598
+ >['output']
599
+
600
+ x.tCh<StandardSubj, ExpectedSubj>()
601
+ x.tCh<ExpectedSubj, StandardSubj>()
602
+
603
+ /* parsed either type check */
604
+
605
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
606
+
607
+ const parsed = x.parse(schema, undefined)
608
+
609
+ type SchemaParsed = typeof parsed
610
+
611
+ x.tCh<SchemaParsed, ExpectedParsed>()
612
+ x.tCh<ExpectedParsed, SchemaParsed>()
613
+
614
+ type ConstructParsed = ReturnType<typeof construct.parse>
615
+
616
+ x.tCh<ConstructParsed, ExpectedParsed>()
617
+ x.tCh<ExpectedParsed, ConstructParsed>()
618
+
619
+ type StructParsed = ReturnType<typeof struct.parse>
620
+
621
+ x.tCh<StructParsed, ExpectedParsed>()
622
+ x.tCh<ExpectedParsed, StructParsed>()
623
+
624
+ type StandardParsed = Extract<
625
+ ReturnType<(typeof struct)['~standard']['validate']>,
626
+ { value: unknown }
627
+ >['value']
628
+
629
+ x.tCh<StandardParsed, ExpectedSubj>()
630
+ x.tCh<ExpectedSubj, StandardParsed>()
631
+
632
+ /* runtime schema check */
633
+
634
+ expect(struct.__schema).toStrictEqual(schema)
635
+ expect(construct.__schema).toStrictEqual(schema)
636
+ expect(construct.__schema === schema).toBe(false)
637
+
638
+ /* parse result check */
639
+
640
+ for (const subj of subjects) {
641
+ const schemaParsed = x.parse(schema, subj)
642
+
643
+ expect(schemaParsed.error).toBe(undefined)
644
+ expect(schemaParsed.data).toStrictEqual(subj)
645
+
646
+ const constructParsed = construct.parse(subj)
647
+
648
+ expect(constructParsed.error).toBe(undefined)
649
+ expect(constructParsed.data).toStrictEqual(subj)
650
+
651
+ const structParsed = struct.parse(subj)
652
+
653
+ expect(structParsed.error).toBe(undefined)
654
+ expect(structParsed.data).toStrictEqual(subj)
655
+
656
+ const standardParsed = struct['~standard'].validate(subj)
657
+
658
+ if (standardParsed instanceof Promise) {
659
+ throw Error('Not expected')
660
+ }
661
+
662
+ if (standardParsed.issues !== undefined) {
663
+ throw Error('not expected')
664
+ }
665
+
666
+ expect(standardParsed.value).toStrictEqual(subj)
667
+ }
668
+ }
669
+ })
670
+
671
+ it('optional + nullable + minLength + maxLength + branded key', () => {
672
+ const schema = {
673
+ type: 'record',
674
+ key: { type: 'string', brand: ['x', 'y'] },
675
+ of: { type: 'boolean' },
676
+ minLength: 1,
677
+ maxLength: 1,
678
+ optional: true,
679
+ nullable: true,
680
+ } as const satisfies x.Schema
681
+
682
+ const struct = x
683
+ .record(x.boolean(), x.string().brand('x', 'y'))
684
+ .minLength(1)
685
+ .maxLength(1)
686
+ .optional()
687
+ .nullable()
688
+
689
+ type ExpectedSubj = Record<string, boolean> | undefined | null
690
+
691
+ const subjects: Array<ExpectedSubj> = [{ x: true }, undefined, null]
692
+
693
+ foldA: {
694
+ const construct = x.makeStruct(schema)
695
+
696
+ /* ensure that schema/construct/struct/~standard subject types are identical */
697
+
698
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
699
+
700
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
701
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
702
+
703
+ type SchemaSubj = x.Infer<typeof schema>
704
+
705
+ x.tCh<SchemaSubj, ExpectedSubj>()
706
+ x.tCh<ExpectedSubj, SchemaSubj>()
707
+
708
+ type StructSubj = x.Infer<typeof struct.__schema>
709
+
710
+ x.tCh<StructSubj, ExpectedSubj>()
711
+ x.tCh<ExpectedSubj, StructSubj>()
712
+
713
+ type StandardSubj = NonNullable<
714
+ (typeof struct)['~standard']['types']
715
+ >['output']
716
+
717
+ x.tCh<StandardSubj, ExpectedSubj>()
718
+ x.tCh<ExpectedSubj, StandardSubj>()
719
+
720
+ /* parsed either type check */
721
+
722
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
723
+
724
+ const parsed = x.parse(schema, undefined)
725
+
726
+ type SchemaParsed = typeof parsed
727
+
728
+ x.tCh<SchemaParsed, ExpectedParsed>()
729
+ x.tCh<ExpectedParsed, SchemaParsed>()
730
+
731
+ type ConstructParsed = ReturnType<typeof construct.parse>
732
+
733
+ x.tCh<ConstructParsed, ExpectedParsed>()
734
+ x.tCh<ExpectedParsed, ConstructParsed>()
735
+
736
+ type StructParsed = ReturnType<typeof struct.parse>
737
+
738
+ x.tCh<StructParsed, ExpectedParsed>()
739
+ x.tCh<ExpectedParsed, StructParsed>()
740
+
741
+ type StandardParsed = Extract<
742
+ ReturnType<(typeof struct)['~standard']['validate']>,
743
+ { value: unknown }
744
+ >['value']
745
+
746
+ x.tCh<StandardParsed, ExpectedSubj>()
747
+ x.tCh<ExpectedSubj, StandardParsed>()
748
+
749
+ /* runtime schema check */
750
+
751
+ expect(struct.__schema).toStrictEqual(schema)
752
+ expect(construct.__schema).toStrictEqual(schema)
753
+ expect(construct.__schema === schema).toBe(false)
754
+
755
+ /* parse result check */
756
+
757
+ for (const subj of subjects) {
758
+ const schemaParsed = x.parse(schema, subj)
759
+
760
+ expect(schemaParsed.error).toBe(undefined)
761
+ expect(schemaParsed.data).toStrictEqual(subj)
762
+
763
+ const constructParsed = construct.parse(subj)
764
+
765
+ expect(constructParsed.error).toBe(undefined)
766
+ expect(constructParsed.data).toStrictEqual(subj)
767
+
768
+ const structParsed = struct.parse(subj)
769
+
770
+ expect(structParsed.error).toBe(undefined)
771
+ expect(structParsed.data).toStrictEqual(subj)
772
+
773
+ const standardParsed = struct['~standard'].validate(subj)
774
+
775
+ if (standardParsed instanceof Promise) {
776
+ throw Error('Not expected')
777
+ }
778
+
779
+ if (standardParsed.issues !== undefined) {
780
+ throw Error('not expected')
781
+ }
782
+
783
+ expect(standardParsed.value).toStrictEqual(subj)
784
+ }
785
+ }
786
+ })
787
+ })
788
+
789
+ describe('Struct parameter keys reduction and schema immutability (foldB)', () => {
790
+ it('optional', () => {
791
+ const schema = {
792
+ type: 'record',
793
+ of: { type: 'boolean' },
794
+ optional: true,
795
+ } as const satisfies x.Schema
796
+
797
+ const prevStruct = x.record(x.boolean())
798
+ const struct = prevStruct.optional()
799
+
800
+ type ExpectedKeys =
801
+ | StructSharedKeys
802
+ | 'description'
803
+ | 'nullable'
804
+ | 'minLength'
805
+ | 'maxLength'
806
+
807
+ foldB: {
808
+ const construct = x.makeStruct(schema)
809
+
810
+ /* ensure that struct keys are reduced after application */
811
+
812
+ type StructKeys = keyof typeof struct
813
+
814
+ x.tCh<StructKeys, ExpectedKeys>()
815
+ x.tCh<ExpectedKeys, StructKeys>()
816
+
817
+ type ConstructKeys = keyof typeof construct
818
+
819
+ x.tCh<ConstructKeys, ExpectedKeys>()
820
+ x.tCh<ExpectedKeys, ConstructKeys>()
821
+
822
+ /* ensure that construct/struct schema types are identical */
823
+
824
+ type ExpectedSchema = typeof schema
825
+ type StructSchema = typeof struct.__schema
826
+
827
+ x.tCh<StructSchema, ExpectedSchema>()
828
+ x.tCh<ExpectedSchema, StructSchema>()
829
+
830
+ type ConstructSchema = typeof struct.__schema
831
+
832
+ x.tCh<ConstructSchema, ExpectedSchema>()
833
+ x.tCh<ExpectedSchema, ConstructSchema>()
834
+
835
+ /* runtime schema check */
836
+
837
+ expect(struct.__schema).toStrictEqual(schema)
838
+ expect(construct.__schema).toStrictEqual(schema)
839
+ expect(construct.__schema === schema).toBe(false)
840
+
841
+ /* runtime schema parameter application immutability check */
842
+
843
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
844
+ }
845
+ })
846
+
847
+ it('optional + nullable', () => {
848
+ const schema = {
849
+ type: 'record',
850
+ of: { type: 'boolean' },
851
+ optional: true,
852
+ nullable: true,
853
+ } as const satisfies x.Schema
854
+
855
+ const prevStruct = x.record(x.boolean()).optional()
856
+ const struct = prevStruct.nullable()
857
+
858
+ type ExpectedKeys =
859
+ | StructSharedKeys
860
+ | 'description'
861
+ | 'minLength'
862
+ | 'maxLength'
863
+
864
+ foldB: {
865
+ const construct = x.makeStruct(schema)
866
+
867
+ /* ensure that struct keys are reduced after application */
868
+
869
+ type StructKeys = keyof typeof struct
870
+
871
+ x.tCh<StructKeys, ExpectedKeys>()
872
+ x.tCh<ExpectedKeys, StructKeys>()
873
+
874
+ type ConstructKeys = keyof typeof construct
875
+
876
+ x.tCh<ConstructKeys, ExpectedKeys>()
877
+ x.tCh<ExpectedKeys, ConstructKeys>()
878
+
879
+ /* ensure that construct/struct schema types are identical */
880
+
881
+ type ExpectedSchema = typeof schema
882
+ type StructSchema = typeof struct.__schema
883
+
884
+ x.tCh<StructSchema, ExpectedSchema>()
885
+ x.tCh<ExpectedSchema, StructSchema>()
886
+
887
+ type ConstructSchema = typeof struct.__schema
888
+
889
+ x.tCh<ConstructSchema, ExpectedSchema>()
890
+ x.tCh<ExpectedSchema, ConstructSchema>()
891
+
892
+ /* runtime schema check */
893
+
894
+ expect(struct.__schema).toStrictEqual(schema)
895
+ expect(construct.__schema).toStrictEqual(schema)
896
+ expect(construct.__schema === schema).toBe(false)
897
+
898
+ /* runtime schema parameter application immutability check */
899
+
900
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
901
+ }
902
+ })
903
+
904
+ it('optional + nullable + minLength', () => {
905
+ const schema = {
906
+ type: 'record',
907
+ of: { type: 'boolean' },
908
+ minLength: 1,
909
+ optional: true,
910
+ nullable: true,
911
+ } as const satisfies x.Schema
912
+
913
+ const prevStruct = x.record(x.boolean()).optional().nullable()
914
+ const struct = prevStruct.minLength(1)
915
+
916
+ type ExpectedKeys = StructSharedKeys | 'description' | 'maxLength'
917
+
918
+ foldB: {
919
+ const construct = x.makeStruct(schema)
920
+
921
+ /* ensure that struct keys are reduced after application */
922
+
923
+ type StructKeys = keyof typeof struct
924
+
925
+ x.tCh<StructKeys, ExpectedKeys>()
926
+ x.tCh<ExpectedKeys, StructKeys>()
927
+
928
+ type ConstructKeys = keyof typeof construct
929
+
930
+ x.tCh<ConstructKeys, ExpectedKeys>()
931
+ x.tCh<ExpectedKeys, ConstructKeys>()
932
+
933
+ /* ensure that construct/struct schema types are identical */
934
+
935
+ type ExpectedSchema = typeof schema
936
+ type StructSchema = typeof struct.__schema
937
+
938
+ x.tCh<StructSchema, ExpectedSchema>()
939
+ x.tCh<ExpectedSchema, StructSchema>()
940
+
941
+ type ConstructSchema = typeof struct.__schema
942
+
943
+ x.tCh<ConstructSchema, ExpectedSchema>()
944
+ x.tCh<ExpectedSchema, ConstructSchema>()
945
+
946
+ /* runtime schema check */
947
+
948
+ expect(struct.__schema).toStrictEqual(schema)
949
+ expect(construct.__schema).toStrictEqual(schema)
950
+ expect(construct.__schema === schema).toBe(false)
951
+
952
+ /* runtime schema parameter application immutability check */
953
+
954
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
955
+ }
956
+ })
957
+
958
+ it('optional + nullable + minLength + maxLength', () => {
959
+ const schema = {
960
+ type: 'record',
961
+ of: { type: 'boolean' },
962
+ minLength: 1,
963
+ maxLength: 1,
964
+ optional: true,
965
+ nullable: true,
966
+ } as const satisfies x.Schema
967
+
968
+ const prevStruct = x.record(x.boolean()).optional().nullable().minLength(1)
969
+
970
+ const struct = prevStruct.maxLength(1)
971
+
972
+ type ExpectedKeys = StructSharedKeys | 'description'
973
+
974
+ foldB: {
975
+ const construct = x.makeStruct(schema)
976
+
977
+ /* ensure that struct keys are reduced after application */
978
+
979
+ type StructKeys = keyof typeof struct
980
+
981
+ x.tCh<StructKeys, ExpectedKeys>()
982
+ x.tCh<ExpectedKeys, StructKeys>()
983
+
984
+ type ConstructKeys = keyof typeof construct
985
+
986
+ x.tCh<ConstructKeys, ExpectedKeys>()
987
+ x.tCh<ExpectedKeys, ConstructKeys>()
988
+
989
+ /* ensure that construct/struct schema types are identical */
990
+
991
+ type ExpectedSchema = typeof schema
992
+ type StructSchema = typeof struct.__schema
993
+
994
+ x.tCh<StructSchema, ExpectedSchema>()
995
+ x.tCh<ExpectedSchema, StructSchema>()
996
+
997
+ type ConstructSchema = typeof struct.__schema
998
+
999
+ x.tCh<ConstructSchema, ExpectedSchema>()
1000
+ x.tCh<ExpectedSchema, ConstructSchema>()
1001
+
1002
+ /* runtime schema check */
1003
+
1004
+ expect(struct.__schema).toStrictEqual(schema)
1005
+ expect(construct.__schema).toStrictEqual(schema)
1006
+ expect(construct.__schema === schema).toBe(false)
1007
+
1008
+ /* runtime schema parameter application immutability check */
1009
+
1010
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
1011
+ }
1012
+ })
1013
+
1014
+ it('optional + nullable + minLength + maxLength + description', () => {
1015
+ const schema = {
1016
+ type: 'record',
1017
+ of: { type: 'boolean' },
1018
+ minLength: 1,
1019
+ maxLength: 1,
1020
+ optional: true,
1021
+ nullable: true,
1022
+ description: 'x',
1023
+ } as const satisfies x.Schema
1024
+
1025
+ const prevStruct = x
1026
+ .record(x.boolean())
1027
+ .optional()
1028
+ .minLength(1)
1029
+ .maxLength(1)
1030
+ .nullable()
1031
+
1032
+ const struct = prevStruct.description('x')
1033
+
1034
+ type ExpectedKeys = StructSharedKeys
1035
+
1036
+ foldB: {
1037
+ const construct = x.makeStruct(schema)
1038
+
1039
+ /* ensure that struct keys are reduced after application */
1040
+
1041
+ type StructKeys = keyof typeof struct
1042
+
1043
+ x.tCh<StructKeys, ExpectedKeys>()
1044
+ x.tCh<ExpectedKeys, StructKeys>()
1045
+
1046
+ type ConstructKeys = keyof typeof construct
1047
+
1048
+ x.tCh<ConstructKeys, ExpectedKeys>()
1049
+ x.tCh<ExpectedKeys, ConstructKeys>()
1050
+
1051
+ /* ensure that construct/struct schema types are identical */
1052
+
1053
+ type ExpectedSchema = typeof schema
1054
+ type StructSchema = typeof struct.__schema
1055
+
1056
+ x.tCh<StructSchema, ExpectedSchema>()
1057
+ x.tCh<ExpectedSchema, StructSchema>()
1058
+
1059
+ type ConstructSchema = typeof struct.__schema
1060
+
1061
+ x.tCh<ConstructSchema, ExpectedSchema>()
1062
+ x.tCh<ExpectedSchema, ConstructSchema>()
1063
+
1064
+ /* runtime schema check */
1065
+
1066
+ expect(struct.__schema).toStrictEqual(schema)
1067
+ expect(construct.__schema).toStrictEqual(schema)
1068
+ expect(construct.__schema === schema).toBe(false)
1069
+
1070
+ /* runtime schema parameter application immutability check */
1071
+
1072
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
1073
+ }
1074
+ })
1075
+
1076
+ it('description + maxLength + minLength + nullable + optional', () => {
1077
+ const schema = {
1078
+ type: 'record',
1079
+ of: { type: 'boolean' },
1080
+ minLength: 1,
1081
+ maxLength: 1,
1082
+ optional: true,
1083
+ nullable: true,
1084
+ description: 'x',
1085
+ } as const satisfies x.Schema
1086
+
1087
+ const prevStruct = x
1088
+ .record(x.boolean())
1089
+ .minLength(1)
1090
+ .maxLength(1)
1091
+ .description(schema.description)
1092
+ .nullable()
1093
+
1094
+ const struct = prevStruct.optional()
1095
+
1096
+ type ExpectedKeys = StructSharedKeys
1097
+
1098
+ foldB: {
1099
+ const construct = x.makeStruct(schema)
1100
+
1101
+ /* ensure that struct keys are reduced after application */
1102
+
1103
+ type StructKeys = keyof typeof struct
1104
+
1105
+ x.tCh<StructKeys, ExpectedKeys>()
1106
+ x.tCh<ExpectedKeys, StructKeys>()
1107
+
1108
+ type ConstructKeys = keyof typeof construct
1109
+
1110
+ x.tCh<ConstructKeys, ExpectedKeys>()
1111
+ x.tCh<ExpectedKeys, ConstructKeys>()
1112
+
1113
+ /* ensure that construct/struct schema types are identical */
1114
+
1115
+ type ExpectedSchema = typeof schema
1116
+ type StructSchema = typeof struct.__schema
1117
+
1118
+ x.tCh<StructSchema, ExpectedSchema>()
1119
+ x.tCh<ExpectedSchema, StructSchema>()
1120
+
1121
+ type ConstructSchema = typeof struct.__schema
1122
+
1123
+ x.tCh<ConstructSchema, ExpectedSchema>()
1124
+ x.tCh<ExpectedSchema, ConstructSchema>()
1125
+
1126
+ /* runtime schema check */
1127
+
1128
+ expect(struct.__schema).toStrictEqual(schema)
1129
+ expect(construct.__schema).toStrictEqual(schema)
1130
+ expect(construct.__schema === schema).toBe(false)
1131
+
1132
+ /* runtime schema parameter application immutability check */
1133
+
1134
+ expect(prevStruct.__schema === struct.__schema).toBe(false)
1135
+ }
1136
+ })
1137
+ })
1138
+
1139
+ describe('ERROR_CODE.invalidType (foldC, foldE)', () => {
1140
+ it('iterate over fixture.DATA_TYPE', () => {
1141
+ const schema = {
1142
+ type: 'record',
1143
+ of: { type: 'boolean' },
1144
+ } as const satisfies x.Schema
1145
+
1146
+ const struct = x.record(x.boolean())
1147
+ const source = fixture.DATA_TYPE.filter(([type]) => type !== 'object')
1148
+
1149
+ foldC: {
1150
+ const construct = x.makeStruct(schema)
1151
+
1152
+ for (const [kind, types] of source) {
1153
+ if (kind === schema.type) {
1154
+ continue
1155
+ }
1156
+
1157
+ for (const subject of types) {
1158
+ const expectedError = [
1159
+ {
1160
+ code: x.ERROR_CODE.invalidType,
1161
+ schema: schema,
1162
+ subject: subject,
1163
+ path: [],
1164
+ },
1165
+ ]
1166
+
1167
+ const parsedSchema = x.parse(schema, subject)
1168
+ const parsedConstruct = construct.parse(subject)
1169
+ const parsedStruct = struct.parse(subject)
1170
+
1171
+ expect(parsedSchema.error).toStrictEqual(expectedError)
1172
+ expect(parsedConstruct.error).toStrictEqual(expectedError)
1173
+ expect(parsedStruct.error).toStrictEqual(expectedError)
1174
+
1175
+ const parsedStandard = struct['~standard'].validate(subject)
1176
+
1177
+ if (parsedStandard instanceof Promise) {
1178
+ throw Error('Not expected')
1179
+ }
1180
+
1181
+ expect(parsedStandard.issues).toStrictEqual([
1182
+ { message: x.ERROR_CODE.invalidType, path: [] },
1183
+ ])
1184
+ }
1185
+ }
1186
+ }
1187
+ })
1188
+
1189
+ it('InvalidSubject error of nested schema should have correct path/schema/subject', () => {
1190
+ const schema = {
1191
+ type: 'record',
1192
+ of: {
1193
+ type: 'array',
1194
+ of: {
1195
+ type: 'object',
1196
+ of: {
1197
+ y: { type: 'literal', of: '_' },
1198
+ },
1199
+ },
1200
+ },
1201
+ } as const satisfies x.Schema
1202
+
1203
+ const struct = x.record(x.array(x.object({ y: x.literal('_') })))
1204
+
1205
+ // prettier-ignore
1206
+ const samples: Array<[
1207
+ subj: Record<string, Array<{ y: unknown }>>,
1208
+ invalidSubj: unknown,
1209
+ invalidSubjSchema: x.Schema,
1210
+ errorPath: x.ErrorPath,
1211
+ ]
1212
+ > = [
1213
+ [{ x: [{ y: '+' }, { y: '_' }, { y: '_' }] }, '+', schema.of.of.of.y, ['x', 0, 'y']],
1214
+ [{ y: [{ y: '_' }, { y: '+' }, { y: '_' }] }, '+', schema.of.of.of.y, ['y', 1, 'y']],
1215
+ [{ z: [{ y: '_' }, { y: '_' }, { y: '+' }] }, '+', schema.of.of.of.y, ['z', 2, 'y']],
1216
+ ]
1217
+
1218
+ foldE: {
1219
+ const construct = x.makeStruct(schema)
1220
+
1221
+ for (const [subject, invalidSubj, invalidSubjSchema, path] of samples) {
1222
+ const expectedError = [
1223
+ {
1224
+ path,
1225
+ code: x.ERROR_CODE.invalidType,
1226
+ schema: invalidSubjSchema,
1227
+ subject: invalidSubj,
1228
+ },
1229
+ ]
1230
+
1231
+ const parsedSchema = x.parse(schema, subject)
1232
+ const parsedConstruct = construct.parse(subject)
1233
+ const parsedStruct = struct.parse(subject)
1234
+
1235
+ expect(parsedSchema.error).toStrictEqual(expectedError)
1236
+ expect(parsedConstruct.error).toStrictEqual(expectedError)
1237
+ expect(parsedStruct.error).toStrictEqual(expectedError)
1238
+
1239
+ const parsedStandard = struct['~standard'].validate(subject)
1240
+
1241
+ if (parsedStandard instanceof Promise) {
1242
+ throw Error('Not expected')
1243
+ }
1244
+
1245
+ expect(parsedStandard.issues).toStrictEqual([
1246
+ { path, message: x.ERROR_CODE.invalidType },
1247
+ ])
1248
+ }
1249
+ }
1250
+ })
1251
+
1252
+ it('Multiple errors', () => {
1253
+ const schema = {
1254
+ type: 'record',
1255
+ of: { type: 'boolean' },
1256
+ } as const satisfies x.Schema
1257
+
1258
+ const subject = { x: true, y: '', z: 0 }
1259
+
1260
+ const parsed = x.parse(schema, subject)
1261
+ const expectedError: x.InvalidSubject[] = [
1262
+ {
1263
+ code: x.ERROR_CODE.invalidType,
1264
+ path: ['y'],
1265
+ subject: subject.y,
1266
+ schema: schema.of,
1267
+ },
1268
+ {
1269
+ code: x.ERROR_CODE.invalidType,
1270
+ path: ['z'],
1271
+ subject: subject.z,
1272
+ schema: schema.of,
1273
+ },
1274
+ ]
1275
+
1276
+ expect(parsed.error).toStrictEqual(expectedError)
1277
+ })
1278
+ })
1279
+
1280
+ describe('ERROR_CODE.invalidRange (foldD)', () => {
1281
+ it('minLength', () => {
1282
+ const schema = {
1283
+ type: 'record',
1284
+ of: { type: 'boolean' },
1285
+ minLength: 3,
1286
+ } as const satisfies x.Schema
1287
+
1288
+ const struct = x.record(x.boolean()).minLength(schema.minLength)
1289
+ const subjects = [
1290
+ {},
1291
+ { 0: true },
1292
+ { 0: true, 1: false },
1293
+ { 0: true, 1: false, 2: undefined },
1294
+ ]
1295
+
1296
+ foldD: {
1297
+ const construct = x.makeStruct(schema)
1298
+
1299
+ for (const subject of subjects) {
1300
+ const expectedError = [
1301
+ {
1302
+ code: x.ERROR_CODE.invalidRange,
1303
+ schema: schema,
1304
+ subject: subject,
1305
+ path: [],
1306
+ },
1307
+ ]
1308
+
1309
+ const parsedSchema = x.parse(schema, subject)
1310
+ const parsedConstruct = construct.parse(subject)
1311
+ const parsedStruct = struct.parse(subject)
1312
+
1313
+ expect(parsedSchema.error).toStrictEqual(expectedError)
1314
+ expect(parsedConstruct.error).toStrictEqual(expectedError)
1315
+ expect(parsedStruct.error).toStrictEqual(expectedError)
1316
+
1317
+ const parsedStandard = struct['~standard'].validate(subject)
1318
+
1319
+ if (parsedStandard instanceof Promise) {
1320
+ throw Error('Not expected')
1321
+ }
1322
+
1323
+ expect(parsedStandard.issues).toStrictEqual([
1324
+ { message: x.ERROR_CODE.invalidRange, path: [] },
1325
+ ])
1326
+ }
1327
+ }
1328
+ })
1329
+
1330
+ it('maxLength', () => {
1331
+ const schema = {
1332
+ type: 'record',
1333
+ of: { type: 'boolean' },
1334
+ maxLength: 1,
1335
+ } as const satisfies x.Schema
1336
+
1337
+ const struct = x.record(x.boolean()).maxLength(schema.maxLength)
1338
+ const subjects = [
1339
+ { 0: false, 1: true },
1340
+ { 0: false, 1: true, 2: false },
1341
+ { 0: false, 1: true, 2: false, 3: true },
1342
+ ]
1343
+
1344
+ foldD: {
1345
+ const construct = x.makeStruct(schema)
1346
+
1347
+ for (const subject of subjects) {
1348
+ const expectedError = [
1349
+ {
1350
+ code: x.ERROR_CODE.invalidRange,
1351
+ schema: schema,
1352
+ subject: subject,
1353
+ path: [],
1354
+ },
1355
+ ]
1356
+
1357
+ const parsedSchema = x.parse(schema, subject)
1358
+ const parsedConstruct = construct.parse(subject)
1359
+ const parsedStruct = struct.parse(subject)
1360
+
1361
+ expect(parsedSchema.error).toStrictEqual(expectedError)
1362
+ expect(parsedConstruct.error).toStrictEqual(expectedError)
1363
+ expect(parsedStruct.error).toStrictEqual(expectedError)
1364
+
1365
+ const parsedStandard = struct['~standard'].validate(subject)
1366
+
1367
+ if (parsedStandard instanceof Promise) {
1368
+ throw Error('Not expected')
1369
+ }
1370
+
1371
+ expect(parsedStandard.issues).toStrictEqual([
1372
+ { message: x.ERROR_CODE.invalidRange, path: [] },
1373
+ ])
1374
+ }
1375
+ }
1376
+ })
1377
+
1378
+ it('minLength + maxLength', () => {
1379
+ const schema = {
1380
+ type: 'array',
1381
+ of: { type: 'boolean' },
1382
+ maxLength: 2,
1383
+ minLength: 2,
1384
+ } as const satisfies x.Schema
1385
+
1386
+ const struct = x
1387
+ .array(x.boolean())
1388
+ .minLength(schema.minLength)
1389
+ .maxLength(schema.maxLength)
1390
+
1391
+ const subjects = [[], [false], [false, true, false]]
1392
+
1393
+ foldD: {
1394
+ const construct = x.makeStruct(schema)
1395
+
1396
+ for (const subject of subjects) {
1397
+ const expectedError = [
1398
+ {
1399
+ code: x.ERROR_CODE.invalidRange,
1400
+ schema: schema,
1401
+ subject: subject,
1402
+ path: [],
1403
+ },
1404
+ ]
1405
+
1406
+ const parsedSchema = x.parse(schema, subject)
1407
+ const parsedConstruct = construct.parse(subject)
1408
+ const parsedStruct = struct.parse(subject)
1409
+
1410
+ expect(parsedSchema.error).toStrictEqual(expectedError)
1411
+ expect(parsedConstruct.error).toStrictEqual(expectedError)
1412
+ expect(parsedStruct.error).toStrictEqual(expectedError)
1413
+
1414
+ const parsedStandard = struct['~standard'].validate(subject)
1415
+
1416
+ if (parsedStandard instanceof Promise) {
1417
+ throw Error('Not expected')
1418
+ }
1419
+
1420
+ expect(parsedStandard.issues).toStrictEqual([
1421
+ { message: x.ERROR_CODE.invalidRange, path: [] },
1422
+ ])
1423
+ }
1424
+ }
1425
+ })
1426
+ })
1427
+
1428
+ describe('Compound schema specifics (foldA)', () => {
1429
+ it('nested primitive schema: optional + nullable + brand', () => {
1430
+ const schema = {
1431
+ type: 'record',
1432
+ of: {
1433
+ type: 'boolean',
1434
+ optional: true,
1435
+ nullable: true,
1436
+ brand: ['x', 'y'],
1437
+ },
1438
+ } as const satisfies x.Schema
1439
+
1440
+ const struct = x.record(
1441
+ x
1442
+ .boolean()
1443
+ .optional()
1444
+ .nullable()
1445
+ .brand(...schema.of.brand)
1446
+ )
1447
+
1448
+ type Branded = boolean & { __x: 'y' }
1449
+
1450
+ // TODO: expected that `{ x?: Branded | undefined | null }` will not be
1451
+ // equivalent to `Record<Branded | undefined | null>`
1452
+ //
1453
+ // either `tCh` has a flaw or typescript itself, need to investigate this later
1454
+ //
1455
+ type ExpectedSubj = { x?: Branded | undefined | null }
1456
+
1457
+ const subjects = [
1458
+ {},
1459
+ { x: null },
1460
+ { x: true as Branded },
1461
+ { x: false as Branded },
1462
+ ] as const satisfies Array<ExpectedSubj>
1463
+
1464
+ foldA: {
1465
+ const construct = x.makeStruct(schema)
1466
+
1467
+ /* ensure that schema/construct/struct/~standard subject types are identical */
1468
+
1469
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
1470
+
1471
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
1472
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
1473
+
1474
+ type SchemaSubj = x.Infer<typeof schema>
1475
+
1476
+ x.tCh<SchemaSubj, ExpectedSubj>()
1477
+ x.tCh<ExpectedSubj, SchemaSubj>()
1478
+
1479
+ type StructSubj = x.Infer<typeof struct.__schema>
1480
+
1481
+ x.tCh<StructSubj, ExpectedSubj>()
1482
+ x.tCh<ExpectedSubj, StructSubj>()
1483
+
1484
+ type StandardSubj = NonNullable<
1485
+ (typeof struct)['~standard']['types']
1486
+ >['output']
1487
+
1488
+ x.tCh<StandardSubj, ExpectedSubj>()
1489
+ x.tCh<ExpectedSubj, StandardSubj>()
1490
+
1491
+ /* parsed either type check */
1492
+
1493
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
1494
+
1495
+ const parsed = x.parse(schema, undefined)
1496
+
1497
+ type SchemaParsed = typeof parsed
1498
+
1499
+ x.tCh<SchemaParsed, ExpectedParsed>()
1500
+ x.tCh<ExpectedParsed, SchemaParsed>()
1501
+
1502
+ type ConstructParsed = ReturnType<typeof construct.parse>
1503
+
1504
+ x.tCh<ConstructParsed, ExpectedParsed>()
1505
+ x.tCh<ExpectedParsed, ConstructParsed>()
1506
+
1507
+ type StructParsed = ReturnType<typeof struct.parse>
1508
+
1509
+ x.tCh<StructParsed, ExpectedParsed>()
1510
+ x.tCh<ExpectedParsed, StructParsed>()
1511
+
1512
+ type StandardParsed = Extract<
1513
+ ReturnType<(typeof struct)['~standard']['validate']>,
1514
+ { value: unknown }
1515
+ >['value']
1516
+
1517
+ x.tCh<StandardParsed, ExpectedSubj>()
1518
+ x.tCh<ExpectedSubj, StandardParsed>()
1519
+
1520
+ /* runtime schema check */
1521
+
1522
+ expect(struct.__schema).toStrictEqual(schema)
1523
+ expect(construct.__schema).toStrictEqual(schema)
1524
+ expect(construct.__schema === schema).toBe(false)
1525
+
1526
+ /* parse result check */
1527
+
1528
+ for (const subj of subjects) {
1529
+ const schemaParsed = x.parse(schema, subj)
1530
+
1531
+ expect(schemaParsed.error).toBe(undefined)
1532
+ expect(schemaParsed.data).toStrictEqual(subj)
1533
+
1534
+ const constructParsed = construct.parse(subj)
1535
+
1536
+ expect(constructParsed.error).toBe(undefined)
1537
+ expect(constructParsed.data).toStrictEqual(subj)
1538
+
1539
+ const structParsed = struct.parse(subj)
1540
+
1541
+ expect(structParsed.error).toBe(undefined)
1542
+ expect(structParsed.data).toStrictEqual(subj)
1543
+
1544
+ const standardParsed = struct['~standard'].validate(subj)
1545
+
1546
+ if (standardParsed instanceof Promise) {
1547
+ throw Error('Not expected')
1548
+ }
1549
+
1550
+ if (standardParsed.issues !== undefined) {
1551
+ throw Error('not expected')
1552
+ }
1553
+
1554
+ expect(standardParsed.value).toStrictEqual(subj)
1555
+ }
1556
+ }
1557
+ })
1558
+
1559
+ /**
1560
+ * We ensure that when a developer makes `Object.keys` on a record,
1561
+ * they are, in fact, keys of existing entities within its record.
1562
+ **/
1563
+ it('parsed record should not keep if its value is undefined', () => {
1564
+ const struct = x.record(x.boolean().optional())
1565
+ const parsed = struct.parse({ x: undefined })
1566
+
1567
+ expect(parsed.data).toStrictEqual({})
1568
+ })
1569
+
1570
+ it('nested compound schema: optional + nullable', () => {
1571
+ const schema = {
1572
+ type: 'record',
1573
+ of: {
1574
+ type: 'array',
1575
+ of: { type: 'boolean' },
1576
+ optional: true,
1577
+ nullable: true,
1578
+ },
1579
+ } as const satisfies x.Schema
1580
+
1581
+ const struct = x.record(x.array(x.boolean()).optional().nullable())
1582
+
1583
+ type ExpectedSubj = { x?: Array<boolean> | undefined | null }
1584
+
1585
+ const subjects = [
1586
+ { x: [] },
1587
+ { x: null },
1588
+ { x: [] },
1589
+ { x: [true] },
1590
+ ] as const satisfies Array<ExpectedSubj>
1591
+
1592
+ foldA: {
1593
+ const construct = x.makeStruct(schema)
1594
+
1595
+ /* ensure that schema/construct/struct/~standard subject types are identical */
1596
+
1597
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
1598
+
1599
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
1600
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
1601
+
1602
+ type SchemaSubj = x.Infer<typeof schema>
1603
+
1604
+ x.tCh<SchemaSubj, ExpectedSubj>()
1605
+ x.tCh<ExpectedSubj, SchemaSubj>()
1606
+
1607
+ type StructSubj = x.Infer<typeof struct.__schema>
1608
+
1609
+ x.tCh<StructSubj, ExpectedSubj>()
1610
+ x.tCh<ExpectedSubj, StructSubj>()
1611
+
1612
+ type StandardSubj = NonNullable<
1613
+ (typeof struct)['~standard']['types']
1614
+ >['output']
1615
+
1616
+ x.tCh<StandardSubj, ExpectedSubj>()
1617
+ x.tCh<ExpectedSubj, StandardSubj>()
1618
+
1619
+ /* parsed either type check */
1620
+
1621
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
1622
+
1623
+ const parsed = x.parse(schema, undefined)
1624
+
1625
+ type SchemaParsed = typeof parsed
1626
+
1627
+ x.tCh<SchemaParsed, ExpectedParsed>()
1628
+ x.tCh<ExpectedParsed, SchemaParsed>()
1629
+
1630
+ type ConstructParsed = ReturnType<typeof construct.parse>
1631
+
1632
+ x.tCh<ConstructParsed, ExpectedParsed>()
1633
+ x.tCh<ExpectedParsed, ConstructParsed>()
1634
+
1635
+ type StructParsed = ReturnType<typeof struct.parse>
1636
+
1637
+ x.tCh<StructParsed, ExpectedParsed>()
1638
+ x.tCh<ExpectedParsed, StructParsed>()
1639
+
1640
+ type StandardParsed = Extract<
1641
+ ReturnType<(typeof struct)['~standard']['validate']>,
1642
+ { value: unknown }
1643
+ >['value']
1644
+
1645
+ x.tCh<StandardParsed, ExpectedSubj>()
1646
+ x.tCh<ExpectedSubj, StandardParsed>()
1647
+
1648
+ /* runtime schema check */
1649
+
1650
+ expect(struct.__schema).toStrictEqual(schema)
1651
+ expect(construct.__schema).toStrictEqual(schema)
1652
+ expect(construct.__schema === schema).toBe(false)
1653
+
1654
+ /* parse result check */
1655
+
1656
+ for (const subj of subjects) {
1657
+ const schemaParsed = x.parse(schema, subj)
1658
+
1659
+ expect(schemaParsed.error).toBe(undefined)
1660
+ expect(schemaParsed.data).toStrictEqual(subj)
1661
+
1662
+ const constructParsed = construct.parse(subj)
1663
+
1664
+ expect(constructParsed.error).toBe(undefined)
1665
+ expect(constructParsed.data).toStrictEqual(subj)
1666
+
1667
+ const structParsed = struct.parse(subj)
1668
+
1669
+ expect(structParsed.error).toBe(undefined)
1670
+ expect(structParsed.data).toStrictEqual(subj)
1671
+
1672
+ const standardParsed = struct['~standard'].validate(subj)
1673
+
1674
+ if (standardParsed instanceof Promise) {
1675
+ throw Error('Not expected')
1676
+ }
1677
+
1678
+ if (standardParsed.issues !== undefined) {
1679
+ throw Error('not expected')
1680
+ }
1681
+
1682
+ expect(standardParsed.value).toStrictEqual(subj)
1683
+ }
1684
+ }
1685
+ })
1686
+
1687
+ it('nested by itself (schema depth: 4)', () => {
1688
+ const schema = {
1689
+ type: 'record',
1690
+ of: {
1691
+ type: 'record',
1692
+ of: {
1693
+ type: 'record',
1694
+ of: { type: 'boolean' },
1695
+ },
1696
+ },
1697
+ } as const satisfies x.Schema
1698
+
1699
+ const struct = x.record(x.record(x.record(x.boolean())))
1700
+
1701
+ type ExpectedSubj = Record<string, Record<string, Record<string, boolean>>>
1702
+
1703
+ const subjects = [
1704
+ { x: { y: { z: true } } },
1705
+ { x: { y: { z: false } } },
1706
+ ] as const satisfies Array<ExpectedSubj>
1707
+
1708
+ foldA: {
1709
+ const construct = x.makeStruct(schema)
1710
+
1711
+ /* ensure that schema/construct/struct/~standard subject types are identical */
1712
+
1713
+ type ConstructSchemaSubj = x.Infer<typeof construct.__schema>
1714
+
1715
+ x.tCh<ConstructSchemaSubj, ExpectedSubj>()
1716
+ x.tCh<ExpectedSubj, ConstructSchemaSubj>()
1717
+
1718
+ type SchemaSubj = x.Infer<typeof schema>
1719
+
1720
+ x.tCh<SchemaSubj, ExpectedSubj>()
1721
+ x.tCh<ExpectedSubj, SchemaSubj>()
1722
+
1723
+ type StructSubj = x.Infer<typeof struct.__schema>
1724
+
1725
+ x.tCh<StructSubj, ExpectedSubj>()
1726
+ x.tCh<ExpectedSubj, StructSubj>()
1727
+
1728
+ type StandardSubj = NonNullable<
1729
+ (typeof struct)['~standard']['types']
1730
+ >['output']
1731
+
1732
+ x.tCh<StandardSubj, ExpectedSubj>()
1733
+ x.tCh<ExpectedSubj, StandardSubj>()
1734
+
1735
+ /* parsed either type check */
1736
+
1737
+ type ExpectedParsed = x.ParseResult<ExpectedSubj>
1738
+
1739
+ const parsed = x.parse(schema, undefined)
1740
+
1741
+ type SchemaParsed = typeof parsed
1742
+
1743
+ x.tCh<SchemaParsed, ExpectedParsed>()
1744
+ x.tCh<ExpectedParsed, SchemaParsed>()
1745
+
1746
+ type ConstructParsed = ReturnType<typeof construct.parse>
1747
+
1748
+ x.tCh<ConstructParsed, ExpectedParsed>()
1749
+ x.tCh<ExpectedParsed, ConstructParsed>()
1750
+
1751
+ type StructParsed = ReturnType<typeof struct.parse>
1752
+
1753
+ x.tCh<StructParsed, ExpectedParsed>()
1754
+ x.tCh<ExpectedParsed, StructParsed>()
1755
+
1756
+ type StandardParsed = Extract<
1757
+ ReturnType<(typeof struct)['~standard']['validate']>,
1758
+ { value: unknown }
1759
+ >['value']
1760
+
1761
+ x.tCh<StandardParsed, ExpectedSubj>()
1762
+ x.tCh<ExpectedSubj, StandardParsed>()
1763
+
1764
+ /* runtime schema check */
1765
+
1766
+ expect(struct.__schema).toStrictEqual(schema)
1767
+ expect(construct.__schema).toStrictEqual(schema)
1768
+ expect(construct.__schema === schema).toBe(false)
1769
+
1770
+ /* parse result check */
1771
+
1772
+ for (const subj of subjects) {
1773
+ const schemaParsed = x.parse(schema, subj)
1774
+
1775
+ expect(schemaParsed.error).toBe(undefined)
1776
+ expect(schemaParsed.data).toStrictEqual(subj)
1777
+
1778
+ const constructParsed = construct.parse(subj)
1779
+
1780
+ expect(constructParsed.error).toBe(undefined)
1781
+ expect(constructParsed.data).toStrictEqual(subj)
1782
+
1783
+ const structParsed = struct.parse(subj)
1784
+
1785
+ expect(structParsed.error).toBe(undefined)
1786
+ expect(structParsed.data).toStrictEqual(subj)
1787
+
1788
+ const standardParsed = struct['~standard'].validate(subj)
1789
+
1790
+ if (standardParsed instanceof Promise) {
1791
+ throw Error('Not expected')
1792
+ }
1793
+
1794
+ if (standardParsed.issues !== undefined) {
1795
+ throw Error('not expected')
1796
+ }
1797
+
1798
+ expect(standardParsed.value).toStrictEqual(subj)
1799
+ }
1800
+ }
1801
+ })
1802
+ })