@hot-updater/console 1.2.0-rc.0 → 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.
Files changed (74) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{index-PQ3UCeQV.js → index-BWU6owTG.js} +2 -2
  3. package/.output/public/assets/native-builds_._buildId-D_Spjl10.js +1 -0
  4. package/.output/public/assets/{native-builds_._buildId-9p4FFpa4.js → native-builds_._buildId-DtoXD8rz.js} +2 -2
  5. package/.output/server/{__23tanstack-start-server-fn-resolver-nWkGzoo3.mjs → __23tanstack-start-server-fn-resolver-CygMPYHZ.mjs} +46 -46
  6. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +1 -1
  7. package/.output/server/_ssr/{BundleIdDisplay-C2YJNpHa.mjs → BundleIdDisplay-DOPz4pUS.mjs} +2 -2
  8. package/.output/server/_ssr/{ConsoleFeatureUnavailable-Co4rDns4.mjs → ConsoleFeatureUnavailable-Xb1JKqcc.mjs} +6 -6
  9. package/.output/server/_ssr/{EventDetails-CTvni55k.mjs → EventDetails-D7MejFCN.mjs} +6 -6
  10. package/.output/server/_ssr/{HashValueDisplay-BBswr65a.mjs → HashValueDisplay-Dd-wu5rY.mjs} +2 -2
  11. package/.output/server/_ssr/{InsightsPageHeader-ueG4Pkov.mjs → InsightsPageHeader-CWf1n4Vt.mjs} +6 -6
  12. package/.output/server/_ssr/{NotFoundPage-BES4AfvN.mjs → NotFoundPage-C5rFdUpg.mjs} +2 -2
  13. package/.output/server/_ssr/{RolloutPercentageBadge-Bkl0uDbV.mjs → RolloutPercentageBadge-g0Bhfl3M.mjs} +3 -3
  14. package/.output/server/_ssr/{StoreSubmission-0TLWxjym.mjs → StoreSubmission-JZ2Zltak.mjs} +4 -4
  15. package/.output/server/_ssr/{alert-dialog-Cn0L__fU.mjs → alert-dialog-CvNvTvIJ.mjs} +1 -1
  16. package/.output/server/_ssr/{api-qUMT9H2M.mjs → api-0vtwA5vY.mjs} +3 -3
  17. package/.output/server/_ssr/{api-keys-Dt6_6uWi.mjs → api-keys-D5dyA4D1.mjs} +8 -8
  18. package/.output/server/_ssr/{api-keys-rpc-BqbL9Iyl.mjs → api-keys-rpc-BOIUHhDi.mjs} +3 -3
  19. package/.output/server/_ssr/{api-keys-DgiwJvLW.mjs → api-keys-yFlQDwQF.mjs} +2 -2
  20. package/.output/server/_ssr/{api-rpc-CzZPviuV.mjs → api-rpc-COeFOpYi.mjs} +3 -3
  21. package/.output/server/_ssr/{auth-rpc-CPZyE2Rz.mjs → auth-rpc-D1ss2GUI.mjs} +4 -4
  22. package/.output/server/_ssr/{auth.server-C2hyK1tQ.mjs → auth.server-DYS1OJ6P.mjs} +2 -2
  23. package/.output/server/_ssr/{badge-C4HK6j63.mjs → badge-CEdVRgyl.mjs} +1 -1
  24. package/.output/server/_ssr/{config.server-CwjEf3S5.mjs → config.server-NnMFjMFX.mjs} +3 -3
  25. package/.output/server/_ssr/{console-access-B2mUKj3_.mjs → console-access-33BNlH97.mjs} +2 -2
  26. package/.output/server/_ssr/{console-features-api-X8ItTwxM.mjs → console-features-api-v1fSycue.mjs} +5 -5
  27. package/.output/server/_ssr/{console-features-rpc-BRTvGLBV.mjs → console-features-rpc-C7XNVqm0.mjs} +3 -3
  28. package/.output/server/_ssr/{dialog-L9IrT3sY.mjs → dialog-C8CS8RMt.mjs} +1 -1
  29. package/.output/server/_ssr/{empty-CkTC2uZd.mjs → empty-CkdEAvCY.mjs} +2 -2
  30. package/.output/server/_ssr/{field-W7Ikmzjb.mjs → field-CW08sZXd.mjs} +2 -2
  31. package/.output/server/_ssr/{input-group-BFnD9l_p.mjs → input-group-D-TpFPuG.mjs} +1 -1
  32. package/.output/server/_ssr/{insights-CtPYme4Q.mjs → insights-CMyzPs8l.mjs} +19 -19
  33. package/.output/server/_ssr/{insights-C9KDOFcN.mjs → insights-CYeBl8N9.mjs} +5 -5
  34. package/.output/server/_ssr/{insights-Dz42GlcZ.mjs → insights-DAi9KknT.mjs} +2 -2
  35. package/.output/server/_ssr/{insights-api-Cuz8lmZx.mjs → insights-api-BFARQ5ct.mjs} +3 -3
  36. package/.output/server/_ssr/{insights-failures-rpc-CxI071Ew.mjs → insights-failures-rpc-Q4WNNJkU.mjs} +4 -4
  37. package/.output/server/_ssr/{insights-recovery-rpc-B-9bs9Gz.mjs → insights-recovery-rpc-CY5RYJKa.mjs} +3 -3
  38. package/.output/server/_ssr/{insights-rpc-5NfC16-Q.mjs → insights-rpc-BO7_evvs.mjs} +3 -3
  39. package/.output/server/_ssr/{insights-search-CtQj0m7f.mjs → insights-search-DKfxgHD3.mjs} +2 -2
  40. package/.output/server/_ssr/{insights-usage-rpc-CFWx4_pK.mjs → insights-usage-rpc-B4czku50.mjs} +3 -3
  41. package/.output/server/_ssr/{insights-usage-rpc-Qnyviyah.mjs → insights-usage-rpc-CUCTFska.mjs} +3 -3
  42. package/.output/server/_ssr/{insights_.distribution-Czd0Jtle.mjs → insights_.distribution-BDeUhZGj.mjs} +2 -2
  43. package/.output/server/_ssr/{insights_.distribution-BamrEztu.mjs → insights_.distribution-BRrbZN1X.mjs} +5 -5
  44. package/.output/server/_ssr/{insights_.distribution-CXItWbX1.mjs → insights_.distribution-C2MXpzKd.mjs} +13 -13
  45. package/.output/server/_ssr/{installations-Dy8zAJI_.mjs → installations-BVk7okiY.mjs} +4 -4
  46. package/.output/server/_ssr/{installations-BaHA_zCp.mjs → installations-BjLMgBoH.mjs} +2 -2
  47. package/.output/server/_ssr/{installations-Q6t6CEZ8.mjs → installations-DxGZEC5G.mjs} +11 -11
  48. package/.output/server/_ssr/{native-builds-BWTUWJUF.mjs → native-builds-CSCgFB21.mjs} +2 -2
  49. package/.output/server/_ssr/{native-builds-Cuw87Y51.mjs → native-builds-CdlyAldW.mjs} +9 -9
  50. package/.output/server/_ssr/{native-builds_._buildId-Dr3Mc32p.mjs → native-builds_._buildId-CMp1h8dT.mjs} +2 -2
  51. package/.output/server/_ssr/{native-builds_._buildId-CbhHK93c.mjs → native-builds_._buildId-GHpGaYyE.mjs} +50 -26
  52. package/.output/server/_ssr/{release-adoption-rpc-BHxQWHWw.mjs → release-adoption-rpc-BJXZvtoQ.mjs} +5 -5
  53. package/.output/server/_ssr/{remote-config-Bmh0t1FQ.mjs → remote-config-DjsEl270.mjs} +17 -17
  54. package/.output/server/_ssr/{remote-config-CrVQfJ9y.mjs → remote-config-DlyyohL2.mjs} +2 -2
  55. package/.output/server/_ssr/{remote-config-DWlhe-Nu.mjs → remote-config-XdYuByHz.mjs} +2 -2
  56. package/.output/server/_ssr/{remote-config-rpc-BYJbiG9k.mjs → remote-config-rpc-GMLPb-90.mjs} +3 -3
  57. package/.output/server/_ssr/{router-Bg_TI2qP.mjs → router-vpxd56RW.mjs} +53 -31
  58. package/.output/server/_ssr/{routes-fs827NQ-.mjs → routes-DyEM4b3x.mjs} +10 -10
  59. package/.output/server/_ssr/{routes-Do9CHvbC.mjs → routes-dQe3XhiS.mjs} +17 -17
  60. package/.output/server/_ssr/{schema-migration-rpc-vH6ZlJgo.mjs → schema-migration-rpc-BfGzEK_o.mjs} +3 -3
  61. package/.output/server/_ssr/{select-BoRQ8ytA.mjs → select-DM1uwtk9.mjs} +1 -1
  62. package/.output/server/_ssr/{server-DtM4lJ0t.mjs → server-BUUKNSPI.mjs} +3 -3
  63. package/.output/server/_ssr/{sidebar-D1AgCVIN.mjs → sidebar-DB2YQlok.mjs} +2 -2
  64. package/.output/server/_ssr/ssr.mjs +1 -1
  65. package/.output/server/_ssr/{switch-CY0ZEeD2.mjs → switch-DLD58zLy.mjs} +2 -2
  66. package/.output/server/_ssr/{table-ClsVe-Mz.mjs → table-P4TnAZhl.mjs} +1 -1
  67. package/.output/server/_ssr/{tabs-BJacHKeX.mjs → tabs-B7ZeLicF.mjs} +2 -2
  68. package/.output/server/{_tanstack-start-manifest_v-CF4kCd6q.mjs → _tanstack-start-manifest_v-C-KC63Zq.mjs} +5 -5
  69. package/.output/server/index.mjs +126 -126
  70. package/package.json +7 -7
  71. package/src/components/ConsoleAccessPage.tsx +36 -6
  72. package/src/components/features/native-builds/InstallCard.tsx +47 -24
  73. package/src/routes/api/native-builds/$buildId/download.ts +9 -4
  74. package/.output/public/assets/native-builds_._buildId-BRiNTN3j.js +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/console",
