react-native-custom-ota 1.0.4 → 1.0.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.
package/bin/custom-ota.js CHANGED
@@ -55,7 +55,7 @@ function detectAppId() {
55
55
  try {
56
56
  const data = JSON.parse(fs.readFileSync(appJsonPath, "utf8"));
57
57
  if (data.name) return String(data.name).toLowerCase();
58
- } catch (e) {}
58
+ } catch (e) { }
59
59
  }
60
60
 
61
61
  const pkgJsonPath = path.join(root, "package.json");
@@ -63,7 +63,7 @@ function detectAppId() {
63
63
  try {
64
64
  const data = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
65
65
  if (data.name) return String(data.name).toLowerCase();
66
- } catch (e) {}
66
+ } catch (e) { }
67
67
  }
68
68
 
69
69
  return "com.otademoapp";
@@ -97,7 +97,15 @@ async function buildAndPublish(p) {
97
97
 
98
98
  const bundleFile = p === "android" ? "index.android.bundle" : "main.jsbundle";
99
99
  const bundlePath = path.join(outDir, bundleFile);
100
- const assetsDir = path.join(outDir, "assets");
100
+
101
+ // Metro bundle output:
102
+ // Android tạo drawable-xxx/ và raw/, iOS tạo assets/ bên trong thư mục chỉ định.
103
+ // Đặt assetsDir = outDir để đồng bộ cấu trúc với JSBundle offline.
104
+ const testBrokenAssets =
105
+ process.env.TEST_BROKEN_ASSETS === "true" ||
106
+ args.includes("--broken-assets");
107
+
108
+ const assetsDir = testBrokenAssets ? path.join(outDir, "assets") : outDir;
101
109
 
102
110
  // 1. Metro Bundle
103
111
  console.log(`📦 [1/3] Đang chạy Metro Bundle...`);
@@ -165,10 +165,32 @@ class CustomOTAManager {
165
165
  const zipPath = `${storage_1.TEMP_DIR}/update-${version}.zip`;
166
166
  await react_native_fs_1.default.unlink(zipPath).catch(() => { });
167
167
  // Download zip file với theo dõi byte chính xác
168
+ // LƯU Ý QUAN TRỌNG VỀ RNFS TRÊN IOS:
169
+ // RNFS Downloader.m trên iOS chỉ gán `_statusCode` và `_contentLength` khi có `begin` callback.
170
+ // Nếu thiếu `begin`, `_statusCode` sẽ là nil, dẫn tới điều kiện emit progress bị bỏ qua hoàn toàn!
168
171
  const downloadRes = react_native_fs_1.default.downloadFile({
169
172
  fromUrl: update.downloadUrl.startsWith("http") ? update.downloadUrl : `${update.downloadUrl}`,
170
173
  toFile: zipPath,
171
- progressDivider: 1,
174
+ begin: (res) => {
175
+ const total = res.contentLength > 0 ? res.contentLength : (update.size || 0);
176
+ const initialProgress = {
177
+ totalBytes: total,
178
+ receivedBytes: 0,
179
+ percentage: 0,
180
+ progress: 0
181
+ };
182
+ if (onProgress) {
183
+ try {
184
+ onProgress(initialProgress, 0);
185
+ }
186
+ catch {
187
+ onProgress(0);
188
+ }
189
+ }
190
+ this.emitProgress(initialProgress);
191
+ },
192
+ progressDivider: 0,
193
+ progressInterval: 50,
172
194
  progress: ({ bytesWritten, contentLength }) => {
173
195
  const total = contentLength > 0 ? contentLength : (update.size || 0);
174
196
  const percent = total > 0 ? Math.min(100, Math.round((bytesWritten / total) * 100)) : 0;
@@ -180,7 +202,6 @@ class CustomOTAManager {
180
202
  progress: progressVal
181
203
  };
182
204
  if (onProgress) {
183
- // Callback nhận cả object lẫn hỗ trợ kiểu cũ nhận số
184
205
  try {
185
206
  onProgress(progressObj, progressVal);
186
207
  }
@@ -196,6 +217,23 @@ class CustomOTAManager {
196
217
  this.emitDetailedStatus(types_1.DetailedStatus.ERROR, `Tải thất bại với mã lỗi HTTP ${res.statusCode}`);
197
218
  throw new Error(`Download failed with status ${res.statusCode}`);
198
219
  }
220
+ // Đảm bảo phát ra trạng thái 100% khi download hoàn tất
221
+ const finalTotal = update.size || 0;
222
+ const completedProgress = {
223
+ totalBytes: finalTotal,
224
+ receivedBytes: finalTotal,
225
+ percentage: 100,
226
+ progress: 1
227
+ };
228
+ if (onProgress) {
229
+ try {
230
+ onProgress(completedProgress, 1);
231
+ }
232
+ catch {
233
+ onProgress(1);
234
+ }
235
+ }
236
+ this.emitProgress(completedProgress);
199
237
  // SHA-256 verification
200
238
  this.emitDetailedStatus(types_1.DetailedStatus.VERIFYING, "Đang kiểm tra tính toàn vẹn (SHA-256)...");
201
239
  const actualHash = await react_native_fs_1.default.hash(zipPath, "sha256");
@@ -159,10 +159,32 @@ export class CustomOTAManager {
159
159
  const zipPath = `${TEMP_DIR}/update-${version}.zip`;
160
160
  await RNFS.unlink(zipPath).catch(() => { });
161
161
  // Download zip file với theo dõi byte chính xác
162
+ // LƯU Ý QUAN TRỌNG VỀ RNFS TRÊN IOS:
163
+ // RNFS Downloader.m trên iOS chỉ gán `_statusCode` và `_contentLength` khi có `begin` callback.
164
+ // Nếu thiếu `begin`, `_statusCode` sẽ là nil, dẫn tới điều kiện emit progress bị bỏ qua hoàn toàn!
162
165
  const downloadRes = RNFS.downloadFile({
163
166
  fromUrl: update.downloadUrl.startsWith("http") ? update.downloadUrl : `${update.downloadUrl}`,
164
167
  toFile: zipPath,
165
- progressDivider: 1,
168
+ begin: (res) => {
169
+ const total = res.contentLength > 0 ? res.contentLength : (update.size || 0);
170
+ const initialProgress = {
171
+ totalBytes: total,
172
+ receivedBytes: 0,
173
+ percentage: 0,
174
+ progress: 0
175
+ };
176
+ if (onProgress) {
177
+ try {
178
+ onProgress(initialProgress, 0);
179
+ }
180
+ catch {
181
+ onProgress(0);
182
+ }
183
+ }
184
+ this.emitProgress(initialProgress);
185
+ },
186
+ progressDivider: 0,
187
+ progressInterval: 50,
166
188
  progress: ({ bytesWritten, contentLength }) => {
167
189
  const total = contentLength > 0 ? contentLength : (update.size || 0);
168
190
  const percent = total > 0 ? Math.min(100, Math.round((bytesWritten / total) * 100)) : 0;
@@ -174,7 +196,6 @@ export class CustomOTAManager {
174
196
  progress: progressVal
175
197
  };
176
198
  if (onProgress) {
177
- // Callback nhận cả object lẫn hỗ trợ kiểu cũ nhận số
178
199
  try {
179
200
  onProgress(progressObj, progressVal);
180
201
  }
@@ -190,6 +211,23 @@ export class CustomOTAManager {
190
211
  this.emitDetailedStatus(DetailedStatus.ERROR, `Tải thất bại với mã lỗi HTTP ${res.statusCode}`);
191
212
  throw new Error(`Download failed with status ${res.statusCode}`);
192
213
  }
214
+ // Đảm bảo phát ra trạng thái 100% khi download hoàn tất
215
+ const finalTotal = update.size || 0;
216
+ const completedProgress = {
217
+ totalBytes: finalTotal,
218
+ receivedBytes: finalTotal,
219
+ percentage: 100,
220
+ progress: 1
221
+ };
222
+ if (onProgress) {
223
+ try {
224
+ onProgress(completedProgress, 1);
225
+ }
226
+ catch {
227
+ onProgress(1);
228
+ }
229
+ }
230
+ this.emitProgress(completedProgress);
193
231
  // SHA-256 verification
194
232
  this.emitDetailedStatus(DetailedStatus.VERIFYING, "Đang kiểm tra tính toàn vẹn (SHA-256)...");
195
233
  const actualHash = await RNFS.hash(zipPath, "sha256");
@@ -165,10 +165,32 @@ class CustomOTAManager {
165
165
  const zipPath = `${storage_1.TEMP_DIR}/update-${version}.zip`;
166
166
  await react_native_fs_1.default.unlink(zipPath).catch(() => { });
167
167
  // Download zip file với theo dõi byte chính xác
168
+ // LƯU Ý QUAN TRỌNG VỀ RNFS TRÊN IOS:
169
+ // RNFS Downloader.m trên iOS chỉ gán `_statusCode` và `_contentLength` khi có `begin` callback.
170
+ // Nếu thiếu `begin`, `_statusCode` sẽ là nil, dẫn tới điều kiện emit progress bị bỏ qua hoàn toàn!
168
171
  const downloadRes = react_native_fs_1.default.downloadFile({
169
172
  fromUrl: update.downloadUrl.startsWith("http") ? update.downloadUrl : `${update.downloadUrl}`,
170
173
  toFile: zipPath,
171
- progressDivider: 1,
174
+ begin: (res) => {
175
+ const total = res.contentLength > 0 ? res.contentLength : (update.size || 0);
176
+ const initialProgress = {
177
+ totalBytes: total,
178
+ receivedBytes: 0,
179
+ percentage: 0,
180
+ progress: 0
181
+ };
182
+ if (onProgress) {
183
+ try {
184
+ onProgress(initialProgress, 0);
185
+ }
186
+ catch {
187
+ onProgress(0);
188
+ }
189
+ }
190
+ this.emitProgress(initialProgress);
191
+ },
192
+ progressDivider: 0,
193
+ progressInterval: 50,
172
194
  progress: ({ bytesWritten, contentLength }) => {
173
195
  const total = contentLength > 0 ? contentLength : (update.size || 0);
174
196
  const percent = total > 0 ? Math.min(100, Math.round((bytesWritten / total) * 100)) : 0;
@@ -180,7 +202,6 @@ class CustomOTAManager {
180
202
  progress: progressVal
181
203
  };
182
204
  if (onProgress) {
183
- // Callback nhận cả object lẫn hỗ trợ kiểu cũ nhận số
184
205
  try {
185
206
  onProgress(progressObj, progressVal);
186
207
  }
@@ -196,6 +217,23 @@ class CustomOTAManager {
196
217
  this.emitDetailedStatus(types_1.DetailedStatus.ERROR, `Tải thất bại với mã lỗi HTTP ${res.statusCode}`);
197
218
  throw new Error(`Download failed with status ${res.statusCode}`);
198
219
  }
220
+ // Đảm bảo phát ra trạng thái 100% khi download hoàn tất
221
+ const finalTotal = update.size || 0;
222
+ const completedProgress = {
223
+ totalBytes: finalTotal,
224
+ receivedBytes: finalTotal,
225
+ percentage: 100,
226
+ progress: 1
227
+ };
228
+ if (onProgress) {
229
+ try {
230
+ onProgress(completedProgress, 1);
231
+ }
232
+ catch {
233
+ onProgress(1);
234
+ }
235
+ }
236
+ this.emitProgress(completedProgress);
199
237
  // SHA-256 verification
200
238
  this.emitDetailedStatus(types_1.DetailedStatus.VERIFYING, "Đang kiểm tra tính toàn vẹn (SHA-256)...");
201
239
  const actualHash = await react_native_fs_1.default.hash(zipPath, "sha256");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-custom-ota",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Self-hosted OTA (Over-The-Air) update library for React Native with automatic rollback, similar to react-native-code-push",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
package/src/manager.ts CHANGED
@@ -211,10 +211,31 @@ export class CustomOTAManager {
211
211
  await RNFS.unlink(zipPath).catch(() => {});
212
212
 
213
213
  // Download zip file với theo dõi byte chính xác
214
+ // LƯU Ý QUAN TRỌNG VỀ RNFS TRÊN IOS:
215
+ // RNFS Downloader.m trên iOS chỉ gán `_statusCode` và `_contentLength` khi có `begin` callback.
216
+ // Nếu thiếu `begin`, `_statusCode` sẽ là nil, dẫn tới điều kiện emit progress bị bỏ qua hoàn toàn!
214
217
  const downloadRes = RNFS.downloadFile({
215
218
  fromUrl: update.downloadUrl.startsWith("http") ? update.downloadUrl : `${update.downloadUrl}`,
216
219
  toFile: zipPath,
217
- progressDivider: 1,
220
+ begin: (res) => {
221
+ const total = res.contentLength > 0 ? res.contentLength : (update.size || 0);
222
+ const initialProgress: DownloadProgress = {
223
+ totalBytes: total,
224
+ receivedBytes: 0,
225
+ percentage: 0,
226
+ progress: 0
227
+ };
228
+ if (onProgress) {
229
+ try {
230
+ (onProgress as any)(initialProgress, 0);
231
+ } catch {
232
+ (onProgress as any)(0);
233
+ }
234
+ }
235
+ this.emitProgress(initialProgress);
236
+ },
237
+ progressDivider: 0,
238
+ progressInterval: 50,
218
239
  progress: ({ bytesWritten, contentLength }) => {
219
240
  const total = contentLength > 0 ? contentLength : (update.size || 0);
220
241
  const percent = total > 0 ? Math.min(100, Math.round((bytesWritten / total) * 100)) : 0;
@@ -228,7 +249,6 @@ export class CustomOTAManager {
228
249
  };
229
250
 
230
251
  if (onProgress) {
231
- // Callback nhận cả object lẫn hỗ trợ kiểu cũ nhận số
232
252
  try {
233
253
  (onProgress as any)(progressObj, progressVal);
234
254
  } catch {
@@ -246,6 +266,23 @@ export class CustomOTAManager {
246
266
  throw new Error(`Download failed with status ${res.statusCode}`);
247
267
  }
248
268
 
269
+ // Đảm bảo phát ra trạng thái 100% khi download hoàn tất
270
+ const finalTotal = update.size || 0;
271
+ const completedProgress: DownloadProgress = {
272
+ totalBytes: finalTotal,
273
+ receivedBytes: finalTotal,
274
+ percentage: 100,
275
+ progress: 1
276
+ };
277
+ if (onProgress) {
278
+ try {
279
+ (onProgress as any)(completedProgress, 1);
280
+ } catch {
281
+ (onProgress as any)(1);
282
+ }
283
+ }
284
+ this.emitProgress(completedProgress);
285
+
249
286
  // SHA-256 verification
250
287
  this.emitDetailedStatus(DetailedStatus.VERIFYING, "Đang kiểm tra tính toàn vẹn (SHA-256)...");
251
288
  const actualHash = await RNFS.hash(zipPath, "sha256");