@faapi/faapi 0.0.0-canary.4e89b9b → 0.0.0-canary.856e11f
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 +302 -255
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +40 -6
- package/dist/index.js +140 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,17 @@ interface SseEvent {
|
|
|
54
54
|
interface SseWriter {
|
|
55
55
|
/** 推送一个 SSE 事件 */
|
|
56
56
|
send(event: SseEvent): void;
|
|
57
|
+
/**
|
|
58
|
+
* 直接写入原始字节/字符串,不做任何 SSE 序列化
|
|
59
|
+
*
|
|
60
|
+
* 用于透传上游已有的 SSE 原文(如 LLM 中转平台逐 chunk 转发 OpenAI 响应)。
|
|
61
|
+
* 调用方负责保证内容符合 HTML5 SSE 规范;`send` 会再次加 `data: ` 前缀,
|
|
62
|
+
* 不适用于原文透传场景。
|
|
63
|
+
*
|
|
64
|
+
* 接受 string 或 Uint8Array(Buffer 是 Uint8Array 子类,自然兼容)。
|
|
65
|
+
* 与 `send` 一致:close/aborted 后静默忽略,不抛错。
|
|
66
|
+
*/
|
|
67
|
+
sendRaw(chunk: string | Uint8Array): void;
|
|
57
68
|
/** 推送一个 error 事件并关闭流(用于流式输出中报错的优雅终止) */
|
|
58
69
|
sendError(error: unknown): void;
|
|
59
70
|
/** 关闭流(多次调用安全) */
|
|
@@ -550,17 +561,19 @@ interface LifecycleContext {
|
|
|
550
561
|
* } satisfies FaapiConfig;
|
|
551
562
|
* ```
|
|
552
563
|
*
|
|
553
|
-
*
|
|
564
|
+
* 自定义业务配置(任意 key):
|
|
554
565
|
* ```ts
|
|
555
566
|
* import type { FaapiConfig } from '@faapi/faapi';
|
|
556
567
|
* export default {
|
|
557
568
|
* cors: { origin: '*' },
|
|
558
|
-
* //
|
|
559
|
-
* db: { host: 'localhost', port: 5432 },
|
|
569
|
+
* // 通过 process.env.XXX 读取 .env 文件加载的环境变量
|
|
570
|
+
* db: { host: process.env.DB_HOST ?? 'localhost', port: 5432 },
|
|
560
571
|
* } satisfies FaapiConfig;
|
|
561
572
|
* ```
|
|
562
573
|
*
|
|
563
|
-
*
|
|
574
|
+
* 多环境差异通过 `.env` 系列文件实现(见 `loadEnv`):
|
|
575
|
+
* - `.env` / `.env.local` / `.env.{env}` / `.env.{env}.local`
|
|
576
|
+
* - 环境由 `NODE_ENV > 'development'` 决定
|
|
564
577
|
*
|
|
565
578
|
* 框架元信息通过环境变量配置(不放在 config 内):
|
|
566
579
|
* - `PORT`:服务端口,默认 3000
|
|
@@ -998,7 +1011,8 @@ declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: stri
|
|
|
998
1011
|
* - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/faapi-config.js`
|
|
999
1012
|
* - prod 模式:`faapi build` 时由 `compileConfig` 生成 `dist/faapi-config.js`
|
|
1000
1013
|
*
|
|
1001
|
-
* 产物由 `compileConfig`
|
|
1014
|
+
* 产物由 `compileConfig` 在构建阶段编译,运行时不读源码、不现场编译。
|
|
1015
|
+
* 环境变量由 `loadEnv` 从 `.env` 系列文件加载到 `process.env`,配置文件中通过 `process.env.XXX` 读取。
|
|
1002
1016
|
*
|
|
1003
1017
|
* - 产物存在 → import 并返回 default
|
|
1004
1018
|
* - 产物不存在但源码有配置文件 → 抛错(强制 rebuild)
|
|
@@ -1010,6 +1024,26 @@ declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: stri
|
|
|
1010
1024
|
*/
|
|
1011
1025
|
declare function loadConfig(rootDir: string, dist: string): Promise<Partial<FaapiConfig> | null>;
|
|
1012
1026
|
|
|
1027
|
+
/**
|
|
1028
|
+
* 加载 `.env` 系列文件到 `process.env`
|
|
1029
|
+
*
|
|
1030
|
+
* 按 Next.js 约定加载四级文件(从低到高):
|
|
1031
|
+
* 1. `.env` — 所有环境共享
|
|
1032
|
+
* 2. `.env.local` — 本地覆盖
|
|
1033
|
+
* 3. `.env.{env}` — 按环境覆盖
|
|
1034
|
+
* 4. `.env.{env}.local` — 按环境本地覆盖
|
|
1035
|
+
*
|
|
1036
|
+
* env 由 `NODE_ENV || 'development'` 决定。调用方应在调 loadEnv 之前自行兜底 NODE_ENV
|
|
1037
|
+
* (dev 设 'development',prod 设 'production')。
|
|
1038
|
+
*
|
|
1039
|
+
* 合并规则:
|
|
1040
|
+
* - 后加载的文件覆盖先加载的同名变量
|
|
1041
|
+
* - **shell 已设置的变量不被覆盖**(`process.env` 已有的值优先)
|
|
1042
|
+
*
|
|
1043
|
+
* @param rootDir 项目根目录(`.env` 文件所在目录)
|
|
1044
|
+
*/
|
|
1045
|
+
declare function loadEnv(rootDir: string): void;
|
|
1046
|
+
|
|
1013
1047
|
declare const VALIDATION_ERROR = "VALIDATION_ERROR";
|
|
1014
1048
|
declare const ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
|
|
1015
1049
|
declare const METHOD_NOT_ALLOWED = "METHOD_NOT_ALLOWED";
|
|
@@ -1181,4 +1215,4 @@ type ProdApp = AppBase;
|
|
|
1181
1215
|
*/
|
|
1182
1216
|
declare function createProdApp(options?: CreateAppOptions): Promise<ProdApp>;
|
|
1183
1217
|
|
|
1184
|
-
export { type ProdApp as App, type CorsOptions, type CreateAppOptions, type DevApp, type FaapiConfig, type FaapiContext, type FaapiContextConfig, FaapiError, type FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, type HelmetOptions, type InjectOptions, type InjectResponse, type Injector, type InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type RouteInfo, type RouteInputSchema, type RouteManifest, RouteNotFoundError, type RouteOutputSchema, type RouteParamSchema, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, type SseEvent, type SseWriter, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, collectRouteSchemaSources, cors, createProdApp as createApp, createContext, createDevApp, createProdApp, createProgram, extractTypeInfo, getInputTypeForMethod, helmet, invalidateProgramCache, invokeHandler, loadConfig, logger, resolveTypeNode };
|
|
1218
|
+
export { type ProdApp as App, type CorsOptions, type CreateAppOptions, type DevApp, type FaapiConfig, type FaapiContext, type FaapiContextConfig, FaapiError, type FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, type HelmetOptions, type InjectOptions, type InjectResponse, type Injector, type InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type RouteInfo, type RouteInputSchema, type RouteManifest, RouteNotFoundError, type RouteOutputSchema, type RouteParamSchema, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, type SseEvent, type SseWriter, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, collectRouteSchemaSources, cors, createProdApp as createApp, createContext, createDevApp, createProdApp, createProgram, extractTypeInfo, getInputTypeForMethod, helmet, invalidateProgramCache, invokeHandler, loadConfig, loadEnv, logger, resolveTypeNode };
|
package/dist/index.js
CHANGED
|
@@ -1043,6 +1043,87 @@ async function loadConfig(rootDir, dist) {
|
|
|
1043
1043
|
return null;
|
|
1044
1044
|
}
|
|
1045
1045
|
|
|
1046
|
+
// src/cli/loadEnv.ts
|
|
1047
|
+
import fs2 from "fs";
|
|
1048
|
+
import path3 from "path";
|
|
1049
|
+
function resolveEnv() {
|
|
1050
|
+
return process.env.NODE_ENV || "development";
|
|
1051
|
+
}
|
|
1052
|
+
function getEnvFiles(env) {
|
|
1053
|
+
return [".env", ".env.local", `.env.${env}`, `.env.${env}.local`];
|
|
1054
|
+
}
|
|
1055
|
+
function parseEnvFile(content, fileVars) {
|
|
1056
|
+
const result = {};
|
|
1057
|
+
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
|
1058
|
+
for (const line of lines) {
|
|
1059
|
+
const trimmed = line.trim();
|
|
1060
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1061
|
+
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(trimmed);
|
|
1062
|
+
if (!match) continue;
|
|
1063
|
+
const [, key, rawValue] = match;
|
|
1064
|
+
const value = parseValue(rawValue, { ...fileVars, ...result });
|
|
1065
|
+
result[key] = value;
|
|
1066
|
+
}
|
|
1067
|
+
return result;
|
|
1068
|
+
}
|
|
1069
|
+
function parseValue(raw, env) {
|
|
1070
|
+
if (raw === "") return "";
|
|
1071
|
+
if (raw[0] === "'") {
|
|
1072
|
+
const end = raw.indexOf("'", 1);
|
|
1073
|
+
return end === -1 ? raw.slice(1) : raw.slice(1, end);
|
|
1074
|
+
}
|
|
1075
|
+
if (raw[0] === '"') {
|
|
1076
|
+
const match = /^"((?:\\.|[^"\\])*)"/.exec(raw);
|
|
1077
|
+
const inner = match ? match[1] : raw.slice(1);
|
|
1078
|
+
return expandEscapesAndVars(inner, env);
|
|
1079
|
+
}
|
|
1080
|
+
const commentMatch = /^(.*?)(\s+#.*)$/.exec(raw);
|
|
1081
|
+
const value = commentMatch ? commentMatch[1] : raw;
|
|
1082
|
+
return value.trim();
|
|
1083
|
+
}
|
|
1084
|
+
function expandEscapesAndVars(str, env) {
|
|
1085
|
+
const escaped = str.replace(/\\(.)/g, (_, ch) => {
|
|
1086
|
+
switch (ch) {
|
|
1087
|
+
case "n":
|
|
1088
|
+
return "\n";
|
|
1089
|
+
case "r":
|
|
1090
|
+
return "\r";
|
|
1091
|
+
case "t":
|
|
1092
|
+
return " ";
|
|
1093
|
+
case "\\":
|
|
1094
|
+
return "\\";
|
|
1095
|
+
case '"':
|
|
1096
|
+
return '"';
|
|
1097
|
+
default:
|
|
1098
|
+
return ch;
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
return escaped.replace(
|
|
1102
|
+
/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
|
|
1103
|
+
(_, braced, plain) => {
|
|
1104
|
+
const varName = braced || plain;
|
|
1105
|
+
return env[varName] ?? process.env[varName] ?? "";
|
|
1106
|
+
}
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
function loadEnv(rootDir) {
|
|
1110
|
+
const env = resolveEnv();
|
|
1111
|
+
const files = getEnvFiles(env);
|
|
1112
|
+
const merged = {};
|
|
1113
|
+
for (const file of files) {
|
|
1114
|
+
const filePath = path3.join(rootDir, file);
|
|
1115
|
+
if (!fs2.existsSync(filePath)) continue;
|
|
1116
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1117
|
+
const parsed = parseEnvFile(content, merged);
|
|
1118
|
+
Object.assign(merged, parsed);
|
|
1119
|
+
}
|
|
1120
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
1121
|
+
if (process.env[key] === void 0) {
|
|
1122
|
+
process.env[key] = value;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1046
1127
|
// src/errors/FaapiError.ts
|
|
1047
1128
|
var FaapiError = class extends Error {
|
|
1048
1129
|
constructor(code, message, statusCode) {
|
|
@@ -1069,14 +1150,14 @@ var ValidationError = class extends FaapiError {
|
|
|
1069
1150
|
issues;
|
|
1070
1151
|
};
|
|
1071
1152
|
var RouteNotFoundError = class extends FaapiError {
|
|
1072
|
-
constructor(
|
|
1073
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
1153
|
+
constructor(path10) {
|
|
1154
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path10}`, 404);
|
|
1074
1155
|
this.name = "RouteNotFoundError";
|
|
1075
1156
|
}
|
|
1076
1157
|
};
|
|
1077
1158
|
var MethodNotAllowedError = class extends FaapiError {
|
|
1078
|
-
constructor(method,
|
|
1079
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
1159
|
+
constructor(method, path10, allowedMethods) {
|
|
1160
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path10}`, 405);
|
|
1080
1161
|
this.allowedMethods = allowedMethods;
|
|
1081
1162
|
this.name = "MethodNotAllowedError";
|
|
1082
1163
|
}
|
|
@@ -1161,6 +1242,11 @@ function createSseWriter() {
|
|
|
1161
1242
|
const text = encodeSseEvent(event);
|
|
1162
1243
|
controller.enqueue(encoder.encode(text));
|
|
1163
1244
|
},
|
|
1245
|
+
sendRaw(chunk) {
|
|
1246
|
+
if (closed || !controller) return;
|
|
1247
|
+
const bytes = typeof chunk === "string" ? encoder.encode(chunk) : chunk;
|
|
1248
|
+
controller.enqueue(bytes);
|
|
1249
|
+
},
|
|
1164
1250
|
sendError(error) {
|
|
1165
1251
|
if (closed || !controller) return;
|
|
1166
1252
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1549,8 +1635,8 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
1549
1635
|
}
|
|
1550
1636
|
|
|
1551
1637
|
// src/cli/createAppCore.ts
|
|
1552
|
-
import
|
|
1553
|
-
import
|
|
1638
|
+
import fs5 from "fs";
|
|
1639
|
+
import path8 from "path";
|
|
1554
1640
|
import { PassThrough } from "stream";
|
|
1555
1641
|
|
|
1556
1642
|
// src/router/sortRoutes.ts
|
|
@@ -1603,45 +1689,45 @@ import {
|
|
|
1603
1689
|
import { createSecureServer as createHttp2SecureServer } from "http2";
|
|
1604
1690
|
import { readFileSync } from "fs";
|
|
1605
1691
|
import { Readable as Readable2 } from "stream";
|
|
1606
|
-
import
|
|
1692
|
+
import path6 from "path";
|
|
1607
1693
|
|
|
1608
1694
|
// src/router/matchRoute.ts
|
|
1609
|
-
function matchRoute(routes, method,
|
|
1695
|
+
function matchRoute(routes, method, path10) {
|
|
1610
1696
|
for (const route of routes) {
|
|
1611
1697
|
if (route.method !== method) {
|
|
1612
1698
|
continue;
|
|
1613
1699
|
}
|
|
1614
1700
|
if (!route.isDynamic) {
|
|
1615
|
-
if (route.urlPath ===
|
|
1701
|
+
if (route.urlPath === path10) {
|
|
1616
1702
|
return { route, params: {} };
|
|
1617
1703
|
}
|
|
1618
1704
|
continue;
|
|
1619
1705
|
}
|
|
1620
|
-
const params = matchDynamicPath(route.urlPath,
|
|
1706
|
+
const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
|
|
1621
1707
|
if (params !== null) {
|
|
1622
1708
|
return { route, params };
|
|
1623
1709
|
}
|
|
1624
1710
|
}
|
|
1625
1711
|
return null;
|
|
1626
1712
|
}
|
|
1627
|
-
function matchWsRoute(wsRoutes,
|
|
1713
|
+
function matchWsRoute(wsRoutes, path10) {
|
|
1628
1714
|
for (const route of wsRoutes) {
|
|
1629
1715
|
if (!route.isDynamic) {
|
|
1630
|
-
if (route.urlPath ===
|
|
1716
|
+
if (route.urlPath === path10) {
|
|
1631
1717
|
return { route, params: {} };
|
|
1632
1718
|
}
|
|
1633
1719
|
continue;
|
|
1634
1720
|
}
|
|
1635
|
-
const params = matchDynamicPath(route.urlPath,
|
|
1721
|
+
const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
|
|
1636
1722
|
if (params !== null) {
|
|
1637
1723
|
return { route, params };
|
|
1638
1724
|
}
|
|
1639
1725
|
}
|
|
1640
1726
|
return null;
|
|
1641
1727
|
}
|
|
1642
|
-
function matchDynamicPath(pattern,
|
|
1728
|
+
function matchDynamicPath(pattern, path10, paramNames, isCatchAll) {
|
|
1643
1729
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
1644
|
-
const pathSegments =
|
|
1730
|
+
const pathSegments = path10.split("/").filter(Boolean);
|
|
1645
1731
|
if (isCatchAll) {
|
|
1646
1732
|
const nonCatchAllCount = patternSegments.length - 1;
|
|
1647
1733
|
if (pathSegments.length <= nonCatchAllCount) {
|
|
@@ -1872,9 +1958,9 @@ async function validateInput(schemaPath, method, inputType, input) {
|
|
|
1872
1958
|
function mapZodIssues(error) {
|
|
1873
1959
|
return error.issues.map((issue) => {
|
|
1874
1960
|
const code = mapZodCode(issue.code, issue.message);
|
|
1875
|
-
const
|
|
1961
|
+
const path10 = issue.path.map(String).join(".") || "";
|
|
1876
1962
|
return {
|
|
1877
|
-
path:
|
|
1963
|
+
path: path10,
|
|
1878
1964
|
code,
|
|
1879
1965
|
expected: issue.expected ?? mapExpectedFromMessage(issue.message),
|
|
1880
1966
|
received: issue.received ?? mapReceivedFromMessage(issue.message),
|
|
@@ -1934,7 +2020,7 @@ function getClientIp(req) {
|
|
|
1934
2020
|
|
|
1935
2021
|
// src/server/handleWsUpgrade.ts
|
|
1936
2022
|
import { WebSocketServer, WebSocket } from "ws";
|
|
1937
|
-
import
|
|
2023
|
+
import path4 from "path";
|
|
1938
2024
|
|
|
1939
2025
|
// src/errors/formatErrorResponse.ts
|
|
1940
2026
|
function formatErrorResponse(error) {
|
|
@@ -2107,7 +2193,7 @@ function attachWebSocket(options) {
|
|
|
2107
2193
|
const finalHandler = async () => {
|
|
2108
2194
|
let handlers;
|
|
2109
2195
|
try {
|
|
2110
|
-
const absoluteFilePath =
|
|
2196
|
+
const absoluteFilePath = path4.resolve(rootDir, route.filePath);
|
|
2111
2197
|
handlers = await loadWsHandler(absoluteFilePath, ctx);
|
|
2112
2198
|
} catch (err) {
|
|
2113
2199
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -2153,8 +2239,8 @@ function attachWebSocket(options) {
|
|
|
2153
2239
|
}
|
|
2154
2240
|
|
|
2155
2241
|
// src/cli/generateSchemaFiles.ts
|
|
2156
|
-
import
|
|
2157
|
-
import
|
|
2242
|
+
import path5 from "path";
|
|
2243
|
+
import fs3 from "fs/promises";
|
|
2158
2244
|
|
|
2159
2245
|
// src/ast/generateZodSchema.ts
|
|
2160
2246
|
var CodeGenContext = class {
|
|
@@ -2463,7 +2549,7 @@ function getSchemaOutputPath(sourceFile, dist, rootDir) {
|
|
|
2463
2549
|
}
|
|
2464
2550
|
const idx = rel.lastIndexOf("/");
|
|
2465
2551
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
2466
|
-
return
|
|
2552
|
+
return path5.resolve(rootDir, dist, relDir, "zod.js");
|
|
2467
2553
|
}
|
|
2468
2554
|
function getRuntimeSchemaPath(filePath, dist, rootDir) {
|
|
2469
2555
|
let rel = filePath.replace(/\\/g, "/");
|
|
@@ -2474,7 +2560,7 @@ function getRuntimeSchemaPath(filePath, dist, rootDir) {
|
|
|
2474
2560
|
}
|
|
2475
2561
|
const idx = rel.lastIndexOf("/");
|
|
2476
2562
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
2477
|
-
return
|
|
2563
|
+
return path5.resolve(rootDir, dist, relDir, "zod.js");
|
|
2478
2564
|
}
|
|
2479
2565
|
function getHelpersImportPath(relDir) {
|
|
2480
2566
|
if (!relDir) return `./${HELPERS_FILENAME}`;
|
|
@@ -2524,7 +2610,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
|
|
|
2524
2610
|
}
|
|
2525
2611
|
const fileEntries = [];
|
|
2526
2612
|
for (const [filePath, fileSources] of sourcesByFile) {
|
|
2527
|
-
const relFile =
|
|
2613
|
+
const relFile = path5.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
2528
2614
|
const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
|
|
2529
2615
|
const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
|
|
2530
2616
|
let relForDir = relFile;
|
|
@@ -2539,7 +2625,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
|
|
|
2539
2625
|
}
|
|
2540
2626
|
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
2541
2627
|
if (usesCoerceHelpers(allSourceCode)) {
|
|
2542
|
-
const helpersPath =
|
|
2628
|
+
const helpersPath = path5.resolve(rootDir, dist, HELPERS_FILENAME);
|
|
2543
2629
|
await writeSchemaFile(helpersPath, generateHelpersFileSource());
|
|
2544
2630
|
}
|
|
2545
2631
|
await Promise.all(
|
|
@@ -2547,8 +2633,8 @@ async function generateSchemaFiles(routes, rootDir, dist) {
|
|
|
2547
2633
|
);
|
|
2548
2634
|
}
|
|
2549
2635
|
async function writeSchemaFile(outputPath, source) {
|
|
2550
|
-
await
|
|
2551
|
-
await
|
|
2636
|
+
await fs3.mkdir(path5.dirname(outputPath), { recursive: true });
|
|
2637
|
+
await fs3.writeFile(outputPath, source, "utf-8");
|
|
2552
2638
|
}
|
|
2553
2639
|
|
|
2554
2640
|
// src/server/createServer.ts
|
|
@@ -2596,15 +2682,15 @@ function limitStreamSize(stream, maxSize) {
|
|
|
2596
2682
|
}
|
|
2597
2683
|
});
|
|
2598
2684
|
}
|
|
2599
|
-
function findAllowedMethods(routes,
|
|
2685
|
+
function findAllowedMethods(routes, path10) {
|
|
2600
2686
|
const methods = /* @__PURE__ */ new Set();
|
|
2601
2687
|
for (const route of routes) {
|
|
2602
|
-
if (route.urlPath ===
|
|
2688
|
+
if (route.urlPath === path10) {
|
|
2603
2689
|
methods.add(route.method);
|
|
2604
2690
|
continue;
|
|
2605
2691
|
}
|
|
2606
2692
|
if (route.isDynamic) {
|
|
2607
|
-
const params = matchDynamicPath(route.urlPath,
|
|
2693
|
+
const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
|
|
2608
2694
|
if (params !== null) {
|
|
2609
2695
|
methods.add(route.method);
|
|
2610
2696
|
}
|
|
@@ -2690,7 +2776,7 @@ async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares,
|
|
|
2690
2776
|
}
|
|
2691
2777
|
ctx.params = match.params;
|
|
2692
2778
|
const { route } = match;
|
|
2693
|
-
const absoluteFilePath =
|
|
2779
|
+
const absoluteFilePath = path6.resolve(rootDir, route.filePath);
|
|
2694
2780
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
2695
2781
|
const input = await resolveInput(route.method, request);
|
|
2696
2782
|
const inputType = getInputTypeForMethod(route.method);
|
|
@@ -2764,8 +2850,8 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
|
|
|
2764
2850
|
}
|
|
2765
2851
|
|
|
2766
2852
|
// src/cli/generateRoutes.ts
|
|
2767
|
-
import
|
|
2768
|
-
import
|
|
2853
|
+
import fs4 from "fs";
|
|
2854
|
+
import path7 from "path";
|
|
2769
2855
|
|
|
2770
2856
|
// src/middleware/loadMiddlewares.ts
|
|
2771
2857
|
var middlewareCache = /* @__PURE__ */ new Map();
|
|
@@ -2943,8 +3029,8 @@ function isFaapiConfigKey(key) {
|
|
|
2943
3029
|
async function createAppBase(options) {
|
|
2944
3030
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
2945
3031
|
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
2946
|
-
const routesPath =
|
|
2947
|
-
if (!
|
|
3032
|
+
const routesPath = path8.resolve(rootDir, dist, ROUTES_FILE);
|
|
3033
|
+
if (!fs5.existsSync(routesPath)) {
|
|
2948
3034
|
throw new Error(
|
|
2949
3035
|
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
2950
3036
|
);
|
|
@@ -3147,8 +3233,8 @@ async function createAppBase(options) {
|
|
|
3147
3233
|
|
|
3148
3234
|
// src/router/scanRoutes.ts
|
|
3149
3235
|
import fg from "fast-glob";
|
|
3150
|
-
import
|
|
3151
|
-
import
|
|
3236
|
+
import path9 from "path";
|
|
3237
|
+
import fs6 from "fs";
|
|
3152
3238
|
|
|
3153
3239
|
// src/router/constants.ts
|
|
3154
3240
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
@@ -3158,9 +3244,9 @@ function isHttpMethod(value) {
|
|
|
3158
3244
|
}
|
|
3159
3245
|
|
|
3160
3246
|
// src/utils/normalizePath.ts
|
|
3161
|
-
function normalizePath(
|
|
3162
|
-
if (!
|
|
3163
|
-
let result =
|
|
3247
|
+
function normalizePath(path10) {
|
|
3248
|
+
if (!path10) return "";
|
|
3249
|
+
let result = path10.replace(/\\/g, "/");
|
|
3164
3250
|
result = result.replace(/\/+/g, "/");
|
|
3165
3251
|
result = result.replace(/\/+$/, "");
|
|
3166
3252
|
if (result && !result.startsWith("/")) {
|
|
@@ -3209,38 +3295,38 @@ function filePathToUrlPath(filePath) {
|
|
|
3209
3295
|
// src/router/scanRoutes.ts
|
|
3210
3296
|
var APP_DIR = "src";
|
|
3211
3297
|
function toProdAbsPath(sourceAbsPath, rootDir, dist) {
|
|
3212
|
-
let rel =
|
|
3298
|
+
let rel = path9.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3213
3299
|
if (rel.startsWith(`${APP_DIR}/`)) {
|
|
3214
3300
|
rel = rel.slice(APP_DIR.length + 1);
|
|
3215
3301
|
}
|
|
3216
3302
|
const prodRel = `${dist}/${rel.replace(/\.ts$/, ".js")}`;
|
|
3217
|
-
return
|
|
3303
|
+
return path9.resolve(rootDir, prodRel);
|
|
3218
3304
|
}
|
|
3219
3305
|
async function findMergedMiddlewares(routeFilePath, rootDir, dist) {
|
|
3220
|
-
const routeDir =
|
|
3221
|
-
const resolvedRoot =
|
|
3306
|
+
const routeDir = path9.dirname(routeFilePath);
|
|
3307
|
+
const resolvedRoot = path9.resolve(rootDir);
|
|
3222
3308
|
const mwPaths = [];
|
|
3223
|
-
let currentDir =
|
|
3309
|
+
let currentDir = path9.resolve(rootDir, routeDir);
|
|
3224
3310
|
while (true) {
|
|
3225
3311
|
if (dist) {
|
|
3226
|
-
const mwPath =
|
|
3227
|
-
const absMwPath =
|
|
3312
|
+
const mwPath = path9.join(currentDir, "middlewares.js");
|
|
3313
|
+
const absMwPath = path9.resolve(rootDir, mwPath);
|
|
3228
3314
|
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, dist);
|
|
3229
|
-
if (
|
|
3315
|
+
if (fs6.existsSync(prodAbsMwPath)) {
|
|
3230
3316
|
mwPaths.push(prodAbsMwPath);
|
|
3231
3317
|
}
|
|
3232
3318
|
} else {
|
|
3233
3319
|
for (const ext of [".ts", ".js"]) {
|
|
3234
|
-
const mwPath =
|
|
3235
|
-
const absMwPath =
|
|
3236
|
-
if (
|
|
3320
|
+
const mwPath = path9.join(currentDir, `middlewares${ext}`);
|
|
3321
|
+
const absMwPath = path9.resolve(rootDir, mwPath);
|
|
3322
|
+
if (fs6.existsSync(absMwPath)) {
|
|
3237
3323
|
mwPaths.push(absMwPath);
|
|
3238
3324
|
break;
|
|
3239
3325
|
}
|
|
3240
3326
|
}
|
|
3241
3327
|
}
|
|
3242
3328
|
if (currentDir === resolvedRoot) break;
|
|
3243
|
-
const parentDir =
|
|
3329
|
+
const parentDir = path9.dirname(currentDir);
|
|
3244
3330
|
if (parentDir === currentDir) break;
|
|
3245
3331
|
currentDir = parentDir;
|
|
3246
3332
|
}
|
|
@@ -3302,7 +3388,7 @@ async function scanRoutes(rootDir, patterns, dist) {
|
|
|
3302
3388
|
const normalizedFile = file.replace(/\\/g, "/");
|
|
3303
3389
|
const fileName = normalizedFile.split("/").pop();
|
|
3304
3390
|
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
3305
|
-
const absPath =
|
|
3391
|
+
const absPath = path9.resolve(rootDir, normalizedFile);
|
|
3306
3392
|
const importPath = dist ? toProdAbsPath(absPath, rootDir, dist) : absPath;
|
|
3307
3393
|
const urlPath = filePathToUrlPath(normalizedFile);
|
|
3308
3394
|
const paramNames = extractParamNames(urlPath);
|
|
@@ -3383,6 +3469,7 @@ export {
|
|
|
3383
3469
|
invalidateProgramCache,
|
|
3384
3470
|
invokeHandler,
|
|
3385
3471
|
loadConfig,
|
|
3472
|
+
loadEnv,
|
|
3386
3473
|
logger,
|
|
3387
3474
|
resolveTypeNode
|
|
3388
3475
|
};
|