@unboundcx/sdk 1.0.3 → 1.0.6

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 +137 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
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",
@@ -11,6 +11,7 @@ export class StorageService {
11
11
  isPublic = false,
12
12
  country = 'US',
13
13
  expireAfter,
14
+ relatedId,
14
15
  createAccessKey = false,
15
16
  accessKeyExpiresIn,
16
17
  }) {
@@ -23,6 +24,7 @@ export class StorageService {
23
24
  isPublic,
24
25
  country,
25
26
  expireAfter,
27
+ relatedId,
26
28
  createAccessKey,
27
29
  accessKeyExpiresIn,
28
30
  },
@@ -34,6 +36,7 @@ export class StorageService {
34
36
  isPublic: { type: 'boolean', required: false },
35
37
  country: { type: 'string', required: false },
36
38
  expireAfter: { type: 'string', required: false },
39
+ relatedId: { type: 'string', required: false },
37
40
  createAccessKey: { type: 'boolean', required: false },
38
41
  accessKeyExpiresIn: { type: 'string', required: false },
39
42
  },
@@ -46,7 +49,9 @@ export class StorageService {
46
49
  if (isNode) {
47
50
  // Node.js environment - use direct buffer approach
48
51
  // Create a simple body with the file buffer and metadata
49
- const boundary = `----formdata-${Date.now()}-${Math.random().toString(36)}`;
52
+ const boundary = `----formdata-${Date.now()}-${Math.random().toString(
53
+ 36,
54
+ )}`;
50
55
  const CRLF = '\r\n';
51
56
  let body = '';
52
57
 
@@ -128,18 +133,24 @@ export class StorageService {
128
133
  }
129
134
 
130
135
  body += `--${boundary}${CRLF}`;
131
- body += `Content-Disposition: form-data; name="files"; filename="${fileName || 'file'}"${CRLF}`;
136
+ body += `Content-Disposition: form-data; name="files"; filename="${
137
+ fileName || 'file'
138
+ }"${CRLF}`;
132
139
  body += `Content-Type: ${contentType}${CRLF}${CRLF}`;
133
140
 
134
141
  // Add other form fields
135
142
  const formFields = [];
136
143
  if (classification) formFields.push(['classification', classification]);
137
144
  if (folder) formFields.push(['folder', folder]);
138
- if (isPublic !== undefined) formFields.push(['isPublic', isPublic.toString()]);
145
+ if (isPublic !== undefined)
146
+ formFields.push(['isPublic', isPublic.toString()]);
139
147
  if (country) formFields.push(['country', country]);
140
148
  if (expireAfter) formFields.push(['expireAfter', expireAfter]);
141
- if (createAccessKey !== undefined) formFields.push(['createAccessKey', createAccessKey.toString()]);
142
- if (accessKeyExpiresIn) formFields.push(['accessKeyExpiresIn', accessKeyExpiresIn]);
149
+ if (relatedId) formFields.push(['relatedId', relatedId]);
150
+ if (createAccessKey !== undefined)
151
+ formFields.push(['createAccessKey', createAccessKey.toString()]);
152
+ if (accessKeyExpiresIn)
153
+ formFields.push(['accessKeyExpiresIn', accessKeyExpiresIn]);
143
154
 
144
155
  // Convert to buffers and combine
145
156
  const headerBuffer = Buffer.from(body, 'utf8');
@@ -149,14 +160,22 @@ export class StorageService {
149
160
  let fieldsBuffer = Buffer.alloc(0);
150
161
  for (const [name, value] of formFields) {
151
162
  const fieldData = `${CRLF}--${boundary}${CRLF}Content-Disposition: form-data; name="${name}"${CRLF}${CRLF}${value}`;
152
- fieldsBuffer = Buffer.concat([fieldsBuffer, Buffer.from(fieldData, 'utf8')]);
163
+ fieldsBuffer = Buffer.concat([
164
+ fieldsBuffer,
165
+ Buffer.from(fieldData, 'utf8'),
166
+ ]);
153
167
  }
154
168
 
155
169
  // Final boundary
156
170
  const endBoundary = Buffer.from(`${CRLF}--${boundary}--${CRLF}`, 'utf8');
157
171
 
158
172
  // Combine all parts
159
- formData = Buffer.concat([headerBuffer, fileBuffer, fieldsBuffer, endBoundary]);
173
+ formData = Buffer.concat([
174
+ headerBuffer,
175
+ fileBuffer,
176
+ fieldsBuffer,
177
+ endBoundary,
178
+ ]);
160
179
 
161
180
  // Set proper Content-Type header
162
181
  headers['content-type'] = `multipart/form-data; boundary=${boundary}`;
@@ -171,18 +190,24 @@ export class StorageService {
171
190
  } else if (file instanceof File) {
172
191
  formData.append('files', file);
173
192
  } else {
174
- 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
+ );
175
196
  }
176
197
 
177
198
  // Add other parameters
178
199
  if (classification) formData.append('classification', classification);
179
200
  if (folder) formData.append('folder', folder);
180
- if (isPublic !== undefined) formData.append('isPublic', isPublic.toString());
201
+ if (isPublic !== undefined)
202
+ formData.append('isPublic', isPublic.toString());
181
203
  if (country) formData.append('country', country);
182
204
  if (expireAfter) formData.append('expireAfter', expireAfter);
183
- if (createAccessKey !== undefined) formData.append('createAccessKey', createAccessKey.toString());
184
- if (accessKeyExpiresIn) formData.append('accessKeyExpiresIn', accessKeyExpiresIn);
185
-
205
+ if (relatedId) formData.append('relatedId', relatedId);
206
+ if (createAccessKey !== undefined)
207
+ formData.append('createAccessKey', createAccessKey.toString());
208
+ if (accessKeyExpiresIn)
209
+ formData.append('accessKeyExpiresIn', accessKeyExpiresIn);
210
+
186
211
  // Don't set Content-Type - let browser handle it automatically
187
212
  }
188
213
 
@@ -191,7 +216,12 @@ export class StorageService {
191
216
  headers,
192
217
  };
193
218
 
194
- const result = await this.sdk._fetch('/storage/upload', 'POST', params, true);
219
+ const result = await this.sdk._fetch(
220
+ '/storage/upload',
221
+ 'POST',
222
+ params,
223
+ true,
224
+ );
195
225
  return result;
196
226
  }
197
227
 
@@ -247,7 +277,12 @@ export class StorageService {
247
277
  // Remove Content-Type to let FormData set it properly
248
278
  delete params.headers['Content-Type'];
249
279
 
250
- const result = await this.sdk._fetch('/storage/upload', 'POST', params, true);
280
+ const result = await this.sdk._fetch(
281
+ '/storage/upload',
282
+ 'POST',
283
+ params,
284
+ true,
285
+ );
251
286
  return result;
252
287
  }
253
288
 
@@ -284,9 +319,9 @@ export class StorageService {
284
319
 
285
320
  let url;
286
321
  if (this.sdk.environment === 'node') {
287
- url = `${this.sdk.baseURL}/storage/file/${storageId}`;
322
+ url = `${this.sdk.baseURL}/storage/${storageId}`;
288
323
  } else {
289
- url = `${this.sdk.fullUrl}/storage/file/${storageId}`;
324
+ url = `${this.sdk.fullUrl}/storage/${storageId}`;
290
325
  }
291
326
 
292
327
  if (download) {
@@ -316,6 +351,92 @@ export class StorageService {
316
351
  return result;
317
352
  }
318
353
 
354
+ async uploadProfileImage({
355
+ file,
356
+ classification = 'user_images',
357
+ fileName,
358
+ }) {
359
+ this.sdk.validateParams(
360
+ { file, classification },
361
+ {
362
+ file: { type: 'object', required: true },
363
+ classification: { type: 'string', required: true },
364
+ fileName: { type: 'string', required: false },
365
+ },
366
+ );
367
+
368
+ // Validate classification
369
+ const validClassifications = ['user_images', 'account_logo'];
370
+ if (!validClassifications.includes(classification)) {
371
+ throw new Error('Invalid classification. Must be "user_images" or "account_logo"');
372
+ }
373
+
374
+ const isNode = typeof window === 'undefined';
375
+ let formData;
376
+ const headers = {};
377
+
378
+ if (isNode) {
379
+ // Node.js environment
380
+ const boundary = `----formdata-${Date.now()}-${Math.random().toString(36)}`;
381
+ const CRLF = '\r\n';
382
+ let body = '';
383
+
384
+ // Add file field
385
+ let contentType = 'application/octet-stream';
386
+ if (fileName) {
387
+ const ext = fileName.split('.').pop().toLowerCase();
388
+ const imageTypes = {
389
+ jpg: 'image/jpeg',
390
+ jpeg: 'image/jpeg',
391
+ png: 'image/png',
392
+ gif: 'image/gif',
393
+ webp: 'image/webp',
394
+ };
395
+ contentType = imageTypes[ext] || 'image/jpeg';
396
+ }
397
+
398
+ body += `--${boundary}${CRLF}`;
399
+ body += `Content-Disposition: form-data; name="files"; filename="${fileName || 'profile-image.jpg'}"${CRLF}`;
400
+ body += `Content-Type: ${contentType}${CRLF}${CRLF}`;
401
+
402
+ const headerBuffer = Buffer.from(body, 'utf8');
403
+ const fileBuffer = Buffer.isBuffer(file) ? file : Buffer.from(file);
404
+
405
+ // Add classification field
406
+ const classificationField = `${CRLF}--${boundary}${CRLF}Content-Disposition: form-data; name="classification"${CRLF}${CRLF}${classification}`;
407
+ const fieldsBuffer = Buffer.from(classificationField, 'utf8');
408
+
409
+ // Final boundary
410
+ const endBoundary = Buffer.from(`${CRLF}--${boundary}--${CRLF}`, 'utf8');
411
+
412
+ // Combine all parts
413
+ formData = Buffer.concat([headerBuffer, fileBuffer, fieldsBuffer, endBoundary]);
414
+ headers['content-type'] = `multipart/form-data; boundary=${boundary}`;
415
+ } else {
416
+ // Browser environment
417
+ formData = new FormData();
418
+
419
+ if (Buffer.isBuffer(file)) {
420
+ const blob = new Blob([file]);
421
+ formData.append('files', blob, fileName || 'profile-image.jpg');
422
+ } else if (file instanceof File) {
423
+ formData.append('files', file);
424
+ } else {
425
+ throw new Error('In browser environment, file must be a Buffer or File object');
426
+ }
427
+
428
+ formData.append('classification', classification);
429
+ }
430
+
431
+ const params = {
432
+ body: formData,
433
+ headers,
434
+ };
435
+
436
+ const result = await this.sdk._fetch('/storage/upload-profile-image', 'POST', params, true);
437
+ return result;
438
+ }
439
+
319
440
  async getFileInfo(storageId) {
320
441
  this.sdk.validateParams(
321
442
  { storageId },