@formspec/build 0.1.0-alpha.44 → 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 +33 -7
  6. package/dist/browser.cjs.map +1 -1
  7. package/dist/browser.js +34 -10
  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 +324 -71
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +320 -70
  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 +310 -64
  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 +311 -67
  31. package/dist/index.js.map +1 -1
  32. package/dist/internals.cjs +106 -23
  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 +107 -26
  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/index.cjs CHANGED
@@ -288,7 +288,9 @@ function resolveTypeNodeMetadata(type, options) {
288
288
  case "object":
289
289
  return {
290
290
  ...type,
291
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
291
+ properties: type.properties.map(
292
+ (property) => resolveObjectPropertyMetadata(property, options)
293
+ )
292
294
  };
293
295
  case "record":
294
296
  return {
@@ -1714,10 +1716,7 @@ function generateJsonSchema(form, options) {
1714
1716
  const metadata = options?.metadata;
1715
1717
  const vendorPrefix = options?.vendorPrefix;
1716
1718
  const enumSerialization = options?.enumSerialization;
1717
- const ir = canonicalizeChainDSL(
1718
- form,
1719
- metadata !== void 0 ? { metadata } : void 0
1720
- );
1719
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1721
1720
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1722
1721
  ...vendorPrefix !== void 0 && { vendorPrefix },
1723
1722
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1933,7 +1932,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1933
1932
  break;
1934
1933
  }
1935
1934
  case "conditional": {
1936
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1935
+ const newRule = createShowRule(
1936
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1937
+ element.value
1938
+ );
1937
1939
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1938
1940
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1939
1941
  result.push(...childElements);
@@ -1990,7 +1992,7 @@ function generateUiSchema(form, options) {
1990
1992
 
1991
1993
  // src/index.ts
1992
1994
  var fs = __toESM(require("fs"), 1);
1993
- var path2 = __toESM(require("path"), 1);
1995
+ var path3 = __toESM(require("path"), 1);
1994
1996
 
1995
1997
  // src/extensions/registry.ts
1996
1998
  var import_internals3 = require("@formspec/core/internals");
@@ -2008,8 +2010,10 @@ function buildConstraintTagSources(extensions) {
2008
2010
  }
2009
2011
  function createExtensionRegistry(extensions) {
2010
2012
  const reservedTagSources = buildConstraintTagSources(extensions);
2013
+ let symbolMap = /* @__PURE__ */ new Map();
2011
2014
  const typeMap = /* @__PURE__ */ new Map();
2012
2015
  const typeNameMap = /* @__PURE__ */ new Map();
2016
+ const brandMap = /* @__PURE__ */ new Map();
2013
2017
  const constraintMap = /* @__PURE__ */ new Map();
2014
2018
  const constraintTagMap = /* @__PURE__ */ new Map();
2015
2019
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -2033,6 +2037,20 @@ function createExtensionRegistry(extensions) {
2033
2037
  registration: type
2034
2038
  });
2035
2039
  }
2040
+ if (type.brand !== void 0) {
2041
+ if (type.brand === "__integerBrand") {
2042
+ throw new Error(
2043
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
2044
+ );
2045
+ }
2046
+ if (brandMap.has(type.brand)) {
2047
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
2048
+ }
2049
+ brandMap.set(type.brand, {
2050
+ extensionId: ext.extensionId,
2051
+ registration: type
2052
+ });
2053
+ }
2036
2054
  if (type.builtinConstraintBroadenings !== void 0) {
2037
2055
  for (const broadening of type.builtinConstraintBroadenings) {
2038
2056
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2102,7 +2120,10 @@ function createExtensionRegistry(extensions) {
2102
2120
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2103
2121
  );
2104
2122
  }
2105
- if (Object.hasOwn(import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS, (0, import_internals3.normalizeConstraintTagName)(canonicalTagName))) {
2123
+ if (Object.hasOwn(
2124
+ import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS,
2125
+ (0, import_internals3.normalizeConstraintTagName)(canonicalTagName)
2126
+ )) {
2106
2127
  throw new Error(
2107
2128
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${(0, import_internals3.normalizeConstraintTagName)(canonicalTagName)}".`
2108
2129
  );
@@ -2123,6 +2144,11 @@ function createExtensionRegistry(extensions) {
2123
2144
  extensions,
2124
2145
  findType: (typeId) => typeMap.get(typeId),
2125
2146
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2147
+ findTypeByBrand: (brand) => brandMap.get(brand),
2148
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2149
+ setSymbolMap: (map) => {
2150
+ symbolMap = map;
2151
+ },
2126
2152
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2127
2153
  findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal.normalizeFormSpecTagName)(tagName)),
2128
2154
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -2193,7 +2219,7 @@ var jsonSchema7Schema = import_zod3.z.lazy(
2193
2219
  );
2194
2220
 
2195
2221
  // src/generators/class-schema.ts
2196
- var ts5 = __toESM(require("typescript"), 1);
2222
+ var ts6 = __toESM(require("typescript"), 1);
2197
2223
 
2198
2224
  // src/analyzer/program.ts
2199
2225
  var ts4 = __toESM(require("typescript"), 1);
@@ -2984,30 +3010,70 @@ function isObjectType(type) {
2984
3010
  function isIntersectionType(type) {
2985
3011
  return !!(type.flags & ts3.TypeFlags.Intersection);
2986
3012
  }
3013
+ function collectBrandIdentifiers(type) {
3014
+ if (!type.isIntersection()) {
3015
+ return [];
3016
+ }
3017
+ const brands = [];
3018
+ for (const prop of type.getProperties()) {
3019
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
3020
+ if (decl === void 0) continue;
3021
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
3022
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
3023
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
3024
+ brands.push(decl.name.expression.text);
3025
+ }
3026
+ return brands;
3027
+ }
3028
+ function resolveCanonicalSymbol(type, checker) {
3029
+ const raw = type.aliasSymbol ?? type.getSymbol();
3030
+ if (raw === void 0) return void 0;
3031
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3032
+ }
3033
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3034
+ if (extensionRegistry === void 0) {
3035
+ return null;
3036
+ }
3037
+ const canonical = resolveCanonicalSymbol(type, checker);
3038
+ if (canonical === void 0) {
3039
+ return null;
3040
+ }
3041
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3042
+ if (registration === void 0) {
3043
+ return null;
3044
+ }
3045
+ return {
3046
+ kind: "custom",
3047
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3048
+ payload: null
3049
+ };
3050
+ }
2987
3051
  function isIntegerBrandedType(type) {
2988
3052
  if (!type.isIntersection()) {
2989
3053
  return false;
2990
3054
  }
2991
- const hasNumberBase = type.types.some(
2992
- (member) => !!(member.flags & ts3.TypeFlags.Number)
2993
- );
3055
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
2994
3056
  if (!hasNumberBase) {
2995
3057
  return false;
2996
3058
  }
2997
- return type.getProperties().some((prop) => {
2998
- const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2999
- if (declaration === void 0) {
3000
- return false;
3001
- }
3002
- if (!ts3.isPropertySignature(declaration) && !ts3.isPropertyDeclaration(declaration)) {
3003
- return false;
3004
- }
3005
- const name = declaration.name;
3006
- if (!ts3.isComputedPropertyName(name)) {
3007
- return false;
3059
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3060
+ }
3061
+ function resolveBrandedCustomType(type, extensionRegistry) {
3062
+ if (extensionRegistry === void 0) {
3063
+ return null;
3064
+ }
3065
+ for (const brand of collectBrandIdentifiers(type)) {
3066
+ const registration = extensionRegistry.findTypeByBrand(brand);
3067
+ if (registration === void 0) {
3068
+ continue;
3008
3069
  }
3009
- return ts3.isIdentifier(name.expression) && name.expression.text === "__integerBrand";
3010
- });
3070
+ return {
3071
+ kind: "custom",
3072
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3073
+ payload: null
3074
+ };
3075
+ }
3076
+ return null;
3011
3077
  }
3012
3078
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
3013
3079
  if (ts3.isParenthesizedTypeNode(typeNode)) {
@@ -4120,6 +4186,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4120
4186
  if (customType) {
4121
4187
  return customType;
4122
4188
  }
4189
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4190
+ if (symbolCustomType) {
4191
+ return symbolCustomType;
4192
+ }
4193
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4194
+ if (brandedCustomType) {
4195
+ return brandedCustomType;
4196
+ }
4123
4197
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4124
4198
  type,
4125
4199
  checker,
@@ -5064,7 +5138,7 @@ function createProgramContextFromProgram(program, filePath) {
5064
5138
  sourceFile
5065
5139
  };
5066
5140
  }
5067
- function createProgramContext(filePath) {
5141
+ function createProgramContext(filePath, additionalFiles) {
5068
5142
  const absolutePath = path.resolve(filePath);
5069
5143
  const fileDir = path.dirname(absolutePath);
5070
5144
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5087,7 +5161,8 @@ function createProgramContext(filePath) {
5087
5161
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5088
5162
  }
5089
5163
  compilerOptions = parsed.options;
5090
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5164
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5165
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5091
5166
  } else {
5092
5167
  compilerOptions = {
5093
5168
  target: ts4.ScriptTarget.ES2022,
@@ -5097,7 +5172,8 @@ function createProgramContext(filePath) {
5097
5172
  skipLibCheck: true,
5098
5173
  declaration: true
5099
5174
  };
5100
- fileNames = [absolutePath];
5175
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5176
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5101
5177
  }
5102
5178
  const program = ts4.createProgram(fileNames, compilerOptions);
5103
5179
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5401,6 +5477,132 @@ function makeFileProvenance(filePath) {
5401
5477
  };
5402
5478
  }
5403
5479
 
5480
+ // src/extensions/symbol-registry.ts
5481
+ var ts5 = __toESM(require("typescript"), 1);
5482
+ var path2 = __toESM(require("path"), 1);
5483
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5484
+ const symbolMap = /* @__PURE__ */ new Map();
5485
+ const normalizedPath = path2.resolve(configPath);
5486
+ const configFile = program.getSourceFile(normalizedPath);
5487
+ if (configFile === void 0) {
5488
+ return symbolMap;
5489
+ }
5490
+ function visit(node) {
5491
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5492
+ processDefineCustomTypeCall(node);
5493
+ }
5494
+ ts5.forEachChild(node, visit);
5495
+ }
5496
+ function processDefineCustomTypeCall(call) {
5497
+ const typeArgNode = call.typeArguments?.[0];
5498
+ if (typeArgNode === void 0) {
5499
+ return;
5500
+ }
5501
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5502
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5503
+ if (canonical === void 0) {
5504
+ return;
5505
+ }
5506
+ const typeName = extractTypeNameFromCallArg(call);
5507
+ if (typeName === null) {
5508
+ return;
5509
+ }
5510
+ let entry;
5511
+ const extensionId = extractEnclosingExtensionId(call, checker);
5512
+ if (extensionId !== null) {
5513
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5514
+ if (reg !== void 0) {
5515
+ entry = { extensionId, registration: reg };
5516
+ }
5517
+ }
5518
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5519
+ if (entry === void 0) {
5520
+ return;
5521
+ }
5522
+ symbolMap.set(canonical, entry);
5523
+ }
5524
+ visit(configFile);
5525
+ return symbolMap;
5526
+ }
5527
+ function isDefineCustomTypeCall(node, checker) {
5528
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5529
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5530
+ if (callSymbol !== void 0) {
5531
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5532
+ const decl = resolved.declarations?.[0];
5533
+ if (decl !== void 0) {
5534
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5535
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5536
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5537
+ }
5538
+ }
5539
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5540
+ }
5541
+ function resolveCanonicalSymbol2(type, checker) {
5542
+ const raw = type.aliasSymbol ?? type.getSymbol();
5543
+ if (raw === void 0) return void 0;
5544
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5545
+ }
5546
+ function extractTypeNameFromCallArg(call) {
5547
+ const arg = call.arguments[0];
5548
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5549
+ return null;
5550
+ }
5551
+ const typeNameProp = arg.properties.find(
5552
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5553
+ );
5554
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5555
+ return null;
5556
+ }
5557
+ return typeNameProp.initializer.text;
5558
+ }
5559
+ function extractEnclosingExtensionId(call, checker) {
5560
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5561
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5562
+ return extractExtensionIdFromCallArg(node);
5563
+ }
5564
+ }
5565
+ return null;
5566
+ }
5567
+ function isDefineExtensionCall(node, checker) {
5568
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5569
+ if (callSymbol !== void 0) {
5570
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5571
+ const decl = resolved.declarations?.[0];
5572
+ if (decl !== void 0) {
5573
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5574
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5575
+ }
5576
+ }
5577
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5578
+ }
5579
+ function extractExtensionIdFromCallArg(call) {
5580
+ const arg = call.arguments[0];
5581
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5582
+ return null;
5583
+ }
5584
+ const prop = arg.properties.find(
5585
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5586
+ );
5587
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5588
+ return null;
5589
+ }
5590
+ return prop.initializer.text;
5591
+ }
5592
+ function findRegistrationByTypeName(registry, typeName) {
5593
+ for (const ext of registry.extensions) {
5594
+ if (ext.types === void 0) {
5595
+ continue;
5596
+ }
5597
+ for (const type of ext.types) {
5598
+ if (type.typeName === typeName) {
5599
+ return { extensionId: ext.extensionId, registration: type };
5600
+ }
5601
+ }
5602
+ }
5603
+ return void 0;
5604
+ }
5605
+
5404
5606
  // src/validate/constraint-validator.ts
