@faapi/faapi 3.2.1 → 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
@@ -1021,6 +1021,45 @@ function createProgram(filePath) {
1021
1021
  if (cached) {
1022
1022
  return cached;
1023
1023
  }
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) {
1024
1063
  const options = {
1025
1064
  strict: true,
1026
1065
  target: ts3.ScriptTarget.ES2022,
@@ -1029,8 +1068,7 @@ function createProgram(filePath) {
1029
1068
  skipLibCheck: true,
1030
1069
  noEmit: true
1031
1070
  };
1032
- let rootNames = [filePath];
1033
- const tsconfigPath = findTsConfig(filePath);
1071
+ const rootNames = [...entryFiles];
1034
1072
  if (tsconfigPath) {
1035
1073
  const tsOptions = parseTsConfig(tsconfigPath);
1036
1074
  if (tsOptions.module !== void 0) {
@@ -1040,16 +1078,14 @@ function createProgram(filePath) {
1040
1078
  options.moduleResolution = tsOptions.moduleResolution;
1041
1079
  }
1042
1080
  if (tsOptions.fileNames.length > 0) {
1043
- if (!tsOptions.fileNames.includes(filePath)) {
1044
- rootNames = [filePath, ...tsOptions.fileNames];
1045
- } else {
1046
- rootNames = tsOptions.fileNames;
1081
+ for (const fileName of tsOptions.fileNames) {
1082
+ if (!rootNames.includes(fileName)) {
1083
+ rootNames.push(fileName);
1084
+ }
1047
1085
  }
1048
1086
  }
1049
1087
  }
1050
- const program = ts3.createProgram(rootNames, options);
1051
- programCache.set(filePath, program);
1052
- return program;
1088
+ return ts3.createProgram(rootNames, options);
1053
1089
  }
1054
1090
  var programCache, tsConfigCache;
1055
1091
  var init_createProgram = __esm({
@@ -2263,12 +2299,11 @@ function collectRouteSchemaSources(routes, rootDir) {
2263
2299
  }
2264
2300
  entry.methods.add(route.method);
2265
2301
  }
2266
- const programByFile = /* @__PURE__ */ new Map();
2302
+ const programByFile = createPrograms([...methodsByFile.keys()]);
2267
2303
  const allTypesByFile = /* @__PURE__ */ new Map();
2268
2304
  const mergedAllTypes = /* @__PURE__ */ new Map();
2269
2305
  for (const filePath of methodsByFile.keys()) {
2270
- const program = createProgram(filePath);
2271
- programByFile.set(filePath, program);
2306
+ const program = programByFile.get(filePath);
2272
2307
  const allTypes = extractAllTypes(program, filePath);
2273
2308
  allTypesByFile.set(filePath, allTypes);
2274
2309
  for (const [name, info] of allTypes) {
@@ -2481,15 +2516,16 @@ function collectToolSchemaSources(tools, rootDir) {
2481
2516
  }
2482
2517
  list.push(tool);
2483
2518
  }
2519
+ const programByFile = createPrograms([...toolsByFile.keys()]);
2484
2520
  const allTypesByFile = /* @__PURE__ */ new Map();
2485
2521
  for (const filePath of toolsByFile.keys()) {
2486
- const program = createProgram(filePath);
2522
+ const program = programByFile.get(filePath);
2487
2523
  const allTypes = extractAllTypes(program, filePath);
2488
2524
  allTypesByFile.set(filePath, allTypes);
2489
2525
  }
2490
2526
  const sources = [];
2491
2527
  for (const [filePath, fileTools] of toolsByFile) {
2492
- const program = createProgram(filePath);
2528
+ const program = programByFile.get(filePath);
2493
2529
  for (const tool of fileTools) {
2494
2530
  const inputTypeName = tool.inputTypeName;
2495
2531
  const typeInfo = extractTypeInfo(program, filePath, inputTypeName);
@@ -2542,9 +2578,10 @@ async function maybeGenerateHelpers(allSourceCode, distDir) {
2542
2578
  }
2543
2579
  async function generateToolArtifacts(tools, rootDir, dist, options) {
2544
2580
  const metadata = [];
2581
+ const programByFile = createPrograms(tools.map((m) => path10.resolve(rootDir, m.filePath)));
2545
2582
  for (const manifest of tools) {
2546
2583
  const absPath = path10.resolve(rootDir, manifest.filePath);
2547
- const program = createProgram(absPath);
2584
+ const program = programByFile.get(absPath);
2548
2585
  const result = extractToolMetadata(program, absPath, manifest.functionName, {
2549
2586
  name: manifest.name,
2550
2587
  filePath: manifest.filePath
@@ -2915,9 +2952,10 @@ function hydrateAgents(manifest) {
2915
2952
  }
2916
2953
  async function generateAgentArtifacts(agents, rootDir, dist) {
2917
2954
  const metadata = [];
2955
+ const programByFile = createPrograms(agents.map((m) => path12.resolve(rootDir, m.filePath)));
2918
2956
  for (const manifest of agents) {
2919
2957
  const absPath = path12.resolve(rootDir, manifest.filePath);
2920
- const program = createProgram(absPath);
2958
+ const program = programByFile.get(absPath);
2921
2959
  const result = extractAgentMetadata(program, absPath, {
2922
2960
  name: manifest.name,
2923
2961
  filePath: manifest.filePath,
@@ -4929,15 +4967,48 @@ var init_detectRouteConflicts = __esm({
4929
4967
  });
4930
4968
 
4931
4969
  // src/router/matchRoute.ts
4932
- function matchRoute(routes, method, path24) {
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: [] };
4933
4974
  for (const route of routes) {
4934
- if (route.method !== method) {
4935
- continue;
4936
- }
4937
- if (!route.isDynamic) {
4938
- if (route.urlPath === path24) {
4939
- 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);
4940
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) {
4941
5012
  continue;
4942
5013
  }
4943
5014
  const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
@@ -4948,13 +5019,12 @@ function matchRoute(routes, method, path24) {
4948
5019
  return null;
4949
5020
  }
4950
5021
  function matchWsRoute(wsRoutes, path24) {
4951
- for (const route of wsRoutes) {
4952
- if (!route.isDynamic) {
4953
- if (route.urlPath === path24) {
4954
- return { route, params: {} };
4955
- }
4956
- continue;
4957
- }
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) {
4958
5028
  const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
4959
5029
  if (params !== null) {
4960
5030
  return { route, params };
@@ -4962,6 +5032,23 @@ function matchWsRoute(wsRoutes, path24) {
4962
5032
  }
4963
5033
  return null;
4964
5034
  }
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
+ }
4965
5052
  function matchDynamicPath(pattern, path24, paramNames, isCatchAll) {
4966
5053
  const patternSegments = pattern.split("/").filter(Boolean);
4967
5054
  const pathSegments = path24.split("/").filter(Boolean);
@@ -5008,9 +5095,12 @@ function matchDynamicPath(pattern, path24, paramNames, isCatchAll) {
5008
5095
  }
5009
5096
  return params;
5010
5097
  }
5098
+ var httpIndexCache, wsIndexCache;
5011
5099
  var init_matchRoute = __esm({
5012
5100
  "src/router/matchRoute.ts"() {
5013
5101
  "use strict";
5102
+ httpIndexCache = /* @__PURE__ */ new WeakMap();
5103
+ wsIndexCache = /* @__PURE__ */ new WeakMap();
5014
5104
  }
5015
5105
  });
5016
5106
 
@@ -5073,6 +5163,7 @@ function createDevOnDemandState() {
5073
5163
  function clearCompiledFiles() {
5074
5164
  state.compiledFiles.clear();
5075
5165
  state.inFlightCompilations.clear();
5166
+ sourcePathCache.clear();
5076
5167
  }
5077
5168
  async function ensureCompiled(sourceAbsPath, rootDir, dist) {
5078
5169
  const inFlight = state.inFlightCompilations.get(sourceAbsPath);
@@ -5170,6 +5261,8 @@ async function deleteSchemaFiles(routes, rootDir, dist) {
5170
5261
  }
5171
5262
  }
5172
5263
  function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
5264
+ const cached = sourcePathCache.get(prodAbsPath);
5265
+ if (cached) return cached;
5173
5266
  const rel = path17.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
5174
5267
  let relWithoutDist = rel;
5175
5268
  if (relWithoutDist.startsWith(`${dist}/`)) {
@@ -5178,8 +5271,14 @@ function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
5178
5271
  const srcRel = `src/${relWithoutDist}`;
5179
5272
  const tsRel = srcRel.replace(/\.js$/, ".ts");
5180
5273
  const tsAbs = path17.resolve(rootDir, tsRel);
5181
- if (fs15.existsSync(tsAbs)) return tsAbs;
5182
- return path17.resolve(rootDir, srcRel);
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;
5183
5282
  }
5184
5283
  function setDevOnDemandEnabled(enabled) {
5185
5284
  state.enabled = enabled;
@@ -5193,7 +5292,7 @@ function setDevDist(dist) {
5193
5292
  function getDevDist() {
5194
5293
  return state.distDir;
5195
5294
  }
5196
- var state;
5295
+ var state, sourcePathCache;
5197
5296
  var init_compileOnDemand = __esm({
5198
5297
  "src/cli/compileOnDemand.ts"() {
5199
5298
  "use strict";
@@ -5201,17 +5300,17 @@ var init_compileOnDemand = __esm({
5201
5300
  init_generateSchemaFiles();
5202
5301
  init_generateSchemaFiles();
5203
5302
  state = createDevOnDemandState();
5303
+ sourcePathCache = /* @__PURE__ */ new Map();
5204
5304
  }
5205
5305
  });
5206
5306
 
5207
5307
  // src/loader/loadRouteModule.ts
5208
- import fs16 from "fs";
5209
5308
  async function loadRouteModule(filePath, method, rootDir) {
5210
5309
  if (isDevOnDemandEnabled() && rootDir) {
5211
5310
  const dist = getDevDist();
5212
5311
  if (dist) {
5213
5312
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
5214
- if (sourcePath && fs16.existsSync(sourcePath)) {
5313
+ if (sourcePath) {
5215
5314
  try {
5216
5315
  await ensureCompiled(sourcePath, rootDir, dist);
5217
5316
  } catch (compileErr) {
@@ -5537,7 +5636,9 @@ function formatSetCookie(name, value, options) {
5537
5636
  return cookie;
5538
5637
  }
5539
5638
  function createContext(request, params, config = {}, ip = "") {
5540
- 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 = "") {
5541
5642
  const meta = { headers: {}, setCookies: [] };
5542
5643
  const parsedCookies = parseCookies(request.headers.get("cookie") ?? "");
5543
5644
  const cookiesObj = {};
@@ -5713,7 +5814,7 @@ var init_parseMultipart = __esm({
5713
5814
  });
5714
5815
 
5715
5816
  // src/runtime/resolveInput.ts
5716
- async function resolveInput(method, request) {
5817
+ async function resolveInputFromUrl(method, request, url) {
5717
5818
  const inputType = getInputTypeForMethod(method);
5718
5819
  if (inputType === "body") {
5719
5820
  const contentType = request.headers.get("content-type") ?? "";
@@ -5748,7 +5849,6 @@ async function resolveInput(method, request) {
5748
5849
  }
5749
5850
  return result.data;
5750
5851
  }
5751
- const url = new URL(request.url);
5752
5852
  return queryToObject(url.searchParams);
5753
5853
  }
5754
5854
  var init_resolveInput = __esm({
@@ -6227,11 +6327,13 @@ var init_validateInput = __esm({
6227
6327
  });
6228
6328
 
6229
6329
  // src/utils/getClientIp.ts
6230
- function getClientIp(req) {
6231
- const xff = req.headers["x-forwarded-for"];
6232
- if (typeof xff === "string" && xff.length > 0) {
6233
- const first = xff.split(",")[0]?.trim();
6234
- 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
+ }
6235
6337
  }
6236
6338
  const remote = req.socket?.remoteAddress;
6237
6339
  if (remote) {
@@ -6488,7 +6590,7 @@ var init_wsHandler = __esm({
6488
6590
  });
6489
6591
 
6490
6592
  // src/server/handleWsUpgrade.ts
6491
- import fs17 from "fs";
6593
+ import fs16 from "fs";
6492
6594
  import { WebSocketServer, WebSocket } from "ws";
6493
6595
  import path18 from "path";
6494
6596
  function getPathname(req) {
@@ -6501,7 +6603,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
6501
6603
  const dist = getDevDist();
6502
6604
  if (dist) {
6503
6605
  const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
6504
- if (sourcePath && fs17.existsSync(sourcePath)) {
6606
+ if (sourcePath && fs16.existsSync(sourcePath)) {
6505
6607
  await ensureCompiled(sourcePath, rootDir, dist);
6506
6608
  }
6507
6609
  }
@@ -6559,7 +6661,7 @@ async function sendResponseToSocket(socket, response) {
6559
6661
  socket.destroy();
6560
6662
  }
6561
6663
  function attachWebSocket(options) {
6562
- const { server, routesRef, rootDir, config, globalMiddlewares } = options;
6664
+ const { server, routesRef, rootDir, config, globalMiddlewares, trustedProxy = false } = options;
6563
6665
  const wss = new WebSocketServer({ noServer: true });
6564
6666
  server.on("upgrade", async (req, socket, head) => {
6565
6667
  const currentWsRoutes = routesRef.wsCurrent;
@@ -6575,7 +6677,7 @@ function attachWebSocket(options) {
6575
6677
  const host = req.headers.host ?? "localhost";
6576
6678
  const url = `http://${host}${req.url ?? "/"}`;
6577
6679
  const request = new Request(url, { method: "GET", headers });
6578
- const ctx = createContext(request, params, config, getClientIp(req));
6680
+ const ctx = createContext(request, params, config, getClientIp(req, trustedProxy));
6579
6681
  const meta = ctx.meta;
6580
6682
  let upgraded = false;
6581
6683
  const finalHandler = async () => {
@@ -6664,16 +6766,26 @@ function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT) {
6664
6766
  const headers = nodeHttpToWebHeaders(req);
6665
6767
  const method = req.method ?? "GET";
6666
6768
  if (method === "GET" || method === "HEAD") {
6667
- 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
+ }
6668
6777
  }
6669
6778
  const stream = Readable3.toWeb(req);
6670
6779
  const limitedStream = limitStreamSize(stream, bodyLimit);
6671
- return new Request(url.toString(), {
6672
- method,
6673
- headers,
6674
- body: limitedStream,
6675
- duplex: "half"
6676
- });
6780
+ return {
6781
+ request: new Request(url.toString(), {
6782
+ method,
6783
+ headers,
6784
+ body: limitedStream,
6785
+ duplex: "half"
6786
+ }),
6787
+ url
6788
+ };
6677
6789
  }
6678
6790
  function limitStreamSize(stream, maxSize) {
6679
6791
  let totalSize = 0;
@@ -6725,22 +6837,6 @@ function limitStreamSize(stream, maxSize) {
6725
6837
  }
6726
6838
  });
6727
6839
  }
6728
- function findAllowedMethods(routes, path24) {
6729
- const methods = /* @__PURE__ */ new Set();
6730
- for (const route of routes) {
6731
- if (route.urlPath === path24) {
6732
- methods.add(route.method);
6733
- continue;
6734
- }
6735
- if (route.isDynamic) {
6736
- const params = matchDynamicPath(route.urlPath, path24, route.paramNames, route.isCatchAll);
6737
- if (params !== null) {
6738
- methods.add(route.method);
6739
- }
6740
- }
6741
- }
6742
- return Array.from(methods);
6743
- }
6744
6840
  function createServer(options) {
6745
6841
  const {
6746
6842
  routes,
@@ -6755,7 +6851,8 @@ function createServer(options) {
6755
6851
  helmet: helmetOption,
6756
6852
  logger: loggerOption,
6757
6853
  bodyLimit = DEFAULT_BODY_LIMIT,
6758
- http2: http2Option
6854
+ http2: http2Option,
6855
+ trustedProxy = false
6759
6856
  } = options;
6760
6857
  const routesRef = { current: routes, wsCurrent: wsRoutes ?? [] };
6761
6858
  const configMiddlewares = [];
@@ -6767,6 +6864,10 @@ function createServer(options) {
6767
6864
  }
6768
6865
  const loggerMiddlewareInst = loggerOption === false ? null : loggerOption === true || loggerOption === void 0 ? logger() : logger(loggerOption);
6769
6866
  if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);
6867
+ const outerMiddlewares = [...configMiddlewares];
6868
+ if (globalMiddlewares && globalMiddlewares.length > 0) {
6869
+ outerMiddlewares.push(...globalMiddlewares);
6870
+ }
6770
6871
  const server = (() => {
6771
6872
  if (http2Option) {
6772
6873
  const h2Opts = typeof http2Option === "object" ? http2Option : {};
@@ -6786,29 +6887,29 @@ function createServer(options) {
6786
6887
  dist,
6787
6888
  req,
6788
6889
  res,
6789
- configMiddlewares,
6890
+ outerMiddlewares,
6790
6891
  onError,
6791
6892
  config,
6792
- globalMiddlewares,
6793
6893
  globalInjectors,
6794
- bodyLimit
6894
+ bodyLimit,
6895
+ trustedProxy
6795
6896
  ).catch(() => {
6796
6897
  res.statusCode = 500;
6797
6898
  res.end();
6798
6899
  });
6799
6900
  });
6800
6901
  if (routesRef.wsCurrent.length > 0) {
6801
- attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares });
6902
+ attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares, trustedProxy });
6802
6903
  }
6803
6904
  return { server, routesRef };
6804
6905
  }
6805
- function prepareRequest(req, config, bodyLimit) {
6806
- const request = toWebRequest(req, bodyLimit);
6906
+ function prepareRequest(req, config, bodyLimit, trustedProxy) {
6907
+ const { request, url } = toWebRequest(req, bodyLimit);
6807
6908
  const method = request.method.toUpperCase();
6808
- const urlPath = new URL(request.url).pathname;
6809
- const ctx = createContext(request, {}, config, getClientIp(req));
6909
+ const urlPath = url.pathname;
6910
+ const ctx = createContextFromUrl(request, url, {}, config, getClientIp(req, trustedProxy));
6810
6911
  const meta = ctx.meta;
6811
- return { request, ctx, meta, method, urlPath };
6912
+ return { request, url, ctx, meta, method, urlPath };
6812
6913
  }
6813
6914
  function resolveRouteOrThrow(routes, method, urlPath) {
6814
6915
  const match = matchRoute(routes, method, urlPath);
@@ -6820,14 +6921,14 @@ function resolveRouteOrThrow(routes, method, urlPath) {
6820
6921
  throw new RouteNotFoundError(urlPath);
6821
6922
  }
6822
6923
  function createRoutePipeline(opts) {
6823
- const { routes, method, urlPath, ctx, request, rootDir, dist, globalInjectors } = opts;
6924
+ const { routes, method, urlPath, url, ctx, request, rootDir, dist, globalInjectors } = opts;
6824
6925
  return async () => {
6825
6926
  const match = resolveRouteOrThrow(routes, method, urlPath);
6826
6927
  ctx.params = match.params;
6827
6928
  const { route } = match;
6828
6929
  const absoluteFilePath = path19.resolve(rootDir, route.filePath);
6829
6930
  const routeModule = await loadRouteModule(absoluteFilePath, route.method, rootDir);
6830
- const input = await resolveInput(route.method, request);
6931
+ const input = await resolveInputFromUrl(route.method, request, url);
6831
6932
  const inputType = getInputTypeForMethod(route.method);
6832
6933
  const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
6833
6934
  if (isDevOnDemandEnabled()) {
@@ -6859,33 +6960,33 @@ async function sendSuccessResponse(response, res) {
6859
6960
  await sendNodeResponse(response, res);
6860
6961
  }
6861
6962
  async function sendErrorResponse(err, meta, res, onError, ctx) {
6862
- await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx.config), meta), res);
6863
- if (onError) {
6963
+ await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx?.config), meta), res);
6964
+ if (onError && ctx) {
6864
6965
  try {
6865
6966
  await onError(err, ctx);
6866
6967
  } catch {
6867
6968
  }
6868
6969
  }
6869
6970
  }
6870
- async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
6871
- const { request, ctx, meta, method, urlPath } = prepareRequest(req, config, bodyLimit);
6872
- const routePipeline = createRoutePipeline({
6873
- routes,
6874
- method,
6875
- urlPath,
6876
- ctx,
6877
- request,
6878
- rootDir,
6879
- dist,
6880
- globalMiddlewares,
6881
- globalInjectors
6882
- });
6883
- const outerMiddlewares = [];
6884
- if (configMiddlewares.length > 0) outerMiddlewares.push(...configMiddlewares);
6885
- if (globalMiddlewares && globalMiddlewares.length > 0) {
6886
- outerMiddlewares.push(...globalMiddlewares);
6887
- }
6971
+ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy) {
6972
+ let meta = { headers: {}, setCookies: [] };
6973
+ let ctx;
6888
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
+ });
6889
6990
  const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
6890
6991
  await sendSuccessResponse(response, res);
6891
6992
  } catch (err) {
@@ -7028,12 +7129,12 @@ var init_skillRegistry = __esm({
7028
7129
  });
7029
7130
 
7030
7131
  // src/cli/createAppCore.ts
7031
- import fs18 from "fs";
7132
+ import fs17 from "fs";
7032
7133
  import path20 from "path";
7033
7134
  import { PassThrough, Readable as Readable4 } from "stream";
7034
7135
  async function loadAndHydrateTools(rootDir, dist) {
7035
7136
  const toolsPath = path20.resolve(rootDir, dist, TOOLS_FILE2);
7036
- if (!fs18.existsSync(toolsPath)) {
7137
+ if (!fs17.existsSync(toolsPath)) {
7037
7138
  return [];
7038
7139
  }
7039
7140
  const serialized = await importWithCacheBust(toolsPath);
@@ -7043,7 +7144,7 @@ async function loadAndHydrateTools(rootDir, dist) {
7043
7144
  }
7044
7145
  async function loadAndHydrateAgents(rootDir, dist) {
7045
7146
  const agentsPath = path20.resolve(rootDir, dist, AGENTS_FILE2);
7046
- if (!fs18.existsSync(agentsPath)) {
7147
+ if (!fs17.existsSync(agentsPath)) {
7047
7148
  return [];
7048
7149
  }
7049
7150
  const serialized = await importWithCacheBust(agentsPath);
@@ -7068,7 +7169,7 @@ async function createAppBase(options) {
7068
7169
  const rootDir = options?.rootDir ?? process.cwd();
7069
7170
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
7070
7171
  const routesPath = path20.resolve(rootDir, dist, ROUTES_FILE);
7071
- if (!fs18.existsSync(routesPath)) {
7172
+ if (!fs17.existsSync(routesPath)) {
7072
7173
  throw new Error(
7073
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`
7074
7175
  );
@@ -7103,7 +7204,8 @@ async function createAppBase(options) {
7103
7204
  helmet: config?.helmet,
7104
7205
  logger: config?.logger,
7105
7206
  bodyLimit: config?.bodyLimit,
7106
- http2: config?.http2
7207
+ http2: config?.http2,
7208
+ trustedProxy: config?.trustedProxy
7107
7209
  });
7108
7210
  const { handlerWrappers, upgradeWrappers } = await loadPlugins(config?.plugins, {
7109
7211
  rootDir,
@@ -7322,6 +7424,7 @@ var init_createAppCore = __esm({
7322
7424
  "bodyLimit",
7323
7425
  "logger",
7324
7426
  "http2",
7427
+ "trustedProxy",
7325
7428
  "response"
7326
7429
  ]);
7327
7430
  }
@@ -7460,7 +7563,7 @@ var init_devCommand = __esm({
7460
7563
 
7461
7564
  // src/cli/compileBuildRoutes.ts
7462
7565
  import path22 from "path";
7463
- import fs19 from "fs";
7566
+ import fs18 from "fs";
7464
7567
  import fg5 from "fast-glob";
7465
7568
  async function compileBuildRoutes(options) {
7466
7569
  const { rootDir, dist, files, logLevel = "silent" } = options;
@@ -7474,7 +7577,7 @@ async function compileBuildRoutes(options) {
7474
7577
  return { compiledFiles: [] };
7475
7578
  }
7476
7579
  const absDist = path22.resolve(rootDir, dist);
7477
- await fs19.promises.mkdir(absDist, { recursive: true });
7580
+ await fs18.promises.mkdir(absDist, { recursive: true });
7478
7581
  const plugins = buildAliasPlugins(rootDir);
7479
7582
  const esbuild = await import("esbuild");
7480
7583
  const outbase = path22.resolve(rootDir, APP_DIR3);
@@ -7509,7 +7612,7 @@ __export(buildCommand_exports, {
7509
7612
  buildCommand: () => buildCommand
7510
7613
  });
7511
7614
  import path23 from "path";
7512
- import fs20 from "fs";
7615
+ import fs19 from "fs";
7513
7616
  async function buildCommand(options) {
7514
7617
  const rootDir = options?.rootDir ?? process.cwd();
7515
7618
  const outdir = options?.dist ?? DEFAULT_DIST2;
@@ -7582,7 +7685,7 @@ loadEnv(process.cwd());
7582
7685
  const app = await createProdApp(${createProdAppArgs});
7583
7686
  await app.listen();
7584
7687
  `;
7585
- await fs20.promises.writeFile(mainPath, mainContent, "utf-8");
7688
+ await fs19.promises.writeFile(mainPath, mainContent, "utf-8");
7586
7689
  console.log(` Written to ${mainPath}`);
7587
7690
  console.log("\nfaapi build completed");
7588
7691
  }