Migrating to Valchecker 1.0
0.0.33 establishes the intended 1.0 compatibility contract ahead of the 1.0 version itself. Applications upgrading from earlier releases should review the breaking and newly formalized behavior below rather than relying on a successful TypeScript build as the whole migration test.
The repository's complete migration guide remains the exhaustive issue-code mapping, removed-export list, and verification checklist. This page is the reader-facing migration path through the most important decisions.
Migration map
| Area | What to review | Canonical destination after migration |
|---|---|---|
| Runtime and packaging | Node.js 22+, ESM-only packages, dynamic CommonJS import | 1.0 Contract |
| Step names and signatures | isXxx / toXxx naming, trailing options objects, removed legacy aliases | API Reference |
| Numeric and loose primitive policy | number() includes NaN/infinities; loose primitives follow template-literal-compatible strings | Primitives |
| Execution | sync/maybe-async preservation, PromiseLike, .toAsync() | Sync and Async |
| Results and issues | structured operation/internal categories, renamed codes/payload fields, non-mutating paths | Issues and Paths |
| Structures | own-property object semantics, strict/loose behavior, union/intersection changes | Structured Data |
| Extension API | public root exports, plugin-name restrictions, callback/issue typing | Extending Valchecker |
Required migration review
- Runtime support is Node.js 22 or newer.
- Published packages are ESM-only; CommonJS uses dynamic
import(). - Built-in validations now use
isXxxnames, concrete transformations usetoXxx, and genericcheck()/transform()retain their names. - Numeric
min()/max()becomeisAtLeast()/isAtMost(); length bounds becomeisLengthAtLeast()/isLengthAtMost(). empty(),integer(),startsWith(), andendsWith()becomeisEmpty(),isInteger(),isStartingWith(), andisEndingWith().parseJSON(),stringifyJSON(), andtoSplitted()becometoJSONValue(),toJSONString(), andtoSplit().number()now matches the JavaScript/TypeScriptnumberidentity, includingNaNand positive or negative infinity. AddisFinite()where finite values are policy.- Loose primitives normalize only the primitive or strings accepted by the corresponding TypeScript template-literal primitive model; they are not unrestricted JavaScript coercion.
execute()preserves sync or maybe-async behavior; use.toAsync()for an unconditional native promise.check(),transform(), andfallback()acceptPromiseLikecallback results for their documented asynchronous operation.union()returns the first successful branch's transformed output.intersection()uses graph-aware plain-object composition and rejects incompatible distinct non-plain instances.- Object validators read declared own properties only;
strictObject()includes unknown enumerable symbol keys;looseObject()preserves unknown own properties. - Issue-path prepending does not mutate child issues.
- Plugin methods cannot collide with core names or use
thenor symbol names. - Callback exceptions in
check(),transform(),toFiltered(), andtoSorted()use their step-specificoperationissues. - Accidental implementation helpers that were never intended as supported root exports have been removed.
For the complete step-by-step list, including JSON serialization changes, mapped-boolean payloads, literal() equality, conversion issue categories, and every issue-code rename, use MIGRATION.md rather than duplicating that leaf-level catalog here.
Rename fluent methods
Before:
v.string()
.min(3)
.max(20)
.startsWith('user_')After:
import { v } from 'valchecker'
v.string()
.isLengthAtLeast(3)
.isLengthAtMost(20)
.isStartingWith('user_')Do the rename by semantics, not by prefix alone. Use each method's generated Reference entry when an old call mixed a semantic operand with a positional message or other removed signature shape.
Make numeric policy explicit
number() is an identity check, not a finite-number policy. Code that previously relied on an implicit finite restriction must add the validation it actually needs:
import { v } from 'valchecker'
const count = v.number()
.isFinite()
.isInteger()
.isAtLeast(0)The same rule applies when a conversion can produce a value your domain does not accept: conversion and validation are separate pipeline responsibilities.
Review issue consumers, not only schemas
Message maps, snapshots, API responses, and form adapters can break even when schema construction still typechecks. Review code that switches on issue codes or reads payload fields.
Important migration classes include:
- numeric bounds use explicit
minimum/maximumpayload fields; - length bounds use
minimumLength/maximumLength; isAtLeast()andisAtMost()each share one issue code across number/bigint variants while retaining discriminated payload unions;toJSONString:serialization_failed,toNumber:conversion_failed, andtoBigint:conversion_failedareoperationissues;check(),transform(),toFiltered(), andtoSorted()use their documented step-specific callback failure code instead of a generic validation/core error.
Use Custom Messages and Error Responses for application handling patterns and the generated step entry for the exact current payload.
Review asynchronous call sites
Do not assume every schema with an async-capable step always returns a promise. An early synchronous failure may prevent asynchronous work from being reached.
Use await schema.execute(input) when either reached mode is acceptable. Append .toAsync() when an API boundary requires an unconditional promise. See Synchronous and Asynchronous Execution for the complete model.
Verify structural output assumptions
Re-test code that depended on unknown object properties, prototype inheritance, symbol keys, branch output selection, or intersection merging. These are observable output contracts, not implementation details.
Prefer task-level tests that assert the output your application consumes. Use Structured Data for composition patterns and the generated Structures Reference for exact per-step behavior.
Finish with the exhaustive checklist
Before declaring a migration complete:
- run the repository/application typecheck against the upgraded packages;
- execute representative success and failure paths, including issue consumers;
- review every item in
MIGRATION.md; - compare any relied-on formal guarantee with the Valchecker 1.0 Contract;
- verify published-package behavior in the same module/runtime environment used in production.
Reporting a problem
Report a problem with:
- exact Valchecker version;
- Node.js and TypeScript versions;
- module resolution mode;
- minimal schema and input;
- actual and expected result;
- whether execution used
execute()or~standard.
Fixes are published under new versions; existing npm versions are never overwritten.