@formspec/build 0.1.0-alpha.43 → 0.1.0-alpha.45

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 (44) hide show
  1. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  2. package/dist/analyzer/program.d.ts +4 -1
  3. package/dist/analyzer/program.d.ts.map +1 -1
  4. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  5. package/dist/browser.cjs +35 -9
  6. package/dist/browser.cjs.map +1 -1
  7. package/dist/browser.js +36 -12
  8. package/dist/browser.js.map +1 -1
  9. package/dist/build-alpha.d.ts +106 -5
  10. package/dist/build-beta.d.ts +106 -5
  11. package/dist/build-internal.d.ts +106 -5
  12. package/dist/build.d.ts +106 -5
  13. package/dist/cli.cjs +343 -59
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +339 -58
  16. package/dist/cli.js.map +1 -1
  17. package/dist/extensions/index.d.ts +1 -1
  18. package/dist/extensions/index.d.ts.map +1 -1
  19. package/dist/extensions/registry.d.ts +64 -5
  20. package/dist/extensions/registry.d.ts.map +1 -1
  21. package/dist/extensions/symbol-registry.d.ts +33 -0
  22. package/dist/extensions/symbol-registry.d.ts.map +1 -0
  23. package/dist/generators/class-schema.d.ts +16 -0
  24. package/dist/generators/class-schema.d.ts.map +1 -1
  25. package/dist/generators/discovered-schema.d.ts.map +1 -1
  26. package/dist/index.cjs +328 -51
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +329 -54
  31. package/dist/index.js.map +1 -1
  32. package/dist/internals.cjs +124 -10
  33. package/dist/internals.cjs.map +1 -1
  34. package/dist/internals.d.ts +1 -1
  35. package/dist/internals.d.ts.map +1 -1
  36. package/dist/internals.js +125 -13
  37. package/dist/internals.js.map +1 -1
  38. package/dist/json-schema/generator.d.ts.map +1 -1
  39. package/dist/metadata/collision-guards.d.ts.map +1 -1
  40. package/dist/metadata/policy.d.ts.map +1 -1
  41. package/dist/metadata/resolve.d.ts.map +1 -1
  42. package/dist/ui-schema/ir-generator.d.ts.map +1 -1
  43. package/dist/validate/constraint-validator.d.ts.map +1 -1
  44. package/package.json +5 -5
