@formspec/build 0.1.0-alpha.11 → 0.1.0-alpha.12
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/README.md +51 -15
- package/dist/__tests__/chain-dsl-canonicalizer.test.d.ts +2 -0
- package/dist/__tests__/chain-dsl-canonicalizer.test.d.ts.map +1 -0
- package/dist/__tests__/constraint-validator.test.d.ts +2 -0
- package/dist/__tests__/constraint-validator.test.d.ts.map +1 -0
- package/dist/__tests__/extension-api.test.d.ts +2 -0
- package/dist/__tests__/extension-api.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +18 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts.map +1 -1
- package/dist/__tests__/ir-analyzer.test.d.ts +11 -0
- package/dist/__tests__/ir-analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/ir-jsdoc-constraints.test.d.ts +12 -0
- package/dist/__tests__/ir-jsdoc-constraints.test.d.ts.map +1 -0
- package/dist/__tests__/ir-json-schema-generator.test.d.ts +11 -0
- package/dist/__tests__/ir-json-schema-generator.test.d.ts.map +1 -0
- package/dist/__tests__/ir-ui-schema-generator.test.d.ts +2 -0
- package/dist/__tests__/ir-ui-schema-generator.test.d.ts.map +1 -0
- package/dist/__tests__/jsdoc-constraints.test.d.ts +4 -4
- package/dist/__tests__/parity/fixtures/address/chain-dsl.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/address/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/address/expected-ir.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/address/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/address/tsdoc.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/address/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/product-config/chain-dsl.d.ts +13 -0
- package/dist/__tests__/parity/fixtures/product-config/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/product-config/expected-ir.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/product-config/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/product-config/tsdoc.d.ts +28 -0
- package/dist/__tests__/parity/fixtures/product-config/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/user-registration/chain-dsl.d.ts +12 -0
- package/dist/__tests__/parity/fixtures/user-registration/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/user-registration/expected-ir.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/user-registration/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/user-registration/tsdoc.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/user-registration/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/parity.test.d.ts +14 -0
- package/dist/__tests__/parity/parity.test.d.ts.map +1 -0
- package/dist/__tests__/parity/utils.d.ts +139 -0
- package/dist/__tests__/parity/utils.d.ts.map +1 -0
- package/dist/analyzer/class-analyzer.d.ts +54 -99
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +78 -30
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +61 -0
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -0
- package/dist/browser.cjs +960 -309
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +10 -6
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +958 -308
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +65 -150
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts +18 -0
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -0
- package/dist/canonicalize/index.d.ts +8 -0
- package/dist/canonicalize/index.d.ts.map +1 -0
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts +34 -0
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -0
- package/dist/cli.cjs +1417 -1656
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1421 -1647
- package/dist/cli.js.map +1 -1
- package/dist/extensions/index.d.ts +8 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/registry.d.ts +55 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/generators/class-schema.d.ts +23 -38
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts +6 -8
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/index.cjs +1353 -1614
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1365 -1610
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +1604 -824
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.d.ts +12 -3
- package/dist/internals.d.ts.map +1 -1
- package/dist/internals.js +1607 -820
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +10 -5
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +84 -0
- package/dist/json-schema/ir-generator.d.ts.map +1 -0
- package/dist/json-schema/schema.d.ts +3 -3
- package/dist/json-schema/types.d.ts +5 -6
- package/dist/json-schema/types.d.ts.map +1 -1
- package/dist/ui-schema/generator.d.ts +5 -15
- package/dist/ui-schema/generator.d.ts.map +1 -1
- package/dist/ui-schema/ir-generator.d.ts +53 -0
- package/dist/ui-schema/ir-generator.d.ts.map +1 -0
- package/dist/validate/constraint-validator.d.ts +66 -0
- package/dist/validate/constraint-validator.d.ts.map +1 -0
- package/dist/validate/index.d.ts +9 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/package.json +5 -4
- package/dist/__tests__/analyzer-edge-cases.test.d.ts +0 -13
- package/dist/__tests__/analyzer-edge-cases.test.d.ts.map +0 -1
- package/dist/__tests__/analyzer.test.d.ts +0 -5
- package/dist/__tests__/analyzer.test.d.ts.map +0 -1
- package/dist/__tests__/codegen.test.d.ts +0 -5
- package/dist/__tests__/codegen.test.d.ts.map +0 -1
- package/dist/__tests__/decorator-pipeline.test.d.ts +0 -11
- package/dist/__tests__/decorator-pipeline.test.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-b-decorators.d.ts +0 -5
- package/dist/__tests__/fixtures/example-b-decorators.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-b-extended.d.ts +0 -5
- package/dist/__tests__/fixtures/example-b-extended.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-c-custom.d.ts +0 -5
- package/dist/__tests__/fixtures/example-c-custom.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-c-decorators.d.ts +0 -5
- package/dist/__tests__/fixtures/example-c-decorators.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-d-mixed-decorators.d.ts +0 -6
- package/dist/__tests__/fixtures/example-d-mixed-decorators.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-e-decorators.d.ts +0 -11
- package/dist/__tests__/fixtures/example-e-decorators.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-e-no-namespace.d.ts +0 -5
- package/dist/__tests__/fixtures/example-e-no-namespace.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-jsdoc-constraints.d.ts +0 -16
- package/dist/__tests__/fixtures/example-jsdoc-constraints.d.ts.map +0 -1
- package/dist/__tests__/fixtures/example-nested-class.d.ts +0 -45
- package/dist/__tests__/fixtures/example-nested-class.d.ts.map +0 -1
- package/dist/__tests__/interface-types.test.d.ts +0 -11
- package/dist/__tests__/interface-types.test.d.ts.map +0 -1
- package/dist/analyzer/decorator-extractor.d.ts +0 -78
- package/dist/analyzer/decorator-extractor.d.ts.map +0 -1
- package/dist/analyzer/type-converter.d.ts +0 -75
- package/dist/analyzer/type-converter.d.ts.map +0 -1
- package/dist/codegen/index.d.ts +0 -75
- package/dist/codegen/index.d.ts.map +0 -1
package/dist/browser.cjs
CHANGED
|
@@ -21,12 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var browser_exports = {};
|
|
22
22
|
__export(browser_exports, {
|
|
23
23
|
buildFormSchemas: () => buildFormSchemas,
|
|
24
|
+
canonicalizeChainDSL: () => canonicalizeChainDSL,
|
|
24
25
|
categorizationSchema: () => categorizationSchema,
|
|
25
26
|
categorySchema: () => categorySchema,
|
|
26
27
|
controlSchema: () => controlSchema,
|
|
27
28
|
generateJsonSchema: () => generateJsonSchema,
|
|
28
29
|
generateUiSchema: () => generateUiSchema,
|
|
29
|
-
generateUiSchemaFromFields: () => generateUiSchemaFromFields,
|
|
30
30
|
getSchemaExtension: () => getSchemaExtension,
|
|
31
31
|
groupLayoutSchema: () => groupLayoutSchema,
|
|
32
32
|
horizontalLayoutSchema: () => horizontalLayoutSchema,
|
|
@@ -41,211 +41,540 @@ __export(browser_exports, {
|
|
|
41
41
|
uiSchemaElementSchema: () => uiSchemaElementSchema,
|
|
42
42
|
uiSchemaElementTypeSchema: () => uiSchemaElementTypeSchema,
|
|
43
43
|
uiSchemaSchema: () => uiSchema,
|
|
44
|
+
validateIR: () => validateIR,
|
|
44
45
|
verticalLayoutSchema: () => verticalLayoutSchema
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(browser_exports);
|
|
47
48
|
|
|
48
|
-
// src/
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
]);
|
|
59
|
-
var jsonSchema7Schema = import_zod.z.lazy(
|
|
60
|
-
() => import_zod.z.object({
|
|
61
|
-
$schema: import_zod.z.string().optional(),
|
|
62
|
-
$id: import_zod.z.string().optional(),
|
|
63
|
-
$ref: import_zod.z.string().optional(),
|
|
64
|
-
// Metadata
|
|
65
|
-
title: import_zod.z.string().optional(),
|
|
66
|
-
description: import_zod.z.string().optional(),
|
|
67
|
-
deprecated: import_zod.z.boolean().optional(),
|
|
68
|
-
// Type
|
|
69
|
-
type: import_zod.z.union([jsonSchemaTypeSchema, import_zod.z.array(jsonSchemaTypeSchema)]).optional(),
|
|
70
|
-
// String validation
|
|
71
|
-
minLength: import_zod.z.number().optional(),
|
|
72
|
-
maxLength: import_zod.z.number().optional(),
|
|
73
|
-
pattern: import_zod.z.string().optional(),
|
|
74
|
-
// Number validation
|
|
75
|
-
minimum: import_zod.z.number().optional(),
|
|
76
|
-
maximum: import_zod.z.number().optional(),
|
|
77
|
-
exclusiveMinimum: import_zod.z.number().optional(),
|
|
78
|
-
exclusiveMaximum: import_zod.z.number().optional(),
|
|
79
|
-
// Enum
|
|
80
|
-
enum: import_zod.z.array(import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean(), import_zod.z.null()])).readonly().optional(),
|
|
81
|
-
const: import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean(), import_zod.z.null()]).optional(),
|
|
82
|
-
// Object
|
|
83
|
-
properties: import_zod.z.record(import_zod.z.string(), jsonSchema7Schema).optional(),
|
|
84
|
-
required: import_zod.z.array(import_zod.z.string()).optional(),
|
|
85
|
-
additionalProperties: import_zod.z.union([import_zod.z.boolean(), jsonSchema7Schema]).optional(),
|
|
86
|
-
// Array
|
|
87
|
-
items: import_zod.z.union([jsonSchema7Schema, import_zod.z.array(jsonSchema7Schema)]).optional(),
|
|
88
|
-
minItems: import_zod.z.number().optional(),
|
|
89
|
-
maxItems: import_zod.z.number().optional(),
|
|
90
|
-
// Composition
|
|
91
|
-
allOf: import_zod.z.array(jsonSchema7Schema).optional(),
|
|
92
|
-
anyOf: import_zod.z.array(jsonSchema7Schema).optional(),
|
|
93
|
-
oneOf: import_zod.z.array(jsonSchema7Schema).optional(),
|
|
94
|
-
not: jsonSchema7Schema.optional(),
|
|
95
|
-
// Conditional
|
|
96
|
-
if: jsonSchema7Schema.optional(),
|
|
97
|
-
then: jsonSchema7Schema.optional(),
|
|
98
|
-
else: jsonSchema7Schema.optional(),
|
|
99
|
-
// Format
|
|
100
|
-
format: import_zod.z.string().optional(),
|
|
101
|
-
// Default
|
|
102
|
-
default: import_zod.z.unknown().optional(),
|
|
103
|
-
// FormSpec extensions
|
|
104
|
-
"x-formspec-source": import_zod.z.string().optional(),
|
|
105
|
-
"x-formspec-params": import_zod.z.array(import_zod.z.string()).readonly().optional(),
|
|
106
|
-
"x-formspec-schemaSource": import_zod.z.string().optional()
|
|
107
|
-
}).passthrough()
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
// src/json-schema/generator.ts
|
|
111
|
-
var import_zod2 = require("zod");
|
|
112
|
-
function parseOrThrow(schema, value, label) {
|
|
113
|
-
try {
|
|
114
|
-
return schema.parse(value);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
if (error instanceof import_zod2.z.ZodError) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Generated ${label} failed validation:
|
|
119
|
-
${error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
49
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
50
|
+
var import_core = require("@formspec/core");
|
|
51
|
+
var CHAIN_DSL_PROVENANCE = {
|
|
52
|
+
surface: "chain-dsl",
|
|
53
|
+
file: "",
|
|
54
|
+
line: 0,
|
|
55
|
+
column: 0
|
|
56
|
+
};
|
|
57
|
+
function isGroup(el) {
|
|
58
|
+
return el._type === "group";
|
|
124
59
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
60
|
+
function isConditional(el) {
|
|
61
|
+
return el._type === "conditional";
|
|
62
|
+
}
|
|
63
|
+
function isField(el) {
|
|
64
|
+
return el._type === "field";
|
|
65
|
+
}
|
|
66
|
+
function canonicalizeChainDSL(form) {
|
|
130
67
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
68
|
+
kind: "form-ir",
|
|
69
|
+
irVersion: import_core.IR_VERSION,
|
|
70
|
+
elements: canonicalizeElements(form.elements),
|
|
71
|
+
typeRegistry: {},
|
|
72
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
134
73
|
};
|
|
135
74
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
75
|
+
function canonicalizeElements(elements) {
|
|
76
|
+
return elements.map(canonicalizeElement);
|
|
77
|
+
}
|
|
78
|
+
function canonicalizeElement(element) {
|
|
79
|
+
if (isField(element)) {
|
|
80
|
+
return canonicalizeField(element);
|
|
81
|
+
}
|
|
82
|
+
if (isGroup(element)) {
|
|
83
|
+
return canonicalizeGroup(element);
|
|
140
84
|
}
|
|
85
|
+
if (isConditional(element)) {
|
|
86
|
+
return canonicalizeConditional(element);
|
|
87
|
+
}
|
|
88
|
+
const _exhaustive = element;
|
|
89
|
+
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
90
|
+
}
|
|
91
|
+
function canonicalizeField(field) {
|
|
141
92
|
switch (field._field) {
|
|
142
93
|
case "text":
|
|
143
|
-
return
|
|
94
|
+
return canonicalizeTextField(field);
|
|
144
95
|
case "number":
|
|
145
|
-
return
|
|
146
|
-
...base,
|
|
147
|
-
type: "number",
|
|
148
|
-
...field.min !== void 0 && { minimum: field.min },
|
|
149
|
-
...field.max !== void 0 && { maximum: field.max }
|
|
150
|
-
};
|
|
96
|
+
return canonicalizeNumberField(field);
|
|
151
97
|
case "boolean":
|
|
152
|
-
return
|
|
153
|
-
case "enum":
|
|
154
|
-
|
|
155
|
-
const isObjectOptions = opts.length > 0 && opts.every(
|
|
156
|
-
(opt) => typeof opt === "object" && "id" in opt && "label" in opt
|
|
157
|
-
);
|
|
158
|
-
if (isObjectOptions) {
|
|
159
|
-
return {
|
|
160
|
-
...base,
|
|
161
|
-
type: "string",
|
|
162
|
-
oneOf: opts.map((o) => ({
|
|
163
|
-
const: o.id,
|
|
164
|
-
title: o.label
|
|
165
|
-
}))
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
return { ...base, type: "string", enum: opts };
|
|
169
|
-
}
|
|
98
|
+
return canonicalizeBooleanField(field);
|
|
99
|
+
case "enum":
|
|
100
|
+
return canonicalizeStaticEnumField(field);
|
|
170
101
|
case "dynamic_enum":
|
|
171
|
-
return
|
|
172
|
-
...base,
|
|
173
|
-
type: "string",
|
|
174
|
-
"x-formspec-source": field.source,
|
|
175
|
-
...field.params !== void 0 && field.params.length > 0 && { "x-formspec-params": field.params }
|
|
176
|
-
};
|
|
102
|
+
return canonicalizeDynamicEnumField(field);
|
|
177
103
|
case "dynamic_schema":
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
-
case "array": {
|
|
185
|
-
const arrayField = field;
|
|
186
|
-
return {
|
|
187
|
-
...base,
|
|
188
|
-
type: "array",
|
|
189
|
-
items: generateNestedSchema(arrayField.items),
|
|
190
|
-
...arrayField.minItems !== void 0 && { minItems: arrayField.minItems },
|
|
191
|
-
...arrayField.maxItems !== void 0 && { maxItems: arrayField.maxItems }
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
case "object": {
|
|
195
|
-
const objectField = field;
|
|
196
|
-
const nestedSchema = generateNestedSchema(objectField.properties);
|
|
197
|
-
return {
|
|
198
|
-
...base,
|
|
199
|
-
...nestedSchema
|
|
200
|
-
};
|
|
201
|
-
}
|
|
104
|
+
return canonicalizeDynamicSchemaField(field);
|
|
105
|
+
case "array":
|
|
106
|
+
return canonicalizeArrayField(field);
|
|
107
|
+
case "object":
|
|
108
|
+
return canonicalizeObjectField(field);
|
|
202
109
|
default: {
|
|
203
110
|
const _exhaustive = field;
|
|
204
|
-
|
|
111
|
+
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
205
112
|
}
|
|
206
113
|
}
|
|
207
114
|
}
|
|
208
|
-
function
|
|
115
|
+
function canonicalizeTextField(field) {
|
|
116
|
+
const type = { kind: "primitive", primitiveKind: "string" };
|
|
117
|
+
return buildFieldNode(
|
|
118
|
+
field.name,
|
|
119
|
+
type,
|
|
120
|
+
field.required,
|
|
121
|
+
buildAnnotations(field.label, field.placeholder)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
function canonicalizeNumberField(field) {
|
|
125
|
+
const type = { kind: "primitive", primitiveKind: "number" };
|
|
126
|
+
const constraints = [];
|
|
127
|
+
if (field.min !== void 0) {
|
|
128
|
+
const c = {
|
|
129
|
+
kind: "constraint",
|
|
130
|
+
constraintKind: "minimum",
|
|
131
|
+
value: field.min,
|
|
132
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
133
|
+
};
|
|
134
|
+
constraints.push(c);
|
|
135
|
+
}
|
|
136
|
+
if (field.max !== void 0) {
|
|
137
|
+
const c = {
|
|
138
|
+
kind: "constraint",
|
|
139
|
+
constraintKind: "maximum",
|
|
140
|
+
value: field.max,
|
|
141
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
142
|
+
};
|
|
143
|
+
constraints.push(c);
|
|
144
|
+
}
|
|
145
|
+
return buildFieldNode(
|
|
146
|
+
field.name,
|
|
147
|
+
type,
|
|
148
|
+
field.required,
|
|
149
|
+
buildAnnotations(field.label),
|
|
150
|
+
constraints
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function canonicalizeBooleanField(field) {
|
|
154
|
+
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
155
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
156
|
+
}
|
|
157
|
+
function canonicalizeStaticEnumField(field) {
|
|
158
|
+
const members = field.options.map((opt) => {
|
|
159
|
+
if (typeof opt === "string") {
|
|
160
|
+
return { value: opt };
|
|
161
|
+
}
|
|
162
|
+
return { value: opt.id, displayName: opt.label };
|
|
163
|
+
});
|
|
164
|
+
const type = { kind: "enum", members };
|
|
165
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
166
|
+
}
|
|
167
|
+
function canonicalizeDynamicEnumField(field) {
|
|
168
|
+
const type = {
|
|
169
|
+
kind: "dynamic",
|
|
170
|
+
dynamicKind: "enum",
|
|
171
|
+
sourceKey: field.source,
|
|
172
|
+
parameterFields: field.params ? [...field.params] : []
|
|
173
|
+
};
|
|
174
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
175
|
+
}
|
|
176
|
+
function canonicalizeDynamicSchemaField(field) {
|
|
177
|
+
const type = {
|
|
178
|
+
kind: "dynamic",
|
|
179
|
+
dynamicKind: "schema",
|
|
180
|
+
sourceKey: field.schemaSource,
|
|
181
|
+
parameterFields: []
|
|
182
|
+
};
|
|
183
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
184
|
+
}
|
|
185
|
+
function canonicalizeArrayField(field) {
|
|
186
|
+
const itemProperties = buildObjectProperties(field.items);
|
|
187
|
+
const itemsType = {
|
|
188
|
+
kind: "object",
|
|
189
|
+
properties: itemProperties,
|
|
190
|
+
additionalProperties: false
|
|
191
|
+
};
|
|
192
|
+
const type = { kind: "array", items: itemsType };
|
|
193
|
+
const constraints = [];
|
|
194
|
+
if (field.minItems !== void 0) {
|
|
195
|
+
const c = {
|
|
196
|
+
kind: "constraint",
|
|
197
|
+
constraintKind: "minItems",
|
|
198
|
+
value: field.minItems,
|
|
199
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
200
|
+
};
|
|
201
|
+
constraints.push(c);
|
|
202
|
+
}
|
|
203
|
+
if (field.maxItems !== void 0) {
|
|
204
|
+
const c = {
|
|
205
|
+
kind: "constraint",
|
|
206
|
+
constraintKind: "maxItems",
|
|
207
|
+
value: field.maxItems,
|
|
208
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
209
|
+
};
|
|
210
|
+
constraints.push(c);
|
|
211
|
+
}
|
|
212
|
+
return buildFieldNode(
|
|
213
|
+
field.name,
|
|
214
|
+
type,
|
|
215
|
+
field.required,
|
|
216
|
+
buildAnnotations(field.label),
|
|
217
|
+
constraints
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
function canonicalizeObjectField(field) {
|
|
221
|
+
const properties = buildObjectProperties(field.properties);
|
|
222
|
+
const type = {
|
|
223
|
+
kind: "object",
|
|
224
|
+
properties,
|
|
225
|
+
additionalProperties: false
|
|
226
|
+
};
|
|
227
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
228
|
+
}
|
|
229
|
+
function canonicalizeGroup(g) {
|
|
230
|
+
return {
|
|
231
|
+
kind: "group",
|
|
232
|
+
label: g.label,
|
|
233
|
+
elements: canonicalizeElements(g.elements),
|
|
234
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function canonicalizeConditional(c) {
|
|
238
|
+
return {
|
|
239
|
+
kind: "conditional",
|
|
240
|
+
fieldName: c.field,
|
|
241
|
+
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
242
|
+
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
243
|
+
value: assertJsonValue(c.value),
|
|
244
|
+
elements: canonicalizeElements(c.elements),
|
|
245
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function assertJsonValue(v) {
|
|
249
|
+
if (v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
250
|
+
return v;
|
|
251
|
+
}
|
|
252
|
+
if (Array.isArray(v)) {
|
|
253
|
+
return v.map(assertJsonValue);
|
|
254
|
+
}
|
|
255
|
+
if (typeof v === "object") {
|
|
256
|
+
const result = {};
|
|
257
|
+
for (const [key, val] of Object.entries(v)) {
|
|
258
|
+
result[key] = assertJsonValue(val);
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
263
|
+
}
|
|
264
|
+
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
265
|
+
return {
|
|
266
|
+
kind: "field",
|
|
267
|
+
name,
|
|
268
|
+
type,
|
|
269
|
+
required: required === true,
|
|
270
|
+
constraints,
|
|
271
|
+
annotations,
|
|
272
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function buildAnnotations(label, placeholder) {
|
|
276
|
+
const annotations = [];
|
|
277
|
+
if (label !== void 0) {
|
|
278
|
+
const a = {
|
|
279
|
+
kind: "annotation",
|
|
280
|
+
annotationKind: "displayName",
|
|
281
|
+
value: label,
|
|
282
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
283
|
+
};
|
|
284
|
+
annotations.push(a);
|
|
285
|
+
}
|
|
286
|
+
if (placeholder !== void 0) {
|
|
287
|
+
const a = {
|
|
288
|
+
kind: "annotation",
|
|
289
|
+
annotationKind: "placeholder",
|
|
290
|
+
value: placeholder,
|
|
291
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
292
|
+
};
|
|
293
|
+
annotations.push(a);
|
|
294
|
+
}
|
|
295
|
+
return annotations;
|
|
296
|
+
}
|
|
297
|
+
function buildObjectProperties(elements, insideConditional = false) {
|
|
298
|
+
const properties = [];
|
|
299
|
+
for (const el of elements) {
|
|
300
|
+
if (isField(el)) {
|
|
301
|
+
const fieldNode = canonicalizeField(el);
|
|
302
|
+
properties.push({
|
|
303
|
+
name: fieldNode.name,
|
|
304
|
+
type: fieldNode.type,
|
|
305
|
+
// Fields inside a conditional branch are always optional in the
|
|
306
|
+
// data schema, regardless of their `required` flag — the condition
|
|
307
|
+
// may not be met, so the field may be absent.
|
|
308
|
+
optional: insideConditional || !fieldNode.required,
|
|
309
|
+
constraints: fieldNode.constraints,
|
|
310
|
+
annotations: fieldNode.annotations,
|
|
311
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
312
|
+
});
|
|
313
|
+
} else if (isGroup(el)) {
|
|
314
|
+
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
315
|
+
} else if (isConditional(el)) {
|
|
316
|
+
properties.push(...buildObjectProperties(el.elements, true));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return properties;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
323
|
+
var import_core2 = require("@formspec/core");
|
|
324
|
+
|
|
325
|
+
// src/json-schema/ir-generator.ts
|
|
326
|
+
function makeContext() {
|
|
327
|
+
return { defs: {} };
|
|
328
|
+
}
|
|
329
|
+
function generateJsonSchemaFromIR(ir) {
|
|
330
|
+
const ctx = makeContext();
|
|
331
|
+
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
332
|
+
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
333
|
+
}
|
|
334
|
+
const properties = {};
|
|
335
|
+
const required = [];
|
|
336
|
+
collectFields(ir.elements, properties, required, ctx);
|
|
337
|
+
const uniqueRequired = [...new Set(required)];
|
|
338
|
+
const result = {
|
|
339
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
340
|
+
type: "object",
|
|
341
|
+
properties,
|
|
342
|
+
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
343
|
+
};
|
|
344
|
+
if (Object.keys(ctx.defs).length > 0) {
|
|
345
|
+
result.$defs = ctx.defs;
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
function collectFields(elements, properties, required, ctx) {
|
|
209
350
|
for (const element of elements) {
|
|
210
|
-
switch (element.
|
|
351
|
+
switch (element.kind) {
|
|
211
352
|
case "field":
|
|
212
|
-
properties[element.name] =
|
|
213
|
-
if (element.required
|
|
353
|
+
properties[element.name] = generateFieldSchema(element, ctx);
|
|
354
|
+
if (element.required) {
|
|
214
355
|
required.push(element.name);
|
|
215
356
|
}
|
|
216
357
|
break;
|
|
217
358
|
case "group":
|
|
218
|
-
collectFields(element.elements, properties, required);
|
|
359
|
+
collectFields(element.elements, properties, required, ctx);
|
|
219
360
|
break;
|
|
220
361
|
case "conditional":
|
|
221
|
-
collectFields(
|
|
222
|
-
element.elements,
|
|
223
|
-
properties,
|
|
224
|
-
required
|
|
225
|
-
);
|
|
362
|
+
collectFields(element.elements, properties, required, ctx);
|
|
226
363
|
break;
|
|
364
|
+
default: {
|
|
365
|
+
const _exhaustive = element;
|
|
366
|
+
void _exhaustive;
|
|
367
|
+
}
|
|
227
368
|
}
|
|
228
369
|
}
|
|
229
370
|
}
|
|
230
|
-
function
|
|
371
|
+
function generateFieldSchema(field, ctx) {
|
|
372
|
+
const schema = generateTypeNode(field.type, ctx);
|
|
373
|
+
applyConstraints(schema, field.constraints);
|
|
374
|
+
applyAnnotations(schema, field.annotations);
|
|
375
|
+
return schema;
|
|
376
|
+
}
|
|
377
|
+
function generateTypeNode(type, ctx) {
|
|
378
|
+
switch (type.kind) {
|
|
379
|
+
case "primitive":
|
|
380
|
+
return generatePrimitiveType(type);
|
|
381
|
+
case "enum":
|
|
382
|
+
return generateEnumType(type);
|
|
383
|
+
case "array":
|
|
384
|
+
return generateArrayType(type, ctx);
|
|
385
|
+
case "object":
|
|
386
|
+
return generateObjectType(type, ctx);
|
|
387
|
+
case "union":
|
|
388
|
+
return generateUnionType(type, ctx);
|
|
389
|
+
case "reference":
|
|
390
|
+
return generateReferenceType(type);
|
|
391
|
+
case "dynamic":
|
|
392
|
+
return generateDynamicType(type);
|
|
393
|
+
case "custom":
|
|
394
|
+
return generateCustomType(type);
|
|
395
|
+
default: {
|
|
396
|
+
const _exhaustive = type;
|
|
397
|
+
return _exhaustive;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function generatePrimitiveType(type) {
|
|
402
|
+
return { type: type.primitiveKind };
|
|
403
|
+
}
|
|
404
|
+
function generateEnumType(type) {
|
|
405
|
+
const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
|
|
406
|
+
if (hasDisplayNames) {
|
|
407
|
+
return {
|
|
408
|
+
oneOf: type.members.map((m) => {
|
|
409
|
+
const entry = { const: m.value };
|
|
410
|
+
if (m.displayName !== void 0) {
|
|
411
|
+
entry.title = m.displayName;
|
|
412
|
+
}
|
|
413
|
+
return entry;
|
|
414
|
+
})
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return { enum: type.members.map((m) => m.value) };
|
|
418
|
+
}
|
|
419
|
+
function generateArrayType(type, ctx) {
|
|
420
|
+
return {
|
|
421
|
+
type: "array",
|
|
422
|
+
items: generateTypeNode(type.items, ctx)
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function generateObjectType(type, ctx) {
|
|
231
426
|
const properties = {};
|
|
232
427
|
const required = [];
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
428
|
+
for (const prop of type.properties) {
|
|
429
|
+
properties[prop.name] = generatePropertySchema(prop, ctx);
|
|
430
|
+
if (!prop.optional) {
|
|
431
|
+
required.push(prop.name);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const schema = { type: "object", properties };
|
|
435
|
+
if (required.length > 0) {
|
|
436
|
+
schema.required = required;
|
|
437
|
+
}
|
|
438
|
+
if (!type.additionalProperties) {
|
|
439
|
+
schema.additionalProperties = false;
|
|
440
|
+
}
|
|
441
|
+
return schema;
|
|
442
|
+
}
|
|
443
|
+
function generatePropertySchema(prop, ctx) {
|
|
444
|
+
const schema = generateTypeNode(prop.type, ctx);
|
|
445
|
+
applyConstraints(schema, prop.constraints);
|
|
446
|
+
applyAnnotations(schema, prop.annotations);
|
|
447
|
+
return schema;
|
|
448
|
+
}
|
|
449
|
+
function generateUnionType(type, ctx) {
|
|
450
|
+
if (isBooleanUnion(type)) {
|
|
451
|
+
return { type: "boolean" };
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
anyOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function isBooleanUnion(type) {
|
|
458
|
+
if (type.members.length !== 2) return false;
|
|
459
|
+
const kinds = type.members.map((m) => m.kind);
|
|
460
|
+
return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
|
|
461
|
+
}
|
|
462
|
+
function generateReferenceType(type) {
|
|
463
|
+
return { $ref: `#/$defs/${type.name}` };
|
|
464
|
+
}
|
|
465
|
+
function generateDynamicType(type) {
|
|
466
|
+
if (type.dynamicKind === "enum") {
|
|
467
|
+
const schema = {
|
|
468
|
+
type: "string",
|
|
469
|
+
"x-formspec-source": type.sourceKey
|
|
470
|
+
};
|
|
471
|
+
if (type.parameterFields.length > 0) {
|
|
472
|
+
schema["x-formspec-params"] = [...type.parameterFields];
|
|
473
|
+
}
|
|
474
|
+
return schema;
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
237
477
|
type: "object",
|
|
238
|
-
|
|
239
|
-
|
|
478
|
+
additionalProperties: true,
|
|
479
|
+
"x-formspec-schemaSource": type.sourceKey
|
|
240
480
|
};
|
|
241
|
-
|
|
481
|
+
}
|
|
482
|
+
function generateCustomType(_type) {
|
|
483
|
+
return { type: "object" };
|
|
484
|
+
}
|
|
485
|
+
function applyConstraints(schema, constraints) {
|
|
486
|
+
for (const constraint of constraints) {
|
|
487
|
+
switch (constraint.constraintKind) {
|
|
488
|
+
case "minimum":
|
|
489
|
+
schema.minimum = constraint.value;
|
|
490
|
+
break;
|
|
491
|
+
case "maximum":
|
|
492
|
+
schema.maximum = constraint.value;
|
|
493
|
+
break;
|
|
494
|
+
case "exclusiveMinimum":
|
|
495
|
+
schema.exclusiveMinimum = constraint.value;
|
|
496
|
+
break;
|
|
497
|
+
case "exclusiveMaximum":
|
|
498
|
+
schema.exclusiveMaximum = constraint.value;
|
|
499
|
+
break;
|
|
500
|
+
case "multipleOf": {
|
|
501
|
+
const { value } = constraint;
|
|
502
|
+
if (value === 1 && schema.type === "number") {
|
|
503
|
+
schema.type = "integer";
|
|
504
|
+
} else {
|
|
505
|
+
schema.multipleOf = value;
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
case "minLength":
|
|
510
|
+
schema.minLength = constraint.value;
|
|
511
|
+
break;
|
|
512
|
+
case "maxLength":
|
|
513
|
+
schema.maxLength = constraint.value;
|
|
514
|
+
break;
|
|
515
|
+
case "minItems":
|
|
516
|
+
schema.minItems = constraint.value;
|
|
517
|
+
break;
|
|
518
|
+
case "maxItems":
|
|
519
|
+
schema.maxItems = constraint.value;
|
|
520
|
+
break;
|
|
521
|
+
case "pattern":
|
|
522
|
+
schema.pattern = constraint.pattern;
|
|
523
|
+
break;
|
|
524
|
+
case "uniqueItems":
|
|
525
|
+
schema.uniqueItems = constraint.value;
|
|
526
|
+
break;
|
|
527
|
+
case "allowedMembers":
|
|
528
|
+
break;
|
|
529
|
+
case "custom":
|
|
530
|
+
break;
|
|
531
|
+
default: {
|
|
532
|
+
const _exhaustive = constraint;
|
|
533
|
+
void _exhaustive;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function applyAnnotations(schema, annotations) {
|
|
539
|
+
for (const annotation of annotations) {
|
|
540
|
+
switch (annotation.annotationKind) {
|
|
541
|
+
case "displayName":
|
|
542
|
+
schema.title = annotation.value;
|
|
543
|
+
break;
|
|
544
|
+
case "description":
|
|
545
|
+
schema.description = annotation.value;
|
|
546
|
+
break;
|
|
547
|
+
case "defaultValue":
|
|
548
|
+
schema.default = annotation.value;
|
|
549
|
+
break;
|
|
550
|
+
case "deprecated":
|
|
551
|
+
schema.deprecated = true;
|
|
552
|
+
break;
|
|
553
|
+
case "placeholder":
|
|
554
|
+
break;
|
|
555
|
+
case "formatHint":
|
|
556
|
+
break;
|
|
557
|
+
case "custom":
|
|
558
|
+
break;
|
|
559
|
+
default: {
|
|
560
|
+
const _exhaustive = annotation;
|
|
561
|
+
void _exhaustive;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/json-schema/generator.ts
|
|
568
|
+
function generateJsonSchema(form) {
|
|
569
|
+
const ir = canonicalizeChainDSL(form);
|
|
570
|
+
return generateJsonSchemaFromIR(ir);
|
|
242
571
|
}
|
|
243
572
|
|
|
244
573
|
// src/ui-schema/schema.ts
|
|
245
|
-
var
|
|
246
|
-
var jsonPointerSchema =
|
|
247
|
-
var ruleEffectSchema =
|
|
248
|
-
var uiSchemaElementTypeSchema =
|
|
574
|
+
var import_zod = require("zod");
|
|
575
|
+
var jsonPointerSchema = import_zod.z.string();
|
|
576
|
+
var ruleEffectSchema = import_zod.z.enum(["SHOW", "HIDE", "ENABLE", "DISABLE"]);
|
|
577
|
+
var uiSchemaElementTypeSchema = import_zod.z.enum([
|
|
249
578
|
"Control",
|
|
250
579
|
"VerticalLayout",
|
|
251
580
|
"HorizontalLayout",
|
|
@@ -254,32 +583,32 @@ var uiSchemaElementTypeSchema = import_zod3.z.enum([
|
|
|
254
583
|
"Category",
|
|
255
584
|
"Label"
|
|
256
585
|
]);
|
|
257
|
-
var ruleConditionSchema =
|
|
258
|
-
() =>
|
|
259
|
-
const:
|
|
260
|
-
enum:
|
|
261
|
-
type:
|
|
586
|
+
var ruleConditionSchema = import_zod.z.lazy(
|
|
587
|
+
() => import_zod.z.object({
|
|
588
|
+
const: import_zod.z.unknown().optional(),
|
|
589
|
+
enum: import_zod.z.array(import_zod.z.unknown()).readonly().optional(),
|
|
590
|
+
type: import_zod.z.string().optional(),
|
|
262
591
|
not: ruleConditionSchema.optional(),
|
|
263
|
-
minimum:
|
|
264
|
-
maximum:
|
|
265
|
-
exclusiveMinimum:
|
|
266
|
-
exclusiveMaximum:
|
|
267
|
-
minLength:
|
|
268
|
-
properties:
|
|
269
|
-
required:
|
|
270
|
-
allOf:
|
|
592
|
+
minimum: import_zod.z.number().optional(),
|
|
593
|
+
maximum: import_zod.z.number().optional(),
|
|
594
|
+
exclusiveMinimum: import_zod.z.number().optional(),
|
|
595
|
+
exclusiveMaximum: import_zod.z.number().optional(),
|
|
596
|
+
minLength: import_zod.z.number().optional(),
|
|
597
|
+
properties: import_zod.z.record(import_zod.z.string(), ruleConditionSchema).optional(),
|
|
598
|
+
required: import_zod.z.array(import_zod.z.string()).optional(),
|
|
599
|
+
allOf: import_zod.z.array(ruleConditionSchema).optional()
|
|
271
600
|
}).strict()
|
|
272
601
|
);
|
|
273
|
-
var schemaBasedConditionSchema =
|
|
602
|
+
var schemaBasedConditionSchema = import_zod.z.object({
|
|
274
603
|
scope: jsonPointerSchema,
|
|
275
604
|
schema: ruleConditionSchema
|
|
276
605
|
}).strict();
|
|
277
|
-
var ruleSchema =
|
|
606
|
+
var ruleSchema = import_zod.z.object({
|
|
278
607
|
effect: ruleEffectSchema,
|
|
279
608
|
condition: schemaBasedConditionSchema
|
|
280
609
|
}).strict();
|
|
281
|
-
var uiSchemaElementSchema =
|
|
282
|
-
() =>
|
|
610
|
+
var uiSchemaElementSchema = import_zod.z.lazy(
|
|
611
|
+
() => import_zod.z.union([
|
|
283
612
|
controlSchema,
|
|
284
613
|
verticalLayoutSchema,
|
|
285
614
|
horizontalLayoutSchema,
|
|
@@ -289,73 +618,73 @@ var uiSchemaElementSchema = import_zod3.z.lazy(
|
|
|
289
618
|
labelElementSchema
|
|
290
619
|
])
|
|
291
620
|
);
|
|
292
|
-
var controlSchema =
|
|
293
|
-
type:
|
|
621
|
+
var controlSchema = import_zod.z.object({
|
|
622
|
+
type: import_zod.z.literal("Control"),
|
|
294
623
|
scope: jsonPointerSchema,
|
|
295
|
-
label:
|
|
624
|
+
label: import_zod.z.union([import_zod.z.string(), import_zod.z.literal(false)]).optional(),
|
|
296
625
|
rule: ruleSchema.optional(),
|
|
297
|
-
options:
|
|
626
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
298
627
|
}).passthrough();
|
|
299
|
-
var verticalLayoutSchema =
|
|
300
|
-
() =>
|
|
301
|
-
type:
|
|
302
|
-
elements:
|
|
628
|
+
var verticalLayoutSchema = import_zod.z.lazy(
|
|
629
|
+
() => import_zod.z.object({
|
|
630
|
+
type: import_zod.z.literal("VerticalLayout"),
|
|
631
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
303
632
|
rule: ruleSchema.optional(),
|
|
304
|
-
options:
|
|
633
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
305
634
|
}).passthrough()
|
|
306
635
|
);
|
|
307
|
-
var horizontalLayoutSchema =
|
|
308
|
-
() =>
|
|
309
|
-
type:
|
|
310
|
-
elements:
|
|
636
|
+
var horizontalLayoutSchema = import_zod.z.lazy(
|
|
637
|
+
() => import_zod.z.object({
|
|
638
|
+
type: import_zod.z.literal("HorizontalLayout"),
|
|
639
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
311
640
|
rule: ruleSchema.optional(),
|
|
312
|
-
options:
|
|
641
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
313
642
|
}).passthrough()
|
|
314
643
|
);
|
|
315
|
-
var groupLayoutSchema =
|
|
316
|
-
() =>
|
|
317
|
-
type:
|
|
318
|
-
label:
|
|
319
|
-
elements:
|
|
644
|
+
var groupLayoutSchema = import_zod.z.lazy(
|
|
645
|
+
() => import_zod.z.object({
|
|
646
|
+
type: import_zod.z.literal("Group"),
|
|
647
|
+
label: import_zod.z.string(),
|
|
648
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
320
649
|
rule: ruleSchema.optional(),
|
|
321
|
-
options:
|
|
650
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
322
651
|
}).passthrough()
|
|
323
652
|
);
|
|
324
|
-
var categorySchema =
|
|
325
|
-
() =>
|
|
326
|
-
type:
|
|
327
|
-
label:
|
|
328
|
-
elements:
|
|
653
|
+
var categorySchema = import_zod.z.lazy(
|
|
654
|
+
() => import_zod.z.object({
|
|
655
|
+
type: import_zod.z.literal("Category"),
|
|
656
|
+
label: import_zod.z.string(),
|
|
657
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
329
658
|
rule: ruleSchema.optional(),
|
|
330
|
-
options:
|
|
659
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
331
660
|
}).passthrough()
|
|
332
661
|
);
|
|
333
|
-
var categorizationSchema =
|
|
334
|
-
() =>
|
|
335
|
-
type:
|
|
336
|
-
elements:
|
|
337
|
-
label:
|
|
662
|
+
var categorizationSchema = import_zod.z.lazy(
|
|
663
|
+
() => import_zod.z.object({
|
|
664
|
+
type: import_zod.z.literal("Categorization"),
|
|
665
|
+
elements: import_zod.z.array(categorySchema),
|
|
666
|
+
label: import_zod.z.string().optional(),
|
|
338
667
|
rule: ruleSchema.optional(),
|
|
339
|
-
options:
|
|
668
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
340
669
|
}).passthrough()
|
|
341
670
|
);
|
|
342
|
-
var labelElementSchema =
|
|
343
|
-
type:
|
|
344
|
-
text:
|
|
671
|
+
var labelElementSchema = import_zod.z.object({
|
|
672
|
+
type: import_zod.z.literal("Label"),
|
|
673
|
+
text: import_zod.z.string(),
|
|
345
674
|
rule: ruleSchema.optional(),
|
|
346
|
-
options:
|
|
675
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
347
676
|
}).passthrough();
|
|
348
|
-
var uiSchema =
|
|
349
|
-
() =>
|
|
677
|
+
var uiSchema = import_zod.z.lazy(
|
|
678
|
+
() => import_zod.z.union([verticalLayoutSchema, horizontalLayoutSchema, groupLayoutSchema, categorizationSchema])
|
|
350
679
|
);
|
|
351
680
|
|
|
352
|
-
// src/ui-schema/generator.ts
|
|
353
|
-
var
|
|
354
|
-
function
|
|
681
|
+
// src/ui-schema/ir-generator.ts
|
|
682
|
+
var import_zod2 = require("zod");
|
|
683
|
+
function parseOrThrow(schema, value, label) {
|
|
355
684
|
try {
|
|
356
685
|
return schema.parse(value);
|
|
357
686
|
} catch (error) {
|
|
358
|
-
if (error instanceof
|
|
687
|
+
if (error instanceof import_zod2.z.ZodError) {
|
|
359
688
|
throw new Error(
|
|
360
689
|
`Generated ${label} failed validation:
|
|
361
690
|
${error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
@@ -400,110 +729,64 @@ function combineRules(parentRule, childRule) {
|
|
|
400
729
|
}
|
|
401
730
|
};
|
|
402
731
|
}
|
|
403
|
-
function
|
|
732
|
+
function fieldNodeToControl(field, parentRule) {
|
|
733
|
+
const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
|
|
734
|
+
const control = {
|
|
735
|
+
type: "Control",
|
|
736
|
+
scope: fieldToScope(field.name),
|
|
737
|
+
...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
|
|
738
|
+
...parentRule !== void 0 && { rule: parentRule }
|
|
739
|
+
};
|
|
740
|
+
return control;
|
|
741
|
+
}
|
|
742
|
+
function groupNodeToLayout(group, parentRule) {
|
|
743
|
+
return {
|
|
744
|
+
type: "Group",
|
|
745
|
+
label: group.label,
|
|
746
|
+
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
747
|
+
...parentRule !== void 0 && { rule: parentRule }
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function irElementsToUiSchema(elements, parentRule) {
|
|
404
751
|
const result = [];
|
|
405
752
|
for (const element of elements) {
|
|
406
|
-
switch (element.
|
|
753
|
+
switch (element.kind) {
|
|
407
754
|
case "field": {
|
|
408
|
-
|
|
409
|
-
type: "Control",
|
|
410
|
-
scope: fieldToScope(element.name),
|
|
411
|
-
...element.label !== void 0 && { label: element.label },
|
|
412
|
-
...parentRule !== void 0 && { rule: parentRule }
|
|
413
|
-
};
|
|
414
|
-
result.push(control);
|
|
755
|
+
result.push(fieldNodeToControl(element, parentRule));
|
|
415
756
|
break;
|
|
416
757
|
}
|
|
417
758
|
case "group": {
|
|
418
|
-
|
|
419
|
-
const group = {
|
|
420
|
-
type: "Group",
|
|
421
|
-
label: groupElement.label,
|
|
422
|
-
elements: elementsToUiSchema(groupElement.elements, parentRule),
|
|
423
|
-
...parentRule !== void 0 && { rule: parentRule }
|
|
424
|
-
};
|
|
425
|
-
result.push(group);
|
|
759
|
+
result.push(groupNodeToLayout(element, parentRule));
|
|
426
760
|
break;
|
|
427
761
|
}
|
|
428
762
|
case "conditional": {
|
|
429
|
-
const
|
|
430
|
-
const newRule = createShowRule(conditionalElement.field, conditionalElement.value);
|
|
763
|
+
const newRule = createShowRule(element.fieldName, element.value);
|
|
431
764
|
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
432
|
-
const childElements =
|
|
765
|
+
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
433
766
|
result.push(...childElements);
|
|
434
767
|
break;
|
|
435
768
|
}
|
|
769
|
+
default: {
|
|
770
|
+
const _exhaustive = element;
|
|
771
|
+
void _exhaustive;
|
|
772
|
+
throw new Error("Unhandled IR element kind");
|
|
773
|
+
}
|
|
436
774
|
}
|
|
437
775
|
}
|
|
438
776
|
return result;
|
|
439
777
|
}
|
|
440
|
-
function
|
|
441
|
-
const control = {
|
|
442
|
-
type: "Control",
|
|
443
|
-
scope: `${scopePrefix}/${field.id}`
|
|
444
|
-
};
|
|
445
|
-
if (field.label !== void 0) {
|
|
446
|
-
control.label = field.label;
|
|
447
|
-
}
|
|
448
|
-
if (field.showWhen !== void 0 && typeof field.showWhen === "object" && "field" in field.showWhen && "value" in field.showWhen) {
|
|
449
|
-
const sw = field.showWhen;
|
|
450
|
-
control.rule = {
|
|
451
|
-
effect: "SHOW",
|
|
452
|
-
condition: {
|
|
453
|
-
scope: `#/properties/${sw.field}`,
|
|
454
|
-
schema: { const: sw.value }
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
return control;
|
|
459
|
-
}
|
|
460
|
-
function generateUiSchemaFromFields(fields) {
|
|
461
|
-
const groupMap = /* @__PURE__ */ new Map();
|
|
462
|
-
const orderedKeys = [];
|
|
463
|
-
const ungrouped = [];
|
|
464
|
-
for (const field of fields) {
|
|
465
|
-
const element = formSpecFieldToElement(field);
|
|
466
|
-
if (field.group !== void 0) {
|
|
467
|
-
if (!groupMap.has(field.group)) {
|
|
468
|
-
groupMap.set(field.group, []);
|
|
469
|
-
orderedKeys.push(field.group);
|
|
470
|
-
}
|
|
471
|
-
groupMap.get(field.group).push(element);
|
|
472
|
-
} else {
|
|
473
|
-
orderedKeys.push(null);
|
|
474
|
-
ungrouped.push(element);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
const elements = [];
|
|
478
|
-
let ungroupedIndex = 0;
|
|
479
|
-
for (const key of orderedKeys) {
|
|
480
|
-
if (key === null) {
|
|
481
|
-
const el = ungrouped[ungroupedIndex++];
|
|
482
|
-
if (el !== void 0) {
|
|
483
|
-
elements.push(el);
|
|
484
|
-
}
|
|
485
|
-
} else {
|
|
486
|
-
const groupElements = groupMap.get(key) ?? [];
|
|
487
|
-
const groupLayout = {
|
|
488
|
-
type: "Group",
|
|
489
|
-
label: key,
|
|
490
|
-
elements: groupElements
|
|
491
|
-
};
|
|
492
|
-
elements.push(groupLayout);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
778
|
+
function generateUiSchemaFromIR(ir) {
|
|
495
779
|
const result = {
|
|
496
780
|
type: "VerticalLayout",
|
|
497
|
-
elements
|
|
781
|
+
elements: irElementsToUiSchema(ir.elements)
|
|
498
782
|
};
|
|
499
|
-
return
|
|
783
|
+
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
500
784
|
}
|
|
785
|
+
|
|
786
|
+
// src/ui-schema/generator.ts
|
|
501
787
|
function generateUiSchema(form) {
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
elements: elementsToUiSchema(form.elements)
|
|
505
|
-
};
|
|
506
|
-
return parseOrThrow2(uiSchema, result, "UI Schema");
|
|
788
|
+
const ir = canonicalizeChainDSL(form);
|
|
789
|
+
return generateUiSchemaFromIR(ir);
|
|
507
790
|
}
|
|
508
791
|
|
|
509
792
|
// src/json-schema/types.ts
|
|
@@ -514,6 +797,373 @@ function getSchemaExtension(schema, key) {
|
|
|
514
797
|
return schema[key];
|
|
515
798
|
}
|
|
516
799
|
|
|
800
|
+
// src/json-schema/schema.ts
|
|
801
|
+
var import_zod3 = require("zod");
|
|
802
|
+
var jsonSchemaTypeSchema = import_zod3.z.enum([
|
|
803
|
+
"string",
|
|
804
|
+
"number",
|
|
805
|
+
"integer",
|
|
806
|
+
"boolean",
|
|
807
|
+
"object",
|
|
808
|
+
"array",
|
|
809
|
+
"null"
|
|
810
|
+
]);
|
|
811
|
+
var jsonSchema7Schema = import_zod3.z.lazy(
|
|
812
|
+
() => import_zod3.z.object({
|
|
813
|
+
$schema: import_zod3.z.string().optional(),
|
|
814
|
+
$id: import_zod3.z.string().optional(),
|
|
815
|
+
$ref: import_zod3.z.string().optional(),
|
|
816
|
+
// Metadata
|
|
817
|
+
title: import_zod3.z.string().optional(),
|
|
818
|
+
description: import_zod3.z.string().optional(),
|
|
819
|
+
deprecated: import_zod3.z.boolean().optional(),
|
|
820
|
+
// Type
|
|
821
|
+
type: import_zod3.z.union([jsonSchemaTypeSchema, import_zod3.z.array(jsonSchemaTypeSchema)]).optional(),
|
|
822
|
+
// String validation
|
|
823
|
+
minLength: import_zod3.z.number().optional(),
|
|
824
|
+
maxLength: import_zod3.z.number().optional(),
|
|
825
|
+
pattern: import_zod3.z.string().optional(),
|
|
826
|
+
// Number validation
|
|
827
|
+
minimum: import_zod3.z.number().optional(),
|
|
828
|
+
maximum: import_zod3.z.number().optional(),
|
|
829
|
+
exclusiveMinimum: import_zod3.z.number().optional(),
|
|
830
|
+
exclusiveMaximum: import_zod3.z.number().optional(),
|
|
831
|
+
// Enum
|
|
832
|
+
enum: import_zod3.z.array(import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean(), import_zod3.z.null()])).readonly().optional(),
|
|
833
|
+
const: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean(), import_zod3.z.null()]).optional(),
|
|
834
|
+
// Object
|
|
835
|
+
properties: import_zod3.z.record(import_zod3.z.string(), jsonSchema7Schema).optional(),
|
|
836
|
+
required: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
837
|
+
additionalProperties: import_zod3.z.union([import_zod3.z.boolean(), jsonSchema7Schema]).optional(),
|
|
838
|
+
// Array
|
|
839
|
+
items: import_zod3.z.union([jsonSchema7Schema, import_zod3.z.array(jsonSchema7Schema)]).optional(),
|
|
840
|
+
minItems: import_zod3.z.number().optional(),
|
|
841
|
+
maxItems: import_zod3.z.number().optional(),
|
|
842
|
+
// Composition
|
|
843
|
+
allOf: import_zod3.z.array(jsonSchema7Schema).optional(),
|
|
844
|
+
anyOf: import_zod3.z.array(jsonSchema7Schema).optional(),
|
|
845
|
+
oneOf: import_zod3.z.array(jsonSchema7Schema).optional(),
|
|
846
|
+
not: jsonSchema7Schema.optional(),
|
|
847
|
+
// Conditional
|
|
848
|
+
if: jsonSchema7Schema.optional(),
|
|
849
|
+
then: jsonSchema7Schema.optional(),
|
|
850
|
+
else: jsonSchema7Schema.optional(),
|
|
851
|
+
// Format
|
|
852
|
+
format: import_zod3.z.string().optional(),
|
|
853
|
+
// Default
|
|
854
|
+
default: import_zod3.z.unknown().optional(),
|
|
855
|
+
// FormSpec extensions
|
|
856
|
+
"x-formspec-source": import_zod3.z.string().optional(),
|
|
857
|
+
"x-formspec-params": import_zod3.z.array(import_zod3.z.string()).readonly().optional(),
|
|
858
|
+
"x-formspec-schemaSource": import_zod3.z.string().optional()
|
|
859
|
+
}).passthrough()
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
// src/validate/constraint-validator.ts
|
|
863
|
+
function makeCode(ctx, category, number) {
|
|
864
|
+
return `${ctx.vendorPrefix}-${category}-${String(number).padStart(3, "0")}`;
|
|
865
|
+
}
|
|
866
|
+
function addContradiction(ctx, message, primary, related) {
|
|
867
|
+
ctx.diagnostics.push({
|
|
868
|
+
code: makeCode(ctx, "CONTRADICTION", 1),
|
|
869
|
+
message,
|
|
870
|
+
severity: "error",
|
|
871
|
+
primaryLocation: primary,
|
|
872
|
+
relatedLocations: [related]
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
function addTypeMismatch(ctx, message, primary) {
|
|
876
|
+
ctx.diagnostics.push({
|
|
877
|
+
code: makeCode(ctx, "TYPE_MISMATCH", 1),
|
|
878
|
+
message,
|
|
879
|
+
severity: "error",
|
|
880
|
+
primaryLocation: primary,
|
|
881
|
+
relatedLocations: []
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
function addUnknownExtension(ctx, message, primary) {
|
|
885
|
+
ctx.diagnostics.push({
|
|
886
|
+
code: makeCode(ctx, "UNKNOWN_EXTENSION", 1),
|
|
887
|
+
message,
|
|
888
|
+
severity: "warning",
|
|
889
|
+
primaryLocation: primary,
|
|
890
|
+
relatedLocations: []
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
function findNumeric(constraints, constraintKind) {
|
|
894
|
+
return constraints.find(
|
|
895
|
+
(c) => c.constraintKind === constraintKind
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
function findLength(constraints, constraintKind) {
|
|
899
|
+
return constraints.find(
|
|
900
|
+
(c) => c.constraintKind === constraintKind
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
function findAllowedMembers(constraints) {
|
|
904
|
+
return constraints.filter(
|
|
905
|
+
(c) => c.constraintKind === "allowedMembers"
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
function checkNumericContradictions(ctx, fieldName, constraints) {
|
|
909
|
+
const min = findNumeric(constraints, "minimum");
|
|
910
|
+
const max = findNumeric(constraints, "maximum");
|
|
911
|
+
const exMin = findNumeric(constraints, "exclusiveMinimum");
|
|
912
|
+
const exMax = findNumeric(constraints, "exclusiveMaximum");
|
|
913
|
+
if (min !== void 0 && max !== void 0 && min.value > max.value) {
|
|
914
|
+
addContradiction(
|
|
915
|
+
ctx,
|
|
916
|
+
`Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
|
|
917
|
+
min.provenance,
|
|
918
|
+
max.provenance
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
|
|
922
|
+
addContradiction(
|
|
923
|
+
ctx,
|
|
924
|
+
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
|
|
925
|
+
exMin.provenance,
|
|
926
|
+
max.provenance
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
|
|
930
|
+
addContradiction(
|
|
931
|
+
ctx,
|
|
932
|
+
`Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
933
|
+
min.provenance,
|
|
934
|
+
exMax.provenance
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
|
|
938
|
+
addContradiction(
|
|
939
|
+
ctx,
|
|
940
|
+
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
941
|
+
exMin.provenance,
|
|
942
|
+
exMax.provenance
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function checkLengthContradictions(ctx, fieldName, constraints) {
|
|
947
|
+
const minLen = findLength(constraints, "minLength");
|
|
948
|
+
const maxLen = findLength(constraints, "maxLength");
|
|
949
|
+
if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
|
|
950
|
+
addContradiction(
|
|
951
|
+
ctx,
|
|
952
|
+
`Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
|
|
953
|
+
minLen.provenance,
|
|
954
|
+
maxLen.provenance
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
const minItems = findLength(constraints, "minItems");
|
|
958
|
+
const maxItems = findLength(constraints, "maxItems");
|
|
959
|
+
if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
|
|
960
|
+
addContradiction(
|
|
961
|
+
ctx,
|
|
962
|
+
`Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
|
|
963
|
+
minItems.provenance,
|
|
964
|
+
maxItems.provenance
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function checkAllowedMembersContradiction(ctx, fieldName, constraints) {
|
|
969
|
+
const members = findAllowedMembers(constraints);
|
|
970
|
+
if (members.length < 2) return;
|
|
971
|
+
const firstSet = new Set(members[0]?.members ?? []);
|
|
972
|
+
for (let i = 1; i < members.length; i++) {
|
|
973
|
+
const current = members[i];
|
|
974
|
+
if (current === void 0) continue;
|
|
975
|
+
for (const m of firstSet) {
|
|
976
|
+
if (!current.members.includes(m)) {
|
|
977
|
+
firstSet.delete(m);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (firstSet.size === 0) {
|
|
982
|
+
const first = members[0];
|
|
983
|
+
const second = members[1];
|
|
984
|
+
if (first !== void 0 && second !== void 0) {
|
|
985
|
+
addContradiction(
|
|
986
|
+
ctx,
|
|
987
|
+
`Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
|
|
988
|
+
first.provenance,
|
|
989
|
+
second.provenance
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function typeLabel(type) {
|
|
995
|
+
switch (type.kind) {
|
|
996
|
+
case "primitive":
|
|
997
|
+
return type.primitiveKind;
|
|
998
|
+
case "enum":
|
|
999
|
+
return "enum";
|
|
1000
|
+
case "array":
|
|
1001
|
+
return "array";
|
|
1002
|
+
case "object":
|
|
1003
|
+
return "object";
|
|
1004
|
+
case "union":
|
|
1005
|
+
return "union";
|
|
1006
|
+
case "reference":
|
|
1007
|
+
return `reference(${type.name})`;
|
|
1008
|
+
case "dynamic":
|
|
1009
|
+
return `dynamic(${type.dynamicKind})`;
|
|
1010
|
+
case "custom":
|
|
1011
|
+
return `custom(${type.typeId})`;
|
|
1012
|
+
default: {
|
|
1013
|
+
const _exhaustive = type;
|
|
1014
|
+
return String(_exhaustive);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function checkTypeApplicability(ctx, fieldName, type, constraints) {
|
|
1019
|
+
const isNumber = type.kind === "primitive" && type.primitiveKind === "number";
|
|
1020
|
+
const isString = type.kind === "primitive" && type.primitiveKind === "string";
|
|
1021
|
+
const isArray = type.kind === "array";
|
|
1022
|
+
const isEnum = type.kind === "enum";
|
|
1023
|
+
const label = typeLabel(type);
|
|
1024
|
+
for (const constraint of constraints) {
|
|
1025
|
+
const ck = constraint.constraintKind;
|
|
1026
|
+
switch (ck) {
|
|
1027
|
+
case "minimum":
|
|
1028
|
+
case "maximum":
|
|
1029
|
+
case "exclusiveMinimum":
|
|
1030
|
+
case "exclusiveMaximum":
|
|
1031
|
+
case "multipleOf": {
|
|
1032
|
+
if (!isNumber) {
|
|
1033
|
+
addTypeMismatch(
|
|
1034
|
+
ctx,
|
|
1035
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
|
|
1036
|
+
constraint.provenance
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
case "minLength":
|
|
1042
|
+
case "maxLength":
|
|
1043
|
+
case "pattern": {
|
|
1044
|
+
if (!isString) {
|
|
1045
|
+
addTypeMismatch(
|
|
1046
|
+
ctx,
|
|
1047
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on string fields, but field type is "${label}"`,
|
|
1048
|
+
constraint.provenance
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
case "minItems":
|
|
1054
|
+
case "maxItems":
|
|
1055
|
+
case "uniqueItems": {
|
|
1056
|
+
if (!isArray) {
|
|
1057
|
+
addTypeMismatch(
|
|
1058
|
+
ctx,
|
|
1059
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on array fields, but field type is "${label}"`,
|
|
1060
|
+
constraint.provenance
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
case "allowedMembers": {
|
|
1066
|
+
if (!isEnum) {
|
|
1067
|
+
addTypeMismatch(
|
|
1068
|
+
ctx,
|
|
1069
|
+
`Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
|
|
1070
|
+
constraint.provenance
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
case "custom": {
|
|
1076
|
+
checkCustomConstraint(ctx, fieldName, type, constraint);
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
default: {
|
|
1080
|
+
const _exhaustive = constraint;
|
|
1081
|
+
throw new Error(
|
|
1082
|
+
`Unhandled constraint kind: ${_exhaustive.constraintKind}`
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
1089
|
+
if (ctx.extensionRegistry === void 0) return;
|
|
1090
|
+
const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
|
|
1091
|
+
if (registration === void 0) {
|
|
1092
|
+
addUnknownExtension(
|
|
1093
|
+
ctx,
|
|
1094
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
|
|
1095
|
+
constraint.provenance
|
|
1096
|
+
);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (registration.applicableTypes === null) return;
|
|
1100
|
+
if (!registration.applicableTypes.includes(type.kind)) {
|
|
1101
|
+
addTypeMismatch(
|
|
1102
|
+
ctx,
|
|
1103
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
1104
|
+
constraint.provenance
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function validateFieldNode(ctx, field) {
|
|
1109
|
+
validateConstraints(ctx, field.name, field.type, field.constraints);
|
|
1110
|
+
if (field.type.kind === "object") {
|
|
1111
|
+
for (const prop of field.type.properties) {
|
|
1112
|
+
validateObjectProperty(ctx, field.name, prop);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
function validateObjectProperty(ctx, parentName, prop) {
|
|
1117
|
+
const qualifiedName = `${parentName}.${prop.name}`;
|
|
1118
|
+
validateConstraints(ctx, qualifiedName, prop.type, prop.constraints);
|
|
1119
|
+
if (prop.type.kind === "object") {
|
|
1120
|
+
for (const nestedProp of prop.type.properties) {
|
|
1121
|
+
validateObjectProperty(ctx, qualifiedName, nestedProp);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
function validateConstraints(ctx, name, type, constraints) {
|
|
1126
|
+
checkNumericContradictions(ctx, name, constraints);
|
|
1127
|
+
checkLengthContradictions(ctx, name, constraints);
|
|
1128
|
+
checkAllowedMembersContradiction(ctx, name, constraints);
|
|
1129
|
+
checkTypeApplicability(ctx, name, type, constraints);
|
|
1130
|
+
}
|
|
1131
|
+
function validateElement(ctx, element) {
|
|
1132
|
+
switch (element.kind) {
|
|
1133
|
+
case "field":
|
|
1134
|
+
validateFieldNode(ctx, element);
|
|
1135
|
+
break;
|
|
1136
|
+
case "group":
|
|
1137
|
+
for (const child of element.elements) {
|
|
1138
|
+
validateElement(ctx, child);
|
|
1139
|
+
}
|
|
1140
|
+
break;
|
|
1141
|
+
case "conditional":
|
|
1142
|
+
for (const child of element.elements) {
|
|
1143
|
+
validateElement(ctx, child);
|
|
1144
|
+
}
|
|
1145
|
+
break;
|
|
1146
|
+
default: {
|
|
1147
|
+
const _exhaustive = element;
|
|
1148
|
+
throw new Error(`Unhandled element kind: ${_exhaustive.kind}`);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function validateIR(ir, options) {
|
|
1153
|
+
const ctx = {
|
|
1154
|
+
diagnostics: [],
|
|
1155
|
+
vendorPrefix: options?.vendorPrefix ?? "FORMSPEC",
|
|
1156
|
+
extensionRegistry: options?.extensionRegistry
|
|
1157
|
+
};
|
|
1158
|
+
for (const element of ir.elements) {
|
|
1159
|
+
validateElement(ctx, element);
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
diagnostics: ctx.diagnostics,
|
|
1163
|
+
valid: ctx.diagnostics.every((d) => d.severity !== "error")
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
|
|
517
1167
|
// src/browser.ts
|
|
518
1168
|
function buildFormSchemas(form) {
|
|
519
1169
|
return {
|
|
@@ -524,12 +1174,12 @@ function buildFormSchemas(form) {
|
|
|
524
1174
|
// Annotate the CommonJS export names for ESM import in node:
|
|
525
1175
|
0 && (module.exports = {
|
|
526
1176
|
buildFormSchemas,
|
|
1177
|
+
canonicalizeChainDSL,
|
|
527
1178
|
categorizationSchema,
|
|
528
1179
|
categorySchema,
|
|
529
1180
|
controlSchema,
|
|
530
1181
|
generateJsonSchema,
|
|
531
1182
|
generateUiSchema,
|
|
532
|
-
generateUiSchemaFromFields,
|
|
533
1183
|
getSchemaExtension,
|
|
534
1184
|
groupLayoutSchema,
|
|
535
1185
|
horizontalLayoutSchema,
|
|
@@ -544,6 +1194,7 @@ function buildFormSchemas(form) {
|
|
|
544
1194
|
uiSchemaElementSchema,
|
|
545
1195
|
uiSchemaElementTypeSchema,
|
|
546
1196
|
uiSchemaSchema,
|
|
1197
|
+
validateIR,
|
|
547
1198
|
verticalLayoutSchema
|
|
548
1199
|
});
|
|
549
1200
|
//# sourceMappingURL=browser.cjs.map
|