@hot-updater/react-native 1.0.0-rc.40 → 1.0.0-rc.42

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 (88) hide show
  1. package/lib/commonjs/appReady.js +25 -31
  2. package/lib/commonjs/appReady.js.map +1 -1
  3. package/lib/commonjs/checkForUpdate.js +9 -9
  4. package/lib/commonjs/checkForUpdate.js.map +1 -1
  5. package/lib/commonjs/error.js +1 -1
  6. package/lib/commonjs/httpClient.js +1 -1
  7. package/lib/commonjs/index.js +304 -326
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/init.types.js +6 -0
  10. package/lib/commonjs/init.types.js.map +1 -0
  11. package/lib/commonjs/native.js +3 -3
  12. package/lib/commonjs/pluginHost.js +11 -15
  13. package/lib/commonjs/pluginHost.js.map +1 -1
  14. package/lib/commonjs/sdkVersion.js +1 -1
  15. package/lib/commonjs/wrap.js +7 -16
  16. package/lib/commonjs/wrap.js.map +1 -1
  17. package/lib/module/appReady.js +23 -28
  18. package/lib/module/appReady.js.map +1 -1
  19. package/lib/module/checkForUpdate.js +10 -10
  20. package/lib/module/checkForUpdate.js.map +1 -1
  21. package/lib/module/clientPlugin.js.map +1 -1
  22. package/lib/module/error.js +1 -1
  23. package/lib/module/httpClient.js +1 -1
  24. package/lib/module/index.js +299 -329
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/init.types.js +4 -0
  27. package/lib/module/init.types.js.map +1 -0
  28. package/lib/module/native.js +3 -3
  29. package/lib/module/pluginHost.js +9 -9
  30. package/lib/module/pluginHost.js.map +1 -1
  31. package/lib/module/sdkVersion.js +1 -1
  32. package/lib/module/wrap.js +7 -15
  33. package/lib/module/wrap.js.map +1 -1
  34. package/lib/typescript/commonjs/appReady.d.ts +18 -9
  35. package/lib/typescript/commonjs/appReady.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/checkForUpdate.d.ts +5 -3
  37. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/clientPlugin.d.ts +1 -1
  39. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/error.d.ts +1 -1
  41. package/lib/typescript/commonjs/httpClient.d.ts +1 -1
  42. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/index.d.ts +122 -110
  44. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  46. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  47. package/lib/typescript/commonjs/native.d.ts +3 -3
  48. package/lib/typescript/commonjs/pluginHost.d.ts +7 -3
  49. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  51. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  52. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/wrap.types.d.ts +13 -40
  54. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  55. package/lib/typescript/module/appReady.d.ts +18 -9
  56. package/lib/typescript/module/appReady.d.ts.map +1 -1
  57. package/lib/typescript/module/checkForUpdate.d.ts +5 -3
  58. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  59. package/lib/typescript/module/clientPlugin.d.ts +1 -1
  60. package/lib/typescript/module/clientPlugin.d.ts.map +1 -1
  61. package/lib/typescript/module/error.d.ts +1 -1
  62. package/lib/typescript/module/httpClient.d.ts +1 -1
  63. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  64. package/lib/typescript/module/index.d.ts +122 -110
  65. package/lib/typescript/module/index.d.ts.map +1 -1
  66. package/lib/typescript/module/init.types.d.ts +21 -0
  67. package/lib/typescript/module/init.types.d.ts.map +1 -0
  68. package/lib/typescript/module/native.d.ts +3 -3
  69. package/lib/typescript/module/pluginHost.d.ts +7 -3
  70. package/lib/typescript/module/pluginHost.d.ts.map +1 -1
  71. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  72. package/lib/typescript/module/wrap.d.ts +2 -3
  73. package/lib/typescript/module/wrap.d.ts.map +1 -1
  74. package/lib/typescript/module/wrap.types.d.ts +13 -40
  75. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  76. package/package.json +4 -3
  77. package/src/appReady.ts +43 -37
  78. package/src/checkForUpdate.ts +19 -11
  79. package/src/clientPlugin.ts +3 -0
  80. package/src/error.ts +1 -1
  81. package/src/httpClient.ts +1 -1
  82. package/src/index.ts +344 -422
  83. package/src/init.types.ts +24 -0
  84. package/src/native.ts +3 -3
  85. package/src/pluginHost.ts +16 -11
  86. package/src/sdkVersion.ts +1 -1
  87. package/src/wrap.tsx +6 -20
  88. package/src/wrap.types.ts +18 -45
