@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/cli.js CHANGED
@@ -243,7 +243,9 @@ function resolveTypeNodeMetadata(type, options) {
243
243
  case "object":
244
244
  return {
245
245
  ...type,
246
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
246
+ properties: type.properties.map(
247
+ (property) => resolveObjectPropertyMetadata(property, options)
248
+ )
247
249
  };
248
250
  case "record":
249
251
  return {
@@ -1604,7 +1606,7 @@ function applyCustomConstraint(schema, constraint, ctx) {
1604
1606
  if (registration.emitsVocabularyKeywords) {
1605
1607
  const target = schema;
1606
1608
  for (const [key, value] of Object.entries(extensionSchema)) {
1607
- if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
1609
+ if (VOCABULARY_MODE_BLOCKED_KEYWORDS.has(key)) {
1608
1610
  throw new Error(
1609
1611
  `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
1610
1612
  );
@@ -1647,13 +1649,13 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
1647
1649
  schema[key] = value;
1648
1650
  }
1649
1651
  }
1650
- var JSON_SCHEMA_STRUCTURAL_KEYWORDS;
1652
+ var VOCABULARY_MODE_BLOCKED_KEYWORDS;
1651
1653
  var init_ir_generator = __esm({
1652
1654
  "src/json-schema/ir-generator.ts"() {
1653
1655
  "use strict";
1654
1656
  init_metadata();
1655
1657
  init_collision_guards();
1656
- JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
1658
+ VOCABULARY_MODE_BLOCKED_KEYWORDS = /* @__PURE__ */ new Set([
1657
1659
  "$schema",
1658
1660
  "$ref",
1659
1661
  "$defs",
@@ -1721,10 +1723,7 @@ function generateJsonSchema(form, options) {
1721
1723
  const metadata = options?.metadata;
1722
1724
  const vendorPrefix = options?.vendorPrefix;
1723
1725
  const enumSerialization = options?.enumSerialization;
1724
- const ir = canonicalizeChainDSL(
1725
- form,
1726
- metadata !== void 0 ? { metadata } : void 0
1727
- );
1726
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1728
1727
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1729
1728
  ...vendorPrefix !== void 0 && { vendorPrefix },
1730
1729
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1953,7 +1952,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1953
1952
  break;
1954
1953
  }
1955
1954
  case "conditional": {
1956
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1955
+ const newRule = createShowRule(
1956
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1957
+ element.value
1958
+ );
1957
1959
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1958
1960
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1959
1961
  result.push(...childElements);
@@ -2044,8 +2046,10 @@ function buildConstraintTagSources(extensions) {
2044
2046
  }
2045
2047
  function createExtensionRegistry(extensions) {
2046
2048
  const reservedTagSources = buildConstraintTagSources(extensions);
2049
+ let symbolMap = /* @__PURE__ */ new Map();
2047
2050
  const typeMap = /* @__PURE__ */ new Map();
2048
2051
  const typeNameMap = /* @__PURE__ */ new Map();
2052
+ const brandMap = /* @__PURE__ */ new Map();
2049
2053
  const constraintMap = /* @__PURE__ */ new Map();
2050
2054
  const constraintTagMap = /* @__PURE__ */ new Map();
2051
2055
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -2069,6 +2073,20 @@ function createExtensionRegistry(extensions) {
2069
2073
  registration: type
2070
2074
  });
2071
2075
  }
2076
+ if (type.brand !== void 0) {
2077
+ if (type.brand === "__integerBrand") {
2078
+ throw new Error(
2079
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
2080
+ );
2081
+ }
2082
+ if (brandMap.has(type.brand)) {
2083
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
2084
+ }
2085
+ brandMap.set(type.brand, {
2086
+ extensionId: ext.extensionId,
2087
+ registration: type
2088
+ });
2089
+ }
2072
2090
  if (type.builtinConstraintBroadenings !== void 0) {
2073
2091
  for (const broadening of type.builtinConstraintBroadenings) {
2074
2092
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2138,7 +2156,10 @@ function createExtensionRegistry(extensions) {
2138
2156
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2139
2157
  );
2140
2158
  }
2141
- if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS, normalizeConstraintTagName(canonicalTagName))) {
2159
+ if (Object.hasOwn(
2160
+ BUILTIN_CONSTRAINT_DEFINITIONS,
2161
+ normalizeConstraintTagName(canonicalTagName)
2162
+ )) {
2142
2163
  throw new Error(
2143
2164
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName(canonicalTagName)}".`
2144
2165
  );
@@ -2159,6 +2180,11 @@ function createExtensionRegistry(extensions) {
2159
2180
  extensions,
2160
2181
  findType: (typeId) => typeMap.get(typeId),
2161
2182
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2183
+ findTypeByBrand: (brand) => brandMap.get(brand),
2184
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2185
+ setSymbolMap: (map) => {
2186
+ symbolMap = map;
2187
+ },
2162
2188
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2163
2189
  findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName(tagName)),
2164
2190
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -3063,6 +3089,71 @@ function isObjectType(type) {
3063
3089
  function isIntersectionType(type) {
3064
3090
  return !!(type.flags & ts3.TypeFlags.Intersection);
3065
3091
  }
3092
+ function collectBrandIdentifiers(type) {
3093
+ if (!type.isIntersection()) {
3094
+ return [];
3095
+ }
3096
+ const brands = [];
3097
+ for (const prop of type.getProperties()) {
3098
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
3099
+ if (decl === void 0) continue;
3100
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
3101
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
3102
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
3103
+ brands.push(decl.name.expression.text);
3104
+ }
3105
+ return brands;
3106
+ }
3107
+ function resolveCanonicalSymbol(type, checker) {
3108
+ const raw = type.aliasSymbol ?? type.getSymbol();
3109
+ if (raw === void 0) return void 0;
3110
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3111
+ }
3112
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3113
+ if (extensionRegistry === void 0) {
3114
+ return null;
3115
+ }
3116
+ const canonical = resolveCanonicalSymbol(type, checker);
3117
+ if (canonical === void 0) {
3118
+ return null;
3119
+ }
3120
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3121
+ if (registration === void 0) {
3122
+ return null;
3123
+ }
3124
+ return {
3125
+ kind: "custom",
3126
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3127
+ payload: null
3128
+ };
3129
+ }
3130
+ function isIntegerBrandedType(type) {
3131
+ if (!type.isIntersection()) {
3132
+ return false;
3133
+ }
3134
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
3135
+ if (!hasNumberBase) {
3136
+ return false;
3137
+ }
3138
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3139
+ }
3140
+ function resolveBrandedCustomType(type, extensionRegistry) {
3141
+ if (extensionRegistry === void 0) {
3142
+ return null;
3143
+ }
3144
+ for (const brand of collectBrandIdentifiers(type)) {
3145
+ const registration = extensionRegistry.findTypeByBrand(brand);
3146
+ if (registration === void 0) {
3147
+ continue;
3148
+ }
3149
+ return {
3150
+ kind: "custom",
3151
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3152
+ payload: null
3153
+ };
3154
+ }
3155
+ return null;
3156
+ }
3066
3157
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
3067
3158
  if (ts3.isParenthesizedTypeNode(typeNode)) {
3068
3159
  return isResolvableObjectLikeAliasTypeNode(typeNode.type);
@@ -4169,6 +4260,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4169
4260
  if (customType) {
4170
4261
  return customType;
4171
4262
  }
4263
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4264
+ if (symbolCustomType) {
4265
+ return symbolCustomType;
4266
+ }
4267
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4268
+ if (brandedCustomType) {
4269
+ return brandedCustomType;
4270
+ }
4172
4271
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4173
4272
  type,
4174
4273
  checker,
@@ -4183,6 +4282,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4183
4282
  if (primitiveAlias) {
4184
4283
  return primitiveAlias;
4185
4284
  }
4285
+ if (isIntegerBrandedType(type)) {
4286
+ return { kind: "primitive", primitiveKind: "integer" };
4287
+ }
4186
4288
  if (type.flags & ts3.TypeFlags.String) {
4187
4289
  return { kind: "primitive", primitiveKind: "string" };
4188
4290
  }
@@ -4285,7 +4387,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4285
4387
  return { kind: "primitive", primitiveKind: "string" };
4286
4388
  }
4287
4389
  function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4288
- if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
4390
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null)) && !isIntegerBrandedType(type)) {
4289
4391
  return null;
4290
4392
  }
4291
4393
  const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
@@ -4371,6 +4473,9 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4371
4473
  visitedAliases
4372
4474
  );
4373
4475
  }
4476
+ if (isIntegerBrandedType(type)) {
4477
+ return { kind: "primitive", primitiveKind: "integer" };
4478
+ }
4374
4479
  if (type.flags & ts3.TypeFlags.String) {
4375
4480
  return { kind: "primitive", primitiveKind: "string" };
4376
4481
  }
@@ -5123,7 +5228,7 @@ function createProgramContextFromProgram(program, filePath) {
5123
5228
  sourceFile
5124
5229
  };
5125
5230
  }
