@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/cli.cjs CHANGED
@@ -264,7 +264,9 @@ function resolveTypeNodeMetadata(type, options) {
264
264
  case "object":
265
265
  return {
266
266
  ...type,
267
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
267
+ properties: type.properties.map(
268
+ (property) => resolveObjectPropertyMetadata(property, options)
269
+ )
268
270
  };
269
271
  case "record":
270
272
  return {
@@ -1743,10 +1745,7 @@ function generateJsonSchema(form, options) {
1743
1745
  const metadata = options?.metadata;
1744
1746
  const vendorPrefix = options?.vendorPrefix;
1745
1747
  const enumSerialization = options?.enumSerialization;
1746
- const ir = canonicalizeChainDSL(
1747
- form,
1748
- metadata !== void 0 ? { metadata } : void 0
1749
- );
1748
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1750
1749
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1751
1750
  ...vendorPrefix !== void 0 && { vendorPrefix },
1752
1751
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1974,7 +1973,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1974
1973
  break;
1975
1974
  }
1976
1975
  case "conditional": {
1977
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1976
+ const newRule = createShowRule(
1977
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1978
+ element.value
1979
+ );
1978
1980
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1979
1981
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1980
1982
  result.push(...childElements);
@@ -2059,8 +2061,10 @@ function buildConstraintTagSources(extensions) {
2059
2061
  }
2060
2062
  function createExtensionRegistry(extensions) {
2061
2063
  const reservedTagSources = buildConstraintTagSources(extensions);
2064
+ let symbolMap = /* @__PURE__ */ new Map();
2062
2065
  const typeMap = /* @__PURE__ */ new Map();
2063
2066
  const typeNameMap = /* @__PURE__ */ new Map();
2067
+ const brandMap = /* @__PURE__ */ new Map();
2064
2068
  const constraintMap = /* @__PURE__ */ new Map();
2065
2069
  const constraintTagMap = /* @__PURE__ */ new Map();
2066
2070
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -2084,6 +2088,20 @@ function createExtensionRegistry(extensions) {
2084
2088
  registration: type
2085
2089
  });
2086
2090
  }
2091
+ if (type.brand !== void 0) {
2092
+ if (type.brand === "__integerBrand") {
2093
+ throw new Error(
2094
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
2095
+ );
2096
+ }
2097
+ if (brandMap.has(type.brand)) {
2098
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
2099
+ }
2100
+ brandMap.set(type.brand, {
2101
+ extensionId: ext.extensionId,
2102
+ registration: type
2103
+ });
2104
+ }
2087
2105
  if (type.builtinConstraintBroadenings !== void 0) {
2088
2106
  for (const broadening of type.builtinConstraintBroadenings) {
2089
2107
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2153,7 +2171,10 @@ function createExtensionRegistry(extensions) {
2153
2171
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2154
2172
  );
2155
2173
  }
2156
- if (Object.hasOwn(import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS, (0, import_internals3.normalizeConstraintTagName)(canonicalTagName))) {
2174
+ if (Object.hasOwn(
2175
+ import_internals3.BUILTIN_CONSTRAINT_DEFINITIONS,
2176
+ (0, import_internals3.normalizeConstraintTagName)(canonicalTagName)
2177
+ )) {
2157
2178
  throw new Error(
2158
2179
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${(0, import_internals3.normalizeConstraintTagName)(canonicalTagName)}".`
2159
2180
  );
@@ -2174,6 +2195,11 @@ function createExtensionRegistry(extensions) {
2174
2195
  extensions,
2175
2196
  findType: (typeId) => typeMap.get(typeId),
2176
2197
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2198
+ findTypeByBrand: (brand) => brandMap.get(brand),
2199
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2200
+ setSymbolMap: (map) => {
2201
+ symbolMap = map;
2202
+ },
2177
2203
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2178
2204
  findConstraintTag: (tagName) => constraintTagMap.get((0, import_internal.normalizeFormSpecTagName)(tagName)),
2179
2205
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -3058,30 +3084,70 @@ function isObjectType(type) {
3058
3084
  function isIntersectionType(type) {
3059
3085
  return !!(type.flags & ts3.TypeFlags.Intersection);
3060
3086
  }
3087
+ function collectBrandIdentifiers(type) {
3088
+ if (!type.isIntersection()) {
3089
+ return [];
3090
+ }
3091
+ const brands = [];
3092
+ for (const prop of type.getProperties()) {
3093
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
3094
+ if (decl === void 0) continue;
3095
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
3096
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
3097
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
3098
+ brands.push(decl.name.expression.text);
3099
+ }
3100
+ return brands;
3101
+ }
3102
+ function resolveCanonicalSymbol(type, checker) {
3103
+ const raw = type.aliasSymbol ?? type.getSymbol();
3104
+ if (raw === void 0) return void 0;
3105
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3106
+ }
3107
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3108
+ if (extensionRegistry === void 0) {
3109
+ return null;
3110
+ }
3111
+ const canonical = resolveCanonicalSymbol(type, checker);
3112
+ if (canonical === void 0) {
3113
+ return null;
3114
+ }
3115
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3116
+ if (registration === void 0) {
3117
+ return null;
3118
+ }
3119
+ return {
3120
+ kind: "custom",
3121
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3122
+ payload: null
3123
+ };
3124
+ }
3061
3125
  function isIntegerBrandedType(type) {
3062
3126
  if (!type.isIntersection()) {
3063
3127
  return false;
3064
3128
  }
3065
- const hasNumberBase = type.types.some(
3066
- (member) => !!(member.flags & ts3.TypeFlags.Number)
3067
- );
3129
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
3068
3130
  if (!hasNumberBase) {
3069
3131
  return false;
3070
3132
  }
3071
- return type.getProperties().some((prop) => {
3072
- const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
3073
- if (declaration === void 0) {
3074
- return false;
3075
- }
3076
- if (!ts3.isPropertySignature(declaration) && !ts3.isPropertyDeclaration(declaration)) {
3077
- return false;
3078
- }
3079
- const name = declaration.name;
3080
- if (!ts3.isComputedPropertyName(name)) {
3081
- return false;
3133
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3134
+ }
3135
+ function resolveBrandedCustomType(type, extensionRegistry) {
3136
+ if (extensionRegistry === void 0) {
3137
+ return null;
3138
+ }
3139
+ for (const brand of collectBrandIdentifiers(type)) {
3140
+ const registration = extensionRegistry.findTypeByBrand(brand);
3141
+ if (registration === void 0) {
3142
+ continue;
3082
3143
  }
3083
- return ts3.isIdentifier(name.expression) && name.expression.text === "__integerBrand";
3084
- });
3144
+ return {
3145
+ kind: "custom",
3146
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3147
+ payload: null
3148
+ };
3149
+ }
3150
+ return null;
3085
3151
  }
3086
3152
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
3087
3153
  if (ts3.isParenthesizedTypeNode(typeNode)) {
@@ -4189,6 +4255,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4189
4255
  if (customType) {
4190
4256
  return customType;
4191
4257
  }
4258
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4259
+ if (symbolCustomType) {
4260
+ return symbolCustomType;
4261
+ }
4262
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4263
+ if (brandedCustomType) {
4264
+ return brandedCustomType;
4265
+ }
4192
4266
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4193
4267
  type,
4194
4268
  checker,
@@ -5149,7 +5223,7 @@ function createProgramContextFromProgram(program, filePath) {
5149
5223
  sourceFile
5150
5224
  };
5151
5225
  }
5152
- function createProgramContext(filePath) {
5226
+ function createProgramContext(filePath, additionalFiles) {
5153
5227
  const absolutePath = path.resolve(filePath);
5154
5228
  const fileDir = path.dirname(absolutePath);
5155
5229
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5172,7 +5246,8 @@ function createProgramContext(filePath) {
5172
5246
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5173
5247
  }
5174
5248
  compilerOptions = parsed.options;
5175
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5249
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5250
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5176
5251
  } else {
5177
5252
  compilerOptions = {
5178
5253
  target: ts4.ScriptTarget.ES2022,
@@ -5182,7 +5257,8 @@ function createProgramContext(filePath) {
5182
5257
  skipLibCheck: true,
5183
5258
  declaration: true
5184
5259
  };
5185
- fileNames = [absolutePath];
5260
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5261
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5186
5262
  }
5187
5263
  const program = ts4.createProgram(fileNames, compilerOptions);
5188
5264
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5495,6 +5571,138 @@ var init_program = __esm({
5495
5571
  }
5496
5572
  });
5497
5573
 
5574
+ // src/extensions/symbol-registry.ts
5575
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5576
+ const symbolMap = /* @__PURE__ */ new Map();
5577
+ const normalizedPath = path2.resolve(configPath);
5578
+ const configFile = program.getSourceFile(normalizedPath);
5579
+ if (configFile === void 0) {
5580
+ return symbolMap;
5581
+ }
5582
+ function visit(node) {
5583
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5584
+ processDefineCustomTypeCall(node);
5585
+ }
5586
+ ts5.forEachChild(node, visit);
5587
+ }
5588
+ function processDefineCustomTypeCall(call) {
5589
+ const typeArgNode = call.typeArguments?.[0];
5590
+ if (typeArgNode === void 0) {
5591
+ return;
5592
+ }
5593
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5594
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5595
+ if (canonical === void 0) {
5596
+ return;
5597
+ }
5598
+ const typeName = extractTypeNameFromCallArg(call);
5599
+ if (typeName === null) {
5600
+ return;
5601
+ }
5602
+ let entry;
5603
+ const extensionId = extractEnclosingExtensionId(call, checker);
5604
+ if (extensionId !== null) {
5605
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5606
+ if (reg !== void 0) {
5607
+ entry = { extensionId, registration: reg };
5608
+ }
5609
+ }
5610
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5611
+ if (entry === void 0) {
5612
+ return;
5613
+ }
5614
+ symbolMap.set(canonical, entry);
5615
+ }
5616
+ visit(configFile);
5617
+ return symbolMap;
5618
+ }
5619
+ function isDefineCustomTypeCall(node, checker) {
5620
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5621
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5622
+ if (callSymbol !== void 0) {
5623
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5624
+ const decl = resolved.declarations?.[0];
5625
+ if (decl !== void 0) {
5626
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5627
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5628
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5629
+ }
5630
+ }
5631
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5632
+ }
5633
+ function resolveCanonicalSymbol2(type, checker) {
5634
+ const raw = type.aliasSymbol ?? type.getSymbol();
5635
+ if (raw === void 0) return void 0;
5636
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5637
+ }
5638
+ function extractTypeNameFromCallArg(call) {
5639
+ const arg = call.arguments[0];
5640
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5641
+ return null;
5642
+ }
5643
+ const typeNameProp = arg.properties.find(
5644
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5645
+ );
5646
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5647
+ return null;
5648
+ }
5649
+ return typeNameProp.initializer.text;
5650
+ }
5651
+ function extractEnclosingExtensionId(call, checker) {
5652
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5653
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5654
+ return extractExtensionIdFromCallArg(node);
5655
+ }
5656
+ }
5657
+ return null;
5658
+ }
5659
+ function isDefineExtensionCall(node, checker) {
5660
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5661
+ if (callSymbol !== void 0) {
5662
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5663
+ const decl = resolved.declarations?.[0];
5664
+ if (decl !== void 0) {
5665
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5666
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5667
+ }
5668
+ }
5669
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5670
+ }
5671
+ function extractExtensionIdFromCallArg(call) {
5672
+ const arg = call.arguments[0];
5673
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5674
+ return null;
5675
+ }
5676
+ const prop = arg.properties.find(
5677
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5678
+ );
5679
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5680
+ return null;
5681
+ }
5682
+ return prop.initializer.text;
5683
+ }
5684
+ function findRegistrationByTypeName(registry, typeName) {
5685
+ for (const ext of registry.extensions) {
5686
+ if (ext.types === void 0) {
5687
+ continue;
5688
+ }
5689
+ for (const type of ext.types) {
5690
+ if (type.typeName === typeName) {
5691
+ return { extensionId: ext.extensionId, registration: type };
5692
+ }
5693
+ }
5694
+ }
5695
+ return void 0;
5696
+ }
5697
+ var ts5, path2;
5698
+ var init_symbol_registry = __esm({
5699
+ "src/extensions/symbol-registry.ts"() {
5700
+ "use strict";
5701
+ ts5 = __toESM(require("typescript"), 1);
5702
+ path2 = __toESM(require("path"), 1);
5703
+ }
5704
+ });
5705
+
5498
5706
  // src/validate/constraint-validator.ts
