@hot-updater/console 0.36.7 → 1.0.0-rc.0
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/.output/nitro.json +1 -1
- package/.output/public/assets/AnalyticsCapabilityContext-BCke57-7.js +1 -0
- package/.output/public/assets/BundleActivityChart-CnsBe_1J.js +42 -0
- package/.output/public/assets/BundleIdDisplay-Dky75zLU.js +1 -0
- package/.output/public/assets/alert-C-1KvDW8.js +1 -0
- package/.output/public/assets/analytics-Bqz0c-BT.js +1 -0
- package/.output/public/assets/analytics-api-CJdrTLLO.js +1 -0
- package/.output/public/assets/api-B_Rh-uzC.js +1 -0
- package/.output/public/assets/api-keys-BurCFhzH.js +1 -0
- package/.output/public/assets/api-keys-api-BEDJdQg3.js +1 -0
- package/.output/public/assets/badge-Bf0fZ-Gx.js +1 -0
- package/.output/public/assets/chart-no-axes-combined-FaYMGmQS.js +1 -0
- package/.output/public/assets/createServerFn-gM2-LFBy.js +3 -0
- package/.output/public/assets/dialog-BS60BRr2.js +1 -0
- package/.output/public/assets/dist-DspNKfMj.js +1 -0
- package/.output/public/assets/download-O-yNBc9H.js +1 -0
- package/.output/public/assets/field-Buq148-A.js +1 -0
- package/.output/public/assets/index-kFhd2S78.js +10 -0
- package/.output/public/assets/installations-BrQ-od41.js +2 -0
- package/.output/public/assets/installations-CGlZ3ROz.js +1 -0
- package/.output/public/assets/lazyRouteComponent-rPzgA7S-.js +1 -0
- package/.output/public/assets/loader-circle-DCYF-wJl.js +1 -0
- package/.output/public/assets/preload-helper-TKOBYGYD.js +1 -0
- package/.output/public/assets/refresh-cw-n8XYbPPj.js +1 -0
- package/.output/public/assets/routes-CrdokJ80.js +1 -0
- package/.output/public/assets/routes-DcQnVajO.js +2 -0
- package/.output/public/assets/search-DLhbWw3W.js +1 -0
- package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
- package/.output/public/assets/signing-HRexfjsH.js +1 -0
- package/.output/public/assets/styles-eB4EtvFS.css +2 -0
- package/.output/public/assets/table-D8CLieHX.js +1 -0
- package/.output/public/assets/textarea-YJLTVAoq.js +1 -0
- package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
- package/.output/public/assets/useOpenInteractionType-4cRD5xO0.js +1 -0
- package/.output/server/__23tanstack-start-server-fn-resolver-DBP9TQ7K.mjs +146 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +16124 -0
- package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +41 -10
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +132 -0
- package/.output/server/_libs/d3-array.mjs +231 -0
- package/.output/server/_libs/d3-color.mjs +330 -0
- package/.output/server/_libs/d3-format.mjs +252 -0
- package/.output/server/_libs/d3-interpolate.mjs +207 -0
- package/.output/server/_libs/d3-path.mjs +77 -0
- package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
- package/.output/server/_libs/d3-shape.mjs +818 -0
- package/.output/server/_libs/decimal.js-light.mjs +884 -0
- package/.output/server/_libs/es-toolkit.mjs +1134 -0
- package/.output/server/_libs/eventemitter3.mjs +236 -0
- package/.output/server/_libs/immer.mjs +532 -0
- package/.output/server/_libs/lucide-react.mjs +141 -13
- package/.output/server/_libs/next-themes.mjs +2 -2
- package/.output/server/_libs/react-redux+[...].mjs +205 -0
- package/.output/server/_libs/recharts+[...].mjs +14117 -0
- package/.output/server/_libs/sonner.mjs +2 -2
- package/.output/server/_libs/tanstack__query-core.mjs +114 -1
- package/.output/server/_libs/tanstack__react-query.mjs +3 -4
- package/.output/server/_runtime.mjs +10 -1
- package/.output/server/_ssr/AnalyticsCapabilityContext-ui4DX8GO.mjs +15 -0
- package/.output/server/_ssr/BundleActivityChart-BiB9ZORH.mjs +288 -0
- package/.output/server/_ssr/BundleIdDisplay-Czk6iR6p.mjs +13 -0
- package/.output/server/_ssr/alert-Dim3MGY5.mjs +86 -0
- package/.output/server/_ssr/analytics-DtMTn8fn.mjs +858 -0
- package/.output/server/_ssr/analytics-api-Cg1M5v_H.mjs +71 -0
- package/.output/server/_ssr/analytics-input-fRRRn0uA.mjs +96 -0
- package/.output/server/_ssr/analytics-rpc-DY5uXvHT.mjs +155 -0
- package/.output/server/_ssr/api-BTSv1tZb.mjs +282 -0
- package/.output/server/_ssr/api-keys-BnM_o2nS.mjs +300 -0
- package/.output/server/_ssr/api-keys-api-CIywsSF5.mjs +60 -0
- package/.output/server/_ssr/api-keys-rpc-BSp1wkIV.mjs +136 -0
- package/.output/server/_ssr/api-rpc-cG5wd_tO.mjs +1468 -0
- package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
- package/.output/server/_ssr/auth-rpc-BrJ6obHJ.mjs +24 -0
- package/.output/server/_ssr/auth.server-DwKoZoQM.mjs +50 -0
- package/.output/server/_ssr/badge-DCxbEeof.mjs +26 -0
- package/.output/server/_ssr/bundle-signing-rpc-COoQhW8E.mjs +16 -0
- package/.output/server/_ssr/bundle-signing.server-D5u8KkMp.mjs +51 -0
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
- package/.output/server/_ssr/config.server-DEiLhKmy.mjs +361 -0
- package/.output/server/_ssr/{createServerFn-DzU0k62V.mjs → createServerFn-B2hcXoPM.mjs} +1 -1
- package/.output/server/_ssr/createServerRpc-DB29C_5p.mjs +12 -0
- package/.output/server/_ssr/{sidebar-Cv_H0_Jv.mjs → createSsrRpc-9P0S0lJ3.mjs} +68 -50
- package/.output/server/_ssr/databaseRows-CwbNwYSl.mjs +166 -0
- package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
- package/.output/server/_ssr/dialog-CQuRMNZ4.mjs +170 -0
- package/.output/server/_ssr/dist-BVSLTpoP.mjs +250 -0
- package/.output/server/_ssr/downloadBundle-CNnYxYO2.mjs +24 -0
- package/.output/server/_ssr/{empty-plugin-adapters-B4HCzgmM.mjs → empty-plugin-adapters-B5QvGpEl.mjs} +1 -1
- package/.output/server/_ssr/field-DRIXKjZG.mjs +74 -0
- package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
- package/.output/server/_ssr/installations-B-azgAvX.mjs +19 -0
- package/.output/server/_ssr/installations-Ctvzp2JH.mjs +535 -0
- package/.output/server/_ssr/router-C-KUBfOi.mjs +513 -0
- package/.output/server/_ssr/routes-BM4ht9jb.mjs +2286 -0
- package/.output/server/_ssr/routes-Z7TwC6nC.mjs +37 -0
- package/.output/server/_ssr/runtime.server-awTLiiXW.mjs +527 -0
- package/.output/server/_ssr/server-BjzmvXnO.mjs +7468 -0
- package/.output/server/_ssr/signing-k8GxyBDF.mjs +202 -0
- package/.output/server/_ssr/ssr.mjs +4 -5740
- package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
- package/.output/server/_ssr/table-v1yCvYo7.mjs +53 -0
- package/.output/server/_ssr/textarea-DYHnskhb.mjs +14 -0
- package/.output/server/_ssr/uuidv7-BbJ-9D_I.mjs +31 -0
- package/.output/server/_tanstack-start-manifest_v-CH4StzJu.mjs +108 -0
- package/.output/server/index.mjs +256 -53
- package/README.md +29 -8
- package/lib/index.d.mts +36 -0
- package/lib/index.mjs +4 -0
- package/lib/vite-internal.mjs +127 -0
- package/lib/vite.d.mts +10 -0
- package/lib/vite.mjs +5 -0
- package/package.json +51 -29
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/logo.svg +1 -0
- package/public/manifest.json +30 -0
- package/public/robots.txt +3 -0
- package/src/components/AppSidebar.tsx +159 -0
- package/src/components/BundleIdDisplay.tsx +18 -0
- package/src/components/ChannelBadge.tsx +31 -0
- package/src/components/ConsoleAccessPage.tsx +122 -0
- package/src/components/EnabledStatusIcon.tsx +54 -0
- package/src/components/HashValueDisplay.tsx +85 -0
- package/src/components/HotUpdaterLogo.tsx +30 -0
- package/src/components/NotFoundPage.tsx +33 -0
- package/src/components/PlatformIcon.tsx +39 -0
- package/src/components/RolloutPercentageBadge.tsx +28 -0
- package/src/components/ThemeProvider.tsx +116 -0
- package/src/components/TimestampDisplay.tsx +41 -0
- package/src/components/features/analytics/ActivityChart.tsx +150 -0
- package/src/components/features/analytics/AnalyticsCapabilityContext.tsx +24 -0
- package/src/components/features/analytics/AnalyticsControls.tsx +115 -0
- package/src/components/features/analytics/AnalyticsErrorAlert.tsx +21 -0
- package/src/components/features/analytics/AnalyticsOverview.tsx +208 -0
- package/src/components/features/analytics/BundleDistribution.tsx +133 -0
- package/src/components/features/analytics/BundleSelector.tsx +114 -0
- package/src/components/features/analytics/InstallationHistoryCard.tsx +248 -0
- package/src/components/features/analytics/InstallationMatchesCard.tsx +132 -0
- package/src/components/features/analytics/InstallationPageHeader.tsx +138 -0
- package/src/components/features/analytics/InstallationPagination.tsx +48 -0
- package/src/components/features/analytics/InstallationSearch.tsx +230 -0
- package/src/components/features/analytics/UpdateOutcomes.tsx +147 -0
- package/src/components/features/api-keys/ApiKeysPage.tsx +377 -0
- package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
- package/src/components/features/bundles/BundleAnalyticsSummary.tsx +123 -0
- package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
- package/src/components/features/bundles/BundleMetadata.tsx +136 -0
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +201 -0
- package/src/components/features/channels/ChannelManagementDialog.tsx +228 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +879 -0
- package/src/components/features/releases/ReleaseStateBadge.tsx +20 -0
- package/src/components/features/signing/BundleSigningPage.tsx +247 -0
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +89 -0
- package/src/components/ui/chart.tsx +371 -0
- package/src/components/ui/combobox.tsx +303 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/dropdown-menu.tsx +270 -0
- package/src/components/ui/field.tsx +235 -0
- package/src/components/ui/input-group.tsx +147 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +25 -0
- package/src/components/ui/progress.tsx +84 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +132 -0
- package/src/components/ui/sidebar.tsx +779 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +56 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/switch.tsx +25 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +30 -0
- package/src/index.ts +48 -0
- package/src/lib/analytics-api.ts +145 -0
- package/src/lib/analytics-error.ts +19 -0
- package/src/lib/analytics-input.ts +187 -0
- package/src/lib/analytics-overview.ts +170 -0
- package/src/lib/analytics-rpc.ts +218 -0
- package/src/lib/api-keys-api.ts +69 -0
- package/src/lib/api-keys-rpc.ts +87 -0
- package/src/lib/api-rpc.ts +478 -0
- package/src/lib/api.ts +444 -0
- package/src/lib/auth-rpc.ts +16 -0
- package/src/lib/bundle-signing-api.ts +14 -0
- package/src/lib/bundle-signing-rpc.ts +16 -0
- package/src/lib/constants.ts +1 -0
- package/src/lib/git.ts +51 -0
- package/src/lib/server/auth.server.ts +43 -0
- package/src/lib/server/bundle-signing.server.ts +100 -0
- package/src/lib/server/config.server.ts +109 -0
- package/src/lib/server/console-runtime.server.ts +70 -0
- package/src/lib/server/deleteBundle.ts +145 -0
- package/src/lib/server/downloadBundle.ts +46 -0
- package/src/lib/server/getBundleChildren.ts +130 -0
- package/src/lib/server/listReleases.ts +61 -0
- package/src/lib/server/releaseActivity.ts +27 -0
- package/src/lib/server/releaseReachability.ts +95 -0
- package/src/lib/server/runtime.server.ts +172 -0
- package/src/lib/utils.ts +9 -0
- package/src/logo.svg +1 -0
- package/src/routeTree.gen.ts +199 -0
- package/src/router.tsx +21 -0
- package/src/routes/-installations-search.ts +19 -0
- package/src/routes/-releases-search.ts +74 -0
- package/src/routes/__root.tsx +174 -0
- package/src/routes/analytics.tsx +139 -0
- package/src/routes/api/auth/$.ts +19 -0
- package/src/routes/api/bundles/$bundleId/download.ts +17 -0
- package/src/routes/api-keys.tsx +37 -0
- package/src/routes/index.tsx +982 -0
- package/src/routes/installations.tsx +194 -0
- package/src/routes/signing.tsx +37 -0
- package/src/styles.css +152 -0
- package/src/virtual-modules.d.ts +21 -0
- package/src/vite-env.d.ts +1 -0
- package/src/vite-internal.ts +195 -0
- package/src/vite.ts +12 -0
- package/.output/public/assets/dist-DJX53faP.js +0 -3
- package/.output/public/assets/index-Iylis5kY.js +0 -10
- package/.output/public/assets/routes-HT21hs0C.js +0 -54
- package/.output/public/assets/styles-C0Re4quB.css +0 -2
- package/.output/server/__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs +0 -66
- package/.output/server/_libs/@floating-ui/core+[...].mjs +0 -698
- package/.output/server/_libs/@floating-ui/dom+[...].mjs +0 -644
- package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +0 -839
- package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +0 -2138
- package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +0 -308
- package/.output/server/_libs/@radix-ui/react-select+[...].mjs +0 -1038
- package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +0 -196
- package/.output/server/_libs/@tanstack/form-core+[...].mjs +0 -3228
- package/.output/server/_libs/@tanstack/react-form+[...].mjs +0 -430
- package/.output/server/_libs/@tanstack/react-table+[...].mjs +0 -2382
- package/.output/server/_libs/radix-ui__number.mjs +0 -6
- package/.output/server/_libs/radix-ui__primitive.mjs +0 -9
- package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
- package/.output/server/_libs/radix-ui__react-collection.mjs +0 -78
- package/.output/server/_libs/radix-ui__react-direction.mjs +0 -13
- package/.output/server/_libs/radix-ui__react-label.mjs +0 -22
- package/.output/server/_libs/radix-ui__react-separator.mjs +0 -30
- package/.output/server/_libs/radix-ui__react-slider.mjs +0 -510
- package/.output/server/_libs/radix-ui__react-switch.mjs +0 -157
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -493
- package/.output/server/_ssr/api-rpc-BIbDUKSz.mjs +0 -292
- package/.output/server/_ssr/config.server-BSS366KT.mjs +0 -41
- package/.output/server/_ssr/deleteBundle-BgbZTtpW.mjs +0 -141
- package/.output/server/_ssr/dist-KboydRVk.mjs +0 -120
- package/.output/server/_ssr/router-C3hqEy3k.mjs +0 -307
- package/.output/server/_ssr/routes-B0lQvvlC.mjs +0 -3048
- package/.output/server/_ssr/start-02sLteYG.mjs +0 -4
- package/.output/server/_ssr/storageProfile-FVkjPoGb.mjs +0 -9
- package/.output/server/_tanstack-start-manifest_v-Bpx52WOU.mjs +0 -20
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/.output/server/package.json +0 -9
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { d as createFileRoute, u as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
2
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/routes-Z7TwC6nC.js
|
|
3
|
+
var optionalString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
4
|
+
function validateReleaseSearch(search) {
|
|
5
|
+
const parsedPage = typeof search.page === "number" ? search.page : typeof search.page === "string" ? Number(search.page) : void 0;
|
|
6
|
+
const beforeReleaseId = optionalString(search.beforeReleaseId);
|
|
7
|
+
const afterReleaseId = beforeReleaseId ? void 0 : optionalString(search.afterReleaseId);
|
|
8
|
+
const enabled = search.enabled === true || search.enabled === "true" ? true : search.enabled === false || search.enabled === "false" ? false : void 0;
|
|
9
|
+
return {
|
|
10
|
+
afterReleaseId,
|
|
11
|
+
beforeReleaseId,
|
|
12
|
+
bundleId: optionalString(search.bundleId),
|
|
13
|
+
channelId: optionalString(search.channelId),
|
|
14
|
+
enabled,
|
|
15
|
+
page: parsedPage !== void 0 && Number.isInteger(parsedPage) && parsedPage > 1 ? parsedPage : void 0,
|
|
16
|
+
platform: search.platform === "ios" || search.platform === "android" ? search.platform : void 0,
|
|
17
|
+
releaseId: optionalString(search.releaseId),
|
|
18
|
+
targetAppVersion: optionalString(search.targetAppVersion)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function updateReleaseFilters(current, filters) {
|
|
22
|
+
return {
|
|
23
|
+
...current,
|
|
24
|
+
...filters,
|
|
25
|
+
afterReleaseId: void 0,
|
|
26
|
+
beforeReleaseId: void 0,
|
|
27
|
+
page: void 0,
|
|
28
|
+
releaseId: void 0
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
var $$splitComponentImporter = () => import("./routes-BM4ht9jb.mjs");
|
|
32
|
+
var Route = createFileRoute("/")({
|
|
33
|
+
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
34
|
+
validateSearch: validateReleaseSearch
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { updateReleaseFilters as n, Route as t };
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { a as parseSearchInstallationsInput, i as parseInstallationHistoryInput, n as parseBundleEventAnalyticsInput, r as parseBundleEventSummaryInput, t as parseActiveInstallationInput } from "./analytics-input-fRRRn0uA.mjs";
|
|
2
|
+
import { t as createUUIDv7 } from "./uuidv7-BbJ-9D_I.mjs";
|
|
3
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/runtime.server-awTLiiXW.js
|
|
4
|
+
var AnalyticsScanLimitExceededError = class extends Error {
|
|
5
|
+
constructor(limit) {
|
|
6
|
+
super(`Analytics event scan exceeded ${limit} rows.`);
|
|
7
|
+
this.limit = limit;
|
|
8
|
+
this.name = "AnalyticsScanLimitExceededError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var HOUR_MS = 3600 * 1e3;
|
|
12
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
13
|
+
var ACTIVE_BUNDLE_EVENT_TYPES = [
|
|
14
|
+
"UPDATE_APPLIED",
|
|
15
|
+
"RECOVERED",
|
|
16
|
+
"RELEASE_ADOPTED",
|
|
17
|
+
"UNCHANGED"
|
|
18
|
+
];
|
|
19
|
+
var activeWindowDefinitions = {
|
|
20
|
+
"24h": {
|
|
21
|
+
bucketCount: 24,
|
|
22
|
+
bucketSizeMs: HOUR_MS
|
|
23
|
+
},
|
|
24
|
+
"7d": {
|
|
25
|
+
bucketCount: 7,
|
|
26
|
+
bucketSizeMs: DAY_MS
|
|
27
|
+
},
|
|
28
|
+
"30d": {
|
|
29
|
+
bucketCount: 30,
|
|
30
|
+
bucketSizeMs: DAY_MS
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function getActiveWindowDefinition(window) {
|
|
34
|
+
return activeWindowDefinitions[window];
|
|
35
|
+
}
|
|
36
|
+
function compareCodePoints$1(left, right) {
|
|
37
|
+
if (left < right) return -1;
|
|
38
|
+
if (left > right) return 1;
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
function isNewer(candidate, current) {
|
|
42
|
+
return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints$1(candidate.id, current.id) > 0;
|
|
43
|
+
}
|
|
44
|
+
function collectActiveInstallationOverview(request) {
|
|
45
|
+
const definition = getActiveWindowDefinition(request.window);
|
|
46
|
+
const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
|
|
47
|
+
const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
|
|
48
|
+
const latestByInstall = /* @__PURE__ */ new Map();
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
const current = latestByInstall.get(row.install_id);
|
|
51
|
+
if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
|
|
52
|
+
}
|
|
53
|
+
const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
|
|
54
|
+
const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
|
|
55
|
+
const bundleCounts = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
|
|
57
|
+
const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
if (!selectedInstallIds.has(row.install_id)) continue;
|
|
60
|
+
const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
|
|
61
|
+
if (bucket === void 0) continue;
|
|
62
|
+
const current = bucket.get(row.install_id);
|
|
63
|
+
if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
|
|
64
|
+
}
|
|
65
|
+
const bundleCountsByBucket = latestByBucket.map((bucket) => {
|
|
66
|
+
const counts = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
|
|
68
|
+
return counts;
|
|
69
|
+
});
|
|
70
|
+
const bundleObservationTotals = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
|
|
72
|
+
return {
|
|
73
|
+
asOfMs: request.asOfMs,
|
|
74
|
+
window: request.window,
|
|
75
|
+
activeInstallations: selectedInstallIds.size,
|
|
76
|
+
series: latestByBucket.map((bucket, index) => ({
|
|
77
|
+
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
78
|
+
value: bucket.size
|
|
79
|
+
})),
|
|
80
|
+
bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints$1(leftId, rightId)).map(([bundleId]) => ({
|
|
81
|
+
bundleId,
|
|
82
|
+
series: bundleCountsByBucket.map((counts, index) => ({
|
|
83
|
+
bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
|
|
84
|
+
value: counts.get(bundleId) ?? 0
|
|
85
|
+
}))
|
|
86
|
+
})),
|
|
87
|
+
bundles: [...bundleCounts].map(([bundleId, installations]) => ({
|
|
88
|
+
bundleId,
|
|
89
|
+
installations
|
|
90
|
+
})).sort((left, right) => right.installations - left.installations || compareCodePoints$1(left.bundleId, right.bundleId))
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
var ANALYTICS_SCAN_MAX_ROWS = 5e4;
|
|
94
|
+
var ANALYTICS_MATERIALIZATION_LIMIT = ANALYTICS_SCAN_MAX_ROWS + 1;
|
|
95
|
+
var ANALYTICS_SCAN_PAGE_SIZE = 1e3;
|
|
96
|
+
var ANALYTICS_LOWER_BOUND_ID = "00000000-0000-0000-0000-000000000000";
|
|
97
|
+
var compareCodePoints = (left, right) => {
|
|
98
|
+
if (left < right) return -1;
|
|
99
|
+
if (left > right) return 1;
|
|
100
|
+
return 0;
|
|
101
|
+
};
|
|
102
|
+
var compareEventNewest = (left, right) => right.received_at_ms - left.received_at_ms || compareCodePoints(right.id, left.id);
|
|
103
|
+
var compareEventOldest = (left, right) => left.received_at_ms - right.received_at_ms || compareCodePoints(left.id, right.id);
|
|
104
|
+
var materializeEventRows = async (scope) => {
|
|
105
|
+
const rows = [];
|
|
106
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
107
|
+
let after = scope.lowerBoundMs === void 0 ? void 0 : {
|
|
108
|
+
receivedAtMs: scope.lowerBoundMs,
|
|
109
|
+
id: ANALYTICS_LOWER_BOUND_ID
|
|
110
|
+
};
|
|
111
|
+
while (rows.length < ANALYTICS_MATERIALIZATION_LIMIT) {
|
|
112
|
+
const limit = Math.min(ANALYTICS_SCAN_PAGE_SIZE, ANALYTICS_MATERIALIZATION_LIMIT - rows.length);
|
|
113
|
+
const page = await scope.persistence.scan({
|
|
114
|
+
beforeReceivedAtMs: scope.cutoffMs,
|
|
115
|
+
...after === void 0 ? {} : { after },
|
|
116
|
+
limit
|
|
117
|
+
});
|
|
118
|
+
if (page.length === 0) break;
|
|
119
|
+
if (page.length > limit) throw new AnalyticsPersistenceOrderError();
|
|
120
|
+
let previous = after === void 0 ? void 0 : {
|
|
121
|
+
received_at_ms: after.receivedAtMs,
|
|
122
|
+
id: after.id
|
|
123
|
+
};
|
|
124
|
+
for (const row of page) {
|
|
125
|
+
if (row.received_at_ms >= scope.cutoffMs || previous !== void 0 && compareEventOldest(previous, row) >= 0 || seenIds.has(row.id)) throw new AnalyticsPersistenceOrderError();
|
|
126
|
+
seenIds.add(row.id);
|
|
127
|
+
rows.push(row);
|
|
128
|
+
previous = row;
|
|
129
|
+
}
|
|
130
|
+
const last = page.at(-1);
|
|
131
|
+
if (last !== void 0) after = {
|
|
132
|
+
receivedAtMs: last.received_at_ms,
|
|
133
|
+
id: last.id
|
|
134
|
+
};
|
|
135
|
+
if (rows.length > 5e4) throw new AnalyticsScanLimitExceededError(ANALYTICS_SCAN_MAX_ROWS);
|
|
136
|
+
}
|
|
137
|
+
return rows;
|
|
138
|
+
};
|
|
139
|
+
var AnalyticsPersistenceOrderError = class extends Error {
|
|
140
|
+
name = "AnalyticsPersistenceOrderError";
|
|
141
|
+
constructor() {
|
|
142
|
+
super("Analytics persistence did not advance its scan cursor.");
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var materializeActiveRows = async (scope, window) => {
|
|
146
|
+
const definition = getActiveWindowDefinition(window);
|
|
147
|
+
const durationMs = definition.bucketCount * definition.bucketSizeMs;
|
|
148
|
+
return (await materializeEventRows({
|
|
149
|
+
...scope,
|
|
150
|
+
lowerBoundMs: scope.cutoffMs - durationMs
|
|
151
|
+
})).filter((row) => row.received_at_ms >= scope.cutoffMs - durationMs && ACTIVE_BUNDLE_EVENT_TYPES.includes(row.type));
|
|
152
|
+
};
|
|
153
|
+
var startOfUtcHour = (value) => {
|
|
154
|
+
const date = new Date(value);
|
|
155
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours());
|
|
156
|
+
};
|
|
157
|
+
var startOfUtcDay = (value) => {
|
|
158
|
+
const date = new Date(value);
|
|
159
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
160
|
+
};
|
|
161
|
+
var getWindowRange = (window, now) => {
|
|
162
|
+
if (window === "24h") return {
|
|
163
|
+
sizeMs: 3600 * 1e3,
|
|
164
|
+
rangeStart: startOfUtcHour(now) - 1380 * 60 * 1e3
|
|
165
|
+
};
|
|
166
|
+
const days = window === "7d" ? 7 : 30;
|
|
167
|
+
return {
|
|
168
|
+
sizeMs: 1440 * 60 * 1e3,
|
|
169
|
+
rangeStart: startOfUtcDay(now) - (days - 1) * 24 * 60 * 60 * 1e3
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
var materializeRowsForWindow = async (scope, window) => {
|
|
173
|
+
if (window === "all") return materializeEventRows(scope);
|
|
174
|
+
const range = getWindowRange(window, scope.cutoffMs);
|
|
175
|
+
return (await materializeEventRows({
|
|
176
|
+
...scope,
|
|
177
|
+
lowerBoundMs: range.rangeStart
|
|
178
|
+
})).filter(({ received_at_ms }) => received_at_ms >= range.rangeStart);
|
|
179
|
+
};
|
|
180
|
+
var bucketStart = (receivedAtMs, sizeMs) => sizeMs === 3600 * 1e3 ? startOfUtcHour(receivedAtMs) : startOfUtcDay(receivedAtMs);
|
|
181
|
+
var createSeries = (request) => {
|
|
182
|
+
const range = request.window === "all" ? void 0 : getWindowRange(request.window, request.cutoffMs);
|
|
183
|
+
const sizeMs = range?.sizeMs ?? 1440 * 60 * 1e3;
|
|
184
|
+
const installIdsByBucket = /* @__PURE__ */ new Map();
|
|
185
|
+
let oldestMs = request.cutoffMs;
|
|
186
|
+
for (const row of request.rows) {
|
|
187
|
+
oldestMs = Math.min(oldestMs, row.received_at_ms);
|
|
188
|
+
const start = bucketStart(row.received_at_ms, sizeMs);
|
|
189
|
+
const installIds = installIdsByBucket.get(start) ?? /* @__PURE__ */ new Set();
|
|
190
|
+
installIds.add(row.install_id);
|
|
191
|
+
installIdsByBucket.set(start, installIds);
|
|
192
|
+
}
|
|
193
|
+
const first = range?.rangeStart ?? startOfUtcDay(oldestMs);
|
|
194
|
+
const last = bucketStart(request.cutoffMs, sizeMs);
|
|
195
|
+
return Array.from({ length: Math.floor((last - first) / sizeMs) + 1 }, (_, index) => {
|
|
196
|
+
const start = first + index * sizeMs;
|
|
197
|
+
return {
|
|
198
|
+
bucketStartMs: start,
|
|
199
|
+
value: installIdsByBucket.get(start)?.size ?? 0
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
var collectEventActivity = (request) => {
|
|
204
|
+
const range = request.window === "all" ? void 0 : getWindowRange(request.window, request.cutoffMs);
|
|
205
|
+
const rows = range === void 0 ? request.rows : request.rows.filter(({ received_at_ms }) => received_at_ms >= range.rangeStart && received_at_ms < request.cutoffMs);
|
|
206
|
+
const installs = /* @__PURE__ */ new Set();
|
|
207
|
+
const installsByCohort = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const row of rows) {
|
|
209
|
+
installs.add(row.install_id);
|
|
210
|
+
const cohort = installsByCohort.get(row.cohort) ?? /* @__PURE__ */ new Set();
|
|
211
|
+
cohort.add(row.install_id);
|
|
212
|
+
installsByCohort.set(row.cohort, cohort);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
summary: installs.size,
|
|
216
|
+
cohorts: [...installsByCohort].sort(([left], [right]) => compareCodePoints(left, right)).map(([cohort, installIds]) => ({
|
|
217
|
+
cohort,
|
|
218
|
+
value: installIds.size
|
|
219
|
+
})),
|
|
220
|
+
series: createSeries({
|
|
221
|
+
...request,
|
|
222
|
+
rows
|
|
223
|
+
})
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
function toSearchRow(row) {
|
|
227
|
+
return {
|
|
228
|
+
installId: row.install_id,
|
|
229
|
+
username: row.username,
|
|
230
|
+
userId: row.user_id,
|
|
231
|
+
lastKnownBundleId: row.to_bundle_id,
|
|
232
|
+
latestStatus: row.type,
|
|
233
|
+
platform: row.platform,
|
|
234
|
+
appVersion: row.app_version,
|
|
235
|
+
channel: row.channel,
|
|
236
|
+
cohort: row.cohort,
|
|
237
|
+
receivedAtMs: row.received_at_ms
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function matchesIdentity(row, query) {
|
|
241
|
+
return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
|
|
242
|
+
}
|
|
243
|
+
function searchEventInstallations(request) {
|
|
244
|
+
const query = request.query.toLowerCase();
|
|
245
|
+
const matchingInstallIds = /* @__PURE__ */ new Set();
|
|
246
|
+
for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
|
|
247
|
+
const latestByInstall = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const row of request.rows) {
|
|
249
|
+
if (!matchingInstallIds.has(row.install_id)) continue;
|
|
250
|
+
const current = latestByInstall.get(row.install_id);
|
|
251
|
+
if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
|
|
252
|
+
}
|
|
253
|
+
const matchingRows = [...latestByInstall.values()].sort((left, right) => compareCodePoints(left.install_id, right.install_id));
|
|
254
|
+
const pageSize = Math.min(Math.max(request.limit, 0), 100);
|
|
255
|
+
return {
|
|
256
|
+
data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
|
|
257
|
+
pagination: {
|
|
258
|
+
total: matchingRows.length,
|
|
259
|
+
limit: request.limit,
|
|
260
|
+
offset: request.offset
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function createBundleEventRow(input) {
|
|
265
|
+
const base = {
|
|
266
|
+
id: createUUIDv7(),
|
|
267
|
+
install_id: input.installId,
|
|
268
|
+
user_id: input.userId ?? null,
|
|
269
|
+
username: input.username ?? null,
|
|
270
|
+
from_release_id: input.fromReleaseId,
|
|
271
|
+
to_release_id: input.toReleaseId,
|
|
272
|
+
to_bundle_id: input.toBundleId,
|
|
273
|
+
platform: input.platform,
|
|
274
|
+
app_version: input.appVersion,
|
|
275
|
+
channel: input.channel,
|
|
276
|
+
cohort: input.cohort,
|
|
277
|
+
fingerprint_hash: input.fingerprintHash,
|
|
278
|
+
sdk_version: input.sdkVersion ?? null,
|
|
279
|
+
received_at_ms: Date.now()
|
|
280
|
+
};
|
|
281
|
+
switch (input.type) {
|
|
282
|
+
case "UPDATE_APPLIED":
|
|
283
|
+
case "RECOVERED":
|
|
284
|
+
case "RELEASE_ADOPTED": return {
|
|
285
|
+
...base,
|
|
286
|
+
type: input.type,
|
|
287
|
+
from_bundle_id: input.fromBundleId,
|
|
288
|
+
update_strategy: input.updateStrategy
|
|
289
|
+
};
|
|
290
|
+
case "UNCHANGED": return {
|
|
291
|
+
...base,
|
|
292
|
+
type: input.type,
|
|
293
|
+
from_bundle_id: null,
|
|
294
|
+
update_strategy: null
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
var isTransitionEventRow = (row) => row.type === "UPDATE_APPLIED" || row.type === "RECOVERED";
|
|
299
|
+
var toHistoryRow = (row) => ({
|
|
300
|
+
id: row.id,
|
|
301
|
+
type: row.type,
|
|
302
|
+
fromBundleId: row.from_bundle_id,
|
|
303
|
+
toBundleId: row.to_bundle_id,
|
|
304
|
+
username: row.username,
|
|
305
|
+
userId: row.user_id,
|
|
306
|
+
platform: row.platform,
|
|
307
|
+
appVersion: row.app_version,
|
|
308
|
+
channel: row.channel,
|
|
309
|
+
cohort: row.cohort,
|
|
310
|
+
receivedAtMs: row.received_at_ms
|
|
311
|
+
});
|
|
312
|
+
var isInstalledForBundle = (row, bundleId) => row.type === "UPDATE_APPLIED" && row.to_bundle_id === bundleId;
|
|
313
|
+
var isRecoveredFromBundle = (row, bundleId) => row.type === "RECOVERED" && row.from_bundle_id === bundleId;
|
|
314
|
+
var countDistinctInstallations = (rows) => new Set(rows.map(({ install_id }) => install_id)).size;
|
|
315
|
+
var getAnalyticsResult = async (persistence, bundleId, window, limit, offset) => {
|
|
316
|
+
const scope = {
|
|
317
|
+
persistence,
|
|
318
|
+
cutoffMs: Date.now()
|
|
319
|
+
};
|
|
320
|
+
const rows = (await materializeRowsForWindow(scope, window)).filter(isTransitionEventRow);
|
|
321
|
+
const installedRows = rows.filter((row) => isInstalledForBundle(row, bundleId));
|
|
322
|
+
const recoveredRows = rows.filter((row) => isRecoveredFromBundle(row, bundleId));
|
|
323
|
+
const installed = collectEventActivity({
|
|
324
|
+
rows: installedRows,
|
|
325
|
+
window,
|
|
326
|
+
cutoffMs: scope.cutoffMs
|
|
327
|
+
});
|
|
328
|
+
const recovered = collectEventActivity({
|
|
329
|
+
rows: recoveredRows,
|
|
330
|
+
window,
|
|
331
|
+
cutoffMs: scope.cutoffMs
|
|
332
|
+
});
|
|
333
|
+
const recentRows = [...installedRows, ...recoveredRows].sort(compareEventNewest);
|
|
334
|
+
return {
|
|
335
|
+
summary: {
|
|
336
|
+
installed: installed.summary,
|
|
337
|
+
recovered: recovered.summary
|
|
338
|
+
},
|
|
339
|
+
series: {
|
|
340
|
+
installed: installed.series,
|
|
341
|
+
recovered: recovered.series
|
|
342
|
+
},
|
|
343
|
+
cohorts: {
|
|
344
|
+
installed: installed.cohorts,
|
|
345
|
+
recovered: recovered.cohorts
|
|
346
|
+
},
|
|
347
|
+
recentEvents: {
|
|
348
|
+
data: recentRows.slice(offset, offset + limit).map(toHistoryRow),
|
|
349
|
+
pagination: {
|
|
350
|
+
total: recentRows.length,
|
|
351
|
+
limit,
|
|
352
|
+
offset
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
var getAnalyticsSummaries = async (persistence, bundleIds, window) => {
|
|
358
|
+
const normalizedBundleIds = [...new Set(bundleIds)];
|
|
359
|
+
if (normalizedBundleIds.length === 0) return [];
|
|
360
|
+
const requestedBundleIds = new Set(normalizedBundleIds);
|
|
361
|
+
const installedByBundleId = /* @__PURE__ */ new Map();
|
|
362
|
+
const recoveredByBundleId = /* @__PURE__ */ new Map();
|
|
363
|
+
const rows = (await materializeRowsForWindow({
|
|
364
|
+
persistence,
|
|
365
|
+
cutoffMs: Date.now()
|
|
366
|
+
}, window)).filter(isTransitionEventRow);
|
|
367
|
+
for (const row of rows) {
|
|
368
|
+
const bundleId = row.type === "UPDATE_APPLIED" ? row.to_bundle_id : row.from_bundle_id;
|
|
369
|
+
if (!requestedBundleIds.has(bundleId)) continue;
|
|
370
|
+
const counts = row.type === "UPDATE_APPLIED" ? installedByBundleId : recoveredByBundleId;
|
|
371
|
+
const installIds = counts.get(bundleId) ?? /* @__PURE__ */ new Set();
|
|
372
|
+
installIds.add(row.install_id);
|
|
373
|
+
counts.set(bundleId, installIds);
|
|
374
|
+
}
|
|
375
|
+
return normalizedBundleIds.map((bundleId) => ({
|
|
376
|
+
bundleId,
|
|
377
|
+
installed: installedByBundleId.get(bundleId)?.size ?? 0,
|
|
378
|
+
recovered: recoveredByBundleId.get(bundleId)?.size ?? 0
|
|
379
|
+
}));
|
|
380
|
+
};
|
|
381
|
+
var createAnalyticsProvider = (persistence) => Object.freeze({
|
|
382
|
+
mode: "bounded",
|
|
383
|
+
maxMatchingRows: ANALYTICS_SCAN_MAX_ROWS,
|
|
384
|
+
async appendBundleEvent(input) {
|
|
385
|
+
await persistence.append(createBundleEventRow(input));
|
|
386
|
+
},
|
|
387
|
+
async getBundleEventSummary(bundleId) {
|
|
388
|
+
const rows = (await materializeEventRows({
|
|
389
|
+
persistence,
|
|
390
|
+
cutoffMs: Date.now()
|
|
391
|
+
})).filter(isTransitionEventRow);
|
|
392
|
+
return {
|
|
393
|
+
installed: countDistinctInstallations(rows.filter((row) => isInstalledForBundle(row, bundleId))),
|
|
394
|
+
recovered: countDistinctInstallations(rows.filter((row) => isRecoveredFromBundle(row, bundleId)))
|
|
395
|
+
};
|
|
396
|
+
},
|
|
397
|
+
getBundleEventSummaries(bundleIds, window) {
|
|
398
|
+
return getAnalyticsSummaries(persistence, bundleIds, window);
|
|
399
|
+
},
|
|
400
|
+
getBundleEventAnalytics(bundleId, window, limit, offset) {
|
|
401
|
+
return getAnalyticsResult(persistence, bundleId, window, limit, offset);
|
|
402
|
+
},
|
|
403
|
+
async getBundleEventOverview() {
|
|
404
|
+
const rows = await materializeEventRows({
|
|
405
|
+
persistence,
|
|
406
|
+
cutoffMs: Date.now()
|
|
407
|
+
});
|
|
408
|
+
const latestByInstall = /* @__PURE__ */ new Map();
|
|
409
|
+
for (const row of rows) {
|
|
410
|
+
const current = latestByInstall.get(row.install_id);
|
|
411
|
+
if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
|
|
412
|
+
}
|
|
413
|
+
const counts = /* @__PURE__ */ new Map();
|
|
414
|
+
for (const row of latestByInstall.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
|
|
415
|
+
return {
|
|
416
|
+
trackedInstallations: latestByInstall.size,
|
|
417
|
+
bundles: [...counts].map(([bundleId, installations]) => ({
|
|
418
|
+
bundleId,
|
|
419
|
+
installations
|
|
420
|
+
})).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
|
|
421
|
+
};
|
|
422
|
+
},
|
|
423
|
+
async getActiveInstallationOverview(input) {
|
|
424
|
+
const asOfMs = Date.now();
|
|
425
|
+
return collectActiveInstallationOverview({
|
|
426
|
+
rows: await materializeActiveRows({
|
|
427
|
+
persistence,
|
|
428
|
+
cutoffMs: asOfMs
|
|
429
|
+
}, input.window),
|
|
430
|
+
asOfMs,
|
|
431
|
+
window: input.window,
|
|
432
|
+
...input.userId === void 0 ? {} : { userId: input.userId }
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
async searchInstallations(query, limit, offset) {
|
|
436
|
+
return searchEventInstallations({
|
|
437
|
+
rows: await materializeEventRows({
|
|
438
|
+
persistence,
|
|
439
|
+
cutoffMs: Date.now()
|
|
440
|
+
}),
|
|
441
|
+
query,
|
|
442
|
+
limit,
|
|
443
|
+
offset
|
|
444
|
+
});
|
|
445
|
+
},
|
|
446
|
+
async getInstallationHistory(installId, limit, offset) {
|
|
447
|
+
const ordered = (await materializeEventRows({
|
|
448
|
+
persistence,
|
|
449
|
+
cutoffMs: Date.now()
|
|
450
|
+
})).filter((row) => row.install_id === installId && isTransitionEventRow(row)).toSorted(compareEventNewest);
|
|
451
|
+
return {
|
|
452
|
+
data: ordered.slice(offset, offset + limit).map(toHistoryRow),
|
|
453
|
+
pagination: {
|
|
454
|
+
total: ordered.length,
|
|
455
|
+
limit,
|
|
456
|
+
offset
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
function createRuntimeHotUpdater(config) {
|
|
462
|
+
const models = Reflect.get(config.database, "models");
|
|
463
|
+
const analytics = typeof models === "object" && models !== null ? Reflect.get(models, "analytics") : void 0;
|
|
464
|
+
if (typeof analytics !== "object" || analytics === null || typeof Reflect.get(analytics, "append") !== "function" || typeof Reflect.get(analytics, "scan") !== "function") return null;
|
|
465
|
+
return createAnalyticsProvider(analytics);
|
|
466
|
+
}
|
|
467
|
+
function createApiKeyStore(config) {
|
|
468
|
+
const models = Reflect.get(config.database, "models");
|
|
469
|
+
const apiKeys = typeof models === "object" && models !== null ? Reflect.get(models, "apiKeys") : void 0;
|
|
470
|
+
if (typeof apiKeys !== "object" || apiKeys === null || typeof Reflect.get(apiKeys, "create") !== "function" || typeof Reflect.get(apiKeys, "findByHash") !== "function" || typeof Reflect.get(apiKeys, "list") !== "function" || typeof Reflect.get(apiKeys, "revoke") !== "function") return null;
|
|
471
|
+
return apiKeys;
|
|
472
|
+
}
|
|
473
|
+
var providerMethods = [
|
|
474
|
+
"appendBundleEvent",
|
|
475
|
+
"getBundleEventSummary",
|
|
476
|
+
"getBundleEventSummaries",
|
|
477
|
+
"getBundleEventAnalytics",
|
|
478
|
+
"getBundleEventOverview",
|
|
479
|
+
"getActiveInstallationOverview",
|
|
480
|
+
"searchInstallations",
|
|
481
|
+
"getInstallationHistory"
|
|
482
|
+
];
|
|
483
|
+
var parseAnalyticsProvider = (value) => {
|
|
484
|
+
if (typeof value !== "object" || value === null || Reflect.get(value, "mode") !== "bounded" || typeof Reflect.get(value, "maxMatchingRows") !== "number" || !providerMethods.every((method) => typeof Reflect.get(value, method) === "function")) return null;
|
|
485
|
+
return value;
|
|
486
|
+
};
|
|
487
|
+
var getAnalyticsCapability = async (provider) => {
|
|
488
|
+
const parsed = parseAnalyticsProvider(provider);
|
|
489
|
+
return parsed === null ? {
|
|
490
|
+
analytics: false,
|
|
491
|
+
analyticsQueries: false,
|
|
492
|
+
eventIngestion: false
|
|
493
|
+
} : {
|
|
494
|
+
analytics: true,
|
|
495
|
+
analyticsQueries: true,
|
|
496
|
+
eventIngestion: true,
|
|
497
|
+
maxMatchingRows: parsed.maxMatchingRows,
|
|
498
|
+
mode: "bounded"
|
|
499
|
+
};
|
|
500
|
+
};
|
|
501
|
+
var requireAnalyticsSupport = async (provider) => {
|
|
502
|
+
const parsedProvider = parseAnalyticsProvider(provider);
|
|
503
|
+
if (parsedProvider === null) throw new Error("Analytics are not supported by the configured database plugin.");
|
|
504
|
+
return parsedProvider;
|
|
505
|
+
};
|
|
506
|
+
async function getBundleEventSummary(provider, input) {
|
|
507
|
+
const { bundleId } = parseBundleEventSummaryInput(input);
|
|
508
|
+
return (await requireAnalyticsSupport(provider)).getBundleEventSummary(bundleId);
|
|
509
|
+
}
|
|
510
|
+
async function getActiveInstallationOverview(provider, input) {
|
|
511
|
+
const parsed = parseActiveInstallationInput(input);
|
|
512
|
+
return (await requireAnalyticsSupport(provider)).getActiveInstallationOverview(parsed);
|
|
513
|
+
}
|
|
514
|
+
async function getBundleEventAnalytics(provider, input) {
|
|
515
|
+
const parsed = parseBundleEventAnalyticsInput(input);
|
|
516
|
+
return (await requireAnalyticsSupport(provider)).getBundleEventAnalytics(parsed.bundleId, parsed.window, parsed.limit ?? 50, parsed.offset ?? 0);
|
|
517
|
+
}
|
|
518
|
+
async function searchInstallations(provider, input) {
|
|
519
|
+
const parsed = parseSearchInstallationsInput(input);
|
|
520
|
+
return (await requireAnalyticsSupport(provider)).searchInstallations(parsed.query, parsed.limit ?? 50, parsed.offset ?? 0);
|
|
521
|
+
}
|
|
522
|
+
async function getInstallationHistory(provider, input) {
|
|
523
|
+
const parsed = parseInstallationHistoryInput(input);
|
|
524
|
+
return (await requireAnalyticsSupport(provider)).getInstallationHistory(parsed.installId, parsed.limit ?? 50, parsed.offset ?? 0);
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
527
|
+
export { createApiKeyStore, createRuntimeHotUpdater, getActiveInstallationOverview, getAnalyticsCapability, getBundleEventAnalytics, getBundleEventSummary, getInstallationHistory, searchInstallations };
|