@hot-updater/react-native 1.1.1 → 1.1.2

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 (44) hide show
  1. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +1 -1
  2. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +91 -4
  3. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +27 -5
  4. package/android/src/main/java/com/hotupdater/HotUpdaterRestartActivity.kt +39 -0
  5. package/android/src/main/java/com/hotupdater/NativeUpdateCheck.kt +2 -0
  6. package/android/src/main/java/com/hotupdater/NativeUpdateChecker.kt +9 -4
  7. package/android/src/main/java/com/hotupdater/PostCrashUpdateCheck.kt +195 -0
  8. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +125 -3
  9. package/ios/HotUpdater/Internal/HotUpdaterLaunchRecovery.swift +90 -10
  10. package/ios/HotUpdater/Internal/HotUpdaterNativeUpdates.swift +2 -0
  11. package/ios/HotUpdater/Internal/NativeUpdateCheck.swift +15 -4
  12. package/ios/HotUpdater/Internal/PostCrashUpdateCheck.swift +248 -0
  13. package/ios/HotUpdater/Package.swift +1 -0
  14. package/lib/commonjs/checkForUpdate.js +10 -1
  15. package/lib/commonjs/checkForUpdate.js.map +1 -1
  16. package/lib/commonjs/httpClient.js +1 -0
  17. package/lib/commonjs/httpClient.js.map +1 -1
  18. package/lib/commonjs/recoveryCheckConfig.js +58 -0
  19. package/lib/commonjs/recoveryCheckConfig.js.map +1 -0
  20. package/lib/commonjs/sdkVersion.js +1 -1
  21. package/lib/module/checkForUpdate.js +10 -1
  22. package/lib/module/checkForUpdate.js.map +1 -1
  23. package/lib/module/httpClient.js +1 -0
  24. package/lib/module/httpClient.js.map +1 -1
  25. package/lib/module/recoveryCheckConfig.js +54 -0
  26. package/lib/module/recoveryCheckConfig.js.map +1 -0
  27. package/lib/module/sdkVersion.js +1 -1
  28. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/httpClient.d.ts +2 -0
  30. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/recoveryCheckConfig.d.ts +28 -0
  32. package/lib/typescript/commonjs/recoveryCheckConfig.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  34. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  35. package/lib/typescript/module/httpClient.d.ts +2 -0
  36. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  37. package/lib/typescript/module/recoveryCheckConfig.d.ts +28 -0
  38. package/lib/typescript/module/recoveryCheckConfig.d.ts.map +1 -0
  39. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  40. package/package.json +4 -4
  41. package/src/checkForUpdate.ts +10 -1
  42. package/src/httpClient.ts +3 -0
  43. package/src/recoveryCheckConfig.ts +68 -0
  44. package/src/sdkVersion.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/react-native",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "React Native OTA solution for self-hosted",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/commonjs/index.js",
@@ -124,9 +124,9 @@
124
124
  },
125
125
  "dependencies": {
126
126
  "use-sync-external-store": "1.5.0",
127
- "@hot-updater/protocol": "1.1.1",
128
- "@hot-updater/plugin-remote-config": "1.1.1",
129
- "@hot-updater/plugin-insights": "1.1.1"
127
+ "@hot-updater/protocol": "1.1.2",
128
+ "@hot-updater/plugin-remote-config": "1.1.2",
129
+ "@hot-updater/plugin-insights": "1.1.2"
130
130
  },
131
131
  "scripts": {
132
132
  "build": "node ./scripts/sync-sdk-version.mjs run -- bob build",
@@ -37,6 +37,7 @@ import {
37
37
  stageBundle,
38
38
  supportsArtifactFiles,
39
39
  } from "./native";
40
+ import { saveRecoveryCheckConfig } from "./recoveryCheckConfig";
40
41
  import { hotUpdaterStore } from "./store";
41
42
  import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
42
43
 
@@ -214,9 +215,17 @@ async function checkForReleaseCatalogUpdate(input: {
214
215
  requestTimeout: options.requestTimeout,
215
216
  updateStrategy: options.updateStrategy,
216
217
  });
218
+ if (catalog !== null) validateCatalog(catalog, expectedScope);
219
+ // The server answered with a valid catalog, or with none yet: only such a
220
+ // request replaces the saved one, so one that fails keeps the last.
221
+ saveRecoveryCheckConfig({
222
+ baseURL: session.baseURL,
223
+ requestHeaders: options.requestHeaders,
224
+ requestTimeout: options.requestTimeout,
225
+ updateStrategy: options.updateStrategy,
226
+ });
217
227
  // The server says this scope has no catalog yet: no update, not a failure.
218
228
  if (catalog === null) return null;
219
- validateCatalog(catalog, expectedScope);
220
229
  const catalogId = catalog.catalogId;
221
230
  const scopeKey = catalog.scopeKey;
222
231
 
package/src/httpClient.ts CHANGED
@@ -59,6 +59,8 @@ export type ResolvedArtifact =
59
59
  };
