@liiift-studio/deploy-vercel-from-sanity 0.1.4 → 0.1.7
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 +92 -70
- package/dist/index.js +333 -132
- package/dist/index.mjs +342 -136
- package/package.json +1 -1
- package/src/components/DeployItem.tsx +339 -155
- package/src/components/DeployTool.tsx +33 -26
- package/src/lib/api.ts +33 -2
- package/src/lib/helpers.ts +25 -8
- package/src/types.ts +11 -3
package/src/lib/api.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// Vercel REST API helpers — all calls require a bearer token
|
|
2
|
-
import type { VercelDeployment } from '../types'
|
|
2
|
+
import type { VercelDeployment, DeploymentEvent } from '../types'
|
|
3
3
|
|
|
4
4
|
const BASE = 'https://api.vercel.com'
|
|
5
5
|
|
|
6
|
+
/** Only allow genuine Vercel deploy hook URLs through triggerDeploy */
|
|
7
|
+
const VERCEL_HOOK_RE = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//
|
|
8
|
+
|
|
6
9
|
async function vercelFetch<T>(path: string, token: string, init?: RequestInit): Promise<T> {
|
|
7
10
|
const res = await fetch(`${BASE}${path}`, {
|
|
8
11
|
...init,
|
|
@@ -52,8 +55,36 @@ export async function cancelDeployment(opts: {
|
|
|
52
55
|
})
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Trigger a deploy by POSTing to the hook URL.
|
|
60
|
+
* Validates the URL is a genuine Vercel hook before calling to prevent
|
|
61
|
+
* SSRF if a document is tampered with outside the Studio schema.
|
|
62
|
+
*/
|
|
56
63
|
export async function triggerDeploy(hookUrl: string): Promise<void> {
|
|
64
|
+
if (!VERCEL_HOOK_RE.test(hookUrl)) {
|
|
65
|
+
throw new Error('Invalid deploy hook URL — must be a Vercel hook (api.vercel.com/v1/integrations/deploy/…)')
|
|
66
|
+
}
|
|
57
67
|
const res = await fetch(hookUrl, { method: 'POST' })
|
|
58
68
|
if (!res.ok) throw new Error(`Deploy hook returned ${res.status}`)
|
|
59
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Fetch build events for a deployment.
|
|
73
|
+
* Returns up to 100 events in reverse chronological order,
|
|
74
|
+
* filtered to lines with actual text content.
|
|
75
|
+
*/
|
|
76
|
+
export async function getDeploymentEvents(opts: {
|
|
77
|
+
deploymentId: string
|
|
78
|
+
token: string
|
|
79
|
+
teamId?: string
|
|
80
|
+
}): Promise<DeploymentEvent[]> {
|
|
81
|
+
const params = new URLSearchParams({ limit: '100', direction: 'backward' })
|
|
82
|
+
if (opts.teamId) params.set('teamId', opts.teamId)
|
|
83
|
+
// API returns either a plain array or a wrapped object depending on version
|
|
84
|
+
const raw = await vercelFetch<DeploymentEvent[] | { events?: DeploymentEvent[] }>(
|
|
85
|
+
`/v2/deployments/${opts.deploymentId}/events?${params}`,
|
|
86
|
+
opts.token,
|
|
87
|
+
)
|
|
88
|
+
const events: DeploymentEvent[] = Array.isArray(raw) ? raw : (raw.events ?? [])
|
|
89
|
+
return events.filter(e => e.text?.trim())
|
|
90
|
+
}
|
package/src/lib/helpers.ts
CHANGED
|
@@ -74,13 +74,30 @@ export function stateLabel(state: VercelDeployState | undefined): {
|
|
|
74
74
|
tone: 'positive' | 'caution' | 'critical' | 'default'
|
|
75
75
|
} {
|
|
76
76
|
switch (state) {
|
|
77
|
-
case 'READY':
|
|
78
|
-
case 'BUILDING':
|
|
79
|
-
case 'QUEUED':
|
|
80
|
-
case 'INITIALIZING':return { label: 'Initializing', tone: 'caution' }
|
|
81
|
-
case 'ERROR':
|
|
82
|
-
case 'CANCELED':
|
|
83
|
-
case 'LOADING':
|
|
84
|
-
default:
|
|
77
|
+
case 'READY': return { label: 'Ready', tone: 'positive' }
|
|
78
|
+
case 'BUILDING': return { label: 'Building', tone: 'caution' }
|
|
79
|
+
case 'QUEUED': return { label: 'Queued', tone: 'caution' }
|
|
80
|
+
case 'INITIALIZING': return { label: 'Initializing', tone: 'caution' }
|
|
81
|
+
case 'ERROR': return { label: 'Error', tone: 'critical' }
|
|
82
|
+
case 'CANCELED': return { label: 'Canceled', tone: 'default' }
|
|
83
|
+
case 'LOADING': return { label: 'Loading…', tone: 'default' }
|
|
84
|
+
default: return { label: 'Unknown', tone: 'default' }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extracts the Vercel project dashboard URL from a deployment's inspectorUrl.
|
|
90
|
+
* inspectorUrl format: https://vercel.com/{team}/{project}/{deploymentId}
|
|
91
|
+
* Returns https://vercel.com/{team}/{project} or null if unparseable.
|
|
92
|
+
*/
|
|
93
|
+
export function projectHref(inspectorUrl: string | undefined): string | null {
|
|
94
|
+
if (!inspectorUrl) return null
|
|
95
|
+
try {
|
|
96
|
+
const { origin, pathname } = new URL(inspectorUrl)
|
|
97
|
+
const parts = pathname.split('/').filter(Boolean)
|
|
98
|
+
if (parts.length < 2) return null
|
|
99
|
+
return `${origin}/${parts[0]}/${parts[1]}`
|
|
100
|
+
} catch {
|
|
101
|
+
return null
|
|
85
102
|
}
|
|
86
103
|
}
|
package/src/types.ts
CHANGED
|
@@ -45,9 +45,17 @@ export interface VercelDeployment {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
export interface
|
|
50
|
-
|
|
48
|
+
/** A single build event returned by GET /v2/deployments/{id}/events */
|
|
49
|
+
export interface DeploymentEvent {
|
|
50
|
+
type: 'command' | 'stdout' | 'stderr' | 'exit' | 'deployment-state'
|
|
51
|
+
text?: string
|
|
52
|
+
created: number
|
|
53
|
+
payload?: Record<string, unknown>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Vercel config document stored at _id: 'config.vercelDeploy' — readable by all authenticated users */
|
|
57
|
+
export interface VercelConfig {
|
|
58
|
+
_id: 'config.vercelDeploy'
|
|
51
59
|
_type: 'vercelDeploy.config'
|
|
52
60
|
accessToken: string
|
|
53
61
|
}
|