@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,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** A `POST /events` body. */
|
|
4
|
+
|
|
5
|
+
/** Details beside an event's fields, by event type. */
|
|
6
|
+
|
|
7
|
+
/** How the server answered one event. */
|
|
8
|
+
|
|
9
|
+
/** Attempts per Insights event, the first one included. */
|
|
10
|
+
const INSIGHTS_MAX_ATTEMPTS = 3;
|
|
11
|
+
/** Wait before the second attempt; it doubles for each later attempt. */
|
|
12
|
+
const INSIGHTS_RETRY_BASE_DELAY_MS = 1000;
|
|
13
|
+
/** Longest wait between attempts, a server's `Retry-After` included. */
|
|
14
|
+
const INSIGHTS_RETRY_MAX_DELAY_MS = 30000;
|
|
15
|
+
/**
|
|
16
|
+
* Reads `Retry-After` as delay-seconds, the form a server under load sends;
|
|
17
|
+
* an HTTP-date falls back to the backoff.
|
|
18
|
+
*/
|
|
19
|
+
const parseRetryAfterMs = value => {
|
|
20
|
+
const seconds = value?.trim();
|
|
21
|
+
return seconds && /^\d+$/.test(seconds) ? Number(seconds) * 1000 : null;
|
|
22
|
+
};
|
|
23
|
+
const getRetryDelayMs = (failedAttempt, retryAfterMs) => Math.min(retryAfterMs ??
|
|
24
|
+
// Jitter spreads the retries of installations that failed together.
|
|
25
|
+
INSIGHTS_RETRY_BASE_DELAY_MS * 2 ** (failedAttempt - 1) * (0.5 + Math.random()), INSIGHTS_RETRY_MAX_DELAY_MS);
|
|
26
|
+
const wait = ms => new Promise(resolve => {
|
|
27
|
+
setTimeout(resolve, ms);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** Makes one POST, with the context's timeout, and settles with its outcome. */
|
|
31
|
+
const postInsightsEvent = async (fetch, event) => {
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch("events", {
|
|
34
|
+
body: JSON.stringify(event),
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "application/json"
|
|
37
|
+
},
|
|
38
|
+
method: "POST"
|
|
39
|
+
});
|
|
40
|
+
if (response.status === 204) return "delivered";
|
|
41
|
+
if (response.status === 404) return "disabled";
|
|
42
|
+
if (response.status === 400 && event.type === "UPDATE_FAILED") {
|
|
43
|
+
return "refused";
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
error: new Error(`Expected HTTP 204 from /events, received ${response.status}`),
|
|
47
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
48
|
+
retryAfterMs: parseRetryAfterMs(response.headers.get("Retry-After"))
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// fetch rejects only when no response arrived (a timeout or a network
|
|
52
|
+
// error), so a later attempt may still get through.
|
|
53
|
+
return {
|
|
54
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
55
|
+
retryable: true,
|
|
56
|
+
retryAfterMs: null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Sends Insights events one at a time and retries each in the background.
|
|
62
|
+
*
|
|
63
|
+
* The server keeps an installation's latest report by arrival, so a later
|
|
64
|
+
* event waits behind an earlier one's retries; otherwise a retried
|
|
65
|
+
* UPDATE_APPLIED could land after the UPDATE_DOWNLOADED that followed it.
|
|
66
|
+
* Callers wait only for first attempts, so a report never waits out a
|
|
67
|
+
* backoff: once an event backs off, every waiting caller is released, and a
|
|
68
|
+
* failure after that only warns. The gate sees each event before each
|
|
69
|
+
* attempt, after every earlier event settled, so it decides with what the
|
|
70
|
+
* server already answered.
|
|
71
|
+
*/
|
|
72
|
+
export const createInsightsEventSender = (fetch, gate) => {
|
|
73
|
+
let queue = Promise.resolve();
|
|
74
|
+
let retrying = false;
|
|
75
|
+
const waiting = new Set();
|
|
76
|
+
return event => new Promise((resolve, reject) => {
|
|
77
|
+
let released = false;
|
|
78
|
+
const release = () => {
|
|
79
|
+
released = true;
|
|
80
|
+
waiting.delete(release);
|
|
81
|
+
resolve();
|
|
82
|
+
};
|
|
83
|
+
if (retrying) release();else waiting.add(release);
|
|
84
|
+
queue = queue.then(async () => {
|
|
85
|
+
try {
|
|
86
|
+
for (let attempt = 1;; attempt += 1) {
|
|
87
|
+
if (!gate.admit(event)) {
|
|
88
|
+
release();
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
const outcome = await postInsightsEvent(fetch, event);
|
|
92
|
+
if (typeof outcome === "string") {
|
|
93
|
+
gate.settle(event, outcome);
|
|
94
|
+
release();
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (!outcome.retryable || attempt === INSIGHTS_MAX_ATTEMPTS) {
|
|
98
|
+
gate.settle(event, "failed");
|
|
99
|
+
waiting.delete(release);
|
|
100
|
+
if (released) {
|
|
101
|
+
console.warn(`[HotUpdater] Insights ${event.type} event was not delivered:`, outcome.error);
|
|
102
|
+
} else {
|
|
103
|
+
reject(outcome.error);
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
retrying = true;
|
|
108
|
+
for (const releaseWaiting of waiting) releaseWaiting();
|
|
109
|
+
await wait(getRetryDelayMs(attempt, outcome.retryAfterMs));
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// A failed request settles in postInsightsEvent, so only a bug gets
|
|
113
|
+
// here. A rejected queue would leave every later event, and a
|
|
114
|
+
// caller waiting on one, pending forever.
|
|
115
|
+
waiting.delete(release);
|
|
116
|
+
if (!released) reject(error);
|
|
117
|
+
} finally {
|
|
118
|
+
retrying = false;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=sender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["INSIGHTS_MAX_ATTEMPTS","INSIGHTS_RETRY_BASE_DELAY_MS","INSIGHTS_RETRY_MAX_DELAY_MS","parseRetryAfterMs","value","seconds","trim","test","Number","getRetryDelayMs","failedAttempt","retryAfterMs","Math","min","random","wait","ms","Promise","resolve","setTimeout","postInsightsEvent","fetch","event","response","body","JSON","stringify","headers","method","status","type","error","Error","retryable","get","String","createInsightsEventSender","gate","queue","retrying","waiting","Set","reject","released","release","delete","add","then","attempt","admit","outcome","settle","console","warn","releaseWaiting"],"sourceRoot":"../../../../src","sources":["plugins/insights/sender.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { isUUIDv7 } from "@hot-updater/core";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { createUUIDv7 } from "./eventId.js";
|
|
6
|
+
import { createInsightsEventSender } from "./sender.js";
|
|
7
|
+
const unchangedEvent = () => ({
|
|
8
|
+
appVersion: "1.0.0",
|
|
9
|
+
channel: "production",
|
|
10
|
+
cohort: "123",
|
|
11
|
+
eventId: createUUIDv7(),
|
|
12
|
+
fingerprintHash: null,
|
|
13
|
+
fromBundleId: null,
|
|
14
|
+
fromReleaseId: null,
|
|
15
|
+
installId: "install-id",
|
|
16
|
+
platform: "ios",
|
|
17
|
+
sdkVersion: "test-sdk-version",
|
|
18
|
+
toBundleId: "bundle-id",
|
|
19
|
+
toReleaseId: null,
|
|
20
|
+
type: "UNCHANGED",
|
|
21
|
+
updateStrategy: null
|
|
22
|
+
});
|
|
23
|
+
const downloadedEvent = () => ({
|
|
24
|
+
...unchangedEvent(),
|
|
25
|
+
fromBundleId: "bundle-id",
|
|
26
|
+
toBundleId: "next-bundle-id",
|
|
27
|
+
type: "UPDATE_DOWNLOADED",
|
|
28
|
+
updateStrategy: "appVersion"
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/** Answers each POST /events with the next response, then 204. */
|
|
32
|
+
const stubEventsEndpoint = (...responses) => {
|
|
33
|
+
const fetchMock = vi.fn(async (_path, _init) => {
|
|
34
|
+
const next = responses.shift() ?? 204;
|
|
35
|
+
if (next instanceof Error) throw next;
|
|
36
|
+
return typeof next === "number" ? new Response(null, {
|
|
37
|
+
status: next
|
|
38
|
+
}) : next;
|
|
39
|
+
});
|
|
40
|
+
const sentEvents = () => fetchMock.mock.calls.map(([, init]) => JSON.parse(String(init?.body)));
|
|
41
|
+
return {
|
|
42
|
+
fetchMock,
|
|
43
|
+
sentEvents
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const openGate = () => ({
|
|
47
|
+
admit: () => true,
|
|
48
|
+
settle: vi.fn()
|
|
49
|
+
});
|
|
50
|
+
describe("Insights event delivery", () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
vi.useFakeTimers();
|
|
53
|
+
// No jitter: the first retry waits exactly 1 s and the second 2 s.
|
|
54
|
+
vi.spyOn(Math, "random").mockReturnValue(0.5);
|
|
55
|
+
});
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
vi.useRealTimers();
|
|
58
|
+
vi.restoreAllMocks();
|
|
59
|
+
});
|
|
60
|
+
it("posts to events and retries a 503 in the background under the same UUIDv7 eventId", async () => {
|
|
61
|
+
const {
|
|
62
|
+
fetchMock,
|
|
63
|
+
sentEvents
|
|
64
|
+
} = stubEventsEndpoint(503, 204);
|
|
65
|
+
const gate = openGate();
|
|
66
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
67
|
+
|
|
68
|
+
// Settles after the failed first attempt, without waiting for the retry.
|
|
69
|
+
await send(unchangedEvent());
|
|
70
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
71
|
+
expect(fetchMock.mock.calls[0]?.[0]).toBe("events");
|
|
72
|
+
expect(fetchMock.mock.calls[0]?.[1]?.method).toBe("POST");
|
|
73
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
74
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
75
|
+
const [first, second] = sentEvents();
|
|
76
|
+
expect(isUUIDv7(first?.eventId)).toBe(true);
|
|
77
|
+
expect(second?.eventId).toBe(first?.eventId);
|
|
78
|
+
expect(gate.settle).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
79
|
+
type: "UNCHANGED"
|
|
80
|
+
}), "delivered");
|
|
81
|
+
});
|
|
82
|
+
it("waits for a 429's Retry-After before the next attempt", async () => {
|
|
83
|
+
const {
|
|
84
|
+
fetchMock
|
|
85
|
+
} = stubEventsEndpoint(new Response(null, {
|
|
86
|
+
headers: {
|
|
87
|
+
"Retry-After": "7"
|
|
88
|
+
},
|
|
89
|
+
status: 429
|
|
90
|
+
}));
|
|
91
|
+
const send = createInsightsEventSender(fetchMock, openGate());
|
|
92
|
+
await send(unchangedEvent());
|
|
93
|
+
await vi.advanceTimersByTimeAsync(6_999);
|
|
94
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
95
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
96
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
97
|
+
});
|
|
98
|
+
it("caps a server's Retry-After at 30 seconds", async () => {
|
|
99
|
+
const {
|
|
100
|
+
fetchMock
|
|
101
|
+
} = stubEventsEndpoint(new Response(null, {
|
|
102
|
+
headers: {
|
|
103
|
+
"Retry-After": "3600"
|
|
104
|
+
},
|
|
105
|
+
status: 503
|
|
106
|
+
}));
|
|
107
|
+
const send = createInsightsEventSender(fetchMock, openGate());
|
|
108
|
+
await send(unchangedEvent());
|
|
109
|
+
await vi.advanceTimersByTimeAsync(29_999);
|
|
110
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
111
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
112
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
113
|
+
});
|
|
114
|
+
it("does not retry a 400", async () => {
|
|
115
|
+
const {
|
|
116
|
+
fetchMock
|
|
117
|
+
} = stubEventsEndpoint(400);
|
|
118
|
+
const gate = openGate();
|
|
119
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
120
|
+
await expect(send(unchangedEvent())).rejects.toThrow("Expected HTTP 204 from /events, received 400");
|
|
121
|
+
await vi.runAllTimersAsync();
|
|
122
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
123
|
+
expect(gate.settle).toHaveBeenCalledWith(expect.objectContaining({
|
|
124
|
+
type: "UNCHANGED"
|
|
125
|
+
}), "failed");
|
|
126
|
+
});
|
|
127
|
+
it("settles an UPDATE_FAILED event an older server refuses with 400, without retrying", async () => {
|
|
128
|
+
const {
|
|
129
|
+
fetchMock
|
|
130
|
+
} = stubEventsEndpoint(400);
|
|
131
|
+
const gate = openGate();
|
|
132
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
133
|
+
await send({
|
|
134
|
+
...unchangedEvent(),
|
|
135
|
+
type: "UPDATE_FAILED"
|
|
136
|
+
});
|
|
137
|
+
await vi.runAllTimersAsync();
|
|
138
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
139
|
+
expect(gate.settle).toHaveBeenCalledWith(expect.objectContaining({
|
|
140
|
+
type: "UPDATE_FAILED"
|
|
141
|
+
}), "refused");
|
|
142
|
+
});
|
|
143
|
+
it("settles a 404, from a server without Insights, as Insights off without retrying", async () => {
|
|
144
|
+
const {
|
|
145
|
+
fetchMock
|
|
146
|
+
} = stubEventsEndpoint(404);
|
|
147
|
+
const gate = openGate();
|
|
148
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
149
|
+
await send(unchangedEvent());
|
|
150
|
+
await vi.runAllTimersAsync();
|
|
151
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
152
|
+
expect(gate.settle).toHaveBeenCalledWith(expect.objectContaining({
|
|
153
|
+
type: "UNCHANGED"
|
|
154
|
+
}), "disabled");
|
|
155
|
+
});
|
|
156
|
+
it("skips an event the gate does not admit", async () => {
|
|
157
|
+
const {
|
|
158
|
+
fetchMock
|
|
159
|
+
} = stubEventsEndpoint();
|
|
160
|
+
const gate = {
|
|
161
|
+
admit: vi.fn(() => false),
|
|
162
|
+
settle: vi.fn()
|
|
163
|
+
};
|
|
164
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
165
|
+
await send(unchangedEvent());
|
|
166
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
167
|
+
expect(gate.settle).not.toHaveBeenCalled();
|
|
168
|
+
});
|
|
169
|
+
it("asks the gate before every attempt, after earlier events settled", async () => {
|
|
170
|
+
const {
|
|
171
|
+
fetchMock
|
|
172
|
+
} = stubEventsEndpoint(503, 204);
|
|
173
|
+
const order = [];
|
|
174
|
+
const gate = {
|
|
175
|
+
admit: event => {
|
|
176
|
+
order.push(`admit ${event.type}`);
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
settle: (event, delivery) => {
|
|
180
|
+
order.push(`${delivery} ${event.type}`);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
184
|
+
await send(unchangedEvent());
|
|
185
|
+
await send(downloadedEvent());
|
|
186
|
+
await vi.runAllTimersAsync();
|
|
187
|
+
expect(order).toEqual(["admit UNCHANGED", "admit UNCHANGED", "delivered UNCHANGED", "admit UPDATE_DOWNLOADED", "delivered UPDATE_DOWNLOADED"]);
|
|
188
|
+
});
|
|
189
|
+
it("drops an event the gate stops admitting during its backoff", async () => {
|
|
190
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
191
|
+
const {
|
|
192
|
+
fetchMock,
|
|
193
|
+
sentEvents
|
|
194
|
+
} = stubEventsEndpoint(503);
|
|
195
|
+
let wanted = true;
|
|
196
|
+
const gate = {
|
|
197
|
+
admit: vi.fn(() => wanted),
|
|
198
|
+
settle: vi.fn()
|
|
199
|
+
};
|
|
200
|
+
const send = createInsightsEventSender(fetchMock, gate);
|
|
201
|
+
await send({
|
|
202
|
+
...unchangedEvent(),
|
|
203
|
+
type: "UPDATE_FAILED"
|
|
204
|
+
});
|
|
205
|
+
wanted = false;
|
|
206
|
+
await vi.runAllTimersAsync();
|
|
207
|
+
wanted = true;
|
|
208
|
+
await send(downloadedEvent());
|
|
209
|
+
expect(sentEvents().map(({
|
|
210
|
+
type
|
|
211
|
+
}) => type)).toEqual(["UPDATE_FAILED", "UPDATE_DOWNLOADED"]);
|
|
212
|
+
expect(gate.settle).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
213
|
+
type: "UPDATE_DOWNLOADED"
|
|
214
|
+
}), "delivered");
|
|
215
|
+
expect(warn).not.toHaveBeenCalled();
|
|
216
|
+
});
|
|
217
|
+
it("stops after three attempts and warns once", async () => {
|
|
218
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
219
|
+
const {
|
|
220
|
+
fetchMock
|
|
221
|
+
} = stubEventsEndpoint(500, new Error("network unavailable"), 503);
|
|
222
|
+
const send = createInsightsEventSender(fetchMock, openGate());
|
|
223
|
+
await send(unchangedEvent());
|
|
224
|
+
await vi.runAllTimersAsync();
|
|
225
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
226
|
+
expect(warn).toHaveBeenCalledExactlyOnceWith("[HotUpdater] Insights UNCHANGED event was not delivered:", expect.objectContaining({
|
|
227
|
+
message: "Expected HTTP 204 from /events, received 503"
|
|
228
|
+
}));
|
|
229
|
+
});
|
|
230
|
+
it("sends a later event after an earlier event's retries without holding its caller", async () => {
|
|
231
|
+
const {
|
|
232
|
+
fetchMock,
|
|
233
|
+
sentEvents
|
|
234
|
+
} = stubEventsEndpoint(503);
|
|
235
|
+
const send = createInsightsEventSender(fetchMock, openGate());
|
|
236
|
+
await send(unchangedEvent());
|
|
237
|
+
await send(downloadedEvent());
|
|
238
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
239
|
+
await vi.runAllTimersAsync();
|
|
240
|
+
const [first, retry, later] = sentEvents();
|
|
241
|
+
expect([first?.type, retry?.type, later?.type]).toEqual(["UNCHANGED", "UNCHANGED", "UPDATE_DOWNLOADED"]);
|
|
242
|
+
expect(retry?.eventId).toBe(first?.eventId);
|
|
243
|
+
expect(later?.eventId).not.toBe(first?.eventId);
|
|
244
|
+
});
|
|
245
|
+
it("keeps sending events after one fails outside its requests", async () => {
|
|
246
|
+
vi.spyOn(console, "warn").mockImplementation(() => {
|
|
247
|
+
throw new Error("console unavailable");
|
|
248
|
+
});
|
|
249
|
+
const {
|
|
250
|
+
fetchMock,
|
|
251
|
+
sentEvents
|
|
252
|
+
} = stubEventsEndpoint(500, 500, 500);
|
|
253
|
+
const send = createInsightsEventSender(fetchMock, openGate());
|
|
254
|
+
await send(unchangedEvent());
|
|
255
|
+
await vi.runAllTimersAsync();
|
|
256
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
257
|
+
|
|
258
|
+
// A queue left rejected would never send this event or settle its caller.
|
|
259
|
+
await send(downloadedEvent());
|
|
260
|
+
expect(sentEvents().map(({
|
|
261
|
+
type
|
|
262
|
+
}) => type)).toEqual(["UNCHANGED", "UNCHANGED", "UNCHANGED", "UPDATE_DOWNLOADED"]);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
//# sourceMappingURL=sender.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["isUUIDv7","afterEach","beforeEach","describe","expect","it","vi","createUUIDv7","createInsightsEventSender","unchangedEvent","appVersion","channel","cohort","eventId","fingerprintHash","fromBundleId","fromReleaseId","installId","platform","sdkVersion","toBundleId","toReleaseId","type","updateStrategy","downloadedEvent","stubEventsEndpoint","responses","fetchMock","fn","_path","_init","next","shift","Error","Response","status","sentEvents","mock","calls","map","init","JSON","parse","String","body","openGate","admit","settle","useFakeTimers","spyOn","Math","mockReturnValue","useRealTimers","restoreAllMocks","gate","send","toHaveBeenCalledOnce","toBe","method","advanceTimersByTimeAsync","toHaveBeenCalledTimes","first","second","toHaveBeenCalledExactlyOnceWith","objectContaining","headers","rejects","toThrow","runAllTimersAsync","toHaveBeenCalledWith","not","toHaveBeenCalled","order","event","push","delivery","toEqual","warn","console","mockImplementation","wanted","message","retry","later"],"sourceRoot":"../../../../src","sources":["plugins/insights/sender.spec.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
/** A day's failure keys kept, so a failing app cannot grow its storage. */
|
|
5
|
+
const MAX_FAILURE_KEYS = 32;
|
|
6
|
+
|
|
7
|
+
/** The UTC day of a time, as `YYYY-MM-DD`. */
|
|
8
|
+
export const utcDay = time => new Date(time).toISOString().slice(0, 10);
|
|
9
|
+
|
|
10
|
+
/** The start of a time's UTC day, in milliseconds. */
|
|
11
|
+
export const utcDayStart = time => Math.floor(time / DAY_MS) * DAY_MS;
|
|
12
|
+
|
|
13
|
+
/** What a report says runs: the fields the server compares to skip a same-day launch. */
|
|
14
|
+
|
|
15
|
+
const sameReport = (left, right) => left.day === right.day && left.channel === right.channel && left.appVersion === right.appVersion && left.bundleId === right.bundleId && left.releaseId === right.releaseId && left.userId === right.userId;
|
|
16
|
+
const isRecord = value => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
const isNullableString = value => value === null || typeof value === "string";
|
|
18
|
+
const parseReport = value => {
|
|
19
|
+
if (value === null) return null;
|
|
20
|
+
try {
|
|
21
|
+
const report = JSON.parse(value);
|
|
22
|
+
if (isRecord(report) && typeof report.day === "string" && typeof report.channel === "string" && typeof report.appVersion === "string" && typeof report.bundleId === "string" && isNullableString(report.releaseId) && isNullableString(report.userId)) {
|
|
23
|
+
return {
|
|
24
|
+
day: report.day,
|
|
25
|
+
channel: report.channel,
|
|
26
|
+
appVersion: report.appVersion,
|
|
27
|
+
bundleId: report.bundleId,
|
|
28
|
+
releaseId: report.releaseId,
|
|
29
|
+
userId: report.userId
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// An unreadable value counts as none.
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
const parseFailures = value => {
|
|
38
|
+
if (value === null) return null;
|
|
39
|
+
try {
|
|
40
|
+
const failures = JSON.parse(value);
|
|
41
|
+
if (isRecord(failures) && typeof failures.day === "string" && Array.isArray(failures.keys) && failures.keys.every(key => typeof key === "string")) {
|
|
42
|
+
return {
|
|
43
|
+
day: failures.day,
|
|
44
|
+
keys: failures.keys
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// An unreadable value counts as none.
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The Insights plugin's memory across launches, in its plugin storage. Every
|
|
55
|
+
* read tolerates a missing or unreadable value, and a storage failure only
|
|
56
|
+
* costs a report that would otherwise have been skipped.
|
|
57
|
+
*/
|
|
58
|
+
export const createInsightsState = storage => {
|
|
59
|
+
/** The server said Insights is off during this runtime. */
|
|
60
|
+
let pausedThisRuntime = false;
|
|
61
|
+
const read = key => {
|
|
62
|
+
try {
|
|
63
|
+
return storage.get(key);
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const write = (key, value) => {
|
|
69
|
+
try {
|
|
70
|
+
storage.set(key, value);
|
|
71
|
+
} catch {
|
|
72
|
+
// Losing the state costs a report, never an update.
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
/**
|
|
77
|
+
* Whether reporting pauses: the server answered that Insights is off
|
|
78
|
+
* during this runtime or in the 24 hours before `now`. A pause that
|
|
79
|
+
* starts after `now`, from a clock that went back, has ended.
|
|
80
|
+
*/
|
|
81
|
+
isPaused(now) {
|
|
82
|
+
if (pausedThisRuntime) return true;
|
|
83
|
+
const pausedAt = Number(read("pausedAt"));
|
|
84
|
+
return Number.isFinite(pausedAt) && pausedAt > 0 && pausedAt <= now && now < pausedAt + DAY_MS;
|
|
85
|
+
},
|
|
86
|
+
pause(now) {
|
|
87
|
+
pausedThisRuntime = true;
|
|
88
|
+
write("pausedAt", String(now));
|
|
89
|
+
},
|
|
90
|
+
/** The server stored a report, so Insights is on. */
|
|
91
|
+
resume() {
|
|
92
|
+
if (read("pausedAt") !== null) write("pausedAt", null);
|
|
93
|
+
},
|
|
94
|
+
/** Whether a launch report repeats what the installation reported that day. */
|
|
95
|
+
repeatsReport(report) {
|
|
96
|
+
const last = parseReport(read("report"));
|
|
97
|
+
return last !== null && sameReport(last, report);
|
|
98
|
+
},
|
|
99
|
+
/** Records a delivered report that says what runs. */
|
|
100
|
+
recordReport(report) {
|
|
101
|
+
write("report", JSON.stringify(report));
|
|
102
|
+
},
|
|
103
|
+
/**
|
|
104
|
+
* Forgets the last report after a delivered download or failure, so the
|
|
105
|
+
* next launch reports again, as the server records it after one.
|
|
106
|
+
*/
|
|
107
|
+
forgetReport() {
|
|
108
|
+
if (read("report") !== null) write("report", null);
|
|
109
|
+
},
|
|
110
|
+
/** Whether a failure was already reported, or refused, that day. */
|
|
111
|
+
hasFailure(day, key) {
|
|
112
|
+
const failures = parseFailures(read("failures"));
|
|
113
|
+
return failures?.day === day && failures.keys.includes(key);
|
|
114
|
+
},
|
|
115
|
+
recordFailure(day, key) {
|
|
116
|
+
const failures = parseFailures(read("failures"));
|
|
117
|
+
const keys = failures?.day === day ? failures.keys : [];
|
|
118
|
+
if (keys.includes(key)) return;
|
|
119
|
+
write("failures", JSON.stringify({
|
|
120
|
+
day,
|
|
121
|
+
keys: [...keys, key].slice(-MAX_FAILURE_KEYS)
|
|
122
|
+
}));
|
|
123
|
+
},
|
|
124
|
+
readUserId() {
|
|
125
|
+
try {
|
|
126
|
+
const user = JSON.parse(read("user") ?? "null");
|
|
127
|
+
return isRecord(user) && typeof user.userId === "string" ? user.userId : null;
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
writeUserId(userId) {
|
|
133
|
+
write("user", userId === null ? null : JSON.stringify({
|
|
134
|
+
userId
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["DAY_MS","MAX_FAILURE_KEYS","utcDay","time","Date","toISOString","slice","utcDayStart","Math","floor","sameReport","left","right","day","channel","appVersion","bundleId","releaseId","userId","isRecord","value","Array","isArray","isNullableString","parseReport","report","JSON","parse","parseFailures","failures","keys","every","key","createInsightsState","storage","pausedThisRuntime","read","get","write","set","isPaused","now","pausedAt","Number","isFinite","pause","String","resume","repeatsReport","last","recordReport","stringify","forgetReport","hasFailure","includes","recordFailure","readUserId","user","writeUserId"],"sourceRoot":"../../../../src","sources":["plugins/insights/state.ts"],"mappings":";;AAEA,MAAMA,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;AAClC;AACA,MAAMC,gBAAgB,GAAG,EAAE;;AAE3B;AACA,OAAO,MAAMC,MAAM,GAAIC,IAAY,IACjC,IAAIC,IAAI,CAACD,IAAI,CAAC,CAACE,WAAW,CAAC,CAAC,CAACC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;;AAE3C;AACA,OAAO,MAAMC,WAAW,GAAIJ,IAAY,IACtCK,IAAI,CAACC,KAAK,CAACN,IAAI,GAAGH,MAAM,CAAC,GAAGA,MAAM;;AAEpC;;AAUA,MAAMU,UAAU,GAAGA,CAACC,IAAmB,EAAEC,KAAoB,KAC3DD,IAAI,CAACE,GAAG,KAAKD,KAAK,CAACC,GAAG,IACtBF,IAAI,CAACG,OAAO,KAAKF,KAAK,CAACE,OAAO,IAC9BH,IAAI,CAACI,UAAU,KAAKH,KAAK,CAACG,UAAU,IACpCJ,IAAI,CAACK,QAAQ,KAAKJ,KAAK,CAACI,QAAQ,IAChCL,IAAI,CAACM,SAAS,KAAKL,KAAK,CAACK,SAAS,IAClCN,IAAI,CAACO,MAAM,KAAKN,KAAK,CAACM,MAAM;AAE9B,MAAMC,QAAQ,GAAIC,KAAc,IAC9B,OAAOA,KAAK,KAAK,QAAQ,IAAIA,KAAK,KAAK,IAAI,IAAI,CAACC,KAAK,CAACC,OAAO,CAACF,KAAK,CAAC;AAEtE,MAAMG,gBAAgB,GAAIH,KAAc,IACtCA,KAAK,KAAK,IAAI,IAAI,OAAOA,KAAK,KAAK,QAAQ;AAE7C,MAAMI,WAAW,GAAIJ,KAAoB,IAA2B;EAClE,IAAIA,KAAK,KAAK,IAAI,EAAE,OAAO,IAAI;EAC/B,IAAI;IACF,MAAMK,MAAe,GAAGC,IAAI,CAACC,KAAK,CAACP,KAAK,CAAC;IACzC,IACED,QAAQ,CAACM,MAAM,CAAC,IAChB,OAAOA,MAAM,CAACZ,GAAG,KAAK,QAAQ,IAC9B,OAAOY,MAAM,CAACX,OAAO,KAAK,QAAQ,IAClC,OAAOW,MAAM,CAACV,UAAU,KAAK,QAAQ,IACrC,OAAOU,MAAM,CAACT,QAAQ,KAAK,QAAQ,IACnCO,gBAAgB,CAACE,MAAM,CAACR,SAAS,CAAC,IAClCM,gBAAgB,CAACE,MAAM,CAACP,MAAM,CAAC,EAC/B;MACA,OAAO;QACLL,GAAG,EAAEY,MAAM,CAACZ,GAAG;QACfC,OAAO,EAAEW,MAAM,CAACX,OAAO;QACvBC,UAAU,EAAEU,MAAM,CAACV,UAAU;QAC7BC,QAAQ,EAAES,MAAM,CAACT,QAAQ;QACzBC,SAAS,EAAEQ,MAAM,CAACR,SAAS;QAC3BC,MAAM,EAAEO,MAAM,CAACP;MACjB,CAAC;IACH;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAO,IAAI;AACb,CAAC;AAED,MAAMU,aAAa,GACjBR,KAAoB,IACkD;EACtE,IAAIA,KAAK,KAAK,IAAI,EAAE,OAAO,IAAI;EAC/B,IAAI;IACF,MAAMS,QAAiB,GAAGH,IAAI,CAACC,KAAK,CAACP,KAAK,CAAC;IAC3C,IACED,QAAQ,CAACU,QAAQ,CAAC,IAClB,OAAOA,QAAQ,CAAChB,GAAG,KAAK,QAAQ,IAChCQ,KAAK,CAACC,OAAO,CAACO,QAAQ,CAACC,IAAI,CAAC,IAC5BD,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAEC,GAAG,IAAK,OAAOA,GAAG,KAAK,QAAQ,CAAC,EACrD;MACA,OAAO;QAAEnB,GAAG,EAAEgB,QAAQ,CAAChB,GAAG;QAAEiB,IAAI,EAAED,QAAQ,CAACC;MAAiB,CAAC;IAC/D;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMG,mBAAmB,GAAIC,OAAgC,IAAK;EACvE;EACA,IAAIC,iBAAiB,GAAG,KAAK;EAE7B,MAAMC,IAAI,GAAIJ,GAAW,IAAoB;IAC3C,IAAI;MACF,OAAOE,OAAO,CAACG,GAAG,CAACL,GAAG,CAAC;IACzB,CAAC,CAAC,MAAM;MACN,OAAO,IAAI;IACb;EACF,CAAC;EACD,MAAMM,KAAK,GAAGA,CAACN,GAAW,EAAEZ,KAAoB,KAAK;IACnD,IAAI;MACFc,OAAO,CAACK,GAAG,CAACP,GAAG,EAAEZ,KAAK,CAAC;IACzB,CAAC,CAAC,MAAM;MACN;IAAA;EAEJ,CAAC;EAED,OAAO;IACL;AACJ;AACA;AACA;AACA;IACIoB,QAAQA,CAACC,GAAW,EAAW;MAC7B,IAAIN,iBAAiB,EAAE,OAAO,IAAI;MAClC,MAAMO,QAAQ,GAAGC,MAAM,CAACP,IAAI,CAAC,UAAU,CAAC,CAAC;MACzC,OACEO,MAAM,CAACC,QAAQ,CAACF,QAAQ,CAAC,IACzBA,QAAQ,GAAG,CAAC,IACZA,QAAQ,IAAID,GAAG,IACfA,GAAG,GAAGC,QAAQ,GAAG1C,MAAM;IAE3B,CAAC;IACD6C,KAAKA,CAACJ,GAAW,EAAE;MACjBN,iBAAiB,GAAG,IAAI;MACxBG,KAAK,CAAC,UAAU,EAAEQ,MAAM,CAACL,GAAG,CAAC,CAAC;IAChC,CAAC;IACD;IACAM,MAAMA,CAAA,EAAG;MACP,IAAIX,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAEE,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC;IACxD,CAAC;IAED;IACAU,aAAaA,CAACvB,MAAqB,EAAW;MAC5C,MAAMwB,IAAI,GAAGzB,WAAW,CAACY,IAAI,CAAC,QAAQ,CAAC,CAAC;MACxC,OAAOa,IAAI,KAAK,IAAI,IAAIvC,UAAU,CAACuC,IAAI,EAAExB,MAAM,CAAC;IAClD,CAAC;IACD;IACAyB,YAAYA,CAACzB,MAAqB,EAAE;MAClCa,KAAK,CAAC,QAAQ,EAAEZ,IAAI,CAACyB,SAAS,CAAC1B,MAAM,CAAC,CAAC;IACzC,CAAC;IACD;AACJ;AACA;AACA;IACI2B,YAAYA,CAAA,EAAG;MACb,IAAIhB,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAEE,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;IACpD,CAAC;IAED;IACAe,UAAUA,CAACxC,GAAW,EAAEmB,GAAW,EAAW;MAC5C,MAAMH,QAAQ,GAAGD,aAAa,CAACQ,IAAI,CAAC,UAAU,CAAC,CAAC;MAChD,OAAOP,QAAQ,EAAEhB,GAAG,KAAKA,GAAG,IAAIgB,QAAQ,CAACC,IAAI,CAACwB,QAAQ,CAACtB,GAAG,CAAC;IAC7D,CAAC;IACDuB,aAAaA,CAAC1C,GAAW,EAAEmB,GAAW,EAAE;MACtC,MAAMH,QAAQ,GAAGD,aAAa,CAACQ,IAAI,CAAC,UAAU,CAAC,CAAC;MAChD,MAAMN,IAAI,GAAGD,QAAQ,EAAEhB,GAAG,KAAKA,GAAG,GAAGgB,QAAQ,CAACC,IAAI,GAAG,EAAE;MACvD,IAAIA,IAAI,CAACwB,QAAQ,CAACtB,GAAG,CAAC,EAAE;MACxBM,KAAK,CACH,UAAU,EACVZ,IAAI,CAACyB,SAAS,CAAC;QAAEtC,GAAG;QAAEiB,IAAI,EAAE,CAAC,GAAGA,IAAI,EAAEE,GAAG,CAAC,CAAC1B,KAAK,CAAC,CAACL,gBAAgB;MAAE,CAAC,CACvE,CAAC;IACH,CAAC;IAEDuD,UAAUA,CAAA,EAAkB;MAC1B,IAAI;QACF,MAAMC,IAAa,GAAG/B,IAAI,CAACC,KAAK,CAACS,IAAI,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QACxD,OAAOjB,QAAQ,CAACsC,IAAI,CAAC,IAAI,OAAOA,IAAI,CAACvC,MAAM,KAAK,QAAQ,GACpDuC,IAAI,CAACvC,MAAM,GACX,IAAI;MACV,CAAC,CAAC,MAAM;QACN,OAAO,IAAI;MACb;IACF,CAAC;IACDwC,WAAWA,CAACxC,MAAqB,EAAE;MACjCoB,KAAK,CAAC,MAAM,EAAEpB,MAAM,KAAK,IAAI,GAAG,IAAI,GAAGQ,IAAI,CAACyB,SAAS,CAAC;QAAEjC;MAAO,CAAC,CAAC,CAAC;IACpE;EACF,CAAC;AACH,CAAC","ignoreList":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { MAX_TARGET_COHORTS_PER_RELEASE, MAX_COMPILED_CATALOG_BYTES, MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE, NUMERIC_COHORT_SIZE, isUUIDv7, parseReleaseCatalogScopeKey } from "@hot-updater/core";
|
|
4
|
+
import { InvalidUpdateResponseError, UpdateHttpError } from "./updateError.js";
|
|
4
5
|
const CACHE_FORMAT_VERSION = "1";
|
|
5
6
|
const MAX_ETAG_BYTES = 1024;
|
|
6
7
|
export const MAX_RELEASE_CATALOG_WIRE_BYTES = MAX_COMPILED_CATALOG_BYTES * 2 + 4 * 1024;
|
|
@@ -128,14 +129,19 @@ const readValidatedCache = async (partition, expectedScope) => {
|
|
|
128
129
|
return cached;
|
|
129
130
|
};
|
|
130
131
|
const consumeSuccessfulResponse = async (response, input, partition) => {
|
|
132
|
+
// The server marks the 404 of a scope that has no catalog yet: no update,
|
|
133
|
+
// not a failure. Any other 404 is a wrong baseURL or route.
|
|
134
|
+
if (response.status === 404 && response.headers.get("x-hot-updater-catalog")?.trim().toLowerCase() === "none") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
131
137
|
if (response.status !== 200) {
|
|
132
|
-
throw new
|
|
138
|
+
throw new UpdateHttpError(response.status, response.statusText);
|
|
133
139
|
}
|
|
134
140
|
const body = await response.text();
|
|
135
141
|
const catalog = parseValidatedCatalog(body, input.expectedScope);
|
|
136
142
|
if (catalog === null) {
|
|
137
143
|
await removeNativeReleaseCatalogCache(partition);
|
|
138
|
-
throw new
|
|
144
|
+
throw new InvalidUpdateResponseError("Received an invalid Release catalog");
|
|
139
145
|
}
|
|
140
146
|
const etag = response.headers.get("etag");
|
|
141
147
|
if (isValidETag(etag)) {
|
|
@@ -157,6 +163,11 @@ const consumeSuccessfulResponse = async (response, input, partition) => {
|
|
|
157
163
|
}
|
|
158
164
|
return catalog;
|
|
159
165
|
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Fetches a scope's catalog, or null when the server answers that the scope
|
|
169
|
+
* has none (a 404 with `x-hot-updater-catalog: none`).
|
|
170
|
+
*/
|
|
160
171
|
export const fetchReleaseCatalogWithCache = async input => {
|
|
161
172
|
const partition = createReleaseCatalogCachePartition(input);
|
|
162
173
|
const cached = await readValidatedCache(partition, input.expectedScope);
|