@uploadcare/upload-client 3.0.0 → 3.1.2-alpha.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.
@@ -117,34 +117,104 @@ function identity(obj) {
117
117
  return obj;
118
118
  }
119
119
 
120
+ const getFileOptions = ({ name }) => name ? [name] : [];
120
121
  const transformFile = identity;
121
122
  var getFormData = () => new FormData();
122
123
 
123
- const isFileTuple = (tuple) => tuple[0] === 'file';
124
- function buildFormData(body) {
125
- const formData = getFormData();
126
- for (const tuple of body) {
127
- if (Array.isArray(tuple[1])) {
128
- // refactor this
129
- tuple[1].forEach((val) => val && formData.append(tuple[0] + '[]', `${val}`));
130
- }
131
- else if (isFileTuple(tuple)) {
132
- const name = tuple[2];
133
- const file = transformFile(tuple[1]); // lgtm[js/superfluous-trailing-arguments]
134
- formData.append(tuple[0], file, name);
135
- }
136
- else if (tuple[1] != null) {
137
- formData.append(tuple[0], `${tuple[1]}`);
124
+ /**
125
+ * FileData type guard.
126
+ */
127
+ const isFileData = (data) => {
128
+ return (data !== undefined &&
129
+ ((typeof Blob !== 'undefined' && data instanceof Blob) ||
130
+ (typeof File !== 'undefined' && data instanceof File) ||
131
+ (typeof Buffer !== 'undefined' && data instanceof Buffer)));
132
+ };
133
+ /**
134
+ * Uuid type guard.
135
+ */
136
+ const isUuid = (data) => {
137
+ const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
138
+ const regExp = new RegExp(UUID_REGEX);
139
+ return !isFileData(data) && regExp.test(data);
140
+ };
141
+ /**
142
+ * Url type guard.
143
+ *
144
+ * @param {NodeFile | BrowserFile | Url | Uuid} data
145
+ */
146
+ const isUrl = (data) => {
147
+ const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
148
+ const regExp = new RegExp(URL_REGEX);
149
+ return !isFileData(data) && regExp.test(data);
150
+ };
151
+
152
+ const isSimpleValue = (value) => {
153
+ return (typeof value === 'string' ||
154
+ typeof value === 'number' ||
155
+ typeof value === 'undefined');
156
+ };
157
+ const isObjectValue = (value) => {
158
+ return !!value && typeof value === 'object' && !Array.isArray(value);
159
+ };
160
+ const isFileValue = (value) => !!value &&
161
+ typeof value === 'object' &&
162
+ 'data' in value &&
163
+ isFileData(value.data);
164
+ function collectParams(params, inputKey, inputValue) {
165
+ if (isFileValue(inputValue)) {
166
+ const { name, contentType } = inputValue;
167
+ const file = transformFile(inputValue.data); // lgtm [js/superfluous-trailing-arguments]
168
+ const options = getFileOptions({ name, contentType });
169
+ params.push([inputKey, file, ...options]);
170
+ }
171
+ else if (isObjectValue(inputValue)) {
172
+ for (const [key, value] of Object.entries(inputValue)) {
173
+ if (typeof value !== 'undefined') {
174
+ params.push([`${inputKey}[${key}]`, String(value)]);
175
+ }
138
176
  }
139
177
  }
178
+ else if (isSimpleValue(inputValue) && inputValue) {
179
+ params.push([inputKey, inputValue.toString()]);
180
+ }
181
+ }
182
+ function getFormDataParams(options) {
183
+ const params = [];
184
+ for (const [key, value] of Object.entries(options)) {
185
+ collectParams(params, key, value);
186
+ }
187
+ return params;
188
+ }
189
+ function buildFormData(options) {
190
+ const formData = getFormData();
191
+ const paramsList = getFormDataParams(options);
192
+ for (const params of paramsList) {
193
+ const [key, value, ...options] = params;
194
+ // node form-data has another signature for append
195
+ formData.append(key, value, ...options);
196
+ }
140
197
  return formData;
141
198
  }
142
199
 
143
200
  const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
201
+ // TODO: generalize value transforming logic and use it here and inside `buildFormData`
144
202
  const createQuery = (query) => Object.entries(query)
145
- .reduce((params, [key, value]) => params.concat(Array.isArray(value)
146
- ? value.map((value) => serializePair(`${key}[]`, value))
147
- : serializePair(key, value)), [])
203
+ .reduce((params, [key, value]) => {
204
+ let param;
205
+ if (typeof value === 'object' && !Array.isArray(value)) {
206
+ param = Object.entries(value)
207
+ .filter((entry) => typeof entry[1] !== 'undefined')
208
+ .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
209
+ }
210
+ else if (Array.isArray(value)) {
211
+ param = value.map((val) => serializePair(`${key}[]`, val));
212
+ }
213
+ else {
214
+ param = serializePair(key, value);
215
+ }
216
+ return params.concat(param);
217
+ }, [])
148
218
  .filter((x) => !!x)
149
219
  .join('&');
150
220
  const getUrl = (base, path, query) => [
@@ -176,7 +246,7 @@ const defaultSettings = {
176
246
  const defaultContentType = 'application/octet-stream';
177
247
  const defaultFilename = 'original';
178
248
 
179
- var version = '3.0.0';
249
+ var version = '3.1.1';
180
250
 
181
251
  /**
182
252
  * Returns User Agent based on version and settings.
@@ -276,11 +346,15 @@ function retryIfThrottled(fn, retryThrottledMaxTimes) {
276
346
  }));
277
347
  }
278
348
 
349
+ function getStoreValue(store) {
350
+ return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
351
+ }
352
+
279
353
  /**
280
354
  * Performs file uploading request to Uploadcare Upload API.
281
355
  * Can be canceled and has progress.
282
356
  */
283
- function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
357
+ function base(file, { publicKey, fileName, contentType, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
284
358
  return retryIfThrottled(() => {
285
359
  var _a;
286
360
  return request({
@@ -291,17 +365,19 @@ function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, se
291
365
  headers: {
292
366
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
293
367
  },
294
- data: buildFormData([
295
- ['file', file, (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename],
296
- ['UPLOADCARE_PUB_KEY', publicKey],
297
- [
298
- 'UPLOADCARE_STORE',
299
- typeof store === 'undefined' ? 'auto' : store ? 1 : 0
300
- ],
301
- ['signature', secureSignature],
302
- ['expire', secureExpire],
303
- ['source', source]
304
- ]),
368
+ data: buildFormData({
369
+ file: {
370
+ data: file,
371
+ name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename,
372
+ contentType
373
+ },
374
+ UPLOADCARE_PUB_KEY: publicKey,
375
+ UPLOADCARE_STORE: getStoreValue(store),
376
+ signature: secureSignature,
377
+ expire: secureExpire,
378
+ source: source,
379
+ metadata
380
+ }),
305
381
  signal,
306
382
  onProgress
307
383
  }).then(({ data, headers, request }) => {
@@ -324,7 +400,7 @@ var TypeEnum;
324
400
  /**
325
401
  * Uploading files from URL.
326
402
  */
327
- function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
403
+ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
328
404
  return retryIfThrottled(() => request({
329
405
  method: 'POST',
330
406
  headers: {
@@ -334,13 +410,14 @@ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, stor
334
410
  jsonerrors: 1,
335
411
  pub_key: publicKey,
336
412
  source_url: sourceUrl,
337
- store: typeof store === 'undefined' ? 'auto' : store ? 1 : undefined,
413
+ store: getStoreValue(store),
338
414
  filename: fileName,
339
415
  check_URL_duplicates: checkForUrlDuplicates ? 1 : undefined,
340
416
  save_URL_duplicates: saveUrlForRecurrentUploads ? 1 : undefined,
341
417
  signature: secureSignature,
342
418
  expire: secureExpire,
343
- source: source
419
+ source: source,
420
+ metadata
344
421
  }),
345
422
  signal
346
423
  }).then(({ data, headers, request }) => {
@@ -483,24 +560,25 @@ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, sour
483
560
  /**
484
561
  * Start multipart uploading.
485
562
  */
486
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
563
+ function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
487
564
  return retryIfThrottled(() => request({
488
565
  method: 'POST',
489
566
  url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
490
567
  headers: {
491
568
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
492
569
  },
493
- data: buildFormData([
494
- ['filename', fileName !== null && fileName !== void 0 ? fileName : defaultFilename],
495
- ['size', size],
496
- ['content_type', contentType !== null && contentType !== void 0 ? contentType : defaultContentType],
497
- ['part_size', multipartChunkSize],
498
- ['UPLOADCARE_STORE', store ? '' : 'auto'],
499
- ['UPLOADCARE_PUB_KEY', publicKey],
500
- ['signature', secureSignature],
501
- ['expire', secureExpire],
502
- ['source', source]
503
- ]),
570
+ data: buildFormData({
571
+ filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
572
+ size: size,
573
+ content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
574
+ part_size: multipartChunkSize,
575
+ UPLOADCARE_STORE: getStoreValue(store),
576
+ UPLOADCARE_PUB_KEY: publicKey,
577
+ signature: secureSignature,
578
+ expire: secureExpire,
579
+ source: source,
580
+ metadata
581
+ }),
504
582
  signal
505
583
  }).then(({ data, headers, request }) => {
506
584
  const response = camelizeKeys(JSON.parse(data));
@@ -549,11 +627,11 @@ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL,
549
627
  headers: {
550
628
  'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
551
629
  },
552
- data: buildFormData([
553
- ['uuid', uuid],
554
- ['UPLOADCARE_PUB_KEY', publicKey],
555
- ['source', source]
556
- ]),
630
+ data: buildFormData({
631
+ uuid: uuid,
632
+ UPLOADCARE_PUB_KEY: publicKey,
633
+ source: source
634
+ }),
557
635
  signal
558
636
  }).then(({ data, headers, request }) => {
559
637
  const response = camelizeKeys(JSON.parse(data));
@@ -579,6 +657,8 @@ class UploadcareFile {
579
657
  this.originalFilename = null;
580
658
  this.imageInfo = null;
581
659
  this.videoInfo = null;
660
+ this.contentInfo = null;
661
+ this.metadata = null;
582
662
  const { uuid, s3Bucket } = fileInfo;
583
663
  const urlBase = s3Bucket
584
664
  ? `https://${s3Bucket}.s3.amazonaws.com/${uuid}/${fileInfo.filename}`
@@ -598,6 +678,8 @@ class UploadcareFile {
598
678
  this.originalFilename = fileInfo.originalFilename;
599
679
  this.imageInfo = camelizeKeys(fileInfo.imageInfo);
600
680
  this.videoInfo = camelizeKeys(fileInfo.videoInfo);
681
+ this.contentInfo = camelizeKeys(fileInfo.contentInfo);
682
+ this.metadata = fileInfo.metadata || null;
601
683
  }
602
684
  }
603
685
 
@@ -649,10 +731,11 @@ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent,
649
731
  });
650
732
  }
651
733
 
652
- const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
734
+ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, contentType, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN, metadata }) => {
653
735
  return base(file, {
654
736
  publicKey,
655
737
  fileName,
738
+ contentType,
656
739
  baseURL,
657
740
  secureSignature,
658
741
  secureExpire,
@@ -662,7 +745,8 @@ const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature,
662
745
  source,
663
746
  integration,
664
747
  userAgent,
665
- retryThrottledRequestMaxTimes
748
+ retryThrottledRequestMaxTimes,
749
+ metadata
666
750
  })
667
751
  .then(({ file }) => {
668
752
  return isReadyPoll({
@@ -965,7 +1049,7 @@ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((
965
1049
  }
966
1050
  });
967
1051
  });
968
- const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, pusherKey = defaultSettings.pusherKey }) => Promise.resolve(preconnect(pusherKey))
1052
+ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, pusherKey = defaultSettings.pusherKey, metadata }) => Promise.resolve(preconnect(pusherKey))
969
1053
  .then(() => fromUrl(sourceUrl, {
970
1054
  publicKey,
971
1055
  fileName,
@@ -979,7 +1063,8 @@ const uploadFromUrl = (sourceUrl, { publicKey, fileName, baseURL, baseCDN, check
979
1063
  source,
980
1064
  integration,
981
1065
  userAgent,
982
- retryThrottledRequestMaxTimes
1066
+ retryThrottledRequestMaxTimes,
1067
+ metadata
983
1068
  }))
984
1069
  .catch((error) => {
985
1070
  const pusher = getPusher(pusherKey);
@@ -1050,34 +1135,6 @@ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProg
1050
1135
  });
1051
1136
  };
1052
1137
 
1053
- /**
1054
- * FileData type guard.
1055
- */
1056
- const isFileData = (data) => {
1057
- return (data !== undefined &&
1058
- ((typeof Blob !== 'undefined' && data instanceof Blob) ||
1059
- (typeof File !== 'undefined' && data instanceof File) ||
1060
- (typeof Buffer !== 'undefined' && data instanceof Buffer)));
1061
- };
1062
- /**
1063
- * Uuid type guard.
1064
- */
1065
- const isUuid = (data) => {
1066
- const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
1067
- const regExp = new RegExp(UUID_REGEX);
1068
- return !isFileData(data) && regExp.test(data);
1069
- };
1070
- /**
1071
- * Url type guard.
1072
- *
1073
- * @param {NodeFile | BrowserFile | Url | Uuid} data
1074
- */
1075
- const isUrl = (data) => {
1076
- const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
1077
- const regExp = new RegExp(URL_REGEX);
1078
- return !isFileData(data) && regExp.test(data);
1079
- };
1080
-
1081
1138
  /**
1082
1139
  * Get file size.
1083
1140
  */
@@ -1147,7 +1204,7 @@ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integr
1147
1204
  }
1148
1205
  throw error;
1149
1206
  }));
1150
- const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, multipartMaxAttempts = defaultSettings.multipartMaxAttempts, baseCDN }) => {
1207
+ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, maxConcurrentRequests = defaultSettings.maxConcurrentRequests, multipartMaxAttempts = defaultSettings.multipartMaxAttempts, baseCDN, metadata }) => {
1151
1208
  const size = fileSize || getFileSize(file);
1152
1209
  let progressValues;
1153
1210
  const createProgressHandler = (totalChunks, chunkIdx) => {
@@ -1180,7 +1237,8 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1180
1237
  source,
1181
1238
  integration,
1182
1239
  userAgent,
1183
- retryThrottledRequestMaxTimes
1240
+ retryThrottledRequestMaxTimes,
1241
+ metadata
1184
1242
  })
1185
1243
  .then(({ uuid, parts }) => {
1186
1244
  const getChunk = prepareChunks(file, size, multipartChunkSize);
@@ -1227,10 +1285,10 @@ const uploadMultipart = (file, { publicKey, fileName, fileSize, baseURL, secureS
1227
1285
  /**
1228
1286
  * Uploads file from provided data.
1229
1287
  */
1230
- function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize, multipartMaxAttempts, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey }) {
1288
+ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartMinFileSize, multipartChunkSize, multipartMaxAttempts, maxConcurrentRequests, baseCDN = defaultSettings.baseCDN, checkForUrlDuplicates, saveUrlForRecurrentUploads, pusherKey, metadata }) {
1231
1289
  if (isFileData(data)) {
1232
1290
  const fileSize = getFileSize(data);
1233
- if (isMultipart(fileSize)) {
1291
+ if (isMultipart(fileSize, multipartMinFileSize)) {
1234
1292
  return uploadMultipart(data, {
1235
1293
  publicKey,
1236
1294
  contentType,
@@ -1248,12 +1306,14 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1248
1306
  userAgent,
1249
1307
  maxConcurrentRequests,
1250
1308
  retryThrottledRequestMaxTimes,
1251
- baseCDN
1309
+ baseCDN,
1310
+ metadata
1252
1311
  });
1253
1312
  }
1254
1313
  return uploadFromObject(data, {
1255
1314
  publicKey,
1256
1315
  fileName,
1316
+ contentType,
1257
1317
  baseURL,
1258
1318
  secureSignature,
1259
1319
  secureExpire,
@@ -1264,7 +1324,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1264
1324
  integration,
1265
1325
  userAgent,
1266
1326
  retryThrottledRequestMaxTimes,
1267
- baseCDN
1327
+ baseCDN,
1328
+ metadata
1268
1329
  });
1269
1330
  }
1270
1331
  if (isUrl(data)) {
@@ -1284,7 +1345,8 @@ function uploadFile(data, { publicKey, fileName, baseURL = defaultSettings.baseU
1284
1345
  integration,
1285
1346
  userAgent,
1286
1347
  retryThrottledRequestMaxTimes,
1287
- pusherKey
1348
+ pusherKey,
1349
+ metadata
1288
1350
  });
1289
1351
  }
1290
1352
  if (isUuid(data)) {