electron-updater 4.3.5 → 4.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +22 -0
  2. package/out/AppAdapter.js +19 -28
  3. package/out/AppAdapter.js.map +1 -1
  4. package/out/AppImageUpdater.js +103 -173
  5. package/out/AppImageUpdater.js.map +1 -1
  6. package/out/AppUpdater.d.ts +1 -3
  7. package/out/AppUpdater.js +517 -773
  8. package/out/AppUpdater.js.map +1 -1
  9. package/out/BaseUpdater.js +79 -103
  10. package/out/BaseUpdater.js.map +1 -1
  11. package/out/DownloadedUpdateHelper.js +150 -224
  12. package/out/DownloadedUpdateHelper.js.map +1 -1
  13. package/out/ElectronAppAdapter.js +35 -63
  14. package/out/ElectronAppAdapter.js.map +1 -1
  15. package/out/MacUpdater.d.ts +1 -1
  16. package/out/MacUpdater.js +132 -182
  17. package/out/MacUpdater.js.map +1 -1
  18. package/out/NoOpLogger.d.ts +6 -0
  19. package/out/NoOpLogger.js +19 -0
  20. package/out/NoOpLogger.js.map +1 -0
  21. package/out/NsisUpdater.js +199 -331
  22. package/out/NsisUpdater.js.map +1 -1
  23. package/out/differentialDownloader/DataSplitter.js +180 -252
  24. package/out/differentialDownloader/DataSplitter.js.map +1 -1
  25. package/out/differentialDownloader/DifferentialDownloader.d.ts +3 -0
  26. package/out/differentialDownloader/DifferentialDownloader.js +243 -308
  27. package/out/differentialDownloader/DifferentialDownloader.js.map +1 -1
  28. package/out/differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader.js +29 -65
  29. package/out/differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader.js.map +1 -1
  30. package/out/differentialDownloader/GenericDifferentialDownloader.js +7 -21
  31. package/out/differentialDownloader/GenericDifferentialDownloader.js.map +1 -1
  32. package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.d.ts +32 -0
  33. package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.js +90 -0
  34. package/out/differentialDownloader/ProgressDifferentialDownloadCallbackTransform.js.map +1 -0
  35. package/out/differentialDownloader/downloadPlanBuilder.js +99 -124
  36. package/out/differentialDownloader/downloadPlanBuilder.js.map +1 -1
  37. package/out/differentialDownloader/multipleRangeDownloader.js +99 -148
  38. package/out/differentialDownloader/multipleRangeDownloader.js.map +1 -1
  39. package/out/electronHttpExecutor.js +73 -106
  40. package/out/electronHttpExecutor.js.map +1 -1
  41. package/out/event.d.ts +35 -0
  42. package/out/event.js +39 -0
  43. package/out/event.js.map +1 -0
  44. package/out/log.d.ts +11 -0
  45. package/out/log.js +19 -0
  46. package/out/log.js.map +1 -0
  47. package/out/main.d.ts +1 -4
  48. package/out/main.js +60 -212
  49. package/out/main.js.map +1 -1
  50. package/out/providerFactory.js +49 -105
  51. package/out/providerFactory.js.map +1 -1
  52. package/out/providers/BintrayProvider.d.ts +2 -2
  53. package/out/providers/BintrayProvider.js +40 -95
  54. package/out/providers/BintrayProvider.js.map +1 -1
  55. package/out/providers/GenericProvider.d.ts +2 -2
  56. package/out/providers/GenericProvider.js +46 -80
  57. package/out/providers/GenericProvider.js.map +1 -1
  58. package/out/providers/GitHubProvider.d.ts +2 -2
  59. package/out/providers/GitHubProvider.js +122 -201
  60. package/out/providers/GitHubProvider.js.map +1 -1
  61. package/out/providers/PrivateGitHubProvider.js +87 -165
  62. package/out/providers/PrivateGitHubProvider.js.map +1 -1
  63. package/out/providers/Provider.js +122 -173
  64. package/out/providers/Provider.js.map +1 -1
  65. package/out/util.d.ts +5 -0
  66. package/out/util.js +42 -0
  67. package/out/util.js.map +1 -0
  68. package/out/windowsExecutableCodeSignatureVerifier.js +99 -139
  69. package/out/windowsExecutableCodeSignatureVerifier.js.map +1 -1
  70. package/package.json +33 -31
@@ -1,326 +1,261 @@
1
1
  "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
