@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.js CHANGED
@@ -229,7 +229,9 @@ function resolveTypeNodeMetadata(type, options) {
229
229
  case "object":
230
230
  return {
231
231
  ...type,
232
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
232
+ properties: type.properties.map(
233
+ (property) => resolveObjectPropertyMetadata(property, options)
234
+ )
233
235
  };
234
236
  case "record":
235
237
  return {
@@ -1655,10 +1657,7 @@ function generateJsonSchema(form, options) {
1655
1657
  const metadata = options?.metadata;
1656
1658
  const vendorPrefix = options?.vendorPrefix;
1657
1659
  const enumSerialization = options?.enumSerialization;
1658
- const ir = canonicalizeChainDSL(
1659
- form,
1660
- metadata !== void 0 ? { metadata } : void 0
1661
- );
1660
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1662
1661
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1663
1662
  ...vendorPrefix !== void 0 && { vendorPrefix },
1664
1663
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1874,7 +1873,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1874
1873
  break;
1875
1874
  }
1876
1875
  case "conditional": {
1877
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1876
+ const newRule = createShowRule(
1877
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1878
+ element.value
1879
+ );
1878
1880
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1879
1881
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1880
1882
  result.push(...childElements);
@@ -1931,7 +1933,7 @@ function generateUiSchema(form, options) {
1931
1933
 
1932
1934
  // src/index.ts
1933
1935
  import * as fs from "fs";
1934
- import * as path2 from "path";
1936
+ import * as path3 from "path";
1935
1937
 
1936
1938
  // src/extensions/registry.ts
1937
1939
  import {
@@ -1955,8 +1957,10 @@ function buildConstraintTagSources(extensions) {
1955
1957
  }
1956
1958
  function createExtensionRegistry(extensions) {
1957
1959
  const reservedTagSources = buildConstraintTagSources(extensions);
1960
+ let symbolMap = /* @__PURE__ */ new Map();
1958
1961
  const typeMap = /* @__PURE__ */ new Map();
1959
1962
  const typeNameMap = /* @__PURE__ */ new Map();
1963
+ const brandMap = /* @__PURE__ */ new Map();
1960
1964
  const constraintMap = /* @__PURE__ */ new Map();
1961
1965
  const constraintTagMap = /* @__PURE__ */ new Map();
1962
1966
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -1980,6 +1984,20 @@ function createExtensionRegistry(extensions) {
1980
1984
  registration: type
1981
1985
  });
1982
1986
  }
1987
+ if (type.brand !== void 0) {
1988
+ if (type.brand === "__integerBrand") {
1989
+ throw new Error(
1990
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
1991
+ );
1992
+ }
1993
+ if (brandMap.has(type.brand)) {
1994
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
1995
+ }
1996
+ brandMap.set(type.brand, {
1997
+ extensionId: ext.extensionId,
1998
+ registration: type
1999
+ });
2000
+ }
1983
2001
  if (type.builtinConstraintBroadenings !== void 0) {
1984
2002
  for (const broadening of type.builtinConstraintBroadenings) {
1985
2003
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2049,7 +2067,10 @@ function createExtensionRegistry(extensions) {
2049
2067
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2050
2068
  );
2051
2069
  }
2052
- if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS, normalizeConstraintTagName(canonicalTagName))) {
2070
+ if (Object.hasOwn(
2071
+ BUILTIN_CONSTRAINT_DEFINITIONS,
2072
+ normalizeConstraintTagName(canonicalTagName)
2073
+ )) {
2053
2074
  throw new Error(
2054
2075
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName(canonicalTagName)}".`
2055
2076
  );
@@ -2070,6 +2091,11 @@ function createExtensionRegistry(extensions) {
2070
2091
  extensions,
2071
2092
  findType: (typeId) => typeMap.get(typeId),
2072
2093
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2094
+ findTypeByBrand: (brand) => brandMap.get(brand),
2095
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2096
+ setSymbolMap: (map) => {
2097
+ symbolMap = map;
2098
+ },
2073
2099
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2074
2100
  findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName(tagName)),
2075
2101
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -2140,7 +2166,7 @@ var jsonSchema7Schema = z3.lazy(
2140
2166
  );
2141
2167
 
2142
2168
  // src/generators/class-schema.ts
2143
- import * as ts5 from "typescript";
2169
+ import * as ts6 from "typescript";
2144
2170
 
2145
2171
  // src/analyzer/program.ts
2146
2172
  import * as ts4 from "typescript";
@@ -2952,30 +2978,70 @@ function isObjectType(type) {
2952
2978
  function isIntersectionType(type) {
2953
2979
  return !!(type.flags & ts3.TypeFlags.Intersection);
2954
2980
  }
2981
+ function collectBrandIdentifiers(type) {
2982
+ if (!type.isIntersection()) {
2983
+ return [];
2984
+ }
2985
+ const brands = [];
2986
+ for (const prop of type.getProperties()) {
2987
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
2988
+ if (decl === void 0) continue;
2989
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
2990
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
2991
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
2992
+ brands.push(decl.name.expression.text);
2993
+ }
2994
+ return brands;
2995
+ }
2996
+ function resolveCanonicalSymbol(type, checker) {
2997
+ const raw = type.aliasSymbol ?? type.getSymbol();
2998
+ if (raw === void 0) return void 0;
2999
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3000
+ }
3001
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3002
+ if (extensionRegistry === void 0) {
3003
+ return null;
3004
+ }
3005
+ const canonical = resolveCanonicalSymbol(type, checker);
3006
+ if (canonical === void 0) {
3007
+ return null;
3008
+ }
3009
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3010
+ if (registration === void 0) {
3011
+ return null;
3012
+ }
3013
+ return {
3014
+ kind: "custom",
3015
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3016
+ payload: null
3017
+ };
3018
+ }
2955
3019
  function isIntegerBrandedType(type) {
2956
3020
  if (!type.isIntersection()) {
2957
3021
  return false;
2958
3022
  }
2959
- const hasNumberBase = type.types.some(
2960
- (member) => !!(member.flags & ts3.TypeFlags.Number)
2961
- );
3023
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
2962
3024
  if (!hasNumberBase) {
2963
3025
  return false;
2964
3026
  }
2965
- return type.getProperties().some((prop) => {
2966
- const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
2967
- if (declaration === void 0) {
2968
- return false;
2969
- }
2970
- if (!ts3.isPropertySignature(declaration) && !ts3.isPropertyDeclaration(declaration)) {
2971
- return false;
2972
- }
2973
- const name = declaration.name;
2974
- if (!ts3.isComputedPropertyName(name)) {
2975
- return false;
3027
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3028
+ }
3029
+ function resolveBrandedCustomType(type, extensionRegistry) {
3030
+ if (extensionRegistry === void 0) {
3031
+ return null;
3032
+ }
3033
+ for (const brand of collectBrandIdentifiers(type)) {
3034
+ const registration = extensionRegistry.findTypeByBrand(brand);
3035
+ if (registration === void 0) {
3036
+ continue;
2976
3037
  }
2977
- return ts3.isIdentifier(name.expression) && name.expression.text === "__integerBrand";
2978
- });
3038
+ return {
3039
+ kind: "custom",
3040
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3041
+ payload: null
3042
+ };
3043
+ }
3044
+ return null;
2979
3045
  }
2980
3046
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
2981
3047
  if (ts3.isParenthesizedTypeNode(typeNode)) {
@@ -4088,6 +4154,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4088
4154
  if (customType) {
4089
4155
  return customType;
4090
4156
  }
4157
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4158
+ if (symbolCustomType) {
4159
+ return symbolCustomType;
4160
+ }
4161
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4162
+ if (brandedCustomType) {
4163
+ return brandedCustomType;
4164
+ }
4091
4165
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4092
4166
  type,
4093
4167
  checker,
@@ -5032,7 +5106,7 @@ function createProgramContextFromProgram(program, filePath) {
5032
5106
  sourceFile
5033
5107
  };
5034
5108
  }
5035
- function createProgramContext(filePath) {
5109
+ function createProgramContext(filePath, additionalFiles) {
5036
5110
  const absolutePath = path.resolve(filePath);
5037
5111
  const fileDir = path.dirname(absolutePath);
5038
5112
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5055,7 +5129,8 @@ function createProgramContext(filePath) {
5055
5129
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5056
5130
  }
5057
5131
  compilerOptions = parsed.options;
5058
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5132
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5133
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5059
5134
  } else {
5060
5135
  compilerOptions = {
5061
5136
  target: ts4.ScriptTarget.ES2022,
@@ -5065,7 +5140,8 @@ function createProgramContext(filePath) {
5065
5140
  skipLibCheck: true,
5066
5141
  declaration: true
5067
5142
  };
5068
- fileNames = [absolutePath];
5143
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5144
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5069
5145
  }
5070
5146
  const program = ts4.createProgram(fileNames, compilerOptions);
5071
5147
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5369,10 +5445,134 @@ function makeFileProvenance(filePath) {
5369
5445
  };
5370
5446
  }
5371
5447
 
5448
+ // src/extensions/symbol-registry.ts
5449
+ import * as ts5 from "typescript";
5450
+ import * as path2 from "path";
5451
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5452
+ const symbolMap = /* @__PURE__ */ new Map();
5453
+ const normalizedPath = path2.resolve(configPath);
5454
+ const configFile = program.getSourceFile(normalizedPath);
5455
+ if (configFile === void 0) {
5456
+ return symbolMap;
5457
+ }
5458
+ function visit(node) {
5459
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5460
+ processDefineCustomTypeCall(node);
5461
+ }
5462
+ ts5.forEachChild(node, visit);
5463
+ }
5464
+ function processDefineCustomTypeCall(call) {
5465
+ const typeArgNode = call.typeArguments?.[0];
5466
+ if (typeArgNode === void 0) {
5467
+ return;
5468
+ }
5469
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5470
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5471
+ if (canonical === void 0) {
5472
+ return;
5473
+ }
5474
+ const typeName = extractTypeNameFromCallArg(call);
5475
+ if (typeName === null) {
5476
+ return;
5477
+ }
5478
+ let entry;
5479
+ const extensionId = extractEnclosingExtensionId(call, checker);
5480
+ if (extensionId !== null) {
5481
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5482
+ if (reg !== void 0) {
5483
+ entry = { extensionId, registration: reg };
5484
+ }
5485
+ }
5486
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5487
+ if (entry === void 0) {
5488
+ return;
5489
+ }
5490
+ symbolMap.set(canonical, entry);
5491
+ }
5492
+ visit(configFile);
5493
+ return symbolMap;
5494
+ }
5495
+ function isDefineCustomTypeCall(node, checker) {
5496
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5497
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5498
+ if (callSymbol !== void 0) {
5499
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5500
+ const decl = resolved.declarations?.[0];
5501
+ if (decl !== void 0) {
5502
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5503
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5504
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5505
+ }
5506
+ }
5507
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5508
+ }
5509
+ function resolveCanonicalSymbol2(type, checker) {
5510
+ const raw = type.aliasSymbol ?? type.getSymbol();
5511
+ if (raw === void 0) return void 0;
5512
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5513
+ }
5514
+ function extractTypeNameFromCallArg(call) {
5515
+ const arg = call.arguments[0];
5516
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5517
+ return null;
5518
+ }
5519
+ const typeNameProp = arg.properties.find(
5520
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5521
+ );
5522
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5523
+ return null;
5524
+ }
5525
+ return typeNameProp.initializer.text;
5526
+ }
5527
+ function extractEnclosingExtensionId(call, checker) {
5528
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5529
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5530
+ return extractExtensionIdFromCallArg(node);
5531
+ }
5532
+ }
5533
+ return null;
5534
+ }
5535
+ function isDefineExtensionCall(node, checker) {
5536
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5537
+ if (callSymbol !== void 0) {
5538
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5539
+ const decl = resolved.declarations?.[0];
5540
+ if (decl !== void 0) {
5541
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5542
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5543
+ }
5544
+ }
5545
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5546
+ }
5547
+ function extractExtensionIdFromCallArg(call) {
5548
+ const arg = call.arguments[0];
5549
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5550
+ return null;
5551
+ }
5552
+ const prop = arg.properties.find(
5553
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5554
+ );
5555
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5556
+ return null;
5557
+ }
5558
+ return prop.initializer.text;
5559
+ }
5560
+ function findRegistrationByTypeName(registry, typeName) {
5561
+ for (const ext of registry.extensions) {
5562
+ if (ext.types === void 0) {
5563
+ continue;
5564
+ }
5565
+ for (const type of ext.types) {
5566
+ if (type.typeName === typeName) {
5567
+ return { extensionId: ext.extensionId, registration: type };
5568
+ }
5569
+ }
5570
+ }
5571
+ return void 0;
5572
+ }
5573
+
5372
5574
  // src/validate/constraint-validator.ts
