Commit Graph
17 Commits
Author SHA1 Message Date
Claude b2f2de2bd4 🔖 chore: 0.4.1 — the published README says the package is not on npm
The README inside a tarball is what npmjs.com renders, so the cereale package
page currently tells visitors: "npm install cereale does not resolve to this
library — the name is unclaimed on the registry." True when it was written,
nonsense on the page of the thing it describes.

Nothing could have prevented it. 0.4.0 was the first publish, so the docs could
only be corrected after the registry proved the claim wrong; the fix has to
ride a second version.

No code changes. Every emitted file is byte-identical to 0.4.0 except the
banner line of dist/cereale.min.js, which carries the version string — checked
by unpacking the published 0.4.0 tarball and diffing it against a fresh pack,
which is also why the changelog no longer claims dist/ is untouched. It said
that first, and it was wrong.

Patch rather than minor: no API surface moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-20 16:23:09 +00:00
Claude 4b910f19bf 🔍 fix: apply the code review — two visible regressions, and the gates behind them
Nine review angles, fourteen verified findings, all but the byte-table
generator applied. The two that mattered most were regressions of mine:

- a:hover repainted button-styled anchors, and in dark theme --accent-text
  equals --accent-solid — hovering the hero CTA drew its label in its own
  background colour. Verified invisible before (computed color == computed
  background) and distinct after: 10.39:1 dark, 6.90:1 light. The buttons and
  the skip link now re-assert their label colours on hover.
- Footer links had lost every non-hover affordance: the text-decoration:none
  carve-out plus body-coloured links left a 2.37:1 shade difference as the
  only cue. The carve-out is deleted — .nav-links a and .brand already
  declare none themselves — and the accent rule is back on the footer.

Also on the page: #ref-filter, the one text input, moves to --border-ui (it
still had the 1.68:1 border the token's own comment calls decorative);
focusable code surfaces get the --accent-on-code ring at -2px offset, inside
the .code overflow clip; #output .out-err drops #ff8095, the last surviving
colour of the deleted indigo palette; the two rgba(255,106,126) washes become
color-mix over --err-line so a grep for the token finds them.

The theme machinery loses a whole block: the dark media query is guarded with
:not([data-theme="light"]), so an explicit light toggle falls through to the
bare :root palette and the 21-token hand-copy in [data-theme="light"] is
gone. Verified in all four system/toggle combinations.

The page stops contradicting the repo: it claimed cereale/min "cannot
tree-shake — nothing left to shake" while src/treeshake.test.ts proves the
opposite on every run. Corrected here and in FRAMEWORKS.md, with the measured
figures (1,837 vs 1,996 bytes for one decorator). The release facts the page
was hand-bumping — both tgz names, "0.4.0 lives in the repository", the
sixty-eight — now fill from meta.js/the bundle like the version badge always
has.

The workflows close three holes:

- The docs sync gate was blind to NEW untracked build outputs (git diff does
  not report them; demonstrated). Both workflows now run check:docs-sync, one
  shared script that fails on anything porcelain reports — which also ends
  the copy-paste divergence between them.
- pages.yml deploys on CI succeeding on main (workflow_run) instead of on the
  push itself, so a deploy implies green tests, not just in-sync docs. The
  deploy job refuses refs other than main, closing the workflow_dispatch
  any-branch deploy, and the build job drops pages/id-token — npm postinstall
  scripts no longer run alongside an OIDC grant.
- The Junie action is pinned to the commit behind v1.7.4 rather than the tag,
  which is the immutability the previous comment promised but a mutable ref
  cannot deliver.

Verified: every fix confirmed in a rendered browser in both themes; 72
contrast pairs still pass; no overflow at 20 widths; 268 tests, build,
check:types, check:docs, actionlint all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-17 16:23:46 +00:00
Claude 5714fab42e 🧹 refactor: cut the duplicated checks and prose from the tree-shaking work
Acting on ponytail-review. The findings were about verification written twice
and comments restating the CHANGELOG, not about the fixes themselves.

- dist/cjs/package.json had two writers: the build script echoed it, then
  build-bundle.mjs rewrote it three lines later with the sideEffects entry.
  One writer now, the one that knows what belongs in it.
- Dropped the banner-version assert. Same process, same `pkg.version` going in
  and coming out — the "stale bundle" it claimed to catch cannot happen.
- Dropped the `includes('toInstanceSync')` text check. ci.yml imports the flat
  bundle and typeof-checks the export, which is the same claim actually tested.
