diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 202390f..250afa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,3 +41,12 @@ jobs: 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 34a0472..3a1cfb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,34 @@ came free with the bookkeeping. 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. A third snippet +that must compile guards against the harness passing vacuously. ### Positioning diff --git a/README.md b/README.md index fe3fdb5..6717f65 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ const user = fromJsonSync(User, body); // a real User user.greet(); // your methods are still there ``` +`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. + ## Where it fits The stack Cereale replaces is **class-validator + class-transformer**: diff --git a/docs/cereale.js b/docs/cereale.js index a68c825..4c326e4 100644 --- a/docs/cereale.js +++ b/docs/cereale.js @@ -1,2 +1,2 @@ -"use strict";var Cereale=(()=>{var Z=Object.defineProperty;var Fn=Object.getOwnPropertyDescriptor;var Jn=Object.getOwnPropertyNames;var zn=Object.prototype.hasOwnProperty;var Bn=(n,e)=>{for(var t in e)Z(n,t,{get:e[t],enumerable:!0})},Un=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Jn(e))!zn.call(n,r)&&r!==t&&Z(n,r,{get:()=>e[r],enumerable:!(o=Fn(e,r))||o.enumerable});return n};var jn=n=>Un(Z({},"__esModule",{value:!0}),n);var kt={};Bn(kt,{Allow:()=>ae,ArrayContains:()=>at,ArrayMaxSize:()=>ot,ArrayMinSize:()=>tt,ArrayNotContains:()=>it,ArrayNotEmpty:()=>et,ArrayUnique:()=>rt,Contains:()=>Ke,Email:()=>Ve,EndsWith:()=>We,Equals:()=>Ge,IsAlpha:()=>Ee,IsAlphanumeric:()=>Ie,IsArray:()=>nt,IsBigInt:()=>de,IsBoolean:()=>ce,IsDate:()=>pe,IsDateString:()=>ve,IsDefined:()=>ye,IsDivisibleBy:()=>Oe,IsEmpty:()=>ge,IsEnum:()=>Xe,IsHexColor:()=>Ne,IsIP:()=>_e,IsIn:()=>He,IsInstance:()=>Qe,IsInt:()=>ue,IsJSON:()=>Fe,IsLatitude:()=>Te,IsLongitude:()=>ke,IsLowercase:()=>Me,IsNotEmpty:()=>me,IsNotIn:()=>Ye,IsNumber:()=>le,IsNumberString:()=>Pe,IsObject:()=>fe,IsOptional:()=>oe,IsPort:()=>Ce,IsSemVer:()=>Ae,IsString:()=>se,IsUUID:()=>je,IsUppercase:()=>Re,IsUrl:()=>Je,JsonAlias:()=>Zn,JsonDeserialize:()=>ne,JsonIgnore:()=>Hn,JsonMapper:()=>nn,JsonMappingError:()=>h,JsonPolymorphic:()=>te,JsonProperty:()=>Gn,JsonReadOnly:()=>Yn,JsonSerialize:()=>Qn,JsonType:()=>ee,JsonValidationError:()=>D,JsonWriteOnly:()=>Xn,Length:()=>$e,Matches:()=>ze,Max:()=>be,MaxDate:()=>lt,MaxLength:()=>Se,Min:()=>he,MinDate:()=>st,MinLength:()=>De,Negative:()=>xe,NotContains:()=>Le,NotEquals:()=>Ze,Positive:()=>we,REDACTED:()=>xn,StartsWith:()=>qe,Validate:()=>ut,ValidateIf:()=>re,ValidateNested:()=>ie,addConstraint:()=>M,collectErrorMessages:()=>dt,configure:()=>Ln,defineRule:()=>Kn,fieldMetadata:()=>b,flattenErrors:()=>X,formatErrors:()=>ct,fromJson:()=>Rn,fromJsonArray:()=>Pn,fromJsonArraySync:()=>Tt,fromJsonSync:()=>Ct,fromRequest:()=>vn,getConfig:()=>qn,modelOf:()=>z,modelOfInstance:()=>B,modelVersion:()=>S,propertyModel:()=>w,resetConfig:()=>Wn,resolveNamingStrategy:()=>U,resolveOptions:()=>x,toInstance:()=>J,toInstanceArray:()=>an,toInstanceArraySync:()=>Mn,toInstanceSync:()=>rn,toJson:()=>An,toJsonSync:()=>Ot,toPlain:()=>on,toPlainSync:()=>In,validate:()=>F,validateOrReject:()=>En,validateOrRejectSync:()=>xt,validateSync:()=>q});var $=Symbol.metadata??Symbol.for("Symbol.metadata");Symbol.metadata??=$;var N=Symbol.for("cereale.model"),ln=0;function S(){return ln}function _n(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 b(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 w(n,e){ln++;let t=_n(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),w(n,e).constraints.push(t)}function z(n){return typeof n!="function"?{}:n[$]?.[N]??{}}function B(n){let e=Object.getPrototypeOf(n);if(!e)return{};let t=Object.getOwnPropertyDescriptor(e,"constructor");return z(t?.value)}function Kn(n,e,t,o){let r=n;Object.hasOwn(r,$)||(r[$]=Object.create(r[$]??null)),M(r[$],e,t,o)}function R(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 un=n=>n&&n.charAt(0).toUpperCase()+n.slice(1),H={identity:n=>n,camelCase:n=>{let e=R(n);return e.length===0?n:e[0]+e.slice(1).map(un).join("")},PascalCase:n=>R(n).map(un).join("")||n,snake_case:n=>R(n).join("_")||n,SCREAMING_SNAKE_CASE:n=>R(n).join("_").toUpperCase()||n,"kebab-case":n=>R(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 cn={namingStrategy:"identity",unknownKeys:"allow",validate:!0,maxDepth:64},k={...cn};function Ln(n){k={...k,...n}}function qn(){return{...k}}function Wn(){k={...cn}}function x(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(b(o),r,n(r),e)})}function m(n,e,t){return(o=>y(r=>({name:n,validate:e,message:t(r)}),o))}function P(n,e,t){return m(n,o=>typeof o=="string"&&e.test(o),t)}function Gn(n){return((e,t)=>{w(b(t),String(t.name)).name=n})}function Zn(...n){return((e,t)=>{let o=w(b(t),String(t.name));o.aliases=[...o.aliases??[],...n]})}function Y(n){return((e,t)=>{w(b(t),String(t.name)).access=n})}var Hn=()=>Y("none"),Yn=()=>Y("readonly"),Xn=()=>Y("writeonly");function Qn(n){return((e,t)=>{w(b(t),String(t.name)).serializer=n})}function ne(n){return((e,t)=>{w(b(t),String(t.name)).deserializer=n})}function ee(n){return((e,t)=>{w(b(t),String(t.name)).type=n})}function te(n,e,t){return((o,r)=>{let a={discriminator:n,subTypes:e,onUnknown:t?.onUnknown??"keep",...t?.fallback?{fallback:t.fallback}:{}};w(b(r),String(r.name)).polymorphic=a})}function oe(){return((n,e)=>{w(b(e),String(e.name)).optional=!0})}function re(n){return((e,t)=>{w(b(t),String(t.name)).condition=n})}function ae(){return((n,e)=>{w(b(e),String(e.name))})}function ie(n){return((e,t)=>{let o=b(t),r=String(t.name);w(o,r).nested=!0,n?.each&&M(o,r,{name:"nestedEach",validate:a=>Array.isArray(a),message:`${r} must be an array`})})}var se=m("isString",n=>typeof n=="string",n=>`${n} must be a string`),le=m("isNumber",n=>typeof n=="number"&&!isNaN(n),n=>`${n} must be a number`),ue=m("isInt",n=>Number.isInteger(n),n=>`${n} must be an integer`),ce=m("isBoolean",n=>typeof n=="boolean",n=>`${n} must be a boolean`),de=m("isBigInt",n=>typeof n=="bigint",n=>`${n} must be a bigint`),pe=m("isDate",n=>n instanceof Date&&!isNaN(n.getTime()),n=>`${n} must be a valid Date object`),fe=m("isObject",n=>typeof n=="object"&&n!==null&&!Array.isArray(n),n=>`${n} must be an object`),ye=m("isDefined",n=>n!=null,n=>`${n} should not be null or undefined`),me=m("isNotEmpty",n=>n!=null&&n!=="",n=>`${n} should not be empty`),ge=m("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 he(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 be(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 we=m("positive",n=>typeof n=="number"&&n>0,n=>`${n} must be positive`),xe=m("negative",n=>typeof n=="number"&&n<0,n=>`${n} must be negative`);function Oe(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 Ce=m("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`),Te=m("isLatitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-90&&n<=90,n=>`${n} must be a latitude between -90 and 90`),ke=m("isLongitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-180&&n<=180,n=>`${n} must be a longitude between -180 and 180`);function De(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 Se(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 $e(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 Ve=P("isEmail",/^[^\s@]+@[^\s@]+\.[^\s@]+$/,n=>`${n} must be a valid email`),Ee=P("isAlpha",/^[A-Za-z]+$/,n=>`${n} must contain only letters`),Ie=P("isAlphanumeric",/^[A-Za-z0-9]+$/,n=>`${n} must contain only letters and numbers`),Ae=P("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`),Ne=P("isHexColor",/^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,n=>`${n} must be a hex color`),Me=m("isLowercase",n=>typeof n=="string"&&n===n.toLowerCase(),n=>`${n} must be lowercase`),Re=m("isUppercase",n=>typeof n=="string"&&n===n.toUpperCase(),n=>`${n} must be uppercase`),Pe=m("isNumberString",n=>typeof n=="string"&&n.trim()!==""&&Number.isFinite(Number(n)),n=>`${n} must be a number string`),ve=m("isDateString",n=>typeof n=="string"&&!isNaN(Date.parse(n)),n=>`${n} must be a valid ISO 8601 date string`),Fe=m("isJson",n=>{if(typeof n!="string")return!1;try{return JSON.parse(n),!0}catch{return!1}},n=>`${n} must be a JSON string`),Je=m("isUrl",n=>{try{return new URL(n),!0}catch{return!1}},n=>`${n} must be a valid URL`);function ze(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 Be="00000000-0000-0000-0000-000000000000",Ue="ffffffff-ffff-ffff-ffff-ffffffffffff";function je(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()===Be||r.toLowerCase()===Ue)?!0:t.test(r),message:`${o} must be a valid UUID${n?` (version ${n})`:""}`,...n?{constraints:[n]}:{}}),e)}var dn=/^(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}$/,pn=n=>{try{return new URL(`http://[${n}]`).hostname===`[${n.toLowerCase()}]`||/^[0-9a-f:.]+$/i.test(n)&&n.includes(":")}catch{return!1}};function _e(n,e){return y(t=>({name:"isIp",validate:o=>typeof o!="string"?!1:n===4?dn.test(o):n===6?pn(o):dn.test(o)||pn(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 Ke=_("contains",(n,e)=>n.includes(e),(n,e)=>`${n} must contain ${JSON.stringify(e)}`),Le=_("notContains",(n,e)=>!n.includes(e),(n,e)=>`${n} must not contain ${JSON.stringify(e)}`),qe=_("startsWith",(n,e)=>n.startsWith(e),(n,e)=>`${n} must start with ${JSON.stringify(e)}`),We=_("endsWith",(n,e)=>n.endsWith(e),(n,e)=>`${n} must end with ${JSON.stringify(e)}`);function Ge(n,e){return y(t=>({name:"equals",validate:o=>o===n,message:`${t} must be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function Ze(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 He(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 Ye(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 Xe(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 Qe(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 nt=n=>y(e=>({name:"isArray",validate:t=>Array.isArray(t),message:`${e} must be an array`}),n),et=n=>V("arrayNotEmpty",e=>e.length>0,e=>`${e} should not be empty`)(n),tt=(n,e)=>V("arrayMinSize",t=>t.length>=n,t=>`${t} must contain at least ${n} elements`,[n])(e),ot=(n,e)=>V("arrayMaxSize",t=>t.length<=n,t=>`${t} must contain at most ${n} elements`,[n])(e);function rt(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 at(n,e){return V("arrayContains",t=>n.every(o=>t.includes(o)),t=>`${t} must contain the following values: ${n.join(", ")}`,[n])(e)}function it(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 st(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 lt(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 ut(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:(l,u)=>s(l,u),message:l=>`${l.property} is invalid`,constraints:o}),r)}let i=new n;return y(()=>({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 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 ct(n){let e=X(n);return Object.entries(e).flatMap(([t,o])=>o.map(r=>`${t}: ${r}`)).join(` -`)}function dt(n){return Object.values(X(n)).flat()}var D=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"}},pt=new Set(["__proto__","constructor","prototype"]),xn="[redacted]";function On(n,e){return n[e]?.access??"readwrite"}function Cn(n,e,t){return n[e]?.name??t(e)}var fn=new WeakMap;function ft(n,e,t){let o=fn.get(n);(!o||o.version!==S())&&(o={version:S(),byStrategy:new Map},fn.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=On(n,e),s=n[e]?.serializer;a={name:Cn(n,e,t.naming),skip:i==="none"||i==="writeonly",...s?{serializer:s}:{}},r.set(e,a)}return a}var yn=new WeakMap;function yt(n,e){let t=yn.get(n);(!t||t.version!==S())&&(t={version:S(),byStrategy:new Map},yn.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 p=r.get(u);if(p&&p!==c)throw new h(`Properties "${p}" 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 p=[Cn(n,u,e.naming),...c.aliases??[]],f=On(n,u);if(f==="none"||f==="readonly"){for(let d of p)a.add(d);continue}for(let d of p)s(d,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 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 mt={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 Tn(n){let e=n[Symbol.toStringTag];if(typeof e=="string"){let t=mt[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}function mn(n){switch(typeof n){case"bigint":return kn;case"symbol":return"a symbol";case"function":return"a function";case"object":return n===null?null:Tn(n);default:return null}}var kn="a bigint, which JSON has no representation for";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(kn,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 l=Tn(n);l!==null&&E(l,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 c=[];for(let p=0;p{let A=mn(C);A!==null&&E(A,O),u[g]=C}))}else{let g=mn(d);g!==null&&E(g,a),u[p.name]=d}}else u[p.name]=K(f,e,t,o+1,r,a);a.pop()}return u}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=yt(z(n),t);for(let s of Object.keys(e)){if(pt.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 h(`Unknown property ${JSON.stringify(s)} for ${n.name}. Allowed: ${[...i.accept.keys()].map(d=>JSON.stringify(d)).join(", ")||"(none declared)"}.`);a[s]=e[s];continue}let u=e[s],c=i.props.get(l);if(c?.deserializer){let d=Dn(c.deserializer).deserialize(u);if(v(d)){let g=l;a[g]=void 0,r.push(d.then(O=>{a[g]=O}))}else a[l]=d;continue}let p=c?.polymorphic;if(p&&u!==null&&u!==void 0){let{discriminator:d,subTypes:g,onUnknown:O,fallback:C}=p,A=T=>{if(T==null||typeof T!="object")return T;let sn=g.find(G=>T[d]===G.name);if(sn)return I(sn.value,T,t,o+1,r);if(C)return I(C,T,t,o+1,r);if(O==="error")throw new h(`Unknown discriminator value ${JSON.stringify(T[d])} for property "${l}". Known values: ${g.map(G=>JSON.stringify(G.name)).join(", ")}.`);return T};a[l]=Array.isArray(u)?u.map(A):A(u);continue}let f=c?.typeFn;if(f&&u!==null&&u!==void 0){let d=f();a[l]=I(d,u,t,o+1,r);continue}a[l]=u}return a}var gn=new WeakMap;function gt(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 ht(n){let e=gn.get(n);if(e&&e.version===S())return e.plan;let t=[];for(let[o,r]of Object.entries(n)){let a=r.access??"readwrite";t.push({key:o,constraints:gt(r.constraints),isOptional:!!r.optional,isNested:!!r.nested,redact:a==="writeonly"||a==="none",...r.condition?{condition:r.condition}:{}})}return gn.set(n,{version:S(),plan:t}),t}var hn=new WeakMap;function Dn(n){let e=hn.get(n);return e||(e=new n,hn.set(n,e)),e}function bn(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 wn(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 bt(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 ht(B(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?xn:l,constraints:{}},c={value:l,object:n,property:s,constraints:[]},p=!1;for(let f of i.constraints){c.constraints=f.constraints||[];let d=f.each&&Array.isArray(l)?bt(f,l,c):(()=>{let g=f.validate(l,c);return v(g)?g.then(O=>({ok:O,index:-1})):{ok:g,index:-1}})();if(v(d)){p=!0;let g={value:l,object:n,property:s,constraints:f.constraints||[]};r.push(d.then(({ok:O,index:C})=>{O||(C>=0&&(g.value=l[C]),bn(u.constraints,f.name,wn(f,g,C)))}));continue}if(!d.ok){d.index>=0&&(c.value=l[d.index]);let g=wn(f,c,d.index);c.value=l,bn(u.constraints,f.name,g)}}if(i.isNested&&l!==null&&l!==void 0){let f=L(l,e,t+1,o,r);f.length>0&&(u.children=f)}(p||Object.keys(u.constraints).length>0||u.children)&&a.push(u)}return a}finally{e.delete(n)}}function $n(n){let e=x(n);return{naming:U(e.namingStrategy),namingKey:e.namingStrategy,maxDepth:e.maxDepth}}function Vn(n){let e=x(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,x(e).maxDepth,t);return t.length===0?o:(await en(t),Sn(o))}function q(n,e){let t=[],o=L(n,new Set,0,x(e).maxDepth,t);return tn(t,"validateSync()","validate()"),o}async function En(n,e){let t=await F(n,e);if(t.length>0)throw new D("Validation failed",t)}function xt(n,e){let t=q(n,e);if(t.length>0)throw new D("Validation failed",t)}async function on(n,e){if(n==null)return n;if(x(e).validate){let r=await F(n,e);if(r.length>0)throw new D("Validation failed during serialization",r)}let t=[],o=K(n,new Set,$n(e),0,t,[]);return await en(t),o}function In(n,e){if(n==null)return n;if(x(e).validate){let r=q(n,e);if(r.length>0)throw new D("Validation failed during serialization",r)}let t=[],o=K(n,new Set,$n(e),0,t,[]);return tn(t,"toPlainSync()","toPlain()"),o}async function An(n,e){return JSON.stringify(await on(n,e))}function Ot(n,e){return JSON.stringify(In(n,e))}async function J(n,e,t){let o=[],r=I(n,e,Vn(t),0,o);if(await en(o),x(t).validate){let a=await F(r,t);if(a.length>0)throw new D("Validation failed during deserialization",a)}return r}function rn(n,e,t){let o=[],r=I(n,e,Vn(t),0,o);if(tn(o,"toInstanceSync()","toInstance()"),x(t).validate){let a=q(r,t);if(a.length>0)throw new D("Validation failed during deserialization",a)}return r}function Nn(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 Nn(n,e),await J(n,e,t)}function Mn(n,e,t){return Nn(n,e),rn(n,e,t)}async function Rn(n,e,t){return J(n,W(e),t)}function Ct(n,e,t){return rn(n,W(e),t)}async function Pn(n,e,t){return an(n,W(e),t)}function Tt(n,e,t){return Mn(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 vn(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=An;static toInstance=J;static toInstanceArray=an;static fromJson=Rn;static fromJsonArray=Pn;static fromRequest=vn;static validate=F;static validateOrReject=En};return jn(kt);})(); +var Cereale=(()=>{var Z=Object.defineProperty;var Fn=Object.getOwnPropertyDescriptor;var Jn=Object.getOwnPropertyNames;var zn=Object.prototype.hasOwnProperty;var Bn=(n,e)=>{for(var t in e)Z(n,t,{get:e[t],enumerable:!0})},Un=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Jn(e))!zn.call(n,r)&&r!==t&&Z(n,r,{get:()=>e[r],enumerable:!(o=Fn(e,r))||o.enumerable});return n};var jn=n=>Un(Z({},"__esModule",{value:!0}),n);var kt={};Bn(kt,{Allow:()=>ae,ArrayContains:()=>at,ArrayMaxSize:()=>ot,ArrayMinSize:()=>tt,ArrayNotContains:()=>it,ArrayNotEmpty:()=>et,ArrayUnique:()=>rt,Contains:()=>Ke,Email:()=>Ve,EndsWith:()=>We,Equals:()=>Ge,IsAlpha:()=>Ee,IsAlphanumeric:()=>Ie,IsArray:()=>nt,IsBigInt:()=>de,IsBoolean:()=>ce,IsDate:()=>pe,IsDateString:()=>ve,IsDefined:()=>ye,IsDivisibleBy:()=>Oe,IsEmpty:()=>ge,IsEnum:()=>Xe,IsHexColor:()=>Ne,IsIP:()=>_e,IsIn:()=>He,IsInstance:()=>Qe,IsInt:()=>ue,IsJSON:()=>Fe,IsLatitude:()=>Te,IsLongitude:()=>ke,IsLowercase:()=>Me,IsNotEmpty:()=>me,IsNotIn:()=>Ye,IsNumber:()=>le,IsNumberString:()=>Re,IsObject:()=>fe,IsOptional:()=>oe,IsPort:()=>Ce,IsSemVer:()=>Ae,IsString:()=>se,IsUUID:()=>je,IsUppercase:()=>Pe,IsUrl:()=>Je,JsonAlias:()=>Zn,JsonDeserialize:()=>ne,JsonIgnore:()=>Hn,JsonMapper:()=>nn,JsonMappingError:()=>h,JsonPolymorphic:()=>te,JsonProperty:()=>Gn,JsonReadOnly:()=>Yn,JsonSerialize:()=>Qn,JsonType:()=>ee,JsonValidationError:()=>D,JsonWriteOnly:()=>Xn,Length:()=>$e,Matches:()=>ze,Max:()=>be,MaxDate:()=>lt,MaxLength:()=>Se,Min:()=>he,MinDate:()=>st,MinLength:()=>De,Negative:()=>xe,NotContains:()=>Le,NotEquals:()=>Ze,Positive:()=>we,REDACTED:()=>xn,StartsWith:()=>qe,Validate:()=>ut,ValidateIf:()=>re,ValidateNested:()=>ie,addConstraint:()=>M,collectErrorMessages:()=>dt,configure:()=>Ln,defineRule:()=>Kn,fieldMetadata:()=>b,flattenErrors:()=>X,formatErrors:()=>ct,fromJson:()=>Pn,fromJsonArray:()=>Rn,fromJsonArraySync:()=>Tt,fromJsonSync:()=>Ct,fromRequest:()=>vn,getConfig:()=>qn,modelOf:()=>z,modelOfInstance:()=>B,modelVersion:()=>S,propertyModel:()=>w,resetConfig:()=>Wn,resolveNamingStrategy:()=>U,resolveOptions:()=>x,toInstance:()=>J,toInstanceArray:()=>an,toInstanceArraySync:()=>Mn,toInstanceSync:()=>rn,toJson:()=>An,toJsonSync:()=>Ot,toPlain:()=>on,toPlainSync:()=>In,validate:()=>F,validateOrReject:()=>En,validateOrRejectSync:()=>xt,validateSync:()=>q});var $=Symbol.metadata??Symbol.for("Symbol.metadata");Symbol.metadata??=$;var N=Symbol.for("cereale.model"),ln=0;function S(){return ln}function _n(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 b(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 w(n,e){ln++;let t=_n(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),w(n,e).constraints.push(t)}function z(n){return typeof n!="function"?{}:n[$]?.[N]??{}}function B(n){let e=Object.getPrototypeOf(n);if(!e)return{};let t=Object.getOwnPropertyDescriptor(e,"constructor");return z(t?.value)}function Kn(n,e,t,o){let r=n;Object.hasOwn(r,$)||(r[$]=Object.create(r[$]??null)),M(r[$],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 un=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(un).join("")},PascalCase:n=>P(n).map(un).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 cn={namingStrategy:"identity",unknownKeys:"allow",validate:!0,maxDepth:64},k={...cn};function Ln(n){k={...k,...n}}function qn(){return{...k}}function Wn(){k={...cn}}function x(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(b(o),r,n(r),e)})}function m(n,e,t){return(o=>y(r=>({name:n,validate:e,message:t(r)}),o))}function R(n,e,t){return m(n,o=>typeof o=="string"&&e.test(o),t)}function Gn(n){return((e,t)=>{w(b(t),String(t.name)).name=n})}function Zn(...n){return((e,t)=>{let o=w(b(t),String(t.name));o.aliases=[...o.aliases??[],...n]})}function Y(n){return((e,t)=>{w(b(t),String(t.name)).access=n})}var Hn=()=>Y("none"),Yn=()=>Y("readonly"),Xn=()=>Y("writeonly");function Qn(n){return((e,t)=>{w(b(t),String(t.name)).serializer=n})}function ne(n){return((e,t)=>{w(b(t),String(t.name)).deserializer=n})}function ee(n){return((e,t)=>{w(b(t),String(t.name)).type=n})}function te(n,e,t){return((o,r)=>{let a={discriminator:n,subTypes:e,onUnknown:t?.onUnknown??"keep",...t?.fallback?{fallback:t.fallback}:{}};w(b(r),String(r.name)).polymorphic=a})}function oe(){return((n,e)=>{w(b(e),String(e.name)).optional=!0})}function re(n){return((e,t)=>{w(b(t),String(t.name)).condition=n})}function ae(){return((n,e)=>{w(b(e),String(e.name))})}function ie(n){return((e,t)=>{let o=b(t),r=String(t.name);w(o,r).nested=!0,n?.each&&M(o,r,{name:"nestedEach",validate:a=>Array.isArray(a),message:`${r} must be an array`})})}var se=m("isString",n=>typeof n=="string",n=>`${n} must be a string`),le=m("isNumber",n=>typeof n=="number"&&!isNaN(n),n=>`${n} must be a number`),ue=m("isInt",n=>Number.isInteger(n),n=>`${n} must be an integer`),ce=m("isBoolean",n=>typeof n=="boolean",n=>`${n} must be a boolean`),de=m("isBigInt",n=>typeof n=="bigint",n=>`${n} must be a bigint`),pe=m("isDate",n=>n instanceof Date&&!isNaN(n.getTime()),n=>`${n} must be a valid Date object`),fe=m("isObject",n=>typeof n=="object"&&n!==null&&!Array.isArray(n),n=>`${n} must be an object`),ye=m("isDefined",n=>n!=null,n=>`${n} should not be null or undefined`),me=m("isNotEmpty",n=>n!=null&&n!=="",n=>`${n} should not be empty`),ge=m("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 he(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 be(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 we=m("positive",n=>typeof n=="number"&&n>0,n=>`${n} must be positive`),xe=m("negative",n=>typeof n=="number"&&n<0,n=>`${n} must be negative`);function Oe(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 Ce=m("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`),Te=m("isLatitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-90&&n<=90,n=>`${n} must be a latitude between -90 and 90`),ke=m("isLongitude",n=>typeof n=="number"&&Number.isFinite(n)&&n>=-180&&n<=180,n=>`${n} must be a longitude between -180 and 180`);function De(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 Se(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 $e(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 Ve=R("isEmail",/^[^\s@]+@[^\s@]+\.[^\s@]+$/,n=>`${n} must be a valid email`),Ee=R("isAlpha",/^[A-Za-z]+$/,n=>`${n} must contain only letters`),Ie=R("isAlphanumeric",/^[A-Za-z0-9]+$/,n=>`${n} must contain only letters and numbers`),Ae=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`),Ne=R("isHexColor",/^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,n=>`${n} must be a hex color`),Me=m("isLowercase",n=>typeof n=="string"&&n===n.toLowerCase(),n=>`${n} must be lowercase`),Pe=m("isUppercase",n=>typeof n=="string"&&n===n.toUpperCase(),n=>`${n} must be uppercase`),Re=m("isNumberString",n=>typeof n=="string"&&n.trim()!==""&&Number.isFinite(Number(n)),n=>`${n} must be a number string`),ve=m("isDateString",n=>typeof n=="string"&&!isNaN(Date.parse(n)),n=>`${n} must be a valid ISO 8601 date string`),Fe=m("isJson",n=>{if(typeof n!="string")return!1;try{return JSON.parse(n),!0}catch{return!1}},n=>`${n} must be a JSON string`),Je=m("isUrl",n=>{try{return new URL(n),!0}catch{return!1}},n=>`${n} must be a valid URL`);function ze(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 Be="00000000-0000-0000-0000-000000000000",Ue="ffffffff-ffff-ffff-ffff-ffffffffffff";function je(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()===Be||r.toLowerCase()===Ue)?!0:t.test(r),message:`${o} must be a valid UUID${n?` (version ${n})`:""}`,...n?{constraints:[n]}:{}}),e)}var dn=/^(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}$/,pn=n=>{try{return new URL(`http://[${n}]`).hostname===`[${n.toLowerCase()}]`||/^[0-9a-f:.]+$/i.test(n)&&n.includes(":")}catch{return!1}};function _e(n,e){return y(t=>({name:"isIp",validate:o=>typeof o!="string"?!1:n===4?dn.test(o):n===6?pn(o):dn.test(o)||pn(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 Ke=_("contains",(n,e)=>n.includes(e),(n,e)=>`${n} must contain ${JSON.stringify(e)}`),Le=_("notContains",(n,e)=>!n.includes(e),(n,e)=>`${n} must not contain ${JSON.stringify(e)}`),qe=_("startsWith",(n,e)=>n.startsWith(e),(n,e)=>`${n} must start with ${JSON.stringify(e)}`),We=_("endsWith",(n,e)=>n.endsWith(e),(n,e)=>`${n} must end with ${JSON.stringify(e)}`);function Ge(n,e){return y(t=>({name:"equals",validate:o=>o===n,message:`${t} must be equal to ${JSON.stringify(n)}`,constraints:[n]}),e)}function Ze(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 He(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 Ye(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 Xe(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 Qe(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 nt=n=>y(e=>({name:"isArray",validate:t=>Array.isArray(t),message:`${e} must be an array`}),n),et=n=>V("arrayNotEmpty",e=>e.length>0,e=>`${e} should not be empty`)(n),tt=(n,e)=>V("arrayMinSize",t=>t.length>=n,t=>`${t} must contain at least ${n} elements`,[n])(e),ot=(n,e)=>V("arrayMaxSize",t=>t.length<=n,t=>`${t} must contain at most ${n} elements`,[n])(e);function rt(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 at(n,e){return V("arrayContains",t=>n.every(o=>t.includes(o)),t=>`${t} must contain the following values: ${n.join(", ")}`,[n])(e)}function it(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 st(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 lt(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 ut(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:(l,u)=>s(l,u),message:l=>`${l.property} is invalid`,constraints:o}),r)}let i=new n;return y(()=>({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 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 ct(n){let e=X(n);return Object.entries(e).flatMap(([t,o])=>o.map(r=>`${t}: ${r}`)).join(` +`)}function dt(n){return Object.values(X(n)).flat()}var D=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"}},pt=new Set(["__proto__","constructor","prototype"]),xn="[redacted]";function On(n,e){return n[e]?.access??"readwrite"}function Cn(n,e,t){return n[e]?.name??t(e)}var fn=new WeakMap;function ft(n,e,t){let o=fn.get(n);(!o||o.version!==S())&&(o={version:S(),byStrategy:new Map},fn.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=On(n,e),s=n[e]?.serializer;a={name:Cn(n,e,t.naming),skip:i==="none"||i==="writeonly",...s?{serializer:s}:{}},r.set(e,a)}return a}var yn=new WeakMap;function yt(n,e){let t=yn.get(n);(!t||t.version!==S())&&(t={version:S(),byStrategy:new Map},yn.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 p=r.get(u);if(p&&p!==c)throw new h(`Properties "${p}" 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 p=[Cn(n,u,e.naming),...c.aliases??[]],f=On(n,u);if(f==="none"||f==="readonly"){for(let d of p)a.add(d);continue}for(let d of p)s(d,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 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 mt={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 Tn(n){let e=n[Symbol.toStringTag];if(typeof e=="string"){let t=mt[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 kn="a bigint, which JSON has no representation for";function mn(n){switch(typeof n){case"bigint":return kn;case"symbol":return"a symbol";case"function":return"a function";case"object":return n===null?null:Tn(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(kn,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 l=Tn(n);l!==null&&E(l,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 c=[];for(let p=0;p{let A=mn(C);A!==null&&E(A,O),u[g]=C}))}else{let g=mn(d);g!==null&&E(g,a),u[p.name]=d}}else u[p.name]=K(f,e,t,o+1,r,a);a.pop()}return u}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=yt(z(n),t);for(let s of Object.keys(e)){if(pt.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 h(`Unknown property ${JSON.stringify(s)} for ${n.name}. Allowed: ${[...i.accept.keys()].map(d=>JSON.stringify(d)).join(", ")||"(none declared)"}.`);a[s]=e[s];continue}let u=e[s],c=i.props.get(l);if(c?.deserializer){let d=Dn(c.deserializer).deserialize(u);if(v(d)){let g=l;a[g]=void 0,r.push(d.then(O=>{a[g]=O}))}else a[l]=d;continue}let p=c?.polymorphic;if(p&&u!==null&&u!==void 0){let{discriminator:d,subTypes:g,onUnknown:O,fallback:C}=p,A=T=>{if(T==null||typeof T!="object")return T;let sn=g.find(G=>T[d]===G.name);if(sn)return I(sn.value,T,t,o+1,r);if(C)return I(C,T,t,o+1,r);if(O==="error")throw new h(`Unknown discriminator value ${JSON.stringify(T[d])} for property "${l}". Known values: ${g.map(G=>JSON.stringify(G.name)).join(", ")}.`);return T};a[l]=Array.isArray(u)?u.map(A):A(u);continue}let f=c?.typeFn;if(f&&u!==null&&u!==void 0){let d=f();a[l]=I(d,u,t,o+1,r);continue}a[l]=u}return a}var gn=new WeakMap;function gt(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 ht(n){let e=gn.get(n);if(e&&e.version===S())return e.plan;let t=[];for(let[o,r]of Object.entries(n)){let a=r.access??"readwrite";t.push({key:o,constraints:gt(r.constraints),isOptional:!!r.optional,isNested:!!r.nested,redact:a==="writeonly"||a==="none",...r.condition?{condition:r.condition}:{}})}return gn.set(n,{version:S(),plan:t}),t}var hn=new WeakMap;function Dn(n){let e=hn.get(n);return e||(e=new n,hn.set(n,e)),e}function bn(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 wn(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 bt(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 ht(B(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?xn:l,constraints:{}},c={value:l,object:n,property:s,constraints:[]},p=!1;for(let f of i.constraints){c.constraints=f.constraints||[];let d=f.each&&Array.isArray(l)?bt(f,l,c):(()=>{let g=f.validate(l,c);return v(g)?g.then(O=>({ok:O,index:-1})):{ok:g,index:-1}})();if(v(d)){p=!0;let g={value:l,object:n,property:s,constraints:f.constraints||[]};r.push(d.then(({ok:O,index:C})=>{O||(C>=0&&(g.value=l[C]),bn(u.constraints,f.name,wn(f,g,C)))}));continue}if(!d.ok){d.index>=0&&(c.value=l[d.index]);let g=wn(f,c,d.index);c.value=l,bn(u.constraints,f.name,g)}}if(i.isNested&&l!==null&&l!==void 0){let f=L(l,e,t+1,o,r);f.length>0&&(u.children=f)}(p||Object.keys(u.constraints).length>0||u.children)&&a.push(u)}return a}finally{e.delete(n)}}function $n(n){let e=x(n);return{naming:U(e.namingStrategy),namingKey:e.namingStrategy,maxDepth:e.maxDepth}}function Vn(n){let e=x(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,x(e).maxDepth,t);return t.length===0?o:(await en(t),Sn(o))}function q(n,e){let t=[],o=L(n,new Set,0,x(e).maxDepth,t);return tn(t,"validateSync()","validate()"),o}async function En(n,e){let t=await F(n,e);if(t.length>0)throw new D("Validation failed",t)}function xt(n,e){let t=q(n,e);if(t.length>0)throw new D("Validation failed",t)}async function on(n,e){if(n==null)return n;if(x(e).validate){let r=await F(n,e);if(r.length>0)throw new D("Validation failed during serialization",r)}let t=[],o=K(n,new Set,$n(e),0,t,[]);return await en(t),o}function In(n,e){if(n==null)return n;if(x(e).validate){let r=q(n,e);if(r.length>0)throw new D("Validation failed during serialization",r)}let t=[],o=K(n,new Set,$n(e),0,t,[]);return tn(t,"toPlainSync()","toPlain()"),o}async function An(n,e){return JSON.stringify(await on(n,e))}function Ot(n,e){return JSON.stringify(In(n,e))}async function J(n,e,t){let o=[],r=I(n,e,Vn(t),0,o);if(await en(o),x(t).validate){let a=await F(r,t);if(a.length>0)throw new D("Validation failed during deserialization",a)}return r}function rn(n,e,t){let o=[],r=I(n,e,Vn(t),0,o);if(tn(o,"toInstanceSync()","toInstance()"),x(t).validate){let a=q(r,t);if(a.length>0)throw new D("Validation failed during deserialization",a)}return r}function Nn(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 Nn(n,e),await J(n,e,t)}function Mn(n,e,t){return Nn(n,e),rn(n,e,t)}async function Pn(n,e,t){return J(n,W(e),t)}function Ct(n,e,t){return rn(n,W(e),t)}async function Rn(n,e,t){return an(n,W(e),t)}function Tt(n,e,t){return Mn(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 vn(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=An;static toInstance=J;static toInstanceArray=an;static fromJson=Pn;static fromJsonArray=Rn;static fromRequest=vn;static validate=F;static validateOrReject=En};return jn(kt);})(); 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..1808ef9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,289 +1,845 @@ - - - Cereale - Spring-like JSON Mapping & Validation for TypeScript - - - - - - - - + + +cereale — validated domain objects, not validated data + + + + + - -