@rific/updater 0.1.1 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/updater",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "OTA update hook for Expo apps — silent background fetch on foreground, manual check with confirmation dialog",
5
5
  "keywords": [
6
6
  "expo",
@@ -23,8 +23,8 @@
23
23
  "type": "commonjs",
24
24
  "exports": {
25
25
  ".": {
26
- "types": "./dist/index.d.ts",
27
26
  "react-native": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
28
  "import": "./dist/index.mjs",
29
29
  "require": "./dist/index.js"
30
30
  }
@@ -37,7 +37,10 @@
37
37
  },
38
38
  "files": [
39
39
  "dist",
40
- "scripts"
40
+ "scripts",
41
+ "src",
42
+ "!src/__tests__",
43
+ "!src/__mocks__"
41
44
  ],
42
45
  "scripts": {
43
46
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
@@ -51,7 +54,7 @@
51
54
  "test": "jest",
52
55
  "test:watch": "jest --watchAll",
53
56
  "typecheck": "tsc --noEmit",
54
- "preversion": "npm run lint && npm test"
57
+ "preversion": "npm run lint && npm test && npm run build"
55
58
  },
56
59
  "devDependencies": {
57
60
  "@testing-library/react": "^16.3.2",
@@ -77,8 +80,8 @@
77
80
  },
78
81
  "peerDependencies": {
79
82
  "expo-updates": ">=0.25.0",
80
- "react": ">=17.0.0",
81
- "react-native": ">=0.70.0"
83
+ "react": ">=18.0.0",
84
+ "react-native": ">=0.76.0"
82
85
  },
83
86
  "publishConfig": {
84
87
  "access": "public"
@@ -0,0 +1,10 @@
1
+ import { checkForUpdateAsync, fetchUpdateAsync } from 'expo-updates'
2
+
3
+ import { UpdateManifest } from './types'
4
+
5
+ export const checkForUpdate = async (): Promise<UpdateManifest | null> => {
6
+ const { isAvailable } = await checkForUpdateAsync()
7
+ if (!isAvailable) return null
8
+ const { manifest } = await fetchUpdateAsync()
9
+ return manifest ? (manifest as unknown as UpdateManifest) : null
10
+ }
@@ -0,0 +1,17 @@
1
+ import { Alert } from 'react-native'
2
+
3
+ import { UpdateManifest } from './types'
4
+
5
+ export const getUpdateConfirmation = (manifest: UpdateManifest): Promise<boolean> => {
6
+ const date = new Date(manifest.createdAt)
7
+ let info = `A new update was released on ${date.toLocaleDateString()} at ${date.toLocaleTimeString()}.`
8
+ if (manifest.metadata?.message) info += `\n\nMessage: ${manifest.metadata.message}.`
9
+ info += '\n\nRestart app to update.'
10
+
11
+ return new Promise((resolve) => {
12
+ Alert.alert('Update available', info, [
13
+ { text: 'Cancel', style: 'cancel', onPress: () => resolve(false) },
14
+ { text: 'Restart', onPress: () => resolve(true) }
15
+ ])
16
+ })
17
+ }
@@ -0,0 +1 @@
1
+ declare const __DEV__: boolean
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { type UpdateManifest } from './types'
2
+ export { useUpdater, type UseUpdaterOptions, type UseUpdaterReturn } from './useUpdater'
package/src/types.ts ADDED
@@ -0,0 +1,4 @@
1
+ export interface UpdateManifest {
2
+ createdAt: string
3
+ metadata?: Record<string, string | undefined>
4
+ }
@@ -0,0 +1,86 @@
1
+ import { reloadAsync } from 'expo-updates'
2
+ import { useEffect, useRef, useState } from 'react'
3
+ import { Alert, AppState, AppStateStatus, Platform } from 'react-native'
4
+
5
+ import { checkForUpdate } from './checkForUpdate'
6
+ import { getUpdateConfirmation } from './getUpdateConfirmation'
7
+ import { UpdateManifest } from './types'
8
+
9
+ export interface UseUpdaterOptions {
10
+ autoCheck?: boolean
11
+ onConfirm?: (manifest: UpdateManifest) => Promise<boolean>
12
+ onError?: (message: string) => void
13
+ }
14
+
15
+ export interface UseUpdaterReturn {
16
+ check: () => Promise<void>
17
+ checking: boolean
18
+ updateReady: boolean
19
+ }
20
+
21
+ const isUnsupported = () => __DEV__ || Platform.OS === 'web'
22
+
23
+ export const useUpdater = (options: UseUpdaterOptions = {}): UseUpdaterReturn => {
24
+ const { autoCheck = true, onConfirm, onError } = options
25
+ const [checking, setChecking] = useState(false)
26
+ const [updateReady, setUpdateReady] = useState(false)
27
+ const checkingRef = useRef(false)
28
+ const appState = useRef(AppState.currentState)
29
+ const stagedManifest = useRef<UpdateManifest | null>(null)
30
+
31
+ useEffect(() => {
32
+ if (!autoCheck || isUnsupported()) return
33
+
34
+ const subscription = AppState.addEventListener('change', (nextState: AppStateStatus) => {
35
+ if (/inactive|background/.test(appState.current) && nextState === 'active') {
36
+ checkForUpdate()
37
+ .then((manifest) => {
38
+ if (manifest) {
39
+ stagedManifest.current = manifest
40
+ setUpdateReady(true)
41
+ }
42
+ })
43
+ .catch(() => {})
44
+ }
45
+ appState.current = nextState
46
+ })
47
+
48
+ return () => subscription.remove()
49
+ }, [autoCheck])
50
+
51
+ const check = async (): Promise<void> => {
52
+ if (__DEV__) {
53
+ Alert.alert('Updates unavailable', 'Update checks are disabled in development mode.')
54
+ return
55
+ }
56
+ if (Platform.OS === 'web') {
57
+ Alert.alert('Updates unavailable', 'Update checks are not supported on web.')
58
+ return
59
+ }
60
+ if (checkingRef.current) return
61
+ checkingRef.current = true
62
+ setChecking(true)
63
+ try {
64
+ const manifest = stagedManifest.current ?? (await checkForUpdate())
65
+ if (!manifest) {
66
+ Alert.alert('No update', 'You are on the most recent version.')
67
+ return
68
+ }
69
+ const confirmFn = onConfirm ?? getUpdateConfirmation
70
+ const confirmed = await confirmFn(manifest)
71
+ if (!confirmed) return
72
+ await reloadAsync()
73
+ } catch (err) {
74
+ const message = err instanceof Error ? err.message : 'Could not check for updates.'
75
+ if (onError) onError(message)
76
+ else Alert.alert('Update error', message)
77
+ } finally {
78
+ stagedManifest.current = null
79
+ setUpdateReady(false)
80
+ checkingRef.current = false
81
+ setChecking(false)
82
+ }
83
+ }
84
+
85
+ return { check, checking, updateReady }
86
+ }