@hot-updater/console 1.1.4 → 1.2.0-rc.1
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/-native-builds-search-C-TGpFAm.js +1 -0
- package/.output/public/assets/{ConsoleFeatureUnavailable-C0KfsKWL.js → ConsoleFeatureUnavailable-CUtcXuY2.js} +1 -1
- package/.output/public/assets/EventDetails-Dl2RvPAk.js +1 -0
- package/.output/public/assets/HashValueDisplay-6lNRxDJX.js +1 -0
- package/.output/public/assets/InsightsPageHeader-DHhqY3DT.js +1 -0
- package/.output/public/assets/NotFoundPage-DZ3DxyRA.js +1 -0
- package/.output/public/assets/PlatformIcon-BBWQlWm9.js +1 -0
- package/.output/public/assets/RolloutPercentageBadge-Cxogpi8F.js +1 -0
- package/.output/public/assets/StoreSubmission-JpngSvHS.js +1 -0
- package/.output/public/assets/alert-dialog-BqYScr9y.js +1 -0
- package/.output/public/assets/api-P2g8dCNg.js +1 -0
- package/.output/public/assets/api-keys-DWQC2q6y.js +1 -0
- package/.output/public/assets/api-keys-FueRkF9q.js +1 -0
- package/.output/public/assets/arrow-left-g9cKqXuC.js +1 -0
- package/.output/public/assets/arrow-right-CNII8TYn.js +1 -0
- package/.output/public/assets/arrow-up-right-DK8Zdg40.js +1 -0
- package/.output/public/assets/badge-DWFcDWho.js +1 -0
- package/.output/public/assets/check-CXVR3s3X.js +1 -0
- package/.output/public/assets/chevron-right-BoUXo82O.js +1 -0
- package/.output/public/assets/circle-off-C7Muo0ss.js +1 -0
- package/.output/public/assets/{clipboard-DTkrlIMn.js → clipboard-DQbOcrL7.js} +1 -1
- package/.output/public/assets/console-features-api-DEXSwFtc.js +1 -0
- package/.output/public/assets/copy-DsuLj3oj.js +1 -0
- package/.output/public/assets/dialog-CNUPxNxH.js +1 -0
- package/.output/public/assets/{dist-dDj-R6uh.js → dist-CPlO6ko9.js} +1 -1
- package/.output/public/assets/download-Cf24LBde.js +1 -0
- package/.output/public/assets/{empty-Bf4DanOL.js → empty-B3Uwdd6k.js} +1 -1
- package/.output/public/assets/{field-GxF9SyUo.js → field-D-2-mnNh.js} +1 -1
- package/.output/public/assets/history-DqHlgSuV.js +1 -0
- package/.output/public/assets/index-BWU6owTG.js +10 -0
- package/.output/public/assets/{input-group-B4nrqIh0.js → input-group-D3OUBk4n.js} +1 -1
- package/.output/public/assets/insights-B-0odqpt.js +42 -0
- package/.output/public/assets/insights-BSVGfxSL.js +1 -0
- package/.output/public/assets/insights-NK5B6ReS.js +2 -0
- package/.output/public/assets/insights-api-BVCXJsoV.js +1 -0
- package/.output/public/assets/insights-search-DghCbEnk.js +1 -0
- package/.output/public/assets/insights-usage-rpc-CKQH0pUS.js +1 -0
- package/.output/public/assets/insights_.distribution-CJvgwcVZ.js +2 -0
- package/.output/public/assets/insights_.distribution-DblZM0mO.js +1 -0
- package/.output/public/assets/insights_.distribution-dvk6vYDM.js +1 -0
- package/.output/public/assets/installations-B674P5Z5.js +2 -0
- package/.output/public/assets/installations-BWxczZ2t.js +1 -0
- package/.output/public/assets/installations-Dv4kWQYz.js +1 -0
- package/.output/public/assets/{key-round-B4fI1psK.js → key-round-DwNKkHhy.js} +1 -1
- package/.output/public/assets/link-DTEyW70e.js +2 -0
- package/.output/public/assets/{list-Dtj7TtzV.js → list--NIee1FZ.js} +1 -1
- package/.output/public/assets/native-builds-BOC71grI.js +1 -0
- package/.output/public/assets/native-builds-BswZazmc.js +1 -0
- package/.output/public/assets/native-builds-c4BpOLAe.js +2 -0
- package/.output/public/assets/native-builds_._buildId-D_Spjl10.js +1 -0
- package/.output/public/assets/native-builds_._buildId-DtoXD8rz.js +2 -0
- package/.output/public/assets/plus-BReVKct-.js +1 -0
- package/.output/public/assets/preload-helper-B44EvKhG.js +1 -0
- package/.output/public/assets/{refresh-cw-DjIKjmrS.js → refresh-cw-B9HHjLej.js} +1 -1
- package/.output/public/assets/relativeTime-YQt84dYr.js +1 -0
- package/.output/public/assets/remote-config-B7ujqhLa.js +6 -0
- package/.output/public/assets/remote-config-WjpozBG5.js +1 -0
- package/.output/public/assets/remote-config-gXeH6PBW.js +1 -0
- package/.output/public/assets/rotate-cw-Cv2aVbYr.js +1 -0
- package/.output/public/assets/routes-BDdBIxYS.js +1 -0
- package/.output/public/assets/routes-CCD33Azv.js +2 -0
- package/.output/public/assets/search-BCel4Oy6.js +1 -0
- package/.output/public/assets/select-CBIU0_M1.js +1 -0
- package/.output/public/assets/{sidebar-BqX460IW.js → sidebar-Di9SN47G.js} +1 -1
- package/.output/public/assets/smartphone-CMaSy_ir.js +1 -0
- package/.output/public/assets/styles-Ccxthuz1.css +2 -0
- package/.output/public/assets/switch-BpdiHL_t.js +1 -0
- package/.output/public/assets/{table-ek1DmRHq.js → table-Bpy3SvSo.js} +1 -1
- package/.output/public/assets/{tabs-CgEH_fMT.js → tabs-Xmt7tZeG.js} +1 -1
- package/.output/public/assets/{undo-2-DxXx9Zvr.js → undo-2-CUX96_BG.js} +1 -1
- package/.output/public/assets/useCompositeListItem-JmyDfo3-.js +1 -0
- package/.output/public/assets/useInfiniteQuery-DX4WpBRG.js +1 -0
- package/.output/public/assets/useMutation-DbYw7boA.js +1 -0
- package/.output/public/assets/uuidv7-zXMtwR9Q.js +1 -0
- package/.output/public/assets/{valueToPercent-B44vDssN.js → valueToPercent-BLwGy_xK.js} +1 -1
- package/.output/server/{__23tanstack-start-server-fn-resolver-KpQ5Z_24.mjs → __23tanstack-start-server-fn-resolver-CygMPYHZ.mjs} +64 -40
- package/.output/server/_chunks/ssr-renderer.mjs +2 -2
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +1 -1
- package/.output/server/_libs/lucide-react.mjs +88 -1
- package/.output/server/_libs/uqr.mjs +858 -0
- package/.output/server/_ssr/-native-builds-search-CwNcoXf4.mjs +37 -0
- package/.output/server/_ssr/{BundleIdDisplay-CjSnY65J.mjs → BundleIdDisplay-DOPz4pUS.mjs} +2 -2
- package/.output/server/_ssr/{ConsoleFeatureUnavailable-CHxn9f-w.mjs → ConsoleFeatureUnavailable-Xb1JKqcc.mjs} +7 -7
- package/.output/server/_ssr/{EventDetails-l6dF54zp.mjs → EventDetails-D7MejFCN.mjs} +8 -8
- package/.output/server/_ssr/{HashValueDisplay-Zs7ABoOi.mjs → HashValueDisplay-Dd-wu5rY.mjs} +2 -2
- package/.output/server/_ssr/{InsightsPageHeader-ZlAlDxQy.mjs → InsightsPageHeader-CWf1n4Vt.mjs} +13 -11
- package/.output/server/_ssr/{NotFoundPage-u28ndvp1.mjs → NotFoundPage-C5rFdUpg.mjs} +2 -2
- package/.output/server/_ssr/PlatformIcon-CFLiTjh1.mjs +29 -0
- package/.output/server/_ssr/RolloutPercentageBadge-g0Bhfl3M.mjs +21 -0
- package/.output/server/_ssr/StoreSubmission-JZ2Zltak.mjs +139 -0
- package/.output/server/_ssr/{alert-dialog-B8AqlJwo.mjs → alert-dialog-CvNvTvIJ.mjs} +1 -1
- package/.output/server/_ssr/{api-C_0B7NXM.mjs → api-0vtwA5vY.mjs} +126 -7
- package/.output/server/_ssr/{api-keys-pRNvn2-9.mjs → api-keys-D5dyA4D1.mjs} +10 -10
- package/.output/server/_ssr/{api-keys-rpc-wecWs0nA.mjs → api-keys-rpc-BOIUHhDi.mjs} +5 -5
- package/.output/server/_ssr/{api-keys-CLH38u9B.mjs → api-keys-yFlQDwQF.mjs} +2 -2
- package/.output/server/_ssr/{api-rpc-DWN8eOHS.mjs → api-rpc-COeFOpYi.mjs} +79 -12
- package/.output/server/_ssr/{auth-UP0wXs4i.mjs → auth-Z-wLddzh.mjs} +1 -1
- package/.output/server/_ssr/{auth-rpc-CLRArbIl.mjs → auth-rpc-D1ss2GUI.mjs} +6 -6
- package/.output/server/_ssr/{auth.server-DR9zc0EU.mjs → auth.server-DYS1OJ6P.mjs} +771 -641
- package/.output/server/_ssr/{badge-BpT7r-ng.mjs → badge-CEdVRgyl.mjs} +1 -1
- package/.output/server/_ssr/{bundleActivity-0qFIcvqt.mjs → bundleActivity-MLrjNJCT.mjs} +1 -1
- package/.output/server/_ssr/{config-WVX4IxSR.mjs → config-CjIBFhJX.mjs} +1 -1
- package/.output/server/_ssr/config.server-NnMFjMFX.mjs +43 -0
- package/.output/server/_ssr/{console-access-aUpQs49I.mjs → console-access-33BNlH97.mjs} +3 -3
- package/.output/server/_ssr/{console-features-4xAGfZHw.mjs → console-features-BTV-XuhQ.mjs} +1 -1
- package/.output/server/_ssr/{console-features-api-DSSgCV3v.mjs → console-features-api-v1fSycue.mjs} +6 -6
- package/.output/server/_ssr/{console-features-rpc-D6kMlR2A.mjs → console-features-rpc-C7XNVqm0.mjs} +5 -5
- package/.output/server/_ssr/createCsrfMiddleware-BwHbC8n-.mjs +159 -0
- package/.output/server/_ssr/createMiddleware-C3ACsfuG.mjs +24 -0
- package/.output/server/_ssr/{createMiddleware-B1iPez7u.mjs → createServerFn-AQvBMBjJ.mjs} +2 -23
- package/.output/server/_ssr/{createServerRpc-BaDJLGBx.mjs → createServerRpc-BVvRVOz5.mjs} +2 -2
- package/.output/server/_ssr/{databaseConfig-C5SkAyUy.mjs → databaseConfig-B_Bg7qS5.mjs} +1 -1
- package/.output/server/_ssr/{databaseJsonValue-Csbuzc9Q.mjs → databaseJsonValue-kS7cvEtz.mjs} +1 -1
- package/.output/server/_ssr/{databaseRows-CPphvbag.mjs → databaseRows-B6H8Cmgq.mjs} +3 -3
- package/.output/server/_ssr/{dialog-B4ALf7NJ.mjs → dialog-C8CS8RMt.mjs} +1 -1
- package/.output/server/_ssr/{dist-CbQbBIw-.mjs → dist-TEpM_UAD.mjs} +125 -125
- package/.output/server/_ssr/{downloadBundle-94jPBGSO.mjs → downloadBundle-DCmr7vsj.mjs} +1 -1
- package/.output/server/_ssr/{empty-Bh1FEeYF.mjs → empty-CkdEAvCY.mjs} +2 -2
- package/.output/server/_ssr/{empty-plugin-adapters-CpCHi1TV.mjs → empty-plugin-adapters-zrZ0xs6n.mjs} +1 -1
- package/.output/server/_ssr/{failureReports-Bh22wois.mjs → failureReports-Cn_zU9S8.mjs} +3 -3
- package/.output/server/_ssr/{field-HKCaPH3e.mjs → field-CW08sZXd.mjs} +2 -2
- package/.output/server/_ssr/{getBundleChildren-DfxB2K8D.mjs → getBundleChildren-rtkEEJw5.mjs} +2 -2
- package/.output/server/_ssr/git-CTA8Aqr0.mjs +56 -0
- package/.output/server/_ssr/{input-group-xm3LzDuq.mjs → input-group-D-TpFPuG.mjs} +1 -1
- package/.output/server/_ssr/{insights-BKZpJ_K4.mjs → insights-CMyzPs8l.mjs} +28 -27
- package/.output/server/_ssr/{insights-CCyTACIw.mjs → insights-CYeBl8N9.mjs} +5 -5
- package/.output/server/_ssr/{insights-CNqWQLJR.mjs → insights-DAi9KknT.mjs} +2 -2
- package/.output/server/_ssr/{insights-api-DtRFz5X3.mjs → insights-api-BFARQ5ct.mjs} +6 -6
- package/.output/server/_ssr/{insights-errors-Iy-lBaM7.mjs → insights-errors-7BCwFybo.mjs} +2 -2
- package/.output/server/_ssr/{insights-failures-BJN3RfLu.mjs → insights-failures-B9VPkzZE.mjs} +2 -2
- package/.output/server/_ssr/{insights-failures-rpc-DLZ91uOG.mjs → insights-failures-rpc-Q4WNNJkU.mjs} +12 -12
- package/.output/server/_ssr/{insights-recovery-BUuQdlA3.mjs → insights-recovery-BtXVU3ro.mjs} +1 -1
- package/.output/server/_ssr/{insights-recovery-rpc-BQHUhPi7.mjs → insights-recovery-rpc-CY5RYJKa.mjs} +8 -8
- package/.output/server/_ssr/{insights-rpc-C6Vukyn7.mjs → insights-rpc-BO7_evvs.mjs} +5 -5
- package/.output/server/_ssr/{insights-search-CBYoxT7v.mjs → insights-search-DKfxgHD3.mjs} +5 -5
- package/.output/server/_ssr/{insights-usage-CHqvtehH.mjs → insights-usage-DiGnuxW8.mjs} +1 -1
- package/.output/server/_ssr/{insights-usage-rpc-BJ7a-xco.mjs → insights-usage-rpc-B4czku50.mjs} +8 -8
- package/.output/server/_ssr/{insights-usage-rpc-C-lNUIsD.mjs → insights-usage-rpc-CUCTFska.mjs} +5 -5
- package/.output/server/_ssr/{insightsUsage-COUG-_-U.mjs → insightsUsage-ClAmoUz8.mjs} +3 -3
- package/.output/server/_ssr/{insights_.distribution-DmLW8I8a.mjs → insights_.distribution-BDeUhZGj.mjs} +2 -2
- package/.output/server/_ssr/{insights_.distribution-CTwilMPm.mjs → insights_.distribution-BRrbZN1X.mjs} +5 -5
- package/.output/server/_ssr/{insights_.distribution-DztDyrBj.mjs → insights_.distribution-C2MXpzKd.mjs} +15 -14
- package/.output/server/_ssr/{installations-Bct7ag5y.mjs → installations-BVk7okiY.mjs} +4 -4
- package/.output/server/_ssr/{installations-B6MK5iA8.mjs → installations-BjLMgBoH.mjs} +2 -2
- package/.output/server/_ssr/{installations-_N4PtYFl.mjs → installations-DxGZEC5G.mjs} +12 -12
- package/.output/server/_ssr/{listReleases-BfUBlCHo.mjs → listReleases-D_GY00SQ.mjs} +1 -1
- package/.output/server/_ssr/native-builds-CSCgFB21.mjs +10 -0
- package/.output/server/_ssr/native-builds-CdlyAldW.mjs +569 -0
- package/.output/server/_ssr/native-builds-DhXL5zf_.mjs +137 -0
- package/.output/server/_ssr/native-builds_._buildId-CMp1h8dT.mjs +10 -0
- package/.output/server/_ssr/native-builds_._buildId-GHpGaYyE.mjs +813 -0
- package/.output/server/_ssr/nativeBuildQr-84cnFQPX.mjs +59 -0
- package/.output/server/_ssr/nativeBuilds-B_7BPjt_.mjs +133 -0
- package/.output/server/_ssr/{plugins-BaD1slHx.mjs → plugins-RIryUNWj.mjs} +6 -5
- package/.output/server/_ssr/qr-code-C7kNRIDX.mjs +22 -0
- package/.output/server/_ssr/{release-adoption-DuYJTGuX.mjs → release-adoption-DoAtNPED.mjs} +2 -2
- package/.output/server/_ssr/{release-adoption-rpc-CHl7G_Zy.mjs → release-adoption-rpc-BJXZvtoQ.mjs} +10 -10
- package/.output/server/_ssr/{releaseAdoption-B39cpvfo.mjs → releaseAdoption-a2byXMfz.mjs} +4 -4
- package/.output/server/_ssr/releaseCatalogCompiler-DLcean2k.mjs +364 -0
- package/.output/server/_ssr/{releaseManagement-DFMUnDF5.mjs → releaseManagement-DHUux2az.mjs} +1 -1
- package/.output/server/_ssr/{releaseReachability-bFsuElSS.mjs → releaseReachability-DZOK_dH-.mjs} +2 -2
- package/.output/server/_ssr/{remote-config-Mj05qVYZ.mjs → remote-config-DjsEl270.mjs} +20 -19
- package/.output/server/_ssr/{remote-config-10-obSZ0.mjs → remote-config-DlyyohL2.mjs} +2 -2
- package/.output/server/_ssr/{remote-config-Dj1eTYi4.mjs → remote-config-XdYuByHz.mjs} +2 -2
- package/.output/server/_ssr/{remote-config-rpc-lteqDSLn.mjs → remote-config-rpc-GMLPb-90.mjs} +6 -6
- package/.output/server/_ssr/request-guard-CPdRBuGW.mjs +108 -0
- package/.output/server/_ssr/{router-Dr4jjjhx.mjs → router-vpxd56RW.mjs} +317 -71
- package/.output/server/_ssr/{routes-BZbrY_qw.mjs → routes-DyEM4b3x.mjs} +14 -14
- package/.output/server/_ssr/{routes-B5KrCDwk.mjs → routes-dQe3XhiS.mjs} +99 -81
- package/.output/server/_ssr/{runtime.server-DsHqVqTr.mjs → runtime.server-BdMY1blX.mjs} +4 -4
- package/.output/server/_ssr/schema-ChlRtZIo.mjs +23 -0
- package/.output/server/_ssr/schema-migration-rpc-BfGzEK_o.mjs +65 -0
- package/.output/server/_ssr/{select-BtdGNSmp.mjs → select-DM1uwtk9.mjs} +3 -27
- package/.output/server/_ssr/{server-BxaHSwHh.mjs → server-BUUKNSPI.mjs} +7 -161
- package/.output/server/_ssr/{sidebar-CVsgosaD.mjs → sidebar-DB2YQlok.mjs} +4 -4
- package/.output/server/_ssr/ssr.mjs +1 -1
- package/.output/server/_ssr/start-BvDsQehg.mjs +49 -0
- package/.output/server/_ssr/{switch-BmARgkUP.mjs → switch-DLD58zLy.mjs} +2 -2
- package/.output/server/_ssr/{table-M5P3YUfY.mjs → table-P4TnAZhl.mjs} +1 -1
- package/.output/server/_ssr/{tabs-CrmfOlJj.mjs → tabs-B7ZeLicF.mjs} +2 -2
- package/.output/server/_ssr/{updateFailures-z1IQy_4q.mjs → updateFailures-n-tGv90S.mjs} +3 -3
- package/.output/server/_ssr/{uuidv7-C88s9jWF.mjs → uuidv7-BIF4nb_q.mjs} +2 -2
- package/.output/server/_ssr/{schema-BiAVyEj2.mjs → version-CQ98ZBpL-CWSkTT9J.mjs} +2 -22
- package/.output/server/_tanstack-start-manifest_v-C-KC63Zq.mjs +184 -0
- package/.output/server/index.mjs +432 -271
- package/lib/vite-internal.mjs +32 -1
- package/package.json +8 -7
- package/src/components/AppSidebar.tsx +12 -1
- package/src/components/ConsoleAccessPage.tsx +49 -3
- package/src/components/QrCode.tsx +35 -0
- package/src/components/SchemaMigrationRequired.tsx +139 -0
- package/src/components/features/insights/BuiltinBundle.tsx +5 -3
- package/src/components/features/native-builds/BuiltInBundleCard.tsx +163 -0
- package/src/components/features/native-builds/CommitLink.tsx +30 -0
- package/src/components/features/native-builds/CopyableText.tsx +48 -0
- package/src/components/features/native-builds/ImportBuild.tsx +91 -0
- package/src/components/features/native-builds/InstallCard.tsx +205 -0
- package/src/components/features/native-builds/NativeBuildDetailPage.tsx +175 -0
- package/src/components/features/native-builds/NativeBuildsPage.tsx +151 -0
- package/src/components/features/native-builds/NativeBuildsTable.tsx +426 -0
- package/src/components/features/native-builds/PatchesCard.tsx +222 -0
- package/src/components/features/native-builds/ReleaseNativeBuildLink.tsx +101 -0
- package/src/components/features/native-builds/ServedNowCard.tsx +214 -0
- package/src/components/features/native-builds/ShortHash.tsx +41 -0
- package/src/components/features/native-builds/StoreSubmission.tsx +73 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +8 -0
- package/src/lib/api-rpc.ts +98 -0
- package/src/lib/api.ts +120 -0
- package/src/lib/native-builds.ts +162 -0
- package/src/lib/qr-code.ts +16 -0
- package/src/lib/schema-migration-api.ts +54 -0
- package/src/lib/schema-migration-rpc.ts +26 -0
- package/src/lib/schema-migration.ts +101 -0
- package/src/lib/server/config.server.ts +23 -14
- package/src/lib/server/console-runtime.server.ts +4 -6
- package/src/lib/server/local-console.ts +164 -0
- package/src/lib/server/nativeBuildQr.ts +73 -0
- package/src/lib/server/nativeBuilds.ts +288 -0
- package/src/lib/shell.ts +9 -0
- package/src/routeTree.gen.ts +88 -1
- package/src/router.tsx +11 -1
- package/src/routes/-native-builds-search.ts +51 -0
- package/src/routes/[_]hot-updater/native-builds/qr.ts +14 -0
- package/src/routes/__root.tsx +11 -1
- package/src/routes/api/native-builds/$buildId/download.ts +50 -0
- package/src/routes/native-builds.tsx +21 -0
- package/src/routes/native-builds_.$buildId.tsx +23 -0
- package/src/start.ts +33 -0
- package/src/virtual-modules.d.ts +18 -0
- package/src/vite-internal.ts +57 -1
- package/.output/public/assets/EventDetails-gbD_fCqc.js +0 -1
- package/.output/public/assets/HashValueDisplay-BGkDPaYi.js +0 -1
- package/.output/public/assets/InsightsPageHeader-BRw8XkJW.js +0 -1
- package/.output/public/assets/NotFoundPage-Cei6W2lm.js +0 -1
- package/.output/public/assets/alert-dialog-BtVg50WQ.js +0 -1
- package/.output/public/assets/api-DoicFJfd.js +0 -1
- package/.output/public/assets/api-keys-D81VHHNM.js +0 -1
- package/.output/public/assets/api-keys-Dia6i8K1.js +0 -1
- package/.output/public/assets/arrow-left-DsivSewE.js +0 -1
- package/.output/public/assets/arrow-up-right-B5aM0F7f.js +0 -1
- package/.output/public/assets/badge-Bqf3UDee.js +0 -1
- package/.output/public/assets/circle-off-BsbZQtsF.js +0 -1
- package/.output/public/assets/console-features-api-DPE4C06Y.js +0 -2
- package/.output/public/assets/dialog-CGKPxz3n.js +0 -1
- package/.output/public/assets/index-DrkdiRIn.js +0 -10
- package/.output/public/assets/insights-C-5t8wh2.js +0 -2
- package/.output/public/assets/insights-D_ngQdiz.js +0 -1
- package/.output/public/assets/insights-KigRBDgC.js +0 -42
- package/.output/public/assets/insights-api-1ZjVu3Sb.js +0 -1
- package/.output/public/assets/insights-search-aT8BVCci.js +0 -1
- package/.output/public/assets/insights-usage-rpc-DqTiXI1_.js +0 -1
- package/.output/public/assets/insights_.distribution-Bzl6RFBR.js +0 -2
- package/.output/public/assets/insights_.distribution-Cthgttqb.js +0 -1
- package/.output/public/assets/insights_.distribution-D2c2v1bu.js +0 -1
- package/.output/public/assets/installations-2Nx8Htf-.js +0 -1
- package/.output/public/assets/installations-BskJ1xQr.js +0 -2
- package/.output/public/assets/installations-K2iJ_r0h.js +0 -1
- package/.output/public/assets/remote-config-D9X3NiwV.js +0 -6
- package/.output/public/assets/remote-config-DzOOiBIO.js +0 -1
- package/.output/public/assets/remote-config-ikg7kak0.js +0 -1
- package/.output/public/assets/routes-9rb9P5Y7.js +0 -2
- package/.output/public/assets/routes-P7683vKQ.js +0 -1
- package/.output/public/assets/search-DuI1FBpg.js +0 -1
- package/.output/public/assets/select-M3H2gxCc.js +0 -1
- package/.output/public/assets/smartphone-CtsHuiIC.js +0 -1
- package/.output/public/assets/styles-CpiW_YdW.css +0 -2
- package/.output/public/assets/switch-By_vfm8I.js +0 -1
- package/.output/public/assets/useCompositeListItem-B-UkrDlu.js +0 -1
- package/.output/server/_ssr/config.server-DTdsG47T.mjs +0 -35
- package/.output/server/_ssr/start-B6ECIE_7.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-_BY9hYTm.mjs +0 -136
package/lib/vite-internal.mjs
CHANGED
|
@@ -9,12 +9,18 @@ const virtualAuthModuleId = "virtual:hot-updater-console/auth";
|
|
|
9
9
|
const virtualConfigModuleId = "virtual:hot-updater-console/config";
|
|
10
10
|
const virtualPackageRouterModuleId = "virtual:hot-updater-console/package-router";
|
|
11
11
|
const virtualRouteTreeModuleId = "virtual:hot-updater-console/route-tree";
|
|
12
|
+
const virtualPackageStartModuleId = "virtual:hot-updater-console/package-start";
|
|
13
|
+
const virtualRequestGuardModuleId = "virtual:hot-updater-console/request-guard";
|
|
12
14
|
const resolvedVirtualAuthModuleId = `\0${virtualAuthModuleId}`;
|
|
13
15
|
const resolvedVirtualConfigModuleId = `\0${virtualConfigModuleId}`;
|
|
14
16
|
const resolvedVirtualPackageRouterModuleId = `\0${virtualPackageRouterModuleId}`;
|
|
17
|
+
const resolvedVirtualPackageStartModuleId = `\0${virtualPackageStartModuleId}`;
|
|
18
|
+
const resolvedVirtualRequestGuardModuleId = `\0${virtualRequestGuardModuleId}`;
|
|
15
19
|
const packageRoot = path.resolve(import.meta.dirname, "..");
|
|
16
20
|
const packagePublicDirectory = path.join(packageRoot, "public");
|
|
17
21
|
const packageRouterFile = path.join(packageRoot, "src/router.tsx");
|
|
22
|
+
const packageStartFile = path.join(packageRoot, "src/start.ts");
|
|
23
|
+
const localConsoleGuardFile = path.join(packageRoot, "src/lib/server/local-console.ts");
|
|
18
24
|
const packageRoutesDirectory = path.join(packageRoot, "src/routes");
|
|
19
25
|
const packageSourceDirectory = path.join(packageRoot, "src");
|
|
20
26
|
const localRouteTreeFile = path.join(packageSourceDirectory, "routeTree.gen.ts");
|
|
@@ -40,6 +46,7 @@ const createConsoleModulesPlugin = (mode) => {
|
|
|
40
46
|
authFile = resolveFromRoot(root, mode.options.auth ?? "console.auth.ts");
|
|
41
47
|
configFile = resolveFromRoot(root, mode.options.config ?? "console.config.ts");
|
|
42
48
|
writeFileIfChanged(path.join(cacheDirectory, "router.ts"), [`export { getRouter } from ${JSON.stringify(virtualPackageRouterModuleId)};`, ""].join("\n"));
|
|
49
|
+
writeFileIfChanged(path.join(cacheDirectory, "start.ts"), [`export { startInstance } from ${JSON.stringify(virtualPackageStartModuleId)};`, ""].join("\n"));
|
|
43
50
|
}
|
|
44
51
|
return {
|
|
45
52
|
assetsInclude: ["**/*.node"],
|
|
@@ -63,10 +70,32 @@ const createConsoleModulesPlugin = (mode) => {
|
|
|
63
70
|
if (id === virtualAuthModuleId) return resolvedVirtualAuthModuleId;
|
|
64
71
|
if (id === virtualConfigModuleId) return resolvedVirtualConfigModuleId;
|
|
65
72
|
if (id === virtualPackageRouterModuleId) return resolvedVirtualPackageRouterModuleId;
|
|
73
|
+
if (id === virtualPackageStartModuleId) return resolvedVirtualPackageStartModuleId;
|
|
74
|
+
if (id === virtualRequestGuardModuleId) return resolvedVirtualRequestGuardModuleId;
|
|
66
75
|
if (id === virtualRouteTreeModuleId) return generatedRouteTreeFile;
|
|
67
76
|
},
|
|
68
77
|
load(id) {
|
|
69
78
|
if (id === resolvedVirtualPackageRouterModuleId) return `export { getRouter } from ${JSON.stringify(toImportPath(packageRouterFile))};`;
|
|
79
|
+
if (id === resolvedVirtualPackageStartModuleId) return `export { startInstance } from ${JSON.stringify(toImportPath(packageStartFile))};`;
|
|
80
|
+
if (id === resolvedVirtualRequestGuardModuleId) {
|
|
81
|
+
if (mode.type === "hosted") return "export default undefined;";
|
|
82
|
+
return [
|
|
83
|
+
`import { guardLocalConsoleRequest, LOCAL_CONSOLE_SESSION_ENV, LOCAL_CONSOLE_SESSION_PARAM } from ${JSON.stringify(toImportPath(localConsoleGuardFile))};`,
|
|
84
|
+
"let session = process.env[LOCAL_CONSOLE_SESSION_ENV] || undefined;",
|
|
85
|
+
"if (session === undefined && import.meta.env.DEV) {",
|
|
86
|
+
" const bytes = crypto.getRandomValues(new Uint8Array(32));",
|
|
87
|
+
" session = btoa(String.fromCharCode(...bytes)).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replaceAll(\"=\", \"\");",
|
|
88
|
+
" console.info(`Open the console at /?${LOCAL_CONSOLE_SESSION_PARAM}=${session}`);",
|
|
89
|
+
"}",
|
|
90
|
+
"const port = process.env.NITRO_PORT || process.env.PORT || undefined;",
|
|
91
|
+
"export default (request) =>",
|
|
92
|
+
" guardLocalConsoleRequest(request, {",
|
|
93
|
+
" session,",
|
|
94
|
+
" port: import.meta.env.DEV ? undefined : port,",
|
|
95
|
+
" checkHost: !import.meta.env.DEV,",
|
|
96
|
+
" });"
|
|
97
|
+
].join("\n");
|
|
98
|
+
}
|
|
70
99
|
if (id === resolvedVirtualConfigModuleId) {
|
|
71
100
|
if (mode.type === "local") return [
|
|
72
101
|
"import { loadConfig } from \"@hot-updater/cli-tools\";",
|
|
@@ -86,7 +115,9 @@ const createConsoleModulesPlugin = (mode) => {
|
|
|
86
115
|
" database: config.database,",
|
|
87
116
|
" storage: config.storage,",
|
|
88
117
|
" plugins: config.plugins,",
|
|
89
|
-
" console: {
|
|
118
|
+
" console: {",
|
|
119
|
+
" gitUrl: config.console.gitUrl,",
|
|
120
|
+
" },",
|
|
90
121
|
" };",
|
|
91
122
|
"};"
|
|
92
123
|
].join("\n");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/console",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.0-rc.1",
|
|
5
5
|
"files": [
|
|
6
6
|
".output",
|
|
7
7
|
"lib",
|
|
@@ -57,11 +57,12 @@
|
|
|
57
57
|
"tailwind-merge": "^3.5.0",
|
|
58
58
|
"tailwindcss": "^4.2.1",
|
|
59
59
|
"tw-animate-css": "^1.4.0",
|
|
60
|
+
"uqr": "^0.1.3",
|
|
60
61
|
"verkit": "0.4.0",
|
|
61
|
-
"@hot-updater/bsdiff": "1.1
|
|
62
|
-
"@hot-updater/
|
|
63
|
-
"@hot-updater/
|
|
64
|
-
"@hot-updater/server": "1.1
|
|
62
|
+
"@hot-updater/bsdiff": "1.2.0-rc.1",
|
|
63
|
+
"@hot-updater/protocol": "1.2.0-rc.1",
|
|
64
|
+
"@hot-updater/plugin-core": "1.2.0-rc.1",
|
|
65
|
+
"@hot-updater/server": "1.2.0-rc.1"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
67
68
|
"@tanstack/devtools-vite": "^0.6.0",
|
|
@@ -86,8 +87,8 @@
|
|
|
86
87
|
"vite-tsconfig-paths": "^6.1.1",
|
|
87
88
|
"vitest": "4.1.4",
|
|
88
89
|
"web-vitals": "^5.1.0",
|
|
89
|
-
"@hot-updater/cli-tools": "1.1
|
|
90
|
-
"@hot-updater/mock": "1.1
|
|
90
|
+
"@hot-updater/cli-tools": "1.2.0-rc.1",
|
|
91
|
+
"@hot-updater/mock": "1.2.0-rc.1"
|
|
91
92
|
},
|
|
92
93
|
"description": "React Native OTA solution for self-hosted",
|
|
93
94
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Link, useRouterState } from "@tanstack/react-router";
|
|
2
|
-
import { LogOut, Moon, Package, Sun } from "lucide-react";
|
|
2
|
+
import { LogOut, HardDrive, Moon, Package, Sun } from "lucide-react";
|
|
3
3
|
import { type ReactElement, type ReactNode, useState } from "react";
|
|
4
4
|
import { toast } from "sonner";
|
|
5
5
|
|
|
@@ -58,6 +58,17 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
|
58
58
|
/>
|
|
59
59
|
),
|
|
60
60
|
},
|
|
61
|
+
{
|
|
62
|
+
label: "Native builds",
|
|
63
|
+
icon: <HardDrive />,
|
|
64
|
+
// A build's page keeps its list's item active.
|
|
65
|
+
active:
|
|
66
|
+
currentPath === "/native-builds" ||
|
|
67
|
+
currentPath.startsWith("/native-builds/"),
|
|
68
|
+
link: (
|
|
69
|
+
<Link to="/native-builds" onClick={closeMobileSidebar} search={{}} />
|
|
70
|
+
),
|
|
71
|
+
},
|
|
61
72
|
// Each feature that is on adds its item, as the feature registry lists it.
|
|
62
73
|
...featureUi.flatMap(({ features, label, navigation }) =>
|
|
63
74
|
navigation === undefined
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { HotUpdaterLogo } from "@/components/HotUpdaterLogo";
|
|
4
4
|
import { Button } from "@/components/ui/button";
|
|
@@ -15,6 +15,40 @@ const providerLabel: Record<ConsoleAuthProvider, string> = {
|
|
|
15
15
|
google: "Google",
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/** A path on this console, or the root for anything that could leave it. */
|
|
19
|
+
const consolePath = (target: string) =>
|
|
20
|
+
target.startsWith("/") && !/^\/[/\\]/.test(target) ? target : "/";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Where sign-in returns to: the page that asked for it, as a path on this
|
|
24
|
+
* console, so a link to a build lands on that build. A page a route sent
|
|
25
|
+
* here names its own target in `returnTo`, relative to the page, as the
|
|
26
|
+
* native build download does: sign-in returns there instead, when it is on
|
|
27
|
+
* this console's origin. Anything that could leave the console, such as
|
|
28
|
+
* `//host` or `/\host`, returns to the page, or to the root.
|
|
29
|
+
*/
|
|
30
|
+
export const signInCallbackURL = (location: {
|
|
31
|
+
readonly href: string;
|
|
32
|
+
readonly pathname: string;
|
|
33
|
+
readonly search: string;
|
|
34
|
+
}): string => {
|
|
35
|
+
const page = consolePath(`${location.pathname}${location.search}`);
|
|
36
|
+
const returnTo = new URLSearchParams(location.search).get("returnTo");
|
|
37
|
+
if (returnTo === null) return page;
|
|
38
|
+
try {
|
|
39
|
+
const target = new URL(returnTo, location.href);
|
|
40
|
+
return target.origin === new URL(location.href).origin
|
|
41
|
+
? consolePath(`${target.pathname}${target.search}`)
|
|
42
|
+
: page;
|
|
43
|
+
} catch {
|
|
44
|
+
return page;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Whether sign-in returns to a native build's download. */
|
|
49
|
+
const returnsToDownload = (callbackURL: string) =>
|
|
50
|
+
/\/api\/native-builds\/[^/]+\/download$/.test(callbackURL);
|
|
51
|
+
|
|
18
52
|
export function ConsoleAccessPage({
|
|
19
53
|
access,
|
|
20
54
|
providers,
|
|
@@ -22,6 +56,13 @@ export function ConsoleAccessPage({
|
|
|
22
56
|
const [pendingProvider, setPendingProvider] =
|
|
23
57
|
useState<ConsoleAuthProvider | null>(null);
|
|
24
58
|
const [signingOut, setSigningOut] = useState(false);
|
|
59
|
+
// Read after the page mounts: the server renders it without a location.
|
|
60
|
+
const [downloadAfterSignIn, setDownloadAfterSignIn] = useState(false);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setDownloadAfterSignIn(
|
|
63
|
+
returnsToDownload(signInCallbackURL(window.location)),
|
|
64
|
+
);
|
|
65
|
+
}, []);
|
|
25
66
|
const [error, setError] = useState<string | null>(null);
|
|
26
67
|
|
|
27
68
|
const signIn = async (provider: ConsoleAuthProvider) => {
|
|
@@ -29,7 +70,10 @@ export function ConsoleAccessPage({
|
|
|
29
70
|
setError(null);
|
|
30
71
|
try {
|
|
31
72
|
const response = await fetch("/api/auth/sign-in/social", {
|
|
32
|
-
body: JSON.stringify({
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
callbackURL: signInCallbackURL(window.location),
|
|
75
|
+
provider,
|
|
76
|
+
}),
|
|
33
77
|
headers: { "content-type": "application/json" },
|
|
34
78
|
method: "POST",
|
|
35
79
|
});
|
|
@@ -71,7 +115,9 @@ export function ConsoleAccessPage({
|
|
|
71
115
|
<div>
|
|
72
116
|
<h1 className="text-lg font-semibold">Hot Updater Console</h1>
|
|
73
117
|
<p className="text-muted-foreground text-sm">
|
|
74
|
-
|
|
118
|
+
{downloadAfterSignIn
|
|
119
|
+
? "Sign in to download the build. It starts once you're in."
|
|
120
|
+
: "Sign in to manage OTA bundles."}
|
|
75
121
|
</p>
|
|
76
122
|
</div>
|
|
77
123
|
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
|
|
3
|
+
import { qrCodePath } from "@/lib/qr-code";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A QR code of `value`, drawn as one SVG path with the standard four-module
|
|
8
|
+
* quiet zone. Phone cameras read dark modules on a light ground, so the code
|
|
9
|
+
* keeps its own two colors in both themes instead of the theme's tokens.
|
|
10
|
+
*/
|
|
11
|
+
export function QrCode({
|
|
12
|
+
value,
|
|
13
|
+
label,
|
|
14
|
+
className,
|
|
15
|
+
}: {
|
|
16
|
+
readonly value: string;
|
|
17
|
+
/** What scanning opens, for screen readers. */
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly className?: string;
|
|
20
|
+
}) {
|
|
21
|
+
const { size, path } = useMemo(() => qrCodePath(value), [value]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<svg
|
|
25
|
+
aria-label={label}
|
|
26
|
+
className={cn("aspect-square rounded-md", className)}
|
|
27
|
+
role="img"
|
|
28
|
+
shapeRendering="crispEdges"
|
|
29
|
+
viewBox={`0 0 ${size} ${size}`}
|
|
30
|
+
>
|
|
31
|
+
<rect fill="#fff" height={size} width={size} />
|
|
32
|
+
<path d={path} fill="#000" />
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ArrowUpRight, Copy, RotateCw } from "lucide-react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { toast } from "sonner";
|
|
4
|
+
|
|
5
|
+
import { Button, buttonVariants } from "@/components/ui/button";
|
|
6
|
+
import {
|
|
7
|
+
Empty,
|
|
8
|
+
EmptyContent,
|
|
9
|
+
EmptyDescription,
|
|
10
|
+
EmptyHeader,
|
|
11
|
+
EmptyTitle,
|
|
12
|
+
} from "@/components/ui/empty";
|
|
13
|
+
import { SidebarTrigger } from "@/components/ui/sidebar";
|
|
14
|
+
import type { SchemaMigrationStatus } from "@/lib/schema-migration";
|
|
15
|
+
|
|
16
|
+
export const DOCTOR_COMMAND = "npx hot-updater doctor";
|
|
17
|
+
|
|
18
|
+
export const DOCTOR_GUIDE_URL = "https://hot-updater.dev/docs/guides/doctor";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What the console shows instead of a page while the schema fence refuses
|
|
22
|
+
* its database: the settings a migration writes, and doctor, which names the
|
|
23
|
+
* command that runs it. Every page reads through the fence, so one notice
|
|
24
|
+
* stands for all.
|
|
25
|
+
*/
|
|
26
|
+
export function SchemaMigrationRequired({
|
|
27
|
+
status,
|
|
28
|
+
onRetry,
|
|
29
|
+
}: {
|
|
30
|
+
readonly status: SchemaMigrationStatus;
|
|
31
|
+
readonly onRetry: () => Promise<void>;
|
|
32
|
+
}) {
|
|
33
|
+
const [checking, setChecking] = useState(false);
|
|
34
|
+
const command = DOCTOR_COMMAND;
|
|
35
|
+
const configFile = (
|
|
36
|
+
<code className="font-mono whitespace-nowrap">hot-updater.config.ts</code>
|
|
37
|
+
);
|
|
38
|
+
const copyCommand = async () => {
|
|
39
|
+
try {
|
|
40
|
+
await navigator.clipboard.writeText(command);
|
|
41
|
+
toast.success("Command copied");
|
|
42
|
+
} catch {
|
|
43
|
+
toast.error("Could not copy the command");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const checkAgain = async () => {
|
|
47
|
+
setChecking(true);
|
|
48
|
+
try {
|
|
49
|
+
await onRetry();
|
|
50
|
+
} finally {
|
|
51
|
+
setChecking(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex h-svh min-h-0 flex-col">
|
|
56
|
+
<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">
|
|
57
|
+
<SidebarTrigger className="-ml-1" />
|
|
58
|
+
<h1 className="text-sm font-medium">Database migration</h1>
|
|
59
|
+
</header>
|
|
60
|
+
<div className="flex min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 p-3 sm:p-6">
|
|
61
|
+
<Empty>
|
|
62
|
+
<EmptyHeader>
|
|
63
|
+
{status.migratable ? (
|
|
64
|
+
<>
|
|
65
|
+
<EmptyTitle>The database needs a migration</EmptyTitle>
|
|
66
|
+
<EmptyDescription>
|
|
67
|
+
Its schema is older than this console's, so the console stops
|
|
68
|
+
before reading it. Run this where {configFile} names the
|
|
69
|
+
database: it names the command that migrates it. Then check
|
|
70
|
+
again.
|
|
71
|
+
</EmptyDescription>
|
|
72
|
+
</>
|
|
73
|
+
) : (
|
|
74
|
+
<>
|
|
75
|
+
<EmptyTitle>The database's schema doesn't match</EmptyTitle>
|
|
76
|
+
<EmptyDescription>
|
|
77
|
+
A newer Hot Updater migrated it, or it uses a layout this
|
|
78
|
+
console can't migrate, so the console stops before reading it.
|
|
79
|
+
Run this where {configFile} names the database to see what it
|
|
80
|
+
needs.
|
|
81
|
+
</EmptyDescription>
|
|
82
|
+
</>
|
|
83
|
+
)}
|
|
84
|
+
</EmptyHeader>
|
|
85
|
+
<EmptyContent>
|
|
86
|
+
<div className="flex w-full items-center gap-2 rounded-md border bg-muted/40 py-1 pr-1 pl-3">
|
|
87
|
+
<code className="min-w-0 flex-1 overflow-x-auto text-left font-mono text-xs whitespace-nowrap">
|
|
88
|
+
{command}
|
|
89
|
+
</code>
|
|
90
|
+
<Button
|
|
91
|
+
aria-label="Copy command"
|
|
92
|
+
onClick={copyCommand}
|
|
93
|
+
size="icon-sm"
|
|
94
|
+
variant="ghost"
|
|
95
|
+
>
|
|
96
|
+
<Copy aria-hidden="true" />
|
|
97
|
+
</Button>
|
|
98
|
+
</div>
|
|
99
|
+
{status.settings.length > 0 ? (
|
|
100
|
+
<ul className="w-full space-y-1 text-left text-xs text-muted-foreground">
|
|
101
|
+
{status.settings.map(({ key, expected, found }) => (
|
|
102
|
+
<li key={key} className="flex justify-between gap-3">
|
|
103
|
+
<code className="font-mono">{key}</code>
|
|
104
|
+
<span className="tabular-nums">
|
|
105
|
+
{found ?? "missing"} → {expected}
|
|
106
|
+
</span>
|
|
107
|
+
</li>
|
|
108
|
+
))}
|
|
109
|
+
</ul>
|
|
110
|
+
) : null}
|
|
111
|
+
<div className="flex flex-wrap justify-center gap-2">
|
|
112
|
+
<Button
|
|
113
|
+
disabled={checking}
|
|
114
|
+
onClick={checkAgain}
|
|
115
|
+
variant="outline"
|
|
116
|
+
>
|
|
117
|
+
<RotateCw
|
|
118
|
+
aria-hidden="true"
|
|
119
|
+
className={checking ? "motion-safe:animate-spin" : undefined}
|
|
120
|
+
data-icon="inline-start"
|
|
121
|
+
/>
|
|
122
|
+
{checking ? "Checking…" : "Check again"}
|
|
123
|
+
</Button>
|
|
124
|
+
<a
|
|
125
|
+
className={buttonVariants({ variant: "ghost" })}
|
|
126
|
+
href={DOCTOR_GUIDE_URL}
|
|
127
|
+
rel="noreferrer"
|
|
128
|
+
target="_blank"
|
|
129
|
+
>
|
|
130
|
+
Doctor guide
|
|
131
|
+
<ArrowUpRight aria-hidden="true" data-icon="inline-end" />
|
|
132
|
+
</a>
|
|
133
|
+
</div>
|
|
134
|
+
</EmptyContent>
|
|
135
|
+
</Empty>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -2,11 +2,13 @@ import { Badge } from "@/components/ui/badge";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A built-in bundle's ID: every native build makes it from its build time with
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* zero random bits, and builds an earlier `hot-updater native-build` made, in
|
|
6
|
+
* the field still, have a node of a platform byte (`01` iOS, `02` Android) and
|
|
7
|
+
* random bits; or the nil UUID in a debug build. A deployed bundle's ID is a
|
|
8
|
+
* random UUIDv7, so it practically never has this shape.
|
|
7
9
|
*/
|
|
8
10
|
const BUILTIN_BUNDLE_ID =
|
|
9
|
-
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-7000-8000-000000000000|00000000-0000-0000-0000-000000000000)$/iu;
|
|
11
|
+
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-7000-8000-(?:000000000000|0[12][0-9a-f]{10})|00000000-0000-0000-0000-000000000000)$/iu;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Whether a reported bundle is one a native build shipped: the report's
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import { ArrowUpRight, CircleCheck, History } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { TimestampDisplay } from "@/components/TimestampDisplay";
|
|
5
|
+
import { Badge } from "@/components/ui/badge";
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
|
+
import {
|
|
8
|
+
nativeBuildArtifacts,
|
|
9
|
+
nativeBuildTarget,
|
|
10
|
+
platformLabel,
|
|
11
|
+
} from "@/lib/native-builds";
|
|
12
|
+
import type { NativeBuildDetail } from "@/lib/server/nativeBuilds";
|
|
13
|
+
|
|
14
|
+
import { CommitLink } from "./CommitLink";
|
|
15
|
+
import { ShortHash } from "./ShortHash";
|
|
16
|
+
import { isSubmitted, StoreSubmissionBadge } from "./StoreSubmission";
|
|
17
|
+
|
|
18
|
+
function Row({
|
|
19
|
+
label,
|
|
20
|
+
children,
|
|
21
|
+
}: {
|
|
22
|
+
readonly label: string;
|
|
23
|
+
readonly children: React.ReactNode;
|
|
24
|
+
}) {
|
|
25
|
+
return (
|
|
26
|
+
<div className="grid gap-1 sm:grid-cols-[10rem_minmax(0,1fr)] sm:gap-4">
|
|
27
|
+
<dt className="text-xs font-medium text-muted-foreground">{label}</dt>
|
|
28
|
+
<dd className="min-w-0 text-sm">{children}</dd>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Whether new deploys patch from this build, or from a newer one of its scope. */
|
|
34
|
+
function BaselineStatus({ detail }: { readonly detail: NativeBuildDetail }) {
|
|
35
|
+
const { build, newestInScope } = detail;
|
|
36
|
+
if (newestInScope === null) return null;
|
|
37
|
+
if (newestInScope.id === build.id) {
|
|
38
|
+
return (
|
|
39
|
+
<div className="flex items-start gap-2 rounded-md border bg-muted/40 p-3 text-xs">
|
|
40
|
+
<CircleCheck
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
className="mt-0.5 size-3.5 shrink-0 text-muted-foreground"
|
|
43
|
+
/>
|
|
44
|
+
<p>
|
|
45
|
+
<span className="font-medium">Current baseline.</span> New deploys to
|
|
46
|
+
this scope add a patch from this build's bundle.
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex items-start gap-2 rounded-md border bg-muted/40 p-3 text-xs">
|
|
53
|
+
<History
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
className="mt-0.5 size-3.5 shrink-0 text-muted-foreground"
|
|
56
|
+
/>
|
|
57
|
+
<p>
|
|
58
|
+
<span className="font-medium">Superseded</span> by{" "}
|
|
59
|
+
<Link
|
|
60
|
+
className="inline-flex items-center gap-0.5 font-medium underline underline-offset-4"
|
|
61
|
+
params={{ buildId: newestInScope.id }}
|
|
62
|
+
to="/native-builds/$buildId"
|
|
63
|
+
>
|
|
64
|
+
{platformLabel(newestInScope.platform)}{" "}
|
|
65
|
+
{nativeBuildArtifacts[newestInScope.artifact_kind].label} ·{" "}
|
|
66
|
+
<TimestampDisplay uuid={newestInScope.id} />
|
|
67
|
+
<ArrowUpRight aria-hidden="true" className="size-3" />
|
|
68
|
+
</Link>
|
|
69
|
+
. New deploys patch from that build, so this one gets no new patches.
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The build's built-in bundle: the bundle every install runs until its
|
|
77
|
+
* first OTA, and the base that OTA's patch starts from.
|
|
78
|
+
*/
|
|
79
|
+
export function BuiltInBundleCard({
|
|
80
|
+
detail,
|
|
81
|
+
}: {
|
|
82
|
+
readonly detail: NativeBuildDetail;
|
|
83
|
+
}) {
|
|
84
|
+
const { build, builtInBundle, channel } = detail;
|
|
85
|
+
const target = nativeBuildTarget(build);
|
|
86
|
+
const childCount = builtInBundle?.childCount ?? 0;
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Card className="overflow-hidden shadow-sm">
|
|
90
|
+
<CardHeader className="border-b px-4 py-3">
|
|
91
|
+
<CardTitle className="text-sm">
|
|
92
|
+
<h2>Built-in bundle</h2>
|
|
93
|
+
</CardTitle>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent className="flex flex-col gap-4 p-4 sm:p-6">
|
|
96
|
+
<dl className="flex flex-col gap-3">
|
|
97
|
+
<Row label="Bundle ID">
|
|
98
|
+
<span className="break-all font-mono text-xs" translate="no">
|
|
99
|
+
{build.min_bundle_id}
|
|
100
|
+
</span>
|
|
101
|
+
</Row>
|
|
102
|
+
<Row label="OTAs patching from it">
|
|
103
|
+
<span className="tabular-nums">
|
|
104
|
+
{childCount === 1 ? "1 bundle" : `${childCount} bundles`}
|
|
105
|
+
</span>
|
|
106
|
+
</Row>
|
|
107
|
+
<Row label="Scope">
|
|
108
|
+
<span className="flex flex-wrap items-center gap-1.5">
|
|
109
|
+
<Link
|
|
110
|
+
aria-label={`Show the builds that embed ${channel}`}
|
|
111
|
+
search={{ channel }}
|
|
112
|
+
to="/native-builds"
|
|
113
|
+
>
|
|
114
|
+
<Badge translate="no" variant="outline">
|
|
115
|
+
{channel}
|
|
116
|
+
</Badge>
|
|
117
|
+
</Link>
|
|
118
|
+
<span className="text-xs">{platformLabel(build.platform)}</span>
|
|
119
|
+
<span className="text-xs text-muted-foreground">
|
|
120
|
+
· {target.label}
|
|
121
|
+
</span>
|
|
122
|
+
{build.fingerprint_hash === null ? (
|
|
123
|
+
<span className="font-mono text-xs" translate="no">
|
|
124
|
+
{target.value}
|
|
125
|
+
</span>
|
|
126
|
+
) : (
|
|
127
|
+
<ShortHash
|
|
128
|
+
label="fingerprint"
|
|
129
|
+
length={12}
|
|
130
|
+
value={build.fingerprint_hash}
|
|
131
|
+
/>
|
|
132
|
+
)}
|
|
133
|
+
</span>
|
|
134
|
+
</Row>
|
|
135
|
+
{!build.build_number ? null : (
|
|
136
|
+
<Row label="Build number">
|
|
137
|
+
<span className="font-mono text-xs tabular-nums" translate="no">
|
|
138
|
+
{build.build_number}
|
|
139
|
+
</span>
|
|
140
|
+
</Row>
|
|
141
|
+
)}
|
|
142
|
+
{isSubmitted(build) ? (
|
|
143
|
+
<Row label="Store">
|
|
144
|
+
<StoreSubmissionBadge build={build} withTime />
|
|
145
|
+
</Row>
|
|
146
|
+
) : null}
|
|
147
|
+
<Row label="Commit">
|
|
148
|
+
<CommitLink hash={build.git_commit_hash} />
|
|
149
|
+
</Row>
|
|
150
|
+
<Row label="Created">
|
|
151
|
+
<TimestampDisplay format="both" uuid={build.id} />
|
|
152
|
+
</Row>
|
|
153
|
+
</dl>
|
|
154
|
+
<BaselineStatus detail={detail} />
|
|
155
|
+
<p className="text-xs text-muted-foreground">
|
|
156
|
+
A store or test install of this build runs this bundle until its first
|
|
157
|
+
OTA. That OTA downloads as a patch from this bundle when one exists,
|
|
158
|
+
and as the full bundle otherwise.
|
|
159
|
+
</p>
|
|
160
|
+
</CardContent>
|
|
161
|
+
</Card>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useConfigQuery } from "@/lib/api";
|
|
2
|
+
import { getCommitUrl } from "@/lib/git";
|
|
3
|
+
|
|
4
|
+
import { ShortHash } from "./ShortHash";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A build's commit as git shows it, its first 7 characters: a link to the
|
|
8
|
+
* commit when `console.gitUrl` is set, else a button that copies the full
|
|
9
|
+
* SHA. The full SHA shows on hover either way.
|
|
10
|
+
*/
|
|
11
|
+
export function CommitLink({ hash }: { readonly hash: string | null }) {
|
|
12
|
+
const config = useConfigQuery();
|
|
13
|
+
if (hash === null) return <span className="text-muted-foreground">—</span>;
|
|
14
|
+
const url = getCommitUrl(config.data?.console.gitUrl, hash);
|
|
15
|
+
return url === null ? (
|
|
16
|
+
<ShortHash label="commit" length={7} value={hash} />
|
|
17
|
+
) : (
|
|
18
|
+
<a
|
|
19
|
+
className="font-mono text-xs whitespace-nowrap underline-offset-4 hover:underline"
|
|
20
|
+
href={url}
|
|
21
|
+
onClick={(event) => event.stopPropagation()}
|
|
22
|
+
rel="noreferrer"
|
|
23
|
+
target="_blank"
|
|
24
|
+
title={hash}
|
|
25
|
+
translate="no"
|
|
26
|
+
>
|
|
27
|
+
{hash.slice(0, 7)}
|
|
28
|
+
</a>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Copy } from "lucide-react";
|
|
2
|
+
import { toast } from "sonner";
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
|
|
6
|
+
/** A command or link on one line, with a button that copies it. */
|
|
7
|
+
export function CopyableText({
|
|
8
|
+
label,
|
|
9
|
+
value,
|
|
10
|
+
wrap = false,
|
|
11
|
+
}: {
|
|
12
|
+
/** What the copy button copies, such as "the install command". */
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly value: string;
|
|
15
|
+
/** Whether a long value wraps instead of scrolling, so all of it shows. */
|
|
16
|
+
readonly wrap?: boolean;
|
|
17
|
+
}) {
|
|
18
|
+
const copy = async () => {
|
|
19
|
+
try {
|
|
20
|
+
await navigator.clipboard.writeText(value);
|
|
21
|
+
toast.success("Copied to clipboard");
|
|
22
|
+
} catch {
|
|
23
|
+
toast.error(`Could not copy ${label}`);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex w-full min-w-0 items-center gap-2 rounded-md border bg-muted/40 py-1 pr-1 pl-3">
|
|
28
|
+
<code
|
|
29
|
+
className={
|
|
30
|
+
wrap
|
|
31
|
+
? "min-w-0 flex-1 text-left font-mono text-xs break-all whitespace-pre-wrap"
|
|
32
|
+
: "min-w-0 flex-1 overflow-x-auto text-left font-mono text-xs whitespace-nowrap"
|
|
33
|
+
}
|
|
34
|
+
>
|
|
35
|
+
{value}
|
|
36
|
+
</code>
|
|
37
|
+
<Button
|
|
38
|
+
aria-label={`Copy ${label}`}
|
|
39
|
+
className="size-11 sm:size-7"
|
|
40
|
+
onClick={() => void copy()}
|
|
41
|
+
size="icon-sm"
|
|
42
|
+
variant="ghost"
|
|
43
|
+
>
|
|
44
|
+
<Copy aria-hidden="true" />
|
|
45
|
+
</Button>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|