@vercel/sandbox 0.0.4 → 0.0.6
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +16 -0
- package/README.md +3 -1
- package/dist/{client/client.d.ts → api-client/api-client.d.ts} +31 -17
- package/dist/{client/client.js → api-client/api-client.js} +50 -44
- package/dist/{client → api-client}/base-client.d.ts +1 -1
- package/dist/{client → api-client}/base-client.js +3 -3
- package/dist/api-client/index.d.ts +1 -0
- package/dist/api-client/index.js +5 -0
- package/dist/{client → api-client}/validators.d.ts +20 -1
- package/dist/{client → api-client}/validators.js +9 -2
- package/dist/command.d.ts +127 -0
- package/dist/command.js +137 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -7
- package/dist/sandbox.d.ts +201 -0
- package/dist/sandbox.js +174 -0
- package/dist/utils/decode-base64-url.d.ts +7 -0
- package/dist/utils/decode-base64-url.js +12 -0
- package/dist/utils/get-credentials.d.ts +26 -0
- package/dist/utils/get-credentials.js +84 -0
- package/dist/utils/get-vercel-oidc-token.d.ts +6 -0
- package/dist/utils/get-vercel-oidc-token.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -3
- package/src/api-client/api-client.ts +225 -0
- package/src/{client → api-client}/base-client.ts +1 -1
- package/src/api-client/index.ts +1 -0
- package/src/{client → api-client}/validators.ts +9 -1
- package/src/command.test.ts +51 -0
- package/src/command.ts +176 -0
- package/src/index.ts +2 -2
- package/src/sandbox.ts +309 -0
- package/src/utils/decode-base64-url.ts +14 -0
- package/src/utils/get-credentials.ts +113 -0
- package/src/utils/get-vercel-oidc-token.ts +31 -0
- package/src/version.ts +1 -1
- package/tsconfig.json +2 -1
- package/typedoc.json +7 -1
- package/vitest.config.ts +8 -0
- package/vitest.setup.ts +4 -0
- package/dist/create-sandbox.d.ts +0 -196
- package/dist/create-sandbox.js +0 -230
- package/dist/utils/deferred-generator.d.ts +0 -5
- package/dist/utils/deferred-generator.js +0 -32
- package/dist/utils/deferred.d.ts +0 -6
- package/dist/utils/deferred.js +0 -12
- package/src/client/client.ts +0 -186
- package/src/create-sandbox.ts +0 -294
- package/src/utils/deferred-generator.ts +0 -38
- package/src/utils/deferred.ts +0 -12
- /package/dist/{client → api-client}/api-error.d.ts +0 -0
- /package/dist/{client → api-client}/api-error.js +0 -0
- /package/dist/{client → api-client}/with-retry.d.ts +0 -0
- /package/dist/{client → api-client}/with-retry.js +0 -0
- /package/src/{client → api-client}/api-error.ts +0 -0
- /package/src/{client → api-client}/with-retry.ts +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Readable, type Writable } from "stream";
|
|
2
|
+
import { APIClient } from "./api-client";
|
|
3
|
+
import { Command, CommandFinished } from "./command";
|
|
4
|
+
import { type Credentials } from "./utils/get-credentials";
|
|
5
|
+
/** @inline */
|
|
6
|
+
export interface CreateSandboxParams {
|
|
7
|
+
/**
|
|
8
|
+
* The source of the sandbox.
|
|
9
|
+
*
|
|
10
|
+
* Omit this parameter start a sandbox without a source.
|
|
11
|
+
*/
|
|
12
|
+
source?: {
|
|
13
|
+
type: "git";
|
|
14
|
+
url: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: "tarball";
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Array of port numbers to expose from the sandbox.
|
|
21
|
+
*/
|
|
22
|
+
ports?: number[];
|
|
23
|
+
/**
|
|
24
|
+
* Timeout in milliseconds before the sandbox auto-terminates.
|
|
25
|
+
*/
|
|
26
|
+
timeout?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Resources to allocate to the sandbox.
|
|
29
|
+
*
|
|
30
|
+
* Your sandbox will get the amount of cores you specify here. For memory,
|
|
31
|
+
* you will get double the amount of cores.
|
|
32
|
+
*/
|
|
33
|
+
resources?: {
|
|
34
|
+
cores: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** @inline */
|
|
38
|
+
interface GetSandboxParams {
|
|
39
|
+
/**
|
|
40
|
+
* Port-to-subdomain route mappings.
|
|
41
|
+
*/
|
|
42
|
+
routes: Array<{
|
|
43
|
+
subdomain: string;
|
|
44
|
+
port: number;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Unique identifier of the sandbox.
|
|
48
|
+
*/
|
|
49
|
+
sandboxId: string;
|
|
50
|
+
}
|
|
51
|
+
/** @inline */
|
|
52
|
+
interface RunCommandParams {
|
|
53
|
+
/**
|
|
54
|
+
* The command to execute
|
|
55
|
+
*/
|
|
56
|
+
cmd: string;
|
|
57
|
+
/**
|
|
58
|
+
* Arguments to pass to the command
|
|
59
|
+
*/
|
|
60
|
+
args?: string[];
|
|
61
|
+
/**
|
|
62
|
+
* Working directory to execute the command in
|
|
63
|
+
*/
|
|
64
|
+
cwd?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Environment variables to set for this command
|
|
67
|
+
*/
|
|
68
|
+
env?: Record<string, string>;
|
|
69
|
+
/**
|
|
70
|
+
* If true, the command will return without waiting for `exitCode`
|
|
71
|
+
*/
|
|
72
|
+
detached?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* A `Writable` stream where `stdout` from the command will be piped
|
|
75
|
+
*/
|
|
76
|
+
stdout?: Writable;
|
|
77
|
+
/**
|
|
78
|
+
* A `Writable` stream where `stderr` from the command will be piped
|
|
79
|
+
*/
|
|
80
|
+
stderr?: Writable;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A Sandbox is an isolated Linux MicroVM to run commands in.
|
|
84
|
+
*
|
|
85
|
+
* Use {@link Sandbox.create} or {@link Sandbox.get} to construct.
|
|
86
|
+
* @hideconstructor
|
|
87
|
+
*/
|
|
88
|
+
export declare class Sandbox {
|
|
89
|
+
private readonly client;
|
|
90
|
+
/**
|
|
91
|
+
* Routes from ports to subdomains.
|
|
92
|
+
/* @hidden
|
|
93
|
+
*/
|
|
94
|
+
readonly routes: {
|
|
95
|
+
subdomain: string;
|
|
96
|
+
port: number;
|
|
97
|
+
}[];
|
|
98
|
+
/**
|
|
99
|
+
* Unique ID of this sandbox.
|
|
100
|
+
*/
|
|
101
|
+
readonly sandboxId: string;
|
|
102
|
+
/**
|
|
103
|
+
* Create a new sandbox.
|
|
104
|
+
*
|
|
105
|
+
* @param params - Creation parameters and optional credentials.
|
|
106
|
+
* @returns A promise resolving to the created {@link Sandbox}.
|
|
107
|
+
*/
|
|
108
|
+
static create(params?: CreateSandboxParams | (CreateSandboxParams & Credentials)): Promise<Sandbox>;
|
|
109
|
+
/**
|
|
110
|
+
* Retrieve an existing sandbox.
|
|
111
|
+
*
|
|
112
|
+
* @param params - Get parameters and optional credentials.
|
|
113
|
+
* @returns A promise resolving to the {@link Sandbox}.
|
|
114
|
+
*/
|
|
115
|
+
static get(params: GetSandboxParams | (GetSandboxParams & Credentials)): Promise<Sandbox>;
|
|
116
|
+
/**
|
|
117
|
+
* Create a new Sandbox instance.
|
|
118
|
+
*
|
|
119
|
+
* @param client - API client used to communicate with the backend
|
|
120
|
+
* @param routes - Port-to-subdomain mappings for exposed ports
|
|
121
|
+
* @param sandboxId - Unique identifier for the sandbox
|
|
122
|
+
*/
|
|
123
|
+
constructor({ client, routes, sandboxId, }: {
|
|
124
|
+
client: APIClient;
|
|
125
|
+
routes: {
|
|
126
|
+
subdomain: string;
|
|
127
|
+
port: number;
|
|
128
|
+
}[];
|
|
129
|
+
sandboxId: string;
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* Get a previously run command by its ID.
|
|
133
|
+
*
|
|
134
|
+
* @param cmdId - ID of the command to retrieve
|
|
135
|
+
* @returns A {@link Command} instance representing the command
|
|
136
|
+
*/
|
|
137
|
+
getCommand(cmdId: string): Command;
|
|
138
|
+
/**
|
|
139
|
+
* Start executing a command in this sandbox.
|
|
140
|
+
*
|
|
141
|
+
* @param command - The command to execute.
|
|
142
|
+
* @param args - Arguments to pass to the command.
|
|
143
|
+
* @returns A {@link CommandFinished} result once execution is done.
|
|
144
|
+
*/
|
|
145
|
+
runCommand(command: string, args?: string[]): Promise<CommandFinished>;
|
|
146
|
+
/**
|
|
147
|
+
* Start executing a command in detached mode.
|
|
148
|
+
*
|
|
149
|
+
* @param params - The command parameters.
|
|
150
|
+
* @returns A {@link Command} instance for the running command.
|
|
151
|
+
*/
|
|
152
|
+
runCommand(params: RunCommandParams & {
|
|
153
|
+
detached: true;
|
|
154
|
+
}): Promise<Command>;
|
|
155
|
+
/**
|
|
156
|
+
* Start executing a command in this sandbox.
|
|
157
|
+
*
|
|
158
|
+
* @param params - The command parameters.
|
|
159
|
+
* @returns A {@link CommandFinished} result once execution is done.
|
|
160
|
+
*/
|
|
161
|
+
runCommand(params: RunCommandParams): Promise<CommandFinished>;
|
|
162
|
+
/**
|
|
163
|
+
* Internal helper to start a command in the sandbox.
|
|
164
|
+
*
|
|
165
|
+
* @param params - Command execution parameters.
|
|
166
|
+
* @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
_runCommand(params: RunCommandParams): Promise<CommandFinished | Command>;
|
|
170
|
+
/**
|
|
171
|
+
* Create a directory in the filesystem of this sandbox.
|
|
172
|
+
*
|
|
173
|
+
* @param path - Path of the directory to create
|
|
174
|
+
*/
|
|
175
|
+
mkDir(path: string): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Write files to the filesystem of this sandbox.
|
|
178
|
+
*
|
|
179
|
+
* @param files - Array of files with path and stream/buffer contents
|
|
180
|
+
* @returns A promise that resolves when the files are written
|
|
181
|
+
*/
|
|
182
|
+
writeFiles(files: {
|
|
183
|
+
path: string;
|
|
184
|
+
stream: Readable | Buffer;
|
|
185
|
+
}[]): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Get the public domain of a port of this sandbox.
|
|
188
|
+
*
|
|
189
|
+
* @param p - Port number to resolve
|
|
190
|
+
* @returns A full domain (e.g. `https://subdomain.vercel.run`)
|
|
191
|
+
* @throws If the port has no associated route
|
|
192
|
+
*/
|
|
193
|
+
domain(p: number): string;
|
|
194
|
+
/**
|
|
195
|
+
* Stop the sandbox.
|
|
196
|
+
*
|
|
197
|
+
* @returns A promise that resolves when the sandbox is stopped
|
|
198
|
+
*/
|
|
199
|
+
stop(): Promise<void>;
|
|
200
|
+
}
|
|
201
|
+
export {};
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Sandbox = void 0;
|
|
4
|
+
const api_client_1 = require("./api-client");
|
|
5
|
+
const command_1 = require("./command");
|
|
6
|
+
const get_credentials_1 = require("./utils/get-credentials");
|
|
7
|
+
/**
|
|
8
|
+
* A Sandbox is an isolated Linux MicroVM to run commands in.
|
|
9
|
+
*
|
|
10
|
+
* Use {@link Sandbox.create} or {@link Sandbox.get} to construct.
|
|
11
|
+
* @hideconstructor
|
|
12
|
+
*/
|
|
13
|
+
class Sandbox {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new sandbox.
|
|
16
|
+
*
|
|
17
|
+
* @param params - Creation parameters and optional credentials.
|
|
18
|
+
* @returns A promise resolving to the created {@link Sandbox}.
|
|
19
|
+
*/
|
|
20
|
+
static async create(params) {
|
|
21
|
+
const credentials = (0, get_credentials_1.getCredentials)(params);
|
|
22
|
+
const client = new api_client_1.APIClient({
|
|
23
|
+
teamId: credentials.teamId,
|
|
24
|
+
token: credentials.token,
|
|
25
|
+
});
|
|
26
|
+
const sandbox = await client.createSandbox({
|
|
27
|
+
source: params?.source,
|
|
28
|
+
projectId: credentials.projectId,
|
|
29
|
+
ports: params?.ports ?? [],
|
|
30
|
+
timeout: params?.timeout,
|
|
31
|
+
...(params?.resources && {
|
|
32
|
+
cores: params.resources.cores,
|
|
33
|
+
memory: 2048 * params.resources.cores,
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
return new Sandbox({
|
|
37
|
+
client,
|
|
38
|
+
sandboxId: sandbox.json.sandboxId,
|
|
39
|
+
routes: sandbox.json.routes,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Retrieve an existing sandbox.
|
|
44
|
+
*
|
|
45
|
+
* @param params - Get parameters and optional credentials.
|
|
46
|
+
* @returns A promise resolving to the {@link Sandbox}.
|
|
47
|
+
*/
|
|
48
|
+
static async get(params) {
|
|
49
|
+
const credentials = (0, get_credentials_1.getCredentials)(params);
|
|
50
|
+
const client = new api_client_1.APIClient({
|
|
51
|
+
teamId: credentials.teamId,
|
|
52
|
+
token: credentials.token,
|
|
53
|
+
});
|
|
54
|
+
return new Sandbox({
|
|
55
|
+
client,
|
|
56
|
+
sandboxId: params.sandboxId,
|
|
57
|
+
routes: params.routes,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a new Sandbox instance.
|
|
62
|
+
*
|
|
63
|
+
* @param client - API client used to communicate with the backend
|
|
64
|
+
* @param routes - Port-to-subdomain mappings for exposed ports
|
|
65
|
+
* @param sandboxId - Unique identifier for the sandbox
|
|
66
|
+
*/
|
|
67
|
+
constructor({ client, routes, sandboxId, }) {
|
|
68
|
+
this.client = client;
|
|
69
|
+
this.routes = routes;
|
|
70
|
+
this.sandboxId = sandboxId;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get a previously run command by its ID.
|
|
74
|
+
*
|
|
75
|
+
* @param cmdId - ID of the command to retrieve
|
|
76
|
+
* @returns A {@link Command} instance representing the command
|
|
77
|
+
*/
|
|
78
|
+
getCommand(cmdId) {
|
|
79
|
+
return new command_1.Command({
|
|
80
|
+
client: this.client,
|
|
81
|
+
sandboxId: this.sandboxId,
|
|
82
|
+
cmdId,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async runCommand(commandOrParams, args) {
|
|
86
|
+
return typeof commandOrParams === "string"
|
|
87
|
+
? this._runCommand({ cmd: commandOrParams, args })
|
|
88
|
+
: this._runCommand(commandOrParams);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Internal helper to start a command in the sandbox.
|
|
92
|
+
*
|
|
93
|
+
* @param params - Command execution parameters.
|
|
94
|
+
* @returns A {@link Command} or {@link CommandFinished}, depending on `detached`.
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
async _runCommand(params) {
|
|
98
|
+
const commandResponse = await this.client.runCommand({
|
|
99
|
+
sandboxId: this.sandboxId,
|
|
100
|
+
command: params.cmd,
|
|
101
|
+
args: params.args ?? [],
|
|
102
|
+
cwd: params.cwd,
|
|
103
|
+
env: params.env ?? {},
|
|
104
|
+
});
|
|
105
|
+
const command = new command_1.Command({
|
|
106
|
+
client: this.client,
|
|
107
|
+
sandboxId: this.sandboxId,
|
|
108
|
+
cmdId: commandResponse.json.cmdId,
|
|
109
|
+
});
|
|
110
|
+
if (params.stdout || params.stderr) {
|
|
111
|
+
(async () => {
|
|
112
|
+
for await (const log of command.logs()) {
|
|
113
|
+
if (log.stream === "stdout") {
|
|
114
|
+
params.stdout?.write(log.data);
|
|
115
|
+
}
|
|
116
|
+
else if (log.stream === "stderr") {
|
|
117
|
+
params.stderr?.write(log.data);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})();
|
|
121
|
+
}
|
|
122
|
+
return params.detached ? command : command.wait();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a directory in the filesystem of this sandbox.
|
|
126
|
+
*
|
|
127
|
+
* @param path - Path of the directory to create
|
|
128
|
+
*/
|
|
129
|
+
async mkDir(path) {
|
|
130
|
+
await this.client.mkDir({
|
|
131
|
+
sandboxId: this.sandboxId,
|
|
132
|
+
path: path,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Write files to the filesystem of this sandbox.
|
|
137
|
+
*
|
|
138
|
+
* @param files - Array of files with path and stream/buffer contents
|
|
139
|
+
* @returns A promise that resolves when the files are written
|
|
140
|
+
*/
|
|
141
|
+
async writeFiles(files) {
|
|
142
|
+
return this.client.writeFiles({
|
|
143
|
+
sandboxId: this.sandboxId,
|
|
144
|
+
files: files,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get the public domain of a port of this sandbox.
|
|
149
|
+
*
|
|
150
|
+
* @param p - Port number to resolve
|
|
151
|
+
* @returns A full domain (e.g. `https://subdomain.vercel.run`)
|
|
152
|
+
* @throws If the port has no associated route
|
|
153
|
+
*/
|
|
154
|
+
domain(p) {
|
|
155
|
+
const route = this.routes.find(({ port }) => port == p);
|
|
156
|
+
if (route) {
|
|
157
|
+
return `https://${route.subdomain}.vercel.run`;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
throw new Error(`No route for port ${p}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Stop the sandbox.
|
|
165
|
+
*
|
|
166
|
+
* @returns A promise that resolves when the sandbox is stopped
|
|
167
|
+
*/
|
|
168
|
+
async stop() {
|
|
169
|
+
await this.client.stopSandbox({
|
|
170
|
+
sandboxId: this.sandboxId,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.Sandbox = Sandbox;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeBase64Url = decodeBase64Url;
|
|
4
|
+
/**
|
|
5
|
+
* Decode a Base64 URL-encoded string into a JSON object.
|
|
6
|
+
*
|
|
7
|
+
* @param base64Url - The Base64 URL-encoded string to decode.
|
|
8
|
+
* @returns The decoded JSON object or null if decoding fails.
|
|
9
|
+
*/
|
|
10
|
+
function decodeBase64Url(base64Url) {
|
|
11
|
+
return JSON.parse(Buffer.from(base64Url.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8"));
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface Credentials {
|
|
2
|
+
/**
|
|
3
|
+
* Authentication token for the Vercel API. It could be an OIDC token
|
|
4
|
+
* or a personal access token.
|
|
5
|
+
*/
|
|
6
|
+
token: string;
|
|
7
|
+
/**
|
|
8
|
+
* The ID of the project to associate Sandbox operations.
|
|
9
|
+
*/
|
|
10
|
+
projectId: string;
|
|
11
|
+
/**
|
|
12
|
+
* The ID of the team to associate Sandbox operations.
|
|
13
|
+
*/
|
|
14
|
+
teamId: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Allow to get credentials to access the Vercel API. Credentials can be
|
|
18
|
+
* provided in two different ways:
|
|
19
|
+
*
|
|
20
|
+
* 1. By passing an object with the `teamId`, `token`, and `projectId` properties.
|
|
21
|
+
* 2. By using an environment variable VERCEL_OIDC_TOKEN.
|
|
22
|
+
*
|
|
23
|
+
* If both methods are used, the object properties take precedence over the
|
|
24
|
+
* environment variable. If neither method is used, an error is thrown.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getCredentials<T>(params?: T | Credentials): Credentials;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCredentials = getCredentials;
|
|
4
|
+
const get_vercel_oidc_token_1 = require("./get-vercel-oidc-token");
|
|
5
|
+
const decode_base64_url_1 = require("./decode-base64-url");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
/**
|
|
8
|
+
* Allow to get credentials to access the Vercel API. Credentials can be
|
|
9
|
+
* provided in two different ways:
|
|
10
|
+
*
|
|
11
|
+
* 1. By passing an object with the `teamId`, `token`, and `projectId` properties.
|
|
12
|
+
* 2. By using an environment variable VERCEL_OIDC_TOKEN.
|
|
13
|
+
*
|
|
14
|
+
* If both methods are used, the object properties take precedence over the
|
|
15
|
+
* environment variable. If neither method is used, an error is thrown.
|
|
16
|
+
*/
|
|
17
|
+
function getCredentials(params) {
|
|
18
|
+
const credentials = getCredentialsFromParams(params ?? {});
|
|
19
|
+
if (credentials) {
|
|
20
|
+
return credentials;
|
|
21
|
+
}
|
|
22
|
+
const oidcToken = (0, get_vercel_oidc_token_1.getVercelOidcToken)();
|
|
23
|
+
if (oidcToken) {
|
|
24
|
+
return getCredentialsFromOIDCToken(oidcToken);
|
|
25
|
+
}
|
|
26
|
+
throw new Error("You must provide credentials to access the Vercel API \n" +
|
|
27
|
+
"either through parameters or using OpenID Connect [https://vercel.com/docs/oidc]");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Attempt to extract credentials from the provided parameters. Either all
|
|
31
|
+
* required fields (`token`, `teamId`, and `projectId`) must be present
|
|
32
|
+
* or none of them, otherwise an error is thrown.
|
|
33
|
+
*/
|
|
34
|
+
function getCredentialsFromParams(params) {
|
|
35
|
+
const missing = [
|
|
36
|
+
"token" in params && typeof params.token === "string" ? null : "token",
|
|
37
|
+
"teamId" in params && typeof params.teamId === "string" ? null : "teamId",
|
|
38
|
+
"projectId" in params && typeof params.projectId === "string"
|
|
39
|
+
? null
|
|
40
|
+
: "projectId",
|
|
41
|
+
].filter((value) => value !== null);
|
|
42
|
+
if (missing.length === 0) {
|
|
43
|
+
return {
|
|
44
|
+
token: params.token,
|
|
45
|
+
projectId: params.projectId,
|
|
46
|
+
teamId: params.teamId,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (missing.length < 3) {
|
|
50
|
+
throw new Error(`Missing credentials parameters to access the Vercel API: ${missing
|
|
51
|
+
.filter((value) => value !== null)
|
|
52
|
+
.join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Schema to validate the payload of the Vercel OIDC token where we expect
|
|
58
|
+
* to find the `teamId` and `projectId`.
|
|
59
|
+
*/
|
|
60
|
+
const schema = zod_1.z.object({
|
|
61
|
+
owner_id: zod_1.z.string(),
|
|
62
|
+
project_id: zod_1.z.string(),
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Extracts credentials from a Vercel OIDC token. The token is expected to be
|
|
66
|
+
* a JWT with a payload that contains `project_id` and `owner_id`.
|
|
67
|
+
*
|
|
68
|
+
* @param token - The Vercel OIDC token.
|
|
69
|
+
* @returns An object containing the token, projectId, and teamId.
|
|
70
|
+
* @throws If the token is invalid or does not contain the required fields.
|
|
71
|
+
*/
|
|
72
|
+
function getCredentialsFromOIDCToken(token) {
|
|
73
|
+
try {
|
|
74
|
+
const payload = schema.parse((0, decode_base64_url_1.decodeBase64Url)(token.split(".")[1]));
|
|
75
|
+
return {
|
|
76
|
+
token,
|
|
77
|
+
projectId: payload.project_id,
|
|
78
|
+
teamId: payload.owner_id,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new Error(`Invalid Vercel OIDC token: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This function is implemented in @vercel/functions but it is asynchronous.
|
|
3
|
+
* In order to keep it synchronous, we are implementing it here as well.
|
|
4
|
+
* Ideally we should remove this and use the one from @vercel/functions.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getVercelOidcToken(): string | undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVercelOidcToken = getVercelOidcToken;
|
|
4
|
+
/**
|
|
5
|
+
* This function is implemented in @vercel/functions but it is asynchronous.
|
|
6
|
+
* In order to keep it synchronous, we are implementing it here as well.
|
|
7
|
+
* Ideally we should remove this and use the one from @vercel/functions.
|
|
8
|
+
*/
|
|
9
|
+
function getVercelOidcToken() {
|
|
10
|
+
const token = getContext().headers?.["x-vercel-oidc-token"] ??
|
|
11
|
+
process.env.VERCEL_OIDC_TOKEN;
|
|
12
|
+
if (!token && process.env.NODE_ENV === "production") {
|
|
13
|
+
throw new Error(`The 'x-vercel-oidc-token' header is missing from the request. Do you have the OIDC option enabled in the Vercel project settings?`);
|
|
14
|
+
}
|
|
15
|
+
return token;
|
|
16
|
+
}
|
|
17
|
+
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
18
|
+
function getContext() {
|
|
19
|
+
const fromSymbol = globalThis;
|
|
20
|
+
return fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.() ?? {};
|
|
21
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.0.
|
|
1
|
+
export declare const VERSION = "0.0.6";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/sandbox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"private": false,
|
|
7
8
|
"keywords": [],
|
|
8
9
|
"author": "",
|
|
9
10
|
"license": "ISC",
|
|
@@ -11,20 +12,26 @@
|
|
|
11
12
|
"async-retry": "1.3.3",
|
|
12
13
|
"form-data": "3.0.0",
|
|
13
14
|
"jsonlines": "0.1.1",
|
|
15
|
+
"lru-cache": "11.1.0",
|
|
16
|
+
"ms": "2.1.3",
|
|
14
17
|
"node-fetch": "2.6.11",
|
|
15
18
|
"zod": "3.24.4"
|
|
16
19
|
},
|
|
17
20
|
"devDependencies": {
|
|
18
21
|
"@types/async-retry": "1.4.9",
|
|
19
22
|
"@types/jsonlines": "0.1.5",
|
|
23
|
+
"@types/ms": "2.1.0",
|
|
20
24
|
"@types/node": "22.15.12",
|
|
21
25
|
"@types/node-fetch": "2.6.12",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
26
|
+
"dotenv": "16.5.0",
|
|
27
|
+
"typedoc": "0.28.5",
|
|
28
|
+
"typescript": "5.8.3",
|
|
29
|
+
"vitest": "3.2.1"
|
|
24
30
|
},
|
|
25
31
|
"scripts": {
|
|
26
32
|
"clean": "rm -rf node_modules dist",
|
|
27
33
|
"build": "tsc",
|
|
34
|
+
"test": "vitest run",
|
|
28
35
|
"typedoc": "typedoc",
|
|
29
36
|
"typecheck": "tsc --noEmit",
|
|
30
37
|
"inject-version": "ts-node scripts/inject-version.ts"
|