1import * as Str from "effect/String"
2import assert from "node:assert/strict"
3import * as v from "valibot"
4
5const person = () => v.object({ name: v.string(), age: v.number(), active: v.boolean() })
6const input = { name: "Ada", age: 37, active: true }
7const small = person()
8const arraySchema = v.array(person())
9const arrayInput = Array.from({ length: 32 }, () => ({ ...input }))
10const recordSchema = v.record(v.string(), person())
11const recordInput = Object.fromEntries(arrayInput.map((value, i) => [String(i), value]))
12const transformedKeyRecordSchema = v.record(
13 v.pipe(v.string(), v.transform(Str.snakeToCamel)),
14 v.string()
15)
16const transformedKeyRecordInput = Object.fromEntries(
17 Array.from({ length: 32 }, (_, i) => [`field_${i}_value`, String(i)])
18)
19const transformedKeyRecordOutput = Object.fromEntries(
20 Array.from({ length: 32 }, (_, i) => [`field${i}Value`, String(i)])
21)
22const unionSchema = v.variant(
23 "tag",
24 Array.from({ length: 8 }, (_, i) => v.object({ tag: v.literal(i), value: v.number() }))
25)
26const transformedSchema = v.object(
27 Object.fromEntries(
28 Array.from({ length: 32 }, (_, i) => [`v${i}`, v.pipe(v.string(), v.transform(Number))])
29 )
30)
31const transformedInput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, String(i)]))
32const transformedOutput = Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, i]))
33const defaultsSchema = v.object(
34 Object.fromEntries(Array.from({ length: 32 }, (_, i) => [`v${i}`, v.optional(v.number(), i)]))
35)
36
37type Case = {
38 readonly schema: v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>
39 readonly input: unknown
40 readonly expected: unknown
41 readonly operation?: "is" | "make"
42 readonly invalid?: boolean
43}
44
45const cases: Record<string, Case> = {
46 parseValid: { schema: small, input, expected: input },
47 parseInvalid: { schema: small, input: { ...input, age: "bad" }, expected: true, invalid: true },
48 isValid: { schema: small, input, expected: true, operation: "is" },
49 isInvalid: { schema: small, input: { ...input, age: "bad" }, expected: false, operation: "is" },
50 array: { schema: arraySchema, input: arrayInput, expected: arrayInput },
51 record: { schema: recordSchema, input: recordInput, expected: recordInput },
52 recordTransformedKeys: {
53 schema: transformedKeyRecordSchema,
54 input: transformedKeyRecordInput,
55 expected: transformedKeyRecordOutput
56 },
57 union: { schema: unionSchema, input: { tag: 7, value: 1 }, expected: { tag: 7, value: 1 } },
58 transform: { schema: transformedSchema, input: transformedInput, expected: transformedOutput },
59 makeStruct: { schema: defaultsSchema, input: {}, expected: transformedOutput, operation: "make" },
60 makeArray: { schema: arraySchema, input: arrayInput, expected: arrayInput, operation: "make" }
61}
62
63const fixture = (name: string) => {
64 const { schema, input, expected, operation, invalid } = cases[name]
65 const parse = operation === "is"
66 ? (input: unknown) => v.is(schema, input)
67 : (input: unknown) => v.parse(schema, input)
68 return {
69 run: invalid
70 ? () => {
71 try {
72 parse(input)
73 return false
74 } catch {
75 return true
76 }
77 }
78 : () => parse(input),
79 validate: (result: unknown) => assert.deepEqual(result, expected)
80 }
81}
82
83export const parseValid = () => fixture("parseValid")
84export const parseInvalid = () => fixture("parseInvalid")
85export const isValid = () => fixture("isValid")
86export const isInvalid = () => fixture("isInvalid")
87export const array = () => fixture("array")
88export const record = () => fixture("record")
89export const recordTransformedKeys = () => fixture("recordTransformedKeys")
90export const union = () => fixture("union")
91export const transform = () => fixture("transform")
92export const makeStruct = () => fixture("makeStruct")
93export const makeArray = () => fixture("makeArray")