2
+ Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.DifferentialDownloader = void 0;
7
-
8
- function _builderUtilRuntime() {
9
- const data = require("builder-util-runtime");
10
-
11
- _builderUtilRuntime = function () {
12
- return data;
13
- };
14
-
15
- return data;
16
- }
17
-
18
- function _fsExtra() {
19
- const data = require("fs-extra");
20
-
21
- _fsExtra = function () {
22
- return data;
23
- };
24
-
25
- return data;
26
- }
27
-
28
- var _fs = require("fs");
29
-
30
- function _DataSplitter() {
31
- const data = require("./DataSplitter");
32
-
33
- _DataSplitter = function () {
34
- return data;
35
- };
36
-
37
- return data;
38
- }
39
-
40
- function _url() {
41
- const data = require("url");
42
-
43
- _url = function () {
44
- return data;
45
- };
46
-
47
- return data;
48
- }
49
-
50
- function _downloadPlanBuilder() {
51
- const data = require("./downloadPlanBuilder");
52
-
53
- _downloadPlanBuilder = function () {
54
- return data;
55
- };
56
-
57
- return data;
58
- }
59
-
60
- function _multipleRangeDownloader() {
61
- const data = require("./multipleRangeDownloader");
62
-
63
- _multipleRangeDownloader = function () {
64
- return data;
65
- };
66
-
67
- return data;
68
- }
69
-
4
+ const builder_util_runtime_1 = require("builder-util-runtime");
5
+ const fs_extra_1 = require("fs-extra");
6
+ const fs_1 = require("fs");
7
+ const DataSplitter_1 = require("./DataSplitter");
8
+ const url_1 = require("url");
9
+ const downloadPlanBuilder_1 = require("./downloadPlanBuilder");
10
+ const multipleRangeDownloader_1 = require("./multipleRangeDownloader");
11
+ const ProgressDifferentialDownloadCallbackTransform_1 = require("./ProgressDifferentialDownloadCallbackTransform");
70
12
  class DifferentialDownloader {
71
- // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
72
- constructor(blockAwareFileInfo, httpExecutor, options) {
73
- this.blockAwareFileInfo = blockAwareFileInfo;
74
- this.httpExecutor = httpExecutor;
75
- this.options = options;
76
- this.fileMetadataBuffer = null;
77
- this.logger = options.logger;
78
- }
79
-
80
- createRequestOptions() {
81
- const result = {
82
- headers: { ...this.options.requestHeaders,
83
- accept: "*/*"
84
- }
85
- };
86
- (0, _builderUtilRuntime().configureRequestUrl)(this.options.newUrl, result); // user-agent, cache-control and other common options
87
-
88
- (0, _builderUtilRuntime().configureRequestOptions)(result);
89
- return result;
90
- }
91
-
92
- doDownload(oldBlockMap, newBlockMap) {
93
- // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it
94
- if (oldBlockMap.version !== newBlockMap.version) {
95
- throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`);
96
- }
97
-
98
- const logger = this.logger;
99
- const operations = (0, _downloadPlanBuilder().computeOperations)(oldBlockMap, newBlockMap, logger);
100
-
101
- if (logger.debug != null) {
102
- logger.debug(JSON.stringify(operations, null, 2));
13
+ // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected
14
+ constructor(blockAwareFileInfo, httpExecutor, options) {
15
+ this.blockAwareFileInfo = blockAwareFileInfo;
16
+ this.httpExecutor = httpExecutor;
17
+ this.options = options;
18
+ this.fileMetadataBuffer = null;
19
+ this.logger = options.logger;
103
20
  }
104
-
105
- let downloadSize = 0;
106
- let copySize = 0;
107
-
108
- for (const operation of operations) {
109
- const length = operation.end - operation.start;
110
-
111
- if (operation.kind === _downloadPlanBuilder().OperationKind.DOWNLOAD) {
112
- downloadSize += length;
113
- } else {
114
- copySize += length;
115
- }
21
+ createRequestOptions() {
22
+ const result = {
23
+ headers: {
24
+ ...this.options.requestHeaders,
25
+ accept: "*/*",
26
+ },
27
+ };
28
+ builder_util_runtime_1.configureRequestUrl(this.options.newUrl, result);
29
+ // user-agent, cache-control and other common options
30
+ builder_util_runtime_1.configureRequestOptions(result);
31
+ return result;
116
32
  }
117
-
118
- const newSize = this.blockAwareFileInfo.size;
119
-
120
- if (downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length) !== newSize) {
121
- throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`);
122
- }
123
-
124
- logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`);
125
- return this.downloadFile(operations);
126
- }
127
-
128
- downloadFile(tasks) {
129
- const fdList = [];
130
-
131
- const closeFiles = () => {
132
- return Promise.all(fdList.map(openedFile => {
133
- return (0, _fsExtra().close)(openedFile.descriptor).catch(e => {
134
- this.logger.error(`cannot close file "${openedFile.path}": ${e}`);
135
- });
136
- }));
137
- };
138
-
139
- return this.doDownloadFile(tasks, fdList).then(closeFiles).catch(e => {
140
- // then must be after catch here (since then always throws error)
141
- return closeFiles().catch(closeFilesError => {
142
- // closeFiles never throw error, but just to be sure
143
- try {
144
- this.logger.error(`cannot close files: ${closeFilesError}`);
145
- } catch (errorOnLog) {
146
- try {
147
- console.error(errorOnLog);
148
- } catch (ignored) {// ok, give up and ignore error
149
- }
150
- }
151
-
152
- throw e;
153
- }).then(() => {
154
- throw e;
155
- });
156
- });
157
- }
158
-
159
- async doDownloadFile(tasks, fdList) {
160
- const oldFileFd = await (0, _fsExtra().open)(this.options.oldFile, "r");
161
- fdList.push({
162
- descriptor: oldFileFd,
163
- path: this.options.oldFile
164
- });
165
- const newFileFd = await (0, _fsExtra().open)(this.options.newFile, "w");
166
- fdList.push({
167
- descriptor: newFileFd,
168
- path: this.options.newFile
169
- });
170
- const fileOut = (0, _fs.createWriteStream)(this.options.newFile, {
171
- fd: newFileFd
172
- });
173
- await new Promise((resolve, reject) => {
174
- const streams = [];
175
- const digestTransform = new (_builderUtilRuntime().DigestTransform)(this.blockAwareFileInfo.sha512); // to simply debug, do manual validation to allow file to be fully written
176
-
177
- digestTransform.isValidateOnEnd = false;
178
- streams.push(digestTransform); // noinspection JSArrowFunctionCanBeReplacedWithShorthand
179
-
180
- fileOut.on("finish", () => {
181
- fileOut.close(() => {
182
- // remove from fd list because closed successfully
183
- fdList.splice(1, 1);
184
-
185
- try {
186
- digestTransform.validate();
187
- } catch (e) {
188
- reject(e);
189
- return;
190
- }
191
-
192
- resolve();
193
- });
194
- });
195
- streams.push(fileOut);
196
- let lastStream = null;
197
-
198
- for (const stream of streams) {
199
- stream.on("error", reject);
200
-
201
- if (lastStream == null) {
202
- lastStream = stream;
203
- } else {
204
- lastStream = lastStream.pipe(stream);
33
+ doDownload(oldBlockMap, newBlockMap) {
34
+ // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it
35
+ if (oldBlockMap.version !== newBlockMap.version) {
36
+ throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`);
205
37
  }
