@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.js CHANGED
@@ -27,10 +27,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
- // src/index.ts
30
+ // src/index.tsx
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- vercelConfigSchema: () => vercelConfigSchema,
34
33
  vercelDeploy: () => vercelDeploy,
35
34
  vercelDeploySchema: () => vercelDeploySchema
36
35
  });
@@ -45,10 +44,18 @@ var sanityUi = __toESM(require("@sanity/ui"));
45
44
  var sanityIcons = __toESM(require("@sanity/icons"));
46
45
  var UI = sanityUi;
47
46
  var ICONS = sanityIcons;
48
- function resolveExport(ns, name) {
47
+ function resolveComponent(ns, name) {
49
48
  const value = ns[name];
50
49
  return typeof value === "function" || typeof value === "object" && value !== null ? value : void 0;
51
50
  }
51
+ function resolveFunction(ns, name) {
52
+ const value = ns[name];
53
+ return typeof value === "function" ? value : void 0;
54
+ }
55
+ function resolveRecord(ns, name) {
56
+ const value = ns[name];
57
+ return typeof value === "object" && value !== null ? value : void 0;
58
+ }
52
59
  var STACK_USES_GAP = typeof UI.Stack === "function";
53
60
 
54
61
  // src/icons.tsx
@@ -57,11 +64,11 @@ var GLYPH = { width: "1em", height: "1em", viewBox: "0 0 25 25", fill: "none" };
57
64
  var MissingIcon = (0, import_react.forwardRef)(function MissingIcon2(props, ref) {
58
65
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...GLYPH, xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, ref });
59
66
  });
60
- var SYMBOL_MAP = resolveExport(ICONS, "icons");
67
+ var SYMBOL_MAP = resolveRecord(ICONS, "icons");
61
68
  function resolveIcon(name, symbol) {
62
- const named = resolveExport(ICONS, name);
69
+ const named = resolveComponent(ICONS, name);
63
70
  if (named) return named;
64
- const Icon = resolveExport(ICONS, "Icon");
71
+ const Icon = resolveComponent(ICONS, "Icon");
65
72
  if (!Icon) return MissingIcon;
66
73
  if (SYMBOL_MAP && !(symbol in SYMBOL_MAP)) return MissingIcon;
67
74
  const Resolved = (0, import_react.forwardRef)(function SanityIcon(props, ref) {
@@ -87,33 +94,76 @@ var TrashIcon = resolveIcon("TrashIcon", "trash");
87
94
  var WarningOutlineIcon = resolveIcon("WarningOutlineIcon", "warning-outline");
88
95
 
89
96
  // src/components/DeployTool.tsx
90
- var import_react10 = require("react");
91
- var import_sanity3 = require("sanity");
97
+ var import_react11 = require("react");
98
+ var import_sanity4 = require("sanity");
99
+
100
+ // src/config.tsx
101
+ var import_react2 = require("react");
102
+ var import_jsx_runtime2 = require("react/jsx-runtime");
103
+ var DEFAULTS = { mode: "direct" };
104
+ var ConfigContext = (0, import_react2.createContext)(DEFAULTS);
105
+ function resolveConfig(options) {
106
+ const config = options ?? {};
107
+ return {
108
+ mode: config.mode ?? "direct",
109
+ // Trailing slashes would double up when request paths are appended.
110
+ proxyUrl: config.proxyUrl?.replace(/\/+$/, ""),
111
+ statusKey: config.statusKey
112
+ };
113
+ }
114
+ function ConfigProvider({ value, children }) {
115
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConfigContext.Provider, { value, children });
116
+ }
117
+ function usePluginConfig() {
118
+ return (0, import_react2.useContext)(ConfigContext);
119
+ }
92
120
 
93
121
  // src/components/DeployItem.tsx
94
- var import_react7 = require("react");
122
+ var import_react8 = require("react");
95
123
  var import_react_dom = require("react-dom");
96
124
 
97
125
  // src/compat/primitives.tsx
98
- var import_react2 = require("react");
99
- var import_jsx_runtime2 = require("react/jsx-runtime");
126
+ var import_react3 = require("react");
127
+ var import_jsx_runtime3 = require("react/jsx-runtime");
100
128
  function domFallback(tag) {
101
- const Fallback = (0, import_react2.forwardRef)(function SanityUiFallback(props, ref) {
102
- const { children, style, id, className, onClick, href, title, ...rest } = props;
129
+ const Fallback = (0, import_react3.forwardRef)(function SanityUiFallback(props, ref) {
130
+ const { children, style, id, className, onClick, href, title, as, ...rest } = props;
131
+ const element = typeof as === "string" ? as : tag;
103
132
  const passthrough = { style, id, className, onClick, href, title, ref };
104
- for (const key of ["role", "type", "value", "checked", "placeholder", "disabled", "onChange", "onKeyDown"]) {
133
+ for (const key of [
134
+ "role",
135
+ "type",
136
+ "value",
137
+ "checked",
138
+ "placeholder",
139
+ "disabled",
140
+ "tabIndex",
141
+ "onChange",
142
+ "onKeyDown",
143
+ "onFocus",
144
+ "onBlur",
145
+ "onMouseEnter",
146
+ "onMouseLeave",
147
+ "htmlFor",
148
+ "target",
149
+ "rel",
150
+ "name",
151
+ "autoComplete",
152
+ "required",
153
+ "readOnly"
154
+ ]) {
105
155
  if (key in rest) passthrough[key] = rest[key];
106
156
  }
107
157
  for (const key of Object.keys(rest)) {
108
158
  if (key.startsWith("aria-") || key.startsWith("data-")) passthrough[key] = rest[key];
109
159
  }
110
- return (0, import_react2.createElement)(tag, passthrough, children);
160
+ return (0, import_react3.createElement)(element, passthrough, children);
111
161
  });
112
162
  Fallback.displayName = `SanityUiFallback(${tag})`;
113
163
  return Fallback;
114
164
  }
115
165
  function primitive(name, tag) {
116
- return resolveExport(UI, name) ?? domFallback(tag);
166
+ return resolveComponent(UI, name) ?? domFallback(tag);
117
167
  }
118
168
  var Box = primitive("Box", "div");
119
169
  var Card = primitive("Card", "div");
@@ -132,12 +182,12 @@ var Dialog = primitive("Dialog", "div");
132
182
  var SanityStack = primitive("Stack", "div");
133
183
  function Stack({ space, children, ...rest }) {
134
184
  const spacing = space === void 0 ? {} : STACK_USES_GAP ? { gap: space } : { space };
135
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SanityStack, { ...rest, ...spacing, children });
185
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SanityStack, { ...rest, ...spacing, children });
136
186
  }
137
187
 
138
188
  // src/compat/toast.tsx
139
- var import_react3 = require("react");
140
- var import_jsx_runtime3 = require("react/jsx-runtime");
189
+ var import_react4 = require("react");
190
+ var import_jsx_runtime4 = require("react/jsx-runtime");
141
191
  var LOCAL_TOAST_MS = 6e3;
142
192
  var MAX_VISIBLE_TOASTS = 4;
143
193
  var TOAST_Z_INDEX = 1e6;
@@ -172,11 +222,19 @@ function scheduleDismiss(id, status) {
172
222
  }
173
223
  function pushLocalToast(params) {
174
224
  const toast = { ...params, id: nextToastId++ };
175
- localToasts = [...localToasts, toast].slice(-MAX_VISIBLE_TOASTS);
225
+ const next = [...localToasts, toast];
226
+ for (const dropped of next.slice(0, Math.max(0, next.length - MAX_VISIBLE_TOASTS))) {
227
+ const timer = dismissTimers.get(dropped.id);
228
+ if (timer) {
229
+ clearTimeout(timer);
230
+ dismissTimers.delete(dropped.id);
231
+ }
232
+ }
233
+ localToasts = next.slice(-MAX_VISIBLE_TOASTS);
176
234
  emitToasts();
177
235
  scheduleDismiss(toast.id, toast.status);
178
236
  }
179
- var installedUseToast = resolveExport(UI, "useToast");
237
+ var installedUseToast = resolveFunction(UI, "useToast");
180
238
  var localToaster = { push: pushLocalToast };
181
239
  function useToast() {
182
240
  const real = installedUseToast;
@@ -184,8 +242,8 @@ function useToast() {
184
242
  return localToaster;
185
243
  }
186
244
  function ToastViewport() {
187
- const [toasts, setToasts] = (0, import_react3.useState)(localToasts);
188
- (0, import_react3.useEffect)(() => {
245
+ const [toasts, setToasts] = (0, import_react4.useState)(localToasts);
246
+ (0, import_react4.useEffect)(() => {
189
247
  if (installedUseToast) return;
190
248
  toastListeners.add(setToasts);
191
249
  setToasts(localToasts);
@@ -193,7 +251,11 @@ function ToastViewport() {
193
251
  toastListeners.delete(setToasts);
194
252
  };
195
253
  }, []);
196
- const hold = (0, import_react3.useCallback)((id) => {
254
+ const resume = (0, import_react4.useCallback)((toast, el) => {
255
+ if (el && el.contains(document.activeElement)) return;
256
+ scheduleDismiss(toast.id, toast.status);
257
+ }, []);
258
+ const hold = (0, import_react4.useCallback)((id) => {
197
259
  const timer = dismissTimers.get(id);
198
260
  if (timer) {
199
261
  clearTimeout(timer);
@@ -201,12 +263,11 @@ function ToastViewport() {
201
263
  }
202
264
  }, []);
203
265
  if (installedUseToast) return null;
204
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
266
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
205
267
  Box,
206
268
  {
207
- role: "status",
208
- "aria-live": "polite",
209
- "aria-atomic": false,
269
+ role: "log",
270
+ "aria-label": "Deployment notifications",
210
271
  style: {
211
272
  position: "fixed",
212
273
  bottom: 16,
@@ -215,23 +276,24 @@ function ToastViewport() {
215
276
  width: "min(360px, calc(100vw - 32px))",
216
277
  pointerEvents: "none"
217
278
  },
218
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
279
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
219
280
  Card,
220
281
  {
221
282
  padding: 3,
222
283
  radius: 2,
223
284
  shadow: 3,
224
- tone: LOCAL_TOAST_TONE[toast.status ?? "info"] ?? "primary",
285
+ tone: LOCAL_TOAST_TONE[toast.status ?? "info"],
225
286
  style: { pointerEvents: "auto" },
226
287
  onMouseEnter: () => hold(toast.id),
227
288
  onFocusCapture: () => hold(toast.id),
228
- onMouseLeave: () => scheduleDismiss(toast.id, toast.status),
229
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Flex, { align: "flex-start", gap: 3, children: [
230
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Stack, { space: 2, flex: 1, children: [
231
- toast.title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { size: 1, weight: "semibold", children: toast.title }),
232
- toast.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { size: 1, muted: true, children: toast.description })
289
+ onMouseLeave: (e) => resume(toast, e.currentTarget),
290
+ onBlurCapture: (e) => resume(toast, e.currentTarget),
291
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Flex, { align: "flex-start", gap: 3, children: [
292
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Stack, { space: 2, flex: 1, children: [
293
+ toast.title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, weight: "semibold", children: toast.title }),
294
+ toast.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, muted: true, children: toast.description })
233
295
  ] }),
234
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
296
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
235
297
  Button,
236
298
  {
237
299
  mode: "bleed",
@@ -251,50 +313,49 @@ function ToastViewport() {
251
313
  }
252
314
 
253
315
  // src/compat/tooltip.tsx
254
- var import_react4 = require("react");
255
- var import_jsx_runtime4 = require("react/jsx-runtime");
256
- var InstalledTooltip = resolveExport(UI, "Tooltip");
316
+ var import_react5 = require("react");
317
+ var import_jsx_runtime5 = require("react/jsx-runtime");
318
+ var InstalledTooltip = resolveComponent(UI, "Tooltip");
257
319
  function Tooltip({ text, children }) {
258
- const id = (0, import_react4.useId)();
259
- const [visible, setVisible] = (0, import_react4.useState)(false);
320
+ const id = (0, import_react5.useId)();
321
+ const [visible, setVisible] = (0, import_react5.useState)(false);
260
322
  if (InstalledTooltip) {
261
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(InstalledTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, children: text }) }), portal: true, children });
323
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InstalledTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { size: 1, children: text }) }), portal: true, children });
262
324
  }
263
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
325
+ const described = visible && (0, import_react5.isValidElement)(children) ? (0, import_react5.cloneElement)(children, { "aria-describedby": id }) : children;
326
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
264
327
  "span",
265
328
  {
266
329
  style: { position: "relative", display: "inline-flex" },
267
- "aria-describedby": id,
268
330
  onMouseEnter: () => setVisible(true),
269
331
  onMouseLeave: () => setVisible(false),
270
332
  onFocusCapture: () => setVisible(true),
271
333
  onBlurCapture: () => setVisible(false),
272
334
  onKeyDown: (e) => {
273
- if (e.key === "Escape") setVisible(false);
335
+ if (e.key === "Escape" && visible) {
336
+ e.stopPropagation();
337
+ setVisible(false);
338
+ }
274
339
  },
275
340
  children: [
276
- children,
277
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
278
- Card,
341
+ described,
342
+ visible && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
343
+ "span",
279
344
  {
280
345
  id,
281
346
  role: "tooltip",
282
- radius: 2,
283
- shadow: 2,
284
- padding: 2,
285
347
  style: {
286
348
  position: "absolute",
287
349
  bottom: "100%",
288
350
  left: "50%",
289
351
  transform: "translateX(-50%)",
290
- marginBottom: 4,
352
+ // No gap between trigger and tooltip, so the pointer can travel onto it
353
+ // without leaving the hover target (WCAG 1.4.13 Hoverable).
354
+ paddingBottom: 4,
291
355
  whiteSpace: "nowrap",
292
- pointerEvents: "none",
293
- zIndex: 1e3,
294
- // Kept in the accessibility tree when hidden so `aria-describedby` still resolves.
295
- visibility: visible ? "visible" : "hidden"
356
+ zIndex: 1e3
296
357
  },
297
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, children: text })
358
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Card, { radius: 2, shadow: 2, padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { size: 1, children: text }) })
298
359
  }
299
360
  )
300
361
  ]
