@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.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +16 -0
  4. package/README.md +3 -1
  5. package/dist/{client/client.d.ts → api-client/api-client.d.ts} +31 -17
  6. package/dist/{client/client.js → api-client/api-client.js} +50 -44
  7. package/dist/{client → api-client}/base-client.d.ts +1 -1
  8. package/dist/{client → api-client}/base-client.js +3 -3
  9. package/dist/api-client/index.d.ts +1 -0
  10. package/dist/api-client/index.js +5 -0
  11. package/dist/{client → api-client}/validators.d.ts +20 -1
  12. package/dist/{client → api-client}/validators.js +9 -2
  13. package/dist/command.d.ts +127 -0
  14. package/dist/command.js +137 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +6 -7
  17. package/dist/sandbox.d.ts +201 -0
  18. package/dist/sandbox.js +174 -0
  19. package/dist/utils/decode-base64-url.d.ts +7 -0
  20. package/dist/utils/decode-base64-url.js +12 -0
  21. package/dist/utils/get-credentials.d.ts +26 -0
  22. package/dist/utils/get-credentials.js +84 -0
  23. package/dist/utils/get-vercel-oidc-token.d.ts +6 -0
  24. package/dist/utils/get-vercel-oidc-token.js +21 -0
  25. package/dist/version.d.ts +1 -1
  26. package/dist/version.js +1 -1
  27. package/package.json +10 -3
  28. package/src/api-client/api-client.ts +225 -0
  29. package/src/{client → api-client}/base-client.ts +1 -1
  30. package/src/api-client/index.ts +1 -0
  31. package/src/{client → api-client}/validators.ts +9 -1
  32. package/src/command.test.ts +51 -0
  33. package/src/command.ts +176 -0
  34. package/src/index.ts +2 -2
  35. package/src/sandbox.ts +309 -0
  36. package/src/utils/decode-base64-url.ts +14 -0
  37. package/src/utils/get-credentials.ts +113 -0
  38. package/src/utils/get-vercel-oidc-token.ts +31 -0
  39. package/src/version.ts +1 -1
  40. package/tsconfig.json +2 -1
  41. package/typedoc.json +7 -1
  42. package/vitest.config.ts +8 -0
  43. package/vitest.setup.ts +4 -0
  44. package/dist/create-sandbox.d.ts +0 -196
  45. package/dist/create-sandbox.js +0 -230
  46. package/dist/utils/deferred-generator.d.ts +0 -5
  47. package/dist/utils/deferred-generator.js +0 -32
  48. package/dist/utils/deferred.d.ts +0 -6
  49. package/dist/utils/deferred.js +0 -12
  50. package/src/client/client.ts +0 -186
  51. package/src/create-sandbox.ts +0 -294
  52. package/src/utils/deferred-generator.ts +0 -38
  53. package/src/utils/deferred.ts +0 -12
  54. /package/dist/{client → api-client}/api-error.d.ts +0 -0
  55. /package/dist/{client → api-client}/api-error.js +0 -0
  56. /package/dist/{client → api-client}/with-retry.d.ts +0 -0
  57. /package/dist/{client → api-client}/with-retry.js +0 -0
  58. /package/src/{client → api-client}/api-error.ts +0 -0
  59. /package/src/{client → api-client}/with-retry.ts +0 -0
