@usestatemachines/sdk 0.4.0 → 0.6.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 (270) hide show
  1. package/README.md +177 -470
  2. package/agents/SKILL.md +38 -47
  3. package/agents/index.js +7 -7
  4. package/agents/llms.txt +49 -45
  5. package/agents/prompt.md +11 -11
  6. package/esm/extensions/client.d.ts +105 -116
  7. package/esm/extensions/client.js +162 -309
  8. package/esm/extensions/credentials.d.ts +13 -0
  9. package/esm/extensions/credentials.js +73 -0
  10. package/esm/extensions/environment.d.ts +25 -0
  11. package/esm/extensions/environment.js +1 -0
  12. package/esm/extensions/errors.d.ts +42 -75
  13. package/esm/extensions/errors.js +125 -110
  14. package/esm/extensions/index.d.ts +6 -11
  15. package/esm/extensions/index.js +2 -7
  16. package/esm/extensions/wait.d.ts +17 -18
  17. package/esm/extensions/wait.js +60 -56
  18. package/esm/extensions/yaml.d.ts +6 -0
  19. package/esm/extensions/yaml.js +137 -0
  20. package/esm/funcs/{catalog-release.d.ts → apps-list.d.ts} +1 -1
  21. package/esm/funcs/apps-list.js +91 -0
  22. package/esm/funcs/{credential-get.d.ts → apps-versions.d.ts} +1 -1
  23. package/esm/funcs/{workspaces-events.js → apps-versions.js} +6 -7
  24. package/esm/funcs/{environments-get-connection.d.ts → audit-events-list.d.ts} +1 -1
  25. package/esm/funcs/audit-events-list.js +93 -0
  26. package/esm/funcs/{workspaces-events.d.ts → environments-connect.d.ts} +1 -1
  27. package/esm/funcs/{environments-reveal.js → environments-connect.js} +7 -6
  28. package/esm/funcs/environments-create.d.ts +2 -1
  29. package/esm/funcs/environments-create.js +5 -10
  30. package/esm/funcs/{environments-events.d.ts → environments-delete.d.ts} +1 -1
  31. package/esm/funcs/{environments-stop.js → environments-delete.js} +8 -6
  32. package/esm/funcs/environments-get.d.ts +2 -1
  33. package/esm/funcs/environments-get.js +2 -1
  34. package/esm/funcs/environments-list.d.ts +1 -1
  35. package/esm/funcs/environments-list.js +11 -16
  36. package/esm/funcs/environments-pause.d.ts +12 -0
  37. package/esm/funcs/{environments-suspend.js → environments-pause.js} +7 -5
  38. package/esm/funcs/environments-resume.d.ts +2 -1
  39. package/esm/funcs/environments-resume.js +3 -1
  40. package/esm/funcs/environments-update.d.ts +12 -0
  41. package/esm/funcs/{get-environment-position.js → environments-update.js} +10 -8
  42. package/esm/funcs/me.d.ts +11 -0
  43. package/esm/funcs/{catalog-get.js → me.js} +5 -5
  44. package/esm/funcs/{catalog-get.d.ts → requests-body.d.ts} +1 -1
  45. package/esm/funcs/{environments-get-connection.js → requests-body.js} +8 -8
  46. package/esm/funcs/requests-get.d.ts +12 -0
  47. package/esm/funcs/{catalog-release.js → requests-get.js} +7 -6
  48. package/esm/funcs/{environments-stop.d.ts → requests-list.d.ts} +1 -1
  49. package/esm/funcs/{list-environment-history.js → requests-list.js} +15 -12
  50. package/esm/funcs/requests-stats.d.ts +12 -0
  51. package/esm/funcs/{environments-events.js → requests-stats.js} +15 -14
  52. package/esm/funcs/snapshots-create.d.ts +12 -0
  53. package/esm/funcs/{snapshots-capture.js → snapshots-create.js} +6 -11
  54. package/esm/funcs/snapshots-delete.d.ts +2 -1
  55. package/esm/funcs/snapshots-delete.js +2 -1
  56. package/esm/funcs/snapshots-get.d.ts +2 -1
  57. package/esm/funcs/snapshots-get.js +2 -1
  58. package/esm/funcs/snapshots-list.d.ts +1 -1
  59. package/esm/funcs/snapshots-list.js +9 -13
  60. package/esm/funcs/snapshots-update.d.ts +2 -1
  61. package/esm/funcs/snapshots-update.js +2 -1
  62. package/esm/funcs/workspaces-get.d.ts +2 -1
  63. package/esm/funcs/workspaces-get.js +2 -1
  64. package/esm/funcs/workspaces-list.d.ts +1 -1
  65. package/esm/funcs/workspaces-list.js +3 -1
  66. package/esm/lib/config.d.ts +3 -3
  67. package/esm/lib/config.js +3 -3
  68. package/esm/models/actor.d.ts +2 -3
  69. package/esm/models/actor.js +2 -3
  70. package/esm/models/{api-profile.d.ts → api.d.ts} +4 -4
  71. package/esm/models/{api-profile.js → api.js} +4 -5
  72. package/esm/models/app.d.ts +9 -1
  73. package/esm/models/app.js +7 -1
  74. package/esm/models/{event.d.ts → audit-event.d.ts} +6 -6
  75. package/esm/models/{event.js → audit-event.js} +7 -6
  76. package/esm/models/body-preview.d.ts +31 -0
  77. package/esm/models/body-preview.js +34 -0
  78. package/esm/models/create-environment.d.ts +51 -0
  79. package/esm/models/create-environment.js +35 -0
  80. package/esm/models/credentials.d.ts +31 -0
  81. package/esm/models/credentials.js +31 -0
  82. package/esm/models/environment-failure.d.ts +22 -0
  83. package/esm/models/environment-failure.js +25 -0
  84. package/esm/models/environment-status.d.ts +14 -0
  85. package/esm/models/environment-status.js +15 -0
  86. package/esm/models/environment.d.ts +27 -27
  87. package/esm/models/environment.js +28 -27
  88. package/esm/models/error.d.ts +38 -0
  89. package/esm/models/error.js +39 -0
  90. package/esm/models/errors/{error-response.d.ts → error.d.ts} +5 -5
  91. package/esm/models/errors/{error-response.js → error.js} +4 -4
  92. package/esm/models/errors/index.d.ts +1 -1
  93. package/esm/models/errors/index.js +1 -1
  94. package/esm/models/index.d.ts +17 -22
  95. package/esm/models/index.js +17 -22
  96. package/esm/models/me.d.ts +66 -0
  97. package/esm/models/me.js +75 -0
  98. package/esm/models/operations/create-credentials.d.ts +25 -0
  99. package/esm/models/operations/create-credentials.js +20 -0
  100. package/esm/models/operations/create-environment.d.ts +14 -0
  101. package/esm/models/operations/create-environment.js +18 -0
  102. package/esm/models/operations/create-snapshot.d.ts +25 -0
  103. package/esm/models/operations/create-snapshot.js +25 -0
  104. package/esm/models/operations/delete-environment.d.ts +13 -0
  105. package/esm/models/operations/delete-environment.js +17 -0
  106. package/esm/models/operations/delete-snapshot.d.ts +0 -12
  107. package/esm/models/operations/delete-snapshot.js +0 -9
  108. package/esm/models/operations/get-environment.d.ts +0 -12
  109. package/esm/models/operations/get-environment.js +0 -9
  110. package/esm/models/operations/get-request-body.d.ts +21 -0
  111. package/esm/models/operations/get-request-body.js +18 -0
  112. package/esm/models/operations/get-request-stats.d.ts +51 -0
  113. package/esm/models/operations/get-request-stats.js +40 -0
  114. package/esm/models/operations/get-request.d.ts +11 -0
  115. package/esm/models/operations/get-request.js +11 -0
  116. package/esm/models/operations/get-snapshot.d.ts +0 -12
  117. package/esm/models/operations/get-snapshot.js +0 -9
  118. package/esm/models/operations/get-workspace.d.ts +0 -12
  119. package/esm/models/operations/get-workspace.js +0 -9
  120. package/esm/models/operations/index.d.ts +13 -15
  121. package/esm/models/operations/index.js +13 -15
  122. package/esm/models/operations/list-apps.d.ts +26 -0
  123. package/esm/models/operations/list-apps.js +23 -0
  124. package/esm/models/operations/list-audit-events.d.ts +30 -0
  125. package/esm/models/operations/list-audit-events.js +25 -0
  126. package/esm/models/operations/list-environments.d.ts +6 -21
  127. package/esm/models/operations/list-environments.js +3 -16
  128. package/esm/models/operations/list-requests.d.ts +68 -0
  129. package/esm/models/operations/list-requests.js +53 -0
  130. package/esm/models/operations/list-snapshots.d.ts +8 -6
  131. package/esm/models/operations/list-snapshots.js +4 -3
  132. package/esm/models/operations/list-versions.d.ts +28 -0
  133. package/esm/models/operations/list-versions.js +24 -0
  134. package/esm/models/operations/list-workspaces.d.ts +8 -4
  135. package/esm/models/operations/list-workspaces.js +7 -4
  136. package/esm/models/operations/pause-environment.d.ts +13 -0
  137. package/esm/models/operations/pause-environment.js +17 -0
  138. package/esm/models/operations/resume-environment.d.ts +2 -12
  139. package/esm/models/operations/resume-environment.js +8 -11
  140. package/esm/models/operations/update-environment.d.ts +29 -0
  141. package/esm/models/operations/update-environment.js +20 -0
  142. package/esm/models/operations/update-snapshot.d.ts +0 -14
  143. package/esm/models/operations/update-snapshot.js +0 -10
  144. package/esm/models/permission.d.ts +17 -0
  145. package/esm/models/permission.js +18 -0
  146. package/esm/models/principal.d.ts +20 -0
  147. package/esm/models/principal.js +24 -0
  148. package/esm/models/request-detail.d.ts +39 -0
  149. package/esm/models/request-detail.js +38 -0
  150. package/esm/models/request-stats.d.ts +26 -0
  151. package/esm/models/request-stats.js +31 -0
  152. package/esm/models/request.d.ts +30 -0
  153. package/esm/models/request.js +34 -0
  154. package/esm/models/snapshot-status.d.ts +13 -0
  155. package/esm/models/snapshot-status.js +14 -0
  156. package/esm/models/snapshot.d.ts +47 -0
  157. package/esm/models/snapshot.js +54 -0
  158. package/esm/models/version.d.ts +27 -0
  159. package/esm/models/version.js +31 -0
  160. package/esm/models/workspace.d.ts +2 -4
  161. package/esm/models/workspace.js +2 -4
  162. package/esm/sdk/apps.d.ts +6 -0
  163. package/esm/sdk/apps.js +15 -0
  164. package/esm/sdk/audit-events.d.ts +5 -0
  165. package/esm/sdk/audit-events.js +11 -0
  166. package/esm/sdk/environments.d.ts +8 -9
  167. package/esm/sdk/environments.js +19 -23
  168. package/esm/sdk/requests.d.ts +9 -0
  169. package/esm/sdk/requests.js +23 -0
  170. package/esm/sdk/sdk.d.ts +11 -11
  171. package/esm/sdk/sdk.js +16 -23
  172. package/esm/sdk/snapshots.d.ts +6 -5
  173. package/esm/sdk/snapshots.js +7 -7
  174. package/esm/sdk/workspaces.d.ts +1 -2
  175. package/esm/sdk/workspaces.js +0 -4
  176. package/esm/types/smart-union.js +1 -2
  177. package/openapi.json +1630 -2380
  178. package/package.json +15 -13
  179. package/esm/extensions/apps.d.ts +0 -18
  180. package/esm/extensions/apps.js +0 -39
  181. package/esm/extensions/connection.d.ts +0 -42
  182. package/esm/extensions/connection.js +0 -133
  183. package/esm/extensions/running-environment.d.ts +0 -46
  184. package/esm/extensions/running-environment.js +0 -66
  185. package/esm/funcs/create-environment-definition.d.ts +0 -11
  186. package/esm/funcs/create-environment-definition.js +0 -94
  187. package/esm/funcs/credential-get.js +0 -76
  188. package/esm/funcs/environments-reveal.d.ts +0 -11
  189. package/esm/funcs/environments-suspend.d.ts +0 -11
  190. package/esm/funcs/get-environment-exchange.d.ts +0 -11
  191. package/esm/funcs/get-environment-exchange.js +0 -96
  192. package/esm/funcs/get-environment-position.d.ts +0 -11
  193. package/esm/funcs/list-environment-history.d.ts +0 -11
  194. package/esm/funcs/snapshots-capture.d.ts +0 -11
  195. package/esm/models/app-instance-snapshot.d.ts +0 -15
  196. package/esm/models/app-instance-snapshot.js +0 -19
  197. package/esm/models/catalog.d.ts +0 -12
  198. package/esm/models/catalog.js +0 -15
  199. package/esm/models/connection-secret.d.ts +0 -30
  200. package/esm/models/connection-secret.js +0 -31
  201. package/esm/models/connection.d.ts +0 -40
  202. package/esm/models/connection.js +0 -44
  203. package/esm/models/create-environment-input.d.ts +0 -39
  204. package/esm/models/create-environment-input.js +0 -25
  205. package/esm/models/credential-kind.d.ts +0 -9
  206. package/esm/models/credential-kind.js +0 -10
  207. package/esm/models/credential.d.ts +0 -48
  208. package/esm/models/credential.js +0 -44
  209. package/esm/models/environment-app.d.ts +0 -53
  210. package/esm/models/environment-app.js +0 -57
  211. package/esm/models/environment-exchange.d.ts +0 -116
  212. package/esm/models/environment-exchange.js +0 -126
  213. package/esm/models/environment-lifetime.d.ts +0 -10
  214. package/esm/models/environment-lifetime.js +0 -14
  215. package/esm/models/environment-position.d.ts +0 -25
  216. package/esm/models/environment-position.js +0 -32
  217. package/esm/models/environment-snapshot.d.ts +0 -40
  218. package/esm/models/environment-snapshot.js +0 -42
  219. package/esm/models/error-response.d.ts +0 -58
  220. package/esm/models/error-response.js +0 -60
  221. package/esm/models/event-page.d.ts +0 -11
  222. package/esm/models/event-page.js +0 -16
  223. package/esm/models/history-body.d.ts +0 -28
  224. package/esm/models/history-body.js +0 -33
  225. package/esm/models/history-cause.d.ts +0 -48
  226. package/esm/models/history-cause.js +0 -63
  227. package/esm/models/history-position.d.ts +0 -11
  228. package/esm/models/history-position.js +0 -15
  229. package/esm/models/history-transaction.d.ts +0 -16
  230. package/esm/models/history-transaction.js +0 -20
  231. package/esm/models/operations/capture-snapshot.d.ts +0 -41
  232. package/esm/models/operations/capture-snapshot.js +0 -36
  233. package/esm/models/operations/create-environment-definition.d.ts +0 -225
  234. package/esm/models/operations/create-environment-definition.js +0 -149
  235. package/esm/models/operations/create-environments.d.ts +0 -27
  236. package/esm/models/operations/create-environments.js +0 -27
  237. package/esm/models/operations/get-catalog.d.ts +0 -13
  238. package/esm/models/operations/get-catalog.js +0 -13
  239. package/esm/models/operations/get-connection.d.ts +0 -25
  240. package/esm/models/operations/get-connection.js +0 -21
  241. package/esm/models/operations/get-credential.d.ts +0 -13
  242. package/esm/models/operations/get-credential.js +0 -13
  243. package/esm/models/operations/get-environment-exchange.d.ts +0 -25
  244. package/esm/models/operations/get-environment-exchange.js +0 -21
  245. package/esm/models/operations/get-environment-position.d.ts +0 -23
  246. package/esm/models/operations/get-environment-position.js +0 -20
  247. package/esm/models/operations/get-release.d.ts +0 -23
  248. package/esm/models/operations/get-release.js +0 -20
  249. package/esm/models/operations/list-environment-events.d.ts +0 -15
  250. package/esm/models/operations/list-environment-events.js +0 -13
  251. package/esm/models/operations/list-environment-history.d.ts +0 -28
  252. package/esm/models/operations/list-environment-history.js +0 -24
  253. package/esm/models/operations/list-workspace-events.d.ts +0 -15
  254. package/esm/models/operations/list-workspace-events.js +0 -13
  255. package/esm/models/operations/reveal-connection.d.ts +0 -59
  256. package/esm/models/operations/reveal-connection.js +0 -55
  257. package/esm/models/operations/stop-environment.d.ts +0 -23
  258. package/esm/models/operations/stop-environment.js +0 -20
  259. package/esm/models/operations/suspend-environment.d.ts +0 -23
  260. package/esm/models/operations/suspend-environment.js +0 -20
  261. package/esm/models/release-actor.d.ts +0 -11
  262. package/esm/models/release-actor.js +0 -15
  263. package/esm/models/release.d.ts +0 -37
  264. package/esm/models/release.js +0 -39
  265. package/esm/sdk/catalog.d.ts +0 -6
  266. package/esm/sdk/catalog.js +0 -15
  267. package/esm/sdk/credential.d.ts +0 -5
  268. package/esm/sdk/credential.js +0 -11
  269. package/esm/types/discriminated-union.d.ts +0 -24
  270. package/esm/types/discriminated-union.js +0 -60
