@jayfong/x-utils 1.0.0-202510061015 → 3.4.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 (143) hide show
  1. package/lib/ai.d.mts +103 -0
  2. package/lib/ai.mjs +75 -0
  3. package/lib/cache.d.mts +464 -0
  4. package/lib/cache.mjs +531 -0
  5. package/lib/captcha.d.mts +259 -0
  6. package/lib/captcha.mjs +174 -0
  7. package/lib/case.d.mts +184 -0
  8. package/lib/case.mjs +120 -0
  9. package/lib/chinese.d.mts +57 -0
  10. package/lib/chinese.mjs +79 -0
  11. package/lib/cli.d.mts +52 -0
  12. package/lib/cli.mjs +62 -0
  13. package/lib/clipboard.d.mts +50 -0
  14. package/lib/clipboard.mjs +122 -0
  15. package/lib/cmd.d.mts +38 -0
  16. package/lib/cmd.mjs +52 -0
  17. package/lib/crawler.d.mts +330 -0
  18. package/lib/crawler.mjs +313 -0
  19. package/lib/crypto.d.mts +158 -0
  20. package/lib/crypto.mjs +213 -0
  21. package/lib/csv.d.mts +67 -0
  22. package/lib/csv.mjs +76 -0
  23. package/lib/data_set.d.mts +76 -0
  24. package/lib/data_set.mjs +104 -0
  25. package/lib/db_value.d.mts +213 -0
  26. package/lib/db_value.mjs +208 -0
  27. package/lib/dispose.d.mts +98 -0
  28. package/lib/dispose.mjs +107 -0
  29. package/lib/file_store.d.mts +167 -0
  30. package/lib/file_store.mjs +120 -0
  31. package/lib/fs.d.mts +111 -0
  32. package/lib/fs.mjs +127 -0
  33. package/lib/html.d.mts +103 -0
  34. package/lib/html.mjs +112 -0
  35. package/lib/json.d.mts +50 -0
  36. package/lib/json.mjs +74 -0
  37. package/lib/jwt.d.mts +128 -0
  38. package/lib/jwt.mjs +104 -0
  39. package/lib/mail.d.mts +125 -0
  40. package/lib/mail.mjs +108 -0
  41. package/lib/pay.d.mts +394 -0
  42. package/lib/pay.mjs +343 -0
  43. package/lib/puppeteer.d.mts +88 -0
  44. package/lib/puppeteer.mjs +219 -0
  45. package/lib/random.d.mts +71 -0
  46. package/lib/random.mjs +92 -0
  47. package/lib/redis.d.mts +32 -0
  48. package/lib/redis.mjs +24 -0
  49. package/lib/runner.d.mts +80 -0
  50. package/lib/runner.mjs +66 -0
  51. package/lib/sensitive_words.d.mts +82 -0
  52. package/lib/sensitive_words.mjs +73 -0
  53. package/lib/sms.d.mts +133 -0
  54. package/lib/sms.mjs +100 -0
  55. package/lib/task.d.mts +37 -0
  56. package/lib/task.mjs +43 -0
  57. package/lib/text.d.mts +84 -0
  58. package/lib/text.mjs +141 -0
  59. package/lib/yaml.d.mts +51 -0
  60. package/lib/yaml.mjs +64 -0
  61. package/package.json +82 -164
  62. package/_cjs/ai.js +0 -80
  63. package/_cjs/alipan.js +0 -173
  64. package/_cjs/cli.js +0 -51
  65. package/_cjs/clipboard.js +0 -65
  66. package/_cjs/cmd.js +0 -42
  67. package/_cjs/compressor.js +0 -47
  68. package/_cjs/crawler.js +0 -341
  69. package/_cjs/csv.js +0 -97
  70. package/_cjs/data_set.js +0 -115
  71. package/_cjs/env.js +0 -82
  72. package/_cjs/fs.js +0 -86
  73. package/_cjs/html.js +0 -62
  74. package/_cjs/http_server.js +0 -79
  75. package/_cjs/json.js +0 -49
  76. package/_cjs/ksb.js +0 -218
  77. package/_cjs/number.js +0 -39
  78. package/_cjs/ocr.js +0 -74
  79. package/_cjs/package.json +0 -3
  80. package/_cjs/path.js +0 -35
  81. package/_cjs/pinyin.js +0 -37
  82. package/_cjs/puppeteer.js +0 -271
  83. package/_cjs/runner.js +0 -88
  84. package/_cjs/task.js +0 -41
  85. package/_cjs/text.js +0 -136
  86. package/_cjs/unicode/lib_unicode.js +0 -58643
  87. package/_cjs/unicode/unicode.js +0 -441
  88. package/_cjs/upload.js +0 -101
  89. package/_cjs/yaml.js +0 -52
  90. package/ai.d.ts +0 -26
  91. package/ai.js +0 -60
  92. package/alipan.d.ts +0 -209
  93. package/alipan.js +0 -153
  94. package/cli.d.ts +0 -14
  95. package/cli.js +0 -21
  96. package/clipboard.d.ts +0 -6
  97. package/clipboard.js +0 -35
  98. package/cmd.d.ts +0 -5
  99. package/cmd.js +0 -22
  100. package/compressor.d.ts +0 -5
  101. package/compressor.js +0 -17
  102. package/crawler.d.ts +0 -162
  103. package/crawler.js +0 -332
  104. package/csv.d.ts +0 -14
  105. package/csv.js +0 -67
  106. package/data_set.d.ts +0 -22
  107. package/data_set.js +0 -85
  108. package/env.d.ts +0 -6
  109. package/env.js +0 -52
  110. package/fs.d.ts +0 -23
  111. package/fs.js +0 -56
  112. package/html.d.ts +0 -7
  113. package/html.js +0 -45
  114. package/http_server.d.ts +0 -13
  115. package/http_server.js +0 -49
  116. package/json.d.ts +0 -6
  117. package/json.js +0 -19
  118. package/ksb.d.ts +0 -37
  119. package/ksb.js +0 -198
  120. package/number.d.ts +0 -3
  121. package/number.js +0 -9
  122. package/ocr.d.ts +0 -17
  123. package/ocr.js +0 -54
  124. package/path.d.ts +0 -2
  125. package/path.js +0 -5
  126. package/pinyin.d.ts +0 -8
  127. package/pinyin.js +0 -17
  128. package/puppeteer.d.ts +0 -38
  129. package/puppeteer.js +0 -241
  130. package/runner.d.ts +0 -13
  131. package/runner.js +0 -58
  132. package/task.d.ts +0 -6
  133. package/task.js +0 -21
  134. package/text.d.ts +0 -63
  135. package/text.js +0 -119
  136. package/unicode/lib_unicode.d.ts +0 -4
  137. package/unicode/lib_unicode.js +0 -58623
  138. package/unicode/unicode.d.ts +0 -347
  139. package/unicode/unicode.js +0 -426
  140. package/upload.d.ts +0 -34
  141. package/upload.js +0 -71
  142. package/yaml.d.ts +0 -6
  143. package/yaml.js +0 -22
