@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.14
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/proguard-rules.pro +15 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1 -1
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +7 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
- package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +102 -20
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
- package/lib/commonjs/checkForUpdate.js +25 -13
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +88 -24
- 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 +30 -9
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +56 -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/notifyAppReadyInsights.js +185 -0
- package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +102 -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/sdkVersion.js.map +1 -1
- package/lib/commonjs/wrap.js +10 -9
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +90 -25
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/checkForUpdate.js +25 -13
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +88 -24
- 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 +31 -10
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +56 -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/notifyAppReadyInsights.js +178 -0
- package/lib/module/notifyAppReadyInsights.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +94 -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/sdkVersion.js.map +1 -1
- package/lib/module/wrap.js +10 -9
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +92 -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 +6 -6
- 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/commonjs/notifyAppReadyInsights.d.ts +25 -0
- 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/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts.map +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 +6 -6
- 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/module/notifyAppReadyInsights.d.ts +25 -0
- 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/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts.map +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 +3 -3
- package/src/checkForUpdate.releaseCatalog.spec.ts +127 -57
- package/src/checkForUpdate.ts +30 -18
- package/src/httpClient.ts +11 -11
- package/src/index.spec.ts +59 -11
- package/src/index.ts +41 -13
- 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} +81 -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/notifyAppReadyInsights.ts +281 -0
- package/src/sdkVersion.ts +1 -1
- package/src/wrap.spec.ts +101 -31
- package/src/wrap.tsx +11 -9
- package/src/wrap.types.ts +5 -6
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
- 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 +0 -128
- 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 +0 -13
- 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 +0 -13
- 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
- package/src/notifyAppReadyAnalytics.ts +0 -194
|
@@ -23,9 +23,10 @@ export type HotUpdaterFallbackComponentProps = {
|
|
|
23
23
|
interface CommonHotUpdaterOptions {
|
|
24
24
|
/**
|
|
25
25
|
* Sends app-ready transitions and same-Bundle Release adoptions to the
|
|
26
|
-
* configured server.
|
|
26
|
+
* configured server. Defaults to `true`; set to `false` to disable client
|
|
27
|
+
* reporting.
|
|
27
28
|
*/
|
|
28
|
-
|
|
29
|
+
insights?: boolean;
|
|
29
30
|
/** Base URL of a server exposing the Hot Updater v1 client HTTP protocol. */
|
|
30
31
|
baseURL: HotUpdaterBaseURL;
|
|
31
32
|
requestHeaders?: Record<string, string>;
|
|
@@ -43,7 +44,7 @@ export type AutoUpdateOptions = CommonHotUpdaterOptions & {
|
|
|
43
44
|
export type HotUpdaterInitOptions = CommonHotUpdaterOptions;
|
|
44
45
|
export type HotUpdaterOptions = AutoUpdateOptions;
|
|
45
46
|
type InternalCommonOptions = {
|
|
46
|
-
|
|
47
|
+
insights?: boolean;
|
|
47
48
|
client: HotUpdaterHttpClient;
|
|
48
49
|
requestHeaders?: Record<string, string>;
|
|
49
50
|
requestTimeout?: number;
|
|
@@ -57,9 +58,7 @@ type InternalAutoUpdateOptions = InternalCommonOptions & {
|
|
|
57
58
|
reloadOnForceUpdate?: boolean;
|
|
58
59
|
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
59
60
|
};
|
|
60
|
-
export type InternalInitOptions = InternalCommonOptions
|
|
61
|
-
analytics?: boolean;
|
|
62
|
-
};
|
|
61
|
+
export type InternalInitOptions = InternalCommonOptions;
|
|
63
62
|
export type InternalWrapOptions = InternalAutoUpdateOptions;
|
|
64
63
|
export {};
|
|
65
64
|
//# sourceMappingURL=wrap.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap.types.d.ts","sourceRoot":"","sources":["../../../src/wrap.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC7C,iBAAiB,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,UAAU,GACV,0BAA0B,CAAC;AAE/B,MAAM,MAAM,gCAAgC,GAAG;IAC7C,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC,iBAAiB,CAAC,CAAC;IACpD,UAAU,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC1C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,eAAe,CAAC,cAAc,CAAC,CAAC;IAC9C,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF,UAAU,uBAAuB;IAC/B
|
|
1
|
+
{"version":3,"file":"wrap.types.d.ts","sourceRoot":"","sources":["../../../src/wrap.types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.14",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/commonjs/index.js",
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
122
|
"use-sync-external-store": "1.5.0",
|
|
123
|
-
"@hot-updater/core": "1.0.0-rc.
|
|
124
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
123
|
+
"@hot-updater/core": "1.0.0-rc.14",
|
|
124
|
+
"@hot-updater/plugin-core": "1.0.0-rc.14"
|
|
125
125
|
},
|
|
126
126
|
"scripts": {
|
|
127
127
|
"build": "node ./scripts/sync-sdk-version.mjs run -- bob build",
|
|
@@ -81,20 +81,21 @@ const createClient = (catalog = createCatalog()) => {
|
|
|
81
81
|
fileHash: "bundle-hash",
|
|
82
82
|
fileUrl: "https://updates.example.com/bundle.zip",
|
|
83
83
|
}));
|
|
84
|
-
const
|
|
84
|
+
const sendInsightsEvent = vi.fn(async () => undefined);
|
|
85
85
|
const session = {
|
|
86
86
|
fetchReleaseCatalog,
|
|
87
87
|
resolveArtifact,
|
|
88
|
-
|
|
88
|
+
sendInsightsEvent,
|
|
89
89
|
};
|
|
90
90
|
const client: HotUpdaterHttpClient = {
|
|
91
91
|
createSession: vi.fn(async () => session),
|
|
92
92
|
};
|
|
93
|
-
return { client, fetchReleaseCatalog, resolveArtifact,
|
|
93
|
+
return { client, fetchReleaseCatalog, resolveArtifact, sendInsightsEvent };
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
describe("checkForUpdate Release catalog protocol", () => {
|
|
97
97
|
beforeEach(() => {
|
|
98
|
+
vi.resetModules();
|
|
98
99
|
vi.clearAllMocks();
|
|
99
100
|
vi.stubGlobal("__DEV__", false);
|
|
100
101
|
mocks.acceptReleaseCatalog.mockReturnValue(true);
|
|
@@ -109,6 +110,59 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
109
110
|
mocks.updateBundle.mockResolvedValue(true);
|
|
110
111
|
});
|
|
111
112
|
|
|
113
|
+
it("reports an optional download only after staging, without treating the target as running", async () => {
|
|
114
|
+
const { checkForUpdate } = await import("./checkForUpdate");
|
|
115
|
+
const { client, sendInsightsEvent } = createClient();
|
|
116
|
+
let complete!: (success: boolean) => void;
|
|
117
|
+
mocks.updateBundle.mockImplementationOnce(
|
|
118
|
+
() =>
|
|
119
|
+
new Promise<boolean>((resolve) => {
|
|
120
|
+
complete = resolve;
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
const update = await checkForUpdate({
|
|
124
|
+
client,
|
|
125
|
+
insights: true,
|
|
126
|
+
updateStrategy: "appVersion",
|
|
127
|
+
});
|
|
128
|
+
expect(update?.shouldForceUpdate).toBe(false);
|
|
129
|
+
const pending = update!.updateBundle();
|
|
130
|
+
await vi.waitFor(() => expect(mocks.updateBundle).toHaveBeenCalledOnce());
|
|
131
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
132
|
+
complete(true);
|
|
133
|
+
await expect(pending).resolves.toBe(true);
|
|
134
|
+
expect(sendInsightsEvent).toHaveBeenCalledExactlyOnceWith(
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
type: "UPDATE_DOWNLOADED",
|
|
137
|
+
fromBundleId: CURRENT_BUNDLE_ID,
|
|
138
|
+
toBundleId: TARGET_BUNDLE_ID,
|
|
139
|
+
fromReleaseId: null,
|
|
140
|
+
toReleaseId: RELEASE_ID,
|
|
141
|
+
updateStrategy: "appVersion",
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
await update!.updateBundle();
|
|
145
|
+
expect(sendInsightsEvent).toHaveBeenCalledOnce();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it.each([false, "failure"])(
|
|
149
|
+
"does not report a download when staging returns %s",
|
|
150
|
+
async (result) => {
|
|
151
|
+
const { checkForUpdate } = await import("./checkForUpdate");
|
|
152
|
+
const { client, sendInsightsEvent } = createClient();
|
|
153
|
+
if (result === false) mocks.updateBundle.mockResolvedValueOnce(false);
|
|
154
|
+
else
|
|
155
|
+
mocks.updateBundle.mockRejectedValueOnce(new Error("download failed"));
|
|
156
|
+
const update = await checkForUpdate({
|
|
157
|
+
client,
|
|
158
|
+
insights: true,
|
|
159
|
+
updateStrategy: "appVersion",
|
|
160
|
+
});
|
|
161
|
+
await update!.updateBundle().catch(() => false);
|
|
162
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
112
166
|
it("selects locally and defers Bundle artifact resolution until install", async () => {
|
|
113
167
|
const { checkForUpdate } = await import("./checkForUpdate");
|
|
114
168
|
const { client, fetchReleaseCatalog, resolveArtifact } = createClient();
|
|
@@ -119,7 +173,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
119
173
|
});
|
|
120
174
|
|
|
121
175
|
expect(result).toMatchObject({
|
|
122
|
-
id:
|
|
176
|
+
id: RELEASE_ID,
|
|
123
177
|
releaseId: RELEASE_ID,
|
|
124
178
|
transitionKind: "INSTALL",
|
|
125
179
|
});
|
|
@@ -182,7 +236,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
182
236
|
},
|
|
183
237
|
],
|
|
184
238
|
});
|
|
185
|
-
const { client, resolveArtifact,
|
|
239
|
+
const { client, resolveArtifact, sendInsightsEvent } =
|
|
186
240
|
createClient(forceCatalog);
|
|
187
241
|
|
|
188
242
|
const result = await checkForUpdate({
|
|
@@ -191,13 +245,14 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
191
245
|
});
|
|
192
246
|
|
|
193
247
|
expect(result).toMatchObject({
|
|
248
|
+
id: RELEASE_ID,
|
|
194
249
|
shouldForceUpdate: false,
|
|
195
250
|
transitionKind: "ADOPT_RELEASE",
|
|
196
251
|
});
|
|
197
252
|
await expect(result?.updateBundle()).resolves.toBe(true);
|
|
198
253
|
expect(resolveArtifact).not.toHaveBeenCalled();
|
|
199
254
|
expect(mocks.updateBundle).not.toHaveBeenCalled();
|
|
200
|
-
expect(
|
|
255
|
+
expect(sendInsightsEvent).not.toHaveBeenCalled();
|
|
201
256
|
expect(mocks.commitReleaseSelection).toHaveBeenCalledWith({
|
|
202
257
|
guard: expect.objectContaining({ generation: 2, scopeKey: SCOPE_KEY }),
|
|
203
258
|
selection: expect.objectContaining({
|
|
@@ -207,7 +262,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
207
262
|
});
|
|
208
263
|
});
|
|
209
264
|
|
|
210
|
-
it("
|
|
265
|
+
it("reports same-file selection as UNCHANGED with the selected release identity", async () => {
|
|
211
266
|
const active: PersistedSelectionReceipt = {
|
|
212
267
|
catalogId: CATALOG_ID,
|
|
213
268
|
bundleId: TARGET_BUNDLE_ID,
|
|
@@ -227,20 +282,23 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
227
282
|
verificationPending: false,
|
|
228
283
|
});
|
|
229
284
|
const { checkForUpdate } = await import("./checkForUpdate");
|
|
230
|
-
const { client,
|
|
285
|
+
const { client, sendInsightsEvent } = createClient();
|
|
231
286
|
|
|
232
287
|
const result = await checkForUpdate({
|
|
233
|
-
|
|
288
|
+
insights: true,
|
|
234
289
|
client,
|
|
235
290
|
updateStrategy: "appVersion",
|
|
236
291
|
});
|
|
237
292
|
await result?.updateBundle();
|
|
238
293
|
|
|
239
|
-
expect(
|
|
294
|
+
expect(sendInsightsEvent).toHaveBeenCalledWith(
|
|
240
295
|
expect.objectContaining({
|
|
296
|
+
fromBundleId: null,
|
|
297
|
+
updateStrategy: null,
|
|
241
298
|
fromReleaseId: MINIMUM_RELEASE_ID,
|
|
299
|
+
toBundleId: TARGET_BUNDLE_ID,
|
|
242
300
|
toReleaseId: RELEASE_ID,
|
|
243
|
-
type: "
|
|
301
|
+
type: "UNCHANGED",
|
|
244
302
|
}),
|
|
245
303
|
);
|
|
246
304
|
});
|
|
@@ -280,7 +338,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
280
338
|
});
|
|
281
339
|
|
|
282
340
|
expect(result).toMatchObject({
|
|
283
|
-
id:
|
|
341
|
+
id: RELEASE_ID,
|
|
284
342
|
releaseId: RELEASE_ID,
|
|
285
343
|
status: "UPDATE",
|
|
286
344
|
transitionKind: "INSTALL",
|
|
@@ -315,7 +373,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
315
373
|
});
|
|
316
374
|
|
|
317
375
|
expect(result).toMatchObject({
|
|
318
|
-
id:
|
|
376
|
+
id: RELEASE_ID,
|
|
319
377
|
releaseId: RELEASE_ID,
|
|
320
378
|
transitionKind: "ADOPT_RELEASE",
|
|
321
379
|
});
|
|
@@ -395,7 +453,7 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
395
453
|
});
|
|
396
454
|
|
|
397
455
|
expect(result).toMatchObject({
|
|
398
|
-
id:
|
|
456
|
+
id: RELEASE_ID,
|
|
399
457
|
releaseId: RELEASE_ID,
|
|
400
458
|
shouldForceUpdate: true,
|
|
401
459
|
status: "ROLLBACK",
|
|
@@ -448,58 +506,70 @@ describe("checkForUpdate Release catalog protocol", () => {
|
|
|
448
506
|
});
|
|
449
507
|
|
|
450
508
|
expect(result).toMatchObject({
|
|
451
|
-
id:
|
|
509
|
+
id: RELEASE_ID,
|
|
452
510
|
shouldForceUpdate: true,
|
|
453
511
|
status: "ROLLBACK",
|
|
454
512
|
transitionKind: "INSTALL",
|
|
455
513
|
});
|
|
456
514
|
});
|
|
457
515
|
|
|
458
|
-
it("
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
516
|
+
it.each(["EMBEDDED", "BUILTIN"] as const)(
|
|
517
|
+
"returns a forced %s rollback with its console ID or built-in fallback",
|
|
518
|
+
async (kind) => {
|
|
519
|
+
const active: PersistedSelectionReceipt = {
|
|
520
|
+
catalogId: CATALOG_ID,
|
|
521
|
+
bundleId: CURRENT_BUNDLE_ID,
|
|
522
|
+
catalogHash: `sha256:${"b".repeat(64)}`,
|
|
523
|
+
channel: CHANNEL,
|
|
524
|
+
generation: 1,
|
|
525
|
+
kind: "BUNDLE",
|
|
526
|
+
releaseId: MINIMUM_RELEASE_ID,
|
|
527
|
+
scopeKey: SCOPE_KEY,
|
|
528
|
+
selectionContextHash: "old-context",
|
|
529
|
+
};
|
|
530
|
+
mocks.getActiveUpdateState.mockReturnValueOnce({
|
|
531
|
+
activeSelection: active,
|
|
532
|
+
highestSeenCatalogs: {},
|
|
533
|
+
stableSelection: active,
|
|
534
|
+
verificationPending: false,
|
|
535
|
+
});
|
|
536
|
+
const { checkForUpdate } = await import("./checkForUpdate");
|
|
537
|
+
const { client, resolveArtifact } = createClient(
|
|
538
|
+
createCatalog({
|
|
539
|
+
releases:
|
|
540
|
+
kind === "EMBEDDED"
|
|
541
|
+
? [{ ...createCatalog().releases[0]!, kind, bundleId: null }]
|
|
542
|
+
: [],
|
|
543
|
+
rollbackReleases: [],
|
|
544
|
+
}),
|
|
545
|
+
);
|
|
480
546
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
547
|
+
const result = await checkForUpdate({
|
|
548
|
+
client,
|
|
549
|
+
updateStrategy: "appVersion",
|
|
550
|
+
});
|
|
485
551
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
552
|
+
expect(result).toMatchObject({
|
|
553
|
+
id: kind === "EMBEDDED" ? RELEASE_ID : MINIMUM_RELEASE_ID,
|
|
554
|
+
releaseId: kind === "EMBEDDED" ? RELEASE_ID : null,
|
|
555
|
+
shouldForceUpdate: true,
|
|
556
|
+
status: "ROLLBACK",
|
|
557
|
+
transitionKind: kind === "EMBEDDED" ? "USE_EMBEDDED" : "USE_BUILTIN",
|
|
558
|
+
});
|
|
559
|
+
await expect(result?.updateBundle()).resolves.toBe(true);
|
|
560
|
+
expect(mocks.commitReleaseSelection).toHaveBeenCalledWith(
|
|
561
|
+
expect.objectContaining({
|
|
562
|
+
selection: expect.objectContaining({
|
|
563
|
+
bundleId: MINIMUM_RELEASE_ID,
|
|
564
|
+
kind,
|
|
565
|
+
releaseId: kind === "EMBEDDED" ? RELEASE_ID : null,
|
|
566
|
+
}),
|
|
499
567
|
}),
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
568
|
+
);
|
|
569
|
+
expect(resolveArtifact).not.toHaveBeenCalled();
|
|
570
|
+
expect(mocks.updateBundle).not.toHaveBeenCalled();
|
|
571
|
+
},
|
|
572
|
+
);
|
|
503
573
|
|
|
504
574
|
it("does not surface a rollback when the app already uses built-in bytes", async () => {
|
|
505
575
|
mocks.getBundleId.mockReturnValueOnce(MINIMUM_RELEASE_ID);
|
package/src/checkForUpdate.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
isChannelSwitched,
|
|
31
31
|
updateBundle,
|
|
32
32
|
} from "./native";
|
|
33
|
+
import { reportBundleDownloaded } from "./notifyAppReadyInsights";
|
|
33
34
|
import {
|
|
34
35
|
hasExpectedReleaseCatalogScope,
|
|
35
36
|
type ExpectedReleaseCatalogScope,
|
|
@@ -83,7 +84,7 @@ export type ReleaseTransitionKind =
|
|
|
83
84
|
| "USE_BUILTIN";
|
|
84
85
|
|
|
85
86
|
export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
|
|
86
|
-
|
|
87
|
+
insights?: boolean;
|
|
87
88
|
client: HotUpdaterHttpClient;
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -128,7 +129,7 @@ const resetProgress = () => {
|
|
|
128
129
|
});
|
|
129
130
|
};
|
|
130
131
|
|
|
131
|
-
const
|
|
132
|
+
const notifyUnchangedSelection = async (input: {
|
|
132
133
|
readonly active: PersistedSelectionReceipt | null;
|
|
133
134
|
readonly desired: PersistedSelectionReceipt;
|
|
134
135
|
readonly appVersion: string;
|
|
@@ -138,16 +139,15 @@ const notifyReleaseAdoption = async (input: {
|
|
|
138
139
|
readonly session: HotUpdaterHttpSession;
|
|
139
140
|
readonly requestHeaders?: Record<string, string>;
|
|
140
141
|
readonly requestTimeout?: number;
|
|
141
|
-
readonly updateStrategy: "appVersion" | "fingerprint";
|
|
142
142
|
}): Promise<void> => {
|
|
143
143
|
const { userId, username } = getPersistedUserIdentity();
|
|
144
144
|
try {
|
|
145
|
-
await input.session.
|
|
145
|
+
await input.session.sendInsightsEvent({
|
|
146
146
|
appVersion: input.appVersion,
|
|
147
147
|
channel: input.desired.channel,
|
|
148
148
|
cohort: input.cohort,
|
|
149
149
|
fingerprintHash: input.fingerprintHash,
|
|
150
|
-
fromBundleId:
|
|
150
|
+
fromBundleId: null,
|
|
151
151
|
fromReleaseId: input.active?.releaseId ?? null,
|
|
152
152
|
installId: getInstallId(),
|
|
153
153
|
platform: input.platform,
|
|
@@ -155,13 +155,13 @@ const notifyReleaseAdoption = async (input: {
|
|
|
155
155
|
requestTimeout: input.requestTimeout,
|
|
156
156
|
toBundleId: input.desired.bundleId,
|
|
157
157
|
toReleaseId: input.desired.releaseId,
|
|
158
|
-
type: "
|
|
159
|
-
updateStrategy:
|
|
158
|
+
type: "UNCHANGED",
|
|
159
|
+
updateStrategy: null,
|
|
160
160
|
...(userId === undefined ? {} : { userId }),
|
|
161
161
|
...(username === undefined ? {} : { username }),
|
|
162
162
|
});
|
|
163
163
|
} catch (error) {
|
|
164
|
-
console.warn("[HotUpdater]
|
|
164
|
+
console.warn("[HotUpdater] Unchanged selection insights failed:", error);
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
167
|
|
|
@@ -218,7 +218,8 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
218
218
|
const scopeKey = catalog.scopeKey;
|
|
219
219
|
|
|
220
220
|
const crashedBundleIds = getCrashHistory();
|
|
221
|
-
const
|
|
221
|
+
const activeState = getActiveUpdateState();
|
|
222
|
+
const active = activeState.activeSelection;
|
|
222
223
|
const explicitScopeSwitch =
|
|
223
224
|
input.explicitChannel !== undefined &&
|
|
224
225
|
input.explicitChannel !== input.currentChannel;
|
|
@@ -340,12 +341,8 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
340
341
|
guard,
|
|
341
342
|
selection: receipt,
|
|
342
343
|
});
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
transitionKind === "ADOPT_RELEASE" &&
|
|
346
|
-
options.analytics
|
|
347
|
-
) {
|
|
348
|
-
await notifyReleaseAdoption({
|
|
344
|
+
if (committed && transitionKind === "ADOPT_RELEASE" && options.insights) {
|
|
345
|
+
await notifyUnchangedSelection({
|
|
349
346
|
active,
|
|
350
347
|
appVersion: input.currentAppVersion,
|
|
351
348
|
cohort: input.cohort,
|
|
@@ -355,7 +352,6 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
355
352
|
requestHeaders: options.requestHeaders,
|
|
356
353
|
requestTimeout: options.requestTimeout,
|
|
357
354
|
session,
|
|
358
|
-
updateStrategy: options.updateStrategy,
|
|
359
355
|
});
|
|
360
356
|
}
|
|
361
357
|
return committed;
|
|
@@ -370,7 +366,7 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
370
366
|
if (!isReleaseSelectionCurrent(guard)) {
|
|
371
367
|
throw new StaleReleaseCatalogError();
|
|
372
368
|
}
|
|
373
|
-
|
|
369
|
+
const downloaded = await updateBundle({
|
|
374
370
|
bundleId: desired.bundleId,
|
|
375
371
|
changedAssets: artifact.changedAssets ?? null,
|
|
376
372
|
channel: input.targetChannel,
|
|
@@ -382,12 +378,28 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
382
378
|
shouldSkipCurrentBundleIdCheck: true,
|
|
383
379
|
status: desired.status,
|
|
384
380
|
});
|
|
381
|
+
if (downloaded) {
|
|
382
|
+
await reportBundleDownloaded(options, {
|
|
383
|
+
fromBundleId: input.currentBundleId,
|
|
384
|
+
fromReleaseId:
|
|
385
|
+
active?.bundleId === input.currentBundleId
|
|
386
|
+
? active.releaseId
|
|
387
|
+
: activeState.stableSelection?.bundleId === input.currentBundleId
|
|
388
|
+
? activeState.stableSelection.releaseId
|
|
389
|
+
: null,
|
|
390
|
+
toBundleId: desired.bundleId,
|
|
391
|
+
toReleaseId: desired.releaseId,
|
|
392
|
+
channel: input.targetChannel,
|
|
393
|
+
updateStrategy: options.updateStrategy,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
return downloaded;
|
|
385
397
|
};
|
|
386
398
|
|
|
387
399
|
return {
|
|
388
400
|
fileHash: null,
|
|
389
401
|
fileUrl: null,
|
|
390
|
-
id: desired.bundleId,
|
|
402
|
+
id: desired.releaseId ?? desired.bundleId,
|
|
391
403
|
message: release?.message ?? null,
|
|
392
404
|
releaseId: desired.releaseId,
|
|
393
405
|
rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
|
package/src/httpClient.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface ArtifactRequest {
|
|
|
27
27
|
readonly requestTimeout?: number;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
interface
|
|
30
|
+
interface InsightsEventCommonParams {
|
|
31
31
|
readonly installId: string;
|
|
32
32
|
readonly userId?: string;
|
|
33
33
|
readonly username?: string;
|
|
@@ -42,30 +42,30 @@ interface AnalyticsEventCommonParams {
|
|
|
42
42
|
readonly requestTimeout?: number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
type
|
|
46
|
-
readonly type: "
|
|
45
|
+
type InsightsTransitionEventParams = InsightsEventCommonParams & {
|
|
46
|
+
readonly type: "UPDATE_DOWNLOADED" | "UPDATE_APPLIED" | "RECOVERED";
|
|
47
47
|
readonly fromBundleId: string;
|
|
48
48
|
readonly toBundleId: string;
|
|
49
49
|
readonly updateStrategy: "fingerprint" | "appVersion";
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
type
|
|
52
|
+
type InsightsUnchangedEventParams = InsightsEventCommonParams & {
|
|
53
53
|
readonly type: "UNCHANGED";
|
|
54
54
|
readonly fromBundleId: null;
|
|
55
55
|
readonly toBundleId: string;
|
|
56
56
|
readonly updateStrategy: null;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export type
|
|
60
|
-
|
|
|
61
|
-
|
|
|
59
|
+
export type InsightsEventParams =
|
|
60
|
+
| InsightsTransitionEventParams
|
|
61
|
+
| InsightsUnchangedEventParams;
|
|
62
62
|
|
|
63
63
|
export interface HotUpdaterHttpSession {
|
|
64
64
|
fetchReleaseCatalog: (
|
|
65
65
|
params: ReleaseCatalogRequest,
|
|
66
66
|
) => Promise<ReleaseCatalog>;
|
|
67
67
|
resolveArtifact: (params: ArtifactRequest) => Promise<ArtifactInfo>;
|
|
68
|
-
|
|
68
|
+
sendInsightsEvent: (params: InsightsEventParams) => Promise<void>;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export interface HotUpdaterHttpClient {
|
|
@@ -147,9 +147,9 @@ const resolveArtifactUrls = (
|
|
|
147
147
|
}),
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
const
|
|
150
|
+
const sendInsightsEvent = async (
|
|
151
151
|
baseURL: string,
|
|
152
|
-
params:
|
|
152
|
+
params: InsightsEventParams,
|
|
153
153
|
): Promise<void> => {
|
|
154
154
|
const controller = new AbortController();
|
|
155
155
|
const timeoutId = setTimeout(() => {
|
|
@@ -250,7 +250,7 @@ const createSession = (baseURL: string): HotUpdaterHttpSession => ({
|
|
|
250
250
|
});
|
|
251
251
|
return resolveArtifactUrls(baseURL, info);
|
|
252
252
|
},
|
|
253
|
-
|
|
253
|
+
sendInsightsEvent: (params) => sendInsightsEvent(baseURL, params),
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
/** Creates the private HTTP client used by HotUpdater.init and HotUpdater.wrap. */
|