Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tangy-carpets-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"conformal": patch
---

Enable isolatedDeclarations in tsconfig
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Exports live in `src/index.ts`, `src/valibot/index.ts`, and `src/zod/index.ts`.
- TypeScript strict mode (see `tsconfig.json` flags).
- ESM-only, isomorphic runtime (no Node-only APIs).
- Prettier formatting: `npm run format:write`.
- Keep runtime deps minimal; `zod` is an optional peer dependency.
- Keep runtime deps minimal; `valibot` is an optional peer dependency.

## Build & test

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ Here's a quick example showing how Conformal handles form validation with a user

```typescript
import { parseFormData } from "conformal";
import * as z from "zod"; // Tip: Use conformal's coerce functions for form input preprocessing
import * as z from "zod";

// Tip: Use conformal's coerce functions for form input preprocessing
const schema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.email("Invalid email address"),
Expand Down
7 changes: 5 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"devDependencies": {
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.7",
"@types/node": "^24.5.2",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"valibot": "^1.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ import { coerceBigint } from "conformal";
console.log(coerceBigint("42")); // 42n
console.log(coerceBigint("9007199254740991")); // 9007199254740991n
console.log(coerceBigint("")); // undefined
console.log(coerceNumber(" ")); // undefined
console.log(coerceBigint(" ")); // undefined
console.log(coerceBigint("abc")); // "abc" (unchanged)
```

Expand Down
5 changes: 4 additions & 1 deletion src/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export function toSubmission<Output>(
};
}

export function getFormErrors(issues?: ReadonlyArray<StandardSchemaV1.Issue>) {
export function getFormErrors(issues?: ReadonlyArray<StandardSchemaV1.Issue>): {
formErrors: string[];
fieldErrors: Record<string, string[]>;
} {
const formErrors: string[] = [];
const fieldErrors: Record<string, string[]> = {};

Expand Down
2 changes: 2 additions & 0 deletions src/valibot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The Valibot Utilities are provided under the `conformal/valibot` subpath. Valibo

These coercion pipes handle the conversion from form input values to rich JS types. They convert empty strings to `undefined`, coerce string inputs to appropriate types (numbers, dates, booleans), and handle `File` objects. They're composable pipes that can be combined with any valibot validation schema using `v.pipe()`.

For concrete reference of coercion rules, refer to the [Coerce Functions](../README.md#coerce-functions) documentation.

```typescript
import * as vf from "conformal/valibot";
import * as v from "valibot";
Expand Down
4 changes: 3 additions & 1 deletion src/valibot/coerce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export function coerceNumber(): v.SchemaWithPipe<
return v.pipe(v.unknown(), v.transform(coerce.coerceNumber));
}

export function coerceBigint() {
export function coerceBigint(): v.SchemaWithPipe<
readonly [v.UnknownSchema, v.TransformAction<unknown, unknown>]
> {
return v.pipe(v.unknown(), v.transform(coerce.coerceBigint));
}

Expand Down
45 changes: 34 additions & 11 deletions src/zod/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,45 @@ import {
/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function string(params?: Parameters<typeof z.string>[0]) {
export function string(
params?: Parameters<typeof z.string>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodString> {
return z.preprocess(coerceString, z.string(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function number(params?: Parameters<typeof z.number>[0]) {
export function number(
params?: Parameters<typeof z.number>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber> {
return z.preprocess(coerceNumber, z.number(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function bigint(params?: Parameters<typeof z.bigint>[0]) {
export function bigint(
params?: Parameters<typeof z.bigint>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBigInt> {
return z.preprocess(coerceBigint, z.bigint(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function boolean(params?: Parameters<typeof z.boolean>[0]) {
export function boolean(
params?: Parameters<typeof z.boolean>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean> {
return z.preprocess(coerceBoolean, z.boolean(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function date(params?: Parameters<typeof z.date>[0]) {
export function date(
params?: Parameters<typeof z.date>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodDate> {
return z.preprocess(coerceDate, z.date(params));
}

Expand All @@ -50,42 +60,55 @@ export function date(params?: Parameters<typeof z.date>[0]) {
export function enum_<const T extends readonly string[]>(
values: T,
params?: Parameters<typeof z.enum>[1],
) {
): z.ZodPipe<
z.ZodTransform<unknown, unknown>,
z.ZodEnum<{
[k in keyof { [k in T[number]]: k }]: { [k in T[number]]: k }[k];
}>
> {
return z.preprocess(coerceString, z.enum(values, params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function file(params?: Parameters<typeof z.file>[0]) {
export function file(
params?: Parameters<typeof z.file>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodFile> {
return z.preprocess(coerceFile, z.file(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function email(params?: Parameters<typeof z.email>[0]) {
export function email(
params?: Parameters<typeof z.email>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodEmail> {
return z.preprocess(coerceString, z.email(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function url(params?: Parameters<typeof z.url>[0]) {
export function url(
params?: Parameters<typeof z.url>[0],
): z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodURL> {
return z.preprocess(coerceString, z.url(params));
}

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export const object = z.object;
export const object: <T extends z.ZodRawShape>(
shape: T,
) => z.ZodObject<z.core.util.Writeable<T>, z.core.$strip> = z.object;

/**
* @deprecated The Zod utilities will be removed in the next major release.
*/
export function array<T extends z.core.SomeType>(
element: T,
params?: Parameters<typeof z.array>[1],
) {
): z.ZodPipe<z.ZodTransform<unknown[], unknown>, z.ZodArray<T>> {
return z.preprocess(coerceArray, z.array(element, params));
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// See also https://aka.ms/tsconfig/module
"module": "nodenext",
"target": "esnext",
"types": ["node"],
"types": [],
"lib": ["esnext", "dom", "dom.iterable"],

// Other Outputs
Expand All @@ -35,6 +35,7 @@
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"isolatedDeclarations": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true
Expand Down