@computesdk/quilt 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,635 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ quilt: () => quilt
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_provider = require("@computesdk/provider");
27
+ var PROVIDER = "quilt";
28
+ var DEFAULT_IMAGE = "prod";
29
+ var DEFAULT_TIMEOUT_MS = 3e5;
30
+ var DEFAULT_POLL_INTERVAL_MS = 1e3;
31
+ function getEnv(name) {
32
+ return typeof process !== "undefined" ? process.env?.[name] : void 0;
33
+ }
34
+ function resolveConfig(config) {
35
+ const baseUrl = config.baseUrl ?? getEnv("QUILT_BASE_URL") ?? getEnv("QUILT_API_BASE_URL");
36
+ if (!baseUrl) {
37
+ throw new Error(
38
+ "Missing Quilt base URL.\n\nPass it: quilt({ baseUrl: 'https://backend.example.com' })\nOr set QUILT_BASE_URL in your environment."
39
+ );
40
+ }
41
+ const apiKey = config.apiKey ?? getEnv("QUILT_API_KEY");
42
+ const accessToken = config.accessToken ?? getEnv("QUILT_ACCESS_TOKEN");
43
+ if (!apiKey && !accessToken) {
44
+ throw new Error(
45
+ "Missing Quilt credentials.\n\nProvide apiKey or accessToken in config:\n quilt({ baseUrl: 'https://backend.example.com', apiKey: 'xxx' })\nOr set QUILT_API_KEY or QUILT_ACCESS_TOKEN in your environment."
46
+ );
47
+ }
48
+ const tenantId = config.tenantId ?? getEnv("QUILT_TENANT_ID");
49
+ const timeoutFromEnv = getEnv("QUILT_TIMEOUT_MS");
50
+ const pollFromEnv = getEnv("QUILT_POLL_INTERVAL_MS");
51
+ const ttlFromEnv = getEnv("QUILT_PUBLISHED_SERVICE_TTL_SECS");
52
+ const authModeFromEnv = getEnv("QUILT_PUBLISHED_SERVICE_AUTH_MODE");
53
+ return {
54
+ baseUrl: baseUrl.replace(/\/+$/, ""),
55
+ authMode: apiKey ? "apiKey" : "accessToken",
56
+ authToken: apiKey ?? accessToken ?? "",
57
+ tenantId,
58
+ image: config.image ?? getEnv("QUILT_IMAGE") ?? DEFAULT_IMAGE,
59
+ timeout: config.timeout ?? parseOptionalInt(timeoutFromEnv) ?? DEFAULT_TIMEOUT_MS,
60
+ publishedServiceAuthMode: config.publishedServiceAuthMode ?? (authModeFromEnv === "public" ? "public" : "service_token"),
61
+ publishedServiceTtlSecs: config.publishedServiceTtlSecs ?? parseOptionalInt(ttlFromEnv) ?? void 0,
62
+ pollIntervalMs: config.pollIntervalMs ?? parseOptionalInt(pollFromEnv) ?? DEFAULT_POLL_INTERVAL_MS
63
+ };
64
+ }
65
+ function parseOptionalInt(value) {
66
+ if (!value) return void 0;
67
+ const parsed = Number.parseInt(value, 10);
68
+ return Number.isFinite(parsed) ? parsed : void 0;
69
+ }
70
+ function buildHeaders(config, includeTenantHeader = false) {
71
+ const headers = {
72
+ Accept: "application/json"
73
+ };
74
+ if (config.authMode === "apiKey") {
75
+ headers["X-Api-Key"] = config.authToken;
76
+ } else {
77
+ headers.Authorization = `Bearer ${config.authToken}`;
78
+ }
79
+ if (includeTenantHeader) {
80
+ if (!config.tenantId) {
81
+ throw new Error(
82
+ "Quilt snapshot operations require tenantId.\n\nPass it: quilt({ tenantId: 'tenant_123', ... })\nOr set QUILT_TENANT_ID in your environment."
83
+ );
84
+ }
85
+ headers["X-Tenant-Id"] = config.tenantId;
86
+ }
87
+ return headers;
88
+ }
89
+ async function requestJson(config, path, init = {}, options = {}) {
90
+ const headers = {
91
+ ...buildHeaders(config, options.includeTenantHeader),
92
+ ...init.headers || {}
93
+ };
94
+ const response = await fetch(`${config.baseUrl}${path}`, {
95
+ ...init,
96
+ headers
97
+ });
98
+ if (options.allow404 && response.status === 404) {
99
+ return null;
100
+ }
101
+ if (!response.ok) {
102
+ throw await buildHttpError(response, path);
103
+ }
104
+ if (response.status === 204) {
105
+ return null;
106
+ }
107
+ return await response.json();
108
+ }
109
+ async function requestNoContent(config, path, init = {}, options = {}) {
110
+ const headers = {
111
+ ...buildHeaders(config, options.includeTenantHeader),
112
+ ...init.headers || {}
113
+ };
114
+ const response = await fetch(`${config.baseUrl}${path}`, {
115
+ ...init,
116
+ headers
117
+ });
118
+ if (options.allow404 && response.status === 404) {
119
+ return;
120
+ }
121
+ if (!response.ok) {
122
+ throw await buildHttpError(response, path);
123
+ }
124
+ }
125
+ async function buildHttpError(response, path) {
126
+ let detail = `${response.status} ${response.statusText}`;
127
+ try {
128
+ const body = await response.json();
129
+ if (body && typeof body === "object") {
130
+ const record = body;
131
+ detail = typeof record.error === "string" && record.error || typeof record.message === "string" && record.message || detail;
132
+ }
133
+ } catch {
134
+ try {
135
+ const text = await response.text();
136
+ if (text) detail = text;
137
+ } catch {
138
+ }
139
+ }
140
+ return new Error(`Quilt API request failed for ${path}: ${detail}`);
141
+ }
142
+ function buildExecCommand(command, options) {
143
+ let fullCommand = command;
144
+ if (options?.env && Object.keys(options.env).length > 0) {
145
+ const envPrefix = Object.entries(options.env).map(([key, value]) => {
146
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
147
+ throw new Error(`Invalid environment variable name "${key}".`);
148
+ }
149
+ return `${key}="${(0, import_provider.escapeShellArg)(String(value))}"`;
150
+ }).join(" ");
151
+ fullCommand = `${envPrefix} ${fullCommand}`;
152
+ }
153
+ if (options?.background) {
154
+ fullCommand = `nohup ${fullCommand} > /dev/null 2>&1 &`;
155
+ }
156
+ return fullCommand;
157
+ }
158
+ function mapStatus(state) {
159
+ switch (state) {
160
+ case "error":
161
+ return "error";
162
+ case "created":
163
+ case "starting":
164
+ case "running":
165
+ return "running";
166
+ case "exited":
167
+ default:
168
+ return "stopped";
169
+ }
170
+ }
171
+ function parseDate(value) {
172
+ if (typeof value === "number") return new Date(value * 1e3);
173
+ if (typeof value === "string") return new Date(value);
174
+ return /* @__PURE__ */ new Date();
175
+ }
176
+ function normalizeFileEntries(stdout) {
177
+ return stdout.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("total ")).flatMap((line) => {
178
+ const parts = line.split(/\s+/);
179
+ if (parts.length < 9) return [];
180
+ const name = parts.slice(8).join(" ");
181
+ if (name === "." || name === "..") return [];
182
+ return [
183
+ {
184
+ name,
185
+ type: parts[0]?.startsWith("d") ? "directory" : "file",
186
+ size: Number.parseInt(parts[4] ?? "0", 10) || 0,
187
+ modified: /* @__PURE__ */ new Date()
188
+ }
189
+ ];
190
+ });
191
+ }
192
+ async function pollOperation(config, operationId, timeoutMs) {
193
+ const deadline = Date.now() + timeoutMs;
194
+ while (Date.now() <= deadline) {
195
+ const operation = await requestJson(
196
+ config,
197
+ `/api/operations/${encodeURIComponent(operationId)}`
198
+ );
199
+ if (!operation) {
200
+ throw new Error(`Quilt operation ${operationId} disappeared while polling.`);
201
+ }
202
+ const status = operation.status ?? "";
203
+ if (status === "succeeded") return operation;
204
+ if (status === "failed" || status === "error" || status === "cancelled") {
205
+ throw new Error(
206
+ `Quilt operation ${operationId} failed: ${operation.error_message ?? operation.error ?? operation.message ?? status}`
207
+ );
208
+ }
209
+ await new Promise((resolve) => setTimeout(resolve, config.pollIntervalMs));
210
+ }
211
+ throw new Error(`Timed out waiting for Quilt operation ${operationId}.`);
212
+ }
213
+ function requireOperationId(envelope, context) {
214
+ const operationId = envelope?.operation_id;
215
+ if (!operationId) {
216
+ throw new Error(`Quilt ${context} did not return an operation_id.`);
217
+ }
218
+ return operationId;
219
+ }
220
+ async function getContainerById(config, sandboxId) {
221
+ return requestJson(
222
+ config,
223
+ `/api/containers/${encodeURIComponent(sandboxId)}`,
224
+ void 0,
225
+ { allow404: true }
226
+ );
227
+ }
228
+ function resolveSnapshotResultId(operation) {
229
+ if (operation.snapshot_id) return operation.snapshot_id;
230
+ if (operation.target_resource_type === "snapshot" && operation.target_resource_id) {
231
+ return operation.target_resource_id;
232
+ }
233
+ const result = operation.result ?? {};
234
+ const snapshotId = result["snapshot_id"];
235
+ return typeof snapshotId === "string" ? snapshotId : null;
236
+ }
237
+ function resolveContainerResultId(operation) {
238
+ if (operation.container_id) return operation.container_id;
239
+ if (operation.target_resource_type === "container" && operation.target_resource_id) {
240
+ return operation.target_resource_id;
241
+ }
242
+ const result = operation.result ?? {};
243
+ const directId = result["container_id"];
244
+ if (typeof directId === "string") return directId;
245
+ const results = result["results"];
246
+ if (Array.isArray(results) && results.length > 0) {
247
+ const first = results[0];
248
+ if (first && typeof first === "object" && typeof first.container_id === "string") {
249
+ return first.container_id;
250
+ }
251
+ }
252
+ return null;
253
+ }
254
+ async function createPublishedService(handle, port, protocol) {
255
+ const wantsWebSocket = protocol === "ws" || protocol === "wss";
256
+ const existing = await requestJson(
257
+ handle.config,
258
+ `/api/containers/${encodeURIComponent(handle.sandboxId)}/services`
259
+ );
260
+ const match = existing?.services?.find((service) => {
261
+ if (service.target_port !== port) return false;
262
+ if (wantsWebSocket && !service.enable_websockets) return false;
263
+ return true;
264
+ });
265
+ if (match) return match;
266
+ const created = await requestJson(
267
+ handle.config,
268
+ `/api/containers/${encodeURIComponent(handle.sandboxId)}/services`,
269
+ {
270
+ method: "POST",
271
+ headers: {
272
+ "Content-Type": "application/json"
273
+ },
274
+ body: JSON.stringify({
275
+ name: `computesdk-${port}`,
276
+ target_port: port,
277
+ protocol: "http",
278
+ enable_websockets: wantsWebSocket,
279
+ auth_mode: handle.config.publishedServiceAuthMode,
280
+ ...handle.config.publishedServiceTtlSecs ? { ttl_secs: handle.config.publishedServiceTtlSecs } : {}
281
+ })
282
+ }
283
+ );
284
+ if (!created) {
285
+ throw new Error(`Failed to create Quilt published service for port ${port}.`);
286
+ }
287
+ return created;
288
+ }
289
+ var quilt = (0, import_provider.defineProvider)({
290
+ name: PROVIDER,
291
+ methods: {
292
+ sandbox: {
293
+ create: async (config, options) => {
294
+ const resolved = resolveConfig(config);
295
+ const {
296
+ timeout: requestedTimeout,
297
+ envs,
298
+ metadata,
299
+ templateId,
300
+ snapshotId,
301
+ name,
302
+ namespace: _namespace,
303
+ sandboxId: _sandboxId,
304
+ directory,
305
+ signal: _signal,
306
+ ...providerOptions
307
+ } = options || {};
308
+ if (snapshotId) {
309
+ const unsupportedKeys = ["envs", "templateId", "directory"];
310
+ if (envs || templateId || directory) {
311
+ throw new Error(
312
+ `Quilt snapshot clone does not support ${unsupportedKeys.join(", ")} create options in one request.`
313
+ );
314
+ }
315
+ const cloneEnvelope = await requestJson(
316
+ resolved,
317
+ `/api/snapshots/${encodeURIComponent(snapshotId)}/clone`,
318
+ {
319
+ method: "POST",
320
+ headers: {
321
+ "Content-Type": "application/json"
322
+ },
323
+ body: JSON.stringify({
324
+ resume_policy: "immediate",
325
+ ...name ? { name } : {},
326
+ ...metadata ? { labels: stringifyRecord(metadata) } : {}
327
+ })
328
+ },
329
+ { includeTenantHeader: true }
330
+ );
331
+ const operation = await pollOperation(
332
+ resolved,
333
+ requireOperationId(cloneEnvelope, "snapshot clone"),
334
+ requestedTimeout ?? resolved.timeout
335
+ );
336
+ const containerId = resolveContainerResultId(operation);
337
+ if (!containerId) {
338
+ throw new Error(`Quilt snapshot clone completed without a container ID.`);
339
+ }
340
+ const container2 = await getContainerById(resolved, containerId);
341
+ if (!container2) {
342
+ throw new Error(`Quilt cloned container ${containerId} was not found after creation.`);
343
+ }
344
+ return {
345
+ sandbox: {
346
+ config: resolved,
347
+ sandboxId: containerId,
348
+ container: container2
349
+ },
350
+ sandboxId: containerId
351
+ };
352
+ }
353
+ const createBody = {
354
+ name: name ?? `computesdk-${crypto.randomUUID().slice(0, 8)}`,
355
+ image: typeof providerOptions.image === "string" ? providerOptions.image : typeof templateId === "string" ? templateId : resolved.image,
356
+ ...envs && Object.keys(envs).length > 0 ? { environment: envs } : {},
357
+ ...metadata ? { labels: stringifyRecord(metadata) } : {},
358
+ ...directory ? { working_directory: directory } : {},
359
+ ...providerOptions
360
+ };
361
+ const container = await requestJson(resolved, "/api/containers", {
362
+ method: "POST",
363
+ headers: {
364
+ "Content-Type": "application/json"
365
+ },
366
+ body: JSON.stringify(createBody)
367
+ });
368
+ if (!container?.container_id) {
369
+ throw new Error("Quilt container create did not return a container_id.");
370
+ }
371
+ return {
372
+ sandbox: {
373
+ config: resolved,
374
+ sandboxId: container.container_id,
375
+ container
376
+ },
377
+ sandboxId: container.container_id
378
+ };
379
+ },
380
+ getById: async (config, sandboxId) => {
381
+ const resolved = resolveConfig(config);
382
+ const container = await getContainerById(resolved, sandboxId);
383
+ if (!container) return null;
384
+ return {
385
+ sandbox: {
386
+ config: resolved,
387
+ sandboxId,
388
+ container
389
+ },
390
+ sandboxId
391
+ };
392
+ },
393
+ list: async (config) => {
394
+ const resolved = resolveConfig(config);
395
+ const sandboxes = [];
396
+ let cursor = void 0;
397
+ while (true) {
398
+ const query = new URLSearchParams();
399
+ if (cursor) query.set("cursor", cursor);
400
+ const response = await requestJson(resolved, `/api/containers${query.toString() ? `?${query.toString()}` : ""}`);
401
+ for (const container of response?.containers ?? []) {
402
+ if (!container.container_id) continue;
403
+ sandboxes.push({
404
+ sandbox: {
405
+ config: resolved,
406
+ sandboxId: container.container_id,
407
+ container
408
+ },
409
+ sandboxId: container.container_id
410
+ });
411
+ }
412
+ if (!response?.pagination?.has_next || !response.pagination.next_cursor) {
413
+ break;
414
+ }
415
+ cursor = response.pagination.next_cursor;
416
+ }
417
+ return sandboxes;
418
+ },
419
+ destroy: async (config, sandboxId) => {
420
+ const resolved = resolveConfig(config);
421
+ await requestNoContent(
422
+ resolved,
423
+ `/api/containers/${encodeURIComponent(sandboxId)}`,
424
+ { method: "DELETE" },
425
+ { allow404: true }
426
+ );
427
+ },
428
+ runCommand: async (handle, command, options) => {
429
+ const startTime = Date.now();
430
+ const fullCommand = buildExecCommand(command, options);
431
+ const response = await requestJson(handle.config, `/api/containers/${encodeURIComponent(handle.sandboxId)}/exec`, {
432
+ method: "POST",
433
+ headers: {
434
+ "Content-Type": "application/json"
435
+ },
436
+ body: JSON.stringify({
437
+ command: ["/bin/sh", "-lc", fullCommand],
438
+ ...options?.cwd ? { workdir: options.cwd } : {},
439
+ ...options?.timeout ? { timeout_ms: options.timeout } : {}
440
+ })
441
+ });
442
+ const stdout = response?.stdout ?? "";
443
+ const stderr = response?.stderr ?? "";
444
+ if (options?.onStdout && stdout) options.onStdout(stdout);
445
+ if (options?.onStderr && stderr) options.onStderr(stderr);
446
+ return {
447
+ stdout,
448
+ stderr,
449
+ exitCode: response?.timed_out ? 124 : response?.exit_code ?? 0,
450
+ durationMs: Date.now() - startTime
451
+ };
452
+ },
453
+ getInfo: async (handle) => {
454
+ const container = await getContainerById(handle.config, handle.sandboxId) ?? handle.container;
455
+ return {
456
+ id: handle.sandboxId,
457
+ provider: PROVIDER,
458
+ status: mapStatus(container.state),
459
+ createdAt: parseDate(container.created_at),
460
+ timeout: handle.config.timeout,
461
+ metadata: {
462
+ name: container.name,
463
+ tenantId: container.tenant_id,
464
+ ipAddress: container.ip_address,
465
+ execReady: container.exec_ready,
466
+ networkReady: container.network_ready,
467
+ state: container.state
468
+ }
469
+ };
470
+ },
471
+ getUrl: async (handle, options) => {
472
+ const service = await createPublishedService(handle, options.port, options.protocol);
473
+ if (options.protocol === "ws" || options.protocol === "wss") {
474
+ const url2 = service.websocket_url;
475
+ if (!url2) {
476
+ throw new Error(`Quilt service ${service.service_id} does not expose a websocket URL.`);
477
+ }
478
+ if (options.protocol === "ws") {
479
+ const parsed = new URL(url2);
480
+ parsed.protocol = "ws:";
481
+ return parsed.toString();
482
+ }
483
+ return url2;
484
+ }
485
+ const url = service.public_url;
486
+ if (!url) {
487
+ throw new Error(`Quilt service ${service.service_id} did not return a public URL.`);
488
+ }
489
+ if (options.protocol === "http") {
490
+ const parsed = new URL(url);
491
+ parsed.protocol = "http:";
492
+ return parsed.toString();
493
+ }
494
+ return url;
495
+ },
496
+ filesystem: {
497
+ readFile: async (handle, path, runCommand) => {
498
+ const result = await runCommand(
499
+ handle,
500
+ `test -f "${(0, import_provider.escapeShellArg)(path)}" && base64 < "${(0, import_provider.escapeShellArg)(path)}" | tr -d '\\n'`
501
+ );
502
+ if (result.exitCode !== 0) {
503
+ throw new Error(`File not found or unreadable: ${path}`);
504
+ }
505
+ return Buffer.from(result.stdout, "base64").toString("utf8");
506
+ },
507
+ writeFile: async (handle, path, content, runCommand) => {
508
+ const parentDir = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) || "/" : ".";
509
+ const encoded = Buffer.from(content).toString("base64");
510
+ const result = await runCommand(
511
+ handle,
512
+ `mkdir -p "${(0, import_provider.escapeShellArg)(parentDir)}" && echo "${encoded}" | base64 -d > "${(0, import_provider.escapeShellArg)(path)}"`
513
+ );
514
+ if (result.exitCode !== 0) {
515
+ throw new Error(`Failed to write file ${path}: ${result.stderr}`);
516
+ }
517
+ },
518
+ mkdir: async (handle, path, runCommand) => {
519
+ const result = await runCommand(handle, `mkdir -p "${(0, import_provider.escapeShellArg)(path)}"`);
520
+ if (result.exitCode !== 0) {
521
+ throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
522
+ }
523
+ },
524
+ readdir: async (handle, path, runCommand) => {
525
+ const result = await runCommand(handle, `ls -la "${(0, import_provider.escapeShellArg)(path)}"`);
526
+ if (result.exitCode !== 0) {
527
+ throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
528
+ }
529
+ return normalizeFileEntries(result.stdout);
530
+ },
531
+ exists: async (handle, path, runCommand) => {
532
+ const result = await runCommand(handle, `test -e "${(0, import_provider.escapeShellArg)(path)}"`);
533
+ return result.exitCode === 0;
534
+ },
535
+ remove: async (handle, path, runCommand) => {
536
+ const result = await runCommand(handle, `rm -rf "${(0, import_provider.escapeShellArg)(path)}"`);
537
+ if (result.exitCode !== 0) {
538
+ throw new Error(`Failed to remove ${path}: ${result.stderr}`);
539
+ }
540
+ }
541
+ },
542
+ getInstance: (handle) => handle
543
+ },
544
+ snapshot: {
545
+ create: async (config, sandboxId, options) => {
546
+ const resolved = resolveConfig(config);
547
+ const envelope = await requestJson(
548
+ resolved,
549
+ `/api/containers/${encodeURIComponent(sandboxId)}/snapshot`,
550
+ {
551
+ method: "POST",
552
+ headers: {
553
+ "Content-Type": "application/json"
554
+ },
555
+ body: JSON.stringify({
556
+ consistency_mode: "crash-consistent",
557
+ network_mode: "reset",
558
+ volume_mode: "exclude",
559
+ labels: {
560
+ ...options?.metadata ? stringifyRecord(options.metadata) : {},
561
+ ...options?.name ? { "computesdk:name": options.name } : {}
562
+ }
563
+ })
564
+ },
565
+ { includeTenantHeader: true }
566
+ );
567
+ const operation = await pollOperation(
568
+ resolved,
569
+ requireOperationId(envelope, "snapshot create"),
570
+ resolved.timeout
571
+ );
572
+ const snapshotId = resolveSnapshotResultId(operation);
573
+ if (!snapshotId) {
574
+ throw new Error(`Quilt snapshot create completed without a snapshot ID.`);
575
+ }
576
+ const snapshot = await requestJson(
577
+ resolved,
578
+ `/api/snapshots/${encodeURIComponent(snapshotId)}`,
579
+ void 0,
580
+ { includeTenantHeader: true }
581
+ );
582
+ if (!snapshot) {
583
+ throw new Error(`Quilt snapshot ${snapshotId} was not found after creation.`);
584
+ }
585
+ return mapSnapshot(snapshot);
586
+ },
587
+ list: async (config, options) => {
588
+ const resolved = resolveConfig(config);
589
+ const query = new URLSearchParams();
590
+ if (options?.sandboxId) {
591
+ query.set("container_id", options.sandboxId);
592
+ }
593
+ const response = await requestJson(
594
+ resolved,
595
+ `/api/snapshots${query.toString() ? `?${query.toString()}` : ""}`,
596
+ void 0,
597
+ { includeTenantHeader: true }
598
+ );
599
+ return (response?.snapshots ?? []).map(mapSnapshot);
600
+ },
601
+ delete: async (config, snapshotId) => {
602
+ const resolved = resolveConfig(config);
603
+ await requestNoContent(
604
+ resolved,
605
+ `/api/snapshots/${encodeURIComponent(snapshotId)}`,
606
+ { method: "DELETE" },
607
+ { includeTenantHeader: true, allow404: true }
608
+ );
609
+ }
610
+ }
611
+ }
612
+ });
613
+ function stringifyRecord(input) {
614
+ return Object.fromEntries(
615
+ Object.entries(input).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)])
616
+ );
617
+ }
618
+ function mapSnapshot(snapshot) {
619
+ return {
620
+ id: snapshot.snapshot_id,
621
+ provider: PROVIDER,
622
+ createdAt: parseDate(snapshot.created_at),
623
+ metadata: {
624
+ sourceContainerId: snapshot.source_container_id,
625
+ pinned: snapshot.pinned ?? false,
626
+ labels: snapshot.labels ?? {},
627
+ expiresAt: snapshot.expires_at ?? null
628
+ }
629
+ };
630
+ }
631
+ // Annotate the CommonJS export names for ESM import in node:
632
+ 0 && (module.exports = {
633
+ quilt
634
+ });
635
+ //# sourceMappingURL=index.js.map