@faapi/faapi 1.2.0 → 1.2.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/index.js CHANGED
@@ -1505,9 +1505,14 @@ function getVitestImportActual() {
1505
1505
  if (typeof vi?.importActual !== "function") return void 0;
1506
1506
  return vi.importActual.bind(vi);
1507
1507
  }
1508
- async function importWithCacheBust(filePath) {
1508
+ async function importWithCacheBust(filePath, bustViteCache = false) {
1509
1509
  const importActual = getVitestImportActual();
1510
1510
  if (importActual) {
1511
+ if (bustViteCache) {
1512
+ let url2 = pathToFileURL(filePath).href;
1513
+ url2 += `?t=${Date.now()}`;
1514
+ return await import(url2);
1515
+ }
1511
1516
  return await importActual(filePath);
1512
1517
  }
1513
1518
  let url = pathToFileURL(filePath).href;
@@ -2134,7 +2139,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
2134
2139
  // src/testServer.ts
2135
2140
  import path12 from "path";
2136
2141
  import os from "os";
2137
- import fs9 from "fs/promises";
2142
+ import fs11 from "fs/promises";
2138
2143
 
2139
2144
  // src/router/scanRoutes.ts
2140
2145
  import fg from "fast-glob";
@@ -2398,205 +2403,6 @@ init_generateSchemaFiles();
2398
2403
 
2399
2404
  // src/validator/validateInput.ts
2400
2405
  init_schemaName();
2401
- var moduleCache = /* @__PURE__ */ new Map();
2402
- function invalidateSchemaCache() {
2403
- moduleCache.clear();
2404
- }
2405
- async function loadSchemaModule(schemaPath) {
2406
- let mod = moduleCache.get(schemaPath);
2407
- if (!mod) {
2408
- mod = await importWithCacheBust(schemaPath);
2409
- moduleCache.set(schemaPath, mod);
2410
- }
2411
- return mod;
2412
- }
2413
- async function validateInput(schemaPath, method, inputType, input) {
2414
- const schemaName = getSchemaName(method, inputType);
2415
- const schemaKey = `${schemaName}Schema`;
2416
- let mod;
2417
- try {
2418
- mod = await loadSchemaModule(schemaPath);
2419
- } catch (err) {
2420
- const reason = err instanceof Error ? err.message : String(err);
2421
- throw new InternalError(`Schema \u6A21\u5757\u52A0\u8F7D\u5931\u8D25: ${schemaPath}: ${reason}`);
2422
- }
2423
- const schema = mod[schemaKey];
2424
- if (schema === void 0 || schema === null) {
2425
- const data = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
2426
- return { valid: true, issues: [], data };
2427
- }
2428
- if (typeof schema !== "object" || typeof schema.safeParse !== "function") {
2429
- throw new InternalError(`Schema \u4E0D\u662F\u6709\u6548\u7684 zod schema: ${schemaPath}#${schemaName}`);
2430
- }
2431
- const inputObj = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
2432
- const zodSchema = schema;
2433
- const result = zodSchema.safeParse(inputObj);
2434
- if (result.success) {
2435
- const data = typeof result.data === "object" && result.data !== null && !Array.isArray(result.data) ? result.data : {};
2436
- return { valid: true, issues: [], data };
2437
- }
2438
- const issues = mapZodIssues(result.error);
2439
- return { valid: false, issues, data: inputObj };
2440
- }
2441
- function mapZodIssues(error) {
2442
- return error.issues.map((issue) => {
2443
- const code = mapZodCode(issue.code, issue.message);
2444
- const path15 = issue.path.map(String).join(".") || "";
2445
- return {
2446
- path: path15,
2447
- code,
2448
- expected: issue.expected ?? mapExpectedFromMessage(issue.message),
2449
- received: issue.received ?? mapReceivedFromMessage(issue.message),
2450
- message: issue.message
2451
- };
2452
- });
2453
- }
2454
- function mapZodCode(zodCode, message) {
2455
- switch (zodCode) {
2456
- case "invalid_type":
2457
- case "invalid_union":
2458
- case "invalid_union_discriminator":
2459
- return "TYPE_MISMATCH";
2460
- case "unrecognized_keys":
2461
- return "INVALID_FORMAT";
2462
- case "invalid_value":
2463
- case "invalid_string":
2464
- case "too_small":
2465
- case "too_big":
2466
- case "invalid_intersection_types":
2467
- case "not_multiple_of":
2468
- return "INVALID_VALUE";
2469
- case "custom":
2470
- return "INVALID_VALUE";
2471
- default:
2472
- if (message.includes("Required") || message.includes("required")) {
2473
- return "MISSING_FIELD";
2474
- }
2475
- return "INVALID_VALUE";
2476
- }
2477
- }
2478
- function mapExpectedFromMessage(message) {
2479
- const match = message.match(/Expected\s+(\w+)/i);
2480
- return match ? match[1].toLowerCase() : "unknown";
2481
- }
2482
- function mapReceivedFromMessage(message) {
2483
- const match = message.match(/received\s+(\w+)/i);
2484
- return match ? match[1].toLowerCase() : "unknown";
2485
- }
2486
-
2487
- // src/server/createServer.ts
2488
- import {
2489
- createServer as createHttpServer
2490
- } from "http";
2491
- import { createSecureServer as createHttp2SecureServer } from "http2";
2492
- import { readFileSync } from "fs";
2493
- import { Readable as Readable2 } from "stream";
2494
- import path11 from "path";
2495
-
2496
- // src/router/matchRoute.ts
2497
- function matchRoute(routes, method, path15) {
2498
- for (const route of routes) {
2499
- if (route.method !== method) {
2500
- continue;
2501
- }
2502
- if (!route.isDynamic) {
2503
- if (route.urlPath === path15) {
2504
- return { route, params: {} };
2505
- }
2506
- continue;
2507
- }
2508
- const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
2509
- if (params !== null) {
2510
- return { route, params };
2511
- }
2512
- }
2513
- return null;
2514
- }
2515
- function matchWsRoute(wsRoutes, path15) {
2516
- for (const route of wsRoutes) {
2517
- if (!route.isDynamic) {
2518
- if (route.urlPath === path15) {
2519
- return { route, params: {} };
2520
- }
2521
- continue;
2522
- }
2523
- const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
2524
- if (params !== null) {
2525
- return { route, params };
2526
- }
2527
- }
2528
- return null;
2529
- }
2530
- function matchDynamicPath(pattern, path15, paramNames, isCatchAll) {
2531
- const patternSegments = pattern.split("/").filter(Boolean);
2532
- const pathSegments = path15.split("/").filter(Boolean);
2533
- if (isCatchAll) {
2534
- const nonCatchAllCount = patternSegments.length - 1;
2535
- if (pathSegments.length <= nonCatchAllCount) {
2536
- return null;
2537
- }
2538
- const params2 = {};
2539
- for (let i = 0; i < nonCatchAllCount; i++) {
2540
- const patternSeg = patternSegments[i];
2541
- const pathSeg = pathSegments[i];
2542
- if (patternSeg.startsWith(":")) {
2543
- const paramName = patternSeg.slice(1);
2544
- params2[paramName] = pathSeg;
2545
- } else if (patternSeg !== pathSeg) {
2546
- return null;
2547
- }
2548
- }
2549
- const catchAllValue = pathSegments.slice(nonCatchAllCount).join("/");
2550
- const catchAllParamName = patternSegments[nonCatchAllCount].slice(4);
2551
- params2[catchAllParamName] = catchAllValue;
2552
- if (Object.keys(params2).length !== paramNames.length) {
2553
- return null;
2554
- }
2555
- return params2;
2556
- }
2557
- if (patternSegments.length !== pathSegments.length) {
2558
- return null;
2559
- }
2560
- const params = {};
2561
- for (let i = 0; i < patternSegments.length; i++) {
2562
- const patternSeg = patternSegments[i];
2563
- const pathSeg = pathSegments[i];
2564
- if (patternSeg.startsWith(":")) {
2565
- const paramName = patternSeg.slice(1);
2566
- params[paramName] = pathSeg;
2567
- } else if (patternSeg !== pathSeg) {
2568
- return null;
2569
- }
2570
- }
2571
- if (Object.keys(params).length !== paramNames.length) {
2572
- return null;
2573
- }
2574
- return params;
2575
- }
2576
-
2577
- // src/loader/resolveExports.ts
2578
- function resolveExport(module, exportName) {
2579
- if (exportName in module && typeof module[exportName] !== "undefined") {
2580
- return module[exportName];
2581
- }
2582
- const defaultExport = module.default;
2583
- if (defaultExport !== null && typeof defaultExport === "object") {
2584
- const value = defaultExport[exportName];
2585
- if (value !== void 0) {
2586
- return value;
2587
- }
2588
- }
2589
- return void 0;
2590
- }
2591
-
2592
- // src/loader/validateRouteModule.ts
2593
- function validateRouteModule(value, method, filePath) {
2594
- if (typeof value !== "function") {
2595
- throw new Error(
2596
- `Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
2597
- );
2598
- }
2599
- }
2600
2406
 
2601
2407
  // src/cli/compileOnDemand.ts
2602
2408
  import path9 from "path";
@@ -2945,32 +2751,232 @@ function getDevDist() {
2945
2751
  return devDistDir;
2946
2752
  }
2947
2753
 
2948
- // src/loader/loadRouteModule.ts
2949
- async function loadRouteModule(filePath, method, rootDir) {
2950
- let module;
2754
+ // src/validator/validateInput.ts
2755
+ var moduleCache = /* @__PURE__ */ new Map();
2756
+ function invalidateSchemaCache() {
2757
+ moduleCache.clear();
2758
+ }
2759
+ async function loadSchemaModule(schemaPath) {
2760
+ let mod = moduleCache.get(schemaPath);
2761
+ if (!mod) {
2762
+ mod = await importWithCacheBust(schemaPath, isDevOnDemandEnabled());
2763
+ moduleCache.set(schemaPath, mod);
2764
+ }
2765
+ return mod;
2766
+ }
2767
+ async function validateInput(schemaPath, method, inputType, input) {
2768
+ const schemaName = getSchemaName(method, inputType);
2769
+ const schemaKey = `${schemaName}Schema`;
2770
+ let mod;
2951
2771
  try {
2952
- module = await importWithCacheBust(filePath);
2772
+ mod = await loadSchemaModule(schemaPath);
2953
2773
  } catch (err) {
2954
- if (isDevOnDemandEnabled() && rootDir) {
2955
- const dist = getDevDist();
2956
- if (dist) {
2957
- const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
2774
+ const reason = err instanceof Error ? err.message : String(err);
2775
+ throw new InternalError(`Schema \u6A21\u5757\u52A0\u8F7D\u5931\u8D25: ${schemaPath}: ${reason}`);
2776
+ }
2777
+ const schema = mod[schemaKey];
2778
+ if (schema === void 0 || schema === null) {
2779
+ const data = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
2780
+ return { valid: true, issues: [], data };
2781
+ }
2782
+ if (typeof schema !== "object" || typeof schema.safeParse !== "function") {
2783
+ throw new InternalError(`Schema \u4E0D\u662F\u6709\u6548\u7684 zod schema: ${schemaPath}#${schemaName}`);
2784
+ }
2785
+ const inputObj = typeof input === "object" && input !== null && !Array.isArray(input) ? input : {};
2786
+ const zodSchema = schema;
2787
+ const result = zodSchema.safeParse(inputObj);
2788
+ if (result.success) {
2789
+ const data = typeof result.data === "object" && result.data !== null && !Array.isArray(result.data) ? result.data : {};
2790
+ return { valid: true, issues: [], data };
2791
+ }
2792
+ const issues = mapZodIssues(result.error);
2793
+ return { valid: false, issues, data: inputObj };
2794
+ }
2795
+ function mapZodIssues(error) {
2796
+ return error.issues.map((issue) => {
2797
+ const code = mapZodCode(issue.code, issue.message);
2798
+ const path15 = issue.path.map(String).join(".") || "";
2799
+ return {
2800
+ path: path15,
2801
+ code,
2802
+ expected: issue.expected ?? mapExpectedFromMessage(issue.message),
2803
+ received: issue.received ?? mapReceivedFromMessage(issue.message),
2804
+ message: issue.message
2805
+ };
2806
+ });
2807
+ }
2808
+ function mapZodCode(zodCode, message) {
2809
+ switch (zodCode) {
2810
+ case "invalid_type":
2811
+ case "invalid_union":
2812
+ case "invalid_union_discriminator":
2813
+ return "TYPE_MISMATCH";
2814
+ case "unrecognized_keys":
2815
+ return "INVALID_FORMAT";
2816
+ case "invalid_value":
2817
+ case "invalid_string":
2818
+ case "too_small":
2819
+ case "too_big":
2820
+ case "invalid_intersection_types":
2821
+ case "not_multiple_of":
2822
+ return "INVALID_VALUE";
2823
+ case "custom":
2824
+ return "INVALID_VALUE";
2825
+ default:
2826
+ if (message.includes("Required") || message.includes("required")) {
2827
+ return "MISSING_FIELD";
2828
+ }
2829
+ return "INVALID_VALUE";
2830
+ }
2831
+ }
2832
+ function mapExpectedFromMessage(message) {
2833
+ const match = message.match(/Expected\s+(\w+)/i);
2834
+ return match ? match[1].toLowerCase() : "unknown";
2835
+ }
2836
+ function mapReceivedFromMessage(message) {
2837
+ const match = message.match(/received\s+(\w+)/i);
2838
+ return match ? match[1].toLowerCase() : "unknown";
2839
+ }
2840
+
2841
+ // src/server/createServer.ts
2842
+ import {
2843
+ createServer as createHttpServer
2844
+ } from "http";
2845
+ import { createSecureServer as createHttp2SecureServer } from "http2";
2846
+ import { readFileSync } from "fs";
2847
+ import { Readable as Readable2 } from "stream";
2848
+ import path11 from "path";
2849
+
2850
+ // src/router/matchRoute.ts
2851
+ function matchRoute(routes, method, path15) {
2852
+ for (const route of routes) {
2853
+ if (route.method !== method) {
2854
+ continue;
2855
+ }
2856
+ if (!route.isDynamic) {
2857
+ if (route.urlPath === path15) {
2858
+ return { route, params: {} };
2859
+ }
2860
+ continue;
2861
+ }
2862
+ const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
2863
+ if (params !== null) {
2864
+ return { route, params };
2865
+ }
2866
+ }
2867
+ return null;
2868
+ }
2869
+ function matchWsRoute(wsRoutes, path15) {
2870
+ for (const route of wsRoutes) {
2871
+ if (!route.isDynamic) {
2872
+ if (route.urlPath === path15) {
2873
+ return { route, params: {} };
2874
+ }
2875
+ continue;
2876
+ }
2877
+ const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
2878
+ if (params !== null) {
2879
+ return { route, params };
2880
+ }
2881
+ }
2882
+ return null;
2883
+ }
2884
+ function matchDynamicPath(pattern, path15, paramNames, isCatchAll) {
2885
+ const patternSegments = pattern.split("/").filter(Boolean);
2886
+ const pathSegments = path15.split("/").filter(Boolean);
2887
+ if (isCatchAll) {
2888
+ const nonCatchAllCount = patternSegments.length - 1;
2889
+ if (pathSegments.length <= nonCatchAllCount) {
2890
+ return null;
2891
+ }
2892
+ const params2 = {};
2893
+ for (let i = 0; i < nonCatchAllCount; i++) {
2894
+ const patternSeg = patternSegments[i];
2895
+ const pathSeg = pathSegments[i];
2896
+ if (patternSeg.startsWith(":")) {
2897
+ const paramName = patternSeg.slice(1);
2898
+ params2[paramName] = pathSeg;
2899
+ } else if (patternSeg !== pathSeg) {
2900
+ return null;
2901
+ }
2902
+ }
2903
+ const catchAllValue = pathSegments.slice(nonCatchAllCount).join("/");
2904
+ const catchAllParamName = patternSegments[nonCatchAllCount].slice(4);
2905
+ params2[catchAllParamName] = catchAllValue;
2906
+ if (Object.keys(params2).length !== paramNames.length) {
2907
+ return null;
2908
+ }
2909
+ return params2;
2910
+ }
2911
+ if (patternSegments.length !== pathSegments.length) {
2912
+ return null;
2913
+ }
2914
+ const params = {};
2915
+ for (let i = 0; i < patternSegments.length; i++) {
2916
+ const patternSeg = patternSegments[i];
2917
+ const pathSeg = pathSegments[i];
2918
+ if (patternSeg.startsWith(":")) {
2919
+ const paramName = patternSeg.slice(1);
2920
+ params[paramName] = pathSeg;
2921
+ } else if (patternSeg !== pathSeg) {
2922
+ return null;
2923
+ }
2924
+ }
2925
+ if (Object.keys(params).length !== paramNames.length) {
2926
+ return null;
2927
+ }
2928
+ return params;
2929
+ }
2930
+
2931
+ // src/loader/loadRouteModule.ts
2932
+ import fs9 from "fs";
2933
+
2934
+ // src/loader/resolveExports.ts
2935
+ function resolveExport(module, exportName) {
2936
+ if (exportName in module && typeof module[exportName] !== "undefined") {
2937
+ return module[exportName];
2938
+ }
2939
+ const defaultExport = module.default;
2940
+ if (defaultExport !== null && typeof defaultExport === "object") {
2941
+ const value = defaultExport[exportName];
2942
+ if (value !== void 0) {
2943
+ return value;
2944
+ }
2945
+ }
2946
+ return void 0;
2947
+ }
2948
+
2949
+ // src/loader/validateRouteModule.ts
2950
+ function validateRouteModule(value, method, filePath) {
2951
+ if (typeof value !== "function") {
2952
+ throw new Error(
2953
+ `Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
2954
+ );
2955
+ }
2956
+ }
2957
+
2958
+ // src/loader/loadRouteModule.ts
2959
+ async function loadRouteModule(filePath, method, rootDir) {
2960
+ if (isDevOnDemandEnabled() && rootDir) {
2961
+ const dist = getDevDist();
2962
+ if (dist) {
2963
+ const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
2964
+ if (sourcePath && fs9.existsSync(sourcePath)) {
2958
2965
  try {
2959
- const compiled = await ensureCompiled(sourcePath, rootDir, dist);
2960
- if (compiled) {
2961
- module = await importWithCacheBust(filePath);
2962
- const handler2 = resolveExport(module, method);
2963
- validateRouteModule(handler2, method, filePath);
2964
- return { handler: handler2, method };
2965
- }
2966
+ await ensureCompiled(sourcePath, rootDir, dist);
2966
2967
  } catch (compileErr) {
2967
- const compileReason = compileErr instanceof Error ? compileErr.message : String(compileErr);
2968
- throw new Error(`Failed to compile route module "${sourcePath}": ${compileReason}`, {
2968
+ const reason = compileErr instanceof Error ? compileErr.message : String(compileErr);
2969
+ throw new Error(`Failed to compile route module "${sourcePath}": ${reason}`, {
2969
2970
  cause: compileErr
2970
2971
  });
2971
2972
  }
2972
2973
  }
2973
2974
  }
2975
+ }
2976
+ let module;
2977
+ try {
2978
+ module = await importWithCacheBust(filePath, isDevOnDemandEnabled());
2979
+ } catch (err) {
2974
2980
  const reason = err instanceof Error ? err.message : String(err);
2975
2981
  throw new Error(`Failed to load route module "${filePath}": ${reason}`, { cause: err });
2976
2982
  }
@@ -3105,6 +3111,7 @@ function getClientIp(req) {
3105
3111
  }
3106
3112
 
3107
3113
  // src/server/handleWsUpgrade.ts
3114
+ import fs10 from "fs";
3108
3115
  import { WebSocketServer, WebSocket } from "ws";
3109
3116
  import path10 from "path";
3110
3117
 
@@ -3204,24 +3211,16 @@ function getPathname(req) {
3204
3211
  return idx >= 0 ? url.slice(0, idx) : url;
3205
3212
  }
3206
3213
  async function loadWsHandler(filePath, ctx, rootDir) {
3207
- let module;
3208
- try {
3209
- module = await importWithCacheBust(filePath);
3210
- } catch (err) {
3211
- if (isDevOnDemandEnabled() && rootDir) {
3212
- const dist = getDevDist();
3213
- if (dist) {
3214
- const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3215
- const compiled = await ensureCompiled(sourcePath, rootDir, dist);
3216
- if (compiled) {
3217
- module = await importWithCacheBust(filePath);
3218
- }
3214
+ if (isDevOnDemandEnabled() && rootDir) {
3215
+ const dist = getDevDist();
3216
+ if (dist) {
3217
+ const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
3218
+ if (sourcePath && fs10.existsSync(sourcePath)) {
3219
+ await ensureCompiled(sourcePath, rootDir, dist);
3219
3220
  }
3220
3221
  }
3221
- if (!module) {
3222
- throw err;
3223
- }
3224
3222
  }
3223
+ const module = await importWithCacheBust(filePath, isDevOnDemandEnabled());
3225
3224
  const handler = module["WS"];
3226
3225
  if (typeof handler !== "function") {
3227
3226
  throw new Error(`WS export not found in ${filePath}`);
@@ -3569,7 +3568,7 @@ async function createTestServer(options) {
3569
3568
  } = options;
3570
3569
  const { routes, wsRoutes } = await scanRoutes(rootDir, patterns);
3571
3570
  const sorted = sortRoutes(routes);
3572
- const schemaDist = dist ? path12.isAbsolute(dist) ? dist : path12.resolve(rootDir, dist) : await fs9.mkdtemp(path12.join(os.tmpdir(), "faapi-test-schema-"));
3571
+ const schemaDist = dist ? path12.isAbsolute(dist) ? dist : path12.resolve(rootDir, dist) : await fs11.mkdtemp(path12.join(os.tmpdir(), "faapi-test-schema-"));
3573
3572
  await generateSchemaFiles(sorted, rootDir, schemaDist);
3574
3573
  const { server } = createServer({
3575
3574
  routes: sorted,
@@ -3602,7 +3601,7 @@ async function createTestServer(options) {
3602
3601
  await new Promise((resolve) => {
3603
3602
  server.close(() => resolve());
3604
3603
  });
3605
- await fs9.rm(schemaDist, { recursive: true, force: true }).catch(() => {
3604
+ await fs11.rm(schemaDist, { recursive: true, force: true }).catch(() => {
3606
3605
  });
3607
3606
  invalidateSchemaCache();
3608
3607
  }
@@ -3744,7 +3743,7 @@ async function connectWs(baseUrl, pathname, options = {}) {
3744
3743
  }
3745
3744
 
3746
3745
  // src/cli/createAppCore.ts
3747
- import fs11 from "fs";
3746
+ import fs13 from "fs";
3748
3747
  import path14 from "path";
3749
3748
  import { PassThrough } from "stream";
3750
3749
 
@@ -3802,7 +3801,7 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
3802
3801
  }
3803
3802
 
3804
3803
  // src/cli/generateRoutes.ts
3805
- import fs10 from "fs";
3804
+ import fs12 from "fs";
3806
3805
  import path13 from "path";
3807
3806
  async function hydrateRoutes(manifest) {
3808
3807
  const hydrateRoute = (serialized) => ({
@@ -3910,7 +3909,7 @@ async function createAppBase(options) {
3910
3909
  const rootDir = options?.rootDir ?? process.cwd();
3911
3910
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
3912
3911
  const routesPath = path14.resolve(rootDir, dist, ROUTES_FILE);
3913
- if (!fs11.existsSync(routesPath)) {
3912
+ if (!fs13.existsSync(routesPath)) {
3914
3913
  throw new Error(
3915
3914
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
3916
3915
  );
@@ -4083,6 +4082,10 @@ async function createAppBase(options) {
4083
4082
  if (config?.lifecycle?.onClose) {
4084
4083
  await config.lifecycle.onClose({ rootDir, routes: sorted, server });
4085
4084
  }
4085
+ if (!server.listening) {
4086
+ app.server = null;
4087
+ return;
4088
+ }
4086
4089
  return new Promise((resolve) => {
4087
4090
  server.close((err) => {
4088
4091
  if (err) console.error("Error closing server:", err);