package/dist/cli.cjs CHANGED
@@ -264,7 +264,9 @@ function resolveTypeNodeMetadata(type, options) {
264
264
  case "object":
265
265
  return {
266
266
  ...type,
267
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
267
+ properties: type.properties.map(
268
+ (property) => resolveObjectPropertyMetadata(property, options)
269
+ )
268
270
  };
269
271
  case "record":
270
272
  return {
@@ -1626,7 +1628,7 @@ function applyCustomConstraint(schema, constraint, ctx) {
1626
1628
  if (registration.emitsVocabularyKeywords) {
1627
1629
  const target = schema;
1628
1630
  for (const [key, value] of Object.entries(extensionSchema)) {
1629
- if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
1631
+ if (VOCABULARY_MODE_BLOCKED_KEYWORDS.has(key)) {
1630
1632
  throw new Error(
1631
1633
  `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
1632
1634
  );
@@ -1669,13 +1671,13 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
1669
1671
  schema[key] = value;
1670
1672
  }
1671
1673
  }
1672
- var JSON_SCHEMA_STRUCTURAL_KEYWORDS;
1674
+ var VOCABULARY_MODE_BLOCKED_KEYWORDS;
1673
1675
  var init_ir_generator = __esm({
1674
1676
  "src/json-schema/ir-generator.ts"() {
1675
1677
  "use strict";
1676
1678
  init_metadata();
1677
1679
  init_collision_guards();
1678
- JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
1680
+ VOCABULARY_MODE_BLOCKED_KEYWORDS = /* @__PURE__ */ new Set([
1679
1681
  "$schema",
1680
1682
  "$ref",
1681
1683
  "$defs",
@@ -1743,10 +1745,7 @@ function generateJsonSchema(form, options) {
1743
1745
  const metadata = options?.metadata;
1744
1746
  const vendorPrefix = options?.vendorPrefix;
1745
1747
  const enumSerialization = options?.enumSerialization;
1746
- const ir = canonicalizeChainDSL(
1747
- form,
1748
- metadata !== void 0 ? { metadata } : void 0
1749
- );
1748
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1750
1749
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1751
1750
  ...vendorPrefix !== void 0 && { vendorPrefix },
1752
1751
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1974,7 +1973,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1974
1973
  break;
1975
1974
  }
1976
1975
  case "conditional": {
1977
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1976
+ const newRule = createShowRule(
1977
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1978
+ element.value
1979
+ );
1978
1980
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1979
1981
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1980
1982
  result.push(...childElements);
@@ -2059,8 +2061,10 @@ function buildConstraintTagSources(extensions) {
2059
2061
  }
2060
2062
  function createExtensionRegistry(extensions) {
2061
2063
  const reservedTagSources = buildConstraintTagSources(extensions);
2064
+ let symbolMap = /* @__PURE__ */ new Map();
2062
2065
  const typeMap = /* @__PURE__ */ new Map();
2063
2066
  const typeNameMap = /* @__PURE__ */ new Map();
2067
+ const brandMap = /* @__PURE__ */ new Map();
2064
2068
  const constraintMap = /* @__PURE__ */ new Map();
2065
2069
  const constraintTagMap = /* @__PURE__ */ new Map();
2066
2070
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -2084,6 +2088,20 @@ function createExtensionRegistry(extensions) {
2084
2088
  registration: type
2085
2089
  });
2086
2090
  }
2091
+ if (type.brand !== void 0) {
2092
+ if (type.brand === "__integerBrand") {
2093
+ throw new Error(
2094
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
2095
+ );
2096
+ }
2097
+ if (brandMap.has(type.brand)) {
2098
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
2099
+ }
2100
+ brandMap.set(type.brand, {
2101
+ extensionId: ext.extensionId,
2102
+ registration: type
2103
+ });
2104
+ }
2087
2105
  if (type.builtinConstraintBroadenings !== void 0) {
2088
2106
  for (const broadening of type.builtinConstraintBroadenings) {
2089
2107
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2153,7 +2171,10 @@ function createExtensionRegistry(extensions) {
2153
2171
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2154
2172
  );
2155
2173
  }
2156
- if (Object.hasOwn(import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS, (0, import_internals3.normalizeConstraintTagName)(canonicalTagName))) {
2174
+ if (Object.hasOwn(
2175
+ import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS,
2176
+ (0, import_internals3.normalizeConstraintTagName)(canonicalTagName)
2177
+ )) {
2157
2178
  throw new Error(
2158
2179
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${(0, import_internals3.normalizeConstraintTagName)(canonicalTagName)}".`
2159
2180
  );
@@ -2174,6 +2195,11 @@ function createExtensionRegistry(extensions) {
2174
2195
  extensions,
2175
2196
  findType: (typeId) => typeMap.get(typeId),
2176
2197
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2198
+ findTypeByBrand: (brand) => brandMap.get(brand),
2199
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2200
+ setSymbolMap: (map) => {
2201
+ symbolMap = map;
2202
+ },
2177
2203
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2178
2204
  findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal.normalizeFormSpecTagName)(tagName)),
2179
2205
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -3058,6 +3084,71 @@ function isObjectType(type) {
3058
3084
  function isIntersectionType(type) {
3059
3085
  return !!(type.flags & ts3.TypeFlags.Intersection);
3060
3086
  }
3087
+ function collectBrandIdentifiers(type) {
3088
+ if (!type.isIntersection()) {
3089
+ return [];
3090
+ }
3091
+ const brands = [];
3092
+ for (const prop of type.getProperties()) {
3093
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
3094
+ if (decl === void 0) continue;
3095
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
3096
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
3097
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
3098
+ brands.push(decl.name.expression.text);
3099
+ }
3100
+ return brands;
3101
+ }
3102
+ function resolveCanonicalSymbol(type, checker) {
3103
+ const raw = type.aliasSymbol ?? type.getSymbol();
3104
+ if (raw === void 0) return void 0;
3105
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3106
+ }
3107
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3108
+ if (extensionRegistry === void 0) {
3109
+ return null;
3110
+ }
3111
+ const canonical = resolveCanonicalSymbol(type, checker);
3112
+ if (canonical === void 0) {
3113
+ return null;
3114
+ }
3115
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3116
+ if (registration === void 0) {
3117
+ return null;
3118
+ }
3119
+ return {
3120
+ kind: "custom",
3121
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3122
+ payload: null
3123
+ };
3124
+ }
3125
+ function isIntegerBrandedType(type) {
3126
+ if (!type.isIntersection()) {
3127
+ return false;
3128
+ }
3129
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
3130
+ if (!hasNumberBase) {
3131
+ return false;
3132
+ }
3133
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3134
+ }
3135
+ function resolveBrandedCustomType(type, extensionRegistry) {
3136
+ if (extensionRegistry === void 0) {
3137
+ return null;
3138
+ }
3139
+ for (const brand of collectBrandIdentifiers(type)) {
3140
+ const registration = extensionRegistry.findTypeByBrand(brand);
3141
+ if (registration === void 0) {
3142
+ continue;
3143
+ }
3144
+ return {
3145
+ kind: "custom",
3146
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3147
+ payload: null
3148
+ };
3149
+ }
3150
+ return null;
3151
+ }
3061
3152
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
3062
3153
  if (ts3.isParenthesizedTypeNode(typeNode)) {
3063
3154
  return isResolvableObjectLikeAliasTypeNode(typeNode.type);
@@ -4164,6 +4255,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4164
4255
  if (customType) {
4165
4256
  return customType;
4166
4257
  }
4258
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4259
+ if (symbolCustomType) {
4260
+ return symbolCustomType;
4261
+ }
4262
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4263
+ if (brandedCustomType) {
4264
+ return brandedCustomType;
4265
+ }
4167
4266
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4168
4267
  type,
4169
4268
  checker,
@@ -4178,6 +4277,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4178
4277
  if (primitiveAlias) {
4179
4278
  return primitiveAlias;
4180
4279
  }
4280
+ if (isIntegerBrandedType(type)) {
4281
+ return { kind: "primitive", primitiveKind: "integer" };
4282
+ }
4181
4283
  if (type.flags & ts3.TypeFlags.String) {
4182
4284
  return { kind: "primitive", primitiveKind: "string" };
4183
4285
  }
@@ -4280,7 +4382,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4280
4382
  return { kind: "primitive", primitiveKind: "string" };
4281
4383
  }
4282
4384
  function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4283
- if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
4385
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null)) && !isIntegerBrandedType(type)) {
4284
4386
  return null;
4285
4387
  }
4286
4388
  const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
@@ -4366,6 +4468,9 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4366
4468
  visitedAliases
4367
4469
  );
4368
4470
  }
