Skip to content

feat(temporal-zod): support custom validation errors (#54) - #69

Open
macalinao wants to merge 1 commit into
masterfrom
feat/custom-errors
Open

feat(temporal-zod): support custom validation errors (#54)#69
macalinao wants to merge 1 commit into
masterfrom
feat/custom-errors

Conversation

@macalinao

@macalinao macalinao commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Closes #54.

Summary

Adds custom validation errors to temporal-zod via an .error() method on every validator — non-breaking, so existing code using the validators directly keeps working:

import { zPlainDate } from "temporal-zod";

// unchanged — default error
z.object({ date: zPlainDate });

// customized — takes Zod's `error` param (string or error-map fn)
z.object({ date: zPlainDate.error({ error: "Invalid date" }) });
z.object({ date: zPlainDate.error({ error: (issue) => `Bad: ${issue.input}` }) });

The custom error surfaces for any invalid input (malformed string or wrong type), works on both the coercing and instance validators, on the main entry and temporal-zod/base.

Implementation notes

  • temporalValidators(cls, { extraInputs?, error? }) threads error into the z.union + z.instanceof — which is what Zod 4 actually surfaces (the union masks the inner transform message), so the default error is unchanged ("Invalid input").
  • withError() augments a schema with a chainable .error() method via Object.assign. Verified safe: z.string().error is undefined (no collision), the augmented schema parses correctly in z.object/z.union, and z.toJSONSchema is unaffected.
  • json-schemas.ts reuses each base validator's own .error() to rebuild, then re-applies JSON Schema metadata. The default keeps its registered $def; a custom-error variant inlines an identical schema (a distinct instance can't share the registered id — dedup is just an optimization).
  • New public types: TemporalError, TemporalErrorParams, WithError, TemporalValidator, TemporalInstanceValidator.

Verification

  • bun run build ✅ · bun run lint ✅ · bun test114 pass / 0 fail
  • Coverage added for string + function errors, nesting in z.object, unchanged default messages, and preserved JSON-Schema metadata. Existing tests use the validators directly (unchanged), confirming non-breaking.

Versioned as a minor bump with a changeset (new feature, non-breaking).

Note on PR #68

The in-flight oxlint/oxfmt migration (#68) also edits temporal-validator.ts. This branch is off current master (Biome/ESLint); whichever merges first, the other needs a trivial rebase. The new code is written to satisfy both lint setups.

Add an .error({ error }) method to every z<Type>/z<Type>Instance validator
that returns a copy with a customized error, taking Zod's params object
({ error } — a string or error-map function). Non-breaking: the plain
validators are unchanged and used directly as before.

- temporalValidators(cls, { extraInputs?, error? }) threads error into the
  z.union + z.instanceof (what Zod 4 actually surfaces; the default error is
  unchanged at "Invalid input")
- withError() augments a schema with a chainable .error() via Object.assign
  (verified safe: no collision, parses in z.object, toJSONSchema intact)
- json-schemas reuses each base validator's .error() to rebuild, re-applying
  JSON Schema metadata (default keeps its $def; custom variants inline an
  equivalent schema)
- New public types: TemporalError, TemporalErrorParams, WithError,
  TemporalValidator, TemporalInstanceValidator
- Tests cover string/function errors, nesting, unchanged defaults, and
  JSON-Schema metadata; README + minor changeset updated
@macalinao
macalinao force-pushed the feat/custom-errors branch from 335ce96 to 8503d33 Compare June 29, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support custom errors

1 participant