@faapi/faapi 1.2.0 → 1.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 +117 -76
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +128 -1
- package/dist/index.js +318 -274
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -545,9 +545,14 @@ function getVitestImportActual() {
|
|
|
545
545
|
if (typeof vi?.importActual !== "function") return void 0;
|
|
546
546
|
return vi.importActual.bind(vi);
|
|
547
547
|
}
|
|
548
|
-
async function importWithCacheBust(filePath) {
|
|
548
|
+
async function importWithCacheBust(filePath, bustViteCache = false) {
|
|
549
549
|
const importActual = getVitestImportActual();
|
|
550
550
|
if (importActual) {
|
|
551
|
+
if (bustViteCache) {
|
|
552
|
+
let url2 = pathToFileURL(filePath).href;
|
|
553
|
+
url2 += `?t=${Date.now()}`;
|
|
554
|
+
return await import(url2);
|
|
555
|
+
}
|
|
551
556
|
return await importActual(filePath);
|
|
552
557
|
}
|
|
553
558
|
let url = pathToFileURL(filePath).href;
|
|
@@ -4324,31 +4329,28 @@ var init_compileOnDemand = __esm({
|
|
|
4324
4329
|
});
|
|
4325
4330
|
|
|
4326
4331
|
// src/loader/loadRouteModule.ts
|
|
4332
|
+
import fs11 from "fs";
|
|
4327
4333
|
async function loadRouteModule(filePath, method, rootDir) {
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
const dist = getDevDist();
|
|
4334
|
-
if (dist) {
|
|
4335
|
-
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
4334
|
+
if (isDevOnDemandEnabled() && rootDir) {
|
|
4335
|
+
const dist = getDevDist();
|
|
4336
|
+
if (dist) {
|
|
4337
|
+
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
4338
|
+
if (sourcePath && fs11.existsSync(sourcePath)) {
|
|
4336
4339
|
try {
|
|
4337
|
-
|
|
4338
|
-
if (compiled) {
|
|
4339
|
-
module = await importWithCacheBust(filePath);
|
|
4340
|
-
const handler2 = resolveExport(module, method);
|
|
4341
|
-
validateRouteModule(handler2, method, filePath);
|
|
4342
|
-
return { handler: handler2, method };
|
|
4343
|
-
}
|
|
4340
|
+
await ensureCompiled(sourcePath, rootDir, dist);
|
|
4344
4341
|
} catch (compileErr) {
|
|
4345
|
-
const
|
|
4346
|
-
throw new Error(`Failed to compile route module "${sourcePath}": ${
|
|
4342
|
+
const reason = compileErr instanceof Error ? compileErr.message : String(compileErr);
|
|
4343
|
+
throw new Error(`Failed to compile route module "${sourcePath}": ${reason}`, {
|
|
4347
4344
|
cause: compileErr
|
|
4348
4345
|
});
|
|
4349
4346
|
}
|
|
4350
4347
|
}
|
|
4351
4348
|
}
|
|
4349
|
+
}
|
|
4350
|
+
let module;
|
|
4351
|
+
try {
|
|
4352
|
+
module = await importWithCacheBust(filePath, isDevOnDemandEnabled());
|
|
4353
|
+
} catch (err) {
|
|
4352
4354
|
const reason = err instanceof Error ? err.message : String(err);
|
|
4353
4355
|
throw new Error(`Failed to load route module "${filePath}": ${reason}`, { cause: err });
|
|
4354
4356
|
}
|
|
@@ -4572,6 +4574,41 @@ function createContext(request, params, config = {}, ip = "") {
|
|
|
4572
4574
|
ctxWithSse.__sseResponse = writer.response;
|
|
4573
4575
|
ctxWithSse.__sseWriter = writer;
|
|
4574
4576
|
return writer;
|
|
4577
|
+
},
|
|
4578
|
+
/**
|
|
4579
|
+
* 显式包装成功响应(返回 Response,不会被自动包裹再次包装)
|
|
4580
|
+
*
|
|
4581
|
+
* 用 config.response.ok(或默认 (data) => ({ data })) 包裹 data 并返回 JSON Response。
|
|
4582
|
+
* handler 也可直接 return data,框架会自动用 ok 包裹,两者等价。
|
|
4583
|
+
*/
|
|
4584
|
+
ok(data) {
|
|
4585
|
+
const responseConfig = config.response;
|
|
4586
|
+
const okFn = responseConfig?.ok ?? ((d) => ({ data: d }));
|
|
4587
|
+
const body = okFn(data);
|
|
4588
|
+
return ctx.json(body);
|
|
4589
|
+
},
|
|
4590
|
+
/**
|
|
4591
|
+
* 返回错误响应(对象形式参数,status 和 code 均可省略)
|
|
4592
|
+
*
|
|
4593
|
+
* - status 省略时 HTTP 状态码默认 500
|
|
4594
|
+
* - code 省略时响应 body 里不含 code 字段(默认 fail 函数只放非 undefined 的字段)
|
|
4595
|
+
* - status 和 code 独立无关联
|
|
4596
|
+
*
|
|
4597
|
+
* body 用 config.response.fail(或默认实现)包装。
|
|
4598
|
+
*/
|
|
4599
|
+
fail(options) {
|
|
4600
|
+
const responseConfig = config.response;
|
|
4601
|
+
const failFn = responseConfig?.fail ?? ((e) => {
|
|
4602
|
+
const error = { message: e.message };
|
|
4603
|
+
if (e.code !== void 0) error.code = e.code;
|
|
4604
|
+
return { error };
|
|
4605
|
+
});
|
|
4606
|
+
const body = failFn({
|
|
4607
|
+
status: options.status,
|
|
4608
|
+
code: options.code,
|
|
4609
|
+
message: options.message
|
|
4610
|
+
});
|
|
4611
|
+
return ctx.json(body, options.status ?? 500);
|
|
4575
4612
|
}
|
|
4576
4613
|
};
|
|
4577
4614
|
const extend = config?.extendContext;
|
|
@@ -4936,6 +4973,12 @@ var init_injectParams = __esm({
|
|
|
4936
4973
|
});
|
|
4937
4974
|
|
|
4938
4975
|
// src/runtime/invokeHandler.ts
|
|
4976
|
+
function wrapResult(result, ctx) {
|
|
4977
|
+
if (result instanceof Response) return result;
|
|
4978
|
+
const responseConfig = ctx.config.response;
|
|
4979
|
+
const okFn = responseConfig?.ok ?? ((d) => ({ data: d }));
|
|
4980
|
+
return okFn(result);
|
|
4981
|
+
}
|
|
4939
4982
|
function mergeMeta(response, meta) {
|
|
4940
4983
|
const hasMeta = meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;
|
|
4941
4984
|
if (!hasMeta) return response;
|
|
@@ -5000,7 +5043,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
5000
5043
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
5001
5044
|
const sseResponse = pickSseAndAutoClose();
|
|
5002
5045
|
if (sseResponse) return sseResponse;
|
|
5003
|
-
return toResponse(result, meta);
|
|
5046
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
5004
5047
|
} catch (err) {
|
|
5005
5048
|
autoCloseSseOnError();
|
|
5006
5049
|
throw err;
|
|
@@ -5011,7 +5054,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
5011
5054
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
5012
5055
|
const sseResponse = pickSseAndAutoClose();
|
|
5013
5056
|
if (sseResponse) return sseResponse;
|
|
5014
|
-
return toResponse(result, meta);
|
|
5057
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
5015
5058
|
} catch (err) {
|
|
5016
5059
|
autoCloseSseOnError();
|
|
5017
5060
|
throw err;
|
|
@@ -5063,7 +5106,7 @@ function invalidateSchemaCache() {
|
|
|
5063
5106
|
async function loadSchemaModule(schemaPath) {
|
|
5064
5107
|
let mod = moduleCache.get(schemaPath);
|
|
5065
5108
|
if (!mod) {
|
|
5066
|
-
mod = await importWithCacheBust(schemaPath);
|
|
5109
|
+
mod = await importWithCacheBust(schemaPath, isDevOnDemandEnabled());
|
|
5067
5110
|
moduleCache.set(schemaPath, mod);
|
|
5068
5111
|
}
|
|
5069
5112
|
return mod;
|
|
@@ -5148,6 +5191,7 @@ var init_validateInput = __esm({
|
|
|
5148
5191
|
init_schemaName();
|
|
5149
5192
|
init_httpErrors();
|
|
5150
5193
|
init_importWithCacheBust();
|
|
5194
|
+
init_compileOnDemand();
|
|
5151
5195
|
moduleCache = /* @__PURE__ */ new Map();
|
|
5152
5196
|
}
|
|
5153
5197
|
});
|
|
@@ -5459,6 +5503,7 @@ var init_wsHandler = __esm({
|
|
|
5459
5503
|
});
|
|
5460
5504
|
|
|
5461
5505
|
// src/server/handleWsUpgrade.ts
|
|
5506
|
+
import fs12 from "fs";
|
|
5462
5507
|
import { WebSocketServer, WebSocket } from "ws";
|
|
5463
5508
|
import path13 from "path";
|
|
5464
5509
|
function getPathname(req) {
|
|
@@ -5467,24 +5512,16 @@ function getPathname(req) {
|
|
|
5467
5512
|
return idx >= 0 ? url.slice(0, idx) : url;
|
|
5468
5513
|
}
|
|
5469
5514
|
async function loadWsHandler(filePath, ctx, rootDir) {
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
if (dist) {
|
|
5477
|
-
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
5478
|
-
const compiled = await ensureCompiled(sourcePath, rootDir, dist);
|
|
5479
|
-
if (compiled) {
|
|
5480
|
-
module = await importWithCacheBust(filePath);
|
|
5481
|
-
}
|
|
5515
|
+
if (isDevOnDemandEnabled() && rootDir) {
|
|
5516
|
+
const dist = getDevDist();
|
|
5517
|
+
if (dist) {
|
|
5518
|
+
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
5519
|
+
if (sourcePath && fs12.existsSync(sourcePath)) {
|
|
5520
|
+
await ensureCompiled(sourcePath, rootDir, dist);
|
|
5482
5521
|
}
|
|
5483
5522
|
}
|
|
5484
|
-
if (!module) {
|
|
5485
|
-
throw err;
|
|
5486
|
-
}
|
|
5487
5523
|
}
|
|
5524
|
+
const module = await importWithCacheBust(filePath, isDevOnDemandEnabled());
|
|
5488
5525
|
const handler = module["WS"];
|
|
5489
5526
|
if (typeof handler !== "function") {
|
|
5490
5527
|
throw new Error(`WS export not found in ${filePath}`);
|
|
@@ -5955,7 +5992,7 @@ var init_loadPlugins = __esm({
|
|
|
5955
5992
|
});
|
|
5956
5993
|
|
|
5957
5994
|
// src/cli/createAppCore.ts
|
|
5958
|
-
import
|
|
5995
|
+
import fs13 from "fs";
|
|
5959
5996
|
import path15 from "path";
|
|
5960
5997
|
import { PassThrough } from "stream";
|
|
5961
5998
|
function isFaapiConfigKey(key) {
|
|
@@ -5965,7 +6002,7 @@ async function createAppBase(options) {
|
|
|
5965
6002
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
5966
6003
|
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
5967
6004
|
const routesPath = path15.resolve(rootDir, dist, ROUTES_FILE);
|
|
5968
|
-
if (!
|
|
6005
|
+
if (!fs13.existsSync(routesPath)) {
|
|
5969
6006
|
throw new Error(
|
|
5970
6007
|
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
5971
6008
|
);
|
|
@@ -6062,41 +6099,40 @@ async function createAppBase(options) {
|
|
|
6062
6099
|
} = injectOpts ?? {};
|
|
6063
6100
|
const queryStr = query ? "?" + new URLSearchParams(Object.entries(query).map(([k, v]) => [k, String(v)])).toString() : "";
|
|
6064
6101
|
return new Promise((resolve3, reject) => {
|
|
6065
|
-
const
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
}
|
|
6082
|
-
},
|
|
6083
|
-
end(data) {
|
|
6084
|
-
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data ?? "");
|
|
6085
|
-
this._body = buf;
|
|
6086
|
-
resolve3({
|
|
6087
|
-
status: this.statusCode,
|
|
6088
|
-
headers: new Headers(this._headers),
|
|
6089
|
-
body: this.parseBody()
|
|
6090
|
-
});
|
|
6091
|
-
},
|
|
6092
|
-
parseBody() {
|
|
6093
|
-
try {
|
|
6094
|
-
return JSON.parse(this._body.toString());
|
|
6095
|
-
} catch {
|
|
6096
|
-
return this._body.toString();
|
|
6097
|
-
}
|
|
6102
|
+
const chunks = [];
|
|
6103
|
+
const mockRes = new PassThrough();
|
|
6104
|
+
mockRes.statusCode = 200;
|
|
6105
|
+
mockRes._headers = {};
|
|
6106
|
+
mockRes.setHeader = function(name, value) {
|
|
6107
|
+
this._headers[name.toLowerCase()] = value;
|
|
6108
|
+
};
|
|
6109
|
+
mockRes.appendHeader = function(name, value) {
|
|
6110
|
+
const key = name.toLowerCase();
|
|
6111
|
+
const existing = this._headers[key];
|
|
6112
|
+
this._headers[key] = existing ? `${existing}, ${value}` : value;
|
|
6113
|
+
};
|
|
6114
|
+
mockRes.writeHead = function(status, headers) {
|
|
6115
|
+
this.statusCode = status;
|
|
6116
|
+
if (headers) {
|
|
6117
|
+
Object.assign(this._headers, headers);
|
|
6098
6118
|
}
|
|
6099
6119
|
};
|
|
6120
|
+
mockRes.on("data", (chunk) => chunks.push(chunk));
|
|
6121
|
+
mockRes.on("error", reject);
|
|
6122
|
+
mockRes.on("finish", () => {
|
|
6123
|
+
const body2 = Buffer.concat(chunks);
|
|
6124
|
+
let parsed;
|
|
6125
|
+
try {
|
|
6126
|
+
parsed = JSON.parse(body2.toString());
|
|
6127
|
+
} catch {
|
|
6128
|
+
parsed = body2.toString();
|
|
6129
|
+
}
|
|
6130
|
+
resolve3({
|
|
6131
|
+
status: mockRes.statusCode,
|
|
6132
|
+
headers: new Headers(mockRes._headers),
|
|
6133
|
+
body: parsed
|
|
6134
|
+
});
|
|
6135
|
+
});
|
|
6100
6136
|
const listeners = server.listeners("request");
|
|
6101
6137
|
const handler = listeners[listeners.length - 1];
|
|
6102
6138
|
if (typeof handler !== "function") {
|
|
@@ -6138,6 +6174,10 @@ async function createAppBase(options) {
|
|
|
6138
6174
|
if (config?.lifecycle?.onClose) {
|
|
6139
6175
|
await config.lifecycle.onClose({ rootDir, routes: sorted, server });
|
|
6140
6176
|
}
|
|
6177
|
+
if (!server.listening) {
|
|
6178
|
+
app.server = null;
|
|
6179
|
+
return;
|
|
6180
|
+
}
|
|
6141
6181
|
return new Promise((resolve3) => {
|
|
6142
6182
|
server.close((err) => {
|
|
6143
6183
|
if (err) console.error("Error closing server:", err);
|
|
@@ -6191,7 +6231,8 @@ var init_createAppCore = __esm({
|
|
|
6191
6231
|
"helmet",
|
|
6192
6232
|
"bodyLimit",
|
|
6193
6233
|
"logger",
|
|
6194
|
-
"http2"
|
|
6234
|
+
"http2",
|
|
6235
|
+
"response"
|
|
6195
6236
|
]);
|
|
6196
6237
|
}
|
|
6197
6238
|
});
|
|
@@ -6287,7 +6328,7 @@ var init_devCommand = __esm({
|
|
|
6287
6328
|
|
|
6288
6329
|
// src/cli/compileBuildRoutes.ts
|
|
6289
6330
|
import path17 from "path";
|
|
6290
|
-
import
|
|
6331
|
+
import fs14 from "fs";
|
|
6291
6332
|
import fg3 from "fast-glob";
|
|
6292
6333
|
async function compileBuildRoutes(options) {
|
|
6293
6334
|
const { rootDir, dist, files, logLevel = "silent" } = options;
|
|
@@ -6301,7 +6342,7 @@ async function compileBuildRoutes(options) {
|
|
|
6301
6342
|
return { compiledFiles: [] };
|
|
6302
6343
|
}
|
|
6303
6344
|
const absDist = path17.resolve(rootDir, dist);
|
|
6304
|
-
await
|
|
6345
|
+
await fs14.promises.mkdir(absDist, { recursive: true });
|
|
6305
6346
|
const plugins = buildAliasPlugins(rootDir);
|
|
6306
6347
|
const esbuild = await import("esbuild");
|
|
6307
6348
|
const outbase = path17.resolve(rootDir, APP_DIR3);
|
|
@@ -6336,7 +6377,7 @@ __export(buildCommand_exports, {
|
|
|
6336
6377
|
buildCommand: () => buildCommand
|
|
6337
6378
|
});
|
|
6338
6379
|
import path18 from "path";
|
|
6339
|
-
import
|
|
6380
|
+
import fs15 from "fs";
|
|
6340
6381
|
async function buildCommand(options) {
|
|
6341
6382
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
6342
6383
|
const outdir = options?.dist ?? DEFAULT_DIST2;
|
|
@@ -6399,7 +6440,7 @@ loadEnv(process.cwd());
|
|
|
6399
6440
|
const app = await createProdApp(${createProdAppArgs});
|
|
6400
6441
|
await app.listen();
|
|
6401
6442
|
`;
|
|
6402
|
-
await
|
|
6443
|
+
await fs15.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
6403
6444
|
console.log(` Written to ${mainPath}`);
|
|
6404
6445
|
console.log("\nfaapi build completed");
|
|
6405
6446
|
}
|