@hot-updater/console 1.0.0-rc.9 → 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-CyMmr0O9.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-CKvQ4VIE.mjs → alert-dialog-C43Doa0I.mjs} +6 -83
- package/.output/server/_ssr/{api-keys-CzZ7Ont4.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-DH6Bhwcz.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-BrGSjz4S.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-DDKmIrpV.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-cIsU-kHG.mjs → insights_.distribution-DN9pR422.mjs} +50 -22
- package/.output/server/_ssr/installations-BHmbbnH8.mjs +5 -0
- package/.output/server/_ssr/{installations-C5anhyP0.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-BuanSsRI.mjs → router-D8NuoJ_I.mjs} +107 -134
- package/.output/server/_ssr/{routes-DY-vqU6R.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-DD2szs3Y.mjs → select-t3iXRwc9.mjs} +3 -3
- package/.output/server/_ssr/server-h4VxnExB.mjs +8325 -0
- package/.output/server/_ssr/{createSsrRpc-CdlfejmI.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-CTj6n-4F.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 +309 -211
- 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 +69 -74
- 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-Cb_D1D6N.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-DBRojHaG.mjs +0 -150
- package/.output/server/_ssr/InsightsPageHeader-mtxVuZKK.mjs +0 -64
- package/.output/server/_ssr/api-BfCgYlj5.mjs +0 -332
- package/.output/server/_ssr/api-keys-api-C3oeq01R.mjs +0 -60
- package/.output/server/_ssr/api-keys-rpc-CdPFdGOx.mjs +0 -136
- package/.output/server/_ssr/api-rpc-B2E1ZIG5.mjs +0 -1399
- package/.output/server/_ssr/auth.server-CmicHh0J.mjs +0 -47
- package/.output/server/_ssr/bundle-signing-rpc-Di7-I6fE.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-Bemj0dpY.mjs +0 -108
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +0 -13
- package/.output/server/_ssr/config.server-B8WsT3s0.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-BrQ2u0fT.mjs +0 -74
- package/.output/server/_ssr/getBundleChildren-DoL8l_AS.mjs +0 -50
- package/.output/server/_ssr/insights-BwQ-wF2a.mjs +0 -10
- package/.output/server/_ssr/insights-gr1I_dJU.mjs +0 -938
- package/.output/server/_ssr/insights-recovery-Cv6RSyeR.mjs +0 -21
- package/.output/server/_ssr/insights-recovery-rpc-DOzxfYfv.mjs +0 -37
- package/.output/server/_ssr/insights-search-1czJMZgk.mjs +0 -21
- package/.output/server/_ssr/insights-usage-rpc-4ImOq_0p.mjs +0 -184
- package/.output/server/_ssr/insights-usage-rpc-BUruODjE.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-VmHuikRE.mjs +0 -10
- package/.output/server/_ssr/installations-HXMF_kKf.mjs +0 -29
- package/.output/server/_ssr/routes-Dru3IRj0.mjs +0 -37
- package/.output/server/_ssr/runtime.server-CsiKFp_Z.mjs +0 -436
- package/.output/server/_ssr/server-DJm72Kj-.mjs +0 -7468
- package/.output/server/_ssr/signing-H8Lg4lPe.mjs +0 -133
- package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-DsFcTLw_.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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { Undo2 } from "lucide-react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
|
|
6
|
+
import { BundleIdDisplay } from "@/components/BundleIdDisplay";
|
|
7
|
+
import {
|
|
8
|
+
AlertDialog,
|
|
9
|
+
AlertDialogAction,
|
|
10
|
+
AlertDialogCancel,
|
|
11
|
+
AlertDialogContent,
|
|
12
|
+
AlertDialogDescription,
|
|
13
|
+
AlertDialogFooter,
|
|
14
|
+
AlertDialogHeader,
|
|
15
|
+
AlertDialogTitle,
|
|
16
|
+
} from "@/components/ui/alert-dialog";
|
|
17
|
+
import { Button } from "@/components/ui/button";
|
|
18
|
+
import {
|
|
19
|
+
usePreflightReleaseMutation,
|
|
20
|
+
useUpdateReleaseMutation,
|
|
21
|
+
} from "@/lib/api";
|
|
22
|
+
import type { AdoptionRelease } from "@/lib/release-adoption";
|
|
23
|
+
|
|
24
|
+
const percent = (rate: number) => `${(rate * 100).toFixed(1)}%`;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Rolls a crashing bundle back the way its editor does: disabling its
|
|
28
|
+
* release sends installations running it back to the previous enabled
|
|
29
|
+
* release on their next update check.
|
|
30
|
+
*/
|
|
31
|
+
export function RollbackButton({
|
|
32
|
+
release,
|
|
33
|
+
rate,
|
|
34
|
+
crashes,
|
|
35
|
+
attempts,
|
|
36
|
+
}: {
|
|
37
|
+
readonly release: AdoptionRelease;
|
|
38
|
+
readonly rate: number;
|
|
39
|
+
readonly crashes: number;
|
|
40
|
+
readonly attempts: number;
|
|
41
|
+
}) {
|
|
42
|
+
const [open, setOpen] = useState(false);
|
|
43
|
+
const [error, setError] = useState("");
|
|
44
|
+
const queryClient = useQueryClient();
|
|
45
|
+
const preflight = usePreflightReleaseMutation();
|
|
46
|
+
const update = useUpdateReleaseMutation();
|
|
47
|
+
const busy = preflight.isPending || update.isPending;
|
|
48
|
+
const id = release.releaseId;
|
|
49
|
+
const rollBack = async () => {
|
|
50
|
+
const input = {
|
|
51
|
+
expectedRevision: release.revision,
|
|
52
|
+
patch: { enabled: false },
|
|
53
|
+
releaseId: release.releaseId,
|
|
54
|
+
};
|
|
55
|
+
setError("");
|
|
56
|
+
try {
|
|
57
|
+
await preflight.mutateAsync(input);
|
|
58
|
+
await update.mutateAsync(input);
|
|
59
|
+
await queryClient.invalidateQueries({
|
|
60
|
+
queryKey: ["insights", "adoption-releases"],
|
|
61
|
+
});
|
|
62
|
+
toast.success(`${id} rolled back`, {
|
|
63
|
+
description:
|
|
64
|
+
"Installations running it return to the previous enabled bundle on their next update check.",
|
|
65
|
+
});
|
|
66
|
+
setOpen(false);
|
|
67
|
+
} catch (caught) {
|
|
68
|
+
setError(
|
|
69
|
+
caught instanceof Error
|
|
70
|
+
? caught.message
|
|
71
|
+
: "The bundle could not be rolled back.",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return (
|
|
76
|
+
<AlertDialog
|
|
77
|
+
open={open}
|
|
78
|
+
onOpenChange={(next) => {
|
|
79
|
+
if (busy) return;
|
|
80
|
+
setOpen(next);
|
|
81
|
+
if (!next) setError("");
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
<Button variant="destructive" size="sm" onClick={() => setOpen(true)}>
|
|
85
|
+
<Undo2 aria-hidden="true" data-icon="inline-start" />
|
|
86
|
+
Roll back
|
|
87
|
+
</Button>
|
|
88
|
+
<AlertDialogContent>
|
|
89
|
+
<AlertDialogHeader>
|
|
90
|
+
<AlertDialogTitle>Roll back this bundle?</AlertDialogTitle>
|
|
91
|
+
<BundleIdDisplay bundleId={id} />
|
|
92
|
+
<AlertDialogDescription>
|
|
93
|
+
It crashed for {percent(rate)} of the installations that tried it (
|
|
94
|
+
{crashes.toLocaleString()} of {attempts.toLocaleString()}). Rolling
|
|
95
|
+
back disables it, so installations running it return to the previous
|
|
96
|
+
enabled bundle on their next update check. You can enable it again
|
|
97
|
+
in its bundle details.
|
|
98
|
+
</AlertDialogDescription>
|
|
99
|
+
</AlertDialogHeader>
|
|
100
|
+
{error ? (
|
|
101
|
+
<p role="alert" className="text-sm text-destructive">
|
|
102
|
+
{error}
|
|
103
|
+
</p>
|
|
104
|
+
) : null}
|
|
105
|
+
<AlertDialogFooter>
|
|
106
|
+
<AlertDialogCancel disabled={busy}>Cancel</AlertDialogCancel>
|
|
107
|
+
<AlertDialogAction
|
|
108
|
+
variant="destructive"
|
|
109
|
+
disabled={busy}
|
|
110
|
+
onClick={() => void rollBack()}
|
|
111
|
+
>
|
|
112
|
+
{busy ? "Rolling back…" : "Roll back"}
|
|
113
|
+
</AlertDialogAction>
|
|
114
|
+
</AlertDialogFooter>
|
|
115
|
+
</AlertDialogContent>
|
|
116
|
+
</AlertDialog>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { ChevronRight, RotateCw } from "lucide-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardFooter,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "@/components/ui/card";
|
|
13
|
+
import { Separator } from "@/components/ui/separator";
|
|
14
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
15
|
+
import {
|
|
16
|
+
Table,
|
|
17
|
+
TableBody,
|
|
18
|
+
TableCell,
|
|
19
|
+
TableHead,
|
|
20
|
+
TableHeader,
|
|
21
|
+
TableRow,
|
|
22
|
+
} from "@/components/ui/table";
|
|
23
|
+
import {
|
|
24
|
+
checkFailureRate,
|
|
25
|
+
failureDetailParts,
|
|
26
|
+
failureRate,
|
|
27
|
+
failureReasonLabel,
|
|
28
|
+
failureStageLabel,
|
|
29
|
+
formatRate,
|
|
30
|
+
type InsightsFailureBreakdown,
|
|
31
|
+
type UpdateFailuresReport,
|
|
32
|
+
} from "@/lib/insights-failures";
|
|
33
|
+
import { cn } from "@/lib/utils";
|
|
34
|
+
|
|
35
|
+
import { EstimatedCount } from "./EstimatedCount";
|
|
36
|
+
import { InsightsErrorAlert } from "./InsightsErrorAlert";
|
|
37
|
+
import { InsightsInfo } from "./InsightsInfo";
|
|
38
|
+
|
|
39
|
+
const share = (events: number, total: number) =>
|
|
40
|
+
total === 0 ? "—" : `${((events / total) * 100).toFixed(1)}%`;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* One kind of failure: its rate as the headline, then how many failures and
|
|
44
|
+
* installations it holds.
|
|
45
|
+
*/
|
|
46
|
+
function FailureSummary({
|
|
47
|
+
id,
|
|
48
|
+
title,
|
|
49
|
+
info,
|
|
50
|
+
rate,
|
|
51
|
+
of,
|
|
52
|
+
failures,
|
|
53
|
+
installations,
|
|
54
|
+
previous,
|
|
55
|
+
children,
|
|
56
|
+
}: {
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly title: string;
|
|
59
|
+
readonly info: string;
|
|
60
|
+
readonly rate: number | null;
|
|
61
|
+
/** What the rate is a share of. */
|
|
62
|
+
readonly of: string;
|
|
63
|
+
readonly failures: number;
|
|
64
|
+
readonly installations: number;
|
|
65
|
+
readonly previous?: {
|
|
66
|
+
readonly rate: number | null;
|
|
67
|
+
readonly complete: boolean;
|
|
68
|
+
};
|
|
69
|
+
readonly children?: ReactNode;
|
|
70
|
+
}) {
|
|
71
|
+
return (
|
|
72
|
+
<section aria-labelledby={id} className="flex min-w-0 flex-col gap-1">
|
|
73
|
+
<h3
|
|
74
|
+
id={id}
|
|
75
|
+
className="flex min-h-11 items-center gap-1 text-sm font-medium sm:min-h-7"
|
|
76
|
+
>
|
|
77
|
+
{title}
|
|
78
|
+
<InsightsInfo label={`About ${title.toLowerCase()}`}>
|
|
79
|
+
{info}
|
|
80
|
+
</InsightsInfo>
|
|
81
|
+
</h3>
|
|
82
|
+
<p className="text-3xl font-semibold tracking-tight tabular-nums">
|
|
83
|
+
<Rate value={rate} />
|
|
84
|
+
</p>
|
|
85
|
+
<p className="text-xs text-muted-foreground">{of}</p>
|
|
86
|
+
{previous ? (
|
|
87
|
+
<RateChange
|
|
88
|
+
value={rate}
|
|
89
|
+
previous={previous.rate}
|
|
90
|
+
complete={previous.complete}
|
|
91
|
+
/>
|
|
92
|
+
) : null}
|
|
93
|
+
<p className="mt-2 text-sm text-muted-foreground tabular-nums">
|
|
94
|
+
{failures.toLocaleString()} {failures === 1 ? "failure" : "failures"}
|
|
95
|
+
{" · "}
|
|
96
|
+
<EstimatedCount value={installations} />{" "}
|
|
97
|
+
{installations === 1 ? "installation" : "installations"}
|
|
98
|
+
</p>
|
|
99
|
+
{children}
|
|
100
|
+
</section>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function Rate({ value }: { readonly value: number | null }) {
|
|
105
|
+
const formatted = formatRate(value);
|
|
106
|
+
return value === null ? (
|
|
107
|
+
formatted
|
|
108
|
+
) : (
|
|
109
|
+
<>
|
|
110
|
+
{formatted.slice(0, -1)}
|
|
111
|
+
<span className="ml-0.5 text-sm font-normal text-muted-foreground">
|
|
112
|
+
%
|
|
113
|
+
</span>
|
|
114
|
+
</>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function RateChange({
|
|
119
|
+
value,
|
|
120
|
+
previous,
|
|
121
|
+
complete,
|
|
122
|
+
}: {
|
|
123
|
+
readonly value: number | null;
|
|
124
|
+
readonly previous: number | null;
|
|
125
|
+
readonly complete: boolean;
|
|
126
|
+
}) {
|
|
127
|
+
// No change to show before a whole previous period was recorded.
|
|
128
|
+
if (!complete || value === null || previous === null) return null;
|
|
129
|
+
const difference = (value - previous) * 100;
|
|
130
|
+
const rounded = Number(difference.toFixed(2));
|
|
131
|
+
return (
|
|
132
|
+
<span
|
|
133
|
+
className={cn(
|
|
134
|
+
"mt-1 block text-xs font-normal",
|
|
135
|
+
rounded > 0 ? "text-warning" : "text-muted-foreground",
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
{rounded === 0
|
|
139
|
+
? "Unchanged"
|
|
140
|
+
: `${rounded > 0 ? "↑ +" : "↓ "}${rounded.toFixed(2)} pp`}{" "}
|
|
141
|
+
vs previous period
|
|
142
|
+
</span>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** One stage and reason, and what else its clients knew, most first. */
|
|
147
|
+
function FailureRow({
|
|
148
|
+
failure,
|
|
149
|
+
total,
|
|
150
|
+
}: {
|
|
151
|
+
readonly failure: InsightsFailureBreakdown;
|
|
152
|
+
readonly total: number;
|
|
153
|
+
}) {
|
|
154
|
+
const label = `${failureStageLabel(failure.stage)} · ${failureReasonLabel(failure.reason)}`;
|
|
155
|
+
return (
|
|
156
|
+
<TableRow className="[&>td]:px-2 [&>td]:py-3 [&>td]:align-top sm:[&>td]:px-4">
|
|
157
|
+
<TableCell className="whitespace-normal">
|
|
158
|
+
<details className="group/failure">
|
|
159
|
+
<summary
|
|
160
|
+
aria-label={label}
|
|
161
|
+
className="flex min-h-11 w-fit max-w-full cursor-pointer list-none items-center gap-3 rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden"
|
|
162
|
+
>
|
|
163
|
+
<ChevronRight
|
|
164
|
+
aria-hidden="true"
|
|
165
|
+
className="size-4 shrink-0 text-muted-foreground group-open/failure:rotate-90"
|
|
166
|
+
/>
|
|
167
|
+
<span className="flex min-w-0 flex-col gap-1">
|
|
168
|
+
<span className="text-xs text-muted-foreground">
|
|
169
|
+
{failureStageLabel(failure.stage)}
|
|
170
|
+
</span>
|
|
171
|
+
<span className="text-sm font-medium wrap-anywhere">
|
|
172
|
+
{failureReasonLabel(failure.reason)}
|
|
173
|
+
</span>
|
|
174
|
+
</span>
|
|
175
|
+
</summary>
|
|
176
|
+
<ul
|
|
177
|
+
aria-label={`${label} details`}
|
|
178
|
+
className="mt-3 flex flex-col gap-3 rounded-md border bg-muted/20 p-3 text-sm"
|
|
179
|
+
>
|
|
180
|
+
{failure.details.map((detail) => {
|
|
181
|
+
const parts = failureDetailParts(detail);
|
|
182
|
+
return (
|
|
183
|
+
<li
|
|
184
|
+
key={JSON.stringify(detail)}
|
|
185
|
+
className="flex justify-between gap-4"
|
|
186
|
+
>
|
|
187
|
+
<span className="wrap-anywhere">
|
|
188
|
+
{parts.length === 0
|
|
189
|
+
? "No details reported"
|
|
190
|
+
: parts.join(" · ")}
|
|
191
|
+
</span>
|
|
192
|
+
<span className="shrink-0 font-medium tabular-nums">
|
|
193
|
+
{detail.events.toLocaleString()}
|
|
194
|
+
</span>
|
|
195
|
+
</li>
|
|
196
|
+
);
|
|
197
|
+
})}
|
|
198
|
+
</ul>
|
|
199
|
+
</details>
|
|
200
|
+
</TableCell>
|
|
201
|
+
<TableCell className="text-right tabular-nums">
|
|
202
|
+
<span className="inline-flex min-h-11 items-center text-sm font-medium">
|
|
203
|
+
{failure.events.toLocaleString()}
|
|
204
|
+
</span>
|
|
205
|
+
</TableCell>
|
|
206
|
+
<TableCell className="text-right tabular-nums text-muted-foreground">
|
|
207
|
+
<span className="inline-flex min-h-11 items-center text-sm">
|
|
208
|
+
{share(failure.events, total)}
|
|
209
|
+
</span>
|
|
210
|
+
</TableCell>
|
|
211
|
+
</TableRow>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function FailuresReport({
|
|
216
|
+
report,
|
|
217
|
+
errors,
|
|
218
|
+
}: {
|
|
219
|
+
readonly report: UpdateFailuresReport;
|
|
220
|
+
readonly errors?: ReactNode;
|
|
221
|
+
}) {
|
|
222
|
+
const breakdown = report.breakdown ?? [];
|
|
223
|
+
const total = breakdown.reduce((sum, { events }) => sum + events, 0);
|
|
224
|
+
const unreasoned = breakdown
|
|
225
|
+
.filter(({ reason }) => reason === "unknown")
|
|
226
|
+
.reduce((sum, { events }) => sum + events, 0);
|
|
227
|
+
const patchAttempts = report.patchDownloads + report.patchFallbacks;
|
|
228
|
+
return (
|
|
229
|
+
<div className="flex flex-col gap-8">
|
|
230
|
+
<div className={cn("grid gap-6", report.checks && "sm:grid-cols-2")}>
|
|
231
|
+
<FailureSummary
|
|
232
|
+
id="download-install-failures"
|
|
233
|
+
title="Downloads & installs"
|
|
234
|
+
info="Failed downloads and installs ÷ those plus successful downloads."
|
|
235
|
+
rate={failureRate(report)}
|
|
236
|
+
of="of update attempts failed"
|
|
237
|
+
failures={report.failedUpdates}
|
|
238
|
+
installations={report.failedInstallations}
|
|
239
|
+
previous={
|
|
240
|
+
report.previous && {
|
|
241
|
+
rate: report.previous.attemptRate,
|
|
242
|
+
complete: report.previous.complete,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
>
|
|
246
|
+
{patchAttempts > 0 ? (
|
|
247
|
+
<p className="text-sm text-muted-foreground tabular-nums">
|
|
248
|
+
{report.patchFallbacks.toLocaleString()} of{" "}
|
|
249
|
+
{patchAttempts.toLocaleString()} patch downloads fell back to the
|
|
250
|
+
full archive
|
|
251
|
+
</p>
|
|
252
|
+
) : null}
|
|
253
|
+
</FailureSummary>
|
|
254
|
+
{report.checks ? (
|
|
255
|
+
<FailureSummary
|
|
256
|
+
id="update-check-failures"
|
|
257
|
+
title="Update checks"
|
|
258
|
+
info="Installations whose update check failed ÷ active installations. A check that fails offline isn't reported."
|
|
259
|
+
rate={checkFailureRate(report.checks)}
|
|
260
|
+
of="of active installations had a check fail"
|
|
261
|
+
failures={report.checks.failures}
|
|
262
|
+
installations={report.checks.failedInstallations}
|
|
263
|
+
previous={
|
|
264
|
+
report.previous && {
|
|
265
|
+
rate: report.previous.checkRate,
|
|
266
|
+
complete: report.previous.complete,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/>
|
|
270
|
+
) : null}
|
|
271
|
+
</div>
|
|
272
|
+
<Separator />
|
|
273
|
+
{errors ? (
|
|
274
|
+
<>
|
|
275
|
+
{errors}
|
|
276
|
+
<Separator />
|
|
277
|
+
</>
|
|
278
|
+
) : null}
|
|
279
|
+
<section
|
|
280
|
+
aria-labelledby="failures-by-stage"
|
|
281
|
+
className="flex flex-col gap-3"
|
|
282
|
+
>
|
|
283
|
+
<h3 className="text-sm font-medium" id="failures-by-stage">
|
|
284
|
+
Failures by stage and reason
|
|
285
|
+
</h3>
|
|
286
|
+
{unreasoned * 2 > total ? (
|
|
287
|
+
<p className="text-sm text-muted-foreground">
|
|
288
|
+
Most report no reason: an SDK that doesn't classify failures
|
|
289
|
+
reports every failed check, offline ones included.
|
|
290
|
+
</p>
|
|
291
|
+
) : null}
|
|
292
|
+
{breakdown.length === 0 ? (
|
|
293
|
+
<p className="text-sm text-muted-foreground">
|
|
294
|
+
No update failures in this period.
|
|
295
|
+
</p>
|
|
296
|
+
) : (
|
|
297
|
+
<Table aria-label="Failures by stage and reason">
|
|
298
|
+
<TableHeader>
|
|
299
|
+
<TableRow className="[&>th]:px-2 sm:[&>th]:px-4">
|
|
300
|
+
<TableHead>Stage / reason</TableHead>
|
|
301
|
+
<TableHead className="w-16 text-right sm:w-24">
|
|
302
|
+
Reports
|
|
303
|
+
</TableHead>
|
|
304
|
+
<TableHead className="w-16 text-right sm:w-24">Share</TableHead>
|
|
305
|
+
</TableRow>
|
|
306
|
+
</TableHeader>
|
|
307
|
+
<TableBody>
|
|
308
|
+
{breakdown.map((failure) => (
|
|
309
|
+
<FailureRow
|
|
310
|
+
key={`${failure.stage} ${failure.reason}`}
|
|
311
|
+
failure={failure}
|
|
312
|
+
total={total}
|
|
313
|
+
/>
|
|
314
|
+
))}
|
|
315
|
+
</TableBody>
|
|
316
|
+
</Table>
|
|
317
|
+
)}
|
|
318
|
+
</section>
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Update failures for Release health's scope and period: counts, rates, and
|
|
325
|
+
* the stage and reason breakdown with what else the clients knew.
|
|
326
|
+
*/
|
|
327
|
+
export function UpdateFailures({
|
|
328
|
+
errors,
|
|
329
|
+
query,
|
|
330
|
+
onRefresh,
|
|
331
|
+
}: {
|
|
332
|
+
readonly errors?: ReactNode;
|
|
333
|
+
readonly query: {
|
|
334
|
+
readonly data: UpdateFailuresReport | undefined;
|
|
335
|
+
readonly error: Error | null;
|
|
336
|
+
readonly isPending: boolean;
|
|
337
|
+
readonly isFetching: boolean;
|
|
338
|
+
};
|
|
339
|
+
readonly onRefresh: () => void;
|
|
340
|
+
}) {
|
|
341
|
+
return (
|
|
342
|
+
<section aria-label="Update failures">
|
|
343
|
+
<Card className="min-w-0 overflow-hidden shadow-sm">
|
|
344
|
+
<CardHeader className="flex flex-col gap-1.5 p-4 sm:p-6">
|
|
345
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
346
|
+
<CardTitle>Update failures</CardTitle>
|
|
347
|
+
<Button
|
|
348
|
+
aria-label="Refresh update failures"
|
|
349
|
+
variant="ghost"
|
|
350
|
+
size="icon-lg"
|
|
351
|
+
disabled={query.isFetching}
|
|
352
|
+
onClick={onRefresh}
|
|
353
|
+
>
|
|
354
|
+
<RotateCw aria-hidden="true" />
|
|
355
|
+
</Button>
|
|
356
|
+
</div>
|
|
357
|
+
<CardDescription>
|
|
358
|
+
Track failure rates, inspect original errors, and open individual
|
|
359
|
+
reports in the selected Release health scope and period.
|
|
360
|
+
</CardDescription>
|
|
361
|
+
</CardHeader>
|
|
362
|
+
<CardContent className="px-4 pb-4 sm:px-6 sm:pb-6">
|
|
363
|
+
{query.isPending ? (
|
|
364
|
+
<Skeleton aria-label="Loading update failures" className="h-64" />
|
|
365
|
+
) : query.error ? (
|
|
366
|
+
<InsightsErrorAlert
|
|
367
|
+
error={query.error}
|
|
368
|
+
fallbackTitle="Update failures unavailable"
|
|
369
|
+
/>
|
|
370
|
+
) : query.data ? (
|
|
371
|
+
<FailuresReport report={query.data} errors={errors} />
|
|
372
|
+
) : null}
|
|
373
|
+
</CardContent>
|
|
374
|
+
{query.data?.coverage.kind === "partial" ? (
|
|
375
|
+
<CardFooter className="border-t px-6 py-5">
|
|
376
|
+
<span className="text-xs text-muted-foreground">Partial</span>
|
|
377
|
+
</CardFooter>
|
|
378
|
+
) : null}
|
|
379
|
+
</Card>
|
|
380
|
+
</section>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import { ChartNoAxesCombined } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import type { ConsoleFeatureUiRegistry } from "../FeatureSlots";
|
|
5
|
+
import {
|
|
6
|
+
BundleInsightsSummary,
|
|
7
|
+
ReleaseFailuresSection,
|
|
8
|
+
releaseActivityColumn,
|
|
9
|
+
} from "./ReleaseActivity";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* What the Insights features add: their navigation item, release activity on
|
|
13
|
+
* the Bundles page and in the release editor, and a release's download
|
|
14
|
+
* failures in the release editor, which a self-hosted server serves too.
|
|
15
|
+
*/
|
|
16
|
+
export const insightsFeatureUi = {
|
|
17
|
+
insights: {
|
|
18
|
+
navigation: {
|
|
19
|
+
icon: <ChartNoAxesCombined />,
|
|
20
|
+
paths: ["/insights", "/insights/distribution", "/installations"],
|
|
21
|
+
// A self-hosted server's admin API serves events, not the overview.
|
|
22
|
+
link: ({ features, onClick }) =>
|
|
23
|
+
features.insightsAnalytics ? (
|
|
24
|
+
<Link to="/insights" onClick={onClick} />
|
|
25
|
+
) : (
|
|
26
|
+
<Link to="/installations" onClick={onClick} />
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
releaseSection: ({ release }) => <ReleaseFailuresSection input={release} />,
|
|
30
|
+
},
|
|
31
|
+
insightsAnalytics: {
|
|
32
|
+
releaseColumn: releaseActivityColumn,
|
|
33
|
+
releaseSection: ({ release }) => <BundleInsightsSummary input={release} />,
|
|
34
|
+
},
|
|
35
|
+
} satisfies ConsoleFeatureUiRegistry;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getPatchBaseBundleId,
|
|
3
|
-
INVALID_COHORT_ERROR_MESSAGE,
|
|
4
|
-
isValidCohort,
|
|
5
|
-
normalizeCohortValue,
|
|
6
|
-
} from "@hot-updater/core";
|
|
7
1
|
import type {
|
|
8
2
|
ChannelRow,
|
|
9
3
|
ReleasePolicyPatch,
|
|
10
4
|
ReleaseRow,
|
|
11
5
|
} from "@hot-updater/plugin-core";
|
|
6
|
+
import {
|
|
7
|
+
getPatchBaseBundleId,
|
|
8
|
+
INVALID_COHORT_ERROR_MESSAGE,
|
|
9
|
+
isValidCohort,
|
|
10
|
+
normalizeCohortValue,
|
|
11
|
+
} from "@hot-updater/protocol";
|
|
12
12
|
import { AlertTriangle, Download, Plus, X } from "lucide-react";
|
|
13
13
|
import { useEffect, useState } from "react";
|
|
14
14
|
import { toast } from "sonner";
|
|
15
15
|
import { normalizeRange } from "verkit";
|
|
16
16
|
|
|
17
17
|
import { BundleIdDisplay } from "@/components/BundleIdDisplay";
|
|
18
|
-
import { BundleInsightsSummary } from "@/components/features/bundles/BundleInsightsSummary";
|
|
19
18
|
import { BundleMetadata } from "@/components/features/bundles/BundleMetadata";
|
|
20
19
|
import { RolloutCohortsDialog } from "@/components/features/bundles/RolloutCohortsDialog";
|
|
20
|
+
import { ReleaseSections } from "@/components/features/FeatureSlots";
|
|
21
21
|
import { PlatformIcon } from "@/components/PlatformIcon";
|
|
22
22
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
23
23
|
import {
|
|
@@ -204,11 +204,14 @@ function RolloutPercentageInput({
|
|
|
204
204
|
export function ReleaseEditorSheet({
|
|
205
205
|
channels,
|
|
206
206
|
onOpenChange,
|
|
207
|
+
onShowScope,
|
|
207
208
|
open,
|
|
208
209
|
releaseId,
|
|
209
210
|
}: {
|
|
210
211
|
channels: readonly ChannelRow[];
|
|
211
212
|
onOpenChange: (open: boolean) => void;
|
|
213
|
+
/** Lists the bundles of this release's catalog scope: its channel, platform, and target kind. */
|
|
214
|
+
onShowScope?: (scopeKey: string) => void;
|
|
212
215
|
open: boolean;
|
|
213
216
|
releaseId: string;
|
|
214
217
|
}) {
|
|
@@ -369,7 +372,7 @@ export function ReleaseEditorSheet({
|
|
|
369
372
|
/>
|
|
370
373
|
{!release.bundle_id ? (
|
|
371
374
|
<span className="text-xs text-foreground">
|
|
372
|
-
Built-in
|
|
375
|
+
Built-in bundle
|
|
373
376
|
</span>
|
|
374
377
|
) : null}
|
|
375
378
|
</span>
|
|
@@ -410,11 +413,12 @@ export function ReleaseEditorSheet({
|
|
|
410
413
|
{release && draft ? (
|
|
411
414
|
<div className="flex flex-col gap-6 px-4 pb-4 sm:px-6 sm:pb-6">
|
|
412
415
|
{channelName ? (
|
|
413
|
-
<
|
|
414
|
-
|
|
416
|
+
<ReleaseSections
|
|
417
|
+
release={{
|
|
415
418
|
platform: release.platform,
|
|
416
419
|
channel: channelName,
|
|
417
420
|
releaseId: release.id,
|
|
421
|
+
builtIn: !release.bundle_id,
|
|
418
422
|
}}
|
|
419
423
|
/>
|
|
420
424
|
) : null}
|
|
@@ -601,7 +605,7 @@ export function ReleaseEditorSheet({
|
|
|
601
605
|
<h3 className="text-sm font-medium">Actions</h3>
|
|
602
606
|
<Button
|
|
603
607
|
className="w-full"
|
|
604
|
-
disabled={release.
|
|
608
|
+
disabled={!release.bundle_id || busy}
|
|
605
609
|
onClick={() => setShowPromote(true)}
|
|
606
610
|
size="sm"
|
|
607
611
|
variant="outline"
|
|
@@ -624,7 +628,7 @@ export function ReleaseEditorSheet({
|
|
|
624
628
|
variant="outline"
|
|
625
629
|
>
|
|
626
630
|
<Download data-icon="inline-start" />
|
|
627
|
-
Download
|
|
631
|
+
Download manifest
|
|
628
632
|
</Button>
|
|
629
633
|
) : null}
|
|
630
634
|
<Button
|
|
@@ -662,7 +666,7 @@ export function ReleaseEditorSheet({
|
|
|
662
666
|
<div className="sm:col-span-2">
|
|
663
667
|
<dt>Artifact ID</dt>
|
|
664
668
|
<dd className="mt-1 break-all font-mono text-foreground">
|
|
665
|
-
{release.bundle_id ?? "Built-in
|
|
669
|
+
{release.bundle_id ?? "Built-in bundle"}
|
|
666
670
|
</dd>
|
|
667
671
|
</div>
|
|
668
672
|
<div>
|
|
@@ -698,6 +702,16 @@ export function ReleaseEditorSheet({
|
|
|
698
702
|
<dd className="mt-1 break-all font-mono text-foreground">
|
|
699
703
|
{release.scope_key}
|
|
700
704
|
</dd>
|
|
705
|
+
{onShowScope ? (
|
|
706
|
+
<Button
|
|
707
|
+
className="mt-1 h-auto px-0 text-xs"
|
|
708
|
+
onClick={() => onShowScope(release.scope_key)}
|
|
709
|
+
size="sm"
|
|
710
|
+
variant="link"
|
|
711
|
+
>
|
|
712
|
+
Show bundles for this target
|
|
713
|
+
</Button>
|
|
714
|
+
) : null}
|
|
701
715
|
</div>
|
|
702
716
|
</dl>
|
|
703
717
|
{diagnostics.isError ? (
|