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