@dofe/file-sdk-web 0.1.1 → 0.1.3

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 CHANGED
@@ -26,8 +26,8 @@ const { fileId, cdnUrl } = await uploader.upload(file, {
26
26
  // Large files: multipart upload with concurrency
27
27
  const { fileId, cdnUrl } = await uploader.uploadMultipart(bigFile, {
28
28
  scope: 'media-asset',
29
- partSize: 5 * 1024 * 1024, // 5MB parts
30
- concurrency: 3, // 3 parallel parts
29
+ partSize: 5 * 1024 * 1024, // 5MB parts
30
+ concurrency: 3, // 3 parallel parts
31
31
  onProgress: ({ percent, loaded, total }) => {
32
32
  console.log(`${percent}% (${loaded}/${total})`);
33
33
  },
@@ -38,28 +38,28 @@ const { fileId, cdnUrl } = await uploader.uploadMultipart(bigFile, {
38
38
 
39
39
  ### `new FileUploader(config)`
40
40
 
41
- | Option | Type | Required | Default | Description |
42
- |--------|------|:---:|---------|-------------|
43
- | `apiBase` | `string` | | — | API base URL (e.g., `/api/proxy/sso` or `https://sso.dofe.ai`) |
44
- | `timeout` | `number` | | `30000` | Request timeout in ms |
41
+ | Option | Type | Required | Default | Description |
42
+ | --------- | -------- | :------: | ------- | -------------------------------------------------------------- |
43
+ | `apiBase` | `string` | | — | API base URL (e.g., `/api/proxy/sso` or `https://sso.dofe.ai`) |
44
+ | `timeout` | `number` | | `30000` | Request timeout in ms |
45
45
 
46
46
  ### Methods
47
47
 
48
- | Method | Returns | Description |
49
- |--------|---------|-------------|
50
- | `upload(file, options)` | `Promise<UploadResult>` | Auto-chooses direct or multipart based on file size (>5MB → multipart) |
51
- | `uploadDirect(file, options)` | `Promise<UploadResult>` | Direct upload for small files |
52
- | `uploadMultipart(file, options)` | `Promise<UploadResult>` | Multipart upload with configurable part size and concurrency |
48
+ | Method | Returns | Description |
49
+ | -------------------------------- | ----------------------- | ---------------------------------------------------------------------- |
50
+ | `upload(file, options)` | `Promise<UploadResult>` | Auto-chooses direct or multipart based on file size (>5MB → multipart) |
51
+ | `uploadDirect(file, options)` | `Promise<UploadResult>` | Direct upload for small files |
52
+ | `uploadMultipart(file, options)` | `Promise<UploadResult>` | Multipart upload with configurable part size and concurrency |
53
53
 
54
54
  ### UploadResult
55
55
 
56
56
  ```typescript
57
57
  interface UploadResult {
58
- fileId: string; // SSO FileSource UUID
59
- key: string; // Object storage key
60
- url: string; // Upload URL / token
61
- cdnUrl: string | null; // CDN URL (null for private files)
62
- bucket: string; // Storage bucket name
58
+ fileId: string; // SSO FileSource UUID
59
+ key: string; // Object storage key
60
+ url: string; // Upload URL / token
61
+ cdnUrl: string | null; // CDN URL (null for private files)
62
+ bucket: string; // Storage bucket name
63
63
  }
64
64
  ```
65
65
 
@@ -28,6 +28,7 @@ export declare class FileUploader {
28
28
  private uploadPart;
29
29
  /**
30
30
  * PUT file/blob directly to a presigned URL.
31
+ * Returns the ETag from the response headers for multipart completion.
31
32
  */
32
33
  private uploadToPresignedUrl;
33
34
  /**
package/dist/uploader.js CHANGED
@@ -71,10 +71,14 @@ class FileUploader {
71
71
  const { token, fileId, key, bucket, url: uploadId } = (_c = initRes.data) !== null && _c !== void 0 ? _c : initRes;
72
72
  // Step 2: Upload first part
73
73
  const part1 = file.slice(0, partSize);
74
- await this.uploadToPresignedUrl(token, part1);
75
- const parts = [{ partNumber: 1, etag: 'uploaded' }];
74
+ const etag1 = await this.uploadToPresignedUrl(token, part1);
75
+ const parts = [{ partNumber: 1, etag: etag1 }];
76
76
  let loaded = partSize;
77
- (_d = options.onProgress) === null || _d === void 0 ? void 0 : _d.call(options, { percent: Math.round((loaded / file.size) * 100), loaded, total: file.size });
77
+ (_d = options.onProgress) === null || _d === void 0 ? void 0 : _d.call(options, {
78
+ percent: Math.round((loaded / file.size) * 100),
79
+ loaded,
80
+ total: file.size,
81
+ });
78
82
  // Step 3: Upload remaining parts in parallel batches
79
83
  for (let i = 2; i <= totalParts; i += concurrency) {
80
84
  const batch = [];
@@ -86,7 +90,11 @@ class FileUploader {
86
90
  parts.push(...results.filter(Boolean));
87
91
  loaded += (batchEnd - i + 1) * partSize;
88
92
  const actualLoaded = Math.min(loaded, file.size);
89
- (_e = options.onProgress) === null || _e === void 0 ? void 0 : _e.call(options, { percent: Math.round((actualLoaded / file.size) * 100), loaded: actualLoaded, total: file.size });
93
+ (_e = options.onProgress) === null || _e === void 0 ? void 0 : _e.call(options, {
94
+ percent: Math.round((actualLoaded / file.size) * 100),
95
+ loaded: actualLoaded,
96
+ total: file.size,
97
+ });
90
98
  }
91
99
  // Step 4: Complete multipart upload
92
100
  const completeRes = await this.fetchJson(`${this.apiBase}/api/uploader/complete`, {
@@ -124,8 +132,8 @@ class FileUploader {
124
132
  const start = (partNumber - 1) * partSize;
125
133
  const end = Math.min(start + partSize, file.size);
126
134
  const chunk = file.slice(start, end);
127
- await this.uploadToPresignedUrl((_b = (_a = tokenRes.data) === null || _a === void 0 ? void 0 : _a.token) !== null && _b !== void 0 ? _b : tokenRes.token, chunk);
128
- return { partNumber, etag: `etag-${partNumber}` };
135
+ const etag = await this.uploadToPresignedUrl((_b = (_a = tokenRes.data) === null || _a === void 0 ? void 0 : _a.token) !== null && _b !== void 0 ? _b : tokenRes.token, chunk);
136
+ return { partNumber, etag };
129
137
  }
130
138
  catch (err) {
131
139
  // Part upload failed; caller can retry
@@ -134,8 +142,10 @@ class FileUploader {
134
142
  }
135
143
  /**
136
144
  * PUT file/blob directly to a presigned URL.
145
+ * Returns the ETag from the response headers for multipart completion.
137
146
  */
138
147
  async uploadToPresignedUrl(presignedUrl, data) {
148
+ var _a, _b;
139
149
  const controller = new AbortController();
140
150
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
141
151
  try {
@@ -150,6 +160,8 @@ class FileUploader {
150
160
  if (!res.ok) {
151
161
  throw new Error(`Upload to storage failed: HTTP ${res.status}`);
152
162
  }
163
+ // 从响应头中提取实际 ETag(用于 multipart complete 验证)
164
+ return (_b = (_a = res.headers.get('ETag')) !== null && _a !== void 0 ? _a : res.headers.get('etag')) !== null && _b !== void 0 ? _b : '';
153
165
  }
154
166
  finally {
155
167
  clearTimeout(timeoutId);
@@ -159,7 +171,7 @@ class FileUploader {
159
171
  * Fetch JSON from API, extracting the data field from the standard response wrapper.
160
172
  */
161
173
  async fetchJson(url, options) {
162
- var _a;
174
+ var _a, _b;
163
175
  const controller = new AbortController();
164
176
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
165
177
  try {
@@ -168,6 +180,14 @@ class FileUploader {
168
180
  throw new Error(`HTTP ${res.status}: ${res.statusText}`);
169
181
  }
170
182
  const json = await res.json();
183
+ // Check for API-level errors in { code, msg, data } envelope
184
+ if (json && typeof json === 'object' && 'code' in json) {
185
+ const code = json.code;
186
+ if (code !== 0 && code !== 200) {
187
+ const msg = (_b = json.msg) !== null && _b !== void 0 ? _b : 'Unknown API error';
188
+ throw new Error(`API error [${code}]: ${msg}`);
189
+ }
190
+ }
171
191
  // Unwrap standard { code, msg, data } envelope
172
192
  if (json && typeof json === 'object' && 'data' in json) {
173
193
  return json;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dofe/file-sdk-web",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "DofeAI unified file uploader SDK for browsers — direct upload, multipart upload with progress, via sso.dofe.ai",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",