diff --git a/src/index.ts b/src/index.ts
index a9e2189..706cd7f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,10 +7,10 @@ import {
parseFormSafe,
} from './parsers';
import {
- BoolAsString,
- CheckboxAsString,
- IntAsString,
- NumAsString,
+ boolAsString,
+ checkboxAsString,
+ intAsString,
+ numAsString,
} from './schemas';
export {
@@ -20,10 +20,9 @@ export {
parseQuerySafe,
parseForm,
parseFormSafe,
- BoolAsString,
- CheckboxAsString,
- IntAsString,
- NumAsString,
+ boolAsString,
+ intAsString,
+ numAsString,
};
export const zx = {
@@ -33,8 +32,8 @@ export const zx = {
parseQuerySafe,
parseForm,
parseFormSafe,
- BoolAsString,
- CheckboxAsString,
- IntAsString,
- NumAsString,
+ boolAsString,
+ checkboxAsString,
+ intAsString,
+ numAsString,
};
diff --git a/src/parsers.test.ts b/src/parsers.test.ts
index 81a01d0..525884b 100644
--- a/src/parsers.test.ts
+++ b/src/parsers.test.ts
@@ -11,7 +11,7 @@ describe('parseParams', () => {
type Result = { id: string; age: number };
const params: Params = { id: 'id1', age: '10' };
const paramsResult = { id: 'id1', age: 10 };
- const objectSchema = { id: z.string(), age: zx.IntAsString };
+ const objectSchema = { id: z.string(), age: zx.intAsString() };
const zodSchema = z.object(objectSchema);
test('parses params using an object', () => {
@@ -41,13 +41,13 @@ describe('parseParamsSafe', () => {
type Result = { id: string; age: number };
const params: Params = { id: 'id1', age: '10' };
const paramsResult = { id: 'id1', age: 10 };
- const objectSchema = { id: z.string(), age: zx.IntAsString };
+ const objectSchema = { id: z.string(), age: zx.intAsString() };
const zodSchema = z.object(objectSchema);
test('parses params using an object', () => {
const result = zx.parseParamsSafe(params, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
});
expect(result.success).toBe(true);
if (result.success !== true) throw new Error('Parsing failed');
@@ -88,7 +88,7 @@ describe('parseQuery', () => {
const queryResult = { id: 'id1', age: 10 };
const objectSchema = {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
};
const zodSchema = z.object(objectSchema);
@@ -96,7 +96,7 @@ describe('parseQuery', () => {
test('parses URLSearchParams using an object', () => {
const result = zx.parseQuery(search, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result).toStrictEqual(queryResult);
@@ -115,7 +115,7 @@ describe('parseQuery', () => {
search.append('friends', 'friend2');
const result = zx.parseQuery(search, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result).toStrictEqual({
@@ -141,7 +141,7 @@ describe('parseQuery', () => {
const request = new Request(`http://example.com?${search.toString()}`);
const result = zx.parseQuery(request, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result).toStrictEqual(queryResult);
@@ -173,7 +173,7 @@ describe('parseQuery', () => {
search,
{
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
},
{ parser: customArrayParser }
@@ -205,7 +205,7 @@ describe('parseQuerySafe', () => {
const queryResult = { id: 'id1', age: 10 };
const zodSchema = z.object({
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
@@ -213,7 +213,7 @@ describe('parseQuerySafe', () => {
const search = new URLSearchParams({ id: 'id1', age: '10' });
const result = zx.parseQuerySafe(search, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result.success).toBe(true);
@@ -237,7 +237,7 @@ describe('parseQuerySafe', () => {
search.append('friends', 'friend2');
const result = zx.parseQuerySafe(search, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result.success).toBe(true);
@@ -268,7 +268,7 @@ describe('parseQuerySafe', () => {
const request = new Request(`http://example.com?${search.toString()}`);
const result = zx.parseQuerySafe(request, {
id: z.string(),
- age: zx.IntAsString,
+ age: zx.intAsString(),
friends: z.array(z.string()).optional(),
});
expect(result.success).toBe(true);
@@ -316,8 +316,8 @@ describe('parseForm', () => {
const formResult = { id: 'id1', age: 10, consent: true };
const objectSchema = {
id: z.string(),
- age: zx.IntAsString,
- consent: zx.CheckboxAsString,
+ age: zx.intAsString(),
+ consent: zx.checkboxAsString(),
friends: z.array(z.string()).optional(),
image: z.instanceof(NodeOnDiskFile).optional(),
};
@@ -379,8 +379,8 @@ describe('parseForm', () => {
});
const result = await zx.parseForm(request, {
id: z.string(),
- age: zx.IntAsString,
- consent: zx.CheckboxAsString,
+ age: zx.intAsString(),
+ consent: zx.checkboxAsString(),
friends: z.array(z.string()).optional(),
image: z.instanceof(NodeOnDiskFile).optional(),
});
@@ -454,8 +454,8 @@ describe('parseFormSafe', () => {
const formResult = { id: 'id1', age: 10, consent: true };
const zodSchema = z.object({
id: z.string(),
- age: zx.IntAsString,
- consent: zx.CheckboxAsString,
+ age: zx.intAsString(),
+ consent: zx.checkboxAsString(),
friends: z.array(z.string()).optional(),
image: z.instanceof(NodeOnDiskFile).optional(),
});
@@ -465,8 +465,8 @@ describe('parseFormSafe', () => {
const request = createFormRequest();
const result = await zx.parseFormSafe(request, {
id: z.string(),
- age: zx.IntAsString,
- consent: zx.CheckboxAsString,
+ age: zx.intAsString(),
+ consent: zx.checkboxAsString(),
friends: z.array(z.string()).optional(),
image: z.instanceof(NodeOnDiskFile).optional(),
});
diff --git a/src/schemas.test.ts b/src/schemas.test.ts
index 35fff22..780bb79 100644
--- a/src/schemas.test.ts
+++ b/src/schemas.test.ts
@@ -1,73 +1,76 @@
import { zx } from './';
-describe('BoolAsString', () => {
+describe('boolAsString', () => {
test('parses true as string', () => {
- expect(zx.BoolAsString.parse('true')).toBe(true);
+ expect(zx.boolAsString().parse('true')).toBe(true);
});
test('parses false as string', () => {
- expect(zx.BoolAsString.parse('false')).toBe(false);
+ expect(zx.boolAsString().parse('false')).toBe(false);
});
test('throws on non-boolean string', () => {
- expect(() => zx.BoolAsString.parse('hello')).toThrowError();
+ expect(() => zx.boolAsString().parse('hello')).toThrowError();
});
});
-describe('CheckboxAsString', () => {
+describe('checkboxAsString', () => {
test('parses "on" as boolean', () => {
- expect(zx.CheckboxAsString.parse('on')).toBe(true);
+ expect(zx.checkboxAsString().parse('on')).toBe(true);
+ });
+ test('parses "true" as boolean', () => {
+ expect(zx.checkboxAsString({ trueValue: 'true' }).parse('true')).toBe(true);
});
test('parses undefined as boolean', () => {
- expect(zx.CheckboxAsString.parse(undefined)).toBe(false);
+ expect(zx.checkboxAsString().parse(undefined)).toBe(false);
});
test('throws on non-"on" string', () => {
- expect(() => zx.CheckboxAsString.parse('hello')).toThrowError();
+ expect(() => zx.checkboxAsString().parse('hello')).toThrowError();
});
});
-describe('IntAsString', () => {
+describe('intAsString', () => {
test('parses int as string', () => {
- expect(zx.IntAsString.parse('3')).toBe(3);
+ expect(zx.intAsString().parse('3')).toBe(3);
});
test('parses int as string with leading 0', () => {
- expect(zx.IntAsString.parse('03')).toBe(3);
+ expect(zx.intAsString().parse('03')).toBe(3);
});
test('parses negative int as string', () => {
- expect(zx.IntAsString.parse('-3')).toBe(-3);
+ expect(zx.intAsString().parse('-3')).toBe(-3);
});
test('throws on int as number', () => {
- expect(() => zx.IntAsString.parse(3)).toThrowError();
+ expect(() => zx.intAsString().parse(3)).toThrowError();
});
test('throws on float', () => {
- expect(() => zx.IntAsString.parse(3.14)).toThrowError();
+ expect(() => zx.intAsString().parse(3.14)).toThrowError();
});
test('throws on string float', () => {
- expect(() => zx.IntAsString.parse('3.14')).toThrowError();
+ expect(() => zx.intAsString().parse('3.14')).toThrowError();
});
test('throws on non-int string', () => {
- expect(() => zx.IntAsString.parse('a3')).toThrowError();
+ expect(() => zx.intAsString().parse('a3')).toThrowError();
});
});
-describe('NumAsString', () => {
+describe('numAsString', () => {
test('parses number with decimal as string', () => {
- expect(zx.NumAsString.parse('3.14')).toBe(3.14);
+ expect(zx.numAsString().parse('3.14')).toBe(3.14);
});
test('parses number with decimal as string with leading 0', () => {
- expect(zx.NumAsString.parse('03.14')).toBe(3.14);
+ expect(zx.numAsString().parse('03.14')).toBe(3.14);
});
test('parses negative number with decimal as string', () => {
- expect(zx.NumAsString.parse('-3.14')).toBe(-3.14);
+ expect(zx.numAsString().parse('-3.14')).toBe(-3.14);
});
test('parses int as string', () => {
- expect(zx.NumAsString.parse('3')).toBe(3);
+ expect(zx.numAsString().parse('3')).toBe(3);
});
test('parses int as string with leading 0', () => {
- expect(zx.NumAsString.parse('03')).toBe(3);
+ expect(zx.numAsString().parse('03')).toBe(3);
});
test('parses negative int as string', () => {
- expect(zx.NumAsString.parse('-3')).toBe(-3);
+ expect(zx.numAsString().parse('-3')).toBe(-3);
});
test('throws on non-number string', () => {
- expect(() => zx.NumAsString.parse('a3')).toThrowError();
+ expect(() => zx.numAsString().parse('a3')).toThrowError();
});
});
diff --git a/src/schemas.ts b/src/schemas.ts
index 3c66848..aaf31dd 100644
--- a/src/schemas.ts
+++ b/src/schemas.ts
@@ -1,4 +1,5 @@
import { z } from 'zod';
+import type { errorUtil } from 'zod/lib/helpers/errorUtil';
/**
* Zod schema to parse strings that are booleans.
@@ -8,37 +9,54 @@ import { z } from 'zod';
* BoolAsString.parse('true') -> true
* ```
*/
-export const BoolAsString = z
- .string()
- .regex(/^(true|false)$/, 'Must be a boolean string ("true" or "false")')
- .transform((value) => value === 'true');
+export const boolAsString = (
+ message:
+ | errorUtil.ErrMessage
+ | undefined = 'Must be a boolean string ("true" or "false")'
+) =>
+ z
+ .string()
+ .regex(/^(true|false)$/, message)
+ .transform((value) => value === 'true');
/**
* Zod schema to parse checkbox formdata.
* Use to parse values.
* @example
* ```ts
- * CheckboxAsString.parse('on') -> true
- * CheckboxAsString.parse(undefined) -> false
+ * checkboxAsString().parse('on') -> true
+ * checkboxAsString().parse(undefined) -> false
* ```
*/
-export const CheckboxAsString = z
- .literal('on')
- .optional()
- .transform((value) => value === 'on');
+export const checkboxAsString = ({
+ trueValue = 'on',
+ ...params
+}: {
+ trueValue?: string;
+} & Parameters[1] = {}) =>
+ z.union(
+ [
+ z.literal(trueValue).transform(() => true),
+ z.literal(undefined).transform(() => false),
+ ],
+ params
+ );
/**
* Zod schema to parse strings that are integers.
* Use to parse values.
* @example
* ```ts
- * IntAsString.parse('3') -> 3
+ * intAsString.parse('3') -> 3
* ```
*/
-export const IntAsString = z
- .string()
- .regex(/^-?\d+$/, 'Must be an integer string')
- .transform((val) => parseInt(val, 10));
+export const intAsString = (
+ message: errorUtil.ErrMessage | undefined = 'Must be an integer string'
+) =>
+ z
+ .string()
+ .regex(/^-?\d+$/, message)
+ .transform((val) => parseInt(val, 10));
/**
* Zod schema to parse strings that are numbers.
@@ -48,7 +66,10 @@ export const IntAsString = z
* NumAsString.parse('3.14') -> 3.14
* ```
*/
-export const NumAsString = z
- .string()
- .regex(/^-?\d*\.?\d+$/, 'Must be a number string')
- .transform(Number);
+export const numAsString = (
+ message: errorUtil.ErrMessage | undefined = 'Must be a number string'
+) =>
+ z
+ .string()
+ .regex(/^-?\d*\.?\d+$/, message)
+ .transform(Number);