- Dropped the treeshake case asserting annotations survive minification; the
  build already asserts it. Its one non-duplicated assertion was the floor on
  the expected count, and that gap was real: the build compared flat >= esm, so
  if tsc ever stopped emitting annotations both sides would read 0 and the
  assert would pass on nothing. Folded in as an explicit `expected < 30` check,
  verified by stripping the annotations and watching the build fail.
- Removed the `CEREALE` placeholder from treeshake.test.ts. A template language
  for one variable, with two no-op `.replace('CEREALE', 'unused')` calls left
  behind by it. Interpolated directly.
- Removed `.replace(/\.js$/, '.js')`, which was the identity function.
- Trimmed the comment above the `Symbol.metadata` install from 16 lines to 7,
  and the one above `minifySyntax` from 9 to 3, keeping the parts that are not
  written down anywhere else.

Also synced the CHANGELOG's byte table to the README's. The two had already
diverged in the webpack column — which is the duplication the review warned
about, showing up before anyone edited either on purpose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-06 03:47:29 +00:00
Claude 3bd190bde6 🌳 fix: keep the Symbol.metadata install through tree-shaking
`sideEffects` named metadata.js, the module holding the global install, but not the
barrel that re-exports it. A side-effect-free barrel is droppable whole, so every
bundler pruned the `export * from './metadata.js'` edge before metadata.js's own
marking was ever consulted, and the install vanished.

Measured on `import { configure } from 'cereale'`: 145 bytes through esbuild, 143
through webpack, 144 through rollup, with `Symbol.metadata` absent from all three.
It matters because tsc's decorator emit reads the well-known symbol directly —

    const _metadata = typeof Symbol === "function" && Symbol.metadata
      ? Object.create(null) : void 0;

so without it a decorated class is handed `metadata: undefined` and ends up with no
rules at all. Two places in the source promised this survived bundling; it did not.

index.js and index.ts are listed now. The cost is ~100 bytes and lands only on
imports that reach nothing else: the single-decorator, validateSync, toPlainSync,
toInstanceSync and whole-library cases are byte-identical before and after, across
all three bundlers. dist/cjs needs nothing — tsc emits `__exportStar(require(...))`,
an unconditional statement no bundler can drop.

Two new cases in treeshake.test.ts pin it, one on src and one on dist/esm, since
those are separate manifest paths and a typo in either is invisible from the other.
Both were checked by removing the entry and watching them fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 17:22:16 +00:00
Claude c48a106a05 🌳 fix: make cereale/min tree-shake too, and correct what I overclaimed
An audit of the last commit found two real problems and several claims of
mine that went further than the evidence.

**cereale/min was not tree-shakable.** scripts/build-bundle.mjs used
esbuild's `minify: true`, whose minifyWhitespace pass strips comments —
/*#__PURE__*/ annotations included. The published entry point therefore
reproduced exactly the bug the previous commit fixed: one decorator came
out at 5,066 bytes with all 26 unrelated rule messages, against 1,837 from
the per-module entry. Every source-level check stayed green, because they
all bundled src/ and the annotations are stripped on the way into dist/.

It is now minified for syntax and identifiers but not whitespace: 33.9 KB
raw and 9.6 KB gzipped against 26.0/8.7, so about a kilobyte over the wire
for a file that behaves correctly however it is used. One decorator via
cereale/min is now 1,996 bytes. The build asserts the annotation count
survives, and treeshake.test.ts now bundles the published artifact as well
as the source — the gap that let this through.

**sideEffects was partly inert.** `./dist/cjs/metadata.js` could never
match: the build writes dist/cjs/package.json, which becomes the nearest
descriptor for everything beneath it, so bundlers read sideEffects from
there. That file now carries its own declaration. `./src/metadata.ts` was
missing while src/ is published, which declared the Symbol.metadata install
droppable in the source tree. Five module-scope caches in utils.ts are
annotated for the same reason as the rules.

I checked the audit's third blocker — that the Symbol.metadata install is
dropped by bundlers — and it is not. It survives every case where it is
load-bearing (a decorator import, toPlainSync, modelOf, and a decorated
model bundled with an app). It is dropped only when importing nothing but
flattenErrors, which needs no metadata, so that is correct.

Corrections to my own wording:
- "all three bundlers land within 20 bytes" held only for the one-decorator
  row; larger imports differ by up to a few hundred bytes
- "measured through three bundlers, and pinned by a test" read as though the
  test covered all three; it covers esbuild, on source and on dist
- "every rule is a top-level call" — 30 of the 68 are
- the docs page "loads nothing from the network" — it fetches its own
  vendored compiler, same-origin, on first Run. It has no third-party
  dependencies, which is the claim I should have made
