@hot-updater/react-native 1.0.0-rc.15 → 1.0.0-rc.17
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 +491 -388
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +5 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +290 -12
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +35 -19
- package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
- package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
- package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
- package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +110 -52
- package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
- package/android/src/newarch/HotUpdaterModule.kt +74 -69
- package/android/src/oldarch/HotUpdaterModule.kt +63 -59
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +1 -1
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +446 -61
- package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +220 -0
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +19 -27
- package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +71 -0
- package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +106 -0
- package/android/src/test/java/com/hotupdater/NoBackupContext.kt +11 -0
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +309 -23
- package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +57 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +238 -157
- package/ios/HotUpdater/Internal/BundleMetadata.swift +3 -53
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +8 -18
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +48 -29
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +108 -1
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- package/lib/commonjs/appReady.js +111 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/appReady.spec.js +360 -0
- package/lib/commonjs/appReady.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
- package/lib/commonjs/appReady.test-utils.js.map +1 -0
- package/lib/commonjs/baseURL.js +16 -0
- package/lib/commonjs/baseURL.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +104 -56
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +337 -74
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +49 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/entryImportGraph.spec.js +50 -0
- package/lib/commonjs/entryImportGraph.spec.js.map +1 -0
- package/lib/commonjs/fetchJSON.js +2 -6
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +15 -67
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/httpClient.spec.js +26 -9
- package/lib/commonjs/httpClient.spec.js.map +1 -1
- package/lib/commonjs/index.js +49 -25
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +126 -40
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +67 -48
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +80 -58
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/pluginHost.js +208 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/pluginHost.spec.js +292 -0
- package/lib/commonjs/pluginHost.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/eventId.js +129 -0
- package/lib/commonjs/plugins/insights/eventId.js.map +1 -0
- package/lib/commonjs/plugins/insights/eventId.spec.js +47 -0
- package/lib/commonjs/plugins/insights/eventId.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/index.js +272 -0
- package/lib/commonjs/plugins/insights/index.js.map +1 -0
- package/lib/commonjs/plugins/insights/insights.spec.js +640 -0
- package/lib/commonjs/plugins/insights/insights.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/sender.js +128 -0
- package/lib/commonjs/plugins/insights/sender.js.map +1 -0
- package/lib/commonjs/plugins/insights/sender.spec.js +265 -0
- package/lib/commonjs/plugins/insights/sender.spec.js.map +1 -0
- package/lib/commonjs/plugins/insights/state.js +145 -0
- package/lib/commonjs/plugins/insights/state.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +13 -2
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +18 -0
- package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/updateError.js +132 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/updateError.spec.js +149 -0
- package/lib/commonjs/updateError.spec.js.map +1 -0
- package/lib/commonjs/wrap.js +43 -25
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.progress.spec.js +195 -0
- package/lib/commonjs/wrap.progress.spec.js.map +1 -0
- package/lib/commonjs/wrap.spec.js +94 -230
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/appReady.js +103 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/appReady.spec.js +359 -0
- package/lib/module/appReady.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
- package/lib/module/appReady.test-utils.js.map +1 -0
- package/lib/module/baseURL.js +11 -0
- package/lib/module/baseURL.js.map +1 -0
- package/lib/module/checkForUpdate.js +103 -57
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +337 -74
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/clientPlugin.js +44 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/entryImportGraph.spec.js +50 -0
- package/lib/module/entryImportGraph.spec.js.map +1 -0
- package/lib/module/fetchJSON.js +2 -6
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +14 -66
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/httpClient.spec.js +26 -9
- package/lib/module/httpClient.spec.js.map +1 -1
- package/lib/module/index.js +44 -27
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +127 -41
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +63 -44
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +80 -58
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/pluginHost.js +200 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/pluginHost.spec.js +291 -0
- package/lib/module/pluginHost.spec.js.map +1 -0
- package/lib/module/plugins/insights/eventId.js +122 -0
- package/lib/module/plugins/insights/eventId.js.map +1 -0
- package/lib/module/plugins/insights/eventId.spec.js +47 -0
- package/lib/module/plugins/insights/eventId.spec.js.map +1 -0
- package/lib/module/plugins/insights/index.js +268 -0
- package/lib/module/plugins/insights/index.js.map +1 -0
- package/lib/module/plugins/insights/insights.spec.js +639 -0
- package/lib/module/plugins/insights/insights.spec.js.map +1 -0
- package/lib/module/plugins/insights/sender.js +123 -0
- package/lib/module/plugins/insights/sender.js.map +1 -0
- package/lib/module/plugins/insights/sender.spec.js +265 -0
- package/lib/module/plugins/insights/sender.spec.js.map +1 -0
- package/lib/module/plugins/insights/state.js +139 -0
- package/lib/module/plugins/insights/state.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +13 -2
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/releaseCatalogCache.spec.js +18 -0
- package/lib/module/releaseCatalogCache.spec.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/updateError.js +126 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/updateError.spec.js +149 -0
- package/lib/module/updateError.spec.js.map +1 -0
- package/lib/module/wrap.js +43 -25
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.progress.spec.js +196 -0
- package/lib/module/wrap.progress.spec.js.map +1 -0
- package/lib/module/wrap.spec.js +93 -229
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +23 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/appReady.test-utils.d.ts +9 -0
- package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/baseURL.d.ts +4 -0
- package/lib/typescript/commonjs/baseURL.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +15 -2
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +185 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts +2 -3
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +2 -30
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -6
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +29 -15
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +29 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +18 -0
- package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/index.d.ts +40 -0
- package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts +74 -0
- package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +1 -0
- package/lib/typescript/commonjs/plugins/insights/state.d.ts +46 -0
- package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +5 -1
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +34 -12
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/updateError.d.ts +26 -0
- package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +6 -5
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +23 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/appReady.test-utils.d.ts +9 -0
- package/lib/typescript/module/appReady.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/baseURL.d.ts +4 -0
- package/lib/typescript/module/baseURL.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +15 -2
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +185 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/module/fetchJSON.d.ts +2 -3
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +2 -30
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -6
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +29 -15
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +29 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/eventId.d.ts +18 -0
- package/lib/typescript/module/plugins/insights/eventId.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/index.d.ts +40 -0
- package/lib/typescript/module/plugins/insights/index.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/sender.d.ts +74 -0
- package/lib/typescript/module/plugins/insights/sender.d.ts.map +1 -0
- package/lib/typescript/module/plugins/insights/state.d.ts +46 -0
- package/lib/typescript/module/plugins/insights/state.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +5 -1
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +34 -12
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/updateError.d.ts +26 -0
- package/lib/typescript/module/updateError.d.ts.map +1 -0
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +6 -5
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +14 -2
- package/plugins/insights/package.json +8 -0
- package/src/appReady.spec.ts +393 -0
- package/src/{notifyAppReadyInsights.test-utils.ts → appReady.test-utils.ts} +7 -7
- package/src/appReady.ts +149 -0
- package/src/baseURL.ts +15 -0
- package/src/checkForUpdate.releaseCatalog.spec.ts +370 -76
- package/src/checkForUpdate.ts +135 -80
- package/src/clientPlugin.ts +231 -0
- package/src/entryImportGraph.spec.ts +65 -0
- package/src/fetchJSON.ts +3 -10
- package/src/httpClient.spec.ts +28 -9
- package/src/httpClient.ts +16 -109
- package/src/index.spec.ts +129 -36
- package/src/index.ts +67 -37
- package/src/native.spec.ts +98 -72
- package/src/native.ts +93 -77
- package/src/pluginHost.spec.ts +297 -0
- package/src/pluginHost.ts +262 -0
- package/src/plugins/insights/eventId.spec.ts +60 -0
- package/src/plugins/insights/eventId.ts +171 -0
- package/src/plugins/insights/index.ts +367 -0
- package/src/plugins/insights/insights.spec.ts +732 -0
- package/src/plugins/insights/sender.spec.ts +287 -0
- package/src/plugins/insights/sender.ts +221 -0
- package/src/plugins/insights/state.ts +184 -0
- package/src/releaseCatalogCache.spec.ts +22 -0
- package/src/releaseCatalogCache.ts +19 -4
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -13
- package/src/updateError.spec.ts +144 -0
- package/src/updateError.ts +153 -0
- package/src/wrap.progress.spec.ts +232 -0
- package/src/wrap.spec.ts +100 -283
- package/src/wrap.tsx +57 -26
- package/src/wrap.types.ts +6 -5
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -225
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -188
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -378
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -124
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -224
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -181
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -377
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -123
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -25
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -25
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -242
- package/src/notifyAppReadyInsights.spec.ts +0 -362
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -138
- package/src/notifyAppReadyInsights.ts +0 -293
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
type RandomValuesSource = {
|
|
2
|
+
readonly getRandomValues?: (array: Uint8Array) => Uint8Array;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
const formatUUIDv7 = (bytes: Uint8Array, timestamp: number): string => {
|
|
6
|
+
// Division, not bit shifts: shifts truncate to 32 bits, and a millisecond
|
|
7
|
+
// timestamp already needs 41.
|
|
8
|
+
let time = timestamp;
|
|
9
|
+
for (let index = 5; index >= 0; index -= 1) {
|
|
10
|
+
bytes[index] = time % 256;
|
|
11
|
+
time = Math.floor(time / 256);
|
|
12
|
+
}
|
|
13
|
+
bytes[6] = 0x70 | (bytes[6]! & 0x0f);
|
|
14
|
+
bytes[8] = 0x80 | (bytes[8]! & 0x3f);
|
|
15
|
+
|
|
16
|
+
const hex = Array.from(bytes, (byte) =>
|
|
17
|
+
byte.toString(16).padStart(2, "0"),
|
|
18
|
+
).join("");
|
|
19
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates an RFC 9562 UUIDv7: a 48-bit millisecond timestamp, then random bits.
|
|
24
|
+
*
|
|
25
|
+
* `createUUIDv7` from `@hot-updater/plugin-core` calls `crypto.getRandomValues`
|
|
26
|
+
* unconditionally, but React Native has no Web Crypto unless the app installs a
|
|
27
|
+
* polyfill. An Insights event ID only has to tell one event from another so a
|
|
28
|
+
* retried POST is counted once, so `Math.random` is enough without one.
|
|
29
|
+
*/
|
|
30
|
+
export const createUUIDv7 = (): string => {
|
|
31
|
+
const bytes = new Uint8Array(16);
|
|
32
|
+
const { crypto } = globalThis as { crypto?: RandomValuesSource };
|
|
33
|
+
if (typeof crypto?.getRandomValues === "function") {
|
|
34
|
+
crypto.getRandomValues(bytes);
|
|
35
|
+
} else {
|
|
36
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
37
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return formatUUIDv7(bytes, Date.now());
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const SHA256_K = [
|
|
44
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
45
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
46
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
47
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
48
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
49
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
50
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
51
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
52
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
53
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
54
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const encodeUtf8 = (value: string): number[] => {
|
|
58
|
+
const bytes: number[] = [];
|
|
59
|
+
for (const character of value) {
|
|
60
|
+
const code = character.codePointAt(0)!;
|
|
61
|
+
if (code < 0x80) {
|
|
62
|
+
bytes.push(code);
|
|
63
|
+
} else if (code < 0x800) {
|
|
64
|
+
bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
|
65
|
+
} else if (code < 0x10000) {
|
|
66
|
+
bytes.push(
|
|
67
|
+
0xe0 | (code >> 12),
|
|
68
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
69
|
+
0x80 | (code & 0x3f),
|
|
70
|
+
);
|
|
71
|
+
} else {
|
|
72
|
+
bytes.push(
|
|
73
|
+
0xf0 | (code >> 18),
|
|
74
|
+
0x80 | ((code >> 12) & 0x3f),
|
|
75
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
76
|
+
0x80 | (code & 0x3f),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return bytes;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** SHA-256 of a string's UTF-8 bytes, since React Native has no Web Crypto. */
|
|
84
|
+
export const sha256 = (value: string): Uint8Array => {
|
|
85
|
+
const bytes = encodeUtf8(value);
|
|
86
|
+
const bitLength = bytes.length * 8;
|
|
87
|
+
bytes.push(0x80);
|
|
88
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
89
|
+
for (let shift = 56; shift >= 0; shift -= 8) {
|
|
90
|
+
// The length's high word, as division, since shifts truncate to 32 bits.
|
|
91
|
+
bytes.push(
|
|
92
|
+
shift >= 32
|
|
93
|
+
? Math.floor(bitLength / 2 ** shift) & 0xff
|
|
94
|
+
: (bitLength >>> shift) & 0xff,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const hash = [
|
|
99
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
100
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
101
|
+
];
|
|
102
|
+
const words: number[] = Array.from({ length: 64 }, () => 0);
|
|
103
|
+
const rotate = (word: number, bits: number) =>
|
|
104
|
+
(word >>> bits) | (word << (32 - bits));
|
|
105
|
+
|
|
106
|
+
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
107
|
+
for (let index = 0; index < 16; index += 1) {
|
|
108
|
+
const at = offset + index * 4;
|
|
109
|
+
words[index] =
|
|
110
|
+
(bytes[at]! << 24) |
|
|
111
|
+
(bytes[at + 1]! << 16) |
|
|
112
|
+
(bytes[at + 2]! << 8) |
|
|
113
|
+
bytes[at + 3]!;
|
|
114
|
+
}
|
|
115
|
+
for (let index = 16; index < 64; index += 1) {
|
|
116
|
+
const low = words[index - 15]!;
|
|
117
|
+
const high = words[index - 2]!;
|
|
118
|
+
const s0 = rotate(low, 7) ^ rotate(low, 18) ^ (low >>> 3);
|
|
119
|
+
const s1 = rotate(high, 17) ^ rotate(high, 19) ^ (high >>> 10);
|
|
120
|
+
words[index] = (words[index - 16]! + s0 + words[index - 7]! + s1) | 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let [a, b, c, d, e, f, g, h] = hash as [
|
|
124
|
+
number,
|
|
125
|
+
number,
|
|
126
|
+
number,
|
|
127
|
+
number,
|
|
128
|
+
number,
|
|
129
|
+
number,
|
|
130
|
+
number,
|
|
131
|
+
number,
|
|
132
|
+
];
|
|
133
|
+
for (let index = 0; index < 64; index += 1) {
|
|
134
|
+
const s1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
|
|
135
|
+
const choose = (e & f) ^ (~e & g);
|
|
136
|
+
const temp1 = (h + s1 + choose + SHA256_K[index]! + words[index]!) | 0;
|
|
137
|
+
const s0 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22);
|
|
138
|
+
const majority = (a & b) ^ (a & c) ^ (b & c);
|
|
139
|
+
const temp2 = (s0 + majority) | 0;
|
|
140
|
+
h = g;
|
|
141
|
+
g = f;
|
|
142
|
+
f = e;
|
|
143
|
+
e = (d + temp1) | 0;
|
|
144
|
+
d = c;
|
|
145
|
+
c = b;
|
|
146
|
+
b = a;
|
|
147
|
+
a = (temp1 + temp2) | 0;
|
|
148
|
+
}
|
|
149
|
+
const results = [a, b, c, d, e, f, g, h];
|
|
150
|
+
for (let index = 0; index < 8; index += 1) {
|
|
151
|
+
hash[index] = (hash[index]! + results[index]!) | 0;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const digest = new Uint8Array(32);
|
|
156
|
+
hash.forEach((word, index) => {
|
|
157
|
+
digest[index * 4] = (word >>> 24) & 0xff;
|
|
158
|
+
digest[index * 4 + 1] = (word >>> 16) & 0xff;
|
|
159
|
+
digest[index * 4 + 2] = (word >>> 8) & 0xff;
|
|
160
|
+
digest[index * 4 + 3] = word & 0xff;
|
|
161
|
+
});
|
|
162
|
+
return digest;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A UUIDv7 derived from a key: its time field is `timestamp` and its random
|
|
167
|
+
* bits are the key's SHA-256, so the same key and time always give the same
|
|
168
|
+
* id, and a server that stores reports by id counts it once.
|
|
169
|
+
*/
|
|
170
|
+
export const createKeyedUUIDv7 = (key: string, timestamp: number): string =>
|
|
171
|
+
formatUUIDv7(sha256(key).slice(0, 16), timestamp);
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineClientPlugin,
|
|
3
|
+
type AppReadyResult,
|
|
4
|
+
type BundleDownloadedInfo,
|
|
5
|
+
type HotUpdaterClientContext,
|
|
6
|
+
type HotUpdaterClientPlugin,
|
|
7
|
+
type UpdateCheckResult,
|
|
8
|
+
type UpdateError,
|
|
9
|
+
} from "../../clientPlugin";
|
|
10
|
+
import { createKeyedUUIDv7, createUUIDv7 } from "./eventId";
|
|
11
|
+
import {
|
|
12
|
+
createInsightsEventSender,
|
|
13
|
+
type InsightsDelivery,
|
|
14
|
+
type InsightsEventBody,
|
|
15
|
+
type InsightsEventSender,
|
|
16
|
+
} from "./sender";
|
|
17
|
+
import {
|
|
18
|
+
createInsightsState,
|
|
19
|
+
type InsightsState,
|
|
20
|
+
type RunningReport,
|
|
21
|
+
utcDay,
|
|
22
|
+
utcDayStart,
|
|
23
|
+
} from "./state";
|
|
24
|
+
|
|
25
|
+
export interface InsightsOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Also report from debug builds (`__DEV__`). Defaults to `false`, so
|
|
28
|
+
* development sessions stay out of production Insights.
|
|
29
|
+
*/
|
|
30
|
+
readonly debug?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The app's own id for the signed-in user, attached to later reports. */
|
|
34
|
+
export interface InsightsUser {
|
|
35
|
+
readonly userId: string | number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface InsightsPlugin extends HotUpdaterClientPlugin {
|
|
39
|
+
readonly id: "insights";
|
|
40
|
+
/**
|
|
41
|
+
* Attaches the signed-in user's id to later reports, or clears it with
|
|
42
|
+
* `null`. It persists on the device, sends nothing by itself, and can be
|
|
43
|
+
* called before `HotUpdater.init`.
|
|
44
|
+
*/
|
|
45
|
+
setUser(user: InsightsUser | null): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type Movement = Pick<
|
|
49
|
+
InsightsEventBody,
|
|
50
|
+
| "type"
|
|
51
|
+
| "channel"
|
|
52
|
+
| "fromBundleId"
|
|
53
|
+
| "fromReleaseId"
|
|
54
|
+
| "toBundleId"
|
|
55
|
+
| "toReleaseId"
|
|
56
|
+
| "updateStrategy"
|
|
57
|
+
| "metadata"
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
/** A report the plugin built, with what the gate needs to decide on it. */
|
|
61
|
+
type PendingEvent = {
|
|
62
|
+
readonly body: InsightsEventBody;
|
|
63
|
+
readonly day: string;
|
|
64
|
+
/** For UNCHANGED, UPDATE_APPLIED and RECOVERED: what the report says runs. */
|
|
65
|
+
readonly report: RunningReport | null;
|
|
66
|
+
/** For UPDATE_FAILED: the day's key the failure is reported under. */
|
|
67
|
+
readonly failureKey: string | null;
|
|
68
|
+
/** For UPDATE_FAILED: a later success for its target made it moot. */
|
|
69
|
+
superseded?: boolean;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const normalizeUserId = (user: InsightsUser | null): string | null => {
|
|
73
|
+
if (user === null) return null;
|
|
74
|
+
const userId = String(user.userId).trim();
|
|
75
|
+
return userId.length === 0 ? null : userId;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Reports update adoption to a server that runs the `insights()` server
|
|
80
|
+
* plugin: launches (at most once a UTC day while nothing changes), downloads,
|
|
81
|
+
* applies, recoveries, and update failures. After the server answers that
|
|
82
|
+
* Insights is off, the plugin sends nothing for 24 hours, and for the rest
|
|
83
|
+
* of that runtime.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { HotUpdater } from "@hot-updater/react-native";
|
|
88
|
+
* import { insights } from "@hot-updater/react-native/plugins/insights";
|
|
89
|
+
*
|
|
90
|
+
* const analytics = insights();
|
|
91
|
+
* HotUpdater.init({ baseURL, plugins: [analytics] });
|
|
92
|
+
* analytics.setUser({ userId: "user-123" });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export const insights = (options: InsightsOptions = {}): InsightsPlugin => {
|
|
96
|
+
let context: HotUpdaterClientContext | null = null;
|
|
97
|
+
let state: InsightsState | null = null;
|
|
98
|
+
/** A user set before setup, applied once the plugin has storage. */
|
|
99
|
+
let pendingUser: { readonly userId: string | null } | null = null;
|
|
100
|
+
/** A download in this runtime: a later no-change report would hide it. */
|
|
101
|
+
let didDownload = false;
|
|
102
|
+
let lastDownloadedSelection: string | null = null;
|
|
103
|
+
const pending = new Map<InsightsEventBody, PendingEvent>();
|
|
104
|
+
let send: InsightsEventSender | null = null;
|
|
105
|
+
|
|
106
|
+
const createEvent = (
|
|
107
|
+
movement: Movement,
|
|
108
|
+
eventId?: (now: number, installId: string) => string,
|
|
109
|
+
): PendingEvent | null => {
|
|
110
|
+
if (context === null || state === null) return null;
|
|
111
|
+
const appVersion = context.appVersion;
|
|
112
|
+
if (appVersion === null) {
|
|
113
|
+
console.warn(
|
|
114
|
+
`[HotUpdater] Insights needs the native app version; the ${movement.type} event was not sent.`,
|
|
115
|
+
);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const now = context.now();
|
|
119
|
+
const installId = context.installId;
|
|
120
|
+
const userId = state.readUserId();
|
|
121
|
+
const body: InsightsEventBody = {
|
|
122
|
+
...movement,
|
|
123
|
+
eventId: eventId?.(now, installId) ?? createUUIDv7(),
|
|
124
|
+
installId,
|
|
125
|
+
...(userId === null ? {} : { userId }),
|
|
126
|
+
platform: context.platform,
|
|
127
|
+
appVersion,
|
|
128
|
+
cohort: context.getCohort(),
|
|
129
|
+
fingerprintHash: context.getFingerprintHash(),
|
|
130
|
+
sdkVersion: context.sdkVersion,
|
|
131
|
+
};
|
|
132
|
+
const day = utcDay(now);
|
|
133
|
+
return {
|
|
134
|
+
body,
|
|
135
|
+
day,
|
|
136
|
+
report:
|
|
137
|
+
movement.type === "UNCHANGED" ||
|
|
138
|
+
movement.type === "UPDATE_APPLIED" ||
|
|
139
|
+
movement.type === "RECOVERED"
|
|
140
|
+
? {
|
|
141
|
+
day,
|
|
142
|
+
channel: movement.channel,
|
|
143
|
+
appVersion,
|
|
144
|
+
bundleId: movement.toBundleId,
|
|
145
|
+
releaseId: movement.toReleaseId,
|
|
146
|
+
userId,
|
|
147
|
+
}
|
|
148
|
+
: null,
|
|
149
|
+
failureKey: null,
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const enqueue = (event: PendingEvent | null) => {
|
|
154
|
+
if (event === null || send === null) return;
|
|
155
|
+
const { type, toBundleId } = event.body;
|
|
156
|
+
if (type === "UPDATE_DOWNLOADED" || type === "UPDATE_APPLIED") {
|
|
157
|
+
// A failure for a target that then arrived no longer describes it.
|
|
158
|
+
for (const queued of pending.values()) {
|
|
159
|
+
if (
|
|
160
|
+
queued.body.type === "UPDATE_FAILED" &&
|
|
161
|
+
queued.body.toBundleId === toBundleId
|
|
162
|
+
) {
|
|
163
|
+
queued.superseded = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
pending.set(event.body, event);
|
|
168
|
+
void send(event.body).catch((error: unknown) => {
|
|
169
|
+
console.warn(
|
|
170
|
+
`[HotUpdater] Insights ${event.body.type} event was not delivered:`,
|
|
171
|
+
error,
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const reportLaunch = (
|
|
177
|
+
channel: string,
|
|
178
|
+
bundleId: string,
|
|
179
|
+
releaseId: string | null,
|
|
180
|
+
fromReleaseId: string | null,
|
|
181
|
+
) => {
|
|
182
|
+
// After a download the server shows the update as waiting; a later
|
|
183
|
+
// no-change report from this runtime would hide it.
|
|
184
|
+
if (didDownload) return;
|
|
185
|
+
enqueue(
|
|
186
|
+
createEvent({
|
|
187
|
+
type: "UNCHANGED",
|
|
188
|
+
channel,
|
|
189
|
+
fromBundleId: null,
|
|
190
|
+
fromReleaseId,
|
|
191
|
+
toBundleId: bundleId,
|
|
192
|
+
toReleaseId: releaseId,
|
|
193
|
+
updateStrategy: null,
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const onAppReady = (result: AppReadyResult) => {
|
|
199
|
+
if (result.status === "UNCHANGED") {
|
|
200
|
+
reportLaunch(result.channel, result.bundleId, result.releaseId, null);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
enqueue(
|
|
204
|
+
createEvent({
|
|
205
|
+
type: result.status,
|
|
206
|
+
channel: result.channel,
|
|
207
|
+
fromBundleId: result.fromBundleId,
|
|
208
|
+
fromReleaseId: result.fromReleaseId,
|
|
209
|
+
toBundleId: result.toBundleId,
|
|
210
|
+
toReleaseId: result.toReleaseId,
|
|
211
|
+
updateStrategy: result.updateStrategy,
|
|
212
|
+
// Tells a crash from the system or the user closing the app.
|
|
213
|
+
...(result.status === "RECOVERED" && result.previousProcessExit !== null
|
|
214
|
+
? { metadata: { previousProcessExit: result.previousProcessExit } }
|
|
215
|
+
: {}),
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const onUpdateCheck = (result: UpdateCheckResult) => {
|
|
221
|
+
if (result.status !== "UNCHANGED") return;
|
|
222
|
+
reportLaunch(
|
|
223
|
+
result.channel,
|
|
224
|
+
result.bundleId,
|
|
225
|
+
result.releaseId,
|
|
226
|
+
result.previousReleaseId === result.releaseId
|
|
227
|
+
? null
|
|
228
|
+
: result.previousReleaseId,
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const onBundleDownloaded = (info: BundleDownloadedInfo) => {
|
|
233
|
+
const selection = JSON.stringify([
|
|
234
|
+
info.channel,
|
|
235
|
+
info.toReleaseId,
|
|
236
|
+
info.toBundleId,
|
|
237
|
+
]);
|
|
238
|
+
if (lastDownloadedSelection === selection) return;
|
|
239
|
+
lastDownloadedSelection = selection;
|
|
240
|
+
didDownload = true;
|
|
241
|
+
enqueue(
|
|
242
|
+
createEvent({
|
|
243
|
+
type: "UPDATE_DOWNLOADED",
|
|
244
|
+
channel: info.channel,
|
|
245
|
+
fromBundleId: info.fromBundleId,
|
|
246
|
+
fromReleaseId: info.fromReleaseId,
|
|
247
|
+
toBundleId: info.toBundleId,
|
|
248
|
+
toReleaseId: info.toReleaseId,
|
|
249
|
+
updateStrategy: info.updateStrategy,
|
|
250
|
+
metadata: {
|
|
251
|
+
delivery: info.delivery,
|
|
252
|
+
...(info.patchFallback ? { patchFallback: true } : {}),
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const onUpdateError = (error: UpdateError) => {
|
|
259
|
+
// A check that could not reach the server means the device is offline,
|
|
260
|
+
// and its report would not arrive either.
|
|
261
|
+
if (error.stage === "check" && error.reason === "network") return;
|
|
262
|
+
const targetBundleId = error.targetBundleId ?? error.bundleId;
|
|
263
|
+
const failureKey = [error.stage, error.reason, targetBundleId].join(" ");
|
|
264
|
+
const event = createEvent(
|
|
265
|
+
{
|
|
266
|
+
type: "UPDATE_FAILED",
|
|
267
|
+
channel: error.channel,
|
|
268
|
+
fromBundleId: error.bundleId,
|
|
269
|
+
fromReleaseId: error.releaseId,
|
|
270
|
+
toBundleId: targetBundleId,
|
|
271
|
+
toReleaseId: error.targetReleaseId ?? null,
|
|
272
|
+
updateStrategy: error.updateStrategy,
|
|
273
|
+
metadata: {
|
|
274
|
+
failure: {
|
|
275
|
+
stage: error.stage,
|
|
276
|
+
reason: error.reason,
|
|
277
|
+
...(error.resource === undefined
|
|
278
|
+
? {}
|
|
279
|
+
: { resource: error.resource }),
|
|
280
|
+
...(error.httpStatus === undefined
|
|
281
|
+
? {}
|
|
282
|
+
: { httpStatus: error.httpStatus }),
|
|
283
|
+
...(error.transport === undefined
|
|
284
|
+
? {}
|
|
285
|
+
: { transport: error.transport }),
|
|
286
|
+
...(error.originCode === undefined
|
|
287
|
+
? {}
|
|
288
|
+
: { originCode: error.originCode }),
|
|
289
|
+
...(error.previousProcessExit === undefined
|
|
290
|
+
? {}
|
|
291
|
+
: { previousProcessExit: error.previousProcessExit }),
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
// One id per installation, UTC day, and failure, so the server
|
|
296
|
+
// stores a repeated failure once.
|
|
297
|
+
(now, installId) =>
|
|
298
|
+
createKeyedUUIDv7(
|
|
299
|
+
[installId, utcDay(now), failureKey].join("\n"),
|
|
300
|
+
utcDayStart(now),
|
|
301
|
+
),
|
|
302
|
+
);
|
|
303
|
+
enqueue(event === null ? null : { ...event, failureKey });
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const admit = (body: InsightsEventBody): boolean => {
|
|
307
|
+
const event = pending.get(body);
|
|
308
|
+
if (event === undefined || state === null || context === null) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
if (event.superseded === true || state.isPaused(context.now())) {
|
|
312
|
+
pending.delete(body);
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
const admitted =
|
|
316
|
+
body.type === "UNCHANGED"
|
|
317
|
+
? event.report === null || !state.repeatsReport(event.report)
|
|
318
|
+
: body.type === "UPDATE_FAILED"
|
|
319
|
+
? event.failureKey === null ||
|
|
320
|
+
!state.hasFailure(event.day, event.failureKey)
|
|
321
|
+
: true;
|
|
322
|
+
if (!admitted) pending.delete(body);
|
|
323
|
+
return admitted;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const settle = (body: InsightsEventBody, delivery: InsightsDelivery) => {
|
|
327
|
+
const event = pending.get(body);
|
|
328
|
+
pending.delete(body);
|
|
329
|
+
if (event === undefined || state === null || context === null) return;
|
|
330
|
+
if (delivery === "failed") return;
|
|
331
|
+
if (delivery === "disabled") {
|
|
332
|
+
state.pause(context.now());
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (event.failureKey !== null) {
|
|
336
|
+
state.recordFailure(event.day, event.failureKey);
|
|
337
|
+
}
|
|
338
|
+
if (delivery === "refused") return;
|
|
339
|
+
state.resume();
|
|
340
|
+
if (event.report !== null) state.recordReport(event.report);
|
|
341
|
+
else state.forgetReport();
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
return defineClientPlugin({
|
|
345
|
+
id: "insights",
|
|
346
|
+
setup(pluginContext) {
|
|
347
|
+
context = pluginContext;
|
|
348
|
+
state = createInsightsState(pluginContext.storage);
|
|
349
|
+
if (pendingUser !== null) {
|
|
350
|
+
state.writeUserId(pendingUser.userId);
|
|
351
|
+
pendingUser = null;
|
|
352
|
+
}
|
|
353
|
+
if (pluginContext.isDebugBuild && options.debug !== true) return;
|
|
354
|
+
|
|
355
|
+
send = createInsightsEventSender(pluginContext.fetch, { admit, settle });
|
|
356
|
+
return { onAppReady, onUpdateCheck, onBundleDownloaded, onUpdateError };
|
|
357
|
+
},
|
|
358
|
+
setUser(user) {
|
|
359
|
+
const userId = normalizeUserId(user);
|
|
360
|
+
if (state === null) {
|
|
361
|
+
pendingUser = { userId };
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
state.writeUserId(userId);
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
};
|