@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. 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.2",
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",
@@ -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 ? mime.lookup(fileName) || 'application/octet-stream' : this._getFallbackContentType(fileName);
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="${fileName || 'file'}"${CRLF}`;
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 = (typeof Buffer !== 'undefined') ? Buffer.from(body, 'utf8') : new TextEncoder().encode(body);
103
- const fileBuffer = (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(file)) ? file : ((typeof Buffer !== 'undefined') ? Buffer.from(file) : new TextEncoder().encode(file));
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 = (typeof Buffer !== 'undefined') ? Buffer.alloc(0) : new Uint8Array(0);
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 = (typeof Buffer !== 'undefined') ? Buffer.from(fieldData, 'utf8') : new TextEncoder().encode(fieldData);
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(fieldsBuffer.length + fieldDataBuffer.length);
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 = (typeof Buffer !== 'undefined') ? Buffer.from(`${CRLF}--${boundary}--${CRLF}`, 'utf8') : new TextEncoder().encode(`${CRLF}--${boundary}--${CRLF}`);
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([headerBuffer, fileBuffer, fieldsBuffer, endBoundary]);
147
+ formData = Buffer.concat([
148
+ headerBuffer,
149
+ fileBuffer,
150
+ fieldsBuffer,
151
+ endBoundary,
152
+ ]);
127
153
  } else {
128
- const totalLength = headerBuffer.length + fileBuffer.length + fieldsBuffer.length + endBoundary.length;
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); offset += headerBuffer.length;
132
- formData.set(fileBuffer, offset); offset += fileBuffer.length;
133
- formData.set(fieldsBuffer, offset); offset += fieldsBuffer.length;
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 ((typeof Buffer !== 'undefined') && Buffer.isBuffer && Buffer.isBuffer(file)) {
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('In browser environment, file must be a Buffer or File object');
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(file, fileName, formFields, endpoint = '/storage/upload') {
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('FormData input not supported for uploadFiles. Use individual File objects or FileList.');
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 (typeof files === 'object' && (files.path || files instanceof File)) {
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 = [{ file: files, fileName: files.name || files.path || 'file' }];
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(36)}`;
341
+ const boundary = `----formdata-${Date.now()}-${Math.random().toString(
342
+ 36,
343
+ )}`;
291
344
  const CRLF = '\r\n';
292
- let body = '';
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) formFields.push(['isPublic', isPublic.toString()]);
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) formData.append('isPublic', isPublic.toString());
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: true },
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
- const result = await this.sdk._fetch(
362
- `/storage/file/${storageId}`,
363
- 'GET',
364
- params,
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(file, fileName || 'profile-image.jpg', formFields, '/storage/upload-profile-image');
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) {