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