befly 3.16.10 → 3.17.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.
Files changed (184) hide show
  1. package/README.md +0 -129
  2. package/befly.js +16413 -0
  3. package/befly.min.js +72 -0
  4. package/package.json +19 -29
  5. package/scripts/syncDb/context.js +99 -0
  6. package/scripts/syncDb/diff.js +133 -0
  7. package/scripts/syncDb/index.js +70 -0
  8. package/scripts/syncDb/query.js +26 -0
  9. package/scripts/syncDb/report.js +190 -0
  10. package/scripts/syncDb/transform.js +111 -0
  11. package/dist/befly.config.d.ts +0 -7
  12. package/dist/befly.config.js +0 -128
  13. package/dist/befly.js +0 -17348
  14. package/dist/befly.min.js +0 -23
  15. package/dist/checks/checkApi.d.ts +0 -1
  16. package/dist/checks/checkApi.js +0 -139
  17. package/dist/checks/checkConfig.d.ts +0 -9
  18. package/dist/checks/checkConfig.js +0 -255
  19. package/dist/checks/checkHook.d.ts +0 -1
  20. package/dist/checks/checkHook.js +0 -132
  21. package/dist/checks/checkMenu.d.ts +0 -3
  22. package/dist/checks/checkMenu.js +0 -106
  23. package/dist/checks/checkPlugin.d.ts +0 -1
  24. package/dist/checks/checkPlugin.js +0 -132
  25. package/dist/checks/checkTable.d.ts +0 -7
  26. package/dist/checks/checkTable.js +0 -431
  27. package/dist/configs/presetRegexp.d.ts +0 -145
  28. package/dist/configs/presetRegexp.js +0 -218
  29. package/dist/hooks/auth.d.ts +0 -3
  30. package/dist/hooks/auth.js +0 -24
  31. package/dist/hooks/cors.d.ts +0 -7
  32. package/dist/hooks/cors.js +0 -36
  33. package/dist/hooks/parser.d.ts +0 -10
  34. package/dist/hooks/parser.js +0 -76
  35. package/dist/hooks/permission.d.ts +0 -11
  36. package/dist/hooks/permission.js +0 -78
  37. package/dist/hooks/validator.d.ts +0 -7
  38. package/dist/hooks/validator.js +0 -52
  39. package/dist/index.d.ts +0 -28
  40. package/dist/index.js +0 -316
  41. package/dist/lib/asyncContext.d.ts +0 -21
  42. package/dist/lib/asyncContext.js +0 -27
  43. package/dist/lib/cacheHelper.d.ts +0 -128
  44. package/dist/lib/cacheHelper.js +0 -477
  45. package/dist/lib/cacheKeys.d.ts +0 -27
  46. package/dist/lib/cacheKeys.js +0 -37
  47. package/dist/lib/cipher.d.ts +0 -153
  48. package/dist/lib/cipher.js +0 -237
  49. package/dist/lib/connect.d.ts +0 -95
  50. package/dist/lib/connect.js +0 -313
  51. package/dist/lib/dbHelper.d.ts +0 -229
  52. package/dist/lib/dbHelper.js +0 -1099
  53. package/dist/lib/dbUtils.d.ts +0 -91
  54. package/dist/lib/dbUtils.js +0 -544
  55. package/dist/lib/jwt.d.ts +0 -13
  56. package/dist/lib/jwt.js +0 -77
  57. package/dist/lib/logger.d.ts +0 -46
  58. package/dist/lib/logger.js +0 -731
  59. package/dist/lib/redisHelper.d.ts +0 -193
  60. package/dist/lib/redisHelper.js +0 -598
  61. package/dist/lib/sqlBuilder.d.ts +0 -160
  62. package/dist/lib/sqlBuilder.js +0 -837
  63. package/dist/lib/sqlCheck.d.ts +0 -23
  64. package/dist/lib/sqlCheck.js +0 -119
  65. package/dist/lib/validator.d.ts +0 -45
  66. package/dist/lib/validator.js +0 -424
  67. package/dist/loader/loadApis.d.ts +0 -12
  68. package/dist/loader/loadApis.js +0 -71
  69. package/dist/loader/loadHooks.d.ts +0 -7
  70. package/dist/loader/loadHooks.js +0 -50
  71. package/dist/loader/loadPlugins.d.ts +0 -8
  72. package/dist/loader/loadPlugins.js +0 -69
  73. package/dist/paths.d.ts +0 -93
  74. package/dist/paths.js +0 -100
  75. package/dist/plugins/cache.d.ts +0 -10
  76. package/dist/plugins/cache.js +0 -24
  77. package/dist/plugins/cipher.d.ts +0 -7
  78. package/dist/plugins/cipher.js +0 -14
  79. package/dist/plugins/config.d.ts +0 -3
  80. package/dist/plugins/config.js +0 -9
  81. package/dist/plugins/db.d.ts +0 -10
  82. package/dist/plugins/db.js +0 -48
  83. package/dist/plugins/jwt.d.ts +0 -6
  84. package/dist/plugins/jwt.js +0 -13
  85. package/dist/plugins/logger.d.ts +0 -10
  86. package/dist/plugins/logger.js +0 -21
  87. package/dist/plugins/redis.d.ts +0 -10
  88. package/dist/plugins/redis.js +0 -40
  89. package/dist/plugins/tool.d.ts +0 -75
  90. package/dist/plugins/tool.js +0 -105
  91. package/dist/router/api.d.ts +0 -14
  92. package/dist/router/api.js +0 -109
  93. package/dist/router/static.d.ts +0 -9
  94. package/dist/router/static.js +0 -56
  95. package/dist/scripts/ensureDist.d.ts +0 -1
  96. package/dist/scripts/ensureDist.js +0 -296
  97. package/dist/sync/syncApi.d.ts +0 -3
  98. package/dist/sync/syncApi.js +0 -163
  99. package/dist/sync/syncCache.d.ts +0 -2
  100. package/dist/sync/syncCache.js +0 -14
  101. package/dist/sync/syncDev.d.ts +0 -6
  102. package/dist/sync/syncDev.js +0 -166
  103. package/dist/sync/syncMenu.d.ts +0 -14
  104. package/dist/sync/syncMenu.js +0 -308
  105. package/dist/sync/syncTable.d.ts +0 -126
  106. package/dist/sync/syncTable.js +0 -1129
  107. package/dist/types/api.d.ts +0 -177
  108. package/dist/types/api.js +0 -4
  109. package/dist/types/befly.d.ts +0 -231
  110. package/dist/types/befly.js +0 -4
  111. package/dist/types/cache.d.ts +0 -96
  112. package/dist/types/cache.js +0 -4
  113. package/dist/types/cipher.d.ts +0 -27
  114. package/dist/types/cipher.js +0 -7
  115. package/dist/types/common.d.ts +0 -127
  116. package/dist/types/common.js +0 -5
  117. package/dist/types/context.d.ts +0 -39
  118. package/dist/types/context.js +0 -4
  119. package/dist/types/coreError.d.ts +0 -31
  120. package/dist/types/coreError.js +0 -38
  121. package/dist/types/crypto.d.ts +0 -20
  122. package/dist/types/crypto.js +0 -4
  123. package/dist/types/database.d.ts +0 -182
  124. package/dist/types/database.js +0 -4
  125. package/dist/types/hook.d.ts +0 -30
  126. package/dist/types/hook.js +0 -19
  127. package/dist/types/jwt.d.ts +0 -76
  128. package/dist/types/jwt.js +0 -4
  129. package/dist/types/logger.d.ts +0 -95
  130. package/dist/types/logger.js +0 -6
  131. package/dist/types/plugin.d.ts +0 -27
  132. package/dist/types/plugin.js +0 -17
  133. package/dist/types/redis.d.ts +0 -80
  134. package/dist/types/redis.js +0 -4
  135. package/dist/types/roleApisCache.d.ts +0 -21
  136. package/dist/types/roleApisCache.js +0 -8
  137. package/dist/types/sync.d.ts +0 -93
  138. package/dist/types/sync.js +0 -4
  139. package/dist/types/table.d.ts +0 -34
  140. package/dist/types/table.js +0 -4
  141. package/dist/types/validate.d.ts +0 -113
  142. package/dist/types/validate.js +0 -4
  143. package/dist/utils/calcPerfTime.d.ts +0 -4
  144. package/dist/utils/calcPerfTime.js +0 -13
  145. package/dist/utils/cors.d.ts +0 -8
  146. package/dist/utils/cors.js +0 -17
  147. package/dist/utils/dbFieldRules.d.ts +0 -31
  148. package/dist/utils/dbFieldRules.js +0 -94
  149. package/dist/utils/fieldClear.d.ts +0 -11
  150. package/dist/utils/fieldClear.js +0 -57
  151. package/dist/utils/formatYmdHms.d.ts +0 -1
  152. package/dist/utils/formatYmdHms.js +0 -20
  153. package/dist/utils/getClientIp.d.ts +0 -6
  154. package/dist/utils/getClientIp.js +0 -39
  155. package/dist/utils/importDefault.d.ts +0 -1
  156. package/dist/utils/importDefault.js +0 -53
  157. package/dist/utils/isDirentDirectory.d.ts +0 -3
  158. package/dist/utils/isDirentDirectory.js +0 -18
  159. package/dist/utils/loadMenuConfigs.d.ts +0 -11
  160. package/dist/utils/loadMenuConfigs.js +0 -130
  161. package/dist/utils/loggerUtils.d.ts +0 -18
  162. package/dist/utils/loggerUtils.js +0 -171
  163. package/dist/utils/mergeAndConcat.d.ts +0 -7
  164. package/dist/utils/mergeAndConcat.js +0 -77
  165. package/dist/utils/normalizeFieldDefinition.d.ts +0 -18
  166. package/dist/utils/normalizeFieldDefinition.js +0 -27
  167. package/dist/utils/processInfo.d.ts +0 -26
  168. package/dist/utils/processInfo.js +0 -41
  169. package/dist/utils/response.d.ts +0 -20
  170. package/dist/utils/response.js +0 -96
  171. package/dist/utils/scanAddons.d.ts +0 -15
  172. package/dist/utils/scanAddons.js +0 -35
  173. package/dist/utils/scanCoreBuiltins.d.ts +0 -3
  174. package/dist/utils/scanCoreBuiltins.js +0 -72
  175. package/dist/utils/scanFiles.d.ts +0 -32
  176. package/dist/utils/scanFiles.js +0 -124
  177. package/dist/utils/scanSources.d.ts +0 -10
  178. package/dist/utils/scanSources.js +0 -46
  179. package/dist/utils/sortModules.d.ts +0 -28
  180. package/dist/utils/sortModules.js +0 -105
  181. package/dist/utils/sqlUtil.d.ts +0 -33
  182. package/dist/utils/sqlUtil.js +0 -146
  183. package/dist/utils/util.d.ts +0 -172
  184. package/dist/utils/util.js +0 -517
