@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.js CHANGED
@@ -229,7 +229,9 @@ function resolveTypeNodeMetadata(type, options) {
229
229
  case "object":
230
230
  return {
231
231
  ...type,
232
- properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
232
+ properties: type.properties.map(
233
+ (property) => resolveObjectPropertyMetadata(property, options)
234
+ )
233
235
  };
234
236
  case "record":
235
237
  return {
@@ -1535,7 +1537,7 @@ function generateCustomType(type, ctx) {
1535
1537
  }
1536
1538
  return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
1537
1539
  }
1538
- var JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
1540
+ var VOCABULARY_MODE_BLOCKED_KEYWORDS = /* @__PURE__ */ new Set([
1539
1541
  "$schema",
1540
1542
  "$ref",
1541
1543
  "$defs",
@@ -1606,7 +1608,7 @@ function applyCustomConstraint(schema, constraint, ctx) {
1606
1608
  if (registration.emitsVocabularyKeywords) {
1607
1609
  const target = schema;
1608
1610
  for (const [key, value] of Object.entries(extensionSchema)) {
1609
- if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
1611
+ if (VOCABULARY_MODE_BLOCKED_KEYWORDS.has(key)) {
1610
1612
  throw new Error(
1611
1613
  `Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
1612
1614
  );
@@ -1655,10 +1657,7 @@ function generateJsonSchema(form, options) {
1655
1657
  const metadata = options?.metadata;
1656
1658
  const vendorPrefix = options?.vendorPrefix;
1657
1659
  const enumSerialization = options?.enumSerialization;
1658
- const ir = canonicalizeChainDSL(
1659
- form,
1660
- metadata !== void 0 ? { metadata } : void 0
1661
- );
1660
+ const ir = canonicalizeChainDSL(form, metadata !== void 0 ? { metadata } : void 0);
1662
1661
  const internalOptions = vendorPrefix === void 0 && enumSerialization === void 0 ? void 0 : {
1663
1662
  ...vendorPrefix !== void 0 && { vendorPrefix },
1664
1663
  ...enumSerialization !== void 0 && { enumSerialization }
@@ -1874,7 +1873,10 @@ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1874
1873
  break;
1875
1874
  }
1876
1875
  case "conditional": {
1877
- const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1876
+ const newRule = createShowRule(
1877
+ fieldNameMap.get(element.fieldName) ?? element.fieldName,
1878
+ element.value
1879
+ );
1878
1880
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1879
1881
  const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1880
1882
  result.push(...childElements);
@@ -1931,7 +1933,7 @@ function generateUiSchema(form, options) {
1931
1933
 
1932
1934
  // src/index.ts
1933
1935
  import * as fs from "fs";
1934
- import * as path2 from "path";
1936
+ import * as path3 from "path";
1935
1937
 
1936
1938
  // src/extensions/registry.ts
1937
1939
  import {
@@ -1955,8 +1957,10 @@ function buildConstraintTagSources(extensions) {
1955
1957
  }
1956
1958
  function createExtensionRegistry(extensions) {
1957
1959
  const reservedTagSources = buildConstraintTagSources(extensions);
1960
+ let symbolMap = /* @__PURE__ */ new Map();
1958
1961
  const typeMap = /* @__PURE__ */ new Map();
1959
1962
  const typeNameMap = /* @__PURE__ */ new Map();
1963
+ const brandMap = /* @__PURE__ */ new Map();
1960
1964
  const constraintMap = /* @__PURE__ */ new Map();
1961
1965
  const constraintTagMap = /* @__PURE__ */ new Map();
1962
1966
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
@@ -1980,6 +1984,20 @@ function createExtensionRegistry(extensions) {
1980
1984
  registration: type
1981
1985
  });
1982
1986
  }
1987
+ if (type.brand !== void 0) {
1988
+ if (type.brand === "__integerBrand") {
1989
+ throw new Error(
1990
+ `Brand "__integerBrand" is reserved for the builtin Integer type and cannot be registered by extensions`
1991
+ );
1992
+ }
1993
+ if (brandMap.has(type.brand)) {
1994
+ throw new Error(`Duplicate custom type brand: "${type.brand}"`);
1995
+ }
1996
+ brandMap.set(type.brand, {
1997
+ extensionId: ext.extensionId,
1998
+ registration: type
1999
+ });
2000
+ }
1983
2001
  if (type.builtinConstraintBroadenings !== void 0) {
1984
2002
  for (const broadening of type.builtinConstraintBroadenings) {
1985
2003
  const key = `${qualifiedId}:${broadening.tagName}`;
@@ -2049,7 +2067,10 @@ function createExtensionRegistry(extensions) {
2049
2067
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
2050
2068
  );
2051
2069
  }
2052
- if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS, normalizeConstraintTagName(canonicalTagName))) {
2070
+ if (Object.hasOwn(
2071
+ BUILTIN_CONSTRAINT_DEFINITIONS,
2072
+ normalizeConstraintTagName(canonicalTagName)
2073
+ )) {
2053
2074
  throw new Error(
2054
2075
  `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName(canonicalTagName)}".`
2055
2076
  );
@@ -2070,6 +2091,11 @@ function createExtensionRegistry(extensions) {
2070
2091
  extensions,
2071
2092
  findType: (typeId) => typeMap.get(typeId),
2072
2093
  findTypeByName: (typeName) => typeNameMap.get(typeName),
2094
+ findTypeByBrand: (brand) => brandMap.get(brand),
2095
+ findTypeBySymbol: (symbol) => symbolMap.get(symbol),
2096
+ setSymbolMap: (map) => {
2097
+ symbolMap = map;
2098
+ },
2073
2099
  findConstraint: (constraintId) => constraintMap.get(constraintId),
2074
2100
  findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName(tagName)),
2075
2101
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
@@ -2140,7 +2166,7 @@ var jsonSchema7Schema = z3.lazy(
2140
2166
  );
2141
2167
 
2142
2168
  // src/generators/class-schema.ts
2143
- import * as ts5 from "typescript";
2169
+ import * as ts6 from "typescript";
2144
2170
 
2145
2171
  // src/analyzer/program.ts
2146
2172
  import * as ts4 from "typescript";
@@ -2952,6 +2978,71 @@ function isObjectType(type) {
2952
2978
  function isIntersectionType(type) {
2953
2979
  return !!(type.flags & ts3.TypeFlags.Intersection);
2954
2980
  }
2981
+ function collectBrandIdentifiers(type) {
2982
+ if (!type.isIntersection()) {
2983
+ return [];
2984
+ }
2985
+ const brands = [];
2986
+ for (const prop of type.getProperties()) {
2987
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
2988
+ if (decl === void 0) continue;
2989
+ if (!ts3.isPropertySignature(decl) && !ts3.isPropertyDeclaration(decl)) continue;
2990
+ if (!ts3.isComputedPropertyName(decl.name)) continue;
2991
+ if (!ts3.isIdentifier(decl.name.expression)) continue;
2992
+ brands.push(decl.name.expression.text);
2993
+ }
2994
+ return brands;
2995
+ }
2996
+ function resolveCanonicalSymbol(type, checker) {
2997
+ const raw = type.aliasSymbol ?? type.getSymbol();
2998
+ if (raw === void 0) return void 0;
2999
+ return raw.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
3000
+ }
3001
+ function resolveSymbolBasedCustomType(type, extensionRegistry, checker) {
3002
+ if (extensionRegistry === void 0) {
3003
+ return null;
3004
+ }
3005
+ const canonical = resolveCanonicalSymbol(type, checker);
3006
+ if (canonical === void 0) {
3007
+ return null;
3008
+ }
3009
+ const registration = extensionRegistry.findTypeBySymbol(canonical);
3010
+ if (registration === void 0) {
3011
+ return null;
3012
+ }
3013
+ return {
3014
+ kind: "custom",
3015
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3016
+ payload: null
3017
+ };
3018
+ }
3019
+ function isIntegerBrandedType(type) {
3020
+ if (!type.isIntersection()) {
3021
+ return false;
3022
+ }
3023
+ const hasNumberBase = type.types.some((member) => !!(member.flags & ts3.TypeFlags.Number));
3024
+ if (!hasNumberBase) {
3025
+ return false;
3026
+ }
3027
+ return collectBrandIdentifiers(type).includes("__integerBrand");
3028
+ }
3029
+ function resolveBrandedCustomType(type, extensionRegistry) {
3030
+ if (extensionRegistry === void 0) {
3031
+ return null;
3032
+ }
3033
+ for (const brand of collectBrandIdentifiers(type)) {
3034
+ const registration = extensionRegistry.findTypeByBrand(brand);
3035
+ if (registration === void 0) {
3036
+ continue;
3037
+ }
3038
+ return {
3039
+ kind: "custom",
3040
+ typeId: `${registration.extensionId}/${registration.registration.typeName}`,
3041
+ payload: null
3042
+ };
3043
+ }
3044
+ return null;
3045
+ }
2955
3046
  function isResolvableObjectLikeAliasTypeNode(typeNode) {
2956
3047
  if (ts3.isParenthesizedTypeNode(typeNode)) {
2957
3048
  return isResolvableObjectLikeAliasTypeNode(typeNode.type);
@@ -4063,6 +4154,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4063
4154
  if (customType) {
4064
4155
  return customType;
4065
4156
  }
4157
+ const symbolCustomType = resolveSymbolBasedCustomType(type, extensionRegistry, checker);
4158
+ if (symbolCustomType) {
4159
+ return symbolCustomType;
4160
+ }
4161
+ const brandedCustomType = resolveBrandedCustomType(type, extensionRegistry);
4162
+ if (brandedCustomType) {
4163
+ return brandedCustomType;
4164
+ }
4066
4165
  const primitiveAlias = tryResolveNamedPrimitiveAlias(
4067
4166
  type,
4068
4167
  checker,
@@ -4077,6 +4176,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4077
4176
  if (primitiveAlias) {
4078
4177
  return primitiveAlias;
4079
4178
  }
4179
+ if (isIntegerBrandedType(type)) {
4180
+ return { kind: "primitive", primitiveKind: "integer" };
4181
+ }
4080
4182
  if (type.flags & ts3.TypeFlags.String) {
4081
4183
  return { kind: "primitive", primitiveKind: "string" };
4082
4184
  }
@@ -4179,7 +4281,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4179
4281
  return { kind: "primitive", primitiveKind: "string" };
4180
4282
  }
4181
4283
  function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
4182
- if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
4284
+ if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null)) && !isIntegerBrandedType(type)) {
4183
4285
  return null;
4184
4286
  }
4185
4287
  const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
@@ -4265,6 +4367,9 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
4265
4367
  visitedAliases
4266
4368
  );
4267
4369
  }
4370
+ if (isIntegerBrandedType(type)) {
4371
+ return { kind: "primitive", primitiveKind: "integer" };
4372
+ }
4268
4373
  if (type.flags & ts3.TypeFlags.String) {
4269
4374
  return { kind: "primitive", primitiveKind: "string" };
4270
4375
  }
@@ -5001,7 +5106,7 @@ function createProgramContextFromProgram(program, filePath) {
5001
5106
  sourceFile
5002
5107
  };
5003
5108
  }
5004
- function createProgramContext(filePath) {
5109
+ function createProgramContext(filePath, additionalFiles) {
5005
5110
  const absolutePath = path.resolve(filePath);
5006
5111
  const fileDir = path.dirname(absolutePath);
5007
5112
  const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
@@ -5024,7 +5129,8 @@ function createProgramContext(filePath) {
5024
5129
  throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
5025
5130
  }
5026
5131
  compilerOptions = parsed.options;
5027
- fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
5132
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5133
+ fileNames = [.../* @__PURE__ */ new Set([...parsed.fileNames, absolutePath, ...normalizedAdditional])];
5028
5134
  } else {
5029
5135
  compilerOptions = {
5030
5136
  target: ts4.ScriptTarget.ES2022,
@@ -5034,7 +5140,8 @@ function createProgramContext(filePath) {
5034
5140
  skipLibCheck: true,
5035
5141
  declaration: true
5036
5142
  };
5037
- fileNames = [absolutePath];
5143
+ const normalizedAdditional = (additionalFiles ?? []).map((f) => path.resolve(f));
5144
+ fileNames = [.../* @__PURE__ */ new Set([absolutePath, ...normalizedAdditional])];
5038
5145
  }
5039
5146
  const program = ts4.createProgram(fileNames, compilerOptions);
5040
5147
  const sourceFile = program.getSourceFile(absolutePath);
@@ -5338,10 +5445,134 @@ function makeFileProvenance(filePath) {
5338
5445
  };
5339
5446
  }
5340
5447
 
5448
+ // src/extensions/symbol-registry.ts
5449
+ import * as ts5 from "typescript";
5450
+ import * as path2 from "path";
5451
+ function buildSymbolMapFromConfig(configPath, program, checker, extensionRegistry) {
5452
+ const symbolMap = /* @__PURE__ */ new Map();
5453
+ const normalizedPath = path2.resolve(configPath);
5454
+ const configFile = program.getSourceFile(normalizedPath);
5455
+ if (configFile === void 0) {
5456
+ return symbolMap;
5457
+ }
5458
+ function visit(node) {
5459
+ if (ts5.isCallExpression(node) && isDefineCustomTypeCall(node, checker)) {
5460
+ processDefineCustomTypeCall(node);
5461
+ }
5462
+ ts5.forEachChild(node, visit);
5463
+ }
5464
+ function processDefineCustomTypeCall(call) {
5465
+ const typeArgNode = call.typeArguments?.[0];
5466
+ if (typeArgNode === void 0) {
5467
+ return;
5468
+ }
5469
+ const resolvedType = checker.getTypeFromTypeNode(typeArgNode);
5470
+ const canonical = resolveCanonicalSymbol2(resolvedType, checker);
5471
+ if (canonical === void 0) {
5472
+ return;
5473
+ }
5474
+ const typeName = extractTypeNameFromCallArg(call);
5475
+ if (typeName === null) {
5476
+ return;
5477
+ }
5478
+ let entry;
5479
+ const extensionId = extractEnclosingExtensionId(call, checker);
5480
+ if (extensionId !== null) {
5481
+ const reg = extensionRegistry.findType(`${extensionId}/${typeName}`);
5482
+ if (reg !== void 0) {
5483
+ entry = { extensionId, registration: reg };
5484
+ }
5485
+ }
5486
+ entry ??= findRegistrationByTypeName(extensionRegistry, typeName);
5487
+ if (entry === void 0) {
5488
+ return;
5489
+ }
5490
+ symbolMap.set(canonical, entry);
5491
+ }
5492
+ visit(configFile);
5493
+ return symbolMap;
5494
+ }
5495
+ function isDefineCustomTypeCall(node, checker) {
5496
+ if (node.typeArguments === void 0 || node.typeArguments.length === 0) return false;
5497
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5498
+ if (callSymbol !== void 0) {
5499
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5500
+ const decl = resolved.declarations?.[0];
5501
+ if (decl !== void 0) {
5502
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5503
+ return resolved.name === "defineCustomType" && // Match whether in node_modules/@formspec/core or monorepo packages/core.
5504
+ (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5505
+ }
5506
+ }
5507
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineCustomType";
5508
+ }
5509
+ function resolveCanonicalSymbol2(type, checker) {
5510
+ const raw = type.aliasSymbol ?? type.getSymbol();
5511
+ if (raw === void 0) return void 0;
5512
+ return raw.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(raw) : raw;
5513
+ }
5514
+ function extractTypeNameFromCallArg(call) {
5515
+ const arg = call.arguments[0];
5516
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5517
+ return null;
5518
+ }
5519
+ const typeNameProp = arg.properties.find(
5520
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "typeName"
5521
+ );
5522
+ if (typeNameProp === void 0 || !ts5.isStringLiteral(typeNameProp.initializer)) {
5523
+ return null;
5524
+ }
5525
+ return typeNameProp.initializer.text;
5526
+ }
5527
+ function extractEnclosingExtensionId(call, checker) {
5528
+ for (let node = call.parent; !ts5.isSourceFile(node); node = node.parent) {
5529
+ if (ts5.isCallExpression(node) && isDefineExtensionCall(node, checker)) {
5530
+ return extractExtensionIdFromCallArg(node);
5531
+ }
5532
+ }
5533
+ return null;
5534
+ }
5535
+ function isDefineExtensionCall(node, checker) {
5536
+ const callSymbol = checker.getSymbolAtLocation(node.expression);
5537
+ if (callSymbol !== void 0) {
5538
+ const resolved = callSymbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(callSymbol) : callSymbol;
5539
+ const decl = resolved.declarations?.[0];
5540
+ if (decl !== void 0) {
5541
+ const sourceFile = decl.getSourceFile().fileName.replace(/\\/g, "/");
5542
+ return resolved.name === "defineExtension" && (sourceFile.includes("@formspec/core") || sourceFile.includes("/packages/core/"));
5543
+ }
5544
+ }
5545
+ return ts5.isIdentifier(node.expression) && node.expression.text === "defineExtension";
5546
+ }
5547
+ function extractExtensionIdFromCallArg(call) {
5548
+ const arg = call.arguments[0];
5549
+ if (arg === void 0 || !ts5.isObjectLiteralExpression(arg)) {
5550
+ return null;
5551
+ }
5552
+ const prop = arg.properties.find(
5553
+ (p) => ts5.isPropertyAssignment(p) && ts5.isIdentifier(p.name) && p.name.text === "extensionId"
5554
+ );
5555
+ if (prop === void 0 || !ts5.isStringLiteral(prop.initializer)) {
5556
+ return null;
5557
+ }
5558
+ return prop.initializer.text;
5559
+ }
5560
+ function findRegistrationByTypeName(registry, typeName) {
5561
+ for (const ext of registry.extensions) {
5562
+ if (ext.types === void 0) {
5563
+ continue;
5564
+ }
5565
+ for (const type of ext.types) {
5566
+ if (type.typeName === typeName) {
5567
+ return { extensionId: ext.extensionId, registration: type };
5568
+ }
5569
+ }
5570
+ }
5571
+ return void 0;
5572
+ }
5573
+
5341
5574
  // src/validate/constraint-validator.ts
5342
- import {
5343
- analyzeConstraintTargets
5344
- } from "@formspec/analysis/internal";
5575
+ import { analyzeConstraintTargets } from "@formspec/analysis/internal";
5345
5576
  function validateFieldNode(ctx, field) {
5346
5577
  const analysis = analyzeConstraintTargets(
5347
5578
  field.name,
@@ -5472,7 +5703,8 @@ function formatLocation(location) {
5472
5703
  return `${location.file}:${String(location.line)}:${String(location.column)}`;
5473
5704
  }
5474
5705
  function generateSchemasFromClass(options) {
5475
- const ctx = createProgramContext(options.filePath);
5706
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5707
+ const ctx = createProgramContext(options.filePath, additionalFiles);
5476
5708
  const classDecl = findClassByName(ctx.sourceFile, options.className);
5477
5709
  if (!classDecl) {
5478
5710
  throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
@@ -5537,7 +5769,8 @@ function generateSchemasDetailed(options) {
5537
5769
  function generateSchemasDetailedInternal(options) {
5538
5770
  let ctx;
5539
5771
  try {
5540
- ctx = createProgramContext(options.filePath);
5772
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5773
+ ctx = createProgramContext(options.filePath, additionalFiles);
5541
5774
  } catch (error) {
5542
5775
  return {
5543
5776
  ok: false,
@@ -5576,13 +5809,16 @@ function generateSchemasFromProgramDetailedInternal(options) {
5576
5809
  }
5577
5810
  function generateSchemasBatch(options) {
5578
5811
  const contextCache = /* @__PURE__ */ new Map();
5812
+ const resolved = resolveOptions(options);
5813
+ let symbolMapProgram;
5579
5814
  return options.targets.map((target) => {
5580
5815
  let ctx;
5581
5816
  try {
5582
- const cacheKey = ts5.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5817
+ const cacheKey = ts6.sys.useCaseSensitiveFileNames ? target.filePath : target.filePath.toLowerCase();
5583
5818
  const cachedContext = contextCache.get(cacheKey);
5584
5819
  if (cachedContext === void 0) {
5585
- ctx = createProgramContext(target.filePath);
5820
+ const additionalFiles = options.configPath !== void 0 ? [options.configPath] : void 0;
5821
+ ctx = createProgramContext(target.filePath, additionalFiles);
5586
5822
  contextCache.set(cacheKey, ctx);
5587
5823
  } else {
5588
5824
  ctx = cachedContext;
@@ -5593,9 +5829,25 @@ function generateSchemasBatch(options) {
5593
5829
  diagnostics: [createProgramContextFailureDiagnostic(target.filePath, error)]
5594
5830
  });
5595
5831
  }
5832
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry) && ctx.program !== symbolMapProgram) {
5833
+ const symbolMap = buildSymbolMapFromConfig(
5834
+ options.configPath,
5835
+ ctx.program,
5836
+ ctx.checker,
5837
+ resolved.extensionRegistry
5838
+ );
5839
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5840
+ symbolMapProgram = ctx.program;
5841
+ }
5596
5842
  return withTarget(
5597
5843
  target,
5598
- generateSchemasFromDetailedProgramContext(ctx, target.filePath, target.typeName, options)
5844
+ generateSchemasFromResolvedOptions(
5845
+ ctx,
5846
+ target.filePath,
5847
+ target.typeName,
5848
+ resolved,
5849
+ options.discriminator
5850
+ )
5599
5851
  );
5600
5852
  });
5601
5853
  }
@@ -5616,11 +5868,19 @@ function generateSchemasBatchFromProgram(options) {
5616
5868
  );
5617
5869
  });
5618
5870
  }
5871
+ function isMutableRegistry(reg) {
5872
+ return "setSymbolMap" in reg && typeof reg.setSymbolMap === "function";
5873
+ }
5619
5874
  function resolveOptions(options) {
5620
5875
  const configRegistry = options.config?.extensions !== void 0 ? createExtensionRegistry(options.config.extensions) : void 0;
5876
+ const legacyRegistry = options.extensionRegistry;
5621
5877
  return {
5622
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5623
- extensionRegistry: options.extensionRegistry ?? configRegistry,
5878
+ // When the caller provides the deprecated extensionRegistry field directly,
5879
+ // it is typed as the read-only ExtensionRegistry interface. We cast here
5880
+ // because the legacy path was introduced before MutableExtensionRegistry was
5881
+ // split out; callers using createExtensionRegistry() always get a mutable
5882
+ // registry, and this cast is safe for all registries produced by this module.
5883
+ extensionRegistry: legacyRegistry ?? configRegistry,
5624
5884
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5625
5885
  vendorPrefix: options.vendorPrefix ?? options.config?.vendorPrefix,
5626
5886
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
@@ -5631,13 +5891,31 @@ function resolveOptions(options) {
5631
5891
  }
5632
5892
  function generateSchemasFromDetailedProgramContext(ctx, filePath, typeName, options) {
5633
5893
  const resolved = resolveOptions(options);
5894
+ if (options.configPath !== void 0 && resolved.extensionRegistry !== void 0 && isMutableRegistry(resolved.extensionRegistry)) {
5895
+ const symbolMap = buildSymbolMapFromConfig(
5896
+ options.configPath,
5897
+ ctx.program,
5898
+ ctx.checker,
5899
+ resolved.extensionRegistry
5900
+ );
5901
+ resolved.extensionRegistry.setSymbolMap(symbolMap);
5902
+ }
5903
+ return generateSchemasFromResolvedOptions(
5904
+ ctx,
5905
+ filePath,
5906
+ typeName,
5907
+ resolved,
5908
+ options.discriminator
5909
+ );
5910
+ }
5911
+ function generateSchemasFromResolvedOptions(ctx, filePath, typeName, resolved, discriminator) {
5634
5912
  const analysisResult = analyzeNamedTypeToIRFromProgramContextDetailed(
5635
5913
  ctx,
5636
5914
  filePath,
5637
5915
  typeName,
5638
5916
  resolved.extensionRegistry,
5639
5917
  resolved.metadata,
5640
- options.discriminator
5918
+ discriminator
5641
5919
  );
5642
5920
  if (!analysisResult.ok) {
5643
5921
  return {
@@ -5679,7 +5957,7 @@ function createProgramContextFailureDiagnostic(filePath, error) {
5679
5957
  }
5680
5958
 
5681
5959
  // src/static-build.ts
5682
- import * as ts6 from "typescript";
5960
+ import * as ts7 from "typescript";
5683
5961
  function toStaticBuildContext(context) {
5684
5962
  return context;
5685
5963
  }
@@ -5694,7 +5972,7 @@ function getModuleSymbol(context) {
5694
5972
  return context.checker.getSymbolAtLocation(context.sourceFile) ?? sourceFileWithSymbol.symbol;
5695
5973
  }
5696
5974
  function isSchemaSourceDeclaration(declaration) {
5697
- return ts6.isClassDeclaration(declaration) || ts6.isInterfaceDeclaration(declaration) || ts6.isTypeAliasDeclaration(declaration);
5975
+ return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
5698
5976
  }
5699
5977
  function resolveModuleExport(context, exportName = "default") {
5700
5978
  const moduleSymbol = getModuleSymbol(context);
@@ -5705,14 +5983,14 @@ function resolveModuleExport(context, exportName = "default") {
5705
5983
  if (exportSymbol === null) {
5706
5984
  return null;
5707
5985
  }
5708
- return exportSymbol.flags & ts6.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5986
+ return exportSymbol.flags & ts7.SymbolFlags.Alias ? context.checker.getAliasedSymbol(exportSymbol) : exportSymbol;
5709
5987
  }
5710
5988
  function resolveModuleExportDeclaration(context, exportName = "default") {
5711
5989
  return resolveModuleExport(context, exportName)?.declarations?.find(isSchemaSourceDeclaration) ?? null;
5712
5990
  }
5713
5991
 
5714
5992
  // src/generators/discovered-schema.ts
5715
- import * as ts7 from "typescript";
5993
+ import * as ts8 from "typescript";
5716
5994
  import { analyzeMetadataForNodeWithChecker as analyzeMetadataForNodeWithChecker2 } from "@formspec/analysis/internal";
5717
5995
  import { IR_VERSION as IR_VERSION3 } from "@formspec/core/internals";
5718
5996
  function toDiscoveredTypeSchemas(result, resolvedMetadata) {
@@ -5722,17 +6000,17 @@ function toDiscoveredTypeSchemas(result, resolvedMetadata) {
5722
6000
  };
5723
6001
  }
5724
6002
  function isNamedTypeDeclaration(declaration) {
5725
- return ts7.isClassDeclaration(declaration) || ts7.isInterfaceDeclaration(declaration) || ts7.isTypeAliasDeclaration(declaration);
6003
+ return ts8.isClassDeclaration(declaration) || ts8.isInterfaceDeclaration(declaration) || ts8.isTypeAliasDeclaration(declaration);
5726
6004
  }
5727
6005
  function hasConcreteTypeArguments(type, checker) {
5728
6006
  if ("aliasTypeArguments" in type && Array.isArray(type.aliasTypeArguments) && type.aliasTypeArguments.length > 0) {
5729
6007
  return true;
5730
6008
  }
5731
- if ((type.flags & ts7.TypeFlags.Object) === 0) {
6009
+ if ((type.flags & ts8.TypeFlags.Object) === 0) {
5732
6010
  return false;
5733
6011
  }
5734
6012
  const objectType = type;
5735
- if ((objectType.objectFlags & ts7.ObjectFlags.Reference) === 0) {
6013
+ if ((objectType.objectFlags & ts8.ObjectFlags.Reference) === 0) {
5736
6014
  return false;
5737
6015
  }
5738
6016
  return checker.getTypeArguments(objectType).length > 0;
@@ -5745,13 +6023,13 @@ function getNamedTypeDeclaration2(type) {
5745
6023
  return declaration;
5746
6024
  }
5747
6025
  }
5748
- const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts7.isTypeAliasDeclaration);
6026
+ const aliasDeclaration = type.aliasSymbol?.declarations?.find(ts8.isTypeAliasDeclaration);
5749
6027
  return aliasDeclaration;
5750
6028
  }
5751
6029
  function getFallbackName(sourceNode, fallback = "AnonymousType") {
5752
6030
  if (sourceNode !== void 0 && "name" in sourceNode) {
5753
6031
  const namedNode = sourceNode;
5754
- if (namedNode.name !== void 0 && ts7.isIdentifier(namedNode.name)) {
6032
+ if (namedNode.name !== void 0 && ts8.isIdentifier(namedNode.name)) {
5755
6033
  return namedNode.name.text;
5756
6034
  }
5757
6035
  }
@@ -5870,17 +6148,14 @@ function toStandaloneJsonSchema(root, typeRegistry, options) {
5870
6148
  rootLogicalName: root.name
5871
6149
  }
5872
6150
  );
5873
- const schema = generateJsonSchemaFromIR(
5874
- ir,
5875
- {
5876
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5877
- extensionRegistry: options?.extensionRegistry,
5878
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5879
- enumSerialization: options?.enumSerialization,
5880
- // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
5881
- vendorPrefix: options?.vendorPrefix
5882
- }
5883
- );
6151
+ const schema = generateJsonSchemaFromIR(ir, {
6152
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6153
+ extensionRegistry: options?.extensionRegistry,
6154
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6155
+ enumSerialization: options?.enumSerialization,
6156
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- migration bridge reads deprecated fields
6157
+ vendorPrefix: options?.vendorPrefix
6158
+ });
5884
6159
  const result = schema.properties?.["__result"];
5885
6160
  if (result === void 0) {
5886
6161
  throw new Error("FormSpec failed to extract the standalone schema root from the synthetic IR.");
@@ -5979,7 +6254,7 @@ function generateSchemasFromResolvedType(options, skipNamedDeclaration = false,
5979
6254
  }
5980
6255
  function generateSchemasFromDeclaration(options) {
5981
6256
  const filePath = options.declaration.getSourceFile().fileName;
5982
- if (ts7.isClassDeclaration(options.declaration)) {
6257
+ if (ts8.isClassDeclaration(options.declaration)) {
5983
6258
  return generateSchemasFromAnalysis(
5984
6259
  analyzeClassToIR(
5985
6260
  options.declaration,
@@ -5995,7 +6270,7 @@ function generateSchemasFromDeclaration(options) {
5995
6270
  options
5996
6271
  );
5997
6272
  }
5998
- if (ts7.isInterfaceDeclaration(options.declaration)) {
6273
+ if (ts8.isInterfaceDeclaration(options.declaration)) {
5999
6274
  return generateSchemasFromAnalysis(
6000
6275
  analyzeInterfaceToIR(
6001
6276
  options.declaration,
@@ -6011,7 +6286,7 @@ function generateSchemasFromDeclaration(options) {
6011
6286
  options
6012
6287
  );
6013
6288
  }
6014
- if (ts7.isTypeAliasDeclaration(options.declaration)) {
6289
+ if (ts8.isTypeAliasDeclaration(options.declaration)) {
6015
6290
  const analyzedAlias = analyzeTypeAliasToIR(
6016
6291
  options.declaration,
6017
6292
  options.context.checker,
@@ -6074,7 +6349,7 @@ function generateSchemasFromReturnType(options) {
6074
6349
  const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
6075
6350
  const type = unwrapPromiseType(options.context.checker, returnType);
6076
6351
  const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
6077
- const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6352
+ const fallbackName = options.declaration.name !== void 0 && ts8.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
6078
6353
  return generateSchemasFromResolvedType({
6079
6354
  ...options,
6080
6355
  type,
@@ -6109,14 +6384,14 @@ function unwrapPromiseTypeNode(typeNode) {
6109
6384
  if (typeNode === void 0) {
6110
6385
  return void 0;
6111
6386
  }
6112
- if (ts7.isParenthesizedTypeNode(typeNode)) {
6387
+ if (ts8.isParenthesizedTypeNode(typeNode)) {
6113
6388
  const unwrapped = unwrapPromiseTypeNode(typeNode.type);
6114
6389
  return unwrapped ?? typeNode;
6115
6390
  }
6116
6391
  return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
6117
6392
  }
6118
6393
  function isPromiseTypeReferenceNode(typeNode) {
6119
- return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6394
+ return ts8.isTypeReferenceNode(typeNode) && ts8.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
6120
6395
  }
6121
6396
 
6122
6397
  // src/generators/mixed-authoring.ts
@@ -6338,8 +6613,8 @@ function writeSchemas(form, options) {
6338
6613
  if (!fs.existsSync(outDir)) {
6339
6614
  fs.mkdirSync(outDir, { recursive: true });
6340
6615
  }
6341
- const jsonSchemaPath = path2.join(outDir, `${name}-schema.json`);
6342
- const uiSchemaPath = path2.join(outDir, `${name}-uischema.json`);
6616
+ const jsonSchemaPath = path3.join(outDir, `${name}-schema.json`);
6617
+ const uiSchemaPath = path3.join(outDir, `${name}-uischema.json`);
6343
6618
  fs.writeFileSync(jsonSchemaPath, JSON.stringify(jsonSchema, null, indent));
6344
6619
  fs.writeFileSync(uiSchemaPath, JSON.stringify(uiSchema2, null, indent));
6345
6620
  return { jsonSchemaPath, uiSchemaPath };