5373
- import {
5374
- analyzeConstraintTargets
5375
- } from "@formspec/analysis/internal";
5575
+ import { analyzeConstraintTargets } from "@formspec/analysis/internal";
5376
5576
  function validateFieldNode(ctx, field) {
5377
5577
  const analysis = analyzeConstraintTargets(
5378
5578
  field.name,
@@ -5503,7 +5703,8 @@ function formatLocation(location) {
5503
5703
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5504
5704
  }
5505
5705
  function generateSchemasFromClass(options) {
5506
- const ctx = createProgramContext(options.filePath);
5706
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5707
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5507
5708
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5508
5709
  if (!classDecl) {
5509
5710
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5568,7 +5769,8 @@ function generateSchemasDetailed(options) {
5568
5769
  function generateSchemasDetailedInternal(options) {
5569
5770
  let ctx;
5570
5771
  try {
5571
- ctx = createProgramContext(options.filePath);
5772
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5773
+ ctx = createProgramContext(options.filePath, additionalFiles);
5572
5774
  } catch (error) {
5573
5775
  return {
5574
5776
  ok: false,
@@ -5607,13 +5809,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5607
5809
  }
5608
5810
  function generateSchemasBatch(options) {
5609
5811
  const contextCache = /* @__PURE__ */ new Map();
5812
+ const resolved = resolveOptions(options);
5813
+ let symbolMapProgram;
5610
5814
  return options.targets.map((target) => {
5611
5815
  let ctx;
5612
5816
  try {
5613
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5817
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5614
5818
  const cachedContext = contextCache.get(cacheKey);
5615
5819
  if (cachedContext === void 0) {
5616
- ctx = createProgramContext(target.filePath);
5820
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5821
+ ctx = createProgramContext(target.filePath, additionalFiles);
5617
5822
  contextCache.set(cacheKey, ctx);
5618
5823
  } else {
5619
5824
  ctx = cachedContext;
@@ -5624,9 +5829,25 @@ function generateSchemasBatch(options) {
5624
5829
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5625
5830
  });
5626
5831
  }
5832
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5833
+ const symbolMap = buildSymbolMapFromConfig(
5834
+ options.configPath,
5835
+ ctx.program,
5836
+ ctx.checker,
5837
+ resolved.extensionRegistry
5838
+ );
5839
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5840
+ symbolMapProgram = ctx.program;
5841
+ }
5627
5842
  return withTarget(
5628
5843
  target,
5629
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5844
+ generateSchemasFromResolvedOptions(
5845
+ ctx,
5846
+ target.filePath,
5847
+ target.typeName,
5848
+ resolved,
5849
+ options.discriminator
5850
+ )
5630
5851
  );
5631
5852
  });
5632
5853
  }
@@ -5647,11 +5868,19 @@ function generateSchemasBatchFromProgram(options) {
5647
5868
  );
5648
5869
  });
5649
5870
  }
5871
+ function isMutableRegistry(reg) {
5872
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
5873
+ }
5650
5874
  function resolveOptions(options) {
5651
5875
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5876
+ const legacyRegistry = options.extensionRegistry;
5652
5877
  return {
5653
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5654
- extensionRegistry: options.extensionRegistry ?? configRegistry,
5878
+ // When the caller provides the deprecated extensionRegistry field directly,
5879
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
5880
+ // because the legacy path was introduced before MutableExtensionRegistry was
5881
+ // split out; callers using createExtensionRegistry() always get a mutable
5882
+ // registry, and this cast is safe for all registries produced by this module.
5883
+ extensionRegistry: legacyRegistry ?? configRegistry,
5655
5884
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5656
5885
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5657
5886
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5662,13 +5891,31 @@ function resolveOptions(options) {
5662
5891
  }
5663
5892
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5664
5893
  const resolved = resolveOptions(options);
5894
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
5895
+ const symbolMap = buildSymbolMapFromConfig(
5896
+ options.configPath,
5897
+ ctx.program,
5898
+ ctx.checker,
5899
+ resolved.extensionRegistry
5900
+ );
5901
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5902
+ }
5903
+ return generateSchemasFromResolvedOptions(
5904
+ ctx,
5905
+ filePath,
5906
+ typeName,
5907
+ resolved,
5908
+ options.discriminator
5909
+ );
5910
+ }
5911
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5665
5912
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5666
5913
  ctx,
5667
5914
  filePath,
5668
5915
  typeName,
5669
5916
  resolved.extensionRegistry,
5670
5917
  resolved.metadata,
5671
- options.discriminator
5918
+ discriminator
5672
5919
  );
5673
5920
  if (!analysisResult.ok) {
5674
5921
  return {
@@ -5710,7 +5957,7 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5710
5957
  }
5711
5958
 
5712
5959
  // src/static-build.ts
5713
- import * as ts6 from "typescript";
5960
+ import * as ts7 from "typescript";
5714
5961
  function toStaticBuildContext(context) {
5715
5962
  return context;
5716
5963
  }
@@ -5725,7 +5972,7 @@ function getModuleSymbol(context) {
5725
5972
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5726
5973
  }
5727
5974
  function isSchemaSourceDeclaration(declaration) {
5728
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
5975
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5729
5976
  }
5730
5977
  function resolveModuleExport(context, exportName = "default") {
5731
5978
  const moduleSymbol = getModuleSymbol(context);
@@ -5736,14 +5983,14 @@ function resolveModuleExport(context, exportName = "default") {
5736
5983
  if (exportSymbol === null) {
5737
5984
  return null;
5738
5985
  }
5739
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5986
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5740
5987
  }
5741
5988
  function resolveModuleExportDeclaration(context, exportName = "default") {
5742
5989
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5743
5990
  }
5744
5991
 
5745
5992
  // src/generators/discovered-schema.ts
5746
- import * as ts7 from "typescript";
5993
+ import * as ts8 from "typescript";
5747
5994
  import { analyzeMetadataForNodeWithChecker as analyzeMetadataForNodeWithChecker2 } from "@formspec/analysis/internal";
5748
5995
  import { IR_VERSION as IR_VERSION3 } from "@formspec/core/internals";
5749
5996
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5753,17 +6000,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5753
6000
  };
5754
6001
  }
5755
6002
  function isNamedTypeDeclaration(declaration) {
5756
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6003
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5757
6004
  }
5758
6005
  function hasConcreteTypeArguments(type, checker) {
5759
6006
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5760
6007
  return true;
5761
6008
  }
5762
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6009
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5763
6010
  return false;
5764
6011
  }
5765
6012
  const objectType = type;
5766
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6013
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5767
6014
  return false;
5768
6015
  }