5499
5707
  function validateFieldNode(ctx, field) {
5500
5708
  const analysis = (0, import_internal4.analyzeConstraintTargets)(
@@ -5641,7 +5849,8 @@ function formatLocation(location) {
5641
5849
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5642
5850
  }
5643
5851
  function generateSchemasFromClass(options) {
5644
- const ctx = createProgramContext(options.filePath);
5852
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5853
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5645
5854
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5646
5855
  if (!classDecl) {
5647
5856
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5706,7 +5915,8 @@ function generateSchemasDetailed(options) {
5706
5915
  function generateSchemasDetailedInternal(options) {
5707
5916
  let ctx;
5708
5917
  try {
5709
- ctx = createProgramContext(options.filePath);
5918
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5919
+ ctx = createProgramContext(options.filePath, additionalFiles);
5710
5920
  } catch (error) {
5711
5921
  return {
5712
5922
  ok: false,
@@ -5745,13 +5955,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5745
5955
  }
5746
5956
  function generateSchemasBatch(options) {
5747
5957
  const contextCache = /* @__PURE__ */ new Map();
5958
+ const resolved = resolveOptions(options);
5959
+ let symbolMapProgram;
5748
5960
  return options.targets.map((target) => {
5749
5961
  let ctx;
5750
5962
  try {
5751
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5963
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5752
5964
  const cachedContext = contextCache.get(cacheKey);
5753
5965
  if (cachedContext === void 0) {
5754
- ctx = createProgramContext(target.filePath);
5966
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5967
+ ctx = createProgramContext(target.filePath, additionalFiles);
5755
5968
  contextCache.set(cacheKey, ctx);
5756
5969
  } else {
5757
5970
  ctx = cachedContext;
@@ -5762,9 +5975,25 @@ function generateSchemasBatch(options) {
5762
5975
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5763
5976
  });
5764
5977
  }
5978
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5979
+ const symbolMap = buildSymbolMapFromConfig(
5980
+ options.configPath,
5981
+ ctx.program,
5982
+ ctx.checker,
5983
+ resolved.extensionRegistry
5984
+ );
5985
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5986
+ symbolMapProgram = ctx.program;
5987
+ }
5765
5988
  return withTarget(
5766
5989
  target,
5767
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5990
+ generateSchemasFromResolvedOptions(
5991
+ ctx,
5992
+ target.filePath,
5993
+ target.typeName,
5994
+ resolved,
5995
+ options.discriminator
5996
+ )
5768
5997
  );
5769
5998
  });
5770
5999
  }
@@ -5785,11 +6014,19 @@ function generateSchemasBatchFromProgram(options) {
5785
6014
  );
5786
6015
  });
5787
6016
  }
6017
+ function isMutableRegistry(reg) {
6018
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
6019
+ }
5788
6020
  function resolveOptions(options) {
5789
6021
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
6022
+ const legacyRegistry = options.extensionRegistry;
5790
6023
  return {
5791
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5792
- extensionRegistry: options.extensionRegistry ?? configRegistry,
6024
+ // When the caller provides the deprecated extensionRegistry field directly,
6025
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
6026
+ // because the legacy path was introduced before MutableExtensionRegistry was
6027
+ // split out; callers using createExtensionRegistry() always get a mutable
6028
+ // registry, and this cast is safe for all registries produced by this module.
6029
+ extensionRegistry: legacyRegistry ?? configRegistry,
5793
6030
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5794
6031
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5795
6032
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5800,13 +6037,31 @@ function resolveOptions(options) {
5800
6037
  }
5801
6038
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5802
6039
  const resolved = resolveOptions(options);
6040
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
6041
+ const symbolMap = buildSymbolMapFromConfig(
6042
+ options.configPath,
6043
+ ctx.program,
6044
+ ctx.checker,
6045
+ resolved.extensionRegistry
6046
+ );
6047
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
6048
+ }
6049
+ return generateSchemasFromResolvedOptions(
6050
+ ctx,
6051
+ filePath,
6052
+ typeName,
6053
+ resolved,
6054
+ options.discriminator
6055
+ );
6056
+ }
6057
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5803
6058
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5804
6059
  ctx,
5805
6060
  filePath,
5806
6061
  typeName,
5807
6062
  resolved.extensionRegistry,
5808
6063
  resolved.metadata,
5809
- options.discriminator
6064
+ discriminator
5810
6065
  );
5811
6066
  if (!analysisResult.ok) {
5812
6067
  return {
@@ -5846,16 +6101,17 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5846
6101
  relatedLocations: []
5847
6102
  };
5848
6103
  }
5849
- var ts5;
6104
+ var ts6;
5850
6105
  var init_class_schema = __esm({
5851
6106
  "src/generators/class-schema.ts"() {
5852
6107
  "use strict";
5853
- ts5 = __toESM(require("typescript"), 1);
6108
+ ts6 = __toESM(require("typescript"), 1);
5854
6109
  init_program();
5855
6110
  init_class_analyzer();
5856
6111
  init_canonicalize();
5857
6112
  init_ir_generator();
5858
6113
  init_extensions();
6114
+ init_symbol_registry();
5859
6115
  init_ir_generator2();
5860
6116
  init_validate();
5861
6117
  }
@@ -5876,7 +6132,7 @@ function getModuleSymbol(context) {
5876
6132
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5877
6133
  }
5878
6134
  function isSchemaSourceDeclaration(declaration) {
5879
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6135
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5880
6136
  }
5881
6137
  function resolveModuleExport(context, exportName = "default") {
5882
6138
  const moduleSymbol = getModuleSymbol(context);
@@ -5887,16 +6143,16 @@ function resolveModuleExport(context, exportName = "default") {
5887
6143
  if (exportSymbol === null) {
5888
6144
  return null;
5889
6145
  }
5890
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6146
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5891
6147
  }
5892
6148
  function resolveModuleExportDeclaration(context, exportName = "default") {
5893
6149
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5894
6150
  }
5895
- var ts6;
6151
+ var ts7;
5896
6152
  var init_static_build = __esm({
5897
6153
  "src/static-build.ts"() {
5898
6154
  "use strict";
5899
- ts6 = __toESM(require("typescript"), 1);
6155
+ ts7 = __toESM(require("typescript"), 1);
5900
6156
  init_program();
5901
6157
  }
5902
6158
  });
@@ -5909,17 +6165,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5909
6165
  };
5910
6166
  }
5911
6167
  function isNamedTypeDeclaration(declaration) {
5912
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6168
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5913
6169
  }
5914
6170
  function hasConcreteTypeArguments(type, checker) {
5915
6171
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5916
6172
  return true;
5917
6173
  }
5918
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6174
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5919
6175
  return false;
5920
6176
  }
5921
6177
  const objectType = type;
5922
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6178
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5923
6179
  return false;
5924
6180
  }
5925
6181
  return checker.getTypeArguments(objectType).length > 0;
@@ -5932,13 +6188,13 @@ function getNamedTypeDeclaration2(type) {
5932
6188
  return declaration;
5933
6189
  }
5934
6190
  }
