@golemui/schemas 1.5.1 → 1.6.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +117 -0
- package/cli.cjs +26 -13
- package/cli.js +283 -121
- package/json-schema-Bdnmzg_d.js +1514 -0
- package/json-schema-Dp0MTcM8.cjs +1 -0
- package/json-schema.cjs +1 -0
- package/json-schema.d.ts +2 -0
- package/json-schema.js +4 -0
- package/lib/json-schema/conditions.d.ts +32 -0
- package/lib/json-schema/convert.d.ts +25 -0
- package/lib/json-schema/declarative-rules.d.ts +17 -0
- package/lib/json-schema/enum-options.d.ts +14 -0
- package/lib/json-schema/expressions.d.ts +49 -0
- package/lib/json-schema/merge-schemas.d.ts +42 -0
- package/lib/json-schema/normalize.d.ts +73 -0
- package/lib/json-schema/pointer.d.ts +36 -0
- package/lib/json-schema/text.d.ts +11 -0
- package/lib/json-schema/types.d.ts +256 -0
- package/lib/json-schema/unions.d.ts +34 -0
- package/lib/json-schema/widget-json.d.ts +26 -0
- package/package.json +6 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const ge=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function _(t){return typeof t!="string"?JSON.stringify(t):JSON.stringify(t).replace(/\.(?=\d)/g,'." + "')}function z(t,e){return e.reduce((r,s,i)=>{const n=i===0?"":"?.";return ge.test(s)?`${r}${n===""?".":n}${s}`:`${r}${n}[${_(s)}]`},t)}function oe(t){for(let e=t;e;e=e.parent){const r=e.parent;if(r?.type==="array"&&e.path===`${r.path}.items`)return e}}function K(t,e){const r=oe(t);if(r===void 0)return z("$form",e.split("."));const s=e.slice(r.path.length+1);return s===""?"$item":z("$item",s.split("."))}function I(t,e){const r=e.map(s=>`${t} === ${_(s)}`);return r.length===1?r[0]:`(${r.join(" || ")})`}const ae=new Set(["title","description","$comment","examples","default","deprecated"]);function be(t,e){if(typeof t=="boolean")return{expression:String(t),vacuous:!1,reads:[]};if(!j(t))return{unsupported:"a value that is not a schema"};const r={vacuous:!1},s=ce(t,[],e,r);if("unsupported"in s)return s;const i=j(t.properties)?Object.keys(t.properties):[],n=[...new Set([...i,...pe(t)])];return{expression:ue(s.tests),vacuous:r.vacuous,reads:n}}function ce(t,e,r,s){for(const[a,c]of Object.entries(t))if(!(a==="properties"||a==="required"||ae.has(a)||a==="type"&&c==="object"))return{unsupported:a};const i=pe(t),n=j(t.properties)?t.properties:{},o=[];for(const[a,c]of Object.entries(n)){const u=[...e,a],p=r(u),d=ve(c,u,r,s);if("unsupported"in d)return d;i.includes(a)?o.push(d.impliesPresence?d.test:`${p} !== undefined && ${d.test}`):d.test!=="true"&&(o.push(`(${p} === undefined || ${d.test})`),s.vacuous=!0)}for(const a of i)a in n||o.push(`${r([...e,a])} !== undefined`);return{tests:o}}function ve(t,e,r,s){if(t===!0)return{test:"true",impliesPresence:!1};if(t===!1)return{test:"false",impliesPresence:!0};if(!j(t))return{unsupported:"a value that is not a schema"};const i=r(e),n=[];let o=!1;for(const[a,c]of Object.entries(t))if(!(a==="type"||a==="properties"||a==="required"||ae.has(a)))if(a==="const"&&x(c))n.push(`${i} === ${_(c)}`),o||=c!==null;else if(a==="enum"&&Array.isArray(c)&&c.every(x))n.push(I(i,c)),o||=!c.includes(null);else if(a==="not"&&B(c)!==void 0)n.push(`!(${I(i,B(c))})`);else if(L[a]!==void 0&&typeof c=="number")n.push(`${i} ${L[a]} ${c}`),o=!0;else return{unsupported:a};if("properties"in t||"required"in t){const a=ce({properties:t.properties,required:t.required},e,r,s);if("unsupported"in a)return a;n.push(...a.tests)}return{test:ue(n),impliesPresence:o}}const L={minimum:">=",maximum:"<=",exclusiveMinimum:">",exclusiveMaximum:"<"};function B(t){if(!(!j(t)||Object.keys(t).length!==1)){if("const"in t&&x(t.const))return[t.const];if(Array.isArray(t.enum)&&t.enum.every(x))return t.enum}}function ue(t){return t.length===0?"true":t.join(" && ")}function pe(t){return Array.isArray(t.required)?t.required.filter(e=>typeof e=="string"):[]}function x(t){return t===null||typeof t=="string"||typeof t=="number"||typeof t=="boolean"}function j(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function V(t){return"match"in t&&!("when"in t)}const Oe={$type:t=>t.type,$path:t=>t.path,$name:t=>t.name,$defName:t=>t.defName,$required:t=>t.required,$inRepeater:t=>t.repeaterDepth>0};function J(t,e){return Object.entries(t.match).every(([r,s])=>{const i=Oe[r],n=i?i(e):e.schema[r];return Ne(s)?$e(n,s):r==="$path"&&typeof s=="string"&&typeof n=="string"?Ae(s,n):de(n,s)})}function Ne(t){if(t===null||typeof t!="object"||Array.isArray(t))return!1;const e=Object.keys(t);return e.length===1&&["$in","$exists","$regex"].includes(e[0])}function $e(t,e){return"$in"in e?e.$in.some(r=>de(r,t)):"$exists"in e?t!==void 0===e.$exists:typeof t=="string"&&new RegExp(e.$regex).test(t)}function Ae(t,e){const r=(i,n)=>{if(i.length===0)return n.length===0;const[o,...a]=i;return o==="**"?n.some((c,u)=>r(a,n.slice(u)))||r(a,[]):n.length===0?!1:(o==="*"||o===n[0])&&r(a,n.slice(1))},s=i=>i===""?[]:i.split(".");return r(s(t),s(e))}function de(t,e){return t===e||JSON.stringify(t)===JSON.stringify(e)}function R(t){return t.replace(/[_-]+/g," ").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/\s+/g," ").trim().replace(/^./,e=>e.toUpperCase())}function we(t){const{schema:e}=t;if(Array.isArray(e.enum)){const r=Array.isArray(e.enumNames)?e.enumNames:[],s=[];return e.enum.forEach((i,n)=>{if(Y(i)){const o=r[n];s.push({label:typeof o=="string"?o:G(i),value:i})}}),s}for(const r of["oneOf","anyOf"]){const s=e[r];if(M(s))return s.filter(i=>Y(i.const)).map(i=>({label:typeof i.title=="string"?i.title:G(i.const),value:i.const}))}}function M(t){return Array.isArray(t)&&t.length>0&&t.every(je)}function je(t){return t!==null&&typeof t=="object"&&"const"in t}function Y(t){return typeof t=="string"||typeof t=="number"||typeof t=="boolean"}function G(t){return typeof t=="string"?R(t):String(t)}const Se=new Set(["minimum","exclusiveMinimum","minLength","minItems","minProperties","minContains"]),Te=new Set(["maximum","exclusiveMaximum","maxLength","maxItems","maxProperties","maxContains"]),xe=new Set(["const","pattern","format","multipleOf","prefixItems","oneOf","anyOf","not","contains"]),Re=new Set(["properties","patternProperties"]),S=Symbol("conflict");function qe(t,e){const r={},s=[];let i;for(const n of t){const o=De(i,n.allowedTypes);o===S?s.push({keyword:"type",pointer:n.pointer}):i=o;for(const[a,c]of Object.entries(n.schema)){if(a==="type")continue;if(!(a in r)){r[a]=c;continue}const u=Ee(a,r[a],c,e);u===S?s.push({keyword:a,pointer:n.pointer}):r[a]=u}}return{schema:r,allowedTypes:i,conflicts:s}}function Pe(t){const e=typeof t.type=="string"?[t.type]:Array.isArray(t.type)?t.type:void 0;if(e===void 0)return;const r=e.filter(ke);return r.includes("number")&&!r.includes("integer")&&r.splice(r.indexOf("number")+1,0,"integer"),r}function ke(t){return t==="string"||t==="number"||t==="integer"||t==="boolean"||t==="object"||t==="array"||t==="null"}function De(t,e){if(e===void 0)return t;if(t===void 0)return e;const r=t.filter(s=>e.includes(s));return r.length>0?r:S}function Ee(t,e,r,s){if(U(e,r))return e;if(typeof e=="number"&&typeof r=="number"){if(Se.has(t))return Math.max(e,r);if(Te.has(t))return Math.min(e,r)}if(t==="required"&&Array.isArray(e)&&Array.isArray(r))return[...e,...r.filter(i=>!e.includes(i))];if(t==="enum"&&Array.isArray(e)&&Array.isArray(r)){const i=e.filter(n=>r.some(o=>U(n,o)));return i.length>0?i:S}return t==="uniqueItems"?e===!0||r===!0:Re.has(t)&&C(e)&&C(r)?Ie(e,r,s):t==="items"?fe(e,r,s):xe.has(t)?S:e}function Ie(t,e,r){const s={...t};for(const[i,n]of Object.entries(e))s[i]=i in s?fe(s[i],n,r):n;return s}function fe(t,e,r){if(U(t,e))return t;const s={allOf:[t,e]},i=C(t)?r.get(t):void 0;return i!==void 0&&r.set(s,i),s}function C(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function U(t,e){return t===e||JSON.stringify(t)===JSON.stringify(e)}function Me(t){return t.replace(/~/g,"~0").replace(/\//g,"~1")}function le(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}function g(t,...e){return t+e.map(r=>`/${Me(String(r))}`).join("")}function he(t,e){const r=t.indexOf("#"),s=r===-1?t:t.slice(0,r),i=r===-1?"":t.slice(r+1);if(s!==""&&s!==e.$id)return{error:`Only refs inside the same document are supported, found "${t}".`};if(i!==""&&!i.startsWith("/"))return{error:`Anchor refs are not supported, found "${t}".`};let n=e;const o=[];for(const a of i===""?[]:i.slice(1).split("/")){let c;try{c=le(decodeURIComponent(a))}catch{return{error:`"${t}" is not a valid URI fragment.`}}if(n===null||typeof n!="object"||!Object.prototype.hasOwnProperty.call(n,c))return{error:`"${t}" points to nothing in the document.`};n=n[c],o.push(c)}return{target:n,pointer:g("",...o)}}function Ce(t){const e=/(?:^|\/)(?:\$defs|definitions|components\/schemas)\/([^/]+)$/.exec(t);return e?le(e[1]):void 0}const Ue=["$defs","definitions","$schema","$id","$anchor","$dynamicAnchor"];function w(t,e,r,s){if(t===!1)return;if(t===!0||t===void 0)return{schema:{},nullable:!1,conditionals:[],refTrail:[...r]};if(t===null||typeof t!="object"||Array.isArray(t)){s.report({severity:"error",code:"invalid-schema",message:"This value is not a JSON Schema object or boolean.",pointer:e});return}He(t,e,s.pointers);const i=_e(t,e);let n=[...r],o=i.nullableKeyword,a;const c=[...i.conditionals],p=[{schema:i.schema,pointer:e,allowedTypes:Z(Pe(i.schema),i.nullableKeyword)}],d=(m,$,P=!1)=>{p.push({schema:m.schema,pointer:$,allowedTypes:Z(m.allowedTypes,P)}),c.push(...m.conditionals),n=[...n,...m.refTrail.slice(r.length)]},f=i.schema.$ref;if(delete i.schema.$ref,typeof f=="string"){const m=We(f,e,[...r],s);if(m===void 0)return;d(m.node,m.pointer),a=Ce(m.pointer)??m.node.defName}const y=i.schema.allOf;if(delete i.schema.allOf,Array.isArray(y)){const m=y.map(($,P)=>{const F=g(e,"allOf",P),k=w($,F,r,s);return k!==void 0&&d(k,F),k});m.length===1&&(a??=m[0]?.defName)}const l=Fe(i.schema);if(l!==void 0){delete i.schema[l.keyword];const m=g(e,l.keyword,l.index),$=w(l.branch,m,r,s);if($===void 0)return;d($,m,!0),o=!0,a??=$.defName}const b=qe(p,s.pointers);for(const m of b.conflicts)s.report({severity:"warning",code:"allof-conflict",message:`The combined schemas disagree on \`${m.keyword}\`. The first value is used.`,pointer:m.pointer});const h=b.schema;Array.isArray(h.enum)&&h.enum.includes(null)&&(h.enum=h.enum.filter(m=>m!==null),o=!0);const N=ze(b.allowedTypes,h,e,s);return N.type===void 0?delete h.type:h.type=N.type,{schema:h,type:N.type,nullable:o||N.nullable,defName:a,conditionals:c,refTrail:n,allowedTypes:b.allowedTypes}}function We(t,e,r,s){const i=r.filter(a=>a===t).length;if(i>=s.maxRefDepth){s.report({severity:"warning",code:"recursive-ref",message:`"${t}" repeats ${i} times on this branch, so the form stops here.`,pointer:e});return}const n=he(t,s.root);if("error"in n){s.report({severity:"error",code:"unresolved-ref",message:n.error,pointer:e});return}n.target!==null&&typeof n.target=="object"&&(s.pointers.has(n.target)||s.pointers.set(n.target,n.pointer));const o=w(n.target,n.pointer,[...r,t],s);return o===void 0?void 0:{node:o,pointer:n.pointer}}function _e(t,e){const r={...t},s=[];for(const n of Ue)delete r[n];"if"in r&&s.push({kind:"if",if:r.if,then:r.then,else:r.else,pointer:e}),delete r.if,delete r.then,delete r.else;for(const n of["dependentRequired","dependentSchemas","dependencies"]){const o=r[n];if(delete r[n],!(o===null||typeof o!="object"||Array.isArray(o)))for(const[a,c]of Object.entries(o)){const u=g(e,n,a);if(Array.isArray(c)){if(n!=="dependentSchemas"){const p=c.filter(d=>typeof d=="string");s.push({kind:"dependentRequired",property:a,required:p,pointer:u})}}else n!=="dependentRequired"&&s.push({kind:"dependentSchemas",property:a,schema:c,pointer:u})}}Array.isArray(r.items)&&(r.prefixItems??=r.items,delete r.items,"additionalItems"in r&&(r.items=r.additionalItems)),delete r.additionalItems;for(const[n,o]of[["exclusiveMinimum","minimum"],["exclusiveMaximum","maximum"]])r[n]===!0&&typeof r[o]=="number"?(r[n]=r[o],delete r[o]):typeof r[n]=="boolean"&&delete r[n];"example"in r&&(r.examples??=[r.example],delete r.example);const i=r.nullable===!0;return delete r.nullable,{schema:r,conditionals:s,nullableKeyword:i}}function Fe(t){for(const e of["oneOf","anyOf"]){const r=t[e];if(!Array.isArray(r)||r.length!==2)continue;const s=r.findIndex(H);if(s!==-1&&!H(r[1-s]))return{keyword:e,branch:r[1-s],index:1-s}}}function H(t){if(t===null||typeof t!="object")return!1;const e=t;return e.type==="null"||e.const===null||Array.isArray(e.enum)&&e.enum.length===1&&e.enum[0]===null}function Z(t,e){return t===void 0||!e||t.includes("null")?t:[...t,"null"]}function ze(t,e,r,s){if(t===void 0)return{type:Be(e),nullable:!1};const i=t.filter(o=>o!=="null"),n=i.includes("number")?i.filter(o=>o!=="integer"):i;return n.length===0?{type:"null",nullable:!1}:(n.length>1&&s.report({severity:"warning",code:"multi-type",message:`The schema allows several types (${n.join(", ")}). The form uses the first one, ${n[0]}.`,pointer:r}),{type:n[0],nullable:t.includes("null")})}const Ke=["format","pattern","minLength","maxLength"],Le=["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf"];function Be(t){if(["properties","additionalProperties","patternProperties","required"].some(r=>r in t))return"object";if("items"in t||"prefixItems"in t)return"array";const e="const"in t?[t.const]:Array.isArray(t.enum)?t.enum:X(t.oneOf)??X(t.anyOf);if(e!==void 0&&e.length>0){const r=[...new Set(e.map(Ve))];return r.every(s=>s==="integer"||s==="number")?r.includes("number")?"number":"integer":r.length===1?r[0]:void 0}if(Ke.some(r=>r in t))return"string";if(Le.some(r=>r in t))return"number"}function X(t){if(!Array.isArray(t)||t.length===0)return;const e=[];for(const r of t){if(r===null||typeof r!="object"||!("const"in r))return;e.push(r.const)}return e}function Ve(t){return t===null?"null":Array.isArray(t)?"array":typeof t=="number"?Number.isInteger(t)?"integer":"number":typeof t=="string"?"string":typeof t=="boolean"?"boolean":"object"}const Je=["additionalProperties","additionalItems","items","contains","not","if","then","else","propertyNames"],Ye=["allOf","anyOf","oneOf","prefixItems","items"],Ge=["properties","patternProperties","dependentSchemas","dependencies"];function He(t,e,r){const s=(i,n)=>{i!==null&&typeof i=="object"&&!r.has(i)&&r.set(i,n)};for(const i of Je)Array.isArray(t[i])||s(t[i],g(e,i));for(const i of Ye){const n=t[i];Array.isArray(n)&&n.forEach((o,a)=>s(o,g(e,i,a)))}for(const i of Ge){const n=t[i];if(n!==null&&typeof n=="object"&&!Array.isArray(n))for(const[o,a]of Object.entries(n))s(a,g(e,i,o))}}function Ze(t,e){const r=t.discriminator;if(q(r)&&typeof r.propertyName=="string"){const i=r.propertyName,n=q(r.mapping)?r.mapping:{},o=e.map(a=>ee(te(a.schema,i))??Object.keys(n).find(c=>n[c]===a.ref)??a.defName);return Q(o)?{property:i,values:o}:void 0}const s=e[0]?me(e[0].schema):{};for(const i of Object.keys(s)){const n=e.map(o=>ee(te(o.schema,i)));if(Q(n))return{property:i,values:n}}}function Q(t){return t.every(e=>e!==void 0)&&new Set(t).size===t.length}function ee(t){if(!q(t))return;const e="const"in t?t.const:Array.isArray(t.enum)&&t.enum.length===1?t.enum[0]:void 0;return Xe(e)?e:void 0}function me(t){return q(t.properties)?t.properties:{}}function te(t,e){return me(t)[e]}function Xe(t){return t===null||typeof t=="string"||typeof t=="number"||typeof t=="boolean"}function q(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function O(t){return t==null?[]:Array.isArray(t)?t:[t]}const Qe=["label","validator","defaultValue","readonly","size","uid"];function D(t,e){const r=O(t);if(e===void 0||r.length===0)return t;const s={...r[0]};for(const n of Qe)e[n]!==void 0&&(s[n]=e[n]);e.props!==void 0&&(s.props={...r[0].props,...e.props});const i=s;return Array.isArray(t)?[i,...r.slice(1)]:i}function re(t,e){const r=O(t).map(s=>{const i=s.include,n=i!==void 0&&"when"in i?`(${i.when}) && ${e}`:e;return{...s,include:{when:n}}});return t==null?t:Array.isArray(t)?r:r[0]}function et(t,e){const r=new Set,s=new Set,i=o=>{const a=[];for(const c of o){if(c.kind==="input"&&typeof c.path=="string"){if(r.has(c.path)){e(c,"path");continue}r.add(c.path)}if(typeof c.uid=="string"){if(s.has(c.uid)){e(c,"uid");continue}s.add(c.uid)}a.push(n(c))}return a},n=o=>{let a=o;Array.isArray(o.children)&&(a={...a,children:i(o.children)});const c=o.props?.template;return ye(c)&&(a={...a,props:{...o.props,template:n(c)}}),a};return i(t)}const ie=["kind","type","uid","actionType","path","label","include","exclude","disabled","readonly","defaultValue","validator","size","on","props","children"];function W(t){const e=i=>{const n=ie.indexOf(i.split(".")[0]);return n===-1?ie.length:n},r=Object.keys(t).filter(i=>t[i]!==void 0).map((i,n)=>({key:i,position:n})).sort((i,n)=>{const o=e(i.key)-e(n.key);if(o!==0)return o;const a=Number(i.key.includes("."))-Number(n.key.includes("."));return a!==0?a:i.position-n.position}),s={};for(const{key:i}of r)s[i]=t[i];if(Array.isArray(t.children)&&(s.children=t.children.map(W)),t.props!==void 0){const i=Object.fromEntries(Object.entries(t.props).filter(([,n])=>n!==void 0));ye(i.template)&&(i.template=W(i.template)),s.props=i}return s}function ye(t){return t!==null&&typeof t=="object"&&typeof t.kind=="string"&&typeof t.type=="string"}const tt="x-golemui",rt=2,it=2e3,nt=["not","contains","propertyNames","patternProperties","unevaluatedProperties","unevaluatedItems","$dynamicRef","$recursiveRef"];function st(t,e){return new ot(t,e).run()}class ot{constructor(e,r){this.schema=e,this.options=r,this.diagnostics=[],this.pointers=new WeakMap,this.normalizedNodes=new WeakMap,this.childrenCache=new WeakMap,this.itemCache=new WeakMap,this.objectUnions=new WeakMap,this.states=new Map,this.stateCounters=new Map,this.widgetNodes=new WeakMap,this.builtNodes=0,this.preset=r.preset,this.userRules=(r.rules??[]).map((i,n)=>V(i)?this.declarativeRuleEntry(i,`rule ${n}`):{name:i.name??`rule ${n}`,test:o=>i.when(o),build:i.build});const s=this.preset.rules.map((i,n)=>({name:i.name??`${this.preset.name} rule ${n}`,test:o=>i.when(o),build:i.build}));this.allRules=[...this.userRules,...s],this.refRoot=r.refRoot??(typeof e=="object"?e:{}),this.vendorKeyword=r.vendorKeyword??tt,this.maxRefDepth=r.maxRefDepth??rt,this.maxNodes=r.maxNodes??it}run(){const e=this.createNode(this.schema,{path:"",pointer:"",required:!1,repeaterDepth:0,refTrail:[]});let r=[];this.schema===!1?this.diagnostics.push({severity:"error",code:"root-not-object",message:"The root schema is `false`, so no data is valid and there is no form.",path:"",pointer:""}):e!==void 0&&e.type!=="object"?this.report(e,{severity:"error",code:"root-not-object",message:"Only an object schema at the root can become a form."}):e!==void 0&&(r=this.buildRoot(e));const s=et(this.preset.root(r),(n,o)=>{const a=this.widgetNodes.get(n);this.diagnostics.push({severity:"error",code:o==="path"?"duplicate-path":"duplicate-uid",message:`Two widgets have the ${o} \`${String(n[o])}\`. The later one is removed.`,path:a?.path??String(n.path??""),pointer:a?.pointer??""})}),i={...this.preset.schemaUrl===void 0?{}:{$schema:this.preset.schemaUrl},...this.states.size===0?{}:{states:Object.fromEntries([...this.states].map(([n,o])=>[o,n]))},form:s.map(W)};return{formDefinition:this.options.transform?.(i)??i,diagnostics:this.diagnostics}}buildRoot(e){const r=this.patchesFor(e),s=this.namedWidget(r);let i=s!==void 0?this.buildNamedWidget(s,e):this.buildWithRules(e,0,this.userRules);if(i===void 0)return this.buildChildren(e);for(const n of r)i=D(i,n);return O(i)}buildNode(e){if(!this.countNode(e))return null;const r=this.patchesFor(e);if(r.some(n=>n.skip===!0))return null;const s=this.namedWidget(r);let i=s!==void 0?this.buildNamedWidget(s,e):this.buildWithRules(e,0,this.allRules);if(i===void 0)return this.report(e,{severity:"error",code:"unsupported-shape",message:`No rule can build this ${e.type??"untyped"} schema, so it is not rendered.`}),null;for(const n of r)i=D(i,n);for(const n of O(i))this.widgetNodes.set(n,e);return i}buildWithRules(e,r,s){for(let i=r;i<s.length;i++){const n=s[i];let o;try{if(!n.test(e))continue;o=n.build(e,this.contextFor(e,i,s))}catch(a){this.report(e,{severity:"warning",code:"rule-error",message:`${n.name} threw (${se(a)}), so the next rule is used.`});continue}if(o!==void 0)return D(o,n.patch)}}buildNamedWidget(e,r){const s=this.preset.widgets[e],i=this.contextFor(r,-1,this.allRules);if(s===void 0)return this.report(r,{severity:"info",code:"custom-widget",message:`The ${this.preset.name} preset has no \`${e}\` widget, so a plain input of that type is built.`}),{kind:"input",type:e,path:r.path,label:i.label(r),validator:i.validator(r)};try{return s(r,i)}catch(n){return this.report(r,{severity:"warning",code:"rule-error",message:`The \`${e}\` builder threw (${se(n)}), so the rules are used.`}),this.buildWithRules(r,0,this.allRules)}}contextFor(e,r,s){return{next:(i=e)=>this.buildWithRules(i,r+1,s),build:i=>this.buildNode(i),children:i=>this.childrenOf(i),buildChildren:i=>this.buildChildren(i),item:i=>this.itemOf(i),label:i=>at(i),validator:i=>this.preset.validator(i),enumOptions:i=>we(i),diagnostic:i=>this.diagnostics.push({...i,path:i.path??e.path,pointer:i.pointer??e.pointer})}}buildChildren(e){const r=this.childrenOf(e),s=this.objectUnions.get(e.schema),i=s?this.buildUnionChildren(e,r,s):r.flatMap(n=>O(this.buildNode(n)));return this.applyConditionals(e,r,i)}applyConditionals(e,r,s){const i=this.compileConditionals(e);if(i.length===0)return s;const n=new Map(r.flatMap(p=>p.name===void 0?[]:[[p.name,p]])),o=new Map,a=new Map,c=(p,d,f)=>p.set(d,[...p.get(d)??[],f]);for(const p of i){const d=p.schema?.schema??{},f=A(d.properties),y=[...T(d),...p.requiredNames].filter(l=>typeof l=="string");for(const[l,b]of Object.entries(f))n.has(l)?c(o,l,{branch:p,raw:b,required:y.includes(l)}):c(a,l,{branch:p,raw:b,required:y.includes(l),pointer:this.pointerOf(b)??g(p.schemaPointer,"properties",l)});for(const l of y)!(l in f)&&n.has(l)&&c(o,l,{branch:p,raw:void 0,required:!0})}const u=this.applyOverlays(e,n,o,s);return this.insertBranchProperties(e,a,u)}compileConditionals(e){const r=this.normalizedNodes.get(e)?.conditionals??[],s=n=>K(e,v(e.path,n.join("."))),i=[];return r.forEach((n,o)=>{if(n.kind!=="if"){i.push({condition:`${s([n.property])} !== undefined`,statePrefix:"dep",group:o,schema:n.kind==="dependentSchemas"?this.normalizeBranch(e,n.schema,n.pointer):void 0,schemaPointer:n.pointer,requiredNames:n.kind==="dependentRequired"?n.required:[],reads:[n.property]});return}const a=be(n.if,s);"unsupported"in a?this.diagnostics.push({severity:"warning",code:"if-unsupported",message:`The \`if\` uses \`${a.unsupported}\`, which has no expression equivalent. The \`then\` and \`else\` properties are always shown and optional.`,path:e.path,pointer:g(n.pointer,"if")}):a.vacuous&&this.diagnostics.push({severity:"info",code:"if-vacuous",message:"The `if` also holds when a property it tests is absent, because that property is not in `if.required`. JSON Schema defines it that way.",path:e.path,pointer:g(n.pointer,"if")});const c="unsupported"in a?void 0:a.expression;for(const u of["then","else"]){const p=n[u];if(p===void 0)continue;const d=g(n.pointer,u);i.push({condition:c===void 0?void 0:u==="then"?c:`!(${c})`,statePrefix:"if",group:o,side:u,schema:this.normalizeBranch(e,p,d),schemaPointer:d,requiredNames:[],reads:"unsupported"in a?[]:a.reads})}}),i}normalizeBranch(e,r,s){const i=this.pointerOf(r)??s,n=w(r,i,this.refTrailOf(e),this.environmentFor(e.path));return n!==void 0&&n.conditionals.length>0&&this.diagnostics.push({severity:"warning",code:"nested-conditional",message:"A conditional inside a conditional branch is not supported, so it is not applied.",path:e.path,pointer:i}),n}applyOverlays(e,r,s,i){let n=i;for(const[o,a]of s){const c=r.get(o),u=a.filter(f=>f.branch.condition!==void 0);if(u.length===0)continue;if(oe(e)!==void 0){this.report(c,{severity:"warning",code:"conditional-validator-in-repeater",message:`Inside a repeater a condition can only show or hide widgets, so the conditional constraints of \`${o}\` are not applied.`});continue}const p={};for(const f of this.selectOverlays(c,u)){const y=this.stateFor(f.prefix,f.condition),l=this.preset.validator(this.derivedNode(c,f.raw,f.required||c.required));l!==void 0&&(p[`validator.${y}`]=l)}const d=n.findIndex(f=>f.kind==="input"&&f.path===c.path);if(d===-1){this.report(c,{severity:"warning",code:"overlay-limit",message:`\`${o}\` is not built as one input, so its conditional constraints are not applied.`});continue}n=[...n],n[d]={...n[d],...p}}return n}selectOverlays(e,r){if(r.every(n=>n.raw===void 0)){const n=[...new Set(r.map(o=>o.branch.condition))];return[{condition:ne(n),raw:void 0,required:!0,prefix:n.length===1?r[0].branch.statePrefix:"required"}]}const s=r[0].branch.group,i=r.filter(n=>n.branch.group===s);return i.length<r.length&&this.report(e,{severity:"warning",code:"overlay-limit",message:`\`${e.name}\` gets constraints from several conditions. Only the first condition is applied.`}),i.map(n=>({condition:n.branch.condition,raw:n.raw,required:n.required,prefix:n.branch.statePrefix}))}derivedNode(e,r,s){if(r===void 0)return{...e,required:s};const i=w({allOf:[e.schema,r]},e.pointer,this.refTrailOf(e),this.environmentFor(e.path));return i===void 0?{...e,required:s}:{...e,schema:i.schema,type:i.type??e.type,required:s}}insertBranchProperties(e,r,s){const i=[...s],n=new Set;for(const[o,a]of r){const[c]=a,u=v(e.path,o);if(!this.isValidPropertyName(o,u,c.pointer))continue;const p=a.some(h=>h.branch.condition===void 0),d=this.createNode(c.raw,{parent:e,name:o,path:u,pointer:c.pointer,required:!p&&a.every(h=>h.required),repeaterDepth:e.repeaterDepth,refTrail:c.branch.schema?.refTrail??this.refTrailOf(e)});if(d===void 0)continue;a.some(h=>!E(h.raw,c.raw)||h.required!==c.required)&&this.report(d,{severity:"warning",code:"branch-schema-conflict",message:`The branches define \`${o}\` differently. The form uses the first definition.`});let f=this.buildNode(d);if(!p&&!ct(a)){const h=[...new Set(a.map(N=>N.branch.condition))];f=re(f,ne(h))}const y=c.branch.reads.map(h=>v(e.path,h));let l=i.reduce((h,N,m)=>y.includes(String(N.path))?m+1:h,i.length);if(l<i.length||y.length>0)for(;l<i.length&&n.has(i[l]);)l++;const b=O(f);b.forEach(h=>n.add(h)),i.splice(l,0,...b)}return i}stateFor(e,r){const s=this.states.get(r);if(s!==void 0)return s;const i=(this.stateCounters.get(e)??0)+1;this.stateCounters.set(e,i);const n=`${e}${String(i).padStart(2,"0")}`;return this.states.set(r,n),n}buildUnionChildren(e,r,s){const{discriminator:i}=s,n=[];let o=!1;for(const a of r)i!==void 0&&a.name===i.property?(n.push(...this.buildSelector(e,s,i)),o=!0):n.push(...O(this.buildNode(a)));return i===void 0?this.report(e,{severity:"warning",code:"no-discriminator",message:`No property tells the \`${s.keyword}\` branches apart (a \`const\` in every branch, or \`discriminator.propertyName\`). Every branch property is rendered, optional and always visible.`}):o||n.push(...this.buildSelector(e,s,i)),n.push(...this.buildUnionProperties(e,s)),n}buildSelector(e,r,s){const{property:i,values:n}=s,o=A(e.schema.properties)[i],a=A(r.branches[0].normalized.schema.properties)[i],c={oneOf:r.branches.map((d,f)=>({const:n[f],title:ut(d,n[f])}))},u=o!==void 0?{allOf:[o,c]}:{title:A(a).title,...c},p=this.createNode(u,{parent:e,name:i,path:v(e.path,i),pointer:this.pointerOf(o)??this.pointerOf(a)??g(e.pointer,"properties",i),required:T(e.schema).includes(i)||r.branches.every(d=>T(d.normalized.schema).includes(i)),repeaterDepth:e.repeaterDepth,refTrail:this.refTrailOf(e)});return p===void 0?[]:O(this.buildNode(p))}buildUnionProperties(e,r){const{discriminator:s}=r,i=A(e.schema.properties),n=new Map;r.branches.forEach((a,c)=>{const u=T(a.normalized.schema);for(const[p,d]of Object.entries(A(a.normalized.schema.properties))){if(p===s?.property)continue;const f=this.pointerOf(d)??g(a.pointer,"properties",p);if(p in i){E(d,i[p])||this.diagnostics.push({severity:"warning",code:"branch-schema-conflict",message:`\`${p}\` is declared next to the union and again in a branch. The declaration next to the union is used.`,path:v(e.path,p),pointer:f});continue}const y=n.get(p)??[];y.push({branchIndex:c,raw:d,required:u.includes(p),pointer:f,refTrail:a.normalized.refTrail}),n.set(p,y)}});const o=[];for(const[a,c]of n){const[u]=c,p=v(e.path,a);if(!this.isValidPropertyName(a,p,u.pointer))continue;const d=this.createNode(u.raw,{parent:e,name:a,path:p,pointer:u.pointer,required:s!==void 0&&u.required,repeaterDepth:e.repeaterDepth,refTrail:u.refTrail});if(d===void 0)continue;c.some(y=>!E(y.raw,u.raw)||y.required!==u.required)&&this.report(d,{severity:"warning",code:"branch-schema-conflict",message:`The branches define \`${a}\` differently. The form uses the first definition.`});let f=this.buildNode(d);if(s!==void 0&&c.length<r.branches.length){const y=K(e,v(e.path,s.property)),l=c.map(b=>s.values[b.branchIndex]);f=re(f,I(y,l))}o.push(...O(f))}return o}objectUnionOf(e,r,s){const i=this.objectUnions.get(e.schema);if(i!==void 0)return i??void 0;let n=null;const o=["oneOf","anyOf"].find(a=>Array.isArray(e.schema[a])&&!M(e.schema[a]));if(o!==void 0){const a=this.environmentFor(s),c=e.schema[o].flatMap((u,p)=>{const d=this.pointerOf(u)??g(r,o,p),f=w(u,d,e.refTrail,a);return f===void 0?[]:[{raw:u,pointer:d,normalized:f}]});if(c.length>0&&c.every(u=>u.normalized.type==="object")){const u=Ze(e.schema,c.map(p=>({schema:p.normalized.schema,ref:pt(p.raw),defName:p.normalized.defName})));n={keyword:o,branches:c,discriminator:u}}}return this.objectUnions.set(e.schema,n),n??void 0}patchesFor(e){const r=this.options.overrides??{},s=e.defName!==void 0?r[`$defs/${e.defName}`]:void 0;return[e.hint,s,r[e.path]].filter(i=>i!==void 0)}namedWidget(e){return[...e].reverse().find(r=>r.widget!==void 0)?.widget}declarativeRuleEntry(e,r){return{name:e.name??r,test:s=>J(e,s),build:(s,i)=>e.skip===!0?null:e.widget!==void 0?this.buildNamedWidget(e.widget,s):i.next(),patch:{label:e.label,props:e.props,validator:e.validator,defaultValue:e.defaultValue,readonly:e.readonly,size:e.size,uid:e.uid}}}childrenOf(e){const r=this.childrenCache.get(e);if(r!==void 0)return r;const s=e.type==="object"?this.propertyNodes(e):e.type==="array"?this.tupleNodes(e):[];return this.childrenCache.set(e,s),s}propertyNodes(e){const r=e.schema.properties;if(r===null||typeof r!="object")return[];const s=Array.isArray(e.schema.required)?e.schema.required:[],i=[];for(const n of this.orderedNames(e,Object.keys(r))){const o=v(e.path,n),a=g(e.pointer,"properties",n);if(!this.isValidPropertyName(n,o,a))continue;const c=this.createNode(r[n],{parent:e,name:n,path:o,pointer:a,required:s.includes(n),repeaterDepth:e.repeaterDepth,refTrail:this.refTrailOf(e)});c!==void 0&&i.push(c)}return i}isValidPropertyName(e,r,s){return e===""||e.includes(".")?(this.diagnostics.push({severity:"error",code:"invalid-property-name",message:`The property name "${e}" cannot be part of a form path, so it is not rendered.`,path:r,pointer:s}),!1):(/^\d+$/.test(e)&&this.diagnostics.push({severity:"warning",code:"numeric-property-name",message:`The form writes "${e}" as an array index, so this object becomes an array.`,path:r,pointer:s}),!0)}orderedNames(e,r){const s=[...this.patchesFor(e)].reverse().find(u=>u.order)?.order??this.declarativeOrder(e);if(s===void 0)return r;const i=s.filter(u=>u!=="*"&&r.includes(u)),n=r.filter(u=>!i.includes(u)),o=s.indexOf("*");if(o===-1)return[...i,...n];const a=s.slice(0,o).filter(u=>i.includes(u)),c=s.slice(o+1).filter(u=>i.includes(u));return[...a,...n,...c]}declarativeOrder(e){for(const r of this.options.rules??[])if(!(!V(r)||r.order===void 0))try{if(J(r,e))return r.order}catch{}}tupleNodes(e){const r=e.schema.prefixItems;if(!Array.isArray(r))return[];const s=typeof e.schema.minItems=="number"?e.schema.minItems:0;return r.flatMap((i,n)=>{const o=this.createNode(i,{parent:e,path:v(e.path,String(n)),pointer:g(e.pointer,"prefixItems",n),required:n<s,repeaterDepth:e.repeaterDepth,refTrail:this.refTrailOf(e)});return o===void 0?[]:[o]})}itemOf(e){if(this.itemCache.has(e))return this.itemCache.get(e);const r=e.schema.items,s=e.type==="array"&&r!==void 0&&r!==!1?this.createNode(r,{parent:e,path:v(e.path,"items"),pointer:g(e.pointer,"items"),required:!1,repeaterDepth:e.repeaterDepth+1,refTrail:this.refTrailOf(e)}):void 0;return this.itemCache.set(e,s),s}createNode(e,r){const s=this.pointerOf(e)??r.pointer,i=w(e,s,r.refTrail,this.environmentFor(r.path));if(i===void 0)return;this.objectUnionOf(i,s,r.path)&&i.type===void 0&&(i.type="object",i.schema.type="object");const n={schema:i.schema,type:i.type,nullable:i.nullable,path:r.path,name:r.name,defName:i.defName,pointer:s,required:r.required,repeaterDepth:r.repeaterDepth,hint:this.hintOf(i,r.path,s),parent:r.parent};return this.normalizedNodes.set(n,i),this.reportUnsupported(n,i),n}hintOf(e,r,s){if(this.vendorKeyword===!1||!(this.vendorKeyword in e.schema))return;const i=e.schema[this.vendorKeyword];if(i!==null&&typeof i=="object"&&!Array.isArray(i))return i;this.diagnostics.push({severity:"warning",code:"invalid-hint",message:`\`${this.vendorKeyword}\` must be an object, so it is ignored.`,path:r,pointer:s})}reportUnsupported(e,r){for(const n of nt)n in e.schema&&this.report(e,{severity:"warning",code:"unsupported-keyword",message:`\`${n}\` has no form equivalent, so the form does not check it.`});const s=e.schema.additionalProperties;s!==null&&typeof s=="object"&&this.report(e,{severity:"warning",code:"unsupported-keyword",message:"No widget edits free-form keys (`additionalProperties` with a schema). Only the declared properties are rendered."});const i=this.objectUnions.get(e.schema);for(const n of["oneOf","anyOf"]){const o=e.schema[n];Array.isArray(o)&&!M(o)&&i?.keyword!==n&&this.report(e,{severity:"warning",code:"unsupported-keyword",message:`\`${n}\` is only supported with object branches, so its branches are not rendered.`})}if(e.type!=="object")for(const n of r.conditionals)this.diagnostics.push({severity:"warning",code:"unsupported-keyword",message:`\`${n.kind}\` is only supported on objects, so it is not applied.`,path:e.path,pointer:n.pointer})}refTrailOf(e){return this.normalizedNodes.get(e)?.refTrail??[]}pointerOf(e){return e!==null&&typeof e=="object"?this.pointers.get(e):void 0}environmentFor(e){return{root:this.refRoot,maxRefDepth:this.maxRefDepth,pointers:this.pointers,report:r=>this.diagnostics.push({...r,path:e})}}countNode(e){return this.builtNodes++,this.builtNodes===this.maxNodes+1&&this.report(e,{severity:"error",code:"max-nodes",message:`The schema has more than ${this.maxNodes} nodes, so the rest is not rendered.`}),this.builtNodes<=this.maxNodes}report(e,r){this.diagnostics.push({...r,path:e.path,pointer:e.pointer})}}function at(t){const e=t.schema.title;return typeof e=="string"&&e!==""?e:t.name!==void 0?R(t.name):void 0}function ne(t){return t.length===1?t[0]:t.map(e=>`(${e})`).join(" || ")}function ct(t){return t.some(e=>e.branch.side==="then"&&t.some(r=>r.branch.group===e.branch.group&&r.branch.side==="else"))}function ut(t,e){const r=t.normalized.schema.title;return typeof r=="string"&&r!==""?r:t.normalized.defName!==void 0?R(t.normalized.defName):typeof e=="string"?R(e):String(e)}function A(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)?t:{}}function T(t){return Array.isArray(t.required)?t.required:[]}function pt(t){const e=A(t).$ref;return typeof e=="string"?e:void 0}function E(t,e){return t===e||JSON.stringify(t)===JSON.stringify(e)}function v(t,e){return t===""?e:`${t}.${e}`}function se(t){return t instanceof Error?t.message:String(t)}exports.fromJsonSchema=st;exports.resolveRef=he;
|
package/json-schema.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./json-schema-Dp0MTcM8.cjs");exports.fromJsonSchema=e.fromJsonSchema;
|
package/json-schema.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { fromJsonSchema } from './lib/json-schema/convert.js';
|
|
2
|
+
export type { BuildContext, BuildResult, ConvertOptions, ConvertResult, DeclarativeRule, Diagnostic, DiagnosticInput, DiagnosticSeverity, EnumOption, FormDefinitionJson, FormWidgetJson, GroupOptions, JsonSchema, JsonType, JsonValue, Localizable, MatchOperator, Preset, Rule, SchemaNode, WidgetBuilder, WidgetPatch, } from './lib/json-schema/types.js';
|
package/json-schema.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An `if` schema written as a reactive expression, or the keyword that has no expression
|
|
3
|
+
* equivalent.
|
|
4
|
+
*/
|
|
5
|
+
export type CompiledCondition = {
|
|
6
|
+
expression: string;
|
|
7
|
+
/** True when a tested property passes by being absent, see {@link compileCondition}. */
|
|
8
|
+
vacuous: boolean;
|
|
9
|
+
/** The top-level property names the condition reads. */
|
|
10
|
+
reads: string[];
|
|
11
|
+
} | {
|
|
12
|
+
unsupported: string;
|
|
13
|
+
};
|
|
14
|
+
/** Reads a property relative to the object that holds the `if`, e.g. `['address', 'country']`. */
|
|
15
|
+
export type ReferenceFor = (segments: string[]) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Compiles the subset of `if` schemas that has an expression equivalent: `true`, `false`,
|
|
18
|
+
* `properties`, `required`, and per property `const`, `enum`, `not` of `const` or `enum`,
|
|
19
|
+
* `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum` and nested `properties`.
|
|
20
|
+
* `type` is not checked, the data has the declared type.
|
|
21
|
+
*
|
|
22
|
+
* As in JSON Schema, a tested property that is not in `required` passes when it is absent,
|
|
23
|
+
* which is `vacuous` in the result.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* compileCondition(
|
|
27
|
+
* { properties: { country: { const: 'US' } }, required: ['country'] },
|
|
28
|
+
* (segments) => `$form.${segments.join('?.')}`,
|
|
29
|
+
* )
|
|
30
|
+
* // { expression: '$form.country === "US"', vacuous: false, reads: ['country'] }
|
|
31
|
+
*/
|
|
32
|
+
export declare function compileCondition(schema: unknown, reference: ReferenceFor): CompiledCondition;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ConvertOptions, ConvertResult, JsonSchema } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a JSON Schema that describes form data into a GolemUI form definition. It is a pure
|
|
4
|
+
* function: use it at runtime with a schema from an API, or at build time to write a form file.
|
|
5
|
+
*
|
|
6
|
+
* For every schema node, the first of these layers that names a widget builds it: a path
|
|
7
|
+
* override, a `$defs` override, the vendor keyword (`x-golemui`) in the schema, the user rules
|
|
8
|
+
* in order, then the preset rules. The other fields of the overrides and the keyword are then
|
|
9
|
+
* merged onto the widget, the most specific last. See {@link ConvertOptions}.
|
|
10
|
+
*
|
|
11
|
+
* The conversion never throws because of the schema shape. What it cannot express exactly is
|
|
12
|
+
* listed in `diagnostics`.
|
|
13
|
+
*
|
|
14
|
+
* @param schema - The JSON Schema of the form data. The root must be an object schema.
|
|
15
|
+
* @param options - The preset of the widget set, plus the optional customization layers.
|
|
16
|
+
* @returns The form definition and the diagnostics.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const { formDefinition, diagnostics } = fromJsonSchema(schema, {
|
|
20
|
+
* preset: guiPreset(),
|
|
21
|
+
* rules: [{ match: { $type: 'string', format: 'email' }, props: { icon: 'mail' } }],
|
|
22
|
+
* overrides: { 'address.street': { widget: 'textarea' } },
|
|
23
|
+
* })
|
|
24
|
+
*/
|
|
25
|
+
export declare function fromJsonSchema(schema: JsonSchema | boolean, options: ConvertOptions): ConvertResult;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DeclarativeRule, Rule, SchemaNode } from './types.js';
|
|
2
|
+
/** True for a {@link DeclarativeRule}, false for a predicate {@link Rule}. */
|
|
3
|
+
export declare function isDeclarativeRule(rule: Rule | DeclarativeRule): rule is DeclarativeRule;
|
|
4
|
+
/**
|
|
5
|
+
* True when every key of `rule.match` holds for the node. See {@link DeclarativeRule}.
|
|
6
|
+
* An invalid `$regex` throws, so the caller reports the rule.
|
|
7
|
+
*/
|
|
8
|
+
export declare function matchesDeclarativeRule(rule: DeclarativeRule, node: SchemaNode): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Matches a dot path against a glob: `*` is exactly one segment, `**` is any number of
|
|
11
|
+
* segments, zero included.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* matchesPathGlob('lines.*.name', 'lines.items.name') // true
|
|
15
|
+
* matchesPathGlob('**.street', 'shipping.address.street') // true
|
|
16
|
+
*/
|
|
17
|
+
export declare function matchesPathGlob(glob: string, path: string): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EnumOption, SchemaNode } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The options of an enumeration node, `undefined` when the node is not one. Two forms count:
|
|
4
|
+
* `enum` (labels from `enumNames` when present, the RJSF convention), and a `oneOf`/`anyOf`
|
|
5
|
+
* whose branches are all `const` (labels from each branch `title`). Other labels are the value
|
|
6
|
+
* in readable form.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // { oneOf: [{ const: 'sm', title: 'Small' }, { const: 'lg' }] }
|
|
10
|
+
* // -> [{ label: 'Small', value: 'sm' }, { label: 'Lg', value: 'lg' }]
|
|
11
|
+
*/
|
|
12
|
+
export declare function enumOptionsOf(node: SchemaNode): EnumOption[] | undefined;
|
|
13
|
+
/** True for a `oneOf`/`anyOf` made only of `const` branches, the labelled enum pattern. */
|
|
14
|
+
export declare function isConstUnion(branches: unknown): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SchemaNode } from './types.js';
|
|
2
|
+
/** A value a condition compares with. */
|
|
3
|
+
export type LiteralValue = string | number | boolean | null;
|
|
4
|
+
/**
|
|
5
|
+
* Writes a value as an expression literal. Core rewrites `.<digit>` as an array index even
|
|
6
|
+
* inside a string literal, so a string is split before such a digit.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* literal('card') // '"card"'
|
|
10
|
+
* literal('v1.2') // '"v1." + "2"'
|
|
11
|
+
*/
|
|
12
|
+
export declare function literal(value: LiteralValue): string;
|
|
13
|
+
/**
|
|
14
|
+
* Writes an expression that reads a path below a scope variable. Every segment after the first
|
|
15
|
+
* uses `?.`, so a missing parent reads as `undefined` instead of throwing. A segment that is not
|
|
16
|
+
* an identifier uses brackets.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* pathExpression('$form', ['payment', 'method']) // '$form.payment?.method'
|
|
20
|
+
* pathExpression('$item', ['unit-price']) // '$item["unit-price"]'
|
|
21
|
+
*/
|
|
22
|
+
export declare function pathExpression(scope: '$form' | '$item', segments: string[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* The innermost array item that holds `node`, the node itself included. Tuple positions do not
|
|
25
|
+
* count, they are fixed fields and not repeated rows.
|
|
26
|
+
*/
|
|
27
|
+
export declare function innermostItemNode(node: SchemaNode): SchemaNode | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Writes an expression that reads a form data path from a condition that belongs to `node`.
|
|
30
|
+
* Inside a repeater row it reads through `$item`, the row the widget is rendered in, so every
|
|
31
|
+
* row sees its own data. Outside a repeater it reads through `$form`.
|
|
32
|
+
*
|
|
33
|
+
* @param node - The node the condition belongs to.
|
|
34
|
+
* @param path - The form data path to read. It must be at or below the row of `node`.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // node path 'payment', outside a repeater:
|
|
38
|
+
* dataReference(node, 'payment.method') // '$form.payment?.method'
|
|
39
|
+
* // node path 'lines.items', the item of the `lines` array:
|
|
40
|
+
* dataReference(node, 'lines.items.method') // '$item.method'
|
|
41
|
+
*/
|
|
42
|
+
export declare function dataReference(node: SchemaNode, path: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* A condition that is true when `reference` equals one of `values`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* equalsAny('$form.kind', ['a', 'b']) // '($form.kind === "a" || $form.kind === "b")'
|
|
48
|
+
*/
|
|
49
|
+
export declare function equalsAny(reference: string, values: LiteralValue[]): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { JsonSchema, JsonType } from './types.js';
|
|
2
|
+
/** One schema taking part in a merge. `allowedTypes` is `undefined` when it allows any type. */
|
|
3
|
+
export type SchemaPart = {
|
|
4
|
+
schema: JsonSchema;
|
|
5
|
+
/** JSON pointer of the part, for conflict reports. */
|
|
6
|
+
pointer: string;
|
|
7
|
+
allowedTypes?: JsonType[];
|
|
8
|
+
};
|
|
9
|
+
/** A keyword whose values the parts cannot both satisfy. The first value is kept. */
|
|
10
|
+
export type MergeConflict = {
|
|
11
|
+
keyword: string;
|
|
12
|
+
pointer: string;
|
|
13
|
+
};
|
|
14
|
+
export type MergedSchema = {
|
|
15
|
+
/** The combined keywords. `type` is not included, see `allowedTypes`. */
|
|
16
|
+
schema: JsonSchema;
|
|
17
|
+
allowedTypes?: JsonType[];
|
|
18
|
+
conflicts: MergeConflict[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Combines schema parts that all apply to the same value, as `allOf` requires. The first part
|
|
22
|
+
* wins for annotations such as `title` and for every keyword with no rule below, so the part
|
|
23
|
+
* that holds the `allOf` or the `$ref` goes first.
|
|
24
|
+
*
|
|
25
|
+
* - `type`: intersected, `number` includes `integer`.
|
|
26
|
+
* - `required`: union. `enum`: intersection. `uniqueItems`: true when any part says so.
|
|
27
|
+
* - Lower bounds take the highest value, upper bounds the lowest.
|
|
28
|
+
* - `properties`, `patternProperties`: a property in several parts becomes `{ allOf: [...] }`,
|
|
29
|
+
* merged later when that property is normalized. The same for `items`.
|
|
30
|
+
* - `const`, `pattern`, `format` and the other keywords in `MUST_AGREE` keep the first value and
|
|
31
|
+
* report a conflict when the parts differ.
|
|
32
|
+
*
|
|
33
|
+
* @param parts - The parts, the one that holds the `allOf` or the `$ref` first.
|
|
34
|
+
* @param pointers - Document pointers of schema objects. A new `allOf` wrapper gets the pointer
|
|
35
|
+
* of its first member.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mergeSchemaParts(parts: SchemaPart[], pointers: WeakMap<object, string>): MergedSchema;
|
|
38
|
+
/**
|
|
39
|
+
* The types a schema declares, with `integer` added wherever `number` is, so an intersection
|
|
40
|
+
* with `integer` keeps it. `undefined` when the schema declares no type.
|
|
41
|
+
*/
|
|
42
|
+
export declare function declaredTypes(schema: JsonSchema): JsonType[] | undefined;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { DiagnosticSeverity, JsonSchema, JsonType } from './types.js';
|
|
2
|
+
/** A conditional keyword of a node or of one of its `allOf` parts, compiled later by the walker. */
|
|
3
|
+
export type Conditional = {
|
|
4
|
+
kind: 'if';
|
|
5
|
+
if: unknown;
|
|
6
|
+
then?: unknown;
|
|
7
|
+
else?: unknown;
|
|
8
|
+
pointer: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'dependentRequired';
|
|
11
|
+
property: string;
|
|
12
|
+
required: string[];
|
|
13
|
+
pointer: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'dependentSchemas';
|
|
16
|
+
property: string;
|
|
17
|
+
schema: unknown;
|
|
18
|
+
pointer: string;
|
|
19
|
+
};
|
|
20
|
+
/** One schema node after normalization. */
|
|
21
|
+
export type NormalizedNode = {
|
|
22
|
+
/**
|
|
23
|
+
* The combined schema. `$ref`, `allOf`, the conditional keywords and `null` types are gone,
|
|
24
|
+
* older draft keywords use their 2020-12 form, and `type` holds the single resolved type.
|
|
25
|
+
*/
|
|
26
|
+
schema: JsonSchema;
|
|
27
|
+
type?: JsonType;
|
|
28
|
+
nullable: boolean;
|
|
29
|
+
/** The definition name of the `$ref` the node came from, e.g. `Address`. */
|
|
30
|
+
defName?: string;
|
|
31
|
+
conditionals: Conditional[];
|
|
32
|
+
/** The `$ref` values followed from the root down to this node, for the recursion limit. */
|
|
33
|
+
refTrail: string[];
|
|
34
|
+
/** The types the schema allows, `undefined` when it allows any type. */
|
|
35
|
+
allowedTypes?: JsonType[];
|
|
36
|
+
};
|
|
37
|
+
/** A problem found while normalizing. The walker adds the form data path. */
|
|
38
|
+
export type NormalizeProblem = {
|
|
39
|
+
severity: DiagnosticSeverity;
|
|
40
|
+
code: string;
|
|
41
|
+
message: string;
|
|
42
|
+
pointer: string;
|
|
43
|
+
};
|
|
44
|
+
export type NormalizeEnvironment = {
|
|
45
|
+
/** The document `$ref` values resolve against. */
|
|
46
|
+
root: JsonSchema;
|
|
47
|
+
/** How many times one `$ref` may repeat in a trail. */
|
|
48
|
+
maxRefDepth: number;
|
|
49
|
+
report(problem: NormalizeProblem): void;
|
|
50
|
+
/**
|
|
51
|
+
* Document pointers of the schema objects seen so far, so a child found through a merge or a
|
|
52
|
+
* `$ref` can still point diagnostics at its place in the input.
|
|
53
|
+
*/
|
|
54
|
+
pointers: WeakMap<object, string>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes one schema node, see {@link NormalizedNode}. Only the node itself is resolved:
|
|
58
|
+
* its `$ref`, its `allOf` parts and a nullable `oneOf`/`anyOf` pair. Properties and items stay
|
|
59
|
+
* as they are, and the walker normalizes them when it reaches them.
|
|
60
|
+
*
|
|
61
|
+
* @param raw - The schema as written in the input, a schema object or a boolean.
|
|
62
|
+
* @param pointer - JSON pointer of `raw` in the input document.
|
|
63
|
+
* @param refTrail - The `$ref` values followed from the root down to the parent.
|
|
64
|
+
* @returns The normalized node, or `undefined` when the node is not rendered: a `false` schema,
|
|
65
|
+
* a `$ref` that cannot be resolved or repeats too often, or a value that is not a schema. Each
|
|
66
|
+
* case except `false` is reported.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const node = normalizeNode({ $ref: '#/$defs/Address', title: 'Shipping' }, '/properties/shipping', [], environment);
|
|
70
|
+
* node.defName // 'Address'
|
|
71
|
+
* node.schema.title // 'Shipping', the keywords next to `$ref` win
|
|
72
|
+
*/
|
|
73
|
+
export declare function normalizeNode(raw: unknown, pointer: string, refTrail: readonly string[], environment: NormalizeEnvironment): NormalizedNode | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { JsonSchema } from './types.js';
|
|
2
|
+
/** Escapes one JSON pointer segment (RFC 6901): `~` becomes `~0` and `/` becomes `~1`. */
|
|
3
|
+
export declare function escapePointerSegment(segment: string): string;
|
|
4
|
+
/** Reverses {@link escapePointerSegment}. `~1` is replaced first, as RFC 6901 requires. */
|
|
5
|
+
export declare function unescapePointerSegment(segment: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Appends segments to a JSON pointer.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* appendPointer('/properties', 'a/b'); // '/properties/a~1b'
|
|
11
|
+
*/
|
|
12
|
+
export declare function appendPointer(pointer: string, ...segments: (string | number)[]): string;
|
|
13
|
+
/** A resolved `$ref`: the target and its JSON pointer in the root document, or why it failed. */
|
|
14
|
+
export type ResolvedRef = {
|
|
15
|
+
target: unknown;
|
|
16
|
+
pointer: string;
|
|
17
|
+
} | {
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a `$ref` inside the root document: `#`, or a `#/...` JSON pointer with percent
|
|
22
|
+
* encoding and `~0`/`~1` escapes. A ref that starts with the root `$id` is local too.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* resolveRef('#/$defs/Address', schema); // { target: schema.$defs.Address, pointer: '/$defs/Address' }
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveRef(ref: string, root: JsonSchema): ResolvedRef;
|
|
28
|
+
/**
|
|
29
|
+
* The definition name a JSON pointer points to, or `undefined` when it does not point to a
|
|
30
|
+
* `$defs`, `definitions` or OpenAPI `components/schemas` entry.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* definitionNameOf('/components/schemas/User'); // 'User'
|
|
34
|
+
* definitionNameOf('/$defs/User/properties/name'); // undefined
|
|
35
|
+
*/
|
|
36
|
+
export declare function definitionNameOf(pointer: string): string | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a property name or an enum value into a label: `_` and `-` become spaces, camelCase is
|
|
3
|
+
* split, and the first letter is upper case. The same rule as the earlier gui-mcp mapper, so
|
|
4
|
+
* labels made from names do not change.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* humanize('firstName') // 'First Name'
|
|
8
|
+
* humanize('zip_code') // 'Zip code'
|
|
9
|
+
* humanize('US') // 'US'
|
|
10
|
+
*/
|
|
11
|
+
export declare function humanize(name: string): string;
|