@liiift-studio/deploy-vercel-from-sanity 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -336,8 +336,11 @@ Requirements for the route:
336
336
  your site's domain, so every call is cross-origin and the preflight fails
337
337
  without an allow-list. Echo one allow-listed origin; do not send `*` to an
338
338
  endpoint that takes an `Authorization` header.
339
- - **Restrict which branches it will bump.** The recovery flow only ever needs the
340
- branches you deploy.
339
+ - **Restrict which branches it will bump**, and read that list from the
340
+ environment rather than hard-coding it. The recovery flow only ever needs the
341
+ branches you deploy, but the list has to change when you add a deploy target —
342
+ and a hard-coded list can only be changed by shipping a deploy, which is the
343
+ very thing that is broken when this route is needed.
341
344
  - **Answer `{ error }` on failure.** The plugin shows that string to the editor
342
345
  verbatim, in preference to its own generic message.
343
346
  - **Serve it over https.** The plugin refuses a plaintext endpoint, because the
package/dist/index.d.mts CHANGED
@@ -60,6 +60,12 @@ interface VercelDeployment {
60
60
  * deployment comes back BLOCKED.
61
61
  */
62
62
  githubCommitAuthorLogin?: string;
63
+ /**
64
+ * Branch the deploy hook is configured for. Present only on hook-triggered
65
+ * deployments, and more reliable than githubCommitRef for learning which branch
66
+ * a target follows, since it describes the hook rather than one commit.
67
+ */
68
+ deployHookRef?: string;
63
69
  /** GitHub repo in "org/repo" format — used to construct commit links */
64
70
  githubRepo?: string;
65
71
  /** GitHub org slug — fallback when githubRepo is absent */
package/dist/index.d.ts CHANGED
@@ -60,6 +60,12 @@ interface VercelDeployment {
60
60
  * deployment comes back BLOCKED.
61
61
  */
62
62
  githubCommitAuthorLogin?: string;
63
+ /**
64
+ * Branch the deploy hook is configured for. Present only on hook-triggered
65
+ * deployments, and more reliable than githubCommitRef for learning which branch
66
+ * a target follows, since it describes the hook rather than one commit.
67
+ */
68
+ deployHookRef?: string;
63
69
  /** GitHub repo in "org/repo" format — used to construct commit links */
64
70
  githubRepo?: string;
65
71
  /** GitHub org slug — fallback when githubRepo is absent */
package/dist/index.js CHANGED
@@ -617,7 +617,7 @@ async function vercelFetch(path, token, init) {
617
617
  async function listDeployments(opts) {
618
618
  const params = new URLSearchParams({
619
619
  projectId: opts.projectId,
620
- "meta-deployHookId": opts.hookId,
620
+ ...opts.branch ? { "meta-githubCommitRef": opts.branch } : { "meta-deployHookId": opts.hookId },
621
621
  limit: String(opts.limit ?? 10)
622
622
  });
623
623
  if (opts.teamId) params.set("teamId", opts.teamId);
@@ -671,7 +671,7 @@ async function proxyFetch(url, statusKey, init) {
671
671
  }
672
672
  return res.json();
673
673
  }
674
- async function fetchDeployments(transport, target, limit) {
674
+ async function fetchDeployments(transport, target, limit, branch) {
675
675
  if (transport.mode === "direct") {
676
676
  if (!target.projectId || !target.hookId) return [];
677
677
  return listDeployments({
@@ -679,11 +679,13 @@ async function fetchDeployments(transport, target, limit) {
679
679
  hookId: target.hookId,
680
680
  token: transport.token,
681
681
  teamId: target.teamId,
682
- limit
682
+ limit,
683
+ branch
683
684
  });
684
685
  }
685
686
  const params = new URLSearchParams({ key: target.proxyKey ?? "" });
686
687
  if (limit) params.set("limit", String(limit));
688
+ if (branch) params.set("branch", branch);
687
689
  const data = await proxyFetch(
688
690
  `${transport.proxyUrl}/deployments?${params}`,
689
691
  transport.statusKey
@@ -952,7 +954,7 @@ function StatusBadge({ state, showSpinner }) {
952
954
  // src/components/DeployHistory.tsx
953
955
  var import_react7 = require("react");
954
956
  var import_jsx_runtime9 = require("react/jsx-runtime");
955
- function DeployHistory({ target, token, onClose }) {
957
+ function DeployHistory({ target, token, branch, onClose }) {
956
958
  const dialogId = (0, import_react7.useId)();
957
959
  const pluginConfig = usePluginConfig();
958
960
  const [deployments, setDeployments] = (0, import_react7.useState)([]);
@@ -971,14 +973,14 @@ function DeployHistory({ target, token, onClose }) {
971
973
  setLoading(true);
972
974
  setError(null);
973
975
  try {
974
- const data = await fetchDeployments(transport, targetRef, 20);
976
+ const data = await fetchDeployments(transport, targetRef, 20, branch);
975
977
  setDeployments(data);
976
978
  } catch (err) {
977
979
  setError(err instanceof Error ? err.message : "Failed to load history");
978
980
  } finally {
979
981
  setLoading(false);
980
982
  }
981
- }, [transport, targetRef]);
983
+ }, [transport, targetRef, branch]);
982
984
  (0, import_react7.useEffect)(() => {
983
985
  load();
984
986
  }, [load]);
@@ -1004,7 +1006,7 @@ function DeployHistory({ target, token, onClose }) {
1004
1006
  ] }) }),
1005
1007
  deployments.map((d) => {
1006
1008
  const { label, tone } = stateLabel(d.state);
1007
- const branch = d.meta?.githubCommitRef ?? "\u2014";
1009
+ const branch2 = d.meta?.githubCommitRef ?? "\u2014";
1008
1010
  const sha = shortSha(d.meta?.githubCommitSha);
1009
1011
  const message = d.meta?.githubCommitMessage?.split("\n")[0] ?? "";
1010
1012
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 3, align: "center", children: [
@@ -1020,7 +1022,7 @@ function DeployHistory({ target, token, onClose }) {
1020
1022
  ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) }),
1021
1023
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { tone, children: label }) }),
1022
1024
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 1, children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: branch }),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: branch2 }),
1024
1026
  sha && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 0, muted: true, children: [
1025
1027
  sha,
1026
1028
  message ? ` \xB7 ${message.slice(0, 40)}${message.length > 40 ? "\u2026" : ""}` : ""
@@ -1054,6 +1056,7 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
1054
1056
  var POLL_INTERVAL_MS = 5e3;
1055
1057
  var LABEL_WIDTH = 64;
1056
1058
  var PENDING_TIMEOUT_MS = 6e4;
1059
+ var BUMP_WATCH_TIMEOUT_MS = 3e5;
1057
1060
  function DeployItem({ target, token, onDelete, onEdit }) {
1058
1061
  const { projectId, hookId } = parseHookUrl(target.url);
1059
1062
  const toast = useToast();
@@ -1083,8 +1086,11 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1083
1086
  const [logError, setLogError] = (0, import_react8.useState)(null);
1084
1087
  const [pollError, setPollError] = (0, import_react8.useState)(null);
1085
1088
  const [bumping, setBumping] = (0, import_react8.useState)(false);
1089
+ const [awaitingBump, setAwaitingBump] = (0, import_react8.useState)(false);
1090
+ const [knownBranch, setKnownBranch] = (0, import_react8.useState)(void 0);
1086
1091
  const [bumpResult, setBumpResult] = (0, import_react8.useState)(null);
1087
1092
  const triggeredFromUidRef = (0, import_react8.useRef)(void 0);
1093
+ const bumpedFromUidRef = (0, import_react8.useRef)(void 0);
1088
1094
  const requestSeqRef = (0, import_react8.useRef)(0);
1089
1095
  const mountedRef = (0, import_react8.useRef)(true);
1090
1096
  (0, import_react8.useEffect)(() => {
@@ -1101,24 +1107,37 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1101
1107
  if (!ready) return;
1102
1108
  const seq = ++requestSeqRef.current;
1103
1109
  try {
1104
- const data = await fetchDeployments(transport, targetRef);
1110
+ const data = await fetchDeployments(transport, targetRef, void 0, knownBranch);
1105
1111
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
1106
1112
  setDeployments(data);
1107
1113
  setPollError(null);
1114
+ if (!knownBranch) {
1115
+ const learned = data[0]?.meta?.deployHookRef ?? data[0]?.meta?.githubCommitRef;
1116
+ if (learned) setKnownBranch(learned);
1117
+ }
1108
1118
  } catch (err) {
1109
1119
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
1110
1120
  setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
1111
1121
  console.error("Deploy-vercel-from-sanity: fetch error", err);
1112
1122
  }
1113
- }, [transport, targetRef]);
1123
+ }, [transport, targetRef, knownBranch]);
1114
1124
  (0, import_react8.useEffect)(() => {
1115
1125
  fetchDeployments2().finally(() => setLoadingInitial(false));
1116
1126
  }, [fetchDeployments2]);
