@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
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @o/native-hot-update
|
|
2
|
+
|
|
3
|
+
Minimal, headless React Native hot update library.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Simple instance-based API
|
|
8
|
+
- Pluggable storage (MMKV, AsyncStorage, custom)
|
|
9
|
+
- Timeout protection (5s + 20s hard limit)
|
|
10
|
+
- Pre-release testing workflow
|
|
11
|
+
- Works with Expo & React Native
|
|
12
|
+
- Zero UI - bring your own components
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
The Takeout template ships with `HotUpdaterSplash` already wrapping your native
|
|
17
|
+
app. The app runs fine without a backend - updates are gated by PostHog feature
|
|
18
|
+
flags (off by default).
|
|
19
|
+
|
|
20
|
+
When ready to deploy OTA updates, set up a backend:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx -y supabase login
|
|
24
|
+
npx hot-updater init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
During `hot-updater init`, select:
|
|
28
|
+
|
|
29
|
+
- **Build plugin**: `Bare`
|
|
30
|
+
- **Storage/Database**: `Supabase`
|
|
31
|
+
|
|
32
|
+
This generates `hot-updater.config.ts` and `.env.hotupdater` with your Supabase
|
|
33
|
+
credentials. Then enable via PostHog feature flags.
|
|
34
|
+
|
|
35
|
+
See https://hot-updater.dev/docs/managed/supabase for full backend setup.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bun add @o/native-hot-update react-native-mmkv
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### 1. Create Hot Updater Instance
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// src/hotUpdater.ts
|
|
49
|
+
import { MMKV } from 'react-native-mmkv'
|
|
50
|
+
import { createHotUpdater } from '@o/native-hot-update'
|
|
51
|
+
import { createMMKVStorage } from '@o/native-hot-update/mmkv'
|
|
52
|
+
|
|
53
|
+
const mmkv = new MMKV({ id: 'hot-updater' })
|
|
54
|
+
|
|
55
|
+
const hotUpdaterInstance = createHotUpdater({
|
|
56
|
+
storage: createMMKVStorage(mmkv),
|
|
57
|
+
updateStrategy: 'appVersion',
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Export the hook and instance
|
|
61
|
+
export const useOtaUpdater = hotUpdaterInstance.useOtaUpdater
|
|
62
|
+
export const hotUpdater = hotUpdaterInstance
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Wrap Your App
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// app/index.tsx or App.tsx
|
|
69
|
+
import { HotUpdater } from '@o/native-hot-update'
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
// Your app component
|
|
73
|
+
return <YourApp />
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// IMPORTANT: wrap() initializes HotUpdater config - required for checkForUpdate
|
|
77
|
+
export default HotUpdater.wrap({
|
|
78
|
+
baseURL: 'https://your-update-server.com',
|
|
79
|
+
updateMode: 'manual', // Use manual mode with custom hooks
|
|
80
|
+
})(App)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Use in Your App
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// app/_layout.tsx
|
|
87
|
+
import { useOtaUpdater } from './hotUpdater'
|
|
88
|
+
|
|
89
|
+
export default function RootLayout() {
|
|
90
|
+
const { userClearedForAccess, progress } = useOtaUpdater({
|
|
91
|
+
enabled: true,
|
|
92
|
+
onUpdateDownloaded: (info) => {
|
|
93
|
+
console.info('Update downloaded:', info.id)
|
|
94
|
+
},
|
|
95
|
+
onError: (error) => {
|
|
96
|
+
console.error('Update failed:', error)
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if (!userClearedForAccess) {
|
|
101
|
+
return <SplashScreen progress={progress} />
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return <YourApp />
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 4. Display Version Info
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { hotUpdater } from './hotUpdater'
|
|
112
|
+
import * as Application from 'expo-application'
|
|
113
|
+
|
|
114
|
+
export function VersionDisplay() {
|
|
115
|
+
const otaId = hotUpdater.getShortOtaId()
|
|
116
|
+
const channel = hotUpdater.getChannel()
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<Text>
|
|
120
|
+
v{Application.nativeApplicationVersion}
|
|
121
|
+
{otaId && ` (${otaId})`} • {channel}
|
|
122
|
+
</Text>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 5. Dev Tools
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { hotUpdater } from './hotUpdater'
|
|
131
|
+
import { Button, View, Text, Alert } from 'react-native'
|
|
132
|
+
|
|
133
|
+
export function DevUpdateTools() {
|
|
134
|
+
const handleCheckUpdate = async () => {
|
|
135
|
+
const update = await hotUpdater.checkForUpdate()
|
|
136
|
+
if (update) {
|
|
137
|
+
Alert.alert('Update available', `ID: ${update.id}`, [
|
|
138
|
+
{ text: 'Later' },
|
|
139
|
+
{ text: 'Reload', onPress: () => hotUpdater.reload() },
|
|
140
|
+
])
|
|
141
|
+
} else {
|
|
142
|
+
Alert.alert('No update available')
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const handleCheckPreRelease = async () => {
|
|
147
|
+
const update = await hotUpdater.checkForUpdate({
|
|
148
|
+
channel: 'production-pre',
|
|
149
|
+
isPreRelease: true,
|
|
150
|
+
})
|
|
151
|
+
if (update) {
|
|
152
|
+
Alert.alert('Pre-release available', `ID: ${update.id}`, [
|
|
153
|
+
{ text: 'Later' },
|
|
154
|
+
{ text: 'Reload', onPress: () => hotUpdater.reload() },
|
|
155
|
+
])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<View>
|
|
161
|
+
<Text>Current: {hotUpdater.getAppliedOta() || 'Native build'}</Text>
|
|
162
|
+
<Text>Channel: {hotUpdater.getChannel()}</Text>
|
|
163
|
+
<Button title="Check for Update" onPress={handleCheckUpdate} />
|
|
164
|
+
<Button title="Check Pre-release" onPress={handleCheckPreRelease} />
|
|
165
|
+
<Button title="Reload App" onPress={() => hotUpdater.reload()} />
|
|
166
|
+
</View>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API
|
|
172
|
+
|
|
173
|
+
### `createHotUpdater(config)`
|
|
174
|
+
|
|
175
|
+
Creates a hot updater instance.
|
|
176
|
+
|
|
177
|
+
**Config:**
|
|
178
|
+
|
|
179
|
+
- `storage` (HotUpdateStorage): Storage adapter for persisting state
|
|
180
|
+
- `updateStrategy` ('appVersion' | 'fingerprint'): Update strategy (default:
|
|
181
|
+
'appVersion')
|
|
182
|
+
|
|
183
|
+
**Returns:** `HotUpdaterInstance`
|
|
184
|
+
|
|
185
|
+
### `useOtaUpdater(options?)`
|
|
186
|
+
|
|
187
|
+
React hook for automatic update checking.
|
|
188
|
+
|
|
189
|
+
**Options:**
|
|
190
|
+
|
|
191
|
+
- `enabled` (boolean): Enable/disable update checking (default: true)
|
|
192
|
+
- `onUpdateDownloaded` (function): Callback when update is downloaded
|
|
193
|
+
- `onError` (function): Callback on error
|
|
194
|
+
|
|
195
|
+
**Returns:**
|
|
196
|
+
|
|
197
|
+
- `userClearedForAccess` (boolean): Whether user can access app
|
|
198
|
+
- `progress` (number): Download progress (0-100)
|
|
199
|
+
- `isUpdatePending` (boolean): Whether update will apply on restart
|
|
200
|
+
|
|
201
|
+
### Instance Methods
|
|
202
|
+
|
|
203
|
+
- `checkForUpdate(options?)` - Manually check for updates
|
|
204
|
+
- `getAppliedOta()` - Get current OTA bundle ID
|
|
205
|
+
- `getShortOtaId()` - Get short OTA ID
|
|
206
|
+
- `getIsUpdatePending()` - Check if update is pending
|
|
207
|
+
- `reload()` - Reload app
|
|
208
|
+
- `getBundleId()` - Get current bundle ID
|
|
209
|
+
- `getMinBundleId()` - Get minimum bundle ID
|
|
210
|
+
- `getChannel()` - Get current channel
|
|
211
|
+
- `clearCrashHistory()` - Clear crashed bundle history (allows retrying failed bundles)
|
|
212
|
+
- `getCrashHistory()` - Get list of crashed bundle IDs
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all) __defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
+
get: () => from[key],
|
|
17
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
28
|
+
value: mod,
|
|
29
|
+
enumerable: true
|
|
30
|
+
}) : target, mod));
|
|
31
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
32
|
+
value: true
|
|
33
|
+
}), mod);
|
|
34
|
+
var createHotUpdater_exports = {};
|
|
35
|
+
__export(createHotUpdater_exports, {
|
|
36
|
+
createHotUpdater: () => createHotUpdater
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(createHotUpdater_exports);
|
|
39
|
+
var import_react_native = require("@hot-updater/react-native");
|
|
40
|
+
var Application = __toESM(require("expo-application"), 1);
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
var import_react_native2 = require("react-native-web");
|
|
43
|
+
const INITIAL_OTA_ID = "00000000-0000-0000-0000-000000000000";
|
|
44
|
+
const BUNDLE_ID_KEY_PREFIX = "hotUpdater.bundleId";
|
|
45
|
+
const PRE_RELEASE_BUNDLE_ID_KEY = "hotUpdater.preReleaseBundleId";
|
|
46
|
+
function createHotUpdater(config) {
|
|
47
|
+
const {
|
|
48
|
+
storage,
|
|
49
|
+
updateStrategy = "appVersion"
|
|
50
|
+
} = config;
|
|
51
|
+
let isUpdatePending = false;
|
|
52
|
+
const getAppliedOta = () => {
|
|
53
|
+
const id = import_react_native.HotUpdater.getBundleId();
|
|
54
|
+
if (id === INITIAL_OTA_ID) return null;
|
|
55
|
+
if (id === import_react_native.HotUpdater.getMinBundleId()) return null;
|
|
56
|
+
return id;
|
|
57
|
+
};
|
|
58
|
+
const getShortOtaId = () => {
|
|
59
|
+
const fullId = getAppliedOta();
|
|
60
|
+
return fullId ? fullId.slice(-12) : null;
|
|
61
|
+
};
|
|
62
|
+
const getIsUpdatePending = () => {
|
|
63
|
+
return isUpdatePending;
|
|
64
|
+
};
|
|
65
|
+
const getPreReleaseBundleId = () => {
|
|
66
|
+
return storage.get(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
67
|
+
};
|
|
68
|
+
const handleUpdateDownloaded = (id, options = {}) => {
|
|
69
|
+
const appVersion = Application.nativeApplicationVersion;
|
|
70
|
+
if (appVersion) {
|
|
71
|
+
storage.set(`${BUNDLE_ID_KEY_PREFIX}.${appVersion}`, id);
|
|
72
|
+
}
|
|
73
|
+
if (options.isPreRelease) {
|
|
74
|
+
storage.set(PRE_RELEASE_BUNDLE_ID_KEY, id);
|
|
75
|
+
} else {
|
|
76
|
+
storage.delete(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
77
|
+
}
|
|
78
|
+
isUpdatePending = true;
|
|
79
|
+
};
|
|
80
|
+
const useOtaUpdater = (options = {}) => {
|
|
81
|
+
const {
|
|
82
|
+
enabled = true,
|
|
83
|
+
onUpdateDownloaded: onUpdateDownloadedCallback,
|
|
84
|
+
onError
|
|
85
|
+
} = options;
|
|
86
|
+
const progress = (0, import_react_native.useHotUpdaterStore)(state => state.progress);
|
|
87
|
+
const isUpdating = progress > 0.01;
|
|
88
|
+
const [isUserClearedForAccess, setIsUserClearedForAccess] = (0, import_react.useState)(false);
|
|
89
|
+
const isUserClearedForAccessRef = (0, import_react.useRef)(isUserClearedForAccess);
|
|
90
|
+
isUserClearedForAccessRef.current = isUserClearedForAccess;
|
|
91
|
+
(0, import_react.useEffect)(() => {
|
|
92
|
+
if (!isUpdating) {
|
|
93
|
+
const timer = setTimeout(() => {
|
|
94
|
+
if (!isUserClearedForAccessRef.current) {
|
|
95
|
+
setIsUserClearedForAccess(true);
|
|
96
|
+
}
|
|
97
|
+
}, 5e3);
|
|
98
|
+
return () => clearTimeout(timer);
|
|
99
|
+
}
|
|
100
|
+
return void 0;
|
|
101
|
+
}, [isUpdating]);
|
|
102
|
+
(0, import_react.useEffect)(() => {
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
if (!isUserClearedForAccessRef.current) {
|
|
105
|
+
setIsUserClearedForAccess(true);
|
|
106
|
+
}
|
|
107
|
+
}, 2e4);
|
|
108
|
+
return () => clearTimeout(timer);
|
|
109
|
+
}, []);
|
|
110
|
+
(0, import_react.useEffect)(() => {
|
|
111
|
+
let shouldContinue = true;
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
if (!enabled) {
|
|
115
|
+
setIsUserClearedForAccess(true);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!shouldContinue) return;
|
|
119
|
+
const updateInfo = await import_react_native.HotUpdater.checkForUpdate({
|
|
120
|
+
updateStrategy
|
|
121
|
+
});
|
|
122
|
+
if (!updateInfo) {
|
|
123
|
+
setIsUserClearedForAccess(true);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!shouldContinue) return;
|
|
127
|
+
if (updateInfo.status === "ROLLBACK") {
|
|
128
|
+
const preReleaseBundleId = getPreReleaseBundleId();
|
|
129
|
+
const currentBundleId = import_react_native.HotUpdater.getBundleId();
|
|
130
|
+
if (preReleaseBundleId && preReleaseBundleId === currentBundleId && currentBundleId > updateInfo.id) {
|
|
131
|
+
import_react_native2.Alert.alert("Update Skipped", "Skipped rollback because you are using a newer pre-release bundle.");
|
|
132
|
+
setIsUserClearedForAccess(true);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!updateInfo.shouldForceUpdate) {
|
|
137
|
+
setIsUserClearedForAccess(true);
|
|
138
|
+
}
|
|
139
|
+
if (!shouldContinue) return;
|
|
140
|
+
await updateInfo.updateBundle();
|
|
141
|
+
handleUpdateDownloaded(updateInfo.id);
|
|
142
|
+
const info = {
|
|
143
|
+
id: updateInfo.id,
|
|
144
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
145
|
+
fileUrl: updateInfo.fileUrl,
|
|
146
|
+
message: updateInfo.message
|
|
147
|
+
};
|
|
148
|
+
onUpdateDownloadedCallback?.(info);
|
|
149
|
+
if (!shouldContinue) return;
|
|
150
|
+
if (updateInfo.shouldForceUpdate) {
|
|
151
|
+
if (!isUserClearedForAccessRef.current) {
|
|
152
|
+
import_react_native.HotUpdater.reload();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
import_react_native2.Alert.alert("Update Downloaded", "An important update has been downloaded. Reload now to apply it?", [{
|
|
156
|
+
text: "Later",
|
|
157
|
+
style: "cancel"
|
|
158
|
+
}, {
|
|
159
|
+
text: "Reload Now",
|
|
160
|
+
onPress: () => import_react_native.HotUpdater.reload()
|
|
161
|
+
}]);
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
onError?.(error);
|
|
165
|
+
setIsUserClearedForAccess(true);
|
|
166
|
+
} finally {
|
|
167
|
+
if (!isUserClearedForAccessRef.current) {
|
|
168
|
+
setIsUserClearedForAccess(true);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
return () => {
|
|
173
|
+
shouldContinue = false;
|
|
174
|
+
};
|
|
175
|
+
}, [enabled, onUpdateDownloadedCallback, onError]);
|
|
176
|
+
return {
|
|
177
|
+
userClearedForAccess: isUserClearedForAccess,
|
|
178
|
+
progress,
|
|
179
|
+
isUpdatePending: getIsUpdatePending()
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
const checkForUpdate = async (options = {}) => {
|
|
183
|
+
const {
|
|
184
|
+
channel,
|
|
185
|
+
isPreRelease = false
|
|
186
|
+
} = options;
|
|
187
|
+
const requestHeaders = {};
|
|
188
|
+
if (channel) {
|
|
189
|
+
requestHeaders["X-Channel"] = channel;
|
|
190
|
+
}
|
|
191
|
+
const updateInfo = await import_react_native.HotUpdater.checkForUpdate({
|
|
192
|
+
updateStrategy,
|
|
193
|
+
...(Object.keys(requestHeaders).length > 0 && {
|
|
194
|
+
requestHeaders
|
|
195
|
+
})
|
|
196
|
+
});
|
|
197
|
+
if (updateInfo) {
|
|
198
|
+
await updateInfo.updateBundle();
|
|
199
|
+
handleUpdateDownloaded(updateInfo.id, {
|
|
200
|
+
isPreRelease
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
id: updateInfo.id,
|
|
204
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
205
|
+
fileUrl: updateInfo.fileUrl,
|
|
206
|
+
message: updateInfo.message
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
useOtaUpdater,
|
|
213
|
+
checkForUpdate,
|
|
214
|
+
getAppliedOta,
|
|
215
|
+
getShortOtaId,
|
|
216
|
+
getIsUpdatePending,
|
|
217
|
+
reload: () => import_react_native.HotUpdater.reload(),
|
|
218
|
+
getBundleId: () => import_react_native.HotUpdater.getBundleId(),
|
|
219
|
+
getMinBundleId: () => import_react_native.HotUpdater.getMinBundleId(),
|
|
220
|
+
getChannel: () => import_react_native.HotUpdater.getChannel(),
|
|
221
|
+
clearCrashHistory: () => import_react_native.HotUpdater.clearCrashHistory(),
|
|
222
|
+
getCrashHistory: () => import_react_native.HotUpdater.getCrashHistory()
|
|
223
|
+
};
|
|
224
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all) __defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: () => from[key],
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
30
|
+
value: mod,
|
|
31
|
+
enumerable: true
|
|
32
|
+
}) : target, mod));
|
|
33
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
34
|
+
value: true
|
|
35
|
+
}), mod);
|
|
36
|
+
var createHotUpdater_exports = {};
|
|
37
|
+
__export(createHotUpdater_exports, {
|
|
38
|
+
createHotUpdater: () => createHotUpdater
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(createHotUpdater_exports);
|
|
41
|
+
var import_react_native = require("@hot-updater/react-native");
|
|
42
|
+
var Application = __toESM(require("expo-application"), 1);
|
|
43
|
+
var import_react = require("react");
|
|
44
|
+
var import_react_native2 = require("react-native");
|
|
45
|
+
var INITIAL_OTA_ID = "00000000-0000-0000-0000-000000000000";
|
|
46
|
+
var BUNDLE_ID_KEY_PREFIX = "hotUpdater.bundleId";
|
|
47
|
+
var PRE_RELEASE_BUNDLE_ID_KEY = "hotUpdater.preReleaseBundleId";
|
|
48
|
+
function createHotUpdater(config) {
|
|
49
|
+
var {
|
|
50
|
+
storage,
|
|
51
|
+
updateStrategy = "appVersion"
|
|
52
|
+
} = config;
|
|
53
|
+
var isUpdatePending = false;
|
|
54
|
+
var getAppliedOta = function getAppliedOta2() {
|
|
55
|
+
var id = import_react_native.HotUpdater.getBundleId();
|
|
56
|
+
if (id === INITIAL_OTA_ID) return null;
|
|
57
|
+
if (id === import_react_native.HotUpdater.getMinBundleId()) return null;
|
|
58
|
+
return id;
|
|
59
|
+
};
|
|
60
|
+
var getShortOtaId = function getShortOtaId2() {
|
|
61
|
+
var fullId = getAppliedOta();
|
|
62
|
+
return fullId ? fullId.slice(-12) : null;
|
|
63
|
+
};
|
|
64
|
+
var getIsUpdatePending = function getIsUpdatePending2() {
|
|
65
|
+
return isUpdatePending;
|
|
66
|
+
};
|
|
67
|
+
var getPreReleaseBundleId = function getPreReleaseBundleId2() {
|
|
68
|
+
return storage.get(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
69
|
+
};
|
|
70
|
+
var handleUpdateDownloaded = function handleUpdateDownloaded2(id) {
|
|
71
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
72
|
+
var appVersion = Application.nativeApplicationVersion;
|
|
73
|
+
if (appVersion) {
|
|
74
|
+
storage.set(`${BUNDLE_ID_KEY_PREFIX}.${appVersion}`, id);
|
|
75
|
+
}
|
|
76
|
+
if (options.isPreRelease) {
|
|
77
|
+
storage.set(PRE_RELEASE_BUNDLE_ID_KEY, id);
|
|
78
|
+
} else {
|
|
79
|
+
storage.delete(PRE_RELEASE_BUNDLE_ID_KEY);
|
|
80
|
+
}
|
|
81
|
+
isUpdatePending = true;
|
|
82
|
+
};
|
|
83
|
+
var useOtaUpdater = function useOtaUpdater2() {
|
|
84
|
+
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
85
|
+
var {
|
|
86
|
+
enabled = true,
|
|
87
|
+
onUpdateDownloaded: onUpdateDownloadedCallback,
|
|
88
|
+
onError
|
|
89
|
+
} = options;
|
|
90
|
+
var progress = (0, import_react_native.useHotUpdaterStore)(function (state) {
|
|
91
|
+
return state.progress;
|
|
92
|
+
});
|
|
93
|
+
var isUpdating = progress > 0.01;
|
|
94
|
+
var [isUserClearedForAccess, setIsUserClearedForAccess] = (0, import_react.useState)(false);
|
|
95
|
+
var isUserClearedForAccessRef = (0, import_react.useRef)(isUserClearedForAccess);
|
|
96
|
+
isUserClearedForAccessRef.current = isUserClearedForAccess;
|
|
97
|
+
(0, import_react.useEffect)(function () {
|
|
98
|
+
if (!isUpdating) {
|
|
99
|
+
var timer = setTimeout(function () {
|
|
100
|
+
if (!isUserClearedForAccessRef.current) {
|
|
101
|
+
setIsUserClearedForAccess(true);
|
|
102
|
+
}
|
|
103
|
+
}, 5e3);
|
|
104
|
+
return function () {
|
|
105
|
+
return clearTimeout(timer);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return void 0;
|
|
109
|
+
}, [isUpdating]);
|
|
110
|
+
(0, import_react.useEffect)(function () {
|
|
111
|
+
var timer = setTimeout(function () {
|
|
112
|
+
if (!isUserClearedForAccessRef.current) {
|
|
113
|
+
setIsUserClearedForAccess(true);
|
|
114
|
+
}
|
|
115
|
+
}, 2e4);
|
|
116
|
+
return function () {
|
|
117
|
+
return clearTimeout(timer);
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
120
|
+
(0, import_react.useEffect)(function () {
|
|
121
|
+
var shouldContinue = true;
|
|
122
|
+
(async function () {
|
|
123
|
+
try {
|
|
124
|
+
if (!enabled) {
|
|
125
|
+
setIsUserClearedForAccess(true);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!shouldContinue) return;
|
|
129
|
+
var updateInfo = await import_react_native.HotUpdater.checkForUpdate({
|
|
130
|
+
updateStrategy
|
|
131
|
+
});
|
|
132
|
+
if (!updateInfo) {
|
|
133
|
+
setIsUserClearedForAccess(true);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!shouldContinue) return;
|
|
137
|
+
if (updateInfo.status === "ROLLBACK") {
|
|
138
|
+
var preReleaseBundleId = getPreReleaseBundleId();
|
|
139
|
+
var currentBundleId = import_react_native.HotUpdater.getBundleId();
|
|
140
|
+
if (preReleaseBundleId && preReleaseBundleId === currentBundleId && currentBundleId > updateInfo.id) {
|
|
141
|
+
import_react_native2.Alert.alert("Update Skipped", "Skipped rollback because you are using a newer pre-release bundle.");
|
|
142
|
+
setIsUserClearedForAccess(true);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!updateInfo.shouldForceUpdate) {
|
|
147
|
+
setIsUserClearedForAccess(true);
|
|
148
|
+
}
|
|
149
|
+
if (!shouldContinue) return;
|
|
150
|
+
await updateInfo.updateBundle();
|
|
151
|
+
handleUpdateDownloaded(updateInfo.id);
|
|
152
|
+
var info = {
|
|
153
|
+
id: updateInfo.id,
|
|
154
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
155
|
+
fileUrl: updateInfo.fileUrl,
|
|
156
|
+
message: updateInfo.message
|
|
157
|
+
};
|
|
158
|
+
onUpdateDownloadedCallback === null || onUpdateDownloadedCallback === void 0 ? void 0 : onUpdateDownloadedCallback(info);
|
|
159
|
+
if (!shouldContinue) return;
|
|
160
|
+
if (updateInfo.shouldForceUpdate) {
|
|
161
|
+
if (!isUserClearedForAccessRef.current) {
|
|
162
|
+
import_react_native.HotUpdater.reload();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
import_react_native2.Alert.alert("Update Downloaded", "An important update has been downloaded. Reload now to apply it?", [{
|
|
166
|
+
text: "Later",
|
|
167
|
+
style: "cancel"
|
|
168
|
+
}, {
|
|
169
|
+
text: "Reload Now",
|
|
170
|
+
onPress: function onPress() {
|
|
171
|
+
return import_react_native.HotUpdater.reload();
|
|
172
|
+
}
|
|
173
|
+
}]);
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
177
|
+
setIsUserClearedForAccess(true);
|
|
178
|
+
} finally {
|
|
179
|
+
if (!isUserClearedForAccessRef.current) {
|
|
180
|
+
setIsUserClearedForAccess(true);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
})();
|
|
184
|
+
return function () {
|
|
185
|
+
shouldContinue = false;
|
|
186
|
+
};
|
|
187
|
+
}, [enabled, onUpdateDownloadedCallback, onError]);
|
|
188
|
+
return {
|
|
189
|
+
userClearedForAccess: isUserClearedForAccess,
|
|
190
|
+
progress,
|
|
191
|
+
isUpdatePending: getIsUpdatePending()
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
var checkForUpdate = async function checkForUpdate2() {
|
|
195
|
+
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
196
|
+
var {
|
|
197
|
+
channel,
|
|
198
|
+
isPreRelease = false
|
|
199
|
+
} = options;
|
|
200
|
+
var requestHeaders = {};
|
|
201
|
+
if (channel) {
|
|
202
|
+
requestHeaders["X-Channel"] = channel;
|
|
203
|
+
}
|
|
204
|
+
var updateInfo = await import_react_native.HotUpdater.checkForUpdate({
|
|
205
|
+
updateStrategy,
|
|
206
|
+
...(Object.keys(requestHeaders).length > 0 && {
|
|
207
|
+
requestHeaders
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
if (updateInfo) {
|
|
211
|
+
await updateInfo.updateBundle();
|
|
212
|
+
handleUpdateDownloaded(updateInfo.id, {
|
|
213
|
+
isPreRelease
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
id: updateInfo.id,
|
|
217
|
+
isCriticalUpdate: updateInfo.shouldForceUpdate,
|
|
218
|
+
fileUrl: updateInfo.fileUrl,
|
|
219
|
+
message: updateInfo.message
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
useOtaUpdater,
|
|
226
|
+
checkForUpdate,
|
|
227
|
+
getAppliedOta,
|
|
228
|
+
getShortOtaId,
|
|
229
|
+
getIsUpdatePending,
|
|
230
|
+
reload: function reload() {
|
|
231
|
+
return import_react_native.HotUpdater.reload();
|
|
232
|
+
},
|
|
233
|
+
getBundleId: function getBundleId() {
|
|
234
|
+
return import_react_native.HotUpdater.getBundleId();
|
|
235
|
+
},
|
|
236
|
+
getMinBundleId: function getMinBundleId() {
|
|
237
|
+
return import_react_native.HotUpdater.getMinBundleId();
|
|
238
|
+
},
|
|
239
|
+
getChannel: function getChannel() {
|
|
240
|
+
return import_react_native.HotUpdater.getChannel();
|
|
241
|
+
},
|
|
242
|
+
clearCrashHistory: function clearCrashHistory() {
|
|
243
|
+
return import_react_native.HotUpdater.clearCrashHistory();
|
|
244
|
+
},
|
|
245
|
+
getCrashHistory: function getCrashHistory() {
|
|
246
|
+
return import_react_native.HotUpdater.getCrashHistory();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=createHotUpdater.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["__toCommonJS","mod","__copyProps","__defProp","value","createHotUpdater_exports","__export","createHotUpdater","module","exports","import_react_native","require","Application","__toESM","import_react","import_react_native2","INITIAL_OTA_ID","BUNDLE_ID_KEY_PREFIX","PRE_RELEASE_BUNDLE_ID_KEY","config","storage","updateStrategy","isUpdatePending","getAppliedOta","getAppliedOta2","id","HotUpdater","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","useHotUpdaterStore","state","isUpdating","isUserClearedForAccess","setIsUserClearedForAccess","useState","isUserClearedForAccessRef","useRef","current","useEffect","timer","setTimeout","clearTimeout","shouldContinue","updateInfo","checkForUpdate","status","preReleaseBundleId","currentBundleId","Alert","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":[]}
|