@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.
@@ -467,6 +467,11 @@ export declare interface GenerateJsonSchemaOptions {
467
467
  * @defaultValue "x-formspec"
468
468
  */
469
469
  readonly vendorPrefix?: string | undefined;
470
+ /**
471
+ * JSON Schema representation to use for static enums.
472
+ * @defaultValue "enum"
473
+ */
474
+ readonly enumSerialization?: "enum" | "oneOf";
470
475
  /** Metadata resolution policy for chain DSL generation. */
471
476
  readonly metadata?: MetadataPolicyInput | undefined;
472
477
  }
@@ -1284,6 +1289,11 @@ export declare interface StaticSchemaGenerationOptions {
1284
1289
  * @defaultValue "x-formspec"
1285
1290
  */
1286
1291
  readonly vendorPrefix?: string | undefined;
1292
+ /**
1293
+ * JSON Schema representation to use for static enums.
1294
+ * @defaultValue "enum"
1295
+ */
1296
+ readonly enumSerialization?: "enum" | "oneOf";
1287
1297
  /** Metadata resolution policy for static schema generation. */
1288
1298
  readonly metadata?: MetadataPolicyInput | undefined;
1289
1299
  /** Discriminator-specific schema generation behavior. */
@@ -467,6 +467,11 @@ export declare interface GenerateJsonSchemaOptions {
467
467
  * @defaultValue "x-formspec"
468
468
  */
469
469
  readonly vendorPrefix?: string | undefined;
470
+ /**
471
+ * JSON Schema representation to use for static enums.
472
+ * @defaultValue "enum"
473
+ */
474
+ readonly enumSerialization?: "enum" | "oneOf";
470
475
  /** Metadata resolution policy for chain DSL generation. */
471
476
  readonly metadata?: MetadataPolicyInput | undefined;
472
477
  }
@@ -1284,6 +1289,11 @@ export declare interface StaticSchemaGenerationOptions {
1284
1289
  * @defaultValue "x-formspec"
1285
1290
  */
1286
1291
  readonly vendorPrefix?: string | undefined;
1292
+ /**
1293
+ * JSON Schema representation to use for static enums.
1294
+ * @defaultValue "enum"
1295
+ */
1296
+ readonly enumSerialization?: "enum" | "oneOf";
1287
1297
  /** Metadata resolution policy for static schema generation. */
1288
1298
  readonly metadata?: MetadataPolicyInput | undefined;
1289
1299
  /** Discriminator-specific schema generation behavior. */
@@ -467,6 +467,11 @@ export declare interface GenerateJsonSchemaOptions {
467
467
  * @defaultValue "x-formspec"
468
468
  */
469
469
  readonly vendorPrefix?: string | undefined;
470
+ /**
471
+ * JSON Schema representation to use for static enums.
472
+ * @defaultValue "enum"
473
+ */
474
+ readonly enumSerialization?: "enum" | "oneOf";
470
475
  /** Metadata resolution policy for chain DSL generation. */
471
476
  readonly metadata?: MetadataPolicyInput | undefined;
472
477
  }
@@ -1284,6 +1289,11 @@ export declare interface StaticSchemaGenerationOptions {
1284
1289
  * @defaultValue "x-formspec"
1285
1290
  */
1286
1291
  readonly vendorPrefix?: string | undefined;
1292
+ /**
1293
+ * JSON Schema representation to use for static enums.
1294
+ * @defaultValue "enum"
1295
+ */
1296
+ readonly enumSerialization?: "enum" | "oneOf";
1287
1297
  /** Metadata resolution policy for static schema generation. */
1288
1298
  readonly metadata?: MetadataPolicyInput | undefined;
1289
1299
  /** Discriminator-specific schema generation behavior. */
package/dist/build.d.ts CHANGED
@@ -467,6 +467,11 @@ export declare interface GenerateJsonSchemaOptions {
467
467
  * @defaultValue "x-formspec"
468
468
  */
469
469
  readonly vendorPrefix?: string | undefined;
470
+ /**
471
+ * JSON Schema representation to use for static enums.
472
+ * @defaultValue "enum"
473
+ */
474
+ readonly enumSerialization?: "enum" | "oneOf";
470
475
  /** Metadata resolution policy for chain DSL generation. */
471
476
  readonly metadata?: MetadataPolicyInput | undefined;
472
477
  }
@@ -1284,6 +1289,11 @@ export declare interface StaticSchemaGenerationOptions {
1284
1289
  * @defaultValue "x-formspec"
1285
1290
  */
1286
1291
  readonly vendorPrefix?: string | undefined;
1292
+ /**
1293
+ * JSON Schema representation to use for static enums.
1294
+ * @defaultValue "enum"
1295
+ */
1296
+ readonly enumSerialization?: "enum" | "oneOf";
1287
1297
  /** Metadata resolution policy for static schema generation. */
1288
1298
  readonly metadata?: MetadataPolicyInput | undefined;
1289
1299
  /** Discriminator-specific schema generation behavior. */
@@ -1 +1 @@
1
- {"version":3,"file":"chain-dsl-canonicalizer.d.ts","sourceRoot":"","sources":["../../src/canonicalize/chain-dsl-canonicalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EASV,WAAW,EACX,QAAQ,EAMR,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAYV,MAAM,EAaP,MAAM,0BAA0B,CAAC;AA2ClC;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CACzC;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,CAAC,SAAS,WAAW,EAAE,CAAC,EACtC,OAAO,CAAC,EAAE,2BAA2B,GACpC,MAAM,CAYR"}
1
+ {"version":3,"file":"chain-dsl-canonicalizer.d.ts","sourceRoot":"","sources":["../../src/canonicalize/chain-dsl-canonicalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EASV,WAAW,EACX,QAAQ,EAMR,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAYV,MAAM,EAaP,MAAM,0BAA0B,CAAC;AA4ClC;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CACzC;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,CAAC,SAAS,WAAW,EAAE,CAAC,EACtC,OAAO,CAAC,EAAE,2BAA2B,GACpC,MAAM,CAoBR"}
package/dist/cli.cjs CHANGED
@@ -79,11 +79,35 @@ function normalizeDeclarationPolicy(input) {
79
79
  displayName: normalizeScalarPolicy(input?.displayName)
80
80
  };
81
81
  }
82
+ function normalizeEnumMemberDisplayNamePolicy(input) {
83
+ if (input?.mode === "infer-if-missing") {
84
+ return {
85
+ mode: "infer-if-missing",
86
+ infer: input.infer
87
+ };
88
+ }
89
+ if (input?.mode === "require-explicit") {
90
+ return {
91
+ mode: "require-explicit",
92
+ infer: () => ""
93
+ };
94
+ }
95
+ return {
96
+ mode: "disabled",
97
+ infer: () => ""
98
+ };
99
+ }
100
+ function normalizeEnumMemberPolicy(input) {
101
+ return {
102
+ displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
103
+ };
104
+ }
82
105
  function normalizeMetadataPolicy(input) {
83
106
  return {
84
107
  type: normalizeDeclarationPolicy(input?.type),
85
108
  field: normalizeDeclarationPolicy(input?.field),
86
- method: normalizeDeclarationPolicy(input?.method)
109
+ method: normalizeDeclarationPolicy(input?.method),
110
+ enumMember: normalizeEnumMemberPolicy(input?.enumMember)
87
111
  };
88
112
  }
89
113
  function getDeclarationMetadataPolicy(policy, declarationKind) {
@@ -196,6 +220,40 @@ function pickResolvedMetadataValue(baseValue, overlayValue) {
196
220
  }
197
221
  return baseValue ?? overlayValue;
198
222
  }
223
+ function resolveEnumMemberDisplayName(current, policy, context) {
224
+ if (current !== void 0) {
225
+ return current;
226
+ }
227
+ if (policy.mode === "require-explicit") {
228
+ throw new Error(
229
+ `Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
230
+ );
231
+ }
232
+ if (policy.mode !== "infer-if-missing") {
233
+ return void 0;
234
+ }
235
+ const inferredValue = policy.infer(context).trim();
236
+ return inferredValue !== "" ? inferredValue : void 0;
237
+ }
238
+ function resolveEnumTypeMetadata(type, options) {
239
+ const members = type.members.map((member) => {
240
+ const displayName = resolveEnumMemberDisplayName(
241
+ member.displayName,
242
+ options.policy.enumMember.displayName,
243
+ {
244
+ surface: options.surface,
245
+ logicalName: String(member.value),
246
+ memberValue: member.value,
247
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
248
+ }
249
+ );
250
+ if (displayName === member.displayName) {
251
+ return member;
252
+ }
253
+ return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
254
+ });
255
+ return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
256
+ }
199
257
  function resolveTypeNodeMetadata(type, options) {
200
258
  switch (type.kind) {
201
259
  case "array":
@@ -218,9 +276,10 @@ function resolveTypeNodeMetadata(type, options) {
218
276
  ...type,
219
277
  members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
220
278
  };
279
+ case "enum":
280
+ return resolveEnumTypeMetadata(type, options);
221
281
  case "reference":
222
282
  case "primitive":
223
- case "enum":
224
283
  case "dynamic":
225
284
  case "custom":
226
285
  return type;
@@ -323,11 +382,10 @@ function getDisplayName(metadata) {
323
382
  return metadata?.displayName?.value;
324
383
  }
325
384
  function resolveFormIRMetadata(ir, options) {
326
- const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
327
- const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
385
+ const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
328
386
  surface: options.surface,
329
387
  declarationKind: "type",
330
- logicalName: rootLogicalName,
388
+ logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
331
389
  ...options.buildContext !== void 0 && { buildContext: options.buildContext }
332
390
  });
333
391
  return {
@@ -372,7 +430,7 @@ function canonicalizeChainDSL(form, options) {
372
430
  const metadataPolicy = normalizeMetadataPolicy(
373
431
  options?.metadata ?? (0, import_internals._getFormSpecMetadataPolicy)(form)
374
432
  );
375
- return {
433
+ const ir = {
376
434
  kind: "form-ir",
377
435
  irVersion: import_internals.IR_VERSION,
378
436
  elements: canonicalizeElements(form.elements, metadataPolicy),
@@ -380,6 +438,13 @@ function canonicalizeChainDSL(form, options) {
380
438
  typeRegistry: {},
381
439
  provenance: CHAIN_DSL_PROVENANCE
382
440
  };
441
+ return resolveFormIRMetadata(ir, {
442
+ policy: metadataPolicy,
443
+ surface: "chain-dsl",
444
+ // Chain DSL has no root/type-metadata authoring surface, so only resolve
445
+ // field/type-registry metadata and enum-member labels here.
446
+ resolveRootTypeMetadata: false
447
+ });
383
448
  }
384
449
  function canonicalizeElements(elements, metadataPolicy) {
385
450
  return elements.map((element) => canonicalizeElement(element, metadataPolicy));
@@ -953,17 +1018,25 @@ var init_collision_guards = __esm({
953
1018
  // src/json-schema/ir-generator.ts
954
1019
  function makeContext(options) {
955
1020
  const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
1021
+ const rawEnumSerialization = options?.enumSerialization;
956
1022
  if (!vendorPrefix.startsWith("x-")) {
957
1023
  throw new Error(
958
1024
  `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
959
1025
  );
960
1026
  }
1027
+ if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
1028
+ throw new Error(
1029
+ `Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
1030
+ );
1031
+ }
1032
+ const enumSerialization = rawEnumSerialization ?? "enum";
961
1033
  return {
962
1034
  defs: {},
963
1035
  typeNameMap: {},
964
1036
  typeRegistry: {},
965
1037
  extensionRegistry: options?.extensionRegistry,
966
- vendorPrefix
1038
+ vendorPrefix,
1039
+ enumSerialization
967
1040
  };
968
1041
  }
969
1042
  function generateJsonSchemaFromIR(ir, options) {
@@ -1137,7 +1210,7 @@ function generateTypeNode(type, ctx) {
1137
1210
  case "primitive":
1138
1211
  return generatePrimitiveType(type);
1139
1212
  case "enum":
1140
- return generateEnumType(type);
1213
+ return generateEnumType(type, ctx);
1141
1214
  case "array":
1142
1215
  return generateArrayType(type, ctx);
1143
1216
  case "object":
@@ -1163,20 +1236,37 @@ function generatePrimitiveType(type) {
1163
1236
  type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
1164
1237
  };
1165
1238
  }
1166
- function generateEnumType(type) {
1167
- const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
1168
- if (hasDisplayNames) {
1239
+ function generateEnumType(type, ctx) {
1240
+ if (ctx.enumSerialization === "oneOf") {
1169
1241
  return {
1170
- oneOf: type.members.map((m) => {
1171
- const entry = { const: m.value };
1172
- if (m.displayName !== void 0) {
1173
- entry.title = m.displayName;
1174
- }
1175
- return entry;
1176
- })
1242
+ oneOf: type.members.map((m) => ({
1243
+ const: m.value,
1244
+ title: m.displayName ?? String(m.value)
1245
+ }))
1177
1246
  };
1178
1247
  }
1179
- return { enum: type.members.map((m) => m.value) };
1248
+ const schema = { enum: type.members.map((m) => m.value) };
1249
+ const displayNames = buildEnumDisplayNameExtension(type);
1250
+ if (displayNames !== void 0) {
1251
+ schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
1252
+ }
1253
+ return schema;
1254
+ }
1255
+ function buildEnumDisplayNameExtension(type) {
1256
+ if (!type.members.some((member) => member.displayName !== void 0)) {
1257
+ return void 0;
1258
+ }
1259
+ const displayNames = /* @__PURE__ */ Object.create(null);
1260
+ for (const member of type.members) {
1261
+ const key = String(member.value);
1262
+ if (Object.hasOwn(displayNames, key)) {
1263
+ throw new Error(
1264
+ `Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
1265
+ );
1266
+ }
1267
+ displayNames[key] = member.displayName ?? key;
1268
+ }
1269
+ return displayNames;
1180
1270
  }
1181
1271
  function generateArrayType(type, ctx) {
1182
1272
  return {
@@ -1576,11 +1666,17 @@ var init_ir_generator = __esm({
1576
1666
 
1577
1667
  // src/json-schema/generator.ts
1578
1668
  function generateJsonSchema(form, options) {
1669
+ const metadata = options?.metadata;
1670
+ const vendorPrefix = options?.vendorPrefix;
1671
+ const enumSerialization = options?.enumSerialization;
1579
1672
  const ir = canonicalizeChainDSL(
1580
1673
  form,
1581
- options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
1674
+ metadata !== void 0 ? { metadata } : void 0
1582
1675
  );
1583
- const internalOptions = options?.vendorPrefix === void 0 ? void 0 : { vendorPrefix: options.vendorPrefix };
1676
+ const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1677
+ ...vendorPrefix !== void 0 && { vendorPrefix },
1678
+ ...enumSerialization !== void 0 && { enumSerialization }
1679
+ };
1584
1680
  return generateJsonSchemaFromIR(ir, internalOptions);
1585
1681
  }
1586
1682
  var init_generator = __esm({
@@ -5594,6 +5690,7 @@ function generateSchemasFromClass(options) {
5594
5690
  {
5595
5691
  extensionRegistry: options.extensionRegistry,
5596
5692
  metadata: options.metadata,
5693
+ enumSerialization: options.enumSerialization,
5597
5694
  vendorPrefix: options.vendorPrefix
5598
5695
  }
5599
5696
  );
@@ -5723,6 +5820,7 @@ function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, opti
5723
5820
  {
5724
5821
  extensionRegistry: options.extensionRegistry,
5725
5822
  metadata: options.metadata,
5823
+ enumSerialization: options.enumSerialization,
5726
5824
  vendorPrefix: options.vendorPrefix
5727
5825
  }
5728
5826
  );
@@ -5939,7 +6037,7 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5939
6037
  column: 0
5940
6038
  }
5941
6039
  };
5942
- const schema = generateJsonSchemaFromIR(
6040
+ const ir = resolveFormIRMetadata(
5943
6041
  {
5944
6042
  kind: "form-ir",
5945
6043
  name: root.name,
@@ -5950,8 +6048,17 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5950
6048
  typeRegistry,
5951
6049
  provenance: syntheticField.provenance
5952
6050
  },
6051
+ {
6052
+ policy: normalizeMetadataPolicy(options?.metadata),
6053
+ surface: "tsdoc",
6054
+ rootLogicalName: root.name
6055
+ }
6056
+ );
6057
+ const schema = generateJsonSchemaFromIR(
6058
+ ir,
5953
6059
  {
5954
6060
  extensionRegistry: options?.extensionRegistry,
6061
+ enumSerialization: options?.enumSerialization,
5955
6062
  vendorPrefix: options?.vendorPrefix
5956
6063
  }
5957
6064
  );
@@ -5978,6 +6085,7 @@ function generateSchemasFromAnalysis(analysis, filePath, options) {
5978
6085
  { file: filePath },
5979
6086
  {
5980
6087
  extensionRegistry: options?.extensionRegistry,
6088
+ enumSerialization: options?.enumSerialization,
5981
6089
  metadata: options?.metadata,
5982
6090
  vendorPrefix: options?.vendorPrefix
5983
6091
  }
@@ -6425,8 +6533,19 @@ function buildFormSchemas(form, options) {
6425
6533
  };
6426
6534
  }
6427
6535
  function writeSchemas(form, options) {
6428
- const { outDir, name = "schema", indent = 2, vendorPrefix, metadata } = options;
6429
- const buildOptions = vendorPrefix === void 0 && metadata === void 0 ? void 0 : { vendorPrefix, metadata };
6536
+ const {
6537
+ outDir,
6538
+ name = "schema",
6539
+ indent = 2,
6540
+ vendorPrefix,
6541
+ enumSerialization,
6542
+ metadata
6543
+ } = options;
6544
+ const buildOptions = vendorPrefix === void 0 && enumSerialization === void 0 && metadata === void 0 ? void 0 : {
6545
+ ...vendorPrefix !== void 0 && { vendorPrefix },
6546
+ ...enumSerialization !== void 0 && { enumSerialization },
6547
+ ...metadata !== void 0 && { metadata }
6548
+ };
6430
6549
  const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
6431
6550
  if (!fs.existsSync(outDir)) {
6432
6551
  fs.mkdirSync(outDir, { recursive: true });
@@ -6474,6 +6593,8 @@ Usage:
6474
6593
  Options:
6475
6594
  -o, --out-dir <dir> Output directory (default: ./generated)
6476
6595
  -n, --name <name> Base name for output files (default: derived from input)
6596
+ --enum-serialization <enum|oneOf>
6597
+ Enum JSON Schema representation (default: enum)
6477
6598
  -h, --help Show this help message
6478
6599
 
6479
6600
  Example:
@@ -6490,6 +6611,7 @@ function parseArgs(args) {
6490
6611
  const positional = [];
6491
6612
  let outDir = "./generated";
6492
6613
  let name = "";
6614
+ let enumSerialization = "enum";
6493
6615
  for (let i = 0; i < args.length; i++) {
6494
6616
  const arg = args[i];
6495
6617
  if (arg === void 0) continue;
@@ -6517,6 +6639,20 @@ function parseArgs(args) {
6517
6639
  i++;
6518
6640
  continue;
6519
6641
  }
6642
+ if (arg === "--enum-serialization") {
6643
+ const nextArg = args[i + 1];
6644
+ if (!nextArg) {
6645
+ console.error("Error: --enum-serialization requires a value");
6646
+ return null;
6647
+ }
6648
+ if (nextArg !== "enum" && nextArg !== "oneOf") {
6649
+ console.error('Error: --enum-serialization must be "enum" or "oneOf"');
6650
+ return null;
6651
+ }
6652
+ enumSerialization = nextArg;
6653
+ i++;
6654
+ continue;
6655
+ }
6520
6656
  if (arg.startsWith("-")) {
6521
6657
  console.error(`Error: Unknown option: ${arg}`);
6522
6658
  return null;
@@ -6536,7 +6672,7 @@ function parseArgs(args) {
6536
6672
  if (!name) {
6537
6673
  name = path3.basename(inputFile, path3.extname(inputFile));
6538
6674
  }
6539
- return { inputFile, outDir, name };
6675
+ return { inputFile, outDir, name, enumSerialization };
6540
6676
  }
6541
6677
  async function main() {
6542
6678
  const args = process.argv.slice(2);
@@ -6544,7 +6680,7 @@ async function main() {
6544
6680
  if (!options) {
6545
6681
  process.exit(1);
6546
6682
  }
6547
- const { inputFile, outDir, name } = options;
6683
+ const { inputFile, outDir, name, enumSerialization } = options;
6548
6684
  const absoluteInput = path3.resolve(process.cwd(), inputFile);
6549
6685
  try {
6550
6686
  const fileUrl = (0, import_node_url.pathToFileURL)(absoluteInput).href;
@@ -6561,7 +6697,7 @@ async function main() {
6561
6697
  const { writeSchemas: writeSchemas2 } = await Promise.resolve().then(() => (init_index(), index_exports));
6562
6698
  const { jsonSchemaPath, uiSchemaPath } = writeSchemas2(
6563
6699
  form,
6564
- { outDir, name }
6700
+ { outDir, name, enumSerialization }
6565
6701
  );
6566
6702
  console.log("Generated:");
6567
6703
  console.log(` ${jsonSchemaPath}`);