@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 +214 -111
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +51 -5
- package/dist/index.js +227 -122
- package/dist/index.js.map +1 -1
- package/dist/testing.js +196 -102
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
package/dist/testing.js
CHANGED
|
@@ -266,7 +266,9 @@ function formatSetCookie(name, value, options) {
|
|
|
266
266
|
return cookie;
|
|
267
267
|
}
|
|
268
268
|
function createContext(request, params, config = {}, ip = "") {
|
|
269
|
-
|
|
269
|
+
return createContextFromUrl(request, new URL(request.url), params, config, ip);
|
|
270
|
+
}
|
|
271
|
+
function createContextFromUrl(request, url, params, config = {}, ip = "") {
|
|
270
272
|
const meta = { headers: {}, setCookies: [] };
|
|
271
273
|
const parsedCookies = parseCookies(request.headers.get("cookie") ?? "");
|
|
272
274
|
const cookiesObj = {};
|
|
@@ -758,7 +760,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
758
760
|
// src/testServer.ts
|
|
759
761
|
import path11 from "path";
|
|
760
762
|
import os from "os";
|
|
761
|
-
import
|
|
763
|
+
import fs9 from "fs/promises";
|
|
762
764
|
|
|
763
765
|
// src/router/scanRoutes.ts
|
|
764
766
|
import fg from "fast-glob";
|
|
@@ -1105,6 +1107,45 @@ function createProgram(filePath) {
|
|
|
1105
1107
|
if (cached) {
|
|
1106
1108
|
return cached;
|
|
1107
1109
|
}
|
|
1110
|
+
const program = buildProgram([filePath], findTsConfig(filePath));
|
|
1111
|
+
programCache.set(filePath, program);
|
|
1112
|
+
return program;
|
|
1113
|
+
}
|
|
1114
|
+
function createPrograms(filePaths) {
|
|
1115
|
+
const unique = [...new Set(filePaths)];
|
|
1116
|
+
const result = /* @__PURE__ */ new Map();
|
|
1117
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1118
|
+
const noTsconfigFiles = [];
|
|
1119
|
+
for (const filePath of unique) {
|
|
1120
|
+
const tsconfigPath = findTsConfig(filePath);
|
|
1121
|
+
if (!tsconfigPath) {
|
|
1122
|
+
noTsconfigFiles.push(filePath);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const group = groups.get(tsconfigPath);
|
|
1126
|
+
if (group) {
|
|
1127
|
+
group.files.push(filePath);
|
|
1128
|
+
} else {
|
|
1129
|
+
groups.set(tsconfigPath, { tsconfigPath, files: [filePath] });
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
for (const { tsconfigPath, files } of groups.values()) {
|
|
1133
|
+
const cacheKey = `shared::${tsconfigPath}::${[...files].sort().join("|")}`;
|
|
1134
|
+
let program = programCache.get(cacheKey);
|
|
1135
|
+
if (!program) {
|
|
1136
|
+
program = buildProgram(files, tsconfigPath);
|
|
1137
|
+
programCache.set(cacheKey, program);
|
|
1138
|
+
}
|
|
1139
|
+
for (const filePath of files) {
|
|
1140
|
+
result.set(filePath, program);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
for (const filePath of noTsconfigFiles) {
|
|
1144
|
+
result.set(filePath, createProgram(filePath));
|
|
1145
|
+
}
|
|
1146
|
+
return result;
|
|
1147
|
+
}
|
|
1148
|
+
function buildProgram(entryFiles, tsconfigPath) {
|
|
1108
1149
|
const options = {
|
|
1109
1150
|
strict: true,
|
|
1110
1151
|
target: ts2.ScriptTarget.ES2022,
|
|
@@ -1113,8 +1154,7 @@ function createProgram(filePath) {
|
|
|
1113
1154
|
skipLibCheck: true,
|
|
1114
1155
|
noEmit: true
|
|
1115
1156
|
};
|
|
1116
|
-
|
|
1117
|
-
const tsconfigPath = findTsConfig(filePath);
|
|
1157
|
+
const rootNames = [...entryFiles];
|
|
1118
1158
|
if (tsconfigPath) {
|
|
1119
1159
|
const tsOptions = parseTsConfig(tsconfigPath);
|
|
1120
1160
|
if (tsOptions.module !== void 0) {
|
|
@@ -1124,16 +1164,14 @@ function createProgram(filePath) {
|
|
|
1124
1164
|
options.moduleResolution = tsOptions.moduleResolution;
|
|
1125
1165
|
}
|
|
1126
1166
|
if (tsOptions.fileNames.length > 0) {
|
|
1127
|
-
|
|
1128
|
-
rootNames
|
|
1129
|
-
|
|
1130
|
-
|
|
1167
|
+
for (const fileName of tsOptions.fileNames) {
|
|
1168
|
+
if (!rootNames.includes(fileName)) {
|
|
1169
|
+
rootNames.push(fileName);
|
|
1170
|
+
}
|
|
1131
1171
|
}
|
|
1132
1172
|
}
|
|
1133
1173
|
}
|
|
1134
|
-
|
|
1135
|
-
programCache.set(filePath, program);
|
|
1136
|
-
return program;
|
|
1174
|
+
return ts2.createProgram(rootNames, options);
|
|
1137
1175
|
}
|
|
1138
1176
|
|
|
1139
1177
|
// src/ast/extractHandlerTypes.ts
|
|
@@ -1912,12 +1950,11 @@ function collectRouteSchemaSources(routes, rootDir) {
|
|
|
1912
1950
|
}
|
|
1913
1951
|
entry.methods.add(route.method);
|
|
1914
1952
|
}
|
|
1915
|
-
const programByFile =
|
|
1953
|
+
const programByFile = createPrograms([...methodsByFile.keys()]);
|
|
1916
1954
|
const allTypesByFile = /* @__PURE__ */ new Map();
|
|
1917
1955
|
const mergedAllTypes = /* @__PURE__ */ new Map();
|
|
1918
1956
|
for (const filePath of methodsByFile.keys()) {
|
|
1919
|
-
const program =
|
|
1920
|
-
programByFile.set(filePath, program);
|
|
1957
|
+
const program = programByFile.get(filePath);
|
|
1921
1958
|
const allTypes = extractAllTypes(program, filePath);
|
|
1922
1959
|
allTypesByFile.set(filePath, allTypes);
|
|
1923
1960
|
for (const [name, info] of allTypes) {
|
|
@@ -2686,7 +2723,10 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
|
|
|
2686
2723
|
state.inFlightSchemaGenerations.delete(schemaPath);
|
|
2687
2724
|
}
|
|
2688
2725
|
}
|
|
2726
|
+
var sourcePathCache = /* @__PURE__ */ new Map();
|
|
2689
2727
|
function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
|
|
2728
|
+
const cached = sourcePathCache.get(prodAbsPath);
|
|
2729
|
+
if (cached) return cached;
|
|
2690
2730
|
const rel = path8.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
|
|
2691
2731
|
let relWithoutDist = rel;
|
|
2692
2732
|
if (relWithoutDist.startsWith(`${dist}/`)) {
|
|
@@ -2695,8 +2735,14 @@ function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
|
|
|
2695
2735
|
const srcRel = `src/${relWithoutDist}`;
|
|
2696
2736
|
const tsRel = srcRel.replace(/\.js$/, ".ts");
|
|
2697
2737
|
const tsAbs = path8.resolve(rootDir, tsRel);
|
|
2698
|
-
|
|
2699
|
-
|
|
2738
|
+
let result;
|
|
2739
|
+
if (fs7.existsSync(tsAbs)) {
|
|
2740
|
+
result = tsAbs;
|
|
2741
|
+
} else {
|
|
2742
|
+
result = path8.resolve(rootDir, srcRel);
|
|
2743
|
+
}
|
|
2744
|
+
sourcePathCache.set(prodAbsPath, result);
|
|
2745
|
+
return result;
|
|
2700
2746
|
}
|
|
2701
2747
|
function isDevOnDemandEnabled() {
|
|
2702
2748
|
return state.enabled;
|
|
@@ -2802,15 +2848,50 @@ import { Readable as Readable2 } from "stream";
|
|
|
2802
2848
|
import path10 from "path";
|
|
2803
2849
|
|
|
2804
2850
|
// src/router/matchRoute.ts
|
|
2805
|
-
|
|
2851
|
+
var httpIndexCache = /* @__PURE__ */ new WeakMap();
|
|
2852
|
+
var wsIndexCache = /* @__PURE__ */ new WeakMap();
|
|
2853
|
+
function getHttpIndex(routes) {
|
|
2854
|
+
let index = httpIndexCache.get(routes);
|
|
2855
|
+
if (index) return index;
|
|
2856
|
+
index = { static: /* @__PURE__ */ new Map(), methodsByStaticPath: /* @__PURE__ */ new Map(), dynamics: [] };
|
|
2806
2857
|
for (const route of routes) {
|
|
2807
|
-
if (route.
|
|
2808
|
-
|
|
2809
|
-
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2858
|
+
if (route.isDynamic) {
|
|
2859
|
+
index.dynamics.push(route);
|
|
2860
|
+
} else {
|
|
2861
|
+
index.static.set(`${route.method}|${route.urlPath}`, route);
|
|
2862
|
+
let methods = index.methodsByStaticPath.get(route.urlPath);
|
|
2863
|
+
if (!methods) {
|
|
2864
|
+
methods = /* @__PURE__ */ new Set();
|
|
2865
|
+
index.methodsByStaticPath.set(route.urlPath, methods);
|
|
2813
2866
|
}
|
|
2867
|
+
methods.add(route.method);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
httpIndexCache.set(routes, index);
|
|
2871
|
+
return index;
|
|
2872
|
+
}
|
|
2873
|
+
function getWsIndex(routes) {
|
|
2874
|
+
let index = wsIndexCache.get(routes);
|
|
2875
|
+
if (index) return index;
|
|
2876
|
+
index = { static: /* @__PURE__ */ new Map(), dynamics: [] };
|
|
2877
|
+
for (const route of routes) {
|
|
2878
|
+
if (route.isDynamic) {
|
|
2879
|
+
index.dynamics.push(route);
|
|
2880
|
+
} else {
|
|
2881
|
+
index.static.set(route.urlPath, route);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
wsIndexCache.set(routes, index);
|
|
2885
|
+
return index;
|
|
2886
|
+
}
|
|
2887
|
+
function matchRoute(routes, method, path12) {
|
|
2888
|
+
const index = getHttpIndex(routes);
|
|
2889
|
+
const staticHit = index.static.get(`${method}|${path12}`);
|
|
2890
|
+
if (staticHit) {
|
|
2891
|
+
return { route: staticHit, params: {} };
|
|
2892
|
+
}
|
|
2893
|
+
for (const route of index.dynamics) {
|
|
2894
|
+
if (route.method !== method) {
|
|
2814
2895
|
continue;
|
|
2815
2896
|
}
|
|
2816
2897
|
const params = matchDynamicPath(route.urlPath, path12, route.paramNames, route.isCatchAll);
|
|
@@ -2821,13 +2902,12 @@ function matchRoute(routes, method, path12) {
|
|
|
2821
2902
|
return null;
|
|
2822
2903
|
}
|
|
2823
2904
|
function matchWsRoute(wsRoutes, path12) {
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
}
|
|
2905
|
+
const index = getWsIndex(wsRoutes);
|
|
2906
|
+
const staticHit = index.static.get(path12);
|
|
2907
|
+
if (staticHit) {
|
|
2908
|
+
return { route: staticHit, params: {} };
|
|
2909
|
+
}
|
|
2910
|
+
for (const route of index.dynamics) {
|
|
2831
2911
|
const params = matchDynamicPath(route.urlPath, path12, route.paramNames, route.isCatchAll);
|
|
2832
2912
|
if (params !== null) {
|
|
2833
2913
|
return { route, params };
|
|
@@ -2835,6 +2915,23 @@ function matchWsRoute(wsRoutes, path12) {
|
|
|
2835
2915
|
}
|
|
2836
2916
|
return null;
|
|
2837
2917
|
}
|
|
2918
|
+
function findAllowedMethods(routes, path12) {
|
|
2919
|
+
const index = getHttpIndex(routes);
|
|
2920
|
+
const methods = /* @__PURE__ */ new Set();
|
|
2921
|
+
const staticMethods = index.methodsByStaticPath.get(path12);
|
|
2922
|
+
if (staticMethods) {
|
|
2923
|
+
for (const method of staticMethods) {
|
|
2924
|
+
methods.add(method);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
for (const route of index.dynamics) {
|
|
2928
|
+
const params = matchDynamicPath(route.urlPath, path12, route.paramNames, route.isCatchAll);
|
|
2929
|
+
if (params !== null) {
|
|
2930
|
+
methods.add(route.method);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
return Array.from(methods);
|
|
2934
|
+
}
|
|
2838
2935
|
function matchDynamicPath(pattern, path12, paramNames, isCatchAll) {
|
|
2839
2936
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
2840
2937
|
const pathSegments = path12.split("/").filter(Boolean);
|
|
@@ -2882,9 +2979,6 @@ function matchDynamicPath(pattern, path12, paramNames, isCatchAll) {
|
|
|
2882
2979
|
return params;
|
|
2883
2980
|
}
|
|
2884
2981
|
|
|
2885
|
-
// src/loader/loadRouteModule.ts
|
|
2886
|
-
import fs8 from "fs";
|
|
2887
|
-
|
|
2888
2982
|
// src/loader/resolveExports.ts
|
|
2889
2983
|
function resolveExport(module, exportName) {
|
|
2890
2984
|
if (exportName in module && typeof module[exportName] !== "undefined") {
|
|
@@ -2915,7 +3009,7 @@ async function loadRouteModule(filePath, method, rootDir) {
|
|
|
2915
3009
|
const dist = getDevDist();
|
|
2916
3010
|
if (dist) {
|
|
2917
3011
|
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
2918
|
-
if (sourcePath
|
|
3012
|
+
if (sourcePath) {
|
|
2919
3013
|
try {
|
|
2920
3014
|
await ensureCompiled(sourcePath, rootDir, dist);
|
|
2921
3015
|
} catch (compileErr) {
|
|
@@ -2980,7 +3074,7 @@ async function parseMultipart(request) {
|
|
|
2980
3074
|
}
|
|
2981
3075
|
|
|
2982
3076
|
// src/runtime/resolveInput.ts
|
|
2983
|
-
async function
|
|
3077
|
+
async function resolveInputFromUrl(method, request, url) {
|
|
2984
3078
|
const inputType = getInputTypeForMethod(method);
|
|
2985
3079
|
if (inputType === "body") {
|
|
2986
3080
|
const contentType = request.headers.get("content-type") ?? "";
|
|
@@ -3015,7 +3109,6 @@ async function resolveInput(method, request) {
|
|
|
3015
3109
|
}
|
|
3016
3110
|
return result.data;
|
|
3017
3111
|
}
|
|
3018
|
-
const url = new URL(request.url);
|
|
3019
3112
|
return queryToObject(url.searchParams);
|
|
3020
3113
|
}
|
|
3021
3114
|
|
|
@@ -3044,11 +3137,13 @@ async function sendNodeResponse(response, res) {
|
|
|
3044
3137
|
}
|
|
3045
3138
|
|
|
3046
3139
|
// src/utils/getClientIp.ts
|
|
3047
|
-
function getClientIp(req) {
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3140
|
+
function getClientIp(req, trustedProxy = false) {
|
|
3141
|
+
if (trustedProxy) {
|
|
3142
|
+
const xff = req.headers["x-forwarded-for"];
|
|
3143
|
+
if (typeof xff === "string" && xff.length > 0) {
|
|
3144
|
+
const first = xff.split(",")[0]?.trim();
|
|
3145
|
+
if (first) return first;
|
|
3146
|
+
}
|
|
3052
3147
|
}
|
|
3053
3148
|
const remote = req.socket?.remoteAddress;
|
|
3054
3149
|
if (remote) {
|
|
@@ -3221,7 +3316,7 @@ function logger(options = {}) {
|
|
|
3221
3316
|
}
|
|
3222
3317
|
|
|
3223
3318
|
// src/server/handleWsUpgrade.ts
|
|
3224
|
-
import
|
|
3319
|
+
import fs8 from "fs";
|
|
3225
3320
|
import { WebSocketServer, WebSocket } from "ws";
|
|
3226
3321
|
import path9 from "path";
|
|
3227
3322
|
|
|
@@ -3279,7 +3374,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
|
|
|
3279
3374
|
const dist = getDevDist();
|
|
3280
3375
|
if (dist) {
|
|
3281
3376
|
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
3282
|
-
if (sourcePath &&
|
|
3377
|
+
if (sourcePath && fs8.existsSync(sourcePath)) {
|
|
3283
3378
|
await ensureCompiled(sourcePath, rootDir, dist);
|
|
3284
3379
|
}
|
|
3285
3380
|
}
|
|
@@ -3337,7 +3432,7 @@ async function sendResponseToSocket(socket, response) {
|
|
|
3337
3432
|
socket.destroy();
|
|
3338
3433
|
}
|
|
3339
3434
|
function attachWebSocket(options) {
|
|
3340
|
-
const { server, routesRef, rootDir, config, globalMiddlewares } = options;
|
|
3435
|
+
const { server, routesRef, rootDir, config, globalMiddlewares, trustedProxy = false } = options;
|
|
3341
3436
|
const wss = new WebSocketServer({ noServer: true });
|
|
3342
3437
|
server.on("upgrade", async (req, socket, head) => {
|
|
3343
3438
|
const currentWsRoutes = routesRef.wsCurrent;
|
|
@@ -3353,7 +3448,7 @@ function attachWebSocket(options) {
|
|
|
3353
3448
|
const host = req.headers.host ?? "localhost";
|
|
3354
3449
|
const url = `http://${host}${req.url ?? "/"}`;
|
|
3355
3450
|
const request = new Request(url, { method: "GET", headers });
|
|
3356
|
-
const ctx = createContext(request, params, config, getClientIp(req));
|
|
3451
|
+
const ctx = createContext(request, params, config, getClientIp(req, trustedProxy));
|
|
3357
3452
|
const meta = ctx.meta;
|
|
3358
3453
|
let upgraded = false;
|
|
3359
3454
|
const finalHandler = async () => {
|
|
@@ -3422,16 +3517,26 @@ function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT) {
|
|
|
3422
3517
|
const headers = nodeHttpToWebHeaders(req);
|
|
3423
3518
|
const method = req.method ?? "GET";
|
|
3424
3519
|
if (method === "GET" || method === "HEAD") {
|
|
3425
|
-
return new Request(url.toString(), { method, headers });
|
|
3520
|
+
return { request: new Request(url.toString(), { method, headers }), url };
|
|
3521
|
+
}
|
|
3522
|
+
const contentLength = req.headers["content-length"];
|
|
3523
|
+
if (contentLength !== void 0) {
|
|
3524
|
+
const declared = Number(Array.isArray(contentLength) ? contentLength[0] : contentLength);
|
|
3525
|
+
if (Number.isFinite(declared) && declared > bodyLimit) {
|
|
3526
|
+
throw new PayloadTooLargeError(bodyLimit);
|
|
3527
|
+
}
|
|
3426
3528
|
}
|
|
3427
3529
|
const stream = Readable2.toWeb(req);
|
|
3428
3530
|
const limitedStream = limitStreamSize(stream, bodyLimit);
|
|
3429
|
-
return
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3531
|
+
return {
|
|
3532
|
+
request: new Request(url.toString(), {
|
|
3533
|
+
method,
|
|
3534
|
+
headers,
|
|
3535
|
+
body: limitedStream,
|
|
3536
|
+
duplex: "half"
|
|
3537
|
+
}),
|
|
3538
|
+
url
|
|
3539
|
+
};
|
|
3435
3540
|
}
|
|
3436
3541
|
function limitStreamSize(stream, maxSize) {
|
|
3437
3542
|
let totalSize = 0;
|
|
@@ -3483,22 +3588,6 @@ function limitStreamSize(stream, maxSize) {
|
|
|
3483
3588
|
}
|
|
3484
3589
|
});
|
|
3485
3590
|
}
|
|
3486
|
-
function findAllowedMethods(routes, path12) {
|
|
3487
|
-
const methods = /* @__PURE__ */ new Set();
|
|
3488
|
-
for (const route of routes) {
|
|
3489
|
-
if (route.urlPath === path12) {
|
|
3490
|
-
methods.add(route.method);
|
|
3491
|
-
continue;
|
|
3492
|
-
}
|
|
3493
|
-
if (route.isDynamic) {
|
|
3494
|
-
const params = matchDynamicPath(route.urlPath, path12, route.paramNames, route.isCatchAll);
|
|
3495
|
-
if (params !== null) {
|
|
3496
|
-
methods.add(route.method);
|
|
3497
|
-
}
|
|
3498
|
-
}
|
|
3499
|
-
}
|
|
3500
|
-
return Array.from(methods);
|
|
3501
|
-
}
|
|
3502
3591
|
function createServer(options) {
|
|
3503
3592
|
const {
|
|
3504
3593
|
routes,
|
|
@@ -3513,7 +3602,8 @@ function createServer(options) {
|
|
|
3513
3602
|
helmet: helmetOption,
|
|
3514
3603
|
logger: loggerOption,
|
|
3515
3604
|
bodyLimit = DEFAULT_BODY_LIMIT,
|
|
3516
|
-
http2: http2Option
|
|
3605
|
+
http2: http2Option,
|
|
3606
|
+
trustedProxy = false
|
|
3517
3607
|
} = options;
|
|
3518
3608
|
const routesRef = { current: routes, wsCurrent: wsRoutes ?? [] };
|
|
3519
3609
|
const configMiddlewares = [];
|
|
@@ -3525,6 +3615,10 @@ function createServer(options) {
|
|
|
3525
3615
|
}
|
|
3526
3616
|
const loggerMiddlewareInst = loggerOption === false ? null : loggerOption === true || loggerOption === void 0 ? logger() : logger(loggerOption);
|
|
3527
3617
|
if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);
|
|
3618
|
+
const outerMiddlewares = [...configMiddlewares];
|
|
3619
|
+
if (globalMiddlewares && globalMiddlewares.length > 0) {
|
|
3620
|
+
outerMiddlewares.push(...globalMiddlewares);
|
|
3621
|
+
}
|
|
3528
3622
|
const server = (() => {
|
|
3529
3623
|
if (http2Option) {
|
|
3530
3624
|
const h2Opts = typeof http2Option === "object" ? http2Option : {};
|
|
@@ -3544,29 +3638,29 @@ function createServer(options) {
|
|
|
3544
3638
|
dist,
|
|
3545
3639
|
req,
|
|
3546
3640
|
res,
|
|
3547
|
-
|
|
3641
|
+
outerMiddlewares,
|
|
3548
3642
|
onError,
|
|
3549
3643
|
config,
|
|
3550
|
-
globalMiddlewares,
|
|
3551
3644
|
globalInjectors,
|
|
3552
|
-
bodyLimit
|
|
3645
|
+
bodyLimit,
|
|
3646
|
+
trustedProxy
|
|
3553
3647
|
).catch(() => {
|
|
3554
3648
|
res.statusCode = 500;
|
|
3555
3649
|
res.end();
|
|
3556
3650
|
});
|
|
3557
3651
|
});
|
|
3558
3652
|
if (routesRef.wsCurrent.length > 0) {
|
|
3559
|
-
attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares });
|
|
3653
|
+
attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares, trustedProxy });
|
|
3560
3654
|
}
|
|
3561
3655
|
return { server, routesRef };
|
|
3562
3656
|
}
|
|
3563
|
-
function prepareRequest(req, config, bodyLimit) {
|
|
3564
|
-
const request = toWebRequest(req, bodyLimit);
|
|
3657
|
+
function prepareRequest(req, config, bodyLimit, trustedProxy) {
|
|
3658
|
+
const { request, url } = toWebRequest(req, bodyLimit);
|
|
3565
3659
|
const method = request.method.toUpperCase();
|
|
3566
|
-
const urlPath =
|
|
3567
|
-
const ctx =
|
|
3660
|
+
const urlPath = url.pathname;
|
|
3661
|
+
const ctx = createContextFromUrl(request, url, {}, config, getClientIp(req, trustedProxy));
|
|
3568
3662
|
const meta = ctx.meta;
|
|
3569
|
-
return { request, ctx, meta, method, urlPath };
|
|
3663
|
+
return { request, url, ctx, meta, method, urlPath };
|
|
3570
3664
|
}
|
|
3571
3665
|
function resolveRouteOrThrow(routes, method, urlPath) {
|
|
3572
3666
|
const match = matchRoute(routes, method, urlPath);
|
|
@@ -3578,14 +3672,14 @@ function resolveRouteOrThrow(routes, method, urlPath) {
|
|
|
3578
3672
|
throw new RouteNotFoundError(urlPath);
|
|
3579
3673
|
}
|
|
3580
3674
|
function createRoutePipeline(opts) {
|
|
3581
|
-
const { routes, method, urlPath, ctx, request, rootDir, dist, globalInjectors } = opts;
|
|
3675
|
+
const { routes, method, urlPath, url, ctx, request, rootDir, dist, globalInjectors } = opts;
|
|
3582
3676
|
return async () => {
|
|
3583
3677
|
const match = resolveRouteOrThrow(routes, method, urlPath);
|
|
3584
3678
|
ctx.params = match.params;
|
|
3585
3679
|
const { route } = match;
|
|
3586
3680
|
const absoluteFilePath = path10.resolve(rootDir, route.filePath);
|
|
3587
3681
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method, rootDir);
|
|
3588
|
-
const input = await
|
|
3682
|
+
const input = await resolveInputFromUrl(route.method, request, url);
|
|
3589
3683
|
const inputType = getInputTypeForMethod(route.method);
|
|
3590
3684
|
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
3591
3685
|
if (isDevOnDemandEnabled()) {
|
|
@@ -3617,33 +3711,33 @@ async function sendSuccessResponse(response, res) {
|
|
|
3617
3711
|
await sendNodeResponse(response, res);
|
|
3618
3712
|
}
|
|
3619
3713
|
async function sendErrorResponse(err, meta, res, onError, ctx) {
|
|
3620
|
-
await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx
|
|
3621
|
-
if (onError) {
|
|
3714
|
+
await sendNodeResponse(mergeMeta(buildErrorResponse(err, ctx?.config), meta), res);
|
|
3715
|
+
if (onError && ctx) {
|
|
3622
3716
|
try {
|
|
3623
3717
|
await onError(err, ctx);
|
|
3624
3718
|
} catch {
|
|
3625
3719
|
}
|
|
3626
3720
|
}
|
|
3627
3721
|
}
|
|
3628
|
-
async function handleRequest(routes, rootDir, dist, req, res,
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
routes,
|
|
3632
|
-
method,
|
|
3633
|
-
urlPath,
|
|
3634
|
-
ctx,
|
|
3635
|
-
request,
|
|
3636
|
-
rootDir,
|
|
3637
|
-
dist,
|
|
3638
|
-
globalMiddlewares,
|
|
3639
|
-
globalInjectors
|
|
3640
|
-
});
|
|
3641
|
-
const outerMiddlewares = [];
|
|
3642
|
-
if (configMiddlewares.length > 0) outerMiddlewares.push(...configMiddlewares);
|
|
3643
|
-
if (globalMiddlewares && globalMiddlewares.length > 0) {
|
|
3644
|
-
outerMiddlewares.push(...globalMiddlewares);
|
|
3645
|
-
}
|
|
3722
|
+
async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy) {
|
|
3723
|
+
let meta = { headers: {}, setCookies: [] };
|
|
3724
|
+
let ctx;
|
|
3646
3725
|
try {
|
|
3726
|
+
const prepared = prepareRequest(req, config, bodyLimit, trustedProxy);
|
|
3727
|
+
ctx = prepared.ctx;
|
|
3728
|
+
meta = prepared.meta;
|
|
3729
|
+
const { request, url, method, urlPath } = prepared;
|
|
3730
|
+
const routePipeline = createRoutePipeline({
|
|
3731
|
+
routes,
|
|
3732
|
+
method,
|
|
3733
|
+
urlPath,
|
|
3734
|
+
url,
|
|
3735
|
+
ctx,
|
|
3736
|
+
request,
|
|
3737
|
+
rootDir,
|
|
3738
|
+
dist,
|
|
3739
|
+
globalInjectors
|
|
3740
|
+
});
|
|
3647
3741
|
const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
|
|
3648
3742
|
await sendSuccessResponse(response, res);
|
|
3649
3743
|
} catch (err) {
|
|
@@ -3670,7 +3764,7 @@ async function createTestServer(options) {
|
|
|
3670
3764
|
} = options;
|
|
3671
3765
|
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns);
|
|
3672
3766
|
const sorted = sortRoutes(routes);
|
|
3673
|
-
const schemaDist = dist ? path11.isAbsolute(dist) ? dist : path11.resolve(rootDir, dist) : await
|
|
3767
|
+
const schemaDist = dist ? path11.isAbsolute(dist) ? dist : path11.resolve(rootDir, dist) : await fs9.mkdtemp(path11.join(os.tmpdir(), "faapi-test-schema-"));
|
|
3674
3768
|
await generateSchemaFiles(sorted, rootDir, schemaDist);
|
|
3675
3769
|
const { server } = createServer({
|
|
3676
3770
|
routes: sorted,
|
|
@@ -3703,7 +3797,7 @@ async function createTestServer(options) {
|
|
|
3703
3797
|
await new Promise((resolve) => {
|
|
3704
3798
|
server.close(() => resolve());
|
|
3705
3799
|
});
|
|
3706
|
-
await
|
|
3800
|
+
await fs9.rm(schemaDist, { recursive: true, force: true }).catch(() => {
|
|
3707
3801
|
});
|
|
3708
3802
|
invalidateSchemaCache();
|
|
3709
3803
|
}
|