diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 250afa6..99ad018 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: 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');" + node --input-type=module -e "import * as m from './dist/cereale.min.js'; if (typeof m.toInstanceSync !== 'function') throw new Error('flat bundle broken');" - name: Run Demo run: npm run demo - name: Published types stand alone diff --git a/.github/workflows/junie-review.yml b/.github/workflows/junie-review.yml new file mode 100644 index 0000000..382fbcc --- /dev/null +++ b/.github/workflows/junie-review.yml @@ -0,0 +1,48 @@ +name: Junie Review + +# Automated PR review by Junie, JetBrains' coding agent. Advisory only: it posts a +# summary and inline comments, and is deliberately not a required check — CI is what +# gates a merge, and a review that can block one on a judgement call is a review that +# gets rubber-stamped. +# +# Requires a JUNIE_API_KEY repository secret (Settings → Secrets and variables → +# Actions). Without it the action fails at the first step rather than skipping, so +# add the secret before merging this file. + +on: + pull_request: + types: [ opened, synchronize, reopened ] + branches: [ main, develop ] + +# A PR that gets three pushes in a minute should end up with one review of the final +# state, not three reviews of intermediate ones. Combined with use_single_comment +# below, each PR keeps exactly one review comment, rewritten as the diff changes. +concurrency: + group: junie-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + name: Junie + runs-on: ubuntu-latest + + # Secrets are not exposed to `pull_request` runs originating from a fork, so a + # fork PR would fail on an empty API key rather than review anything. Skip those + # explicitly — a skipped job reads as "not applicable", a failed one as "broken". + if: github.event.pull_request.head.repo.full_name == github.repository + + permissions: + contents: read # read the diff; Junie does not push from this workflow + pull-requests: write # post the review summary and inline comments + issues: write # the PR conversation is an issue timeline to the API + + steps: + - uses: actions/checkout@v4 + - name: Review the pull request + uses: JetBrains/junie-github-action@v1 + with: + junie_api_key: ${{ secrets.JUNIE_API_KEY }} + # Built-in structured review prompt, as opposed to a free-form instruction. + prompt: "code-review" + # Update one comment across re-runs instead of appending a new one per push. + use_single_comment: "true" diff --git a/CHANGELOG.md b/CHANGELOG.md index f11a858..46059c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,119 @@ 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.4.0] - 2026-08-05 + +### `cereale/min` — one file, no bundler + +The whole library flattened into a single minified ES module: **33.9 KB, 9.6 KB gzipped**, for +import maps, ` +``` diff --git a/README.md b/README.md index b308c8d..1e3d79e 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,9 @@ user.greet(); // your methods are still there **[avalon-vanguard.github.io/cereale](https://avalon-vanguard.github.io/cereale/)** — an interactive playground that runs this library in your browser, the full decorator reference, -and the toolchain matrix. The page is self-contained and loads nothing from the network; it is -served from `docs/` on `main`, and `npm run build:docs` rebuilds its assets to open locally. +and the toolchain matrix. The page has no third-party dependencies — no CDN, no analytics, no webfonts; the only thing it +fetches is its own vendored compiler, and only when you first press Run. It is served from +`docs/` on `main`, and `npm run build:docs` rebuilds its assets to open locally. ## Where it fits @@ -108,6 +109,20 @@ inside a native binary with no standalone transform API. | swc | ✅ | `jsc.transform.decoratorVersion: "2022-03"` | | **oxc** | ❌ | Used by **Vite 8** and **Vitest 4** — see below | +### Frameworks + +**[FRAMEWORKS.md](FRAMEWORKS.md)** has a setup recipe for each, every one of them run before it +was written. The short version: + +| | | | +| --- | --- | --- | +| **Angular** 21 | ✅ | Flip the scaffolded `experimentalDecorators` to `false`. Angular does not need it — `ngtsc` erases its own decorators itself | +| **React, Vue, Svelte, Solid, Astro, Nuxt** | ✅ | Any Vite 8 app: add the `cereale/vite` plugin below | +| **Bun** | ✅ | No configuration | +| **Node** + `tsc` | ✅ | Just the flag | +| **Next.js** 16 | ⚠️ | Not inline: it derives both the SWC parser *and* the transform from one flag, so decorators either compile legacy or fail to parse. Keep your models in a package compiled by `tsc` | +| **NestJS** 11 | ⚠️ | Not inline: its DI needs `emitDecoratorMetadata`. Same precompiled-package route — verified working alongside `@Injectable()` in a program with both legacy flags on | + **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 @@ -480,6 +495,50 @@ costs a few percent on `toPlain`, which is the price of never emitting `{}` wher 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. +## Bundle size + +Cereale tree-shakes. Every rule is declared so that a bundler can drop the ones you did not +import, which matters for a library with 68 decorators — you pay for what you name and nothing +else. Minified bytes, measured through esbuild, rollup and webpack. The table was measured by hand; +what is [pinned by a test](src/treeshake.test.ts) is the property behind it — that a given +import drops the parts of the library it does not reach — checked through esbuild on both the +source and the published bundle: + +| What you import | esbuild | rollup | webpack | +| --- | ---: | ---: | ---: | +| `flattenErrors` | 394 | 367 | 394 | +| one decorator | 1,837 | 1,823 | 1,818 | +| `validateSync` | 3,722 | 3,554 | 3,738 | +| `toPlainSync` | 7,744 | 7,769 | 7,771 | +| `toInstanceSync` | 7,900 | 7,942 | 7,944 | +| a typical DTO — 5 decorators, map, validate, format errors | 10,395 | 10,402 | 10,360 | +| the whole library | 26,266 | 25,671 | 26,879 | + +The serializer and the deserializer drop independently: read JSON and you do not pay for +writing it. The validator is kept by both, because `validate` defaults to `true` and the entry +points reference it whatever a given call site passes. + +The floor is about a hundred bytes: cereale installs `Symbol.metadata` if the runtime lacks it, +and that install has to survive tree-shaking or a `tsc`-compiled consumer decorates its classes +with no metadata at all. It is why `sideEffects` names `index.js` as well as `metadata.js` — +marking only the latter leaves the barrel itself droppable, so the edge to it is pruned before +its own marking is ever read. That cost lands only on the first row; every import that touches a +model was already carrying it. + +This did not come for free. Thirty of the rules are declared as top-level calls — +`export const IsString = rule(…)` — and rollup can prove such a call side-effect-free by reading +the factory, but esbuild and webpack will not. Without a `/*#__PURE__*/` annotation on each of +them, importing one decorator pulled in the message and validator of all 68: **4,909 bytes +instead of 1,837**. Nothing failed; the library was simply three times heavier in every +consumer's bundle, and the only way to find out was to measure. Note what that means for +measuring: rollup alone would have shown nothing wrong. + +`cereale/min` tree-shakes too, which took a second fix — esbuild's `minify` strips comments, +annotations included, so the flat bundle was silently reproducing the same bug (5,066 bytes for +one decorator). It is now minified for syntax and identifiers but not whitespace: 33.9 KB raw, +9.6 KB gzipped, about a kilobyte over the wire more than full minification would give. Still, +if you are using a bundler, import from `cereale` rather than `cereale/min`. + ## Notes and Limitations - **Rules are checked, types are not inferred.** You write both the field type and the rule; diff --git a/docs/cereale.js b/docs/cereale.js index 1806bc7..3a5c5ee 100644 --- a/docs/cereale.js +++ b/docs/cereale.js @@ -1,2 +1,2 @@ -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);})(); +var Cereale=(()=>{var Z=Object.defineProperty;var zn=Object.getOwnPropertyDescriptor;var Bn=Object.getOwnPropertyNames;var Un=Object.prototype.hasOwnProperty;var jn=(n,e)=>{for(var t in e)Z(n,t,{get:e[t],enumerable:!0})},_n=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Bn(e))!Un.call(n,r)&&r!==t&&Z(n,r,{get:()=>e[r],enumerable:!(o=zn(e,r))||o.enumerable});return n};var Kn=n=>_n(Z({},"__esModule",{value:!0}),n);var St={};jn(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:()=>Cn,StartsWith:()=>We,Validate:()=>ct,ValidateIf:()=>ae,ValidateNested:()=>se,addConstraint:()=>M,collectErrorMessages:()=>pt,configure:()=>qn,defineRule:()=>cn,fieldMetadata:()=>x,flattenErrors:()=>X,formatErrors:()=>dt,fromJson:()=>vn,fromJsonArray:()=>Fn,fromJsonArraySync:()=>kt,fromJsonSync:()=>Tt,fromRequest:()=>Jn,getConfig:()=>Wn,modelOf:()=>z,modelOfInstance:()=>B,modelVersion:()=>$,propertyModel:()=>O,resetConfig:()=>Gn,resolveNamingStrategy:()=>U,resolveOptions:()=>C,toInstance:()=>J,toInstanceArray:()=>an,toInstanceArraySync:()=>Rn,toInstanceSync:()=>rn,toJson:()=>Mn,toJsonSync:()=>Ct,toPlain:()=>on,toPlainSync:()=>Nn,validate:()=>F,validateOrReject:()=>An,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 Ln(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=Ln(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 cn(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 dn=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(dn).join("")},PascalCase:n=>P(n).map(dn).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 pn={namingStrategy:"identity",unknownKeys:"allow",validate:!0,maxDepth:64},k={...pn};function qn(n){k={...k,...n}}function Wn(){return{...k}}function Gn(){k={...pn}}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 fn=/^(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}$/,yn=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?fn.test(o):n===6?yn(o):fn.test(o)||yn(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"]),Cn="[redacted]";function Tn(n,e){return n[e]?.access??"readwrite"}function kn(n,e,t){return n[e]?.name??t(e)}var mn=new WeakMap;function yt(n,e,t){let o=mn.get(n);(!o||o.version!==$())&&(o={version:$(),byStrategy:new Map},mn.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=Tn(n,e),s=n[e]?.serializer;a={name:kn(n,e,t.naming),skip:i==="none"||i==="writeonly",...s?{serializer:s}:{}},r.set(e,a)}return a}var gn=new WeakMap;function mt(n,e){let t=gn.get(n);(!t||t.version!==$())&&(t={version:$(),byStrategy:new Map},gn.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=[kn(n,l,e.naming),...d.aliases??[]],b=Tn(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 Sn(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 $n="a bigint, which JSON has no representation for";function hn(n){switch(typeof n){case"bigint":return $n;case"symbol":return"a symbol";case"function":return"a function";case"object":return n===null?null:Sn(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($n,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=Sn(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=hn(w);A!==null&&E(A,b),p[u]=w}))}else{let u=hn(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=Dn(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 bn=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=bn.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 bn.set(n,{version:$(),plan:t}),t}var wn=new WeakMap;function Dn(n){let e=wn.get(n);return e||(e=new n,wn.set(n,e)),e}function xn(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 On(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?Cn: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]),xn(p.constraints,l.name,On(l,u,w)))}));continue}if(!d.ok){d.index>=0&&(f.value=c[d.index]);let u=On(l,f,d.index);f.value=c,xn(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 En(n){let e=C(n);return{naming:U(e.namingStrategy),namingKey:e.namingStrategy,maxDepth:e.maxDepth}}function In(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),Vn(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 An(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,En(e),0,t,[]);return await en(t),o}function Nn(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,En(e),0,t,[]);return tn(t,"toPlainSync()","toPlain()"),o}async function Mn(n,e){return JSON.stringify(await on(n,e))}function Ct(n,e){return JSON.stringify(Nn(n,e))}async function J(n,e,t){let o=[],r=I(n,e,In(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,In(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 Pn(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 Pn(n,e),await J(n,e,t)}function Rn(n,e,t){return Pn(n,e),rn(n,e,t)}async function vn(n,e,t){return J(n,W(e),t)}function Tt(n,e,t){return rn(n,W(e),t)}async function Fn(n,e,t){return an(n,W(e),t)}function kt(n,e,t){return Rn(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 Jn(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=Mn;static toInstance=J;static toInstanceArray=an;static fromJson=vn;static fromJsonArray=Fn;static fromRequest=Jn;static validate=F;static validateOrReject=An};return Kn(St);})(); diff --git a/docs/index.html b/docs/index.html index adc51bf..7367112 100644 --- a/docs/index.html +++ b/docs/index.html @@ -271,6 +271,7 @@ header.nav { .panel p { color: var(--text-muted); font-size: .9375rem; margin: 0; } .tick { color: var(--ok); font-weight: 700; } .cross { color: var(--bad); font-weight: 700; } +.warn-mark { color: var(--warn); font-weight: 700; } .compare { display: grid; gap: 1rem; } @media (min-width: 860px) { .compare { grid-template-columns: 1fr 1fr; } } @@ -693,6 +694,26 @@ Break the cycle with @JsonIgnore() on the back-reference, or supply a that fixes it. +
+ + + + + + + + + + + +
Framework support
FrameworkWorksWhat it takes
Angular 21✓Flip the scaffolded experimentalDecorators to false — Angular does not need it
React, Vue, Svelte…✓Any Vite 8 app — add the plugin below
Bun✓Nothing
Node + tsc✓Just the flag
Next.js 16~Not inline — keep models in a package compiled by tsc
NestJS 11~Not inline — its DI needs emitDecoratorMetadata; same precompiled route
+
+

