react-native-ota-hot-update 2.0.3 → 2.0.4

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 (66) hide show
  1. package/README.md +17 -98
  2. package/android/generated/java/com/otahotupdate/NativeOtaHotUpdateSpec.java +4 -0
  3. package/android/generated/jni/RNOtaHotUpdateSpec-generated.cpp +6 -0
  4. package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI-generated.cpp +7 -0
  5. package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI.h +9 -0
  6. package/android/src/main/java/com/otahotupdate/OtaHotUpdateModule.kt +7 -0
  7. package/android/src/oldarch/OtaHotUpdateSpec.kt +1 -0
  8. package/ios/OtaHotUpdate.mm +13 -0
  9. package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec-generated.mm +7 -0
  10. package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec.h +3 -0
  11. package/ios/generated/RNOtaHotUpdateSpecJSI-generated.cpp +7 -0
  12. package/ios/generated/RNOtaHotUpdateSpecJSI.h +9 -0
  13. package/lib/commonjs/NativeOtaHotUpdate.js.map +1 -1
  14. package/lib/commonjs/gits/helper/fileReader.js +36 -0
  15. package/lib/commonjs/gits/helper/fileReader.js.map +1 -0
  16. package/lib/commonjs/gits/helper/fs.js +152 -0
  17. package/lib/commonjs/gits/helper/fs.js.map +1 -0
  18. package/lib/commonjs/gits/index.js +130 -0
  19. package/lib/commonjs/gits/index.js.map +1 -0
  20. package/lib/commonjs/index.d.js +6 -0
  21. package/lib/commonjs/index.d.js.map +1 -0
  22. package/lib/commonjs/index.js +63 -1
  23. package/lib/commonjs/index.js.map +1 -1
  24. package/lib/commonjs/type.js +2 -0
  25. package/lib/commonjs/type.js.map +1 -0
  26. package/lib/module/NativeOtaHotUpdate.js.map +1 -1
  27. package/lib/module/gits/helper/fileReader.js +36 -0
  28. package/lib/module/gits/helper/fileReader.js.map +1 -0
  29. package/lib/module/gits/helper/fs.js +139 -0
  30. package/lib/module/gits/helper/fs.js.map +1 -0
  31. package/lib/module/gits/index.js +123 -0
  32. package/lib/module/gits/index.js.map +1 -0
  33. package/lib/module/index.d.js +4 -0
  34. package/lib/module/index.d.js.map +1 -0
  35. package/lib/module/index.js +62 -1
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/type.js +2 -0
  38. package/lib/module/type.js.map +1 -0
  39. package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts +1 -0
  40. package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/src/gits/helper/fs.d.ts +16 -0
  42. package/lib/typescript/commonjs/src/gits/helper/fs.d.ts.map +1 -0
  43. package/lib/typescript/commonjs/src/gits/index.d.ts +25 -0
  44. package/lib/typescript/commonjs/src/gits/index.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/src/index.d.ts +25 -8
  46. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/src/type.d.ts +145 -0
  48. package/lib/typescript/commonjs/src/type.d.ts.map +1 -0
  49. package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts +1 -0
  50. package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts.map +1 -1
  51. package/lib/typescript/module/src/gits/helper/fs.d.ts +16 -0
  52. package/lib/typescript/module/src/gits/helper/fs.d.ts.map +1 -0
  53. package/lib/typescript/module/src/gits/index.d.ts +25 -0
  54. package/lib/typescript/module/src/gits/index.d.ts.map +1 -0
  55. package/lib/typescript/module/src/index.d.ts +25 -8
  56. package/lib/typescript/module/src/index.d.ts.map +1 -1
  57. package/lib/typescript/module/src/type.d.ts +145 -0
  58. package/lib/typescript/module/src/type.d.ts.map +1 -0
  59. package/package.json +8 -2
  60. package/src/NativeOtaHotUpdate.ts +1 -0
  61. package/src/gits/helper/fileReader.js +44 -0
  62. package/src/gits/helper/fs.ts +154 -0
  63. package/src/gits/index.ts +118 -0
  64. package/src/index.d.ts +80 -0
  65. package/src/index.tsx +62 -9
  66. package/src/type.ts +163 -0
