@hot-updater/react-native 0.12.7 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/android/generated/java/com/hotupdater/NativeHotUpdaterSpec.java +32 -0
  2. package/android/generated/jni/HotUpdaterSpec-generated.cpp +6 -0
  3. package/android/generated/jni/react/renderer/components/HotUpdaterSpec/HotUpdaterSpecJSI-generated.cpp +6 -0
  4. package/android/generated/jni/react/renderer/components/HotUpdaterSpec/HotUpdaterSpecJSI.h +9 -0
  5. package/android/src/main/java/com/hotupdater/HotUpdater.kt +47 -1
  6. package/android/src/newarch/HotUpdaterModule.kt +6 -0
  7. package/android/src/newarch/ReactIntegrationManager.kt +28 -6
  8. package/android/src/oldarch/HotUpdaterModule.kt +16 -0
  9. package/android/src/oldarch/ReactIntegrationManager.kt +15 -1
  10. package/dist/checkForUpdate.d.ts +2 -3
  11. package/dist/fetchUpdateInfo.d.ts +2 -0
  12. package/dist/index.d.ts +124 -0
  13. package/dist/index.js +68 -47
  14. package/dist/index.mjs +67 -47
  15. package/dist/native.d.ts +8 -0
  16. package/dist/runUpdateProcess.d.ts +4 -5
  17. package/dist/store.d.ts +1 -1
  18. package/dist/wrap.d.ts +2 -1
  19. package/ios/HotUpdater/HotUpdater.mm +68 -5
  20. package/ios/generated/HotUpdaterSpec/HotUpdaterSpec-generated.mm +7 -0
  21. package/ios/generated/HotUpdaterSpec/HotUpdaterSpec.h +37 -1
  22. package/ios/generated/HotUpdaterSpecJSI-generated.cpp +6 -0
  23. package/ios/generated/HotUpdaterSpecJSI.h +9 -0
  24. package/package.json +5 -5
  25. package/src/checkForUpdate.ts +14 -22
  26. package/src/fetchUpdateInfo.ts +22 -0
  27. package/src/index.ts +129 -3
  28. package/src/native.ts +21 -1
  29. package/src/runUpdateProcess.ts +11 -10
  30. package/src/specs/NativeHotUpdater.ts +3 -0
  31. package/src/store.ts +4 -4
  32. package/src/wrap.tsx +19 -4
  33. package/dist/ensureUpdateInfo.d.ts +0 -2
  34. package/src/ensureUpdateInfo.ts +0 -36
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as __WEBPACK_EXTERNAL_MODULE_react_native_4af9217e__ from "react-native";
2
- import * as __WEBPACK_EXTERNAL_MODULE__hot_updater_js_db235456__ from "@hot-updater/js";
3
2
  import * as __WEBPACK_EXTERNAL_MODULE_use_sync_external_store_shim_with_selector_83d70c15__ from "use-sync-external-store/shim/with-selector";
4
3
  import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react";
5
4
  var __webpack_modules__ = {
@@ -36,33 +35,36 @@ function __webpack_require__(moduleId) {
36
35
  __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
37
36
  })();
38
37
  var external_react_native_ = __webpack_require__("react-native");
39
- const ensureUpdateInfo = async (source, { appVersion, bundleId, platform }, requestHeaders)=>{
40
- try {
41
- let bundles = null;
42
- if ("string" == typeof source) {
43
- if (source.startsWith("http")) return await fetch(source, {
44
- headers: {
45
- "x-app-platform": platform,
46
- "x-app-version": appVersion,
47
- "x-bundle-id": bundleId,
48
- ...requestHeaders
49
- }
50
- }).then((res)=>res.json());
51
- } else bundles = "function" == typeof source ? await source() : source;
52
- return bundles ?? [];
53
- } catch {
54
- return [];
55
- }
56
- };
57
38
  class HotUpdaterError extends Error {
58
39
  constructor(message){
59
40
  super(message);
60
41
  this.name = "HotUpdaterError";
61
42
  }
62
43
  }
