@usestatemachines/sdk 0.1.1 → 0.2.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 (39) hide show
  1. package/README.md +175 -75
  2. package/agents/SKILL.md +24 -44
  3. package/agents/index.js +7 -7
  4. package/agents/llms.txt +18 -39
  5. package/agents/prompt.md +11 -34
  6. package/esm/extensions/apps.d.ts +8 -0
  7. package/esm/extensions/apps.js +23 -0
  8. package/esm/extensions/client.d.ts +33 -3
  9. package/esm/extensions/client.js +144 -28
  10. package/esm/extensions/errors.d.ts +24 -5
  11. package/esm/extensions/errors.js +3 -2
  12. package/esm/extensions/index.d.ts +3 -1
  13. package/esm/extensions/index.js +1 -0
  14. package/esm/extensions/running-environment.d.ts +29 -0
  15. package/esm/extensions/running-environment.js +35 -0
  16. package/esm/extensions/signal.d.ts +1 -2
  17. package/esm/extensions/signal.js +9 -4
  18. package/esm/extensions/wait.d.ts +1 -0
  19. package/esm/extensions/wait.js +1 -1
  20. package/esm/funcs/create-environment-definition.d.ts +11 -0
  21. package/esm/funcs/create-environment-definition.js +94 -0
  22. package/esm/lib/config.d.ts +2 -2
  23. package/esm/lib/config.js +2 -2
  24. package/esm/models/connection-secret.d.ts +16 -0
  25. package/esm/models/connection-secret.js +13 -0
  26. package/esm/models/connection.d.ts +14 -0
  27. package/esm/models/connection.js +14 -0
  28. package/esm/models/error-response.d.ts +4 -0
  29. package/esm/models/error-response.js +4 -0
  30. package/esm/models/operations/create-environment-definition.d.ts +143 -0
  31. package/esm/models/operations/create-environment-definition.js +90 -0
  32. package/esm/models/operations/index.d.ts +1 -0
  33. package/esm/models/operations/index.js +1 -0
  34. package/esm/models/operations/reveal-connection.d.ts +26 -4
  35. package/esm/models/operations/reveal-connection.js +28 -2
  36. package/esm/sdk/sdk.d.ts +3 -1
  37. package/esm/sdk/sdk.js +5 -0
  38. package/openapi.json +394 -7
  39. package/package.json +1 -1
@@ -1,10 +1,13 @@
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 { expandApps } from './apps.js';
4
5
  import { Connection } from './connection.js';
5
- import { StateMachinesError, toStateMachinesError } from './errors.js';
6
+ import { EnvironmentFailedError, StateMachinesError, TimeoutError, toStateMachinesError, } from './errors.js';
7
+ import { RunningEnvironment } from './running-environment.js';
6
8
  import { linkSignal } from './signal.js';
7
- import { disposed, poll, ready } from './wait.js';
9
+ import { defaultTimeoutMs, disposed, poll, ready, } from './wait.js';
10
+ const stopAfterFailureMs = 30000;
8
11
  /** Thrown from a hook so the generated retry loop stops, whatever the abort reason. */