@@ -0,0 +1,145 @@
1
+ export interface UpdateOption {
2
+ /**
3
+ * Optional headers to include with the update request.
4
+ * Typically used for authentication or custom metadata.
5
+ */
6
+ headers?: Record<string, string>;
7
+ /**
8
+ * Callback to track download progress.
9
+ * @param received - Number of bytes received.
10
+ * @param total - Total number of bytes to be downloaded.
11
+ */
12
+ progress?(received: string, total: string): void;
13
+ /**
14
+ * Callback triggered when the update succeeds.
15
+ */
16
+ updateSuccess?(): void;
17
+ /**
18
+ * Callback triggered when the update fails.
19
+ * @param message - Optional error message or object describing the failure.
20
+ */
21
+ updateFail?(message?: string | Error): void;
22
+ /**
23
+ * Indicates whether the app should restart after installing the update.
24
+ * Default: `false`.
25
+ */
26
+ restartAfterInstall?: boolean;
27
+ /**
28
+ * Custom extension for the bundle file, if applicable.
29
+ * For example: '.jsbundle'.
30
+ */
31
+ extensionBundle?: string;
32
+ }
33
+ /**
34
+ * Options for updating a Git repository.
35
+ */
36
+ export interface UpdateGitOption {
37
+ /**
38
+ * The URL of the Git repository to check update.
39
+ */
40
+ url: string;
41
+ /**
42
+ * Optional callback to monitor the progress of the update.
43
+ * @param received - The number of bytes received so far.
44
+ * @param total - The total number of bytes to be received.
45
+ */
46
+ onProgress?(received: number, total: number): void;
47
+ /**
48
+ * Optional branch name to update or switch to.
49
+ * If not specified, the default branch will be main.
50
+ */
51
+ branch?: string;
52
+ /**
53
+ * Optional name of the folder where the repository will be cloned or updated.
54
+ * If not specified, a default folder name will be git_hot_update.
55
+ */
56
+ folderName?: string;
57
+ /**
58
+ * Optional callback when pull success, should handle for case update.
59
+ */
60
+ onPullSuccess?(): void;
61
+ /**
62
+ * Optional callback when pull failed.
63
+ */
64
+ onPullFailed?(msg: string): void;
65
+ /**
66
+ * Optional callback when clone success, handle it in the first time clone.
67
+ */
68
+ onCloneSuccess?(): void;
69
+ /**
70
+ * Optional callback when clone failed.
71
+ */
72
+ onCloneFailed?(msg: string): void;
73
+ /**
74
+ * The bundle path of the Git repository, it should place at root.
75
+ * Eg: the folder name is git_hot_update, bundle file place at git_hot_update/output/main.jsbundle, so bundlePath should be: "output/main.jsbundle".
76
+ */
77
+ bundlePath: string;
78
+ /**
79
+ * Optional restart app after clone / pull success for apply the new bundle.
80
+ */
81
+ restartAfterInstall?: boolean;
82
+ /**
83
+ * Optional when all process success, use for set loading false.
84
+ */
85
+ onFinishProgress?(): void;
86
+ }
87
+ export interface CloneOption {
88
+ /**
89
+ * The Git repository URL to be cloned.
90
+ * Example: "https://github.com/user/repo.git".
91
+ */
92
+ url: string;
93
+ /**
94
+ * Optional name of the folder where the repository will be cloned.
95
+ * If not provided, the repository name git_hot_update will be used.
96
+ */
97
+ folderName?: string;
98
+ /**
99
+ * Callback to track the progress of the cloning process.
100
+ * @param received - Number of bytes received so far.
101
+ * @param total - Total number of bytes to be downloaded.
102
+ */
103
+ onProgress?(received: number, total: number): void;
104
+ /**
105
+ * The branch to be checked out after cloning.
106
+ * Defaults to the repository's default branch if not specified.
107
+ */
108
+ branch?: string;
109
+ /**
110
+ * The bundle path of the Git repository, it should place at root.
111
+ * Eg: the folder name is git_hot_update, bundle file place at git_hot_update/output/main.jsbundle, so bundlePath should be: "output/main.jsbundle".
112
+ */
113
+ bundlePath: string;
114
+ /**
115
+ * Optional username to set up the Git user configuration.
116
+ * Used for operations like committing or signing, not for authentication.
117
+ * Example: "John Doe".
118
+ */
119
+ userName?: string;
120
+ /**
121
+ * Optional email to set up the Git user configuration.
122
+ * Used for operations like committing or signing, not for authentication.
123
+ * Example: "john.doe@example.com".
124
+ */
125
+ email?: string;
126
+ }
127
+ export interface PullOption {
128
+ /**
129
+ * Optional name of the folder containing the Git repository to pull from.
130
+ * Defaults to the current directory if not specified.
131
+ */
132
+ folderName?: string;
133
+ /**
134
+ * Callback to track the progress of the pull operation.
135
+ * @param received - Number of bytes received so far.
136
+ * @param total - Total number of bytes to be downloaded.
137
+ */
138
+ onProgress?(received: number, total: number): void;
139
+ /**
140
+ * The name of the branch to pull updates from.
141
+ * This branch must exist in the remote repository.
142
+ */
143
+ branch: string;
144
+ }
145
+ //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../src/type.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjD;;OAEG;IACH,aAAa,CAAC,IAAI,IAAI,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAE5C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,UAAU,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,aAAa,CAAC,IAAI,IAAI,CAAC;IACvB;;OAEG;IACH,YAAY,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;OAEG;IACH,cAAc,CAAC,IAAI,IAAI,CAAC;IACxB;;OAEG;IACH,aAAa,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;OAEG;IACH,gBAAgB,CAAC,IAAI,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnD;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-ota-hot-update",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Hot update for react native",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
@@ -69,6 +69,10 @@
69
69
  "publishConfig": {
70
70
  "registry": "https://registry.npmjs.org/"
71
71
  },
