@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/cli.js
CHANGED
|
@@ -58,11 +58,35 @@ function normalizeDeclarationPolicy(input) {
|
|
|
58
58
|
displayName: normalizeScalarPolicy(input?.displayName)
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
function normalizeEnumMemberDisplayNamePolicy(input) {
|
|
62
|
+
if (input?.mode === "infer-if-missing") {
|
|
63
|
+
return {
|
|
64
|
+
mode: "infer-if-missing",
|
|
65
|
+
infer: input.infer
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (input?.mode === "require-explicit") {
|
|
69
|
+
return {
|
|
70
|
+
mode: "require-explicit",
|
|
71
|
+
infer: () => ""
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
mode: "disabled",
|
|
76
|
+
infer: () => ""
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function normalizeEnumMemberPolicy(input) {
|
|
80
|
+
return {
|
|
81
|
+
displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
61
84
|
function normalizeMetadataPolicy(input) {
|
|
62
85
|
return {
|
|
63
86
|
type: normalizeDeclarationPolicy(input?.type),
|
|
64
87
|
field: normalizeDeclarationPolicy(input?.field),
|
|
65
|
-
method: normalizeDeclarationPolicy(input?.method)
|
|
88
|
+
method: normalizeDeclarationPolicy(input?.method),
|
|
89
|
+
enumMember: normalizeEnumMemberPolicy(input?.enumMember)
|
|
66
90
|
};
|
|
67
91
|
}
|
|
68
92
|
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
@@ -175,6 +199,40 @@ function pickResolvedMetadataValue(baseValue, overlayValue) {
|
|
|
175
199
|
}
|
|
176
200
|
return baseValue ?? overlayValue;
|
|
177
201
|
}
|
|
202
|
+
function resolveEnumMemberDisplayName(current, policy, context) {
|
|
203
|
+
if (current !== void 0) {
|
|
204
|
+
return current;
|
|
205
|
+
}
|
|
206
|
+
if (policy.mode === "require-explicit") {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (policy.mode !== "infer-if-missing") {
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
const inferredValue = policy.infer(context).trim();
|
|
215
|
+
return inferredValue !== "" ? inferredValue : void 0;
|
|
216
|
+
}
|
|
217
|
+
function resolveEnumTypeMetadata(type, options) {
|
|
218
|
+
const members = type.members.map((member) => {
|
|
219
|
+
const displayName = resolveEnumMemberDisplayName(
|
|
220
|
+
member.displayName,
|
|
221
|
+
options.policy.enumMember.displayName,
|
|
222
|
+
{
|
|
223
|
+
surface: options.surface,
|
|
224
|
+
logicalName: String(member.value),
|
|
225
|
+
memberValue: member.value,
|
|
226
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
if (displayName === member.displayName) {
|
|
230
|
+
return member;
|
|
231
|
+
}
|
|
232
|
+
return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
|
|
233
|
+
});
|
|
234
|
+
return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
|
|
235
|
+
}
|
|
178
236
|
function resolveTypeNodeMetadata(type, options) {
|
|
179
237
|
switch (type.kind) {
|
|
180
238
|
case "array":
|
|
@@ -197,9 +255,10 @@ function resolveTypeNodeMetadata(type, options) {
|
|
|
197
255
|
...type,
|
|
198
256
|
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
199
257
|
};
|
|
258
|
+
case "enum":
|
|
259
|
+
return resolveEnumTypeMetadata(type, options);
|
|
200
260
|
case "reference":
|
|
201
261
|
case "primitive":
|
|
202
|
-
case "enum":
|
|
203
262
|
case "dynamic":
|
|
204
263
|
case "custom":
|
|
205
264
|
return type;
|
|
@@ -302,11 +361,10 @@ function getDisplayName(metadata) {
|
|
|
302
361
|
return metadata?.displayName?.value;
|
|
303
362
|
}
|
|
304
363
|
function resolveFormIRMetadata(ir, options) {
|
|
305
|
-
const
|
|
306
|
-
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
364
|
+
const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
307
365
|
surface: options.surface,
|
|
308
366
|
declarationKind: "type",
|
|
309
|
-
logicalName: rootLogicalName,
|
|
367
|
+
logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
|
|
310
368
|
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
311
369
|
});
|
|
312
370
|
return {
|
|
@@ -352,7 +410,7 @@ function canonicalizeChainDSL(form, options) {
|
|
|
352
410
|
const metadataPolicy = normalizeMetadataPolicy(
|
|
353
411
|
options?.metadata ?? _getFormSpecMetadataPolicy(form)
|
|
354
412
|
);
|
|
355
|
-
|
|
413
|
+
const ir = {
|
|
356
414
|
kind: "form-ir",
|
|
357
415
|
irVersion: IR_VERSION,
|
|
358
416
|
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
@@ -360,6 +418,13 @@ function canonicalizeChainDSL(form, options) {
|
|
|
360
418
|
typeRegistry: {},
|
|
361
419
|
provenance: CHAIN_DSL_PROVENANCE
|
|
362
420
|
};
|
|
421
|
+
return resolveFormIRMetadata(ir, {
|
|
422
|
+
policy: metadataPolicy,
|
|
423
|
+
surface: "chain-dsl",
|
|
424
|
+
// Chain DSL has no root/type-metadata authoring surface, so only resolve
|
|
425
|
+
// field/type-registry metadata and enum-member labels here.
|
|
426
|
+
resolveRootTypeMetadata: false
|
|
427
|
+
});
|
|
363
428
|
}
|
|
364
429
|
function canonicalizeElements(elements, metadataPolicy) {
|
|
365
430
|
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
@@ -931,17 +996,25 @@ var init_collision_guards = __esm({
|
|
|
931
996
|
// src/json-schema/ir-generator.ts
|
|
932
997
|
function makeContext(options) {
|
|
933
998
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
999
|
+
const rawEnumSerialization = options?.enumSerialization;
|
|
934
1000
|
if (!vendorPrefix.startsWith("x-")) {
|
|
935
1001
|
throw new Error(
|
|
936
1002
|
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
937
1003
|
);
|
|
938
1004
|
}
|
|
1005
|
+
if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
|
|
1006
|
+
throw new Error(
|
|
1007
|
+
`Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
const enumSerialization = rawEnumSerialization ?? "enum";
|
|
939
1011
|
return {
|
|
940
1012
|
defs: {},
|
|
941
1013
|
typeNameMap: {},
|
|
942
1014
|
typeRegistry: {},
|
|
943
1015
|
extensionRegistry: options?.extensionRegistry,
|
|
944
|
-
vendorPrefix
|
|
1016
|
+
vendorPrefix,
|
|
1017
|
+
enumSerialization
|
|
945
1018
|
};
|
|
946
1019
|
}
|
|
947
1020
|
function generateJsonSchemaFromIR(ir, options) {
|
|
@@ -1115,7 +1188,7 @@ function generateTypeNode(type, ctx) {
|
|
|
1115
1188
|
case "primitive":
|
|
1116
1189
|
return generatePrimitiveType(type);
|
|
1117
1190
|
case "enum":
|
|
1118
|
-
return generateEnumType(type);
|
|
1191
|
+
return generateEnumType(type, ctx);
|
|
1119
1192
|
case "array":
|
|
1120
1193
|
return generateArrayType(type, ctx);
|
|
1121
1194
|
case "object":
|
|
@@ -1141,20 +1214,37 @@ function generatePrimitiveType(type) {
|
|
|
1141
1214
|
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
1142
1215
|
};
|
|
1143
1216
|
}
|
|
1144
|
-
function generateEnumType(type) {
|
|
1145
|
-
|
|
1146
|
-
if (hasDisplayNames) {
|
|
1217
|
+
function generateEnumType(type, ctx) {
|
|
1218
|
+
if (ctx.enumSerialization === "oneOf") {
|
|
1147
1219
|
return {
|
|
1148
|
-
oneOf: type.members.map((m) => {
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}
|
|
1153
|
-
return entry;
|
|
1154
|
-
})
|
|
1220
|
+
oneOf: type.members.map((m) => ({
|
|
1221
|
+
const: m.value,
|
|
1222
|
+
title: m.displayName ?? String(m.value)
|
|
1223
|
+
}))
|
|
1155
1224
|
};
|
|
1156
1225
|
}
|
|
1157
|
-
|
|
1226
|
+
const schema = { enum: type.members.map((m) => m.value) };
|
|
1227
|
+
const displayNames = buildEnumDisplayNameExtension(type);
|
|
1228
|
+
if (displayNames !== void 0) {
|
|
1229
|
+
schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
|
|
1230
|
+
}
|
|
1231
|
+
return schema;
|
|
1232
|
+
}
|
|
1233
|
+
function buildEnumDisplayNameExtension(type) {
|
|
1234
|
+
if (!type.members.some((member) => member.displayName !== void 0)) {
|
|
1235
|
+
return void 0;
|
|
1236
|
+
}
|
|
1237
|
+
const displayNames = /* @__PURE__ */ Object.create(null);
|
|
1238
|
+
for (const member of type.members) {
|
|
1239
|
+
const key = String(member.value);
|
|
1240
|
+
if (Object.hasOwn(displayNames, key)) {
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
`Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
displayNames[key] = member.displayName ?? key;
|
|
1246
|
+
}
|
|
1247
|
+
return displayNames;
|
|
1158
1248
|
}
|
|
1159
1249
|
function generateArrayType(type, ctx) {
|
|
1160
1250
|
return {
|
|
@@ -1554,11 +1644,17 @@ var init_ir_generator = __esm({
|
|
|
1554
1644
|
|
|
1555
1645
|
// src/json-schema/generator.ts
|
|
1556
1646
|
function generateJsonSchema(form, options) {
|
|
1647
|
+
const metadata = options?.metadata;
|
|
1648
|
+
const vendorPrefix = options?.vendorPrefix;
|
|
1649
|
+
const enumSerialization = options?.enumSerialization;
|
|
1557
1650
|
const ir = canonicalizeChainDSL(
|
|
1558
1651
|
form,
|
|
1559
|
-
|
|
1652
|
+
metadata !== void 0 ? { metadata } : void 0
|
|
1560
1653
|
);
|
|
1561
|
-
const internalOptions =
|
|
1654
|
+
const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
|
|
1655
|
+
...vendorPrefix !== void 0 && { vendorPrefix },
|
|
1656
|
+
...enumSerialization !== void 0 && { enumSerialization }
|
|
1657
|
+
};
|
|
1562
1658
|
return generateJsonSchemaFromIR(ir, internalOptions);
|
|
1563
1659
|
}
|
|
1564
1660
|
var init_generator = __esm({
|
|
@@ -5606,6 +5702,7 @@ function generateSchemasFromClass(options) {
|
|
|
5606
5702
|
{
|
|
5607
5703
|
extensionRegistry: options.extensionRegistry,
|
|
5608
5704
|
metadata: options.metadata,
|
|
5705
|
+
enumSerialization: options.enumSerialization,
|
|
5609
5706
|
vendorPrefix: options.vendorPrefix
|
|
5610
5707
|
}
|
|
5611
5708
|
);
|
|
@@ -5735,6 +5832,7 @@ function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, opti
|
|
|
5735
5832
|
{
|
|
5736
5833
|
extensionRegistry: options.extensionRegistry,
|
|
5737
5834
|
metadata: options.metadata,
|
|
5835
|
+
enumSerialization: options.enumSerialization,
|
|
5738
5836
|
vendorPrefix: options.vendorPrefix
|
|
5739
5837
|
}
|
|
5740
5838
|
);
|
|
@@ -5951,7 +6049,7 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
|
|
|
5951
6049
|
column: 0
|
|
5952
6050
|
}
|
|
5953
6051
|
};
|
|
5954
|
-
const
|
|
6052
|
+
const ir = resolveFormIRMetadata(
|
|
5955
6053
|
{
|
|
5956
6054
|
kind: "form-ir",
|
|
5957
6055
|
name: root.name,
|
|
@@ -5962,8 +6060,17 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
|
|
|
5962
6060
|
typeRegistry,
|
|
5963
6061
|
provenance: syntheticField.provenance
|
|
5964
6062
|
},
|
|
6063
|
+
{
|
|
6064
|
+
policy: normalizeMetadataPolicy(options?.metadata),
|
|
6065
|
+
surface: "tsdoc",
|
|
6066
|
+
rootLogicalName: root.name
|
|
6067
|
+
}
|
|
6068
|
+
);
|
|
6069
|
+
const schema = generateJsonSchemaFromIR(
|
|
6070
|
+
ir,
|
|
5965
6071
|
{
|
|
5966
6072
|
extensionRegistry: options?.extensionRegistry,
|
|
6073
|
+
enumSerialization: options?.enumSerialization,
|
|
5967
6074
|
vendorPrefix: options?.vendorPrefix
|
|
5968
6075
|
}
|
|
5969
6076
|
);
|
|
@@ -5990,6 +6097,7 @@ function generateSchemasFromAnalysis(analysis, filePath, options) {
|
|
|
5990
6097
|
{ file: filePath },
|
|
5991
6098
|
{
|
|
5992
6099
|
extensionRegistry: options?.extensionRegistry,
|
|
6100
|
+
enumSerialization: options?.enumSerialization,
|
|
5993
6101
|
metadata: options?.metadata,
|
|
5994
6102
|
vendorPrefix: options?.vendorPrefix
|
|
5995
6103
|
}
|
|
@@ -6435,8 +6543,19 @@ function buildFormSchemas(form, options) {
|
|
|
6435
6543
|
};
|
|
6436
6544
|
}
|
|
6437
6545
|
function writeSchemas(form, options) {
|
|
6438
|
-
const {
|
|
6439
|
-
|
|
6546
|
+
const {
|
|
6547
|
+
outDir,
|
|
6548
|
+
name = "schema",
|
|
6549
|
+
indent = 2,
|
|
6550
|
+
vendorPrefix,
|
|
6551
|
+
enumSerialization,
|
|
6552
|
+
metadata
|
|
6553
|
+
} = options;
|
|
6554
|
+
const buildOptions = vendorPrefix === void 0 && enumSerialization === void 0 && metadata === void 0 ? void 0 : {
|
|
6555
|
+
...vendorPrefix !== void 0 && { vendorPrefix },
|
|
6556
|
+
...enumSerialization !== void 0 && { enumSerialization },
|
|
6557
|
+
...metadata !== void 0 && { metadata }
|
|
6558
|
+
};
|
|
6440
6559
|
const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
|
|
6441
6560
|
if (!fs.existsSync(outDir)) {
|
|
6442
6561
|
fs.mkdirSync(outDir, { recursive: true });
|
|
@@ -6481,6 +6600,8 @@ Usage:
|
|
|
6481
6600
|
Options:
|
|
6482
6601
|
-o, --out-dir <dir> Output directory (default: ./generated)
|
|
6483
6602
|
-n, --name <name> Base name for output files (default: derived from input)
|
|
6603
|
+
--enum-serialization <enum|oneOf>
|
|
6604
|
+
Enum JSON Schema representation (default: enum)
|
|
6484
6605
|
-h, --help Show this help message
|
|
6485
6606
|
|
|
6486
6607
|
Example:
|
|
@@ -6497,6 +6618,7 @@ function parseArgs(args) {
|
|
|
6497
6618
|
const positional = [];
|
|
6498
6619
|
let outDir = "./generated";
|
|
6499
6620
|
let name = "";
|
|
6621
|
+
let enumSerialization = "enum";
|
|
6500
6622
|
for (let i = 0; i < args.length; i++) {
|
|
6501
6623
|
const arg = args[i];
|
|
6502
6624
|
if (arg === void 0) continue;
|
|
@@ -6524,6 +6646,20 @@ function parseArgs(args) {
|
|
|
6524
6646
|
i++;
|
|
6525
6647
|
continue;
|
|
6526
6648
|
}
|
|
6649
|
+
if (arg === "--enum-serialization") {
|
|
6650
|
+
const nextArg = args[i + 1];
|
|
6651
|
+
if (!nextArg) {
|
|
6652
|
+
console.error("Error: --enum-serialization requires a value");
|
|
6653
|
+
return null;
|
|
6654
|
+
}
|
|
6655
|
+
if (nextArg !== "enum" && nextArg !== "oneOf") {
|
|
6656
|
+
console.error('Error: --enum-serialization must be "enum" or "oneOf"');
|
|
6657
|
+
return null;
|
|
6658
|
+
}
|
|
6659
|
+
enumSerialization = nextArg;
|
|
6660
|
+
i++;
|
|
6661
|
+
continue;
|
|
6662
|
+
}
|
|
6527
6663
|
if (arg.startsWith("-")) {
|
|
6528
6664
|
console.error(`Error: Unknown option: ${arg}`);
|
|
6529
6665
|
return null;
|
|
@@ -6543,7 +6679,7 @@ function parseArgs(args) {
|
|
|
6543
6679
|
if (!name) {
|
|
6544
6680
|
name = path3.basename(inputFile, path3.extname(inputFile));
|
|
6545
6681
|
}
|
|
6546
|
-
return { inputFile, outDir, name };
|
|
6682
|
+
return { inputFile, outDir, name, enumSerialization };
|
|
6547
6683
|
}
|
|
6548
6684
|
async function main() {
|
|
6549
6685
|
const args = process.argv.slice(2);
|
|
@@ -6551,7 +6687,7 @@ async function main() {
|
|
|
6551
6687
|
if (!options) {
|
|
6552
6688
|
process.exit(1);
|
|
6553
6689
|
}
|
|
6554
|
-
const { inputFile, outDir, name } = options;
|
|
6690
|
+
const { inputFile, outDir, name, enumSerialization } = options;
|
|
6555
6691
|
const absoluteInput = path3.resolve(process.cwd(), inputFile);
|
|
6556
6692
|
try {
|
|
6557
6693
|
const fileUrl = pathToFileURL(absoluteInput).href;
|
|
@@ -6568,7 +6704,7 @@ async function main() {
|
|
|
6568
6704
|
const { writeSchemas: writeSchemas2 } = await Promise.resolve().then(() => (init_index(), index_exports));
|
|
6569
6705
|
const { jsonSchemaPath, uiSchemaPath } = writeSchemas2(
|
|
6570
6706
|
form,
|
|
6571
|
-
{ outDir, name }
|
|
6707
|
+
{ outDir, name, enumSerialization }
|
|
6572
6708
|
);
|
|
6573
6709
|
console.log("Generated:");
|
|
6574
6710
|
console.log(` ${jsonSchemaPath}`);
|