@@ -303,56 +364,59 @@ function Tooltip({ text, children }) {
303
364
  }
304
365
 
305
366
  // src/compat/menu.tsx
306
- var import_react5 = require("react");
307
- var import_jsx_runtime5 = require("react/jsx-runtime");
308
- var InstalledMenuButton = resolveExport(UI, "MenuButton");
309
- var InstalledMenu = resolveExport(UI, "Menu");
310
- var InstalledMenuItem = resolveExport(UI, "MenuItem");
367
+ var import_react6 = require("react");
368
+ var import_jsx_runtime6 = require("react/jsx-runtime");
369
+ var InstalledMenuButton = resolveComponent(UI, "MenuButton");
370
+ var InstalledMenu = resolveComponent(UI, "Menu");
371
+ var InstalledMenuItem = resolveComponent(UI, "MenuItem");
311
372
  var INSTALLED_MENU = InstalledMenuButton && InstalledMenu && InstalledMenuItem ? { MenuButton: InstalledMenuButton, Menu: InstalledMenu, MenuItem: InstalledMenuItem } : null;
312
373
  var itemKey = (item, index) => item.key ?? `${index}-${item.text}`;
313
374
  function ActionMenu({ id, label, items, buttonIcon }) {
314
- const menuId = (0, import_react5.useId)();
315
- const [open, setOpen] = (0, import_react5.useState)(false);
316
- const [activeIndex, setActiveIndex] = (0, import_react5.useState)(0);
317
- const wrapRef = (0, import_react5.useRef)(null);
318
- const triggerRef = (0, import_react5.useRef)(null);
319
- const itemRefs = (0, import_react5.useRef)([]);
320
- const [frozenItems, setFrozenItems] = (0, import_react5.useState)(items);
375
+ const menuId = (0, import_react6.useId)();
376
+ const [open, setOpen] = (0, import_react6.useState)(false);
377
+ const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
378
+ const wrapRef = (0, import_react6.useRef)(null);
379
+ const triggerRef = (0, import_react6.useRef)(null);
380
+ const itemRefs = (0, import_react6.useRef)([]);
381
+ const [frozenItems, setFrozenItems] = (0, import_react6.useState)(items);
321
382
  const shownItems = open ? frozenItems : items;
322
- const close = (0, import_react5.useCallback)((returnFocus = true) => {
383
+ const close = (0, import_react6.useCallback)((returnFocus = true) => {
323
384
  setOpen(false);
324
385
  if (returnFocus) triggerRef.current?.focus();
325
386
  }, []);
326
- const openMenu = (0, import_react5.useCallback)((index) => {
387
+ const openMenu = (0, import_react6.useCallback)((index) => {
327
388
  setFrozenItems(items);
328
389
  setActiveIndex(index);
329
390
  setOpen(true);
330
391
  }, [items]);
331
- (0, import_react5.useEffect)(() => {
392
+ (0, import_react6.useEffect)(() => {
332
393
  if (!open || INSTALLED_MENU) return;
333
394
  itemRefs.current[activeIndex]?.focus();
334
395
  }, [open, activeIndex]);
335
- (0, import_react5.useEffect)(() => {
396
+ (0, import_react6.useEffect)(() => {
336
397
  if (INSTALLED_MENU || !open) return;
337
398
  const onPointerDown = (e) => {
338
- if (!wrapRef.current?.contains(e.target)) setOpen(false);
399
+ if (wrapRef.current?.contains(e.target)) return;
400
+ const hadFocus = wrapRef.current?.contains(document.activeElement);
401
+ setOpen(false);
402
+ if (hadFocus) triggerRef.current?.focus();
339
403
  };
340
404
  document.addEventListener("mousedown", onPointerDown);
341
405
  return () => document.removeEventListener("mousedown", onPointerDown);
342
406
  }, [open]);
343
- const runAction = (0, import_react5.useCallback)((item) => {
407
+ const runAction = (0, import_react6.useCallback)((item) => {
344
408
  close();
345
409
  item.onClick?.();
346
410
  }, [close]);
347
411
  if (INSTALLED_MENU) {
348
412
  const { MenuButton, Menu, MenuItem } = INSTALLED_MENU;
349
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
413
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
350
414
  MenuButton,
351
415
  {
352
416
  id,
353
- button: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { mode: "ghost", icon: buttonIcon, padding: 2, "aria-label": label }),
417
+ button: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { mode: "ghost", icon: buttonIcon, padding: 2, "aria-label": label }),
354
418
  popover: { placement: "bottom-end" },
355
- menu: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Menu, { children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
419
+ menu: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Menu, { children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
356
420
  MenuItem,
357
421
  {
358
422
  text: item.text,
@@ -373,7 +437,20 @@ function ActionMenu({ id, label, items, buttonIcon }) {
373
437
  return;
374
438
  }
375
439
  if (e.key === "Tab") {
376
- close(false);
440
+ triggerRef.current?.focus();
441
+ setOpen(false);
442
+ return;
443
+ }
444
+ if (e.key === "Enter" || e.key === " ") {
445
+ e.preventDefault();
446
+ const item = shownItems[activeIndex];
447
+ if (item) {
448
+ if (item.href) {
449
+ itemRefs.current[activeIndex]?.click();
450
+ } else {
451
+ runAction(item);
452
+ }
453
+ }
377
454
  return;
378
455
  }
379
456
  if (e.key === "ArrowDown") {
@@ -406,8 +483,8 @@ function ActionMenu({ id, label, items, buttonIcon }) {
406
483
  openMenu(items.length - 1);
407
484
  }
408
485
  };
409
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [
410
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
486
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
411
488
  Button,
412
489
  {
413
490
  ref: triggerRef,
@@ -423,7 +500,7 @@ function ActionMenu({ id, label, items, buttonIcon }) {
423
500
  onKeyDown: onTriggerKeyDown
424
501
  }
425
502
  ),
426
- open && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
503
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
427
504
  Card,
428
505
  {
429
506
  id: menuId,
@@ -434,13 +511,13 @@ function ActionMenu({ id, label, items, buttonIcon }) {
434
511
  "aria-labelledby": id,
435
512
  onKeyDown: onMenuKeyDown,
436
513
  style: { position: "absolute", top: "100%", right: 0, marginTop: 4, zIndex: 1e3, minWidth: 200 },
437
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Stack, { space: 1, role: "none", children: shownItems.map((item, i) => {
514
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stack, { space: 1, role: "none", children: shownItems.map((item, i) => {
438
515
  const Icon = item.icon;
439
- const row = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2, children: [
440
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { width: "1em", height: "1em", "aria-hidden": "true" }),
441
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { size: 1, children: item.text })
516
+ const row = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2, children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { width: "1em", height: "1em", "aria-hidden": "true" }),
518
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { size: 1, children: item.text })
442
519
  ] });
443
- const content = item.tone === "critical" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Card, { tone: "critical", radius: 1, children: row }) : row;
520
+ const content = item.tone === "critical" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { tone: "critical", radius: 1, children: row }) : row;
444
521
  const shared = {
445
522
  role: "menuitem",
446
523
  // Roving tabindex — the menu is one tab stop, arrows move within it.
@@ -448,7 +525,6 @@ function ActionMenu({ id, label, items, buttonIcon }) {
448
525
  ref: (el) => {
449
526
  itemRefs.current[i] = el;
450
527
  },
451
- onMouseEnter: () => setActiveIndex(i),
452
528
  style: {
453
529
  display: "block",
454
530
  width: "100%",
@@ -466,22 +542,24 @@ function ActionMenu({ id, label, items, buttonIcon }) {
466
542
  outlineOffset: -2
467
543
  }
468
544
  };
469
- return item.href ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
545
+ return item.href ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
470
546
  "a",
471
547
  {
472
548
  ...shared,
473
549
  href: item.href,
474
550
  target: "_blank",
475
551
  rel: "noreferrer",
476
- onClick: () => close(false),
552
+ onClick: () => {
553
+ setOpen(false);
554
+ triggerRef.current?.focus();
555
+ },
477
556
  children: content
478
557
  },
479
558
  itemKey(item, i)
480
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
481
- "button",
559
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
560
+ "div",
482
561
  {
483
562
  ...shared,
484
- type: "button",
485
563
  onClick: () => runAction(item),
486
564
  children: content
487
565
  },
@@ -494,8 +572,8 @@ function ActionMenu({ id, label, items, buttonIcon }) {
494
572
  }
495
573
 
496
574
  // src/compat/code.tsx
497
- var import_jsx_runtime6 = require("react/jsx-runtime");
498
- var InstalledCode = resolveExport(UI, "Code");
575
+ var import_jsx_runtime7 = require("react/jsx-runtime");
576
+ var InstalledCode = resolveComponent(UI, "Code");
499
577
  var FALLBACK_CODE_STYLE = {
500
578
  display: "block",
501
579
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
@@ -504,8 +582,8 @@ var FALLBACK_CODE_STYLE = {
504
582
  margin: 0
505
583
  };
506
584
  function Code({ style, children }) {
507
- if (InstalledCode) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InstalledCode, { size: 1, style, children });
508
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
585
+ if (InstalledCode) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InstalledCode, { size: 1, style, children });
586
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
509
587
  }
510
588
 
511
589
  // src/lib/api.ts
@@ -521,7 +599,7 @@ async function vercelFetch(path, token, init) {
521
599
  }
522
600
  });
523
601
  if (!res.ok) {
524
- 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." : "";
602
+ 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." : "";
525
603
  throw new Error(`Vercel API ${res.status}${hint}`);
526
604
  }
527
605
  return res.json();
@@ -560,15 +638,75 @@ async function getDeploymentEvents(opts) {
560
638
  const params = new URLSearchParams({ limit: "100", direction: "backward" });
561
639
  if (opts.teamId) params.set("teamId", opts.teamId);
562
640
  const raw = await vercelFetch(
563
- `/v2/deployments/${opts.deploymentId}/events?${params}`,
641
+ `/v2/deployments/${encodeURIComponent(opts.deploymentId)}/events?${params}`,
564
642
  opts.token
565
643
  );
566
644
  const events = Array.isArray(raw) ? raw : raw.events ?? [];
567
645
  return events.filter((e) => e.text?.trim());
568
646
  }
569
647
 
648
+ // src/lib/transport.ts
649
+ async function proxyFetch(url, statusKey, init) {
650
+ const res = await fetch(url, {
651
+ ...init,
652
+ headers: {
653
+ "Content-Type": "application/json",
654
+ ...statusKey ? { "x-deploy-status-key": statusKey } : {},
655
+ ...init?.headers
656
+ }
657
+ });
658
+ if (!res.ok) {
659
+ 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." : "";
660
+ throw new Error(`Deploy proxy ${res.status}${hint}`);
661
+ }
662
+ return res.json();
663
+ }
664
+ async function fetchDeployments(transport, target, limit) {
665
+ if (transport.mode === "direct") {
666
+ if (!target.projectId || !target.hookId) return [];
667
+ return listDeployments({
668
+ projectId: target.projectId,
669
+ hookId: target.hookId,
670
+ token: transport.token,
671
+ teamId: target.teamId,
672
+ limit
673
+ });
674
+ }
675
+ const params = new URLSearchParams({ key: target.proxyKey ?? "" });
676
+ if (limit) params.set("limit", String(limit));
677
+ const data = await proxyFetch(
678
+ `${transport.proxyUrl}/deployments?${params}`,
679
+ transport.statusKey
680
+ );
681
+ return data.deployments ?? [];
682
+ }
683
+ async function cancelDeploy(transport, target, deploymentId) {
684
+ if (transport.mode === "direct") {
685
+ return cancelDeployment({ deploymentId, token: transport.token, teamId: target.teamId });
686
+ }
687
+ await proxyFetch(`${transport.proxyUrl}/cancel`, transport.statusKey, {
688
+ method: "POST",
689
+ body: JSON.stringify({ key: target.proxyKey, deploymentId })
690
+ });
691
+ }
692
+ async function fetchDeploymentEvents(transport, target, deploymentId) {
693
+ if (transport.mode === "direct") {
694
+ return getDeploymentEvents({ deploymentId, token: transport.token, teamId: target.teamId });
695
+ }
696
+ const params = new URLSearchParams({ key: target.proxyKey ?? "", deploymentId });
697
+ const data = await proxyFetch(
698
+ `${transport.proxyUrl}/events?${params}`,
699
+ transport.statusKey
700
+ );
701
+ return data.events ?? [];
702
+ }
703
+
704
+ // src/components/DeployItem.tsx
705
+ var import_sanity = require("sanity");
706
+
570
707
  // src/lib/helpers.ts
571
708
  function parseHookUrl(url) {
709
+ if (!url) return { projectId: "", hookId: "" };
572
710
  try {
573
711
  const path = new URL(url).pathname;
574
712
  const parts = path.split("/").filter(Boolean);
@@ -658,29 +796,33 @@ function projectHref(inspectorUrl) {
658
796
  }
659
797
 
660
798
  // src/components/StatusBadge.tsx
661
- var import_jsx_runtime7 = require("react/jsx-runtime");
799
+ var import_jsx_runtime8 = require("react/jsx-runtime");
662
800
  function StatusBadge({ state, showSpinner }) {
663
801
  const { label, tone } = stateLabel(state);
664
802
  const spinning = showSpinner && (state === "QUEUED" || state === "INITIALIZING" || state === "BUILDING");
665
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Flex, { align: "center", gap: 2, children: [
666
- spinning && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Spinner, { muted: true }),
667
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Badge, { tone, padding: 2, children: label })
803
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Flex, { align: "center", gap: 2, children: [
804
+ spinning && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, { muted: true }),
805
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Badge, { tone, padding: 2, children: label })
668
806
  ] });
669
807
  }
670
808
 
671
809
  // src/components/DeployHistory.tsx
672
- var import_react6 = require("react");
673
- var import_jsx_runtime8 = require("react/jsx-runtime");
810
+ var import_react7 = require("react");
811
+ var import_jsx_runtime9 = require("react/jsx-runtime");
674
812
  function DeployHistory({ target, token, onClose }) {
675
- const [deployments, setDeployments] = (0, import_react6.useState)([]);
676
- const [loading, setLoading] = (0, import_react6.useState)(true);
677
- const [error, setError] = (0, import_react6.useState)(null);
813
+ const dialogId = (0, import_react7.useId)();
814
+ const pluginConfig = usePluginConfig();
815
+ const [deployments, setDeployments] = (0, import_react7.useState)([]);
816
+ const [loading, setLoading] = (0, import_react7.useState)(true);
817
+ const [error, setError] = (0, import_react7.useState)(null);
678
818
  const { projectId, hookId } = parseHookUrl(target.url);
679
- const load = (0, import_react6.useCallback)(async () => {
819
+ const transport = pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token };
820
+ const targetRef = { projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId };
821
+ const load = (0, import_react7.useCallback)(async () => {
680
822
  setLoading(true);
681
823
  setError(null);
682
824
  try {
683
- const data = await listDeployments({ projectId, hookId, token, teamId: target.teamId, limit: 10 });
825
+ const data = await fetchDeployments(transport, targetRef, 20);
684
826
  setDeployments(data);
685
827
  } catch (err) {
686
828
  setError(err instanceof Error ? err.message : "Failed to load history");
@@ -688,55 +830,55 @@ function DeployHistory({ target, token, onClose }) {
688
830
  setLoading(false);
689
831
  }
690
832
  }, [projectId, hookId, token, target.teamId]);
691
- (0, import_react6.useEffect)(() => {
833
+ (0, import_react7.useEffect)(() => {
692
834
  load();
693
835
  }, [load]);
694
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
836
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
695
837
  Dialog,
696
838
  {
697
839
  header: `${target.name} \u2014 Deployment History`,
698
- id: "deploy-history",
840
+ id: dialogId,
699
841
  onClose,
700
842
  width: 2,
701
- footer: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
702
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box, { padding: 4, children: [
703
- loading && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, { muted: true }) }),
704
- error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, children: error }) }),
705
- !loading && !error && deployments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
706
- !loading && deployments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 2, children: [
707
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Flex, { gap: 3, children: [
708
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
709
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
710
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
711
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
712
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { style: { width: 64 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
843
+ footer: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
844
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box, { padding: 4, children: [
845
+ loading && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true }) }),
846
+ error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: error }) }),
847
+ !loading && !error && deployments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
848
+ !loading && deployments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 3, children: [
850
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
851
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
853
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { style: { width: 64 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
713
855
  ] }) }),
714
856
  deployments.map((d) => {
715
857
  const { label, tone } = stateLabel(d.state);
716
858
  const branch = d.meta?.githubCommitRef ?? "\u2014";
717
859
  const sha = shortSha(d.meta?.githubCommitSha);
718
860
  const message = d.meta?.githubCommitMessage?.split("\n")[0] ?? "";
719
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Flex, { gap: 3, align: "center", children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: deploymentHref(d.url) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
861
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 3, align: "center", children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: deploymentHref(d.url) ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
721
863
  "a",
722
864
  {
723
865
  href: deploymentHref(d.url),
724
866
  target: "_blank",
725
867
  rel: "noreferrer",
726
868
  style: { color: "inherit" },
727
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
869
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
728
870
  }
729
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) }),
730
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Badge, { tone, children: label }) }),
731
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 1, children: [
732
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, children: branch }),
733
- sha && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { size: 0, muted: true, children: [
871
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { tone, children: label }) }),
873
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 1, children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: branch }),
875
+ sha && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 0, muted: true, children: [
734
876
  sha,
735
877
  message ? ` \xB7 ${message.slice(0, 40)}${message.length > 40 ? "\u2026" : ""}` : ""
736
878
  ] })
737
879
  ] }) }),
