@faapi/faapi 6.22.0 → 6.23.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 +369 -256
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +31 -2
- package/dist/index.js +149 -111
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-CXWkRx4W.d.ts → routeTypes-CJV5M90s.d.ts} +8 -0
- package/dist/testing.d.ts +3 -1
- package/dist/testing.js +80 -47
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2046,14 +2046,14 @@ var ValidationError = class extends FaapiError {
|
|
|
2046
2046
|
issues;
|
|
2047
2047
|
};
|
|
2048
2048
|
var RouteNotFoundError = class extends FaapiError {
|
|
2049
|
-
constructor(
|
|
2050
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
2049
|
+
constructor(path32) {
|
|
2050
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path32}`, 404);
|
|
2051
2051
|
this.name = "RouteNotFoundError";
|
|
2052
2052
|
}
|
|
2053
2053
|
};
|
|
2054
2054
|
var MethodNotAllowedError = class extends FaapiError {
|
|
2055
|
-
constructor(method,
|
|
2056
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
2055
|
+
constructor(method, path32, allowedMethods) {
|
|
2056
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path32}`, 405);
|
|
2057
2057
|
this.allowedMethods = allowedMethods;
|
|
2058
2058
|
this.name = "MethodNotAllowedError";
|
|
2059
2059
|
}
|
|
@@ -3442,13 +3442,14 @@ function buildAliasPlugins(rootDir) {
|
|
|
3442
3442
|
}
|
|
3443
3443
|
|
|
3444
3444
|
// src/cli/compileSourceFiles.ts
|
|
3445
|
+
var SOURCE_IGNORE = ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts", `${APP_DIR}/resources/**`];
|
|
3445
3446
|
async function compileSourceFiles(options) {
|
|
3446
3447
|
const { rootDir, dist, files, logLevel = "silent", production, atomicWrite } = options;
|
|
3447
3448
|
const entryPoints = files ?? await fg2([`${APP_DIR}/**/*.ts`], {
|
|
3448
3449
|
cwd: rootDir,
|
|
3449
3450
|
onlyFiles: true,
|
|
3450
3451
|
absolute: true,
|
|
3451
|
-
ignore:
|
|
3452
|
+
ignore: SOURCE_IGNORE
|
|
3452
3453
|
});
|
|
3453
3454
|
if (entryPoints.length === 0) {
|
|
3454
3455
|
return { compiledFiles: [] };
|
|
@@ -4353,8 +4354,8 @@ function loadEnv(rootDir) {
|
|
|
4353
4354
|
}
|
|
4354
4355
|
|
|
4355
4356
|
// src/cli/createAppCore.ts
|
|
4356
|
-
import
|
|
4357
|
-
import
|
|
4357
|
+
import fs22 from "fs";
|
|
4358
|
+
import path27 from "path";
|
|
4358
4359
|
|
|
4359
4360
|
// src/router/sortRoutes.ts
|
|
4360
4361
|
function sortRoutes(routes) {
|
|
@@ -4418,7 +4419,7 @@ import {
|
|
|
4418
4419
|
import { createSecureServer as createHttp2SecureServer } from "http2";
|
|
4419
4420
|
import { readFileSync } from "fs";
|
|
4420
4421
|
import { Readable as Readable2 } from "stream";
|
|
4421
|
-
import
|
|
4422
|
+
import path20 from "path";
|
|
4422
4423
|
|
|
4423
4424
|
// src/router/matchRoute.ts
|
|
4424
4425
|
var httpIndexCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4460,25 +4461,25 @@ function getWsIndex(routes) {
|
|
|
4460
4461
|
wsIndexCache.set(routes, index);
|
|
4461
4462
|
return index;
|
|
4462
4463
|
}
|
|
4463
|
-
function matchRoute(routes, method,
|
|
4464
|
+
function matchRoute(routes, method, path32) {
|
|
4464
4465
|
const index = getHttpIndex(routes);
|
|
4465
4466
|
const upper = method.toUpperCase();
|
|
4466
|
-
const hit = matchByMethod(index, upper,
|
|
4467
|
+
const hit = matchByMethod(index, upper, path32);
|
|
4467
4468
|
if (hit) return hit;
|
|
4468
4469
|
if (upper === "HEAD") {
|
|
4469
|
-
return matchByMethod(index, "GET",
|
|
4470
|
+
return matchByMethod(index, "GET", path32);
|
|
4470
4471
|
}
|
|
4471
4472
|
return null;
|
|
4472
4473
|
}
|
|
4473
|
-
function matchByMethod(index, method,
|
|
4474
|
-
const staticHit = index.static.get(`${method}|${
|
|
4474
|
+
function matchByMethod(index, method, path32) {
|
|
4475
|
+
const staticHit = index.static.get(`${method}|${path32}`);
|
|
4475
4476
|
if (staticHit) {
|
|
4476
4477
|
return { route: staticHit, params: {} };
|
|
4477
4478
|
}
|
|
4478
4479
|
if (index.dynamics.length === 0) {
|
|
4479
4480
|
return null;
|
|
4480
4481
|
}
|
|
4481
|
-
const pathSegments =
|
|
4482
|
+
const pathSegments = path32.split("/").filter(Boolean);
|
|
4482
4483
|
for (const entry of index.dynamics) {
|
|
4483
4484
|
const route = entry.route;
|
|
4484
4485
|
if (route.method !== method) {
|
|
@@ -4491,31 +4492,31 @@ function matchByMethod(index, method, path31) {
|
|
|
4491
4492
|
}
|
|
4492
4493
|
return null;
|
|
4493
4494
|
}
|
|
4494
|
-
function matchWsRoute(wsRoutes,
|
|
4495
|
+
function matchWsRoute(wsRoutes, path32) {
|
|
4495
4496
|
const index = getWsIndex(wsRoutes);
|
|
4496
|
-
const staticHit = index.static.get(
|
|
4497
|
+
const staticHit = index.static.get(path32);
|
|
4497
4498
|
if (staticHit) {
|
|
4498
4499
|
return { route: staticHit, params: {} };
|
|
4499
4500
|
}
|
|
4500
4501
|
for (const route of index.dynamics) {
|
|
4501
|
-
const params = matchDynamicPath(route.urlPath,
|
|
4502
|
+
const params = matchDynamicPath(route.urlPath, path32, route.paramNames, route.isCatchAll);
|
|
4502
4503
|
if (params !== null) {
|
|
4503
4504
|
return { route, params };
|
|
4504
4505
|
}
|
|
4505
4506
|
}
|
|
4506
4507
|
return null;
|
|
4507
4508
|
}
|
|
4508
|
-
function findAllowedMethods(routes,
|
|
4509
|
+
function findAllowedMethods(routes, path32) {
|
|
4509
4510
|
const index = getHttpIndex(routes);
|
|
4510
4511
|
const methods = /* @__PURE__ */ new Set();
|
|
4511
|
-
const staticMethods = index.methodsByStaticPath.get(
|
|
4512
|
+
const staticMethods = index.methodsByStaticPath.get(path32);
|
|
4512
4513
|
if (staticMethods) {
|
|
4513
4514
|
for (const method of staticMethods) {
|
|
4514
4515
|
methods.add(method);
|
|
4515
4516
|
}
|
|
4516
4517
|
}
|
|
4517
4518
|
if (index.dynamics.length > 0) {
|
|
4518
|
-
const pathSegments =
|
|
4519
|
+
const pathSegments = path32.split("/").filter(Boolean);
|
|
4519
4520
|
for (const entry of index.dynamics) {
|
|
4520
4521
|
const params = matchSegments(
|
|
4521
4522
|
entry.segments,
|
|
@@ -4533,10 +4534,10 @@ function findAllowedMethods(routes, path31) {
|
|
|
4533
4534
|
}
|
|
4534
4535
|
return Array.from(methods);
|
|
4535
4536
|
}
|
|
4536
|
-
function matchDynamicPath(pattern,
|
|
4537
|
+
function matchDynamicPath(pattern, path32, paramNames, isCatchAll) {
|
|
4537
4538
|
return matchSegments(
|
|
4538
4539
|
pattern.split("/").filter(Boolean),
|
|
4539
|
-
|
|
4540
|
+
path32.split("/").filter(Boolean),
|
|
4540
4541
|
paramNames,
|
|
4541
4542
|
isCatchAll
|
|
4542
4543
|
);
|
|
@@ -5039,10 +5040,18 @@ function formatSetCookie(name, value, options) {
|
|
|
5039
5040
|
if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
|
|
5040
5041
|
return cookie;
|
|
5041
5042
|
}
|
|
5042
|
-
function createContext(request, params, config = {}, ip = "", registries) {
|
|
5043
|
-
return createContextFromUrl(
|
|
5043
|
+
function createContext(request, params, config = {}, ip = "", registries, resourcesDir) {
|
|
5044
|
+
return createContextFromUrl(
|
|
5045
|
+
request,
|
|
5046
|
+
new URL(request.url),
|
|
5047
|
+
params,
|
|
5048
|
+
config,
|
|
5049
|
+
ip,
|
|
5050
|
+
registries,
|
|
5051
|
+
resourcesDir
|
|
5052
|
+
);
|
|
5044
5053
|
}
|
|
5045
|
-
function createContextFromUrl(request, url, params, config = {}, ip = "", registries) {
|
|
5054
|
+
function createContextFromUrl(request, url, params, config = {}, ip = "", registries, resourcesDir) {
|
|
5046
5055
|
const meta = { headers: {}, setCookies: [] };
|
|
5047
5056
|
const parsedCookies = parseCookies(request.headers.get("cookie") ?? "");
|
|
5048
5057
|
const cookiesObj = {};
|
|
@@ -5070,6 +5079,9 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
5070
5079
|
ua: request.headers.get("user-agent") ?? "",
|
|
5071
5080
|
cookies: cookiesObj,
|
|
5072
5081
|
config,
|
|
5082
|
+
// 运行时资源根目录(HTTP/WS 链路由 createServer/handleWsUpgrade 传入;
|
|
5083
|
+
// testing 直调未传时为 undefined,见 contextTypes.resourcesDir 注释)
|
|
5084
|
+
resourcesDir,
|
|
5073
5085
|
meta,
|
|
5074
5086
|
setStatus(status) {
|
|
5075
5087
|
meta.status = status;
|
|
@@ -5664,9 +5676,9 @@ async function validateInput(schemaPath, method, inputType, input) {
|
|
|
5664
5676
|
function mapZodIssues(error) {
|
|
5665
5677
|
return error.issues.map((issue) => {
|
|
5666
5678
|
const code = mapZodCode(issue);
|
|
5667
|
-
const
|
|
5679
|
+
const path32 = issue.path.map(String).join(".") || "";
|
|
5668
5680
|
return {
|
|
5669
|
-
path:
|
|
5681
|
+
path: path32,
|
|
5670
5682
|
code,
|
|
5671
5683
|
expected: issue.expected ?? mapExpectedFromMessage(issue.message),
|
|
5672
5684
|
received: issue.received ?? mapReceivedFromMessage(issue.message),
|
|
@@ -5887,9 +5899,17 @@ function etag(options = {}) {
|
|
|
5887
5899
|
}
|
|
5888
5900
|
|
|
5889
5901
|
// src/server/handleWsUpgrade.ts
|
|
5890
|
-
import
|
|
5902
|
+
import fs17 from "fs";
|
|
5891
5903
|
import { WebSocketServer, WebSocket } from "ws";
|
|
5904
|
+
import path19 from "path";
|
|
5905
|
+
|
|
5906
|
+
// src/cli/copyResources.ts
|
|
5892
5907
|
import path18 from "path";
|
|
5908
|
+
import fs16 from "fs";
|
|
5909
|
+
var RESOURCES_OUTPUT_DIR = "resources";
|
|
5910
|
+
function resolveResourcesDir(rootDir, dist) {
|
|
5911
|
+
return path18.resolve(rootDir, dist, RESOURCES_OUTPUT_DIR);
|
|
5912
|
+
}
|
|
5893
5913
|
|
|
5894
5914
|
// src/server/serverUtils.ts
|
|
5895
5915
|
function nodeHttpToWebHeaders(req) {
|
|
@@ -6029,7 +6049,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
|
|
|
6029
6049
|
const dist = getDevDist();
|
|
6030
6050
|
if (dist) {
|
|
6031
6051
|
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
6032
|
-
if (sourcePath &&
|
|
6052
|
+
if (sourcePath && fs17.existsSync(sourcePath)) {
|
|
6033
6053
|
await ensureCompiled(sourcePath, rootDir, dist);
|
|
6034
6054
|
}
|
|
6035
6055
|
}
|
|
@@ -6127,6 +6147,7 @@ function attachWebSocket(options) {
|
|
|
6127
6147
|
trustedProxy = false,
|
|
6128
6148
|
registries
|
|
6129
6149
|
} = options;
|
|
6150
|
+
const resourcesDir = resolveResourcesDir(rootDir, dist);
|
|
6130
6151
|
const wss = new WebSocketServer({ noServer: true });
|
|
6131
6152
|
server.on("upgrade", async (req, socket, head) => {
|
|
6132
6153
|
try {
|
|
@@ -6158,12 +6179,19 @@ function attachWebSocket(options) {
|
|
|
6158
6179
|
const host = req.headers.host ?? "localhost";
|
|
6159
6180
|
const fullUrl = new URL(`http://${host}${req.url ?? "/"}`);
|
|
6160
6181
|
const request = new Request(fullUrl.toString(), { method: "GET", headers });
|
|
6161
|
-
const ctx = createContext(
|
|
6182
|
+
const ctx = createContext(
|
|
6183
|
+
request,
|
|
6184
|
+
params,
|
|
6185
|
+
config,
|
|
6186
|
+
getClientIp(req, trustedProxy),
|
|
6187
|
+
registries,
|
|
6188
|
+
resourcesDir
|
|
6189
|
+
);
|
|
6162
6190
|
const meta = ctx.meta;
|
|
6163
6191
|
let upgraded = false;
|
|
6164
6192
|
const finalHandler = async () => {
|
|
6165
6193
|
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
6166
|
-
if (
|
|
6194
|
+
if (fs17.existsSync(schemaPath)) {
|
|
6167
6195
|
if (isDevOnDemandEnabled()) {
|
|
6168
6196
|
const devDist = getDevDist();
|
|
6169
6197
|
if (devDist) {
|
|
@@ -6202,7 +6230,7 @@ function attachWebSocket(options) {
|
|
|
6202
6230
|
}
|
|
6203
6231
|
let handlers;
|
|
6204
6232
|
try {
|
|
6205
|
-
const absoluteFilePath =
|
|
6233
|
+
const absoluteFilePath = path19.resolve(rootDir, route.filePath);
|
|
6206
6234
|
handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
|
|
6207
6235
|
} catch (err) {
|
|
6208
6236
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -6362,6 +6390,7 @@ function createServer(options) {
|
|
|
6362
6390
|
trustedProxy = false
|
|
6363
6391
|
} = options;
|
|
6364
6392
|
const routesRef = { current: routes, wsCurrent: wsRoutes ?? [] };
|
|
6393
|
+
const resourcesDir = resolveResourcesDir(rootDir, dist);
|
|
6365
6394
|
const configMiddlewares = [];
|
|
6366
6395
|
if (compressionOption) {
|
|
6367
6396
|
const compOpts = typeof compressionOption === "object" ? compressionOption : {};
|
|
@@ -6408,7 +6437,8 @@ function createServer(options) {
|
|
|
6408
6437
|
bodyLimit,
|
|
6409
6438
|
trustedProxy,
|
|
6410
6439
|
registries,
|
|
6411
|
-
routesRef.wsCurrent
|
|
6440
|
+
routesRef.wsCurrent,
|
|
6441
|
+
resourcesDir
|
|
6412
6442
|
).catch((err) => {
|
|
6413
6443
|
console.error("[faapi] Request pipeline failed after error response attempt:", err);
|
|
6414
6444
|
try {
|
|
@@ -6434,7 +6464,7 @@ function createServer(options) {
|
|
|
6434
6464
|
});
|
|
6435
6465
|
return { server, routesRef };
|
|
6436
6466
|
}
|
|
6437
|
-
function prepareRequest(req, request, url, config, trustedProxy, registries, preMatched) {
|
|
6467
|
+
function prepareRequest(req, request, url, config, trustedProxy, registries, preMatched, resourcesDir) {
|
|
6438
6468
|
const method = request.method.toUpperCase();
|
|
6439
6469
|
const urlPath = url.pathname;
|
|
6440
6470
|
const ctx = createContextFromUrl(
|
|
@@ -6443,7 +6473,8 @@ function prepareRequest(req, request, url, config, trustedProxy, registries, pre
|
|
|
6443
6473
|
preMatched?.params ?? {},
|
|
6444
6474
|
config,
|
|
6445
6475
|
getClientIp(req, trustedProxy),
|
|
6446
|
-
registries
|
|
6476
|
+
registries,
|
|
6477
|
+
resourcesDir
|
|
6447
6478
|
);
|
|
6448
6479
|
const meta = ctx.meta;
|
|
6449
6480
|
return { request, url, ctx, meta, method, urlPath };
|
|
@@ -6462,7 +6493,7 @@ function getRoutePaths(route, rootDir, dist) {
|
|
|
6462
6493
|
let cached = routePathCache.get(route);
|
|
6463
6494
|
if (!cached) {
|
|
6464
6495
|
cached = {
|
|
6465
|
-
absFilePath:
|
|
6496
|
+
absFilePath: path20.resolve(rootDir, route.filePath),
|
|
6466
6497
|
schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir)
|
|
6467
6498
|
};
|
|
6468
6499
|
routePathCache.set(route, cached);
|
|
@@ -6585,7 +6616,7 @@ async function sendErrorResponse(err, meta, res, onError, ctx) {
|
|
|
6585
6616
|
}
|
|
6586
6617
|
}
|
|
6587
6618
|
}
|
|
6588
|
-
async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries, wsRoutes) {
|
|
6619
|
+
async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares, onError, config, globalInjectors, bodyLimit, trustedProxy, registries, wsRoutes, resourcesDir) {
|
|
6589
6620
|
let meta = { headers: {}, setCookies: [] };
|
|
6590
6621
|
let ctx;
|
|
6591
6622
|
let sseEarlySent = false;
|
|
@@ -6605,7 +6636,8 @@ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares,
|
|
|
6605
6636
|
config,
|
|
6606
6637
|
trustedProxy,
|
|
6607
6638
|
registries,
|
|
6608
|
-
preMatched
|
|
6639
|
+
preMatched,
|
|
6640
|
+
resourcesDir
|
|
6609
6641
|
);
|
|
6610
6642
|
ctx = prepared.ctx;
|
|
6611
6643
|
meta = prepared.meta;
|
|
@@ -6681,8 +6713,8 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
|
|
|
6681
6713
|
}
|
|
6682
6714
|
|
|
6683
6715
|
// src/cli/generateRoutes.ts
|
|
6684
|
-
import
|
|
6685
|
-
import
|
|
6716
|
+
import fs18 from "fs";
|
|
6717
|
+
import path21 from "path";
|
|
6686
6718
|
async function hydrateRoutes(manifest) {
|
|
6687
6719
|
const hydrateRoute = (serialized) => ({
|
|
6688
6720
|
method: serialized.method,
|
|
@@ -6726,18 +6758,18 @@ function createIdleTaskDriver() {
|
|
|
6726
6758
|
}
|
|
6727
6759
|
|
|
6728
6760
|
// src/cli/loadPlugins.ts
|
|
6729
|
-
import
|
|
6730
|
-
import
|
|
6761
|
+
import path23 from "path";
|
|
6762
|
+
import fs20 from "fs";
|
|
6731
6763
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
6732
6764
|
|
|
6733
6765
|
// src/cli/compileConfig.ts
|
|
6734
|
-
import
|
|
6735
|
-
import
|
|
6766
|
+
import path22 from "path";
|
|
6767
|
+
import fs19 from "fs";
|
|
6736
6768
|
async function compileProjectModules(entryPoints, rootDir, dist) {
|
|
6737
6769
|
const { insideFiles, outsideFiles } = await collectRelativeImports(entryPoints, rootDir);
|
|
6738
6770
|
const esbuild = await import("esbuild");
|
|
6739
6771
|
const aliasPlugins = buildAliasPlugins(rootDir);
|
|
6740
|
-
const absDist =
|
|
6772
|
+
const absDist = path22.resolve(rootDir, dist);
|
|
6741
6773
|
await esbuild.build({
|
|
6742
6774
|
entryPoints: [...entryPoints, ...outsideFiles],
|
|
6743
6775
|
outdir: absDist,
|
|
@@ -6751,7 +6783,7 @@ async function compileProjectModules(entryPoints, rootDir, dist) {
|
|
|
6751
6783
|
logLevel: "silent"
|
|
6752
6784
|
});
|
|
6753
6785
|
if (insideFiles.length > 0) {
|
|
6754
|
-
const appOutbase =
|
|
6786
|
+
const appOutbase = path22.resolve(rootDir, "src");
|
|
6755
6787
|
await esbuild.build({
|
|
6756
6788
|
entryPoints: insideFiles,
|
|
6757
6789
|
outdir: absDist,
|
|
@@ -6832,30 +6864,30 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
6832
6864
|
var TS_EXTS = /\.(ts|tsx|mts|cts)$/;
|
|
6833
6865
|
var JS_EXTS = /\.(js|mjs|cjs|jsx)$/;
|
|
6834
6866
|
function resolveLocalPluginSource(specifier, baseDir) {
|
|
6835
|
-
const base =
|
|
6836
|
-
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) &&
|
|
6867
|
+
const base = path23.isAbsolute(specifier) ? specifier : path23.resolve(baseDir, specifier);
|
|
6868
|
+
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs20.existsSync(base)) return base;
|
|
6837
6869
|
for (const ext of [".ts", ".js"]) {
|
|
6838
|
-
if (
|
|
6870
|
+
if (fs20.existsSync(base + ext)) return base + ext;
|
|
6839
6871
|
}
|
|
6840
6872
|
for (const indexExt of ["/index.ts", "/index.js"]) {
|
|
6841
|
-
if (
|
|
6873
|
+
if (fs20.existsSync(base + indexExt)) return base + indexExt;
|
|
6842
6874
|
}
|
|
6843
6875
|
return null;
|
|
6844
6876
|
}
|
|
6845
6877
|
function mirrorProductPath(sourcePath, baseDir, distDir) {
|
|
6846
|
-
let rel =
|
|
6878
|
+
let rel = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
6847
6879
|
if (rel.startsWith("src/")) rel = rel.slice(4);
|
|
6848
|
-
return
|
|
6880
|
+
return path23.resolve(distDir, rel.replace(TS_EXTS, ".js"));
|
|
6849
6881
|
}
|
|
6850
6882
|
async function importPluginModule(specifier, baseDir, dist) {
|
|
6851
6883
|
const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
|
|
6852
|
-
if (!isRelative && !
|
|
6884
|
+
if (!isRelative && !path23.isAbsolute(specifier)) {
|
|
6853
6885
|
return import(specifier);
|
|
6854
6886
|
}
|
|
6855
|
-
const distDir = dist ?
|
|
6887
|
+
const distDir = dist ? path23.resolve(baseDir, dist) : null;
|
|
6856
6888
|
const sourcePath = resolveLocalPluginSource(specifier, baseDir);
|
|
6857
6889
|
const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
|
|
6858
|
-
if (productPath !== null &&
|
|
6890
|
+
if (productPath !== null && fs20.existsSync(productPath) && (sourcePath === null || isProductFresh(sourcePath, productPath))) {
|
|
6859
6891
|
return import(pathToFileURL3(productPath).href);
|
|
6860
6892
|
}
|
|
6861
6893
|
if (sourcePath && TS_EXTS.test(sourcePath)) {
|
|
@@ -6864,13 +6896,13 @@ async function importPluginModule(specifier, baseDir, dist) {
|
|
|
6864
6896
|
`Local plugin "${specifier}" has no up-to-date build artifact (dist is stale or missing). Run "faapi build" first, or use "faapi dev" for on-demand compilation.`
|
|
6865
6897
|
);
|
|
6866
6898
|
}
|
|
6867
|
-
const relFromRoot =
|
|
6899
|
+
const relFromRoot = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
6868
6900
|
if (relFromRoot.startsWith("src/")) {
|
|
6869
6901
|
await ensureCompiled(sourcePath, baseDir, dist);
|
|
6870
6902
|
} else {
|
|
6871
6903
|
await compileProjectModules([sourcePath], baseDir, dist);
|
|
6872
6904
|
}
|
|
6873
|
-
if (productPath &&
|
|
6905
|
+
if (productPath && fs20.existsSync(productPath)) {
|
|
6874
6906
|
return import(pathToFileURL3(productPath).href);
|
|
6875
6907
|
}
|
|
6876
6908
|
}
|
|
@@ -6882,7 +6914,7 @@ async function importPluginModule(specifier, baseDir, dist) {
|
|
|
6882
6914
|
`${specifier}/index.ts`,
|
|
6883
6915
|
`${specifier}.js`,
|
|
6884
6916
|
`${specifier}/index.js`
|
|
6885
|
-
].map((c) =>
|
|
6917
|
+
].map((c) => path23.isAbsolute(c) ? c : path23.join(baseDir, c));
|
|
6886
6918
|
throw new Error(
|
|
6887
6919
|
`Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
|
|
6888
6920
|
${candidates.join("\n ")}
|
|
@@ -6907,11 +6939,11 @@ function resolveDeclaration(decl) {
|
|
|
6907
6939
|
}
|
|
6908
6940
|
|
|
6909
6941
|
// src/cli/manifestLoader.ts
|
|
6910
|
-
import
|
|
6911
|
-
import
|
|
6942
|
+
import fs21 from "fs";
|
|
6943
|
+
import path26 from "path";
|
|
6912
6944
|
|
|
6913
6945
|
// src/cli/generateAgentArtifacts.ts
|
|
6914
|
-
import
|
|
6946
|
+
import path24 from "path";
|
|
6915
6947
|
|
|
6916
6948
|
// src/ast/extractAgentMetadata.ts
|
|
6917
6949
|
import ts11 from "typescript";
|
|
@@ -7191,9 +7223,9 @@ function validateAgentList(metadata) {
|
|
|
7191
7223
|
}
|
|
7192
7224
|
async function generateAgentArtifacts(agents, rootDir, dist) {
|
|
7193
7225
|
const metadata = [];
|
|
7194
|
-
const programByFile = createPrograms(agents.map((m) =>
|
|
7226
|
+
const programByFile = createPrograms(agents.map((m) => path24.resolve(rootDir, m.filePath)));
|
|
7195
7227
|
for (const manifest of agents) {
|
|
7196
|
-
const absPath =
|
|
7228
|
+
const absPath = path24.resolve(rootDir, manifest.filePath);
|
|
7197
7229
|
const program = programByFile.get(absPath);
|
|
7198
7230
|
const result = extractAgentMetadata(program, absPath, {
|
|
7199
7231
|
name: manifest.name,
|
|
@@ -7209,14 +7241,14 @@ async function generateAgentArtifacts(agents, rootDir, dist) {
|
|
|
7209
7241
|
}
|
|
7210
7242
|
validateAgentList(metadata);
|
|
7211
7243
|
const serialized = serializeAgents(metadata, dist);
|
|
7212
|
-
const agentsPath =
|
|
7244
|
+
const agentsPath = path24.resolve(rootDir, dist, AGENTS_FILE);
|
|
7213
7245
|
await writeAgentsModule(serialized, agentsPath);
|
|
7214
7246
|
return metadata;
|
|
7215
7247
|
}
|
|
7216
7248
|
|
|
7217
7249
|
// src/cli/generateTaskArtifacts.ts
|
|
7218
7250
|
init_generateZodArtifacts();
|
|
7219
|
-
import
|
|
7251
|
+
import path25 from "path";
|
|
7220
7252
|
init_createProgram();
|
|
7221
7253
|
init_atomicWrite();
|
|
7222
7254
|
init_extractHandlerTypes();
|
|
@@ -7254,7 +7286,7 @@ function collectTaskSchemaSources(tasks, rootDir) {
|
|
|
7254
7286
|
const tasksByFile = /* @__PURE__ */ new Map();
|
|
7255
7287
|
for (const task of tasks) {
|
|
7256
7288
|
if (!task.inputTypeName) continue;
|
|
7257
|
-
const absPath =
|
|
7289
|
+
const absPath = path25.resolve(rootDir, task.filePath);
|
|
7258
7290
|
let list = tasksByFile.get(absPath);
|
|
7259
7291
|
if (!list) {
|
|
7260
7292
|
list = [];
|
|
@@ -7311,9 +7343,9 @@ function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
|
|
|
7311
7343
|
async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
7312
7344
|
const metadata = [];
|
|
7313
7345
|
if (manifests.length > 0) {
|
|
7314
|
-
const programByFile = createPrograms(manifests.map((m) =>
|
|
7346
|
+
const programByFile = createPrograms(manifests.map((m) => path25.resolve(rootDir, m.filePath)));
|
|
7315
7347
|
for (const manifest of manifests) {
|
|
7316
|
-
const absPath =
|
|
7348
|
+
const absPath = path25.resolve(rootDir, manifest.filePath);
|
|
7317
7349
|
const result = extractToolMetadata(programByFile.get(absPath), absPath, "run", {
|
|
7318
7350
|
name: manifest.name,
|
|
7319
7351
|
filePath: manifest.filePath
|
|
@@ -7324,7 +7356,7 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
|
7324
7356
|
}
|
|
7325
7357
|
}
|
|
7326
7358
|
const serialized = serializeTasks(manifests, dist);
|
|
7327
|
-
const tasksPath =
|
|
7359
|
+
const tasksPath = path25.resolve(rootDir, dist, TASKS_FILE);
|
|
7328
7360
|
await writeTasksModule(serialized, tasksPath);
|
|
7329
7361
|
if (metadata.length === 0) {
|
|
7330
7362
|
return hydrateTasks(serialized);
|
|
@@ -7346,8 +7378,8 @@ async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
|
7346
7378
|
var TOOLS_FILE2 = "faapi-tools.js";
|
|
7347
7379
|
var AGENTS_FILE2 = "faapi-agents.js";
|
|
7348
7380
|
async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
|
|
7349
|
-
const toolsPath =
|
|
7350
|
-
if (!
|
|
7381
|
+
const toolsPath = path26.resolve(rootDir, dist, TOOLS_FILE2);
|
|
7382
|
+
if (!fs21.existsSync(toolsPath)) {
|
|
7351
7383
|
return [];
|
|
7352
7384
|
}
|
|
7353
7385
|
const serialized = await importWithCacheBust(toolsPath);
|
|
@@ -7356,8 +7388,8 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
|
|
|
7356
7388
|
return hydrated;
|
|
7357
7389
|
}
|
|
7358
7390
|
async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
|
|
7359
|
-
const agentsPath =
|
|
7360
|
-
if (!
|
|
7391
|
+
const agentsPath = path26.resolve(rootDir, dist, AGENTS_FILE2);
|
|
7392
|
+
if (!fs21.existsSync(agentsPath)) {
|
|
7361
7393
|
return [];
|
|
7362
7394
|
}
|
|
7363
7395
|
const serialized = await importWithCacheBust(agentsPath);
|
|
@@ -7372,8 +7404,8 @@ function getTaskDriverOptions(config) {
|
|
|
7372
7404
|
return void 0;
|
|
7373
7405
|
}
|
|
7374
7406
|
async function loadAndHydrateTasks(rootDir, dist, registries = defaultRegistries) {
|
|
7375
|
-
const tasksPath =
|
|
7376
|
-
if (!
|
|
7407
|
+
const tasksPath = path26.resolve(rootDir, dist, TASKS_FILE);
|
|
7408
|
+
if (!fs21.existsSync(tasksPath)) {
|
|
7377
7409
|
return [];
|
|
7378
7410
|
}
|
|
7379
7411
|
const serialized = await importWithCacheBust(tasksPath);
|
|
@@ -7565,8 +7597,9 @@ function logStartupSummary(routes, wsRoutes, tools, agents, taskMetas, p) {
|
|
|
7565
7597
|
async function createAppBase(options) {
|
|
7566
7598
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
7567
7599
|
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
7568
|
-
const
|
|
7569
|
-
|
|
7600
|
+
const resourcesDir = resolveResourcesDir(rootDir, dist);
|
|
7601
|
+
const routesPath = path27.resolve(rootDir, dist, ROUTES_FILE);
|
|
7602
|
+
if (!fs22.existsSync(routesPath)) {
|
|
7570
7603
|
throw new Error(
|
|
7571
7604
|
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
7572
7605
|
);
|
|
@@ -7645,6 +7678,8 @@ async function createAppBase(options) {
|
|
|
7645
7678
|
routes: sorted,
|
|
7646
7679
|
wsRoutes,
|
|
7647
7680
|
rootDir,
|
|
7681
|
+
dist,
|
|
7682
|
+
resourcesDir,
|
|
7648
7683
|
tasks: taskQueue,
|
|
7649
7684
|
async listen(listenPort) {
|
|
7650
7685
|
const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
|
|
@@ -7656,7 +7691,8 @@ async function createAppBase(options) {
|
|
|
7656
7691
|
routes: sorted,
|
|
7657
7692
|
server,
|
|
7658
7693
|
registries,
|
|
7659
|
-
tasks: taskQueue
|
|
7694
|
+
tasks: taskQueue,
|
|
7695
|
+
resourcesDir
|
|
7660
7696
|
});
|
|
7661
7697
|
console.log("- onBoot hook executed");
|
|
7662
7698
|
} catch (err) {
|
|
@@ -7691,7 +7727,8 @@ async function createAppBase(options) {
|
|
|
7691
7727
|
routes: sorted,
|
|
7692
7728
|
server,
|
|
7693
7729
|
registries,
|
|
7694
|
-
tasks: taskQueue
|
|
7730
|
+
tasks: taskQueue,
|
|
7731
|
+
resourcesDir
|
|
7695
7732
|
});
|
|
7696
7733
|
console.log("- onReady hook executed");
|
|
7697
7734
|
}
|
|
@@ -7715,7 +7752,8 @@ async function createAppBase(options) {
|
|
|
7715
7752
|
routes: sorted,
|
|
7716
7753
|
server,
|
|
7717
7754
|
registries,
|
|
7718
|
-
tasks: taskQueue
|
|
7755
|
+
tasks: taskQueue,
|
|
7756
|
+
resourcesDir
|
|
7719
7757
|
});
|
|
7720
7758
|
}
|
|
7721
7759
|
registries.tool.clear();
|
|
@@ -7777,21 +7815,21 @@ async function createAppBase(options) {
|
|
|
7777
7815
|
}
|
|
7778
7816
|
|
|
7779
7817
|
// src/cli/createDevApp.ts
|
|
7780
|
-
import
|
|
7818
|
+
import path31 from "path";
|
|
7781
7819
|
|
|
7782
7820
|
// src/router/scanRoutes.ts
|
|
7783
7821
|
import fg3 from "fast-glob";
|
|
7784
|
-
import
|
|
7785
|
-
import
|
|
7822
|
+
import path28 from "path";
|
|
7823
|
+
import fs23 from "fs";
|
|
7786
7824
|
|
|
7787
7825
|
// src/router/constants.ts
|
|
7788
7826
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
7789
7827
|
var HTTP_METHOD_SET = new Set(HTTP_METHODS);
|
|
7790
7828
|
|
|
7791
7829
|
// src/utils/normalizePath.ts
|
|
7792
|
-
function normalizePath(
|
|
7793
|
-
if (!
|
|
7794
|
-
let result =
|
|
7830
|
+
function normalizePath(path32) {
|
|
7831
|
+
if (!path32) return "";
|
|
7832
|
+
let result = path32.replace(/\\/g, "/");
|
|
7795
7833
|
result = result.replace(/\/+/g, "/");
|
|
7796
7834
|
result = result.replace(/\/+$/, "");
|
|
7797
7835
|
if (result && !result.startsWith("/")) {
|
|
@@ -7852,34 +7890,34 @@ function extractExportsFromSource(source) {
|
|
|
7852
7890
|
return names;
|
|
7853
7891
|
}
|
|
7854
7892
|
function collectMiddlewarePaths(routeFilePath, rootDir, dist) {
|
|
7855
|
-
const routeDir =
|
|
7856
|
-
const resolvedRoot =
|
|
7893
|
+
const routeDir = path28.dirname(routeFilePath);
|
|
7894
|
+
const resolvedRoot = path28.resolve(rootDir);
|
|
7857
7895
|
const paths = [];
|
|
7858
|
-
let currentDir =
|
|
7896
|
+
let currentDir = path28.resolve(rootDir, routeDir);
|
|
7859
7897
|
while (true) {
|
|
7860
7898
|
if (dist) {
|
|
7861
|
-
const mwTsPath =
|
|
7862
|
-
const mwJsPath =
|
|
7863
|
-
const absTsPath =
|
|
7864
|
-
const absJsPath =
|
|
7865
|
-
const absMwPath =
|
|
7899
|
+
const mwTsPath = path28.join(currentDir, "middlewares.ts");
|
|
7900
|
+
const mwJsPath = path28.join(currentDir, "middlewares.js");
|
|
7901
|
+
const absTsPath = path28.resolve(rootDir, mwTsPath);
|
|
7902
|
+
const absJsPath = path28.resolve(rootDir, mwJsPath);
|
|
7903
|
+
const absMwPath = fs23.existsSync(absTsPath) ? absTsPath : fs23.existsSync(absJsPath) ? absJsPath : null;
|
|
7866
7904
|
if (absMwPath) {
|
|
7867
|
-
const relMwPath =
|
|
7868
|
-
const prodAbsPath =
|
|
7905
|
+
const relMwPath = path28.relative(rootDir, absMwPath);
|
|
7906
|
+
const prodAbsPath = path28.resolve(rootDir, toProdFilePath(relMwPath, dist));
|
|
7869
7907
|
paths.push(prodAbsPath);
|
|
7870
7908
|
}
|
|
7871
7909
|
} else {
|
|
7872
7910
|
for (const ext of [".ts", ".js"]) {
|
|
7873
|
-
const mwPath =
|
|
7874
|
-
const absMwPath =
|
|
7875
|
-
if (
|
|
7911
|
+
const mwPath = path28.join(currentDir, `middlewares${ext}`);
|
|
7912
|
+
const absMwPath = path28.resolve(rootDir, mwPath);
|
|
7913
|
+
if (fs23.existsSync(absMwPath)) {
|
|
7876
7914
|
paths.push(absMwPath);
|
|
7877
7915
|
break;
|
|
7878
7916
|
}
|
|
7879
7917
|
}
|
|
7880
7918
|
}
|
|
7881
7919
|
if (currentDir === resolvedRoot) break;
|
|
7882
|
-
const parentDir =
|
|
7920
|
+
const parentDir = path28.dirname(currentDir);
|
|
7883
7921
|
if (parentDir === currentDir) break;
|
|
7884
7922
|
currentDir = parentDir;
|
|
7885
7923
|
}
|
|
@@ -7898,7 +7936,7 @@ async function scanRoutes(rootDir, patterns, dist) {
|
|
|
7898
7936
|
const normalizedFile = file.replace(/\\/g, "/");
|
|
7899
7937
|
const fileName = normalizedFile.split("/").pop();
|
|
7900
7938
|
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
7901
|
-
const absPath =
|
|
7939
|
+
const absPath = path28.resolve(rootDir, normalizedFile);
|
|
7902
7940
|
const urlPath = filePathToUrlPath(normalizedFile);
|
|
7903
7941
|
const paramNames = extractParamNames(urlPath);
|
|
7904
7942
|
const isDynamic = paramNames.length > 0;
|
|
@@ -7911,7 +7949,7 @@ async function scanRoutes(rootDir, patterns, dist) {
|
|
|
7911
7949
|
const mwPaths = collectMiddlewarePaths(normalizedFile, rootDir);
|
|
7912
7950
|
middlewareBundle = await loadMergedMiddlewares(mwPaths);
|
|
7913
7951
|
}
|
|
7914
|
-
const source = await
|
|
7952
|
+
const source = await fs23.promises.readFile(absPath, "utf8").catch(() => "");
|
|
7915
7953
|
const exportNames = extractExportsFromSource(source);
|
|
7916
7954
|
const methods = HTTP_METHODS.filter((m) => exportNames.has(m));
|
|
7917
7955
|
for (const method of methods) {
|
|
@@ -7947,8 +7985,8 @@ async function scanRoutes(rootDir, patterns, dist) {
|
|
|
7947
7985
|
|
|
7948
7986
|
// src/tools/scanTools.ts
|
|
7949
7987
|
import fg4 from "fast-glob";
|
|
7950
|
-
import
|
|
7951
|
-
import
|
|
7988
|
+
import path29 from "path";
|
|
7989
|
+
import fs24 from "fs";
|
|
7952
7990
|
var TOOL_PATTERNS = ["src/tools/**/*.ts"];
|
|
7953
7991
|
var TOOL_EXPORT_RE = new RegExp(
|
|
7954
7992
|
String.raw`export\s+(?:async\s+)?(?:function\s+|const\s+)([A-Za-z_$][\w$]*)\s*(?:\(|=)`,
|
|
@@ -7998,8 +8036,8 @@ async function scanTools(rootDir, patterns) {
|
|
|
7998
8036
|
if (fileName !== "handler.ts" && fileName !== "handler.js") {
|
|
7999
8037
|
continue;
|
|
8000
8038
|
}
|
|
8001
|
-
const absPath =
|
|
8002
|
-
const source = await
|
|
8039
|
+
const absPath = path29.resolve(rootDir, normalizedFile);
|
|
8040
|
+
const source = await fs24.promises.readFile(absPath, "utf8").catch(() => "");
|
|
8003
8041
|
const exportNames = extractToolExportsFromSource(source);
|
|
8004
8042
|
const namespace = filePathToToolNamespace(normalizedFile);
|
|
8005
8043
|
for (const fnName of exportNames) {
|
|
@@ -8023,8 +8061,8 @@ async function scanTools(rootDir, patterns) {
|
|
|
8023
8061
|
|
|
8024
8062
|
// src/agents/scanAgents.ts
|
|
8025
8063
|
import fg5 from "fast-glob";
|
|
8026
|
-
import
|
|
8027
|
-
import
|
|
8064
|
+
import path30 from "path";
|
|
8065
|
+
import fs25 from "fs";
|
|
8028
8066
|
var DEFAULT_AGENT_PATTERNS = ["src/agents/**/handler.ts"];
|
|
8029
8067
|
var RUN_EXPORT_RE = /export\s+(?:async\s+)?(?:function\s+|const\s+)run\b/;
|
|
8030
8068
|
function extractAgentNameFromPath(filePath) {
|
|
@@ -8056,8 +8094,8 @@ async function scanAgents(rootDir, patterns) {
|
|
|
8056
8094
|
if (fileName !== "handler.ts" && fileName !== "handler.js") {
|
|
8057
8095
|
continue;
|
|
8058
8096
|
}
|
|
8059
|
-
const absPath =
|
|
8060
|
-
const source = await
|
|
8097
|
+
const absPath = path30.resolve(rootDir, normalizedFile);
|
|
8098
|
+
const source = await fs25.promises.readFile(absPath, "utf8").catch(() => "");
|
|
8061
8099
|
const { hasRun } = detectAgentExports(source);
|
|
8062
8100
|
const name = extractAgentNameFromPath(normalizedFile);
|
|
8063
8101
|
const prevFile = seen.get(name);
|
|
@@ -8128,7 +8166,7 @@ async function createDevApp(options) {
|
|
|
8128
8166
|
if (!suppressProgramInvalidation) invalidateProgramCache();
|
|
8129
8167
|
const tasks = await scanTasks(ctx.rootDir, TASK_PATTERNS);
|
|
8130
8168
|
for (const task of tasks) {
|
|
8131
|
-
await ensureCompiled(
|
|
8169
|
+
await ensureCompiled(path31.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
|
|
8132
8170
|
}
|
|
8133
8171
|
await generateTaskArtifacts(tasks, ctx.rootDir, ctx.dist);
|
|
8134
8172
|
ctx.taskQueue.invalidateModules();
|