@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
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { Link, useRouterState } from "@tanstack/react-router";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
KeyRound,
|
|
5
|
-
LogOut,
|
|
6
|
-
Moon,
|
|
7
|
-
Package,
|
|
8
|
-
ShieldCheck,
|
|
9
|
-
Sun,
|
|
10
|
-
} from "lucide-react";
|
|
11
|
-
import { useState } from "react";
|
|
2
|
+
import { LogOut, Moon, Package, Sun } from "lucide-react";
|
|
3
|
+
import { type ReactElement, type ReactNode, useState } from "react";
|
|
12
4
|
import { toast } from "sonner";
|
|
13
5
|
|
|
6
|
+
import { useFeatureUi } from "@/components/features/FeatureSlots";
|
|
14
7
|
import { HotUpdaterLogo } from "@/components/HotUpdaterLogo";
|
|
15
8
|
import { useTheme } from "@/components/ThemeProvider";
|
|
16
9
|
import {
|
|
@@ -24,23 +17,61 @@ import {
|
|
|
24
17
|
SidebarMenu,
|
|
25
18
|
SidebarMenuButton,
|
|
26
19
|
SidebarMenuItem,
|
|
20
|
+
useSidebar,
|
|
27
21
|
} from "@/components/ui/sidebar";
|
|
28
|
-
|
|
22
|
+
|
|
23
|
+
type NavigationItem = {
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly icon: ReactNode;
|
|
26
|
+
readonly active: boolean;
|
|
27
|
+
readonly link: ReactElement;
|
|
28
|
+
};
|
|
29
29
|
|
|
30
30
|
export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
31
|
+
const { setOpenMobile } = useSidebar();
|
|
32
|
+
const closeMobileSidebar = () => setOpenMobile(false);
|
|
31
33
|
const [signingOut, setSigningOut] = useState(false);
|
|
32
|
-
const
|
|
34
|
+
const featureUi = useFeatureUi();
|
|
33
35
|
const { theme, setTheme } = useTheme();
|
|
34
36
|
const routerState = useRouterState();
|
|
35
37
|
const currentPath = routerState.location.pathname;
|
|
36
38
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const navigation: readonly NavigationItem[] = [
|
|
40
|
+
{
|
|
41
|
+
label: "Bundles",
|
|
42
|
+
icon: <Package />,
|
|
43
|
+
active: currentPath === "/",
|
|
44
|
+
link: (
|
|
45
|
+
<Link
|
|
46
|
+
to="/"
|
|
47
|
+
onClick={closeMobileSidebar}
|
|
48
|
+
search={{
|
|
49
|
+
afterReleaseId: undefined,
|
|
50
|
+
beforeReleaseId: undefined,
|
|
51
|
+
channelId: undefined,
|
|
52
|
+
enabled: undefined,
|
|
53
|
+
releaseId: undefined,
|
|
54
|
+
platform: undefined,
|
|
55
|
+
bundleId: undefined,
|
|
56
|
+
scopeKey: undefined,
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
),
|
|
60
|
+
},
|
|
61
|
+
// Each feature that is on adds its item, as the feature registry lists it.
|
|
62
|
+
...featureUi.flatMap(({ features, label, navigation }) =>
|
|
63
|
+
navigation === undefined
|
|
64
|
+
? []
|
|
65
|
+
: [
|
|
66
|
+
{
|
|
67
|
+
label,
|
|
68
|
+
icon: navigation.icon,
|
|
69
|
+
active: navigation.paths.includes(currentPath),
|
|
70
|
+
link: navigation.link({ features, onClick: closeMobileSidebar }),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
),
|
|
74
|
+
];
|
|
44
75
|
|
|
45
76
|
const signOut = async () => {
|
|
46
77
|
setSigningOut(true);
|
|
@@ -63,6 +94,7 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
|
63
94
|
<SidebarHeader className="h-12 justify-center">
|
|
64
95
|
<Link
|
|
65
96
|
to="/"
|
|
97
|
+
onClick={closeMobileSidebar}
|
|
66
98
|
search={{
|
|
67
99
|
afterReleaseId: undefined,
|
|
68
100
|
beforeReleaseId: undefined,
|
|
@@ -70,9 +102,8 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
|
70
102
|
enabled: undefined,
|
|
71
103
|
releaseId: undefined,
|
|
72
104
|
platform: undefined,
|
|
73
|
-
targetAppVersion: undefined,
|
|
74
105
|
bundleId: undefined,
|
|
75
|
-
|
|
106
|
+
scopeKey: undefined,
|
|
76
107
|
}}
|
|
77
108
|
className="flex items-center gap-3 p-1 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-2"
|
|
78
109
|
>
|
|
@@ -93,63 +124,18 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
|
93
124
|
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
|
|
94
125
|
<SidebarGroupContent>
|
|
95
126
|
<SidebarMenu>
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
isActive={isBundlesActive}
|
|
99
|
-
render={
|
|
100
|
-
<Link
|
|
101
|
-
to="/"
|
|
102
|
-
search={{
|
|
103
|
-
afterReleaseId: undefined,
|
|
104
|
-
beforeReleaseId: undefined,
|
|
105
|
-
channelId: undefined,
|
|
106
|
-
enabled: undefined,
|
|
107
|
-
releaseId: undefined,
|
|
108
|
-
platform: undefined,
|
|
109
|
-
targetAppVersion: undefined,
|
|
110
|
-
bundleId: undefined,
|
|
111
|
-
page: undefined,
|
|
112
|
-
}}
|
|
113
|
-
/>
|
|
114
|
-
}
|
|
115
|
-
tooltip="Bundles"
|
|
116
|
-
>
|
|
117
|
-
<Package />
|
|
118
|
-
<span>Bundles</span>
|
|
119
|
-
</SidebarMenuButton>
|
|
120
|
-
</SidebarMenuItem>
|
|
121
|
-
<SidebarMenuItem>
|
|
122
|
-
<SidebarMenuButton
|
|
123
|
-
isActive={isInsightsActive}
|
|
124
|
-
render={<Link to="/insights" />}
|
|
125
|
-
tooltip="Insights"
|
|
126
|
-
>
|
|
127
|
-
<ChartNoAxesCombined />
|
|
128
|
-
<span>Insights</span>
|
|
129
|
-
</SidebarMenuButton>
|
|
130
|
-
</SidebarMenuItem>
|
|
131
|
-
{apiKeyCapability.data?.apiKeys ? (
|
|
132
|
-
<SidebarMenuItem>
|
|
127
|
+
{navigation.map((item) => (
|
|
128
|
+
<SidebarMenuItem key={item.label}>
|
|
133
129
|
<SidebarMenuButton
|
|
134
|
-
isActive={
|
|
135
|
-
render={
|
|
136
|
-
tooltip=
|
|
130
|
+
isActive={item.active}
|
|
131
|
+
render={item.link}
|
|
132
|
+
tooltip={item.label}
|
|
137
133
|
>
|
|
138
|
-
|
|
139
|
-
<span>
|
|
134
|
+
{item.icon}
|
|
135
|
+
<span>{item.label}</span>
|
|
140
136
|
</SidebarMenuButton>
|
|
141
137
|
</SidebarMenuItem>
|
|
142
|
-
)
|
|
143
|
-
<SidebarMenuItem>
|
|
144
|
-
<SidebarMenuButton
|
|
145
|
-
isActive={isSigningActive}
|
|
146
|
-
render={<Link to="/signing" />}
|
|
147
|
-
tooltip="Bundle signing"
|
|
148
|
-
>
|
|
149
|
-
<ShieldCheck />
|
|
150
|
-
<span>Bundle signing</span>
|
|
151
|
-
</SidebarMenuButton>
|
|
152
|
-
</SidebarMenuItem>
|
|
138
|
+
))}
|
|
153
139
|
</SidebarMenu>
|
|
154
140
|
</SidebarGroupContent>
|
|
155
141
|
</SidebarGroup>
|
|
@@ -183,6 +169,15 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
|
183
169
|
</SidebarMenuButton>
|
|
184
170
|
</SidebarMenuItem>
|
|
185
171
|
</SidebarMenu>
|
|
172
|
+
{/* The Console's license asks a hosted service to keep this notice. */}
|
|
173
|
+
<a
|
|
174
|
+
href="https://hot-updater.dev"
|
|
175
|
+
target="_blank"
|
|
176
|
+
rel="noreferrer"
|
|
177
|
+
className="px-2 pb-1 text-[11px] text-sidebar-foreground/60 hover:text-sidebar-foreground group-data-[collapsible=icon]:hidden"
|
|
178
|
+
>
|
|
179
|
+
Powered by hot-updater
|
|
180
|
+
</a>
|
|
186
181
|
</SidebarFooter>
|
|
187
182
|
</Sidebar>
|
|
188
183
|
);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ArrowUpRight } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { NotFoundPage } from "@/components/NotFoundPage";
|
|
4
|
+
import { buttonVariants } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Empty,
|
|
7
|
+
EmptyContent,
|
|
8
|
+
EmptyDescription,
|
|
9
|
+
EmptyHeader,
|
|
10
|
+
EmptyTitle,
|
|
11
|
+
} from "@/components/ui/empty";
|
|
12
|
+
import { SidebarTrigger } from "@/components/ui/sidebar";
|
|
13
|
+
import { consoleFeatures, unavailableReason } from "@/lib/console-features";
|
|
14
|
+
import {
|
|
15
|
+
notFoundFeature,
|
|
16
|
+
useConsoleFeatures,
|
|
17
|
+
} from "@/lib/console-features-api";
|
|
18
|
+
|
|
19
|
+
export const CONSOLE_DEPLOYMENT_GUIDE_URL =
|
|
20
|
+
"https://hot-updater.dev/docs/guides/console-deployment";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A feature route's not-found state: the plugin to add, or, for a feature a
|
|
24
|
+
* self-hosted server does not serve, the database config it needs.
|
|
25
|
+
*/
|
|
26
|
+
export function ConsoleFeatureUnavailable({
|
|
27
|
+
data,
|
|
28
|
+
}: {
|
|
29
|
+
readonly data?: unknown;
|
|
30
|
+
}) {
|
|
31
|
+
const feature = notFoundFeature(data);
|
|
32
|
+
const remote = useConsoleFeatures().data?.remote === true;
|
|
33
|
+
if (feature === undefined) return <NotFoundPage />;
|
|
34
|
+
|
|
35
|
+
const { label, plugin } = consoleFeatures[feature];
|
|
36
|
+
const pluginCall = <code className="font-mono">{`${plugin}()`}</code>;
|
|
37
|
+
return (
|
|
38
|
+
<div className="flex h-svh min-h-0 flex-col">
|
|
39
|
+
<header className="sticky top-0 z-10 flex shrink-0 items-center gap-3 border-b bg-background px-3 py-3 sm:min-h-12 sm:bg-card/70 sm:px-4 sm:backdrop-blur-sm">
|
|
40
|
+
<SidebarTrigger className="-ml-1" />
|
|
41
|
+
<h1 className="text-sm font-medium">{label}</h1>
|
|
42
|
+
</header>
|
|
43
|
+
<div className="flex min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 p-3 sm:p-6">
|
|
44
|
+
<Empty>
|
|
45
|
+
<EmptyHeader>
|
|
46
|
+
{unavailableReason(feature, { remote }) === "remote" ? (
|
|
47
|
+
<>
|
|
48
|
+
<EmptyTitle>Needs the server's database</EmptyTitle>
|
|
49
|
+
<EmptyDescription>
|
|
50
|
+
This console reaches a self-hosted server through its admin
|
|
51
|
+
API, which does not serve this page. Configure the console
|
|
52
|
+
with the database the server uses.
|
|
53
|
+
</EmptyDescription>
|
|
54
|
+
</>
|
|
55
|
+
) : (
|
|
56
|
+
<>
|
|
57
|
+
<EmptyTitle>{label} not installed</EmptyTitle>
|
|
58
|
+
<EmptyDescription>
|
|
59
|
+
Add {pluginCall} to <code className="font-mono">plugins</code>{" "}
|
|
60
|
+
where you create the server and in the console config.
|
|
61
|
+
</EmptyDescription>
|
|
62
|
+
</>
|
|
63
|
+
)}
|
|
64
|
+
</EmptyHeader>
|
|
65
|
+
<EmptyContent>
|
|
66
|
+
<a
|
|
67
|
+
className={buttonVariants({ variant: "outline" })}
|
|
68
|
+
href={CONSOLE_DEPLOYMENT_GUIDE_URL}
|
|
69
|
+
rel="noreferrer"
|
|
70
|
+
target="_blank"
|
|
71
|
+
>
|
|
72
|
+
Console setup guide
|
|
73
|
+
<ArrowUpRight aria-hidden="true" data-icon="inline-end" />
|
|
74
|
+
</a>
|
|
75
|
+
</EmptyContent>
|
|
76
|
+
</Empty>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -3,14 +3,14 @@ import { Link } from "@tanstack/react-router";
|
|
|
3
3
|
import { buttonVariants } from "@/components/ui/button";
|
|
4
4
|
|
|
5
5
|
const homeSearch = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
targetAppVersion: undefined,
|
|
9
|
-
page: undefined,
|
|
10
|
-
after: undefined,
|
|
11
|
-
before: undefined,
|
|
6
|
+
afterReleaseId: undefined,
|
|
7
|
+
beforeReleaseId: undefined,
|
|
12
8
|
bundleId: undefined,
|
|
13
|
-
|
|
9
|
+
channelId: undefined,
|
|
10
|
+
enabled: undefined,
|
|
11
|
+
platform: undefined,
|
|
12
|
+
releaseId: undefined,
|
|
13
|
+
scopeKey: undefined,
|
|
14
14
|
} as const;
|
|
15
15
|
|
|
16
16
|
export function NotFoundPage() {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type ConsoleFeature,
|
|
5
|
+
type ConsoleFeatures,
|
|
6
|
+
consoleFeatures,
|
|
7
|
+
} from "@/lib/console-features";
|
|
8
|
+
import { useConsoleFeatures } from "@/lib/console-features-api";
|
|
9
|
+
|
|
10
|
+
import { apiKeysFeatureUi } from "./api-keys/apiKeysFeatureUi";
|
|
11
|
+
import { insightsFeatureUi } from "./insights/insightsFeatureUi";
|
|
12
|
+
import { remoteConfigFeatureUi } from "./remote-config/remoteConfigFeatureUi";
|
|
13
|
+
|
|
14
|
+
/** A release, as the Bundles page and the release editor hand it to slots. */
|
|
15
|
+
export interface ReleaseSlotInput {
|
|
16
|
+
readonly releaseId: string;
|
|
17
|
+
readonly platform: "ios" | "android";
|
|
18
|
+
readonly channel: string;
|
|
19
|
+
/** The release runs the native build's built-in bundle, which nothing downloads. */
|
|
20
|
+
readonly builtIn?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A column a feature adds to the Bundles page's release list. `Cell` renders
|
|
25
|
+
* one release, and gets the page's releases too, so a feature can read them
|
|
26
|
+
* all in one request that every cell shares.
|
|
27
|
+
*/
|
|
28
|
+
export interface ReleaseColumn {
|
|
29
|
+
readonly Cell: ComponentType<{
|
|
30
|
+
readonly releaseId: string;
|
|
31
|
+
readonly releases: readonly ReleaseSlotInput[];
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A navigation item a feature adds below Bundles. */
|
|
36
|
+
export interface FeatureNavigation {
|
|
37
|
+
readonly icon: ReactNode;
|
|
38
|
+
/** The pathnames where the item shows as active. */
|
|
39
|
+
readonly paths: readonly string[];
|
|
40
|
+
readonly link: (props: {
|
|
41
|
+
readonly features: ConsoleFeatures;
|
|
42
|
+
readonly onClick: () => void;
|
|
43
|
+
}) => ReactElement;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** What a feature adds to the console's pages while it is on. */
|
|
47
|
+
export interface ConsoleFeatureUi {
|
|
48
|
+
readonly navigation?: FeatureNavigation;
|
|
49
|
+
readonly releaseColumn?: ReleaseColumn;
|
|
50
|
+
/** A section of the release editor, above its settings. */
|
|
51
|
+
readonly releaseSection?: ComponentType<{
|
|
52
|
+
readonly release: ReleaseSlotInput;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** What each feature adds, keyed like `consoleFeatures`. */
|
|
57
|
+
export type ConsoleFeatureUiRegistry = {
|
|
58
|
+
readonly [F in ConsoleFeature]?: ConsoleFeatureUi;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const consoleFeatureUi: ConsoleFeatureUiRegistry = {
|
|
62
|
+
...insightsFeatureUi,
|
|
63
|
+
...apiKeysFeatureUi,
|
|
64
|
+
...remoteConfigFeatureUi,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const featureIds = Object.keys(consoleFeatures) as ConsoleFeature[];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* What the features that are on add, in the registry's order, with each
|
|
71
|
+
* feature's label; nothing until the features load.
|
|
72
|
+
*/
|
|
73
|
+
export const useFeatureUi = (): readonly (ConsoleFeatureUi & {
|
|
74
|
+
readonly feature: ConsoleFeature;
|
|
75
|
+
readonly label: string;
|
|
76
|
+
readonly features: ConsoleFeatures;
|
|
77
|
+
})[] => {
|
|
78
|
+
const features = useConsoleFeatures().data?.features;
|
|
79
|
+
if (features === undefined) return [];
|
|
80
|
+
return featureIds.flatMap((feature) => {
|
|
81
|
+
const ui = consoleFeatureUi[feature];
|
|
82
|
+
return ui !== undefined && features[feature]
|
|
83
|
+
? [{ ...ui, feature, label: consoleFeatures[feature].label, features }]
|
|
84
|
+
: [];
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/** The release list's feature columns, with their headers. */
|
|
89
|
+
export const useReleaseColumns = () =>
|
|
90
|
+
useFeatureUi().flatMap(({ feature, label, releaseColumn }) =>
|
|
91
|
+
releaseColumn === undefined ? [] : [{ ...releaseColumn, feature, label }],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/** The release editor's feature sections, for one release. */
|
|
95
|
+
export function ReleaseSections({
|
|
96
|
+
release,
|
|
97
|
+
}: {
|
|
98
|
+
readonly release: ReleaseSlotInput;
|
|
99
|
+
}) {
|
|
100
|
+
return useFeatureUi().map(({ feature, releaseSection: Section }) =>
|
|
101
|
+
Section === undefined ? null : <Section key={feature} release={release} />,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import { KeyRound } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import type { ConsoleFeatureUiRegistry } from "../FeatureSlots";
|
|
5
|
+
|
|
6
|
+
/** What API key management adds: its navigation item. */
|
|
7
|
+
export const apiKeysFeatureUi = {
|
|
8
|
+
apiKeys: {
|
|
9
|
+
navigation: {
|
|
10
|
+
icon: <KeyRound />,
|
|
11
|
+
paths: ["/api-keys"],
|
|
12
|
+
link: ({ onClick }) => <Link to="/api-keys" onClick={onClick} />,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
} satisfies ConsoleFeatureUiRegistry;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getPatchBaseFileHash, getPatchFileHash } from "@hot-updater/core";
|
|
2
1
|
import type { Bundle, ReleaseRow } from "@hot-updater/plugin-core";
|
|
2
|
+
import { getPatchBaseFileHash, getPatchFileHash } from "@hot-updater/protocol";
|
|
3
3
|
import { ExternalLink } from "lucide-react";
|
|
4
4
|
|
|
5
5
|
import { HashValueDisplay } from "@/components/HashValueDisplay";
|
|
@@ -36,7 +36,7 @@ export function BundleMetadata({ bundle, release }: BundleMetadataProps) {
|
|
|
36
36
|
const hasMetadata =
|
|
37
37
|
target ||
|
|
38
38
|
bundle?.gitCommitHash ||
|
|
39
|
-
bundle?.
|
|
39
|
+
bundle?.manifestFileHash ||
|
|
40
40
|
hbcPatchBaseFileHash ||
|
|
41
41
|
hbcPatchFileHash;
|
|
42
42
|
|
|
@@ -86,11 +86,14 @@ export function BundleMetadata({ bundle, release }: BundleMetadataProps) {
|
|
|
86
86
|
}
|
|
87
87
|
/>
|
|
88
88
|
) : null}
|
|
89
|
-
{bundle?.
|
|
89
|
+
{bundle?.manifestFileHash ? (
|
|
90
90
|
<Row
|
|
91
|
-
label="
|
|
91
|
+
label="Manifest hash"
|
|
92
92
|
value={
|
|
93
|
-
<HashValueDisplay
|
|
93
|
+
<HashValueDisplay
|
|
94
|
+
maxLength={16}
|
|
95
|
+
value={bundle.manifestFileHash}
|
|
96
|
+
/>
|
|
94
97
|
}
|
|
95
98
|
/>
|
|
96
99
|
) : null}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ChannelRow } from "@hot-updater/plugin-core";
|
|
2
2
|
import { Loader2, Plus, Tag, Trash2 } from "lucide-react";
|
|
3
3
|
import { type FormEvent, useState } from "react";
|
|
4
4
|
import { toast } from "sonner";
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
useCreateChannelMutation,
|
|
29
29
|
useDeleteChannelMutation,
|
|
30
30
|
} from "@/lib/api";
|
|
31
|
+
import { useDialogTarget } from "@/lib/use-dialog-target";
|
|
31
32
|
|
|
32
33
|
interface ChannelManagementDialogProps {
|
|
33
34
|
open: boolean;
|
|
@@ -42,6 +43,8 @@ export function ChannelManagementDialog({
|
|
|
42
43
|
const [channelToDelete, setChannelToDelete] = useState<ChannelRow | null>(
|
|
43
44
|
null,
|
|
44
45
|
);
|
|
46
|
+
// Kept while the confirmation animates closed.
|
|
47
|
+
const shownChannelToDelete = useDialogTarget(channelToDelete);
|
|
45
48
|
const { data: channels = [], isPending: isLoadingChannels } =
|
|
46
49
|
useChannelsQuery();
|
|
47
50
|
const createChannel = useCreateChannelMutation();
|
|
@@ -55,10 +58,7 @@ export function ChannelManagementDialog({
|
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
try {
|
|
58
|
-
const result = await createChannel.mutateAsync({
|
|
59
|
-
row: { id: createUUIDv7(), name: normalizedName },
|
|
60
|
-
onConflict: "returnExisting",
|
|
61
|
-
});
|
|
61
|
+
const result = await createChannel.mutateAsync({ name: normalizedName });
|
|
62
62
|
setChannelName("");
|
|
63
63
|
if (result.inserted) {
|
|
64
64
|
toast.success(`Channel ${result.row.name} created`);
|
|
@@ -199,7 +199,9 @@ export function ChannelManagementDialog({
|
|
|
199
199
|
>
|
|
200
200
|
<AlertDialogContent>
|
|
201
201
|
<AlertDialogHeader>
|
|
202
|
-
<AlertDialogTitle>
|
|
202
|
+
<AlertDialogTitle>
|
|
203
|
+
Delete {shownChannelToDelete?.name}?
|
|
204
|
+
</AlertDialogTitle>
|
|
203
205
|
<AlertDialogDescription>
|
|
204
206
|
Only empty channels can be removed. The server checks this when
|
|
205
207
|
you delete it, and the channel can be created again later with a
|
|
@@ -36,13 +36,18 @@ import {
|
|
|
36
36
|
TableHeader,
|
|
37
37
|
TableRow,
|
|
38
38
|
} from "@/components/ui/table";
|
|
39
|
-
import
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_INSIGHTS_RETENTION,
|
|
41
|
+
type InsightsRetentionDays,
|
|
42
|
+
keepsTwelveMonths,
|
|
43
|
+
} from "@/lib/insights-retention";
|
|
40
44
|
import type { InsightsSearch } from "@/lib/insights-search";
|
|
41
|
-
import type { AppUsageReport } from "@/lib/insights-usage";
|
|
45
|
+
import type { AppUsageReport, UsageWindow } from "@/lib/insights-usage";
|
|
42
46
|
|
|
47
|
+
import { EstimatedCount } from "./EstimatedCount";
|
|
43
48
|
import { InsightsErrorAlert } from "./InsightsErrorAlert";
|
|
44
49
|
import { InsightsInfo } from "./InsightsInfo";
|
|
45
|
-
import {
|
|
50
|
+
import { UsagePeriodSelector } from "./InsightsPeriodSelector";
|
|
46
51
|
import { ReportingDevicesSummary } from "./ReportingDevicesSummary";
|
|
47
52
|
|
|
48
53
|
const dates = new Intl.DateTimeFormat("en", {
|
|
@@ -62,23 +67,42 @@ export function AppUsage({
|
|
|
62
67
|
search,
|
|
63
68
|
window,
|
|
64
69
|
onWindowChange,
|
|
70
|
+
retention = DEFAULT_INSIGHTS_RETENTION,
|
|
65
71
|
}: {
|
|
66
72
|
readonly query: UseQueryResult<AppUsageReport>;
|
|
73
|
+
/** How long the server keeps Insights rows. */
|
|
74
|
+
readonly retention?: InsightsRetentionDays;
|
|
67
75
|
readonly search: InsightsSearch;
|
|
68
|
-
readonly window:
|
|
69
|
-
readonly onWindowChange: (window:
|
|
76
|
+
readonly window: UsageWindow;
|
|
77
|
+
readonly onWindowChange: (window: UsageWindow) => void;
|
|
70
78
|
}) {
|
|
71
79
|
const report = query.error ? undefined : query.data;
|
|
72
80
|
const formatTime = (ms: number) =>
|
|
73
|
-
(window === "30d" ? dates : times).format(ms);
|
|
81
|
+
(window === "30d" || window === "12m" ? dates : times).format(ms);
|
|
82
|
+
const distributionPeriod = report
|
|
83
|
+
? `since ${dates.format(report.distributionSinceMs)}, 00:00 UTC`
|
|
84
|
+
: "in the selected period";
|
|
85
|
+
const distributionTotals = report
|
|
86
|
+
? {
|
|
87
|
+
versions: report.versions.reduce(
|
|
88
|
+
(sum, row) => sum + row.installations,
|
|
89
|
+
0,
|
|
90
|
+
),
|
|
91
|
+
platforms: report.platforms.reduce(
|
|
92
|
+
(sum, row) => sum + row.installations,
|
|
93
|
+
0,
|
|
94
|
+
),
|
|
95
|
+
}
|
|
96
|
+
: undefined;
|
|
74
97
|
return (
|
|
75
98
|
<div className="grid min-w-0 gap-6 xl:grid-cols-[minmax(0,2fr)_minmax(16rem,1fr)]">
|
|
76
99
|
<Card aria-label="App usage" role="region" className="min-w-0 shadow-sm">
|
|
77
100
|
<CardHeader className="flex-row flex-wrap items-center justify-between gap-4">
|
|
78
101
|
<CardTitle>App usage</CardTitle>
|
|
79
|
-
<
|
|
102
|
+
<UsagePeriodSelector
|
|
80
103
|
window={window}
|
|
81
104
|
onWindowChange={onWindowChange}
|
|
105
|
+
twelveMonths={keepsTwelveMonths(retention)}
|
|
82
106
|
/>
|
|
83
107
|
</CardHeader>
|
|
84
108
|
<CardContent className="flex flex-col gap-4">
|
|
@@ -183,7 +207,11 @@ export function AppUsage({
|
|
|
183
207
|
<TableRow key={point.startMs}>
|
|
184
208
|
<TableCell>{times.format(point.startMs)}</TableCell>
|
|
185
209
|
<TableCell className="text-right tabular-nums">
|
|
186
|
-
{point.installations
|
|
210
|
+
{point.installations === null ? (
|
|
211
|
+
"Unknown"
|
|
212
|
+
) : (
|
|
213
|
+
<EstimatedCount value={point.installations} />
|
|
214
|
+
)}
|
|
187
215
|
</TableCell>
|
|
188
216
|
</TableRow>
|
|
189
217
|
))}
|
|
@@ -204,11 +232,9 @@ export function AppUsage({
|
|
|
204
232
|
Distribution
|
|
205
233
|
</CardTitle>
|
|
206
234
|
<InsightsInfo label="How distribution is counted">
|
|
207
|
-
Each
|
|
208
|
-
|
|
209
|
-
{report?.truncated
|
|
210
|
-
? " Shares reflect only the available history."
|
|
211
|
-
: ""}
|
|
235
|
+
Each installation counts once, by its latest report{" "}
|
|
236
|
+
{distributionPeriod}.
|
|
237
|
+
{report?.truncated ? " Some history is missing." : ""}
|
|
212
238
|
</InsightsInfo>
|
|
213
239
|
</CardHeader>
|
|
214
240
|
<CardContent className="flex-1">
|
|
@@ -259,7 +285,8 @@ export function AppUsage({
|
|
|
259
285
|
: "Android"
|
|
260
286
|
: row.name;
|
|
261
287
|
const share =
|
|
262
|
-
(row.installations /
|
|
288
|
+
(row.installations /
|
|
289
|
+
(distributionTotals?.[dimension] ?? 1)) *
|
|
263
290
|
100;
|
|
264
291
|
return (
|
|
265
292
|
<div
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A built-in bundle's ID: every native build makes it from its build time with
|
|
5
|
+
* the random bits zeroed (the CLI's `generateMinBundleId`, and the iOS and
|
|
6
|
+
* Android fallbacks), or the nil UUID when iOS can't read its build time. A
|
|
7
|
+
* deployed bundle's ID is a random UUIDv7, so it never has this shape.
|
|
8
|
+
*/
|
|
9
|
+
const BUILTIN_BUNDLE_ID =
|
|
10
|
+
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-7000-8000-000000000000|00000000-0000-0000-0000-000000000000)$/iu;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether a reported bundle is one a native build shipped: the report's
|
|
14
|
+
* `minBundleId`, or, from an SDK that reports none, an ID in the built-in
|
|
15
|
+
* shape.
|
|
16
|
+
*/
|
|
17
|
+
export const isBuiltinBundle = (
|
|
18
|
+
bundleId: string | null | undefined,
|
|
19
|
+
minBundleId: string | undefined,
|
|
20
|
+
): boolean =>
|
|
21
|
+
bundleId !== null &&
|
|
22
|
+
bundleId !== undefined &&
|
|
23
|
+
(bundleId === minBundleId || BUILTIN_BUNDLE_ID.test(bundleId));
|
|
24
|
+
|
|
25
|
+
/** Marks a bundle ID as the bundle its native build shipped. */
|
|
26
|
+
export function BuiltinBundleBadge() {
|
|
27
|
+
return (
|
|
28
|
+
<Badge
|
|
29
|
+
className="shrink-0 font-sans font-normal"
|
|
30
|
+
title="The bundle the native build shipped"
|
|
31
|
+
variant="secondary"
|
|
32
|
+
>
|
|
33
|
+
Built-in bundle
|
|
34
|
+
</Badge>
|
|
35
|
+
);
|
|
36
|
+
}
|