@hot-updater/react-native 1.0.4 → 1.0.6

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 (68) hide show
  1. package/HotUpdater.podspec +24 -3
  2. package/Package.swift +100 -0
  3. package/android/build.gradle +3 -4
  4. package/android/hot-updater-builtin-assets.gradle +67 -0
  5. package/android/hot-updater-min-bundle-id.gradle +69 -0
  6. package/android/src/main/java/com/hotupdater/ArtifactFileResolver.kt +85 -0
  7. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +116 -11
  8. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +117 -12
  9. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +5 -52
  10. package/android/src/newarch/HotUpdaterModule.kt +46 -2
  11. package/android/src/oldarch/HotUpdaterModule.kt +46 -2
  12. package/ios/HotUpdater/Interface/HotUpdaterInterface.h +66 -0
  13. package/ios/HotUpdater/Interface/HotUpdaterInterface.m +3 -0
  14. package/ios/HotUpdater/Internal/ArtifactFileResolver.swift +87 -0
  15. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +88 -13
  16. package/ios/HotUpdater/Internal/HotUpdater.mm +41 -90
  17. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +25 -11
  18. package/ios/HotUpdater/Internal/HotUpdaterInterface.swift +26 -0
  19. package/ios/HotUpdater/Internal/HotUpdaterMinBundleId.m +10 -0
  20. package/ios/HotUpdater/Package.swift +88 -0
  21. package/ios/Plugins/HotUpdaterMinBundleIdPlugin/HotUpdaterMinBundleIdPlugin.swift +28 -0
  22. package/ios/scripts/generate-min-bundle-id.zsh +49 -0
  23. package/lib/commonjs/checkForUpdate.js +8 -0
  24. package/lib/commonjs/checkForUpdate.js.map +1 -1
  25. package/lib/commonjs/fetchUpdateResponse.js +5 -2
  26. package/lib/commonjs/fetchUpdateResponse.js.map +1 -1
  27. package/lib/commonjs/httpClient.js +64 -5
  28. package/lib/commonjs/httpClient.js.map +1 -1
  29. package/lib/commonjs/native.js +9 -0
  30. package/lib/commonjs/native.js.map +1 -1
  31. package/lib/commonjs/sdkVersion.js +1 -1
  32. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  33. package/lib/module/checkForUpdate.js +8 -0
  34. package/lib/module/checkForUpdate.js.map +1 -1
  35. package/lib/module/fetchUpdateResponse.js +4 -1
  36. package/lib/module/fetchUpdateResponse.js.map +1 -1
  37. package/lib/module/httpClient.js +67 -7
  38. package/lib/module/httpClient.js.map +1 -1
  39. package/lib/module/native.js +9 -0
  40. package/lib/module/native.js.map +1 -1
  41. package/lib/module/sdkVersion.js +1 -1
  42. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  43. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +2 -0
  45. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/httpClient.d.ts +18 -2
  47. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  50. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +13 -1
  51. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  52. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  53. package/lib/typescript/module/fetchUpdateResponse.d.ts +2 -0
  54. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -1
  55. package/lib/typescript/module/httpClient.d.ts +18 -2
  56. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  57. package/lib/typescript/module/native.d.ts.map +1 -1
  58. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  59. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +13 -1
  60. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  61. package/package.json +5 -4
  62. package/react-native.config.js +7 -0
  63. package/src/checkForUpdate.ts +11 -0
  64. package/src/fetchUpdateResponse.ts +4 -1
  65. package/src/httpClient.ts +84 -9
  66. package/src/native.ts +7 -0
  67. package/src/sdkVersion.ts +1 -1
  68. package/src/specs/NativeHotUpdater.ts +13 -1
@@ -18,6 +18,7 @@ import type {
18
18
  UpdateErrorStage,
19
19
  } from "./clientPlugin";
20
20
  import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
21
+ import { DEFAULT_REQUEST_TIMEOUT_MS } from "./fetchUpdateResponse";
21
22
  import type { HotUpdaterHttpClient } from "./httpClient";
