@vercel/sandbox 0.0.7 → 0.0.8

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @vercel/sandbox@0.0.7 build /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.8 build /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
3
3
  > tsc
4
4
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @vercel/sandbox@0.0.7 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
2
+ > @vercel/sandbox@0.0.8 typecheck /home/runner/work/sandbox-sdk/sandbox-sdk/packages/sandbox
3
3
  > tsc --noEmit
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @vercel/sandbox
2
2
 
3
+ ## 0.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - Write files using a single compressed stream ([#44](https://github.com/vercel/sandbox-sdk/pull/44))
8
+ - Expose `runtime` parameter ([#46](https://github.com/vercel/sandbox-sdk/pull/46))
9
+ - Add git depth and revision options to sandbox source ([#47](https://github.com/vercel/sandbox-sdk/pull/47))
10
+
3
11
  ## 0.0.7
4
12
 
5
13
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { BaseClient, type Parsed, type RequestParams } from "./base-client";
2
2
  import { Command, CommandFinished, LogLine, StoppedSandbox } from "./validators";
3
- import { Readable } from "stream";
3
+ import { FileWriter } from "./file-writer";
4
4
  import { z } from "zod";
5
5
  export declare class APIClient extends BaseClient {
6
6
  private teamId;
@@ -16,6 +16,8 @@ export declare class APIClient extends BaseClient {
16
16
  source?: {
17
17
  type: "git";
18
18
  url: string;
19
+ depth?: number;
20
+ revision?: string;
19
21
  } | {
20
22
  type: "tarball";
21
23
  url: string;
@@ -24,6 +26,7 @@ export declare class APIClient extends BaseClient {
24
26
  resources?: {
25
27
  vcpus: number;
26
28
  };
29
+ runtime?: "node22" | "python3.13";
27
30
  }): Promise<Parsed<{
28
31
  sandboxId: string;
29
32
  routes: {
@@ -55,11 +58,17 @@ export declare class APIClient extends BaseClient {
55
58
  path: string;
56
59
  cwd?: string;
57
60
  }): Promise<Parsed<{}>>;
61
+ getFileWriter(params: {
62
+ sandboxId: string;
63
+ }): {
64
+ response: Promise<import("node-fetch").Response>;
65
+ writer: FileWriter;
66
+ };
58
67
  writeFiles(params: {
59
68
  sandboxId: string;
60
69
  files: {
61
70
  path: string;
62
- stream: Readable | Buffer;
71
+ stream: Buffer;
63
72
  }[];
64
73
  }): Promise<void>;
65
74
  readFile(params: {
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.APIClient = void 0;
7
- const form_data_1 = __importDefault(require("form-data"));
8
7
  const base_client_1 = require("./base-client");
9
8
  const validators_1 = require("./validators");
10
9
  const api_error_1 = require("./api-error");
10
+ const file_writer_1 = require("./file-writer");
11
11
  const lru_cache_1 = require("lru-cache");
12
12
  const version_1 = require("../version");
13
13
  const jsonlines_1 = __importDefault(require("jsonlines"));
@@ -50,6 +50,7 @@ class APIClient extends base_client_1.BaseClient {
50
50
  source: params.source,
51
51
  timeout: params.timeout,
52
52
  resources: params.resources,
53
+ runtime: params.runtime,
53
54
  }),
54
55
  }));
55
56
  }
@@ -75,16 +76,26 @@ class APIClient extends base_client_1.BaseClient {
75
76
  body: JSON.stringify({ path: params.path, cwd: params.cwd }),
76
77
  }));
77
78
  }
79
+ getFileWriter(params) {
80
+ const writer = new file_writer_1.FileWriter();
81
+ return {
82
+ response: this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
83
+ method: "POST",
84
+ headers: { "content-type": "application/gzip" },
85
+ body: writer.readable,
86
+ }),
87
+ writer,
88
+ };
89
+ }
78
90
  async writeFiles(params) {
79
- const formData = new form_data_1.default();
91
+ const { writer, response } = this.getFileWriter({
92
+ sandboxId: params.sandboxId,
93
+ });
80
94
  for (const file of params.files) {
81
- formData.append(file.path, file.stream, file.path);
95
+ await writer.addFile({ name: file.path, content: file.stream });
82
96
  }
83
- await (0, base_client_1.parseOrThrow)(validators_1.WrittenFile, await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
84
- method: "POST",
85
- headers: { ...formData.getHeaders() },
86
- body: formData,
87
- }));
97
+ await writer.end();
98
+ await (0, base_client_1.parseOrThrow)(validators_1.WrittenFile, await response);
88
99
  }
