@theholocron/holocron-plugin-vercel 2.0.0-alpha.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/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/index.d.mts +115 -0
- package/dist/index.mjs +240 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Newton Koumantzelis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# `@theholocron/holocron-plugin-vercel`
|
|
2
|
+
|
|
3
|
+
Vercel plugin for [Holocron](../cli). Implements the `deployment`
|
|
4
|
+
capability against the [Vercel REST API](https://vercel.com/docs/rest-api).
|
|
5
|
+
|
|
6
|
+
## Auth
|
|
7
|
+
|
|
8
|
+
Token resolution order:
|
|
9
|
+
|
|
10
|
+
1. `--token <PAT>` flag on the holocron invocation
|
|
11
|
+
2. `HOLOCRON_VERCEL_TOKEN` env var
|
|
12
|
+
3. `VERCEL_TOKEN` env var (the default the Vercel CLI also reads)
|
|
13
|
+
|
|
14
|
+
If none are set, the plugin throws a clear error. No `vercel auth`
|
|
15
|
+
fallback — Vercel's CLI auth is per-account and the scopes don't
|
|
16
|
+
always cover what holocron needs at the API level. Explicit token only.
|
|
17
|
+
|
|
18
|
+
## Config
|
|
19
|
+
|
|
20
|
+
```jsonc
|
|
21
|
+
// holocron.config.json
|
|
22
|
+
{
|
|
23
|
+
"providers": {
|
|
24
|
+
"deployment": ["vercel", { "teamId": "team_xxx" }]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- `teamId` (optional) — Vercel team id. When set, all requests are
|
|
30
|
+
scoped to that team. Leave unset for personal-account projects.
|
|
31
|
+
|
|
32
|
+
## Status
|
|
33
|
+
|
|
34
|
+
**v0.0.0 — first port.** Capability covers:
|
|
35
|
+
|
|
36
|
+
- `listProjects()` / `ensureProject()` — idempotent project create
|
|
37
|
+
- `updateProjectSettings()` — toggle preview deploys, git-creates-deploys
|
|
38
|
+
- `setEnvVar()` / `listEnvVars()` — per-target env vars
|
|
39
|
+
- `triggerDeployment()` — branch deploys with optional named target
|
|
40
|
+
- `getDeployment()` — fetch a deployment by id
|
|
41
|
+
|
|
42
|
+
Out of scope for v1 (file a follow-up if needed):
|
|
43
|
+
|
|
44
|
+
- Domain management (`addDomain` / `removeDomain`)
|
|
45
|
+
- Deletion (`deleteProject`)
|
|
46
|
+
- Marketplace integrations (e.g. `vercel install neon` for vault-managed databases)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger } from "@theholocron/cli";
|
|
2
|
+
|
|
3
|
+
//#region src/auth.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Token resolution for the Vercel plugin.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
9
|
+
* 2. HOLOCRON_VERCEL_TOKEN env var (preferred — explicit intent)
|
|
10
|
+
* 3. VERCEL_TOKEN env var (the default the Vercel CLI also reads)
|
|
11
|
+
*
|
|
12
|
+
* No `vercel auth` fallback — Vercel CLI auth is per-account-scoped
|
|
13
|
+
* and the resulting tokens don't always cover team operations.
|
|
14
|
+
*/
|
|
15
|
+
declare class AuthError extends Error {
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
interface ResolveTokenInput {
|
|
19
|
+
/** From `--token` CLI flag. */
|
|
20
|
+
cliToken?: string;
|
|
21
|
+
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
}
|
|
24
|
+
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/rest.d.ts
|
|
27
|
+
/**
|
|
28
|
+
* Thin REST wrapper around api.vercel.com.
|
|
29
|
+
*
|
|
30
|
+
* Differences from the GitHub REST client:
|
|
31
|
+
* - Vercel uses simple `Bearer <token>` (no api-version header)
|
|
32
|
+
* - Team scoping is a query-string param (`teamId=...`), not a path
|
|
33
|
+
* prefix; the client appends it to every URL when configured
|
|
34
|
+
* - 204 responses are honored the same way; transport failures get
|
|
35
|
+
* wrapped in `ProviderApiError` with `status: 0` for clear hint
|
|
36
|
+
* output
|
|
37
|
+
*/
|
|
38
|
+
interface RestClientOptions {
|
|
39
|
+
token: string;
|
|
40
|
+
/** Vercel team id. When set, scoped to that team for every request. */
|
|
41
|
+
teamId?: string;
|
|
42
|
+
fetch?: typeof fetch;
|
|
43
|
+
baseUrl?: string;
|
|
44
|
+
}
|
|
45
|
+
interface RequestOptions {
|
|
46
|
+
method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
47
|
+
body?: unknown;
|
|
48
|
+
/** Additional query-string params. */
|
|
49
|
+
query?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
declare class VercelRestClient {
|
|
52
|
+
private readonly token;
|
|
53
|
+
private readonly teamId?;
|
|
54
|
+
private readonly fetchImpl;
|
|
55
|
+
readonly baseUrl: string;
|
|
56
|
+
constructor(opts: RestClientOptions);
|
|
57
|
+
request<T>(path: string, opts?: RequestOptions): Promise<T>;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/capabilities/deployment.d.ts
|
|
61
|
+
interface DeploymentOptions {
|
|
62
|
+
/** Optional framework hint passed to project creates. Defaults to "nextjs". */
|
|
63
|
+
defaultFramework?: string;
|
|
64
|
+
}
|
|
65
|
+
declare class VercelDeployment implements Deployment {
|
|
66
|
+
private readonly rest;
|
|
67
|
+
readonly key: "deployment";
|
|
68
|
+
readonly providerName = "vercel";
|
|
69
|
+
private readonly defaultFramework;
|
|
70
|
+
constructor(rest: VercelRestClient, opts?: DeploymentOptions);
|
|
71
|
+
listProjects(): Promise<DeploymentProject[]>;
|
|
72
|
+
ensureProject(input: {
|
|
73
|
+
name: string;
|
|
74
|
+
framework?: string;
|
|
75
|
+
repo?: string;
|
|
76
|
+
rootDirectory?: string;
|
|
77
|
+
}): Promise<DeploymentProject>;
|
|
78
|
+
updateProjectSettings(projectId: string, settings: DeploymentProjectSettings): Promise<DeploymentProject>;
|
|
79
|
+
listEnvVars(projectId: string, target: DeploymentTarget): Promise<string[]>;
|
|
80
|
+
setEnvVar(projectId: string, target: DeploymentTarget, name: string, value: string): Promise<void>;
|
|
81
|
+
triggerDeployment(input: {
|
|
82
|
+
projectId: string;
|
|
83
|
+
branch: string;
|
|
84
|
+
target?: DeploymentTrigger;
|
|
85
|
+
}): Promise<DeploymentRecord>;
|
|
86
|
+
getDeployment(deploymentId: string): Promise<DeploymentRecord>;
|
|
87
|
+
/** GET project by name with 404→null soft-skip. */
|
|
88
|
+
private getProjectByName;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/index.d.ts
|
|
92
|
+
interface VercelPluginOptions extends ResolveTokenInput {
|
|
93
|
+
/** Vercel team id. Set when working with a team-owned project. */
|
|
94
|
+
teamId?: string;
|
|
95
|
+
/** Default framework slug for new project creates. Defaults to "nextjs". */
|
|
96
|
+
defaultFramework?: string;
|
|
97
|
+
/** Override base URL for tests. */
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
/** Override `fetch` for tests. */
|
|
100
|
+
fetch?: typeof fetch;
|
|
101
|
+
}
|
|
102
|
+
interface PluginContext {
|
|
103
|
+
options: VercelPluginOptions;
|
|
104
|
+
rest: VercelRestClient;
|
|
105
|
+
}
|
|
106
|
+
declare function createContext(options?: VercelPluginOptions): PluginContext;
|
|
107
|
+
declare function deployment(ctx: PluginContext): Deployment;
|
|
108
|
+
declare function createPlugin(options?: VercelPluginOptions): {
|
|
109
|
+
name: string;
|
|
110
|
+
capabilities: {
|
|
111
|
+
deployment: () => Deployment;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
export { AuthError, PluginContext, ResolveTokenInput, VercelDeployment, VercelPluginOptions, VercelRestClient, createContext, createPlugin, deployment, resolveToken };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { ProviderApiError } from "@theholocron/cli";
|
|
2
|
+
//#region src/auth.ts
|
|
3
|
+
/**
|
|
4
|
+
* Token resolution for the Vercel plugin.
|
|
5
|
+
*
|
|
6
|
+
* Resolution order:
|
|
7
|
+
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
8
|
+
* 2. HOLOCRON_VERCEL_TOKEN env var (preferred — explicit intent)
|
|
9
|
+
* 3. VERCEL_TOKEN env var (the default the Vercel CLI also reads)
|
|
10
|
+
*
|
|
11
|
+
* No `vercel auth` fallback — Vercel CLI auth is per-account-scoped
|
|
12
|
+
* and the resulting tokens don't always cover team operations.
|
|
13
|
+
*/
|
|
14
|
+
var AuthError = class extends Error {
|
|
15
|
+
name = "AuthError";
|
|
16
|
+
};
|
|
17
|
+
function resolveToken(input = {}) {
|
|
18
|
+
const env = input.env ?? process.env;
|
|
19
|
+
const token = input.cliToken || env.HOLOCRON_VERCEL_TOKEN || env.VERCEL_TOKEN;
|
|
20
|
+
if (!token) throw new AuthError("no Vercel token found. Pass --token <PAT>, or set HOLOCRON_VERCEL_TOKEN / VERCEL_TOKEN.");
|
|
21
|
+
return token;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/capabilities/deployment.ts
|
|
25
|
+
/**
|
|
26
|
+
* `deployment` capability for Vercel.
|
|
27
|
+
*
|
|
28
|
+
* Ported from rando-id/rando.id `packages/cli/src/adapters/vercel.ts`,
|
|
29
|
+
* adapted to the holocron `Deployment` interface:
|
|
30
|
+
*
|
|
31
|
+
* - `ensureProject` is the idempotent create (GET-then-POST), since
|
|
32
|
+
* `holocron setup` re-runs on every invocation
|
|
33
|
+
* - `setEnvVar` uses Vercel's `upsert=true` so it's idempotent
|
|
34
|
+
* create-or-update
|
|
35
|
+
* - `triggerDeployment` infers a branch preview when `target` is
|
|
36
|
+
* omitted; named targets ('production' / 'staging') opt into
|
|
37
|
+
* environment-scoped deploys
|
|
38
|
+
*
|
|
39
|
+
* Vercel encrypts env-var values at rest server-side; we send them as
|
|
40
|
+
* `type: 'encrypted'`. Unlike GitHub Actions secrets, there's no
|
|
41
|
+
* sealed-box / libsodium step on the client.
|
|
42
|
+
*/
|
|
43
|
+
var VercelDeployment = class {
|
|
44
|
+
rest;
|
|
45
|
+
key = "deployment";
|
|
46
|
+
providerName = "vercel";
|
|
47
|
+
defaultFramework;
|
|
48
|
+
constructor(rest, opts = {}) {
|
|
49
|
+
this.rest = rest;
|
|
50
|
+
this.defaultFramework = opts.defaultFramework ?? "nextjs";
|
|
51
|
+
}
|
|
52
|
+
async listProjects() {
|
|
53
|
+
return (await this.rest.request("/v10/projects")).projects.map(mapProject);
|
|
54
|
+
}
|
|
55
|
+
async ensureProject(input) {
|
|
56
|
+
const existing = await this.getProjectByName(input.name);
|
|
57
|
+
if (existing) return existing;
|
|
58
|
+
const body = {
|
|
59
|
+
name: input.name,
|
|
60
|
+
framework: input.framework ?? this.defaultFramework
|
|
61
|
+
};
|
|
62
|
+
if (input.repo) body.gitRepository = {
|
|
63
|
+
type: "github",
|
|
64
|
+
repo: input.repo
|
|
65
|
+
};
|
|
66
|
+
if (input.rootDirectory) body.rootDirectory = input.rootDirectory;
|
|
67
|
+
return mapProject(await this.rest.request("/v11/projects", {
|
|
68
|
+
method: "POST",
|
|
69
|
+
body
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
async updateProjectSettings(projectId, settings) {
|
|
73
|
+
const body = {};
|
|
74
|
+
if (settings.previewDeploymentsDisabled !== void 0) body.previewDeploymentsDisabled = settings.previewDeploymentsDisabled;
|
|
75
|
+
if (settings.gitProviderCreateDeployments !== void 0) body.gitProviderOptions = { createDeployments: settings.gitProviderCreateDeployments };
|
|
76
|
+
return mapProject(await this.rest.request(`/v9/projects/${encodeURIComponent(projectId)}`, {
|
|
77
|
+
method: "PATCH",
|
|
78
|
+
body
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
async listEnvVars(projectId, target) {
|
|
82
|
+
return (await this.rest.request(`/v9/projects/${encodeURIComponent(projectId)}/env`)).envs.filter((e) => e.target.includes(target)).map((e) => e.key);
|
|
83
|
+
}
|
|
84
|
+
async setEnvVar(projectId, target, name, value) {
|
|
85
|
+
await this.rest.request(`/v10/projects/${encodeURIComponent(projectId)}/env`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
query: { upsert: "true" },
|
|
88
|
+
body: {
|
|
89
|
+
key: name,
|
|
90
|
+
value,
|
|
91
|
+
target: [target],
|
|
92
|
+
type: "encrypted"
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async triggerDeployment(input) {
|
|
97
|
+
const project = await this.rest.request(`/v10/projects/${encodeURIComponent(input.projectId)}`);
|
|
98
|
+
const repoId = project.link?.repoId;
|
|
99
|
+
if (!repoId) throw new ProviderApiError(`Vercel project "${project.name}" has no linked GitHub repo — cannot trigger a deployment. Link the repo first.`, 400, void 0);
|
|
100
|
+
return mapDeployment(await this.rest.request("/v13/deployments", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: {
|
|
103
|
+
name: project.name,
|
|
104
|
+
gitSource: {
|
|
105
|
+
type: "github",
|
|
106
|
+
ref: input.branch,
|
|
107
|
+
repoId
|
|
108
|
+
},
|
|
109
|
+
...input.target ? { target: input.target } : {}
|
|
110
|
+
}
|
|
111
|
+
}), input.branch);
|
|
112
|
+
}
|
|
113
|
+
async getDeployment(deploymentId) {
|
|
114
|
+
const raw = await this.rest.request(`/v13/deployments/${encodeURIComponent(deploymentId)}`);
|
|
115
|
+
return mapDeployment(raw, raw.meta?.githubCommitRef ?? null);
|
|
116
|
+
}
|
|
117
|
+
/** GET project by name with 404→null soft-skip. */
|
|
118
|
+
async getProjectByName(name) {
|
|
119
|
+
try {
|
|
120
|
+
return mapProject(await this.rest.request(`/v10/projects/${encodeURIComponent(name)}`));
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (err instanceof ProviderApiError && err.status === 404) return null;
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
function mapProject(raw) {
|
|
128
|
+
const project = {
|
|
129
|
+
id: raw.id,
|
|
130
|
+
name: raw.name,
|
|
131
|
+
gitLinked: Boolean(raw.link?.repoId),
|
|
132
|
+
rootDirectory: raw.rootDirectory ?? null
|
|
133
|
+
};
|
|
134
|
+
if (raw.framework) project.framework = raw.framework;
|
|
135
|
+
return project;
|
|
136
|
+
}
|
|
137
|
+
function mapDeployment(raw, branch) {
|
|
138
|
+
const record = {
|
|
139
|
+
id: raw.id,
|
|
140
|
+
url: raw.url,
|
|
141
|
+
branch,
|
|
142
|
+
status: normalizeState(raw.readyState)
|
|
143
|
+
};
|
|
144
|
+
if (raw.target) record.target = raw.target;
|
|
145
|
+
return record;
|
|
146
|
+
}
|
|
147
|
+
function normalizeState(s) {
|
|
148
|
+
switch (s) {
|
|
149
|
+
case "INITIALIZING":
|
|
150
|
+
case "QUEUED": return "queued";
|
|
151
|
+
case "BUILDING": return "building";
|
|
152
|
+
case "READY": return "ready";
|
|
153
|
+
case "ERROR": return "error";
|
|
154
|
+
case "CANCELED": return "cancelled";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/rest.ts
|
|
159
|
+
/**
|
|
160
|
+
* Thin REST wrapper around api.vercel.com.
|
|
161
|
+
*
|
|
162
|
+
* Differences from the GitHub REST client:
|
|
163
|
+
* - Vercel uses simple `Bearer <token>` (no api-version header)
|
|
164
|
+
* - Team scoping is a query-string param (`teamId=...`), not a path
|
|
165
|
+
* prefix; the client appends it to every URL when configured
|
|
166
|
+
* - 204 responses are honored the same way; transport failures get
|
|
167
|
+
* wrapped in `ProviderApiError` with `status: 0` for clear hint
|
|
168
|
+
* output
|
|
169
|
+
*/
|
|
170
|
+
var VercelRestClient = class {
|
|
171
|
+
token;
|
|
172
|
+
teamId;
|
|
173
|
+
fetchImpl;
|
|
174
|
+
baseUrl;
|
|
175
|
+
constructor(opts) {
|
|
176
|
+
this.token = opts.token;
|
|
177
|
+
if (opts.teamId !== void 0) this.teamId = opts.teamId;
|
|
178
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
179
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.vercel.com").replace(/\/+$/, "");
|
|
180
|
+
}
|
|
181
|
+
async request(path, opts = {}) {
|
|
182
|
+
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
183
|
+
if (this.teamId) url.searchParams.set("teamId", this.teamId);
|
|
184
|
+
for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
|
|
185
|
+
const fullUrl = url.toString();
|
|
186
|
+
const headers = {
|
|
187
|
+
authorization: `Bearer ${this.token}`,
|
|
188
|
+
accept: "application/json"
|
|
189
|
+
};
|
|
190
|
+
const init = {
|
|
191
|
+
method: opts.method ?? "GET",
|
|
192
|
+
headers
|
|
193
|
+
};
|
|
194
|
+
if (opts.body !== void 0) {
|
|
195
|
+
headers["content-type"] = "application/json";
|
|
196
|
+
init.body = JSON.stringify(opts.body);
|
|
197
|
+
}
|
|
198
|
+
let res;
|
|
199
|
+
try {
|
|
200
|
+
res = await this.fetchImpl(fullUrl, init);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
const detail = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
203
|
+
throw new ProviderApiError(`Vercel ${init.method} ${path} failed: ${detail}`, 0, void 0);
|
|
204
|
+
}
|
|
205
|
+
if (!res.ok) {
|
|
206
|
+
const body = await res.text().catch(() => "");
|
|
207
|
+
throw new ProviderApiError(`Vercel ${init.method} ${path} → ${res.status}`, res.status, body);
|
|
208
|
+
}
|
|
209
|
+
if (res.status === 204) return void 0;
|
|
210
|
+
const text = await res.text();
|
|
211
|
+
if (!text) return void 0;
|
|
212
|
+
return JSON.parse(text);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/index.ts
|
|
217
|
+
function createContext(options = {}) {
|
|
218
|
+
const restOpts = { token: resolveToken(options) };
|
|
219
|
+
if (options.teamId !== void 0) restOpts.teamId = options.teamId;
|
|
220
|
+
if (options.baseUrl !== void 0) restOpts.baseUrl = options.baseUrl;
|
|
221
|
+
if (options.fetch !== void 0) restOpts.fetch = options.fetch;
|
|
222
|
+
return {
|
|
223
|
+
options,
|
|
224
|
+
rest: new VercelRestClient(restOpts)
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function deployment(ctx) {
|
|
228
|
+
const opts = {};
|
|
229
|
+
if (ctx.options.defaultFramework !== void 0) opts.defaultFramework = ctx.options.defaultFramework;
|
|
230
|
+
return new VercelDeployment(ctx.rest, opts);
|
|
231
|
+
}
|
|
232
|
+
function createPlugin(options = {}) {
|
|
233
|
+
const ctx = createContext(options);
|
|
234
|
+
return {
|
|
235
|
+
name: "@theholocron/holocron-plugin-vercel",
|
|
236
|
+
capabilities: { deployment: () => deployment(ctx) }
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
export { AuthError, VercelDeployment, VercelRestClient, createContext, createPlugin, deployment, resolveToken };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theholocron/holocron-plugin-vercel",
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
|
+
"description": "Holocron plugin for Vercel. Implements the deployment capability against the Vercel REST API.",
|
|
5
|
+
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-vercel#readme",
|
|
6
|
+
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/theholocron/holocron.git",
|
|
10
|
+
"directory": "packages/holocron-plugin-vercel"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Newton Koumantzelis",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.mjs",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@theholocron/cli": "2.0.0-alpha.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@theholocron/tsconfig": "^4.1.0",
|
|
28
|
+
"@tsconfig/node-lts": "^24.0.0",
|
|
29
|
+
"@vitest/coverage-v8": "^3.2.6",
|
|
30
|
+
"eslint": "^9.36.0",
|
|
31
|
+
"globals": "^16.5.0",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^3.2.6",
|
|
34
|
+
"tsdown": "^0.22.3",
|
|
35
|
+
"@theholocron/cli": "2.0.0-alpha.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:coverage": "vitest run --coverage"
|
|
51
|
+
},
|
|
52
|
+
"types": "./dist/index.d.mts"
|
|
53
|
+
}
|