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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  2. package/dist/analyzer/program.d.ts +4 -1
  3. package/dist/analyzer/program.d.ts.map +1 -1
  4. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  5. package/dist/browser.cjs +35 -9
  6. package/dist/browser.cjs.map +1 -1
  7. package/dist/browser.js +36 -12
  8. package/dist/browser.js.map +1 -1
  9. package/dist/build-alpha.d.ts +106 -5
  10. package/dist/build-beta.d.ts +106 -5
  11. package/dist/build-internal.d.ts +106 -5
  12. package/dist/build.d.ts +106 -5
  13. package/dist/cli.cjs +343 -59
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +339 -58
  16. package/dist/cli.js.map +1 -1
  17. package/dist/extensions/index.d.ts +1 -1
  18. package/dist/extensions/index.d.ts.map +1 -1
  19. package/dist/extensions/registry.d.ts +64 -5
  20. package/dist/extensions/registry.d.ts.map +1 -1
  21. package/dist/extensions/symbol-registry.d.ts +33 -0
  22. package/dist/extensions/symbol-registry.d.ts.map +1 -0
  23. package/dist/generators/class-schema.d.ts +16 -0
  24. package/dist/generators/class-schema.d.ts.map +1 -1
  25. package/dist/generators/discovered-schema.d.ts.map +1 -1
  26. package/dist/index.cjs +328 -51
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +329 -54
  31. package/dist/index.js.map +1 -1
  32. package/dist/internals.cjs +124 -10
  33. package/dist/internals.cjs.map +1 -1
  34. package/dist/internals.d.ts +1 -1
  35. package/dist/internals.d.ts.map +1 -1
  36. package/dist/internals.js +125 -13
  37. package/dist/internals.js.map +1 -1
  38. package/dist/json-schema/generator.d.ts.map +1 -1
  39. package/dist/metadata/collision-guards.d.ts.map +1 -1
  40. package/dist/metadata/policy.d.ts.map +1 -1
  41. package/dist/metadata/resolve.d.ts.map +1 -1
  42. package/dist/ui-schema/ir-generator.d.ts.map +1 -1
  43. package/dist/validate/constraint-validator.d.ts.map +1 -1
  44. package/package.json +5 -5
package/dist/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 {
@@ -1594,7 +1596,7 @@ function generateCustomType(type, ctx) {
1594
1596
  }
1595
1597
  return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
1596
1598
  }