89
100
  async readFile(params) {
90
101
  const response = await this.request(`/v1/sandboxes/${params.sandboxId}/fs/read`, {
@@ -0,0 +1,52 @@
1
+ import { Readable } from "stream";
2
+ interface FileBuffer {
3
+ /**
4
+ * The name (path) of the file to write.
5
+ */
6
+ name: string;
7
+ /**
8
+ * The content of the file as a Buffer.
9
+ */
10
+ content: Buffer;
11
+ }
12
+ interface FileStream {
13
+ /**
14
+ * The name (path) of the file to write.
15
+ */
16
+ name: string;
17
+ /**
18
+ * A Readable stream to consume the content of the file.
19
+ */
20
+ content: Readable;
21
+ /**
22
+ * The expected size of the file. This is required to write
23
+ * the header of the compressed file.
24
+ */
25
+ size: number;
26
+ }
27
+ /**
28
+ * Allows to create a Readable stream with methods to write files
29
+ * to it and to finish it. Files written are compressed together
30
+ * and gzipped in the stream.
31
+ */
32
+ export declare class FileWriter {
33
+ readable: Readable;
34
+ private pack;
35
+ constructor();
36
+ /**
37
+ * Allows to add a file to the stream. Size is required to write
38
+ * the tarball header so when content is a stream it must be
39
+ * provided.
40
+ *
41
+ * Returns a Promise resolved once the file is written in the
42
+ * stream.
43
+ */
44
+ addFile(file: FileBuffer | FileStream): Promise<void>;
45
+ /**
46
+ * Allows to finish the stream returning a Promise that will
47
+ * resolve once the readable is effectively closed or
48
+ * errored.
49
+ */
50
+ end(): Promise<void>;
51
+ }
52
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileWriter = void 0;
7
+ const zlib_1 = __importDefault(require("zlib"));
8
+ const tar_stream_1 = __importDefault(require("tar-stream"));
9
+ const stream_1 = require("stream");
10
+ /**
11
+ * Allows to create a Readable stream with methods to write files
12
+ * to it and to finish it. Files written are compressed together
13
+ * and gzipped in the stream.
14
+ */
15
+ class FileWriter {
16
+ constructor() {
17
+ const gzip = zlib_1.default.createGzip();
18
+ this.pack = tar_stream_1.default.pack();
19
+ this.readable = this.pack.pipe(gzip);
20
+ }
21
+ /**
22
+ * Allows to add a file to the stream. Size is required to write
23
+ * the tarball header so when content is a stream it must be
24
+ * provided.
25
+ *
26
+ * Returns a Promise resolved once the file is written in the
27
+ * stream.
28
+ */
29
+ async addFile(file) {
30
+ return new Promise((resolve, reject) => {
31
+ const entry = this.pack.entry("size" in file
32
+ ? { name: file.name, size: file.size }
33
+ : { name: file.name, size: file.content.length }, (error) => {
34
+ if (error) {
35
+ return reject(error);
36
+ }
37
+ else {
38
+ resolve();
39
+ }
40
+ });
41
+ if (file.content instanceof stream_1.Readable) {
42
+ file.content.pipe(entry);
43
+ }
44
+ else {
45
+ entry.end(file.content);
46
+ }
47
+ });
48
+ }
49
+ /**
50
+ * Allows to finish the stream returning a Promise that will
51
+ * resolve once the readable is effectively closed or
52
+ * errored.
53
+ */
54
+ async end() {
55
+ return new Promise((resolve, reject) => {
56
+ this.readable.on("error", reject);
57
+ this.readable.on("end", resolve);
58
+ this.pack.finalize();
59
+ });
60
+ }
61
+ }
62
+ exports.FileWriter = FileWriter;
package/dist/sandbox.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Readable, type Writable } from "stream";
1
+ import { type Writable } from "stream";
2
2
  import { APIClient } from "./api-client";
3
3
  import { Command, CommandFinished } from "./command";
4
4
  import { type Credentials } from "./utils/get-credentials";
@@ -8,10 +8,16 @@ export interface CreateSandboxParams {
8
8
  * The source of the sandbox.
9
9
  *
10
10
  * Omit this parameter start a sandbox without a source.
11
+ *
12
+ * For git sources:
13
+ * - `depth`: Creates shallow clones with limited commit history (minimum: 1)
14
+ * - `revision`: Clones and checks out a specific commit, branch, or tag
11
15
  */
12
16
  source?: {
13
17
  type: "git";
14
18
  url: string;
19
+ depth?: number;
20
+ revision?: string;
15
21
  } | {
16
22
  type: "tarball";
17
23
  url: string;
@@ -33,6 +39,11 @@ export interface CreateSandboxParams {
33
39
  resources?: {
34
40
  vcpus: number;
35
41
  };
42
+ /**
43
+ * The runtime of the sandbox, currently only `node22` and `python3.13` are supported.
44
+ * If not specified, the default runtime `node22` will be used.
45
+ */
46
+ runtime?: "node22" | "python3.13";
36
47
  }
37
48
  /** @inline */
38
49
  interface GetSandboxParams {
@@ -181,7 +192,7 @@ export declare class Sandbox {
181
192
  */
182
193
  writeFiles(files: {
183
194
  path: string;
184
- stream: Readable | Buffer;
195
+ stream: Buffer;
185
196
  }[]): Promise<void>;
186
197
  /**
187
198
  * Get the public domain of a port of this sandbox.
package/dist/sandbox.js CHANGED
@@ -29,6 +29,7 @@ class Sandbox {
29
29
  ports: params?.ports ?? [],
30
30
  timeout: params?.timeout,
31
31
  resources: params?.resources,
32
+ runtime: params?.runtime,
32
33
  });
33
34
  return new Sandbox({
34
35
  client,
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.0.7";
1
+ export declare const VERSION = "0.0.8";
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // Autogenerated by inject-version.ts
5
- exports.VERSION = "0.0.7";
5
+ exports.VERSION = "0.0.8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/sandbox",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,6 +15,8 @@
15
15
  "lru-cache": "11.1.0",
16
16
  "ms": "2.1.3",
17
17
  "node-fetch": "2.6.11",
18
+ "tar-stream": "3.1.7",
19
+ "zlib": "1.0.5",
18
20
  "zod": "3.24.4"
19
21
  },
20
22
  "devDependencies": {
@@ -23,6 +25,7 @@
23
25
  "@types/ms": "2.1.0",
24
26
  "@types/node": "22.15.12",
25
27
  "@types/node-fetch": "2.6.12",
28
+ "@types/tar-stream": "3.1.4",
26
29
  "dotenv": "16.5.0",
27
30
  "typedoc": "0.28.5",
28
31
  "typescript": "5.8.3",
@@ -1,4 +1,3 @@
1
- import FormData from "form-data";
2
1
  import {
3
2
  BaseClient,
4
3
  parseOrThrow,
@@ -14,8 +13,8 @@ import {
14
13
  StoppedSandbox,
15
14
  WrittenFile,
16
15
  } from "./validators";
17
- import { Readable } from "stream";
18
16
  import { APIError } from "./api-error";
17
+ import { FileWriter } from "./file-writer";
19
18
  import { LRUCache } from "lru-cache";
20
19
  import { VERSION } from "../version";
21
20
  import { z } from "zod";
@@ -60,9 +59,12 @@ export class APIClient extends BaseClient {
60
59
  async createSandbox(params: {
61
60
  ports?: number[];
62
61
  projectId: string;
63
- source?: { type: "git"; url: string } | { type: "tarball"; url: string };
62
+ source?:
63
+ | { type: "git"; url: string; depth?: number; revision?: string }
64
+ | { type: "tarball"; url: string };
64
65
  timeout?: number;
65
66
  resources?: { vcpus: number };
67
+ runtime?: "node22" | "python3.13";
66
68
  }) {
67
69
  return parseOrThrow(
68
70
  CreatedSandbox,
@@ -74,6 +76,7 @@ export class APIClient extends BaseClient {
74
76
  source: params.source,
75
77
  timeout: params.timeout,
76
78
  resources: params.resources,
79
+ runtime: params.runtime,
77
80
  }),
78
81
  }),
79
82
  );
@@ -141,24 +144,32 @@ export class APIClient extends BaseClient {
141
144
  );
142
145
  }
143
146
 
147
+ getFileWriter(params: { sandboxId: string }) {
148
+ const writer = new FileWriter();
149
+ return {
150
+ response: this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
151
+ method: "POST",
152
+ headers: { "content-type": "application/gzip" },
153
+ body: writer.readable,
154
+ }),
155
+ writer,
156
+ };
157
+ }
158
+
144
159
  async writeFiles(params: {
145
160
  sandboxId: string;
146
- files: { path: string; stream: Readable | Buffer }[];
161
+ files: { path: string; stream: Buffer }[];
147
162
  }) {
148
- const formData = new FormData();
163
+ const { writer, response } = this.getFileWriter({
164
+ sandboxId: params.sandboxId,
165
+ });
149
166
 
150
167
  for (const file of params.files) {
151
- formData.append(file.path, file.stream, file.path);
168
+ await writer.addFile({ name: file.path, content: file.stream });
152
169
  }
153
170
 
154
- await parseOrThrow(
155
- WrittenFile,
156
- await this.request(`/v1/sandboxes/${params.sandboxId}/fs/write`, {
157
- method: "POST",
158
- headers: { ...formData.getHeaders() },
159
- body: formData,
160
- }),
161
- );
171
+ await writer.end();
172
+ await parseOrThrow(WrittenFile, await response);
162
173
  }
163
174
 
164
175
  async readFile(params: {
@@ -0,0 +1,90 @@
1
+ import zlib from "zlib";
2
+ import tar, { type Pack } from "tar-stream";
3
+ import { Readable } from "stream";
4
+
5
+ interface FileBuffer {
6
+ /**
7
+ * The name (path) of the file to write.
8
+ */
9
+ name: string;
10
+ /**
11
+ * The content of the file as a Buffer.
12
+ */
13
+ content: Buffer;
14
+ }
15
+
16
+ interface FileStream {
17
+ /**
18
+ * The name (path) of the file to write.
19
+ */
20
+ name: string;
21
+ /**
22
+ * A Readable stream to consume the content of the file.
23
+ */
24
+ content: Readable;
25
+ /**
26
+ * The expected size of the file. This is required to write
27
+ * the header of the compressed file.
28
+ */
29
+ size: number;
30
+ }
31
+
32
+ /**
33
+ * Allows to create a Readable stream with methods to write files
34
+ * to it and to finish it. Files written are compressed together
35
+ * and gzipped in the stream.
36
+ */
37
+ export class FileWriter {
38
+ public readable: Readable;
39
+ private pack: Pack;
40
+
41
+ constructor() {
42
+ const gzip = zlib.createGzip();
43
+ this.pack = tar.pack();
44
+ this.readable = this.pack.pipe(gzip);
45
+ }
46
+
47
+ /**
48
+ * Allows to add a file to the stream. Size is required to write
49
+ * the tarball header so when content is a stream it must be
50
+ * provided.
51
+ *
52
+ * Returns a Promise resolved once the file is written in the
53
+ * stream.
54
+ */
55
+ async addFile(file: FileBuffer | FileStream) {
56
+ return new Promise<void>((resolve, reject) => {
57
+ const entry = this.pack.entry(
58
+ "size" in file
59
+ ? { name: file.name, size: file.size }
60
+ : { name: file.name, size: file.content.length },
61
+ (error) => {
62
+ if (error) {
63
+ return reject(error);
64
+ } else {
65
+ resolve();
66
+ }
67
+ },
68
+ );
69
+
70
+ if (file.content instanceof Readable) {
71
+ file.content.pipe(entry);
72
+ } else {
73
+ entry.end(file.content);
74
+ }
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Allows to finish the stream returning a Promise that will
80
+ * resolve once the readable is effectively closed or
81
+ * errored.
82
+ */
83
+ async end() {
84
+ return new Promise<void>((resolve, reject) => {
85
+ this.readable.on("error", reject);
86
+ this.readable.on("end", resolve);
87
+ this.pack.finalize();
88
+ });
89
+ }
90
+ }
package/src/sandbox.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Readable, type Writable } from "stream";
1
+ import { type Writable } from "stream";
2
2
  import { APIClient } from "./api-client";
3
3
  import { Command, CommandFinished } from "./command";
4
4
  import { getCredentials, type Credentials } from "./utils/get-credentials";
@@ -9,8 +9,14 @@ export interface CreateSandboxParams {
9
9
  * The source of the sandbox.
10
10
  *
11
11
  * Omit this parameter start a sandbox without a source.
12
+ *
13
+ * For git sources:
14
+ * - `depth`: Creates shallow clones with limited commit history (minimum: 1)
15
+ * - `revision`: Clones and checks out a specific commit, branch, or tag
12
16
  */
13
- source?: { type: "git"; url: string } | { type: "tarball"; url: string };
17
+ source?:
18
+ | { type: "git"; url: string; depth?: number; revision?: string }
19
+ | { type: "tarball"; url: string };
14
20
  /**
15
21
  * Array of port numbers to expose from the sandbox.
16
22
  */
@@ -26,6 +32,12 @@ export interface CreateSandboxParams {
26
32
  * 2048 MB of memory per vCPU.
27
33
  */
28
34
  resources?: { vcpus: number };
35
+
36
+ /**
37
+ * The runtime of the sandbox, currently only `node22` and `python3.13` are supported.
38
+ * If not specified, the default runtime `node22` will be used.
39
+ */
40
+ runtime?: "node22" | "python3.13";
29
41
  }
30
42
 
31
43
  /** @inline */
@@ -113,6 +125,7 @@ export class Sandbox {
113
125
  ports: params?.ports ?? [],
114
126
  timeout: params?.timeout,
115
127
  resources: params?.resources,
128
+ runtime: params?.runtime,
116
129
  });
117
130
 
118
131
  return new Sandbox({
@@ -270,7 +283,7 @@ export class Sandbox {
270
283
  * @param files - Array of files with path and stream/buffer contents
271
284
  * @returns A promise that resolves when the files are written
272
285
  */
273
- async writeFiles(files: { path: string; stream: Readable | Buffer }[]) {
286
+ async writeFiles(files: { path: string; stream: Buffer }[]) {
274
287
  return this.client.writeFiles({
275
288
  sandboxId: this.sandboxId,
276
289
  files: files,
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Autogenerated by inject-version.ts
2
- export const VERSION = "0.0.7";
2
+ export const VERSION = "0.0.8";