5769
6016
  return checker.getTypeArguments(objectType).length > 0;
@@ -5776,13 +6023,13 @@ function getNamedTypeDeclaration2(type) {
5776
6023
  return declaration;
5777
6024
  }
5778
6025
  }
5779
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6026
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5780
6027
  return aliasDeclaration;
5781
6028
  }
5782
6029
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5783
6030
  if (sourceNode !== void 0 && "name" in sourceNode) {
5784
6031
  const namedNode = sourceNode;
5785
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6032
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5786
6033
  return namedNode.name.text;
5787
6034
  }
5788
6035
  }
@@ -5901,17 +6148,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5901
6148
  rootLogicalName: root.name
5902
6149
  }
5903
6150
  );
5904
- const schema = generateJsonSchemaFromIR(
5905
- ir,
5906
- {
5907
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5908
- extensionRegistry: options?.extensionRegistry,
5909
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5910
- enumSerialization: options?.enumSerialization,
5911
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5912
- vendorPrefix: options?.vendorPrefix
5913
- }
5914
- );
6151
+ const schema = generateJsonSchemaFromIR(ir, {
6152
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6153
+ extensionRegistry: options?.extensionRegistry,
6154
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6155
+ enumSerialization: options?.enumSerialization,
6156
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6157
+ vendorPrefix: options?.vendorPrefix
6158
+ });
5915
6159
  const result = schema.properties?.["__result"];