72
+ "dependencies": {
73
+ "buffer": "^6.0.3",
74
+ "isomorphic-git": "git+https://github.com/vantuan88291/isomorphic-git.git"
75
+ },
72
76
  "devDependencies": {
73
77
  "@commitlint/config-conventional": "^17.0.2",
74
78
  "@evilmartians/lefthook": "^1.5.0",
@@ -87,6 +91,7 @@
87
91
  "react": "18.3.1",
88
92
  "react-native": "0.76.3",
89
93
  "react-native-builder-bob": "^0.33.1",
94
+ "react-native-fs": "2.20.0",
90
95
  "release-it": "^17.10.0",
91
96
  "turbo": "^1.10.7",
92
97
  "typescript": "^5.2.2"
@@ -96,7 +101,8 @@
96
101
  },
97
102
  "peerDependencies": {
98
103
  "react": "*",
99
- "react-native": "*"
104
+ "react-native": "*",
105
+ "react-native-fs": "*"
100
106
  },
101
107
  "workspaces": [
102
108
  "example"
@@ -3,6 +3,7 @@ import { TurboModuleRegistry } from 'react-native';
3
3
 
4
4
  export interface Spec extends TurboModule {
5
5
  setupBundlePath(path: string, extension: string): Promise<boolean>;
6
+ setExactBundlePath(path: string): Promise<boolean>;
6
7
  deleteBundle(i: number): Promise<boolean>;
7
8
  restart(): void;
8
9
  getCurrentVersion(a: number): Promise<string>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @format
3
+ */
4
+ FileReader.prototype.readAsArrayBuffer = function (blob) {
5
+ if (this.readyState === this.LOADING) throw new Error('InvalidStateError');
6
+ this._setReadyState(this.LOADING);
7
+ this._result = null;
8
+ this._error = null;
9
+ const fr = new FileReader();
10
+ fr.onloadend = () => {
11
+ const content = atob(fr.result.replace(/data:[^;]+;base64,/, ''));
12
+ const buffer = new ArrayBuffer(content.length);
13
+ const view = new Uint8Array(buffer);
14
+ view.set(Array.from(content).map((c) => c.charCodeAt(0)));
15
+ this._result = buffer;
16
+ this._setReadyState(this.DONE);
17
+ };
18
+ fr.readAsDataURL(blob);
19
+ };
20
+
21
+ // from: https://stackoverflow.com/questions/42829838/react-native-atob-btoa-not-working-without-remote-js-debugging
22
+ const chars =
23
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
24
+ const atob = (input = '') => {
25
+ let str = input.replace(/[=]+$/, '');
26
+ let output = '';
27
+
28
+ if (str.length % 4 == 1) {
29
+ throw new Error(
30
+ "'atob' failed: The string to be decoded is not correctly encoded."
31
+ );
32
+ }
33
+ for (
34
+ let bc = 0, bs = 0, buffer, i = 0;
35
+ (buffer = str.charAt(i++));
36
+ ~buffer && ((bs = bc % 4 ? bs * 64 + buffer : buffer), bc++ % 4)
37
+ ? (output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6))))
38
+ : 0
39
+ ) {
40
+ buffer = chars.indexOf(buffer);
41
+ }
42
+
43
+ return output;
44
+ };
@@ -0,0 +1,154 @@
1
+ import { Buffer } from 'buffer';
2
+
3
+ let RNFS = {
4
+ unlink: console.log,
5
+ readdir: console.log,
6
+ mkdir: console.log,
7
+ readFile: console.log,
8
+ writeFile: console.log,
9
+ stat: console.log,
10
+ };
11
+ try {
12
+ RNFS = require('react-native-fs');
13
+ } catch {}
14
+
15
+ function Err(name: string) {
16
+ return class extends Error {
17
+ public code = name;
18
+ constructor(...args: any) {
19
+ super(...args);
20
+ if (this.message) {
21
+ this.message = name + ': ' + this.message;
22
+ } else {
23
+ this.message = name;
24
+ }
25
+ }
26
+ };
27
+ }
28
+
29
+ // const EEXIST = Err('EEXIST'); // <-- Unused because RNFS's mkdir never throws
30
+ const ENOENT = Err('ENOENT');
31
+ const ENOTDIR = Err('ENOTDIR');
32
+ // const ENOTEMPTY = Err('ENOTEMPTY'); // <-- Unused because RNFS's unlink is recursive by default
33
+
34
+ export const readdir = async (path: string) => {
35
+ try {
36
+ return await RNFS.readdir(path);
37
+ } catch (err: any) {
38
+ switch (err.message) {
39
+ case 'Attempt to get length of null array': {
40
+ throw new ENOTDIR(path);
41
+ }
42
+ case 'Folder does not exist': {
43
+ throw new ENOENT(path);
44
+ }
45
+ default:
46
+ throw err;
47
+ }
48
+ }
49
+ };
50
+
51
+ export const mkdir = async (path: string) => {
52
+ return RNFS.mkdir(path);
53
+ };
54
+
55
+ export const readFile = async (
56
+ path: string,
57
+ opts?: string | { [key: string]: string }
58
+ ) => {
59
+ let encoding;
60
+
61
+ if (typeof opts === 'string') {
62
+ encoding = opts;
63
+ } else if (typeof opts === 'object') {
64
+ encoding = opts.encoding;
65
+ }
66
+
67
+ // @ts-ignore
68
+ let result: string | Uint8Array = await RNFS.readFile(
69
+ path,
70
+ encoding || 'base64'
71
+ );
72
+
73
+ if (!encoding) {
74
+ // @ts-ignore
75
+ result = Buffer.from(result, 'base64');
76
+ }
77
+
78
+ return result;
79
+ };
80
+ export const writeFile = async (
81
+ path: string,
82
+ content: string | Uint8Array,
83
+ opts?: string | { [key: string]: string }
84
+ ) => {
85
+ let encoding;
86
+
87
+ if (typeof opts === 'string') {
88
+ encoding = opts;
89
+ } else if (typeof opts === 'object') {
90
+ encoding = opts.encoding;
91
+ }
92
+
93
+ if (typeof content === 'string') {
94
+ encoding = encoding || 'utf8';
95
+ } else {
96
+ encoding = 'base64';
97
+ content = Buffer.from(content).toString('base64');
98
+ }
99
+
100
+ await RNFS.writeFile(path, content as string, encoding);
101
+ };
102
+
103
+ export const stat = async (path: string) => {
104
+ try {
105
+ const r = await RNFS.stat(path);
106
+ // we monkeypatch the result with a `isSymbolicLink` method because isomorphic-git needs it.
107
+ // Since RNFS doesn't appear to support symlinks at all, we'll just always return false.
108
+ // @ts-ignore
109
+ r.isSymbolicLink = () => false;
110
+ return r;
111
+ } catch (err: any) {
112
+ switch (err.message) {
113
+ case 'File does not exist': {
114
+ throw new ENOENT(path);
115
+ }
116
+ default:
117
+ throw err;
118
+ }
119
+ }
120
+ };
121
+
122
+ // Since there are no symbolic links, lstat and stat are equivalent
123
+ export const lstat = stat;
124
+
125
+ export const unlink = async (path: string) => {
126
+ try {
127
+ await RNFS.unlink(path);
128
+ } catch (err: any) {
129
+ switch (err.message) {
130
+ case 'File does not exist': {
131
+ throw new ENOENT(path);
132
+ }
133
+ default:
134
+ throw err;
135
+ }
136
+ }
137
+ };
138
+
139
+ // RNFS doesn't have a separate rmdir method, so we can use unlink for deleting directories too
140
+ export const rmdir = unlink;
141
+
142
+ // These are optional, which is good because there is no equivalent in RNFS
143
+ export const readlink = async () => {
144
+ throw new Error('not implemented');
145
+ };
146
+ export const symlink = async () => {
147
+ throw new Error('not implemented');
148
+ };
149
+
150
+ // Technically we could pull this off by using `readFile` + `writeFile` with the `mode` option
151
+ // However, it's optional, because isomorphic-git will do exactly that (a readFile and a writeFile with the new mode)
152
+ export const chmod = async () => {
153
+ throw new Error('not implemented');
154
+ };
@@ -0,0 +1,118 @@
1
+ import './helper/fileReader.js';
2
+
3
+ // @ts-ignore
4
+ import git, { PromiseFsClient } from 'isomorphic-git/index.umd.min.js';
5
+ import http from 'isomorphic-git/http/web/index.js';
6
+ import * as promises from './helper/fs';
7
+ import type { CloneOption, PullOption } from '../type';
8
+
9
+ const fs: PromiseFsClient = { promises };
10
+ const getFolder = (folderName?: string) => {
11
+ try {
12
+ const { DocumentDirectoryPath } = require('react-native-fs');
13
+ return DocumentDirectoryPath + (folderName || '/git_hot_update');
14
+ } catch (e) {}
15
+ return '';
16
+ };
17
+ /**
18
+ * Should set config after clone success, otherwise cannot pull
19
+ */
20
+ const setConfig = async (
21
+ folderName?: string,
22
+ options?: {
23
+ userName?: string;
24
+ email?: string;
25
+ }
26
+ ) => {
27
+ await git.setConfig({
28
+ fs,
29
+ dir: getFolder(folderName),
30
+ path: options?.userName || 'user.name',
31
+ value: options?.email || 'hotupdate',
32
+ });
33
+ };
34
+ const cloneRepo = async (options: CloneOption) => {
35
+ try {
36
+ await git.clone({
37
+ fs,
38
+ http,
39
+ dir: getFolder(options?.folderName),
40
+ url: options?.url,
41
+ singleBranch: true,
42
+ depth: 1,
43
+ ref: options?.branch,
44
+ onProgress({ loaded, total }: { loaded: number; total: number }) {
45
+ if (options?.onProgress && total > 0) {
46
+ options?.onProgress(loaded, total);
47
+ }
48
+ },
49
+ });
50
+ await setConfig(options?.folderName, {
51
+ email: options?.email,
52
+ userName: options?.userName,
53
+ });
54
+ return {
55
+ success: true,
56
+ msg: null,
57
+ bundle: `${getFolder(options?.folderName)}/${options.bundlePath}`,
58
+ };
59
+ } catch (e: any) {
60
+ return {
61
+ success: false,
62
+ msg: e.toString(),
63
+ bundle: null,
64
+ };
65
+ }
66
+ };
67
+ const pullUpdate = async (options: PullOption) => {
68
+ try {
69
+ let count = 0;
70
+ await git.pull({
71
+ fs,
72
+ http,
73
+ dir: getFolder(options?.folderName),
74
+ ref: options?.branch,
75
+ singleBranch: true,
76
+ onProgress({ loaded, total }: { loaded: number; total: number }) {
77
+ if (total > 0) {
78
+ count = total;
79
+ if (options?.onProgress) {
80
+ options?.onProgress(loaded, total);
81
+ }
82
+ }
83
+ },
84
+ });
85
+ return {
86
+ success: count > 0,
87
+ msg: count > 0 ? 'Pull success' : 'No updated',
88
+ };
89
+ } catch (e: any) {
90
+ console.log(e.toString());
91
+ return {
92
+ success: false,
93
+ msg: e.toString(),
94
+ };
95
+ }
96
+ };
97
+ const getBranchName = async (folderName?: string) => {
98
+ try {
99
+ return await git.currentBranch({
100
+ fs,
101
+ dir: getFolder(folderName),
102
+ fullname: false,
103
+ });
104
+ } catch (e: any) {
105
+ console.log(e.toString());
106
+ return null;
107
+ }
108
+ };
109
+ const removeGitUpdate = (folderName?: string) => {
110
+ fs.promises.unlink(getFolder(folderName));
111
+ };
112
+ export default {
113
+ cloneRepo,
114
+ pullUpdate,
115
+ getBranchName,
116
+ setConfig,
117
+ removeGitUpdate,
118
+ };
package/src/index.d.ts ADDED
@@ -0,0 +1,80 @@
1
+ import type { DownloadManager } from './download';
2
+ import type { UpdateGitOption, UpdateOption } from './type';
3
+
4
+ export interface GitModule {
5
+ checkForGitUpdate(options: UpdateGitOption): Promise<void>;
6
+ removeGitUpdate(folder?: string): void;
7
+ getBranchName(): Promise<string | null>;
8
+ pullUpdate(options: {
9
+ branch: string;
10
+ folderName?: string;
11
+ onProgress?: (received: number, total: number) => void;
12
+ }): Promise<{ success: boolean; msg?: string }>;
13
+ cloneRepo(options: {
14
+ url: string;
15
+ branch?: string;
16
+ folderName?: string;
17
+ bundlePath: string;
18
+ onProgress?: (received: number, total: number) => void;
19
+ }): Promise<{ success: boolean; bundle?: string; msg?: string }>;
20
+ }
21
+
22
+ export interface OtaHotUpdate {
23
+ /**
24
+ * Set up the path to the downloaded bundle file.
25
+ * @param path - The path to the bundle file.
26
+ * @param extension - Optional extension for the bundle file.
27
+ */
28
+ setupBundlePath(path: string, extension?: string): Promise<boolean>;
29
+
30
+ /**
31
+ * Set up an exact path to the bundle file for the update.
32
+ * @param path - The exact path to the bundle file.
33
+ */
34
+ setupExactBundlePath(path: string): Promise<boolean>;
35
+
36
+ /**
37
+ * Remove the current update and optionally restart the app.
38
+ * @param restartAfterRemoved - Whether to restart the app after removing the update.
39
+ */
40
+ removeUpdate(restartAfterRemoved?: boolean): void;
41
+
42
+ /**
43
+ * Download a bundle file from a specified URI.
44
+ * @param downloadManager - The download manager instance.
45
+ * @param uri - The URI of the bundle file.
46
+ * @param version - The version of the bundle file.
47
+ * @param option - Additional update options.
48
+ */
49
+ downloadBundleUri(
50
+ downloadManager: DownloadManager,
51
+ uri: string,
52
+ version: number,
53
+ option?: UpdateOption
54
+ ): Promise<void>;
55
+
56
+ /**
57
+ * Reset the app by restarting it.
58
+ */
59
+ resetApp(): Promise<void>;
60
+
61
+ /**
62
+ * Get the current version of the app.
63
+ */
64
+ getCurrentVersion(): Promise<number>;
65
+
66
+ /**
67
+ * Set the current version of the app.
68
+ * @param version - The version to set.
69
+ */
70
+ setCurrentVersion(version: number): Promise<boolean>;
71
+
72
+ /**
73
+ * Git-related operations.
74
+ */
75
+ git: GitModule;
76
+ }
77
+
78
+ declare const OtaHotUpdate: OtaHotUpdate;
79
+
80
+ export default OtaHotUpdate;
package/src/index.tsx CHANGED
@@ -1,5 +1,7 @@
1
1
  import { NativeModules, Platform } from 'react-native';