package/src/appReady.ts CHANGED
@@ -8,11 +8,7 @@ import {
8
8
  type NotifyAppReadyResult,
9
9
  readNotifyAppReady,
10
10
  } from "./native";
11
- import {
12
- buildPluginEvent,
13
- dispatchPluginHook,
14
- emitPluginHook,
15
- } from "./pluginHost";
11
+ import type { PluginHost } from "./pluginHost";
16
12
 
17
13
  export type NotifyAppReadyOptions = {
18
14
  onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
@@ -21,10 +17,6 @@ export type NotifyAppReadyOptions = {
21
17
 
22
18
  type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
23
19
 
24
- let didEmitAppReady = false;
25
- /** Settles once this runtime's launch was read, whether it was reported or failed. */
26
- let appReadyRead: Promise<unknown> | null = null;
27
-
28
20
  const waitForNextFrame = () =>
29
21
  new Promise<void>((resolve) => {
30
22
  const requestAnimationFrame = (
@@ -87,6 +79,7 @@ const toAppReadyResult = (
87
79
  };
88
80
 
89
81
  const notifyAppReady = async (
82
+ host: PluginHost,
90
83
  options: NotifyAppReadyOptions,
91
84
  ): Promise<NotifyAppReadyResult | undefined> => {
92
85
  try {
@@ -97,10 +90,9 @@ const notifyAppReady = async (
97
90
  } while (nativeReadResult.pending);
98
91
 
99
92
  const { result, transition } = nativeReadResult;
100
- if (!didEmitAppReady) {
101
- didEmitAppReady = true;
102
- emitPluginHook("onAppReady", () => toAppReadyResult(result, transition));
103
- }
93
+ host.emitPluginHook("onAppReady", () =>
94
+ toAppReadyResult(result, transition),
95
+ );
104
96
 
105
97
  options.onNotifyAppReady?.(result);
106
98
  return result;
@@ -113,29 +105,43 @@ const notifyAppReady = async (
113
105
  }
114
106
  };
115
107
 
116
- /** Reads this runtime's launch once native finalizes it and reports it to plugins. */
117
- export const handleNotifyAppReady = (
118
- options: NotifyAppReadyOptions,
119
- ): Promise<NotifyAppReadyResult | undefined> => {
120
- const readiness = notifyAppReady(options);
121
- appReadyRead ??= readiness;
122
- return readiness;
123
- };
108
+ /** An instance's launch: read once, and reported to the instance's plugins. */
109
+ export interface LaunchReporter {
110
+ /**
111
+ * Reads this launch once native finalizes it and reports it to the
112
+ * plugins, once. It rejects only if `onError` throws.
113
+ */
114
+ read(
115
+ options: NotifyAppReadyOptions,
116
+ ): Promise<NotifyAppReadyResult | undefined>;
117
+ /** Settles once the launch was read, whether it was reported or failed. */
118
+ readonly appReady: Promise<unknown>;
119
+ /**
120
+ * Calls a plugin hook after the instance's `onAppReady`, so plugins see the
121
+ * launch before anything that followed it. The event is built now, from the
122
+ * state it describes, and only when a plugin listens.
123
+ */
124
+ emit: PluginHost["emitPluginHook"];
125
+ }
124
126
 
125
- /**
126
- * Calls a plugin hook after this runtime's `onAppReady`, so plugins see the
127
- * launch before anything that followed it. The event is built now, from the
128
- * state it describes, and only when a plugin listens.
129
- */
130
- export const emitAfterAppReady: typeof emitPluginHook = (
131
- name,
132
- createPayload,
133
- ) => {
134
- const payload = buildPluginEvent(name, createPayload);
135
- if (payload === null) return;
136
- if (appReadyRead === null) {
137
- dispatchPluginHook(name, payload);
138
- return;
139
- }
140
- void appReadyRead.then(() => dispatchPluginHook(name, payload));
127
+ export const createLaunchReporter = (host: PluginHost): LaunchReporter => {
128
+ let launchRead: Promise<NotifyAppReadyResult | undefined> | null = null;
129
+ return {
130
+ read: (options) => {
131
+ launchRead ??= notifyAppReady(host, options);
132
+ return launchRead;
133
+ },
134
+ get appReady() {
135
+ return launchRead ?? Promise.resolve();
136
+ },
137
+ emit: (name, createPayload) => {
138
+ const payload = host.buildPluginEvent(name, createPayload);
139
+ if (payload === null) return;
140
+ if (launchRead === null) {
141
+ host.dispatchPluginHook(name, payload);
142
+ return;
143
+ }
144
+ void launchRead.then(() => host.dispatchPluginHook(name, payload));
145
+ },
146
+ };
141
147
  };
@@ -11,7 +11,7 @@ import {
11
11
  } from "@hot-updater/protocol";
12
12
  import { Platform } from "react-native";
13
13
 
14
- import { emitAfterAppReady, getRunningReleaseId } from "./appReady";
14
+ import { getRunningReleaseId, type LaunchReporter } from "./appReady";
15
15
  import type {
16
16
  ReleaseTransitionKind,
17
17
  UpdateErrorResource,
@@ -45,7 +45,6 @@ export interface CheckForUpdateOptions {
45
45
  * Update strategy
46
46
  * - "fingerprint": Use fingerprint hash to check for updates
47
47
  * - "appVersion": Use app version to check for updates
48
- * - Can override the strategy set in HotUpdater.wrap()
49
48
  */
50
49
  updateStrategy: "appVersion" | "fingerprint";
51
50
 
@@ -75,13 +74,15 @@ export type CheckForUpdateResult = {
75
74
  readonly transitionKind?: ReleaseTransitionKind;
76
75
  /**
77
76
  * Updates the bundle.
78
- * This method is equivalent to `HotUpdater.updateBundle()` but with all required arguments pre-filled.
77
+ * This method is equivalent to `hotUpdater.updateBundle()` but with all required arguments pre-filled.
79
78
  */
80
79
  updateBundle: () => Promise<boolean>;
81
80
  };
82
81
 
83
82
  export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
84
83
  client: HotUpdaterHttpClient;
84
+ /** Calls the checking instance's plugin hooks. */
85
+ emit: LaunchReporter["emit"];
85
86
  }
86
87
 
87
88
  const sameReceipt = (
@@ -137,12 +138,13 @@ type UpdateErrorScope = {
137
138
  * the SDK's JavaScript raised happened in `stage`, fetching `resource`.
138
139
  */
139
140
  export const reportUpdateError = (
141
+ emit: LaunchReporter["emit"],
140
142
  error: unknown,
141
143
  stage: UpdateErrorStage,
142
144
  resource: UpdateErrorResource | undefined,
143
145
  scope: UpdateErrorScope,
144
146
  ): void => {
145
- emitAfterAppReady("onUpdateError", () => {
147
+ emit("onUpdateError", () => {
146
148
  const classification = classifyUpdateError(error, stage, resource);
147
149
  if (classification === null) return null;
148
150
  return {
@@ -354,7 +356,7 @@ async function checkForReleaseCatalogUpdate(input: {
354
356
  selection: receipt,
355
357
  });
356
358
  if (committed && transitionKind === "ADOPT_RELEASE") {
357
- emitAfterAppReady("onUpdateCheck", () => ({
359
+ options.emit("onUpdateCheck", () => ({
358
360
  status: "UNCHANGED",
359
361
  channel: receipt.channel,
360
362
  bundleId: receipt.bundleId,
@@ -386,7 +388,7 @@ async function checkForReleaseCatalogUpdate(input: {
386
388
  status: desired.status,
387
389
  });
388
390
  if (delivery !== null && desired.bundleId !== input.currentBundleId) {
389
- emitAfterAppReady("onBundleDownloaded", () => ({
391
+ options.emit("onBundleDownloaded", () => ({
390
392
  channel: input.targetChannel,
391
393
  fromBundleId: input.currentBundleId,
392
394
  fromReleaseId,
@@ -402,7 +404,13 @@ async function checkForReleaseCatalogUpdate(input: {
402
404
  try {
403
405
  return await installSelection();
404
406
  } catch (error) {
405
- reportUpdateError(error, "download", "artifact", errorScope);
407
+ reportUpdateError(
408
+ options.emit,
409
+ error,
410
+ "download",
411
+ "artifact",
412
+ errorScope,
413
+ );
406
414
  throw error;
407
415
  }
408
416
  };
@@ -413,7 +421,7 @@ async function checkForReleaseCatalogUpdate(input: {
413
421
  : desired.status === "ROLLBACK"
414
422
  ? true
415
423
  : (release?.shouldForceUpdate ?? false);
416
- emitAfterAppReady("onUpdateCheck", () => ({
424
+ options.emit("onUpdateCheck", () => ({
417
425
  status: "UPDATE_AVAILABLE",
418
426
  channel: input.targetChannel,
419
427
  fromBundleId: input.currentBundleId,
@@ -471,7 +479,7 @@ export async function checkForUpdate(
471
479
 
472
480
  if (isSwitched && explicitChannel && explicitChannel !== currentChannel) {
473
481
  const error = new HotUpdaterError(
474
- `Runtime channel is already switched to "${currentChannel}". Call HotUpdater.resetChannel() before checking "${explicitChannel}".`,
482
+ `Runtime channel is already switched to "${currentChannel}". Call hotUpdater.resetChannel() before checking "${explicitChannel}".`,
475
483
  );
476
484
  options.onError?.(error);
477
485
  throw error;
@@ -495,7 +503,7 @@ export async function checkForUpdate(
495
503
  targetChannel,
496
504
  });
497
505
  if (result === null) {
498
- emitAfterAppReady("onUpdateCheck", () => {
506
+ options.emit("onUpdateCheck", () => {
499
507
  const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
500
508
  return {
501
509
  status: "UNCHANGED",
@@ -508,7 +516,7 @@ export async function checkForUpdate(
508
516
  }
509
517
  return result;
510
518
  } catch (error) {
511
- reportUpdateError(error, "check", "catalog", {
519
+ reportUpdateError(options.emit, error, "check", "catalog", {
512
520
  bundleId: currentBundleId,
513
521
  channel: targetChannel,
514
522
  updateStrategy: options.updateStrategy,
@@ -2,9 +2,12 @@ export {
2
2
  defineClientPlugin,
3
3
  type AppReadyResult,
4
4
  type BundleDownloadedInfo,
5
+ type ClientPluginApi,
6
+ type ClientPluginApis,
5
7
  type HotUpdaterClientContext,
6
8
  type HotUpdaterClientHooks,
7
9
  type HotUpdaterClientPlugin,
10
+ type HotUpdaterClientSetup,
8
11
  type HotUpdaterClientStorage,
9
12
  type ReleaseTransitionKind,
10
13
  type UpdateCheckResult,
package/src/error.ts CHANGED
@@ -92,7 +92,7 @@ export enum HotUpdaterErrorCode {
92
92
  /**
93
93
  * Bundle is in crashed history and cannot be applied.
94
94
  * Thrown when attempting to install a bundle that previously caused a crash.
95
- * Use HotUpdater.clearCrashHistory() to allow retrying this bundle.
95
+ * Use hotUpdater.clearCrashHistory() to allow retrying this bundle.
96
96
  * @retryable false - Bundle was marked as crashed for safety
97
97
  */
98
98
  BUNDLE_IN_CRASHED_HISTORY = "BUNDLE_IN_CRASHED_HISTORY",
package/src/httpClient.ts CHANGED
@@ -180,7 +180,7 @@ const createSession = (
180
180
  },
181
181
  });
182
182
 
183
- /** Creates the private HTTP client used by HotUpdater.init and HotUpdater.wrap. */
183
+ /** Creates the private HTTP client HotUpdater.init configures. */
184
184
  export const createHttpClient = (
185
185
  baseURL: HotUpdaterBaseURL,
186
186
  onResponse?: (response: UpdateHttpResponse) => void,