diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a581e16..250afa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,5 +37,16 @@ jobs: run: | node --input-type=module -e "import * as m from './dist/esm/index.js'; if (typeof m.toInstance !== 'function') throw new Error('ESM entry point broken');" node --input-type=commonjs -e "const m = require('./dist/cjs/index.js'); if (typeof m.toInstance !== 'function') throw new Error('CJS entry point broken');" + node --input-type=module -e "import { standardDecorators } from './dist/esm/vite.js'; if (standardDecorators().enforce !== 'pre') throw new Error('ESM cereale/vite broken');" + node --input-type=commonjs -e "const { standardDecorators } = require('./dist/cjs/vite.js'); if (standardDecorators().enforce !== 'pre') throw new Error('CJS cereale/vite broken');" - name: Run Demo run: npm run demo + - name: Published types stand alone + run: npm run check:types + - name: Landing page loads nothing from the network + run: npm run check:docs + - name: Landing page bundle is in sync with src/ + run: | + npm run build:docs + git diff --exit-code -- docs/ \ + || (echo "docs/ is stale — run 'npm run build:docs' and commit the result" && exit 1) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98065b4..9b1e63d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,167 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - 2026-08-05 + +Every change here comes from the same question: where does cereale currently fail *quietly*? +Three answers, each of which cost a real user nothing to hit and everything to diagnose. + +### Vite 8 and Vitest 4 silently drop decorators — `cereale/vite` + +Both transform TypeScript with oxc, which does not implement the standard decorator transform +and does not say so. It leaves the syntax in the output, so: + +- `vitest` reports `0 test` next to a bare `SyntaxError` +- `vite build` reports **success**, having emitted a bundle that throws the moment it is imported + +Cereale now ships the plugin that fixes it: + +```ts +// vite.config.ts / vitest.config.ts +import { standardDecorators } from 'cereale/vite'; + +export default defineConfig({ plugins: [standardDecorators()] }); +``` + +It transforms with esbuild, falling back to the TypeScript compiler; cereale depends on +neither, and says which to install if somehow neither is present. Options: `include`, +`target`, and `transformer` to pin one deliberately. The library's own test suite runs +through it, so it is exercised by every test rather than by one test about itself. + +### Legacy decorators now say so + +With `experimentalDecorators: true` — still the default in most existing TypeScript projects, +because class-validator required it — decorators are invoked as `(prototype, "name")` and +cereale died with `TypeError: Cannot convert undefined or null to object`, which names neither +the cause nor the fix. Every decorator now resolves its metadata through one checkpoint that +raises an error naming the tsconfig setting instead. The same checkpoint rejects application +to a method, getter or `accessor` field, all of which previously recorded metadata that +nothing would ever read. + +### Values JSON cannot carry are refused, not emptied + +A populated `Map` serialized to `{}`. A `Set` serialized to `{}`. A `Uint8Array` to +`{"0":1,"1":2}`. A `bigint` passed straight through, so the caller's own `JSON.stringify` +threw somewhere unrelated. `RegExp`, `Error`, `Promise`, `WeakMap`, `DataView`, symbols and +functions all had their own version of the same failure. All of them now raise a +`JsonMappingError` that names the property path and the two ways out: + +``` +JsonMappingError: lines[1].tags[0] is a Set, which cannot be serialized to JSON. +Give the property a @JsonSerialize() serializer that converts it, or drop it from the +output with @JsonIgnore(). +``` + +The check also covers what a `@JsonSerialize` serializer hands back, sync or async. This is +**breaking** for anyone relying on the old behaviour, though "relying on" is a strong word for +losing data without being told. + +Circular-reference and depth-limit errors now name the path too (`at child.parent`), which +came free with the bookkeeping. + +### 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. Every sibling subclass then inherited a rule meant for one + of them. +- The plugin's TypeScript path emitted a `//# sourceMappingURL=` comment pointing at a file + nobody wrote, which Vite followed and failed to read on every transformed module. +- `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 — an error inside a dependency, in code the consumer may never call, + that they could not fix from the outside. It now takes a structural `JsonBody` + (`{ json(): Promise }`), which a `Request` still satisfies. The library's own type + tests had been hiding this by enabling both `DOM` and `skipLibCheck`; `npm run check:types` + now compiles a consumer against `dist/` with neither. + +### The landing page + +`docs/index.html` was rebuilt. Its playground had been dead for some time and said nothing +about it: the page 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. The copy was still selling +the 0.1.0 pitch ("Spring-like"), listed about half the decorators, 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, which +`npm run check:docs` now enforces in CI. The playground runs the real bundled library across +six examples; the reference lists 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 +snippet with the real `tsc` and writes the verbatim diagnostic into `docs/diagnostics.js`, +failing the build if a snippet the page calls a compile error ever compiles. Two more snippets +that must compile guard against the harness passing vacuously. + +### Corrected + +The README's toolchain table said esbuild takes "the same settings via `tsconfigRaw`". It does +not: esbuild lowers standard decorators only when its **own top-level `target`** is below +`esnext`. A `target` inside `tsconfigRaw` sets the `useDefineForClassFields` default and +nothing else, so following that advice leaves decorator syntax in the output — the same silent +passthrough the section blames on oxc. Both the table and the landing page now say so, and +`src/toolchain.test.ts` asserts both halves, so the trap is documented by a test rather than by +a sentence. + +Also corrected in the same pass: the toolchain table is described as executed by a test, but +the oxc row — the only ✗ — cannot be, because oxc ships inside a native binary with no +standalone transform API. The claim now covers the three rows it actually covers. + +### A rename now actually takes effect + +**Breaking.** The docs said that once a property carries `@JsonProperty`, its original name +"is no longer accepted on input". It stopped being *mapped*, but it was not refused: unlike +`@JsonReadOnly`, whose JSON name goes into the blocked set, a renamed property's old key fell +through to the unknown-key policy, and the default `allow` copied it onto the instance +untouched. The value landed on a declared property having skipped everything declared for it — +no `@JsonType` conversion, so `@ValidateNested` then inspected a plain object with no model and +reported nothing. A payload aimed at the previous version of a class was accepted in part, in +silence. + +Names that no longer reach their property are now refused. That covers three routes to the +same hole: + +- the property key of a field renamed with `@JsonProperty` +- the raw key of a field a naming strategy renders differently (`firstName` under `snake_case`) +- the property key of a field that is both renamed and `@JsonReadOnly`, which was still + settable under its own key + +Refused, not silently swallowed. A stale name is a mismatch with whatever produced the payload +rather than a deliberate refusal like `@JsonReadOnly`, so `unknownKeys: 'error'` still reports +it — and now says which property it was reaching for and what that property is called now: + +``` +JsonMappingError: "ref" is not a JSON name for Order: property "ref" is mapped to +"order_ref". Send that name, or add @JsonAlias("ref") to keep accepting this one. +``` + +`@JsonAlias` remains the way to keep an old name working, and a key that some *other* property +legitimately answers to is still mapped to that property. + +Two reference entries on the landing page were also imprecise: `@IsNotEmpty()` and `@IsEmpty()` +read as complements but are not (`[]` and `{}` pass both), and `unknownKeys` is +deserialization-only. + +### Positioning + +`zod-alternative` is out of the keywords, and the README leads with the comparison that +actually applies: cereale replaces **class-validator + class-transformer**. It does not infer +types from schemas, and framing it against Zod invited exactly the objection that it is +missing `z.infer` — which is a different design, not a gap. + +The README's toolchain support table (`tsc`, esbuild, swc ✅, oxc ❌) is now +[executed by a test](src/toolchain.test.ts): each row compiles a decorated class with that +tool and asserts the metadata arrived, so the table cannot quietly go stale. + +### Performance + +Serialization is a few percent slower for the representability check. Primitives are handled +inline, and arrays and dates skip it, so it costs one `Symbol.toStringTag` read per object. +Validation is unchanged. + ## [0.2.0] - 2026-08-04 > The project stays on 0.x while nothing has been published: under semver that signals the diff --git a/README.md b/README.md index 6666c63..a2f8902 100644 --- a/README.md +++ b/README.md @@ -25,22 +25,34 @@ const user = fromJsonSync(User, body); // a real User user.greet(); // your methods are still there ``` -## Why not Zod? +`docs/index.html` is a self-contained page with an interactive playground that runs this +library in the browser. Build its assets with `npm run build:docs` and open the file — it +loads nothing from the network. -Zod is excellent, and if a plain validated object is what you want, use it. The difference is -what you get back: +## Where it fits + +The stack Cereale replaces is **class-validator + class-transformer**: + +| | class-validator + class-transformer | Cereale | +| --- | --- | --- | +| Packages to install | 2, plus `reflect-metadata` | 1, no runtime dependencies | +| Decorators | legacy (`experimentalDecorators`) | TC39 standard | +| Rules checked against the field | no — `@IsInt() name: string` compiles | **yes, at compile time** | +| Mapping and validation | two libraries that must agree | one model | + +The comparison people ask about is **Zod**, and it is worth being precise about, because +Cereale is not a drop-in for it: | | Zod | Cereale | | --- | --- | --- | | Result of parsing | an anonymous object matching a schema | an instance of **your class** | | Methods, getters, inheritance | none — data only | preserved | | Where the type comes from | inferred from the schema | your class declaration | -| Rules checked against the type | not applicable — schema *is* the type | **yes, at compile time** | | Bidirectional mapping (renaming both ways) | not the focus | first-class | -Cereale does not infer your type from a schema, so you still write the field type and the rule. -What it guarantees is that **the two cannot disagree** — `@IsInt() name!: string` does not -compile. That is the guarantee class-validator has never offered. +Cereale does **not** infer your type from a schema. You write the field type and the rule, and +what it guarantees is that **the two cannot disagree** — `@IsInt() name!: string` does not +compile. If you want `z.infer`, you want Zod; that is a different design, not a missing feature. Reach for Cereale when your domain model is already a class — a NestJS provider, a TypeORM entity, anything with behaviour attached. Reach for Zod when you just want the data. @@ -51,6 +63,8 @@ entity, anything with behaviour attached. Reach for Zod when you just want the d - **Real instances:** nested objects, polymorphic subtypes and arrays all come back as classes. - **Field-name mapping:** `@JsonProperty`, `@JsonAlias` and naming strategies, both directions. - **Access control:** keep passwords out of responses and server-owned ids out of requests. +- **Nothing fails quietly:** a misconfigured compiler, a cycle, or a value JSON cannot carry + raises an error that names the cause — never an empty object. - **Sync and async:** every entry point has a synchronous twin. - **Zero dependencies**, ESM + CJS, Node 20+. @@ -60,7 +74,7 @@ entity, anything with behaviour attached. Reach for Zod when you just want the d npm install cereale ``` -Cereale v2 uses **TC39 standard decorators**, so no `experimentalDecorators` flag: +Cereale uses **TC39 standard decorators** (since 0.2.0), so no `experimentalDecorators` flag: ```json { @@ -74,10 +88,46 @@ Cereale v2 uses **TC39 standard decorators**, so no `experimentalDecorators` fla Requires TypeScript 5.2+ and Node 20+. `reflect-metadata` is not needed and `emitDecoratorMetadata` is not read. -> **Toolchain note.** Standard decorators are transformed by `tsc` and by esbuild (so Vite -> works). They are **not** yet transformed by oxc; if your toolchain uses it, decorator syntax -> will fail to parse. The 0.1.x line, which uses legacy decorators, remains available for -> those setups. +`experimentalDecorators` must be **off**. The two decorator systems cannot coexist in one +program, so a project that still needs legacy decorators for another library cannot use +Cereale yet. If yours is configured for them, you get an error saying exactly that rather +than a `TypeError` from somewhere inside the engine. + +### Toolchain support + +Whether Cereale works at all depends on your compiler emitting standard decorators, so the three +✅ rows are [checked by a test](src/toolchain.test.ts) rather than asserted here — each compiles a +decorated class with that tool and asserts the metadata arrived. The ❌ row cannot be: oxc ships +inside a native binary with no standalone transform API. + +| Transformer | Status | Notes | +| --- | --- | --- | +| `tsc` | ✅ | With `experimentalDecorators: false` and `target: ES2022`+ | +| esbuild | ✅ | `experimentalDecorators: false` via `tsconfigRaw`, **plus** esbuild's own top-level `target: es2022`. Its default `esnext` target leaves decorator syntax in the output | +| swc | ✅ | `jsc.transform.decoratorVersion: "2022-03"` | +| **oxc** | ❌ | Used by **Vite 8** and **Vitest 4** — see below | + +**If you are on Vite 8 or Vitest 4**, oxc leaves decorator syntax in the output without +reporting anything: `vitest` prints `0 test` next to a bare `SyntaxError`, and `vite build` +reports success while emitting a bundle that throws the moment it is imported. Cereale ships +the plugin that fixes it: + +```ts +// vite.config.ts / vitest.config.ts +import { defineConfig } from 'vite'; +import { standardDecorators } from 'cereale/vite'; + +export default defineConfig({ + plugins: [standardDecorators()], +}); +``` + +It transforms `.ts`, `.mts` and `.cts` outside `node_modules` with esbuild, falling back to +the TypeScript compiler if esbuild is not installed — Cereale depends on neither. Pass +`include` to widen or narrow the set (decorated classes in `.tsx` files need this), +`transformer: 'esbuild' | 'typescript'` to pin one, or `target` to change the output level +from the default `es2022`. Nothing in the plugin is specific to Cereale; delete it once oxc +implements the transform. ## Quick Start @@ -424,6 +474,11 @@ against `JSON.parse` + `JSON.stringify` (5.8 us) on the same machine: If you validate at the edge and map internally afterwards, `{ validate: false }` skips the dominant cost. +Serialization also checks every value it walks against the set JSON cannot represent. That +costs a few percent on `toPlain`, which is the price of never emitting `{}` where a `Map` +used to be; primitives are handled inline and the check is skipped for arrays and dates, so +it is one `Symbol.toStringTag` read per object. + ## Notes and Limitations - **Rules are checked, types are not inferred.** You write both the field type and the rule; @@ -431,15 +486,36 @@ dominant cost. model, not this one. - **`abstract` fields cannot be decorated.** Standard decorators do not apply to abstract members. Declare the field concretely in the base class instead. -- **oxc does not transform standard decorators yet.** `tsc` and esbuild do. +- **`accessor` fields cannot be decorated.** Their value lives in a private slot that mapping + and validation cannot reach. Applying a decorator to one is an error, not a silent no-op. +- **oxc does not transform standard decorators yet.** `tsc`, esbuild and swc do — see + [Toolchain support](#toolchain-support) for the Vite/Vitest plugin. +- **Values JSON cannot carry are rejected**, not quietly dropped. `Map`, `Set`, `RegExp`, + `Error`, typed arrays, `bigint`, `symbol` and functions all raise a `JsonMappingError` naming + the property path: -- **Circular references** are rejected during serialization with a `JsonMappingError`. Break - the cycle with `@JsonIgnore()` on the back-reference. + ``` + JsonMappingError: lines[1].tags[0] is a Set, which cannot be serialized to JSON. + Give the property a @JsonSerialize() serializer that converts it, or drop it from the + output with @JsonIgnore(). + ``` + + Serializing a populated `Map` to `{}` and returning success is the failure mode this + library exists to prevent, so it does not do it either. +- **Circular references** are rejected during serialization with a `JsonMappingError` that + names where the cycle closed. Break it with `@JsonIgnore()` on the back-reference. - **`validate()` on a plain object** returns no errors: rules live on the class, so validate the instance you get back from `toInstance`, not the raw payload. - **Renaming is not backwards-compatible by itself.** Once a property carries - `@JsonProperty`, its original name is no longer accepted on input — add `@JsonAlias` to - keep older clients working. + `@JsonProperty`, its original name no longer reaches it — and is refused rather than copied + onto the instance behind the rename's back. Add `@JsonAlias` to keep older clients working. + Under `unknownKeys: 'error'` the stale name is reported along with what the property is + called now: + + ``` + JsonMappingError: "ref" is not a JSON name for Order: property "ref" is mapped to + "order_ref". Send that name, or add @JsonAlias("ref") to keep accepting this one. + ``` ## Contributing diff --git a/docs/cereale.js b/docs/cereale.js index fcc7f59..1806bc7 100644 --- a/docs/cereale.js +++ b/docs/cereale.js @@ -1,2 +1,2 @@ -"use strict";var Cereale=(()=>{var L=Object.defineProperty;var In=Object.getOwnPropertyDescriptor;var An=Object.getOwnPropertyNames;var Nn=Object.prototype.hasOwnProperty;var Mn=(n,e)=>{for(var t in e)L(n,t,{get:e[t],enumerable:!0})},Pn=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of An(e))!Nn.call(n,r)&&r!==t&&L(n,r,{get:()=>e[r],enumerable:!(o=In(e,r))||o.enumerable});return n};var Rn=n=>Pn(L({},"__esModule",{value:!0}),n);var gt={};Mn(gt,{Allow:()=>Yn,ArrayContains:()=>Ye,ArrayMaxSize:()=>He,ArrayMinSize:()=>Ge,ArrayNotContains:()=>Qe,ArrayNotEmpty:()=>Ze,ArrayUnique:()=>Xe,Contains:()=>Fe,Email:()=>Oe,EndsWith:()=>Ue,Equals:()=>Be,IsAlpha:()=>Ce,IsAlphanumeric:()=>Te,IsArray:()=>We,IsBigInt:()=>re,IsBoolean:()=>oe,IsDate:()=>ae,IsDateString:()=>Ee,IsDefined:()=>se,IsDivisibleBy:()=>me,IsEmpty:()=>ue,IsEnum:()=>Le,IsHexColor:()=>Ve,IsIP:()=>ve,IsIn:()=>_e,IsInstance:()=>qe,IsInt:()=>te,IsJSON:()=>Ie,IsLatitude:()=>ge,IsLongitude:()=>he,IsLowercase:()=>$e,IsNotEmpty:()=>le,IsNotIn:()=>Ke,IsNumber:()=>ee,IsNumberString:()=>Se,IsObject:()=>ie,IsOptional:()=>Hn,IsPort:()=>ye,IsSemVer:()=>De,IsString:()=>ne,IsUUID:()=>Re,IsUppercase:()=>ke,IsUrl:()=>Ae,JsonAlias:()=>jn,JsonDeserialize:()=>Wn,JsonIgnore:()=>_n,JsonMapper:()=>G,JsonMappingError:()=>b,JsonPolymorphic:()=>Gn,JsonProperty:()=>Bn,JsonReadOnly:()=>Kn,JsonSerialize:()=>qn,JsonType:()=>Zn,JsonValidationError:()=>C,JsonWriteOnly:()=>Ln,Length:()=>we,Matches:()=>Ne,Max:()=>de,MaxDate:()=>et,MaxLength:()=>xe,Min:()=>ce,MinDate:()=>nt,MinLength:()=>be,Negative:()=>fe,NotContains:()=>Je,NotEquals:()=>je,Positive:()=>pe,REDACTED:()=>yn,StartsWith:()=>ze,Validate:()=>tt,ValidateIf:()=>Xn,ValidateNested:()=>Qn,addConstraint:()=>E,collectErrorMessages:()=>rt,configure:()=>Jn,defineRule:()=>Fn,flattenErrors:()=>Z,formatErrors:()=>ot,fromJson:()=>kn,fromJsonArray:()=>Sn,fromJsonArraySync:()=>yt,fromJsonSync:()=>mt,fromRequest:()=>En,getConfig:()=>zn,modelOf:()=>R,modelOfInstance:()=>v,modelVersion:()=>T,propertyModel:()=>h,resetConfig:()=>Un,resolveNamingStrategy:()=>F,resolveOptions:()=>x,toInstance:()=>P,toInstanceArray:()=>nn,toInstanceArraySync:()=>$n,toInstanceSync:()=>Q,toJson:()=>Dn,toJsonSync:()=>ft,toPlain:()=>Y,toPlainSync:()=>Tn,validate:()=>M,validateOrReject:()=>Cn,validateOrRejectSync:()=>pt,validateSync:()=>j});Symbol.metadata??=Symbol.for("Symbol.metadata");var S=Symbol.for("cereale.model"),on=0;function T(){return on}function vn(n){if(!Object.hasOwn(n,S)){let e=n[S],t={};for(let[o,r]of Object.entries(e??{}))t[o]={...r,constraints:[...r.constraints]};n[S]=t}return n[S]}function h(n,e){on++;let t=vn(n);return t[e]??={constraints:[]}}function E(n,e,t,o){o?.each&&(t.each=!0),o?.message&&(t.message=o.message,t.hasCustomMessage=!0),h(n,e).constraints.push(t)}function R(n){return typeof n!="function"?{}:n[Symbol.metadata]?.[S]??{}}function v(n){let e=Object.getPrototypeOf(n);if(!e)return{};let t=Object.getOwnPropertyDescriptor(e,"constructor");return R(t?.value)}function Fn(n,e,t,o){let r=n;r[Symbol.metadata]??=Object.create(null),E(r[Symbol.metadata],e,t,o)}function I(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[_\-\s]+/g," ").trim().split(" ").filter(Boolean).map(e=>e.toLowerCase())}var rn=n=>n&&n.charAt(0).toUpperCase()+n.slice(1),q={identity:n=>n,camelCase:n=>{let e=I(n);return e.length===0?n:e[0]+e.slice(1).map(rn).join("")},PascalCase:n=>I(n).map(rn).join("")||n,snake_case:n=>I(n).join("_")||n,SCREAMING_SNAKE_CASE:n=>I(n).join("_").toUpperCase()||n,"kebab-case":n=>I(n).join("-")||n};function F(n){if(!n)return q.identity;if(typeof n=="function")return n;let e=q[n];if(!e)throw new Error(`Unknown naming strategy ${JSON.stringify(n)}. Use one of: ${Object.keys(q).join(", ")}, or pass your own function.`);return e}var an={namingStrategy:"identity",unknownKeys:"allow",validate:!0,maxDepth:64},O={...an};function Jn(n){O={...O,...n}}function zn(){return{...O}}function Un(){O={...an}}function x(n){return n?{namingStrategy:n.namingStrategy??O.namingStrategy,unknownKeys:n.unknownKeys??O.unknownKeys,validate:n.validate??O.validate,maxDepth:n.maxDepth??O.maxDepth}:O}function d(n,e){return((t,o)=>{let r=String(o.name);E(o.metadata,r,n(r),e)})}function p(n,e,t){return(o=>d(r=>({name:n,validate:e,message:t(r)}),o))}function A(n,e,t){return p(n,o=>typeof o=="string"&&e.test(o),t)}function Bn(n){return((e,t)=>{h(t.metadata,String(t.name)).name=n})}function jn(...n){return((e,t)=>{let o=h(t.metadata,String(t.name));o.aliases=[...o.aliases??[],...n]})}function W(n){return((e,t)=>{h(t.metadata,String(t.name)).access=n})}var _n=()=>W("none"),Kn=()=>W("readonly"),Ln=()=>W("writeonly");function qn(n){return((e,t)=>{h(t.metadata,String(t.name)).serializer=n})}function Wn(n){return((e,t)=>{h(t.metadata,String(t.name)).deserializer=n})}function Zn(n){return((e,t)=>{h(t.metadata,String(t.name)).type=n})}function Gn(n,e,t){return((o,r)=>{let a={discriminator:n,subTypes:e,onUnknown:t?.onUnknown??"keep",...t?.fallback?{fallback:t.fallback}:{}};h(r.metadata,String(r.name)).polymorphic=a})}function Hn(){return((n,e)=>{h(e.metadata,String(e.name)).optional=!0})}function Xn(n){return((e,t)=>{h(t.metadata,String(t.name)).condition=n})}function Yn(){return((n,e)=>{h(e.metadata,String(e.name))})}function Qn(n){return((e,t)=>{let o=String(t.name);h(t.metadata,o).nested=!0,n?.each&&E(t.metadata,o,{name:"nestedEach",validate:r=>Array.isArray(r),message:`${o} must be an array`})})}var ne=p("isString",n=>typeof n=="string",n=>`${n} must be a string`),ee=p("isNumber",n=>typeof n=="number"&&!isNaN(n),n=>`${n} must be a number`),te=p("isInt",n=>Number.isInteger(n),n=>`${n} must be an integer`),oe=p("isBoolean",n=>typeof n=="boolean",n=>`${n} must be a boolean`),re=p("isBigInt",n=>typeof n=="bigint",n=>`${n} must be a bigint`),ae=p("isDate",n=>n instanceof Date&&!isNaN(n.getTime()),n=>`${n} must be a valid Date object`),ie=p("isObject",n=>typeof n=="object"&&n!==null&&!Array.isArray(n),n=>`${n} must be an object`),se=p("isDefined",n=>n!=null,n=>`${n} should not be null or undefined`),le=p("isNotEmpty",n=>n!=null&&n!=="",n=>`${n} should not be empty`),ue=p("isEmpty",n=>n==null||n===""?!0:Array.isArray(n)?n.length===0:typeof n=="object"?Object.keys(n).length===0:!1,n=>`${n} must be empty`);function ce(n,e){return d(t=>({name:"min",validate:o=>typeof o=="number"&&o>=n,message:`${t} must be at least ${n}`,constraints:[n]}),e)}function de(n,e){return d(t=>({name:"max",validate:o=>typeof o=="number"&&o<=n,message:`${t} must be at most ${n}`,constraints:[n]}),e)}var pe=p("positive",n=>typeof n=="number"&&n>0,n=>`${n} must be positive`),fe=p("negative",n=>typeof n=="number"&&n<0,n=>`${n} must be negative`);function me(n,e){return d(t=>({name:"isDivisibleBy",validate:o=>typeof o=="number"&&Number.isFinite(o)&&n!==0&&o%n===0,message:`${t} must be divisible by ${n}`,constraints:[n]}),e)}var ye=p("isPort",n=>{let e=typeof n=="string"&&n.trim()!==""?Number(n):n;return typeof e=="number"&&Number.isInteger(e)&&e>=0&&e<=65535},n=>`${n} must be a valid port number`),ge=p("isLatitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-90&&n<=90,n=>`${n} must be a latitude between -90 and 90`),he=p("isLongitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-180&&n<=180,n=>`${n} must be a longitude between -180 and 180`);function be(n,e){return d(t=>({name:"minLength",validate:o=>typeof o=="string"&&o.length>=n,message:`${t} must be longer than or equal to ${n} characters`,constraints:[n]}),e)}function xe(n,e){return d(t=>({name:"maxLength",validate:o=>typeof o=="string"&&o.length<=n,message:`${t} must be shorter than or equal to ${n} characters`,constraints:[n]}),e)}function we(n,e,t){return d(o=>({name:"length",validate:r=>typeof r=="string"&&r.length>=n&&(e===void 0||r.length<=e),message:e===void 0?`${o} must be at least ${n} characters`:`${o} must be between ${n} and ${e} characters`,constraints:e===void 0?[n]:[n,e]}),t)}var Oe=A("isEmail",/^[^\s@]+@[^\s@]+\.[^\s@]+$/,n=>`${n} must be a valid email`),Ce=A("isAlpha",/^[A-Za-z]+$/,n=>`${n} must contain only letters`),Te=A("isAlphanumeric",/^[A-Za-z0-9]+$/,n=>`${n} must contain only letters and numbers`),De=A("isSemVer",/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,n=>`${n} must be a valid semantic version`),Ve=A("isHexColor",/^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,n=>`${n} must be a hex color`),$e=p("isLowercase",n=>typeof n=="string"&&n===n.toLowerCase(),n=>`${n} must be lowercase`),ke=p("isUppercase",n=>typeof n=="string"&&n===n.toUpperCase(),n=>`${n} must be uppercase`),Se=p("isNumberString",n=>typeof n=="string"&&n.trim()!==""&&Number.isFinite(Number(n)),n=>`${n} must be a number string`),Ee=p("isDateString",n=>typeof n=="string"&&!isNaN(Date.parse(n)),n=>`${n} must be a valid ISO 8601 date string`),Ie=p("isJson",n=>{if(typeof n!="string")return!1;try{return JSON.parse(n),!0}catch{return!1}},n=>`${n} must be a JSON string`),Ae=p("isUrl",n=>{try{return new URL(n),!0}catch{return!1}},n=>`${n} must be a valid URL`);function Ne(n,e){let t=n.flags.includes("g")||n.flags.includes("y")?new RegExp(n.source,n.flags.replace(/[gy]/g,"")):n;return d(o=>({name:"matches",validate:r=>typeof r=="string"&&t.test(r),message:`${o} must match ${n} regular expression`,constraints:[n]}),e)}var Me="00000000-0000-0000-0000-000000000000",Pe="ffffffff-ffff-ffff-ffff-ffffffffffff";function Re(n,e){let t=n?new RegExp(`^[0-9a-f]{8}-[0-9a-f]{4}-${n}[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`,"i"):/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;return d(o=>({name:"isUuid",validate:r=>typeof r!="string"?!1:!n&&(r.toLowerCase()===Me||r.toLowerCase()===Pe)?!0:t.test(r),message:`${o} must be a valid UUID${n?` (version ${n})`:""}`,...n?{constraints:[n]}:{}}),e)}var sn=/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/,ln=n=>{try{return new URL(`http://[${n}]`).hostname===`[${n.toLowerCase()}]`||/^[0-9a-f:.]+$/i.test(n)&&n.includes(":")}catch{return!1}};function ve(n,e){return d(t=>({name:"isIp",validate:o=>typeof o!="string"?!1:n===4?sn.test(o):n===6?ln(o):sn.test(o)||ln(o),message:`${t} must be a valid IP${n?`v${n}`:""} address`,...n?{constraints:[n]}:{}}),e)}function z(n,e,t){function o(r,a){return d(i=>({name:n,validate:s=>typeof s=="string"&&e(s,r),message:t(i,r),constraints:[r]}),a)}return o}var Fe=z("contains",(n,e)=>n.includes(e),(n,e)=>`${n} must contain ${JSON.stringify(e)}`),Je=z("notContains",(n,e)=>!n.includes(e),(n,e)=>`${n} must not contain ${JSON.stringify(e)}`),ze=z("startsWith",(n,e)=>n.startsWith(e),(n,e)=>`${n} must start with ${JSON.stringify(e)}`),Ue=z("endsWith",(n,e)=>n.endsWith(e),(n,e)=>`${n} must end with ${JSON.stringify(e)}`);function Be(n,e){return d(t=>({name:"equals",validate:o=>o===n,message:`${t} must be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function je(n,e){return d(t=>({name:"notEquals",validate:o=>o!==n,message:`${t} must not be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function _e(n,e){return d(t=>({name:"isIn",validate:o=>n.includes(o),message:`${t} must be one of the following values: ${n.join(", ")}`,constraints:[n]}),e)}function Ke(n,e){return d(t=>({name:"isNotIn",validate:o=>!n.includes(o),message:`${t} must not be one of the following values: ${n.join(", ")}`,constraints:[n]}),e)}function Le(n,e){let t=Object.keys(n).filter(o=>typeof n[n[o]]!="number").map(o=>n[o]);return d(o=>({name:"isEnum",validate:r=>t.includes(r),message:`${o} must be one of the following values: ${t.join(", ")}`,constraints:[t]}),e)}function qe(n,e){return d(t=>({name:"isInstance",validate:o=>o instanceof n,message:`${t} must be an instance of ${n.name}`,constraints:[n]}),e)}function $(n,e,t,o){return r=>d(a=>({name:n,validate:i=>Array.isArray(i)&&e(i),message:t(a),...o?{constraints:o}:{}}),r)}var We=n=>d(e=>({name:"isArray",validate:t=>Array.isArray(t),message:`${e} must be an array`}),n),Ze=n=>$("arrayNotEmpty",e=>e.length>0,e=>`${e} should not be empty`)(n),Ge=(n,e)=>$("arrayMinSize",t=>t.length>=n,t=>`${t} must contain at least ${n} elements`,[n])(e),He=(n,e)=>$("arrayMaxSize",t=>t.length<=n,t=>`${t} must contain at most ${n} elements`,[n])(e);function Xe(n,e){return $("arrayUnique",t=>{let o=n?t.map(n):t;return new Set(o).size===o.length},t=>`${t} must not contain duplicate values`)(e)}function Ye(n,e){return $("arrayContains",t=>n.every(o=>t.includes(o)),t=>`${t} must contain the following values: ${n.join(", ")}`,[n])(e)}function Qe(n,e){return $("arrayNotContains",t=>n.every(o=>!t.includes(o)),t=>`${t} must not contain any of the following values: ${n.join(", ")}`,[n])(e)}var J=n=>typeof n=="function"?n():n;function nt(n,e){return d(()=>({name:"minDate",validate:t=>t instanceof Date&&!isNaN(t.getTime())&&t.getTime()>=J(n).getTime(),message:t=>`${t.property} must not be earlier than ${J(n).toISOString()}`,constraints:[n]}),e)}function et(n,e){return d(()=>({name:"maxDate",validate:t=>t instanceof Date&&!isNaN(t.getTime())&&t.getTime()<=J(n).getTime(),message:t=>`${t.property} must not be later than ${J(n).toISOString()}`,constraints:[n]}),e)}function tt(n,e,t){let o=Array.isArray(e)?e:[],r=Array.isArray(e)?t:e;if(typeof n=="function"&&!n.prototype?.validate){let s=n;return d(()=>({name:"custom",validate:(l,u)=>s(l,u),message:l=>`${l.property} is invalid`,constraints:o}),r)}let i=new n;return d(()=>({name:n.name,validate:(s,l)=>i.validate(s,l),message:s=>i.defaultMessage?i.defaultMessage(s):`${s.property} is invalid`,constraints:o}),r)}function Z(n){let e={},t=(o,r)=>{for(let a of o){let i=a.property.startsWith("[")?`${r}${a.property}`:r?`${r}.${a.property}`:a.property,s=Object.values(a.constraints);s.length>0&&(e[i]??=[]).push(...s),a.children?.length&&t(a.children,i)}};return t(n,""),e}function ot(n){let e=Z(n);return Object.entries(e).flatMap(([t,o])=>o.map(r=>`${t}: ${r}`)).join(` -`)}function rt(n){return Object.values(Z(n)).flat()}var C=class extends Error{constructor(t,o){super(t);this.errors=o;this.name="JsonValidationError"}toString(){return`${this.message}: ${JSON.stringify(this.errors,null,2)}`}},b=class extends Error{constructor(e){super(e),this.name="JsonMappingError"}},at=new Set(["__proto__","constructor","prototype"]),yn="[redacted]";function gn(n,e){return n[e]?.access??"readwrite"}function hn(n,e,t){return n[e]?.name??t(e)}var un=new WeakMap;function it(n,e,t){let o=un.get(n);(!o||o.version!==T())&&(o={version:T(),byStrategy:new Map},un.set(n,o));let r=o.byStrategy.get(t.namingKey);r||(r=new Map,o.byStrategy.set(t.namingKey,r));let a=r.get(e);if(!a){let i=gn(n,e),s=n[e]?.serializer;a={name:hn(n,e,t.naming),skip:i==="none"||i==="writeonly",...s?{serializer:s}:{}},r.set(e,a)}return a}var cn=new WeakMap;function st(n,e){let t=cn.get(n);(!t||t.version!==T())&&(t={version:T(),byStrategy:new Map},cn.set(n,t));let o=t.byStrategy.get(e.namingKey);if(o)return o;let r=new Map,a=new Set,i=new Map,s=(u,c)=>{let y=r.get(u);if(y&&y!==c)throw new b(`Properties "${y}" and "${c}" both map to the JSON name ${JSON.stringify(u)}. Give one of them a distinct @JsonProperty name.`);r.set(u,c)};for(let[u,c]of Object.entries(n)){let y=[hn(n,u,e.naming),...c.aliases??[]],m=gn(n,u);if(m==="none"||m==="readonly"){for(let f of y)a.add(f);continue}for(let f of y)s(f,u);(c.deserializer||c.polymorphic||c.type)&&i.set(u,{...c.deserializer?{deserializer:c.deserializer}:{},...c.polymorphic?{polymorphic:c.polymorphic}:{},...c.type?{typeFn:c.type}:{}})}let l={accept:r,blocked:a,props:i};return t.byStrategy.set(e.namingKey,l),l}function N(n){return n!==null&&typeof n=="object"&&typeof n.then=="function"}async function H(n){for(;n.length>0;){let e=n.splice(0,n.length);await Promise.all(e)}}function X(n,e,t){if(n.length!==0){for(let o of n)o.catch(()=>{});throw n.length=0,new b(`${e} requires every serializer, deserializer and validator to be synchronous, but one returned a Promise. Use ${t} instead, or make the hook synchronous.`)}}function U(n,e,t,o,r){if(n==null||typeof n!="object")return n;if(o>t.maxDepth)throw new b(`Maximum nesting depth of ${t.maxDepth} exceeded while serializing. Raise it with the maxDepth option if this structure is legitimate.`);if(n instanceof Date)return n.toISOString();if(e.has(n))throw new b("Circular reference detected during serialization. Break the cycle with @JsonIgnore() on the back-reference, or supply a @JsonSerialize() serializer for that property.");e.add(n);try{if(Array.isArray(n)){let s=[];for(let l of n)s.push(U(l,e,t,o+1,r));return s}let a=v(n),i={};for(let s of Object.keys(n)){let l=it(a,s,t);if(l.skip)continue;let u=n[s];if(l.serializer&&u!==null&&u!==void 0){let c=bn(l.serializer).serialize(u);if(N(c)){let y=l.name;i[y]=void 0,r.push(c.then(m=>{i[y]=m}))}else i[l.name]=c}else i[l.name]=U(u,e,t,o+1,r)}return i}finally{e.delete(n)}}function k(n,e,t,o,r){if(e==null)return e;if(o>t.maxDepth)throw new b(`Maximum nesting depth of ${t.maxDepth} exceeded while deserializing. Raise it with the maxDepth option if this structure is legitimate.`);if(Array.isArray(e))return e.map(s=>k(n,s,t,o+1,r));if(typeof e!="object")return e;let a=new n,i=st(R(n),t);for(let s of Object.keys(e)){if(at.has(s)||i.blocked.has(s))continue;let l=i.accept.get(s);if(l===void 0){if(t.unknownKeys==="strip")continue;if(t.unknownKeys==="error")throw new b(`Unknown property ${JSON.stringify(s)} for ${n.name}. Allowed: ${[...i.accept.keys()].map(f=>JSON.stringify(f)).join(", ")||"(none declared)"}.`);a[s]=e[s];continue}let u=e[s],c=i.props.get(l);if(c?.deserializer){let f=bn(c.deserializer).deserialize(u);if(N(f)){let g=l;a[g]=void 0,r.push(f.then(D=>{a[g]=D}))}else a[l]=f;continue}let y=c?.polymorphic;if(y&&u!==null&&u!==void 0){let{discriminator:f,subTypes:g,onUnknown:D,fallback:V}=y,en=w=>{if(w==null||typeof w!="object")return w;let tn=g.find(K=>w[f]===K.name);if(tn)return k(tn.value,w,t,o+1,r);if(V)return k(V,w,t,o+1,r);if(D==="error")throw new b(`Unknown discriminator value ${JSON.stringify(w[f])} for property "${l}". Known values: ${g.map(K=>JSON.stringify(K.name)).join(", ")}.`);return w};a[l]=Array.isArray(u)?u.map(en):en(u);continue}let m=c?.typeFn;if(m&&u!==null&&u!==void 0){let f=m();a[l]=k(f,u,t,o+1,r);continue}a[l]=u}return a}var dn=new WeakMap;function lt(n){let e=[],t=new Set;for(let o of n){if(typeof o.message=="string"){let r=`${o.name}|${String(o.constraints)}|${o.message}|${o.each??!1}`;if(t.has(r))continue;t.add(r)}e.push(o)}return e}function ut(n){let e=dn.get(n);if(e&&e.version===T())return e.plan;let t=[];for(let[o,r]of Object.entries(n)){let a=r.access??"readwrite";t.push({key:o,constraints:lt(r.constraints),isOptional:!!r.optional,isNested:!!r.nested,redact:a==="writeonly"||a==="none",...r.condition?{condition:r.condition}:{}})}return dn.set(n,{version:T(),plan:t}),t}var pn=new WeakMap;function bn(n){let e=pn.get(n);return e||(e=new n,pn.set(n,e)),e}function fn(n,e,t){if(!(e in n)){n[e]=t;return}let o=2;for(;`${e}_${o}`in n;)o++;n[`${e}_${o}`]=t}function mn(n,e,t){let o=typeof n.message=="function"?n.message(e):n.message;return n.each&&!n.hasCustomMessage&&(o=t>=0?`each element in ${o} (failed at index ${t})`:`each element in ${o}`),o}function ct(n,e,t){for(let o=0;o0?t.children=o:delete t.children,(Object.keys(t.constraints).length>0||t.children)&&e.push(t)}return e}function B(n,e,t,o,r){let a=[];if(n==null||typeof n!="object")return a;if(t>o)throw new b(`Maximum nesting depth of ${o} exceeded while validating. Raise it with the maxDepth option if this structure is legitimate.`);if(e.has(n))return a;e.add(n);try{if(Array.isArray(n)){for(let i=0;i0&&a.push({property:`[${i}]`,value:n[i],constraints:{},children:s})}return a}for(let i of ut(v(n))){let s=i.key,l=n[s];if(i.condition&&!i.condition(n)||i.isOptional&&l==null)continue;let u={property:s,value:i.redact?yn:l,constraints:{}},c={value:l,object:n,property:s,constraints:[]},y=!1;for(let m of i.constraints){c.constraints=m.constraints||[];let f=m.each&&Array.isArray(l)?ct(m,l,c):(()=>{let g=m.validate(l,c);return N(g)?g.then(D=>({ok:D,index:-1})):{ok:g,index:-1}})();if(N(f)){y=!0;let g={value:l,object:n,property:s,constraints:m.constraints||[]};r.push(f.then(({ok:D,index:V})=>{D||(V>=0&&(g.value=l[V]),fn(u.constraints,m.name,mn(m,g,V)))}));continue}if(!f.ok){f.index>=0&&(c.value=l[f.index]);let g=mn(m,c,f.index);c.value=l,fn(u.constraints,m.name,g)}}if(i.isNested&&l!==null&&l!==void 0){let m=B(l,e,t+1,o,r);m.length>0&&(u.children=m)}(y||Object.keys(u.constraints).length>0||u.children)&&a.push(u)}return a}finally{e.delete(n)}}function wn(n){let e=x(n);return{naming:F(e.namingStrategy),namingKey:e.namingStrategy,maxDepth:e.maxDepth}}function On(n){let e=x(n);return{naming:F(e.namingStrategy),namingKey:e.namingStrategy,unknownKeys:e.unknownKeys,maxDepth:e.maxDepth}}async function M(n,e){let t=[],o=B(n,new Set,0,x(e).maxDepth,t);return t.length===0?o:(await H(t),xn(o))}function j(n,e){let t=[],o=B(n,new Set,0,x(e).maxDepth,t);return X(t,"validateSync()","validate()"),o}async function Cn(n,e){let t=await M(n,e);if(t.length>0)throw new C("Validation failed",t)}function pt(n,e){let t=j(n,e);if(t.length>0)throw new C("Validation failed",t)}async function Y(n,e){if(n==null)return n;if(x(e).validate){let r=await M(n,e);if(r.length>0)throw new C("Validation failed during serialization",r)}let t=[],o=U(n,new Set,wn(e),0,t);return await H(t),o}function Tn(n,e){if(n==null)return n;if(x(e).validate){let r=j(n,e);if(r.length>0)throw new C("Validation failed during serialization",r)}let t=[],o=U(n,new Set,wn(e),0,t);return X(t,"toPlainSync()","toPlain()"),o}async function Dn(n,e){return JSON.stringify(await Y(n,e))}function ft(n,e){return JSON.stringify(Tn(n,e))}async function P(n,e,t){let o=[],r=k(n,e,On(t),0,o);if(await H(o),x(t).validate){let a=await M(r,t);if(a.length>0)throw new C("Validation failed during deserialization",a)}return r}function Q(n,e,t){let o=[],r=k(n,e,On(t),0,o);if(X(o,"toInstanceSync()","toInstance()"),x(t).validate){let a=j(r,t);if(a.length>0)throw new C("Validation failed during deserialization",a)}return r}function Vn(n,e){if(!Array.isArray(e))throw new b(`Expected an array to map to ${n.name}[], received ${typeof e}.`)}async function nn(n,e,t){return Vn(n,e),await P(n,e,t)}function $n(n,e,t){return Vn(n,e),Q(n,e,t)}async function kn(n,e,t){return P(n,_(e),t)}function mt(n,e,t){return Q(n,_(e),t)}async function Sn(n,e,t){return nn(n,_(e),t)}function yt(n,e,t){return $n(n,_(e),t)}function _(n){try{return JSON.parse(n)}catch(e){throw new b(`Input is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}}async function En(n,e,t){let o;try{o=await e.json()}catch(r){throw new b(`Request body is not valid JSON: ${r instanceof Error?r.message:String(r)}`)}return P(n,o,t)}var G=class{static toPlain=Y;static toJson=Dn;static toInstance=P;static toInstanceArray=nn;static fromJson=kn;static fromJsonArray=Sn;static fromRequest=En;static validate=M;static validateOrReject=Cn};return Rn(gt);})(); +var Cereale=(()=>{var Z=Object.defineProperty;var Jn=Object.getOwnPropertyDescriptor;var zn=Object.getOwnPropertyNames;var Bn=Object.prototype.hasOwnProperty;var Un=(n,e)=>{for(var t in e)Z(n,t,{get:e[t],enumerable:!0})},jn=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of zn(e))!Bn.call(n,r)&&r!==t&&Z(n,r,{get:()=>e[r],enumerable:!(o=Jn(e,r))||o.enumerable});return n};var _n=n=>jn(Z({},"__esModule",{value:!0}),n);var St={};Un(St,{Allow:()=>ie,ArrayContains:()=>it,ArrayMaxSize:()=>rt,ArrayMinSize:()=>ot,ArrayNotContains:()=>st,ArrayNotEmpty:()=>tt,ArrayUnique:()=>at,Contains:()=>Le,Email:()=>Ee,EndsWith:()=>Ge,Equals:()=>Ze,IsAlpha:()=>Ie,IsAlphanumeric:()=>Ae,IsArray:()=>et,IsBigInt:()=>pe,IsBoolean:()=>de,IsDate:()=>fe,IsDateString:()=>Fe,IsDefined:()=>me,IsDivisibleBy:()=>Ce,IsEmpty:()=>he,IsEnum:()=>Qe,IsHexColor:()=>Me,IsIP:()=>Ke,IsIn:()=>Ye,IsInstance:()=>nt,IsInt:()=>ce,IsJSON:()=>Je,IsLatitude:()=>ke,IsLongitude:()=>Se,IsLowercase:()=>Pe,IsNotEmpty:()=>ge,IsNotIn:()=>Xe,IsNumber:()=>ue,IsNumberString:()=>ve,IsObject:()=>ye,IsOptional:()=>re,IsPort:()=>Te,IsSemVer:()=>Ne,IsString:()=>le,IsUUID:()=>_e,IsUppercase:()=>Re,IsUrl:()=>ze,JsonAlias:()=>Hn,JsonDeserialize:()=>ee,JsonIgnore:()=>Yn,JsonMapper:()=>nn,JsonMappingError:()=>h,JsonPolymorphic:()=>oe,JsonProperty:()=>Zn,JsonReadOnly:()=>Xn,JsonSerialize:()=>ne,JsonType:()=>te,JsonValidationError:()=>S,JsonWriteOnly:()=>Qn,Length:()=>Ve,Matches:()=>Be,Max:()=>we,MaxDate:()=>ut,MaxLength:()=>De,Min:()=>be,MinDate:()=>lt,MinLength:()=>$e,Negative:()=>Oe,NotContains:()=>qe,NotEquals:()=>He,Positive:()=>xe,REDACTED:()=>On,StartsWith:()=>We,Validate:()=>ct,ValidateIf:()=>ae,ValidateNested:()=>se,addConstraint:()=>M,collectErrorMessages:()=>pt,configure:()=>qn,defineRule:()=>Ln,fieldMetadata:()=>x,flattenErrors:()=>X,formatErrors:()=>dt,fromJson:()=>Rn,fromJsonArray:()=>vn,fromJsonArraySync:()=>kt,fromJsonSync:()=>Tt,fromRequest:()=>Fn,getConfig:()=>Wn,modelOf:()=>z,modelOfInstance:()=>B,modelVersion:()=>$,propertyModel:()=>O,resetConfig:()=>Gn,resolveNamingStrategy:()=>U,resolveOptions:()=>C,toInstance:()=>J,toInstanceArray:()=>an,toInstanceArraySync:()=>Pn,toInstanceSync:()=>rn,toJson:()=>Nn,toJsonSync:()=>Ct,toPlain:()=>on,toPlainSync:()=>An,validate:()=>F,validateOrReject:()=>In,validateOrRejectSync:()=>Ot,validateSync:()=>q});var D=Symbol.metadata??Symbol.for("Symbol.metadata");Symbol.metadata??=D;var N=Symbol.for("cereale.model"),un=0;function $(){return un}function Kn(n){if(!Object.hasOwn(n,N)){let e=n[N],t={};for(let[o,r]of Object.entries(e??{}))t[o]={...r,constraints:[...r.constraints]};n[N]=t}return n[N]}function x(n){let e=n;if(typeof e!="object"||e===null||typeof e.kind!="string")throw new TypeError('cereale needs TC39 standard decorators, but the compiler emitted legacy ones. Set "experimentalDecorators": false in tsconfig.json (and drop "emitDecoratorMetadata"). The two decorator systems cannot coexist in one program, so a project that still needs legacy decorators for another library cannot use cereale yet.');if(e.kind!=="field")throw new TypeError(`cereale decorators apply to fields, but this one was applied to a ${e.kind}.`+(e.kind==="accessor"?" An `accessor` field keeps its value in a private slot that mapping and validation cannot reach \u2014 declare it as a plain field instead.":""));if(typeof e.metadata!="object"||e.metadata===null)throw new TypeError(`The decorator context for "${String(e.name)}" carries no metadata object, so cereale has nowhere to record the rule. The compiler emitted its decorator helpers without metadata support: make sure cereale is imported before the decorated class is evaluated (importing it installs the Symbol.metadata fallback) and that the build targets ES2022 or later.`);return e.metadata}function O(n,e){un++;let t=Kn(n);return t[e]??={constraints:[]}}function M(n,e,t,o){o?.each&&(t.each=!0),o?.message&&(t.message=o.message,t.hasCustomMessage=!0),O(n,e).constraints.push(t)}function z(n){return typeof n!="function"?{}:n[D]?.[N]??{}}function B(n){let e=Object.getPrototypeOf(n);if(!e)return{};let t=Object.getOwnPropertyDescriptor(e,"constructor");return z(t?.value)}function Ln(n,e,t,o){let r=n;Object.hasOwn(r,D)||(r[D]=Object.create(r[D]??null)),M(r[D],e,t,o)}function P(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[_\-\s]+/g," ").trim().split(" ").filter(Boolean).map(e=>e.toLowerCase())}var cn=n=>n&&n.charAt(0).toUpperCase()+n.slice(1),H={identity:n=>n,camelCase:n=>{let e=P(n);return e.length===0?n:e[0]+e.slice(1).map(cn).join("")},PascalCase:n=>P(n).map(cn).join("")||n,snake_case:n=>P(n).join("_")||n,SCREAMING_SNAKE_CASE:n=>P(n).join("_").toUpperCase()||n,"kebab-case":n=>P(n).join("-")||n};function U(n){if(!n)return H.identity;if(typeof n=="function")return n;let e=H[n];if(!e)throw new Error(`Unknown naming strategy ${JSON.stringify(n)}. Use one of: ${Object.keys(H).join(", ")}, or pass your own function.`);return e}var dn={namingStrategy:"identity",unknownKeys:"allow",validate:!0,maxDepth:64},k={...dn};function qn(n){k={...k,...n}}function Wn(){return{...k}}function Gn(){k={...dn}}function C(n){return n?{namingStrategy:n.namingStrategy??k.namingStrategy,unknownKeys:n.unknownKeys??k.unknownKeys,validate:n.validate??k.validate,maxDepth:n.maxDepth??k.maxDepth}:k}function y(n,e){return((t,o)=>{let r=String(o.name);M(x(o),r,n(r),e)})}function g(n,e,t){return(o=>y(r=>({name:n,validate:e,message:t(r)}),o))}function R(n,e,t){return g(n,o=>typeof o=="string"&&e.test(o),t)}function Zn(n){return((e,t)=>{O(x(t),String(t.name)).name=n})}function Hn(...n){return((e,t)=>{let o=O(x(t),String(t.name));o.aliases=[...o.aliases??[],...n]})}function Y(n){return((e,t)=>{O(x(t),String(t.name)).access=n})}var Yn=()=>Y("none"),Xn=()=>Y("readonly"),Qn=()=>Y("writeonly");function ne(n){return((e,t)=>{O(x(t),String(t.name)).serializer=n})}function ee(n){return((e,t)=>{O(x(t),String(t.name)).deserializer=n})}function te(n){return((e,t)=>{O(x(t),String(t.name)).type=n})}function oe(n,e,t){return((o,r)=>{let a={discriminator:n,subTypes:e,onUnknown:t?.onUnknown??"keep",...t?.fallback?{fallback:t.fallback}:{}};O(x(r),String(r.name)).polymorphic=a})}function re(){return((n,e)=>{O(x(e),String(e.name)).optional=!0})}function ae(n){return((e,t)=>{O(x(t),String(t.name)).condition=n})}function ie(){return((n,e)=>{O(x(e),String(e.name))})}function se(n){return((e,t)=>{let o=x(t),r=String(t.name);O(o,r).nested=!0,n?.each&&M(o,r,{name:"nestedEach",validate:a=>Array.isArray(a),message:`${r} must be an array`})})}var le=g("isString",n=>typeof n=="string",n=>`${n} must be a string`),ue=g("isNumber",n=>typeof n=="number"&&!isNaN(n),n=>`${n} must be a number`),ce=g("isInt",n=>Number.isInteger(n),n=>`${n} must be an integer`),de=g("isBoolean",n=>typeof n=="boolean",n=>`${n} must be a boolean`),pe=g("isBigInt",n=>typeof n=="bigint",n=>`${n} must be a bigint`),fe=g("isDate",n=>n instanceof Date&&!isNaN(n.getTime()),n=>`${n} must be a valid Date object`),ye=g("isObject",n=>typeof n=="object"&&n!==null&&!Array.isArray(n),n=>`${n} must be an object`),me=g("isDefined",n=>n!=null,n=>`${n} should not be null or undefined`),ge=g("isNotEmpty",n=>n!=null&&n!=="",n=>`${n} should not be empty`),he=g("isEmpty",n=>n==null||n===""?!0:Array.isArray(n)?n.length===0:typeof n=="object"?Object.keys(n).length===0:!1,n=>`${n} must be empty`);function be(n,e){return y(t=>({name:"min",validate:o=>typeof o=="number"&&o>=n,message:`${t} must be at least ${n}`,constraints:[n]}),e)}function we(n,e){return y(t=>({name:"max",validate:o=>typeof o=="number"&&o<=n,message:`${t} must be at most ${n}`,constraints:[n]}),e)}var xe=g("positive",n=>typeof n=="number"&&n>0,n=>`${n} must be positive`),Oe=g("negative",n=>typeof n=="number"&&n<0,n=>`${n} must be negative`);function Ce(n,e){return y(t=>({name:"isDivisibleBy",validate:o=>typeof o=="number"&&Number.isFinite(o)&&n!==0&&o%n===0,message:`${t} must be divisible by ${n}`,constraints:[n]}),e)}var Te=g("isPort",n=>{let e=typeof n=="string"&&n.trim()!==""?Number(n):n;return typeof e=="number"&&Number.isInteger(e)&&e>=0&&e<=65535},n=>`${n} must be a valid port number`),ke=g("isLatitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-90&&n<=90,n=>`${n} must be a latitude between -90 and 90`),Se=g("isLongitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-180&&n<=180,n=>`${n} must be a longitude between -180 and 180`);function $e(n,e){return y(t=>({name:"minLength",validate:o=>typeof o=="string"&&o.length>=n,message:`${t} must be longer than or equal to ${n} characters`,constraints:[n]}),e)}function De(n,e){return y(t=>({name:"maxLength",validate:o=>typeof o=="string"&&o.length<=n,message:`${t} must be shorter than or equal to ${n} characters`,constraints:[n]}),e)}function Ve(n,e,t){return y(o=>({name:"length",validate:r=>typeof r=="string"&&r.length>=n&&(e===void 0||r.length<=e),message:e===void 0?`${o} must be at least ${n} characters`:`${o} must be between ${n} and ${e} characters`,constraints:e===void 0?[n]:[n,e]}),t)}var Ee=R("isEmail",/^[^\s@]+@[^\s@]+\.[^\s@]+$/,n=>`${n} must be a valid email`),Ie=R("isAlpha",/^[A-Za-z]+$/,n=>`${n} must contain only letters`),Ae=R("isAlphanumeric",/^[A-Za-z0-9]+$/,n=>`${n} must contain only letters and numbers`),Ne=R("isSemVer",/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,n=>`${n} must be a valid semantic version`),Me=R("isHexColor",/^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,n=>`${n} must be a hex color`),Pe=g("isLowercase",n=>typeof n=="string"&&n===n.toLowerCase(),n=>`${n} must be lowercase`),Re=g("isUppercase",n=>typeof n=="string"&&n===n.toUpperCase(),n=>`${n} must be uppercase`),ve=g("isNumberString",n=>typeof n=="string"&&n.trim()!==""&&Number.isFinite(Number(n)),n=>`${n} must be a number string`),Fe=g("isDateString",n=>typeof n=="string"&&!isNaN(Date.parse(n)),n=>`${n} must be a valid ISO 8601 date string`),Je=g("isJson",n=>{if(typeof n!="string")return!1;try{return JSON.parse(n),!0}catch{return!1}},n=>`${n} must be a JSON string`),ze=g("isUrl",n=>{try{return new URL(n),!0}catch{return!1}},n=>`${n} must be a valid URL`);function Be(n,e){let t=n.flags.includes("g")||n.flags.includes("y")?new RegExp(n.source,n.flags.replace(/[gy]/g,"")):n;return y(o=>({name:"matches",validate:r=>typeof r=="string"&&t.test(r),message:`${o} must match ${n} regular expression`,constraints:[n]}),e)}var Ue="00000000-0000-0000-0000-000000000000",je="ffffffff-ffff-ffff-ffff-ffffffffffff";function _e(n,e){let t=n?new RegExp(`^[0-9a-f]{8}-[0-9a-f]{4}-${n}[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`,"i"):/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;return y(o=>({name:"isUuid",validate:r=>typeof r!="string"?!1:!n&&(r.toLowerCase()===Ue||r.toLowerCase()===je)?!0:t.test(r),message:`${o} must be a valid UUID${n?` (version ${n})`:""}`,...n?{constraints:[n]}:{}}),e)}var pn=/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/,fn=n=>{try{return new URL(`http://[${n}]`).hostname===`[${n.toLowerCase()}]`||/^[0-9a-f:.]+$/i.test(n)&&n.includes(":")}catch{return!1}};function Ke(n,e){return y(t=>({name:"isIp",validate:o=>typeof o!="string"?!1:n===4?pn.test(o):n===6?fn(o):pn.test(o)||fn(o),message:`${t} must be a valid IP${n?`v${n}`:""} address`,...n?{constraints:[n]}:{}}),e)}function _(n,e,t){function o(r,a){return y(i=>({name:n,validate:s=>typeof s=="string"&&e(s,r),message:t(i,r),constraints:[r]}),a)}return o}var Le=_("contains",(n,e)=>n.includes(e),(n,e)=>`${n} must contain ${JSON.stringify(e)}`),qe=_("notContains",(n,e)=>!n.includes(e),(n,e)=>`${n} must not contain ${JSON.stringify(e)}`),We=_("startsWith",(n,e)=>n.startsWith(e),(n,e)=>`${n} must start with ${JSON.stringify(e)}`),Ge=_("endsWith",(n,e)=>n.endsWith(e),(n,e)=>`${n} must end with ${JSON.stringify(e)}`);function Ze(n,e){return y(t=>({name:"equals",validate:o=>o===n,message:`${t} must be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function He(n,e){return y(t=>({name:"notEquals",validate:o=>o!==n,message:`${t} must not be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function Ye(n,e){return y(t=>({name:"isIn",validate:o=>n.includes(o),message:`${t} must be one of the following values: ${n.join(", ")}`,constraints:[n]}),e)}function Xe(n,e){return y(t=>({name:"isNotIn",validate:o=>!n.includes(o),message:`${t} must not be one of the following values: ${n.join(", ")}`,constraints:[n]}),e)}function Qe(n,e){let t=Object.keys(n).filter(o=>typeof n[n[o]]!="number").map(o=>n[o]);return y(o=>({name:"isEnum",validate:r=>t.includes(r),message:`${o} must be one of the following values: ${t.join(", ")}`,constraints:[t]}),e)}function nt(n,e){return y(t=>({name:"isInstance",validate:o=>o instanceof n,message:`${t} must be an instance of ${n.name}`,constraints:[n]}),e)}function V(n,e,t,o){return r=>y(a=>({name:n,validate:i=>Array.isArray(i)&&e(i),message:t(a),...o?{constraints:o}:{}}),r)}var et=n=>y(e=>({name:"isArray",validate:t=>Array.isArray(t),message:`${e} must be an array`}),n),tt=n=>V("arrayNotEmpty",e=>e.length>0,e=>`${e} should not be empty`)(n),ot=(n,e)=>V("arrayMinSize",t=>t.length>=n,t=>`${t} must contain at least ${n} elements`,[n])(e),rt=(n,e)=>V("arrayMaxSize",t=>t.length<=n,t=>`${t} must contain at most ${n} elements`,[n])(e);function at(n,e){return V("arrayUnique",t=>{let o=n?t.map(n):t;return new Set(o).size===o.length},t=>`${t} must not contain duplicate values`)(e)}function it(n,e){return V("arrayContains",t=>n.every(o=>t.includes(o)),t=>`${t} must contain the following values: ${n.join(", ")}`,[n])(e)}function st(n,e){return V("arrayNotContains",t=>n.every(o=>!t.includes(o)),t=>`${t} must not contain any of the following values: ${n.join(", ")}`,[n])(e)}var j=n=>typeof n=="function"?n():n;function lt(n,e){return y(()=>({name:"minDate",validate:t=>t instanceof Date&&!isNaN(t.getTime())&&t.getTime()>=j(n).getTime(),message:t=>`${t.property} must not be earlier than ${j(n).toISOString()}`,constraints:[n]}),e)}function ut(n,e){return y(()=>({name:"maxDate",validate:t=>t instanceof Date&&!isNaN(t.getTime())&&t.getTime()<=j(n).getTime(),message:t=>`${t.property} must not be later than ${j(n).toISOString()}`,constraints:[n]}),e)}function ct(n,e,t){let o=Array.isArray(e)?e:[],r=Array.isArray(e)?t:e;if(typeof n=="function"&&!n.prototype?.validate){let s=n;return y(()=>({name:"custom",validate:(c,p)=>s(c,p),message:c=>`${c.property} is invalid`,constraints:o}),r)}let i=new n;return y(()=>({name:n.name,validate:(s,c)=>i.validate(s,c),message:s=>i.defaultMessage?i.defaultMessage(s):`${s.property} is invalid`,constraints:o}),r)}function X(n){let e={},t=(o,r)=>{for(let a of o){let i=a.property.startsWith("[")?`${r}${a.property}`:r?`${r}.${a.property}`:a.property,s=Object.values(a.constraints);s.length>0&&(e[i]??=[]).push(...s),a.children?.length&&t(a.children,i)}};return t(n,""),e}function dt(n){let e=X(n);return Object.entries(e).flatMap(([t,o])=>o.map(r=>`${t}: ${r}`)).join(` +`)}function pt(n){return Object.values(X(n)).flat()}var S=class extends Error{constructor(t,o){super(t);this.errors=o;this.name="JsonValidationError"}toString(){return`${this.message}: ${JSON.stringify(this.errors,null,2)}`}},h=class extends Error{constructor(e){super(e),this.name="JsonMappingError"}},ft=new Set(["__proto__","constructor","prototype"]),On="[redacted]";function Cn(n,e){return n[e]?.access??"readwrite"}function Tn(n,e,t){return n[e]?.name??t(e)}var yn=new WeakMap;function yt(n,e,t){let o=yn.get(n);(!o||o.version!==$())&&(o={version:$(),byStrategy:new Map},yn.set(n,o));let r=o.byStrategy.get(t.namingKey);r||(r=new Map,o.byStrategy.set(t.namingKey,r));let a=r.get(e);if(!a){let i=Cn(n,e),s=n[e]?.serializer;a={name:Tn(n,e,t.naming),skip:i==="none"||i==="writeonly",...s?{serializer:s}:{}},r.set(e,a)}return a}var mn=new WeakMap;function mt(n,e){let t=mn.get(n);(!t||t.version!==$())&&(t={version:$(),byStrategy:new Map},mn.set(n,t));let o=t.byStrategy.get(e.namingKey);if(o)return o;let r=new Map,a=new Set,i=new Map,s=new Map,c=[],p=[],f=(l,d)=>{let u=r.get(l);if(u&&u!==d)throw new h(`Properties "${u}" and "${d}" both map to the JSON name ${JSON.stringify(l)}. Give one of them a distinct @JsonProperty name.`);r.set(l,d)};for(let[l,d]of Object.entries(n)){let u=[Tn(n,l,e.naming),...d.aliases??[]],b=Cn(n,l);if(b==="none"||b==="readonly"){for(let w of u)a.add(w);c.push(l);continue}for(let w of u)f(w,l);u.includes(l)||p.push({property:l,accepted:u[0]}),(d.deserializer||d.polymorphic||d.type)&&s.set(l,{...d.deserializer?{deserializer:d.deserializer}:{},...d.polymorphic?{polymorphic:d.polymorphic}:{},...d.type?{typeFn:d.type}:{}})}for(let l of c)r.has(l)||a.add(l);for(let l of p)!r.has(l.property)&&!a.has(l.property)&&i.set(l.property,l);let m={accept:r,blocked:a,stale:i,props:s};return t.byStrategy.set(e.namingKey,m),m}function v(n){return n!==null&&typeof n=="object"&&typeof n.then=="function"}async function en(n){for(;n.length>0;){let e=n.splice(0,n.length);await Promise.all(e)}}function tn(n,e,t){if(n.length!==0){for(let o of n)o.catch(()=>{});throw n.length=0,new h(`${e} requires every serializer, deserializer and validator to be synchronous, but one returned a Promise. Use ${t} instead, or make the hook synchronous.`)}}var gt={Map:"a Map",Set:"a Set",WeakMap:"a WeakMap",WeakSet:"a WeakSet",WeakRef:"a WeakRef",Promise:"a Promise",ArrayBuffer:"an ArrayBuffer",SharedArrayBuffer:"a SharedArrayBuffer",DataView:"a DataView",Generator:"a generator",AsyncGenerator:"an async generator"};function kn(n){let e=n[Symbol.toStringTag];if(typeof e=="string"){let t=gt[e];if(t!==void 0)return t;if(ArrayBuffer.isView(n))return`a ${e}`}return n instanceof RegExp?"a RegExp":n instanceof Error?"an Error, whose message and stack are not enumerable":null}var Sn="a bigint, which JSON has no representation for";function gn(n){switch(typeof n){case"bigint":return Sn;case"symbol":return"a symbol";case"function":return"a function";case"object":return n===null?null:kn(n);default:return null}}function Q(n){if(n.length===0)return"the value passed in";let e="";for(let t of n)typeof t=="number"?e+=`[${t}]`:e+=e===""?t:`.${t}`;return e}function E(n,e){throw new h(`${Q(e)} is ${n}, which cannot be serialized to JSON. Give the property a @JsonSerialize() serializer that converts it, or drop it from the output with @JsonIgnore().`)}function K(n,e,t,o,r,a){if(n==null)return n;let i=typeof n;if(i!=="object")return i==="bigint"&&E(Sn,a),i==="symbol"&&E("a symbol",a),i==="function"&&E("a function",a),n;if(o>t.maxDepth)throw new h(`Maximum nesting depth of ${t.maxDepth} exceeded while serializing at ${Q(a)}. Raise it with the maxDepth option if this structure is legitimate.`);if(n instanceof Date)return n.toISOString();let s=Array.isArray(n);if(!s){let c=kn(n);c!==null&&E(c,a)}if(e.has(n))throw new h(`Circular reference detected during serialization at ${Q(a)}. Break the cycle with @JsonIgnore() on the back-reference, or supply a @JsonSerialize() serializer for that property.`);e.add(n);try{if(s){let f=[];for(let m=0;m{let A=gn(w);A!==null&&E(A,b),p[u]=w}))}else{let u=gn(d);u!==null&&E(u,a),p[m.name]=d}}else p[m.name]=K(l,e,t,o+1,r,a);a.pop()}return p}finally{e.delete(n)}}function I(n,e,t,o,r){if(e==null)return e;if(o>t.maxDepth)throw new h(`Maximum nesting depth of ${t.maxDepth} exceeded while deserializing. Raise it with the maxDepth option if this structure is legitimate.`);if(Array.isArray(e))return e.map(s=>I(n,s,t,o+1,r));if(typeof e!="object")return e;let a=new n,i=mt(z(n),t);for(let s of Object.keys(e)){if(ft.has(s)||i.blocked.has(s))continue;let c=i.stale.get(s);if(c!==void 0){if(t.unknownKeys==="error")throw new h(`${JSON.stringify(s)} is not a JSON name for ${n.name}: property "${c.property}" is mapped to ${JSON.stringify(c.accepted)}. Send that name, or add @JsonAlias(${JSON.stringify(s)}) to keep accepting this one.`);continue}let p=i.accept.get(s);if(p===void 0){if(t.unknownKeys==="strip")continue;if(t.unknownKeys==="error")throw new h(`Unknown property ${JSON.stringify(s)} for ${n.name}. Allowed: ${[...i.accept.keys()].map(u=>JSON.stringify(u)).join(", ")||"(none declared)"}.`);a[s]=e[s];continue}let f=e[s],m=i.props.get(p);if(m?.deserializer){let u=$n(m.deserializer).deserialize(f);if(v(u)){let b=p;a[b]=void 0,r.push(u.then(w=>{a[b]=w}))}else a[p]=u;continue}let l=m?.polymorphic;if(l&&f!==null&&f!==void 0){let{discriminator:u,subTypes:b,onUnknown:w,fallback:A}=l,sn=T=>{if(T==null||typeof T!="object")return T;let ln=b.find(G=>T[u]===G.name);if(ln)return I(ln.value,T,t,o+1,r);if(A)return I(A,T,t,o+1,r);if(w==="error")throw new h(`Unknown discriminator value ${JSON.stringify(T[u])} for property "${p}". Known values: ${b.map(G=>JSON.stringify(G.name)).join(", ")}.`);return T};a[p]=Array.isArray(f)?f.map(sn):sn(f);continue}let d=m?.typeFn;if(d&&f!==null&&f!==void 0){let u=d();a[p]=I(u,f,t,o+1,r);continue}a[p]=f}return a}var hn=new WeakMap;function ht(n){let e=[],t=new Set;for(let o of n){if(typeof o.message=="string"){let r=`${o.name}|${String(o.constraints)}|${o.message}|${o.each??!1}`;if(t.has(r))continue;t.add(r)}e.push(o)}return e}function bt(n){let e=hn.get(n);if(e&&e.version===$())return e.plan;let t=[];for(let[o,r]of Object.entries(n)){let a=r.access??"readwrite";t.push({key:o,constraints:ht(r.constraints),isOptional:!!r.optional,isNested:!!r.nested,redact:a==="writeonly"||a==="none",...r.condition?{condition:r.condition}:{}})}return hn.set(n,{version:$(),plan:t}),t}var bn=new WeakMap;function $n(n){let e=bn.get(n);return e||(e=new n,bn.set(n,e)),e}function wn(n,e,t){if(!(e in n)){n[e]=t;return}let o=2;for(;`${e}_${o}`in n;)o++;n[`${e}_${o}`]=t}function xn(n,e,t){let o=typeof n.message=="function"?n.message(e):n.message;return n.each&&!n.hasCustomMessage&&(o=t>=0?`each element in ${o} (failed at index ${t})`:`each element in ${o}`),o}function wt(n,e,t){for(let o=0;o0?t.children=o:delete t.children,(Object.keys(t.constraints).length>0||t.children)&&e.push(t)}return e}function L(n,e,t,o,r){let a=[];if(n==null||typeof n!="object")return a;if(t>o)throw new h(`Maximum nesting depth of ${o} exceeded while validating. Raise it with the maxDepth option if this structure is legitimate.`);if(e.has(n))return a;e.add(n);try{if(Array.isArray(n)){for(let i=0;i0&&a.push({property:`[${i}]`,value:n[i],constraints:{},children:s})}return a}for(let i of bt(B(n))){let s=i.key,c=n[s];if(i.condition&&!i.condition(n)||i.isOptional&&c==null)continue;let p={property:s,value:i.redact?On:c,constraints:{}},f={value:c,object:n,property:s,constraints:[]},m=!1;for(let l of i.constraints){f.constraints=l.constraints||[];let d=l.each&&Array.isArray(c)?wt(l,c,f):(()=>{let u=l.validate(c,f);return v(u)?u.then(b=>({ok:b,index:-1})):{ok:u,index:-1}})();if(v(d)){m=!0;let u={value:c,object:n,property:s,constraints:l.constraints||[]};r.push(d.then(({ok:b,index:w})=>{b||(w>=0&&(u.value=c[w]),wn(p.constraints,l.name,xn(l,u,w)))}));continue}if(!d.ok){d.index>=0&&(f.value=c[d.index]);let u=xn(l,f,d.index);f.value=c,wn(p.constraints,l.name,u)}}if(i.isNested&&c!==null&&c!==void 0){let l=L(c,e,t+1,o,r);l.length>0&&(p.children=l)}(m||Object.keys(p.constraints).length>0||p.children)&&a.push(p)}return a}finally{e.delete(n)}}function Vn(n){let e=C(n);return{naming:U(e.namingStrategy),namingKey:e.namingStrategy,maxDepth:e.maxDepth}}function En(n){let e=C(n);return{naming:U(e.namingStrategy),namingKey:e.namingStrategy,unknownKeys:e.unknownKeys,maxDepth:e.maxDepth}}async function F(n,e){let t=[],o=L(n,new Set,0,C(e).maxDepth,t);return t.length===0?o:(await en(t),Dn(o))}function q(n,e){let t=[],o=L(n,new Set,0,C(e).maxDepth,t);return tn(t,"validateSync()","validate()"),o}async function In(n,e){let t=await F(n,e);if(t.length>0)throw new S("Validation failed",t)}function Ot(n,e){let t=q(n,e);if(t.length>0)throw new S("Validation failed",t)}async function on(n,e){if(n==null)return n;if(C(e).validate){let r=await F(n,e);if(r.length>0)throw new S("Validation failed during serialization",r)}let t=[],o=K(n,new Set,Vn(e),0,t,[]);return await en(t),o}function An(n,e){if(n==null)return n;if(C(e).validate){let r=q(n,e);if(r.length>0)throw new S("Validation failed during serialization",r)}let t=[],o=K(n,new Set,Vn(e),0,t,[]);return tn(t,"toPlainSync()","toPlain()"),o}async function Nn(n,e){return JSON.stringify(await on(n,e))}function Ct(n,e){return JSON.stringify(An(n,e))}async function J(n,e,t){let o=[],r=I(n,e,En(t),0,o);if(await en(o),C(t).validate){let a=await F(r,t);if(a.length>0)throw new S("Validation failed during deserialization",a)}return r}function rn(n,e,t){let o=[],r=I(n,e,En(t),0,o);if(tn(o,"toInstanceSync()","toInstance()"),C(t).validate){let a=q(r,t);if(a.length>0)throw new S("Validation failed during deserialization",a)}return r}function Mn(n,e){if(!Array.isArray(e))throw new h(`Expected an array to map to ${n.name}[], received ${typeof e}.`)}async function an(n,e,t){return Mn(n,e),await J(n,e,t)}function Pn(n,e,t){return Mn(n,e),rn(n,e,t)}async function Rn(n,e,t){return J(n,W(e),t)}function Tt(n,e,t){return rn(n,W(e),t)}async function vn(n,e,t){return an(n,W(e),t)}function kt(n,e,t){return Pn(n,W(e),t)}function W(n){try{return JSON.parse(n)}catch(e){throw new h(`Input is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}}async function Fn(n,e,t){let o;try{o=await e.json()}catch(r){throw new h(`Request body is not valid JSON: ${r instanceof Error?r.message:String(r)}`)}return J(n,o,t)}var nn=class{static toPlain=on;static toJson=Nn;static toInstance=J;static toInstanceArray=an;static fromJson=Rn;static fromJsonArray=vn;static fromRequest=Fn;static validate=F;static validateOrReject=In};return _n(St);})(); diff --git a/docs/diagnostics.js b/docs/diagnostics.js new file mode 100644 index 0000000..1f8906f --- /dev/null +++ b/docs/diagnostics.js @@ -0,0 +1,29 @@ +// Generated by scripts/build-docs.mjs from real tsc output — do not edit. +window.CEREALE_DIAGNOSTICS = { + "hero": [ + { + "code": 1240, + "messages": [ + "Unable to resolve signature of property decorator when called as an expression.", + "Argument of type 'ClassFieldDecoratorContext & { name: \"placedAt\"; private: false; static: false; }' is not assignable to parameter of type 'ClassFieldDecoratorContext'.", + "The types returned by 'access.get(...)' are incompatible between these types.", + "Type 'Date' is not assignable to type 'string'." + ], + "line": 12 + } + ], + "compare": [ + { + "code": 1240, + "messages": [ + "Unable to resolve signature of property decorator when called as an expression.", + "Argument of type 'ClassFieldDecoratorContext & { name: \"age\"; private: false; static: false; }' is not assignable to parameter of type 'ClassFieldDecoratorContext'.", + "The types returned by 'access.get(...)' are incompatible between these types.", + "Type 'number' is not assignable to type 'string'." + ], + "line": 4 + } + ], + "instances": [], + "correct": [] +}; diff --git a/docs/index.html b/docs/index.html index c1a7955..a8b8ca1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,289 +1,856 @@ - - - Cereale - Spring-like JSON Mapping & Validation for TypeScript - - - - - - - - + + +cereale — validated domain objects, not validated data + + + + + - -