@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.
Files changed (44) hide show
  1. package/README.md +212 -0
  2. package/dist/cjs/createHotUpdater.cjs +224 -0
  3. package/dist/cjs/createHotUpdater.native.js +250 -0
  4. package/dist/cjs/createHotUpdater.native.js.map +1 -0
  5. package/dist/cjs/helpers.cjs +40 -0
  6. package/dist/cjs/helpers.native.js +43 -0
  7. package/dist/cjs/helpers.native.js.map +1 -0
  8. package/dist/cjs/index.cjs +31 -0
  9. package/dist/cjs/index.native.js +34 -0
  10. package/dist/cjs/index.native.js.map +1 -0
  11. package/dist/cjs/mmkv.cjs +34 -0
  12. package/dist/cjs/mmkv.native.js +43 -0
  13. package/dist/cjs/mmkv.native.js.map +1 -0
  14. package/dist/cjs/types.cjs +18 -0
  15. package/dist/cjs/types.native.js +21 -0
  16. package/dist/cjs/types.native.js.map +1 -0
  17. package/dist/esm/createHotUpdater.mjs +188 -0
  18. package/dist/esm/createHotUpdater.mjs.map +1 -0
  19. package/dist/esm/createHotUpdater.native.js +211 -0
  20. package/dist/esm/createHotUpdater.native.js.map +1 -0
  21. package/dist/esm/helpers.mjs +14 -0
  22. package/dist/esm/helpers.mjs.map +1 -0
  23. package/dist/esm/helpers.native.js +14 -0
  24. package/dist/esm/helpers.native.js.map +1 -0
  25. package/dist/esm/index.js +4 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/index.mjs +4 -0
  28. package/dist/esm/index.mjs.map +1 -0
  29. package/dist/esm/index.native.js +4 -0
  30. package/dist/esm/index.native.js.map +1 -0
  31. package/dist/esm/mmkv.mjs +9 -0
  32. package/dist/esm/mmkv.mjs.map +1 -0
  33. package/dist/esm/mmkv.native.js +15 -0
  34. package/dist/esm/mmkv.native.js.map +1 -0
  35. package/dist/esm/types.mjs +2 -0
  36. package/dist/esm/types.mjs.map +1 -0
  37. package/dist/esm/types.native.js +2 -0
  38. package/dist/esm/types.native.js.map +1 -0
  39. package/package.json +67 -0
  40. package/src/createHotUpdater.ts +250 -0
  41. package/src/helpers.ts +23 -0
  42. package/src/index.ts +6 -0
  43. package/src/mmkv.ts +20 -0
  44. 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,4 @@
1
+ import { HotUpdater, useHotUpdaterStore } from "@hot-updater/react-native";
2
+ import { createHotUpdater } from "./createHotUpdater.mjs";
3
+ export { HotUpdater, createHotUpdater, useHotUpdaterStore };
4
+ //# sourceMappingURL=index.js.map
@@ -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,4 @@
1
+ import { HotUpdater, useHotUpdaterStore } from "@hot-updater/react-native";
2
+ import { createHotUpdater } from "./createHotUpdater.mjs";
3
+ export { HotUpdater, createHotUpdater, useHotUpdaterStore };
4
+ //# sourceMappingURL=index.mjs.map
@@ -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,4 @@
1
+ import { HotUpdater, useHotUpdaterStore } from "@hot-updater/react-native";
2
+ import { createHotUpdater } from "./createHotUpdater.native.js";
3
+ export { HotUpdater, createHotUpdater, useHotUpdaterStore };
4
+ //# sourceMappingURL=index.native.js.map
@@ -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,9 @@
1
+ function createMMKVStorage(mmkv) {
2
+ return {
3
+ get: key => mmkv.getString(key),
4
+ set: (key, value) => mmkv.set(key, value),
5
+ delete: key => mmkv.delete(key)
6
+ };
7
+ }
8
+ export { createMMKVStorage };
9
+ //# sourceMappingURL=mmkv.mjs.map
@@ -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,2 @@
1
+
2
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":[],"sourcesContent":[],"mappings":"","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.native.js.map
@@ -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
+ }