@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.
@@ -106,11 +106,35 @@ function normalizeDeclarationPolicy(input) {
106
106
  displayName: normalizeScalarPolicy(input?.displayName)
107
107
  };
108
108
  }
109
+ function normalizeEnumMemberDisplayNamePolicy(input) {
110
+ if (input?.mode === "infer-if-missing") {
111
+ return {
112
+ mode: "infer-if-missing",
113
+ infer: input.infer
114
+ };
115
+ }
116
+ if (input?.mode === "require-explicit") {
117
+ return {
118
+ mode: "require-explicit",
119
+ infer: () => ""
120
+ };
121
+ }
122
+ return {
123
+ mode: "disabled",
124
+ infer: () => ""
125
+ };
126
+ }
127
+ function normalizeEnumMemberPolicy(input) {
128
+ return {
129
+ displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
130
+ };
131
+ }
109
132
  function normalizeMetadataPolicy(input) {
110
133
  return {
111
134
  type: normalizeDeclarationPolicy(input?.type),
112
135
  field: normalizeDeclarationPolicy(input?.field),
113
- method: normalizeDeclarationPolicy(input?.method)
136
+ method: normalizeDeclarationPolicy(input?.method),
137
+ enumMember: normalizeEnumMemberPolicy(input?.enumMember)
114
138
  };
115
139
  }
116
140
  function getDeclarationMetadataPolicy(policy, declarationKind) {
@@ -207,6 +231,40 @@ function resolveResolvedMetadata(current, policy, context) {
207
231
  ...displayNamePlural !== void 0 && { displayNamePlural }
208
232
  };
209
233
  }
234
+ function resolveEnumMemberDisplayName(current, policy, context) {
235
+ if (current !== void 0) {
236
+ return current;
237
+ }
238
+ if (policy.mode === "require-explicit") {
239
+ throw new Error(
240
+ `Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
241
+ );
242
+ }
243
+ if (policy.mode !== "infer-if-missing") {
244
+ return void 0;
245
+ }
246
+ const inferredValue = policy.infer(context).trim();
247
+ return inferredValue !== "" ? inferredValue : void 0;
248
+ }
249
+ function resolveEnumTypeMetadata(type, options) {
250
+ const members = type.members.map((member) => {
251
+ const displayName = resolveEnumMemberDisplayName(
252
+ member.displayName,
253
+ options.policy.enumMember.displayName,
254
+ {
255
+ surface: options.surface,
256
+ logicalName: String(member.value),
257
+ memberValue: member.value,
258
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
259
+ }
260
+ );
261
+ if (displayName === member.displayName) {
262
+ return member;
263
+ }
264
+ return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
265
+ });
266
+ return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
267
+ }
210
268
  function resolveTypeNodeMetadata(type, options) {
211
269
  switch (type.kind) {
212
270
  case "array":
@@ -229,9 +287,10 @@ function resolveTypeNodeMetadata(type, options) {
229
287
  ...type,
230
288
  members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
231
289
  };
290
+ case "enum":
291
+ return resolveEnumTypeMetadata(type, options);
232
292
  case "reference":
233
293
  case "primitive":
234
- case "enum":
235
294
  case "dynamic":
236
295
  case "custom":
237
296
  return type;
@@ -310,11 +369,10 @@ function getDisplayName(metadata) {
310
369
  return metadata?.displayName?.value;
311
370
  }
312
371
  function resolveFormIRMetadata(ir, options) {
313
- const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
314
- const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
372
+ const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
315
373
  surface: options.surface,
316
374
  declarationKind: "type",
317
- logicalName: rootLogicalName,
375
+ logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
318
376
  ...options.buildContext !== void 0 && { buildContext: options.buildContext }
319
377
  });
320
378
  return {
@@ -350,7 +408,7 @@ function canonicalizeChainDSL(form, options) {
350
408
  const metadataPolicy = normalizeMetadataPolicy(
351
409
  options?.metadata ?? (0, import_internals._getFormSpecMetadataPolicy)(form)
352
410
  );
353
- return {
411
+ const ir = {
354
412
  kind: "form-ir",
355
413
  irVersion: import_internals.IR_VERSION,
356
414
  elements: canonicalizeElements(form.elements, metadataPolicy),
@@ -358,6 +416,13 @@ function canonicalizeChainDSL(form, options) {
358
416
  typeRegistry: {},
359
417
  provenance: CHAIN_DSL_PROVENANCE
360
418
  };
419
+ return resolveFormIRMetadata(ir, {
420
+ policy: metadataPolicy,
421
+ surface: "chain-dsl",
422
+ // Chain DSL has no root/type-metadata authoring surface, so only resolve
423
+ // field/type-registry metadata and enum-member labels here.
424
+ resolveRootTypeMetadata: false
425
+ });
361
426
  }
362
427
  function canonicalizeElements(elements, metadataPolicy) {
363
428
  return elements.map((element) => canonicalizeElement(element, metadataPolicy));
@@ -4197,17 +4262,25 @@ function assertNoSerializedNameCollisions(ir) {
4197
4262
  // src/json-schema/ir-generator.ts
4198
4263
  function makeContext(options) {
4199
4264
  const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
4265
+ const rawEnumSerialization = options?.enumSerialization;
4200
4266
  if (!vendorPrefix.startsWith("x-")) {
4201
4267
  throw new Error(
4202
4268
  `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
4203
4269
  );
4204
4270
  }
4271
+ if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
4272
+ throw new Error(
4273
+ `Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
4274
+ );
4275
+ }
4276
+ const enumSerialization = rawEnumSerialization ?? "enum";
4205
4277
  return {
4206
4278
  defs: {},
4207
4279
  typeNameMap: {},
4208
4280
  typeRegistry: {},
4209
4281
  extensionRegistry: options?.extensionRegistry,
4210
- vendorPrefix
4282
+ vendorPrefix,
4283
+ enumSerialization
4211
4284
  };
4212
4285
  }
4213
4286
  function generateJsonSchemaFromIR(ir, options) {
@@ -4381,7 +4454,7 @@ function generateTypeNode(type, ctx) {
4381
4454
  case "primitive":
4382
4455
  return generatePrimitiveType(type);
4383
4456
  case "enum":
4384
- return generateEnumType(type);
4457
+ return generateEnumType(type, ctx);
4385
4458
  case "array":
4386
4459
  return generateArrayType(type, ctx);
4387
4460
  case "object":
@@ -4407,20 +4480,37 @@ function generatePrimitiveType(type) {
4407
4480
  type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
4408
4481
  };
4409
4482
  }
4410
- function generateEnumType(type) {
4411
- const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
4412
- if (hasDisplayNames) {
4483
+ function generateEnumType(type, ctx) {
4484
+ if (ctx.enumSerialization === "oneOf") {
4413
4485
  return {
4414
- oneOf: type.members.map((m) => {
4415
- const entry = { const: m.value };
4416
- if (m.displayName !== void 0) {
4417
- entry.title = m.displayName;
4418
- }
4419
- return entry;
4420
- })
4486
+ oneOf: type.members.map((m) => ({
4487
+ const: m.value,
4488
+ title: m.displayName ?? String(m.value)
4489
+ }))
4421
4490
  };
4422
4491
  }
4423
- return { enum: type.members.map((m) => m.value) };
4492
+ const schema = { enum: type.members.map((m) => m.value) };
4493
+ const displayNames = buildEnumDisplayNameExtension(type);
4494
+ if (displayNames !== void 0) {
4495
+ schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
4496
+ }
4497
+ return schema;
4498
+ }
4499
+ function buildEnumDisplayNameExtension(type) {
4500
+ if (!type.members.some((member) => member.displayName !== void 0)) {
4501
+ return void 0;
4502
+ }
4503
+ const displayNames = /* @__PURE__ */ Object.create(null);
4504
+ for (const member of type.members) {
4505
+ const key = String(member.value);
4506
+ if (Object.hasOwn(displayNames, key)) {
4507
+ throw new Error(
4508
+ `Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
4509
+ );
4510
+ }
4511
+ displayNames[key] = member.displayName ?? key;
4512
+ }
4513
+ return displayNames;
4424
4514
  }