738
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
739
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
880
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
740
882
  Button,
741
883
  {
742
884
  as: "a",
@@ -749,7 +891,7 @@ function DeployHistory({ target, token, onClose }) {
749
891
  icon: LaunchIcon,
750
892
  style: { fontSize: "12px" }
751
893
  }
752
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) })
894
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) })
753
895
  ] }) }, d.uid);
754
896
  })
755
897
  ] })
@@ -759,41 +901,49 @@ function DeployHistory({ target, token, onClose }) {
759
901
  }
760
902
 
761
903
  // src/components/DeployItem.tsx
762
- var import_jsx_runtime9 = require("react/jsx-runtime");
904
+ var import_jsx_runtime10 = require("react/jsx-runtime");
763
905
  var POLL_INTERVAL_MS = 5e3;
764
906
  var LABEL_WIDTH = 64;
765
907
  var PENDING_TIMEOUT_MS = 6e4;
766
908
  function DeployItem({ target, token, onDelete, onEdit }) {
767
909
  const { projectId, hookId } = parseHookUrl(target.url);
768
910
  const toast = useToast();
769
- const [deployments, setDeployments] = (0, import_react7.useState)([]);
770
- const [loadingInitial, setLoadingInitial] = (0, import_react7.useState)(true);
771
- const [pendingSince, setPendingSince] = (0, import_react7.useState)(null);
772
- const [canceling, setCanceling] = (0, import_react7.useState)(false);
773
- const [deployError, setDeployError] = (0, import_react7.useState)(null);
774
- const [showHistory, setShowHistory] = (0, import_react7.useState)(false);
775
- const [elapsed, setElapsed] = (0, import_react7.useState)(0);
776
- const [copied, setCopied] = (0, import_react7.useState)(false);
777
- const [showDetails, setShowDetails] = (0, import_react7.useState)(false);
778
- const [showErrorLogs, setShowErrorLogs] = (0, import_react7.useState)(false);
779
- const [errorLines, setErrorLines] = (0, import_react7.useState)([]);
780
- const [loadingLogs, setLoadingLogs] = (0, import_react7.useState)(false);
781
- const [logError, setLogError] = (0, import_react7.useState)(null);
782
- const [pollError, setPollError] = (0, import_react7.useState)(null);
783
- const triggeredFromUidRef = (0, import_react7.useRef)(void 0);
784
- const requestSeqRef = (0, import_react7.useRef)(0);
785
- const mountedRef = (0, import_react7.useRef)(true);
786
- (0, import_react7.useEffect)(() => () => {
787
- mountedRef.current = false;
911
+ const pluginConfig = usePluginConfig();
912
+ const client = (0, import_sanity.useClient)({ apiVersion: "2025-01-01" });
913
+ const transport = pluginConfig.mode === "proxy" ? { mode: "proxy", proxyUrl: pluginConfig.proxyUrl ?? "", statusKey: pluginConfig.statusKey } : { mode: "direct", token };
914
+ const targetRef = { projectId, hookId, proxyKey: target.proxyKey, teamId: target.teamId };
915
+ const [deployments, setDeployments] = (0, import_react8.useState)([]);
916
+ const [loadingInitial, setLoadingInitial] = (0, import_react8.useState)(true);
917
+ const [pendingSince, setPendingSince] = (0, import_react8.useState)(null);
918
+ const [canceling, setCanceling] = (0, import_react8.useState)(false);
919
+ const [deployError, setDeployError] = (0, import_react8.useState)(null);
920
+ const [showHistory, setShowHistory] = (0, import_react8.useState)(false);
921
+ const [elapsed, setElapsed] = (0, import_react8.useState)(0);
922
+ const [copied, setCopied] = (0, import_react8.useState)(false);
923
+ const [showDetails, setShowDetails] = (0, import_react8.useState)(false);
924
+ const [showErrorLogs, setShowErrorLogs] = (0, import_react8.useState)(false);
925
+ const [errorLines, setErrorLines] = (0, import_react8.useState)([]);
926
+ const [loadingLogs, setLoadingLogs] = (0, import_react8.useState)(false);
927
+ const [logError, setLogError] = (0, import_react8.useState)(null);
928
+ const [pollError, setPollError] = (0, import_react8.useState)(null);
929
+ const triggeredFromUidRef = (0, import_react8.useRef)(void 0);
930
+ const requestSeqRef = (0, import_react8.useRef)(0);
931
+ const mountedRef = (0, import_react8.useRef)(true);
932
+ (0, import_react8.useEffect)(() => {
933
+ mountedRef.current = true;
934
+ return () => {
935
+ mountedRef.current = false;
936
+ };
788
937
  }, []);
789
938
  const latest = deployments[0];
790
939
  const isPending = pendingSince !== null;
791
940
  const isActive = isPending || isActiveState(latest?.state);
792
- const fetchDeployments = (0, import_react7.useCallback)(async () => {
793
- if (!projectId || !hookId || !token) return;
941
+ const fetchDeployments2 = (0, import_react8.useCallback)(async () => {
942
+ const ready = transport.mode === "proxy" ? Boolean(target.proxyKey && pluginConfig.proxyUrl) : Boolean(projectId && hookId && token);
943
+ if (!ready) return;
794
944
  const seq = ++requestSeqRef.current;
795
945
  try {
796
- const data = await listDeployments({ projectId, hookId, token, teamId: target.teamId });
946
+ const data = await fetchDeployments(transport, targetRef);
797
947
  if (seq !== requestSeqRef.current || !mountedRef.current) return;
798
948
  setDeployments(data);
799
949
  setPollError(null);
@@ -802,26 +952,26 @@ function DeployItem({ target, token, onDelete, onEdit }) {
802
952
  setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
803
953
  console.error("Deploy-vercel-from-sanity: fetch error", err);
804
954
  }
805
- }, [projectId, hookId, token, target.teamId]);
806
- (0, import_react7.useEffect)(() => {
807
- fetchDeployments().finally(() => setLoadingInitial(false));
808
- }, [fetchDeployments]);
809
- (0, import_react7.useEffect)(() => {
955
+ }, [projectId, hookId, token, target.teamId, target.proxyKey, transport.mode, pluginConfig.proxyUrl]);
956
+ (0, import_react8.useEffect)(() => {
957
+ fetchDeployments2().finally(() => setLoadingInitial(false));
958
+ }, [fetchDeployments2]);
959
+ (0, import_react8.useEffect)(() => {
810
960
  if (!isActive) return;
811
- const id = setInterval(fetchDeployments, POLL_INTERVAL_MS);
961
+ const id = setInterval(fetchDeployments2, POLL_INTERVAL_MS);
812
962
  return () => clearInterval(id);
813
- }, [isActive, fetchDeployments]);
814
- (0, import_react7.useEffect)(() => {
963
+ }, [isActive, fetchDeployments2]);
964
+ (0, import_react8.useEffect)(() => {
815
965
  if (!isPending) return;
816
966
  if (latest?.uid && latest.uid !== triggeredFromUidRef.current) setPendingSince(null);
817
967
  }, [isPending, latest?.uid]);
818
- (0, import_react7.useEffect)(() => {
968
+ (0, import_react8.useEffect)(() => {
819
969
  if (!isPending) return;
820
970
  const id = setTimeout(() => setPendingSince(null), PENDING_TIMEOUT_MS);
821
971
  return () => clearTimeout(id);
822
972
  }, [isPending]);
823
- const prevStateRef = (0, import_react7.useRef)(void 0);
824
- (0, import_react7.useEffect)(() => {
973
+ const prevStateRef = (0, import_react8.useRef)(void 0);
974
+ (0, import_react8.useEffect)(() => {
825
975
  const current = latest?.state;
826
976
  const prev = prevStateRef.current;
827
977
  if (prev && isActiveState(prev) && current && !isActiveState(current)) {
@@ -835,13 +985,13 @@ function DeployItem({ target, token, onDelete, onEdit }) {
835
985
  }
836
986
  prevStateRef.current = current;
837
987
  }, [latest?.state, target.name, toast]);
838
- (0, import_react7.useEffect)(() => {
988
+ (0, import_react8.useEffect)(() => {
839
989
  setShowErrorLogs(false);
840
990
  setErrorLines([]);
841
991
  setLogError(null);
842
992
  }, [latest?.uid]);
843
- const timerRef = (0, import_react7.useRef)(null);
844
- (0, import_react7.useEffect)(() => {
993
+ const timerRef = (0, import_react8.useRef)(null);
994
+ (0, import_react8.useEffect)(() => {
845
995
  if (isActive) {
846
996
  const start = pendingSince ?? latest?.created ?? Date.now();
847
997
  setElapsed(Math.floor((Date.now() - start) / 1e3));
@@ -856,7 +1006,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
856
1006
  if (timerRef.current) clearInterval(timerRef.current);
857
1007
  };
858
1008
  }, [isActive, pendingSince, latest?.created]);
859
- const deploy = (0, import_react7.useCallback)(() => {
1009
+ const deploy = (0, import_react8.useCallback)(() => {
860
1010
  (0, import_react_dom.flushSync)(() => {
861
1011
  setDeployError(null);
862
1012
  triggeredFromUidRef.current = latest?.uid;
@@ -864,27 +1014,37 @@ function DeployItem({ target, token, onDelete, onEdit }) {
864
1014
  });
865
1015
  void (async () => {
866
1016
  try {
867
- await triggerDeploy(target.url);
868
- setTimeout(fetchDeployments, 2e3);
1017
+ if (pluginConfig.mode === "proxy") {
1018
+ if (!target.proxyKey) throw new Error("Target has no proxy key \u2014 set one on the deploy target");
1019
+ await client.create({
1020
+ _type: "vercelDeploy.request",
1021
+ target: { _type: "reference", _ref: target._id },
1022
+ proxyKey: target.proxyKey,
1023
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString()
1024
+ });
1025
+ } else {
1026
+ await triggerDeploy(target.url ?? "");
1027
+ }
1028
+ setTimeout(fetchDeployments2, 2e3);
869
1029
  } catch (err) {
870
1030
  setPendingSince(null);
871
1031
  setDeployError(err instanceof Error ? err.message : "Deploy failed");
872
1032
  }
873
1033
  })();
874
- }, [target.url, fetchDeployments, latest?.uid]);
875
- const cancel = (0, import_react7.useCallback)(async () => {
1034
+ }, [target.url, target.proxyKey, target._id, pluginConfig.mode, client, fetchDeployments2, latest?.uid]);
1035
+ const cancel = (0, import_react8.useCallback)(async () => {
876
1036
  if (!latest?.uid) return;
877
1037
  setCanceling(true);
878
1038
  try {
879
- await cancelDeployment({ deploymentId: latest.uid, token, teamId: target.teamId });
880
- await fetchDeployments();
1039
+ await cancelDeploy(transport, targetRef, latest.uid);
1040
+ await fetchDeployments2();
881
1041
  } catch (err) {
882
1042
  console.error("deploy-vercel-from-sanity: cancel error", err);
883
1043
  } finally {
884
1044
  setCanceling(false);
885
1045
  }
886
- }, [latest?.uid, token, target.teamId, fetchDeployments]);
887
- const copyUrl = (0, import_react7.useCallback)(() => {
1046
+ }, [latest?.uid, token, target.teamId, fetchDeployments2]);
1047
+ const copyUrl = (0, import_react8.useCallback)(() => {
888
1048
  if (!latest?.url) return;
889
1049
  const fullUrl = deploymentHref(latest.url) ?? "";
890
1050
  navigator.clipboard.writeText(fullUrl).then(() => {
@@ -894,16 +1054,12 @@ function DeployItem({ target, token, onDelete, onEdit }) {
894
1054
  window.prompt("Copy deployment URL:", fullUrl);
895
1055
  });
896
1056
  }, [latest?.url]);
897
- const fetchErrorLogs = (0, import_react7.useCallback)(async () => {
1057
+ const fetchErrorLogs = (0, import_react8.useCallback)(async () => {
898
1058
  if (!latest?.uid) return;
899
1059
  setLoadingLogs(true);
900
1060
  setLogError(null);
901
1061
  try {
902
- const events = await getDeploymentEvents({
903
- deploymentId: latest.uid,
904
- token,
905
- teamId: target.teamId
906
- });
1062
+ const events = await fetchDeploymentEvents(transport, targetRef, latest.uid);
907
1063
  const lines = events.filter((e) => e.type === "stderr" || e.type === "stdout").map((e) => e.text ?? "").filter(Boolean).reverse().slice(-30);
908
1064
  setErrorLines(lines.length > 0 ? lines : ["No stderr or stdout was captured for this build. Open the full build log in Vercel for details."]);
909
1065
  } catch (err) {
@@ -912,7 +1068,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
912
1068
  setLoadingLogs(false);
913
1069
  }
914
1070
  }, [latest?.uid, token, target.teamId]);
915
- const toggleErrorLogs = (0, import_react7.useCallback)(() => {
1071
+ const toggleErrorLogs = (0, import_react8.useCallback)(() => {
916
1072
  if (!showErrorLogs && errorLines.length === 0 && !logError) fetchErrorLogs();
917
1073
  setShowErrorLogs((v) => !v);
918
1074
  }, [showErrorLogs, errorLines.length, logError, fetchErrorLogs]);
@@ -925,20 +1081,20 @@ function DeployItem({ target, token, onDelete, onEdit }) {
925
1081
  const deployedAt = latest?.created ? timeAgo(latest.created) : null;
926
1082
  const vercelProjectUrl = projectHref(latest?.inspectorUrl);
927
1083
  const isError = latest?.state === "ERROR";
928
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
929
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "stretch", className: "dvfs-card-flex", children: [
930
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
931
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 3, padding: 3, style: { flex: 1 }, children: [
932
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", justify: "space-between", gap: 2, children: [
933
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
934
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
935
- branch && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1084
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1085
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "stretch", className: "dvfs-card-flex", children: [
1086
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
1087
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 3, padding: 3, style: { flex: 1 }, children: [
1088
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", justify: "space-between", gap: 2, children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
1090
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
1091
+ branch && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 1, children: [
936
1092
  branch,
937
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) })
1093
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("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" }) })
938
1094
  ] }) }),
939
- token && !loadingInitial && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
940
- isPending ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatusBadge, { state: latest?.state }),
941
- isActiveState(latest?.state) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1095
+ token && !loadingInitial && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1096
+ isPending ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StatusBadge, { state: latest?.state }),
1097
+ isActiveState(latest?.state) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
942
1098
  Button,
943
1099
  {
944
1100
  text: "Cancel",
@@ -952,14 +1108,14 @@ function DeployItem({ target, token, onDelete, onEdit }) {
952
1108
  ),
953
1109
  isPending ? (
954
1110
  /* Optimistic — dimmed spinner only; no elapsed time until a real build exists */
955
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
956
- ) : isActive ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
957
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true, style: { marginLeft: 4 } }),
958
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: formatDuration(elapsed) })
959
- ] }) : deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: deployedAt }) : null
1111
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
1112
+ ) : isActive ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 1, children: [
1113
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { muted: true, style: { marginLeft: 4 } }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, children: formatDuration(elapsed) })
1115
+ ] }) : deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, children: deployedAt }) : null
960
1116
  ] })