1597
- var JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
1599
+ var VOCABULARY_MODE_BLOCKED_KEYWORDS = /* @__PURE__ */ new Set([
1598
1600
  "$schema",
1599
1601
  "$ref",
1600
1602
  "$defs",
@@ -1665,7 +1667,7 @@ function applyCustomConstraint(schema, constraint, ctx) {
1665
1667
  if (registration.emitsVocabularyKeywords) {
1666
1668
  const target = schema;
1667
1669
  for (const [key, value] of Object.entries(extensionSchema)) {
1668
- if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
1670
+ if (VOCABULARY_MODE_BLOCKED_KEYWORDS.has(key)) {
1669
1671
  throw new Error(
1670
1672
  `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
1671
1673
  );
@@ -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,6 +3010,71 @@ 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
+ }
3051
+ function isIntegerBrandedType(type) {
3052
+ if (!type.isIntersection()) {
3053
+ return false;
3054
+ }
3055
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
3056
+ if (!hasNumberBase) {
3057
+ return false;
3058
+ }
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;
3069
+ }
3070
+ return {
3071
+ kind: "custom",
3072
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3073
+ payload: null
3074
+ };
3075
+ }
3076
+ return null;
3077
+ }
2987
3078
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
2988
3079
  if (ts3.isParenthesizedTypeNode(typeNode)) {
2989
3080
  return isResolvableObjectLikeAliasTypeNode(typeNode.type);
@@ -4095,6 +4186,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4095
4186
  if (customType) {
4096
4187
  return customType;
4097
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
+ }
4098
4197
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4099
4198
  type,
4100
4199
  checker,
@@ -4109,6 +4208,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4109
4208
  if (primitiveAlias) {
4110
4209
  return primitiveAlias;
4111
4210
  }
4211
+ if (isIntegerBrandedType(type)) {
4212
+ return { kind: "primitive", primitiveKind: "integer" };
4213
+ }
4112
4214
  if (type.flags & ts3.TypeFlags.String) {
4113
4215
  return { kind: "primitive", primitiveKind: "string" };
4114
4216
  }
@@ -4211,7 +4313,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4211
4313
  return { kind: "primitive", primitiveKind: "string" };
4212
4314
  }
4213
4315
  function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4214
- if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
4316
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null)) && !isIntegerBrandedType(type)) {
4215
4317
  return null;
4216
4318
  }
4217
4319
  const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
@@ -4297,6 +4399,9 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4297
4399
  visitedAliases
4298
4400
  );
4299
4401
  }
4402
+ if (isIntegerBrandedType(type)) {
4403
+ return { kind: "primitive", primitiveKind: "integer" };
4404
+ }
4300
4405
  if (type.flags & ts3.TypeFlags.String) {
4301
4406
  return { kind: "primitive", primitiveKind: "string" };
4302
4407
  }
@@ -5033,7 +5138,7 @@ function createProgramContextFromProgram(program, filePath) {
5033
5138
  sourceFile
5034
5139
  };
5035
5140
  }
5036
- function createProgramContext(filePath) {
5141
+ function createProgramContext(filePath, additionalFiles) {
5037
5142
  const absolutePath = path.resolve(filePath);
5038
5143
  const fileDir = path.dirname(absolutePath);
5039
5144
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5056,7 +5161,8 @@ function createProgramContext(filePath) {
5056
5161
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5057
5162
  }
5058
5163
  compilerOptions = parsed.options;
5059
- 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])];
5060
5166
  } else {
5061
5167
  compilerOptions = {
5062
5168
  target: ts4.ScriptTarget.ES2022,
@@ -5066,7 +5172,8 @@ function createProgramContext(filePath) {
5066
5172
  skipLibCheck: true,
5067
5173
  declaration: true
5068
5174
  };
5069
- fileNames = [absolutePath];
5175
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5176
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5070
5177
  }
5071
5178
  const program = ts4.createProgram(fileNames, compilerOptions);
5072
5179
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5370,6 +5477,132 @@ function makeFileProvenance(filePath) {
5370
5477
  };
5371
5478
  }
5372
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
+
5373
5606
  // src/validate/constraint-validator.ts
5374
5607
  var import_internal4 = require("@formspec/analysis/internal");
5375
5608
  function validateFieldNode(ctx, field) {
@@ -5502,7 +5735,8 @@ function formatLocation(location) {
5502
5735
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5503
5736
  }
5504
5737
  function generateSchemasFromClass(options) {
5505
- const ctx = createProgramContext(options.filePath);
5738
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5739
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5506
5740
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5507
5741
  if (!classDecl) {
5508
5742
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5567,7 +5801,8 @@ function generateSchemasDetailed(options) {
5567
5801
  function generateSchemasDetailedInternal(options) {
5568
5802
  let ctx;
5569
5803
  try {
5570
- ctx = createProgramContext(options.filePath);
5804
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5805
+ ctx = createProgramContext(options.filePath, additionalFiles);
5571
5806
  } catch (error) {
5572
5807
  return {
5573
5808
  ok: false,
@@ -5606,13 +5841,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5606
5841
  }
5607
5842
  function generateSchemasBatch(options) {
5608
5843
  const contextCache = /* @__PURE__ */ new Map();
5844
+ const resolved = resolveOptions(options);
5845
+ let symbolMapProgram;
5609
5846
  return options.targets.map((target) => {
5610
5847
  let ctx;
5611
5848
  try {
5612
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5849
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5613
5850
  const cachedContext = contextCache.get(cacheKey);
5614
5851
  if (cachedContext === void 0) {
5615
- ctx = createProgramContext(target.filePath);
5852
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5853
+ ctx = createProgramContext(target.filePath, additionalFiles);
5616
5854
  contextCache.set(cacheKey, ctx);
5617
5855
  } else {
5618
5856
  ctx = cachedContext;
@@ -5623,9 +5861,25 @@ function generateSchemasBatch(options) {
5623
5861
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5624
5862
  });
5625
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
+ }
5626
5874
  return withTarget(
5627
5875
  target,
5628
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5876
+ generateSchemasFromResolvedOptions(
5877
+ ctx,
5878
+ target.filePath,
5879
+ target.typeName,
5880
+ resolved,
5881
+ options.discriminator
5882
+ )
5629
5883
  );
5630
5884
  });
5631
5885
  }
@@ -5646,11 +5900,19 @@ function generateSchemasBatchFromProgram(options) {
5646
5900
  );
5647
5901
  });
5648
5902
  }
5903
+ function isMutableRegistry(reg) {
5904
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
5905
+ }
5649
5906
  function resolveOptions(options) {
5650
5907
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5908
+ const legacyRegistry = options.extensionRegistry;
5651
5909
  return {
5652
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5653
- 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,
5654
5916
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5655
5917
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5656
5918
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5661,13 +5923,31 @@ function resolveOptions(options) {
5661
5923
  }
5662
5924
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5663
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) {
5664
5944
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5665
5945
  ctx,
5666
5946
  filePath,
5667
5947
  typeName,
5668
5948
  resolved.extensionRegistry,
5669
5949
  resolved.metadata,
5670
- options.discriminator
5950
+ discriminator
5671
5951
  );
5672
5952
  if (!analysisResult.ok) {
5673
5953
  return {
@@ -5709,7 +5989,7 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5709
5989
  }
5710
5990
 
5711
5991
  // src/static-build.ts
5712
- var ts6 = __toESM(require("typescript"), 1);
5992
+ var ts7 = __toESM(require("typescript"), 1);
5713
5993
  function toStaticBuildContext(context) {
5714
5994
  return context;
5715
5995
  }
@@ -5724,7 +6004,7 @@ function getModuleSymbol(context) {
5724
6004
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5725
6005
  }
5726
6006
  function isSchemaSourceDeclaration(declaration) {
5727
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6007
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5728
6008
  }
5729
6009
  function resolveModuleExport(context, exportName = "default") {
5730
6010
  const moduleSymbol = getModuleSymbol(context);
@@ -5735,14 +6015,14 @@ function resolveModuleExport(context, exportName = "default") {
5735
6015
  if (exportSymbol === null) {
5736
6016
  return null;
5737
6017
  }
5738
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6018
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5739
6019
  }
5740
6020
  function resolveModuleExportDeclaration(context, exportName = "default") {
5741
6021
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5742
6022
  }
5743
6023
 
5744
6024
  // src/generators/discovered-schema.ts
5745
- var ts7 = __toESM(require("typescript"), 1);
6025
+ var ts8 = __toESM(require("typescript"), 1);
5746
6026
  var import_internal5 = require("@formspec/analysis/internal");
5747
6027
  var import_internals6 = require("@formspec/core/internals");
5748
6028
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5752,17 +6032,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5752
6032
  };
5753
6033
  }
5754
6034
  function isNamedTypeDeclaration(declaration) {
5755
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6035
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5756
6036
  }
5757
6037
  function hasConcreteTypeArguments(type, checker) {
5758
6038
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5759
6039
  return true;
5760
6040
  }
5761
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6041
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5762
6042
  return false;
5763
6043
  }
5764
6044
  const objectType = type;
5765
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6045
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5766
6046
  return false;
5767
6047
  }
5768
6048
  return checker.getTypeArguments(objectType).length > 0;
@@ -5775,13 +6055,13 @@ function getNamedTypeDeclaration2(type) {
5775
6055
  return declaration;
5776
6056
  }
5777
6057
  }
5778
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6058
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5779
6059
  return aliasDeclaration;
5780
6060
  }
5781
6061
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5782
6062
  if (sourceNode !== void 0 && "name" in sourceNode) {
5783
6063
  const namedNode = sourceNode;
5784
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6064
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5785
6065
  return namedNode.name.text;
5786
6066
  }
5787
6067
  }
@@ -5900,17 +6180,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5900
6180
  rootLogicalName: root.name
5901
6181
  }
5902
6182
  );
5903
- const schema = generateJsonSchemaFromIR(
5904
- ir,
5905
- {
5906
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5907
- extensionRegistry: options?.extensionRegistry,
5908
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5909
- enumSerialization: options?.enumSerialization,
5910
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5911
- vendorPrefix: options?.vendorPrefix
5912
- }
5913
- );
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
+ });
5914
6191
  const result = schema.properties?.["__result"];
5915
6192
  if (result === void 0) {
5916
6193
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6009,7 +6286,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6009
6286
  }
6010
6287
  function generateSchemasFromDeclaration(options) {
6011
6288
  const filePath = options.declaration.getSourceFile().fileName;
6012
- if (ts7.isClassDeclaration(options.declaration)) {
6289
+ if (ts8.isClassDeclaration(options.declaration)) {
6013
6290
  return generateSchemasFromAnalysis(
6014
6291
  analyzeClassToIR(
6015
6292
  options.declaration,
@@ -6025,7 +6302,7 @@ function generateSchemasFromDeclaration(options) {
6025
6302
  options
6026
6303
  );
6027
6304
  }
6028
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6305
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6029
6306
  return generateSchemasFromAnalysis(
6030
6307
  analyzeInterfaceToIR(
6031
6308
  options.declaration,
@@ -6041,7 +6318,7 @@ function generateSchemasFromDeclaration(options) {
6041
6318
  options
6042
6319
  );
6043
6320
  }
6044
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6321
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6045
6322
  const analyzedAlias = analyzeTypeAliasToIR(
6046
6323
  options.declaration,
6047
6324
  options.context.checker,
@@ -6104,7 +6381,7 @@ function generateSchemasFromReturnType(options) {
6104
6381
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6105
6382
  const type = unwrapPromiseType(options.context.checker, returnType);
6106
6383
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6107
- 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";
6108
6385
  return generateSchemasFromResolvedType({
6109
6386
  ...options,
6110
6387
  type,
@@ -6139,14 +6416,14 @@ function unwrapPromiseTypeNode(typeNode) {
6139
6416
  if (typeNode === void 0) {
6140
6417
  return void 0;
6141
6418
  }
6142
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6419
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6143
6420
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6144
6421
  return unwrapped ?? typeNode;
6145
6422
  }
6146
6423
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6147
6424
  }
6148
6425
  function isPromiseTypeReferenceNode(typeNode) {
6149
- 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;
6150
6427
  }
6151
6428
 
6152
6429
  // src/generators/mixed-authoring.ts
@@ -6368,8 +6645,8 @@ function writeSchemas(form, options) {
6368
6645
  if (!fs.existsSync(outDir)) {
6369
6646
  fs.mkdirSync(outDir, { recursive: true });
6370
6647
  }
6371
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6372
- 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`);
6373
6650
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6374
6651
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6375
6652
  return { jsonSchemaPath, uiSchemaPath };