4471
+ if (isIntegerBrandedType(type)) {
4472
+ return { kind: "primitive", primitiveKind: "integer" };
4473
+ }
4369
4474
  if (type.flags & ts3.TypeFlags.String) {
4370
4475
  return { kind: "primitive", primitiveKind: "string" };
4371
4476
  }
@@ -5118,7 +5223,7 @@ function createProgramContextFromProgram(program, filePath) {
5118
5223
  sourceFile
5119
5224
  };
5120
5225
  }
5121
- function createProgramContext(filePath) {
5226
+ function createProgramContext(filePath, additionalFiles) {
5122
5227
  const absolutePath = path.resolve(filePath);
5123
5228
  const fileDir = path.dirname(absolutePath);
5124
5229
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5141,7 +5246,8 @@ function createProgramContext(filePath) {
5141
5246
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5142
5247
  }
5143
5248
  compilerOptions = parsed.options;
5144
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5249
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5250
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5145
5251
  } else {
5146
5252
  compilerOptions = {
5147
5253
  target: ts4.ScriptTarget.ES2022,
@@ -5151,7 +5257,8 @@ function createProgramContext(filePath) {
5151
5257
  skipLibCheck: true,
5152
5258
  declaration: true
5153
5259
  };
5154
- fileNames = [absolutePath];
5260
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5261
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5155
5262
  }
5156
5263
  const program = ts4.createProgram(fileNames, compilerOptions);
5157
5264
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5464,6 +5571,138 @@ var init_program = __esm({
5464
5571
  }
5465
5572
  });
5466
5573
 
5574
+ // src/extensions/symbol-registry.ts
5575
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5576
+ const symbolMap = /* @__PURE__ */ new Map();
5577
+ const normalizedPath = path2.resolve(configPath);
5578
+ const configFile = program.getSourceFile(normalizedPath);
5579
+ if (configFile === void 0) {
5580
+ return symbolMap;
5581
+ }
5582
+ function visit(node) {
5583
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5584
+ processDefineCustomTypeCall(node);
5585
+ }
5586
+ ts5.forEachChild(node, visit);
5587
+ }
5588
+ function processDefineCustomTypeCall(call) {
5589
+ const typeArgNode = call.typeArguments?.[0];
5590
+ if (typeArgNode === void 0) {
5591
+ return;
5592
+ }
5593
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5594
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5595
+ if (canonical === void 0) {
5596
+ return;
5597
+ }
5598
+ const typeName = extractTypeNameFromCallArg(call);
5599
+ if (typeName === null) {
5600
+ return;
5601
+ }
5602
+ let entry;
5603
+ const extensionId = extractEnclosingExtensionId(call, checker);
5604
+ if (extensionId !== null) {
5605
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5606
+ if (reg !== void 0) {
5607
+ entry = { extensionId, registration: reg };
5608
+ }
5609
+ }
5610
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5611
+ if (entry === void 0) {
5612
+ return;
5613
+ }
5614
+ symbolMap.set(canonical, entry);
5615
+ }
5616
+ visit(configFile);
5617
+ return symbolMap;
5618
+ }
5619
+ function isDefineCustomTypeCall(node, checker) {
5620
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5621
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5622
+ if (callSymbol !== void 0) {
5623
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5624
+ const decl = resolved.declarations?.[0];
5625
+ if (decl !== void 0) {
5626
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5627
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5628
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5629
+ }
5630
+ }
5631
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5632
+ }
5633
+ function resolveCanonicalSymbol2(type, checker) {
5634
+ const raw = type.aliasSymbol ?? type.getSymbol();
5635
+ if (raw === void 0) return void 0;
5636
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5637
+ }
5638
+ function extractTypeNameFromCallArg(call) {
5639
+ const arg = call.arguments[0];
5640
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5641
+ return null;
5642
+ }
5643
+ const typeNameProp = arg.properties.find(
5644
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5645
+ );
5646
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5647
+ return null;
5648
+ }
5649
+ return typeNameProp.initializer.text;
5650
+ }
5651
+ function extractEnclosingExtensionId(call, checker) {
5652
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5653
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5654
+ return extractExtensionIdFromCallArg(node);
5655
+ }
5656
+ }
5657
+ return null;
5658
+ }
5659
+ function isDefineExtensionCall(node, checker) {
5660
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5661
+ if (callSymbol !== void 0) {
5662
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5663
+ const decl = resolved.declarations?.[0];
5664
+ if (decl !== void 0) {
5665
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5666
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5667
+ }
5668
+ }
5669
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5670
+ }
5671
+ function extractExtensionIdFromCallArg(call) {
5672
+ const arg = call.arguments[0];
5673
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5674
+ return null;
5675
+ }
5676
+ const prop = arg.properties.find(
5677
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5678
+ );
5679
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5680
+ return null;
5681
+ }
5682
+ return prop.initializer.text;
5683
+ }
5684
+ function findRegistrationByTypeName(registry, typeName) {
5685
+ for (const ext of registry.extensions) {
5686
+ if (ext.types === void 0) {
5687
+ continue;
5688
+ }
5689
+ for (const type of ext.types) {
5690
+ if (type.typeName === typeName) {
5691
+ return { extensionId: ext.extensionId, registration: type };
5692
+ }
5693
+ }
5694
+ }
5695
+ return void 0;
5696
+ }
5697
+ var ts5, path2;
5698
+ var init_symbol_registry = __esm({
5699
+ "src/extensions/symbol-registry.ts"() {
5700
+ "use strict";
5701
+ ts5 = __toESM(require("typescript"), 1);
5702
+ path2 = __toESM(require("path"), 1);
5703
+ }
5704
+ });
5705
+
5467
5706
  // src/validate/constraint-validator.ts
