befly 3.10.19 → 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 (125) hide show
  1. package/dist/befly.config.d.ts +1 -1
  2. package/dist/befly.config.js +3 -5
  3. package/dist/befly.js +15621 -0
  4. package/dist/befly.min.js +21 -0
  5. package/dist/checks/checkApi.js +2 -3
  6. package/dist/checks/checkHook.js +50 -3
  7. package/dist/checks/checkMenu.d.ts +2 -2
  8. package/dist/checks/checkMenu.js +3 -3
  9. package/dist/checks/checkPlugin.js +50 -3
  10. package/dist/checks/checkTable.d.ts +1 -1
  11. package/dist/checks/checkTable.js +1 -1
  12. package/dist/hooks/auth.d.ts +3 -1
  13. package/dist/hooks/auth.js +3 -1
  14. package/dist/hooks/cors.d.ts +3 -1
  15. package/dist/hooks/cors.js +3 -1
  16. package/dist/hooks/parser.d.ts +3 -1
  17. package/dist/hooks/parser.js +4 -3
  18. package/dist/hooks/permission.d.ts +3 -1
  19. package/dist/hooks/permission.js +5 -3
  20. package/dist/hooks/validator.d.ts +3 -1
  21. package/dist/hooks/validator.js +4 -2
  22. package/dist/{main.d.ts → index.d.ts} +1 -1
  23. package/dist/{main.js → index.js} +24 -24
  24. package/dist/lib/cacheHelper.js +2 -2
  25. package/dist/lib/cipher.d.ts +1 -1
  26. package/dist/lib/connect.d.ts +1 -1
  27. package/dist/lib/connect.js +1 -1
  28. package/dist/lib/dbHelper.d.ts +3 -3
  29. package/dist/lib/dbHelper.js +9 -11
  30. package/dist/lib/dbUtils.d.ts +1 -1
  31. package/dist/lib/dbUtils.js +2 -3
  32. package/dist/lib/jwt.d.ts +2 -2
  33. package/dist/lib/logger.d.ts +17 -7
  34. package/dist/lib/logger.js +620 -210
  35. package/dist/lib/redisHelper.js +2 -2
  36. package/dist/lib/sqlBuilder.d.ts +1 -1
  37. package/dist/lib/sqlBuilder.js +1 -1
  38. package/dist/lib/validator.d.ts +1 -1
  39. package/dist/lib/validator.js +1 -1
  40. package/dist/loader/loadApis.d.ts +2 -2
  41. package/dist/loader/loadApis.js +3 -3
  42. package/dist/loader/loadHooks.d.ts +3 -4
  43. package/dist/loader/loadHooks.js +6 -8
  44. package/dist/loader/loadPlugins.d.ts +4 -4
  45. package/dist/loader/loadPlugins.js +7 -7
  46. package/dist/plugins/cache.d.ts +4 -2
  47. package/dist/plugins/cache.js +3 -1
  48. package/dist/plugins/cipher.d.ts +3 -1
  49. package/dist/plugins/cipher.js +3 -1
  50. package/dist/plugins/config.d.ts +4 -2
  51. package/dist/plugins/config.js +2 -0
  52. package/dist/plugins/db.d.ts +4 -2
  53. package/dist/plugins/db.js +6 -4
  54. package/dist/plugins/jwt.d.ts +4 -2
  55. package/dist/plugins/jwt.js +3 -1
  56. package/dist/plugins/logger.d.ts +7 -3
  57. package/dist/plugins/logger.js +3 -1
  58. package/dist/plugins/redis.d.ts +4 -2
  59. package/dist/plugins/redis.js +5 -3
  60. package/dist/plugins/tool.d.ts +3 -1
  61. package/dist/plugins/tool.js +2 -0
  62. package/dist/router/api.d.ts +3 -3
  63. package/dist/router/api.js +5 -5
  64. package/dist/router/static.d.ts +1 -1
  65. package/dist/router/static.js +3 -3
  66. package/dist/scripts/ensureDist.js +218 -2
  67. package/dist/sync/syncApi.d.ts +2 -2
  68. package/dist/sync/syncApi.js +2 -2
  69. package/dist/sync/syncCache.d.ts +1 -1
  70. package/dist/sync/syncDev.d.ts +1 -1
  71. package/dist/sync/syncDev.js +2 -2
  72. package/dist/sync/syncMenu.d.ts +2 -2
  73. package/dist/sync/syncMenu.js +4 -4
  74. package/dist/sync/syncTable.d.ts +6 -6
  75. package/dist/sync/syncTable.js +4 -4
  76. package/dist/types/api.d.ts +4 -4
  77. package/dist/types/befly.d.ts +8 -12
  78. package/dist/types/cache.d.ts +2 -2
  79. package/dist/types/cipher.d.ts +1 -1
  80. package/dist/types/context.d.ts +1 -1
  81. package/dist/types/database.d.ts +1 -1
  82. package/dist/types/hook.d.ts +4 -2
  83. package/dist/types/logger.d.ts +14 -2
  84. package/dist/types/plugin.d.ts +3 -1
  85. package/dist/types/sync.d.ts +2 -2
  86. package/dist/utils/cors.d.ts +1 -1
  87. package/dist/utils/importDefault.js +1 -1
  88. package/dist/utils/loadMenuConfigs.d.ts +26 -2
  89. package/dist/utils/loadMenuConfigs.js +44 -3
  90. package/dist/utils/mergeAndConcat.d.ts +7 -0
  91. package/dist/utils/mergeAndConcat.js +72 -0
  92. package/dist/utils/processAtSymbol.d.ts +4 -0
  93. package/dist/utils/{processFields.js → processAtSymbol.js} +2 -2
  94. package/dist/utils/response.d.ts +1 -1
  95. package/dist/utils/response.js +1 -1
  96. package/dist/utils/scanAddons.js +3 -3
  97. package/dist/utils/scanConfig.js +6 -7
  98. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  99. package/dist/utils/scanCoreBuiltins.js +65 -0
  100. package/dist/utils/scanFiles.js +19 -6
  101. package/dist/utils/scanSources.d.ts +2 -2
  102. package/dist/utils/scanSources.js +16 -11
  103. package/dist/utils/sortModules.js +2 -2
  104. package/dist/utils/util.d.ts +84 -0
  105. package/dist/utils/util.js +262 -0
  106. package/package.json +20 -14
  107. package/dist/utils/arrayKeysToCamel.d.ts +0 -13
  108. package/dist/utils/arrayKeysToCamel.js +0 -18
  109. package/dist/utils/configTypes.d.ts +0 -1
  110. package/dist/utils/configTypes.js +0 -1
  111. package/dist/utils/genShortId.d.ts +0 -10
  112. package/dist/utils/genShortId.js +0 -12
  113. package/dist/utils/keysToCamel.d.ts +0 -10
  114. package/dist/utils/keysToCamel.js +0 -21
  115. package/dist/utils/keysToSnake.d.ts +0 -10
  116. package/dist/utils/keysToSnake.js +0 -21
  117. package/dist/utils/pickFields.d.ts +0 -4
  118. package/dist/utils/pickFields.js +0 -16
  119. package/dist/utils/processFields.d.ts +0 -4
  120. package/dist/utils/regex.d.ts +0 -145
  121. package/dist/utils/regex.js +0 -202
  122. package/dist/utils/sqlLog.d.ts +0 -14
  123. package/dist/utils/sqlLog.js +0 -25
  124. /package/dist/utils/{process.d.ts → processInfo.d.ts} +0 -0
  125. /package/dist/utils/{process.js → processInfo.js} +0 -0