1117
1127
  (0, import_react8.useEffect)(() => {
1118
- if (!isActive) return;
1128
+ if (!isActive && !awaitingBump) return;
1119
1129
  const id = setInterval(fetchDeployments2, POLL_INTERVAL_MS);
1120
1130
  return () => clearInterval(id);
1121
- }, [isActive, fetchDeployments2]);
1131
+ }, [isActive, awaitingBump, fetchDeployments2]);
1132
+ (0, import_react8.useEffect)(() => {
1133
+ if (!awaitingBump) return;
1134
+ if (latest?.uid && latest.uid !== bumpedFromUidRef.current) setAwaitingBump(false);
1135
+ }, [awaitingBump, latest?.uid]);
1136
+ (0, import_react8.useEffect)(() => {
1137
+ if (!awaitingBump) return;
1138
+ const id = setTimeout(() => setAwaitingBump(false), BUMP_WATCH_TIMEOUT_MS);
1139
+ return () => clearTimeout(id);
1140
+ }, [awaitingBump]);
1122
1141
  (0, import_react8.useEffect)(() => {
1123
1142
  if (!isPending) return;
1124
1143
  if (latest?.uid && latest.uid !== triggeredFromUidRef.current) setPendingSince(null);
@@ -1248,6 +1267,8 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1248
1267
  // failure. Unused by workflow-dispatch mode.
1249
1268
  studioToken: client.config().token
1250
1269
  });
