@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.
Files changed (182) hide show
  1. package/README.md +430 -0
  2. package/android/build.gradle +105 -0
  3. package/android/src/main/AndroidManifest.xml +6 -0
  4. package/android/src/main/java/com/swiftpatch/BundleManager.kt +107 -0
  5. package/android/src/main/java/com/swiftpatch/CrashDetector.kt +79 -0
  6. package/android/src/main/java/com/swiftpatch/CryptoVerifier.kt +69 -0
  7. package/android/src/main/java/com/swiftpatch/DownloadManager.kt +120 -0
  8. package/android/src/main/java/com/swiftpatch/EventQueue.kt +86 -0
  9. package/android/src/main/java/com/swiftpatch/FileUtils.kt +60 -0
  10. package/android/src/main/java/com/swiftpatch/PatchApplier.kt +60 -0
  11. package/android/src/main/java/com/swiftpatch/SignalCrashHandler.kt +84 -0
  12. package/android/src/main/java/com/swiftpatch/SlotManager.kt +299 -0
  13. package/android/src/main/java/com/swiftpatch/SwiftPatchModule.kt +630 -0
  14. package/android/src/main/java/com/swiftpatch/SwiftPatchPackage.kt +21 -0
  15. package/android/src/main/jni/CMakeLists.txt +12 -0
  16. package/android/src/main/jni/bspatch.c +188 -0
  17. package/android/src/main/jni/bspatch.h +57 -0
  18. package/android/src/main/jni/bspatch_jni.c +28 -0
  19. package/ios/Libraries/bspatch/bspatch.c +188 -0
  20. package/ios/Libraries/bspatch/bspatch.h +50 -0
  21. package/ios/Libraries/bspatch/module.modulemap +4 -0
  22. package/ios/SwiftPatch/BundleManager.swift +113 -0
  23. package/ios/SwiftPatch/CrashDetector.swift +71 -0
  24. package/ios/SwiftPatch/CryptoVerifier.swift +70 -0
  25. package/ios/SwiftPatch/DownloadManager.swift +125 -0
  26. package/ios/SwiftPatch/EventQueue.swift +116 -0
  27. package/ios/SwiftPatch/FileUtils.swift +38 -0
  28. package/ios/SwiftPatch/PatchApplier.swift +41 -0
  29. package/ios/SwiftPatch/SignalCrashHandler.swift +129 -0
  30. package/ios/SwiftPatch/SlotManager.swift +360 -0
  31. package/ios/SwiftPatch/SwiftPatchModule.m +56 -0
  32. package/ios/SwiftPatch/SwiftPatchModule.swift +621 -0
  33. package/lib/commonjs/SwiftPatchCore.js +140 -0
  34. package/lib/commonjs/SwiftPatchCore.js.map +1 -0
  35. package/lib/commonjs/SwiftPatchProvider.js +617 -0
  36. package/lib/commonjs/SwiftPatchProvider.js.map +1 -0
  37. package/lib/commonjs/constants.js +50 -0
  38. package/lib/commonjs/constants.js.map +1 -0
  39. package/lib/commonjs/core/Downloader.js +63 -0
  40. package/lib/commonjs/core/Downloader.js.map +1 -0
  41. package/lib/commonjs/core/Installer.js +46 -0
  42. package/lib/commonjs/core/Installer.js.map +1 -0
  43. package/lib/commonjs/core/Rollback.js +36 -0
  44. package/lib/commonjs/core/Rollback.js.map +1 -0
  45. package/lib/commonjs/core/UpdateChecker.js +57 -0
  46. package/lib/commonjs/core/UpdateChecker.js.map +1 -0
  47. package/lib/commonjs/core/Verifier.js +82 -0
  48. package/lib/commonjs/core/Verifier.js.map +1 -0
  49. package/lib/commonjs/core/index.js +41 -0
  50. package/lib/commonjs/core/index.js.map +1 -0
  51. package/lib/commonjs/index.js +154 -0
  52. package/lib/commonjs/index.js.map +1 -0
  53. package/lib/commonjs/modal/SwiftPatchModal.js +667 -0
  54. package/lib/commonjs/modal/SwiftPatchModal.js.map +1 -0
  55. package/lib/commonjs/modal/useSwiftPatchModal.js +26 -0
  56. package/lib/commonjs/modal/useSwiftPatchModal.js.map +1 -0
  57. package/lib/commonjs/native/NativeSwiftPatch.js +85 -0
  58. package/lib/commonjs/native/NativeSwiftPatch.js.map +1 -0
  59. package/lib/commonjs/native/NativeSwiftPatchSpec.js +15 -0
  60. package/lib/commonjs/native/NativeSwiftPatchSpec.js.map +1 -0
  61. package/lib/commonjs/package.json +1 -0
  62. package/lib/commonjs/types.js +126 -0
  63. package/lib/commonjs/types.js.map +1 -0
  64. package/lib/commonjs/useSwiftPatch.js +31 -0
  65. package/lib/commonjs/useSwiftPatch.js.map +1 -0
  66. package/lib/commonjs/utils/api.js +206 -0
  67. package/lib/commonjs/utils/api.js.map +1 -0
  68. package/lib/commonjs/utils/device.js +23 -0
  69. package/lib/commonjs/utils/device.js.map +1 -0
  70. package/lib/commonjs/utils/logger.js +30 -0
  71. package/lib/commonjs/utils/logger.js.map +1 -0
  72. package/lib/commonjs/utils/storage.js +31 -0
  73. package/lib/commonjs/utils/storage.js.map +1 -0
  74. package/lib/commonjs/withSwiftPatch.js +42 -0
  75. package/lib/commonjs/withSwiftPatch.js.map +1 -0
  76. package/lib/module/SwiftPatchCore.js +135 -0
  77. package/lib/module/SwiftPatchCore.js.map +1 -0
  78. package/lib/module/SwiftPatchProvider.js +611 -0
  79. package/lib/module/SwiftPatchProvider.js.map +1 -0
  80. package/lib/module/constants.js +46 -0
  81. package/lib/module/constants.js.map +1 -0
  82. package/lib/module/core/Downloader.js +57 -0
  83. package/lib/module/core/Downloader.js.map +1 -0
  84. package/lib/module/core/Installer.js +41 -0
  85. package/lib/module/core/Installer.js.map +1 -0
  86. package/lib/module/core/Rollback.js +31 -0
  87. package/lib/module/core/Rollback.js.map +1 -0
  88. package/lib/module/core/UpdateChecker.js +51 -0
  89. package/lib/module/core/UpdateChecker.js.map +1 -0
  90. package/lib/module/core/Verifier.js +76 -0
  91. package/lib/module/core/Verifier.js.map +1 -0
  92. package/lib/module/core/index.js +8 -0
  93. package/lib/module/core/index.js.map +1 -0
  94. package/lib/module/index.js +34 -0
  95. package/lib/module/index.js.map +1 -0
  96. package/lib/module/modal/SwiftPatchModal.js +661 -0
  97. package/lib/module/modal/SwiftPatchModal.js.map +1 -0
  98. package/lib/module/modal/useSwiftPatchModal.js +22 -0
  99. package/lib/module/modal/useSwiftPatchModal.js.map +1 -0
  100. package/lib/module/native/NativeSwiftPatch.js +78 -0
  101. package/lib/module/native/NativeSwiftPatch.js.map +1 -0
  102. package/lib/module/native/NativeSwiftPatchSpec.js +12 -0
  103. package/lib/module/native/NativeSwiftPatchSpec.js.map +1 -0
  104. package/lib/module/types.js +139 -0
  105. package/lib/module/types.js.map +1 -0
  106. package/lib/module/useSwiftPatch.js +26 -0
  107. package/lib/module/useSwiftPatch.js.map +1 -0
  108. package/lib/module/utils/api.js +197 -0
  109. package/lib/module/utils/api.js.map +1 -0
  110. package/lib/module/utils/device.js +18 -0
  111. package/lib/module/utils/device.js.map +1 -0
  112. package/lib/module/utils/logger.js +26 -0
  113. package/lib/module/utils/logger.js.map +1 -0
  114. package/lib/module/utils/storage.js +24 -0
  115. package/lib/module/utils/storage.js.map +1 -0
  116. package/lib/module/withSwiftPatch.js +37 -0
  117. package/lib/module/withSwiftPatch.js.map +1 -0
  118. package/lib/typescript/SwiftPatchCore.d.ts +64 -0
  119. package/lib/typescript/SwiftPatchCore.d.ts.map +1 -0
  120. package/lib/typescript/SwiftPatchProvider.d.ts +22 -0
  121. package/lib/typescript/SwiftPatchProvider.d.ts.map +1 -0
  122. package/lib/typescript/constants.d.ts +33 -0
  123. package/lib/typescript/constants.d.ts.map +1 -0
  124. package/lib/typescript/core/Downloader.d.ts +34 -0
  125. package/lib/typescript/core/Downloader.d.ts.map +1 -0
  126. package/lib/typescript/core/Installer.d.ts +25 -0
  127. package/lib/typescript/core/Installer.d.ts.map +1 -0
  128. package/lib/typescript/core/Rollback.d.ts +18 -0
  129. package/lib/typescript/core/Rollback.d.ts.map +1 -0
  130. package/lib/typescript/core/UpdateChecker.d.ts +27 -0
  131. package/lib/typescript/core/UpdateChecker.d.ts.map +1 -0
  132. package/lib/typescript/core/Verifier.d.ts +31 -0
  133. package/lib/typescript/core/Verifier.d.ts.map +1 -0
  134. package/lib/typescript/core/index.d.ts +8 -0
  135. package/lib/typescript/core/index.d.ts.map +1 -0
  136. package/lib/typescript/index.d.ts +13 -0
  137. package/lib/typescript/index.d.ts.map +1 -0
  138. package/lib/typescript/modal/SwiftPatchModal.d.ts +11 -0
  139. package/lib/typescript/modal/SwiftPatchModal.d.ts.map +1 -0
  140. package/lib/typescript/modal/useSwiftPatchModal.d.ts +7 -0
  141. package/lib/typescript/modal/useSwiftPatchModal.d.ts.map +1 -0
  142. package/lib/typescript/native/NativeSwiftPatch.d.ts +61 -0
  143. package/lib/typescript/native/NativeSwiftPatch.d.ts.map +1 -0
  144. package/lib/typescript/native/NativeSwiftPatchSpec.d.ts +67 -0
  145. package/lib/typescript/native/NativeSwiftPatchSpec.d.ts.map +1 -0
  146. package/lib/typescript/types.d.ts +266 -0
  147. package/lib/typescript/types.d.ts.map +1 -0
  148. package/lib/typescript/useSwiftPatch.d.ts +12 -0
  149. package/lib/typescript/useSwiftPatch.d.ts.map +1 -0
  150. package/lib/typescript/utils/api.d.ts +87 -0
  151. package/lib/typescript/utils/api.d.ts.map +1 -0
  152. package/lib/typescript/utils/device.d.ts +9 -0
  153. package/lib/typescript/utils/device.d.ts.map +1 -0
  154. package/lib/typescript/utils/logger.d.ts +8 -0
  155. package/lib/typescript/utils/logger.d.ts.map +1 -0
  156. package/lib/typescript/utils/storage.d.ts +14 -0
  157. package/lib/typescript/utils/storage.d.ts.map +1 -0
  158. package/lib/typescript/withSwiftPatch.d.ts +12 -0
  159. package/lib/typescript/withSwiftPatch.d.ts.map +1 -0
  160. package/package.json +99 -0
  161. package/react-native-swiftpatch.podspec +50 -0
  162. package/src/SwiftPatchCore.ts +148 -0
  163. package/src/SwiftPatchProvider.tsx +514 -0
  164. package/src/constants.ts +49 -0
  165. package/src/core/Downloader.ts +74 -0
  166. package/src/core/Installer.ts +38 -0
  167. package/src/core/Rollback.ts +28 -0
  168. package/src/core/UpdateChecker.ts +70 -0
  169. package/src/core/Verifier.ts +92 -0
  170. package/src/core/index.ts +11 -0
  171. package/src/index.ts +64 -0
  172. package/src/modal/SwiftPatchModal.tsx +657 -0
  173. package/src/modal/useSwiftPatchModal.ts +24 -0
  174. package/src/native/NativeSwiftPatch.ts +205 -0
  175. package/src/native/NativeSwiftPatchSpec.ts +139 -0
  176. package/src/types.ts +336 -0
  177. package/src/useSwiftPatch.ts +29 -0
  178. package/src/utils/api.ts +244 -0
  179. package/src/utils/device.ts +15 -0
  180. package/src/utils/logger.ts +29 -0
  181. package/src/utils/storage.ts +23 -0
  182. package/src/withSwiftPatch.tsx +41 -0
