@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/README.md +171 -37
- package/dist/index.d.mts +54 -36
- package/dist/index.d.ts +54 -36
- package/dist/index.js +687 -468
- package/dist/index.mjs +538 -318
- package/package.json +14 -9
- package/proxy/.env.example +40 -0
- package/proxy/README.md +228 -0
- package/proxy/core.ts +283 -0
- package/proxy/nextjs-app-router/route.ts +129 -0
- package/src/compat/code.tsx +0 -29
- package/src/compat/index.ts +0 -14
- package/src/compat/menu.tsx +0 -225
- package/src/compat/primitives.tsx +0 -93
- package/src/compat/resolve.ts +0 -55
- package/src/compat/toast.tsx +0 -173
- package/src/compat/tooltip.tsx +0 -78
- package/src/components/DeployHistory.tsx +0 -160
- package/src/components/DeployItem.tsx +0 -596
- package/src/components/DeployTargetForm.tsx +0 -172
- package/src/components/DeployTool.tsx +0 -318
- package/src/components/StatusBadge.tsx +0 -23
- package/src/components/TokenSetup.tsx +0 -98
- package/src/icons.tsx +0 -78
- package/src/index.ts +0 -47
- package/src/lib/api.ts +0 -102
- package/src/lib/helpers.ts +0 -131
- package/src/schema/schemaIcon.tsx +0 -38
- package/src/schema/vercelConfig.ts +0 -34
- package/src/schema/vercelDeploy.ts +0 -49
- package/src/types.ts +0 -77
- package/src/version.ts +0 -2
package/src/index.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// deploy-vercel-from-sanity — Sanity Studio plugin for Vercel deployments (Studio v3.30 through v6)
|
|
2
|
-
import { definePlugin } from 'sanity'
|
|
3
|
-
import { RocketIcon } from './icons'
|
|
4
|
-
import { DeployTool } from './components/DeployTool'
|
|
5
|
-
import { vercelDeploySchema } from './schema/vercelDeploy'
|
|
6
|
-
import { vercelConfigSchema } from './schema/vercelConfig'
|
|
7
|
-
import type { VercelDeployPluginConfig } from './types'
|
|
8
|
-
|
|
9
|
-
export { vercelDeploySchema } from './schema/vercelDeploy'
|
|
10
|
-
export { vercelConfigSchema } from './schema/vercelConfig'
|
|
11
|
-
export type { VercelDeployPluginConfig, DeployTarget, VercelDeployment, VercelDeployState } from './types'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Sanity Studio plugin — trigger and monitor Vercel deployments.
|
|
15
|
-
*
|
|
16
|
-
* Supports Studio v3.30 through v6 from a single build; see the compatibility
|
|
17
|
-
* table in the README for how @sanity/ui and @sanity/icons are resolved.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* // sanity.config.ts
|
|
21
|
-
* import { vercelDeploy } from '@liiift-studio/deploy-vercel-from-sanity'
|
|
22
|
-
*
|
|
23
|
-
* export default defineConfig({
|
|
24
|
-
* plugins: [
|
|
25
|
-
* vercelDeploy(),
|
|
26
|
-
* // or with options:
|
|
27
|
-
* vercelDeploy({ title: 'Deploy', name: 'vercel-deploy' }),
|
|
28
|
-
* ],
|
|
29
|
-
* })
|
|
30
|
-
*/
|
|
31
|
-
export const vercelDeploy = definePlugin<VercelDeployPluginConfig | void>(options => {
|
|
32
|
-
const config = options ?? {}
|
|
33
|
-
return {
|
|
34
|
-
name: 'deploy-vercel-from-sanity',
|
|
35
|
-
schema: {
|
|
36
|
-
types: [vercelDeploySchema, vercelConfigSchema],
|
|
37
|
-
},
|
|
38
|
-
tools: [
|
|
39
|
-
{
|
|
40
|
-
name: config.name ?? 'vercel-deploy',
|
|
41
|
-
title: config.title ?? 'Deploy',
|
|
42
|
-
icon: config.icon ?? RocketIcon,
|
|
43
|
-
component: DeployTool,
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
}
|
|
47
|
-
})
|
package/src/lib/api.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// Vercel REST API helpers — all calls require a bearer token
|
|
2
|
-
import type { VercelDeployment, DeploymentEvent } from '../types'
|
|
3
|
-
|
|
4
|
-
const BASE = 'https://api.vercel.com'
|
|
5
|
-
|
|
6
|
-
/** Only allow genuine Vercel deploy hook URLs through triggerDeploy */
|
|
7
|
-
const VERCEL_HOOK_RE = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//
|
|
8
|
-
|
|
9
|
-
async function vercelFetch<T>(path: string, token: string, init?: RequestInit): Promise<T> {
|
|
10
|
-
const res = await fetch(`${BASE}${path}`, {
|
|
11
|
-
...init,
|
|
12
|
-
headers: {
|
|
13
|
-
Authorization: `Bearer ${token}`,
|
|
14
|
-
'Content-Type': 'application/json',
|
|
15
|
-
...init?.headers,
|
|
16
|
-
},
|
|
17
|
-
})
|
|
18
|
-
if (!res.ok) {
|
|
19
|
-
const hint =
|
|
20
|
-
res.status === 401 ? ' — token is invalid or expired. Reconnect your API token.' :
|
|
21
|
-
res.status === 403 ? ' — token lacks the required permissions. Ensure it has Full Account scope.' :
|
|
22
|
-
res.status === 404 ? ' — resource not found. Check the deploy hook URL and team ID.' :
|
|
23
|
-
res.status === 429 ? ' — rate limit reached. Wait a moment and try again.' :
|
|
24
|
-
res.status >= 500 ? ' — Vercel is experiencing issues. Try again shortly.' :
|
|
25
|
-
''
|
|
26
|
-
throw new Error(`Vercel API ${res.status}${hint}`)
|
|
27
|
-
}
|
|
28
|
-
return res.json() as Promise<T>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Fetch the last N deployments triggered by a specific deploy hook */
|
|
32
|
-
export async function listDeployments(opts: {
|
|
33
|
-
projectId: string
|
|
34
|
-
hookId: string
|
|
35
|
-
token: string
|
|
36
|
-
teamId?: string
|
|
37
|
-
limit?: number
|
|
38
|
-
}): Promise<VercelDeployment[]> {
|
|
39
|
-
const params = new URLSearchParams({
|
|
40
|
-
projectId: opts.projectId,
|
|
41
|
-
'meta-deployHookId': opts.hookId,
|
|
42
|
-
limit: String(opts.limit ?? 10),
|
|
43
|
-
})
|
|
44
|
-
if (opts.teamId) params.set('teamId', opts.teamId)
|
|
45
|
-
const data = await vercelFetch<{ deployments: VercelDeployment[] }>(
|
|
46
|
-
`/v6/deployments?${params}`,
|
|
47
|
-
opts.token,
|
|
48
|
-
)
|
|
49
|
-
return data.deployments ?? []
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Cancel an in-progress deployment */
|
|
53
|
-
export async function cancelDeployment(opts: {
|
|
54
|
-
deploymentId: string
|
|
55
|
-
token: string
|
|
56
|
-
teamId?: string
|
|
57
|
-
}): Promise<void> {
|
|
58
|
-
// teamId is free text from the target document — encode it rather than splicing it in raw,
|
|
59
|
-
// or an '&' injects extra parameters into an authenticated request.
|
|
60
|
-
const params = new URLSearchParams()
|
|
61
|
-
if (opts.teamId) params.set('teamId', opts.teamId)
|
|
62
|
-
const query = params.toString() ? `?${params}` : ''
|
|
63
|
-
await vercelFetch(
|
|
64
|
-
`/v12/deployments/${encodeURIComponent(opts.deploymentId)}/cancel${query}`,
|
|
65
|
-
opts.token,
|
|
66
|
-
{ method: 'PATCH' },
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Trigger a deploy by POSTing to the hook URL.
|
|
72
|
-
* Validates the URL is a genuine Vercel hook before calling to prevent
|
|
73
|
-
* SSRF if a document is tampered with outside the Studio schema.
|
|
74
|
-
*/
|
|
75
|
-
export async function triggerDeploy(hookUrl: string): Promise<void> {
|
|
76
|
-
if (!VERCEL_HOOK_RE.test(hookUrl)) {
|
|
77
|
-
throw new Error('Invalid deploy hook URL — must be a Vercel hook (api.vercel.com/v1/integrations/deploy/…)')
|
|
78
|
-
}
|
|
79
|
-
const res = await fetch(hookUrl, { method: 'POST' })
|
|
80
|
-
if (!res.ok) throw new Error(`Deploy hook returned ${res.status}`)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Fetch build events for a deployment.
|
|
85
|
-
* Returns up to 100 events in reverse chronological order,
|
|
86
|
-
* filtered to lines with actual text content.
|
|
87
|
-
*/
|
|
88
|
-
export async function getDeploymentEvents(opts: {
|
|
89
|
-
deploymentId: string
|
|
90
|
-
token: string
|
|
91
|
-
teamId?: string
|
|
92
|
-
}): Promise<DeploymentEvent[]> {
|
|
93
|
-
const params = new URLSearchParams({ limit: '100', direction: 'backward' })
|
|
94
|
-
if (opts.teamId) params.set('teamId', opts.teamId)
|
|
95
|
-
// API returns either a plain array or a wrapped object depending on version
|
|
96
|
-
const raw = await vercelFetch<DeploymentEvent[] | { events?: DeploymentEvent[] }>(
|
|
97
|
-
`/v2/deployments/${opts.deploymentId}/events?${params}`,
|
|
98
|
-
opts.token,
|
|
99
|
-
)
|
|
100
|
-
const events: DeploymentEvent[] = Array.isArray(raw) ? raw : (raw.events ?? [])
|
|
101
|
-
return events.filter(e => e.text?.trim())
|
|
102
|
-
}
|
package/src/lib/helpers.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// URL parsing and time formatting utilities
|
|
2
|
-
import type { VercelDeployment, VercelDeployState } from '../types'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Extracts projectId and hookId from a Vercel deploy hook URL.
|
|
6
|
-
* Format: https://api.vercel.com/v1/integrations/deploy/{projectId}/{hookId}
|
|
7
|
-
*/
|
|
8
|
-
export function parseHookUrl(url: string): { projectId: string; hookId: string } {
|
|
9
|
-
try {
|
|
10
|
-
const path = new URL(url).pathname
|
|
11
|
-
const parts = path.split('/').filter(Boolean)
|
|
12
|
-
// parts: ['v1', 'integrations', 'deploy', '{projectId}', '{hookId}']
|
|
13
|
-
return {
|
|
14
|
-
projectId: parts[3] ?? '',
|
|
15
|
-
hookId: parts[4] ?? '',
|
|
16
|
-
}
|
|
17
|
-
} catch {
|
|
18
|
-
return { projectId: '', hookId: '' }
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Active states — deployment is in progress and should be polled */
|
|
23
|
-
const ACTIVE_STATES: ReadonlySet<VercelDeployState> = new Set([
|
|
24
|
-
'QUEUED',
|
|
25
|
-
'INITIALIZING',
|
|
26
|
-
'BUILDING',
|
|
27
|
-
])
|
|
28
|
-
|
|
29
|
-
export function isActiveState(state: VercelDeployState | undefined): boolean {
|
|
30
|
-
return !!state && ACTIVE_STATES.has(state)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Human-readable elapsed duration from a number of seconds */
|
|
34
|
-
export function formatDuration(seconds: number): string {
|
|
35
|
-
if (seconds < 60) return `${seconds}s`
|
|
36
|
-
const m = Math.floor(seconds / 60)
|
|
37
|
-
const s = seconds % 60
|
|
38
|
-
return `${m}m ${s}s`
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Human-readable relative time from a Unix ms timestamp */
|
|
42
|
-
export function timeAgo(ms: number): string {
|
|
43
|
-
const diff = Math.floor((Date.now() - ms) / 1000)
|
|
44
|
-
if (diff < 60) return `${diff}s ago`
|
|
45
|
-
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`
|
|
46
|
-
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`
|
|
47
|
-
return `${Math.floor(diff / 86400)}d ago`
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Validates a URL is safe to use as an href.
|
|
52
|
-
* Rejects anything that isn't http/https to block javascript: injection
|
|
53
|
-
* from a compromised API response.
|
|
54
|
-
*/
|
|
55
|
-
export function safeHref(url: string | undefined): string | undefined {
|
|
56
|
-
if (!url) return undefined
|
|
57
|
-
try {
|
|
58
|
-
const parsed = new URL(url)
|
|
59
|
-
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return undefined
|
|
60
|
-
return url
|
|
61
|
-
} catch {
|
|
62
|
-
return undefined
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Builds a validated https URL from a Vercel deployment hostname.
|
|
68
|
-
*
|
|
69
|
-
* The API returns a bare host, so concatenating it fixes only the scheme —
|
|
70
|
-
* a value like `@evil.com` yields `https://@evil.com`, which navigates to an
|
|
71
|
-
* attacker-chosen host. Round-tripping through URL and checking the host was
|
|
72
|
-
* not rewritten closes that.
|
|
73
|
-
*
|
|
74
|
-
* @param host Deployment hostname from the Vercel API, e.g. `my-app-abc123.vercel.app`.
|
|
75
|
-
*/
|
|
76
|
-
export function deploymentHref(host: string | undefined): string | undefined {
|
|
77
|
-
if (!host) return undefined
|
|
78
|
-
// Reject anything carrying credentials, a scheme, a port or a path — a plain host only.
|
|
79
|
-
if (!/^[a-z0-9.-]+$/i.test(host)) return undefined
|
|
80
|
-
return safeHref(`https://${host}`)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Truncates a commit SHA to 7 chars */
|
|
84
|
-
export function shortSha(sha: string | undefined): string {
|
|
85
|
-
return sha ? sha.slice(0, 7) : ''
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Returns a label and tone for a Vercel deployment state */
|
|
89
|
-
export function stateLabel(state: VercelDeployState | undefined): {
|
|
90
|
-
label: string
|
|
91
|
-
tone: 'positive' | 'caution' | 'critical' | 'default'
|
|
92
|
-
} {
|
|
93
|
-
switch (state) {
|
|
94
|
-
case 'READY': return { label: 'Ready', tone: 'positive' }
|
|
95
|
-
case 'BUILDING': return { label: 'Building', tone: 'caution' }
|
|
96
|
-
case 'QUEUED': return { label: 'Queued', tone: 'caution' }
|
|
97
|
-
case 'INITIALIZING': return { label: 'Initializing', tone: 'caution' }
|
|
98
|
-
case 'ERROR': return { label: 'Error', tone: 'critical' }
|
|
99
|
-
case 'CANCELED': return { label: 'Canceled', tone: 'default' }
|
|
100
|
-
case 'LOADING': return { label: 'Loading…', tone: 'default' }
|
|
101
|
-
default: return { label: 'Unknown', tone: 'default' }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Constructs a GitHub commit URL from deployment meta fields.
|
|
107
|
-
* Returns null if the required repo or SHA info is not present.
|
|
108
|
-
*/
|
|
109
|
-
export function githubCommitHref(meta: VercelDeployment['meta']): string | null {
|
|
110
|
-
if (!meta?.githubCommitSha) return null
|
|
111
|
-
const repo = meta.githubRepo ?? null
|
|
112
|
-
if (!repo) return null
|
|
113
|
-
return `https://github.com/${repo}/commit/${meta.githubCommitSha}`
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Extracts the Vercel project dashboard URL from a deployment's inspectorUrl.
|
|
118
|
-
* inspectorUrl format: https://vercel.com/{team}/{project}/{deploymentId}
|
|
119
|
-
* Returns https://vercel.com/{team}/{project} or null if unparseable.
|
|
120
|
-
*/
|
|
121
|
-
export function projectHref(inspectorUrl: string | undefined): string | null {
|
|
122
|
-
if (!inspectorUrl) return null
|
|
123
|
-
try {
|
|
124
|
-
const { origin, pathname } = new URL(inspectorUrl)
|
|
125
|
-
const parts = pathname.split('/').filter(Boolean)
|
|
126
|
-
if (parts.length < 2) return null
|
|
127
|
-
return `${origin}/${parts[0]}/${parts[1]}`
|
|
128
|
-
} catch {
|
|
129
|
-
return null
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Eager rocket glyph for schema icons — Studio serialises these with renderToString, which cannot resolve a lazy component
|
|
2
|
-
import { forwardRef } from 'react'
|
|
3
|
-
import type { SVGProps } from 'react'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Static copy of Sanity's rocket glyph.
|
|
7
|
-
*
|
|
8
|
-
* Schema type icons are rendered through `renderToString` when the Studio
|
|
9
|
-
* publishes its schema to Canvas/Create. On @sanity/icons v5 the resolved icon is
|
|
10
|
-
* a `React.lazy` component behind Suspense, and a synchronous server render
|
|
11
|
-
* cannot resolve a dynamic import — so the published icon comes out blank. This
|
|
12
|
-
* eager component sidesteps that for the one place it matters. Artwork is MIT,
|
|
13
|
-
* from @sanity/icons.
|
|
14
|
-
*/
|
|
15
|
-
export const SchemaRocketIcon = forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
|
16
|
-
function SchemaRocketIcon(props, ref) {
|
|
17
|
-
return (
|
|
18
|
-
<svg
|
|
19
|
-
width="1em"
|
|
20
|
-
height="1em"
|
|
21
|
-
viewBox="0 0 25 25"
|
|
22
|
-
fill="none"
|
|
23
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
-
aria-hidden="true"
|
|
25
|
-
focusable="false"
|
|
26
|
-
{...props}
|
|
27
|
-
ref={ref}
|
|
28
|
-
>
|
|
29
|
-
<path
|
|
30
|
-
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"
|
|
31
|
-
stroke="currentColor"
|
|
32
|
-
strokeWidth={1.2}
|
|
33
|
-
strokeLinejoin="round"
|
|
34
|
-
/>
|
|
35
|
-
</svg>
|
|
36
|
-
)
|
|
37
|
-
},
|
|
38
|
-
)
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Sanity schema for the vercelDeploy.config singleton that holds the Vercel API token
|
|
2
|
-
import { defineField, defineType } from 'sanity'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Registers the document the plugin stores its Vercel token in.
|
|
6
|
-
*
|
|
7
|
-
* The document was previously written without a registered type, which made it
|
|
8
|
-
* invisible to Structure, absent from schema extraction, and impossible to
|
|
9
|
-
* inspect or revoke from inside the Studio. Registering it does not change where
|
|
10
|
-
* the token lives — see the security note in the README about dataset read
|
|
11
|
-
* access — but it does make the credential visible and removable.
|
|
12
|
-
*/
|
|
13
|
-
export const vercelConfigSchema = defineType({
|
|
14
|
-
name: 'vercelDeploy.config',
|
|
15
|
-
title: 'Vercel Deploy Configuration',
|
|
16
|
-
type: 'document',
|
|
17
|
-
fields: [
|
|
18
|
-
defineField({
|
|
19
|
-
name: 'accessToken',
|
|
20
|
-
title: 'Vercel API Token',
|
|
21
|
-
type: 'string',
|
|
22
|
-
description:
|
|
23
|
-
'Readable by anyone who can read this dataset. Delete this document to revoke the stored token.',
|
|
24
|
-
}),
|
|
25
|
-
],
|
|
26
|
-
preview: {
|
|
27
|
-
select: { token: 'accessToken' },
|
|
28
|
-
prepare: ({ token }: { token?: string }) => ({
|
|
29
|
-
title: 'Vercel API Token',
|
|
30
|
-
// Never render the secret itself in a preview.
|
|
31
|
-
subtitle: token ? 'Connected' : 'Not set',
|
|
32
|
-
}),
|
|
33
|
-
},
|
|
34
|
-
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Sanity schema for vercel_deploy documents — stores deploy hook targets
|
|
2
|
-
import { defineField, defineType } from 'sanity'
|
|
3
|
-
import { SchemaRocketIcon } from './schemaIcon'
|
|
4
|
-
|
|
5
|
-
export const vercelDeploySchema = defineType({
|
|
6
|
-
name: 'vercel_deploy',
|
|
7
|
-
title: 'Deploy Target',
|
|
8
|
-
type: 'document',
|
|
9
|
-
icon: SchemaRocketIcon,
|
|
10
|
-
fields: [
|
|
11
|
-
defineField({
|
|
12
|
-
name: 'name',
|
|
13
|
-
title: 'Name',
|
|
14
|
-
type: 'string',
|
|
15
|
-
description: 'Display label shown in the Deploy tool (e.g. "Production", "Staging")',
|
|
16
|
-
validation: Rule => Rule.required(),
|
|
17
|
-
}),
|
|
18
|
-
defineField({
|
|
19
|
-
name: 'url',
|
|
20
|
-
title: 'Deploy Hook URL',
|
|
21
|
-
type: 'url',
|
|
22
|
-
description: 'From Vercel → Project Settings → Git → Deploy Hooks',
|
|
23
|
-
validation: Rule =>
|
|
24
|
-
Rule.required().uri({ scheme: ['https'] }).custom(url => {
|
|
25
|
-
if (typeof url !== 'string') return true
|
|
26
|
-
if (!url.includes('api.vercel.com/v1/integrations/deploy/')) {
|
|
27
|
-
return 'Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/…)'
|
|
28
|
-
}
|
|
29
|
-
return true
|
|
30
|
-
}),
|
|
31
|
-
}),
|
|
32
|
-
defineField({
|
|
33
|
-
name: 'teamId',
|
|
34
|
-
title: 'Vercel Team ID',
|
|
35
|
-
type: 'string',
|
|
36
|
-
description: 'Required for team-owned projects — find it in Vercel Team Settings',
|
|
37
|
-
}),
|
|
38
|
-
defineField({
|
|
39
|
-
name: 'disableDeleteAction',
|
|
40
|
-
title: 'Prevent deletion',
|
|
41
|
-
type: 'boolean',
|
|
42
|
-
description: 'Lock this target so it cannot be deleted from the Studio',
|
|
43
|
-
initialValue: false,
|
|
44
|
-
}),
|
|
45
|
-
],
|
|
46
|
-
preview: {
|
|
47
|
-
select: { title: 'name', subtitle: 'url' },
|
|
48
|
-
},
|
|
49
|
-
})
|
package/src/types.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
// TypeScript types for deploy-vercel-from-sanity
|
|
2
|
-
|
|
3
|
-
export type VercelDeployState =
|
|
4
|
-
| 'QUEUED'
|
|
5
|
-
| 'INITIALIZING'
|
|
6
|
-
| 'BUILDING'
|
|
7
|
-
| 'READY'
|
|
8
|
-
| 'ERROR'
|
|
9
|
-
| 'CANCELED'
|
|
10
|
-
| 'LOADING' // internal — before first API response
|
|
11
|
-
|
|
12
|
-
/** A vercel_deploy document stored in the Sanity dataset */
|
|
13
|
-
export interface DeployTarget {
|
|
14
|
-
_id: string
|
|
15
|
-
_type: 'vercel_deploy'
|
|
16
|
-
name: string
|
|
17
|
-
/** Full Vercel deploy hook URL */
|
|
18
|
-
url: string
|
|
19
|
-
/** Vercel team ID — optional, only needed for team projects */
|
|
20
|
-
teamId?: string
|
|
21
|
-
/** Prevent editors from deleting this target */
|
|
22
|
-
disableDeleteAction?: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** A single deployment returned by GET /v6/deployments */
|
|
26
|
-
export interface VercelDeployment {
|
|
27
|
-
uid: string
|
|
28
|
-
/** Preview hostname, e.g. my-project-abc123.vercel.app */
|
|
29
|
-
url: string
|
|
30
|
-
state: VercelDeployState
|
|
31
|
-
/** Unix ms timestamp — when the deployment was created */
|
|
32
|
-
created: number
|
|
33
|
-
/** Unix ms timestamp — when the deployment became ready */
|
|
34
|
-
ready?: number
|
|
35
|
-
/** Link to the Vercel dashboard page for this deployment */
|
|
36
|
-
inspectorUrl?: string
|
|
37
|
-
creator?: {
|
|
38
|
-
uid: string
|
|
39
|
-
username: string
|
|
40
|
-
avatar?: string
|
|
41
|
-
}
|
|
42
|
-
meta?: {
|
|
43
|
-
githubCommitMessage?: string
|
|
44
|
-
githubCommitRef?: string
|
|
45
|
-
githubCommitSha?: string
|
|
46
|
-
githubCommitAuthorName?: string
|
|
47
|
-
/** GitHub repo in "org/repo" format — used to construct commit links */
|
|
48
|
-
githubRepo?: string
|
|
49
|
-
/** GitHub org slug — fallback when githubRepo is absent */
|
|
50
|
-
githubCommitOrg?: string
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** A single build event returned by GET /v2/deployments/{id}/events */
|
|
55
|
-
export interface DeploymentEvent {
|
|
56
|
-
type: 'command' | 'stdout' | 'stderr' | 'exit' | 'deployment-state'
|
|
57
|
-
text?: string
|
|
58
|
-
created: number
|
|
59
|
-
payload?: Record<string, unknown>
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Vercel config document stored at _id: 'config.vercelDeploy' — readable by all authenticated users */
|
|
63
|
-
export interface VercelConfig {
|
|
64
|
-
_id: 'config.vercelDeploy'
|
|
65
|
-
_type: 'vercelDeploy.config'
|
|
66
|
-
accessToken: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Plugin configuration options */
|
|
70
|
-
export interface VercelDeployPluginConfig {
|
|
71
|
-
/** Tool name slug shown in Studio sidebar (default: 'vercel-deploy') */
|
|
72
|
-
name?: string
|
|
73
|
-
/** Tool label shown in Studio sidebar (default: 'Deploy') */
|
|
74
|
-
title?: string
|
|
75
|
-
/** Custom icon component */
|
|
76
|
-
icon?: React.ComponentType
|
|
77
|
-
}
|
package/src/version.ts
DELETED