206
- }
207
-
208
- const firstStream = streams[0];
209
- let w;
210
-
211
- if (this.options.isUseMultipleRangeRequest) {
212
- w = (0, _multipleRangeDownloader().executeTasksUsingMultipleRangeRequests)(this, tasks, firstStream, oldFileFd, reject);
213
- w(0);
214
- return;
215
- }
216
-
217
- let downloadOperationCount = 0;
218
- let actualUrl = null;
219
- this.logger.info(`Differential download: ${this.options.newUrl}`);
220
- const requestOptions = this.createRequestOptions();
221
- requestOptions.redirect = "manual";
222
-
223
- w = index => {
224
- if (index >= tasks.length) {
225
- if (this.fileMetadataBuffer != null) {
226
- firstStream.write(this.fileMetadataBuffer);
227
- }
228
-
229
- firstStream.end();
230
- return;
38
+ const logger = this.logger;
39
+ const operations = downloadPlanBuilder_1.computeOperations(oldBlockMap, newBlockMap, logger);
40
+ if (logger.debug != null) {
41
+ logger.debug(JSON.stringify(operations, null, 2));
231
42
  }
232
-
233
- const operation = tasks[index++];
234
-
235
- if (operation.kind === _downloadPlanBuilder().OperationKind.COPY) {
236
- (0, _DataSplitter().copyData)(operation, firstStream, oldFileFd, reject, () => w(index));
237
- return;
43
+ let downloadSize = 0;
44
+ let copySize = 0;
45
+ for (const operation of operations) {
46
+ const length = operation.end - operation.start;
47
+ if (operation.kind === downloadPlanBuilder_1.OperationKind.DOWNLOAD) {
48
+ downloadSize += length;
49
+ }
50
+ else {
51
+ copySize += length;
52
+ }
238
53
  }
239
-
240
- const range = `bytes=${operation.start}-${operation.end - 1}`;
241
- requestOptions.headers.range = range;
242
- const debug = this.logger.debug;
243
-
244
- if (debug != null) {
245
- debug(`download range: ${range}`);
54
+ const newSize = this.blockAwareFileInfo.size;
55
+ if (downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length) !== newSize) {
56
+ throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`);
246
57
  }
247
-
248
- const request = this.httpExecutor.createRequest(requestOptions, response => {
249
- // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.
250
- if (response.statusCode >= 400) {
251
- reject((0, _builderUtilRuntime().createHttpError)(response));
252
- }
253
-
254
- response.pipe(firstStream, {
255
- end: false
256
- });
257
- response.once("end", () => {
258
- if (++downloadOperationCount === 100) {
259
- downloadOperationCount = 0;
260
- setTimeout(() => w(index), 1000);
261
- } else {
262
- w(index);
263
- }
264
- });
58
+ logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`);
59
+ return this.downloadFile(operations);
60
+ }
61
+ downloadFile(tasks) {
62
+ const fdList = [];
63
+ const closeFiles = () => {
64
+ return Promise.all(fdList.map(openedFile => {
65
+ return fs_extra_1.close(openedFile.descriptor).catch(e => {
66
+ this.logger.error(`cannot close file "${openedFile.path}": ${e}`);
67
+ });
68
+ }));
69
+ };
70
+ return this.doDownloadFile(tasks, fdList)
71
+ .then(closeFiles)
72
+ .catch(e => {
73
+ // then must be after catch here (since then always throws error)
74
+ return closeFiles()
75
+ .catch(closeFilesError => {
76
+ // closeFiles never throw error, but just to be sure
77
+ try {
78
+ this.logger.error(`cannot close files: ${closeFilesError}`);
79
+ }
80
+ catch (errorOnLog) {
81
+ try {
82
+ console.error(errorOnLog);
83
+ }
84
+ catch (ignored) {
85
+ // ok, give up and ignore error
86
+ }
87
+ }
88
+ throw e;
89
+ })
90
+ .then(() => {
91
+ throw e;
92
+ });
265
93
  });
266
- request.on("redirect", (statusCode, method, redirectUrl) => {
267
- this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`);
268
- actualUrl = redirectUrl;
269
- (0, _builderUtilRuntime().configureRequestUrl)(new (_url().URL)(actualUrl), requestOptions);
270
- request.followRedirect();
94
+ }
95
+ async doDownloadFile(tasks, fdList) {
96
+ const oldFileFd = await fs_extra_1.open(this.options.oldFile, "r");
97
+ fdList.push({ descriptor: oldFileFd, path: this.options.oldFile });
98
+ const newFileFd = await fs_extra_1.open(this.options.newFile, "w");
99
+ fdList.push({ descriptor: newFileFd, path: this.options.newFile });
100
+ const fileOut = fs_1.createWriteStream(this.options.newFile, { fd: newFileFd });
101
+ await new Promise((resolve, reject) => {
102
+ const streams = [];
103
+ // Create our download info transformer if we have one
104
+ let downloadInfoTransform = undefined;
105
+ if (!this.options.isUseMultipleRangeRequest && this.options.onProgress) {
106
+ // TODO: Does not support multiple ranges (someone feel free to PR this!)
107
+ const expectedByteCounts = [];
108
+ let grandTotalBytes = 0;
109
+ for (const task of tasks) {
110
+ if (task.kind === downloadPlanBuilder_1.OperationKind.DOWNLOAD) {
111
+ expectedByteCounts.push(task.end - task.start);
112
+ grandTotalBytes += task.end - task.start;
113
+ }
114
+ }
115
+ const progressDifferentialDownloadInfo = {
116
+ expectedByteCounts: expectedByteCounts,
117
+ grandTotal: grandTotalBytes,
118
+ };
119
+ downloadInfoTransform = new ProgressDifferentialDownloadCallbackTransform_1.ProgressDifferentialDownloadCallbackTransform(progressDifferentialDownloadInfo, this.options.cancellationToken, this.options.onProgress);
120
+ streams.push(downloadInfoTransform);
121
+ }
122
+ const digestTransform = new builder_util_runtime_1.DigestTransform(this.blockAwareFileInfo.sha512);
123
+ // to simply debug, do manual validation to allow file to be fully written
124
+ digestTransform.isValidateOnEnd = false;
125
+ streams.push(digestTransform);
126
+ // noinspection JSArrowFunctionCanBeReplacedWithShorthand
127
+ fileOut.on("finish", () => {
128
+ ;
129
+ fileOut.close(() => {
130
+ // remove from fd list because closed successfully
131
+ fdList.splice(1, 1);
132
+ try {
133
+ digestTransform.validate();
134
+ }
135
+ catch (e) {
136
+ reject(e);
137
+ return;
138
+ }
139
+ resolve(undefined);
140
+ });
141
+ });
142
+ streams.push(fileOut);
143
+ let lastStream = null;
144
+ for (const stream of streams) {
145
+ stream.on("error", reject);
146
+ if (lastStream == null) {
147
+ lastStream = stream;
148
+ }
149
+ else {
150
+ lastStream = lastStream.pipe(stream);
151
+ }
152
+ }
153
+ const firstStream = streams[0];
154
+ let w;
155
+ if (this.options.isUseMultipleRangeRequest) {
156
+ w = multipleRangeDownloader_1.executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject);
157
+ w(0);
158
+ return;
159
+ }
160
+ let downloadOperationCount = 0;
161
+ let actualUrl = null;
162
+ this.logger.info(`Differential download: ${this.options.newUrl}`);
163
+ const requestOptions = this.createRequestOptions();
164
+ requestOptions.redirect = "manual";
165
+ w = (index) => {
166
+ var _a, _b;
167
+ if (index >= tasks.length) {
168
+ if (this.fileMetadataBuffer != null) {
169
+ firstStream.write(this.fileMetadataBuffer);
170
+ }
171
+ firstStream.end();
172
+ return;
173
+ }
174
+ const operation = tasks[index++];
175
+ if (operation.kind === downloadPlanBuilder_1.OperationKind.COPY) {
176
+ // We are copying, let's not send status updates to the UI
177
+ if (downloadInfoTransform) {
178
+ downloadInfoTransform.beginFileCopy();
179
+ }
180
+ DataSplitter_1.copyData(operation, firstStream, oldFileFd, reject, () => w(index));
181
+ return;
182
+ }
183
+ const range = `bytes=${operation.start}-${operation.end - 1}`;
184
+ requestOptions.headers.range = range;
185
+ (_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug) === null || _b === void 0 ? void 0 : _b.call(_a, `download range: ${range}`);
186
+ // We are starting to download
187
+ if (downloadInfoTransform) {
188
+ downloadInfoTransform.beginRangeDownload();
189
+ }
190
+ const request = this.httpExecutor.createRequest(requestOptions, response => {
191
+ // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.
192
+ if (response.statusCode >= 400) {
193
+ reject(builder_util_runtime_1.createHttpError(response));
194
+ }
195
+ response.pipe(firstStream, {
196
+ end: false,
197
+ });
198
+ response.once("end", () => {
199
+ // Pass on that we are downloading a segment
200
+ if (downloadInfoTransform) {
201
+ downloadInfoTransform.endRangeDownload();
202
+ }
203
+ if (++downloadOperationCount === 100) {
204
+ downloadOperationCount = 0;
205
+ setTimeout(() => w(index), 1000);
206
+ }
207
+ else {
208
+ w(index);
209
+ }
210
+ });
211
+ });
212
+ request.on("redirect", (statusCode, method, redirectUrl) => {
213
+ this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`);
214
+ actualUrl = redirectUrl;
215
+ builder_util_runtime_1.configureRequestUrl(new url_1.URL(actualUrl), requestOptions);
216
+ request.followRedirect();
217
+ });
218
+ this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
219
+ request.end();
220
+ };
221
+ w(0);
271
222
  });
