withruntime 0.8.4 → 0.9.0

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 (91) hide show
  1. package/COMPATIBILITY.md +145 -0
  2. package/E2B.md +23 -13
  3. package/README.md +8 -5
  4. package/README.src.md +8 -5
  5. package/compatibility-lock.json +281 -0
  6. package/dist/ai-harness/index.js +2 -2
  7. package/dist/api-defaults.d.ts +17 -0
  8. package/dist/api-defaults.js +22 -0
  9. package/dist/blaxel/preview.d.ts +1 -1
  10. package/dist/blaxel/preview.js +1 -1
  11. package/dist/blaxel/process.d.ts +1 -1
  12. package/dist/blaxel/process.js +11 -2
  13. package/dist/cli-words.d.ts +27 -0
  14. package/dist/cli-words.js +135 -0
  15. package/dist/cli.js +162 -31
  16. package/dist/client.d.ts +4 -1
  17. package/dist/client.js +8 -2
  18. package/dist/cloudflare/index.d.ts +294 -0
  19. package/dist/cloudflare/index.js +554 -0
  20. package/dist/cloudflare/worker.d.ts +415 -0
  21. package/dist/cloudflare/worker.js +297 -0
  22. package/dist/codesandbox/guest-service.d.ts +31 -0
  23. package/dist/codesandbox/guest-service.js +453 -0
  24. package/dist/codesandbox/index.d.ts +386 -0
  25. package/dist/codesandbox/index.js +1313 -0
  26. package/dist/compat/core.d.ts +39 -0
  27. package/dist/compat/core.js +199 -0
  28. package/dist/compat/files.d.ts +26 -0
  29. package/dist/compat/files.js +115 -0
  30. package/dist/compat/shell.d.ts +4 -0
  31. package/dist/compat/shell.js +165 -0
  32. package/dist/daytona/context.js +2 -1
  33. package/dist/daytona/process.d.ts +1 -1
  34. package/dist/daytona/process.js +27 -4
  35. package/dist/e2b/code-interpreter.d.ts +4 -5
  36. package/dist/e2b/code-interpreter.js +54 -56
  37. package/dist/e2b/commands.d.ts +5 -4
  38. package/dist/e2b/commands.js +37 -29
  39. package/dist/e2b/errors.js +1 -1
  40. package/dist/e2b/filesystem.d.ts +8 -6
  41. package/dist/e2b/filesystem.js +127 -28
  42. package/dist/e2b/sandbox.d.ts +4 -3
  43. package/dist/e2b/sandbox.js +10 -10
  44. package/dist/errors.d.ts +8 -0
  45. package/dist/errors.js +15 -2
  46. package/dist/freestyle/index.d.ts +301 -0
  47. package/dist/freestyle/index.js +310 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/mcp.js +2 -2
  50. package/dist/modal/index.d.ts +205 -0
  51. package/dist/modal/index.js +485 -0
  52. package/dist/network-cli.js +1 -1
  53. package/dist/openai-agents/index.js +9 -26
  54. package/dist/products/audit.d.ts +3 -0
  55. package/dist/products/index.d.ts +2 -0
  56. package/dist/products/index.js +2 -0
  57. package/dist/products/interpreter.d.ts +11 -4
  58. package/dist/products/interpreter.js +33 -9
  59. package/dist/products/jobs.js +3 -2
  60. package/dist/products/limits.d.ts +10 -0
  61. package/dist/products/previews.d.ts +10 -2
  62. package/dist/products/previews.js +3 -2
  63. package/dist/products/tailscale.d.ts +57 -0
  64. package/dist/products/tailscale.js +17 -0
  65. package/dist/products/watch.d.ts +25 -4
  66. package/dist/products/watch.js +75 -14
  67. package/dist/runloop/index.d.ts +340 -0
  68. package/dist/runloop/index.js +438 -0
  69. package/dist/runloop/pagination.d.ts +46 -0
  70. package/dist/runloop/pagination.js +82 -0
  71. package/dist/runloop/snapshots.d.ts +52 -0
  72. package/dist/runloop/snapshots.js +244 -0
  73. package/dist/sandbox.d.ts +40 -9
  74. package/dist/sandbox.js +273 -76
  75. package/dist/snapshots.d.ts +11 -1
  76. package/dist/snapshots.js +11 -1
  77. package/dist/sprites/index.d.ts +251 -0
  78. package/dist/sprites/index.js +552 -0
  79. package/dist/tar.d.ts +8 -1
  80. package/dist/tar.js +185 -35
  81. package/dist/tools/index.js +2 -1
  82. package/dist/transport.d.ts +20 -2
  83. package/dist/transport.js +48 -11
  84. package/dist/types.d.ts +52 -3
  85. package/dist/vercel/command.js +12 -1
  86. package/dist/vercel/errors.d.ts +2 -2
  87. package/dist/vercel/errors.js +12 -1
  88. package/dist/vercel/filesystem.d.ts +21 -18
  89. package/dist/vercel/filesystem.js +95 -48
  90. package/dist/vercel/sandbox.js +5 -5
  91. package/package.json +45 -3