@@ -1,317 +1,214 @@
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 { expandSource } from './apps.js';
5
- import { Connection } from './connection.js';
6
- import { CallerAbortedError, EnvironmentFailedError, SnapshotFailedError, SnapshotTimeoutError, StateMachinesError, TimeoutError, toStateMachinesError, } from './errors.js';
7
- import { RunningEnvironment } from './running-environment.js';
4
+ import { bindCredentials } from './credentials.js';
5
+ import { CallerAbortedError, StateMachinesError, toStateMachinesError, } from './errors.js';
8
6
  import { linkSignal } from './signal.js';
9
- import { backoff, defaultTimeoutMs, disposed, poll, pollUntil, ready, sleep, snapshotReady, suspended, } from './wait.js';
10
- const stopAfterFailureMs = 30000;
11
- const busyBudgetMs = 60000;
7
+ import { environmentAt, poll, snapshotAt, } from './wait.js';
12
8
  export class StateMachines {
13
9
  #sdk;
14
10
  #fetch;
15
11
  #workspaceId;
16
12
  #callSignals = new Map();
13
+ #apis = new Map();
17
14
  #lastCall = 0;
18
- #credentialWorkspace;
19
15
  constructor(options = {}) {
20
16
  const apiKey = options.apiKey ?? environmentApiKey();
21
17
  if (!apiKey) {
22
- throw new TypeError('Pass apiKey or set STATE_API_KEY.');
18
+ throw new TypeError('Pass apiKey or set STATEMACHINES_API_KEY.');
23
19
  }
24
20
  // Checked here so a malformed key is never echoed by a header validation error.
25
21
  if (!/^[\x21-\x7e]+$/.test(apiKey)) {
26
22
  throw new TypeError('apiKey must be printable ASCII with no spaces.');
27
23
  }
28
- if (options.fetch && options.httpClient) {
29
- throw new TypeError('Pass fetch or httpClient, not both.');
24
+ const attemptTimeoutMs = options.attemptTimeoutMs ?? 60000;
25
+ if (!(attemptTimeoutMs > 0 && Number.isFinite(attemptTimeoutMs))) {
26
+ throw new RangeError('attemptTimeoutMs must be a positive, finite number.');
30
27
  }
31
28
  const fetcher = options.fetch;
32
29
  // The arrow keeps `this` unbound; runtimes such as workerd reject fetch called on another object.
33
- const timeoutMs = options.timeoutMs ?? 60000;
34
- if (!(timeoutMs > 0 && Number.isFinite(timeoutMs))) {
35
- throw new RangeError('timeoutMs must be a positive, finite number.');
36
- }
37
- const httpClient = options.httpClient?.clone() ??
38
- new HTTPClient(fetcher ? { fetcher: (input) => fetcher(input) } : {});
39
- addAttemptTimeout(httpClient, timeoutMs, this.#callSignals);
30
+ const httpClient = new HTTPClient(fetcher ? { fetcher: (input) => fetcher(input) } : {});
31
+ addAttemptTimeout(httpClient, attemptTimeoutMs, this.#callSignals);
40
32
  this.#fetch = fetcher;
41
33
  this.#workspaceId = options.workspaceId;
42
34
  this.#sdk = new Generated({
43
35
  apiKey,
44
36
  userAgent: `statemachines-sdk-ts/${SDK_METADATA.sdkVersion}`,
45
37
  httpClient,
46
- ...(options.serverURL ? { serverURL: options.serverURL } : {}),
38
+ ...(options.baseUrl ? { serverURL: options.baseUrl } : {}),
47
39
  });
48
40
  }
49
- credential = {
50
- get: (options) => this.#unwrap(options, (call) => this.#sdk.credential.get(call)),
51
- };
52
- catalog = {
53
- get: (options) => this.#unwrap(options, (call) => this.#sdk.catalog.get(call)),
54
- release: (releaseId, options) => this.#unwrap(options, (call) => this.#sdk.catalog.release({ releaseId }, call)),
41
+ /** The caller: its principal, organization, workspace, permissions and limits. */
42
+ me() {
43
+ return this.#call(undefined, (call) => this.#sdk.me(call));
44
+ }
45
+ apps = {
46
+ list: (query = {}) => this.#call(undefined, (call) => this.#sdk.apps.list(query, call)),
47
+ /** Published versions, including retired ones. */
48
+ versions: (appId, query = {}) => this.#call(undefined, (call) => this.#sdk.apps.versions({ ...query, appId }, call)),
55
49
  };
56
50
  workspaces = {
57
- list: (query = {}, options) => this.#unwrap(options, (call) => this.#sdk.workspaces.list(query, call)),
58
- get: (workspaceId, options) => this.#unwrap(options, (call) => this.#sdk.workspaces.get({ workspaceId }, call)),
59
- events: (workspaceId, query = {}, options) => this.#call(options, (call) => this.#sdk.workspaces.events({ ...query, workspaceId }, call)),
51
+ list: (query = {}) => this.#call(undefined, (call) => this.#sdk.workspaces.list(query, call)),
52
+ get: (workspaceId) => this.#call(undefined, (call) => this.#sdk.workspaces.get({ workspaceId }, call)),
60
53
  };
61
54
  environments = {
62
- list: (query = {}, options) => this.#call(options, async (call) => {
63
- const { workspaceId, ...filters } = query;
64
- const resolved = await this.#workspace(workspaceId, call.signal);
65
- return this.#sdk.environments.list({ ...filters, workspaceId: resolved }, call);
66
- }),
67
- get: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.get({ environmentId }, call)),
68
- /**
69
- * Creates an environment and waits until it's running. If the wait fails, stops the
70
- * environment (unless the caller passed its own `idempotencyKey`), then rethrows the
71
- * error with `environmentId`, `stopped` and `stopError`.
72
- */
73
- start: async (input, options = {}) => {
74
- const { readyTimeoutMs, ...createOptions } = options;
75
- const { idempotencyKey, signal } = options;
76
- const timeoutMs = readyTimeoutMs ?? defaultTimeoutMs;
77
- if (!(timeoutMs > 0)) {
78
- throw new RangeError('readyTimeoutMs must be more than 0.');
79
- }
80
- const created = await this.environments.create(input, createOptions);
81
- try {
82
- const environment = await this.environments.waitUntilReady(created.id, {
83
- timeoutMs,
84
- ...(signal ? { signal } : {}),
85
- });
86
- return new RunningEnvironment(this, environment);
87
- }
88
- catch (error) {
89
- // The API replays a create by key, returning the same environment to another
90
- // start() that may still be using it, so only the SDK's own key allows a stop.
91
- const outcome = idempotencyKey === undefined
92
- ? await this.#stopAfterFailure(created.id)
93
- : { environmentId: created.id, stopped: false };
94
- const isCallerReason = signal?.aborted && Object.is(error, signal.reason);
95
- if (isCallerReason) {
96
- if (outcome.stopped) {
97
- throw error;
98
- }
99
- throw Object.assign(new StateMachinesError(new CallerAbortedError(error)), outcome);
100
- }
101
- if (isStartError(error) && Object.isExtensible(error)) {
102
- Object.assign(error, outcome);
103
- }
104
- throw error;
105
- }
106
- },
107
- /**
108
- * A string app is shorthand for `{ key: <the string>, appId: <the string> }`. Every
109
- * failure carries the `idempotencyKey`; replaying the create with it is safe.
110
- */
55
+ /** Creates an environment and, unless `wait` is false, waits until it is `running`. */
111
56
  create: async (input, options = {}) => {
57
+ const { signal } = options;
112
58
  const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
113
- try {
114
- const body = expandSource(input);
115
- return await this.#call(options, async (call) => {
116
- const workspaceId = await this.#workspace(options.workspaceId, call.signal);
117
- const { data } = await this.#sdk.environments.create({ workspaceId, idempotencyKey, body }, call);
118
- const [environment] = data;
119
- if (!environment) {
120
- throw new Error('Invalid response from the State API: the create returned no environment.');
121
- }
122
- return environment;
59
+ const created = await this.#call(signal, (call) => this.#sdk.environments.create({ idempotencyKey, body: this.#inWorkspace(input) }, call));
60
+ return options.wait === false
61
+ ? this.#bind(created)
62
+ : this.environments.wait(created.id, {
63
+ status: 'running',
64
+ ...waitOptions(options),
123
65
  });
124
- }
125
- catch (error) {
126
- // Always a new instance: concurrent creates can share one failed workspace lookup.
127
- throw new StateMachinesError(error, { idempotencyKey });
128
- }
129
66
  },
130
- /** Ends the environment and discards its data. Safe to repeat. */
131
- stop: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.stop({ environmentId }, call)),
132
- /**
133
- * Suspends a running environment: its sandboxes end and its data stays for `resume`.
134
- * Its lifetime is paused meanwhile. Use `stop` to end it.
135
- */
136
- suspend: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.suspend({ environmentId }, call)),
137
- /** Queues a suspended environment to run again on the data it was suspended with. */
138
- resume: (environmentId, options) => this.#unwrap(options, (call) => this.#sdk.environments.resume({ environmentId }, call)),
139
- events: (environmentId, query = {}, options) => this.#call(options, (call) => this.#sdk.environments.events({ ...query, environmentId }, call)),
140
- /** The app's users, in the release's order. */
141
- actors: async (environmentId, appKey, options) => (await this.environments.getConnection(environmentId, appKey, options))
142
- .actors,
143
- getConnection: (environmentId, appKey, options) => this.#unwrap(options, (call) => this.#sdk.environments.getConnection({ environmentId, appKey }, call)),
144
- reveal: (environmentId, appKey, actorId, options) => this.#reveal(environmentId, appKey, { actorId }, options),
145
- /** Resolves when the environment is running. Never suspends or stops it. */
146
- waitUntilReady: (environmentId, options) => poll((signal) => this.#poll(environmentId, signal), ready, options),
147
- /** Resolves when the environment is suspended and can resume. */
148
- waitUntilSuspended: (environmentId, options) => poll((signal) => this.#poll(environmentId, signal), suspended, options),
149
- /** Resolves when the environment ended and every sandbox is confirmed disposed. */
150
- waitUntilStopped: (environmentId, options) => poll((signal) => this.#poll(environmentId, signal), disposed, options),
67
+ /** Newest first. Leaves deleted environments out unless `status` asks for them. */
68
+ list: async (query = {}) => {
69
+ const page = await this.#call(undefined, (call) => this.#sdk.environments.list(this.#inWorkspace(query), call));
70
+ return { ...page, data: page.data.map((each) => this.#bind(each)) };
71
+ },
72
+ get: async (environmentId) => this.#bind(await this.#call(undefined, (call) => this.#sdk.environments.get({ environmentId }, call))),
73
+ /** `labels` replaces the whole map. */
74
+ update: async (environmentId, body) => this.#bind(await this.#call(undefined, (call) => this.#sdk.environments.update({ environmentId, body }, call))),
75
+ /** Takes effect when the API answers: the gateway refuses calls from then on. */
76
+ pause: async (environmentId, options = {}) => this.#bind(await this.#call(options.signal, (call) => this.#sdk.environments.pause({ environmentId }, call))),
77
+ /** Resumes a paused or failed environment and, unless `wait` is false, waits until it is `running`. */
78
+ resume: async (environmentId, options = {}) => {
79
+ const resumed = await this.#call(options.signal, (call) => this.#sdk.environments.resume({ environmentId }, call));
80
+ return options.wait === false
81
+ ? this.#bind(resumed)
82
+ : this.environments.wait(environmentId, {
83
+ status: 'running',
84
+ ...waitOptions(options),
85
+ });
86
+ },
87
+ /** Deletes the environment and its data. Safe to repeat. */
88
+ delete: async (environmentId, options = {}) => this.#bind(await this.#call(options.signal, (call) => this.#sdk.environments.delete({ environmentId }, call))),
151
89
  /**
152
- * actorId defaults to the actor of the environment's declared default connection, or
153
- * else the app's first actor (for Salesforce, the administrator). Pick another from
154
- * actors() to act with that user's permissions.
90
+ * Resolves when the environment has `status`. Throws once it cannot get there without
91
+ * another call: `EnvironmentFailedError` if it failed, else `UnexpectedStatusError`.
155
92
  */
156
- connect: async (environmentId, appKey, options = {}) => {
157
- const details = await this.environments.getConnection(environmentId, appKey, options);
158
- if (details.status !== 'ready' || !details.baseUrl) {
159
- throw new TypeError(`App ${appKey} is not ready to connect (${details.status}).`);
160
- }
161
- // The API resolves { actorId } to that actor's first connection, not the declared one.
162
- const body = options.actorId === undefined && details.defaultConnection
163
- ? { connectionKey: details.defaultConnection }
164
- : { actorId: pickActor(appKey, details.actors, options.actorId).id };
165
- const api = pickApi(appKey, details.apis, options.apiId);
166
- const secret = await this.#reveal(environmentId, appKey, body, options);
167
- return new Connection(details.baseUrl, api, secret, this.#fetch);
168
- },
93
+ wait: async (environmentId, options) => this.#bind(await poll(environmentId, (signal) => this.#read(signal, (call) => this.#sdk.environments.get({ environmentId }, call)), environmentAt(options.status), options)),
94
+ /** Credentials for one app of the environment. */
95
+ connect: (environmentId, app, options = {}) => this.#connect(environmentId, null, app, options),
169
96
  };
170
97
  snapshots = {
171
- /** Returns the API's answer at once: it may still be `capturing`. */
172
- create: (environmentId, input = {}, options = {}) => this.#createSnapshot(environmentId, input, options, 0),
173
- /**
174
- * Creates a snapshot and waits until it's `ready`. Needs `snapshots:create` and
175
- * `snapshots:read`. A failed or timed-out snapshot is never deleted: the error
176
- * carries `snapshotId` and `idempotencyKey`.
177
- */
178
- capture: async (environmentId, input = {}, options = {}) => {
179
- const { readyTimeoutMs, ...createOptions } = options;
180
- const { signal } = options;
98
+ /** Creates a snapshot of a running environment and, unless `wait` is false, waits until it is `ready`. */
99
+ create: async (input, options = {}) => {
181
100
  const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
182
- const timeoutMs = readyTimeoutMs ?? defaultTimeoutMs;
183
- if (!(timeoutMs > 0)) {
184
- throw new RangeError('readyTimeoutMs must be more than 0.');
185
- }
186
- const created = await this.#createSnapshot(environmentId, input, { ...createOptions, idempotencyKey }, busyBudgetMs);
187
- try {
188
- signal?.throwIfAborted();
189
- if (snapshotReady(created)) {
190
- return created;
191
- }
192
- return await this.snapshots.waitUntilReady(created.id, {
193
- timeoutMs,
194
- ...(signal ? { signal } : {}),
101
+ const created = await this.#call(options.signal, (call) => this.#sdk.snapshots.create({ idempotencyKey, body: input }, call));
102
+ return options.wait === false
103
+ ? created
104
+ : this.snapshots.wait(created.id, {
105
+ status: 'ready',
106
+ ...waitOptions(options),
195
107
  });
196
- }
197
- catch (error) {
198
- const snapshotId = created.id;
199
- if (signal?.aborted && Object.is(error, signal.reason)) {
200
- throw Object.assign(new StateMachinesError(new CallerAbortedError(error), {
201
- idempotencyKey,
202
- }), { snapshotId });
203
- }
204
- if (error instanceof StateMachinesError ||
205
- error instanceof SnapshotFailedError ||
206
- error instanceof SnapshotTimeoutError) {
207
- Object.assign(error, { snapshotId, idempotencyKey });
208
- }
209
- throw error;
210
- }
211
108
  },
212
- /** Resolves when the snapshot is `ready`. Never deletes it. */
213
- 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)),
214
- /** Deleted snapshots too, with `deletedAt` set. */
215
- get: (snapshotId, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.get({ snapshotId }, call)),
216
- /** Newest first. Deleted snapshots are left out. */
217
- list: (query = {}, options) => this.#call(options, async (call) => {
218
- const { workspaceId, ...filters } = query;
219
- const resolved = await this.#workspace(workspaceId, call.signal);
220
- return this.#sdk.snapshots.list({ ...filters, workspaceId: resolved }, call);
221
- }),
109
+ /** Newest first. Leaves deleted snapshots out unless `status` asks for them. */
110
+ list: (query = {}) => this.#call(undefined, (call) => this.#sdk.snapshots.list(this.#inWorkspace(query), call)),
111
+ get: (snapshotId) => this.#call(undefined, (call) => this.#sdk.snapshots.get({ snapshotId }, call)),
112
+ /** `null` clears the name. */
113
+ update: (snapshotId, body) => this.#call(undefined, (call) => this.#sdk.snapshots.update({ snapshotId, body }, call)),
114
+ /** A `saving` snapshot cannot be deleted. Safe to repeat. */
115
+ delete: (snapshotId) => this.#call(undefined, (call) => this.#sdk.snapshots.delete({ snapshotId }, call)),
222
116
  /**
223
- * `null` clears the name. A stale `expectedVersion`, or a deleted snapshot, is a 409
224
- * `version-conflict`; so is a retry of a rename whose first answer was lost.
117
+ * Resolves when the snapshot has `status`. Throws once it cannot get there without
118
+ * another call: `SnapshotFailedError` if it failed, else `UnexpectedStatusError`.
225
119
  */
226
- rename: (snapshotId, body, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.update({ snapshotId, body }, call)),
227
- /** Marks the snapshot deleted and returns it; safe to repeat. A capturing snapshot is a 409 `not-ready`. */
228
- delete: (snapshotId, options) => this.#unwrap(options, (call) => this.#sdk.snapshots.delete({ snapshotId }, call)),
120
+ wait: (snapshotId, options) => poll(snapshotId, (signal) => this.#read(signal, (call) => this.#sdk.snapshots.get({ snapshotId }, call)), snapshotAt(options.status), options),
229
121
  };
230
- async #createSnapshot(environmentId, input, options, busyMs) {
231
- const idempotencyKey = options.idempotencyKey ?? crypto.randomUUID();
232
- try {
233
- return await this.#call(options, async (call) => {
234
- const workspaceId = await this.#workspace(options.workspaceId, call.signal);
235
- const request = {
236
- workspaceId,
237
- idempotencyKey,
238
- body: { ...input, environmentId },
239
- };
240
- const deadline = Date.now() + busyMs;
241
- for (let attempt = 0;; attempt += 1) {
242
- try {
243
- return (await this.#sdk.snapshots.capture(request, call)).data;
244
- }
245
- catch (error) {
246
- const busy = toStateMachinesError(error);
247
- const waitMs = Math.max(busy.retryAfter ?? 0, backoff(attempt));
248
- if (busy.code !== 'snapshot-limit' ||
249
- Date.now() + waitMs >= deadline) {
250
- throw error;
251
- }
252
- await sleep(waitMs, call.signal);
253
- }
254
- }
255
- });
256
- }
257
- catch (error) {
258
- throw new StateMachinesError(error, { idempotencyKey });
259
- }
260
- }
261
- #reveal(environmentId, appKey, body, options) {
262
- return this.#unwrap(options, (call) => this.#sdk.environments.reveal({ environmentId, appKey, body }, call));
263
- }
264
- async #stopAfterFailure(environmentId) {
265
- const budget = linkSignal(undefined, stopAfterFailureMs);
266
- try {
267
- // abortable() ends the wait at the budget even during a retry delay.
268
- await abortable(this.environments.stop(environmentId, { signal: budget.signal }), budget.signal);
269
- return { environmentId, stopped: true };
270
- }
271
- catch (stopError) {
272
- return { environmentId, stopped: false, stopError };
273
- }
274
- finally {
275
- budget.dispose();
276
- }
122
+ requests = {
123
+ /** Calls the gateway recorded for one environment, newest first. Recording is best effort. */
124
+ list: (query) => this.#call(undefined, (call) => this.#sdk.requests.list(query, call)),
125
+ get: (requestId) => this.#call(undefined, (call) => this.#sdk.requests.get({ requestId }, call)),
126
+ stats: (query) => this.#call(undefined, (call) => this.#sdk.requests.stats(query, call)),
127
+ /** The recorded body, in full. */
128
+ body: (requestId, direction) =>
129
+ // Read inside the call: settling it aborts the signal the body streams under.
130
+ this.#call(undefined, async (call) => {
131
+ const stream = await this.#sdk.requests.body({ requestId, direction }, call);
132
+ return new Uint8Array(await new Response(stream).arrayBuffer());
133
+ }),
134
+ };
135
+ auditEvents = {
136
+ /** Newest first. An API key that names no filter gets its own workspace. */
137
+ list: (query = {}) => this.#call(undefined, (call) => this.#sdk.auditEvents.list(query.environmentId ? query : this.#inWorkspace(query), call)),
138
+ };
139
+ #inWorkspace(input) {
140
+ return input.workspaceId === undefined && this.#workspaceId !== undefined
141
+ ? { ...input, workspaceId: this.#workspaceId }
142
+ : input;
277
143
  }
