feat(errors): StandardSchemaV1 runtime validation + message-as-function (RFC 0001) - #59
Merged
Merged
Conversation
RFC 0001 captures the design decisions for issue #32 before any code lands.
…-as-function Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds cross-vendor parity coverage (zod 4, valibot 1, @ark/type 2) for the new standard-schema mode. All schemas expose '~standard' with version=1 and a vendor string. The runtime dispatch is exercised end-to-end through error(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…type, /contract-parity Each vendor gets a dedicated file under tests/integration/. The cross-vendor '~standard' contract check is also there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sibling files to integration/vendor-zod.test.ts, completing the one-file-per-vendor split. contract-parity.test.ts checks that each vendor exposes '~standard' with version=1 and a vendor string. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…olders Each validator now has its own folder (zod, valibot, arktype) with a vendor.test.ts. The cross-vendor contract-parity.test.ts stays flat at the integration root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The placeholder test asserted true === true and added no value. The warnLegacy path is covered indirectly by the existing legacy form tests (instantiating any legacy error triggers the warn-once path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Updates four docs pages to the new standard-schema mode: - message-templates: now leads with the function form (data) => string; legacy template form moved to a 'Legacy' section. - fields-schema: corrected valibot/arktype imports; documents runtime validation and ArgsValidationError. - error-factory: documents the function-form message and ArgsValidationError behavior. - api-reference: rewritten end-to-end. Adds the ArgsValidationError section, message-as-function signature, ErrorInstance methods addNote + from. Removes the broken title-template substitution that was rendering literally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…001) src/index.ts now exports ArgsValidationError so consumers can instanceof-check. tests/types/error-type.test.ts locks the public type contract for the new error() API: - inference from zod, valibot, @ark/type schemas - preserve transforms and zod brands in the message function - instance core shape, addNote and from chainability - legacy template form still type-checks - ArgsValidationError constructor signature Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Locks the runtime behavior on: async validators (loud error), throwing validators, non-conformant issues, circular values, schema call count, legacy template fallback, empty messages, function-message throws, deprecation gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tests/snapshots/exports.test.ts locks the list of named runtime exports and the runtime identity of ArgsValidationError. tests/perf/instantiate.bench.ts provides an informational vitest bench comparing: no fields, schema-validated factory, legacy template form. Not part of the regular test:run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both tests.yml and types.yml now run on a Node matrix instead of a single version. fail-fast is disabled so we see the full picture on any failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ve import vitest.config.ts now excludes tests/perf/ from the regular test:run so bench files do not break the suite. The bench can still be invoked via pnpm exec vitest bench. The bench file itself now uses the correct relative path (../../src/index.js). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Acorn parse fails on the embedded curly brace in the table cell. Use a simpler type reference (ReadonlyArray<StandardSchemaV1.Issue>) which avoids the inline object shape that triggered the expression parser. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements RFC 0001. Closes #32. The new API promotes the existing StandardSchemaV1 type to runtime validation and replaces the implicit message template with an explicit function form. The legacy template path keeps working in 1.x and emits a single deprecation warning per call site (silence with
DEESSEJS_ERRORS_LEGACY_TEMPLATES=1).Before / After
Defining an error
Handling the failure
What landed
error()now calls~standard-schema/validateat instantiation whenfieldsis provided. Invalid inputs throwArgsValidationErrorcarrying the validator's issues and vendor.fieldsis supplied,messagemust be a function(data) => stringreceiving the parsed (post-transform) data. The legacy string template still works and is deprecated.ArgsValidationError: new exported class withsource,vendor,issues. Re-exported fromsrc/index.tsso consumers caninstanceof-check.ErrorFactory.schemaremoved (decision A from the RFC). The duplication betweenfieldsandschemais gone.API surface
error(config):namerequired,fieldsoptionalStandardSchemaV1,messageoptionalstring | (data) => string,inheritsoptional.ArgsValidationError(name, issues, vendor): constructor signature.is(err, factory): type guard unchanged.raise(err): unchanged.causes(err): unchanged.StandardSchemaV1: re-exported from@standard-schema/spec.Test coverage (137 tests across 13 files)
tests/error.test.ts,tests/from.test.ts,tests/is.test.ts,tests/raise.test.ts,tests/causes.test.ts: existing unit tests, all pass.tests/standard-schema.test.ts: legacy and standard mode with mock schema.tests/types/error-type.test.ts:expectTypeOfagainst zod, valibot, arktype. Verifies inference ofz.coerce.number(),z.brand, instance core shape,addNote/fromchainability,ArgsValidationErrorcontract.tests/edge-cases.test.ts: async validators, throwing validators, non-conformant issues, circular values, call count, legacy template, deprecation gate.tests/snapshots/exports.test.ts: public API surface locked (5 runtime exports:ArgsValidationError, causes, error, is, raise).tests/integration/{zod,valibot,arktype}/vendor.test.ts: cross-vendor parity.tests/integration/contract-parity.test.ts:it.eachacross the three vendors on~standardconformance.tests/perf/instantiate.bench.ts: vitest bench for no-fields, schema-validated, and legacy forms. Excluded fromtest:runviavitest.config.ts; run withpnpm exec vitest bench.Documentation
apps/web/content/docs/{message-templates,fields-schema,error-factory,api-reference}.mdx: updated to the new API. Valibot and ArkType examples corrected (the previous versions imported from non-existent paths).README.md: refreshed to match the shared template; npm page now reflects the new description and SEO fields.apps/web/content/docs/recipes.mdx: still uses the older generic form; left for a follow-up PR to keep this one focused.Design doc in this PR
docs/internal/engineering/rfcs/0001-standard-schema-fields.md: full design doc with Q1-Q4 decisions. The "Decisions locked by review" section replaces the "Open questions" section.Issues opened for follow-up
CI matrix
Both
tests.ymlandtypes.ymlrun against Node 20, 22, 24 withfail-fast: false. Verified locally:pnpm test:run: 137 passedAll four CI jobs are green on the latest commit.
Out of scope
ErrorGrouptyped error union (issue [FEATURE] Add typed ErrorGroup with split() and exhaustive match() #33): depends on this RFC landing; will be a follow-up PR.recipes.mdx,index.mdx: queued as a follow-up.Checklist
expectTypeOftests/integration/<vendor>/console.warnCo-Authored-By: Claude Fable 5 noreply@anthropic.com