@@ -1,75 +0,0 @@
1
- /**
2
- * 工具插件
3
- * 提供常用的工具函数
4
- */
5
- import type { JsonValue } from "../types/common";
6
- import type { RequestContext } from "../types/context";
7
- import type { Plugin } from "../types/plugin";
8
- type ToolExtra = Record<string, JsonValue | undefined>;
9
- /**
10
- * 成功响应
11
- * @param msg - 消息
12
- * @param data - 数据(可选)
13
- * @param other - 其他字段(可选)
14
- * @returns 成功响应对象
15
- */
16
- export declare function Yes<TData extends JsonValue = JsonValue, TOther extends ToolExtra = ToolExtra>(msg: string, data?: TData, other?: TOther): {
17
- code: 0;
18
- msg: string;
19
- data: TData;
20
- } & TOther;
21
- /**
22
- * 失败响应
23
- * @param msg - 消息
24
- * @param data - 数据(可选)
25
- * @param other - 其他字段(可选)
26
- * @returns 失败响应对象
27
- */
28
- export declare function No<TData extends JsonValue = JsonValue, TOther extends ToolExtra = ToolExtra>(msg: string, data?: TData, other?: TOther): {
29
- code: 1;
30
- msg: string;
31
- data: TData;
32
- } & TOther;
33
- /**
34
- * 响应选项
35
- */
36
- interface ResponseOptions {
37
- /** HTTP 状态码,默认 200 */
38
- status?: number;
39
- /** Content-Type,默认根据 data 类型自动判断 */
40
- contentType?: string;
41
- /** 额外的响应头 */
42
- headers?: Record<string, string>;
43
- }
44
- /**
45
- * 统一响应函数
46
- *
47
- * 自动识别数据类型并设置正确的 Content-Type:
48
- * - 对象 → application/json
49
- * - 字符串 → text/plain
50
- * - 可通过 options.contentType 手动指定
51
- *
52
- * @param ctx 请求上下文
53
- * @param data 响应数据(对象或字符串)
54
- * @param options 响应选项
55
- * @returns Response 对象
56
- *
57
- * @example
58
- * // JSON 响应(自动)
59
- * return Raw(ctx, { code: 'SUCCESS', message: '成功' });
60
- *
61
- * // 纯文本响应(自动)
62
- * return Raw(ctx, 'success');
63
- *
64
- * // XML 响应(手动指定)
65
- * return Raw(ctx, xmlString, { contentType: 'application/xml' });
66
- *
67
- * // 自定义状态码和额外头
68
- * return Raw(ctx, { error: 'Not Found' }, {
69
- * status: 404,
70
- * headers: { 'X-Custom': 'value' }
71
- * });
72
- */
73
- export declare function Raw(ctx: RequestContext, data: JsonValue | string, options?: ResponseOptions): Response;
74
- declare const toolPlugin: Plugin;
75
- export default toolPlugin;
@@ -1,105 +0,0 @@
1
- /**
2
- * 工具插件
3
- * 提供常用的工具函数
4
- */
5
- /**
6
- * 成功响应
7
- * @param msg - 消息
8
- * @param data - 数据(可选)
9
- * @param other - 其他字段(可选)
10
- * @returns 成功响应对象
11
- */
12
- export function Yes(msg, data = {}, other = {}) {
13
- return {
14
- code: 0,
15
- msg: msg,
16
- data: data,
17
- ...other
18
- };
19
- }
20
- /**
21
- * 失败响应
22
- * @param msg - 消息
23
- * @param data - 数据(可选)
24
- * @param other - 其他字段(可选)
25
- * @returns 失败响应对象
26
- */
27
- export function No(msg, data = null, other = {}) {
28
- return {
29
- code: 1,
30
- msg: msg,
31
- data: data,
32
- ...other
33
- };
34
- }
35
- /**
36
- * 统一响应函数
37
- *
38
- * 自动识别数据类型并设置正确的 Content-Type:
39
- * - 对象 → application/json
40
- * - 字符串 → text/plain
41
- * - 可通过 options.contentType 手动指定
42
- *
43
- * @param ctx 请求上下文
44
- * @param data 响应数据(对象或字符串)
45
- * @param options 响应选项
46
- * @returns Response 对象
47
- *
48
- * @example
49
- * // JSON 响应(自动)
50
- * return Raw(ctx, { code: 'SUCCESS', message: '成功' });
51
- *
52
- * // 纯文本响应(自动)
53
- * return Raw(ctx, 'success');
54
- *
55
- * // XML 响应(手动指定)
56
- * return Raw(ctx, xmlString, { contentType: 'application/xml' });
57
- *
58
- * // 自定义状态码和额外头
59
- * return Raw(ctx, { error: 'Not Found' }, {
60
- * status: 404,
61
- * headers: { 'X-Custom': 'value' }
62
- * });
63
- */
64
- export function Raw(ctx, data, options = {}) {
65
- const { status = 200, contentType, headers = {} } = options;
66
- // 自动判断 Content-Type
67
- let finalContentType = contentType;
68
- let body;
69
- if (typeof data === "string") {
70
- // 字符串类型
71
- body = data;
72
- if (!finalContentType) {
73
- // 自动判断:XML 或纯文本
74
- finalContentType = data.trim().startsWith("<") ? "application/xml" : "text/plain";
75
- }
76
- }
77
- else {
78
- // JSON 序列化(对象/数组/原始值均可)
79
- body = JSON.stringify(data);
80
- finalContentType = finalContentType || "application/json";
81
- }
82
- // 合并响应头
83
- const responseHeaders = {
84
- ...ctx.corsHeaders,
85
- "Content-Type": finalContentType,
86
- ...headers
87
- };
88
- return new Response(body, {
89
- status: status,
90
- headers: responseHeaders
91
- });
92
- }
93
- const toolPlugin = {
94
- name: "tool",
95
- enable: true,
96
- deps: [],
97
- handler: () => {
98
- return {
99
- Yes: Yes,
100
- No: No,
101
- Raw: Raw
102
- };
103
- }
104
- };
105
- export default toolPlugin;
@@ -1,14 +0,0 @@
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>;
@@ -1,109 +0,0 @@
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 生成的 path(例如 /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
- };
42
- const api = apis.get(apiPath);
43
- if (api) {
44
- ctx.api = api;
45
- }
46
- return withCtx({
47
- requestId: requestId,
48
- method: req.method,
49
- route: apiPath,
50
- ip: clientIp,
51
- now: now
52
- }, async () => {
53
- try {
54
- // 4. 串联执行所有钩子
55
- for (const hook of hooks) {
56
- await hook.handler(context, ctx);
57
- // 如果钩子已经设置了 response,停止执行
58
- if (ctx.response) {
59
- return ctx.response;
60
- }
61
- }
62
- // hooks 全部通过后记录请求日志(拦截请求仅由 ErrorResponse 记录)
63
- if (ctx.api && req.method !== "OPTIONS") {
64
- const logData = {
65
- event: "request",
66
- apiName: ctx.api.name
67
- };
68
- if (ctx.body && Object.keys(ctx.body).length > 0) {
69
- logData["body"] = ctx.body;
70
- }
71
- logData["msg"] = "request";
72
- Logger.info(logData);
73
- }
74
- // 5. 执行 API handler
75
- if (!ctx.api) {
76
- if (req.method !== "OPTIONS") {
77
- ctx.response = Response.json({
78
- code: 1,
79
- msg: "接口不存在"
80
- }, {
81
- headers: ctx.corsHeaders
82
- });
83
- }
84
- }
85
- else if (ctx.api.handler) {
86
- const result = await ctx.api.handler(context, ctx);
87
- if (result instanceof Response) {
88
- ctx.response = result;
89
- }
90
- else {
91
- ctx.result = result;
92
- }
93
- }
94
- // 7. 返回响应(自动处理 response/result/日志)
95
- return FinalResponse(ctx);
96
- }
97
- catch (err) {
98
- // 全局错误处理
99
- const errorPath = ctx.api ? apiPath : req.url;
100
- Logger.error({ err: err, path: errorPath, msg: "请求错误" });
101
- ctx.result = {
102
- code: 1,
103
- msg: "内部服务错误"
104
- };
105
- return FinalResponse(ctx);
106
- }
107
- });
108
- };
109
- }
@@ -1,9 +0,0 @@
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>;
@@ -1,56 +0,0 @@
1
- /**
2
- * 静态文件路由处理器
3
- * 处理 /* 路径的静态文件请求
4
- */
5
- // 外部依赖
6
- import { join } from "pathe";
7
- import { Logger } from "../lib/logger";
8
- // 相对导入
9
- import { appDir } from "../paths";
10
- import { setCorsOptions } from "../utils/cors";
11
- /**
12
- * 静态文件处理器工厂
13
- */
14
- export function staticHandler(corsConfig = undefined) {
15
- return async (req) => {
16
- // 设置 CORS 响应头
17
- const corsHeaders = setCorsOptions(req, corsConfig);
18
- const url = new URL(req.url);
19
- const filePath = join(appDir, "public", url.pathname);
20
- try {
21
- // OPTIONS预检请求
22
- if (req.method === "OPTIONS") {
23
- return new Response(null, {
24
- status: 204,
25
- headers: corsHeaders
26
- });
27
- }
28
- const file = Bun.file(filePath);
29
- if (await file.exists()) {
30
- return new Response(file, {
31
- headers: {
32
- "Content-Type": file.type || "application/octet-stream",
33
- ...corsHeaders
34
- }
35
- });
36
- }
37
- else {
38
- return Response.json({
39
- code: 1,
40
- msg: "文件未找到"
41
- }, {
42
- headers: corsHeaders
43
- });
44
- }
45
- }
46
- catch (error) {
47
- Logger.error({ err: error, msg: "静态文件处理失败" });
48
- return Response.json({
49
- code: 1,
50
- msg: "文件读取失败"
51
- }, {
52
- headers: corsHeaders
53
- });
54
- }
55
- };
56
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,296 +0,0 @@
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();
@@ -1,3 +0,0 @@
1
- import type { BeflyContext } from "../types/befly";
2
- import type { ScanFileResult } from "../utils/scanFiles";
3
- export declare function syncApi(ctx: Pick<BeflyContext, "db" | "cache">, apis: ScanFileResult[]): Promise<void>;