@@ -0,0 +1,667 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SwiftPatchModal = SwiftPatchModal;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _useSwiftPatch = require("../useSwiftPatch");
10
+ var _api = require("../utils/api");
11
+ var _NativeSwiftPatch = _interopRequireDefault(require("../native/NativeSwiftPatch"));
12
+ var _types = require("../types");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
+ function SwiftPatchModal({
17
+ visible,
18
+ onClose,
19
+ serverUrl,
20
+ deploymentKey,
21
+ customHeaders = {}
22
+ }) {
23
+ const ctx = (0, _useSwiftPatch.useSwiftPatch)();
24
+ const [screen, setScreen] = (0, _react.useState)('login');
25
+ const [sdkToken, setSdkToken] = (0, _react.useState)(null);
26
+ const [pin, setPin] = (0, _react.useState)('');
27
+ const [loginError, setLoginError] = (0, _react.useState)('');
28
+ const [isLoading, setIsLoading] = (0, _react.useState)(false);
29
+ const [buckets, setBuckets] = (0, _react.useState)([]);
30
+ const [bundles, setBundles] = (0, _react.useState)([]);
31
+ const [selectedBucket, setSelectedBucket] = (0, _react.useState)(null);
32
+ const [hasMoreBundles, setHasMoreBundles] = (0, _react.useState)(false);
33
+ const [pageOffset, setPageOffset] = (0, _react.useState)(0);
34
+ const [downloadingHash, setDownloadingHash] = (0, _react.useState)(null);
35
+ const [downloadProgress, setDownloadProgress] = (0, _react.useState)(0);
36
+ const [metadata, setMetadata] = (0, _react.useState)(null);
37
+
38
+ // Fetch metadata on open
39
+ (0, _react.useEffect)(() => {
40
+ if (visible) {
41
+ _NativeSwiftPatch.default.getSlotMetadata().then(setMetadata).catch(() => {});
42
+ }
43
+ }, [visible]);
44
+
45
+ // Login handler
46
+ const handleLogin = (0, _react.useCallback)(async () => {
47
+ if (pin.length < 4) {
48
+ setLoginError('PIN must be at least 4 characters');
49
+ return;
50
+ }
51
+ setIsLoading(true);
52
+ setLoginError('');
53
+ const result = await (0, _api.verifySDKPin)({
54
+ serverUrl,
55
+ deploymentKey,
56
+ pin,
57
+ customHeaders
58
+ });
59
+ setIsLoading(false);
60
+ if (result) {
61
+ setSdkToken(result.sdkToken);
62
+ setScreen('dashboard');
63
+ // Fetch buckets
64
+ const bucketsData = await (0, _api.listBuckets)({
65
+ serverUrl,
66
+ sdkToken: result.sdkToken,
67
+ deploymentKey,
68
+ customHeaders
69
+ });
70
+ setBuckets(bucketsData);
71
+ } else {
72
+ setLoginError('Invalid PIN. Please try again.');
73
+ }
74
+ }, [pin, serverUrl, deploymentKey, customHeaders]);
75
+
76
+ // Load bundles for a bucket
77
+ const loadBundles = (0, _react.useCallback)(async (bucket, offset = 0) => {
78
+ if (!sdkToken) return;
79
+ setIsLoading(true);
80
+ setSelectedBucket(bucket);
81
+ setPageOffset(offset);
82
+ const result = await (0, _api.listBundles)({
83
+ serverUrl,
84
+ sdkToken,
85
+ deploymentKey,
86
+ channelId: bucket.id,
87
+ pageOffset: offset,
88
+ customHeaders
89
+ });
90
+ if (offset === 0) {
91
+ setBundles(result.bundles);
92
+ } else {
93
+ setBundles(prev => [...prev, ...result.bundles]);
94
+ }
95
+ setHasMoreBundles(result.hasMore);
96
+ setIsLoading(false);
97
+ setScreen('bundles');
98
+ }, [sdkToken, serverUrl, deploymentKey, customHeaders]);
99
+
100
+ // Download a specific bundle (stage)
101
+ const handleDownloadBundle = (0, _react.useCallback)(async bundle => {
102
+ try {
103
+ setDownloadingHash(bundle.bundleHash);
104
+ setDownloadProgress(0);
105
+
106
+ // Download as stage bundle for testing
107
+ await ctx.downloadStageBundle(`${serverUrl}/releases/${bundle.id}/download`, bundle.bundleHash);
108
+ setDownloadingHash(null);
109
+ setDownloadProgress(100);
110
+
111
+ // Refresh metadata
112
+ const meta = await _NativeSwiftPatch.default.getSlotMetadata();
113
+ setMetadata(meta);
114
+ } catch (e) {
115
+ setDownloadingHash(null);
116
+ setDownloadProgress(0);
117
+ }
118
+ }, [ctx, serverUrl]);
119
+
120
+ // Switch environment
121
+ const handleSwitchEnvironment = (0, _react.useCallback)(async () => {
122
+ const currentEnv = metadata?.environment || 'PROD';
123
+ const newEnv = currentEnv === 'PROD' ? _types.EnvironmentMode.STAGING : _types.EnvironmentMode.PRODUCTION;
124
+ const meta = await ctx.switchEnvironment(newEnv);
125
+ setMetadata(meta);
126
+ }, [ctx, metadata]);
127
+
128
+ // Stabilize
129
+ const handleStabilize = (0, _react.useCallback)(async () => {
130
+ try {
131
+ const meta = await ctx.stabilize();
132
+ setMetadata(meta);
133
+ } catch (_e) {}
134
+ }, [ctx]);
135
+
136
+ // Rollback
137
+ const handleRollback = (0, _react.useCallback)(async () => {
138
+ try {
139
+ await ctx.rollback();
140
+ const meta = await _NativeSwiftPatch.default.getSlotMetadata();
141
+ setMetadata(meta);
142
+ } catch (_e) {}
143
+ }, [ctx]);
144
+
145
+ // Restart
146
+ const handleRestart = (0, _react.useCallback)(() => {
147
+ ctx.restart();
148
+ }, [ctx]);
149
+ const handleClose = (0, _react.useCallback)(() => {
150
+ setPin('');
151
+ setLoginError('');
152
+ setScreen(sdkToken ? 'dashboard' : 'login');
153
+ onClose();
154
+ }, [onClose, sdkToken]);
155
+
156
+ // ─── Render Login Screen ───────────────────────────────────
157
+
158
+ const renderLogin = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
159
+ style: styles.screenContainer,
160
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
161
+ style: styles.title,
162
+ children: "SwiftPatch Dashboard"
163
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
164
+ style: styles.subtitle,
165
+ children: "Enter your SDK PIN to access"
166
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
167
+ style: styles.pinInput,
168
+ value: pin,
169
+ onChangeText: text => {
170
+ setPin(text.replace(/[^0-9a-zA-Z]/g, ''));
171
+ setLoginError('');
172
+ },
173
+ placeholder: "Enter PIN",
174
+ placeholderTextColor: "#666",
175
+ secureTextEntry: true,
176
+ maxLength: 8,
177
+ autoFocus: true
178
+ }), loginError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
179
+ style: styles.errorText,
180
+ children: loginError
181
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
182
+ style: [styles.button, pin.length < 4 && styles.buttonDisabled],
183
+ onPress: handleLogin,
184
+ disabled: pin.length < 4 || isLoading,
185
+ children: isLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
186
+ color: "#fff",
187
+ size: "small"
188
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
189
+ style: styles.buttonText,
190
+ children: "Unlock"
191
+ })
192
+ })]
193
+ });
194
+
195
+ // ─── Render Dashboard Screen ───────────────────────────────
196
+
197
+ const renderDashboard = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
198
+ style: styles.screenContainer,
199
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
200
+ style: styles.title,
201
+ children: "Dashboard"
202
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
203
+ style: styles.card,
204
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
205
+ style: styles.cardTitle,
206
+ children: "Current Status"
207
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
208
+ style: styles.row,
209
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
210
+ style: styles.label,
211
+ children: "Environment:"
212
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
213
+ style: styles.envBadge,
214
+ onPress: handleSwitchEnvironment,
215
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
216
+ style: styles.envBadgeText,
217
+ children: metadata?.environment || 'PROD'
218
+ })
219
+ })]
220
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
221
+ style: styles.row,
222
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
223
+ style: styles.label,
224
+ children: "Slot:"
225
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
226
+ style: styles.value,
227
+ children: metadata?.prod?.currentSlot || 'DEFAULT'
228
+ })]
229
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
230
+ style: styles.row,
231
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
232
+ style: styles.label,
233
+ children: "New Hash:"
234
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
235
+ style: styles.value,
236
+ children: [metadata?.prod?.newHash?.substring(0, 12) || 'none', "..."]
237
+ })]
238
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
239
+ style: styles.row,
240
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
241
+ style: styles.label,
242
+ children: "Stable Hash:"
243
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
244
+ style: styles.value,
245
+ children: [metadata?.prod?.stableHash?.substring(0, 12) || 'none', "..."]
246
+ })]
247
+ }), metadata?.prod?.tempHash && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
248
+ style: styles.row,
249
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
250
+ style: styles.label,
251
+ children: "Pending:"
252
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
253
+ style: [styles.value, {
254
+ color: '#f59e0b'
255
+ }],
256
+ children: [metadata.prod.tempHash.substring(0, 12), "... (restart required)"]
257
+ })]
258
+ })]
259
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
260
+ style: styles.card,
261
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
262
+ style: styles.cardTitle,
263
+ children: "Actions"
264
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
265
+ style: styles.actionsRow,
266
+ children: [metadata?.prod?.currentSlot === 'NEW_SLOT' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
267
+ style: styles.actionButton,
268
+ onPress: handleStabilize,
269
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
270
+ style: styles.actionButtonText,
271
+ children: "Stabilize"
272
+ })
273
+ }), metadata?.prod?.currentSlot !== 'DEFAULT_SLOT' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
274
+ style: [styles.actionButton, styles.dangerButton],
275
+ onPress: handleRollback,
276
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
277
+ style: styles.actionButtonText,
278
+ children: "Rollback"
279
+ })
280
+ }), ctx.isRestartRequired && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
281
+ style: [styles.actionButton, styles.successButton],
282
+ onPress: handleRestart,
283
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
284
+ style: styles.actionButtonText,
285
+ children: "Restart"
286
+ })
287
+ })]
288
+ })]
289
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
290
+ style: styles.card,
291
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
292
+ style: styles.cardTitle,
293
+ children: "Channels"
294
+ }), buckets.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
295
+ style: styles.emptyText,
296
+ children: "No channels found"
297
+ }) : buckets.map(bucket => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
298
+ style: styles.bucketItem,
299
+ onPress: () => loadBundles(bucket),
300
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
301
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
302
+ style: styles.bucketName,
303
+ children: bucket.name
304
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
305
+ style: styles.bucketMeta,
306
+ children: [bucket.totalReleases, " releases ", bucket.isDefault ? '(default)' : '']
307
+ })]
308
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
309
+ style: styles.chevron,
310
+ children: "\u203A"
311
+ })]
312
+ }, bucket.id))]
313
+ })]
314
+ });
315
+
316
+ // ─── Render Bundles Screen ─────────────────────────────────
317
+
318
+ const renderBundles = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
319
+ style: styles.screenContainer,
320
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
321
+ onPress: () => setScreen('dashboard'),
322
+ style: styles.backButton,
323
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
324
+ style: styles.backText,
325
+ children: "\u2039 Back"
326
+ })
327
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
328
+ style: styles.title,
329
+ children: selectedBucket?.name || 'Bundles'
330
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
331
+ data: bundles,
332
+ keyExtractor: item => item.id,
333
+ renderItem: ({
334
+ item
335
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
336
+ style: styles.bundleCard,
337
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
338
+ style: styles.bundleHeader,
339
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
340
+ style: styles.bundleVersion,
341
+ children: ["v", item.version, " (", item.buildNumber, ")"]
342
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
343
+ style: [styles.statusBadge, item.status === 'RELEASED' ? styles.releasedBadge : styles.readyBadge],
344
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
345
+ style: styles.statusBadgeText,
346
+ children: item.status
347
+ })
348
+ })]
349
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
350
+ style: styles.bundlePlatform,
351
+ children: item.platform
352
+ }), item.releaseNote && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
353
+ style: styles.bundleNote,
354
+ children: item.releaseNote
355
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
356
+ style: styles.bundleFooter,
357
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
358
+ style: styles.bundleSize,
359
+ children: formatSize(item.bundleSize)
360
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
361
+ style: [styles.downloadButton, downloadingHash === item.bundleHash && styles.buttonDisabled],
362
+ onPress: () => handleDownloadBundle(item),
363
+ disabled: downloadingHash !== null,
364
+ children: downloadingHash === item.bundleHash ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
365
+ color: "#fff",
366
+ size: "small"
367
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
368
+ style: styles.downloadButtonText,
369
+ children: "Download"
370
+ })
371
+ })]
372
+ })]
373
+ }),
374
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
375
+ style: styles.emptyText,
376
+ children: "No bundles found"
377
+ }),
378
+ onEndReached: () => {
379
+ if (hasMoreBundles && !isLoading && selectedBucket) {
380
+ loadBundles(selectedBucket, pageOffset + 1);
381
+ }
382
+ },
383
+ onEndReachedThreshold: 0.5,
384
+ ListFooterComponent: isLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
385
+ style: {
386
+ margin: 20
387
+ },
388
+ color: "#007AFF"
389
+ }) : null
390
+ })]
391
+ });
392
+
393
+ // ─── Main Render ───────────────────────────────────────────
394
+
395
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
396
+ visible: visible,
397
+ animationType: "slide",
398
+ presentationStyle: "pageSheet",
399
+ onRequestClose: handleClose,
400
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
401
+ style: styles.container,
402
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
403
+ style: styles.header,
404
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
405
+ style: styles.headerTitle,
406
+ children: "SwiftPatch"
407
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
408
+ onPress: handleClose,
409
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
410
+ style: styles.closeButton,
411
+ children: "\u2715"
412
+ })
413
+ })]
414
+ }), downloadingHash && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
415
+ style: styles.progressBar,
416
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
417
+ style: [styles.progressFill, {
418
+ width: `${downloadProgress}%`
419
+ }]
420
+ })
421
+ }), screen === 'login' && renderLogin(), screen === 'dashboard' && renderDashboard(), screen === 'bundles' && renderBundles()]
422
+ })
423
+ });
424
+ }
425
+ function formatSize(bytes) {
426
+ if (bytes < 1024) return `${bytes} B`;
427
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
428
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
429
+ }
430
+ const styles = _reactNative.StyleSheet.create({
431
+ container: {
432
+ flex: 1,
433
+ backgroundColor: '#0f0f23'
434
+ },
435
+ header: {
436
+ flexDirection: 'row',
437
+ justifyContent: 'space-between',
438
+ alignItems: 'center',
439
+ paddingHorizontal: 20,
440
+ paddingVertical: 16,
441
+ borderBottomWidth: 1,
442
+ borderBottomColor: '#1a1a3e'
443
+ },
444
+ headerTitle: {
445
+ fontSize: 18,
446
+ fontWeight: '700',
447
+ color: '#fff'
448
+ },
449
+ closeButton: {
450
+ fontSize: 20,
451
+ color: '#888',
452
+ padding: 4
453
+ },
454
+ progressBar: {
455
+ height: 3,
456
+ backgroundColor: '#1a1a3e'
457
+ },
458
+ progressFill: {
459
+ height: '100%',
460
+ backgroundColor: '#007AFF'
461
+ },
462
+ screenContainer: {
463
+ flex: 1,
464
+ padding: 20
465
+ },
466
+ title: {
467
+ fontSize: 24,
468
+ fontWeight: '700',
469
+ color: '#fff',
470
+ marginBottom: 8
471
+ },
472
+ subtitle: {
473
+ fontSize: 14,
474
+ color: '#888',
475
+ marginBottom: 24
476
+ },
477
+ pinInput: {
478
+ backgroundColor: '#1a1a3e',
479
+ color: '#fff',
480
+ fontSize: 24,
481
+ textAlign: 'center',
482
+ letterSpacing: 8,
483
+ padding: 16,
484
+ borderRadius: 12,
485
+ marginBottom: 16
486
+ },
487
+ errorText: {
488
+ color: '#ef4444',
489
+ fontSize: 14,
490
+ marginBottom: 16,
491
+ textAlign: 'center'
492
+ },
493
+ button: {
494
+ backgroundColor: '#007AFF',
495
+ paddingVertical: 14,
496
+ borderRadius: 10,
497
+ alignItems: 'center'
498
+ },
499
+ buttonDisabled: {
500
+ opacity: 0.5
501
+ },
502
+ buttonText: {
503
+ color: '#fff',
504
+ fontSize: 16,
505
+ fontWeight: '600'
506
+ },
507
+ card: {
508
+ backgroundColor: '#1a1a3e',
509
+ borderRadius: 12,
510
+ padding: 16,
511
+ marginBottom: 16
512
+ },
513
+ cardTitle: {
514
+ fontSize: 16,
515
+ fontWeight: '600',
516
+ color: '#fff',
517
+ marginBottom: 12
518
+ },
519
+ row: {
520
+ flexDirection: 'row',
521
+ justifyContent: 'space-between',
522
+ alignItems: 'center',
523
+ paddingVertical: 6
524
+ },
525
+ label: {
526
+ color: '#888',
527
+ fontSize: 14
528
+ },
529
+ value: {
530
+ color: '#fff',
531
+ fontSize: 14,
532
+ fontFamily: 'Courier'
533
+ },
534
+ envBadge: {
535
+ backgroundColor: '#007AFF',
536
+ paddingHorizontal: 12,
537
+ paddingVertical: 4,
538
+ borderRadius: 6
539
+ },
540
+ envBadgeText: {
541
+ color: '#fff',
542
+ fontSize: 12,
543
+ fontWeight: '600'
544
+ },
545
+ actionsRow: {
546
+ flexDirection: 'row',
547
+ gap: 8
548
+ },
549
+ actionButton: {
550
+ backgroundColor: '#007AFF',
551
+ paddingHorizontal: 16,
552
+ paddingVertical: 10,
553
+ borderRadius: 8,
554
+ flex: 1,
555
+ alignItems: 'center'
556
+ },
557
+ dangerButton: {
558
+ backgroundColor: '#ef4444'
559
+ },
560
+ successButton: {
561
+ backgroundColor: '#22c55e'
562
+ },
563
+ actionButtonText: {
564
+ color: '#fff',
565
+ fontSize: 14,
566
+ fontWeight: '600'
567
+ },
568
+ bucketItem: {
569
+ flexDirection: 'row',
570
+ justifyContent: 'space-between',
571
+ alignItems: 'center',
572
+ paddingVertical: 12,
573
+ borderBottomWidth: 1,
574
+ borderBottomColor: '#252550'
575
+ },
576
+ bucketName: {
577
+ color: '#fff',
578
+ fontSize: 15,
579
+ fontWeight: '500'
580
+ },
581
+ bucketMeta: {
582
+ color: '#666',
583
+ fontSize: 12,
584
+ marginTop: 2
585
+ },
586
+ chevron: {
587
+ color: '#666',
588
+ fontSize: 20
589
+ },
590
+ emptyText: {
591
+ color: '#666',
592
+ textAlign: 'center',
593
+ paddingVertical: 20
594
+ },
595
+ backButton: {
596
+ marginBottom: 12
597
+ },
598
+ backText: {
599
+ color: '#007AFF',
600
+ fontSize: 16
601
+ },
602
+ bundleCard: {
603
+ backgroundColor: '#1a1a3e',
604
+ borderRadius: 10,
605
+ padding: 14,
606
+ marginBottom: 10
607
+ },
608
+ bundleHeader: {
609
+ flexDirection: 'row',
610
+ justifyContent: 'space-between',
611
+ alignItems: 'center',
612
+ marginBottom: 4
613
+ },
614
+ bundleVersion: {
615
+ color: '#fff',
616
+ fontSize: 16,
617
+ fontWeight: '600'
618
+ },
619
+ statusBadge: {
620
+ paddingHorizontal: 8,
621
+ paddingVertical: 2,
622
+ borderRadius: 4
623
+ },
624
+ releasedBadge: {
625
+ backgroundColor: '#22c55e33'
626
+ },
627
+ readyBadge: {
628
+ backgroundColor: '#f59e0b33'
629
+ },
630
+ statusBadgeText: {
631
+ fontSize: 11,
632
+ fontWeight: '600',
633
+ color: '#fff'
634
+ },
635
+ bundlePlatform: {
636
+ color: '#888',
637
+ fontSize: 12,
638
+ marginBottom: 4
639
+ },
640
+ bundleNote: {
641
+ color: '#aaa',
642
+ fontSize: 13,
643
+ marginBottom: 8
644
+ },
645
+ bundleFooter: {
646
+ flexDirection: 'row',
647
+ justifyContent: 'space-between',
648
+ alignItems: 'center',
649
+ marginTop: 8
650
+ },
651
+ bundleSize: {
652
+ color: '#666',
653
+ fontSize: 12
654
+ },
655
+ downloadButton: {
656
+ backgroundColor: '#007AFF',
657
+ paddingHorizontal: 16,
658
+ paddingVertical: 8,
659
+ borderRadius: 6
660
+ },
661
+ downloadButtonText: {
662
+ color: '#fff',
663
+ fontSize: 13,
664
+ fontWeight: '600'
665
+ }
666
+ });
667
+ //# sourceMappingURL=SwiftPatchModal.js.map