1270
+ bumpedFromUidRef.current = latest?.uid;
1271
+ setAwaitingBump(true);
1251
1272
  setBumpResult({
1252
1273
  ok: true,
1253
1274
  message: `Version bump requested on ${ref}. The new deploy appears here in a minute or two.`
@@ -1264,7 +1285,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1264
1285
  } finally {
1265
1286
  setBumping(false);
1266
1287
  }
1267
- }, [pluginConfig.unblock, latest?.meta?.githubCommitRef, currentUser, target.name, toast, client]);
1288
+ }, [pluginConfig.unblock, latest?.meta?.githubCommitRef, latest?.uid, currentUser, target.name, toast, client]);
1268
1289
  const branch = latest?.meta?.githubCommitRef;
1269
1290
  const commitMsg = latest?.meta?.githubCommitMessage?.split("\n")[0];
1270
1291
  const sha = shortSha(latest?.meta?.githubCommitSha);
@@ -1478,9 +1499,12 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1478
1499
  ", which Vercel will build."
1479
1500
  ] })
1480
1501
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, children: pluginConfig.unblock ? "This deployment carries no branch information, so a version bump cannot be targeted. Ask a developer to deploy manually." : "Ask a developer to push a version bump \u2014 a commit by an authorised author is needed before this site can deploy." }),
1481
- bumpResult && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 0, weight: bumpResult.ok ? "semibold" : void 0, children: [
1482
- bumpResult.ok ? "\u2713 " : "",
1483
- bumpResult.message
1502
+ bumpResult && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, children: [
1503
+ awaitingBump && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { muted: true }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 0, weight: bumpResult.ok ? "semibold" : void 0, children: [
1505
+ bumpResult.ok && !awaitingBump ? "\u2713 " : "",
1506
+ bumpResult.message
1507
+ ] })
1484
1508
  ] })
1485
1509
  ] }) })
1486
1510
  ] }),
@@ -1585,6 +1609,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1585
1609
  {
1586
1610
  target,
1587
1611
  token,
1612
+ branch: knownBranch,
1588
1613
  onClose: () => setShowHistory(false)
1589
1614
  }
1590
1615
  )
@@ -1870,7 +1895,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1870
1895
  }
1871
1896
 
1872
1897
  // src/version.ts
1873
- var VERSION = "1.5.0";
1898
+ var VERSION = "1.6.0";
1874
1899
 
1875
1900
  // src/components/DeployTool.tsx
1876
1901
  var import_jsx_runtime13 = require("react/jsx-runtime");
