@faapi/faapi 3.2.0 → 3.3.0

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.
package/dist/cli/index.js CHANGED
@@ -476,9 +476,9 @@ var init_constants = __esm({
476
476
  });
477
477
 
478
478
  // src/utils/normalizePath.ts
479
- function normalizePath(path23) {
480
- if (!path23) return "";
481
- let result = path23.replace(/\\/g, "/");
479
+ function normalizePath(path24) {
480
+ if (!path24) return "";
481
+ let result = path24.replace(/\\/g, "/");
482
482
  result = result.replace(/\/+/g, "/");
483
483
  result = result.replace(/\/+$/, "");
484
484
  if (result && !result.startsWith("/")) {
@@ -964,35 +964,143 @@ var init_extractToolMetadata = __esm({
964
964
 
965
965
  // src/ast/createProgram.ts
966
966
  import ts3 from "typescript";
967
+ import fs7 from "fs";
968
+ import path7 from "path";
967
969
  function invalidateProgramCache() {
968
970
  programCache.clear();
971
+ tsConfigCache.clear();
972
+ }
973
+ function findTsConfig(filePath) {
974
+ let dir = path7.dirname(filePath);
975
+ const root = path7.parse(dir).root;
976
+ while (true) {
977
+ const candidate = path7.join(dir, "tsconfig.json");
978
+ if (fs7.existsSync(candidate)) {
979
+ return candidate;
980
+ }
981
+ if (dir === root) return null;
982
+ const parent = path7.dirname(dir);
983
+ if (parent === dir) return null;
984
+ dir = parent;
985
+ }
986
+ }
987
+ function parseTsConfig(tsconfigPath) {
988
+ const cached = tsConfigCache.get(tsconfigPath);
989
+ if (cached) return cached;
990
+ const result = { fileNames: [] };
991
+ try {
992
+ const configFile = ts3.readConfigFile(tsconfigPath, (p) => fs7.readFileSync(p, "utf-8"));
993
+ if (configFile.error) {
994
+ tsConfigCache.set(tsconfigPath, result);
995
+ return result;
996
+ }
997
+ const config = configFile.config ?? {};
998
+ const basePath = path7.dirname(tsconfigPath);
999
+ const parsed = ts3.parseJsonConfigFileContent(
1000
+ config,
1001
+ ts3.sys,
1002
+ basePath,
1003
+ /* existingOptions */
1004
+ void 0,
1005
+ tsconfigPath
1006
+ );
1007
+ result.fileNames = parsed.fileNames;
1008
+ if (parsed.options.module !== void 0) {
1009
+ result.module = parsed.options.module;
1010
+ }
1011
+ if (parsed.options.moduleResolution !== void 0) {
1012
+ result.moduleResolution = parsed.options.moduleResolution;
1013
+ }
1014
+ } catch {
1015
+ }
1016
+ tsConfigCache.set(tsconfigPath, result);
1017
+ return result;
969
1018
  }
970
1019
  function createProgram(filePath) {
971
1020
  const cached = programCache.get(filePath);
972
1021
  if (cached) {
973
1022
  return cached;
974
1023
  }
975
- const program = ts3.createProgram([filePath], {
1024
+ const program = buildProgram([filePath], findTsConfig(filePath));
1025
+ programCache.set(filePath, program);
1026
+ return program;
1027
+ }
1028
+ function createPrograms(filePaths) {
1029
+ const unique = [...new Set(filePaths)];
1030
+ const result = /* @__PURE__ */ new Map();
1031
+ const groups = /* @__PURE__ */ new Map();
1032
+ const noTsconfigFiles = [];
1033
+ for (const filePath of unique) {
1034
+ const tsconfigPath = findTsConfig(filePath);
1035
+ if (!tsconfigPath) {
1036
+ noTsconfigFiles.push(filePath);
1037
+ continue;
1038
+ }
1039
+ const group = groups.get(tsconfigPath);
1040
+ if (group) {
1041
+ group.files.push(filePath);
1042
+ } else {
1043
+ groups.set(tsconfigPath, { tsconfigPath, files: [filePath] });
1044
+ }
1045
+ }
1046
+ for (const { tsconfigPath, files } of groups.values()) {
1047
+ const cacheKey = `shared::${tsconfigPath}::${[...files].sort().join("|")}`;
1048
+ let program = programCache.get(cacheKey);
1049
+ if (!program) {
1050
+ program = buildProgram(files, tsconfigPath);
1051
+ programCache.set(cacheKey, program);
1052
+ }
1053
+ for (const filePath of files) {
1054
+ result.set(filePath, program);
1055
+ }
1056
+ }
1057
+ for (const filePath of noTsconfigFiles) {
1058
+ result.set(filePath, createProgram(filePath));
1059
+ }
1060
+ return result;
1061
+ }
1062
+ function buildProgram(entryFiles, tsconfigPath) {
1063
+ const options = {
976
1064
  strict: true,
977
1065
  target: ts3.ScriptTarget.ES2022,
978
1066
  module: ts3.ModuleKind.NodeNext,
979
1067
  moduleResolution: ts3.ModuleResolutionKind.NodeNext,
980
1068
  skipLibCheck: true,
981
1069
  noEmit: true
982
- });
983
- programCache.set(filePath, program);
984
- return program;
1070
+ };
1071
+ const rootNames = [...entryFiles];
1072
+ if (tsconfigPath) {
1073
+ const tsOptions = parseTsConfig(tsconfigPath);
1074
+ if (tsOptions.module !== void 0) {
1075
+ options.module = tsOptions.module;
1076
+ }
1077
+ if (tsOptions.moduleResolution !== void 0) {
1078
+ options.moduleResolution = tsOptions.moduleResolution;
1079
+ }
1080
+ if (tsOptions.fileNames.length > 0) {
1081
+ for (const fileName of tsOptions.fileNames) {
1082
+ if (!rootNames.includes(fileName)) {
1083
+ rootNames.push(fileName);
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ return ts3.createProgram(rootNames, options);
985
1089
  }
986
- var programCache;
1090
+ var programCache, tsConfigCache;
987
1091
  var init_createProgram = __esm({
988
1092
  "src/ast/createProgram.ts"() {
989
1093
  "use strict";
990
1094
  programCache = /* @__PURE__ */ new Map();
1095
+ tsConfigCache = /* @__PURE__ */ new Map();
991
1096
  }
992
1097
  });
993
1098
 
994
1099
  // src/ast/resolveTypeNode.ts
995
1100
  import ts4 from "typescript";
1101
+ function setProgramContext(program) {
1102
+ currentProgram = program;
1103
+ }
996
1104
  function resolveTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
997
1105
  const kind = typeNode.kind;
998
1106
  switch (kind) {
@@ -1305,11 +1413,69 @@ function resolveTypeReference(typeNode, checker, visited = /* @__PURE__ */ new S
1305
1413
  if (ts4.isEnumDeclaration(declaration)) {
1306
1414
  return resolveEnumDeclaration(declaration);
1307
1415
  }
1416
+ if (ts4.isImportSpecifier(declaration) || ts4.isImportClause(declaration)) {
1417
+ const resolved = resolveImportAlias(typeNode, symbol, checker, visited);
1418
+ if (resolved) return resolved;
1419
+ }
1308
1420
  }
1309
1421
  }
1310
1422
  }
1311
1423
  throw new SchemaExtractionError(typeNode.getText(), `\u65E0\u6CD5\u89E3\u6790\u7684\u5F15\u7528\u7C7B\u578B "${typeName}"`);
1312
1424
  }
1425
+ function resolveImportAlias(typeNode, symbol, checker, visited) {
1426
+ const typeName = typeNode.typeName.getText();
1427
+ try {
1428
+ const aliased = checker.getAliasedSymbol(symbol);
1429
+ if (aliased && aliased.declarations && aliased.declarations.length > 0) {
1430
+ const decl = aliased.declarations[0];
1431
+ if (ts4.isInterfaceDeclaration(decl)) {
1432
+ return resolveInterfaceDeclaration(decl, checker, visited);
1433
+ }
1434
+ if (ts4.isTypeAliasDeclaration(decl)) {
1435
+ return resolveTypeNode(decl.type, checker, visited);
1436
+ }
1437
+ if (ts4.isEnumDeclaration(decl)) {
1438
+ return resolveEnumDeclaration(decl);
1439
+ }
1440
+ }
1441
+ } catch {
1442
+ }
1443
+ const program = currentProgram;
1444
+ if (!program) return null;
1445
+ const allSFs = program.getSourceFiles();
1446
+ for (const sourceFile of allSFs) {
1447
+ if (sourceFile.fileName.includes("/node_modules/") || sourceFile.fileName.includes("typescript/lib/")) {
1448
+ continue;
1449
+ }
1450
+ const found = findTopLevelDecl(sourceFile, typeName);
1451
+ if (found) {
1452
+ if (found.kind === "interface") {
1453
+ return resolveInterfaceDeclaration(found.node, checker, visited);
1454
+ }
1455
+ if (found.kind === "typeAlias") {
1456
+ return resolveTypeNode(found.node.type, checker, visited);
1457
+ }
1458
+ if (found.kind === "enum") {
1459
+ return resolveEnumDeclaration(found.node);
1460
+ }
1461
+ }
1462
+ }
1463
+ return null;
1464
+ }
1465
+ function findTopLevelDecl(sourceFile, typeName) {
1466
+ let found = null;
1467
+ ts4.forEachChild(sourceFile, (node) => {
1468
+ if (found) return;
1469
+ if (ts4.isInterfaceDeclaration(node) && node.name.text === typeName) {
1470
+ found = { kind: "interface", node };
1471
+ } else if (ts4.isTypeAliasDeclaration(node) && node.name.text === typeName) {
1472
+ found = { kind: "typeAlias", node };
1473
+ } else if (ts4.isEnumDeclaration(node) && node.name.text === typeName) {
1474
+ found = { kind: "enum", node };
1475
+ }
1476
+ });
1477
+ return found;
1478
+ }
1313
1479
  function resolveEnumDeclaration(node) {
1314
1480
  const members = [];
1315
1481
  let nextNumericValue = 0;
@@ -1488,10 +1654,11 @@ function validateConstraints(constraints, type, fieldName) {
1488
1654
  }
1489
1655
  }
1490
1656
  }
1491
- var SchemaExtractionError, NUMBER_CONSTRAINT_KINDS, LENGTH_CONSTRAINT_KINDS, STRING_FORMAT_CONSTRAINT_KINDS;
1657
+ var currentProgram, SchemaExtractionError, NUMBER_CONSTRAINT_KINDS, LENGTH_CONSTRAINT_KINDS, STRING_FORMAT_CONSTRAINT_KINDS;
1492
1658
  var init_resolveTypeNode = __esm({
1493
1659
  "src/ast/resolveTypeNode.ts"() {
1494
1660
  "use strict";
1661
+ currentProgram = null;
1495
1662
  SchemaExtractionError = class extends Error {
1496
1663
  constructor(typeText, reason, options) {
1497
1664
  super(`\u65E0\u6CD5\u89E3\u6790\u7C7B\u578B "${typeText}": ${reason}`, options);
@@ -1531,80 +1698,90 @@ function extractTypeInfo(program, filePath, typeName) {
1531
1698
  const sourceFile = program.getSourceFile(filePath);
1532
1699
  if (!sourceFile) return null;
1533
1700
  const checker = program.getTypeChecker();
1534
- let result = null;
1535
- ts5.forEachChild(sourceFile, (node) => {
1536
- if (result) return;
1537
- if (ts5.isInterfaceDeclaration(node) && node.name.text === typeName) {
1538
- const visited = /* @__PURE__ */ new Set();
1539
- visited.add(typeName);
1540
- const runtimeType = withFileContext(
1541
- filePath,
1542
- typeName,
1543
- () => resolveInterfaceDeclaration(node, checker, visited)
1544
- );
1545
- result = {
1546
- name: typeName,
1547
- properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1548
- runtimeType
1549
- };
1550
- return;
1551
- }
1552
- if (ts5.isTypeAliasDeclaration(node) && node.name.text === typeName) {
1553
- const visited = /* @__PURE__ */ new Set();
1554
- visited.add(typeName);
1555
- const runtimeType = withFileContext(
1556
- filePath,
1557
- typeName,
1558
- () => resolveTypeNode(node.type, checker, visited)
1559
- );
1560
- result = {
1561
- name: typeName,
1562
- properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1563
- runtimeType
1564
- };
1565
- return;
1566
- }
1567
- });
1568
- return result;
1701
+ setProgramContext(program);
1702
+ try {
1703
+ let result = null;
1704
+ ts5.forEachChild(sourceFile, (node) => {
1705
+ if (result) return;
1706
+ if (ts5.isInterfaceDeclaration(node) && node.name.text === typeName) {
1707
+ const visited = /* @__PURE__ */ new Set();
1708
+ visited.add(typeName);
1709
+ const runtimeType = withFileContext(
1710
+ filePath,
1711
+ typeName,
1712
+ () => resolveInterfaceDeclaration(node, checker, visited)
1713
+ );
1714
+ result = {
1715
+ name: typeName,
1716
+ properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1717
+ runtimeType
1718
+ };
1719
+ return;
1720
+ }
1721
+ if (ts5.isTypeAliasDeclaration(node) && node.name.text === typeName) {
1722
+ const visited = /* @__PURE__ */ new Set();
1723
+ visited.add(typeName);
1724
+ const runtimeType = withFileContext(
1725
+ filePath,
1726
+ typeName,
1727
+ () => resolveTypeNode(node.type, checker, visited)
1728
+ );
1729
+ result = {
1730
+ name: typeName,
1731
+ properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1732
+ runtimeType
1733
+ };
1734
+ return;
1735
+ }
1736
+ });
1737
+ return result;
1738
+ } finally {
1739
+ setProgramContext(null);
1740
+ }
1569
1741
  }
1570
1742
  function extractAllTypes(program, filePath) {
1571
1743
  const sourceFile = program.getSourceFile(filePath);
1572
1744
  if (!sourceFile) return /* @__PURE__ */ new Map();
1573
1745
  const checker = program.getTypeChecker();
1574
- const result = /* @__PURE__ */ new Map();
1575
- ts5.forEachChild(sourceFile, (node) => {
1576
- if (ts5.isInterfaceDeclaration(node)) {
1577
- const visited = /* @__PURE__ */ new Set();
1578
- visited.add(node.name.text);
1579
- const runtimeType = withFileContext(
1580
- filePath,
1581
- node.name.text,
1582
- () => resolveInterfaceDeclaration(node, checker, visited)
1583
- );
1584
- result.set(node.name.text, {
1585
- name: node.name.text,
1586
- properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1587
- runtimeType
1588
- });
1589
- return;
1590
- }
1591
- if (ts5.isTypeAliasDeclaration(node)) {
1592
- const visited = /* @__PURE__ */ new Set();
1593
- visited.add(node.name.text);
1594
- const runtimeType = withFileContext(
1595
- filePath,
1596
- node.name.text,
1597
- () => resolveTypeNode(node.type, checker, visited)
1598
- );
1599
- result.set(node.name.text, {
1600
- name: node.name.text,
1601
- properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1602
- runtimeType
1603
- });
1604
- return;
1605
- }
1606
- });
1607
- return result;
1746
+ setProgramContext(program);
1747
+ try {
1748
+ const result = /* @__PURE__ */ new Map();
1749
+ ts5.forEachChild(sourceFile, (node) => {
1750
+ if (ts5.isInterfaceDeclaration(node)) {
1751
+ const visited = /* @__PURE__ */ new Set();
1752
+ visited.add(node.name.text);
1753
+ const runtimeType = withFileContext(
1754
+ filePath,
1755
+ node.name.text,
1756
+ () => resolveInterfaceDeclaration(node, checker, visited)
1757
+ );
1758
+ result.set(node.name.text, {
1759
+ name: node.name.text,
1760
+ properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1761
+ runtimeType
1762
+ });
1763
+ return;
1764
+ }
1765
+ if (ts5.isTypeAliasDeclaration(node)) {
1766
+ const visited = /* @__PURE__ */ new Set();
1767
+ visited.add(node.name.text);
1768
+ const runtimeType = withFileContext(
1769
+ filePath,
1770
+ node.name.text,
1771
+ () => resolveTypeNode(node.type, checker, visited)
1772
+ );
1773
+ result.set(node.name.text, {
1774
+ name: node.name.text,
1775
+ properties: runtimeType.kind === "object" ? runtimeType.properties : [],
1776
+ runtimeType
1777
+ });
1778
+ return;
1779
+ }
1780
+ });
1781
+ return result;
1782
+ } finally {
1783
+ setProgramContext(null);
1784
+ }
1608
1785
  }
1609
1786
  function withFileContext(filePath, typeName, fn) {
1610
1787
  try {
@@ -2110,11 +2287,11 @@ var init_analyzeInjection = __esm({
2110
2287
  });
2111
2288
 
2112
2289
  // src/cli/collectRouteSchemaSources.ts
2113
- import path7 from "path";
2290
+ import path8 from "path";
2114
2291
  function collectRouteSchemaSources(routes, rootDir) {
2115
2292
  const methodsByFile = /* @__PURE__ */ new Map();
2116
2293
  for (const route of routes) {
2117
- const filePath = rootDir ? path7.resolve(rootDir, route.filePath) : route.filePath;
2294
+ const filePath = rootDir ? path8.resolve(rootDir, route.filePath) : route.filePath;
2118
2295
  let entry = methodsByFile.get(filePath);
2119
2296
  if (!entry) {
2120
2297
  entry = { urlPath: route.urlPath, methods: /* @__PURE__ */ new Set() };
@@ -2122,12 +2299,11 @@ function collectRouteSchemaSources(routes, rootDir) {
2122
2299
  }
2123
2300
  entry.methods.add(route.method);
2124
2301
  }
2125
- const programByFile = /* @__PURE__ */ new Map();
2302
+ const programByFile = createPrograms([...methodsByFile.keys()]);
2126
2303
  const allTypesByFile = /* @__PURE__ */ new Map();
2127
2304
  const mergedAllTypes = /* @__PURE__ */ new Map();
2128
2305
  for (const filePath of methodsByFile.keys()) {
2129
- const program = createProgram(filePath);
2130
- programByFile.set(filePath, program);
2306
+ const program = programByFile.get(filePath);
2131
2307
  const allTypes = extractAllTypes(program, filePath);
2132
2308
  allTypesByFile.set(filePath, allTypes);
2133
2309
  for (const [name, info] of allTypes) {
@@ -2177,8 +2353,8 @@ __export(generateSchemaFiles_exports, {
2177
2353
  getRuntimeSchemaPath: () => getRuntimeSchemaPath,
2178
2354
  getSchemaOutputPath: () => getSchemaOutputPath
2179
2355
  });
2180
- import path8 from "path";
2181
- import fs7 from "fs/promises";
2356
+ import path9 from "path";
2357
+ import fs8 from "fs/promises";
2182
2358
  function getSchemaOutputPath(sourceFile, dist, rootDir) {
2183
2359
  let rel = sourceFile.replace(/\\/g, "/");
2184
2360
  if (rel.startsWith("src/")) {
@@ -2186,7 +2362,7 @@ function getSchemaOutputPath(sourceFile, dist, rootDir) {
2186
2362
  }
2187
2363
  const idx = rel.lastIndexOf("/");
2188
2364
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
2189
- return path8.resolve(rootDir, dist, relDir, "zod.js");
2365
+ return path9.resolve(rootDir, dist, relDir, "zod.js");
2190
2366
  }
2191
2367
  function getRuntimeSchemaPath(filePath, dist, rootDir) {
2192
2368
  let rel = filePath.replace(/\\/g, "/");
@@ -2197,7 +2373,7 @@ function getRuntimeSchemaPath(filePath, dist, rootDir) {
2197
2373
  }
2198
2374
  const idx = rel.lastIndexOf("/");
2199
2375
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
2200
- return path8.resolve(rootDir, dist, relDir, "zod.js");
2376
+ return path9.resolve(rootDir, dist, relDir, "zod.js");
2201
2377
  }
2202
2378
  function getHelpersImportPath(relDir) {
2203
2379
  if (!relDir) return `./${HELPERS_FILENAME}`;
@@ -2247,7 +2423,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2247
2423
  }
2248
2424
  const fileEntries = [];
2249
2425
  for (const [filePath, fileSources] of sourcesByFile) {
2250
- const relFile = path8.relative(rootDir, filePath).replace(/\\/g, "/");
2426
+ const relFile = path9.relative(rootDir, filePath).replace(/\\/g, "/");
2251
2427
  const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
2252
2428
  const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
2253
2429
  let relForDir = relFile;
@@ -2262,7 +2438,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2262
2438
  }
2263
2439
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
2264
2440
  if (usesCoerceHelpers(allSourceCode)) {
2265
- const helpersPath = path8.resolve(rootDir, dist, HELPERS_FILENAME);
2441
+ const helpersPath = path9.resolve(rootDir, dist, HELPERS_FILENAME);
2266
2442
  await writeSchemaFile(helpersPath, generateHelpersFileSource());
2267
2443
  }
2268
2444
  await Promise.all(
@@ -2270,8 +2446,8 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2270
2446
  );
2271
2447
  }
2272
2448
  async function writeSchemaFile(outputPath, source) {
2273
- await fs7.mkdir(path8.dirname(outputPath), { recursive: true });
2274
- await fs7.writeFile(outputPath, source, "utf-8");
2449
+ await fs8.mkdir(path9.dirname(outputPath), { recursive: true });
2450
+ await fs8.writeFile(outputPath, source, "utf-8");
2275
2451
  }
2276
2452
  var init_generateSchemaFiles = __esm({
2277
2453
  "src/cli/generateSchemaFiles.ts"() {
@@ -2282,8 +2458,8 @@ var init_generateSchemaFiles = __esm({
2282
2458
  });
2283
2459
 
2284
2460
  // src/cli/generateToolArtifacts.ts
2285
- import path9 from "path";
2286
- import fs8 from "fs/promises";
2461
+ import path10 from "path";
2462
+ import fs9 from "fs/promises";
2287
2463
  import { existsSync } from "fs";
2288
2464
  function getToolSchemaOutputPath(sourceFile, dist, rootDir) {
2289
2465
  let rel = sourceFile.replace(/\\/g, "/");
@@ -2292,7 +2468,7 @@ function getToolSchemaOutputPath(sourceFile, dist, rootDir) {
2292
2468
  }
2293
2469
  const idx = rel.lastIndexOf("/");
2294
2470
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
2295
- return path9.resolve(rootDir, dist, relDir, "zod.js");
2471
+ return path10.resolve(rootDir, dist, relDir, "zod.js");
2296
2472
  }
2297
2473
  function toProdFilePath3(filePath, dist) {
2298
2474
  let rel = filePath.replace(/\\/g, "/");
@@ -2312,12 +2488,12 @@ function serializeTools(tools, dist = "dist") {
2312
2488
  }));
2313
2489
  }
2314
2490
  async function writeToolsModule(manifest, outputPath) {
2315
- const dir = path9.dirname(outputPath);
2316
- await fs8.mkdir(dir, { recursive: true });
2491
+ const dir = path10.dirname(outputPath);
2492
+ await fs9.mkdir(dir, { recursive: true });
2317
2493
  const content = `// \u81EA\u52A8\u751F\u6210,\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91(faapi build/dev \u4EA7\u7269)
2318
2494
  export const tools = ${JSON.stringify(manifest, null, 2)};
2319
2495
  `;
2320
- await fs8.writeFile(outputPath, content, "utf-8");
2496
+ await fs9.writeFile(outputPath, content, "utf-8");
2321
2497
  }
2322
2498
  function hydrateTools(manifest) {
2323
2499
  return manifest.map((t) => ({
@@ -2332,7 +2508,7 @@ function collectToolSchemaSources(tools, rootDir) {
2332
2508
  const toolsByFile = /* @__PURE__ */ new Map();
2333
2509
  for (const tool of tools) {
2334
2510
  if (!tool.inputTypeName) continue;
2335
- const absPath = path9.resolve(rootDir, tool.filePath);
2511
+ const absPath = path10.resolve(rootDir, tool.filePath);
2336
2512
  let list = toolsByFile.get(absPath);
2337
2513
  if (!list) {
2338
2514
  list = [];
@@ -2340,15 +2516,16 @@ function collectToolSchemaSources(tools, rootDir) {
2340
2516
  }
2341
2517
  list.push(tool);
2342
2518
  }
2519
+ const programByFile = createPrograms([...toolsByFile.keys()]);
2343
2520
  const allTypesByFile = /* @__PURE__ */ new Map();
2344
2521
  for (const filePath of toolsByFile.keys()) {
2345
- const program = createProgram(filePath);
2522
+ const program = programByFile.get(filePath);
2346
2523
  const allTypes = extractAllTypes(program, filePath);
2347
2524
  allTypesByFile.set(filePath, allTypes);
2348
2525
  }
2349
2526
  const sources = [];
2350
2527
  for (const [filePath, fileTools] of toolsByFile) {
2351
- const program = createProgram(filePath);
2528
+ const program = programByFile.get(filePath);
2352
2529
  for (const tool of fileTools) {
2353
2530
  const inputTypeName = tool.inputTypeName;
2354
2531
  const typeInfo = extractTypeInfo(program, filePath, inputTypeName);
@@ -2394,16 +2571,17 @@ function generateToolSchemaFileSource(sources, allTypes, helpersImportPath) {
2394
2571
  }
2395
2572
  async function maybeGenerateHelpers(allSourceCode, distDir) {
2396
2573
  if (!usesCoerceHelpers(allSourceCode)) return;
2397
- const helpersPath = path9.resolve(distDir, HELPERS_FILENAME);
2574
+ const helpersPath = path10.resolve(distDir, HELPERS_FILENAME);
2398
2575
  if (existsSync(helpersPath)) return;
2399
- await fs8.mkdir(path9.dirname(helpersPath), { recursive: true });
2400
- await fs8.writeFile(helpersPath, generateHelpersFileSource(), "utf-8");
2576
+ await fs9.mkdir(path10.dirname(helpersPath), { recursive: true });
2577
+ await fs9.writeFile(helpersPath, generateHelpersFileSource(), "utf-8");
2401
2578
  }
2402
2579
  async function generateToolArtifacts(tools, rootDir, dist, options) {
2403
2580
  const metadata = [];
2581
+ const programByFile = createPrograms(tools.map((m) => path10.resolve(rootDir, m.filePath)));
2404
2582
  for (const manifest of tools) {
2405
- const absPath = path9.resolve(rootDir, manifest.filePath);
2406
- const program = createProgram(absPath);
2583
+ const absPath = path10.resolve(rootDir, manifest.filePath);
2584
+ const program = programByFile.get(absPath);
2407
2585
  const result = extractToolMetadata(program, absPath, manifest.functionName, {
2408
2586
  name: manifest.name,
2409
2587
  filePath: manifest.filePath
@@ -2413,7 +2591,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
2413
2591
  }
2414
2592
  }
2415
2593
  const serialized = serializeTools(metadata, dist);
2416
- const toolsPath = path9.resolve(rootDir, dist, TOOLS_FILE);
2594
+ const toolsPath = path10.resolve(rootDir, dist, TOOLS_FILE);
2417
2595
  await writeToolsModule(serialized, toolsPath);
2418
2596
  if (options?.skipSchema) {
2419
2597
  return metadata;
@@ -2436,7 +2614,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
2436
2614
  }
2437
2615
  const fileEntries = [];
2438
2616
  for (const [filePath, fileSources] of sourcesByFile) {
2439
- const relFile = path9.relative(rootDir, filePath).replace(/\\/g, "/");
2617
+ const relFile = path10.relative(rootDir, filePath).replace(/\\/g, "/");
2440
2618
  const outputPath = getToolSchemaOutputPath(relFile, dist, rootDir);
2441
2619
  const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
2442
2620
  let relForDir = relFile;
@@ -2450,7 +2628,7 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
2450
2628
  fileEntries.push({ outputPath, source });
2451
2629
  }
2452
2630
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
2453
- const distDir = path9.resolve(rootDir, dist);
2631
+ const distDir = path10.resolve(rootDir, dist);
2454
2632
  await maybeGenerateHelpers(allSourceCode, distDir);
2455
2633
  await Promise.all(
2456
2634
  fileEntries.map(({ outputPath, source }) => writeToolSchemaFile(outputPath, source))
@@ -2458,8 +2636,8 @@ async function generateToolArtifacts(tools, rootDir, dist, options) {
2458
2636
  return metadata;
2459
2637
  }
2460
2638
  async function writeToolSchemaFile(outputPath, source) {
2461
- await fs8.mkdir(path9.dirname(outputPath), { recursive: true });
2462
- await fs8.writeFile(outputPath, source, "utf-8");
2639
+ await fs9.mkdir(path10.dirname(outputPath), { recursive: true });
2640
+ await fs9.writeFile(outputPath, source, "utf-8");
2463
2641
  }
2464
2642
  var TOOLS_FILE;
2465
2643
  var init_generateToolArtifacts = __esm({
@@ -2476,8 +2654,8 @@ var init_generateToolArtifacts = __esm({
2476
2654
 
2477
2655
  // src/agents/scanAgents.ts
2478
2656
  import fg3 from "fast-glob";
2479
- import path10 from "path";
2480
- import fs9 from "fs";
2657
+ import path11 from "path";
2658
+ import fs10 from "fs";
2481
2659
  function extractAgentNameFromPath(filePath) {
2482
2660
  const normalized = filePath.replace(/\\/g, "/");
2483
2661
  const match = normalized.match(/(?:^|\/)agents\/([^/]+)\/handler\.ts$/);
@@ -2507,8 +2685,8 @@ async function scanAgents(rootDir, patterns) {
2507
2685
  if (fileName !== "handler.ts" && fileName !== "handler.js") {
2508
2686
  continue;
2509
2687
  }
2510
- const absPath = path10.resolve(rootDir, normalizedFile);
2511
- const source = await fs9.promises.readFile(absPath, "utf8").catch(() => "");
2688
+ const absPath = path11.resolve(rootDir, normalizedFile);
2689
+ const source = await fs10.promises.readFile(absPath, "utf8").catch(() => "");
2512
2690
  const { hasRun } = detectAgentExports(source);
2513
2691
  const name = extractAgentNameFromPath(normalizedFile);
2514
2692
  const prevFile = seen.get(name);
@@ -2728,8 +2906,8 @@ var init_extractAgentMetadata = __esm({
2728
2906
  });
2729
2907
 
2730
2908
  // src/cli/generateAgentArtifacts.ts
2731
- import path11 from "path";
2732
- import fs10 from "fs/promises";
2909
+ import path12 from "path";
2910
+ import fs11 from "fs/promises";
2733
2911
  function toProdFilePath4(filePath, dist) {
2734
2912
  let rel = filePath.replace(/\\/g, "/");
2735
2913
  if (rel.startsWith("src/")) {
@@ -2752,12 +2930,12 @@ function serializeAgents(agents, dist = "dist") {
2752
2930
  }));
2753
2931
  }
2754
2932
  async function writeAgentsModule(manifest, outputPath) {
2755
- const dir = path11.dirname(outputPath);
2756
- await fs10.mkdir(dir, { recursive: true });
2933
+ const dir = path12.dirname(outputPath);
2934
+ await fs11.mkdir(dir, { recursive: true });
2757
2935
  const content = `// \u81EA\u52A8\u751F\u6210,\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91(faapi build/dev \u4EA7\u7269)
2758
2936
  export const agents = ${JSON.stringify(manifest, null, 2)};
2759
2937
  `;
2760
- await fs10.writeFile(outputPath, content, "utf-8");
2938
+ await fs11.writeFile(outputPath, content, "utf-8");
2761
2939
  }
2762
2940
  function hydrateAgents(manifest) {
2763
2941
  return manifest.map((a) => ({
@@ -2774,9 +2952,10 @@ function hydrateAgents(manifest) {
2774
2952
  }
2775
2953
  async function generateAgentArtifacts(agents, rootDir, dist) {
2776
2954
  const metadata = [];
2955
+ const programByFile = createPrograms(agents.map((m) => path12.resolve(rootDir, m.filePath)));
2777
2956
  for (const manifest of agents) {
2778
- const absPath = path11.resolve(rootDir, manifest.filePath);
2779
- const program = createProgram(absPath);
2957
+ const absPath = path12.resolve(rootDir, manifest.filePath);
2958
+ const program = programByFile.get(absPath);
2780
2959
  const result = extractAgentMetadata(program, absPath, {
2781
2960
  name: manifest.name,
2782
2961
  filePath: manifest.filePath,
@@ -2787,7 +2966,7 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
2787
2966
  }
2788
2967
  }
2789
2968
  const serialized = serializeAgents(metadata, dist);
2790
- const agentsPath = path11.resolve(rootDir, dist, AGENTS_FILE);
2969
+ const agentsPath = path12.resolve(rootDir, dist, AGENTS_FILE);
2791
2970
  await writeAgentsModule(serialized, agentsPath);
2792
2971
  return metadata;
2793
2972
  }
@@ -2802,15 +2981,15 @@ var init_generateAgentArtifacts = __esm({
2802
2981
  });
2803
2982
 
2804
2983
  // src/config/loadConfig.ts
2805
- import path12 from "path";
2806
- import fs11 from "fs";
2984
+ import path13 from "path";
2985
+ import fs12 from "fs";
2807
2986
  async function loadConfig(rootDir, dist) {
2808
- const configProductPath = path12.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
2809
- if (fs11.existsSync(configProductPath)) {
2987
+ const configProductPath = path13.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
2988
+ if (fs12.existsSync(configProductPath)) {
2810
2989
  const module = await importWithCacheBust(configProductPath);
2811
2990
  return module.default ?? {};
2812
2991
  }
2813
- const hasSourceConfig = fs11.existsSync(path12.join(rootDir, "faapi.config.ts")) || fs11.existsSync(path12.join(rootDir, "faapi.config.js"));
2992
+ const hasSourceConfig = fs12.existsSync(path13.join(rootDir, "faapi.config.ts")) || fs12.existsSync(path13.join(rootDir, "faapi.config.js"));
2814
2993
  if (hasSourceConfig) {
2815
2994
  throw new Error(
2816
2995
  `[faapi] ${dist}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
@@ -2828,8 +3007,8 @@ var init_loadConfig = __esm({
2828
3007
  });
2829
3008
 
2830
3009
  // src/cli/loadEnv.ts
2831
- import fs12 from "fs";
2832
- import path13 from "path";
3010
+ import fs13 from "fs";
3011
+ import path14 from "path";
2833
3012
  function resolveEnv() {
2834
3013
  return process.env.NODE_ENV || "development";
2835
3014
  }
@@ -2895,9 +3074,9 @@ function loadEnv(rootDir) {
2895
3074
  const files = getEnvFiles(env);
2896
3075
  const merged = {};
2897
3076
  for (const file of files) {
2898
- const filePath = path13.join(rootDir, file);
2899
- if (!fs12.existsSync(filePath)) continue;
2900
- const content = fs12.readFileSync(filePath, "utf-8");
3077
+ const filePath = path14.join(rootDir, file);
3078
+ if (!fs13.existsSync(filePath)) continue;
3079
+ const content = fs13.readFileSync(filePath, "utf-8");
2901
3080
  const parsed = parseEnvFile(content, merged);
2902
3081
  Object.assign(merged, parsed);
2903
3082
  }
@@ -3003,7 +3182,7 @@ var init_esm = __esm({
3003
3182
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
3004
3183
  const statMethod = opts.lstat ? lstat : stat;
3005
3184
  if (wantBigintFsStats) {
3006
- this._stat = (path23) => statMethod(path23, { bigint: true });
3185
+ this._stat = (path24) => statMethod(path24, { bigint: true });
3007
3186
  } else {
3008
3187
  this._stat = statMethod;
3009
3188
  }
@@ -3028,8 +3207,8 @@ var init_esm = __esm({
3028
3207
  const par = this.parent;
3029
3208
  const fil = par && par.files;
3030
3209
  if (fil && fil.length > 0) {
3031
- const { path: path23, depth } = par;
3032
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path23));
3210
+ const { path: path24, depth } = par;
3211
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
3033
3212
  const awaited = await Promise.all(slice);
3034
3213
  for (const entry of awaited) {
3035
3214
  if (!entry)
@@ -3069,20 +3248,20 @@ var init_esm = __esm({
3069
3248
  this.reading = false;
3070
3249
  }
3071
3250
  }
3072
- async _exploreDir(path23, depth) {
3251
+ async _exploreDir(path24, depth) {
3073
3252
  let files;
3074
3253
  try {
3075
- files = await readdir(path23, this._rdOptions);
3254
+ files = await readdir(path24, this._rdOptions);
3076
3255
  } catch (error) {
3077
3256
  this._onError(error);
3078
3257
  }
3079
- return { files, depth, path: path23 };
3258
+ return { files, depth, path: path24 };
3080
3259
  }
3081
- async _formatEntry(dirent, path23) {
3260
+ async _formatEntry(dirent, path24) {
3082
3261
  let entry;
3083
3262
  const basename3 = this._isDirent ? dirent.name : dirent;
3084
3263
  try {
3085
- const fullPath = presolve(pjoin(path23, basename3));
3264
+ const fullPath = presolve(pjoin(path24, basename3));
3086
3265
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
3087
3266
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
3088
3267
  } catch (err) {
@@ -3143,16 +3322,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
3143
3322
  import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
3144
3323
  import * as sysPath from "path";
3145
3324
  import { type as osType } from "os";
3146
- function createFsWatchInstance(path23, options, listener, errHandler, emitRaw) {
3325
+ function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
3147
3326
  const handleEvent = (rawEvent, evPath) => {
3148
- listener(path23);
3149
- emitRaw(rawEvent, evPath, { watchedPath: path23 });
3150
- if (evPath && path23 !== evPath) {
3151
- fsWatchBroadcast(sysPath.resolve(path23, evPath), KEY_LISTENERS, sysPath.join(path23, evPath));
3327
+ listener(path24);
3328
+ emitRaw(rawEvent, evPath, { watchedPath: path24 });
3329
+ if (evPath && path24 !== evPath) {
3330
+ fsWatchBroadcast(sysPath.resolve(path24, evPath), KEY_LISTENERS, sysPath.join(path24, evPath));
3152
3331
  }
3153
3332
  };
3154
3333
  try {
3155
- return fs_watch(path23, {
3334
+ return fs_watch(path24, {
3156
3335
  persistent: options.persistent
3157
3336
  }, handleEvent);
3158
3337
  } catch (error) {
@@ -3497,12 +3676,12 @@ var init_handler = __esm({
3497
3676
  listener(val1, val2, val3);
3498
3677
  });
3499
3678
  };
3500
- setFsWatchListener = (path23, fullPath, options, handlers) => {
3679
+ setFsWatchListener = (path24, fullPath, options, handlers) => {
3501
3680
  const { listener, errHandler, rawEmitter } = handlers;
3502
3681
  let cont = FsWatchInstances.get(fullPath);
3503
3682
  let watcher;
3504
3683
  if (!options.persistent) {
3505
- watcher = createFsWatchInstance(path23, options, listener, errHandler, rawEmitter);
3684
+ watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
3506
3685
  if (!watcher)
3507
3686
  return;
3508
3687
  return watcher.close.bind(watcher);
@@ -3513,7 +3692,7 @@ var init_handler = __esm({
3513
3692
  addAndConvert(cont, KEY_RAW, rawEmitter);
3514
3693
  } else {
3515
3694
  watcher = createFsWatchInstance(
3516
- path23,
3695
+ path24,
3517
3696
  options,
3518
3697
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
3519
3698
  errHandler,
@@ -3528,7 +3707,7 @@ var init_handler = __esm({
3528
3707
  cont.watcherUnusable = true;
3529
3708
  if (isWindows && error.code === "EPERM") {
3530
3709
  try {
3531
- const fd = await open(path23, "r");
3710
+ const fd = await open(path24, "r");
3532
3711
  await fd.close();
3533
3712
  broadcastErr(error);
3534
3713
  } catch (err) {
@@ -3559,7 +3738,7 @@ var init_handler = __esm({
3559
3738
  };
3560
3739
  };
3561
3740
  FsWatchFileInstances = /* @__PURE__ */ new Map();
3562
- setFsWatchFileListener = (path23, fullPath, options, handlers) => {
3741
+ setFsWatchFileListener = (path24, fullPath, options, handlers) => {
3563
3742
  const { listener, rawEmitter } = handlers;
3564
3743
  let cont = FsWatchFileInstances.get(fullPath);
3565
3744
  const copts = cont && cont.options;
@@ -3581,7 +3760,7 @@ var init_handler = __esm({
3581
3760
  });
3582
3761
  const currmtime = curr.mtimeMs;
3583
3762
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
3584
- foreach(cont.listeners, (listener2) => listener2(path23, curr));
3763
+ foreach(cont.listeners, (listener2) => listener2(path24, curr));
3585
3764
  }
3586
3765
  })
3587
3766
  };
@@ -3609,13 +3788,13 @@ var init_handler = __esm({
3609
3788
  * @param listener on fs change
3610
3789
  * @returns closer for the watcher instance
3611
3790
  */
3612
- _watchWithNodeFs(path23, listener) {
3791
+ _watchWithNodeFs(path24, listener) {
3613
3792
  const opts = this.fsw.options;
3614
- const directory = sysPath.dirname(path23);
3615
- const basename3 = sysPath.basename(path23);
3793
+ const directory = sysPath.dirname(path24);
3794
+ const basename3 = sysPath.basename(path24);
3616
3795
  const parent = this.fsw._getWatchedDir(directory);
3617
3796
  parent.add(basename3);
3618
- const absolutePath = sysPath.resolve(path23);
3797
+ const absolutePath = sysPath.resolve(path24);
3619
3798
  const options = {
3620
3799
  persistent: opts.persistent
3621
3800
  };
@@ -3625,12 +3804,12 @@ var init_handler = __esm({
3625
3804
  if (opts.usePolling) {
3626
3805
  const enableBin = opts.interval !== opts.binaryInterval;
3627
3806
  options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
3628
- closer = setFsWatchFileListener(path23, absolutePath, options, {
3807
+ closer = setFsWatchFileListener(path24, absolutePath, options, {
3629
3808
  listener,
3630
3809
  rawEmitter: this.fsw._emitRaw
3631
3810
  });
3632
3811
  } else {
3633
- closer = setFsWatchListener(path23, absolutePath, options, {
3812
+ closer = setFsWatchListener(path24, absolutePath, options, {
3634
3813
  listener,
3635
3814
  errHandler: this._boundHandleError,
3636
3815
  rawEmitter: this.fsw._emitRaw
@@ -3652,7 +3831,7 @@ var init_handler = __esm({
3652
3831
  let prevStats = stats;
3653
3832
  if (parent.has(basename3))
3654
3833
  return;
3655
- const listener = async (path23, newStats) => {
3834
+ const listener = async (path24, newStats) => {
3656
3835
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
3657
3836
  return;
3658
3837
  if (!newStats || newStats.mtimeMs === 0) {
@@ -3666,11 +3845,11 @@ var init_handler = __esm({
3666
3845
  this.fsw._emit(EV.CHANGE, file, newStats2);
3667
3846
  }
3668
3847
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
3669
- this.fsw._closeFile(path23);
3848
+ this.fsw._closeFile(path24);
3670
3849
  prevStats = newStats2;
3671
3850
  const closer2 = this._watchWithNodeFs(file, listener);
3672
3851
  if (closer2)
3673
- this.fsw._addPathCloser(path23, closer2);
3852
+ this.fsw._addPathCloser(path24, closer2);
3674
3853
  } else {
3675
3854
  prevStats = newStats2;
3676
3855
  }
@@ -3702,7 +3881,7 @@ var init_handler = __esm({
3702
3881
  * @param item basename of this item
3703
3882
  * @returns true if no more processing is needed for this entry.
3704
3883
  */
3705
- async _handleSymlink(entry, directory, path23, item) {
3884
+ async _handleSymlink(entry, directory, path24, item) {
3706
3885
  if (this.fsw.closed) {
3707
3886
  return;
3708
3887
  }
@@ -3712,7 +3891,7 @@ var init_handler = __esm({
3712
3891
  this.fsw._incrReadyCount();
3713
3892
  let linkPath;
3714
3893
  try {
3715
- linkPath = await fsrealpath(path23);
3894
+ linkPath = await fsrealpath(path24);
3716
3895
  } catch (e) {
3717
3896
  this.fsw._emitReady();
3718
3897
  return true;
@@ -3722,12 +3901,12 @@ var init_handler = __esm({
3722
3901
  if (dir.has(item)) {
3723
3902
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
3724
3903
  this.fsw._symlinkPaths.set(full, linkPath);
3725
- this.fsw._emit(EV.CHANGE, path23, entry.stats);
3904
+ this.fsw._emit(EV.CHANGE, path24, entry.stats);
3726
3905
  }
3727
3906
  } else {
3728
3907
  dir.add(item);
3729
3908
  this.fsw._symlinkPaths.set(full, linkPath);
3730
- this.fsw._emit(EV.ADD, path23, entry.stats);
3909
+ this.fsw._emit(EV.ADD, path24, entry.stats);
3731
3910
  }
3732
3911
  this.fsw._emitReady();
3733
3912
  return true;
@@ -3756,9 +3935,9 @@ var init_handler = __esm({
3756
3935
  return;
3757
3936
  }
3758
3937
  const item = entry.path;
3759
- let path23 = sysPath.join(directory, item);
3938
+ let path24 = sysPath.join(directory, item);
3760
3939
  current.add(item);
3761
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path23, item)) {
3940
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
3762
3941
  return;
3763
3942
  }
3764
3943
  if (this.fsw.closed) {
@@ -3767,8 +3946,8 @@ var init_handler = __esm({
3767
3946
  }
3768
3947
  if (item === target || !target && !previous.has(item)) {
3769
3948
  this.fsw._incrReadyCount();
3770
- path23 = sysPath.join(dir, sysPath.relative(dir, path23));
3771
- this._addToNodeFs(path23, initialAdd, wh, depth + 1);
3949
+ path24 = sysPath.join(dir, sysPath.relative(dir, path24));
3950
+ this._addToNodeFs(path24, initialAdd, wh, depth + 1);
3772
3951
  }
3773
3952
  }).on(EV.ERROR, this._boundHandleError);
3774
3953
  return new Promise((resolve3, reject) => {
@@ -3837,13 +4016,13 @@ var init_handler = __esm({
3837
4016
  * @param depth Child path actually targeted for watch
3838
4017
  * @param target Child path actually targeted for watch
3839
4018
  */
3840
- async _addToNodeFs(path23, initialAdd, priorWh, depth, target) {
4019
+ async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
3841
4020
  const ready = this.fsw._emitReady;
3842
- if (this.fsw._isIgnored(path23) || this.fsw.closed) {
4021
+ if (this.fsw._isIgnored(path24) || this.fsw.closed) {
3843
4022
  ready();
3844
4023
  return false;
3845
4024
  }
3846
- const wh = this.fsw._getWatchHelpers(path23);
4025
+ const wh = this.fsw._getWatchHelpers(path24);
3847
4026
  if (priorWh) {
3848
4027
  wh.filterPath = (entry) => priorWh.filterPath(entry);
3849
4028
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -3859,8 +4038,8 @@ var init_handler = __esm({
3859
4038
  const follow = this.fsw.options.followSymlinks;
3860
4039
  let closer;
3861
4040
  if (stats.isDirectory()) {
3862
- const absPath = sysPath.resolve(path23);
3863
- const targetPath = follow ? await fsrealpath(path23) : path23;
4041
+ const absPath = sysPath.resolve(path24);
4042
+ const targetPath = follow ? await fsrealpath(path24) : path24;
3864
4043
  if (this.fsw.closed)
3865
4044
  return;
3866
4045
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -3870,29 +4049,29 @@ var init_handler = __esm({
3870
4049
  this.fsw._symlinkPaths.set(absPath, targetPath);
3871
4050
  }
3872
4051
  } else if (stats.isSymbolicLink()) {
3873
- const targetPath = follow ? await fsrealpath(path23) : path23;
4052
+ const targetPath = follow ? await fsrealpath(path24) : path24;
3874
4053
  if (this.fsw.closed)
3875
4054
  return;
3876
4055
  const parent = sysPath.dirname(wh.watchPath);
3877
4056
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
3878
4057
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
3879
- closer = await this._handleDir(parent, stats, initialAdd, depth, path23, wh, targetPath);
4058
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
3880
4059
  if (this.fsw.closed)
3881
4060
  return;
3882
4061
  if (targetPath !== void 0) {
3883
- this.fsw._symlinkPaths.set(sysPath.resolve(path23), targetPath);
4062
+ this.fsw._symlinkPaths.set(sysPath.resolve(path24), targetPath);
3884
4063
  }
3885
4064
  } else {
3886
4065
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
3887
4066
  }
3888
4067
  ready();
3889
4068
  if (closer)
3890
- this.fsw._addPathCloser(path23, closer);
4069
+ this.fsw._addPathCloser(path24, closer);
3891
4070
  return false;
3892
4071
  } catch (error) {
3893
4072
  if (this.fsw._handleError(error)) {
3894
4073
  ready();
3895
- return path23;
4074
+ return path24;
3896
4075
  }
3897
4076
  }
3898
4077
  }
@@ -3931,26 +4110,26 @@ function createPattern(matcher) {
3931
4110
  }
3932
4111
  return () => false;
3933
4112
  }
3934
- function normalizePath2(path23) {
3935
- if (typeof path23 !== "string")
4113
+ function normalizePath2(path24) {
4114
+ if (typeof path24 !== "string")
3936
4115
  throw new Error("string expected");
3937
- path23 = sysPath2.normalize(path23);
3938
- path23 = path23.replace(/\\/g, "/");
4116
+ path24 = sysPath2.normalize(path24);
4117
+ path24 = path24.replace(/\\/g, "/");
3939
4118
  let prepend = false;
3940
- if (path23.startsWith("//"))
4119
+ if (path24.startsWith("//"))
3941
4120
  prepend = true;
3942
4121
  const DOUBLE_SLASH_RE2 = /\/\//;
3943
- while (path23.match(DOUBLE_SLASH_RE2))
3944
- path23 = path23.replace(DOUBLE_SLASH_RE2, "/");
4122
+ while (path24.match(DOUBLE_SLASH_RE2))
4123
+ path24 = path24.replace(DOUBLE_SLASH_RE2, "/");
3945
4124
  if (prepend)
3946
- path23 = "/" + path23;
3947
- return path23;
4125
+ path24 = "/" + path24;
4126
+ return path24;
3948
4127
  }
3949
4128
  function matchPatterns(patterns, testString, stats) {
3950
- const path23 = normalizePath2(testString);
4129
+ const path24 = normalizePath2(testString);
3951
4130
  for (let index = 0; index < patterns.length; index++) {
3952
4131
  const pattern = patterns[index];
3953
- if (pattern(path23, stats)) {
4132
+ if (pattern(path24, stats)) {
3954
4133
  return true;
3955
4134
  }
3956
4135
  }
@@ -4011,19 +4190,19 @@ var init_esm2 = __esm({
4011
4190
  }
4012
4191
  return str;
4013
4192
  };
4014
- normalizePathToUnix = (path23) => toUnix(sysPath2.normalize(toUnix(path23)));
4015
- normalizeIgnored = (cwd = "") => (path23) => {
4016
- if (typeof path23 === "string") {
4017
- return normalizePathToUnix(sysPath2.isAbsolute(path23) ? path23 : sysPath2.join(cwd, path23));
4193
+ normalizePathToUnix = (path24) => toUnix(sysPath2.normalize(toUnix(path24)));
4194
+ normalizeIgnored = (cwd = "") => (path24) => {
4195
+ if (typeof path24 === "string") {
4196
+ return normalizePathToUnix(sysPath2.isAbsolute(path24) ? path24 : sysPath2.join(cwd, path24));
4018
4197
  } else {
4019
- return path23;
4198
+ return path24;
4020
4199
  }
4021
4200
  };
4022
- getAbsolutePath = (path23, cwd) => {
4023
- if (sysPath2.isAbsolute(path23)) {
4024
- return path23;
4201
+ getAbsolutePath = (path24, cwd) => {
4202
+ if (sysPath2.isAbsolute(path24)) {
4203
+ return path24;
4025
4204
  }
4026
- return sysPath2.join(cwd, path23);
4205
+ return sysPath2.join(cwd, path24);
4027
4206
  };
4028
4207
  EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
4029
4208
  DirEntry = class {
@@ -4078,10 +4257,10 @@ var init_esm2 = __esm({
4078
4257
  STAT_METHOD_F = "stat";
4079
4258
  STAT_METHOD_L = "lstat";
4080
4259
  WatchHelper = class {
4081
- constructor(path23, follow, fsw) {
4260
+ constructor(path24, follow, fsw) {
4082
4261
  this.fsw = fsw;
4083
- const watchPath = path23;
4084
- this.path = path23 = path23.replace(REPLACER_RE, "");
4262
+ const watchPath = path24;
4263
+ this.path = path24 = path24.replace(REPLACER_RE, "");
4085
4264
  this.watchPath = watchPath;
4086
4265
  this.fullWatchPath = sysPath2.resolve(watchPath);
4087
4266
  this.dirParts = [];
@@ -4203,20 +4382,20 @@ var init_esm2 = __esm({
4203
4382
  this._closePromise = void 0;
4204
4383
  let paths = unifyPaths(paths_);
4205
4384
  if (cwd) {
4206
- paths = paths.map((path23) => {
4207
- const absPath = getAbsolutePath(path23, cwd);
4385
+ paths = paths.map((path24) => {
4386
+ const absPath = getAbsolutePath(path24, cwd);
4208
4387
  return absPath;
4209
4388
  });
4210
4389
  }
4211
- paths.forEach((path23) => {
4212
- this._removeIgnoredPath(path23);
4390
+ paths.forEach((path24) => {
4391
+ this._removeIgnoredPath(path24);
4213
4392
  });
4214
4393
  this._userIgnored = void 0;
4215
4394
  if (!this._readyCount)
4216
4395
  this._readyCount = 0;
4217
4396
  this._readyCount += paths.length;
4218
- Promise.all(paths.map(async (path23) => {
4219
- const res = await this._nodeFsHandler._addToNodeFs(path23, !_internal, void 0, 0, _origAdd);
4397
+ Promise.all(paths.map(async (path24) => {
4398
+ const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
4220
4399
  if (res)
4221
4400
  this._emitReady();
4222
4401
  return res;
@@ -4238,17 +4417,17 @@ var init_esm2 = __esm({
4238
4417
  return this;
4239
4418
  const paths = unifyPaths(paths_);
4240
4419
  const { cwd } = this.options;
4241
- paths.forEach((path23) => {
4242
- if (!sysPath2.isAbsolute(path23) && !this._closers.has(path23)) {
4420
+ paths.forEach((path24) => {
4421
+ if (!sysPath2.isAbsolute(path24) && !this._closers.has(path24)) {
4243
4422
  if (cwd)
4244
- path23 = sysPath2.join(cwd, path23);
4245
- path23 = sysPath2.resolve(path23);
4423
+ path24 = sysPath2.join(cwd, path24);
4424
+ path24 = sysPath2.resolve(path24);
4246
4425
  }
4247
- this._closePath(path23);
4248
- this._addIgnoredPath(path23);
4249
- if (this._watched.has(path23)) {
4426
+ this._closePath(path24);
4427
+ this._addIgnoredPath(path24);
4428
+ if (this._watched.has(path24)) {
4250
4429
  this._addIgnoredPath({
4251
- path: path23,
4430
+ path: path24,
4252
4431
  recursive: true
4253
4432
  });
4254
4433
  }
@@ -4312,38 +4491,38 @@ var init_esm2 = __esm({
4312
4491
  * @param stats arguments to be passed with event
4313
4492
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
4314
4493
  */
4315
- async _emit(event, path23, stats) {
4494
+ async _emit(event, path24, stats) {
4316
4495
  if (this.closed)
4317
4496
  return;
4318
4497
  const opts = this.options;
4319
4498
  if (isWindows)
4320
- path23 = sysPath2.normalize(path23);
4499
+ path24 = sysPath2.normalize(path24);
4321
4500
  if (opts.cwd)
4322
- path23 = sysPath2.relative(opts.cwd, path23);
4323
- const args = [path23];
4501
+ path24 = sysPath2.relative(opts.cwd, path24);
4502
+ const args = [path24];
4324
4503
  if (stats != null)
4325
4504
  args.push(stats);
4326
4505
  const awf = opts.awaitWriteFinish;
4327
4506
  let pw;
4328
- if (awf && (pw = this._pendingWrites.get(path23))) {
4507
+ if (awf && (pw = this._pendingWrites.get(path24))) {
4329
4508
  pw.lastChange = /* @__PURE__ */ new Date();
4330
4509
  return this;
4331
4510
  }
4332
4511
  if (opts.atomic) {
4333
4512
  if (event === EVENTS.UNLINK) {
4334
- this._pendingUnlinks.set(path23, [event, ...args]);
4513
+ this._pendingUnlinks.set(path24, [event, ...args]);
4335
4514
  setTimeout(() => {
4336
- this._pendingUnlinks.forEach((entry, path24) => {
4515
+ this._pendingUnlinks.forEach((entry, path25) => {
4337
4516
  this.emit(...entry);
4338
4517
  this.emit(EVENTS.ALL, ...entry);
4339
- this._pendingUnlinks.delete(path24);
4518
+ this._pendingUnlinks.delete(path25);
4340
4519
  });
4341
4520
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
4342
4521
  return this;
4343
4522
  }
4344
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path23)) {
4523
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
4345
4524
  event = EVENTS.CHANGE;
4346
- this._pendingUnlinks.delete(path23);
4525
+ this._pendingUnlinks.delete(path24);
4347
4526
  }
4348
4527
  }
4349
4528
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -4361,16 +4540,16 @@ var init_esm2 = __esm({
4361
4540
  this.emitWithAll(event, args);
4362
4541
  }
4363
4542
  };
4364
- this._awaitWriteFinish(path23, awf.stabilityThreshold, event, awfEmit);
4543
+ this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
4365
4544
  return this;
4366
4545
  }
4367
4546
  if (event === EVENTS.CHANGE) {
4368
- const isThrottled = !this._throttle(EVENTS.CHANGE, path23, 50);
4547
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
4369
4548
  if (isThrottled)
4370
4549
  return this;
4371
4550
  }
4372
4551
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
4373
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path23) : path23;
4552
+ const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path24) : path24;
4374
4553
  let stats2;
4375
4554
  try {
4376
4555
  stats2 = await stat3(fullPath);
@@ -4401,23 +4580,23 @@ var init_esm2 = __esm({
4401
4580
  * @param timeout duration of time to suppress duplicate actions
4402
4581
  * @returns tracking object or false if action should be suppressed
4403
4582
  */
4404
- _throttle(actionType, path23, timeout) {
4583
+ _throttle(actionType, path24, timeout) {
4405
4584
  if (!this._throttled.has(actionType)) {
4406
4585
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
4407
4586
  }
4408
4587
  const action = this._throttled.get(actionType);
4409
4588
  if (!action)
4410
4589
  throw new Error("invalid throttle");
4411
- const actionPath = action.get(path23);
4590
+ const actionPath = action.get(path24);
4412
4591
  if (actionPath) {
4413
4592
  actionPath.count++;
4414
4593
  return false;
4415
4594
  }
4416
4595
  let timeoutObject;
4417
4596
  const clear = () => {
4418
- const item = action.get(path23);
4597
+ const item = action.get(path24);
4419
4598
  const count = item ? item.count : 0;
4420
- action.delete(path23);
4599
+ action.delete(path24);
4421
4600
  clearTimeout(timeoutObject);
4422
4601
  if (item)
4423
4602
  clearTimeout(item.timeoutObject);
@@ -4425,7 +4604,7 @@ var init_esm2 = __esm({
4425
4604
  };
4426
4605
  timeoutObject = setTimeout(clear, timeout);
4427
4606
  const thr = { timeoutObject, clear, count: 0 };
4428
- action.set(path23, thr);
4607
+ action.set(path24, thr);
4429
4608
  return thr;
4430
4609
  }
4431
4610
  _incrReadyCount() {
@@ -4439,44 +4618,44 @@ var init_esm2 = __esm({
4439
4618
  * @param event
4440
4619
  * @param awfEmit Callback to be called when ready for event to be emitted.
4441
4620
  */
4442
- _awaitWriteFinish(path23, threshold, event, awfEmit) {
4621
+ _awaitWriteFinish(path24, threshold, event, awfEmit) {
4443
4622
  const awf = this.options.awaitWriteFinish;
4444
4623
  if (typeof awf !== "object")
4445
4624
  return;
4446
4625
  const pollInterval = awf.pollInterval;
4447
4626
  let timeoutHandler;
4448
- let fullPath = path23;
4449
- if (this.options.cwd && !sysPath2.isAbsolute(path23)) {
4450
- fullPath = sysPath2.join(this.options.cwd, path23);
4627
+ let fullPath = path24;
4628
+ if (this.options.cwd && !sysPath2.isAbsolute(path24)) {
4629
+ fullPath = sysPath2.join(this.options.cwd, path24);
4451
4630
  }
4452
4631
  const now = /* @__PURE__ */ new Date();
4453
4632
  const writes = this._pendingWrites;
4454
4633
  function awaitWriteFinishFn(prevStat) {
4455
4634
  statcb(fullPath, (err, curStat) => {
4456
- if (err || !writes.has(path23)) {
4635
+ if (err || !writes.has(path24)) {
4457
4636
  if (err && err.code !== "ENOENT")
4458
4637
  awfEmit(err);
4459
4638
  return;
4460
4639
  }
4461
4640
  const now2 = Number(/* @__PURE__ */ new Date());
4462
4641
  if (prevStat && curStat.size !== prevStat.size) {
4463
- writes.get(path23).lastChange = now2;
4642
+ writes.get(path24).lastChange = now2;
4464
4643
  }
4465
- const pw = writes.get(path23);
4644
+ const pw = writes.get(path24);
4466
4645
  const df = now2 - pw.lastChange;
4467
4646
  if (df >= threshold) {
4468
- writes.delete(path23);
4647
+ writes.delete(path24);
4469
4648
  awfEmit(void 0, curStat);
4470
4649
  } else {
4471
4650
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
4472
4651
  }
4473
4652
  });
4474
4653
  }
4475
- if (!writes.has(path23)) {
4476
- writes.set(path23, {
4654
+ if (!writes.has(path24)) {
4655
+ writes.set(path24, {
4477
4656
  lastChange: now,
4478
4657
  cancelWait: () => {
4479
- writes.delete(path23);
4658
+ writes.delete(path24);
4480
4659
  clearTimeout(timeoutHandler);
4481
4660
  return event;
4482
4661
  }
@@ -4487,8 +4666,8 @@ var init_esm2 = __esm({
4487
4666
  /**
4488
4667
  * Determines whether user has asked to ignore this path.
4489
4668
  */
4490
- _isIgnored(path23, stats) {
4491
- if (this.options.atomic && DOT_RE.test(path23))
4669
+ _isIgnored(path24, stats) {
4670
+ if (this.options.atomic && DOT_RE.test(path24))
4492
4671
  return true;
4493
4672
  if (!this._userIgnored) {
4494
4673
  const { cwd } = this.options;
@@ -4498,17 +4677,17 @@ var init_esm2 = __esm({
4498
4677
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
4499
4678
  this._userIgnored = anymatch(list, void 0);
4500
4679
  }
4501
- return this._userIgnored(path23, stats);
4680
+ return this._userIgnored(path24, stats);
4502
4681
  }
4503
- _isntIgnored(path23, stat4) {
4504
- return !this._isIgnored(path23, stat4);
4682
+ _isntIgnored(path24, stat4) {
4683
+ return !this._isIgnored(path24, stat4);
4505
4684
  }
4506
4685
  /**
4507
4686
  * Provides a set of common helpers and properties relating to symlink handling.
4508
4687
  * @param path file or directory pattern being watched
4509
4688
  */
4510
- _getWatchHelpers(path23) {
4511
- return new WatchHelper(path23, this.options.followSymlinks, this);
4689
+ _getWatchHelpers(path24) {
4690
+ return new WatchHelper(path24, this.options.followSymlinks, this);
4512
4691
  }
4513
4692
  // Directory helpers
4514
4693
  // -----------------
@@ -4540,63 +4719,63 @@ var init_esm2 = __esm({
4540
4719
  * @param item base path of item/directory
4541
4720
  */
4542
4721
  _remove(directory, item, isDirectory) {
4543
- const path23 = sysPath2.join(directory, item);
4544
- const fullPath = sysPath2.resolve(path23);
4545
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path23) || this._watched.has(fullPath);
4546
- if (!this._throttle("remove", path23, 100))
4722
+ const path24 = sysPath2.join(directory, item);
4723
+ const fullPath = sysPath2.resolve(path24);
4724
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
4725
+ if (!this._throttle("remove", path24, 100))
4547
4726
  return;
4548
4727
  if (!isDirectory && this._watched.size === 1) {
4549
4728
  this.add(directory, item, true);
4550
4729
  }
4551
- const wp = this._getWatchedDir(path23);
4730
+ const wp = this._getWatchedDir(path24);
4552
4731
  const nestedDirectoryChildren = wp.getChildren();
4553
- nestedDirectoryChildren.forEach((nested) => this._remove(path23, nested));
4732
+ nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
4554
4733
  const parent = this._getWatchedDir(directory);
4555
4734
  const wasTracked = parent.has(item);
4556
4735
  parent.remove(item);
4557
4736
  if (this._symlinkPaths.has(fullPath)) {
4558
4737
  this._symlinkPaths.delete(fullPath);
4559
4738
  }
4560
- let relPath = path23;
4739
+ let relPath = path24;
4561
4740
  if (this.options.cwd)
4562
- relPath = sysPath2.relative(this.options.cwd, path23);
4741
+ relPath = sysPath2.relative(this.options.cwd, path24);
4563
4742
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
4564
4743
  const event = this._pendingWrites.get(relPath).cancelWait();
4565
4744
  if (event === EVENTS.ADD)
4566
4745
  return;
4567
4746
  }
4568
- this._watched.delete(path23);
4747
+ this._watched.delete(path24);
4569
4748
  this._watched.delete(fullPath);
4570
4749
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
4571
- if (wasTracked && !this._isIgnored(path23))
4572
- this._emit(eventName, path23);
4573
- this._closePath(path23);
4750
+ if (wasTracked && !this._isIgnored(path24))
4751
+ this._emit(eventName, path24);
4752
+ this._closePath(path24);
4574
4753
  }
4575
4754
  /**
4576
4755
  * Closes all watchers for a path
4577
4756
  */
4578
- _closePath(path23) {
4579
- this._closeFile(path23);
4580
- const dir = sysPath2.dirname(path23);
4581
- this._getWatchedDir(dir).remove(sysPath2.basename(path23));
4757
+ _closePath(path24) {
4758
+ this._closeFile(path24);
4759
+ const dir = sysPath2.dirname(path24);
4760
+ this._getWatchedDir(dir).remove(sysPath2.basename(path24));
4582
4761
  }
4583
4762
  /**
4584
4763
  * Closes only file-specific watchers
4585
4764
  */
4586
- _closeFile(path23) {
4587
- const closers = this._closers.get(path23);
4765
+ _closeFile(path24) {
4766
+ const closers = this._closers.get(path24);
4588
4767
  if (!closers)
4589
4768
  return;
4590
4769
  closers.forEach((closer) => closer());
4591
- this._closers.delete(path23);
4770
+ this._closers.delete(path24);
4592
4771
  }
4593
- _addPathCloser(path23, closer) {
4772
+ _addPathCloser(path24, closer) {
4594
4773
  if (!closer)
4595
4774
  return;
4596
- let list = this._closers.get(path23);
4775
+ let list = this._closers.get(path24);
4597
4776
  if (!list) {
4598
4777
  list = [];
4599
- this._closers.set(path23, list);
4778
+ this._closers.set(path24, list);
4600
4779
  }
4601
4780
  list.push(closer);
4602
4781
  }
@@ -4623,8 +4802,8 @@ var init_esm2 = __esm({
4623
4802
  });
4624
4803
 
4625
4804
  // src/cli/compileDevRoutes.ts
4626
- import path14 from "path";
4627
- import fs13 from "fs";
4805
+ import path15 from "path";
4806
+ import fs14 from "fs";
4628
4807
  import fg4 from "fast-glob";
4629
4808
  async function compileDevRoutes(options) {
4630
4809
  const { rootDir, dist, files, logLevel = "silent" } = options;
@@ -4637,11 +4816,11 @@ async function compileDevRoutes(options) {
4637
4816
  if (entryPoints.length === 0) {
4638
4817
  return { compiledFiles: [] };
4639
4818
  }
4640
- const absDist = path14.resolve(rootDir, dist);
4641
- await fs13.promises.mkdir(absDist, { recursive: true });
4819
+ const absDist = path15.resolve(rootDir, dist);
4820
+ await fs14.promises.mkdir(absDist, { recursive: true });
4642
4821
  const plugins = buildAliasPlugins(rootDir);
4643
4822
  const esbuild = await import("esbuild");
4644
- const outbase = path14.resolve(rootDir, APP_DIR2);
4823
+ const outbase = path15.resolve(rootDir, APP_DIR2);
4645
4824
  const result = await esbuild.build({
4646
4825
  entryPoints,
4647
4826
  outdir: absDist,
@@ -4658,10 +4837,10 @@ async function compileDevRoutes(options) {
4658
4837
  if (result.outputFiles) {
4659
4838
  await Promise.all(
4660
4839
  result.outputFiles.map(async (file) => {
4661
- await fs13.promises.mkdir(path14.dirname(file.path), { recursive: true });
4840
+ await fs14.promises.mkdir(path15.dirname(file.path), { recursive: true });
4662
4841
  const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
4663
- await fs13.promises.writeFile(tmp, file.contents);
4664
- await fs13.promises.rename(tmp, file.path);
4842
+ await fs14.promises.writeFile(tmp, file.contents);
4843
+ await fs14.promises.rename(tmp, file.path);
4665
4844
  })
4666
4845
  );
4667
4846
  }
@@ -4677,7 +4856,7 @@ var init_compileDevRoutes = __esm({
4677
4856
  });
4678
4857
 
4679
4858
  // src/cli/watcher.ts
4680
- import path15 from "path";
4859
+ import path16 from "path";
4681
4860
  function startWatcher(options) {
4682
4861
  const { rootDir, app, devDist } = options;
4683
4862
  let rebuildTimer = null;
@@ -4727,11 +4906,11 @@ function startWatcher(options) {
4727
4906
  }
4728
4907
  });
4729
4908
  watcher.on("add", (file) => {
4730
- pendingFiles.add(path15.resolve(rootDir, file));
4909
+ pendingFiles.add(path16.resolve(rootDir, file));
4731
4910
  scheduleRebuild();
4732
4911
  });
4733
4912
  watcher.on("change", (file) => {
4734
- pendingFiles.add(path15.resolve(rootDir, file));
4913
+ pendingFiles.add(path16.resolve(rootDir, file));
4735
4914
  scheduleRebuild();
4736
4915
  });
4737
4916
  watcher.on("unlink", () => {
@@ -4788,42 +4967,91 @@ var init_detectRouteConflicts = __esm({
4788
4967
  });
4789
4968
 
4790
4969
  // src/router/matchRoute.ts
4791
- function matchRoute(routes, method, path23) {
4970
+ function getHttpIndex(routes) {
4971
+ let index = httpIndexCache.get(routes);
4972
+ if (index) return index;
4973
+ index = { static: /* @__PURE__ */ new Map(), methodsByStaticPath: /* @__PURE__ */ new Map(), dynamics: [] };
4792
4974
  for (const route of routes) {
4793
- if (route.method !== method) {
4794
- continue;
4795
- }
4796
- if (!route.isDynamic) {
4797
- if (route.urlPath === path23) {
4798
- return { route, params: {} };
4975
+ if (route.isDynamic) {
4976
+ index.dynamics.push(route);
4977
+ } else {
4978
+ index.static.set(`${route.method}|${route.urlPath}`, route);
4979
+ let methods = index.methodsByStaticPath.get(route.urlPath);
4980
+ if (!methods) {
4981
+ methods = /* @__PURE__ */ new Set();
4982
+ index.methodsByStaticPath.set(route.urlPath, methods);
4799
4983
  }
4984
+ methods.add(route.method);
4985
+ }
4986
+ }
4987
+ httpIndexCache.set(routes, index);
4988
+ return index;
4989
+ }
4990
+ function getWsIndex(routes) {
4991
+ let index = wsIndexCache.get(routes);
4992
+ if (index) return index;
4993
+ index = { static: /* @__PURE__ */ new Map(), dynamics: [] };
4994
+ for (const route of routes) {
4995
+ if (route.isDynamic) {
4996
+ index.dynamics.push(route);
4997
+ } else {
4998
+ index.static.set(route.urlPath, route);
4999
+ }
5000
+ }
5001
+ wsIndexCache.set(routes, index);
5002
+ return index;
5003
+ }
5004
+ function matchRoute(routes, method, path24) {
5005
+ const index = getHttpIndex(routes);
5006
+ const staticHit = index.static.get(`${method}|${path24}`);
5007
+ if (staticHit) {
5008
+ return { route: staticHit, params: {} };
5009
+ }
5010
+ for (const route of index.dynamics) {
5011
+ if (route.method !== method) {
4800
5012
  continue;
4801
5013
  }
4802
- const params = matchDynamicPath(route.urlPath, path23, route.paramNames, route.isCatchAll);
5014
+ const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
4803
5015
  if (params !== null) {
4804
5016
  return { route, params };
4805
5017
  }
4806
5018
  }
4807
5019
  return null;
4808
5020
  }
4809
- function matchWsRoute(wsRoutes, path23) {
4810
- for (const route of wsRoutes) {
4811
- if (!route.isDynamic) {
4812
- if (route.urlPath === path23) {
4813
- return { route, params: {} };
4814
- }
4815
- continue;
4816
- }
4817
- const params = matchDynamicPath(route.urlPath, path23, route.paramNames, route.isCatchAll);
5021
+ function matchWsRoute(wsRoutes, path24) {
5022
+ const index = getWsIndex(wsRoutes);
5023
+ const staticHit = index.static.get(path24);
5024
+ if (staticHit) {
5025
+ return { route: staticHit, params: {} };
5026
+ }
5027
+ for (const route of index.dynamics) {
5028
+ const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
4818
5029
  if (params !== null) {
4819
5030
  return { route, params };
4820
5031
  }
4821
5032
  }
4822
5033
  return null;
4823
5034
  }
4824
- function matchDynamicPath(pattern, path23, paramNames, isCatchAll) {
5035
+ function findAllowedMethods(routes, path24) {
5036
+ const index = getHttpIndex(routes);
5037
+ const methods = /* @__PURE__ */ new Set();
5038
+ const staticMethods = index.methodsByStaticPath.get(path24);
5039
+ if (staticMethods) {
5040
+ for (const method of staticMethods) {
5041
+ methods.add(method);
5042
+ }
5043
+ }
5044
+ for (const route of index.dynamics) {
5045
+ const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
5046
+ if (params !== null) {
5047
+ methods.add(route.method);
5048
+ }
5049
+ }
5050
+ return Array.from(methods);
5051
+ }
5052
+ function matchDynamicPath(pattern, path24, paramNames, isCatchAll) {
4825
5053
  const patternSegments = pattern.split("/").filter(Boolean);
4826
- const pathSegments = path23.split("/").filter(Boolean);
5054
+ const pathSegments = path24.split("/").filter(Boolean);
4827
5055
  if (isCatchAll) {
4828
5056
  const nonCatchAllCount = patternSegments.length - 1;
4829
5057
  if (pathSegments.length <= nonCatchAllCount) {
@@ -4867,9 +5095,12 @@ function matchDynamicPath(pattern, path23, paramNames, isCatchAll) {
4867
5095
  }
4868
5096
  return params;
4869
5097
  }
5098
+ var httpIndexCache, wsIndexCache;
4870
5099
  var init_matchRoute = __esm({
4871
5100
  "src/router/matchRoute.ts"() {
4872
5101
  "use strict";
5102
+ httpIndexCache = /* @__PURE__ */ new WeakMap();
5103
+ wsIndexCache = /* @__PURE__ */ new WeakMap();
4873
5104
  }
4874
5105
  });
4875
5106
 
@@ -4908,12 +5139,12 @@ var init_validateRouteModule = __esm({
4908
5139
  });
4909
5140
 
4910
5141
  // src/cli/compileOnDemand.ts
4911
- import path16 from "path";
4912
- import fs14 from "fs";
5142
+ import path17 from "path";
5143
+ import fs15 from "fs";
4913
5144
  function isProductFresh(sourceAbsPath, productAbsPath) {
4914
5145
  try {
4915
- const srcStat = fs14.statSync(sourceAbsPath);
4916
- const prodStat = fs14.statSync(productAbsPath);
5146
+ const srcStat = fs15.statSync(sourceAbsPath);
5147
+ const prodStat = fs15.statSync(productAbsPath);
4917
5148
  return prodStat.mtimeMs >= srcStat.mtimeMs;
4918
5149
  } catch {
4919
5150
  return false;
@@ -4932,6 +5163,7 @@ function createDevOnDemandState() {
4932
5163
  function clearCompiledFiles() {
4933
5164
  state.compiledFiles.clear();
4934
5165
  state.inFlightCompilations.clear();
5166
+ sourcePathCache.clear();
4935
5167
  }
4936
5168
  async function ensureCompiled(sourceAbsPath, rootDir, dist) {
4937
5169
  const inFlight = state.inFlightCompilations.get(sourceAbsPath);
@@ -4943,7 +5175,7 @@ async function ensureCompiled(sourceAbsPath, rootDir, dist) {
4943
5175
  if (state.compiledFiles.has(sourceAbsPath)) {
4944
5176
  return false;
4945
5177
  }
4946
- if (!fs14.existsSync(sourceAbsPath)) {
5178
+ if (!fs15.existsSync(sourceAbsPath)) {
4947
5179
  return false;
4948
5180
  }
4949
5181
  const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);
@@ -4969,11 +5201,11 @@ async function ensureCompiled(sourceAbsPath, rootDir, dist) {
4969
5201
  }
4970
5202
  }
4971
5203
  function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
4972
- const rel = path16.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
5204
+ const rel = path17.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
4973
5205
  if (!rel.startsWith("src/")) return null;
4974
5206
  const relWithoutSrc = rel.slice(4);
4975
5207
  const jsRel = relWithoutSrc.replace(/\.ts$/, ".js");
4976
- return path16.resolve(rootDir, dist, jsRel);
5208
+ return path17.resolve(rootDir, dist, jsRel);
4977
5209
  }
4978
5210
  function clearGeneratedSchemas() {
4979
5211
  state.generatedSchemas.clear();
@@ -4989,9 +5221,9 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
4989
5221
  if (state.generatedSchemas.has(schemaPath)) {
4990
5222
  return false;
4991
5223
  }
4992
- const prodAbsPath = path16.resolve(rootDir, routeFilePath);
5224
+ const prodAbsPath = path17.resolve(rootDir, routeFilePath);
4993
5225
  const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);
4994
- if (!fs14.existsSync(sourceAbsPath)) {
5226
+ if (!fs15.existsSync(sourceAbsPath)) {
4995
5227
  return false;
4996
5228
  }
4997
5229
  if (isProductFresh(sourceAbsPath, schemaPath)) {
@@ -5002,7 +5234,7 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
5002
5234
  if (fileRoutes.length === 0) {
5003
5235
  return false;
5004
5236
  }
5005
- const sourceRelPath = path16.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
5237
+ const sourceRelPath = path17.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
5006
5238
  const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
5007
5239
  const generatePromise = (async () => {
5008
5240
  await generateSchemaFiles(sourceRoutes, rootDir, dist);
@@ -5023,22 +5255,30 @@ async function deleteSchemaFiles(routes, rootDir, dist) {
5023
5255
  if (deleted.has(schemaPath)) continue;
5024
5256
  deleted.add(schemaPath);
5025
5257
  try {
5026
- await fs14.promises.unlink(schemaPath);
5258
+ await fs15.promises.unlink(schemaPath);
5027
5259
  } catch {
5028
5260
  }
5029
5261
  }
5030
5262
  }
5031
5263
  function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
5032
- const rel = path16.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
5264
+ const cached = sourcePathCache.get(prodAbsPath);
5265
+ if (cached) return cached;
5266
+ const rel = path17.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
5033
5267
  let relWithoutDist = rel;
5034
5268
  if (relWithoutDist.startsWith(`${dist}/`)) {
5035
5269
  relWithoutDist = relWithoutDist.slice(dist.length + 1);
5036
5270
  }
5037
5271
  const srcRel = `src/${relWithoutDist}`;
5038
5272
  const tsRel = srcRel.replace(/\.js$/, ".ts");
5039
- const tsAbs = path16.resolve(rootDir, tsRel);
5040
- if (fs14.existsSync(tsAbs)) return tsAbs;
5041
- return path16.resolve(rootDir, srcRel);
5273
+ const tsAbs = path17.resolve(rootDir, tsRel);
5274
+ let result;
5275
+ if (fs15.existsSync(tsAbs)) {
5276
+ result = tsAbs;
5277
+ } else {
5278
+ result = path17.resolve(rootDir, srcRel);
5279
+ }
5280
+ sourcePathCache.set(prodAbsPath, result);
5281
+ return result;
5042
5282
  }
5043
5283
  function setDevOnDemandEnabled(enabled) {
5044
5284
  state.enabled = enabled;
@@ -5052,7 +5292,7 @@ function setDevDist(dist) {
5052
5292
  function getDevDist() {
5053
5293
  return state.distDir;
5054
5294
  }
5055
- var state;
5295
+ var state, sourcePathCache;
5056
5296
  var init_compileOnDemand = __esm({
5057
5297
  "src/cli/compileOnDemand.ts"() {
5058
5298
  "use strict";
@@ -5060,17 +5300,17 @@ var init_compileOnDemand = __esm({
5060
5300
  init_generateSchemaFiles();
5061
5301
  init_generateSchemaFiles();
5062
5302
  state = createDevOnDemandState();
5303
+ sourcePathCache = /* @__PURE__ */ new Map();
5063
5304
  }
5064
5305
  });
5065
5306
 
5066
5307
  // src/loader/loadRouteModule.ts
5067
- import fs15 from "fs";
5068
5308
  async function loadRouteModule(filePath, method, rootDir) {
5069
5309
  if (isDevOnDemandEnabled() && rootDir) {
5070
5310
  const dist = getDevDist();
5071
5311
  if (dist) {
5072
5312
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
5073
- if (sourcePath && fs15.existsSync(sourcePath)) {
5313
+ if (sourcePath) {
5074
5314
  try {
5075
5315
  await ensureCompiled(sourcePath, rootDir, dist);
5076
5316
  } catch (compileErr) {
@@ -5250,14 +5490,14 @@ var init_httpErrors = __esm({
5250
5490
  issues;
5251
5491
  };
5252
5492
  RouteNotFoundError = class extends FaapiError {
5253
- constructor(path23) {
5254
- super("ROUTE_NOT_FOUND", `Route not found: ${path23}`, 404);
5493
+ constructor(path24) {
5494
+ super("ROUTE_NOT_FOUND", `Route not found: ${path24}`, 404);
5255
5495
  this.name = "RouteNotFoundError";
5256
5496
  }
5257
5497
  };
5258
5498
  MethodNotAllowedError = class extends FaapiError {
5259
- constructor(method, path23, allowedMethods) {
5260
- super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path23}`, 405);
5499
+ constructor(method, path24, allowedMethods) {
5500
+ super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path24}`, 405);
5261
5501
  this.allowedMethods = allowedMethods;
5262
5502
  this.name = "MethodNotAllowedError";
5263
5503
  }
@@ -5396,7 +5636,9 @@ function formatSetCookie(name, value, options) {
5396
5636
  return cookie;
5397
5637
  }
5398
5638
  function createContext(request, params, config = {}, ip = "") {
5399
- const url = new URL(request.url);
5639
+ return createContextFromUrl(request, new URL(request.url), params, config, ip);
5640
+ }
5641
+ function createContextFromUrl(request, url, params, config = {}, ip = "") {
5400
5642
  const meta = { headers: {}, setCookies: [] };
5401
5643
  const parsedCookies = parseCookies(request.headers.get("cookie") ?? "");
5402
5644
  const cookiesObj = {};
@@ -5572,7 +5814,7 @@ var init_parseMultipart = __esm({
5572
5814
  });
5573
5815
 
5574
5816
  // src/runtime/resolveInput.ts
5575
- async function resolveInput(method, request) {
5817
+ async function resolveInputFromUrl(method, request, url) {
5576
5818
  const inputType = getInputTypeForMethod(method);
5577
5819
  if (inputType === "body") {
5578
5820
  const contentType = request.headers.get("content-type") ?? "";
@@ -5607,7 +5849,6 @@ async function resolveInput(method, request) {
5607
5849
  }
5608
5850
  return result.data;
5609
5851
  }
5610
- const url = new URL(request.url);
5611
5852
  return queryToObject(url.searchParams);
5612
5853
  }
5613
5854
  var init_resolveInput = __esm({
@@ -6031,9 +6272,9 @@ async function validateInput(schemaPath, method, inputType, input) {
6031
6272
  function mapZodIssues(error) {
6032
6273
  return error.issues.map((issue) => {
6033
6274
  const code = mapZodCode(issue.code, issue.message);
6034
- const path23 = issue.path.map(String).join(".") || "";
6275
+ const path24 = issue.path.map(String).join(".") || "";
6035
6276
  return {
6036
- path: path23,
6277
+ path: path24,
6037
6278
  code,
6038
6279
  expected: issue.expected ?? mapExpectedFromMessage(issue.message),
6039
6280
  received: issue.received ?? mapReceivedFromMessage(issue.message),
@@ -6086,11 +6327,13 @@ var init_validateInput = __esm({
6086
6327
  });
6087
6328
 
6088
6329
  // src/utils/getClientIp.ts
6089
- function getClientIp(req) {
6090
- const xff = req.headers["x-forwarded-for"];
6091
- if (typeof xff === "string" && xff.length > 0) {
6092
- const first = xff.split(",")[0]?.trim();
6093
- if (first) return first;
6330
+ function getClientIp(req, trustedProxy = false) {
6331
+ if (trustedProxy) {
6332
+ const xff = req.headers["x-forwarded-for"];
6333
+ if (typeof xff === "string" && xff.length > 0) {
6334
+ const first = xff.split(",")[0]?.trim();
6335
+ if (first) return first;
6336
+ }
6094
6337
  }
6095
6338
  const remote = req.socket?.remoteAddress;
6096
6339
  if (remote) {
@@ -6349,7 +6592,7 @@ var init_wsHandler = __esm({
6349
6592
  // src/server/handleWsUpgrade.ts
6350
6593
  import fs16 from "fs";
6351
6594
  import { WebSocketServer, WebSocket } from "ws";
6352
- import path17 from "path";
6595
+ import path18 from "path";
6353
6596
  function getPathname(req) {
6354
6597
  const url = req.url ?? "/";
6355
6598
  const idx = url.indexOf("?");
@@ -6418,7 +6661,7 @@ async function sendResponseToSocket(socket, response) {
6418
6661
  socket.destroy();
6419
6662
  }
6420
6663
  function attachWebSocket(options) {
6421
- const { server, routesRef, rootDir, config, globalMiddlewares } = options;
6664
+ const { server, routesRef, rootDir, config, globalMiddlewares, trustedProxy = false } = options;
6422
6665
  const wss = new WebSocketServer({ noServer: true });
6423
6666
  server.on("upgrade", async (req, socket, head) => {
6424
6667
  const currentWsRoutes = routesRef.wsCurrent;
@@ -6434,13 +6677,13 @@ function attachWebSocket(options) {
6434
6677
  const host = req.headers.host ?? "localhost";
6435
6678
  const url = `http://${host}${req.url ?? "/"}`;
6436
6679
  const request = new Request(url, { method: "GET", headers });
6437
- const ctx = createContext(request, params, config, getClientIp(req));
6680
+ const ctx = createContext(request, params, config, getClientIp(req, trustedProxy));
6438
6681
  const meta = ctx.meta;
6439
6682
  let upgraded = false;
6440
6683
  const finalHandler = async () => {
6441
6684
  let handlers;
6442
6685
  try {
6443
- const absoluteFilePath = path17.resolve(rootDir, route.filePath);
6686
+ const absoluteFilePath = path18.resolve(rootDir, route.filePath);
6444
6687
  handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
6445
6688
  } catch (err) {
6446
6689
  const reason = err instanceof Error ? err.message : String(err);
@@ -6514,7 +6757,7 @@ import {
6514
6757
  import { createSecureServer as createHttp2SecureServer } from "http2";
6515
6758
  import { readFileSync } from "fs";
6516
6759
  import { Readable as Readable3 } from "stream";
6517
- import path18 from "path";
6760
+ import path19 from "path";
6518
6761
  function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT) {
6519
6762
  const forwardedProto = req.headers["x-forwarded-proto"];
6520
6763
  const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
@@ -6523,16 +6766,26 @@ function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT) {
6523
6766
  const headers = nodeHttpToWebHeaders(req);
6524
6767
  const method = req.method ?? "GET";
6525
6768
  if (method === "GET" || method === "HEAD") {
6526
- return new Request(url.toString(), { method, headers });
6769
+ return { request: new Request(url.toString(), { method, headers }), url };
6770
+ }
6771
+ const contentLength = req.headers["content-length"];
6772
+ if (contentLength !== void 0) {
6773
+ const declared = Number(Array.isArray(contentLength) ? contentLength[0] : contentLength);
6774
+ if (Number.isFinite(declared) && declared > bodyLimit) {
6775
+ throw new PayloadTooLargeError(bodyLimit);
6776
+ }
6527
6777
  }
6528
6778
  const stream = Readable3.toWeb(req);
6529
6779
  const limitedStream = limitStreamSize(stream, bodyLimit);
6530
- return new Request(url.toString(), {
6531
- method,
6532
- headers,
6533
- body: limitedStream,
6534
- duplex: "half"
6535
- });
6780
+ return {
6781
+ request: new Request(url.toString(), {
6782
+ method,
6783
+ headers,
6784
+ body: limitedStream,
6785
+ duplex: "half"
6786
+ }),
6787
+ url
6788
+ };
6536
6789
  }
6537
6790
  function limitStreamSize(stream, maxSize) {
6538
6791
  let totalSize = 0;
@@ -6584,22 +6837,6 @@ function limitStreamSize(stream, maxSize) {
6584
6837
  }
6585
6838
  });
6586
6839
  }
6587
- function findAllowedMethods(routes, path23) {
6588
- const methods = /* @__PURE__ */ new Set();
6589
- for (const route of routes) {
6590
- if (route.urlPath === path23) {
6591
- methods.add(route.method);
6592
- continue;
6593
- }
6594
- if (route.isDynamic) {
6595
- const params = matchDynamicPath(route.urlPath, path23, route.paramNames, route.isCatchAll);
6596
- if (params !== null) {
6597
- methods.add(route.method);
6598
- }
6599
- }
6600
- }
6601
- return Array.from(methods);
6602
- }
6603
6840
  function createServer(options) {
6604
6841
  const {
6605
6842
  routes,
@@ -6614,7 +6851,8 @@ function createServer(options) {
6614
6851
  helmet: helmetOption,
6615
6852
  logger: loggerOption,
6616
6853
  bodyLimit = DEFAULT_BODY_LIMIT,
6617
- http2: http2Option
6854
+ http2: http2Option,
6855
+ trustedProxy = false
6618
6856
  } = options;
6619
6857
  const routesRef = { current: routes, wsCurrent: wsRoutes ?? [] };
6620
6858
  const configMiddlewares = [];
@@ -6626,6 +6864,10 @@ function createServer(options) {
6626
6864
  }
6627
6865
  const loggerMiddlewareInst = loggerOption === false ? null : loggerOption === true || loggerOption === void 0 ? logger() : logger(loggerOption);
6628
6866
  if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);
6867
+ const outerMiddlewares = [...configMiddlewares];
6868
+ if (globalMiddlewares && globalMiddlewares.length > 0) {
6869
+ outerMiddlewares.push(...globalMiddlewares);
6870
+ }
6629
6871
  const server = (() => {
6630
6872
  if (http2Option) {
6631
6873
  const h2Opts = typeof http2Option === "object" ? http2Option : {};
@@ -6645,29 +6887,29 @@ function createServer(options) {
6645
6887
  dist,
6646
6888
  req,
6647
6889
  res,
6648
- configMiddlewares,
6890
+ outerMiddlewares,
6649
6891
  onError,
6650
6892
  config,
6651
- globalMiddlewares,
6652
6893
  globalInjectors,
6653
- bodyLimit
6894
+ bodyLimit,
6895
+ trustedProxy
6654
6896
  ).catch(() => {
6655
6897
  res.statusCode = 500;
6656
6898
  res.end();
6657
6899
  });
6658
6900
  });
6659
6901
  if (routesRef.wsCurrent.length > 0) {
6660
- attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares });
6902
+ attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares, trustedProxy });
6661
6903
  }
6662
6904
  return { server, routesRef };
6663
6905
  }
6664
- function prepareRequest(req, config, bodyLimit) {
6665
- const request = toWebRequest(req, bodyLimit);
6906
+ function prepareRequest(req, config, bodyLimit, trustedProxy) {
6907
+ const { request, url } = toWebRequest(req, bodyLimit);
6666
6908
  const method = request.method.toUpperCase();
6667
- const urlPath = new URL(request.url).pathname;
6668
- const ctx = createContext(request, {}, config, getClientIp(req));
6909
+ const urlPath = url.pathname;
6910
+ const ctx = createContextFromUrl(request, url, {}, config, getClientIp(req, trustedProxy));
6669
6911
  const meta = ctx.meta;
6670
- return { request, ctx, meta, method, urlPath };
6912
+ return { request, url, ctx, meta, method, urlPath };
6671
6913
  }
6672
6914
  function resolveRouteOrThrow(routes, method, urlPath) {
6673
6915
  const match = matchRoute(routes, method, urlPath);
@@ -6679,14 +6921,14 @@ function resolveRouteOrThrow(routes, method, urlPath) {
6679
6921
  throw new RouteNotFoundError(urlPath);
6680
6922
  }
6681
6923
  function createRoutePipeline(opts) {
6682
- const { routes, method, urlPath, ctx, request, rootDir, dist, globalInjectors } = opts;
6924
+ const { routes, method, urlPath, url, ctx, request, rootDir, dist, globalInjectors } = opts;
6683
6925
  return async () => {
6684
6926
  const match = resolveRouteOrThrow(routes, method, urlPath);
6685
6927
  ctx.params = match.params;
6686
6928
  const { route } = match;
6687
- const absoluteFilePath = path18.resolve(rootDir, route.filePath);
6929
+ const absoluteFilePath = path19.resolve(rootDir, route.filePath);
6688
6930
  const routeModule = await loadRouteModule(absoluteFilePath, route.method, rootDir);
6689
- const input = await resolveInput(route.method, request);
6931
+ const input = await resolveInputFromUrl(route.method, request, url);
6690
6932
  const inputType = getInputTypeForMethod(route.method);
6691
6933
  const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
6692
6934
  if (isDevOnDemandEnabled()) {
@@ -6718,33 +6960,33 @@ async function sendSuccessResponse(response, res) {
6718
6960
  await sendNodeResponse(response, res);
6719
6961
  }
6720
6962
  async function sendErrorResponse(err, meta, res, onError, ctx) {
6721
- await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx.config), meta), res);
6722
- if (onError) {
6963
+ await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx?.config), meta), res);
6964
+ if (onError && ctx) {
6723
6965
  try {
6724
6966
  await onError(err, ctx);
6725
6967
  } catch {
6726
6968
  }
6727
6969
  }
6728
6970
  }
6729
- async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
6730
- const { request, ctx, meta, method, urlPath } = prepareRequest(req, config, bodyLimit);
6731
- const routePipeline = createRoutePipeline({
6732
- routes,
6733
- method,
6734
- urlPath,
6735
- ctx,
6736
- request,
6737
- rootDir,
6738
- dist,
6739
- globalMiddlewares,
6740
- globalInjectors
6741
- });
6742
- const outerMiddlewares = [];
6743
- if (configMiddlewares.length > 0) outerMiddlewares.push(...configMiddlewares);
6744
- if (globalMiddlewares && globalMiddlewares.length > 0) {
6745
- outerMiddlewares.push(...globalMiddlewares);
6746
- }
6971
+ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy) {
6972
+ let meta = { headers: {}, setCookies: [] };
6973
+ let ctx;
6747
6974
  try {
6975
+ const prepared = prepareRequest(req, config, bodyLimit, trustedProxy);
6976
+ ctx = prepared.ctx;
6977
+ meta = prepared.meta;
6978
+ const { request, url, method, urlPath } = prepared;
6979
+ const routePipeline = createRoutePipeline({
6980
+ routes,
6981
+ method,
6982
+ urlPath,
6983
+ url,
6984
+ ctx,
6985
+ request,
6986
+ rootDir,
6987
+ dist,
6988
+ globalInjectors
6989
+ });
6748
6990
  const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
6749
6991
  await sendSuccessResponse(response, res);
6750
6992
  } catch (err) {
@@ -6888,10 +7130,10 @@ var init_skillRegistry = __esm({
6888
7130
 
6889
7131
  // src/cli/createAppCore.ts
6890
7132
  import fs17 from "fs";
6891
- import path19 from "path";
7133
+ import path20 from "path";
6892
7134
  import { PassThrough, Readable as Readable4 } from "stream";
6893
7135
  async function loadAndHydrateTools(rootDir, dist) {
6894
- const toolsPath = path19.resolve(rootDir, dist, TOOLS_FILE2);
7136
+ const toolsPath = path20.resolve(rootDir, dist, TOOLS_FILE2);
6895
7137
  if (!fs17.existsSync(toolsPath)) {
6896
7138
  return [];
6897
7139
  }
@@ -6901,7 +7143,7 @@ async function loadAndHydrateTools(rootDir, dist) {
6901
7143
  return hydrated;
6902
7144
  }
6903
7145
  async function loadAndHydrateAgents(rootDir, dist) {
6904
- const agentsPath = path19.resolve(rootDir, dist, AGENTS_FILE2);
7146
+ const agentsPath = path20.resolve(rootDir, dist, AGENTS_FILE2);
6905
7147
  if (!fs17.existsSync(agentsPath)) {
6906
7148
  return [];
6907
7149
  }
@@ -6926,7 +7168,7 @@ function isFaapiConfigKey(key) {
6926
7168
  async function createAppBase(options) {
6927
7169
  const rootDir = options?.rootDir ?? process.cwd();
6928
7170
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
6929
- const routesPath = path19.resolve(rootDir, dist, ROUTES_FILE);
7171
+ const routesPath = path20.resolve(rootDir, dist, ROUTES_FILE);
6930
7172
  if (!fs17.existsSync(routesPath)) {
6931
7173
  throw new Error(
6932
7174
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
@@ -6962,7 +7204,8 @@ async function createAppBase(options) {
6962
7204
  helmet: config?.helmet,
6963
7205
  logger: config?.logger,
6964
7206
  bodyLimit: config?.bodyLimit,
6965
- http2: config?.http2
7207
+ http2: config?.http2,
7208
+ trustedProxy: config?.trustedProxy
6966
7209
  });
6967
7210
  const { handlerWrappers, upgradeWrappers } = await loadPlugins(config?.plugins, {
6968
7211
  rootDir,
@@ -7181,6 +7424,7 @@ var init_createAppCore = __esm({
7181
7424
  "bodyLimit",
7182
7425
  "logger",
7183
7426
  "http2",
7427
+ "trustedProxy",
7184
7428
  "response"
7185
7429
  ]);
7186
7430
  }
@@ -7253,7 +7497,7 @@ __export(devCommand_exports, {
7253
7497
  generateRouteArtifacts: () => generateRouteArtifacts,
7254
7498
  generateToolArtifactsForDev: () => generateToolArtifactsForDev
7255
7499
  });
7256
- import path20 from "path";
7500
+ import path21 from "path";
7257
7501
  async function devCommand(options) {
7258
7502
  const rootDir = process.cwd();
7259
7503
  if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
@@ -7280,7 +7524,7 @@ async function devCommand(options) {
7280
7524
  async function generateRouteArtifacts(rootDir, patterns, dist) {
7281
7525
  const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);
7282
7526
  const sorted = sortRoutes(routes);
7283
- const routesPath = path20.resolve(rootDir, dist, ROUTES_FILE2);
7527
+ const routesPath = path21.resolve(rootDir, dist, ROUTES_FILE2);
7284
7528
  const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);
7285
7529
  await writeRoutesModule(serialized, routesPath);
7286
7530
  }
@@ -7318,7 +7562,7 @@ var init_devCommand = __esm({
7318
7562
  });
7319
7563
 
7320
7564
  // src/cli/compileBuildRoutes.ts
7321
- import path21 from "path";
7565
+ import path22 from "path";
7322
7566
  import fs18 from "fs";
7323
7567
  import fg5 from "fast-glob";
7324
7568
  async function compileBuildRoutes(options) {
@@ -7332,11 +7576,11 @@ async function compileBuildRoutes(options) {
7332
7576
  if (entryPoints.length === 0) {
7333
7577
  return { compiledFiles: [] };
7334
7578
  }
7335
- const absDist = path21.resolve(rootDir, dist);
7579
+ const absDist = path22.resolve(rootDir, dist);
7336
7580
  await fs18.promises.mkdir(absDist, { recursive: true });
7337
7581
  const plugins = buildAliasPlugins(rootDir);
7338
7582
  const esbuild = await import("esbuild");
7339
- const outbase = path21.resolve(rootDir, APP_DIR3);
7583
+ const outbase = path22.resolve(rootDir, APP_DIR3);
7340
7584
  await esbuild.build({
7341
7585
  entryPoints,
7342
7586
  outdir: absDist,
@@ -7367,7 +7611,7 @@ var buildCommand_exports = {};
7367
7611
  __export(buildCommand_exports, {
7368
7612
  buildCommand: () => buildCommand
7369
7613
  });
7370
- import path22 from "path";
7614
+ import path23 from "path";
7371
7615
  import fs19 from "fs";
7372
7616
  async function buildCommand(options) {
7373
7617
  const rootDir = options?.rootDir ?? process.cwd();
@@ -7412,9 +7656,9 @@ async function buildCommand(options) {
7412
7656
  }
7413
7657
  console.log("\n[4/8] Generating schema...");
7414
7658
  await generateSchemaFiles(sorted, rootDir, outdir);
7415
- console.log(` Schema: zod.js files under ${path22.resolve(rootDir, outdir)}`);
7659
+ console.log(` Schema: zod.js files under ${path23.resolve(rootDir, outdir)}`);
7416
7660
  console.log("\n[5/8] Generating routes manifest...");
7417
- const routesPath = path22.resolve(rootDir, outdir, "faapi-routes.js");
7661
+ const routesPath = path23.resolve(rootDir, outdir, "faapi-routes.js");
7418
7662
  const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
7419
7663
  await writeRoutesModule(serialized, routesPath);
7420
7664
  console.log(` Written to ${routesPath}`);
@@ -7422,14 +7666,14 @@ async function buildCommand(options) {
7422
7666
  const tools = await scanTools(rootDir, TOOL_PATTERNS);
7423
7667
  const toolMeta = await generateToolArtifacts(tools, rootDir, outdir);
7424
7668
  console.log(` Found ${toolMeta.length} tool(s)`);
7425
- console.log(` Tool manifest: ${path22.resolve(rootDir, outdir, "faapi-tools.js")}`);
7669
+ console.log(` Tool manifest: ${path23.resolve(rootDir, outdir, "faapi-tools.js")}`);
7426
7670
  console.log("\n[7/8] Generating agent manifest...");
7427
7671
  const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);
7428
7672
  const agentMeta = await generateAgentArtifacts(agents, rootDir, outdir);
7429
7673
  console.log(` Found ${agentMeta.length} agent(s)`);
7430
- console.log(` Agent manifest: ${path22.resolve(rootDir, outdir, "faapi-agents.js")}`);
7674
+ console.log(` Agent manifest: ${path23.resolve(rootDir, outdir, "faapi-agents.js")}`);
7431
7675
  console.log("\n[8/8] Generating entry file...");
7432
- const mainPath = path22.resolve(rootDir, outdir, "main.js");
7676
+ const mainPath = path23.resolve(rootDir, outdir, "main.js");
7433
7677
  const createProdAppArgs = options?.dist && options.dist !== DEFAULT_DIST2 ? `{ dist: '${outdir}' }` : "";
7434
7678
  const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
7435
7679
  import { createProdApp, loadEnv } from '@faapi/faapi';