@theholocron/holocron-plugin-vercel 2.0.0-alpha.5 → 2.0.0-alpha.51
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 +10 -4
- package/dist/index.d.mts +8 -59
- package/dist/index.mjs +28 -93
- package/package.json +14 -10
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- editorconfig-checker-disable-file -->
|
|
2
|
+
|
|
1
3
|
# `@theholocron/holocron-plugin-vercel`
|
|
2
4
|
|
|
3
5
|
Vercel plugin for [Holocron](../cli). Implements the `deployment`
|
|
@@ -5,8 +7,10 @@ capability against the [Vercel REST API](https://vercel.com/docs/rest-api).
|
|
|
5
7
|
|
|
6
8
|
## Install
|
|
7
9
|
|
|
10
|
+
<!-- prettier-ignore -->
|
|
8
11
|
```bash
|
|
9
12
|
pnpm add -D @theholocron/holocron-plugin-vercel@alpha
|
|
13
|
+
|
|
10
14
|
```
|
|
11
15
|
|
|
12
16
|
## Auth
|
|
@@ -23,17 +27,19 @@ always cover what holocron needs at the API level. Explicit token only.
|
|
|
23
27
|
|
|
24
28
|
## Config
|
|
25
29
|
|
|
30
|
+
<!-- prettier-ignore -->
|
|
26
31
|
```jsonc
|
|
27
32
|
// holocron.config.json
|
|
28
33
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
"providers": {
|
|
35
|
+
"deployment": ["vercel", { "teamId": "team_xxx" }],
|
|
36
|
+
},
|
|
32
37
|
}
|
|
38
|
+
|
|
33
39
|
```
|
|
34
40
|
|
|
35
41
|
- `teamId` (optional) — Vercel team id. When set, all requests are
|
|
36
|
-
|
|
42
|
+
scoped to that team. Leave unset for personal-account projects.
|
|
37
43
|
|
|
38
44
|
## Status
|
|
39
45
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,66 +1,15 @@
|
|
|
1
|
-
import { Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger } from "@theholocron/cli";
|
|
1
|
+
import { AuthError, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, ResolveTokenInput, RestClient, RestClient as RestClient$1 } from "@theholocron/cli";
|
|
2
2
|
|
|
3
3
|
//#region src/auth.d.ts
|
|
4
|
-
|
|
5
|
-
* Token resolution for the Vercel plugin.
|
|
6
|
-
*
|
|
7
|
-
* Resolution order (matches the standard 4-step precedence set by
|
|
8
|
-
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
9
|
-
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
10
|
-
* 2. HOLOCRON_VERCEL_TOKEN env var (preferred — explicit intent)
|
|
11
|
-
* 3. VERCEL_TOKEN env var (the default the Vercel CLI also reads)
|
|
12
|
-
* 4. keyring (com.theholocron.cli / "vercel")
|
|
13
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
14
|
-
*
|
|
15
|
-
* No `vercel auth` fallback — Vercel CLI auth is per-account-scoped
|
|
16
|
-
* and the resulting tokens don't always cover team operations.
|
|
17
|
-
*/
|
|
18
|
-
declare class AuthError extends Error {
|
|
19
|
-
name: string;
|
|
20
|
-
}
|
|
21
|
-
interface ResolveTokenInput {
|
|
22
|
-
/** From `--token` CLI flag. */
|
|
23
|
-
cliToken?: string;
|
|
24
|
-
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
25
|
-
env?: NodeJS.ProcessEnv;
|
|
26
|
-
/** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
|
|
27
|
-
keyring?: (provider: string) => string | null;
|
|
28
|
-
}
|
|
29
|
-
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
4
|
+
declare const resolveToken: (input?: import("@theholocron/http-client").ResolveTokenInput) => string;
|
|
30
5
|
//#endregion
|
|
31
6
|
//#region src/rest.d.ts
|
|
32
|
-
|
|
33
|
-
* Thin REST wrapper around api.vercel.com.
|
|
34
|
-
*
|
|
35
|
-
* Differences from the GitHub REST client:
|
|
36
|
-
* - Vercel uses simple `Bearer <token>` (no api-version header)
|
|
37
|
-
* - Team scoping is a query-string param (`teamId=...`), not a path
|
|
38
|
-
* prefix; the client appends it to every URL when configured
|
|
39
|
-
* - 204 responses are honored the same way; transport failures get
|
|
40
|
-
* wrapped in `ProviderApiError` with `status: 0` for clear hint
|
|
41
|
-
* output
|
|
42
|
-
*/
|
|
43
|
-
interface RestClientOptions {
|
|
7
|
+
declare function createVercelRestClient(opts: {
|
|
44
8
|
token: string;
|
|
45
|
-
/** Vercel team id. When set, scoped to that team for every request. */
|
|
46
9
|
teamId?: string;
|
|
47
|
-
fetch?: typeof fetch;
|
|
48
10
|
baseUrl?: string;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
52
|
-
body?: unknown;
|
|
53
|
-
/** Additional query-string params. */
|
|
54
|
-
query?: Record<string, string>;
|
|
55
|
-
}
|
|
56
|
-
declare class VercelRestClient {
|
|
57
|
-
private readonly token;
|
|
58
|
-
private readonly teamId?;
|
|
59
|
-
private readonly fetchImpl;
|
|
60
|
-
readonly baseUrl: string;
|
|
61
|
-
constructor(opts: RestClientOptions);
|
|
62
|
-
request<T>(path: string, opts?: RequestOptions): Promise<T>;
|
|
63
|
-
}
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
}): RestClient$1;
|
|
64
13
|
//#endregion
|
|
65
14
|
//#region src/capabilities/deployment.d.ts
|
|
66
15
|
interface DeploymentOptions {
|
|
@@ -72,7 +21,7 @@ declare class VercelDeployment implements Deployment {
|
|
|
72
21
|
readonly key: "deployment";
|
|
73
22
|
readonly providerName = "vercel";
|
|
74
23
|
private readonly defaultFramework;
|
|
75
|
-
constructor(rest:
|
|
24
|
+
constructor(rest: RestClient, opts?: DeploymentOptions);
|
|
76
25
|
listProjects(): Promise<DeploymentProject[]>;
|
|
77
26
|
ensureProject(input: {
|
|
78
27
|
name: string;
|
|
@@ -127,7 +76,7 @@ interface VercelPluginOptions extends ResolveTokenInput {
|
|
|
127
76
|
}
|
|
128
77
|
interface PluginContext {
|
|
129
78
|
options: VercelPluginOptions;
|
|
130
|
-
rest:
|
|
79
|
+
rest: RestClient;
|
|
131
80
|
}
|
|
132
81
|
declare function createContext(options?: VercelPluginOptions): PluginContext;
|
|
133
82
|
declare function deployment(ctx: PluginContext): Deployment;
|
|
@@ -145,4 +94,4 @@ declare function createPlugin(options?: VercelPluginOptions): {
|
|
|
145
94
|
*/
|
|
146
95
|
declare const AUTH_HINT: string;
|
|
147
96
|
//#endregion
|
|
148
|
-
export { AUTH_HINT, AuthError, PluginContext, ResolveTokenInput, VercelDeployment, VercelPluginOptions,
|
|
97
|
+
export { AUTH_HINT, AuthError, PluginContext, type ResolveTokenInput, VercelDeployment, VercelPluginOptions, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createPlugin, createVercelRestClient, deployment, resolveToken, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
import { ProviderApiError,
|
|
1
|
+
import { AuthError, ProviderApiError, createResolveToken, createRestClient } from "@theholocron/cli";
|
|
2
2
|
//#region src/auth.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* 2. HOLOCRON_VERCEL_TOKEN env var (preferred — explicit intent)
|
|
10
|
-
* 3. VERCEL_TOKEN env var (the default the Vercel CLI also reads)
|
|
11
|
-
* 4. keyring (com.theholocron.cli / "vercel")
|
|
12
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
13
|
-
*
|
|
14
|
-
* No `vercel auth` fallback — Vercel CLI auth is per-account-scoped
|
|
15
|
-
* and the resulting tokens don't always cover team operations.
|
|
16
|
-
*/
|
|
17
|
-
var AuthError = class extends Error {
|
|
18
|
-
name = "AuthError";
|
|
19
|
-
};
|
|
20
|
-
function resolveToken(input = {}) {
|
|
21
|
-
const env = input.env ?? process.env;
|
|
22
|
-
const keyring = input.keyring ?? getToken;
|
|
23
|
-
const token = input.cliToken || env["HOLOCRON_VERCEL_TOKEN"] || env["VERCEL_TOKEN"] || keyring("vercel");
|
|
24
|
-
if (!token) throw new AuthError("no Vercel token found. Pass --token <PAT>, set HOLOCRON_VERCEL_TOKEN / VERCEL_TOKEN, or run: holocron auth set vercel <PAT>");
|
|
25
|
-
return token;
|
|
26
|
-
}
|
|
3
|
+
const resolveToken = createResolveToken({
|
|
4
|
+
envName: "HOLOCRON_VERCEL_TOKEN",
|
|
5
|
+
vendorEnvName: "VERCEL_TOKEN",
|
|
6
|
+
keyringService: "vercel",
|
|
7
|
+
errorMessage: "no Vercel token found. Pass --token <PAT>, set HOLOCRON_VERCEL_TOKEN / VERCEL_TOKEN, or run: holocron auth set vercel <PAT>"
|
|
8
|
+
});
|
|
27
9
|
//#endregion
|
|
28
10
|
//#region src/capabilities/deployment.ts
|
|
29
11
|
/**
|
|
@@ -160,64 +142,15 @@ function normalizeState(s) {
|
|
|
160
142
|
}
|
|
161
143
|
//#endregion
|
|
162
144
|
//#region src/rest.ts
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
* output
|
|
173
|
-
*/
|
|
174
|
-
var VercelRestClient = class {
|
|
175
|
-
token;
|
|
176
|
-
teamId;
|
|
177
|
-
fetchImpl;
|
|
178
|
-
baseUrl;
|
|
179
|
-
constructor(opts) {
|
|
180
|
-
this.token = opts.token;
|
|
181
|
-
if (opts.teamId !== void 0) this.teamId = opts.teamId;
|
|
182
|
-
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
183
|
-
let url = opts.baseUrl ?? "https://api.vercel.com";
|
|
184
|
-
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
185
|
-
this.baseUrl = url;
|
|
186
|
-
}
|
|
187
|
-
async request(path, opts = {}) {
|
|
188
|
-
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
189
|
-
if (this.teamId) url.searchParams.set("teamId", this.teamId);
|
|
190
|
-
for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
|
|
191
|
-
const fullUrl = url.toString();
|
|
192
|
-
const headers = {
|
|
193
|
-
authorization: `Bearer ${this.token}`,
|
|
194
|
-
accept: "application/json"
|
|
195
|
-
};
|
|
196
|
-
const init = {
|
|
197
|
-
method: opts.method ?? "GET",
|
|
198
|
-
headers
|
|
199
|
-
};
|
|
200
|
-
if (opts.body !== void 0) {
|
|
201
|
-
headers["content-type"] = "application/json";
|
|
202
|
-
init.body = JSON.stringify(opts.body);
|
|
203
|
-
}
|
|
204
|
-
let res;
|
|
205
|
-
try {
|
|
206
|
-
res = await this.fetchImpl(fullUrl, init);
|
|
207
|
-
} catch (err) {
|
|
208
|
-
const detail = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
209
|
-
throw new ProviderApiError(`Vercel ${init.method} ${path} failed: ${detail}`, 0, void 0);
|
|
210
|
-
}
|
|
211
|
-
if (!res.ok) {
|
|
212
|
-
const body = await res.text().catch(() => "");
|
|
213
|
-
throw new ProviderApiError(`Vercel ${init.method} ${path} → ${res.status}`, res.status, body);
|
|
214
|
-
}
|
|
215
|
-
if (res.status === 204) return void 0;
|
|
216
|
-
const text = await res.text();
|
|
217
|
-
if (!text) return void 0;
|
|
218
|
-
return JSON.parse(text);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
145
|
+
function createVercelRestClient(opts) {
|
|
146
|
+
return createRestClient({
|
|
147
|
+
baseUrl: opts.baseUrl ?? "https://api.vercel.com",
|
|
148
|
+
token: opts.token,
|
|
149
|
+
defaultQuery: opts.teamId ? { teamId: opts.teamId } : void 0,
|
|
150
|
+
vendor: "Vercel",
|
|
151
|
+
fetch: opts.fetch
|
|
152
|
+
});
|
|
153
|
+
}
|
|
221
154
|
//#endregion
|
|
222
155
|
//#region src/verify-token.ts
|
|
223
156
|
/**
|
|
@@ -226,10 +159,11 @@ var VercelRestClient = class {
|
|
|
226
159
|
* response into the normalized `VerifyTokenResult` shape.
|
|
227
160
|
*/
|
|
228
161
|
async function verifyToken(token, opts = {}) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
162
|
+
const rest = createVercelRestClient({
|
|
163
|
+
token,
|
|
164
|
+
baseUrl: opts.baseUrl,
|
|
165
|
+
fetch: opts.fetch
|
|
166
|
+
});
|
|
233
167
|
try {
|
|
234
168
|
const res = await rest.request("/v2/user");
|
|
235
169
|
return {
|
|
@@ -246,13 +180,14 @@ async function verifyToken(token, opts = {}) {
|
|
|
246
180
|
//#endregion
|
|
247
181
|
//#region src/index.ts
|
|
248
182
|
function createContext(options = {}) {
|
|
249
|
-
const restOpts = { token: resolveToken(options) };
|
|
250
|
-
if (options.teamId !== void 0) restOpts.teamId = options.teamId;
|
|
251
|
-
if (options.baseUrl !== void 0) restOpts.baseUrl = options.baseUrl;
|
|
252
|
-
if (options.fetch !== void 0) restOpts.fetch = options.fetch;
|
|
253
183
|
return {
|
|
254
184
|
options,
|
|
255
|
-
rest:
|
|
185
|
+
rest: createVercelRestClient({
|
|
186
|
+
token: resolveToken(options),
|
|
187
|
+
teamId: options.teamId,
|
|
188
|
+
baseUrl: options.baseUrl,
|
|
189
|
+
fetch: options.fetch
|
|
190
|
+
})
|
|
256
191
|
};
|
|
257
192
|
}
|
|
258
193
|
function deployment(ctx) {
|
|
@@ -275,4 +210,4 @@ function createPlugin(options = {}) {
|
|
|
275
210
|
*/
|
|
276
211
|
const AUTH_HINT = "generate a Vercel Personal Access Token at https://vercel.com/account/tokens (scope: Full Account for team ops), then run: holocron auth set vercel <PAT>";
|
|
277
212
|
//#endregion
|
|
278
|
-
export { AUTH_HINT, AuthError, VercelDeployment,
|
|
213
|
+
export { AUTH_HINT, AuthError, VercelDeployment, createContext, createPlugin, createVercelRestClient, deployment, resolveToken, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-vercel",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.51",
|
|
4
4
|
"description": "Holocron plugin for Vercel. Implements the deployment capability against the Vercel REST API.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-vercel#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -21,19 +21,23 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@theholocron/cli": "2.0.0-alpha.
|
|
24
|
+
"@theholocron/cli": "2.0.0-alpha.51"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
27
|
+
"@types/node": "^26",
|
|
28
|
+
"@theholocron/eslint-config": "^7.0.0",
|
|
29
|
+
"@theholocron/tsconfig": "^7.0.0",
|
|
30
|
+
"@theholocron/tsdown-config": "^7.0.0",
|
|
31
|
+
"@theholocron/vitest-config": "^7.0.0",
|
|
32
|
+
"@vitest/eslint-plugin": "^1.6.23",
|
|
33
|
+
"eslint": "^10.7.0",
|
|
34
|
+
"eslint-plugin-n": "^18.2.2",
|
|
35
|
+
"globals": "^17.7.0",
|
|
34
36
|
"tsdown": "^0.22.3",
|
|
35
37
|
"tsx": "^4.22.4",
|
|
36
|
-
"
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"vitest": "^4.1.10",
|
|
40
|
+
"@theholocron/cli": "2.0.0-alpha.51"
|
|
37
41
|
},
|
|
38
42
|
"publishConfig": {
|
|
39
43
|
"access": "public"
|