@liiift-studio/deploy-vercel-from-sanity 1.2.0 → 1.3.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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // src/index.ts
1
+ // src/index.tsx
2
2
  import { definePlugin } from "sanity";
3
3
 
4
4
  // src/icons.tsx
@@ -9,10 +9,18 @@ import * as sanityUi from "@sanity/ui";
9
9
  import * as sanityIcons from "@sanity/icons";
10
10
  var UI = sanityUi;
11
11
  var ICONS = sanityIcons;
12
- function resolveExport(ns, name) {
12
+ function resolveComponent(ns, name) {
13
13
  const value = ns[name];
14
14
  return typeof value === "function" || typeof value === "object" && value !== null ? value : void 0;
15
15
  }
16
+ function resolveFunction(ns, name) {
17
+ const value = ns[name];
18
+ return typeof value === "function" ? value : void 0;
19
+ }
20
+ function resolveRecord(ns, name) {
21
+ const value = ns[name];
22
+ return typeof value === "object" && value !== null ? value : void 0;
23
+ }
16
24
  var STACK_USES_GAP = typeof UI.Stack === "function";
17
25
 
18
26
  // src/icons.tsx
@@ -21,11 +29,11 @@ var GLYPH = { width: "1em", height: "1em", viewBox: "0 0 25 25", fill: "none" };
21
29
  var MissingIcon = forwardRef(function MissingIcon2(props, ref) {
22
30
  return /* @__PURE__ */ jsx("svg", { ...GLYPH, xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, ref });
23
31
  });
24
- var SYMBOL_MAP = resolveExport(ICONS, "icons");
32
+ var SYMBOL_MAP = resolveRecord(ICONS, "icons");
25
33
  function resolveIcon(name, symbol) {
26
- const named = resolveExport(ICONS, name);
34
+ const named = resolveComponent(ICONS, name);
27
35
  if (named) return named;
28
- const Icon = resolveExport(ICONS, "Icon");
36
+ const Icon = resolveComponent(ICONS, "Icon");
29
37
  if (!Icon) return MissingIcon;
30
38
  if (SYMBOL_MAP && !(symbol in SYMBOL_MAP)) return MissingIcon;
31
39
  const Resolved = forwardRef(function SanityIcon(props, ref) {
@@ -52,7 +60,28 @@ var WarningOutlineIcon = resolveIcon("WarningOutlineIcon", "warning-outline");
52
60
 
53
61
  // src/components/DeployTool.tsx
54
62
  import { useState as useState8, useEffect as useEffect5, useCallback as useCallback7 } from "react";
55
- import { useClient as useClient3 } from "sanity";
63
+ import { useClient as useClient4 } from "sanity";
64
+
65
+ // src/config.tsx
66
+ import { createContext, useContext } from "react";
67
+ import { jsx as jsx2 } from "react/jsx-runtime";
68
+ var DEFAULTS = { mode: "direct" };
69
+ var ConfigContext = createContext(DEFAULTS);
70
+ function resolveConfig(options) {
71
+ const config = options ?? {};
72
+ return {
73
+ mode: config.mode ?? "direct",
74
+ // Trailing slashes would double up when request paths are appended.
75
+ proxyUrl: config.proxyUrl?.replace(/\/+$/, ""),
76
+ statusKey: config.statusKey
77
+ };
78
+ }
79
+ function ConfigProvider({ value, children }) {
80
+ return /* @__PURE__ */ jsx2(ConfigContext.Provider, { value, children });
81
+ }
82
+ function usePluginConfig() {
83
+ return useContext(ConfigContext);
84
+ }
56
85
 
57
86
  // src/components/DeployItem.tsx
58
87
  import { useState as useState5, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef2 } from "react";
@@ -60,24 +89,46 @@ import { flushSync } from "react-dom";
60
89
 
61
90
  // src/compat/primitives.tsx
62
91
  import { createElement, forwardRef as forwardRef2 } from "react";
63
- import { jsx as jsx2 } from "react/jsx-runtime";
92
+ import { jsx as jsx3 } from "react/jsx-runtime";
64
93
  function domFallback(tag) {
65
94
  const Fallback = forwardRef2(function SanityUiFallback(props, ref) {
66
- const { children, style, id, className, onClick, href, title, ...rest } = props;
95
+ const { children, style, id, className, onClick, href, title, as, ...rest } = props;
96
+ const element = typeof as === "string" ? as : tag;
67
97
  const passthrough = { style, id, className, onClick, href, title, ref };
68
- for (const key of ["role", "type", "value", "checked", "placeholder", "disabled", "onChange", "onKeyDown"]) {
98
+ for (const key of [
99
+ "role",
100
+ "type",
101
+ "value",
102
+ "checked",
103
+ "placeholder",
104
+ "disabled",
105
+ "tabIndex",
106
+ "onChange",
107
+ "onKeyDown",
108
+ "onFocus",
109
+ "onBlur",
110
+ "onMouseEnter",
111
+ "onMouseLeave",
112
+ "htmlFor",
113
+ "target",
114
+ "rel",
115
+ "name",
116
+ "autoComplete",
117
+ "required",
118
+ "readOnly"
119
+ ]) {
69
120
  if (key in rest) passthrough[key] = rest[key];
70
121
  }
71
122
  for (const key of Object.keys(rest)) {
72
123
  if (key.startsWith("aria-") || key.startsWith("data-")) passthrough[key] = rest[key];
73
124
  }
74
- return createElement(tag, passthrough, children);
125
+ return createElement(element, passthrough, children);
75
126
  });
76
127
  Fallback.displayName = `SanityUiFallback(${tag})`;
77
128
  return Fallback;
78
129
  }
79
130
  function primitive(name, tag) {
80
- return resolveExport(UI, name) ?? domFallback(tag);
131
+ return resolveComponent(UI, name) ?? domFallback(tag);
81
132
  }
82
133
  var Box = primitive("Box", "div");
83
134
  var Card = primitive("Card", "div");
@@ -96,12 +147,12 @@ var Dialog = primitive("Dialog", "div");
96
147
  var SanityStack = primitive("Stack", "div");
97
148
  function Stack({ space, children, ...rest }) {
98
149
  const spacing = space === void 0 ? {} : STACK_USES_GAP ? { gap: space } : { space };
99
- return /* @__PURE__ */ jsx2(SanityStack, { ...rest, ...spacing, children });
150
+ return /* @__PURE__ */ jsx3(SanityStack, { ...rest, ...spacing, children });
100
151
  }
101
152
 
102
153
  // src/compat/toast.tsx
103
154
  import { useCallback, useEffect, useState } from "react";
104
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
155
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
105
156
  var LOCAL_TOAST_MS = 6e3;
106
157
  var MAX_VISIBLE_TOASTS = 4;
107
158
  var TOAST_Z_INDEX = 1e6;
@@ -136,11 +187,19 @@ function scheduleDismiss(id, status) {
136
187
  }
137
188
  function pushLocalToast(params) {
138
189
  const toast = { ...params, id: nextToastId++ };
139
- localToasts = [...localToasts, toast].slice(-MAX_VISIBLE_TOASTS);
190
+ const next = [...localToasts, toast];
191
+ for (const dropped of next.slice(0, Math.max(0, next.length - MAX_VISIBLE_TOASTS))) {
192
+ const timer = dismissTimers.get(dropped.id);
193
+ if (timer) {
194
+ clearTimeout(timer);
195
+ dismissTimers.delete(dropped.id);
196
+ }
197
+ }
198
+ localToasts = next.slice(-MAX_VISIBLE_TOASTS);
140
199
  emitToasts();
141
200
  scheduleDismiss(toast.id, toast.status);
142
201
  }
143
- var installedUseToast = resolveExport(UI, "useToast");
202
+ var installedUseToast = resolveFunction(UI, "useToast");
144
203
  var localToaster = { push: pushLocalToast };
145
204
  function useToast() {
146
205
  const real = installedUseToast;
@@ -157,6 +216,10 @@ function ToastViewport() {
157
216
  toastListeners.delete(setToasts);
158
217
  };
159
218
  }, []);
219
+ const resume = useCallback((toast, el) => {
220
+ if (el && el.contains(document.activeElement)) return;
221
+ scheduleDismiss(toast.id, toast.status);
222
+ }, []);
160
223
  const hold = useCallback((id) => {
161
224
  const timer = dismissTimers.get(id);
162
225
  if (timer) {
@@ -165,12 +228,11 @@ function ToastViewport() {
165
228
  }
166
229
  }, []);
167
230
  if (installedUseToast) return null;
168
- return /* @__PURE__ */ jsx3(
231
+ return /* @__PURE__ */ jsx4(
169
232
  Box,
170
233
  {
171
- role: "status",
172
- "aria-live": "polite",
173
- "aria-atomic": false,
234
+ role: "log",
235
+ "aria-label": "Deployment notifications",
174
236
  style: {
175
237
  position: "fixed",
176
238
  bottom: 16,
@@ -179,23 +241,24 @@ function ToastViewport() {
179
241
  width: "min(360px, calc(100vw - 32px))",
180
242
  pointerEvents: "none"
181
243
  },
182
- children: /* @__PURE__ */ jsx3(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ jsx3(
244
+ children: /* @__PURE__ */ jsx4(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ jsx4(
183
245
  Card,
184
246
  {
185
247
  padding: 3,
186
248
  radius: 2,
187
249
  shadow: 3,
188
- tone: LOCAL_TOAST_TONE[toast.status ?? "info"] ?? "primary",
250
+ tone: LOCAL_TOAST_TONE[toast.status ?? "info"],
189
251
  style: { pointerEvents: "auto" },
190
252
  onMouseEnter: () => hold(toast.id),
191
253
  onFocusCapture: () => hold(toast.id),
192
- onMouseLeave: () => scheduleDismiss(toast.id, toast.status),
254
+ onMouseLeave: (e) => resume(toast, e.currentTarget),
255
+ onBlurCapture: (e) => resume(toast, e.currentTarget),
193
256
  children: /* @__PURE__ */ jsxs(Flex, { align: "flex-start", gap: 3, children: [
194
257
  /* @__PURE__ */ jsxs(Stack, { space: 2, flex: 1, children: [
195
- toast.title && /* @__PURE__ */ jsx3(Text, { size: 1, weight: "semibold", children: toast.title }),
196
- toast.description && /* @__PURE__ */ jsx3(Text, { size: 1, muted: true, children: toast.description })
258
+ toast.title && /* @__PURE__ */ jsx4(Text, { size: 1, weight: "semibold", children: toast.title }),
259
+ toast.description && /* @__PURE__ */ jsx4(Text, { size: 1, muted: true, children: toast.description })
197
260
  ] }),
198
- /* @__PURE__ */ jsx3(
261
+ /* @__PURE__ */ jsx4(
199
262
  Button,
200
263
  {
201
264
  mode: "bleed",
@@ -215,50 +278,49 @@ function ToastViewport() {
215
278
  }
216
279
 
217
280
  // src/compat/tooltip.tsx
218
- import { useId, useState as useState2 } from "react";
219
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
220
- var InstalledTooltip = resolveExport(UI, "Tooltip");
281
+ import { cloneElement, isValidElement, useId, useState as useState2 } from "react";
282
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
283
+ var InstalledTooltip = resolveComponent(UI, "Tooltip");
221
284
  function Tooltip({ text, children }) {
222
285
  const id = useId();
223
286
  const [visible, setVisible] = useState2(false);
224
287
  if (InstalledTooltip) {
225
- return /* @__PURE__ */ jsx4(InstalledTooltip, { content: /* @__PURE__ */ jsx4(Box, { padding: 2, children: /* @__PURE__ */ jsx4(Text, { size: 1, children: text }) }), portal: true, children });
288
+ return /* @__PURE__ */ jsx5(InstalledTooltip, { content: /* @__PURE__ */ jsx5(Box, { padding: 2, children: /* @__PURE__ */ jsx5(Text, { size: 1, children: text }) }), portal: true, children });
226
289
  }
290
+ const described = visible && isValidElement(children) ? cloneElement(children, { "aria-describedby": id }) : children;
227
291
  return /* @__PURE__ */ jsxs2(
228
292
  "span",
229
293
  {
230
294
  style: { position: "relative", display: "inline-flex" },
231
- "aria-describedby": id,
232
295
  onMouseEnter: () => setVisible(true),
233
296
  onMouseLeave: () => setVisible(false),
234
297
  onFocusCapture: () => setVisible(true),
235
298
  onBlurCapture: () => setVisible(false),
236
299
  onKeyDown: (e) => {
237
- if (e.key === "Escape") setVisible(false);
300
+ if (e.key === "Escape" && visible) {
301
+ e.stopPropagation();
302
+ setVisible(false);
303
+ }
238
304
  },
239
305
  children: [
240
- children,
241
- /* @__PURE__ */ jsx4(
242
- Card,
306
+ described,
307
+ visible && /* @__PURE__ */ jsx5(
308
+ "span",
243
309
  {
244
310
  id,
245
311
  role: "tooltip",
246
- radius: 2,
247
- shadow: 2,
248
- padding: 2,
249
312
  style: {
250
313
  position: "absolute",
251
314
  bottom: "100%",
252
315
  left: "50%",
253
316
  transform: "translateX(-50%)",
254
- marginBottom: 4,
317
+ // No gap between trigger and tooltip, so the pointer can travel onto it
318
+ // without leaving the hover target (WCAG 1.4.13 Hoverable).
319
+ paddingBottom: 4,
255
320
  whiteSpace: "nowrap",
256
- pointerEvents: "none",
257
- zIndex: 1e3,
258
- // Kept in the accessibility tree when hidden so `aria-describedby` still resolves.
259
- visibility: visible ? "visible" : "hidden"
321
+ zIndex: 1e3
260
322
  },
261
- children: /* @__PURE__ */ jsx4(Text, { size: 1, children: text })
323
+ children: /* @__PURE__ */ jsx5(Card, { radius: 2, shadow: 2, padding: 2, children: /* @__PURE__ */ jsx5(Text, { size: 1, children: text }) })
262
324
  }
263
325
  )
264
326
  ]
@@ -268,10 +330,10 @@ function Tooltip({ text, children }) {
268
330
 
269
331
  // src/compat/menu.tsx
270
332
  import { useCallback as useCallback2, useEffect as useEffect2, useId as useId2, useRef, useState as useState3 } from "react";
271
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
272
- var InstalledMenuButton = resolveExport(UI, "MenuButton");
273
- var InstalledMenu = resolveExport(UI, "Menu");
274
- var InstalledMenuItem = resolveExport(UI, "MenuItem");
333
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
334
+ var InstalledMenuButton = resolveComponent(UI, "MenuButton");
335
+ var InstalledMenu = resolveComponent(UI, "Menu");
336
+ var InstalledMenuItem = resolveComponent(UI, "MenuItem");
275
337
  var INSTALLED_MENU = InstalledMenuButton && InstalledMenu && InstalledMenuItem ? { MenuButton: InstalledMenuButton, Menu: InstalledMenu, MenuItem: InstalledMenuItem } : null;
276
338
  var itemKey = (item, index) => item.key ?? `${index}-${item.text}`;
277
339
  function ActionMenu({ id, label, items, buttonIcon }) {
@@ -299,7 +361,10 @@ function ActionMenu({ id, label, items, buttonIcon }) {
299
361
  useEffect2(() => {
300
362
  if (INSTALLED_MENU || !open) return;
301
363
  const onPointerDown = (e) => {
302
- if (!wrapRef.current?.contains(e.target)) setOpen(false);
364
+ if (wrapRef.current?.contains(e.target)) return;
365
+ const hadFocus = wrapRef.current?.contains(document.activeElement);
366
+ setOpen(false);
367
+ if (hadFocus) triggerRef.current?.focus();
303
368
  };
304
369
  document.addEventListener("mousedown", onPointerDown);
305
370
  return () => document.removeEventListener("mousedown", onPointerDown);
@@ -310,13 +375,13 @@ function ActionMenu({ id, label, items, buttonIcon }) {
310
375
  }, [close]);
311
376
  if (INSTALLED_MENU) {
312
377
  const { MenuButton, Menu, MenuItem } = INSTALLED_MENU;
313
- return /* @__PURE__ */ jsx5(
378
+ return /* @__PURE__ */ jsx6(
314
379
  MenuButton,
315
380
  {
316
381
  id,
317
- button: /* @__PURE__ */ jsx5(Button, { mode: "ghost", icon: buttonIcon, padding: 2, "aria-label": label }),
382
+ button: /* @__PURE__ */ jsx6(Button, { mode: "ghost", icon: buttonIcon, padding: 2, "aria-label": label }),
318
383
  popover: { placement: "bottom-end" },
319
- menu: /* @__PURE__ */ jsx5(Menu, { children: items.map((item, i) => /* @__PURE__ */ jsx5(
384
+ menu: /* @__PURE__ */ jsx6(Menu, { children: items.map((item, i) => /* @__PURE__ */ jsx6(
320
385
  MenuItem,
321
386
  {
322
387
  text: item.text,
@@ -337,7 +402,20 @@ function ActionMenu({ id, label, items, buttonIcon }) {
337
402
  return;
338
403
  }
339
404
  if (e.key === "Tab") {
340
- close(false);
405
+ triggerRef.current?.focus();
406
+ setOpen(false);
407
+ return;
408
+ }
409
+ if (e.key === "Enter" || e.key === " ") {
410
+ e.preventDefault();
411
+ const item = shownItems[activeIndex];
412
+ if (item) {
413
+ if (item.href) {
414
+ itemRefs.current[activeIndex]?.click();
415
+ } else {
416
+ runAction(item);
417
+ }
418
+ }
341
419
  return;
342
420
  }
343
421
  if (e.key === "ArrowDown") {
@@ -371,7 +449,7 @@ function ActionMenu({ id, label, items, buttonIcon }) {
371
449
  }
372
450
  };
373
451
  return /* @__PURE__ */ jsxs3("div", { ref: wrapRef, style: { position: "relative" }, children: [
374
- /* @__PURE__ */ jsx5(
452
+ /* @__PURE__ */ jsx6(
375
453
  Button,
376
454
  {
377
455
  ref: triggerRef,
@@ -387,7 +465,7 @@ function ActionMenu({ id, label, items, buttonIcon }) {
387
465
  onKeyDown: onTriggerKeyDown
388
466
  }
389
467
  ),
390
- open && /* @__PURE__ */ jsx5(
468
+ open && /* @__PURE__ */ jsx6(
391
469
  Card,
392
470
  {
393
471
  id: menuId,
@@ -398,13 +476,13 @@ function ActionMenu({ id, label, items, buttonIcon }) {
398
476
  "aria-labelledby": id,
399
477
  onKeyDown: onMenuKeyDown,
400
478
  style: { position: "absolute", top: "100%", right: 0, marginTop: 4, zIndex: 1e3, minWidth: 200 },
401
- children: /* @__PURE__ */ jsx5(Stack, { space: 1, role: "none", children: shownItems.map((item, i) => {
479
+ children: /* @__PURE__ */ jsx6(Stack, { space: 1, role: "none", children: shownItems.map((item, i) => {
402
480
  const Icon = item.icon;
403
481
  const row = /* @__PURE__ */ jsxs3(Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2, children: [
404
- /* @__PURE__ */ jsx5(Icon, { width: "1em", height: "1em", "aria-hidden": "true" }),
405
- /* @__PURE__ */ jsx5(Text, { size: 1, children: item.text })
482
+ /* @__PURE__ */ jsx6(Icon, { width: "1em", height: "1em", "aria-hidden": "true" }),
483
+ /* @__PURE__ */ jsx6(Text, { size: 1, children: item.text })
406
484
  ] });
407
- const content = item.tone === "critical" ? /* @__PURE__ */ jsx5(Card, { tone: "critical", radius: 1, children: row }) : row;
485
+ const content = item.tone === "critical" ? /* @__PURE__ */ jsx6(Card, { tone: "critical", radius: 1, children: row }) : row;
408
486
  const shared = {
409
487
  role: "menuitem",
410
488
  // Roving tabindex — the menu is one tab stop, arrows move within it.
@@ -412,7 +490,6 @@ function ActionMenu({ id, label, items, buttonIcon }) {
412
490
  ref: (el) => {
413
491
  itemRefs.current[i] = el;
414
492
  },
415
- onMouseEnter: () => setActiveIndex(i),
416
493
  style: {
417
494
  display: "block",
418
495
  width: "100%",
@@ -430,22 +507,24 @@ function ActionMenu({ id, label, items, buttonIcon }) {
430
507
  outlineOffset: -2
431
508
  }
432
509
  };
433
- return item.href ? /* @__PURE__ */ jsx5(
510
+ return item.href ? /* @__PURE__ */ jsx6(
434
511
  "a",
435
512
  {
436
513
  ...shared,
437
514
  href: item.href,
438
515
  target: "_blank",
439
516
  rel: "noreferrer",
440
- onClick: () => close(false),
517
+ onClick: () => {
518
+ setOpen(false);
519
+ triggerRef.current?.focus();
520
+ },
441
521
  children: content
442
522
  },
443
523
  itemKey(item, i)
444
- ) : /* @__PURE__ */ jsx5(
445
- "button",
524
+ ) : /* @__PURE__ */ jsx6(
525
+ "div",
446
526
  {
447
527
  ...shared,
448
- type: "button",
449
528
  onClick: () => runAction(item),
450
529
  children: content
451
530
  },
@@ -458,8 +537,8 @@ function ActionMenu({ id, label, items, buttonIcon }) {
458
537
  }
459
538
 
460
539
  // src/compat/code.tsx
461
- import { jsx as jsx6 } from "react/jsx-runtime";
462
- var InstalledCode = resolveExport(UI, "Code");
540
+ import { jsx as jsx7 } from "react/jsx-runtime";
541
+ var InstalledCode = resolveComponent(UI, "Code");
463
542
  var FALLBACK_CODE_STYLE = {
464
543
  display: "block",
465
544
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
@@ -468,8 +547,8 @@ var FALLBACK_CODE_STYLE = {
468
547
  margin: 0
469
548
  };
470
549
  function Code({ style, children }) {
471
- if (InstalledCode) return /* @__PURE__ */ jsx6(InstalledCode, { size: 1, style, children });
472
- return /* @__PURE__ */ jsx6("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
550
+ if (InstalledCode) return /* @__PURE__ */ jsx7(InstalledCode, { size: 1, style, children });
551
+ return /* @__PURE__ */ jsx7("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
473
552
  }
474
553
 
475
554
  // src/lib/api.ts
@@ -485,7 +564,7 @@ async function vercelFetch(path, token, init) {
485
564
  }
486
565
  });
487
566
  if (!res.ok) {
488
- const hint = res.status === 401 ? " \u2014 token is invalid or expired. Reconnect your API token." : res.status === 403 ? " \u2014 token lacks the required permissions. Ensure it has Full Account scope." : res.status === 404 ? " \u2014 resource not found. Check the deploy hook URL and team ID." : res.status === 429 ? " \u2014 rate limit reached. Wait a moment and try again." : res.status >= 500 ? " \u2014 Vercel is experiencing issues. Try again shortly." : "";
567
+ const hint = res.status === 401 ? " \u2014 token is invalid or expired. Reconnect your API token." : res.status === 403 ? " \u2014 token lacks the required permissions. Scope it to the team that owns the project, and set the target's Team ID." : res.status === 404 ? " \u2014 resource not found. Check the deploy hook URL and team ID." : res.status === 429 ? " \u2014 rate limit reached. Wait a moment and try again." : res.status >= 500 ? " \u2014 Vercel is experiencing issues. Try again shortly." : "";
489
568
  throw new Error(`Vercel API ${res.status}${hint}`);
490
569
  }
491
570
  return res.json();
@@ -524,15 +603,75 @@ async function getDeploymentEvents(opts) {
524
603
  const params = new URLSearchParams({ limit: "100", direction: "backward" });
525
604
  if (opts.teamId) params.set("teamId", opts.teamId);
526
605
  const raw = await vercelFetch(
527
- `/v2/deployments/${opts.deploymentId}/events?${params}`,
606
+ `/v2/deployments/${encodeURIComponent(opts.deploymentId)}/events?${params}`,
528
607
  opts.token
529
608
  );
530
609
  const events = Array.isArray(raw) ? raw : raw.events ?? [];
531
610
  return events.filter((e) => e.text?.trim());
532
611
  }
533
612
 
613
+ // src/lib/transport.ts
614
+ async function proxyFetch(url, statusKey, init) {
615
+ const res = await fetch(url, {
616
+ ...init,
617
+ headers: {
618
+ "Content-Type": "application/json",
619
+ ...statusKey ? { "x-deploy-status-key": statusKey } : {},
620
+ ...init?.headers
621
+ }
622
+ });
623
+ if (!res.ok) {
624
+ const hint = res.status === 401 ? " \u2014 the proxy rejected the status key. Check `statusKey` matches the proxy." : res.status === 404 ? " \u2014 the proxy has no target with that key. Check `proxyKey` matches the proxy configuration." : res.status >= 500 ? " \u2014 the deploy proxy is failing. Check its logs." : "";
625
+ throw new Error(`Deploy proxy ${res.status}${hint}`);
626
+ }
627
+ return res.json();
628
+ }
629
+ async function fetchDeployments(transport, target, limit) {
630
+ if (transport.mode === "direct") {
631
+ if (!target.projectId || !target.hookId) return [];
632
+ return listDeployments({
633
+ projectId: target.projectId,
634
+ hookId: target.hookId,
635
+ token: transport.token,
636
+ teamId: target.teamId,
637
+ limit
638
+ });
639
+ }
640
+ const params = new URLSearchParams({ key: target.proxyKey ?? "" });
641
+ if (limit) params.set("limit", String(limit));
642
+ const data = await proxyFetch(
643
+ `${transport.proxyUrl}/deployments?${params}`,
644
+ transport.statusKey
645
+ );
646
+ return data.deployments ?? [];
647
+ }
648
+ async function cancelDeploy(transport, target, deploymentId) {
649
+ if (transport.mode === "direct") {
650
+ return cancelDeployment({ deploymentId, token: transport.token, teamId: target.teamId });
651
+ }
652
+ await proxyFetch(`${transport.proxyUrl}/cancel`, transport.statusKey, {
653
+ method: "POST",
654
+ body: JSON.stringify({ key: target.proxyKey, deploymentId })
655
+ });
656
+ }
657
+ async function fetchDeploymentEvents(transport, target, deploymentId) {
658
+ if (transport.mode === "direct") {
659
+ return getDeploymentEvents({ deploymentId, token: transport.token, teamId: target.teamId });
660
+ }
661
+ const params = new URLSearchParams({ key: target.proxyKey ?? "", deploymentId });
662
+ const data = await proxyFetch(
663
+ `${transport.proxyUrl}/events?${params}`,
664
+ transport.statusKey
665
+ );
666
+ return data.events ?? [];
667
+ }
668
+
669
+ // src/components/DeployItem.tsx
670
+ import { useClient } from "sanity";
671
+
534
672
  // src/lib/helpers.ts
535
673
  function parseHookUrl(url) {
674
+ if (!url) return { projectId: "", hookId: "" };
536
675
  try {
537
676
  const path = new URL(url).pathname;
538
677
  const parts = path.split("/").filter(Boolean);
@@ -622,29 +761,33 @@ function projectHref(inspectorUrl) {
622
761
  }
623
762
 
624
763
  // src/components/StatusBadge.tsx
625
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
764
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
626
765
  function StatusBadge({ state, showSpinner }) {
627
766
  const { label, tone } = stateLabel(state);
628
767
  const spinning = showSpinner && (state === "QUEUED" || state === "INITIALIZING" || state === "BUILDING");
629
768
  return /* @__PURE__ */ jsxs4(Flex, { align: "center", gap: 2, children: [
630
- spinning && /* @__PURE__ */ jsx7(Spinner, { muted: true }),
631
- /* @__PURE__ */ jsx7(Badge, { tone, padding: 2, children: label })
769
+ spinning && /* @__PURE__ */ jsx8(Spinner, { muted: true }),
770
+ /* @__PURE__ */ jsx8(Badge, { tone, padding: 2, children: label })
632
771
  ] });
633
772
  }
634
773
 
635
774
  // src/components/DeployHistory.tsx
636
- import { useEffect as useEffect3, useState as useState4, useCallback as useCallback3 } from "react";
637
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
775
+ import { useId as useId3, useEffect as useEffect3, useState as useState4, useCallback as useCallback3 } from "react";
776
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
638
777
  function DeployHistory({ target, token, onClose }) {
778
+ const dialogId = useId3();
779
+ const pluginConfig = usePluginConfig();
639
780
  const [deployments, setDeployments] = useState4([]);
640
781
  const [loading, setLoading] = useState4(true);
641
782
  const [error, setError] = useState4(null);
642
783
  const { projectId, hookId } = parseHookUrl(target.url);
784
+ const transport = pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token };
785
+ const targetRef = { projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId };
643
786
  const load = useCallback3(async () => {
644
787
  setLoading(true);
645
788
  setError(null);
646
789
  try {
647
- const data = await listDeployments({ projectId, hookId, token, teamId: target.teamId, limit: 10 });
790
+ const data = await fetchDeployments(transport, targetRef, 20);
648
791
  setDeployments(data);
649
792
  } catch (err) {
650
793
  setError(err instanceof Error ? err.message : "Failed to load history");
@@ -655,52 +798,52 @@ function DeployHistory({ target, token, onClose }) {
655
798
  useEffect3(() => {
656
799
  load();
657
800
  }, [load]);
658
- return /* @__PURE__ */ jsx8(
801
+ return /* @__PURE__ */ jsx9(
659
802
  Dialog,
660
803
  {
661
804
  header: `${target.name} \u2014 Deployment History`,
662
- id: "deploy-history",
805
+ id: dialogId,
663
806
  onClose,
664
807
  width: 2,
665
- footer: /* @__PURE__ */ jsx8(Box, { padding: 3, children: /* @__PURE__ */ jsx8(Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
808
+ footer: /* @__PURE__ */ jsx9(Box, { padding: 3, children: /* @__PURE__ */ jsx9(Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
666
809
  children: /* @__PURE__ */ jsxs5(Box, { padding: 4, children: [
667
- loading && /* @__PURE__ */ jsx8(Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ jsx8(Spinner, { muted: true }) }),
668
- error && /* @__PURE__ */ jsx8(Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ jsx8(Text, { size: 1, children: error }) }),
669
- !loading && !error && deployments.length === 0 && /* @__PURE__ */ jsx8(Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ jsx8(Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
810
+ loading && /* @__PURE__ */ jsx9(Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ jsx9(Spinner, { muted: true }) }),
811
+ error && /* @__PURE__ */ jsx9(Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ jsx9(Text, { size: 1, children: error }) }),
812
+ !loading && !error && deployments.length === 0 && /* @__PURE__ */ jsx9(Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
670
813
  !loading && deployments.length > 0 && /* @__PURE__ */ jsxs5(Stack, { space: 2, children: [
671
- /* @__PURE__ */ jsx8(Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ jsxs5(Flex, { gap: 3, children: [
672
- /* @__PURE__ */ jsx8(Box, { flex: 2, children: /* @__PURE__ */ jsx8(Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
673
- /* @__PURE__ */ jsx8(Box, { flex: 1, children: /* @__PURE__ */ jsx8(Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
674
- /* @__PURE__ */ jsx8(Box, { flex: 2, children: /* @__PURE__ */ jsx8(Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
675
- /* @__PURE__ */ jsx8(Box, { flex: 1, children: /* @__PURE__ */ jsx8(Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
676
- /* @__PURE__ */ jsx8(Box, { style: { width: 64 }, children: /* @__PURE__ */ jsx8(Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
814
+ /* @__PURE__ */ jsx9(Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ jsxs5(Flex, { gap: 3, children: [
815
+ /* @__PURE__ */ jsx9(Box, { flex: 2, children: /* @__PURE__ */ jsx9(Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
816
+ /* @__PURE__ */ jsx9(Box, { flex: 1, children: /* @__PURE__ */ jsx9(Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
817
+ /* @__PURE__ */ jsx9(Box, { flex: 2, children: /* @__PURE__ */ jsx9(Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
818
+ /* @__PURE__ */ jsx9(Box, { flex: 1, children: /* @__PURE__ */ jsx9(Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
819
+ /* @__PURE__ */ jsx9(Box, { style: { width: 64 }, children: /* @__PURE__ */ jsx9(Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
677
820
  ] }) }),
678
821
  deployments.map((d) => {
679
822
  const { label, tone } = stateLabel(d.state);
680
823
  const branch = d.meta?.githubCommitRef ?? "\u2014";
681
824
  const sha = shortSha(d.meta?.githubCommitSha);
682
825
  const message = d.meta?.githubCommitMessage?.split("\n")[0] ?? "";
683
- return /* @__PURE__ */ jsx8(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs5(Flex, { gap: 3, align: "center", children: [
684
- /* @__PURE__ */ jsx8(Box, { flex: 2, style: { overflow: "hidden" }, children: deploymentHref(d.url) ? /* @__PURE__ */ jsx8(
826
+ return /* @__PURE__ */ jsx9(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs5(Flex, { gap: 3, align: "center", children: [
827
+ /* @__PURE__ */ jsx9(Box, { flex: 2, style: { overflow: "hidden" }, children: deploymentHref(d.url) ? /* @__PURE__ */ jsx9(
685
828
  "a",
686
829
  {
687
830
  href: deploymentHref(d.url),
688
831
  target: "_blank",
689
832
  rel: "noreferrer",
690
833
  style: { color: "inherit" },
691
- children: /* @__PURE__ */ jsx8(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
834
+ children: /* @__PURE__ */ jsx9(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
692
835
  }
693
- ) : /* @__PURE__ */ jsx8(Text, { size: 1, muted: true, children: "\u2014" }) }),
694
- /* @__PURE__ */ jsx8(Box, { flex: 1, children: /* @__PURE__ */ jsx8(Badge, { tone, children: label }) }),
695
- /* @__PURE__ */ jsx8(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxs5(Stack, { space: 1, children: [
696
- /* @__PURE__ */ jsx8(Text, { size: 1, children: branch }),
836
+ ) : /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "\u2014" }) }),
837
+ /* @__PURE__ */ jsx9(Box, { flex: 1, children: /* @__PURE__ */ jsx9(Badge, { tone, children: label }) }),
838
+ /* @__PURE__ */ jsx9(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxs5(Stack, { space: 1, children: [
839
+ /* @__PURE__ */ jsx9(Text, { size: 1, children: branch }),
697
840
  sha && /* @__PURE__ */ jsxs5(Text, { size: 0, muted: true, children: [
698
841
  sha,
699
842
  message ? ` \xB7 ${message.slice(0, 40)}${message.length > 40 ? "\u2026" : ""}` : ""
700
843
  ] })
701
844
  ] }) }),
702
- /* @__PURE__ */ jsx8(Box, { flex: 1, children: /* @__PURE__ */ jsx8(Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
703
- /* @__PURE__ */ jsx8(Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ jsx8(
845
+ /* @__PURE__ */ jsx9(Box, { flex: 1, children: /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
846
+ /* @__PURE__ */ jsx9(Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ jsx9(
704
847
  Button,
705
848
  {
706
849
  as: "a",
@@ -713,7 +856,7 @@ function DeployHistory({ target, token, onClose }) {
713
856
  icon: LaunchIcon,
714
857
  style: { fontSize: "12px" }
715
858
  }
716
- ) : /* @__PURE__ */ jsx8(Text, { size: 1, muted: true, children: "\u2014" }) })
859
+ ) : /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "\u2014" }) })
717
860
  ] }) }, d.uid);
718
861
  })
719
862
  ] })
@@ -723,13 +866,17 @@ function DeployHistory({ target, token, onClose }) {
723
866
  }
724
867
 
725
868
  // src/components/DeployItem.tsx
726
- import { Fragment, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
869
+ import { Fragment, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
727
870
  var POLL_INTERVAL_MS = 5e3;
728
871
  var LABEL_WIDTH = 64;
729
872
  var PENDING_TIMEOUT_MS = 6e4;
730
873
  function DeployItem({ target, token, onDelete, onEdit }) {
731
874
  const { projectId, hookId } = parseHookUrl(target.url);
732
875
  const toast = useToast();
876
+ const pluginConfig = usePluginConfig();
877
+ const client = useClient({ apiVersion: "2025-01-01" });
878
+ const transport = pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token };
879
+ const targetRef = { projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId };
733
880
  const [deployments, setDeployments] = useState5([]);
734
881
  const [loadingInitial, setLoadingInitial] = useState5(true);
735
882
  const [pendingSince, setPendingSince] = useState5(null);
@@ -747,17 +894,21 @@ function DeployItem({ target, token, onDelete, onEdit }) {
747
894
  const triggeredFromUidRef = useRef2(void 0);
748
895
  const requestSeqRef = useRef2(0);
749
896
  const mountedRef = useRef2(true);
750
- useEffect4(() => () => {
751
- mountedRef.current = false;
897
+ useEffect4(() => {
898
+ mountedRef.current = true;
899
+ return () => {
900
+ mountedRef.current = false;
901
+ };
752
902
  }, []);
753
903
  const latest = deployments[0];
754
904
  const isPending = pendingSince !== null;
755
905
  const isActive = isPending || isActiveState(latest?.state);
756
- const fetchDeployments = useCallback4(async () => {
757
- if (!projectId || !hookId || !token) return;
906
+ const fetchDeployments2 = useCallback4(async () => {
907
+ const ready = transport.mode === "proxy" ? Boolean(target.proxyKey && pluginConfig.proxyUrl) : Boolean(projectId && hookId && token);
908
+ if (!ready) return;
758
909
  const seq = ++requestSeqRef.current;
759
910
  try {
760
- const data = await listDeployments({ projectId, hookId, token, teamId: target.teamId });
911
+ const data = await fetchDeployments(transport, targetRef);
761
912
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
762
913
  setDeployments(data);
763
914
  setPollError(null);
@@ -766,15 +917,15 @@ function DeployItem({ target, token, onDelete, onEdit }) {
766
917
  setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
767
918
  console.error("Deploy-vercel-from-sanity: fetch error", err);
768
919
  }
769
- }, [projectId, hookId, token, target.teamId]);
920
+ }, [projectId, hookId, token, target.teamId, target.proxyKey, transport.mode, pluginConfig.proxyUrl]);
770
921
  useEffect4(() => {
771
- fetchDeployments().finally(() => setLoadingInitial(false));
772
- }, [fetchDeployments]);
922
+ fetchDeployments2().finally(() => setLoadingInitial(false));
923
+ }, [fetchDeployments2]);
773
924
  useEffect4(() => {
774
925
  if (!isActive) return;
775
- const id = setInterval(fetchDeployments, POLL_INTERVAL_MS);
926
+ const id = setInterval(fetchDeployments2, POLL_INTERVAL_MS);
776
927
  return () => clearInterval(id);
777
- }, [isActive, fetchDeployments]);
928
+ }, [isActive, fetchDeployments2]);
778
929
  useEffect4(() => {
779
930
  if (!isPending) return;
780
931
  if (latest?.uid && latest.uid !== triggeredFromUidRef.current) setPendingSince(null);
@@ -828,26 +979,36 @@ function DeployItem({ target, token, onDelete, onEdit }) {
828
979
  });
829
980
  void (async () => {
830
981
  try {
831
- await triggerDeploy(target.url);
832
- setTimeout(fetchDeployments, 2e3);
982
+ if (pluginConfig.mode === "proxy") {
983
+ if (!target.proxyKey) throw new Error("Target has no proxy key \u2014 set one on the deploy target");
984
+ await client.create({
985
+ _type: "vercelDeploy.request",
986
+ target: { _type: "reference", _ref: target._id },
987
+ proxyKey: target.proxyKey,
988
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString()
989
+ });
990
+ } else {
991
+ await triggerDeploy(target.url ?? "");
992
+ }
993
+ setTimeout(fetchDeployments2, 2e3);
833
994
  } catch (err) {
834
995
  setPendingSince(null);
835
996
  setDeployError(err instanceof Error ? err.message : "Deploy failed");
836
997
  }
837
998
  })();
838
- }, [target.url, fetchDeployments, latest?.uid]);
999
+ }, [target.url, target.proxyKey, target._id, pluginConfig.mode, client, fetchDeployments2, latest?.uid]);
839
1000
  const cancel = useCallback4(async () => {
840
1001
  if (!latest?.uid) return;
841
1002
  setCanceling(true);
842
1003
  try {
843
- await cancelDeployment({ deploymentId: latest.uid, token, teamId: target.teamId });
844
- await fetchDeployments();
1004
+ await cancelDeploy(transport, targetRef, latest.uid);
1005
+ await fetchDeployments2();
845
1006
  } catch (err) {
846
1007
  console.error("deploy-vercel-from-sanity: cancel error", err);
847
1008
  } finally {
848
1009
  setCanceling(false);
849
1010
  }
850
- }, [latest?.uid, token, target.teamId, fetchDeployments]);
1011
+ }, [latest?.uid, token, target.teamId, fetchDeployments2]);
851
1012
  const copyUrl = useCallback4(() => {
852
1013
  if (!latest?.url) return;
853
1014
  const fullUrl = deploymentHref(latest.url) ?? "";
@@ -863,11 +1024,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
863
1024
  setLoadingLogs(true);
864
1025
  setLogError(null);
865
1026
  try {
866
- const events = await getDeploymentEvents({
867
- deploymentId: latest.uid,
868
- token,
869
- teamId: target.teamId
870
- });
1027
+ const events = await fetchDeploymentEvents(transport, targetRef, latest.uid);
871
1028
  const lines = events.filter((e) => e.type === "stderr" || e.type === "stdout").map((e) => e.text ?? "").filter(Boolean).reverse().slice(-30);
872
1029
  setErrorLines(lines.length > 0 ? lines : ["No stderr or stdout was captured for this build. Open the full build log in Vercel for details."]);
873
1030
  } catch (err) {
@@ -890,19 +1047,19 @@ function DeployItem({ target, token, onDelete, onEdit }) {
890
1047
  const vercelProjectUrl = projectHref(latest?.inspectorUrl);
891
1048
  const isError = latest?.state === "ERROR";
892
1049
  return /* @__PURE__ */ jsxs6(Fragment, { children: [
893
- /* @__PURE__ */ jsx9(Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs6(Flex, { align: "stretch", className: "dvfs-card-flex", children: [
1050
+ /* @__PURE__ */ jsx10(Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs6(Flex, { align: "stretch", className: "dvfs-card-flex", children: [
894
1051
  /* @__PURE__ */ jsxs6(Flex, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
895
1052
  /* @__PURE__ */ jsxs6(Stack, { space: 3, padding: 3, style: { flex: 1 }, children: [
896
1053
  /* @__PURE__ */ jsxs6(Flex, { align: "center", justify: "space-between", gap: 2, children: [
897
1054
  /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
898
- /* @__PURE__ */ jsx9(Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
899
- branch && /* @__PURE__ */ jsx9(Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1055
+ /* @__PURE__ */ jsx10(Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
1056
+ branch && /* @__PURE__ */ jsx10(Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
900
1057
  branch,
901
- /* @__PURE__ */ jsx9("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ jsx9("path", { d: "M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) })
1058
+ /* @__PURE__ */ jsx10("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ jsx10("path", { d: "M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) })
902
1059
  ] }) }),
903
1060
  token && !loadingInitial && /* @__PURE__ */ jsxs6(Fragment, { children: [
904
- isPending ? /* @__PURE__ */ jsx9(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ jsx9(StatusBadge, { state: latest?.state }),
905
- isActiveState(latest?.state) && /* @__PURE__ */ jsx9(
1061
+ isPending ? /* @__PURE__ */ jsx10(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ jsx10(StatusBadge, { state: latest?.state }),
1062
+ isActiveState(latest?.state) && /* @__PURE__ */ jsx10(
906
1063
  Button,
907
1064
  {
908
1065
  text: "Cancel",
@@ -916,14 +1073,14 @@ function DeployItem({ target, token, onDelete, onEdit }) {
916
1073
  ),
917
1074
  isPending ? (
918
1075
  /* Optimistic — dimmed spinner only; no elapsed time until a real build exists */
919
- /* @__PURE__ */ jsx9(Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
1076
+ /* @__PURE__ */ jsx10(Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
920
1077
  ) : isActive ? /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
921
- /* @__PURE__ */ jsx9(Spinner, { muted: true, style: { marginLeft: 4 } }),
922
- /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: formatDuration(elapsed) })
923
- ] }) : deployedAt ? /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: deployedAt }) : null
1078
+ /* @__PURE__ */ jsx10(Spinner, { muted: true, style: { marginLeft: 4 } }),
1079
+ /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: formatDuration(elapsed) })
1080
+ ] }) : deployedAt ? /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: deployedAt }) : null
924
1081
  ] })
925
1082
  ] }),
926
- /* @__PURE__ */ jsx9(
1083
+ /* @__PURE__ */ jsx10(
927
1084
  ActionMenu,
928
1085
  {
929
1086
  id: `menu-${target._id}`,
@@ -939,32 +1096,32 @@ function DeployItem({ target, token, onDelete, onEdit }) {
939
1096
  }
940
1097
  )
941
1098
  ] }),
942
- /* @__PURE__ */ jsx9("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
943
- !token ? /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
944
- /* @__PURE__ */ jsx9(Spinner, { muted: true }),
945
- /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "Loading\u2026" })
1099
+ /* @__PURE__ */ jsx10("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
1100
+ !token ? /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: pluginConfig.mode === "proxy" ? target.proxyKey ? "Waiting for status from the deploy proxy." : "Set a Proxy Key on this target to see deployment status." : "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
1101
+ /* @__PURE__ */ jsx10(Spinner, { muted: true }),
1102
+ /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: "Loading\u2026" })
946
1103
  ] }) : /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
947
1104
  /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
948
- latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx9(Fragment, { children: /* @__PURE__ */ jsx9(
1105
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx10(Fragment, { children: /* @__PURE__ */ jsx10(
949
1106
  "a",
950
1107
  {
951
1108
  href: deploymentHref(latest.url),
952
1109
  target: "_blank",
953
1110
  rel: "noreferrer",
954
1111
  style: { color: "inherit" },
955
- children: /* @__PURE__ */ jsx9(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx9(Text, { size: 1, children: latest.url }) })
1112
+ children: /* @__PURE__ */ jsx10(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx10(Text, { size: 1, children: latest.url }) })
956
1113
  }
957
1114
  ) }),
958
- sha && /* @__PURE__ */ jsx9(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ jsx9(
1115
+ sha && /* @__PURE__ */ jsx10(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ jsx10(
959
1116
  "a",
960
1117
  {
961
1118
  href: commitHref,
962
1119
  target: "_blank",
963
1120
  rel: "noreferrer",
964
1121
  style: { color: "inherit", textDecoration: "none" },
965
- children: /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
1122
+ children: /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
966
1123
  }
967
- ) : /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
1124
+ ) : /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
968
1125
  creator && /* @__PURE__ */ jsxs6(Text, { size: 1, muted: true, children: [
969
1126
  "by ",
970
1127
  creator
@@ -974,20 +1131,39 @@ function DeployItem({ target, token, onDelete, onEdit }) {
974
1131
  formatDuration(Math.floor((latest.ready - latest.created) / 1e3)),
975
1132
  " to build"
976
1133
  ] }),
977
- latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx9(Fragment, { children: /* @__PURE__ */ jsx9(Tooltip, { text: copied ? "Copied!" : "Copy URL", children: /* @__PURE__ */ jsx9(
978
- Button,
979
- {
980
- mode: "ghost",
981
- icon: copied ? CheckmarkIcon : CopyIcon,
982
- padding: 1,
983
- tone: copied ? "positive" : "default",
984
- onClick: copyUrl,
985
- style: { cursor: "pointer" }
986
- }
987
- ) }) })
1134
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ jsxs6(Fragment, { children: [
1135
+ /* @__PURE__ */ jsx10(Tooltip, { text: copied ? "Copied" : "Copy deployment URL", children: /* @__PURE__ */ jsx10(
1136
+ Button,
1137
+ {
1138
+ mode: "ghost",
1139
+ icon: copied ? CheckmarkIcon : CopyIcon,
1140
+ padding: 1,
1141
+ tone: copied ? "positive" : "default",
1142
+ onClick: copyUrl,
1143
+ "aria-label": copied ? "Deployment URL copied" : "Copy deployment URL",
1144
+ style: { cursor: "pointer" }
1145
+ }
1146
+ ) }),
1147
+ /* @__PURE__ */ jsx10(
1148
+ "span",
1149
+ {
1150
+ role: "status",
1151
+ "aria-live": "polite",
1152
+ style: {
1153
+ position: "absolute",
1154
+ width: 1,
1155
+ height: 1,
1156
+ overflow: "hidden",
1157
+ clip: "rect(0 0 0 0)",
1158
+ whiteSpace: "nowrap"
1159
+ },
1160
+ children: copied ? "Deployment URL copied to clipboard" : ""
1161
+ }
1162
+ )
1163
+ ] })
988
1164
  ] }),
989
1165
  /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
990
- commitMsg && /* @__PURE__ */ jsx9(
1166
+ commitMsg && /* @__PURE__ */ jsx10(
991
1167
  Text,
992
1168
  {
993
1169
  size: 0,
@@ -997,7 +1173,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
997
1173
  }
998
1174
  ),
999
1175
  isError && /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
1000
- /* @__PURE__ */ jsx9(
1176
+ /* @__PURE__ */ jsx10(
1001
1177
  Button,
1002
1178
  {
1003
1179
  "aria-expanded": showErrorLogs,
@@ -1013,30 +1189,30 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1013
1189
  ),
1014
1190
  showErrorLogs && /* @__PURE__ */ jsxs6(Card, { tone: "critical", radius: 2, padding: 3, children: [
1015
1191
  loadingLogs && /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
1016
- /* @__PURE__ */ jsx9(Spinner, { muted: true }),
1017
- /* @__PURE__ */ jsx9(Text, { size: 1, muted: true, children: "Loading logs\u2026" })
1192
+ /* @__PURE__ */ jsx10(Spinner, { muted: true }),
1193
+ /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: "Loading logs\u2026" })
1018
1194
  ] }),
1019
1195
  logError && /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
1020
- /* @__PURE__ */ jsx9(Text, { size: 1, children: logError }),
1021
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx9("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1022
- /* @__PURE__ */ jsx9(LaunchIcon, {}),
1023
- /* @__PURE__ */ jsx9(Text, { size: 1, children: "View full logs in Vercel" })
1196
+ /* @__PURE__ */ jsx10(Text, { size: 1, children: logError }),
1197
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx10("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1198
+ /* @__PURE__ */ jsx10(LaunchIcon, {}),
1199
+ /* @__PURE__ */ jsx10(Text, { size: 1, children: "View full logs in Vercel" })
1024
1200
  ] }) })
1025
1201
  ] }),
1026
1202
  !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
1027
- /* @__PURE__ */ jsx9(Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ jsx9(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
1028
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx9("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1029
- /* @__PURE__ */ jsx9(LaunchIcon, {}),
1030
- /* @__PURE__ */ jsx9(Text, { size: 1, children: "View full logs in Vercel" })
1203
+ /* @__PURE__ */ jsx10(Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ jsx10(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
1204
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx10("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1205
+ /* @__PURE__ */ jsx10(LaunchIcon, {}),
1206
+ /* @__PURE__ */ jsx10(Text, { size: 1, children: "View full logs in Vercel" })
1031
1207
  ] }) })
1032
1208
  ] })
1033
1209
  ] })
1034
1210
  ] }),
1035
- deployError && /* @__PURE__ */ jsx9(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsx9(Text, { size: 1, children: deployError }) })
1211
+ deployError && /* @__PURE__ */ jsx10(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsx10(Text, { size: 1, children: deployError }) })
1036
1212
  ] })
1037
1213
  ] }),
1038
- pollError && /* @__PURE__ */ jsx9(Card, { tone: "caution", padding: 3, radius: 2, role: "status", children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
1039
- /* @__PURE__ */ jsx9(WarningOutlineIcon, { "aria-hidden": "true" }),
1214
+ pollError && /* @__PURE__ */ jsx10(Card, { tone: "caution", padding: 3, radius: 2, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 2, children: [
1215
+ /* @__PURE__ */ jsx10(WarningOutlineIcon, { "aria-hidden": "true" }),
1040
1216
  /* @__PURE__ */ jsxs6(Text, { size: 1, children: [
1041
1217
  "Status updates paused \u2014 ",
1042
1218
  pollError
@@ -1044,7 +1220,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1044
1220
  ] }) })
1045
1221
  ] }),
1046
1222
  /* @__PURE__ */ jsxs6(Box, { children: [
1047
- /* @__PURE__ */ jsx9(
1223
+ /* @__PURE__ */ jsx10(
1048
1224
  Button,
1049
1225
  {
1050
1226
  mode: "ghost",
@@ -1057,61 +1233,61 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1057
1233
  style: { width: "100%", justifyContent: "flex-start", borderRadius: 0, cursor: "pointer" }
1058
1234
  }
1059
1235
  ),
1060
- showDetails && /* @__PURE__ */ jsx9(Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
1236
+ showDetails && /* @__PURE__ */ jsx10(Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ jsxs6(Stack, { space: 2, children: [
1061
1237
  /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1062
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
1063
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
1238
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
1239
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
1064
1240
  ] }),
1065
1241
  /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1066
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
1067
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
1242
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
1243
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
1068
1244
  ] }),
1069
1245
  target.teamId && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1070
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
1071
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
1246
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
1247
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
1072
1248
  ] }),
1073
1249
  fullSha && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "flex-start", children: [
1074
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
1075
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
1250
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
1251
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
1076
1252
  ] }),
1077
1253
  latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1078
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
1079
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
1254
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
1255
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
1080
1256
  ] }),
1081
1257
  branch && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1082
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
1083
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
1258
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
1259
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
1084
1260
  ] }),
1085
1261
  latest?.uid && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1086
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
1087
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
1262
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
1263
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
1088
1264
  ] }),
1089
1265
  latest?.url && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "flex-start", children: [
1090
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
1091
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
1266
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
1267
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
1092
1268
  ] }),
1093
1269
  safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1094
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
1095
- /* @__PURE__ */ jsx9("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1096
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
1097
- /* @__PURE__ */ jsx9(LaunchIcon, { style: { width: 10, height: 10 } })
1270
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
1271
+ /* @__PURE__ */ jsx10("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs6(Flex, { align: "center", gap: 1, children: [
1272
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
1273
+ /* @__PURE__ */ jsx10(LaunchIcon, { style: { width: 10, height: 10 } })
1098
1274
  ] }) })
1099
1275
  ] }),
1100
1276
  latest?.created && /* @__PURE__ */ jsxs6(Flex, { gap: 2, align: "center", children: [
1101
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
1102
- /* @__PURE__ */ jsx9(Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
1277
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
1278
+ /* @__PURE__ */ jsx10(Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
1103
1279
  ] })
1104
1280
  ] }) })
1105
1281
  ] })
1106
1282
  ] }),
1107
- /* @__PURE__ */ jsx9(
1283
+ /* @__PURE__ */ jsx10(
1108
1284
  Flex,
1109
1285
  {
1110
1286
  direction: "column",
1111
1287
  gap: 2,
1112
1288
  className: "dvfs-deploy-col",
1113
1289
  style: { flexShrink: 0, alignSelf: "stretch" },
1114
- children: /* @__PURE__ */ jsx9(
1290
+ children: /* @__PURE__ */ jsx10(
1115
1291
  Button,
1116
1292
  {
1117
1293
  text: "Deploy",
@@ -1131,7 +1307,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1131
1307
  }
1132
1308
  )
1133
1309
  ] }) }),
1134
- showHistory && /* @__PURE__ */ jsx9(
1310
+ showHistory && /* @__PURE__ */ jsx10(
1135
1311
  DeployHistory,
1136
1312
  {
1137
1313
  target,
@@ -1143,16 +1319,18 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1143
1319
  }
1144
1320
 
1145
1321
  // src/components/TokenSetup.tsx
1146
- import { useId as useId3, useState as useState6, useCallback as useCallback5 } from "react";
1147
- import { useClient } from "sanity";
1148
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1322
+ import { useId as useId4, useState as useState6, useCallback as useCallback5 } from "react";
1323
+ import { useClient as useClient2 } from "sanity";
1324
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
1149
1325
  var TOKEN_DOC_ID = "config.vercelDeploy";
1150
- function TokenSetup({ onSaved, onCancel }) {
1151
- const tokenId = useId3();
1152
- const client = useClient({ apiVersion: "2025-01-01" });
1326
+ function TokenSetup({ onSaved, onCancel, hasToken = false }) {
1327
+ const dialogId = useId4();
1328
+ const tokenId = useId4();
1329
+ const client = useClient2({ apiVersion: "2025-01-01" });
1153
1330
  const [token, setToken] = useState6("");
1154
1331
  const [saving, setSaving] = useState6(false);
1155
1332
  const [error, setError] = useState6(null);
1333
+ const [revoking, setRevoking] = useState6(false);
1156
1334
  const save = useCallback5(async () => {
1157
1335
  if (!token.trim()) return;
1158
1336
  setSaving(true);
@@ -1170,43 +1348,71 @@ function TokenSetup({ onSaved, onCancel }) {
1170
1348
  setSaving(false);
1171
1349
  }
1172
1350
  }, [client, token, onSaved]);
1173
- return /* @__PURE__ */ jsx10(
1351
+ const revoke = useCallback5(async () => {
1352
+ setRevoking(true);
1353
+ setError(null);
1354
+ try {
1355
+ await client.delete(TOKEN_DOC_ID);
1356
+ onSaved();
1357
+ } catch (err) {
1358
+ setError(err instanceof Error ? err.message : "Failed to remove token");
1359
+ } finally {
1360
+ setRevoking(false);
1361
+ }
1362
+ }, [client, onSaved]);
1363
+ return /* @__PURE__ */ jsx11(
1174
1364
  Dialog,
1175
1365
  {
1176
1366
  header: "Connect Vercel API token",
1177
- id: "token-setup",
1367
+ id: dialogId,
1178
1368
  onClose: onCancel,
1179
1369
  width: 1,
1180
- footer: /* @__PURE__ */ jsxs7(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1181
- onCancel && /* @__PURE__ */ jsx10(Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
1182
- /* @__PURE__ */ jsx10(
1370
+ footer: /* @__PURE__ */ jsxs7(Flex, { padding: 3, gap: 2, justify: hasToken ? "space-between" : "flex-end", children: [
1371
+ hasToken && /* @__PURE__ */ jsx11(
1183
1372
  Button,
1184
1373
  {
1185
- text: "Save and connect",
1186
- tone: "primary",
1187
- icon: CheckmarkCircleIcon,
1188
- loading: saving,
1189
- disabled: !token.trim() || saving,
1190
- onClick: save,
1374
+ text: "Remove token",
1375
+ mode: "ghost",
1376
+ tone: "critical",
1377
+ icon: TrashIcon,
1378
+ loading: revoking,
1379
+ disabled: revoking || saving,
1380
+ onClick: revoke,
1191
1381
  style: { cursor: "pointer" }
1192
1382
  }
1193
- )
1383
+ ),
1384
+ /* @__PURE__ */ jsxs7(Flex, { gap: 2, children: [
1385
+ onCancel && /* @__PURE__ */ jsx11(Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
1386
+ /* @__PURE__ */ jsx11(
1387
+ Button,
1388
+ {
1389
+ text: "Save and connect",
1390
+ tone: "primary",
1391
+ icon: CheckmarkCircleIcon,
1392
+ loading: saving,
1393
+ disabled: !token.trim() || saving,
1394
+ onClick: save,
1395
+ style: { cursor: "pointer" }
1396
+ }
1397
+ )
1398
+ ] })
1194
1399
  ] }),
1195
1400
  children: /* @__PURE__ */ jsxs7(Stack, { space: 4, padding: 4, children: [
1196
1401
  /* @__PURE__ */ jsxs7(Stack, { space: 3, children: [
1197
- /* @__PURE__ */ jsx10(Text, { size: 1, muted: true, children: "A Vercel API token lets this tool read deployment status, history, build logs, and branch metadata. Without it you can still trigger deploys \u2014 you just won't see any feedback." }),
1402
+ /* @__PURE__ */ jsx11(Text, { size: 1, muted: true, children: "A Vercel API token lets this tool read deployment status, history, build logs, and branch metadata. Without it you can still trigger deploys \u2014 you just won't see any feedback." }),
1198
1403
  /* @__PURE__ */ jsxs7(Text, { size: 1, muted: true, children: [
1199
1404
  "Create one at ",
1200
- /* @__PURE__ */ jsx10("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
1405
+ /* @__PURE__ */ jsx11("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
1201
1406
  " with",
1202
1407
  " ",
1203
- /* @__PURE__ */ jsx10("strong", { children: "Full Account" }),
1204
- " scope. The token is stored in your Sanity dataset and shared across all authenticated studio users."
1408
+ "scope limited to the ",
1409
+ /* @__PURE__ */ jsx11("strong", { children: "team" }),
1410
+ " that owns your projects. The token is stored in cleartext in your Sanity dataset \u2014 readable by anyone who can read the dataset, including unauthenticated readers if it is public and shared across all authenticated studio users."
1205
1411
  ] })
1206
1412
  ] }),
1207
1413
  /* @__PURE__ */ jsxs7(Stack, { space: 2, children: [
1208
- /* @__PURE__ */ jsx10(Label, { as: "label", size: 1, htmlFor: tokenId, children: "Vercel API Token" }),
1209
- /* @__PURE__ */ jsx10(
1414
+ /* @__PURE__ */ jsx11(Label, { as: "label", size: 1, htmlFor: tokenId, children: "Vercel API Token" }),
1415
+ /* @__PURE__ */ jsx11(
1210
1416
  TextInput,
1211
1417
  {
1212
1418
  id: tokenId,
@@ -1217,35 +1423,40 @@ function TokenSetup({ onSaved, onCancel }) {
1217
1423
  }
1218
1424
  )
1219
1425
  ] }),
1220
- error && /* @__PURE__ */ jsx10(Card, { tone: "critical", padding: 3, radius: 2, role: "alert", children: /* @__PURE__ */ jsx10(Text, { size: 1, children: error }) })
1426
+ error && /* @__PURE__ */ jsx11(Card, { tone: "critical", padding: 3, radius: 2, role: "alert", children: /* @__PURE__ */ jsx11(Text, { size: 1, children: error }) })
1221
1427
  ] })
1222
1428
  }
1223
1429
  );
1224
1430
  }
1225
1431
 
1226
1432
  // src/components/DeployTargetForm.tsx
1227
- import { useId as useId4, useState as useState7, useCallback as useCallback6 } from "react";
1228
- import { useClient as useClient2 } from "sanity";
1229
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1433
+ import { useId as useId5, useState as useState7, useCallback as useCallback6 } from "react";
1434
+ import { useClient as useClient3 } from "sanity";
1435
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1230
1436
  var VERCEL_HOOK_RE2 = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//;
1231
1437
  function DeployTargetForm({ initial, onSaved, onClose }) {
1232
- const client = useClient2({ apiVersion: "2025-01-01" });
1438
+ const client = useClient3({ apiVersion: "2025-01-01" });
1233
1439
  const isEdit = Boolean(initial);
1234
- const nameId = useId4();
1235
- const urlId = useId4();
1236
- const urlErrorId = useId4();
1237
- const urlHelpId = useId4();
1238
- const teamId_ = useId4();
1239
- const teamHelpId = useId4();
1240
- const disableId = useId4();
1440
+ const pluginConfig = usePluginConfig();
1441
+ const proxyKeyId = useId5();
1442
+ const dialogId = useId5();
1443
+ const nameId = useId5();
1444
+ const urlId = useId5();
1445
+ const urlErrorId = useId5();
1446
+ const urlHelpId = useId5();
1447
+ const teamId_ = useId5();
1448
+ const teamHelpId = useId5();
1449
+ const disableId = useId5();
1241
1450
  const [name, setName] = useState7(initial?.name ?? "");
1242
1451
  const [url, setUrl] = useState7(initial?.url ?? "");
1243
1452
  const [teamId, setTeamId] = useState7(initial?.teamId ?? "");
1453
+ const [proxyKey, setProxyKey] = useState7(initial?.proxyKey ?? "");
1244
1454
  const [disableDelete, setDisableDelete] = useState7(initial?.disableDeleteAction ?? false);
1245
1455
  const [saving, setSaving] = useState7(false);
1246
1456
  const [error, setError] = useState7(null);
1247
1457
  const urlValid = !url || VERCEL_HOOK_RE2.test(url.trim());
1248
- const canSave = name.trim() && url.trim() && urlValid;
1458
+ const isProxy = pluginConfig.mode === "proxy";
1459
+ const canSave = Boolean(name.trim()) && urlValid && (isProxy ? Boolean(proxyKey.trim()) : Boolean(url.trim()));
1249
1460
  const save = useCallback6(async () => {
1250
1461
  if (!canSave) return;
1251
1462
  setSaving(true);
@@ -1254,6 +1465,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1254
1465
  name: name.trim(),
1255
1466
  url: url.trim(),
1256
1467
  teamId: teamId.trim() || null,
1468
+ proxyKey: proxyKey.trim() || null,
1257
1469
  disableDeleteAction: disableDelete
1258
1470
  };
1259
1471
  try {
@@ -1268,17 +1480,17 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1268
1480
  } finally {
1269
1481
  setSaving(false);
1270
1482
  }
1271
- }, [canSave, isEdit, initial, client, name, url, teamId, disableDelete, onSaved]);
1272
- return /* @__PURE__ */ jsx11(
1483
+ }, [canSave, isEdit, initial, client, name, url, teamId, proxyKey, disableDelete, onSaved]);
1484
+ return /* @__PURE__ */ jsx12(
1273
1485
  Dialog,
1274
1486
  {
1275
1487
  header: isEdit ? `Edit "${initial?.name}"` : "Add deploy target",
1276
- id: "deploy-target-form",
1488
+ id: dialogId,
1277
1489
  onClose,
1278
1490
  width: 1,
1279
1491
  footer: /* @__PURE__ */ jsxs8(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1280
- /* @__PURE__ */ jsx11(Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1281
- /* @__PURE__ */ jsx11(
1492
+ /* @__PURE__ */ jsx12(Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1493
+ /* @__PURE__ */ jsx12(
1282
1494
  Button,
1283
1495
  {
1284
1496
  text: isEdit ? "Save changes" : "Add target",
@@ -1291,13 +1503,13 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1291
1503
  }
1292
1504
  )
1293
1505
  ] }),
1294
- children: /* @__PURE__ */ jsx11(Box, { padding: 4, children: /* @__PURE__ */ jsxs8(Stack, { space: 4, children: [
1506
+ children: /* @__PURE__ */ jsx12(Box, { padding: 4, children: /* @__PURE__ */ jsxs8(Stack, { space: 4, children: [
1295
1507
  /* @__PURE__ */ jsxs8(Stack, { space: 2, children: [
1296
1508
  /* @__PURE__ */ jsxs8(Label, { as: "label", size: 1, htmlFor: nameId, children: [
1297
1509
  "Name ",
1298
- /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: "*" })
1510
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", children: "*" })
1299
1511
  ] }),
1300
- /* @__PURE__ */ jsx11(
1512
+ /* @__PURE__ */ jsx12(
1301
1513
  TextInput,
1302
1514
  {
1303
1515
  id: nameId,
@@ -1309,12 +1521,29 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1309
1521
  }
1310
1522
  )
1311
1523
  ] }),
1312
- /* @__PURE__ */ jsxs8(Stack, { space: 2, children: [
1524
+ isProxy ? /* @__PURE__ */ jsxs8(Stack, { space: 2, children: [
1525
+ /* @__PURE__ */ jsxs8(Label, { as: "label", size: 1, htmlFor: proxyKeyId, children: [
1526
+ "Proxy Key ",
1527
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", children: "*" })
1528
+ ] }),
1529
+ /* @__PURE__ */ jsx12(
1530
+ TextInput,
1531
+ {
1532
+ id: proxyKeyId,
1533
+ required: true,
1534
+ "aria-required": "true",
1535
+ value: proxyKey,
1536
+ onChange: (e) => setProxyKey(e.target.value),
1537
+ placeholder: "production"
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsx12(Text, { size: 0, muted: true, children: "Matches a key configured on your deploy proxy, which holds the real hook URL. Contains no secret." })
1541
+ ] }) : /* @__PURE__ */ jsxs8(Stack, { space: 2, children: [
1313
1542
  /* @__PURE__ */ jsxs8(Label, { as: "label", size: 1, htmlFor: urlId, children: [
1314
1543
  "Deploy hook URL ",
1315
- /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: "*" })
1544
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", children: "*" })
1316
1545
  ] }),
1317
- /* @__PURE__ */ jsx11(
1546
+ /* @__PURE__ */ jsx12(
1318
1547
  TextInput,
1319
1548
  {
1320
1549
  id: urlId,
@@ -1327,15 +1556,15 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1327
1556
  placeholder: "https://api.vercel.com/v1/integrations/deploy/\u2026"
1328
1557
  }
1329
1558
  ),
1330
- url && !urlValid && /* @__PURE__ */ jsx11(Card, { tone: "critical", padding: 2, radius: 2, role: "alert", children: /* @__PURE__ */ jsx11(Text, { id: urlErrorId, size: 1, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }) }),
1331
- /* @__PURE__ */ jsx11(Text, { id: urlHelpId, size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1559
+ url && !urlValid && /* @__PURE__ */ jsx12(Card, { tone: "critical", padding: 2, radius: 2, role: "alert", children: /* @__PURE__ */ jsx12(Text, { id: urlErrorId, size: 1, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }) }),
1560
+ /* @__PURE__ */ jsx12(Text, { id: urlHelpId, size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1332
1561
  ] }),
1333
1562
  /* @__PURE__ */ jsxs8(Stack, { space: 2, children: [
1334
1563
  /* @__PURE__ */ jsxs8(Label, { as: "label", size: 1, htmlFor: teamId_, children: [
1335
1564
  "Team ID ",
1336
- /* @__PURE__ */ jsx11("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1565
+ /* @__PURE__ */ jsx12("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1337
1566
  ] }),
1338
- /* @__PURE__ */ jsx11(
1567
+ /* @__PURE__ */ jsx12(
1339
1568
  TextInput,
1340
1569
  {
1341
1570
  id: teamId_,
@@ -1345,14 +1574,10 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1345
1574
  placeholder: "team_xxxxxxxx"
1346
1575
  }
1347
1576
  ),
1348
- /* @__PURE__ */ jsxs8(Text, { id: teamHelpId, size: 0, muted: true, children: [
1349
- "Required for team-owned Vercel projects. Find it at Vercel \u2192 Settings \u2192 General \u2192 Team ID (starts with ",
1350
- /* @__PURE__ */ jsx11("code", { children: "team_" }),
1351
- ")."
1352
- ] })
1577
+ /* @__PURE__ */ jsx12(Text, { id: teamHelpId, size: 0, muted: true, children: isProxy ? "Not used in proxy mode \u2014 the proxy supplies the team from its own configuration." : "Required for team-owned Vercel projects. Find it at Vercel \u2192 Settings \u2192 General \u2192 Team ID (starts with team_)." })
1353
1578
  ] }),
1354
1579
  /* @__PURE__ */ jsxs8(Flex, { align: "center", gap: 3, children: [
1355
- /* @__PURE__ */ jsx11(
1580
+ /* @__PURE__ */ jsx12(
1356
1581
  Switch,
1357
1582
  {
1358
1583
  checked: disableDelete,
@@ -1361,25 +1586,27 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1361
1586
  }
1362
1587
  ),
1363
1588
  /* @__PURE__ */ jsxs8(Stack, { space: 1, children: [
1364
- /* @__PURE__ */ jsx11(Label, { as: "label", size: 1, htmlFor: disableId, children: "Disable delete action" }),
1365
- /* @__PURE__ */ jsx11(Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1589
+ /* @__PURE__ */ jsx12(Label, { as: "label", size: 1, htmlFor: disableId, children: "Disable delete action" }),
1590
+ /* @__PURE__ */ jsx12(Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1366
1591
  ] })
1367
1592
  ] }),
1368
- error && /* @__PURE__ */ jsx11(Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx11(Text, { size: 1, children: error }) })
1593
+ error && /* @__PURE__ */ jsx12(Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx12(Text, { size: 1, children: error }) })
1369
1594
  ] }) })
1370
1595
  }
1371
1596
  );
1372
1597
  }
1373
1598
 
1374
1599
  // src/version.ts
1375
- var VERSION = "1.2.0";
1600
+ var VERSION = "1.3.0";
1376
1601
 
1377
1602
  // src/components/DeployTool.tsx
1378
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1603
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1379
1604
  var TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`;
1380
1605
  var TARGETS_QUERY = `*[_type == "vercel_deploy" && !(_id in path("drafts.**"))] | order(_createdAt asc)`;
1381
1606
  function DeployTool() {
1382
- const client = useClient3({ apiVersion: "2025-01-01" });
1607
+ const client = useClient4({ apiVersion: "2025-01-01" });
1608
+ const pluginConfig = usePluginConfig();
1609
+ const usesToken = pluginConfig.mode === "direct";
1383
1610
  const toast = useToast();
1384
1611
  const [token, setToken] = useState8(null);
1385
1612
  const [targets, setTargets] = useState8([]);
@@ -1470,14 +1697,14 @@ function DeployTool() {
1470
1697
  }
1471
1698
  }, [client, pendingDelete, toast]);
1472
1699
  if (loading) {
1473
- return /* @__PURE__ */ jsx12(Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ jsx12(Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ jsx12(Spinner, { muted: true }) }) });
1700
+ return /* @__PURE__ */ jsx13(Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ jsx13(Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ jsx13(Spinner, { muted: true }) }) });
1474
1701
  }
1475
1702
  return /* @__PURE__ */ jsxs9(Card, { height: "fill", tone: "transparent", children: [
1476
- /* @__PURE__ */ jsx12(Box, { padding: 5, children: /* @__PURE__ */ jsxs9(Stack, { space: 5, children: [
1703
+ /* @__PURE__ */ jsx13(Box, { padding: 5, children: /* @__PURE__ */ jsxs9(Stack, { space: 5, children: [
1477
1704
  /* @__PURE__ */ jsxs9(Flex, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1478
- /* @__PURE__ */ jsx12(Heading, { size: 2, children: "Deploy with Vercel" }),
1705
+ /* @__PURE__ */ jsx13(Heading, { size: 2, children: "Deploy with Vercel" }),
1479
1706
  /* @__PURE__ */ jsxs9(Flex, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1480
- /* @__PURE__ */ jsx12(
1707
+ usesToken && /* @__PURE__ */ jsx13(
1481
1708
  Button,
1482
1709
  {
1483
1710
  text: token ? "Token connected" : "Connect API token",
@@ -1489,7 +1716,7 @@ function DeployTool() {
1489
1716
  style: { cursor: "pointer" }
1490
1717
  }
1491
1718
  ),
1492
- /* @__PURE__ */ jsx12(
1719
+ /* @__PURE__ */ jsx13(
1493
1720
  Button,
1494
1721
  {
1495
1722
  text: "Add target",
@@ -1502,12 +1729,12 @@ function DeployTool() {
1502
1729
  )
1503
1730
  ] })
1504
1731
  ] }),
1505
- !token && /* @__PURE__ */ jsx12(Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ jsxs9(Flex, { align: "center", justify: "space-between", gap: 4, children: [
1732
+ usesToken && !token && /* @__PURE__ */ jsx13(Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ jsxs9(Flex, { align: "center", justify: "space-between", gap: 4, children: [
1506
1733
  /* @__PURE__ */ jsxs9(Stack, { space: 2, children: [
1507
- /* @__PURE__ */ jsx12(Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1508
- /* @__PURE__ */ jsx12(Text, { size: 1, muted: true, children: "You can trigger deploys now. Connect a Vercel API token to also see deployment status, build logs, history, and commit metadata." })
1734
+ /* @__PURE__ */ jsx13(Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1735
+ /* @__PURE__ */ jsx13(Text, { size: 1, muted: true, children: "You can trigger deploys now. Connect a Vercel API token to also see deployment status, build logs, history, and commit metadata." })
1509
1736
  ] }),
1510
- /* @__PURE__ */ jsx12(
1737
+ /* @__PURE__ */ jsx13(
1511
1738
  Button,
1512
1739
  {
1513
1740
  text: "Connect",
@@ -1518,15 +1745,15 @@ function DeployTool() {
1518
1745
  }
1519
1746
  )
1520
1747
  ] }) }),
1521
- targets.length === 0 && /* @__PURE__ */ jsx12(Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ jsxs9(Stack, { space: 4, style: { textAlign: "center" }, children: [
1522
- /* @__PURE__ */ jsx12(Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1748
+ targets.length === 0 && /* @__PURE__ */ jsx13(Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ jsxs9(Stack, { space: 4, style: { textAlign: "center" }, children: [
1749
+ /* @__PURE__ */ jsx13(Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1523
1750
  /* @__PURE__ */ jsxs9(Text, { size: 1, muted: true, children: [
1524
1751
  "Add a deploy target using the button above, or create a",
1525
1752
  " ",
1526
- /* @__PURE__ */ jsx12("code", { children: "vercel_deploy" }),
1753
+ /* @__PURE__ */ jsx13("code", { children: "vercel_deploy" }),
1527
1754
  " document directly in the dataset."
1528
1755
  ] }),
1529
- /* @__PURE__ */ jsx12(Flex, { justify: "center", children: /* @__PURE__ */ jsx12(
1756
+ /* @__PURE__ */ jsx13(Flex, { justify: "center", children: /* @__PURE__ */ jsx13(
1530
1757
  Button,
1531
1758
  {
1532
1759
  text: "Add deploy target",
@@ -1537,12 +1764,12 @@ function DeployTool() {
1537
1764
  }
1538
1765
  ) })
1539
1766
  ] }) }),
1540
- targets.length > 0 && /* @__PURE__ */ jsx12("div", { className: "dvfs-grid", style: {
1767
+ targets.length > 0 && /* @__PURE__ */ jsx13("div", { className: "dvfs-grid", style: {
1541
1768
  display: "grid",
1542
1769
  gridTemplateColumns: "repeat(auto-fill, minmax(min(540px, 100%), 1fr))",
1543
1770
  gap: "16px",
1544
1771
  alignItems: "start"
1545
- }, children: targets.map((target) => /* @__PURE__ */ jsx12(
1772
+ }, children: targets.map((target) => /* @__PURE__ */ jsx13(
1546
1773
  DeployItem,
1547
1774
  {
1548
1775
  target,
@@ -1553,7 +1780,7 @@ function DeployTool() {
1553
1780
  target._id
1554
1781
  )) })
1555
1782
  ] }) }),
1556
- /* @__PURE__ */ jsx12(
1783
+ /* @__PURE__ */ jsx13(
1557
1784
  Box,
1558
1785
  {
1559
1786
  style: {
@@ -1570,17 +1797,18 @@ function DeployTool() {
1570
1797
  ] })
1571
1798
  }
1572
1799
  ),
1573
- showTokenSetup && /* @__PURE__ */ jsx12(
1800
+ showTokenSetup && /* @__PURE__ */ jsx13(
1574
1801
  TokenSetup,
1575
1802
  {
1576
1803
  onSaved: () => {
1577
1804
  setShowTokenSetup(false);
1578
1805
  load();
1579
1806
  },
1580
- onCancel: () => setShowTokenSetup(false)
1807
+ onCancel: () => setShowTokenSetup(false),
1808
+ hasToken: Boolean(token)
1581
1809
  }
1582
1810
  ),
1583
- showCreateForm && /* @__PURE__ */ jsx12(
1811
+ showCreateForm && /* @__PURE__ */ jsx13(
1584
1812
  DeployTargetForm,
1585
1813
  {
1586
1814
  onSaved: () => {
@@ -1590,7 +1818,7 @@ function DeployTool() {
1590
1818
  onClose: () => setShowCreateForm(false)
1591
1819
  }
1592
1820
  ),
1593
- pendingEdit && /* @__PURE__ */ jsx12(
1821
+ pendingEdit && /* @__PURE__ */ jsx13(
1594
1822
  DeployTargetForm,
1595
1823
  {
1596
1824
  initial: pendingEdit,
@@ -1601,7 +1829,7 @@ function DeployTool() {
1601
1829
  onClose: () => setPendingEdit(null)
1602
1830
  }
1603
1831
  ),
1604
- pendingDelete && /* @__PURE__ */ jsx12(
1832
+ pendingDelete && /* @__PURE__ */ jsx13(
1605
1833
  Dialog,
1606
1834
  {
1607
1835
  header: "Delete deploy target?",
@@ -1609,7 +1837,7 @@ function DeployTool() {
1609
1837
  onClose: () => setPendingDelete(null),
1610
1838
  width: 1,
1611
1839
  footer: /* @__PURE__ */ jsxs9(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1612
- /* @__PURE__ */ jsx12(
1840
+ /* @__PURE__ */ jsx13(
1613
1841
  Button,
1614
1842
  {
1615
1843
  text: "Cancel",
@@ -1618,7 +1846,7 @@ function DeployTool() {
1618
1846
  style: { cursor: "pointer" }
1619
1847
  }
1620
1848
  ),
1621
- /* @__PURE__ */ jsx12(
1849
+ /* @__PURE__ */ jsx13(
1622
1850
  Button,
1623
1851
  {
1624
1852
  text: "Delete",
@@ -1631,16 +1859,16 @@ function DeployTool() {
1631
1859
  }
1632
1860
  )
1633
1861
  ] }),
1634
- children: /* @__PURE__ */ jsx12(Box, { padding: 4, children: /* @__PURE__ */ jsxs9(Stack, { space: 3, children: [
1862
+ children: /* @__PURE__ */ jsx13(Box, { padding: 4, children: /* @__PURE__ */ jsxs9(Stack, { space: 3, children: [
1635
1863
  /* @__PURE__ */ jsxs9(Flex, { align: "center", gap: 2, children: [
1636
- /* @__PURE__ */ jsx12(WarningOutlineIcon, {}),
1637
- /* @__PURE__ */ jsx12(Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1864
+ /* @__PURE__ */ jsx13(WarningOutlineIcon, {}),
1865
+ /* @__PURE__ */ jsx13(Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1638
1866
  ] }),
1639
- /* @__PURE__ */ jsx12(Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1867
+ /* @__PURE__ */ jsx13(Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1640
1868
  ] }) })
1641
1869
  }
1642
1870
  ),
1643
- /* @__PURE__ */ jsx12(ToastViewport, {})
1871
+ /* @__PURE__ */ jsx13(ToastViewport, {})
1644
1872
  ] });
1645
1873
  }
1646
1874
 
@@ -1649,10 +1877,10 @@ import { defineField, defineType } from "sanity";
1649
1877
 
1650
1878
  // src/schema/schemaIcon.tsx
1651
1879
  import { forwardRef as forwardRef3 } from "react";
1652
- import { jsx as jsx13 } from "react/jsx-runtime";
1880
+ import { jsx as jsx14 } from "react/jsx-runtime";
1653
1881
  var SchemaRocketIcon = forwardRef3(
1654
1882
  function SchemaRocketIcon2(props, ref) {
1655
- return /* @__PURE__ */ jsx13(
1883
+ return /* @__PURE__ */ jsx14(
1656
1884
  "svg",
1657
1885
  {
1658
1886
  width: "1em",
@@ -1664,7 +1892,7 @@ var SchemaRocketIcon = forwardRef3(
1664
1892
  focusable: "false",
1665
1893
  ...props,
1666
1894
  ref,
1667
- children: /* @__PURE__ */ jsx13(
1895
+ children: /* @__PURE__ */ jsx14(
1668
1896
  "path",
1669
1897
  {
1670
1898
  d: "M12.5 20.5L15.5 14M11 9.49999L4.5 12.5M9 14C9 14 7.54688 14.9531 6.5 16C5.5 17 4.5 20.5 4.5 20.5C4.5 20.5 8 19.5 9 18.5C10 17.5 11 16 11 16M9 14C9 14 10.1 9.9 12.5 7.5C15.5 4.5 20.5 4.5 20.5 4.5C20.5 4.5 20.5 9.5 17.5 12.5C15.7492 14.2508 11 16 11 16L9 14ZM16.5 9.99999C16.5 10.8284 15.8284 11.5 15 11.5C14.1716 11.5 13.5 10.8284 13.5 9.99999C13.5 9.17157 14.1716 8.49999 15 8.49999C15.8284 8.49999 16.5 9.17157 16.5 9.99999Z",
@@ -1696,8 +1924,11 @@ var vercelDeploySchema = defineType({
1696
1924
  name: "url",
1697
1925
  title: "Deploy Hook URL",
1698
1926
  type: "url",
1699
- description: "From Vercel \u2192 Project Settings \u2192 Git \u2192 Deploy Hooks",
1700
- validation: (Rule) => Rule.required().uri({ scheme: ["https"] }).custom((url) => {
1927
+ description: "From Vercel \u2192 Project Settings \u2192 Git \u2192 Deploy Hooks. Leave empty in proxy mode and set Proxy Key instead \u2014 a hook URL is itself a deploy credential, so anyone who can read this dataset can trigger a build with it.",
1928
+ validation: (Rule) => Rule.uri({ scheme: ["https"] }).custom((url, context) => {
1929
+ const doc = context.document;
1930
+ if (!url && !doc?.proxyKey) return "Set either a Deploy Hook URL or a Proxy Key";
1931
+ if (!url) return true;
1701
1932
  if (typeof url !== "string") return true;
1702
1933
  if (!url.includes("api.vercel.com/v1/integrations/deploy/")) {
1703
1934
  return "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)";
@@ -1705,6 +1936,17 @@ var vercelDeploySchema = defineType({
1705
1936
  return true;
1706
1937
  })
1707
1938
  }),
1939
+ defineField({
1940
+ name: "proxyKey",
1941
+ title: "Proxy Key",
1942
+ type: "string",
1943
+ description: "Used in proxy mode. Matches a key configured on the deploy proxy, which holds the real hook URL. Contains no secret.",
1944
+ validation: (Rule) => Rule.custom((key, context) => {
1945
+ const doc = context.document;
1946
+ if (!key && !doc?.url) return "Set either a Deploy Hook URL or a Proxy Key";
1947
+ return true;
1948
+ })
1949
+ }),
1708
1950
  defineField({
1709
1951
  name: "teamId",
1710
1952
  title: "Vercel Team ID",
@@ -1724,50 +1966,28 @@ var vercelDeploySchema = defineType({
1724
1966
  }
1725
1967
  });
1726
1968
 
1727
- // src/schema/vercelConfig.ts
1728
- import { defineField as defineField2, defineType as defineType2 } from "sanity";
1729
- var vercelConfigSchema = defineType2({
1730
- name: "vercelDeploy.config",
1731
- title: "Vercel Deploy Configuration",
1732
- type: "document",
1733
- fields: [
1734
- defineField2({
1735
- name: "accessToken",
1736
- title: "Vercel API Token",
1737
- type: "string",
1738
- description: "Readable by anyone who can read this dataset. Delete this document to revoke the stored token."
1739
- })
1740
- ],
1741
- preview: {
1742
- select: { token: "accessToken" },
1743
- prepare: ({ token }) => ({
1744
- title: "Vercel API Token",
1745
- // Never render the secret itself in a preview.
1746
- subtitle: token ? "Connected" : "Not set"
1747
- })
1748
- }
1749
- });
1750
-
1751
- // src/index.ts
1969
+ // src/index.tsx
1970
+ import { jsx as jsx15 } from "react/jsx-runtime";
1752
1971
  var vercelDeploy = definePlugin((options) => {
1753
1972
  const config = options ?? {};
1973
+ const resolved = resolveConfig(options);
1754
1974
  return {
1755
1975
  name: "deploy-vercel-from-sanity",
1756
1976
  schema: {
1757
- types: [vercelDeploySchema, vercelConfigSchema]
1977
+ types: [vercelDeploySchema]
1758
1978
  },
1759
1979
  tools: [
1760
1980
  {
1761
1981
  name: config.name ?? "vercel-deploy",
1762
1982
  title: config.title ?? "Deploy",
1763
1983
  icon: config.icon ?? RocketIcon,
1764
- component: DeployTool
1984
+ // Wrapped so the tool tree can read the resolved config without prop drilling.
1985
+ component: () => /* @__PURE__ */ jsx15(ConfigProvider, { value: resolved, children: /* @__PURE__ */ jsx15(DeployTool, {}) })
1765
1986
  }
1766
1987
  ]
1767
1988
  };
1768
1989
  });
1769
1990
  export {
1770
- vercelConfigSchema,
1771
1991
  vercelDeploy,
1772
1992
  vercelDeploySchema
1773
1993
  };