@hot-updater/react-native 0.24.0 → 0.24.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/lib/commonjs/DefaultResolver.js +38 -0
- package/lib/commonjs/DefaultResolver.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +35 -50
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +26 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js +12 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js +44 -13
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/DefaultResolver.js +34 -0
- package/lib/module/DefaultResolver.js.map +1 -0
- package/lib/module/checkForUpdate.js +35 -50
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +26 -14
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +12 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js +44 -13
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/DefaultResolver.d.ts +10 -0
- package/lib/typescript/commonjs/DefaultResolver.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +115 -0
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +64 -10
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/module/DefaultResolver.d.ts +10 -0
- package/lib/typescript/module/DefaultResolver.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +2 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +115 -0
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +64 -10
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/DefaultResolver.ts +36 -0
- package/src/checkForUpdate.ts +43 -59
- package/src/index.ts +51 -19
- package/src/types.ts +135 -0
- package/src/wrap.tsx +161 -72
|
@@ -1,3 +1,118 @@
|
|
|
1
|
+
import type { AppUpdateInfo } from "@hot-updater/core";
|
|
2
|
+
import type { NotifyAppReadyResult } from "./native";
|
|
3
|
+
/**
|
|
4
|
+
* Parameters passed to resolver.checkUpdate method
|
|
5
|
+
*/
|
|
6
|
+
export interface ResolverCheckUpdateParams {
|
|
7
|
+
/**
|
|
8
|
+
* The platform the app is running on
|
|
9
|
+
*/
|
|
10
|
+
platform: "ios" | "android";
|
|
11
|
+
/**
|
|
12
|
+
* The current app version
|
|
13
|
+
*/
|
|
14
|
+
appVersion: string;
|
|
15
|
+
/**
|
|
16
|
+
* The current bundle ID
|
|
17
|
+
*/
|
|
18
|
+
bundleId: string;
|
|
19
|
+
/**
|
|
20
|
+
* Minimum bundle ID from build time
|
|
21
|
+
*/
|
|
22
|
+
minBundleId: string;
|
|
23
|
+
/**
|
|
24
|
+
* The channel name (e.g., "production", "staging")
|
|
25
|
+
*/
|
|
26
|
+
channel: string;
|
|
27
|
+
/**
|
|
28
|
+
* Update strategy being used
|
|
29
|
+
*/
|
|
30
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
31
|
+
/**
|
|
32
|
+
* The fingerprint hash (only present when using fingerprint strategy)
|
|
33
|
+
*/
|
|
34
|
+
fingerprintHash: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Request headers from global config (for optional use)
|
|
37
|
+
*/
|
|
38
|
+
requestHeaders?: Record<string, string>;
|
|
39
|
+
/**
|
|
40
|
+
* Request timeout from global config (for optional use)
|
|
41
|
+
*/
|
|
42
|
+
requestTimeout?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parameters passed to resolver.notifyAppReady method
|
|
46
|
+
*/
|
|
47
|
+
export interface ResolverNotifyAppReadyParams {
|
|
48
|
+
/**
|
|
49
|
+
* The bundle state from native notifyAppReady
|
|
50
|
+
* - "PROMOTED": Staging bundle was promoted to stable
|
|
51
|
+
* - "RECOVERED": App recovered from crash, rollback occurred
|
|
52
|
+
* - "STABLE": No changes, bundle is stable
|
|
53
|
+
*/
|
|
54
|
+
status: "PROMOTED" | "RECOVERED" | "STABLE";
|
|
55
|
+
/**
|
|
56
|
+
* Present only when status is "RECOVERED"
|
|
57
|
+
*/
|
|
58
|
+
crashedBundleId?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Request headers from global config (for optional use)
|
|
61
|
+
*/
|
|
62
|
+
requestHeaders?: Record<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* Request timeout from global config (for optional use)
|
|
65
|
+
*/
|
|
66
|
+
requestTimeout?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolver interface for custom network operations
|
|
70
|
+
*/
|
|
71
|
+
export interface HotUpdaterResolver {
|
|
72
|
+
/**
|
|
73
|
+
* Custom implementation for checking updates.
|
|
74
|
+
* When provided, this completely replaces the default fetchUpdateInfo flow.
|
|
75
|
+
*
|
|
76
|
+
* @param params - All parameters needed to check for updates
|
|
77
|
+
* @returns Update information or null if up to date
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* checkUpdate: async (params) => {
|
|
82
|
+
* const response = await fetch(`https://api.custom.com/check`, {
|
|
83
|
+
* method: 'POST',
|
|
84
|
+
* body: JSON.stringify(params),
|
|
85
|
+
* headers: params.requestHeaders,
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* if (!response.ok) return null;
|
|
89
|
+
* return response.json();
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
checkUpdate?: (params: ResolverCheckUpdateParams) => Promise<AppUpdateInfo | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Custom implementation for notifying app ready.
|
|
96
|
+
* When provided, this completely replaces the default notifyAppReady network flow.
|
|
97
|
+
* Note: The native notifyAppReady for bundle promotion still happens automatically.
|
|
98
|
+
*
|
|
99
|
+
* @param params - All parameters about the current app state
|
|
100
|
+
* @returns Notification result
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* notifyAppReady: async (params) => {
|
|
105
|
+
* await fetch(`https://api.custom.com/notify`, {
|
|
106
|
+
* method: 'POST',
|
|
107
|
+
* body: JSON.stringify(params),
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* return { status: "STABLE" };
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
notifyAppReady?: (params: ResolverNotifyAppReadyParams) => Promise<NotifyAppReadyResult | undefined>;
|
|
115
|
+
}
|
|
1
116
|
/**
|
|
2
117
|
* Information about a signature verification failure.
|
|
3
118
|
* This is a security-critical event that indicates the bundle
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAgBpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,4BAA4B,CAS9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAE5B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,cAAc,EAAE,aAAa,GAAG,YAAY,CAAC;IAE7C;;OAEG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IAE5C;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,yBAAyB,KAC9B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAEnC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,4BAA4B,KACjC,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAgBpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,4BAA4B,CAS9B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { HotUpdaterError } from "./error";
|
|
3
3
|
import { type NotifyAppReadyResult } from "./native";
|
|
4
|
+
import type { HotUpdaterResolver } from "./types";
|
|
4
5
|
export interface RunUpdateProcessResponse {
|
|
5
6
|
status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
|
|
6
7
|
shouldForceUpdate: boolean;
|
|
@@ -12,11 +13,6 @@ type UpdateStatus = "CHECK_FOR_UPDATE" | "UPDATING" | "UPDATE_PROCESS_COMPLETED"
|
|
|
12
13
|
* Common options shared between auto and manual update modes
|
|
13
14
|
*/
|
|
14
15
|
interface CommonHotUpdaterOptions {
|
|
15
|
-
/**
|
|
16
|
-
* Base URL for update server
|
|
17
|
-
* @example "https://update.example.com"
|
|
18
|
-
*/
|
|
19
|
-
baseURL: string;
|
|
20
16
|
/**
|
|
21
17
|
* Custom request headers for update checks
|
|
22
18
|
*/
|
|
@@ -54,7 +50,38 @@ interface CommonHotUpdaterOptions {
|
|
|
54
50
|
*/
|
|
55
51
|
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
56
52
|
}
|
|
57
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Configuration with baseURL for standard server-based updates
|
|
55
|
+
*/
|
|
56
|
+
interface BaseURLConfig {
|
|
57
|
+
/**
|
|
58
|
+
* Base URL for update server
|
|
59
|
+
* @example "https://update.example.com"
|
|
60
|
+
*/
|
|
61
|
+
baseURL: string;
|
|
62
|
+
/**
|
|
63
|
+
* Resolver is not allowed when using baseURL
|
|
64
|
+
*/
|
|
65
|
+
resolver?: never;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Configuration with resolver for custom network operations
|
|
69
|
+
*/
|
|
70
|
+
interface ResolverConfig {
|
|
71
|
+
/**
|
|
72
|
+
* Custom resolver for network operations
|
|
73
|
+
*/
|
|
74
|
+
resolver: HotUpdaterResolver;
|
|
75
|
+
/**
|
|
76
|
+
* baseURL is not allowed when using resolver
|
|
77
|
+
*/
|
|
78
|
+
baseURL?: never;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Union type ensuring baseURL and resolver are mutually exclusive
|
|
82
|
+
*/
|
|
83
|
+
type NetworkConfig = BaseURLConfig | ResolverConfig;
|
|
84
|
+
export type AutoUpdateOptions = CommonHotUpdaterOptions & NetworkConfig & {
|
|
58
85
|
/**
|
|
59
86
|
* Update strategy
|
|
60
87
|
* - "fingerprint": Use fingerprint hash to check for updates
|
|
@@ -108,15 +135,42 @@ export interface AutoUpdateOptions extends CommonHotUpdaterOptions {
|
|
|
108
135
|
* @see {@link https://hot-updater.dev/docs/react-native-api/wrap#onupdateprocesscompleted}
|
|
109
136
|
*/
|
|
110
137
|
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
111
|
-
}
|
|
112
|
-
export
|
|
138
|
+
};
|
|
139
|
+
export type ManualUpdateOptions = CommonHotUpdaterOptions & NetworkConfig & {
|
|
113
140
|
/**
|
|
114
141
|
* Update mode
|
|
115
142
|
* - "manual": Only notify app ready, user manually calls checkForUpdate()
|
|
116
143
|
*/
|
|
117
144
|
updateMode: "manual";
|
|
118
|
-
}
|
|
145
|
+
};
|
|
119
146
|
export type HotUpdaterOptions = AutoUpdateOptions | ManualUpdateOptions;
|
|
120
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Internal options after normalization in index.ts
|
|
149
|
+
* Always has resolver (never baseURL)
|
|
150
|
+
*/
|
|
151
|
+
type InternalCommonOptions = {
|
|
152
|
+
resolver: HotUpdaterResolver;
|
|
153
|
+
requestHeaders?: Record<string, string>;
|
|
154
|
+
requestTimeout?: number;
|
|
155
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
156
|
+
};
|
|
157
|
+
type InternalAutoUpdateOptions = InternalCommonOptions & {
|
|
158
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
159
|
+
updateMode: "auto";
|
|
160
|
+
onError?: (error: HotUpdaterError | Error | unknown) => void;
|
|
161
|
+
fallbackComponent?: React.FC<{
|
|
162
|
+
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
163
|
+
progress: number;
|
|
164
|
+
message: string | null;
|
|
165
|
+
}>;
|
|
166
|
+
onProgress?: (progress: number) => void;
|
|
167
|
+
reloadOnForceUpdate?: boolean;
|
|
168
|
+
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
169
|
+
};
|
|
170
|
+
type InternalManualUpdateOptions = InternalCommonOptions & {
|
|
171
|
+
updateMode: "manual";
|
|
172
|
+
};
|
|
173
|
+
export type InternalWrapOptions = InternalAutoUpdateOptions | InternalManualUpdateOptions;
|
|
174
|
+
export declare function wrap<P extends React.JSX.IntrinsicAttributes = object>(options: InternalWrapOptions): (WrappedComponent: React.ComponentType<P>) => React.ComponentType<P>;
|
|
121
175
|
export {};
|
|
122
176
|
//# sourceMappingURL=wrap.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../../src/wrap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../../src/wrap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC7C,iBAAiB,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,KAAK,YAAY,GACb,kBAAkB,GAClB,UAAU,GACV,0BAA0B,CAAC;AAE/B;;GAEG;AACH,UAAU,uBAAuB;IAC/B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC3D;AAED;;GAEG;AACH,UAAU,aAAa;IACrB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED;;GAEG;AACH,UAAU,cAAc;IACtB;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAC;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;AAEpD,MAAM,MAAM,iBAAiB,GAAG,uBAAuB,GACrD,aAAa,GAAG;IACd;;;;OAIG;IACH,cAAc,EAAE,aAAa,GAAG,YAAY,CAAC;IAE7C;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,GAAG,OAAO,KAAK,IAAI,CAAC;IAE7D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC;QAC1D,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC,CAAC;IAEH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACzE,CAAC;AAEJ,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,GACvD,aAAa,GAAG;IACd;;;OAGG;IACH,UAAU,EAAE,QAAQ,CAAC;CACtB,CAAC;AAEJ,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAExE;;;GAGG;AACH,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC3D,CAAC;AAEF,KAAK,yBAAyB,GAAG,qBAAqB,GAAG;IACvD,cAAc,EAAE,aAAa,GAAG,YAAY,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,GAAG,OAAO,KAAK,IAAI,CAAC;IAC7D,iBAAiB,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC;QAC1D,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,wBAAwB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF,KAAK,2BAA2B,GAAG,qBAAqB,GAAG;IACzD,UAAU,EAAE,QAAQ,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,yBAAyB,GACzB,2BAA2B,CAAC;AAmChC,wBAAgB,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,mBAAmB,GAAG,MAAM,EACnE,OAAO,EAAE,mBAAmB,GAC3B,CAAC,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CA8HtE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -116,18 +116,18 @@
|
|
|
116
116
|
"@types/use-sync-external-store": "^0.0.6",
|
|
117
117
|
"del-cli": "^6.0.0",
|
|
118
118
|
"expo": "^50.0.0",
|
|
119
|
-
"react": "19.1.
|
|
119
|
+
"react": "19.1.0",
|
|
120
120
|
"react-native": "0.79.1",
|
|
121
121
|
"react-native-builder-bob": "^0.40.10",
|
|
122
122
|
"typescript": "^5.8.3",
|
|
123
|
-
"hot-updater": "0.24.
|
|
123
|
+
"hot-updater": "0.24.1"
|
|
124
124
|
},
|
|
125
125
|
"dependencies": {
|
|
126
126
|
"use-sync-external-store": "1.5.0",
|
|
127
|
-
"@hot-updater/cli-tools": "0.24.
|
|
128
|
-
"@hot-updater/
|
|
129
|
-
"@hot-updater/core": "0.24.
|
|
130
|
-
"@hot-updater/
|
|
127
|
+
"@hot-updater/cli-tools": "0.24.1",
|
|
128
|
+
"@hot-updater/core": "0.24.1",
|
|
129
|
+
"@hot-updater/plugin-core": "0.24.1",
|
|
130
|
+
"@hot-updater/js": "0.24.1"
|
|
131
131
|
},
|
|
132
132
|
"scripts": {
|
|
133
133
|
"build": "bob build && tsc -p plugin/tsconfig.build.json",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AppUpdateInfo } from "@hot-updater/core";
|
|
2
|
+
import { fetchUpdateInfo } from "./fetchUpdateInfo";
|
|
3
|
+
import type { HotUpdaterResolver, ResolverCheckUpdateParams } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a default resolver that uses baseURL for network operations.
|
|
7
|
+
* This encapsulates the existing baseURL logic into a resolver.
|
|
8
|
+
*
|
|
9
|
+
* @param baseURL - The base URL for the update server
|
|
10
|
+
* @returns A HotUpdaterResolver that uses the baseURL
|
|
11
|
+
*/
|
|
12
|
+
export function createDefaultResolver(baseURL: string): HotUpdaterResolver {
|
|
13
|
+
return {
|
|
14
|
+
checkUpdate: async (
|
|
15
|
+
params: ResolverCheckUpdateParams,
|
|
16
|
+
): Promise<AppUpdateInfo | null> => {
|
|
17
|
+
// Build URL based on strategy (existing buildUpdateUrl logic)
|
|
18
|
+
let url: string;
|
|
19
|
+
if (params.updateStrategy === "fingerprint") {
|
|
20
|
+
if (!params.fingerprintHash) {
|
|
21
|
+
throw new Error("Fingerprint hash is required");
|
|
22
|
+
}
|
|
23
|
+
url = `${baseURL}/fingerprint/${params.platform}/${params.fingerprintHash}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
24
|
+
} else {
|
|
25
|
+
url = `${baseURL}/app-version/${params.platform}/${params.appVersion}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Use existing fetchUpdateInfo
|
|
29
|
+
return fetchUpdateInfo({
|
|
30
|
+
url,
|
|
31
|
+
requestHeaders: params.requestHeaders,
|
|
32
|
+
requestTimeout: params.requestTimeout,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
package/src/checkForUpdate.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { AppUpdateInfo
|
|
1
|
+
import type { AppUpdateInfo } from "@hot-updater/core";
|
|
2
2
|
import { Platform } from "react-native";
|
|
3
3
|
import { HotUpdaterError } from "./error";
|
|
4
|
-
import { fetchUpdateInfo } from "./fetchUpdateInfo";
|
|
5
4
|
import {
|
|
6
5
|
getAppVersion,
|
|
7
6
|
getBundleId,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
getMinBundleId,
|
|
11
10
|
updateBundle,
|
|
12
11
|
} from "./native";
|
|
12
|
+
import type { HotUpdaterResolver } from "./types";
|
|
13
13
|
|
|
14
14
|
export interface CheckForUpdateOptions {
|
|
15
15
|
/**
|
|
@@ -37,28 +37,9 @@ export type CheckForUpdateResult = AppUpdateInfo & {
|
|
|
37
37
|
updateBundle: () => Promise<boolean>;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
// Internal type that includes
|
|
40
|
+
// Internal type that includes resolver for use within index.ts
|
|
41
41
|
export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Internal function to build update URL (not exported)
|
|
46
|
-
function buildUpdateUrl(
|
|
47
|
-
baseURL: string,
|
|
48
|
-
updateStrategy: "appVersion" | "fingerprint",
|
|
49
|
-
params: UpdateBundleParams,
|
|
50
|
-
): string {
|
|
51
|
-
switch (updateStrategy) {
|
|
52
|
-
case "fingerprint": {
|
|
53
|
-
if (!params.fingerprintHash) {
|
|
54
|
-
throw new HotUpdaterError("Fingerprint hash is required");
|
|
55
|
-
}
|
|
56
|
-
return `${baseURL}/fingerprint/${params.platform}/${params.fingerprintHash}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
57
|
-
}
|
|
58
|
-
case "appVersion": {
|
|
59
|
-
return `${baseURL}/app-version/${params.platform}/${params.appVersion}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
42
|
+
resolver: HotUpdaterResolver;
|
|
62
43
|
}
|
|
63
44
|
|
|
64
45
|
export async function checkForUpdate(
|
|
@@ -75,13 +56,6 @@ export async function checkForUpdate(
|
|
|
75
56
|
return null;
|
|
76
57
|
}
|
|
77
58
|
|
|
78
|
-
if (!options.baseURL || !options.updateStrategy) {
|
|
79
|
-
options.onError?.(
|
|
80
|
-
new HotUpdaterError("'baseURL' and 'updateStrategy' are required"),
|
|
81
|
-
);
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
59
|
const currentAppVersion = getAppVersion();
|
|
86
60
|
const platform = Platform.OS as "ios" | "android";
|
|
87
61
|
const currentBundleId = getBundleId();
|
|
@@ -95,35 +69,45 @@ export async function checkForUpdate(
|
|
|
95
69
|
|
|
96
70
|
const fingerprintHash = getFingerprintHash();
|
|
97
71
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
72
|
+
if (!options.resolver?.checkUpdate) {
|
|
73
|
+
options.onError?.(
|
|
74
|
+
new HotUpdaterError("Resolver is required but not configured"),
|
|
75
|
+
);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let updateInfo: AppUpdateInfo | null = null;
|
|
106
80
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
81
|
+
try {
|
|
82
|
+
updateInfo = await options.resolver.checkUpdate({
|
|
83
|
+
platform,
|
|
84
|
+
appVersion: currentAppVersion,
|
|
85
|
+
bundleId: currentBundleId,
|
|
86
|
+
minBundleId,
|
|
87
|
+
channel,
|
|
88
|
+
updateStrategy: options.updateStrategy,
|
|
89
|
+
fingerprintHash,
|
|
90
|
+
requestHeaders: options.requestHeaders,
|
|
91
|
+
requestTimeout: options.requestTimeout,
|
|
92
|
+
});
|
|
93
|
+
} catch (error) {
|
|
94
|
+
options.onError?.(error as Error);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!updateInfo) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
116
101
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
});
|
|
102
|
+
return {
|
|
103
|
+
...updateInfo,
|
|
104
|
+
updateBundle: async () => {
|
|
105
|
+
return updateBundle({
|
|
106
|
+
bundleId: updateInfo.id,
|
|
107
|
+
fileUrl: updateInfo.fileUrl,
|
|
108
|
+
fileHash: updateInfo.fileHash,
|
|
109
|
+
status: updateInfo.status,
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
};
|
|
129
113
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
checkForUpdate,
|
|
4
4
|
type InternalCheckForUpdateOptions,
|
|
5
5
|
} from "./checkForUpdate";
|
|
6
|
+
import { createDefaultResolver } from "./DefaultResolver";
|
|
6
7
|
import {
|
|
7
8
|
addListener,
|
|
8
9
|
clearCrashHistory,
|
|
@@ -17,13 +18,17 @@ import {
|
|
|
17
18
|
updateBundle,
|
|
18
19
|
} from "./native";
|
|
19
20
|
import { hotUpdaterStore } from "./store";
|
|
20
|
-
import
|
|
21
|
+
import type { HotUpdaterResolver } from "./types";
|
|
22
|
+
import { type HotUpdaterOptions, type InternalWrapOptions, wrap } from "./wrap";
|
|
21
23
|
|
|
22
24
|
export type { HotUpdaterEvent, NotifyAppReadyResult } from "./native";
|
|
23
25
|
export * from "./store";
|
|
24
26
|
export {
|
|
25
27
|
extractSignatureFailure,
|
|
28
|
+
type HotUpdaterResolver,
|
|
26
29
|
isSignatureVerificationError,
|
|
30
|
+
type ResolverCheckUpdateParams,
|
|
31
|
+
type ResolverNotifyAppReadyParams,
|
|
27
32
|
type SignatureVerificationFailure,
|
|
28
33
|
} from "./types";
|
|
29
34
|
export type { HotUpdaterOptions, RunUpdateProcessResponse } from "./wrap";
|
|
@@ -41,15 +46,15 @@ addListener("onProgress", ({ progress }) => {
|
|
|
41
46
|
function createHotUpdaterClient() {
|
|
42
47
|
// Global configuration stored from wrap
|
|
43
48
|
const globalConfig: {
|
|
44
|
-
|
|
49
|
+
resolver: HotUpdaterResolver | null;
|
|
45
50
|
requestHeaders?: Record<string, string>;
|
|
46
51
|
requestTimeout?: number;
|
|
47
52
|
} = {
|
|
48
|
-
|
|
53
|
+
resolver: null,
|
|
49
54
|
};
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
if (globalConfig.
|
|
56
|
+
const ensureGlobalResolver = (methodName: string) => {
|
|
57
|
+
if (!globalConfig.resolver) {
|
|
53
58
|
throw new Error(
|
|
54
59
|
`[HotUpdater] ${methodName} requires HotUpdater.wrap() to be used.\n\n` +
|
|
55
60
|
`To fix this issue, wrap your root component with HotUpdater.wrap():\n\n` +
|
|
@@ -67,9 +72,8 @@ function createHotUpdaterClient() {
|
|
|
67
72
|
`For more information, visit: https://hot-updater.dev/docs/react-native-api/wrap`,
|
|
68
73
|
);
|
|
69
74
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
75
|
+
return globalConfig.resolver;
|
|
76
|
+
};
|
|
73
77
|
|
|
74
78
|
return {
|
|
75
79
|
/**
|
|
@@ -96,12 +100,44 @@ function createHotUpdaterClient() {
|
|
|
96
100
|
* })(App);
|
|
97
101
|
* ```
|
|
98
102
|
*/
|
|
99
|
-
wrap: (
|
|
100
|
-
|
|
103
|
+
wrap: (options: HotUpdaterOptions) => {
|
|
104
|
+
let normalizedOptions: InternalWrapOptions;
|
|
105
|
+
|
|
106
|
+
if ("baseURL" in options && options.baseURL) {
|
|
107
|
+
const { baseURL, ...rest } = options;
|
|
108
|
+
normalizedOptions = {
|
|
109
|
+
...rest,
|
|
110
|
+
resolver: createDefaultResolver(baseURL),
|
|
111
|
+
};
|
|
112
|
+
} else if ("resolver" in options && options.resolver) {
|
|
113
|
+
normalizedOptions = options;
|
|
114
|
+
} else {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`[HotUpdater] Either baseURL or resolver must be provided.\n\n` +
|
|
117
|
+
`Option 1: Using baseURL (recommended for most cases)\n` +
|
|
118
|
+
` export default HotUpdater.wrap({\n` +
|
|
119
|
+
` baseURL: "<your-update-server-url>",\n` +
|
|
120
|
+
` updateStrategy: "appVersion",\n` +
|
|
121
|
+
` updateMode: "auto"\n` +
|
|
122
|
+
` })(App);\n\n` +
|
|
123
|
+
`Option 2: Using custom resolver (advanced)\n` +
|
|
124
|
+
` export default HotUpdater.wrap({\n` +
|
|
125
|
+
` resolver: {\n` +
|
|
126
|
+
` checkUpdate: async (params) => { /* custom logic */ },\n` +
|
|
127
|
+
` notifyAppReady: async (params) => { /* custom logic */ }\n` +
|
|
128
|
+
` },\n` +
|
|
129
|
+
` updateMode: "manual"\n` +
|
|
130
|
+
` })(App);\n\n` +
|
|
131
|
+
`For more information, visit: https://hot-updater.dev/docs/react-native-api/wrap`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
globalConfig.resolver = normalizedOptions.resolver;
|
|
101
136
|
globalConfig.requestHeaders = options.requestHeaders;
|
|
102
137
|
globalConfig.requestTimeout = options.requestTimeout;
|
|
103
|
-
|
|
104
|
-
|
|
138
|
+
|
|
139
|
+
return wrap(normalizedOptions);
|
|
140
|
+
},
|
|
105
141
|
|
|
106
142
|
/**
|
|
107
143
|
* Reloads the app.
|
|
@@ -206,14 +242,11 @@ function createHotUpdaterClient() {
|
|
|
206
242
|
* ```
|
|
207
243
|
*/
|
|
208
244
|
checkForUpdate: (config: CheckForUpdateOptions) => {
|
|
209
|
-
const
|
|
245
|
+
const resolver = ensureGlobalResolver("checkForUpdate");
|
|
210
246
|
|
|
211
|
-
// Merge globalConfig with provided config
|
|
212
|
-
// baseURL is always from wrap (globalConfig)
|
|
213
|
-
// requestHeaders/requestTimeout from wrap are used as defaults, but can be overridden
|
|
214
247
|
const mergedConfig: InternalCheckForUpdateOptions = {
|
|
215
248
|
...config,
|
|
216
|
-
|
|
249
|
+
resolver,
|
|
217
250
|
requestHeaders: {
|
|
218
251
|
...globalConfig.requestHeaders,
|
|
219
252
|
...config.requestHeaders,
|
|
@@ -258,8 +291,7 @@ function createHotUpdaterClient() {
|
|
|
258
291
|
* ```
|
|
259
292
|
*/
|
|
260
293
|
updateBundle: (params: UpdateParams) => {
|
|
261
|
-
|
|
262
|
-
|
|
294
|
+
ensureGlobalResolver("updateBundle");
|
|
263
295
|
return updateBundle(params);
|
|
264
296
|
},
|
|
265
297
|
|