@vercel/sandbox 1.1.9 → 1.2.1

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 (66) hide show
  1. package/dist/api-client/api-client.d.ts +18 -13
  2. package/dist/api-client/api-client.js +46 -0
  3. package/dist/api-client/api-client.js.map +1 -1
  4. package/dist/api-client/validators.d.ts +1 -0
  5. package/dist/api-client/validators.js.map +1 -1
  6. package/dist/sandbox.d.ts +39 -5
  7. package/dist/sandbox.js +111 -23
  8. package/dist/sandbox.js.map +1 -1
  9. package/dist/version.d.ts +1 -1
  10. package/dist/version.js +1 -1
  11. package/package.json +6 -1
  12. package/.turbo/turbo-build.log +0 -4
  13. package/.turbo/turbo-test.log +0 -24
  14. package/.turbo/turbo-typecheck.log +0 -4
  15. package/CHANGELOG.md +0 -267
  16. package/__mocks__/picocolors.ts +0 -13
  17. package/scripts/inject-version.ts +0 -11
  18. package/src/api-client/api-client.test.ts +0 -176
  19. package/src/api-client/api-client.ts +0 -520
  20. package/src/api-client/api-error.ts +0 -46
  21. package/src/api-client/base-client.ts +0 -171
  22. package/src/api-client/file-writer.ts +0 -90
  23. package/src/api-client/index.ts +0 -2
  24. package/src/api-client/validators.ts +0 -144
  25. package/src/api-client/with-retry.ts +0 -131
  26. package/src/auth/api.ts +0 -31
  27. package/src/auth/error.ts +0 -8
  28. package/src/auth/file.ts +0 -69
  29. package/src/auth/index.ts +0 -9
  30. package/src/auth/infer-scope.test.ts +0 -178
  31. package/src/auth/linked-project.test.ts +0 -86
  32. package/src/auth/linked-project.ts +0 -40
  33. package/src/auth/oauth.ts +0 -333
  34. package/src/auth/poll-for-token.ts +0 -89
  35. package/src/auth/project.ts +0 -92
  36. package/src/auth/zod.ts +0 -16
  37. package/src/command.test.ts +0 -103
  38. package/src/command.ts +0 -287
  39. package/src/constants.ts +0 -1
  40. package/src/index.ts +0 -4
  41. package/src/sandbox.test.ts +0 -93
  42. package/src/sandbox.ts +0 -572
  43. package/src/snapshot.ts +0 -110
  44. package/src/utils/array.ts +0 -15
  45. package/src/utils/consume-readable.ts +0 -12
  46. package/src/utils/decode-base64-url.ts +0 -14
  47. package/src/utils/dev-credentials.test.ts +0 -217
  48. package/src/utils/dev-credentials.ts +0 -196
  49. package/src/utils/get-credentials.test.ts +0 -20
  50. package/src/utils/get-credentials.ts +0 -183
  51. package/src/utils/jwt-expiry.test.ts +0 -125
  52. package/src/utils/jwt-expiry.ts +0 -105
  53. package/src/utils/log.ts +0 -20
  54. package/src/utils/normalizePath.test.ts +0 -114
  55. package/src/utils/normalizePath.ts +0 -33
  56. package/src/utils/resolveSignal.ts +0 -24
  57. package/src/utils/types.test.js +0 -7
  58. package/src/utils/types.ts +0 -23
  59. package/src/version.ts +0 -2
  60. package/test-utils/mock-response.ts +0 -12
  61. package/tsconfig.json +0 -16
  62. package/turbo.json +0 -9
  63. package/typedoc.json +0 -13
  64. package/vercel.json +0 -9
  65. package/vitest.config.ts +0 -9
  66. package/vitest.setup.ts +0 -4
