@solidactions/sdk 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.
Files changed (167) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.clavix/outputs/dbos-http-sdk/full-prd.md +142 -0
  3. package/.clavix/outputs/dbos-http-sdk/quick-prd.md +12 -0
  4. package/.clavix/outputs/dbos-http-sdk/tasks.md +630 -0
  5. package/.clavix/outputs/prompts/dbos-http-api-20260110-033219.md +91 -0
  6. package/.husky/pre-commit +1 -0
  7. package/.prettierignore +3 -0
  8. package/.prettierrc +9 -0
  9. package/CODE_OF_CONDUCT.md +49 -0
  10. package/CONTRIBUTING.md +47 -0
  11. package/LICENSE +21 -0
  12. package/README.md +172 -0
  13. package/dist/dbos-config.schema.json +132 -0
  14. package/dist/schemas/system_db_schema.d.ts +73 -0
  15. package/dist/schemas/system_db_schema.d.ts.map +1 -0
  16. package/dist/schemas/system_db_schema.js +3 -0
  17. package/dist/schemas/system_db_schema.js.map +1 -0
  18. package/dist/src/adminserver.d.ts +79 -0
  19. package/dist/src/adminserver.d.ts.map +1 -0
  20. package/dist/src/adminserver.js +495 -0
  21. package/dist/src/adminserver.js.map +1 -0
  22. package/dist/src/authdecorators.d.ts +2 -0
  23. package/dist/src/authdecorators.d.ts.map +1 -0
  24. package/dist/src/authdecorators.js +48 -0
  25. package/dist/src/authdecorators.js.map +1 -0
  26. package/dist/src/cli/cli.d.ts +9 -0
  27. package/dist/src/cli/cli.d.ts.map +1 -0
  28. package/dist/src/cli/cli.js +116 -0
  29. package/dist/src/cli/cli.js.map +1 -0
  30. package/dist/src/cli/commands.d.ts +3 -0
  31. package/dist/src/cli/commands.d.ts.map +1 -0
  32. package/dist/src/cli/commands.js +46 -0
  33. package/dist/src/cli/commands.js.map +1 -0
  34. package/dist/src/client.d.ts +85 -0
  35. package/dist/src/client.d.ts.map +1 -0
  36. package/dist/src/client.js +186 -0
  37. package/dist/src/client.js.map +1 -0
  38. package/dist/src/conductor/conductor.d.ts +28 -0
  39. package/dist/src/conductor/conductor.d.ts.map +1 -0
  40. package/dist/src/conductor/conductor.js +376 -0
  41. package/dist/src/conductor/conductor.js.map +1 -0
  42. package/dist/src/conductor/protocol.d.ts +238 -0
  43. package/dist/src/conductor/protocol.d.ts.map +1 -0
  44. package/dist/src/conductor/protocol.js +353 -0
  45. package/dist/src/conductor/protocol.js.map +1 -0
  46. package/dist/src/config.d.ts +91 -0
  47. package/dist/src/config.d.ts.map +1 -0
  48. package/dist/src/config.js +199 -0
  49. package/dist/src/config.js.map +1 -0
  50. package/dist/src/context.d.ts +62 -0
  51. package/dist/src/context.d.ts.map +1 -0
  52. package/dist/src/context.js +118 -0
  53. package/dist/src/context.js.map +1 -0
  54. package/dist/src/database_utils.d.ts +17 -0
  55. package/dist/src/database_utils.d.ts.map +1 -0
  56. package/dist/src/database_utils.js +53 -0
  57. package/dist/src/database_utils.js.map +1 -0
  58. package/dist/src/datasource.d.ts +109 -0
  59. package/dist/src/datasource.d.ts.map +1 -0
  60. package/dist/src/datasource.js +204 -0
  61. package/dist/src/datasource.js.map +1 -0
  62. package/dist/src/dbos-executor.d.ts +189 -0
  63. package/dist/src/dbos-executor.d.ts.map +1 -0
  64. package/dist/src/dbos-executor.js +817 -0
  65. package/dist/src/dbos-executor.js.map +1 -0
  66. package/dist/src/dbos.d.ts +519 -0
  67. package/dist/src/dbos.d.ts.map +1 -0
  68. package/dist/src/dbos.js +1282 -0
  69. package/dist/src/dbos.js.map +1 -0
  70. package/dist/src/debouncer.d.ts +33 -0
  71. package/dist/src/debouncer.d.ts.map +1 -0
  72. package/dist/src/debouncer.js +170 -0
  73. package/dist/src/debouncer.js.map +1 -0
  74. package/dist/src/debugpoint.d.ts +26 -0
  75. package/dist/src/debugpoint.d.ts.map +1 -0
  76. package/dist/src/debugpoint.js +65 -0
  77. package/dist/src/debugpoint.js.map +1 -0
  78. package/dist/src/decorators.d.ts +219 -0
  79. package/dist/src/decorators.d.ts.map +1 -0
  80. package/dist/src/decorators.js +873 -0
  81. package/dist/src/decorators.js.map +1 -0
  82. package/dist/src/error.d.ts +130 -0
  83. package/dist/src/error.d.ts.map +1 -0
  84. package/dist/src/error.js +290 -0
  85. package/dist/src/error.js.map +1 -0
  86. package/dist/src/http_client.d.ts +82 -0
  87. package/dist/src/http_client.d.ts.map +1 -0
  88. package/dist/src/http_client.js +286 -0
  89. package/dist/src/http_client.js.map +1 -0
  90. package/dist/src/http_system_database.d.ts +84 -0
  91. package/dist/src/http_system_database.d.ts.map +1 -0
  92. package/dist/src/http_system_database.js +429 -0
  93. package/dist/src/http_system_database.js.map +1 -0
  94. package/dist/src/index.d.ts +14 -0
  95. package/dist/src/index.d.ts.map +1 -0
  96. package/dist/src/index.js +53 -0
  97. package/dist/src/index.js.map +1 -0
  98. package/dist/src/scheduler/crontab.d.ts +14 -0
  99. package/dist/src/scheduler/crontab.d.ts.map +1 -0
  100. package/dist/src/scheduler/crontab.js +308 -0
  101. package/dist/src/scheduler/crontab.js.map +1 -0
  102. package/dist/src/scheduler/scheduler.d.ts +41 -0
  103. package/dist/src/scheduler/scheduler.d.ts.map +1 -0
  104. package/dist/src/scheduler/scheduler.js +165 -0
  105. package/dist/src/scheduler/scheduler.js.map +1 -0
  106. package/dist/src/serialization.d.ts +57 -0
  107. package/dist/src/serialization.d.ts.map +1 -0
  108. package/dist/src/serialization.js +306 -0
  109. package/dist/src/serialization.js.map +1 -0
  110. package/dist/src/solidactions-executor.d.ts +177 -0
  111. package/dist/src/solidactions-executor.d.ts.map +1 -0
  112. package/dist/src/solidactions-executor.js +817 -0
  113. package/dist/src/solidactions-executor.js.map +1 -0
  114. package/dist/src/solidactions.d.ts +519 -0
  115. package/dist/src/solidactions.d.ts.map +1 -0
  116. package/dist/src/solidactions.js +1284 -0
  117. package/dist/src/solidactions.js.map +1 -0
  118. package/dist/src/step.d.ts +16 -0
  119. package/dist/src/step.d.ts.map +1 -0
  120. package/dist/src/step.js +3 -0
  121. package/dist/src/step.js.map +1 -0
  122. package/dist/src/system_database.d.ts +141 -0
  123. package/dist/src/system_database.d.ts.map +1 -0
  124. package/dist/src/system_database.js +25 -0
  125. package/dist/src/system_database.js.map +1 -0
  126. package/dist/src/telemetry/collector.d.ts +13 -0
  127. package/dist/src/telemetry/collector.d.ts.map +1 -0
  128. package/dist/src/telemetry/collector.js +63 -0
  129. package/dist/src/telemetry/collector.js.map +1 -0
  130. package/dist/src/telemetry/exporters.d.ts +13 -0
  131. package/dist/src/telemetry/exporters.d.ts.map +1 -0
  132. package/dist/src/telemetry/exporters.js +101 -0
  133. package/dist/src/telemetry/exporters.js.map +1 -0
  134. package/dist/src/telemetry/logs.d.ts +52 -0
  135. package/dist/src/telemetry/logs.d.ts.map +1 -0
  136. package/dist/src/telemetry/logs.js +287 -0
  137. package/dist/src/telemetry/logs.js.map +1 -0
  138. package/dist/src/telemetry/traces.d.ts +52 -0
  139. package/dist/src/telemetry/traces.d.ts.map +1 -0
  140. package/dist/src/telemetry/traces.js +150 -0
  141. package/dist/src/telemetry/traces.js.map +1 -0
  142. package/dist/src/utils.d.ts +26 -0
  143. package/dist/src/utils.d.ts.map +1 -0
  144. package/dist/src/utils.js +136 -0
  145. package/dist/src/utils.js.map +1 -0
  146. package/dist/src/wfqueue.d.ts +64 -0
  147. package/dist/src/wfqueue.d.ts.map +1 -0
  148. package/dist/src/wfqueue.js +147 -0
  149. package/dist/src/wfqueue.js.map +1 -0
  150. package/dist/src/workflow.d.ts +154 -0
  151. package/dist/src/workflow.d.ts.map +1 -0
  152. package/dist/src/workflow.js +99 -0
  153. package/dist/src/workflow.js.map +1 -0
  154. package/dist/src/workflow_management.d.ts +15 -0
  155. package/dist/src/workflow_management.d.ts.map +1 -0
  156. package/dist/src/workflow_management.js +87 -0
  157. package/dist/src/workflow_management.js.map +1 -0
  158. package/dist/tsconfig.tsbuildinfo +1 -0
  159. package/docs/api-schema.md +1441 -0
  160. package/docs/migration-guide.md +460 -0
  161. package/docs/phase-14-changes.md +156 -0
  162. package/docs/solidsteps-ai-prompt.md +534 -0
  163. package/eslint.config.cjs +50 -0
  164. package/package.json +84 -0
  165. package/solidactions-ai-prompt.md +1504 -0
  166. package/solidactions-config.schema.json +132 -0
  167. package/solidactions-test-config.yaml +15 -0
