@usestatemachines/sdk 0.2.0 → 0.3.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.
- package/README.md +95 -5
- package/agents/SKILL.md +10 -3
- package/agents/index.js +6 -6
- package/agents/llms.txt +8 -2
- package/agents/prompt.md +1 -1
- package/esm/extensions/apps.d.ts +11 -1
- package/esm/extensions/apps.js +16 -0
- package/esm/extensions/client.d.ts +36 -3
- package/esm/extensions/client.js +96 -11
- package/esm/extensions/errors.d.ts +26 -1
- package/esm/extensions/errors.js +34 -1
- package/esm/extensions/index.d.ts +4 -4
- package/esm/extensions/index.js +1 -1
- package/esm/extensions/running-environment.d.ts +4 -1
- package/esm/extensions/running-environment.js +9 -0
- package/esm/extensions/wait.d.ts +10 -3
- package/esm/extensions/wait.js +22 -12
- package/esm/funcs/environments-list.js +1 -0
- package/esm/funcs/snapshots-capture.d.ts +11 -0
- package/esm/funcs/snapshots-capture.js +94 -0
- package/esm/funcs/snapshots-delete.d.ts +11 -0
- package/esm/funcs/snapshots-delete.js +92 -0
- package/esm/funcs/snapshots-get.d.ts +11 -0
- package/esm/funcs/snapshots-get.js +92 -0
- package/esm/funcs/snapshots-list.d.ts +11 -0
- package/esm/funcs/snapshots-list.js +98 -0
- package/esm/funcs/snapshots-update.d.ts +11 -0
- package/esm/funcs/snapshots-update.js +93 -0
- package/esm/lib/config.d.ts +2 -2
- package/esm/lib/config.js +2 -2
- package/esm/models/app-instance-snapshot.d.ts +15 -0
- package/esm/models/app-instance-snapshot.js +19 -0
- package/esm/models/create-environment-input.d.ts +6 -2
- package/esm/models/create-environment-input.js +3 -1
- package/esm/models/environment-app.d.ts +1 -0
- package/esm/models/environment-app.js +1 -0
- package/esm/models/environment-snapshot.d.ts +39 -0
- package/esm/models/environment-snapshot.js +41 -0
- package/esm/models/environment.d.ts +1 -0
- package/esm/models/environment.js +1 -0
- package/esm/models/error-response.d.ts +1 -0
- package/esm/models/error-response.js +1 -0
- package/esm/models/index.d.ts +2 -0
- package/esm/models/index.js +2 -0
- package/esm/models/operations/capture-snapshot.d.ts +41 -0
- package/esm/models/operations/capture-snapshot.js +36 -0
- package/esm/models/operations/delete-snapshot.d.ts +23 -0
- package/esm/models/operations/delete-snapshot.js +20 -0
- package/esm/models/operations/get-snapshot.d.ts +23 -0
- package/esm/models/operations/get-snapshot.js +20 -0
- package/esm/models/operations/index.d.ts +5 -0
- package/esm/models/operations/index.js +5 -0
- package/esm/models/operations/list-environments.d.ts +2 -0
- package/esm/models/operations/list-environments.js +1 -0
- package/esm/models/operations/list-snapshots.d.ts +30 -0
- package/esm/models/operations/list-snapshots.js +25 -0
- package/esm/models/operations/update-snapshot.d.ts +37 -0
- package/esm/models/operations/update-snapshot.js +29 -0
- package/esm/sdk/sdk.d.ts +3 -0
- package/esm/sdk/sdk.js +5 -0
- package/esm/sdk/snapshots.d.ts +9 -0
- package/esm/sdk/snapshots.js +27 -0
- package/openapi.json +1195 -255
- package/package.json +10 -11
package/esm/extensions/client.js
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { SDK_METADATA } from '../lib/config.js';
|
|
2
2
|
import { HTTPClient } from '../lib/http.js';
|
|
3
3
|
import { StateMachines as Generated } from '../sdk/sdk.js';
|
|
4
|
-
import {
|
|
4
|
+
import { expandSource } from './apps.js';
|
|
5
5
|
import { Connection } from './connection.js';
|
|
6
|
-
import { EnvironmentFailedError, StateMachinesError, TimeoutError, toStateMachinesError, } from './errors.js';
|
|
6
|
+
import { CallerAbortedError, EnvironmentFailedError, SnapshotFailedError, SnapshotTimeoutError, StateMachinesError, TimeoutError, toStateMachinesError, } from './errors.js';
|
|
7
7
|
import { RunningEnvironment } from './running-environment.js';
|
|
8
8
|
import { linkSignal } from './signal.js';
|
|
9
|
-
import { defaultTimeoutMs, disposed, poll, ready, } from './wait.js';
|
|
9
|
+
import { backoff, defaultTimeoutMs, disposed, poll, pollUntil, ready, sleep, snapshotReady, } from './wait.js';
|
|
10
10
|
const stopAfterFailureMs = 30000;
|
|
11
|
-
|
|
12
|
-
class CallerAbortedError extends Error {
|
|
13
|
-
name = 'CallerAbortedError';
|
|
14
|
-
constructor(reason) {
|
|
15
|
-
super('The caller aborted the request.', { cause: reason });
|
|
16
|
-
}
|
|
17
|
-
}
|
|
11
|
+
const busyBudgetMs = 60000;
|
|
18
12
|
export class StateMachines {
|
|
19
13
|
#sdk;
|
|
20
14
|
#fetch;
|
|
@@ -117,7 +111,7 @@ export class StateMachines {
|
|
|
117
111
|
create: async (input, options = {}) => {
|
|
118
112
|
const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
|
|
119
113
|
try {
|
|
120
|
-
const body =
|
|
114
|
+
const body = expandSource(input);
|
|
121
115
|
return await this.#call(options, async (call) => {
|
|
122
116
|
const workspaceId = await this.#workspace(options.workspaceId, call.signal);
|
|
123
117
|
const { data } = await this.#sdk.environments.create({ workspaceId, idempotencyKey, body }, call);
|
|
@@ -163,6 +157,97 @@ export class StateMachines {
|
|
|
163
157
|
return new Connection(details.baseUrl, api, secret, this.#fetch);
|
|
164
158
|
},
|
|
165
159
|
};
|
|
160
|
+
snapshots = {
|
|
161
|
+
/** Returns the API's answer at once: it may still be `capturing`. */
|
|
162
|
+
create: (environmentId, input = {}, options = {}) => this.#createSnapshot(environmentId, input, options, 0),
|
|
163
|
+
/**
|
|
164
|
+
* Creates a snapshot and waits until it's `ready`. Needs `snapshots:create` and
|
|
165
|
+
* `snapshots:read`. A failed or timed-out snapshot is never deleted: the error
|
|
166
|
+
* carries `snapshotId` and `idempotencyKey`.
|
|
167
|
+
*/
|
|
168
|
+
capture: async (environmentId, input = {}, options = {}) => {
|
|
169
|
+
const { readyTimeoutMs, ...createOptions } = options;
|
|
170
|
+
const { signal } = options;
|
|
171
|
+
const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
|
|
172
|
+
const timeoutMs = readyTimeoutMs ?? defaultTimeoutMs;
|
|
173
|
+
if (!(timeoutMs > 0)) {
|
|
174
|
+
throw new RangeError('readyTimeoutMs must be more than 0.');
|
|
175
|
+
}
|
|
176
|
+
const created = await this.#createSnapshot(environmentId, input, { ...createOptions, idempotencyKey }, busyBudgetMs);
|
|
177
|
+
try {
|
|
178
|
+
signal?.throwIfAborted();
|
|
179
|
+
if (snapshotReady(created)) {
|
|
180
|
+
return created;
|
|
181
|
+
}
|
|
182
|
+
return await this.snapshots.waitUntilReady(created.id, {
|
|
183
|
+
timeoutMs,
|
|
184
|
+
...(signal ? { signal } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
const snapshotId = created.id;
|
|
189
|
+
if (signal?.aborted && Object.is(error, signal.reason)) {
|
|
190
|
+
throw Object.assign(new StateMachinesError(new CallerAbortedError(error), {
|
|
191
|
+
idempotencyKey,
|
|
192
|
+
}), { snapshotId });
|
|
193
|
+
}
|
|
194
|
+
if (error instanceof StateMachinesError ||
|
|
195
|
+
error instanceof SnapshotFailedError ||
|
|
196
|
+
error instanceof SnapshotTimeoutError) {
|
|
197
|
+
Object.assign(error, { snapshotId, idempotencyKey });
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
/** Resolves when the snapshot is `ready`. Never deletes it. */
|
|
203
|
+
waitUntilReady: (snapshotId, options) => pollUntil((signal) => this.#unwrap({ signal }, (call) => this.#sdk.snapshots.get({ snapshotId }, { ...call, retries: { strategy: 'none' } })), snapshotReady, options, (timeoutMs, last) => new SnapshotTimeoutError(timeoutMs, last, snapshotId)),
|
|
204
|
+
/** Deleted snapshots too, with `deletedAt` set. */
|
|
205
|
+
get: (snapshotId, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.get({ snapshotId }, call)),
|
|
206
|
+
/** Newest first. Deleted snapshots are left out. */
|
|
207
|
+
list: (query = {}, options) => this.#call(options, async (call) => {
|
|
208
|
+
const { workspaceId, ...filters } = query;
|
|
209
|
+
const resolved = await this.#workspace(workspaceId, call.signal);
|
|
210
|
+
return this.#sdk.snapshots.list({ ...filters, workspaceId: resolved }, call);
|
|
211
|
+
}),
|
|
212
|
+
/**
|
|
213
|
+
* `null` clears the name. A stale `expectedVersion`, or a deleted snapshot, is a 409
|
|
214
|
+
* `version-conflict`; so is a retry of a rename whose first answer was lost.
|
|
215
|
+
*/
|
|
216
|
+
rename: (snapshotId, body, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.update({ snapshotId, body }, call)),
|
|
217
|
+
/** Marks the snapshot deleted and returns it; safe to repeat. A capturing snapshot is a 409 `not-ready`. */
|
|
218
|
+
delete: (snapshotId, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.delete({ snapshotId }, call)),
|
|
219
|
+
};
|
|
220
|
+
async #createSnapshot(environmentId, input, options, busyMs) {
|
|
221
|
+
const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
|
|
222
|
+
try {
|
|
223
|
+
return await this.#call(options, async (call) => {
|
|
224
|
+
const workspaceId = await this.#workspace(options.workspaceId, call.signal);
|
|
225
|
+
const request = {
|
|
226
|
+
workspaceId,
|
|
227
|
+
idempotencyKey,
|
|
228
|
+
body: { ...input, environmentId },
|
|
229
|
+
};
|
|
230
|
+
const deadline = Date.now() + busyMs;
|
|
231
|
+
for (let attempt = 0;; attempt += 1) {
|
|
232
|
+
try {
|
|
233
|
+
return (await this.#sdk.snapshots.capture(request, call)).data;
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
const busy = toStateMachinesError(error);
|
|
237
|
+
const waitMs = Math.max(busy.retryAfter ?? 0, backoff(attempt));
|
|
238
|
+
if (busy.code !== 'snapshot-limit' ||
|
|
239
|
+
Date.now() + waitMs >= deadline) {
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
await sleep(waitMs, call.signal);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
throw new StateMachinesError(error, { idempotencyKey });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
166
251
|
#reveal(environmentId, appKey, body, options) {
|
|
167
252
|
return this.#unwrap(options, (call) => this.#sdk.environments.reveal({ environmentId, appKey, body }, call));
|
|
168
253
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Environment } from '../models/environment.js';
|
|
2
|
+
import type { EnvironmentSnapshot } from '../models/environment-snapshot.js';
|
|
2
3
|
import type { Code } from '../models/error-response.js';
|
|
3
4
|
/** What `environments.start()` adds to the error it rethrows after a failed wait. */
|
|
4
5
|
export interface StartFailure {
|
|
@@ -30,6 +31,8 @@ export declare class StateMachinesError extends Error implements Details, StartF
|
|
|
30
31
|
readonly environmentId?: string;
|
|
31
32
|
readonly stopped?: boolean;
|
|
32
33
|
readonly stopError?: unknown;
|
|
34
|
+
/** Set when `snapshots.capture()` fails after the snapshot was created. */
|
|
35
|
+
readonly snapshotId?: string;
|
|
33
36
|
readonly status: number | null;
|
|
34
37
|
readonly code: Code | null;
|
|
35
38
|
readonly requestId: string | null;
|
|
@@ -38,7 +41,7 @@ export declare class StateMachinesError extends Error implements Details, StartF
|
|
|
38
41
|
readonly retryAfter: number | null;
|
|
39
42
|
/** Transient: a 502, 503 or 504, or the request never got a response. */
|
|
40
43
|
readonly retryable: boolean;
|
|
41
|
-
/** Set on
|
|
44
|
+
/** Set on create and capture failures. A replay with it returns the same result, so use it when the outcome is unknown. */
|
|
42
45
|
readonly idempotencyKey?: string;
|
|
43
46
|
/** Wraps any thrown value. Wrapping a `StateMachinesError` copies it, keeping its cause. */
|
|
44
47
|
constructor(error: unknown, extra?: {
|
|
@@ -68,4 +71,26 @@ export declare class TimeoutError extends Error implements StartFailure {
|
|
|
68
71
|
readonly stopError?: unknown;
|
|
69
72
|
constructor(timeoutMs: number, environment: Environment | null);
|
|
70
73
|
}
|
|
74
|
+
/** Thrown from a hook so the generated retry loop stops, whatever the abort reason. */
|
|
75
|
+
export declare class CallerAbortedError extends Error {
|
|
76
|
+
readonly name = "CallerAbortedError";
|
|
77
|
+
constructor(reason: unknown);
|
|
78
|
+
}
|
|
79
|
+
/** A snapshot that ended `failed`, or was deleted, while the SDK waited for `ready`. */
|
|
80
|
+
export declare class SnapshotFailedError extends Error {
|
|
81
|
+
readonly snapshot: EnvironmentSnapshot;
|
|
82
|
+
readonly name = "SnapshotFailedError";
|
|
83
|
+
readonly snapshotId: string;
|
|
84
|
+
readonly idempotencyKey?: string;
|
|
85
|
+
constructor(snapshot: EnvironmentSnapshot);
|
|
86
|
+
}
|
|
87
|
+
/** The wait for `ready` passed its deadline. The capture may still finish on the server. */
|
|
88
|
+
export declare class SnapshotTimeoutError extends Error {
|
|
89
|
+
readonly timeoutMs: number;
|
|
90
|
+
readonly snapshot: EnvironmentSnapshot | null;
|
|
91
|
+
readonly snapshotId: string;
|
|
92
|
+
readonly name = "SnapshotTimeoutError";
|
|
93
|
+
readonly idempotencyKey?: string;
|
|
94
|
+
constructor(timeoutMs: number, snapshot: EnvironmentSnapshot | null, snapshotId: string);
|
|
95
|
+
}
|
|
71
96
|
export {};
|
package/esm/extensions/errors.js
CHANGED
|
@@ -17,7 +17,7 @@ export class StateMachinesError extends Error {
|
|
|
17
17
|
retryAfter;
|
|
18
18
|
/** Transient: a 502, 503 or 504, or the request never got a response. */
|
|
19
19
|
retryable;
|
|
20
|
-
/** Set on
|
|
20
|
+
/** Set on create and capture failures. A replay with it returns the same result, so use it when the outcome is unknown. */
|
|
21
21
|
idempotencyKey;
|
|
22
22
|
/** Wraps any thrown value. Wrapping a `StateMachinesError` copies it, keeping its cause. */
|
|
23
23
|
constructor(error, extra = {}) {
|
|
@@ -128,3 +128,36 @@ export class TimeoutError extends Error {
|
|
|
128
128
|
this.environment = environment;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
+
/** Thrown from a hook so the generated retry loop stops, whatever the abort reason. */
|
|
132
|
+
export class CallerAbortedError extends Error {
|
|
133
|
+
name = 'CallerAbortedError';
|
|
134
|
+
constructor(reason) {
|
|
135
|
+
super('The caller aborted the request.', { cause: reason });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** A snapshot that ended `failed`, or was deleted, while the SDK waited for `ready`. */
|
|
139
|
+
export class SnapshotFailedError extends Error {
|
|
140
|
+
snapshot;
|
|
141
|
+
name = 'SnapshotFailedError';
|
|
142
|
+
snapshotId;
|
|
143
|
+
constructor(snapshot) {
|
|
144
|
+
super(snapshot.status === 'failed'
|
|
145
|
+
? `Snapshot ${snapshot.id} is failed (${snapshot.errorCode ?? 'no error code'}).`
|
|
146
|
+
: `Snapshot ${snapshot.id} was deleted.`);
|
|
147
|
+
this.snapshot = snapshot;
|
|
148
|
+
this.snapshotId = snapshot.id;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** The wait for `ready` passed its deadline. The capture may still finish on the server. */
|
|
152
|
+
export class SnapshotTimeoutError extends Error {
|
|
153
|
+
timeoutMs;
|
|
154
|
+
snapshot;
|
|
155
|
+
snapshotId;
|
|
156
|
+
name = 'SnapshotTimeoutError';
|
|
157
|
+
constructor(timeoutMs, snapshot, snapshotId) {
|
|
158
|
+
super(`Timed out after ${timeoutMs} ms waiting for snapshot ${snapshotId}.`);
|
|
159
|
+
this.timeoutMs = timeoutMs;
|
|
160
|
+
this.snapshot = snapshot;
|
|
161
|
+
this.snapshotId = snapshotId;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export type { Fetcher, HTTPClientOptions } from '../lib/http.js';
|
|
2
2
|
export { HTTPClient } from '../lib/http.js';
|
|
3
3
|
export { ConnectionError, ErrorResponse, HTTPClientError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, ResponseValidationError, SDKValidationError, StateMachinesHTTPError, UnexpectedClientError, UnexpectedResponseError, } from '../models/errors/index.js';
|
|
4
|
-
export type { ApiProfile, App, Catalog, Connection as ConnectionDetails, ConnectionSecret, CreateEnvironmentInput, Credential, Environment, EnvironmentApp, Event, EventPage, Release, ReleaseActor, Workspace, } from '../models/index.js';
|
|
4
|
+
export type { ApiProfile, App, AppInstanceSnapshot, Catalog, Connection as ConnectionDetails, ConnectionSecret, CreateEnvironmentInput, Credential, Environment, EnvironmentApp, EnvironmentSnapshot, Event, EventPage, Release, ReleaseActor, Workspace, } from '../models/index.js';
|
|
5
5
|
export { Code as ErrorCode, Disposal, EnvironmentStatus, StopReason, } from '../models/index.js';
|
|
6
6
|
export { Status as EnvironmentListStatus } from '../models/operations/list-environments.js';
|
|
7
|
-
export type { AppInput, EnvironmentInput } from './apps.js';
|
|
8
|
-
export type { CallOptions, ConnectOptions, CreateOptions, ListEnvironmentsQuery, Page, StartOptions, StateMachinesOptions, } from './client.js';
|
|
7
|
+
export type { AppInput, EnvironmentInput, SnapshotEnvironmentInput, } from './apps.js';
|
|
8
|
+
export type { CallOptions, CaptureInput, CaptureOptions, ConnectOptions, CreateOptions, ListEnvironmentsQuery, ListSnapshotsQuery, Page, StartOptions, StateMachinesOptions, } from './client.js';
|
|
9
9
|
export { StateMachines } from './client.js';
|
|
10
10
|
export { Connection, formatActorHeader } from './connection.js';
|
|
11
|
-
export { DisposalIncompleteError, EnvironmentFailedError, StateMachinesError, TimeoutError, } from './errors.js';
|
|
11
|
+
export { DisposalIncompleteError, EnvironmentFailedError, SnapshotFailedError, SnapshotTimeoutError, StateMachinesError, TimeoutError, } from './errors.js';
|
|
12
12
|
export { RunningEnvironment } from './running-environment.js';
|
|
13
13
|
export type { WaitOptions } from './wait.js';
|
package/esm/extensions/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export { Code as ErrorCode, Disposal, EnvironmentStatus, StopReason, } from '../
|
|
|
4
4
|
export { Status as EnvironmentListStatus } from '../models/operations/list-environments.js';
|
|
5
5
|
export { StateMachines } from './client.js';
|
|
6
6
|
export { Connection, formatActorHeader } from './connection.js';
|
|
7
|
-
export { DisposalIncompleteError, EnvironmentFailedError, StateMachinesError, TimeoutError, } from './errors.js';
|
|
7
|
+
export { DisposalIncompleteError, EnvironmentFailedError, SnapshotFailedError, SnapshotTimeoutError, StateMachinesError, TimeoutError, } from './errors.js';
|
|
8
8
|
export { RunningEnvironment } from './running-environment.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Environment } from '../models/environment.js';
|
|
2
|
+
import type { EnvironmentSnapshot } from '../models/environment-snapshot.js';
|
|
2
3
|
import type { ReleaseActor } from '../models/release-actor.js';
|
|
3
|
-
import type { CallOptions, ConnectOptions, StateMachines } from './client.js';
|
|
4
|
+
import type { CallOptions, CaptureInput, CaptureOptions, ConnectOptions, StateMachines } from './client.js';
|
|
4
5
|
import type { Connection } from './connection.js';
|
|
5
6
|
import type { WaitOptions } from './wait.js';
|
|
6
7
|
/** A started environment. It remains the handle after `stop()`. */
|
|
@@ -18,6 +19,8 @@ export declare class RunningEnvironment {
|
|
|
18
19
|
connect(appKey: string, options?: ConnectOptions): Promise<Connection>;
|
|
19
20
|
/** The app's users, in the release's order. */
|
|
20
21
|
actors(appKey: string, options?: CallOptions): Promise<ReleaseActor[]>;
|
|
22
|
+
/** Captures a snapshot in the environment's own workspace and waits until it's `ready`. */
|
|
23
|
+
snapshot(input?: CaptureInput, options?: CaptureOptions): Promise<EnvironmentSnapshot>;
|
|
21
24
|
/** Safe to call twice. Doesn't wait for disposal: use `waitUntilStopped()`. */
|
|
22
25
|
stop(options?: CallOptions): Promise<Environment>;
|
|
23
26
|
/** Resolves when the environment ended and every sandbox is confirmed disposed. */
|
|
@@ -4,10 +4,12 @@ export class RunningEnvironment {
|
|
|
4
4
|
/** The record when `start()` resolved. Use `sm.environments.get(id)` for fresh state. */
|
|
5
5
|
environment;
|
|
6
6
|
#environments;
|
|
7
|
+
#snapshots;
|
|
7
8
|
constructor(sm, environment) {
|
|
8
9
|
this.id = environment.id;
|
|
9
10
|
this.environment = environment;
|
|
10
11
|
this.#environments = sm.environments;
|
|
12
|
+
this.#snapshots = sm.snapshots;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* actorId defaults to the actor of the environment's declared default connection, or
|
|
@@ -21,6 +23,13 @@ export class RunningEnvironment {
|
|
|
21
23
|
actors(appKey, options) {
|
|
22
24
|
return this.#environments.actors(this.id, appKey, options);
|
|
23
25
|
}
|
|
26
|
+
/** Captures a snapshot in the environment's own workspace and waits until it's `ready`. */
|
|
27
|
+
snapshot(input, options = {}) {
|
|
28
|
+
return this.#snapshots.capture(this.id, input, {
|
|
29
|
+
...options,
|
|
30
|
+
workspaceId: options.workspaceId ?? this.environment.workspaceId,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
24
33
|
/** Safe to call twice. Doesn't wait for disposal: use `waitUntilStopped()`. */
|
|
25
34
|
stop(options) {
|
|
26
35
|
return this.#environments.stop(this.id, options);
|
package/esm/extensions/wait.d.ts
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import type { Environment } from '../models/environment.js';
|
|
2
|
+
import type { EnvironmentSnapshot } from '../models/environment-snapshot.js';
|
|
2
3
|
export interface WaitOptions {
|
|
3
4
|
/** Overall deadline for the wait. Defaults to 10 minutes. */
|
|
4
5
|
timeoutMs?: number;
|
|
5
6
|
/** Aborting rejects the wait with `signal.reason`. */
|
|
6
7
|
signal?: AbortSignal;
|
|
7
8
|
}
|
|
8
|
-
type Read = (signal: AbortSignal) => Promise<
|
|
9
|
-
type Decide = (
|
|
9
|
+
type Read<T> = (signal: AbortSignal) => Promise<T>;
|
|
10
|
+
type Decide<T> = (value: T) => boolean;
|
|
10
11
|
export declare const defaultTimeoutMs = 600000;
|
|
12
|
+
export declare function poll(read: Read<Environment>, done: Decide<Environment>, options?: WaitOptions): Promise<Environment>;
|
|
11
13
|
/**
|
|
12
14
|
* Reads until `done` returns true. `done` throws to end the wait with a failure.
|
|
13
15
|
* Transient read failures are retried with backoff until the deadline.
|
|
14
16
|
*/
|
|
15
|
-
export declare function
|
|
17
|
+
export declare function pollUntil<T>(read: Read<T>, done: Decide<T>, options: WaitOptions | undefined, timeout: (timeoutMs: number, last: T | null) => Error): Promise<T>;
|
|
16
18
|
/** Resolves on `running`. Never stops the environment. */
|
|
17
19
|
export declare function ready(environment: Environment): boolean;
|
|
18
20
|
/** Resolves once the environment ended and every app's sandbox is confirmed disposed. */
|
|
19
21
|
export declare function disposed(environment: Environment): boolean;
|
|
22
|
+
/** Resolves on `ready`. A failed or deleted snapshot ends the wait. */
|
|
23
|
+
export declare function snapshotReady(snapshot: EnvironmentSnapshot): boolean;
|
|
24
|
+
/** 1 s × 1.5ⁿ, capped at 5 s, with ±20% jitter. */
|
|
25
|
+
export declare function backoff(attempt: number): number;
|
|
26
|
+
export declare function sleep(ms: number, signal: AbortSignal | null | undefined): Promise<void>;
|
|
20
27
|
export {};
|
package/esm/extensions/wait.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { DisposalIncompleteError, EnvironmentFailedError, TimeoutError, toStateMachinesError, } from './errors.js';
|
|
1
|
+
import { DisposalIncompleteError, EnvironmentFailedError, SnapshotFailedError, TimeoutError, toStateMachinesError, } from './errors.js';
|
|
2
2
|
import { linkSignal } from './signal.js';
|
|
3
3
|
export const defaultTimeoutMs = 600000;
|
|
4
4
|
const readTimeoutMs = 30000;
|
|
5
|
+
export function poll(read, done, options) {
|
|
6
|
+
return pollUntil(read, done, options, (timeoutMs, last) => new TimeoutError(timeoutMs, last));
|
|
7
|
+
}
|
|
5
8
|
/**
|
|
6
9
|
* Reads until `done` returns true. `done` throws to end the wait with a failure.
|
|
7
10
|
* Transient read failures are retried with backoff until the deadline.
|
|
8
11
|
*/
|
|
9
|
-
export async function
|
|
12
|
+
export async function pollUntil(read, done, options = {}, timeout) {
|
|
10
13
|
const { signal } = options;
|
|
11
14
|
const timeoutMs = options.timeoutMs ?? defaultTimeoutMs;
|
|
12
15
|
if (!(timeoutMs >= 0)) {
|
|
@@ -18,20 +21,20 @@ export async function poll(read, done, options = {}) {
|
|
|
18
21
|
signal?.throwIfAborted();
|
|
19
22
|
const remaining = deadline - Date.now();
|
|
20
23
|
if (remaining <= 0) {
|
|
21
|
-
throw
|
|
24
|
+
throw timeout(timeoutMs, last);
|
|
22
25
|
}
|
|
23
26
|
let waitMs = backoff(attempt);
|
|
24
|
-
let
|
|
27
|
+
let value = null;
|
|
25
28
|
const link = linkSignal(signal, Math.min(readTimeoutMs, remaining));
|
|
26
29
|
try {
|
|
27
|
-
|
|
28
|
-
last =
|
|
30
|
+
value = await read(link.signal);
|
|
31
|
+
last = value;
|
|
29
32
|
}
|
|
30
33
|
catch (error) {
|
|
31
34
|
// Classify by signal state: a stalled body read surfaces as an arbitrary error.
|
|
32
35
|
signal?.throwIfAborted();
|
|
33
36
|
if (Date.now() >= deadline) {
|
|
34
|
-
throw
|
|
37
|
+
throw timeout(timeoutMs, last);
|
|
35
38
|
}
|
|
36
39
|
if (!link.signal.aborted) {
|
|
37
40
|
const failure = toStateMachinesError(error);
|
|
@@ -47,10 +50,10 @@ export async function poll(read, done, options = {}) {
|
|
|
47
50
|
// A read can settle after an abort or past the deadline; neither may resolve the wait.
|
|
48
51
|
signal?.throwIfAborted();
|
|
49
52
|
if (Date.now() >= deadline) {
|
|
50
|
-
throw
|
|
53
|
+
throw timeout(timeoutMs, last);
|
|
51
54
|
}
|
|
52
|
-
if (
|
|
53
|
-
return
|
|
55
|
+
if (value !== null && done(value)) {
|
|
56
|
+
return value;
|
|
54
57
|
}
|
|
55
58
|
await sleep(Math.min(waitMs, deadline - Date.now()), signal);
|
|
56
59
|
}
|
|
@@ -79,11 +82,18 @@ export function disposed(environment) {
|
|
|
79
82
|
}
|
|
80
83
|
return environment.apps.every((app) => app.disposal === 'complete');
|
|
81
84
|
}
|
|
85
|
+
/** Resolves on `ready`. A failed or deleted snapshot ends the wait. */
|
|
86
|
+
export function snapshotReady(snapshot) {
|
|
87
|
+
if (snapshot.status === 'failed' || snapshot.deletedAt) {
|
|
88
|
+
throw new SnapshotFailedError(snapshot);
|
|
89
|
+
}
|
|
90
|
+
return snapshot.status === 'ready';
|
|
91
|
+
}
|
|
82
92
|
/** 1 s × 1.5ⁿ, capped at 5 s, with ±20% jitter. */
|
|
83
|
-
function backoff(attempt) {
|
|
93
|
+
export function backoff(attempt) {
|
|
84
94
|
return Math.min(5000, 1000 * 1.5 ** attempt) * (0.8 + Math.random() * 0.4);
|
|
85
95
|
}
|
|
86
|
-
function sleep(ms, signal) {
|
|
96
|
+
export function sleep(ms, signal) {
|
|
87
97
|
return new Promise((resolve, reject) => {
|
|
88
98
|
if (signal?.aborted) {
|
|
89
99
|
reject(signal.reason);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StateMachinesCore } from "../core.js";
|
|
2
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
3
|
+
import { ConnectionError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, UnexpectedClientError } from "../models/errors/http-client-errors.js";
|
|
4
|
+
import * as errors from "../models/errors/index.js";
|
|
5
|
+
import { ResponseValidationError } from "../models/errors/response-validation-error.js";
|
|
6
|
+
import { SDKValidationError } from "../models/errors/sdk-validation-error.js";
|
|
7
|
+
import { StateMachinesHTTPError } from "../models/errors/state-machines-http-error.js";
|
|
8
|
+
import * as operations from "../models/operations/index.js";
|
|
9
|
+
import { APIPromise } from "../types/async.js";
|
|
10
|
+
import { Result } from "../types/fp.js";
|
|
11
|
+
export declare function snapshotsCapture(client: StateMachinesCore, request: operations.CaptureSnapshotRequest, options?: RequestOptions): APIPromise<Result<operations.CaptureSnapshotResponse, errors.ErrorResponse | StateMachinesHTTPError | ResponseValidationError | ConnectionError | RequestAbortedError | RequestTimeoutError | InvalidRequestError | UnexpectedClientError | SDKValidationError>>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
|
3
|
+
*/
|
|
4
|
+
import * as z from "zod/v4-mini";
|
|
5
|
+
import { encodeJSON, encodeSimple } from "../lib/encodings.js";
|
|
6
|
+
import { matchStatusCode } from "../lib/http.js";
|
|
7
|
+
import * as M from "../lib/matchers.js";
|
|
8
|
+
import { compactMap } from "../lib/primitives.js";
|
|
9
|
+
import { safeParse } from "../lib/schemas.js";
|
|
10
|
+
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
|
|
11
|
+
import { pathToFunc } from "../lib/url.js";
|
|
12
|
+
import * as errors from "../models/errors/index.js";
|
|
13
|
+
import * as operations from "../models/operations/index.js";
|
|
14
|
+
import { APIPromise } from "../types/async.js";
|
|
15
|
+
export function snapshotsCapture(client, request, options) {
|
|
16
|
+
return new APIPromise($do(client, request, options));
|
|
17
|
+
}
|
|
18
|
+
async function $do(client, request, options) {
|
|
19
|
+
const parsed = safeParse(request, (value) => z.parse(operations.CaptureSnapshotRequest$outboundSchema, value), "Input validation failed");
|
|
20
|
+
if (!parsed.ok) {
|
|
21
|
+
return [parsed, { status: "invalid" }];
|
|
22
|
+
}
|
|
23
|
+
const payload = parsed.value;
|
|
24
|
+
const body = encodeJSON("body", payload.body, { explode: true });
|
|
25
|
+
const pathParams = {
|
|
26
|
+
workspaceId: encodeSimple("workspaceId", payload.workspaceId, {
|
|
27
|
+
explode: false,
|
|
28
|
+
charEncoding: "percent",
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
const path = pathToFunc("/v1/workspaces/{workspaceId}/snapshots")(pathParams);
|
|
32
|
+
const headers = new Headers(compactMap({
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
Accept: "application/json",
|
|
35
|
+
"idempotency-key": encodeSimple("idempotency-key", payload["idempotency-key"], { explode: false, charEncoding: "none" }),
|
|
36
|
+
}));
|
|
37
|
+
const secConfig = await extractSecurity(client._options.apiKey);
|
|
38
|
+
const securityInput = secConfig == null ? {} : { apiKey: secConfig };
|
|
39
|
+
const requestSecurity = resolveGlobalSecurity(securityInput);
|
|
40
|
+
const context = {
|
|
41
|
+
options: client._options,
|
|
42
|
+
baseURL: options?.serverURL ?? client._baseURL ?? "",
|
|
43
|
+
operationID: "captureSnapshot",
|
|
44
|
+
oAuth2Scopes: null,
|
|
45
|
+
resolvedSecurity: requestSecurity,
|
|
46
|
+
securitySource: client._options.apiKey,
|
|
47
|
+
retryConfig: options?.retries
|
|
48
|
+
|| client._options.retryConfig
|
|
49
|
+
|| {
|
|
50
|
+
strategy: "backoff",
|
|
51
|
+
backoff: {
|
|
52
|
+
initialInterval: 1000,
|
|
53
|
+
maxInterval: 5000,
|
|
54
|
+
exponent: 1.5,
|
|
55
|
+
maxElapsedTime: 600000,
|
|
56
|
+
},
|
|
57
|
+
retryConnectionErrors: true,
|
|
58
|
+
}
|
|
59
|
+
|| { strategy: "none" },
|
|
60
|
+
retryCodes: options?.retryCodes || ["502", "503", "504"],
|
|
61
|
+
};
|
|
62
|
+
const requestRes = client._createRequest(context, {
|
|
63
|
+
security: requestSecurity,
|
|
64
|
+
method: "POST",
|
|
65
|
+
baseURL: options?.serverURL,
|
|
66
|
+
path: path,
|
|
67
|
+
headers: headers,
|
|
68
|
+
body: body,
|
|
69
|
+
userAgent: client._options.userAgent,
|
|
70
|
+
timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1,
|
|
71
|
+
}, options);
|
|
72
|
+
if (!requestRes.ok) {
|
|
73
|
+
return [requestRes, { status: "invalid" }];
|
|
74
|
+
}
|
|
75
|
+
const req = requestRes.value;
|
|
76
|
+
const doResult = await client._do(req, {
|
|
77
|
+
context,
|
|
78
|
+
isErrorStatusCode: (statusCode) => matchStatusCode({ status: statusCode }, ["4XX", "5XX"]),
|
|
79
|
+
retryConfig: context.retryConfig,
|
|
80
|
+
retryCodes: context.retryCodes,
|
|
81
|
+
});
|
|
82
|
+
if (!doResult.ok) {
|
|
83
|
+
return [doResult, { status: "request-error", request: req }];
|
|
84
|
+
}
|
|
85
|
+
const response = doResult.value;
|
|
86
|
+
const responseFields = {
|
|
87
|
+
HttpMeta: { Response: response, Request: req },
|
|
88
|
+
};
|
|
89
|
+
const [result] = await M.match(M.json(202, operations.CaptureSnapshotResponse$inboundSchema), M.jsonErr([400, 401, 403, 404, 409, 413, 422, 429], errors.ErrorResponse$inboundSchema), M.jsonErr([500, 503], errors.ErrorResponse$inboundSchema), M.fail("4XX"), M.fail("5XX"))(response, req, { extraFields: responseFields });
|
|
90
|
+
if (!result.ok) {
|
|
91
|
+
return [result, { status: "complete", request: req, response }];
|
|
92
|
+
}
|
|
93
|
+
return [result, { status: "complete", request: req, response }];
|
|
94
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StateMachinesCore } from "../core.js";
|
|
2
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
3
|
+
import { ConnectionError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, UnexpectedClientError } from "../models/errors/http-client-errors.js";
|
|
4
|
+
import * as errors from "../models/errors/index.js";
|
|
5
|
+
import { ResponseValidationError } from "../models/errors/response-validation-error.js";
|
|
6
|
+
import { SDKValidationError } from "../models/errors/sdk-validation-error.js";
|
|
7
|
+
import { StateMachinesHTTPError } from "../models/errors/state-machines-http-error.js";
|
|
8
|
+
import * as operations from "../models/operations/index.js";
|
|
9
|
+
import { APIPromise } from "../types/async.js";
|
|
10
|
+
import { Result } from "../types/fp.js";
|
|
11
|
+
export declare function snapshotsDelete(client: StateMachinesCore, request: operations.DeleteSnapshotRequest, options?: RequestOptions): APIPromise<Result<operations.DeleteSnapshotResponse, errors.ErrorResponse | StateMachinesHTTPError | ResponseValidationError | ConnectionError | RequestAbortedError | RequestTimeoutError | InvalidRequestError | UnexpectedClientError | SDKValidationError>>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
|
3
|
+
*/
|
|
4
|
+
import * as z from "zod/v4-mini";
|
|
5
|
+
import { encodeSimple } from "../lib/encodings.js";
|
|
6
|
+
import { matchStatusCode } from "../lib/http.js";
|
|
7
|
+
import * as M from "../lib/matchers.js";
|
|
8
|
+
import { compactMap } from "../lib/primitives.js";
|
|
9
|
+
import { safeParse } from "../lib/schemas.js";
|
|
10
|
+
import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
|
|
11
|
+
import { pathToFunc } from "../lib/url.js";
|
|
12
|
+
import * as errors from "../models/errors/index.js";
|
|
13
|
+
import * as operations from "../models/operations/index.js";
|
|
14
|
+
import { APIPromise } from "../types/async.js";
|
|
15
|
+
export function snapshotsDelete(client, request, options) {
|
|
16
|
+
return new APIPromise($do(client, request, options));
|
|
17
|
+
}
|
|
18
|
+
async function $do(client, request, options) {
|
|
19
|
+
const parsed = safeParse(request, (value) => z.parse(operations.DeleteSnapshotRequest$outboundSchema, value), "Input validation failed");
|
|
20
|
+
if (!parsed.ok) {
|
|
21
|
+
return [parsed, { status: "invalid" }];
|
|
22
|
+
}
|
|
23
|
+
const payload = parsed.value;
|
|
24
|
+
const body = null;
|
|
25
|
+
const pathParams = {
|
|
26
|
+
snapshotId: encodeSimple("snapshotId", payload.snapshotId, {
|
|
27
|
+
explode: false,
|
|
28
|
+
charEncoding: "percent",
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
const path = pathToFunc("/v1/snapshots/{snapshotId}")(pathParams);
|
|
32
|
+
const headers = new Headers(compactMap({
|
|
33
|
+
Accept: "application/json",
|
|
34
|
+
}));
|
|
35
|
+
const secConfig = await extractSecurity(client._options.apiKey);
|
|
36
|
+
const securityInput = secConfig == null ? {} : { apiKey: secConfig };
|
|
37
|
+
const requestSecurity = resolveGlobalSecurity(securityInput);
|
|
38
|
+
const context = {
|
|
39
|
+
options: client._options,
|
|
40
|
+
baseURL: options?.serverURL ?? client._baseURL ?? "",
|
|
41
|
+
operationID: "deleteSnapshot",
|
|
42
|
+
oAuth2Scopes: null,
|
|
43
|
+
resolvedSecurity: requestSecurity,
|
|
44
|
+
securitySource: client._options.apiKey,
|
|
45
|
+
retryConfig: options?.retries
|
|
46
|
+
|| client._options.retryConfig
|
|
47
|
+
|| {
|
|
48
|
+
strategy: "backoff",
|
|
49
|
+
backoff: {
|
|
50
|
+
initialInterval: 1000,
|
|
51
|
+
maxInterval: 5000,
|
|
52
|
+
exponent: 1.5,
|
|
53
|
+
maxElapsedTime: 600000,
|
|
54
|
+
},
|
|
55
|
+
retryConnectionErrors: true,
|
|
56
|
+
}
|
|
57
|
+
|| { strategy: "none" },
|
|
58
|
+
retryCodes: options?.retryCodes || ["502", "503", "504"],
|
|
59
|
+
};
|
|
60
|
+
const requestRes = client._createRequest(context, {
|
|
61
|
+
security: requestSecurity,
|
|
62
|
+
method: "DELETE",
|
|
63
|
+
baseURL: options?.serverURL,
|
|
64
|
+
path: path,
|
|
65
|
+
headers: headers,
|
|
66
|
+
body: body,
|
|
67
|
+
userAgent: client._options.userAgent,
|
|
68
|
+
timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1,
|
|
69
|
+
}, options);
|
|
70
|
+
if (!requestRes.ok) {
|
|
71
|
+
return [requestRes, { status: "invalid" }];
|
|
72
|
+
}
|
|
73
|
+
const req = requestRes.value;
|
|
74
|
+
const doResult = await client._do(req, {
|
|
75
|
+
context,
|
|
76
|
+
isErrorStatusCode: (statusCode) => matchStatusCode({ status: statusCode }, ["4XX", "5XX"]),
|
|
77
|
+
retryConfig: context.retryConfig,
|
|
78
|
+
retryCodes: context.retryCodes,
|
|
79
|
+
});
|
|
80
|
+
if (!doResult.ok) {
|
|
81
|
+
return [doResult, { status: "request-error", request: req }];
|
|
82
|
+
}
|
|
83
|
+
const response = doResult.value;
|
|
84
|
+
const responseFields = {
|
|
85
|
+
HttpMeta: { Response: response, Request: req },
|
|
86
|
+
};
|
|
87
|
+
const [result] = await M.match(M.json(200, operations.DeleteSnapshotResponse$inboundSchema), M.jsonErr([400, 401, 403, 404, 409, 413, 422, 429], errors.ErrorResponse$inboundSchema), M.jsonErr([500, 503], errors.ErrorResponse$inboundSchema), M.fail("4XX"), M.fail("5XX"))(response, req, { extraFields: responseFields });
|
|
88
|
+
if (!result.ok) {
|
|
89
|
+
return [result, { status: "complete", request: req, response }];
|
|
90
|
+
}
|
|
91
|
+
return [result, { status: "complete", request: req, response }];
|
|
92
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StateMachinesCore } from "../core.js";
|
|
2
|
+
import { RequestOptions } from "../lib/sdks.js";
|
|
3
|
+
import { ConnectionError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, UnexpectedClientError } from "../models/errors/http-client-errors.js";
|
|
4
|
+
import * as errors from "../models/errors/index.js";
|
|
5
|
+
import { ResponseValidationError } from "../models/errors/response-validation-error.js";
|
|
6
|
+
import { SDKValidationError } from "../models/errors/sdk-validation-error.js";
|
|
7
|
+
import { StateMachinesHTTPError } from "../models/errors/state-machines-http-error.js";
|
|
8
|
+
import * as operations from "../models/operations/index.js";
|
|
9
|
+
import { APIPromise } from "../types/async.js";
|
|
10
|
+
import { Result } from "../types/fp.js";
|
|
11
|
+
export declare function snapshotsGet(client: StateMachinesCore, request: operations.GetSnapshotRequest, options?: RequestOptions): APIPromise<Result<operations.GetSnapshotResponse, errors.ErrorResponse | StateMachinesHTTPError | ResponseValidationError | ConnectionError | RequestAbortedError | RequestTimeoutError | InvalidRequestError | UnexpectedClientError | SDKValidationError>>;
|