961
1117
  ] }),
962
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1118
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
963
1119
  ActionMenu,
964
1120
  {
965
1121
  id: `menu-${target._id}`,
@@ -975,55 +1131,74 @@ function DeployItem({ target, token, onDelete, onEdit }) {
975
1131
  }
976
1132
  )
977
1133
  ] }),
978
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
979
- !token ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
980
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true }),
981
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Loading\u2026" })
982
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
983
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
984
- latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1134
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
1135
+ !token ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(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__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { muted: true }),
1137
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, children: "Loading\u2026" })
1138
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
1140
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
985
1141
  "a",
986
1142
  {
987
1143
  href: deploymentHref(latest.url),
988
1144
  target: "_blank",
989
1145
  rel: "noreferrer",
990
1146
  style: { color: "inherit" },
991
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: latest.url }) })
1147
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: latest.url }) })
992
1148
  }
993
1149
  ) }),
994
- sha && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1150
+ sha && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
995
1151
  "a",
996
1152
  {
997
1153
  href: commitHref,
998
1154
  target: "_blank",
999
1155
  rel: "noreferrer",
1000
1156
  style: { color: "inherit", textDecoration: "none" },
1001
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
1157
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
1002
1158
  }
1003
- ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
1004
- creator && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, muted: true, children: [
1159
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
1160
+ creator && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 1, muted: true, children: [
1005
1161
  "by ",
1006
1162
  creator
1007
1163
  ] }),
