@hot-updater/console 1.0.0-rc.8 → 1.0.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/ConsoleFeatureUnavailable-jB4Loglh.js +1 -0
- package/.output/public/assets/EventDetails-B6yUsyNA.js +1 -0
- package/.output/public/assets/HashValueDisplay-BFX_kY3-.js +1 -0
- package/.output/public/assets/InsightsInfo-BNO0WznZ.js +1 -0
- package/.output/public/assets/InsightsPageHeader-B892oVml.js +1 -0
- package/.output/public/assets/NotFoundPage-DIqCpWa3.js +1 -0
- package/.output/public/assets/alert-dialog-Bbk_4-WJ.js +1 -0
- package/.output/public/assets/api-DCM0wr6O.js +1 -0
- package/.output/public/assets/api-keys-CIO9L_wu.js +1 -0
- package/.output/public/assets/api-keys-DVTL_--f.js +1 -0
- package/.output/public/assets/arrow-left-CnMYpqB7.js +1 -0
- package/.output/public/assets/arrow-up-right-BtffZA6e.js +1 -0
- package/.output/public/assets/chevron-right-BqD5wtFb.js +1 -0
- package/.output/public/assets/clipboard-DS9ucUQL.js +1 -0
- package/.output/public/assets/console-features-api-BH5m_PtQ.js +1 -0
- package/.output/public/assets/dialog-Bqbb7B0N.js +1 -0
- package/.output/public/assets/dist-LxKJB-Vu.js +1 -0
- package/.output/public/assets/empty-CuT2UhFp.js +1 -0
- package/.output/public/assets/field-BBDpjIqu.js +1 -0
- package/.output/public/assets/index-6o51GkFe.js +11 -0
- package/.output/public/assets/input-group-ja_30qaF.js +1 -0
- package/.output/public/assets/insights-CmraDzQh.js +1 -0
- package/.output/public/assets/insights-CxZ2GCYV.js +2 -0
- package/.output/public/assets/insights-api-BigAU9sZ.js +1 -0
- package/.output/public/assets/insights-usage-rpc-DYEhsCKT.js +1 -0
- package/.output/public/assets/insights-vNRCrfnS.js +42 -0
- package/.output/public/assets/insights_.distribution-ChOerfJt.js +1 -0
- package/.output/public/assets/insights_.distribution-D-e4iga8.js +1 -0
- package/.output/public/assets/insights_.distribution-DZtkibX1.js +2 -0
- package/.output/public/assets/installations-BazSJXE0.js +2 -0
- package/.output/public/assets/installations-ClHTqdAO.js +1 -0
- package/.output/public/assets/installations-DpKsBuHi.js +1 -0
- package/.output/public/assets/key-round-wS_SIZ6b.js +1 -0
- package/.output/public/assets/list-CgrYiadM.js +1 -0
- package/.output/public/assets/refresh-cw-BYgoNCco.js +1 -0
- package/.output/public/assets/remote-config-BWwHOkCK.js +1 -0
- package/.output/public/assets/remote-config-Br56BODb.js +6 -0
- package/.output/public/assets/remote-config-CXOi0LRu.js +1 -0
- package/.output/public/assets/routes-B88Erkpk.js +2 -0
- package/.output/public/assets/routes-CfzsAleC.js +1 -0
- package/.output/public/assets/search-DaTDPW25.js +1 -0
- package/.output/public/assets/select-BZZBXMkr.js +1 -0
- package/.output/public/assets/sidebar-DNVyJPfX.js +3 -0
- package/.output/public/assets/smartphone-DpTFgtKf.js +1 -0
- package/.output/public/assets/styles-DTGEAJFv.css +2 -0
- package/.output/public/assets/switch-DZ1TPwx-.js +1 -0
- package/.output/public/assets/table-Gm4qRWwE.js +1 -0
- package/.output/public/assets/tabs-C7FOEjFC.js +1 -0
- package/.output/public/assets/undo-2-CmrHjwFc.js +1 -0
- package/.output/public/assets/useCompositeListItem-9E4pj_CH.js +1 -0
- package/.output/public/assets/valueToPercent-CH8GCHaL.js +1 -0
- package/.output/server/__23tanstack-start-server-fn-resolver-BpRlos5Q.mjs +170 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +4389 -4207
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +3802 -4022
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +140 -97
- package/.output/server/_libs/lucide-react.mjs +221 -9
- package/.output/server/_libs/next-themes.mjs +1 -1
- package/.output/server/_libs/react-redux+[...].mjs +1 -1
- package/.output/server/_libs/recharts+[...].mjs +483 -901
- package/.output/server/_libs/sonner.mjs +1 -1
- package/.output/server/_libs/tanstack__history.mjs +68 -41
- package/.output/server/_libs/tanstack__query-core.mjs +2010 -309
- package/.output/server/_libs/tanstack__react-query.mjs +448 -21
- package/.output/server/_libs/verkit.mjs +102 -1
- package/.output/server/_ssr/BundleIdDisplay-x94o9rlU.mjs +13 -0
- package/.output/server/_ssr/ConsoleFeatureUnavailable-ldW8v8-q.mjs +58 -0
- package/.output/server/_ssr/EventDetails-Q8kfj9Nr.mjs +344 -0
- package/.output/server/_ssr/{HashValueDisplay-C5tS4oGo.mjs → HashValueDisplay-n1GholGS.mjs} +3 -3
- package/.output/server/_ssr/InsightsInfo-DuJFatQO.mjs +75 -0
- package/.output/server/_ssr/InsightsPageHeader-BSUX3Ye9.mjs +145 -0
- package/.output/server/_ssr/NotFoundPage-DZzOOzS3.mjs +37 -0
- package/.output/server/_ssr/{dialog-WiIAKl4L.mjs → alert-dialog-C43Doa0I.mjs} +6 -83
- package/.output/server/_ssr/{api-keys-aXriWrv7.mjs → api-keys-B4sYm8dM.mjs} +48 -9
- package/.output/server/_ssr/api-keys-CEnM9fqG.mjs +5 -0
- package/.output/server/_ssr/api-keys-rpc-BIFuUgMt.mjs +52 -0
- package/.output/server/_ssr/api-oZjRn9tP.mjs +186 -0
- package/.output/server/_ssr/api-rpc-BNiFRRK8.mjs +240 -0
- package/.output/server/_ssr/{auth-BF8mbAgF.mjs → auth-UP0wXs4i.mjs} +1 -1
- package/.output/server/_ssr/{auth-rpc-B7w3AdRr.mjs → auth-rpc-DnZqYxyq.mjs} +6 -6
- package/.output/server/_ssr/auth.server-DFpyxRDd.mjs +5374 -0
- package/.output/server/_ssr/bundleActivity-M5gzBFNg.mjs +18 -0
- package/.output/server/_ssr/config-WVX4IxSR.mjs +15 -0
- package/.output/server/_ssr/config.server-DskXCAyI.mjs +35 -0
- package/.output/server/_ssr/console-access-BJfAXknd.mjs +17 -0
- package/.output/server/_ssr/console-features-Oz0q20jJ.mjs +61 -0
- package/.output/server/_ssr/console-features-api-C9kiKvaV.mjs +43 -0
- package/.output/server/_ssr/console-features-rpc-P0GL7Z-R.mjs +19 -0
- package/.output/server/_ssr/{createServerFn-B2hcXoPM.mjs → createMiddleware-Cm6Tn0_Y.mjs} +67 -56
- package/.output/server/_ssr/{createServerRpc-DB29C_5p.mjs → createServerRpc-RLR4H-Fn.mjs} +2 -2
- package/.output/server/_ssr/databaseConfig-C5SkAyUy.mjs +5 -0
- package/.output/server/_ssr/{databaseJsonValue-d6I3nAS4.mjs → databaseJsonValue-Csbuzc9Q.mjs} +28 -5
- package/.output/server/_ssr/databaseRows-CPphvbag.mjs +73 -0
- package/.output/server/_ssr/dialog-BbYDMjdC.mjs +84 -0
- package/.output/server/_ssr/{dist-CyzPaI5v.mjs → dist-CbQbBIw-.mjs} +140 -7
- package/.output/server/_ssr/{downloadBundle-CI7JUKvC.mjs → downloadBundle-CZysjmNW.mjs} +10 -10
- package/.output/server/_ssr/empty-CVLGfxuu.mjs +60 -0
- package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CPtwX8a7.mjs} +1 -1
- package/.output/server/_ssr/failureReports-DKDhVsXA.mjs +33 -0
- package/.output/server/_ssr/field-asw83X0v.mjs +189 -0
- package/.output/server/_ssr/getBundleChildren-Cv9iz1As.mjs +22 -0
- package/.output/server/_ssr/{input-group-Coj6SwVP.mjs → input-group-CuYJHosb.mjs} +2 -2
- package/.output/server/_ssr/insights-B55FG5fm.mjs +14 -0
- package/.output/server/_ssr/insights-CWnHIFUV.mjs +5 -0
- package/.output/server/_ssr/insights-CpLpedFZ.mjs +2315 -0
- package/.output/server/_ssr/insights-api-sjws23WF.mjs +122 -0
- package/.output/server/_ssr/insights-errors-BjnFUyZj.mjs +34 -0
- package/.output/server/_ssr/insights-failures-rpc-BvbWvqNf.mjs +36 -0
- package/.output/server/_ssr/insights-failures-xaa41Buf.mjs +46 -0
- package/.output/server/_ssr/insights-recovery-BWQhiZaF.mjs +28 -0
- package/.output/server/_ssr/insights-recovery-rpc-GiYXdkaY.mjs +32 -0
- package/.output/server/_ssr/{insights-rpc-l2-oBYth.mjs → insights-rpc-DP_8Rmz4.mjs} +22 -14
- package/.output/server/_ssr/insights-usage-BpBM0U0c.mjs +29 -0
- package/.output/server/_ssr/insights-usage-rpc-BiolgB8e.mjs +29 -0
- package/.output/server/_ssr/insights-usage-rpc-KStL0zOx.mjs +41 -0
- package/.output/server/_ssr/insightsUsage-CzhVjsc8.mjs +56 -0
- package/.output/server/_ssr/insights_.distribution-CVrnbP3R.mjs +7 -0
- package/.output/server/_ssr/insights_.distribution-D2HaNTAA.mjs +16 -0
- package/.output/server/_ssr/{insights_.distribution-INw6QxbU.mjs → insights_.distribution-DN9pR422.mjs} +50 -22
- package/.output/server/_ssr/installations-BHmbbnH8.mjs +5 -0
- package/.output/server/_ssr/{installations-DH-tQuRo.mjs → installations-BSCmFq8Q.mjs} +279 -367
- package/.output/server/_ssr/installations-DD1g__Nc.mjs +69 -0
- package/.output/server/_ssr/listReleases-BfUBlCHo.mjs +79 -0
- package/.output/server/_ssr/plugins-BaD1slHx.mjs +2086 -0
- package/.output/server/_ssr/release-adoption-005tQRpo.mjs +55 -0
- package/.output/server/_ssr/release-adoption-rpc-BsstqxUp.mjs +41 -0
- package/.output/server/_ssr/releaseAdoption-CtOFkwli.mjs +114 -0
- package/.output/server/_ssr/releaseManagement-DFMUnDF5.mjs +11 -0
- package/.output/server/_ssr/remote-config-C2Ee8lXO.mjs +5 -0
- package/.output/server/_ssr/remote-config-Dv1ccMo-.mjs +17 -0
- package/.output/server/_ssr/remote-config-of0lGQH8.mjs +2648 -0
- package/.output/server/_ssr/remote-config-rpc-BzX31mUS.mjs +115 -0
- package/.output/server/_ssr/rolldown-runtime-rSIU-vHC.mjs +13 -0
- package/.output/server/_ssr/{router-DfFyAp5p.mjs → router-D8NuoJ_I.mjs} +117 -136
- package/.output/server/_ssr/{routes-BtsfVx-p.mjs → routes-DgvnFqDy.mjs} +195 -429
- package/.output/server/_ssr/routes-V8sDybGf.mjs +409 -0
- package/.output/server/_ssr/runtime.server-De0QVWc9.mjs +104 -0
- package/.output/server/_ssr/schema-BiAVyEj2.mjs +361 -0
- package/.output/server/_ssr/{select-CyvLaMza.mjs → select-t3iXRwc9.mjs} +3 -3
- package/.output/server/_ssr/server-h4VxnExB.mjs +8325 -0
- package/.output/server/_ssr/{createSsrRpc-C-dLcQuy.mjs → sidebar-DJcErBMo.mjs} +107 -47
- package/.output/server/_ssr/ssr.mjs +1 -1
- package/.output/server/_ssr/start-BfFbHKMK.mjs +4 -0
- package/.output/server/_ssr/switch-E-btwMGt.mjs +43 -0
- package/.output/server/_ssr/{table-CkVhsNpe.mjs → table-37Dd23TN.mjs} +2 -2
- package/.output/server/_ssr/tabs-DEFnTm95.mjs +45 -0
- package/.output/server/_ssr/updateFailures-H6pnnYK4.mjs +40 -0
- package/.output/server/_ssr/{uuidv7-D0QFkPL_.mjs → uuidv7-C88s9jWF.mjs} +2 -2
- package/.output/server/_tanstack-start-manifest_v-55AGsehh.mjs +139 -0
- package/.output/server/index.mjs +303 -205
- package/LICENSE +13 -14
- package/README.md +80 -71
- package/lib/index.d.mts +23 -11
- package/lib/vite-internal.mjs +13 -3
- package/lib/vite.d.mts +2 -0
- package/package.json +14 -14
- package/src/components/AppSidebar.tsx +74 -75
- package/src/components/ConsoleAccessPage.tsx +5 -1
- package/src/components/ConsoleFeatureUnavailable.tsx +80 -0
- package/src/components/NotFoundPage.tsx +7 -7
- package/src/components/features/FeatureSlots.tsx +103 -0
- package/src/components/features/api-keys/apiKeysFeatureUi.tsx +15 -0
- package/src/components/features/bundles/BundleMetadata.tsx +8 -5
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
- package/src/components/features/channels/ChannelManagementDialog.tsx +8 -6
- package/src/components/features/insights/AppUsage.tsx +41 -14
- package/src/components/features/insights/BuiltinBundle.tsx +36 -0
- package/src/components/features/insights/ComparedBundlesChart.tsx +310 -0
- package/src/components/features/insights/EstimatedCount.tsx +18 -0
- package/src/components/features/insights/EventDetails.tsx +390 -26
- package/src/components/features/insights/EventHistoryCard.tsx +111 -25
- package/src/components/features/insights/EventHistoryList.tsx +1 -1
- package/src/components/features/insights/FailureReports.tsx +407 -0
- package/src/components/features/insights/InsightsControls.tsx +240 -142
- package/src/components/features/insights/InsightsErrorAlert.tsx +14 -1
- package/src/components/features/insights/InsightsOverview.tsx +601 -403
- package/src/components/features/insights/InsightsPageHeader.tsx +19 -14
- package/src/components/features/insights/InsightsPeriodSelector.tsx +67 -12
- package/src/components/features/insights/InstallationHistoryCard.tsx +65 -10
- package/src/components/features/insights/InstallationMatchesCard.tsx +1 -1
- package/src/components/features/insights/ReleaseActivity.tsx +308 -0
- package/src/components/features/insights/ReportingDevicesSummary.tsx +13 -13
- package/src/components/features/insights/RollbackButton.tsx +118 -0
- package/src/components/features/insights/UpdateFailures.tsx +382 -0
- package/src/components/features/insights/insightsFeatureUi.tsx +35 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +27 -13
- package/src/components/features/remote-config/ConditionDialog.tsx +592 -0
- package/src/components/features/remote-config/ConditionsCard.tsx +247 -0
- package/src/components/features/remote-config/ParameterDialog.tsx +426 -0
- package/src/components/features/remote-config/ParametersCard.tsx +254 -0
- package/src/components/features/remote-config/PreviewCard.tsx +284 -0
- package/src/components/features/remote-config/PublishDialog.tsx +325 -0
- package/src/components/features/remote-config/RemoteConfigPage.tsx +353 -0
- package/src/components/features/remote-config/RuleSummary.tsx +21 -0
- package/src/components/features/remote-config/ValueInput.tsx +151 -0
- package/src/components/features/remote-config/VersionsCard.tsx +384 -0
- package/src/components/features/remote-config/remoteConfigFeatureUi.tsx +15 -0
- package/src/components/features/remote-config/useDateTimeText.ts +16 -0
- package/src/components/ui/sidebar.tsx +22 -29
- package/src/index.ts +26 -19
- package/src/lib/api-keys-api.ts +0 -28
- package/src/lib/api-keys-rpc.ts +27 -38
- package/src/lib/api-rpc.ts +125 -288
- package/src/lib/api.ts +15 -134
- package/src/lib/bundle-activity.ts +24 -4
- package/src/lib/console-access.ts +20 -0
- package/src/lib/console-features-api.ts +68 -0
- package/src/lib/console-features-rpc.ts +13 -0
- package/src/lib/console-features.ts +98 -0
- package/src/lib/insights-api.ts +56 -2
- package/src/lib/insights-errors.ts +64 -0
- package/src/lib/insights-failures-rpc.ts +36 -0
- package/src/lib/insights-failures.ts +130 -0
- package/src/lib/insights-recovery-rpc.ts +13 -17
- package/src/lib/insights-recovery.ts +12 -36
- package/src/lib/insights-retention.ts +21 -0
- package/src/lib/insights-rpc.ts +30 -12
- package/src/lib/insights-search.ts +50 -4
- package/src/lib/insights-usage-rpc.ts +15 -9
- package/src/lib/insights-usage.ts +41 -3
- package/src/lib/insights-view.ts +34 -5
- package/src/lib/release-adoption-api.ts +214 -0
- package/src/lib/release-adoption-rpc.ts +49 -0
- package/src/lib/release-adoption.ts +164 -0
- package/src/lib/remote-config-api.ts +119 -0
- package/src/lib/remote-config-draft.ts +666 -0
- package/src/lib/remote-config-rpc.ts +196 -0
- package/src/lib/scroll-restoration.ts +21 -0
- package/src/lib/server/auth.server.ts +13 -5
- package/src/lib/server/bundleActivity.ts +27 -28
- package/src/lib/server/config.server.ts +21 -76
- package/src/lib/server/console-feature-apis.ts +70 -0
- package/src/lib/server/console-runtime.server.ts +82 -38
- package/src/lib/server/downloadBundle.ts +14 -15
- package/src/lib/server/failureReports.ts +50 -0
- package/src/lib/server/getBundleChildren.ts +31 -122
- package/src/lib/server/insightsUsage.ts +52 -113
- package/src/lib/server/listReleases.ts +109 -44
- package/src/lib/server/releaseAdoption.ts +151 -0
- package/src/lib/server/releaseReachability.ts +8 -8
- package/src/lib/server/remoteConfig.ts +75 -0
- package/src/lib/server/runtime.server.ts +120 -31
- package/src/lib/server/updateFailures.ts +66 -0
- package/src/lib/use-dialog-target.ts +27 -0
- package/src/routeTree.gen.ts +44 -44
- package/src/router.tsx +3 -2
- package/src/routes/-installations-search.ts +32 -8
- package/src/routes/-releases-search.ts +76 -29
- package/src/routes/__root.tsx +7 -1
- package/src/routes/api/bundles/$bundleId/download.ts +2 -5
- package/src/routes/api-keys.tsx +5 -2
- package/src/routes/index.tsx +210 -152
- package/src/routes/insights.tsx +91 -23
- package/src/routes/insights_.distribution.tsx +50 -9
- package/src/routes/installations.tsx +97 -40
- package/src/routes/remote-config.tsx +42 -0
- package/src/styles.css +9 -0
- package/src/vite-internal.ts +15 -3
- package/src/vite.ts +2 -0
- package/.output/public/assets/HashValueDisplay-BpTc3Oyg.js +0 -1
- package/.output/public/assets/InsightsPageHeader-DC2eEst8.js +0 -1
- package/.output/public/assets/api-CdUyUqzR.js +0 -42
- package/.output/public/assets/api-keys-64PM9PwW.js +0 -1
- package/.output/public/assets/api-keys-api-BeaJnvY5.js +0 -1
- package/.output/public/assets/arrow-left-04Rh8Zgp.js +0 -1
- package/.output/public/assets/card-CYHs5FZv.js +0 -1
- package/.output/public/assets/chart-no-axes-combined-DwyMsVFW.js +0 -1
- package/.output/public/assets/createServerFn-DU71xucx.js +0 -3
- package/.output/public/assets/dialog-IVMkBRCq.js +0 -1
- package/.output/public/assets/dist-BR4GzeEu.js +0 -1
- package/.output/public/assets/field-B3CYDpjg.js +0 -1
- package/.output/public/assets/index-C39o7rJH.js +0 -10
- package/.output/public/assets/info-C3y9HNoU.js +0 -1
- package/.output/public/assets/input-group-nLZylhWF.js +0 -1
- package/.output/public/assets/insights-Bm9v8uO3.js +0 -2
- package/.output/public/assets/insights-DS3iO7Up.js +0 -1
- package/.output/public/assets/insights-search-Byj3g3Mp.js +0 -1
- package/.output/public/assets/insights-usage-rpc-BaGR3lgq.js +0 -1
- package/.output/public/assets/insights_.distribution-B1i6cjlQ.js +0 -1
- package/.output/public/assets/insights_.distribution-C1Wf27eB.js +0 -2
- package/.output/public/assets/installations-CkFW4fV9.js +0 -2
- package/.output/public/assets/installations-DxTaIcHD.js +0 -1
- package/.output/public/assets/lazyRouteComponent-BapzjYnb.js +0 -1
- package/.output/public/assets/list-Dqt53fWZ.js +0 -1
- package/.output/public/assets/loader-circle-CUKfUqow.js +0 -1
- package/.output/public/assets/preload-helper-ClBMtM1g.js +0 -1
- package/.output/public/assets/refresh-cw-CNE_2PnD.js +0 -1
- package/.output/public/assets/routes-Cgtvspjg.js +0 -2
- package/.output/public/assets/routes-O2r2UUN8.js +0 -1
- package/.output/public/assets/shield-check-BFcJ46kz.js +0 -1
- package/.output/public/assets/signing-BgspC-xP.js +0 -1
- package/.output/public/assets/styles-DnrWuBwd.css +0 -2
- package/.output/public/assets/table-BuWQgxJJ.js +0 -1
- package/.output/public/assets/useMutation-CSV9NCUl.js +0 -1
- package/.output/public/assets/useOpenInteractionType-D5kduCTD.js +0 -1
- package/.output/public/assets/valueToPercent-BAtcbSLk.js +0 -1
- package/.output/server/__23tanstack-start-server-fn-resolver-CmYkol9D.mjs +0 -150
- package/.output/server/_ssr/InsightsPageHeader-Dma3dVj1.mjs +0 -64
- package/.output/server/_ssr/api-C3bGmgY0.mjs +0 -332
- package/.output/server/_ssr/api-keys-api-BRF6rbRC.mjs +0 -60
- package/.output/server/_ssr/api-keys-rpc-BMa1ZM6N.mjs +0 -136
- package/.output/server/_ssr/api-rpc-BYQ2e0z6.mjs +0 -1399
- package/.output/server/_ssr/auth.server-CmicHh0J.mjs +0 -47
- package/.output/server/_ssr/bundle-signing-rpc-DbQ4pFAX.mjs +0 -16
- package/.output/server/_ssr/bundle-signing.server-Uqt1OF49.mjs +0 -12
- package/.output/server/_ssr/bundleActivity-pBrPSG01.mjs +0 -28
- package/.output/server/_ssr/card-B7KSO_25.mjs +0 -108
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +0 -13
- package/.output/server/_ssr/config.server-C-d1QaBU.mjs +0 -368
- package/.output/server/_ssr/databaseRows-5bCRTxm-.mjs +0 -126
- package/.output/server/_ssr/deleteBundle-8X-jq7Z7.mjs +0 -66
- package/.output/server/_ssr/field-BDwuU8RE.mjs +0 -74
- package/.output/server/_ssr/getBundleChildren-DoL8l_AS.mjs +0 -50
- package/.output/server/_ssr/insights-BDJjaDLp.mjs +0 -938
- package/.output/server/_ssr/insights-D0FWpfOS.mjs +0 -10
- package/.output/server/_ssr/insights-recovery-Cv6RSyeR.mjs +0 -21
- package/.output/server/_ssr/insights-recovery-rpc-BE9YsK_T.mjs +0 -37
- package/.output/server/_ssr/insights-search-1czJMZgk.mjs +0 -21
- package/.output/server/_ssr/insights-usage-rpc-BE_aCnea.mjs +0 -184
- package/.output/server/_ssr/insights-usage-rpc-CtAolz-t.mjs +0 -24
- package/.output/server/_ssr/insightsContract-B09K_Px1.mjs +0 -303
- package/.output/server/_ssr/insightsHistory-Ci4rYAgl.mjs +0 -41
- package/.output/server/_ssr/insightsRecovery-CaghWL2t.mjs +0 -113
- package/.output/server/_ssr/insightsUsage-DNIj6jxF.mjs +0 -71
- package/.output/server/_ssr/insights_.distribution-Cp0kf1vW.mjs +0 -10
- package/.output/server/_ssr/installations-Bhyiwx8G.mjs +0 -29
- package/.output/server/_ssr/routes-xBkI2Fmt.mjs +0 -37
- package/.output/server/_ssr/runtime.server-CsiKFp_Z.mjs +0 -436
- package/.output/server/_ssr/server-BdkuOWe8.mjs +0 -7468
- package/.output/server/_ssr/signing-CQ70saFm.mjs +0 -133
- package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-CrBsoKQQ.mjs +0 -125
- package/src/components/features/bundles/BundleActivityChart.tsx +0 -97
- package/src/components/features/bundles/BundleInsightsSummary.tsx +0 -126
- package/src/components/features/signing/BundleSigningPage.tsx +0 -139
- package/src/lib/bundle-signing-api.ts +0 -14
- package/src/lib/bundle-signing-rpc.ts +0 -16
- package/src/lib/server/bundle-signing.server.ts +0 -23
- package/src/lib/server/deleteBundle.ts +0 -145
- package/src/lib/server/insightsHistory.ts +0 -52
- package/src/lib/server/insightsRecovery.ts +0 -187
- package/src/routes/signing.tsx +0 -37
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
HotUpdaterCoreApi,
|
|
3
|
+
StorageAdapter,
|
|
4
4
|
} from "@hot-updater/plugin-core";
|
|
5
5
|
|
|
6
6
|
interface DownloadBundleDependencies {
|
|
7
|
-
readonly
|
|
8
|
-
|
|
7
|
+
readonly core: Pick<HotUpdaterCoreApi, "getBundle">;
|
|
8
|
+
/** The server's storage, which reads the manifest of its protocol. */
|
|
9
|
+
readonly storage: StorageAdapter;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const downloadBundle = async (
|
|
12
13
|
bundleId: string,
|
|
13
|
-
{
|
|
14
|
+
{ core, storage }: DownloadBundleDependencies,
|
|
14
15
|
): Promise<Response> => {
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
16
|
+
const detail = await core.getBundle(bundleId);
|
|
17
|
+
if (!detail) return new Response("Bundle not found", { status: 404 });
|
|
17
18
|
|
|
18
|
-
const storageUri = bundle.
|
|
19
|
-
if (!storageUri) {
|
|
20
|
-
return new Response("Bundle has no storage URI", { status: 404 });
|
|
21
|
-
}
|
|
19
|
+
const storageUri = detail.bundle.manifest_storage_uri;
|
|
22
20
|
|
|
23
21
|
const protocol = new URL(storageUri).protocol.replace(":", "");
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const storageAdapter = storage.protocol === protocol ? storage : undefined;
|
|
23
|
+
if (storageAdapter?.get !== undefined) {
|
|
24
|
+
const { response } = await storageAdapter.get({ storageUri });
|
|
26
25
|
if (!response)
|
|
27
26
|
return new Response("Storage object not found", { status: 404 });
|
|
28
27
|
|
|
29
28
|
const headers = new Headers(response.headers);
|
|
30
29
|
headers.set("cache-control", "private, no-store");
|
|
31
|
-
headers.set("content-disposition",
|
|
30
|
+
headers.set("content-disposition", 'attachment; filename="manifest.json"');
|
|
32
31
|
return new Response(response.body, {
|
|
33
32
|
headers,
|
|
34
33
|
status: response.status,
|
|
@@ -37,7 +36,7 @@ export const downloadBundle = async (
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
if (protocol !== "http" && protocol !== "https") {
|
|
40
|
-
return new Response(`No storage
|
|
39
|
+
return new Response(`No storage adapter for protocol: ${protocol}`, {
|
|
41
40
|
status: 503,
|
|
42
41
|
});
|
|
43
42
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { InsightsReads } from "@hot-updater/server/plugins";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
FailureReportsInput,
|
|
5
|
+
FailureReportsPage,
|
|
6
|
+
} from "../insights-errors";
|
|
7
|
+
import { readFailureReportsInput } from "../insights-errors";
|
|
8
|
+
import { recoveryWindows } from "../insights-recovery";
|
|
9
|
+
import type { InsightsEventRow } from "../insights-view";
|
|
10
|
+
|
|
11
|
+
/** Bounded raw-event reads, with continuation even when a batch finds no failures. */
|
|
12
|
+
export async function listFailureReports(
|
|
13
|
+
reads: Pick<InsightsReads, "listEvents" | "getRetention">,
|
|
14
|
+
input: FailureReportsInput,
|
|
15
|
+
): Promise<FailureReportsPage> {
|
|
16
|
+
readFailureReportsInput(input);
|
|
17
|
+
const { rawDays } = await reads.getRetention();
|
|
18
|
+
const duration = Math.min(
|
|
19
|
+
90 * 86_400_000,
|
|
20
|
+
rawDays * 86_400_000,
|
|
21
|
+
input.window ? recoveryWindows[input.window].durationMs : Infinity,
|
|
22
|
+
);
|
|
23
|
+
const sinceMs = Math.max(0, input.beforeReceivedAtMs - duration);
|
|
24
|
+
const data: InsightsEventRow[] = [];
|
|
25
|
+
let cursor = input.cursor;
|
|
26
|
+
let scanned = 0;
|
|
27
|
+
for (let batch = 0; batch < 5; batch += 1) {
|
|
28
|
+
const page = await reads.listEvents({
|
|
29
|
+
sinceMs,
|
|
30
|
+
beforeReceivedAtMs: input.beforeReceivedAtMs,
|
|
31
|
+
limit: 100,
|
|
32
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
33
|
+
});
|
|
34
|
+
scanned += page.data.length;
|
|
35
|
+
data.push(
|
|
36
|
+
...page.data.filter(
|
|
37
|
+
(event) =>
|
|
38
|
+
event.type === "UPDATE_FAILED" &&
|
|
39
|
+
event.platform === input.platform &&
|
|
40
|
+
event.channel === input.channel &&
|
|
41
|
+
(input.releaseId === undefined ||
|
|
42
|
+
(event.failure?.stage !== "check" &&
|
|
43
|
+
event.toReleaseId === input.releaseId)),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
cursor = page.nextCursor ?? undefined;
|
|
47
|
+
if (!cursor || data.length >= 50) break;
|
|
48
|
+
}
|
|
49
|
+
return { data, scanned, sinceMs, nextCursor: cursor ?? null };
|
|
50
|
+
}
|
|
@@ -1,130 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type CursorPaginationInfo = {
|
|
15
|
-
hasNextPage: boolean;
|
|
16
|
-
nextCursor?: string | null;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
async function collectBundleChildrenByBaseIds(
|
|
20
|
-
baseBundleIds: string[],
|
|
21
|
-
deps: GetBundleChildrenDeps,
|
|
22
|
-
): Promise<Record<string, Bundle[]>> {
|
|
23
|
-
const uniqueBaseBundleIds = [...new Set(baseBundleIds.filter(Boolean))];
|
|
24
|
-
const bundlesByBaseId = Object.fromEntries(
|
|
25
|
-
uniqueBaseBundleIds.map((bundleId) => [bundleId, [] as Bundle[]]),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
const baseBundles = (
|
|
29
|
-
await Promise.all(
|
|
30
|
-
uniqueBaseBundleIds.map((bundleId) =>
|
|
31
|
-
deps.databaseClient.getBundleById(bundleId),
|
|
32
|
-
),
|
|
33
|
-
)
|
|
34
|
-
).filter((bundle): bundle is Bundle => Boolean(bundle));
|
|
35
|
-
|
|
36
|
-
const groupMap = new Map<Bundle["platform"], Set<string>>();
|
|
37
|
-
|
|
38
|
-
for (const baseBundle of baseBundles) {
|
|
39
|
-
const existingGroup = groupMap.get(baseBundle.platform);
|
|
40
|
-
|
|
41
|
-
if (existingGroup) {
|
|
42
|
-
existingGroup.add(baseBundle.id);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
groupMap.set(baseBundle.platform, new Set([baseBundle.id]));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const [platform, bundleIds] of groupMap) {
|
|
50
|
-
const seenBundleIds = new Set<string>();
|
|
51
|
-
const seenCursors = new Set<string>();
|
|
52
|
-
let after: string | undefined;
|
|
53
|
-
|
|
54
|
-
while (true) {
|
|
55
|
-
const page = await deps.databaseClient.getBundles({
|
|
56
|
-
where: {
|
|
57
|
-
platform,
|
|
58
|
-
},
|
|
59
|
-
limit: CHILDREN_QUERY_LIMIT,
|
|
60
|
-
cursor: after ? { after } : undefined,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
for (const bundle of page.data) {
|
|
64
|
-
const parentBundleIds = getBundlePatches(bundle).map(
|
|
65
|
-
(patch) => patch.baseBundleId,
|
|
66
|
-
);
|
|
67
|
-
const matchedParentBundleIds = parentBundleIds.filter((bundleId) =>
|
|
68
|
-
bundleIds.has(bundleId),
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
matchedParentBundleIds.length === 0 ||
|
|
73
|
-
seenBundleIds.has(bundle.id)
|
|
74
|
-
) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
seenBundleIds.add(bundle.id);
|
|
79
|
-
for (const parentBundleId of matchedParentBundleIds) {
|
|
80
|
-
bundlesByBaseId[parentBundleId]?.push(bundle);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const pagination = page.pagination as CursorPaginationInfo;
|
|
85
|
-
const nextCursor = pagination.nextCursor ?? undefined;
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
!pagination.hasNextPage ||
|
|
89
|
-
!nextCursor ||
|
|
90
|
-
seenCursors.has(nextCursor)
|
|
91
|
-
) {
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
seenCursors.add(nextCursor);
|
|
96
|
-
after = nextCursor;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return bundlesByBaseId;
|
|
101
|
-
}
|
|
102
|
-
|
|
1
|
+
import {
|
|
2
|
+
type Bundle,
|
|
3
|
+
type HotUpdaterCoreApi,
|
|
4
|
+
rowToBundle,
|
|
5
|
+
} from "@hot-updater/plugin-core";
|
|
6
|
+
|
|
7
|
+
/** The most children the lineage panel lists; the count comes from the base bundle's counter. */
|
|
8
|
+
export const BUNDLE_CHILDREN_LIMIT = 100;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The bundles with a patch from this base, newest first: one read of the
|
|
12
|
+
* patches index, then each child bundle.
|
|
13
|
+
*/
|
|
103
14
|
export async function getBundleChildren(
|
|
104
|
-
|
|
105
|
-
|
|
15
|
+
core: Pick<HotUpdaterCoreApi, "getBundle" | "listPatchesFromBase">,
|
|
16
|
+
baseBundleId: string,
|
|
106
17
|
): Promise<Bundle[]> {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
18
|
+
const patches = await core.listPatchesFromBase(baseBundleId, {
|
|
19
|
+
order: "desc",
|
|
20
|
+
limit: BUNDLE_CHILDREN_LIMIT,
|
|
21
|
+
});
|
|
22
|
+
const details = await Promise.all(
|
|
23
|
+
[...new Set(patches.map(({ bundle_id }) => bundle_id))].map((id) =>
|
|
24
|
+
core.getBundle(id),
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
return details.flatMap((detail) =>
|
|
28
|
+
detail === null ? [] : [rowToBundle(detail.bundle, detail.patches)],
|
|
110
29
|
);
|
|
111
|
-
|
|
112
|
-
return childrenByBaseId[baseBundleId] ?? [];
|
|
113
30
|
}
|
|
114
31
|
|
|
32
|
+
/** How many bundles patch from each base: each base bundle's reference counter, in one batch read. */
|
|
115
33
|
export async function getBundleChildCounts(
|
|
116
|
-
|
|
117
|
-
|
|
34
|
+
core: Pick<HotUpdaterCoreApi, "countBundleChildren">,
|
|
35
|
+
baseBundleIds: readonly string[],
|
|
118
36
|
): Promise<Record<string, number>> {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
deps,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
return Object.fromEntries(
|
|
125
|
-
Object.entries(childrenByBaseId).map(([bundleId, bundles]) => [
|
|
126
|
-
bundleId,
|
|
127
|
-
bundles.length,
|
|
128
|
-
]),
|
|
129
|
-
);
|
|
37
|
+
const ids = [...new Set(baseBundleIds.filter(Boolean))];
|
|
38
|
+
return ids.length === 0 ? {} : core.countBundleChildren(ids);
|
|
130
39
|
}
|
|
@@ -1,124 +1,63 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InsightsModel } from "@hot-updater/server/plugins";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { insightsPeriodEnd, recoveryWindows } from "../insights-recovery";
|
|
4
|
+
import {
|
|
5
|
+
readAppUsageInput,
|
|
6
|
+
type AppUsageInput,
|
|
7
|
+
type AppUsageReport,
|
|
8
|
+
type UsageWindow,
|
|
9
9
|
} from "../insights-usage";
|
|
10
|
-
import { readInsightsHistory } from "./insightsHistory";
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
input: AppUsageInput,
|
|
15
|
-
beforeReceivedAtMs = Date.now(),
|
|
16
|
-
): Promise<AppUsageReport> {
|
|
17
|
-
readRecoveryInput(input);
|
|
18
|
-
const history = await readInsightsHistory(
|
|
19
|
-
model,
|
|
20
|
-
input.window,
|
|
21
|
-
beforeReceivedAtMs,
|
|
22
|
-
);
|
|
23
|
-
const { intervalMs, durationMs } = recoveryWindows[input.window];
|
|
24
|
-
const startMs = Math.max(0, beforeReceivedAtMs - durationMs);
|
|
25
|
-
const bucketStart = (ms: number) => Math.floor(ms / intervalMs) * intervalMs;
|
|
26
|
-
const latest = new Map<string, BundleEventRow>();
|
|
27
|
-
const observed = new Map<string, BundleEventRow>();
|
|
28
|
-
const buckets = new Map<number, Set<string>>();
|
|
29
|
-
const appVersions = new Set<string>();
|
|
11
|
+
const DAY_MS = 86_400_000;
|
|
12
|
+
const WEEK_MS = 7 * DAY_MS;
|
|
30
13
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
14
|
+
/**
|
|
15
|
+
* What a period reads, ending with the current hour. The 12-month period is
|
|
16
|
+
* 52 weeks of whole UTC days, the last one today, so it reads the daily
|
|
17
|
+
* counts: the hourly ones are kept 90 days.
|
|
18
|
+
*/
|
|
19
|
+
const usageRange = (window: UsageWindow, now: number) => {
|
|
20
|
+
const end = insightsPeriodEnd(now);
|
|
21
|
+
if (window === "12m") {
|
|
22
|
+
const lastDay = Math.ceil(end / DAY_MS) * DAY_MS;
|
|
23
|
+
return {
|
|
24
|
+
start: Math.max(0, lastDay - 52 * WEEK_MS),
|
|
25
|
+
end,
|
|
26
|
+
intervalMs: WEEK_MS,
|
|
43
27
|
};
|
|
44
|
-
observed.set(row.install_id, row);
|
|
45
|
-
if (
|
|
46
|
-
row.channel !== input.channel ||
|
|
47
|
-
(input.platform !== "all" && row.platform !== input.platform)
|
|
48
|
-
)
|
|
49
|
-
continue;
|
|
50
|
-
appVersions.add(row.app_version);
|
|
51
|
-
if (input.appVersion !== undefined && row.app_version !== input.appVersion)
|
|
52
|
-
continue;
|
|
53
|
-
latest.set(row.install_id, row);
|
|
54
|
-
const start = bucketStart(row.received_at_ms);
|
|
55
|
-
const installations = buckets.get(start) ?? new Set<string>();
|
|
56
|
-
installations.add(row.install_id);
|
|
57
|
-
buckets.set(start, installations);
|
|
58
28
|
}
|
|
29
|
+
const { durationMs, intervalMs } = recoveryWindows[window];
|
|
30
|
+
return { start: Math.max(0, end - durationMs), end, intervalMs };
|
|
31
|
+
};
|
|
59
32
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
const points: AppUsageReport["points"][number][] = [];
|
|
76
|
-
for (
|
|
77
|
-
let start = bucketStart(startMs);
|
|
78
|
-
start < beforeReceivedAtMs;
|
|
79
|
-
start += intervalMs
|
|
80
|
-
) {
|
|
81
|
-
points.push({
|
|
82
|
-
startMs: start,
|
|
83
|
-
// Missing history is unknown, while a completely read interval with no reports is zero.
|
|
84
|
-
installations:
|
|
85
|
-
start + intervalMs <= history.sinceMs
|
|
86
|
-
? null
|
|
87
|
-
: (buckets.get(start)?.size ?? 0),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
const bundleCounts = new Map<string, BundleDistribution>();
|
|
91
|
-
for (const row of latest.values()) {
|
|
92
|
-
const key = JSON.stringify([
|
|
93
|
-
row.app_version,
|
|
94
|
-
row.platform,
|
|
95
|
-
row.to_release_id,
|
|
96
|
-
]);
|
|
97
|
-
bundleCounts.set(key, {
|
|
98
|
-
appVersion: row.app_version,
|
|
99
|
-
platform: row.platform,
|
|
100
|
-
releaseId: row.to_release_id,
|
|
101
|
-
installations: (bundleCounts.get(key)?.installations ?? 0) + 1,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
33
|
+
export async function getAppUsageReport(
|
|
34
|
+
model: InsightsModel,
|
|
35
|
+
input: AppUsageInput,
|
|
36
|
+
now = Date.now(),
|
|
37
|
+
): Promise<AppUsageReport> {
|
|
38
|
+
readAppUsageInput(input);
|
|
39
|
+
const { start, end, intervalMs } = usageRange(input.window, now);
|
|
40
|
+
const report = await model.getAppUsage({
|
|
41
|
+
channel: input.channel,
|
|
42
|
+
platform: input.platform,
|
|
43
|
+
...(input.appVersion === undefined ? {} : { appVersion: input.appVersion }),
|
|
44
|
+
timeRange: { start, end },
|
|
45
|
+
intervalMs,
|
|
46
|
+
});
|
|
104
47
|
return {
|
|
105
|
-
bundleDistribution:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
),
|
|
112
|
-
|
|
113
|
-
sinceMs: history.sinceMs,
|
|
114
|
-
beforeReceivedAtMs,
|
|
48
|
+
bundleDistribution: report.bundleDistribution,
|
|
49
|
+
activeInstallations: report.activeInstallations,
|
|
50
|
+
sinceMs:
|
|
51
|
+
report.coverage.sinceMs === null
|
|
52
|
+
? start
|
|
53
|
+
: Math.max(start, report.coverage.sinceMs),
|
|
54
|
+
distributionSinceMs: start - (start % DAY_MS),
|
|
55
|
+
beforeReceivedAtMs: end,
|
|
115
56
|
intervalMs,
|
|
116
|
-
truncated:
|
|
117
|
-
appVersions:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
platforms: distribution("platform"),
|
|
122
|
-
points,
|
|
57
|
+
truncated: report.coverage.kind === "partial",
|
|
58
|
+
appVersions: report.appVersions,
|
|
59
|
+
versions: report.versions,
|
|
60
|
+
platforms: report.platforms,
|
|
61
|
+
points: report.points,
|
|
123
62
|
};
|
|
124
63
|
}
|
|
@@ -1,61 +1,126 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
HotUpdaterCoreApi,
|
|
3
|
+
ReleaseFilter,
|
|
4
|
+
ReleaseRow,
|
|
5
|
+
} from "@hot-updater/plugin-core";
|
|
2
6
|
|
|
3
7
|
export interface ReleaseListInput {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
platform?: "ios" | "android";
|
|
12
|
-
targetAppVersion?: string;
|
|
8
|
+
/** One of the filter sets the release indexes serve. */
|
|
9
|
+
readonly filter: ReleaseFilter;
|
|
10
|
+
/** Releases older than this id: the next page. */
|
|
11
|
+
readonly beforeReleaseId?: string;
|
|
12
|
+
/** Releases newer than this id: the previous page. */
|
|
13
|
+
readonly afterReleaseId?: string;
|
|
14
|
+
readonly limit: number;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export interface ReleaseListResult {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
/** Newest first. */
|
|
19
|
+
readonly data: readonly ReleaseRow[];
|
|
20
|
+
/** `beforeReleaseId` of the next page, when there may be older releases. */
|
|
21
|
+
readonly next?: string;
|
|
22
|
+
/** `afterReleaseId` of the previous page, when there may be newer releases. */
|
|
23
|
+
readonly previous?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
27
|
+
typeof value === "object" && value !== null;
|
|
28
|
+
|
|
29
|
+
const text = (value: unknown, name: string): string => {
|
|
30
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
31
|
+
throw new Error(`Release filter ${name} is required.`);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const enabledOf = (value: unknown) => {
|
|
37
|
+
if (value === undefined) return {};
|
|
38
|
+
if (typeof value !== "boolean") {
|
|
39
|
+
throw new Error("Release filter enabled must be true or false.");
|
|
40
|
+
}
|
|
41
|
+
return { enabled: value };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A release list's filter: none, a channel with its platform (and enabled),
|
|
46
|
+
* a bundle, or a scope (and enabled). Those are the sets the release indexes
|
|
47
|
+
* serve, so any other combination is refused.
|
|
48
|
+
*/
|
|
49
|
+
export const readReleaseFilter = (value: unknown): ReleaseFilter => {
|
|
50
|
+
if (value === undefined) return { kind: "all" };
|
|
51
|
+
if (!isRecord(value)) throw new Error("Invalid release filter.");
|
|
52
|
+
switch (value.kind) {
|
|
53
|
+
case "all":
|
|
54
|
+
return { kind: "all" };
|
|
55
|
+
case "channelPlatform": {
|
|
56
|
+
if (value.platform !== "ios" && value.platform !== "android") {
|
|
57
|
+
throw new Error("Release filter platform must be ios or android.");
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
kind: "channelPlatform",
|
|
61
|
+
channelId: text(value.channelId, "channelId"),
|
|
62
|
+
platform: value.platform,
|
|
63
|
+
...enabledOf(value.enabled),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
case "bundle":
|
|
67
|
+
return { kind: "bundle", bundleId: text(value.bundleId, "bundleId") };
|
|
68
|
+
case "scope":
|
|
69
|
+
return {
|
|
70
|
+
kind: "scope",
|
|
71
|
+
scopeKey: text(value.scopeKey, "scopeKey"),
|
|
72
|
+
...enabledOf(value.enabled),
|
|
73
|
+
};
|
|
74
|
+
default:
|
|
75
|
+
throw new Error("Invalid release filter.");
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* One page of releases, newest first, through the index the filter names:
|
|
81
|
+
* each page reads only its own rows. `next` is set for a full page, so the
|
|
82
|
+
* last page may lead to an empty one.
|
|
83
|
+
*/
|
|
24
84
|
export async function listReleases(
|
|
25
|
-
|
|
85
|
+
core: Pick<HotUpdaterCoreApi, "listReleases">,
|
|
26
86
|
input: ReleaseListInput,
|
|
27
87
|
): Promise<ReleaseListResult> {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
88
|
+
const { filter, limit } = input;
|
|
89
|
+
if (input.afterReleaseId !== undefined) {
|
|
90
|
+
// Newer releases, read oldest first from the cursor, then turned around.
|
|
91
|
+
const data = (
|
|
92
|
+
await core.listReleases({
|
|
93
|
+
filter,
|
|
94
|
+
order: "asc",
|
|
95
|
+
after: input.afterReleaseId,
|
|
96
|
+
limit,
|
|
97
|
+
})
|
|
98
|
+
).reverse();
|
|
99
|
+
const first = data[0]?.id;
|
|
100
|
+
const last = data.at(-1)?.id;
|
|
101
|
+
return {
|
|
102
|
+
data,
|
|
103
|
+
...(last === undefined ? {} : { next: last }),
|
|
104
|
+
...(first !== undefined && data.length === limit
|
|
105
|
+
? { previous: first }
|
|
106
|
+
: {}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const data = await core.listReleases({
|
|
110
|
+
filter,
|
|
111
|
+
order: "desc",
|
|
112
|
+
limit,
|
|
33
113
|
...(input.beforeReleaseId === undefined
|
|
34
114
|
? {}
|
|
35
|
-
: {
|
|
36
|
-
...(input.bundleId === undefined ? {} : { bundleId: input.bundleId }),
|
|
37
|
-
...(input.channelId === undefined ? {} : { channelId: input.channelId }),
|
|
38
|
-
...(input.enabled === undefined ? {} : { enabled: input.enabled }),
|
|
39
|
-
...(input.platform === undefined ? {} : { platform: input.platform }),
|
|
40
|
-
...(input.targetAppVersion === undefined
|
|
41
|
-
? {}
|
|
42
|
-
: { targetAppVersion: input.targetAppVersion }),
|
|
43
|
-
limit: input.limit + 1,
|
|
115
|
+
: { after: input.beforeReleaseId }),
|
|
44
116
|
});
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const data = isPreviousRequest
|
|
48
|
-
? hasExtraRow
|
|
49
|
-
? rows.slice(1)
|
|
50
|
-
: rows
|
|
51
|
-
: rows.slice(0, input.limit);
|
|
52
|
-
|
|
117
|
+
const first = data[0]?.id;
|
|
118
|
+
const last = data.at(-1)?.id;
|
|
53
119
|
return {
|
|
54
120
|
data,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
121
|
+
...(last !== undefined && data.length === limit ? { next: last } : {}),
|
|
122
|
+
...(first !== undefined && input.beforeReleaseId !== undefined
|
|
123
|
+
? { previous: first }
|
|
124
|
+
: {}),
|
|
60
125
|
};
|
|
61
126
|
}
|