5935
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6191
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5936
6192
  return aliasDeclaration;
5937
6193
  }
5938
6194
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5939
6195
  if (sourceNode !== void 0 && "name" in sourceNode) {
5940
6196
  const namedNode = sourceNode;
5941
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6197
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5942
6198
  return namedNode.name.text;
5943
6199
  }
5944
6200
  }
@@ -6057,17 +6313,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
6057
6313
  rootLogicalName: root.name
6058
6314
  }
6059
6315
  );
6060
- const schema = generateJsonSchemaFromIR(
6061
- ir,
6062
- {
6063
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6064
- extensionRegistry: options?.extensionRegistry,
6065
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6066
- enumSerialization: options?.enumSerialization,
6067
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6068
- vendorPrefix: options?.vendorPrefix
6069
- }
6070
- );
6316
+ const schema = generateJsonSchemaFromIR(ir, {
6317
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6318
+ extensionRegistry: options?.extensionRegistry,
6319
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6320
+ enumSerialization: options?.enumSerialization,
6321
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6322
+ vendorPrefix: options?.vendorPrefix
6323
+ });
6071
6324
  const result = schema.properties?.["__result"];
6072
6325
  if (result === void 0) {
6073
6326
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6166,7 +6419,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6166
6419
  }
6167
6420
  function generateSchemasFromDeclaration(options) {
6168
6421
  const filePath = options.declaration.getSourceFile().fileName;
6169
- if (ts7.isClassDeclaration(options.declaration)) {
6422
+ if (ts8.isClassDeclaration(options.declaration)) {
6170
6423
  return generateSchemasFromAnalysis(
6171
6424
  analyzeClassToIR(
6172
6425
  options.declaration,
@@ -6182,7 +6435,7 @@ function generateSchemasFromDeclaration(options) {
6182
6435
  options
6183
6436
  );
6184
6437
  }
6185
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6438
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6186
6439
  return generateSchemasFromAnalysis(
6187
6440
  analyzeInterfaceToIR(
6188
6441
  options.declaration,
@@ -6198,7 +6451,7 @@ function generateSchemasFromDeclaration(options) {
6198
6451
  options
6199
6452
  );
6200
6453
  }
6201
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6454
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6202
6455
  const analyzedAlias = analyzeTypeAliasToIR(
6203
6456
  options.declaration,
6204
6457
  options.context.checker,
@@ -6261,7 +6514,7 @@ function generateSchemasFromReturnType(options) {
6261
6514
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6262
6515
  const type = unwrapPromiseType(options.context.checker, returnType);
6263
6516
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6264
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6517
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6265
6518
  return generateSchemasFromResolvedType({
6266
6519
  ...options,
6267
6520
  type,
@@ -6296,20 +6549,20 @@ function unwrapPromiseTypeNode(typeNode) {
6296
6549
  if (typeNode === void 0) {
6297
6550
  return void 0;
6298
6551
  }
6299
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6552
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6300
6553
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6301
6554
  return unwrapped ?? typeNode;
6302
6555
  }
6303
6556
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6304
6557
  }
6305
6558
  function isPromiseTypeReferenceNode(typeNode) {
6306
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6559
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6307
6560
  }
6308
- var ts7, import_internal5, import_internals6;
6561
+ var ts8, import_internal5, import_internals6;
6309
6562
  var init_discovered_schema = __esm({
6310
6563
  "src/generators/discovered-schema.ts"() {
6311
6564
  "use strict";
6312
- ts7 = __toESM(require("typescript"), 1);
6565
+ ts8 = __toESM(require("typescript"), 1);
6313
6566
  import_internal5 = require("@formspec/analysis/internal");
6314
6567
  init_class_analyzer();
6315
6568
  init_class_schema();
@@ -6575,13 +6828,13 @@ function writeSchemas(form, options) {
6575
6828
  if (!fs.existsSync(outDir)) {
6576
6829
  fs.mkdirSync(outDir, { recursive: true });
6577
6830
  }
6578
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6579
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6831
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6832
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6580
6833
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6581
6834
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6582
6835
  return { jsonSchemaPath, uiSchemaPath };
6583
6836
  }
6584
- var fs, path2;
6837
+ var fs, path3;
6585
6838
  var init_index = __esm({
6586
6839
  "src/index.ts"() {
6587
6840
  "use strict";
@@ -6589,7 +6842,7 @@ var init_index = __esm({
6589
6842
  init_generator2();
6590
6843
  init_ir_generator();
6591
6844
  fs = __toESM(require("fs"), 1);
6592
- path2 = __toESM(require("path"), 1);
6845
+ path3 = __toESM(require("path"), 1);
6593
6846
  init_extensions();
6594
6847
  init_schema2();
6595
6848
  init_schema();
@@ -6606,7 +6859,7 @@ var init_index = __esm({
6606
6859
  });
6607
6860
 
6608
6861
  // src/cli.ts
6609
- var path3 = __toESM(require("path"), 1);
6862
+ var path4 = __toESM(require("path"), 1);
6610
6863
  var import_node_url = require("url");
6611
6864
  function printHelp() {
6612
6865
  console.log(`
@@ -6695,7 +6948,7 @@ function parseArgs(args) {
6695
6948
  return null;
6696
6949
  }
6697
6950
  if (!name) {
6698
- name = path3.basename(inputFile, path3.extname(inputFile));
6951
+ name = path4.basename(inputFile, path4.extname(inputFile));
6699
6952
  }
6700
6953
  return { inputFile, outDir, name, enumSerialization };
6701
6954
  }
@@ -6706,7 +6959,7 @@ async function main() {
6706
6959
  process.exit(1);
6707
6960
  }
6708
6961
  const { inputFile, outDir, name, enumSerialization } = options;
6709
- const absoluteInput = path3.resolve(process.cwd(), inputFile);
6962
+ const absoluteInput = path4.resolve(process.cwd(), inputFile);
6710
6963
  try {
6711
6964
  const fileUrl = (0, import_node_url.pathToFileURL)(absoluteInput).href;
6712
6965
  const module2 = await import(fileUrl);