@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.
package/dist/internals.js CHANGED
@@ -51,11 +51,35 @@ function normalizeDeclarationPolicy(input) {
51
51
  displayName: normalizeScalarPolicy(input?.displayName)
52
52
  };
53
53
  }
54
+ function normalizeEnumMemberDisplayNamePolicy(input) {
55
+ if (input?.mode === "infer-if-missing") {
56
+ return {
57
+ mode: "infer-if-missing",
58
+ infer: input.infer
59
+ };
60
+ }
61
+ if (input?.mode === "require-explicit") {
62
+ return {
63
+ mode: "require-explicit",
64
+ infer: () => ""
65
+ };
66
+ }
67
+ return {
68
+ mode: "disabled",
69
+ infer: () => ""
70
+ };
71
+ }
72
+ function normalizeEnumMemberPolicy(input) {
73
+ return {
74
+ displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
75
+ };
76
+ }
54
77
  function normalizeMetadataPolicy(input) {
55
78
  return {
56
79
  type: normalizeDeclarationPolicy(input?.type),
57
80
  field: normalizeDeclarationPolicy(input?.field),
58
- method: normalizeDeclarationPolicy(input?.method)
81
+ method: normalizeDeclarationPolicy(input?.method),
82
+ enumMember: normalizeEnumMemberPolicy(input?.enumMember)
59
83
  };
60
84
  }
61
85
  function getDeclarationMetadataPolicy(policy, declarationKind) {
@@ -152,6 +176,40 @@ function resolveResolvedMetadata(current, policy, context) {
152
176
  ...displayNamePlural !== void 0 && { displayNamePlural }
153
177
  };
154
178
  }
