diff --git a/.changeset/tangy-carpets-repeat.md b/.changeset/tangy-carpets-repeat.md new file mode 100644 index 0000000..0d66205 --- /dev/null +++ b/.changeset/tangy-carpets-repeat.md @@ -0,0 +1,5 @@ +--- +"conformal": patch +--- + +Enable isolatedDeclarations in tsconfig diff --git a/AGENTS.md b/AGENTS.md index 2181532..ed1b916 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/README.md b/README.md index df7acd0..1a91386 100644 --- a/README.md +++ b/README.md @@ -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"), diff --git a/package-lock.json b/package-lock.json index dc58056..bde0588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,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", @@ -1240,6 +1239,8 @@ "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "undici-types": "~7.12.0" } @@ -2714,7 +2715,9 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/universalify": { "version": "0.1.2", diff --git a/package.json b/package.json index ec523d9..dfd4391 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/README.md b/src/README.md index 34f20aa..083fc28 100644 --- a/src/README.md +++ b/src/README.md @@ -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) ``` diff --git a/src/submission.ts b/src/submission.ts index 49a614d..0ce3ecf 100644 --- a/src/submission.ts +++ b/src/submission.ts @@ -25,7 +25,10 @@ export function toSubmission( }; } -export function getFormErrors(issues?: ReadonlyArray) { +export function getFormErrors(issues?: ReadonlyArray): { + formErrors: string[]; + fieldErrors: Record; +} { const formErrors: string[] = []; const fieldErrors: Record = {}; diff --git a/src/valibot/README.md b/src/valibot/README.md index 3410745..b0a03cf 100644 --- a/src/valibot/README.md +++ b/src/valibot/README.md @@ -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"; diff --git a/src/valibot/coerce.ts b/src/valibot/coerce.ts index ce5bbcd..6b47d3b 100644 --- a/src/valibot/coerce.ts +++ b/src/valibot/coerce.ts @@ -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] +> { return v.pipe(v.unknown(), v.transform(coerce.coerceBigint)); } diff --git a/src/zod/schemas.ts b/src/zod/schemas.ts index aafddaa..d6b9f8d 100644 --- a/src/zod/schemas.ts +++ b/src/zod/schemas.ts @@ -12,35 +12,45 @@ import { /** * @deprecated The Zod utilities will be removed in the next major release. */ -export function string(params?: Parameters[0]) { +export function string( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function number( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function bigint( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function boolean( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function date( + params?: Parameters[0], +): z.ZodPipe, z.ZodDate> { return z.preprocess(coerceDate, z.date(params)); } @@ -50,35 +60,48 @@ export function date(params?: Parameters[0]) { export function enum_( values: T, params?: Parameters[1], -) { +): z.ZodPipe< + z.ZodTransform, + 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[0]) { +export function file( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function email( + params?: Parameters[0], +): z.ZodPipe, 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[0]) { +export function url( + params?: Parameters[0], +): z.ZodPipe, 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: ( + shape: T, +) => z.ZodObject, z.core.$strip> = z.object; /** * @deprecated The Zod utilities will be removed in the next major release. @@ -86,6 +109,6 @@ export const object = z.object; export function array( element: T, params?: Parameters[1], -) { +): z.ZodPipe, z.ZodArray> { return z.preprocess(coerceArray, z.array(element, params)); } diff --git a/tsconfig.json b/tsconfig.json index d96ab6f..fd00300 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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 @@ -35,6 +35,7 @@ "jsx": "react-jsx", "verbatimModuleSyntax": true, "isolatedModules": true, + "isolatedDeclarations": true, "noUncheckedSideEffectImports": true, "moduleDetection": "force", "skipLibCheck": true