272
- this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
273
- request.end();
274
- };
275
-
276
- w(0);
277
- });
278
- }
279
-
280
- async readRemoteBytes(start, endInclusive) {
281
- const buffer = Buffer.allocUnsafe(endInclusive + 1 - start);
282
- const requestOptions = this.createRequestOptions();
283
- requestOptions.headers.range = `bytes=${start}-${endInclusive}`;
284
- let position = 0;
285
- await this.request(requestOptions, chunk => {
286
- chunk.copy(buffer, position);
287
- position += chunk.length;
288
- });
289
-
290
- if (position !== buffer.length) {
291
- throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`);
292
223
  }
293
-
294
- return buffer;
295
- }
296
-
297
- request(requestOptions, dataHandler) {
298
- return new Promise((resolve, reject) => {
299
- const request = this.httpExecutor.createRequest(requestOptions, response => {
300
- if (!(0, _multipleRangeDownloader().checkIsRangesSupported)(response, reject)) {
301
- return;
224
+ async readRemoteBytes(start, endInclusive) {
225
+ const buffer = Buffer.allocUnsafe(endInclusive + 1 - start);
226
+ const requestOptions = this.createRequestOptions();
227
+ requestOptions.headers.range = `bytes=${start}-${endInclusive}`;
228
+ let position = 0;
229
+ await this.request(requestOptions, chunk => {
230
+ chunk.copy(buffer, position);
231
+ position += chunk.length;
232
+ });
233
+ if (position !== buffer.length) {
234
+ throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`);
302
235
  }