2
2
  import type { DownloadManager } from './download';
3
+ import type { UpdateGitOption, UpdateOption } from './type';
4
+ import git from './gits';
3
5
 
4
6
  const LINKING_ERROR =
5
7
  `The package 'react-native-ota-hot-update' doesn't seem to be linked. Make sure: \n\n` +
@@ -10,14 +12,6 @@ const LINKING_ERROR =
10
12
  // @ts-expect-error
11
13
  const isTurboModuleEnabled = global.__turboModuleProxy != null;
12
14
 
13
- export interface UpdateOption {
14
- headers?: object;
15
- progress?(received: string, total: string): void;
16
- updateSuccess?(): void;
17
- updateFail?(message?: string): void;
18
- restartAfterInstall?: boolean;
19
- extensionBundle?: string;
20
- }
21
15
  const OtaHotUpdateModule = isTurboModuleEnabled
22
16
  ? require('./NativeOtaHotUpdate').default
23
17
  : NativeModules.OtaHotUpdate;
@@ -56,6 +50,9 @@ const downloadBundleFile = async (
56
50
  function setupBundlePath(path: string, extension?: string): Promise<boolean> {
57
51
  return RNhotupdate.setupBundlePath(path, extension);
58
52
  }
53
+ function setupExactBundlePath(path: string): Promise<boolean> {
54
+ return RNhotupdate.setExactBundlePath(path);
55
+ }
59
56
  function deleteBundlePath(): Promise<boolean> {
60
57
  return RNhotupdate.deleteBundle(1);
61
58
  }
@@ -139,12 +136,68 @@ async function downloadBundleUri(
139
136
  installFail(option, e);
140
137
  }
141
138
  }