179
+ function resolveEnumMemberDisplayName(current, policy, context) {
180
+ if (current !== void 0) {
181
+ return current;
182
+ }
183
+ if (policy.mode === "require-explicit") {
184
+ throw new Error(
185
+ `Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
186
+ );
187
+ }
188
+ if (policy.mode !== "infer-if-missing") {
189
+ return void 0;
190
+ }
191
+ const inferredValue = policy.infer(context).trim();
192
+ return inferredValue !== "" ? inferredValue : void 0;
193
+ }
194
+ function resolveEnumTypeMetadata(type, options) {
195
+ const members = type.members.map((member) => {
196
+ const displayName = resolveEnumMemberDisplayName(
197
+ member.displayName,
198
+ options.policy.enumMember.displayName,
199
+ {
200
+ surface: options.surface,
201
+ logicalName: String(member.value),
202
+ memberValue: member.value,
203
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
204
+ }
205
+ );
206
+ if (displayName === member.displayName) {
207
+ return member;
208
+ }
209
+ return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
210
+ });
211
+ return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
212
+ }
155
213
  function resolveTypeNodeMetadata(type, options) {
156
214
  switch (type.kind) {
157
215
  case "array":
@@ -174,9 +232,10 @@ function resolveTypeNodeMetadata(type, options) {
174
232
  ...type,
175
233
  members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
176
234
  };
235
+ case "enum":
236
+ return resolveEnumTypeMetadata(type, options);
177
237
  case "reference":
178
238
  case "primitive":
179
- case "enum":
180
239
  case "dynamic":
181
240
  case "custom":
182
241
  return type;
@@ -255,11 +314,10 @@ function getDisplayName(metadata) {
255
314
  return metadata?.displayName?.value;
256
315
  }
257
316
  function resolveFormIRMetadata(ir, options) {
258
- const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
259
- const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
317
+ const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
260
318
  surface: options.surface,
261
319
  declarationKind: "type",
262
- logicalName: rootLogicalName,
320
+ logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
263
321
  ...options.buildContext !== void 0 && { buildContext: options.buildContext }
264
322
  });
265
323
  return {
@@ -295,7 +353,7 @@ function canonicalizeChainDSL(form, options) {
295
353
  const metadataPolicy = normalizeMetadataPolicy(
296
354
  options?.metadata ?? _getFormSpecMetadataPolicy(form)
297
355
  );
298
- return {
356
+ const ir = {
299
357
  kind: "form-ir",
300
358
  irVersion: IR_VERSION,
301
359
  elements: canonicalizeElements(form.elements, metadataPolicy),
@@ -303,6 +361,13 @@ function canonicalizeChainDSL(form, options) {
303
361
  typeRegistry: {},
304
362
  provenance: CHAIN_DSL_PROVENANCE
305
363
  };
364
+ return resolveFormIRMetadata(ir, {
365
+ policy: metadataPolicy,
366
+ surface: "chain-dsl",
367
+ // Chain DSL has no root/type-metadata authoring surface, so only resolve
368
+ // field/type-registry metadata and enum-member labels here.
369
+ resolveRootTypeMetadata: false
370
+ });
306
371
  }
307
372
  function canonicalizeElements(elements, metadataPolicy) {
308
373
  return elements.map((element) => canonicalizeElement(element, metadataPolicy));
@@ -4171,17 +4236,25 @@ function assertNoSerializedNameCollisions(ir) {
4171
4236
  // src/json-schema/ir-generator.ts
4172
4237
  function makeContext(options) {
4173
4238
  const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
4239
+ const rawEnumSerialization = options?.enumSerialization;
4174
4240
  if (!vendorPrefix.startsWith("x-")) {
4175
4241
  throw new Error(
4176
4242
  `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
4177
4243
  );
4178
4244
  }
4245
+ if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
4246
+ throw new Error(
4247
+ `Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
4248
+ );
4249
+ }
4250
+ const enumSerialization = rawEnumSerialization ?? "enum";
4179
4251
  return {
4180
4252
  defs: {},
4181
4253
  typeNameMap: {},
4182
4254
  typeRegistry: {},
4183
4255
  extensionRegistry: options?.extensionRegistry,
4184
- vendorPrefix
4256
+ vendorPrefix,
4257
+ enumSerialization
4185
4258
  };
4186
4259
  }
4187
4260
  function generateJsonSchemaFromIR(ir, options) {
@@ -4355,7 +4428,7 @@ function generateTypeNode(type, ctx) {
4355
4428
  case "primitive":
4356
4429
  return generatePrimitiveType(type);
4357
4430
  case "enum":
4358
- return generateEnumType(type);
4431
+ return generateEnumType(type, ctx);
4359
4432
  case "array":
4360
4433
  return generateArrayType(type, ctx);
4361
4434
  case "object":
@@ -4381,20 +4454,37 @@ function generatePrimitiveType(type) {
4381
4454
  type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
4382
4455
  };
4383
4456
  }
4384
- function generateEnumType(type) {
4385
- const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
4386
- if (hasDisplayNames) {
4457
+ function generateEnumType(type, ctx) {
4458
+ if (ctx.enumSerialization === "oneOf") {
4387
4459
  return {
4388
- oneOf: type.members.map((m) => {
4389
- const entry = { const: m.value };
4390
- if (m.displayName !== void 0) {
4391
- entry.title = m.displayName;
4392
- }
4393
- return entry;
4394
- })
4460
+ oneOf: type.members.map((m) => ({
4461
+ const: m.value,
4462
+ title: m.displayName ?? String(m.value)
4463
+ }))
4395
4464
  };
4396
4465
  }
4397
- return { enum: type.members.map((m) => m.value) };
4466
+ const schema = { enum: type.members.map((m) => m.value) };
4467
+ const displayNames = buildEnumDisplayNameExtension(type);
4468
+ if (displayNames !== void 0) {
4469
+ schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
4470
+ }
4471
+ return schema;
4472
+ }
4473
+ function buildEnumDisplayNameExtension(type) {
4474
+ if (!type.members.some((member) => member.displayName !== void 0)) {
4475
+ return void 0;
4476
+ }
4477
+ const displayNames = /* @__PURE__ */ Object.create(null);
4478
+ for (const member of type.members) {
4479
+ const key = String(member.value);
4480
+ if (Object.hasOwn(displayNames, key)) {
4481
+ throw new Error(
4482
+ `Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
4483
+ );
4484
+ }
4485
+ displayNames[key] = member.displayName ?? key;
4486
+ }
4487
+ return displayNames;
4398
4488
  }
