@uploadcare/upload-client 3.1.0 → 4.0.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.
@@ -3,1555 +3,1560 @@ import https from 'https';
3
3
  import { parse } from 'url';
4
4
  import { Transform, Readable } from 'stream';
5
5
  import NodeFormData from 'form-data';
6
- import { AbortController } from 'abort-controller';
7
- export { AbortController } from 'abort-controller';
8
6
  import WebSocket from 'ws';
9
7
 
10
- class UploadClientError extends Error {
11
- constructor(message, code, request, response, headers) {
12
- super();
13
- this.name = 'UploadClientError';
14
- this.message = message;
15
- this.code = code;
16
- this.request = request;
17
- this.response = response;
18
- this.headers = headers;
19
- Object.setPrototypeOf(this, UploadClientError.prototype);
20
- }
21
- }
22
- const cancelError = (message = 'Request canceled') => {
23
- const error = new UploadClientError(message);
24
- error.isCancel = true;
25
- return error;
8
+ class UploadClientError extends Error {
9
+ constructor(message, code, request, response, headers) {
10
+ super();
11
+ this.name = 'UploadClientError';
12
+ this.message = message;
13
+ this.code = code;
14
+ this.request = request;
15
+ this.response = response;
16
+ this.headers = headers;
17
+ Object.setPrototypeOf(this, UploadClientError.prototype);
18
+ }
19
+ }
20
+ const cancelError = (message = 'Request canceled') => {
21
+ const error = new UploadClientError(message);
22
+ error.isCancel = true;
23
+ return error;
26
24
  };
27
25
 
28
- const onCancel = (signal, callback) => {
29
- if (signal) {
30
- if (signal.aborted) {
31
- Promise.resolve().then(callback);
32
- }
33
- else {
34
- signal.addEventListener('abort', () => callback(), { once: true });
35
- }
36
- }
26
+ const onCancel = (signal, callback) => {
27
+ if (signal) {
28
+ if (signal.aborted) {
29
+ Promise.resolve().then(callback);
30
+ }
31
+ else {
32
+ signal.addEventListener('abort', () => callback(), { once: true });
33
+ }
34
+ }
37
35
  };
38
36
 
39
- // ProgressEmitter is a simple PassThrough-style transform stream which keeps
40
- // track of the number of bytes which have been piped through it and will
41
- // invoke the `onprogress` function whenever new number are available.
42
- class ProgressEmitter extends Transform {
43
- constructor(onProgress, size) {
44
- super();
45
- this._onprogress = onProgress;
46
- this._position = 0;
47
- this.size = size;
48
- }
49
- _transform(chunk, encoding, callback) {
50
- this._position += chunk.length;
51
- this._onprogress({
52
- isComputable: true,
53
- value: this._position / this.size
54
- });
55
- callback(null, chunk);
56
- }
57
- }
58
- const getLength = (formData) => new Promise((resolve, reject) => {
59
- formData.getLength((error, length) => {
60
- if (error)
61
- reject(error);
62
- else
63
- resolve(length);
64
- });
65
- });
66
- function isFormData(formData) {
67
- if (formData && formData.toString() === '[object FormData]') {
68
- return true;
69
- }
70
- return false;
71
- }
72
- function isReadable(data, isFormData) {
73
- if (data && (data instanceof Readable || isFormData)) {
74
- return true;
75
- }
76
- return false;
77
- }
78
- const request = (params) => {
79
- const { method = 'GET', url, data, headers = {}, signal, onProgress } = params;
80
- return Promise.resolve()
81
- .then(() => {
82
- if (isFormData(data)) {
83
- return getLength(data);
84
- }
85
- else {
86
- return undefined;
87
- }
88
- })
89
- .then((length) => new Promise((resolve, reject) => {
90
- const isFormData = !!length;
91
- let aborted = false;
92
- const options = parse(url);
93
- options.method = method;
94
- options.headers = isFormData
95
- ? Object.assign(data.getHeaders(), headers)
96
- : headers;
97
- if (isFormData || (data && data.length)) {
98
- options.headers['Content-Length'] =
99
- length || data.length;
100
- }
101
- const req = options.protocol !== 'https:'
102
- ? http.request(options)
103
- : https.request(options);
104
- onCancel(signal, () => {
105
- aborted = true;
106
- req.abort();
107
- reject(cancelError());
108
- });
109
- req.on('response', (res) => {
110
- if (aborted)
111
- return;
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
- const resChunks = [];
114
- res.on('data', (data) => {
115
- resChunks.push(data);
116
- });
117
- res.on('end', () => resolve({
118
- data: Buffer.concat(resChunks).toString('utf8'),
119
- status: res.statusCode,
120
- headers: res.headers,
121
- request: params
122
- }));
123
- });
124
- req.on('error', (err) => {
125
- if (aborted)
126
- return;
127
- reject(err);
128
- });
129
- if (isReadable(data, isFormData)) {
130
- if (onProgress && length) {
131
- data.pipe(new ProgressEmitter(onProgress, length)).pipe(req);
132
- }
133
- else {
134
- data.pipe(req);
135
- }
136
- }
137
- else {
138
- req.end(data);
139
- }
140
- }));
37
+ // ProgressEmitter is a simple PassThrough-style transform stream which keeps
38
+ // track of the number of bytes which have been piped through it and will
39
+ // invoke the `onprogress` function whenever new number are available.
40
+ class ProgressEmitter extends Transform {
41
+ constructor(onProgress, size) {
42
+ super();
43
+ this._onprogress = onProgress;
44
+ this._position = 0;
45
+ this.size = size;
46
+ }
47
+ _transform(chunk, encoding, callback) {
48
+ this._position += chunk.length;
49
+ this._onprogress({
50
+ isComputable: true,
51
+ value: this._position / this.size
52
+ });
53
+ callback(null, chunk);
54
+ }
55
+ }
56
+ const getLength = (formData) => new Promise((resolve, reject) => {
57
+ formData.getLength((error, length) => {
58
+ if (error)
59
+ reject(error);
60
+ else
61
+ resolve(length);
62
+ });
63
+ });
64
+ function isFormData(formData) {
65
+ if (formData && formData.toString() === '[object FormData]') {
66
+ return true;
67
+ }
68
+ return false;
69
+ }
70
+ function isReadable(data, isFormData) {
71
+ if (data && (data instanceof Readable || isFormData)) {
72
+ return true;
73
+ }
74
+ return false;
75
+ }
76
+ const request = (params) => {
77
+ const { method = 'GET', url, data, headers = {}, signal, onProgress } = params;
78
+ return Promise.resolve()
79
+ .then(() => {
80
+ if (isFormData(data)) {
81
+ return getLength(data);
82
+ }
83
+ else {
84
+ return undefined;
85
+ }
86
+ })
87
+ .then((length) => new Promise((resolve, reject) => {
88
+ const isFormData = !!length;
89
+ let aborted = false;
90
+ const options = parse(url);
91
+ options.method = method;
92
+ options.headers = isFormData
93
+ ? Object.assign(data.getHeaders(), headers)
94
+ : headers;
95
+ if (isFormData || (data && data.length)) {
96
+ options.headers['Content-Length'] =
97
+ length || data.length;
98
+ }
99
+ const req = options.protocol !== 'https:'
100
+ ? http.request(options)
101
+ : https.request(options);
102
+ onCancel(signal, () => {
103
+ aborted = true;
104
+ req.abort();
105
+ reject(cancelError());
106
+ });
107
+ req.on('response', (res) => {
108
+ if (aborted)
109
+ return;
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ const resChunks = [];
112
+ res.on('data', (data) => {
113
+ resChunks.push(data);
114
+ });
115
+ res.on('end', () => resolve({
116
+ data: Buffer.concat(resChunks).toString('utf8'),
117
+ status: res.statusCode,
118
+ headers: res.headers,
119
+ request: params
120
+ }));
121
+ });
122
+ req.on('error', (err) => {
123
+ if (aborted)
124
+ return;
125
+ reject(err);
126
+ });
127
+ if (isReadable(data, isFormData)) {
128
+ if (onProgress && length) {
129
+ data.pipe(new ProgressEmitter(onProgress, length)).pipe(req);
130
+ }
131
+ else {
132
+ data.pipe(req);
133
+ }
134
+ }
135
+ else {
136
+ req.end(data);
137
+ }
138
+ }));
141
139
  };
142
140
 
143
- function identity(obj) {
144
- return obj;
141
+ function identity(obj) {
142
+ return obj;
145
143
  }
146
144
 
147
- const transformFile = identity;
145
+ // node form-data has another append signature
146
+ // see docs at https://www.npmjs.com/package/formdata-node
147
+ const getFileOptions = ({ name, contentType }) => [
148
+ Object.entries({
149
+ filename: name,
150
+ contentType
151
+ })
152
+ .filter(([, value]) => !!value)
153
+ .reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [key]: value })), {})
154
+ ].filter((value) => !!value);
155
+ const transformFile = identity;
148
156
  var getFormData = () => new NodeFormData();
149
157
 
150
- /**
151
- * FileData type guard.
152
- */
153
- const isFileData = (data) => {
154
- return (data !== undefined &&
155
- ((typeof Blob !== 'undefined' && data instanceof Blob) ||
156
- (typeof File !== 'undefined' && data instanceof File) ||
157
- (typeof Buffer !== 'undefined' && data instanceof Buffer)));
158
- };
159
- /**
160
- * Uuid type guard.
161
- */
162
- const isUuid = (data) => {
163
- const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
164
- const regExp = new RegExp(UUID_REGEX);
165
- return !isFileData(data) && regExp.test(data);
166
- };
167
- /**
168
- * Url type guard.
169
- *
170
- * @param {NodeFile | BrowserFile | Url | Uuid} data
171
- */
172
- const isUrl = (data) => {
173
- const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
174
- const regExp = new RegExp(URL_REGEX);
175
- return !isFileData(data) && regExp.test(data);
158
+ /**
159
+ * FileData type guard.
160
+ */
161
+ const isFileData = (data) => {
162
+ return (data !== undefined &&
163
+ ((typeof Blob !== 'undefined' && data instanceof Blob) ||
164
+ (typeof File !== 'undefined' && data instanceof File) ||
165
+ (typeof Buffer !== 'undefined' && data instanceof Buffer)));
166
+ };
167
+ /**
168
+ * Uuid type guard.
169
+ */
170
+ const isUuid = (data) => {
171
+ const UUID_REGEX = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}';
172
+ const regExp = new RegExp(UUID_REGEX);
173
+ return !isFileData(data) && regExp.test(data);
174
+ };
175
+ /**
176
+ * Url type guard.
177
+ *
178
+ * @param {NodeFile | BrowserFile | Url | Uuid} data
179
+ */
180
+ const isUrl = (data) => {
181
+ const URL_REGEX = '^(?:\\w+:)?\\/\\/([^\\s\\.]+\\.\\S{2}|localhost[\\:?\\d]*)\\S*$';
182
+ const regExp = new RegExp(URL_REGEX);
183
+ return !isFileData(data) && regExp.test(data);
176
184
  };
177
185
 
