@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/cli.cjs CHANGED
@@ -202,7 +202,7 @@ function canonicalizeArrayField(field) {
202
202
  const itemsType = {
203
203
  kind: "object",
204
204
  properties: itemProperties,
205
- additionalProperties: false
205
+ additionalProperties: true
206
206
  };
207
207
  const type = { kind: "array", items: itemsType };
208
208
  const constraints = [];
@@ -237,7 +237,7 @@ function canonicalizeObjectField(field) {
237
237
  const type = {
238
238
  kind: "object",
239
239
  properties,
240
- additionalProperties: false
240
+ additionalProperties: true
241
241
  };
242
242
  return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
243
243
  }
@@ -437,11 +437,21 @@ var init_canonicalize = __esm({
437
437
  });
438
438
 
439
439
  // src/json-schema/ir-generator.ts
440
- function makeContext() {
441
- return { defs: {} };
440
+ function makeContext(options) {
441
+ const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
442
+ if (!vendorPrefix.startsWith("x-")) {
443
+ throw new Error(
444
+ `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
445
+ );
446
+ }
447
+ return {
448
+ defs: {},
449
+ extensionRegistry: options?.extensionRegistry,
450
+ vendorPrefix
451
+ };
442
452
  }
443
- function generateJsonSchemaFromIR(ir) {
444
- const ctx = makeContext();
453
+ function generateJsonSchemaFromIR(ir, options) {
454
+ const ctx = makeContext(options);
445
455
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
446
456
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
447
457
  }
@@ -493,16 +503,16 @@ function generateFieldSchema(field, ctx) {
493
503
  directConstraints.push(c);
494
504
  }
495
505
  }
496
- applyConstraints(schema, directConstraints);
497
- applyAnnotations(schema, field.annotations);
506
+ applyConstraints(schema, directConstraints, ctx);
507
+ applyAnnotations(schema, field.annotations, ctx);
498
508
  if (pathConstraints.length === 0) {
499
509
  return schema;
500
510
  }
501
- return applyPathTargetedConstraints(schema, pathConstraints);
511
+ return applyPathTargetedConstraints(schema, pathConstraints, ctx);
502
512
  }
503
- function applyPathTargetedConstraints(schema, pathConstraints) {
513
+ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
504
514
  if (schema.type === "array" && schema.items) {
505
- schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
515
+ schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
506
516
  return schema;
507
517
  }
508
518
  const byTarget = /* @__PURE__ */ new Map();
@@ -516,7 +526,7 @@ function applyPathTargetedConstraints(schema, pathConstraints) {
516
526
  const propertyOverrides = {};
517
527
  for (const [target, constraints] of byTarget) {
518
528
  const subSchema = {};
519
- applyConstraints(subSchema, constraints);
529
+ applyConstraints(subSchema, constraints, ctx);
520
530
  propertyOverrides[target] = subSchema;
521
531
  }
522
532
  if (schema.$ref) {
@@ -560,6 +570,8 @@ function generateTypeNode(type, ctx) {
560
570
  return generateArrayType(type, ctx);
561
571
  case "object":
562
572
  return generateObjectType(type, ctx);
573
+ case "record":
574
+ return generateRecordType(type, ctx);
563
575
  case "union":
564
576
  return generateUnionType(type, ctx);
565
577
  case "reference":
@@ -567,7 +579,7 @@ function generateTypeNode(type, ctx) {
567
579
  case "dynamic":
568
580
  return generateDynamicType(type);
569
581
  case "custom":
570
- return generateCustomType(type);
582
+ return generateCustomType(type, ctx);
571
583
  default: {
572
584
  const _exhaustive = type;
573
585
  return _exhaustive;
@@ -616,16 +628,27 @@ function generateObjectType(type, ctx) {
616
628
  }
617
629
  return schema;
618
630
  }
631
+ function generateRecordType(type, ctx) {
632
+ return {
633
+ type: "object",
634
+ additionalProperties: generateTypeNode(type.valueType, ctx)
635
+ };
636
+ }
619
637
  function generatePropertySchema(prop, ctx) {
620
638
  const schema = generateTypeNode(prop.type, ctx);
621
- applyConstraints(schema, prop.constraints);
622
- applyAnnotations(schema, prop.annotations);
639
+ applyConstraints(schema, prop.constraints, ctx);
640
+ applyAnnotations(schema, prop.annotations, ctx);
623
641
  return schema;
624
642
  }
625
643
  function generateUnionType(type, ctx) {
626
644
  if (isBooleanUnion(type)) {
627
645
  return { type: "boolean" };
628
646
  }
647
+ if (isNullableUnion(type)) {
648
+ return {
649
+ oneOf: type.members.map((m) => generateTypeNode(m, ctx))
650
+ };
651
+ }
629
652
  return {
630
653
  anyOf: type.members.map((m) => generateTypeNode(m, ctx))
631
654
  };
@@ -635,6 +658,13 @@ function isBooleanUnion(type) {
635
658
  const kinds = type.members.map((m) => m.kind);
636
659
  return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
637
660
  }
661
+ function isNullableUnion(type) {
662
+ if (type.members.length !== 2) return false;
663
+ const nullCount = type.members.filter(
664
+ (m) => m.kind === "primitive" && m.primitiveKind === "null"
665
+ ).length;
666
+ return nullCount === 1;
667
+ }
638
668
  function generateReferenceType(type) {
639
669
  return { $ref: `#/$defs/${type.name}` };
640
670
  }
@@ -655,10 +685,7 @@ function generateDynamicType(type) {
655
685
  "x-formspec-schemaSource": type.sourceKey
656
686
  };
657
687
  }
658
- function generateCustomType(_type) {
659
- return { type: "object" };
660
- }
661
- function applyConstraints(schema, constraints) {
688
+ function applyConstraints(schema, constraints, ctx) {
662
689
  for (const constraint of constraints) {
663
690
  switch (constraint.constraintKind) {
664
691
  case "minimum":
@@ -703,6 +730,7 @@ function applyConstraints(schema, constraints) {
703
730
  case "allowedMembers":
704
731
  break;
705
732
  case "custom":
733
+ applyCustomConstraint(schema, constraint, ctx);
706
734
  break;
707
735
  default: {
708
736
  const _exhaustive = constraint;
@@ -711,7 +739,7 @@ function applyConstraints(schema, constraints) {
711
739
  }
712
740
  }
713
741
  }
714
- function applyAnnotations(schema, annotations) {
742
+ function applyAnnotations(schema, annotations, ctx) {
715
743
  for (const annotation of annotations) {
716
744
  switch (annotation.annotationKind) {
717
745
  case "displayName":
@@ -731,6 +759,7 @@ function applyAnnotations(schema, annotations) {
731
759
  case "formatHint":
732
760
  break;
733
761
  case "custom":
762
+ applyCustomAnnotation(schema, annotation, ctx);
734
763
  break;
735
764
  default: {
736
765
  const _exhaustive = annotation;
@@ -739,6 +768,36 @@ function applyAnnotations(schema, annotations) {
739
768
  }
740
769
  }
741
770
  }
771
+ function generateCustomType(type, ctx) {
772
+ const registration = ctx.extensionRegistry?.findType(type.typeId);
773
+ if (registration === void 0) {
774
+ throw new Error(
775
+ `Cannot generate JSON Schema for custom type "${type.typeId}" without a matching extension registration`
776
+ );
777
+ }
778
+ return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
779
+ }
780
+ function applyCustomConstraint(schema, constraint, ctx) {
781
+ const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
782
+ if (registration === void 0) {
783
+ throw new Error(
784
+ `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
785
+ );
786
+ }
787
+ Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
788
+ }
789
+ function applyCustomAnnotation(schema, annotation, ctx) {
790
+ const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
791
+ if (registration === void 0) {
792
+ throw new Error(
793
+ `Cannot generate JSON Schema for custom annotation "${annotation.annotationId}" without a matching extension registration`
794
+ );
795
+ }
796
+ if (registration.toJsonSchema === void 0) {
797
+ return;
798
+ }
799
+ Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
800
+ }
742
801
  var init_ir_generator = __esm({
743
802
  "src/json-schema/ir-generator.ts"() {
744
803
  "use strict";
@@ -746,9 +805,9 @@ var init_ir_generator = __esm({
746
805
  });
747
806
 
748
807
  // src/json-schema/generator.ts
749
- function generateJsonSchema(form) {
808
+ function generateJsonSchema(form, options) {
750
809
  const ir = canonicalizeChainDSL(form);
751
- return generateJsonSchemaFromIR(ir);
810
+ return generateJsonSchemaFromIR(ir, options);
752
811
  }
753
812
  var init_generator = __esm({
754
813
  "src/json-schema/generator.ts"() {
@@ -1010,6 +1069,61 @@ var init_types = __esm({
1010
1069
  }
1011
1070
  });
1012
1071
 
1072
+ // src/extensions/registry.ts
1073
+ function createExtensionRegistry(extensions) {
1074
+ const typeMap = /* @__PURE__ */ new Map();
1075
+ const constraintMap = /* @__PURE__ */ new Map();
1076
+ const annotationMap = /* @__PURE__ */ new Map();
1077
+ for (const ext of extensions) {
1078
+ if (ext.types !== void 0) {
1079
+ for (const type of ext.types) {
1080
+ const qualifiedId = `${ext.extensionId}/${type.typeName}`;
1081
+ if (typeMap.has(qualifiedId)) {
1082
+ throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1083
+ }
1084
+ typeMap.set(qualifiedId, type);
1085
+ }
1086
+ }
1087
+ if (ext.constraints !== void 0) {
1088
+ for (const constraint of ext.constraints) {
1089
+ const qualifiedId = `${ext.extensionId}/${constraint.constraintName}`;
1090
+ if (constraintMap.has(qualifiedId)) {
1091
+ throw new Error(`Duplicate custom constraint ID: "${qualifiedId}"`);
1092
+ }
1093
+ constraintMap.set(qualifiedId, constraint);
1094
+ }
1095
+ }
1096
+ if (ext.annotations !== void 0) {
1097
+ for (const annotation of ext.annotations) {
1098
+ const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
1099
+ if (annotationMap.has(qualifiedId)) {
1100
+ throw new Error(`Duplicate custom annotation ID: "${qualifiedId}"`);
1101
+ }
1102
+ annotationMap.set(qualifiedId, annotation);
1103
+ }
1104
+ }
1105
+ }
1106
+ return {
1107
+ extensions,
1108
+ findType: (typeId) => typeMap.get(typeId),
1109
+ findConstraint: (constraintId) => constraintMap.get(constraintId),
1110
+ findAnnotation: (annotationId) => annotationMap.get(annotationId)
1111
+ };
1112
+ }
1113
+ var init_registry = __esm({
1114
+ "src/extensions/registry.ts"() {
1115
+ "use strict";
1116
+ }
1117
+ });
1118
+
1119
+ // src/extensions/index.ts
1120
+ var init_extensions = __esm({
1121
+ "src/extensions/index.ts"() {
1122
+ "use strict";
1123
+ init_registry();
1124
+ }
1125
+ });
1126
+
1013
1127
  // src/json-schema/schema.ts
1014
1128
  var import_zod3, jsonSchemaTypeSchema, jsonSchema7Schema;
1015
1129
  var init_schema2 = __esm({
@@ -1182,6 +1296,15 @@ function createFormSpecTSDocConfig() {
1182
1296
  })
1183
1297
  );
1184
1298
  }
1299
+ for (const tagName of ["displayName", "description"]) {
1300
+ config.addTagDefinition(
1301
+ new import_tsdoc.TSDocTagDefinition({
1302
+ tagName: "@" + tagName,
1303
+ syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
1304
+ allowMultiple: true
1305
+ })
1306
+ );
1307
+ }
1185
1308
  return config;
1186
1309
  }
1187
1310
  function getParser() {
@@ -1210,6 +1333,27 @@ function parseTSDocTags(node, file = "") {
1210
1333
  const docComment = parserContext.docComment;
1211
1334
  for (const block of docComment.customBlocks) {
1212
1335
  const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
1336
+ if (tagName === "displayName" || tagName === "description") {
1337
+ const text2 = extractBlockText(block).trim();
1338
+ if (text2 === "") continue;
1339
+ const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
1340
+ if (tagName === "displayName") {
1341
+ annotations.push({
1342
+ kind: "annotation",
1343
+ annotationKind: "displayName",
1344
+ value: text2,
1345
+ provenance: provenance2
1346
+ });
1347
+ } else {
1348
+ annotations.push({
1349
+ kind: "annotation",
1350
+ annotationKind: "description",
1351
+ value: text2,
1352
+ provenance: provenance2
1353
+ });
1354
+ }
1355
+ continue;
1356
+ }
1213
1357
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
1214
1358
  const text = extractBlockText(block).trim();
1215
1359
  if (text === "") continue;
@@ -1241,41 +1385,6 @@ function parseTSDocTags(node, file = "") {
1241
1385
  constraints.push(constraintNode);
1242
1386
  }
1243
1387
  }
1244
- let displayName;
1245
- let description;
1246
- let displayNameTag;
1247
- let descriptionTag;
1248
- for (const tag of jsDocTagsAll) {
1249
- const tagName = tag.tagName.text;
1250
- const commentText = getTagCommentText(tag);
1251
- if (commentText === void 0 || commentText.trim() === "") {
1252
- continue;
1253
- }
1254
- const trimmed = commentText.trim();
1255
- if (tagName === "Field_displayName") {
1256
- displayName = trimmed;
1257
- displayNameTag = tag;
1258
- } else if (tagName === "Field_description") {
1259
- description = trimmed;
1260
- descriptionTag = tag;
1261
- }
1262
- }
1263
- if (displayName !== void 0 && displayNameTag) {
1264
- annotations.push({
1265
- kind: "annotation",
1266
- annotationKind: "displayName",
1267
- value: displayName,
1268
- provenance: provenanceForJSDocTag(displayNameTag, file)
1269
- });
1270
- }
1271
- if (description !== void 0 && descriptionTag) {
1272
- annotations.push({
1273
- kind: "annotation",
1274
- annotationKind: "description",
1275
- value: description,
1276
- provenance: provenanceForJSDocTag(descriptionTag, file)
1277
- });
1278
- }
1279
1388
  return { constraints, annotations };
1280
1389
  }
1281
1390
  function extractPathTarget(text) {
@@ -1469,14 +1578,12 @@ function extractDefaultValueAnnotation(initializer, file = "") {
1469
1578
  }
1470
1579
  };
1471
1580
  }
1472
- var ts3, import_core4;
1581
+ var ts3;
1473
1582
  var init_jsdoc_constraints = __esm({
1474
1583
  "src/analyzer/jsdoc-constraints.ts"() {
1475
1584
  "use strict";
1476
1585
  ts3 = __toESM(require("typescript"), 1);
1477
- import_core4 = require("@formspec/core");
1478
1586
  init_tsdoc_parser();
1479
- init_json_utils();
1480
1587
  }
1481
1588
  });
1482
1589
 
@@ -1574,18 +1681,19 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1574
1681
  const tsType = checker.getTypeAtLocation(prop);
1575
1682
  const optional = prop.questionToken !== void 0;
1576
1683
  const provenance = provenanceForNode(prop, file);
1577
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1684
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1578
1685
  const constraints = [];
1579
1686
  if (prop.type) {
1580
1687
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1581
1688
  }
1582
1689
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1583
- const annotations = [];
1690
+ let annotations = [];
1584
1691
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1585
1692
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1586
1693
  if (defaultAnnotation) {
1587
1694
  annotations.push(defaultAnnotation);
1588
1695
  }
1696
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1589
1697
  return {
1590
1698
  kind: "field",
1591
1699
  name,
@@ -1604,14 +1712,15 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1604
1712
  const tsType = checker.getTypeAtLocation(prop);
1605
1713
  const optional = prop.questionToken !== void 0;
1606
1714
  const provenance = provenanceForNode(prop, file);
1607
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1715
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1608
1716
  const constraints = [];
1609
1717
  if (prop.type) {
1610
1718
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1611
1719
  }
1612
1720
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1613
- const annotations = [];
1721
+ let annotations = [];
1614
1722
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1723
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1615
1724
  return {
1616
1725
  kind: "field",
1617
1726
  name,
@@ -1622,6 +1731,68 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1622
1731
  provenance
1623
1732
  };
1624
1733
  }
1734
+ function applyEnumMemberDisplayNames(type, annotations) {
1735
+ if (!annotations.some(
1736
+ (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
1737
+ )) {
1738
+ return { type, annotations: [...annotations] };
1739
+ }
1740
+ const consumed = /* @__PURE__ */ new Set();
1741
+ const nextType = rewriteEnumDisplayNames(type, annotations, consumed);
1742
+ if (consumed.size === 0) {
1743
+ return { type, annotations: [...annotations] };
1744
+ }
1745
+ return {
1746
+ type: nextType,
1747
+ annotations: annotations.filter((annotation) => !consumed.has(annotation))
1748
+ };
1749
+ }
1750
+ function rewriteEnumDisplayNames(type, annotations, consumed) {
1751
+ switch (type.kind) {
1752
+ case "enum":
1753
+ return applyEnumMemberDisplayNamesToEnum(type, annotations, consumed);
1754
+ case "union": {
1755
+ return {
1756
+ ...type,
1757
+ members: type.members.map(
1758
+ (member) => rewriteEnumDisplayNames(member, annotations, consumed)
1759
+ )
1760
+ };
1761
+ }
1762
+ default:
1763
+ return type;
1764
+ }
1765
+ }
1766
+ function applyEnumMemberDisplayNamesToEnum(type, annotations, consumed) {
1767
+ const displayNames = /* @__PURE__ */ new Map();
1768
+ for (const annotation of annotations) {
1769
+ if (annotation.annotationKind !== "displayName") continue;
1770
+ const parsed = parseEnumMemberDisplayName(annotation.value);
1771
+ if (!parsed) continue;
1772
+ consumed.add(annotation);
1773
+ const member = type.members.find((m) => String(m.value) === parsed.value);
1774
+ if (!member) continue;
1775
+ displayNames.set(String(member.value), parsed.label);
1776
+ }
1777
+ if (displayNames.size === 0) {
1778
+ return type;
1779
+ }
1780
+ return {
1781
+ ...type,
1782
+ members: type.members.map((member) => {
1783
+ const displayName = displayNames.get(String(member.value));
1784
+ return displayName !== void 0 ? { ...member, displayName } : member;
1785
+ })
1786
+ };
1787
+ }
1788
+ function parseEnumMemberDisplayName(value) {
1789
+ const trimmed = value.trim();
1790
+ const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(trimmed);
1791
+ if (!match?.[1] || !match[2]) return null;
1792
+ const label = match[2].trim();
1793
+ if (label === "") return null;
1794
+ return { value: match[1], label };
1795
+ }
1625
1796
  function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
1626
1797
  if (type.flags & ts4.TypeFlags.String) {
1627
1798
  return { kind: "primitive", primitiveKind: "string" };
@@ -1732,7 +1903,30 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting) {
1732
1903
  const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
1733
1904
  return { kind: "array", items };
1734
1905
  }
1906
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
1907
+ if (type.getProperties().length > 0) {
1908
+ return null;
1909
+ }
1910
+ const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
1911
+ if (!indexInfo) {
1912
+ return null;
1913
+ }
1914
+ if (visiting.has(type)) {
1915
+ return null;
1916
+ }
1917
+ visiting.add(type);
1918
+ try {
1919
+ const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
1920
+ return { kind: "record", valueType };
1921
+ } finally {
1922
+ visiting.delete(type);
1923
+ }
1924
+ }
1735
1925
  function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1926
+ const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
1927
+ if (recordNode) {
1928
+ return recordNode;
1929
+ }
1736
1930
  if (visiting.has(type)) {
1737
1931
  return { kind: "object", properties: [], additionalProperties: false };
1738
1932
  }
@@ -1764,7 +1958,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1764
1958
  const objectNode = {
1765
1959
  kind: "object",
1766
1960
  properties,
1767
- additionalProperties: false
1961
+ additionalProperties: true
1768
1962
  };
1769
1963
  if (typeName) {
1770
1964
  typeRegistry[typeName] = {
@@ -1991,7 +2185,9 @@ __export(index_exports, {
1991
2185
  categorizationSchema: () => categorizationSchema,
1992
2186
  categorySchema: () => categorySchema,
1993
2187
  controlSchema: () => controlSchema,
2188
+ createExtensionRegistry: () => createExtensionRegistry,
1994
2189
  generateJsonSchema: () => generateJsonSchema,
2190
+ generateJsonSchemaFromIR: () => generateJsonSchemaFromIR,
1995
2191
  generateSchemas: () => generateSchemas,
1996
2192
  generateSchemasFromClass: () => generateSchemasFromClass,
1997
2193
  generateUiSchema: () => generateUiSchema,
@@ -2012,15 +2208,19 @@ __export(index_exports, {
2012
2208
  verticalLayoutSchema: () => verticalLayoutSchema,
2013
2209
  writeSchemas: () => writeSchemas
2014
2210
  });
2015
- function buildFormSchemas(form) {
2211
+ function buildFormSchemas(form, options) {
2016
2212
  return {
2017
- jsonSchema: generateJsonSchema(form),
2213
+ jsonSchema: generateJsonSchema(form, options),
2018
2214
  uiSchema: generateUiSchema(form)
2019
2215
  };
2020
2216
  }
2021
2217
  function writeSchemas(form, options) {
2022
- const { outDir, name = "schema", indent = 2 } = options;
2023
- const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form);
2218
+ const { outDir, name = "schema", indent = 2, extensionRegistry, vendorPrefix } = options;
2219
+ const buildOptions = extensionRegistry === void 0 && vendorPrefix === void 0 ? void 0 : {
2220
+ extensionRegistry,
2221
+ vendorPrefix
2222
+ };
2223
+ const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
2024
2224
  if (!fs.existsSync(outDir)) {
2025
2225
  fs.mkdirSync(outDir, { recursive: true });
2026
2226
  }
@@ -2036,12 +2236,15 @@ var init_index = __esm({
2036
2236
  "use strict";
2037
2237
  init_generator();
2038
2238
  init_generator2();
2239
+ init_ir_generator();
2039
2240
  fs = __toESM(require("fs"), 1);
2040
2241
  path2 = __toESM(require("path"), 1);
2041
2242
  init_types();
2243
+ init_extensions();
2042
2244
  init_schema();
2043
2245
  init_schema2();
2044
2246
  init_generator();
2247
+ init_ir_generator();
2045
2248
  init_generator2();
2046
2249
  init_class_schema();
2047
2250
  }