5405
5607
  var import_internal4 = require("@formspec/analysis/internal");
5406
5608
  function validateFieldNode(ctx, field) {
@@ -5533,7 +5735,8 @@ function formatLocation(location) {
5533
5735
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5534
5736
  }
5535
5737
  function generateSchemasFromClass(options) {
5536
- const ctx = createProgramContext(options.filePath);
5738
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5739
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5537
5740
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5538
5741
  if (!classDecl) {
5539
5742
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5598,7 +5801,8 @@ function generateSchemasDetailed(options) {
5598
5801
  function generateSchemasDetailedInternal(options) {
5599
5802
  let ctx;
5600
5803
  try {
5601
- ctx = createProgramContext(options.filePath);
5804
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5805
+ ctx = createProgramContext(options.filePath, additionalFiles);
5602
5806
  } catch (error) {
5603
5807
  return {
5604
5808
  ok: false,
@@ -5637,13 +5841,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5637
5841
  }
5638
5842
  function generateSchemasBatch(options) {
5639
5843
  const contextCache = /* @__PURE__ */ new Map();
5844
+ const resolved = resolveOptions(options);
5845
+ let symbolMapProgram;
5640
5846
  return options.targets.map((target) => {
5641
5847
  let ctx;
5642
5848
  try {
5643
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5849
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5644
5850
  const cachedContext = contextCache.get(cacheKey);
5645
5851
  if (cachedContext === void 0) {
5646
- ctx = createProgramContext(target.filePath);
5852
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5853
+ ctx = createProgramContext(target.filePath, additionalFiles);
5647
5854
  contextCache.set(cacheKey, ctx);
5648
5855
  } else {
5649
5856
  ctx = cachedContext;
@@ -5654,9 +5861,25 @@ function generateSchemasBatch(options) {
5654
5861
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5655
5862
  });
5656
5863
  }
5864
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5865
+ const symbolMap = buildSymbolMapFromConfig(
5866
+ options.configPath,
5867
+ ctx.program,
5868
+ ctx.checker,
5869
+ resolved.extensionRegistry
5870
+ );
5871
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5872
+ symbolMapProgram = ctx.program;
5873
+ }
5657
5874
  return withTarget(
5658
5875
  target,
5659
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5876
+ generateSchemasFromResolvedOptions(
5877
+ ctx,
5878
+ target.filePath,
5879
+ target.typeName,
5880
+ resolved,
5881
+ options.discriminator
5882
+ )
5660
5883
  );
5661
5884
  });
5662
5885
  }
@@ -5677,11 +5900,19 @@ function generateSchemasBatchFromProgram(options) {
5677
5900
  );
5678
5901
  });
5679
5902
  }
5903
+ function isMutableRegistry(reg) {
5904
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
5905
+ }
5680
5906
  function resolveOptions(options) {
5681
5907
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5908
+ const legacyRegistry = options.extensionRegistry;
5682
5909
  return {
5683
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5684
- extensionRegistry: options.extensionRegistry ?? configRegistry,
5910
+ // When the caller provides the deprecated extensionRegistry field directly,
5911
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
5912
+ // because the legacy path was introduced before MutableExtensionRegistry was
5913
+ // split out; callers using createExtensionRegistry() always get a mutable
5914
+ // registry, and this cast is safe for all registries produced by this module.
5915
+ extensionRegistry: legacyRegistry ?? configRegistry,
5685
5916
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5686
5917
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5687
5918
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5692,13 +5923,31 @@ function resolveOptions(options) {
5692
5923
  }
5693
5924
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5694
5925
  const resolved = resolveOptions(options);
5926
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
5927
+ const symbolMap = buildSymbolMapFromConfig(
5928
+ options.configPath,
5929
+ ctx.program,
5930
+ ctx.checker,
5931
+ resolved.extensionRegistry
5932
+ );
5933
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5934
+ }
5935
+ return generateSchemasFromResolvedOptions(
5936
+ ctx,
5937
+ filePath,
5938
+ typeName,
5939
+ resolved,
5940
+ options.discriminator
5941
+ );
5942
+ }
5943
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5695
5944
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5696
5945
  ctx,
5697
5946
  filePath,
5698
5947
  typeName,
5699
5948
  resolved.extensionRegistry,
5700
5949
  resolved.metadata,
5701
- options.discriminator
5950
+ discriminator
5702
5951
  );
5703
5952
  if (!analysisResult.ok) {
5704
5953
  return {
@@ -5740,7 +5989,7 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5740
5989
  }
5741
5990
 
5742
5991
  // src/static-build.ts
5743
- var ts6 = __toESM(require("typescript"), 1);
5992
+ var ts7 = __toESM(require("typescript"), 1);
5744
5993
  function toStaticBuildContext(context) {
5745
5994
  return context;
5746
5995
  }
@@ -5755,7 +6004,7 @@ function getModuleSymbol(context) {
5755
6004
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5756
6005
  }
5757
6006
  function isSchemaSourceDeclaration(declaration) {
5758
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6007
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5759
6008
  }
5760
6009
  function resolveModuleExport(context, exportName = "default") {
5761
6010
  const moduleSymbol = getModuleSymbol(context);
@@ -5766,14 +6015,14 @@ function resolveModuleExport(context, exportName = "default") {
5766
6015
  if (exportSymbol === null) {
5767
6016
  return null;
5768
6017
  }
5769
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6018
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5770
6019
  }
5771
6020
  function resolveModuleExportDeclaration(context, exportName = "default") {
5772
6021
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5773
6022
  }
5774
6023
 
5775
6024
  // src/generators/discovered-schema.ts
5776
- var ts7 = __toESM(require("typescript"), 1);
6025
+ var ts8 = __toESM(require("typescript"), 1);
5777
6026
  var import_internal5 = require("@formspec/analysis/internal");
5778
6027
  var import_internals6 = require("@formspec/core/internals");
5779
6028
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5783,17 +6032,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5783
6032
  };
5784
6033
  }
5785
6034
  function isNamedTypeDeclaration(declaration) {
5786
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6035
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5787
6036
  }
5788
6037
  function hasConcreteTypeArguments(type, checker) {
5789
6038
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5790
6039
  return true;
5791
6040
  }
5792
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6041
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5793
6042
  return false;
5794
6043
  }