142
-
139
+ const checkForGitUpdate = async (options: UpdateGitOption) => {
140
+ try {
141
+ if (!options.url || !options.bundlePath) {
142
+ throw new Error(`url or bundlePath should not be null`);
143
+ }
144
+ const branch = await git.getBranchName();
145
+ if (branch) {
146
+ const pull = await git.pullUpdate({
147
+ branch,
148
+ onProgress: options?.onProgress,
149
+ folderName: options?.folderName,
150
+ });
151
+ if (pull.success) {
152
+ options?.onPullSuccess?.();
153
+ if (options?.restartAfterInstall) {
154
+ setTimeout(() => {
155
+ resetApp();
156
+ }, 300);
157
+ }
158
+ } else {
159
+ options?.onPullFailed?.(pull.msg);
160
+ }
161
+ } else {
162
+ const clone = await git.cloneRepo({
163
+ onProgress: options?.onProgress,
164
+ folderName: options?.folderName,
165
+ url: options.url,
166
+ branch: options?.branch,
167
+ bundlePath: options.bundlePath,
168
+ });
169
+ if (clone.success && clone.bundle) {
170
+ await setupExactBundlePath(clone.bundle);
171
+ options?.onCloneSuccess?.();
172
+ if (options?.restartAfterInstall) {
173
+ setTimeout(() => {
174
+ resetApp();
175
+ }, 300);
176
+ }
177
+ } else {
178
+ options?.onCloneFailed?.(clone.msg);
179
+ }
180
+ }
181
+ } catch (e: any) {
182
+ options?.onCloneFailed?.(e.toString());
183
+ } finally {
184
+ options?.onFinishProgress?.();
185
+ }
186
+ };
143
187
  export default {
144
188
  setupBundlePath,
189
+ setupExactBundlePath,
145
190
  removeUpdate: removeBundle,
146
191
  downloadBundleUri,
147
192
  resetApp,
148
193
  getCurrentVersion: getVersionAsNumber,
149
194
  setCurrentVersion,
195
+ git: {
196
+ checkForGitUpdate,
197
+ ...git,
198
+ removeGitUpdate: (folder?: string) => {
199
+ RNhotupdate.setExactBundlePath('');
200
+ git.removeGitUpdate(folder);
201
+ },
202
+ },
150
203
  };