react-native-instantpay-code-push 1.1.0
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/InstantpayCodePush.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +158 -0
- package/android/build.gradle +91 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/instantpaycodepush/BundleFileStorageService.kt +835 -0
- package/android/src/main/java/com/instantpaycodepush/BundleMetadata.kt +249 -0
- package/android/src/main/java/com/instantpaycodepush/CommonHelper.kt +39 -0
- package/android/src/main/java/com/instantpaycodepush/DecompressService.kt +85 -0
- package/android/src/main/java/com/instantpaycodepush/DecompressionStrategy.kt +24 -0
- package/android/src/main/java/com/instantpaycodepush/FileManagerService.kt +105 -0
- package/android/src/main/java/com/instantpaycodepush/HashUtils.kt +50 -0
- package/android/src/main/java/com/instantpaycodepush/InstantpayCodePushModule.kt +182 -0
- package/android/src/main/java/com/instantpaycodepush/InstantpayCodePushPackage.kt +33 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePush.kt +101 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePushException.kt +135 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePushImpl.kt +329 -0
- package/android/src/main/java/com/instantpaycodepush/OkHttpDownloadService.kt +283 -0
- package/android/src/main/java/com/instantpaycodepush/ReactIntegrationManager.kt +141 -0
- package/android/src/main/java/com/instantpaycodepush/ReactIntegrationManagerBase.kt +35 -0
- package/android/src/main/java/com/instantpaycodepush/SignatureVerifier.kt +354 -0
- package/android/src/main/java/com/instantpaycodepush/VersionedPreferencesService.kt +70 -0
- package/android/src/main/java/com/instantpaycodepush/ZipDecompressionStrategy.kt +198 -0
- package/ios/InstantpayCodePush.h +5 -0
- package/ios/InstantpayCodePush.mm +21 -0
- package/lib/module/DefaultResolver.js +34 -0
- package/lib/module/DefaultResolver.js.map +1 -0
- package/lib/module/NativeInstantpayCodePush.js +5 -0
- package/lib/module/NativeInstantpayCodePush.js.map +1 -0
- package/lib/module/checkForUpdate.js +68 -0
- package/lib/module/checkForUpdate.js.map +1 -0
- package/lib/module/error.js +137 -0
- package/lib/module/error.js.map +1 -0
- package/lib/module/fetchUpdateInfo.js +36 -0
- package/lib/module/fetchUpdateInfo.js.map +1 -0
- package/lib/module/global.d.js +8 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/hooks/useEventCallback.js +13 -0
- package/lib/module/hooks/useEventCallback.js.map +1 -0
- package/lib/module/index.js +291 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native.js +233 -0
- package/lib/module/native.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/store.js +53 -0
- package/lib/module/store.js.map +1 -0
- package/lib/module/types.js +62 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/wrap.js +171 -0
- package/lib/module/wrap.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/DefaultResolver.d.ts +10 -0
- package/lib/typescript/src/DefaultResolver.d.ts.map +1 -0
- package/lib/typescript/src/NativeInstantpayCodePush.d.ts +100 -0
- package/lib/typescript/src/NativeInstantpayCodePush.d.ts.map +1 -0
- package/lib/typescript/src/checkForUpdate.d.ts +29 -0
- package/lib/typescript/src/checkForUpdate.d.ts.map +1 -0
- package/lib/typescript/src/error.d.ts +124 -0
- package/lib/typescript/src/error.d.ts.map +1 -0
- package/lib/typescript/src/fetchUpdateInfo.d.ts +8 -0
- package/lib/typescript/src/fetchUpdateInfo.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useEventCallback.d.ts +5 -0
- package/lib/typescript/src/hooks/useEventCallback.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +203 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native.d.ts +128 -0
- package/lib/typescript/src/native.d.ts.map +1 -0
- package/lib/typescript/src/store.d.ts +11 -0
- package/lib/typescript/src/store.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +174 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/wrap.d.ts +179 -0
- package/lib/typescript/src/wrap.d.ts.map +1 -0
- package/package.json +174 -0
- package/src/DefaultResolver.ts +36 -0
- package/src/NativeInstantpayCodePush.ts +111 -0
- package/src/checkForUpdate.ts +122 -0
- package/src/error.ts +159 -0
- package/src/fetchUpdateInfo.ts +47 -0
- package/src/global.d.ts +23 -0
- package/src/hooks/useEventCallback.ts +30 -0
- package/src/index.tsx +379 -0
- package/src/native.ts +280 -0
- package/src/store.ts +69 -0
- package/src/types.ts +227 -0
- package/src/wrap.tsx +384 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type NotifyAppReadyResult } from "./native";
|
|
3
|
+
import type { IpayCodePushResolver } from "./types";
|
|
4
|
+
import type { IpayCodePushError } from "./error";
|
|
5
|
+
export interface RunUpdateProcessResponse {
|
|
6
|
+
status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
|
|
7
|
+
shouldForceUpdate: boolean;
|
|
8
|
+
message: string | null;
|
|
9
|
+
id: string;
|
|
10
|
+
}
|
|
11
|
+
type UpdateStatus = "CHECK_FOR_UPDATE" | "UPDATING" | "UPDATE_PROCESS_COMPLETED";
|
|
12
|
+
/**
|
|
13
|
+
* Common options shared between auto and manual update modes
|
|
14
|
+
*/
|
|
15
|
+
interface CommonIpayCodePushOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Custom request headers for update checks
|
|
18
|
+
*/
|
|
19
|
+
requestHeaders?: Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Request timeout in milliseconds
|
|
22
|
+
* @default 5000
|
|
23
|
+
*/
|
|
24
|
+
requestTimeout?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Callback invoked when the app is ready and bundle verification completes.
|
|
27
|
+
* Provides information about bundle promotion, recovery from crashes, or stable state.
|
|
28
|
+
*
|
|
29
|
+
* @param result - Bundle state information
|
|
30
|
+
* @param result.status - Current bundle state:
|
|
31
|
+
* - "PROMOTED": Staging bundle was promoted to stable (new update applied)
|
|
32
|
+
* - "RECOVERED": App recovered from a crash, rollback occurred
|
|
33
|
+
* - "STABLE": No changes, bundle is stable
|
|
34
|
+
* @param result.crashedBundleId - Present only when status is "RECOVERED"
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* IpayCodePush.wrap({
|
|
39
|
+
* baseURL: "https://api.example.com",
|
|
40
|
+
* updateMode: "manual",
|
|
41
|
+
* onNotifyAppReady: ({ status, crashedBundleId }) => {
|
|
42
|
+
* if (status === "RECOVERED") {
|
|
43
|
+
* analytics.track('bundle_rollback', { crashedBundleId });
|
|
44
|
+
* } else if (status === "PROMOTED") {
|
|
45
|
+
* analytics.track('bundle_promoted');
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
* })(App);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
52
|
+
}
|
|
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: IpayCodePushResolver;
|
|
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 = CommonIpayCodePushOptions & NetworkConfig & {
|
|
85
|
+
/**
|
|
86
|
+
* Update strategy
|
|
87
|
+
* - "fingerprint": Use fingerprint hash to check for updates
|
|
88
|
+
* - "appVersion": Use app version to check for updates
|
|
89
|
+
*/
|
|
90
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
91
|
+
/**
|
|
92
|
+
* Update mode
|
|
93
|
+
* - "auto": Automatically check and download updates
|
|
94
|
+
*/
|
|
95
|
+
updateMode: "auto";
|
|
96
|
+
onError?: (error: IpayCodePushError | Error | unknown) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Component to show while downloading a new bundle update.
|
|
99
|
+
*
|
|
100
|
+
* When an update exists and the bundle is being downloaded, this component will block access
|
|
101
|
+
* to the entry point and show download progress.
|
|
102
|
+
*
|
|
103
|
+
*
|
|
104
|
+
* ```tsx
|
|
105
|
+
* IpayCodePush.wrap({
|
|
106
|
+
* baseURL: "<update-server-url>",
|
|
107
|
+
* updateStrategy: "appVersion",
|
|
108
|
+
* fallbackComponent: ({ progress = 0 }) => (
|
|
109
|
+
* <View style={styles.container}>
|
|
110
|
+
* <Text style={styles.text}>Updating... {progress}%</Text>
|
|
111
|
+
* </View>
|
|
112
|
+
* )
|
|
113
|
+
* })(App)
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* If not defined, the bundle will download in the background without blocking the screen.
|
|
117
|
+
*/
|
|
118
|
+
fallbackComponent?: React.FC<{
|
|
119
|
+
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
120
|
+
progress: number;
|
|
121
|
+
message: string | null;
|
|
122
|
+
}>;
|
|
123
|
+
onProgress?: (progress: number) => void;
|
|
124
|
+
/**
|
|
125
|
+
* When a force update exists, the app will automatically reload.
|
|
126
|
+
* If `false`, When a force update exists, the app will not reload. `shouldForceUpdate` will be returned as `true` in `onUpdateProcessCompleted`.
|
|
127
|
+
* If `true`, When a force update exists, the app will automatically reload.
|
|
128
|
+
* @default true
|
|
129
|
+
*/
|
|
130
|
+
reloadOnForceUpdate?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Callback function that is called when the update process is completed.
|
|
133
|
+
*
|
|
134
|
+
*/
|
|
135
|
+
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
136
|
+
};
|
|
137
|
+
export type ManualUpdateOptions = CommonIpayCodePushOptions & NetworkConfig & {
|
|
138
|
+
/**
|
|
139
|
+
* Update mode
|
|
140
|
+
* - "manual": Only notify app ready, user manually calls checkForUpdate()
|
|
141
|
+
*/
|
|
142
|
+
updateMode: "manual";
|
|
143
|
+
};
|
|
144
|
+
export type IpayCodePushOptions = AutoUpdateOptions | ManualUpdateOptions;
|
|
145
|
+
/**
|
|
146
|
+
* Internal options after normalization in index.ts
|
|
147
|
+
* Always has resolver (never baseURL)
|
|
148
|
+
*/
|
|
149
|
+
type InternalCommonOptions = {
|
|
150
|
+
resolver: IpayCodePushResolver;
|
|
151
|
+
requestHeaders?: Record<string, string>;
|
|
152
|
+
requestTimeout?: number;
|
|
153
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
154
|
+
};
|
|
155
|
+
type InternalAutoUpdateOptions = InternalCommonOptions & {
|
|
156
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
157
|
+
updateMode: "auto";
|
|
158
|
+
onError?: (error: IpayCodePushError | Error | unknown) => void;
|
|
159
|
+
fallbackComponent?: React.FC<{
|
|
160
|
+
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
161
|
+
progress: number;
|
|
162
|
+
message: string | null;
|
|
163
|
+
}>;
|
|
164
|
+
onProgress?: (progress: number) => void;
|
|
165
|
+
reloadOnForceUpdate?: boolean;
|
|
166
|
+
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
167
|
+
};
|
|
168
|
+
type InternalManualUpdateOptions = InternalCommonOptions & {
|
|
169
|
+
updateMode: "manual";
|
|
170
|
+
};
|
|
171
|
+
export type InternalWrapOptions = InternalAutoUpdateOptions | InternalManualUpdateOptions;
|
|
172
|
+
/**
|
|
173
|
+
* @usage IpayCodePushHOC.wrap({})(App); App Component or any other component
|
|
174
|
+
* @param options
|
|
175
|
+
* @returns
|
|
176
|
+
*/
|
|
177
|
+
export declare function wrap<T extends React.JSX.IntrinsicAttributes = object>(options: InternalWrapOptions): (WrappedComponent: React.ComponentType<T>) => React.ComponentType<T>;
|
|
178
|
+
export {};
|
|
179
|
+
//# sourceMappingURL=wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../../src/wrap.tsx"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-instantpay-code-push",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "React Native plugin for the CodePush service",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"*.podspec",
|
|
22
|
+
"react-native.config.js",
|
|
23
|
+
"!ios/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/gradle",
|
|
26
|
+
"!android/gradlew",
|
|
27
|
+
"!android/gradlew.bat",
|
|
28
|
+
"!android/local.properties",
|
|
29
|
+
"!**/__tests__",
|
|
30
|
+
"!**/__fixtures__",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/.*"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"example": "yarn workspace react-native-instantpay-code-push-example",
|
|
36
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
37
|
+
"prepare": "bob build",
|
|
38
|
+
"typecheck": "tsc",
|
|
39
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
40
|
+
"test": "jest",
|
|
41
|
+
"release": "release-it --only-version",
|
|
42
|
+
"generateBundle" : "ruby generateBundle.rb --projectDir /var/www/npm-package/react-native-instantpay-code-push/example --platform ANDROID --enableHermes true"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"react-native",
|
|
46
|
+
"ios",
|
|
47
|
+
"android"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/InstantPay/react-native-instantpay-code-push.git"
|
|
52
|
+
},
|
|
53
|
+
"author": "Dhananjay Kumar <dhananjayspeed@gmail.com> (https://github.com/InstantPay)",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/InstantPay/react-native-instantpay-code-push/issues"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/InstantPay/react-native-instantpay-code-push#readme",
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"registry": "https://registry.npmjs.org/"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
64
|
+
"@eslint/compat": "^1.3.2",
|
|
65
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
66
|
+
"@eslint/js": "^9.35.0",
|
|
67
|
+
"@react-native/babel-preset": "0.83.0",
|
|
68
|
+
"@react-native/eslint-config": "0.83.0",
|
|
69
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
70
|
+
"@types/jest": "^29.5.14",
|
|
71
|
+
"@types/react": "^19.2.0",
|
|
72
|
+
"@types/use-sync-external-store": "^1.5.0",
|
|
73
|
+
"commitlint": "^19.8.1",
|
|
74
|
+
"del-cli": "^6.0.0",
|
|
75
|
+
"eslint": "^9.35.0",
|
|
76
|
+
"eslint-config-prettier": "^10.1.8",
|
|
77
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
78
|
+
"jest": "^29.7.0",
|
|
79
|
+
"lefthook": "^2.0.3",
|
|
80
|
+
"prettier": "^3.0.4",
|
|
81
|
+
"react": "19.2.0",
|
|
82
|
+
"react-native": "0.83.0",
|
|
83
|
+
"react-native-builder-bob": "^0.40.17",
|
|
84
|
+
"release-it": "^19.0.4",
|
|
85
|
+
"turbo": "^2.5.6",
|
|
86
|
+
"typescript": "^5.9.2"
|
|
87
|
+
},
|
|
88
|
+
"peerDependencies": {
|
|
89
|
+
"react": "*",
|
|
90
|
+
"react-native": "*"
|
|
91
|
+
},
|
|
92
|
+
"workspaces": [
|
|
93
|
+
"example"
|
|
94
|
+
],
|
|
95
|
+
"packageManager": "yarn@4.11.0",
|
|
96
|
+
"react-native-builder-bob": {
|
|
97
|
+
"source": "src",
|
|
98
|
+
"output": "lib",
|
|
99
|
+
"targets": [
|
|
100
|
+
[
|
|
101
|
+
"module",
|
|
102
|
+
{
|
|
103
|
+
"esm": true
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"typescript",
|
|
108
|
+
{
|
|
109
|
+
"project": "tsconfig.build.json"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"codegenConfig": {
|
|
115
|
+
"name": "InstantpayCodePushSpec",
|
|
116
|
+
"type": "modules",
|
|
117
|
+
"jsSrcsDir": "src",
|
|
118
|
+
"android": {
|
|
119
|
+
"javaPackageName": "com.instantpaycodepush"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"prettier": {
|
|
123
|
+
"quoteProps": "consistent",
|
|
124
|
+
"singleQuote": true,
|
|
125
|
+
"tabWidth": 2,
|
|
126
|
+
"trailingComma": "es5",
|
|
127
|
+
"useTabs": false
|
|
128
|
+
},
|
|
129
|
+
"jest": {
|
|
130
|
+
"preset": "react-native",
|
|
131
|
+
"modulePathIgnorePatterns": [
|
|
132
|
+
"<rootDir>/example/node_modules",
|
|
133
|
+
"<rootDir>/lib/"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"commitlint": {
|
|
137
|
+
"extends": [
|
|
138
|
+
"@commitlint/config-conventional"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"release-it": {
|
|
142
|
+
"git": {
|
|
143
|
+
"commitMessage": "chore: release ${version}",
|
|
144
|
+
"tagName": "v${version}"
|
|
145
|
+
},
|
|
146
|
+
"npm": {
|
|
147
|
+
"publish": true
|
|
148
|
+
},
|
|
149
|
+
"github": {
|
|
150
|
+
"release": true
|
|
151
|
+
},
|
|
152
|
+
"plugins": {
|
|
153
|
+
"@release-it/conventional-changelog": {
|
|
154
|
+
"preset": {
|
|
155
|
+
"name": "angular"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"create-react-native-library": {
|
|
161
|
+
"type": "turbo-module",
|
|
162
|
+
"languages": "kotlin-objc",
|
|
163
|
+
"tools": [
|
|
164
|
+
"eslint",
|
|
165
|
+
"jest",
|
|
166
|
+
"lefthook",
|
|
167
|
+
"release-it"
|
|
168
|
+
],
|
|
169
|
+
"version": "0.56.0"
|
|
170
|
+
},
|
|
171
|
+
"dependencies": {
|
|
172
|
+
"use-sync-external-store": "1.6.0"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AppUpdateInfo } from "./types";
|
|
2
|
+
import { fetchUpdateInfo } from "./fetchUpdateInfo";
|
|
3
|
+
import type { IpayCodePushResolver, 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): IpayCodePushResolver {
|
|
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
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export interface UpdateBundleParams {
|
|
5
|
+
bundleId: string;
|
|
6
|
+
fileUrl: string | null;
|
|
7
|
+
/**
|
|
8
|
+
* File hash for integrity/signature verification.
|
|
9
|
+
*
|
|
10
|
+
* Format depends on signing configuration:
|
|
11
|
+
* - Signed: `sig:<base64_signature>` - Native will verify signature (and implicitly hash)
|
|
12
|
+
* - Unsigned: `<hex_hash>` - Native will verify SHA256 hash only
|
|
13
|
+
*
|
|
14
|
+
* Native determines verification mode by checking for "sig:" prefix.
|
|
15
|
+
*/
|
|
16
|
+
fileHash: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Spec extends TurboModule {
|
|
20
|
+
|
|
21
|
+
// Methods
|
|
22
|
+
reload(): Promise<void>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Downloads and applies a bundle update.
|
|
26
|
+
*
|
|
27
|
+
* @param params - Update bundle parameters
|
|
28
|
+
* @returns Promise that resolves to true if successful
|
|
29
|
+
* @throws {IpayCodePushErrorCode} Rejects with one of the following error codes:
|
|
30
|
+
*
|
|
31
|
+
* Parameter validation:
|
|
32
|
+
* - MISSING_BUNDLE_ID: Missing or empty bundleId
|
|
33
|
+
* - INVALID_FILE_URL: Invalid fileUrl provided
|
|
34
|
+
*
|
|
35
|
+
* Bundle storage:
|
|
36
|
+
* - DIRECTORY_CREATION_FAILED: Failed to create bundle directory
|
|
37
|
+
* - DOWNLOAD_FAILED: Failed to download bundle
|
|
38
|
+
* - INCOMPLETE_DOWNLOAD: Download incomplete (size mismatch)
|
|
39
|
+
* - EXTRACTION_FORMAT_ERROR: Invalid or corrupted archive format
|
|
40
|
+
* - INVALID_BUNDLE: Bundle missing required platform files
|
|
41
|
+
* - INSUFFICIENT_DISK_SPACE: Insufficient disk space
|
|
42
|
+
* - MOVE_OPERATION_FAILED: Failed to move bundle files
|
|
43
|
+
* - BUNDLE_IN_CRASHED_HISTORY: Bundle was previously marked as crashed
|
|
44
|
+
*
|
|
45
|
+
* Signature:
|
|
46
|
+
* - SIGNATURE_VERIFICATION_FAILED: Any signature/hash verification failure
|
|
47
|
+
*
|
|
48
|
+
* Internal:
|
|
49
|
+
* - SELF_DEALLOCATED: Native object was deallocated (iOS)
|
|
50
|
+
* - UNKNOWN_ERROR: Fallback for rare or platform-specific errors
|
|
51
|
+
*
|
|
52
|
+
* Note: iOS normalizes rare signature/storage errors to SIGNATURE_VERIFICATION_FAILED
|
|
53
|
+
* or UNKNOWN_ERROR to keep the JS error surface small.
|
|
54
|
+
*/
|
|
55
|
+
updateBundle(params: UpdateBundleParams): Promise<boolean>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Notifies the native side that the app has successfully started with the given bundle.
|
|
59
|
+
* If the bundle matches the staging bundle, it promotes to stable.
|
|
60
|
+
*
|
|
61
|
+
* @param params - Parameters containing the bundle ID
|
|
62
|
+
* @returns Object with status and optional crashedBundleId
|
|
63
|
+
* - `status: "PROMOTED"` - Staging bundle was promoted to stable (ACTIVE event)
|
|
64
|
+
* - `status: "RECOVERED"` - App recovered from crash, rollback occurred (ROLLBACK event)
|
|
65
|
+
* - `status: "STABLE"` - No changes, already stable
|
|
66
|
+
* - `crashedBundleId` - Present only when status is "RECOVERED"
|
|
67
|
+
*/
|
|
68
|
+
notifyAppReady(params: { bundleId: string }): {
|
|
69
|
+
status: "PROMOTED" | "RECOVERED" | "STABLE";
|
|
70
|
+
crashedBundleId?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets the list of bundle IDs that have been marked as crashed.
|
|
75
|
+
* These bundles will be rejected if attempted to install again.
|
|
76
|
+
*
|
|
77
|
+
* @returns Array of crashed bundle IDs
|
|
78
|
+
*/
|
|
79
|
+
getCrashHistory(): string[];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Clears the crashed bundle history, allowing previously crashed bundles
|
|
83
|
+
* to be installed again.
|
|
84
|
+
*
|
|
85
|
+
* @returns true if clearing was successful
|
|
86
|
+
*/
|
|
87
|
+
clearCrashHistory(): boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Gets the base URL for the current active bundle directory.
|
|
91
|
+
* Returns the file:// URL to the bundle directory without trailing slash.
|
|
92
|
+
* This is used for Expo DOM components to construct full asset paths.
|
|
93
|
+
*
|
|
94
|
+
* @returns Base URL string (e.g., "file:///data/.../bundle-store/abc123") or "" if not available
|
|
95
|
+
*/
|
|
96
|
+
getBaseURL: () => string;
|
|
97
|
+
|
|
98
|
+
// EventEmitter
|
|
99
|
+
addListener(eventName: string): void;
|
|
100
|
+
|
|
101
|
+
removeListeners(count: number): void;
|
|
102
|
+
|
|
103
|
+
readonly getConstants: () => {
|
|
104
|
+
MIN_BUNDLE_ID: string;
|
|
105
|
+
APP_VERSION: string | null;
|
|
106
|
+
CHANNEL: string;
|
|
107
|
+
FINGERPRINT_HASH: string | null;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('InstantpayCodePush');
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Platform } from "react-native";
|
|
2
|
+
import { IpayCodePushError } from "./error";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getAppVersion,
|
|
6
|
+
getBundleId,
|
|
7
|
+
getChannel,
|
|
8
|
+
getFingerprintHash,
|
|
9
|
+
getMinBundleId,
|
|
10
|
+
updateBundle,
|
|
11
|
+
} from "./native";
|
|
12
|
+
|
|
13
|
+
import type { IpayCodePushResolver, AppUpdateInfo } from "./types";
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export interface CheckForUpdateOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Update strategy
|
|
19
|
+
* - "fingerprint": Use fingerprint hash to check for updates
|
|
20
|
+
* - "appVersion": Use app version to check for updates
|
|
21
|
+
* - Can override the strategy set in IpayCodePush.wrap()
|
|
22
|
+
*/
|
|
23
|
+
updateStrategy: "appVersion" | "fingerprint";
|
|
24
|
+
|
|
25
|
+
requestHeaders?: Record<string, string>;
|
|
26
|
+
|
|
27
|
+
onError?: (error: Error) => void;
|
|
28
|
+
/**
|
|
29
|
+
* The timeout duration for the request.
|
|
30
|
+
* @default 5000
|
|
31
|
+
*/
|
|
32
|
+
requestTimeout?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type CheckForUpdateResult = AppUpdateInfo & {
|
|
36
|
+
/**
|
|
37
|
+
* Updates the bundle.
|
|
38
|
+
* This method is equivalent to `IpayCodePush.updateBundle()` but with all required arguments pre-filled.
|
|
39
|
+
*/
|
|
40
|
+
updateBundle: () => Promise<boolean>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Internal type that includes resolver for use within index.ts
|
|
44
|
+
export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
|
|
45
|
+
resolver: IpayCodePushResolver;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function checkForUpdate(
|
|
49
|
+
options: InternalCheckForUpdateOptions,
|
|
50
|
+
): Promise<CheckForUpdateResult | null> {
|
|
51
|
+
if (__DEV__) {
|
|
52
|
+
console.log('checkForUpdate : Running in dev mode')
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if(Platform.OS == 'ios'){
|
|
57
|
+
console.log("Under Development in iOS Platform");
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!["ios", "android"].includes(Platform.OS)) {
|
|
62
|
+
options.onError?.(
|
|
63
|
+
new IpayCodePushError("IpayCodePush is only supported on iOS and Android"),
|
|
64
|
+
);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const currentAppVersion = getAppVersion();
|
|
69
|
+
const platform = Platform.OS as "ios" | "android";
|
|
70
|
+
const currentBundleId = getBundleId();
|
|
71
|
+
const minBundleId = getMinBundleId();
|
|
72
|
+
const channel = getChannel();
|
|
73
|
+
|
|
74
|
+
if (!currentAppVersion) {
|
|
75
|
+
options.onError?.(new IpayCodePushError("Failed to get app version"));
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fingerprintHash = getFingerprintHash();
|
|
80
|
+
|
|
81
|
+
if (!options.resolver?.checkUpdate) {
|
|
82
|
+
options.onError?.(
|
|
83
|
+
new IpayCodePushError("Resolver is required but not configured"),
|
|
84
|
+
);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let updateInfo: AppUpdateInfo | null = null;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
updateInfo = await options.resolver.checkUpdate({
|
|
92
|
+
platform,
|
|
93
|
+
appVersion: currentAppVersion,
|
|
94
|
+
bundleId: currentBundleId,
|
|
95
|
+
minBundleId,
|
|
96
|
+
channel,
|
|
97
|
+
updateStrategy: options.updateStrategy,
|
|
98
|
+
fingerprintHash,
|
|
99
|
+
requestHeaders: options.requestHeaders,
|
|
100
|
+
requestTimeout: options.requestTimeout,
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
options.onError?.(error as Error);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!updateInfo) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
...updateInfo,
|
|
113
|
+
updateBundle: async () => {
|
|
114
|
+
return updateBundle({
|
|
115
|
+
bundleId: updateInfo.id,
|
|
116
|
+
fileUrl: updateInfo.fileUrl,
|
|
117
|
+
fileHash: updateInfo.fileHash,
|
|
118
|
+
status: updateInfo.status,
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|