5795
6044
  const objectType = type;
5796
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6045
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5797
6046
  return false;
5798
6047
  }
5799
6048
  return checker.getTypeArguments(objectType).length > 0;
@@ -5806,13 +6055,13 @@ function getNamedTypeDeclaration2(type) {
5806
6055
  return declaration;
5807
6056
  }
5808
6057
  }
5809
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6058
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5810
6059
  return aliasDeclaration;
5811
6060
  }
5812
6061
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5813
6062
  if (sourceNode !== void 0 && "name" in sourceNode) {
5814
6063
  const namedNode = sourceNode;
5815
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6064
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5816
6065
  return namedNode.name.text;
5817
6066
  }
5818
6067
  }
@@ -5931,17 +6180,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5931
6180
  rootLogicalName: root.name
5932
6181
  }
5933
6182
  );
5934
- const schema = generateJsonSchemaFromIR(
5935
- ir,
5936
- {
5937
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5938
- extensionRegistry: options?.extensionRegistry,
5939
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5940
- enumSerialization: options?.enumSerialization,
5941
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5942
- vendorPrefix: options?.vendorPrefix
5943
- }
5944
- );
6183
+ const schema = generateJsonSchemaFromIR(ir, {
6184
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6185
+ extensionRegistry: options?.extensionRegistry,
6186
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6187
+ enumSerialization: options?.enumSerialization,
6188
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6189
+ vendorPrefix: options?.vendorPrefix
6190
+ });
5945
6191
  const result = schema.properties?.["__result"];
