@hot-updater/react-native 0.18.5 → 0.19.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 (51) hide show
  1. package/android/src/main/java/com/hotupdater/HotUpdater.kt +7 -0
  2. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +13 -0
  3. package/android/src/newarch/HotUpdaterModule.kt +1 -0
  4. package/android/src/oldarch/HotUpdaterModule.kt +1 -0
  5. package/ios/HotUpdater/Internal/HotUpdater.mm +2 -1
  6. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +8 -2
  7. package/lib/commonjs/checkForUpdate.js +27 -20
  8. package/lib/commonjs/checkForUpdate.js.map +1 -1
  9. package/lib/commonjs/fetchUpdateInfo.js +53 -48
  10. package/lib/commonjs/fetchUpdateInfo.js.map +1 -1
  11. package/lib/commonjs/index.js +0 -12
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/native.js +5 -19
  14. package/lib/commonjs/native.js.map +1 -1
  15. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  16. package/lib/module/checkForUpdate.js +28 -21
  17. package/lib/module/checkForUpdate.js.map +1 -1
  18. package/lib/module/fetchUpdateInfo.js +53 -48
  19. package/lib/module/fetchUpdateInfo.js.map +1 -1
  20. package/lib/module/index.js +1 -13
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/native.js +4 -17
  23. package/lib/module/native.js.map +1 -1
  24. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  25. package/lib/typescript/commonjs/checkForUpdate.d.ts +11 -2
  26. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +9 -3
  28. package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/index.d.ts +0 -12
  30. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/native.d.ts +1 -5
  32. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +1 -0
  34. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  35. package/lib/typescript/module/checkForUpdate.d.ts +11 -2
  36. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  37. package/lib/typescript/module/fetchUpdateInfo.d.ts +9 -3
  38. package/lib/typescript/module/fetchUpdateInfo.d.ts.map +1 -1
  39. package/lib/typescript/module/index.d.ts +0 -12
  40. package/lib/typescript/module/index.d.ts.map +1 -1
  41. package/lib/typescript/module/native.d.ts +1 -5
  42. package/lib/typescript/module/native.d.ts.map +1 -1
  43. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +1 -0
  44. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  45. package/package.json +10 -5
  46. package/plugin/build/withHotUpdater.js +138 -7
  47. package/src/checkForUpdate.ts +44 -35
  48. package/src/fetchUpdateInfo.ts +65 -51
  49. package/src/index.ts +0 -13
  50. package/src/native.ts +4 -22
  51. package/src/specs/NativeHotUpdater.ts +1 -0
@@ -1,12 +1,12 @@
1
- import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
1
+ import type { AppUpdateInfo, UpdateBundleParams } from "@hot-updater/core";
2
2
  import { Platform } from "react-native";
3
3
  import { HotUpdaterError } from "./error";
4
4
  import { type UpdateSource, fetchUpdateInfo } from "./fetchUpdateInfo";
5
5
  import {
6
- HotUpdaterConstants,
7
6
  getAppVersion,
8
7
  getBundleId,
9
8
  getChannel,
9
+ getFingerprintHash,
10
10
  getMinBundleId,
11
11
  updateBundle,
12
12
  } from "./native";
@@ -55,30 +55,22 @@ export async function checkForUpdate(
55
55
  return null;
56
56
  }
57
57
 
58
- const baseArgs = {
59
- bundleId: currentBundleId,
60
- platform,
61
- minBundleId,
62
- channel: channel ?? undefined,
63
- };
58
+ const fingerprintHash = getFingerprintHash();
64
59
 