@@ -1,520 +0,0 @@
1
- import {
2
- BaseClient,
3
- parseOrThrow,
4
- type Parsed,
5
- type RequestParams,
6
- } from "./base-client";
7
- import {
8
- SandboxAndRoutesResponse,
9
- SandboxResponse,
10
- CommandResponse,
11
- CommandFinishedResponse,
12
- EmptyResponse,
13
- LogLine,
14
- LogLineStdout,
15
- LogLineStderr,
16
- SandboxesResponse,
17
- ExtendTimeoutResponse,
18
- SnapshotResponse,
19
- CreateSnapshotResponse,
20
- } from "./validators";
21
- import { APIError, StreamError } from "./api-error";
22
- import { FileWriter } from "./file-writer";
23
- import { VERSION } from "../version";
24
- import { consumeReadable } from "../utils/consume-readable";
25
- import { z } from "zod";
26
- import jsonlines from "jsonlines";
27
- import os from "os";
28
- import { Readable } from "stream";
29
- import { normalizePath } from "../utils/normalizePath";
30
- import { JwtExpiry } from "../utils/jwt-expiry";
31
- import { getPrivateParams, WithPrivate } from "../utils/types";
32
- import { RUNTIMES } from "../constants";
33
-
34
- export interface WithFetchOptions {
35
- fetch?: typeof globalThis.fetch;
36
- }
37
-
38
- export class APIClient extends BaseClient {
39
- private teamId: string;
40
- private tokenExpiry: JwtExpiry | null;
41
-
42
- constructor(params: {
43
- baseUrl?: string;
44
- teamId: string;
45
- token: string;
46
- fetch?: typeof globalThis.fetch;
47
- }) {
48
- super({
49
- baseUrl: params.baseUrl ?? "https://vercel.com/api",
50
- token: params.token,
51
- debug: false,
52
- fetch: params.fetch,
53
- });
54
-
55
- this.teamId = params.teamId;
56
- this.tokenExpiry = JwtExpiry.fromToken(params.token);
57
- }
58
-
59
- private async ensureValidToken(): Promise<void> {
60
- if (!this.tokenExpiry) {
61
- return;
62
- }
63
-
64
- const newExpiry = await this.tokenExpiry.tryRefresh();
65
- if (!newExpiry) {
66
- return;
67
- }
68
-
69
- this.tokenExpiry = newExpiry;
70
- this.token = this.tokenExpiry.token;
71
- if (this.tokenExpiry.payload) {
72
- this.teamId = this.tokenExpiry.payload?.owner_id;
73
- }
74
- }
75
-
76
- protected async request(path: string, params?: RequestParams) {
77
- await this.ensureValidToken();
78
-
79
- return super.request(path, {
80
- ...params,
81
- query: { teamId: this.teamId, ...params?.query },
82
- headers: {
83
- "content-type": "application/json",
84
- "user-agent": `vercel/sandbox/${VERSION} (Node.js/${process.version}; ${os.platform()}/${os.arch()})`,
85
- ...params?.headers,
86
- },
87
- });
88
- }
89
-
90
- async getSandbox(
91
- params: WithPrivate<{ sandboxId: string; signal?: AbortSignal }>,
92
- ) {
93
- const privateParams = getPrivateParams(params);
94
- let querystring = new URLSearchParams(privateParams).toString();
95
- querystring = querystring ? `?${querystring}` : "";
96
- return parseOrThrow(
97
- SandboxAndRoutesResponse,
98
- await this.request(`/v1/sandboxes/${params.sandboxId}${querystring}`, {
99
- signal: params.signal,
100
- }),
101
- );
102
- }
103
-
104
- async createSandbox(
105
- params: WithPrivate<{
106
- ports?: number[];
107
- projectId: string;
108
- source?:
109
- | {
110
- type: "git";
111
- url: string;
112
- depth?: number;
113
- revision?: string;
114
- username?: string;
115
- password?: string;
116
- }
117
- | { type: "tarball"; url: string }
118
- | { type: "snapshot"; snapshotId: string };
119
- timeout?: number;
120
- resources?: { vcpus: number };
121
- runtime?: RUNTIMES | (string & {});
122
- signal?: AbortSignal;
123
- }>,
124
- ) {
125
- const privateParams = getPrivateParams(params);
126
- return parseOrThrow(
127
- SandboxAndRoutesResponse,
128
- await this.request("/v1/sandboxes", {
129
- method: "POST",
130
- body: JSON.stringify({
131
- projectId: params.projectId,
132
- ports: params.ports,
133
- source: params.source,
134
- timeout: params.timeout,
135
- resources: params.resources,
136
- runtime: params.runtime,
137
- ...privateParams,
138
- }),
139
- signal: params.signal,
140
- }),
141
- );
142
- }
143
-
144
- async runCommand(params: {
145
- sandboxId: string;
146
- cwd?: string;
147
- command: string;
148
- args: string[];
149
- env: Record<string, string>;
150
- sudo: boolean;
151
- signal?: AbortSignal;
152
- }) {
153
- return parseOrThrow(
154
- CommandResponse,
155
- await this.request(`/v1/sandboxes/${params.sandboxId}/cmd`, {
156
- method: "POST",
157
- body: JSON.stringify({
158
- command: params.command,
159
- args: params.args,
160
- cwd: params.cwd,
161
- env: params.env,
162
- sudo: params.sudo,
163
- }),
164
- signal: params.signal,
165
- }),
166
- );
167
- }
168
-
169
- async getCommand(params: {
170
- sandboxId: string;
171
- cmdId: string;
172
- wait: true;
173
- signal?: AbortSignal;
174
- }): Promise<Parsed<z.infer<typeof CommandFinishedResponse>>>;
175
- async getCommand(params: {
176
- sandboxId: string;
177
- cmdId: string;
178
- wait?: boolean;
179
- signal?: AbortSignal;
180
- }): Promise<Parsed<z.infer<typeof CommandResponse>>>;
181
- async getCommand(params: {
182
- sandboxId: string;
183
- cmdId: string;
184
- wait?: boolean;
185
- signal?: AbortSignal;
186
- }) {
187
- return params.wait
188
- ? parseOrThrow(
189
- CommandFinishedResponse,
190
- await this.request(
191
- `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
192
- { signal: params.signal, query: { wait: "true" } },
193
- ),
194
- )
195
- : parseOrThrow(
196
- CommandResponse,
197
- await this.request(
198
- `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}`,
199
- { signal: params.signal },
200
- ),
201
- );
202
- }
203
-
204
- async mkDir(params: {
205
- sandboxId: string;
206
- path: string;
207
- cwd?: string;
208
- signal?: AbortSignal;
209
- }) {
210
- return parseOrThrow(
211
- EmptyResponse,
212
- await this.request(`/v1/sandboxes/${params.sandboxId}/fs/mkdir`, {
213
- method: "POST",
214
- body: JSON.stringify({ path: params.path, cwd: params.cwd }),
215
- signal: params.signal,
216
- }),
217
- );
218
- }
219
-
220
- getFileWriter(params: {
221
- sandboxId: string;
222
- extractDir: string;
223
- signal?: AbortSignal;
224
- }) {
225
- const writer = new FileWriter();
226
- return {
227
- response: (async () => {
228
- return this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
229
- method: "POST",
230
- headers: {
231
- "content-type": "application/gzip",
232
- "x-cwd": params.extractDir,
233
- },
234
- body: await consumeReadable(writer.readable),
235
- signal: params.signal,
236
- });
237
- })(),
238
- writer,
239
- };
240
- }
241
-
242
- async listSandboxes(params: {
243
- /**
244
- * The ID or name of the project to which the sandboxes belong.
245
- * @example "my-project"
246
- */
247
- projectId: string;
248
- /**
249
- * Maximum number of sandboxes to list from a request.
250
- * @example 10
251
- */
252
- limit?: number;
253
- /**
254
- * Get sandboxes created after this JavaScript timestamp.
255
- * @example 1540095775941
256
- */
257
- since?: number | Date;
258
- /**
259
- * Get sandboxes created before this JavaScript timestamp.
260
- * @example 1540095775951
261
- */
262
- until?: number | Date;
263
- signal?: AbortSignal;
264
- }) {
265
- return parseOrThrow(
266
- SandboxesResponse,
267
- await this.request(`/v1/sandboxes`, {
268
- query: {
269
- project: params.projectId,
270
- limit: params.limit,
271
- since:
272
- typeof params.since === "number"
273
- ? params.since
274
- : params.since?.getTime(),
275
- until:
276
- typeof params.until === "number"
277
- ? params.until
278
- : params.until?.getTime(),
279
- },
280
- method: "GET",
281
- signal: params.signal,
282
- }),
283
- );
284
- }
285
-
286
- async writeFiles(params: {
287
- sandboxId: string;
288
- cwd: string;
289
- files: { path: string; content: Buffer }[];
290
- extractDir: string;
291
- signal?: AbortSignal;
292
- }) {
293
- const { writer, response } = this.getFileWriter({
294
- sandboxId: params.sandboxId,
295
- extractDir: params.extractDir,
296
- signal: params.signal,
297
- });
298
-
299
- for (const file of params.files) {
300
- await writer.addFile({
301
- name: normalizePath({
302
- filePath: file.path,
303
- extractDir: params.extractDir,
304
- cwd: params.cwd,
305
- }),
306
- content: file.content,
307
- });
308
- }
309
-
310
- writer.end();
311
- await parseOrThrow(EmptyResponse, await response);
312
- }
313
-
314
- async readFile(params: {
315
- sandboxId: string;
316
- path: string;
317
- cwd?: string;
318
- signal?: AbortSignal;
319
- }): Promise<NodeJS.ReadableStream | null> {
320
- const response = await this.request(
321
- `/v1/sandboxes/${params.sandboxId}/fs/read`,
322
- {
323
- method: "POST",
324
- body: JSON.stringify({ path: params.path, cwd: params.cwd }),
325
- signal: params.signal,
326
- },
327
- );
328
-
329
- if (response.status === 404) {
330
- return null;
331
- }
332
-
333
- if (response.body === null) {
334
- return null;
335
- }
336
-
337
- return Readable.fromWeb(response.body);
338
- }
339
-
340
- async killCommand(params: {
341
- sandboxId: string;
342
- commandId: string;
343
- signal: number;
344
- abortSignal?: AbortSignal;
345
- }) {
346
- return parseOrThrow(
347
- CommandResponse,
348
- await this.request(
349
- `/v1/sandboxes/${params.sandboxId}/${params.commandId}/kill`,
350
- {
351
- method: "POST",
352
- body: JSON.stringify({ signal: params.signal }),
353
- signal: params.abortSignal,
354
- },
355
- ),
356
- );
357
- }
358
-
359
- getLogs(params: {
360
- sandboxId: string;
361
- cmdId: string;
362
- signal?: AbortSignal;
363
- }): AsyncGenerator<
364
- z.infer<typeof LogLineStdout> | z.infer<typeof LogLineStderr>,
365
- void,
366
- void
367
- > &
368
- Disposable & { close(): void } {
369
- const self = this;
370
- const disposer = new AbortController();
371
- const signal = !params.signal
372
- ? disposer.signal
373
- : mergeSignals(params.signal, disposer.signal);
374
-
375
- const generator = (async function* () {
376
- const url = `/v1/sandboxes/${params.sandboxId}/cmd/${params.cmdId}/logs`;
377
- const response = await self.request(url, {
378
- method: "GET",
379
- signal,
380
- });
381
- if (response.headers.get("content-type") !== "application/x-ndjson") {
382
- throw new APIError(response, {
383
- message: "Expected a stream of logs",
384
- sandboxId: params.sandboxId,
385
- });
386
- }
387
-
388
- if (response.body === null) {
389
- throw new APIError(response, {
390
- message: "No response body",
391
- sandboxId: params.sandboxId,
392
- });
393
- }
394
-
395
- const jsonlinesStream = jsonlines.parse();
396
- pipe(response.body, jsonlinesStream).catch((err) => {
397
- console.error("Error piping logs:", err);
398
- });
399
-
400
- for await (const chunk of jsonlinesStream) {
401
- const parsed = LogLine.parse(chunk);
402
- if (parsed.stream === "error") {
403
- throw new StreamError(
404
- parsed.data.code,
405
- parsed.data.message,
406
- params.sandboxId,
407
- );
408
- }
409
- yield parsed;
410
- }
411
- })();
412
-
413
- return Object.assign(generator, {
414
- [Symbol.dispose]() {
415
- disposer.abort("Disposed");
416
- },
417
- close: () => disposer.abort("Disposed"),
418
- });
419
- }
420
-
421
- async stopSandbox(params: {
422
- sandboxId: string;
423
- signal?: AbortSignal;
424
- }): Promise<Parsed<z.infer<typeof SandboxResponse>>> {
425
- const url = `/v1/sandboxes/${params.sandboxId}/stop`;
426
- return parseOrThrow(
427
- SandboxResponse,
428
- await this.request(url, { method: "POST", signal: params.signal }),
429
- );
430
- }
431
-
432
- async extendTimeout(params: {
433
- sandboxId: string;
434
- duration: number;
435
- signal?: AbortSignal;
436
- }): Promise<Parsed<z.infer<typeof ExtendTimeoutResponse>>> {
437
- const url = `/v1/sandboxes/${params.sandboxId}/extend-timeout`;
438
- return parseOrThrow(
439
- ExtendTimeoutResponse,
440
- await this.request(url, {
441
- method: "POST",
442
- body: JSON.stringify({ duration: params.duration }),
443
- signal: params.signal,
444
- }),
445
- );
446
- }
447
-
448
- async createSnapshot(params: {
449
- sandboxId: string;
450
- signal?: AbortSignal;
451
- }): Promise<Parsed<z.infer<typeof CreateSnapshotResponse>>> {
452
- const url = `/v1/sandboxes/${params.sandboxId}/snapshot`;
453
- return parseOrThrow(
454
- CreateSnapshotResponse,
455
- await this.request(url, { method: "POST", signal: params.signal }),
456
- );
457
- }
458
-
459
- async deleteSnapshot(params: {
460
- snapshotId: string;
461
- signal?: AbortSignal;
462
- }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {
463
- const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;
464
- return parseOrThrow(
465
- SnapshotResponse,
466
- await this.request(url, { method: "DELETE", signal: params.signal }),
467
- );
468
- }
469
-
470
- async getSnapshot(params: {
471
- snapshotId: string;
472
- signal?: AbortSignal;
473
- }): Promise<Parsed<z.infer<typeof SnapshotResponse>>> {
474
- const url = `/v1/sandboxes/snapshots/${params.snapshotId}`;
475
- return parseOrThrow(
476
- SnapshotResponse,
477
- await this.request(url, { signal: params.signal }),
478
- );
479
- }
480
- }
481
-
482
- async function pipe(
483
- readable: ReadableStream<Uint8Array>,
484
- output: NodeJS.WritableStream,
485
- ) {
486
- const reader = readable.getReader();
487
- try {
488
- while (true) {
489
- const read = await reader.read();
490
- if (read.value) {
491
- output.write(Buffer.from(read.value));
492
- }
493
- if (read.done) {
494
- break;
495
- }
496
- }
497
- } catch (err) {
498
- output.emit("error", err);
499
- } finally {
500
- output.end();
501
- }
502
- }
503
-
504
- function mergeSignals(...signals: [AbortSignal, ...AbortSignal[]]) {
505
- const controller = new AbortController();
506
- const onAbort = () => {
507
- controller.abort();
508
- for (const signal of signals) {
509
- signal.removeEventListener("abort", onAbort);
510
- }
511
- };
512
- for (const signal of signals) {
513
- if (signal.aborted) {
514
- controller.abort();
515
- break;
516
- }
517
- signal.addEventListener("abort", onAbort);
518
- }
519
- return controller.signal;
520
- }
@@ -1,46 +0,0 @@
1
- interface Options<ErrorData> {
2
- message?: string;
3
- json?: ErrorData;
4
- text?: string;
5
- sandboxId?: string;
6
- }
7
-
8
- export class APIError<ErrorData> extends Error {
9
- public response: Response;
10
- public message: string;
11
- public json?: ErrorData;
12
- public text?: string;
13
- public sandboxId?: string;
14
-
15
- constructor(response: Response, options?: Options<ErrorData>) {
16
- super(response.statusText);
17
- if (Error.captureStackTrace) {
18
- Error.captureStackTrace(this, APIError);
19
- }
20
-
21
- this.response = response;
22
- this.message = options?.message ?? "";
23
- this.json = options?.json;
24
- this.text = options?.text;
25
- this.sandboxId = options?.sandboxId;
26
- }
27
- }
28
-
29
- /**
30
- * Error thrown when a stream error is received streaming.
31
- * This typically occurs when the sandbox is stopped while streaming.
32
- */
33
- export class StreamError extends Error {
34
- public code: string;
35
- public sandboxId: string;
36
-
37
- constructor(code: string, message: string, sandboxId: string) {
38
- super(message);
39
- this.name = "StreamError";
40
- this.code = code;
41
- this.sandboxId = sandboxId;
42
- if (Error.captureStackTrace) {
43
- Error.captureStackTrace(this, StreamError);
44
- }
45
- }
46
- }