@formspec/build 0.1.0-alpha.14 → 0.1.0-alpha.15

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.
Files changed (60) hide show
  1. package/dist/__tests__/extension-runtime.integration.test.d.ts +2 -0
  2. package/dist/__tests__/extension-runtime.integration.test.d.ts.map +1 -0
  3. package/dist/__tests__/fixtures/edge-cases.d.ts +11 -0
  4. package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -1
  5. package/dist/__tests__/fixtures/example-a-builtins.d.ts +6 -6
  6. package/dist/__tests__/fixtures/example-interface-types.d.ts +26 -26
  7. package/dist/__tests__/fixtures/example-interface-types.d.ts.map +1 -1
  8. package/dist/__tests__/jsdoc-constraints.test.d.ts +4 -5
  9. package/dist/__tests__/jsdoc-constraints.test.d.ts.map +1 -1
  10. package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts +19 -0
  11. package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts.map +1 -0
  12. package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts +6 -0
  13. package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts.map +1 -0
  14. package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts +17 -0
  15. package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts.map +1 -0
  16. package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts +9 -0
  17. package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts.map +1 -0
  18. package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts +6 -0
  19. package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts.map +1 -0
  20. package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts +19 -0
  21. package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts.map +1 -0
  22. package/dist/__tests__/parity/utils.d.ts +6 -1
  23. package/dist/__tests__/parity/utils.d.ts.map +1 -1
  24. package/dist/analyzer/class-analyzer.d.ts +1 -1
  25. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  26. package/dist/analyzer/jsdoc-constraints.d.ts +7 -51
  27. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  28. package/dist/analyzer/tsdoc-parser.d.ts +6 -8
  29. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  30. package/dist/browser.cjs +387 -98
  31. package/dist/browser.cjs.map +1 -1
  32. package/dist/browser.d.ts +15 -2
  33. package/dist/browser.d.ts.map +1 -1
  34. package/dist/browser.js +385 -98
  35. package/dist/browser.js.map +1 -1
  36. package/dist/build.d.ts +131 -5
  37. package/dist/canonicalize/tsdoc-canonicalizer.d.ts +3 -3
  38. package/dist/cli.cjs +272 -69
  39. package/dist/cli.cjs.map +1 -1
  40. package/dist/cli.js +271 -72
  41. package/dist/cli.js.map +1 -1
  42. package/dist/index.cjs +257 -67
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.ts +20 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +255 -71
  47. package/dist/index.js.map +1 -1
  48. package/dist/internals.cjs +461 -137
  49. package/dist/internals.cjs.map +1 -1
  50. package/dist/internals.js +459 -139
  51. package/dist/internals.js.map +1 -1
  52. package/dist/json-schema/generator.d.ts +8 -2
  53. package/dist/json-schema/generator.d.ts.map +1 -1
  54. package/dist/json-schema/ir-generator.d.ts +24 -2
  55. package/dist/json-schema/ir-generator.d.ts.map +1 -1
  56. package/dist/json-schema/types.d.ts +1 -1
  57. package/dist/json-schema/types.d.ts.map +1 -1
  58. package/dist/validate/constraint-validator.d.ts +3 -7
  59. package/dist/validate/constraint-validator.d.ts.map +1 -1
  60. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -34,7 +34,9 @@ __export(index_exports, {
34
34
  categorizationSchema: () => categorizationSchema,
35
35
  categorySchema: () => categorySchema,
36
36
  controlSchema: () => controlSchema,
37
+ createExtensionRegistry: () => createExtensionRegistry,
37
38
  generateJsonSchema: () => generateJsonSchema,
39
+ generateJsonSchemaFromIR: () => generateJsonSchemaFromIR,
38
40
  generateSchemas: () => generateSchemas,
39
41
  generateSchemasFromClass: () => generateSchemasFromClass,
40
42
  generateUiSchema: () => generateUiSchema,
@@ -236,7 +238,7 @@ function canonicalizeArrayField(field) {
236
238
  const itemsType = {
237
239
  kind: "object",
238
240
  properties: itemProperties,
239
- additionalProperties: false
241
+ additionalProperties: true
240
242
  };
241
243
  const type = { kind: "array", items: itemsType };
242
244
  const constraints = [];
@@ -271,7 +273,7 @@ function canonicalizeObjectField(field) {
271
273
  const type = {
272
274
  kind: "object",
273
275
  properties,
274
- additionalProperties: false
276
+ additionalProperties: true
275
277
  };
276
278
  return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
277
279
  }
@@ -443,11 +445,21 @@ function wrapInConditional(field, layout, provenance) {
443
445
  }
444
446
 
445
447
  // src/json-schema/ir-generator.ts
446
- function makeContext() {
447
- return { defs: {} };
448
+ function makeContext(options) {
449
+ const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
450
+ if (!vendorPrefix.startsWith("x-")) {
451
+ throw new Error(
452
+ `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
453
+ );
454
+ }
455
+ return {
456
+ defs: {},
457
+ extensionRegistry: options?.extensionRegistry,
458
+ vendorPrefix
459
+ };
448
460
  }
449
- function generateJsonSchemaFromIR(ir) {
450
- const ctx = makeContext();
461
+ function generateJsonSchemaFromIR(ir, options) {
462
+ const ctx = makeContext(options);
451
463
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
452
464
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
453
465
  }
@@ -499,16 +511,16 @@ function generateFieldSchema(field, ctx) {
499
511
  directConstraints.push(c);
500
512
  }
501
513
  }
502
- applyConstraints(schema, directConstraints);
503
- applyAnnotations(schema, field.annotations);
514
+ applyConstraints(schema, directConstraints, ctx);
515
+ applyAnnotations(schema, field.annotations, ctx);
504
516
  if (pathConstraints.length === 0) {
505
517
  return schema;
506
518
  }
507
- return applyPathTargetedConstraints(schema, pathConstraints);
519
+ return applyPathTargetedConstraints(schema, pathConstraints, ctx);
508
520
  }
509
- function applyPathTargetedConstraints(schema, pathConstraints) {
521
+ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
510
522
  if (schema.type === "array" && schema.items) {
511
- schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
523
+ schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
512
524
  return schema;
513
525
  }
514
526
  const byTarget = /* @__PURE__ */ new Map();
@@ -522,7 +534,7 @@ function applyPathTargetedConstraints(schema, pathConstraints) {
522
534
  const propertyOverrides = {};
523
535
  for (const [target, constraints] of byTarget) {
524
536
  const subSchema = {};
525
- applyConstraints(subSchema, constraints);
537
+ applyConstraints(subSchema, constraints, ctx);
526
538
  propertyOverrides[target] = subSchema;
527
539
  }
528
540
  if (schema.$ref) {
@@ -566,6 +578,8 @@ function generateTypeNode(type, ctx) {
566
578
  return generateArrayType(type, ctx);
567
579
  case "object":
568
580
  return generateObjectType(type, ctx);
581
+ case "record":
582
+ return generateRecordType(type, ctx);
569
583
  case "union":
570
584
  return generateUnionType(type, ctx);
571
585
  case "reference":
@@ -573,7 +587,7 @@ function generateTypeNode(type, ctx) {
573
587
  case "dynamic":
574
588
  return generateDynamicType(type);
575
589
  case "custom":
576
- return generateCustomType(type);
590
+ return generateCustomType(type, ctx);
577
591
  default: {
578
592
  const _exhaustive = type;
579
593
  return _exhaustive;
@@ -622,16 +636,27 @@ function generateObjectType(type, ctx) {
622
636
  }
623
637
  return schema;
624
638
  }
639
+ function generateRecordType(type, ctx) {
640
+ return {
641
+ type: "object",
642
+ additionalProperties: generateTypeNode(type.valueType, ctx)
643
+ };
644
+ }
625
645
  function generatePropertySchema(prop, ctx) {
626
646
  const schema = generateTypeNode(prop.type, ctx);
627
- applyConstraints(schema, prop.constraints);
628
- applyAnnotations(schema, prop.annotations);
647
+ applyConstraints(schema, prop.constraints, ctx);
648
+ applyAnnotations(schema, prop.annotations, ctx);
629
649
  return schema;
630
650
  }
631
651
  function generateUnionType(type, ctx) {
632
652
  if (isBooleanUnion(type)) {
633
653
  return { type: "boolean" };
634
654
  }
655
+ if (isNullableUnion(type)) {
656
+ return {
657
+ oneOf: type.members.map((m) => generateTypeNode(m, ctx))
658
+ };
659
+ }
635
660
  return {
636
661
  anyOf: type.members.map((m) => generateTypeNode(m, ctx))
637
662
  };
@@ -641,6 +666,13 @@ function isBooleanUnion(type) {
641
666
  const kinds = type.members.map((m) => m.kind);
642
667
  return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
643
668
  }
669
+ function isNullableUnion(type) {
670
+ if (type.members.length !== 2) return false;
671
+ const nullCount = type.members.filter(
672
+ (m) => m.kind === "primitive" && m.primitiveKind === "null"
673
+ ).length;
674
+ return nullCount === 1;
675
+ }
644
676
  function generateReferenceType(type) {
645
677
  return { $ref: `#/$defs/${type.name}` };
646
678
  }
@@ -661,10 +693,7 @@ function generateDynamicType(type) {
661
693
  "x-formspec-schemaSource": type.sourceKey
662
694
  };
663
695
  }
664
- function generateCustomType(_type) {
665
- return { type: "object" };
666
- }
667
- function applyConstraints(schema, constraints) {
696
+ function applyConstraints(schema, constraints, ctx) {
668
697
  for (const constraint of constraints) {
669
698
  switch (constraint.constraintKind) {
670
699
  case "minimum":
@@ -709,6 +738,7 @@ function applyConstraints(schema, constraints) {
709
738
  case "allowedMembers":
710
739
  break;
711
740
  case "custom":
741
+ applyCustomConstraint(schema, constraint, ctx);
712
742
  break;
713
743
  default: {
714
744
  const _exhaustive = constraint;
@@ -717,7 +747,7 @@ function applyConstraints(schema, constraints) {
717
747
  }
718
748
  }
719
749
  }
720
- function applyAnnotations(schema, annotations) {
750
+ function applyAnnotations(schema, annotations, ctx) {
721
751
  for (const annotation of annotations) {
722
752
  switch (annotation.annotationKind) {
723
753
  case "displayName":
@@ -737,6 +767,7 @@ function applyAnnotations(schema, annotations) {
737
767
  case "formatHint":
738
768
  break;
739
769
  case "custom":
770
+ applyCustomAnnotation(schema, annotation, ctx);
740
771
  break;
741
772
  default: {
742
773
  const _exhaustive = annotation;
@@ -745,11 +776,41 @@ function applyAnnotations(schema, annotations) {
745
776
  }
746
777
  }
747
778
  }
779
+ function generateCustomType(type, ctx) {
780
+ const registration = ctx.extensionRegistry?.findType(type.typeId);
781
+ if (registration === void 0) {
782
+ throw new Error(
783
+ `Cannot generate JSON Schema for custom type "${type.typeId}" without a matching extension registration`
784
+ );
785
+ }
786
+ return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
787
+ }
788
+ function applyCustomConstraint(schema, constraint, ctx) {
789
+ const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
790
+ if (registration === void 0) {
791
+ throw new Error(
792
+ `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
793
+ );
794
+ }
795
+ Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
796
+ }
797
+ function applyCustomAnnotation(schema, annotation, ctx) {
798
+ const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
799
+ if (registration === void 0) {
800
+ throw new Error(
801
+ `Cannot generate JSON Schema for custom annotation "${annotation.annotationId}" without a matching extension registration`
802
+ );
803
+ }
804
+ if (registration.toJsonSchema === void 0) {
805
+ return;
806
+ }
807
+ Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
808
+ }
748
809
 
749
810
  // src/json-schema/generator.ts
750
- function generateJsonSchema(form) {
811
+ function generateJsonSchema(form, options) {
751
812
  const ir = canonicalizeChainDSL(form);
752
- return generateJsonSchemaFromIR(ir);
813
+ return generateJsonSchemaFromIR(ir, options);
753
814
  }
754
815
 
755
816
  // src/ui-schema/schema.ts
@@ -983,6 +1044,48 @@ function getSchemaExtension(schema, key) {
983
1044
  return schema[key];
984
1045
  }
985
1046
 
1047
+ // src/extensions/registry.ts
1048
+ function createExtensionRegistry(extensions) {
1049
+ const typeMap = /* @__PURE__ */ new Map();
1050
+ const constraintMap = /* @__PURE__ */ new Map();
1051
+ const annotationMap = /* @__PURE__ */ new Map();
1052
+ for (const ext of extensions) {
1053
+ if (ext.types !== void 0) {
1054
+ for (const type of ext.types) {
1055
+ const qualifiedId = `${ext.extensionId}/${type.typeName}`;
1056
+ if (typeMap.has(qualifiedId)) {
1057
+ throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1058
+ }
1059
+ typeMap.set(qualifiedId, type);
1060
+ }
1061
+ }
1062
+ if (ext.constraints !== void 0) {
1063
+ for (const constraint of ext.constraints) {
1064
+ const qualifiedId = `${ext.extensionId}/${constraint.constraintName}`;
1065
+ if (constraintMap.has(qualifiedId)) {
1066
+ throw new Error(`Duplicate custom constraint ID: "${qualifiedId}"`);
1067
+ }
1068
+ constraintMap.set(qualifiedId, constraint);
1069
+ }
1070
+ }
1071
+ if (ext.annotations !== void 0) {
1072
+ for (const annotation of ext.annotations) {
1073
+ const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
1074
+ if (annotationMap.has(qualifiedId)) {
1075
+ throw new Error(`Duplicate custom annotation ID: "${qualifiedId}"`);
1076
+ }
1077
+ annotationMap.set(qualifiedId, annotation);
1078
+ }
1079
+ }
1080
+ }
1081
+ return {
1082
+ extensions,
1083
+ findType: (typeId) => typeMap.get(typeId),
1084
+ findConstraint: (constraintId) => constraintMap.get(constraintId),
1085
+ findAnnotation: (annotationId) => annotationMap.get(annotationId)
1086
+ };
1087
+ }
1088
+
986
1089
  // src/json-schema/schema.ts
987
1090
  var import_zod3 = require("zod");
988
1091
  var jsonSchemaTypeSchema = import_zod3.z.enum([
@@ -1122,7 +1225,6 @@ var ts4 = __toESM(require("typescript"), 1);
1122
1225
 
1123
1226
  // src/analyzer/jsdoc-constraints.ts
1124
1227
  var ts3 = __toESM(require("typescript"), 1);
1125
- var import_core4 = require("@formspec/core");
1126
1228
 
1127
1229
  // src/analyzer/tsdoc-parser.ts
1128
1230
  var ts2 = __toESM(require("typescript"), 1);
@@ -1164,6 +1266,15 @@ function createFormSpecTSDocConfig() {
1164
1266
  })
1165
1267
  );
1166
1268
  }
1269
+ for (const tagName of ["displayName", "description"]) {
1270
+ config.addTagDefinition(
1271
+ new import_tsdoc.TSDocTagDefinition({
1272
+ tagName: "@" + tagName,
1273
+ syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
1274
+ allowMultiple: true
1275
+ })
1276
+ );
1277
+ }
1167
1278
  return config;
1168
1279
  }
1169
1280
  var sharedParser;
@@ -1193,6 +1304,27 @@ function parseTSDocTags(node, file = "") {
1193
1304
  const docComment = parserContext.docComment;
1194
1305
  for (const block of docComment.customBlocks) {
1195
1306
  const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
1307
+ if (tagName === "displayName" || tagName === "description") {
1308
+ const text2 = extractBlockText(block).trim();
1309
+ if (text2 === "") continue;
1310
+ const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
1311
+ if (tagName === "displayName") {
1312
+ annotations.push({
1313
+ kind: "annotation",
1314
+ annotationKind: "displayName",
1315
+ value: text2,
1316
+ provenance: provenance2
1317
+ });
1318
+ } else {
1319
+ annotations.push({
1320
+ kind: "annotation",
1321
+ annotationKind: "description",
1322
+ value: text2,
1323
+ provenance: provenance2
1324
+ });
1325
+ }
1326
+ continue;
1327
+ }
1196
1328
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
1197
1329
  const text = extractBlockText(block).trim();
1198
1330
  if (text === "") continue;
@@ -1224,41 +1356,6 @@ function parseTSDocTags(node, file = "") {
1224
1356
  constraints.push(constraintNode);
1225
1357
  }
1226
1358
  }
1227
- let displayName;
1228
- let description;
1229
- let displayNameTag;
1230
- let descriptionTag;
1231
- for (const tag of jsDocTagsAll) {
1232
- const tagName = tag.tagName.text;
1233
- const commentText = getTagCommentText(tag);
1234
- if (commentText === void 0 || commentText.trim() === "") {
1235
- continue;
1236
- }
1237
- const trimmed = commentText.trim();
1238
- if (tagName === "Field_displayName") {
1239
- displayName = trimmed;
1240
- displayNameTag = tag;
1241
- } else if (tagName === "Field_description") {
1242
- description = trimmed;
1243
- descriptionTag = tag;
1244
- }
1245
- }
1246
- if (displayName !== void 0 && displayNameTag) {
1247
- annotations.push({
1248
- kind: "annotation",
1249
- annotationKind: "displayName",
1250
- value: displayName,
1251
- provenance: provenanceForJSDocTag(displayNameTag, file)
1252
- });
1253
- }
1254
- if (description !== void 0 && descriptionTag) {
1255
- annotations.push({
1256
- kind: "annotation",
1257
- annotationKind: "description",
1258
- value: description,
1259
- provenance: provenanceForJSDocTag(descriptionTag, file)
1260
- });
1261
- }
1262
1359
  return { constraints, annotations };
1263
1360
  }
1264
1361
  function extractPathTarget(text) {
@@ -1523,18 +1620,19 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1523
1620
  const tsType = checker.getTypeAtLocation(prop);
1524
1621
  const optional = prop.questionToken !== void 0;
1525
1622
  const provenance = provenanceForNode(prop, file);
1526
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1623
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1527
1624
  const constraints = [];
1528
1625
  if (prop.type) {
1529
1626
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1530
1627
  }
1531
1628
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1532
- const annotations = [];
1629
+ let annotations = [];
1533
1630
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1534
1631
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1535
1632
  if (defaultAnnotation) {
1536
1633
  annotations.push(defaultAnnotation);
1537
1634
  }
1635
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1538
1636
  return {
1539
1637
  kind: "field",
1540
1638
  name,
@@ -1553,14 +1651,15 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1553
1651
  const tsType = checker.getTypeAtLocation(prop);
1554
1652
  const optional = prop.questionToken !== void 0;
1555
1653
  const provenance = provenanceForNode(prop, file);
1556
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1654
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1557
1655
  const constraints = [];
1558
1656
  if (prop.type) {
1559
1657
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1560
1658
  }
1561
1659
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1562
- const annotations = [];
1660
+ let annotations = [];
1563
1661
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1662
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1564
1663
  return {
1565
1664
  kind: "field",
1566
1665
  name,
@@ -1571,6 +1670,68 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1571
1670
  provenance
1572
1671
  };
1573
1672
  }
1673
+ function applyEnumMemberDisplayNames(type, annotations) {
1674
+ if (!annotations.some(
1675
+ (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
1676
+ )) {
1677
+ return { type, annotations: [...annotations] };
1678
+ }
1679
+ const consumed = /* @__PURE__ */ new Set();
1680
+ const nextType = rewriteEnumDisplayNames(type, annotations, consumed);
1681
+ if (consumed.size === 0) {
1682
+ return { type, annotations: [...annotations] };
1683
+ }
1684
+ return {
1685
+ type: nextType,
1686
+ annotations: annotations.filter((annotation) => !consumed.has(annotation))
1687
+ };
1688
+ }
1689
+ function rewriteEnumDisplayNames(type, annotations, consumed) {
1690
+ switch (type.kind) {
1691
+ case "enum":
1692
+ return applyEnumMemberDisplayNamesToEnum(type, annotations, consumed);
1693
+ case "union": {
1694
+ return {
1695
+ ...type,
1696
+ members: type.members.map(
1697
+ (member) => rewriteEnumDisplayNames(member, annotations, consumed)
1698
+ )
1699
+ };
1700
+ }
1701
+ default:
1702
+ return type;
1703
+ }
1704
+ }
1705
+ function applyEnumMemberDisplayNamesToEnum(type, annotations, consumed) {
1706
+ const displayNames = /* @__PURE__ */ new Map();
1707
+ for (const annotation of annotations) {
1708
+ if (annotation.annotationKind !== "displayName") continue;
1709
+ const parsed = parseEnumMemberDisplayName(annotation.value);
1710
+ if (!parsed) continue;
1711
+ consumed.add(annotation);
1712
+ const member = type.members.find((m) => String(m.value) === parsed.value);
1713
+ if (!member) continue;
1714
+ displayNames.set(String(member.value), parsed.label);
1715
+ }
1716
+ if (displayNames.size === 0) {
1717
+ return type;
1718
+ }
1719
+ return {
1720
+ ...type,
1721
+ members: type.members.map((member) => {
1722
+ const displayName = displayNames.get(String(member.value));
1723
+ return displayName !== void 0 ? { ...member, displayName } : member;
1724
+ })
1725
+ };
1726
+ }
1727
+ function parseEnumMemberDisplayName(value) {
1728
+ const trimmed = value.trim();
1729
+ const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(trimmed);
1730
+ if (!match?.[1] || !match[2]) return null;
1731
+ const label = match[2].trim();
1732
+ if (label === "") return null;
1733
+ return { value: match[1], label };
1734
+ }
1574
1735
  function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
1575
1736
  if (type.flags & ts4.TypeFlags.String) {
1576
1737
  return { kind: "primitive", primitiveKind: "string" };
@@ -1681,7 +1842,30 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting) {
1681
1842
  const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
1682
1843
  return { kind: "array", items };
1683
1844
  }
1845
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
1846
+ if (type.getProperties().length > 0) {
1847
+ return null;
1848
+ }
1849
+ const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
1850
+ if (!indexInfo) {
1851
+ return null;
1852
+ }
1853
+ if (visiting.has(type)) {
1854
+ return null;
1855
+ }
1856
+ visiting.add(type);
1857
+ try {
1858
+ const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
1859
+ return { kind: "record", valueType };
1860
+ } finally {
1861
+ visiting.delete(type);
1862
+ }
1863
+ }
1684
1864
  function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1865
+ const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
1866
+ if (recordNode) {
1867
+ return recordNode;
1868
+ }
1685
1869
  if (visiting.has(type)) {
1686
1870
  return { kind: "object", properties: [], additionalProperties: false };
1687
1871
  }
@@ -1713,7 +1897,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1713
1897
  const objectNode = {
1714
1898
  kind: "object",
1715
1899
  properties,
1716
- additionalProperties: false
1900
+ additionalProperties: true
1717
1901
  };
1718
1902
  if (typeName) {
1719
1903
  typeRegistry[typeName] = {
@@ -1916,15 +2100,19 @@ function generateSchemas(options) {
1916
2100
  }
1917
2101
 
1918
2102
  // src/index.ts
1919
- function buildFormSchemas(form) {
2103
+ function buildFormSchemas(form, options) {
1920
2104
  return {
1921
- jsonSchema: generateJsonSchema(form),
2105
+ jsonSchema: generateJsonSchema(form, options),
1922
2106
  uiSchema: generateUiSchema(form)
1923
2107
  };
1924
2108
  }
1925
2109
  function writeSchemas(form, options) {
1926
- const { outDir, name = "schema", indent = 2 } = options;
1927
- const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form);
2110
+ const { outDir, name = "schema", indent = 2, extensionRegistry, vendorPrefix } = options;
2111
+ const buildOptions = extensionRegistry === void 0 && vendorPrefix === void 0 ? void 0 : {
2112
+ extensionRegistry,
2113
+ vendorPrefix
2114
+ };
2115
+ const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
1928
2116
  if (!fs.existsSync(outDir)) {
1929
2117
  fs.mkdirSync(outDir, { recursive: true });
1930
2118
  }
@@ -1940,7 +2128,9 @@ function writeSchemas(form, options) {
1940
2128
  categorizationSchema,
1941
2129
  categorySchema,
1942
2130
  controlSchema,
2131
+ createExtensionRegistry,
1943
2132
  generateJsonSchema,
2133
+ generateJsonSchemaFromIR,
1944
2134
  generateSchemas,
1945
2135
  generateSchemasFromClass,
1946
2136
  generateUiSchema,