docschema
Version:
Schema declaration and validation library using JsDoc comments
21 lines (20 loc) • 28.6 kB
JavaScript
const $e=Object.freeze({"@virtual":"@abstract","@extends":"@augments","@constructor":"@class","@const":"@constant","@defaultvalue":"@default","@desc":"@description","@host":"@external","@fileoverview":"@file","@overview":"@file","@emits":"@fires","@func":"@function","@method":"@function","@var":"@member","@arg":"@param","@argument":"@param","@prop":"@property","@return":"@returns","@exception":"@throws","@yield":"@yields","@linkcode":"@link","@linkplain":"@link"});function J(r){return(r.match(/(.*)[\/\\]/u)??"")[1]??""}function F(){if(F.isIt===void 0){const r=new Function("try {return this===window;}catch(e){ return false;}");F.isIt=r()}return F.isIt??!1}F.isIt=void 0;function be(r){for(const e in r)if(Object.hasOwn(r,e))return!1;return!0}function Y(r,e){let t=0,n=r.length-1,s=-1;for(;;){const i=Math.floor((t+n)/2);if(e>=(r[i]??0)?t=i+1:n=i-1,s=n,t>n)break}return s}function V(r,e=0){const t=r[e]??"",n=xe(t);if(!n)return 0;let s=0,i=e,o=!1;for(i=e;i<r.length;i++){const a=r[i];if(a===t)s+=1;else if(a===n&&(s-=1,s===0)){o=!0;break}}return o?i-e:0}function z(r,e=0){const t=r[e]??"";if(!t||!z.quotes.has(t))return 0;let n=e+1;for(;;){const s=r.indexOf(t,n);if(s===-1){n=0;break}if(s>0&&r[s-1]!=="\\"){n=s-e;break}}return n}z.quotes=new Set(["'",'"',"`"]);function ae(r,e=0){let t=r.indexOf(`
`,e);return t<0&&(t=r.length-1,t<0&&(t=0)),t-e}function ce(r){const e=/^\s*\/\/\s*|\n\s*\/\/\s*/umg;return r.replace(e," ").trim()}function xe(r){switch(r){case"(":return")";case"[":return"]";case"<":return">";case"{":return"}";default:return""}}const Ee=Object.freeze(new Set(["(","[","<","{"])),we=Object.freeze(new Set(["'",'"',"`"]));function R(r,e=new Set(["|"])){const t=[],n=le(r.trim()),s=n.length+1;let i=0,o="";for(let a=0;a<s;a++){const c=n[a]??"";if(e.has(c)||a===s-1){const u=a===s-1?a+1:a,f=n.substring(i,u).trim();t.push(f),i=a+1}else if(Ee.has(c)){const u=V(n,a);u>0&&(a+=u)}else if(we.has(c)){const u=z(n,a);u>0&&(a+=u)}else if(c==="/"&&o==="/"){const u=ae(n,a);u>0&&(a+=u)}o=c}return t}function ke(r){const e=["",""],t=r.length+1;let n="";for(let s=0;s<t;s++){const i=r[s]??"";if(n=s>0?r[s-1]??"":" ",!(i===" "||i==="\r"||i===`
`))if(i==="/")if(n==="/"){const o=ae(r,s);o>0&&(s+=o)}else{if(n===" "||n==="\r"||n===`
`)continue;throw new SyntaxError(`Unexpected single slash in expression "${r}"`)}else{e[0]=r.substring(0,s).trim(),e[0]=ce(e[0]),e[1]=r.substring(s).trim();break}}return e}function Te(r){const e=["",""],t=V(r),n=r.indexOf("//",t);return n>-1?(e[0]=r.substring(0,n).trim(),e[1]=r.substring(n+2).trim(),e[1]=ce(e[1])):e[0]=r.trim(),e}function B(...r){const e=r.reduce(B.reducer,[]),t=[];for(const n of e)if(n!=="."){if(n===".."){t.pop();continue}t.push(n)}return t.join("/")}B.reducer=(r,e)=>(r.length>0&&(e=e.replace(/^[\/\\]+/u,"")),e=e.replace(/[\/\\]+$/u,""),r.concat(e.split(/[\/\\]/u)));function Se(r,e='"'){return typeof r=="string"?`${e}${r}${e}`:r instanceof Object?JSON.stringify(r):(r??"").toString()}function ue(r){return typeof r=="string"&&(r.startsWith("'")&&r.endsWith("'")||r.startsWith('"')&&r.endsWith('"'))?r.substring(1,r.length-1):(r??"").toString()}function le(r){return r.replace(/^\s*\((.*)\)\s*$/u,"$1").trim()}function D(r,e=30){if(!(r instanceof Object))return String(r);let t=JSON.stringify(r);return t=t.length>e?`${t.substring(0,e-4)} ...`:t,t}function Ae(r){for(let e=0;e<r.length;e++)r[e]=(r[e]??"").trim()}function Ie(r){const e=[],t=/(?:^|\n|;)import\s*('[^']+'|"[^"]+"|`[^`]+`)/ugm,n=r.matchAll(t);for(const s of n){let i=s[1]??"";i=i.substring(1,i.length-1),e.push(i)}return e}const ee=await Fe();async function Fe(){let r=null;return F()||(r=await import("fs")),r}function Ce(r){const e=decodeURI(r);if(!ee)throw new Error(`Could not read file ${e}, becauseFileSystem (fs) is not imported correctly`);return ee.readFileSync(e,"utf-8")}function Ne(r,e,t){const n=new XMLHttpRequest;n.open("GET",encodeURI(r),e),n.onload=()=>{if(n.readyState===4)if(n.status===200)t instanceof Function&&t(n.responseText);else throw new Error(n.statusText)},n.onerror=()=>{throw new Error(n.statusText)},n.send(null)}function te(r){let e=r;for(e=e.replace(/\\/ug,"/"),e=e.replace(/(?<![.])(\.\/)/ug,"/"),e=e.replace(/[\/]+/ug,"/"),e=e.replace(/^([a-z]+:\/)/um,"$1/");;){let t=!1;if(e=e.replace(/[^.\/]+\/\.\.\//u,()=>(t=!0,"")),!t)break}return e}function Me(r){let e="";return F()?Ne(te(r),!1,t=>{e=t}):e=Ce(te(r)),e}const b="boolean",y="number",C="string",T={array:{min:y,max:y,length:y},number:{min:y,max:y,gte:y,lte:y,gt:y,lt:y,step:y,int:b,finite:b,safeInt:b},string:{min:y,max:y,length:y,startsWith:C,endsWith:C,includes:C,excludes:C,url:C,pattern:RegExp,ip:b,email:b,cuid:b,cuid2:b,ulid:b,uuid:b,ipv4:b,ipv6:b}},Be=Object.keys(T).map(r=>`"${r}"`).join(", "),Pe={array:Object.keys(T.array).map(r=>`"${r}"`).join(", "),number:Object.keys(T.number).map(r=>`"${r}"`).join(", "),string:Object.keys(T.string).map(r=>`"${r}"`).join(", ")};function M(r){return r?` at ${r?.file}:${r?.startLine}`:""}function je(r,e,t){throw new SyntaxError(`"${r}" is not a valid filter for type "${e}"`+M(t)+`. Use any of these filters: ${Pe[e]}`)}function Le(r,e,t){if(!(r in T))throw new SyntaxError(`Filters are not compatible with ${r} type`+M(t)+`. Use filters only with types: ${Be}`);for(const n in e){if(!(n in T[r])){je(n,r,t);continue}const s=T[r][n];let i=e[n];if(s!==Array&&i instanceof Array){if(i.length===0)throw new SyntaxError(`Filter "${n}" (used for ${r} type) is an empty Array`+M(t)+`. When the filter is an Array, it should be a tuple with the filter value at index 0 and a custom error message at index 1. In ${JSON.stringify(e)}`);i=i[0]}const o=typeof i;if(typeof s=="string"){const a=s;if(o!==a)throw new SyntaxError(`Filter "${n}" (used for ${r} type) has a value with wrong type`+M(t)+`. The type for filter "${n}" should be "${a}", but it is "${o}" instead, in ${JSON.stringify(e)}`)}else if(!(i instanceof s))throw new SyntaxError(`Filter "${n}" (used for ${r} type) has a value with wrong type`+M(t)+`. The type for filter "${n}" should be "${s.name}", but it is "${o}" instead, in ${JSON.stringify(e)}`)}}function Oe(r){let e=r;return e=e.replace(/^ *-? */u,""),e=e.replaceAll("\r","").replaceAll(`
`," "),e}function We(r){const e={};for(const t in r)e[t]=r[t]instanceof Array?r[t]:[r[t],""];return e}function H(r,e,t){const n={description:"",filters:{}};if(!r)return n;const s=Oe(r),i=s.indexOf("{");if(i===-1)n.description=s;else{const o=V(s,i);if(o>0){const a=s.substring(i,i+o+1);n.description=(s.substring(0,i).trim()+s.substring(i+o+1)).trim();const c=Function(`return ${a}`)();for(const f of e)Le(f.typeName,c,t);const u=We(c);n.filters=u}}return n}const Re=new Set(["string","number","bigint","boolean","undefined","symbol","null"]),De=new Set(["'",'"',"`"]);function x(r){return le(r.trim())}const re={simple:{any:r=>{const e=x(r);return e!==""&&e!=="*"&&e!=="any"?!1:{typeName:"any",typeExpression:e}},boolean:r=>{const e=x(r);return e!=="true"&&e!=="false"?!1:{typeName:"boolean",typeExpression:e,value:e==="true"}},number:r=>{const e=x(r);return/^-?\d+\.?\d*$/u.exec(e)===null?!1:{typeName:"number",typeExpression:e,value:parseFloat(e)}},string:r=>{const e=x(r),t=e[0]??"";return!De.has(t)||z(e)!==e.length-1?!1:{typeName:"string",typeExpression:e,value:e.slice(1,-1)}},tryPrimitive:r=>{const e=x(r),t=e.toLowerCase();return Re.has(t)?{typeName:t,typeExpression:e}:!1}},complex:{array:(r,e,t)=>{const n=x(r),s=/^array\.?<(.+)>$/ui.exec(n);if(s===null)return!1;const i=s[1]??"*";return{typeName:"array",typeExpression:n,types:e(i,t)}},array2:(r,e,t)=>{const n=x(r),i=/^((?=[^(]).+(?<=[^)]))\[\]$|^\((.*)\)\[\]$/ui.exec(n);if(i===null)return!1;const o=i[1]??i[2]??"";return{typeName:"array",typeExpression:n,types:e(o,t)}},literal:(r,e,t)=>{const n=x(r),s=n[0]??"";let i=null,o=!1;if(s==="{")i=/^\{(.*)\}$/usi,o=!1;else if(s==="[")i=/^\[(.*)\]$/usi,o=!0;else return!1;const a=i.exec(n);if(a===null)return!1;const c=(a?.[1]??"").trim(),u=R(c,new Set([","])),f=[];let h=0;for(const d of u){const $=ke(d),g=f.length,S=g-1;if(g>0){const{description:j,filters:_}=H($[0],f[S].valueTypes,t);f[S].description=j,f[S].filters=_}if($[1]==="")continue;let U="",E="";if(o)U=$[1]??"",E=h.toString(),h+=1;else{const j=R($[1],new Set([":"])),_=j[0]??"";U=j[1]??"",E=_.replaceAll(" ","")}const Q=Te(U),de=Q[0].replace(/\s/ug,"");let P=e(de,t);o||E.endsWith("?")&&(E=E.substring(0,E.length-1),P=P.concat(e("undefined",t)));const{description:he,filters:ye}=H(Q[1],P,t),ge={key:E,valueTypes:P,description:he,filters:ye};f.push(ge)}return{typeName:o?"arrayLiteral":"objectLiteral",typeExpression:n,pairs:f}},object:(r,e,t)=>{const n=x(r),s=/^(?:object|record)\.?<(.+)>$/ui.exec(n);if(s===null)return!1;const i=(s?.[1]??"").trim(),o=R(i,new Set([","]));return{typeName:"object",typeExpression:n,typePairs:[{keyTypes:e(o[0]??"*",t),valueTypes:e(o[1]??"*",t)}]}},typedef:r=>{const e=x(r);return/^\w+$/u.exec(e)===null?!1:{typeName:"typedef",typeExpression:e}},directImport:r=>{const e=x(r),t=/(?:^|\n|;)import\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*\)\s*.\s*(\w+)/ugm.exec(e);return t===null?!1:{typeName:"directImport",typeExpression:e,directImport:{file:ue(t[1])??"",typeName:t[2]??""}}}}},Ve=Object.freeze({"":"*",any:"*","[]":"*[]",array:"*[]","{}":"Object<*,*>",object:"Object<*,*>"}),ze=Object.freeze(new Set(["|","&"])),Ue=[re.simple,re.complex];function _e(r){const e=r.trim(),t=e.toLowerCase();return Ve[t]??e}function Je(r,e,t){for(const n in e){const s=e[n];if(s){const i=s(r,K,t);if(i)return i}}return!1}function qe(r,e){const t=[];for(const n of r)if(n){const[s]=K(n,e);s&&t.push(s)}return t}function ve(r,e){for(const t of Ue){const n=Je(r,t,e);if(n)return[n]}return[{typeName:"any",typeExpression:r}]}function K(r,e){const t=_e(r),n=R(t,ze);return n.length>1?qe(n,e):ve(t,e)}const w=new Map,q=new Map,ne=[];class Ze{#e={file:"",startLine:0,endLine:0};getParsedAst(e){const t=w.get(e);if(!t)throw new Error(`There is no parsed AST for file ${e}`);return t}parseComments(e,t=""){const n=this.#t(e),s=[],i=[];for(const o of n){let{comment:a}=o;this.#e={file:t,startLine:o.startLine,endLine:o.endLine},a=this.#c(a),a=this.#d(a);const c=this.#n(a);Ae(c);const u=this.#u(c),f={elements:u,file:t,startLine:o.startLine,endLine:o.endLine,lineAfterComment:o.lineAfterComment,localTypedefs:i,ambientTypedefs:[],importedTypedefs:[],directlyImportedTypedefs:[],strict:u.strict};s.push(f)}for(const o of s)(o.elements.typedef||o.elements.callback)&&i.push(o);return s}parseFile(e){const t=[],n=[];if(!w.has(e)){w.set(e,n);let s="";const i=Me(e),o=this.parseComments(i,e);n.push(...o);const a=Ie(i);for(const c of n)c.ambientTypedefs=ne;if(a.length>0){s=J(e);for(let c of a){if(c[0]!==".")continue;c=B(s,c);const u=w.get(c)??this.parseFile(c);for(const f of u)ne.push(f)}}for(const c of n){c.importedTypedefs=t;const u=c.elements.import;if(u.length!==0){s=s||J(e);for(const f of u){if(f.name!=="from"||f.description==="")continue;const h=f.typeExpression.split(",").map(g=>g.trim()).filter(g=>g!=="");let d=ue(f.description);if(d[0]!==".")continue;d=B(s,d);const $=w.get(d)??this.parseFile(d);for(const g of $){const S=g.elements.typedef?.name;S&&h.includes(S)&&(t.includes(g)||t.push(g))}}}}for(const c of n)if(c.elements.typedef)for(const u of c.elements.typedef.types){if(u.typeName!=="directImport"||!u.directImport)continue;if(!u.directImport.file.startsWith("."))throw new Error(`Cannot import type ${u.directImport.typeName} from path ${u.directImport.file}
at ${c.file}:${c.startLine}
Only relative paths are allowed.`);s=s||J(e);const f=B(s,u.directImport.file);try{const h=this.parseFile(f);for(const d of h)if(d.elements.typedef?.name===u.directImport.typeName){const{directlyImportedTypedefs:g}=c;g.includes(d)||c.directlyImportedTypedefs.push(d);break}}catch{throw new Error(`Cannot import type ${u.directImport.typeName} from path ${u.directImport.file}
at ${c.file}:${c.startLine}
File not found.`)}}}return w.get(e)??[]}removeFileFromCache(e){w.delete(e)}#n(e){return e.replaceAll("\r","").split(`
`)}#t(e){const t=this.#r(e),n=[],s=/(\/\*\*[\t ]*\r?\n(?:[ \t]* \*.*\r?\n)+[ \t*]*\*\/|\/\*\*.*\*\/)(?: *\r?\n([^\r\n\/][^\r\n\/]*))?/ug;for(;;){const i=s.exec(e);if(!i)break;const o=i[1]??"",a=i[2]??"",c=i.index??0,u=Y(t,c),f=Y(t,c+o.length);n.push({comment:o,index:c,startLine:u,endLine:f,lineAfterComment:a})}return n}#r(e){const t=[-1,0],n=/\r?\n/ug;for(;;){const s=n.exec(e);if(!s)break;t[t.length]=(s.index??0)+1}return t}#s(e,t){const n=[];q.has(t)||q.set(t,new RegExp(`^@${t}[^wd]`,"u"));const s=q.get(t);let i=!1,o="";for(let a of e){if(a=a.trim(),s?.test(a))i=!0,o&&n.push(o),o="";else if(a.startsWith("@")){i=!1;continue}i&&(o+=o?`
${a}`:a)}return o&&n.push(o),n}#i(e,t=!0,n=!0,s=!0){let i=e.replace(/^ *@[a-z]+ */u,"");const o={id:0,typeExpression:"",types:[],name:"",description:"",filters:{},optional:!1,defaultValue:void 0,destructured:void 0};if(t){if(i.startsWith("{")){const a=V(i);a>0&&(o.typeExpression=i.slice(1,a),i=i.slice(a+1))}o.typeExpression=o.typeExpression.trim()}if(n){i=i.trim();let a=0;for(const c of i){if(a+=1,c===" "||c==="\r"||c===`
`||c===" ")break;o.name+=c}i=i.slice(a)}if(this.#m(o),o.types=K(o.typeExpression,this.#e),s&&i){const{description:a,filters:c}=H(i,o.types,this.#e);o.description=a,o.filters=c}if(o.name.includes(".")){const a=o.name.split(".");o.destructured=[a[0]??"",a[1]??""]}return o}#a(e){const t=/^[ \t]*@([a-zA-Z]+)(?: .*)?$/u,n=new Set;for(const s of e){const o=t.exec(s)?.[1];o&&n.add(o)}return n}#c(e){return e.replaceAll(/@[a-z]+/ug,t=>$e[t]??t)}#u(e){const t=this.#a(e);return{description:this.#f(e),scope:this.#p(e),returns:t.has("returns")?this.#o(e,"returns"):null,param:t.has("param")?this.#l(e,"param"):[],enum:t.has("enum")?this.#o(e,"enum"):null,type:t.has("type")?this.#o(e,"type"):null,callback:t.has("callback")?this.#o(e,"callback",!0):null,typedef:t.has("typedef")?this.#o(e,"typedef",!0):null,yields:t.has("yields")?this.#o(e,"yields"):null,property:t.has("property")?this.#l(e,"property"):[],strict:t.has("strict"),import:t.has("import")?this.#l(e,"import"):[]}}#f(e){let t=!0,n="";for(const s of e)if(s.startsWith("@description")){const i=s.substring(13).trim();n.endsWith(`
`)||(n+=`
`),n+=i,t=!0}else if(s.startsWith("@"))t=!1;else if(t){const i=s.trim();i===""?n+=`
`:(n.endsWith(`
`)||(n+=" "),n+=i)}return n=n.trim(),n}#l(e,t){const n=[],s=this.#s(e,t);for(const c of s){const u=this.#i(c,!0,!0,!0);u.name&&n.push(u)}let i="";for(let c=n.length-1;c>=0;c--){const u=n[c],f=u?.destructured?.[0];f?i=f:i&&u?.name===i&&(n.splice(c,1),i="")}let o=-1,a="";for(const c of n)c?.destructured?.[0]&&a||(o+=1),a=c?.destructured?.[0]??"",c.id=o;return n}#o(e,t,n=!1){let s=this.#s(e,t);s=s.slice(-1);const i=[];for(const o of s){const a=this.#i(o,!0,n,!0);i.push(a)}return i[0]??null}#p(e){const t={private:!1,protected:!1,public:!0};for(const n of e)for(const s in t)n.startsWith(`@${s}`)&&(t[s]=!0);return(t.private||t.protected)&&(t.public=!1),t}#m(e){if(e.name){const n=/^\[(?<name>[^\]=]+)(?:=(?<defaultValue>[^=]+))?\]$/u.exec(e.name);n&&(e.optional=!0,e.defaultValue=n.groups?.defaultValue??void 0,e.name=n.groups?.name??e.name)}if(e.typeExpression){const t=/(?<typeExpression>.+)= *$/u.exec(e.typeExpression);t&&(e.optional=!0,e.typeExpression=t.groups?.typeExpression??e.typeExpression)}}#d(e){const t=/\n?[ \t]*\*\/$|^[ \t]*(?:\/\*\* *\r?\n?| \* *)/ugm;return e.trim().replace(t,"")}}class Ge extends Error{expectedType="";filter=void 0;kind="";message="";pass=!0;tag="";value=void 0;valuePath=[];constructor(e){super(e),this.message=e,this.name="ValidationError"}}let l=fe();function se(){l.expectedType="",l.filter=void 0,l.kind="",l.message="",l.pass=!0,l.tag="",l.value=void 0,l.valuePath.length=0}function L(){l=fe()}function v(){const r=new Ge(l.message);for(const e in l)r[e]=l[e];throw r.stack=He(r),r}function fe(){return{expectedType:"",filter:void 0,kind:"",message:"",pass:!0,tag:"",value:void 0,valuePath:[]}}function He(r){if(!r.stack)return"";const e=r.stack.split(`
`);let t="";if(e.length>=2){t=e[0]??"";for(let n=6;n<e.length;n++)t+=`
${e[n]}`}return r.stack=t,r.stack}function Z(){return new Error("The schema has not been created yet")}function Xe(r,e,t){return new SyntaxError(`Could not create schema at ${r}:${e}:${t}. Make sure there is a valid JsDoc comment at the previous line`)}function pe(r,e,t){return new SyntaxError(`The schema at ${r}:${e}:${t} must be defined with one @enum tag, one or more @param tags, or a @typedef`)}const I={email:/^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/ui,cuid:/^c[^\s-]{8,}$/ui,cuid2:/^[a-z][a-z0-9]*$/u,ulid:/^[0-9A-HJKMNP-TV-Z]{26}$/u,uuid:/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/ui,ipv4:/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/u,ipv6:/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/u},p={array:"Expected array ",number:"Expected number ",string:"Expected string "};function k(r,e,t){const[n,s]=t;return n===!0&&!I[r].test(e)?(m(s||`${p.string}"${e}" to be ${r}`,r,t[0]),!1):n===!1&&I[r].test(e)?(m(s||`${p.string}"${e}" to not be ${r}`,r,t[0]),!1):!0}function m(r,e,t){l.message=r,l.kind="filter",l.filter={name:e,value:t}}const A={array:{min:(r,e)=>{const[t,n]=e;if(r.length<t){const s=D(r);return m(n||`${p.array}"${s}" to have ${t} or more elements`,"min",t),!1}return!0},max:(r,e)=>{const[t,n]=e;if(r.length>t){const s=D(r);return m(n||`${p.array}"${s}" to have ${t} or less elements`,"max",t),!1}return!0},length:(r,e)=>{const[t,n]=e;if(r.length!==t){const s=D(r);return m(n||`${p.array}"${s}" to have exactly ${t} elements`,"length",t),!1}return!0}},number:{min:(r,e)=>{const[t,n]=e;return r<t?(m(n||`${p.number}${r} to be ${t} or higher`,"min",t),!1):!0},max:(r,e)=>{const[t,n]=e;return r>t?(m(n||`${p.number}${r} to be ${t} or lower`,"max",t),!1):!0},gte:(r,e)=>{const[t,n]=e;return r<t?(m(n||`${p.number}${r} to be ${t} or higher`,"gte",t),!1):!0},lte:(r,e)=>{const[t,n]=e;return r>t?(m(n||`${p.number}${r} to be ${t} or lower`,"lte",t),!1):!0},gt:(r,e)=>{const[t,n]=e;return r<=t?(m(n||`${p.number}${r} to be higher than ${t}`,"gt",t),!1):!0},lt:(r,e)=>{const[t,n]=e;return r>=t?(m(n||`${p.number}${r} to be lower than ${t}`,"lt",t),!1):!0},step:(r,e)=>{const[t,n]=e;return r%t!==0?(m(n||`${p.number}${r} to be multiple of ${t}`,"step",t),!1):!0},int:(r,e)=>{const[t,n]=e,s=Number.isInteger(r);return t===!0&&!s?(m(n||`${p.number}${r} to be integer`,"int",t),!1):t===!1&&s?(m(n||`${p.number}${r} not to be integer`,"int",t),!1):!0},finite:(r,e)=>{const[t,n]=e,s=Number.isFinite(r);return t===!0&&!s?(m(n||`${p.number}${r} to be finite`,"finite",t),!1):t===!1&&s?(m(n||`${p.number}${r} not to be finite`,"finite",t),!1):!0},safeInt:(r,e)=>{const[t,n]=e;return Number.isInteger(r)?t===!0&&(r<Number.MIN_SAFE_INTEGER||r>Number.MAX_SAFE_INTEGER)?(m(n||`${p.number}${r} to be safe`,"safeInt",t),!1):t===!1&&r>=Number.MIN_SAFE_INTEGER&&r<=Number.MAX_SAFE_INTEGER?(m(n||`${p.number}${r} not to be safe`,"safeInt",t),!1):!0:(m(n||`${p.number}${r} to be integer`,"safeInt",t),!1)}},string:{min:(r,e)=>{const[t,n]=e;return r.length<t?(m(n||`${p.string}"${r}" to be at least ${t} characters long`,"min",t),!1):!0},max:(r,e)=>{const[t,n]=e;return r.length>t?(m(n||`${p.string}"${r}" to be max ${t} characters long`,"max",t),!1):!0},length:(r,e)=>{const[t,n]=e;return r.length!==t?(m(n||`${p.string}"${r}" to be exactly ${t} characters long`,"length",t),!1):!0},startsWith:(r,e)=>{const[t,n]=e;return r.startsWith(t)?!0:(m(n||`${p.string}"${r}" to start with "${t}"`,"startsWith",t),!1)},endsWith:(r,e)=>{const[t,n]=e;return r.endsWith(t)?!0:(m(n||`${p.string}"${r}" to end with "${t}"`,"endsWith",t),!1)},includes:(r,e)=>{const[t,n]=e;return r.includes(t)?!0:(m(n||`${p.string}"${r}" to include "${t}"`,"endsWith",t),!1)},excludes:(r,e)=>{const[t,n]=e;return r.includes(t)?(m(n||`${p.string}"${r}" not to include "${t}"`,"endsWith",t),!1):!0},url:(r,e)=>{const[t,n]=e;if(t===!0)try{const s=new URL(r)}catch{return m(n||`${p.string}"${r}" to be URL`,"url",t),!1}if(t===!1){try{const s=new URL(r)}catch{return!0}return m(n||`${p.string}"${r}" not to be URL`,"url",t),!1}return!0},pattern:(r,e)=>{const[t,n]=e;return t.test(r)?!0:(m(n||`${p.string}"${r}" to respect the regex pattern ${t}`,"pattern",t),!1)},ip:(r,e)=>{const[t,n]=e;return t===!0&&!I.ipv4.test(r)&&!I.ipv6.test(r)?(m(n||`${p.string}"${r}" to be IP address`,"ip",t),!1):t===!1&&(I.ipv4.test(r)||I.ipv6.test(r))?(m(n||`${p.string}"${r}" to not be IP address`,"ip",t),!1):!0},email:(r,e)=>k("email",r,e),cuid:(r,e)=>k("cuid",r,e),cuid2:(r,e)=>k("cuid2",r,e),ulid:(r,e)=>k("ulid",r,e),uuid:(r,e)=>k("uuid",r,e),ipv4:(r,e)=>k("ipv4",r,e),ipv6:(r,e)=>k("ipv6",r,e)}};function G(r,e){throw new SyntaxError(`"${r}" is not a valid filter for ${e}s`)}const O={array:(r,e)=>{for(const t in r)if((!(t in A.array)||!(r[t]instanceof Array))&&G(t,"array"),A.array[t](e,r[t])===!1)return!1;return!0},number:(r,e)=>{for(const t in r)if((!(t in A.number)||!(r[t]instanceof Array))&&G(t,"number"),A.number[t](e,r[t])===!1)return!1;return!0},string:(r,e)=>{for(const t in r)if((!(t in A.string)||!(r[t]instanceof Array))&&G(t,"string"),A.string[t](e,r[t])===!1)return!1;return!0}};function Ke(r,e){if(be(r))return!0;if(e instanceof Array)return O.array(r,e);if(typeof e=="number")return O.number(r,e);if(typeof e=="string")return O.string(r,e);const t=Object.keys(O).map(n=>`"${n}"`).join(", ");return l.kind="filter",l.message=`Expected "${e}" to be any of these types ${t}`,!1}const ie={simple:{any:()=>!0,boolean:(r,e)=>N(r,e),number:(r,e)=>N(r,e),string:(r,e)=>N(r,e),null:(r,e)=>e===null,undefined:(r,e)=>e===void 0,bigint:(r,e)=>N(r,e),symbol:(r,e)=>N(r,e)},complex:{array:(r,e,t,n,s,i,o)=>{if(!Array.isArray(e))return!1;for(let a=0;a<e.length;a++){const c=r?.types??[];if(!o(c,e[a],a,n,{},i,!1,!1))return!1}return!0},object:(r,e,t,n,s,i,o)=>{if(!(e instanceof Object))return!1;const a=r.typePairs??[];for(const c of a)for(const u in e){let f=o(c.keyTypes,u,u,n,s,i,!1,!1);if(!f||(f=o(c.valueTypes,e[u],u,n,s,i,!1,!1),!f))return!1}return!0},objectLiteral:(r,e,t,n,s,i,o)=>{if(!(e instanceof Object))return!1;const a=r.pairs??[];for(const c of a)if(!o(c.valueTypes,e[c.key],c.key,n,c.filters,i,!1,!1))return!1;return!0},typedef:(r,e,t,n,s,i,o,a,c)=>{for(let f=0;f<3;f++){let h=[];f===0?h=n.localTypedefs:f===1?h=n.importedTypedefs:f===2&&(h=n.ambientTypedefs);for(const d of h){const $=d.elements.typedef??d.elements.callback;if($?.name===r.typeExpression)return oe($,e,t,d,i,o,a,c)}}const u=e?.constructor?.name;return!!(typeof u=="string"&&u&&r.typeExpression===u)},directImport(r,e,t,n,s,i,o,a,c){const u=n.directlyImportedTypedefs;for(const f of u){const h=f.elements.typedef??f.elements.callback;if(!(!r.directImport||h?.name!==r.directImport.typeName))return oe(h,e,t,f,i,o,a,c)}return!1}}};function oe(r,e,t,n,s,i,o,a){const c=n.elements.callback?"param":"property",u=n.elements[c];if(u&&u.length>0){if(!(e instanceof Object))return!1;let d=null;return n.elements.callback?d=s.validateFunctionArguments(n,e,a):d=o?s.validate(n,e,a):s.check(n,e,a),d.pass}const f=r?.types??[];return i(f,e,t,n,r?.filters,s,o,a)}function N(r,e){return typeof e===r.typeName?"value"in r?e===r.value:!0:!1}function me(r,e,t,n,s,i,o,a){let c=!1;for(const u of r){const{typeName:f}=u,h=ie.simple[f]??ie.complex[f];if(!(h instanceof Function))throw new SyntaxError(`Wrong typeName ${f}`);if(c||(f.startsWith("object")||f.startsWith("array"),t!==""&&(l.valuePath.push(t.toString()),c=!0)),l.kind="type",l.expectedType=u.typeExpression,l.value=e,h(u,e,t,n,s,i,me,o,a)&&Ke(s,e))return l.expectedType="",l.kind="",l.pass=!0,l.value=void 0,l.valuePath.pop(),!0}return l.pass=!1,l.kind=l.kind||"",!1}class Qe{get lastError(){return l}check(e,t,n=!1){return this.#e(e,t,!1,n)}validate(e,t,n=!1){return this.#e(e,t,!0,n)}checkFunctionArguments(e,t,n=!1){return this.#n(e,t,!1,n)}validateFunctionArguments(e,t,n=!1){return this.#n(e,t,!0,n)}checkReturns(e,t,n=!1){return this.#r("returns",e,t,!1,n)}validateReturns(e,t,n=!1){return this.#r("returns",e,t,!0,n)}#e(e,t,n,s){const{elements:i}=e,o=s||e.strict;if(i.param.length>0)return this.#t("param",e,t,n,o);if(i.typedef)return this.#s(e,t,n,o);if(i.enum)return this.#r("enum",e,t,n,o);if(i.type)return this.#r("type",e,t,n,o);throw pe(e.file,e.endLine+1,0)}#n(e,t,n=!0,s=!1){if(e.elements.type&&e.elements.param.length===0)return this.#r("type",e,t,n,s);L();const i=e.elements.param;if((e.strict||s)&&!this.#c(e,i,t,n))return l.pass=!1,l;for(const o of i){se();const{id:a}=o,c=o.destructured?t[a]?.[o.destructured[1]]:t[a];if(o.optional&&c===void 0)continue;const u=this.#i("argument",o,c,a,e,n,s);if(!n&&!u)break}return l}#t(e,t,n,s=!0,i=!1){L();const o=t.elements[e];if((t.strict||i)&&!this.#a(e,t,o,n,s))return l.pass=!1,l;for(const a of o){se();const c=a.name,u=a.destructured?n[a.destructured[0]]?.[a.destructured[1]]:n[a.name];if(a.optional&&u===void 0)continue;const f=this.#i(e,a,u,c,t,s,i);if(!s&&!f)break}return l}#r(e,t,n,s=!0,i=!1){L();const o=t.elements[e];if(o===void 0)throw new Error("The AST you are trying to use is not valid");return o===null?(l.pass=n===void 0,l):(this.#i(e,o,n,"",t,s,i),l)}#s(e,t,n=!0,s=!1){if(L(),e.elements?.typedef===void 0)throw new Error("The AST you are trying to use is not valid");return this.#t("property",e,t,n,s)}#i(e,t,n,s,i,o,a){const c=me(t.types,n,s,i,t.filters,this,o,a);if(l.tag=e==="argument"?"param":e,!c){if(!l.message){const u=t.types[0]?.typeExpression??"";l.message=this.#u(e,u,n,s)}o&&v()}return c}#a(e,t,n,s,i){const o=Object.keys(s),a=new Array(n.length);for(let c=0;c<n.length;c++)a[c]=n[c]?.name??"";for(const c of o)if(!a.includes(c))return l.kind="strict",l.tag=e==="argument"?"param":e,l.message=`Strict validation failed. Property "${c}" is missing in the schema at file://${t.file}:${t.startLine}`,l.pass=!1,i&&v(),!1;return!0}#c(e,t,n,s){return n.length<=t.length?!0:(l.kind="strict",l.tag="param",l.message=`Arguments count is higher than expected in the schema at file://${e.file}:${e.startLine}`,l.pass=!1,s&&v(),!1)}#u(e,t,n,s){let i="";e==="argument"?i=e:e==="param"||e==="property"?i=Se(s):e==="returns"?i="the returned value":i="value";let o=`Expected ${i} to be of type ${t}, but the actual `;if(n==null)o+=`value is ${n}`;else{let a=n?.constructor?.name;(a==="Boolean"||a==="Number"||a==="String"||a==="Array"||a==="Object")&&(a=""),a?o+=`value is an instance of ${a}`:(o+=`type is ${typeof n}`,o+=n instanceof Object?`, and the value is ${D(n,300)}`:"")}return o}}const Ye=[/at (.+) \((.+):(\d+):(\d+)\)$/u,/at ()(.+):(\d+):(\d+)$/u,/(.*)@(.+):(\d+):(\d+)$/u,/(.*)@(.+):(\d+)()$/u];function et(r){let e=null;for(const t of Ye)if(e=t.exec(r??""),e)break;return e||null}function tt(r){const e=/^file:(?:\/\/\/([a-zA-Z]:[\\\/].*)|\/*(\/.+))/u.exec(r.trim());return e?.[1]??e?.[2]??r}function rt(r=1){let e=[];try{throw new Error("")}catch(s){s instanceof Error&&(e=(s.stack??"").replaceAll("\r","").split(`
`).map(i=>i.trim()),e=e.splice((e[0]??"").startsWith("Error")?2:1))}if(!e[r])return null;const t=et(e[r]??"");if(!t)return null;const n=tt(t[2]??"");return{fileName:decodeURI(n),lineNumber:parseInt(t[3]??"0"),columnNumber:parseInt(t[4]??"0")}}const nt=new Ze,W=new Qe;let X=0;function st(r){X=r}class it{#e=null;#n="";#t=0;#r=0;constructor(e){this.#s()}get lastError(){return W.lastError}get ast(){return this.#e}approves(e){if(!this.#e)throw Z();return W.check(this.#e,e).pass}check(e){if(!this.#e)throw Z();return W.check(this.#e,e)}validate(e){if(!this.#e)throw Z();return W.validate(this.#e,e),e}#s(){const e=rt(2+X);if(X=0,!e)throw new Error("Could not create schema, because the stack was not parsed successfully");this.#t=e.lineNumber,this.#r=e.columnNumber,this.#n=e.fileName.replace(/^file:[\/]+/u,"");const t=nt.parseFile(this.#n);let n=null;for(const i of t){if(this.#t<i.endLine)break;(this.#t===i.endLine+1||this.#t===i.startLine&&i.elements.type)&&(n=i)}if(!n)throw Xe(this.#n,this.#t,this.#r);const{elements:s}=n;if(!s.type&&!s.enum&&!s.typedef&&s.param.length===0)throw pe(this.#n,this.#t,this.#r);this.#e=n}}function ot(r){return st(1),new it(r)}const at=null;export{it as DocSchema,Ze as DocSchemaParser,Qe as DocSchemaValidator,Ge as ValidationError,ot as default,ot as docSchema,at as type};
//# sourceMappingURL=bundle.js.map