44
+ const fetchUpdateInfo = async (source, { appVersion, bundleId, platform, minBundleId, channel }, requestHeaders)=>{
45
+ try {
46
+ return fetch(source, {
47
+ headers: {
48
+ "x-app-platform": platform,
49
+ "x-app-version": appVersion,
50
+ "x-bundle-id": bundleId,
51
+ ...minBundleId ? {
52
+ "x-min-bundle-id": minBundleId
53
+ } : {},
54
+ ...channel ? {
55
+ "x-channel": channel
56
+ } : {},
57
+ ...requestHeaders
58
+ }
59
+ }).then((res)=>200 === res.status ? res.json() : null);
60
+ } catch {
61
+ return null;
62
+ }
63
+ };
63
64
  const NIL_UUID = "00000000-0000-0000-0000-000000000000";
64
65
  const HotUpdater = {
65
- HOT_UPDATER_BUNDLE_ID: NIL_UUID
66
+ HOT_UPDATER_BUNDLE_ID: NIL_UUID,
67
+ CHANNEL: "production"
66
68
  };
67
69
  const LINKING_ERROR = `The package '@hot-updater/react-native' doesn't seem to be linked. Make sure: \n\n` + external_react_native_.Platform.select({
68
70
  ios: "- You have run 'pod install'\n",
@@ -89,7 +91,15 @@ const reload = ()=>{
89
91
  HotUpdaterNative.reload();
90
92
  });
91
93
  };