@@ -0,0 +1,72 @@
1
+ import { isPlainObject } from "./util";
2
+ function cloneDeepLoose(value) {
3
+ if (Array.isArray(value)) {
4
+ const arr = [];
5
+ for (const item of value) {
6
+ arr.push(cloneDeepLoose(item));
7
+ }
8
+ return arr;
9
+ }
10
+ if (isPlainObject(value)) {
11
+ const out = {};
12
+ for (const key of Object.keys(value)) {
13
+ out[key] = cloneDeepLoose(value[key]);
14
+ }
15
+ return out;
16
+ }
17
+ return value;
18
+ }
19
+ function mergeInto(target, source) {
20
+ if (source === undefined) {
21
+ return target;
22
+ }
23
+ if (Array.isArray(target) && Array.isArray(source)) {
24
+ for (const item of source) {
25
+ target.push(cloneDeepLoose(item));
26
+ }
27
+ return target;
28
+ }
29
+ if (isPlainObject(target) && isPlainObject(source)) {
30
+ for (const key of Object.keys(source)) {
31
+ const srcVal = source[key];
32
+ if (srcVal === undefined) {
33
+ continue;
34
+ }
35
+ const curVal = target[key];
36
+ if (Array.isArray(curVal) && Array.isArray(srcVal)) {
37
+ const nextArr = [];
38
+ for (const item of curVal) {
39
+ nextArr.push(cloneDeepLoose(item));
40
+ }
41
+ for (const item of srcVal) {
42
+ nextArr.push(cloneDeepLoose(item));
43
+ }
44
+ target[key] = nextArr;
45
+ continue;
46
+ }
47
+ if (isPlainObject(curVal) && isPlainObject(srcVal)) {
48
+ target[key] = mergeInto(cloneDeepLoose(curVal), srcVal);
49
+ continue;
50
+ }
51
+ target[key] = cloneDeepLoose(srcVal);
52
+ }
53
+ return target;
54
+ }
55
+ return cloneDeepLoose(source);
56
+ }
57
+ /**
58
+ * 深度合并对象,并对数组执行 concat(保持 scanConfig 现有语义)。
59
+ * - undefined 会被忽略
60
+ * - plain object 深合并
61
+ * - array 与 array 合并为新数组(保持输入不被污染)
62
+ */
63
+ export function mergeAndConcat(...items) {
64
+ let acc = {};
65
+ for (const item of items) {
66
+ if (item === undefined) {
67
+ continue;
68
+ }
69
+ acc = mergeInto(acc, item);
70
+ }
71
+ return acc;
72
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 处理字段定义:将 @ 符号引用替换为实际字段定义
3
+ */
4
+ export declare function processAtSymbol(fields: Record<string, any>, apiName: string, routePath: string): Record<string, any>;
@@ -1,8 +1,8 @@
1
- import { presetFields } from "../configs/presetFields.js";
1
+ import { presetFields } from "../configs/presetFields";
2
2
  /**
3
3
  * 处理字段定义:将 @ 符号引用替换为实际字段定义
4
4
  */
5
- export function processFields(fields, apiName, routePath) {
5
+ export function processAtSymbol(fields, apiName, routePath) {
6
6
  if (!fields || typeof fields !== "object")
7
7
  return fields;
8
8
  const processed = {};
@@ -1,4 +1,4 @@
1
- import type { RequestContext } from "../types/context.ts";
1
+ import type { RequestContext } from "../types/context";
2
2
  /**
3
3
  * 创建错误响应(专用于 Hook 中间件)
4
4
  * 在钩子中提前拦截请求时使用
@@ -1,4 +1,4 @@
1
- import { Logger } from "../lib/logger.js";
1
+ import { Logger } from "../lib/logger";
2
2
  /**
3
3
  * 创建错误响应(专用于 Hook 中间件)
4
4
  * 在钩子中提前拦截请求时使用
@@ -1,8 +1,8 @@
1
1
  import { existsSync, readdirSync } from "node:fs";
2
- import { camelCase } from "es-toolkit/string";
3
2
  import { join, resolve } from "pathe";
4
- import { appAddonsDir, appDir } from "../paths.js";
5
- import { isDirentDirectory } from "./isDirentDirectory.js";
3
+ import { appAddonsDir, appDir } from "../paths";
4
+ import { isDirentDirectory } from "./isDirentDirectory";
5
+ import { camelCase } from "./util";
6
6
  /** 扫描 node_modules/@befly-addon + 项目 addons/(项目优先级更高) */
7
7
  export const scanAddons = () => {
8
8
  const addonMap = new Map();
@@ -9,9 +9,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
9
9
  import { existsSync } from "node:fs";
10
10
  import { isAbsolute, join } from "node:path";
11
11
  import { pathToFileURL } from "node:url";
12
- import { isPlainObject } from "es-toolkit";
13
- import { get, set } from "es-toolkit/compat";
14
- import { mergeAndConcat } from "merge-anything";
12
+ import { mergeAndConcat } from "./mergeAndConcat";
13
+ import { getByPath, isPlainObject, setByPath } from "./util";
15
14
  /**
16
15
  * 扫描并合并配置文件(矩阵搜索:dirs × files)
17
16
  * @param options - 加载选项
@@ -68,9 +67,9 @@ export async function scanConfig(options) {
68
67
  if (paths && paths.length > 0) {
69
68
  const result = {};
70
69
  for (const path of paths) {
71
- const value = get(firstConfig, path);
70
+ const value = getByPath(firstConfig, path);
72
71
  if (value !== undefined) {
73
- set(result, path, value);
72
+ setByPath(result, path, value);
74
73
  }
75
74
  }
76
75
  return result;
@@ -94,9 +93,9 @@ export async function scanConfig(options) {
94
93
  if (paths && paths.length > 0) {
95
94
  const result = {};
96
95
  for (const path of paths) {
97
- const value = get(finalConfig, path);
96
+ const value = getByPath(finalConfig, path);
98
97
  if (value !== undefined) {
99
- set(result, path, value);
98
+ setByPath(result, path, value);
100
99
  }
101
100
  }
102
101
  return result;
@@ -0,0 +1,3 @@
1
+ import type { ScanFileResult } from "./scanFiles";
2
+ export declare function scanCoreBuiltinPlugins(): ScanFileResult[];
3
+ export declare function scanCoreBuiltinHooks(): ScanFileResult[];
@@ -0,0 +1,65 @@
1
+ import coreHookAuth from "../hooks/auth";
2
+ import coreHookCors from "../hooks/cors";
3
+ import coreHookParser from "../hooks/parser";
4
+ import coreHookPermission from "../hooks/permission";
5
+ import coreHookValidator from "../hooks/validator";
6
+ import corePluginCache from "../plugins/cache";
7
+ import corePluginCipher from "../plugins/cipher";
8
+ import corePluginConfig from "../plugins/config";
9
+ import corePluginDb from "../plugins/db";
10
+ import corePluginJwt from "../plugins/jwt";
11
+ import corePluginLogger from "../plugins/logger";
12
+ import corePluginRedis from "../plugins/redis";
13
+ import corePluginTool from "../plugins/tool";
14
+ function toCoreBuiltinScanFileResult(type, item) {
15
+ const name = item.name;
16
+ return {
17
+ source: "core",
18
+ type: type,
19
+ sourceName: "核心",
20
+ filePath: `core:${type}:${name}`,
21
+ relativePath: name,
22
+ fileName: name,
23
+ moduleName: name,
24
+ addonName: "",
25
+ fileBaseName: name,
26
+ fileDir: "(builtin)",
27
+ name: name,
28
+ enable: item ? item.enable : undefined,
29
+ deps: Array.isArray(item && item.deps) ? item.deps : [],
30
+ handler: item ? item.handler : null
31
+ };
32
+ }
33
+ export function scanCoreBuiltinPlugins() {
34
+ const plugins = [];
35
+ const builtinPlugins = [
36
+ //
37
+ corePluginLogger,
38
+ corePluginRedis,
39
+ corePluginDb,
40
+ corePluginCache,
41
+ corePluginTool,
42
+ corePluginCipher,
43
+ corePluginJwt,
44
+ corePluginConfig
45
+ ];
46
+ for (const plugin of builtinPlugins) {
47
+ plugins.push(toCoreBuiltinScanFileResult("plugin", plugin));
48
+ }
49
+ return plugins;
50
+ }
51
+ export function scanCoreBuiltinHooks() {
52
+ const hooks = [];
53
+ const builtinHooks = [
54
+ //
55
+ coreHookAuth,
56
+ coreHookCors,
57
+ coreHookParser,
58
+ coreHookValidator,
59
+ coreHookPermission
60
+ ];
61
+ for (const hook of builtinHooks) {
62
+ hooks.push(toCoreBuiltinScanFileResult("hook", hook));
63
+ }
64
+ return hooks;
65
+ }
@@ -1,8 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
- import { forOwn } from "es-toolkit/compat";
3
- import { camelCase } from "es-toolkit/string";
4
2
  import { relative, normalize, parse, join } from "pathe";
5
- import { importDefault } from "./importDefault.js";
3
+ import { importDefault } from "./importDefault";
4
+ import { camelCase, forOwn } from "./util";
6
5
  function parseAddonNameFromPath(normalizedPath) {
7
6
  // 期望路径中包含:/node_modules/@befly-addon/<addonName>/...
8
7
  const parts = normalizedPath.split("/").filter(Boolean);
@@ -14,6 +13,18 @@ function parseAddonNameFromPath(normalizedPath) {
14
13
  return null;
15
14
  return addonName;
16
15
  }
16
+ function parseLocalAddonNameFromPath(normalizedPath) {
17
+ // 兼容项目本地 addons:/addons/<addonName>/...
18
+ // 注意:normalizedPath 已通过 pathe.normalize 统一为 /
19
+ const parts = normalizedPath.split("/").filter(Boolean);
20
+ const idx = parts.lastIndexOf("addons");
21
+ if (idx < 0)
22
+ return null;
23
+ const addonName = parts[idx + 1];
24
+ if (typeof addonName !== "string" || addonName.trim() === "")
25
+ return null;
26
+ return addonName;
27
+ }
17
28
  /**
18
29
  * 扫描指定目录下的文件
19
30
  * @param dir 目录路径
@@ -55,13 +66,15 @@ export async function scanFiles(dir, source, type, pattern) {
55
66
  let addonName = "";
56
67
  let moduleName = "";
57
68
  if (source === "core") {
58
- moduleName = baseName;
69
+ // core:不做任何命名转换,直接使用文件名作为 moduleName(例如 auth / rate_limit)。
70
+ addonName = "";
71
+ moduleName = fileName;
59
72
  }
60
73
  else if (source === "app") {
61
74
  moduleName = `app_${baseName}`;
62
75
  }
63
76
  else {
64
- const parsedAddonName = parseAddonNameFromPath(normalizedFile);
77
+ const parsedAddonName = parseAddonNameFromPath(normalizedFile) || parseLocalAddonNameFromPath(normalizedFile);
65
78
  if (!parsedAddonName) {
66
79
  throw new Error(`scanFiles addon moduleName 解析失败:未找到 @befly-addon/<addon>/ 段落:${normalizedFile}`);
67
80
  }
@@ -101,7 +114,7 @@ export async function scanFiles(dir, source, type, pattern) {
101
114
  base[key] = value;
102
115
  });
103
116
  if (type === "api") {
104
- base.routePrefix = source === "core" ? "/core" : source === "app" ? "/app" : `/addon/${addonName}`;
117
+ base.routePrefix = source === "app" ? "/app" : `/addon/${addonName}`;
105
118
  base.routePath = `/api${base.routePrefix}/${relativePath}`;
106
119
  }
107
120
  results.push(base);
@@ -1,5 +1,5 @@
1
- import type { AddonInfo } from "./scanAddons.ts";
2
- import type { ScanFileResult } from "./scanFiles.ts";
1
+ import type { AddonInfo } from "./scanAddons";
2
+ import type { ScanFileResult } from "./scanFiles";
3
3
  export type ScanSourcesResult = {
4
4
  hooks: ScanFileResult[];
5
5
  plugins: ScanFileResult[];
@@ -1,7 +1,8 @@
1
1
  import { join } from "pathe";
2
- import { coreDistDir, appDir } from "../paths.js";
3
- import { scanAddons } from "./scanAddons.js";
4
- import { scanFiles } from "./scanFiles.js";
2
+ import { appDir } from "../paths";
3
+ import { scanAddons } from "./scanAddons";
4
+ import { scanCoreBuiltinHooks, scanCoreBuiltinPlugins } from "./scanCoreBuiltins";
5
+ import { scanFiles } from "./scanFiles";
5
6
  export const scanSources = async () => {
6
7
  const apis = [];
7
8
  const plugins = [];
@@ -14,23 +15,27 @@ export const scanSources = async () => {
14
15
  tables.push(...(await scanFiles(join(addon.fullPath, "tables"), "addon", "table", "*.json")));
15
16
  }
16
17
  // 处理插件
17
- plugins.push(...(await scanFiles(join(coreDistDir, "plugins"), "core", "plugin", "*.js")));
18
+ // core 内置插件:必须静态导入(支持 bun bundle 单文件)。
19
+ // 约束:core 插件名由 export default.name 指定。
20
+ plugins.push(...scanCoreBuiltinPlugins());
18
21
  plugins.push(...(await scanFiles(join(appDir, "plugins"), "app", "plugin", "*.{ts,js}")));
19
22
  for (const addon of addons) {
20
23
  plugins.push(...(await scanFiles(join(addon.fullPath, "plugins"), "addon", "plugin", "*.{ts,js}")));
21
24
  }
22
- // 处理接口
23
- apis.push(...(await scanFiles(join(coreDistDir, "apis"), "core", "api", "**/*.js")));
24
- apis.push(...(await scanFiles(join(appDir, "apis"), "app", "api", "**/*.{ts,js}")));
25
- for (const addon of addons) {
26
- apis.push(...(await scanFiles(join(addon.fullPath, "apis"), "addon", "api", "**/*.{ts,js}")));
27
- }
28
25
  // 处理钩子
29
- hooks.push(...(await scanFiles(join(coreDistDir, "hooks"), "core", "hook", "*.js")));
26
+ // core 内置钩子:必须静态导入(支持 bun bundle 单文件)。
27
+ // 约束:core 钩子名由 export default.name 指定。
28
+ hooks.push(...scanCoreBuiltinHooks());
30
29
  hooks.push(...(await scanFiles(join(appDir, "hooks"), "app", "hook", "*.{ts,js}")));
31
30
  for (const addon of addons) {
32
31
  hooks.push(...(await scanFiles(join(addon.fullPath, "hooks"), "addon", "hook", "*.{ts,js}")));
33
32
  }
33
+ // 处理接口
34
+ // 说明:core 没有内置 apis;接口只从「项目(app)」与「组件(addon)」中扫描加载。
35
+ apis.push(...(await scanFiles(join(appDir, "apis"), "app", "api", "**/*.{ts,js}")));
36
+ for (const addon of addons) {
37
+ apis.push(...(await scanFiles(join(addon.fullPath, "apis"), "addon", "api", "**/*.{ts,js}")));
38
+ }
34
39
  return {
35
40
  hooks: hooks,
36
41
  plugins: plugins,
@@ -1,5 +1,5 @@
1
- import { camelCase } from "es-toolkit/string";
2
- import { Logger } from "../lib/logger.js";
1
+ import { Logger } from "../lib/logger";
2
+ import { camelCase } from "./util";
3
3
  /**
4
4
  * 按 deps 拓扑排序 scanSources 扫描得到的插件/钩子。
5
5
  *
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 注意:本文件用于集中维护 core 内部通用 utils。
3
+ * - 按项目规范:避免零散小文件噪音;实现集中在本文件,而不是做 re-export 聚合。
4
+ * - 仅在 packages/core 内部使用;core 对外不承诺这些路径导出。
5
+ */
6
+ export declare function isPlainObject(value: unknown): value is Record<string, any>;
7
+ /**
8
+ * 激进空值判断(项目约定):
9
+ * - null/undefined => empty
10
+ * - "" / 全空白字符串 => empty
11
+ * - 0 / NaN => empty
12
+ * - false => empty
13
+ * - Array => length === 0
14
+ * - Map/Set => size === 0
15
+ * - plain object => 无自有 key
16
+ * - 其他类型 => false
17
+ */
18
+ export declare function isEmpty(value: unknown): boolean;
19
+ export declare function forOwn(obj: unknown, iteratee: (value: any, key: string) => void): void;
20
+ /**
21
+ * 把字符串转为小驼峰。
22
+ * - 主要用于文件名/目录名(例如 my_plugin / my-plugin / my plugin)。
23
+ */
24
+ export declare function camelCase(input: string): string;
25
+ /**
26
+ * 把字符串转为 snake_case。
27
+ * - 主要用于表名/字段名(例如 userId -> user_id)。
28
+ */
29
+ export declare function snakeCase(input: string): string;
30
+ /**
31
+ * 对象字段名转小驼峰
32
+ * @param obj - 源对象
33
+ * @returns 字段名转为小驼峰格式的新对象
34
+ *
35
+ * @example
36
+ * keysToCamel({ user_id: 123, user_name: 'John' }) // { userId: 123, userName: 'John' }
37
+ * keysToCamel({ created_at: 1697452800000 }) // { createdAt: 1697452800000 }
38
+ */
39
+ export declare const keysToCamel: <T = any>(obj: Record<string, any>) => T;
40
+ /**
41
+ * 对象字段名转下划线
42
+ * @param obj - 源对象
43
+ * @returns 字段名转为下划线格式的新对象
44
+ *
45
+ * @example
46
+ * keysToSnake({ userId: 123, userName: 'John' }) // { user_id: 123, user_name: 'John' }
47
+ * keysToSnake({ createdAt: 1697452800000 }) // { created_at: 1697452800000 }
48
+ */
49
+ export declare const keysToSnake: <T = any>(obj: Record<string, any>) => T;
50
+ /**
51
+ * 数组对象字段名批量转小驼峰
52
+ * @param arr - 源数组
53
+ * @returns 字段名转为小驼峰格式的新数组
54
+ *
55
+ * @example
56
+ * arrayKeysToCamel([
57
+ * { user_id: 1, user_name: 'John' },
58
+ * { user_id: 2, user_name: 'Jane' }
59
+ * ])
60
+ * // [{ userId: 1, userName: 'John' }, { userId: 2, userName: 'Jane' }]
61
+ */
62
+ export declare const arrayKeysToCamel: <T = any>(arr: Record<string, any>[]) => T[];
63
+ export declare function getByPath(obj: unknown, path: string): unknown;
64
+ export declare function setByPath(target: Record<string, any>, path: string, value: unknown): void;
65
+ /**
66
+ * 返回一个移除指定 key 的浅拷贝。
67
+ * - 仅处理 plain object;其他类型返回空对象,避免日志场景抛错。
68
+ */
69
+ export declare function omit<T extends Record<string, any>>(obj: unknown, keys: string[]): Partial<T>;
70
+ /**
71
+ * 挑选指定字段
72
+ */
73
+ export declare const pickFields: <T extends Record<string, any>>(obj: T, keys: string[]) => Partial<T>;
74
+ export declare function keyBy<T>(items: T[], getKey: (item: T) => string): Record<string, T>;
75
+ /**
76
+ * 生成短 ID
77
+ * 由时间戳(base36)+ 随机字符组成,约 13 位
78
+ * - 前 8 位:时间戳(可排序)
79
+ * - 后 5 位:随机字符(防冲突)
80
+ * @returns 短 ID 字符串
81
+ * @example
82
+ * genShortId() // "lxyz1a2b3c4"
83
+ */
84
+ export declare function genShortId(): string;