- cereale/min's size, everywhere it appears

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 16:41:32 +00:00
Claude b4f0657d09 📦 feat: add cereale/min, and a verified framework guide
**cereale/min** — the library flattened into one minified ES module,
25.5 KB / 8.6 KB gzipped, for import maps, <script type="module">, Deno
and Workers. Built from dist/esm/index.js, so the decorator lowering and
the ES2025 target are tsc's; esbuild only flattens and minifies.

It is an addition rather than a replacement, and the measurement is the
reason. Bundled through esbuild the flat and per-module builds land within
2 bytes of each other; through rollup + terser the flat one is 165 bytes
smaller; unused decorators tree-shake out of both. With the size argument a
wash, per-module stays the default import for the one thing it does better
— readable stack traces without source maps. (My first pass at that
measurement reported "shaken" for every symbol because both rollup builds
had failed and grep was reading missing files as absence. The check now
asserts the bundle is non-empty and that a *used* symbol is present, so it
can tell a real result from a broken harness.)

**FRAMEWORKS.md** — a recipe per framework, each one run before it was
written, with the versions and date verified against.

Angular works, which was not obvious: the CLI scaffolds
experimentalDecorators: true, but ngtsc erases @Component and @Injectable
into static properties rather than leaning on TypeScript's decorator emit.
Flip the flag and both systems coexist. Verified with ngc on Angular 21.2
with strictTemplates — templates still type-check and a wrong cereale rule
is still TS1240 inside the Angular build.

Next.js cannot work inline, structurally: it derives both the SWC parser's
decorator support and the transform mode from the one flag, so on gives
legacy emit and off makes @ a syntax error. NestJS cannot either — its DI
needs design:type from emitDecoratorMetadata.

Both have the same answer: keep the cereale classes in a package compiled
by tsc and import the built output. Verified inside a program with BOTH
legacy flags on, alongside @Injectable() — mapping and validation work, and
the compile-time guarantee still holds where the rules are written.

Also verified: Bun 1.3 needs no configuration, and a real Vite 8 build with
the plugin works where the same build without it silently leaves decorator
syntax in the bundle.

Version 0.4.0: cereale/min is a new public entry point, and cutting a minor
keeps the existing v0.3.0 tag meaningful instead of force-moving it onto a
commit it was never cut from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 16:00:26 +00:00
Claude fd42675d3d 🔗 docs: link the live site, now that Pages is confirmed
Pages serves docs/ from main, so the page rebuilt in #6 is live at
avalon-vanguard.github.io/cereale. The README pointed at the local file
because the URL could not be verified from here; it now links the site and
keeps the local instructions as the fallback. package.json homepage moves
there too — npm renders it as the package's headline link, and a live
playground is a better landing spot than an anchor inside the README.

Adds canonical and Open Graph tags. No og:image: a preview card with a
broken image is worse than one without, and there is no artwork yet.