92
- const getBundleId = ()=>HotUpdater.HOT_UPDATER_BUNDLE_ID;
94
+ const getMinBundleId = ()=>{
95
+ const constants = HotUpdaterNative.getConstants();
96
+ return constants.MIN_BUNDLE_ID;
97
+ };
98
+ const getBundleId = ()=>{
99
+ const minBundleId = getMinBundleId();
100
+ return minBundleId.localeCompare(HotUpdater.HOT_UPDATER_BUNDLE_ID) >= 0 ? minBundleId : HotUpdater.HOT_UPDATER_BUNDLE_ID;
101
+ };
102
+ const getChannel = ()=>HotUpdater.CHANNEL;
93
103
  async function checkForUpdate(config) {
94
104
  if (__DEV__) return null;
95
105
  if (![
@@ -98,28 +108,25 @@ async function checkForUpdate(config) {
98
108
  ].includes(external_react_native_.Platform.OS)) throw new HotUpdaterError("HotUpdater is only supported on iOS and Android");
99
109
  const currentAppVersion = await getAppVersion();
100
110
  const platform = external_react_native_.Platform.OS;
101
- const currentBundleId = await getBundleId();
111
+ const currentBundleId = getBundleId();
112
+ const minBundleId = getMinBundleId();
113
+ const channel = getChannel();
102
114
  if (!currentAppVersion) throw new HotUpdaterError("Failed to get app version");
103
- const ensuredUpdateInfo = await ensureUpdateInfo(config.source, {
115
+ return fetchUpdateInfo(config.source, {
104
116
  appVersion: currentAppVersion,
105
117
  bundleId: currentBundleId,
106
- platform
118
+ platform,
119
+ minBundleId,
120
+ channel
107
121
  }, config.requestHeaders);
108
- let updateInfo = null;
109
- if (Array.isArray(ensuredUpdateInfo)) {
110
- const bundles = ensuredUpdateInfo;
111
- updateInfo = await (0, __WEBPACK_EXTERNAL_MODULE__hot_updater_js_db235456__.getUpdateInfo)(bundles, {
112
- appVersion: currentAppVersion,
113
- bundleId: currentBundleId,
114
- platform
115
- });
116
- } else updateInfo = ensuredUpdateInfo;
117
- return updateInfo;
118
122
  }
119
123
  const runUpdateProcess = async ({ reloadOnForceUpdate = true, ...checkForUpdateConfig })=>{
120
124
  const updateInfo = await checkForUpdate(checkForUpdateConfig);
121
125
  if (!updateInfo) return {
122
- status: "UP_TO_DATE"
126
+ status: "UP_TO_DATE",
127
+ shouldForceUpdate: false,
128
+ message: null,
129
+ id: getBundleId()
123
130
  };
124
131
  const isUpdated = await updateBundle(updateInfo.id, updateInfo.fileUrl);
125
132
  if (isUpdated && updateInfo.shouldForceUpdate && reloadOnForceUpdate) reload();
@@ -127,7 +134,8 @@ const runUpdateProcess = async ({ reloadOnForceUpdate = true, ...checkForUpdateC
127
134
  return {
128
135
  status: updateInfo.status,
129
136
  shouldForceUpdate: updateInfo.shouldForceUpdate,
130
- id: updateInfo.id
137
+ id: updateInfo.id,
138
+ message: updateInfo.message
131
139
  };
132
140
  };
133
141
  const { useSyncExternalStoreWithSelector } = __WEBPACK_EXTERNAL_MODULE_use_sync_external_store_shim_with_selector_83d70c15__["default"];
@@ -141,10 +149,10 @@ const createHotUpdaterStore = ()=>{
141
149
  const emitChange = ()=>{
142
150
  for (const listener of listeners)listener();
143
151
  };
144
- const setProgress = (progress)=>{
152
+ const setState = (newState)=>{
145
153
  state = {
146
- isBundleUpdated: 1 === progress,
147
- progress
154
+ ...state,
155
+ ...newState
148
156
  };
149
157
  emitChange();
150
158
  };
@@ -154,7 +162,7 @@ const createHotUpdaterStore = ()=>{
154
162
  };
155
163
  return {
156
164
  getSnapshot,
157
- setProgress,
165
+ setState,
158
166
  subscribe
159
167
  };
160
168
  };
@@ -178,6 +186,7 @@ function wrap(config) {
178
186
  return (WrappedComponent)=>{
179
187
  const HotUpdaterHOC = ()=>{
180
188
  const progress = useHotUpdaterStore((state)=>state.progress);
189
+ const [message, setMessage] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(null);
181
190
  const [updateStatus, setUpdateStatus] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)("CHECK_FOR_UPDATE");
182
191
  const initHotUpdater = useEventCallback(async ()=>{
183
192
  try {
@@ -186,9 +195,13 @@ function wrap(config) {
186
195
  source: restConfig.source,
187
196
  requestHeaders: restConfig.requestHeaders
188
197
  });
198
+ setMessage(updateInfo?.message ?? null);
189
199
  if (!updateInfo) {
190
200
  restConfig.onUpdateProcessCompleted?.({
191
- status: "UP_TO_DATE"
201
+ status: "UP_TO_DATE",
202
+ shouldForceUpdate: false,
203
+ message: null,
204
+ id: getBundleId()
192
205
  });
193
206
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
194
207
  return;
@@ -198,7 +211,8 @@ function wrap(config) {
198
211
  restConfig.onUpdateProcessCompleted?.({
199
212
  id: updateInfo.id,
200
213
  status: updateInfo.status,
201
- shouldForceUpdate: updateInfo.shouldForceUpdate
214
+ shouldForceUpdate: updateInfo.shouldForceUpdate,
215
+ message: updateInfo.message
202
216
  });
203
217
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
204
218
  return;
@@ -210,7 +224,8 @@ function wrap(config) {
210
224
  restConfig.onUpdateProcessCompleted?.({
211
225
  id: updateInfo.id,
212
226
  status: updateInfo.status,
213
- shouldForceUpdate: updateInfo.shouldForceUpdate
227
+ shouldForceUpdate: updateInfo.shouldForceUpdate,
228
+ message: updateInfo.message
214
229
  });
215
230
  setUpdateStatus("UPDATE_PROCESS_COMPLETED");
216
231
  } catch (error) {
@@ -229,24 +244,29 @@ function wrap(config) {
229
244
  }, []);
230
245
  if (restConfig.fallbackComponent && "UPDATE_PROCESS_COMPLETED" !== updateStatus) {
231
246
  const Fallback = restConfig.fallbackComponent;
232
- return /*#__PURE__*/ React.createElement(Fallback, {
247
+ return /*#__PURE__*/ __WEBPACK_EXTERNAL_MODULE_react__["default"].createElement(Fallback, {
233
248
  progress: progress,
234
- status: updateStatus
249
+ status: updateStatus,
250
+ message: message
235
251
  });
236
252
  }
237
- return /*#__PURE__*/ React.createElement(WrappedComponent, null);
253
+ return /*#__PURE__*/ __WEBPACK_EXTERNAL_MODULE_react__["default"].createElement(WrappedComponent, null);
238
254
  };
239
255
  return HotUpdaterHOC;
240
256
  };
241
257
  }
242
258
  addListener("onProgress", ({ progress })=>{
243
- hotUpdaterStore.setProgress(progress);
259
+ hotUpdaterStore.setState({
260
+ progress
261
+ });
244
262
  });
245
263
  const src_HotUpdater = {
246
264
  wrap: wrap,
247
265
  reload: reload,
248
266
  getAppVersion: getAppVersion,
249
267
  getBundleId: getBundleId,
268
+ getMinBundleId: getMinBundleId,
269
+ getChannel: getChannel,
250
270
  addListener: addListener,
251
271
  checkForUpdate: checkForUpdate,
252
272
  runUpdateProcess: runUpdateProcess,
package/dist/native.d.ts CHANGED
@@ -20,6 +20,13 @@ export declare const getAppVersion: () => Promise<string | null>;
20
20
  * Reloads the app.
21
21
  */
22
22
  export declare const reload: () => void;
23
+ /**
24
+ * Fetches the minimum bundle id, which represents the initial bundle of the app
25
+ * since it is created at build time.
26
+ *
27
+ * @returns {string} Resolves with the minimum bundle id or null if not available.
28
+ */
29
+ export declare const getMinBundleId: () => string;
23
30
  /**
24
31
  * Fetches the current bundle version id.
25
32
  *
@@ -27,3 +34,4 @@ export declare const reload: () => void;
27
34
  * @returns {Promise<string>} Resolves with the current version id or null if not available.
28
35
  */
29
36
  export declare const getBundleId: () => string;
37
+ export declare const getChannel: () => string;
@@ -1,11 +1,10 @@
1
1
  import { type CheckForUpdateConfig } from "./checkForUpdate";
2
- export type RunUpdateProcessResponse = {
3
- status: "ROLLBACK" | "UPDATE";
2
+ export interface RunUpdateProcessResponse {
3
+ status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
4
4
  shouldForceUpdate: boolean;
5
+ message: string | null;
5
6
  id: string;
6
- } | {
7
- status: "UP_TO_DATE";
8
- };
7
+ }
9
8
  export interface RunUpdateProcessConfig extends CheckForUpdateConfig {
10
9
  /**
11
10
  * If `true`, the app will be reloaded when the downloaded bundle is a force update.
package/dist/store.d.ts CHANGED
@@ -4,7 +4,7 @@ export type HotUpdaterState = {
4
4
  };
5
5
  export declare const hotUpdaterStore: {
6
6
  getSnapshot: () => HotUpdaterState;
7
- setProgress: (progress: number) => void;
7
+ setState: (newState: Partial<HotUpdaterState>) => void;
8
8
  subscribe: (listener: () => void) => () => boolean;
9
9
  };
10
10
  export declare const useHotUpdaterStore: <T = HotUpdaterState>(selector?: (snapshot: HotUpdaterState) => T) => T;
package/dist/wrap.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type React from "react";
1
+ import React from "react";
2
2
  import { type CheckForUpdateConfig } from "./checkForUpdate";
3
3
  import { HotUpdaterError } from "./error";
4
4
  import type { RunUpdateProcessResponse } from "./runUpdateProcess";
@@ -28,6 +28,7 @@ export interface HotUpdaterConfig extends CheckForUpdateConfig {
28
28
  fallbackComponent?: React.FC<{
29
29
  status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
30
30
  progress: number;
31
+ message: string | null;
31
32
  }>;
32
33
  onError?: (error: HotUpdaterError) => void;
33
34
  onProgress?: (progress: number) => void;
@@ -21,6 +21,73 @@
21
21
 
22
22
  RCT_EXPORT_MODULE();
23
23
 
24
+ #pragma mark - React Native Constants
25
+
26
+ - (NSString *)getMinBundleId {
27
+ static NSString *uuid = nil;
28
+ static dispatch_once_t onceToken;
29
+ dispatch_once(&onceToken, ^{
30
+ NSDate *buildDate = nil;
31
+ NSURL *fallbackURL = nil;
32
+ #if DEBUG
33
+ uuid = @"00000000-0000-0000-0000-000000000000";
34
+ return;
35
+ #else
36
+ fallbackURL = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
37
+ #endif
38
+ if (fallbackURL) {
39
+ NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fallbackURL path] error:nil];
40
+ buildDate = attributes[NSFileModificationDate];
41
+ }
42
+ if (!buildDate) {
43
+ uuid = @"00000000-0000-0000-0000-000000000000";
44
+ return;
45
+ }
46
+
47
+ uint64_t buildTimestampMs = (uint64_t)([buildDate timeIntervalSince1970] * 1000.0);
48
+
49
+ unsigned char bytes[16];
50
+ bytes[0] = (buildTimestampMs >> 40) & 0xFF;
51
+ bytes[1] = (buildTimestampMs >> 32) & 0xFF;
52
+ bytes[2] = (buildTimestampMs >> 24) & 0xFF;
53
+ bytes[3] = (buildTimestampMs >> 16) & 0xFF;
54
+ bytes[4] = (buildTimestampMs >> 8) & 0xFF;
55
+ bytes[5] = buildTimestampMs & 0xFF;
56
+
57
+ bytes[6] = 0x70;
58
+ bytes[7] = 0x00;
59
+
60
+ bytes[8] = 0x80;
61
+ bytes[9] = 0x00;
62
+
63
+ bytes[10] = 0x00;
64
+ bytes[11] = 0x00;
65
+ bytes[12] = 0x00;
66
+ bytes[13] = 0x00;
67
+ bytes[14] = 0x00;
68
+ bytes[15] = 0x00;
69
+
70
+ uuid = [NSString stringWithFormat:
71
+ @"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
72
+ bytes[0], bytes[1], bytes[2], bytes[3],
73
+ bytes[4], bytes[5],
74
+ bytes[6], bytes[7],
75
+ bytes[8], bytes[9],
76
+ bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]];
77
+ });
78
+ return uuid;
79
+ }
80
+
81
+ - (NSDictionary *)constantsToExport {
82
+ return @{ @"MIN_BUNDLE_ID": [self getMinBundleId] };
83
+ }
84
+
85
+
86
+ - (NSDictionary*) getConstants {
87
+ return [self constantsToExport];
88
+ }
89
+
90
+
24
91
  #pragma mark - Bundle URL Management
25
92
 
26
93
  - (NSString *)getAppVersion {
@@ -49,11 +116,7 @@ RCT_EXPORT_MODULE();
49
116
  }
50
117
 
51
118
  + (NSURL *)fallbackURL {
52
- #if DEBUG
53
- return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
54
- #else
55
119
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
56
- #endif
57
120
  }
58
121
 
59
122
  + (NSURL *)bundleURL {
@@ -170,7 +233,7 @@ RCT_EXPORT_MODULE();
170
233
  }
171
234
  [fileManager createDirectoryAtPath:tempDir withIntermediateDirectories:YES attributes:nil error:nil];
172
235
 
173
- NSString *tempZipFile = [tempDir stringByAppendingPathComponent:@"build.zip"];
236
+ NSString *tempZipFile = [tempDir stringByAppendingPathComponent:@"bundle.zip"];
174
237
  NSString *extractedDir = [tempDir stringByAppendingPathComponent:@"extracted"];
175
238
  [fileManager createDirectoryAtPath:extractedDir withIntermediateDirectories:YES attributes:nil error:nil];
176
239
 
@@ -46,6 +46,10 @@ namespace facebook::react {
46
46
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "removeListeners", @selector(removeListeners:), args, count);
47
47
  }
48
48
 
49
+ static facebook::jsi::Value __hostFunction_NativeHotUpdaterSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
50
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, ObjectKind, "getConstants", @selector(getConstants), args, count);
51
+ }
52
+
49
53
  NativeHotUpdaterSpecJSI::NativeHotUpdaterSpecJSI(const ObjCTurboModule::InitParams &params)
50
54
  : ObjCTurboModule(params) {
51
55
 
@@ -63,5 +67,8 @@ namespace facebook::react {
63
67
 
64
68
  methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeHotUpdaterSpecJSI_removeListeners};
65
69
 
70
+
71
+ methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeHotUpdaterSpecJSI_getConstants};
72
+
66
73
  }
67
74
  } // namespace facebook::react
@@ -30,7 +30,33 @@
30
30
  #import <optional>
31
31
  #import <vector>
32
32
 
33
+ namespace JS {
34
+ namespace NativeHotUpdater {
35
+ struct Constants {
33
36
 
37
+ struct Builder {
38
+ struct Input {
39
+ RCTRequired<NSString *> MIN_BUNDLE_ID;
40
+ };
41
+
42
+ /** Initialize with a set of values */
43
+ Builder(const Input i);
44
+ /** Initialize with an existing Constants */
45
+ Builder(Constants i);
46
+ /** Builds the object. Generally used only by the infrastructure. */
47
+ NSDictionary *buildUnsafeRawValue() const { return _factory(); };
48
+ private:
49
+ NSDictionary *(^_factory)(void);
50
+ };
51
+
52
+ static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; }
53
+ NSDictionary *unsafeRawValue() const { return _v; }
54
+ private:
55
+ Constants(NSDictionary *const v) : _v(v) {}
56
+ NSDictionary *_v;
57
+ };
58
+ }
59
+ }
34
60
  @protocol NativeHotUpdaterSpec <RCTBridgeModule, RCTTurboModule>
35
61
 
36
62
  - (void)reload;
@@ -42,6 +68,8 @@
42
68
  reject:(RCTPromiseRejectBlock)reject;
43
69
  - (void)addListener:(NSString *)eventName;
44
70
  - (void)removeListeners:(double)count;
71
+ - (facebook::react::ModuleConstants<JS::NativeHotUpdater::Constants::Builder>)constantsToExport;
72
+ - (facebook::react::ModuleConstants<JS::NativeHotUpdater::Constants::Builder>)getConstants;
45
73
 
46
74
  @end
47
75
 
@@ -63,5 +91,13 @@ namespace facebook::react {
63
91
  NativeHotUpdaterSpecJSI(const ObjCTurboModule::InitParams &params);
64
92
  };
65
93
  } // namespace facebook::react
66
-
94
+ inline JS::NativeHotUpdater::Constants::Builder::Builder(const Input i) : _factory(^{
95
+ NSMutableDictionary *d = [NSMutableDictionary new];
96
+ auto MIN_BUNDLE_ID = i.MIN_BUNDLE_ID.get();
97
+ d[@"MIN_BUNDLE_ID"] = MIN_BUNDLE_ID;
98
+ return d;
99
+ }) {}
100
+ inline JS::NativeHotUpdater::Constants::Builder::Builder(Constants i) : _factory(^{
101
+ return i.unsafeRawValue();
102
+ }) {}
67
103
  #endif // HotUpdaterSpec_H
@@ -43,6 +43,11 @@ static jsi::Value __hostFunction_NativeHotUpdaterCxxSpecJSI_removeListeners(jsi:
43
43
  );
44
44
  return jsi::Value::undefined();
45
45
  }
46
+ static jsi::Value __hostFunction_NativeHotUpdaterCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
47
+ return static_cast<NativeHotUpdaterCxxSpecJSI *>(&turboModule)->getConstants(
48
+ rt
49
+ );
50
+ }
46
51
 
47
52
  NativeHotUpdaterCxxSpecJSI::NativeHotUpdaterCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
48
53
  : TurboModule("HotUpdater", jsInvoker) {
@@ -51,6 +56,7 @@ NativeHotUpdaterCxxSpecJSI::NativeHotUpdaterCxxSpecJSI(std::shared_ptr<CallInvok
51
56
  methodMap_["getAppVersion"] = MethodMetadata {0, __hostFunction_NativeHotUpdaterCxxSpecJSI_getAppVersion};
52
57
  methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeHotUpdaterCxxSpecJSI_addListener};
53
58
  methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeHotUpdaterCxxSpecJSI_removeListeners};
59
+ methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeHotUpdaterCxxSpecJSI_getConstants};
54
60
  }
55
61
 
56
62
 
@@ -25,6 +25,7 @@ public:
25
25
  virtual jsi::Value getAppVersion(jsi::Runtime &rt) = 0;
26
26
  virtual void addListener(jsi::Runtime &rt, jsi::String eventName) = 0;
27
27
  virtual void removeListeners(jsi::Runtime &rt, double count) = 0;
28
+ virtual jsi::Object getConstants(jsi::Runtime &rt) = 0;
28
29
 
29
30
  };
30
31
 
@@ -91,6 +92,14 @@ private:
91
92
  return bridging::callFromJs<void>(
92
93
  rt, &T::removeListeners, jsInvoker_, instance_, std::move(count));
93
94
  }
95
+ jsi::Object getConstants(jsi::Runtime &rt) override {
96
+ static_assert(
97
+ bridging::getParameterCount(&T::getConstants) == 1,
98
+ "Expected getConstants(...) to have 1 parameters");
99
+
100
+ return bridging::callFromJs<jsi::Object>(
101
+ rt, &T::getConstants, jsInvoker_, instance_);
102
+ }
94
103
 
95
104
  private:
96
105
  friend class NativeHotUpdaterCxxSpec;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/react-native",
3
- "version": "0.12.7",
3
+ "version": "0.13.1",
4
4
  "description": "React Native OTA solution for self-hosted",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -67,8 +67,8 @@
67
67
  "includesGeneratedCode": true
68
68
  },
