Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
cloud-walker committed Nov 22, 2023
1 parent f74820d commit da7e6de
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 48 deletions.
72 changes: 48 additions & 24 deletions packages/zod-sp/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import { test, expect, describe } from 'vitest'
import { z } from 'zod'
import {describe, expect, test} from 'vitest'
import {z} from 'zod'

import { zodSp } from './main.js'
import {zodSp} from './main.js'

const ISO_DATE_STUB = '2023-11-22T07:32:03.889Z'

test.each([
{ input: '', expected: {} },
{ input: 'a=1', expected: { a: '1' } },
{ input: 'a=true', expected: { a: 'true' } },
{ input: 'a=asd', expected: { a: 'asd' } },
{ input: 'a=1&b=foo&b=bar', expected: { a: '1', b: 'bar' } },
{ input: 'a=1&b=foo,bar', expected: { a: '1', b: 'foo,bar' } },
{ input: 'a=1&b[]=foo,bar', expected: { a: '1', b: ['foo', 'bar'] } },
])('.parse($input) -> $expected', ({ input, expected }) => {
{input: '', expected: {}},
{input: 'a=1', expected: {a: '1'}},
{input: 'a=true', expected: {a: 'true'}},
{input: 'a=asd', expected: {a: 'asd'}},
{input: 'a=1&b=foo&b=bar', expected: {a: '1', b: 'bar'}},
{input: 'a=1&b=foo,bar', expected: {a: '1', b: 'foo,bar'}},
{input: 'a=1&b[]=foo,bar', expected: {a: '1', b: ['foo', 'bar']}},
])('.parse($input) -> $expected', ({input, expected}) => {
expect(zodSp.parse(new URLSearchParams(input))).toEqual(expected)
})

test.each([
{ input: {}, expected: '' },
{ input: { a: 1 }, expected: 'a=1' },
{ input: { a: true }, expected: 'a=true' },
{ input: { a: 'asd' }, expected: 'a=asd' },
{ input: { a: 1, b: ['foo', 'bar'] }, expected: 'a=1&b%5B%5D=foo%2Cbar' },
{ input: { a: 1, b: null, c: undefined }, expected: 'a=1' },
{ input: { a: 1, b: new Date(ISO_DATE_STUB), c: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)] }, expected: `a=1&b=${encodeURIComponent(ISO_DATE_STUB)}&c%5B%5D=${encodeURIComponent(`${ISO_DATE_STUB},${ISO_DATE_STUB}`)}` },
])('.serialize($input) -> $expected', ({ input, expected }) => {
{input: {}, expected: ''},
{input: {a: 1}, expected: 'a=1'},
{input: {a: true}, expected: 'a=true'},
{input: {a: 'asd'}, expected: 'a=asd'},
{input: {a: 1, b: ['foo', 'bar']}, expected: 'a=1&b%5B%5D=foo%2Cbar'},
{input: {a: 1, b: null, c: undefined}, expected: 'a=1'},
{
input: {
a: 1,
b: new Date(ISO_DATE_STUB),
c: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)],
},
expected: `a=1&b=${encodeURIComponent(
ISO_DATE_STUB,
)}&c%5B%5D=${encodeURIComponent(`${ISO_DATE_STUB},${ISO_DATE_STUB}`)}`,
},
])('.serialize($input) -> $expected', ({input, expected}) => {
expect(zodSp.serialize(input).toString()).toBe(expected)
})

Expand All @@ -42,11 +51,26 @@ describe('.with(ZodSchema)', () => {
const dummySp = zodSp.with(ZodSchema)

test.each([
{ input: '', expected: { c: false, e: defaultTuple } },
{ input: 'c=true&a=123&e[]=foo,123', expected: { a: '123', c: true, e: ['foo', 123] } },
{ input: 'c=true&a=123&e[]=foo,123', expected: { a: '123', c: true, e: ['foo', 123] } },
{ input: zodSp.serialize({ d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)] }), expected: { c: false, d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)], e: ['default', 0] } },
])('.parse($input) -> $expected', ({ input, expected }) => {
{input: '', expected: {c: false, e: defaultTuple}},
{
input: 'c=true&a=123&e[]=foo,123',
expected: {a: '123', c: true, e: ['foo', 123]},
},
{
input: 'c=true&a=123&e[]=foo,123',
expected: {a: '123', c: true, e: ['foo', 123]},
},
{
input: zodSp.serialize({
d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)],
}),
expected: {
c: false,
d: [new Date(ISO_DATE_STUB), new Date(ISO_DATE_STUB)],
e: ['default', 0],
},
},
])('.parse($input) -> $expected', ({input, expected}) => {
expect(dummySp.parse(new URLSearchParams(input))).toEqual(expected)
})
})
46 changes: 22 additions & 24 deletions packages/zod-sp/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { ZodObject, ZodRawShape, TypeOf } from 'zod'
import {TypeOf, ZodObject, ZodRawShape} from 'zod'

export const zodSp = {
parse(searchParams: URLSearchParams): Record<string, string | string[]> {
return [...searchParams.entries()].reduce<Record<string, string | string[]>>((acc, [key, value]) => {
// if (value == null) {
// return acc
// }
const serializeValue = (value: {}) =>
value instanceof Date ? value.toISOString() : String(value)

if (key.endsWith('[]')) {
acc[key.replace('[]', '')] = value.split(',')
} else {
acc[key] = value
}
return acc
}, {})
type RawParams = Record<string, string | string[]>

export const zodSp = {
parse(searchParams: URLSearchParams): RawParams {
return [...searchParams.entries()].reduce<RawParams>(
(acc, [key, value]) => {
if (key.endsWith('[]')) {
acc[key.replace('[]', '')] = value.split(',')
} else {
acc[key] = value
}
return acc
},
{},
)
},
serialize<T extends Record<string, unknown>>(params: T): URLSearchParams {
const searchParams = new URLSearchParams()
Expand All @@ -24,28 +28,22 @@ export const zodSp = {

if (Array.isArray(value)) {
if (value.length > 0) {
searchParams.set(`${key}[]`, value.map(v => {
const serialized = v instanceof Date ? v.toISOString() : String(v)
return serialized
}).join(','))
searchParams.set(`${key}[]`, value.map(serializeValue).join(','))
}
return
}

const serialized = value instanceof Date ? value.toISOString() : String(value)

searchParams.set(key, serialized)
searchParams.set(key, serializeValue(value))
})
return searchParams
},
with<Z extends ZodObject<ZodRawShape>>(schema: Z) {
type T = TypeOf<Z>
const parse = (searchParams: URLSearchParams): T => {
const parsed = this.parse(searchParams)
console.log(parsed)
return schema.parse(parsed)
}
const serialize = this.serialize
return { parse, serialize } as const
}
return {parse, serialize} as const
},
} as const

0 comments on commit da7e6de

Please sign in to comment.