@faapi/faapi 6.20.0 → 6.21.1
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 +297 -144
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +16 -21
- package/dist/index.js +300 -148
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-DTqdo28V.d.ts → routeTypes-gyz4PMh7.d.ts} +46 -6
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +263 -114
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -711,8 +711,8 @@ var init_parseRouteFile = __esm({
|
|
|
711
711
|
// src/utils/importWithCacheBust.ts
|
|
712
712
|
import fs8 from "fs";
|
|
713
713
|
import { pathToFileURL } from "url";
|
|
714
|
-
function setLoadTimestamp(
|
|
715
|
-
loadTs =
|
|
714
|
+
function setLoadTimestamp(ts12) {
|
|
715
|
+
loadTs = ts12;
|
|
716
716
|
}
|
|
717
717
|
function getVitestImportActual() {
|
|
718
718
|
const vi = globalThis.vi;
|
|
@@ -2513,8 +2513,16 @@ var init_resolveInjection = __esm({
|
|
|
2513
2513
|
// Phase 2.3
|
|
2514
2514
|
tasks: "tasks",
|
|
2515
2515
|
// 任务子系统:TaskClient(入队/查询)
|
|
2516
|
-
log: "log"
|
|
2516
|
+
log: "log",
|
|
2517
2517
|
// 请求级日志器(ctx.log 同一实例:scope http,自动带 requestId 等字段)
|
|
2518
|
+
// raw 系:恒原始输入(无 schema、无校验、管线永不转换),与 query/params/body
|
|
2519
|
+
// 的「校验转换后」口径二分互补
|
|
2520
|
+
rawQuery: "rawQuery",
|
|
2521
|
+
// URLSearchParams(原始查询参数)
|
|
2522
|
+
rawParams: "rawParams",
|
|
2523
|
+
// Record<string, string>(URL 原始路径段)
|
|
2524
|
+
rawBody: "rawBody"
|
|
2525
|
+
// string | undefined(请求体原始文本)
|
|
2518
2526
|
};
|
|
2519
2527
|
injectionCache = /* @__PURE__ */ new WeakMap();
|
|
2520
2528
|
}
|
|
@@ -2572,7 +2580,19 @@ var init_analyzeInjection = __esm({
|
|
|
2572
2580
|
|
|
2573
2581
|
// src/cli/collectRouteSchemaSources.ts
|
|
2574
2582
|
import path11 from "path";
|
|
2575
|
-
|
|
2583
|
+
import ts8 from "typescript";
|
|
2584
|
+
function findExportedTypeName(sourceFile, name) {
|
|
2585
|
+
for (const statement of sourceFile.statements) {
|
|
2586
|
+
const modifiers = ts8.canHaveModifiers(statement) ? ts8.getModifiers(statement) : void 0;
|
|
2587
|
+
const isExported = modifiers?.some((m) => m.kind === ts8.SyntaxKind.ExportKeyword);
|
|
2588
|
+
if (!isExported) continue;
|
|
2589
|
+
if ((ts8.isInterfaceDeclaration(statement) || ts8.isTypeAliasDeclaration(statement)) && statement.name?.text === name) {
|
|
2590
|
+
return true;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
return false;
|
|
2594
|
+
}
|
|
2595
|
+
function collectRouteSchemaSources(routes, rootDir, wsRoutes) {
|
|
2576
2596
|
const methodsByFile = /* @__PURE__ */ new Map();
|
|
2577
2597
|
for (const route of routes) {
|
|
2578
2598
|
const filePath = rootDir ? path11.resolve(rootDir, route.filePath) : route.filePath;
|
|
@@ -2583,9 +2603,17 @@ function collectRouteSchemaSources(routes, rootDir) {
|
|
|
2583
2603
|
}
|
|
2584
2604
|
entry.methods.add(route.method);
|
|
2585
2605
|
}
|
|
2586
|
-
const
|
|
2606
|
+
const wsFiles = [];
|
|
2607
|
+
for (const wsRoute of wsRoutes ?? []) {
|
|
2608
|
+
wsFiles.push({
|
|
2609
|
+
filePath: rootDir ? path11.resolve(rootDir, wsRoute.filePath) : wsRoute.filePath,
|
|
2610
|
+
urlPath: wsRoute.urlPath
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
const allFiles = [.../* @__PURE__ */ new Set([...methodsByFile.keys(), ...wsFiles.map((f) => f.filePath)])];
|
|
2614
|
+
const programByFile = createPrograms(allFiles);
|
|
2587
2615
|
const resolversByFile = /* @__PURE__ */ new Map();
|
|
2588
|
-
for (const filePath of
|
|
2616
|
+
for (const filePath of allFiles) {
|
|
2589
2617
|
resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath), filePath));
|
|
2590
2618
|
}
|
|
2591
2619
|
const sources = [];
|
|
@@ -2641,6 +2669,28 @@ function collectRouteSchemaSources(routes, rootDir) {
|
|
|
2641
2669
|
}
|
|
2642
2670
|
}
|
|
2643
2671
|
}
|
|
2672
|
+
for (const wsFile of wsFiles) {
|
|
2673
|
+
const program = programByFile.get(wsFile.filePath);
|
|
2674
|
+
const sourceFile = program?.getSourceFile(wsFile.filePath);
|
|
2675
|
+
if (!sourceFile) continue;
|
|
2676
|
+
const resolver = resolversByFile.get(wsFile.filePath);
|
|
2677
|
+
if (findExportedTypeName(sourceFile, "Query")) {
|
|
2678
|
+
sources.push({
|
|
2679
|
+
urlPath: wsFile.urlPath,
|
|
2680
|
+
filePath: wsFile.filePath,
|
|
2681
|
+
schemaName: getSchemaName("WS", "query"),
|
|
2682
|
+
typeInfo: resolver.resolve("Query") ?? null
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
if (findExportedTypeName(sourceFile, "Params")) {
|
|
2686
|
+
sources.push({
|
|
2687
|
+
urlPath: wsFile.urlPath,
|
|
2688
|
+
filePath: wsFile.filePath,
|
|
2689
|
+
schemaName: getSchemaName("WS", "params"),
|
|
2690
|
+
typeInfo: resolver.resolve("Params") ?? null
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2644
2694
|
return { sources, resolversByFile };
|
|
2645
2695
|
}
|
|
2646
2696
|
var init_collectRouteSchemaSources = __esm({
|
|
@@ -2727,9 +2777,9 @@ function generateSchemaFileSource(sources, resolveType, helpersImportPath) {
|
|
|
2727
2777
|
lines.push(...schemaBlocks);
|
|
2728
2778
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
2729
2779
|
}
|
|
2730
|
-
async function generateSchemaFiles(routes, rootDir, dist) {
|
|
2731
|
-
if (routes.length === 0) return;
|
|
2732
|
-
const { sources, resolversByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
2780
|
+
async function generateSchemaFiles(routes, rootDir, dist, wsRoutes) {
|
|
2781
|
+
if (routes.length === 0 && (wsRoutes?.length ?? 0) === 0) return;
|
|
2782
|
+
const { sources, resolversByFile } = collectRouteSchemaSources(routes, rootDir, wsRoutes);
|
|
2733
2783
|
await generateZodArtifacts(sources, {
|
|
2734
2784
|
generateFileSource: generateSchemaFileSource,
|
|
2735
2785
|
resolversByFile,
|
|
@@ -2800,14 +2850,14 @@ var init_generateZodArtifacts = __esm({
|
|
|
2800
2850
|
});
|
|
2801
2851
|
|
|
2802
2852
|
// src/ast/jsDocMetadata.ts
|
|
2803
|
-
import
|
|
2853
|
+
import ts9 from "typescript";
|
|
2804
2854
|
function hasExportModifier(node) {
|
|
2805
|
-
if (!
|
|
2806
|
-
const modifiers =
|
|
2807
|
-
return !!modifiers?.some((m) => m.kind ===
|
|
2855
|
+
if (!ts9.canHaveModifiers(node)) return false;
|
|
2856
|
+
const modifiers = ts9.getModifiers(node);
|
|
2857
|
+
return !!modifiers?.some((m) => m.kind === ts9.SyntaxKind.ExportKeyword);
|
|
2808
2858
|
}
|
|
2809
2859
|
function getJSDocFromNode(node) {
|
|
2810
|
-
const apiDocs =
|
|
2860
|
+
const apiDocs = ts9.getJSDocCommentsAndTags(node).filter((entry) => ts9.isJSDoc(entry));
|
|
2811
2861
|
if (apiDocs.length > 0) return apiDocs[0];
|
|
2812
2862
|
const directDocs = node.jsDoc;
|
|
2813
2863
|
if (directDocs && directDocs.length > 0) return directDocs[0];
|
|
@@ -2838,7 +2888,7 @@ var init_jsDocMetadata = __esm({
|
|
|
2838
2888
|
});
|
|
2839
2889
|
|
|
2840
2890
|
// src/ast/extractToolMetadata.ts
|
|
2841
|
-
import
|
|
2891
|
+
import ts10 from "typescript";
|
|
2842
2892
|
function extractToolMetadata(program, filePath, functionName, pathMeta) {
|
|
2843
2893
|
const sourceFile = program.getSourceFile(filePath);
|
|
2844
2894
|
if (!sourceFile) return null;
|
|
@@ -2859,18 +2909,18 @@ function extractToolMetadata(program, filePath, functionName, pathMeta) {
|
|
|
2859
2909
|
}
|
|
2860
2910
|
function findExportedFunction(sourceFile, functionName) {
|
|
2861
2911
|
let result = null;
|
|
2862
|
-
|
|
2912
|
+
ts10.forEachChild(sourceFile, (node) => {
|
|
2863
2913
|
if (result) return;
|
|
2864
|
-
if (
|
|
2914
|
+
if (ts10.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === functionName) {
|
|
2865
2915
|
result = { fn: node, jsDocOwner: node };
|
|
2866
2916
|
return;
|
|
2867
2917
|
}
|
|
2868
|
-
if (
|
|
2918
|
+
if (ts10.isVariableStatement(node) && hasExportModifier(node)) {
|
|
2869
2919
|
for (const decl of node.declarationList.declarations) {
|
|
2870
2920
|
if (result) break;
|
|
2871
|
-
const nameText =
|
|
2921
|
+
const nameText = ts10.isIdentifier(decl.name) ? decl.name.text : decl.name.getText(sourceFile);
|
|
2872
2922
|
if (nameText !== functionName || !decl.initializer) continue;
|
|
2873
|
-
if (
|
|
2923
|
+
if (ts10.isArrowFunction(decl.initializer) || ts10.isFunctionExpression(decl.initializer)) {
|
|
2874
2924
|
result = { fn: decl.initializer, jsDocOwner: node };
|
|
2875
2925
|
}
|
|
2876
2926
|
}
|
|
@@ -2882,7 +2932,7 @@ function getFirstParamTypeName(fn, sourceFile) {
|
|
|
2882
2932
|
const firstParam = fn.parameters[0];
|
|
2883
2933
|
if (!firstParam) return void 0;
|
|
2884
2934
|
if (!firstParam.type) return void 0;
|
|
2885
|
-
if (!
|
|
2935
|
+
if (!ts10.isTypeReferenceNode(firstParam.type)) return void 0;
|
|
2886
2936
|
return firstParam.type.typeName.getText(sourceFile);
|
|
2887
2937
|
}
|
|
2888
2938
|
var init_extractToolMetadata = __esm({
|
|
@@ -3092,7 +3142,7 @@ var init_scanAgents = __esm({
|
|
|
3092
3142
|
});
|
|
3093
3143
|
|
|
3094
3144
|
// src/ast/extractAgentMetadata.ts
|
|
3095
|
-
import
|
|
3145
|
+
import ts11 from "typescript";
|
|
3096
3146
|
function extractAgentMetadata(program, filePath, pathMeta) {
|
|
3097
3147
|
const sourceFile = program.getSourceFile(filePath);
|
|
3098
3148
|
if (!sourceFile) return null;
|
|
@@ -3136,23 +3186,23 @@ function extractAgentMetadata(program, filePath, pathMeta) {
|
|
|
3136
3186
|
}
|
|
3137
3187
|
function findConfigExport(sourceFile) {
|
|
3138
3188
|
let result = null;
|
|
3139
|
-
|
|
3189
|
+
ts11.forEachChild(sourceFile, (node) => {
|
|
3140
3190
|
if (result) return;
|
|
3141
|
-
if (
|
|
3191
|
+
if (ts11.isVariableStatement(node) && hasExportModifier(node)) {
|
|
3142
3192
|
for (const decl of node.declarationList.declarations) {
|
|
3143
3193
|
if (result) break;
|
|
3144
|
-
const nameText =
|
|
3194
|
+
const nameText = ts11.isIdentifier(decl.name) ? decl.name.text : "";
|
|
3145
3195
|
if (nameText !== "config" || !decl.initializer) continue;
|
|
3146
|
-
if (
|
|
3196
|
+
if (ts11.isObjectLiteralExpression(decl.initializer)) {
|
|
3147
3197
|
result = { jsDocOwner: node, objectLiteral: decl.initializer };
|
|
3148
|
-
} else if (
|
|
3198
|
+
} else if (ts11.isArrowFunction(decl.initializer)) {
|
|
3149
3199
|
result = { jsDocOwner: node, objectLiteral: getReturnObjectLiteral(decl.initializer) };
|
|
3150
3200
|
} else {
|
|
3151
3201
|
result = { jsDocOwner: node, objectLiteral: null };
|
|
3152
3202
|
}
|
|
3153
3203
|
}
|
|
3154
3204
|
}
|
|
3155
|
-
if (
|
|
3205
|
+
if (ts11.isFunctionDeclaration(node) && hasExportModifier(node) && node.name?.text === "config") {
|
|
3156
3206
|
const returnObj = getReturnObjectLiteral(node);
|
|
3157
3207
|
result = { jsDocOwner: node, objectLiteral: returnObj };
|
|
3158
3208
|
}
|
|
@@ -3162,12 +3212,12 @@ function findConfigExport(sourceFile) {
|
|
|
3162
3212
|
function getReturnObjectLiteral(fn) {
|
|
3163
3213
|
const body = fn.body;
|
|
3164
3214
|
if (!body) return null;
|
|
3165
|
-
if (
|
|
3215
|
+
if (ts11.isObjectLiteralExpression(body)) {
|
|
3166
3216
|
return body;
|
|
3167
3217
|
}
|
|
3168
|
-
if (
|
|
3218
|
+
if (ts11.isBlock(body)) {
|
|
3169
3219
|
for (const stmt of body.statements) {
|
|
3170
|
-
if (
|
|
3220
|
+
if (ts11.isReturnStatement(stmt) && stmt.expression && ts11.isObjectLiteralExpression(stmt.expression)) {
|
|
3171
3221
|
return stmt.expression;
|
|
3172
3222
|
}
|
|
3173
3223
|
}
|
|
@@ -3182,8 +3232,8 @@ function extractConfigFields(objLit, sourceFile) {
|
|
|
3182
3232
|
let maxTurns;
|
|
3183
3233
|
let inputDescription;
|
|
3184
3234
|
for (const prop of objLit.properties) {
|
|
3185
|
-
if (
|
|
3186
|
-
if (!
|
|
3235
|
+
if (ts11.isSpreadAssignment(prop)) continue;
|
|
3236
|
+
if (!ts11.isPropertyAssignment(prop)) {
|
|
3187
3237
|
throw SchemaExtractionError.at(
|
|
3188
3238
|
prop,
|
|
3189
3239
|
"config",
|
|
@@ -3234,13 +3284,13 @@ function extractConfigFields(objLit, sourceFile) {
|
|
|
3234
3284
|
return { systemPrompt, tools, agents, model, maxTurns, inputDescription };
|
|
3235
3285
|
}
|
|
3236
3286
|
function getPropertyName(name) {
|
|
3237
|
-
if (
|
|
3238
|
-
if (
|
|
3287
|
+
if (ts11.isIdentifier(name)) return name.text;
|
|
3288
|
+
if (ts11.isStringLiteral(name)) return name.text;
|
|
3239
3289
|
return null;
|
|
3240
3290
|
}
|
|
3241
3291
|
function extractStringValue(expr) {
|
|
3242
3292
|
if (isStringLikeLiteral(expr)) return expr.text;
|
|
3243
|
-
if (
|
|
3293
|
+
if (ts11.isBinaryExpression(expr) && expr.operatorToken.kind === ts11.SyntaxKind.PlusToken) {
|
|
3244
3294
|
const left = extractStringValue(expr.left);
|
|
3245
3295
|
if (left === void 0) return void 0;
|
|
3246
3296
|
const right = extractStringValue(expr.right);
|
|
@@ -3286,17 +3336,17 @@ function requireNumberValue(prop, fieldName, sourceFile) {
|
|
|
3286
3336
|
return value;
|
|
3287
3337
|
}
|
|
3288
3338
|
function extractNumberValue(expr) {
|
|
3289
|
-
if (
|
|
3339
|
+
if (ts11.isNumericLiteral(expr)) {
|
|
3290
3340
|
const num = Number(expr.text);
|
|
3291
3341
|
return Number.isNaN(num) ? void 0 : num;
|
|
3292
3342
|
}
|
|
3293
3343
|
return void 0;
|
|
3294
3344
|
}
|
|
3295
3345
|
function isStringLikeLiteral(node) {
|
|
3296
|
-
return
|
|
3346
|
+
return ts11.isStringLiteral(node) || ts11.isNoSubstitutionTemplateLiteral(node);
|
|
3297
3347
|
}
|
|
3298
3348
|
function extractStringArrayValue(expr) {
|
|
3299
|
-
if (!
|
|
3349
|
+
if (!ts11.isArrayLiteralExpression(expr)) return void 0;
|
|
3300
3350
|
const values = [];
|
|
3301
3351
|
for (const element of expr.elements) {
|
|
3302
3352
|
const value = extractStringValue(element);
|
|
@@ -3784,7 +3834,7 @@ function clearGeneratedSchemas() {
|
|
|
3784
3834
|
state.generatedSchemas.clear();
|
|
3785
3835
|
state.inFlightSchemaGenerations.clear();
|
|
3786
3836
|
}
|
|
3787
|
-
async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir, dist) {
|
|
3837
|
+
async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir, dist, wsRoutes) {
|
|
3788
3838
|
const inFlight2 = state.inFlightSchemaGenerations.get(schemaPath);
|
|
3789
3839
|
if (inFlight2) {
|
|
3790
3840
|
await inFlight2;
|
|
@@ -3803,13 +3853,15 @@ async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir,
|
|
|
3803
3853
|
return false;
|
|
3804
3854
|
}
|
|
3805
3855
|
const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);
|
|
3806
|
-
|
|
3856
|
+
const fileWsRoutes = (wsRoutes ?? []).filter((r) => r.filePath === routeFilePath);
|
|
3857
|
+
if (fileRoutes.length === 0 && fileWsRoutes.length === 0) {
|
|
3807
3858
|
return false;
|
|
3808
3859
|
}
|
|
3809
3860
|
const sourceRelPath = path20.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3810
3861
|
const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
|
|
3862
|
+
const sourceWsRoutes = fileWsRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
|
|
3811
3863
|
const generatePromise = (async () => {
|
|
3812
|
-
await generateSchemaFiles(sourceRoutes, rootDir, dist);
|
|
3864
|
+
await generateSchemaFiles(sourceRoutes, rootDir, dist, sourceWsRoutes);
|
|
3813
3865
|
state.generatedSchemas.add(schemaPath);
|
|
3814
3866
|
})();
|
|
3815
3867
|
state.inFlightSchemaGenerations.set(schemaPath, generatePromise);
|
|
@@ -6314,17 +6366,24 @@ function encodeSseEvent(event) {
|
|
|
6314
6366
|
out += "\n";
|
|
6315
6367
|
return out;
|
|
6316
6368
|
}
|
|
6317
|
-
function createSseWriter() {
|
|
6369
|
+
function createSseWriter(options = {}) {
|
|
6370
|
+
const { onFirstWrite } = options;
|
|
6318
6371
|
const encoder = new TextEncoder();
|
|
6319
6372
|
let controller = null;
|
|
6320
6373
|
let closed = false;
|
|
6321
6374
|
let aborted = false;
|
|
6375
|
+
let firstWriteTriggered = false;
|
|
6322
6376
|
let drainWaiters = [];
|
|
6323
6377
|
const resolveDrainWaiters = () => {
|
|
6324
6378
|
const waiters = drainWaiters;
|
|
6325
6379
|
drainWaiters = [];
|
|
6326
6380
|
for (const resolve3 of waiters) resolve3();
|
|
6327
6381
|
};
|
|
6382
|
+
const triggerFirstWrite = () => {
|
|
6383
|
+
if (firstWriteTriggered) return;
|
|
6384
|
+
firstWriteTriggered = true;
|
|
6385
|
+
onFirstWrite?.(response);
|
|
6386
|
+
};
|
|
6328
6387
|
const stream = new ReadableStream(
|
|
6329
6388
|
{
|
|
6330
6389
|
start(c) {
|
|
@@ -6358,11 +6417,13 @@ function createSseWriter() {
|
|
|
6358
6417
|
if (closed || !controller) return;
|
|
6359
6418
|
const text = encodeSseEvent(event);
|
|
6360
6419
|
controller.enqueue(encoder.encode(text));
|
|
6420
|
+
triggerFirstWrite();
|
|
6361
6421
|
},
|
|
6362
6422
|
sendRaw(chunk) {
|
|
6363
6423
|
if (closed || !controller) return;
|
|
6364
6424
|
const bytes = typeof chunk === "string" ? encoder.encode(chunk) : chunk;
|
|
6365
6425
|
controller.enqueue(bytes);
|
|
6426
|
+
triggerFirstWrite();
|
|
6366
6427
|
},
|
|
6367
6428
|
sendError(error) {
|
|
6368
6429
|
if (closed || !controller) return;
|
|
@@ -6370,6 +6431,7 @@ function createSseWriter() {
|
|
|
6370
6431
|
const text = encodeSseEvent({ event: "error", data: message });
|
|
6371
6432
|
try {
|
|
6372
6433
|
controller.enqueue(encoder.encode(text));
|
|
6434
|
+
triggerFirstWrite();
|
|
6373
6435
|
} finally {
|
|
6374
6436
|
writer.close();
|
|
6375
6437
|
}
|
|
@@ -6817,6 +6879,32 @@ var init_logger = __esm({
|
|
|
6817
6879
|
}
|
|
6818
6880
|
});
|
|
6819
6881
|
|
|
6882
|
+
// src/utils/queryToObject.ts
|
|
6883
|
+
function queryToObject(params) {
|
|
6884
|
+
const cached = queryCache.get(params);
|
|
6885
|
+
if (cached) return cached;
|
|
6886
|
+
const result = {};
|
|
6887
|
+
for (const [key, value] of params) {
|
|
6888
|
+
const existing = result[key];
|
|
6889
|
+
if (existing === void 0) {
|
|
6890
|
+
result[key] = value;
|
|
6891
|
+
} else if (Array.isArray(existing)) {
|
|
6892
|
+
existing.push(value);
|
|
6893
|
+
} else {
|
|
6894
|
+
result[key] = [existing, value];
|
|
6895
|
+
}
|
|
6896
|
+
}
|
|
6897
|
+
queryCache.set(params, result);
|
|
6898
|
+
return result;
|
|
6899
|
+
}
|
|
6900
|
+
var queryCache;
|
|
6901
|
+
var init_queryToObject = __esm({
|
|
6902
|
+
"src/utils/queryToObject.ts"() {
|
|
6903
|
+
"use strict";
|
|
6904
|
+
queryCache = /* @__PURE__ */ new WeakMap();
|
|
6905
|
+
}
|
|
6906
|
+
});
|
|
6907
|
+
|
|
6820
6908
|
// src/runtime/createContext.ts
|
|
6821
6909
|
function resolveRequestId(request) {
|
|
6822
6910
|
const header = request.headers.get("x-request-id");
|
|
@@ -6866,7 +6954,11 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
6866
6954
|
log,
|
|
6867
6955
|
request,
|
|
6868
6956
|
params,
|
|
6869
|
-
|
|
6957
|
+
rawParams: params,
|
|
6958
|
+
// ctx.query 为对象口径(createContext 挂原始 string 值对象;handler 声明
|
|
6959
|
+
// query 类型时管线校验后替换为转换值)。原始 URLSearchParams 恒在 rawQuery。
|
|
6960
|
+
query: queryToObject(url.searchParams),
|
|
6961
|
+
rawQuery: url.searchParams,
|
|
6870
6962
|
headers: request.headers,
|
|
6871
6963
|
method: request.method,
|
|
6872
6964
|
path: url.pathname,
|
|
@@ -6914,13 +7006,19 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
6914
7006
|
/**
|
|
6915
7007
|
* 创建 SSE writer,用于流式推送事件
|
|
6916
7008
|
*
|
|
6917
|
-
* handler 调用此方法后,通过返回的 writer
|
|
6918
|
-
*
|
|
7009
|
+
* handler 调用此方法后,通过返回的 writer 推送事件。HTTP 服务场景下首次
|
|
7010
|
+
* 写入(send/sendRaw/sendError)即触发 server 层提前接管钩子,把 writer.response
|
|
7011
|
+
* 接入底层连接——流式期间字节即产即达(见 createServer.md「SSE 提前接管」)。
|
|
7012
|
+
* 无钩子场景(app.inject / 测试直调 invokeHandler)回落为 handler 返回后交出。
|
|
6919
7013
|
*
|
|
6920
7014
|
* 与 ctx.json / ctx.html 互斥:一个 handler 只能用一种响应方式。
|
|
6921
7015
|
*/
|
|
6922
7016
|
sse() {
|
|
6923
|
-
const writer = createSseWriter(
|
|
7017
|
+
const writer = createSseWriter({
|
|
7018
|
+
onFirstWrite: (response) => {
|
|
7019
|
+
ctx.__earlyRespond?.(response);
|
|
7020
|
+
}
|
|
7021
|
+
});
|
|
6924
7022
|
const ctxWithSse = ctx;
|
|
6925
7023
|
ctxWithSse.__sseResponse = writer.response;
|
|
6926
7024
|
ctxWithSse.__sseWriter = writer;
|
|
@@ -6972,32 +7070,7 @@ var init_createContext = __esm({
|
|
|
6972
7070
|
init_sse();
|
|
6973
7071
|
init_responseFormatter();
|
|
6974
7072
|
init_logger();
|
|
6975
|
-
|
|
6976
|
-
});
|
|
6977
|
-
|
|
6978
|
-
// src/utils/queryToObject.ts
|
|
6979
|
-
function queryToObject(params) {
|
|
6980
|
-
const cached = queryCache.get(params);
|
|
6981
|
-
if (cached) return cached;
|
|
6982
|
-
const result = {};
|
|
6983
|
-
for (const [key, value] of params) {
|
|
6984
|
-
const existing = result[key];
|
|
6985
|
-
if (existing === void 0) {
|
|
6986
|
-
result[key] = value;
|
|
6987
|
-
} else if (Array.isArray(existing)) {
|
|
6988
|
-
existing.push(value);
|
|
6989
|
-
} else {
|
|
6990
|
-
result[key] = [existing, value];
|
|
6991
|
-
}
|
|
6992
|
-
}
|
|
6993
|
-
queryCache.set(params, result);
|
|
6994
|
-
return result;
|
|
6995
|
-
}
|
|
6996
|
-
var queryCache;
|
|
6997
|
-
var init_queryToObject = __esm({
|
|
6998
|
-
"src/utils/queryToObject.ts"() {
|
|
6999
|
-
"use strict";
|
|
7000
|
-
queryCache = /* @__PURE__ */ new WeakMap();
|
|
7073
|
+
init_queryToObject();
|
|
7001
7074
|
}
|
|
7002
7075
|
});
|
|
7003
7076
|
|
|
@@ -7055,46 +7128,29 @@ var init_parseMultipart = __esm({
|
|
|
7055
7128
|
async function resolveInputFromUrl(method, request, url) {
|
|
7056
7129
|
const inputType = getInputTypeForMethod(method);
|
|
7057
7130
|
if (inputType === "body") {
|
|
7058
|
-
|
|
7059
|
-
if (contentType.includes("multipart/form-data")) {
|
|
7060
|
-
return parseMultipart(request);
|
|
7061
|
-
}
|
|
7062
|
-
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
7063
|
-
const text2 = await request.text();
|
|
7064
|
-
if (isBlankText(text2)) return null;
|
|
7065
|
-
const params = new URLSearchParams(text2);
|
|
7066
|
-
const obj = {};
|
|
7067
|
-
for (const [key, value] of params) {
|
|
7068
|
-
obj[key] = value;
|
|
7069
|
-
}
|
|
7070
|
-
return obj;
|
|
7071
|
-
}
|
|
7072
|
-
const text = await request.text();
|
|
7073
|
-
if (isBlankText(text)) {
|
|
7074
|
-
return null;
|
|
7075
|
-
}
|
|
7076
|
-
const result = parseJsonBody(text);
|
|
7077
|
-
if (!result.success) {
|
|
7078
|
-
throw new ValidationError("\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5\u7684 JSON", [
|
|
7079
|
-
{
|
|
7080
|
-
path: "body",
|
|
7081
|
-
code: "INVALID_FORMAT",
|
|
7082
|
-
expected: "JSON",
|
|
7083
|
-
received: "text",
|
|
7084
|
-
message: "\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5\u7684 JSON"
|
|
7085
|
-
}
|
|
7086
|
-
]);
|
|
7087
|
-
}
|
|
7088
|
-
return result.data;
|
|
7131
|
+
return parseBodyByContentType(request);
|
|
7089
7132
|
}
|
|
7090
|
-
return queryToObject(url.searchParams);
|
|
7091
|
-
}
|
|
7092
|
-
function isBlankText(text) {
|
|
7093
|
-
return text.length === 0 || !/\S/.test(text);
|
|
7133
|
+
return { input: queryToObject(url.searchParams) };
|
|
7094
7134
|
}
|
|
7095
|
-
async function
|
|
7135
|
+
async function parseBodyByContentType(request) {
|
|
7136
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
7137
|
+
if (contentType.includes("multipart/form-data")) {
|
|
7138
|
+
return { input: await parseMultipart(request) };
|
|
7139
|
+
}
|
|
7140
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
7141
|
+
const text2 = await request.text();
|
|
7142
|
+
if (isBlankText(text2)) return { input: null };
|
|
7143
|
+
const params = new URLSearchParams(text2);
|
|
7144
|
+
const obj = {};
|
|
7145
|
+
for (const [key, value] of params) {
|
|
7146
|
+
obj[key] = value;
|
|
7147
|
+
}
|
|
7148
|
+
return { input: obj, rawBody: text2 };
|
|
7149
|
+
}
|
|
7096
7150
|
const text = await request.text();
|
|
7097
|
-
if (isBlankText(text))
|
|
7151
|
+
if (isBlankText(text)) {
|
|
7152
|
+
return { input: null };
|
|
7153
|
+
}
|
|
7098
7154
|
const result = parseJsonBody(text);
|
|
7099
7155
|
if (!result.success) {
|
|
7100
7156
|
throw new ValidationError("\u8BF7\u6C42\u4F53\u4E0D\u662F\u5408\u6CD5\u7684 JSON", [
|
|
@@ -7107,7 +7163,13 @@ async function resolveBodyForQueryMethod(request) {
|
|
|
7107
7163
|
}
|
|
7108
7164
|
]);
|
|
7109
7165
|
}
|
|
7110
|
-
return result.data;
|
|
7166
|
+
return { input: result.data, rawBody: text };
|
|
7167
|
+
}
|
|
7168
|
+
function isBlankText(text) {
|
|
7169
|
+
return text.length === 0 || !/\S/.test(text);
|
|
7170
|
+
}
|
|
7171
|
+
async function resolveBodyForQueryMethod(request) {
|
|
7172
|
+
return parseBodyByContentType(request);
|
|
7111
7173
|
}
|
|
7112
7174
|
var init_resolveInput = __esm({
|
|
7113
7175
|
"src/runtime/resolveInput.ts"() {
|
|
@@ -7505,12 +7567,17 @@ var init_agentHandle = __esm({
|
|
|
7505
7567
|
// src/injection/injectParams.ts
|
|
7506
7568
|
function getBuiltinInjectionValue(type, ctx, body) {
|
|
7507
7569
|
switch (type) {
|
|
7508
|
-
case "query":
|
|
7509
|
-
|
|
7510
|
-
return validated ?? queryToObject(ctx.query);
|
|
7511
|
-
}
|
|
7570
|
+
case "query":
|
|
7571
|
+
return ctx.query;
|
|
7512
7572
|
case "params":
|
|
7513
7573
|
return ctx.params;
|
|
7574
|
+
// raw 系:恒原始,管线永不覆盖(ctx 字段即注入值)
|
|
7575
|
+
case "rawQuery":
|
|
7576
|
+
return ctx.rawQuery;
|
|
7577
|
+
case "rawParams":
|
|
7578
|
+
return ctx.rawParams;
|
|
7579
|
+
case "rawBody":
|
|
7580
|
+
return ctx.rawBody;
|
|
7514
7581
|
case "headers":
|
|
7515
7582
|
return ctx.headers;
|
|
7516
7583
|
case "context":
|
|
@@ -7576,7 +7643,6 @@ var init_injectParams = __esm({
|
|
|
7576
7643
|
"src/injection/injectParams.ts"() {
|
|
7577
7644
|
"use strict";
|
|
7578
7645
|
init_resolveInjection();
|
|
7579
|
-
init_queryToObject();
|
|
7580
7646
|
init_agentRegistry();
|
|
7581
7647
|
init_agentHandle();
|
|
7582
7648
|
}
|
|
@@ -8424,6 +8490,7 @@ function attachWebSocket(options) {
|
|
|
8424
8490
|
server,
|
|
8425
8491
|
routesRef,
|
|
8426
8492
|
rootDir,
|
|
8493
|
+
dist,
|
|
8427
8494
|
config,
|
|
8428
8495
|
globalMiddlewares,
|
|
8429
8496
|
trustedProxy = false,
|
|
@@ -8458,12 +8525,50 @@ function attachWebSocket(options) {
|
|
|
8458
8525
|
const { route, params } = match;
|
|
8459
8526
|
const headers = nodeHttpToWebHeaders(req);
|
|
8460
8527
|
const host = req.headers.host ?? "localhost";
|
|
8461
|
-
const
|
|
8462
|
-
const request = new Request(
|
|
8528
|
+
const fullUrl = new URL(`http://${host}${req.url ?? "/"}`);
|
|
8529
|
+
const request = new Request(fullUrl.toString(), { method: "GET", headers });
|
|
8463
8530
|
const ctx = createContext(request, params, config, getClientIp(req, trustedProxy), registries);
|
|
8464
8531
|
const meta = ctx.meta;
|
|
8465
8532
|
let upgraded = false;
|
|
8466
8533
|
const finalHandler = async () => {
|
|
8534
|
+
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
8535
|
+
if (fs19.existsSync(schemaPath)) {
|
|
8536
|
+
if (isDevOnDemandEnabled()) {
|
|
8537
|
+
const devDist = getDevDist();
|
|
8538
|
+
if (devDist) {
|
|
8539
|
+
await ensureSchemaGenerated(
|
|
8540
|
+
schemaPath,
|
|
8541
|
+
route.filePath,
|
|
8542
|
+
routesRef.current,
|
|
8543
|
+
rootDir,
|
|
8544
|
+
dist,
|
|
8545
|
+
routesRef.wsCurrent
|
|
8546
|
+
);
|
|
8547
|
+
}
|
|
8548
|
+
}
|
|
8549
|
+
const searchParams = fullUrl.searchParams;
|
|
8550
|
+
const rawQueryObject = queryToObject(searchParams);
|
|
8551
|
+
const paramsResult = await validateInput(schemaPath, "WS", "params", params);
|
|
8552
|
+
if (!paramsResult.valid) {
|
|
8553
|
+
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", paramsResult.issues);
|
|
8554
|
+
}
|
|
8555
|
+
if (paramsResult.data !== params && typeof paramsResult.data === "object" && paramsResult.data !== null) {
|
|
8556
|
+
ctx.params = {
|
|
8557
|
+
...params,
|
|
8558
|
+
...paramsResult.data
|
|
8559
|
+
};
|
|
8560
|
+
}
|
|
8561
|
+
const queryResult = await validateInput(schemaPath, "WS", "query", rawQueryObject);
|
|
8562
|
+
if (!queryResult.valid) {
|
|
8563
|
+
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", queryResult.issues);
|
|
8564
|
+
}
|
|
8565
|
+
if (queryResult.data !== rawQueryObject && typeof queryResult.data === "object" && queryResult.data !== null) {
|
|
8566
|
+
ctx.query = {
|
|
8567
|
+
...rawQueryObject,
|
|
8568
|
+
...queryResult.data
|
|
8569
|
+
};
|
|
8570
|
+
}
|
|
8571
|
+
}
|
|
8467
8572
|
let handlers;
|
|
8468
8573
|
try {
|
|
8469
8574
|
const absoluteFilePath = path25.resolve(rootDir, route.filePath);
|
|
@@ -8527,7 +8632,11 @@ var init_handleWsUpgrade = __esm({
|
|
|
8527
8632
|
init_invokeHandler();
|
|
8528
8633
|
init_importWithCacheBust();
|
|
8529
8634
|
init_getClientIp();
|
|
8635
|
+
init_queryToObject();
|
|
8530
8636
|
init_serverUtils();
|
|
8637
|
+
init_validateInput();
|
|
8638
|
+
init_httpErrors();
|
|
8639
|
+
init_generateSchemaFiles();
|
|
8531
8640
|
init_compileOnDemand();
|
|
8532
8641
|
init_loadMiddlewares();
|
|
8533
8642
|
init_pendingMeta();
|
|
@@ -8691,7 +8800,8 @@ function createServer(options) {
|
|
|
8691
8800
|
globalInjectors,
|
|
8692
8801
|
bodyLimit,
|
|
8693
8802
|
trustedProxy,
|
|
8694
|
-
registries
|
|
8803
|
+
registries,
|
|
8804
|
+
routesRef.wsCurrent
|
|
8695
8805
|
).catch((err) => {
|
|
8696
8806
|
console.error("[faapi] Request pipeline failed after error response attempt:", err);
|
|
8697
8807
|
try {
|
|
@@ -8709,6 +8819,7 @@ function createServer(options) {
|
|
|
8709
8819
|
server,
|
|
8710
8820
|
routesRef,
|
|
8711
8821
|
rootDir,
|
|
8822
|
+
dist,
|
|
8712
8823
|
config,
|
|
8713
8824
|
globalMiddlewares,
|
|
8714
8825
|
trustedProxy,
|
|
@@ -8716,14 +8827,13 @@ function createServer(options) {
|
|
|
8716
8827
|
});
|
|
8717
8828
|
return { server, routesRef };
|
|
8718
8829
|
}
|
|
8719
|
-
function prepareRequest(req,
|
|
8720
|
-
const { request, url } = toWebRequest(req, bodyLimit, requestSignal);
|
|
8830
|
+
function prepareRequest(req, request, url, config, trustedProxy, registries, preMatched) {
|
|
8721
8831
|
const method = request.method.toUpperCase();
|
|
8722
8832
|
const urlPath = url.pathname;
|
|
8723
8833
|
const ctx = createContextFromUrl(
|
|
8724
8834
|
request,
|
|
8725
8835
|
url,
|
|
8726
|
-
{},
|
|
8836
|
+
preMatched?.params ?? {},
|
|
8727
8837
|
config,
|
|
8728
8838
|
getClientIp(req, trustedProxy),
|
|
8729
8839
|
registries
|
|
@@ -8752,19 +8862,33 @@ function getRoutePaths(route, rootDir, dist) {
|
|
|
8752
8862
|
return cached;
|
|
8753
8863
|
}
|
|
8754
8864
|
function createRoutePipeline(opts) {
|
|
8755
|
-
const {
|
|
8865
|
+
const {
|
|
8866
|
+
routes,
|
|
8867
|
+
method,
|
|
8868
|
+
urlPath,
|
|
8869
|
+
url,
|
|
8870
|
+
ctx,
|
|
8871
|
+
request,
|
|
8872
|
+
rootDir,
|
|
8873
|
+
dist,
|
|
8874
|
+
globalInjectors,
|
|
8875
|
+
preMatched,
|
|
8876
|
+
wsRoutes
|
|
8877
|
+
} = opts;
|
|
8756
8878
|
return async () => {
|
|
8757
|
-
const match = resolveRouteOrThrow(routes, method, urlPath);
|
|
8758
|
-
ctx.params = match.params;
|
|
8879
|
+
const match = preMatched ?? resolveRouteOrThrow(routes, method, urlPath);
|
|
8759
8880
|
const { route } = match;
|
|
8760
8881
|
const { absFilePath, schemaPath } = getRoutePaths(route, rootDir, dist);
|
|
8761
8882
|
const routeModule = await loadRouteModule(absFilePath, route.method, rootDir);
|
|
8762
|
-
const input = await resolveInputFromUrl(route.method, request, url);
|
|
8883
|
+
const { input, rawBody } = await resolveInputFromUrl(route.method, request, url);
|
|
8884
|
+
if (rawBody !== void 0) {
|
|
8885
|
+
ctx.rawBody = rawBody;
|
|
8886
|
+
}
|
|
8763
8887
|
const inputType = getInputTypeForMethod(route.method);
|
|
8764
8888
|
if (isDevOnDemandEnabled()) {
|
|
8765
8889
|
const devDist = getDevDist();
|
|
8766
8890
|
if (devDist) {
|
|
8767
|
-
await ensureSchemaGenerated(schemaPath, route.filePath, routes, rootDir, dist);
|
|
8891
|
+
await ensureSchemaGenerated(schemaPath, route.filePath, routes, rootDir, dist, wsRoutes);
|
|
8768
8892
|
}
|
|
8769
8893
|
}
|
|
8770
8894
|
const result = await validateInput(schemaPath, route.method, inputType, input);
|
|
@@ -8772,7 +8896,7 @@ function createRoutePipeline(opts) {
|
|
|
8772
8896
|
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", result.issues);
|
|
8773
8897
|
}
|
|
8774
8898
|
if (inputType === "query" && result.data !== input && typeof result.data === "object" && result.data !== null) {
|
|
8775
|
-
ctx.
|
|
8899
|
+
ctx.query = {
|
|
8776
8900
|
...input,
|
|
8777
8901
|
...result.data
|
|
8778
8902
|
};
|
|
@@ -8794,7 +8918,7 @@ function createRoutePipeline(opts) {
|
|
|
8794
8918
|
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", queryResult.issues);
|
|
8795
8919
|
}
|
|
8796
8920
|
if (queryResult.data !== rawQuery && typeof queryResult.data === "object" && queryResult.data !== null) {
|
|
8797
|
-
ctx.
|
|
8921
|
+
ctx.query = {
|
|
8798
8922
|
...rawQuery,
|
|
8799
8923
|
...queryResult.data
|
|
8800
8924
|
};
|
|
@@ -8803,16 +8927,20 @@ function createRoutePipeline(opts) {
|
|
|
8803
8927
|
let body;
|
|
8804
8928
|
if (inputType === "query" && hasBody(route.method)) {
|
|
8805
8929
|
const parsed = await resolveBodyForQueryMethod(request);
|
|
8806
|
-
if (parsed !== void 0) {
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8930
|
+
if (parsed.rawBody !== void 0) {
|
|
8931
|
+
ctx.rawBody = parsed.rawBody;
|
|
8932
|
+
}
|
|
8933
|
+
const bodyResult = await validateInput(schemaPath, route.method, "body", parsed.input);
|
|
8934
|
+
if (!bodyResult.valid) {
|
|
8935
|
+
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", bodyResult.issues);
|
|
8812
8936
|
}
|
|
8937
|
+
body = bodyResult.data;
|
|
8813
8938
|
} else if (hasBody(route.method)) {
|
|
8814
8939
|
body = result.data;
|
|
8815
8940
|
}
|
|
8941
|
+
if (body !== void 0 && body !== null) {
|
|
8942
|
+
ctx.body = body;
|
|
8943
|
+
}
|
|
8816
8944
|
if (route.middlewares === void 0 && route.injectors === void 0 && route.middlewarePaths) {
|
|
8817
8945
|
await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);
|
|
8818
8946
|
const bundle = await loadMergedMiddlewares(route.middlewarePaths);
|
|
@@ -8848,25 +8976,37 @@ async function sendErrorResponse(err, meta, res, onError, ctx) {
|
|
|
8848
8976
|
}
|
|
8849
8977
|
}
|
|
8850
8978
|
}
|
|
8851
|
-
async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries) {
|
|
8979
|
+
async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries, wsRoutes) {
|
|
8852
8980
|
let meta = { headers: {}, setCookies: [] };
|
|
8853
8981
|
let ctx;
|
|
8982
|
+
let sseEarlySent = false;
|
|
8854
8983
|
const abortController = new AbortController();
|
|
8855
8984
|
res.on("close", () => {
|
|
8856
8985
|
if (!res.writableEnded) abortController.abort();
|
|
8857
8986
|
});
|
|
8858
8987
|
try {
|
|
8988
|
+
const { request, url } = toWebRequest(req, bodyLimit, abortController.signal);
|
|
8989
|
+
const method = request.method.toUpperCase();
|
|
8990
|
+
const urlPath = url.pathname;
|
|
8991
|
+
const preMatched = matchRoute(routes, method, urlPath);
|
|
8859
8992
|
const prepared = prepareRequest(
|
|
8860
8993
|
req,
|
|
8994
|
+
request,
|
|
8995
|
+
url,
|
|
8861
8996
|
config,
|
|
8862
|
-
bodyLimit,
|
|
8863
8997
|
trustedProxy,
|
|
8864
8998
|
registries,
|
|
8865
|
-
|
|
8999
|
+
preMatched
|
|
8866
9000
|
);
|
|
8867
9001
|
ctx = prepared.ctx;
|
|
8868
9002
|
meta = prepared.meta;
|
|
8869
|
-
|
|
9003
|
+
ctx.__earlyRespond = (response2) => {
|
|
9004
|
+
if (sseEarlySent) return;
|
|
9005
|
+
sseEarlySent = true;
|
|
9006
|
+
void sendNodeResponse(mergeMeta(response2, meta), res).catch(() => {
|
|
9007
|
+
if (!res.writableEnded) res.destroy();
|
|
9008
|
+
});
|
|
9009
|
+
};
|
|
8870
9010
|
const routePipeline = createRoutePipeline({
|
|
8871
9011
|
routes,
|
|
8872
9012
|
method,
|
|
@@ -8876,12 +9016,25 @@ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares,
|
|
|
8876
9016
|
request,
|
|
8877
9017
|
rootDir,
|
|
8878
9018
|
dist,
|
|
8879
|
-
globalInjectors
|
|
9019
|
+
globalInjectors,
|
|
9020
|
+
preMatched,
|
|
9021
|
+
wsRoutes
|
|
8880
9022
|
});
|
|
8881
9023
|
const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
|
|
8882
|
-
|
|
9024
|
+
if (!sseEarlySent) {
|
|
9025
|
+
await sendSuccessResponse(response, res);
|
|
9026
|
+
}
|
|
8883
9027
|
} catch (err) {
|
|
8884
9028
|
if (res.destroyed || res.writableEnded) return;
|
|
9029
|
+
if (sseEarlySent) {
|
|
9030
|
+
if (onError && ctx) {
|
|
9031
|
+
try {
|
|
9032
|
+
await onError(err, ctx);
|
|
9033
|
+
} catch {
|
|
9034
|
+
}
|
|
9035
|
+
}
|
|
9036
|
+
return;
|
|
9037
|
+
}
|
|
8885
9038
|
if (err instanceof PayloadTooLargeError) {
|
|
8886
9039
|
res.setHeader("Connection", "close");
|
|
8887
9040
|
res.once("finish", () => req.destroy());
|
|
@@ -10309,14 +10462,14 @@ async function createDevApp(options) {
|
|
|
10309
10462
|
void (async () => {
|
|
10310
10463
|
try {
|
|
10311
10464
|
const { generateSchemaFiles: generateSchemaFiles2 } = await Promise.resolve().then(() => (init_generateSchemaFiles(), generateSchemaFiles_exports));
|
|
10312
|
-
await generateSchemaFiles2(sorted, ctx.rootDir, ctx.dist);
|
|
10465
|
+
await generateSchemaFiles2(sorted, ctx.rootDir, ctx.dist, reScanned.wsRoutes);
|
|
10313
10466
|
} catch (err) {
|
|
10314
10467
|
console.error("[faapi] Background schema regeneration failed:", err);
|
|
10315
10468
|
}
|
|
10316
10469
|
})();
|
|
10317
10470
|
} else {
|
|
10318
10471
|
const { generateSchemaFiles: generateSchemaFiles2 } = await Promise.resolve().then(() => (init_generateSchemaFiles(), generateSchemaFiles_exports));
|
|
10319
|
-
await generateSchemaFiles2(sorted, ctx.rootDir, ctx.dist);
|
|
10472
|
+
await generateSchemaFiles2(sorted, ctx.rootDir, ctx.dist, reScanned.wsRoutes);
|
|
10320
10473
|
}
|
|
10321
10474
|
ctx.updateRoutes(sorted, reScanned.wsRoutes);
|
|
10322
10475
|
};
|
|
@@ -10551,7 +10704,7 @@ async function buildCommand(options) {
|
|
|
10551
10704
|
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
10552
10705
|
reportRouteConflicts(sorted);
|
|
10553
10706
|
console.log("\n[4/8] Generating schema...");
|
|
10554
|
-
await generateSchemaFiles(sorted, rootDir, outdir);
|
|
10707
|
+
await generateSchemaFiles(sorted, rootDir, outdir, wsRoutes);
|
|
10555
10708
|
console.log(` Schema: zod.js files under ${path33.resolve(rootDir, outdir)}`);
|
|
10556
10709
|
console.log("\n[5/8] Generating routes manifest...");
|
|
10557
10710
|
const routesPath = path33.resolve(rootDir, outdir, "faapi-routes.js");
|