@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.cjs
CHANGED
|
@@ -101,11 +101,35 @@ function normalizeDeclarationPolicy(input) {
|
|
|
101
101
|
displayName: normalizeScalarPolicy(input?.displayName)
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
function normalizeEnumMemberDisplayNamePolicy(input) {
|
|
105
|
+
if (input?.mode === "infer-if-missing") {
|
|
106
|
+
return {
|
|
107
|
+
mode: "infer-if-missing",
|
|
108
|
+
infer: input.infer
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (input?.mode === "require-explicit") {
|
|
112
|
+
return {
|
|
113
|
+
mode: "require-explicit",
|
|
114
|
+
infer: () => ""
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
mode: "disabled",
|
|
119
|
+
infer: () => ""
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function normalizeEnumMemberPolicy(input) {
|
|
123
|
+
return {
|
|
124
|
+
displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
104
127
|
function normalizeMetadataPolicy(input) {
|
|
105
128
|
return {
|
|
106
129
|
type: normalizeDeclarationPolicy(input?.type),
|
|
107
130
|
field: normalizeDeclarationPolicy(input?.field),
|
|
108
|
-
method: normalizeDeclarationPolicy(input?.method)
|
|
131
|
+
method: normalizeDeclarationPolicy(input?.method),
|
|
132
|
+
enumMember: normalizeEnumMemberPolicy(input?.enumMember)
|
|
109
133
|
};
|
|
110
134
|
}
|
|
111
135
|
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
@@ -202,6 +226,134 @@ function resolveResolvedMetadata(current, policy, context) {
|
|
|
202
226
|
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
203
227
|
};
|
|
204
228
|
}
|
|
229
|
+
function resolveEnumMemberDisplayName(current, policy, context) {
|
|
230
|
+
if (current !== void 0) {
|
|
231
|
+
return current;
|
|
232
|
+
}
|
|
233
|
+
if (policy.mode === "require-explicit") {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (policy.mode !== "infer-if-missing") {
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
241
|
+
const inferredValue = policy.infer(context).trim();
|
|
242
|
+
return inferredValue !== "" ? inferredValue : void 0;
|
|
243
|
+
}
|
|
244
|
+
function resolveEnumTypeMetadata(type, options) {
|
|
245
|
+
const members = type.members.map((member) => {
|
|
246
|
+
const displayName = resolveEnumMemberDisplayName(
|
|
247
|
+
member.displayName,
|
|
248
|
+
options.policy.enumMember.displayName,
|
|
249
|
+
{
|
|
250
|
+
surface: options.surface,
|
|
251
|
+
logicalName: String(member.value),
|
|
252
|
+
memberValue: member.value,
|
|
253
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
if (displayName === member.displayName) {
|
|
257
|
+
return member;
|
|
258
|
+
}
|
|
259
|
+
return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
|
|
260
|
+
});
|
|
261
|
+
return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
|
|
262
|
+
}
|
|
263
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
264
|
+
switch (type.kind) {
|
|
265
|
+
case "array":
|
|
266
|
+
return {
|
|
267
|
+
...type,
|
|
268
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
269
|
+
};
|
|
270
|
+
case "object":
|
|
271
|
+
return {
|
|
272
|
+
...type,
|
|
273
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
274
|
+
};
|
|
275
|
+
case "record":
|
|
276
|
+
return {
|
|
277
|
+
...type,
|
|
278
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
279
|
+
};
|
|
280
|
+
case "union":
|
|
281
|
+
return {
|
|
282
|
+
...type,
|
|
283
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
284
|
+
};
|
|
285
|
+
case "enum":
|
|
286
|
+
return resolveEnumTypeMetadata(type, options);
|
|
287
|
+
case "reference":
|
|
288
|
+
case "primitive":
|
|
289
|
+
case "dynamic":
|
|
290
|
+
case "custom":
|
|
291
|
+
return type;
|
|
292
|
+
default: {
|
|
293
|
+
const _exhaustive = type;
|
|
294
|
+
return _exhaustive;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
299
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
300
|
+
surface: options.surface,
|
|
301
|
+
declarationKind: "field",
|
|
302
|
+
logicalName: property.name,
|
|
303
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
...property,
|
|
307
|
+
...metadata !== void 0 && { metadata },
|
|
308
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function resolveFieldMetadataNode(field, options) {
|
|
312
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
313
|
+
surface: options.surface,
|
|
314
|
+
declarationKind: "field",
|
|
315
|
+
logicalName: field.name,
|
|
316
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
...field,
|
|
320
|
+
...metadata !== void 0 && { metadata },
|
|
321
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function resolveFormElementMetadata(element, options) {
|
|
325
|
+
switch (element.kind) {
|
|
326
|
+
case "field":
|
|
327
|
+
return resolveFieldMetadataNode(element, options);
|
|
328
|
+
case "group":
|
|
329
|
+
return {
|
|
330
|
+
...element,
|
|
331
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
332
|
+
};
|
|
333
|
+
case "conditional":
|
|
334
|
+
return {
|
|
335
|
+
...element,
|
|
336
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
337
|
+
};
|
|
338
|
+
default: {
|
|
339
|
+
const _exhaustive = element;
|
|
340
|
+
return _exhaustive;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
345
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
346
|
+
surface: options.surface,
|
|
347
|
+
declarationKind: "type",
|
|
348
|
+
logicalName: typeDefinition.name,
|
|
349
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
350
|
+
});
|
|
351
|
+
return {
|
|
352
|
+
...typeDefinition,
|
|
353
|
+
...metadata !== void 0 && { metadata },
|
|
354
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
355
|
+
};
|
|
356
|
+
}
|
|
205
357
|
function resolveMetadata(explicit, policy, context) {
|
|
206
358
|
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
207
359
|
}
|
|
@@ -211,6 +363,25 @@ function getSerializedName(logicalName, metadata) {
|
|
|
211
363
|
function getDisplayName(metadata) {
|
|
212
364
|
return metadata?.displayName?.value;
|
|
213
365
|
}
|
|
366
|
+
function resolveFormIRMetadata(ir, options) {
|
|
367
|
+
const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
368
|
+
surface: options.surface,
|
|
369
|
+
declarationKind: "type",
|
|
370
|
+
logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
|
|
371
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
...ir,
|
|
375
|
+
...metadata !== void 0 && { metadata },
|
|
376
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
377
|
+
typeRegistry: Object.fromEntries(
|
|
378
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
379
|
+
name,
|
|
380
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
381
|
+
])
|
|
382
|
+
)
|
|
383
|
+
};
|
|
384
|
+
}
|
|
214
385
|
|
|
215
386
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
216
387
|
var CHAIN_DSL_PROVENANCE = {
|
|
@@ -232,7 +403,7 @@ function canonicalizeChainDSL(form, options) {
|
|
|
232
403
|
const metadataPolicy = normalizeMetadataPolicy(
|
|
233
404
|
options?.metadata ?? (0, import_internals._getFormSpecMetadataPolicy)(form)
|
|
234
405
|
);
|
|
235
|
-
|
|
406
|
+
const ir = {
|
|
236
407
|
kind: "form-ir",
|
|
237
408
|
irVersion: import_internals.IR_VERSION,
|
|
238
409
|
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
@@ -240,6 +411,13 @@ function canonicalizeChainDSL(form, options) {
|
|
|
240
411
|
typeRegistry: {},
|
|
241
412
|
provenance: CHAIN_DSL_PROVENANCE
|
|
242
413
|
};
|
|
414
|
+
return resolveFormIRMetadata(ir, {
|
|
415
|
+
policy: metadataPolicy,
|
|
416
|
+
surface: "chain-dsl",
|
|
417
|
+
// Chain DSL has no root/type-metadata authoring surface, so only resolve
|
|
418
|
+
// field/type-registry metadata and enum-member labels here.
|
|
419
|
+
resolveRootTypeMetadata: false
|
|
420
|
+
});
|
|
243
421
|
}
|
|
244
422
|
function canonicalizeElements(elements, metadataPolicy) {
|
|
245
423
|
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
@@ -698,17 +876,25 @@ function assertNoSerializedNameCollisions(ir) {
|
|
|
698
876
|
// src/json-schema/ir-generator.ts
|
|
699
877
|
function makeContext(options) {
|
|
700
878
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
879
|
+
const rawEnumSerialization = options?.enumSerialization;
|
|
701
880
|
if (!vendorPrefix.startsWith("x-")) {
|
|
702
881
|
throw new Error(
|
|
703
882
|
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
704
883
|
);
|
|
705
884
|
}
|
|
885
|
+
if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
|
|
886
|
+
throw new Error(
|
|
887
|
+
`Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
const enumSerialization = rawEnumSerialization ?? "enum";
|
|
706
891
|
return {
|
|
707
892
|
defs: {},
|
|
708
893
|
typeNameMap: {},
|
|
709
894
|
typeRegistry: {},
|
|
710
895
|
extensionRegistry: options?.extensionRegistry,
|
|
711
|
-
vendorPrefix
|
|
896
|
+
vendorPrefix,
|
|
897
|
+
enumSerialization
|
|
712
898
|
};
|
|
713
899
|
}
|
|
714
900
|
function generateJsonSchemaFromIR(ir, options) {
|
|
@@ -882,7 +1068,7 @@ function generateTypeNode(type, ctx) {
|
|
|
882
1068
|
case "primitive":
|
|
883
1069
|
return generatePrimitiveType(type);
|
|
884
1070
|
case "enum":
|
|
885
|
-
return generateEnumType(type);
|
|
1071
|
+
return generateEnumType(type, ctx);
|
|
886
1072
|
case "array":
|
|
887
1073
|
return generateArrayType(type, ctx);
|
|
888
1074
|
case "object":
|
|
@@ -908,20 +1094,37 @@ function generatePrimitiveType(type) {
|
|
|
908
1094
|
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
909
1095
|
};
|
|
910
1096
|
}
|
|
911
|
-
function generateEnumType(type) {
|
|
912
|
-
|
|
913
|
-
if (hasDisplayNames) {
|
|
1097
|
+
function generateEnumType(type, ctx) {
|
|
1098
|
+
if (ctx.enumSerialization === "oneOf") {
|
|
914
1099
|
return {
|
|
915
|
-
oneOf: type.members.map((m) => {
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
}
|
|
920
|
-
return entry;
|
|
921
|
-
})
|
|
1100
|
+
oneOf: type.members.map((m) => ({
|
|
1101
|
+
const: m.value,
|
|
1102
|
+
title: m.displayName ?? String(m.value)
|
|
1103
|
+
}))
|
|
922
1104
|
};
|
|
923
1105
|
}
|
|
924
|
-
|
|
1106
|
+
const schema = { enum: type.members.map((m) => m.value) };
|
|
1107
|
+
const displayNames = buildEnumDisplayNameExtension(type);
|
|
1108
|
+
if (displayNames !== void 0) {
|
|
1109
|
+
schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
|
|
1110
|
+
}
|
|
1111
|
+
return schema;
|
|
1112
|
+
}
|
|
1113
|
+
function buildEnumDisplayNameExtension(type) {
|
|
1114
|
+
if (!type.members.some((member) => member.displayName !== void 0)) {
|
|
1115
|
+
return void 0;
|
|
1116
|
+
}
|
|
1117
|
+
const displayNames = /* @__PURE__ */ Object.create(null);
|
|
1118
|
+
for (const member of type.members) {
|
|
1119
|
+
const key = String(member.value);
|
|
1120
|
+
if (Object.hasOwn(displayNames, key)) {
|
|
1121
|
+
throw new Error(
|
|
1122
|
+
`Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
displayNames[key] = member.displayName ?? key;
|
|
1126
|
+
}
|
|
1127
|
+
return displayNames;
|
|
925
1128
|
}
|
|
926
1129
|
function generateArrayType(type, ctx) {
|
|
927
1130
|
return {
|
|
@@ -1314,11 +1517,17 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
|
|
|
1314
1517
|
|
|
1315
1518
|
// src/json-schema/generator.ts
|
|
1316
1519
|
function generateJsonSchema(form, options) {
|
|
1520
|
+
const metadata = options?.metadata;
|
|
1521
|
+
const vendorPrefix = options?.vendorPrefix;
|
|
1522
|
+
const enumSerialization = options?.enumSerialization;
|
|
1317
1523
|
const ir = canonicalizeChainDSL(
|
|
1318
1524
|
form,
|
|
1319
|
-
|
|
1525
|
+
metadata !== void 0 ? { metadata } : void 0
|
|
1320
1526
|
);
|
|
1321
|
-
const internalOptions =
|
|
1527
|
+
const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
|
|
1528
|
+
...vendorPrefix !== void 0 && { vendorPrefix },
|
|
1529
|
+
...enumSerialization !== void 0 && { enumSerialization }
|
|
1530
|
+
};
|
|
1322
1531
|
return generateJsonSchemaFromIR(ir, internalOptions);
|
|
1323
1532
|
}
|
|
1324
1533
|
|