@seaverse/utils-sdk 0.1.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.
package/README.md ADDED
@@ -0,0 +1,446 @@
1
+ # @seaverse/utils-sdk
2
+
3
+ SeaVerse Utils SDK - 通用工具集合,包含文件上传、图片压缩等功能
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@seaverse/utils-sdk.svg)](https://www.npmjs.com/package/@seaverse/utils-sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## 功能特性
9
+
10
+ - 📤 **文件上传** - 预签名 URL 两步上传,安全高效
11
+ - 🖼️ **图片压缩** - 自动压缩到 1080p,可配置质量和尺寸
12
+ - 📊 **上传进度** - 实时进度回调,支持速度和剩余时间计算
13
+ - ✅ **文件验证** - 大小和类型验证
14
+ - ⚡ **TypeScript** - 完整的类型定义
15
+ - 🌍 **环境配置** - 支持多环境切换
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install @seaverse/utils-sdk
21
+ # 或
22
+ pnpm add @seaverse/utils-sdk
23
+ ```
24
+
25
+ ## 快速开始
26
+
27
+ ### 基础用法
28
+
29
+ ```typescript
30
+ import { UtilsClient } from '@seaverse/utils-sdk';
31
+
32
+ // 初始化客户端
33
+ const client = new UtilsClient({
34
+ token: 'your-bearer-token',
35
+ });
36
+
37
+ // 上传文件
38
+ const file = document.querySelector('input[type="file"]').files[0];
39
+ const result = await client.uploadFile(file);
40
+
41
+ console.log('CDN URL:', result.cdnUrl);
42
+ console.log('文件大小:', result.fileSize);
43
+ console.log('是否压缩:', result.compressed);
44
+ ```
45
+
46
+ ### 带进度回调
47
+
48
+ ```typescript
49
+ const result = await client.uploadFile(file, {
50
+ onProgress: (progress) => {
51
+ console.log(`进度: ${progress.percent}%`);
52
+ console.log(`阶段: ${progress.stage}`);
53
+ console.log(`速度: ${progress.speed} 字节/秒`);
54
+ console.log(`剩余时间: ${progress.remainingTime} 秒`);
55
+ },
56
+ });
57
+ ```
58
+
59
+ ### 禁用图片压缩
60
+
61
+ ```typescript
62
+ const result = await client.uploadFile(file, {
63
+ compress: false, // 禁用压缩
64
+ });
65
+ ```
66
+
67
+ ### 自定义压缩配置
68
+
69
+ ```typescript
70
+ const client = new UtilsClient({
71
+ token: 'your-bearer-token',
72
+ compress: {
73
+ enabled: true,
74
+ maxWidth: 1920, // 自定义最大宽度
75
+ maxHeight: 1080, // 自定义最大高度
76
+ quality: 0.9, // 自定义压缩质量
77
+ },
78
+ });
79
+ ```
80
+
81
+ ## API 参考
82
+
83
+ ### UtilsClient
84
+
85
+ #### 构造函数
86
+
87
+ ```typescript
88
+ new UtilsClient(options: UtilsClientOptions)
89
+ ```
90
+
91
+ **参数:**
92
+
93
+ - `token` (string, 必需): Bearer Token
94
+ - `baseURL` (string, 可选): API 基础 URL,默认 `https://resource.seaverse.ai`
95
+ - `timeout` (number, 可选): 请求超时(毫秒),默认 60000
96
+ - `compress` (CompressionConfig, 可选): 图片压缩配置
97
+
98
+ **CompressionConfig:**
99
+
100
+ - `enabled` (boolean): 是否启用压缩,默认 `true`
101
+ - `maxWidth` (number): 最大宽度,默认 `1080`
102
+ - `maxHeight` (number): 最大高度,默认 `1080`
103
+ - `quality` (number): 压缩质量 (0-1),默认 `0.8`
104
+
105
+ #### uploadFile()
106
+
107
+ 上传文件,自动处理压缩、验证、上传流程。
108
+
109
+ ```typescript
110
+ async uploadFile(file: File, options?: UploadOptions): Promise<UploadResult>
111
+ ```
112
+
113
+ **参数:**
114
+
115
+ - `file`: 要上传的文件对象
116
+ - `options` (可选):
117
+ - `onProgress`: 进度回调函数
118
+ - `compress`: 是否压缩(覆盖全局配置)
119
+ - `metadata`: 自定义元数据
120
+
121
+ **返回:**
122
+
123
+ ```typescript
124
+ {
125
+ fileName: string // 文件名
126
+ originalFileName: string // 原始文件名
127
+ cdnUrl: string // CDN 访问 URL
128
+ objectPath: string // 存储路径
129
+ fileSize: number // 文件大小(字节)
130
+ originalSize: number // 原始大小(字节)
131
+ compressed: boolean // 是否被压缩
132
+ contentType: string // MIME 类型
133
+ duration: number // 上传耗时(毫秒)
134
+ expiresAt: string // 过期时间
135
+ }
136
+ ```
137
+
138
+ **错误:**
139
+
140
+ - 抛出 `UtilsAPIError` 异常
141
+
142
+ **示例:**
143
+
144
+ ```typescript
145
+ try {
146
+ const result = await client.uploadFile(file, {
147
+ onProgress: (progress) => {
148
+ updateProgressBar(progress.percent);
149
+ },
150
+ });
151
+ console.log('上传成功:', result.cdnUrl);
152
+ } catch (error) {
153
+ if (error instanceof UtilsAPIError) {
154
+ console.error(`上传失败 [${error.code}]: ${error.message}`);
155
+ }
156
+ }
157
+ ```
158
+
159
+ #### setToken()
160
+
161
+ 更新 Bearer Token。
162
+
163
+ ```typescript
164
+ setToken(token: string): void
165
+ ```
166
+
167
+ #### setBaseURL()
168
+
169
+ 更新基础 URL。
170
+
171
+ ```typescript
172
+ setBaseURL(baseURL: string): void
173
+ ```
174
+
175
+ #### updateCompressionConfig()
176
+
177
+ 更新压缩配置。
178
+
179
+ ```typescript
180
+ updateCompressionConfig(config: Partial<CompressionConfig>): void
181
+ ```
182
+
183
+ ## 工具函数
184
+
185
+ ### validateFile()
186
+
187
+ 验证文件大小和类型。
188
+
189
+ ```typescript
190
+ import { validateFile } from '@seaverse/utils-sdk';
191
+
192
+ const result = validateFile(file, {
193
+ maxSize: 10 * 1024 * 1024, // 10MB
194
+ allowedTypes: ['image/jpeg', 'image/png'],
195
+ });
196
+
197
+ if (!result.valid) {
198
+ console.error('验证失败:', result.errors);
199
+ }
200
+ ```
201
+
202
+ ### compressImage()
203
+
204
+ 手动压缩图片。
205
+
206
+ ```typescript
207
+ import { compressImage } from '@seaverse/utils-sdk';
208
+
209
+ const result = await compressImage(file, {
210
+ maxWidth: 1920,
211
+ maxHeight: 1080,
212
+ quality: 0.8,
213
+ });
214
+
215
+ console.log('压缩率:', result.compressionRatio);
216
+ console.log('压缩后大小:', result.compressedSize);
217
+ ```
218
+
219
+ ### formatBytes()
220
+
221
+ 格式化字节数为可读字符串。
222
+
223
+ ```typescript
224
+ import { formatBytes } from '@seaverse/utils-sdk';
225
+
226
+ console.log(formatBytes(1024)); // "1 KB"
227
+ console.log(formatBytes(1048576)); // "1 MB"
228
+ console.log(formatBytes(1073741824)); // "1 GB"
229
+ ```
230
+
231
+ ## 错误处理
232
+
233
+ ```typescript
234
+ import { UtilsAPIError, ErrorCode } from '@seaverse/utils-sdk';
235
+
236
+ try {
237
+ const result = await client.uploadFile(file);
238
+ } catch (error) {
239
+ if (error instanceof UtilsAPIError) {
240
+ switch (error.code) {
241
+ case ErrorCode.FILE_TOO_LARGE:
242
+ alert('文件太大,请选择小于 100MB 的文件');
243
+ break;
244
+ case ErrorCode.INVALID_FILE_TYPE:
245
+ alert('不支持的文件类型');
246
+ break;
247
+ case ErrorCode.NETWORK_ERROR:
248
+ alert('网络错误,请检查网络连接');
249
+ break;
250
+ default:
251
+ alert(`上传失败: ${error.message}`);
252
+ }
253
+ }
254
+ }
255
+ ```
256
+
257
+ ## 错误码
258
+
259
+ | 错误码 | 说明 |
260
+ |--------|------|
261
+ | `FILE_TOO_LARGE` | 文件大小超过限制 |
262
+ | `INVALID_FILE_TYPE` | 不支持的文件类型 |
263
+ | `PRESIGNED_URL_FAILED` | 获取预签名 URL 失败 |
264
+ | `UPLOAD_FAILED` | 上传失败 |
265
+ | `COMPRESSION_FAILED` | 图片压缩失败 |
266
+ | `NETWORK_ERROR` | 网络错误 |
267
+ | `TIMEOUT` | 请求超时 |
268
+ | `VALIDATION_FAILED` | 文件验证失败 |
269
+
270
+ ## 环境配置
271
+
272
+ 不同环境使用不同的 baseURL:
273
+
274
+ ```typescript
275
+ // 开发环境(默认)
276
+ const client = new UtilsClient({
277
+ token: devToken,
278
+ // baseURL 默认为 'https://resource.seaverse.ai'
279
+ });
280
+
281
+ // 生产环境
282
+ const client = new UtilsClient({
283
+ baseURL: 'https://resource.sg.seaverse.dev',
284
+ token: prodToken,
285
+ });
286
+
287
+ // 本地开发
288
+ const client = new UtilsClient({
289
+ baseURL: 'http://localhost:8003',
290
+ token: localToken,
291
+ });
292
+ ```
293
+
294
+ ## 与 Auth SDK 集成
295
+
296
+ ```typescript
297
+ import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';
298
+ import { UtilsClient } from '@seaverse/utils-sdk';
299
+
300
+ // 1. 使用 Auth SDK 登录
301
+ const authClient = new SeaVerseBackendAPIClient({
302
+ appId: 'your-app-id',
303
+ });
304
+
305
+ const loginResult = await authClient.login({
306
+ email: 'user@example.com',
307
+ password: 'password',
308
+ });
309
+
310
+ // 2. 使用相同的 token 初始化 Utils SDK
311
+ const utilsClient = new UtilsClient({
312
+ token: loginResult.token,
313
+ });
314
+
315
+ // 3. 上传文件
316
+ const result = await utilsClient.uploadFile(file);
317
+ ```
318
+
319
+ ## 上传流程说明
320
+
321
+ SDK 使用预签名 URL 两步上传模式:
322
+
323
+ ```
324
+ 1. 获取预签名 URL
325
+ POST /api/v1/resources/presign-upload
326
+ → 返回 upload_url 和 cdn_url
327
+
328
+ 2. 上传到 GCS
329
+ PUT {upload_url}
330
+ → 直接上传到 Google Cloud Storage
331
+
332
+ 3. 返回 CDN URL
333
+ 使用 cdn_url 访问文件
334
+ ```
335
+
336
+ **优势:**
337
+
338
+ - ✅ 安全:Token 不暴露给第三方存储
339
+ - ✅ 高效:直接上传到 GCS,无需经过后端
340
+ - ✅ 可靠:GCS 提供高可用性保证
341
+
342
+ ## 图片压缩说明
343
+
344
+ ### 压缩策略
345
+
346
+ - 最大尺寸: 1080×1080 像素(默认配置)
347
+ - 保持宽高比自动缩放
348
+ - 可配置压缩质量 (0-1)
349
+ - 支持自定义最大宽高(maxWidth、maxHeight)
350
+
351
+ ### 压缩触发条件
352
+
353
+ 只有以下条件同时满足时才会压缩:
354
+
355
+ 1. 文件是图片类型(MIME type 以 `image/` 开头)
356
+ 2. 压缩配置启用(`compress.enabled === true`)
357
+ 3. 未在上传选项中明确禁用(`options.compress !== false`)
358
+
359
+ ### 跳过压缩
360
+
361
+ ```typescript
362
+ // 方式 1:全局禁用
363
+ const client = new UtilsClient({
364
+ token: 'xxx',
365
+ compress: { enabled: false },
366
+ });
367
+
368
+ // 方式 2:单次上传禁用
369
+ const result = await client.uploadFile(file, {
370
+ compress: false,
371
+ });
372
+ ```
373
+
374
+ ## 常见问题
375
+
376
+ ### Q1: 如何获取 token?
377
+
378
+ **A**: 通过 `@seaverse/auth-sdk` 登录获取 Bearer Token
379
+
380
+ ### Q2: 支持哪些文件类型?
381
+
382
+ **A**:
383
+ - **图片**: JPEG, PNG, GIF, WebP
384
+ - **文档**: PDF, TXT, MD
385
+ - **其他**: 根据后端配置而定
386
+
387
+ ### Q3: 上传文件大小限制?
388
+
389
+ **A**:
390
+ - 默认限制: 100MB
391
+ - 可通过验证规则自定义
392
+ - 图片会自动压缩,减小传输大小
393
+
394
+ ### Q4: 如何处理上传失败?
395
+
396
+ **A**:
397
+ ```typescript
398
+ try {
399
+ await client.uploadFile(file);
400
+ } catch (error) {
401
+ if (error instanceof UtilsAPIError) {
402
+ // 根据错误码处理
403
+ if (error.code === ErrorCode.NETWORK_ERROR) {
404
+ // 重试逻辑
405
+ }
406
+ }
407
+ }
408
+ ```
409
+
410
+ ### Q5: 压缩会影响图片质量吗?
411
+
412
+ **A**:
413
+ - 默认质量为 0.8,平衡文件大小和画质
414
+ - 可通过 `quality` 参数调整 (0-1)
415
+ - 建议值: 0.7-0.9
416
+
417
+ ## 开发
418
+
419
+ ```bash
420
+ # 安装依赖
421
+ pnpm install
422
+
423
+ # 构建
424
+ pnpm build
425
+
426
+ # 开发模式
427
+ pnpm dev
428
+
429
+ # 类型检查
430
+ pnpm typecheck
431
+ ```
432
+
433
+ ## 许可证
434
+
435
+ MIT © [SeaVerse Team](mailto:support@seaverse.com)
436
+
437
+ ## 更新日志
438
+
439
+ ### v0.1.0 (2026-01-07)
440
+
441
+ - 🎉 初始版本发布
442
+ - ✨ 支持文件上传(预签名 URL 模式)
443
+ - ✨ 支持图片自动压缩(1080p)
444
+ - ✨ 支持上传进度回调
445
+ - ✨ 支持文件验证
446
+ - ✨ 完整的 TypeScript 类型定义
@@ -0,0 +1,96 @@
1
+ /**
2
+ * SeaVerse Utils SDK Client
3
+ */
4
+ import { UtilsClientOptions, UploadOptions, UploadResult, UploadProgress, PresignUploadResponse, CompressionConfig } from './models.js';
5
+ /**
6
+ * SeaVerse Utils API Client
7
+ *
8
+ * 提供文件上传、图片压缩等功能,支持多租户隔离。
9
+ * 自动处理预签名 URL 上传流程:
10
+ * 1. 获取预签名 URL
11
+ * 2. 直接上传到 GCS
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const client = new UtilsClient({
16
+ * token: 'your-bearer-token'
17
+ * });
18
+ *
19
+ * const file = document.querySelector('input[type="file"]').files[0];
20
+ * const result = await client.uploadFile(file, {
21
+ * onProgress: (progress) => {
22
+ * console.log(`${progress.percent}% - ${progress.stage}`);
23
+ * }
24
+ * });
25
+ *
26
+ * console.log('CDN URL:', result.cdnUrl);
27
+ * ```
28
+ */
29
+ export declare class UtilsClient {
30
+ private axios;
31
+ private compressionConfig;
32
+ constructor(options: UtilsClientOptions);
33
+ /**
34
+ * 上传文件
35
+ *
36
+ * 自动处理完整的上传流程:
37
+ * 1. 文件验证
38
+ * 2. 图片压缩(如果启用)
39
+ * 3. 获取预签名 URL
40
+ * 4. 上传到 GCS
41
+ *
42
+ * @param file - 要上传的文件
43
+ * @param options - 上传选项
44
+ * @returns 上传结果
45
+ * @throws {UtilsAPIError} 上传失败时抛出错误
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const result = await client.uploadFile(file, {
50
+ * onProgress: (progress) => {
51
+ * console.log(`上传进度: ${progress.percent}%`);
52
+ * },
53
+ * compress: true
54
+ * });
55
+ * ```
56
+ */
57
+ uploadFile(file: File, options?: UploadOptions): Promise<UploadResult>;
58
+ /**
59
+ * 获取预签名 URL
60
+ *
61
+ * @param contentType - 文件 MIME 类型
62
+ * @returns 预签名 URL 响应
63
+ * @throws {UtilsAPIError} 请求失败时抛出错误
64
+ */
65
+ getPresignedUrl(contentType: string): Promise<PresignUploadResponse>;
66
+ /**
67
+ * 上传文件到预签名 URL
68
+ *
69
+ * 使用 XMLHttpRequest 实现,以支持上传进度回调
70
+ *
71
+ * @param uploadUrl - 预签名 URL
72
+ * @param file - 要上传的文件
73
+ * @param onProgress - 进度回调函数
74
+ * @throws {UtilsAPIError} 上传失败时抛出错误
75
+ */
76
+ uploadToPresignedUrl(uploadUrl: string, file: File, onProgress?: (progress: UploadProgress) => void): Promise<void>;
77
+ /**
78
+ * 更新 Bearer Token
79
+ *
80
+ * @param token - 新的 Bearer Token
81
+ */
82
+ setToken(token: string): void;
83
+ /**
84
+ * 更新基础 URL
85
+ *
86
+ * @param baseURL - 新的基础 URL
87
+ */
88
+ setBaseURL(baseURL: string): void;
89
+ /**
90
+ * 更新压缩配置
91
+ *
92
+ * @param config - 新的压缩配置
93
+ */
94
+ updateCompressionConfig(config: Partial<CompressionConfig>): void;
95
+ }
96
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EAEd,qBAAqB,EACrB,iBAAiB,EAGlB,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,iBAAiB,CAA8B;gBAE3C,OAAO,EAAE,kBAAkB;IA4BvC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IA8F5E;;;;;;OAMG;IACG,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAgC1E;;;;;;;;;OASG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,IAAI,EACV,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,GAC9C,OAAO,CAAC,IAAI,CAAC;IAgEhB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7B;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;OAIG;IACH,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;CAMlE"}