@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/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(path31) {
2050
- super("ROUTE_NOT_FOUND", `Route not found: ${path31}`, 404);
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, path31, allowedMethods) {
2056
- super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path31}`, 405);
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: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
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 fs21 from "fs";
4357
- import path26 from "path";
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 path19 from "path";
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, path31) {
4464
+ function matchRoute(routes, method, path32) {
4464
4465
  const index = getHttpIndex(routes);
4465
4466
  const upper = method.toUpperCase();
4466
- const hit = matchByMethod(index, upper, path31);
4467
+ const hit = matchByMethod(index, upper, path32);
4467
4468
  if (hit) return hit;
4468
4469
  if (upper === "HEAD") {
4469
- return matchByMethod(index, "GET", path31);
4470
+ return matchByMethod(index, "GET", path32);
4470
4471
  }
4471
4472
  return null;
4472
4473
  }
4473
- function matchByMethod(index, method, path31) {
4474
- const staticHit = index.static.get(`${method}|${path31}`);
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 = path31.split("/").filter(Boolean);
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, path31) {
4495
+ function matchWsRoute(wsRoutes, path32) {
4495
4496
  const index = getWsIndex(wsRoutes);
4496
- const staticHit = index.static.get(path31);
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, path31, route.paramNames, route.isCatchAll);
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, path31) {
4509
+ function findAllowedMethods(routes, path32) {
4509
4510
  const index = getHttpIndex(routes);
4510
4511
  const methods = /* @__PURE__ */ new Set();
4511
- const staticMethods = index.methodsByStaticPath.get(path31);
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 = path31.split("/").filter(Boolean);
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, path31, paramNames, isCatchAll) {
4537
+ function matchDynamicPath(pattern, path32, paramNames, isCatchAll) {
4537
4538
  return matchSegments(
4538
4539
  pattern.split("/").filter(Boolean),
4539
- path31.split("/").filter(Boolean),
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(request, new URL(request.url), params, config, ip, registries);
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 path31 = issue.path.map(String).join(".") || "";
5679
+ const path32 = issue.path.map(String).join(".") || "";
5668
5680
  return {
5669
- path: path31,
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 fs16 from "fs";
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 && fs16.existsSync(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(request, params, config, getClientIp(req, trustedProxy), registries);
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 (fs16.existsSync(schemaPath)) {
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 = path18.resolve(rootDir, route.filePath);
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: path19.resolve(rootDir, route.filePath),
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 fs17 from "fs";
6685
- import path20 from "path";
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 path22 from "path";
6730
- import fs19 from "fs";
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 path21 from "path";
6735
- import fs18 from "fs";
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 = path21.resolve(rootDir, dist);
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 = path21.resolve(rootDir, "src");
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 = path22.isAbsolute(specifier) ? specifier : path22.resolve(baseDir, specifier);
6836
- if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs19.existsSync(base)) return 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 (fs19.existsSync(base + ext)) return base + ext;
6870
+ if (fs20.existsSync(base + ext)) return base + ext;
6839
6871
  }
6840
6872
  for (const indexExt of ["/index.ts", "/index.js"]) {
6841
- if (fs19.existsSync(base + indexExt)) return base + indexExt;
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 = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
6878
+ let rel = path23.relative(baseDir, sourcePath).replace(/\\/g, "/");
6847
6879
  if (rel.startsWith("src/")) rel = rel.slice(4);
6848
- return path22.resolve(distDir, rel.replace(TS_EXTS, ".js"));
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 && !path22.isAbsolute(specifier)) {
6884
+ if (!isRelative && !path23.isAbsolute(specifier)) {
6853
6885
  return import(specifier);
6854
6886
  }
6855
- const distDir = dist ? path22.resolve(baseDir, dist) : null;
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 && fs19.existsSync(productPath) && (sourcePath === null || isProductFresh(sourcePath, productPath))) {
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 = path22.relative(baseDir, sourcePath).replace(/\\/g, "/");
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 && fs19.existsSync(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) => path22.isAbsolute(c) ? c : path22.join(baseDir, 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 fs20 from "fs";
6911
- import path25 from "path";
6942
+ import fs21 from "fs";
6943
+ import path26 from "path";
6912
6944
 
6913
6945
  // src/cli/generateAgentArtifacts.ts
6914
- import path23 from "path";
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) => path23.resolve(rootDir, m.filePath)));
7226
+ const programByFile = createPrograms(agents.map((m) => path24.resolve(rootDir, m.filePath)));
7195
7227
  for (const manifest of agents) {
7196
- const absPath = path23.resolve(rootDir, manifest.filePath);
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 = path23.resolve(rootDir, dist, AGENTS_FILE);
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 path24 from "path";
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 = path24.resolve(rootDir, task.filePath);
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) => path24.resolve(rootDir, m.filePath)));
7346
+ const programByFile = createPrograms(manifests.map((m) => path25.resolve(rootDir, m.filePath)));
7315
7347
  for (const manifest of manifests) {
7316
- const absPath = path24.resolve(rootDir, manifest.filePath);
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 = path24.resolve(rootDir, dist, TASKS_FILE);
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 = path25.resolve(rootDir, dist, TOOLS_FILE2);
7350
- if (!fs20.existsSync(toolsPath)) {
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 = path25.resolve(rootDir, dist, AGENTS_FILE2);
7360
- if (!fs20.existsSync(agentsPath)) {
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 = path25.resolve(rootDir, dist, TASKS_FILE);
7376
- if (!fs20.existsSync(tasksPath)) {
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 routesPath = path26.resolve(rootDir, dist, ROUTES_FILE);
7569
- if (!fs21.existsSync(routesPath)) {
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 path30 from "path";
7818
+ import path31 from "path";
7781
7819
 
7782
7820
  // src/router/scanRoutes.ts
7783
7821
  import fg3 from "fast-glob";
7784
- import path27 from "path";
7785
- import fs22 from "fs";
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(path31) {
7793
- if (!path31) return "";
7794
- let result = path31.replace(/\\/g, "/");
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 = path27.dirname(routeFilePath);
7856
- const resolvedRoot = path27.resolve(rootDir);
7893
+ const routeDir = path28.dirname(routeFilePath);
7894
+ const resolvedRoot = path28.resolve(rootDir);
7857
7895
  const paths = [];
7858
- let currentDir = path27.resolve(rootDir, routeDir);
7896
+ let currentDir = path28.resolve(rootDir, routeDir);
7859
7897
  while (true) {
7860
7898
  if (dist) {
7861
- const mwTsPath = path27.join(currentDir, "middlewares.ts");
7862
- const mwJsPath = path27.join(currentDir, "middlewares.js");
7863
- const absTsPath = path27.resolve(rootDir, mwTsPath);
7864
- const absJsPath = path27.resolve(rootDir, mwJsPath);
7865
- const absMwPath = fs22.existsSync(absTsPath) ? absTsPath : fs22.existsSync(absJsPath) ? absJsPath : null;
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 = path27.relative(rootDir, absMwPath);
7868
- const prodAbsPath = path27.resolve(rootDir, toProdFilePath(relMwPath, dist));
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 = path27.join(currentDir, `middlewares${ext}`);
7874
- const absMwPath = path27.resolve(rootDir, mwPath);
7875
- if (fs22.existsSync(absMwPath)) {
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 = path27.dirname(currentDir);
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 = path27.resolve(rootDir, normalizedFile);
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 fs22.promises.readFile(absPath, "utf8").catch(() => "");
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 path28 from "path";
7951
- import fs23 from "fs";
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 = path28.resolve(rootDir, normalizedFile);
8002
- const source = await fs23.promises.readFile(absPath, "utf8").catch(() => "");
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 path29 from "path";
8027
- import fs24 from "fs";
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 = path29.resolve(rootDir, normalizedFile);
8060
- const source = await fs24.promises.readFile(absPath, "utf8").catch(() => "");
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(path30.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
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();