278
- /** One read for the poller, which owns retries, timing and the caller's signal. */
279
- #poll(environmentId, signal) {
280
- return this.#unwrap({ signal }, (call) => this.#sdk.environments.get({ environmentId }, { ...call, retries: { strategy: 'none' } }));
144
+ async #connect(environmentId, known, app, options) {
145
+ const body = options.actorId === undefined ? {} : { actorId: options.actorId };
146
+ const [credentials, apis] = await Promise.all([
147
+ this.#call(undefined, (call) => this.#sdk.environments.connect({ environmentId, appName: app, body }, call)),
148
+ (async () => {
149
+ const environment = known ?? (await this.environments.get(environmentId));
150
+ const entry = environment.apps.find(({ name }) => name === app);
151
+ return entry ? this.#versionApis(entry.appId, entry.versionId) : [];
152
+ })(),
153
+ ]);
154
+ return bindCredentials(credentials, apis, this.#fetch);
281
155
  }
282
- /** Resolves the workspace once per client; a failed lookup is retried by the next call. */
283
- #workspace(explicit, signal) {
284
- const workspaceId = explicit ?? this.#workspaceId;
285
- if (workspaceId) {
286
- return Promise.resolve(workspaceId);
156
+ /** A version's APIs never change, so each is read once per client. */
157
+ #versionApis(appId, versionId) {
158
+ let apis = this.#apis.get(versionId);
159
+ if (!apis) {
160
+ apis = this.#findVersion(appId, versionId);
161
+ apis.catch(() => this.#apis.delete(versionId));
162
+ this.#apis.set(versionId, apis);
287
163
  }
288
- this.#credentialWorkspace ?? (this.#credentialWorkspace = this.#lookupWorkspace());
289
- // The lookup is shared, so one caller's abort only stops that caller waiting.
290
- return abortable(this.#credentialWorkspace, signal);
164
+ return apis;
291
165
  }
292
- #lookupWorkspace() {
293
- const lookup = this.credential.get().then((credential) => {
294
- if (!credential.workspaceId) {
295
- throw new TypeError('This credential has no workspace. Pass workspaceId, or use a workspace API key.');
166
+ async #findVersion(appId, versionId) {
167
+ let cursor;
168
+ do {
169
+ const page = await this.apps.versions(appId, cursor ? { cursor } : {});
170
+ const version = page.data.find(({ id }) => id === versionId);
171
+ if (version) {
172
+ return version.apis;
296
173
  }
297
- return credential.workspaceId;
174
+ cursor = page.nextCursor ?? undefined;
175
+ } while (cursor);
176
+ throw new StateMachinesError({
177
+ message: `Version ${versionId} of ${appId} was not found.`,
178
+ status: null,
179
+ code: 'not_found',
180
+ requestId: null,
181
+ fields: null,
182
+ retryAfterMs: null,
298
183
  });
299
- lookup.catch(() => {
300
- if (this.#credentialWorkspace === lookup) {
301
- this.#credentialWorkspace = undefined;
302
- }
303
- });
304
- return lookup;
305
184
  }
306
- #unwrap(options, run) {
307
- return this.#call(options, async (call) => (await run(call)).data);
185
+ #bind(data) {
186
+ const { id } = data;
187
+ const helpers = {
188
+ connect: (app, options = {}) => this.#connect(id, data, app, options),
189
+ pause: (options) => this.environments.pause(id, options),
190
+ resume: (options) => this.environments.resume(id, options),
191
+ delete: (options) => this.environments.delete(id, options),
192
+ refresh: () => this.environments.get(id),
193
+ snapshots: {
194
+ create: (input = {}, options) => this.snapshots.create({ ...input, environmentId: id }, options),
195
+ },
196
+ };
197
+ return Object.defineProperties({ ...data }, Object.fromEntries(Object.entries(helpers).map(([name, value]) => [
198
+ name,
199
+ { value, enumerable: false },
200
+ ])));
201
+ }
202
+ /** One read for the poller, which owns retries, timing and the caller's signal. */
203
+ #read(signal, run) {
204
+ return this.#call(signal, (call) => run({ ...call, retries: { strategy: 'none' } }));
308
205
  }
309
206
  /**
310
207
  * Runs one API call under its own signal. Closing it when the call settles
311
208
  * releases every attempt's timeout timer and listener.
312
209
  */
313
- async #call(options, run) {
314
- const scope = linkSignal(options?.signal);
210
+ async #call(signal, run) {
211
+ const scope = linkSignal(signal);
315
212
  const id = String(++this.#lastCall);
316
213
  this.#callSignals.set(id, scope.signal);
317
214
  try {
@@ -322,9 +219,7 @@ export class StateMachines {
322
219
  }
323
220
  catch (error) {
324
221
  // A caller abort can surface as a raw TimeoutError while a body is read; it is never retryable.
325
- throw toStateMachinesError(options?.signal?.aborted
326
- ? new CallerAbortedError(options.signal.reason)
327
- : error);
222
+ throw toStateMachinesError(signal?.aborted ? new CallerAbortedError(signal.reason) : error);
328
223
  }
329
224
  finally {
330
225
  this.#callSignals.delete(id);
@@ -347,14 +242,13 @@ function addAttemptTimeout(httpClient, timeoutMs, calls) {
347
242
  httpClient.addHook('beforeRequest', (request) => {
348
243
  const headers = new Headers(request.headers);
349
244
  const call = calls.get(headers.get(CALL_HEADER) ?? '');
350
- // The request's own signal still counts: a caller's hook may have set it.
351
245
  const sources = call ? [call, request.signal] : [request.signal];
352
246
  const aborted = sources.find(({ aborted }) => aborted);
353
247
  headers.delete(CALL_HEADER);
354
248
  if (aborted) {
355
249
  throw new CallerAbortedError(aborted.reason);
356
250
  }
357
- const attempt = new Request(request, {
251
+ const attempt = new globalThis.Request(request, {
358
252
  headers,
359
253
  signal: linkSignal(sources, timeoutMs).signal,
360
254
  });
@@ -369,56 +263,15 @@ function addAttemptTimeout(httpClient, timeoutMs, calls) {
369
263
  }
370
264
  });
371
265
  }
372
- function isStartError(error) {
373
- return (error instanceof StateMachinesError ||
374
- error instanceof EnvironmentFailedError ||
375
- error instanceof TimeoutError);
376
- }
377
- function abortable(promise, signal) {
378
- if (!signal) {
379
- return promise;
380
- }
381
- return new Promise((resolve, reject) => {
382
- const onAbort = () => reject(signal.reason);
383
- if (signal.aborted) {
384
- onAbort();
385
- return;
386
- }
387
- signal.addEventListener('abort', onAbort, { once: true });
388
- promise
389
- .then(resolve, reject)
390
- .finally(() => signal.removeEventListener('abort', onAbort));
391
- });
392
- }
393
- function pickActor(appKey, actors, actorId) {
394
- const [first] = actors;
395
- if (!first) {
396
- throw new TypeError(`App ${appKey} has no actors.`);
397
- }
398
- if (actorId === undefined) {
399
- return first;
400
- }
401
- const actor = actors.find(({ id }) => id === actorId);
402
- if (!actor) {
403
- throw new TypeError(`Unknown actor ${actorId} for ${appKey}. Choose one of: ${ids(actors)}.`);
404
- }
405
- return actor;
406
- }
407
- function pickApi(appKey, apis, apiId) {
408
- const api = apiId
409
- ? apis.find(({ id }) => id === apiId)
410
- : apis.length === 1
411
- ? apis[0]
412
- : undefined;
413
- if (!api) {
414
- throw new TypeError(`Choose one API for ${appKey}: ${ids(apis) || 'none available'}.`);
415
- }
416
- return api;
417
- }
418
- function ids(choices) {
419
- return choices.map(({ id }) => id).join(', ');
266
+ function waitOptions(options) {
267
+ return {
268
+ ...(options.timeoutMs === undefined
269
+ ? {}
270
+ : { timeoutMs: options.timeoutMs }),
271
+ ...(options.signal ? { signal: options.signal } : {}),
272
+ };
420
273
  }
421
274
  function environmentApiKey() {
422
275
  const runtime = globalThis;
423
- return runtime.process?.env?.STATE_API_KEY;
276
+ return runtime.process?.env?.STATEMACHINES_API_KEY;
424
277
  }
@@ -0,0 +1,13 @@
1
+ import type { Api } from '../models/api.js';
2
+ import type { Credentials as CredentialsData } from '../models/credentials.js';
3
+ /** The API's `Credentials`, plus `fetch` to call the app with them. */
4
+ export type Credentials = CredentialsData & {
5
+ /**
6
+ * Sends one request to the app: `path` is joined to `url`, `headers` are added, and
7
+ * `token` goes in the header of the API whose path matches (`Authorization: Bearer`
8
+ * for Salesforce REST and Bulk 2.0). Never retries, never follows redirects, never
9
+ * sends the State Machines API key. The SOAP session header is the caller's job.
10
+ */
11
+ fetch(path: string, init?: RequestInit): Promise<Response>;
12
+ };
13
+ export declare function bindCredentials(data: CredentialsData, apis: readonly Api[], fetcher: typeof fetch | undefined): Credentials;