@@ -0,0 +1,107 @@
1
+ import { asyncExitHook } from "exit-hook";
2
+ // src/dispose.ts
3
+ /**
4
+ * 资源释放管理器。
5
+ *
6
+ * 用于集中管理一组同步/异步清理函数,并在退出时统一、可靠地释放,
7
+ * 适用于数据库连接、文件句柄、定时器、子进程等资源的生命周期管理。
8
+ *
9
+ * 主要行为:
10
+ * - 通过 `add()` 按注册顺序追加清理函数;
11
+ * - `dispose()` 幂等:首次调用按注册顺序依次执行全部清理函数,执行后
12
+ * 清空列表,之后再次调用直接返回,不会重复执行;
13
+ * - 清理函数逐个执行,单个抛错不会中断后续清理,全部执行完后将错误
14
+ * 以 `AggregateError` 汇总抛出(无错误则不抛);
15
+ * - 实现 `Symbol.asyncDispose`,可直接配合 `await using` 使用;
16
+ * - 开启 `disposeOnExit` 时自动注册进程退出钩子,退出时调用 `dispose()`。
17
+ *
18
+ * @example
19
+ * const dispose = new XDispose({ disposeOnExit: true });
20
+ *
21
+ * dispose.add(() => db.close());
22
+ * dispose.add(async () => {
23
+ * await fs.rm(tmpDir, { recursive: true, force: true });
24
+ * });
25
+ *
26
+ * // 进程退出时自动清理,也可手动触发
27
+ * await dispose.dispose();
28
+ */
29
+ var XDispose = class XDispose {
30
+ options;
31
+ /** 已注册的清理函数列表,按注册顺序存放。 */
32
+ disposes = [];
33
+ /** 是否已完成清理,保证 `dispose()` 幂等。 */
34
+ disposed = false;
35
+ /**
36
+ * 创建资源释放管理器。
37
+ *
38
+ * @param options 构造配置;`disposeOnExit` 为 `true` 时立即注册
39
+ * 进程退出钩子(见 {@link XDisposeOptions})。
40
+ */
41
+ constructor(options) {
42
+ this.options = options;
43
+ if (this.options?.disposeOnExit) asyncExitHook(() => this.dispose().catch((err) => {
44
+ console.error("进程退出清理失败", err);
45
+ }), { wait: 3e3 });
46
+ }
47
+ /**
48
+ * 注册一个清理函数。
49
+ *
50
+ * 清理函数支持同步或异步返回值,`dispose()` 执行时会 `await` 其结果。
51
+ * 已调用过 `dispose()` 后再注册会抛错,因为此时资源已释放完毕。
52
+ *
53
+ * @param fn 清理函数,按注册顺序在 `dispose()` 中依次执行。
54
+ *
55
+ * @example
56
+ * dispose.add(() => server.close());
57
+ */
58
+ add(fn) {
59
+ if (this.disposed) throw new Error("XDispose 已完成清理,不能再注册新的清理函数");
60
+ this.disposes.push(fn);
61
+ }
62
+ /**
63
+ * 释放所有已注册的资源。
64
+ *
65
+ * 幂等:首次调用按注册顺序依次执行全部清理函数并清空列表,之后的
66
+ * 调用直接返回。单个清理函数抛错不会中断后续执行,全部执行完后若
67
+ * 存在错误则以 `AggregateError` 汇总抛出。
68
+ *
69
+ * @returns 全部清理函数成功执行后 resolve;存在失败时 reject,
70
+ * 错误为聚合了全部失败项的 `AggregateError`。
71
+ */
72
+ async dispose() {
73
+ if (this.disposed) return;
74
+ this.disposed = true;
75
+ const errors = [];
76
+ for (const fn of this.disposes) try {
77
+ await fn();
78
+ } catch (err) {
79
+ errors.push(err);
80
+ }
81
+ this.disposes = [];
82
+ if (errors.length > 0) throw new AggregateError(errors, "XDispose 清理过程中发生错误");
83
+ }
84
+ /**
85
+ * `await using` 支持。
86
+ *
87
+ * 等价于调用 `dispose()`,用于在 `using` 语句作用域结束时自动释放。
88
+ */
89
+ async [Symbol.asyncDispose]() {
90
+ await this.dispose();
91
+ }
92
+ /**
93
+ * 进程级共享的释放管理器实例。
94
+ *
95
+ * 该实例已开启 `disposeOnExit`,进程退出时会自动执行注册到其上的全部
96
+ * 清理函数。适用于不希望手动创建并持有 `XDispose` 实例的场景,直接
97
+ * 通过 `XDispose.exit.add(...)` 注册清理函数即可,进程退出时统一释放。
98
+ *
99
+ * 注意:作为进程级单例,该实例被 `dispose()` 后无法再注册清理函数,
100
+ * 通常无需手动调用,进程退出时由退出钩子自动处理。
101
+ *
102
+ * @example
103
+ * XDispose.exit.add(() => db.close());
104
+ */
105
+ static exit = new XDispose({ disposeOnExit: true });
106
+ };
107
+ export { XDispose };
@@ -0,0 +1,167 @@
1
+ import { MsValue } from "vtils";
2
+ /**
3
+ * `XFileStore` 的初始化配置。
4
+ *
5
+ * 面向腾讯云 COS 等兼容 S3 协议的对象存储:通过预签名 URL 让客户端
6
+ * 直传文件,文件内容不经过业务服务器中转。
7
+ */
8
+ export interface XFileStoreOptions {
9
+ /** 对象存储访问密钥 ID(如腾讯云 SecretId)。 */
10
+ accessKey: string;
11
+ /** 对象存储访问密钥 Key(如腾讯云 SecretKey)。 */
12
+ secretKey: string;
13
+ /**
14
+ * 对象存储服务端点,可带或不带协议前缀。
15
+ *
16
+ * - 不含协议(如 `cos.ap-guangzhou.myqcloud.com`)时,自动补全为
17
+ * `https://`;
18
+ * - 已是完整 URL(如 `https://cos.ap-guangzhou.myqcloud.com`)时
19
+ * 原样保留。
20
+ */
21
+ endpoint: string;
22
+ /** 存储桶名称,例如 `image-311111`。 */
23
+ bucket: string;
24
+ /**
25
+ * 存储目录前缀,可为空字符串,表示上传到桶根目录。
26
+ *
27
+ * 构造时会去除首尾的 `/`,例如 `/images/` 会被规范化为 `images`。
28
+ */
29
+ dir: string;
30
+ /**
31
+ * 文件公开访问的基础地址(通常为 CDN 域名),可带或不带协议前缀。
32
+ *
33
+ * 构造时会去除末尾的 `/`;最终公开地址为
34
+ * `${cdnUrl}/${dir}/${fileName}`。
35
+ */
36
+ cdnUrl: string;
37
+ }
38
+ /**
39
+ * `getUploadUrl` 的入参。
40
+ */
41
+ export interface XFileStoreGetUploadUrlPayload {
42
+ /**
43
+ * 原始文件名,仅用于推导 MIME 类型与生成最终文件名。
44
+ *
45
+ * - `keepName` 为 `false`(默认)时,最终文件名会被替换为
46
+ * `${XRandom.cuid2()}.${扩展名}`,原始文件名只参与 MIME 推导;
47
+ * - `keepName` 为 `true` 时,最终文件名与原始文件名完全一致。
48
+ */
49
+ fileName: string;
50
+ /**
51
+ * 预签名 URL 的有效时长,需大于 0。
52
+ *
53
+ * 支持毫秒数字(如 `60000`)或时长字符串(如 `'1m'`、`'2h'`),
54
+ * 内部统一换算为秒后传给 AWS SDK。
55
+ */
56
+ ttl: MsValue;
57
+ /**
58
+ * 是否保留原始文件名。
59
+ *
60
+ * @default false 默认生成随机文件名,避免同名覆盖与外部可控路径写入。
61
+ */
62
+ keepName?: boolean;
63
+ }
64
+ /**
65
+ * `getUploadUrl` 的结果。
66
+ */
67
+ export interface XFileStoreGetUploadUrlResult {
68
+ /** 文件 MIME 类型,例如 `image/png`。 */
69
+ contentType: string;
70
+ /** 预签名上传地址,客户端可直接对其发起 PUT 上传。 */
71
+ uploadUrl: string;
72
+ /** 文件上传后的公开访问地址。 */
73
+ publicUrl: string;
74
+ }
75
+ /**
76
+ * `upload` 的入参。
77
+ */
78
+ export interface XFileStoreUploadPayload {
79
+ /** 文件名,语义与 `XFileStoreGetUploadUrlPayload.fileName` 一致。 */
80
+ fileName: string;
81
+ /** 文件二进制内容。 */
82
+ content: Buffer;
83
+ /**
84
+ * 是否保留原始文件名。
85
+ *
86
+ * @default false 默认生成随机文件名。
87
+ */
88
+ keepName?: boolean;
89
+ }
90
+ /**
91
+ * `upload` 的结果。
92
+ *
93
+ * 与 `getUploadUrl` 返回结构一致:上传完成后返回与预签名阶段相同的
94
+ * 内容类型、上传地址与公开访问地址。
95
+ */
96
+ export interface XFileStoreUploadResult extends XFileStoreGetUploadUrlResult {}
97
+ /**
98
+ * 基于 S3 协议对象存储(如腾讯云 COS)的文件直传工具。
99
+ *
100
+ * 提供两条链路:
101
+ * 1. `getUploadUrl`:只生成预签名上传地址,由客户端自行 PUT 上传;
102
+ * 2. `upload`:内部生成预签名地址后,直接上传 `Buffer` 内容。
103
+ *
104
+ * @example
105
+ * const fileStore = new XFileStore({
106
+ * accessKey: '...',
107
+ * secretKey: '...',
108
+ * endpoint: 'cos.ap-guangzhou.myqcloud.com',
109
+ * bucket: 'image-311111',
110
+ * dir: 'x',
111
+ * cdnUrl: 'cdn.example.com',
112
+ * });
113
+ *
114
+ * const { uploadUrl, publicUrl } = await fileStore.getUploadUrl({
115
+ * fileName: 'avatar.png',
116
+ * ttl: '10m',
117
+ * });
118
+ *
119
+ * @remarks
120
+ * - `endpoint`、`cdnUrl` 缺省协议时自动补全 `https://`,`dir` 去除首尾 `/`;
121
+ * - 随机文件名使用 24 位 CUID2,碰撞概率极低,适合作为对象 Key;
122
+ * - 无法识别的文件类型兜底为 `application/octet-stream`。
123
+ */
124
+ export declare class XFileStore {
125
+ private options;
126
+ private endpoint;
127
+ private dir;
128
+ private cdnUrl;
129
+ constructor(options: XFileStoreOptions);
130
+ /**
131
+ * 生成文件上传的预签名 URL。
132
+ *
133
+ * 处理流程:
134
+ * 1. 依据 `fileName` 推断 MIME 类型,推断失败时兜底为
135
+ * `application/octet-stream`;
136
+ * 2. 依据 `keepName` 决定沿用原始文件名或生成随机文件名;
137
+ * 3. 拼接 `dir` 前缀作为对象 Key,并生成带签名的 PUT 地址;
138
+ * 4. 依据 `cdnUrl` 与 Key 组装公开访问地址。
139
+ *
140
+ * @param payload 上传参数,见 {@link XFileStoreGetUploadUrlPayload}。
141
+ * @returns 内容类型、预签名上传地址与公开访问地址。
142
+ *
143
+ * @example
144
+ * const { uploadUrl, publicUrl } = await fileStore.getUploadUrl({
145
+ * fileName: 'avatar.png',
146
+ * ttl: '10m',
147
+ * });
148
+ */
149
+ getUploadUrl(payload: XFileStoreGetUploadUrlPayload): Promise<XFileStoreGetUploadUrlResult>;
150
+ /**
151
+ * 生成预签名 URL 并直接上传文件内容。
152
+ *
153
+ * 先复用 {@link XFileStore.getUploadUrl} 生成预签名地址(默认有效期
154
+ * 1 分钟),再以 PUT 方式将 `content` 上传到该地址,最后返回与
155
+ * `getUploadUrl` 一致的结果。
156
+ *
157
+ * @param payload 上传参数,见 {@link XFileStoreUploadPayload}。
158
+ * @returns 内容类型、预签名上传地址与公开访问地址。
159
+ *
160
+ * @example
161
+ * const result = await fileStore.upload({
162
+ * fileName: 'avatar.png',
163
+ * content: Buffer.from('...'),
164
+ * });
165
+ */
166
+ upload(payload: XFileStoreUploadPayload): Promise<XFileStoreUploadResult>;
167
+ }
@@ -0,0 +1,120 @@
1
+ import { XRandom } from "./random.mjs";
2
+ import { xr } from "@jayfong/x-request";
3
+ import { isUrl, ms } from "vtils";
4
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
5
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
6
+ import mime from "mime";
7
+ // src/file_store.ts
8
+ /**
9
+ * 基于 S3 协议对象存储(如腾讯云 COS)的文件直传工具。
10
+ *
11
+ * 提供两条链路:
12
+ * 1. `getUploadUrl`:只生成预签名上传地址,由客户端自行 PUT 上传;
13
+ * 2. `upload`:内部生成预签名地址后,直接上传 `Buffer` 内容。
14
+ *
15
+ * @example
16
+ * const fileStore = new XFileStore({
17
+ * accessKey: '...',
18
+ * secretKey: '...',
19
+ * endpoint: 'cos.ap-guangzhou.myqcloud.com',
20
+ * bucket: 'image-311111',
21
+ * dir: 'x',
22
+ * cdnUrl: 'cdn.example.com',
23
+ * });
24
+ *
25
+ * const { uploadUrl, publicUrl } = await fileStore.getUploadUrl({
26
+ * fileName: 'avatar.png',
27
+ * ttl: '10m',
28
+ * });
29
+ *
30
+ * @remarks
31
+ * - `endpoint`、`cdnUrl` 缺省协议时自动补全 `https://`,`dir` 去除首尾 `/`;
32
+ * - 随机文件名使用 24 位 CUID2,碰撞概率极低,适合作为对象 Key;
33
+ * - 无法识别的文件类型兜底为 `application/octet-stream`。
34
+ */
35
+ var XFileStore = class {
36
+ options;
37
+ endpoint = "";
38
+ dir = "";
39
+ cdnUrl = "";
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.endpoint = isUrl(this.options.endpoint) ? this.options.endpoint : `https://${this.options.endpoint}`;
43
+ this.dir = this.options.dir ? `${this.options.dir.replace(/^\/+/, "").replace(/\/+$/, "")}` : "";
44
+ this.cdnUrl = (isUrl(this.options.cdnUrl) ? this.options.cdnUrl : `https://${this.options.cdnUrl}`).replace(/\/+$/, "");
45
+ }
46
+ /**
47
+ * 生成文件上传的预签名 URL。
48
+ *
49
+ * 处理流程:
50
+ * 1. 依据 `fileName` 推断 MIME 类型,推断失败时兜底为
51
+ * `application/octet-stream`;
52
+ * 2. 依据 `keepName` 决定沿用原始文件名或生成随机文件名;
53
+ * 3. 拼接 `dir` 前缀作为对象 Key,并生成带签名的 PUT 地址;
54
+ * 4. 依据 `cdnUrl` 与 Key 组装公开访问地址。
55
+ *
56
+ * @param payload 上传参数,见 {@link XFileStoreGetUploadUrlPayload}。
57
+ * @returns 内容类型、预签名上传地址与公开访问地址。
58
+ *
59
+ * @example
60
+ * const { uploadUrl, publicUrl } = await fileStore.getUploadUrl({
61
+ * fileName: 'avatar.png',
62
+ * ttl: '10m',
63
+ * });
64
+ */
65
+ async getUploadUrl(payload) {
66
+ const s3Client = new S3Client({
67
+ credentials: {
68
+ accessKeyId: this.options.accessKey,
69
+ secretAccessKey: this.options.secretKey
70
+ },
71
+ endpoint: this.endpoint,
72
+ region: "x"
73
+ });
74
+ const contentType = mime.getType(payload.fileName) || "application/octet-stream";
75
+ const extension = mime.getExtension(contentType) || "bin";
76
+ const fileName = payload.keepName ? payload.fileName : `${XRandom.cuid2()}.${extension}`;
77
+ const dir = this.dir ? `${this.dir}/` : "";
78
+ const command = new PutObjectCommand({
79
+ Bucket: this.options.bucket,
80
+ Key: `${dir}${fileName}`,
81
+ ContentType: contentType
82
+ });
83
+ return {
84
+ contentType,
85
+ uploadUrl: await getSignedUrl(s3Client, command, { expiresIn: ms(payload.ttl, true) }),
86
+ publicUrl: `${this.cdnUrl}/${dir}${fileName}`
87
+ };
88
+ }
89
+ /**
90
+ * 生成预签名 URL 并直接上传文件内容。
91
+ *
92
+ * 先复用 {@link XFileStore.getUploadUrl} 生成预签名地址(默认有效期
93
+ * 1 分钟),再以 PUT 方式将 `content` 上传到该地址,最后返回与
94
+ * `getUploadUrl` 一致的结果。
95
+ *
96
+ * @param payload 上传参数,见 {@link XFileStoreUploadPayload}。
97
+ * @returns 内容类型、预签名上传地址与公开访问地址。
98
+ *
99
+ * @example
100
+ * const result = await fileStore.upload({
101
+ * fileName: 'avatar.png',
102
+ * content: Buffer.from('...'),
103
+ * });
104
+ */
105
+ async upload(payload) {
106
+ const res = await this.getUploadUrl({
107
+ fileName: payload.fileName,
108
+ ttl: ms("1m"),
109
+ keepName: payload.keepName
110
+ });
111
+ await xr.post({
112
+ url: res.uploadUrl,
113
+ method: "PUT",
114
+ headers: { "Content-Type": res.contentType },
115
+ data: payload.content
116
+ });
117
+ return res;
118
+ }
119
+ };
120
+ export { XFileStore };
package/lib/fs.d.mts ADDED
@@ -0,0 +1,111 @@
1
+ import fs from "fs-extra";
2
+ import { globby, globbyStream, globbySync } from "globby";
3
+ /**
4
+ * 额外的文件系统辅助方法:
5
+ * - `getMimeType`:将扩展名 / 文件名 / MIME 字符串统一转换为 MIME 类型;
6
+ * - `getExtension`:将 MIME 字符串 / 文件名统一转换为扩展名;
7
+ * - `globby` / `globbySync` / `globbyStream`:按 glob 模式查找文件与目录。
8
+ *
9
+ * 这些方法会被合并到 `XFs` 中,与 `fs-extra` 的 API 一起导出。
10
+ */
11
+ declare const XFsExtra: {
12
+ /**
13
+ * 获取 MIME 类型。
14
+ *
15
+ * 入参支持以下形式:
16
+ * - 扩展名:`json`、`png`
17
+ * - 文件名:`file.txt`、`archive.tar.gz`
18
+ * - MIME 字符串:`application/json`
19
+ *
20
+ * 处理规则:
21
+ * 1. 去除首尾空白后,如果为空,返回通用兜底值 `application/octet-stream`;
22
+ * 2. 如果已是 MIME 形式(包含 `/`),直接返回原值;
23
+ * 3. 否则交给 `mime.getType` 推断;
24
+ * 4. 推断失败时,返回 `application/octet-stream`。
25
+ */
26
+ getMimeType(extOrName: string): string;
27
+ /**
28
+ * 获取扩展名(不带 `.`)。
29
+ *
30
+ * 入参支持以下形式:
31
+ * - MIME 字符串:`image/png`、`text/plain`
32
+ * - 文件名:`file.txt`、`archive.tar.gz`
33
+ * - 扩展名:`json`
34
+ *
35
+ * 处理规则:
36
+ * 1. 去除首尾空白后,如果为空,返回空字符串;
37
+ * 2. 如果是 MIME 形式(包含 `/`),优先通过 `mime.getExtension` 推断,
38
+ * 若推断失败则退化为取 `/` 右侧片段;
39
+ * 3. 如果是文件名(包含 `.`),返回最后一段后缀;
40
+ * 4. 否则按扩展名/MIME 简写尝试 `mime.getExtension`,失败返回空字符串。
41
+ */
42
+ getExtension(typeOrName: string): string;
43
+ /**
44
+ * 按 glob 模式异步查找文件与目录。
45
+ *
46
+ * 转发自 `globby` 的异步版本,返回 `Promise`,适合 `await` 使用。
47
+ * 模式只支持正斜杠 `/`;拼接路径片段时请用 `path.posix.join`,
48
+ * Windows 上含反斜杠的模式会静默匹配失败。支持以 `!` 开头的取反
49
+ * 模式排除文件;当只传入取反模式(如 `['!*.json']`)时,会自动
50
+ * 前置一个匹配全部文件的兜底模式,再应用取反(可通过
51
+ * `options.expandNegationOnlyPatterns` 关闭)。
52
+ *
53
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
54
+ * @param options 可选配置;除 `globby` 自身选项(如 `gitignore`、
55
+ * `cwd`)外,其余选项透传给 `fast-glob`,默认在
56
+ * `process.cwd()` 下查找。
57
+ * @returns 匹配到的路径数组;传入 `objectMode: true` 或
58
+ * `stats: true` 时返回 `GlobEntry` 对象数组。
59
+ *
60
+ * @example
61
+ * await XFs.globby(['*.ts', '!*.test.ts']);
62
+ * // ['index.ts', 'utils.ts']
63
+ */
64
+ globby: typeof globby;
65
+ /**
66
+ * 按 glob 模式同步查找文件与目录。
67
+ *
68
+ * 转发自 `globby` 的同步版本,阻塞执行并直接返回结果,行为与
69
+ * `globby` 一致:模式只支持正斜杠 `/`,支持 `!` 取反排除,只传入
70
+ * 取反模式时自动前置匹配全部文件的兜底模式。
71
+ *
72
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
73
+ * @param options 可选配置;除 `globby` 自身选项(如 `gitignore`、
74
+ * `cwd`)外,其余选项透传给 `fast-glob`,默认在
75
+ * `process.cwd()` 下查找。
76
+ * @returns 匹配到的路径数组;传入 `objectMode: true` 或
77
+ * `stats: true` 时返回 `GlobEntry` 对象数组。
78
+ *
79
+ * @example
80
+ * XFs.globbySync(['*.ts', '!*.test.ts']);
81
+ * // ['index.ts', 'utils.ts']
82
+ */
83
+ globbySync: typeof globbySync;
84
+ /**
85
+ * 按 glob 模式流式查找文件与目录。
86
+ *
87
+ * 转发自 `globby` 的流式版本,返回可异步迭代的 `Readable` 流,
88
+ * 适合边匹配边处理,避免一次性在内存中持有全部结果。模式与选项
89
+ * 行为同 `globby`:只支持正斜杠 `/`,支持 `!` 取反排除。
90
+ *
91
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
92
+ * @param options 可选配置;除 `globby` 自身选项外,其余选项
93
+ * 透传给 `fast-glob`,默认在 `process.cwd()` 下查找。
94
+ * @returns 逐个产出匹配路径的可读流;传入 `objectMode: true` 或
95
+ * `stats: true` 时产出 `GlobEntry` 对象。
96
+ *
97
+ * @example
98
+ * for await (const path of XFs.globbyStream('*.ts')) {
99
+ * console.log(path);
100
+ * }
101
+ */
102
+ globbyStream: typeof globbyStream;
103
+ };
104
+ /**
105
+ * `XFs` = `fs-extra` + 轻量文件类型工具。
106
+ *
107
+ * 这样调用方既可以直接使用完整的文件系统 API(如 `readFile`、`writeFile`),
108
+ * 也可以在同一对象上使用 `getMimeType` 和 `getExtension`。
109
+ */
110
+ export declare const XFs: typeof fs & typeof XFsExtra;
111
+ export {};
package/lib/fs.mjs ADDED
@@ -0,0 +1,127 @@
1
+ import fs from "fs-extra";
2
+ import mime from "mime";
3
+ import { globby, globbyStream, globbySync } from "globby";
4
+ // src/fs.ts
5
+ /**
6
+ * 额外的文件系统辅助方法:
7
+ * - `getMimeType`:将扩展名 / 文件名 / MIME 字符串统一转换为 MIME 类型;
8
+ * - `getExtension`:将 MIME 字符串 / 文件名统一转换为扩展名;
9
+ * - `globby` / `globbySync` / `globbyStream`:按 glob 模式查找文件与目录。
10
+ *
11
+ * 这些方法会被合并到 `XFs` 中,与 `fs-extra` 的 API 一起导出。
12
+ */
13
+ const XFsExtra = {
14
+ /**
15
+ * 获取 MIME 类型。
16
+ *
17
+ * 入参支持以下形式:
18
+ * - 扩展名:`json`、`png`
19
+ * - 文件名:`file.txt`、`archive.tar.gz`
20
+ * - MIME 字符串:`application/json`
21
+ *
22
+ * 处理规则:
23
+ * 1. 去除首尾空白后,如果为空,返回通用兜底值 `application/octet-stream`;
24
+ * 2. 如果已是 MIME 形式(包含 `/`),直接返回原值;
25
+ * 3. 否则交给 `mime.getType` 推断;
26
+ * 4. 推断失败时,返回 `application/octet-stream`。
27
+ */
28
+ getMimeType(extOrName) {
29
+ const value = extOrName.trim();
30
+ if (!value) return "application/octet-stream";
31
+ if (value.includes("/")) return value;
32
+ return mime.getType(value) || "application/octet-stream";
33
+ },
34
+ /**
35
+ * 获取扩展名(不带 `.`)。
36
+ *
37
+ * 入参支持以下形式:
38
+ * - MIME 字符串:`image/png`、`text/plain`
39
+ * - 文件名:`file.txt`、`archive.tar.gz`
40
+ * - 扩展名:`json`
41
+ *
42
+ * 处理规则:
43
+ * 1. 去除首尾空白后,如果为空,返回空字符串;
44
+ * 2. 如果是 MIME 形式(包含 `/`),优先通过 `mime.getExtension` 推断,
45
+ * 若推断失败则退化为取 `/` 右侧片段;
46
+ * 3. 如果是文件名(包含 `.`),返回最后一段后缀;
47
+ * 4. 否则按扩展名/MIME 简写尝试 `mime.getExtension`,失败返回空字符串。
48
+ */
49
+ getExtension(typeOrName) {
50
+ const value = typeOrName.trim();
51
+ if (!value) return "";
52
+ if (value.includes("/")) return mime.getExtension(value) || value.split("/").pop() || "";
53
+ if (value.includes(".")) return value.split(".").pop();
54
+ return mime.getExtension(value) || "";
55
+ },
56
+ /**
57
+ * 按 glob 模式异步查找文件与目录。
58
+ *
59
+ * 转发自 `globby` 的异步版本,返回 `Promise`,适合 `await` 使用。
60
+ * 模式只支持正斜杠 `/`;拼接路径片段时请用 `path.posix.join`,
61
+ * Windows 上含反斜杠的模式会静默匹配失败。支持以 `!` 开头的取反
62
+ * 模式排除文件;当只传入取反模式(如 `['!*.json']`)时,会自动
63
+ * 前置一个匹配全部文件的兜底模式,再应用取反(可通过
64
+ * `options.expandNegationOnlyPatterns` 关闭)。
65
+ *
66
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
67
+ * @param options 可选配置;除 `globby` 自身选项(如 `gitignore`、
68
+ * `cwd`)外,其余选项透传给 `fast-glob`,默认在
69
+ * `process.cwd()` 下查找。
70
+ * @returns 匹配到的路径数组;传入 `objectMode: true` 或
71
+ * `stats: true` 时返回 `GlobEntry` 对象数组。
72
+ *
73
+ * @example
74
+ * await XFs.globby(['*.ts', '!*.test.ts']);
75
+ * // ['index.ts', 'utils.ts']
76
+ */
77
+ globby,
78
+ /**
79
+ * 按 glob 模式同步查找文件与目录。
80
+ *
81
+ * 转发自 `globby` 的同步版本,阻塞执行并直接返回结果,行为与
82
+ * `globby` 一致:模式只支持正斜杠 `/`,支持 `!` 取反排除,只传入
83
+ * 取反模式时自动前置匹配全部文件的兜底模式。
84
+ *
85
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
86
+ * @param options 可选配置;除 `globby` 自身选项(如 `gitignore`、
87
+ * `cwd`)外,其余选项透传给 `fast-glob`,默认在
88
+ * `process.cwd()` 下查找。
89
+ * @returns 匹配到的路径数组;传入 `objectMode: true` 或
90
+ * `stats: true` 时返回 `GlobEntry` 对象数组。
91
+ *
92
+ * @example
93
+ * XFs.globbySync(['*.ts', '!*.test.ts']);
94
+ * // ['index.ts', 'utils.ts']
95
+ */
96
+ globbySync,
97
+ /**
98
+ * 按 glob 模式流式查找文件与目录。
99
+ *
100
+ * 转发自 `globby` 的流式版本,返回可异步迭代的 `Readable` 流,
101
+ * 适合边匹配边处理,避免一次性在内存中持有全部结果。模式与选项
102
+ * 行为同 `globby`:只支持正斜杠 `/`,支持 `!` 取反排除。
103
+ *
104
+ * @param patterns 一个 glob 模式或模式数组,支持取反排除。
105
+ * @param options 可选配置;除 `globby` 自身选项外,其余选项
106
+ * 透传给 `fast-glob`,默认在 `process.cwd()` 下查找。
107
+ * @returns 逐个产出匹配路径的可读流;传入 `objectMode: true` 或
108
+ * `stats: true` 时产出 `GlobEntry` 对象。
109
+ *
110
+ * @example
111
+ * for await (const path of XFs.globbyStream('*.ts')) {
112
+ * console.log(path);
113
+ * }
114
+ */
115
+ globbyStream
116
+ };
117
+ /**
118
+ * `XFs` = `fs-extra` + 轻量文件类型工具。
119
+ *
120
+ * 这样调用方既可以直接使用完整的文件系统 API(如 `readFile`、`writeFile`),
121
+ * 也可以在同一对象上使用 `getMimeType` 和 `getExtension`。
122
+ */
123
+ const XFs = {
124
+ ...fs,
125
+ ...XFsExtra
126
+ };
127
+ export { XFs };