5126
- function createProgramContext(filePath) {
5231
+ function createProgramContext(filePath, additionalFiles) {
5127
5232
  const absolutePath = path.resolve(filePath);
5128
5233
  const fileDir = path.dirname(absolutePath);
5129
5234
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5146,7 +5251,8 @@ function createProgramContext(filePath) {
5146
5251
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5147
5252
  }
5148
5253
  compilerOptions = parsed.options;
5149
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5254
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5255
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5150
5256
  } else {
5151
5257
  compilerOptions = {
5152
5258
  target: ts4.ScriptTarget.ES2022,
@@ -5156,7 +5262,8 @@ function createProgramContext(filePath) {
5156
5262
  skipLibCheck: true,
5157
5263
  declaration: true
5158
5264
  };
5159
- fileNames = [absolutePath];
5265
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5266
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5160
5267
  }
5161
5268
  const program = ts4.createProgram(fileNames, compilerOptions);
5162
5269
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5466,10 +5573,139 @@ var init_program = __esm({
5466
5573
  }
5467
5574
  });
5468
5575
 
5576
+ // src/extensions/symbol-registry.ts
5577
+ import * as ts5 from "typescript";
5578
+ import * as path2 from "path";
5579
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5580
+ const symbolMap = /* @__PURE__ */ new Map();
5581
+ const normalizedPath = path2.resolve(configPath);
5582
+ const configFile = program.getSourceFile(normalizedPath);
5583
+ if (configFile === void 0) {
5584
+ return symbolMap;
5585
+ }
5586
+ function visit(node) {
5587
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5588
+ processDefineCustomTypeCall(node);
5589
+ }
5590
+ ts5.forEachChild(node, visit);
5591
+ }
5592
+ function processDefineCustomTypeCall(call) {
5593
+ const typeArgNode = call.typeArguments?.[0];
5594
+ if (typeArgNode === void 0) {
5595
+ return;
5596
+ }
5597
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5598
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5599
+ if (canonical === void 0) {
5600
+ return;
5601
+ }
5602
+ const typeName = extractTypeNameFromCallArg(call);
5603
+ if (typeName === null) {
5604
+ return;
5605
+ }
5606
+ let entry;
5607
+ const extensionId = extractEnclosingExtensionId(call, checker);
5608
+ if (extensionId !== null) {
5609
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5610
+ if (reg !== void 0) {
5611
+ entry = { extensionId, registration: reg };
5612
+ }
5613
+ }
5614
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5615
+ if (entry === void 0) {
5616
+ return;
5617
+ }
5618
+ symbolMap.set(canonical, entry);
5619
+ }
5620
+ visit(configFile);
5621
+ return symbolMap;
5622
+ }
5623
+ function isDefineCustomTypeCall(node, checker) {
5624
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5625
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5626
+ if (callSymbol !== void 0) {
5627
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5628
+ const decl = resolved.declarations?.[0];
5629
+ if (decl !== void 0) {
5630
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5631
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5632
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5633
+ }
5634
+ }
5635
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5636
+ }
5637
+ function resolveCanonicalSymbol2(type, checker) {
5638
+ const raw = type.aliasSymbol ?? type.getSymbol();
5639
+ if (raw === void 0) return void 0;
5640
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5641
+ }
5642
+ function extractTypeNameFromCallArg(call) {
5643
+ const arg = call.arguments[0];
5644
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5645
+ return null;
5646
+ }
5647
+ const typeNameProp = arg.properties.find(
5648
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5649
+ );
5650
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5651
+ return null;
5652
+ }
5653
+ return typeNameProp.initializer.text;
5654
+ }
5655
+ function extractEnclosingExtensionId(call, checker) {
5656
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5657
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5658
+ return extractExtensionIdFromCallArg(node);
5659
+ }
5660
+ }
5661
+ return null;
5662
+ }
5663
+ function isDefineExtensionCall(node, checker) {
5664
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5665
+ if (callSymbol !== void 0) {
5666
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5667
+ const decl = resolved.declarations?.[0];
5668
+ if (decl !== void 0) {
5669
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5670
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5671
+ }
5672
+ }
5673
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5674
+ }
5675
+ function extractExtensionIdFromCallArg(call) {
5676
+ const arg = call.arguments[0];
5677
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5678
+ return null;
5679
+ }
5680
+ const prop = arg.properties.find(
5681
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5682
+ );
5683
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5684
+ return null;
5685
+ }
5686
+ return prop.initializer.text;
5687
+ }
5688
+ function findRegistrationByTypeName(registry, typeName) {
5689
+ for (const ext of registry.extensions) {
5690
+ if (ext.types === void 0) {
5691
+ continue;
5692
+ }
5693
+ for (const type of ext.types) {
5694
+ if (type.typeName === typeName) {
5695
+ return { extensionId: ext.extensionId, registration: type };
5696
+ }
5697
+ }
5698
+ }
5699
+ return void 0;
5700
+ }
5701
+ var init_symbol_registry = __esm({
5702
+ "src/extensions/symbol-registry.ts"() {
5703
+ "use strict";
5704
+ }
5705
+ });
5706
+
5469
5707
  // src/validate/constraint-validator.ts
