@formspec/build 0.1.0-alpha.44 → 0.1.0-alpha.46

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 +364 -116
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +360 -115
  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 +32 -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 +350 -109
  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 +351 -112
  31. package/dist/index.js.map +1 -1
  32. package/dist/internals.cjs +121 -23
  33. package/dist/internals.cjs.map +1 -1
  34. package/dist/internals.d.ts +2 -2
  35. package/dist/internals.d.ts.map +1 -1
  36. package/dist/internals.js +121 -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,32 @@ function generateSchemasBatchFromProgram(options) {
5677
5900
  );
5678
5901
  });
5679
5902
  }
5903
+ function isMutableRegistry(reg) {
5904
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
5905
+ }
5906
+ function resolveStaticOptions(options) {
5907
+ const legacyRegistry = options.extensionRegistry;
5908
+ const configRegistry = legacyRegistry === void 0 && options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5909
+ return {
5910
+ extensionRegistry: legacyRegistry ?? configRegistry,
5911
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5912
+ vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5913
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5914
+ enumSerialization: options.enumSerialization ?? options.config?.enumSerialization,
5915
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5916
+ metadata: options.metadata ?? options.config?.metadata
5917
+ };
5918
+ }
5680
5919
  function resolveOptions(options) {
5681
5920
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5921
+ const legacyRegistry = options.extensionRegistry;
5682
5922
  return {
5683
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5684
- extensionRegistry: options.extensionRegistry ?? configRegistry,
5923
+ // When the caller provides the deprecated extensionRegistry field directly,
5924
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
5925
+ // because the legacy path was introduced before MutableExtensionRegistry was
5926
+ // split out; callers using createExtensionRegistry() always get a mutable
5927
+ // registry, and this cast is safe for all registries produced by this module.
5928
+ extensionRegistry: legacyRegistry ?? configRegistry,
5685
5929
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5686
5930
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5687
5931
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5692,13 +5936,31 @@ function resolveOptions(options) {
5692
5936
  }
5693
5937
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5694
5938
  const resolved = resolveOptions(options);
5939
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
5940
+ const symbolMap = buildSymbolMapFromConfig(
5941
+ options.configPath,
5942
+ ctx.program,
5943
+ ctx.checker,
5944
+ resolved.extensionRegistry
5945
+ );
5946
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5947
+ }
5948
+ return generateSchemasFromResolvedOptions(
5949
+ ctx,
5950
+ filePath,
5951
+ typeName,
5952
+ resolved,
5953
+ options.discriminator
5954
+ );
5955
+ }
5956
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5695
5957
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5696
5958
  ctx,
5697
5959
  filePath,
5698
5960
  typeName,
5699
5961
  resolved.extensionRegistry,
5700
5962
  resolved.metadata,
5701
- options.discriminator
5963
+ discriminator
5702
5964
  );
5703
5965
  if (!analysisResult.ok) {
5704
5966
  return {
@@ -5740,7 +6002,7 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5740
6002
  }
5741
6003
 
5742
6004
  // src/static-build.ts
5743
- var ts6 = __toESM(require("typescript"), 1);
6005
+ var ts7 = __toESM(require("typescript"), 1);
5744
6006
  function toStaticBuildContext(context) {
5745
6007
  return context;
5746
6008
  }
@@ -5755,7 +6017,7 @@ function getModuleSymbol(context) {
5755
6017
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5756
6018
  }
5757
6019
  function isSchemaSourceDeclaration(declaration) {
5758
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6020
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5759
6021
  }
5760
6022
  function resolveModuleExport(context, exportName = "default") {
5761
6023
  const moduleSymbol = getModuleSymbol(context);
@@ -5766,14 +6028,14 @@ function resolveModuleExport(context, exportName = "default") {
5766
6028
  if (exportSymbol === null) {
5767
6029
  return null;
5768
6030
  }
5769
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6031
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5770
6032
  }
5771
6033
  function resolveModuleExportDeclaration(context, exportName = "default") {
5772
6034
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5773
6035
  }
5774
6036
 
5775
6037
  // src/generators/discovered-schema.ts
5776
- var ts7 = __toESM(require("typescript"), 1);
6038
+ var ts8 = __toESM(require("typescript"), 1);
5777
6039
  var import_internal5 = require("@formspec/analysis/internal");
5778
6040
  var import_internals6 = require("@formspec/core/internals");
5779
6041
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5783,17 +6045,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5783
6045
  };
5784
6046
  }
5785
6047
  function isNamedTypeDeclaration(declaration) {
5786
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6048
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5787
6049
  }
5788
6050
  function hasConcreteTypeArguments(type, checker) {
5789
6051
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5790
6052
  return true;
5791
6053
  }
5792
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6054
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5793
6055
  return false;
5794
6056
  }
5795
6057
  const objectType = type;
5796
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6058
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5797
6059
  return false;
5798
6060
  }
5799
6061
  return checker.getTypeArguments(objectType).length > 0;
@@ -5806,13 +6068,13 @@ function getNamedTypeDeclaration2(type) {
5806
6068
  return declaration;
5807
6069
  }
5808
6070
  }