4399
4489
  function generateArrayType(type, ctx) {
4400
4490
  return {
@@ -4743,6 +4833,66 @@ function generateCustomType(type, ctx) {
4743
4833
  }
4744
4834
  return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
4745
4835
  }
4836
+ var JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
4837
+ "$schema",
4838
+ "$ref",
4839
+ "$defs",
4840
+ "$id",
4841
+ "$anchor",
4842
+ "$dynamicRef",
4843
+ "$dynamicAnchor",
4844
+ "$vocabulary",
4845
+ "$comment",
4846
+ "type",
4847
+ "enum",
4848
+ "const",
4849
+ "properties",
4850
+ "patternProperties",
4851
+ "additionalProperties",
4852
+ "required",
4853
+ "items",
4854
+ "prefixItems",
4855
+ "additionalItems",
4856
+ "contains",
4857
+ "allOf",
4858
+ "oneOf",
4859
+ "anyOf",
4860
+ "not",
4861
+ "if",
4862
+ "then",
4863
+ "else",
4864
+ "minimum",
4865
+ "maximum",
4866
+ "exclusiveMinimum",
4867
+ "exclusiveMaximum",
4868
+ "multipleOf",
4869
+ "minLength",
4870
+ "maxLength",
4871
+ "pattern",
4872
+ "minItems",
4873
+ "maxItems",
4874
+ "uniqueItems",
4875
+ "minProperties",
4876
+ "maxProperties",
4877
+ "minContains",
4878
+ "maxContains",
4879
+ "format",
4880
+ "title",
4881
+ "description",
4882
+ "default",
4883
+ "deprecated",
4884
+ "readOnly",
4885
+ "writeOnly",
4886
+ "examples",
4887
+ "dependentRequired",
4888
+ "dependentSchemas",
4889
+ "propertyNames",
4890
+ "unevaluatedItems",
4891
+ "unevaluatedProperties",
4892
+ "contentEncoding",
4893
+ "contentMediaType",
4894
+ "contentSchema"
4895
+ ]);
4746
4896
  function applyCustomConstraint(schema, constraint, ctx) {
4747
4897
  const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
4748
4898
  if (registration === void 0) {
@@ -4750,12 +4900,25 @@ function applyCustomConstraint(schema, constraint, ctx) {
4750
4900
  `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
4751
4901
  );
4752
4902
  }
4753
- assignVendorPrefixedExtensionKeywords(
4754
- schema,
4755
- registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
4756
- ctx.vendorPrefix,
4757
- `custom constraint "${constraint.constraintId}"`
4758
- );
4903
+ const extensionSchema = registration.toJsonSchema(constraint.payload, ctx.vendorPrefix);
4904
+ if (registration.emitsVocabularyKeywords) {
4905
+ const target = schema;
4906
+ for (const [key, value] of Object.entries(extensionSchema)) {
4907
+ if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
4908
+ throw new Error(
4909
+ `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
4910
+ );
4911
+ }
4912
+ target[key] = value;
4913
+ }
4914
+ } else {
4915
+ assignVendorPrefixedExtensionKeywords(
4916
+ schema,
4917
+ extensionSchema,
4918
+ ctx.vendorPrefix,
4919
+ `custom constraint "${constraint.constraintId}"`
4920
+ );
4921
+ }
4759
4922
  }
4760
4923
  function applyCustomAnnotation(schema, annotation, ctx) {
4761
4924
  const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
@@ -5319,7 +5482,7 @@ function createExtensionRegistry(extensions) {
5319
5482
 
5320
5483
  // src/generators/method-schema.ts
5321
5484
  import { IR_VERSION as IR_VERSION3 } from "@formspec/core/internals";
5322
- function typeToJsonSchema(type, checker) {
5485
+ function typeToJsonSchema(type, checker, options) {
5323
5486
  const typeRegistry = {};
5324
5487
  const visiting = /* @__PURE__ */ new Set();
5325
5488
  const diagnostics = [];
@@ -5359,7 +5522,10 @@ function typeToJsonSchema(type, checker) {
5359
5522
  typeRegistry,
5360
5523
  provenance: fieldProvenance
5361
5524
  };
5362
- const schema = generateJsonSchemaFromIR(ir);
5525
+ const schema = generateJsonSchemaFromIR(
5526
+ ir,
5527
+ options?.enumSerialization === void 0 ? void 0 : { enumSerialization: options.enumSerialization }
5528
+ );
5363
5529
  const fieldSchema = schema.properties?.["__result"];
5364
5530
  if (fieldSchema) {
5365
5531
  if (schema.$defs && Object.keys(schema.$defs).length > 0) {
@@ -5369,16 +5535,16 @@ function typeToJsonSchema(type, checker) {
5369
5535
  }
5370
5536
  return { type: "object" };
5371
5537
  }
5372
- function generateMethodSchemas(method, checker, loadedFormSpecs) {
5373
- const returnType = typeToJsonSchema(method.returnType, checker);
5374
- const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
5538
+ function generateMethodSchemas(method, checker, loadedFormSpecs, options) {
5539
+ const returnType = typeToJsonSchema(method.returnType, checker, options);
5540
+ const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs, options);
5375
5541
  return {
5376
5542
  name: method.name,
5377
5543
  params,
5378
5544
  returnType
5379
5545
  };
5380
5546
  }
5381
- function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5547
+ function generateParamsSchemas(parameters, checker, loadedFormSpecs, options) {
5382
5548
  if (parameters.length === 0) {
5383
5549
  return null;
5384
5550
  }
@@ -5399,7 +5565,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5399
5565
  }
5400
5566
  if (parameters.length === 1 && parameters[0]) {
5401
5567
  const param = parameters[0];
5402
- const jsonSchema = typeToJsonSchema(param.type, checker);
5568
+ const jsonSchema = typeToJsonSchema(param.type, checker, options);
5403
5569
  return {
5404
5570
  jsonSchema,
5405
5571
  uiSchema: null,
@@ -5409,7 +5575,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
5409
5575
  const properties = {};
5410
5576
  const required = [];
5411
5577
  for (const param of parameters) {
5412
- const paramSchema = typeToJsonSchema(param.type, checker);
5578
+ const paramSchema = typeToJsonSchema(param.type, checker, options);
5413
5579
  properties[param.name] = paramSchema;
5414
5580
  if (!param.optional) {
5415
5581
  required.push(param.name);