5470
- import {
5471
- analyzeConstraintTargets
5472
- } from "@formspec/analysis/internal";
5708
+ import { analyzeConstraintTargets } from "@formspec/analysis/internal";
5473
5709
  function validateFieldNode(ctx, field) {
5474
5710
  const analysis = analyzeConstraintTargets(
5475
5711
  field.name,
@@ -5555,7 +5791,7 @@ var init_validate = __esm({
5555
5791
  });
5556
5792
 
5557
5793
  // src/generators/class-schema.ts
5558
- import * as ts5 from "typescript";
5794
+ import * as ts6 from "typescript";
5559
5795
  function generateClassSchemas(analysis, source, options) {
5560
5796
  const result = generateClassSchemasDetailed(analysis, source, options);
5561
5797
  if (!result.ok || result.jsonSchema === void 0 || result.uiSchema === void 0) {
@@ -5614,7 +5850,8 @@ function formatLocation(location) {
5614
5850
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5615
5851
  }
5616
5852
  function generateSchemasFromClass(options) {
5617
- const ctx = createProgramContext(options.filePath);
5853
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5854
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5618
5855
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5619
5856
  if (!classDecl) {
5620
5857
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5679,7 +5916,8 @@ function generateSchemasDetailed(options) {
5679
5916
  function generateSchemasDetailedInternal(options) {
5680
5917
  let ctx;
5681
5918
  try {
5682
- ctx = createProgramContext(options.filePath);
5919
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5920
+ ctx = createProgramContext(options.filePath, additionalFiles);
5683
5921
  } catch (error) {
5684
5922
  return {
5685
5923
  ok: false,
@@ -5718,13 +5956,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5718
5956
  }
5719
5957
  function generateSchemasBatch(options) {
5720
5958
  const contextCache = /* @__PURE__ */ new Map();
5959
+ const resolved = resolveOptions(options);
5960
+ let symbolMapProgram;
5721
5961
  return options.targets.map((target) => {
5722
5962
  let ctx;
5723
5963
  try {
5724
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5964
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5725
5965
  const cachedContext = contextCache.get(cacheKey);
5726
5966
  if (cachedContext === void 0) {
5727
- ctx = createProgramContext(target.filePath);
5967
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5968
+ ctx = createProgramContext(target.filePath, additionalFiles);
5728
5969
  contextCache.set(cacheKey, ctx);
5729
5970
  } else {
5730
5971
  ctx = cachedContext;
@@ -5735,9 +5976,25 @@ function generateSchemasBatch(options) {
5735
5976
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5736
5977
  });
5737
5978
  }
5979
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5980
+ const symbolMap = buildSymbolMapFromConfig(
5981
+ options.configPath,
5982
+ ctx.program,
5983
+ ctx.checker,
5984
+ resolved.extensionRegistry
5985
+ );
5986
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5987
+ symbolMapProgram = ctx.program;
5988
+ }
5738
5989
  return withTarget(
5739
5990
  target,
5740
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5991
+ generateSchemasFromResolvedOptions(
5992
+ ctx,
5993
+ target.filePath,
5994
+ target.typeName,
5995
+ resolved,
5996
+ options.discriminator
5997
+ )
5741
5998
  );
5742
5999
  });
5743
6000
  }
@@ -5758,11 +6015,19 @@ function generateSchemasBatchFromProgram(options) {
5758
6015
  );
5759
6016
  });
5760
6017
  }
6018
+ function isMutableRegistry(reg) {
6019
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
6020
+ }
5761
6021
  function resolveOptions(options) {
5762
6022
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
6023
+ const legacyRegistry = options.extensionRegistry;
5763
6024
  return {
5764
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5765
- extensionRegistry: options.extensionRegistry ?? configRegistry,
6025
+ // When the caller provides the deprecated extensionRegistry field directly,
6026
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
6027
+ // because the legacy path was introduced before MutableExtensionRegistry was
6028
+ // split out; callers using createExtensionRegistry() always get a mutable
6029
+ // registry, and this cast is safe for all registries produced by this module.
6030
+ extensionRegistry: legacyRegistry ?? configRegistry,
5766
6031
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5767
6032
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5768
6033
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5773,13 +6038,31 @@ function resolveOptions(options) {
5773
6038
  }
5774
6039
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5775
6040
  const resolved = resolveOptions(options);
6041
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
6042
+ const symbolMap = buildSymbolMapFromConfig(
6043
+ options.configPath,
6044
+ ctx.program,
6045
+ ctx.checker,
6046
+ resolved.extensionRegistry
6047
+ );
6048
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
6049
+ }
6050
+ return generateSchemasFromResolvedOptions(
6051
+ ctx,
6052
+ filePath,
6053
+ typeName,
6054
+ resolved,
6055
+ options.discriminator
6056
+ );
6057
+ }
6058
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5776
6059
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5777
6060
  ctx,
5778
6061
  filePath,
5779
6062
  typeName,
5780
6063
  resolved.extensionRegistry,
5781
6064
  resolved.metadata,
5782
- options.discriminator
6065
+ discriminator
5783
6066
  );
5784
6067
  if (!analysisResult.ok) {
5785
6068
  return {
@@ -5827,13 +6110,14 @@ var init_class_schema = __esm({
5827
6110
  init_canonicalize();
5828
6111
  init_ir_generator();
5829
6112
  init_extensions();
6113
+ init_symbol_registry();
5830
6114
  init_ir_generator2();
5831
6115
  init_validate();
5832
6116
  }
5833
6117
  });
5834
6118
 
5835
6119
  // src/static-build.ts
5836
- import * as ts6 from "typescript";
6120
+ import * as ts7 from "typescript";
5837
6121
  function toStaticBuildContext(context) {
5838
6122
  return context;
5839
6123
  }
@@ -5848,7 +6132,7 @@ function getModuleSymbol(context) {
5848
6132
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5849
6133
  }
5850
6134
  function isSchemaSourceDeclaration(declaration) {
5851
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
6135
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5852
6136
  }
5853
6137
  function resolveModuleExport(context, exportName = "default") {
5854
6138
  const moduleSymbol = getModuleSymbol(context);
@@ -5859,7 +6143,7 @@ function resolveModuleExport(context, exportName = "default") {
5859
6143
  if (exportSymbol === null) {
5860
6144
  return null;
5861
6145
  }
5862
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
6146
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5863
6147
  }
5864
6148
  function resolveModuleExportDeclaration(context, exportName = "default") {
5865
6149
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
@@ -5872,7 +6156,7 @@ var init_static_build = __esm({
5872
6156
  });
5873
6157
 
5874
6158
  // src/generators/discovered-schema.ts
5875
- import * as ts7 from "typescript";
6159
+ import * as ts8 from "typescript";
5876
6160
  import { analyzeMetadataForNodeWithChecker as analyzeMetadataForNodeWithChecker2 } from "@formspec/analysis/internal";
5877
6161
  import { IR_VERSION as IR_VERSION3 } from "@formspec/core/internals";
5878
6162
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5882,17 +6166,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5882
6166
  };
5883
6167
  }
5884
6168
  function isNamedTypeDeclaration(declaration) {
5885
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6169
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5886
6170
  }
5887
6171
  function hasConcreteTypeArguments(type, checker) {
5888
6172
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5889
6173
  return true;
5890
6174
  }
5891
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6175
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5892
6176
  return false;
5893
6177
  }
5894
6178
  const objectType = type;
5895
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6179
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5896
6180
  return false;
5897
6181
  }
5898
6182
  return checker.getTypeArguments(objectType).length > 0;
@@ -5905,13 +6189,13 @@ function getNamedTypeDeclaration2(type) {
5905
6189
  return declaration;
5906
6190
  }
5907
6191
  }
5908
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6192
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5909
6193
  return aliasDeclaration;
5910
6194
  }
5911
6195
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5912
6196
  if (sourceNode !== void 0 && "name" in sourceNode) {
5913
6197
  const namedNode = sourceNode;
5914
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6198
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5915
6199
  return namedNode.name.text;
5916
6200
  }
5917
6201
  }