5809
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6071
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5810
6072
  return aliasDeclaration;
5811
6073
  }
5812
6074
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5813
6075
  if (sourceNode !== void 0 && "name" in sourceNode) {
5814
6076
  const namedNode = sourceNode;
5815
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6077
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5816
6078
  return namedNode.name.text;
5817
6079
  }
5818
6080
  }
@@ -5848,10 +6110,9 @@ function omitApiName(metadata) {
5848
6110
  const { apiName: _apiName, ...rest } = metadata;
5849
6111
  return Object.keys(rest).length > 0 ? rest : void 0;
5850
6112
  }
5851
- function enforceRequiredMetadata(metadata, declarationKind, logicalName, options) {
6113
+ function enforceRequiredMetadata(metadata, declarationKind, logicalName, metadataPolicy) {
5852
6114
  const declarationPolicy = getDeclarationMetadataPolicy(
5853
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5854
- normalizeMetadataPolicy(options.metadata),
6115
+ normalizeMetadataPolicy(metadataPolicy),
5855
6116
  declarationKind
5856
6117
  );
5857
6118
  if (metadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
@@ -5896,7 +6157,7 @@ function describeRootType(rootType, typeRegistry, fallbackName) {
5896
6157
  type: definition.type
5897
6158
  };
5898
6159
  }
5899
- function toStandaloneJsonSchema(root, typeRegistry, options) {
6160
+ function toStandaloneJsonSchema(root, typeRegistry, resolved) {
5900
6161
  const syntheticFieldMetadata = omitApiName(root.metadata);
5901
6162
  const syntheticField = {
5902
6163
  kind: "field",
@@ -5925,23 +6186,16 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5925
6186
  provenance: syntheticField.provenance
5926
6187
  },
5927
6188
  {
5928
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5929
- policy: normalizeMetadataPolicy(options?.metadata),
6189
+ policy: normalizeMetadataPolicy(resolved?.metadata),
5930
6190
  surface: "tsdoc",
5931
6191
  rootLogicalName: root.name
5932
6192
  }
5933
6193
  );
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
- );
6194
+ const schema = generateJsonSchemaFromIR(ir, {
6195
+ extensionRegistry: resolved?.extensionRegistry,
6196
+ enumSerialization: resolved?.enumSerialization,
6197
+ vendorPrefix: resolved?.vendorPrefix
6198
+ });
5945
6199
  const result = schema.properties?.["__result"];
5946
6200
  if (result === void 0) {
5947
6201
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -5958,26 +6212,23 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5958
6212
  $defs: schema.$defs
5959
6213
  };
5960
6214
  }
5961
- function generateSchemasFromAnalysis(analysis, filePath, options) {
6215
+ function generateSchemasFromAnalysis(analysis, filePath, resolved) {
5962
6216
  return toDiscoveredTypeSchemas(
5963
6217
  generateClassSchemas(
5964
6218
  analysis,
5965
6219
  { file: filePath },
5966
6220
  {
5967
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5968
- extensionRegistry: options?.extensionRegistry,
5969
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5970
- enumSerialization: options?.enumSerialization,
5971
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5972
- metadata: options?.metadata,
5973
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5974
- vendorPrefix: options?.vendorPrefix
6221
+ extensionRegistry: resolved?.extensionRegistry,
6222
+ enumSerialization: resolved?.enumSerialization,
6223
+ metadata: resolved?.metadata,
6224
+ vendorPrefix: resolved?.vendorPrefix
5975
6225
  }
5976
6226
  ),
5977
6227
  analysis.metadata
5978
6228
  );
5979
6229
  }
5980
6230
  function generateSchemasFromResolvedType(options, skipNamedDeclaration = false, rootOverride) {
6231
+ const resolved = resolveStaticOptions(options);
5981
6232
  const namedDeclaration = skipNamedDeclaration || hasConcreteTypeArguments(options.type, options.context.checker) ? void 0 : getNamedTypeDeclaration2(options.type);
5982
6233
  if (namedDeclaration !== void 0) {
5983
6234
  return generateSchemasFromDeclaration({
@@ -5995,10 +6246,8 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
5995
6246
  typeRegistry,
5996
6247
  /* @__PURE__ */ new Set(),
5997
6248
  options.sourceNode,
5998
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5999
- createAnalyzerMetadataPolicy(options.metadata, options.discriminator),
6000
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6001
- options.extensionRegistry,
6249
+ createAnalyzerMetadataPolicy(resolved.metadata, options.discriminator),
6250
+ resolved.extensionRegistry,
6002
6251
  diagnostics
6003
6252
  );
6004
6253
  if (diagnostics.length > 0) {
@@ -6029,71 +6278,64 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6029
6278
  root.annotations
6030
6279
  ),
6031
6280
  filePath,
6032
- options
6281
+ resolved
6033
6282
  );
6034
6283
  }
6035
6284
  return {
6036
- jsonSchema: toStandaloneJsonSchema(root, typeRegistry, options),
6285
+ jsonSchema: toStandaloneJsonSchema(root, typeRegistry, resolved),
6037
6286
  uiSchema: null,
6038
6287
  ...root.metadata !== void 0 && { resolvedMetadata: root.metadata }
6039
6288
  };
6040
6289
  }
6041
6290
  function generateSchemasFromDeclaration(options) {
6042
6291
  const filePath = options.declaration.getSourceFile().fileName;
6043
- if (ts7.isClassDeclaration(options.declaration)) {
6292
+ const resolved = resolveStaticOptions(options);
6293
+ if (ts8.isClassDeclaration(options.declaration)) {
6044
6294
  return generateSchemasFromAnalysis(
6045
6295
  analyzeClassToIR(
6046
6296
  options.declaration,
6047
6297
  options.context.checker,
6048
6298
  filePath,
6049
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6050
- options.extensionRegistry,
6051
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6052
- options.metadata,
6299
+ resolved.extensionRegistry,
6300
+ resolved.metadata,
6053
6301
  options.discriminator
6054
6302
  ),
6055
6303
  filePath,
6056
- options
6304
+ resolved
6057
6305
  );
6058
6306
  }
6059
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6307
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6060
6308
  return generateSchemasFromAnalysis(
6061
6309
  analyzeInterfaceToIR(
6062
6310
  options.declaration,
6063
6311
  options.context.checker,
6064
6312
  filePath,
6065
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6066
- options.extensionRegistry,
6067
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6068
- options.metadata,
6313
+ resolved.extensionRegistry,
6314
+ resolved.metadata,
6069
6315
  options.discriminator
6070
6316
  ),
6071
6317
  filePath,
6072
- options
6318
+ resolved
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,
6079
6325
  filePath,
6080
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6081
- options.extensionRegistry,
6082
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6083
- options.metadata,
6326
+ resolved.extensionRegistry,
6327
+ resolved.metadata,
6084
6328
  options.discriminator
6085
6329
  );
6086
6330
  if (analyzedAlias.ok) {
6087
- return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, options);
6331
+ return generateSchemasFromAnalysis(analyzedAlias.analysis, filePath, resolved);
6088
6332
  }
6089
6333
  const aliasRootInfo = analyzeDeclarationRootInfo(
6090
6334
  options.declaration,
6091
6335
  options.context.checker,
6092
6336
  filePath,
6093
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6094
- options.extensionRegistry,
6095
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6096
- options.metadata
6337
+ resolved.extensionRegistry,
6338
+ resolved.metadata
6097
6339
  );
6098
6340
  if (aliasRootInfo.diagnostics.length > 0) {
6099
6341
  const diagnosticDetails = aliasRootInfo.diagnostics.map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`).join("; ");
@@ -6135,7 +6377,7 @@ function generateSchemasFromReturnType(options) {
6135
6377
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6136
6378
  const type = unwrapPromiseType(options.context.checker, returnType);
6137
6379
  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";
6380
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6139
6381
  return generateSchemasFromResolvedType({
6140
6382
  ...options,
6141
6383
  type,
@@ -6144,20 +6386,19 @@ function generateSchemasFromReturnType(options) {
6144
6386
  });
6145
6387
  }
6146
6388
  function resolveDeclarationMetadata(options) {
6389
+ const resolved = resolveStaticOptions(options);
6147
6390
  const analysis = (0, import_internal5.analyzeMetadataForNodeWithChecker)({
6148
6391
  checker: options.context.checker,
6149
6392
  node: options.declaration,
6150
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6151
- metadata: options.metadata,
6152
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6153
- extensions: options.extensionRegistry?.extensions,
6393
+ metadata: resolved.metadata,
6394
+ extensions: resolved.extensionRegistry?.extensions,
6154
6395
  buildContext: options.context
6155
6396
  });
6156
6397
  if (analysis === null) {
6157
6398
  return void 0;
6158
6399
  }
6159
6400
  const metadata = analysis.resolvedMetadata;
6160
- enforceRequiredMetadata(metadata, analysis.declarationKind, analysis.logicalName, options);
6401
+ enforceRequiredMetadata(metadata, analysis.declarationKind, analysis.logicalName, resolved.metadata);
6161
6402
  return metadata;
6162
6403
  }
6163
6404
  function unwrapPromiseType(checker, type) {
@@ -6170,14 +6411,14 @@ function unwrapPromiseTypeNode(typeNode) {
6170
6411
  if (typeNode === void 0) {
6171
6412
  return void 0;
6172
6413
  }
6173
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6414
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6174
6415
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6175
6416
  return unwrapped ?? typeNode;
6176
6417
  }
6177
6418
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6178
6419
  }
6179
6420
  function isPromiseTypeReferenceNode(typeNode) {
6180
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6421
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6181
6422
  }
6182
6423
 
6183
6424
  // src/generators/mixed-authoring.ts
@@ -6399,8 +6640,8 @@ function writeSchemas(form, options) {
6399
6640
  if (!fs.existsSync(outDir)) {
6400
6641
  fs.mkdirSync(outDir, { recursive: true });
6401
6642
  }
6402
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6403
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6643
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6644
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6404
6645
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6405
6646
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6406
6647
  return { jsonSchemaPath, uiSchemaPath };