3
3
  "type": "module",
4
- "version": "1.2.0-rc.0",
4
+ "version": "1.2.0-rc.1",
5
5
  "files": [
6
6
  ".output",
7
7
  "lib",
@@ -59,10 +59,10 @@
59
59
  "tw-animate-css": "^1.4.0",
60
60
  "uqr": "^0.1.3",
61
61
  "verkit": "0.4.0",
62
- "@hot-updater/bsdiff": "1.2.0-rc.0",
63
- "@hot-updater/plugin-core": "1.2.0-rc.0",
64
- "@hot-updater/protocol": "1.2.0-rc.0",
65
- "@hot-updater/server": "1.2.0-rc.0"
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"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@tanstack/devtools-vite": "^0.6.0",
@@ -87,8 +87,8 @@
87
87
  "vite-tsconfig-paths": "^6.1.1",
88
88
  "vitest": "4.1.4",
89
89
  "web-vitals": "^5.1.0",
90
- "@hot-updater/cli-tools": "1.2.0-rc.0",
91
- "@hot-updater/mock": "1.2.0-rc.0"
90
+ "@hot-updater/cli-tools": "1.2.0-rc.1",
91
+ "@hot-updater/mock": "1.2.0-rc.1"
92
92
  },
93
93
  "description": "React Native OTA solution for self-hosted",
94
94
  "license": "SEE LICENSE IN LICENSE",
@@ -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,19 +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
+
18
22
  /**
19
23
  * Where sign-in returns to: the page that asked for it, as a path on this
20
- * console, so a link to a build lands on that build. Anything that could
21
- * leave the console, such as `//host` or `/\host`, returns to the root.
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.
22
29
  */
23
30
  export const signInCallbackURL = (location: {
31
+ readonly href: string;
24
32
  readonly pathname: string;
25
33
  readonly search: string;
26
34
  }): string => {
27
- const target = `${location.pathname}${location.search}`;
28
- return target.startsWith("/") && !/^\/[/\\]/.test(target) ? target : "/";
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
+ }
29
46
  };
