BREAKING CHANGE: cereale moves from legacy `experimentalDecorators` to TC39
standard decorators, which is what makes validation rules type-checked against
the fields they are attached to.
class User {
@IsString() name!: string; // fine
@IsString() age!: number; // Type 'number' is not assignable to 'string'
}
Legacy decorators receive (target: any, key: string) and lose the field type
entirely, so this was impossible in v1. Standard decorators receive
ClassFieldDecoratorContext<This, Value>, which carries it. Rules now checked:
scalar rules against scalar fields; { each: true } against arrays, in both
directions; @JsonType against the field's class; @JsonSerialize/@JsonDeserialize
against the field's type; @IsIn and @IsEnum against the field's value type.
17 tests invoke the real compiler to assert the wrong code stays rejected — a
guarantee nobody checks is one that quietly stops holding.
Positioning follows the capability: validated domain objects, not validated
data. The README now leads with the Zod comparison. Cereale does not infer your
type from a schema — you still write the field type and the rule — but it
guarantees the two cannot disagree, which is what class-validator never offered.
Removed
- metadata-storage.ts and its WeakMap singleton. Metadata lives on
context.metadata now, which also removes the dual ESM/CJS double-singleton
hazard. Inheritance merging becomes structural rather than reconstructed on
every read, so the subclass-shadowing defect fixed by hand in 0.1.0 cannot
reoccur by construction.
- registerDecorator, replaced by defineRule(Class, 'field', constraint).
Unchanged: the engine, options, naming strategies, access control, error
helpers, the sync API, and the performance work. 193 tests pass.
Toolchain note: standard decorators are transformed by tsc and esbuild, but not
yet by oxc. The library builds with tsc and consumers on esbuild/Vite are fine;
Vitest 4 uses oxc, so the test runner needs an esbuild transform plugin. This is
recorded in vitest.config.ts and the README, and is the reason 1.x should stay
available for oxc-based toolchains.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import { defineConfig } from 'vitest/config';
|
|
import { transform } from 'esbuild';
|
|
|
|
/**
|
|
* Transpiles test sources with esbuild instead of oxc.
|
|
*
|
|
* Vitest 4 transforms with oxc, which does not yet implement the TC39 standard decorator
|
|
* transform — it leaves the syntax in place and Node then fails to parse it, reporting
|
|
* "0 test" rather than an error. esbuild and tsc both implement it, so the library's own
|
|
* build (`tsc`) and consumers bundling with esbuild or Vite are unaffected; only the test
|
|
* runner needs this. Remove it once oxc gains standard-decorator support.
|
|
*/
|
|
function standardDecorators() {
|
|
return {
|
|
name: 'cereale:standard-decorators',
|
|
enforce: 'pre' as const,
|
|
async transform(code: string, id: string) {
|
|
if (!/\.ts$/.test(id) || id.includes('node_modules')) return null;
|
|
const result = await transform(code, {
|
|
loader: 'ts',
|
|
target: 'es2022',
|
|
sourcefile: id,
|
|
sourcemap: true,
|
|
// Standard semantics, not the legacy ones: the library reads context.metadata.
|
|
tsconfigRaw: { compilerOptions: { experimentalDecorators: false, useDefineForClassFields: true } },
|
|
});
|
|
return { code: result.code, map: result.map };
|
|
},
|
|
};
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins: [standardDecorators()],
|
|
test: {
|
|
include: ['src/**/*.test.ts'],
|
|
coverage: {
|
|
provider: 'v8',
|
|
reporter: ['text', 'lcov'],
|
|
include: ['src/**/*.ts'],
|
|
exclude: ['src/**/*.test.ts', 'src/example.ts', 'src/index.ts'],
|
|
},
|
|
},
|
|
});
|