178
- const isSimpleValue = (value) => {
179
- return (typeof value === 'string' ||
180
- typeof value === 'number' ||
181
- typeof value === 'undefined');
182
- };
183
- const isObjectValue = (value) => {
184
- return !!value && typeof value === 'object' && !Array.isArray(value);
185
- };
186
- const isFileValue = (value) => !!value &&
187
- typeof value === 'object' &&
188
- 'data' in value &&
189
- isFileData(value.data);
190
- function collectParams(params, inputKey, inputValue) {
191
- if (isFileValue(inputValue)) {
192
- const name = inputValue.name;
193
- const file = transformFile(inputValue.data); // lgtm [js/superfluous-trailing-arguments]
194
- params.push(name ? [inputKey, file, name] : [inputKey, file]);
195
- }
196
- else if (isObjectValue(inputValue)) {
197
- for (const [key, value] of Object.entries(inputValue)) {
198
- if (typeof value !== 'undefined') {
199
- params.push([`${inputKey}[${key}]`, String(value)]);
200
- }
201
- }
202
- }
203
- else if (isSimpleValue(inputValue) && inputValue) {
204
- params.push([inputKey, inputValue.toString()]);
205
- }
206
- }
207
- function getFormDataParams(options) {
208
- const params = [];
209
- for (const [key, value] of Object.entries(options)) {
210
- collectParams(params, key, value);
211
- }
212
- return params;
213
- }
214
- function buildFormData(options) {
215
- const formData = getFormData();
216
- const params = getFormDataParams(options);
217
- for (const param of params) {
218
- formData.append(...param);
219
- }
220
- return formData;
186
+ const isSimpleValue = (value) => {
187
+ return (typeof value === 'string' ||
188
+ typeof value === 'number' ||
189
+ typeof value === 'undefined');
190
+ };
191
+ const isObjectValue = (value) => {
192
+ return !!value && typeof value === 'object' && !Array.isArray(value);
193
+ };
194
+ const isFileValue = (value) => !!value &&
195
+ typeof value === 'object' &&
196
+ 'data' in value &&
197
+ isFileData(value.data);
198
+ function collectParams(params, inputKey, inputValue) {
199
+ if (isFileValue(inputValue)) {
200
+ const { name, contentType } = inputValue;
201
+ const file = transformFile(inputValue.data); // lgtm [js/superfluous-trailing-arguments]
202
+ const options = getFileOptions({ name, contentType });
203
+ params.push([inputKey, file, ...options]);
204
+ }
205
+ else if (isObjectValue(inputValue)) {
206
+ for (const [key, value] of Object.entries(inputValue)) {
207
+ if (typeof value !== 'undefined') {
208
+ params.push([`${inputKey}[${key}]`, String(value)]);
209
+ }
210
+ }
211
+ }
212
+ else if (isSimpleValue(inputValue) && inputValue) {
213
+ params.push([inputKey, inputValue.toString()]);
214
+ }
215
+ }
216
+ function getFormDataParams(options) {
217
+ const params = [];
218
+ for (const [key, value] of Object.entries(options)) {
219
+ collectParams(params, key, value);
220
+ }
221
+ return params;
222
+ }
223
+ function buildFormData(options) {
224
+ const formData = getFormData();
225
+ const paramsList = getFormDataParams(options);
226
+ for (const params of paramsList) {
227
+ const [key, value, ...options] = params;
228
+ // node form-data has another signature for append
229
+ formData.append(key, value, ...options);
230
+ }
231
+ return formData;
221
232
  }
222
233
 
223
- const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
224
- // TODO: generalize value transforming logic and use it here and inside `buildFormData`
225
- const createQuery = (query) => Object.entries(query)
226
- .reduce((params, [key, value]) => {
227
- let param;
228
- if (typeof value === 'object' && !Array.isArray(value)) {
229
- param = Object.entries(value)
230
- .filter((entry) => typeof entry[1] !== 'undefined')
231
- .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
232
- }
233
- else if (Array.isArray(value)) {
234
- param = value.map((val) => serializePair(`${key}[]`, val));
235
- }
236
- else {
237
- param = serializePair(key, value);
238
- }
239
- return params.concat(param);
240
- }, [])
241
- .filter((x) => !!x)
242
- .join('&');
243
- const getUrl = (base, path, query) => [
244
- base,
245
- path,
246
- query && Object.keys(query).length > 0 ? '?' : '',
247
- query && createQuery(query)
248
- ]
249
- .filter(Boolean)
234
+ const serializePair = (key, value) => typeof value !== 'undefined' ? `${key}=${encodeURIComponent(value)}` : null;
235
+ // TODO: generalize value transforming logic and use it here and inside `buildFormData`
236
+ const createQuery = (query) => Object.entries(query)
237
+ .reduce((params, [key, value]) => {
238
+ let param;
239
+ if (typeof value === 'object' && !Array.isArray(value)) {
240
+ param = Object.entries(value)
241
+ .filter((entry) => typeof entry[1] !== 'undefined')
242
+ .map((entry) => serializePair(`${key}[${entry[0]}]`, String(entry[1])));
243
+ }
244
+ else if (Array.isArray(value)) {
245
+ param = value.map((val) => serializePair(`${key}[]`, val));
246
+ }
247
+ else {
248
+ param = serializePair(key, value);
249
+ }
250
+ return params.concat(param);
251
+ }, [])
252
+ .filter((x) => !!x)
253
+ .join('&');
254
+ const getUrl = (base, path, query) => [
255
+ base,
256
+ path,
257
+ query && Object.keys(query).length > 0 ? '?' : '',
258
+ query && createQuery(query)
259
+ ]
260
+ .filter(Boolean)
250
261
  .join('');
251
262
 
252
- /*
253
- Settings for future support:
254
- parallelDirectUploads: 10,
255
- */
256
- const defaultSettings = {
257
- baseCDN: 'https://ucarecdn.com',
258
- baseURL: 'https://upload.uploadcare.com',
259
- maxContentLength: 50 * 1024 * 1024,
260
- retryThrottledRequestMaxTimes: 1,
261
- multipartMinFileSize: 25 * 1024 * 1024,
262
- multipartChunkSize: 5 * 1024 * 1024,
263
- multipartMinLastPartSize: 1024 * 1024,
264
- maxConcurrentRequests: 4,
265
- multipartMaxAttempts: 3,
266
- pollingTimeoutMilliseconds: 10000,
267
- pusherKey: '79ae88bd931ea68464d9'
268
- };
269
- const defaultContentType = 'application/octet-stream';
263
+ /*
264
+ Settings for future support:
265
+ parallelDirectUploads: 10,
266
+ */
267
+ const defaultSettings = {
268
+ baseCDN: 'https://ucarecdn.com',
269
+ baseURL: 'https://upload.uploadcare.com',
270
+ maxContentLength: 50 * 1024 * 1024,
271
+ retryThrottledRequestMaxTimes: 1,
272
+ multipartMinFileSize: 25 * 1024 * 1024,
273
+ multipartChunkSize: 5 * 1024 * 1024,
274
+ multipartMinLastPartSize: 1024 * 1024,
275
+ maxConcurrentRequests: 4,
276
+ multipartMaxAttempts: 3,
277
+ pollingTimeoutMilliseconds: 10000,
278
+ pusherKey: '79ae88bd931ea68464d9'
279
+ };
280
+ const defaultContentType = 'application/octet-stream';
270
281
  const defaultFilename = 'original';
271
282
 
272
- var version = '3.1.0';
283
+ var version = '3.1.1';
273
284
 
274
- /**
275
- * Returns User Agent based on version and settings.
276
- */
277
- function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
278
- const libraryName = 'UploadcareUploadClient';
279
- const libraryVersion = version;
280
- const languageName = 'JavaScript';
281
- if (typeof userAgent === 'string') {
282
- return userAgent;
283
- }
284
- if (typeof userAgent === 'function') {
285
- return userAgent({
286
- publicKey,
287
- libraryName,
288
- libraryVersion,
289
- languageName,
290
- integration
291
- });
292
- }
293
- const mainInfo = [libraryName, libraryVersion, publicKey]
294
- .filter(Boolean)
295
- .join('/');
296
- const additionInfo = [languageName, integration].filter(Boolean).join('; ');
297
- return `${mainInfo} (${additionInfo})`;
285
+ /**
286
+ * Returns User Agent based on version and settings.
287
+ */
288
+ function getUserAgent({ userAgent, publicKey = '', integration = '' } = {}) {
289
+ const libraryName = 'UploadcareUploadClient';
290
+ const libraryVersion = version;
291
+ const languageName = 'JavaScript';
292
+ if (typeof userAgent === 'string') {
293
+ return userAgent;
294
+ }
295
+ if (typeof userAgent === 'function') {
296
+ return userAgent({
297
+ publicKey,
298
+ libraryName,
299
+ libraryVersion,
300
+ languageName,
301
+ integration
302
+ });
303
+ }
304
+ const mainInfo = [libraryName, libraryVersion, publicKey]
305
+ .filter(Boolean)
306
+ .join('/');
307
+ const additionInfo = [languageName, integration].filter(Boolean).join('; ');
308
+ return `${mainInfo} (${additionInfo})`;
298
309
  }
299
310
 
300
- const SEPARATOR = /\W|_/g;
301
- /**
302
- * Transforms a string to camelCased.
303
- */
304
- function camelize(text) {
305
- return text
306
- .split(SEPARATOR)
307
- .map((word, index) => word.charAt(0)[index > 0 ? 'toUpperCase' : 'toLowerCase']() +
308
- word.slice(1))
309
- .join('');
310
- }
311
- /**
312
- * Transforms keys of an object to camelCased recursively.
313
- */
314
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
315
- function camelizeKeys(source) {
316
- if (!source || typeof source !== 'object') {
317
- return source;
318
- }
319
- return Object.keys(source).reduce((accumulator, key) => {
320
- accumulator[camelize(key)] =
321
- typeof source[key] === 'object' ? camelizeKeys(source[key]) : source[key];
322
- return accumulator;
323
- }, {});
311
+ const SEPARATOR = /\W|_/g;
312
+ /**
313
+ * Transforms a string to camelCased.
314
+ */
315
+ function camelize(text) {
316
+ return text
317
+ .split(SEPARATOR)
318
+ .map((word, index) => word.charAt(0)[index > 0 ? 'toUpperCase' : 'toLowerCase']() +
319
+ word.slice(1))
320
+ .join('');
321
+ }
322
+ /**
323
+ * Transforms keys of an object to camelCased recursively.
324
+ */
325
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
326
+ function camelizeKeys(source) {
327
+ if (!source || typeof source !== 'object') {
328
+ return source;
329
+ }
330
+ return Object.keys(source).reduce((accumulator, key) => {
331
+ accumulator[camelize(key)] =
332
+ typeof source[key] === 'object' ? camelizeKeys(source[key]) : source[key];
333
+ return accumulator;
334
+ }, {});
324
335
  }
325
336
 
326
- /**
327
- * setTimeout as Promise.
328
- *
329
- * @param {number} ms Timeout in milliseconds.
330
- */
337
+ /**
338
+ * setTimeout as Promise.
339
+ *
340
+ * @param {number} ms Timeout in milliseconds.
341
+ */
331
342
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
332
343
 
333
- const defaultOptions = {
334
- factor: 2,
335
- time: 100
336
- };
337
- function retrier(fn, options = defaultOptions) {
338
- let attempts = 0;
339
- function runAttempt(fn) {
340
- const defaultDelayTime = Math.round(options.time * Math.pow(options.factor, attempts));
341
- const retry = (ms) => delay(ms !== null && ms !== void 0 ? ms : defaultDelayTime).then(() => {
342
- attempts += 1;
343
- return runAttempt(fn);
344
- });
345
- return fn({
346
- attempt: attempts,
347
- retry
348
- });
349
- }
350
- return runAttempt(fn);
344
+ const defaultOptions = {
345
+ factor: 2,
346
+ time: 100
347
+ };
348
+ function retrier(fn, options = defaultOptions) {
349
+ let attempts = 0;
350
+ function runAttempt(fn) {
351
+ const defaultDelayTime = Math.round(options.time * Math.pow(options.factor, attempts));
352
+ const retry = (ms) => delay(ms !== null && ms !== void 0 ? ms : defaultDelayTime).then(() => {
353
+ attempts += 1;
354
+ return runAttempt(fn);
355
+ });
356
+ return fn({
357
+ attempt: attempts,
358
+ retry
359
+ });
360
+ }
361
+ return runAttempt(fn);
351
362
  }
352
363
 
353
- const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
354
- const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
355
- function getTimeoutFromThrottledRequest(error) {
356
- const { headers } = error || {};
357
- return ((headers &&
358
- Number.parseInt(headers['x-throttle-wait-seconds']) * 1000) ||
359
- DEFAULT_RETRY_AFTER_TIMEOUT);
360
- }
361
- function retryIfThrottled(fn, retryThrottledMaxTimes) {
362
- return retrier(({ attempt, retry }) => fn().catch((error) => {
363
- if ('response' in error &&
364
- (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
365
- attempt < retryThrottledMaxTimes) {
366
- return retry(getTimeoutFromThrottledRequest(error));
367
- }
368
- throw error;
369
- }));
364
+ const REQUEST_WAS_THROTTLED_CODE = 'RequestThrottledError';
365
+ const DEFAULT_RETRY_AFTER_TIMEOUT = 15000;
366
+ function getTimeoutFromThrottledRequest(error) {
367
+ const { headers } = error || {};
368
+ return ((headers &&
369
+ Number.parseInt(headers['x-throttle-wait-seconds']) * 1000) ||
370
+ DEFAULT_RETRY_AFTER_TIMEOUT);
371
+ }
372
+ function retryIfThrottled(fn, retryThrottledMaxTimes) {
373
+ return retrier(({ attempt, retry }) => fn().catch((error) => {
374
+ if ('response' in error &&
375
+ (error === null || error === void 0 ? void 0 : error.code) === REQUEST_WAS_THROTTLED_CODE &&
376
+ attempt < retryThrottledMaxTimes) {
377
+ return retry(getTimeoutFromThrottledRequest(error));
378
+ }
379
+ throw error;
380
+ }));
370
381
  }
371
382
 
372
- function getStoreValue(store) {
373
- return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
383
+ function getStoreValue(store) {
384
+ return typeof store === 'undefined' ? 'auto' : store ? '1' : '0';
374
385
  }
375
386
 
376
- /**
377
- * Performs file uploading request to Uploadcare Upload API.
378
- * Can be canceled and has progress.
379
- */
380
- function base(file, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
381
- return retryIfThrottled(() => {
382
- var _a;
383
- return request({
384
- method: 'POST',
385
- url: getUrl(baseURL, '/base/', {
386
- jsonerrors: 1
387
- }),
388
- headers: {
389
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
390
- },
391
- data: buildFormData({
392
- file: {
393
- data: file,
394
- name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename
395
- },
396
- UPLOADCARE_PUB_KEY: publicKey,
397
- UPLOADCARE_STORE: getStoreValue(store),
398
- signature: secureSignature,
399
- expire: secureExpire,
400
- source: source,
401
- metadata
402
- }),
403
- signal,
404
- onProgress
405
- }).then(({ data, headers, request }) => {
406
- const response = camelizeKeys(JSON.parse(data));
407
- if ('error' in response) {
408
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
409
- }
410
- else {
411
- return response;
412
- }
413
- });
414
- }, retryThrottledRequestMaxTimes);
387
+ /**
388
+ * Performs file uploading request to Uploadcare Upload API.
389
+ * Can be canceled and has progress.
390
+ */
391
+ function base(file, { publicKey, fileName, contentType, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
392
+ return retryIfThrottled(() => {
393
+ var _a;
394
+ return request({
395
+ method: 'POST',
396
+ url: getUrl(baseURL, '/base/', {
397
+ jsonerrors: 1
398
+ }),
399
+ headers: {
400
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
401
+ },
402
+ data: buildFormData({
403
+ file: {
404
+ data: file,
405
+ name: (_a = fileName !== null && fileName !== void 0 ? fileName : file.name) !== null && _a !== void 0 ? _a : defaultFilename,
406
+ contentType
407
+ },
408
+ UPLOADCARE_PUB_KEY: publicKey,
409
+ UPLOADCARE_STORE: getStoreValue(store),
410
+ signature: secureSignature,
411
+ expire: secureExpire,
412
+ source: source,
413
+ metadata
414
+ }),
415
+ signal,
416
+ onProgress
417
+ }).then(({ data, headers, request }) => {
418
+ const response = camelizeKeys(JSON.parse(data));
419
+ if ('error' in response) {
420
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
421
+ }
422
+ else {
423
+ return response;
424
+ }
425
+ });
426
+ }, retryThrottledRequestMaxTimes);
415
427
  }
416
428
 
417
- var TypeEnum;
418
- (function (TypeEnum) {
419
- TypeEnum["Token"] = "token";
420
- TypeEnum["FileInfo"] = "file_info";
421
- })(TypeEnum || (TypeEnum = {}));
422
- /**
423
- * Uploading files from URL.
424
- */
425
- function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
426
- return retryIfThrottled(() => request({
427
- method: 'POST',
428
- headers: {
429
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
430
- },
431
- url: getUrl(baseURL, '/from_url/', {
432
- jsonerrors: 1,
433
- pub_key: publicKey,
434
- source_url: sourceUrl,
435
- store: getStoreValue(store),
436
- filename: fileName,
437
- check_URL_duplicates: checkForUrlDuplicates ? 1 : undefined,
438
- save_URL_duplicates: saveUrlForRecurrentUploads ? 1 : undefined,
439
- signature: secureSignature,
440
- expire: secureExpire,
441
- source: source,
442
- metadata
443
- }),
444
- signal
445
- }).then(({ data, headers, request }) => {
446
- const response = camelizeKeys(JSON.parse(data));
447
- if ('error' in response) {
448
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
449
- }
450
- else {
451
- return response;
452
- }
453
- }), retryThrottledRequestMaxTimes);
429
+ var TypeEnum;
430
+ (function (TypeEnum) {
431
+ TypeEnum["Token"] = "token";
432
+ TypeEnum["FileInfo"] = "file_info";
433
+ })(TypeEnum || (TypeEnum = {}));
434
+ /**
435
+ * Uploading files from URL.
436
+ */
437
+ function fromUrl(sourceUrl, { publicKey, baseURL = defaultSettings.baseURL, store, fileName, checkForUrlDuplicates, saveUrlForRecurrentUploads, secureSignature, secureExpire, source = 'url', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
438
+ return retryIfThrottled(() => request({
439
+ method: 'POST',
440
+ headers: {
441
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
442
+ },
443
+ url: getUrl(baseURL, '/from_url/', {
444
+ jsonerrors: 1,
445
+ pub_key: publicKey,
446
+ source_url: sourceUrl,
447
+ store: getStoreValue(store),
448
+ filename: fileName,
449
+ check_URL_duplicates: checkForUrlDuplicates ? 1 : undefined,
450
+ save_URL_duplicates: saveUrlForRecurrentUploads ? 1 : undefined,
451
+ signature: secureSignature,
452
+ expire: secureExpire,
453
+ source: source,
454
+ metadata
455
+ }),
456
+ signal
457
+ }).then(({ data, headers, request }) => {
458
+ const response = camelizeKeys(JSON.parse(data));
459
+ if ('error' in response) {
460
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
461
+ }
462
+ else {
463
+ return response;
464
+ }
465
+ }), retryThrottledRequestMaxTimes);
454
466
  }
455
467
 
456
- var Status;
457
- (function (Status) {
458
- Status["Unknown"] = "unknown";
459
- Status["Waiting"] = "waiting";
460
- Status["Progress"] = "progress";
461
- Status["Error"] = "error";
462
- Status["Success"] = "success";
463
- })(Status || (Status = {}));
464
- const isErrorResponse = (response) => {
465
- return 'status' in response && response.status === Status.Error;
466
- };
467
- /**
468
- * Checking upload status and working with file tokens.
469
- */
470
- function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes } = {}) {
471
- return retryIfThrottled(() => request({
472
- method: 'GET',
473
- headers: publicKey
474
- ? {
475
- 'X-UC-User-Agent': getUserAgent({
476
- publicKey,
477
- integration,
478
- userAgent
479
- })
480
- }
481
- : undefined,
482
- url: getUrl(baseURL, '/from_url/status/', {
483
- jsonerrors: 1,
484
- token
485
- }),
486
- signal
487
- }).then(({ data, headers, request }) => {
488
- const response = camelizeKeys(JSON.parse(data));
489
- if ('error' in response && !isErrorResponse(response)) {
490
- throw new UploadClientError(response.error.content, undefined, request, response, headers);
491
- }
492
- else {
493
- return response;
494
- }
495
- }), retryThrottledRequestMaxTimes);
468
+ var Status;
469
+ (function (Status) {
470
+ Status["Unknown"] = "unknown";
471
+ Status["Waiting"] = "waiting";
472
+ Status["Progress"] = "progress";
473
+ Status["Error"] = "error";
474
+ Status["Success"] = "success";
475
+ })(Status || (Status = {}));
476
+ const isErrorResponse = (response) => {
477
+ return 'status' in response && response.status === Status.Error;
478
+ };
479
+ /**
480
+ * Checking upload status and working with file tokens.
481
+ */
482
+ function fromUrlStatus(token, { publicKey, baseURL = defaultSettings.baseURL, signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes } = {}) {
483
+ return retryIfThrottled(() => request({
484
+ method: 'GET',
485
+ headers: publicKey
486
+ ? {
487
+ 'X-UC-User-Agent': getUserAgent({
488
+ publicKey,
489
+ integration,
490
+ userAgent
491
+ })
492
+ }
493
+ : undefined,
494
+ url: getUrl(baseURL, '/from_url/status/', {
495
+ jsonerrors: 1,
496
+ token
497
+ }),
498
+ signal
499
+ }).then(({ data, headers, request }) => {
500
+ const response = camelizeKeys(JSON.parse(data));
501
+ if ('error' in response && !isErrorResponse(response)) {
502
+ throw new UploadClientError(response.error.content, undefined, request, response, headers);
503
+ }
504
+ else {
505
+ return response;
506
+ }
507
+ }), retryThrottledRequestMaxTimes);
496
508
  }
497
509
 
498
- /**
499
- * Create files group.
500
- */
501
- function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallback, secureSignature, secureExpire, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
502
- return retryIfThrottled(() => request({
503
- method: 'POST',
504
- headers: {
505
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
506
- },
507
- url: getUrl(baseURL, '/group/', {
508
- jsonerrors: 1,
509
- pub_key: publicKey,
510
- files: uuids,
511
- callback: jsonpCallback,
512
- signature: secureSignature,
513
- expire: secureExpire,
514
- source
515
- }),
516
- signal
517
- }).then(({ data, headers, request }) => {
518
- const response = camelizeKeys(JSON.parse(data));
519
- if ('error' in response) {
520
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
521
- }
522
- else {
523
- return response;
524
- }
525
- }), retryThrottledRequestMaxTimes);
510
+ /**
511
+ * Create files group.
512
+ */
513
+ function group(uuids, { publicKey, baseURL = defaultSettings.baseURL, jsonpCallback, secureSignature, secureExpire, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
514
+ return retryIfThrottled(() => request({
515
+ method: 'POST',
516
+ headers: {
517
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
518
+ },
519
+ url: getUrl(baseURL, '/group/', {
520
+ jsonerrors: 1,
521
+ pub_key: publicKey,
522
+ files: uuids,
523
+ callback: jsonpCallback,
524
+ signature: secureSignature,
525
+ expire: secureExpire,
526
+ source
527
+ }),
528
+ signal
529
+ }).then(({ data, headers, request }) => {
530
+ const response = camelizeKeys(JSON.parse(data));
531
+ if ('error' in response) {
532
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
533
+ }
534
+ else {
535
+ return response;
536
+ }
537
+ }), retryThrottledRequestMaxTimes);
526
538
  }
527
539
 
528
- /**
529
- * Get info about group.
530
- */
531
- function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
532
- return retryIfThrottled(() => request({
533
- method: 'GET',
534
- headers: {
535
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
536
- },
537
- url: getUrl(baseURL, '/group/info/', {
538
- jsonerrors: 1,
539
- pub_key: publicKey,
540
- group_id: id,
541
- source
542
- }),
543
- signal
544
- }).then(({ data, headers, request }) => {
545
- const response = camelizeKeys(JSON.parse(data));
546
- if ('error' in response) {
547
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
548
- }
549
- else {
550
- return response;
551
- }
552
- }), retryThrottledRequestMaxTimes);
540
+ /**
541
+ * Get info about group.
542
+ */
543
+ function groupInfo(id, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
544
+ return retryIfThrottled(() => request({
545
+ method: 'GET',
546
+ headers: {
547
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
548
+ },
549
+ url: getUrl(baseURL, '/group/info/', {
550
+ jsonerrors: 1,
551
+ pub_key: publicKey,
552
+ group_id: id,
553
+ source
554
+ }),
555
+ signal
556
+ }).then(({ data, headers, request }) => {
557
+ const response = camelizeKeys(JSON.parse(data));
558
+ if ('error' in response) {
559
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
560
+ }
561
+ else {
562
+ return response;
563
+ }
564
+ }), retryThrottledRequestMaxTimes);
553
565
  }
554
566
 
555
- /**
556
- * Returns a JSON dictionary holding file info.
557
- */
558
- function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
559
- return retryIfThrottled(() => request({
560
- method: 'GET',
561
- headers: {
562
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
563
- },
564
- url: getUrl(baseURL, '/info/', {
565
- jsonerrors: 1,
566
- pub_key: publicKey,
567
- file_id: uuid,
568
- source
569
- }),
570
- signal
571
- }).then(({ data, headers, request }) => {
572
- const response = camelizeKeys(JSON.parse(data));
573
- if ('error' in response) {
574
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
575
- }
576
- else {
577
- return response;
578
- }
579
- }), retryThrottledRequestMaxTimes);
567
+ /**
568
+ * Returns a JSON dictionary holding file info.
569
+ */
570
+ function info(uuid, { publicKey, baseURL = defaultSettings.baseURL, signal, source, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
571
+ return retryIfThrottled(() => request({
572
+ method: 'GET',
573
+ headers: {
574
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
575
+ },
576
+ url: getUrl(baseURL, '/info/', {
577
+ jsonerrors: 1,
578
+ pub_key: publicKey,
579
+ file_id: uuid,
580
+ source
581
+ }),
582
+ signal
583
+ }).then(({ data, headers, request }) => {
584
+ const response = camelizeKeys(JSON.parse(data));
585
+ if ('error' in response) {
586
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
587
+ }
588
+ else {
589
+ return response;
590
+ }
591
+ }), retryThrottledRequestMaxTimes);
580
592
  }
581
593
 
582
- /**
583
- * Start multipart uploading.
584
- */
585
- function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
586
- return retryIfThrottled(() => request({
587
- method: 'POST',
588
- url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
589
- headers: {
590
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
591
- },
592
- data: buildFormData({
593
- filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
594
- size: size,
595
- content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
596
- part_size: multipartChunkSize,
597
- UPLOADCARE_STORE: getStoreValue(store),
598
- UPLOADCARE_PUB_KEY: publicKey,
599
- signature: secureSignature,
600
- expire: secureExpire,
601
- source: source,
602
- metadata
603
- }),
604
- signal
605
- }).then(({ data, headers, request }) => {
606
- const response = camelizeKeys(JSON.parse(data));
607
- if ('error' in response) {
608
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
609
- }
610
- else {
611
- // convert to array
612
- response.parts = Object.keys(response.parts).map((key) => response.parts[key]);
613
- return response;
614
- }
615
- }), retryThrottledRequestMaxTimes);
594
+ /**
595
+ * Start multipart uploading.
596
+ */
597
+ function multipartStart(size, { publicKey, contentType, fileName, multipartChunkSize = defaultSettings.multipartChunkSize, baseURL = '', secureSignature, secureExpire, store, signal, source = 'local', integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes, metadata }) {
598
+ return retryIfThrottled(() => request({
599
+ method: 'POST',
600
+ url: getUrl(baseURL, '/multipart/start/', { jsonerrors: 1 }),
601
+ headers: {
602
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
603
+ },
604
+ data: buildFormData({
605
+ filename: fileName !== null && fileName !== void 0 ? fileName : defaultFilename,
606
+ size: size,
607
+ content_type: contentType !== null && contentType !== void 0 ? contentType : defaultContentType,
608
+ part_size: multipartChunkSize,
609
+ UPLOADCARE_STORE: getStoreValue(store),
610
+ UPLOADCARE_PUB_KEY: publicKey,
611
+ signature: secureSignature,
612
+ expire: secureExpire,
613
+ source: source,
614
+ metadata
615
+ }),
616
+ signal
617
+ }).then(({ data, headers, request }) => {
618
+ const response = camelizeKeys(JSON.parse(data));
619
+ if ('error' in response) {
620
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
621
+ }
622
+ else {
623
+ // convert to array
624
+ response.parts = Object.keys(response.parts).map((key) => response.parts[key]);
625
+ return response;
626
+ }
627
+ }), retryThrottledRequestMaxTimes);
616
628
  }
617
629
 
618
- /**
619
- * Complete multipart uploading.
620
- */
621
- function multipartUpload(part, url, { signal, onProgress }) {
622
- return request({
623
- method: 'PUT',
624
- url,
625
- data: part,
626
- // Upload request can't be non-computable because we always know exact size
627
- onProgress: onProgress,
628
- signal
629
- })
630
- .then((result) => {
631
- // hack for node ¯\_(ツ)_/¯
632
- if (onProgress)
633
- onProgress({
634
- isComputable: true,
635
- value: 1
636
- });
637
- return result;
638
- })
639
- .then(({ status }) => ({ code: status }));
630
+ /**
631
+ * Complete multipart uploading.
632
+ */
633
+ function multipartUpload(part, url, { signal, onProgress }) {
634
+ return request({
635
+ method: 'PUT',
636
+ url,
637
+ data: part,
638
+ // Upload request can't be non-computable because we always know exact size
639
+ onProgress: onProgress,
640
+ signal
641
+ })
642
+ .then((result) => {
643
+ // hack for node ¯\_(ツ)_/¯
644
+ if (onProgress)
645
+ onProgress({
646
+ isComputable: true,
647
+ value: 1
648
+ });
649
+ return result;
650
+ })
651
+ .then(({ status }) => ({ code: status }));
640
652
  }
641
653
 
642
- /**
643
- * Complete multipart uploading.
644
- */
645
- function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
646
- return retryIfThrottled(() => request({
647
- method: 'POST',
648
- url: getUrl(baseURL, '/multipart/complete/', { jsonerrors: 1 }),
649
- headers: {
650
- 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
651
- },
652
- data: buildFormData({
653
- uuid: uuid,
654
- UPLOADCARE_PUB_KEY: publicKey,
655
- source: source
656
- }),
657
- signal
658
- }).then(({ data, headers, request }) => {
659
- const response = camelizeKeys(JSON.parse(data));
660
- if ('error' in response) {
661
- throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
662
- }
663
- else {
664
- return response;
665
- }
666
- }), retryThrottledRequestMaxTimes);
654
+ /**
655
+ * Complete multipart uploading.
656
+ */
657
+ function multipartComplete(uuid, { publicKey, baseURL = defaultSettings.baseURL, source = 'local', signal, integration, userAgent, retryThrottledRequestMaxTimes = defaultSettings.retryThrottledRequestMaxTimes }) {
658
+ return retryIfThrottled(() => request({
659
+ method: 'POST',
660
+ url: getUrl(baseURL, '/multipart/complete/', { jsonerrors: 1 }),
661
+ headers: {
662
+ 'X-UC-User-Agent': getUserAgent({ publicKey, integration, userAgent })
663
+ },
664
+ data: buildFormData({
665
+ uuid: uuid,
666
+ UPLOADCARE_PUB_KEY: publicKey,
667
+ source: source
668
+ }),
669
+ signal
670
+ }).then(({ data, headers, request }) => {
671
+ const response = camelizeKeys(JSON.parse(data));
672
+ if ('error' in response) {
673
+ throw new UploadClientError(response.error.content, response.error.errorCode, request, response, headers);
674
+ }
675
+ else {
676
+ return response;
677
+ }
678
+ }), retryThrottledRequestMaxTimes);
667
679
  }
668
680
 
669
- class UploadcareFile {
670
- constructor(fileInfo, { baseCDN, defaultEffects, fileName }) {
671
- this.name = null;
672
- this.size = null;
673
- this.isStored = null;
674
- this.isImage = null;
675
- this.mimeType = null;
676
- this.cdnUrl = null;
677
- this.cdnUrlModifiers = null;
678
- this.originalUrl = null;
679
- this.originalFilename = null;
680
- this.imageInfo = null;
681
- this.videoInfo = null;
682
- this.contentInfo = null;
683
- this.metadata = null;
684
- const { uuid, s3Bucket } = fileInfo;
685
- const urlBase = s3Bucket
686
- ? `https://${s3Bucket}.s3.amazonaws.com/${uuid}/${fileInfo.filename}`
687
- : `${baseCDN}/${uuid}/`;
688
- const cdnUrlModifiers = defaultEffects ? `-/${defaultEffects}` : null;
689
- const cdnUrl = `${urlBase}${cdnUrlModifiers || ''}`;
690
- const originalUrl = uuid ? urlBase : null;
691
- this.uuid = uuid;
692
- this.name = fileName || fileInfo.filename;
693
- this.size = fileInfo.size;
694
- this.isStored = fileInfo.isStored;
695
- this.isImage = fileInfo.isImage;
696
- this.mimeType = fileInfo.mimeType;
697
- this.cdnUrl = cdnUrl;
698
- this.cdnUrlModifiers = cdnUrlModifiers;
699
- this.originalUrl = originalUrl;
700
- this.originalFilename = fileInfo.originalFilename;
701
- this.imageInfo = camelizeKeys(fileInfo.imageInfo);
702
- this.videoInfo = camelizeKeys(fileInfo.videoInfo);
703
- this.contentInfo = camelizeKeys(fileInfo.contentInfo);
704
- this.metadata = fileInfo.metadata || null;
705
- }
681
+ class UploadcareFile {
682
+ constructor(fileInfo, { baseCDN, fileName }) {
683
+ this.name = null;
684
+ this.size = null;
685
+ this.isStored = null;
686
+ this.isImage = null;
687
+ this.mimeType = null;
688
+ this.cdnUrl = null;
689
+ this.s3Url = null;
690
+ this.originalFilename = null;
691
+ this.imageInfo = null;
692
+ this.videoInfo = null;
693
+ this.contentInfo = null;
694
+ this.metadata = null;
695
+ this.s3Bucket = null;
696
+ const { uuid, s3Bucket } = fileInfo;
697
+ const cdnUrl = `${baseCDN}/${uuid}/`;
698
+ const s3Url = s3Bucket
699
+ ? `https://${s3Bucket}.s3.amazonaws.com/${uuid}/${fileInfo.filename}`
700
+ : null;
701
+ this.uuid = uuid;
702
+ this.name = fileName || fileInfo.filename;
703
+ this.size = fileInfo.size;
704
+ this.isStored = fileInfo.isStored;
705
+ this.isImage = fileInfo.isImage;
706
+ this.mimeType = fileInfo.mimeType;
707
+ this.cdnUrl = cdnUrl;
708
+ this.originalFilename = fileInfo.originalFilename;
709
+ this.imageInfo = camelizeKeys(fileInfo.imageInfo);
710
+ this.videoInfo = camelizeKeys(fileInfo.videoInfo);
711
+ this.contentInfo = camelizeKeys(fileInfo.contentInfo);
712
+ this.metadata = fileInfo.metadata || null;
713
+ this.s3Bucket = s3Bucket || null;
714
+ this.s3Url = s3Url;
715
+ }
706
716
  }
707
717
 
708
- const DEFAULT_INTERVAL = 500;
709
- const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((resolve, reject) => {
710
- let timeoutId;
711
- onCancel(signal, () => {
712
- timeoutId && clearTimeout(timeoutId);
713
- reject(cancelError('Poll cancelled'));
714
- });
715
- const tick = () => {
716
- try {
717
- Promise.resolve(check(signal))
718
- .then((result) => {
719
- if (result) {
720
- resolve(result);
721
- }
722
- else {
723
- timeoutId = setTimeout(tick, interval);
724
- }
725
- })
726
- .catch((error) => reject(error));
727
- }
728
- catch (error) {
729
- reject(error);
730
- }
731
- };
732
- timeoutId = setTimeout(tick, 0);
718
+ const DEFAULT_INTERVAL = 500;
719
+ const poll = ({ check, interval = DEFAULT_INTERVAL, signal }) => new Promise((resolve, reject) => {
720
+ let timeoutId;
721
+ onCancel(signal, () => {
722
+ timeoutId && clearTimeout(timeoutId);
723
+ reject(cancelError('Poll cancelled'));
724
+ });
725
+ const tick = () => {
726
+ try {
727
+ Promise.resolve(check(signal))
728
+ .then((result) => {
729
+ if (result) {
730
+ resolve(result);
731
+ }
732
+ else {
733
+ timeoutId = setTimeout(tick, interval);
734
+ }
735
+ })
736
+ .catch((error) => reject(error));
737
+ }
738
+ catch (error) {
739
+ reject(error);
740
+ }
741
+ };
742
+ timeoutId = setTimeout(tick, 0);
733
743
  });
734
744
 
735
- function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, signal, onProgress }) {
736
- return poll({
737
- check: (signal) => info(file, {
738
- publicKey,
739
- baseURL,
740
- signal,
741
- source,
742
- integration,
743
- userAgent,
744
- retryThrottledRequestMaxTimes
745
- }).then((response) => {
746
- if (response.isReady) {
747
- return response;
748
- }
749
- onProgress && onProgress({ isComputable: true, value: 1 });
750
- return false;
751
- }),
752
- signal
753
- });
745
+ function isReadyPoll({ file, publicKey, baseURL, source, integration, userAgent, retryThrottledRequestMaxTimes, signal, onProgress }) {
746
+ return poll({
747
+ check: (signal) => info(file, {
748
+ publicKey,
749
+ baseURL,
750
+ signal,
751
+ source,
752
+ integration,
753
+ userAgent,
754
+ retryThrottledRequestMaxTimes
755
+ }).then((response) => {
756
+ if (response.isReady) {
757
+ return response;
758
+ }
759
+ onProgress && onProgress({ isComputable: true, value: 1 });
760
+ return false;
761
+ }),
762
+ signal
763
+ });
754
764
  }
755
765
 
756
- const uploadFromObject = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN, metadata }) => {
757
- return base(file, {
758
- publicKey,
759
- fileName,
760
- baseURL,
761
- secureSignature,
762
- secureExpire,
763
- store,
764
- signal,
765
- onProgress,
766
- source,
767
- integration,
768
- userAgent,
769
- retryThrottledRequestMaxTimes,
770
- metadata
771
- })
772
- .then(({ file }) => {
773
- return isReadyPoll({
774
- file,
775
- publicKey,
776
- baseURL,
777
- source,
778
- integration,
779
- userAgent,
780
- retryThrottledRequestMaxTimes,
781
- onProgress,
782
- signal
783
- });
784
- })
785
- .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
766
+ const uploadDirect = (file, { publicKey, fileName, baseURL, secureSignature, secureExpire, store, contentType, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN, metadata }) => {
767
+ return base(file, {
768
+ publicKey,
769
+ fileName,
770
+ contentType,
771
+ baseURL,
772
+ secureSignature,
773
+ secureExpire,
774
+ store,
775
+ signal,
776
+ onProgress,
777
+ source,
778
+ integration,
779
+ userAgent,
780
+ retryThrottledRequestMaxTimes,
781
+ metadata
782
+ })
783
+ .then(({ file }) => {
784
+ return isReadyPoll({
785
+ file,
786
+ publicKey,
787
+ baseURL,
788
+ source,
789
+ integration,
790
+ userAgent,
791
+ retryThrottledRequestMaxTimes,
792
+ onProgress,
793
+ signal
794
+ });
795
+ })
796
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
786
797
  };
787
798
 
788
- const race = (fns, { signal } = {}) => {
789
- let lastError = null;
790
- let winnerIndex = null;
791
- const controllers = fns.map(() => new AbortController());
792
- const createStopRaceCallback = (i) => () => {
793
- winnerIndex = i;
794
- controllers.forEach((controller, index) => index !== i && controller.abort());
795
- };
796
- onCancel(signal, () => {
797
- controllers.forEach((controller) => controller.abort());
798
- });
799
- return Promise.all(fns.map((fn, i) => {
800
- const stopRace = createStopRaceCallback(i);
801
- return Promise.resolve()
802
- .then(() => fn({ stopRace, signal: controllers[i].signal }))
803
- .then((result) => {
804
- stopRace();
805
- return result;
806
- })
807
- .catch((error) => {
808
- lastError = error;
809
- return null;
810
- });
811
- })).then((results) => {
812
- if (winnerIndex === null) {
813
- throw lastError;
814
- }
815
- else {
816
- return results[winnerIndex];
817
- }
818
- });
799
+ const race = (fns, { signal } = {}) => {
800
+ let lastError = null;
801
+ let winnerIndex = null;
802
+ const controllers = fns.map(() => new AbortController());
803
+ const createStopRaceCallback = (i) => () => {
804
+ winnerIndex = i;
805
+ controllers.forEach((controller, index) => index !== i && controller.abort());
806
+ };
807
+ onCancel(signal, () => {
808
+ controllers.forEach((controller) => controller.abort());
809
+ });
810
+ return Promise.all(fns.map((fn, i) => {
811
+ const stopRace = createStopRaceCallback(i);
812
+ return Promise.resolve()
813
+ .then(() => fn({ stopRace, signal: controllers[i].signal }))
814
+ .then((result) => {
815
+ stopRace();
816
+ return result;
817
+ })
818
+ .catch((error) => {
819
+ lastError = error;
820
+ return null;
821
+ });
822
+ })).then((results) => {
823
+ if (winnerIndex === null) {
824
+ throw lastError;
825
+ }
826
+ else {
827
+ return results[winnerIndex];
828
+ }
829
+ });
819
830
  };
820
831
 
821
- class Events {
822
- constructor() {
823
- this.events = Object.create({});
824
- }
825
- emit(event, data) {
826
- var _a;
827
- (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
828
- }
829
- on(event, callback) {
830
- this.events[event] = this.events[event] || [];
831
- this.events[event].push(callback);
832
- }
833
- off(event, callback) {
834
- if (callback) {
835
- this.events[event] = this.events[event].filter((fn) => fn !== callback);
836
- }
837
- else {
838
- this.events[event] = [];
839
- }
840
- }
832
+ class Events {
833
+ constructor() {
834
+ this.events = Object.create({});
835
+ }
836
+ emit(event, data) {
837
+ var _a;
838
+ (_a = this.events[event]) === null || _a === void 0 ? void 0 : _a.forEach((fn) => fn(data));
839
+ }
840
+ on(event, callback) {
841
+ this.events[event] = this.events[event] || [];
842
+ this.events[event].push(callback);
843
+ }
844
+ off(event, callback) {
845
+ if (callback) {
846
+ this.events[event] = this.events[event].filter((fn) => fn !== callback);
847
+ }
848
+ else {
849
+ this.events[event] = [];
850
+ }
851
+ }
841
852
  }
842
853
 
843
- const response = (type, data) => {
844
- if (type === 'success') {
845
- return Object.assign({ status: Status.Success }, data);
846
- }
847
- if (type === 'progress') {
848
- return Object.assign({ status: Status.Progress }, data);
849
- }
850
- return Object.assign({ status: Status.Error }, data);
851
- };
852
- class Pusher {
853
- constructor(pusherKey, disconnectTime = 30000) {
854
- this.ws = undefined;
855
- this.queue = [];
856
- this.isConnected = false;
857
- this.subscribers = 0;
858
- this.emmitter = new Events();
859
- this.disconnectTimeoutId = null;
860
- this.key = pusherKey;
861
- this.disconnectTime = disconnectTime;
862
- }
863
- connect() {
864
- this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId);
865
- if (!this.isConnected && !this.ws) {
866
- const pusherUrl = `wss://ws.pusherapp.com/app/${this.key}?protocol=5&client=js&version=1.12.2`;
867
- this.ws = new WebSocket(pusherUrl);
868
- this.ws.addEventListener('error', (error) => {
869
- this.emmitter.emit('error', new Error(error.message));
870
- });
871
- this.emmitter.on('connected', () => {
872
- this.isConnected = true;
873
- this.queue.forEach((message) => this.send(message.event, message.data));
874
- this.queue = [];
875
- });
876
- this.ws.addEventListener('message', (e) => {
877
- const data = JSON.parse(e.data.toString());
878
- switch (data.event) {
879
- case 'pusher:connection_established': {
880
- this.emmitter.emit('connected', undefined);
881
- break;
882
- }
883
- case 'pusher:ping': {
884
- this.send('pusher:pong', {});
885
- break;
886
- }
887
- case 'progress':
888
- case 'success':
889
- case 'fail': {
890
- this.emmitter.emit(data.channel, response(data.event, JSON.parse(data.data)));
891
- }
892
- }
893
- });
894
- }
895
- }
896
- disconnect() {
897
- const actualDisconect = () => {
898
- var _a;
899
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
900
- this.ws = undefined;
901
- this.isConnected = false;
902
- };
903
- if (this.disconnectTime) {
904
- this.disconnectTimeoutId = setTimeout(() => {
905
- actualDisconect();
906
- }, this.disconnectTime);
907
- }
908
- else {
909
- actualDisconect();
910
- }
911
- }
912
- send(event, data) {
913
- var _a;
914
- const str = JSON.stringify({ event, data });
915
- (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(str);
916
- }
917
- subscribe(token, handler) {
918
- this.subscribers += 1;
919
- this.connect();
920
- const channel = `task-status-${token}`;
921
- const message = {
922
- event: 'pusher:subscribe',
923
- data: { channel }
924
- };
925
- this.emmitter.on(channel, handler);
926
- if (this.isConnected) {
927
- this.send(message.event, message.data);
928
- }
929
- else {
930
- this.queue.push(message);
931
- }
932
- }
933
- unsubscribe(token) {
934
- this.subscribers -= 1;
935
- const channel = `task-status-${token}`;
936
- const message = {
937
- event: 'pusher:unsubscribe',
938
- data: { channel }
939
- };
940
- this.emmitter.off(channel);
941
- if (this.isConnected) {
942
- this.send(message.event, message.data);
943
- }
944
- else {
945
- this.queue = this.queue.filter((msg) => msg.data.channel !== channel);
946
- }
947
- if (this.subscribers === 0) {
948
- this.disconnect();
949
- }
950
- }
951
- onError(callback) {
952
- this.emmitter.on('error', callback);
953
- return () => this.emmitter.off('error', callback);
954
- }
955
- }
956
- let pusher = null;
957
- const getPusher = (key) => {
958
- if (!pusher) {
959
- // no timeout for nodeJS and 30000 ms for browser
960
- const disconectTimeout = typeof window === 'undefined' ? 0 : 30000;
961
- pusher = new Pusher(key, disconectTimeout);
962
- }
963
- return pusher;
964
- };
965
- const preconnect = (key) => {
966
- getPusher(key).connect();
854
+ const response = (type, data) => {
855
+ if (type === 'success') {
856
+ return Object.assign({ status: Status.Success }, data);
857
+ }
858
+ if (type === 'progress') {
859
+ return Object.assign({ status: Status.Progress }, data);
860
+ }
861
+ return Object.assign({ status: Status.Error }, data);
862
+ };
863
+ class Pusher {
864
+ constructor(pusherKey, disconnectTime = 30000) {
865
+ this.ws = undefined;
866
+ this.queue = [];
867
+ this.isConnected = false;
868
+ this.subscribers = 0;
869
+ this.emmitter = new Events();
870
+ this.disconnectTimeoutId = null;
871
+ this.key = pusherKey;
872
+ this.disconnectTime = disconnectTime;
873
+ }
874
+ connect() {
875
+ this.disconnectTimeoutId && clearTimeout(this.disconnectTimeoutId);
876
+ if (!this.isConnected && !this.ws) {
877
+ const pusherUrl = `wss://ws.pusherapp.com/app/${this.key}?protocol=5&client=js&version=1.12.2`;
878
+ this.ws = new WebSocket(pusherUrl);
879
+ this.ws.addEventListener('error', (error) => {
880
+ this.emmitter.emit('error', new Error(error.message));
881
+ });
882
+ this.emmitter.on('connected', () => {
883
+ this.isConnected = true;
884
+ this.queue.forEach((message) => this.send(message.event, message.data));
885
+ this.queue = [];
886
+ });
887
+ this.ws.addEventListener('message', (e) => {
888
+ const data = JSON.parse(e.data.toString());
889
+ switch (data.event) {
890
+ case 'pusher:connection_established': {
891
+ this.emmitter.emit('connected', undefined);
892
+ break;
893
+ }
894
+ case 'pusher:ping': {
895
+ this.send('pusher:pong', {});
896
+ break;
897
+ }
898
+ case 'progress':
899
+ case 'success':
900
+ case 'fail': {
901
+ this.emmitter.emit(data.channel, response(data.event, JSON.parse(data.data)));
902
+ }
903
+ }
904
+ });
905
+ }
906
+ }
907
+ disconnect() {
908
+ const actualDisconect = () => {
909
+ var _a;
910
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
911
+ this.ws = undefined;
912
+ this.isConnected = false;
913
+ };
914
+ if (this.disconnectTime) {
915
+ this.disconnectTimeoutId = setTimeout(() => {
916
+ actualDisconect();
917
+ }, this.disconnectTime);
918
+ }
919
+ else {
920
+ actualDisconect();
921
+ }
922
+ }
923
+ send(event, data) {
924
+ var _a;
925
+ const str = JSON.stringify({ event, data });
926
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(str);
927
+ }
928
+ subscribe(token, handler) {
929
+ this.subscribers += 1;
930
+ this.connect();
931
+ const channel = `task-status-${token}`;
932
+ const message = {
933
+ event: 'pusher:subscribe',
934
+ data: { channel }
935
+ };
936
+ this.emmitter.on(channel, handler);
937
+ if (this.isConnected) {
938
+ this.send(message.event, message.data);
939
+ }
940
+ else {
941
+ this.queue.push(message);
942
+ }
943
+ }
944
+ unsubscribe(token) {
945
+ this.subscribers -= 1;
946
+ const channel = `task-status-${token}`;
947
+ const message = {
948
+ event: 'pusher:unsubscribe',
949
+ data: { channel }
950
+ };
951
+ this.emmitter.off(channel);
952
+ if (this.isConnected) {
953
+ this.send(message.event, message.data);
954
+ }
955
+ else {
956
+ this.queue = this.queue.filter((msg) => msg.data.channel !== channel);
957
+ }
958
+ if (this.subscribers === 0) {
959
+ this.disconnect();
960
+ }
961
+ }
962
+ onError(callback) {
963
+ this.emmitter.on('error', callback);
964
+ return () => this.emmitter.off('error', callback);
965
+ }
966
+ }
967
+ let pusher = null;
968
+ const getPusher = (key) => {
969
+ if (!pusher) {
970
+ // no timeout for nodeJS and 30000 ms for browser
971
+ const disconectTimeout = typeof window === 'undefined' ? 0 : 30000;
972
+ pusher = new Pusher(key, disconectTimeout);
973
+ }
974
+ return pusher;
975
+ };
976
+ const preconnect = (key) => {
977
+ getPusher(key).connect();
967
978
  };
968
979
 
969
- function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retryThrottledRequestMaxTimes, onProgress, signal }) {
970
- return poll({
971
- check: (signal) => fromUrlStatus(token, {
972
- publicKey,
973
- baseURL,
974
- integration,
975
- userAgent,
976
- retryThrottledRequestMaxTimes,
977
- signal
978
- }).then((response) => {
979
- switch (response.status) {
980
- case Status.Error: {
981
- return new UploadClientError(response.error, response.errorCode);
982
- }
983
- case Status.Waiting: {
984
- return false;
985
- }
986
- case Status.Unknown: {
987
- return new UploadClientError(`Token "${token}" was not found.`);
988
- }
989
- case Status.Progress: {
990
- if (onProgress) {
991
- if (response.total === 'unknown') {
992
- onProgress({ isComputable: false });
993
- }
994
- else {
995
- onProgress({
996
- isComputable: true,
997
- value: response.done / response.total
998
- });
999
- }
1000
- }
1001
- return false;
1002
- }
1003
- case Status.Success: {
1004
- if (onProgress)
1005
- onProgress({
1006
- isComputable: true,
1007
- value: response.done / response.total
1008
- });
1009
- return response;
1010
- }
1011
- default: {
1012
- throw new Error('Unknown status');
1013
- }
1014
- }
1015
- }),
1016
- signal
1017
- });
1018
- }
1019
- const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((resolve, reject) => {
1020
- const pusher = getPusher(pusherKey);
1021
- const unsubErrorHandler = pusher.onError(reject);
1022
- const destroy = () => {
1023
- unsubErrorHandler();
1024
- pusher.unsubscribe(token);
1025
- };
1026
- onCancel(signal, () => {
1027
- destroy();
1028
- reject(cancelError('pusher cancelled'));
1029
- });
1030
- pusher.subscribe(token, (result) => {
1031
- switch (result.status) {
1032
- case Status.Progress: {
1033
- if (onProgress) {
1034
- if (result.total === 'unknown') {
1035
- onProgress({ isComputable: false });
1036
- }
1037
- else {
1038
- onProgress({
1039
- isComputable: true,
1040
- value: result.done / result.total
1041
- });
1042
- }
1043
- }
1044
- break;
1045
- }
1046
- case Status.Success: {
1047
- destroy();
1048
- if (onProgress)
1049
- onProgress({
1050
- isComputable: true,
1051
- value: result.done / result.total
1052
- });
1053
- resolve(result);
1054
- break;
1055
- }
1056
- case Status.Error: {
1057
- destroy();
1058
- reject(new UploadClientError(result.msg, result.error_code));
1059
- }
1060
- }
1061
- });
1062
- });
1063
- 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))
1064
- .then(() => fromUrl(sourceUrl, {
1065
- publicKey,
1066
- fileName,
1067
- baseURL,
1068
- checkForUrlDuplicates,
1069
- saveUrlForRecurrentUploads,
1070
- secureSignature,
1071
- secureExpire,
1072
- store,
1073
- signal,
1074
- source,
1075
- integration,
1076
- userAgent,
1077
- retryThrottledRequestMaxTimes,
1078
- metadata
1079
- }))
1080
- .catch((error) => {
1081
- const pusher = getPusher(pusherKey);
1082
- pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
1083
- return Promise.reject(error);
1084
- })
1085
- .then((urlResponse) => {
1086
- if (urlResponse.type === TypeEnum.FileInfo) {
1087
- return urlResponse;
1088
- }
1089
- else {
1090
- return race([
1091
- ({ signal }) => pollStrategy({
1092
- token: urlResponse.token,
1093
- publicKey,
1094
- baseURL,
1095
- integration,
1096
- userAgent,
1097
- retryThrottledRequestMaxTimes,
1098
- onProgress,
1099
- signal
1100
- }),
1101
- ({ signal }) => pushStrategy({
1102
- token: urlResponse.token,
1103
- pusherKey,
1104
- signal,
1105
- onProgress
1106
- })
1107
- ], { signal });
1108
- }
1109
- })
1110
- .then((result) => {
1111
- if (result instanceof UploadClientError)
1112
- throw result;
1113
- return result;
1114
- })
1115
- .then((result) => isReadyPoll({
1116
- file: result.uuid,
1117
- publicKey,
1118
- baseURL,
1119
- integration,
1120
- userAgent,
1121
- retryThrottledRequestMaxTimes,
1122
- onProgress,
1123
- signal
1124
- }))
980
+ function pollStrategy({ token, publicKey, baseURL, integration, userAgent, retryThrottledRequestMaxTimes, onProgress, signal }) {
981
+ return poll({
982
+ check: (signal) => fromUrlStatus(token, {
983
+ publicKey,
984
+ baseURL,
985
+ integration,
986
+ userAgent,
987
+ retryThrottledRequestMaxTimes,
988
+ signal
989
+ }).then((response) => {
990
+ switch (response.status) {
991
+ case Status.Error: {
992
+ return new UploadClientError(response.error, response.errorCode);
993
+ }
994
+ case Status.Waiting: {
995
+ return false;
996
+ }
997
+ case Status.Unknown: {
998
+ return new UploadClientError(`Token "${token}" was not found.`);
999
+ }
1000
+ case Status.Progress: {
1001
+ if (onProgress) {
1002
+ if (response.total === 'unknown') {
1003
+ onProgress({ isComputable: false });
1004
+ }
1005
+ else {
1006
+ onProgress({
1007
+ isComputable: true,
1008
+ value: response.done / response.total
1009
+ });
1010
+ }
1011
+ }
1012
+ return false;
1013
+ }
1014
+ case Status.Success: {
1015
+ if (onProgress)
1016
+ onProgress({
1017
+ isComputable: true,
1018
+ value: response.done / response.total
1019
+ });
1020
+ return response;
1021
+ }
1022
+ default: {
1023
+ throw new Error('Unknown status');
1024
+ }
1025
+ }
1026
+ }),
1027
+ signal
1028
+ });
1029
+ }
1030
+ const pushStrategy = ({ token, pusherKey, signal, onProgress }) => new Promise((resolve, reject) => {
1031
+ const pusher = getPusher(pusherKey);
1032
+ const unsubErrorHandler = pusher.onError(reject);
1033
+ const destroy = () => {
1034
+ unsubErrorHandler();
1035
+ pusher.unsubscribe(token);
1036
+ };
1037
+ onCancel(signal, () => {
1038
+ destroy();
1039
+ reject(cancelError('pusher cancelled'));
1040
+ });
1041
+ pusher.subscribe(token, (result) => {
1042
+ switch (result.status) {
1043
+ case Status.Progress: {
1044
+ if (onProgress) {
1045
+ if (result.total === 'unknown') {
1046
+ onProgress({ isComputable: false });
1047
+ }
1048
+ else {
1049
+ onProgress({
1050
+ isComputable: true,
1051
+ value: result.done / result.total
1052
+ });
1053
+ }
1054
+ }
1055
+ break;
1056
+ }
1057
+ case Status.Success: {
1058
+ destroy();
1059
+ if (onProgress)
1060
+ onProgress({
1061
+ isComputable: true,
1062
+ value: result.done / result.total
1063
+ });
1064
+ resolve(result);
1065
+ break;
1066
+ }
1067
+ case Status.Error: {
1068
+ destroy();
1069
+ reject(new UploadClientError(result.msg, result.error_code));
1070
+ }
1071
+ }
1072
+ });
1073
+ });
1074
+ 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))
1075
+ .then(() => fromUrl(sourceUrl, {
1076
+ publicKey,
1077
+ fileName,
1078
+ baseURL,
1079
+ checkForUrlDuplicates,
1080
+ saveUrlForRecurrentUploads,
1081
+ secureSignature,
1082
+ secureExpire,
1083
+ store,
1084
+ signal,
1085
+ source,
1086
+ integration,
1087
+ userAgent,
1088
+ retryThrottledRequestMaxTimes,
1089
+ metadata
1090
+ }))
1091
+ .catch((error) => {
1092
+ const pusher = getPusher(pusherKey);
1093
+ pusher === null || pusher === void 0 ? void 0 : pusher.disconnect();
1094
+ return Promise.reject(error);
1095
+ })
1096
+ .then((urlResponse) => {
1097
+ if (urlResponse.type === TypeEnum.FileInfo) {
1098
+ return urlResponse;
1099
+ }
1100
+ else {
1101
+ return race([
1102
+ ({ signal }) => pollStrategy({
1103
+ token: urlResponse.token,
1104
+ publicKey,
1105
+ baseURL,
1106
+ integration,
1107
+ userAgent,
1108
+ retryThrottledRequestMaxTimes,
1109
+ onProgress,
1110
+ signal
1111
+ }),
1112
+ ({ signal }) => pushStrategy({
1113
+ token: urlResponse.token,
1114
+ pusherKey,
1115
+ signal,
1116
+ onProgress
1117
+ })
1118
+ ], { signal });
1119
+ }
1120
+ })
1121
+ .then((result) => {
1122
+ if (result instanceof UploadClientError)
1123
+ throw result;
1124
+ return result;
1125
+ })
1126
+ .then((result) => isReadyPoll({
1127
+ file: result.uuid,
1128
+ publicKey,
1129
+ baseURL,
1130
+ integration,
1131
+ userAgent,
1132
+ retryThrottledRequestMaxTimes,
1133
+ onProgress,
1134
+ signal
1135
+ }))
1125
1136
  .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
1126
1137
 
1127
- const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
1128
- return info(uuid, {
1129
- publicKey,
1130
- baseURL,
1131
- signal,
1132
- source,
1133
- integration,
1134
- userAgent,
1135
- retryThrottledRequestMaxTimes
1136
- })
1137
- .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
1138
- .then((result) => {
1139
- // hack for node ¯\_(ツ)_/¯
1140
- if (onProgress)
1141
- onProgress({
1142
- isComputable: true,
1143
- value: 1
1144
- });
1145
- return result;
1146
- });
1138
+ const uploadFromUploaded = (uuid, { publicKey, fileName, baseURL, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, baseCDN }) => {
1139
+ return info(uuid, {
1140
+ publicKey,
1141
+ baseURL,
1142
+ signal,
1143
+ source,
1144
+ integration,
1145
+ userAgent,
1146
+ retryThrottledRequestMaxTimes
1147
+ })
1148
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN, fileName }))
1149
+ .then((result) => {
1150
+ // hack for node ¯\_(ツ)_/¯
1151
+ if (onProgress)
1152
+ onProgress({
1153
+ isComputable: true,
1154
+ value: 1
1155
+ });
1156
+ return result;
1157
+ });
1147
1158
  };
1148
1159
 
1149
- /**
1150
- * Get file size.
1151
- */
1152
- const getFileSize = (file) => {
1153
- return file.length || file.size;
1154
- };
1155
- /**
1156
- * Check if FileData is multipart data.
1157
- */
1158
- const isMultipart = (fileSize, multipartMinFileSize = defaultSettings.multipartMinFileSize) => {
1159
- return fileSize >= multipartMinFileSize;
1160
+ /**
1161
+ * Get file size.
1162
+ */
1163
+ const getFileSize = (file) => {
1164
+ return file.length || file.size;
1165
+ };
1166
+ /**
1167
+ * Check if FileData is multipart data.
1168
+ */
1169
+ const isMultipart = (fileSize, multipartMinFileSize = defaultSettings.multipartMinFileSize) => {
1170
+ return fileSize >= multipartMinFileSize;
1160
1171
  };
1161
1172
 
1162
- const sliceChunk = (file, index, fileSize, chunkSize) => {
1163
- const start = chunkSize * index;
1164
- const end = Math.min(start + chunkSize, fileSize);
1165
- return file.slice(start, end);
1173
+ const sliceChunk = (file, index, fileSize, chunkSize) => {
1174
+ const start = chunkSize * index;
1175
+ const end = Math.min(start + chunkSize, fileSize);
1176
+ return file.slice(start, end);
1166
1177
  };
1167
1178
 
1168
- function prepareChunks(file, fileSize, chunkSize) {
1169
- return (index) => sliceChunk(file, index, fileSize, chunkSize);
1179
+ function prepareChunks(file, fileSize, chunkSize) {
1180
+ return (index) => sliceChunk(file, index, fileSize, chunkSize);
1170
1181
  }
1171
1182
 
1172
- const runWithConcurrency = (concurrency, tasks) => {
1173
- return new Promise((resolve, reject) => {
1174
- const results = [];
1175
- let rejected = false;
1176
- let settled = tasks.length;
1177
- const forRun = [...tasks];
1178
- const run = () => {
1179
- const index = tasks.length - forRun.length;
1180
- const next = forRun.shift();
1181
- if (next) {
1182
- next()
1183
- .then((result) => {
1184
- if (rejected)
1185
- return;
1186
- results[index] = result;
1187
- settled -= 1;
1188
- if (settled) {
1189
- run();
1190
- }
1191
- else {
1192
- resolve(results);
1193
- }
1194
- })
1195
- .catch((error) => {
1196
- rejected = true;
1197
- reject(error);
1198
- });
1199
- }
1200
- };
1201
- for (let i = 0; i < concurrency; i++) {
1202
- run();
1203
- }
1204
- });
1183
+ const runWithConcurrency = (concurrency, tasks) => {
1184
+ return new Promise((resolve, reject) => {
1185
+ const results = [];
1186
+ let rejected = false;
1187
+ let settled = tasks.length;
1188
+ const forRun = [...tasks];
1189
+ const run = () => {
1190
+ const index = tasks.length - forRun.length;
1191
+ const next = forRun.shift();
1192
+ if (next) {
1193
+ next()
1194
+ .then((result) => {
1195
+ if (rejected)
1196
+ return;
1197
+ results[index] = result;
1198
+ settled -= 1;
1199
+ if (settled) {
1200
+ run();
1201
+ }
1202
+ else {
1203
+ resolve(results);
1204
+ }
1205
+ })
1206
+ .catch((error) => {
1207
+ rejected = true;
1208
+ reject(error);
1209
+ });
1210
+ }
1211
+ };
1212
+ for (let i = 0; i < concurrency; i++) {
1213
+ run();
1214
+ }
1215
+ });
1205
1216
  };
1206
1217
 
1207
- const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integration, multipartMaxAttempts }) => retrier(({ attempt, retry }) => multipartUpload(chunk, url, {
1208
- publicKey,
1209
- onProgress,
1210
- signal,
1211
- integration
1212
- }).catch((error) => {
1213
- if (attempt < multipartMaxAttempts) {
1214
- return retry();
1215
- }
1216
- throw error;
1217
- }));
1218
- 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 }) => {
1219
- const size = fileSize || getFileSize(file);
1220
- let progressValues;
1221
- const createProgressHandler = (totalChunks, chunkIdx) => {
1222
- if (!onProgress)
1223
- return;
1224
- if (!progressValues) {
1225
- progressValues = Array(totalChunks).fill(0);
1226
- }
1227
- const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1228
- return (info) => {
1229
- if (!info.isComputable) {
1230
- return;
1231
- }
1232
- progressValues[chunkIdx] = info.value;
1233
- onProgress({
1234
- isComputable: true,
1235
- value: sum(progressValues) / totalChunks
1236
- });
1237
- };
1238
- };
1239
- return multipartStart(size, {
1240
- publicKey,
1241
- contentType,
1242
- fileName: fileName !== null && fileName !== void 0 ? fileName : file.name,
1243
- baseURL,
1244
- secureSignature,
1245
- secureExpire,
1246
- store,
1247
- signal,
1248
- source,
1249
- integration,
1250
- userAgent,
1251
- retryThrottledRequestMaxTimes,
1252
- metadata
1253
- })
1254
- .then(({ uuid, parts }) => {
1255
- const getChunk = prepareChunks(file, size, multipartChunkSize);
1256
- return Promise.all([
1257
- uuid,
1258
- runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPartWithRetry(getChunk(index), url, {
1259
- publicKey,
1260
- onProgress: createProgressHandler(parts.length, index),
1261
- signal,
1262
- integration,
1263
- multipartMaxAttempts
1264
- })))
1265
- ]);
1266
- })
1267
- .then(([uuid]) => multipartComplete(uuid, {
1268
- publicKey,
1269
- baseURL,
1270
- source,
1271
- integration,
1272
- userAgent,
1273
- retryThrottledRequestMaxTimes
1274
- }))
1275
- .then((fileInfo) => {
1276
- if (fileInfo.isReady) {
1277
- return fileInfo;
1278
- }
1279
- else {
1280
- return isReadyPoll({
1281
- file: fileInfo.uuid,
1282
- publicKey,
1283
- baseURL,
1284
- source,
1285
- integration,
1286
- userAgent,
1287
- retryThrottledRequestMaxTimes,
1288
- onProgress,
1289
- signal
1290
- });
1291
- }
1292
- })
1293
- .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
1218
+ const uploadPartWithRetry = (chunk, url, { publicKey, onProgress, signal, integration, multipartMaxAttempts }) => retrier(({ attempt, retry }) => multipartUpload(chunk, url, {
1219
+ publicKey,
1220
+ onProgress,
1221
+ signal,
1222
+ integration
1223
+ }).catch((error) => {
1224
+ if (attempt < multipartMaxAttempts) {
1225
+ return retry();
1226
+ }
1227
+ throw error;
1228
+ }));
1229
+ 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 }) => {
1230
+ const size = fileSize || getFileSize(file);
1231
+ let progressValues;
1232
+ const createProgressHandler = (totalChunks, chunkIdx) => {
1233
+ if (!onProgress)
1234
+ return;
1235
+ if (!progressValues) {
1236
+ progressValues = Array(totalChunks).fill(0);
1237
+ }
1238
+ const sum = (values) => values.reduce((sum, next) => sum + next, 0);
1239
+ return (info) => {
1240
+ if (!info.isComputable) {
1241
+ return;
1242
+ }
1243
+ progressValues[chunkIdx] = info.value;
1244
+ onProgress({
1245
+ isComputable: true,
1246
+ value: sum(progressValues) / totalChunks
1247
+ });
1248
+ };
1249
+ };
1250
+ return multipartStart(size, {
1251
+ publicKey,
1252
+ contentType,
1253
+ fileName: fileName !== null && fileName !== void 0 ? fileName : file.name,
1254
+ baseURL,
1255
+ secureSignature,
1256
+ secureExpire,
1257
+ store,
1258
+ signal,
1259
+ source,
1260
+ integration,
1261
+ userAgent,
1262
+ retryThrottledRequestMaxTimes,
1263
+ metadata
1264
+ })
1265
+ .then(({ uuid, parts }) => {
1266
+ const getChunk = prepareChunks(file, size, multipartChunkSize);
1267
+ return Promise.all([
1268
+ uuid,
1269
+ runWithConcurrency(maxConcurrentRequests, parts.map((url, index) => () => uploadPartWithRetry(getChunk(index), url, {
1270
+ publicKey,
1271
+ onProgress: createProgressHandler(parts.length, index),
1272
+ signal,
1273
+ integration,
1274
+ multipartMaxAttempts
1275
+ })))
1276
+ ]);
1277
+ })
1278
+ .then(([uuid]) => multipartComplete(uuid, {
1279
+ publicKey,
1280
+ baseURL,
1281
+ source,
1282
+ integration,
1283
+ userAgent,
1284
+ retryThrottledRequestMaxTimes
1285
+ }))
1286
+ .then((fileInfo) => {
1287
+ if (fileInfo.isReady) {
1288
+ return fileInfo;
1289
+ }
1290
+ else {
1291
+ return isReadyPoll({
1292
+ file: fileInfo.uuid,
1293
+ publicKey,
1294
+ baseURL,
1295
+ source,
1296
+ integration,
1297
+ userAgent,
1298
+ retryThrottledRequestMaxTimes,
1299
+ onProgress,
1300
+ signal
1301
+ });
1302
+ }
1303
+ })
1304
+ .then((fileInfo) => new UploadcareFile(fileInfo, { baseCDN }));
1294
1305
  };
1295
1306
 
1296
- /**
1297
- * Uploads file from provided data.
1298
- */
1299
- 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 }) {
1300
- if (isFileData(data)) {
1301
- const fileSize = getFileSize(data);
1302
- if (isMultipart(fileSize, multipartMinFileSize)) {
1303
- return uploadMultipart(data, {
1304
- publicKey,
1305
- contentType,
1306
- multipartChunkSize,
1307
- multipartMaxAttempts,
1308
- fileName,
1309
- baseURL,
1310
- secureSignature,
1311
- secureExpire,
1312
- store,
1313
- signal,
1314
- onProgress,
1315
- source,
1316
- integration,
1317
- userAgent,
1318
- maxConcurrentRequests,
1319
- retryThrottledRequestMaxTimes,
1320
- baseCDN,
1321
- metadata
1322
- });
1323
- }
1324
- return uploadFromObject(data, {
1325
- publicKey,
1326
- fileName,
1327
- baseURL,
1328
- secureSignature,
1329
- secureExpire,
1330
- store,
1331
- signal,
1332
- onProgress,
1333
- source,
1334
- integration,
1335
- userAgent,
1336
- retryThrottledRequestMaxTimes,
1337
- baseCDN,
1338
- metadata
1339
- });
1340
- }
1341
- if (isUrl(data)) {
1342
- return uploadFromUrl(data, {
1343
- publicKey,
1344
- fileName,
1345
- baseURL,
1346
- baseCDN,
1347
- checkForUrlDuplicates,
1348
- saveUrlForRecurrentUploads,
1349
- secureSignature,
1350
- secureExpire,
1351
- store,
1352
- signal,
1353
- onProgress,
1354
- source,
1355
- integration,
1356
- userAgent,
1357
- retryThrottledRequestMaxTimes,
1358
- pusherKey,
1359
- metadata
1360
- });
1361
- }
1362
- if (isUuid(data)) {
1363
- return uploadFromUploaded(data, {
1364
- publicKey,
1365
- fileName,
1366
- baseURL,
1367
- signal,
1368
- onProgress,
1369
- source,
1370
- integration,
1371
- userAgent,
1372
- retryThrottledRequestMaxTimes,
1373
- baseCDN
1374
- });
1375
- }
1376
- throw new TypeError(`File uploading from "${data}" is not supported`);
1307
+ /**
1308
+ * Uploads file from provided data.
1309
+ */
1310
+ 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 }) {
1311
+ if (isFileData(data)) {
1312
+ const fileSize = getFileSize(data);
1313
+ if (isMultipart(fileSize, multipartMinFileSize)) {
1314
+ return uploadMultipart(data, {
1315
+ publicKey,
1316
+ contentType,
1317
+ multipartChunkSize,
1318
+ multipartMaxAttempts,
1319
+ fileName,
1320
+ baseURL,
1321
+ secureSignature,
1322
+ secureExpire,
1323
+ store,
1324
+ signal,
1325
+ onProgress,
1326
+ source,
1327
+ integration,
1328
+ userAgent,
1329
+ maxConcurrentRequests,
1330
+ retryThrottledRequestMaxTimes,
1331
+ baseCDN,
1332
+ metadata
1333
+ });
1334
+ }
1335
+ return uploadDirect(data, {
1336
+ publicKey,
1337
+ fileName,
1338
+ contentType,
1339
+ baseURL,
1340
+ secureSignature,
1341
+ secureExpire,
1342
+ store,
1343
+ signal,
1344
+ onProgress,
1345
+ source,
1346
+ integration,
1347
+ userAgent,
1348
+ retryThrottledRequestMaxTimes,
1349
+ baseCDN,
1350
+ metadata
1351
+ });
1352
+ }
1353
+ if (isUrl(data)) {
1354
+ return uploadFromUrl(data, {
1355
+ publicKey,
1356
+ fileName,
1357
+ baseURL,
1358
+ baseCDN,
1359
+ checkForUrlDuplicates,
1360
+ saveUrlForRecurrentUploads,
1361
+ secureSignature,
1362
+ secureExpire,
1363
+ store,
1364
+ signal,
1365
+ onProgress,
1366
+ source,
1367
+ integration,
1368
+ userAgent,
1369
+ retryThrottledRequestMaxTimes,
1370
+ pusherKey,
1371
+ metadata
1372
+ });
1373
+ }
1374
+ if (isUuid(data)) {
1375
+ return uploadFromUploaded(data, {
1376
+ publicKey,
1377
+ fileName,
1378
+ baseURL,
1379
+ signal,
1380
+ onProgress,
1381
+ source,
1382
+ integration,
1383
+ userAgent,
1384
+ retryThrottledRequestMaxTimes,
1385
+ baseCDN
1386
+ });
1387
+ }
1388
+ throw new TypeError(`File uploading from "${data}" is not supported`);
1377
1389
  }
1378
1390
 
1379
- class UploadcareGroup {
1380
- constructor(groupInfo, files) {
1381
- this.storedAt = null;
1382
- this.uuid = groupInfo.id;
1383
- this.filesCount = groupInfo.filesCount;
1384
- this.totalSize = Object.values(groupInfo.files).reduce((acc, file) => acc + file.size, 0);
1385
- this.isStored = !!groupInfo.datetimeStored;
1386
- this.isImage = !!Object.values(groupInfo.files).filter((file) => file.isImage).length;
1387
- this.cdnUrl = groupInfo.cdnUrl;
1388
- this.files = files;
1389
- this.createdAt = groupInfo.datetimeCreated;
1390
- this.storedAt = groupInfo.datetimeStored;
1391
- }
1391
+ class UploadcareGroup {
1392
+ constructor(groupInfo, files) {
1393
+ this.storedAt = null;
1394
+ this.uuid = groupInfo.id;
1395
+ this.filesCount = groupInfo.filesCount;
1396
+ this.totalSize = Object.values(groupInfo.files).reduce((acc, file) => acc + file.size, 0);
1397
+ this.isStored = !!groupInfo.datetimeStored;
1398
+ this.isImage = !!Object.values(groupInfo.files).filter((file) => file.isImage).length;
1399
+ this.cdnUrl = groupInfo.cdnUrl;
1400
+ this.files = files;
1401
+ this.createdAt = groupInfo.datetimeCreated;
1402
+ this.storedAt = groupInfo.datetimeStored;
1403
+ }
1392
1404
  }
1393
1405
 
1394
- /**
1395
- * FileData type guard.
1396
- */
1397
- const isFileDataArray = (data) => {
1398
- for (const item of data) {
1399
- if (!isFileData(item)) {
1400
- return false;
1401
- }
1402
- }
1403
- return true;
1404
- };
1405
- /**
1406
- * Uuid type guard.
1407
- */
1408
- const isUuidArray = (data) => {
1409
- for (const item of data) {
1410
- if (!isUuid(item)) {
1411
- return false;
1412
- }
1413
- }
1414
- return true;
1415
- };
1416
- /**
1417
- * Url type guard.
1418
- */
1419
- const isUrlArray = (data) => {
1420
- for (const item of data) {
1421
- if (!isUrl(item)) {
1422
- return false;
1423
- }
1424
- }
1425
- return true;
1406
+ /**
1407
+ * FileData type guard.
1408
+ */
1409
+ const isFileDataArray = (data) => {
1410
+ for (const item of data) {
1411
+ if (!isFileData(item)) {
1412
+ return false;
1413
+ }
1414
+ }
1415
+ return true;
1416
+ };
1417
+ /**
1418
+ * Uuid type guard.
1419
+ */
1420
+ const isUuidArray = (data) => {
1421
+ for (const item of data) {
1422
+ if (!isUuid(item)) {
1423
+ return false;
1424
+ }
1425
+ }
1426
+ return true;
1427
+ };
1428
+ /**
1429
+ * Url type guard.
1430
+ */
1431
+ const isUrlArray = (data) => {
1432
+ for (const item of data) {
1433
+ if (!isUrl(item)) {
1434
+ return false;
1435
+ }
1436
+ }
1437
+ return true;
1426
1438
  };
1427
1439
 
1428
- function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, baseCDN = defaultSettings.baseCDN, jsonpCallback, defaultEffects }) {
1429
- if (!isFileDataArray(data) && !isUrlArray(data) && !isUuidArray(data)) {
1430
- throw new TypeError(`Group uploading from "${data}" is not supported`);
1431
- }
1432
- let progressValues;
1433
- let isStillComputable = true;
1434
- const filesCount = data.length;
1435
- const createProgressHandler = (size, index) => {
1436
- if (!onProgress)
1437
- return;
1438
- if (!progressValues) {
1439
- progressValues = Array(size).fill(0);
1440
- }
1441
- const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
1442
- return (info) => {
1443
- if (!info.isComputable || !isStillComputable) {
1444
- isStillComputable = false;
1445
- onProgress({ isComputable: false });
1446
- return;
1447
- }
1448
- progressValues[index] = info.value;
1449
- onProgress({ isComputable: true, value: normalize(progressValues) });
1450
- };
1451
- };
1452
- return Promise.all(data.map((file, index) => uploadFile(file, {
1453
- publicKey,
1454
- fileName,
1455
- baseURL,
1456
- secureSignature,
1457
- secureExpire,
1458
- store,
1459
- signal,
1460
- onProgress: createProgressHandler(filesCount, index),
1461
- source,
1462
- integration,
1463
- userAgent,
1464
- retryThrottledRequestMaxTimes,
1465
- contentType,
1466
- multipartChunkSize,
1467
- baseCDN
1468
- }))).then((files) => {
1469
- const uuids = files.map((file) => file.uuid);
1470
- const addDefaultEffects = (file) => {
1471
- const cdnUrlModifiers = defaultEffects ? `-/${defaultEffects}` : null;
1472
- const cdnUrl = `${file.urlBase}${cdnUrlModifiers || ''}`;
1473
- return Object.assign(Object.assign({}, file), { cdnUrlModifiers,
1474
- cdnUrl });
1475
- };
1476
- const filesInGroup = defaultEffects ? files.map(addDefaultEffects) : files;
1477
- return group(uuids, {
1478
- publicKey,
1479
- baseURL,
1480
- jsonpCallback,
1481
- secureSignature,
1482
- secureExpire,
1483
- signal,
1484
- source,
1485
- integration,
1486
- userAgent,
1487
- retryThrottledRequestMaxTimes
1488
- })
1489
- .then((groupInfo) => new UploadcareGroup(groupInfo, filesInGroup))
1490
- .then((group) => {
1491
- onProgress && onProgress({ isComputable: true, value: 1 });
1492
- return group;
1493
- });
1494
- });
1440
+ function uploadFileGroup(data, { publicKey, fileName, baseURL = defaultSettings.baseURL, secureSignature, secureExpire, store, signal, onProgress, source, integration, userAgent, retryThrottledRequestMaxTimes, contentType, multipartChunkSize = defaultSettings.multipartChunkSize, baseCDN = defaultSettings.baseCDN, jsonpCallback }) {
1441
+ if (!isFileDataArray(data) && !isUrlArray(data) && !isUuidArray(data)) {
1442
+ throw new TypeError(`Group uploading from "${data}" is not supported`);
1443
+ }
1444
+ let progressValues;
1445
+ let isStillComputable = true;
1446
+ const filesCount = data.length;
1447
+ const createProgressHandler = (size, index) => {
1448
+ if (!onProgress)
1449
+ return;
1450
+ if (!progressValues) {
1451
+ progressValues = Array(size).fill(0);
1452
+ }
1453
+ const normalize = (values) => values.reduce((sum, next) => sum + next) / size;
1454
+ return (info) => {
1455
+ if (!info.isComputable || !isStillComputable) {
1456
+ isStillComputable = false;
1457
+ onProgress({ isComputable: false });
1458
+ return;
1459
+ }
1460
+ progressValues[index] = info.value;
1461
+ onProgress({ isComputable: true, value: normalize(progressValues) });
1462
+ };
1463
+ };
1464
+ return Promise.all(data.map((file, index) => uploadFile(file, {
1465
+ publicKey,
1466
+ fileName,
1467
+ baseURL,
1468
+ secureSignature,
1469
+ secureExpire,
1470
+ store,
1471
+ signal,
1472
+ onProgress: createProgressHandler(filesCount, index),
1473
+ source,
1474
+ integration,
1475
+ userAgent,
1476
+ retryThrottledRequestMaxTimes,
1477
+ contentType,
1478
+ multipartChunkSize,
1479
+ baseCDN
1480
+ }))).then((files) => {
1481
+ const uuids = files.map((file) => file.uuid);
1482
+ return group(uuids, {
1483
+ publicKey,
1484
+ baseURL,
1485
+ jsonpCallback,
1486
+ secureSignature,
1487
+ secureExpire,
1488
+ signal,
1489
+ source,
1490
+ integration,
1491
+ userAgent,
1492
+ retryThrottledRequestMaxTimes
1493
+ })
1494
+ .then((groupInfo) => new UploadcareGroup(groupInfo, files))
1495
+ .then((group) => {
1496
+ onProgress && onProgress({ isComputable: true, value: 1 });
1497
+ return group;
1498
+ });
1499
+ });
1495
1500
  }
1496
1501
 
1497
- /**
1498
- * Populate options with settings.
1499
- */
1500
- const populateOptionsWithSettings = (options, settings) => (Object.assign(Object.assign({}, settings), options));
1501
- class UploadClient {
1502
- constructor(settings) {
1503
- this.settings = Object.assign({}, defaultSettings, settings);
1504
- }
1505
- updateSettings(newSettings) {
1506
- this.settings = Object.assign(this.settings, newSettings);
1507
- }
1508
- getSettings() {
1509
- return this.settings;
1510
- }
1511
- base(file, options) {
1512
- const settings = this.getSettings();
1513
- return base(file, populateOptionsWithSettings(options, settings));
1514
- }
1515
- info(uuid, options) {
1516
- const settings = this.getSettings();
1517
- return info(uuid, populateOptionsWithSettings(options, settings));
1518
- }
1519
- fromUrl(sourceUrl, options) {
1520
- const settings = this.getSettings();
1521
- return fromUrl(sourceUrl, populateOptionsWithSettings(options, settings));
1522
- }
1523
- fromUrlStatus(token, options) {
1524
- const settings = this.getSettings();
1525
- return fromUrlStatus(token, populateOptionsWithSettings(options, settings));
1526
- }
1527
- group(uuids, options) {
1528
- const settings = this.getSettings();
1529
- return group(uuids, populateOptionsWithSettings(options, settings));
1530
- }
1531
- groupInfo(id, options) {
1532
- const settings = this.getSettings();
1533
- return groupInfo(id, populateOptionsWithSettings(options, settings));
1534
- }
1535
- multipartStart(size, options) {
1536
- const settings = this.getSettings();
1537
- return multipartStart(size, populateOptionsWithSettings(options, settings));
1538
- }
1539
- multipartUpload(part, url, options) {
1540
- const settings = this.getSettings();
1541
- return multipartUpload(part, url, populateOptionsWithSettings(options, settings));
1542
- }
1543
- multipartComplete(uuid, options) {
1544
- const settings = this.getSettings();
1545
- return multipartComplete(uuid, populateOptionsWithSettings(options, settings));
1546
- }
1547
- uploadFile(data, options) {
1548
- const settings = this.getSettings();
1549
- return uploadFile(data, populateOptionsWithSettings(options, settings));
1550
- }
1551
- uploadFileGroup(data, options) {
1552
- const settings = this.getSettings();
1553
- return uploadFileGroup(data, populateOptionsWithSettings(options, settings));
1554
- }
1502
+ /**
1503
+ * Populate options with settings.
1504
+ */
1505
+ const populateOptionsWithSettings = (options, settings) => (Object.assign(Object.assign({}, settings), options));
1506
+ class UploadClient {
1507
+ constructor(settings) {
1508
+ this.settings = Object.assign({}, defaultSettings, settings);
1509
+ }
1510
+ updateSettings(newSettings) {
1511
+ this.settings = Object.assign(this.settings, newSettings);
1512
+ }
1513
+ getSettings() {
1514
+ return this.settings;
1515
+ }
1516
+ base(file, options = {}) {
1517
+ const settings = this.getSettings();
1518
+ return base(file, populateOptionsWithSettings(options, settings));
1519
+ }
1520
+ info(uuid, options = {}) {
1521
+ const settings = this.getSettings();
1522
+ return info(uuid, populateOptionsWithSettings(options, settings));
1523
+ }
1524
+ fromUrl(sourceUrl, options = {}) {
1525
+ const settings = this.getSettings();
1526
+ return fromUrl(sourceUrl, populateOptionsWithSettings(options, settings));
1527
+ }
1528
+ fromUrlStatus(token, options = {}) {
1529
+ const settings = this.getSettings();
1530
+ return fromUrlStatus(token, populateOptionsWithSettings(options, settings));
1531
+ }
1532
+ group(uuids, options = {}) {
1533
+ const settings = this.getSettings();
1534
+ return group(uuids, populateOptionsWithSettings(options, settings));
1535
+ }
1536
+ groupInfo(id, options = {}) {
1537
+ const settings = this.getSettings();
1538
+ return groupInfo(id, populateOptionsWithSettings(options, settings));
1539
+ }
1540
+ multipartStart(size, options = {}) {
1541
+ const settings = this.getSettings();
1542
+ return multipartStart(size, populateOptionsWithSettings(options, settings));
1543
+ }
1544
+ multipartUpload(part, url, options = {}) {
1545
+ const settings = this.getSettings();
1546
+ return multipartUpload(part, url, populateOptionsWithSettings(options, settings));
1547
+ }
1548
+ multipartComplete(uuid, options = {}) {
1549
+ const settings = this.getSettings();
1550
+ return multipartComplete(uuid, populateOptionsWithSettings(options, settings));
1551
+ }
1552
+ uploadFile(data, options = {}) {
1553
+ const settings = this.getSettings();
1554
+ return uploadFile(data, populateOptionsWithSettings(options, settings));
1555
+ }
1556
+ uploadFileGroup(data, options = {}) {
1557
+ const settings = this.getSettings();
1558
+ return uploadFileGroup(data, populateOptionsWithSettings(options, settings));
1559
+ }
1555
1560
  }
1556
1561
 
1557
- export { UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, base, fromUrl, fromUrlStatus, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadFromObject as uploadBase, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };
1562
+ export { UploadClient, UploadClientError, UploadcareFile, UploadcareGroup, base, fromUrl, fromUrlStatus, group, groupInfo, info, multipartComplete, multipartStart, multipartUpload, uploadDirect, uploadFile, uploadFileGroup, uploadFromUploaded, uploadFromUrl, uploadMultipart };