303
-
304
- response.on("data", dataHandler);
305
- response.on("end", () => resolve());
306
- });
307
- this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
308
- request.end();
309
- });
310
- }
311
-
236
+ return buffer;
237
+ }
238
+ request(requestOptions, dataHandler) {
239
+ return new Promise((resolve, reject) => {
240
+ const request = this.httpExecutor.createRequest(requestOptions, response => {
241
+ if (!multipleRangeDownloader_1.checkIsRangesSupported(response, reject)) {
242
+ return;
243
+ }
244
+ response.on("data", dataHandler);
245
+ response.on("end", () => resolve());
246
+ });
247
+ this.httpExecutor.addErrorAndTimeoutHandlers(request, reject);
248
+ request.end();
249
+ });
250
+ }
312
251
  }
313
-
314
252
  exports.DifferentialDownloader = DifferentialDownloader;
315
-
316
253
  function formatBytes(value, symbol = " KB") {
317
- return new Intl.NumberFormat("en").format((value / 1024).toFixed(2)) + symbol;
318
- } // safety
319
-
320
-
254
+ return new Intl.NumberFormat("en").format((value / 1024).toFixed(2)) + symbol;
255
+ }
256
+ // safety
321
257
  function removeQuery(url) {
322
- const index = url.indexOf("?");
323
- return index < 0 ? url : url.substring(0, index);
324
- }
325
- // __ts-babel@6.0.4
258
+ const index = url.indexOf("?");
259
+ return index < 0 ? url : url.substring(0, index);
260
+ }
326
261
  //# sourceMappingURL=DifferentialDownloader.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/differentialDownloader/DifferentialDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import { BlockMapDataHolder, createHttpError, DigestTransform, HttpExecutor, configureRequestUrl, configureRequestOptions } from \"builder-util-runtime\"\nimport { BlockMap } from \"builder-util-runtime/out/blockMapApi\"\nimport { close, open } from \"fs-extra\"\nimport { createWriteStream } from \"fs\"\nimport { OutgoingHttpHeaders, RequestOptions } from \"http\"\nimport { Logger } from \"../main\"\nimport { copyData } from \"./DataSplitter\"\nimport { URL } from \"url\"\nimport { computeOperations, Operation, OperationKind } from \"./downloadPlanBuilder\"\nimport { checkIsRangesSupported, executeTasksUsingMultipleRangeRequests } from \"./multipleRangeDownloader\"\n\nexport interface DifferentialDownloaderOptions {\n readonly oldFile: string\n readonly newUrl: URL\n readonly logger: Logger\n readonly newFile: string\n\n readonly requestHeaders: OutgoingHttpHeaders | null\n\n readonly isUseMultipleRangeRequest?: boolean\n}\n\nexport abstract class DifferentialDownloader {\n fileMetadataBuffer: Buffer | null = null\n\n private readonly logger: Logger\n\n // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected\n constructor(protected readonly blockAwareFileInfo: BlockMapDataHolder, readonly httpExecutor: HttpExecutor<any>, readonly options: DifferentialDownloaderOptions) {\n this.logger = options.logger\n }\n\n createRequestOptions(): RequestOptions {\n const result = {\n headers: {\n ...this.options.requestHeaders,\n accept: \"*/*\",\n },\n }\n configureRequestUrl(this.options.newUrl, result)\n // user-agent, cache-control and other common options\n configureRequestOptions(result)\n return result\n }\n\n protected doDownload(oldBlockMap: BlockMap, newBlockMap: BlockMap): Promise<any> {\n // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it\n if (oldBlockMap.version !== newBlockMap.version) {\n throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`)\n }\n\n const logger = this.logger\n const operations = computeOperations(oldBlockMap, newBlockMap, logger)\n if (logger.debug != null) {\n logger.debug(JSON.stringify(operations, null, 2))\n }\n\n let downloadSize = 0\n let copySize = 0\n for (const operation of operations) {\n const length = operation.end - operation.start\n if (operation.kind === OperationKind.DOWNLOAD) {\n downloadSize += length\n }\n else {\n copySize += length\n }\n }\n\n const newSize = this.blockAwareFileInfo.size\n if ((downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length)) !== newSize) {\n throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`)\n }\n\n logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`)\n\n return this.downloadFile(operations)\n }\n\n private downloadFile(tasks: Array<Operation>): Promise<any> {\n const fdList: Array<OpenedFile> = []\n const closeFiles = (): Promise<Array<void>> => {\n return Promise.all(fdList.map(openedFile => {\n return close(openedFile.descriptor)\n .catch(e => {\n this.logger.error(`cannot close file \"${openedFile.path}\": ${e}`)\n })\n }))\n }\n return this.doDownloadFile(tasks, fdList)\n .then(closeFiles)\n .catch(e => {\n // then must be after catch here (since then always throws error)\n return closeFiles()\n .catch(closeFilesError => {\n // closeFiles never throw error, but just to be sure\n try {\n this.logger.error(`cannot close files: ${closeFilesError}`)\n }\n catch (errorOnLog) {\n try {\n console.error(errorOnLog)\n }\n catch (ignored) {\n // ok, give up and ignore error\n }\n }\n throw e\n })\n .then(() => {\n throw e\n })\n })\n }\n\n private async doDownloadFile(tasks: Array<Operation>, fdList: Array<OpenedFile>): Promise<any> {\n const oldFileFd = await open(this.options.oldFile, \"r\")\n fdList.push({descriptor: oldFileFd, path: this.options.oldFile})\n const newFileFd = await open(this.options.newFile, \"w\")\n fdList.push({descriptor: newFileFd, path: this.options.newFile})\n const fileOut = createWriteStream(this.options.newFile, {fd: newFileFd})\n await new Promise((resolve, reject) => {\n const streams: Array<any> = []\n const digestTransform = new DigestTransform(this.blockAwareFileInfo.sha512)\n // to simply debug, do manual validation to allow file to be fully written\n digestTransform.isValidateOnEnd = false\n streams.push(digestTransform)\n\n // noinspection JSArrowFunctionCanBeReplacedWithShorthand\n fileOut.on(\"finish\", () => {\n (fileOut.close as any)(() => {\n // remove from fd list because closed successfully\n fdList.splice(1, 1)\n try {\n digestTransform.validate()\n }\n catch (e) {\n reject(e)\n return\n }\n\n resolve()\n })\n })\n\n streams.push(fileOut)\n\n let lastStream = null\n for (const stream of streams) {\n stream.on(\"error\", reject)\n if (lastStream == null) {\n lastStream = stream\n }\n else {\n lastStream = lastStream.pipe(stream)\n }\n }\n\n const firstStream = streams[0]\n\n let w: any\n if (this.options.isUseMultipleRangeRequest) {\n w = executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject)\n w(0)\n return\n }\n\n let downloadOperationCount = 0\n let actualUrl: string | null = null\n this.logger.info(`Differential download: ${this.options.newUrl}`)\n\n const requestOptions = this.createRequestOptions();\n (requestOptions as any).redirect = \"manual\"\n\n w = (index: number): void => {\n if (index >= tasks.length) {\n if (this.fileMetadataBuffer != null) {\n firstStream.write(this.fileMetadataBuffer)\n }\n firstStream.end()\n return\n }\n\n const operation = tasks[index++]\n if (operation.kind === OperationKind.COPY) {\n copyData(operation, firstStream, oldFileFd, reject, () => w(index))\n return\n }\n\n const range = `bytes=${operation.start}-${operation.end - 1}`\n requestOptions.headers!!.range = range\n\n const debug = this.logger.debug\n if (debug != null) {\n debug(`download range: ${range}`)\n }\n\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.\n if (response.statusCode >= 400) {\n reject(createHttpError(response))\n }\n\n response.pipe(firstStream, {\n end: false\n })\n response.once(\"end\", () => {\n if (++downloadOperationCount === 100) {\n downloadOperationCount = 0\n setTimeout(() => w(index), 1000)\n }\n else {\n w(index)\n }\n })\n })\n request.on(\"redirect\", (statusCode: number, method: string, redirectUrl: string) => {\n this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`)\n actualUrl = redirectUrl\n configureRequestUrl(new URL(actualUrl), requestOptions)\n request.followRedirect()\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n }\n\n w(0)\n })\n }\n\n protected async readRemoteBytes(start: number, endInclusive: number): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe((endInclusive + 1) - start)\n const requestOptions = this.createRequestOptions()\n requestOptions.headers!!.range = `bytes=${start}-${endInclusive}`\n let position = 0\n await this.request(requestOptions, chunk => {\n chunk.copy(buffer, position)\n position += chunk.length\n })\n\n if (position !== buffer.length) {\n throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`)\n }\n return buffer\n }\n\n private request(requestOptions: RequestOptions, dataHandler: (chunk: Buffer) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n if (!checkIsRangesSupported(response, reject)) {\n return\n }\n\n response.on(\"data\", dataHandler)\n response.on(\"end\", () => resolve())\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n })\n }\n}\n\nfunction formatBytes(value: number, symbol = \" KB\"): string {\n return new Intl.NumberFormat(\"en\").format((value / 1024).toFixed(2) as any) + symbol\n}\n\n// safety\nfunction removeQuery(url: string): string {\n const index = url.indexOf(\"?\")\n return index < 0 ? url : url.substring(0, index)\n}\n\ninterface OpenedFile {\n readonly descriptor: number\n readonly path: string\n}"],"sourceRoot":""}
