@usestatemachines/sdk 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -521
- package/agents/SKILL.md +38 -47
- package/agents/index.js +7 -7
- package/agents/llms.txt +50 -46
- package/agents/prompt.md +11 -11
- package/esm/extensions/client.d.ts +104 -127
- package/esm/extensions/client.js +162 -322
- package/esm/extensions/credentials.d.ts +13 -0
- package/esm/extensions/credentials.js +73 -0
- package/esm/extensions/environment.d.ts +25 -0
- package/esm/extensions/environment.js +1 -0
- package/esm/extensions/errors.d.ts +42 -75
- package/esm/extensions/errors.js +125 -110
- package/esm/extensions/index.d.ts +6 -11
- package/esm/extensions/index.js +2 -7
- package/esm/extensions/wait.d.ts +17 -18
- package/esm/extensions/wait.js +60 -56
- package/esm/extensions/yaml.d.ts +6 -0
- package/esm/extensions/yaml.js +137 -0
- package/esm/funcs/{catalog-release.d.ts → apps-list.d.ts} +1 -1
- package/esm/funcs/{environments-get-connection.js → apps-list.js} +11 -16
- package/esm/funcs/{credential-get.d.ts → apps-versions.d.ts} +1 -1
- package/esm/funcs/{workspaces-events.js → apps-versions.js} +6 -7
- package/esm/funcs/{environments-get-connection.d.ts → audit-events-list.d.ts} +1 -1
- package/esm/funcs/audit-events-list.js +93 -0
- package/esm/funcs/{workspaces-events.d.ts → environments-connect.d.ts} +1 -1
- package/esm/funcs/{environments-reveal.js → environments-connect.js} +7 -6
- package/esm/funcs/environments-create.d.ts +2 -1
- package/esm/funcs/environments-create.js +5 -10
- package/esm/funcs/{environments-events.d.ts → environments-delete.d.ts} +1 -1
- package/esm/funcs/{environments-stop.js → environments-delete.js} +7 -6
- package/esm/funcs/environments-get.d.ts +2 -1
- package/esm/funcs/environments-get.js +2 -1
- package/esm/funcs/environments-list.d.ts +1 -1
- package/esm/funcs/environments-list.js +11 -16
- package/esm/funcs/environments-pause.d.ts +12 -0
- package/esm/funcs/{environments-suspend.js → environments-pause.js} +6 -5
- package/esm/funcs/environments-resume.d.ts +2 -1
- package/esm/funcs/environments-resume.js +2 -1
- package/esm/funcs/environments-update.d.ts +12 -0
- package/esm/funcs/{environments-events.js → environments-update.js} +9 -13
- package/esm/funcs/me.d.ts +11 -0
- package/esm/funcs/{catalog-get.js → me.js} +5 -5
- package/esm/funcs/{catalog-get.d.ts → requests-body.d.ts} +1 -1
- package/esm/funcs/{environments-get-request-payload.js → requests-body.js} +5 -9
- package/esm/funcs/{environments-get-request-stats.d.ts → requests-get.d.ts} +2 -1
- package/esm/funcs/{catalog-release.js → requests-get.js} +7 -6
- package/esm/funcs/{environments-stop.d.ts → requests-list.d.ts} +1 -1
- package/esm/funcs/{environments-list-requests.js → requests-list.js} +9 -14
- package/esm/funcs/requests-stats.d.ts +12 -0
- package/esm/funcs/{environments-get-request-stats.js → requests-stats.js} +10 -14
- package/esm/funcs/snapshots-create.d.ts +12 -0
- package/esm/funcs/{snapshots-capture.js → snapshots-create.js} +6 -11
- package/esm/funcs/snapshots-delete.d.ts +2 -1
- package/esm/funcs/snapshots-delete.js +2 -1
- package/esm/funcs/snapshots-get.d.ts +2 -1
- package/esm/funcs/snapshots-get.js +2 -1
- package/esm/funcs/snapshots-list.d.ts +1 -1
- package/esm/funcs/snapshots-list.js +9 -13
- package/esm/funcs/snapshots-update.d.ts +2 -1
- package/esm/funcs/snapshots-update.js +2 -1
- package/esm/funcs/workspaces-get.d.ts +2 -1
- package/esm/funcs/workspaces-get.js +2 -1
- package/esm/funcs/workspaces-list.d.ts +1 -1
- package/esm/funcs/workspaces-list.js +3 -1
- package/esm/lib/config.d.ts +3 -3
- package/esm/lib/config.js +3 -3
- package/esm/models/actor.d.ts +2 -3
- package/esm/models/actor.js +2 -3
- package/esm/models/{api-profile.d.ts → api.d.ts} +4 -4
- package/esm/models/{api-profile.js → api.js} +4 -5
- package/esm/models/app.d.ts +9 -1
- package/esm/models/app.js +7 -1
- package/esm/models/{event.d.ts → audit-event.d.ts} +6 -6
- package/esm/models/{event.js → audit-event.js} +7 -6
- package/esm/models/{request-payload-preview.d.ts → body-preview.d.ts} +4 -4
- package/esm/models/{request-payload-preview.js → body-preview.js} +5 -4
- package/esm/models/create-environment.d.ts +51 -0
- package/esm/models/create-environment.js +35 -0
- package/esm/models/credentials.d.ts +31 -0
- package/esm/models/credentials.js +31 -0
- package/esm/models/environment-failure.d.ts +22 -0
- package/esm/models/environment-failure.js +25 -0
- package/esm/models/environment-status.d.ts +14 -0
- package/esm/models/environment-status.js +15 -0
- package/esm/models/environment.d.ts +27 -27
- package/esm/models/environment.js +28 -27
- package/esm/models/error.d.ts +38 -0
- package/esm/models/error.js +39 -0
- package/esm/models/errors/{error-response.d.ts → error.d.ts} +5 -5
- package/esm/models/errors/{error-response.js → error.js} +4 -4
- package/esm/models/errors/index.d.ts +1 -1
- package/esm/models/errors/index.js +1 -1
- package/esm/models/index.d.ts +17 -20
- package/esm/models/index.js +17 -20
- package/esm/models/me.d.ts +66 -0
- package/esm/models/me.js +75 -0
- package/esm/models/operations/create-credentials.d.ts +25 -0
- package/esm/models/operations/create-credentials.js +20 -0
- package/esm/models/operations/create-environment.d.ts +14 -0
- package/esm/models/operations/create-environment.js +18 -0
- package/esm/models/operations/create-snapshot.d.ts +25 -0
- package/esm/models/operations/create-snapshot.js +25 -0
- package/esm/models/operations/delete-environment.d.ts +13 -0
- package/esm/models/operations/delete-environment.js +17 -0
- package/esm/models/operations/delete-snapshot.d.ts +0 -12
- package/esm/models/operations/delete-snapshot.js +0 -9
- package/esm/models/operations/get-environment.d.ts +0 -12
- package/esm/models/operations/get-environment.js +0 -9
- package/esm/models/operations/get-request-body.d.ts +21 -0
- package/esm/models/operations/{get-environment-request-payload.js → get-request-body.js} +3 -4
- package/esm/models/operations/get-request-stats.d.ts +51 -0
- package/esm/models/operations/get-request-stats.js +40 -0
- package/esm/models/operations/get-request.d.ts +11 -0
- package/esm/models/operations/get-request.js +11 -0
- package/esm/models/operations/get-snapshot.d.ts +0 -12
- package/esm/models/operations/get-snapshot.js +0 -9
- package/esm/models/operations/get-workspace.d.ts +0 -12
- package/esm/models/operations/get-workspace.js +0 -9
- package/esm/models/operations/index.d.ts +13 -16
- package/esm/models/operations/index.js +13 -16
- package/esm/models/operations/list-apps.d.ts +26 -0
- package/esm/models/operations/list-apps.js +23 -0
- package/esm/models/operations/list-audit-events.d.ts +30 -0
- package/esm/models/operations/list-audit-events.js +25 -0
- package/esm/models/operations/list-environments.d.ts +6 -21
- package/esm/models/operations/list-environments.js +3 -16
- package/esm/models/operations/list-requests.d.ts +68 -0
- package/esm/models/operations/list-requests.js +53 -0
- package/esm/models/operations/list-snapshots.d.ts +8 -6
- package/esm/models/operations/list-snapshots.js +4 -3
- package/esm/models/operations/list-versions.d.ts +28 -0
- package/esm/models/operations/list-versions.js +24 -0
- package/esm/models/operations/list-workspaces.d.ts +8 -4
- package/esm/models/operations/list-workspaces.js +7 -4
- package/esm/models/operations/pause-environment.d.ts +13 -0
- package/esm/models/operations/pause-environment.js +17 -0
- package/esm/models/operations/resume-environment.d.ts +0 -12
- package/esm/models/operations/resume-environment.js +0 -9
- package/esm/models/operations/update-environment.d.ts +29 -0
- package/esm/models/operations/update-environment.js +20 -0
- package/esm/models/operations/update-snapshot.d.ts +0 -14
- package/esm/models/operations/update-snapshot.js +0 -10
- package/esm/models/permission.d.ts +17 -0
- package/esm/models/permission.js +18 -0
- package/esm/models/principal.d.ts +20 -0
- package/esm/models/principal.js +24 -0
- package/esm/models/request-detail.d.ts +39 -0
- package/esm/models/request-detail.js +38 -0
- package/esm/models/{environment-request-stats.d.ts → request-stats.d.ts} +3 -3
- package/esm/models/{environment-request-stats.js → request-stats.js} +3 -3
- package/esm/models/request.d.ts +30 -0
- package/esm/models/{environment-request.js → request.js} +11 -11
- package/esm/models/snapshot-status.d.ts +13 -0
- package/esm/models/snapshot-status.js +14 -0
- package/esm/models/snapshot.d.ts +47 -0
- package/esm/models/snapshot.js +54 -0
- package/esm/models/version.d.ts +27 -0
- package/esm/models/version.js +31 -0
- package/esm/models/workspace.d.ts +2 -4
- package/esm/models/workspace.js +2 -4
- package/esm/sdk/apps.d.ts +6 -0
- package/esm/sdk/apps.js +15 -0
- package/esm/sdk/audit-events.d.ts +5 -0
- package/esm/sdk/audit-events.js +11 -0
- package/esm/sdk/environments.d.ts +8 -13
- package/esm/sdk/environments.js +18 -38
- package/esm/sdk/requests.d.ts +9 -0
- package/esm/sdk/requests.js +23 -0
- package/esm/sdk/sdk.d.ts +11 -8
- package/esm/sdk/sdk.js +16 -11
- package/esm/sdk/snapshots.d.ts +6 -5
- package/esm/sdk/snapshots.js +7 -7
- package/esm/sdk/workspaces.d.ts +1 -2
- package/esm/sdk/workspaces.js +0 -4
- package/openapi.json +1697 -2507
- package/package.json +15 -13
- package/esm/extensions/apps.d.ts +0 -18
- package/esm/extensions/apps.js +0 -39
- package/esm/extensions/connection.d.ts +0 -42
- package/esm/extensions/connection.js +0 -133
- package/esm/extensions/running-environment.d.ts +0 -46
- package/esm/extensions/running-environment.js +0 -66
- package/esm/funcs/create-environment-definition.d.ts +0 -11
- package/esm/funcs/create-environment-definition.js +0 -94
- package/esm/funcs/credential-get.js +0 -76
- package/esm/funcs/environments-get-request-payload.d.ts +0 -11
- package/esm/funcs/environments-get-request.d.ts +0 -11
- package/esm/funcs/environments-get-request.js +0 -96
- package/esm/funcs/environments-list-requests.d.ts +0 -11
- package/esm/funcs/environments-reveal.d.ts +0 -11
- package/esm/funcs/environments-suspend.d.ts +0 -11
- package/esm/funcs/snapshots-capture.d.ts +0 -11
- package/esm/models/app-instance-snapshot.d.ts +0 -15
- package/esm/models/app-instance-snapshot.js +0 -19
- package/esm/models/catalog.d.ts +0 -12
- package/esm/models/catalog.js +0 -15
- package/esm/models/connection-secret.d.ts +0 -30
- package/esm/models/connection-secret.js +0 -31
- package/esm/models/connection.d.ts +0 -40
- package/esm/models/connection.js +0 -44
- package/esm/models/create-environment-input.d.ts +0 -39
- package/esm/models/create-environment-input.js +0 -25
- package/esm/models/credential-kind.d.ts +0 -9
- package/esm/models/credential-kind.js +0 -10
- package/esm/models/credential.d.ts +0 -48
- package/esm/models/credential.js +0 -44
- package/esm/models/environment-app.d.ts +0 -45
- package/esm/models/environment-app.js +0 -47
- package/esm/models/environment-lifetime.d.ts +0 -10
- package/esm/models/environment-lifetime.js +0 -14
- package/esm/models/environment-request-detail.d.ts +0 -40
- package/esm/models/environment-request-detail.js +0 -39
- package/esm/models/environment-request.d.ts +0 -31
- package/esm/models/environment-snapshot.d.ts +0 -40
- package/esm/models/environment-snapshot.js +0 -42
- package/esm/models/error-response.d.ts +0 -57
- package/esm/models/error-response.js +0 -59
- package/esm/models/event-page.d.ts +0 -11
- package/esm/models/event-page.js +0 -16
- package/esm/models/operations/capture-snapshot.d.ts +0 -41
- package/esm/models/operations/capture-snapshot.js +0 -36
- package/esm/models/operations/create-environment-definition.d.ts +0 -153
- package/esm/models/operations/create-environment-definition.js +0 -93
- package/esm/models/operations/create-environments.d.ts +0 -27
- package/esm/models/operations/create-environments.js +0 -27
- package/esm/models/operations/get-catalog.d.ts +0 -13
- package/esm/models/operations/get-catalog.js +0 -13
- package/esm/models/operations/get-connection.d.ts +0 -25
- package/esm/models/operations/get-connection.js +0 -21
- package/esm/models/operations/get-credential.d.ts +0 -13
- package/esm/models/operations/get-credential.js +0 -13
- package/esm/models/operations/get-environment-request-payload.d.ts +0 -23
- package/esm/models/operations/get-environment-request-stats.d.ts +0 -70
- package/esm/models/operations/get-environment-request-stats.js +0 -55
- package/esm/models/operations/get-environment-request.d.ts +0 -25
- package/esm/models/operations/get-environment-request.js +0 -21
- package/esm/models/operations/get-release.d.ts +0 -23
- package/esm/models/operations/get-release.js +0 -20
- package/esm/models/operations/list-environment-events.d.ts +0 -15
- package/esm/models/operations/list-environment-events.js +0 -13
- package/esm/models/operations/list-environment-requests.d.ts +0 -75
- package/esm/models/operations/list-environment-requests.js +0 -59
- package/esm/models/operations/list-workspace-events.d.ts +0 -15
- package/esm/models/operations/list-workspace-events.js +0 -13
- package/esm/models/operations/reveal-connection.d.ts +0 -59
- package/esm/models/operations/reveal-connection.js +0 -55
- package/esm/models/operations/stop-environment.d.ts +0 -25
- package/esm/models/operations/stop-environment.js +0 -26
- package/esm/models/operations/suspend-environment.d.ts +0 -25
- package/esm/models/operations/suspend-environment.js +0 -26
- package/esm/models/release-actor.d.ts +0 -11
- package/esm/models/release-actor.js +0 -15
- package/esm/models/release.d.ts +0 -37
- package/esm/models/release.js +0 -39
- package/esm/sdk/catalog.d.ts +0 -6
- package/esm/sdk/catalog.js +0 -15
- package/esm/sdk/credential.d.ts +0 -5
- package/esm/sdk/credential.js +0 -11
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @usestatemachines/sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for [State](https://usestatemachines.com):
|
|
4
|
-
apps, connect to them,
|
|
3
|
+
TypeScript SDK for [State Machines](https://usestatemachines.com): create disposable environments
|
|
4
|
+
of replica apps, connect to them, take snapshots of their data, pause and resume them, and delete
|
|
5
|
+
them.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
- Retries transient failures safely, including `create`, which is idempotent.
|
|
7
|
+
Documentation: [docs.usestatemachines.com](https://docs.usestatemachines.com)
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -17,587 +17,243 @@ npm install @usestatemachines/sdk
|
|
|
17
17
|
```ts
|
|
18
18
|
import { StateMachines } from '@usestatemachines/sdk';
|
|
19
19
|
|
|
20
|
-
const sm = new StateMachines(); // reads
|
|
21
|
-
const env = await sm.environments.
|
|
20
|
+
const sm = new StateMachines(); // reads STATEMACHINES_API_KEY
|
|
21
|
+
const env = await sm.environments.create({ apps: ['salesforce'] });
|
|
22
22
|
|
|
23
23
|
try {
|
|
24
|
-
const salesforce = await env.connect('salesforce'
|
|
24
|
+
const salesforce = await env.connect('salesforce');
|
|
25
25
|
const res = await salesforce.fetch('/services/data/v67.0/limits');
|
|
26
26
|
console.log(res.status, await res.json());
|
|
27
27
|
} finally {
|
|
28
|
-
await env.
|
|
28
|
+
await env.delete();
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`
|
|
33
|
-
|
|
34
|
-
minutes by default). A string app is shorthand for `{ key: s, appId: s }`; nothing else is inferred.
|
|
35
|
-
Salesforce offers several APIs, so pass `apiId`.
|
|
32
|
+
`create` waits until the environment is `running`. If an app fails to start, the server deletes
|
|
33
|
+
the environment. [Waiting](#waiting) lists what each method throws.
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
was created, `start()` stops the environment, then rethrows the original error. The SDK's errors
|
|
39
|
-
(`EnvironmentFailedError`, `TimeoutError` or a `StateMachinesError`; check with `instanceof`)
|
|
40
|
-
carry `environmentId` and `stopped`. `stopped` is `true` when the stop request succeeded. When
|
|
41
|
-
it's `false`, the stop wasn't confirmed: `stopError` says why, and you should call
|
|
42
|
-
`sm.environments.stop(err.environmentId)`, which is safe to repeat. The exception is your own key,
|
|
43
|
-
below.
|
|
44
|
-
|
|
45
|
-
When you abort the `signal` during the wait, `start()` still stops the environment. If the stop
|
|
46
|
-
succeeded, it rejects with your `signal.reason`, unchanged. If not, it rejects with a
|
|
47
|
-
`StateMachinesError` whose `cause` leads to your reason, carrying `environmentId`, `stopped` and
|
|
48
|
-
`stopError`, so you can still clean up.
|
|
49
|
-
|
|
50
|
-
If you pass your own `idempotencyKey`, `start()` doesn't stop the environment on failure: another
|
|
51
|
-
`start()` with the same key gets the same environment and may be using it. The error carries
|
|
52
|
-
`environmentId` with `stopped: false` and no `stopError`. Stop it only once no other `start()` with
|
|
53
|
-
that key needs it.
|
|
54
|
-
|
|
55
|
-
If the create step fails, including an abort, `start()` stops nothing and rejects with a
|
|
56
|
-
`StateMachinesError` that carries `idempotencyKey`. The API may still have created the
|
|
57
|
-
environment, so replay with that key (see
|
|
58
|
-
[Recovering a failed create](#recovering-a-failed-create)) and stop what it returns.
|
|
59
|
-
|
|
60
|
-
## Choosing an actor
|
|
35
|
+
## The client
|
|
61
36
|
|
|
62
37
|
```ts
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!rep) {
|
|
71
|
-
throw new Error('No sales representative in this release.');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Act with a sales rep's permissions to test what they can and can't do.
|
|
75
|
-
const salesforce = await env.connect('salesforce', { actorId: rep.id, apiId: 'rest' });
|
|
76
|
-
const res = await salesforce.fetch('/services/data/v67.0/sobjects/Account');
|
|
77
|
-
console.log(res.status);
|
|
78
|
-
} finally {
|
|
79
|
-
await env.stop();
|
|
80
|
-
}
|
|
38
|
+
const sm = new StateMachines({
|
|
39
|
+
apiKey: process.env.STATEMACHINES_API_KEY, // the default where `process` exists
|
|
40
|
+
baseUrl: 'https://api.usestatemachines.com', // the default
|
|
41
|
+
workspaceId: 'ws_...', // for member tokens; an API key has its own workspace
|
|
42
|
+
fetch, // defaults to the global fetch
|
|
43
|
+
attemptTimeoutMs: 60_000, // per HTTP attempt; a retry gets a fresh one
|
|
44
|
+
});
|
|
81
45
|
```
|
|
82
46
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
47
|
+
| Namespace | Methods |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `sm` | `me()` |
|
|
50
|
+
| `sm.apps` | `list(query?)`, `versions(appId, query?)` |
|
|
51
|
+
| `sm.workspaces` | `list(query?)`, `get(id)` |
|
|
52
|
+
| `sm.environments` | `create(input, options?)`, `list(query?)`, `get(id)`, `update(id, { name?, labels? })`, `pause(id)`, `resume(id, options?)`, `delete(id)`, `wait(id, { status })`, `connect(id, app, { actorId? }?)` |
|
|
53
|
+
| `sm.snapshots` | `create({ environmentId, name? }, options?)`, `list(query?)`, `get(id)`, `update(id, { name })`, `delete(id)`, `wait(id, { status })` |
|
|
54
|
+
| `sm.requests` | `list(query)`, `get(id)`, `stats(query)`, `body(id, 'request' \| 'response')` |
|
|
55
|
+
| `sm.auditEvents` | `list(query?)` |
|
|
87
56
|
|
|
88
|
-
|
|
57
|
+
Every environment the SDK returns carries bound helpers: `env.connect(app)`, `env.pause()`,
|
|
58
|
+
`env.resume()`, `env.delete()`, `env.refresh()` and `env.snapshots.create({ name? })`. They are
|
|
59
|
+
not enumerable, so `JSON.stringify(env)` holds only the API's fields.
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
data:
|
|
61
|
+
## Apps
|
|
92
62
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
await env.waitUntilSuspended();
|
|
96
|
-
// Later, from the same handle or with sm.environments.resume(env.id):
|
|
97
|
-
await env.resume();
|
|
98
|
-
await env.waitUntilReady();
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
- `env.suspend()` is `sm.environments.suspend(id)`. The environment reads `suspending`, then
|
|
102
|
-
`suspended`, its apps read `suspended` and `suspendedAt` is set. Only a running environment
|
|
103
|
-
whose apps are all ready can be suspended; anything else answers `409 not-ready`.
|
|
104
|
-
- While suspended, the lifetime is paused: `expiresAt` moves out by the suspended time when it
|
|
105
|
-
resumes, and the environment doesn't count toward the live-environment cap.
|
|
106
|
-
- `resume()` starts it again; it answers `409 not-suspended` unless the environment is
|
|
107
|
-
`suspended` or `failed`, and `429 environment-limit` when the cap is reached. `waitUntilSuspended()` resolves
|
|
108
|
-
on `suspended` and throws `EnvironmentFailedError` if the environment ends instead.
|
|
109
|
-
- Suspending isn't cleanup: a suspended environment keeps its data until you stop it.
|
|
110
|
-
`stop()` interrupts ongoing work, including suspension and snapshot capture.
|
|
111
|
-
|
|
112
|
-
Lifecycle commands require orchestration availability. `stop`, `suspend` and `resume` each
|
|
113
|
-
send a fresh idempotency key and reuse it across automatic retries. You can pass your own
|
|
114
|
-
`{ idempotencyKey }`. Failures carry that key so you can retry the same command after a timeout;
|
|
115
|
-
a timeout does not establish whether the command was accepted.
|
|
116
|
-
|
|
117
|
-
## Full control
|
|
118
|
-
|
|
119
|
-
Use `create`, `waitUntilReady` and `connect` directly when you want to run each step yourself, or
|
|
120
|
-
keep an environment up after a failed boot so you can inspect it. You don't need this path for
|
|
121
|
-
app keys, labels or `lifetimeMinutes`: `start()` takes the same input.
|
|
122
|
-
**Nothing is stopped automatically on this path**: stop the environment yourself, even when a step
|
|
123
|
-
throws.
|
|
63
|
+
A string names the app, and its name in the environment is the same string. Use the object form
|
|
64
|
+
for your own name, for two copies of one app, or for settings:
|
|
124
65
|
|
|
125
66
|
```ts
|
|
126
|
-
|
|
127
|
-
name: '
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
67
|
+
await sm.environments.create({
|
|
68
|
+
name: 'Checkout tests',
|
|
69
|
+
labels: { team: 'crm' },
|
|
70
|
+
timeLimitMinutes: 30,
|
|
71
|
+
apps: [
|
|
72
|
+
'salesforce',
|
|
73
|
+
{
|
|
74
|
+
app: 'salesforce',
|
|
75
|
+
name: 'partner-org',
|
|
76
|
+
settings: { settings: { soapApiLoginEnabled: false } },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
132
79
|
});
|
|
80
|
+
```
|
|
133
81
|
|
|
134
|
-
|
|
135
|
-
|
|
82
|
+
`settings` is passed to the app unchanged. Salesforce takes
|
|
83
|
+
`{ preset?: 'development', configure?, settings?: { soapApiLoginEnabled?: boolean } }`, where
|
|
84
|
+
`configure` can set the organization's `Name` and the administrator's `Username`. A refused value
|
|
85
|
+
fails the start with `failure.code: 'invalid_settings'`, and its `failure.message` names the
|
|
86
|
+
setting.
|
|
136
87
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
console.log(res.status);
|
|
140
|
-
} finally {
|
|
141
|
-
await sm.environments.stop(environment.id);
|
|
142
|
-
// In CI: fail the run when cleanup can't be confirmed.
|
|
143
|
-
await sm.environments.waitUntilStopped(environment.id);
|
|
144
|
-
}
|
|
145
|
-
```
|
|
88
|
+
`sm.apps.list()` lists the apps. `sm.apps.versions(appId)` lists each version's APIs and actors.
|
|
89
|
+
A new environment runs each app's default version.
|
|
146
90
|
|
|
147
|
-
|
|
148
|
-
2. `waitUntilReady(id)` polls until it is `running`. It throws `EnvironmentFailedError` if the
|
|
149
|
-
environment becomes `failed`, `suspending`, `suspended`, `stopping` or `stopped`, and
|
|
150
|
-
`TimeoutError` after `timeoutMs` (default 10 minutes). It **never stops the environment**,
|
|
151
|
-
even when it throws.
|
|
152
|
-
3. `connect(id, appKey, { apiId, actorId })` returns a `Connection` to one app (see
|
|
153
|
-
[Connections](#connections)).
|
|
154
|
-
4. `stop(id)` returns as soon as the API accepts it, and a second call also resolves.
|
|
155
|
-
`waitUntilStopped(id)` resolves only when the environment is `stopped` **and**
|
|
156
|
-
every app's `disposal` is `complete`. If any disposal is `failed` or `unknown`, it throws
|
|
157
|
-
`DisposalIncompleteError` with the environment.
|
|
158
|
-
|
|
159
|
-
`lifetimeMinutes` counts from when the environment starts, so boot time uses part of it; time spent
|
|
160
|
-
suspended doesn't count. When it runs out, the environment stops with `stopReason: 'expired'`. The
|
|
161
|
-
default and maximum are in `(await sm.credential.get()).limits.environmentLifetime`.
|
|
162
|
-
|
|
163
|
-
Both waits accept `{ timeoutMs, signal }`. Aborting the `signal` rejects the wait with
|
|
164
|
-
`signal.reason`. They poll at growing intervals of about 1 to 5 seconds, with random jitter, and
|
|
165
|
-
keep polling through transient failures.
|
|
166
|
-
|
|
167
|
-
`start()` and `create()` take the same input, including `{ key, appId }` objects for a custom key
|
|
168
|
-
or two instances of one app.
|
|
91
|
+
## Credentials
|
|
169
92
|
|
|
170
|
-
|
|
93
|
+
`env.connect(app, { actorId? })` returns the API's `Credentials`: `url`, `headers`, `token`,
|
|
94
|
+
`expiresAt` and `login`, plus `fetch(path, init?)`.
|
|
171
95
|
|
|
172
|
-
|
|
173
|
-
|
|
96
|
+
- `fetch` joins `path` to `url`; a leading `/` means the root of the app. It adds `headers`
|
|
97
|
+
(`StateMachines-Environment-Token`), and puts `token` in the header of the API whose path
|
|
98
|
+
matches: `Authorization: Bearer` for Salesforce REST and Bulk 2.0.
|
|
99
|
+
- Salesforce SOAP takes the token in the envelope: put `credentials.token` in
|
|
100
|
+
`SessionHeader/sessionId` yourself.
|
|
101
|
+
- `fetch` never sends your API key, never follows redirects, never retries and only reaches paths
|
|
102
|
+
under `url`.
|
|
103
|
+
- `actorId` defaults to the app's default actor (for Salesforce, the administrator). Pick another
|
|
104
|
+
from the version's `actors` to act with that user's permissions.
|
|
105
|
+
- `login` holds what the vendor's own login call needs, when the version declares one for the
|
|
106
|
+
actor.
|
|
107
|
+
- `token` and `headers` are secrets. To use another client, such as jsforce, send `headers` and
|
|
108
|
+
the actor header only to `url`, with redirects off.
|
|
174
109
|
|
|
175
|
-
|
|
176
|
-
|
|
110
|
+
Environments created with `tokenInUrl: true` carry the environment token in `url` instead, and
|
|
111
|
+
`headers` is empty.
|
|
177
112
|
|
|
178
|
-
|
|
179
|
-
const salesforce = await env.connect('salesforce', { apiId: 'rest' });
|
|
180
|
-
await salesforce.fetch('/services/data/v67.0/sobjects/Account', {
|
|
181
|
-
method: 'POST',
|
|
182
|
-
headers: { 'Content-Type': 'application/json' },
|
|
183
|
-
body: JSON.stringify({ Name: 'Acme' }),
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Waits until the snapshot is ready.
|
|
187
|
-
const snapshot = await env.snapshot({ name: 'seeded accounts' });
|
|
188
|
-
console.log(snapshot.id, snapshot.sizeBytes);
|
|
189
|
-
} finally {
|
|
190
|
-
await env.stop();
|
|
191
|
-
}
|
|
192
|
-
```
|
|
113
|
+
## Pausing and resuming
|
|
193
114
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
115
|
+
`env.pause()` takes effect when the API answers: the gateway refuses calls, the compute is
|
|
116
|
+
released and the data stays. The time limit stops counting. `env.resume()` boots the apps again
|
|
117
|
+
on the same data and waits until `running`. A paused or failed environment is deleted after one
|
|
118
|
+
day.
|
|
198
119
|
|
|
199
|
-
|
|
200
|
-
captured; `snapshotAppInstanceKeys` restores only some of them. The new environment's
|
|
201
|
-
`sourceEnvironmentSnapshotId` and each app's `sourceAppInstanceSnapshotId` name the source.
|
|
202
|
-
|
|
203
|
-
- Every app is paused while the snapshot is cut, so a multi-app snapshot records one instant
|
|
204
|
-
across apps (`consistency: 'environment'`). A single app, or a capture whose pause was refused,
|
|
205
|
-
is `'app_instance'`. It can only be taken while the environment is `running` and each app is
|
|
206
|
-
`ready`.
|
|
207
|
-
- `readyTimeoutMs` bounds the wait for `ready` (10 minutes by default), as in `start()`. While the
|
|
208
|
-
API is busy (429 `snapshot-limit`), the capture is retried with the same idempotency key for up
|
|
209
|
-
to 60 seconds.
|
|
210
|
-
- The SDK never deletes a snapshot for you, and every capture error thrown after the snapshot was
|
|
211
|
-
created carries `snapshotId`. A wait past `readyTimeoutMs` throws `SnapshotTimeoutError`, and an
|
|
212
|
-
abort after the create leaves the capture running on the server: `sm.snapshots.waitUntilReady(err.snapshotId)`
|
|
213
|
-
waits again. A failed capture throws `SnapshotFailedError` and stays failed: capture again.
|
|
214
|
-
- Capturing needs `snapshots:create` and `snapshots:read`, restoring needs `snapshots:read`,
|
|
215
|
-
renaming needs `snapshots:create`, and deleting needs `snapshots:delete`. Keys created by members don't hold `snapshots:delete`, so
|
|
216
|
-
`delete` returns 403 for them.
|
|
217
|
-
|
|
218
|
-
`sm.snapshots` has the individual steps:
|
|
219
|
-
|
|
220
|
-
- `create(environmentId, input)` returns the API's answer at once, possibly still `capturing`, and
|
|
221
|
-
doesn't retry `snapshot-limit`. It needs only `snapshots:create`. `waitUntilReady(id)` waits for
|
|
222
|
-
`ready`. `capture(environmentId, input, options)` does both; `env.snapshot()` calls it.
|
|
223
|
-
- `list({ sourceEnvironmentId, cursor, limit })` returns one page, newest first, without deleted
|
|
224
|
-
snapshots. `get(id)` also returns deleted ones, with `deletedAt` set.
|
|
225
|
-
- `rename(id, { name, expectedVersion })` needs the snapshot's current `version`. A stale one, or a
|
|
226
|
-
deleted snapshot, is 409 `version-conflict`, and so is a retried rename whose first answer was
|
|
227
|
-
lost: `get(id)` shows the current name and version. `name: null` clears the name.
|
|
228
|
-
- `delete(id)` marks the snapshot deleted and returns it. A snapshot that is still `capturing`
|
|
229
|
-
can't be deleted (409 `not-ready`).
|
|
230
|
-
|
|
231
|
-
## Authentication
|
|
232
|
-
|
|
233
|
-
`new StateMachines()` reads the API key from `STATE_API_KEY`, but only where a global `process`
|
|
234
|
-
exists. Without a key, the constructor throws a `TypeError`.
|
|
235
|
-
|
|
236
|
-
In Cloudflare Workers, pass `apiKey` explicitly:
|
|
120
|
+
## Snapshots
|
|
237
121
|
|
|
238
122
|
```ts
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
export default {
|
|
242
|
-
async fetch(_request: Request, env: { STATE_API_KEY: string }) {
|
|
243
|
-
const sm = new StateMachines({ apiKey: env.STATE_API_KEY });
|
|
244
|
-
// Bound the call: retries could otherwise hold the request for minutes.
|
|
245
|
-
const { data } = await sm.environments.list(
|
|
246
|
-
{ status: 'live' },
|
|
247
|
-
{ signal: AbortSignal.timeout(20_000) },
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
return Response.json(data.map(({ id, status }) => ({ id, status })));
|
|
251
|
-
},
|
|
252
|
-
};
|
|
123
|
+
const seeded = await env.snapshots.create({ name: 'seeded' });
|
|
124
|
+
const copy = await sm.environments.create({ snapshotId: seeded.id });
|
|
253
125
|
```
|
|
254
126
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
2. `workspaceId` passed to the constructor
|
|
260
|
-
3. the workspace of the API key, read once from `sm.credential.get()` and cached
|
|
261
|
-
|
|
262
|
-
A user session that has no workspace can't supply one, so those calls throw and ask for
|
|
263
|
-
`workspaceId`.
|
|
127
|
+
A snapshot holds every app of a running environment at one moment, and waits until `ready`. A
|
|
128
|
+
pause or delete during the snapshot fails it with `failure.code: 'interrupted'`. One snapshot is
|
|
129
|
+
`saving` per environment at a time. `sm.snapshots.delete(id)` deletes a `ready` or `failed`
|
|
130
|
+
snapshot.
|
|
264
131
|
|
|
265
|
-
|
|
266
|
-
(copied, never changed; not combined with `fetch`) and `timeoutMs` (see below).
|
|
132
|
+
## Waiting
|
|
267
133
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
Every API call retries on its own, with growing, jittered delays of up to about 5 seconds between
|
|
271
|
-
attempts:
|
|
272
|
-
|
|
273
|
-
| Response | Retried | Why |
|
|
134
|
+
| Method | Waits for | Throws |
|
|
274
135
|
| --- | --- | --- |
|
|
275
|
-
| `
|
|
276
|
-
|
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
279
|
-
| Any other status: other `4xx`, and other `5xx` | No | Not retried by the SDK. |
|
|
280
|
-
|
|
281
|
-
- A `Retry-After` header is honoured, up to 5 seconds.
|
|
282
|
-
- Retries have a 10-minute budget, checked after each failed attempt. It is not a deadline: the
|
|
283
|
-
last delay and attempt can end later. Pass a `signal` to bound a call: an abort during a
|
|
284
|
-
retry delay takes effect when the delay ends, at most about 5 seconds later.
|
|
285
|
-
- `timeoutMs` (constructor option, default 60 seconds) limits **each attempt**. A timed-out
|
|
286
|
-
attempt is retried with a fresh timeout.
|
|
287
|
-
- A `signal` passed to a call aborts the whole call, including retries. The call rejects with a
|
|
288
|
-
`StateMachinesError` whose `status` is `null`. The waits, and `start()` while it waits, reject
|
|
289
|
-
with `signal.reason` instead (see [Full control](#full-control)).
|
|
290
|
-
|
|
291
|
-
```ts
|
|
292
|
-
const environment = await sm.environments.get('env_123', {
|
|
293
|
-
signal: AbortSignal.timeout(30_000),
|
|
294
|
-
});
|
|
295
|
-
```
|
|
136
|
+
| `environments.create` | `running` | `EnvironmentFailedError` when the environment fails; `UnexpectedStatusError` on `paused` or `deleted` for another reason; `WaitTimeoutError` |
|
|
137
|
+
| `environments.resume` | `running` | `EnvironmentFailedError` when the environment fails; `UnexpectedStatusError` on `paused` or `deleted` for another reason; `WaitTimeoutError` |
|
|
138
|
+
| `snapshots.create` | `ready` | `SnapshotFailedError` on `failed`; `UnexpectedStatusError` on `deleted` |
|
|
139
|
+
| `environments.wait(id, { status })`, `snapshots.wait(id, { status })` | `status` | the same rule: a status that cannot reach `status` without another call |
|
|
296
140
|
|
|
297
|
-
|
|
141
|
+
Pass `{ wait: false }` to `create`, `resume` or `snapshots.create` to get the API's answer at once.
|
|
142
|
+
`timeoutMs` bounds the wait, and `signal` aborts it. The option types document each default.
|
|
143
|
+
`pause` and `delete` do not wait: the status changes when the API answers.
|
|
298
144
|
|
|
299
|
-
|
|
300
|
-
with that key and the **same input** returns the environment the first call created, instead of
|
|
301
|
-
creating a duplicate. If the first call never reached the API, the replay creates it. Replaying
|
|
302
|
-
with a different input fails with `idempotency-conflict`. Keys are kept for a day.
|
|
145
|
+
## YAML
|
|
303
146
|
|
|
304
147
|
```ts
|
|
305
|
-
import {
|
|
306
|
-
|
|
307
|
-
const sm = new StateMachines();
|
|
308
|
-
const input = { name: 'nightly', apps: [{ key: 'crm', appId: 'salesforce' }] };
|
|
309
|
-
|
|
310
|
-
async function createOnce() {
|
|
311
|
-
try {
|
|
312
|
-
return await sm.environments.create(input);
|
|
313
|
-
} catch (error) {
|
|
314
|
-
// Safe after any failure: the replay can't create a second environment.
|
|
315
|
-
if (error instanceof StateMachinesError && error.idempotencyKey) {
|
|
316
|
-
return sm.environments.create(input, { idempotencyKey: error.idempotencyKey });
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
throw error;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
148
|
+
import { parseEnvironmentYaml } from '@usestatemachines/sdk';
|
|
322
149
|
|
|
323
|
-
const
|
|
150
|
+
const input = parseEnvironmentYaml(await readFile('environment.yaml', 'utf8'));
|
|
151
|
+
const env = await sm.environments.create(input);
|
|
324
152
|
```
|
|
325
153
|
|
|
326
|
-
|
|
327
|
-
`
|
|
328
|
-
|
|
329
|
-
returns that same environment, which `start()` stopped (or, with your own key, left running for
|
|
330
|
-
you to stop). And don't replay an error whose `cause`
|
|
331
|
-
is a `TypeError`: the input was invalid and nothing was sent.
|
|
154
|
+
The document has the fields of `create`. A bad document throws `StateMachinesError` with
|
|
155
|
+
`code: 'invalid_request'`, `status: null` and `fields` keyed by dotted path. Anchors, aliases,
|
|
156
|
+
tags and duplicate keys are refused.
|
|
332
157
|
|
|
333
158
|
## Errors
|
|
334
159
|
|
|
335
|
-
|
|
160
|
+
A failed call throws `StateMachinesError` with `code`, `status`, `message`, `requestId`, `fields`
|
|
161
|
+
and `retryAfterMs`. `status` is `null` when no response arrived. The message never holds a
|
|
162
|
+
response body.
|
|
336
163
|
|
|
337
|
-
|
|
|
164
|
+
| Class | Thrown when | Carries |
|
|
338
165
|
| --- | --- | --- |
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
341
|
-
| `
|
|
342
|
-
| `
|
|
343
|
-
| `
|
|
344
|
-
| `retryable` | `boolean` | `true` for a `502`, `503` or `504`, a connection error or an attempt timeout. `false` for caller aborts, request validation errors and responses that aren't valid JSON. |
|
|
345
|
-
| `idempotencyKey` | `string \| undefined` | Set on `environments.create` failures, including the create step of `start()`, and on `snapshots.create` and `snapshots.capture` failures. |
|
|
346
|
-
|
|
347
|
-
`status` is `null` when no HTTP response was received, when a response wasn't valid JSON, and when
|
|
348
|
-
the SDK rejected the input before sending (its `cause` is then a `TypeError`).
|
|
349
|
-
Error messages never include response bodies. The waits throw their own classes:
|
|
350
|
-
`EnvironmentFailedError` and `DisposalIncompleteError` (both with `environment`) and
|
|
351
|
-
`TimeoutError` (with `timeoutMs` and the last `environment` seen, or `null`). Check errors with
|
|
352
|
-
`instanceof`, not `name`: a platform abort is also named `TimeoutError`.
|
|
353
|
-
|
|
354
|
-
An SDK error that `start()` rethrows after a failed wait also carries `environmentId`, `stopped`
|
|
355
|
-
and, when the stop failed, `stopError`. The stop gets 30 seconds and doesn't wait for disposal.
|
|
356
|
-
Invalid `apps` fail before any request, as a `StateMachinesError` with `status: null`, the
|
|
357
|
-
`idempotencyKey`, and one of these messages: `Pass at least one app.`,
|
|
358
|
-
`App key "<key>" is used twice.` or `"<s>" can't be used as an app key; pass { key, appId } instead.`
|
|
359
|
-
Starting from a snapshot fails the same way with `Pass apps or snapshotId.`,
|
|
360
|
-
`Pass apps or snapshotId, not both.` or `Pass snapshotAppInstanceKeys with snapshotId.`
|
|
361
|
-
|
|
362
|
-
Snapshot waits throw `SnapshotFailedError`, with the `snapshot` (its `errorCode` is
|
|
363
|
-
`capture-failed`, or `capture-interrupted` when the API restarted mid-capture), and
|
|
364
|
-
`SnapshotTimeoutError`, with `timeoutMs` and the last `snapshot` seen, or `null`. Both carry
|
|
365
|
-
`snapshotId`. From `capture`, they and any `StateMachinesError` thrown after the snapshot was
|
|
366
|
-
created also carry `snapshotId` and `idempotencyKey`.
|
|
166
|
+
| `StateMachinesError` | any failed call | `code`, `status`, `requestId`, `fields`, `retryAfterMs` |
|
|
167
|
+
| `EnvironmentFailedError` (a `StateMachinesError`) | a waited environment failed | `environment` |
|
|
168
|
+
| `SnapshotFailedError` (a `StateMachinesError`) | a waited snapshot failed | `snapshot` |
|
|
169
|
+
| `UnexpectedStatusError` | a waited resource cannot reach the status without another call | `resource`, `status` |
|
|
170
|
+
| `WaitTimeoutError` | a wait passed `timeoutMs` | `resource` (the last read), `timeoutMs` |
|
|
367
171
|
|
|
368
172
|
| Code | Status | Meaning |
|
|
369
173
|
| --- | --- | --- |
|
|
370
|
-
| `
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
374
|
-
| `
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
## Pagination
|
|
391
|
-
|
|
392
|
-
`environments.list`, `environments.events`, `workspaces.events` and `snapshots.list` return one
|
|
393
|
-
page, `{ data, nextCursor }`. Pass `nextCursor` back as `cursor` until it is `null`:
|
|
394
|
-
|
|
395
|
-
```ts
|
|
396
|
-
let cursor: string | undefined;
|
|
397
|
-
|
|
398
|
-
do {
|
|
399
|
-
const page = await sm.environments.list({ status: 'live', cursor });
|
|
400
|
-
|
|
401
|
-
for (const environment of page.data) {
|
|
402
|
-
console.log(environment.id, environment.status);
|
|
403
|
-
}
|
|
174
|
+
| `invalid_request` | 400 | A wrong field; see `fields`. |
|
|
175
|
+
| `unauthenticated` | 401 | A missing, expired or revoked key. |
|
|
176
|
+
| `forbidden` | 403 | The key lacks the permission. |
|
|
177
|
+
| `not_found` | 404 | No such resource in this workspace. |
|
|
178
|
+
| `invalid_status` | 409 | The status does not allow the call. |
|
|
179
|
+
| `snapshot_in_progress` | 409 | The environment has a snapshot in `saving`. |
|
|
180
|
+
| `idempotency_key_reused` | 409 | The same `idempotencyKey` with a different input. |
|
|
181
|
+
| `workspace_archived` | 409 | A write in an archived workspace. |
|
|
182
|
+
| `body_too_large` | 413 | The request body is over the API's size limit, which the message names. |
|
|
183
|
+
| `version_unavailable` | 422 | The app has no default version. |
|
|
184
|
+
| `environment_limit_reached` | 429 | Too many environments `starting` or `running`. |
|
|
185
|
+
| `internal_error` | 500 | An error on the server. |
|
|
186
|
+
| `unavailable` | 503 | Retried automatically. |
|
|
187
|
+
|
|
188
|
+
### Errors from the app URL
|
|
189
|
+
|
|
190
|
+
`Credentials.fetch` returns the gateway's and the app's answers as responses, with the same JSON
|
|
191
|
+
error envelope, and never retries them. These codes come from State Machines, in front of the app:
|
|
404
192
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
193
|
+
| Code | Status | Meaning |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| `environment_not_running` | 409 | The environment is `paused`, `failed` or `deleted`. The body's `error.status` names the status. |
|
|
196
|
+
| `environment_starting` | 503 | The environment is still `starting`. The response carries `Retry-After`. |
|
|
197
|
+
| `environment_unavailable` | 503 | The app is restarting or did not answer. The response carries `Retry-After`. |
|
|
198
|
+
| `response_too_large` | 502 | The app's response is over the size limit of State Machines. |
|
|
199
|
+
| `capacity_exceeded` | 429 | The app has too many requests in flight. |
|
|
200
|
+
| `unsupported_protocol` | 501 | The app does not serve this API version or protocol. |
|
|
408
201
|
|
|
409
|
-
##
|
|
410
|
-
|
|
411
|
-
`connect()` needs the app to be ready, which `start()` guarantees. It takes `{ apiId, actorId }`
|
|
412
|
-
and throws a `TypeError` before revealing any token when a choice is missing or wrong:
|
|
413
|
-
|
|
414
|
-
- `apiId` is required when the app has several API profiles, as Salesforce does:
|
|
415
|
-
`Choose one API for <appKey>: <ids>.` There is no default API.
|
|
416
|
-
- `actorId` defaults to the actor of the environment's declared default connection, or else the
|
|
417
|
-
app's first actor (for Salesforce, the administrator). One that isn't among the app's actors
|
|
418
|
-
throws `Unknown actor <id> for <appKey>. Choose one of: <ids>.`
|
|
419
|
-
|
|
420
|
-
It returns a `Connection` with `baseUrl`, `environmentId`, `appKey`, `actorId`, `api` and
|
|
421
|
-
`expiresAt`, and these ways to call the app:
|
|
422
|
-
|
|
423
|
-
- `headers()` returns `X-State-Environment-Token` plus the app's actor header, such as
|
|
424
|
-
`Authorization: Bearer <token>`. For an API that takes the actor token in the request body,
|
|
425
|
-
such as SOAP, it returns only `X-State-Environment-Token`.
|
|
426
|
-
- `fetch(path, init)` sends one request with those headers and returns the raw `Response`. A
|
|
427
|
-
leading `/` means the root of `baseUrl`. It:
|
|
428
|
-
- only reaches URLs under `baseUrl`, and throws for any other path
|
|
429
|
-
- never follows redirects (`redirect: 'manual'`)
|
|
430
|
-
- never sends your API key
|
|
431
|
-
- never retries
|
|
432
|
-
- rejects `x-state-*` headers from the caller
|
|
433
|
-
- `actorToken()` returns the actor token itself, for APIs that take it in the request body, such
|
|
434
|
-
as SOAP. Treat it as a secret.
|
|
435
|
-
|
|
436
|
-
`formatActorHeader(actorHeader, token)` returns the `{ name, value }` for an API profile's actor
|
|
437
|
-
header: `Bearer <token>` for `Authorization`, and the raw token for any other header. It returns
|
|
438
|
-
`null` for a SOAP profile (`SOAP SessionHeader`), whose token goes in the envelope instead.
|
|
439
|
-
|
|
440
|
-
**SOAP APIs:** Salesforce's `soap-partner` profile, when the release offers it (the default
|
|
441
|
-
release does), takes the actor token in the envelope's `SessionHeader/sessionId`. XML-escape any
|
|
442
|
-
value you put in the envelope:
|
|
202
|
+
## Retries and timeouts
|
|
443
203
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
<env:Body><undelete><ids>001000000000000AAA</ids></undelete></env:Body>
|
|
449
|
-
</env:Envelope>`;
|
|
450
|
-
const response = await soap.fetch('/services/Soap/u/67.0', {
|
|
451
|
-
method: 'POST',
|
|
452
|
-
headers: { 'Content-Type': 'text/xml', SOAPAction: '""' },
|
|
453
|
-
body: envelope,
|
|
454
|
-
});
|
|
455
|
-
```
|
|
204
|
+
The SDK retries `502`, `503`, `504` and connection errors with backoff, until a deadline.
|
|
205
|
+
`create` and `snapshots.create` send one `Idempotency-Key` for every retry; pass your own
|
|
206
|
+
`idempotencyKey` to replay a call whose outcome you don't know. Each HTTP attempt times out after
|
|
207
|
+
`attemptTimeoutMs`.
|
|
456
208
|
|
|
457
|
-
|
|
458
|
-
as jsforce only sends it if you configure custom headers. Prefer `Connection.fetch`. If you pass
|
|
459
|
-
`headers()` to another client, those headers carry live tokens: only send them to `baseUrl`, and
|
|
460
|
-
don't let the client follow redirects, which would forward the tokens to another host:
|
|
209
|
+
## Pagination
|
|
461
210
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const response = await fetch(new URL('services/data/v67.0/limits', salesforce.baseUrl), {
|
|
465
|
-
headers: salesforce.headers(),
|
|
466
|
-
redirect: 'manual',
|
|
467
|
-
});
|
|
468
|
-
```
|
|
211
|
+
Every `list` returns one page, `{ data, nextCursor }`. Pass `nextCursor` back as `cursor` until it
|
|
212
|
+
is `null`. [`openapi.json`](openapi.json) in this package gives the range and the default of `limit`.
|
|
469
213
|
|
|
470
214
|
## Recorded requests
|
|
471
215
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const stats = await sm.environments.getRequestStats(env.id, {
|
|
477
|
-
from: new Date(Date.now() - 60 * 60_000),
|
|
478
|
-
to: new Date(),
|
|
479
|
-
});
|
|
480
|
-
console.log(stats.count, stats.p95Ms);
|
|
481
|
-
const page = await sm.environments.listRequests(env.id, { appKey: 'salesforce', limit: 20 });
|
|
482
|
-
const first = page.data[0];
|
|
483
|
-
if (first) {
|
|
484
|
-
const detail = await sm.environments.getRequest(env.id, first.id);
|
|
485
|
-
console.log(detail.status, detail.responseBody.preview);
|
|
486
|
-
const body = await sm.environments.getRequestPayload(env.id, first.id, 'response');
|
|
487
|
-
const bytes = await new Response(body).arrayBuffer();
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
Payload coverage reports credential redaction and omissions. The detail preview
|
|
492
|
-
is bounded; the payload method returns the full stored body as a byte stream.
|
|
216
|
+
`sm.requests.list({ environmentId })` pages the calls the gateway recorded for an environment,
|
|
217
|
+
newest first. `sm.requests.get(id)` adds headers and a preview of each body. `sm.requests.body(id,
|
|
218
|
+
'response')` returns the full body as a `Uint8Array`. `sm.requests.stats({ environmentId, from,
|
|
219
|
+
to })` counts them. Recording is best effort.
|
|
493
220
|
|
|
494
221
|
## Supported runtimes
|
|
495
222
|
|
|
496
|
-
- **Node.js
|
|
497
|
-
|
|
498
|
-
- **Cloudflare Workers.**
|
|
223
|
+
- **Node.js**, at the versions that `engines` in `package.json` allows. ESM only.
|
|
224
|
+
- **Cloudflare Workers.** Pass `apiKey` explicitly.
|
|
499
225
|
- **Not browsers.** Never ship an API key to a browser.
|
|
500
226
|
|
|
501
227
|
TypeScript consumers need `lib: ["DOM"]` or `@types/node`, for the `fetch`, `Request` and
|
|
502
|
-
`Response` types.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
## Known limitations
|
|
532
|
-
|
|
533
|
-
- Vendor clients such as jsforce don't send `X-State-Environment-Token` unless you configure
|
|
534
|
-
custom headers, so the gateway rejects them. Use `Connection.fetch`, or pass `headers()` as
|
|
535
|
-
shown in [Connections](#connections).
|
|
536
|
-
- There is no default API profile: pass `apiId` whenever an app has more than one.
|
|
537
|
-
- Without a declared default connection, the default actor is the first one the release lists;
|
|
538
|
-
releases don't declare a default actor yet.
|
|
539
|
-
- `await using` isn't supported. Stop the environment in `finally`.
|
|
540
|
-
|
|
541
|
-
## What's new in 0.5.0
|
|
542
|
-
|
|
543
|
-
### Added
|
|
544
|
-
|
|
545
|
-
- `sm.environments.listRequests`, `getRequest`, `getRequestPayload` and `getRequestStats`
|
|
546
|
-
read best-effort gateway request history, including after the environment stops.
|
|
547
|
-
- A failed environment retains its application data and can be resumed or stopped.
|
|
548
|
-
Stop takes priority over startup, recovery and snapshot capture.
|
|
549
|
-
|
|
550
|
-
### Removed
|
|
551
|
-
|
|
552
|
-
- The generated `listEnvironmentHistory`, `getEnvironmentPosition` and
|
|
553
|
-
`getEnvironmentExchange` operations and their history/position/exchange models.
|
|
554
|
-
Use the request-history methods above for recorded replica traffic.
|
|
555
|
-
- `EnvironmentApp.forkedAt`. Database object history and point-in-time forks are no longer
|
|
556
|
-
part of the API.
|
|
557
|
-
|
|
558
|
-
`waitUntilStopped()` now requires `stopped` and completed disposal. A retained `failed`
|
|
559
|
-
environment is not stopped. Lifecycle commands require Temporal availability; existing
|
|
560
|
-
replica traffic does not.
|
|
561
|
-
|
|
562
|
-
## What's new in 0.4.0
|
|
563
|
-
|
|
564
|
-
- `sm.environments.suspend(id)` ends an environment's sandboxes and keeps its data;
|
|
565
|
-
`sm.environments.resume(id)` runs it again on that data, and
|
|
566
|
-
`sm.environments.waitUntilSuspended(id)` waits for the suspension (see
|
|
567
|
-
[Suspending and resuming](#suspending-and-resuming)).
|
|
568
|
-
- `RunningEnvironment` gains `suspend()`, `resume()`, `waitUntilSuspended()` and
|
|
569
|
-
`waitUntilReady()`.
|
|
570
|
-
- `stop` is unchanged: it ends the environment and discards its data, suspended or not.
|
|
571
|
-
- New statuses: `suspending` and `suspended` for environments, `suspended` and `recovering` for
|
|
572
|
-
apps. New fields: `suspendedAt` and `sourceEnvironmentId` on an environment, `forkedAt` on an
|
|
573
|
-
app. `waitUntilReady()` throws `EnvironmentFailedError` on `suspending` and `suspended`.
|
|
574
|
-
- Snapshots: `consistency` is `'environment'` (every app paused together), `'app_instance'` (each
|
|
575
|
-
app captured on its own) or `null` until ready. One capture runs per environment at a time; a
|
|
576
|
-
second gets `409 capture-in-progress`.
|
|
577
|
-
|
|
578
|
-
## What's new in 0.3.0
|
|
579
|
-
|
|
580
|
-
- `sm.snapshots`: `capture`, `create`, `waitUntilReady`, `get`, `list`, `rename` and `delete`
|
|
581
|
-
(see [Snapshots](#snapshots)).
|
|
582
|
-
- `env.snapshot()` captures a running environment and waits until the snapshot is `ready`.
|
|
583
|
-
- `start({ snapshotId })` and `create({ snapshotId })` start an environment from a snapshot.
|
|
584
|
-
Environments carry `sourceEnvironmentSnapshotId`, and apps `sourceAppInstanceSnapshotId`.
|
|
585
|
-
- `environments.list({ snapshotId })` lists the environments started from a snapshot.
|
|
586
|
-
- `SnapshotFailedError` and `SnapshotTimeoutError`.
|
|
587
|
-
|
|
588
|
-
## What's new in 0.2.0
|
|
589
|
-
|
|
590
|
-
- `sm.environments.start()` creates an environment and waits until it's running. It returns a
|
|
591
|
-
`RunningEnvironment` with `connect`, `actors`, `stop` and `waitUntilStopped`.
|
|
592
|
-
- String apps: `apps: ['salesforce']` means `[{ key: 'salesforce', appId: 'salesforce' }]`, in
|
|
593
|
-
`start()` and `create()`.
|
|
594
|
-
- `actors(appKey)` lists an app's actors in the release's order.
|
|
595
|
-
- `actorId` is optional in `connect()`. It defaults to the environment's declared default
|
|
596
|
-
connection's actor, or else the app's first actor.
|
|
597
|
-
- When readiness fails, `start()` stops the environment before it rethrows.
|
|
598
|
-
- Cancelling a call now takes effect immediately (fixes 0.1.1). A `signal` used to reach an
|
|
599
|
-
in-flight request only when its attempt timeout (60 seconds by default) fired. An abort during
|
|
600
|
-
a retry delay still takes effect when the delay ends.
|
|
228
|
+
`Response` types. While the version is `0.x`, a minor version may include breaking changes. Enums
|
|
229
|
+
are open: handle values you don't recognise.
|
|
230
|
+
|
|
231
|
+
## Upgrade from 0.5
|
|
232
|
+
|
|
233
|
+
0.6 follows the current API. 0.5 and older call routes that the API no longer serves, so they fail
|
|
234
|
+
against it.
|
|
235
|
+
|
|
236
|
+
| 0.5 | 0.6 |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `STATE_API_KEY` | `STATEMACHINES_API_KEY` |
|
|
239
|
+
| Default host `https://state-api.usestatemachines.workers.dev` | `https://api.usestatemachines.com` |
|
|
240
|
+
| `sm.environments.start(input)` | `sm.environments.create(input)`, which waits until `running` |
|
|
241
|
+
| `env.stop()`, `sm.environments.stop(id)` | `env.delete()`, `sm.environments.delete(id)` |
|
|
242
|
+
| `env.suspend()`, `sm.environments.suspend(id)` | `env.pause()`, `sm.environments.pause(id)` |
|
|
243
|
+
| `env.resume()` then `env.waitUntilReady()` | `env.resume()`, which waits until `running` |
|
|
244
|
+
| `waitUntilReady`, `waitUntilStopped`, `waitUntilSuspended` | `sm.environments.wait(id, { status })` |
|
|
245
|
+
| Apps as `{ key, appId }` | Apps as `{ app, name }`, or the app name as a string |
|
|
246
|
+
| `env.connect(app, { apiId, actorId })` returns a `Connection` | `env.connect(app, { actorId })` returns `Credentials`; `fetch` picks the API from the path |
|
|
247
|
+
| `connection.baseUrl`, `connection.headers()` | `credentials.url`, `credentials.headers`, `credentials.token` |
|
|
248
|
+
| `X-State-Environment-Token` | `StateMachines-Environment-Token` |
|
|
249
|
+
| `env.snapshot(options)`, `sm.snapshots.capture(environmentId, options)` | `env.snapshots.create(options)`, `sm.snapshots.create({ environmentId, ...options })` |
|
|
250
|
+
| `sm.environments.start({ snapshotId })` | `sm.environments.create({ snapshotId })` |
|
|
251
|
+
| `sm.environments.listRequests`, `getRequest`, `getRequestPayload`, `getRequestStats` | `sm.requests.list({ environmentId })`, `get(id)`, `body(id, direction)`, `stats(query)` |
|
|
252
|
+
| `sm.credential.get()` | `sm.me()` |
|
|
253
|
+
| The catalog | `sm.apps.list()`, `sm.apps.versions(appId)` |
|
|
254
|
+
| `TimeoutError` | `WaitTimeoutError` |
|
|
255
|
+
| `error.retryAfter` | `error.retryAfterMs` |
|
|
256
|
+
| Error codes such as `environment-limit` and `capture-in-progress` | Codes such as `environment_limit_reached` and `snapshot_in_progress`; see [Errors](#errors) |
|
|
601
257
|
|
|
602
258
|
## License
|
|
603
259
|
|