@h-ai/storage 0.1.0-alpha5

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.
@@ -0,0 +1,506 @@
1
+ export { ClientDownloadOptions, ClientUploadOptions, UploadProgress, downloadAndSave, downloadWithPresignedUrl, formatFileSize, getFileExtension, getMimeType, uploadWithPresignedUrl } from './client/index.js';
2
+ import { z } from 'zod';
3
+ import * as _h_ai_core from '@h-ai/core';
4
+ import { HaiResult } from '@h-ai/core';
5
+ import { Buffer } from 'node:buffer';
6
+
7
+ /**
8
+ * @h-ai/storage — 错误码 + 配置 Schema
9
+ * @module storage-config
10
+ */
11
+
12
+ /**
13
+ * 存储类型枚举
14
+ *
15
+ * 支持的存储类型:
16
+ * - `s3` - S3 协议对象存储(兼容 AWS S3、MinIO、阿里云 OSS 等)
17
+ * - `local` - 本地文件存储(仅后端)
18
+ */
19
+ declare const StorageTypeSchema: z.ZodEnum<{
20
+ s3: "s3";
21
+ local: "local";
22
+ }>;
23
+ /** 存储类型 */
24
+ type StorageType = z.infer<typeof StorageTypeSchema>;
25
+ /**
26
+ * S3 存储配置 Schema
27
+ *
28
+ * 支持标准 AWS S3 以及兼容 S3 协议的存储服务(MinIO、阿里云 OSS 等)。
29
+ * 必填字段:bucket、region、accessKeyId、secretAccessKey。
30
+ */
31
+ declare const S3ConfigSchema: z.ZodObject<{
32
+ type: z.ZodLiteral<"s3">;
33
+ bucket: z.ZodString;
34
+ region: z.ZodString;
35
+ accessKeyId: z.ZodString;
36
+ secretAccessKey: z.ZodString;
37
+ endpoint: z.ZodOptional<z.ZodString>;
38
+ forcePathStyle: z.ZodDefault<z.ZodBoolean>;
39
+ prefix: z.ZodDefault<z.ZodString>;
40
+ publicUrl: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strip>;
42
+ /** S3 配置类型(parse 后,所有字段已填充默认值) */
43
+ type S3Config = z.infer<typeof S3ConfigSchema>;
44
+ /**
45
+ * 本地存储配置 Schema
46
+ *
47
+ * 基于本地文件系统,仅后端可用。必填字段:root。
48
+ */
49
+ declare const LocalConfigSchema: z.ZodObject<{
50
+ type: z.ZodLiteral<"local">;
51
+ root: z.ZodString;
52
+ directoryMode: z.ZodDefault<z.ZodNumber>;
53
+ fileMode: z.ZodDefault<z.ZodNumber>;
54
+ }, z.core.$strip>;
55
+ /** 本地配置类型(parse 后,所有字段已填充默认值) */
56
+ type LocalConfig = z.infer<typeof LocalConfigSchema>;
57
+ /**
58
+ * 统一存储配置 Schema
59
+ *
60
+ * 支持 S3 和本地文件两种类型的配置验证。
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * // S3 配置
65
+ * const s3Config = StorageConfigSchema.parse({
66
+ * type: 's3',
67
+ * bucket: 'my-bucket',
68
+ * region: 'us-east-1',
69
+ * accessKeyId: 'AKIAXXXXXXXX',
70
+ * secretAccessKey: 'xxxxx'
71
+ * })
72
+ *
73
+ * // MinIO 配置
74
+ * const minioConfig = StorageConfigSchema.parse({
75
+ * type: 's3',
76
+ * bucket: 'my-bucket',
77
+ * region: 'us-east-1',
78
+ * endpoint: 'http://localhost:9000',
79
+ * accessKeyId: 'minioadmin',
80
+ * secretAccessKey: 'minioadmin',
81
+ * forcePathStyle: true
82
+ * })
83
+ *
84
+ * // 本地存储配置
85
+ * const localConfig = StorageConfigSchema.parse({
86
+ * type: 'local',
87
+ * root: '/data/uploads'
88
+ * })
89
+ * ```
90
+ */
91
+ declare const StorageConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
92
+ type: z.ZodLiteral<"s3">;
93
+ bucket: z.ZodString;
94
+ region: z.ZodString;
95
+ accessKeyId: z.ZodString;
96
+ secretAccessKey: z.ZodString;
97
+ endpoint: z.ZodOptional<z.ZodString>;
98
+ forcePathStyle: z.ZodDefault<z.ZodBoolean>;
99
+ prefix: z.ZodDefault<z.ZodString>;
100
+ publicUrl: z.ZodOptional<z.ZodString>;
101
+ }, z.core.$strip>, z.ZodObject<{
102
+ type: z.ZodLiteral<"local">;
103
+ root: z.ZodString;
104
+ directoryMode: z.ZodDefault<z.ZodNumber>;
105
+ fileMode: z.ZodDefault<z.ZodNumber>;
106
+ }, z.core.$strip>], "type">;
107
+ /** 存储配置类型(parse 后的完整配置) */
108
+ type StorageConfig = z.infer<typeof StorageConfigSchema>;
109
+ /** 存储配置输入类型(parse 前,允许省略带默认值的字段) */
110
+ type StorageConfigInput = z.input<typeof StorageConfigSchema>;
111
+ /**
112
+ * 签名 URL 配置 Schema
113
+ *
114
+ * 用于控制 `storage.presign.getUrl()` 生成的下载签名 URL 参数。
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const url = await storage.presign.getUrl('report.pdf', {
119
+ * expiresIn: 600, // 10 分钟过期
120
+ * responseContentDisposition: 'attachment; filename="report.pdf"',
121
+ * })
122
+ * ```
123
+ */
124
+ declare const PresignOptionsSchema: z.ZodObject<{
125
+ expiresIn: z.ZodDefault<z.ZodNumber>;
126
+ responseContentType: z.ZodOptional<z.ZodString>;
127
+ responseContentDisposition: z.ZodOptional<z.ZodString>;
128
+ }, z.core.$strip>;
129
+ /** 签名 URL 配置类型(parse 后) */
130
+ type PresignOptions = z.infer<typeof PresignOptionsSchema>;
131
+ /**
132
+ * 上传签名 URL 配置 Schema
133
+ *
134
+ * 继承 PresignOptionsSchema 并扩展上传专属参数,用于 `storage.presign.putUrl()`。
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * const url = await storage.presign.putUrl('uploads/avatar.png', {
139
+ * contentType: 'image/png',
140
+ * maxSize: 5 * 1024 * 1024, // 5MB 限制
141
+ * expiresIn: 300,
142
+ * })
143
+ * ```
144
+ */
145
+ declare const PresignUploadOptionsSchema: z.ZodObject<{
146
+ expiresIn: z.ZodDefault<z.ZodNumber>;
147
+ responseContentType: z.ZodOptional<z.ZodString>;
148
+ responseContentDisposition: z.ZodOptional<z.ZodString>;
149
+ contentType: z.ZodDefault<z.ZodString>;
150
+ maxSize: z.ZodOptional<z.ZodNumber>;
151
+ }, z.core.$strip>;
152
+ /** 上传签名 URL 配置类型(parse 后) */
153
+ type PresignUploadOptions = z.infer<typeof PresignUploadOptionsSchema>;
154
+
155
+ declare const HaiStorageError: {
156
+ readonly CONNECTION_FAILED: _h_ai_core.HaiErrorDef;
157
+ readonly OPERATION_FAILED: _h_ai_core.HaiErrorDef;
158
+ readonly NOT_FOUND: _h_ai_core.HaiErrorDef;
159
+ readonly ALREADY_EXISTS: _h_ai_core.HaiErrorDef;
160
+ readonly PERMISSION_DENIED: _h_ai_core.HaiErrorDef;
161
+ readonly QUOTA_EXCEEDED: _h_ai_core.HaiErrorDef;
162
+ readonly INVALID_PATH: _h_ai_core.HaiErrorDef;
163
+ readonly IO_ERROR: _h_ai_core.HaiErrorDef;
164
+ readonly NETWORK_ERROR: _h_ai_core.HaiErrorDef;
165
+ readonly NOT_INITIALIZED: _h_ai_core.HaiErrorDef;
166
+ readonly UNSUPPORTED_TYPE: _h_ai_core.HaiErrorDef;
167
+ readonly CONFIG_ERROR: _h_ai_core.HaiErrorDef;
168
+ readonly PRESIGN_FAILED: _h_ai_core.HaiErrorDef;
169
+ readonly UPLOAD_FAILED: _h_ai_core.HaiErrorDef;
170
+ readonly DOWNLOAD_FAILED: _h_ai_core.HaiErrorDef;
171
+ };
172
+ /**
173
+ * 文件元数据接口
174
+ *
175
+ * 表示存储中单个文件的元信息,由 put/head/copy/list 等操作返回。
176
+ */
177
+ interface FileMetadata {
178
+ /** 文件键/路径(不含配置 prefix,对用户透明) */
179
+ key: string;
180
+ /** 文件大小(字节) */
181
+ size: number;
182
+ /** 内容类型(MIME),未指定时根据扩展名自动推断 */
183
+ contentType: string;
184
+ /** 最后修改时间 */
185
+ lastModified: Date;
186
+ /** ETag(用于缓存验证,本地存储为 MD5,S3 由服务端生成) */
187
+ etag?: string;
188
+ /** 自定义元数据键值对 */
189
+ metadata?: Record<string, string>;
190
+ }
191
+ /**
192
+ * 列表选项
193
+ *
194
+ * 用于控制 `storage.dir.list()` 的过滤、分页和目录模拟行为。
195
+ */
196
+ interface ListOptions {
197
+ /** 前缀过滤(只返回以此前缀开头的文件) */
198
+ prefix?: string;
199
+ /**
200
+ * 分页标记(从上次 ListResult.nextContinuationToken 获取)
201
+ *
202
+ * 注意:本地存储目前不支持此字段,仅 S3 Provider 有效。
203
+ */
204
+ continuationToken?: string;
205
+ /** 每页最大数量(默认 1000) */
206
+ maxKeys?: number;
207
+ /** 分隔符(用于模拟目录结构,通常为 '/';传入后子目录作为 commonPrefixes 返回) */
208
+ delimiter?: string;
209
+ }
210
+ /**
211
+ * 列表结果
212
+ *
213
+ * 包含当前页的文件列表、目录前缀、分页信息。
214
+ */
215
+ interface ListResult {
216
+ /** 当前页文件元数据列表 */
217
+ files: FileMetadata[];
218
+ /** 公共前缀(使用 delimiter 时由子目录路径组成) */
219
+ commonPrefixes: string[];
220
+ /** 下一页标记;无后续页时为 undefined */
221
+ nextContinuationToken?: string;
222
+ /** 是否被截断(true 表示还有更多数据,可用 nextContinuationToken 继续拉取) */
223
+ isTruncated: boolean;
224
+ }
225
+ /**
226
+ * 上传选项
227
+ *
228
+ * 用于控制 `storage.file.put()` 的元数据与缓存行为。
229
+ */
230
+ interface UploadOptions {
231
+ /** 内容类型(不指定时根据文件扩展名自动推断) */
232
+ contentType?: string;
233
+ /** 自定义元数据键值对(存储后可通过 head 获取) */
234
+ metadata?: Record<string, string>;
235
+ /** Cache-Control 响应头(例如 'max-age=3600') */
236
+ cacheControl?: string;
237
+ /** Content-Disposition 响应头(例如 'attachment; filename="a.txt"') */
238
+ contentDisposition?: string;
239
+ }
240
+ /**
241
+ * 下载选项
242
+ *
243
+ * 用于控制 `storage.file.get()` 的范围读取。
244
+ * 两个字段均为字节偏移(含),与 HTTP Range 语义一致。
245
+ */
246
+ interface DownloadOptions {
247
+ /** 范围起始位置(字节,含);不指定时从文件开头读取 */
248
+ rangeStart?: number;
249
+ /** 范围结束位置(字节,含);不指定时读取到文件末尾 */
250
+ rangeEnd?: number;
251
+ }
252
+ /**
253
+ * 复制选项
254
+ *
255
+ * 用于控制 `storage.file.copy()` 的目标文件元数据。
256
+ * 提供任意字段时,S3 Provider 会使用 MetadataDirective='REPLACE' 覆盖源文件元数据。
257
+ */
258
+ interface CopyOptions {
259
+ /** 目标文件的内容类型(不指定时继承源文件) */
260
+ contentType?: string;
261
+ /** 目标文件的自定义元数据(不指定时继承源文件) */
262
+ metadata?: Record<string, string>;
263
+ }
264
+ /**
265
+ * 文件操作接口
266
+ *
267
+ * 提供文件的读写操作。
268
+ */
269
+ interface FileOperations {
270
+ /**
271
+ * 上传文件
272
+ *
273
+ * @param key - 文件键/路径
274
+ * @param data - 文件内容(Buffer、Uint8Array 或字符串)
275
+ * @param options - 上传选项
276
+ * @returns 上传结果包含文件元数据
277
+ * @example
278
+ * ```ts
279
+ * const result = await storage.file.put('uploads/a.txt', 'hello')
280
+ * if (result.success) {
281
+ * result.data.key
282
+ * }
283
+ * ```
284
+ */
285
+ put: (key: string, data: Buffer | Uint8Array | string, options?: UploadOptions) => Promise<HaiResult<FileMetadata>>;
286
+ /**
287
+ * 下载文件
288
+ *
289
+ * @param key - 文件键/路径
290
+ * @param options - 下载选项
291
+ * @returns 文件内容
292
+ * @example
293
+ * ```ts
294
+ * const result = await storage.file.get('uploads/a.txt')
295
+ * if (result.success) {
296
+ * const buffer = result.data
297
+ * }
298
+ * ```
299
+ */
300
+ get: (key: string, options?: DownloadOptions) => Promise<HaiResult<Buffer>>;
301
+ /**
302
+ * 获取文件元数据
303
+ *
304
+ * @param key - 文件键/路径
305
+ * @returns 文件元数据
306
+ * @example
307
+ * ```ts
308
+ * const result = await storage.file.head('uploads/a.txt')
309
+ * if (result.success) {
310
+ * result.data.size
311
+ * }
312
+ * ```
313
+ */
314
+ head: (key: string) => Promise<HaiResult<FileMetadata>>;
315
+ /**
316
+ * 检查文件是否存在
317
+ *
318
+ * @param key - 文件键/路径
319
+ * @returns 是否存在
320
+ * @example
321
+ * ```ts
322
+ * const result = await storage.file.exists('uploads/a.txt')
323
+ * if (result.success) {
324
+ * result.data
325
+ * }
326
+ * ```
327
+ */
328
+ exists: (key: string) => Promise<HaiResult<boolean>>;
329
+ /**
330
+ * 删除文件
331
+ *
332
+ * @param key - 文件键/路径
333
+ * @returns 删除结果
334
+ * @example
335
+ * ```ts
336
+ * await storage.file.delete('uploads/a.txt')
337
+ * ```
338
+ */
339
+ delete: (key: string) => Promise<HaiResult<void>>;
340
+ /**
341
+ * 批量删除文件
342
+ *
343
+ * @param keys - 文件键/路径列表
344
+ * @returns 删除结果
345
+ * @example
346
+ * ```ts
347
+ * await storage.file.deleteMany(['a.txt', 'b.txt'])
348
+ * ```
349
+ */
350
+ deleteMany: (keys: string[]) => Promise<HaiResult<void>>;
351
+ /**
352
+ * 复制文件
353
+ *
354
+ * @param sourceKey - 源文件键
355
+ * @param destKey - 目标文件键
356
+ * @param options - 复制选项
357
+ * @returns 目标文件元数据
358
+ * @example
359
+ * ```ts
360
+ * await storage.file.copy('a.txt', 'b.txt')
361
+ * ```
362
+ */
363
+ copy: (sourceKey: string, destKey: string, options?: CopyOptions) => Promise<HaiResult<FileMetadata>>;
364
+ }
365
+ /**
366
+ * 目录操作接口
367
+ *
368
+ * 提供目录(前缀)的列表操作。
369
+ */
370
+ interface DirOperations {
371
+ /**
372
+ * 列出目录内容
373
+ *
374
+ * @param options - 列表选项
375
+ * @returns 文件和目录列表
376
+ * @example
377
+ * ```ts
378
+ * const result = await storage.dir.list({ prefix: 'uploads/' })
379
+ * if (result.success) {
380
+ * result.data.files
381
+ * }
382
+ * ```
383
+ */
384
+ list: (options?: ListOptions) => Promise<HaiResult<ListResult>>;
385
+ /**
386
+ * 删除目录(删除指定前缀下的所有文件)
387
+ *
388
+ * @param prefix - 目录前缀
389
+ * @returns 删除结果
390
+ * @example
391
+ * ```ts
392
+ * await storage.dir.delete('uploads/tmp/')
393
+ * ```
394
+ */
395
+ delete: (prefix: string) => Promise<HaiResult<void>>;
396
+ }
397
+ /**
398
+ * 签名 URL 操作接口
399
+ *
400
+ * 提供生成临时授权 URL 的功能,支持前端直接上传下载。
401
+ */
402
+ interface PresignOperations {
403
+ /**
404
+ * 生成下载签名 URL
405
+ *
406
+ * @param key - 文件键/路径
407
+ * @param options - 签名选项
408
+ * @returns 签名 URL
409
+ * @example
410
+ * ```ts
411
+ * const result = await storage.presign.getUrl('a.txt', { expiresIn: 60 })
412
+ * ```
413
+ */
414
+ getUrl: (key: string, options?: PresignOptions) => Promise<HaiResult<string>>;
415
+ /**
416
+ * 生成上传签名 URL
417
+ *
418
+ * @param key - 文件键/路径
419
+ * @param options - 上传签名选项
420
+ * @returns 签名 URL
421
+ * @example
422
+ * ```ts
423
+ * const result = await storage.presign.putUrl('a.txt', { contentType: 'text/plain' })
424
+ * ```
425
+ */
426
+ putUrl: (key: string, options?: PresignUploadOptions) => Promise<HaiResult<string>>;
427
+ /**
428
+ * 获取公开访问 URL(如果配置了 publicUrl)
429
+ *
430
+ * @param key - 文件键/路径
431
+ * @returns 公开 URL,如果未配置 publicUrl 则返回 null
432
+ * @example
433
+ * ```ts
434
+ * const url = storage.presign.publicUrl('a.txt')
435
+ * ```
436
+ */
437
+ publicUrl: (key: string) => string | null;
438
+ }
439
+ /**
440
+ * 存储函数接口
441
+ *
442
+ * 统一的存储访问入口:
443
+ * - `storage.init()` — 初始化存储连接
444
+ * - `storage.close()` — 关闭连接并释放资源
445
+ * - `storage.file` — 文件操作(上传/下载/删除/复制等)
446
+ * - `storage.dir` — 目录操作(列表/删除)
447
+ * - `storage.presign` — 签名 URL 操作
448
+ * - `storage.config` — 当前连接配置(未初始化时为 null)
449
+ * - `storage.isInitialized` — 初始化状态
450
+ */
451
+ interface StorageFunctions {
452
+ /**
453
+ * 初始化存储连接
454
+ *
455
+ * 已有连接时会先 close 再重新初始化。
456
+ *
457
+ * @param config - 存储配置(支持省略带默认值的字段)
458
+ * @returns 成功 ok(undefined);失败返回 err(含 StorageError)
459
+ */
460
+ init: (config: StorageConfigInput) => Promise<HaiResult<void>>;
461
+ /**
462
+ * 关闭存储连接并释放资源
463
+ *
464
+ * 重复调用不会报错。
465
+ */
466
+ close: () => Promise<void>;
467
+ /** 当前解析后的存储配置;未初始化或已关闭时为 null */
468
+ readonly config: StorageConfig | null;
469
+ /** 是否已完成初始化 */
470
+ readonly isInitialized: boolean;
471
+ /** 文件操作接口(put / get / head / exists / delete / deleteMany / copy) */
472
+ readonly file: FileOperations;
473
+ /** 目录操作接口(list / delete) */
474
+ readonly dir: DirOperations;
475
+ /** 签名 URL 操作接口(getUrl / putUrl / publicUrl) */
476
+ readonly presign: PresignOperations;
477
+ }
478
+ /**
479
+ * 存储 Provider 接口
480
+ *
481
+ * 由各具体存储实现(S3、Local)实现此接口。
482
+ * Provider 仅在 storage-main 内部使用,不直接对外暴露实例。
483
+ */
484
+ interface StorageProvider {
485
+ /** Provider 名称标识(如 's3'、'local') */
486
+ readonly name: string;
487
+ /**
488
+ * 建立与存储服务的连接
489
+ *
490
+ * @param config - 经 Schema 校验后的完整配置
491
+ * @returns 连接结果
492
+ */
493
+ connect: (config: StorageConfig) => Promise<HaiResult<void>>;
494
+ /** 关闭连接并释放资源 */
495
+ close: () => Promise<void>;
496
+ /** 检查是否已连接 */
497
+ isConnected: () => boolean;
498
+ /** 文件操作接口 */
499
+ readonly file: FileOperations;
500
+ /** 目录操作接口 */
501
+ readonly dir: DirOperations;
502
+ /** 签名 URL 操作接口 */
503
+ readonly presign: PresignOperations;
504
+ }
505
+
506
+ export { type CopyOptions, type DirOperations, type DownloadOptions, type FileMetadata, type FileOperations, HaiStorageError, type ListOptions, type ListResult, type LocalConfig, LocalConfigSchema, type PresignOperations, type PresignOptions, PresignOptionsSchema, type PresignUploadOptions, PresignUploadOptionsSchema, type S3Config, S3ConfigSchema, type StorageConfig, type StorageConfigInput, StorageConfigSchema, type StorageFunctions, type StorageProvider, type StorageType, StorageTypeSchema, type UploadOptions };
@@ -0,0 +1,4 @@
1
+ export { HaiStorageError, LocalConfigSchema, PresignOptionsSchema, PresignUploadOptionsSchema, S3ConfigSchema, StorageConfigSchema, StorageTypeSchema } from './chunk-KHZOR6CR.js';
2
+ export { downloadAndSave, downloadWithPresignedUrl, formatFileSize, getFileExtension, getMimeType, uploadWithPresignedUrl } from './chunk-U427BQIQ.js';
3
+ //# sourceMappingURL=browser.js.map
4
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"browser.js"}
@@ -0,0 +1,76 @@
1
+ import { storageM } from './chunk-U427BQIQ.js';
2
+ import { z } from 'zod';
3
+ import { core } from '@h-ai/core';
4
+
5
+ var StorageTypeSchema = z.enum(["s3", "local"]);
6
+ var S3ConfigSchema = z.object({
7
+ /** 存储类型 */
8
+ type: z.literal("s3"),
9
+ /** 存储桶名称 */
10
+ bucket: z.string().min(1, storageM("storage_config_bucketRequired")),
11
+ /** 区域 */
12
+ region: z.string().min(1, storageM("storage_config_regionRequired")),
13
+ /** Access Key ID */
14
+ accessKeyId: z.string().min(1, storageM("storage_config_accessKeyIdRequired")),
15
+ /** Secret Access Key */
16
+ secretAccessKey: z.string().min(1, storageM("storage_config_secretAccessKeyRequired")),
17
+ /** 自定义端点(用于 MinIO、阿里云 OSS 等) */
18
+ endpoint: z.string().url().optional(),
19
+ /** 强制使用路径风格 URL(某些 S3 兼容服务需要) */
20
+ forcePathStyle: z.boolean().default(false),
21
+ /** 路径前缀(所有操作都会加上此前缀) */
22
+ prefix: z.string().default(""),
23
+ /** 公开访问基础 URL(用于生成公开 URL) */
24
+ publicUrl: z.string().url().optional()
25
+ });
26
+ var LocalConfigSchema = z.object({
27
+ /** 存储类型 */
28
+ type: z.literal("local"),
29
+ /** 根目录路径 */
30
+ root: z.string().min(1, storageM("storage_config_rootRequired")),
31
+ /** 目录创建权限 */
32
+ directoryMode: z.number().default(493),
33
+ /** 文件创建权限 */
34
+ fileMode: z.number().default(420)
35
+ });
36
+ var StorageConfigSchema = z.discriminatedUnion("type", [
37
+ S3ConfigSchema,
38
+ LocalConfigSchema
39
+ ]);
40
+ var PresignOptionsSchema = z.object({
41
+ /** 过期时间(秒),范围 1~604800(7 天),默认 3600(1 小时) */
42
+ expiresIn: z.number().min(1).max(604800).default(3600),
43
+ // 最长 7 天
44
+ /** 响应内容类型(用于下载时设置 Content-Type) */
45
+ responseContentType: z.string().optional(),
46
+ /** 响应内容处置(用于设置下载文件名) */
47
+ responseContentDisposition: z.string().optional()
48
+ });
49
+ var PresignUploadOptionsSchema = PresignOptionsSchema.extend({
50
+ /** 上传内容类型(必须与实际上传匹配) */
51
+ contentType: z.string().default("application/octet-stream"),
52
+ /** 最大文件大小限制(bytes) */
53
+ maxSize: z.number().optional()
54
+ });
55
+ var StorageErrorInfo = {
56
+ CONNECTION_FAILED: "001:500",
57
+ OPERATION_FAILED: "002:500",
58
+ NOT_FOUND: "003:404",
59
+ ALREADY_EXISTS: "004:409",
60
+ PERMISSION_DENIED: "005:403",
61
+ QUOTA_EXCEEDED: "006:413",
62
+ INVALID_PATH: "007:400",
63
+ IO_ERROR: "008:500",
64
+ NETWORK_ERROR: "009:502",
65
+ NOT_INITIALIZED: "010:500",
66
+ UNSUPPORTED_TYPE: "011:400",
67
+ CONFIG_ERROR: "012:500",
68
+ PRESIGN_FAILED: "013:500",
69
+ UPLOAD_FAILED: "014:500",
70
+ DOWNLOAD_FAILED: "015:500"
71
+ };
72
+ var HaiStorageError = core.error.buildHaiErrorsDef("storage", StorageErrorInfo);
73
+
74
+ export { HaiStorageError, LocalConfigSchema, PresignOptionsSchema, PresignUploadOptionsSchema, S3ConfigSchema, StorageConfigSchema, StorageTypeSchema };
75
+ //# sourceMappingURL=chunk-KHZOR6CR.js.map
76
+ //# sourceMappingURL=chunk-KHZOR6CR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage-config.ts","../src/storage-types.ts"],"names":[],"mappings":"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","file":"chunk-KHZOR6CR.js","sourcesContent":["/**\n * @h-ai/storage — 错误码 + 配置 Schema\n * @module storage-config\n */\n\nimport { z } from 'zod'\nimport { storageM } from './storage-i18n.js'\n\n// ─── 配置 Schema ───\n\n/**\n * 存储类型枚举\n *\n * 支持的存储类型:\n * - `s3` - S3 协议对象存储(兼容 AWS S3、MinIO、阿里云 OSS 等)\n * - `local` - 本地文件存储(仅后端)\n */\nexport const StorageTypeSchema = z.enum(['s3', 'local'])\n\n/** 存储类型 */\nexport type StorageType = z.infer<typeof StorageTypeSchema>\n\n/**\n * S3 存储配置 Schema\n *\n * 支持标准 AWS S3 以及兼容 S3 协议的存储服务(MinIO、阿里云 OSS 等)。\n * 必填字段:bucket、region、accessKeyId、secretAccessKey。\n */\nexport const S3ConfigSchema = z.object({\n /** 存储类型 */\n type: z.literal('s3'),\n\n /** 存储桶名称 */\n bucket: z.string().min(1, storageM('storage_config_bucketRequired')),\n\n /** 区域 */\n region: z.string().min(1, storageM('storage_config_regionRequired')),\n\n /** Access Key ID */\n accessKeyId: z.string().min(1, storageM('storage_config_accessKeyIdRequired')),\n\n /** Secret Access Key */\n secretAccessKey: z.string().min(1, storageM('storage_config_secretAccessKeyRequired')),\n\n /** 自定义端点(用于 MinIO、阿里云 OSS 等) */\n endpoint: z.string().url().optional(),\n\n /** 强制使用路径风格 URL(某些 S3 兼容服务需要) */\n forcePathStyle: z.boolean().default(false),\n\n /** 路径前缀(所有操作都会加上此前缀) */\n prefix: z.string().default(''),\n\n /** 公开访问基础 URL(用于生成公开 URL) */\n publicUrl: z.string().url().optional(),\n})\n\n/** S3 配置类型(parse 后,所有字段已填充默认值) */\nexport type S3Config = z.infer<typeof S3ConfigSchema>\n\n/**\n * 本地存储配置 Schema\n *\n * 基于本地文件系统,仅后端可用。必填字段:root。\n */\nexport const LocalConfigSchema = z.object({\n /** 存储类型 */\n type: z.literal('local'),\n\n /** 根目录路径 */\n root: z.string().min(1, storageM('storage_config_rootRequired')),\n\n /** 目录创建权限 */\n directoryMode: z.number().default(0o755),\n\n /** 文件创建权限 */\n fileMode: z.number().default(0o644),\n})\n\n/** 本地配置类型(parse 后,所有字段已填充默认值) */\nexport type LocalConfig = z.infer<typeof LocalConfigSchema>\n\n/**\n * 统一存储配置 Schema\n *\n * 支持 S3 和本地文件两种类型的配置验证。\n *\n * @example\n * ```ts\n * // S3 配置\n * const s3Config = StorageConfigSchema.parse({\n * type: 's3',\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'AKIAXXXXXXXX',\n * secretAccessKey: 'xxxxx'\n * })\n *\n * // MinIO 配置\n * const minioConfig = StorageConfigSchema.parse({\n * type: 's3',\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * endpoint: 'http://localhost:9000',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * forcePathStyle: true\n * })\n *\n * // 本地存储配置\n * const localConfig = StorageConfigSchema.parse({\n * type: 'local',\n * root: '/data/uploads'\n * })\n * ```\n */\nexport const StorageConfigSchema = z.discriminatedUnion('type', [\n S3ConfigSchema,\n LocalConfigSchema,\n])\n\n/** 存储配置类型(parse 后的完整配置) */\nexport type StorageConfig = z.infer<typeof StorageConfigSchema>\n\n/** 存储配置输入类型(parse 前,允许省略带默认值的字段) */\nexport type StorageConfigInput = z.input<typeof StorageConfigSchema>\n\n// ─── 签名 URL 配置 ───\n\n/**\n * 签名 URL 配置 Schema\n *\n * 用于控制 `storage.presign.getUrl()` 生成的下载签名 URL 参数。\n *\n * @example\n * ```ts\n * const url = await storage.presign.getUrl('report.pdf', {\n * expiresIn: 600, // 10 分钟过期\n * responseContentDisposition: 'attachment; filename=\"report.pdf\"',\n * })\n * ```\n */\nexport const PresignOptionsSchema = z.object({\n /** 过期时间(秒),范围 1~604800(7 天),默认 3600(1 小时) */\n expiresIn: z.number().min(1).max(604800).default(3600), // 最长 7 天\n\n /** 响应内容类型(用于下载时设置 Content-Type) */\n responseContentType: z.string().optional(),\n\n /** 响应内容处置(用于设置下载文件名) */\n responseContentDisposition: z.string().optional(),\n})\n\n/** 签名 URL 配置类型(parse 后) */\nexport type PresignOptions = z.infer<typeof PresignOptionsSchema>\n\n/**\n * 上传签名 URL 配置 Schema\n *\n * 继承 PresignOptionsSchema 并扩展上传专属参数,用于 `storage.presign.putUrl()`。\n *\n * @example\n * ```ts\n * const url = await storage.presign.putUrl('uploads/avatar.png', {\n * contentType: 'image/png',\n * maxSize: 5 * 1024 * 1024, // 5MB 限制\n * expiresIn: 300,\n * })\n * ```\n */\nexport const PresignUploadOptionsSchema = PresignOptionsSchema.extend({\n /** 上传内容类型(必须与实际上传匹配) */\n contentType: z.string().default('application/octet-stream'),\n\n /** 最大文件大小限制(bytes) */\n maxSize: z.number().optional(),\n})\n\n/** 上传签名 URL 配置类型(parse 后) */\nexport type PresignUploadOptions = z.infer<typeof PresignUploadOptionsSchema>\n","/**\n * @h-ai/storage — 公共类型定义\n *\n * 包含:\n * - 错误类型(StorageError)\n * - 文件元数据(FileMetadata)\n * - 列表结果(ListResult)\n * - 文件操作接口(FileOperations)\n * - 目录操作接口(DirOperations)\n * - 签名 URL 操作接口(PresignOperations)\n * - 函数接口(StorageFunctions)\n * - Provider 接口(StorageProvider)\n * @module storage-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { Buffer } from 'node:buffer'\nimport type { PresignOptions, PresignUploadOptions, StorageConfig, StorageConfigInput } from './storage-config.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst StorageErrorInfo = {\n CONNECTION_FAILED: '001:500',\n OPERATION_FAILED: '002:500',\n NOT_FOUND: '003:404',\n ALREADY_EXISTS: '004:409',\n PERMISSION_DENIED: '005:403',\n QUOTA_EXCEEDED: '006:413',\n INVALID_PATH: '007:400',\n IO_ERROR: '008:500',\n NETWORK_ERROR: '009:502',\n NOT_INITIALIZED: '010:500',\n UNSUPPORTED_TYPE: '011:400',\n CONFIG_ERROR: '012:500',\n PRESIGN_FAILED: '013:500',\n UPLOAD_FAILED: '014:500',\n DOWNLOAD_FAILED: '015:500',\n} as const satisfies ErrorInfo\n\nexport const HaiStorageError = core.error.buildHaiErrorsDef('storage', StorageErrorInfo)\n\n// ─── 文件元数据 ───\n\n/**\n * 文件元数据接口\n *\n * 表示存储中单个文件的元信息,由 put/head/copy/list 等操作返回。\n */\nexport interface FileMetadata {\n /** 文件键/路径(不含配置 prefix,对用户透明) */\n key: string\n /** 文件大小(字节) */\n size: number\n /** 内容类型(MIME),未指定时根据扩展名自动推断 */\n contentType: string\n /** 最后修改时间 */\n lastModified: Date\n /** ETag(用于缓存验证,本地存储为 MD5,S3 由服务端生成) */\n etag?: string\n /** 自定义元数据键值对 */\n metadata?: Record<string, string>\n}\n\n/**\n * 列表选项\n *\n * 用于控制 `storage.dir.list()` 的过滤、分页和目录模拟行为。\n */\nexport interface ListOptions {\n /** 前缀过滤(只返回以此前缀开头的文件) */\n prefix?: string\n /**\n * 分页标记(从上次 ListResult.nextContinuationToken 获取)\n *\n * 注意:本地存储目前不支持此字段,仅 S3 Provider 有效。\n */\n continuationToken?: string\n /** 每页最大数量(默认 1000) */\n maxKeys?: number\n /** 分隔符(用于模拟目录结构,通常为 '/';传入后子目录作为 commonPrefixes 返回) */\n delimiter?: string\n}\n\n/**\n * 列表结果\n *\n * 包含当前页的文件列表、目录前缀、分页信息。\n */\nexport interface ListResult {\n /** 当前页文件元数据列表 */\n files: FileMetadata[]\n /** 公共前缀(使用 delimiter 时由子目录路径组成) */\n commonPrefixes: string[]\n /** 下一页标记;无后续页时为 undefined */\n nextContinuationToken?: string\n /** 是否被截断(true 表示还有更多数据,可用 nextContinuationToken 继续拉取) */\n isTruncated: boolean\n}\n\n// ─── 上传下载选项 ───\n\n/**\n * 上传选项\n *\n * 用于控制 `storage.file.put()` 的元数据与缓存行为。\n */\nexport interface UploadOptions {\n /** 内容类型(不指定时根据文件扩展名自动推断) */\n contentType?: string\n /** 自定义元数据键值对(存储后可通过 head 获取) */\n metadata?: Record<string, string>\n /** Cache-Control 响应头(例如 'max-age=3600') */\n cacheControl?: string\n /** Content-Disposition 响应头(例如 'attachment; filename=\"a.txt\"') */\n contentDisposition?: string\n}\n\n/**\n * 下载选项\n *\n * 用于控制 `storage.file.get()` 的范围读取。\n * 两个字段均为字节偏移(含),与 HTTP Range 语义一致。\n */\nexport interface DownloadOptions {\n /** 范围起始位置(字节,含);不指定时从文件开头读取 */\n rangeStart?: number\n /** 范围结束位置(字节,含);不指定时读取到文件末尾 */\n rangeEnd?: number\n}\n\n/**\n * 复制选项\n *\n * 用于控制 `storage.file.copy()` 的目标文件元数据。\n * 提供任意字段时,S3 Provider 会使用 MetadataDirective='REPLACE' 覆盖源文件元数据。\n */\nexport interface CopyOptions {\n /** 目标文件的内容类型(不指定时继承源文件) */\n contentType?: string\n /** 目标文件的自定义元数据(不指定时继承源文件) */\n metadata?: Record<string, string>\n}\n\n// ─── 操作接口 ───\n\n/**\n * 文件操作接口\n *\n * 提供文件的读写操作。\n */\nexport interface FileOperations {\n /**\n * 上传文件\n *\n * @param key - 文件键/路径\n * @param data - 文件内容(Buffer、Uint8Array 或字符串)\n * @param options - 上传选项\n * @returns 上传结果包含文件元数据\n * @example\n * ```ts\n * const result = await storage.file.put('uploads/a.txt', 'hello')\n * if (result.success) {\n * result.data.key\n * }\n * ```\n */\n put: (key: string, data: Buffer | Uint8Array | string, options?: UploadOptions) => Promise<HaiResult<FileMetadata>>\n\n /**\n * 下载文件\n *\n * @param key - 文件键/路径\n * @param options - 下载选项\n * @returns 文件内容\n * @example\n * ```ts\n * const result = await storage.file.get('uploads/a.txt')\n * if (result.success) {\n * const buffer = result.data\n * }\n * ```\n */\n get: (key: string, options?: DownloadOptions) => Promise<HaiResult<Buffer>>\n\n /**\n * 获取文件元数据\n *\n * @param key - 文件键/路径\n * @returns 文件元数据\n * @example\n * ```ts\n * const result = await storage.file.head('uploads/a.txt')\n * if (result.success) {\n * result.data.size\n * }\n * ```\n */\n head: (key: string) => Promise<HaiResult<FileMetadata>>\n\n /**\n * 检查文件是否存在\n *\n * @param key - 文件键/路径\n * @returns 是否存在\n * @example\n * ```ts\n * const result = await storage.file.exists('uploads/a.txt')\n * if (result.success) {\n * result.data\n * }\n * ```\n */\n exists: (key: string) => Promise<HaiResult<boolean>>\n\n /**\n * 删除文件\n *\n * @param key - 文件键/路径\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.file.delete('uploads/a.txt')\n * ```\n */\n delete: (key: string) => Promise<HaiResult<void>>\n\n /**\n * 批量删除文件\n *\n * @param keys - 文件键/路径列表\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.file.deleteMany(['a.txt', 'b.txt'])\n * ```\n */\n deleteMany: (keys: string[]) => Promise<HaiResult<void>>\n\n /**\n * 复制文件\n *\n * @param sourceKey - 源文件键\n * @param destKey - 目标文件键\n * @param options - 复制选项\n * @returns 目标文件元数据\n * @example\n * ```ts\n * await storage.file.copy('a.txt', 'b.txt')\n * ```\n */\n copy: (sourceKey: string, destKey: string, options?: CopyOptions) => Promise<HaiResult<FileMetadata>>\n}\n\n/**\n * 目录操作接口\n *\n * 提供目录(前缀)的列表操作。\n */\nexport interface DirOperations {\n /**\n * 列出目录内容\n *\n * @param options - 列表选项\n * @returns 文件和目录列表\n * @example\n * ```ts\n * const result = await storage.dir.list({ prefix: 'uploads/' })\n * if (result.success) {\n * result.data.files\n * }\n * ```\n */\n list: (options?: ListOptions) => Promise<HaiResult<ListResult>>\n\n /**\n * 删除目录(删除指定前缀下的所有文件)\n *\n * @param prefix - 目录前缀\n * @returns 删除结果\n * @example\n * ```ts\n * await storage.dir.delete('uploads/tmp/')\n * ```\n */\n delete: (prefix: string) => Promise<HaiResult<void>>\n}\n\n/**\n * 签名 URL 操作接口\n *\n * 提供生成临时授权 URL 的功能,支持前端直接上传下载。\n */\nexport interface PresignOperations {\n /**\n * 生成下载签名 URL\n *\n * @param key - 文件键/路径\n * @param options - 签名选项\n * @returns 签名 URL\n * @example\n * ```ts\n * const result = await storage.presign.getUrl('a.txt', { expiresIn: 60 })\n * ```\n */\n getUrl: (key: string, options?: PresignOptions) => Promise<HaiResult<string>>\n\n /**\n * 生成上传签名 URL\n *\n * @param key - 文件键/路径\n * @param options - 上传签名选项\n * @returns 签名 URL\n * @example\n * ```ts\n * const result = await storage.presign.putUrl('a.txt', { contentType: 'text/plain' })\n * ```\n */\n putUrl: (key: string, options?: PresignUploadOptions) => Promise<HaiResult<string>>\n\n /**\n * 获取公开访问 URL(如果配置了 publicUrl)\n *\n * @param key - 文件键/路径\n * @returns 公开 URL,如果未配置 publicUrl 则返回 null\n * @example\n * ```ts\n * const url = storage.presign.publicUrl('a.txt')\n * ```\n */\n publicUrl: (key: string) => string | null\n}\n\n// ─── 函数接口 ───\n\n/**\n * 存储函数接口\n *\n * 统一的存储访问入口:\n * - `storage.init()` — 初始化存储连接\n * - `storage.close()` — 关闭连接并释放资源\n * - `storage.file` — 文件操作(上传/下载/删除/复制等)\n * - `storage.dir` — 目录操作(列表/删除)\n * - `storage.presign` — 签名 URL 操作\n * - `storage.config` — 当前连接配置(未初始化时为 null)\n * - `storage.isInitialized` — 初始化状态\n */\nexport interface StorageFunctions {\n /**\n * 初始化存储连接\n *\n * 已有连接时会先 close 再重新初始化。\n *\n * @param config - 存储配置(支持省略带默认值的字段)\n * @returns 成功 ok(undefined);失败返回 err(含 StorageError)\n */\n init: (config: StorageConfigInput) => Promise<HaiResult<void>>\n\n /**\n * 关闭存储连接并释放资源\n *\n * 重复调用不会报错。\n */\n close: () => Promise<void>\n\n /** 当前解析后的存储配置;未初始化或已关闭时为 null */\n readonly config: StorageConfig | null\n\n /** 是否已完成初始化 */\n readonly isInitialized: boolean\n\n /** 文件操作接口(put / get / head / exists / delete / deleteMany / copy) */\n readonly file: FileOperations\n\n /** 目录操作接口(list / delete) */\n readonly dir: DirOperations\n\n /** 签名 URL 操作接口(getUrl / putUrl / publicUrl) */\n readonly presign: PresignOperations\n}\n\n// ─── Provider 接口 ───\n\n/**\n * 存储 Provider 接口\n *\n * 由各具体存储实现(S3、Local)实现此接口。\n * Provider 仅在 storage-main 内部使用,不直接对外暴露实例。\n */\nexport interface StorageProvider {\n /** Provider 名称标识(如 's3'、'local') */\n readonly name: string\n\n /**\n * 建立与存储服务的连接\n *\n * @param config - 经 Schema 校验后的完整配置\n * @returns 连接结果\n */\n connect: (config: StorageConfig) => Promise<HaiResult<void>>\n\n /** 关闭连接并释放资源 */\n close: () => Promise<void>\n\n /** 检查是否已连接 */\n isConnected: () => boolean\n\n /** 文件操作接口 */\n readonly file: FileOperations\n\n /** 目录操作接口 */\n readonly dir: DirOperations\n\n /** 签名 URL 操作接口 */\n readonly presign: PresignOperations\n}\n"]}