1008
- latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, muted: true, children: [
1164
+ latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 1, muted: true, children: [
1009
1165
  "Took ",
1010
1166
  formatDuration(Math.floor((latest.ready - latest.created) / 1e3)),
1011
1167
  " to build"
1012
1168
  ] }),
1013
- latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tooltip, { text: copied ? "Copied!" : "Copy URL", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1014
- Button,
1015
- {
1016
- mode: "ghost",
1017
- icon: copied ? CheckmarkIcon : CopyIcon,
1018
- padding: 1,
1019
- tone: copied ? "positive" : "default",
1020
- onClick: copyUrl,
1021
- style: { cursor: "pointer" }
1022
- }
1023
- ) }) })
1169
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1170
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Tooltip, { text: copied ? "Copied" : "Copy deployment URL", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1171
+ Button,
1172
+ {
1173
+ mode: "ghost",
1174
+ icon: copied ? CheckmarkIcon : CopyIcon,
1175
+ padding: 1,
1176
+ tone: copied ? "positive" : "default",
1177
+ onClick: copyUrl,
1178
+ "aria-label": copied ? "Deployment URL copied" : "Copy deployment URL",
1179
+ style: { cursor: "pointer" }
1180
+ }
1181
+ ) }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1183
+ "span",
1184
+ {
1185
+ role: "status",
1186
+ "aria-live": "polite",
1187
+ style: {
1188
+ position: "absolute",
1189
+ width: 1,
1190
+ height: 1,
1191
+ overflow: "hidden",
1192
+ clip: "rect(0 0 0 0)",
1193
+ whiteSpace: "nowrap"
1194
+ },
1195
+ children: copied ? "Deployment URL copied to clipboard" : ""
1196
+ }
1197
+ )
1198
+ ] })
1024
1199
  ] }),
1025
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
1026
- commitMsg && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1200
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
1201
+ commitMsg && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1027
1202
  Text,
1028
1203
  {
1029
1204
  size: 0,
@@ -1032,8 +1207,8 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1032
1207
  children: commitMsg
1033
1208
  }
1034
1209
  ),
1035
- isError && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1036
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1210
+ isError && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1211
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1037
1212
  Button,
1038
1213
  {
1039
1214
  "aria-expanded": showErrorLogs,
@@ -1047,40 +1222,40 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1047
1222
  style: { alignSelf: "flex-start", cursor: "pointer" }
1048
1223
  }
1049
1224
  ),
1050
- showErrorLogs && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Card, { tone: "critical", radius: 2, padding: 3, children: [
1051
- loadingLogs && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
1052
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true }),
1053
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Loading logs\u2026" })
1225
+ showErrorLogs && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Card, { tone: "critical", radius: 2, padding: 3, children: [
1226
+ loadingLogs && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, children: [
1227
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { muted: true }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, children: "Loading logs\u2026" })
1054
1229
  ] }),
1055
- logError && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1056
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: logError }),
1057
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, {}),
1059
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
1230
+ logError && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: logError }),
1232
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 1, children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LaunchIcon, {}),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
1060
1235
  ] }) })
1061
1236
  ] }),
1062
- !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1063
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
1064
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1065
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, {}),
1066
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
1237
+ !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
1239
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 1, children: [
1240
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LaunchIcon, {}),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
1067
1242
  ] }) })
1068
1243
  ] })
1069
1244
  ] })
1070
1245
  ] }),
1071
- deployError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: deployError }) })
1246
+ deployError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: deployError }) })
1072
1247
  ] })
1073
1248
  ] }),
1074
- pollError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "caution", padding: 3, radius: 2, role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
1075
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(WarningOutlineIcon, { "aria-hidden": "true" }),
1076
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, children: [
1249
+ pollError && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { tone: "caution", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 2, children: [
1250
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WarningOutlineIcon, { "aria-hidden": "true" }),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 1, children: [
1077
1252
  "Status updates paused \u2014 ",
1078
1253
  pollError
1079
1254
  ] })
1080
1255
  ] }) })
1081
1256
  ] }),
1082
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box, { children: [
1083
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1257
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box, { children: [
1258
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1084
1259
  Button,
1085
1260
  {
1086
1261
  mode: "ghost",
@@ -1093,61 +1268,61 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1093
1268
  style: { width: "100%", justifyContent: "flex-start", borderRadius: 0, cursor: "pointer" }
1094
1269
  }
1095
1270
  ),
1096
- showDetails && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1097
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1098
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
1099
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
1271
+ showDetails && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1273
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
1100
1275
  ] }),
1101
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1102
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
1103
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
1276
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
1278
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
1104
1279
  ] }),
1105
- target.teamId && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
1107
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
1280
+ target.teamId && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1281
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
1108
1283
  ] }),
1109
- fullSha && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1110
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
1111
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
1284
+ fullSha && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
1112
1287
  ] }),
1113
- latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1114
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
1288
+ latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1289
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
1290
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
1116
1291
  ] }),
1117
- branch && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1118
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
1292
+ branch && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
1120
1295
  ] }),
1121
- latest?.uid && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1122
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
1123
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
1296
+ latest?.uid && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1297
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
1124
1299
  ] }),
