@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.3
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.
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1 -1
- package/lib/commonjs/checkForUpdate.js +4 -4
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +34 -23
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/httpClient.js +2 -2
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +8 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +49 -11
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +8 -6
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +107 -6
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +21 -21
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.js → notifyAppReadyInsights.js} +27 -27
- package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +29 -29
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +7 -7
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/wrap.js +5 -5
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +58 -25
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/checkForUpdate.js +4 -4
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +34 -23
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/httpClient.js +2 -2
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +9 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +49 -11
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +6 -4
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +107 -6
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +16 -16
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.js → notifyAppReadyInsights.js} +27 -27
- package/lib/module/notifyAppReadyInsights.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +21 -21
- package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +4 -4
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/wrap.js +4 -4
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +60 -25
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +5 -5
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -5
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +4 -3
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/{module/notifyAppReadyAnalytics.d.ts → commonjs/notifyAppReadyInsights.d.ts} +2 -2
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +5 -6
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +5 -5
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -5
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +4 -3
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/{commonjs/notifyAppReadyAnalytics.d.ts → module/notifyAppReadyInsights.d.ts} +2 -2
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/wrap.types.d.ts +5 -6
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/checkForUpdate.releaseCatalog.spec.ts +71 -56
- package/src/checkForUpdate.ts +5 -9
- package/src/httpClient.ts +10 -10
- package/src/index.spec.ts +55 -11
- package/src/index.ts +10 -10
- package/src/native.spec.ts +110 -6
- package/src/native.ts +9 -8
- package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +17 -17
- package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +22 -22
- package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
- package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +5 -5
- package/src/{notifyAppReadyAnalytics.ts → notifyAppReadyInsights.ts} +33 -33
- package/src/sdkVersion.ts +1 -1
- package/src/wrap.spec.ts +70 -31
- package/src/wrap.tsx +4 -4
- package/src/wrap.types.ts +5 -6
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
NotifyAppReadyInsightsEvent,
|
|
5
5
|
NotifyAppReadyResult,
|
|
6
6
|
} from "./native";
|
|
7
7
|
import {
|
|
8
8
|
createNotifyReadResult,
|
|
9
9
|
stubNotifyFrame,
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./notifyAppReadyInsights.test-utils";
|
|
11
11
|
|
|
12
12
|
vi.mock("react-native", () => ({
|
|
13
13
|
Platform: { OS: "ios" },
|
|
@@ -25,7 +25,7 @@ const mocks = vi.hoisted(() => ({
|
|
|
25
25
|
getPersistedUserIdentity: vi.fn(() => ({})),
|
|
26
26
|
readNotifyAppReady: vi.fn<
|
|
27
27
|
() => {
|
|
28
|
-
|
|
28
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null;
|
|
29
29
|
pending: boolean;
|
|
30
30
|
result: NotifyAppReadyResult;
|
|
31
31
|
}
|
|
@@ -51,20 +51,20 @@ vi.mock("./native", () => ({
|
|
|
51
51
|
}));
|
|
52
52
|
|
|
53
53
|
const createClient = () => {
|
|
54
|
-
const
|
|
54
|
+
const sendInsightsEvent = vi.fn().mockResolvedValue(undefined);
|
|
55
55
|
return {
|
|
56
56
|
client: {
|
|
57
57
|
createSession: vi.fn(async () => ({
|
|
58
58
|
fetchReleaseCatalog: vi.fn(),
|
|
59
59
|
resolveArtifact: vi.fn(),
|
|
60
|
-
|
|
60
|
+
sendInsightsEvent,
|
|
61
61
|
})),
|
|
62
62
|
},
|
|
63
|
-
|
|
63
|
+
sendInsightsEvent,
|
|
64
64
|
};
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
describe("automatic notifyAppReady
|
|
67
|
+
describe("automatic notifyAppReady insights", () => {
|
|
68
68
|
beforeEach(() => {
|
|
69
69
|
vi.resetModules();
|
|
70
70
|
vi.unstubAllGlobals();
|
|
@@ -107,12 +107,12 @@ describe("automatic notifyAppReady analytics", () => {
|
|
|
107
107
|
),
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
-
const { client,
|
|
110
|
+
const { client, sendInsightsEvent } = createClient();
|
|
111
111
|
const onNotifyAppReady = vi.fn();
|
|
112
112
|
const { init } = await import("./wrap");
|
|
113
113
|
|
|
114
114
|
init({
|
|
115
|
-
|
|
115
|
+
insights: true,
|
|
116
116
|
onNotifyAppReady,
|
|
117
117
|
requestHeaders: { Authorization: "Bearer token" },
|
|
118
118
|
requestTimeout: 1000,
|
|
@@ -120,7 +120,7 @@ describe("automatic notifyAppReady analytics", () => {
|
|
|
120
120
|
});
|
|
121
121
|
await vi.runOnlyPendingTimersAsync();
|
|
122
122
|
|
|
123
|
-
expect(
|
|
123
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith({
|
|
124
124
|
appVersion: "1.0.0",
|
|
125
125
|
channel: "production",
|
|
126
126
|
cohort: "123",
|
|
@@ -162,14 +162,14 @@ describe("automatic notifyAppReady analytics", () => {
|
|
|
162
162
|
},
|
|
163
163
|
),
|
|
164
164
|
);
|
|
165
|
-
const { client,
|
|
165
|
+
const { client, sendInsightsEvent } = createClient();
|
|
166
166
|
const onNotifyAppReady = vi.fn();
|
|
167
167
|
const { init } = await import("./wrap");
|
|
168
168
|
|
|
169
|
-
init({
|
|
169
|
+
init({ insights: true, client, onNotifyAppReady });
|
|
170
170
|
await vi.runOnlyPendingTimersAsync();
|
|
171
171
|
|
|
172
|
-
expect(
|
|
172
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith({
|
|
173
173
|
appVersion: "1.0.0",
|
|
174
174
|
channel: "production",
|
|
175
175
|
cohort: "123",
|
|
@@ -194,16 +194,16 @@ describe("automatic notifyAppReady analytics", () => {
|
|
|
194
194
|
|
|
195
195
|
it("sends one UNCHANGED event for repeated init calls", async () => {
|
|
196
196
|
stubNotifyFrame();
|
|
197
|
-
const { client,
|
|
197
|
+
const { client, sendInsightsEvent } = createClient();
|
|
198
198
|
const onNotifyAppReady = vi.fn();
|
|
199
199
|
const { init } = await import("./wrap");
|
|
200
200
|
|
|
201
|
-
init({
|
|
202
|
-
init({
|
|
201
|
+
init({ insights: true, client, onNotifyAppReady });
|
|
202
|
+
init({ insights: true, client, onNotifyAppReady });
|
|
203
203
|
await vi.runOnlyPendingTimersAsync();
|
|
204
204
|
|
|
205
|
-
expect(
|
|
206
|
-
expect(
|
|
205
|
+
expect(sendInsightsEvent).toHaveBeenCalledTimes(1);
|
|
206
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith({
|
|
207
207
|
appVersion: "1.0.0",
|
|
208
208
|
channel: "production",
|
|
209
209
|
cohort: "123",
|
|
@@ -228,16 +228,16 @@ describe("automatic notifyAppReady analytics", () => {
|
|
|
228
228
|
});
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
it("skips automatic
|
|
231
|
+
it("skips automatic insights when disabled while preserving readiness", async () => {
|
|
232
232
|
stubNotifyFrame();
|
|
233
|
-
const { client,
|
|
233
|
+
const { client, sendInsightsEvent } = createClient();
|
|
234
234
|
const onNotifyAppReady = vi.fn();
|
|
235
235
|
const { init } = await import("./wrap");
|
|
236
236
|
|
|
237
|
-
init({
|
|
237
|
+
init({ insights: false, client, onNotifyAppReady });
|
|
238
238
|
await vi.runOnlyPendingTimersAsync();
|
|
239
239
|
|
|
240
|
-
expect(
|
|
240
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
241
241
|
expect(onNotifyAppReady).toHaveBeenCalledWith({ status: "UNCHANGED" });
|
|
242
242
|
});
|
|
243
243
|
});
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
NotifyAppReadyInsightsEvent,
|
|
5
5
|
NotifyAppReadyResult,
|
|
6
6
|
} from "./native";
|
|
7
7
|
|
|
8
8
|
export const createNotifyReadResult = (
|
|
9
9
|
result: NotifyAppReadyResult = { status: "UNCHANGED" },
|
|
10
|
-
|
|
10
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null = null,
|
|
11
11
|
pending = false,
|
|
12
12
|
): {
|
|
13
|
-
|
|
13
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null;
|
|
14
14
|
pending: boolean;
|
|
15
15
|
result: NotifyAppReadyResult;
|
|
16
16
|
} => ({
|
|
17
|
-
|
|
17
|
+
insightsEvent,
|
|
18
18
|
pending,
|
|
19
19
|
result,
|
|
20
20
|
});
|
package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
NotifyAppReadyInsightsEvent,
|
|
5
5
|
NotifyAppReadyResult,
|
|
6
6
|
} from "./native";
|
|
7
7
|
import {
|
|
8
8
|
createNotifyReadResult,
|
|
9
9
|
stubNotifyFrame,
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./notifyAppReadyInsights.test-utils";
|
|
11
11
|
|
|
12
12
|
vi.mock("react-native", () => ({
|
|
13
13
|
Platform: { OS: "ios" },
|
|
@@ -36,7 +36,7 @@ const mocks = vi.hoisted(() => ({
|
|
|
36
36
|
getPersistedUserIdentity: vi.fn(() => ({})),
|
|
37
37
|
readNotifyAppReady: vi.fn<
|
|
38
38
|
() => {
|
|
39
|
-
|
|
39
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null;
|
|
40
40
|
pending: boolean;
|
|
41
41
|
result: NotifyAppReadyResult;
|
|
42
42
|
}
|
|
@@ -110,7 +110,7 @@ describe("automatic notifyAppReady transport failures", () => {
|
|
|
110
110
|
"https://updates.example.test/hot-updater",
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
init({
|
|
113
|
+
init({ insights: true, client, onError, onNotifyAppReady });
|
|
114
114
|
await vi.runOnlyPendingTimersAsync();
|
|
115
115
|
|
|
116
116
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
@@ -119,7 +119,7 @@ describe("automatic notifyAppReady transport failures", () => {
|
|
|
119
119
|
status: "UNCHANGED",
|
|
120
120
|
});
|
|
121
121
|
expect(warn).toHaveBeenCalledWith(
|
|
122
|
-
"[HotUpdater] Automatic notifyAppReady
|
|
122
|
+
"[HotUpdater] Automatic notifyAppReady insights failed:",
|
|
123
123
|
expect.any(Error),
|
|
124
124
|
);
|
|
125
125
|
} finally {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Platform } from "react-native";
|
|
2
2
|
|
|
3
3
|
import type { HotUpdaterError } from "./error";
|
|
4
|
-
import type {
|
|
4
|
+
import type { InsightsEventParams, HotUpdaterHttpClient } from "./httpClient";
|
|
5
5
|
import {
|
|
6
6
|
getAppVersion,
|
|
7
7
|
getBundleId,
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
getFingerprintHash,
|
|
11
11
|
getInstallId,
|
|
12
12
|
getPersistedUserIdentity,
|
|
13
|
-
type
|
|
13
|
+
type NotifyAppReadyInsightsEvent,
|
|
14
14
|
type NotifyAppReadyResult,
|
|
15
15
|
readNotifyAppReady,
|
|
16
16
|
} from "./native";
|
|
17
17
|
|
|
18
18
|
export type NotifyAppReadyOptions = {
|
|
19
|
-
|
|
19
|
+
insights?: boolean;
|
|
20
20
|
client: HotUpdaterHttpClient;
|
|
21
21
|
requestHeaders?: Record<string, string>;
|
|
22
22
|
requestTimeout?: number;
|
|
@@ -26,7 +26,7 @@ export type NotifyAppReadyOptions = {
|
|
|
26
26
|
|
|
27
27
|
type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
|
|
28
28
|
|
|
29
|
-
let
|
|
29
|
+
let didAttemptAutomaticInsights = false;
|
|
30
30
|
|
|
31
31
|
const waitForNextFrame = () =>
|
|
32
32
|
new Promise<void>((resolve) => {
|
|
@@ -48,16 +48,16 @@ const assertNever = (value: never): never => {
|
|
|
48
48
|
throw new Error(`[HotUpdater] Unexpected notifyAppReady status: ${value}`);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const
|
|
51
|
+
const buildNotifyAppReadyInsightsParams = (
|
|
52
52
|
nativeResult: NotifyAppReadyResult,
|
|
53
|
-
|
|
53
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null,
|
|
54
54
|
options: Pick<NotifyAppReadyOptions, "requestHeaders" | "requestTimeout">,
|
|
55
|
-
):
|
|
55
|
+
): InsightsEventParams => {
|
|
56
56
|
const appVersion = getAppVersion();
|
|
57
57
|
|
|
58
58
|
if (!appVersion) {
|
|
59
59
|
throw new Error(
|
|
60
|
-
"[HotUpdater] Automatic
|
|
60
|
+
"[HotUpdater] Automatic insights requires a non-null native app version.",
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -66,7 +66,7 @@ const buildNotifyAppReadyAnalyticsParams = (
|
|
|
66
66
|
|
|
67
67
|
if (!installId) {
|
|
68
68
|
throw new Error(
|
|
69
|
-
"[HotUpdater] Automatic
|
|
69
|
+
"[HotUpdater] Automatic insights requires a non-null native install id.",
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -78,12 +78,12 @@ const buildNotifyAppReadyAnalyticsParams = (
|
|
|
78
78
|
channel: getChannel(),
|
|
79
79
|
cohort: getCohort(),
|
|
80
80
|
fingerprintHash: getFingerprintHash(),
|
|
81
|
-
fromReleaseId:
|
|
81
|
+
fromReleaseId: insightsEvent?.fromReleaseId ?? null,
|
|
82
82
|
installId,
|
|
83
83
|
platform,
|
|
84
84
|
requestHeaders: options.requestHeaders,
|
|
85
85
|
requestTimeout: options.requestTimeout,
|
|
86
|
-
toReleaseId:
|
|
86
|
+
toReleaseId: insightsEvent?.toReleaseId ?? null,
|
|
87
87
|
...(userId != null ? { userId } : {}),
|
|
88
88
|
...(username != null ? { username } : {}),
|
|
89
89
|
};
|
|
@@ -94,7 +94,7 @@ const buildNotifyAppReadyAnalyticsParams = (
|
|
|
94
94
|
|
|
95
95
|
if (!bundleId) {
|
|
96
96
|
throw new Error(
|
|
97
|
-
"[HotUpdater] Automatic
|
|
97
|
+
"[HotUpdater] Automatic insights requires a non-null current bundle id.",
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -108,57 +108,57 @@ const buildNotifyAppReadyAnalyticsParams = (
|
|
|
108
108
|
}
|
|
109
109
|
case "UPDATE_APPLIED":
|
|
110
110
|
case "RECOVERED":
|
|
111
|
-
if (!
|
|
111
|
+
if (!insightsEvent) {
|
|
112
112
|
throw new Error(
|
|
113
|
-
"[HotUpdater] Native launch report is missing persisted metadata required for automatic
|
|
113
|
+
"[HotUpdater] Native launch report is missing persisted metadata required for automatic insights.",
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
switch (
|
|
117
|
+
switch (insightsEvent.type) {
|
|
118
118
|
case "UPDATE_APPLIED":
|
|
119
119
|
return {
|
|
120
120
|
...commonParams,
|
|
121
|
-
fromBundleId:
|
|
122
|
-
toBundleId:
|
|
121
|
+
fromBundleId: insightsEvent.fromBundleId,
|
|
122
|
+
toBundleId: insightsEvent.toBundleId,
|
|
123
123
|
type: "UPDATE_APPLIED",
|
|
124
|
-
updateStrategy:
|
|
124
|
+
updateStrategy: insightsEvent.updateStrategy,
|
|
125
125
|
};
|
|
126
126
|
case "RECOVERED":
|
|
127
127
|
return {
|
|
128
128
|
...commonParams,
|
|
129
|
-
fromBundleId:
|
|
130
|
-
toBundleId:
|
|
129
|
+
fromBundleId: insightsEvent.fromBundleId,
|
|
130
|
+
toBundleId: insightsEvent.toBundleId,
|
|
131
131
|
type: "RECOVERED",
|
|
132
|
-
updateStrategy:
|
|
132
|
+
updateStrategy: insightsEvent.updateStrategy,
|
|
133
133
|
};
|
|
134
134
|
default:
|
|
135
|
-
return assertNever(
|
|
135
|
+
return assertNever(insightsEvent.type);
|
|
136
136
|
}
|
|
137
137
|
default:
|
|
138
138
|
return assertNever(nativeResult);
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
const
|
|
142
|
+
const maybeSendAutomaticInsights = async (
|
|
143
143
|
options: NotifyAppReadyOptions,
|
|
144
144
|
nativeResult: NotifyAppReadyResult,
|
|
145
|
-
|
|
145
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null,
|
|
146
146
|
): Promise<void> => {
|
|
147
|
-
if (!options.
|
|
147
|
+
if (!options.insights || didAttemptAutomaticInsights) {
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
didAttemptAutomaticInsights = true;
|
|
152
152
|
|
|
153
|
-
if (nativeResult.status !== "UNCHANGED" && !
|
|
153
|
+
if (nativeResult.status !== "UNCHANGED" && !insightsEvent) {
|
|
154
154
|
throw new Error(
|
|
155
|
-
"[HotUpdater] Native launch report is missing persisted metadata required for automatic
|
|
155
|
+
"[HotUpdater] Native launch report is missing persisted metadata required for automatic insights.",
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
const session = await options.client.createSession();
|
|
160
|
-
await session.
|
|
161
|
-
|
|
160
|
+
await session.sendInsightsEvent(
|
|
161
|
+
buildNotifyAppReadyInsightsParams(nativeResult, insightsEvent, options),
|
|
162
162
|
);
|
|
163
163
|
};
|
|
164
164
|
|
|
@@ -172,14 +172,14 @@ export const handleNotifyAppReady = async (
|
|
|
172
172
|
nativeReadResult = readNotifyAppReady();
|
|
173
173
|
} while (nativeReadResult.pending);
|
|
174
174
|
|
|
175
|
-
const {
|
|
175
|
+
const { insightsEvent, result: nativeResult } = nativeReadResult;
|
|
176
176
|
|
|
177
177
|
try {
|
|
178
|
-
await
|
|
178
|
+
await maybeSendAutomaticInsights(options, nativeResult, insightsEvent);
|
|
179
179
|
} catch (error) {
|
|
180
180
|
const warning = error instanceof Error ? error : new Error(String(error));
|
|
181
181
|
console.warn(
|
|
182
|
-
"[HotUpdater] Automatic notifyAppReady
|
|
182
|
+
"[HotUpdater] Automatic notifyAppReady insights failed:",
|
|
183
183
|
warning,
|
|
184
184
|
);
|
|
185
185
|
}
|
package/src/sdkVersion.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const HOT_UPDATER_SDK_VERSION = "1.0.0-rc.
|
|
1
|
+
export const HOT_UPDATER_SDK_VERSION = "1.0.0-rc.3";
|
package/src/wrap.spec.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, waitFor } from "@testing-library/react";
|
|
4
|
+
import { createElement, type ComponentType } from "react";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
6
|
|
|
4
7
|
import type {
|
|
5
|
-
|
|
8
|
+
NotifyAppReadyInsightsEvent,
|
|
6
9
|
NotifyAppReadyResult,
|
|
7
10
|
} from "./native";
|
|
8
11
|
import type { HotUpdaterOptions } from "./wrap";
|
|
@@ -15,14 +18,14 @@ vi.mock("react-native", () => ({
|
|
|
15
18
|
|
|
16
19
|
const createNotifyReadResult = (
|
|
17
20
|
result: NotifyAppReadyResult = { status: "UNCHANGED" },
|
|
18
|
-
|
|
21
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null = null,
|
|
19
22
|
pending = false,
|
|
20
23
|
): {
|
|
21
|
-
|
|
24
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null;
|
|
22
25
|
pending: boolean;
|
|
23
26
|
result: NotifyAppReadyResult;
|
|
24
27
|
} => ({
|
|
25
|
-
|
|
28
|
+
insightsEvent,
|
|
26
29
|
pending,
|
|
27
30
|
result,
|
|
28
31
|
});
|
|
@@ -32,6 +35,7 @@ const mocks = vi.hoisted(() => ({
|
|
|
32
35
|
checkForUpdate: vi.fn(),
|
|
33
36
|
getAppVersion: vi.fn(() => "1.0.0"),
|
|
34
37
|
getBundleId: vi.fn(() => "bundle-id"),
|
|
38
|
+
getUpdateId: vi.fn(() => "release-id"),
|
|
35
39
|
getChannel: vi.fn(() => "production"),
|
|
36
40
|
getCohort: vi.fn(() => "123"),
|
|
37
41
|
getFingerprintHash: vi.fn(() => "fingerprint-hash"),
|
|
@@ -39,7 +43,7 @@ const mocks = vi.hoisted(() => ({
|
|
|
39
43
|
getPersistedUserIdentity: vi.fn(() => ({})),
|
|
40
44
|
readNotifyAppReady: vi.fn<
|
|
41
45
|
() => {
|
|
42
|
-
|
|
46
|
+
insightsEvent: NotifyAppReadyInsightsEvent | null;
|
|
43
47
|
pending: boolean;
|
|
44
48
|
result: NotifyAppReadyResult;
|
|
45
49
|
}
|
|
@@ -55,6 +59,7 @@ vi.mock("./native", () => ({
|
|
|
55
59
|
addListener: mocks.addListener,
|
|
56
60
|
getAppVersion: mocks.getAppVersion,
|
|
57
61
|
getBundleId: mocks.getBundleId,
|
|
62
|
+
getUpdateId: mocks.getUpdateId,
|
|
58
63
|
getChannel: mocks.getChannel,
|
|
59
64
|
getCohort: mocks.getCohort,
|
|
60
65
|
getFingerprintHash: mocks.getFingerprintHash,
|
|
@@ -65,16 +70,16 @@ vi.mock("./native", () => ({
|
|
|
65
70
|
}));
|
|
66
71
|
|
|
67
72
|
const createClient = (
|
|
68
|
-
|
|
73
|
+
sendInsightsEvent = vi.fn().mockResolvedValue(undefined),
|
|
69
74
|
) => ({
|
|
70
75
|
client: {
|
|
71
76
|
createSession: vi.fn(async () => ({
|
|
72
77
|
fetchReleaseCatalog: vi.fn(),
|
|
73
78
|
resolveArtifact: vi.fn(),
|
|
74
|
-
|
|
79
|
+
sendInsightsEvent,
|
|
75
80
|
})),
|
|
76
81
|
},
|
|
77
|
-
|
|
82
|
+
sendInsightsEvent,
|
|
78
83
|
});
|
|
79
84
|
|
|
80
85
|
describe("HotUpdater wrap initialization", () => {
|
|
@@ -92,6 +97,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
92
97
|
mocks.getAppVersion.mockReturnValue("1.0.0");
|
|
93
98
|
mocks.getPersistedUserIdentity.mockReturnValue({});
|
|
94
99
|
mocks.getBundleId.mockReturnValue("bundle-id");
|
|
100
|
+
mocks.getUpdateId.mockReturnValue("release-id");
|
|
95
101
|
mocks.getChannel.mockReturnValue("production");
|
|
96
102
|
mocks.getCohort.mockReturnValue("123");
|
|
97
103
|
mocks.getFingerprintHash.mockReturnValue("fingerprint-hash");
|
|
@@ -99,6 +105,39 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
99
105
|
mocks.readNotifyAppReady.mockReturnValue(createNotifyReadResult());
|
|
100
106
|
});
|
|
101
107
|
|
|
108
|
+
afterEach(cleanup);
|
|
109
|
+
|
|
110
|
+
it("reports the console ID when up to date while retaining file IDs in insights", async () => {
|
|
111
|
+
const onUpdateProcessCompleted = vi.fn();
|
|
112
|
+
const { client, sendInsightsEvent } = createClient();
|
|
113
|
+
const { wrap } = await import("./wrap");
|
|
114
|
+
const WrappedComponent = wrap({
|
|
115
|
+
insights: true,
|
|
116
|
+
client,
|
|
117
|
+
onUpdateProcessCompleted,
|
|
118
|
+
updateStrategy: "appVersion",
|
|
119
|
+
})(() => null);
|
|
120
|
+
|
|
121
|
+
render(createElement(WrappedComponent));
|
|
122
|
+
|
|
123
|
+
await waitFor(() =>
|
|
124
|
+
expect(onUpdateProcessCompleted).toHaveBeenCalledWith({
|
|
125
|
+
id: "release-id",
|
|
126
|
+
message: null,
|
|
127
|
+
shouldForceUpdate: false,
|
|
128
|
+
status: "UP_TO_DATE",
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
await waitFor(() =>
|
|
132
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith(
|
|
133
|
+
expect.objectContaining({
|
|
134
|
+
toBundleId: "bundle-id",
|
|
135
|
+
type: "UNCHANGED",
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
102
141
|
it("returns void from init and defers notifyAppReady to the next frame", async () => {
|
|
103
142
|
vi.useFakeTimers();
|
|
104
143
|
|
|
@@ -110,7 +149,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
110
149
|
);
|
|
111
150
|
vi.stubGlobal("requestAnimationFrame", requestAnimationFrame);
|
|
112
151
|
|
|
113
|
-
const { client,
|
|
152
|
+
const { client, sendInsightsEvent } = createClient();
|
|
114
153
|
const { init } = await import("./wrap");
|
|
115
154
|
|
|
116
155
|
const result = init({
|
|
@@ -123,17 +162,17 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
123
162
|
|
|
124
163
|
expect(result).toBeUndefined();
|
|
125
164
|
expect(mocks.readNotifyAppReady).not.toHaveBeenCalled();
|
|
126
|
-
expect(
|
|
165
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
127
166
|
|
|
128
167
|
expect(requestAnimationFrame).toHaveBeenCalled();
|
|
129
168
|
|
|
130
169
|
await vi.runOnlyPendingTimersAsync();
|
|
131
170
|
|
|
132
171
|
expect(mocks.readNotifyAppReady).toHaveBeenCalledWith();
|
|
133
|
-
expect(
|
|
172
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
134
173
|
});
|
|
135
174
|
|
|
136
|
-
it("waits for native launch verification before sending
|
|
175
|
+
it("waits for native launch verification before sending insights", async () => {
|
|
137
176
|
vi.useFakeTimers();
|
|
138
177
|
|
|
139
178
|
vi.stubGlobal(
|
|
@@ -162,18 +201,18 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
162
201
|
),
|
|
163
202
|
);
|
|
164
203
|
|
|
165
|
-
const { client,
|
|
204
|
+
const { client, sendInsightsEvent } = createClient();
|
|
166
205
|
const { init } = await import("./wrap");
|
|
167
206
|
|
|
168
|
-
init({
|
|
207
|
+
init({ insights: true, client });
|
|
169
208
|
|
|
170
209
|
await vi.runAllTimersAsync();
|
|
171
210
|
|
|
172
211
|
expect(mocks.readNotifyAppReady).toHaveBeenCalledTimes(2);
|
|
173
|
-
expect(
|
|
212
|
+
expect(sendInsightsEvent).toHaveBeenCalledTimes(1);
|
|
174
213
|
});
|
|
175
214
|
|
|
176
|
-
it("sends automatic
|
|
215
|
+
it("sends automatic insights only from init when enabled", async () => {
|
|
177
216
|
vi.useFakeTimers();
|
|
178
217
|
|
|
179
218
|
vi.stubGlobal(
|
|
@@ -205,11 +244,11 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
205
244
|
username: "alice",
|
|
206
245
|
});
|
|
207
246
|
|
|
208
|
-
const { client,
|
|
247
|
+
const { client, sendInsightsEvent } = createClient();
|
|
209
248
|
const { init } = await import("./wrap");
|
|
210
249
|
|
|
211
250
|
init({
|
|
212
|
-
|
|
251
|
+
insights: true,
|
|
213
252
|
requestHeaders: {
|
|
214
253
|
Authorization: "Bearer token",
|
|
215
254
|
},
|
|
@@ -219,7 +258,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
219
258
|
|
|
220
259
|
await vi.runOnlyPendingTimersAsync();
|
|
221
260
|
|
|
222
|
-
expect(
|
|
261
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith({
|
|
223
262
|
appVersion: "1.0.0",
|
|
224
263
|
channel: "production",
|
|
225
264
|
cohort: "123",
|
|
@@ -241,7 +280,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
241
280
|
});
|
|
242
281
|
});
|
|
243
282
|
|
|
244
|
-
it("guards automatic
|
|
283
|
+
it("guards automatic insights to a single delivery attempt per runtime", async () => {
|
|
245
284
|
vi.useFakeTimers();
|
|
246
285
|
|
|
247
286
|
vi.stubGlobal(
|
|
@@ -268,18 +307,18 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
268
307
|
),
|
|
269
308
|
);
|
|
270
309
|
|
|
271
|
-
const { client,
|
|
310
|
+
const { client, sendInsightsEvent } = createClient();
|
|
272
311
|
const { init } = await import("./wrap");
|
|
273
312
|
|
|
274
|
-
init({
|
|
275
|
-
init({
|
|
313
|
+
init({ insights: true, client });
|
|
314
|
+
init({ insights: true, client });
|
|
276
315
|
|
|
277
316
|
await vi.runOnlyPendingTimersAsync();
|
|
278
317
|
|
|
279
|
-
expect(
|
|
318
|
+
expect(sendInsightsEvent).toHaveBeenCalledTimes(1);
|
|
280
319
|
});
|
|
281
320
|
|
|
282
|
-
it("warns without interrupting app readiness when
|
|
321
|
+
it("warns without interrupting app readiness when insights transport fails", async () => {
|
|
283
322
|
vi.useFakeTimers();
|
|
284
323
|
|
|
285
324
|
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
@@ -312,7 +351,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
312
351
|
const { init } = await import("./wrap");
|
|
313
352
|
|
|
314
353
|
init({
|
|
315
|
-
|
|
354
|
+
insights: true,
|
|
316
355
|
onError,
|
|
317
356
|
onNotifyAppReady,
|
|
318
357
|
client,
|
|
@@ -322,7 +361,7 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
322
361
|
|
|
323
362
|
expect(onError).not.toHaveBeenCalled();
|
|
324
363
|
expect(warn).toHaveBeenCalledWith(
|
|
325
|
-
"[HotUpdater] Automatic notifyAppReady
|
|
364
|
+
"[HotUpdater] Automatic notifyAppReady insights failed:",
|
|
326
365
|
error,
|
|
327
366
|
);
|
|
328
367
|
expect(onNotifyAppReady).toHaveBeenCalledWith({
|
|
@@ -344,14 +383,14 @@ describe("HotUpdater wrap initialization", () => {
|
|
|
344
383
|
|
|
345
384
|
it("preserves wrapped component prop inference", async () => {
|
|
346
385
|
const { wrap } = await import("./wrap");
|
|
347
|
-
const Component:
|
|
386
|
+
const Component: ComponentType<{ title: string }> = () => null;
|
|
348
387
|
|
|
349
388
|
const WrappedComponent = wrap({
|
|
350
389
|
client: createClient().client,
|
|
351
390
|
updateStrategy: "appVersion",
|
|
352
391
|
})(Component);
|
|
353
392
|
|
|
354
|
-
const acceptsTitleProps:
|
|
393
|
+
const acceptsTitleProps: ComponentType<{ title: string }> =
|
|
355
394
|
WrappedComponent;
|
|
356
395
|
expect(acceptsTitleProps).toBe(WrappedComponent);
|
|
357
396
|
});
|
package/src/wrap.tsx
CHANGED
|
@@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { checkForUpdate } from "./checkForUpdate";
|
|
4
4
|
import { useEventCallback } from "./hooks/useEventCallback";
|
|
5
|
-
import {
|
|
6
|
-
import { handleNotifyAppReady } from "./
|
|
5
|
+
import { getUpdateId, reload } from "./native";
|
|
6
|
+
import { handleNotifyAppReady } from "./notifyAppReadyInsights";
|
|
7
7
|
import { useHotUpdaterStore } from "./store";
|
|
8
8
|
import type {
|
|
9
9
|
InternalInitOptions,
|
|
@@ -46,7 +46,7 @@ export function wrap(
|
|
|
46
46
|
setUpdateStatus("CHECK_FOR_UPDATE");
|
|
47
47
|
|
|
48
48
|
const updateInfo = await checkForUpdate({
|
|
49
|
-
|
|
49
|
+
insights: restOptions.insights,
|
|
50
50
|
client: restOptions.client,
|
|
51
51
|
updateStrategy: restOptions.updateStrategy,
|
|
52
52
|
requestHeaders: restOptions.requestHeaders,
|
|
@@ -61,7 +61,7 @@ export function wrap(
|
|
|
61
61
|
status: "UP_TO_DATE",
|
|
62
62
|
shouldForceUpdate: false,
|
|
63
63
|
message: null,
|
|
64
|
-
id:
|
|
64
|
+
id: getUpdateId(),
|
|
65
65
|
});
|
|
66
66
|
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
67
67
|
return;
|