Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/js-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type {
SandboxNetworkOpts,
SandboxLifecycle,
SandboxInfoLifecycle,
SnapshotCreateOpts,
SnapshotInfo,
SnapshotListOpts,
SnapshotPaginator,
Expand Down
18 changes: 11 additions & 7 deletions packages/js-sdk/src/sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SandboxPaginator,
SandboxBetaCreateOpts,
SandboxApiOpts,
SnapshotCreateOpts,
SnapshotListOpts,
SnapshotInfo,
SnapshotPaginator,
Expand Down Expand Up @@ -604,24 +605,27 @@ export class Sandbox extends SandboxApi {
* Snapshots are persistent and survive sandbox deletion.
*
* Use the returned `snapshotId` with `Sandbox.create(snapshotId)` to create a new sandbox from the snapshot.
* When a `name` is provided, the snapshot is also addressable as `team-slug/name`.
*
* @param opts connection options.
* @param opts snapshot options including an optional `name` for the snapshot template.
*
* @returns snapshot information including the snapshot ID.
* @returns snapshot information including the snapshot ID and optional names.
*
* @example
* ```ts
* const sandbox = await Sandbox.create()
* await sandbox.files.write('/app/state.json', '{"step": 1}')
*
* // Create a snapshot
* const snapshot = await sandbox.createSnapshot()
* // Create a named snapshot
* const snapshot = await sandbox.createSnapshot({ name: 'my-snapshot' })
* console.log(snapshot.snapshotId) // 'team-slug/my-snapshot:default'
* console.log(snapshot.names) // ['team-slug/my-snapshot:default']
*
* // Create a new sandbox from the snapshot
* const newSandbox = await Sandbox.create(snapshot.snapshotId)
* // Create a new sandbox from the named snapshot
* const newSandbox = await Sandbox.create('team-slug/my-snapshot')
* ```
*/
async createSnapshot(opts?: SandboxApiOpts): Promise<SnapshotInfo> {
async createSnapshot(opts?: SnapshotCreateOpts): Promise<SnapshotInfo> {
return await SandboxApi.createSnapshot(
this.sandboxId,
this.resolveApiOpts(opts)
Expand Down
28 changes: 26 additions & 2 deletions packages/js-sdk/src/sandbox/sandboxApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,20 @@ export interface SnapshotListOpts extends SandboxApiOpts {
nextToken?: string
}

/**
* Options for creating a snapshot.
*/
export interface SnapshotCreateOpts extends SandboxApiOpts {
/**
* Optional name for the snapshot template.
*
* If a snapshot template with this name already exists, a new build will be
* assigned to the existing template instead of creating a new one.
* The snapshot can then be referenced as `team-slug/name` when creating sandboxes.
*/
name?: string
}

/**
* Information about a snapshot.
*/
Expand All @@ -276,6 +290,13 @@ export interface SnapshotInfo {
* Can be used with Sandbox.create() to create a new sandbox from this snapshot.
*/
snapshotId: string

/**
* Full names of the snapshot template including team namespace and tag
* (e.g. team-slug/my-snapshot:v2).
* Present when the snapshot was created with a name.
*/
names?: string[]
}

/**
Expand Down Expand Up @@ -687,7 +708,7 @@ export class SandboxApi {
*/
static async createSnapshot(
sandboxId: string,
opts?: SandboxApiOpts
opts?: SnapshotCreateOpts
): Promise<SnapshotInfo> {
const config = new ConnectionConfig(opts)
const client = new ApiClient(config)
Expand All @@ -698,7 +719,9 @@ export class SandboxApi {
sandboxID: sandboxId,
},
},
body: {},
body: {
...(opts?.name !== undefined && { name: opts.name }),
},
signal: config.getSignal(opts?.requestTimeoutMs),
})

Expand All @@ -713,6 +736,7 @@ export class SandboxApi {

return {
snapshotId: res.data!.snapshotID,
...(res.data!.names?.length && { names: res.data!.names }),
}
}

Expand Down