@@ -0,0 +1,552 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { PassThrough, Writable } from "node:stream";
3
+ import { RuntimeError } from "../errors.js";
4
+ import { all, client, CompatibilityError, create, destroy, execOptions, lookup, only, ready, region, } from "../compat/core.js";
5
+ import { GuestFiles } from "../compat/files.js";
6
+ export { CompatibilityError };
7
+ export class FilesystemError extends Error {
8
+ code;
9
+ path;
10
+ syscall;
11
+ constructor(message, code, path, syscall) {
12
+ super(message);
13
+ this.code = code;
14
+ this.path = path;
15
+ this.syscall = syscall;
16
+ this.name = "FilesystemError";
17
+ }
18
+ }
19
+ async function fileOperation(path, syscall, run) {
20
+ try {
21
+ return await run();
22
+ }
23
+ catch (error) {
24
+ if (!(error instanceof RuntimeError))
25
+ throw error;
26
+ const codes = {
27
+ file_not_found: "ENOENT",
28
+ not_found: "ENOENT",
29
+ already_exists: "EEXIST",
30
+ file_exists: "EEXIST",
31
+ not_a_directory: "ENOTDIR",
32
+ is_a_directory: "EISDIR",
33
+ permission_denied: "EACCES",
34
+ directory_not_empty: "ENOTEMPTY",
35
+ invalid_request: "EINVAL",
36
+ };
37
+ throw new FilesystemError(error.message, codes[error.code] ?? (error.status === 404 ? "ENOENT" : "UNKNOWN"), path, syscall);
38
+ }
39
+ }
40
+ export class ExecError extends Error {
41
+ exitCode;
42
+ stdout;
43
+ stderr;
44
+ constructor(message, exitCode, stdout, stderr) {
45
+ super(message);
46
+ this.exitCode = exitCode;
47
+ this.stdout = stdout;
48
+ this.stderr = stderr;
49
+ this.name = "ExecError";
50
+ }
51
+ }
52
+ export class SpritesClient {
53
+ runtime;
54
+ constructor(token, options = {}) {
55
+ only("Sprites client", options, [
56
+ "apiKey",
57
+ "baseUrl",
58
+ "baseURL",
59
+ "timeout",
60
+ "timeoutMs",
61
+ "maxRetries",
62
+ "fetch",
63
+ "client",
64
+ "controlMode",
65
+ "waitForCapacityMs",
66
+ ]);
67
+ if (options.controlMode)
68
+ throw new CompatibilityError("Sprites", "control-mode wire protocol");
69
+ this.runtime = client({
70
+ ...options,
71
+ apiKey: options.apiKey ?? token,
72
+ baseUrl: options.baseUrl ?? options.baseURL,
73
+ timeoutMs: options.timeoutMs ?? options.timeout,
74
+ });
75
+ }
76
+ sprite(name) {
77
+ return new Sprite(name, this);
78
+ }
79
+ async createSprite(name, input = {}, extra = {}) {
80
+ const o = ["ramMB", "cpus", "region", "storageGB"].some((k) => k in input)
81
+ ? { ...extra, config: input }
82
+ : { ...input, ...extra };
83
+ only("Sprites create", o, [
84
+ "config",
85
+ "environment",
86
+ "labels",
87
+ "urlSettings",
88
+ "runtime",
89
+ "waitForCapacity",
90
+ ]);
91
+ if (o.runtime !== undefined)
92
+ throw new CompatibilityError("Sprites", `vendor runtime image ${o.runtime}; import it into Runtime first`);
93
+ if (o.urlSettings?.privateAccess)
94
+ throw new CompatibilityError("Sprites", "privateAccess roles");
95
+ const c = o.config ?? {};
96
+ only("Sprites config", c, ["ramMB", "cpus", "region", "storageGB"]);
97
+ const s = await create(this.runtime, "sprites", {
98
+ name,
99
+ persistent: true,
100
+ vcpu: c.cpus,
101
+ memoryMiB: c.ramMB,
102
+ diskMiB: c.storageGB === undefined ? undefined : Math.ceil((c.storageGB * 1e9) / 1048576),
103
+ region: region("Sprites", c.region),
104
+ labels: { "compat.labels": JSON.stringify(o.labels ?? []) },
105
+ }, o.environment);
106
+ const sprite = new Sprite(name, this);
107
+ sprite.hydrate(s);
108
+ if (o.urlSettings) {
109
+ try {
110
+ await sprite.updateURLSettings(o.urlSettings);
111
+ }
112
+ catch (e) {
113
+ await destroy(s);
114
+ throw e;
115
+ }
116
+ }
117
+ return sprite;
118
+ }
119
+ async getSprite(name) {
120
+ const sprite = this.sprite(name);
121
+ sprite.hydrate(await lookup(this.runtime, name));
122
+ return sprite;
123
+ }
124
+ async deleteSprite(name) {
125
+ await destroy(await lookup(this.runtime, name));
126
+ }
127
+ async listSprites(options = {}) {
128
+ only("Sprites list", options, ["prefix", "maxResults", "continuationToken", "bulkLoad"]);
129
+ const values = (await all(this.runtime, "sprites")).filter((s) => (s.info.name ?? s.id).startsWith(options.prefix ?? ""));
130
+ const offset = Number(options.continuationToken ?? 0), limit = options.maxResults ?? 100;
131
+ return {
132
+ sprites: values.slice(offset, offset + limit).map((s) => ({
133
+ id: s.id,
134
+ name: s.info.name ?? s.id,
135
+ status: s.state,
136
+ createdAt: new Date(s.info.createdAt),
137
+ updatedAt: new Date(s.info.createdAt),
138
+ config: {
139
+ cpus: s.info.vcpu,
140
+ ramMB: s.info.memoryMiB,
141
+ storageGB: (s.info.diskMiB * 1048576) / 1e9,
142
+ region: s.info.region,
143
+ },
144
+ })),
145
+ hasMore: offset + limit < values.length,
146
+ nextContinuationToken: offset + limit < values.length ? String(offset + limit) : undefined,
147
+ };
148
+ }
149
+ async listAllSprites(prefix) {
150
+ return (await all(this.runtime, "sprites"))
151
+ .filter((s) => (s.info.name ?? s.id).startsWith(prefix ?? ""))
152
+ .map((s) => {
153
+ const sprite = this.sprite(s.info.name ?? s.id);
154
+ sprite.hydrate(s);
155
+ return sprite;
156
+ });
157
+ }
158
+ async restartSprite(name) {
159
+ const s = await lookup(this.runtime, name);
160
+ await s.stop();
161
+ await s.restart();
162
+ return { spriteName: name, machineId: s.id, message: "Restarted" };
163
+ }
164
+ updateURLSettings(name, settings) {
165
+ return this.sprite(name).updateURLSettings(settings);
166
+ }
167
+ }
168
+ export class Sprite {
169
+ name;
170
+ client;
171
+ id;
172
+ status;
173
+ config;
174
+ createdAt;
175
+ updatedAt;
176
+ url;
177
+ labels = [];
178
+ constructor(name, client) {
179
+ this.name = name;
180
+ this.client = client;
181
+ }
182
+ hydrate(s) {
183
+ this.id = s.id;
184
+ this.status = s.state;
185
+ this.config = {
186
+ cpus: s.info.vcpu,
187
+ ramMB: s.info.memoryMiB,
188
+ region: s.info.region,
189
+ storageGB: (s.info.diskMiB * 1048576) / 1e9,
190
+ };
191
+ this.createdAt = new Date(s.info.createdAt);
192
+ this.updatedAt = this.createdAt;
193
+ this.labels = JSON.parse(s.info.labels["compat.labels"] ?? "[]");
194
+ }
195
+ async native() {
196
+ const s = await lookup(this.client.runtime, this.id ?? this.name);
197
+ this.hydrate(s);
198
+ return s;
199
+ }
200
+ spawn(command, args = [], options = {}) {
201
+ return new SpriteCommand(this, command, args, options);
202
+ }
203
+ exec(command, options) {
204
+ return this.execFile("bash", ["-c", command], options);
205
+ }
206
+ async execFile(file, args = [], options = {}) {
207
+ options.signal?.throwIfAborted();
208
+ if (options.encoding &&
209
+ options.encoding !== "buffer" &&
210
+ !Buffer.isEncoding(options.encoding))
211
+ throw new TypeError(`Unknown encoding: ${String(options.encoding)}`);
212
+ only("Sprites exec", options, [
213
+ "cwd",
214
+ "env",
215
+ "tty",
216
+ "rows",
217
+ "cols",
218
+ "detachable",
219
+ "sessionId",
220
+ "controlMode",
221
+ "encoding",
222
+ "maxBuffer",
223
+ "signal",
224
+ "timeout",
225
+ ]);
226
+ if (options.controlMode)
227
+ throw new CompatibilityError("Sprites", "control-mode wire protocol");
228
+ const cmd = this.spawn(file, args, options);
229
+ const out = [], err = [];
230
+ let bytes = 0;
231
+ let failure;
232
+ const capture = (target) => (chunk) => {
233
+ bytes += chunk.length;
234
+ if (bytes > (options.maxBuffer ?? 10 * 1024 * 1024)) {
235
+ failure = new Error("maxBuffer exceeded");
236
+ cmd.kill("SIGKILL");
237
+ }
238
+ else
239
+ target.push(chunk);
240
+ };
241
+ cmd.stdout.on("data", capture(out));
242
+ cmd.stderr.on("data", capture(err));
243
+ const abort = () => {
244
+ failure = new Error("Command aborted");
245
+ cmd.kill("SIGTERM");
246
+ };
247
+ options.signal?.addEventListener("abort", abort, { once: true });
248
+ if (options.signal?.aborted)
249
+ abort();
250
+ const timer = options.timeout ? setTimeout(abort, options.timeout) : undefined;
251
+ try {
252
+ const exitCode = await cmd.wait();
253
+ if (failure)
254
+ throw failure;
255
+ const stdout = Buffer.concat(out), stderr = Buffer.concat(err);
256
+ const r = {
257
+ stdout: options.encoding === "buffer"
258
+ ? stdout
259
+ : stdout.toString(options.encoding ?? "utf8"),
260
+ stderr: options.encoding === "buffer"
261
+ ? stderr
262
+ : stderr.toString(options.encoding ?? "utf8"),
263
+ exitCode,
264
+ };
265
+ if (exitCode !== 0)
266
+ throw new ExecError(`Command exited with code ${exitCode}`, exitCode, r.stdout, r.stderr);
267
+ return r;
268
+ }
269
+ finally {
270
+ if (timer)
271
+ clearTimeout(timer);
272
+ options.signal?.removeEventListener("abort", abort);
273
+ }
274
+ }
275
+ execFileHTTP(file, args, options) {
276
+ return this.execFile(file, args, options);
277
+ }
278
+ createSession(command, args = [], options = {}) {
279
+ return this.spawn(command, args, { ...options, detachable: true });
280
+ }
281
+ attachSession(sessionId, options = {}) {
282
+ return this.spawn("", [], { ...options, sessionId });
283
+ }
284
+ async listSessions() {
285
+ return (await (await this.native()).processes.list())
286
+ .filter((p) => p.state === "running")
287
+ .map((p) => ({
288
+ id: p.id,
289
+ command: p.command,
290
+ created: new Date(p.startedAt),
291
+ workdir: p.cwd,
292
+ isActive: p.state === "running",
293
+ tty: p.pty,
294
+ }));
295
+ }
296
+ delete() {
297
+ return this.client.deleteSprite(this.id ?? this.name);
298
+ }
299
+ destroy() {
300
+ return this.delete();
301
+ }
302
+ restart() {
303
+ return this.client.restartSprite(this.id ?? this.name);
304
+ }
305
+ filesystem(workingDir = "/") {
306
+ return new SpriteFilesystem(() => this.native(), workingDir);
307
+ }
308
+ async createCheckpoint(_comment) {
309
+ throw new CompatibilityError("Sprites", "filesystem-only checkpoints; Runtime snapshots also include running memory");
310
+ }
311
+ async listCheckpoints() {
312
+ throw new CompatibilityError("Sprites", "filesystem-only checkpoints");
313
+ }
314
+ async getCheckpoint(_id) {
315
+ throw new CompatibilityError("Sprites", "filesystem-only checkpoints");
316
+ }
317
+ async updateURLSettings(settings) {
318
+ only("Sprites URL settings", settings, ["auth"]);
319
+ if (settings.auth !== "public" && settings.auth !== "sprite")
320
+ throw new TypeError("auth must be public or sprite");
321
+ const p = await (await this.native()).previews.create(8080, { visibility: settings.auth === "public" ? "public" : "private" });
322
+ this.url = p.urlWithToken ?? p.url;
323
+ }
324
+ async update(options) {
325
+ only("Sprites update", options, ["labels", "urlSettings"]);
326
+ const s = await this.native();
327
+ if (options.labels)
328
+ await s.update({
329
+ labels: { ...s.info.labels, "compat.labels": JSON.stringify(options.labels) },
330
+ });
331
+ if (options.urlSettings)
332
+ await this.updateURLSettings(options.urlSettings);
333
+ this.hydrate(s);
334
+ return this;
335
+ }
336
+ }
337
+ export class SpriteCommand extends EventEmitter {
338
+ stdout = new PassThrough();
339
+ stderr = new PassThrough();
340
+ stdin;
341
+ process;
342
+ completion;
343
+ code = -1;
344
+ controller = new AbortController();
345
+ constructor(sprite, command, args = [], options = {}) {
346
+ super();
347
+ only("Sprites spawn", options, [
348
+ "cwd",
349
+ "env",
350
+ "tty",
351
+ "rows",
352
+ "cols",
353
+ "detachable",
354
+ "sessionId",
355
+ "controlMode",
356
+ "encoding",
357
+ "maxBuffer",
358
+ "signal",
359
+ "timeout",
360
+ ]);
361
+ if (options.controlMode)
362
+ throw new CompatibilityError("Sprites", "control-mode wire protocol");
363
+ this.process = (async () => {
364
+ const s = await ready(await sprite.native());
365
+ return options.sessionId
366
+ ? s.processes.get(options.sessionId)
367
+ : s.spawn([command, ...args], {
368
+ ...(await execOptions(s, { cwd: options.cwd, env: options.env })),
369
+ stdin: "pipe",
370
+ outputEncoding: "base64",
371
+ pty: options.tty ? { rows: options.rows, cols: options.cols } : undefined,
372
+ });
373
+ })();
374
+ this.stdin = new Writable({
375
+ write: (chunk, _encoding, callback) => {
376
+ void this.process.then((p) => p.write(chunk)).then(() => callback(), callback);
377
+ },
378
+ final: (callback) => {
379
+ void this.process.then((p) => p.write("", { eof: true })).then(() => callback(), callback);
380
+ },
381
+ });
382
+ this.completion = this.follow();
383
+ this.completion.catch((error) => {
384
+ if (this.listenerCount("error"))
385
+ this.emit("error", error);
386
+ });
387
+ }
388
+ async follow() {
389
+ try {
390
+ const p = await this.process;
391
+ this.emit("spawn");
392
+ this.emit("session", p.id);
393
+ for await (const e of p.outputBytes({ signal: this.controller.signal })) {
394
+ if (e.type === "stdout")
395
+ this.stdout.write(e.data);
396
+ else if (e.type === "stderr")
397
+ this.stderr.write(e.data);
398
+ else if (e.type === "truncated")
399
+ throw new Error("Sprite command output was truncated");
400
+ else if (e.type === "exit")
401
+ this.code = e.exitCode ?? -1;
402
+ }
403
+ this.emit("exit", this.code);
404
+ this.emit("close", this.code);
405
+ return this.code;
406
+ }
407
+ finally {
408
+ this.stdout.end();
409
+ this.stderr.end();
410
+ }
411
+ }
412
+ async start() {
413
+ await this.process;
414
+ }
415
+ wait() {
416
+ return this.completion;
417
+ }
418
+ kill(signal = "SIGTERM") {
419
+ this.signal(signal);
420
+ }
421
+ signal(signal) {
422
+ if (!["SIGTERM", "SIGKILL", "SIGINT", "SIGHUP", "SIGQUIT", "SIGUSR1", "SIGUSR2"].includes(signal))
423
+ throw new TypeError(`Unsupported signal ${signal}`);
424
+ void this.process
425
+ .then((p) => p.kill(signal))
426
+ .catch((error) => {
427
+ if (this.listenerCount("error"))
428
+ this.emit("error", error);
429
+ });
430
+ }
431
+ resize(cols, rows) {
432
+ void this.process
433
+ .then((p) => p.resize(cols, rows))
434
+ .catch((error) => {
435
+ if (this.listenerCount("error"))
436
+ this.emit("error", error);
437
+ });
438
+ }
439
+ close() {
440
+ this.controller.abort();
441
+ }
442
+ exitCode() {
443
+ return this.code;
444
+ }
445
+ }
446
+ export class SpriteFilesystem {
447
+ get;
448
+ files;
449
+ constructor(get, cwd = "/") {
450
+ this.get = get;
451
+ this.files = new GuestFiles(get, cwd);
452
+ }
453
+ async readFile(path, encoding) {
454
+ const bytes = Buffer.from(await fileOperation(path, "read", () => this.files.readFile(path)));
455
+ return encoding === "utf8" ? bytes.toString("utf8") : bytes;
456
+ }
457
+ writeFile(path, data, options) {
458
+ return fileOperation(path, "write", () => this.files.writeFile(path, data, options));
459
+ }
460
+ async readdir(path, options = {}) {
461
+ only("Sprites readdir", options, ["withFileTypes", "recursive", "pattern", "encoding"]);
462
+ if (options.encoding && options.encoding !== "utf8")
463
+ throw new CompatibilityError("Sprites", "directory name encodings other than utf8");
464
+ const s = await this.get();
465
+ const entries = await s.files.list(this.files.path(path), {
466
+ hidden: true,
467
+ glob: options.pattern,
468
+ });
469
+ if (options.recursive) {
470
+ if (options.pattern)
471
+ throw new CompatibilityError("Sprites", "recursive listing with a glob pattern");
472
+ for (let i = 0; i < entries.length; i++)
473
+ if (entries[i].type === "directory")
474
+ entries.push(...(await s.files.list(entries[i].path, { hidden: true })));
475
+ }
476
+ return entries.map((e) => options.withFileTypes
477
+ ? {
478
+ name: e.name,
479
+ parentPath: path,
480
+ isDirectory: () => e.type === "directory",
481
+ isFile: () => e.type === "file",
482
+ isSymbolicLink: () => e.type === "symlink",
483
+ }
484
+ : e.name);
485
+ }
486
+ async mkdir(path, options = {}) {
487
+ await fileOperation(path, "mkdir", () => this.files.mkdir(path, options.recursive));
488
+ if (options.mode !== undefined)
489
+ await this.files.chmod(path, options.mode);
490
+ }
491
+ async rm(path, options = {}) {
492
+ if (!options.force && !(await this.files.exists(path)))
493
+ throw new FilesystemError(`Path not found: ${path}`, "ENOENT", path, "rm");
494
+ await fileOperation(path, "rm", () => this.files.remove(path, options.recursive));
495
+ }
496
+ async stat(path) {
497
+ const e = await fileOperation(path, "stat", () => this.files.stat(path));
498
+ return {
499
+ size: e.size,
500
+ mode: parseInt(e.mode, 8),
501
+ mtime: new Date(e.modifiedAt),
502
+ atime: new Date(e.modifiedAt),
503
+ birthtime: new Date(e.modifiedAt),
504
+ isDirectory: () => e.type === "directory",
505
+ isFile: () => e.type === "file",
506
+ isSymbolicLink: () => e.type === "symlink",
507
+ };
508
+ }
509
+ rename(from, to) {
510
+ return fileOperation(from, "rename", () => this.files.rename(from, to, true));
511
+ }
512
+ copyFile(from, to, options = {}) {
513
+ return fileOperation(from, "copyFile", () => this.files.copy(from, to, options.recursive, true));
514
+ }
515
+ chmod(path, mode, options = {}) {
516
+ return fileOperation(path, "chmod", () => this.files.chmod(path, mode, options.recursive));
517
+ }
518
+ exists(path) {
519
+ return this.files.exists(path);
520
+ }
521
+ appendFile(path, data) {
522
+ return fileOperation(path, "appendFile", () => this.files.appendFile(path, data));
523
+ }
524
+ async readJSON(path) {
525
+ return JSON.parse(await this.readFile(path, "utf8"));
526
+ }
527
+ writeJSON(path, data, options = {}) {
528
+ return this.writeFile(path, JSON.stringify(data, null, options.spaces));
529
+ }
530
+ }
531
+ export class CheckpointStream {
532
+ messages;
533
+ index = 0;
534
+ constructor(messages) {
535
+ this.messages = messages;
536
+ }
537
+ async next() {
538
+ return this.messages[this.index++] ?? null;
539
+ }
540
+ async processAll(handler) {
541
+ for await (const message of this)
542
+ await handler(message);
543
+ }
544
+ close() {
545
+ this.index = this.messages.length;
546
+ }
547
+ async *[Symbol.asyncIterator]() {
548
+ let message;
549
+ while ((message = await this.next()))
550
+ yield message;
551
+ }
552
+ }
package/dist/tar.d.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  export declare function tarHeader(name: string, size: number, mode: number, type: "0" | "5" | "2", link?: string, mtime?: number): Uint8Array;
2
2
  export declare function packDirectory(root: string): Promise<Uint8Array>;
3
- /** Unpacks into `target`, refusing any entry that would land outside it. */
3
+ /** Unpacks a gzipped tar that arrives as a stream into `target`, refusing any
4
+ * entry that would land outside it, holding no more than a chunk at a time.
5
+ * It unpacks into a folder beside the target and moves it into place only
6
+ * once the whole archive arrived, so an archive cut short leaves nothing
7
+ * behind that could pass for the folder. A target that exists is merged
8
+ * into, files of the same name replaced. */
9
+ export declare function unpackStream(source: AsyncIterable<Uint8Array> | Iterable<Uint8Array>, target: string): Promise<void>;
10
+ /** Unpacks a whole gzipped tar held in memory, by the same rules. */
4
11
  export declare function unpackArchive(archive: Uint8Array, target: string): Promise<void>;