package/dist/index.mjs CHANGED
@@ -582,7 +582,7 @@ async function vercelFetch(path, token, init) {
582
582
  async function listDeployments(opts) {
583
583
  const params = new URLSearchParams({
584
584
  projectId: opts.projectId,
585
- "meta-deployHookId": opts.hookId,
585
+ ...opts.branch ? { "meta-githubCommitRef": opts.branch } : { "meta-deployHookId": opts.hookId },
586
586
  limit: String(opts.limit ?? 10)
587
587
  });
588
588
  if (opts.teamId) params.set("teamId", opts.teamId);
@@ -636,7 +636,7 @@ async function proxyFetch(url, statusKey, init) {
636
636
  }
637
637
  return res.json();
638
638
  }
639
- async function fetchDeployments(transport, target, limit) {
639
+ async function fetchDeployments(transport, target, limit, branch) {
640
640
  if (transport.mode === "direct") {
641
641
  if (!target.projectId || !target.hookId) return [];
642
642
  return listDeployments({
@@ -644,11 +644,13 @@ async function fetchDeployments(transport, target, limit) {
644
644
  hookId: target.hookId,
645
645
  token: transport.token,
646
646
  teamId: target.teamId,
647
- limit
647
+ limit,
648
+ branch
648
649
  });
649
650
  }
650
651
  const params = new URLSearchParams({ key: target.proxyKey ?? "" });
651
652
  if (limit) params.set("limit", String(limit));
653
+ if (branch) params.set("branch", branch);
652
654
  const data = await proxyFetch(
653
655
  `${transport.proxyUrl}/deployments?${params}`,
654
656
  transport.statusKey
@@ -917,7 +919,7 @@ function StatusBadge({ state, showSpinner }) {
917
919
  // src/components/DeployHistory.tsx
918
920
  import { useId as useId3, useEffect as useEffect3, useMemo, useState as useState4, useCallback as useCallback3 } from "react";
919
921
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
920
- function DeployHistory({ target, token, onClose }) {
922
+ function DeployHistory({ target, token, branch, onClose }) {
921
923
  const dialogId = useId3();
922
924
  const pluginConfig = usePluginConfig();
923
925
  const [deployments, setDeployments] = useState4([]);
@@ -936,14 +938,14 @@ function DeployHistory({ target, token, onClose }) {
936
938
  setLoading(true);
937
939
  setError(null);
938
940
  try {
939
- const data = await fetchDeployments(transport, targetRef, 20);
941
+ const data = await fetchDeployments(transport, targetRef, 20, branch);
940
942
  setDeployments(data);
941
943
  } catch (err) {
942
944
  setError(err instanceof Error ? err.message : "Failed to load history");
943
945
  } finally {
944
946
  setLoading(false);
945
947
  }
946
- }, [transport, targetRef]);
948
+ }, [transport, targetRef, branch]);
947
949
  useEffect3(() => {
948
950
  load();
949
951
  }, [load]);
@@ -969,7 +971,7 @@ function DeployHistory({ target, token, onClose }) {
969
971
  ] }) }),
970
972
  deployments.map((d) => {
971
973
  const { label, tone } = stateLabel(d.state);
972
- const branch = d.meta?.githubCommitRef ?? "\u2014";
974
+ const branch2 = d.meta?.githubCommitRef ?? "\u2014";
973
975
  const sha = shortSha(d.meta?.githubCommitSha);
974
976
  const message = d.meta?.githubCommitMessage?.split("\n")[0] ?? "";
975
977
  return /* @__PURE__ */ jsx9(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs5(Flex, { gap: 3, align: "center", children: [
@@ -985,7 +987,7 @@ function DeployHistory({ target, token, onClose }) {
985
987
  ) : /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "\u2014" }) }),
986
988
  /* @__PURE__ */ jsx9(Box, { flex: 1, children: /* @__PURE__ */ jsx9(Badge, { tone, children: label }) }),
987
989
  /* @__PURE__ */ jsx9(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxs5(Stack, { space: 1, children: [
988
- /* @__PURE__ */ jsx9(Text, { size: 1, children: branch }),
990
+ /* @__PURE__ */ jsx9(Text, { size: 1, children: branch2 }),
989
991
  sha && /* @__PURE__ */ jsxs5(Text, { size: 0, muted: true, children: [
990
992
  sha,
991
993
  message ? ` \xB7 ${message.slice(0, 40)}${message.length > 40 ? "\u2026" : ""}` : ""
@@ -1019,6 +1021,7 @@ import { Fragment, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1019
1021
  var POLL_INTERVAL_MS = 5e3;
1020
1022
  var LABEL_WIDTH = 64;
1021
1023
  var PENDING_TIMEOUT_MS = 6e4;
1024
+ var BUMP_WATCH_TIMEOUT_MS = 3e5;
1022
1025
  function DeployItem({ target, token, onDelete, onEdit }) {
1023
1026
  const { projectId, hookId } = parseHookUrl(target.url);
1024
1027
  const toast = useToast();
@@ -1048,8 +1051,11 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1048
1051
  const [logError, setLogError] = useState5(null);
1049
1052
  const [pollError, setPollError] = useState5(null);
1050
1053
  const [bumping, setBumping] = useState5(false);
1054
+ const [awaitingBump, setAwaitingBump] = useState5(false);
1055
+ const [knownBranch, setKnownBranch] = useState5(void 0);
1051
1056
  const [bumpResult, setBumpResult] = useState5(null);
1052
1057
  const triggeredFromUidRef = useRef2(void 0);
1058
+ const bumpedFromUidRef = useRef2(void 0);
1053
1059
  const requestSeqRef = useRef2(0);
1054
1060
  const mountedRef = useRef2(true);
1055
1061
  useEffect4(() => {
@@ -1066,24 +1072,37 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1066
1072
  if (!ready) return;
1067
1073
  const seq = ++requestSeqRef.current;
1068
1074
  try {
1069
- const data = await fetchDeployments(transport, targetRef);
1075
+ const data = await fetchDeployments(transport, targetRef, void 0, knownBranch);
1070
1076
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
1071
1077
  setDeployments(data);
1072
1078
  setPollError(null);
1079
+ if (!knownBranch) {
1080
+ const learned = data[0]?.meta?.deployHookRef ?? data[0]?.meta?.githubCommitRef;
1081
+ if (learned) setKnownBranch(learned);
1082
+ }
1073
1083
  } catch (err) {
1074
1084
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
1075
1085
  setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
1076
1086
  console.error("Deploy-vercel-from-sanity: fetch error", err);
1077
1087
  }
1078
- }, [transport, targetRef]);
1088
+ }, [transport, targetRef, knownBranch]);
1079
1089
  useEffect4(() => {
1080
1090
  fetchDeployments2().finally(() => setLoadingInitial(false));
1081
1091
  }, [fetchDeployments2]);
1082
1092
  useEffect4(() => {
1083
- if (!isActive) return;
1093
+ if (!isActive && !awaitingBump) return;
1084
1094
  const id = setInterval(fetchDeployments2, POLL_INTERVAL_MS);
1085
1095
  return () => clearInterval(id);
1086
- }, [isActive, fetchDeployments2]);
1096
+ }, [isActive, awaitingBump, fetchDeployments2]);
1097
+ useEffect4(() => {
1098
+ if (!awaitingBump) return;
1099
+ if (latest?.uid && latest.uid !== bumpedFromUidRef.current) setAwaitingBump(false);
1100
+ }, [awaitingBump, latest?.uid]);
1101
+ useEffect4(() => {
1102
+ if (!awaitingBump) return;
1103
+ const id = setTimeout(() => setAwaitingBump(false), BUMP_WATCH_TIMEOUT_MS);
1104
+ return () => clearTimeout(id);
1105
+ }, [awaitingBump]);
1087
1106
  useEffect4(() => {
1088
1107
  if (!isPending) return;
1089
1108
  if (latest?.uid && latest.uid !== triggeredFromUidRef.current) setPendingSince(null);
@@ -1213,6 +1232,8 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1213
1232
  // failure. Unused by workflow-dispatch mode.
1214
1233
  studioToken: client.config().token
1215
1234
  });
1235
+ bumpedFromUidRef.current = latest?.uid;
1236
+ setAwaitingBump(true);
1216
1237
  setBumpResult({
1217
1238
  ok: true,
1218
1239
  message: `Version bump requested on ${ref}. The new deploy appears here in a minute or two.`
@@ -1229,7 +1250,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1229
1250
  } finally {
1230
1251
  setBumping(false);
1231
1252
  }
1232
- }, [pluginConfig.unblock, latest?.meta?.githubCommitRef, currentUser, target.name, toast, client]);
1253
+ }, [pluginConfig.unblock, latest?.meta?.githubCommitRef, latest?.uid, currentUser, target.name, toast, client]);
1233
1254
  const branch = latest?.meta?.githubCommitRef;
1234
1255
  const commitMsg = latest?.meta?.githubCommitMessage?.split("\n")[0];
1235
1256
  const sha = shortSha(latest?.meta?.githubCommitSha);
@@ -1443,9 +1464,12 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1443
1464
  ", which Vercel will build."
1444
1465
  ] })
1445
1466
  ] }) : /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, children: pluginConfig.unblock ? "This deployment carries no branch information, so a version bump cannot be targeted. Ask a developer to deploy manually." : "Ask a developer to push a version bump \u2014 a commit by an authorised author is needed before this site can deploy." }),