@@ -1,230 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Command = exports.Sandbox = exports.SDK = void 0;
4
- const client_1 = require("./client/client");
5
- /**
6
- * SDK for interacting with the Sandbox API. Provides methods to create and
7
- * manage sandboxes configured with specific source code and network ports.
8
- *
9
- * Example:
10
- * ```ts
11
- * const sdk = new SDK({
12
- * teamId: process.env.VERCEL_TEAM_ID!,
13
- * token: process.env.VERCEL_TOKEN!,
14
- * });
15
- * ```
16
- *
17
- * @see {@link SDK.createSandbox} to start a new sandbox.
18
- */
19
- class SDK {
20
- /**
21
- * Create a new instance of `SDK`.
22
- *
23
- * @param config - Configuration options for the SDK.
24
- * @param config.teamId - The Vercel team ID used to scope the Sandbox.
25
- * @param config.token - The API token used for authentication.
26
- */
27
- constructor({ teamId, token }) {
28
- this.client = new client_1.SandboxClient({ teamId, token });
29
- }
30
- /**
31
- * Creates a new sandbox instance using the provided Git repository and exposed ports.
32
- *
33
- * The repository must be public. On start, the sandbox will clone the repository
34
- * and expose the specified ports for access.
35
- *
36
- * @param params - Configuration parameters for the sandbox.
37
- * @param params.source - The source of the sandbox, currently supports Git repositories only.
38
- * @param params.source.type - Type of source, must be `"git"`.
39
- * @param params.source.url - The URL of the public Git repository to clone.
40
- * @param config.projectId - The Vercel project ID used to link the Sandbox to.
41
- * @param params.ports - Array of port numbers to expose from the sandbox.
42
- * @param params.timeout - (Optional) Timeout in seconds before the sandbox auto-terminates.
43
- *
44
- * @returns A promise that resolves to a `Sandbox` instance.
45
- */
46
- async createSandbox(params) {
47
- const { client } = this;
48
- const sandbox = await client.createSandbox({
49
- source: params.source,
50
- projectId: params.projectId,
51
- ports: params.ports,
52
- timeout: params.timeout,
53
- });
54
- return new Sandbox({
55
- client,
56
- sandboxId: sandbox.json.sandboxId,
57
- routes: sandbox.json.routes,
58
- });
59
- }
60
- /** @hidden */
61
- async getSandbox({ routes, sandboxId, }) {
62
- return new Sandbox({
63
- client: this.client,
64
- sandboxId: sandboxId,
65
- routes: routes,
66
- });
67
- }
68
- }
69
- exports.SDK = SDK;
70
- /**
71
- * A Sandbox is an isolated Linux MicroVM that you can your experiments on.
72
- *
73
- * @see {@link SDK.createSandbox} to construct a Sandbox.
74
- * @hideconstructor
75
- */
76
- class Sandbox {
77
- constructor({ client, routes, sandboxId, }) {
78
- this.client = client;
79
- this.routes = routes;
80
- this.sandboxId = sandboxId;
81
- }
82
- /**
83
- * Start executing a command in this sandbox.
84
- * @param command
85
- * @param args
86
- * @returns
87
- */
88
- async runCommand(command, args = []) {
89
- const commandResponse = await this.client.runCommand({
90
- sandboxId: this.sandboxId,
91
- command,
92
- args,
93
- });
94
- return new Command({
95
- client: this.client,
96
- sandboxId: this.sandboxId,
97
- cmdId: commandResponse.json.cmdId,
98
- });
99
- }
100
- /**
101
- * Write files to the filesystem of this sandbox.
102
- */
103
- async writeFiles(files) {
104
- return this.client.writeFiles({
105
- sandboxId: this.sandboxId,
106
- files: files,
107
- });
108
- }
109
- /**
110
- * Get the public domain of a port of this sandbox.
111
- *
112
- * E.g. `2grza2l7imxe.vercel.run`
113
- */
114
- domain(p) {
115
- const route = this.routes.find(({ port }) => port == p);
116
- if (route) {
117
- return `https://${route.subdomain}.vercel.run`;
118
- }
119
- else {
120
- throw new Error(`No route for port ${p}`);
121
- }
122
- }
123
- }
124
- exports.Sandbox = Sandbox;
125
- /**
126
- * A command executed in a Sandbox.
127
- *
128
- * You can {@link wait} on commands to access their {@link exitCode}, and
129
- * iterate over their output with {@link logs}.
130
- *
131
- * @see {@link Sandbox.runCommand} to start a command.
132
- *
133
- * @hideconstructor
134
- */
135
- class Command {
136
- constructor({ client, sandboxId, cmdId, }) {
137
- this.client = client;
138
- this.sandboxId = sandboxId;
139
- this.cmdId = cmdId;
140
- this.exitCode = null;
141
- }
142
- /**
143
- * Iterate over the output of this command.
144
- *
145
- * ```
146
- * for await (const log of cmd.logs()) {
147
- * if (log.stream === "stdout") {
148
- * process.stdout.write(log.data);
149
- * } else {
150
- * process.stderr.write(log.data);
151
- * }
152
- * }
153
- * ```
154
- *
155
- * @see {@link Command.stdout}, {@link Command.stderr}, and {@link Command.output}
156
- * to access output as a string.
157
- */
158
- logs() {
159
- return this.client.getLogs({
160
- sandboxId: this.sandboxId,
161
- cmdId: this.cmdId,
162
- });
163
- }
164
- /**
165
- * Wait for a command to exit and populate it's exit code.
166
- *
167
- * ```
168
- * await cmd.wait()
169
- * if (cmd.exitCode != 0) {
170
- * console.error("Something went wrong...")
171
- * }
172
- * ````
173
- */
174
- async wait() {
175
- const command = await this.client.getCommand({
176
- sandboxId: this.sandboxId,
177
- cmdId: this.cmdId,
178
- wait: true,
179
- });
180
- this.exitCode = command.json.exitCode;
181
- return this;
182
- }
183
- /**
184
- * Print command logs to stdout/stderr
185
- */
186
- async printLogs() {
187
- for await (const log of this.logs()) {
188
- if (log.stream === "stdout") {
189
- process.stdout.write(log.data);
190
- }
191
- else {
192
- process.stderr.write(log.data);
193
- }
194
- }
195
- }
196
- /**
197
- * Get the output of `stdout` or `stderr` as a string.
198
- *
199
- * NOTE: This may error with string conversion errors if the command does
200
- * not ouptut valid unicode.
201
- */
202
- async output(stream = "both") {
203
- let data = "";
204
- for await (const log of this.logs()) {
205
- if (log.stream === stream) {
206
- data += log.data;
207
- }
208
- }
209
- return data;
210
- }
211
- /**
212
- * Get the output of `stdout` as a string.
213
- *
214
- * NOTE: This may error with string conversion errors if the command does
215
- * not ouptut valid unicode.
216
- */
217
- async stdout() {
218
- return this.output("stdout");
219
- }
220
- /**
221
- * Get the output of `stderr` as a string.
222
- *
223
- * NOTE: This may error with string conversion errors if the command does
224
- * not ouptut valid unicode.
225
- */
226
- async stderr() {
227
- return this.output("stderr");
228
- }
229
- }
230
- exports.Command = Command;
@@ -1,5 +0,0 @@
1
- export interface DeferredGenerator<T, R> {
2
- generator(): AsyncGenerator<T, R, void>;
3
- next: (value: IteratorResult<T | Promise<T>>) => void;
4
- }
5
- export declare function createDeferredGenerator<T, R>(): DeferredGenerator<T, R>;
@@ -1,32 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDeferredGenerator = createDeferredGenerator;
4
- const deferred_1 = require("./deferred");
5
- function createDeferredGenerator() {
6
- const deferreds = [new deferred_1.Deferred()];
7
- let currentIndex = 0;
8
- const next = (value) => {
9
- const currentDeferred = deferreds[currentIndex];
10
- if (!value.done) {
11
- deferreds.push(new deferred_1.Deferred());
12
- currentIndex++;
13
- }
14
- currentDeferred.resolve(value);
15
- };
16
- function generator() {
17
- let currentIndex = 0;
18
- return (async function* () {
19
- while (true) {
20
- const result = await deferreds[currentIndex].promise;
21
- if (result.done)
22
- return result.value;
23
- yield result.value;
24
- currentIndex++;
25
- }
26
- })();
27
- }
28
- return {
29
- generator,
30
- next,
31
- };
32
- }
@@ -1,6 +0,0 @@
1
- export declare class Deferred<T> {
2
- promise: Promise<T>;
3
- resolve: (value: T | PromiseLike<T>) => void;
4
- reject: (reason?: any) => void;
5
- constructor();
6
- }
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Deferred = void 0;
4
- class Deferred {
5
- constructor() {
6
- this.promise = new Promise((res, rej) => {
7
- this.resolve = res;
8
- this.reject = rej;
9
- });
10
- }
11
- }
12
- exports.Deferred = Deferred;
@@ -1,186 +0,0 @@
1
- import FormData from "form-data";
2
- import { APIClient, parseOrThrow, type RequestParams } from "./base-client";
3
- import {
4
- Command,
5
- CreatedCommand,
6
- CreatedSandbox,
7
- LogLine,
8
- WrittenFile,
9
- } from "./validators";
10
- import { Readable } from "stream";
11
- import { APIError } from "./api-error";
12
- import { createDeferredGenerator } from "../utils/deferred-generator";
13
- import { VERSION } from "../version";
14
- import { z } from "zod";
15
- import jsonlines from "jsonlines";
16
- import os from "os";
17
-
18
- export class SandboxClient extends APIClient {
19
- private teamId: string;
20
-
21
- constructor(params: { host?: string; teamId: string; token: string }) {
22
- super({
23
- host: params.host ?? "https://api.vercel.com",
24
- token: params.token,
25
- debug: false,
26
- });
27
-
28
- this.teamId = params.teamId;
29
- }
30
-
31
- protected async request(path: string, params?: RequestParams) {
32
- return super.request(path, {
33
- ...params,
34
- query: { teamId: this.teamId, ...params?.query },
35
- headers: {
36
- "content-type": "application/json",
37
- "user-agent": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,
38
- ...params?.headers,
39
- },
40
- });
41
- }
42
-
43
- async createSandbox(params: {
44
- ports: number[];
45
- projectId: string;
46
- source: { type: "git"; url: string };
47
- timeout?: number;
48
- }) {
49
- return parseOrThrow(
50
- CreatedSandbox,
51
- await this.request("/v1/sandboxes", {
52
- method: "POST",
53
- body: JSON.stringify({
54
- projectId: params.projectId,
55
- ports: params.ports,
56
- source: params.source,
57
- timeout: params.timeout,
58
- }),
59
- }),
60
- );
61
- }
62
-
63
- async runCommand(params: {
64
- sandboxId: string;
65
- cwd?: string;
66
- command: string;
67
- args: string[];
68
- }) {
69
- return parseOrThrow(
70
- CreatedCommand,
71
- await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
72
- method: "POST",
73
- body: JSON.stringify({
74
- command: params.command,
75
- args: params.args,
76
- cwd: params.cwd,
77
- }),
78
- }),
79
- );
80
- }
81
-
82
- async getCommand(params: {
83
- sandboxId: string;
84
- cmdId: string;
85
- wait?: boolean;
86
- }) {
87
- return parseOrThrow(
88
- Command,
89
- await this.request(
90
- `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
91
- { query: { wait: params.wait ? "true" : undefined } },
92
- ),
93
- );
94
- }
95
-
96
- async writeFiles(params: {
97
- sandboxId: string;
98
- files: { path: string; stream: Readable | Buffer }[];
99
- }) {
100
- const formData = new FormData();
101
-
102
- for (const file of params.files) {
103
- formData.append(file.path, file.stream, file.path);
104
- }
105
-
106
- await parseOrThrow(
107
- WrittenFile,
108
- await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
109
- method: "POST",
110
- headers: { ...formData.getHeaders() },
111
- body: formData,
112
- }),
113
- );
114
- }
115
-
116
- async readFile(params: {
117
- sandboxId: string;
118
- path: string;
119
- cwd?: string;
120
- }): Promise<NodeJS.ReadableStream | null> {
121
- const response = await this.request(
122
- `/v1/sandboxes/${params.sandboxId}/fs/read`,
123
- {
124
- method: "POST",
125
- body: JSON.stringify({ path: params.path, cwd: params.cwd }),
126
- },
127
- );
128
-
129
- if (response.status === 404) {
130
- return null;
131
- }
132
-
133
- return response.body;
134
- }
135
-
136
- getLogs(params: { sandboxId: string; cmdId: string }) {
137
- const deferred = createDeferredGenerator<z.infer<typeof LogLine>, void>();
138
-
139
- (async () => {
140
- const response = await this.request(
141
- `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`,
142
- { method: "GET" },
143
- );
144
-
145
- if (response.headers.get("content-type") !== "application/x-ndjson") {
146
- throw new APIError(response, {
147
- message: "Expected a stream of logs",
148
- });
149
- }
150
-
151
- const parser = jsonlines.parse();
152
- response.body.pipe(parser);
153
-
154
- parser.on("data", (data) => {
155
- const parsed = LogLine.safeParse(data);
156
- if (parsed.success) {
157
- deferred.next({
158
- value: parsed.data,
159
- done: false,
160
- });
161
- } else {
162
- deferred.next({
163
- value: Promise.reject(parsed.error),
164
- done: false,
165
- });
166
- }
167
- });
168
-
169
- parser.on("error", (err) => {
170
- deferred.next({
171
- value: Promise.reject(err),
172
- done: false,
173
- });
174
- });
175
-
176
- parser.on("end", () => {
177
- deferred.next({
178
- value: undefined,
179
- done: true,
180
- });
181
- });
182
- })();
183
-
184
- return deferred.generator();
185
- }
186
- }