4425
4515
  function generateArrayType(type, ctx) {
4426
4516
  return {
@@ -5337,7 +5427,7 @@ function createExtensionRegistry(extensions) {
5337
5427
 
5338
5428
  // src/generators/method-schema.ts
5339
5429
  var import_internals6 = require("@formspec/core/internals");
5340
- function typeToJsonSchema(type, checker) {
5430
+ function typeToJsonSchema(type, checker, options) {
5341
5431
  const typeRegistry = {};
5342
5432
  const visiting = /* @__PURE__ */ new Set();
5343
5433
  const diagnostics = [];
@@ -5377,7 +5467,10 @@ function typeToJsonSchema(type, checker) {
5377
5467
  typeRegistry,
5378
5468
  provenance: fieldProvenance
5379
5469
  };
5380
- const schema = generateJsonSchemaFromIR(ir);
5470
+ const schema = generateJsonSchemaFromIR(
5471
+ ir,
5472
+ options?.enumSerialization === void 0 ? void 0 : { enumSerialization: options.enumSerialization }
5473
+ );
5381
5474
  const fieldSchema = schema.properties?.["__result"];
5382
5475
  if (fieldSchema) {
5383
5476
  if (schema.$defs && Object.keys(schema.$defs).length > 0) {
@@ -5387,16 +5480,16 @@ function typeToJsonSchema(type, checker) {
5387
5480
  }
5388
5481
  return { type: "object" };
5389
5482
  }
5390
- function generateMethodSchemas(method, checker, loadedFormSpecs) {
5391
- const returnType = typeToJsonSchema(method.returnType, checker);
5392
- const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
5483
+ function generateMethodSchemas(method, checker, loadedFormSpecs, options) {
5484
+ const returnType = typeToJsonSchema(method.returnType, checker, options);
5485
+ const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs, options);
5393
5486
  return {
5394
5487
  name: method.name,
5395
5488
  params,
5396
5489
  returnType
5397
5490
  };
5398
5491
  }
5399
- function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5492
+ function generateParamsSchemas(parameters, checker, loadedFormSpecs, options) {
5400
5493
  if (parameters.length === 0) {
5401
5494
  return null;
5402
5495
  }
@@ -5417,7 +5510,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5417
5510
  }
5418
5511
  if (parameters.length === 1 && parameters[0]) {
5419
5512
  const param = parameters[0];
5420
- const jsonSchema = typeToJsonSchema(param.type, checker);
5513
+ const jsonSchema = typeToJsonSchema(param.type, checker, options);
5421
5514
  return {
5422
5515
  jsonSchema,
5423
5516
  uiSchema: null,
@@ -5427,7 +5520,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5427
5520
  const properties = {};
5428
5521
  const required = [];
5429
5522
  for (const param of parameters) {
5430
- const paramSchema = typeToJsonSchema(param.type, checker);
5523
+ const paramSchema = typeToJsonSchema(param.type, checker, options);
5431
5524
  properties[param.name] = paramSchema;
5432
5525
  if (!param.optional) {
5433
5526
  required.push(param.name);