30
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
+
31
52
  export function ConsoleAccessPage({
32
53
  access,
33
54
  providers,
@@ -35,6 +56,13 @@ export function ConsoleAccessPage({
35
56
  const [pendingProvider, setPendingProvider] =
36
57
  useState<ConsoleAuthProvider | null>(null);
37
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
+ }, []);
38
66
  const [error, setError] = useState<string | null>(null);
39
67
 
40
68
  const signIn = async (provider: ConsoleAuthProvider) => {
@@ -87,7 +115,9 @@ export function ConsoleAccessPage({
87
115
  <div>
88
116
  <h1 className="text-lg font-semibold">Hot Updater Console</h1>
89
117
  <p className="text-muted-foreground text-sm">
90
- Sign in to manage OTA bundles.
118
+ {downloadAfterSignIn
119
+ ? "Sign in to download the build. It starts once you're in."
120
+ : "Sign in to manage OTA bundles."}
91
121
  </p>
92
122
  </div>
93
123
  </div>
@@ -17,12 +17,25 @@ import {
17
17
  import { CopyableText } from "./CopyableText";
18
18
  import { isSubmitted } from "./StoreSubmission";
19
19
 
20
- /** What scanning a build's QR code gets, by its artifact. */
20
+ /**
21
+ * What an artifact installs on, where the file alone isn't enough: the card
22
+ * says it once, under the QR code, or without one, in its place.
23
+ */
24
+ const INSTALL_LIMITS: Partial<Record<NativeBuildRow["artifact_kind"], string>> =
25
+ {
26
+ ipa: "The .ipa installs only on devices its signing profile allows (development or ad hoc); an App Store export reaches testers through TestFlight.",
27
+ aab: "The .aab installs through Google Play or bundletool, not from the file.",
28
+ };
29
+
30
+ /**
31
+ * What scanning a build's QR code gets, by its artifact. The install steps
32
+ * beside it add only what this doesn't say, so the card says each thing once.
33
+ */
21
34
  const QR_CAPTIONS: Record<NativeBuildRow["artifact_kind"], string> = {
22
35
  apk: "Scan with an Android phone's camera, then open the APK to install it.",
23
- ipa: "Scan to download the .ipa. It installs only on devices its signing profile allows (development or ad hoc); an App Store export reaches testers through TestFlight.",
24
- aab: "Scan to download the .aab. It installs through Google Play or bundletool, not from the file.",
25
- app: "Scan to download the .app.zip on a Mac, then install it on a simulator.",
36
+ ipa: `Scan to download. ${INSTALL_LIMITS.ipa}`,
37
+ aab: `Scan to download. ${INSTALL_LIMITS.aab}`,
38
+ app: "Scan to download the .app.zip on a Mac.",
26
39
  };
27
40
 
28
41
  /**
@@ -36,12 +49,16 @@ function DownloadQrCode({ build }: { readonly build: NativeBuildRow }) {
36
49
  // A storage without download URLs serves the build only through the
37
50
  // console, which a phone that scans isn't signed in to.
38
51
  if (url === null) {
52
+ const limits = INSTALL_LIMITS[build.artifact_kind];
39
53
  return (
40
- <p className="text-xs text-muted-foreground">
41
- This storage gives no download link a phone can open without signing in,
42
- so there is no QR code. Download the build here, or open this page on
43
- the phone signed in to the console.
44
- </p>
54
+ <div className="flex flex-col gap-2 text-xs text-muted-foreground">
55
+ <p>
56
+ This storage gives no download link a phone can open without signing
57
+ in, so there is no QR code. Download the build here, or open this page
58
+ on the phone signed in to the console.
59
+ </p>
60
+ {limits ? <p>{limits}</p> : null}
61
+ </div>
45
62
  );
46
63
  }
47
64
 
@@ -80,7 +97,10 @@ function SubmittedVersion({ build }: { readonly build: NativeBuildRow }) {
80
97
  );
81
98
  }
82
99
 
83
- /** How the artifact installs, beside its download. */
100
+ /**
101
+ * How else the artifact installs, beside its QR code's caption and its
102
+ * download: none of it repeats the caption.
103
+ */
84
104
  function InstallSteps({ build }: { readonly build: NativeBuildRow }) {
85
105
  switch (build.artifact_kind) {
86
106
  case "apk":
@@ -99,8 +119,7 @@ function InstallSteps({ build }: { readonly build: NativeBuildRow }) {
99
119
  return (
100
120
  <div className="flex min-w-0 flex-col gap-2">
101
121
  <p className="text-xs text-muted-foreground">
102
- A simulator build runs only in the iOS Simulator. Download the
103
- archive, then install it on the booted simulator:
122
+ It runs only in the iOS Simulator. Install it on the booted one:
104
123
  </p>
105
124
  <CopyableText
106
125
  label="the install command"
@@ -112,27 +131,30 @@ function InstallSteps({ build }: { readonly build: NativeBuildRow }) {
112
131
  return isSubmitted(build) ? (
113
132
  <p className="text-xs text-muted-foreground">
114
133
  Submitted to Google Play internal testing as{" "}
115
- <SubmittedVersion build={build} />. Testers install it from Google
116
- Play once its release is rolled out; on an app Play Console hasn't
117
- published, the release is a draft you roll out in Play Console.
134
+ <SubmittedVersion build={build} />. Testers get it once its release is
135
+ rolled out; on an app Play Console hasn't published, the release is a
136
+ draft you roll out in Play Console.
118
137
  </p>
119
138
  ) : (
120
- <p className="text-xs text-muted-foreground">
121
- An AAB installs through Google Play or bundletool, not from a
122
- download.
123
- </p>
139
+ <div className="flex min-w-0 flex-col gap-2">
140
+ <p className="text-xs text-muted-foreground">
141
+ For testers, build and release to internal testing:
142
+ </p>
143
+ <CopyableText
144
+ label="the Google Play command"
145
+ value="npx hot-updater google-play upload"
146
+ />
147
+ </div>
124
148
  );
125
149
  case "ipa":
126
150
  return isSubmitted(build) ? (
127
151
  <p className="text-xs text-muted-foreground">
128
- On TestFlight as <SubmittedVersion build={build} />: testers install
129
- it from TestFlight.
152
+ On TestFlight as <SubmittedVersion build={build} />.
130
153
  </p>
131
154
  ) : (
132
155
  <div className="flex min-w-0 flex-col gap-2">
133
156
  <p className="text-xs text-muted-foreground">
134
- An IPA installs on registered devices through Xcode or Apple
135
- Configurator. For testers, upload it to TestFlight:
157
+ Over USB, Xcode or Apple Configurator installs it. For testers:
136
158
  </p>
137
159
  <CopyableText
138
160
  label="the TestFlight command"
@@ -146,7 +168,8 @@ function InstallSteps({ build }: { readonly build: NativeBuildRow }) {
146
168
  /**
147
169
  * Installs one native build: every artifact by a QR code of its download,
148
170
  * which a phone scans, and by download here; an APK also with `adb`, a
149
- * simulator app with `xcrun simctl`, and an IPA's testers with TestFlight.
171
+ * simulator app with `xcrun simctl`, and an IPA's or an AAB's testers through
172
+ * its store.
150
173
  */
151
174
  export function InstallCard({ build }: { readonly build: NativeBuildRow }) {
152
175
  const fileName = nativeBuildFileName(build);
@@ -20,18 +20,23 @@ export const Route = createFileRoute("/api/native-builds/$buildId/download")({
20
20
  deps = await prepareConfig(request);
21
21
  } catch (error) {
22
22
  // A browser without a session opens the build's page instead,
23
- // where the console signs it in and returns to the build. The
24
- // path is relative to this one, so it stays under the console's
25
- // own path, wherever that is mounted.
23
+ // where the console signs it in and returns to this download
24
+ // (`returnTo`, relative to that page). Both paths are relative,
25
+ // so they stay under the console's own path, wherever that is
26
+ // mounted; sign-in takes `returnTo` only on the console's origin.
26
27
  if (
27
28
  error instanceof Response &&
28
29
  error.status === 401 &&
29
30
  isNavigation(request)
30
31
  ) {
32
+ const buildId = encodeURIComponent(params.buildId);
33
+ const returnTo = encodeURIComponent(
34
+ `../api/native-builds/${buildId}/download`,
35
+ );
31
36
  return new Response(null, {
32
37
  headers: {
33
38
  "cache-control": "no-store",
34
- location: `../../../native-builds/${encodeURIComponent(params.buildId)}`,
39
+ location: `../../../native-builds/${buildId}?returnTo=${returnTo}`,
35
40
  },
36
41
  status: 302,
37
42
  });
@@ -1 +0,0 @@
1
- import{$ as e,Fn as t,In as n,Ln as r,Wr as i,ii as a,ir as o,p as s,ti as c}from"./sidebar-Di9SN47G.js";import{t as l}from"./link-DTEyW70e.js";import{t as u}from"./arrow-up-right-DK8Zdg40.js";import{a as d,c as f,f as p,n as m,o as h,p as ee,r as te,s as ne,t as re,u as ie}from"./native-builds-BOC71grI.js";import{t as ae}from"./chevron-right-BoUXo82O.js";import{n as oe,t as g}from"./native-builds_._buildId-9p4FFpa4.js";import{t as se}from"./download-Cf24LBde.js";import{n as ce,t as le}from"./history-DqHlgSuV.js";import{r as ue}from"./-native-builds-search-C-TGpFAm.js";import{t as de}from"./refresh-cw-B9HHjLej.js";import{a as _,i as fe,n as v,r as pe,t as me}from"./StoreSubmission-JpngSvHS.js";import{a as y,d as b,f as x,i as S,n as C,o as w,r as he,s as T,t as E,u as D}from"./badge-DWFcDWho.js";import{r as ge}from"./uuidv7-zXMtwR9Q.js";import{a as O,i as k,n as A,o as j,r as M,t as N}from"./empty-B3Uwdd6k.js";import{t as _e}from"./PlatformIcon-BBWQlWm9.js";import{d as ve,l as ye,u as be}from"./api-P2g8dCNg.js";import{a as P,i as F,n as I,o as L,r as R,t as z}from"./table-Bpy3SvSo.js";import{t as xe}from"./RolloutPercentageBadge-Cxogpi8F.js";var Se=o(`package-open`,[[`path`,{d:`M12 22v-9`,key:`x3hkom`}],[`path`,{d:`M15.17 2.21a1.67 1.67 0 0 1 1.63 0L21 4.57a1.93 1.93 0 0 1 0 3.36L8.82 14.79a1.655 1.655 0 0 1-1.64 0L3 12.43a1.93 1.93 0 0 1 0-3.36z`,key:`2ntwy6`}],[`path`,{d:`M20 13v3.87a2.06 2.06 0 0 1-1.11 1.83l-6 3.08a1.93 1.93 0 0 1-1.78 0l-6-3.08A2.06 2.06 0 0 1 4 16.87V13`,key:`1pmm1c`}],[`path`,{d:`M21 12.43a1.93 1.93 0 0 0 0-3.36L8.83 2.2a1.64 1.64 0 0 0-1.63 0L3 4.57a1.93 1.93 0 0 0 0 3.36l12.18 6.86a1.636 1.636 0 0 0 1.63 0z`,key:`12ttoo`}]]),B=i();function V({label:e,children:t}){return(0,B.jsxs)(`div`,{className:`grid gap-1 sm:grid-cols-[10rem_minmax(0,1fr)] sm:gap-4`,children:[(0,B.jsx)(`dt`,{className:`text-xs font-medium text-muted-foreground`,children:e}),(0,B.jsx)(`dd`,{className:`min-w-0 text-sm`,children:t})]})}function Ce({detail:e}){let{build:t,newestInScope:n}=e;return n===null?null:n.id===t.id?(0,B.jsxs)(`div`,{className:`flex items-start gap-2 rounded-md border bg-muted/40 p-3 text-xs`,children:[(0,B.jsx)(oe,{"aria-hidden":`true`,className:`mt-0.5 size-3.5 shrink-0 text-muted-foreground`}),(0,B.jsxs)(`p`,{children:[(0,B.jsx)(`span`,{className:`font-medium`,children:`Current baseline.`}),` New deploys to this scope add a patch from this build's bundle.`]})]}):(0,B.jsxs)(`div`,{className:`flex items-start gap-2 rounded-md border bg-muted/40 p-3 text-xs`,children:[(0,B.jsx)(le,{"aria-hidden":`true`,className:`mt-0.5 size-3.5 shrink-0 text-muted-foreground`}),(0,B.jsxs)(`p`,{children:[(0,B.jsx)(`span`,{className:`font-medium`,children:`Superseded`}),` by`,` `,(0,B.jsxs)(l,{className:`inline-flex items-center gap-0.5 font-medium underline underline-offset-4`,params:{buildId:n.id},to:`/native-builds/$buildId`,children:[f(n.platform),` `,te[n.artifact_kind].label,` ·`,` `,(0,B.jsx)(p,{uuid:n.id}),(0,B.jsx)(u,{"aria-hidden":`true`,className:`size-3`})]}),`. New deploys patch from that build, so this one gets no new patches.`]})]})}function we({detail:e}){let{build:t,builtInBundle:n,channel:r}=e,i=ne(t),a=n?.childCount??0;return(0,B.jsxs)(w,{className:`overflow-hidden shadow-sm`,children:[(0,B.jsx)(D,{className:`border-b px-4 py-3`,children:(0,B.jsx)(b,{className:`text-sm`,children:(0,B.jsx)(`h2`,{children:`Built-in bundle`})})}),(0,B.jsxs)(T,{className:`flex flex-col gap-4 p-4 sm:p-6`,children:[(0,B.jsxs)(`dl`,{className:`flex flex-col gap-3`,children:[(0,B.jsx)(V,{label:`Bundle ID`,children:(0,B.jsx)(`span`,{className:`break-all font-mono text-xs`,translate:`no`,children:t.min_bundle_id})}),(0,B.jsx)(V,{label:`OTAs patching from it`,children:(0,B.jsx)(`span`,{className:`tabular-nums`,children:a===1?`1 bundle`:`${a} bundles`})}),(0,B.jsx)(V,{label:`Scope`,children:(0,B.jsxs)(`span`,{className:`flex flex-wrap items-center gap-1.5`,children:[(0,B.jsx)(l,{"aria-label":`Show the builds that embed ${r}`,search:{channel:r},to:`/native-builds`,children:(0,B.jsx)(E,{translate:`no`,variant:`outline`,children:r})}),(0,B.jsx)(`span`,{className:`text-xs`,children:f(t.platform)}),(0,B.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`· `,i.label]}),t.fingerprint_hash===null?(0,B.jsx)(`span`,{className:`font-mono text-xs`,translate:`no`,children:i.value}):(0,B.jsx)(fe,{label:`fingerprint`,length:12,value:t.fingerprint_hash})]})}),t.build_number?(0,B.jsx)(V,{label:`Build number`,children:(0,B.jsx)(`span`,{className:`font-mono text-xs tabular-nums`,translate:`no`,children:t.build_number})}):null,v(t)?(0,B.jsx)(V,{label:`Store`,children:(0,B.jsx)(me,{build:t,withTime:!0})}):null,(0,B.jsx)(V,{label:`Commit`,children:(0,B.jsx)(pe,{hash:t.git_commit_hash})}),(0,B.jsx)(V,{label:`Created`,children:(0,B.jsx)(p,{format:`both`,uuid:t.id})})]}),(0,B.jsx)(Ce,{detail:e}),(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`A store or test install of this build runs this bundle until its first OTA. That OTA downloads as a patch from this bundle when one exists, and as the full bundle otherwise.`})]})]})}var Te=a(c(),1),H=(e=>(e[e.Border=-1]=`Border`,e[e.Data=0]=`Data`,e[e.Function=1]=`Function`,e[e.Position=2]=`Position`,e[e.Timing=3]=`Timing`,e[e.Alignment=4]=`Alignment`,e))(H||{}),Ee=[0,1],De=[1,0],Oe=[2,3],ke=[3,2],Ae={L:Ee,M:De,Q:Oe,H:ke},je=/^\d*$/,Me=/^[A-Z0-9 $%*+./:-]*$/,U=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`,W=1,G=40,Ne=3,Pe=3,K=40,Fe=10,Ie=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],Le=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],Re=class{constructor(e,t,n,r){if(this.version=e,this.ecc=t,e<W||e>G)throw RangeError(`Version value out of range`);if(r<-1||r>7)throw RangeError(`Mask value out of range`);this.size=e*4+17;let i=Array.from({length:this.size}).fill(!1);for(let e=0;e<this.size;e++)this.modules.push(i.slice()),this.types.push(i.map(()=>0));this.drawFunctionPatterns();let a=this.addEccAndInterleave(n);if(this.drawCodewords(a),r===-1){let e=1e9;for(let t=0;t<8;t++){this.applyMask(t),this.drawFormatBits(t);let n=this.getPenaltyScore();n<e&&(r=t,e=n),this.applyMask(t)}}this.mask=r,this.applyMask(r),this.drawFormatBits(r)}size;mask;modules=[];types=[];getModule(e,t){return e>=0&&e<this.size&&t>=0&&t<this.size&&this.modules[t][e]}drawFunctionPatterns(){for(let e=0;e<this.size;e++)this.setFunctionModule(6,e,e%2==0,H.Timing),this.setFunctionModule(e,6,e%2==0,H.Timing);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);let e=this.getAlignmentPatternPositions(),t=e.length;for(let n=0;n<t;n++)for(let r=0;r<t;r++)n===0&&r===0||n===0&&r===t-1||n===t-1&&r===0||this.drawAlignmentPattern(e[n],e[r]);this.drawFormatBits(0),this.drawVersion()}drawFormatBits(e){let t=this.ecc[1]<<3|e,n=t;for(let e=0;e<10;e++)n=n<<1^(n>>>9)*1335;let r=(t<<10|n)^21522;for(let e=0;e<=5;e++)this.setFunctionModule(8,e,J(r,e));this.setFunctionModule(8,7,J(r,6)),this.setFunctionModule(8,8,J(r,7)),this.setFunctionModule(7,8,J(r,8));for(let e=9;e<15;e++)this.setFunctionModule(14-e,8,J(r,e));for(let e=0;e<8;e++)this.setFunctionModule(this.size-1-e,8,J(r,e));for(let e=8;e<15;e++)this.setFunctionModule(8,this.size-15+e,J(r,e));this.setFunctionModule(8,this.size-8,!0)}drawVersion(){if(this.version<7)return;let e=this.version;for(let t=0;t<12;t++)e=e<<1^(e>>>11)*7973;let t=this.version<<12|e;for(let e=0;e<18;e++){let n=J(t,e),r=this.size-11+e%3,i=Math.floor(e/3);this.setFunctionModule(r,i,n),this.setFunctionModule(i,r,n)}}drawFinderPattern(e,t){for(let n=-4;n<=4;n++)for(let r=-4;r<=4;r++){let i=Math.max(Math.abs(r),Math.abs(n)),a=e+r,o=t+n;a>=0&&a<this.size&&o>=0&&o<this.size&&this.setFunctionModule(a,o,i!==2&&i!==4,H.Position)}}drawAlignmentPattern(e,t){for(let n=-2;n<=2;n++)for(let r=-2;r<=2;r++)this.setFunctionModule(e+r,t+n,Math.max(Math.abs(r),Math.abs(n))!==1,H.Alignment)}setFunctionModule(e,t,n,r=H.Function){this.modules[t][e]=n,this.types[t][e]=r}addEccAndInterleave(e){let t=this.version,n=this.ecc;if(e.length!==Z(t,n))throw RangeError(`Invalid argument`);let r=Le[n[0]][t],i=Ie[n[0]][t],a=Math.floor(X(t)/8),o=r-a%r,s=Math.floor(a/r),c=[],l=Ze(i);for(let t=0,n=0;t<r;t++){let r=e.slice(n,n+s-i+(t<o?0:1));n+=r.length;let a=Qe(r,l);t<o&&r.push(0),c.push(r.concat(a))}let u=[];for(let e=0;e<c[0].length;e++)c.forEach((t,n)=>{(e!==s-i||n>=o)&&u.push(t[e])});return u}drawCodewords(e){if(e.length!==Math.floor(X(this.version)/8))throw RangeError(`Invalid argument`);let t=0;for(let n=this.size-1;n>=1;n-=2){n===6&&(n=5);for(let r=0;r<this.size;r++)for(let i=0;i<2;i++){let a=n-i,o=n+1&2?r:this.size-1-r;!this.types[o][a]&&t<e.length*8&&(this.modules[o][a]=J(e[t>>>3],7-(t&7)),t++)}}}applyMask(e){if(e<0||e>7)throw RangeError(`Mask value out of range`);for(let t=0;t<this.size;t++)for(let n=0;n<this.size;n++){let r;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error(`Unreachable`)}!this.types[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}getPenaltyScore(){let e=0;for(let t=0;t<this.size;t++){let n=!1,r=0,i=[0,0,0,0,0,0,0];for(let a=0;a<this.size;a++)this.modules[t][a]===n?(r++,r===5?e+=Ne:r>5&&e++):(this.finderPenaltyAddHistory(r,i),n||(e+=this.finderPenaltyCountPatterns(i)*K),n=this.modules[t][a],r=1);e+=this.finderPenaltyTerminateAndCount(n,r,i)*K}for(let t=0;t<this.size;t++){let n=!1,r=0,i=[0,0,0,0,0,0,0];for(let a=0;a<this.size;a++)this.modules[a][t]===n?(r++,r===5?e+=Ne:r>5&&e++):(this.finderPenaltyAddHistory(r,i),n||(e+=this.finderPenaltyCountPatterns(i)*K),n=this.modules[a][t],r=1);e+=this.finderPenaltyTerminateAndCount(n,r,i)*K}for(let t=0;t<this.size-1;t++)for(let n=0;n<this.size-1;n++){let r=this.modules[t][n];r===this.modules[t][n+1]&&r===this.modules[t+1][n]&&r===this.modules[t+1][n+1]&&(e+=Pe)}let t=0;for(let e of this.modules)t=e.reduce((e,t)=>e+ +!!t,t);let n=this.size*this.size,r=Math.ceil(Math.abs(t*20-n*10)/n)-1;return e+=r*Fe,e}getAlignmentPatternPositions(){if(this.version===1)return[];{let e=Math.floor(this.version/7)+2,t=this.version===32?26:Math.ceil((this.version*4+4)/(e*2-2))*2,n=[6];for(let r=this.size-7;n.length<e;r-=t)n.splice(1,0,r);return n}}finderPenaltyCountPatterns(e){let t=e[1],n=t>0&&e[2]===t&&e[3]===t*3&&e[4]===t&&e[5]===t;return(n&&e[0]>=t*4&&e[6]>=t?1:0)+(n&&e[6]>=t*4&&e[0]>=t?1:0)}finderPenaltyTerminateAndCount(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),t+=this.size,this.finderPenaltyAddHistory(t,n),this.finderPenaltyCountPatterns(n)}finderPenaltyAddHistory(e,t){t[0]===0&&(e+=this.size),t.pop(),t.unshift(e)}};function q(e,t,n){if(t<0||t>31||e>>>t)throw RangeError(`Value out of range`);for(let r=t-1;r>=0;r--)n.push(e>>>r&1)}function J(e,t){return(e>>>t&1)!=0}var Y=class{constructor(e,t,n){if(this.mode=e,this.numChars=t,this.bitData=n,t<0)throw RangeError(`Invalid argument`);this.bitData=n.slice()}getData(){return this.bitData.slice()}},ze=[1,10,12,14],Be=[2,9,11,13],Ve=[4,8,16,16];function He(e,t){return e[Math.floor((t+7)/17)+1]}function Ue(e){let t=[];for(let n of e)q(n,8,t);return new Y(Ve,e.length,t)}function We(e){if(!qe(e))throw RangeError(`String contains non-numeric characters`);let t=[];for(let n=0;n<e.length;){let r=Math.min(e.length-n,3);q(Number.parseInt(e.substring(n,n+r),10),r*3+1,t),n+=r}return new Y(ze,e.length,t)}function Ge(e){if(!Je(e))throw RangeError(`String contains unencodable characters in alphanumeric mode`);let t=[],n;for(n=0;n+2<=e.length;n+=2){let r=U.indexOf(e.charAt(n))*45;r+=U.indexOf(e.charAt(n+1)),q(r,11,t)}return n<e.length&&q(U.indexOf(e.charAt(n)),6,t),new Y(Be,e.length,t)}function Ke(e){return e===``?[]:qe(e)?[We(e)]:Je(e)?[Ge(e)]:[Ue(Xe(e))]}function qe(e){return je.test(e)}function Je(e){return Me.test(e)}function Ye(e,t){let n=0;for(let r of e){let e=He(r.mode,t);if(r.numChars>=1<<e)return 1/0;n+=4+e+r.bitData.length}return n}function Xe(e){e=encodeURI(e);let t=[];for(let n=0;n<e.length;n++)e.charAt(n)===`%`?(t.push(Number.parseInt(e.substring(n+1,n+3),16)),n+=2):t.push(e.charCodeAt(n));return t}function X(e){if(e<W||e>G)throw RangeError(`Version number out of range`);let t=(16*e+128)*e+64;if(e>=2){let n=Math.floor(e/7)+2;t-=(25*n-10)*n-55,e>=7&&(t-=36)}return t}function Z(e,t){return Math.floor(X(e)/8)-Ie[t[0]][e]*Le[t[0]][e]}function Ze(e){if(e<1||e>255)throw RangeError(`Degree out of range`);let t=[];for(let n=0;n<e-1;n++)t.push(0);t.push(1);let n=1;for(let r=0;r<e;r++){for(let e=0;e<t.length;e++)t[e]=Q(t[e],n),e+1<t.length&&(t[e]^=t[e+1]);n=Q(n,2)}return t}function Qe(e,t){let n=t.map(e=>0);for(let r of e){let e=r^n.shift();n.push(0),t.forEach((t,r)=>n[r]^=Q(t,e))}return n}function Q(e,t){if(e>>>8||t>>>8)throw RangeError(`Byte out of range`);let n=0;for(let r=7;r>=0;r--)n=n<<1^(n>>>7)*285,n^=(t>>>r&1)*e;return n}function $e(e,t,n=1,r=40,i=-1,a=!0){if(!(W<=n&&n<=r&&r<=G)||i<-1||i>7)throw RangeError(`Invalid value`);let o,s;for(o=n;;o++){let n=Z(o,t)*8,i=Ye(e,o);if(i<=n){s=i;break}if(o>=r)throw RangeError(`Data too long`)}for(let e of[De,Oe,ke])a&&s<=Z(o,e)*8&&(t=e);let c=[];for(let t of e){q(t.mode[0],4,c),q(t.numChars,He(t.mode,o),c);for(let e of t.getData())c.push(e)}let l=Z(o,t)*8;q(0,Math.min(4,l-c.length),c),q(0,(8-c.length%8)%8,c);for(let e=236;c.length<l;e^=253)q(e,8,c);let u=Array.from({length:Math.ceil(c.length/8)},()=>0);return c.forEach((e,t)=>u[t>>>3]|=e<<7-(t&7)),new Re(o,t,u,i)}function et(e,t){let{ecc:n=`L`,boostEcc:r=!1,minVersion:i=1,maxVersion:a=40,maskPattern:o=-1,border:s=1}=t||{},c=typeof e==`string`?Ke(e):Array.isArray(e)?[Ue(e)]:void 0;if(!c)throw Error(`uqr only supports encoding string and binary data, but got: ${typeof e}`);let l=$e(c,Ae[n],i,a,o,r),u=tt({version:l.version,maskPattern:l.mask,size:l.size,data:l.modules,types:l.types},s);return t?.invert&&(u.data=u.data.map(e=>e.map(e=>!e))),t?.onEncoded?.(u),u}function tt(e,t=1){if(!t)return e;let{size:n}=e,r=n+t*2;e.size=r,e.data.forEach(e=>{for(let n=0;n<t;n++)e.unshift(!1),e.push(!1)});for(let n=0;n<t;n++)e.data.unshift(Array.from({length:r},e=>!1)),e.data.push(Array.from({length:r},e=>!1));let i=H.Border;e.types.forEach(e=>{for(let n=0;n<t;n++)e.unshift(i),e.push(i)});for(let n=0;n<t;n++)e.types.unshift(Array.from({length:r},e=>i)),e.types.push(Array.from({length:r},e=>i));return e}var nt=e=>{let{data:t,size:n}=et(e,{ecc:`M`,border:4}),r=``;return t.forEach((e,t)=>e.forEach((e,n)=>{e&&(r+=`M${n} ${t}h1v1h-1z`)})),{size:n,path:r}};function rt({value:e,label:t,className:n}){let{size:i,path:a}=(0,Te.useMemo)(()=>nt(e),[e]);return(0,B.jsxs)(`svg`,{"aria-label":t,className:r(`aspect-square rounded-md`,n),role:`img`,shapeRendering:`crispEdges`,viewBox:`0 0 ${i} ${i}`,children:[(0,B.jsx)(`rect`,{fill:`#fff`,height:i,width:i}),(0,B.jsx)(`path`,{d:a,fill:`#000`})]})}var it={apk:`Scan with an Android phone's camera, then open the APK to install it.`,ipa:`Scan to download the .ipa. It installs only on devices its signing profile allows (development or ad hoc); an App Store export reaches testers through TestFlight.`,aab:`Scan to download the .aab. It installs through Google Play or bundletool, not from the file.`,app:`Scan to download the .app.zip on a Mac, then install it on a simulator.`};function at({build:t}){let n=be(t.id,!0),r=n.data?.url;return r===null?(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`This storage gives no download link a phone can open without signing in, so there is no QR code. Download the build here, or open this page on the phone signed in to the console.`}):(0,B.jsxs)(`div`,{className:`flex flex-col items-center gap-2`,children:[r===void 0?n.isError?(0,B.jsxs)(C,{variant:`destructive`,children:[(0,B.jsx)(x,{}),(0,B.jsx)(y,{children:`The download link couldn't be created`}),(0,B.jsx)(S,{children:n.error.message})]}):(0,B.jsx)(e,{className:`size-48`}):(0,B.jsx)(rt,{className:`size-48`,label:`Download ${h(t)}`,value:r}),(0,B.jsx)(`p`,{className:`max-w-56 text-center text-xs text-muted-foreground`,children:it[t.artifact_kind]})]})}function $({build:e}){return(0,B.jsx)(`span`,{className:`font-mono text-foreground`,translate:`no`,children:e.submitted_version??`this build`})}function ot({build:e}){switch(e.artifact_kind){case`apk`:return(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-2`,children:[(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`On an emulator or a phone over USB, download the APK and install it:`}),(0,B.jsx)(_,{label:`the install command`,value:re(e)})]});case`app`:return(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-2`,children:[(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`A simulator build runs only in the iOS Simulator. Download the archive, then install it on the booted simulator:`}),(0,B.jsx)(_,{label:`the install command`,value:ie(e)})]});case`aab`:return v(e)?(0,B.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Submitted to Google Play internal testing as`,` `,(0,B.jsx)($,{build:e}),`. Testers install it from Google Play once its release is rolled out; on an app Play Console hasn't published, the release is a draft you roll out in Play Console.`]}):(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`An AAB installs through Google Play or bundletool, not from a download.`});case`ipa`:return v(e)?(0,B.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`On TestFlight as `,(0,B.jsx)($,{build:e}),`: testers install it from TestFlight.`]}):(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-2`,children:[(0,B.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`An IPA installs on registered devices through Xcode or Apple Configurator. For testers, upload it to TestFlight:`}),(0,B.jsx)(_,{label:`the TestFlight command`,value:`npx hot-updater testflight`})]})}}function st({build:e}){let t=h(e);return(0,B.jsxs)(w,{className:`overflow-hidden shadow-sm`,children:[(0,B.jsx)(D,{className:`border-b px-4 py-3`,children:(0,B.jsx)(b,{className:`text-sm`,children:(0,B.jsx)(`h2`,{children:`Install`})})}),(0,B.jsxs)(T,{className:`grid gap-6 p-4 sm:grid-cols-[12rem_minmax(0,1fr)] sm:p-6`,children:[(0,B.jsx)(at,{build:e}),(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-4`,children:[(0,B.jsx)(ot,{build:e}),(0,B.jsxs)(`a`,{className:n({className:`h-auto min-h-11 w-full min-w-0 py-2 whitespace-normal sm:min-h-8 sm:w-fit`,variant:`outline`}),download:!0,href:d(e.id),children:[(0,B.jsx)(se,{"data-icon":`inline-start`}),(0,B.jsxs)(`span`,{className:`text-left [overflow-wrap:anywhere]`,children:[`Download `,t]})]})]})]})]})}var ct=`https://hot-updater.dev/docs/guides/bundle-diffing#first-ota-update`;function lt({detail:r,page:i,onPage:a}){let o=i.before!==void 0||i.after!==void 0,s=ve(r.build.min_bundle_id,i,o),c=o?s.data:r.patches,{next:d,previous:f}=c??{};return(0,B.jsxs)(w,{className:`overflow-hidden shadow-sm`,id:`patches`,children:[(0,B.jsx)(D,{className:`border-b px-4 py-3`,children:(0,B.jsx)(b,{className:`text-sm`,children:(0,B.jsx)(`h2`,{children:`Patches from this build`})})}),(0,B.jsxs)(T,{className:`p-0`,children:[o&&s.isError?(0,B.jsx)(`div`,{className:`p-4`,children:(0,B.jsxs)(C,{variant:`destructive`,children:[(0,B.jsx)(x,{}),(0,B.jsx)(y,{children:`Patches couldn't be loaded`}),(0,B.jsx)(S,{children:s.error.message}),(0,B.jsx)(he,{children:(0,B.jsxs)(t,{onClick:()=>void s.refetch(),size:`xs`,variant:`outline`,children:[(0,B.jsx)(de,{"data-icon":`inline-start`}),`Retry`]})})]})}):c===void 0?(0,B.jsxs)(`div`,{className:`flex flex-col gap-2 p-4`,children:[(0,B.jsx)(`span`,{className:`sr-only`,children:`Loading patches`}),(0,B.jsx)(e,{className:`h-4 w-full`}),(0,B.jsx)(e,{className:`h-4 w-2/3`})]}):c.data.length===0?o?(0,B.jsxs)(N,{className:`py-10`,children:[(0,B.jsx)(k,{children:(0,B.jsx)(j,{children:`No more patches`})}),(0,B.jsx)(t,{className:`min-h-11 sm:min-h-8`,onClick:()=>a({}),variant:`outline`,children:`Show the newest patches`})]}):(0,B.jsxs)(N,{className:`py-10`,children:[(0,B.jsxs)(k,{children:[(0,B.jsx)(O,{variant:`icon`,children:(0,B.jsx)(ce,{})}),(0,B.jsx)(j,{children:`No patches from this build`}),(0,B.jsxs)(M,{children:[`A deploy adds a patch from this build's bundle while it is its scope's newest build and the deploy config turns patches on, with `,(0,B.jsx)(`code`,{className:`font-mono`,children:`patch`}),` in`,` `,(0,B.jsx)(`code`,{className:`font-mono`,children:`hot-updater.config.ts`}),`.`]})]}),(0,B.jsx)(A,{children:(0,B.jsxs)(`a`,{className:n({className:`min-h-11 sm:min-h-8`,variant:`outline`}),href:ct,rel:`noreferrer`,target:`_blank`,children:[`Bundle diffing guide`,(0,B.jsx)(u,{"data-icon":`inline-end`})]})})]}):(0,B.jsxs)(z,{"aria-busy":o&&s.isPlaceholderData,"aria-label":`Patches from this build`,className:`table-fixed`,children:[(0,B.jsx)(P,{children:(0,B.jsxs)(L,{children:[(0,B.jsx)(F,{children:`Target bundle`}),(0,B.jsx)(F,{className:`w-28`,children:`Patch size`}),(0,B.jsx)(F,{className:`hidden w-32 sm:table-cell`,children:`Created`})]})}),(0,B.jsx)(I,{children:c.data.map(e=>(0,B.jsxs)(L,{children:[(0,B.jsx)(R,{className:`py-3 whitespace-normal`,children:(0,B.jsx)(l,{className:`block font-mono text-xs [overflow-wrap:anywhere] underline-offset-4 hover:underline`,search:{bundleId:e.bundleId},title:e.bundleId,to:`/`,translate:`no`,children:e.bundleId})}),(0,B.jsx)(R,{className:`text-xs tabular-nums`,children:m(e.byteSize)}),(0,B.jsx)(R,{className:`hidden sm:table-cell`,children:(0,B.jsx)(p,{uuid:e.bundleId})})]},e.bundleId))})]}),d!==void 0||f!==void 0?(0,B.jsxs)(`nav`,{"aria-label":`Patch pages`,className:`flex items-center justify-end gap-2 border-t px-4 py-2`,children:[(0,B.jsxs)(t,{className:`min-h-11 sm:min-h-7`,disabled:f===void 0||o&&s.isPlaceholderData,onClick:()=>a({after:f}),size:`sm`,variant:`ghost`,children:[(0,B.jsx)(ee,{"data-icon":`inline-start`}),`Newer`]}),(0,B.jsxs)(t,{className:`min-h-11 sm:min-h-7`,disabled:d===void 0||o&&s.isPlaceholderData,onClick:()=>a({before:d}),size:`sm`,variant:`ghost`,children:[`Older`,(0,B.jsx)(ae,{"data-icon":`inline-end`})]})]}):null]})]})}var ut=(e,t)=>{let n=e.data.find(e=>e.bundleId===t);if(n!==void 0)return{kind:`patch`,byteSize:n.byteSize};let r=e.data.at(-1)?.bundleId;return e.next!==void 0&&r!==void 0&&t<r?{kind:`unknown`}:{kind:`full`}};function dt({download:e}){switch(e.kind){case`patch`:return(0,B.jsxs)(`span`,{className:`text-xs`,children:[`Patch`,` `,(0,B.jsxs)(`span`,{className:`text-muted-foreground tabular-nums`,children:[`(`,m(e.byteSize),`)`]})]});case`full`:return(0,B.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`Full bundle`});case`unknown`:return(0,B.jsx)(`a`,{className:`text-xs underline-offset-4 hover:underline`,href:`#patches`,children:`See patches`})}}function ft({detail:e}){let{servedNow:t,patches:r,catalogScopeKey:i}=e,a=(0,B.jsxs)(l,{className:`inline-flex items-center gap-0.5 underline-offset-4 hover:underline`,search:{scopeKey:i},to:`/`,children:[`All releases in this scope`,(0,B.jsx)(u,{"aria-hidden":`true`,className:`size-3`})]});return(0,B.jsxs)(w,{className:`overflow-hidden shadow-sm`,children:[(0,B.jsx)(D,{className:`border-b px-4 py-3`,children:(0,B.jsx)(b,{className:`text-sm`,children:(0,B.jsx)(`h2`,{children:`Served now`})})}),(0,B.jsx)(T,{className:`p-0`,children:t.length===0?(0,B.jsxs)(N,{className:`py-10`,children:[(0,B.jsxs)(k,{children:[(0,B.jsx)(O,{variant:`icon`,children:(0,B.jsx)(Se,{})}),(0,B.jsx)(j,{children:`No OTA for this build yet`}),(0,B.jsxs)(M,{children:[`No release in this build's scope is newer than its built-in bundle. Deploy one with`,` `,(0,B.jsx)(`code`,{className:`font-mono`,children:`hot-updater deploy`}),`, and it shows here.`]})]}),(0,B.jsx)(A,{children:(0,B.jsx)(l,{className:n({className:`min-h-11 sm:min-h-8`,variant:`outline`}),search:{scopeKey:i},to:`/`,children:`All releases in this scope`})})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsxs)(z,{"aria-label":`Served now`,className:`table-fixed`,children:[(0,B.jsx)(P,{children:(0,B.jsxs)(L,{children:[(0,B.jsx)(F,{children:`Bundle`}),(0,B.jsx)(F,{className:`hidden w-40 md:table-cell`,children:`Rollout`}),(0,B.jsx)(F,{className:`hidden w-32 sm:table-cell`,children:`Created`}),(0,B.jsx)(F,{className:`w-32`,children:`First OTA download`})]})}),(0,B.jsx)(I,{children:t.map(e=>{let t=e.bundleId,n=e.rolloutCohortCount/ge*100;return(0,B.jsxs)(L,{children:[(0,B.jsx)(R,{className:`py-3 whitespace-normal`,children:(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1`,children:[(0,B.jsx)(l,{className:`font-mono text-xs [overflow-wrap:anywhere] underline-offset-4 hover:underline`,search:{releaseId:e.releaseId},title:t,to:`/`,translate:`no`,children:t}),e.message?(0,B.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e.message}):null]})}),(0,B.jsx)(R,{className:`hidden md:table-cell`,children:(0,B.jsxs)(`div`,{className:`flex flex-wrap gap-1`,children:[e.targetCohorts.length>0?(0,B.jsx)(E,{variant:`secondary`,children:e.targetCohorts.length===1?`1 cohort`:`${e.targetCohorts.length} cohorts`}):(0,B.jsx)(xe,{percentage:n}),e.shouldForceUpdate?(0,B.jsx)(E,{variant:`outline`,children:`Forced`}):null]})}),(0,B.jsx)(R,{className:`hidden sm:table-cell`,children:(0,B.jsx)(p,{uuid:t})}),(0,B.jsx)(R,{children:(0,B.jsx)(dt,{download:ut(r,t)})})]},e.releaseId)})})]}),(0,B.jsxs)(`p`,{className:`border-t px-4 py-3 text-xs text-muted-foreground`,children:[`What the update check serves an install of this build now, newest first: not the history. `,a]})]})})]})}function pt({buildId:r,search:i,onSearchChange:a}){let o=ye(r),c=o.data,u={...i.patchesBefore===void 0?{}:{before:i.patchesBefore},...i.patchesAfter===void 0?{}:{after:i.patchesAfter}};return(0,B.jsxs)(`div`,{className:`flex h-svh min-h-0 flex-col`,children:[(0,B.jsxs)(`header`,{className:`sticky top-0 z-10 flex shrink-0 items-center gap-x-2 border-b bg-background px-3 py-3 sm:min-h-12 sm:bg-card/70 sm:px-4 sm:backdrop-blur-sm`,children:[(0,B.jsx)(s,{className:`-ml-1 size-11 lg:size-7`}),(0,B.jsxs)(l,{className:n({className:`min-h-11 sm:min-h-7`,size:`sm`,variant:`ghost`}),to:`/native-builds`,children:[(0,B.jsx)(ee,{"data-icon":`inline-start`}),`Native builds`]}),c?(0,B.jsxs)(`h1`,{className:`flex min-w-0 items-center gap-1.5 text-sm font-medium`,children:[(0,B.jsx)(_e,{className:`size-3.5 shrink-0 text-muted-foreground`,platform:c.build.platform}),(0,B.jsxs)(`span`,{className:`min-w-0 [overflow-wrap:anywhere]`,children:[f(c.build.platform),` `,te[c.build.artifact_kind].label,` ·`,` `,(0,B.jsx)(`span`,{translate:`no`,children:c.channel})]}),(0,B.jsx)(`span`,{className:`hidden text-xs font-normal text-muted-foreground tabular-nums sm:inline`,children:m(c.build.byte_size)})]}):(0,B.jsx)(`h1`,{className:`sr-only`,children:`Native build`})]}),(0,B.jsx)(`div`,{className:`min-h-0 min-w-0 flex-1 overflow-y-auto bg-muted/5 p-3 sm:p-6`,children:(0,B.jsx)(`div`,{className:`mx-auto flex w-full max-w-5xl flex-col gap-4 sm:gap-6`,children:o.isError?(0,B.jsxs)(C,{variant:`destructive`,children:[(0,B.jsx)(x,{}),(0,B.jsx)(y,{children:`The native build couldn't be loaded`}),(0,B.jsx)(S,{children:o.error.message}),(0,B.jsx)(he,{children:(0,B.jsxs)(t,{onClick:()=>void o.refetch(),size:`xs`,variant:`outline`,children:[(0,B.jsx)(de,{"data-icon":`inline-start`}),`Retry`]})})]}):c===void 0?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(`span`,{className:`sr-only`,children:`Loading the native build`}),(0,B.jsx)(e,{className:`h-56 w-full rounded-xl`}),(0,B.jsx)(e,{className:`h-64 w-full rounded-xl`})]}):c===null?(0,B.jsxs)(N,{className:`border py-16`,children:[(0,B.jsxs)(k,{children:[(0,B.jsx)(O,{variant:`icon`,children:(0,B.jsx)(ue,{})}),(0,B.jsx)(j,{children:`Native build not found`}),(0,B.jsxs)(M,{children:[`No native build has this ID. It may have been deleted with`,` `,(0,B.jsx)(`code`,{className:`font-mono`,children:`native-build delete`}),`, or be from another console.`]})]}),(0,B.jsx)(A,{children:(0,B.jsx)(l,{className:n({className:`min-h-11 sm:min-h-8`,variant:`outline`}),to:`/native-builds`,children:`Show all native builds`})})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(st,{build:c.build}),(0,B.jsx)(we,{detail:c}),(0,B.jsx)(ft,{detail:c}),(0,B.jsx)(lt,{detail:c,onPage:e=>a({...e.before===void 0?{}:{patchesBefore:e.before},...e.after===void 0?{}:{patchesAfter:e.after}}),page:u})]})})})]})}function mt(){let{buildId:e}=g.useParams(),t=g.useSearch(),n=g.useNavigate();return(0,B.jsx)(pt,{buildId:e,onSearchChange:e=>void n({search:e,replace:!0}),search:t})}export{mt as component};