@@ -0,0 +1,1504 @@
1
+ # Build Reliable Applications With SolidActions
2
+
3
+ ## Guidelines
4
+
5
+ - Respond in a friendly and concise manner
6
+ - Ask clarifying questions when requirements are ambiguous
7
+ - Generate code in TypeScript using the SolidActions library. Make sure to fully type everything.
8
+ - You MUST import all methods and classes used in the code you generate
9
+ - You SHALL keep all code in a single file unless otherwise specified.
10
+ - You MUST await all promises.
11
+ - SolidActions is a workflow automation SDK.
12
+
13
+ ## Workflow Guidelines
14
+
15
+ Workflows provide durable execution so you can write programs that are resilient to any failure.
16
+ Workflows are comprised of steps, which are ordinary TypeScript functions called with SolidActions.runStep().
17
+ When using SolidActions workflows, you should call any function that performs complex operations or accesses external APIs or services as a step using SolidActions.runStep.
18
+
19
+ If a workflow is interrupted for any reason (e.g., an executor restarts or crashes), when your program restarts the workflow automatically resumes execution from the last completed step.
20
+
21
+ - If asked to add SolidActions to existing code, you MUST ask which function to make a workflow. Do NOT recommend any changes until they have told you what function to make a workflow. Do NOT make a function a workflow unless SPECIFICALLY requested.
22
+ - When making a function a workflow, you should make all functions it calls steps. Do NOT change the functions in any way.
23
+ - Do NOT make functions steps unless they are DIRECTLY called by a workflow.
24
+ - If the workflow function performs a non-deterministic action, you MUST move that action to its own function and make that function a step. Examples of non-deterministic actions include accessing an external API or service, accessing files on disk, generating a random number, of getting the current time.
25
+ - Do NOT use Promise.all() due to the risks posed by multiple rejections. Using Promise.allSettled() for parallelism is allowed for single-step promises only. For any complex parallel execution, you should instead use SolidActions.startWorkflow and SolidActions queues to achieve the parallelism.
26
+ - SolidActions workflows and steps should NOT have side effects in memory outside of their own scope. They can access global variables, but they should NOT create or update global variables or variables outside their scope.
27
+ - Do NOT call any SolidActions context method (SolidActions.send, SolidActions.recv, SolidActions.startWorkflow, SolidActions.sleep, SolidActions.setEvent, SolidActions.getEvent) from a step.
28
+ - Do NOT start workflows from inside a step.
29
+ - Do NOT call SolidActions.setEvent and SolidActions.recv from outside a workflow function.
30
+ - Do NOT use SolidActions.getApi, SolidActions.postApi, or other SolidActions HTTP annotations. These are DEPRECATED. Instead, use Express for HTTP serving by default, unless another web framework is specified.
31
+
32
+ ## SolidActions Lifecycle Guidelines
33
+
34
+ SolidActions should be installed and imported from the `@dbos-inc/dbos-sdk` package.
35
+ Due to its internal workflow registry, The SolidActions library and SolidActions workflows cannot be bundled with JavaScript or TypeScript bundlers (Webpack, Vite, Rollup, esbuild, Parcel, etc.) and must be treated as an external library by these tools. Configuration for bundlers should be suggested if these tools are in use and cannot be avoided.
36
+
37
+ SolidActions does not support "serverless" frameworks due to its long-running background jobs. SolidActions programs MUST have a starting file (typically 'main.ts' or 'server.ts') that creates all objects and workflow functions during startup.
38
+
39
+ Any SolidActions program MUST call SolidActions.setConfig and SolidActions.launch in its main function, like so.
40
+ You MUST use this default configuration (changing the name as appropriate) unless otherwise specified.
41
+
42
+ ```javascript
43
+ SolidActions.setConfig({
44
+ name: 'dbos-node-starter',
45
+ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL,
46
+ });
47
+ await SolidActions.launch();
48
+ ```
49
+
50
+ Here is an example main function using Express:
51
+
52
+ ```javascript
53
+ import { SolidActions } from '@dbos-inc/dbos-sdk';
54
+
55
+ async function main() {
56
+ SolidActions.setConfig({
57
+ name: 'dbos-node-starter',
58
+ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL,
59
+ });
60
+ await SolidActions.launch();
61
+ const PORT = 3000;
62
+ app.listen(PORT, () => {
63
+ console.log(`🚀 Server is running on http://localhost:${PORT}`);
64
+ });
65
+ }
66
+
67
+ main().catch(console.log);
68
+ ```
69
+
70
+ ## Workflow and Steps Examples
71
+
72
+ Simple example:
73
+
74
+ ```javascript
75
+ import { SolidActions } from '@dbos-inc/dbos-sdk';
76
+
77
+ async function stepOne() {
78
+ SolidActions.logger.info('Step one completed!');
79
+ }
80
+
81
+ async function stepTwo() {
82
+ SolidActions.logger.info('Step two completed!');
83
+ }
84
+
85
+ async function exampleFunction() {
86
+ await SolidActions.runStep(() => stepOne());
87
+ await SolidActions.runStep(() => stepTwo());
88
+ }
89
+ const exampleWorkflow = SolidActions.registerWorkflow(exampleFunction);
90
+
91
+ async function main() {
92
+ SolidActions.setConfig({
93
+ name: 'dbos-node-starter',
94
+ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL,
95
+ });
96
+ await SolidActions.launch();
97
+ await exampleWorkflow();
98
+ await SolidActions.shutdown();
99
+ }
100
+
101
+ main().catch(console.log);
102
+ ```
103
+
104
+ Example with Express:
105
+
106
+ ```javascript
107
+ import { SolidActions } from '@dbos-inc/dbos-sdk';
108
+ import express from 'express';
109
+
110
+ export const app = express();
111
+ app.use(express.json());
112
+
113
+ async function stepOne() {
114
+ SolidActions.logger.info('Step one completed!');
115
+ }
116
+
117
+ async function stepTwo() {
118
+ SolidActions.logger.info('Step two completed!');
119
+ }
120
+
121
+ async function exampleFunction() {
122
+ await SolidActions.runStep(() => stepOne());
123
+ await SolidActions.runStep(() => stepTwo());
124
+ }
125
+ const exampleWorkflow = SolidActions.registerWorkflow(exampleFunction);
126
+
127
+ app.get('/', async (req, res) => {
128
+ await exampleWorkflow();
129
+ res.send();
130
+ });
131
+
132
+ async function main() {
133
+ SolidActions.setConfig({
134
+ name: 'dbos-node-starter',
135
+ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL,
136
+ });
137
+ await SolidActions.launch();
138
+ const PORT = 3000;
139
+ app.listen(PORT, () => {
140
+ console.log(`🚀 Server is running on http://localhost:${PORT}`);
141
+ });
142
+ }
143
+
144
+ main().catch(console.log);
145
+ ```
146
+
147
+ Example with queues:
148
+
149
+ ```javascript
150
+ import { SolidActions, WorkflowQueue } from "@dbos-inc/dbos-sdk";
151
+ import express from "express";
152
+
153
+ export const app = express();
154
+ app.use(express.json());
155
+
156
+ const queue = new WorkflowQueue("example_queue");
157
+
158
+ async function taskFunction(n: number) {
159
+ await SolidActions.sleep(5000);
160
+ SolidActions.logger.info(`Task ${n} completed!`)
161
+ }
162
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction);
163
+
164
+ async function queueFunction() {
165
+ SolidActions.logger.info("Enqueueing tasks!")
166
+ const handles = []
167
+ for (let i = 0; i < 10; i++) {
168
+ handles.push(await SolidActions.startWorkflow(taskWorkflow, { queueName: queue.name })(i))
169
+ }
170
+ const results = []
171
+ for (const h of handles) {
172
+ results.push(await h.getResult())
173
+ }
174
+ SolidActions.logger.info(`Successfully completed ${results.length} tasks`)
175
+ }
176
+ const queueWorkflow = SolidActions.registerWorkflow(queueFunction)
177
+
178
+ app.get("/", async (req, res) => {
179
+ await queueWorkflow();
180
+ res.send();
181
+ });
182
+
183
+ async function main() {
184
+ SolidActions.setConfig({
185
+ "name": "dbos-node-starter",
186
+ "systemDatabaseUrl": process.env.SolidActions_SYSTEM_DATABASE_URL,
187
+ });
188
+ await SolidActions.launch();
189
+ const PORT = 3000;
190
+ app.listen(PORT, () => {
191
+ console.log(`🚀 Server is running on http://localhost:${PORT}`);
192
+ });
193
+ }
194
+
195
+ main().catch(console.log);
196
+ ```
197
+
198
+ ### Scheduled Workflow
199
+
200
+ You can schedule SolidActions workflows to run exactly once per time interval.
201
+ To do this, use the the `SolidActions.registerScheduled` method or the `SolidActions.scheduled` decorator and specify the schedule in crontab syntax. For example:
202
+
203
+ - A scheduled workflow MUST specify a crontab schedule.
204
+ - It MUST take in two arguments, scheduled and actual time. Both are Date of when the workflow started.
205
+
206
+ ```typescript
207
+ async function scheduledFunction(schedTime: Date, startTime: Date) {
208
+ SolidActions.logger.info(`I am a workflow scheduled to run every 30 seconds`);
209
+ }
210
+
211
+ const scheduledWorkflow = SolidActions.registerWorkflow(scheduledFunction);
212
+ SolidActions.registerScheduled(scheduledWorkflow, { crontab: '*/30 * * * * *' });
213
+ ```
214
+
215
+ Or using decorators:
216
+
217
+ ```typescript
218
+ class ScheduledExample {
219
+ @SolidActions.workflow()
220
+ @SolidActions.scheduled({ crontab: '*/30 * * * * *' })
221
+ static async scheduledWorkflow(schedTime: Date, startTime: Date) {
222
+ SolidActions.logger.info(`I am a workflow scheduled to run every 30 seconds`);
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## Workflow Documentation:
228
+
229
+ Workflows provide **durable execution** so you can write programs that are **resilient to any failure**.
230
+ Workflows are comprised of steps, which wrap ordinary TypeScript (or JavaScript) functions.
231
+ If a workflow is interrupted for any reason (e.g., an executor restarts or crashes), when your program restarts the workflow automatically resumes execution from the last completed step.
232
+
233
+ To write a workflow, register a TypeScript function with `SolidActions.registerWorkflow`.
234
+ The function's inputs and outputs must be serializable to JSON.
235
+ For example:
236
+
237
+ ```typescript
238
+ async function stepOne() {
239
+ SolidActions.logger.info('Step one completed!');
240
+ }
241
+
242
+ async function stepTwo() {
243
+ SolidActions.logger.info('Step two completed!');
244
+ }
245
+
246
+ async function workflowFunction() {
247
+ await SolidActions.runStep(() => stepOne(), { name: 'stepOne' });
248
+ await SolidActions.runStep(() => stepTwo(), { name: 'stepTwo' });
249
+ }
250
+ const workflow = SolidActions.registerWorkflow(workflowFunction);
251
+
252
+ await workflow();
253
+ ```
254
+
255
+ Alternatively, you can register workflows and steps with decorators:
256
+
257
+ ```typescript
258
+ export class Example {
259
+ @SolidActions.step()
260
+ static async stepOne() {
261
+ SolidActions.logger.info('Step one completed!');
262
+ }
263
+
264
+ @SolidActions.step()
265
+ static async stepTwo() {
266
+ SolidActions.logger.info('Step two completed!');
267
+ }
268
+
269
+ // Call steps from workflows
270
+ @SolidActions.workflow()
271
+ static async exampleWorkflow() {
272
+ await Toolbox.stepOne();
273
+ await Toolbox.stepTwo();
274
+ }
275
+ }
276
+
277
+ await Example.exampleWorkflow();
278
+ ```
279
+
280
+ ## Starting Workflows In The Background
281
+
282
+ One common use-case for workflows is building reliable background tasks that keep running even when your program is interrupted, restarted, or crashes.
283
+ You can use `SolidActions.startWorkflow` to start a workflow in the background.
284
+ If you start a workflow this way, it returns a workflow handle, from which you can access information about the workflow or wait for it to complete and retrieve its result.
285
+
286
+ Here's an example:
287
+
288
+ ```javascript
289
+ class Example {
290
+ @SolidActions.workflow()
291
+ static async exampleWorkflow(var1: string, var2: string) {
292
+ return var1 + var2;
293
+ }
294
+ }
295
+
296
+ async function main() {
297
+ // Start exampleWorkflow in the background
298
+ const handle = await SolidActions.startWorkflow(Example).exampleWorkflow("one", "two");
299
+ // Wait for the workflow to complete and return its results
300
+ const result = await handle.getResult();
301
+ }
302
+ ```
303
+
304
+ After starting a workflow in the background, you can use `SolidActions.retrieveWorkflow` to retrieve a workflow's handle from its ID.
305
+ You can also retrieve a workflow's handle from outside of your SolidActions application with 'SolidActionsClient.retrieveWorkflow`.
306
+
307
+ If you need to run many workflows in the background and manage their concurrency or flow control, you can also use SolidActions queues.
308
+
309
+ ## Workflow IDs and Idempotency
310
+
311
+ Every time you execute a workflow, that execution is assigned a unique ID, by default a UUID.
312
+ You can access this ID through the `SolidActions.workflowID` context variable.
313
+ Workflow IDs are useful for communicating with workflows and developing interactive workflows.
314
+
315
+ You can set the workflow ID of a workflow as an argument to `SolidActions.startWorkflow()`.
316
+ Workflow IDs must be **globally unique** for your application.
317
+ An assigned workflow ID acts as an idempotency key: if a workflow is called multiple times with the same ID, it executes only once.
318
+ This is useful if your operations have side effects like making a payment or sending an email.
319
+ Workflow IDs are also useful for communicating with workflows and developing interactive workflows - see Communicating with Workflows for more details.
320
+
321
+ For example:
322
+
323
+ ```javascript
324
+ class Example {
325
+ @SolidActions.workflow()
326
+ static async exampleWorkflow(var1: string, var2: string) {
327
+ // ...
328
+ }
329
+ }
330
+
331
+ async function main() {
332
+ const myID: string = ...
333
+ const handle = await SolidActions.startWorkflow(Example, {workflowID: myID}).exampleWorkflow("one", "two");
334
+ const result = await handle.getResult();
335
+ }
336
+ ```
337
+
338
+ ## Determinism
339
+
340
+ Workflows are in most respects normal TypeScript functions.
341
+ They can have loops, branches, conditionals, and so on.
342
+ However, a workflow function must be **deterministic**: if called multiple times with the same inputs, it should invoke the same steps with the same inputs in the same order (given the same return values from those steps).
343
+ If you need to perform a non-deterministic operation like accessing the database, calling a third-party API, generating a random number, or getting the local time, you shouldn't do it directly in a workflow function.
344
+ Instead, you should do all database operations in transactions and all other non-deterministic operations in steps.
345
+
346
+ For example, **don't do this**:
347
+
348
+ ```javascript
349
+ class Example {
350
+ @SolidActions.workflow()
351
+ static async exampleWorkflow() {
352
+ // Don't make an HTTP request in a workflow function
353
+ const body = await fetch('https://example.com').then((r) => r.text());
354
+ await Example.exampleTransaction(body);
355
+ }
356
+ }
357
+ ```
358
+
359
+ Instead, do this:
360
+
361
+ ```javascript
362
+ class Example {
363
+ @SolidActions.workflow()
364
+ static async exampleWorkflow() {
365
+ // Don't make an HTTP request in a workflow function
366
+ const body = await SolidActions.runStep(
367
+ async () => {
368
+ return await fetch('https://example.com').then((r) => r.text());
369
+ },
370
+ { name: 'fetchBody' },
371
+ );
372
+ await Example.exampleTransaction(body);
373
+ }
374
+ }
375
+ ```
376
+
377
+ Or this:
378
+
379
+ ```javascript
380
+ class Example {
381
+ @SolidActions.step()
382
+ static async fetchBody() {
383
+ // Instead, make HTTP requests in steps
384
+ return await fetch('https://example.com').then((r) => r.text());
385
+ }
386
+
387
+ @SolidActions.workflow()
388
+ static async exampleWorkflow() {
389
+ const body = await Example.fetchBody();
390
+ await Example.exampleTransaction(body);
391
+ }
392
+ }
393
+ ```
394
+
395
+ ### Running Steps In Parallel
396
+
397
+ Initiating several concurrent steps in a workflow, followed by awaiting them with `Promise.allSettled`, is valid as long as the steps are started in a deterministic order. For example the following is allowed:
398
+
399
+ ```typescript
400
+ const results = await Promise.allSettled([step1('arg1'), step2('arg2'), step3('arg3'), step4('arg4')]);
401
+ ```
402
+
403
+ This is allowed because each step is started in a well-defined sequence before awaiting.
404
+
405
+ By contrast, the following is not allowed:
406
+
407
+ ```typescript
408
+ const results = await Promise.allSettled([
409
+ async () => {
410
+ await step1('arg1');
411
+ await step2('arg3');
412
+ },
413
+ async () => {
414
+ await step3('arg2');
415
+ await step4('arg4');
416
+ },
417
+ ]);
418
+ ```
419
+
420
+ Here, `step2` and `step4` may be started in either order since their execution depends on the relative time taken by `step1` and `step3`.
421
+
422
+ If you need to run sequences of operations concurrently, start child workflows with `startWorkflow` and await the results from their `WorkflowHandle`s.
423
+
424
+ Avoid using `Promise.all` because of how it handles errors and rejections. When any promise rejects, `Promise.all` immediately fails, leaving the other promises unresolved. If one of those later throws an unhandled exception, it can crash your Node.js process. Instead, prefer `Promise.allSettled`, which safely waits for all promises to complete and reports their outcomes.
425
+
426
+ ## Workflow Timeouts
427
+
428
+ You can set a timeout for a workflow by passing a `timeoutMS` argument to `SolidActions.startWorkflow`.
429
+ When the timeout expires, the workflow **and all its children** are cancelled.
430
+ Cancelling a workflow sets its status to `CANCELLED` and preempts its execution at the beginning of its next step.
431
+
432
+ Timeouts are **start-to-completion**: a workflow's timeout does not begin until the workflow starts execution.
433
+ Also, timeouts are **durable**: they are stored in the database and persist across restarts, so workflows can have very long timeouts.
434
+
435
+ Example syntax:
436
+
437
+ ```javascript
438
+ async function taskFunction(task) {
439
+ // ...
440
+ }
441
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction);
442
+
443
+ async function main() {
444
+ const task = ...
445
+ const timeout = ... // Timeout in milliseconds
446
+ const handle = await SolidActions.startWorkflow(taskWorkflow, {timeoutMS: timeout})(task);
447
+ }
448
+ ```
449
+
450
+ ## Durable Sleep
451
+
452
+ You can use `SolidActions.sleep()` to put your workflow to sleep for any period of time.
453
+ This sleep is **durable**&mdash;SolidActions saves the wakeup time in the database so that even if the workflow is interrupted and restarted multiple times while sleeping, it still wakes up on schedule.
454
+
455
+ Sleeping is useful for scheduling a workflow to run in the future (even days, weeks, or months from now).
456
+ For example:
457
+
458
+ ```javascript
459
+ @SolidActions.workflow()
460
+ static async exampleWorkflow(timeToSleep, task) {
461
+ await SolidActions.sleep(timeToSleep);
462
+ await runTask(task);
463
+ }
464
+ ```
465
+
466
+ ## Debouncing Workflows
467
+
468
+ You can create a `Debouncer` to debounce your workflows.
469
+ Debouncing delays workflow execution until some time has passed since the workflow has last been called.
470
+ This is useful for preventing wasted work when a workflow may be triggered multiple times in quick succession.
471
+ For example, if a user is editing an input field, you can debounce their changes to execute a processing workflow only after they haven't edited the field for some time:
472
+
473
+ ### Debouncer
474
+
475
+ ```typescript
476
+ new Debouncer<Args extends unknown[], Return>(
477
+ params: DebouncerConfig<Args, Return>
478
+ )
479
+ ```
480
+
481
+ ```typescript
482
+ interface DebouncerConfig<Args extends unknown[], Return> {
483
+ workflow: (...args: Args) => Promise<Return>;
484
+ startWorkflowParams?: StartWorkflowParams;
485
+ debounceTimeoutMs?: number;
486
+ }
487
+ ```
488
+
489
+ **Parameters:**
490
+
491
+ - **workflow**: The workflow to debounce. Note that workflows from configured instances cannot be debounced.
492
+ - **startWorkflowParams**: Optional workflow parameters, as in `startWorkflow`. Applied to all workflows started from this debouncer.
493
+ - **debounceTimeoutMs**: After this time elapses since the first time a workflow is submitted from this debouncer, the workflow is started regardless of the debounce period.
494
+
495
+ ### debouncer.debounce
496
+
497
+ ```typescript
498
+ debouncer.debounce(
499
+ debounceKey: string,
500
+ debouncePeriodMs: number,
501
+ ...args: Args
502
+ ): Promise<WorkflowHandle<Return>>
503
+ ```
504
+
505
+ Submit a workflow for execution but delay it by `debouncePeriodMs`.
506
+ Returns a handle to the workflow.
507
+ The workflow may be debounced again, which further delays its execution (up to `debounceTimeoutMs`).
508
+ When the workflow eventually executes, it uses the **last** set of inputs passed into `debounce`.
509
+ After the workflow begins execution, the next call to `debounce` starts the debouncing process again for a new workflow execution.
510
+
511
+ **Parameters:**
512
+
513
+ - **debounceKey**: A key used to group workflow executions that will be debounced together. For example, if the debounce key is set to customer ID, each customer's workflows would be debounced separately.
514
+ - **debouncePeriodMs**: Delay this workflow's execution by this period in milliseconds.
515
+ - **...args**: Variadic workflow arguments.
516
+
517
+ **Example Syntax**:
518
+
519
+ ```typescript
520
+ async function processInput(userInput: string) {
521
+ ...
522
+ }
523
+ const processInputWorkflow = SolidActions.registerWorkflow(processInput);
524
+
525
+ // Each time a user submits a new input, debounce the processInput workflow.
526
+ // The workflow will wait until 60 seconds after the user stops submitting new inputs,
527
+ // then process the last input submitted.
528
+ const debouncer = new Debouncer({
529
+ workflow: processInputWorkflow,
530
+ });
531
+
532
+ async function onUserInputSubmit(userId: string, userInput: string) {
533
+ const debounceKey = userId;
534
+ const debouncePeriodMs = 60000; // 60 seconds
535
+ await debouncer.debounce(debounceKey, debouncePeriodMs, userInput);
536
+ }
537
+ ```
538
+
539
+ ## Workflow Versioning and Recovery
540
+
541
+ Because SolidActions recovers workflows by re-executing them using information saved in the database, a workflow cannot safely be recovered if its code has changed since the workflow was started.
542
+ To guard against this, SolidActions _versions_ applications and their workflows.
543
+ When SolidActions is launched, it computes an application version from a hash of the source code of its workflows (this can be overridden through the `applicationVersion`) configuration parameter.
544
+ All workflows are tagged with the application version on which they started.
545
+
546
+ When SolidActions tries to recover workflows, it only recovers workflows whose version matches the current application version.
547
+ This prevents unsafe recovery of workflows that depend on different code.
548
+ You cannot change the version of a workflow, but you can use `SolidActions.forkWorkflow` to restart a workflow from a specific step on a specific code version.
549
+
550
+ ## Workflow Communication
551
+
552
+ SolidActions provides a few different ways to communicate with your workflows.
553
+ You can:
554
+
555
+ - Send messages to workflows
556
+ - Publish events from workflows for clients to read
557
+ - Stream values from workflows to clients
558
+
559
+ ## Workflow Messaging and Notifications
560
+
561
+ You can send messages to a specific workflow.
562
+ This is useful for signaling a workflow or sending notifications to it while it's running.
563
+
564
+ <img src={require('@site/static/img/workflow-communication/workflow-messages.png').default} alt="SolidActions Steps" width="750" className="custom-img"/>
565
+
566
+ ### Send
567
+
568
+ ```typescript
569
+ SolidActions.send<T>(destinationID: string, message: T, topic?: string): Promise<void>;
570
+ ```
571
+
572
+ You can call `SolidActions.send()` to send a message to a workflow.
573
+ Messages can optionally be associated with a topic and are queued on the receiver per topic.
574
+
575
+ You can also call `send` from outside of your SolidActions application with the SolidActions Client.
576
+
577
+ ### Recv
578
+
579
+ ```typescript
580
+ SolidActions.recv<T>(topic?: string, timeoutSeconds?: number): Promise<T | null>
581
+ ```
582
+
583
+ Workflows can call `SolidActions.recv()` to receive messages sent to them, optionally for a particular topic.
584
+ Each call to `recv()` waits for and consumes the next message to arrive in the queue for the specified topic, returning `null` if the wait times out.
585
+ If the topic is not specified, this method only receives messages sent without a topic.
586
+
587
+ ### Messages Example
588
+
589
+ Messages are especially useful for sending notifications to a workflow.
590
+ For example, in the e-commerce demo, the checkout workflow, after redirecting customers to a secure payments service, must wait for a notification from that service that the payment has finished processing.
591
+
592
+ To wait for this notification, the payments workflow uses `recv()`, executing failure-handling code if the notification doesn't arrive in time:
593
+
594
+ ```javascript
595
+ @SolidActions.workflow()
596
+ static async checkoutWorkflow(...): Promise<void> {
597
+ ...
598
+ const notification = await SolidActions.recv<string>(PAYMENT_STATUS, timeout);
599
+ if (notification) {
600
+ ... // Handle the notification.
601
+ } else {
602
+ ... // Handle a timeout.
603
+ }
604
+ }
605
+ ```
606
+
607
+ A webhook waits for the payment processor to send the notification, then uses `send()` to forward it to the workflow:
608
+
609
+ ```javascript
610
+ static async paymentWebhook(): Promise<void> {
611
+ const notificationMessage = ... // Parse the notification.
612
+ const workflowID = ... // Retrieve the workflow ID from notification metadata.
613
+ await SolidActions.send(workflowID, notificationMessage, PAYMENT_STATUS);
614
+ }
615
+ ```
616
+
617
+ ### Reliability Guarantees
618
+
619
+ All messages are persisted to the database, so if `send` completes successfully, the destination workflow is guaranteed to be able to `recv` it.
620
+ If you're sending a message from a workflow, SolidActions guarantees exactly-once delivery.
621
+ If you're sending a message from normal TypeScript code, you can specify an idempotency key for `send` to guarantee exactly-once delivery.
622
+
623
+ ## Workflow Events
624
+
625
+ Workflows can publish _events_, which are key-value pairs associated with the workflow.
626
+ They are useful for publishing information about the status of a workflow or to send a result to clients while the workflow is running.
627
+
628
+ <img src={require('@site/static/img/workflow-communication/workflow-events.png').default} alt="SolidActions Steps" width="750" className="custom-img"/>
629
+
630
+ ### setEvent
631
+
632
+ ```typescript
633
+ SolidActions.setEvent<T>(key: string, value: T): Promise<void>
634
+ ```
635
+
636
+ Any workflow can call `SolidActions.setEvent` to publish a key-value pair, or update its value if has already been published.
637
+
638
+ ### getEvent
639
+
640
+ ```typescript
641
+ SolidActions.getEvent<T>(workflowID: string, key: string, timeoutSeconds?: number): Promise<T | null>
642
+ ```
643
+
644
+ You can call `SolidActions.getEvent` to retrieve the value published by a particular workflow ID for a particular key.
645
+ If the event does not yet exist, this call waits for it to be published, returning `null` if the wait times out.
646
+
647
+ You can also call `getEvent` from outside of your SolidActions application with SolidActions Client.
648
+
649
+ ### Events Example
650
+
651
+ Events are especially useful for writing interactive workflows that communicate information to their caller.
652
+ For example, in the e-commerce demo, the checkout workflow, after validating an order, directs the customer to a secure payments service to handle credit card processing.
653
+ To communicate the payments URL to the customer, it uses events.
654
+
655
+ The checkout workflow emits the payments URL using `setEvent()`:
656
+
657
+ ```javascript
658
+ @SolidActions.workflow()
659
+ static async checkoutWorkflow(...): Promise<void> {
660
+ ...
661
+ const paymentsURL = ...
662
+ await SolidActions.setEvent(PAYMENT_URL, paymentsURL);
663
+ ...
664
+ }
665
+ ```
666
+
667
+ The HTTP handler that originally started the workflow uses `getEvent()` to await this URL, then redirects the customer to it:
668
+
669
+ ```javascript
670
+ static async webCheckout(...): Promise<void> {
671
+ const handle = await SolidActions.startWorkflow(Shop).checkoutWorkflow(...);
672
+ const url = await SolidActions.getEvent<string>(handle.workflowID, PAYMENT_URL);
673
+ if (url === null) {
674
+ SolidActions.koaContext.redirect(`${origin}/checkout/cancel`);
675
+ } else {
676
+ SolidActions.koaContext.redirect(url);
677
+ }
678
+ }
679
+ ```
680
+
681
+ ### Reliability Guarantees
682
+
683
+ All events are persisted to the database, so the latest version of an event is always retrievable.
684
+ Additionally, if `getEvent` is called in a workflow, the retrieved value is persisted in the database so workflow recovery can use that value, even if the event is later updated.
685
+
686
+ ## Workflow Streaming
687
+
688
+ Workflows can stream data in real time to clients.
689
+ This is useful for streaming results from a long-running workflow or LLM call or for monitoring or progress reporting.
690
+
691
+ <img src={require('@site/static/img/workflow-communication/workflow-streams.png').default} alt="SolidActions Steps" width="750" className="custom-img"/>
692
+
693
+ ### Writing to Streams
694
+
695
+ ```typescript
696
+ SolidActions.writeStream<T>(key: string, value: T): Promise<void>
697
+ ```
698
+
699
+ You can write values to a stream from a workflow or its steps using `SolidActions.writeStream`.
700
+ A workflow may have any number of streams, each identified by a unique key.
701
+
702
+ When you are done writing to a stream, you should close it with `SolidActions.closeStream`.
703
+ Otherwise, streams are automatically closed when the workflow terminates.
704
+
705
+ ```typescript
706
+ SolidActions.closeStream(key: string): Promise<void>
707
+ ```
708
+
709
+ SolidActions streams are immutable and append-only.
710
+ Writes to a stream from a workflow happen exactly-once.
711
+ Writes to a stream from a step happen at-least-once; if a step fails and is retried, it may write to the stream multiple times.
712
+ Readers will see all values written to the stream from all tries of the step in the order in which they were written.
713
+
714
+ **Example syntax:**
715
+
716
+ ```typescript
717
+ async function producerWorkflowFunction() {
718
+ await SolidActions.writeStream('example_key', { step: 1, data: 'value1' });
719
+ await SolidActions.writeStream('example_key', { step: 2, data: 'value2' });
720
+ await SolidActions.closeStream('example_key'); // Signal completion
721
+ }
722
+
723
+ const producerWorkflow = SolidActions.registerWorkflow(producerWorkflowFunction);
724
+ ```
725
+
726
+ ### Reading from Streams
727
+
728
+ ```typescript
729
+ SolidActions.readStream<T>(workflowID: string, key: string): AsyncGenerator<T, void, unknown>
730
+ ```
731
+
732
+ You can read values from a stream from anywhere using `SolidActions.readStream`.
733
+ This function reads values from a stream identified by a workflow ID and key, yielding each value in order until the stream is closed or the workflow terminates.
734
+
735
+ You can also read from a stream from outside a SolidActions application with a SolidActions Client.
736
+
737
+ **Example syntax:**
738
+
739
+ ```typescript
740
+ for await (const value of SolidActions.readStream(workflowID, 'example_key')) {
741
+ console.log(`Received: ${JSON.stringify(value)}`);
742
+ }
743
+ ```
744
+
745
+ ## Steps
746
+
747
+ When using SolidActions workflows, you should call any function that performs complex operations or accesses external APIs or services as a _step_.
748
+ If a workflow is interrupted, upon restart it automatically resumes execution from the **last completed step**.
749
+
750
+ You can use `SolidActions.runStep` to call a function as a step. For a function to be used as a step, it should have a return value that can be serialized as JSON, and should not have non-durable side effects. ALWAYS call steps this way unless otherwise specify.
751
+
752
+ ```javascript
753
+ async function generateRandomNumber() {
754
+ return Math.random();
755
+ }
756
+
757
+ async function workflowFunction() {
758
+ const randomNumber = await SolidActions.runStep(() => generateRandomNumber(), { name: 'generateRandomNumber' });
759
+ }
760
+ const workflow = SolidActions.registerWorkflow(workflowFunction);
761
+ ```
762
+
763
+ Alternatively, you can register a function as a step using `SolidActions.registerStep`:
764
+ NEVER do this unless specifically asked, ALWAYS use SolidActions.runStep instead.
765
+
766
+ ```javascript
767
+ async function generateRandomNumber() {
768
+ return Math.random();
769
+ }
770
+ const randomStep = SolidActions.registerStep(generateRandomNumber);
771
+
772
+ async function workflowFunction() {
773
+ const randomNumber = await randomStep();
774
+ }
775
+ const workflow = SolidActions.registerWorkflow(workflowFunction);
776
+ ```
777
+
778
+ Or use the `@SolidActions.step()` decorator:
779
+ NEVER do this unless specifically asked, ALWAYS use SolidActions.runStep instead.
780
+
781
+ ```typescript
782
+ export class Example {
783
+ @SolidActions.step()
784
+ static async generateRandomNumber() {
785
+ return Math.random();
786
+ }
787
+
788
+ @SolidActions.workflow()
789
+ static async exampleWorkflow() {
790
+ await Example.generateRandomNumber();
791
+ }
792
+ }
793
+ ```
794
+
795
+ ### Configurable Retries
796
+
797
+ You can optionally configure a step to automatically retry any exception a set number of times with exponential backoff.
798
+ This is useful for automatically handling transient failures, like making requests to unreliable APIs.
799
+ Retries are configurable through arguments to the step decorator:
800
+
801
+ ```typescript
802
+ export interface StepConfig {
803
+ retriesAllowed?: boolean; // Should failures be retried? (default false)
804
+ intervalSeconds?: number; // Seconds to wait before the first retry attempt (default 1).
805
+ maxAttempts?: number; // Maximum number of retry attempts (default 3). If errors occur more times than this, throw an exception.
806
+ backoffRate?: number; // Multiplier by which the retry interval increases after a retry attempt (default 2).
807
+ }
808
+ ```
809
+
810
+ For example, let's configure this step to retry exceptions (such as if `example.com` is temporarily down) up to 10 times:
811
+
812
+ ```javascript
813
+ async function fetchFunction() {
814
+ return await fetch('https://example.com').then((r) => r.text());
815
+ }
816
+
817
+ async function workflowFunction() {
818
+ const randomNumber = await SolidActions.runStep(() => fetchFunction(), {
819
+ name: 'fetchFunction',
820
+ retriesAllowed: true,
821
+ maxAttempts: 10,
822
+ });
823
+ }
824
+ ```
825
+
826
+ Or if registering the step:
827
+
828
+ ```javascript
829
+ async function fetchFunction() {
830
+ return await fetch('https://example.com').then((r) => r.text());
831
+ }
832
+ const fetchStep = SolidActions.registerStep(fetchFunction, {
833
+ retriesAllowed: true,
834
+ maxAttempts: 10,
835
+ });
836
+ ```
837
+
838
+ Or if using decorators:
839
+
840
+ ```javascript
841
+ @SolidActions.step({retriesAllowed: true, maxAttempts: 10})
842
+ static async exampleStep() {
843
+ return await fetch("https://example.com").then(r => r.text());
844
+ }
845
+ ```
846
+
847
+ ## Queues
848
+
849
+ You can use queues to run many workflows at once with managed concurrency.
850
+ Queues provide _flow control_, letting you manage how many workflows run at once or how often workflows are started.
851
+
852
+ To create a queue, specify its name:
853
+
854
+ ```javascript
855
+ import { SolidActions, WorkflowQueue } from '@dbos-inc/dbos-sdk';
856
+
857
+ const queue = new WorkflowQueue('example_queue');
858
+ ```
859
+
860
+ You can then enqueue any workflow by passing the queue as an argument to `SolidActions.startWorkflow`.
861
+ Enqueuing a function submits it for execution and returns a handle to it.
862
+ Queued tasks are started in first-in, first-out (FIFO) order.
863
+
864
+ ```javascript
865
+ const queue = new WorkflowQueue("example_queue");
866
+
867
+ class Tasks {
868
+ @SolidActions.workflow()
869
+ static async processTask(task) {
870
+ // ...
871
+ }
872
+ }
873
+
874
+ async function main() {
875
+ const task = ...
876
+ const handle = await SolidActions.startWorkflow(Tasks, {queueName: queue.name}).processTask(task)
877
+ }
878
+ ```
879
+
880
+ ### Queue Example
881
+
882
+ Here's an example of a workflow using a queue to process tasks in parallel:
883
+
884
+ ```javascript
885
+ import { SolidActions, WorkflowQueue } from '@dbos-inc/dbos-sdk';
886
+
887
+ const queue = new WorkflowQueue('example_queue');
888
+
889
+ async function taskFunction(task) {
890
+ // ...
891
+ }
892
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction, { name: 'taskWorkflow' });
893
+
894
+ async function queueFunction(tasks) {
895
+ const handles = [];
896
+
897
+ // Enqueue each task so all tasks are processed concurrently.
898
+ for (const task of tasks) {
899
+ handles.push(await SolidActions.startWorkflow(taskWorkflow, { queueName: queue.name })(task));
900
+ }
901
+
902
+ // Wait for each task to complete and retrieve its result.
903
+ // Return the results of all tasks.
904
+ const results = [];
905
+ for (const h of handles) {
906
+ results.push(await h.getResult());
907
+ }
908
+ return results;
909
+ }
910
+ const queueWorkflow = SolidActions.registerWorkflow(queueFunction, { name: 'queueWorkflow' });
911
+ ```
912
+
913
+ ### Enqueueing from Another Application
914
+
915
+ Often, you want to enqueue a workflow from outside your SolidActions application.
916
+ For example, let's say you have an API server and a data processing service.
917
+ You're using SolidActions to build a durable data pipeline in the data processing service.
918
+ When the API server receives a request, it should enqueue the data pipeline for execution on the data processing service.
919
+
920
+ You can use the SolidActions Client to enqueue workflows from outside your SolidActions application by connecting directly to your SolidActions application's system database.
921
+ Since the SolidActions Client is designed to be used from outside your SolidActions application, workflow and queue metadata must be specified explicitly.
922
+
923
+ For example, this code enqueues the `dataPipeline` workflow on the `pipelineQueue` queue with `task` as an argument.
924
+
925
+ ```ts
926
+ import { SolidActionsClient } from '@dbos-inc/dbos-sdk';
927
+
928
+ const client = await SolidActionsClient.create({ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL });
929
+
930
+ type ProcessTask = typeof Tasks.processTask;
931
+ await client.enqueue<ProcessTask>(
932
+ {
933
+ workflowName: 'dataPipeline',
934
+ queueName: 'pipelineQueue',
935
+ },
936
+ task,
937
+ );
938
+ ```
939
+
940
+ ### Managing Concurrency
941
+
942
+ You can control how many workflows from a queue run simultaneously by configuring concurrency limits.
943
+ This helps prevent resource exhaustion when workflows consume significant memory or processing power.
944
+
945
+ #### Worker Concurrency
946
+
947
+ Worker concurrency sets the maximum number of workflows from a queue that can run concurrently on a single SolidActions process.
948
+ This is particularly useful for resource-intensive workflows to avoid exhausting the resources of any process.
949
+ For example, this queue has a worker concurrency of 5, so each process will run at most 5 workflows from this queue simultaneously:
950
+
951
+ ```javascript
952
+ import { SolidActions, WorkflowQueue } from '@dbos-inc/dbos-sdk';
953
+
954
+ const queue = new WorkflowQueue('example_queue', { workerConcurrency: 5 });
955
+ ```
956
+
957
+ #### Global Concurrency
958
+
959
+ Global concurrency limits the total number of workflows from a queue that can run concurrently across all SolidActions processes in your application.
960
+ For example, this queue will have a maximum of 10 workflows running simultaneously across your entire application.
961
+
962
+ :::warning
963
+ Worker concurrency limits are recommended for most use cases.
964
+ Take care when using a global concurrency limit as any `PENDING` workflow on the queue counts toward the limit, including workflows from previous application versions
965
+ :::
966
+
967
+ ```javascript
968
+ import { SolidActions, WorkflowQueue } from '@dbos-inc/dbos-sdk';
969
+
970
+ const queue = new WorkflowQueue('example_queue', { concurrency: 10 });
971
+ ```
972
+
973
+ #### In-Order Processing
974
+
975
+ You can use a queue with `concurrency=1` to guarantee sequential, in-order processing of events.
976
+ Only a single event will be processed at a time.
977
+ For example, this app processes events sequentially in the order of their arrival:
978
+
979
+ ```javascript
980
+ import { SolidActions, WorkflowQueue } from '@dbos-inc/dbos-sdk';
981
+ import express from 'express';
982
+
983
+ const serialQueue = new WorkflowQueue('in_order_queue', { concurrency: 1 });
984
+ const app = express();
985
+
986
+ class Tasks {
987
+ @SolidActions.workflow()
988
+ static async processTask(task) {
989
+ // ... process task
990
+ }
991
+ }
992
+
993
+ app.get('/events/:event', async (req, res) => {
994
+ await SolidActions.startWorkflow(Tasks, { queueName: serialQueue.name }).processTask(req.params);
995
+ await res.send('Workflow Started!');
996
+ });
997
+
998
+ // Launch SolidActions and start the server
999
+ async function main() {
1000
+ await SolidActions.launch();
1001
+ app.listen(3000, () => {});
1002
+ }
1003
+
1004
+ main().catch(console.log);
1005
+ ```
1006
+
1007
+ ### Rate Limiting
1008
+
1009
+ You can set _rate limits_ for a queue, limiting the number of functions that it can start in a given period.
1010
+ Rate limits are global across all SolidActions processes using this queue.
1011
+ For example, this queue has a limit of 50 with a period of 30 seconds, so it may not start more than 50 functions in 30 seconds:
1012
+
1013
+ ```javascript
1014
+ const queue = new WorkflowQueue('example_queue', { rateLimit: { limitPerPeriod: 50, periodSec: 30 } });
1015
+ ```
1016
+
1017
+ Rate limits are especially useful when working with a rate-limited API, such as many LLM APIs.
1018
+
1019
+ ### Setting Timeouts
1020
+
1021
+ You can set a timeout for an enqueued workflow by passing a `timeoutMS` argument to `SolidActions.startWorkflow`.
1022
+ When the timeout expires, the workflow **and all its children** are cancelled.
1023
+ Cancelling a workflow sets its status to `CANCELLED` and preempts its execution at the beginning of its next step.
1024
+
1025
+ Timeouts are **start-to-completion**: a workflow's timeout does not begin until the workflow is dequeued and starts execution.
1026
+ Also, timeouts are **durable**: they are stored in the database and persist across restarts, so workflows can have very long timeouts.
1027
+
1028
+ Example syntax:
1029
+
1030
+ ```javascript
1031
+ const queue = new WorkflowQueue("example_queue");
1032
+
1033
+ async function taskFunction(task) {
1034
+ // ...
1035
+ }
1036
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction, {"name": "taskWorkflow"});
1037
+
1038
+ async function main() {
1039
+ const task = ...
1040
+ const timeout = ... // Timeout in milliseconds
1041
+ const handle = await SolidActions.startWorkflow(taskWorkflow, {queueName: queue.name, timeoutMS: timeout})(task);
1042
+ }
1043
+ ```
1044
+
1045
+ ### Partitioning Queues
1046
+
1047
+ You can **partition** queues to distribute work across dynamically created queue partitions.
1048
+ When you enqueue a workflow on a partitioned queue, you must supply a queue partition key.
1049
+ Partitioned queues dequeue workflows and apply flow control limits for individual partitions, not for the entire queue.
1050
+ Essentially, you can think of each partition as a "subqueue" you dynamically create by enqueueing a workflow with a partition key.
1051
+
1052
+ For example, suppose you want your users to each be able to run at most one task at a time.
1053
+ You can do this with a partitioned queue with a maximum concurrency limit of 1 where the partition key is user ID.
1054
+
1055
+ **Example Syntax**
1056
+
1057
+ ```ts
1058
+ const queue = new WorkflowQueue('example_queue', { partitionQueue: true, concurrency: 1 });
1059
+
1060
+ async function onUserTaskSubmission(userID: string, task: Task) {
1061
+ // Partition the task queue by user ID. As the queue has a
1062
+ // maximum concurrency of 1, this means that at most one
1063
+ // task can run at once per user (but tasks from different
1064
+ // users can run concurrently).
1065
+ await SolidActions.startWorkflow(taskWorkflow, {
1066
+ queueName: queue.name,
1067
+ enqueueOptions: { queuePartitionKey: userID },
1068
+ })(task);
1069
+ }
1070
+ ```
1071
+
1072
+ ### Deduplication
1073
+
1074
+ You can set a deduplication ID for an enqueued workflow as an argument to `SolidActions.startWorkflow`.
1075
+ At any given time, only one workflow with a specific deduplication ID can be enqueued in the specified queue.
1076
+ If a workflow with a deduplication ID is currently enqueued or actively executing (status `ENQUEUED` or `PENDING`), subsequent workflow enqueue attempt with the same deduplication ID in the same queue will raise a `SolidActionsQueueDuplicatedError` exception.
1077
+
1078
+ For example, this is useful if you only want to have one workflow active at a time per user&mdash;set the deduplication ID to the user's ID.
1079
+
1080
+ Example syntax:
1081
+
1082
+ ```javascript
1083
+ const queue = new WorkflowQueue("example_queue");
1084
+
1085
+ async function taskFunction(task) {
1086
+ // ...
1087
+ }
1088
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction, {"name": "taskWorkflow"});
1089
+
1090
+ async function main() {
1091
+ const task = ...
1092
+ const dedup: string = ...
1093
+ try {
1094
+ const handle = await SolidActions.startWorkflow(taskWorkflow, {queueName: queue.name, enqueueOptions: {deduplicationID: dedup}})(task);
1095
+ } catch (e) {
1096
+ // Handle SolidActionsQueueDuplicatedError
1097
+ }
1098
+ }
1099
+ ```
1100
+
1101
+ ### Priority
1102
+
1103
+ You can set a priority for an enqueued workflow as an argument to `SolidActions.startWorkflow`.
1104
+ Workflows with the same priority are dequeued in **FIFO (first in, first out)** order. Priority values can range from `1` to `2,147,483,647`, where **a low number indicates a higher priority**.
1105
+ If using priority, you must set `usePriority: true` on your queue.
1106
+
1107
+ :::tip
1108
+ Workflows without assigned priorities have the highest priority and are dequeued before workflows with assigned priorities.
1109
+ :::
1110
+
1111
+ Example syntax:
1112
+
1113
+ ```javascript
1114
+ const queue = new WorkflowQueue("example_queue", {usePriority: true});
1115
+
1116
+ async function taskFunction(task) {
1117
+ // ...
1118
+ }
1119
+ const taskWorkflow = SolidActions.registerWorkflow(taskFunction, {"name": "taskWorkflow"});
1120
+
1121
+ async function main() {
1122
+ const task = ...
1123
+ const priority: number = ...
1124
+ const handle = await SolidActions.startWorkflow(taskWorkflow, {queueName: queue.name, enqueueOptions: {priority: priority}})(task);
1125
+ }
1126
+ ```
1127
+
1128
+ ## Classes
1129
+
1130
+ You can use class instance methods as workflows and steps.
1131
+ Any class instance method can be freely used as a step using the SolidActions.step decorator or SolidActions.runstep; there are no special requirements.
1132
+ To use a class instance method as a workflow, you must use the SolidActions.workflow decorator and the class must inherit from `ConfiguredInstance`.
1133
+ For example:
1134
+
1135
+ ```typescript
1136
+ class MyClass extends ConfiguredInstance {
1137
+ cfg: MyConfig;
1138
+ constructor(name: string, config: MyConfig) {
1139
+ super(name);
1140
+ this.cfg = cfg;
1141
+ }
1142
+
1143
+ override async initialize(): Promise<void> {
1144
+ // ... Validate this.cfg; will be called at SolidActions.launch()
1145
+ }
1146
+
1147
+ @SolidActions.workflow()
1148
+ async testWorkflow(p: string): Promise<void> {
1149
+ // ... Operations that use this.cfg
1150
+ }
1151
+ }
1152
+
1153
+ const myClassInstance = new MyClass('instanceA');
1154
+ ```
1155
+
1156
+ When you create a new instance of such a class, the constructor for the base `ConfiguredInstance` must be called with a `name`.
1157
+ This `name` should be unique among instances of the same class.
1158
+ Additionally, all `ConfiguredInstance` classes must be instantiated before SolidActions.launch() is called.
1159
+
1160
+ The reason for these requirements is to enable workflow recovery. When you create a new instance of, SolidActions stores it in a global registry indexed by `name`. When SolidActions needs to recover a workflow belonging to that class, it looks up the `name` so it can run the workflow using the right class instance. While names are used by SolidActions Transact internally to find the correct object instance across system restarts, they are also potentially useful for monitoring, tracing, and debugging.
1161
+ You should AVOID using ConfiguredInstance if at all possible and instead use registerWorkflow on regular non-class functions.
1162
+
1163
+ ### Testing SolidActions Functions
1164
+
1165
+ Unless otherwise specified, recommend testing SolidActions applications with jest.
1166
+
1167
+ You MUST use this beforeAll to reset SolidActions between tests:
1168
+
1169
+ ```javascript
1170
+ beforeAll(async () => {
1171
+ SolidActions.setConfig({
1172
+ name: 'my-app',
1173
+ databaseUrl: process.env.SolidActions_TESTING_DATABASE_URL,
1174
+ });
1175
+ await SolidActions.launch();
1176
+ });
1177
+ ```
1178
+
1179
+ ### Logging
1180
+
1181
+ ALWAYS log errors like this:
1182
+
1183
+ ```typescript
1184
+ SolidActions.logger.error(`Error: ${(error as Error).message}`);
1185
+ ```
1186
+
1187
+ ## Workflow Handles
1188
+
1189
+ A workflow handle represents the state of a particular active or completed workflow execution.
1190
+ You obtain a workflow handle when using `SolidActions.startWorkflow` to start a workflow in the background.
1191
+ If you know a workflow's identity, you can also retrieve its handle using `SolidActions.retrieveWorkflow`.
1192
+
1193
+ Workflow handles have the following methods:
1194
+
1195
+ ### handle.workflowID
1196
+
1197
+ ```typescript
1198
+ handle.workflowID(): string;
1199
+ ```
1200
+
1201
+ Retrieve the ID of the workflow.
1202
+
1203
+ ### handle.getResult
1204
+
1205
+ ```typescript
1206
+ handle.getResult(): Promise<R>;
1207
+ ```
1208
+
1209
+ Wait for the workflow to complete, then return its result.
1210
+
1211
+ ### handle.getStatus
1212
+
1213
+ ```typescript
1214
+ handle.getStatus(): Promise<WorkflowStatus>;
1215
+ ```
1216
+
1217
+ Retrieve the WorkflowStatus of the workflow:
1218
+
1219
+ ### Workflow Status
1220
+
1221
+ Some workflow introspection and management methods return a `WorkflowStatus`.
1222
+ This object has the following definition:
1223
+
1224
+ ```typescript
1225
+ export interface WorkflowStatus {
1226
+ // The workflow ID
1227
+ readonly workflowID: string;
1228
+ // The workflow status. Must be one of ENQUEUED, PENDING, SUCCESS, ERROR, CANCELLED, or RETRIES_EXCEEDED
1229
+ readonly status: string;
1230
+ // The name of the workflow function.
1231
+ readonly workflowName: string;
1232
+ // The name of the workflow's class, if any
1233
+ readonly workflowClassName: string; // The class name holding the workflow function.
1234
+ // The name with which the workflow's class instance was configured, if any.
1235
+ readonly workflowConfigName?: string;
1236
+ // If the workflow was enqueued, the name of the queue.
1237
+ readonly queueName?: string;
1238
+ // The workflow's output, if any.
1239
+ readonly output?: unknown;
1240
+ // The error thrown by the workflow, if any.
1241
+ readonly error?: unknown;
1242
+ // The deserialized workflow inputs.
1243
+ readonly input?: unknown[];
1244
+ // The ID of the executor (process) that most recently executed this workflow.
1245
+ readonly executorId?: string;
1246
+ // The application version on which this workflow started.
1247
+ readonly applicationVersion?: string;
1248
+ // The number of times this workflow has been started.
1249
+ readonly recoveryAttempts?: number;
1250
+ // Workflow start time, as a UNIX epoch timestamp in milliseconds
1251
+ readonly createdAt: number;
1252
+ // Last time the workflow status was updated, as a UNIX epoch timestamp in milliseconds. For a completed workflow, this is the workflow completion timestamp.
1253
+ readonly updatedAt?: number;
1254
+ // The timeout specified for this workflow, if any. Timeouts are start-to-close.
1255
+ readonly timeoutMS?: number | null;
1256
+ // The deadline at which this workflow times out, if any. Not set until the workflow begins execution.
1257
+ readonly deadlineEpochMS?: number;
1258
+ }
1259
+ ```
1260
+
1261
+ ## SolidActions Variables
1262
+
1263
+ ### SolidActions.workflowID
1264
+
1265
+ ```typescript
1266
+ SolidActions.workflowID: string | undefined;
1267
+ ```
1268
+
1269
+ Return the ID of the current workflow, if in a workflow.
1270
+
1271
+ ### SolidActions.stepID
1272
+
1273
+ ```typescript
1274
+ SolidActions.stepID: string | undefined;
1275
+ ```
1276
+
1277
+ Return the unique ID of the current step within a workflow.
1278
+
1279
+ ### SolidActions.stepStatus
1280
+
1281
+ ```typescript
1282
+ SolidActions.stepStatus: StepStatus | undefined;
1283
+ ```
1284
+
1285
+ Return the status of the currently executing step.
1286
+ This object has the following properties:
1287
+
1288
+ ```typescript
1289
+ interface StepStatus {
1290
+ // The unique ID of this step in its workflow.
1291
+ stepID: number;
1292
+ // For steps with automatic retries, which attempt number (zero-indexed) is currently executing.
1293
+ currentAttempt?: number;
1294
+ // For steps with automatic retries, the maximum number of attempts that will be made before the step fails.
1295
+ maxAttempts?: number;
1296
+ }
1297
+ ```
1298
+
1299
+ ## Workflow Management Methods
1300
+
1301
+ ### SolidActions.listWorkflows
1302
+
1303
+ ```typescript
1304
+ SolidActions.listWorkflows(
1305
+ input: GetWorkflowsInput
1306
+ ): Promise<WorkflowStatus[]>
1307
+ ```
1308
+
1309
+ ```typescript
1310
+ interface GetWorkflowsInput {
1311
+ workflowIDs?: string[];
1312
+ workflowName?: string;
1313
+ status?: string;
1314
+ startTime?: string;
1315
+ endTime?: string;
1316
+ applicationVersion?: string;
1317
+ authenticatedUser?: string;
1318
+ limit?: number;
1319
+ offset?: number;
1320
+ sortDesc?: boolean;
1321
+ }
1322
+ ```
1323
+
1324
+ Retrieve a list of WorkflowStatus of all workflows matching specified criteria.
1325
+
1326
+ **Parameters:**
1327
+
1328
+ - **workflowIDs**: Retrieve workflows with these IDs.
1329
+ - **workflowName**: Retrieve workflows with this name.
1330
+ - **status**: Retrieve workflows with this status (Must be `ENQUEUED`, `PENDING`, `SUCCESS`, `ERROR`, `CANCELLED`, or `RETRIES_EXCEEDED`)
1331
+ - **startTime**: Retrieve workflows started after this (RFC 3339-compliant) timestamp.
1332
+ - **endTime**: Retrieve workflows started before this (RFC 3339-compliant) timestamp.
1333
+ - **applicationVersion**: Retrieve workflows tagged with this application version.
1334
+ - **authenticatedUser**: Retrieve workflows run by this authenticated user.
1335
+ - **limit**: Retrieve up to this many workflows.
1336
+ - **offset**: Skip this many workflows from the results returned (for pagination).
1337
+ - **sortDesc**: Whether to sort the results in descending (`True`) or ascending (`False`) order by workflow start time.
1338
+
1339
+ ### SolidActions.listQueuedWorkflows
1340
+
1341
+ ```typescript
1342
+ SolidActions.listQueuedWorkflows(
1343
+ input: GetQueuedWorkflowsInput
1344
+ ): Promise<WorkflowStatus[]>
1345
+ ```
1346
+
1347
+ ```typescript
1348
+ interface GetQueuedWorkflowsInput {
1349
+ workflowName?: string;
1350
+ status?: string;
1351
+ queueName?: number;
1352
+ startTime?: string;
1353
+ endTime?: string;
1354
+ limit?: number;
1355
+ offset?: number;
1356
+ sortDesc?: boolean;
1357
+ }
1358
+ ```
1359
+
1360
+ Retrieve a list of WorkflowStatus of all **currently enqueued** workflows matching specified criteria.
1361
+
1362
+ **Parameters:**
1363
+
1364
+ - **workflowName**: Retrieve workflows with this name.
1365
+ - **status**: Retrieve workflows with this status (Must be `ENQUEUED`, `PENDING`, `SUCCESS`, `ERROR`, `CANCELLED`, or `RETRIES_EXCEEDED`)
1366
+ - **queueName**: Retrieve workflows running on this queue.
1367
+ - **startTime**: Retrieve workflows started after this (RFC 3339-compliant) timestamp.
1368
+ - **endTime**: Retrieve workflows started before this (RFC 3339-compliant) timestamp.
1369
+ - **limit**: Retrieve up to this many workflows.
1370
+ - **offset**: Skip this many workflows from the results returned (for pagination).
1371
+ - **sortDesc**: Whether to sort the results in descending (`True`) or ascending (`False`) order by workflow start time.
1372
+
1373
+ ### SolidActions.listWorkflowSteps
1374
+
1375
+ ```typescript
1376
+ SolidActions.listWorkflowSteps(
1377
+ workflowID: string)
1378
+ : Promise<StepInfo[]>
1379
+ ```
1380
+
1381
+ Retrieve the steps of a workflow.
1382
+ This is a list of `StepInfo` objects, with the following structure:
1383
+
1384
+ ```typescript
1385
+ interface StepInfo {
1386
+ // The unique ID of the step in the workflow. Zero-indexed.
1387
+ readonly functionID: number;
1388
+ // The name of the step
1389
+ readonly name: string;
1390
+ // The step's output, if any
1391
+ readonly output: unknown;
1392
+ // The error the step threw, if any
1393
+ readonly error: Error | null;
1394
+ // If the step starts or retrieves the result of a workflow, its ID
1395
+ readonly childWorkflowID: string | null;
1396
+ }
1397
+ ```
1398
+
1399
+ ### SolidActions.cancelWorkflow
1400
+
1401
+ ```typescript
1402
+ cancelWorkflow(
1403
+ workflowID: string
1404
+ ): Promise<void>
1405
+ ```
1406
+
1407
+ Cancel a workflow.
1408
+ This sets is status to `CANCELLED`, removes it from its queue (if it is enqueued) and preempts its execution (interrupting it at the beginning of its next step)
1409
+
1410
+ ### SolidActions.resumeWorkflow
1411
+
1412
+ ```typescript
1413
+ SolidActions.resumeWorkflow<T>(
1414
+ workflowID: string
1415
+ ): Promise<WorkflowHandle<Awaited<T>>>
1416
+ ```
1417
+
1418
+ Resume a workflow.
1419
+ This immediately starts it from its last completed step.
1420
+ You can use this to resume workflows that are cancelled or have exceeded their maximum recovery attempts.
1421
+ You can also use this to start an enqueued workflow immediately, bypassing its queue.
1422
+
1423
+ ### SolidActions.forkWorkflow
1424
+
1425
+ ```typescript
1426
+ static async forkWorkflow<T>(
1427
+ workflowID: string,
1428
+ startStep: number,
1429
+ options?: { newWorkflowID?: string; applicationVersion?: string; timeoutMS?: number },
1430
+ ): Promise<WorkflowHandle<Awaited<T>>>
1431
+ ```
1432
+
1433
+ Start a new execution of a workflow from a specific step.
1434
+ The input step ID (`startStep`) must match the `functionID` of the step returned by `listWorkflowSteps`.
1435
+ The specified `startStep` is the step from which the new workflow will start, so any steps whose ID is less than `startStep` will not be re-executed.
1436
+
1437
+ **Parameters:**
1438
+
1439
+ - **workflowID**: The ID of the workflow to fork.
1440
+ - **startStep**: The ID of the step from which to start the forked workflow. Must match the `functionID` of the step in the original workflow execution.
1441
+ - **newWorkflowID**: The ID of the new workflow created by the fork. If not specified, a random UUID is used.
1442
+ - **applicationVersion**: The application version on which the forked workflow will run. Useful for "patching" workflows that failed due to a bug in the previous application version.
1443
+ - **timeoutMS**: A timeout for the forked workflow in milliseconds.
1444
+
1445
+ ## Configuring SolidActions
1446
+
1447
+ To configure SolidActions, pass in a configuration with `SolidActions.setConfig` before you call `SolidActions.launch`.
1448
+ For example:
1449
+
1450
+ ```javascript
1451
+ SolidActions.setConfig({
1452
+ name: 'my-app',
1453
+ systemDatabaseUrl: process.env.SolidActions_SYSTEM_DATABASE_URL,
1454
+ });
1455
+ await SolidActions.launch();
1456
+ ```
1457
+
1458
+ A configuration object has the following fields.
1459
+ All fields except `name` are optional.
1460
+
1461
+ ```javascript
1462
+ export interface SolidActionsConfig {
1463
+ name?: string;
1464
+
1465
+ systemDatabaseUrl?: string;
1466
+ systemDatabasePoolSize?: number;
1467
+ systemDatabasePool?: Pool;
1468
+
1469
+ enableOTLP?: boolean;
1470
+ logLevel?: string;
1471
+ otlpLogsEndpoints?: string[];
1472
+ otlpTracesEndpoints?: string[];
1473
+
1474
+ runAdminServer?: boolean;
1475
+ adminPort?: number;
1476
+
1477
+ applicationVersion?: string;
1478
+ }
1479
+ ```
1480
+
1481
+ - **name**: Your application's name.
1482
+ - **systemDatabaseUrl**: A connection string to a Postgres database in which SolidActions can store internal state. The supported format is:
1483
+
1484
+ ```
1485
+ postgresql://[username]:[password]@[hostname]:[port]/[database name]
1486
+ ```
1487
+
1488
+ The default is:
1489
+
1490
+ ```
1491
+ postgresql://postgres:dbos@localhost:5432/[application name]_dbos_sys
1492
+ ```
1493
+
1494
+ If the Postgres database referenced by this connection string does not exist, SolidActions will attempt to create it.
1495
+
1496
+ - **systemDatabasePoolSize**: The size of the connection pool used for the SolidActions system database. Defaults to 10.
1497
+ - **systemDatabasePool**: A custom `node-postgres` connection pool to use to connect to your system database. If provided, SolidActions will not create a connection pool but use this instead.
1498
+ - **enableOTLP**: Enable SolidActions OpenTelemetry tracing and export. Defaults to False.
1499
+ - **logLevel**: Configure the SolidActions logger severity. Defaults to `info`.
1500
+ - **otlpTracesEndpoints**: SolidActions operations automatically generate OpenTelemetry Traces. Use this field to declare a list of OTLP-compatible receivers.
1501
+ - **otlpLogsEndpoints**: SolidActions operations automatically generate OpenTelemetry Logs. Use this field to declare a list of OTLP-compatible receivers.
1502
+ - **runAdminServer**: Whether to run an HTTP admin server for workflow management operations. Defaults to True.
1503
+ - **adminPort**: The port on which the admin server runs. Defaults to 3001.
1504
+ - **applicationVersion**: The code version for this application and its workflows.