5916
6160
  if (result === void 0) {
5917
6161
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6010,7 +6254,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6010
6254
  }
6011
6255
  function generateSchemasFromDeclaration(options) {
6012
6256
  const filePath = options.declaration.getSourceFile().fileName;
6013
- if (ts7.isClassDeclaration(options.declaration)) {
6257
+ if (ts8.isClassDeclaration(options.declaration)) {
6014
6258
  return generateSchemasFromAnalysis(
6015
6259
  analyzeClassToIR(
6016
6260
  options.declaration,
@@ -6026,7 +6270,7 @@ function generateSchemasFromDeclaration(options) {
6026
6270
  options
6027
6271
  );
6028
6272
  }
6029
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6273
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6030
6274
  return generateSchemasFromAnalysis(
6031
6275
  analyzeInterfaceToIR(
6032
6276
  options.declaration,
@@ -6042,7 +6286,7 @@ function generateSchemasFromDeclaration(options) {
6042
6286
  options
6043
6287
  );
6044
6288
  }
6045
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6289
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6046
6290
  const analyzedAlias = analyzeTypeAliasToIR(
6047
6291
  options.declaration,
6048
6292
  options.context.checker,
@@ -6105,7 +6349,7 @@ function generateSchemasFromReturnType(options) {
6105
6349
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6106
6350
  const type = unwrapPromiseType(options.context.checker, returnType);
6107
6351
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6108
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6352
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6109
6353
  return generateSchemasFromResolvedType({
6110
6354
  ...options,
6111
6355
  type,
@@ -6140,14 +6384,14 @@ function unwrapPromiseTypeNode(typeNode) {
6140
6384
  if (typeNode === void 0) {
6141
6385
  return void 0;
6142
6386
  }
6143
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6387
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6144
6388
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6145
6389
  return unwrapped ?? typeNode;
6146
6390
  }
6147
6391
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6148
6392
  }
6149
6393
  function isPromiseTypeReferenceNode(typeNode) {
6150
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6394
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6151
6395
  }
6152
6396
 
6153
6397
  // src/generators/mixed-authoring.ts
@@ -6369,8 +6613,8 @@ function writeSchemas(form, options) {
6369
6613
  if (!fs.existsSync(outDir)) {
6370
6614
  fs.mkdirSync(outDir, { recursive: true });
6371
6615
  }
6372
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6373
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6616
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6617
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6374
6618
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6375
6619
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6376
6620
  return { jsonSchemaPath, uiSchemaPath };