60
60
 
61
61
  export interface HotUpdaterHttpSession {
62
+ /** The server this session requests, with a baseURL function resolved. */
63
+ readonly baseURL: string;
62
64
  /** Resolves to null when the server says the scope has no catalog, so no update. */
63
65
  fetchReleaseCatalog: (
64
66
  params: ReleaseCatalogRequest,
@@ -143,6 +145,7 @@ const createSession = (
143
145
  baseURL: string,
144
146
  onResponse?: (response: UpdateHttpResponse) => void,
145
147
  ): HotUpdaterHttpSession => ({
148
+ baseURL,
146
149
  fetchReleaseCatalog: async (params): Promise<ReleaseCatalog | null> => {
147
150
  const channelKey = encodeChannelKey(params.channel);
148
151
  let strategyValue: string;
@@ -0,0 +1,68 @@
1
+ import type { CheckForUpdateOptions } from "./checkForUpdate";
2
+ import { DEFAULT_REQUEST_TIMEOUT_MS } from "./fetchUpdateResponse";
3
+ import { setStorageItem } from "./native";
4
+
5
+ /**
6
+ * The native key-value entry native reads after a crash: once a launch of the
7
+ * app ends in a crash that doesn't roll back, the next launch checks for an
8
+ * update natively, without the app's JavaScript, whose own update flow may be
9
+ * what crashes. Native knows no server of its own, so the app's update checks
10
+ * leave it here. A device that never checked, such as one whose user hasn't
11
+ * accepted the privacy terms yet, has none, so native sends no request.
12
+ */
13
+ export const RECOVERY_CHECK_CONFIG_KEY = "hot-updater/recovery-check-config";
14
+
15
+ /** What native reads; native rejects any other `version`. */
16
+ interface RecoveryCheckConfig {
17
+ readonly version: 1;
18
+ readonly baseURL: string;
19
+ readonly requestHeaders: Record<string, string>;
20
+ readonly requestTimeoutMs: number;
21
+ readonly updateStrategy: CheckForUpdateOptions["updateStrategy"];
22
+ }
23
+
24
+ const write = (config: RecoveryCheckConfig) => {
25
+ // An update check never fails because native couldn't keep this.
26
+ try {
27
+ setStorageItem(RECOVERY_CHECK_CONFIG_KEY, JSON.stringify(config));
28
+ } catch (error) {
29
+ console.warn(
30
+ "[HotUpdater] Failed to save the recovery check config",
31
+ error,
32
+ );
33
+ }
34
+ };
35
+
36
+ /**
37
+ * The headers as the request sent them: `Headers` lowercases names and
38
+ * joins two spellings of one name, as the catalog request did, so native
39
+ * repeats the same values instead of picking one of the spellings.
40
+ */
41
+ const sentHeaders = (headers: Record<string, string> | undefined) =>
42
+ Object.fromEntries(new Headers(headers).entries());
43
+
44
+ /**
45
+ * Saves the request of an update check the server answered: the server it
46
+ * resolved, its headers and timeout, and its update strategy, which only a
47
+ * check knows. Only answered checks save it, so native always repeats a
48
+ * request that reached the server, whichever instance made it last, and a
49
+ * device that never checked sends nothing. Native checks of main save theirs
50
+ * the same way. The channel is left out on purpose: native checks
51
+ * the device's own channel, the one the crashing bundle came from and its fix
52
+ * is published to. A check of another channel is a one-off until its update
53
+ * is applied, and a crash must not switch the device's channel.
54
+ */
55
+ export const saveRecoveryCheckConfig = (input: {
56
+ readonly baseURL: string;
57
+ readonly requestHeaders?: Record<string, string>;
58
+ readonly requestTimeout?: number;
59
+ readonly updateStrategy: CheckForUpdateOptions["updateStrategy"];
60
+ }) => {
61
+ write({
62
+ version: 1,
63
+ baseURL: input.baseURL,
64
+ requestHeaders: sentHeaders(input.requestHeaders),
65
+ requestTimeoutMs: input.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS,
66
+ updateStrategy: input.updateStrategy,
67
+ });
68
+ };
package/src/sdkVersion.ts CHANGED
@@ -1 +1 @@
1
- export const HOT_UPDATER_SDK_VERSION = "1.1.1";
1
+ export const HOT_UPDATER_SDK_VERSION = "1.1.2";