1446
- bumpResult && /* @__PURE__ */ jsxs6(Text, { size: 0, weight: bumpResult.ok ? "semibold" : void 0, children: [
1447
- bumpResult.ok ? "\u2713 " : "",
1448
- bumpResult.message
1467
+ bumpResult && /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
1468
+ awaitingBump && /* @__PURE__ */ jsx10(Spinner, { muted: true }),
1469
+ /* @__PURE__ */ jsxs6(Text, { size: 0, weight: bumpResult.ok ? "semibold" : void 0, children: [
1470
+ bumpResult.ok && !awaitingBump ? "\u2713 " : "",
1471
+ bumpResult.message
1472
+ ] })
1449
1473
  ] })
1450
1474
  ] }) })
1451
1475
  ] }),
@@ -1550,6 +1574,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1550
1574
  {
1551
1575
  target,
1552
1576
  token,
1577
+ branch: knownBranch,
1553
1578
  onClose: () => setShowHistory(false)
1554
1579
  }
1555
1580
  )
@@ -1835,7 +1860,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1835
1860
  }
1836
1861
 
1837
1862
  // src/version.ts
1838
- var VERSION = "1.5.0";
1863
+ var VERSION = "1.6.0";
1839
1864
 
1840
1865
  // src/components/DeployTool.tsx
