@formspec/build 0.1.0-alpha.40 → 0.1.0-alpha.42

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 {
@@ -4769,6 +4859,66 @@ function generateCustomType(type, ctx) {
4769
4859
  }
4770
4860
  return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
4771
4861
  }
4862
+ var JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
4863
+ "$schema",
4864
+ "$ref",
4865
+ "$defs",
4866
+ "$id",
4867
+ "$anchor",
4868
+ "$dynamicRef",
4869
+ "$dynamicAnchor",
4870
+ "$vocabulary",
4871
+ "$comment",
4872
+ "type",
4873
+ "enum",
4874
+ "const",
4875
+ "properties",
4876
+ "patternProperties",
4877
+ "additionalProperties",
4878
+ "required",
4879
+ "items",
4880
+ "prefixItems",
4881
+ "additionalItems",
4882
+ "contains",
4883
+ "allOf",
4884
+ "oneOf",
4885
+ "anyOf",
4886
+ "not",
4887
+ "if",
4888
+ "then",
4889
+ "else",
4890
+ "minimum",
4891
+ "maximum",
4892
+ "exclusiveMinimum",
4893
+ "exclusiveMaximum",
4894
+ "multipleOf",
4895
+ "minLength",
4896
+ "maxLength",
4897
+ "pattern",
4898
+ "minItems",
4899
+ "maxItems",
4900
+ "uniqueItems",
4901
+ "minProperties",
4902
+ "maxProperties",
4903
+ "minContains",
4904
+ "maxContains",
4905
+ "format",
4906
+ "title",
4907
+ "description",
4908
+ "default",
4909
+ "deprecated",
4910
+ "readOnly",
4911
+ "writeOnly",
4912
+ "examples",
4913
+ "dependentRequired",
4914
+ "dependentSchemas",
4915
+ "propertyNames",
4916
+ "unevaluatedItems",
4917
+ "unevaluatedProperties",
4918
+ "contentEncoding",
4919
+ "contentMediaType",
4920
+ "contentSchema"
4921
+ ]);
4772
4922
  function applyCustomConstraint(schema, constraint, ctx) {
4773
4923
  const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
4774
4924
  if (registration === void 0) {
@@ -4776,12 +4926,25 @@ function applyCustomConstraint(schema, constraint, ctx) {
4776
4926
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
4777
4927
  );
4778
4928
  }
4779
- assignVendorPrefixedExtensionKeywords(
4780
- schema,
4781
- registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
4782
- ctx.vendorPrefix,
4783
- `custom constraint "${constraint.constraintId}"`
4784
- );
4929
+ const extensionSchema = registration.toJsonSchema(constraint.payload, ctx.vendorPrefix);
4930
+ if (registration.emitsVocabularyKeywords) {
4931
+ const target = schema;
4932
+ for (const [key, value] of Object.entries(extensionSchema)) {
4933
+ if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
4934
+ throw new Error(
4935
+ `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
4936
+ );
4937
+ }
4938
+ target[key] = value;
4939
+ }
4940
+ } else {
4941
+ assignVendorPrefixedExtensionKeywords(
4942
+ schema,
4943
+ extensionSchema,
4944
+ ctx.vendorPrefix,
4945
+ `custom constraint "${constraint.constraintId}"`
4946
+ );
4947
+ }
4785
4948
  }
4786
4949
  function applyCustomAnnotation(schema, annotation, ctx) {
4787
4950
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -5337,7 +5500,7 @@ function createExtensionRegistry(extensions) {
5337
5500
 
5338
5501
  // src/generators/method-schema.ts
5339
5502
  var import_internals6 = require("@formspec/core/internals");
5340
- function typeToJsonSchema(type, checker) {
5503
+ function typeToJsonSchema(type, checker, options) {
5341
5504
  const typeRegistry = {};
5342
5505
  const visiting = /* @__PURE__ */ new Set();
5343
5506
  const diagnostics = [];
@@ -5377,7 +5540,10 @@ function typeToJsonSchema(type, checker) {
5377
5540
  typeRegistry,
5378
5541
  provenance: fieldProvenance
5379
5542
  };
5380
- const schema = generateJsonSchemaFromIR(ir);
5543
+ const schema = generateJsonSchemaFromIR(
5544
+ ir,
5545
+ options?.enumSerialization === void 0 ? void 0 : { enumSerialization: options.enumSerialization }
5546
+ );
5381
5547
  const fieldSchema = schema.properties?.["__result"];
5382
5548
  if (fieldSchema) {
5383
5549
  if (schema.$defs && Object.keys(schema.$defs).length > 0) {
@@ -5387,16 +5553,16 @@ function typeToJsonSchema(type, checker) {
5387
5553
  }
5388
5554
  return { type: "object" };
5389
5555
  }
5390
- function generateMethodSchemas(method, checker, loadedFormSpecs) {
5391
- const returnType = typeToJsonSchema(method.returnType, checker);
5392
- const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
5556
+ function generateMethodSchemas(method, checker, loadedFormSpecs, options) {
5557
+ const returnType = typeToJsonSchema(method.returnType, checker, options);
5558
+ const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs, options);
5393
5559
  return {
5394
5560
  name: method.name,
5395
5561
  params,
5396
5562
  returnType
5397
5563
  };
5398
5564
  }
5399
- function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5565
+ function generateParamsSchemas(parameters, checker, loadedFormSpecs, options) {
5400
5566
  if (parameters.length === 0) {
5401
5567
  return null;
5402
5568
  }
@@ -5417,7 +5583,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5417
5583
  }
5418
5584
  if (parameters.length === 1 && parameters[0]) {
5419
5585
  const param = parameters[0];
5420
- const jsonSchema = typeToJsonSchema(param.type, checker);
5586
+ const jsonSchema = typeToJsonSchema(param.type, checker, options);
5421
5587
  return {
5422
5588
  jsonSchema,
5423
5589
  uiSchema: null,
@@ -5427,7 +5593,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5427
5593
  const properties = {};
5428
5594
  const required = [];
5429
5595
  for (const param of parameters) {
5430
- const paramSchema = typeToJsonSchema(param.type, checker);
5596
+ const paramSchema = typeToJsonSchema(param.type, checker, options);
5431
5597
  properties[param.name] = paramSchema;
5432
5598
  if (!param.optional) {
5433
5599
  required.push(param.name);