@@ -6030,17 +6314,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
6030
6314
  rootLogicalName: root.name
6031
6315
  }
6032
6316
  );
6033
- const schema = generateJsonSchemaFromIR(
6034
- ir,
6035
- {
6036
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6037
- extensionRegistry: options?.extensionRegistry,
6038
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6039
- enumSerialization: options?.enumSerialization,
6040
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6041
- vendorPrefix: options?.vendorPrefix
6042
- }
6043
- );
6317
+ const schema = generateJsonSchemaFromIR(ir, {
6318
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6319
+ extensionRegistry: options?.extensionRegistry,
6320
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6321
+ enumSerialization: options?.enumSerialization,
6322
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6323
+ vendorPrefix: options?.vendorPrefix
6324
+ });
6044
6325
  const result = schema.properties?.["__result"];
6045
6326
  if (result === void 0) {
6046
6327
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -6139,7 +6420,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
6139
6420
  }
6140
6421
  function generateSchemasFromDeclaration(options) {
6141
6422
  const filePath = options.declaration.getSourceFile().fileName;
6142
- if (ts7.isClassDeclaration(options.declaration)) {
6423
+ if (ts8.isClassDeclaration(options.declaration)) {
6143
6424
  return generateSchemasFromAnalysis(
6144
6425
  analyzeClassToIR(
6145
6426
  options.declaration,
@@ -6155,7 +6436,7 @@ function generateSchemasFromDeclaration(options) {
6155
6436
  options
6156
6437
  );
6157
6438
  }
6158
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6439
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
6159
6440
  return generateSchemasFromAnalysis(
6160
6441
  analyzeInterfaceToIR(
6161
6442
  options.declaration,
@@ -6171,7 +6452,7 @@ function generateSchemasFromDeclaration(options) {
6171
6452
  options
6172
6453
  );
6173
6454
  }
6174
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6455
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6175
6456
  const analyzedAlias = analyzeTypeAliasToIR(
6176
6457
  options.declaration,
6177
6458
  options.context.checker,
@@ -6234,7 +6515,7 @@ function generateSchemasFromReturnType(options) {
6234
6515
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6235
6516
  const type = unwrapPromiseType(options.context.checker, returnType);
6236
6517
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6237
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6518
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6238
6519
  return generateSchemasFromResolvedType({
6239
6520
  ...options,
6240
6521
  type,
@@ -6269,14 +6550,14 @@ function unwrapPromiseTypeNode(typeNode) {
6269
6550
  if (typeNode === void 0) {
6270
6551
  return void 0;
6271
6552
  }
6272
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6553
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6273
6554
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6274
6555
  return unwrapped ?? typeNode;
6275
6556
  }
6276
6557
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6277
6558
  }
6278
6559
  function isPromiseTypeReferenceNode(typeNode) {
6279
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6560
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6280
6561
  }
6281
6562
  var init_discovered_schema = __esm({
6282
6563
  "src/generators/discovered-schema.ts"() {
@@ -6521,7 +6802,7 @@ __export(index_exports, {
6521
6802
  writeSchemas: () => writeSchemas
6522
6803
  });
6523
6804
  import * as fs from "fs";
6524
- import * as path2 from "path";
6805
+ import * as path3 from "path";
6525
6806
  function buildFormSchemas(form, options) {
6526
6807
  return {
6527
6808
  jsonSchema: generateJsonSchema(form, options),
@@ -6546,8 +6827,8 @@ function writeSchemas(form, options) {
6546
6827
  if (!fs.existsSync(outDir)) {
6547
6828
  fs.mkdirSync(outDir, { recursive: true });
6548
6829
  }
6549
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6550
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6830
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6831
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6551
6832
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6552
6833
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6553
6834
  return { jsonSchemaPath, uiSchemaPath };
@@ -6574,7 +6855,7 @@ var init_index = __esm({
6574
6855
  });
6575
6856
 
6576
6857
  // src/cli.ts
6577
- import * as path3 from "path";
6858
+ import * as path4 from "path";
6578
6859
  import { pathToFileURL } from "url";
6579
6860
  function printHelp() {
6580
6861
  console.log(`
@@ -6663,7 +6944,7 @@ function parseArgs(args) {
6663
6944
  return null;
6664
6945
  }
6665
6946
  if (!name) {
6666
- name = path3.basename(inputFile, path3.extname(inputFile));
6947
+ name = path4.basename(inputFile, path4.extname(inputFile));
6667
6948
  }
6668
6949
  return { inputFile, outDir, name, enumSerialization };
6669
6950
  }
@@ -6674,7 +6955,7 @@ async function main() {
6674
6955
  process.exit(1);
6675
6956
  }
6676
6957
  const { inputFile, outDir, name, enumSerialization } = options;
6677
- const absoluteInput = path3.resolve(process.cwd(), inputFile);
6958
+ const absoluteInput = path4.resolve(process.cwd(), inputFile);
6678
6959
  try {
6679
6960
  const fileUrl = pathToFileURL(absoluteInput).href;
6680
6961
  const module = await import(fileUrl);