react-native-update 10.37.4 → 10.37.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,6 @@ interface ZipFile {
18
18
  export class DownloadTask {
19
19
  private context: common.Context;
20
20
  private hash: string;
21
- private readonly DOWNLOAD_CHUNK_SIZE = 4096;
22
21
  private eventHub: EventHub;
23
22
 
24
23
  constructor(context: common.Context) {
@@ -53,27 +52,104 @@ export class DownloadTask {
53
52
  private async downloadFile(params: DownloadTaskParams): Promise<void> {
54
53
  const httpRequest = http.createHttp();
55
54
  this.hash = params.hash;
55
+ let writer: fileIo.File | null = null;
56
+ let contentLength = 0;
57
+ let received = 0;
58
+ let writeError: Error | null = null;
59
+ let writeQueue = Promise.resolve();
60
+
61
+ const closeWriter = async () => {
62
+ if (writer) {
63
+ await fileIo.close(writer);
64
+ writer = null;
65
+ }
66
+ };
67
+
68
+ const dataEndPromise = new Promise<void>((resolve, reject) => {
69
+ httpRequest.on('dataEnd', () => {
70
+ writeQueue
71
+ .then(async () => {
72
+ if (writeError) {
73
+ throw writeError;
74
+ }
75
+ await closeWriter();
76
+ resolve();
77
+ })
78
+ .catch(async error => {
79
+ await closeWriter();
80
+ reject(error);
81
+ });
82
+ });
83
+ });
56
84
 
57
85
  try {
58
- try {
59
- let exists = fileIo.accessSync(params.targetFile);
60
- if (exists) {
61
- await fileIo.unlink(params.targetFile);
62
- } else {
63
- const targetDir = params.targetFile.substring(
64
- 0,
65
- params.targetFile.lastIndexOf('/'),
66
- );
67
- exists = fileIo.accessSync(targetDir);
68
- if (!exists) {
69
- await fileIo.mkdir(targetDir);
70
- }
86
+ let exists = fileIo.accessSync(params.targetFile);
87
+ if (exists) {
88
+ await fileIo.unlink(params.targetFile);
89
+ } else {
90
+ const targetDir = params.targetFile.substring(
91
+ 0,
92
+ params.targetFile.lastIndexOf('/'),
93
+ );
94
+ exists = fileIo.accessSync(targetDir);
95
+ if (!exists) {
96
+ await fileIo.mkdir(targetDir);
71
97
  }
72
- } catch (error) {
73
- throw error;
74
98
  }
75
99
 
76
- const response = await httpRequest.request(params.url, {
100
+ writer = await fileIo.open(
101
+ params.targetFile,
102
+ fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE,
103
+ );
104
+
105
+ httpRequest.on('headersReceive', (header: Record<string, string>) => {
106
+ if (!header) {
107
+ return;
108
+ }
109
+ const lengthKey = Object.keys(header).find(
110
+ key => key.toLowerCase() === 'content-length',
111
+ );
112
+ if (!lengthKey) {
113
+ return;
114
+ }
115
+ const length = parseInt(header[lengthKey], 10);
116
+ if (!Number.isNaN(length)) {
117
+ contentLength = length;
118
+ }
119
+ });
120
+
121
+ httpRequest.on('dataReceive', (data: ArrayBuffer) => {
122
+ if (writeError) {
123
+ return;
124
+ }
125
+ received += data.byteLength;
126
+ writeQueue = writeQueue.then(async () => {
127
+ if (!writer || writeError) {
128
+ return;
129
+ }
130
+ try {
131
+ await fileIo.write(writer.fd, data);
132
+ } catch (error) {
133
+ writeError = error as Error;
134
+ }
135
+ });
136
+ this.onProgressUpdate(received, contentLength);
137
+ });
138
+
139
+ httpRequest.on(
140
+ 'dataReceiveProgress',
141
+ (data: http.DataReceiveProgressInfo) => {
142
+ if (data.totalSize > 0) {
143
+ contentLength = data.totalSize;
144
+ }
145
+ if (data.receiveSize > received) {
146
+ received = data.receiveSize;
147
+ }
148
+ this.onProgressUpdate(received, contentLength);
149
+ },
150
+ );
151
+
152
+ const responseCode = await httpRequest.requestInStream(params.url, {
77
153
  method: http.RequestMethod.GET,
78
154
  readTimeout: 60000,
79
155
  connectTimeout: 60000,
@@ -81,32 +157,14 @@ export class DownloadTask {
81
157
  'Content-Type': 'application/octet-stream',
82
158
  },
83
159
  });
84
- if (response.responseCode > 299) {
85
- throw Error(`Server error: ${response.responseCode}`);
160
+ if (responseCode > 299) {
161
+ throw Error(`Server error: ${responseCode}`);
86
162
  }
87
163
 
88
- const contentLength = parseInt(response.header['content-length'] || '0');
89
- const writer = await fileIo.open(
90
- params.targetFile,
91
- fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE,
92
- );
93
- let received = 0;
94
- const data = response.result as ArrayBuffer;
95
- const chunks = Math.ceil(data.byteLength / this.DOWNLOAD_CHUNK_SIZE);
96
- for (let i = 0; i < chunks; i++) {
97
- const start = i * this.DOWNLOAD_CHUNK_SIZE;
98
- const end = Math.min(start + this.DOWNLOAD_CHUNK_SIZE, data.byteLength);
99
- const chunk = data.slice(start, end);
100
-
101
- await fileIo.write(writer.fd, chunk);
102
- received += chunk.byteLength;
103
-
104
- this.onProgressUpdate(received, contentLength);
105
- }
106
- await fileIo.close(writer);
164
+ await dataEndPromise;
107
165
  const stats = await fileIo.stat(params.targetFile);
108
166
  const fileSize = stats.size;
109
- if (fileSize !== contentLength) {
167
+ if (contentLength > 0 && fileSize !== contentLength) {
110
168
  throw Error(
111
169
  `Download incomplete: expected ${contentLength} bytes but got ${stats.size} bytes`,
112
170
  );
@@ -115,6 +173,15 @@ export class DownloadTask {
115
173
  console.error('Download failed:', error);
116
174
  throw error;
117
175
  } finally {
176
+ try {
177
+ await closeWriter();
178
+ } catch (closeError) {
179
+ console.error('Failed to close file:', closeError);
180
+ }
181
+ httpRequest.off('headersReceive');
182
+ httpRequest.off('dataReceive');
183
+ httpRequest.off('dataReceiveProgress');
184
+ httpRequest.off('dataEnd');
118
185
  httpRequest.destroy();
119
186
  }
120
187
  }
@@ -432,7 +499,6 @@ export class DownloadTask {
432
499
  default:
433
500
  throw Error(`Unknown task type: ${params.type}`);
434
501
  }
435
-
436
502
  } catch (error) {
437
503
  console.error('Task execution failed:', error.message);
438
504
  if (params.type !== DownloadTaskParams.TASK_TYPE_CLEANUP) {
@@ -86,7 +86,7 @@ export class PushyTurboModule extends TurboModule {
86
86
  return UpdateModuleImpl.getLocalHashInfo(this.context, hash);
87
87
  }
88
88
 
89
- async setUuid(uuid: string): Promise<boolean> {
89
+ async setUuid(uuid: string): Promise<void> {
90
90
  logger.debug(TAG, ',call setUuid');
91
91
  return UpdateModuleImpl.setUuid(this.context, uuid);
92
92
  }
@@ -131,15 +131,6 @@ export class PushyTurboModule extends TurboModule {
131
131
  return UpdateModuleImpl.downloadFullUpdate(this.context, options);
132
132
  }
133
133
 
134
- async downloadAndInstallApk(options: {
135
- url: string;
136
- target: string;
137
- hash: string;
138
- }): Promise<void> {
139
- logger.debug(TAG, ',call downloadAndInstallApk');
140
- return UpdateModuleImpl.downloadAndInstallApk(this.mUiCtx, options);
141
- }
142
-
143
134
  addListener(_eventName: string): void {
144
135
  logger.debug(TAG, ',call addListener');
145
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "10.37.4",
3
+ "version": "10.37.6",
4
4
  "description": "react-native hot update",
5
5
  "main": "src/index",
6
6
  "scripts": {
package/src/client.ts CHANGED
@@ -282,19 +282,23 @@ export class Pushy {
282
282
  type: 'errorChecking',
283
283
  message: this.t('error_cannot_connect_backup', { message: e.message }),
284
284
  });
285
- const backupEndpoints = await this.getBackupEndpoints();
285
+ const backupEndpoints = await this.getBackupEndpoints().catch();
286
286
  if (backupEndpoints) {
287
- try {
288
- resp = await promiseAny(
289
- backupEndpoints.map(endpoint =>
290
- enhancedFetch(this.getCheckUrl(endpoint), fetchPayload),
291
- ),
292
- );
293
- } catch (err: any) {
294
- this.throwIfEnabled(Error('errorCheckingUseBackup'));
295
- }
287
+ resp = await promiseAny(
288
+ backupEndpoints.map(endpoint =>
289
+ enhancedFetch(this.getCheckUrl(endpoint), fetchPayload),
290
+ ),
291
+ ).catch(() => {
292
+ this.report({
293
+ type: 'errorChecking',
294
+ message: this.t('errorCheckingUseBackup'),
295
+ });
296
+ });
296
297
  } else {
297
- this.throwIfEnabled(Error('errorCheckingGetBackup'));
298
+ this.report({
299
+ type: 'errorChecking',
300
+ message: this.t('errorCheckingGetBackup'),
301
+ });
298
302
  }
299
303
  }
300
304
  if (!resp) {
@@ -316,7 +320,7 @@ export class Pushy {
316
320
  type: 'errorChecking',
317
321
  message: errorMessage,
318
322
  });
319
- this.throwIfEnabled(Error(errorMessage));
323
+ this.throwIfEnabled(Error('errorChecking: ' + errorMessage));
320
324
  return this.lastRespJson ? await this.lastRespJson : emptyObj;
321
325
  }
322
326
  this.lastRespJson = resp.json();