1125
- latest?.url && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1126
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
1300
+ latest?.url && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1301
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
1128
1303
  ] }),
1129
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1130
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
1131
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1132
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
1133
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, { style: { width: 10, height: 10 } })
1304
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1305
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { align: "center", gap: 1, children: [
1307
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LaunchIcon, { style: { width: 10, height: 10 } })
1134
1309
  ] }) })
1135
1310
  ] }),
1136
- latest?.created && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1137
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
1138
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
1311
+ latest?.created && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { gap: 2, align: "center", children: [
1312
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
1139
1314
  ] })
1140
1315
  ] }) })
1141
1316
  ] })
1142
1317
  ] }),
1143
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1318
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1144
1319
  Flex,
1145
1320
  {
1146
1321
  direction: "column",
1147
1322
  gap: 2,
1148
1323
  className: "dvfs-deploy-col",
1149
1324
  style: { flexShrink: 0, alignSelf: "stretch" },
1150
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1325
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1151
1326
  Button,
1152
1327
  {
1153
1328
  text: "Deploy",
@@ -1167,7 +1342,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1167
1342
  }
1168
1343
  )
1169
1344
  ] }) }),
1170
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1345
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1171
1346
  DeployHistory,
1172
1347
  {
1173
1348
  target,
@@ -1179,17 +1354,19 @@ function DeployItem({ target, token, onDelete, onEdit }) {
1179
1354
  }
1180
1355
 
1181
1356
  // src/components/TokenSetup.tsx
1182
- var import_react8 = require("react");
1183
- var import_sanity = require("sanity");
1184
- var import_jsx_runtime10 = require("react/jsx-runtime");
1357
+ var import_react9 = require("react");
1358
+ var import_sanity2 = require("sanity");
1359
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1185
1360
  var TOKEN_DOC_ID = "config.vercelDeploy";
1186
- function TokenSetup({ onSaved, onCancel }) {
1187
- const tokenId = (0, import_react8.useId)();
1188
- const client = (0, import_sanity.useClient)({ apiVersion: "2025-01-01" });
1189
- const [token, setToken] = (0, import_react8.useState)("");
1190
- const [saving, setSaving] = (0, import_react8.useState)(false);
1191
- const [error, setError] = (0, import_react8.useState)(null);
1192
- const save = (0, import_react8.useCallback)(async () => {
1361
+ function TokenSetup({ onSaved, onCancel, hasToken = false }) {
1362
+ const dialogId = (0, import_react9.useId)();
1363
+ const tokenId = (0, import_react9.useId)();
1364
+ const client = (0, import_sanity2.useClient)({ apiVersion: "2025-01-01" });
1365
+ const [token, setToken] = (0, import_react9.useState)("");
1366
+ const [saving, setSaving] = (0, import_react9.useState)(false);
1367
+ const [error, setError] = (0, import_react9.useState)(null);
1368
+ const [revoking, setRevoking] = (0, import_react9.useState)(false);
1369
+ const save = (0, import_react9.useCallback)(async () => {
1193
1370
  if (!token.trim()) return;
1194
1371
  setSaving(true);
1195
1372
  setError(null);
@@ -1206,43 +1383,71 @@ function TokenSetup({ onSaved, onCancel }) {
1206
1383
  setSaving(false);
1207
1384
  }
1208
1385
  }, [client, token, onSaved]);
1209
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1386
+ const revoke = (0, import_react9.useCallback)(async () => {
1387
+ setRevoking(true);
1388
+ setError(null);
1389
+ try {
1390
+ await client.delete(TOKEN_DOC_ID);
1391
+ onSaved();
1392
+ } catch (err) {
1393
+ setError(err instanceof Error ? err.message : "Failed to remove token");
1394
+ } finally {
1395
+ setRevoking(false);
1396
+ }
1397
+ }, [client, onSaved]);
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1210
1399
  Dialog,
1211
1400
  {
1212
1401
  header: "Connect Vercel API token",
1213
- id: "token-setup",
1402
+ id: dialogId,
1214
1403
  onClose: onCancel,
1215
1404
  width: 1,
1216
- footer: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1217
- onCancel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
1218
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1405
+ footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { padding: 3, gap: 2, justify: hasToken ? "space-between" : "flex-end", children: [
1406
+ hasToken && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1219
1407
  Button,
1220
1408
  {
1221
- text: "Save and connect",
1222
- tone: "primary",
1223
- icon: CheckmarkCircleIcon,
1224
- loading: saving,
1225
- disabled: !token.trim() || saving,
1226
- onClick: save,
1409
+ text: "Remove token",
1410
+ mode: "ghost",
1411
+ tone: "critical",
1412
+ icon: TrashIcon,
1413
+ loading: revoking,
1414
+ disabled: revoking || saving,
1415
+ onClick: revoke,
1227
1416
  style: { cursor: "pointer" }
1228
1417
  }
1229
- )
1418
+ ),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { gap: 2, children: [
1420
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1422
+ Button,
1423
+ {
1424
+ text: "Save and connect",
1425
+ tone: "primary",
1426
+ icon: CheckmarkCircleIcon,
1427
+ loading: saving,
1428
+ disabled: !token.trim() || saving,
1429
+ onClick: save,
1430
+ style: { cursor: "pointer" }
1431
+ }
1432
+ )
1433
+ ] })
1230
1434
  ] }),
1231
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 4, padding: 4, children: [
1232
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 3, children: [
1233
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(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." }),
1234
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 1, muted: true, children: [
1435
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 4, padding: 4, children: [
1436
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 3, children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(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." }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { size: 1, muted: true, children: [
1235
1439
  "Create one at ",
1236
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
1237
1441
  " with",
1238
1442
  " ",
1239
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "Full Account" }),
1240
- " scope. The token is stored in your Sanity dataset and shared across all authenticated studio users."
1443
+ "scope limited to the ",
1444
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: "team" }),
1445
+ " 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."
1241
1446
  ] })
1242
1447
  ] }),
1243
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { as: "label", size: 1, htmlFor: tokenId, children: "Vercel API Token" }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1448
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { as: "label", size: 1, htmlFor: tokenId, children: "Vercel API Token" }),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1246
1451
  TextInput,
1247
1452
  {
1248
1453
  id: tokenId,
@@ -1253,36 +1458,41 @@ function TokenSetup({ onSaved, onCancel }) {
1253
1458
  }
1254
1459
  )
1255
1460
  ] }),
1256
- error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { tone: "critical", padding: 3, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: error }) })
1461
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Card, { tone: "critical", padding: 3, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { size: 1, children: error }) })
1257
1462
  ] })
1258
1463
  }
1259
1464
  );
1260
1465
  }
1261
1466
 
1262
1467
  // src/components/DeployTargetForm.tsx
1263
- var import_react9 = require("react");
1264
- var import_sanity2 = require("sanity");
1265
- var import_jsx_runtime11 = require("react/jsx-runtime");
1468
+ var import_react10 = require("react");
1469
+ var import_sanity3 = require("sanity");
1470
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1266
1471
  var VERCEL_HOOK_RE2 = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//;
1267
1472
  function DeployTargetForm({ initial, onSaved, onClose }) {
1268
- const client = (0, import_sanity2.useClient)({ apiVersion: "2025-01-01" });
1473
+ const client = (0, import_sanity3.useClient)({ apiVersion: "2025-01-01" });
1269
1474
  const isEdit = Boolean(initial);
1270
- const nameId = (0, import_react9.useId)();
1271
- const urlId = (0, import_react9.useId)();
1272
- const urlErrorId = (0, import_react9.useId)();
1273
- const urlHelpId = (0, import_react9.useId)();
1274
- const teamId_ = (0, import_react9.useId)();
1275
- const teamHelpId = (0, import_react9.useId)();
1276
- const disableId = (0, import_react9.useId)();
1277
- const [name, setName] = (0, import_react9.useState)(initial?.name ?? "");
1278
- const [url, setUrl] = (0, import_react9.useState)(initial?.url ?? "");
1279
- const [teamId, setTeamId] = (0, import_react9.useState)(initial?.teamId ?? "");
1280
- const [disableDelete, setDisableDelete] = (0, import_react9.useState)(initial?.disableDeleteAction ?? false);
1281
- const [saving, setSaving] = (0, import_react9.useState)(false);
1282
- const [error, setError] = (0, import_react9.useState)(null);
1475
+ const pluginConfig = usePluginConfig();
1476
+ const proxyKeyId = (0, import_react10.useId)();
1477
+ const dialogId = (0, import_react10.useId)();
1478
+ const nameId = (0, import_react10.useId)();
1479
+ const urlId = (0, import_react10.useId)();
1480
+ const urlErrorId = (0, import_react10.useId)();
1481
+ const urlHelpId = (0, import_react10.useId)();
1482
+ const teamId_ = (0, import_react10.useId)();
1483
+ const teamHelpId = (0, import_react10.useId)();
1484
+ const disableId = (0, import_react10.useId)();
1485
+ const [name, setName] = (0, import_react10.useState)(initial?.name ?? "");
1486
+ const [url, setUrl] = (0, import_react10.useState)(initial?.url ?? "");
1487
+ const [teamId, setTeamId] = (0, import_react10.useState)(initial?.teamId ?? "");
1488
+ const [proxyKey, setProxyKey] = (0, import_react10.useState)(initial?.proxyKey ?? "");
1489
+ const [disableDelete, setDisableDelete] = (0, import_react10.useState)(initial?.disableDeleteAction ?? false);
1490
+ const [saving, setSaving] = (0, import_react10.useState)(false);
1491
+ const [error, setError] = (0, import_react10.useState)(null);
1283
1492
  const urlValid = !url || VERCEL_HOOK_RE2.test(url.trim());
1284
- const canSave = name.trim() && url.trim() && urlValid;
1285
- const save = (0, import_react9.useCallback)(async () => {
1493
+ const isProxy = pluginConfig.mode === "proxy";
1494
+ const canSave = Boolean(name.trim()) && urlValid && (isProxy ? Boolean(proxyKey.trim()) : Boolean(url.trim()));
1495
+ const save = (0, import_react10.useCallback)(async () => {
1286
1496
  if (!canSave) return;
1287
1497
  setSaving(true);
1288
1498
  setError(null);
@@ -1290,6 +1500,7 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1290
1500
  name: name.trim(),
1291
1501
  url: url.trim(),
1292
1502
  teamId: teamId.trim() || null,
1503
+ proxyKey: proxyKey.trim() || null,
1293
1504
  disableDeleteAction: disableDelete
1294
1505
  };
1295
1506
  try {
@@ -1304,17 +1515,17 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1304
1515
  } finally {
1305
1516
  setSaving(false);
1306
1517
  }
1307
- }, [canSave, isEdit, initial, client, name, url, teamId, disableDelete, onSaved]);
1308
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1518
+ }, [canSave, isEdit, initial, client, name, url, teamId, proxyKey, disableDelete, onSaved]);
1519
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1309
1520
  Dialog,
