@hot-updater/react-native
Version:
React Native OTA solution for self-hosted
228 lines (223 loc) • 6.68 kB
text/typescript
import { checkForUpdate } from "./checkForUpdate";
import {
addListener,
getAppVersion,
getBundleId,
getChannel,
getFingerprintHash,
getMinBundleId,
reload,
updateBundle,
} from "./native";
import { runUpdateProcess } from "./runUpdateProcess";
import { hotUpdaterStore } from "./store";
import { wrap } from "./wrap";
export type { HotUpdaterEvent } from "./native";
export * from "./store";
export type { HotUpdaterOptions } from "./wrap";
addListener("onProgress", ({ progress }) => {
hotUpdaterStore.setState({
progress,
});
});
export const HotUpdater = {
/**
* `HotUpdater.wrap` checks for updates at the entry point, and if there is a bundle to update, it downloads the bundle and applies the update strategy.
*
* @param {object} options - Configuration options
* @param {string} options.source - Update server URL
* @param {object} [options.requestHeaders] - Request headers
* @param {React.ComponentType} [options.fallbackComponent] - Component to display during updates
* @param {boolean} [options.reloadOnForceUpdate=true] - Whether to automatically reload the app on force updates
* @param {Function} [options.onUpdateProcessCompleted] - Callback after update process completes
* @param {Function} [options.onProgress] - Callback to track bundle download progress
* @returns {Function} Higher-order component that wraps the app component
*
* @example
* ```tsx
* export default HotUpdater.wrap({
* source: "<your-update-server-url>",
* requestHeaders: {
* "Authorization": "Bearer <your-access-token>",
* },
* })(App);
* ```
*/
wrap,
/**
* Reloads the app.
*/
reload,
/**
* Returns whether an update has finished downloading in this app session.
*
* When it returns true, calling `HotUpdater.reload()` (or restarting the app)
* will apply the downloaded update bundle.
*
* - Derived from `progress` reaching 1.0
* - Resets to false when a new download starts (progress < 1)
*
* @returns {boolean} True if a downloaded update is ready to apply
* @example
* ```ts
* if (HotUpdater.isUpdateDownloaded()) {
* await HotUpdater.reload();
* }
* ```
*/
isUpdateDownloaded: () => hotUpdaterStore.getSnapshot().isUpdateDownloaded,
/**
* Fetches the current app version.
*/
getAppVersion,
/**
* Fetches the current bundle ID of the app.
*/
getBundleId,
/**
* Retrieves the initial bundle ID based on the build time of the native app.
*/
getMinBundleId,
/**
* Fetches the current channel of the app.
*
* If no channel is specified, the app is assigned to the 'production' channel.
*
* @returns {string} The current release channel of the app
* @default "production"
* @example
* ```ts
* const channel = HotUpdater.getChannel();
* console.log(`Current channel: ${channel}`);
* ```
*/
getChannel,
/**
* Adds a listener to HotUpdater events.
*
* @param {keyof HotUpdaterEvent} eventName - The name of the event to listen for
* @param {(event: HotUpdaterEvent[T]) => void} listener - The callback function to handle the event
* @returns {() => void} A cleanup function that removes the event listener
*
* @example
* ```ts
* const unsubscribe = HotUpdater.addListener("onProgress", ({ progress }) => {
* console.log(`Update progress: ${progress * 100}%`);
* });
*
* // Unsubscribe when no longer needed
* unsubscribe();
* ```
*/
addListener,
/**
* Manually checks for updates.
*
* @param {Object} config - Update check configuration
* @param {string} config.source - Update server URL
* @param {Record<string, string>} [config.requestHeaders] - Request headers
*
* @returns {Promise<UpdateInfo | null>} Update information or null if up to date
*
* @example
* ```ts
* const updateInfo = await HotUpdater.checkForUpdate({
* source: "<your-update-server-url>",
* requestHeaders: {
* Authorization: "Bearer <your-access-token>",
* },
* });
*
* if (!updateInfo) {
* console.log("App is up to date");
* return;
* }
*
* await HotUpdater.updateBundle(updateInfo.id, updateInfo.fileUrl);
* if (updateInfo.shouldForceUpdate) {
* await HotUpdater.reload();
* }
* ```
*/
checkForUpdate,
/**
* Manually checks and applies updates for the application.
*
* @param {RunUpdateProcessConfig} config - Update process configuration
* @param {string} config.source - Update server URL
* @param {Record<string, string>} [config.requestHeaders] - Request headers
* @param {boolean} [config.reloadOnForceUpdate=false] - Whether to automatically reload on force update
*
* @example
* ```ts
* // Auto reload on force update
* const result = await HotUpdater.runUpdateProcess({
* source: "<your-update-server-url>",
* requestHeaders: {
* // Add necessary headers
* },
* reloadOnForceUpdate: true
* });
*
* // Manually handle reload on force update
* const result = await HotUpdater.runUpdateProcess({
* source: "<your-update-server-url>",
* reloadOnForceUpdate: false
* });
*
* if(result.status !== "UP_TO_DATE" && result.shouldForceUpdate) {
* await HotUpdater.reload();
* }
* ```
*
* @returns {Promise<RunUpdateProcessResponse>} The result of the update process
*/
runUpdateProcess,
/**
* Updates the bundle of the app.
*
* @param {UpdateBundleParams} params - Parameters object required for bundle update
* @param {string} params.bundleId - The bundle ID of the app
* @param {string|null} params.fileUrl - The URL of the zip file
*
* @returns {Promise<boolean>} Whether the update was successful
*
* @example
* ```ts
* const updateInfo = await HotUpdater.checkForUpdate({
* source: "<your-update-server-url>",
* requestHeaders: {
* Authorization: "Bearer <your-access-token>",
* },
* });
*
* if (!updateInfo) {
* return {
* status: "UP_TO_DATE",
* };
* }
*
* await HotUpdater.updateBundle({
* bundleId: updateInfo.id,
* fileUrl: updateInfo.fileUrl
* });
* if (updateInfo.shouldForceUpdate) {
* await HotUpdater.reload();
* }
* ```
*/
updateBundle,
/**
* Fetches the fingerprint of the app.
*
* @returns {string} The fingerprint of the app
*
* @example
* ```ts
* const fingerprint = HotUpdater.getFingerprintHash();
* console.log(`Fingerprint: ${fingerprint}`);
* ```
*/
getFingerprintHash,
};
export { getUpdateSource } from "./checkForUpdate";