5946
6192
  if (result === void 0) {
5947
6193
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6040,7 +6286,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6040
6286
  }
6041
6287
  function generateSchemasFromDeclaration(options) {
6042
6288
  const filePath = options.declaration.getSourceFile().fileName;
6043
- if (ts7.isClassDeclaration(options.declaration)) {
6289
+ if (ts8.isClassDeclaration(options.declaration)) {
6044
6290
  return generateSchemasFromAnalysis(
6045
6291
  analyzeClassToIR(
6046
6292
  options.declaration,
@@ -6056,7 +6302,7 @@ function generateSchemasFromDeclaration(options) {
6056
6302
  options
6057
6303
  );
6058
6304
  }
6059
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6305
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6060
6306
  return generateSchemasFromAnalysis(
6061
6307
  analyzeInterfaceToIR(
6062
6308
  options.declaration,
@@ -6072,7 +6318,7 @@ function generateSchemasFromDeclaration(options) {
6072
6318
  options
6073
6319
  );
6074
6320
  }
6075
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6321
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6076
6322
  const analyzedAlias = analyzeTypeAliasToIR(
6077
6323
  options.declaration,
6078
6324
  options.context.checker,
@@ -6135,7 +6381,7 @@ function generateSchemasFromReturnType(options) {
6135
6381
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6136
6382
  const type = unwrapPromiseType(options.context.checker, returnType);
6137
6383
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6138
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6384
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6139
6385
  return generateSchemasFromResolvedType({
6140
6386
  ...options,
6141
6387
  type,
@@ -6170,14 +6416,14 @@ function unwrapPromiseTypeNode(typeNode) {
6170
6416
  if (typeNode === void 0) {
6171
6417
  return void 0;
6172
6418
  }
6173
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6419
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6174
6420
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6175
6421
  return unwrapped ?? typeNode;
6176
6422
  }
6177
6423
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6178
6424
  }
6179
6425
  function isPromiseTypeReferenceNode(typeNode) {
6180
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6426
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6181
6427
  }
6182
6428
 
6183
6429
  // src/generators/mixed-authoring.ts
@@ -6399,8 +6645,8 @@ function writeSchemas(form, options) {
6399
6645
  if (!fs.existsSync(outDir)) {
6400
6646
  fs.mkdirSync(outDir, { recursive: true });
6401
6647
  }
6402
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6403
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6648
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6649
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6404
6650
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6405
6651
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6406
6652
  return { jsonSchemaPath, uiSchemaPath };