1310
1521
  {
1311
1522
  header: isEdit ? `Edit "${initial?.name}"` : "Add deploy target",
1312
- id: "deploy-target-form",
1523
+ id: dialogId,
1313
1524
  onClose,
1314
1525
  width: 1,
1315
- footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1316
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1317
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1526
+ footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1527
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1528
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1318
1529
  Button,
1319
1530
  {
1320
1531
  text: isEdit ? "Save changes" : "Add target",
@@ -1327,13 +1538,13 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1327
1538
  }
1328
1539
  )
1329
1540
  ] }),
1330
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 4, children: [
1331
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1332
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: nameId, children: [
1541
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 4, children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Label, { as: "label", size: 1, htmlFor: nameId, children: [
1333
1544
  "Name ",
1334
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: "*" })
1545
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": "true", children: "*" })
1335
1546
  ] }),
1336
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1547
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1337
1548
  TextInput,
1338
1549
  {
1339
1550
  id: nameId,
@@ -1345,12 +1556,29 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1345
1556
  }
1346
1557
  )
1347
1558
  ] }),
1348
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1349
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: urlId, children: [
1559
+ isProxy ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Label, { as: "label", size: 1, htmlFor: proxyKeyId, children: [
1561
+ "Proxy Key ",
1562
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": "true", children: "*" })
1563
+ ] }),
1564
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1565
+ TextInput,
1566
+ {
1567
+ id: proxyKeyId,
1568
+ required: true,
1569
+ "aria-required": "true",
1570
+ value: proxyKey,
1571
+ onChange: (e) => setProxyKey(e.target.value),
1572
+ placeholder: "production"
1573
+ }
1574
+ ),
1575
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 0, muted: true, children: "Matches a key configured on your deploy proxy, which holds the real hook URL. Contains no secret." })
1576
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1577
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Label, { as: "label", size: 1, htmlFor: urlId, children: [
1350
1578
  "Deploy hook URL ",
1351
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: "*" })
1579
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": "true", children: "*" })
1352
1580
  ] }),
1353
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1581
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1354
1582
  TextInput,
1355
1583
  {
1356
1584
  id: urlId,
@@ -1363,15 +1591,15 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1363
1591
  placeholder: "https://api.vercel.com/v1/integrations/deploy/\u2026"
1364
1592
  }
1365
1593
  ),
1366
- url && !urlValid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Card, { tone: "critical", padding: 2, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { id: urlErrorId, size: 1, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }) }),
1367
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { id: urlHelpId, size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1594
+ url && !urlValid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { tone: "critical", padding: 2, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { id: urlErrorId, size: 1, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }) }),
1595
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { id: urlHelpId, size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1368
1596
  ] }),
1369
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1370
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: teamId_, children: [
1597
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Label, { as: "label", size: 1, htmlFor: teamId_, children: [
1371
1599
  "Team ID ",
1372
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1600
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1373
1601
  ] }),
1374
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1602
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1375
1603
  TextInput,
1376
1604
  {
1377
1605
  id: teamId_,
@@ -1381,14 +1609,10 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1381
1609
  placeholder: "team_xxxxxxxx"
1382
1610
  }
1383
1611
  ),
1384
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { id: teamHelpId, size: 0, muted: true, children: [
1385
- "Required for team-owned Vercel projects. Find it at Vercel \u2192 Settings \u2192 General \u2192 Team ID (starts with ",
1386
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("code", { children: "team_" }),
1387
- ")."
1388
- ] })
1612
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(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_)." })
1389
1613
  ] }),
1390
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { align: "center", gap: 3, children: [
1391
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1614
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", gap: 3, children: [
1615
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1392
1616
  Switch,
1393
1617
  {
1394
1618
  checked: disableDelete,
@@ -1396,36 +1620,38 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1396
1620
  id: disableId
1397
1621
  }
1398
1622
  ),
1399
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 1, children: [
1400
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { as: "label", size: 1, htmlFor: disableId, children: "Disable delete action" }),
1401
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1623
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 1, children: [
1624
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Label, { as: "label", size: 1, htmlFor: disableId, children: "Disable delete action" }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1402
1626
  ] })
1403
1627
  ] }),
1404
- error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { size: 1, children: error }) })
1628
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, children: error }) })
1405
1629
  ] }) })
1406
1630
  }
1407
1631
  );
1408
1632
  }
1409
1633
 
1410
1634
  // src/version.ts
1411
- var VERSION = "1.2.0";
1635
+ var VERSION = "1.3.0";
1412
1636
 
1413
1637
  // src/components/DeployTool.tsx