22
23
  import {
23
24
  acceptReleaseCatalog,
@@ -392,6 +393,16 @@ async function checkForReleaseCatalogUpdate(input: {
392
393
  manifestFileHash: artifact.manifestFileHash,
393
394
  manifestUrl: artifact.manifestUrl,
394
395
  ...(artifact.archiveUrl ? { archiveUrl: artifact.archiveUrl } : {}),
396
+ ...(artifact.filesUrl
397
+ ? {
398
+ filesUrl: artifact.filesUrl,
399
+ ...(options.requestHeaders
400
+ ? { requestHeaders: options.requestHeaders }
401
+ : {}),
402
+ requestTimeout:
403
+ options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS,
404
+ }
405
+ : {}),
395
406
  selection: receipt,
396
407
  shouldSkipCurrentBundleIdCheck: true,
397
408
  status: desired.status,
@@ -8,12 +8,15 @@ export interface UpdateRequest {
8
8
  readonly onResponse?: (response: UpdateHttpResponse) => void;
9
9
  }
10
10
 
11
+ /** How long an update request may take when the app sets no `requestTimeout`. */
12
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 5000;
13
+
11
14
  /** Read once for parsing and diagnostics, under the same request timeout. */
12
15
  export const fetchUpdateResponse = async ({
13
16
  url,
14
17
  resource,
15
18
  requestHeaders,
16
- requestTimeout = 5000,
19
+ requestTimeout = DEFAULT_REQUEST_TIMEOUT_MS,
17
20
  onResponse,
18
21
  }: UpdateRequest): Promise<{ response: Response; body: string | null }> => {
19
22
  const controller = new AbortController();
package/src/httpClient.ts CHANGED
@@ -1,15 +1,19 @@
1
1
  import {
2
+ ARTIFACT_FILES_PROTOCOL_VERSION,
2
3
  ARTIFACT_PROTOCOL_VERSION,
3
4
  canonicalizeAppVersion,
4
5
  encodeChannelKey,
5
6
  resolveBaseURL,
7
+ type ArtifactAssetFile,
6
8
  type ArtifactInfo,
9
+ type ArtifactInfoV2,
10
+ type ChangedAssetPatch,
7
11
  type HotUpdaterBaseURL,
8
12
  type ReleaseCatalog,
9
13
  type UpdateHttpResponse,
10
14
  } from "@hot-updater/protocol";
11
15
 
12
- import { fetchJSON } from "./fetchJSON";
16
+ import { fetchJSON, FetchJSONResponseError } from "./fetchJSON";
13
17
  import { fetchReleaseCatalogWithCache } from "./releaseCatalogCache";
14
18
  import { InvalidUpdateResponseError } from "./updateError";
15
19
 
@@ -30,12 +34,32 @@ export interface ArtifactRequest {
30
34
  readonly requestTimeout?: number;
31
35
  }
32
36
 
37
+ /**
38
+ * How native downloads a Bundle. In artifact protocol v1, `assets` holds a
39
+ * URL for every file. In v2, it holds only the Hermes patch, and native asks
40
+ * `filesUrl` for the URLs of the files it downloads one by one.
41
+ */
42
+ export interface ResolvedArtifact {
43
+ readonly manifestUrl: string;
44
+ readonly manifestFileHash: string;
45
+ readonly archiveUrl?: string;
46
+ readonly assets: Record<
47
+ string,
48
+ {
49
+ readonly file?: ArtifactAssetFile;
50
+ readonly fileHash?: string;
51
+ readonly patch?: ChangedAssetPatch | null;
52
+ }
53
+ >;
54
+ readonly filesUrl?: string;
55
+ }
56
+
33
57
  export interface HotUpdaterHttpSession {
34
58
  /** Resolves to null when the server says the scope has no catalog, so no update. */
35
59
  fetchReleaseCatalog: (
36
60
  params: ReleaseCatalogRequest,
37
61
  ) => Promise<ReleaseCatalog | null>;
38
- resolveArtifact: (params: ArtifactRequest) => Promise<ArtifactInfo>;
62
+ resolveArtifact: (params: ArtifactRequest) => Promise<ResolvedArtifact>;
39
63
  }
40
64
 
41
65
  export interface HotUpdaterHttpClient {
@@ -59,6 +83,7 @@ const requireArtifactUrl = (value: string): string => {
59
83
  return value;
60
84
  };
61
85
 
86
+ // Legacy: artifact protocol v1: validates a server that predates v2; remove with the fallback.
62
87
  const requireArtifactUrls = (info: ArtifactInfo): ArtifactInfo => {
63
88
  requireArtifactUrl(info.manifestUrl);
64
89
  if (info.archiveUrl) requireArtifactUrl(info.archiveUrl);
@@ -91,6 +116,25 @@ const requireArtifactProtocolV1 = (info: ArtifactInfo): ArtifactInfo => {
91
116
  return info;
92
117
  };
93
118
 
119
+ const requireArtifactProtocolV2 = (info: ArtifactInfoV2): ArtifactInfoV2 => {
120
+ if (
121
+ info?.artifactProtocolVersion !== ARTIFACT_FILES_PROTOCOL_VERSION ||
122
+ !info.manifestUrl ||
123
+ !info.manifestFileHash ||
124
+ (info.archiveUrl !== undefined && typeof info.archiveUrl !== "string") ||
125
+ (info.patch !== undefined &&
126
+ (!info.patch?.assetPath || !info.patch.patchUrl))
127
+ ) {
128
+ throw new InvalidUpdateResponseError(
129
+ `Server does not support artifact protocol ${ARTIFACT_FILES_PROTOCOL_VERSION}.`,
130
+ );
131
+ }
132
+ requireArtifactUrl(info.manifestUrl);
133
+ if (info.archiveUrl) requireArtifactUrl(info.archiveUrl);
134
+ if (info.patch) requireArtifactUrl(info.patch.patchUrl);
135
+ return info;
136
+ };
137
+
94
138
  const createSession = (
95
139
  baseURL: string,
96
140
  onResponse?: (response: UpdateHttpResponse) => void,
@@ -133,16 +177,47 @@ const createSession = (
133
177
  url,
134
178
  });
135
179
  },
136
- resolveArtifact: async (params): Promise<ArtifactInfo> => {
137
- const info = await fetchJSON<ArtifactInfo>({
180
+ resolveArtifact: async (params): Promise<ResolvedArtifact> => {
181
+ const target = encodeURIComponent(params.targetBundleId);
182
+ const route = `${target}/from/${encodeURIComponent(params.currentBundleId)}`;
183
+ const request = {
138
184
  ...(onResponse ? { onResponse } : {}),
139
185
  requestHeaders: params.requestHeaders,
140
186
  requestTimeout: params.requestTimeout,
141
- url: `${baseURL}/artifacts/v1/${encodeURIComponent(
142
- params.targetBundleId,
143
- )}/from/${encodeURIComponent(params.currentBundleId)}`,
144
- });
145
- return requireArtifactUrls(requireArtifactProtocolV1(info));
187
+ };
188
+ let info: ArtifactInfoV2;
189
+ try {
190
+ info = await fetchJSON<ArtifactInfoV2>({
191
+ ...request,
192
+ url: `${baseURL}/artifacts/v2/${route}`,
193
+ });
194
+ } catch (error) {
195
+ // Legacy: artifact protocol v1: a server older than v2 has no such route. Remove this
196
+ // fallback once no supported server predates v2.
197
+ if (!(error instanceof FetchJSONResponseError) || error.status !== 404) {
198
+ throw error;
199
+ }
200
+ return requireArtifactUrls(
201
+ requireArtifactProtocolV1(
202
+ await fetchJSON<ArtifactInfo>({
203
+ ...request,
204
+ url: `${baseURL}/artifacts/v1/${route}`,
205
+ }),
206
+ ),
207
+ );
208
+ }
209
+ const { archiveUrl, manifestFileHash, manifestUrl, patch } =
210
+ requireArtifactProtocolV2(info);
211
+ const { assetPath, ...assetPatch } = patch ?? { assetPath: undefined };
212
+ return {
213
+ ...(archiveUrl ? { archiveUrl } : {}),
214
+ assets: assetPath
215
+ ? { [assetPath]: { patch: assetPatch as ChangedAssetPatch } }
216
+ : {},
217
+ filesUrl: `${baseURL}/artifacts/v2/${target}/files`,
218
+ manifestFileHash,
219
+ manifestUrl,
220
+ };
146
221
  },
147
222
  });
148
223
 
package/src/native.ts CHANGED
@@ -461,6 +461,13 @@ export async function stageBundle(
461
461
  manifestFileHash: targetManifestFileHash,
462
462
  manifestUrl: targetManifestUrl,
463
463
  ...(params.archiveUrl ? { archiveUrl: params.archiveUrl } : {}),
464
+ ...(params.filesUrl ? { filesUrl: params.filesUrl } : {}),
465
+ ...(params.filesUrl && params.requestHeaders
466
+ ? { requestHeaders: params.requestHeaders }
467
+ : {}),
468
+ ...(params.filesUrl && params.requestTimeout
469
+ ? { requestTimeout: params.requestTimeout }
470
+ : {}),
464
471
  ...(selection === undefined ? {} : { selection }),
465
472
  });
466
473
  sessionState.markBundleInstalled(updateBundleId, targetChannel);
package/src/sdkVersion.ts CHANGED
@@ -1 +1 @@
1
- export const HOT_UPDATER_SDK_VERSION = "1.0.4";
1
+ export const HOT_UPDATER_SDK_VERSION = "1.0.6";
@@ -15,8 +15,20 @@ export interface UpdateBundleParams {
15
15
  manifestFileHash: string;
16
16
  /** Optional tar.br URL; integrity and sizes come from the verified manifest. */
17
17
  archiveUrl?: string | null;
18
- /** Full protocol v1 target file descriptor map. */
18
+ /**
19
+ * Target file descriptors: every file with its URL in artifact protocol v1,
20
+ * or only the Hermes patch in v2, where native asks `filesUrl`.
21
+ */
19
22
  assets: UnsafeObject;
23
+ /**
24
+ * Artifact protocol v2: where native POSTs the paths of the files it
25
+ * downloads one by one, after reusing local files and choosing the archive.
26
+ */
27
+ filesUrl?: string | null;
28
+ /** Headers for the `filesUrl` request, as the update check sent them. */
29
+ requestHeaders?: UnsafeObject | null;
30
+ /** Milliseconds the `filesUrl` request may take, as the update check's. */
31
+ requestTimeout?: number | null;
20
32
  /** Full Release Catalog selection receipt committed with the staged Bundle. */
21
33
  selection?: UnsafeObject | null;
22
34
  }