@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/internals.cjs
CHANGED
|
@@ -30,20 +30,410 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/internals.ts
|
|
31
31
|
var internals_exports = {};
|
|
32
32
|
__export(internals_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
analyzeClassToIR: () => analyzeClassToIR,
|
|
34
|
+
analyzeInterfaceToIR: () => analyzeInterfaceToIR,
|
|
35
|
+
analyzeTypeAliasToIR: () => analyzeTypeAliasToIR,
|
|
36
|
+
canonicalizeChainDSL: () => canonicalizeChainDSL,
|
|
37
|
+
canonicalizeTSDoc: () => canonicalizeTSDoc,
|
|
36
38
|
collectFormSpecReferences: () => collectFormSpecReferences,
|
|
39
|
+
createExtensionRegistry: () => createExtensionRegistry,
|
|
37
40
|
createProgramContext: () => createProgramContext,
|
|
38
41
|
findClassByName: () => findClassByName,
|
|
39
42
|
findInterfaceByName: () => findInterfaceByName,
|
|
40
43
|
findTypeAliasByName: () => findTypeAliasByName,
|
|
41
44
|
generateClassSchemas: () => generateClassSchemas,
|
|
45
|
+
generateJsonSchemaFromIR: () => generateJsonSchemaFromIR,
|
|
42
46
|
generateMethodSchemas: () => generateMethodSchemas,
|
|
43
|
-
|
|
47
|
+
generateUiSchemaFromIR: () => generateUiSchemaFromIR,
|
|
48
|
+
validateIR: () => validateIR
|
|
44
49
|
});
|
|
45
50
|
module.exports = __toCommonJS(internals_exports);
|
|
46
51
|
|
|
52
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
53
|
+
var import_core = require("@formspec/core");
|
|
54
|
+
var CHAIN_DSL_PROVENANCE = {
|
|
55
|
+
surface: "chain-dsl",
|
|
56
|
+
file: "",
|
|
57
|
+
line: 0,
|
|
58
|
+
column: 0
|
|
59
|
+
};
|
|
60
|
+
function isGroup(el) {
|
|
61
|
+
return el._type === "group";
|
|
62
|
+
}
|
|
63
|
+
function isConditional(el) {
|
|
64
|
+
return el._type === "conditional";
|
|
65
|
+
}
|
|
66
|
+
function isField(el) {
|
|
67
|
+
return el._type === "field";
|
|
68
|
+
}
|
|
69
|
+
function canonicalizeChainDSL(form) {
|
|
70
|
+
return {
|
|
71
|
+
kind: "form-ir",
|
|
72
|
+
irVersion: import_core.IR_VERSION,
|
|
73
|
+
elements: canonicalizeElements(form.elements),
|
|
74
|
+
typeRegistry: {},
|
|
75
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function canonicalizeElements(elements) {
|
|
79
|
+
return elements.map(canonicalizeElement);
|
|
80
|
+
}
|
|
81
|
+
function canonicalizeElement(element) {
|
|
82
|
+
if (isField(element)) {
|
|
83
|
+
return canonicalizeField(element);
|
|
84
|
+
}
|
|
85
|
+
if (isGroup(element)) {
|
|
86
|
+
return canonicalizeGroup(element);
|
|
87
|
+
}
|
|
88
|
+
if (isConditional(element)) {
|
|
89
|
+
return canonicalizeConditional(element);
|
|
90
|
+
}
|
|
91
|
+
const _exhaustive = element;
|
|
92
|
+
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
93
|
+
}
|
|
94
|
+
function canonicalizeField(field) {
|
|
95
|
+
switch (field._field) {
|
|
96
|
+
case "text":
|
|
97
|
+
return canonicalizeTextField(field);
|
|
98
|
+
case "number":
|
|
99
|
+
return canonicalizeNumberField(field);
|
|
100
|
+
case "boolean":
|
|
101
|
+
return canonicalizeBooleanField(field);
|
|
102
|
+
case "enum":
|
|
103
|
+
return canonicalizeStaticEnumField(field);
|
|
104
|
+
case "dynamic_enum":
|
|
105
|
+
return canonicalizeDynamicEnumField(field);
|
|
106
|
+
case "dynamic_schema":
|
|
107
|
+
return canonicalizeDynamicSchemaField(field);
|
|
108
|
+
case "array":
|
|
109
|
+
return canonicalizeArrayField(field);
|
|
110
|
+
case "object":
|
|
111
|
+
return canonicalizeObjectField(field);
|
|
112
|
+
default: {
|
|
113
|
+
const _exhaustive = field;
|
|
114
|
+
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function canonicalizeTextField(field) {
|
|
119
|
+
const type = { kind: "primitive", primitiveKind: "string" };
|
|
120
|
+
const constraints = [];
|
|
121
|
+
if (field.minLength !== void 0) {
|
|
122
|
+
const c = {
|
|
123
|
+
kind: "constraint",
|
|
124
|
+
constraintKind: "minLength",
|
|
125
|
+
value: field.minLength,
|
|
126
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
127
|
+
};
|
|
128
|
+
constraints.push(c);
|
|
129
|
+
}
|
|
130
|
+
if (field.maxLength !== void 0) {
|
|
131
|
+
const c = {
|
|
132
|
+
kind: "constraint",
|
|
133
|
+
constraintKind: "maxLength",
|
|
134
|
+
value: field.maxLength,
|
|
135
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
136
|
+
};
|
|
137
|
+
constraints.push(c);
|
|
138
|
+
}
|
|
139
|
+
if (field.pattern !== void 0) {
|
|
140
|
+
const c = {
|
|
141
|
+
kind: "constraint",
|
|
142
|
+
constraintKind: "pattern",
|
|
143
|
+
pattern: field.pattern,
|
|
144
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
145
|
+
};
|
|
146
|
+
constraints.push(c);
|
|
147
|
+
}
|
|
148
|
+
return buildFieldNode(
|
|
149
|
+
field.name,
|
|
150
|
+
type,
|
|
151
|
+
field.required,
|
|
152
|
+
buildAnnotations(field.label, field.placeholder),
|
|
153
|
+
constraints
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
function canonicalizeNumberField(field) {
|
|
157
|
+
const type = { kind: "primitive", primitiveKind: "number" };
|
|
158
|
+
const constraints = [];
|
|
159
|
+
if (field.min !== void 0) {
|
|
160
|
+
const c = {
|
|
161
|
+
kind: "constraint",
|
|
162
|
+
constraintKind: "minimum",
|
|
163
|
+
value: field.min,
|
|
164
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
165
|
+
};
|
|
166
|
+
constraints.push(c);
|
|
167
|
+
}
|
|
168
|
+
if (field.max !== void 0) {
|
|
169
|
+
const c = {
|
|
170
|
+
kind: "constraint",
|
|
171
|
+
constraintKind: "maximum",
|
|
172
|
+
value: field.max,
|
|
173
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
174
|
+
};
|
|
175
|
+
constraints.push(c);
|
|
176
|
+
}
|
|
177
|
+
if (field.multipleOf !== void 0) {
|
|
178
|
+
const c = {
|
|
179
|
+
kind: "constraint",
|
|
180
|
+
constraintKind: "multipleOf",
|
|
181
|
+
value: field.multipleOf,
|
|
182
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
183
|
+
};
|
|
184
|
+
constraints.push(c);
|
|
185
|
+
}
|
|
186
|
+
return buildFieldNode(
|
|
187
|
+
field.name,
|
|
188
|
+
type,
|
|
189
|
+
field.required,
|
|
190
|
+
buildAnnotations(field.label),
|
|
191
|
+
constraints
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function canonicalizeBooleanField(field) {
|
|
195
|
+
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
196
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
197
|
+
}
|
|
198
|
+
function canonicalizeStaticEnumField(field) {
|
|
199
|
+
const members = field.options.map((opt) => {
|
|
200
|
+
if (typeof opt === "string") {
|
|
201
|
+
return { value: opt };
|
|
202
|
+
}
|
|
203
|
+
return { value: opt.id, displayName: opt.label };
|
|
204
|
+
});
|
|
205
|
+
const type = { kind: "enum", members };
|
|
206
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
207
|
+
}
|
|
208
|
+
function canonicalizeDynamicEnumField(field) {
|
|
209
|
+
const type = {
|
|
210
|
+
kind: "dynamic",
|
|
211
|
+
dynamicKind: "enum",
|
|
212
|
+
sourceKey: field.source,
|
|
213
|
+
parameterFields: field.params ? [...field.params] : []
|
|
214
|
+
};
|
|
215
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
216
|
+
}
|
|
217
|
+
function canonicalizeDynamicSchemaField(field) {
|
|
218
|
+
const type = {
|
|
219
|
+
kind: "dynamic",
|
|
220
|
+
dynamicKind: "schema",
|
|
221
|
+
sourceKey: field.schemaSource,
|
|
222
|
+
parameterFields: []
|
|
223
|
+
};
|
|
224
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
225
|
+
}
|
|
226
|
+
function canonicalizeArrayField(field) {
|
|
227
|
+
const itemProperties = buildObjectProperties(field.items);
|
|
228
|
+
const itemsType = {
|
|
229
|
+
kind: "object",
|
|
230
|
+
properties: itemProperties,
|
|
231
|
+
additionalProperties: false
|
|
232
|
+
};
|
|
233
|
+
const type = { kind: "array", items: itemsType };
|
|
234
|
+
const constraints = [];
|
|
235
|
+
if (field.minItems !== void 0) {
|
|
236
|
+
const c = {
|
|
237
|
+
kind: "constraint",
|
|
238
|
+
constraintKind: "minItems",
|
|
239
|
+
value: field.minItems,
|
|
240
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
241
|
+
};
|
|
242
|
+
constraints.push(c);
|
|
243
|
+
}
|
|
244
|
+
if (field.maxItems !== void 0) {
|
|
245
|
+
const c = {
|
|
246
|
+
kind: "constraint",
|
|
247
|
+
constraintKind: "maxItems",
|
|
248
|
+
value: field.maxItems,
|
|
249
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
250
|
+
};
|
|
251
|
+
constraints.push(c);
|
|
252
|
+
}
|
|
253
|
+
return buildFieldNode(
|
|
254
|
+
field.name,
|
|
255
|
+
type,
|
|
256
|
+
field.required,
|
|
257
|
+
buildAnnotations(field.label),
|
|
258
|
+
constraints
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
function canonicalizeObjectField(field) {
|
|
262
|
+
const properties = buildObjectProperties(field.properties);
|
|
263
|
+
const type = {
|
|
264
|
+
kind: "object",
|
|
265
|
+
properties,
|
|
266
|
+
additionalProperties: false
|
|
267
|
+
};
|
|
268
|
+
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
269
|
+
}
|
|
270
|
+
function canonicalizeGroup(g) {
|
|
271
|
+
return {
|
|
272
|
+
kind: "group",
|
|
273
|
+
label: g.label,
|
|
274
|
+
elements: canonicalizeElements(g.elements),
|
|
275
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function canonicalizeConditional(c) {
|
|
279
|
+
return {
|
|
280
|
+
kind: "conditional",
|
|
281
|
+
fieldName: c.field,
|
|
282
|
+
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
283
|
+
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
284
|
+
value: assertJsonValue(c.value),
|
|
285
|
+
elements: canonicalizeElements(c.elements),
|
|
286
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function assertJsonValue(v) {
|
|
290
|
+
if (v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
291
|
+
return v;
|
|
292
|
+
}
|
|
293
|
+
if (Array.isArray(v)) {
|
|
294
|
+
return v.map(assertJsonValue);
|
|
295
|
+
}
|
|
296
|
+
if (typeof v === "object") {
|
|
297
|
+
const result = {};
|
|
298
|
+
for (const [key, val] of Object.entries(v)) {
|
|
299
|
+
result[key] = assertJsonValue(val);
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
304
|
+
}
|
|
305
|
+
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
306
|
+
return {
|
|
307
|
+
kind: "field",
|
|
308
|
+
name,
|
|
309
|
+
type,
|
|
310
|
+
required: required === true,
|
|
311
|
+
constraints,
|
|
312
|
+
annotations,
|
|
313
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function buildAnnotations(label, placeholder) {
|
|
317
|
+
const annotations = [];
|
|
318
|
+
if (label !== void 0) {
|
|
319
|
+
const a = {
|
|
320
|
+
kind: "annotation",
|
|
321
|
+
annotationKind: "displayName",
|
|
322
|
+
value: label,
|
|
323
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
324
|
+
};
|
|
325
|
+
annotations.push(a);
|
|
326
|
+
}
|
|
327
|
+
if (placeholder !== void 0) {
|
|
328
|
+
const a = {
|
|
329
|
+
kind: "annotation",
|
|
330
|
+
annotationKind: "placeholder",
|
|
331
|
+
value: placeholder,
|
|
332
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
333
|
+
};
|
|
334
|
+
annotations.push(a);
|
|
335
|
+
}
|
|
336
|
+
return annotations;
|
|
337
|
+
}
|
|
338
|
+
function buildObjectProperties(elements, insideConditional = false) {
|
|
339
|
+
const properties = [];
|
|
340
|
+
for (const el of elements) {
|
|
341
|
+
if (isField(el)) {
|
|
342
|
+
const fieldNode = canonicalizeField(el);
|
|
343
|
+
properties.push({
|
|
344
|
+
name: fieldNode.name,
|
|
345
|
+
type: fieldNode.type,
|
|
346
|
+
// Fields inside a conditional branch are always optional in the
|
|
347
|
+
// data schema, regardless of their `required` flag — the condition
|
|
348
|
+
// may not be met, so the field may be absent.
|
|
349
|
+
optional: insideConditional || !fieldNode.required,
|
|
350
|
+
constraints: fieldNode.constraints,
|
|
351
|
+
annotations: fieldNode.annotations,
|
|
352
|
+
provenance: CHAIN_DSL_PROVENANCE
|
|
353
|
+
});
|
|
354
|
+
} else if (isGroup(el)) {
|
|
355
|
+
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
356
|
+
} else if (isConditional(el)) {
|
|
357
|
+
properties.push(...buildObjectProperties(el.elements, true));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return properties;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
364
|
+
var import_core2 = require("@formspec/core");
|
|
365
|
+
function canonicalizeTSDoc(analysis, source) {
|
|
366
|
+
const file = source?.file ?? "";
|
|
367
|
+
const provenance = {
|
|
368
|
+
surface: "tsdoc",
|
|
369
|
+
file,
|
|
370
|
+
line: 1,
|
|
371
|
+
column: 0
|
|
372
|
+
};
|
|
373
|
+
const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
|
|
374
|
+
return {
|
|
375
|
+
kind: "form-ir",
|
|
376
|
+
irVersion: import_core2.IR_VERSION,
|
|
377
|
+
elements,
|
|
378
|
+
typeRegistry: analysis.typeRegistry,
|
|
379
|
+
provenance
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function assembleElements(fields, layouts, provenance) {
|
|
383
|
+
const elements = [];
|
|
384
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
385
|
+
const topLevelOrder = [];
|
|
386
|
+
for (let i = 0; i < fields.length; i++) {
|
|
387
|
+
const field = fields[i];
|
|
388
|
+
const layout = layouts[i];
|
|
389
|
+
if (!field || !layout) continue;
|
|
390
|
+
const element = wrapInConditional(field, layout, provenance);
|
|
391
|
+
if (layout.groupLabel !== void 0) {
|
|
392
|
+
const label = layout.groupLabel;
|
|
393
|
+
let groupElements = groupMap.get(label);
|
|
394
|
+
if (!groupElements) {
|
|
395
|
+
groupElements = [];
|
|
396
|
+
groupMap.set(label, groupElements);
|
|
397
|
+
topLevelOrder.push({ type: "group", label });
|
|
398
|
+
}
|
|
399
|
+
groupElements.push(element);
|
|
400
|
+
} else {
|
|
401
|
+
topLevelOrder.push({ type: "element", element });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const entry of topLevelOrder) {
|
|
405
|
+
if (entry.type === "group") {
|
|
406
|
+
const groupElements = groupMap.get(entry.label);
|
|
407
|
+
if (groupElements) {
|
|
408
|
+
const groupNode = {
|
|
409
|
+
kind: "group",
|
|
410
|
+
label: entry.label,
|
|
411
|
+
elements: groupElements,
|
|
412
|
+
provenance
|
|
413
|
+
};
|
|
414
|
+
elements.push(groupNode);
|
|
415
|
+
groupMap.delete(entry.label);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
elements.push(entry.element);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return elements;
|
|
422
|
+
}
|
|
423
|
+
function wrapInConditional(field, layout, provenance) {
|
|
424
|
+
if (layout.showWhen === void 0) {
|
|
425
|
+
return field;
|
|
426
|
+
}
|
|
427
|
+
const conditional = {
|
|
428
|
+
kind: "conditional",
|
|
429
|
+
fieldName: layout.showWhen.field,
|
|
430
|
+
value: layout.showWhen.value,
|
|
431
|
+
elements: [field],
|
|
432
|
+
provenance
|
|
433
|
+
};
|
|
434
|
+
return conditional;
|
|
435
|
+
}
|
|
436
|
+
|
|
47
437
|
// src/analyzer/program.ts
|
|
48
438
|
var ts = __toESM(require("typescript"), 1);
|
|
49
439
|
var path = __toESM(require("path"), 1);
|
|
@@ -119,290 +509,104 @@ function findTypeAliasByName(sourceFile, aliasName) {
|
|
|
119
509
|
// src/analyzer/class-analyzer.ts
|
|
120
510
|
var ts4 = __toESM(require("typescript"), 1);
|
|
121
511
|
|
|
122
|
-
// src/analyzer/
|
|
512
|
+
// src/analyzer/jsdoc-constraints.ts
|
|
513
|
+
var ts3 = __toESM(require("typescript"), 1);
|
|
514
|
+
var import_core4 = require("@formspec/core");
|
|
515
|
+
|
|
516
|
+
// src/analyzer/tsdoc-parser.ts
|
|
123
517
|
var ts2 = __toESM(require("typescript"), 1);
|
|
124
|
-
var
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
518
|
+
var import_tsdoc = require("@microsoft/tsdoc");
|
|
519
|
+
var import_core3 = require("@formspec/core");
|
|
520
|
+
var NUMERIC_CONSTRAINT_MAP = {
|
|
521
|
+
Minimum: "minimum",
|
|
522
|
+
Maximum: "maximum",
|
|
523
|
+
ExclusiveMinimum: "exclusiveMinimum",
|
|
524
|
+
ExclusiveMaximum: "exclusiveMaximum"
|
|
525
|
+
};
|
|
526
|
+
var LENGTH_CONSTRAINT_MAP = {
|
|
527
|
+
MinLength: "minLength",
|
|
528
|
+
MaxLength: "maxLength"
|
|
529
|
+
};
|
|
530
|
+
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["Pattern", "EnumOptions"]);
|
|
531
|
+
function isBuiltinConstraintName(tagName) {
|
|
532
|
+
return tagName in import_core3.BUILTIN_CONSTRAINT_DEFINITIONS;
|
|
136
533
|
}
|
|
137
|
-
function
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const callee = expr.expression;
|
|
148
|
-
let name = null;
|
|
149
|
-
if (ts2.isIdentifier(callee)) {
|
|
150
|
-
name = callee.text;
|
|
151
|
-
} else if (ts2.isPropertyAccessExpression(callee)) {
|
|
152
|
-
name = callee.name.text;
|
|
153
|
-
}
|
|
154
|
-
if (!name) return null;
|
|
155
|
-
const args = expr.arguments.map(extractArgValue);
|
|
156
|
-
return {
|
|
157
|
-
name,
|
|
158
|
-
args,
|
|
159
|
-
node: decorator
|
|
160
|
-
};
|
|
534
|
+
function createFormSpecTSDocConfig() {
|
|
535
|
+
const config = new import_tsdoc.TSDocConfiguration();
|
|
536
|
+
for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
537
|
+
config.addTagDefinition(
|
|
538
|
+
new import_tsdoc.TSDocTagDefinition({
|
|
539
|
+
tagName: "@" + tagName,
|
|
540
|
+
syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
|
|
541
|
+
allowMultiple: true
|
|
542
|
+
})
|
|
543
|
+
);
|
|
161
544
|
}
|
|
162
|
-
return
|
|
545
|
+
return config;
|
|
163
546
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
if (ts2.isPrefixUnaryExpression(node)) {
|
|
181
|
-
if (node.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(node.operand)) {
|
|
182
|
-
return -Number(node.operand.text);
|
|
183
|
-
}
|
|
184
|
-
if (node.operator === ts2.SyntaxKind.PlusToken && ts2.isNumericLiteral(node.operand)) {
|
|
185
|
-
return Number(node.operand.text);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (ts2.isArrayLiteralExpression(node)) {
|
|
189
|
-
return node.elements.map((el) => {
|
|
190
|
-
if (ts2.isSpreadElement(el)) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
return extractArgValue(el);
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
if (ts2.isObjectLiteralExpression(node)) {
|
|
197
|
-
const obj = {};
|
|
198
|
-
for (const prop of node.properties) {
|
|
199
|
-
if (ts2.isPropertyAssignment(prop)) {
|
|
200
|
-
const key = getPropertyName(prop.name);
|
|
201
|
-
if (key) {
|
|
202
|
-
obj[key] = extractArgValue(prop.initializer);
|
|
203
|
-
}
|
|
204
|
-
} else if (ts2.isShorthandPropertyAssignment(prop)) {
|
|
205
|
-
const key = prop.name.text;
|
|
206
|
-
obj[key] = null;
|
|
547
|
+
var sharedParser;
|
|
548
|
+
function getParser() {
|
|
549
|
+
sharedParser ??= new import_tsdoc.TSDocParser(createFormSpecTSDocConfig());
|
|
550
|
+
return sharedParser;
|
|
551
|
+
}
|
|
552
|
+
function parseTSDocTags(node, file = "") {
|
|
553
|
+
const constraints = [];
|
|
554
|
+
const annotations = [];
|
|
555
|
+
const sourceFile = node.getSourceFile();
|
|
556
|
+
const sourceText = sourceFile.getFullText();
|
|
557
|
+
const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
558
|
+
if (commentRanges) {
|
|
559
|
+
for (const range of commentRanges) {
|
|
560
|
+
if (range.kind !== ts2.SyntaxKind.MultiLineCommentTrivia) {
|
|
561
|
+
continue;
|
|
207
562
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (ts2.isNoSubstitutionTemplateLiteral(node)) {
|
|
212
|
-
return node.text;
|
|
213
|
-
}
|
|
214
|
-
if (ts2.isRegularExpressionLiteral(node)) {
|
|
215
|
-
const regexText = node.text;
|
|
216
|
-
const lastSlash = regexText.lastIndexOf("/");
|
|
217
|
-
if (lastSlash > 0) {
|
|
218
|
-
return regexText.substring(1, lastSlash);
|
|
219
|
-
}
|
|
220
|
-
return regexText;
|
|
221
|
-
}
|
|
222
|
-
if (ts2.isNewExpression(node)) {
|
|
223
|
-
if (ts2.isIdentifier(node.expression) && node.expression.text === "RegExp" && node.arguments && node.arguments.length > 0) {
|
|
224
|
-
const firstArg = node.arguments[0];
|
|
225
|
-
if (firstArg && ts2.isStringLiteral(firstArg)) {
|
|
226
|
-
return firstArg.text;
|
|
563
|
+
const commentText = sourceText.substring(range.pos, range.end);
|
|
564
|
+
if (!commentText.startsWith("/**")) {
|
|
565
|
+
continue;
|
|
227
566
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (ts2.isNumericLiteral(name)) {
|
|
243
|
-
return name.text;
|
|
244
|
-
}
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
var FORMSPEC_DECORATORS = {
|
|
248
|
-
// Display metadata
|
|
249
|
-
Field: { argTypes: ["object"] },
|
|
250
|
-
// Grouping
|
|
251
|
-
Group: { argTypes: ["string"] },
|
|
252
|
-
// Conditional display
|
|
253
|
-
ShowWhen: { argTypes: ["object"] },
|
|
254
|
-
// Enum options
|
|
255
|
-
EnumOptions: { argTypes: ["array"] },
|
|
256
|
-
// Numeric constraints
|
|
257
|
-
Minimum: { argTypes: ["number"] },
|
|
258
|
-
Maximum: { argTypes: ["number"] },
|
|
259
|
-
ExclusiveMinimum: { argTypes: ["number"] },
|
|
260
|
-
ExclusiveMaximum: { argTypes: ["number"] },
|
|
261
|
-
// String constraints
|
|
262
|
-
MinLength: { argTypes: ["number"] },
|
|
263
|
-
MaxLength: { argTypes: ["number"] },
|
|
264
|
-
Pattern: { argTypes: ["string"] }
|
|
265
|
-
};
|
|
266
|
-
function isFormSpecDecoratorsPath(fileName) {
|
|
267
|
-
const normalized = fileName.replace(/\\/g, "/");
|
|
268
|
-
return normalized.includes("node_modules/@formspec/decorators") || normalized.includes("/packages/decorators/");
|
|
269
|
-
}
|
|
270
|
-
function resolveDecorator(decorator, checker) {
|
|
271
|
-
const expr = decorator.expression;
|
|
272
|
-
let targetNode;
|
|
273
|
-
let name;
|
|
274
|
-
if (ts2.isIdentifier(expr)) {
|
|
275
|
-
targetNode = expr;
|
|
276
|
-
name = expr.text;
|
|
277
|
-
} else if (ts2.isCallExpression(expr)) {
|
|
278
|
-
if (ts2.isIdentifier(expr.expression)) {
|
|
279
|
-
targetNode = expr.expression;
|
|
280
|
-
name = expr.expression.text;
|
|
281
|
-
} else {
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
} else {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
if (name in FORMSPEC_DECORATORS) {
|
|
288
|
-
const symbol = checker.getSymbolAtLocation(targetNode);
|
|
289
|
-
if (symbol) {
|
|
290
|
-
const declarations = symbol.declarations;
|
|
291
|
-
if (declarations && declarations.length > 0) {
|
|
292
|
-
const decl = declarations[0];
|
|
293
|
-
if (decl) {
|
|
294
|
-
const sourceFile = decl.getSourceFile();
|
|
295
|
-
const fileName = sourceFile.fileName;
|
|
296
|
-
if (isFormSpecDecoratorsPath(fileName)) {
|
|
297
|
-
return {
|
|
298
|
-
name,
|
|
299
|
-
isFormSpec: true,
|
|
300
|
-
isMarker: !ts2.isCallExpression(expr)
|
|
301
|
-
};
|
|
302
|
-
}
|
|
567
|
+
const parser = getParser();
|
|
568
|
+
const parserContext = parser.parseRange(
|
|
569
|
+
import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
|
|
570
|
+
);
|
|
571
|
+
const docComment = parserContext.docComment;
|
|
572
|
+
for (const block of docComment.customBlocks) {
|
|
573
|
+
const tagName = block.blockTag.tagName.substring(1);
|
|
574
|
+
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
575
|
+
const text = extractBlockText(block).trim();
|
|
576
|
+
if (text === "") continue;
|
|
577
|
+
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
578
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
579
|
+
if (constraintNode) {
|
|
580
|
+
constraints.push(constraintNode);
|
|
303
581
|
}
|
|
304
582
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
let extendsBuiltin;
|
|
312
|
-
let extensionName;
|
|
313
|
-
let isMarker = false;
|
|
314
|
-
for (const prop of props) {
|
|
315
|
-
const escapedName = prop.getEscapedName();
|
|
316
|
-
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.extends") || escapedName.includes("FORMSPEC_EXTENDS"))) {
|
|
317
|
-
const propType = checker.getTypeOfSymbol(prop);
|
|
318
|
-
if (propType.isStringLiteral()) {
|
|
319
|
-
extendsBuiltin = propType.value;
|
|
583
|
+
if (docComment.deprecatedBlock !== void 0) {
|
|
584
|
+
annotations.push({
|
|
585
|
+
kind: "annotation",
|
|
586
|
+
annotationKind: "deprecated",
|
|
587
|
+
provenance: provenanceForComment(range, sourceFile, file, "deprecated")
|
|
588
|
+
});
|
|
320
589
|
}
|
|
321
590
|
}
|
|
322
|
-
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.extension") || escapedName.includes("FORMSPEC_EXTENSION"))) {
|
|
323
|
-
const propType = checker.getTypeOfSymbol(prop);
|
|
324
|
-
if (propType.isStringLiteral()) {
|
|
325
|
-
extensionName = propType.value;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.marker") || escapedName.includes("FORMSPEC_MARKER"))) {
|
|
329
|
-
isMarker = true;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
if (extendsBuiltin) {
|
|
333
|
-
return {
|
|
334
|
-
name,
|
|
335
|
-
extendsBuiltin,
|
|
336
|
-
isFormSpec: true,
|
|
337
|
-
isMarker: false
|
|
338
|
-
};
|
|
339
591
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
name,
|
|
343
|
-
extensionName,
|
|
344
|
-
isFormSpec: true,
|
|
345
|
-
isMarker
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
if (isMarker) {
|
|
349
|
-
return {
|
|
350
|
-
name,
|
|
351
|
-
isFormSpec: true,
|
|
352
|
-
isMarker: true
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// src/analyzer/jsdoc-constraints.ts
|
|
359
|
-
var ts3 = __toESM(require("typescript"), 1);
|
|
360
|
-
var import_core2 = require("@formspec/core");
|
|
361
|
-
function extractJSDocConstraints(node) {
|
|
362
|
-
const results = [];
|
|
363
|
-
const jsDocTags = ts3.getJSDocTags(node);
|
|
364
|
-
for (const tag of jsDocTags) {
|
|
592
|
+
const jsDocTagsAll = ts2.getJSDocTags(node);
|
|
593
|
+
for (const tag of jsDocTagsAll) {
|
|
365
594
|
const tagName = tag.tagName.text;
|
|
366
|
-
if (!(tagName
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
const constraintName = tagName;
|
|
370
|
-
const expectedType = import_core2.CONSTRAINT_TAG_DEFINITIONS[constraintName];
|
|
595
|
+
if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
371
596
|
const commentText = getTagCommentText(tag);
|
|
372
|
-
if (commentText === void 0 || commentText === "")
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
if (expectedType === "number") {
|
|
380
|
-
const value = Number(trimmed);
|
|
381
|
-
if (Number.isNaN(value)) {
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
results.push(createSyntheticDecorator(constraintName, value));
|
|
385
|
-
} else if (expectedType === "json") {
|
|
386
|
-
try {
|
|
387
|
-
const parsed = JSON.parse(trimmed);
|
|
388
|
-
if (!Array.isArray(parsed)) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
results.push(createSyntheticDecorator(constraintName, parsed));
|
|
392
|
-
} catch {
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
} else {
|
|
396
|
-
results.push(createSyntheticDecorator(constraintName, trimmed));
|
|
597
|
+
if (commentText === void 0 || commentText.trim() === "") continue;
|
|
598
|
+
const text = commentText.trim();
|
|
599
|
+
const provenance = provenanceForJSDocTag(tag, file);
|
|
600
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
601
|
+
if (constraintNode) {
|
|
602
|
+
constraints.push(constraintNode);
|
|
397
603
|
}
|
|
398
604
|
}
|
|
399
|
-
return results;
|
|
400
|
-
}
|
|
401
|
-
function extractJSDocFieldMetadata(node) {
|
|
402
|
-
const jsDocTags = ts3.getJSDocTags(node);
|
|
403
605
|
let displayName;
|
|
404
606
|
let description;
|
|
405
|
-
|
|
607
|
+
let displayNameTag;
|
|
608
|
+
let descriptionTag;
|
|
609
|
+
for (const tag of jsDocTagsAll) {
|
|
406
610
|
const tagName = tag.tagName.text;
|
|
407
611
|
const commentText = getTagCommentText(tag);
|
|
408
612
|
if (commentText === void 0 || commentText.trim() === "") {
|
|
@@ -411,18 +615,132 @@ function extractJSDocFieldMetadata(node) {
|
|
|
411
615
|
const trimmed = commentText.trim();
|
|
412
616
|
if (tagName === "Field_displayName") {
|
|
413
617
|
displayName = trimmed;
|
|
618
|
+
displayNameTag = tag;
|
|
414
619
|
} else if (tagName === "Field_description") {
|
|
415
620
|
description = trimmed;
|
|
621
|
+
descriptionTag = tag;
|
|
416
622
|
}
|
|
417
623
|
}
|
|
418
|
-
if (displayName
|
|
624
|
+
if (displayName !== void 0 && displayNameTag) {
|
|
625
|
+
annotations.push({
|
|
626
|
+
kind: "annotation",
|
|
627
|
+
annotationKind: "displayName",
|
|
628
|
+
value: displayName,
|
|
629
|
+
provenance: provenanceForJSDocTag(displayNameTag, file)
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
if (description !== void 0 && descriptionTag) {
|
|
633
|
+
annotations.push({
|
|
634
|
+
kind: "annotation",
|
|
635
|
+
annotationKind: "description",
|
|
636
|
+
value: description,
|
|
637
|
+
provenance: provenanceForJSDocTag(descriptionTag, file)
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return { constraints, annotations };
|
|
641
|
+
}
|
|
642
|
+
function extractBlockText(block) {
|
|
643
|
+
return extractPlainText(block.content);
|
|
644
|
+
}
|
|
645
|
+
function extractPlainText(node) {
|
|
646
|
+
let result = "";
|
|
647
|
+
if (node instanceof import_tsdoc.DocPlainText) {
|
|
648
|
+
return node.text;
|
|
649
|
+
}
|
|
650
|
+
if (node instanceof import_tsdoc.DocSoftBreak) {
|
|
651
|
+
return " ";
|
|
652
|
+
}
|
|
653
|
+
if (typeof node.getChildNodes === "function") {
|
|
654
|
+
for (const child of node.getChildNodes()) {
|
|
655
|
+
result += extractPlainText(child);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
function parseConstraintValue(tagName, text, provenance) {
|
|
661
|
+
if (!isBuiltinConstraintName(tagName)) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
const expectedType = import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName];
|
|
665
|
+
if (expectedType === "number") {
|
|
666
|
+
const value = Number(text);
|
|
667
|
+
if (Number.isNaN(value)) {
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
const numericKind = NUMERIC_CONSTRAINT_MAP[tagName];
|
|
671
|
+
if (numericKind) {
|
|
672
|
+
return {
|
|
673
|
+
kind: "constraint",
|
|
674
|
+
constraintKind: numericKind,
|
|
675
|
+
value,
|
|
676
|
+
provenance
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const lengthKind = LENGTH_CONSTRAINT_MAP[tagName];
|
|
680
|
+
if (lengthKind) {
|
|
681
|
+
return {
|
|
682
|
+
kind: "constraint",
|
|
683
|
+
constraintKind: lengthKind,
|
|
684
|
+
value,
|
|
685
|
+
provenance
|
|
686
|
+
};
|
|
687
|
+
}
|
|
419
688
|
return null;
|
|
420
689
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
690
|
+
if (expectedType === "json") {
|
|
691
|
+
try {
|
|
692
|
+
const parsed = JSON.parse(text);
|
|
693
|
+
if (!Array.isArray(parsed)) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
const members = [];
|
|
697
|
+
for (const item of parsed) {
|
|
698
|
+
if (typeof item === "string" || typeof item === "number") {
|
|
699
|
+
members.push(item);
|
|
700
|
+
} else if (typeof item === "object" && item !== null && "id" in item) {
|
|
701
|
+
const id = item["id"];
|
|
702
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
703
|
+
members.push(id);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
kind: "constraint",
|
|
709
|
+
constraintKind: "allowedMembers",
|
|
710
|
+
members,
|
|
711
|
+
provenance
|
|
712
|
+
};
|
|
713
|
+
} catch {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
kind: "constraint",
|
|
719
|
+
constraintKind: "pattern",
|
|
720
|
+
pattern: text,
|
|
721
|
+
provenance
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function provenanceForComment(range, sourceFile, file, tagName) {
|
|
725
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(range.pos);
|
|
726
|
+
return {
|
|
727
|
+
surface: "tsdoc",
|
|
728
|
+
file,
|
|
729
|
+
line: line + 1,
|
|
730
|
+
column: character,
|
|
731
|
+
tagName: "@" + tagName
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function provenanceForJSDocTag(tag, file) {
|
|
735
|
+
const sourceFile = tag.getSourceFile();
|
|
736
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(tag.getStart());
|
|
737
|
+
return {
|
|
738
|
+
surface: "tsdoc",
|
|
739
|
+
file,
|
|
740
|
+
line: line + 1,
|
|
741
|
+
column: character,
|
|
742
|
+
tagName: "@" + tag.tagName.text
|
|
424
743
|
};
|
|
425
|
-
return createSyntheticDecorator("Field", fieldOpts);
|
|
426
744
|
}
|
|
427
745
|
function getTagCommentText(tag) {
|
|
428
746
|
if (tag.comment === void 0) {
|
|
@@ -431,27 +749,73 @@ function getTagCommentText(tag) {
|
|
|
431
749
|
if (typeof tag.comment === "string") {
|
|
432
750
|
return tag.comment;
|
|
433
751
|
}
|
|
434
|
-
return
|
|
752
|
+
return ts2.getTextOfJSDocComment(tag.comment);
|
|
435
753
|
}
|
|
436
|
-
|
|
754
|
+
|
|
755
|
+
// src/analyzer/jsdoc-constraints.ts
|
|
756
|
+
function extractJSDocConstraintNodes(node, file = "") {
|
|
757
|
+
const result = parseTSDocTags(node, file);
|
|
758
|
+
return [...result.constraints];
|
|
759
|
+
}
|
|
760
|
+
function extractJSDocAnnotationNodes(node, file = "") {
|
|
761
|
+
const result = parseTSDocTags(node, file);
|
|
762
|
+
return [...result.annotations];
|
|
763
|
+
}
|
|
764
|
+
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
765
|
+
if (!initializer) return null;
|
|
766
|
+
let value;
|
|
767
|
+
if (ts3.isStringLiteral(initializer)) {
|
|
768
|
+
value = initializer.text;
|
|
769
|
+
} else if (ts3.isNumericLiteral(initializer)) {
|
|
770
|
+
value = Number(initializer.text);
|
|
771
|
+
} else if (initializer.kind === ts3.SyntaxKind.TrueKeyword) {
|
|
772
|
+
value = true;
|
|
773
|
+
} else if (initializer.kind === ts3.SyntaxKind.FalseKeyword) {
|
|
774
|
+
value = false;
|
|
775
|
+
} else if (initializer.kind === ts3.SyntaxKind.NullKeyword) {
|
|
776
|
+
value = null;
|
|
777
|
+
} else if (ts3.isPrefixUnaryExpression(initializer)) {
|
|
778
|
+
if (initializer.operator === ts3.SyntaxKind.MinusToken && ts3.isNumericLiteral(initializer.operand)) {
|
|
779
|
+
value = -Number(initializer.operand.text);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (value === void 0) return null;
|
|
783
|
+
const sourceFile = initializer.getSourceFile();
|
|
784
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(initializer.getStart());
|
|
437
785
|
return {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
786
|
+
kind: "annotation",
|
|
787
|
+
annotationKind: "defaultValue",
|
|
788
|
+
value,
|
|
789
|
+
provenance: {
|
|
790
|
+
surface: "tsdoc",
|
|
791
|
+
file,
|
|
792
|
+
line: line + 1,
|
|
793
|
+
column: character
|
|
794
|
+
}
|
|
441
795
|
};
|
|
442
796
|
}
|
|
443
797
|
|
|
444
798
|
// src/analyzer/class-analyzer.ts
|
|
445
|
-
function
|
|
799
|
+
function isObjectType(type) {
|
|
800
|
+
return !!(type.flags & ts4.TypeFlags.Object);
|
|
801
|
+
}
|
|
802
|
+
function isTypeReference(type) {
|
|
803
|
+
return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
|
|
804
|
+
}
|
|
805
|
+
function analyzeClassToIR(classDecl, checker, file = "") {
|
|
446
806
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
447
807
|
const fields = [];
|
|
808
|
+
const fieldLayouts = [];
|
|
809
|
+
const typeRegistry = {};
|
|
810
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
448
811
|
const instanceMethods = [];
|
|
449
812
|
const staticMethods = [];
|
|
450
813
|
for (const member of classDecl.members) {
|
|
451
814
|
if (ts4.isPropertyDeclaration(member)) {
|
|
452
|
-
const
|
|
453
|
-
if (
|
|
454
|
-
fields.push(
|
|
815
|
+
const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
|
|
816
|
+
if (fieldNode) {
|
|
817
|
+
fields.push(fieldNode);
|
|
818
|
+
fieldLayouts.push({});
|
|
455
819
|
}
|
|
456
820
|
} else if (ts4.isMethodDeclaration(member)) {
|
|
457
821
|
const methodInfo = analyzeMethod(member, checker);
|
|
@@ -465,155 +829,25 @@ function analyzeClass(classDecl, checker) {
|
|
|
465
829
|
}
|
|
466
830
|
}
|
|
467
831
|
}
|
|
468
|
-
return {
|
|
469
|
-
name,
|
|
470
|
-
fields,
|
|
471
|
-
instanceMethods,
|
|
472
|
-
staticMethods
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function analyzeField(prop, checker) {
|
|
476
|
-
if (!ts4.isIdentifier(prop.name)) {
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
|
-
const name = prop.name.text;
|
|
480
|
-
const typeNode = prop.type;
|
|
481
|
-
const type = checker.getTypeAtLocation(prop);
|
|
482
|
-
const optional = prop.questionToken !== void 0;
|
|
483
|
-
const decorators = extractDecorators(prop);
|
|
484
|
-
for (const dec of decorators) {
|
|
485
|
-
if (dec.node) {
|
|
486
|
-
const resolved = resolveDecorator(dec.node, checker);
|
|
487
|
-
if (resolved) {
|
|
488
|
-
dec.resolved = resolved;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
if (prop.type) {
|
|
493
|
-
const aliasConstraints = extractTypeAliasConstraints(prop.type, checker);
|
|
494
|
-
decorators.push(...aliasConstraints);
|
|
495
|
-
}
|
|
496
|
-
const jsdocConstraints = extractJSDocConstraints(prop);
|
|
497
|
-
decorators.push(...jsdocConstraints);
|
|
498
|
-
const deprecated = hasDeprecatedTag(prop);
|
|
499
|
-
const defaultValue = extractDefaultValue(prop.initializer);
|
|
500
|
-
return {
|
|
501
|
-
name,
|
|
502
|
-
typeNode,
|
|
503
|
-
type,
|
|
504
|
-
optional,
|
|
505
|
-
decorators,
|
|
506
|
-
deprecated,
|
|
507
|
-
defaultValue
|
|
508
|
-
};
|
|
832
|
+
return { name, fields, fieldLayouts, typeRegistry, instanceMethods, staticMethods };
|
|
509
833
|
}
|
|
510
|
-
function
|
|
511
|
-
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
512
|
-
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
513
|
-
if (!symbol?.declarations) return [];
|
|
514
|
-
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
515
|
-
if (!aliasDecl) return [];
|
|
516
|
-
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
517
|
-
return extractJSDocConstraints(aliasDecl);
|
|
518
|
-
}
|
|
519
|
-
function hasDeprecatedTag(node) {
|
|
520
|
-
const jsDocTags = ts4.getJSDocTags(node);
|
|
521
|
-
return jsDocTags.some((tag) => tag.tagName.text === "deprecated");
|
|
522
|
-
}
|
|
523
|
-
function extractDefaultValue(initializer) {
|
|
524
|
-
if (!initializer) return void 0;
|
|
525
|
-
if (ts4.isStringLiteral(initializer)) {
|
|
526
|
-
return initializer.text;
|
|
527
|
-
}
|
|
528
|
-
if (ts4.isNumericLiteral(initializer)) {
|
|
529
|
-
return Number(initializer.text);
|
|
530
|
-
}
|
|
531
|
-
if (initializer.kind === ts4.SyntaxKind.TrueKeyword) {
|
|
532
|
-
return true;
|
|
533
|
-
}
|
|
534
|
-
if (initializer.kind === ts4.SyntaxKind.FalseKeyword) {
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
if (initializer.kind === ts4.SyntaxKind.NullKeyword) {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
if (ts4.isPrefixUnaryExpression(initializer)) {
|
|
541
|
-
if (initializer.operator === ts4.SyntaxKind.MinusToken && ts4.isNumericLiteral(initializer.operand)) {
|
|
542
|
-
return -Number(initializer.operand.text);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return void 0;
|
|
546
|
-
}
|
|
547
|
-
function analyzeMethod(method, checker) {
|
|
548
|
-
if (!ts4.isIdentifier(method.name)) {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
const name = method.name.text;
|
|
552
|
-
const parameters = [];
|
|
553
|
-
for (const param of method.parameters) {
|
|
554
|
-
if (ts4.isIdentifier(param.name)) {
|
|
555
|
-
const paramInfo = analyzeParameter(param, checker);
|
|
556
|
-
parameters.push(paramInfo);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
const returnTypeNode = method.type;
|
|
560
|
-
const signature = checker.getSignatureFromDeclaration(method);
|
|
561
|
-
const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getTypeAtLocation(method);
|
|
562
|
-
return {
|
|
563
|
-
name,
|
|
564
|
-
parameters,
|
|
565
|
-
returnTypeNode,
|
|
566
|
-
returnType
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
function analyzeParameter(param, checker) {
|
|
570
|
-
const name = ts4.isIdentifier(param.name) ? param.name.text : "param";
|
|
571
|
-
const typeNode = param.type;
|
|
572
|
-
const type = checker.getTypeAtLocation(param);
|
|
573
|
-
const formSpecExportName = detectFormSpecReference(typeNode);
|
|
574
|
-
const optional = param.questionToken !== void 0 || param.initializer !== void 0;
|
|
575
|
-
return {
|
|
576
|
-
name,
|
|
577
|
-
typeNode,
|
|
578
|
-
type,
|
|
579
|
-
formSpecExportName,
|
|
580
|
-
optional
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function detectFormSpecReference(typeNode) {
|
|
584
|
-
if (!typeNode) return null;
|
|
585
|
-
if (!ts4.isTypeReferenceNode(typeNode)) return null;
|
|
586
|
-
const typeName = ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts4.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
|
|
587
|
-
if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
|
|
588
|
-
const typeArg = typeNode.typeArguments?.[0];
|
|
589
|
-
if (!typeArg || !ts4.isTypeQueryNode(typeArg)) return null;
|
|
590
|
-
if (ts4.isIdentifier(typeArg.exprName)) {
|
|
591
|
-
return typeArg.exprName.text;
|
|
592
|
-
}
|
|
593
|
-
if (ts4.isQualifiedName(typeArg.exprName)) {
|
|
594
|
-
return typeArg.exprName.right.text;
|
|
595
|
-
}
|
|
596
|
-
return null;
|
|
597
|
-
}
|
|
598
|
-
function analyzeInterface(interfaceDecl, checker) {
|
|
834
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
599
835
|
const name = interfaceDecl.name.text;
|
|
600
836
|
const fields = [];
|
|
837
|
+
const typeRegistry = {};
|
|
838
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
601
839
|
for (const member of interfaceDecl.members) {
|
|
602
840
|
if (ts4.isPropertySignature(member)) {
|
|
603
|
-
const
|
|
604
|
-
if (
|
|
605
|
-
fields.push(
|
|
841
|
+
const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
|
|
842
|
+
if (fieldNode) {
|
|
843
|
+
fields.push(fieldNode);
|
|
606
844
|
}
|
|
607
845
|
}
|
|
608
846
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
fields,
|
|
612
|
-
instanceMethods: [],
|
|
613
|
-
staticMethods: []
|
|
614
|
-
};
|
|
847
|
+
const fieldLayouts = fields.map(() => ({}));
|
|
848
|
+
return { name, fields, fieldLayouts, typeRegistry, instanceMethods: [], staticMethods: [] };
|
|
615
849
|
}
|
|
616
|
-
function
|
|
850
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
617
851
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
618
852
|
const sourceFile = typeAlias.getSourceFile();
|
|
619
853
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -625,11 +859,13 @@ function analyzeTypeAlias(typeAlias, checker) {
|
|
|
625
859
|
}
|
|
626
860
|
const name = typeAlias.name.text;
|
|
627
861
|
const fields = [];
|
|
862
|
+
const typeRegistry = {};
|
|
863
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
628
864
|
for (const member of typeAlias.type.members) {
|
|
629
865
|
if (ts4.isPropertySignature(member)) {
|
|
630
|
-
const
|
|
631
|
-
if (
|
|
632
|
-
fields.push(
|
|
866
|
+
const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
|
|
867
|
+
if (fieldNode) {
|
|
868
|
+
fields.push(fieldNode);
|
|
633
869
|
}
|
|
634
870
|
}
|
|
635
871
|
}
|
|
@@ -638,411 +874,601 @@ function analyzeTypeAlias(typeAlias, checker) {
|
|
|
638
874
|
analysis: {
|
|
639
875
|
name,
|
|
640
876
|
fields,
|
|
877
|
+
fieldLayouts: fields.map(() => ({})),
|
|
878
|
+
typeRegistry,
|
|
641
879
|
instanceMethods: [],
|
|
642
880
|
staticMethods: []
|
|
643
881
|
}
|
|
644
882
|
};
|
|
645
883
|
}
|
|
646
|
-
function
|
|
884
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
647
885
|
if (!ts4.isIdentifier(prop.name)) {
|
|
648
886
|
return null;
|
|
649
887
|
}
|
|
650
888
|
const name = prop.name.text;
|
|
651
|
-
const
|
|
652
|
-
const type = checker.getTypeAtLocation(prop);
|
|
889
|
+
const tsType = checker.getTypeAtLocation(prop);
|
|
653
890
|
const optional = prop.questionToken !== void 0;
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
891
|
+
const provenance = provenanceForNode(prop, file);
|
|
892
|
+
const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
893
|
+
const constraints = [];
|
|
894
|
+
if (prop.type) {
|
|
895
|
+
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
896
|
+
}
|
|
897
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
898
|
+
const annotations = [];
|
|
899
|
+
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
900
|
+
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
901
|
+
if (defaultAnnotation) {
|
|
902
|
+
annotations.push(defaultAnnotation);
|
|
903
|
+
}
|
|
666
904
|
return {
|
|
905
|
+
kind: "field",
|
|
667
906
|
name,
|
|
668
|
-
typeNode,
|
|
669
907
|
type,
|
|
670
|
-
optional,
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
908
|
+
required: !optional,
|
|
909
|
+
constraints,
|
|
910
|
+
annotations,
|
|
911
|
+
provenance
|
|
674
912
|
};
|
|
675
913
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
914
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
|
|
915
|
+
if (!ts4.isIdentifier(prop.name)) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
const name = prop.name.text;
|
|
919
|
+
const tsType = checker.getTypeAtLocation(prop);
|
|
920
|
+
const optional = prop.questionToken !== void 0;
|
|
921
|
+
const provenance = provenanceForNode(prop, file);
|
|
922
|
+
const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
923
|
+
const constraints = [];
|
|
924
|
+
if (prop.type) {
|
|
925
|
+
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
926
|
+
}
|
|
927
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
928
|
+
const annotations = [];
|
|
929
|
+
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
930
|
+
return {
|
|
931
|
+
kind: "field",
|
|
932
|
+
name,
|
|
933
|
+
type,
|
|
934
|
+
required: !optional,
|
|
935
|
+
constraints,
|
|
936
|
+
annotations,
|
|
937
|
+
provenance
|
|
938
|
+
};
|
|
683
939
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
940
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
941
|
+
if (type.flags & ts4.TypeFlags.String) {
|
|
942
|
+
return { kind: "primitive", primitiveKind: "string" };
|
|
943
|
+
}
|
|
944
|
+
if (type.flags & ts4.TypeFlags.Number) {
|
|
945
|
+
return { kind: "primitive", primitiveKind: "number" };
|
|
946
|
+
}
|
|
947
|
+
if (type.flags & ts4.TypeFlags.Boolean) {
|
|
948
|
+
return { kind: "primitive", primitiveKind: "boolean" };
|
|
949
|
+
}
|
|
950
|
+
if (type.flags & ts4.TypeFlags.Null) {
|
|
951
|
+
return { kind: "primitive", primitiveKind: "null" };
|
|
952
|
+
}
|
|
953
|
+
if (type.flags & ts4.TypeFlags.Undefined) {
|
|
954
|
+
return { kind: "primitive", primitiveKind: "null" };
|
|
955
|
+
}
|
|
956
|
+
if (type.isStringLiteral()) {
|
|
957
|
+
return {
|
|
958
|
+
kind: "enum",
|
|
959
|
+
members: [{ value: type.value }]
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
if (type.isNumberLiteral()) {
|
|
963
|
+
return {
|
|
964
|
+
kind: "enum",
|
|
965
|
+
members: [{ value: type.value }]
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
if (type.isUnion()) {
|
|
969
|
+
return resolveUnionType(type, checker, file, typeRegistry, visiting);
|
|
970
|
+
}
|
|
971
|
+
if (checker.isArrayType(type)) {
|
|
972
|
+
return resolveArrayType(type, checker, file, typeRegistry, visiting);
|
|
973
|
+
}
|
|
974
|
+
if (isObjectType(type)) {
|
|
975
|
+
return resolveObjectType(type, checker, file, typeRegistry, visiting);
|
|
976
|
+
}
|
|
977
|
+
return { kind: "primitive", primitiveKind: "string" };
|
|
978
|
+
}
|
|
979
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting) {
|
|
980
|
+
const allTypes = type.types;
|
|
981
|
+
const nonNullTypes = allTypes.filter(
|
|
982
|
+
(t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
983
|
+
);
|
|
984
|
+
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
985
|
+
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
|
|
986
|
+
if (isBooleanUnion2) {
|
|
987
|
+
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
988
|
+
if (hasNull) {
|
|
989
|
+
return {
|
|
990
|
+
kind: "union",
|
|
991
|
+
members: [boolNode, { kind: "primitive", primitiveKind: "null" }]
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
return boolNode;
|
|
995
|
+
}
|
|
996
|
+
const allStringLiterals = nonNullTypes.every((t) => t.isStringLiteral());
|
|
997
|
+
if (allStringLiterals && nonNullTypes.length > 0) {
|
|
998
|
+
const stringTypes = nonNullTypes.filter((t) => t.isStringLiteral());
|
|
999
|
+
const enumNode = {
|
|
1000
|
+
kind: "enum",
|
|
1001
|
+
members: stringTypes.map((t) => ({ value: t.value }))
|
|
1002
|
+
};
|
|
1003
|
+
if (hasNull) {
|
|
1004
|
+
return {
|
|
1005
|
+
kind: "union",
|
|
1006
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
return enumNode;
|
|
1010
|
+
}
|
|
1011
|
+
const allNumberLiterals = nonNullTypes.every((t) => t.isNumberLiteral());
|
|
1012
|
+
if (allNumberLiterals && nonNullTypes.length > 0) {
|
|
1013
|
+
const numberTypes = nonNullTypes.filter((t) => t.isNumberLiteral());
|
|
1014
|
+
const enumNode = {
|
|
1015
|
+
kind: "enum",
|
|
1016
|
+
members: numberTypes.map((t) => ({ value: t.value }))
|
|
1017
|
+
};
|
|
1018
|
+
if (hasNull) {
|
|
1019
|
+
return {
|
|
1020
|
+
kind: "union",
|
|
1021
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
return enumNode;
|
|
1025
|
+
}
|
|
1026
|
+
if (nonNullTypes.length === 1 && nonNullTypes[0]) {
|
|
1027
|
+
const inner = resolveTypeNode(nonNullTypes[0], checker, file, typeRegistry, visiting);
|
|
1028
|
+
if (hasNull) {
|
|
1029
|
+
return {
|
|
1030
|
+
kind: "union",
|
|
1031
|
+
members: [inner, { kind: "primitive", primitiveKind: "null" }]
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return inner;
|
|
1035
|
+
}
|
|
1036
|
+
const members = nonNullTypes.map(
|
|
1037
|
+
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting)
|
|
1038
|
+
);
|
|
1039
|
+
if (hasNull) {
|
|
1040
|
+
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
1041
|
+
}
|
|
1042
|
+
return { kind: "union", members };
|
|
1043
|
+
}
|
|
1044
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
1045
|
+
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
1046
|
+
const elementType = typeArgs?.[0];
|
|
1047
|
+
const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
|
|
1048
|
+
return { kind: "array", items };
|
|
1049
|
+
}
|
|
1050
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1051
|
+
if (visiting.has(type)) {
|
|
1052
|
+
return { kind: "object", properties: [], additionalProperties: false };
|
|
1053
|
+
}
|
|
1054
|
+
visiting.add(type);
|
|
1055
|
+
const typeName = getNamedTypeName(type);
|
|
1056
|
+
if (typeName && typeName in typeRegistry) {
|
|
1057
|
+
visiting.delete(type);
|
|
1058
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1059
|
+
}
|
|
1060
|
+
const properties = [];
|
|
1061
|
+
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
|
|
1062
|
+
for (const prop of type.getProperties()) {
|
|
1063
|
+
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
1064
|
+
if (!declaration) continue;
|
|
1065
|
+
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1066
|
+
const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
|
|
1067
|
+
const propTypeNode = resolveTypeNode(propType, checker, file, typeRegistry, visiting);
|
|
1068
|
+
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
1069
|
+
properties.push({
|
|
1070
|
+
name: prop.name,
|
|
1071
|
+
type: propTypeNode,
|
|
1072
|
+
optional,
|
|
1073
|
+
constraints: fieldNodeInfo?.constraints ?? [],
|
|
1074
|
+
annotations: fieldNodeInfo?.annotations ?? [],
|
|
1075
|
+
provenance: fieldNodeInfo?.provenance ?? provenanceForFile(file)
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
visiting.delete(type);
|
|
1079
|
+
const objectNode = {
|
|
1080
|
+
kind: "object",
|
|
1081
|
+
properties,
|
|
1082
|
+
additionalProperties: false
|
|
1083
|
+
};
|
|
1084
|
+
if (typeName) {
|
|
1085
|
+
typeRegistry[typeName] = {
|
|
1086
|
+
name: typeName,
|
|
1087
|
+
type: objectNode,
|
|
1088
|
+
provenance: provenanceForFile(file)
|
|
1089
|
+
};
|
|
1090
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1091
|
+
}
|
|
1092
|
+
return objectNode;
|
|
1093
|
+
}
|
|
1094
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
|
|
687
1095
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
688
1096
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
689
1097
|
);
|
|
690
1098
|
for (const symbol of symbols) {
|
|
691
1099
|
const declarations = symbol.declarations;
|
|
692
1100
|
if (!declarations) continue;
|
|
693
|
-
const classDecl = declarations.find(
|
|
1101
|
+
const classDecl = declarations.find(ts4.isClassDeclaration);
|
|
694
1102
|
if (classDecl) {
|
|
695
1103
|
const map = /* @__PURE__ */ new Map();
|
|
696
1104
|
for (const member of classDecl.members) {
|
|
697
|
-
if (
|
|
698
|
-
const
|
|
699
|
-
if (
|
|
1105
|
+
if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
|
|
1106
|
+
const fieldNode = analyzeFieldToIR(member, checker, file, typeRegistry, visiting);
|
|
1107
|
+
if (fieldNode) {
|
|
1108
|
+
map.set(fieldNode.name, {
|
|
1109
|
+
constraints: [...fieldNode.constraints],
|
|
1110
|
+
annotations: [...fieldNode.annotations],
|
|
1111
|
+
provenance: fieldNode.provenance
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
700
1114
|
}
|
|
701
1115
|
}
|
|
702
1116
|
return map;
|
|
703
1117
|
}
|
|
704
|
-
const interfaceDecl = declarations.find(
|
|
1118
|
+
const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
|
|
705
1119
|
if (interfaceDecl) {
|
|
706
|
-
return
|
|
1120
|
+
return buildFieldNodeInfoMap(interfaceDecl.members, checker, file, typeRegistry, visiting);
|
|
707
1121
|
}
|
|
708
|
-
const typeAliasDecl = declarations.find(
|
|
709
|
-
if (typeAliasDecl &&
|
|
710
|
-
return
|
|
1122
|
+
const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
|
|
1123
|
+
if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
1124
|
+
return buildFieldNodeInfoMap(
|
|
1125
|
+
typeAliasDecl.type.members,
|
|
1126
|
+
checker,
|
|
1127
|
+
file,
|
|
1128
|
+
typeRegistry,
|
|
1129
|
+
visiting
|
|
1130
|
+
);
|
|
711
1131
|
}
|
|
712
1132
|
}
|
|
713
1133
|
return null;
|
|
714
1134
|
}
|
|
715
|
-
function
|
|
1135
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
716
1136
|
const map = /* @__PURE__ */ new Map();
|
|
717
1137
|
for (const member of members) {
|
|
718
|
-
if (
|
|
719
|
-
const
|
|
720
|
-
if (
|
|
721
|
-
map.set(
|
|
1138
|
+
if (ts4.isPropertySignature(member)) {
|
|
1139
|
+
const fieldNode = analyzeInterfacePropertyToIR(member, checker, file, typeRegistry, visiting);
|
|
1140
|
+
if (fieldNode) {
|
|
1141
|
+
map.set(fieldNode.name, {
|
|
1142
|
+
constraints: [...fieldNode.constraints],
|
|
1143
|
+
annotations: [...fieldNode.annotations],
|
|
1144
|
+
provenance: fieldNode.provenance
|
|
1145
|
+
});
|
|
722
1146
|
}
|
|
723
1147
|
}
|
|
724
1148
|
}
|
|
725
1149
|
return map;
|
|
726
1150
|
}
|
|
727
|
-
function
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const fieldInfo = fieldInfoMap?.get(prop.name) ?? void 0;
|
|
736
|
-
result.push({ name: prop.name, type: propType, optional, fieldInfo });
|
|
737
|
-
}
|
|
738
|
-
return result;
|
|
1151
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file) {
|
|
1152
|
+
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
1153
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1154
|
+
if (!symbol?.declarations) return [];
|
|
1155
|
+
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
1156
|
+
if (!aliasDecl) return [];
|
|
1157
|
+
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
1158
|
+
return extractJSDocConstraintNodes(aliasDecl, file);
|
|
739
1159
|
}
|
|
740
|
-
function
|
|
741
|
-
|
|
1160
|
+
function provenanceForNode(node, file) {
|
|
1161
|
+
const sourceFile = node.getSourceFile();
|
|
1162
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
1163
|
+
return {
|
|
1164
|
+
surface: "tsdoc",
|
|
1165
|
+
file,
|
|
1166
|
+
line: line + 1,
|
|
1167
|
+
column: character
|
|
1168
|
+
};
|
|
742
1169
|
}
|
|
743
|
-
function
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
return { jsonSchema: { type: "null" }, formSpecFieldType: "null" };
|
|
755
|
-
}
|
|
756
|
-
if (type.flags & ts5.TypeFlags.Undefined) {
|
|
757
|
-
return { jsonSchema: {}, formSpecFieldType: "undefined" };
|
|
1170
|
+
function provenanceForFile(file) {
|
|
1171
|
+
return { surface: "tsdoc", file, line: 0, column: 0 };
|
|
1172
|
+
}
|
|
1173
|
+
function getNamedTypeName(type) {
|
|
1174
|
+
const symbol = type.getSymbol();
|
|
1175
|
+
if (symbol?.declarations) {
|
|
1176
|
+
const decl = symbol.declarations[0];
|
|
1177
|
+
if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
|
|
1178
|
+
const name = ts4.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
|
|
1179
|
+
if (name) return name;
|
|
1180
|
+
}
|
|
758
1181
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
1182
|
+
const aliasSymbol = type.aliasSymbol;
|
|
1183
|
+
if (aliasSymbol?.declarations) {
|
|
1184
|
+
const aliasDecl = aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
1185
|
+
if (aliasDecl) {
|
|
1186
|
+
return aliasDecl.name.text;
|
|
1187
|
+
}
|
|
764
1188
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
function analyzeMethod(method, checker) {
|
|
1192
|
+
if (!ts4.isIdentifier(method.name)) {
|
|
1193
|
+
return null;
|
|
770
1194
|
}
|
|
771
|
-
|
|
772
|
-
|
|
1195
|
+
const name = method.name.text;
|
|
1196
|
+
const parameters = [];
|
|
1197
|
+
for (const param of method.parameters) {
|
|
1198
|
+
if (ts4.isIdentifier(param.name)) {
|
|
1199
|
+
const paramInfo = analyzeParameter(param, checker);
|
|
1200
|
+
parameters.push(paramInfo);
|
|
1201
|
+
}
|
|
773
1202
|
}
|
|
774
|
-
|
|
775
|
-
|
|
1203
|
+
const returnTypeNode = method.type;
|
|
1204
|
+
const signature = checker.getSignatureFromDeclaration(method);
|
|
1205
|
+
const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getTypeAtLocation(method);
|
|
1206
|
+
return { name, parameters, returnTypeNode, returnType };
|
|
1207
|
+
}
|
|
1208
|
+
function analyzeParameter(param, checker) {
|
|
1209
|
+
const name = ts4.isIdentifier(param.name) ? param.name.text : "param";
|
|
1210
|
+
const typeNode = param.type;
|
|
1211
|
+
const type = checker.getTypeAtLocation(param);
|
|
1212
|
+
const formSpecExportName = detectFormSpecReference(typeNode);
|
|
1213
|
+
const optional = param.questionToken !== void 0 || param.initializer !== void 0;
|
|
1214
|
+
return { name, typeNode, type, formSpecExportName, optional };
|
|
1215
|
+
}
|
|
1216
|
+
function detectFormSpecReference(typeNode) {
|
|
1217
|
+
if (!typeNode) return null;
|
|
1218
|
+
if (!ts4.isTypeReferenceNode(typeNode)) return null;
|
|
1219
|
+
const typeName = ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts4.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
|
|
1220
|
+
if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
|
|
1221
|
+
const typeArg = typeNode.typeArguments?.[0];
|
|
1222
|
+
if (!typeArg || !ts4.isTypeQueryNode(typeArg)) return null;
|
|
1223
|
+
if (ts4.isIdentifier(typeArg.exprName)) {
|
|
1224
|
+
return typeArg.exprName.text;
|
|
776
1225
|
}
|
|
777
|
-
if (
|
|
778
|
-
return
|
|
1226
|
+
if (ts4.isQualifiedName(typeArg.exprName)) {
|
|
1227
|
+
return typeArg.exprName.right.text;
|
|
779
1228
|
}
|
|
780
|
-
return
|
|
1229
|
+
return null;
|
|
781
1230
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
jsonSchema: { type: "boolean" },
|
|
792
|
-
formSpecFieldType: "boolean"
|
|
793
|
-
};
|
|
794
|
-
if (hasNull) {
|
|
795
|
-
result.jsonSchema = { oneOf: [{ type: "boolean" }, { type: "null" }] };
|
|
796
|
-
}
|
|
797
|
-
return result;
|
|
1231
|
+
|
|
1232
|
+
// src/json-schema/ir-generator.ts
|
|
1233
|
+
function makeContext() {
|
|
1234
|
+
return { defs: {} };
|
|
1235
|
+
}
|
|
1236
|
+
function generateJsonSchemaFromIR(ir) {
|
|
1237
|
+
const ctx = makeContext();
|
|
1238
|
+
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
1239
|
+
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
798
1240
|
}
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1241
|
+
const properties = {};
|
|
1242
|
+
const required = [];
|
|
1243
|
+
collectFields(ir.elements, properties, required, ctx);
|
|
1244
|
+
const uniqueRequired = [...new Set(required)];
|
|
1245
|
+
const result = {
|
|
1246
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1247
|
+
type: "object",
|
|
1248
|
+
properties,
|
|
1249
|
+
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
1250
|
+
};
|
|
1251
|
+
if (Object.keys(ctx.defs).length > 0) {
|
|
1252
|
+
result.$defs = ctx.defs;
|
|
810
1253
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1254
|
+
return result;
|
|
1255
|
+
}
|
|
1256
|
+
function collectFields(elements, properties, required, ctx) {
|
|
1257
|
+
for (const element of elements) {
|
|
1258
|
+
switch (element.kind) {
|
|
1259
|
+
case "field":
|
|
1260
|
+
properties[element.name] = generateFieldSchema(element, ctx);
|
|
1261
|
+
if (element.required) {
|
|
1262
|
+
required.push(element.name);
|
|
1263
|
+
}
|
|
1264
|
+
break;
|
|
1265
|
+
case "group":
|
|
1266
|
+
collectFields(element.elements, properties, required, ctx);
|
|
1267
|
+
break;
|
|
1268
|
+
case "conditional":
|
|
1269
|
+
collectFields(element.elements, properties, required, ctx);
|
|
1270
|
+
break;
|
|
1271
|
+
default: {
|
|
1272
|
+
const _exhaustive = element;
|
|
1273
|
+
void _exhaustive;
|
|
1274
|
+
}
|
|
820
1275
|
}
|
|
821
|
-
return result;
|
|
822
1276
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1277
|
+
}
|
|
1278
|
+
function generateFieldSchema(field, ctx) {
|
|
1279
|
+
const schema = generateTypeNode(field.type, ctx);
|
|
1280
|
+
applyConstraints(schema, field.constraints);
|
|
1281
|
+
applyAnnotations(schema, field.annotations);
|
|
1282
|
+
return schema;
|
|
1283
|
+
}
|
|
1284
|
+
function generateTypeNode(type, ctx) {
|
|
1285
|
+
switch (type.kind) {
|
|
1286
|
+
case "primitive":
|
|
1287
|
+
return generatePrimitiveType(type);
|
|
1288
|
+
case "enum":
|
|
1289
|
+
return generateEnumType(type);
|
|
1290
|
+
case "array":
|
|
1291
|
+
return generateArrayType(type, ctx);
|
|
1292
|
+
case "object":
|
|
1293
|
+
return generateObjectType(type, ctx);
|
|
1294
|
+
case "union":
|
|
1295
|
+
return generateUnionType(type, ctx);
|
|
1296
|
+
case "reference":
|
|
1297
|
+
return generateReferenceType(type);
|
|
1298
|
+
case "dynamic":
|
|
1299
|
+
return generateDynamicType(type);
|
|
1300
|
+
case "custom":
|
|
1301
|
+
return generateCustomType(type);
|
|
1302
|
+
default: {
|
|
1303
|
+
const _exhaustive = type;
|
|
1304
|
+
return _exhaustive;
|
|
827
1305
|
}
|
|
828
|
-
return result;
|
|
829
1306
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1307
|
+
}
|
|
1308
|
+
function generatePrimitiveType(type) {
|
|
1309
|
+
return { type: type.primitiveKind };
|
|
1310
|
+
}
|
|
1311
|
+
function generateEnumType(type) {
|
|
1312
|
+
const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
|
|
1313
|
+
if (hasDisplayNames) {
|
|
1314
|
+
return {
|
|
1315
|
+
oneOf: type.members.map((m) => {
|
|
1316
|
+
const entry = { const: m.value };
|
|
1317
|
+
if (m.displayName !== void 0) {
|
|
1318
|
+
entry.title = m.displayName;
|
|
1319
|
+
}
|
|
1320
|
+
return entry;
|
|
1321
|
+
})
|
|
1322
|
+
};
|
|
833
1323
|
}
|
|
834
|
-
return {
|
|
835
|
-
jsonSchema: { oneOf: schemas },
|
|
836
|
-
formSpecFieldType: "union"
|
|
837
|
-
};
|
|
1324
|
+
return { enum: type.members.map((m) => m.value) };
|
|
838
1325
|
}
|
|
839
|
-
function
|
|
840
|
-
const typeArgs = type.typeArguments;
|
|
841
|
-
const elementType = typeArgs?.[0];
|
|
842
|
-
const itemSchema = elementType ? convertTypeInternal(elementType, checker, visiting).jsonSchema : {};
|
|
1326
|
+
function generateArrayType(type, ctx) {
|
|
843
1327
|
return {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
items: itemSchema
|
|
847
|
-
},
|
|
848
|
-
formSpecFieldType: "array"
|
|
1328
|
+
type: "array",
|
|
1329
|
+
items: generateTypeNode(type.items, ctx)
|
|
849
1330
|
};
|
|
850
1331
|
}
|
|
851
|
-
function
|
|
852
|
-
if (visiting.has(type)) {
|
|
853
|
-
return { jsonSchema: { type: "object" }, formSpecFieldType: "object" };
|
|
854
|
-
}
|
|
855
|
-
visiting.add(type);
|
|
1332
|
+
function generateObjectType(type, ctx) {
|
|
856
1333
|
const properties = {};
|
|
857
1334
|
const required = [];
|
|
858
|
-
for (const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
required.push(propInfo.name);
|
|
1335
|
+
for (const prop of type.properties) {
|
|
1336
|
+
properties[prop.name] = generatePropertySchema(prop, ctx);
|
|
1337
|
+
if (!prop.optional) {
|
|
1338
|
+
required.push(prop.name);
|
|
863
1339
|
}
|
|
864
1340
|
}
|
|
865
|
-
|
|
1341
|
+
const schema = { type: "object", properties };
|
|
1342
|
+
if (required.length > 0) {
|
|
1343
|
+
schema.required = required;
|
|
1344
|
+
}
|
|
1345
|
+
if (!type.additionalProperties) {
|
|
1346
|
+
schema.additionalProperties = false;
|
|
1347
|
+
}
|
|
1348
|
+
return schema;
|
|
1349
|
+
}
|
|
1350
|
+
function generatePropertySchema(prop, ctx) {
|
|
1351
|
+
const schema = generateTypeNode(prop.type, ctx);
|
|
1352
|
+
applyConstraints(schema, prop.constraints);
|
|
1353
|
+
applyAnnotations(schema, prop.annotations);
|
|
1354
|
+
return schema;
|
|
1355
|
+
}
|
|
1356
|
+
function generateUnionType(type, ctx) {
|
|
1357
|
+
if (isBooleanUnion(type)) {
|
|
1358
|
+
return { type: "boolean" };
|
|
1359
|
+
}
|
|
866
1360
|
return {
|
|
867
|
-
|
|
868
|
-
type: "object",
|
|
869
|
-
properties,
|
|
870
|
-
...required.length > 0 ? { required } : {}
|
|
871
|
-
},
|
|
872
|
-
formSpecFieldType: "object"
|
|
1361
|
+
anyOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
873
1362
|
};
|
|
874
1363
|
}
|
|
875
|
-
function
|
|
876
|
-
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
if (
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
propInfo.name,
|
|
892
|
-
propInfo.type,
|
|
893
|
-
propInfo.fieldInfo?.decorators ?? [],
|
|
894
|
-
propInfo.optional,
|
|
895
|
-
checker,
|
|
896
|
-
visitedTypes
|
|
897
|
-
)
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
visitedTypes.delete(type);
|
|
901
|
-
if (nestedFields.length > 0) {
|
|
902
|
-
field.fields = nestedFields;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
for (const dec of decorators) {
|
|
907
|
-
applyDecoratorToField(field, dec);
|
|
908
|
-
}
|
|
909
|
-
return field;
|
|
910
|
-
}
|
|
911
|
-
function applyDecoratorToField(field, decorator) {
|
|
912
|
-
const { args } = decorator;
|
|
913
|
-
const resolved = decorator.resolved;
|
|
914
|
-
const effectiveName = resolved?.extendsBuiltin ?? decorator.name;
|
|
915
|
-
switch (effectiveName) {
|
|
916
|
-
case "Field": {
|
|
917
|
-
const opts = args[0];
|
|
918
|
-
if (typeof opts === "object" && opts !== null && !Array.isArray(opts)) {
|
|
919
|
-
if (typeof opts["displayName"] === "string") {
|
|
920
|
-
field.label = opts["displayName"];
|
|
921
|
-
}
|
|
922
|
-
if (typeof opts["description"] === "string") {
|
|
923
|
-
field.description = opts["description"];
|
|
924
|
-
}
|
|
925
|
-
if (typeof opts["placeholder"] === "string") {
|
|
926
|
-
field.placeholder = opts["placeholder"];
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
break;
|
|
1364
|
+
function isBooleanUnion(type) {
|
|
1365
|
+
if (type.members.length !== 2) return false;
|
|
1366
|
+
const kinds = type.members.map((m) => m.kind);
|
|
1367
|
+
return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
|
|
1368
|
+
}
|
|
1369
|
+
function generateReferenceType(type) {
|
|
1370
|
+
return { $ref: `#/$defs/${type.name}` };
|
|
1371
|
+
}
|
|
1372
|
+
function generateDynamicType(type) {
|
|
1373
|
+
if (type.dynamicKind === "enum") {
|
|
1374
|
+
const schema = {
|
|
1375
|
+
type: "string",
|
|
1376
|
+
"x-formspec-source": type.sourceKey
|
|
1377
|
+
};
|
|
1378
|
+
if (type.parameterFields.length > 0) {
|
|
1379
|
+
schema["x-formspec-params"] = [...type.parameterFields];
|
|
930
1380
|
}
|
|
931
|
-
|
|
932
|
-
if (typeof args[0] === "number") {
|
|
933
|
-
field.min = args[0];
|
|
934
|
-
}
|
|
935
|
-
break;
|
|
936
|
-
case "Maximum":
|
|
937
|
-
if (typeof args[0] === "number") {
|
|
938
|
-
field.max = args[0];
|
|
939
|
-
}
|
|
940
|
-
break;
|
|
941
|
-
case "MinLength":
|
|
942
|
-
if (typeof args[0] === "number") {
|
|
943
|
-
field.minLength = args[0];
|
|
944
|
-
}
|
|
945
|
-
break;
|
|
946
|
-
case "MaxLength":
|
|
947
|
-
if (typeof args[0] === "number") {
|
|
948
|
-
field.maxLength = args[0];
|
|
949
|
-
}
|
|
950
|
-
break;
|
|
951
|
-
case "Pattern":
|
|
952
|
-
if (typeof args[0] === "string") {
|
|
953
|
-
field.pattern = args[0];
|
|
954
|
-
}
|
|
955
|
-
break;
|
|
956
|
-
case "EnumOptions":
|
|
957
|
-
if (Array.isArray(args[0])) {
|
|
958
|
-
field.options = args[0];
|
|
959
|
-
}
|
|
960
|
-
break;
|
|
961
|
-
case "ShowWhen":
|
|
962
|
-
if (typeof args[0] === "object" && args[0] !== null) {
|
|
963
|
-
field.showWhen = args[0];
|
|
964
|
-
}
|
|
965
|
-
break;
|
|
966
|
-
case "Group":
|
|
967
|
-
if (typeof args[0] === "string") {
|
|
968
|
-
field.group = args[0];
|
|
969
|
-
}
|
|
970
|
-
break;
|
|
1381
|
+
return schema;
|
|
971
1382
|
}
|
|
1383
|
+
return {
|
|
1384
|
+
type: "object",
|
|
1385
|
+
additionalProperties: true,
|
|
1386
|
+
"x-formspec-schemaSource": type.sourceKey
|
|
1387
|
+
};
|
|
972
1388
|
}
|
|
973
|
-
function
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1389
|
+
function generateCustomType(_type) {
|
|
1390
|
+
return { type: "object" };
|
|
1391
|
+
}
|
|
1392
|
+
function applyConstraints(schema, constraints) {
|
|
1393
|
+
for (const constraint of constraints) {
|
|
1394
|
+
switch (constraint.constraintKind) {
|
|
1395
|
+
case "minimum":
|
|
1396
|
+
schema.minimum = constraint.value;
|
|
1397
|
+
break;
|
|
1398
|
+
case "maximum":
|
|
1399
|
+
schema.maximum = constraint.value;
|
|
1400
|
+
break;
|
|
1401
|
+
case "exclusiveMinimum":
|
|
1402
|
+
schema.exclusiveMinimum = constraint.value;
|
|
1403
|
+
break;
|
|
1404
|
+
case "exclusiveMaximum":
|
|
1405
|
+
schema.exclusiveMaximum = constraint.value;
|
|
1406
|
+
break;
|
|
1407
|
+
case "multipleOf": {
|
|
1408
|
+
const { value } = constraint;
|
|
1409
|
+
if (value === 1 && schema.type === "number") {
|
|
1410
|
+
schema.type = "integer";
|
|
1411
|
+
} else {
|
|
1412
|
+
schema.multipleOf = value;
|
|
989
1413
|
}
|
|
990
1414
|
break;
|
|
991
1415
|
}
|
|
992
|
-
case "
|
|
993
|
-
|
|
994
|
-
result.minimum = args[0];
|
|
995
|
-
}
|
|
1416
|
+
case "minLength":
|
|
1417
|
+
schema.minLength = constraint.value;
|
|
996
1418
|
break;
|
|
997
|
-
case "
|
|
998
|
-
|
|
999
|
-
result.maximum = args[0];
|
|
1000
|
-
}
|
|
1419
|
+
case "maxLength":
|
|
1420
|
+
schema.maxLength = constraint.value;
|
|
1001
1421
|
break;
|
|
1002
|
-
case "
|
|
1003
|
-
|
|
1004
|
-
result.exclusiveMinimum = args[0];
|
|
1005
|
-
}
|
|
1422
|
+
case "minItems":
|
|
1423
|
+
schema.minItems = constraint.value;
|
|
1006
1424
|
break;
|
|
1007
|
-
case "
|
|
1008
|
-
|
|
1009
|
-
result.exclusiveMaximum = args[0];
|
|
1010
|
-
}
|
|
1425
|
+
case "maxItems":
|
|
1426
|
+
schema.maxItems = constraint.value;
|
|
1011
1427
|
break;
|
|
1012
|
-
case "
|
|
1013
|
-
|
|
1014
|
-
result.minLength = args[0];
|
|
1015
|
-
}
|
|
1428
|
+
case "pattern":
|
|
1429
|
+
schema.pattern = constraint.pattern;
|
|
1016
1430
|
break;
|
|
1017
|
-
case "
|
|
1018
|
-
|
|
1019
|
-
result.maxLength = args[0];
|
|
1020
|
-
}
|
|
1431
|
+
case "uniqueItems":
|
|
1432
|
+
schema.uniqueItems = constraint.value;
|
|
1021
1433
|
break;
|
|
1022
|
-
case "
|
|
1023
|
-
if (typeof args[0] === "string") {
|
|
1024
|
-
result.pattern = args[0];
|
|
1025
|
-
}
|
|
1434
|
+
case "allowedMembers":
|
|
1026
1435
|
break;
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
} else {
|
|
1033
|
-
setSchemaExtension(result, key, args[0] ?? true);
|
|
1436
|
+
case "custom":
|
|
1437
|
+
break;
|
|
1438
|
+
default: {
|
|
1439
|
+
const _exhaustive = constraint;
|
|
1440
|
+
void _exhaustive;
|
|
1034
1441
|
}
|
|
1035
1442
|
}
|
|
1036
1443
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1444
|
+
}
|
|
1445
|
+
function applyAnnotations(schema, annotations) {
|
|
1446
|
+
for (const annotation of annotations) {
|
|
1447
|
+
switch (annotation.annotationKind) {
|
|
1448
|
+
case "displayName":
|
|
1449
|
+
schema.title = annotation.value;
|
|
1450
|
+
break;
|
|
1451
|
+
case "description":
|
|
1452
|
+
schema.description = annotation.value;
|
|
1453
|
+
break;
|
|
1454
|
+
case "defaultValue":
|
|
1455
|
+
schema.default = annotation.value;
|
|
1456
|
+
break;
|
|
1457
|
+
case "deprecated":
|
|
1458
|
+
schema.deprecated = true;
|
|
1459
|
+
break;
|
|
1460
|
+
case "placeholder":
|
|
1461
|
+
break;
|
|
1462
|
+
case "formatHint":
|
|
1463
|
+
break;
|
|
1464
|
+
case "custom":
|
|
1465
|
+
break;
|
|
1466
|
+
default: {
|
|
1467
|
+
const _exhaustive = annotation;
|
|
1468
|
+
void _exhaustive;
|
|
1469
|
+
}
|
|
1043
1470
|
}
|
|
1044
1471
|
}
|
|
1045
|
-
return result;
|
|
1046
1472
|
}
|
|
1047
1473
|
|
|
1048
1474
|
// src/ui-schema/schema.ts
|
|
@@ -1153,7 +1579,7 @@ var uiSchema = import_zod.z.lazy(
|
|
|
1153
1579
|
() => import_zod.z.union([verticalLayoutSchema, horizontalLayoutSchema, groupLayoutSchema, categorizationSchema])
|
|
1154
1580
|
);
|
|
1155
1581
|
|
|
1156
|
-
// src/ui-schema/generator.ts
|
|
1582
|
+
// src/ui-schema/ir-generator.ts
|
|
1157
1583
|
var import_zod2 = require("zod");
|
|
1158
1584
|
function parseOrThrow(schema, value, label) {
|
|
1159
1585
|
try {
|
|
@@ -1168,101 +1594,488 @@ ${error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
|
1168
1594
|
throw error;
|
|
1169
1595
|
}
|
|
1170
1596
|
}
|
|
1171
|
-
function
|
|
1597
|
+
function fieldToScope(fieldName) {
|
|
1598
|
+
return `#/properties/${fieldName}`;
|
|
1599
|
+
}
|
|
1600
|
+
function createShowRule(fieldName, value) {
|
|
1601
|
+
return {
|
|
1602
|
+
effect: "SHOW",
|
|
1603
|
+
condition: {
|
|
1604
|
+
scope: fieldToScope(fieldName),
|
|
1605
|
+
schema: { const: value }
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function combineRules(parentRule, childRule) {
|
|
1610
|
+
const parentCondition = parentRule.condition;
|
|
1611
|
+
const childCondition = childRule.condition;
|
|
1612
|
+
return {
|
|
1613
|
+
effect: "SHOW",
|
|
1614
|
+
condition: {
|
|
1615
|
+
scope: "#",
|
|
1616
|
+
schema: {
|
|
1617
|
+
allOf: [
|
|
1618
|
+
{
|
|
1619
|
+
properties: {
|
|
1620
|
+
[parentCondition.scope.replace("#/properties/", "")]: parentCondition.schema
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
properties: {
|
|
1625
|
+
[childCondition.scope.replace("#/properties/", "")]: childCondition.schema
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
function fieldNodeToControl(field, parentRule) {
|
|
1634
|
+
const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
|
|
1172
1635
|
const control = {
|
|
1173
1636
|
type: "Control",
|
|
1174
|
-
scope:
|
|
1637
|
+
scope: fieldToScope(field.name),
|
|
1638
|
+
...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
|
|
1639
|
+
...parentRule !== void 0 && { rule: parentRule }
|
|
1175
1640
|
};
|
|
1176
|
-
if (field.label !== void 0) {
|
|
1177
|
-
control.label = field.label;
|
|
1178
|
-
}
|
|
1179
|
-
if (field.showWhen !== void 0 && typeof field.showWhen === "object" && "field" in field.showWhen && "value" in field.showWhen) {
|
|
1180
|
-
const sw = field.showWhen;
|
|
1181
|
-
control.rule = {
|
|
1182
|
-
effect: "SHOW",
|
|
1183
|
-
condition: {
|
|
1184
|
-
scope: `#/properties/${sw.field}`,
|
|
1185
|
-
schema: { const: sw.value }
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
1641
|
return control;
|
|
1190
1642
|
}
|
|
1191
|
-
function
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1643
|
+
function groupNodeToLayout(group, parentRule) {
|
|
1644
|
+
return {
|
|
1645
|
+
type: "Group",
|
|
1646
|
+
label: group.label,
|
|
1647
|
+
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
1648
|
+
...parentRule !== void 0 && { rule: parentRule }
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function irElementsToUiSchema(elements, parentRule) {
|
|
1652
|
+
const result = [];
|
|
1653
|
+
for (const element of elements) {
|
|
1654
|
+
switch (element.kind) {
|
|
1655
|
+
case "field": {
|
|
1656
|
+
result.push(fieldNodeToControl(element, parentRule));
|
|
1657
|
+
break;
|
|
1201
1658
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1659
|
+
case "group": {
|
|
1660
|
+
result.push(groupNodeToLayout(element, parentRule));
|
|
1661
|
+
break;
|
|
1662
|
+
}
|
|
1663
|
+
case "conditional": {
|
|
1664
|
+
const newRule = createShowRule(element.fieldName, element.value);
|
|
1665
|
+
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
1666
|
+
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
1667
|
+
result.push(...childElements);
|
|
1668
|
+
break;
|
|
1669
|
+
}
|
|
1670
|
+
default: {
|
|
1671
|
+
const _exhaustive = element;
|
|
1672
|
+
void _exhaustive;
|
|
1673
|
+
throw new Error("Unhandled IR element kind");
|
|
1215
1674
|
}
|
|
1216
|
-
} else {
|
|
1217
|
-
const groupElements = groupMap.get(key) ?? [];
|
|
1218
|
-
const groupLayout = {
|
|
1219
|
-
type: "Group",
|
|
1220
|
-
label: key,
|
|
1221
|
-
elements: groupElements
|
|
1222
|
-
};
|
|
1223
|
-
elements.push(groupLayout);
|
|
1224
1675
|
}
|
|
1225
1676
|
}
|
|
1677
|
+
return result;
|
|
1678
|
+
}
|
|
1679
|
+
function generateUiSchemaFromIR(ir) {
|
|
1226
1680
|
const result = {
|
|
1227
1681
|
type: "VerticalLayout",
|
|
1228
|
-
elements
|
|
1682
|
+
elements: irElementsToUiSchema(ir.elements)
|
|
1229
1683
|
};
|
|
1230
1684
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
1231
1685
|
}
|
|
1232
1686
|
|
|
1233
1687
|
// src/generators/class-schema.ts
|
|
1234
|
-
function generateClassSchemas(analysis,
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1688
|
+
function generateClassSchemas(analysis, source) {
|
|
1689
|
+
const ir = canonicalizeTSDoc(analysis, source);
|
|
1690
|
+
return {
|
|
1691
|
+
jsonSchema: generateJsonSchemaFromIR(ir),
|
|
1692
|
+
uiSchema: generateUiSchemaFromIR(ir)
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// src/validate/constraint-validator.ts
|
|
1697
|
+
function makeCode(ctx, category, number) {
|
|
1698
|
+
return `${ctx.vendorPrefix}-${category}-${String(number).padStart(3, "0")}`;
|
|
1699
|
+
}
|
|
1700
|
+
function addContradiction(ctx, message, primary, related) {
|
|
1701
|
+
ctx.diagnostics.push({
|
|
1702
|
+
code: makeCode(ctx, "CONTRADICTION", 1),
|
|
1703
|
+
message,
|
|
1704
|
+
severity: "error",
|
|
1705
|
+
primaryLocation: primary,
|
|
1706
|
+
relatedLocations: [related]
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
function addTypeMismatch(ctx, message, primary) {
|
|
1710
|
+
ctx.diagnostics.push({
|
|
1711
|
+
code: makeCode(ctx, "TYPE_MISMATCH", 1),
|
|
1712
|
+
message,
|
|
1713
|
+
severity: "error",
|
|
1714
|
+
primaryLocation: primary,
|
|
1715
|
+
relatedLocations: []
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
function addUnknownExtension(ctx, message, primary) {
|
|
1719
|
+
ctx.diagnostics.push({
|
|
1720
|
+
code: makeCode(ctx, "UNKNOWN_EXTENSION", 1),
|
|
1721
|
+
message,
|
|
1722
|
+
severity: "warning",
|
|
1723
|
+
primaryLocation: primary,
|
|
1724
|
+
relatedLocations: []
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
function findNumeric(constraints, constraintKind) {
|
|
1728
|
+
return constraints.find(
|
|
1729
|
+
(c) => c.constraintKind === constraintKind
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
function findLength(constraints, constraintKind) {
|
|
1733
|
+
return constraints.find(
|
|
1734
|
+
(c) => c.constraintKind === constraintKind
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
function findAllowedMembers(constraints) {
|
|
1738
|
+
return constraints.filter(
|
|
1739
|
+
(c) => c.constraintKind === "allowedMembers"
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
function checkNumericContradictions(ctx, fieldName, constraints) {
|
|
1743
|
+
const min = findNumeric(constraints, "minimum");
|
|
1744
|
+
const max = findNumeric(constraints, "maximum");
|
|
1745
|
+
const exMin = findNumeric(constraints, "exclusiveMinimum");
|
|
1746
|
+
const exMax = findNumeric(constraints, "exclusiveMaximum");
|
|
1747
|
+
if (min !== void 0 && max !== void 0 && min.value > max.value) {
|
|
1748
|
+
addContradiction(
|
|
1749
|
+
ctx,
|
|
1750
|
+
`Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
|
|
1751
|
+
min.provenance,
|
|
1752
|
+
max.provenance
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
|
|
1756
|
+
addContradiction(
|
|
1757
|
+
ctx,
|
|
1758
|
+
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
|
|
1759
|
+
exMin.provenance,
|
|
1760
|
+
max.provenance
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
|
|
1764
|
+
addContradiction(
|
|
1765
|
+
ctx,
|
|
1766
|
+
`Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
1767
|
+
min.provenance,
|
|
1768
|
+
exMax.provenance
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
|
|
1772
|
+
addContradiction(
|
|
1773
|
+
ctx,
|
|
1774
|
+
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
1775
|
+
exMin.provenance,
|
|
1776
|
+
exMax.provenance
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function checkLengthContradictions(ctx, fieldName, constraints) {
|
|
1781
|
+
const minLen = findLength(constraints, "minLength");
|
|
1782
|
+
const maxLen = findLength(constraints, "maxLength");
|
|
1783
|
+
if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
|
|
1784
|
+
addContradiction(
|
|
1785
|
+
ctx,
|
|
1786
|
+
`Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
|
|
1787
|
+
minLen.provenance,
|
|
1788
|
+
maxLen.provenance
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
const minItems = findLength(constraints, "minItems");
|
|
1792
|
+
const maxItems = findLength(constraints, "maxItems");
|
|
1793
|
+
if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
|
|
1794
|
+
addContradiction(
|
|
1795
|
+
ctx,
|
|
1796
|
+
`Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
|
|
1797
|
+
minItems.provenance,
|
|
1798
|
+
maxItems.provenance
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
function checkAllowedMembersContradiction(ctx, fieldName, constraints) {
|
|
1803
|
+
const members = findAllowedMembers(constraints);
|
|
1804
|
+
if (members.length < 2) return;
|
|
1805
|
+
const firstSet = new Set(members[0]?.members ?? []);
|
|
1806
|
+
for (let i = 1; i < members.length; i++) {
|
|
1807
|
+
const current = members[i];
|
|
1808
|
+
if (current === void 0) continue;
|
|
1809
|
+
for (const m of firstSet) {
|
|
1810
|
+
if (!current.members.includes(m)) {
|
|
1811
|
+
firstSet.delete(m);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
if (firstSet.size === 0) {
|
|
1816
|
+
const first = members[0];
|
|
1817
|
+
const second = members[1];
|
|
1818
|
+
if (first !== void 0 && second !== void 0) {
|
|
1819
|
+
addContradiction(
|
|
1820
|
+
ctx,
|
|
1821
|
+
`Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
|
|
1822
|
+
first.provenance,
|
|
1823
|
+
second.provenance
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
function typeLabel(type) {
|
|
1829
|
+
switch (type.kind) {
|
|
1830
|
+
case "primitive":
|
|
1831
|
+
return type.primitiveKind;
|
|
1832
|
+
case "enum":
|
|
1833
|
+
return "enum";
|
|
1834
|
+
case "array":
|
|
1835
|
+
return "array";
|
|
1836
|
+
case "object":
|
|
1837
|
+
return "object";
|
|
1838
|
+
case "union":
|
|
1839
|
+
return "union";
|
|
1840
|
+
case "reference":
|
|
1841
|
+
return `reference(${type.name})`;
|
|
1842
|
+
case "dynamic":
|
|
1843
|
+
return `dynamic(${type.dynamicKind})`;
|
|
1844
|
+
case "custom":
|
|
1845
|
+
return `custom(${type.typeId})`;
|
|
1846
|
+
default: {
|
|
1847
|
+
const _exhaustive = type;
|
|
1848
|
+
return String(_exhaustive);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
function checkTypeApplicability(ctx, fieldName, type, constraints) {
|
|
1853
|
+
const isNumber = type.kind === "primitive" && type.primitiveKind === "number";
|
|
1854
|
+
const isString = type.kind === "primitive" && type.primitiveKind === "string";
|
|
1855
|
+
const isArray = type.kind === "array";
|
|
1856
|
+
const isEnum = type.kind === "enum";
|
|
1857
|
+
const label = typeLabel(type);
|
|
1858
|
+
for (const constraint of constraints) {
|
|
1859
|
+
const ck = constraint.constraintKind;
|
|
1860
|
+
switch (ck) {
|
|
1861
|
+
case "minimum":
|
|
1862
|
+
case "maximum":
|
|
1863
|
+
case "exclusiveMinimum":
|
|
1864
|
+
case "exclusiveMaximum":
|
|
1865
|
+
case "multipleOf": {
|
|
1866
|
+
if (!isNumber) {
|
|
1867
|
+
addTypeMismatch(
|
|
1868
|
+
ctx,
|
|
1869
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
|
|
1870
|
+
constraint.provenance
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
case "minLength":
|
|
1876
|
+
case "maxLength":
|
|
1877
|
+
case "pattern": {
|
|
1878
|
+
if (!isString) {
|
|
1879
|
+
addTypeMismatch(
|
|
1880
|
+
ctx,
|
|
1881
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on string fields, but field type is "${label}"`,
|
|
1882
|
+
constraint.provenance
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
break;
|
|
1886
|
+
}
|
|
1887
|
+
case "minItems":
|
|
1888
|
+
case "maxItems":
|
|
1889
|
+
case "uniqueItems": {
|
|
1890
|
+
if (!isArray) {
|
|
1891
|
+
addTypeMismatch(
|
|
1892
|
+
ctx,
|
|
1893
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on array fields, but field type is "${label}"`,
|
|
1894
|
+
constraint.provenance
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
case "allowedMembers": {
|
|
1900
|
+
if (!isEnum) {
|
|
1901
|
+
addTypeMismatch(
|
|
1902
|
+
ctx,
|
|
1903
|
+
`Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
|
|
1904
|
+
constraint.provenance
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
break;
|
|
1908
|
+
}
|
|
1909
|
+
case "custom": {
|
|
1910
|
+
checkCustomConstraint(ctx, fieldName, type, constraint);
|
|
1911
|
+
break;
|
|
1912
|
+
}
|
|
1913
|
+
default: {
|
|
1914
|
+
const _exhaustive = constraint;
|
|
1915
|
+
throw new Error(
|
|
1916
|
+
`Unhandled constraint kind: ${_exhaustive.constraintKind}`
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1244
1919
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
1923
|
+
if (ctx.extensionRegistry === void 0) return;
|
|
1924
|
+
const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
|
|
1925
|
+
if (registration === void 0) {
|
|
1926
|
+
addUnknownExtension(
|
|
1927
|
+
ctx,
|
|
1928
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
|
|
1929
|
+
constraint.provenance
|
|
1930
|
+
);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
if (registration.applicableTypes === null) return;
|
|
1934
|
+
if (!registration.applicableTypes.includes(type.kind)) {
|
|
1935
|
+
addTypeMismatch(
|
|
1936
|
+
ctx,
|
|
1937
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
1938
|
+
constraint.provenance
|
|
1251
1939
|
);
|
|
1252
|
-
uiElements.push(formSpecField);
|
|
1253
1940
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1941
|
+
}
|
|
1942
|
+
function validateFieldNode(ctx, field) {
|
|
1943
|
+
validateConstraints(ctx, field.name, field.type, field.constraints);
|
|
1944
|
+
if (field.type.kind === "object") {
|
|
1945
|
+
for (const prop of field.type.properties) {
|
|
1946
|
+
validateObjectProperty(ctx, field.name, prop);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
function validateObjectProperty(ctx, parentName, prop) {
|
|
1951
|
+
const qualifiedName = `${parentName}.${prop.name}`;
|
|
1952
|
+
validateConstraints(ctx, qualifiedName, prop.type, prop.constraints);
|
|
1953
|
+
if (prop.type.kind === "object") {
|
|
1954
|
+
for (const nestedProp of prop.type.properties) {
|
|
1955
|
+
validateObjectProperty(ctx, qualifiedName, nestedProp);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
function validateConstraints(ctx, name, type, constraints) {
|
|
1960
|
+
checkNumericContradictions(ctx, name, constraints);
|
|
1961
|
+
checkLengthContradictions(ctx, name, constraints);
|
|
1962
|
+
checkAllowedMembersContradiction(ctx, name, constraints);
|
|
1963
|
+
checkTypeApplicability(ctx, name, type, constraints);
|
|
1964
|
+
}
|
|
1965
|
+
function validateElement(ctx, element) {
|
|
1966
|
+
switch (element.kind) {
|
|
1967
|
+
case "field":
|
|
1968
|
+
validateFieldNode(ctx, element);
|
|
1969
|
+
break;
|
|
1970
|
+
case "group":
|
|
1971
|
+
for (const child of element.elements) {
|
|
1972
|
+
validateElement(ctx, child);
|
|
1973
|
+
}
|
|
1974
|
+
break;
|
|
1975
|
+
case "conditional":
|
|
1976
|
+
for (const child of element.elements) {
|
|
1977
|
+
validateElement(ctx, child);
|
|
1978
|
+
}
|
|
1979
|
+
break;
|
|
1980
|
+
default: {
|
|
1981
|
+
const _exhaustive = element;
|
|
1982
|
+
throw new Error(`Unhandled element kind: ${_exhaustive.kind}`);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
function validateIR(ir, options) {
|
|
1987
|
+
const ctx = {
|
|
1988
|
+
diagnostics: [],
|
|
1989
|
+
vendorPrefix: options?.vendorPrefix ?? "FORMSPEC",
|
|
1990
|
+
extensionRegistry: options?.extensionRegistry
|
|
1991
|
+
};
|
|
1992
|
+
for (const element of ir.elements) {
|
|
1993
|
+
validateElement(ctx, element);
|
|
1994
|
+
}
|
|
1995
|
+
return {
|
|
1996
|
+
diagnostics: ctx.diagnostics,
|
|
1997
|
+
valid: ctx.diagnostics.every((d) => d.severity !== "error")
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// src/extensions/registry.ts
|
|
2002
|
+
function createExtensionRegistry(extensions) {
|
|
2003
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
2004
|
+
const constraintMap = /* @__PURE__ */ new Map();
|
|
2005
|
+
const annotationMap = /* @__PURE__ */ new Map();
|
|
2006
|
+
for (const ext of extensions) {
|
|
2007
|
+
if (ext.types !== void 0) {
|
|
2008
|
+
for (const type of ext.types) {
|
|
2009
|
+
const qualifiedId = `${ext.extensionId}/${type.typeName}`;
|
|
2010
|
+
if (typeMap.has(qualifiedId)) {
|
|
2011
|
+
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
2012
|
+
}
|
|
2013
|
+
typeMap.set(qualifiedId, type);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (ext.constraints !== void 0) {
|
|
2017
|
+
for (const constraint of ext.constraints) {
|
|
2018
|
+
const qualifiedId = `${ext.extensionId}/${constraint.constraintName}`;
|
|
2019
|
+
if (constraintMap.has(qualifiedId)) {
|
|
2020
|
+
throw new Error(`Duplicate custom constraint ID: "${qualifiedId}"`);
|
|
2021
|
+
}
|
|
2022
|
+
constraintMap.set(qualifiedId, constraint);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
if (ext.annotations !== void 0) {
|
|
2026
|
+
for (const annotation of ext.annotations) {
|
|
2027
|
+
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
2028
|
+
if (annotationMap.has(qualifiedId)) {
|
|
2029
|
+
throw new Error(`Duplicate custom annotation ID: "${qualifiedId}"`);
|
|
2030
|
+
}
|
|
2031
|
+
annotationMap.set(qualifiedId, annotation);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
return {
|
|
2036
|
+
extensions,
|
|
2037
|
+
findType: (typeId) => typeMap.get(typeId),
|
|
2038
|
+
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
2039
|
+
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
1258
2040
|
};
|
|
1259
|
-
const uiSchema2 = generateUiSchemaFromFields(uiElements);
|
|
1260
|
-
return { jsonSchema, uiSchema: uiSchema2 };
|
|
1261
2041
|
}
|
|
1262
2042
|
|
|
1263
2043
|
// src/generators/method-schema.ts
|
|
2044
|
+
var import_core5 = require("@formspec/core");
|
|
2045
|
+
function typeToJsonSchema(type, checker) {
|
|
2046
|
+
const typeRegistry = {};
|
|
2047
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
2048
|
+
const typeNode = resolveTypeNode(type, checker, "", typeRegistry, visiting);
|
|
2049
|
+
const fieldProvenance = { surface: "tsdoc", file: "", line: 0, column: 0 };
|
|
2050
|
+
const ir = {
|
|
2051
|
+
kind: "form-ir",
|
|
2052
|
+
irVersion: import_core5.IR_VERSION,
|
|
2053
|
+
elements: [
|
|
2054
|
+
{
|
|
2055
|
+
kind: "field",
|
|
2056
|
+
name: "__result",
|
|
2057
|
+
type: typeNode,
|
|
2058
|
+
required: true,
|
|
2059
|
+
constraints: [],
|
|
2060
|
+
annotations: [],
|
|
2061
|
+
provenance: fieldProvenance
|
|
2062
|
+
}
|
|
2063
|
+
],
|
|
2064
|
+
typeRegistry,
|
|
2065
|
+
provenance: fieldProvenance
|
|
2066
|
+
};
|
|
2067
|
+
const schema = generateJsonSchemaFromIR(ir);
|
|
2068
|
+
const fieldSchema = schema.properties?.["__result"];
|
|
2069
|
+
if (fieldSchema) {
|
|
2070
|
+
if (schema.$defs && Object.keys(schema.$defs).length > 0) {
|
|
2071
|
+
return { ...fieldSchema, $defs: schema.$defs };
|
|
2072
|
+
}
|
|
2073
|
+
return fieldSchema;
|
|
2074
|
+
}
|
|
2075
|
+
return { type: "object" };
|
|
2076
|
+
}
|
|
1264
2077
|
function generateMethodSchemas(method, checker, loadedFormSpecs) {
|
|
1265
|
-
const returnType =
|
|
2078
|
+
const returnType = typeToJsonSchema(method.returnType, checker);
|
|
1266
2079
|
const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
|
|
1267
2080
|
return {
|
|
1268
2081
|
name: method.name,
|
|
@@ -1291,7 +2104,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
|
1291
2104
|
}
|
|
1292
2105
|
if (parameters.length === 1 && parameters[0]) {
|
|
1293
2106
|
const param = parameters[0];
|
|
1294
|
-
const jsonSchema =
|
|
2107
|
+
const jsonSchema = typeToJsonSchema(param.type, checker);
|
|
1295
2108
|
return {
|
|
1296
2109
|
jsonSchema,
|
|
1297
2110
|
uiSchema: null,
|
|
@@ -1301,7 +2114,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
|
1301
2114
|
const properties = {};
|
|
1302
2115
|
const required = [];
|
|
1303
2116
|
for (const param of parameters) {
|
|
1304
|
-
const paramSchema =
|
|
2117
|
+
const paramSchema = typeToJsonSchema(param.type, checker);
|
|
1305
2118
|
properties[param.name] = paramSchema;
|
|
1306
2119
|
if (!param.optional) {
|
|
1307
2120
|
required.push(param.name);
|
|
@@ -1330,16 +2143,21 @@ function collectFormSpecReferences(methods) {
|
|
|
1330
2143
|
}
|
|
1331
2144
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1332
2145
|
0 && (module.exports = {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
2146
|
+
analyzeClassToIR,
|
|
2147
|
+
analyzeInterfaceToIR,
|
|
2148
|
+
analyzeTypeAliasToIR,
|
|
2149
|
+
canonicalizeChainDSL,
|
|
2150
|
+
canonicalizeTSDoc,
|
|
1336
2151
|
collectFormSpecReferences,
|
|
2152
|
+
createExtensionRegistry,
|
|
1337
2153
|
createProgramContext,
|
|
1338
2154
|
findClassByName,
|
|
1339
2155
|
findInterfaceByName,
|
|
1340
2156
|
findTypeAliasByName,
|
|
1341
2157
|
generateClassSchemas,
|
|
2158
|
+
generateJsonSchemaFromIR,
|
|
1342
2159
|
generateMethodSchemas,
|
|
1343
|
-
|
|
2160
|
+
generateUiSchemaFromIR,
|
|
2161
|
+
validateIR
|
|
1344
2162
|
});
|
|
1345
2163
|
//# sourceMappingURL=internals.cjs.map
|