65
- return fetchUpdateInfo(
66
- options.source,
67
- HotUpdaterConstants.UPDATE_STRATEGY === "appVersion"
68
- ? {
69
- _updateStrategy: HotUpdaterConstants.UPDATE_STRATEGY,
70
- appVersion: currentAppVersion,
71
- ...baseArgs,
72
- }
73
- : {
74
- _updateStrategy: HotUpdaterConstants.UPDATE_STRATEGY,
75
- fingerprintHash: HotUpdaterConstants.FINGERPRINT_HASH!,
76
- ...baseArgs,
77
- },
78
- options.requestHeaders,
79
- options.onError,
80
- options.requestTimeout,
81
- ).then((updateInfo) => {
60
+ return fetchUpdateInfo({
61
+ source: options.source,
62
+ params: {
63
+ bundleId: currentBundleId,
64
+ appVersion: currentAppVersion,
65
+ platform,
66
+ minBundleId,
67
+ channel,
68
+ fingerprintHash,
69
+ },
70
+ requestHeaders: options.requestHeaders,
71
+ onError: options.onError,
72
+ requestTimeout: options.requestTimeout,
73
+ }).then((updateInfo) => {
82
74
  if (!updateInfo) {
83
75
  return null;
84
76
  }
@@ -96,13 +88,30 @@ export async function checkForUpdate(
96
88
  });
97
89
  }
98
90
 
99
- export const getUpdateSource = (baseUrl: string) => (args: GetBundlesArgs) => {
100
- switch (args._updateStrategy) {
101
- case "appVersion":
102
- return `${baseUrl}/app-version/${args.platform}/${args.appVersion}/${args.channel}/${args.minBundleId}/${args.bundleId}`;
103
- case "fingerprint":
104
- return `${baseUrl}/fingerprint/${args.platform}/${args.fingerprintHash}/${args.channel}/${args.minBundleId}/${args.bundleId}`;
105
- default:
106
- return baseUrl;
107
- }
108
- };
91
+ export interface GetUpdateSourceOptions {
92
+ /**
93
+ * The update strategy to use.
94
+ * @description
95
+ * - "fingerprint": Use the fingerprint hash to check for updates.
96
+ * - "appVersion": Use the target app version to check for updates.
97
+ */
98
+ updateStrategy: "appVersion" | "fingerprint";
99
+ }
100
+
101
+ export const getUpdateSource =
102
+ (baseUrl: string, options: GetUpdateSourceOptions) =>
103
+ (params: UpdateBundleParams) => {
104
+ switch (options.updateStrategy) {
105
+ case "fingerprint": {
106
+ if (!params.fingerprintHash) {
107
+ throw new HotUpdaterError("Fingerprint hash is required");
108
+ }
109
+ return `${baseUrl}/fingerprint/${params.platform}/${params.fingerprintHash}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
110
+ }
111
+ case "appVersion": {
112
+ return `${baseUrl}/app-version/${params.platform}/${params.appVersion}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
113
+ }
114
+ default:
115
+ return baseUrl;
116
+ }
117
+ };
@@ -1,65 +1,79 @@
1
- import type { AppUpdateInfo, GetBundlesArgs } from "@hot-updater/core";
1
+ import type {
2
+ AppUpdateInfo,
3
+ UpdateBundleParams,
4
+ UpdateStrategy,
5
+ } from "@hot-updater/core";
2
6
 
3
7
  export type UpdateSource =
4
8
  | string
5
- | ((args: GetBundlesArgs) => Promise<AppUpdateInfo | null>)
6
- | ((args: GetBundlesArgs) => string);
9
+ | ((params: UpdateBundleParams) => Promise<AppUpdateInfo | null>)
10
+ | ((params: UpdateBundleParams) => string);
7
11
 
8
- export const fetchUpdateInfo = async (
9
- source: UpdateSource,
10
- args: GetBundlesArgs,
12
+ function buildRequestHeaders(
13
+ params: UpdateBundleParams,
11
14
  requestHeaders?: Record<string, string>,
12
- onError?: (error: Error) => void,
13
- requestTimeout = 5000,
14
- ): Promise<AppUpdateInfo | null> => {
15
- if (typeof source === "function") {
16
- const url = source(args);
17
- if (typeof url !== "string") {
18
- return null;
19
- }
20
- source = url;
21
- }
15
+ ): Record<string, string> {
16
+ const updateStrategy: UpdateStrategy = params.fingerprintHash
17
+ ? "fingerprint"
18
+ : "appVersion";
22
19
 
23
- const controller = new AbortController();
24
- const timeoutId = setTimeout(() => {
25
- controller.abort();
26
- }, requestTimeout);
20
+ return {
21
+ "Content-Type": "application/json",
22
+ "x-app-platform": params.platform,
23
+ "x-bundle-id": params.bundleId,
24
+ ...(updateStrategy === "fingerprint"
25
+ ? { "x-fingerprint-hash": params.fingerprintHash! }
26
+ : { "x-app-version": params.appVersion }),
27
+ ...(params.minBundleId && { "x-min-bundle-id": params.minBundleId }),
28
+ ...(params.channel && { "x-channel": params.channel }),
29
+ ...(requestHeaders ?? {}),
30
+ };
31
+ }
27
32
 
28
- try {
29
- let headers: Record<string, string> = {};
33
+ async function resolveSource(
34
+ source: UpdateSource,
35
+ params: UpdateBundleParams,
36
+ ): Promise<{ url: string } | { info: AppUpdateInfo | null }> {
37
+ if (typeof source !== "function") {
38
+ return { url: source };
39
+ }
40
+ const result = source(params);
41
+ if (typeof result === "string") {
42
+ return { url: result };
43
+ }
44
+ return { info: await result };
45
+ }
30
46
 
31
- switch (args._updateStrategy) {
32
- case "fingerprint":
33
- headers = {
34
- "Content-Type": "application/json",
35
- "x-app-platform": args.platform,
36
- "x-bundle-id": args.bundleId,
37
- "x-fingerprint-hash": args.fingerprintHash,
38
- ...(args.minBundleId ? { "x-min-bundle-id": args.minBundleId } : {}),
39
- ...(args.channel ? { "x-channel": args.channel } : {}),
40
- ...requestHeaders,
41
- };
42
- break;
43
- case "appVersion":
44
- headers = {
45
- "Content-Type": "application/json",
46
- "x-app-platform": args.platform,
47
- "x-bundle-id": args.bundleId,
48
- "x-app-version": args.appVersion,
49
- ...(args.minBundleId ? { "x-min-bundle-id": args.minBundleId } : {}),
50
- ...(args.channel ? { "x-channel": args.channel } : {}),
51
- ...requestHeaders,
52
- };
53
- break;
54
- default:
55
- throw new Error("Invalid update strategy");
47
+ export const fetchUpdateInfo = async ({
48
+ source,
49
+ params,
50
+ requestHeaders,
51
+ onError,
52
+ requestTimeout = 5000,
53
+ }: {
54
+ source: UpdateSource;
55
+ params: UpdateBundleParams;
56
+ requestHeaders?: Record<string, string>;
57
+ onError?: (error: Error) => void;
58
+ requestTimeout?: number;
59
+ }): Promise<AppUpdateInfo | null> => {
60
+ try {
61
+ const resolvedSource = await resolveSource(source, params);
62
+ if ("info" in resolvedSource) {
63
+ return resolvedSource.info;
56
64
  }
57
65
 
58
- const response = await fetch(source, {
66
+ const controller = new AbortController();
67
+ const timeoutId = setTimeout(() => {
68
+ controller.abort();
69
+ }, requestTimeout);
70
+
71
+ const headers = buildRequestHeaders(params, requestHeaders);
72
+
73
+ const response = await fetch(resolvedSource.url, {
59
74
  signal: controller.signal,
60
75
  headers,
61
76
  });
62
-
63
77
  clearTimeout(timeoutId);
64
78
 
65
79
  if (response.status !== 200) {
@@ -69,9 +83,9 @@ export const fetchUpdateInfo = async (
69
83
  } catch (error: any) {
70
84
  if (error.name === "AbortError") {
71
85
  onError?.(new Error("Request timed out"));
72
- return null;
86
+ } else {
87
+ onError?.(error as Error);
73
88
  }
74
- onError?.(error as Error);
75
89
  return null;
76
90
  }
77
91
  };
package/src/index.ts CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  getChannel,
7
7
  getFingerprintHash,
8
8
  getMinBundleId,
9
- getReleaseChannel,
10
9
  reload,
11
10
  updateBundle,
12
11
  } from "./native";
@@ -79,18 +78,6 @@ export const HotUpdater = {
79
78
  * ```
80
79
  */
81
80
  getChannel,
82
- /**
83
- * The initial channel of the native app.
84
- *
85
- * @returns {string} The current release channel of the app
86
- * @default "production"
87
- * @example
88
- * ```ts
89
- * const channel = HotUpdater.getReleaseChannel();
90
- * console.log(`Current release channel: ${channel}`);
91
- * ```
92
- */
93
- getReleaseChannel,
94
81
  /**
95
82
  * Adds a listener to HotUpdater events.
96
83
  *
package/src/native.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { UpdateStatus, UpdateStrategy } from "@hot-updater/core";
2
- import { NativeEventEmitter, Platform } from "react-native";
1
+ import type { UpdateStatus } from "@hot-updater/core";
2
+ import { NativeEventEmitter } from "react-native";
3
3
  import HotUpdaterNative, {
4
4
  type UpdateBundleParams,
5
5
  } from "./specs/NativeHotUpdater";
@@ -7,20 +7,9 @@ import HotUpdaterNative, {
7
7
  const NIL_UUID = "00000000-0000-0000-0000-000000000000";
8
8
 
9
9
  declare const __HOT_UPDATER_BUNDLE_ID: string | undefined;
10
- declare const __HOT_UPDATER_FINGERPRINT_HASH_IOS: string | null;
11
- declare const __HOT_UPDATER_FINGERPRINT_HASH_ANDROID: string | null;
12
- declare const __HOT_UPDATER_UPDATE_STRATEGY: UpdateStrategy;
13
- declare const __HOT_UPDATER_CHANNEL: string | null;
14
10
 
15
11
  export const HotUpdaterConstants = {
16
- OVER_THE_AIR_CHANNEL: __HOT_UPDATER_CHANNEL,
17
12
  HOT_UPDATER_BUNDLE_ID: __HOT_UPDATER_BUNDLE_ID || NIL_UUID,
18
- FINGERPRINT_HASH: Platform.select({
19
- ios: __HOT_UPDATER_FINGERPRINT_HASH_IOS,
20
- android: __HOT_UPDATER_FINGERPRINT_HASH_ANDROID,
21
- default: null,
22
- }),
23
- UPDATE_STRATEGY: __HOT_UPDATER_UPDATE_STRATEGY,
24
13
  };
25
14
 
26
15
  export type HotUpdaterEvent = {
@@ -141,14 +130,6 @@ export const getBundleId = (): string => {
141
130
  * @returns {string} Resolves with the channel or null if not available.
142
131
  */
143
132
  export const getChannel = (): string => {
144
- if (HotUpdaterConstants.OVER_THE_AIR_CHANNEL) {
145
- return HotUpdaterConstants.OVER_THE_AIR_CHANNEL;
146
- }
147
- const constants = HotUpdaterNative.getConstants();
148
- return constants.CHANNEL;
149
- };
150
-
151
- export const getReleaseChannel = (): string => {
152
133
  const constants = HotUpdaterNative.getConstants();
153
134
  return constants.CHANNEL;
154
135
  };
@@ -159,5 +140,6 @@ export const getReleaseChannel = (): string => {
159
140
  * @returns {string | null} Resolves with the fingerprint hash
160
141
  */
161
142
  export const getFingerprintHash = (): string | null => {
162
- return HotUpdaterConstants.FINGERPRINT_HASH;
143
+ const constants = HotUpdaterNative.getConstants();
144
+ return constants.FINGERPRINT_HASH;
163
145
  };
@@ -18,6 +18,7 @@ export interface Spec extends TurboModule {
18
18
  MIN_BUNDLE_ID: string;
19
19
  APP_VERSION: string | null;
20
20
  CHANNEL: string;
21
+ FINGERPRINT_HASH: string | null;
21
22
  };
22
23
  }
23
24