@golemui/schemas 1.6.0-rc.1 → 2.0.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 CHANGED
@@ -1,3 +1,93 @@
1
+ ## 2.0.0-rc.0 (2026-10-08)
2
+
3
+ ### 🚀 Features
4
+
5
+ - ⚠️ **gui-components:** Make @golemui/gui-components a standalone project ([#406](https://github.com/golemui/golemui/pull/406))
6
+
7
+ ### ⚠️ Breaking Changes
8
+
9
+ - **gui-components:** Make @golemui/gui-components a standalone project ([#406](https://github.com/golemui/golemui/pull/406))
10
+ Deprecate Flex layout
11
+ * fix(gui-components): add migration guide to v2.0
12
+ BREAKING CHANGE: GolemUI 2.0 makes @golemui/gui-components a standalone project.
13
+ - Styles: index.css no longer includes the form container, repeater and
14
+ invalid submit button styles. Import @golemui/gui-shared/forms.css after it.
15
+ - Styles: every rule is in a golemui.* cascade layer, so unlayered app CSS,
16
+ including global resets, now overrides component styles.
17
+ - Styles: component selectors are no longer nested under .gui-form, and
18
+ narrow layouts query the gui-container container instead of gui-form.
19
+ - Layouts: a grid without direction is now a stack instead of a row. Add
20
+ direction: 'row' to keep a row.
21
+ - Layouts: flex is deprecated and renders as a grid. Grid gaps are tokens
22
+ (none to xl) instead of pixels, justify places children along a row, and
23
+ autoFit, columnGap, rowGap and align are deprecated. The root layout is grid.
24
+ - Markup: tabs, accordions and alerts render gui-tabs, gui-accordion and
25
+ gui-alert. Their old classes (.gui-accordion__section,
26
+ .gui-alert-notification--*, .active) are gone, as are the .gui-flex and
27
+ .gui-grid__widget classes. Errors render as divs instead of a ul.
28
+ - Elements: every event is prefixed with gui- (input/change -> gui-input and
29
+ gui-change, blur -> gui-blur, inputError -> gui-input-error, and so on),
30
+ bubbles and is composed.
31
+ - Elements: multi-word attributes are kebab-case (autogrow -> auto-grow).
32
+ - Elements: gui-button's actionType is now type, and invalid is removed.
33
+ - Elements: touched is unset by default, so errors show as soon as they are set.
34
+ - Packages: @golemui/gui-components only peers on lit, and @golemui/gui-shared
35
+ now depends on it. @golemui/gui-components/internals no longer exports
36
+ createIntersectionObserver or NO_AVAILABLE_TIMES_MESSAGE.
37
+ * chore: update gitignore
38
+ * refactor(lit): move ssr to lit-utils
39
+ move ssr to lit-utils
40
+ * docs: update readme
41
+ * refactor(lit): move ssr to lit-utils, add ssr to components
42
+ * fix(gui-components): report missing upload service when dependencies are set
43
+ * fix(gui-components): avoid duplicated ids in all pickers
44
+ * fix(gui-components): set caret icon as a CSS property
45
+ * test: check ssr parity across frameworks with components
46
+ * ci: add workflow to test lit-labs/ssr against our implementation
47
+ * feat(gui-components): add timeLabel prop to date-time inputs
48
+ * chore: fix lint errors
49
+ * chore: improve ssr performance
50
+ * docs: move components ssr docs to website
51
+ * fix(gui-components): send value in numeric inputs
52
+ * feat(gui-components): add nitro plugin for nuxt users
53
+ * fix(gui-components): avoid ssr rendering for time lists
54
+ * chore: reset harnesses
55
+ * docs: update components readme
56
+ * fix(gui-components): misc issues related with required, aria and styling
57
+ * test(gui-components): fix tests
58
+ * test(gui-components): fix firefox test failing by 0.000001
59
+ * fix(gui-components): fix layout issues with pickers
60
+ * fix(schemas): make grid a the reserved root
61
+ * fix(schemas): make grid a the reserved root
62
+ * docs: fix astro template, update migration guide
63
+ * fix(gui-components): avoid double hints in pickers
64
+ * fix(gui-react): fix singleOpen in react accordion
65
+ * fix(gui-components): fix currency stepping
66
+ * fix(gui-components): reset value on form reset
67
+ * fix(gui-components): Segmented date and time inputs keep an outdated validity
68
+ * fix(gui-components): block pickers submissions with partly typed or impossible dates
69
+ * fix(gui-components): Dates and times outside their bounds now block a native submit
70
+ * fix(gui-components): block native submit with pending uploads in progress
71
+ * test: fix typing issues with specs
72
+
73
+ ### ❤️ Thank You
74
+
75
+ - Raúl Jiménez @Elecash
76
+
77
+ ## 1.6.0 (2026-10-03)
78
+
79
+ ### 🚀 Features
80
+
81
+ - **schemas:** convert a JSON Schema into a form definition ([#402](https://github.com/golemui/golemui/pull/402))
82
+
83
+ ### ❤️ Thank You
84
+
85
+ - Mud Scientist @mudscientist
86
+
87
+ ## 1.6.0-rc.1 (2026-10-02)
88
+
89
+ This was a version bump only for schemas to align it with other projects, there were no code changes.
90
+
1
91
  ## 1.6.0-rc.0 (2026-10-02)
2
92
 
3
93
  ### 🚀 Features
package/README.md CHANGED
@@ -114,7 +114,7 @@ npx @golemui/schemas generate
114
114
  ```
115
115
 
116
116
  `init` writes `schemas.config.mjs` (the manifest and config), a starter validators schema, a
117
- starter `flex` and example input component schema, an example form and a test skeleton, then
117
+ starter `grid` and example input component schema, an example form and a test skeleton, then
118
118
  runs `generate`. Run it with no flags for prompts.
119
119
 
120
120
  `schemas.config.mjs` and the component schemas are the implementer's to edit. Everything
package/cli.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- "use strict";const d=require("node:fs"),s=require("node:path"),N=require("node:readline/promises"),w=require("node:url"),M=require("./generate-implementation-schemas-D57DzM2J.cjs"),z=require("node:module"),T=require("./json-schema-Dp0MTcM8.cjs"),R=["json-schema.config.mjs","json-schema.config.json"];async function k(e,t){const r=process.cwd(),{config:o,baseDir:i}=await L(h(e,"config"),r),a=U(h(e,"fail-on")),l=t[0]?s.resolve(r,t[0]):o.input!==void 0?s.resolve(i,o.input):void 0;if(l===void 0)throw new Error("Missing the JSON Schema file. Pass it as `convert <schema.json>`, or set `input` in the config.");const c=h(e,"out"),n=c?s.resolve(r,c):o.output!==void 0?s.resolve(i,o.output):void 0;if(n!==void 0&&d.existsSync(n)&&e.get("force")!==!0)throw new Error(`${s.relative(r,n)} exists. A generated form is often edited by hand, so it is only replaced with --force.`);const f=_(l),v=h(e,"pointer")??o.pointer,b=h(e,"preset"),O=await D(b??o.preset,o.presetOptions,b!==void 0?r:i),{formDefinition:A,diagnostics:x}=T.fromJsonSchema(v===void 0?f:F(f,v),{preset:O,rules:o.rules,overrides:o.overrides,refRoot:f,vendorKeyword:o.vendorKeyword,maxRefDepth:o.maxRefDepth,maxNodes:o.maxNodes,transform:o.transform}),j=`${JSON.stringify(A,null,2)}
3
- `;n===void 0?process.stdout.write(j):(d.mkdirSync(s.dirname(n),{recursive:!0}),d.writeFileSync(n,j,"utf-8"),console.log(`Wrote ${s.relative(r,n)}`));for(const $ of x)console.error(V($));return x.some($=>a.includes($.severity))?1:0}async function L(e,t){const r=e!==void 0?s.resolve(t,e):R.map(i=>s.join(t,i)).find(i=>d.existsSync(i));if(r===void 0)return{config:{},baseDir:t};if(!d.existsSync(r))throw new Error(`Config file ${e} not found.`);const o=r.endsWith(".json")?_(r):(await import(w.pathToFileURL(r).href)).default;return{config:C(o,s.relative(t,r)),baseDir:s.dirname(r)}}const S={input:e=>typeof e=="string",output:e=>typeof e=="string",pointer:e=>typeof e=="string",preset:e=>typeof e=="string"||W(e),presetOptions:m,rules:e=>Array.isArray(e)&&e.every(m),overrides:e=>m(e)&&Object.values(e).every(m),vendorKeyword:e=>typeof e=="string"||e===!1,maxRefDepth:E,maxNodes:E,transform:e=>typeof e=="function"};function C(e,t){if(!m(e))throw new Error(`${t} must hold a configuration object (the default export of a .mjs).`);for(const[r,o]of Object.entries(e)){const i=S[r];if(i===void 0)throw new Error(`${t}: unknown field \`${r}\`. The fields are ${Object.keys(S).join(", ")}.`);if(o!==void 0&&!i(o))throw new Error(`${t}: \`${r}\` has the wrong type.`)}return e}async function D(e,t,r){if(e===void 0)throw new Error("No preset. Pass --preset with the preset module of a widget set, e.g. --preset @golemui/gui-schemas/json-schema, or set `preset` in the config.");if(typeof e!="string")return e;let o;try{o=z.createRequire(s.join(r,"noop.js")).resolve(e)}catch{throw new Error(`Cannot find the preset module "${e}" from ${r}. Is it installed?`)}const i=await import(w.pathToFileURL(o).href),a=i.preset??i.default?.preset;if(typeof a!="function")throw new Error(`"${e}" has no \`preset\` export. A preset module exports \`preset(options)\`, which returns the preset.`);return a(t??{})}function F(e,t){const r=T.resolveRef(`#${t.replace(/^#/,"")}`,e);if("error"in r)throw new Error(`--pointer ${t}: ${r.error}`);if(!m(r.target))throw new Error(`--pointer ${t} does not point to a schema object.`);return r.target}function _(e){if(!d.existsSync(e))throw new Error(`${e} not found.`);try{return JSON.parse(d.readFileSync(e,"utf-8"))}catch(t){throw new Error(`${e} is not valid JSON: ${t instanceof Error?t.message:t}`)}}function U(e){switch(e){case void 0:return[];case"error":return["error"];case"warning":return["error","warning"];default:throw new Error(`Invalid --fail-on "${e}". Use error or warning.`)}}function V(e){const t=`${e.path===""?"(root)":e.path} (${e.pointer||"/"})`;return`${e.severity} ${e.code} at ${t}: ${e.message}`}function h(e,t){const r=e.get(t);return typeof r=="string"?r:void 0}function W(e){return m(e)&&Array.isArray(e.rules)&&typeof e.root=="function"}function E(e){return typeof e=="number"&&Number.isInteger(e)&&e>0}function m(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}const B="../src/lib/form.editor.schema.json",J=[{kind:"layout",type:"flex",uid:"#root",props:{direction:"column",gap:16},children:[{kind:"input",type:"__IMPLEMENTATION__-input",uid:"email",path:"email",label:"Email address",validator:{type:"string",required:!0,format:"email"},props:{placeholder:"you@example.com"}}]}],G={$schema:B,form:J},Y="https://json-schema.org/draft/2020-12/schema",K="__ID_BASE__components/example-input.schema.json",H="STARTER TEMPLATE, owned by you. One component schema per widget type. Copy this file for each widget, then list it in schemas.config.mjs. The pattern to keep: allOf the baseWidget, `kind` and `type` consts, one `$defs` entry per prop, a `.state` suffixed pattern for every suffixable key, `additionalProperties: false` inside `props`, and `unevaluatedProperties: false` at the root.",Z="__IMPLEMENTATION__ example input widget",Q="object",X={labelDef:{$ref:"../core/common.schema.json#/$defs/localizable"},disabledDef:{$ref:"../core/common.schema.json#/$defs/boolOrWhen"},readonlyDef:{$ref:"../core/common.schema.json#/$defs/boolOrWhen"},hintProp:{type:"string"},placeholderProp:{type:"string"},maxlengthProp:{type:"number"}},ee=[{$ref:"../core/common.schema.json#/$defs/baseWidget"}],te={kind:{const:"input"},type:{const:"__IMPLEMENTATION__-input"},path:{$ref:"../core/common.schema.json#/$defs/dotPath"},label:{$ref:"#/$defs/labelDef"},disabled:{$ref:"#/$defs/disabledDef"},readonly:{$ref:"#/$defs/readonlyDef"},on:{$ref:"../core/common.schema.json#/$defs/on"},validator:{$ref:"../validators.schema.json#/$defs/validator"},defaultValue:{type:"string"},props:{type:"object",properties:{hint:{$ref:"#/$defs/hintProp"},placeholder:{$ref:"#/$defs/placeholderProp"},maxlength:{$ref:"#/$defs/maxlengthProp"}},patternProperties:{"^hint\\.[^.]+$":{$ref:"#/$defs/hintProp"},"^placeholder\\.[^.]+$":{$ref:"#/$defs/placeholderProp"},"^maxlength\\.[^.]+$":{$ref:"#/$defs/maxlengthProp"}},additionalProperties:!1}},re={"^label\\.[^.]+$":{$ref:"#/$defs/labelDef"},"^disabled\\.[^.]+$":{$ref:"#/$defs/disabledDef"},"^readonly\\.[^.]+$":{$ref:"#/$defs/readonlyDef"},"^validator\\.[^.]+$":{$ref:"../validators.schema.json#/$defs/validator"}},oe=["kind","type","path"],ie=!1,se={$schema:Y,$id:K,$comment:H,title:Z,type:Q,$defs:X,allOf:ee,properties:te,patternProperties:re,required:oe,unevaluatedProperties:ie},ne="https://json-schema.org/draft/2020-12/schema",ae="__ID_BASE__components/flex.schema.json",le="STARTER TEMPLATE, owned by you. `flex` is a reserved widget type: keep the name and the `children` array, change the props to match your layout widget.",de="__IMPLEMENTATION__ flex widget",ce="object",me={directionProp:{enum:["row","row-reverse","column","column-reverse"]},justifyProp:{enum:["start","center","end","stretch","space-between","space-around"]},alignProp:{enum:["start","center","end","stretch","baseline"]},wrapProp:{type:"boolean"},gapProp:{type:"number"},paddingProp:{type:"number"}},pe=[{$ref:"../core/common.schema.json#/$defs/baseWidget"}],fe={kind:{const:"layout"},type:{const:"flex"},children:{type:"array",minItems:1,items:{$ref:"../widgets.schema.json#/$defs/formWidget"}},props:{type:"object",properties:{direction:{$ref:"#/$defs/directionProp"},justify:{$ref:"#/$defs/justifyProp"},align:{$ref:"#/$defs/alignProp"},wrap:{$ref:"#/$defs/wrapProp"},gap:{$ref:"#/$defs/gapProp"},padding:{$ref:"#/$defs/paddingProp"}},patternProperties:{"^direction\\.[^.]+$":{$ref:"#/$defs/directionProp"},"^justify\\.[^.]+$":{$ref:"#/$defs/justifyProp"},"^align\\.[^.]+$":{$ref:"#/$defs/alignProp"},"^wrap\\.[^.]+$":{$ref:"#/$defs/wrapProp"},"^gap\\.[^.]+$":{$ref:"#/$defs/gapProp"},"^padding\\.[^.]+$":{$ref:"#/$defs/paddingProp"}},additionalProperties:!1}},he=["kind","type","children"],ue=!1,ye={$schema:ne,$id:ae,$comment:le,title:de,type:ce,$defs:me,allOf:pe,properties:fe,required:he,unevaluatedProperties:ue},$e="https://json-schema.org/draft/2020-12/schema",ge="__ID_BASE__validators.schema.json",we="STARTER TEMPLATE, owned by you. Edit it freely. The only contract is that it exposes #/$defs/validator, which every component schema references. Copied from the golemui gui validators when the tree was scaffolded, it does not track them afterwards.",ve="__IMPLEMENTATION__ validator definitions",be={localizable:{$ref:"./core/common.schema.json#/$defs/localizable"},stringValidator:{type:"object",properties:{type:{const:"string"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is empty or absent"},minLength:{type:"number",description:"Minimum number of characters the string must contain"},maxLength:{type:"number",description:"Maximum number of characters the string may contain"},pattern:{type:"string",description:"A regular expression pattern the value must match"},format:{type:"string",description:"A named format the string must conform to (e.g. `email`, `url`, `date`, `uuid`)",enum:["email","hostname","ipv4","ipv6","url","uuid","date","time","date-time","duration"]},const:{description:"The string must equal exactly this value"},enum:{type:"array",description:"The string must be one of these allowed values"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},required:{$ref:"#/$defs/localizable",description:"Shown when a required field is empty"},minLength:{$ref:"#/$defs/localizable",description:"Shown when the value is shorter than `minLength`"},maxLength:{$ref:"#/$defs/localizable",description:"Shown when the value is longer than `maxLength`"},pattern:{$ref:"#/$defs/localizable",description:"Shown when the value does not match the `pattern` regex"},format:{$ref:"#/$defs/localizable",description:"Shown when the value does not conform to the specified `format`"},enum:{$ref:"#/$defs/localizable",description:"Shown when the value is not in the `enum` list"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},numberValidator:{type:"object",properties:{type:{type:"string",enum:["number","integer"]},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is absent"},minimum:{type:"number",description:"The value must be greater than or equal to this number (inclusive)"},maximum:{type:"number",description:"The value must be less than or equal to this number (inclusive)"},exclusiveMinimum:{type:"number",description:"The value must be strictly greater than this number (exclusive)"},exclusiveMaximum:{type:"number",description:"The value must be strictly less than this number (exclusive)"},multipleOf:{type:"number",description:"The value must be a multiple of this number"},const:{description:"The value must equal exactly this number"},enum:{type:"array",description:"The value must be one of these allowed numbers"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},minimum:{$ref:"#/$defs/localizable",description:"Shown when the value is below `minimum`"},maximum:{$ref:"#/$defs/localizable",description:"Shown when the value exceeds `maximum`"},exclusiveMinimum:{$ref:"#/$defs/localizable",description:"Shown when the value is not strictly above `exclusiveMinimum`"},exclusiveMaximum:{$ref:"#/$defs/localizable",description:"Shown when the value is not strictly below `exclusiveMaximum`"},multipleOf:{$ref:"#/$defs/localizable",description:"Shown when the value is not a multiple of `multipleOf`"},enum:{$ref:"#/$defs/localizable",description:"Shown when the value is not in the `enum` list"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},booleanValidator:{type:"object",properties:{type:{const:"boolean"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is absent or false (e.g. an unchecked required checkbox)"},const:{description:"The value must equal exactly this boolean (e.g. `true` to require acceptance)"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},arrayValidator:{type:"object",properties:{type:{const:"array"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the array is empty or absent"},minItems:{type:"number",description:"The array must contain at least this many items"},maxItems:{type:"number",description:"The array must contain no more than this many items"},uniqueItems:{type:"boolean",description:"When true, all items in the array must be distinct values"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},required:{$ref:"#/$defs/localizable",description:"Shown when a required array is empty or absent"},minItems:{$ref:"#/$defs/localizable",description:"Shown when the array has fewer items than `minItems`"},maxItems:{$ref:"#/$defs/localizable",description:"Shown when the array has more items than `maxItems`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},fileValidator:{type:"object",description:"Validates the file envelope stored by the `fileUpload` widget (`{ id, name, size, type, status, error?, data? }` or `null`).",properties:{type:{const:"file"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if no file is set"},blockPendingUploads:{type:"boolean",description:"When true (the default), validation fails while the file's status is `uploading` or `error`, so an unfinished upload cannot be submitted"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value is not a file envelope"},required:{$ref:"#/$defs/localizable",description:"Shown when a required field has no file"},pendingUploads:{$ref:"#/$defs/localizable",description:"Shown while the file is still uploading or failed to upload"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},filesValidator:{type:"object",description:"Validates the array of file envelopes stored by the `multiFileUpload` widget.",properties:{type:{const:"files"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the array is empty or absent"},minItems:{type:"number",description:"The array must contain at least this many files"},maxItems:{type:"number",description:"The array must contain no more than this many files"},blockPendingUploads:{type:"boolean",description:"When true (the default), validation fails while any file's status is `uploading` or `error`, so an unfinished upload cannot be submitted"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value is not an array of file envelopes"},required:{$ref:"#/$defs/localizable",description:"Shown when a required array is empty or absent"},minItems:{$ref:"#/$defs/localizable",description:"Shown when the array has fewer files than `minItems`"},maxItems:{$ref:"#/$defs/localizable",description:"Shown when the array has more files than `maxItems`"},pendingUploads:{$ref:"#/$defs/localizable",description:"Shown while any file is still uploading or failed to upload"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},customValidator:{type:"object",description:"A custom validator that delegates validation to application code. The registered validator function receives these constraint keys and must return a Standard Schema V1-compliant schema object (https://standardschema.dev). Add arbitrary constraint keys alongside `type` - they are passed as-is to the registered validator function.",properties:{type:{const:"custom"},required:{type:"boolean",description:"Makes the field mandatory - evaluation is still delegated to the custom validator"}},required:["type"],additionalProperties:!0},validator:{description:"Validation rules for this field. The `type` discriminant selects the validator: `string`, `number`, `integer`, `boolean`, `array`, `file`, `files`, or `custom`.",oneOf:[{$ref:"#/$defs/stringValidator"},{$ref:"#/$defs/numberValidator"},{$ref:"#/$defs/booleanValidator"},{$ref:"#/$defs/arrayValidator"},{$ref:"#/$defs/fileValidator"},{$ref:"#/$defs/filesValidator"},{$ref:"#/$defs/customValidator"}]}},xe={$schema:$e,$id:ge,$comment:we,title:ve,$defs:be};function I(e){return`${e}-input`}function je(e,t){const r=e.replace(/__IMPLEMENTATION__/g,t.implementation).replace(/__ID_BASE__/g,t.idBase),o=r.match(/__[A-Z_]+__/);if(o!==null)throw new Error(`Unsubstituted placeholder ${o[0]} in a starter template.`);return r}function u(e,t){return je(JSON.stringify(e,null,2),t)+`
4
- `}function Se(e){return{"schemas.config.mjs":Ee(e),"src/lib/validators.schema.json":u(xe,e),"src/lib/components/flex.schema.json":u(ye,e),"src/lib/components/example-input.schema.json":u(se,e),"examples/example.form.json":u(G,e),"test/schemas.spec.ts":Pe(e)}}function Ee(e){return`// The manifest and configuration for this implementation's JSON schema tree.
2
+ "use strict";const d=require("node:fs"),s=require("node:path"),N=require("node:readline/promises"),w=require("node:url"),M=require("./generate-implementation-schemas-D57DzM2J.cjs"),z=require("node:module"),T=require("./json-schema-Dp0MTcM8.cjs"),R=["json-schema.config.mjs","json-schema.config.json"];async function k(e,t){const r=process.cwd(),{config:o,baseDir:i}=await L(h(e,"config"),r),a=U(h(e,"fail-on")),l=t[0]?s.resolve(r,t[0]):o.input!==void 0?s.resolve(i,o.input):void 0;if(l===void 0)throw new Error("Missing the JSON Schema file. Pass it as `convert <schema.json>`, or set `input` in the config.");const c=h(e,"out"),n=c?s.resolve(r,c):o.output!==void 0?s.resolve(i,o.output):void 0;if(n!==void 0&&d.existsSync(n)&&e.get("force")!==!0)throw new Error(`${s.relative(r,n)} exists. A generated form is often edited by hand, so it is only replaced with --force.`);const f=_(l),v=h(e,"pointer")??o.pointer,b=h(e,"preset"),q=await D(b??o.preset,o.presetOptions,b!==void 0?r:i),{formDefinition:A,diagnostics:x}=T.fromJsonSchema(v===void 0?f:F(f,v),{preset:q,rules:o.rules,overrides:o.overrides,refRoot:f,vendorKeyword:o.vendorKeyword,maxRefDepth:o.maxRefDepth,maxNodes:o.maxNodes,transform:o.transform}),j=`${JSON.stringify(A,null,2)}
3
+ `;n===void 0?process.stdout.write(j):(d.mkdirSync(s.dirname(n),{recursive:!0}),d.writeFileSync(n,j,"utf-8"),console.log(`Wrote ${s.relative(r,n)}`));for(const g of x)console.error(V(g));return x.some(g=>a.includes(g.severity))?1:0}async function L(e,t){const r=e!==void 0?s.resolve(t,e):R.map(i=>s.join(t,i)).find(i=>d.existsSync(i));if(r===void 0)return{config:{},baseDir:t};if(!d.existsSync(r))throw new Error(`Config file ${e} not found.`);const o=r.endsWith(".json")?_(r):(await import(w.pathToFileURL(r).href)).default;return{config:C(o,s.relative(t,r)),baseDir:s.dirname(r)}}const S={input:e=>typeof e=="string",output:e=>typeof e=="string",pointer:e=>typeof e=="string",preset:e=>typeof e=="string"||W(e),presetOptions:m,rules:e=>Array.isArray(e)&&e.every(m),overrides:e=>m(e)&&Object.values(e).every(m),vendorKeyword:e=>typeof e=="string"||e===!1,maxRefDepth:E,maxNodes:E,transform:e=>typeof e=="function"};function C(e,t){if(!m(e))throw new Error(`${t} must hold a configuration object (the default export of a .mjs).`);for(const[r,o]of Object.entries(e)){const i=S[r];if(i===void 0)throw new Error(`${t}: unknown field \`${r}\`. The fields are ${Object.keys(S).join(", ")}.`);if(o!==void 0&&!i(o))throw new Error(`${t}: \`${r}\` has the wrong type.`)}return e}async function D(e,t,r){if(e===void 0)throw new Error("No preset. Pass --preset with the preset module of a widget set, e.g. --preset @golemui/gui-schemas/json-schema, or set `preset` in the config.");if(typeof e!="string")return e;let o;try{o=z.createRequire(s.join(r,"noop.js")).resolve(e)}catch{throw new Error(`Cannot find the preset module "${e}" from ${r}. Is it installed?`)}const i=await import(w.pathToFileURL(o).href),a=i.preset??i.default?.preset;if(typeof a!="function")throw new Error(`"${e}" has no \`preset\` export. A preset module exports \`preset(options)\`, which returns the preset.`);return a(t??{})}function F(e,t){const r=T.resolveRef(`#${t.replace(/^#/,"")}`,e);if("error"in r)throw new Error(`--pointer ${t}: ${r.error}`);if(!m(r.target))throw new Error(`--pointer ${t} does not point to a schema object.`);return r.target}function _(e){if(!d.existsSync(e))throw new Error(`${e} not found.`);try{return JSON.parse(d.readFileSync(e,"utf-8"))}catch(t){throw new Error(`${e} is not valid JSON: ${t instanceof Error?t.message:t}`)}}function U(e){switch(e){case void 0:return[];case"error":return["error"];case"warning":return["error","warning"];default:throw new Error(`Invalid --fail-on "${e}". Use error or warning.`)}}function V(e){const t=`${e.path===""?"(root)":e.path} (${e.pointer||"/"})`;return`${e.severity} ${e.code} at ${t}: ${e.message}`}function h(e,t){const r=e.get(t);return typeof r=="string"?r:void 0}function W(e){return m(e)&&Array.isArray(e.rules)&&typeof e.root=="function"}function E(e){return typeof e=="number"&&Number.isInteger(e)&&e>0}function m(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}const B="../src/lib/form.editor.schema.json",J=[{kind:"layout",type:"grid",uid:"#root",props:{gap:"md"},children:[{kind:"input",type:"__IMPLEMENTATION__-input",uid:"email",path:"email",label:"Email address",validator:{type:"string",required:!0,format:"email"},props:{placeholder:"you@example.com"}}]}],G={$schema:B,form:J},K="https://json-schema.org/draft/2020-12/schema",Y="__ID_BASE__components/example-input.schema.json",H="STARTER TEMPLATE, owned by you. One component schema per widget type. Copy this file for each widget, then list it in schemas.config.mjs. The pattern to keep: allOf the baseWidget, `kind` and `type` consts, one `$defs` entry per prop, a `.state` suffixed pattern for every suffixable key, `additionalProperties: false` inside `props`, and `unevaluatedProperties: false` at the root.",Z="__IMPLEMENTATION__ example input widget",Q="object",X={labelDef:{$ref:"../core/common.schema.json#/$defs/localizable"},disabledDef:{$ref:"../core/common.schema.json#/$defs/boolOrWhen"},readonlyDef:{$ref:"../core/common.schema.json#/$defs/boolOrWhen"},hintProp:{type:"string"},placeholderProp:{type:"string"},maxlengthProp:{type:"number"}},ee=[{$ref:"../core/common.schema.json#/$defs/baseWidget"}],te={kind:{const:"input"},type:{const:"__IMPLEMENTATION__-input"},path:{$ref:"../core/common.schema.json#/$defs/dotPath"},label:{$ref:"#/$defs/labelDef"},disabled:{$ref:"#/$defs/disabledDef"},readonly:{$ref:"#/$defs/readonlyDef"},on:{$ref:"../core/common.schema.json#/$defs/on"},validator:{$ref:"../validators.schema.json#/$defs/validator"},defaultValue:{type:"string"},props:{type:"object",properties:{hint:{$ref:"#/$defs/hintProp"},placeholder:{$ref:"#/$defs/placeholderProp"},maxlength:{$ref:"#/$defs/maxlengthProp"}},patternProperties:{"^hint\\.[^.]+$":{$ref:"#/$defs/hintProp"},"^placeholder\\.[^.]+$":{$ref:"#/$defs/placeholderProp"},"^maxlength\\.[^.]+$":{$ref:"#/$defs/maxlengthProp"}},additionalProperties:!1}},re={"^label\\.[^.]+$":{$ref:"#/$defs/labelDef"},"^disabled\\.[^.]+$":{$ref:"#/$defs/disabledDef"},"^readonly\\.[^.]+$":{$ref:"#/$defs/readonlyDef"},"^validator\\.[^.]+$":{$ref:"../validators.schema.json#/$defs/validator"}},oe=["kind","type","path"],ie=!1,se={$schema:K,$id:Y,$comment:H,title:Z,type:Q,$defs:X,allOf:ee,properties:te,patternProperties:re,required:oe,unevaluatedProperties:ie},ne="https://json-schema.org/draft/2020-12/schema",ae="__ID_BASE__components/grid.schema.json",le="STARTER TEMPLATE, owned by you. `grid` is a reserved widget type: core wraps every array-shaped form in a `grid` root. Keep the name and the `children` array, change the props to match your layout widget.",de="__IMPLEMENTATION__ grid widget",ce="object",me={directionProp:{enum:["row","column"]},columnsProp:{oneOf:[{type:"integer",minimum:1,maximum:12},{const:"auto"}]},gapProp:{enum:["none","xs","sm","md","lg","xl"]},justifyProp:{enum:["stretch","start","center","end","space-between"]}},pe=[{$ref:"../core/common.schema.json#/$defs/baseWidget"}],fe={kind:{const:"layout"},type:{const:"grid"},children:{type:"array",minItems:1,items:{$ref:"../widgets.schema.json#/$defs/formWidget"}},props:{type:"object",properties:{direction:{$ref:"#/$defs/directionProp"},columns:{$ref:"#/$defs/columnsProp"},gap:{$ref:"#/$defs/gapProp"},justify:{$ref:"#/$defs/justifyProp"}},patternProperties:{"^direction\\.[^.]+$":{$ref:"#/$defs/directionProp"},"^columns\\.[^.]+$":{$ref:"#/$defs/columnsProp"},"^gap\\.[^.]+$":{$ref:"#/$defs/gapProp"},"^justify\\.[^.]+$":{$ref:"#/$defs/justifyProp"}},additionalProperties:!1}},he=["kind","type","children"],ue=!1,ye={$schema:ne,$id:ae,$comment:le,title:de,type:ce,$defs:me,allOf:pe,properties:fe,required:he,unevaluatedProperties:ue},ge="https://json-schema.org/draft/2020-12/schema",$e="__ID_BASE__validators.schema.json",we="STARTER TEMPLATE, owned by you. Edit it freely. The only contract is that it exposes #/$defs/validator, which every component schema references. Copied from the golemui gui validators when the tree was scaffolded, it does not track them afterwards.",ve="__IMPLEMENTATION__ validator definitions",be={localizable:{$ref:"./core/common.schema.json#/$defs/localizable"},stringValidator:{type:"object",properties:{type:{const:"string"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is empty or absent"},minLength:{type:"number",description:"Minimum number of characters the string must contain"},maxLength:{type:"number",description:"Maximum number of characters the string may contain"},pattern:{type:"string",description:"A regular expression pattern the value must match"},format:{type:"string",description:"A named format the string must conform to (e.g. `email`, `url`, `date`, `uuid`)",enum:["email","hostname","ipv4","ipv6","url","uuid","date","time","date-time","duration"]},const:{description:"The string must equal exactly this value"},enum:{type:"array",description:"The string must be one of these allowed values"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},required:{$ref:"#/$defs/localizable",description:"Shown when a required field is empty"},minLength:{$ref:"#/$defs/localizable",description:"Shown when the value is shorter than `minLength`"},maxLength:{$ref:"#/$defs/localizable",description:"Shown when the value is longer than `maxLength`"},pattern:{$ref:"#/$defs/localizable",description:"Shown when the value does not match the `pattern` regex"},format:{$ref:"#/$defs/localizable",description:"Shown when the value does not conform to the specified `format`"},enum:{$ref:"#/$defs/localizable",description:"Shown when the value is not in the `enum` list"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},numberValidator:{type:"object",properties:{type:{type:"string",enum:["number","integer"]},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is absent"},minimum:{type:"number",description:"The value must be greater than or equal to this number (inclusive)"},maximum:{type:"number",description:"The value must be less than or equal to this number (inclusive)"},exclusiveMinimum:{type:"number",description:"The value must be strictly greater than this number (exclusive)"},exclusiveMaximum:{type:"number",description:"The value must be strictly less than this number (exclusive)"},multipleOf:{type:"number",description:"The value must be a multiple of this number"},const:{description:"The value must equal exactly this number"},enum:{type:"array",description:"The value must be one of these allowed numbers"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},minimum:{$ref:"#/$defs/localizable",description:"Shown when the value is below `minimum`"},maximum:{$ref:"#/$defs/localizable",description:"Shown when the value exceeds `maximum`"},exclusiveMinimum:{$ref:"#/$defs/localizable",description:"Shown when the value is not strictly above `exclusiveMinimum`"},exclusiveMaximum:{$ref:"#/$defs/localizable",description:"Shown when the value is not strictly below `exclusiveMaximum`"},multipleOf:{$ref:"#/$defs/localizable",description:"Shown when the value is not a multiple of `multipleOf`"},enum:{$ref:"#/$defs/localizable",description:"Shown when the value is not in the `enum` list"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},booleanValidator:{type:"object",properties:{type:{const:"boolean"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the value is absent or false (e.g. an unchecked required checkbox)"},const:{description:"The value must equal exactly this boolean (e.g. `true` to require acceptance)"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},const:{$ref:"#/$defs/localizable",description:"Shown when the value does not equal `const`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},arrayValidator:{type:"object",properties:{type:{const:"array"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the array is empty or absent"},minItems:{type:"number",description:"The array must contain at least this many items"},maxItems:{type:"number",description:"The array must contain no more than this many items"},uniqueItems:{type:"boolean",description:"When true, all items in the array must be distinct values"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value fails general type validation"},required:{$ref:"#/$defs/localizable",description:"Shown when a required array is empty or absent"},minItems:{$ref:"#/$defs/localizable",description:"Shown when the array has fewer items than `minItems`"},maxItems:{$ref:"#/$defs/localizable",description:"Shown when the array has more items than `maxItems`"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},fileValidator:{type:"object",description:"Validates the file envelope stored by the `fileUpload` widget (`{ id, name, size, type, status, error?, data? }` or `null`).",properties:{type:{const:"file"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if no file is set"},blockPendingUploads:{type:"boolean",description:"When true (the default), validation fails while the file's status is `uploading` or `error`, so an unfinished upload cannot be submitted"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value is not a file envelope"},required:{$ref:"#/$defs/localizable",description:"Shown when a required field has no file"},pendingUploads:{$ref:"#/$defs/localizable",description:"Shown while the file is still uploading or failed to upload"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},filesValidator:{type:"object",description:"Validates the array of file envelopes stored by the `multiFileUpload` widget.",properties:{type:{const:"files"},required:{type:"boolean",description:"Makes the field mandatory - validation fails if the array is empty or absent"},minItems:{type:"number",description:"The array must contain at least this many files"},maxItems:{type:"number",description:"The array must contain no more than this many files"},blockPendingUploads:{type:"boolean",description:"When true (the default), validation fails while any file's status is `uploading` or `error`, so an unfinished upload cannot be submitted"},messages:{type:"object",description:"Custom error messages that override the default text for each constraint violation",properties:{invalid:{$ref:"#/$defs/localizable",description:"Shown when the value is not an array of file envelopes"},required:{$ref:"#/$defs/localizable",description:"Shown when a required array is empty or absent"},minItems:{$ref:"#/$defs/localizable",description:"Shown when the array has fewer files than `minItems`"},maxItems:{$ref:"#/$defs/localizable",description:"Shown when the array has more files than `maxItems`"},pendingUploads:{$ref:"#/$defs/localizable",description:"Shown while any file is still uploading or failed to upload"}},additionalProperties:!1}},required:["type"],additionalProperties:!1},customValidator:{type:"object",description:"A custom validator that delegates validation to application code. The registered validator function receives these constraint keys and must return a Standard Schema V1-compliant schema object (https://standardschema.dev). Add arbitrary constraint keys alongside `type` - they are passed as-is to the registered validator function.",properties:{type:{const:"custom"},required:{type:"boolean",description:"Makes the field mandatory - evaluation is still delegated to the custom validator"}},required:["type"],additionalProperties:!0},validator:{description:"Validation rules for this field. The `type` discriminant selects the validator: `string`, `number`, `integer`, `boolean`, `array`, `file`, `files`, or `custom`.",oneOf:[{$ref:"#/$defs/stringValidator"},{$ref:"#/$defs/numberValidator"},{$ref:"#/$defs/booleanValidator"},{$ref:"#/$defs/arrayValidator"},{$ref:"#/$defs/fileValidator"},{$ref:"#/$defs/filesValidator"},{$ref:"#/$defs/customValidator"}]}},xe={$schema:ge,$id:$e,$comment:we,title:ve,$defs:be};function I(e){return`${e}-input`}function je(e,t){const r=e.replace(/__IMPLEMENTATION__/g,t.implementation).replace(/__ID_BASE__/g,t.idBase),o=r.match(/__[A-Z_]+__/);if(o!==null)throw new Error(`Unsubstituted placeholder ${o[0]} in a starter template.`);return r}function u(e,t){return je(JSON.stringify(e,null,2),t)+`
4
+ `}function Se(e){return{"schemas.config.mjs":Ee(e),"src/lib/validators.schema.json":u(xe,e),"src/lib/components/grid.schema.json":u(ye,e),"src/lib/components/example-input.schema.json":u(se,e),"examples/example.form.json":u(G,e),"test/schemas.spec.ts":Pe(e)}}function Ee(e){return`// The manifest and configuration for this implementation's JSON schema tree.
5
5
  // You own this file. Every file marked GENERATED is rebuilt from it by
6
6
  // \`npx @golemui/schemas generate\`, so add a widget by writing its component schema
7
7
  // under src/lib/components/ and listing it in \`manifest\` below.
@@ -20,10 +20,10 @@ export default {
20
20
  'Named boolean conditions keyed by state name, each mapping to a reactive expression. ' +
21
21
  'Root-level widget props such as \`label\`, \`disabled\`, \`readonly\` and \`validator\` accept ' +
22
22
  'a \`.stateName\` suffix, as does any key inside \`props\`.',
23
- // One entry per widget type. \`flex\` and \`repeater\` are reserved names: use them for
23
+ // One entry per widget type. \`grid\` and \`repeater\` are reserved names: use them for
24
24
  // your layout and repeat widgets, and do not give another widget those types.
25
25
  manifest: [
26
- { type: 'flex', schemaFile: 'flex.schema.json', kind: 'layout' },
26
+ { type: 'grid', schemaFile: 'grid.schema.json', kind: 'layout' },
27
27
  {
28
28
  type: '${I(e.implementation)}',
29
29
  schemaFile: 'example-input.schema.json',
@@ -131,7 +131,7 @@ Options for convert:
131
131
  Diagnostics are written to standard error. The config holds the same settings plus
132
132
  rules, overrides and presetOptions, see the @golemui/schemas README.
133
133
 
134
- Every command accepts --help.`;function Te(e){const t=new Map,r=[];let o;for(let i=0;i<e.length;i+=1){const a=e[i];if(!a.startsWith("--")){o===void 0?o=a:r.push(a);continue}const l=a.slice(2),c=l.indexOf("=");if(c!==-1){t.set(l.slice(0,c),l.slice(c+1));continue}const n=e[i+1];n===void 0||n.startsWith("--")?t.set(l,!0):(t.set(l,n),i+=1)}return{command:o,positionals:r,flags:t}}function y(e,t){const r=e.get(t);return typeof r=="string"?r:void 0}async function g(e,t,r){if(process.stdin.isTTY!==!0)throw new Error(`Missing --${t}. There is no terminal to ask on.`);const o=N.createInterface({input:process.stdin,output:process.stdout});try{const i=(await o.question(`${e} [${r}] `)).trim();return i===""?r:i}finally{o.close()}}function _e(e){if(!/^[a-z][a-z0-9-]*$/.test(e))throw new Error(`Invalid implementation name "${e}". Use lowercase letters, digits and hyphens, starting with a letter.`)}function Ie(e){let t;try{t=new URL(e)}catch{throw new Error(`Invalid --id-base "${e}". It must be an absolute URL.`)}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new Error(`Invalid --id-base "${e}". It must be an http or https URL.`);return e.endsWith("/")?e:`${e}/`}function qe(e,t,r){const o=s.join(e,t);d.mkdirSync(s.dirname(o),{recursive:!0}),d.writeFileSync(o,r,"utf-8"),console.log(`Wrote ${t}`)}async function Oe(e){const t=y(e,"name")??await g("Implementation name?","name","my-widgets");_e(t);const r=Ie(y(e,"id-base")??await g("Base URL of the published tree?","id-base",`https://example.com/schemas/${t}/`)),o=y(e,"dir")??await g("Directory?","dir","schemas"),i=s.resolve(process.cwd(),o),a=Se({implementation:t,idBase:r}),l=Object.keys(a).filter(n=>d.existsSync(s.join(i,n)));if(l.length>0&&e.get("force")!==!0)throw new Error(`Refusing to overwrite starter files you may have edited: ${l.join(", ")}. Pass --force to replace them.`);for(const[n,f]of Object.entries(a))qe(i,n,f);await q(i);const c=s.relative(process.cwd(),i)||".";console.log(`
134
+ Every command accepts --help.`;function Te(e){const t=new Map,r=[];let o;for(let i=0;i<e.length;i+=1){const a=e[i];if(!a.startsWith("--")){o===void 0?o=a:r.push(a);continue}const l=a.slice(2),c=l.indexOf("=");if(c!==-1){t.set(l.slice(0,c),l.slice(c+1));continue}const n=e[i+1];n===void 0||n.startsWith("--")?t.set(l,!0):(t.set(l,n),i+=1)}return{command:o,positionals:r,flags:t}}function y(e,t){const r=e.get(t);return typeof r=="string"?r:void 0}async function $(e,t,r){if(process.stdin.isTTY!==!0)throw new Error(`Missing --${t}. There is no terminal to ask on.`);const o=N.createInterface({input:process.stdin,output:process.stdout});try{const i=(await o.question(`${e} [${r}] `)).trim();return i===""?r:i}finally{o.close()}}function _e(e){if(!/^[a-z][a-z0-9-]*$/.test(e))throw new Error(`Invalid implementation name "${e}". Use lowercase letters, digits and hyphens, starting with a letter.`)}function Ie(e){let t;try{t=new URL(e)}catch{throw new Error(`Invalid --id-base "${e}". It must be an absolute URL.`)}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new Error(`Invalid --id-base "${e}". It must be an http or https URL.`);return e.endsWith("/")?e:`${e}/`}function Oe(e,t,r){const o=s.join(e,t);d.mkdirSync(s.dirname(o),{recursive:!0}),d.writeFileSync(o,r,"utf-8"),console.log(`Wrote ${t}`)}async function qe(e){const t=y(e,"name")??await $("Implementation name?","name","my-widgets");_e(t);const r=Ie(y(e,"id-base")??await $("Base URL of the published tree?","id-base",`https://example.com/schemas/${t}/`)),o=y(e,"dir")??await $("Directory?","dir","schemas"),i=s.resolve(process.cwd(),o),a=Se({implementation:t,idBase:r}),l=Object.keys(a).filter(n=>d.existsSync(s.join(i,n)));if(l.length>0&&e.get("force")!==!0)throw new Error(`Refusing to overwrite starter files you may have edited: ${l.join(", ")}. Pass --force to replace them.`);for(const[n,f]of Object.entries(a))Oe(i,n,f);await O(i);const c=s.relative(process.cwd(),i)||".";console.log(`
135
135
  Scaffolded the ${t} schema tree in ${c}.
136
136
 
137
137
  Next:
@@ -144,6 +144,6 @@ Next:
144
144
  ${p} or to validators.schema.json. A CI step that regenerates and then runs
145
145
  \`git diff --exit-code\` catches a forgotten rerun.
146
146
  4. Point a form file's "$schema" at src/lib/form.editor.schema.json, as
147
- examples/example.form.json does. Editors need that bundle, Ajv uses the per-file tree.`)}function Ae(e,t){if(e===null||typeof e!="object")throw new Error(`${t} must export a configuration object as its default export.`);const r=e,i=["implementation","idBase","generatorPath","formTitle"].filter(a=>typeof r[a]!="string");if(i.length>0)throw new Error(`${t} is missing required string fields: ${i.join(", ")}.`);if(!Array.isArray(r.manifest)||r.manifest.length===0)throw new Error(`${t} must declare a non-empty \`manifest\` array.`);if(!r.idBase.endsWith("/"))throw new Error(`${t}: \`idBase\` must end with a slash.`);return e}async function q(e){const t=s.join(e,p);if(!d.existsSync(t))throw new Error(`No ${p} in ${e}. Run this from the directory holding it, pass --dir, or scaffold a new tree with \`npx @golemui/schemas init\`.`);const r=await import(w.pathToFileURL(t).href),o=Ae(r.default,p);await M.generateImplementationSchemas(o,e)}async function Ne(e){const{command:t,positionals:r,flags:o}=Te(e);if(o.get("help")===!0||t==="help"||t===void 0)return console.log(P),t===void 0&&o.get("help")!==!0?1:0;try{switch(t){case"init":return await Oe(o),0;case"generate":return await q(s.resolve(process.cwd(),y(o,"dir")??".")),0;case"convert":return await k(o,r);default:return console.error(`Unknown command "${t}".
147
+ examples/example.form.json does. Editors need that bundle, Ajv uses the per-file tree.`)}function Ae(e,t){if(e===null||typeof e!="object")throw new Error(`${t} must export a configuration object as its default export.`);const r=e,i=["implementation","idBase","generatorPath","formTitle"].filter(a=>typeof r[a]!="string");if(i.length>0)throw new Error(`${t} is missing required string fields: ${i.join(", ")}.`);if(!Array.isArray(r.manifest)||r.manifest.length===0)throw new Error(`${t} must declare a non-empty \`manifest\` array.`);if(!r.idBase.endsWith("/"))throw new Error(`${t}: \`idBase\` must end with a slash.`);return e}async function O(e){const t=s.join(e,p);if(!d.existsSync(t))throw new Error(`No ${p} in ${e}. Run this from the directory holding it, pass --dir, or scaffold a new tree with \`npx @golemui/schemas init\`.`);const r=await import(w.pathToFileURL(t).href),o=Ae(r.default,p);await M.generateImplementationSchemas(o,e)}async function Ne(e){const{command:t,positionals:r,flags:o}=Te(e);if(o.get("help")===!0||t==="help"||t===void 0)return console.log(P),t===void 0&&o.get("help")!==!0?1:0;try{switch(t){case"init":return await qe(o),0;case"generate":return await O(s.resolve(process.cwd(),y(o,"dir")??".")),0;case"convert":return await k(o,r);default:return console.error(`Unknown command "${t}".
148
148
 
149
149
  ${P}`),1}}catch(i){return console.error(i instanceof Error?i.message:i),1}}Ne(process.argv.slice(2)).then(e=>{process.exitCode=e});
package/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync as p, mkdirSync as O, writeFileSync as A, readFileSync as R } from "node:fs";
3
- import { resolve as d, relative as g, dirname as b, join as u } from "node:path";
2
+ import { existsSync as p, mkdirSync as O, writeFileSync as A, readFileSync as k } from "node:fs";
3
+ import { resolve as d, relative as $, dirname as b, join as u } from "node:path";
4
4
  import { createInterface as C } from "node:readline/promises";
5
5
  import { pathToFileURL as x } from "node:url";
6
6
  import { g as L } from "./generate-implementation-schemas-DxCUi0Jr.js";
@@ -8,7 +8,7 @@ import { createRequire as D } from "node:module";
8
8
  import { f as F, r as V } from "./json-schema-Bdnmzg_d.js";
9
9
  const U = ["json-schema.config.mjs", "json-schema.config.json"];
10
10
  async function W(e, t) {
11
- const r = process.cwd(), { config: o, baseDir: i } = await B(h(e, "config"), r), n = K(h(e, "fail-on")), a = t[0] ? d(r, t[0]) : o.input !== void 0 ? d(i, o.input) : void 0;
11
+ const r = process.cwd(), { config: o, baseDir: i } = await B(h(e, "config"), r), n = Y(h(e, "fail-on")), a = t[0] ? d(r, t[0]) : o.input !== void 0 ? d(i, o.input) : void 0;
12
12
  if (a === void 0)
13
13
  throw new Error(
14
14
  "Missing the JSON Schema file. Pass it as `convert <schema.json>`, or set `input` in the config."
@@ -16,14 +16,14 @@ async function W(e, t) {
16
16
  const l = h(e, "out"), s = l ? d(r, l) : o.output !== void 0 ? d(i, o.output) : void 0;
17
17
  if (s !== void 0 && p(s) && e.get("force") !== !0)
18
18
  throw new Error(
19
- `${g(r, s)} exists. A generated form is often edited by hand, so it is only replaced with --force.`
19
+ `${$(r, s)} exists. A generated form is often edited by hand, so it is only replaced with --force.`
20
20
  );
21
21
  const f = q(a), j = h(e, "pointer") ?? o.pointer, S = h(e, "preset"), z = await G(
22
22
  S ?? o.preset,
23
23
  o.presetOptions,
24
24
  S !== void 0 ? r : i
25
- ), { formDefinition: k, diagnostics: E } = F(
26
- j === void 0 ? f : Y(f, j),
25
+ ), { formDefinition: R, diagnostics: E } = F(
26
+ j === void 0 ? f : K(f, j),
27
27
  {
28
28
  preset: z,
29
29
  rules: o.rules,
@@ -35,9 +35,9 @@ async function W(e, t) {
35
35
  maxNodes: o.maxNodes,
36
36
  transform: o.transform
37
37
  }
38
- ), P = `${JSON.stringify(k, null, 2)}
38
+ ), P = `${JSON.stringify(R, null, 2)}
39
39
  `;
40
- s === void 0 ? process.stdout.write(P) : (O(b(s), { recursive: !0 }), A(s, P, "utf-8"), console.log(`Wrote ${g(r, s)}`));
40
+ s === void 0 ? process.stdout.write(P) : (O(b(s), { recursive: !0 }), A(s, P, "utf-8"), console.log(`Wrote ${$(r, s)}`));
41
41
  for (const w of E)
42
42
  console.error(H(w));
43
43
  return E.some((w) => n.includes(w.severity)) ? 1 : 0;
@@ -52,7 +52,7 @@ async function B(e, t) {
52
52
  /* @vite-ignore */
53
53
  x(r).href
54
54
  )).default;
55
- return { config: J(o, g(t, r)), baseDir: b(r) };
55
+ return { config: J(o, $(t, r)), baseDir: b(r) };
56
56
  }
57
57
  const T = {
58
58
  input: (e) => typeof e == "string",
@@ -106,7 +106,7 @@ async function G(e, t, r) {
106
106
  );
107
107
  return n(t ?? {});
108
108
  }
109
- function Y(e, t) {
109
+ function K(e, t) {
110
110
  const r = V(`#${t.replace(/^#/, "")}`, e);
111
111
  if ("error" in r)
112
112
  throw new Error(`--pointer ${t}: ${r.error}`);
@@ -118,12 +118,12 @@ function q(e) {
118
118
  if (!p(e))
119
119
  throw new Error(`${e} not found.`);
120
120
  try {
121
- return JSON.parse(R(e, "utf-8"));
121
+ return JSON.parse(k(e, "utf-8"));
122
122
  } catch (t) {
123
123
  throw new Error(`${e} is not valid JSON: ${t instanceof Error ? t.message : t}`);
124
124
  }
125
125
  }
126
- function K(e) {
126
+ function Y(e) {
127
127
  switch (e) {
128
128
  case void 0:
129
129
  return [];
@@ -152,7 +152,7 @@ function I(e) {
152
152
  function c(e) {
153
153
  return e !== null && typeof e == "object" && !Array.isArray(e);
154
154
  }
155
- const Q = "../src/lib/form.editor.schema.json", X = [{ kind: "layout", type: "flex", uid: "#root", props: { direction: "column", gap: 16 }, children: [{ kind: "input", type: "__IMPLEMENTATION__-input", uid: "email", path: "email", label: "Email address", validator: { type: "string", required: !0, format: "email" }, props: { placeholder: "you@example.com" } }] }], ee = {
155
+ const Q = "../src/lib/form.editor.schema.json", X = [{ kind: "layout", type: "grid", uid: "#root", props: { gap: "md" }, children: [{ kind: "input", type: "__IMPLEMENTATION__-input", uid: "email", path: "email", label: "Email address", validator: { type: "string", required: !0, format: "email" }, props: { placeholder: "you@example.com" } }] }], ee = {
156
156
  $schema: Q,
157
157
  form: X
158
158
  }, te = "https://json-schema.org/draft/2020-12/schema", re = "__ID_BASE__components/example-input.schema.json", oe = "STARTER TEMPLATE, owned by you. One component schema per widget type. Copy this file for each widget, then list it in schemas.config.mjs. The pattern to keep: allOf the baseWidget, `kind` and `type` consts, one `$defs` entry per prop, a `.state` suffixed pattern for every suffixable key, `additionalProperties: false` inside `props`, and `unevaluatedProperties: false` at the root.", ie = "__IMPLEMENTATION__ example input widget", se = "object", ne = { labelDef: { $ref: "../core/common.schema.json#/$defs/localizable" }, disabledDef: { $ref: "../core/common.schema.json#/$defs/boolOrWhen" }, readonlyDef: { $ref: "../core/common.schema.json#/$defs/boolOrWhen" }, hintProp: { type: "string" }, placeholderProp: { type: "string" }, maxlengthProp: { type: "number" } }, ae = [{ $ref: "../core/common.schema.json#/$defs/baseWidget" }], le = { kind: { const: "input" }, type: { const: "__IMPLEMENTATION__-input" }, path: { $ref: "../core/common.schema.json#/$defs/dotPath" }, label: { $ref: "#/$defs/labelDef" }, disabled: { $ref: "#/$defs/disabledDef" }, readonly: { $ref: "#/$defs/readonlyDef" }, on: { $ref: "../core/common.schema.json#/$defs/on" }, validator: { $ref: "../validators.schema.json#/$defs/validator" }, defaultValue: { type: "string" }, props: { type: "object", properties: { hint: { $ref: "#/$defs/hintProp" }, placeholder: { $ref: "#/$defs/placeholderProp" }, maxlength: { $ref: "#/$defs/maxlengthProp" } }, patternProperties: { "^hint\\.[^.]+$": { $ref: "#/$defs/hintProp" }, "^placeholder\\.[^.]+$": { $ref: "#/$defs/placeholderProp" }, "^maxlength\\.[^.]+$": { $ref: "#/$defs/maxlengthProp" } }, additionalProperties: !1 } }, de = { "^label\\.[^.]+$": { $ref: "#/$defs/labelDef" }, "^disabled\\.[^.]+$": { $ref: "#/$defs/disabledDef" }, "^readonly\\.[^.]+$": { $ref: "#/$defs/readonlyDef" }, "^validator\\.[^.]+$": { $ref: "../validators.schema.json#/$defs/validator" } }, ce = ["kind", "type", "path"], me = !1, pe = {
@@ -167,13 +167,13 @@ const Q = "../src/lib/form.editor.schema.json", X = [{ kind: "layout", type: "fl
167
167
  patternProperties: de,
168
168
  required: ce,
169
169
  unevaluatedProperties: me
170
- }, fe = "https://json-schema.org/draft/2020-12/schema", he = "__ID_BASE__components/flex.schema.json", ue = "STARTER TEMPLATE, owned by you. `flex` is a reserved widget type: keep the name and the `children` array, change the props to match your layout widget.", ye = "__IMPLEMENTATION__ flex widget", $e = "object", ge = { directionProp: { enum: ["row", "row-reverse", "column", "column-reverse"] }, justifyProp: { enum: ["start", "center", "end", "stretch", "space-between", "space-around"] }, alignProp: { enum: ["start", "center", "end", "stretch", "baseline"] }, wrapProp: { type: "boolean" }, gapProp: { type: "number" }, paddingProp: { type: "number" } }, we = [{ $ref: "../core/common.schema.json#/$defs/baseWidget" }], ve = { kind: { const: "layout" }, type: { const: "flex" }, children: { type: "array", minItems: 1, items: { $ref: "../widgets.schema.json#/$defs/formWidget" } }, props: { type: "object", properties: { direction: { $ref: "#/$defs/directionProp" }, justify: { $ref: "#/$defs/justifyProp" }, align: { $ref: "#/$defs/alignProp" }, wrap: { $ref: "#/$defs/wrapProp" }, gap: { $ref: "#/$defs/gapProp" }, padding: { $ref: "#/$defs/paddingProp" } }, patternProperties: { "^direction\\.[^.]+$": { $ref: "#/$defs/directionProp" }, "^justify\\.[^.]+$": { $ref: "#/$defs/justifyProp" }, "^align\\.[^.]+$": { $ref: "#/$defs/alignProp" }, "^wrap\\.[^.]+$": { $ref: "#/$defs/wrapProp" }, "^gap\\.[^.]+$": { $ref: "#/$defs/gapProp" }, "^padding\\.[^.]+$": { $ref: "#/$defs/paddingProp" } }, additionalProperties: !1 } }, be = ["kind", "type", "children"], xe = !1, je = {
170
+ }, fe = "https://json-schema.org/draft/2020-12/schema", he = "__ID_BASE__components/grid.schema.json", ue = "STARTER TEMPLATE, owned by you. `grid` is a reserved widget type: core wraps every array-shaped form in a `grid` root. Keep the name and the `children` array, change the props to match your layout widget.", ye = "__IMPLEMENTATION__ grid widget", ge = "object", $e = { directionProp: { enum: ["row", "column"] }, columnsProp: { oneOf: [{ type: "integer", minimum: 1, maximum: 12 }, { const: "auto" }] }, gapProp: { enum: ["none", "xs", "sm", "md", "lg", "xl"] }, justifyProp: { enum: ["stretch", "start", "center", "end", "space-between"] } }, we = [{ $ref: "../core/common.schema.json#/$defs/baseWidget" }], ve = { kind: { const: "layout" }, type: { const: "grid" }, children: { type: "array", minItems: 1, items: { $ref: "../widgets.schema.json#/$defs/formWidget" } }, props: { type: "object", properties: { direction: { $ref: "#/$defs/directionProp" }, columns: { $ref: "#/$defs/columnsProp" }, gap: { $ref: "#/$defs/gapProp" }, justify: { $ref: "#/$defs/justifyProp" } }, patternProperties: { "^direction\\.[^.]+$": { $ref: "#/$defs/directionProp" }, "^columns\\.[^.]+$": { $ref: "#/$defs/columnsProp" }, "^gap\\.[^.]+$": { $ref: "#/$defs/gapProp" }, "^justify\\.[^.]+$": { $ref: "#/$defs/justifyProp" } }, additionalProperties: !1 } }, be = ["kind", "type", "children"], xe = !1, je = {
171
171
  $schema: fe,
172
172
  $id: he,
173
173
  $comment: ue,
174
174
  title: ye,
175
- type: $e,
176
- $defs: ge,
175
+ type: ge,
176
+ $defs: $e,
177
177
  allOf: we,
178
178
  properties: ve,
179
179
  required: be,
@@ -202,7 +202,7 @@ function Ae(e) {
202
202
  return {
203
203
  "schemas.config.mjs": qe(e),
204
204
  "src/lib/validators.schema.json": y(_e, e),
205
- "src/lib/components/flex.schema.json": y(je, e),
205
+ "src/lib/components/grid.schema.json": y(je, e),
206
206
  "src/lib/components/example-input.schema.json": y(
207
207
  pe,
208
208
  e
@@ -231,10 +231,10 @@ export default {
231
231
  'Named boolean conditions keyed by state name, each mapping to a reactive expression. ' +
232
232
  'Root-level widget props such as \`label\`, \`disabled\`, \`readonly\` and \`validator\` accept ' +
233
233
  'a \`.stateName\` suffix, as does any key inside \`props\`.',
234
- // One entry per widget type. \`flex\` and \`repeater\` are reserved names: use them for
234
+ // One entry per widget type. \`grid\` and \`repeater\` are reserved names: use them for
235
235
  // your layout and repeat widgets, and do not give another widget those types.
236
236
  manifest: [
237
- { type: 'flex', schemaFile: 'flex.schema.json', kind: 'layout' },
237
+ { type: 'grid', schemaFile: 'grid.schema.json', kind: 'layout' },
238
238
  {
239
239
  type: '${N(e.implementation)}',
240
240
  schemaFile: 'example-input.schema.json',
@@ -367,7 +367,7 @@ function Me(e) {
367
367
  }
368
368
  return { command: o, positionals: r, flags: t };
369
369
  }
370
- function $(e, t) {
370
+ function g(e, t) {
371
371
  const r = e.get(t);
372
372
  return typeof r == "string" ? r : void 0;
373
373
  }
@@ -388,7 +388,7 @@ function ze(e) {
388
388
  `Invalid implementation name "${e}". Use lowercase letters, digits and hyphens, starting with a letter.`
389
389
  );
390
390
  }
391
- function ke(e) {
391
+ function Re(e) {
392
392
  let t;
393
393
  try {
394
394
  t = new URL(e);
@@ -399,28 +399,28 @@ function ke(e) {
399
399
  throw new Error(`Invalid --id-base "${e}". It must be an http or https URL.`);
400
400
  return e.endsWith("/") ? e : `${e}/`;
401
401
  }
402
- function Re(e, t, r) {
402
+ function ke(e, t, r) {
403
403
  const o = u(e, t);
404
404
  O(b(o), { recursive: !0 }), A(o, r, "utf-8"), console.log(`Wrote ${t}`);
405
405
  }
406
406
  async function Ce(e) {
407
- const t = $(e, "name") ?? await v("Implementation name?", "name", "my-widgets");
407
+ const t = g(e, "name") ?? await v("Implementation name?", "name", "my-widgets");
408
408
  ze(t);
409
- const r = ke(
410
- $(e, "id-base") ?? await v(
409
+ const r = Re(
410
+ g(e, "id-base") ?? await v(
411
411
  "Base URL of the published tree?",
412
412
  "id-base",
413
413
  `https://example.com/schemas/${t}/`
414
414
  )
415
- ), o = $(e, "dir") ?? await v("Directory?", "dir", "schemas"), i = d(process.cwd(), o), n = Ae({ implementation: t, idBase: r }), a = Object.keys(n).filter((s) => p(u(i, s)));
415
+ ), o = g(e, "dir") ?? await v("Directory?", "dir", "schemas"), i = d(process.cwd(), o), n = Ae({ implementation: t, idBase: r }), a = Object.keys(n).filter((s) => p(u(i, s)));
416
416
  if (a.length > 0 && e.get("force") !== !0)
417
417
  throw new Error(
418
418
  `Refusing to overwrite starter files you may have edited: ${a.join(", ")}. Pass --force to replace them.`
419
419
  );
420
420
  for (const [s, f] of Object.entries(n))
421
- Re(i, s, f);
421
+ ke(i, s, f);
422
422
  await M(i);
423
- const l = g(process.cwd(), i) || ".";
423
+ const l = $(process.cwd(), i) || ".";
424
424
  console.log(`
425
425
  Scaffolded the ${t} schema tree in ${l}.
426
426
 
@@ -469,7 +469,7 @@ async function De(e) {
469
469
  case "init":
470
470
  return await Ce(o), 0;
471
471
  case "generate":
472
- return await M(d(process.cwd(), $(o, "dir") ?? ".")), 0;
472
+ return await M(d(process.cwd(), g(o, "dir") ?? ".")), 0;
473
473
  case "convert":
474
474
  return await W(o, r);
475
475
  default:
@@ -8,7 +8,7 @@ export declare const EDITOR_BUNDLE_FILE = "form.editor.schema.json";
8
8
  * Builds the self-contained editor schema by walking the `$ref` graph from the form
9
9
  * envelope. Every reachable file is inlined, so the result loads with no network access.
10
10
  * @param config - The implementation's schema configuration.
11
- * @param readSchema - Returns the parsed schema at a lib-relative path, e.g. `components/flex.schema.json`.
11
+ * @param readSchema - Returns the parsed schema at a lib-relative path, e.g. `components/grid.schema.json`.
12
12
  * @returns The bundle, ready to be serialized.
13
13
  * @example
14
14
  * const bundle = buildEditorBundle(config, (path) =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golemui/schemas",
3
- "version": "1.6.0-rc.1",
3
+ "version": "2.0.0-rc.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"