@formspec/build 0.1.0-alpha.40 → 0.1.0-alpha.41
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/dist/browser.cjs +226 -17
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +226 -17
- package/dist/browser.js.map +1 -1
- package/dist/build-alpha.d.ts +10 -0
- package/dist/build-beta.d.ts +10 -0
- package/dist/build-internal.d.ts +10 -0
- package/dist/build.d.ts +10 -0
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +163 -27
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +163 -27
- package/dist/cli.js.map +1 -1
- package/dist/generators/class-schema.d.ts +5 -0
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/discovered-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts +9 -2
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/index.cjs +143 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +143 -24
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +120 -27
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +120 -27
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +5 -0
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +5 -0
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/metadata/policy.d.ts +3 -2
- package/dist/metadata/policy.d.ts.map +1 -1
- package/dist/metadata/resolve.d.ts +1 -0
- package/dist/metadata/resolve.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/browser.js
CHANGED
|
@@ -51,11 +51,35 @@ function normalizeDeclarationPolicy(input) {
|
|
|
51
51
|
displayName: normalizeScalarPolicy(input?.displayName)
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
function normalizeEnumMemberDisplayNamePolicy(input) {
|
|
55
|
+
if (input?.mode === "infer-if-missing") {
|
|
56
|
+
return {
|
|
57
|
+
mode: "infer-if-missing",
|
|
58
|
+
infer: input.infer
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (input?.mode === "require-explicit") {
|
|
62
|
+
return {
|
|
63
|
+
mode: "require-explicit",
|
|
64
|
+
infer: () => ""
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mode: "disabled",
|
|
69
|
+
infer: () => ""
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function normalizeEnumMemberPolicy(input) {
|
|
73
|
+
return {
|
|
74
|
+
displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
54
77
|
function normalizeMetadataPolicy(input) {
|
|
55
78
|
return {
|
|
56
79
|
type: normalizeDeclarationPolicy(input?.type),
|
|
57
80
|
field: normalizeDeclarationPolicy(input?.field),
|
|
58
|
-
method: normalizeDeclarationPolicy(input?.method)
|
|
81
|
+
method: normalizeDeclarationPolicy(input?.method),
|
|
82
|
+
enumMember: normalizeEnumMemberPolicy(input?.enumMember)
|
|
59
83
|
};
|
|
60
84
|
}
|
|
61
85
|
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
@@ -152,6 +176,134 @@ function resolveResolvedMetadata(current, policy, context) {
|
|
|
152
176
|
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
153
177
|
};
|
|
154
178
|
}
|
|
179
|
+
function resolveEnumMemberDisplayName(current, policy, context) {
|
|
180
|
+
if (current !== void 0) {
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
if (policy.mode === "require-explicit") {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (policy.mode !== "infer-if-missing") {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
const inferredValue = policy.infer(context).trim();
|
|
192
|
+
return inferredValue !== "" ? inferredValue : void 0;
|
|
193
|
+
}
|
|
194
|
+
function resolveEnumTypeMetadata(type, options) {
|
|
195
|
+
const members = type.members.map((member) => {
|
|
196
|
+
const displayName = resolveEnumMemberDisplayName(
|
|
197
|
+
member.displayName,
|
|
198
|
+
options.policy.enumMember.displayName,
|
|
199
|
+
{
|
|
200
|
+
surface: options.surface,
|
|
201
|
+
logicalName: String(member.value),
|
|
202
|
+
memberValue: member.value,
|
|
203
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
if (displayName === member.displayName) {
|
|
207
|
+
return member;
|
|
208
|
+
}
|
|
209
|
+
return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
|
|
210
|
+
});
|
|
211
|
+
return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
|
|
212
|
+
}
|
|
213
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
214
|
+
switch (type.kind) {
|
|
215
|
+
case "array":
|
|
216
|
+
return {
|
|
217
|
+
...type,
|
|
218
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
219
|
+
};
|
|
220
|
+
case "object":
|
|
221
|
+
return {
|
|
222
|
+
...type,
|
|
223
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
224
|
+
};
|
|
225
|
+
case "record":
|
|
226
|
+
return {
|
|
227
|
+
...type,
|
|
228
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
229
|
+
};
|
|
230
|
+
case "union":
|
|
231
|
+
return {
|
|
232
|
+
...type,
|
|
233
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
234
|
+
};
|
|
235
|
+
case "enum":
|
|
236
|
+
return resolveEnumTypeMetadata(type, options);
|
|
237
|
+
case "reference":
|
|
238
|
+
case "primitive":
|
|
239
|
+
case "dynamic":
|
|
240
|
+
case "custom":
|
|
241
|
+
return type;
|
|
242
|
+
default: {
|
|
243
|
+
const _exhaustive = type;
|
|
244
|
+
return _exhaustive;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
249
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
250
|
+
surface: options.surface,
|
|
251
|
+
declarationKind: "field",
|
|
252
|
+
logicalName: property.name,
|
|
253
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
...property,
|
|
257
|
+
...metadata !== void 0 && { metadata },
|
|
258
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function resolveFieldMetadataNode(field, options) {
|
|
262
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
263
|
+
surface: options.surface,
|
|
264
|
+
declarationKind: "field",
|
|
265
|
+
logicalName: field.name,
|
|
266
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
...field,
|
|
270
|
+
...metadata !== void 0 && { metadata },
|
|
271
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function resolveFormElementMetadata(element, options) {
|
|
275
|
+
switch (element.kind) {
|
|
276
|
+
case "field":
|
|
277
|
+
return resolveFieldMetadataNode(element, options);
|
|
278
|
+
case "group":
|
|
279
|
+
return {
|
|
280
|
+
...element,
|
|
281
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
282
|
+
};
|
|
283
|
+
case "conditional":
|
|
284
|
+
return {
|
|
285
|
+
...element,
|
|
286
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
287
|
+
};
|
|
288
|
+
default: {
|
|
289
|
+
const _exhaustive = element;
|
|
290
|
+
return _exhaustive;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
295
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
296
|
+
surface: options.surface,
|
|
297
|
+
declarationKind: "type",
|
|
298
|
+
logicalName: typeDefinition.name,
|
|
299
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
...typeDefinition,
|
|
303
|
+
...metadata !== void 0 && { metadata },
|
|
304
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
305
|
+
};
|
|
306
|
+
}
|
|
155
307
|
function resolveMetadata(explicit, policy, context) {
|
|
156
308
|
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
157
309
|
}
|
|
@@ -161,6 +313,25 @@ function getSerializedName(logicalName, metadata) {
|
|
|
161
313
|
function getDisplayName(metadata) {
|
|
162
314
|
return metadata?.displayName?.value;
|
|
163
315
|
}
|
|
316
|
+
function resolveFormIRMetadata(ir, options) {
|
|
317
|
+
const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
318
|
+
surface: options.surface,
|
|
319
|
+
declarationKind: "type",
|
|
320
|
+
logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
|
|
321
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
322
|
+
});
|
|
323
|
+
return {
|
|
324
|
+
...ir,
|
|
325
|
+
...metadata !== void 0 && { metadata },
|
|
326
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
327
|
+
typeRegistry: Object.fromEntries(
|
|
328
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
329
|
+
name,
|
|
330
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
331
|
+
])
|
|
332
|
+
)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
164
335
|
|
|
165
336
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
166
337
|
var CHAIN_DSL_PROVENANCE = {
|
|
@@ -182,7 +353,7 @@ function canonicalizeChainDSL(form, options) {
|
|
|
182
353
|
const metadataPolicy = normalizeMetadataPolicy(
|
|
183
354
|
options?.metadata ?? _getFormSpecMetadataPolicy(form)
|
|
184
355
|
);
|
|
185
|
-
|
|
356
|
+
const ir = {
|
|
186
357
|
kind: "form-ir",
|
|
187
358
|
irVersion: IR_VERSION,
|
|
188
359
|
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
@@ -190,6 +361,13 @@ function canonicalizeChainDSL(form, options) {
|
|
|
190
361
|
typeRegistry: {},
|
|
191
362
|
provenance: CHAIN_DSL_PROVENANCE
|
|
192
363
|
};
|
|
364
|
+
return resolveFormIRMetadata(ir, {
|
|
365
|
+
policy: metadataPolicy,
|
|
366
|
+
surface: "chain-dsl",
|
|
367
|
+
// Chain DSL has no root/type-metadata authoring surface, so only resolve
|
|
368
|
+
// field/type-registry metadata and enum-member labels here.
|
|
369
|
+
resolveRootTypeMetadata: false
|
|
370
|
+
});
|
|
193
371
|
}
|
|
194
372
|
function canonicalizeElements(elements, metadataPolicy) {
|
|
195
373
|
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
@@ -648,17 +826,25 @@ function assertNoSerializedNameCollisions(ir) {
|
|
|
648
826
|
// src/json-schema/ir-generator.ts
|
|
649
827
|
function makeContext(options) {
|
|
650
828
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
829
|
+
const rawEnumSerialization = options?.enumSerialization;
|
|
651
830
|
if (!vendorPrefix.startsWith("x-")) {
|
|
652
831
|
throw new Error(
|
|
653
832
|
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
654
833
|
);
|
|
655
834
|
}
|
|
835
|
+
if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
|
|
836
|
+
throw new Error(
|
|
837
|
+
`Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
const enumSerialization = rawEnumSerialization ?? "enum";
|
|
656
841
|
return {
|
|
657
842
|
defs: {},
|
|
658
843
|
typeNameMap: {},
|
|
659
844
|
typeRegistry: {},
|
|
660
845
|
extensionRegistry: options?.extensionRegistry,
|
|
661
|
-
vendorPrefix
|
|
846
|
+
vendorPrefix,
|
|
847
|
+
enumSerialization
|
|
662
848
|
};
|
|
663
849
|
}
|
|
664
850
|
function generateJsonSchemaFromIR(ir, options) {
|
|
@@ -832,7 +1018,7 @@ function generateTypeNode(type, ctx) {
|
|
|
832
1018
|
case "primitive":
|
|
833
1019
|
return generatePrimitiveType(type);
|
|
834
1020
|
case "enum":
|
|
835
|
-
return generateEnumType(type);
|
|
1021
|
+
return generateEnumType(type, ctx);
|
|
836
1022
|
case "array":
|
|
837
1023
|
return generateArrayType(type, ctx);
|
|
838
1024
|
case "object":
|
|
@@ -858,20 +1044,37 @@ function generatePrimitiveType(type) {
|
|
|
858
1044
|
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
859
1045
|
};
|
|
860
1046
|
}
|
|
861
|
-
function generateEnumType(type) {
|
|
862
|
-
|
|
863
|
-
if (hasDisplayNames) {
|
|
1047
|
+
function generateEnumType(type, ctx) {
|
|
1048
|
+
if (ctx.enumSerialization === "oneOf") {
|
|
864
1049
|
return {
|
|
865
|
-
oneOf: type.members.map((m) => {
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
}
|
|
870
|
-
return entry;
|
|
871
|
-
})
|
|
1050
|
+
oneOf: type.members.map((m) => ({
|
|
1051
|
+
const: m.value,
|
|
1052
|
+
title: m.displayName ?? String(m.value)
|
|
1053
|
+
}))
|
|
872
1054
|
};
|
|
873
1055
|
}
|
|
874
|
-
|
|
1056
|
+
const schema = { enum: type.members.map((m) => m.value) };
|
|
1057
|
+
const displayNames = buildEnumDisplayNameExtension(type);
|
|
1058
|
+
if (displayNames !== void 0) {
|
|
1059
|
+
schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
|
|
1060
|
+
}
|
|
1061
|
+
return schema;
|
|
1062
|
+
}
|
|
1063
|
+
function buildEnumDisplayNameExtension(type) {
|
|
1064
|
+
if (!type.members.some((member) => member.displayName !== void 0)) {
|
|
1065
|
+
return void 0;
|
|
1066
|
+
}
|
|
1067
|
+
const displayNames = /* @__PURE__ */ Object.create(null);
|
|
1068
|
+
for (const member of type.members) {
|
|
1069
|
+
const key = String(member.value);
|
|
1070
|
+
if (Object.hasOwn(displayNames, key)) {
|
|
1071
|
+
throw new Error(
|
|
1072
|
+
`Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
displayNames[key] = member.displayName ?? key;
|
|
1076
|
+
}
|
|
1077
|
+
return displayNames;
|
|
875
1078
|
}
|
|
876
1079
|
function generateArrayType(type, ctx) {
|
|
877
1080
|
return {
|
|
@@ -1264,11 +1467,17 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
|
|
|
1264
1467
|
|
|
1265
1468
|
// src/json-schema/generator.ts
|
|
1266
1469
|
function generateJsonSchema(form, options) {
|
|
1470
|
+
const metadata = options?.metadata;
|
|
1471
|
+
const vendorPrefix = options?.vendorPrefix;
|
|
1472
|
+
const enumSerialization = options?.enumSerialization;
|
|
1267
1473
|
const ir = canonicalizeChainDSL(
|
|
1268
1474
|
form,
|
|
1269
|
-
|
|
1475
|
+
metadata !== void 0 ? { metadata } : void 0
|
|
1270
1476
|
);
|
|
1271
|
-
const internalOptions =
|
|
1477
|
+
const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
|
|
1478
|
+
...vendorPrefix !== void 0 && { vendorPrefix },
|
|
1479
|
+
...enumSerialization !== void 0 && { enumSerialization }
|
|
1480
|
+
};
|
|
1272
1481
|
return generateJsonSchemaFromIR(ir, internalOptions);
|
|
1273
1482
|
}
|
|
1274
1483
|
|