@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.js CHANGED
@@ -182,7 +182,7 @@ function canonicalizeArrayField(field) {
182
182
  const itemsType = {
183
183
  kind: "object",
184
184
  properties: itemProperties,
185
- additionalProperties: false
185
+ additionalProperties: true
186
186
  };
187
187
  const type = { kind: "array", items: itemsType };
188
188
  const constraints = [];
@@ -217,7 +217,7 @@ function canonicalizeObjectField(field) {
217
217
  const type = {
218
218
  kind: "object",
219
219
  properties,
220
- additionalProperties: false
220
+ additionalProperties: true
221
221
  };
222
222
  return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
223
223
  }
@@ -415,11 +415,21 @@ var init_canonicalize = __esm({
415
415
  });
416
416
 
417
417
  // src/json-schema/ir-generator.ts
418
- function makeContext() {
419
- return { defs: {} };
418
+ function makeContext(options) {
419
+ const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
420
+ if (!vendorPrefix.startsWith("x-")) {
421
+ throw new Error(
422
+ `Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
423
+ );
424
+ }
425
+ return {
426
+ defs: {},
427
+ extensionRegistry: options?.extensionRegistry,
428
+ vendorPrefix
429
+ };
420
430
  }
421
- function generateJsonSchemaFromIR(ir) {
422
- const ctx = makeContext();
431
+ function generateJsonSchemaFromIR(ir, options) {
432
+ const ctx = makeContext(options);
423
433
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
424
434
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
425
435
  }
@@ -471,16 +481,16 @@ function generateFieldSchema(field, ctx) {
471
481
  directConstraints.push(c);
472
482
  }
473
483
  }
474
- applyConstraints(schema, directConstraints);
475
- applyAnnotations(schema, field.annotations);
484
+ applyConstraints(schema, directConstraints, ctx);
485
+ applyAnnotations(schema, field.annotations, ctx);
476
486
  if (pathConstraints.length === 0) {
477
487
  return schema;
478
488
  }
479
- return applyPathTargetedConstraints(schema, pathConstraints);
489
+ return applyPathTargetedConstraints(schema, pathConstraints, ctx);
480
490
  }
481
- function applyPathTargetedConstraints(schema, pathConstraints) {
491
+ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
482
492
  if (schema.type === "array" && schema.items) {
483
- schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
493
+ schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
484
494
  return schema;
485
495
  }
486
496
  const byTarget = /* @__PURE__ */ new Map();
@@ -494,7 +504,7 @@ function applyPathTargetedConstraints(schema, pathConstraints) {
494
504
  const propertyOverrides = {};
495
505
  for (const [target, constraints] of byTarget) {
496
506
  const subSchema = {};
497
- applyConstraints(subSchema, constraints);
507
+ applyConstraints(subSchema, constraints, ctx);
498
508
  propertyOverrides[target] = subSchema;
499
509
  }
500
510
  if (schema.$ref) {
@@ -538,6 +548,8 @@ function generateTypeNode(type, ctx) {
538
548
  return generateArrayType(type, ctx);
539
549
  case "object":
540
550
  return generateObjectType(type, ctx);
551
+ case "record":
552
+ return generateRecordType(type, ctx);
541
553
  case "union":
542
554
  return generateUnionType(type, ctx);
543
555
  case "reference":
@@ -545,7 +557,7 @@ function generateTypeNode(type, ctx) {
545
557
  case "dynamic":
546
558
  return generateDynamicType(type);
547
559
  case "custom":
548
- return generateCustomType(type);
560
+ return generateCustomType(type, ctx);
549
561
  default: {
550
562
  const _exhaustive = type;
551
563
  return _exhaustive;
@@ -594,16 +606,27 @@ function generateObjectType(type, ctx) {
594
606
  }
595
607
  return schema;
596
608
  }
609
+ function generateRecordType(type, ctx) {
610
+ return {
611
+ type: "object",
612
+ additionalProperties: generateTypeNode(type.valueType, ctx)
613
+ };
614
+ }
597
615
  function generatePropertySchema(prop, ctx) {
598
616
  const schema = generateTypeNode(prop.type, ctx);
599
- applyConstraints(schema, prop.constraints);
600
- applyAnnotations(schema, prop.annotations);
617
+ applyConstraints(schema, prop.constraints, ctx);
618
+ applyAnnotations(schema, prop.annotations, ctx);
601
619
  return schema;
602
620
  }
603
621
  function generateUnionType(type, ctx) {
604
622
  if (isBooleanUnion(type)) {
605
623
  return { type: "boolean" };
606
624
  }
625
+ if (isNullableUnion(type)) {
626
+ return {
627
+ oneOf: type.members.map((m) => generateTypeNode(m, ctx))
628
+ };
629
+ }
607
630
  return {
608
631
  anyOf: type.members.map((m) => generateTypeNode(m, ctx))
609
632
  };
@@ -613,6 +636,13 @@ function isBooleanUnion(type) {
613
636
  const kinds = type.members.map((m) => m.kind);
614
637
  return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
615
638
  }
639
+ function isNullableUnion(type) {
640
+ if (type.members.length !== 2) return false;
641
+ const nullCount = type.members.filter(
642
+ (m) => m.kind === "primitive" && m.primitiveKind === "null"
643
+ ).length;
644
+ return nullCount === 1;
645
+ }
616
646
  function generateReferenceType(type) {
617
647
  return { $ref: `#/$defs/${type.name}` };
618
648
  }
@@ -633,10 +663,7 @@ function generateDynamicType(type) {
633
663
  "x-formspec-schemaSource": type.sourceKey
634
664
  };
635
665
  }
636
- function generateCustomType(_type) {
637
- return { type: "object" };
638
- }
639
- function applyConstraints(schema, constraints) {
666
+ function applyConstraints(schema, constraints, ctx) {
640
667
  for (const constraint of constraints) {
641
668
  switch (constraint.constraintKind) {
642
669
  case "minimum":
@@ -681,6 +708,7 @@ function applyConstraints(schema, constraints) {
681
708
  case "allowedMembers":
682
709
  break;
683
710
  case "custom":
711
+ applyCustomConstraint(schema, constraint, ctx);
684
712
  break;
685
713
  default: {
686
714
  const _exhaustive = constraint;
@@ -689,7 +717,7 @@ function applyConstraints(schema, constraints) {
689
717
  }
690
718
  }
691
719
  }
692
- function applyAnnotations(schema, annotations) {
720
+ function applyAnnotations(schema, annotations, ctx) {
693
721
  for (const annotation of annotations) {
694
722
  switch (annotation.annotationKind) {
695
723
  case "displayName":
@@ -709,6 +737,7 @@ function applyAnnotations(schema, annotations) {
709
737
  case "formatHint":
710
738
  break;
711
739
  case "custom":
740
+ applyCustomAnnotation(schema, annotation, ctx);
712
741
  break;
713
742
  default: {
714
743
  const _exhaustive = annotation;
@@ -717,6 +746,36 @@ function applyAnnotations(schema, annotations) {
717
746
  }
718
747
  }
719
748
  }
749
+ function generateCustomType(type, ctx) {
750
+ const registration = ctx.extensionRegistry?.findType(type.typeId);
751
+ if (registration === void 0) {
752
+ throw new Error(
753
+ `Cannot generate JSON Schema for custom type "${type.typeId}" without a matching extension registration`
754
+ );
755
+ }
756
+ return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
757
+ }
758
+ function applyCustomConstraint(schema, constraint, ctx) {
759
+ const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
760
+ if (registration === void 0) {
761
+ throw new Error(
762
+ `Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
763
+ );
764
+ }
765
+ Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
766
+ }
767
+ function applyCustomAnnotation(schema, annotation, ctx) {
768
+ const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
769
+ if (registration === void 0) {
770
+ throw new Error(
771
+ `Cannot generate JSON Schema for custom annotation "${annotation.annotationId}" without a matching extension registration`
772
+ );
773
+ }
774
+ if (registration.toJsonSchema === void 0) {
775
+ return;
776
+ }
777
+ Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
778
+ }
720
779
  var init_ir_generator = __esm({
721
780
  "src/json-schema/ir-generator.ts"() {
722
781
  "use strict";
@@ -724,9 +783,9 @@ var init_ir_generator = __esm({
724
783
  });
725
784
 
726
785
  // src/json-schema/generator.ts
727
- function generateJsonSchema(form) {
786
+ function generateJsonSchema(form, options) {
728
787
  const ir = canonicalizeChainDSL(form);
729
- return generateJsonSchemaFromIR(ir);
788
+ return generateJsonSchemaFromIR(ir, options);
730
789
  }
731
790
  var init_generator = __esm({
732
791
  "src/json-schema/generator.ts"() {
@@ -987,6 +1046,61 @@ var init_types = __esm({
987
1046
  }
988
1047
  });
989
1048
 
1049
+ // src/extensions/registry.ts
1050
+ function createExtensionRegistry(extensions) {
1051
+ const typeMap = /* @__PURE__ */ new Map();
1052
+ const constraintMap = /* @__PURE__ */ new Map();
1053
+ const annotationMap = /* @__PURE__ */ new Map();
1054
+ for (const ext of extensions) {
1055
+ if (ext.types !== void 0) {
1056
+ for (const type of ext.types) {
1057
+ const qualifiedId = `${ext.extensionId}/${type.typeName}`;
1058
+ if (typeMap.has(qualifiedId)) {
1059
+ throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
1060
+ }
1061
+ typeMap.set(qualifiedId, type);
1062
+ }
1063
+ }
1064
+ if (ext.constraints !== void 0) {
1065
+ for (const constraint of ext.constraints) {
1066
+ const qualifiedId = `${ext.extensionId}/${constraint.constraintName}`;
1067
+ if (constraintMap.has(qualifiedId)) {
1068
+ throw new Error(`Duplicate custom constraint ID: "${qualifiedId}"`);
1069
+ }
1070
+ constraintMap.set(qualifiedId, constraint);
1071
+ }
1072
+ }
1073
+ if (ext.annotations !== void 0) {
1074
+ for (const annotation of ext.annotations) {
1075
+ const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
1076
+ if (annotationMap.has(qualifiedId)) {
1077
+ throw new Error(`Duplicate custom annotation ID: "${qualifiedId}"`);
1078
+ }
1079
+ annotationMap.set(qualifiedId, annotation);
1080
+ }
1081
+ }
1082
+ }
1083
+ return {
1084
+ extensions,
1085
+ findType: (typeId) => typeMap.get(typeId),
1086
+ findConstraint: (constraintId) => constraintMap.get(constraintId),
1087
+ findAnnotation: (annotationId) => annotationMap.get(annotationId)
1088
+ };
1089
+ }
1090
+ var init_registry = __esm({
1091
+ "src/extensions/registry.ts"() {
1092
+ "use strict";
1093
+ }
1094
+ });
1095
+
1096
+ // src/extensions/index.ts
1097
+ var init_extensions = __esm({
1098
+ "src/extensions/index.ts"() {
1099
+ "use strict";
1100
+ init_registry();
1101
+ }
1102
+ });
1103
+
990
1104
  // src/json-schema/schema.ts
991
1105
  import { z as z3 } from "zod";
992
1106
  var jsonSchemaTypeSchema, jsonSchema7Schema;
@@ -1173,6 +1287,15 @@ function createFormSpecTSDocConfig() {
1173
1287
  })
1174
1288
  );
1175
1289
  }
1290
+ for (const tagName of ["displayName", "description"]) {
1291
+ config.addTagDefinition(
1292
+ new TSDocTagDefinition({
1293
+ tagName: "@" + tagName,
1294
+ syntaxKind: TSDocTagSyntaxKind.BlockTag,
1295
+ allowMultiple: true
1296
+ })
1297
+ );
1298
+ }
1176
1299
  return config;
1177
1300
  }
1178
1301
  function getParser() {
@@ -1201,6 +1324,27 @@ function parseTSDocTags(node, file = "") {
1201
1324
  const docComment = parserContext.docComment;
1202
1325
  for (const block of docComment.customBlocks) {
1203
1326
  const tagName = normalizeConstraintTagName(block.blockTag.tagName.substring(1));
1327
+ if (tagName === "displayName" || tagName === "description") {
1328
+ const text2 = extractBlockText(block).trim();
1329
+ if (text2 === "") continue;
1330
+ const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
1331
+ if (tagName === "displayName") {
1332
+ annotations.push({
1333
+ kind: "annotation",
1334
+ annotationKind: "displayName",
1335
+ value: text2,
1336
+ provenance: provenance2
1337
+ });
1338
+ } else {
1339
+ annotations.push({
1340
+ kind: "annotation",
1341
+ annotationKind: "description",
1342
+ value: text2,
1343
+ provenance: provenance2
1344
+ });
1345
+ }
1346
+ continue;
1347
+ }
1204
1348
  if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
1205
1349
  const text = extractBlockText(block).trim();
1206
1350
  if (text === "") continue;
@@ -1232,41 +1376,6 @@ function parseTSDocTags(node, file = "") {
1232
1376
  constraints.push(constraintNode);
1233
1377
  }
1234
1378
  }
1235
- let displayName;
1236
- let description;
1237
- let displayNameTag;
1238
- let descriptionTag;
1239
- for (const tag of jsDocTagsAll) {
1240
- const tagName = tag.tagName.text;
1241
- const commentText = getTagCommentText(tag);
1242
- if (commentText === void 0 || commentText.trim() === "") {
1243
- continue;
1244
- }
1245
- const trimmed = commentText.trim();
1246
- if (tagName === "Field_displayName") {
1247
- displayName = trimmed;
1248
- displayNameTag = tag;
1249
- } else if (tagName === "Field_description") {
1250
- description = trimmed;
1251
- descriptionTag = tag;
1252
- }
1253
- }
1254
- if (displayName !== void 0 && displayNameTag) {
1255
- annotations.push({
1256
- kind: "annotation",
1257
- annotationKind: "displayName",
1258
- value: displayName,
1259
- provenance: provenanceForJSDocTag(displayNameTag, file)
1260
- });
1261
- }
1262
- if (description !== void 0 && descriptionTag) {
1263
- annotations.push({
1264
- kind: "annotation",
1265
- annotationKind: "description",
1266
- value: description,
1267
- provenance: provenanceForJSDocTag(descriptionTag, file)
1268
- });
1269
- }
1270
1379
  return { constraints, annotations };
1271
1380
  }
1272
1381
  function extractPathTarget(text) {
@@ -1417,11 +1526,6 @@ var init_tsdoc_parser = __esm({
1417
1526
 
1418
1527
  // src/analyzer/jsdoc-constraints.ts
1419
1528
  import * as ts3 from "typescript";
1420
- import {
1421
- BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
1422
- isBuiltinConstraintName as isBuiltinConstraintName2,
1423
- normalizeConstraintTagName as normalizeConstraintTagName2
1424
- } from "@formspec/core";
1425
1529
  function extractJSDocConstraintNodes(node, file = "") {
1426
1530
  const result = parseTSDocTags(node, file);
1427
1531
  return [...result.constraints];
@@ -1467,7 +1571,6 @@ var init_jsdoc_constraints = __esm({
1467
1571
  "src/analyzer/jsdoc-constraints.ts"() {
1468
1572
  "use strict";
1469
1573
  init_tsdoc_parser();
1470
- init_json_utils();
1471
1574
  }
1472
1575
  });
1473
1576
 
@@ -1566,18 +1669,19 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
1566
1669
  const tsType = checker.getTypeAtLocation(prop);
1567
1670
  const optional = prop.questionToken !== void 0;
1568
1671
  const provenance = provenanceForNode(prop, file);
1569
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1672
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1570
1673
  const constraints = [];
1571
1674
  if (prop.type) {
1572
1675
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1573
1676
  }
1574
1677
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1575
- const annotations = [];
1678
+ let annotations = [];
1576
1679
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1577
1680
  const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
1578
1681
  if (defaultAnnotation) {
1579
1682
  annotations.push(defaultAnnotation);
1580
1683
  }
1684
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1581
1685
  return {
1582
1686
  kind: "field",
1583
1687
  name,
@@ -1596,14 +1700,15 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1596
1700
  const tsType = checker.getTypeAtLocation(prop);
1597
1701
  const optional = prop.questionToken !== void 0;
1598
1702
  const provenance = provenanceForNode(prop, file);
1599
- const type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1703
+ let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
1600
1704
  const constraints = [];
1601
1705
  if (prop.type) {
1602
1706
  constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
1603
1707
  }
1604
1708
  constraints.push(...extractJSDocConstraintNodes(prop, file));
1605
- const annotations = [];
1709
+ let annotations = [];
1606
1710
  annotations.push(...extractJSDocAnnotationNodes(prop, file));
1711
+ ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
1607
1712
  return {
1608
1713
  kind: "field",
1609
1714
  name,
@@ -1614,6 +1719,68 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
1614
1719
  provenance
1615
1720
  };
1616
1721
  }
1722
+ function applyEnumMemberDisplayNames(type, annotations) {
1723
+ if (!annotations.some(
1724
+ (annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
1725
+ )) {
1726
+ return { type, annotations: [...annotations] };
1727
+ }
1728
+ const consumed = /* @__PURE__ */ new Set();
1729
+ const nextType = rewriteEnumDisplayNames(type, annotations, consumed);
1730
+ if (consumed.size === 0) {
1731
+ return { type, annotations: [...annotations] };
1732
+ }
1733
+ return {
1734
+ type: nextType,
1735
+ annotations: annotations.filter((annotation) => !consumed.has(annotation))
1736
+ };
1737
+ }
1738
+ function rewriteEnumDisplayNames(type, annotations, consumed) {
1739
+ switch (type.kind) {
1740
+ case "enum":
1741
+ return applyEnumMemberDisplayNamesToEnum(type, annotations, consumed);
1742
+ case "union": {
1743
+ return {
1744
+ ...type,
1745
+ members: type.members.map(
1746
+ (member) => rewriteEnumDisplayNames(member, annotations, consumed)
1747
+ )
1748
+ };
1749
+ }
1750
+ default:
1751
+ return type;
1752
+ }
1753
+ }
1754
+ function applyEnumMemberDisplayNamesToEnum(type, annotations, consumed) {
1755
+ const displayNames = /* @__PURE__ */ new Map();
1756
+ for (const annotation of annotations) {
1757
+ if (annotation.annotationKind !== "displayName") continue;
1758
+ const parsed = parseEnumMemberDisplayName(annotation.value);
1759
+ if (!parsed) continue;
1760
+ consumed.add(annotation);
1761
+ const member = type.members.find((m) => String(m.value) === parsed.value);
1762
+ if (!member) continue;
1763
+ displayNames.set(String(member.value), parsed.label);
1764
+ }
1765
+ if (displayNames.size === 0) {
1766
+ return type;
1767
+ }
1768
+ return {
1769
+ ...type,
1770
+ members: type.members.map((member) => {
1771
+ const displayName = displayNames.get(String(member.value));
1772
+ return displayName !== void 0 ? { ...member, displayName } : member;
1773
+ })
1774
+ };
1775
+ }
1776
+ function parseEnumMemberDisplayName(value) {
1777
+ const trimmed = value.trim();
1778
+ const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(trimmed);
1779
+ if (!match?.[1] || !match[2]) return null;
1780
+ const label = match[2].trim();
1781
+ if (label === "") return null;
1782
+ return { value: match[1], label };
1783
+ }
1617
1784
  function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
1618
1785
  if (type.flags & ts4.TypeFlags.String) {
1619
1786
  return { kind: "primitive", primitiveKind: "string" };
@@ -1724,7 +1891,30 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting) {
1724
1891
  const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
1725
1892
  return { kind: "array", items };
1726
1893
  }
1894
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
1895
+ if (type.getProperties().length > 0) {
1896
+ return null;
1897
+ }
1898
+ const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
1899
+ if (!indexInfo) {
1900
+ return null;
1901
+ }
1902
+ if (visiting.has(type)) {
1903
+ return null;
1904
+ }
1905
+ visiting.add(type);
1906
+ try {
1907
+ const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
1908
+ return { kind: "record", valueType };
1909
+ } finally {
1910
+ visiting.delete(type);
1911
+ }
1912
+ }
1727
1913
  function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1914
+ const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
1915
+ if (recordNode) {
1916
+ return recordNode;
1917
+ }
1728
1918
  if (visiting.has(type)) {
1729
1919
  return { kind: "object", properties: [], additionalProperties: false };
1730
1920
  }
@@ -1756,7 +1946,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
1756
1946
  const objectNode = {
1757
1947
  kind: "object",
1758
1948
  properties,
1759
- additionalProperties: false
1949
+ additionalProperties: true
1760
1950
  };
1761
1951
  if (typeName) {
1762
1952
  typeRegistry[typeName] = {
@@ -1982,7 +2172,9 @@ __export(index_exports, {
1982
2172
  categorizationSchema: () => categorizationSchema,
1983
2173
  categorySchema: () => categorySchema,
1984
2174
  controlSchema: () => controlSchema,
2175
+ createExtensionRegistry: () => createExtensionRegistry,
1985
2176
  generateJsonSchema: () => generateJsonSchema,
2177
+ generateJsonSchemaFromIR: () => generateJsonSchemaFromIR,
1986
2178
  generateSchemas: () => generateSchemas,
1987
2179
  generateSchemasFromClass: () => generateSchemasFromClass,
1988
2180
  generateUiSchema: () => generateUiSchema,
@@ -2005,15 +2197,19 @@ __export(index_exports, {
2005
2197
  });
2006
2198
  import * as fs from "fs";
2007
2199
  import * as path2 from "path";
2008
- function buildFormSchemas(form) {
2200
+ function buildFormSchemas(form, options) {
2009
2201
  return {
2010
- jsonSchema: generateJsonSchema(form),
2202
+ jsonSchema: generateJsonSchema(form, options),
2011
2203
  uiSchema: generateUiSchema(form)
2012
2204
  };
2013
2205
  }
2014
2206
  function writeSchemas(form, options) {
2015
- const { outDir, name = "schema", indent = 2 } = options;
2016
- const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form);
2207
+ const { outDir, name = "schema", indent = 2, extensionRegistry, vendorPrefix } = options;
2208
+ const buildOptions = extensionRegistry === void 0 && vendorPrefix === void 0 ? void 0 : {
2209
+ extensionRegistry,
2210
+ vendorPrefix
2211
+ };
2212
+ const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
2017
2213
  if (!fs.existsSync(outDir)) {
2018
2214
  fs.mkdirSync(outDir, { recursive: true });
2019
2215
  }
@@ -2028,10 +2224,13 @@ var init_index = __esm({
2028
2224
  "use strict";
2029
2225
  init_generator();
2030
2226
  init_generator2();
2227
+ init_ir_generator();
2031
2228
  init_types();
2229
+ init_extensions();
2032
2230
  init_schema();
2033
2231
  init_schema2();
2034
2232
  init_generator();
2233
+ init_ir_generator();
2035
2234
  init_generator2();
2036
2235
  init_class_schema();
2037
2236
  }