9
12
  class CallerAbortedError extends Error {
10
13
  name = 'CallerAbortedError';
@@ -16,6 +19,8 @@ export class StateMachines {
16
19
  #sdk;
17
20
  #fetch;
18
21
  #workspaceId;
22
+ #callSignals = new Map();
23
+ #lastCall = 0;
19
24
  #credentialWorkspace;
20
25
  constructor(options = {}) {
21
26
  const apiKey = options.apiKey ?? environmentApiKey();
@@ -37,7 +42,7 @@ export class StateMachines {
37
42
  }
38
43
  const httpClient = options.httpClient?.clone() ??
39
44
  new HTTPClient(fetcher ? { fetcher: (input) => fetcher(input) } : {});
40
- addAttemptTimeout(httpClient, timeoutMs);
45
+ addAttemptTimeout(httpClient, timeoutMs, this.#callSignals);
41
46
  this.#fetch = fetcher;
42
47
  this.#workspaceId = options.workspaceId;
43
48
  this.#sdk = new Generated({
@@ -66,13 +71,56 @@ export class StateMachines {
66
71
  return this.#sdk.environments.list({ ...filters, workspaceId: resolved }, call);
67
72
  }),
68
73
  get: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.get({ environmentId }, call)),
69
- /** Every failure carries the `idempotencyKey`; replaying the create with it is safe. */
74
+ /**
75
+ * Creates an environment and waits until it's running. If the wait fails, stops the
76
+ * environment (unless the caller passed its own `idempotencyKey`), then rethrows the
77
+ * error with `environmentId`, `stopped` and `stopError`.
78
+ */
79
+ start: async (input, options = {}) => {
80
+ const { readyTimeoutMs, ...createOptions } = options;
81
+ const { idempotencyKey, signal } = options;
82
+ const timeoutMs = readyTimeoutMs ?? defaultTimeoutMs;
83
+ if (!(timeoutMs > 0)) {
84
+ throw new RangeError('readyTimeoutMs must be more than 0.');
85
+ }
86
+ const created = await this.environments.create(input, createOptions);
87
+ try {
88
+ const environment = await this.environments.waitUntilReady(created.id, {
89
+ timeoutMs,
90
+ ...(signal ? { signal } : {}),
91
+ });
92
+ return new RunningEnvironment(this, environment);
93
+ }
94
+ catch (error) {
95
+ // The API replays a create by key, returning the same environment to another
96
+ // start() that may still be using it, so only the SDK's own key allows a stop.
97
+ const outcome = idempotencyKey === undefined
98
+ ? await this.#stopAfterFailure(created.id)
99
+ : { environmentId: created.id, stopped: false };
100
+ const isCallerReason = signal?.aborted && Object.is(error, signal.reason);
101
+ if (isCallerReason) {
102
+ if (outcome.stopped) {
103
+ throw error;
104
+ }
105
+ throw Object.assign(new StateMachinesError(new CallerAbortedError(error)), outcome);
106
+ }
107
+ if (isStartError(error) && Object.isExtensible(error)) {
108
+ Object.assign(error, outcome);
109
+ }
110
+ throw error;
111
+ }
112
+ },
113
+ /**
114
+ * A string app is shorthand for `{ key: <the string>, appId: <the string> }`. Every
115
+ * failure carries the `idempotencyKey`; replaying the create with it is safe.
116
+ */
70
117
  create: async (input, options = {}) => {
71
118
  const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
72
119
  try {
120
+ const body = expandApps(input);
73
121
  return await this.#call(options, async (call) => {
74
122
  const workspaceId = await this.#workspace(options.workspaceId, call.signal);
75
- const { data } = await this.#sdk.environments.create({ workspaceId, idempotencyKey, body: input }, call);
123
+ const { data } = await this.#sdk.environments.create({ workspaceId, idempotencyKey, body }, call);
76
124
  const [environment] = data;
77
125
  if (!environment) {
78
126
  throw new Error('Invalid response from the State API: the create returned no environment.');
@@ -87,23 +135,51 @@ export class StateMachines {
87
135
  },
88
136
  stop: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.stop({ environmentId }, call)),
89
137
  events: (environmentId, query = {}, options) => this.#call(options, (call) => this.#sdk.environments.events({ ...query, environmentId }, call)),
138
+ /** The app's users, in the release's order. */
139
+ actors: async (environmentId, appKey, options) => (await this.environments.getConnection(environmentId, appKey, options))
140
+ .actors,
90
141
  getConnection: (environmentId, appKey, options) => this.#unwrap(options, (call) => this.#sdk.environments.getConnection({ environmentId, appKey }, call)),
91
- reveal: (environmentId, appKey, actorId, options) => this.#unwrap(options, (call) => this.#sdk.environments.reveal({ environmentId, appKey, body: { actorId } }, call)),
142
+ reveal: (environmentId, appKey, actorId, options) => this.#reveal(environmentId, appKey, { actorId }, options),
92
143
  /** Resolves when the environment is running. Never stops it. */
93
144
  waitUntilReady: (environmentId, options) => poll((signal) => this.#poll(environmentId, signal), ready, options),
94
145
  /** Resolves when the environment ended and every sandbox is confirmed disposed. */
95
146
  waitUntilStopped: (environmentId, options) => poll((signal) => this.#poll(environmentId, signal), disposed, options),
147
+ /**
148
+ * actorId defaults to the actor of the environment's declared default connection, or
149
+ * else the app's first actor (for Salesforce, the administrator). Pick another from
150
+ * actors() to act with that user's permissions.
151
+ */
96
152
  connect: async (environmentId, appKey, options = {}) => {
97
153
  const details = await this.environments.getConnection(environmentId, appKey, options);
98
154
  if (details.status !== 'ready' || !details.baseUrl) {
99
155
  throw new TypeError(`App ${appKey} is not ready to connect (${details.status}).`);
100
156
  }
101
- const actor = pick('actor', details.actors, options.actorId);
102
- const api = pick('API', details.apis, options.apiId);
103
- const secret = await this.environments.reveal(environmentId, appKey, actor.id, options);
157
+ // The API resolves { actorId } to that actor's first connection, not the declared one.
158
+ const body = options.actorId === undefined && details.defaultConnection
159
+ ? { connectionKey: details.defaultConnection }
160
+ : { actorId: pickActor(appKey, details.actors, options.actorId).id };
161
+ const api = pickApi(appKey, details.apis, options.apiId);
162
+ const secret = await this.#reveal(environmentId, appKey, body, options);
104
163
  return new Connection(details.baseUrl, api, secret, this.#fetch);
105
164
  },
106
165
  };
166
+ #reveal(environmentId, appKey, body, options) {
167
+ return this.#unwrap(options, (call) => this.#sdk.environments.reveal({ environmentId, appKey, body }, call));
168
+ }
169
+ async #stopAfterFailure(environmentId) {
170
+ const budget = linkSignal(undefined, stopAfterFailureMs);
171
+ try {
172
+ // abortable() ends the wait at the budget even during a retry delay.
173
+ await abortable(this.environments.stop(environmentId, { signal: budget.signal }), budget.signal);
174
+ return { environmentId, stopped: true };
175
+ }
176
+ catch (stopError) {
177
+ return { environmentId, stopped: false, stopError };
178
+ }
179
+ finally {
180
+ budget.dispose();
181
+ }
182
+ }
107
183
  /** One read for the poller, which owns retries, timing and the caller's signal. */
108
184
  #poll(environmentId, signal) {
109
185
  return this.#unwrap({ signal }, (call) => this.#sdk.environments.get({ environmentId }, { ...call, retries: { strategy: 'none' } }));
@@ -141,8 +217,13 @@ export class StateMachines {
141
217
  */
142
218
  async #call(options, run) {
143
219
  const scope = linkSignal(options?.signal);
220
+ const id = String(++this.#lastCall);
221
+ this.#callSignals.set(id, scope.signal);
144
222
  try {
145
- return await run({ signal: scope.signal });
223
+ return await run({
224
+ signal: scope.signal,
225
+ headers: { [CALL_HEADER]: id },
226
+ });
146
227
  }
147
228
  catch (error) {
148
229
  // A caller abort can surface as a raw TimeoutError while a body is read; it is never retryable.
@@ -151,35 +232,53 @@ export class StateMachines {
151
232
  : error);
152
233
  }
153
234
  finally {
235
+ this.#callSignals.delete(id);
154
236
  scope.close();
155
237
  }
156
238
  }
157
239
  }
158
240
  /**
159
- * Gives each attempt its own timeout, linked to the caller's signal. The generated
241
+ * Names the call a request belongs to. Never sent: the attempt hook removes it.
242
+ * Needed because the generated loop clones each attempt, and after a garbage
243
+ * collection a clone's signal no longer follows the call's (Node 20 and 24).
244
+ */
245
+ export const CALL_HEADER = 'x-statemachines-call';
246
+ /**
247
+ * Gives each attempt its own timeout, linked to its call's signal. The generated
160
248
  * `timeoutMs` is never used: it is dropped when a caller passes a signal.
161
249
  */
162
- function addAttemptTimeout(httpClient, timeoutMs) {
250
+ function addAttemptTimeout(httpClient, timeoutMs, calls) {
163
251
  const callers = new WeakMap();
164
252
  httpClient.addHook('beforeRequest', (request) => {
165
- const caller = request.signal;
166
- if (caller.aborted) {
167
- throw new CallerAbortedError(caller.reason);
253
+ const headers = new Headers(request.headers);
254
+ const call = calls.get(headers.get(CALL_HEADER) ?? '');
255
+ // The request's own signal still counts: a caller's hook may have set it.
256
+ const sources = call ? [call, request.signal] : [request.signal];
257
+ const aborted = sources.find(({ aborted }) => aborted);
258
+ headers.delete(CALL_HEADER);
259
+ if (aborted) {
260
+ throw new CallerAbortedError(aborted.reason);
168
261
  }
169
262
  const attempt = new Request(request, {
170
- signal: linkSignal(caller, timeoutMs).signal,
263
+ headers,
264
+ signal: linkSignal(sources, timeoutMs).signal,
171
265
  });
172
- callers.set(attempt, caller);
266
+ callers.set(attempt, sources);
173
267
  return attempt;
174
268
  });
175
269
  // Without this, a timeout-shaped abort reason looks like a retryable timeout to the generated loop.
176
270
  httpClient.addHook('requestError', (_error, request) => {
177
- const caller = callers.get(request);
178
- if (caller?.aborted) {
179
- throw new CallerAbortedError(caller.reason);
271
+ const aborted = callers.get(request)?.find(({ aborted }) => aborted);
272
+ if (aborted) {
273
+ throw new CallerAbortedError(aborted.reason);
180
274
  }
181
275
  });
182
276
  }
277
+ function isStartError(error) {
278
+ return (error instanceof StateMachinesError ||
279
+ error instanceof EnvironmentFailedError ||
280
+ error instanceof TimeoutError);
281
+ }
183
282
  function abortable(promise, signal) {
184
283
  if (!signal) {
185
284
  return promise;
@@ -196,16 +295,33 @@ function abortable(promise, signal) {
196
295
  .finally(() => signal.removeEventListener('abort', onAbort));
197
296
  });
198
297
  }
199
- function pick(kind, choices, id) {
200
- const found = id
201
- ? choices.find((choice) => choice.id === id)
202
- : choices.length === 1
203
- ? choices[0]
298
+ function pickActor(appKey, actors, actorId) {
299
+ const [first] = actors;
300
+ if (!first) {
301
+ throw new TypeError(`App ${appKey} has no actors.`);
302
+ }
303
+ if (actorId === undefined) {
304
+ return first;
305
+ }
306
+ const actor = actors.find(({ id }) => id === actorId);
307
+ if (!actor) {
308
+ throw new TypeError(`Unknown actor ${actorId} for ${appKey}. Choose one of: ${ids(actors)}.`);
309
+ }
310
+ return actor;
311
+ }
312
+ function pickApi(appKey, apis, apiId) {
313
+ const api = apiId
314
+ ? apis.find(({ id }) => id === apiId)
315
+ : apis.length === 1
316
+ ? apis[0]
204
317
  : undefined;
205
- if (!found) {
206
- throw new TypeError(`Choose one ${kind}: ${choices.map((choice) => choice.id).join(', ') || 'none available'}.`);
318
+ if (!api) {
319
+ throw new TypeError(`Choose one API for ${appKey}: ${ids(apis) || 'none available'}.`);
207
320
  }
208
- return found;
321
+ return api;
322
+ }
323
+ function ids(choices) {
324
+ return choices.map(({ id }) => id).join(', ');
209
325
  }
210
326
  function environmentApiKey() {
211
327
  const runtime = globalThis;
@@ -1,5 +1,14 @@
1
1
  import type { Environment } from '../models/environment.js';
2
2
  import type { Code } from '../models/error-response.js';
3
+ /** What `environments.start()` adds to the error it rethrows after a failed wait. */
4
+ export interface StartFailure {
5
+ /** The environment the start created. */
6
+ readonly environmentId?: string;
7
+ /** Whether the stop request succeeded. Disposal may still be in progress. */
8
+ readonly stopped?: boolean;
9
+ /** Why the stop request failed, when it did. */
10
+ readonly stopError?: unknown;
11
+ }
3
12
  interface Details {
4
13
  readonly message: string;
5
14
  readonly cause?: unknown;
@@ -12,11 +21,15 @@ interface Details {
12
21
  readonly idempotencyKey?: string;
13
22
  }
14
23
  /**
15
- * Any failed API call: an error response, an invalid response, or no response at
16
- * all (`status` null). The message never includes a response body.
24
+ * Any failed API call: an error response, an invalid response, no response at all,
25
+ * or input the SDK rejected before sending (`status` null for the last three). The
26
+ * message never includes a response body.
17
27
  */
18
- export declare class StateMachinesError extends Error implements Details {
28
+ export declare class StateMachinesError extends Error implements Details, StartFailure {
19
29
  readonly name = "StateMachinesError";
30
+ readonly environmentId?: string;
31
+ readonly stopped?: boolean;
32
+ readonly stopError?: unknown;
20
33
  readonly status: number | null;
21
34
  readonly code: Code | null;
22
35
  readonly requestId: string | null;
@@ -33,9 +46,12 @@ export declare class StateMachinesError extends Error implements Details {
33
46
  });
34
47
  }
35
48
  export declare function toStateMachinesError(error: unknown): StateMachinesError;
36
- export declare class EnvironmentFailedError extends Error {
49
+ export declare class EnvironmentFailedError extends Error implements StartFailure {
37
50
  readonly environment: Environment;
38
51
  readonly name = "EnvironmentFailedError";
52
+ readonly environmentId?: string;
53
+ readonly stopped?: boolean;
54
+ readonly stopError?: unknown;
39
55
  constructor(environment: Environment);
40
56
  }
41
57
  export declare class DisposalIncompleteError extends Error {
@@ -43,10 +59,13 @@ export declare class DisposalIncompleteError extends Error {
43
59
  readonly name = "DisposalIncompleteError";
44
60
  constructor(environment: Environment);
45
61
  }
46
- export declare class TimeoutError extends Error {
62
+ export declare class TimeoutError extends Error implements StartFailure {
47
63
  readonly timeoutMs: number;
48
64
  readonly environment: Environment | null;
49
65
  readonly name = "TimeoutError";
66
+ readonly environmentId?: string;
67
+ readonly stopped?: boolean;
68
+ readonly stopError?: unknown;
50
69
  constructor(timeoutMs: number, environment: Environment | null);
51
70
  }
52
71
  export {};
@@ -3,8 +3,9 @@ import { ConnectionError, RequestTimeoutError, } from '../models/errors/http-cli
3
3
  import { ResponseValidationError } from '../models/errors/response-validation-error.js';
4
4
  import { StateMachinesHTTPError } from '../models/errors/state-machines-http-error.js';
5
5
  /**
6
- * Any failed API call: an error response, an invalid response, or no response at
7
- * all (`status` null). The message never includes a response body.
6
+ * Any failed API call: an error response, an invalid response, no response at all,
7
+ * or input the SDK rejected before sending (`status` null for the last three). The
8
+ * message never includes a response body.
8
9
  */
9
10
  export class StateMachinesError extends Error {
10
11
  name = 'StateMachinesError';
@@ -4,8 +4,10 @@ export { ConnectionError, ErrorResponse, HTTPClientError, InvalidRequestError, R
4
4
  export type { ApiProfile, App, Catalog, Connection as ConnectionDetails, ConnectionSecret, CreateEnvironmentInput, Credential, Environment, EnvironmentApp, 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 { CallOptions, ConnectOptions, CreateOptions, ListEnvironmentsQuery, Page, StateMachinesOptions, } from './client.js';
7
+ export type { AppInput, EnvironmentInput } from './apps.js';
8
+ export type { CallOptions, ConnectOptions, CreateOptions, ListEnvironmentsQuery, Page, StartOptions, StateMachinesOptions, } from './client.js';
8
9
  export { StateMachines } from './client.js';
9
10
  export { Connection, formatActorHeader } from './connection.js';
10
11
  export { DisposalIncompleteError, EnvironmentFailedError, StateMachinesError, TimeoutError, } from './errors.js';
12
+ export { RunningEnvironment } from './running-environment.js';
11
13
  export type { WaitOptions } from './wait.js';
@@ -5,3 +5,4 @@ export { Status as EnvironmentListStatus } from '../models/operations/list-envir
5
5
  export { StateMachines } from './client.js';
6
6
  export { Connection, formatActorHeader } from './connection.js';
7
7
  export { DisposalIncompleteError, EnvironmentFailedError, StateMachinesError, TimeoutError, } from './errors.js';
8
+ export { RunningEnvironment } from './running-environment.js';
@@ -0,0 +1,29 @@
1
+ import type { Environment } from '../models/environment.js';
2
+ import type { ReleaseActor } from '../models/release-actor.js';
3
+ import type { CallOptions, ConnectOptions, StateMachines } from './client.js';
4
+ import type { Connection } from './connection.js';
5
+ import type { WaitOptions } from './wait.js';
6
+ /** A started environment. It remains the handle after `stop()`. */
7
+ export declare class RunningEnvironment {
8
+ #private;
9
+ readonly id: string;
10
+ /** The record when `start()` resolved. Use `sm.environments.get(id)` for fresh state. */
11
+ readonly environment: Environment;
12
+ constructor(sm: StateMachines, environment: Environment);
13
+ /**
14
+ * actorId defaults to the actor of the environment's declared default connection, or
15
+ * else the app's first actor (for Salesforce, the administrator). Pick another from
16
+ * actors() to act with that user's permissions.
17
+ */
18
+ connect(appKey: string, options?: ConnectOptions): Promise<Connection>;
19
+ /** The app's users, in the release's order. */
20
+ actors(appKey: string, options?: CallOptions): Promise<ReleaseActor[]>;
21
+ /** Safe to call twice. Doesn't wait for disposal: use `waitUntilStopped()`. */
22
+ stop(options?: CallOptions): Promise<Environment>;
23
+ /** Resolves when the environment ended and every sandbox is confirmed disposed. */
24
+ waitUntilStopped(options?: WaitOptions): Promise<Environment>;
25
+ toJSON(): {
26
+ id: string;
27
+ environment: Environment;
28
+ };
29
+ }
@@ -0,0 +1,35 @@
1
+ /** A started environment. It remains the handle after `stop()`. */
2
+ export class RunningEnvironment {
3
+ id;
4
+ /** The record when `start()` resolved. Use `sm.environments.get(id)` for fresh state. */
5
+ environment;
6
+ #environments;
7
+ constructor(sm, environment) {
8
+ this.id = environment.id;
9
+ this.environment = environment;
10
+ this.#environments = sm.environments;
11
+ }
12
+ /**
13
+ * actorId defaults to the actor of the environment's declared default connection, or
14
+ * else the app's first actor (for Salesforce, the administrator). Pick another from
15
+ * actors() to act with that user's permissions.
16
+ */
17
+ connect(appKey, options) {
18
+ return this.#environments.connect(this.id, appKey, options);
19
+ }
20
+ /** The app's users, in the release's order. */
21
+ actors(appKey, options) {
22
+ return this.#environments.actors(this.id, appKey, options);
23
+ }
24
+ /** Safe to call twice. Doesn't wait for disposal: use `waitUntilStopped()`. */
25
+ stop(options) {
26
+ return this.#environments.stop(this.id, options);
27
+ }
28
+ /** Resolves when the environment ended and every sandbox is confirmed disposed. */
29
+ waitUntilStopped(options) {
30
+ return this.#environments.waitUntilStopped(this.id, options);
31
+ }
32
+ toJSON() {
33
+ return { id: this.id, environment: this.environment };
34
+ }
35
+ }
@@ -1,4 +1,3 @@
1
- /** A signal that aborts on the parent's abort, after `timeoutMs`, or on `close()`. */
2
1
  export interface LinkedSignal {
3
2
  readonly signal: AbortSignal;
4
3
  /** Releases the timer and the parent listener without aborting. */
@@ -11,4 +10,4 @@ export interface LinkedSignal {
11
10
  * on Node 20.20 in our probes. The timer and the listener hold the controller, so
12
11
  * the abort reaches in-flight requests.
13
12
  */
14
- export declare function linkSignal(parent: AbortSignal | undefined, timeoutMs?: number): LinkedSignal;
13
+ export declare function linkSignal(parent: AbortSignal | undefined | readonly AbortSignal[], timeoutMs?: number): LinkedSignal;
@@ -4,22 +4,27 @@
4
4
  * the abort reaches in-flight requests.
5
5
  */
6
6
  export function linkSignal(parent, timeoutMs) {
7
+ const parents = parent === undefined ? [] : [parent].flat();
7
8
  const controller = new AbortController();
8
9
  const timer = timeoutMs === undefined
9
10
  ? undefined
10
11
  : setTimeout(() => controller.abort(new DOMException(`Timed out after ${timeoutMs} ms.`, 'TimeoutError')), timeoutMs);
11
- const onAbort = () => controller.abort(parent?.reason);
12
+ const onAbort = () => controller.abort(parents.find(({ aborted }) => aborted)?.reason);
12
13
  const dispose = () => {
13
14
  clearTimeout(timer);
14
- parent?.removeEventListener('abort', onAbort);
15
+ for (const each of parents) {
16
+ each.removeEventListener('abort', onAbort);
17
+ }
15
18
  };
16
19
  timer?.unref?.();
17
20
  controller.signal.addEventListener('abort', dispose, { once: true });
18
- if (parent?.aborted) {
21
+ if (parents.some(({ aborted }) => aborted)) {
19
22
  onAbort();
20
23
  }
21
24
  else {
22
- parent?.addEventListener('abort', onAbort, { once: true });
25
+ for (const each of parents) {
26
+ each.addEventListener('abort', onAbort, { once: true });
27
+ }
23
28
  }
24
29
  return {
25
30
  signal: controller.signal,
@@ -7,6 +7,7 @@ export interface WaitOptions {
7
7
  }
8
8
  type Read = (signal: AbortSignal) => Promise<Environment>;
9
9
  type Decide = (environment: Environment) => boolean;
10
+ export declare const defaultTimeoutMs = 600000;
10
11
  /**
11
12
  * Reads until `done` returns true. `done` throws to end the wait with a failure.
12
13
  * Transient read failures are retried with backoff until the deadline.
@@ -1,6 +1,6 @@
1
1
  import { DisposalIncompleteError, EnvironmentFailedError, TimeoutError, toStateMachinesError, } from './errors.js';
2
2
  import { linkSignal } from './signal.js';
3
- const defaultTimeoutMs = 600000;
3
+ export const defaultTimeoutMs = 600000;
4
4
  const readTimeoutMs = 30000;
5
5
  /**
6
6
  * Reads until `done` returns true. `done` throws to end the wait with a failure.
@@ -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 createEnvironmentDefinition(client: StateMachinesCore, request: operations.CreateEnvironmentDefinitionRequest, options?: RequestOptions): APIPromise<Result<operations.CreateEnvironmentDefinitionResponse, 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 createEnvironmentDefinition(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.CreateEnvironmentDefinitionRequest$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}/environment-definitions")(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: "createEnvironmentDefinition",
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.CreateEnvironmentDefinitionResponse$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
+ }
@@ -31,7 +31,7 @@ export declare function serverURLFromOptions(options: SDKOptions): URL | null;
31
31
  export declare const SDK_METADATA: {
32
32
  readonly language: "typescript";
33
33
  readonly openapiDocVersion: "1.0.0";
34
- readonly sdkVersion: "0.1.1";
34
+ readonly sdkVersion: "0.2.0";
35
35
  readonly genVersion: "2.941.0";
36
- readonly userAgent: "speakeasy-sdk/typescript 0.1.1 2.941.0 1.0.0 @usestatemachines/sdk";
36
+ readonly userAgent: "speakeasy-sdk/typescript 0.2.0 2.941.0 1.0.0 @usestatemachines/sdk";
37
37
  };
package/esm/lib/config.js CHANGED
@@ -24,7 +24,7 @@ export function serverURLFromOptions(options) {
24
24
  export const SDK_METADATA = {
25
25
  language: "typescript",
26
26
  openapiDocVersion: "1.0.0",
27
- sdkVersion: "0.1.1",
27
+ sdkVersion: "0.2.0",
28
28
  genVersion: "2.941.0",
29
- userAgent: "speakeasy-sdk/typescript 0.1.1 2.941.0 1.0.0 @usestatemachines/sdk",
29
+ userAgent: "speakeasy-sdk/typescript 0.2.0 2.941.0 1.0.0 @usestatemachines/sdk",
30
30
  };
@@ -1,14 +1,30 @@
1
1
  import * as z from "zod/v4-mini";
2
2
  import { Result as SafeParseResult } from "../types/fp.js";
3
3
  import { SDKValidationError } from "./errors/sdk-validation-error.js";
4
+ export type Native = {
5
+ authentication: string;
6
+ baseUrl: string;
7
+ credentials: {
8
+ [k: string]: string;
9
+ };
10
+ key: string;
11
+ parameters: {
12
+ [k: string]: string;
13
+ };
14
+ };
4
15
  export type ConnectionSecret = {
5
16
  actorId: string;
6
17
  actorToken: string;
7
18
  appKey: string;
19
+ connectionKey: string | null;
8
20
  environmentId: string;
9
21
  environmentToken: string;
10
22
  expiresAt: Date;
23
+ native?: Native | undefined;
11
24
  };
12
25
  /** @internal */
26
+ export declare const Native$inboundSchema: z.ZodMiniType<Native, unknown>;
27
+ export declare function nativeFromJSON(jsonString: string): SafeParseResult<Native, SDKValidationError>;
28
+ /** @internal */
13
29
  export declare const ConnectionSecret$inboundSchema: z.ZodMiniType<ConnectionSecret, unknown>;
14
30
  export declare function connectionSecretFromJSON(jsonString: string): SafeParseResult<ConnectionSecret, SDKValidationError>;