1
+ {"version":3,"file":"DifferentialDownloader.js","sourceRoot":"","sources":["../../src/differentialDownloader/DifferentialDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import { BlockMapDataHolder, createHttpError, DigestTransform, HttpExecutor, configureRequestUrl, configureRequestOptions } from \"builder-util-runtime\"\nimport { BlockMap } from \"builder-util-runtime/out/blockMapApi\"\nimport { close, open } from \"fs-extra\"\nimport { createWriteStream } from \"fs\"\nimport { OutgoingHttpHeaders, RequestOptions } from \"http\"\nimport { ProgressInfo, CancellationToken } from \"builder-util-runtime\"\nimport { Logger } from \"../main\"\nimport { copyData } from \"./DataSplitter\"\nimport { URL } from \"url\"\nimport { computeOperations, Operation, OperationKind } from \"./downloadPlanBuilder\"\nimport { checkIsRangesSupported, executeTasksUsingMultipleRangeRequests } from \"./multipleRangeDownloader\"\nimport { ProgressDifferentialDownloadCallbackTransform, ProgressDifferentialDownloadInfo } from \"./ProgressDifferentialDownloadCallbackTransform\"\n\nexport interface DifferentialDownloaderOptions {\n readonly oldFile: string\n readonly newUrl: URL\n readonly logger: Logger\n readonly newFile: string\n\n readonly requestHeaders: OutgoingHttpHeaders | null\n\n readonly isUseMultipleRangeRequest?: boolean\n\n readonly cancellationToken: CancellationToken\n onProgress?: (progress: ProgressInfo) => void\n}\n\nexport abstract class DifferentialDownloader {\n fileMetadataBuffer: Buffer | null = null\n\n private readonly logger: Logger\n\n // noinspection TypeScriptAbstractClassConstructorCanBeMadeProtected\n constructor(protected readonly blockAwareFileInfo: BlockMapDataHolder, readonly httpExecutor: HttpExecutor<any>, readonly options: DifferentialDownloaderOptions) {\n this.logger = options.logger\n }\n\n createRequestOptions(): RequestOptions {\n const result = {\n headers: {\n ...this.options.requestHeaders,\n accept: \"*/*\",\n },\n }\n configureRequestUrl(this.options.newUrl, result)\n // user-agent, cache-control and other common options\n configureRequestOptions(result)\n return result\n }\n\n protected doDownload(oldBlockMap: BlockMap, newBlockMap: BlockMap): Promise<any> {\n // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it\n if (oldBlockMap.version !== newBlockMap.version) {\n throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`)\n }\n\n const logger = this.logger\n const operations = computeOperations(oldBlockMap, newBlockMap, logger)\n if (logger.debug != null) {\n logger.debug(JSON.stringify(operations, null, 2))\n }\n\n let downloadSize = 0\n let copySize = 0\n for (const operation of operations) {\n const length = operation.end - operation.start\n if (operation.kind === OperationKind.DOWNLOAD) {\n downloadSize += length\n } else {\n copySize += length\n }\n }\n\n const newSize = this.blockAwareFileInfo.size\n if (downloadSize + copySize + (this.fileMetadataBuffer == null ? 0 : this.fileMetadataBuffer.length) !== newSize) {\n throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newSize: ${newSize}`)\n }\n\n logger.info(`Full: ${formatBytes(newSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newSize / 100))}%)`)\n\n return this.downloadFile(operations)\n }\n\n private downloadFile(tasks: Array<Operation>): Promise<any> {\n const fdList: Array<OpenedFile> = []\n const closeFiles = (): Promise<Array<void>> => {\n return Promise.all(\n fdList.map(openedFile => {\n return close(openedFile.descriptor).catch(e => {\n this.logger.error(`cannot close file \"${openedFile.path}\": ${e}`)\n })\n })\n )\n }\n return this.doDownloadFile(tasks, fdList)\n .then(closeFiles)\n .catch(e => {\n // then must be after catch here (since then always throws error)\n return closeFiles()\n .catch(closeFilesError => {\n // closeFiles never throw error, but just to be sure\n try {\n this.logger.error(`cannot close files: ${closeFilesError}`)\n } catch (errorOnLog) {\n try {\n console.error(errorOnLog)\n } catch (ignored) {\n // ok, give up and ignore error\n }\n }\n throw e\n })\n .then(() => {\n throw e\n })\n })\n }\n\n private async doDownloadFile(tasks: Array<Operation>, fdList: Array<OpenedFile>): Promise<any> {\n const oldFileFd = await open(this.options.oldFile, \"r\")\n fdList.push({ descriptor: oldFileFd, path: this.options.oldFile })\n const newFileFd = await open(this.options.newFile, \"w\")\n fdList.push({ descriptor: newFileFd, path: this.options.newFile })\n const fileOut = createWriteStream(this.options.newFile, { fd: newFileFd })\n await new Promise((resolve, reject) => {\n const streams: Array<any> = []\n\n // Create our download info transformer if we have one\n let downloadInfoTransform: ProgressDifferentialDownloadCallbackTransform | undefined = undefined\n if (!this.options.isUseMultipleRangeRequest && this.options.onProgress) {\n // TODO: Does not support multiple ranges (someone feel free to PR this!)\n const expectedByteCounts: Array<number> = []\n let grandTotalBytes = 0\n\n for (const task of tasks) {\n if (task.kind === OperationKind.DOWNLOAD) {\n expectedByteCounts.push(task.end - task.start)\n grandTotalBytes += task.end - task.start\n }\n }\n\n const progressDifferentialDownloadInfo: ProgressDifferentialDownloadInfo = {\n expectedByteCounts: expectedByteCounts,\n grandTotal: grandTotalBytes,\n }\n\n downloadInfoTransform = new ProgressDifferentialDownloadCallbackTransform(progressDifferentialDownloadInfo, this.options.cancellationToken, this.options.onProgress)\n streams.push(downloadInfoTransform)\n }\n\n const digestTransform = new DigestTransform(this.blockAwareFileInfo.sha512)\n // to simply debug, do manual validation to allow file to be fully written\n digestTransform.isValidateOnEnd = false\n streams.push(digestTransform)\n\n // noinspection JSArrowFunctionCanBeReplacedWithShorthand\n fileOut.on(\"finish\", () => {\n ;(fileOut.close as any)(() => {\n // remove from fd list because closed successfully\n fdList.splice(1, 1)\n try {\n digestTransform.validate()\n } catch (e) {\n reject(e)\n return\n }\n\n resolve(undefined)\n })\n })\n\n streams.push(fileOut)\n\n let lastStream = null\n for (const stream of streams) {\n stream.on(\"error\", reject)\n if (lastStream == null) {\n lastStream = stream\n } else {\n lastStream = lastStream.pipe(stream)\n }\n }\n\n const firstStream = streams[0]\n\n let w: any\n if (this.options.isUseMultipleRangeRequest) {\n w = executeTasksUsingMultipleRangeRequests(this, tasks, firstStream, oldFileFd, reject)\n w(0)\n return\n }\n\n let downloadOperationCount = 0\n let actualUrl: string | null = null\n this.logger.info(`Differential download: ${this.options.newUrl}`)\n\n const requestOptions = this.createRequestOptions()\n ;(requestOptions as any).redirect = \"manual\"\n\n w = (index: number): void => {\n if (index >= tasks.length) {\n if (this.fileMetadataBuffer != null) {\n firstStream.write(this.fileMetadataBuffer)\n }\n firstStream.end()\n return\n }\n\n const operation = tasks[index++]\n if (operation.kind === OperationKind.COPY) {\n // We are copying, let's not send status updates to the UI\n if (downloadInfoTransform) {\n downloadInfoTransform.beginFileCopy()\n }\n\n copyData(operation, firstStream, oldFileFd, reject, () => w(index))\n return\n }\n\n const range = `bytes=${operation.start}-${operation.end - 1}`\n requestOptions.headers!.range = range\n\n this.logger?.debug?.(`download range: ${range}`)\n\n // We are starting to download\n if (downloadInfoTransform) {\n downloadInfoTransform.beginRangeDownload()\n }\n\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n // Electron net handles redirects automatically, our NodeJS test server doesn't use redirects - so, we don't check 3xx codes.\n if (response.statusCode >= 400) {\n reject(createHttpError(response))\n }\n\n response.pipe(firstStream, {\n end: false,\n })\n response.once(\"end\", () => {\n // Pass on that we are downloading a segment\n if (downloadInfoTransform) {\n downloadInfoTransform.endRangeDownload()\n }\n\n if (++downloadOperationCount === 100) {\n downloadOperationCount = 0\n setTimeout(() => w(index), 1000)\n } else {\n w(index)\n }\n })\n })\n request.on(\"redirect\", (statusCode: number, method: string, redirectUrl: string) => {\n this.logger.info(`Redirect to ${removeQuery(redirectUrl)}`)\n actualUrl = redirectUrl\n configureRequestUrl(new URL(actualUrl), requestOptions)\n request.followRedirect()\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n }\n\n w(0)\n })\n }\n\n protected async readRemoteBytes(start: number, endInclusive: number): Promise<Buffer> {\n const buffer = Buffer.allocUnsafe(endInclusive + 1 - start)\n const requestOptions = this.createRequestOptions()\n requestOptions.headers!.range = `bytes=${start}-${endInclusive}`\n let position = 0\n await this.request(requestOptions, chunk => {\n chunk.copy(buffer, position)\n position += chunk.length\n })\n\n if (position !== buffer.length) {\n throw new Error(`Received data length ${position} is not equal to expected ${buffer.length}`)\n }\n return buffer\n }\n\n private request(requestOptions: RequestOptions, dataHandler: (chunk: Buffer) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = this.httpExecutor.createRequest(requestOptions, response => {\n if (!checkIsRangesSupported(response, reject)) {\n return\n }\n\n response.on(\"data\", dataHandler)\n response.on(\"end\", () => resolve())\n })\n this.httpExecutor.addErrorAndTimeoutHandlers(request, reject)\n request.end()\n })\n }\n}\n\nfunction formatBytes(value: number, symbol = \" KB\"): string {\n return new Intl.NumberFormat(\"en\").format((value / 1024).toFixed(2) as any) + symbol\n}\n\n// safety\nfunction removeQuery(url: string): string {\n const index = url.indexOf(\"?\")\n return index < 0 ? url : url.substring(0, index)\n}\n\ninterface OpenedFile {\n readonly descriptor: number\n readonly path: string\n}\n"]}