@o/native-hot-update 0.16.19
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/README.md +212 -0
- package/dist/cjs/createHotUpdater.cjs +224 -0
- package/dist/cjs/createHotUpdater.native.js +250 -0
- package/dist/cjs/createHotUpdater.native.js.map +1 -0
- package/dist/cjs/helpers.cjs +40 -0
- package/dist/cjs/helpers.native.js +43 -0
- package/dist/cjs/helpers.native.js.map +1 -0
- package/dist/cjs/index.cjs +31 -0
- package/dist/cjs/index.native.js +34 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/mmkv.cjs +34 -0
- package/dist/cjs/mmkv.native.js +43 -0
- package/dist/cjs/mmkv.native.js.map +1 -0
- package/dist/cjs/types.cjs +18 -0
- package/dist/cjs/types.native.js +21 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/esm/createHotUpdater.mjs +188 -0
- package/dist/esm/createHotUpdater.mjs.map +1 -0
- package/dist/esm/createHotUpdater.native.js +211 -0
- package/dist/esm/createHotUpdater.native.js.map +1 -0
- package/dist/esm/helpers.mjs +14 -0
- package/dist/esm/helpers.mjs.map +1 -0
- package/dist/esm/helpers.native.js +14 -0
- package/dist/esm/helpers.native.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/index.mjs +4 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +4 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/mmkv.mjs +9 -0
- package/dist/esm/mmkv.mjs.map +1 -0
- package/dist/esm/mmkv.native.js +15 -0
- package/dist/esm/mmkv.native.js.map +1 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/package.json +67 -0
- package/src/createHotUpdater.ts +250 -0
- package/src/helpers.ts +23 -0
- package/src/index.ts +6 -0
- package/src/mmkv.ts +20 -0
- package/src/types.ts +66 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { HotUpdater, useHotUpdaterStore } from "@hot-updater/react-native";
|
|
2
|
+
import * as Application from "expo-application";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Alert } from "react-native";
|
|
5
|
+
var INITIAL_OTA_ID = "00000000-0000-0000-0000-000000000000";
|
|
6
|
+
var BUNDLE_ID_KEY_PREFIX = "hotUpdater.bundleId";
|
|
7
|
+
var PRE_RELEASE_BUNDLE_ID_KEY = "hotUpdater.preReleaseBundleId";
|
|
8
|
+
function createHotUpdater(config) {
|
|
9
|
+
var {
|
|
10
|
+
storage,
|
|
11
|
+
updateStrategy = "appVersion"
|
|
12
|
+
} = config;
|
|
13
|
+
var isUpdatePending = false;
|
|
14
|
+
var getAppliedOta = function getAppliedOta2() {
|
|
15
|
+
var id = HotUpdater.getBundleId();
|
|
16
|
+
if (id === INITIAL_OTA_ID) return null;
|
|
17
|
+
if (id === HotUpdater.getMinBundleId()) return null;
|
|
18
|
+
return id;
|
|
19
|
+
};
|
|
20
|
+
var getShortOtaId = function getShortOtaId2() {
|
|
21
|
+
var fullId = getAppliedOta();
|
|
22
|
+
return fullId ? fullId.slice(-12) : null;
|
|
23
|
+
};
|
|
24
|
+
var getIsUpdatePending = function getIsUpdatePending2() {
|
|
25
|
+
return isUpdatePending;
|
|
26
|
+
};
|
|
27
|
+
var getPreReleaseBundleId = function getPreReleaseBundleId2() {
|
|
28
|
+
return storage.get(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
29
|
+
};
|
|
30
|
+
var handleUpdateDownloaded = function handleUpdateDownloaded2(id) {
|
|
31
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
32
|
+
var appVersion = Application.nativeApplicationVersion;
|
|
33
|
+
if (appVersion) {
|
|
34
|
+
storage.set(`${BUNDLE_ID_KEY_PREFIX}.${appVersion}`, id);
|
|
35
|
+
}
|
|
36
|
+
if (options.isPreRelease) {
|
|
37
|
+
storage.set(PRE_RELEASE_BUNDLE_ID_KEY, id);
|
|
38
|
+
} else {
|
|
39
|
+
storage.delete(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
40
|
+
}
|
|
41
|
+
isUpdatePending = true;
|
|
42
|
+
};
|
|
43
|
+
var useOtaUpdater = function useOtaUpdater2() {
|
|
44
|
+
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
45
|
+
var {
|
|
46
|
+
enabled = true,
|
|
47
|
+
onUpdateDownloaded: onUpdateDownloadedCallback,
|
|
48
|
+
onError
|
|
49
|
+
} = options;
|
|
50
|
+
var progress = useHotUpdaterStore(function (state) {
|
|
51
|
+
return state.progress;
|
|
52
|
+
});
|
|
53
|
+
var isUpdating = progress > 0.01;
|
|
54
|
+
var [isUserClearedForAccess, setIsUserClearedForAccess] = useState(false);
|
|
55
|
+
var isUserClearedForAccessRef = useRef(isUserClearedForAccess);
|
|
56
|
+
isUserClearedForAccessRef.current = isUserClearedForAccess;
|
|
57
|
+
useEffect(function () {
|
|
58
|
+
if (!isUpdating) {
|
|
59
|
+
var timer = setTimeout(function () {
|
|
60
|
+
if (!isUserClearedForAccessRef.current) {
|
|
61
|
+
setIsUserClearedForAccess(true);
|
|
62
|
+
}
|
|
63
|
+
}, 5e3);
|
|
64
|
+
return function () {
|
|
65
|
+
return clearTimeout(timer);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}, [isUpdating]);
|
|
70
|
+
useEffect(function () {
|
|
71
|
+
var timer = setTimeout(function () {
|
|
72
|
+
if (!isUserClearedForAccessRef.current) {
|
|
73
|
+
setIsUserClearedForAccess(true);
|
|
74
|
+
}
|
|
75
|
+
}, 2e4);
|
|
76
|
+
return function () {
|
|
77
|
+
return clearTimeout(timer);
|
|
78
|
+
};
|
|
79
|
+
}, []);
|
|
80
|
+
useEffect(function () {
|
|
81
|
+
var shouldContinue = true;
|
|
82
|
+
(async function () {
|
|
83
|
+
try {
|
|
84
|
+
if (!enabled) {
|
|
85
|
+
setIsUserClearedForAccess(true);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!shouldContinue) return;
|
|
89
|
+
var updateInfo = await HotUpdater.checkForUpdate({
|
|
90
|
+
updateStrategy
|
|
91
|
+
});
|
|
92
|
+
if (!updateInfo) {
|
|
93
|
+
setIsUserClearedForAccess(true);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!shouldContinue) return;
|
|
97
|
+
if (updateInfo.status === "ROLLBACK") {
|
|
98
|
+
var preReleaseBundleId = getPreReleaseBundleId();
|
|
99
|
+
var currentBundleId = HotUpdater.getBundleId();
|
|
100
|
+
if (preReleaseBundleId && preReleaseBundleId === currentBundleId && currentBundleId > updateInfo.id) {
|
|
101
|
+
Alert.alert("Update Skipped", "Skipped rollback because you are using a newer pre-release bundle.");
|
|
102
|
+
setIsUserClearedForAccess(true);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!updateInfo.shouldForceUpdate) {
|
|
107
|
+
setIsUserClearedForAccess(true);
|
|
108
|
+
}
|
|
109
|
+
if (!shouldContinue) return;
|
|
110
|
+
await updateInfo.updateBundle();
|
|
111
|
+
handleUpdateDownloaded(updateInfo.id);
|
|
112
|
+
var info = {
|
|
113
|
+
id: updateInfo.id,
|
|
114
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
115
|
+
fileUrl: updateInfo.fileUrl,
|
|
116
|
+
message: updateInfo.message
|
|
117
|
+
};
|
|
118
|
+
onUpdateDownloadedCallback === null || onUpdateDownloadedCallback === void 0 ? void 0 : onUpdateDownloadedCallback(info);
|
|
119
|
+
if (!shouldContinue) return;
|
|
120
|
+
if (updateInfo.shouldForceUpdate) {
|
|
121
|
+
if (!isUserClearedForAccessRef.current) {
|
|
122
|
+
HotUpdater.reload();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
Alert.alert("Update Downloaded", "An important update has been downloaded. Reload now to apply it?", [{
|
|
126
|
+
text: "Later",
|
|
127
|
+
style: "cancel"
|
|
128
|
+
}, {
|
|
129
|
+
text: "Reload Now",
|
|
130
|
+
onPress: function onPress() {
|
|
131
|
+
return HotUpdater.reload();
|
|
132
|
+
}
|
|
133
|
+
}]);
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
137
|
+
setIsUserClearedForAccess(true);
|
|
138
|
+
} finally {
|
|
139
|
+
if (!isUserClearedForAccessRef.current) {
|
|
140
|
+
setIsUserClearedForAccess(true);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
})();
|
|
144
|
+
return function () {
|
|
145
|
+
shouldContinue = false;
|
|
146
|
+
};
|
|
147
|
+
}, [enabled, onUpdateDownloadedCallback, onError]);
|
|
148
|
+
return {
|
|
149
|
+
userClearedForAccess: isUserClearedForAccess,
|
|
150
|
+
progress,
|
|
151
|
+
isUpdatePending: getIsUpdatePending()
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
var checkForUpdate = async function checkForUpdate2() {
|
|
155
|
+
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
156
|
+
var {
|
|
157
|
+
channel,
|
|
158
|
+
isPreRelease = false
|
|
159
|
+
} = options;
|
|
160
|
+
var requestHeaders = {};
|
|
161
|
+
if (channel) {
|
|
162
|
+
requestHeaders["X-Channel"] = channel;
|
|
163
|
+
}
|
|
164
|
+
var updateInfo = await HotUpdater.checkForUpdate({
|
|
165
|
+
updateStrategy,
|
|
166
|
+
...(Object.keys(requestHeaders).length > 0 && {
|
|
167
|
+
requestHeaders
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
if (updateInfo) {
|
|
171
|
+
await updateInfo.updateBundle();
|
|
172
|
+
handleUpdateDownloaded(updateInfo.id, {
|
|
173
|
+
isPreRelease
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
id: updateInfo.id,
|
|
177
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
178
|
+
fileUrl: updateInfo.fileUrl,
|
|
179
|
+
message: updateInfo.message
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
useOtaUpdater,
|
|
186
|
+
checkForUpdate,
|
|
187
|
+
getAppliedOta,
|
|
188
|
+
getShortOtaId,
|
|
189
|
+
getIsUpdatePending,
|
|
190
|
+
reload: function reload() {
|
|
191
|
+
return HotUpdater.reload();
|
|
192
|
+
},
|
|
193
|
+
getBundleId: function getBundleId() {
|
|
194
|
+
return HotUpdater.getBundleId();
|
|
195
|
+
},
|
|
196
|
+
getMinBundleId: function getMinBundleId() {
|
|
197
|
+
return HotUpdater.getMinBundleId();
|
|
198
|
+
},
|
|
199
|
+
getChannel: function getChannel() {
|
|
200
|
+
return HotUpdater.getChannel();
|
|
201
|
+
},
|
|
202
|
+
clearCrashHistory: function clearCrashHistory() {
|
|
203
|
+
return HotUpdater.clearCrashHistory();
|
|
204
|
+
},
|
|
205
|
+
getCrashHistory: function getCrashHistory() {
|
|
206
|
+
return HotUpdater.getCrashHistory();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export { createHotUpdater };
|
|
211
|
+
//# sourceMappingURL=createHotUpdater.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","useHotUpdaterStore","Application","useEffect","useRef","useState","Alert","INITIAL_OTA_ID","BUNDLE_ID_KEY_PREFIX","PRE_RELEASE_BUNDLE_ID_KEY","createHotUpdater","config","storage","updateStrategy","isUpdatePending","getAppliedOta","getAppliedOta2","id","getBundleId","getMinBundleId","getShortOtaId","getShortOtaId2","fullId","slice","getIsUpdatePending","getIsUpdatePending2","getPreReleaseBundleId","getPreReleaseBundleId2","get","handleUpdateDownloaded","handleUpdateDownloaded2","options","arguments","length","appVersion","nativeApplicationVersion","set","isPreRelease","delete","useOtaUpdater","useOtaUpdater2","enabled","onUpdateDownloaded","onUpdateDownloadedCallback","onError","progress","state","isUpdating","isUserClearedForAccess","setIsUserClearedForAccess","isUserClearedForAccessRef","current","timer","setTimeout","clearTimeout","shouldContinue","updateInfo","checkForUpdate","status","preReleaseBundleId","currentBundleId","alert","shouldForceUpdate","updateBundle","info","isCriticalUpdate","fileUrl","message","reload","text","style","onPress","error","userClearedForAccess","checkForUpdate2","channel","requestHeaders","Object","keys"],"sources":["../../src/createHotUpdater.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HotUpdater } from "@hot-updater/react-native";
|
|
2
|
+
const INITIAL_OTA_ID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
function getAppliedOta() {
|
|
4
|
+
const id = HotUpdater.getBundleId();
|
|
5
|
+
if (id === INITIAL_OTA_ID) return null;
|
|
6
|
+
if (id === HotUpdater.getMinBundleId()) return null;
|
|
7
|
+
return id;
|
|
8
|
+
}
|
|
9
|
+
function getShortOtaId() {
|
|
10
|
+
const fullId = getAppliedOta();
|
|
11
|
+
return fullId ? fullId.slice(-12) : null;
|
|
12
|
+
}
|
|
13
|
+
export { getAppliedOta, getShortOtaId };
|
|
14
|
+
//# sourceMappingURL=helpers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","INITIAL_OTA_ID","getAppliedOta","id","getBundleId","getMinBundleId","getShortOtaId","fullId","slice"],"sources":["../../src/helpers.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,UAAA,QAAkB;AAE3B,MAAMC,cAAA,GAAiB;AAMhB,SAASC,cAAA,EAA+B;EAC7C,MAAMC,EAAA,GAAKH,UAAA,CAAWI,WAAA,CAAY;EAClC,IAAID,EAAA,KAAOF,cAAA,EAAgB,OAAO;EAClC,IAAIE,EAAA,KAAOH,UAAA,CAAWK,cAAA,CAAe,GAAG,OAAO;EAC/C,OAAOF,EAAA;AACT;AAMO,SAASG,cAAA,EAA+B;EAC7C,MAAMC,MAAA,GAASL,aAAA,CAAc;EAC7B,OAAOK,MAAA,GAASA,MAAA,CAAOC,KAAA,CAAM,GAAG,IAAI;AACtC","ignoreList":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HotUpdater } from "@hot-updater/react-native";
|
|
2
|
+
var INITIAL_OTA_ID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
function getAppliedOta() {
|
|
4
|
+
var id = HotUpdater.getBundleId();
|
|
5
|
+
if (id === INITIAL_OTA_ID) return null;
|
|
6
|
+
if (id === HotUpdater.getMinBundleId()) return null;
|
|
7
|
+
return id;
|
|
8
|
+
}
|
|
9
|
+
function getShortOtaId() {
|
|
10
|
+
var fullId = getAppliedOta();
|
|
11
|
+
return fullId ? fullId.slice(-12) : null;
|
|
12
|
+
}
|
|
13
|
+
export { getAppliedOta, getShortOtaId };
|
|
14
|
+
//# sourceMappingURL=helpers.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","INITIAL_OTA_ID","getAppliedOta","id","getBundleId","getMinBundleId","getShortOtaId","fullId","slice"],"sources":["../../src/helpers.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,UAAA,QAAkB;AAE3B,IAAAC,cAAM,yCAAiB;AAMhB,SAASC,cAAA,EAA+B;EAC7C,IAAAC,EAAM,GAAAH,UAAK,CAAAI,WAAW;EACtB,IAAID,EAAA,KAAOF,cAAA,EAAgB,OAAO;EAClC,IAAIE,EAAA,KAAOH,UAAA,CAAWK,cAAA,CAAe,GAAG,OAAO;EAC/C,OAAOF,EAAA;AACT;AAMO,SAASG,cAAA,EAA+B;EAC7C,IAAAC,MAAM,GAAAL,aAAS;EACf,OAAOK,MAAA,GAASA,MAAA,CAAOC,KAAA,CAAM,GAAG,IAAI;AACtC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","useHotUpdaterStore","createHotUpdater"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AACA,SAASA,UAAA,EAAYC,kBAAA,QAA0B;AAG/C,SAASC,gBAAA,QAAwB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","useHotUpdaterStore","createHotUpdater"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AACA,SAASA,UAAA,EAAYC,kBAAA,QAA0B;AAG/C,SAASC,gBAAA,QAAwB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["HotUpdater","useHotUpdaterStore","createHotUpdater"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AACA,SAASA,UAAA,EAAYC,kBAAA,QAA0B;AAG/C,SAASC,gBAAA,QAAwB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createMMKVStorage","mmkv","get","key","getString","set","value","delete"],"sources":["../../src/mmkv.ts"],"sourcesContent":[null],"mappings":"AAaO,SAASA,kBAAkBC,IAAA,EAA8B;EAC9D,OAAO;IACLC,GAAA,EAAMC,GAAA,IAAgBF,IAAA,CAAKG,SAAA,CAAUD,GAAG;IACxCE,GAAA,EAAKA,CAACF,GAAA,EAAaG,KAAA,KAAkBL,IAAA,CAAKI,GAAA,CAAIF,GAAA,EAAKG,KAAK;IACxDC,MAAA,EAASJ,GAAA,IAAgBF,IAAA,CAAKM,MAAA,CAAOJ,GAAG;EAC1C;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function createMMKVStorage(mmkv) {
|
|
2
|
+
return {
|
|
3
|
+
get: function get(key) {
|
|
4
|
+
return mmkv.getString(key);
|
|
5
|
+
},
|
|
6
|
+
set: function set(key, value) {
|
|
7
|
+
return mmkv.set(key, value);
|
|
8
|
+
},
|
|
9
|
+
delete: function _delete(key) {
|
|
10
|
+
return mmkv.delete(key);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export { createMMKVStorage };
|
|
15
|
+
//# sourceMappingURL=mmkv.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createMMKVStorage","mmkv","get","key","getString","set","value"],"sources":["../../src/mmkv.ts"],"sourcesContent":[null],"mappings":"AAaO,SAASA,kBAAkBC,IAAA,EAA8B;EAC9D,OAAO;IACLC,GAAA,EAAK,SAAiBA,IAAAC,GAAK;MAC3B,OAAMF,IAAa,CAAAG,SAAkB,CAAAD,GAAK;IAC1C;IACFE,GAAA,WAAAA,IAAAF,GAAA,EAAAG,KAAA;MACF,OAAAL,IAAA,CAAAI,GAAA,CAAAF,GAAA,EAAAG,KAAA","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":[],"sourcesContent":[],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":[],"sourcesContent":[],"mappings":"","ignoreList":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@o/native-hot-update",
|
|
3
|
+
"version": "0.16.19",
|
|
4
|
+
"main": "dist/cjs",
|
|
5
|
+
"module": "dist/esm",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"@tamagui/build": "2.3.1",
|
|
8
|
+
"@types/react": "^18.3.18",
|
|
9
|
+
"react": "*",
|
|
10
|
+
"react-native": "*",
|
|
11
|
+
"react-native-mmkv": "^3.1.0",
|
|
12
|
+
"typescript": "^5.7.3",
|
|
13
|
+
"@hot-updater/react-native": "^0.25.8"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"expo-application": "*",
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"@hot-updater/react-native": "^0.25.8",
|
|
20
|
+
"react-native-mmkv": "^3.1.0"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
"./package.json": "./package.json",
|
|
24
|
+
".": {
|
|
25
|
+
"react-native": "./dist/esm/index.native.js",
|
|
26
|
+
"types": "./src/index.ts",
|
|
27
|
+
"import": "./dist/esm/index.mjs",
|
|
28
|
+
"require": "./dist/cjs/index.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./mmkv": {
|
|
31
|
+
"types": "./src/mmkv.ts",
|
|
32
|
+
"import": "./dist/esm/mmkv.mjs",
|
|
33
|
+
"require": "./dist/cjs/mmkv.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src"
|
|
39
|
+
],
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"react-native-mmkv": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tamagui-build --skip-types",
|
|
50
|
+
"watch": "tamagui-build --skip-types --watch",
|
|
51
|
+
"clean": "tamagui-build clean",
|
|
52
|
+
"typecheck": "tko run typecheck",
|
|
53
|
+
"lint": "oxlint src",
|
|
54
|
+
"check": "tsc --noEmit"
|
|
55
|
+
},
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"source": "src/index.ts",
|
|
58
|
+
"type": "module",
|
|
59
|
+
"types": "./src/index.ts",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/natew/orez.git",
|
|
63
|
+
"directory": "packages/native-hot-update"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": null,
|
|
66
|
+
"optionalDependencies": null
|
|
67
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { HotUpdater, useHotUpdaterStore } from '@hot-updater/react-native'
|
|
2
|
+
import * as Application from 'expo-application'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { Alert } from 'react-native'
|
|
5
|
+
|
|
6
|
+
import type { HotUpdaterConfig, HotUpdaterInstance, UpdateInfo } from './types'
|
|
7
|
+
|
|
8
|
+
const INITIAL_OTA_ID = '00000000-0000-0000-0000-000000000000'
|
|
9
|
+
const BUNDLE_ID_KEY_PREFIX = 'hotUpdater.bundleId'
|
|
10
|
+
const PRE_RELEASE_BUNDLE_ID_KEY = 'hotUpdater.preReleaseBundleId'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a HotUpdater instance with custom hooks and helpers.
|
|
14
|
+
* NOTE: Requires HotUpdater.wrap() to be called first (e.g., in HotUpdaterSplash).
|
|
15
|
+
*/
|
|
16
|
+
export function createHotUpdater(config: HotUpdaterConfig): HotUpdaterInstance {
|
|
17
|
+
const { storage, updateStrategy = 'appVersion' } = config
|
|
18
|
+
|
|
19
|
+
let isUpdatePending = false
|
|
20
|
+
|
|
21
|
+
const getAppliedOta = (): string | null => {
|
|
22
|
+
const id = HotUpdater.getBundleId()
|
|
23
|
+
if (id === INITIAL_OTA_ID) return null
|
|
24
|
+
if (id === HotUpdater.getMinBundleId()) return null
|
|
25
|
+
return id
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const getShortOtaId = (): string | null => {
|
|
29
|
+
const fullId = getAppliedOta()
|
|
30
|
+
return fullId ? fullId.slice(-12) : null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getIsUpdatePending = (): boolean => {
|
|
34
|
+
return isUpdatePending
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getPreReleaseBundleId = (): string | undefined => {
|
|
38
|
+
return storage.get(PRE_RELEASE_BUNDLE_ID_KEY)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const handleUpdateDownloaded = (
|
|
42
|
+
id: string,
|
|
43
|
+
options: { isPreRelease?: boolean } = {}
|
|
44
|
+
) => {
|
|
45
|
+
const appVersion = Application.nativeApplicationVersion
|
|
46
|
+
if (appVersion) {
|
|
47
|
+
storage.set(`${BUNDLE_ID_KEY_PREFIX}.${appVersion}`, id)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.isPreRelease) {
|
|
51
|
+
storage.set(PRE_RELEASE_BUNDLE_ID_KEY, id)
|
|
52
|
+
} else {
|
|
53
|
+
storage.delete(PRE_RELEASE_BUNDLE_ID_KEY)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
isUpdatePending = true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Hook that checks for OTA updates with timeout protection.
|
|
61
|
+
* Returns userClearedForAccess which controls when to show the app.
|
|
62
|
+
*/
|
|
63
|
+
const useOtaUpdater = (
|
|
64
|
+
options: {
|
|
65
|
+
enabled?: boolean
|
|
66
|
+
onUpdateDownloaded?: (info: UpdateInfo) => void
|
|
67
|
+
onError?: (error: unknown) => void
|
|
68
|
+
} = {}
|
|
69
|
+
) => {
|
|
70
|
+
const {
|
|
71
|
+
enabled = true,
|
|
72
|
+
onUpdateDownloaded: onUpdateDownloadedCallback,
|
|
73
|
+
onError,
|
|
74
|
+
} = options
|
|
75
|
+
|
|
76
|
+
const progress = useHotUpdaterStore((state) => state.progress)
|
|
77
|
+
const isUpdating = progress > 0.01
|
|
78
|
+
|
|
79
|
+
const [isUserClearedForAccess, setIsUserClearedForAccess] = useState(false)
|
|
80
|
+
const isUserClearedForAccessRef = useRef(isUserClearedForAccess)
|
|
81
|
+
isUserClearedForAccessRef.current = isUserClearedForAccess
|
|
82
|
+
|
|
83
|
+
// 5s timeout if no update started
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!isUpdating) {
|
|
86
|
+
const timer = setTimeout(() => {
|
|
87
|
+
if (!isUserClearedForAccessRef.current) {
|
|
88
|
+
setIsUserClearedForAccess(true)
|
|
89
|
+
}
|
|
90
|
+
}, 5000)
|
|
91
|
+
return () => clearTimeout(timer)
|
|
92
|
+
}
|
|
93
|
+
return undefined
|
|
94
|
+
}, [isUpdating])
|
|
95
|
+
|
|
96
|
+
// 20s hard timeout
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const timer = setTimeout(() => {
|
|
99
|
+
if (!isUserClearedForAccessRef.current) {
|
|
100
|
+
setIsUserClearedForAccess(true)
|
|
101
|
+
}
|
|
102
|
+
}, 20000)
|
|
103
|
+
return () => clearTimeout(timer)
|
|
104
|
+
}, [])
|
|
105
|
+
|
|
106
|
+
// Update check
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
let shouldContinue = true
|
|
109
|
+
|
|
110
|
+
;(async () => {
|
|
111
|
+
try {
|
|
112
|
+
if (!enabled) {
|
|
113
|
+
setIsUserClearedForAccess(true)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!shouldContinue) return
|
|
118
|
+
|
|
119
|
+
const updateInfo = await HotUpdater.checkForUpdate({
|
|
120
|
+
updateStrategy,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
if (!updateInfo) {
|
|
124
|
+
setIsUserClearedForAccess(true)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!shouldContinue) return
|
|
129
|
+
|
|
130
|
+
// Handle rollback for pre-release bundles
|
|
131
|
+
if (updateInfo.status === 'ROLLBACK') {
|
|
132
|
+
const preReleaseBundleId = getPreReleaseBundleId()
|
|
133
|
+
const currentBundleId = HotUpdater.getBundleId()
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
preReleaseBundleId &&
|
|
137
|
+
preReleaseBundleId === currentBundleId &&
|
|
138
|
+
currentBundleId > updateInfo.id
|
|
139
|
+
) {
|
|
140
|
+
Alert.alert(
|
|
141
|
+
'Update Skipped',
|
|
142
|
+
'Skipped rollback because you are using a newer pre-release bundle.'
|
|
143
|
+
)
|
|
144
|
+
setIsUserClearedForAccess(true)
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!updateInfo.shouldForceUpdate) {
|
|
150
|
+
setIsUserClearedForAccess(true)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!shouldContinue) return
|
|
154
|
+
|
|
155
|
+
await updateInfo.updateBundle()
|
|
156
|
+
handleUpdateDownloaded(updateInfo.id)
|
|
157
|
+
|
|
158
|
+
const info: UpdateInfo = {
|
|
159
|
+
id: updateInfo.id,
|
|
160
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
161
|
+
fileUrl: updateInfo.fileUrl,
|
|
162
|
+
message: updateInfo.message,
|
|
163
|
+
}
|
|
164
|
+
onUpdateDownloadedCallback?.(info)
|
|
165
|
+
|
|
166
|
+
if (!shouldContinue) return
|
|
167
|
+
|
|
168
|
+
if (updateInfo.shouldForceUpdate) {
|
|
169
|
+
if (!isUserClearedForAccessRef.current) {
|
|
170
|
+
HotUpdater.reload()
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
Alert.alert(
|
|
174
|
+
'Update Downloaded',
|
|
175
|
+
'An important update has been downloaded. Reload now to apply it?',
|
|
176
|
+
[
|
|
177
|
+
{ text: 'Later', style: 'cancel' },
|
|
178
|
+
{ text: 'Reload Now', onPress: () => HotUpdater.reload() },
|
|
179
|
+
]
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
onError?.(error)
|
|
184
|
+
setIsUserClearedForAccess(true)
|
|
185
|
+
} finally {
|
|
186
|
+
if (!isUserClearedForAccessRef.current) {
|
|
187
|
+
setIsUserClearedForAccess(true)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
})()
|
|
191
|
+
|
|
192
|
+
return () => {
|
|
193
|
+
shouldContinue = false
|
|
194
|
+
}
|
|
195
|
+
}, [enabled, onUpdateDownloadedCallback, onError])
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
userClearedForAccess: isUserClearedForAccess,
|
|
199
|
+
progress,
|
|
200
|
+
isUpdatePending: getIsUpdatePending(),
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Manually check for updates (for debug/testing).
|
|
206
|
+
*/
|
|
207
|
+
const checkForUpdate = async (
|
|
208
|
+
options: { channel?: string; isPreRelease?: boolean } = {}
|
|
209
|
+
) => {
|
|
210
|
+
const { channel, isPreRelease = false } = options
|
|
211
|
+
|
|
212
|
+
const requestHeaders: Record<string, string> = {}
|
|
213
|
+
if (channel) {
|
|
214
|
+
requestHeaders['X-Channel'] = channel
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const updateInfo = await HotUpdater.checkForUpdate({
|
|
218
|
+
updateStrategy,
|
|
219
|
+
...(Object.keys(requestHeaders).length > 0 && { requestHeaders }),
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
if (updateInfo) {
|
|
223
|
+
await updateInfo.updateBundle()
|
|
224
|
+
handleUpdateDownloaded(updateInfo.id, { isPreRelease })
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
id: updateInfo.id,
|
|
228
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
229
|
+
fileUrl: updateInfo.fileUrl,
|
|
230
|
+
message: updateInfo.message,
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return null
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
useOtaUpdater,
|
|
239
|
+
checkForUpdate,
|
|
240
|
+
getAppliedOta,
|
|
241
|
+
getShortOtaId,
|
|
242
|
+
getIsUpdatePending,
|
|
243
|
+
reload: () => HotUpdater.reload(),
|
|
244
|
+
getBundleId: () => HotUpdater.getBundleId(),
|
|
245
|
+
getMinBundleId: () => HotUpdater.getMinBundleId(),
|
|
246
|
+
getChannel: () => HotUpdater.getChannel(),
|
|
247
|
+
clearCrashHistory: () => HotUpdater.clearCrashHistory(),
|
|
248
|
+
getCrashHistory: () => HotUpdater.getCrashHistory(),
|
|
249
|
+
}
|
|
250
|
+
}
|