5468
5707
  function validateFieldNode(ctx, field) {
5469
5708
  const analysis = (0, import_internal4.analyzeConstraintTargets)(
@@ -5610,7 +5849,8 @@ function formatLocation(location) {
5610
5849
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5611
5850
  }
5612
5851
  function generateSchemasFromClass(options) {
5613
- const ctx = createProgramContext(options.filePath);
5852
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5853
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5614
5854
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5615
5855
  if (!classDecl) {
5616
5856
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5675,7 +5915,8 @@ function generateSchemasDetailed(options) {
5675
5915
  function generateSchemasDetailedInternal(options) {
5676
5916
  let ctx;
5677
5917
  try {
5678
- ctx = createProgramContext(options.filePath);
5918
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5919
+ ctx = createProgramContext(options.filePath, additionalFiles);
5679
5920
  } catch (error) {
5680
5921
  return {
5681
5922
  ok: false,
@@ -5714,13 +5955,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5714
5955
  }
5715
5956
  function generateSchemasBatch(options) {
5716
5957
  const contextCache = /* @__PURE__ */ new Map();
5958
+ const resolved = resolveOptions(options);
5959
+ let symbolMapProgram;
5717
5960
  return options.targets.map((target) => {
5718
5961
  let ctx;
5719
5962
  try {
5720
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5963
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5721
5964
  const cachedContext = contextCache.get(cacheKey);
5722
5965
  if (cachedContext === void 0) {
5723
- ctx = createProgramContext(target.filePath);
5966
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5967
+ ctx = createProgramContext(target.filePath, additionalFiles);
5724
5968
  contextCache.set(cacheKey, ctx);
5725
5969
  } else {
5726
5970
  ctx = cachedContext;
@@ -5731,9 +5975,25 @@ function generateSchemasBatch(options) {
5731
5975
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5732
5976
  });
5733
5977
  }
5978
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5979
+ const symbolMap = buildSymbolMapFromConfig(
5980
+ options.configPath,
5981
+ ctx.program,
5982
+ ctx.checker,
5983
+ resolved.extensionRegistry
5984
+ );
5985
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5986
+ symbolMapProgram = ctx.program;
5987
+ }
5734
5988
  return withTarget(
5735
5989
  target,
5736
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5990
+ generateSchemasFromResolvedOptions(
5991
+ ctx,
5992
+ target.filePath,
5993
+ target.typeName,
5994
+ resolved,
5995
+ options.discriminator
5996
+ )
5737
5997
  );
5738
5998
  });
5739
5999
  }
@@ -5754,11 +6014,19 @@ function generateSchemasBatchFromProgram(options) {
5754
6014
  );
5755
6015
  });
5756
6016
  }
6017
+ function isMutableRegistry(reg) {
6018
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
6019
+ }
5757
6020
  function resolveOptions(options) {
5758
6021
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
6022
+ const legacyRegistry = options.extensionRegistry;
5759
6023
  return {
5760
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5761
- extensionRegistry: options.extensionRegistry ?? configRegistry,
6024
+ // When the caller provides the deprecated extensionRegistry field directly,
6025
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
6026
+ // because the legacy path was introduced before MutableExtensionRegistry was
6027
+ // split out; callers using createExtensionRegistry() always get a mutable
6028
+ // registry, and this cast is safe for all registries produced by this module.
6029
+ extensionRegistry: legacyRegistry ?? configRegistry,
5762
6030
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5763
6031
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5764
6032
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5769,13 +6037,31 @@ function resolveOptions(options) {
5769
6037
  }
5770
6038
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5771
6039
  const resolved = resolveOptions(options);
6040
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
6041
+ const symbolMap = buildSymbolMapFromConfig(
6042
+ options.configPath,
6043
+ ctx.program,
6044
+ ctx.checker,
6045
+ resolved.extensionRegistry
6046
+ );
6047
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
6048
+ }
6049
+ return generateSchemasFromResolvedOptions(
6050
+ ctx,
6051
+ filePath,
6052
+ typeName,
6053
+ resolved,
6054
+ options.discriminator
6055
+ );
6056
+ }
6057
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5772
6058
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5773
6059
  ctx,
5774
6060
  filePath,
5775
6061
  typeName,
5776
6062
  resolved.extensionRegistry,
5777
6063
  resolved.metadata,
5778
- options.discriminator
6064
+ discriminator
5779
6065
  );
5780
6066
  if (!analysisResult.ok) {
5781
6067
  return {
@@ -5815,16 +6101,17 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5815
6101
  relatedLocations: []
5816
6102
  };
5817
6103
  }
5818
- var ts5;
6104
+ var ts6;
5819
6105
  var init_class_schema = __esm({
5820
6106
  "src/generators/class-schema.ts"() {
5821
6107
  "use strict";
5822
- ts5 = __toESM(require("typescript"), 1);
6108
+ ts6 = __toESM(require("typescript"), 1);
5823
6109
  init_program();
5824
6110
  init_class_analyzer();
5825
6111
  init_canonicalize();
5826
6112
  init_ir_generator();
5827
6113
  init_extensions();
6114
+ init_symbol_registry();
5828
6115
  init_ir_generator2();
5829
6116
  init_validate();
5830
6117
  }
@@ -5845,7 +6132,7 @@ function getModuleSymbol(context) {
5845
6132
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5846
6133
  }
5847
6134
  function isSchemaSourceDeclaration(declaration) {
5848
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6135
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5849
6136
  }
5850
6137
  function resolveModuleExport(context, exportName = "default") {
5851
6138
  const moduleSymbol = getModuleSymbol(context);
@@ -5856,16 +6143,16 @@ function resolveModuleExport(context, exportName = "default") {
5856
6143
  if (exportSymbol === null) {
5857
6144
  return null;
5858
6145
  }
5859
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6146
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5860
6147
  }
5861
6148
  function resolveModuleExportDeclaration(context, exportName = "default") {
5862
6149
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5863
6150
  }
5864
- var ts6;
6151
+ var ts7;
5865
6152
  var init_static_build = __esm({
5866
6153
  "src/static-build.ts"() {
5867
6154
  "use strict";
5868
- ts6 = __toESM(require("typescript"), 1);
6155
+ ts7 = __toESM(require("typescript"), 1);
5869
6156
  init_program();
5870
6157
  }
5871
6158
  });
@@ -5878,17 +6165,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5878
6165
  };
5879
6166
  }
5880
6167
  function isNamedTypeDeclaration(declaration) {
5881
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6168
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5882
6169
  }
5883
6170
  function hasConcreteTypeArguments(type, checker) {
5884
6171
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5885
6172
  return true;
5886
6173
  }
5887
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6174
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5888
6175
  return false;
5889
6176
  }
5890
6177
  const objectType = type;
5891
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6178
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5892
6179
  return false;
5893
6180
  }
5894
6181
  return checker.getTypeArguments(objectType).length > 0;
@@ -5901,13 +6188,13 @@ function getNamedTypeDeclaration2(type) {
5901
6188
  return declaration;
5902
6189
  }
5903
6190
  }
