duroxide 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +158 -0
- package/index.d.ts +220 -0
- package/index.js +321 -0
- package/lib/duroxide.js +727 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Affan Dar and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# duroxide-node
|
|
2
|
+
|
|
3
|
+
Node.js/TypeScript SDK for the [Duroxide](https://github.com/affandar/duroxide) durable execution runtime. Write reliable, long-running workflows in JavaScript using generator functions — backed by a Rust runtime that handles persistence, replay, and fault tolerance.
|
|
4
|
+
|
|
5
|
+
> See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Durable orchestrations** — generator-based workflows that survive process restarts
|
|
10
|
+
- **Automatic replay** — the Rust runtime replays history on restart, your code picks up where it left off
|
|
11
|
+
- **Activities** — async functions for side effects (API calls, DB writes, etc.)
|
|
12
|
+
- **Timers** — durable delays that persist across restarts
|
|
13
|
+
- **Sub-orchestrations** — compose workflows from smaller workflows
|
|
14
|
+
- **External events** — pause workflows and wait for signals
|
|
15
|
+
- **Fan-out/fan-in** — run tasks in parallel with `ctx.all()` (supports all task types)
|
|
16
|
+
- **Race conditions** — wait for the first of multiple tasks with `ctx.race()` (supports all task types)
|
|
17
|
+
- **Cooperative cancellation** — activities detect when they're no longer needed via `ctx.isCancelled()`
|
|
18
|
+
- **Continue-as-new** — restart orchestrations with fresh history for eternal workflows
|
|
19
|
+
- **Structured tracing** — orchestration and activity logs route through Rust's `tracing` crate
|
|
20
|
+
- **SQLite & PostgreSQL** — pluggable storage backends
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install duroxide
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
const { SqliteProvider, Client, Runtime } = require('duroxide');
|
|
30
|
+
|
|
31
|
+
async function main() {
|
|
32
|
+
// 1. Open a storage backend
|
|
33
|
+
const provider = await SqliteProvider.open('sqlite:myapp.db');
|
|
34
|
+
const client = new Client(provider);
|
|
35
|
+
const runtime = new Runtime(provider);
|
|
36
|
+
|
|
37
|
+
// 2. Register activities (async functions with side effects)
|
|
38
|
+
runtime.registerActivity('Greet', async (ctx, name) => {
|
|
39
|
+
ctx.traceInfo(`greeting ${name}`);
|
|
40
|
+
return `Hello, ${name}!`;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 3. Register orchestrations (generator functions)
|
|
44
|
+
runtime.registerOrchestration('GreetWorkflow', function* (ctx, input) {
|
|
45
|
+
const greeting = yield ctx.scheduleActivity('Greet', input.name);
|
|
46
|
+
ctx.traceInfo(`got: ${greeting}`);
|
|
47
|
+
return greeting;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 4. Start the runtime
|
|
51
|
+
await runtime.start();
|
|
52
|
+
|
|
53
|
+
// 5. Start an orchestration and wait for it
|
|
54
|
+
await client.startOrchestration('greet-1', 'GreetWorkflow', { name: 'World' });
|
|
55
|
+
const result = await client.waitForOrchestration('greet-1');
|
|
56
|
+
console.log(result.output); // "Hello, World!"
|
|
57
|
+
|
|
58
|
+
await runtime.shutdown();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
main();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Why Generators (not async/await)?
|
|
65
|
+
|
|
66
|
+
Duroxide uses `function*` generators instead of `async function` for orchestrations. This is a deliberate design choice — see [Architecture](docs/architecture.md#yield-vs-await) for the full explanation. The short version: generators give Rust full control over when and how each step executes, which is essential for deterministic replay.
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// ✅ Orchestrations use yield
|
|
70
|
+
runtime.registerOrchestration('MyWorkflow', function* (ctx, input) {
|
|
71
|
+
const result = yield ctx.scheduleActivity('DoWork', input);
|
|
72
|
+
return result;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ✅ Activities use async/await (normal async functions)
|
|
76
|
+
runtime.registerActivity('DoWork', async (ctx, input) => {
|
|
77
|
+
const data = await fetch(`https://api.example.com/${input}`);
|
|
78
|
+
return data;
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Orchestration Context API
|
|
83
|
+
|
|
84
|
+
All scheduling methods return descriptors that must be **yielded**:
|
|
85
|
+
|
|
86
|
+
| Method | Description |
|
|
87
|
+
|--------|-------------|
|
|
88
|
+
| `yield ctx.scheduleActivity(name, input)` | Run an activity |
|
|
89
|
+
| `yield ctx.scheduleActivityWithRetry(name, input, retryPolicy)` | Run with retry |
|
|
90
|
+
| `yield ctx.scheduleTimer(delayMs)` | Durable delay |
|
|
91
|
+
| `yield ctx.waitForEvent(eventName)` | Wait for external signal |
|
|
92
|
+
| `yield ctx.scheduleSubOrchestration(name, input)` | Run child workflow (await result) |
|
|
93
|
+
| `yield ctx.scheduleSubOrchestrationWithId(name, id, input)` | Child with explicit ID |
|
|
94
|
+
| `yield ctx.startOrchestration(name, id, input)` | Fire-and-forget orchestration |
|
|
95
|
+
| `yield ctx.all([task1, task2, ...])` | Parallel execution (like `Promise.all`) |
|
|
96
|
+
| `yield ctx.race(task1, task2)` | First-to-complete (like `Promise.race`) |
|
|
97
|
+
| `yield ctx.utcNow()` | Deterministic timestamp |
|
|
98
|
+
| `yield ctx.newGuid()` | Deterministic GUID |
|
|
99
|
+
| `yield ctx.continueAsNew(newInput)` | Restart with fresh history |
|
|
100
|
+
|
|
101
|
+
Tracing methods are **fire-and-forget** (no yield needed):
|
|
102
|
+
|
|
103
|
+
| Method | Description |
|
|
104
|
+
|--------|-------------|
|
|
105
|
+
| `ctx.traceInfo(message)` | INFO log (suppressed during replay) |
|
|
106
|
+
| `ctx.traceWarn(message)` | WARN log |
|
|
107
|
+
| `ctx.traceError(message)` | ERROR log |
|
|
108
|
+
| `ctx.traceDebug(message)` | DEBUG log |
|
|
109
|
+
|
|
110
|
+
## Storage Backends
|
|
111
|
+
|
|
112
|
+
### SQLite
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
const provider = await SqliteProvider.open('sqlite:path/to/db.db');
|
|
116
|
+
// or in-memory:
|
|
117
|
+
const provider = await SqliteProvider.inMemory();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### PostgreSQL
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
const provider = await PostgresProvider.connectWithSchema(
|
|
124
|
+
'postgresql://user:pass@host:5432/db',
|
|
125
|
+
'my_schema'
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Logging
|
|
130
|
+
|
|
131
|
+
Duroxide uses Rust's `tracing` crate. Control verbosity with `RUST_LOG`:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
RUST_LOG=info node app.js # INFO and above
|
|
135
|
+
RUST_LOG=duroxide=debug node app.js # DEBUG for duroxide only
|
|
136
|
+
RUST_LOG=duroxide::activity=info node app.js # Activity traces only
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
- [Architecture](docs/architecture.md) — how the Rust/JS interop works, yield vs await, limitations
|
|
142
|
+
- [User Guide](docs/user-guide.md) — patterns, recipes, and best practices
|
|
143
|
+
|
|
144
|
+
## Tests
|
|
145
|
+
|
|
146
|
+
Requires PostgreSQL (see `.env.example`):
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npm test # e2e tests (23 PG + 1 SQLite smoketest)
|
|
150
|
+
npm run test:races # Race/join composition tests (7 tests)
|
|
151
|
+
npm run test:admin # Admin API tests (14 tests)
|
|
152
|
+
npm run test:scenarios # Scenario tests (6 tests)
|
|
153
|
+
npm run test:all # Everything (50 tests)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
[MIT](LICENSE)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
6
|
+
/** Runtime options configurable from JavaScript. */
|
|
7
|
+
export interface JsRuntimeOptions {
|
|
8
|
+
/** Orchestration concurrency (default: 4) */
|
|
9
|
+
orchestrationConcurrency?: number
|
|
10
|
+
/** Worker/activity concurrency (default: 8) */
|
|
11
|
+
workerConcurrency?: number
|
|
12
|
+
/** Dispatcher poll interval in ms (default: 100) */
|
|
13
|
+
dispatcherPollIntervalMs?: number
|
|
14
|
+
/**
|
|
15
|
+
* Worker lock timeout in ms (default: 30000). Controls how often the activity
|
|
16
|
+
* manager renews locks, which affects cancellation detection speed.
|
|
17
|
+
*/
|
|
18
|
+
workerLockTimeoutMs?: number
|
|
19
|
+
}
|
|
20
|
+
/** Orchestration status returned to JS. */
|
|
21
|
+
export interface JsOrchestrationStatus {
|
|
22
|
+
status: string
|
|
23
|
+
output?: string
|
|
24
|
+
error?: string
|
|
25
|
+
}
|
|
26
|
+
/** System metrics returned to JS. */
|
|
27
|
+
export interface JsSystemMetrics {
|
|
28
|
+
totalInstances: number
|
|
29
|
+
totalExecutions: number
|
|
30
|
+
runningInstances: number
|
|
31
|
+
completedInstances: number
|
|
32
|
+
failedInstances: number
|
|
33
|
+
totalEvents: number
|
|
34
|
+
}
|
|
35
|
+
/** Queue depths returned to JS. */
|
|
36
|
+
export interface JsQueueDepths {
|
|
37
|
+
orchestratorQueue: number
|
|
38
|
+
workerQueue: number
|
|
39
|
+
timerQueue: number
|
|
40
|
+
}
|
|
41
|
+
/** Instance info returned to JS. */
|
|
42
|
+
export interface JsInstanceInfo {
|
|
43
|
+
instanceId: string
|
|
44
|
+
orchestrationName: string
|
|
45
|
+
orchestrationVersion: string
|
|
46
|
+
currentExecutionId: number
|
|
47
|
+
status: string
|
|
48
|
+
output?: string
|
|
49
|
+
createdAt: number
|
|
50
|
+
updatedAt: number
|
|
51
|
+
parentInstanceId?: string
|
|
52
|
+
}
|
|
53
|
+
/** Execution info returned to JS. */
|
|
54
|
+
export interface JsExecutionInfo {
|
|
55
|
+
executionId: number
|
|
56
|
+
status: string
|
|
57
|
+
output?: string
|
|
58
|
+
startedAt: number
|
|
59
|
+
completedAt?: number
|
|
60
|
+
eventCount: number
|
|
61
|
+
}
|
|
62
|
+
/** Instance tree returned to JS. */
|
|
63
|
+
export interface JsInstanceTree {
|
|
64
|
+
rootId: string
|
|
65
|
+
allIds: Array<string>
|
|
66
|
+
size: number
|
|
67
|
+
}
|
|
68
|
+
/** Delete result returned to JS. */
|
|
69
|
+
export interface JsDeleteInstanceResult {
|
|
70
|
+
instancesDeleted: number
|
|
71
|
+
executionsDeleted: number
|
|
72
|
+
eventsDeleted: number
|
|
73
|
+
queueMessagesDeleted: number
|
|
74
|
+
}
|
|
75
|
+
/** Prune options from JS. */
|
|
76
|
+
export interface JsPruneOptions {
|
|
77
|
+
keepLast?: number
|
|
78
|
+
completedBefore?: number
|
|
79
|
+
}
|
|
80
|
+
/** Prune result returned to JS. */
|
|
81
|
+
export interface JsPruneResult {
|
|
82
|
+
instancesProcessed: number
|
|
83
|
+
executionsDeleted: number
|
|
84
|
+
eventsDeleted: number
|
|
85
|
+
}
|
|
86
|
+
/** Instance filter from JS. */
|
|
87
|
+
export interface JsInstanceFilter {
|
|
88
|
+
instanceIds?: Array<string>
|
|
89
|
+
completedBefore?: number
|
|
90
|
+
limit?: number
|
|
91
|
+
}
|
|
92
|
+
/** A single history event returned to JS. */
|
|
93
|
+
export interface JsEvent {
|
|
94
|
+
eventId: number
|
|
95
|
+
kind: string
|
|
96
|
+
sourceEventId?: number
|
|
97
|
+
timestampMs: number
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Emit an activity trace through the current Rust ActivityContext.
|
|
101
|
+
* Delegates to ActivityContext.trace_info/warn/error/debug which includes
|
|
102
|
+
* all structured fields (instance_id, activity_name, activity_id, worker_id, etc.)
|
|
103
|
+
*/
|
|
104
|
+
export declare function activityTraceLog(token: string, level: string, message: string): void
|
|
105
|
+
/**
|
|
106
|
+
* Emit an orchestration trace through the Rust OrchestrationContext.
|
|
107
|
+
* Delegates to OrchestrationContext.trace() which checks is_replaying
|
|
108
|
+
* and includes all structured fields (instance_id, orchestration_name, etc.)
|
|
109
|
+
*/
|
|
110
|
+
export declare function orchestrationTraceLog(instanceId: string, level: string, message: string): void
|
|
111
|
+
/**
|
|
112
|
+
* Check if an activity's cancellation token has been triggered.
|
|
113
|
+
* Returns true if the activity has been cancelled (e.g., due to losing a race/select).
|
|
114
|
+
*/
|
|
115
|
+
export declare function activityIsCancelled(token: string): boolean
|
|
116
|
+
/** Wraps duroxide's Client for use from JavaScript. */
|
|
117
|
+
export declare class JsClient {
|
|
118
|
+
constructor(provider: JsSqliteProvider)
|
|
119
|
+
/** Create a client backed by PostgreSQL. */
|
|
120
|
+
static fromPostgres(provider: JsPostgresProvider): JsClient
|
|
121
|
+
/** Start a new orchestration instance. */
|
|
122
|
+
startOrchestration(instanceId: string, orchestrationName: string, input: string): Promise<void>
|
|
123
|
+
/** Start a new orchestration instance with a specific version. */
|
|
124
|
+
startOrchestrationVersioned(instanceId: string, orchestrationName: string, input: string, version: string): Promise<void>
|
|
125
|
+
/** Get the current status of an orchestration instance. */
|
|
126
|
+
getStatus(instanceId: string): Promise<JsOrchestrationStatus>
|
|
127
|
+
/** Wait for an orchestration to complete (with timeout in milliseconds). */
|
|
128
|
+
waitForOrchestration(instanceId: string, timeoutMs: number): Promise<JsOrchestrationStatus>
|
|
129
|
+
/** Cancel a running orchestration instance. */
|
|
130
|
+
cancelInstance(instanceId: string, reason?: string | undefined | null): Promise<void>
|
|
131
|
+
/** Raise an external event to an orchestration instance. */
|
|
132
|
+
raiseEvent(instanceId: string, eventName: string, data: string): Promise<void>
|
|
133
|
+
/** Get system metrics (if provider supports management). */
|
|
134
|
+
getSystemMetrics(): Promise<JsSystemMetrics>
|
|
135
|
+
/** Get queue depths (if provider supports management). */
|
|
136
|
+
getQueueDepths(): Promise<JsQueueDepths>
|
|
137
|
+
/** List all orchestration instance IDs. */
|
|
138
|
+
listAllInstances(): Promise<Array<string>>
|
|
139
|
+
/** List orchestration instance IDs by status. */
|
|
140
|
+
listInstancesByStatus(status: string): Promise<Array<string>>
|
|
141
|
+
/** Get detailed info about a specific instance. */
|
|
142
|
+
getInstanceInfo(instanceId: string): Promise<JsInstanceInfo>
|
|
143
|
+
/** Get detailed info about a specific execution within an instance. */
|
|
144
|
+
getExecutionInfo(instanceId: string, executionId: number): Promise<JsExecutionInfo>
|
|
145
|
+
/** List execution IDs for an instance. */
|
|
146
|
+
listExecutions(instanceId: string): Promise<Array<number>>
|
|
147
|
+
/** Read the event history for a specific execution. */
|
|
148
|
+
readExecutionHistory(instanceId: string, executionId: number): Promise<Array<JsEvent>>
|
|
149
|
+
/** Get the full instance tree (root + all descendants). */
|
|
150
|
+
getInstanceTree(instanceId: string): Promise<JsInstanceTree>
|
|
151
|
+
/** Delete an orchestration instance and all its data. */
|
|
152
|
+
deleteInstance(instanceId: string, force: boolean): Promise<JsDeleteInstanceResult>
|
|
153
|
+
/** Delete multiple instances matching a filter. */
|
|
154
|
+
deleteInstanceBulk(filter: JsInstanceFilter): Promise<JsDeleteInstanceResult>
|
|
155
|
+
/** Prune old executions from a single instance. */
|
|
156
|
+
pruneExecutions(instanceId: string, options: JsPruneOptions): Promise<JsPruneResult>
|
|
157
|
+
/** Prune old executions from multiple instances matching a filter. */
|
|
158
|
+
pruneExecutionsBulk(filter: JsInstanceFilter, options: JsPruneOptions): Promise<JsPruneResult>
|
|
159
|
+
}
|
|
160
|
+
/** Wraps duroxide-pg's PostgresProvider for use from JavaScript. */
|
|
161
|
+
export declare class JsPostgresProvider {
|
|
162
|
+
/**
|
|
163
|
+
* Connect to a PostgreSQL database.
|
|
164
|
+
* Uses the default "public" schema.
|
|
165
|
+
*/
|
|
166
|
+
static connect(databaseUrl: string): Promise<JsPostgresProvider>
|
|
167
|
+
/**
|
|
168
|
+
* Connect to a PostgreSQL database with a custom schema.
|
|
169
|
+
* The schema will be created if it does not exist.
|
|
170
|
+
*/
|
|
171
|
+
static connectWithSchema(databaseUrl: string, schema: string): Promise<JsPostgresProvider>
|
|
172
|
+
}
|
|
173
|
+
/** Wraps duroxide's SqliteProvider for use from JavaScript. */
|
|
174
|
+
export declare class JsSqliteProvider {
|
|
175
|
+
/**
|
|
176
|
+
* Open a SQLite database at the given file path.
|
|
177
|
+
* Path should be a sqlite: URL, e.g. "sqlite:./data.db" or "sqlite:/tmp/test.db".
|
|
178
|
+
* The file will be created if it does not exist.
|
|
179
|
+
*/
|
|
180
|
+
static open(path: string): Promise<JsSqliteProvider>
|
|
181
|
+
/** Create an in-memory SQLite database (useful for testing). */
|
|
182
|
+
static inMemory(): Promise<JsSqliteProvider>
|
|
183
|
+
}
|
|
184
|
+
/** Builder for the duroxide runtime, wrapping registration and startup. */
|
|
185
|
+
export declare class JsRuntime {
|
|
186
|
+
constructor(provider: JsSqliteProvider, options?: JsRuntimeOptions | undefined | null)
|
|
187
|
+
/** Create a runtime backed by PostgreSQL. */
|
|
188
|
+
static fromPostgres(provider: JsPostgresProvider, options?: JsRuntimeOptions | undefined | null): JsRuntime
|
|
189
|
+
/**
|
|
190
|
+
* Set the generator driver functions (called once from JS before registering orchestrations).
|
|
191
|
+
* These three functions handle: creating generators, driving next steps, and disposing.
|
|
192
|
+
*/
|
|
193
|
+
setGeneratorDriver(createFn: (arg: string) => any, nextFn: (arg: string) => any, disposeFn: (arg: string) => any): void
|
|
194
|
+
/**
|
|
195
|
+
* Register a JavaScript activity function.
|
|
196
|
+
* The JS function receives (contextInfoJson, input) and returns a Promise<string>.
|
|
197
|
+
*/
|
|
198
|
+
registerActivity(name: string, callback: (arg: string) => any): void
|
|
199
|
+
/**
|
|
200
|
+
* Register a JavaScript orchestration (generator function).
|
|
201
|
+
* The orchestration name is used for both registration and the generator function lookup.
|
|
202
|
+
*/
|
|
203
|
+
registerOrchestration(name: string): void
|
|
204
|
+
/** Register a versioned JavaScript orchestration. */
|
|
205
|
+
registerOrchestrationVersioned(name: string, version: string): void
|
|
206
|
+
/**
|
|
207
|
+
* Start the runtime. This processes orchestrations and activities until shutdown.
|
|
208
|
+
*
|
|
209
|
+
* # Safety
|
|
210
|
+
* This is async and takes &mut self. napi-rs requires async &mut methods to be marked unsafe.
|
|
211
|
+
*/
|
|
212
|
+
start(): Promise<void>
|
|
213
|
+
/**
|
|
214
|
+
* Shutdown the runtime gracefully.
|
|
215
|
+
*
|
|
216
|
+
* # Safety
|
|
217
|
+
* Must not be called concurrently from multiple threads.
|
|
218
|
+
*/
|
|
219
|
+
shutdown(timeoutMs?: number | undefined | null): Promise<void>
|
|
220
|
+
}
|