check-docs.mjs flagged the canonical link as a remote subresource, which it
is not — the browser never fetches it. Rather than exempt the URL, the
check now looks at rel and only flags the relations that actually fetch or
connect. Verified it still catches a CDN stylesheet, a preconnect and a
script src; a check that cannot tell a declaration from a request is one
that gets switched off the first time it is wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 15:33:55 +00:00
Claude 9796d599dc 📦 chore: make the published package actually complete
`files` was ["dist"], but dist carries 256KB of .js.map and .d.ts.map
whose `sources` point at ../../src/*.ts — which was not published. Every
shipped sourcemap resolved to nothing: 44% of the tarball, dead weight.

The source is 116KB and its comments are the most detailed explanation of
why the engine does what it does, so it now ships (tests and the demo
excluded) and the maps resolve. Verified from a real `npm pack` install:
both utils.js.map and utils.d.ts.map now resolve to a file that exists, so
stepping into cereale in a debugger and "go to definition" from a decorator
both land in the real TypeScript.

Also: CHANGELOG.md ships; the repository/homepage/bugs URLs said
Avalon-Vanguard and only worked via GitHub's redirect, now lowercase to
match the org; publishConfig.access is explicit so a later move to a scoped
name cannot quietly attempt a private publish.

Adds a Publish to npm workflow, deliberately manual — pushing a tag does
not publish, because a tag is a decision to cut a release and publishing is
a decision to make it public and immutable. It asserts the tag exists and
points at the commit being published, refuses a version already on the
registry, runs the full verify gate, prints the file list, and defaults to
a dry run.

Checked end to end against the tarball: a strict consumer (no skipLibCheck,
no DOM lib) compiles and runs against both `cereale` and `cereale/vite`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 15:03:28 +00:00
Claude c828f5cfe9 🌾 feat: rebuild the landing page, and stop it from rotting again
The old page had been quietly broken for some time. It loaded
@babel/standalone from an **unpinned** CDN URL, which rolled over to Babel
8 and dropped the `proposal-class-properties` plugin the page asked for, so
Babel.transform threw before it ever reached the decorators — and the
decorator config it passed was `{ legacy: true }`, which 0.2.0 had already
made wrong. Nothing on the page said so. The copy was still selling the
0.1.0 pitch ("Spring-like"), listed about half the decorators, showed
`npm install cereale` for a package the registry returns 404 for, and
claimed "Zero overhead" against a README that publishes the real
microsecond costs.

The rebuild is one self-contained page: hand-written CSS, no Tailwind CDN,
no CodeMirror, and a vendored compiler pinned by package.json. It loads
nothing from the network. The playground runs the real bundled library
across six examples, all verified in a headless browser. The reference
covers all 68 decorators and the full API, counted from the bundle at
runtime so it cannot drift.

The hero's compiler error is not typed into the HTML. scripts/build-docs.mjs
compiles the snippets with the real tsc and writes the verbatim diagnostics
into docs/diagnostics.js, failing the build if a snippet the page calls a
compile error ever compiles — and two snippets that must compile guard
against the harness passing vacuously.

Three guards keep it honest, all wired into CI:
- check:docs fails on any remote subresource
- build:docs + git diff fails if docs/ is stale against src/
- check:types compiles a consumer against dist/ with no DOM lib, no
  @types/node and no skipLibCheck

That last one found a real packaging defect: `fromRequest` was declared as
taking the global `Request`, so cereale's own published .d.ts raised
"Cannot find name 'Request'" in any project whose lib and types did not
happen to supply it — inside a dependency, in code they may never call, and
unfixable from the outside. It now takes a structural JsonBody, which a
Request still satisfies. The library's own type tests had been hiding it by
enabling both DOM and skipLibCheck.

An adversarial review of the finished page caught four more: the lede
claimed *every* rule is type-checked (@IsDefined and @IsNotIn deliberately
are not), the guarantee section was wrong about the mechanism (a legacy
decorator does get design:type under emitDecoratorMetadata — the real claim
is about its type signature), one sample called a Movie method on a Media[]
and did not compile, and "nested objects come back as real classes" omitted
that you have to declare them. WCAG contrast was measured rather than
eyeballed: seven real failures fixed in the two themes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 10:03:16 +00:00
Claude 0938300477 🔊 feat!: make the three silent failures loud
Every change here answers one question: where does cereale currently fail
without saying so?

**Vite 8 / Vitest 4 drop decorators silently.** Both transform with oxc,
which does not implement the standard decorator transform and does not
report that. `vitest` prints "0 test" beside a bare SyntaxError, and
`vite build` reports success while emitting a bundle that throws on first
import. Ship the plugin that fixes it as `cereale/vite`, transforming with
esbuild and falling back to tsc — cereale depends on neither. The library's
own suite now runs through it, so it is exercised by every test.

**Legacy decorators died opaquely.** With `experimentalDecorators: true`,
still the default in most existing TypeScript projects, decorators are
invoked as (prototype, "name") and cereale raised "TypeError: Cannot convert
undefined or null to object". All decorators now resolve metadata through
one checkpoint that names the tsconfig setting instead, and reject
application to a method, getter or accessor field.

**Values JSON cannot carry were emptied.** A populated Map serialized to
{}, a Uint8Array to index-keyed noise, a bigint straight through so the
caller's own JSON.stringify threw somewhere unrelated. All now raise
JsonMappingError naming the property path and both ways out. Covers what a
@JsonSerialize serializer returns, sync or async. Circular-reference and
depth errors name the path too.

Also fixed: defineRule on a subclass with no decorators of its own wrote
the rule into its base class, because the base's metadata object is
inherited through the static prototype chain and `??=` found it non-nullish.

The README's toolchain table (tsc, esbuild, swc ✅, oxc ❌) is now executed
by a test rather than asserted, and the positioning leads with
class-validator + class-transformer, the stack cereale actually replaces,
rather than Zod, which it deliberately is not.

193 -> 249 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 09:11:32 +00:00
Claude d56c47d55c 🔖 chore: number the standard-decorator line 0.2.0, not 2.0.0
Nothing has been published to npm, so 2.0.0 claimed a 1.0.0 predecessor that no
one could install. Staying on 0.x states what is true — the API may still move —
and under semver a breaking change is then a minor bump, which is precisely the
relationship between the two lines: 0.1.x keeps legacy experimentalDecorators,
0.2.x moves to TC39 standard decorators.

The break itself is unchanged and still documented; only the number and the
references to the other line moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 08:04:28 +00:00
Claude 305be7a16f 🔒 fix: resolve the metadata symbol into a binding, not a per-use lookup
Review on #4 flagged that the Symbol.metadata polyfill is a module-level side
effect while package.json declares "sideEffects": false, so a bundler is
permitted to drop the module.

Checking it narrowed the concern and corrected half of it. The decorator
transforms are not exposed: esbuild's helper is

    __knownSymbol = (name, symbol) =>
      (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name)

which already falls back. The exposure was in this library's own read path,
which used `Symbol.metadata` directly. Had the symbol been absent,
`clazz[undefined]` would read a property literally named "undefined",
modelOf() would return an empty model, and every object would validate clean —
silent success, the worst failure mode a validation library can have.

The key is now resolved once into METADATA_KEY, with the same Symbol.for
fallback the transforms use, and all reads and writes go through it. The global
assignment stays for consumer emit that reads Symbol.metadata directly, and
package.json now lists metadata.js under sideEffects so bundlers keep it.

193 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-05 07:20:41 +00:00
Claude 297d3bfe77 ✨ feat!: v2 — strongly typed decorators on the TC39 standard
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
2026-08-04 20:35:26 +00:00
Claude 83ad2a289d 📝 docs: bring README, demo and playground in line with the code; release 0.1.0
The README described a library that did not exist in places. It showed
@ValidateNested({ each: true }), which did not compile; told users to enable
emitDecoratorMetadata, which the library never reads; and documented none of the
mapping API. Its Quick Start now runs verbatim — verified by compiling and
executing it against the local source.

- README: document field-name mapping, access control, options, error helpers
  and the 30 new validators; drop the emitDecoratorMetadata instruction; add a
  Notes and Limitations section covering circular references, validate() on
  plain objects, and the fact that @JsonProperty stops the original name from
  being accepted unless you add @JsonAlias
- CHANGELOG.md: new, covering 0.1.0
- example.ts: rewritten as a tour of the current API — read-only ids, write-only
  secrets, renamed fields, conditional validation, flattened errors, and a
  base-class rule reaching a subclass
- docs: the playground hand-listed its symbol table in three parallel places and
  exposed IsEmail, which is not an export. It now derives scope from the bundle,
  so new decorators work there as soon as they ship. Bundle regenerated
- version 0.1.0

136 tests, 97% statement and 100% function coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-03 23:53:09 +00:00
Claude 66e19f690f 🔧 fix: repair test, build and CI infrastructure
The vitest suite silently ran zero tests: vitest 4 transpiles with oxc, which
does not pick up `experimentalDecorators` from a tsconfig that excludes the
files being transformed, so every decorator-using suite failed to parse and was
reported as "0 test". A vitest.config.ts enabling legacy decorators brings all
40 existing tests back to life.

- Add vitest.config.ts (oxc legacy decorators + v8 coverage config)
- Type-check test files: move the test/demo exclusions from the base tsconfig
  onto the two build configs, and fix the strict-mode errors this surfaced
- Stop shipping src/example.ts in dist (it invokes runExample() at import time,
  a side effect in a package declaring "sideEffects": false)
- CI: run lint, tests with coverage, build and entry-point smoke checks; drop
  EOL Node 18, add Node 24; commit package-lock.json so `npm ci` works
- Add `verify`, `test:watch` and `build:docs` scripts, and an engines field;
  `build:docs` regenerates the previously hand-maintained docs/cereale.js

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
2026-08-03 23:14:16 +00:00
Enzo Marioni fa75147e4e ✨ feat: implement core Cereale library for JSON mapping and validation
- 🎨 add Spring-like decorators (@JsonSerialize, @JsonDeserialize, etc.)
- ⚙️ implement JsonMapper and metadata storage for transformations
- 🧪 add comprehensive test suite using Vitest
- 📝 add README, CONTRIBUTING, and API documentation
- 👷 setup GitHub Actions CI workflow
- 🔧 configure TypeScript and project settings
2026-04-18 19:33:19 +02:00
Enzo Marioni 7fe7e8c7c3 ✨ feat: implement core Optimus library for JSON mapping and validation
- 🎨 add Spring-like decorators (@JsonSerialize, @JsonDeserialize, etc.)
- ⚙️ implement JsonMapper and metadata storage for transformations
- 🧪 add comprehensive test suite using Vitest
- 📝 add README, CONTRIBUTING, and API documentation
- 👷 setup GitHub Actions CI workflow
- 🔧 configure TypeScript and project settings
2026-04-11 16:58:12 +02:00