@unboundcx/sdk 2.6.2 → 2.6.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/package.json +1 -1
- package/services/storage.js +110 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.3",
|
|
4
4
|
"description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
package/services/storage.js
CHANGED
|
@@ -6,11 +6,13 @@ export class StorageService {
|
|
|
6
6
|
// Private helper method to detect content type from filename
|
|
7
7
|
_getContentType(fileName) {
|
|
8
8
|
if (!fileName) return 'application/octet-stream';
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
try {
|
|
11
11
|
// Try to use mime-types package if available
|
|
12
12
|
const mime = import('mime-types');
|
|
13
|
-
return mime.lookup
|
|
13
|
+
return mime.lookup
|
|
14
|
+
? mime.lookup(fileName) || 'application/octet-stream'
|
|
15
|
+
: this._getFallbackContentType(fileName);
|
|
14
16
|
} catch (error) {
|
|
15
17
|
return this._getFallbackContentType(fileName);
|
|
16
18
|
}
|
|
@@ -93,24 +95,40 @@ export class StorageService {
|
|
|
93
95
|
|
|
94
96
|
// Add file field with proper MIME type detection
|
|
95
97
|
const contentType = this._getContentType(fileName);
|
|
96
|
-
|
|
98
|
+
|
|
97
99
|
body += `--${boundary}${CRLF}`;
|
|
98
|
-
body += `Content-Disposition: form-data; name="files"; filename="${
|
|
100
|
+
body += `Content-Disposition: form-data; name="files"; filename="${
|
|
101
|
+
fileName || 'file'
|
|
102
|
+
}"${CRLF}`;
|
|
99
103
|
body += `Content-Type: ${contentType}${CRLF}${CRLF}`;
|
|
100
104
|
|
|
101
105
|
// Convert to buffers and combine
|
|
102
|
-
const headerBuffer =
|
|
103
|
-
|
|
106
|
+
const headerBuffer =
|
|
107
|
+
typeof Buffer !== 'undefined'
|
|
108
|
+
? Buffer.from(body, 'utf8')
|
|
109
|
+
: new TextEncoder().encode(body);
|
|
110
|
+
const fileBuffer =
|
|
111
|
+
typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(file)
|
|
112
|
+
? file
|
|
113
|
+
: typeof Buffer !== 'undefined'
|
|
114
|
+
? Buffer.from(file)
|
|
115
|
+
: new TextEncoder().encode(file);
|
|
104
116
|
|
|
105
117
|
// Add form fields
|
|
106
|
-
let fieldsBuffer =
|
|
118
|
+
let fieldsBuffer =
|
|
119
|
+
typeof Buffer !== 'undefined' ? Buffer.alloc(0) : new Uint8Array(0);
|
|
107
120
|
for (const [name, value] of formFields) {
|
|
108
121
|
const fieldData = `${CRLF}--${boundary}${CRLF}Content-Disposition: form-data; name="${name}"${CRLF}${CRLF}${value}`;
|
|
109
|
-
const fieldDataBuffer =
|
|
122
|
+
const fieldDataBuffer =
|
|
123
|
+
typeof Buffer !== 'undefined'
|
|
124
|
+
? Buffer.from(fieldData, 'utf8')
|
|
125
|
+
: new TextEncoder().encode(fieldData);
|
|
110
126
|
if (typeof Buffer !== 'undefined') {
|
|
111
127
|
fieldsBuffer = Buffer.concat([fieldsBuffer, fieldDataBuffer]);
|
|
112
128
|
} else {
|
|
113
|
-
const newBuffer = new Uint8Array(
|
|
129
|
+
const newBuffer = new Uint8Array(
|
|
130
|
+
fieldsBuffer.length + fieldDataBuffer.length,
|
|
131
|
+
);
|
|
114
132
|
newBuffer.set(fieldsBuffer);
|
|
115
133
|
newBuffer.set(fieldDataBuffer, fieldsBuffer.length);
|
|
116
134
|
fieldsBuffer = newBuffer;
|
|
@@ -118,27 +136,42 @@ export class StorageService {
|
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
// Final boundary
|
|
121
|
-
const endBoundary =
|
|
139
|
+
const endBoundary =
|
|
140
|
+
typeof Buffer !== 'undefined'
|
|
141
|
+
? Buffer.from(`${CRLF}--${boundary}--${CRLF}`, 'utf8')
|
|
142
|
+
: new TextEncoder().encode(`${CRLF}--${boundary}--${CRLF}`);
|
|
122
143
|
|
|
123
144
|
// Combine all parts
|
|
124
145
|
let formData;
|
|
125
146
|
if (typeof Buffer !== 'undefined') {
|
|
126
|
-
formData = Buffer.concat([
|
|
147
|
+
formData = Buffer.concat([
|
|
148
|
+
headerBuffer,
|
|
149
|
+
fileBuffer,
|
|
150
|
+
fieldsBuffer,
|
|
151
|
+
endBoundary,
|
|
152
|
+
]);
|
|
127
153
|
} else {
|
|
128
|
-
const totalLength =
|
|
154
|
+
const totalLength =
|
|
155
|
+
headerBuffer.length +
|
|
156
|
+
fileBuffer.length +
|
|
157
|
+
fieldsBuffer.length +
|
|
158
|
+
endBoundary.length;
|
|
129
159
|
formData = new Uint8Array(totalLength);
|
|
130
160
|
let offset = 0;
|
|
131
|
-
formData.set(headerBuffer, offset);
|
|
132
|
-
|
|
133
|
-
formData.set(
|
|
161
|
+
formData.set(headerBuffer, offset);
|
|
162
|
+
offset += headerBuffer.length;
|
|
163
|
+
formData.set(fileBuffer, offset);
|
|
164
|
+
offset += fileBuffer.length;
|
|
165
|
+
formData.set(fieldsBuffer, offset);
|
|
166
|
+
offset += fieldsBuffer.length;
|
|
134
167
|
formData.set(endBoundary, offset);
|
|
135
168
|
}
|
|
136
169
|
|
|
137
170
|
return {
|
|
138
171
|
formData,
|
|
139
172
|
headers: {
|
|
140
|
-
'content-type': `multipart/form-data; boundary=${boundary}
|
|
141
|
-
}
|
|
173
|
+
'content-type': `multipart/form-data; boundary=${boundary}`,
|
|
174
|
+
},
|
|
142
175
|
};
|
|
143
176
|
}
|
|
144
177
|
|
|
@@ -147,13 +180,19 @@ export class StorageService {
|
|
|
147
180
|
const formData = new FormData();
|
|
148
181
|
|
|
149
182
|
// Add the file - handle both Buffer and File objects
|
|
150
|
-
if (
|
|
183
|
+
if (
|
|
184
|
+
typeof Buffer !== 'undefined' &&
|
|
185
|
+
Buffer.isBuffer &&
|
|
186
|
+
Buffer.isBuffer(file)
|
|
187
|
+
) {
|
|
151
188
|
const blob = new Blob([file]);
|
|
152
189
|
formData.append('files', blob, fileName || 'file');
|
|
153
190
|
} else if (file instanceof File) {
|
|
154
191
|
formData.append('files', file);
|
|
155
192
|
} else {
|
|
156
|
-
throw new Error(
|
|
193
|
+
throw new Error(
|
|
194
|
+
'In browser environment, file must be a Buffer or File object',
|
|
195
|
+
);
|
|
157
196
|
}
|
|
158
197
|
|
|
159
198
|
// Add other parameters
|
|
@@ -163,12 +202,17 @@ export class StorageService {
|
|
|
163
202
|
|
|
164
203
|
return {
|
|
165
204
|
formData,
|
|
166
|
-
headers: {} // Let browser handle content-type automatically
|
|
205
|
+
headers: {}, // Let browser handle content-type automatically
|
|
167
206
|
};
|
|
168
207
|
}
|
|
169
208
|
|
|
170
209
|
// Shared upload logic
|
|
171
|
-
async _performUpload(
|
|
210
|
+
async _performUpload(
|
|
211
|
+
file,
|
|
212
|
+
fileName,
|
|
213
|
+
formFields,
|
|
214
|
+
endpoint = '/storage/upload',
|
|
215
|
+
) {
|
|
172
216
|
const isNode = typeof window === 'undefined';
|
|
173
217
|
let formData, headers;
|
|
174
218
|
|
|
@@ -256,9 +300,11 @@ export class StorageService {
|
|
|
256
300
|
|
|
257
301
|
// Handle different file input formats and convert to array for processing
|
|
258
302
|
let fileArray = [];
|
|
259
|
-
|
|
303
|
+
|
|
260
304
|
if (typeof window !== 'undefined' && files instanceof FormData) {
|
|
261
|
-
throw new Error(
|
|
305
|
+
throw new Error(
|
|
306
|
+
'FormData input not supported for uploadFiles. Use individual File objects or FileList.',
|
|
307
|
+
);
|
|
262
308
|
} else if (typeof window !== 'undefined' && files instanceof FileList) {
|
|
263
309
|
// Browser FileList
|
|
264
310
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -268,11 +314,16 @@ export class StorageService {
|
|
|
268
314
|
// File array (Node.js or Browser)
|
|
269
315
|
fileArray = files.map((file, index) => ({
|
|
270
316
|
file,
|
|
271
|
-
fileName: file.name || `file-${index}
|
|
317
|
+
fileName: file.name || `file-${index}`,
|
|
272
318
|
}));
|
|
273
|
-
} else if (
|
|
319
|
+
} else if (
|
|
320
|
+
typeof files === 'object' &&
|
|
321
|
+
(files.path || files instanceof File)
|
|
322
|
+
) {
|
|
274
323
|
// Single file object (Node.js) or File (Browser)
|
|
275
|
-
fileArray = [
|
|
324
|
+
fileArray = [
|
|
325
|
+
{ file: files, fileName: files.name || files.path || 'file' },
|
|
326
|
+
];
|
|
276
327
|
} else {
|
|
277
328
|
throw new Error(
|
|
278
329
|
'Invalid files format. Expected FileList, File array, or File object',
|
|
@@ -287,52 +338,56 @@ export class StorageService {
|
|
|
287
338
|
|
|
288
339
|
if (isNode) {
|
|
289
340
|
// Node.js: Create multipart form data manually
|
|
290
|
-
const boundary = `----formdata-${Date.now()}-${Math.random().toString(
|
|
341
|
+
const boundary = `----formdata-${Date.now()}-${Math.random().toString(
|
|
342
|
+
36,
|
|
343
|
+
)}`;
|
|
291
344
|
const CRLF = '\r\n';
|
|
292
|
-
|
|
293
|
-
|
|
345
|
+
const body = '';
|
|
346
|
+
|
|
294
347
|
const bufferParts = [];
|
|
295
|
-
|
|
348
|
+
|
|
296
349
|
// Add all files
|
|
297
350
|
for (const { file, fileName } of fileArray) {
|
|
298
351
|
const contentType = this._getContentType(fileName);
|
|
299
352
|
const fileHeader = `--${boundary}${CRLF}Content-Disposition: form-data; name="files"; filename="${fileName}"${CRLF}Content-Type: ${contentType}${CRLF}${CRLF}`;
|
|
300
|
-
|
|
353
|
+
|
|
301
354
|
bufferParts.push(Buffer.from(fileHeader, 'utf8'));
|
|
302
355
|
bufferParts.push(Buffer.isBuffer(file) ? file : Buffer.from(file));
|
|
303
356
|
bufferParts.push(Buffer.from(CRLF, 'utf8'));
|
|
304
357
|
}
|
|
305
|
-
|
|
358
|
+
|
|
306
359
|
// Add form fields
|
|
307
360
|
const formFields = [];
|
|
308
361
|
if (classification) formFields.push(['classification', classification]);
|
|
309
362
|
if (expireAfter) formFields.push(['expireAfter', expireAfter]);
|
|
310
|
-
if (isPublic !== undefined)
|
|
363
|
+
if (isPublic !== undefined)
|
|
364
|
+
formFields.push(['isPublic', isPublic.toString()]);
|
|
311
365
|
if (metadata) formFields.push(['metadata', JSON.stringify(metadata)]);
|
|
312
|
-
|
|
366
|
+
|
|
313
367
|
for (const [name, value] of formFields) {
|
|
314
368
|
const fieldData = `--${boundary}${CRLF}Content-Disposition: form-data; name="${name}"${CRLF}${CRLF}${value}${CRLF}`;
|
|
315
369
|
bufferParts.push(Buffer.from(fieldData, 'utf8'));
|
|
316
370
|
}
|
|
317
|
-
|
|
371
|
+
|
|
318
372
|
// Final boundary
|
|
319
373
|
bufferParts.push(Buffer.from(`--${boundary}--${CRLF}`, 'utf8'));
|
|
320
|
-
|
|
374
|
+
|
|
321
375
|
formData = Buffer.concat(bufferParts);
|
|
322
376
|
headers['content-type'] = `multipart/form-data; boundary=${boundary}`;
|
|
323
377
|
} else {
|
|
324
378
|
// Browser: Use native FormData
|
|
325
379
|
formData = new FormData();
|
|
326
|
-
|
|
380
|
+
|
|
327
381
|
// Add all files
|
|
328
382
|
for (const { file, fileName } of fileArray) {
|
|
329
383
|
formData.append('files', file, fileName);
|
|
330
384
|
}
|
|
331
|
-
|
|
385
|
+
|
|
332
386
|
// Add optional parameters
|
|
333
387
|
if (classification) formData.append('classification', classification);
|
|
334
388
|
if (expireAfter) formData.append('expireAfter', expireAfter);
|
|
335
|
-
if (isPublic !== undefined)
|
|
389
|
+
if (isPublic !== undefined)
|
|
390
|
+
formData.append('isPublic', isPublic.toString());
|
|
336
391
|
if (metadata) formData.append('metadata', JSON.stringify(metadata));
|
|
337
392
|
}
|
|
338
393
|
|
|
@@ -344,12 +399,13 @@ export class StorageService {
|
|
|
344
399
|
return await this.sdk._fetch('/storage/upload', 'POST', params, true);
|
|
345
400
|
}
|
|
346
401
|
|
|
347
|
-
async getFile(storageId, download = false) {
|
|
402
|
+
async getFile(storageId, path, download = false) {
|
|
348
403
|
this.sdk.validateParams(
|
|
349
|
-
{ storageId },
|
|
404
|
+
{ storageId, path },
|
|
350
405
|
{
|
|
351
|
-
storageId: { type: 'string', required:
|
|
406
|
+
storageId: { type: 'string', required: false },
|
|
352
407
|
download: { type: 'boolean', required: false },
|
|
408
|
+
path: { type: 'string', require: false },
|
|
353
409
|
},
|
|
354
410
|
);
|
|
355
411
|
|
|
@@ -358,11 +414,12 @@ export class StorageService {
|
|
|
358
414
|
params.query = { download: 'true' };
|
|
359
415
|
}
|
|
360
416
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
'
|
|
364
|
-
|
|
365
|
-
|
|
417
|
+
let url = `/storage/${storageId}`;
|
|
418
|
+
if (path) {
|
|
419
|
+
url += `/storage/${path.startsWith('/') ? path.slice(1) : path}`;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const result = await this.sdk._fetch(url, 'GET', params);
|
|
366
423
|
return result;
|
|
367
424
|
}
|
|
368
425
|
|
|
@@ -432,7 +489,12 @@ export class StorageService {
|
|
|
432
489
|
formFields.push(['classification', classification]);
|
|
433
490
|
|
|
434
491
|
// Use the correct profile image endpoint with proper FormData
|
|
435
|
-
return this._performUpload(
|
|
492
|
+
return this._performUpload(
|
|
493
|
+
file,
|
|
494
|
+
fileName || 'profile-image.jpg',
|
|
495
|
+
formFields,
|
|
496
|
+
'/storage/upload-profile-image',
|
|
497
|
+
);
|
|
436
498
|
}
|
|
437
499
|
|
|
438
500
|
async getFileInfo(storageId) {
|