5904
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6191
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5905
6192
  return aliasDeclaration;
5906
6193
  }
5907
6194
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5908
6195
  if (sourceNode !== void 0 && "name" in sourceNode) {
5909
6196
  const namedNode = sourceNode;
5910
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6197
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5911
6198
  return namedNode.name.text;
5912
6199
  }
5913
6200
  }
@@ -6026,17 +6313,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
6026
6313
  rootLogicalName: root.name
6027
6314
  }
6028
6315
  );
6029
- const schema = generateJsonSchemaFromIR(
6030
- ir,
6031
- {
6032
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6033
- extensionRegistry: options?.extensionRegistry,
6034
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6035
- enumSerialization: options?.enumSerialization,
6036
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6037
- vendorPrefix: options?.vendorPrefix
6038
- }
6039
- );
6316
+ const schema = generateJsonSchemaFromIR(ir, {
6317
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6318
+ extensionRegistry: options?.extensionRegistry,
6319
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6320
+ enumSerialization: options?.enumSerialization,
6321
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6322
+ vendorPrefix: options?.vendorPrefix
6323
+ });
6040
6324
  const result = schema.properties?.["__result"];
6041
6325
  if (result === void 0) {
6042
6326
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6135,7 +6419,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6135
6419
  }
6136
6420
  function generateSchemasFromDeclaration(options) {
6137
6421
  const filePath = options.declaration.getSourceFile().fileName;
6138
- if (ts7.isClassDeclaration(options.declaration)) {
6422
+ if (ts8.isClassDeclaration(options.declaration)) {
6139
6423
  return generateSchemasFromAnalysis(
6140
6424
  analyzeClassToIR(
6141
6425
  options.declaration,
@@ -6151,7 +6435,7 @@ function generateSchemasFromDeclaration(options) {
6151
6435
  options
6152
6436
  );
6153
6437
  }
6154
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6438
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6155
6439
  return generateSchemasFromAnalysis(
6156
6440
  analyzeInterfaceToIR(
6157
6441
  options.declaration,
@@ -6167,7 +6451,7 @@ function generateSchemasFromDeclaration(options) {
6167
6451
  options
6168
6452
  );
6169
6453
  }
6170
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6454
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6171
6455
  const analyzedAlias = analyzeTypeAliasToIR(
6172
6456
  options.declaration,
6173
6457
  options.context.checker,
@@ -6230,7 +6514,7 @@ function generateSchemasFromReturnType(options) {
6230
6514
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6231
6515
  const type = unwrapPromiseType(options.context.checker, returnType);
6232
6516
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6233
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6517
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6234
6518
  return generateSchemasFromResolvedType({
6235
6519
  ...options,
6236
6520
  type,
@@ -6265,20 +6549,20 @@ function unwrapPromiseTypeNode(typeNode) {
6265
6549
  if (typeNode === void 0) {
6266
6550
  return void 0;
6267
6551
  }
6268
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6552
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6269
6553
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6270
6554
  return unwrapped ?? typeNode;
6271
6555
  }
6272
6556
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6273
6557
  }
6274
6558
  function isPromiseTypeReferenceNode(typeNode) {
6275
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6559
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6276
6560
  }
6277
- var ts7, import_internal5, import_internals6;
6561
+ var ts8, import_internal5, import_internals6;
6278
6562
  var init_discovered_schema = __esm({
6279
6563
  "src/generators/discovered-schema.ts"() {
6280
6564
  "use strict";
6281
- ts7 = __toESM(require("typescript"), 1);
6565
+ ts8 = __toESM(require("typescript"), 1);
6282
6566
  import_internal5 = require("@formspec/analysis/internal");
6283
6567
  init_class_analyzer();
6284
6568
  init_class_schema();
@@ -6544,13 +6828,13 @@ function writeSchemas(form, options) {
6544
6828
  if (!fs.existsSync(outDir)) {
6545
6829
  fs.mkdirSync(outDir, { recursive: true });
6546
6830
  }
6547
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6548
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6831
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6832
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6549
6833
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6550
6834
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6551
6835
  return { jsonSchemaPath, uiSchemaPath };
6552
6836
  }
6553
- var fs, path2;
6837
+ var fs, path3;
6554
6838
  var init_index = __esm({
6555
6839
  "src/index.ts"() {
6556
6840
  "use strict";
@@ -6558,7 +6842,7 @@ var init_index = __esm({
6558
6842
  init_generator2();
6559
6843
  init_ir_generator();
6560
6844
  fs = __toESM(require("fs"), 1);
6561
- path2 = __toESM(require("path"), 1);
6845
+ path3 = __toESM(require("path"), 1);
6562
6846
  init_extensions();
6563
6847
  init_schema2();
6564
6848
  init_schema();
@@ -6575,7 +6859,7 @@ var init_index = __esm({
6575
6859
  });
6576
6860
 
6577
6861
  // src/cli.ts
6578
- var path3 = __toESM(require("path"), 1);
6862
+ var path4 = __toESM(require("path"), 1);
6579
6863
  var import_node_url = require("url");
6580
6864
  function printHelp() {
6581
6865
  console.log(`
@@ -6664,7 +6948,7 @@ function parseArgs(args) {
6664
6948
  return null;
6665
6949
  }
6666
6950
  if (!name) {
6667
- name = path3.basename(inputFile, path3.extname(inputFile));
6951
+ name = path4.basename(inputFile, path4.extname(inputFile));
6668
6952
  }
6669
6953
  return { inputFile, outDir, name, enumSerialization };
6670
6954
  }
@@ -6675,7 +6959,7 @@ async function main() {
6675
6959
  process.exit(1);
6676
6960
  }
6677
6961
  const { inputFile, outDir, name, enumSerialization } = options;
6678
- const absoluteInput = path3.resolve(process.cwd(), inputFile);
6962
+ const absoluteInput = path4.resolve(process.cwd(), inputFile);
6679
6963
  try {
6680
6964
  const fileUrl = (0, import_node_url.pathToFileURL)(absoluteInput).href;
6681
6965
  const module2 = await import(fileUrl);