+ Each of these was set up and run before it was written down. + FRAMEWORKS.md + has the full recipe for every one, including the two that need the precompiled route. +

+
vite.config.ts
import { defineConfig } from 'vite';
diff --git a/docs/meta.js b/docs/meta.js
index 752d437..e7ae56a 100644
--- a/docs/meta.js
+++ b/docs/meta.js
@@ -1,5 +1,5 @@
 // Generated by scripts/build-docs.mjs — do not edit.
 window.CEREALE_META = {
-  "version": "0.3.0",
+  "version": "0.4.0",
   "node": ">=20.0.0"
 };
diff --git a/package.json b/package.json
index 6fb0ce8..a4d34f8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cereale",
-  "version": "0.3.0",
+  "version": "0.4.0",
   "description": "Strongly typed JSON mapping and validation for TypeScript classes \u2014 validated domain objects, not validated data. A zero-dependency replacement for class-validator + class-transformer, on TC39 standard decorators.",
   "type": "module",
   "main": "./dist/cjs/index.js",
@@ -16,21 +16,29 @@
       "types": "./dist/esm/vite.d.ts",
       "import": "./dist/esm/vite.js",
       "require": "./dist/cjs/vite.js"
+    },
+    "./min": {
+      "types": "./dist/esm/index.d.ts",
+      "default": "./dist/cereale.min.js"
     }
   },
   "sideEffects": [
+    "./dist/esm/index.js",
     "./dist/esm/metadata.js",
-    "./dist/cjs/metadata.js"
+    "./dist/cereale.min.js",
+    "./src/index.ts",
+    "./src/metadata.ts"
   ],
   "files": [
     "dist",
     "src",
+    "FRAMEWORKS.md",
     "CHANGELOG.md",
     "!src/**/*.test.ts",
     "!src/example.ts"
   ],
   "scripts": {
-    "build": "rm -rf dist && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json",
+    "build": "rm -rf dist && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node scripts/build-bundle.mjs",
     "build:docs": "node scripts/build-docs.mjs",
     "demo": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/example.ts",
     "type-check": "tsc --noEmit",
diff --git a/scripts/build-bundle.mjs b/scripts/build-bundle.mjs
new file mode 100644
index 0000000..6b331be
--- /dev/null
+++ b/scripts/build-bundle.mjs
@@ -0,0 +1,82 @@
+/**
+ * Flattens the ESM build into a single minified module.
+ *
+ * This is an *addition*, not a replacement. `dist/esm` stays the default `import`: it keeps
+ * readable stack traces for anyone who does not load source maps, and it is what a bundler
+ * should be handed.
+ *
+ * Where the single file does earn its place is everywhere a bundler is not involved: a
+ * `