1414
- var import_jsx_runtime12 = require("react/jsx-runtime");
1638
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1415
1639
  var TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`;
1416
1640
  var TARGETS_QUERY = `*[_type == "vercel_deploy" && !(_id in path("drafts.**"))] | order(_createdAt asc)`;
1417
1641
  function DeployTool() {
1418
- const client = (0, import_sanity3.useClient)({ apiVersion: "2025-01-01" });
1642
+ const client = (0, import_sanity4.useClient)({ apiVersion: "2025-01-01" });
1643
+ const pluginConfig = usePluginConfig();
1644
+ const usesToken = pluginConfig.mode === "direct";
1419
1645
  const toast = useToast();
1420
- const [token, setToken] = (0, import_react10.useState)(null);
1421
- const [targets, setTargets] = (0, import_react10.useState)([]);
1422
- const [loading, setLoading] = (0, import_react10.useState)(true);
1423
- const [showTokenSetup, setShowTokenSetup] = (0, import_react10.useState)(false);
1424
- const [showCreateForm, setShowCreateForm] = (0, import_react10.useState)(false);
1425
- const [pendingEdit, setPendingEdit] = (0, import_react10.useState)(null);
1426
- const [pendingDelete, setPendingDelete] = (0, import_react10.useState)(null);
1427
- const [deleting, setDeleting] = (0, import_react10.useState)(false);
1428
- const load = (0, import_react10.useCallback)(async () => {
1646
+ const [token, setToken] = (0, import_react11.useState)(null);
1647
+ const [targets, setTargets] = (0, import_react11.useState)([]);
1648
+ const [loading, setLoading] = (0, import_react11.useState)(true);
1649
+ const [showTokenSetup, setShowTokenSetup] = (0, import_react11.useState)(false);
1650
+ const [showCreateForm, setShowCreateForm] = (0, import_react11.useState)(false);
1651
+ const [pendingEdit, setPendingEdit] = (0, import_react11.useState)(null);
1652
+ const [pendingDelete, setPendingDelete] = (0, import_react11.useState)(null);
1653
+ const [deleting, setDeleting] = (0, import_react11.useState)(false);
1654
+ const load = (0, import_react11.useCallback)(async () => {
1429
1655
  setLoading(true);
1430
1656
  try {
1431
1657
  const [fetchedToken, fetchedTargets] = await Promise.all([
@@ -1440,10 +1666,10 @@ function DeployTool() {
1440
1666
  setLoading(false);
1441
1667
  }
1442
1668
  }, [client]);
1443
- (0, import_react10.useEffect)(() => {
1669
+ (0, import_react11.useEffect)(() => {
1444
1670
  load();
1445
1671
  }, [load]);
1446
- (0, import_react10.useEffect)(() => {
1672
+ (0, import_react11.useEffect)(() => {
1447
1673
  const existing = document.getElementById("dvfs-styles");
1448
1674
  if (existing) {
1449
1675
  const n = Number(existing.dataset.refs ?? "1") + 1;
@@ -1481,7 +1707,7 @@ function DeployTool() {
1481
1707
  if (left <= 0) style.remove();
1482
1708
  };
1483
1709
  }, []);
1484
- (0, import_react10.useEffect)(() => {
1710
+ (0, import_react11.useEffect)(() => {
1485
1711
  const sub = client.listen(TARGETS_QUERY).subscribe(() => {
1486
1712
  client.fetch(TARGETS_QUERY).then(setTargets).catch((err) => {
1487
1713
  console.error("deploy-vercel-from-sanity: subscription refresh error", err);
@@ -1489,7 +1715,7 @@ function DeployTool() {
1489
1715
  });
1490
1716
  return () => sub.unsubscribe();
1491
1717
  }, [client]);
1492
- const confirmDelete = (0, import_react10.useCallback)(async () => {
1718
+ const confirmDelete = (0, import_react11.useCallback)(async () => {
1493
1719
  if (!pendingDelete) return;
1494
1720
  setDeleting(true);
1495
1721
  try {
@@ -1506,14 +1732,14 @@ function DeployTool() {
1506
1732
  }
1507
1733
  }, [client, pendingDelete, toast]);
1508
1734
  if (loading) {
1509
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { muted: true }) }) });
1735
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner, { muted: true }) }) });
1510
1736
  }
1511
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Card, { height: "fill", tone: "transparent", children: [
1512
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box, { padding: 5, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 5, children: [
1513
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1514
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Heading, { size: 2, children: "Deploy with Vercel" }),
1515
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1516
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1737
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Card, { height: "fill", tone: "transparent", children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box, { padding: 5, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Stack, { space: 5, children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Flex, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1740
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Heading, { size: 2, children: "Deploy with Vercel" }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Flex, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1742
+ usesToken && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1517
1743
  Button,
1518
1744
  {
1519
1745
  text: token ? "Token connected" : "Connect API token",
@@ -1525,7 +1751,7 @@ function DeployTool() {
1525
1751
  style: { cursor: "pointer" }
1526
1752
  }
1527
1753
  ),
1528
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1754
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1529
1755
  Button,
1530
1756
  {
1531
1757
  text: "Add target",
@@ -1538,12 +1764,12 @@ function DeployTool() {
1538
1764
  )
1539
1765
  ] })
1540
1766
  ] }),
1541
- !token && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", justify: "space-between", gap: 4, children: [
1542
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1543
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1544
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(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." })
1767
+ usesToken && !token && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Flex, { align: "center", justify: "space-between", gap: 4, children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Stack, { space: 2, children: [
1769
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1770
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(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." })
1545
1771
  ] }),
1546
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1772
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1547
1773
  Button,
1548
1774
  {
1549
1775
  text: "Connect",
@@ -1554,15 +1780,15 @@ function DeployTool() {
1554
1780
  }
1555
1781
  )
1556
1782
  ] }) }),
1557
- targets.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 4, style: { textAlign: "center" }, children: [
1558
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1559
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { size: 1, muted: true, children: [
1783
+ targets.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Stack, { space: 4, style: { textAlign: "center" }, children: [
1784
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1785
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { size: 1, muted: true, children: [
1560
1786
  "Add a deploy target using the button above, or create a",
1561
1787
  " ",
1562
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { children: "vercel_deploy" }),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { children: "vercel_deploy" }),
1563
1789
  " document directly in the dataset."
1564
1790
  ] }),
1565
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Flex, { justify: "center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1791
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Flex, { justify: "center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1566
1792
  Button,
1567
1793
  {
1568
1794
  text: "Add deploy target",
@@ -1573,12 +1799,12 @@ function DeployTool() {
1573
1799
  }
1574
1800
  ) })
1575
1801
  ] }) }),
1576
- targets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dvfs-grid", style: {
1802
+ targets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dvfs-grid", style: {
1577
1803
  display: "grid",
1578
1804
  gridTemplateColumns: "repeat(auto-fill, minmax(min(540px, 100%), 1fr))",
1579
1805
  gap: "16px",
1580
1806
  alignItems: "start"
1581
- }, children: targets.map((target) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1807
+ }, children: targets.map((target) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1582
1808
  DeployItem,
1583
1809
  {
1584
1810
  target,
@@ -1589,7 +1815,7 @@ function DeployTool() {
1589
1815
  target._id
1590
1816
  )) })
1591
1817
  ] }) }),
1592
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1818
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1593
1819
  Box,
1594
1820
  {
1595
1821
  style: {
@@ -1600,23 +1826,24 @@ function DeployTool() {
1600
1826
  pointerEvents: "none",
1601
1827
  userSelect: "none"
1602
1828
  },
1603
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { size: 0, muted: true, children: [
1829
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { size: 0, muted: true, children: [
1604
1830
  "v",
1605
1831
  VERSION
1606
1832
  ] })
1607
1833
  }
1608
1834
  ),
1609
- showTokenSetup && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1835
+ showTokenSetup && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1610
1836
  TokenSetup,
1611
1837
  {
1612
1838
  onSaved: () => {
1613
1839
  setShowTokenSetup(false);
1614
1840
  load();
1615
1841
  },
1616
- onCancel: () => setShowTokenSetup(false)
1842
+ onCancel: () => setShowTokenSetup(false),
1843
+ hasToken: Boolean(token)
1617
1844
  }
1618
1845
  ),
1619
- showCreateForm && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1846
+ showCreateForm && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1620
1847
  DeployTargetForm,
1621
1848
  {
1622
1849
  onSaved: () => {
@@ -1626,7 +1853,7 @@ function DeployTool() {
1626
1853
  onClose: () => setShowCreateForm(false)
1627
1854
  }
1628
1855
  ),
1629
- pendingEdit && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1856
+ pendingEdit && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1630
1857
  DeployTargetForm,
1631
1858
  {
1632
1859
  initial: pendingEdit,
@@ -1637,15 +1864,15 @@ function DeployTool() {
1637
1864
  onClose: () => setPendingEdit(null)
1638
1865
  }
1639
1866
  ),
1640
- pendingDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1867
+ pendingDelete && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1641
1868
  Dialog,
1642
1869
  {
1643
1870
  header: "Delete deploy target?",
1644
1871
  id: "confirm-delete",
1645
1872
  onClose: () => setPendingDelete(null),
1646
1873
  width: 1,
1647
- footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1648
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1874
+ footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1875
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1649
1876
  Button,
1650
1877
  {
1651
1878
  text: "Cancel",
@@ -1654,7 +1881,7 @@ function DeployTool() {
1654
1881
  style: { cursor: "pointer" }
1655
1882
  }
1656
1883
  ),
1657
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1884
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1658
1885
  Button,
1659
1886
  {
1660
1887
  text: "Delete",
@@ -1667,28 +1894,28 @@ function DeployTool() {
1667
1894
  }
1668
1895
  )
1669
1896
  ] }),
1670
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 3, children: [
1671
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", gap: 2, children: [
1672
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WarningOutlineIcon, {}),
1673
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1897
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Stack, { space: 3, children: [
1898
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Flex, { align: "center", gap: 2, children: [
1899
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(WarningOutlineIcon, {}),
1900
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1674
1901
  ] }),
1675
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1902
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1676
1903
  ] }) })
1677
1904
  }
1678
1905
  ),
1679
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToastViewport, {})
1906
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToastViewport, {})
1680
1907
  ] });
1681
1908
  }
1682
1909
 
1683
1910
  // src/schema/vercelDeploy.ts
1684
- var import_sanity4 = require("sanity");
1911
+ var import_sanity5 = require("sanity");
1685
1912
 
1686
1913
  // src/schema/schemaIcon.tsx
1687
- var import_react11 = require("react");
1688
- var import_jsx_runtime13 = require("react/jsx-runtime");
1689
- var SchemaRocketIcon = (0, import_react11.forwardRef)(
1914
+ var import_react12 = require("react");
1915
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1916
+ var SchemaRocketIcon = (0, import_react12.forwardRef)(
1690
1917
  function SchemaRocketIcon2(props, ref) {
1691
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1692
1919
  "svg",
1693
1920
  {
1694
1921
  width: "1em",
@@ -1700,7 +1927,7 @@ var SchemaRocketIcon = (0, import_react11.forwardRef)(
1700
1927
  focusable: "false",
1701
1928
  ...props,
1702
1929
  ref,
1703
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1930
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1704
1931
  "path",
1705
1932
  {
1706
1933
  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",
@@ -1715,25 +1942,28 @@ var SchemaRocketIcon = (0, import_react11.forwardRef)(
1715
1942
  );
1716
1943
 
1717
1944
  // src/schema/vercelDeploy.ts
1718
- var vercelDeploySchema = (0, import_sanity4.defineType)({
1945
+ var vercelDeploySchema = (0, import_sanity5.defineType)({
1719
1946
  name: "vercel_deploy",
1720
1947
  title: "Deploy Target",
1721
1948
  type: "document",
1722
1949
  icon: SchemaRocketIcon,
1723
1950
  fields: [
1724
- (0, import_sanity4.defineField)({
1951
+ (0, import_sanity5.defineField)({
1725
1952
  name: "name",
1726
1953
  title: "Name",
1727
1954
  type: "string",
1728
1955
  description: 'Display label shown in the Deploy tool (e.g. "Production", "Staging")',
1729
1956
  validation: (Rule) => Rule.required()
1730
1957
  }),
1731
- (0, import_sanity4.defineField)({
1958
+ (0, import_sanity5.defineField)({
1732
1959
  name: "url",
1733
1960
  title: "Deploy Hook URL",
1734
1961
  type: "url",
1735
- description: "From Vercel \u2192 Project Settings \u2192 Git \u2192 Deploy Hooks",
1736
- validation: (Rule) => Rule.required().uri({ scheme: ["https"] }).custom((url) => {
1962
+ 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.",
1963
+ validation: (Rule) => Rule.uri({ scheme: ["https"] }).custom((url, context) => {
1964
+ const doc = context.document;
1965
+ if (!url && !doc?.proxyKey) return "Set either a Deploy Hook URL or a Proxy Key";
1966
+ if (!url) return true;
1737
1967
  if (typeof url !== "string") return true;
1738
1968
  if (!url.includes("api.vercel.com/v1/integrations/deploy/")) {
1739
1969
  return "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)";
@@ -1741,13 +1971,24 @@ var vercelDeploySchema = (0, import_sanity4.defineType)({
1741
1971
  return true;
1742
1972
  })
1743
1973
  }),
1744
- (0, import_sanity4.defineField)({
1974
+ (0, import_sanity5.defineField)({
1975
+ name: "proxyKey",
1976
+ title: "Proxy Key",
1977
+ type: "string",
1978
+ description: "Used in proxy mode. Matches a key configured on the deploy proxy, which holds the real hook URL. Contains no secret.",
1979
+ validation: (Rule) => Rule.custom((key, context) => {
1980
+ const doc = context.document;
1981
+ if (!key && !doc?.url) return "Set either a Deploy Hook URL or a Proxy Key";
1982
+ return true;
1983
+ })
1984
+ }),
1985
+ (0, import_sanity5.defineField)({
1745
1986
  name: "teamId",
1746
1987
  title: "Vercel Team ID",
1747
1988
  type: "string",
1748
1989
  description: "Required for team-owned projects \u2014 find it in Vercel Team Settings"
1749
1990
  }),
1750
- (0, import_sanity4.defineField)({
1991
+ (0, import_sanity5.defineField)({
1751
1992
  name: "disableDeleteAction",
1752
1993
  title: "Prevent deletion",
1753
1994
  type: "boolean",
@@ -1760,51 +2001,29 @@ var vercelDeploySchema = (0, import_sanity4.defineType)({
1760
2001
  }
1761
2002
  });
1762
2003
 
1763
- // src/schema/vercelConfig.ts
1764
- var import_sanity5 = require("sanity");
1765
- var vercelConfigSchema = (0, import_sanity5.defineType)({
1766
- name: "vercelDeploy.config",
1767
- title: "Vercel Deploy Configuration",
1768
- type: "document",
1769
- fields: [
1770
- (0, import_sanity5.defineField)({
1771
- name: "accessToken",
1772
- title: "Vercel API Token",
1773
- type: "string",
1774
- description: "Readable by anyone who can read this dataset. Delete this document to revoke the stored token."
1775
- })
1776
- ],
1777
- preview: {
1778
- select: { token: "accessToken" },
1779
- prepare: ({ token }) => ({
1780
- title: "Vercel API Token",
1781
- // Never render the secret itself in a preview.
1782
- subtitle: token ? "Connected" : "Not set"
1783
- })
1784
- }
1785
- });
1786
-
1787
- // src/index.ts
2004
+ // src/index.tsx
2005
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1788
2006
  var vercelDeploy = (0, import_sanity6.definePlugin)((options) => {
1789
2007
  const config = options ?? {};
2008
+ const resolved = resolveConfig(options);
1790
2009
  return {
1791
2010
  name: "deploy-vercel-from-sanity",
1792
2011
  schema: {
1793
- types: [vercelDeploySchema, vercelConfigSchema]
2012
+ types: [vercelDeploySchema]
1794
2013
  },
1795
2014
  tools: [
1796
2015
  {
1797
2016
  name: config.name ?? "vercel-deploy",
1798
2017
  title: config.title ?? "Deploy",
1799
2018
  icon: config.icon ?? RocketIcon,
1800
- component: DeployTool
2019
+ // Wrapped so the tool tree can read the resolved config without prop drilling.
2020
+ component: () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ConfigProvider, { value: resolved, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DeployTool, {}) })
1801
2021
  }
1802
2022
  ]
1803
2023
  };
1804
2024
  });
1805
2025
  // Annotate the CommonJS export names for ESM import in node:
1806
2026
  0 && (module.exports = {
1807
- vercelConfigSchema,
1808
2027
  vercelDeploy,
1809
2028
  vercelDeploySchema
1810
2029
  });