1841
1866
  import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/deploy-vercel-from-sanity",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Sanity Studio plugin — trigger and monitor Vercel deployments with full status, history, and build logs. Supports Studio v3.30 through v6.",
5
5
  "license": "MIT",
6
6
  "author": "Liiift Studio",
package/proxy/core.ts CHANGED
@@ -184,7 +184,7 @@ async function assertDeploymentInProject(
184
184
 
185
185
  /** List recent deployments for a proxy key. */
186
186
  export async function handleDeployments(
187
- params: { key: string | null; limit?: number; statusKey: string | null },
187
+ params: { key: string | null; limit?: number; statusKey: string | null; branch?: string | null },
188
188
  env: ProxyEnv,
189
189
  ): Promise<ProxyResult> {
190
190
  const denied = checkStatusKey(params.statusKey, env)
@@ -192,9 +192,20 @@ export async function handleDeployments(
192
192
  const project = params.key ? env.projects[params.key.trim().toLowerCase()] : undefined
193
193
  if (!project) return { status: 404, body: { error: 'Unknown target key' } }
194
194
 
195
+ // Branch names come from the caller, so they are held to git's own character set
196
+ // before being spliced into an authenticated query. Anything else is ignored
197
+ // rather than rejected, which degrades to the previous hook-only behaviour.
198
+ const branch = params.branch && /^[A-Za-z0-9._\-/]{1,255}$/.test(params.branch)
199
+ ? params.branch
200
+ : undefined
201
+
195
202
  const query = new URLSearchParams({
196
203
  projectId: project.projectId,
197
- 'meta-deployHookId': project.hookId,
204
+ // Branch filtering reports what is actually live on that branch; hook filtering
205
+ // only ever shows deployments this tool triggered, so a git push never appears.
206
+ ...(branch
207
+ ? { 'meta-githubCommitRef': branch }
208
+ : { 'meta-deployHookId': project.hookId }),
198
209
  limit: String(params.limit ?? 10),
199
210
  })
200
211
  if (project.teamId) query.set('teamId', project.teamId)
@@ -87,7 +87,7 @@ async function handleGet(request: Request): Promise<Response> {
87
87
  switch (operation(request)) {
88
88
  case 'deployments': {
89
89
  const limit = Number(url.searchParams.get('limit')) || undefined
90
- return json(await handleDeployments({ key, limit, statusKey }, env), request)
90
+ return json(await handleDeployments({ key, limit, statusKey, branch: url.searchParams.get('branch') }, env), request)
91
91
  }
92
92
  case 'events':
93
93
  return json(await handleEvents({ key, deploymentId: url.searchParams.get('deploymentId'), statusKey }, env), request)