befly 3.10.18 → 3.11.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.
Files changed (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -2,11 +2,6 @@
2
2
  * 工具插件
3
3
  * 提供常用的工具函数
4
4
  */
5
-
6
- import type { RequestContext } from "../types/context.ts";
7
- // 类型导入
8
- import type { Plugin } from "../types/plugin.ts";
9
-
10
5
  /**
11
6
  * 成功响应
12
7
  * @param msg - 消息
@@ -14,7 +9,7 @@ import type { Plugin } from "../types/plugin.ts";
14
9
  * @param other - 其他字段(可选)
15
10
  * @returns 成功响应对象
16
11
  */
17
- export function Yes(msg: string, data: any = {}, other: Record<string, any> = {}) {
12
+ export function Yes(msg, data = {}, other = {}) {
18
13
  return {
19
14
  code: 0,
20
15
  msg: msg,
@@ -22,7 +17,6 @@ export function Yes(msg: string, data: any = {}, other: Record<string, any> = {}
22
17
  ...other
23
18
  };
24
19
  }
25
-
26
20
  /**
27
21
  * 失败响应
28
22
  * @param msg - 消息
@@ -30,7 +24,7 @@ export function Yes(msg: string, data: any = {}, other: Record<string, any> = {}
30
24
  * @param other - 其他字段(可选)
31
25
  * @returns 失败响应对象
32
26
  */
33
- export function No(msg: string, data: any = null, other: Record<string, any> = {}) {
27
+ export function No(msg, data = null, other = {}) {
34
28
  return {
35
29
  code: 1,
36
30
  msg: msg,
@@ -38,19 +32,6 @@ export function No(msg: string, data: any = null, other: Record<string, any> = {
38
32
  ...other
39
33
  };
40
34
  }
41
-
42
- /**
43
- * 响应选项
44
- */
45
- interface ResponseOptions {
46
- /** HTTP 状态码,默认 200 */
47
- status?: number;
48
- /** Content-Type,默认根据 data 类型自动判断 */
49
- contentType?: string;
50
- /** 额外的响应头 */
51
- headers?: Record<string, string>;
52
- }
53
-
54
35
  /**
55
36
  * 统一响应函数
56
37
  *
@@ -80,13 +61,11 @@ interface ResponseOptions {
80
61
  * headers: { 'X-Custom': 'value' }
81
62
  * });
82
63
  */
83
- export function Raw(ctx: RequestContext, data: Record<string, any> | string, options: ResponseOptions = {}): Response {
64
+ export function Raw(ctx, data, options = {}) {
84
65
  const { status = 200, contentType, headers = {} } = options;
85
-
86
66
  // 自动判断 Content-Type
87
67
  let finalContentType = contentType;
88
- let body: string;
89
-
68
+ let body;
90
69
  if (typeof data === "string") {
91
70
  // 字符串类型
92
71
  body = data;
@@ -94,26 +73,26 @@ export function Raw(ctx: RequestContext, data: Record<string, any> | string, opt
94
73
  // 自动判断:XML 或纯文本
95
74
  finalContentType = data.trim().startsWith("<") ? "application/xml" : "text/plain";
96
75
  }
97
- } else {
76
+ }
77
+ else {
98
78
  // 对象类型,JSON 序列化
99
79
  body = JSON.stringify(data);
100
80
  finalContentType = finalContentType || "application/json";
101
81
  }
102
-
103
82
  // 合并响应头
104
83
  const responseHeaders = {
105
84
  ...ctx.corsHeaders,
106
85
  "Content-Type": finalContentType,
107
86
  ...headers
108
87
  };
109
-
110
88
  return new Response(body, {
111
89
  status: status,
112
90
  headers: responseHeaders
113
91
  });
114
92
  }
115
-
116
93
  export default {
94
+ name: "tool",
95
+ enable: true,
117
96
  deps: [],
118
97
  handler: () => {
119
98
  return {
@@ -122,4 +101,4 @@ export default {
122
101
  Raw: Raw
123
102
  };
124
103
  }
125
- } satisfies Plugin;
104
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * API路由处理器
3
+ * 处理 /api/* 路径的请求
4
+ */
5
+ import type { ApiRoute } from "../types/api";
6
+ import type { BeflyContext } from "../types/befly";
7
+ import type { Hook } from "../types/hook";
8
+ /**
9
+ * API处理器工厂函数
10
+ * @param apis - API路由映射表
11
+ * @param hooks - 钩子列表
12
+ * @param context - 应用上下文
13
+ */
14
+ export declare function apiHandler(apis: Map<string, ApiRoute>, hooks: Hook[], context: BeflyContext): (req: Request, server?: any) => Promise<Response>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * API路由处理器
3
+ * 处理 /api/* 路径的请求
4
+ */
5
+ import { withCtx } from "../lib/asyncContext";
6
+ import { Logger } from "../lib/logger";
7
+ // 相对导入
8
+ import { getClientIp } from "../utils/getClientIp";
9
+ import { FinalResponse } from "../utils/response";
10
+ import { genShortId } from "../utils/util";
11
+ /**
12
+ * API处理器工厂函数
13
+ * @param apis - API路由映射表
14
+ * @param hooks - 钩子列表
15
+ * @param context - 应用上下文
16
+ */
17
+ export function apiHandler(apis, hooks, context) {
18
+ return async (req, server) => {
19
+ // 1. 生成请求 ID
20
+ const requestId = genShortId();
21
+ // 2. 创建请求上下文
22
+ const url = new URL(req.url);
23
+ // 只用接口路径做存在性判断与匹配:不要把 method 拼进 key
24
+ // 说明:apisMap 的 key 来源于 scanFiles/loadApis 生成的 routePath(例如 /api/core/xxx)
25
+ const apiPath = url.pathname || "/";
26
+ const clientIp = getClientIp(req, server);
27
+ const now = Date.now();
28
+ const ctx = {
29
+ method: req.method,
30
+ body: {},
31
+ user: {},
32
+ req: req,
33
+ now: now,
34
+ ip: clientIp,
35
+ headers: req.headers,
36
+ route: apiPath,
37
+ requestId: requestId,
38
+ corsHeaders: {
39
+ "X-Request-ID": requestId
40
+ },
41
+ api: apis.get(apiPath),
42
+ response: undefined,
43
+ result: undefined
44
+ };
45
+ return withCtx({
46
+ requestId: requestId,
47
+ method: req.method,
48
+ route: apiPath,
49
+ ip: clientIp,
50
+ now: now
51
+ }, async () => {
52
+ try {
53
+ // 4. 串联执行所有钩子
54
+ for (const hook of hooks) {
55
+ await hook.handler(context, ctx);
56
+ // 如果钩子已经设置了 response,停止执行
57
+ if (ctx.response) {
58
+ return ctx.response;
59
+ }
60
+ }
61
+ // hooks 全部通过后记录请求日志(拦截请求仅由 ErrorResponse 记录)
62
+ if (ctx.api && req.method !== "OPTIONS") {
63
+ const logData = {
64
+ event: "request",
65
+ apiName: ctx.api.name
66
+ };
67
+ if (ctx.body && Object.keys(ctx.body).length > 0) {
68
+ logData.body = ctx.body;
69
+ }
70
+ Logger.info(logData, "request");
71
+ }
72
+ // 5. 执行 API handler
73
+ if (!ctx.api) {
74
+ if (req.method !== "OPTIONS") {
75
+ ctx.response = Response.json({
76
+ code: 1,
77
+ msg: "接口不存在"
78
+ }, {
79
+ headers: ctx.corsHeaders
80
+ });
81
+ }
82
+ }
83
+ else if (ctx.api.handler) {
84
+ const result = await ctx.api.handler(context, ctx);
85
+ if (result instanceof Response) {
86
+ ctx.response = result;
87
+ }
88
+ else {
89
+ ctx.result = result;
90
+ }
91
+ }
92
+ // 7. 返回响应(自动处理 response/result/日志)
93
+ return FinalResponse(ctx);
94
+ }
95
+ catch (err) {
96
+ // 全局错误处理
97
+ const errorPath = ctx.api ? apiPath : req.url;
98
+ Logger.error({ err: err, path: errorPath }, "请求错误");
99
+ ctx.result = {
100
+ code: 1,
101
+ msg: "内部服务错误"
102
+ };
103
+ return FinalResponse(ctx);
104
+ }
105
+ });
106
+ };
107
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 静态文件路由处理器
3
+ * 处理 /* 路径的静态文件请求
4
+ */
5
+ import type { CorsConfig } from "../types/befly";
6
+ /**
7
+ * 静态文件处理器工厂
8
+ */
9
+ export declare function staticHandler(corsConfig?: CorsConfig | undefined): (req: Request) => Promise<Response>;
@@ -2,29 +2,21 @@
2
2
  * 静态文件路由处理器
3
3
  * 处理 /* 路径的静态文件请求
4
4
  */
5
-
6
- // 类型导入
7
- import type { CorsConfig } from "../types/befly.ts";
8
-
9
5
  // 外部依赖
10
6
  import { join } from "pathe";
11
-
12
- import { Logger } from "../lib/logger.ts";
7
+ import { Logger } from "../lib/logger";
13
8
  // 相对导入
14
- import { appDir } from "../paths.ts";
15
- import { setCorsOptions } from "../utils/cors.ts";
16
-
9
+ import { appDir } from "../paths";
10
+ import { setCorsOptions } from "../utils/cors";
17
11
  /**
18
12
  * 静态文件处理器工厂
19
13
  */
20
- export function staticHandler(corsConfig: CorsConfig | undefined = undefined) {
21
- return async (req: Request): Promise<Response> => {
14
+ export function staticHandler(corsConfig = undefined) {
15
+ return async (req) => {
22
16
  // 设置 CORS 响应头
23
17
  const corsHeaders = setCorsOptions(req, corsConfig);
24
-
25
18
  const url = new URL(req.url);
26
19
  const filePath = join(appDir, "public", url.pathname);
27
-
28
20
  try {
29
21
  // OPTIONS预检请求
30
22
  if (req.method === "OPTIONS") {
@@ -33,7 +25,6 @@ export function staticHandler(corsConfig: CorsConfig | undefined = undefined) {
33
25
  headers: corsHeaders
34
26
  });
35
27
  }
36
-
37
28
  const file = Bun.file(filePath);
38
29
  if (await file.exists()) {
39
30
  return new Response(file, {
@@ -42,29 +33,24 @@ export function staticHandler(corsConfig: CorsConfig | undefined = undefined) {
42
33
  ...corsHeaders
43
34
  }
44
35
  });
45
- } else {
46
- return Response.json(
47
- {
48
- code: 1,
49
- msg: "文件未找到"
50
- },
51
- {
52
- headers: corsHeaders
53
- }
54
- );
55
36
  }
56
- } catch (error: any) {
57
- Logger.error({ err: error }, "静态文件处理失败");
58
-
59
- return Response.json(
60
- {
37
+ else {
38
+ return Response.json({
61
39
  code: 1,
62
- msg: "文件读取失败"
63
- },
64
- {
40
+ msg: "文件未找到"
41
+ }, {
65
42
  headers: corsHeaders
66
- }
67
- );
43
+ });
44
+ }
45
+ }
46
+ catch (error) {
47
+ Logger.error({ err: error }, "静态文件处理失败");
48
+ return Response.json({
49
+ code: 1,
50
+ msg: "文件读取失败"
51
+ }, {
52
+ headers: corsHeaders
53
+ });
68
54
  }
69
55
  };
70
56
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,296 @@
1
+ // 内部依赖(Node.js 内置模块)
2
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ function isTsSourcePath(p) {
5
+ // 注意:.d.ts 是构建产物,必须允许;这里仅阻断指向源码的 .ts 入口。
6
+ if (!p.endsWith(".ts")) {
7
+ return false;
8
+ }
9
+ if (p.endsWith(".d.ts") || p.endsWith(".d.mts") || p.endsWith(".d.cts")) {
10
+ return false;
11
+ }
12
+ return true;
13
+ }
14
+ function isRecord(value) {
15
+ return typeof value === "object" && value !== null;
16
+ }
17
+ function collectExportPaths(exportsValue, out) {
18
+ if (typeof exportsValue === "string") {
19
+ out.push(exportsValue);
20
+ return;
21
+ }
22
+ if (Array.isArray(exportsValue)) {
23
+ for (const item of exportsValue) {
24
+ collectExportPaths(item, out);
25
+ }
26
+ return;
27
+ }
28
+ if (!isRecord(exportsValue)) {
29
+ return;
30
+ }
31
+ for (const value of Object.values(exportsValue)) {
32
+ collectExportPaths(value, out);
33
+ }
34
+ }
35
+ function assertPathExists(packageRoot, relativeOrBare) {
36
+ if (!relativeOrBare.startsWith("./")) {
37
+ return;
38
+ }
39
+ if (relativeOrBare.includes("*")) {
40
+ const beforeStar = relativeOrBare.split("*")[0] ?? "";
41
+ const dirPath = resolve(packageRoot, beforeStar);
42
+ if (!existsSync(dirPath)) {
43
+ process.stderr.write(`[ensureDist] missing export directory: ${beforeStar}\n`);
44
+ process.exit(1);
45
+ }
46
+ const stat = statSync(dirPath);
47
+ if (!stat.isDirectory()) {
48
+ process.stderr.write(`[ensureDist] export directory is not a directory: ${beforeStar}\n`);
49
+ process.exit(1);
50
+ }
51
+ return;
52
+ }
53
+ const absPath = resolve(packageRoot, relativeOrBare);
54
+ if (!existsSync(absPath)) {
55
+ process.stderr.write(`[ensureDist] missing export file: ${relativeOrBare}\n`);
56
+ process.exit(1);
57
+ }
58
+ const stat = statSync(absPath);
59
+ if (!stat.isFile()) {
60
+ process.stderr.write(`[ensureDist] export path is not a file: ${relativeOrBare}\n`);
61
+ process.exit(1);
62
+ }
63
+ }
64
+ function findTsImportSpecifiers(content) {
65
+ const matches = [];
66
+ const patterns = [/from\s+["'][^"']+\.ts["']/g, /import\(\s*["'][^"']+\.ts["']\s*\)/g, /<reference\s+path=["'][^"']+\.ts["']\s*\/>/g];
67
+ for (const pattern of patterns) {
68
+ const found = content.match(pattern);
69
+ if (found && found.length > 0) {
70
+ matches.push(...found);
71
+ }
72
+ }
73
+ return matches;
74
+ }
75
+ function collectExportTypePaths(exportsValue, out) {
76
+ if (!isRecord(exportsValue)) {
77
+ return;
78
+ }
79
+ for (const value of Object.values(exportsValue)) {
80
+ if (typeof value === "string") {
81
+ continue;
82
+ }
83
+ if (Array.isArray(value)) {
84
+ for (const item of value) {
85
+ collectExportTypePaths(item, out);
86
+ }
87
+ continue;
88
+ }
89
+ if (!isRecord(value)) {
90
+ continue;
91
+ }
92
+ const typesPath = value["types"];
93
+ if (typeof typesPath === "string") {
94
+ out.push(typesPath);
95
+ }
96
+ collectExportTypePaths(value, out);
97
+ }
98
+ }
99
+ function expandDtsExportPath(packageRoot, exportPath) {
100
+ if (!exportPath.startsWith("./")) {
101
+ return [];
102
+ }
103
+ if (!exportPath.includes("*")) {
104
+ return [resolve(packageRoot, exportPath)];
105
+ }
106
+ const beforeStar = exportPath.split("*")[0] ?? "";
107
+ const dirPath = resolve(packageRoot, beforeStar);
108
+ if (!existsSync(dirPath)) {
109
+ return [];
110
+ }
111
+ const stat = statSync(dirPath);
112
+ if (!stat.isDirectory()) {
113
+ return [];
114
+ }
115
+ const files = readdirSync(dirPath);
116
+ return files.filter((file) => file.endsWith(".d.ts")).map((file) => resolve(dirPath, file));
117
+ }
118
+ function findDtsSpecifiers(content) {
119
+ const specifiers = [];
120
+ const patterns = [/from\s+["']([^"']+)["']/g, /import\(\s*["']([^"']+)["']\s*\)/g, /<reference\s+path=["']([^"']+)["']\s*\/>/g];
121
+ for (const pattern of patterns) {
122
+ for (const match of content.matchAll(pattern)) {
123
+ const spec = match[1];
124
+ if (typeof spec === "string" && spec.length > 0) {
125
+ specifiers.push(spec);
126
+ }
127
+ }
128
+ }
129
+ return specifiers;
130
+ }
131
+ function resolveLocalDtsDependency(fromDtsPath, specifier) {
132
+ if (!specifier.startsWith("./") && !specifier.startsWith("../")) {
133
+ return null;
134
+ }
135
+ if (specifier.endsWith(".json")) {
136
+ return null;
137
+ }
138
+ const baseDir = dirname(fromDtsPath);
139
+ const absNoCheck = resolve(baseDir, specifier);
140
+ const tryPaths = [];
141
+ if (specifier.endsWith(".d.ts")) {
142
+ tryPaths.push(absNoCheck);
143
+ }
144
+ else if (specifier.endsWith(".js") || specifier.endsWith(".mjs") || specifier.endsWith(".cjs")) {
145
+ const replaced = absNoCheck.replace(/\.(mjs|cjs|js)$/u, ".d.ts");
146
+ tryPaths.push(replaced);
147
+ tryPaths.push(`${absNoCheck}.d.ts`);
148
+ }
149
+ else {
150
+ tryPaths.push(`${absNoCheck}.d.ts`);
151
+ }
152
+ tryPaths.push(resolve(absNoCheck, "index.d.ts"));
153
+ for (const p of tryPaths) {
154
+ if (existsSync(p)) {
155
+ return p;
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+ function collectReachableDtsFromEntrypoints(distDir, entrypoints) {
161
+ const visited = new Set();
162
+ const queue = [];
163
+ for (const entry of entrypoints) {
164
+ if (!entry.startsWith(distDir)) {
165
+ continue;
166
+ }
167
+ if (existsSync(entry) && entry.endsWith(".d.ts")) {
168
+ queue.push(entry);
169
+ }
170
+ }
171
+ while (queue.length > 0) {
172
+ const current = queue.pop();
173
+ if (!current) {
174
+ continue;
175
+ }
176
+ if (visited.has(current)) {
177
+ continue;
178
+ }
179
+ visited.add(current);
180
+ const content = readFileSync(current, { encoding: "utf8" });
181
+ const specifiers = findDtsSpecifiers(content);
182
+ for (const specifier of specifiers) {
183
+ const resolved = resolveLocalDtsDependency(current, specifier);
184
+ if (!resolved) {
185
+ continue;
186
+ }
187
+ if (!resolved.startsWith(distDir)) {
188
+ continue;
189
+ }
190
+ if (!visited.has(resolved)) {
191
+ queue.push(resolved);
192
+ }
193
+ }
194
+ }
195
+ return Array.from(visited);
196
+ }
197
+ function main() {
198
+ const packageRoot = process.cwd();
199
+ const pkgPath = resolve(packageRoot, "package.json");
200
+ if (!existsSync(pkgPath)) {
201
+ process.stderr.write("[ensureDist] package.json not found\n");
202
+ process.exit(1);
203
+ }
204
+ const pkg = JSON.parse(readFileSync(pkgPath, { encoding: "utf8" }));
205
+ // 额外一致性校验:默认入口必须走 dist 产物(JS + DTS 分离)。
206
+ if (typeof pkg.main !== "string" || !pkg.main.startsWith("./dist/")) {
207
+ process.stderr.write("[ensureDist] package.json main must be a string under ./dist/\n");
208
+ process.exit(1);
209
+ }
210
+ if (typeof pkg.types !== "string" || !pkg.types.startsWith("./dist/")) {
211
+ process.stderr.write("[ensureDist] package.json types must be a string under ./dist/\n");
212
+ process.exit(1);
213
+ }
214
+ if (isTsSourcePath(pkg.main) || isTsSourcePath(pkg.types)) {
215
+ process.stderr.write("[ensureDist] package.json main/types must not point to .ts source files\n");
216
+ process.exit(1);
217
+ }
218
+ if (!isRecord(pkg.exports) || !isRecord(pkg.exports["."])) {
219
+ process.stderr.write("[ensureDist] package.json exports['.'] must be an object with { types, default }\n");
220
+ process.exit(1);
221
+ }
222
+ const dotExport = pkg.exports["."];
223
+ const dotTypes = dotExport["types"];
224
+ const dotDefault = dotExport["default"];
225
+ if (dotTypes !== pkg.types) {
226
+ process.stderr.write("[ensureDist] exports['.'].types must equal package.json types\n");
227
+ process.exit(1);
228
+ }
229
+ if (dotDefault !== pkg.main) {
230
+ process.stderr.write("[ensureDist] exports['.'].default must equal package.json main\n");
231
+ process.exit(1);
232
+ }
233
+ if (typeof dotTypes === "string" && isTsSourcePath(dotTypes)) {
234
+ process.stderr.write("[ensureDist] exports['.'].types must not point to .ts source files\n");
235
+ process.exit(1);
236
+ }
237
+ if (typeof dotDefault === "string" && isTsSourcePath(dotDefault)) {
238
+ process.stderr.write("[ensureDist] exports['.'].default must not point to .ts source files\n");
239
+ process.exit(1);
240
+ }
241
+ const distDir = resolve(packageRoot, "dist");
242
+ if (!existsSync(distDir)) {
243
+ process.stderr.write("[ensureDist] dist/ not found (did you run build?)\n");
244
+ process.exit(1);
245
+ }
246
+ // dist-only 发布规范:从对外可达入口(pkg.types + exports.*.types)出发的声明闭包中,
247
+ // 任何 .d.ts 都不允许引用源码 .ts(防止消费者类型解析回退到源码)。
248
+ {
249
+ const exportTypePaths = [];
250
+ if (typeof pkg.types === "string") {
251
+ exportTypePaths.push(pkg.types);
252
+ }
253
+ collectExportTypePaths(pkg.exports, exportTypePaths);
254
+ const entryDtsAbsPaths = new Set();
255
+ for (const exportPath of exportTypePaths) {
256
+ for (const absPath of expandDtsExportPath(packageRoot, exportPath)) {
257
+ entryDtsAbsPaths.add(absPath);
258
+ }
259
+ }
260
+ if (entryDtsAbsPaths.size === 0) {
261
+ process.stderr.write("[ensureDist] no exported .d.ts entrypoints found (types/exports.types)\n");
262
+ process.exit(1);
263
+ }
264
+ const reachableDtsPaths = collectReachableDtsFromEntrypoints(distDir, Array.from(entryDtsAbsPaths));
265
+ if (reachableDtsPaths.length === 0) {
266
+ process.stderr.write("[ensureDist] no reachable .d.ts files found from exported entrypoints\n");
267
+ process.exit(1);
268
+ }
269
+ const offenders = [];
270
+ for (const absPath of reachableDtsPaths) {
271
+ const content = readFileSync(absPath, { encoding: "utf8" });
272
+ const matches = findTsImportSpecifiers(content);
273
+ if (matches.length > 0) {
274
+ offenders.push({ filePath: absPath, matches: matches });
275
+ }
276
+ }
277
+ if (offenders.length > 0) {
278
+ process.stderr.write("[ensureDist] exported declaration files must not reference .ts source files\n");
279
+ process.stderr.write(`${JSON.stringify(offenders, null, 4)}\n`);
280
+ process.exit(1);
281
+ }
282
+ }
283
+ const paths = [];
284
+ if (typeof pkg.main === "string") {
285
+ paths.push(pkg.main);
286
+ }
287
+ if (typeof pkg.types === "string") {
288
+ paths.push(pkg.types);
289
+ }
290
+ collectExportPaths(pkg.exports, paths);
291
+ for (const p of paths) {
292
+ assertPathExists(packageRoot, p);
293
+ }
294
+ process.stdout.write("[ensureDist] OK\n");
295
+ }
296
+ main();
@@ -0,0 +1,3 @@
1
+ import type { BeflyContext } from "../types/befly";
2
+ import type { SyncApiItem } from "../types/sync";
3
+ export declare function syncApi(ctx: Pick<BeflyContext, "db" | "cache">, apis: SyncApiItem[]): Promise<void>;