69
69
  "peerDependencies": {
70
- "react-native": "*",
71
- "react": "*"
70
+ "react": "*",
71
+ "react-native": "*"
72
72
  },
73
73
  "devDependencies": {
74
74
  "@react-native-community/cli": "15.0.1",
@@ -81,8 +81,8 @@
81
81
  },
82
82
  "dependencies": {
83
83
  "use-sync-external-store": "1.4.0",
84
- "@hot-updater/js": "0.12.7",
85
- "@hot-updater/core": "0.12.7"
84
+ "@hot-updater/js": "0.13.1",
85
+ "@hot-updater/core": "0.13.1"
86
86
  },
87
87
  "scripts": {
88
88
  "build": "rslib build",
@@ -1,12 +1,15 @@
1
- import type { Bundle, BundleArg, UpdateInfo } from "@hot-updater/core";
2
- import { getUpdateInfo } from "@hot-updater/js";
3
1
  import { Platform } from "react-native";
4
- import { ensureUpdateInfo } from "./ensureUpdateInfo";
5
2
  import { HotUpdaterError } from "./error";
6
- import { getAppVersion, getBundleId } from "./native";
3
+ import { fetchUpdateInfo } from "./fetchUpdateInfo";
4
+ import {
5
+ getAppVersion,
6
+ getBundleId,
7
+ getChannel,
8
+ getMinBundleId,
9
+ } from "./native";
7
10
 
8
11
  export interface CheckForUpdateConfig {
9
- source: BundleArg;
12
+ source: string;
10
13
  requestHeaders?: Record<string, string>;
11
14
  }
12
15
 
@@ -23,34 +26,23 @@ export async function checkForUpdate(config: CheckForUpdateConfig) {
23
26
 
24
27
  const currentAppVersion = await getAppVersion();
25
28
  const platform = Platform.OS as "ios" | "android";
26
- const currentBundleId = await getBundleId();
29
+ const currentBundleId = getBundleId();
30
+ const minBundleId = getMinBundleId();
31
+ const channel = getChannel();
27
32
 
28
33
  if (!currentAppVersion) {
29
34
  throw new HotUpdaterError("Failed to get app version");
30
35
  }
31
36
 
32
- const ensuredUpdateInfo = await ensureUpdateInfo(
37
+ return fetchUpdateInfo(
33
38
  config.source,
34
39
  {
35
40
  appVersion: currentAppVersion,
36
41
  bundleId: currentBundleId,
37
42
  platform,
43
+ minBundleId,
44
+ channel,
38
45
  },
39
46
  config.requestHeaders,
40
47
  );
41
-
42
- let updateInfo: UpdateInfo | null = null;
43
- if (Array.isArray(ensuredUpdateInfo)) {
44
- const bundles: Bundle[] = ensuredUpdateInfo;
45
-
46
- updateInfo = await getUpdateInfo(bundles, {
47
- appVersion: currentAppVersion,
48
- bundleId: currentBundleId,
49
- platform,
50
- });
51
- } else {
52
- updateInfo = ensuredUpdateInfo;
53
- }
54
-
55
- return updateInfo;
56
48
  }
@@ -0,0 +1,22 @@
1
+ import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
2
+
3
+ export const fetchUpdateInfo = async (
4
+ source: string,
5
+ { appVersion, bundleId, platform, minBundleId, channel }: GetBundlesArgs,
6
+ requestHeaders?: Record<string, string>,
7
+ ): Promise<AppUpdateInfo | null> => {
8
+ try {
9
+ return fetch(source, {
10
+ headers: {
11
+ "x-app-platform": platform,
12
+ "x-app-version": appVersion,
13
+ "x-bundle-id": bundleId,
14
+ ...(minBundleId ? { "x-min-bundle-id": minBundleId } : {}),
15
+ ...(channel ? { "x-channel": channel } : {}),
16
+ ...requestHeaders,
17
+ },
18
+ }).then((res) => (res.status === 200 ? res.json() : null));
19
+ } catch {
20
+ return null;
21
+ }
22
+ };