1import { assert, describe, it } from "@effect/vitest"
2import { Formatter, Schema, type SchemaAST, SchemaRepresentation } from "effect"
3import { throws } from "../../utils/assert.ts"
4
5function assertFilterReviver<T>(input: {
6 readonly schema: Schema.Codec<T>
7 readonly id: string
8 readonly payload: Schema.Json
9 readonly schemas?: ReadonlyArray<SchemaAST.AST>
10 readonly reviver: SchemaRepresentation.FilterReviver<any>
11 readonly dependencies?: ReadonlyArray<SchemaRepresentation.AnyReviver>
12 readonly valid: unknown
13 readonly invalid: unknown
14 readonly hasToJsonSchema?: boolean
15}): void {
16 const check = input.schema.ast.checks?.at(-1)
17 assert.isDefined(check)
18 assert.strictEqual(check._tag, "Filter")
19 if (check._tag !== "Filter") return
20 assert.deepStrictEqual(check.annotations?.representation, {
21 id: input.id,
22 payload: input.payload,
23 ...(input.schemas === undefined ? undefined : { schemas: input.schemas })
24 })
25
26 const document = SchemaRepresentation.toRepresentation(input.schema.ast)
27 const json = SchemaRepresentation.toJson(document)
28 const revived = SchemaRepresentation.fromRepresentation(SchemaRepresentation.fromJson(json), {
29 revivers: [input.reviver, ...(input.dependencies ?? [])]
30 }) as Schema.Codec<unknown>
31
32 assert.strictEqual(Schema.decodeUnknownResult(revived)(input.valid)._tag, "Success")
33 assert.strictEqual(Schema.decodeUnknownResult(revived)(input.invalid)._tag, "Failure")
34 assert.deepStrictEqual(
35 SchemaRepresentation.toJson(SchemaRepresentation.toRepresentation(revived.ast)),
36 json
37 )
38 const revivedCheck = revived.ast.checks?.at(-1)
39 assert.isDefined(revivedCheck)
40 assert.strictEqual(revivedCheck._tag, "Filter")
41 if (revivedCheck._tag !== "Filter") return
42 assert.strictEqual(revivedCheck.annotations?.representation?.id, input.id)
43 assert.strictEqual(
44 typeof revivedCheck.annotations?.toJsonSchema,
45 input.hasToJsonSchema === false ? "undefined" : "function"
46 )
47 assert.strictEqual(typeof revivedCheck.annotations?.toCode, "function")
48}
49
50function assertDeclarationReviver(input: {
51 readonly schema: Schema.Top
52 readonly id: string
53 readonly payload: Schema.Json
54 readonly reviver: SchemaRepresentation.DeclarationReviver<any>
55 readonly dependencies?: ReadonlyArray<SchemaRepresentation.AnyReviver>
56}): void {
57 const representation = input.schema.ast.annotations?.representation
58 assert.deepStrictEqual(representation, {
59 id: input.id,
60 payload: input.payload
61 })
62
63 const document = SchemaRepresentation.toRepresentation(input.schema.ast)
64 const json = SchemaRepresentation.toJson(document)
65 const revived = SchemaRepresentation.fromRepresentation(SchemaRepresentation.fromJson(json), {
66 revivers: [input.reviver, ...(input.dependencies ?? [])]
67 })
68
69 assert.deepStrictEqual(
70 SchemaRepresentation.toJson(SchemaRepresentation.toRepresentation(revived.ast)),
71 json
72 )
73 assert.strictEqual(
74 (revived.ast.annotations?.representation as { readonly id?: string } | undefined)?.id,
75 input.id
76 )
77 assert.strictEqual(typeof revived.ast.annotations?.toCode, "function")
78}
79
80describe("SchemaRepresentation built-in string revivers", () => {
81 it("revives isStringFinite", () => {
82 assertFilterReviver({
83 schema: Schema.String.check(Schema.isStringFinite()),
84 id: "effect/schema/isStringFinite",
85 payload: null,
86 reviver: SchemaRepresentation.isStringFiniteReviver,
87 valid: "1.5",
88 invalid: "Infinity"
89 })
90 })
91
92 it("revives isStringBigInt", () => {
93 assertFilterReviver({
94 schema: Schema.String.check(Schema.isStringBigInt()),
95 id: "effect/schema/isStringBigInt",
96 payload: null,
97 reviver: SchemaRepresentation.isStringBigIntReviver,
98 valid: "-10",
99 invalid: "1.5"
100 })
101 })
102
103 it("revives isStringSymbol", () => {
104 assertFilterReviver({
105 schema: Schema.String.check(Schema.isStringSymbol()),
106 id: "effect/schema/isStringSymbol",
107 payload: null,
108 reviver: SchemaRepresentation.isStringSymbolReviver,
109 valid: "Symbol(shared)",
110 invalid: "shared"
111 })
112 })
113
114 it("revives isMinLength", () => {
115 assertFilterReviver({
116 schema: Schema.String.check(Schema.isMinLength(1.8)),
117 id: "effect/schema/isMinLength",
118 payload: { minLength: 1 },
119 reviver: SchemaRepresentation.isMinLengthReviver,
120 valid: "a",
121 invalid: ""
122 })
123 })
124
125 it("revives isMaxLength", () => {
126 assertFilterReviver({
127 schema: Schema.String.check(Schema.isMaxLength(3.8)),
128 id: "effect/schema/isMaxLength",
129 payload: { maxLength: 3 },
130 reviver: SchemaRepresentation.isMaxLengthReviver,
131 valid: "abc",
132 invalid: "abcd"
133 })
134 })
135
136 it("revives isBetweenLength", () => {
137 assertFilterReviver({
138 schema: Schema.String.check(Schema.isBetweenLength(1.8, 3.8)),
139 id: "effect/schema/isBetweenLength",
140 payload: { minimum: 1, maximum: 3 },
141 reviver: SchemaRepresentation.isBetweenLengthReviver,
142 valid: "ab",
143 invalid: ""
144 })
145 })
146
147 it("revives isPattern", () => {
148 assertFilterReviver({
149 schema: Schema.String.check(Schema.isPattern(/^a+$/i)),
150 id: "effect/schema/isPattern",
151 payload: { source: "^a+$", flags: "i" },
152 reviver: SchemaRepresentation.isPatternReviver,
153 valid: "AAA",
154 invalid: "bbb"
155 })
156 })
157
158 it("revives isMinCodePoints", () => {
159 assertFilterReviver({
160 schema: Schema.String.check(Schema.isMinCodePoints(2.8)),
161 id: "effect/schema/isMinCodePoints",
162 payload: { minCodePoints: 2 },
163 reviver: SchemaRepresentation.isMinCodePointsReviver,
164 valid: "😀a",
165 invalid: "😀"
166 })
167 })
168
169 it("revives isMaxCodePoints", () => {
170 assertFilterReviver({
171 schema: Schema.String.check(Schema.isMaxCodePoints(1.8)),
172 id: "effect/schema/isMaxCodePoints",
173 payload: { maxCodePoints: 1 },
174 reviver: SchemaRepresentation.isMaxCodePointsReviver,
175 valid: "😀",
176 invalid: "😀a"
177 })
178 })
179
180 it("revives isBetweenCodePoints", () => {
181 assertFilterReviver({
182 schema: Schema.String.check(Schema.isBetweenCodePoints(1.8, 1.8)),
183 id: "effect/schema/isBetweenCodePoints",
184 payload: { minimum: 1, maximum: 1 },
185 reviver: SchemaRepresentation.isBetweenCodePointsReviver,
186 valid: "😀",
187 invalid: "😀a"
188 })
189 })
190
191 it("revives isTrimmed", () => {
192 assertFilterReviver({
193 schema: Schema.String.check(Schema.isTrimmed()),
194 id: "effect/schema/isTrimmed",
195 payload: null,
196 reviver: SchemaRepresentation.isTrimmedReviver,
197 valid: "text",
198 invalid: " text "
199 })
200 })
201
202 it("revives isUUID", () => {
203 assertFilterReviver({
204 schema: Schema.String.check(Schema.isUUID(4)),
205 id: "effect/schema/isUUID",
206 payload: { version: 4 },
207 reviver: SchemaRepresentation.isUUIDReviver,
208 valid: "123e4567-e89b-42d3-a456-426614174000",
209 invalid: "123e4567-e89b-12d3-a456-426614174000"
210 })
211 })
212
213 it("revives isGUID", () => {
214 assertFilterReviver({
215 schema: Schema.String.check(Schema.isGUID()),
216 id: "effect/schema/isGUID",
217 payload: null,
218 reviver: SchemaRepresentation.isGUIDReviver,
219 valid: "123e4567-e89b-12d3-a456-426614174000",
220 invalid: "not-a-guid"
221 })
222 })
223
224 it("revives isULID", () => {
225 assertFilterReviver({
226 schema: Schema.String.check(Schema.isULID()),
227 id: "effect/schema/isULID",
228 payload: null,
229 reviver: SchemaRepresentation.isULIDReviver,
230 valid: "01ARZ3NDEKTSV4RRFFQ69G5FAV",
231 invalid: "not-a-ulid"
232 })
233 })
234
235 it("revives isBase64", () => {
236 assertFilterReviver({
237 schema: Schema.String.check(Schema.isBase64()),
238 id: "effect/schema/isBase64",
239 payload: null,
240 reviver: SchemaRepresentation.isBase64Reviver,
241 valid: "YQ==",
242 invalid: "?"
243 })
244 })
245
246 it("revives isBase64Url", () => {
247 assertFilterReviver({
248 schema: Schema.String.check(Schema.isBase64Url()),
249 id: "effect/schema/isBase64Url",
250 payload: null,
251 reviver: SchemaRepresentation.isBase64UrlReviver,
252 valid: "YQ",
253 invalid: "?"
254 })
255 })
256
257 it("revives isStartingWith", () => {
258 assertFilterReviver({
259 schema: Schema.String.check(Schema.isStartingWith("pre")),
260 id: "effect/schema/isStartingWith",
261 payload: { startsWith: "pre" },
262 reviver: SchemaRepresentation.isStartingWithReviver,
263 valid: "prefix",
264 invalid: "suffix"
265 })
266 })
267
268 it("revives isEndingWith", () => {
269 assertFilterReviver({
270 schema: Schema.String.check(Schema.isEndingWith("end")),
271 id: "effect/schema/isEndingWith",
272 payload: { endsWith: "end" },
273 reviver: SchemaRepresentation.isEndingWithReviver,
274 valid: "weekend",
275 invalid: "ending"
276 })
277 })
278
279 it("revives isIncluding", () => {
280 assertFilterReviver({
281 schema: Schema.String.check(Schema.isIncluding("mid")),
282 id: "effect/schema/isIncluding",
283 payload: { includes: "mid" },
284 reviver: SchemaRepresentation.isIncludingReviver,
285 valid: "middle",
286 invalid: "outside"
287 })
288 })
289
290 it("revives isUppercased", () => {
291 assertFilterReviver({
292 schema: Schema.String.check(Schema.isUppercased()),
293 id: "effect/schema/isUppercased",
294 payload: null,
295 reviver: SchemaRepresentation.isUppercasedReviver,
296 valid: "ABC1",
297 invalid: "Abc"
298 })
299 })
300
301 it("revives isLowercased", () => {
302 assertFilterReviver({
303 schema: Schema.String.check(Schema.isLowercased()),
304 id: "effect/schema/isLowercased",
305 payload: null,
306 reviver: SchemaRepresentation.isLowercasedReviver,
307 valid: "abc1",
308 invalid: "Abc"
309 })
310 })
311
312 it("revives isCapitalized", () => {
313 assertFilterReviver({
314 schema: Schema.String.check(Schema.isCapitalized()),
315 id: "effect/schema/isCapitalized",
316 payload: null,
317 reviver: SchemaRepresentation.isCapitalizedReviver,
318 valid: "Hello",
319 invalid: "hello"
320 })
321 })
322
323 it("revives isUncapitalized", () => {
324 assertFilterReviver({
325 schema: Schema.String.check(Schema.isUncapitalized()),
326 id: "effect/schema/isUncapitalized",
327 payload: null,
328 reviver: SchemaRepresentation.isUncapitalizedReviver,
329 valid: "hello",
330 invalid: "Hello"
331 })
332 })
333})
334
335function expectInvalidPayload(json: Schema.Json, reviver: SchemaRepresentation.AnyReviver): void {
336 const path = Formatter.formatPath([
337 "representation",
338 "checks",
339 0,
340 "representation",
341 "payload"
342 ])
343 throws(
344 () => SchemaRepresentation.fromRepresentation(SchemaRepresentation.fromJson(json), { revivers: [reviver] }),
345 `Invalid representation payload for ${reviver.id}\n at ${path}`
346 )
347}
348
349describe("SchemaRepresentation built-in number revivers", () => {
350 it("revives isFinite", () => {
351 assertFilterReviver({
352 schema: Schema.Number.check(Schema.isFinite()),
353 id: "effect/schema/isFinite",
354 payload: null,
355 reviver: SchemaRepresentation.isFiniteReviver,
356 valid: 1,
357 invalid: Number.POSITIVE_INFINITY
358 })
359 })
360
361 it("revives isInt", () => {
362 assertFilterReviver({
363 schema: Schema.Number.check(Schema.isInt()),
364 id: "effect/schema/isInt",
365 payload: null,
366 reviver: SchemaRepresentation.isIntReviver,
367 valid: 1,
368 invalid: 1.5
369 })
370 })
371
372 it("revives isMultipleOf", () => {
373 assertFilterReviver({
374 schema: Schema.Number.check(Schema.isMultipleOf(3)),
375 id: "effect/schema/isMultipleOf",
376 payload: { divisor: 3 },
377 reviver: SchemaRepresentation.isMultipleOfReviver,
378 valid: 6,
379 invalid: 7
380 })
381 })
382
383 it("revives isGreaterThan", () => {
384 assertFilterReviver({
385 schema: Schema.Number.check(Schema.isGreaterThan(1)),
386 id: "effect/schema/isGreaterThan",
387 payload: { exclusiveMinimum: 1 },
388 reviver: SchemaRepresentation.isGreaterThanReviver,
389 valid: 2,
390 invalid: 1
391 })
392 })
393
394 it("revives isGreaterThanOrEqualTo", () => {
395 assertFilterReviver({
396 schema: Schema.Number.check(Schema.isGreaterThanOrEqualTo(1)),
397 id: "effect/schema/isGreaterThanOrEqualTo",
398 payload: { minimum: 1 },
399 reviver: SchemaRepresentation.isGreaterThanOrEqualToReviver,
400 valid: 1,
401 invalid: 0
402 })
403 })
404
405 it("revives isLessThan", () => {
406 assertFilterReviver({
407 schema: Schema.Number.check(Schema.isLessThan(2)),
408 id: "effect/schema/isLessThan",
409 payload: { exclusiveMaximum: 2 },
410 reviver: SchemaRepresentation.isLessThanReviver,
411 valid: 1,
412 invalid: 2
413 })
414 })
415
416 it("revives isLessThanOrEqualTo", () => {
417 assertFilterReviver({
418 schema: Schema.Number.check(Schema.isLessThanOrEqualTo(2)),
419 id: "effect/schema/isLessThanOrEqualTo",
420 payload: { maximum: 2 },
421 reviver: SchemaRepresentation.isLessThanOrEqualToReviver,
422 valid: 2,
423 invalid: 3
424 })
425 })
426
427 it("revives isBetween", () => {
428 assertFilterReviver({
429 schema: Schema.Number.check(
430 Schema.isBetween({ minimum: 1, maximum: 3, exclusiveMinimum: true })
431 ),
432 id: "effect/schema/isBetween",
433 payload: { minimum: 1, maximum: 3, exclusiveMinimum: true },
434 reviver: SchemaRepresentation.isBetweenReviver,
435 valid: 2,
436 invalid: 1
437 })
438 })
439
440 it("normalizes isBetween flags", () => {
441 const check = Schema.isBetween({
442 minimum: 1,
443 maximum: 3,
444 exclusiveMinimum: false,
445 exclusiveMaximum: true
446 })
447
448 assert.deepStrictEqual(check.annotations?.representation, {
449 id: "effect/schema/isBetween",
450 payload: { minimum: 1, maximum: 3, exclusiveMaximum: true }
451 })
452 })
453
454 it("rejects a non-numeric isMultipleOf payload", () => {
455 const json = SchemaRepresentation.toJson(
456 SchemaRepresentation.toRepresentation(Schema.Number.check(Schema.isMultipleOf(2)).ast)
457 ) as any
458 json.representation.checks[0].representation.payload.divisor = "2"
459
460 expectInvalidPayload(json, SchemaRepresentation.isMultipleOfReviver)
461 })
462
463 it("rejects a non-canonical isBetween payload", () => {
464 const json = SchemaRepresentation.toJson(
465 SchemaRepresentation.toRepresentation(
466 Schema.Number.check(Schema.isBetween({ minimum: 1, maximum: 3 })).ast
467 )
468 ) as any
469 json.representation.checks[0].representation.payload.exclusiveMinimum = false
470
471 expectInvalidPayload(json, SchemaRepresentation.isBetweenReviver)
472 })
473})
474
475describe("SchemaRepresentation built-in BigInt revivers", () => {
476 it("revives isGreaterThanBigInt", () => {
477 assertFilterReviver({
478 schema: Schema.BigInt.check(Schema.isGreaterThanBigInt(10n)),
479 id: "effect/schema/isGreaterThanBigInt",
480 payload: { exclusiveMinimum: "10" },
481 reviver: SchemaRepresentation.isGreaterThanBigIntReviver,
482 valid: 11n,
483 invalid: 10n
484 })
485 })
486
487 it("revives isGreaterThanOrEqualToBigInt", () => {
488 assertFilterReviver({
489 schema: Schema.BigInt.check(Schema.isGreaterThanOrEqualToBigInt(10n)),
490 id: "effect/schema/isGreaterThanOrEqualToBigInt",
491 payload: { minimum: "10" },
492 reviver: SchemaRepresentation.isGreaterThanOrEqualToBigIntReviver,
493 valid: 10n,
494 invalid: 9n
495 })
496 })
497
498 it("revives isLessThanBigInt", () => {
499 assertFilterReviver({
500 schema: Schema.BigInt.check(Schema.isLessThanBigInt(10n)),
501 id: "effect/schema/isLessThanBigInt",
502 payload: { exclusiveMaximum: "10" },
503 reviver: SchemaRepresentation.isLessThanBigIntReviver,
504 valid: 9n,
505 invalid: 10n
506 })
507 })
508
509 it("revives isLessThanOrEqualToBigInt", () => {
510 assertFilterReviver({
511 schema: Schema.BigInt.check(Schema.isLessThanOrEqualToBigInt(10n)),
512 id: "effect/schema/isLessThanOrEqualToBigInt",
513 payload: { maximum: "10" },
514 reviver: SchemaRepresentation.isLessThanOrEqualToBigIntReviver,
515 valid: 10n,
516 invalid: 11n
517 })
518 })
519
520 it("revives isBetweenBigInt", () => {
521 assertFilterReviver({
522 schema: Schema.BigInt.check(
523 Schema.isBetweenBigInt({ minimum: -10n, maximum: 10n, exclusiveMaximum: true })
524 ),
525 id: "effect/schema/isBetweenBigInt",
526 payload: { minimum: "-10", maximum: "10", exclusiveMaximum: true },
527 reviver: SchemaRepresentation.isBetweenBigIntReviver,
528 valid: 0n,
529 invalid: 10n
530 })
531 })
532
533 it("persists large bounds as canonical decimal strings", () => {
534 const value = 900719925474099312345678901234567890n
535 assert.deepStrictEqual(Schema.isGreaterThanBigInt(value).annotations?.representation, {
536 id: "effect/schema/isGreaterThanBigInt",
537 payload: { exclusiveMinimum: "900719925474099312345678901234567890" }
538 })
539 })
540
541 it("normalizes isBetweenBigInt flags", () => {
542 assert.deepStrictEqual(
543 Schema.isBetweenBigInt({
544 minimum: -1n,
545 maximum: 1n,
546 exclusiveMinimum: false,
547 exclusiveMaximum: true
548 }).annotations?.representation,
549 {
550 id: "effect/schema/isBetweenBigInt",
551 payload: { minimum: "-1", maximum: "1", exclusiveMaximum: true }
552 }
553 )
554 })
555})
556
557function date(millis: number): Date {
558 return new globalThis.Date(millis)
559}
560
561const epoch = "1970-01-01T00:00:00.000Z"
562
563describe("SchemaRepresentation built-in Date revivers", () => {
564 it("revives isGreaterThanDate", () => {
565 assertFilterReviver({
566 schema: Schema.Any.check(Schema.isGreaterThanDate(date(0))),
567 id: "effect/schema/isGreaterThanDate",
568 payload: { exclusiveMinimum: epoch },
569 reviver: SchemaRepresentation.isGreaterThanDateReviver,
570 valid: date(1),
571 invalid: date(0)
572 })
573 })
574
575 it("revives isGreaterThanOrEqualToDate", () => {
576 assertFilterReviver({
577 schema: Schema.Any.check(Schema.isGreaterThanOrEqualToDate(date(0))),
578 id: "effect/schema/isGreaterThanOrEqualToDate",
579 payload: { minimum: epoch },
580 reviver: SchemaRepresentation.isGreaterThanOrEqualToDateReviver,
581 valid: date(0),
582 invalid: date(-1)
583 })
584 })
585
586 it("revives isLessThanDate", () => {
587 assertFilterReviver({
588 schema: Schema.Any.check(Schema.isLessThanDate(date(0))),
589 id: "effect/schema/isLessThanDate",
590 payload: { exclusiveMaximum: epoch },
591 reviver: SchemaRepresentation.isLessThanDateReviver,
592 valid: date(-1),
593 invalid: date(0)
594 })
595 })
596
597 it("revives isLessThanOrEqualToDate", () => {
598 assertFilterReviver({
599 schema: Schema.Any.check(Schema.isLessThanOrEqualToDate(date(0))),
600 id: "effect/schema/isLessThanOrEqualToDate",
601 payload: { maximum: epoch },
602 reviver: SchemaRepresentation.isLessThanOrEqualToDateReviver,
603 valid: date(0),
604 invalid: date(1)
605 })
606 })
607
608 it("revives isBetweenDate", () => {
609 assertFilterReviver({
610 schema: Schema.Any.check(
611 Schema.isBetweenDate({ minimum: date(0), maximum: date(2), exclusiveMaximum: true })
612 ),
613 id: "effect/schema/isBetweenDate",
614 payload: {
615 minimum: epoch,
616 maximum: "1970-01-01T00:00:00.002Z",
617 exclusiveMaximum: true
618 },
619 reviver: SchemaRepresentation.isBetweenDateReviver,
620 valid: date(1),
621 invalid: date(2)
622 })
623 })
624
625 it("persists millisecond precision", () => {
626 assert.deepStrictEqual(Schema.isGreaterThanDate(date(123)).annotations?.representation, {
627 id: "effect/schema/isGreaterThanDate",
628 payload: { exclusiveMinimum: "1970-01-01T00:00:00.123Z" }
629 })
630 })
631
632 it("normalizes isBetweenDate flags", () => {
633 assert.deepStrictEqual(
634 Schema.isBetweenDate({
635 minimum: date(-1),
636 maximum: date(1),
637 exclusiveMinimum: false,
638 exclusiveMaximum: true
639 }).annotations?.representation,
640 {
641 id: "effect/schema/isBetweenDate",
642 payload: {
643 minimum: "1969-12-31T23:59:59.999Z",
644 maximum: "1970-01-01T00:00:00.001Z",
645 exclusiveMaximum: true
646 }
647 }
648 )
649 })
650})
651
652describe("SchemaRepresentation built-in collection revivers", () => {
653 it("revives isMinSize", () => {
654 assertFilterReviver({
655 schema: Schema.Any.check(Schema.isMinSize(2)),
656 id: "effect/schema/isMinSize",
657 payload: { minSize: 2 },
658 reviver: SchemaRepresentation.isMinSizeReviver,
659 valid: new Set([1, 2]),
660 invalid: new Set([1])
661 })
662 })
663
664 it("revives isMaxSize", () => {
665 assertFilterReviver({
666 schema: Schema.Any.check(Schema.isMaxSize(1)),
667 id: "effect/schema/isMaxSize",
668 payload: { maxSize: 1 },
669 reviver: SchemaRepresentation.isMaxSizeReviver,
670 valid: new Set([1]),
671 invalid: new Set([1, 2])
672 })
673 })
674
675 it("revives isBetweenSize", () => {
676 assertFilterReviver({
677 schema: Schema.Any.check(Schema.isBetweenSize(1, 2)),
678 id: "effect/schema/isBetweenSize",
679 payload: { minimum: 1, maximum: 2 },
680 reviver: SchemaRepresentation.isBetweenSizeReviver,
681 valid: new Set([1]),
682 invalid: new Set()
683 })
684 })
685
686 it("revives isUnique", () => {
687 assertFilterReviver({
688 schema: Schema.Any.check(Schema.isUnique()),
689 id: "effect/schema/isUnique",
690 payload: null,
691 reviver: SchemaRepresentation.isUniqueReviver,
692 valid: [1, 2],
693 invalid: [1, 1]
694 })
695 })
696
697 it("revives isUniqueKey", () => {
698 assertFilterReviver({
699 schema: Schema.Any.check(Schema.isUniqueKey()),
700 id: "effect/schema/isUniqueKey",
701 payload: null,
702 reviver: SchemaRepresentation.isUniqueKeyReviver,
703 valid: [["a", 1], ["b", 1]],
704 invalid: [["a", 1], ["a", 2]],
705 hasToJsonSchema: false
706 })
707 })
708
709 it("normalizes isMinSize", () => {
710 assert.deepStrictEqual(Schema.isMinSize(-1).annotations?.representation, {
711 id: "effect/schema/isMinSize",
712 payload: { minSize: 0 }
713 })
714 })
715
716 it("normalizes isMaxSize", () => {
717 assert.deepStrictEqual(Schema.isMaxSize(2.9).annotations?.representation, {
718 id: "effect/schema/isMaxSize",
719 payload: { maxSize: 2 }
720 })
721 })
722
723 it("normalizes isBetweenSize", () => {
724 assert.deepStrictEqual(Schema.isBetweenSize(1.9, 3.7).annotations?.representation, {
725 id: "effect/schema/isBetweenSize",
726 payload: { minimum: 1, maximum: 3 }
727 })
728 })
729})
730
731describe("SchemaRepresentation built-in object revivers", () => {
732 it("revives isMinProperties", () => {
733 assertFilterReviver({
734 schema: Schema.Any.check(Schema.isMinProperties(2)),
735 id: "effect/schema/isMinProperties",
736 payload: { minProperties: 2 },
737 reviver: SchemaRepresentation.isMinPropertiesReviver,
738 valid: { a: 1, b: 2 },
739 invalid: { a: 1 }
740 })
741 })
742
743 it("revives isMaxProperties", () => {
744 assertFilterReviver({
745 schema: Schema.Any.check(Schema.isMaxProperties(1)),
746 id: "effect/schema/isMaxProperties",
747 payload: { maxProperties: 1 },
748 reviver: SchemaRepresentation.isMaxPropertiesReviver,
749 valid: { a: 1 },
750 invalid: { a: 1, b: 2 }
751 })
752 })
753
754 it("revives isBetweenProperties", () => {
755 assertFilterReviver({
756 schema: Schema.Any.check(Schema.isBetweenProperties(1, 2)),
757 id: "effect/schema/isBetweenProperties",
758 payload: { minimum: 1, maximum: 2 },
759 reviver: SchemaRepresentation.isBetweenPropertiesReviver,
760 valid: { a: 1 },
761 invalid: {}
762 })
763 })
764
765 it("revives isPropertyNames", () => {
766 const names = Schema.String.check(Schema.isPattern(/^[A-Z]/))
767 assertFilterReviver({
768 schema: Schema.Any.check(Schema.isPropertyNames(names)),
769 id: "effect/schema/isPropertyNames",
770 payload: null,
771 schemas: [names.ast],
772 reviver: SchemaRepresentation.isPropertyNamesReviver,
773 dependencies: [SchemaRepresentation.isPatternReviver],
774 valid: { Alpha: 1 },
775 invalid: { alpha: 1 }
776 })
777 })
778
779 it("persists the encoded key schema for isPropertyNames", () => {
780 const names = Schema.String.check(Schema.isPattern(/^[A-Z]/))
781 const check = Schema.isPropertyNames(names)
782
783 assert.deepStrictEqual(check.annotations?.representation, {
784 id: "effect/schema/isPropertyNames",
785 payload: null,
786 schemas: [names.ast]
787 })
788 })
789
790 it("normalizes isMinProperties", () => {
791 assert.deepStrictEqual(Schema.isMinProperties(-1).annotations?.representation, {
792 id: "effect/schema/isMinProperties",
793 payload: { minProperties: 0 }
794 })
795 })
796
797 it("normalizes isMaxProperties", () => {
798 assert.deepStrictEqual(Schema.isMaxProperties(2.9).annotations?.representation, {
799 id: "effect/schema/isMaxProperties",
800 payload: { maxProperties: 2 }
801 })
802 })
803
804 it("normalizes isBetweenProperties", () => {
805 assert.deepStrictEqual(Schema.isBetweenProperties(1.9, 3.7).annotations?.representation, {
806 id: "effect/schema/isBetweenProperties",
807 payload: { minimum: 1, maximum: 3 }
808 })
809 })
810})
811
812describe("SchemaRepresentation built-in declaration revivers", () => {
813 it("revives Option", () => {
814 assertDeclarationReviver({
815 schema: Schema.Option(Schema.String),
816 id: "effect/schema/Option",
817 payload: null,
818 reviver: SchemaRepresentation.OptionReviver
819 })
820 })
821
822 it("revives Result", () => {
823 assertDeclarationReviver({
824 schema: Schema.Result(Schema.String, Schema.Number),
825 id: "effect/schema/Result",
826 payload: null,
827 reviver: SchemaRepresentation.ResultReviver
828 })
829 })
830
831 it("revives Redacted", () => {
832 assertDeclarationReviver({
833 schema: Schema.Redacted(Schema.String),
834 id: "effect/schema/Redacted",
835 payload: null,
836 reviver: SchemaRepresentation.RedactedReviver
837 })
838 })
839
840 it("revives CauseReason", () => {
841 assertDeclarationReviver({
842 schema: Schema.CauseReason(Schema.String, Schema.Number),
843 id: "effect/schema/CauseReason",
844 payload: null,
845 reviver: SchemaRepresentation.CauseReasonReviver
846 })
847 })
848
849 it("revives Cause", () => {
850 assertDeclarationReviver({
851 schema: Schema.Cause(Schema.String, Schema.Number),
852 id: "effect/schema/Cause",
853 payload: null,
854 reviver: SchemaRepresentation.CauseReviver
855 })
856 })
857
858 it("revives Error", () => {
859 assertDeclarationReviver({
860 schema: Schema.ErrorInstance(),
861 id: "effect/schema/Error",
862 payload: null,
863 reviver: SchemaRepresentation.ErrorInstanceReviver
864 })
865 })
866
867 it("revives Exit", () => {
868 assertDeclarationReviver({
869 schema: Schema.Exit(Schema.String, Schema.Number, Schema.Boolean),
870 id: "effect/schema/Exit",
871 payload: null,
872 reviver: SchemaRepresentation.ExitReviver
873 })
874 })
875
876 it("revives ReadonlyMap", () => {
877 assertDeclarationReviver({
878 schema: Schema.ReadonlyMap(Schema.String, Schema.Number),
879 id: "effect/schema/ReadonlyMap",
880 payload: null,
881 reviver: SchemaRepresentation.ReadonlyMapReviver
882 })
883 })
884
885 it("revives HashMap", () => {
886 assertDeclarationReviver({
887 schema: Schema.HashMap(Schema.String, Schema.Number),
888 id: "effect/schema/HashMap",
889 payload: null,
890 reviver: SchemaRepresentation.HashMapReviver
891 })
892 })
893
894 it("revives Graph", () => {
895 assertDeclarationReviver({
896 schema: Schema.Graph("directed", Schema.String, Schema.Number),
897 id: "effect/schema/Graph",
898 payload: "directed",
899 reviver: SchemaRepresentation.GraphReviver
900 })
901 })
902
903 it("revives ReadonlySet", () => {
904 assertDeclarationReviver({
905 schema: Schema.ReadonlySet(Schema.String),
906 id: "effect/schema/ReadonlySet",
907 payload: null,
908 reviver: SchemaRepresentation.ReadonlySetReviver
909 })
910 })
911
912 it("revives HashSet", () => {
913 assertDeclarationReviver({
914 schema: Schema.HashSet(Schema.String),
915 id: "effect/schema/HashSet",
916 payload: null,
917 reviver: SchemaRepresentation.HashSetReviver
918 })
919 })
920
921 it("revives Chunk", () => {
922 assertDeclarationReviver({
923 schema: Schema.Chunk(Schema.String),
924 id: "effect/schema/Chunk",
925 payload: null,
926 reviver: SchemaRepresentation.ChunkReviver
927 })
928 })
929
930 it("revives RegExp", () => {
931 assertDeclarationReviver({
932 schema: Schema.RegExp,
933 id: "effect/schema/RegExp",
934 payload: null,
935 reviver: SchemaRepresentation.RegExpReviver
936 })
937 })
938
939 it("revives URL", () => {
940 assertDeclarationReviver({
941 schema: Schema.URL,
942 id: "effect/schema/URL",
943 payload: null,
944 reviver: SchemaRepresentation.URLReviver
945 })
946 })
947
948 it("revives Date", () => {
949 assertDeclarationReviver({
950 schema: Schema.Date,
951 id: "effect/schema/Date",
952 payload: null,
953 reviver: SchemaRepresentation.DateReviver
954 })
955 })
956
957 it("revives Duration", () => {
958 assertDeclarationReviver({
959 schema: Schema.Duration,
960 id: "effect/schema/Duration",
961 payload: null,
962 reviver: SchemaRepresentation.DurationReviver
963 })
964 })
965
966 it("revives ByteSize", () => {
967 assertDeclarationReviver({
968 schema: Schema.ByteSize,
969 id: "effect/schema/ByteSize",
970 payload: null,
971 reviver: SchemaRepresentation.ByteSizeReviver
972 })
973 })
974
975 it("revives BigDecimal", () => {
976 assertDeclarationReviver({
977 schema: Schema.BigDecimal,
978 id: "effect/schema/BigDecimal",
979 payload: null,
980 reviver: SchemaRepresentation.BigDecimalReviver
981 })
982 })
983
984 it("revives File", () => {
985 assertDeclarationReviver({
986 schema: Schema.File,
987 id: "effect/schema/File",
988 payload: null,
989 reviver: SchemaRepresentation.FileReviver
990 })
991 })
992
993 it("revives FormData", () => {
994 assertDeclarationReviver({
995 schema: Schema.FormData,
996 id: "effect/schema/FormData",
997 payload: null,
998 reviver: SchemaRepresentation.FormDataReviver
999 })
1000 })
1001
1002 it("revives URLSearchParams", () => {
1003 assertDeclarationReviver({
1004 schema: Schema.URLSearchParams,
1005 id: "effect/schema/URLSearchParams",
1006 payload: null,
1007 reviver: SchemaRepresentation.URLSearchParamsReviver
1008 })
1009 })
1010
1011 it("revives Uint8Array", () => {
1012 assertDeclarationReviver({
1013 schema: Schema.Uint8Array,
1014 id: "effect/schema/Uint8Array",
1015 payload: null,
1016 reviver: SchemaRepresentation.Uint8ArrayReviver
1017 })
1018 })
1019
1020 it("revives DateTimeUtc", () => {
1021 assertDeclarationReviver({
1022 schema: Schema.DateTimeUtc,
1023 id: "effect/schema/DateTimeUtc",
1024 payload: null,
1025 reviver: SchemaRepresentation.DateTimeUtcReviver
1026 })
1027 })
1028
1029 it("revives TimeZoneOffset", () => {
1030 assertDeclarationReviver({
1031 schema: Schema.TimeZoneOffset,
1032 id: "effect/schema/TimeZoneOffset",
1033 payload: null,
1034 reviver: SchemaRepresentation.TimeZoneOffsetReviver
1035 })
1036 })
1037
1038 it("revives TimeZoneNamed", () => {
1039 assertDeclarationReviver({
1040 schema: Schema.TimeZoneNamed,
1041 id: "effect/schema/TimeZoneNamed",
1042 payload: null,
1043 reviver: SchemaRepresentation.TimeZoneNamedReviver
1044 })
1045 })
1046
1047 it("revives TimeZone", () => {
1048 assertDeclarationReviver({
1049 schema: Schema.TimeZone,
1050 id: "effect/schema/TimeZone",
1051 payload: null,
1052 reviver: SchemaRepresentation.TimeZoneReviver
1053 })
1054 })
1055
1056 it("revives DateTimeZoned", () => {
1057 assertDeclarationReviver({
1058 schema: Schema.DateTimeZoned,
1059 id: "effect/schema/DateTimeZoned",
1060 payload: null,
1061 reviver: SchemaRepresentation.DateTimeZonedReviver
1062 })
1063 })
1064
1065 it("revives Json", () => {
1066 assertDeclarationReviver({
1067 schema: Schema.Json,
1068 id: "effect/schema/Json",
1069 payload: null,
1070 reviver: SchemaRepresentation.JsonReviver
1071 })
1072 })
1073
1074 it("revives MutableJson", () => {
1075 assertDeclarationReviver({
1076 schema: Schema.MutableJson,
1077 id: "effect/schema/MutableJson",
1078 payload: null,
1079 reviver: SchemaRepresentation.MutableJsonReviver
1080 })
1081 })
1082
1083 it("persists Error includeStack", () => {
1084 assert.deepStrictEqual(Schema.ErrorInstance({ includeStack: true }).ast.annotations?.representation, {
1085 id: "effect/schema/Error",
1086 payload: { includeStack: true }
1087 })
1088 })
1089
1090 it("persists Error excludeCause", () => {
1091 assert.deepStrictEqual(Schema.ErrorInstance({ excludeCause: true }).ast.annotations?.representation, {
1092 id: "effect/schema/Error",
1093 payload: { excludeCause: true }
1094 })
1095 })
1096
1097 it("omits disabled Error options", () => {
1098 assert.deepStrictEqual(
1099 Schema.ErrorInstance({ includeStack: false, excludeCause: false }).ast.annotations?.representation,
1100 { id: "effect/schema/Error", payload: null }
1101 )
1102 })
1103
1104 it("persists a Redacted label", () => {
1105 assert.deepStrictEqual(
1106 Schema.Redacted(Schema.String, { label: "password" }).ast.annotations?.representation,
1107 { id: "effect/schema/Redacted", payload: { label: "password" } }
1108 )
1109 })
1110
1111 it("persists Redacted disallowJsonEncode", () => {
1112 assert.deepStrictEqual(
1113 Schema.Redacted(Schema.String, { disallowJsonEncode: true }).ast.annotations?.representation,
1114 { id: "effect/schema/Redacted", payload: { disallowJsonEncode: true } }
1115 )
1116 })
1117
1118 it("omits disabled Redacted options", () => {
1119 assert.deepStrictEqual(
1120 Schema.Redacted(Schema.String, {
1121 label: undefined,
1122 disallowJsonEncode: false
1123 }).ast.annotations?.representation,
1124 { id: "effect/schema/Redacted", payload: null }
1125 )
1126 })
1127})