@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,196 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RemoteConfigActive,
|
|
3
|
+
RemoteConfigEvaluatedParameter,
|
|
4
|
+
RemoteConfigEvaluationContext,
|
|
5
|
+
RemoteConfigTemplateIssue,
|
|
6
|
+
RemoteConfigVersionDetail,
|
|
7
|
+
RemoteConfigVersionsPage,
|
|
8
|
+
} from "@hot-updater/server/plugins";
|
|
9
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
10
|
+
|
|
11
|
+
import { consoleAccess } from "./console-access";
|
|
12
|
+
import type { ConsolePublishResult } from "./server/remoteConfig";
|
|
13
|
+
|
|
14
|
+
export type { ConsolePublishResult } from "./server/remoteConfig";
|
|
15
|
+
|
|
16
|
+
/** What the preview shows: each parameter's value for the device, or what to fix first. */
|
|
17
|
+
export type RemoteConfigPreview =
|
|
18
|
+
| {
|
|
19
|
+
readonly status: "ok";
|
|
20
|
+
readonly parameters: Readonly<
|
|
21
|
+
Record<string, RemoteConfigEvaluatedParameter>
|
|
22
|
+
>;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
readonly status: "invalid";
|
|
26
|
+
readonly issues: readonly RemoteConfigTemplateIssue[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Remote Config, once the console's access check and the feature guard pass. */
|
|
30
|
+
const remoteConfig = async () => {
|
|
31
|
+
const [{ prepareConfig }, { requireFeature }] = await Promise.all([
|
|
32
|
+
import("./server/config.server"),
|
|
33
|
+
import("./server/runtime.server"),
|
|
34
|
+
]);
|
|
35
|
+
const { runtime } = await prepareConfig();
|
|
36
|
+
return requireFeature(runtime, "remoteConfig");
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const field = (input: unknown, key: string): unknown =>
|
|
40
|
+
typeof input === "object" && input !== null
|
|
41
|
+
? Reflect.get(input, key)
|
|
42
|
+
: undefined;
|
|
43
|
+
|
|
44
|
+
const versionNumber = (value: unknown, name: string): number => {
|
|
45
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
46
|
+
throw new TypeError(`${name} must be a non-negative integer.`);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const parseCursor = (input: unknown): { readonly cursor?: string } => {
|
|
52
|
+
const cursor = field(input, "cursor");
|
|
53
|
+
if (cursor !== undefined && typeof cursor !== "string") {
|
|
54
|
+
throw new TypeError("cursor must be text.");
|
|
55
|
+
}
|
|
56
|
+
return cursor === undefined ? {} : { cursor };
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const parseVersion = (input: unknown): { readonly version: number } => ({
|
|
60
|
+
version: versionNumber(field(input, "version"), "version"),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const parsePublish = (
|
|
64
|
+
input: unknown,
|
|
65
|
+
): {
|
|
66
|
+
readonly template: unknown;
|
|
67
|
+
readonly baseVersion: number;
|
|
68
|
+
readonly description?: string;
|
|
69
|
+
} => {
|
|
70
|
+
const description = field(input, "description");
|
|
71
|
+
if (description !== undefined && typeof description !== "string") {
|
|
72
|
+
throw new TypeError("description must be text.");
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
template: field(input, "template"),
|
|
76
|
+
baseVersion: versionNumber(field(input, "baseVersion"), "baseVersion"),
|
|
77
|
+
...(description === undefined ? {} : { description }),
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const parseRollback = (
|
|
82
|
+
input: unknown,
|
|
83
|
+
): { readonly version: number; readonly baseVersion: number } => ({
|
|
84
|
+
version: versionNumber(field(input, "version"), "version"),
|
|
85
|
+
baseVersion: versionNumber(field(input, "baseVersion"), "baseVersion"),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const CONTEXT_FIELDS = [
|
|
89
|
+
"platform",
|
|
90
|
+
"appVersion",
|
|
91
|
+
"channel",
|
|
92
|
+
"cohort",
|
|
93
|
+
"fingerprintHash",
|
|
94
|
+
] as const;
|
|
95
|
+
|
|
96
|
+
const parsePreview = (
|
|
97
|
+
input: unknown,
|
|
98
|
+
): {
|
|
99
|
+
readonly template: unknown;
|
|
100
|
+
readonly context: RemoteConfigEvaluationContext;
|
|
101
|
+
} => {
|
|
102
|
+
const raw = field(input, "context");
|
|
103
|
+
const context: Record<string, string> = {};
|
|
104
|
+
for (const key of CONTEXT_FIELDS) {
|
|
105
|
+
const value = field(raw, key);
|
|
106
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
107
|
+
context[key] = value.trim();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (
|
|
111
|
+
context.platform !== undefined &&
|
|
112
|
+
context.platform !== "ios" &&
|
|
113
|
+
context.platform !== "android"
|
|
114
|
+
) {
|
|
115
|
+
throw new TypeError('platform must be "ios" or "android".');
|
|
116
|
+
}
|
|
117
|
+
const now = field(raw, "now");
|
|
118
|
+
if (now !== undefined && (typeof now !== "number" || !Number.isFinite(now))) {
|
|
119
|
+
throw new TypeError("now must be a time in milliseconds since the epoch.");
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
template: field(input, "template"),
|
|
123
|
+
// Without a time, the preview evaluates at the server's clock, as devices do.
|
|
124
|
+
context: {
|
|
125
|
+
...(context as RemoteConfigEvaluationContext),
|
|
126
|
+
now: now ?? Date.now(),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const getRemoteConfigRpc = createServerFn({ method: "GET" })
|
|
132
|
+
.middleware([consoleAccess])
|
|
133
|
+
.handler(
|
|
134
|
+
async (): Promise<RemoteConfigActive> => (await remoteConfig()).getActive(),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
export const listRemoteConfigVersionsRpc = createServerFn({ method: "GET" })
|
|
138
|
+
.middleware([consoleAccess])
|
|
139
|
+
.validator(parseCursor)
|
|
140
|
+
.handler(
|
|
141
|
+
async ({ data }): Promise<RemoteConfigVersionsPage> =>
|
|
142
|
+
(await remoteConfig()).listVersions({ limit: 20, ...data }),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
export const getRemoteConfigVersionRpc = createServerFn({ method: "GET" })
|
|
146
|
+
.middleware([consoleAccess])
|
|
147
|
+
.validator(parseVersion)
|
|
148
|
+
.handler(
|
|
149
|
+
async ({ data }): Promise<RemoteConfigVersionDetail | null> =>
|
|
150
|
+
(await remoteConfig()).getVersion(data.version),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
export const publishRemoteConfigRpc = createServerFn({ method: "POST" })
|
|
154
|
+
.middleware([consoleAccess])
|
|
155
|
+
.validator(parsePublish)
|
|
156
|
+
.handler(
|
|
157
|
+
async ({ data }): Promise<ConsolePublishResult> =>
|
|
158
|
+
(await remoteConfig()).publish(data),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
export const rollbackRemoteConfigRpc = createServerFn({ method: "POST" })
|
|
162
|
+
.middleware([consoleAccess])
|
|
163
|
+
.validator(parseRollback)
|
|
164
|
+
.handler(
|
|
165
|
+
async ({
|
|
166
|
+
data,
|
|
167
|
+
}): Promise<ConsolePublishResult | { readonly status: "not_found" }> =>
|
|
168
|
+
(await remoteConfig()).rollback(data),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
/** What a device with `context` would receive from a template, published or not. */
|
|
172
|
+
export const previewRemoteConfigRpc = createServerFn({ method: "POST" })
|
|
173
|
+
.middleware([consoleAccess])
|
|
174
|
+
.validator(parsePreview)
|
|
175
|
+
.handler(async ({ data }): Promise<RemoteConfigPreview> => {
|
|
176
|
+
await remoteConfig();
|
|
177
|
+
const {
|
|
178
|
+
evaluateRemoteConfig,
|
|
179
|
+
RemoteConfigValidationError,
|
|
180
|
+
validateRemoteConfigTemplate,
|
|
181
|
+
} = await import("@hot-updater/server/plugins");
|
|
182
|
+
try {
|
|
183
|
+
return {
|
|
184
|
+
status: "ok",
|
|
185
|
+
parameters: evaluateRemoteConfig(
|
|
186
|
+
validateRemoteConfigTemplate(data.template),
|
|
187
|
+
data.context,
|
|
188
|
+
),
|
|
189
|
+
};
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (error instanceof RemoteConfigValidationError) {
|
|
192
|
+
return { status: "invalid", issues: error.issues };
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnyRouter, ParsedLocation } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
declare module "@tanstack/react-router" {
|
|
4
|
+
interface StaticDataRouteOption {
|
|
5
|
+
/**
|
|
6
|
+
* The route's scroll-restoration key for a location; without one, each
|
|
7
|
+
* history entry keeps its own scroll position.
|
|
8
|
+
*/
|
|
9
|
+
readonly scrollRestorationKey?: (location: ParsedLocation) => string;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** A location's scroll-restoration key: its route's own, else its history entry's. */
|
|
14
|
+
export const routeScrollRestorationKey = (
|
|
15
|
+
router: Pick<AnyRouter, "getMatchedRoutes">,
|
|
16
|
+
location: ParsedLocation,
|
|
17
|
+
): string => {
|
|
18
|
+
const [, , foundRoute] = router.getMatchedRoutes(location.pathname);
|
|
19
|
+
const routeKey = foundRoute?.options.staticData?.scrollRestorationKey;
|
|
20
|
+
return routeKey?.(location) ?? location.state.__TSR_key!;
|
|
21
|
+
};
|
|
@@ -13,11 +13,19 @@ const accessError = (
|
|
|
13
13
|
{ status: access.status === "unauthenticated" ? 401 : 403 },
|
|
14
14
|
);
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Server function middleware checks access before input, and prepareConfig
|
|
17
|
+
// checks again for its callers: one request asks the adapter once.
|
|
18
|
+
const accessByRequest = new WeakMap<Request, Promise<ConsoleAccess>>();
|
|
19
|
+
|
|
20
|
+
export const getConsoleAccess = (request: Request): Promise<ConsoleAccess> => {
|
|
21
|
+
let access = accessByRequest.get(request);
|
|
22
|
+
if (!access) {
|
|
23
|
+
access = getConsoleAuthAdapter().then((adapter) =>
|
|
24
|
+
adapter.getAccess(request),
|
|
25
|
+
);
|
|
26
|
+
accessByRequest.set(request, access);
|
|
27
|
+
}
|
|
28
|
+
return access;
|
|
21
29
|
};
|
|
22
30
|
|
|
23
31
|
export const getConsoleAuthProviders = async (
|
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
InsightsModel,
|
|
3
|
+
ReleaseReference,
|
|
4
|
+
} from "@hot-updater/server/plugins";
|
|
2
5
|
|
|
3
6
|
import type { BundleActivityInput } from "../bundle-activity";
|
|
4
|
-
import type { RecoveryReport } from "../insights-recovery";
|
|
5
|
-
import { readInsightsHistory } from "./insightsHistory";
|
|
6
|
-
import { buildRecoveryReport } from "./insightsRecovery";
|
|
7
7
|
|
|
8
8
|
export async function getBundleActivity(
|
|
9
9
|
model: InsightsModel,
|
|
10
10
|
inputs: readonly BundleActivityInput[],
|
|
11
|
-
now = Date.now(),
|
|
12
11
|
) {
|
|
13
12
|
if (inputs.length === 0) return {};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
13
|
+
const releases: ReleaseReference[] = inputs.map((input) => ({
|
|
14
|
+
releaseId: input.releaseId,
|
|
15
|
+
platform: input.platform,
|
|
16
|
+
channel: input.channel,
|
|
17
|
+
}));
|
|
18
|
+
const activity = await model.getReleaseActivity({ releases });
|
|
19
|
+
return Object.fromEntries(
|
|
20
|
+
activity.data.flatMap((item) =>
|
|
21
|
+
item.release === undefined
|
|
22
|
+
? []
|
|
23
|
+
: [
|
|
24
|
+
[
|
|
25
|
+
item.release.releaseId,
|
|
26
|
+
{
|
|
27
|
+
downloads: item.metrics.downloads,
|
|
28
|
+
launched: item.metrics.applies,
|
|
29
|
+
failedLaunches: item.metrics.failedLaunches,
|
|
30
|
+
measuredAtMs: activity.measuredAtMs,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
],
|
|
34
|
+
),
|
|
35
|
+
);
|
|
37
36
|
}
|
|
@@ -1,98 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
assertStorageOperations,
|
|
3
|
-
createDatabaseClient,
|
|
4
|
-
type DatabaseClient,
|
|
5
|
-
type StoragePluginWith,
|
|
6
|
-
} from "@hot-updater/plugin-core";
|
|
1
|
+
import type { HotUpdaterCoreApi } from "@hot-updater/plugin-core";
|
|
7
2
|
import { getRequest } from "@tanstack/react-start/server";
|
|
8
3
|
|
|
9
|
-
import type { ResolvedHotUpdaterConsoleConfig } from "../../index";
|
|
10
4
|
import { requireConsoleAccess } from "./auth.server";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
type ResolvedConsoleConfig
|
|
14
|
-
|
|
15
|
-
};
|
|
5
|
+
import {
|
|
6
|
+
resolveConsoleConfig,
|
|
7
|
+
type ResolvedConsoleConfig,
|
|
8
|
+
} from "./console-runtime.server";
|
|
9
|
+
import type { ConsoleRuntime } from "./runtime.server";
|
|
16
10
|
|
|
17
11
|
let configPromise: Promise<ResolvedConsoleConfig> | null = null;
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
typeof import("./runtime.server").createRuntimeHotUpdater
|
|
21
|
-
> | null = null;
|
|
22
|
-
let apiKeyStore: ReturnType<
|
|
23
|
-
typeof import("./runtime.server").createApiKeyStore
|
|
24
|
-
> | null = null;
|
|
25
|
-
let apiKeyStoreResolved = false;
|
|
26
|
-
let storagePluginPromise: Promise<
|
|
27
|
-
StoragePluginWith<"get" | "put" | "exists" | "delete">
|
|
28
|
-
> | null = null;
|
|
12
|
+
let core: HotUpdaterCoreApi | null = null;
|
|
13
|
+
let runtime: ConsoleRuntime | null = null;
|
|
29
14
|
|
|
30
15
|
const loadCachedConfig = async (request: Request) => {
|
|
31
16
|
if (!configPromise) {
|
|
32
|
-
configPromise = resolveConsoleConfig(request)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}))
|
|
37
|
-
.catch((error) => {
|
|
38
|
-
configPromise = null;
|
|
39
|
-
throw error;
|
|
40
|
-
});
|
|
17
|
+
configPromise = resolveConsoleConfig(request).catch((error) => {
|
|
18
|
+
configPromise = null;
|
|
19
|
+
throw error;
|
|
20
|
+
});
|
|
41
21
|
}
|
|
42
22
|
|
|
43
23
|
return configPromise;
|
|
44
24
|
};
|
|
45
25
|
|
|
46
|
-
const loadCachedStoragePlugin = async (config: ResolvedConsoleConfig) => {
|
|
47
|
-
if (!storagePluginPromise) {
|
|
48
|
-
storagePluginPromise = Promise.resolve(config.storage)
|
|
49
|
-
.then((storagePlugin) => {
|
|
50
|
-
assertStorageOperations(storagePlugin, [
|
|
51
|
-
"get",
|
|
52
|
-
"put",
|
|
53
|
-
"exists",
|
|
54
|
-
"delete",
|
|
55
|
-
]);
|
|
56
|
-
return storagePlugin;
|
|
57
|
-
})
|
|
58
|
-
.catch((error) => {
|
|
59
|
-
storagePluginPromise = null;
|
|
60
|
-
throw error;
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return storagePluginPromise;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
26
|
export const prepareConfig = async (request: Request = getRequest()) => {
|
|
68
27
|
try {
|
|
69
28
|
await requireConsoleAccess(request);
|
|
70
29
|
const config = await loadCachedConfig(request);
|
|
71
30
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (!hotUpdater) {
|
|
77
|
-
const { createRuntimeHotUpdater } = await import("./runtime.server");
|
|
78
|
-
hotUpdater = createRuntimeHotUpdater(config);
|
|
79
|
-
}
|
|
31
|
+
// Bundles, releases, catalogs, and channels: core assembled as the
|
|
32
|
+
// server's, on the server's own path, or a self-hosted server's admin API.
|
|
33
|
+
core ??= config.core;
|
|
80
34
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
35
|
+
// The console's features: those of the plugins its config lists.
|
|
36
|
+
if (!runtime) {
|
|
37
|
+
const { createConsoleRuntime } = await import("./runtime.server");
|
|
38
|
+
runtime = createConsoleRuntime(config);
|
|
85
39
|
}
|
|
86
40
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
config,
|
|
91
|
-
databaseClient,
|
|
92
|
-
hotUpdater,
|
|
93
|
-
apiKeyStore,
|
|
94
|
-
storagePlugin,
|
|
95
|
-
};
|
|
41
|
+
// Each bundle file is read and deleted with its protocol's storage.
|
|
42
|
+
return { config, core, runtime, storage: config.storage };
|
|
96
43
|
} catch (error) {
|
|
97
44
|
if (
|
|
98
45
|
!(
|
|
@@ -105,5 +52,3 @@ export const prepareConfig = async (request: Request = getRequest()) => {
|
|
|
105
52
|
throw error;
|
|
106
53
|
}
|
|
107
54
|
};
|
|
108
|
-
|
|
109
|
-
export const isConfigLoaded = () => Boolean(configPromise);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { RemoteDatabase } from "@hot-updater/plugin-core";
|
|
2
|
+
import {
|
|
3
|
+
type ApiKeyManagementAPI,
|
|
4
|
+
createInsightsAdminReads,
|
|
5
|
+
createInsightsModel,
|
|
6
|
+
createInsightsReads,
|
|
7
|
+
type InsightsApi,
|
|
8
|
+
type InsightsModel,
|
|
9
|
+
type RemoteConfigApi,
|
|
10
|
+
} from "@hot-updater/server/plugins";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
type ConsoleFeature,
|
|
14
|
+
ConsoleFeatureUnavailableError,
|
|
15
|
+
} from "../console-features";
|
|
16
|
+
import {
|
|
17
|
+
type ConsoleRemoteConfig,
|
|
18
|
+
createAdminRemoteConfig,
|
|
19
|
+
createLocalRemoteConfig,
|
|
20
|
+
} from "./remoteConfig";
|
|
21
|
+
|
|
22
|
+
/** A GET on a self-hosted server's admin handler. */
|
|
23
|
+
export type FetchAdmin = RemoteDatabase["fetchAdmin"];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* What serves one console feature: `local` builds it from the API of the
|
|
27
|
+
* feature's plugin, which the console assembles over the database as the
|
|
28
|
+
* server does; `remote` builds it over a self-hosted server's admin API, for
|
|
29
|
+
* a feature that API serves.
|
|
30
|
+
*/
|
|
31
|
+
export interface ConsoleFeatureApi<TPluginApi, TApi> {
|
|
32
|
+
readonly local: (pluginApi: TPluginApi) => TApi;
|
|
33
|
+
readonly remote?: (fetchAdmin: FetchAdmin) => TApi;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const featureApi = <TPluginApi, TApi>(
|
|
37
|
+
api: ConsoleFeatureApi<TPluginApi, TApi>,
|
|
38
|
+
): ConsoleFeatureApi<TPluginApi, TApi> => api;
|
|
39
|
+
|
|
40
|
+
/** What serves each console feature, keyed like `consoleFeatures`. */
|
|
41
|
+
export const consoleFeatureApis = {
|
|
42
|
+
insights: featureApi({
|
|
43
|
+
local: createInsightsReads,
|
|
44
|
+
remote: (fetchAdmin: FetchAdmin) =>
|
|
45
|
+
createInsightsAdminReads(fetchAdmin, {
|
|
46
|
+
unavailable: () =>
|
|
47
|
+
new ConsoleFeatureUnavailableError("insights", { remote: true }),
|
|
48
|
+
}),
|
|
49
|
+
}),
|
|
50
|
+
insightsAnalytics: featureApi({
|
|
51
|
+
local: (api: InsightsApi): InsightsModel => createInsightsModel(api),
|
|
52
|
+
}),
|
|
53
|
+
remoteConfig: featureApi({
|
|
54
|
+
local: (api: RemoteConfigApi): ConsoleRemoteConfig =>
|
|
55
|
+
createLocalRemoteConfig(api),
|
|
56
|
+
remote: createAdminRemoteConfig,
|
|
57
|
+
}),
|
|
58
|
+
apiKeys: featureApi({
|
|
59
|
+
local: (api: ApiKeyManagementAPI) => api,
|
|
60
|
+
}),
|
|
61
|
+
} satisfies {
|
|
62
|
+
readonly [F in ConsoleFeature]: ConsoleFeatureApi<never, unknown>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** What serves each console feature, as its server functions use it. */
|
|
66
|
+
export type ConsoleFeatureApis = {
|
|
67
|
+
readonly [F in ConsoleFeature]: ReturnType<
|
|
68
|
+
(typeof consoleFeatureApis)[F]["local"]
|
|
69
|
+
>;
|
|
70
|
+
};
|
|
@@ -1,42 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnyHotUpdaterPlugin,
|
|
3
|
+
type ConfiguredDatabase,
|
|
4
|
+
createMemoryAdapter,
|
|
5
|
+
type EngineDatabase,
|
|
6
|
+
type HotUpdaterCoreApi,
|
|
7
|
+
isRemoteDatabase,
|
|
8
|
+
type StorageAdapter,
|
|
9
|
+
} from "@hot-updater/plugin-core";
|
|
10
|
+
import { createHotUpdater } from "@hot-updater/server";
|
|
11
|
+
|
|
1
12
|
import type {
|
|
2
13
|
ConsoleAuthAdapter,
|
|
3
|
-
ConsoleSigningConfig,
|
|
4
14
|
HotUpdaterConsoleConfigSource,
|
|
5
|
-
ResolvedHotUpdaterConsoleConfig,
|
|
6
15
|
} from "../../index";
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
/** What the console runs on, assembled from its config as the server is. */
|
|
18
|
+
export interface ResolvedConsoleConfig {
|
|
19
|
+
readonly gitUrl?: string;
|
|
20
|
+
/** The server's database, or its admin API. */
|
|
21
|
+
readonly database: ConfiguredDatabase;
|
|
22
|
+
/**
|
|
23
|
+
* Core's API, assembled over the database as the server assembles it, so
|
|
24
|
+
* the console's writes take the server's path, or a self-hosted server's
|
|
25
|
+
* admin API.
|
|
26
|
+
*/
|
|
27
|
+
readonly core: HotUpdaterCoreApi;
|
|
28
|
+
/** The server's storage, which reads the bundle files. */
|
|
29
|
+
readonly storage: StorageAdapter;
|
|
30
|
+
/** The plugins the server runs, as the config lists them. */
|
|
31
|
+
readonly plugins: readonly AnyHotUpdaterPlugin[];
|
|
32
|
+
/**
|
|
33
|
+
* The plugins' APIs by id, which serve their features; absent over a
|
|
34
|
+
* self-hosted server's admin API, whose plugins run on the server.
|
|
35
|
+
*/
|
|
36
|
+
readonly api?: Readonly<Record<string, unknown>>;
|
|
37
|
+
}
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
enabled: true,
|
|
37
|
-
provider: getProviderName(signing),
|
|
38
|
-
};
|
|
39
|
-
};
|
|
39
|
+
/**
|
|
40
|
+
* Core and the plugins over `database`, assembled by `createHotUpdater` as
|
|
41
|
+
* the server assembles them, with public client routes unless a plugin
|
|
42
|
+
* provides clientAuth: the console mounts no handlers. cli-tools'
|
|
43
|
+
* `assembleServer` does the same for the CLI; the console keeps its own
|
|
44
|
+
* copy, since a hosted console never loads cli-tools.
|
|
45
|
+
*/
|
|
46
|
+
const assemble = (
|
|
47
|
+
database: EngineDatabase,
|
|
48
|
+
storage: StorageAdapter,
|
|
49
|
+
plugins: readonly AnyHotUpdaterPlugin[],
|
|
50
|
+
) =>
|
|
51
|
+
createHotUpdater({
|
|
52
|
+
database,
|
|
53
|
+
storage,
|
|
54
|
+
plugins,
|
|
55
|
+
...(plugins.some((plugin) => plugin.provides?.clientAuth)
|
|
56
|
+
? {}
|
|
57
|
+
: { clientAccess: "public" }),
|
|
58
|
+
} as Parameters<typeof createHotUpdater>[0]);
|
|
40
59
|
|
|
41
60
|
export const getConsoleAuthAdapter = async (): Promise<ConsoleAuthAdapter> => {
|
|
42
61
|
const module = await import("virtual:hot-updater-console/auth");
|
|
@@ -45,17 +64,42 @@ export const getConsoleAuthAdapter = async (): Promise<ConsoleAuthAdapter> => {
|
|
|
45
64
|
|
|
46
65
|
export const resolveConsoleConfig = async (
|
|
47
66
|
request: Request,
|
|
48
|
-
): Promise<
|
|
67
|
+
): Promise<ResolvedConsoleConfig> => {
|
|
49
68
|
const { default: source } =
|
|
50
69
|
(await import("virtual:hot-updater-console/config")) as {
|
|
51
70
|
readonly default: HotUpdaterConsoleConfigSource;
|
|
52
71
|
};
|
|
53
72
|
|
|
54
73
|
const config = typeof source === "function" ? await source(request) : source;
|
|
55
|
-
const {
|
|
56
|
-
const
|
|
74
|
+
const { database, storage, plugins = [] } = config;
|
|
75
|
+
const gitUrl =
|
|
76
|
+
config.console?.gitUrl === undefined
|
|
77
|
+
? {}
|
|
78
|
+
: { gitUrl: config.console.gitUrl };
|
|
79
|
+
// A self-hosted server runs the plugins itself: core is its admin API.
|
|
80
|
+
// The plugins are still checked as the server checks them, on a database
|
|
81
|
+
// nothing reads, so one the server would refuse is refused here too.
|
|
82
|
+
if (isRemoteDatabase(database)) {
|
|
83
|
+
const checked = assemble(
|
|
84
|
+
{ name: "memory", adapter: createMemoryAdapter() },
|
|
85
|
+
storage,
|
|
86
|
+
plugins,
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
...gitUrl,
|
|
90
|
+
database,
|
|
91
|
+
core: database.core,
|
|
92
|
+
storage,
|
|
93
|
+
plugins: checked.plugins,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const server = assemble(database, storage, plugins);
|
|
57
97
|
return {
|
|
58
|
-
...
|
|
59
|
-
|
|
98
|
+
...gitUrl,
|
|
99
|
+
database,
|
|
100
|
+
core: server.core,
|
|
101
|
+
storage,
|
|
102
|
+
plugins: server.plugins,
|
|
103
|
+
api: server.api,
|
|
60
104
|
};
|
|
61
105
|
};
|