partial-uploader 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -1
- package/dist/index.js +169 -71
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +190 -0
- package/package.json +6 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
type RefreshResult = {
|
|
2
|
+
headers?: any;
|
|
3
|
+
url?: string;
|
|
4
|
+
};
|
|
5
|
+
type UnauthorizedCallback = (error: Response) => Promise<RefreshResult | null>;
|
|
6
|
+
declare const uploadWithPartialFile: (url: string, file: any, headers?: any, chunkSize?: number, delay_number?: number, concurrency?: number, onUnauthorized?: UnauthorizedCallback) => Promise<{
|
|
2
7
|
success: boolean;
|
|
3
8
|
id: string;
|
|
4
9
|
message: string;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
14
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
15
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,85 +19,172 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
19
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
20
|
});
|
|
10
21
|
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
11
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
50
|
exports.uploadWithPartialFile = void 0;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
51
|
+
var uploadWithPartialFile = function (url, file, headers, chunkSize, delay_number, concurrency, onUnauthorized) {
|
|
52
|
+
if (headers === void 0) { headers = {}; }
|
|
53
|
+
if (chunkSize === void 0) { chunkSize = 26214400; }
|
|
54
|
+
if (delay_number === void 0) { delay_number = 50; }
|
|
55
|
+
if (concurrency === void 0) { concurrency = 1; }
|
|
56
|
+
return __awaiter(void 0, void 0, void 0, function () {
|
|
57
|
+
var sharedContext, id, fileName, totalSize, totalChunks, uploadChunk, queue_1, workers, e_1;
|
|
58
|
+
return __generator(this, function (_a) {
|
|
59
|
+
switch (_a.label) {
|
|
60
|
+
case 0:
|
|
61
|
+
sharedContext = {
|
|
62
|
+
url: url,
|
|
63
|
+
headers: __assign({}, headers)
|
|
64
|
+
};
|
|
65
|
+
return [4 /*yield*/, delay(delay_number)];
|
|
66
|
+
case 1:
|
|
67
|
+
_a.sent();
|
|
68
|
+
id = generateGuid();
|
|
69
|
+
fileName = file.name || 'file';
|
|
70
|
+
totalSize = file.size;
|
|
71
|
+
totalChunks = Math.ceil(totalSize / chunkSize) || 1;
|
|
72
|
+
uploadChunk = function (index) { return __awaiter(void 0, void 0, void 0, function () {
|
|
73
|
+
var start, end, chunk, retry, formData, res, refreshData, e_2;
|
|
74
|
+
return __generator(this, function (_a) {
|
|
75
|
+
switch (_a.label) {
|
|
76
|
+
case 0:
|
|
77
|
+
start = index * chunkSize;
|
|
78
|
+
end = Math.min(start + chunkSize, totalSize);
|
|
79
|
+
chunk = file.slice(start, end);
|
|
80
|
+
retry = 0;
|
|
81
|
+
_a.label = 1;
|
|
82
|
+
case 1:
|
|
83
|
+
if (!(retry < 3)) return [3 /*break*/, 9];
|
|
84
|
+
_a.label = 2;
|
|
85
|
+
case 2:
|
|
86
|
+
_a.trys.push([2, 6, , 8]);
|
|
87
|
+
formData = new FormData();
|
|
88
|
+
formData.append('file', chunk, "".concat(fileName, "_chunk_").concat(index));
|
|
89
|
+
formData.append('fileGuid', id);
|
|
90
|
+
formData.append('isDone', (index === totalChunks - 1).toString());
|
|
91
|
+
formData.append('totalSize', totalSize.toString());
|
|
92
|
+
formData.append('totalChunks', totalChunks.toString());
|
|
93
|
+
formData.append('filename', fileName);
|
|
94
|
+
formData.append('index', index.toString());
|
|
95
|
+
return [4 /*yield*/, fetch(sharedContext.url, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
body: formData,
|
|
98
|
+
headers: sharedContext.headers
|
|
99
|
+
})];
|
|
100
|
+
case 3:
|
|
101
|
+
res = _a.sent();
|
|
102
|
+
if (!(res.status === 401 && onUnauthorized)) return [3 /*break*/, 5];
|
|
103
|
+
return [4 /*yield*/, onUnauthorized(res)];
|
|
104
|
+
case 4:
|
|
105
|
+
refreshData = _a.sent();
|
|
106
|
+
if (refreshData) {
|
|
107
|
+
if (refreshData.headers) {
|
|
108
|
+
sharedContext.headers = __assign(__assign({}, sharedContext.headers), refreshData.headers);
|
|
109
|
+
}
|
|
110
|
+
if (refreshData.url) {
|
|
111
|
+
sharedContext.url = refreshData.url;
|
|
112
|
+
}
|
|
113
|
+
// Token yenilendi, bu retry hakkından düşmeden aynı parçayı tekrar dene
|
|
114
|
+
retry--;
|
|
115
|
+
return [3 /*break*/, 8];
|
|
116
|
+
}
|
|
117
|
+
_a.label = 5;
|
|
118
|
+
case 5:
|
|
119
|
+
if (res.ok)
|
|
120
|
+
return [2 /*return*/, true];
|
|
121
|
+
// Diğer hatalarda (500 vb.) retry devam etsin
|
|
122
|
+
if (retry === 2)
|
|
123
|
+
throw new Error("Server error: ".concat(res.status));
|
|
124
|
+
return [3 /*break*/, 8];
|
|
125
|
+
case 6:
|
|
126
|
+
e_2 = _a.sent();
|
|
127
|
+
if (retry === 2)
|
|
128
|
+
throw e_2;
|
|
129
|
+
// Ağ hatalarında bekleme süresini artır
|
|
130
|
+
return [4 /*yield*/, delay(delay_number + (retry + 1) * 1000)];
|
|
131
|
+
case 7:
|
|
132
|
+
// Ağ hatalarında bekleme süresini artır
|
|
133
|
+
_a.sent();
|
|
134
|
+
return [3 /*break*/, 8];
|
|
135
|
+
case 8:
|
|
136
|
+
retry++;
|
|
137
|
+
return [3 /*break*/, 1];
|
|
138
|
+
case 9: return [2 /*return*/, false];
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}); };
|
|
142
|
+
_a.label = 2;
|
|
143
|
+
case 2:
|
|
144
|
+
_a.trys.push([2, 4, , 5]);
|
|
145
|
+
queue_1 = Array.from({ length: totalChunks }, function (_, i) { return i; });
|
|
146
|
+
workers = Array(Math.min(concurrency, totalChunks)).fill(null).map(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
147
|
+
var index, success;
|
|
148
|
+
return __generator(this, function (_a) {
|
|
149
|
+
switch (_a.label) {
|
|
150
|
+
case 0:
|
|
151
|
+
if (!(queue_1.length > 0)) return [3 /*break*/, 4];
|
|
152
|
+
index = queue_1.shift();
|
|
153
|
+
return [4 /*yield*/, uploadChunk(index)];
|
|
154
|
+
case 1:
|
|
155
|
+
success = _a.sent();
|
|
156
|
+
if (!success)
|
|
157
|
+
throw new Error("file could not be loaded");
|
|
158
|
+
if (!(delay_number > 0)) return [3 /*break*/, 3];
|
|
159
|
+
return [4 /*yield*/, delay(delay_number)];
|
|
160
|
+
case 2:
|
|
161
|
+
_a.sent();
|
|
162
|
+
_a.label = 3;
|
|
163
|
+
case 3: return [3 /*break*/, 0];
|
|
164
|
+
case 4: return [2 /*return*/];
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}); });
|
|
168
|
+
return [4 /*yield*/, Promise.all(workers)];
|
|
169
|
+
case 3:
|
|
170
|
+
_a.sent();
|
|
171
|
+
return [2 /*return*/, { success: true, id: id, message: "file uploaded successfully" }];
|
|
172
|
+
case 4:
|
|
173
|
+
e_1 = _a.sent();
|
|
174
|
+
return [2 /*return*/, { success: false, id: id, message: e_1.message || "file could not be loaded" }];
|
|
175
|
+
case 5: return [2 /*return*/];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
82
179
|
};
|
|
83
|
-
|
|
180
|
+
exports.uploadWithPartialFile = uploadWithPartialFile;
|
|
181
|
+
var delay = function (ms) { return new Promise(function (f) { return setTimeout(f, ms); }); };
|
|
182
|
+
var generateGuid = function () {
|
|
84
183
|
var chars = '0123456789abcdef';
|
|
85
184
|
var guid = '';
|
|
86
185
|
for (var i = 0; i < 40; i++) {
|
|
87
186
|
var randomIndex = Math.floor(Math.random() * chars.length);
|
|
88
187
|
guid += chars.charAt(randomIndex);
|
|
89
188
|
}
|
|
90
|
-
;
|
|
91
189
|
return guid;
|
|
92
190
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
var vitest_1 = require("vitest");
|
|
40
|
+
var index_1 = require("./index");
|
|
41
|
+
// @vitest-environment happy-dom
|
|
42
|
+
(0, vitest_1.describe)('uploadWithPartialFile', function () {
|
|
43
|
+
var mockUrl = 'https://api.example.com/upload';
|
|
44
|
+
var mockFile = new File(['hello world'], 'test.txt', { type: 'text/plain' });
|
|
45
|
+
(0, vitest_1.beforeEach)(function () {
|
|
46
|
+
vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn());
|
|
47
|
+
vitest_1.vi.useFakeTimers();
|
|
48
|
+
});
|
|
49
|
+
(0, vitest_1.it)('should upload a small file in a single chunk successfully', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
50
|
+
var resultPromise, result, formData;
|
|
51
|
+
var _a;
|
|
52
|
+
return __generator(this, function (_b) {
|
|
53
|
+
switch (_b.label) {
|
|
54
|
+
case 0:
|
|
55
|
+
fetch.mockResolvedValue({
|
|
56
|
+
ok: true,
|
|
57
|
+
status: 200
|
|
58
|
+
});
|
|
59
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, mockFile, {}, 1024, 0);
|
|
60
|
+
// Fast-forward any delays
|
|
61
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
62
|
+
case 1:
|
|
63
|
+
// Fast-forward any delays
|
|
64
|
+
_b.sent();
|
|
65
|
+
return [4 /*yield*/, resultPromise];
|
|
66
|
+
case 2:
|
|
67
|
+
result = _b.sent();
|
|
68
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
69
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(1);
|
|
70
|
+
formData = (_a = vitest_1.vi.mocked(fetch).mock.calls[0][1]) === null || _a === void 0 ? void 0 : _a.body;
|
|
71
|
+
(0, vitest_1.expect)(formData.get('isDone')).toBe('true');
|
|
72
|
+
(0, vitest_1.expect)(formData.get('totalChunks')).toBe('1');
|
|
73
|
+
return [2 /*return*/];
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}); });
|
|
77
|
+
(0, vitest_1.it)('should upload a large file in multiple chunks', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
78
|
+
var largeFile, resultPromise, result;
|
|
79
|
+
return __generator(this, function (_a) {
|
|
80
|
+
switch (_a.label) {
|
|
81
|
+
case 0:
|
|
82
|
+
fetch.mockResolvedValue({
|
|
83
|
+
ok: true,
|
|
84
|
+
status: 200
|
|
85
|
+
});
|
|
86
|
+
largeFile = new File(['0123456789a'], 'large.txt');
|
|
87
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, largeFile, {}, 5, 0, 1);
|
|
88
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
89
|
+
case 1:
|
|
90
|
+
_a.sent();
|
|
91
|
+
return [4 /*yield*/, resultPromise];
|
|
92
|
+
case 2:
|
|
93
|
+
result = _a.sent();
|
|
94
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
95
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(3);
|
|
96
|
+
return [2 /*return*/];
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}); });
|
|
100
|
+
(0, vitest_1.it)('should retry on network error and succeed', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
101
|
+
var resultPromise, result;
|
|
102
|
+
return __generator(this, function (_a) {
|
|
103
|
+
switch (_a.label) {
|
|
104
|
+
case 0:
|
|
105
|
+
fetch
|
|
106
|
+
.mockRejectedValueOnce(new Error('Network error'))
|
|
107
|
+
.mockResolvedValueOnce({ ok: true, status: 200 });
|
|
108
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, mockFile, {}, 1024, 10);
|
|
109
|
+
// First attempt fails, wait for delay
|
|
110
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
111
|
+
case 1:
|
|
112
|
+
// First attempt fails, wait for delay
|
|
113
|
+
_a.sent();
|
|
114
|
+
return [4 /*yield*/, resultPromise];
|
|
115
|
+
case 2:
|
|
116
|
+
result = _a.sent();
|
|
117
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
118
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(2);
|
|
119
|
+
return [2 /*return*/];
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}); });
|
|
123
|
+
(0, vitest_1.it)('should refresh token on 401 and retry successfully', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
124
|
+
var onUnauthorized, resultPromise, result, secondCallHeaders;
|
|
125
|
+
var _a;
|
|
126
|
+
return __generator(this, function (_b) {
|
|
127
|
+
switch (_b.label) {
|
|
128
|
+
case 0:
|
|
129
|
+
fetch
|
|
130
|
+
.mockResolvedValueOnce({ ok: false, status: 401 }) // First attempt: 401
|
|
131
|
+
.mockResolvedValueOnce({ ok: true, status: 200 }); // Second attempt: Success
|
|
132
|
+
onUnauthorized = vitest_1.vi.fn().mockResolvedValue({
|
|
133
|
+
headers: { 'Authorization': 'Bearer new-token' }
|
|
134
|
+
});
|
|
135
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, mockFile, { 'Authorization': 'Bearer old' }, 1024, 0, 1, onUnauthorized);
|
|
136
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
137
|
+
case 1:
|
|
138
|
+
_b.sent();
|
|
139
|
+
return [4 /*yield*/, resultPromise];
|
|
140
|
+
case 2:
|
|
141
|
+
result = _b.sent();
|
|
142
|
+
(0, vitest_1.expect)(onUnauthorized).toHaveBeenCalled();
|
|
143
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
144
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(2);
|
|
145
|
+
secondCallHeaders = (_a = vitest_1.vi.mocked(fetch).mock.calls[1][1]) === null || _a === void 0 ? void 0 : _a.headers;
|
|
146
|
+
(0, vitest_1.expect)(secondCallHeaders['Authorization']).toBe('Bearer new-token');
|
|
147
|
+
return [2 /*return*/];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}); });
|
|
151
|
+
(0, vitest_1.it)('should fail after maximum retries', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
152
|
+
var resultPromise, result;
|
|
153
|
+
return __generator(this, function (_a) {
|
|
154
|
+
switch (_a.label) {
|
|
155
|
+
case 0:
|
|
156
|
+
fetch.mockRejectedValue(new Error('Persistent error'));
|
|
157
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, mockFile, {}, 1024, 0);
|
|
158
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
159
|
+
case 1:
|
|
160
|
+
_a.sent();
|
|
161
|
+
return [4 /*yield*/, resultPromise];
|
|
162
|
+
case 2:
|
|
163
|
+
result = _a.sent();
|
|
164
|
+
(0, vitest_1.expect)(result.success).toBe(false);
|
|
165
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(3); // Retry limit is 3 in code
|
|
166
|
+
return [2 /*return*/];
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}); });
|
|
170
|
+
(0, vitest_1.it)('should handle concurrency correctly', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
171
|
+
var largeFile, resultPromise, result;
|
|
172
|
+
return __generator(this, function (_a) {
|
|
173
|
+
switch (_a.label) {
|
|
174
|
+
case 0:
|
|
175
|
+
fetch.mockResolvedValue({ ok: true, status: 200 });
|
|
176
|
+
largeFile = new File(['a'.repeat(100)], 'concurrency.txt');
|
|
177
|
+
resultPromise = (0, index_1.uploadWithPartialFile)(mockUrl, largeFile, {}, 10, 0, 5);
|
|
178
|
+
return [4 /*yield*/, vitest_1.vi.runAllTimersAsync()];
|
|
179
|
+
case 1:
|
|
180
|
+
_a.sent();
|
|
181
|
+
return [4 /*yield*/, resultPromise];
|
|
182
|
+
case 2:
|
|
183
|
+
result = _a.sent();
|
|
184
|
+
(0, vitest_1.expect)(result.success).toBe(true);
|
|
185
|
+
(0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(10);
|
|
186
|
+
return [2 /*return*/];
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}); });
|
|
190
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "partial-uploader",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"/dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc src/index.ts"
|
|
11
|
+
"build": "tsc src/index.ts",
|
|
12
|
+
"test": "vitest run"
|
|
12
13
|
},
|
|
13
14
|
"author": "Yusuf Unal <theunal9@gmail.com> (https://github.com/theunal)",
|
|
14
15
|
"license": "MIT",
|
|
@@ -23,6 +24,8 @@
|
|
|
23
24
|
"upload"
|
|
24
25
|
],
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"
|
|
27
|
+
"happy-dom": "^20.3.4",
|
|
28
|
+
"typescript": "^5.1.3",
|
|
29
|
+
"vitest": "^4.0.17"
|
|
27
30
|
}
|
|
28
31
|
}
|