@swiftpatch/react-native 2.0.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/README.md +430 -0
- package/android/build.gradle +105 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/swiftpatch/BundleManager.kt +107 -0
- package/android/src/main/java/com/swiftpatch/CrashDetector.kt +79 -0
- package/android/src/main/java/com/swiftpatch/CryptoVerifier.kt +69 -0
- package/android/src/main/java/com/swiftpatch/DownloadManager.kt +120 -0
- package/android/src/main/java/com/swiftpatch/EventQueue.kt +86 -0
- package/android/src/main/java/com/swiftpatch/FileUtils.kt +60 -0
- package/android/src/main/java/com/swiftpatch/PatchApplier.kt +60 -0
- package/android/src/main/java/com/swiftpatch/SignalCrashHandler.kt +84 -0
- package/android/src/main/java/com/swiftpatch/SlotManager.kt +299 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchModule.kt +630 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchPackage.kt +21 -0
- package/android/src/main/jni/CMakeLists.txt +12 -0
- package/android/src/main/jni/bspatch.c +188 -0
- package/android/src/main/jni/bspatch.h +57 -0
- package/android/src/main/jni/bspatch_jni.c +28 -0
- package/ios/Libraries/bspatch/bspatch.c +188 -0
- package/ios/Libraries/bspatch/bspatch.h +50 -0
- package/ios/Libraries/bspatch/module.modulemap +4 -0
- package/ios/SwiftPatch/BundleManager.swift +113 -0
- package/ios/SwiftPatch/CrashDetector.swift +71 -0
- package/ios/SwiftPatch/CryptoVerifier.swift +70 -0
- package/ios/SwiftPatch/DownloadManager.swift +125 -0
- package/ios/SwiftPatch/EventQueue.swift +116 -0
- package/ios/SwiftPatch/FileUtils.swift +38 -0
- package/ios/SwiftPatch/PatchApplier.swift +41 -0
- package/ios/SwiftPatch/SignalCrashHandler.swift +129 -0
- package/ios/SwiftPatch/SlotManager.swift +360 -0
- package/ios/SwiftPatch/SwiftPatchModule.m +56 -0
- package/ios/SwiftPatch/SwiftPatchModule.swift +621 -0
- package/lib/commonjs/SwiftPatchCore.js +140 -0
- package/lib/commonjs/SwiftPatchCore.js.map +1 -0
- package/lib/commonjs/SwiftPatchProvider.js +617 -0
- package/lib/commonjs/SwiftPatchProvider.js.map +1 -0
- package/lib/commonjs/constants.js +50 -0
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/core/Downloader.js +63 -0
- package/lib/commonjs/core/Downloader.js.map +1 -0
- package/lib/commonjs/core/Installer.js +46 -0
- package/lib/commonjs/core/Installer.js.map +1 -0
- package/lib/commonjs/core/Rollback.js +36 -0
- package/lib/commonjs/core/Rollback.js.map +1 -0
- package/lib/commonjs/core/UpdateChecker.js +57 -0
- package/lib/commonjs/core/UpdateChecker.js.map +1 -0
- package/lib/commonjs/core/Verifier.js +82 -0
- package/lib/commonjs/core/Verifier.js.map +1 -0
- package/lib/commonjs/core/index.js +41 -0
- package/lib/commonjs/core/index.js.map +1 -0
- package/lib/commonjs/index.js +154 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/modal/SwiftPatchModal.js +667 -0
- package/lib/commonjs/modal/SwiftPatchModal.js.map +1 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js +26 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatch.js +85 -0
- package/lib/commonjs/native/NativeSwiftPatch.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js +15 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +126 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/useSwiftPatch.js +31 -0
- package/lib/commonjs/useSwiftPatch.js.map +1 -0
- package/lib/commonjs/utils/api.js +206 -0
- package/lib/commonjs/utils/api.js.map +1 -0
- package/lib/commonjs/utils/device.js +23 -0
- package/lib/commonjs/utils/device.js.map +1 -0
- package/lib/commonjs/utils/logger.js +30 -0
- package/lib/commonjs/utils/logger.js.map +1 -0
- package/lib/commonjs/utils/storage.js +31 -0
- package/lib/commonjs/utils/storage.js.map +1 -0
- package/lib/commonjs/withSwiftPatch.js +42 -0
- package/lib/commonjs/withSwiftPatch.js.map +1 -0
- package/lib/module/SwiftPatchCore.js +135 -0
- package/lib/module/SwiftPatchCore.js.map +1 -0
- package/lib/module/SwiftPatchProvider.js +611 -0
- package/lib/module/SwiftPatchProvider.js.map +1 -0
- package/lib/module/constants.js +46 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/core/Downloader.js +57 -0
- package/lib/module/core/Downloader.js.map +1 -0
- package/lib/module/core/Installer.js +41 -0
- package/lib/module/core/Installer.js.map +1 -0
- package/lib/module/core/Rollback.js +31 -0
- package/lib/module/core/Rollback.js.map +1 -0
- package/lib/module/core/UpdateChecker.js +51 -0
- package/lib/module/core/UpdateChecker.js.map +1 -0
- package/lib/module/core/Verifier.js +76 -0
- package/lib/module/core/Verifier.js.map +1 -0
- package/lib/module/core/index.js +8 -0
- package/lib/module/core/index.js.map +1 -0
- package/lib/module/index.js +34 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/modal/SwiftPatchModal.js +661 -0
- package/lib/module/modal/SwiftPatchModal.js.map +1 -0
- package/lib/module/modal/useSwiftPatchModal.js +22 -0
- package/lib/module/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/module/native/NativeSwiftPatch.js +78 -0
- package/lib/module/native/NativeSwiftPatch.js.map +1 -0
- package/lib/module/native/NativeSwiftPatchSpec.js +12 -0
- package/lib/module/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/module/types.js +139 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useSwiftPatch.js +26 -0
- package/lib/module/useSwiftPatch.js.map +1 -0
- package/lib/module/utils/api.js +197 -0
- package/lib/module/utils/api.js.map +1 -0
- package/lib/module/utils/device.js +18 -0
- package/lib/module/utils/device.js.map +1 -0
- package/lib/module/utils/logger.js +26 -0
- package/lib/module/utils/logger.js.map +1 -0
- package/lib/module/utils/storage.js +24 -0
- package/lib/module/utils/storage.js.map +1 -0
- package/lib/module/withSwiftPatch.js +37 -0
- package/lib/module/withSwiftPatch.js.map +1 -0
- package/lib/typescript/SwiftPatchCore.d.ts +64 -0
- package/lib/typescript/SwiftPatchCore.d.ts.map +1 -0
- package/lib/typescript/SwiftPatchProvider.d.ts +22 -0
- package/lib/typescript/SwiftPatchProvider.d.ts.map +1 -0
- package/lib/typescript/constants.d.ts +33 -0
- package/lib/typescript/constants.d.ts.map +1 -0
- package/lib/typescript/core/Downloader.d.ts +34 -0
- package/lib/typescript/core/Downloader.d.ts.map +1 -0
- package/lib/typescript/core/Installer.d.ts +25 -0
- package/lib/typescript/core/Installer.d.ts.map +1 -0
- package/lib/typescript/core/Rollback.d.ts +18 -0
- package/lib/typescript/core/Rollback.d.ts.map +1 -0
- package/lib/typescript/core/UpdateChecker.d.ts +27 -0
- package/lib/typescript/core/UpdateChecker.d.ts.map +1 -0
- package/lib/typescript/core/Verifier.d.ts +31 -0
- package/lib/typescript/core/Verifier.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +8 -0
- package/lib/typescript/core/index.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +13 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts +11 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts +7 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts +61 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts +67 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +266 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/useSwiftPatch.d.ts +12 -0
- package/lib/typescript/useSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/utils/api.d.ts +87 -0
- package/lib/typescript/utils/api.d.ts.map +1 -0
- package/lib/typescript/utils/device.d.ts +9 -0
- package/lib/typescript/utils/device.d.ts.map +1 -0
- package/lib/typescript/utils/logger.d.ts +8 -0
- package/lib/typescript/utils/logger.d.ts.map +1 -0
- package/lib/typescript/utils/storage.d.ts +14 -0
- package/lib/typescript/utils/storage.d.ts.map +1 -0
- package/lib/typescript/withSwiftPatch.d.ts +12 -0
- package/lib/typescript/withSwiftPatch.d.ts.map +1 -0
- package/package.json +99 -0
- package/react-native-swiftpatch.podspec +50 -0
- package/src/SwiftPatchCore.ts +148 -0
- package/src/SwiftPatchProvider.tsx +514 -0
- package/src/constants.ts +49 -0
- package/src/core/Downloader.ts +74 -0
- package/src/core/Installer.ts +38 -0
- package/src/core/Rollback.ts +28 -0
- package/src/core/UpdateChecker.ts +70 -0
- package/src/core/Verifier.ts +92 -0
- package/src/core/index.ts +11 -0
- package/src/index.ts +64 -0
- package/src/modal/SwiftPatchModal.tsx +657 -0
- package/src/modal/useSwiftPatchModal.ts +24 -0
- package/src/native/NativeSwiftPatch.ts +205 -0
- package/src/native/NativeSwiftPatchSpec.ts +139 -0
- package/src/types.ts +336 -0
- package/src/useSwiftPatch.ts +29 -0
- package/src/utils/api.ts +244 -0
- package/src/utils/device.ts +15 -0
- package/src/utils/logger.ts +29 -0
- package/src/utils/storage.ts +23 -0
- package/src/withSwiftPatch.tsx +41 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
|
|
2
|
+
import type { BundleInfo, DownloadProgress, SlotMetadata } from '../types';
|
|
3
|
+
import { NativeEvents } from '../constants';
|
|
4
|
+
|
|
5
|
+
const LINKING_ERROR =
|
|
6
|
+
`The package '@swiftpatch/react-native' doesn't seem to be linked. Make sure: \n\n` +
|
|
7
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
8
|
+
'- You rebuilt the app after installing the package\n' +
|
|
9
|
+
'- You are not using Expo Go\n';
|
|
10
|
+
|
|
11
|
+
// Try to get TurboModule first (New Architecture), fallback to legacy
|
|
12
|
+
let SwiftPatchNative: any;
|
|
13
|
+
let isTurboModuleEnabled = false;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// @ts-ignore - TurboModuleRegistry is not typed
|
|
17
|
+
const TurboModuleRegistry = require('react-native').TurboModuleRegistry;
|
|
18
|
+
if (TurboModuleRegistry) {
|
|
19
|
+
SwiftPatchNative = TurboModuleRegistry.get('SwiftPatch');
|
|
20
|
+
if (SwiftPatchNative) {
|
|
21
|
+
isTurboModuleEnabled = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
// TurboModuleRegistry not available, will use legacy
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fallback to legacy NativeModules if TurboModule not available
|
|
29
|
+
if (!SwiftPatchNative) {
|
|
30
|
+
SwiftPatchNative = NativeModules.SwiftPatch
|
|
31
|
+
? NativeModules.SwiftPatch
|
|
32
|
+
: new Proxy(
|
|
33
|
+
{},
|
|
34
|
+
{
|
|
35
|
+
get() {
|
|
36
|
+
throw new Error(LINKING_ERROR);
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Event emitter for native events
|
|
43
|
+
export const swiftPatchEmitter = new NativeEventEmitter(SwiftPatchNative);
|
|
44
|
+
|
|
45
|
+
// Export whether TurboModule is enabled for debugging
|
|
46
|
+
export const IS_TURBO_MODULE_ENABLED = isTurboModuleEnabled;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Native module interface
|
|
50
|
+
*/
|
|
51
|
+
export interface NativeSwiftPatchModule {
|
|
52
|
+
// ─── Initialization ────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
initialize(config: {
|
|
55
|
+
deploymentKey: string;
|
|
56
|
+
serverUrl: string;
|
|
57
|
+
publicKey: string | null;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
|
|
60
|
+
getJSBundleFile(): string | null;
|
|
61
|
+
|
|
62
|
+
setDebugMode(enabled: boolean): void;
|
|
63
|
+
|
|
64
|
+
// ─── Mount State (crash detection) ─────────────────────────
|
|
65
|
+
|
|
66
|
+
markMounted(): Promise<void>;
|
|
67
|
+
|
|
68
|
+
markUnmounted(): Promise<void>;
|
|
69
|
+
|
|
70
|
+
// ─── Bundle Information ────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
getCurrentBundle(): Promise<BundleInfo | null>;
|
|
73
|
+
|
|
74
|
+
getSlotMetadata(): Promise<SlotMetadata>;
|
|
75
|
+
|
|
76
|
+
getDeviceId(): Promise<string>;
|
|
77
|
+
|
|
78
|
+
getAppVersion(): Promise<string>;
|
|
79
|
+
|
|
80
|
+
// ─── Environment ───────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
switchEnvironment(environment: string): Promise<SlotMetadata>;
|
|
83
|
+
|
|
84
|
+
// ─── Download & Install ────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
downloadUpdate(
|
|
87
|
+
url: string,
|
|
88
|
+
expectedHash: string,
|
|
89
|
+
isPatch: boolean,
|
|
90
|
+
signature: string | null
|
|
91
|
+
): Promise<void>;
|
|
92
|
+
|
|
93
|
+
downloadStageBundle(url: string, expectedHash: string): Promise<void>;
|
|
94
|
+
|
|
95
|
+
installUpdate(bundleHash: string): Promise<void>;
|
|
96
|
+
|
|
97
|
+
hasPendingInstall(): Promise<boolean>;
|
|
98
|
+
|
|
99
|
+
confirmInstall(): Promise<void>;
|
|
100
|
+
|
|
101
|
+
// ─── Stabilization ─────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
stabilize(): Promise<SlotMetadata>;
|
|
104
|
+
|
|
105
|
+
// ─── Restart & Rollback ────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
restart(): void;
|
|
108
|
+
|
|
109
|
+
rollback(): Promise<string>;
|
|
110
|
+
|
|
111
|
+
clearPendingUpdate(): Promise<void>;
|
|
112
|
+
|
|
113
|
+
// ─── Event Queue ───────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
popEvents(): Promise<Array<Record<string, unknown>>>;
|
|
116
|
+
|
|
117
|
+
acknowledgeEvents(eventIdsJson: string): Promise<void>;
|
|
118
|
+
|
|
119
|
+
// ─── Launch Tracking ───────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
recordSuccessfulLaunch(bundleHash: string): Promise<number>;
|
|
122
|
+
|
|
123
|
+
getLaunchCount(bundleHash: string): Promise<number>;
|
|
124
|
+
|
|
125
|
+
// ─── Status Reporting ──────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
reportStatus(
|
|
128
|
+
releaseId: string,
|
|
129
|
+
status: 'downloaded' | 'installed' | 'crashed' | 'rolledBack'
|
|
130
|
+
): Promise<void>;
|
|
131
|
+
|
|
132
|
+
// ─── Legacy / Advanced ─────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
computeHash(filePath: string): Promise<string>;
|
|
135
|
+
|
|
136
|
+
applyPatch(
|
|
137
|
+
currentBundlePath: string,
|
|
138
|
+
patchPath: string,
|
|
139
|
+
outputPath: string
|
|
140
|
+
): Promise<string>;
|
|
141
|
+
|
|
142
|
+
verifySignature(
|
|
143
|
+
data: string,
|
|
144
|
+
signature: string,
|
|
145
|
+
publicKey: string
|
|
146
|
+
): Promise<boolean>;
|
|
147
|
+
|
|
148
|
+
cancelDownload(): Promise<void>;
|
|
149
|
+
|
|
150
|
+
getDownloadedBundlePath(bundleHash: string): Promise<string>;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default SwiftPatchNative as NativeSwiftPatchModule;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Subscribe to download progress events
|
|
157
|
+
*/
|
|
158
|
+
export function onDownloadProgress(
|
|
159
|
+
callback: (progress: DownloadProgress) => void
|
|
160
|
+
): () => void {
|
|
161
|
+
const subscription = swiftPatchEmitter.addListener(
|
|
162
|
+
NativeEvents.DOWNLOAD_PROGRESS,
|
|
163
|
+
callback
|
|
164
|
+
);
|
|
165
|
+
return () => subscription.remove();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Subscribe to rollback events
|
|
170
|
+
*/
|
|
171
|
+
export function onRollback(
|
|
172
|
+
callback: (reason: string) => void
|
|
173
|
+
): () => void {
|
|
174
|
+
const subscription = swiftPatchEmitter.addListener(
|
|
175
|
+
NativeEvents.ROLLBACK_OCCURRED,
|
|
176
|
+
(event: { reason: string }) => callback(event.reason)
|
|
177
|
+
);
|
|
178
|
+
return () => subscription.remove();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Subscribe to version change events
|
|
183
|
+
*/
|
|
184
|
+
export function onVersionChanged(
|
|
185
|
+
callback: (reason: string) => void
|
|
186
|
+
): () => void {
|
|
187
|
+
const subscription = swiftPatchEmitter.addListener(
|
|
188
|
+
NativeEvents.VERSION_CHANGED,
|
|
189
|
+
(event: { reason: string }) => callback(event.reason)
|
|
190
|
+
);
|
|
191
|
+
return () => subscription.remove();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Subscribe to native SDK events
|
|
196
|
+
*/
|
|
197
|
+
export function onNativeEvent(
|
|
198
|
+
callback: (event: Record<string, unknown>) => void
|
|
199
|
+
): () => void {
|
|
200
|
+
const subscription = swiftPatchEmitter.addListener(
|
|
201
|
+
NativeEvents.NATIVE_EVENT,
|
|
202
|
+
callback
|
|
203
|
+
);
|
|
204
|
+
return () => subscription.remove();
|
|
205
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TurboModule Spec for SwiftPatch
|
|
3
|
+
*
|
|
4
|
+
* This file defines the interface for the New Architecture TurboModule.
|
|
5
|
+
* Codegen will use this to generate native code for iOS and Android.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TurboModule } from 'react-native';
|
|
9
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
10
|
+
|
|
11
|
+
export interface BundleInfo {
|
|
12
|
+
hash: string;
|
|
13
|
+
version: string;
|
|
14
|
+
installedAt: string;
|
|
15
|
+
isOriginal: boolean;
|
|
16
|
+
slot: string;
|
|
17
|
+
environment: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SlotMetadata {
|
|
21
|
+
environment: string;
|
|
22
|
+
prod: {
|
|
23
|
+
currentSlot: string;
|
|
24
|
+
newHash: string | null;
|
|
25
|
+
stableHash: string | null;
|
|
26
|
+
tempHash: string | null;
|
|
27
|
+
};
|
|
28
|
+
stage: {
|
|
29
|
+
newHash: string | null;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Spec extends TurboModule {
|
|
34
|
+
// ─── Initialization ────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
initialize(config: {
|
|
37
|
+
deploymentKey: string;
|
|
38
|
+
serverUrl: string;
|
|
39
|
+
publicKey: string | null;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
|
|
42
|
+
getJSBundleFile(): string | null;
|
|
43
|
+
|
|
44
|
+
setDebugMode(enabled: boolean): void;
|
|
45
|
+
|
|
46
|
+
// ─── Mount State (crash detection) ─────────────────────────
|
|
47
|
+
|
|
48
|
+
markMounted(): Promise<void>;
|
|
49
|
+
|
|
50
|
+
markUnmounted(): Promise<void>;
|
|
51
|
+
|
|
52
|
+
// ─── Bundle Information ────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
getCurrentBundle(): Promise<BundleInfo | null>;
|
|
55
|
+
|
|
56
|
+
getSlotMetadata(): Promise<SlotMetadata>;
|
|
57
|
+
|
|
58
|
+
getDeviceId(): Promise<string>;
|
|
59
|
+
|
|
60
|
+
getAppVersion(): Promise<string>;
|
|
61
|
+
|
|
62
|
+
// ─── Environment ───────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
switchEnvironment(environment: string): Promise<SlotMetadata>;
|
|
65
|
+
|
|
66
|
+
// ─── Download & Install ────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
downloadUpdate(
|
|
69
|
+
url: string,
|
|
70
|
+
expectedHash: string,
|
|
71
|
+
isPatch: boolean,
|
|
72
|
+
signature: string | null
|
|
73
|
+
): Promise<void>;
|
|
74
|
+
|
|
75
|
+
downloadStageBundle(url: string, expectedHash: string): Promise<void>;
|
|
76
|
+
|
|
77
|
+
installUpdate(bundleHash: string): Promise<void>;
|
|
78
|
+
|
|
79
|
+
hasPendingInstall(): Promise<boolean>;
|
|
80
|
+
|
|
81
|
+
confirmInstall(): Promise<void>;
|
|
82
|
+
|
|
83
|
+
// ─── Stabilization ─────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
stabilize(): Promise<SlotMetadata>;
|
|
86
|
+
|
|
87
|
+
// ─── Restart & Rollback ────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
restart(): void;
|
|
90
|
+
|
|
91
|
+
rollback(): Promise<string>;
|
|
92
|
+
|
|
93
|
+
clearPendingUpdate(): Promise<void>;
|
|
94
|
+
|
|
95
|
+
// ─── Event Queue ───────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
popEvents(): Promise<Array<Record<string, unknown>>>;
|
|
98
|
+
|
|
99
|
+
acknowledgeEvents(eventIdsJson: string): Promise<void>;
|
|
100
|
+
|
|
101
|
+
// ─── Launch Tracking ───────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
recordSuccessfulLaunch(bundleHash: string): Promise<number>;
|
|
104
|
+
|
|
105
|
+
getLaunchCount(bundleHash: string): Promise<number>;
|
|
106
|
+
|
|
107
|
+
// ─── Status Reporting ──────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
reportStatus(
|
|
110
|
+
releaseId: string,
|
|
111
|
+
status: 'downloaded' | 'installed' | 'crashed' | 'rolledBack'
|
|
112
|
+
): Promise<void>;
|
|
113
|
+
|
|
114
|
+
// ─── Legacy / Advanced ─────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
computeHash(filePath: string): Promise<string>;
|
|
117
|
+
|
|
118
|
+
applyPatch(
|
|
119
|
+
currentBundlePath: string,
|
|
120
|
+
patchPath: string,
|
|
121
|
+
outputPath: string
|
|
122
|
+
): Promise<string>;
|
|
123
|
+
|
|
124
|
+
verifySignature(
|
|
125
|
+
data: string,
|
|
126
|
+
signature: string,
|
|
127
|
+
publicKey: string
|
|
128
|
+
): Promise<boolean>;
|
|
129
|
+
|
|
130
|
+
cancelDownload(): Promise<void>;
|
|
131
|
+
|
|
132
|
+
getDownloadedBundlePath(bundleHash: string): Promise<string>;
|
|
133
|
+
|
|
134
|
+
// Event emitter support
|
|
135
|
+
addListener(eventName: string): void;
|
|
136
|
+
removeListeners(count: number): void;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('SwiftPatch');
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwiftPatch SDK Configuration
|
|
3
|
+
*/
|
|
4
|
+
export interface SwiftPatchConfig {
|
|
5
|
+
/** Deployment key from SwiftPatch dashboard */
|
|
6
|
+
deploymentKey: string;
|
|
7
|
+
|
|
8
|
+
/** Server URL (defaults to cloud) */
|
|
9
|
+
serverUrl?: string;
|
|
10
|
+
|
|
11
|
+
/** Check for updates when app comes to foreground */
|
|
12
|
+
checkOnResume?: boolean;
|
|
13
|
+
|
|
14
|
+
/** Minimum interval between update checks (ms) */
|
|
15
|
+
checkInterval?: number;
|
|
16
|
+
|
|
17
|
+
/** Install mode for non-mandatory updates */
|
|
18
|
+
installMode?: InstallMode;
|
|
19
|
+
|
|
20
|
+
/** Install mode for mandatory updates */
|
|
21
|
+
mandatoryInstallMode?: InstallMode;
|
|
22
|
+
|
|
23
|
+
/** Enable debug logging */
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
|
|
26
|
+
/** Custom headers for API requests */
|
|
27
|
+
customHeaders?: Record<string, string>;
|
|
28
|
+
|
|
29
|
+
/** Public key for bundle verification (optional) */
|
|
30
|
+
publicKey?: string;
|
|
31
|
+
|
|
32
|
+
/** Auto-stabilize after N successful launches (0 = manual only) */
|
|
33
|
+
autoStabilizeAfterLaunches?: number;
|
|
34
|
+
|
|
35
|
+
/** SDK PIN for in-app dashboard access (optional) */
|
|
36
|
+
sdkPin?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* When to install downloaded updates
|
|
41
|
+
*/
|
|
42
|
+
export enum InstallMode {
|
|
43
|
+
/** Install immediately and restart */
|
|
44
|
+
IMMEDIATE = 'immediate',
|
|
45
|
+
|
|
46
|
+
/** Install on next app restart */
|
|
47
|
+
ON_NEXT_RESTART = 'onNextRestart',
|
|
48
|
+
|
|
49
|
+
/** Install on next resume from background */
|
|
50
|
+
ON_NEXT_RESUME = 'onNextResume',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Current update status
|
|
55
|
+
*/
|
|
56
|
+
export enum UpdateStatus {
|
|
57
|
+
/** Checking for updates */
|
|
58
|
+
CHECKING = 'checking',
|
|
59
|
+
|
|
60
|
+
/** No update available */
|
|
61
|
+
UP_TO_DATE = 'upToDate',
|
|
62
|
+
|
|
63
|
+
/** Update available, not downloaded */
|
|
64
|
+
UPDATE_AVAILABLE = 'updateAvailable',
|
|
65
|
+
|
|
66
|
+
/** Downloading update */
|
|
67
|
+
DOWNLOADING = 'downloading',
|
|
68
|
+
|
|
69
|
+
/** Download complete, ready to install */
|
|
70
|
+
READY_TO_INSTALL = 'readyToInstall',
|
|
71
|
+
|
|
72
|
+
/** Installing update */
|
|
73
|
+
INSTALLING = 'installing',
|
|
74
|
+
|
|
75
|
+
/** Update installed, restart required */
|
|
76
|
+
RESTART_REQUIRED = 'restartRequired',
|
|
77
|
+
|
|
78
|
+
/** Error occurred */
|
|
79
|
+
ERROR = 'error',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Slot states for the dual-slot architecture
|
|
84
|
+
*/
|
|
85
|
+
export enum SlotState {
|
|
86
|
+
DEFAULT_SLOT = 'DEFAULT_SLOT',
|
|
87
|
+
STABLE_SLOT = 'STABLE_SLOT',
|
|
88
|
+
NEW_SLOT = 'NEW_SLOT',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Environment mode
|
|
93
|
+
*/
|
|
94
|
+
export enum EnvironmentMode {
|
|
95
|
+
PRODUCTION = 'PROD',
|
|
96
|
+
STAGING = 'STAGE',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Information about an available release
|
|
101
|
+
*/
|
|
102
|
+
export interface ReleaseInfo {
|
|
103
|
+
/** Unique release ID */
|
|
104
|
+
id: string;
|
|
105
|
+
|
|
106
|
+
/** Semantic version (e.g., "1.2.3") */
|
|
107
|
+
version: string;
|
|
108
|
+
|
|
109
|
+
/** Release notes */
|
|
110
|
+
releaseNote: string | null;
|
|
111
|
+
|
|
112
|
+
/** Whether update is mandatory */
|
|
113
|
+
isMandatory: boolean;
|
|
114
|
+
|
|
115
|
+
/** SHA-256 hash of the bundle */
|
|
116
|
+
bundleHash: string;
|
|
117
|
+
|
|
118
|
+
/** Download URL (patch or full bundle) */
|
|
119
|
+
downloadUrl: string;
|
|
120
|
+
|
|
121
|
+
/** Size in bytes */
|
|
122
|
+
downloadSize: number;
|
|
123
|
+
|
|
124
|
+
/** Whether this is a patch (true) or full bundle (false) */
|
|
125
|
+
isPatch: boolean;
|
|
126
|
+
|
|
127
|
+
/** Cryptographic signature (if signing enabled) */
|
|
128
|
+
signature?: string;
|
|
129
|
+
|
|
130
|
+
/** Release timestamp */
|
|
131
|
+
releasedAt: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Current bundle information
|
|
136
|
+
*/
|
|
137
|
+
export interface BundleInfo {
|
|
138
|
+
/** SHA-256 hash of current bundle */
|
|
139
|
+
hash: string;
|
|
140
|
+
|
|
141
|
+
/** Version string */
|
|
142
|
+
version: string;
|
|
143
|
+
|
|
144
|
+
/** When bundle was installed */
|
|
145
|
+
installedAt: string;
|
|
146
|
+
|
|
147
|
+
/** Whether this is the original bundled bundle */
|
|
148
|
+
isOriginal: boolean;
|
|
149
|
+
|
|
150
|
+
/** Current slot state */
|
|
151
|
+
slot: SlotState;
|
|
152
|
+
|
|
153
|
+
/** Current environment */
|
|
154
|
+
environment: EnvironmentMode;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Slot metadata for the dual-slot system
|
|
159
|
+
*/
|
|
160
|
+
export interface SlotMetadata {
|
|
161
|
+
environment: EnvironmentMode;
|
|
162
|
+
prod: {
|
|
163
|
+
currentSlot: SlotState;
|
|
164
|
+
newHash: string | null;
|
|
165
|
+
stableHash: string | null;
|
|
166
|
+
tempHash: string | null;
|
|
167
|
+
};
|
|
168
|
+
stage: {
|
|
169
|
+
newHash: string | null;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Download progress information
|
|
175
|
+
*/
|
|
176
|
+
export interface DownloadProgress {
|
|
177
|
+
/** Bytes downloaded so far */
|
|
178
|
+
downloadedBytes: number;
|
|
179
|
+
|
|
180
|
+
/** Total bytes to download */
|
|
181
|
+
totalBytes: number;
|
|
182
|
+
|
|
183
|
+
/** Progress percentage (0-100) */
|
|
184
|
+
percentage: number;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* SDK event types for analytics
|
|
189
|
+
*/
|
|
190
|
+
export enum SwiftPatchEventType {
|
|
191
|
+
DOWNLOAD_PROD_STARTED = 'DOWNLOAD_PROD_STARTED',
|
|
192
|
+
DOWNLOAD_PROD_COMPLETED = 'DOWNLOAD_PROD_COMPLETED',
|
|
193
|
+
DOWNLOAD_PROD_FAILED = 'DOWNLOAD_PROD_FAILED',
|
|
194
|
+
INSTALLED_PROD = 'INSTALLED_PROD',
|
|
195
|
+
ROLLBACK_PROD = 'ROLLBACK_PROD',
|
|
196
|
+
STABILIZE_PROD = 'STABILIZE_PROD',
|
|
197
|
+
CORRUPTION_PROD = 'CORRUPTION_PROD',
|
|
198
|
+
DOWNLOAD_STAGE_STARTED = 'DOWNLOAD_STAGE_STARTED',
|
|
199
|
+
DOWNLOAD_STAGE_COMPLETED = 'DOWNLOAD_STAGE_COMPLETED',
|
|
200
|
+
DOWNLOAD_STAGE_FAILED = 'DOWNLOAD_STAGE_FAILED',
|
|
201
|
+
INSTALLED_STAGE = 'INSTALLED_STAGE',
|
|
202
|
+
SYNC_ERROR = 'SYNC_ERROR',
|
|
203
|
+
VERSION_CHANGED = 'VERSION_CHANGED',
|
|
204
|
+
CRASH_DETECTED = 'CRASH_DETECTED',
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A queued SDK event
|
|
209
|
+
*/
|
|
210
|
+
export interface SwiftPatchEvent {
|
|
211
|
+
id: string;
|
|
212
|
+
eventType: SwiftPatchEventType;
|
|
213
|
+
timestamp: number;
|
|
214
|
+
releaseHash?: string;
|
|
215
|
+
errorMessage?: string;
|
|
216
|
+
metadata?: Record<string, unknown>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* SwiftPatch state exposed via hook
|
|
221
|
+
*/
|
|
222
|
+
export interface SwiftPatchState {
|
|
223
|
+
/** Current update status */
|
|
224
|
+
status: UpdateStatus;
|
|
225
|
+
|
|
226
|
+
/** Download progress (if downloading) */
|
|
227
|
+
downloadProgress: DownloadProgress | null;
|
|
228
|
+
|
|
229
|
+
/** Currently installed bundle info */
|
|
230
|
+
currentBundle: BundleInfo | null;
|
|
231
|
+
|
|
232
|
+
/** Available update info */
|
|
233
|
+
availableUpdate: ReleaseInfo | null;
|
|
234
|
+
|
|
235
|
+
/** Whether restart is required to apply update */
|
|
236
|
+
isRestartRequired: boolean;
|
|
237
|
+
|
|
238
|
+
/** Last error (if status is ERROR) */
|
|
239
|
+
error: SwiftPatchError | null;
|
|
240
|
+
|
|
241
|
+
/** Last check timestamp */
|
|
242
|
+
lastCheckedAt: Date | null;
|
|
243
|
+
|
|
244
|
+
/** Current slot metadata */
|
|
245
|
+
slotMetadata: SlotMetadata | null;
|
|
246
|
+
|
|
247
|
+
/** Current environment */
|
|
248
|
+
environment: EnvironmentMode;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* SwiftPatch actions exposed via hook
|
|
253
|
+
*/
|
|
254
|
+
export interface SwiftPatchActions {
|
|
255
|
+
/** Check for available updates */
|
|
256
|
+
checkForUpdate: () => Promise<ReleaseInfo | null>;
|
|
257
|
+
|
|
258
|
+
/** Download available update */
|
|
259
|
+
downloadUpdate: () => Promise<void>;
|
|
260
|
+
|
|
261
|
+
/** Install downloaded update */
|
|
262
|
+
installUpdate: () => Promise<void>;
|
|
263
|
+
|
|
264
|
+
/** Restart app to apply update */
|
|
265
|
+
restart: () => void;
|
|
266
|
+
|
|
267
|
+
/** Manually rollback to previous version */
|
|
268
|
+
rollback: () => Promise<void>;
|
|
269
|
+
|
|
270
|
+
/** Clear pending update */
|
|
271
|
+
clearPendingUpdate: () => Promise<void>;
|
|
272
|
+
|
|
273
|
+
/** Get current bundle info */
|
|
274
|
+
getCurrentBundle: () => Promise<BundleInfo | null>;
|
|
275
|
+
|
|
276
|
+
/** Stabilize current bundle (promote NEW → STABLE) */
|
|
277
|
+
stabilize: () => Promise<SlotMetadata>;
|
|
278
|
+
|
|
279
|
+
/** Switch environment (PROD / STAGE) */
|
|
280
|
+
switchEnvironment: (env: EnvironmentMode) => Promise<SlotMetadata>;
|
|
281
|
+
|
|
282
|
+
/** Get slot metadata */
|
|
283
|
+
getSlotMetadata: () => Promise<SlotMetadata>;
|
|
284
|
+
|
|
285
|
+
/** Mark app as mounted (for crash detection) */
|
|
286
|
+
markMounted: () => void;
|
|
287
|
+
|
|
288
|
+
/** Download a stage bundle for testing */
|
|
289
|
+
downloadStageBundle: (url: string, hash: string) => Promise<void>;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Combined hook return type
|
|
294
|
+
*/
|
|
295
|
+
export type UseSwiftPatchReturn = SwiftPatchState & SwiftPatchActions;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Simplified update hook return type
|
|
299
|
+
*/
|
|
300
|
+
export interface UseSwiftPatchUpdateReturn {
|
|
301
|
+
isRestartRequired: boolean;
|
|
302
|
+
newReleaseBundle: ReleaseInfo | null;
|
|
303
|
+
currentBundle: BundleInfo | null;
|
|
304
|
+
status: UpdateStatus;
|
|
305
|
+
restart: () => void;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* SwiftPatch error
|
|
310
|
+
*/
|
|
311
|
+
export interface SwiftPatchError {
|
|
312
|
+
code: SwiftPatchErrorCode;
|
|
313
|
+
message: string;
|
|
314
|
+
details?: unknown;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export enum SwiftPatchErrorCode {
|
|
318
|
+
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
319
|
+
DOWNLOAD_ERROR = 'DOWNLOAD_ERROR',
|
|
320
|
+
VERIFICATION_ERROR = 'VERIFICATION_ERROR',
|
|
321
|
+
PATCH_ERROR = 'PATCH_ERROR',
|
|
322
|
+
INSTALL_ERROR = 'INSTALL_ERROR',
|
|
323
|
+
CONFIG_ERROR = 'CONFIG_ERROR',
|
|
324
|
+
STABILIZE_ERROR = 'STABILIZE_ERROR',
|
|
325
|
+
ENVIRONMENT_ERROR = 'ENVIRONMENT_ERROR',
|
|
326
|
+
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Modal hook return type
|
|
331
|
+
*/
|
|
332
|
+
export interface UseSwiftPatchModalReturn {
|
|
333
|
+
showModal: () => void;
|
|
334
|
+
hideModal: () => void;
|
|
335
|
+
isModalVisible: boolean;
|
|
336
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useSwiftPatchContext } from './SwiftPatchProvider';
|
|
2
|
+
import type {
|
|
3
|
+
UseSwiftPatchReturn,
|
|
4
|
+
UseSwiftPatchUpdateReturn,
|
|
5
|
+
} from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Main hook for accessing all SwiftPatch state and actions.
|
|
9
|
+
* Must be used within SwiftPatchProvider.
|
|
10
|
+
*/
|
|
11
|
+
export function useSwiftPatch(): UseSwiftPatchReturn {
|
|
12
|
+
return useSwiftPatchContext();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Simplified hook that only exposes update-related state.
|
|
17
|
+
* Useful for showing update banners or restart prompts.
|
|
18
|
+
*/
|
|
19
|
+
export function useSwiftPatchUpdate(): UseSwiftPatchUpdateReturn {
|
|
20
|
+
const ctx = useSwiftPatchContext();
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
isRestartRequired: ctx.isRestartRequired,
|
|
24
|
+
newReleaseBundle: ctx.availableUpdate,
|
|
25
|
+
currentBundle: ctx.currentBundle,
|
|
26
|
+
status: ctx.status,
|
|
27
|
+
restart: ctx.restart,
|
|
28
|
+
};
|
|
29
|
+
}
|