@solidactions/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +25 -4
  2. package/dist/src/cli/cli.js +0 -0
  3. package/dist/src/testing/index.d.ts +13 -0
  4. package/dist/src/testing/index.d.ts.map +1 -0
  5. package/dist/src/testing/index.js +19 -0
  6. package/dist/src/testing/index.js.map +1 -0
  7. package/dist/src/testing/mock_server.d.ts +134 -0
  8. package/dist/src/testing/mock_server.d.ts.map +1 -0
  9. package/dist/src/testing/mock_server.js +613 -0
  10. package/dist/src/testing/mock_server.js.map +1 -0
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/docs/sdk-reference.md +1207 -0
  13. package/package.json +6 -1
  14. package/.claude/settings.local.json +0 -7
  15. package/dist/dbos-config.schema.json +0 -132
  16. package/dist/src/cli/commands.d.ts +0 -3
  17. package/dist/src/cli/commands.d.ts.map +0 -1
  18. package/dist/src/cli/commands.js +0 -46
  19. package/dist/src/cli/commands.js.map +0 -1
  20. package/dist/src/datasource.d.ts +0 -109
  21. package/dist/src/datasource.d.ts.map +0 -1
  22. package/dist/src/datasource.js +0 -204
  23. package/dist/src/datasource.js.map +0 -1
  24. package/dist/src/dbos-executor.d.ts +0 -189
  25. package/dist/src/dbos-executor.d.ts.map +0 -1
  26. package/dist/src/dbos-executor.js +0 -817
  27. package/dist/src/dbos-executor.js.map +0 -1
  28. package/dist/src/dbos.d.ts +0 -519
  29. package/dist/src/dbos.d.ts.map +0 -1
  30. package/dist/src/dbos.js +0 -1282
  31. package/dist/src/dbos.js.map +0 -1
  32. package/dist/src/debouncer.d.ts +0 -33
  33. package/dist/src/debouncer.d.ts.map +0 -1
  34. package/dist/src/debouncer.js +0 -170
  35. package/dist/src/debouncer.js.map +0 -1
  36. package/dist/src/scheduler/crontab.d.ts +0 -14
  37. package/dist/src/scheduler/crontab.d.ts.map +0 -1
  38. package/dist/src/scheduler/crontab.js +0 -308
  39. package/dist/src/scheduler/crontab.js.map +0 -1
  40. package/dist/src/scheduler/scheduler.d.ts +0 -41
  41. package/dist/src/scheduler/scheduler.d.ts.map +0 -1
  42. package/dist/src/scheduler/scheduler.js +0 -165
  43. package/dist/src/scheduler/scheduler.js.map +0 -1
  44. package/dist/src/wfqueue.d.ts +0 -64
  45. package/dist/src/wfqueue.d.ts.map +0 -1
  46. package/dist/src/wfqueue.js +0 -147
  47. package/dist/src/wfqueue.js.map +0 -1
  48. package/docs/api-schema.md +0 -1441
  49. package/docs/migration-guide.md +0 -460
  50. package/docs/phase-14-changes.md +0 -156
  51. package/docs/solidsteps-ai-prompt.md +0 -534
  52. package/solidactions-ai-prompt.md +0 -1504
package/dist/src/dbos.js DELETED
@@ -1,1282 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DBOS = exports.runInternalStep = exports.getExecutor = void 0;
7
- const context_1 = require("./context");
8
- const dbos_executor_1 = require("./dbos-executor");
9
- const traces_1 = require("./telemetry/traces");
10
- const workflow_1 = require("./workflow");
11
- const logs_1 = require("./telemetry/logs");
12
- const error_1 = require("./error");
13
- const config_1 = require("./config");
14
- const decorators_1 = require("./decorators");
15
- const utils_1 = require("./utils");
16
- const serialization_1 = require("./serialization");
17
- const adminserver_1 = require("./adminserver");
18
- const node_crypto_1 = require("node:crypto");
19
- const conductor_1 = require("./conductor/conductor");
20
- const system_database_1 = require("./system_database");
21
- const authdecorators_1 = require("./authdecorators");
22
- const node_assert_1 = __importDefault(require("node:assert"));
23
- function getExecutor() {
24
- if (!dbos_executor_1.SolidActionsExecutor.globalInstance) {
25
- throw new error_1.DBOSExecutorNotInitializedError();
26
- }
27
- return dbos_executor_1.SolidActionsExecutor.globalInstance;
28
- }
29
- exports.getExecutor = getExecutor;
30
- function runInternalStep(callback, funcName, childWFID, assignedFuncID) {
31
- if (DBOS.isWithinWorkflow()) {
32
- if (DBOS.isInStep()) {
33
- // OK to use directly
34
- return callback();
35
- }
36
- else if (DBOS.isInWorkflow()) {
37
- return dbos_executor_1.SolidActionsExecutor.globalInstance.runInternalStep(callback, funcName, DBOS.workflowID, assignedFuncID ?? (0, context_1.functionIDGetIncrement)(), childWFID);
38
- }
39
- else {
40
- throw new error_1.DBOSInvalidWorkflowTransitionError(`Invalid call to \`${funcName}\` inside a \`transaction\` or \`procedure\``);
41
- }
42
- }
43
- return callback();
44
- }
45
- exports.runInternalStep = runInternalStep;
46
- class DBOS {
47
- ///////
48
- // Lifecycle
49
- ///////
50
- static adminServer = undefined;
51
- static conductor = undefined;
52
- /**
53
- * Set configuration of `SolidActions` prior to `launch`
54
- * @param config - configuration of services needed by SolidActions
55
- */
56
- static setConfig(config) {
57
- (0, node_assert_1.default)(!DBOS.isInitialized(), 'Cannot call DBOS.setConfig after DBOS.launch');
58
- DBOS.#dbosConfig = config;
59
- }
60
- /**
61
- * Check if DBOS has been `launch`ed (and not `shutdown`)
62
- * @returns `true` if DBOS has been launched, or `false` otherwise
63
- */
64
- static isInitialized() {
65
- return !!dbos_executor_1.SolidActionsExecutor.globalInstance?.initialized;
66
- }
67
- /**
68
- * Launch DBOS, starting recovery and request handling
69
- * @param options - Launch options for connecting to DBOS Conductor
70
- */
71
- static async launch(options) {
72
- const debugMode = options?.debugMode ?? process.env.DBOS_DEBUG_WORKFLOW_ID !== undefined;
73
- const configFile = await (0, config_1.readConfigFile)();
74
- // If no setConfig() was called, try to auto-configure from solidsteps.yaml + env vars
75
- if (!DBOS.#dbosConfig) {
76
- const solidStepsConfig = await (0, config_1.readSolidStepsConfig)();
77
- if (solidStepsConfig.project || process.env.DBOS_API_URL) {
78
- // Auto-configure: use project name from solidsteps.yaml, API config from env vars
79
- try {
80
- const httpConfig = (0, config_1.getHttpConfig)(configFile);
81
- DBOS.#dbosConfig = {
82
- name: solidStepsConfig.project || configFile.name,
83
- api: {
84
- url: httpConfig.apiUrl,
85
- key: httpConfig.apiKey,
86
- timeout: httpConfig.timeout,
87
- maxRetries: httpConfig.maxRetries,
88
- },
89
- };
90
- }
91
- catch {
92
- // If we can't get HTTP config, fall back to normal config file handling
93
- }
94
- }
95
- }
96
- let internalConfig = DBOS.#dbosConfig
97
- ? (0, config_1.translateSolidActionsConfig)(DBOS.#dbosConfig, debugMode)
98
- : (0, config_1.getSolidActionsConfig)(configFile);
99
- let runtimeConfig = DBOS.#dbosConfig ? (0, config_1.translateRuntimeConfig)(DBOS.#dbosConfig) : (0, config_1.getRuntimeConfig)(configFile);
100
- if (process.env.DBOS__CLOUD === 'true' || process.env.SOLIDACTIONS__CLOUD === 'true') {
101
- [internalConfig, runtimeConfig] = (0, config_1.overwriteConfigForCloud)(internalConfig, runtimeConfig, configFile);
102
- }
103
- utils_1.globalParams.enableOTLP = DBOS.#dbosConfig?.enableOTLP ?? (0, utils_1.defaultEnableOTLP)();
104
- if (!(0, traces_1.isTraceContextWorking)())
105
- (0, traces_1.installTraceContextManager)(internalConfig.name);
106
- // Do nothing if DBOS is already initialized
107
- if (DBOS.isInitialized()) {
108
- return;
109
- }
110
- (0, decorators_1.finalizeClassRegistrations)();
111
- (0, decorators_1.insertAllMiddleware)();
112
- // Globally set the application version and executor ID.
113
- // In DBOS Cloud, instead use the value supplied through environment variables.
114
- if (process.env.DBOS__CLOUD !== 'true') {
115
- if (DBOS.#dbosConfig?.applicationVersion) {
116
- utils_1.globalParams.appVersion = DBOS.#dbosConfig.applicationVersion;
117
- }
118
- else if (DBOS.#dbosConfig?.enablePatching) {
119
- utils_1.globalParams.appVersion = 'PATCHING_ENABLED';
120
- }
121
- if (DBOS.#dbosConfig?.executorID) {
122
- utils_1.globalParams.executorID = DBOS.#dbosConfig.executorID;
123
- }
124
- }
125
- if (options?.conductorKey) {
126
- // Always use a generated executor ID in Conductor.
127
- utils_1.globalParams.executorID = (0, node_crypto_1.randomUUID)();
128
- }
129
- dbos_executor_1.SolidActionsExecutor.globalInstance = new dbos_executor_1.SolidActionsExecutor(internalConfig, { debugMode });
130
- (0, decorators_1.recordDBOSLaunch)();
131
- const executor = dbos_executor_1.SolidActionsExecutor.globalInstance;
132
- await executor.init();
133
- const debugWorkflowId = process.env.DBOS_DEBUG_WORKFLOW_ID;
134
- if (debugWorkflowId) {
135
- DBOS.logger.info(`Debugging workflow "${debugWorkflowId}"`);
136
- const handle = await executor.executeWorkflowId(debugWorkflowId);
137
- await handle.getResult();
138
- DBOS.logger.info(`Workflow Debugging complete. Exiting process.`);
139
- await executor.destroy();
140
- process.exit(0);
141
- return; // return for cases where process.exit is mocked
142
- }
143
- await dbos_executor_1.SolidActionsExecutor.globalInstance.initEventReceivers();
144
- if (options?.conductorKey) {
145
- if (!options.conductorURL) {
146
- const dbosDomain = process.env.DBOS_DOMAIN || 'cloud.dbos.dev';
147
- options.conductorURL = `wss://${dbosDomain}/conductor/v1alpha1`;
148
- }
149
- DBOS.conductor = new conductor_1.Conductor(dbos_executor_1.SolidActionsExecutor.globalInstance, options.conductorKey, options.conductorURL);
150
- DBOS.conductor.dispatchLoop();
151
- }
152
- // Start the DBOS admin server
153
- const logger = DBOS.logger;
154
- if (runtimeConfig.runAdminServer) {
155
- const adminApp = adminserver_1.DBOSAdminServer.setupAdminApp(executor);
156
- try {
157
- await adminserver_1.DBOSAdminServer.checkPortAvailabilityIPv4Ipv6(runtimeConfig.admin_port, logger);
158
- // Wrap the listen call in a promise to properly catch errors
159
- DBOS.adminServer = await new Promise((resolve, reject) => {
160
- const server = adminApp.listen(runtimeConfig?.admin_port, () => {
161
- DBOS.logger.debug(`DBOS Admin Server is running at http://localhost:${runtimeConfig?.admin_port}`);
162
- resolve(server);
163
- });
164
- server.on('error', (err) => {
165
- reject(err);
166
- });
167
- });
168
- }
169
- catch (e) {
170
- logger.warn(`Unable to start DBOS admin server on port ${runtimeConfig.admin_port}`);
171
- }
172
- }
173
- }
174
- /**
175
- * Logs all workflows that can be invoked externally.
176
- */
177
- static logRegisteredEndpoints() {
178
- if (!dbos_executor_1.SolidActionsExecutor.globalInstance)
179
- return;
180
- for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
181
- lcl.logRegisteredEndpoints?.();
182
- }
183
- }
184
- /**
185
- * Shut down DBOS processing:
186
- * Stops receiving external workflow requests
187
- * Disconnects from administration / Conductor
188
- * Stops workflow processing and disconnects from databases
189
- */
190
- static async shutdown() {
191
- // Stop the admin server
192
- if (DBOS.adminServer) {
193
- DBOS.adminServer.close();
194
- DBOS.adminServer = undefined;
195
- }
196
- // Stop the conductor
197
- if (DBOS.conductor) {
198
- DBOS.conductor.stop();
199
- while (!DBOS.conductor.isClosed) {
200
- await (0, utils_1.sleepms)(500);
201
- }
202
- DBOS.conductor = undefined;
203
- }
204
- // Stop the executor
205
- if (dbos_executor_1.SolidActionsExecutor.globalInstance) {
206
- await dbos_executor_1.SolidActionsExecutor.globalInstance.deactivateEventReceivers();
207
- await dbos_executor_1.SolidActionsExecutor.globalInstance.destroy();
208
- dbos_executor_1.SolidActionsExecutor.globalInstance = undefined;
209
- }
210
- // Reset the global app version and executor ID
211
- utils_1.globalParams.appVersion = process.env.DBOS__APPVERSION || '';
212
- utils_1.globalParams.wasComputed = false;
213
- utils_1.globalParams.appID = process.env.DBOS__APPID || '';
214
- utils_1.globalParams.executorID = process.env.DBOS__VMID || 'local';
215
- (0, decorators_1.recordDBOSShutdown)();
216
- }
217
- //////
218
- // Convenience APIs for SolidSteps workflows
219
- //////
220
- /**
221
- * Get the workflow input from WORKFLOW_INPUT environment variable.
222
- * This is set by the SolidSteps runner from the webhook payload.
223
- * @returns Parsed input or empty object if not set
224
- */
225
- static getInput() {
226
- const raw = process.env.WORKFLOW_INPUT;
227
- if (!raw) {
228
- return {};
229
- }
230
- try {
231
- return JSON.parse(raw);
232
- }
233
- catch {
234
- return {};
235
- }
236
- }
237
- /**
238
- * Run a workflow with minimal boilerplate.
239
- * Handles launch(), startWorkflow(), getResult(), and shutdown().
240
- *
241
- * @param workflow - The workflow function to run
242
- * @param options - Optional settings for input and workflow ID
243
- * @returns Promise that resolves when workflow completes (or rejects on error)
244
- *
245
- * @example
246
- * ```typescript
247
- * async function myWorkflow(input: MyInput): Promise<MyOutput> {
248
- * // workflow logic
249
- * }
250
- *
251
- * const wf = DBOS.registerWorkflow(myWorkflow, { name: 'my-workflow' });
252
- * await DBOS.run(wf);
253
- * ```
254
- */
255
- static async run(workflow, options) {
256
- try {
257
- await DBOS.launch();
258
- const input = options?.input ?? DBOS.getInput();
259
- const handle = await DBOS.startWorkflow(workflow, {
260
- workflowID: options?.workflowID,
261
- })(input);
262
- await handle.getResult();
263
- await DBOS.shutdown();
264
- process.exit(0);
265
- }
266
- catch (error) {
267
- console.error('Workflow failed:', error);
268
- try {
269
- await DBOS.shutdown();
270
- }
271
- catch {
272
- // Ignore shutdown errors
273
- }
274
- process.exit(1);
275
- }
276
- }
277
- /**
278
- * Interface for signal URLs returned by getSignalUrls()
279
- */
280
- static getSignalUrls(topic) {
281
- const baseApiUrl = process.env.SOLIDACTIONS_API_URL?.replace('/api/internal', '') ||
282
- process.env.DBOS_API_URL?.replace('/api/internal', '') ||
283
- process.env.APP_URL ||
284
- 'http://localhost:8000';
285
- const workflowId = DBOS.workflowID;
286
- if (!workflowId) {
287
- throw new error_1.DBOSError('getSignalUrls() must be called from within a workflow');
288
- }
289
- const topicParam = topic ? `&topic=${encodeURIComponent(topic)}` : '';
290
- const base = `${baseApiUrl}/api/signal/${workflowId}`;
291
- return {
292
- base,
293
- approve: `${base}?choice=approve${topicParam}`,
294
- reject: `${base}?choice=reject${topicParam}`,
295
- custom: (action) => `${base}?choice=${encodeURIComponent(action)}${topicParam}`,
296
- };
297
- }
298
- /** Stop listening for external events (for testing) */
299
- static async deactivateEventReceivers() {
300
- return dbos_executor_1.SolidActionsExecutor.globalInstance?.deactivateEventReceivers();
301
- }
302
- /** Start listening for external events (for testing) */
303
- static async initEventReceivers() {
304
- return dbos_executor_1.SolidActionsExecutor.globalInstance?.initEventReceivers();
305
- }
306
- // Global DBOS executor instance
307
- static get #executor() {
308
- return getExecutor();
309
- }
310
- //////
311
- // Globals
312
- //////
313
- static #dbosConfig;
314
- //////
315
- // Context
316
- //////
317
- /** Get the current DBOS Logger, appropriate to the current context */
318
- static get logger() {
319
- const lctx = (0, context_1.getCurrentContextStore)();
320
- if (lctx?.logger)
321
- return lctx.logger;
322
- const executor = dbos_executor_1.SolidActionsExecutor.globalInstance;
323
- if (executor)
324
- return executor.logger;
325
- return new logs_1.GlobalLogger();
326
- }
327
- /** Get the current DBOS Tracer, for starting spans */
328
- static get tracer() {
329
- const executor = dbos_executor_1.SolidActionsExecutor.globalInstance;
330
- if (executor)
331
- return executor.tracer;
332
- }
333
- /** Get the current DBOS tracing span, appropriate to the current context */
334
- static get span() {
335
- return (0, traces_1.getActiveSpan)();
336
- }
337
- /**
338
- * Get the current request object (such as an HTTP request)
339
- * This is intended for use in event libraries that know the type of the current request,
340
- * and set it using `withTracedContext` or `runWithContext`
341
- */
342
- static requestObject() {
343
- return (0, context_1.getCurrentContextStore)()?.request;
344
- }
345
- /** Get the current HTTP request (within `@DBOS.getApi` et al) */
346
- static getRequest() {
347
- return this.requestObject();
348
- }
349
- /** Get the current HTTP request (within `@DBOS.getApi` et al) */
350
- static get request() {
351
- const r = DBOS.getRequest();
352
- if (!r)
353
- throw new error_1.DBOSError('`DBOS.request` accessed from outside of HTTP requests');
354
- return r;
355
- }
356
- /** Get the current application version */
357
- static get applicationVersion() {
358
- return utils_1.globalParams.appVersion;
359
- }
360
- /** Get the current workflow ID */
361
- static get workflowID() {
362
- return (0, context_1.getCurrentContextStore)()?.workflowId;
363
- }
364
- /**
365
- * Get the current run ID (alias for workflowID).
366
- * This is the unique identifier assigned by SolidSteps at trigger time.
367
- */
368
- static get runID() {
369
- return DBOS.workflowID;
370
- }
371
- /** Get the current step number, within the current workflow */
372
- static get stepID() {
373
- if (DBOS.isInStep()) {
374
- return (0, context_1.getCurrentContextStore)()?.curStepFunctionId;
375
- }
376
- else if (DBOS.isInTransaction()) {
377
- return (0, context_1.getCurrentContextStore)()?.curTxFunctionId;
378
- }
379
- else {
380
- return undefined;
381
- }
382
- }
383
- static get stepStatus() {
384
- return (0, context_1.getCurrentContextStore)()?.stepStatus;
385
- }
386
- /** Get the current authenticated user */
387
- static get authenticatedUser() {
388
- return (0, context_1.getCurrentContextStore)()?.authenticatedUser ?? '';
389
- }
390
- /** Get the roles granted to the current authenticated user */
391
- static get authenticatedRoles() {
392
- return (0, context_1.getCurrentContextStore)()?.authenticatedRoles ?? [];
393
- }
394
- /** Get the role assumed by the current user giving authorization to execute the current function */
395
- static get assumedRole() {
396
- return (0, context_1.getCurrentContextStore)()?.assumedRole ?? '';
397
- }
398
- /** @returns true if called from within a transaction, false otherwise */
399
- static isInTransaction() {
400
- return (0, context_1.getCurrentContextStore)()?.curTxFunctionId !== undefined;
401
- }
402
- /** @returns true if called from within a step, false otherwise */
403
- static isInStep() {
404
- return (0, context_1.getCurrentContextStore)()?.curStepFunctionId !== undefined;
405
- }
406
- /**
407
- * @returns true if called from within a workflow
408
- * (regardless of whether the workflow is currently executing a step,
409
- * transaction, or procedure), false otherwise
410
- */
411
- static isWithinWorkflow() {
412
- return (0, context_1.getCurrentContextStore)()?.workflowId !== undefined;
413
- }
414
- /**
415
- * @returns true if called from within a workflow that is not currently executing
416
- * a step, transaction, or procedure, or false otherwise
417
- */
418
- static isInWorkflow() {
419
- return DBOS.isWithinWorkflow() && !DBOS.isInTransaction() && !DBOS.isInStep();
420
- }
421
- //////
422
- // Access to system DB, for event receivers etc.
423
- //////
424
- /**
425
- * Get a state item from the system database, which provides a key/value store interface for event dispatchers.
426
- * The full key for the database state should include the service, function, and item.
427
- * Values are versioned. A version can either be a sequence number (long integer), or a time (high precision floating point).
428
- * If versions are in use, any upsert is discarded if the version field is less than what is already stored.
429
- *
430
- * Examples of state that could be kept:
431
- * Offsets into kafka topics, per topic partition
432
- * Last time for which a scheduling service completed schedule dispatch
433
- *
434
- * @param service - should be unique to the event receiver keeping state, to separate from others
435
- * @param workflowFnName - function name; should be the fully qualified / unique function name dispatched
436
- * @param key - The subitem kept by event receiver service for the function, allowing multiple values to be stored per function
437
- * @returns The latest system database state for the specified service+workflow+item
438
- */
439
- static async getEventDispatchState(svc, wfn, key) {
440
- (0, decorators_1.ensureDBOSIsLaunched)('getEventDispatchState');
441
- return await DBOS.#executor.getEventDispatchState(svc, wfn, key);
442
- }
443
- /**
444
- * Set a state item into the system database, which provides a key/value store interface for event dispatchers.
445
- * The full key for the database state should include the service, function, and item; these fields are part of `state`.
446
- * Values are versioned. A version can either be a sequence number (long integer), or a time (high precision floating point).
447
- * If versions are in use, any upsert is discarded if the version field is less than what is already stored.
448
- *
449
- * @param state - the service, workflow, item, version, and value to write to the database
450
- * @returns The upsert returns the current record, which may be useful if it is more recent than the `state` provided.
451
- */
452
- static async upsertEventDispatchState(state) {
453
- (0, decorators_1.ensureDBOSIsLaunched)('upsertEventDispatchState');
454
- return await DBOS.#executor.upsertEventDispatchState(state);
455
- }
456
- //////
457
- // Workflow and other operations
458
- //////
459
- /**
460
- * Get the workflow status given a workflow ID
461
- * @param workflowID - ID of the workflow
462
- * @returns status of the workflow as `WorkflowStatus`, or `null` if there is no workflow with `workflowID`
463
- */
464
- static getWorkflowStatus(workflowID) {
465
- (0, decorators_1.ensureDBOSIsLaunched)('getWorkflowStatus');
466
- if (DBOS.isWithinWorkflow()) {
467
- if (DBOS.isInStep()) {
468
- // OK to use directly
469
- return DBOS.#executor.getWorkflowStatus(workflowID);
470
- }
471
- else if (DBOS.isInWorkflow()) {
472
- return DBOS.#executor.getWorkflowStatus(workflowID, DBOS.workflowID, (0, context_1.functionIDGetIncrement)());
473
- }
474
- else {
475
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `getWorkflowStatus` inside a `transaction` or `procedure`');
476
- }
477
- }
478
- return DBOS.#executor.getWorkflowStatus(workflowID);
479
- }
480
- /**
481
- * Get the workflow result, given a workflow ID
482
- * @param workflowID - ID of the workflow
483
- * @param timeoutSeconds - Maximum time to wait for result
484
- * @returns The return value of the workflow, or throws the exception thrown by the workflow, or `null` if times out
485
- */
486
- static async getResult(workflowID, timeoutSeconds) {
487
- (0, decorators_1.ensureDBOSIsLaunched)('getResult');
488
- let timerFuncID = undefined;
489
- if (DBOS.isWithinWorkflow() && timeoutSeconds !== undefined) {
490
- timerFuncID = (0, context_1.functionIDGetIncrement)();
491
- }
492
- return await DBOS.getResultInternal(workflowID, timeoutSeconds, timerFuncID, undefined);
493
- }
494
- static async getResultInternal(workflowID, timeoutSeconds, timerFuncID, assignedFuncID) {
495
- return await runInternalStep(async () => {
496
- const rres = await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.awaitWorkflowResult(workflowID, timeoutSeconds, DBOS.workflowID, timerFuncID);
497
- if (!rres)
498
- return null;
499
- if (rres?.cancelled) {
500
- throw new error_1.DBOSAwaitedWorkflowCancelledError(workflowID);
501
- }
502
- return dbos_executor_1.SolidActionsExecutor.reviveResultOrError(rres, DBOS.#executor.serializer);
503
- }, 'DBOS.getResult', workflowID, assignedFuncID);
504
- }
505
- /**
506
- * Create a workflow handle with a given workflow ID.
507
- * This call always returns a handle, even if the workflow does not exist.
508
- * The resulting handle will check the database to provide any workflow information.
509
- * @param workflowID - ID of the workflow
510
- * @returns `WorkflowHandle` that can be used to poll for the status or result of any workflow with `workflowID`
511
- */
512
- static retrieveWorkflow(workflowID) {
513
- (0, decorators_1.ensureDBOSIsLaunched)('retrieveWorkflow');
514
- if (DBOS.isWithinWorkflow()) {
515
- if (!DBOS.isInWorkflow()) {
516
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `retrieveWorkflow` inside a `transaction` or `step`');
517
- }
518
- return new workflow_1.RetrievedHandle(dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase, workflowID);
519
- }
520
- return DBOS.#executor.retrieveWorkflow(workflowID);
521
- }
522
- /**
523
- * Query the system database for all workflows matching the provided predicate
524
- * @param input - `GetWorkflowsInput` predicate for filtering returned workflows
525
- * @returns `WorkflowStatus` array containing details of the matching workflows
526
- */
527
- static async listWorkflows(input) {
528
- (0, decorators_1.ensureDBOSIsLaunched)('listWorkflows');
529
- return await runInternalStep(async () => {
530
- return await DBOS.#executor.listWorkflows(input);
531
- }, 'DBOS.listWorkflows');
532
- }
533
- /**
534
- * Retrieve the steps of a workflow
535
- * @param workflowID - ID of the workflow
536
- * @returns `StepInfo` array listing the executed steps of the workflow. If the workflow is not found, `undefined` is returned.
537
- */
538
- static async listWorkflowSteps(workflowID) {
539
- (0, decorators_1.ensureDBOSIsLaunched)('listWorkflowSteps');
540
- return await runInternalStep(async () => {
541
- return await DBOS.#executor.listWorkflowSteps(workflowID);
542
- }, 'DBOS.listWorkflowSteps');
543
- }
544
- /**
545
- * Cancel a workflow given its ID.
546
- * If the workflow is currently running, `DBOSWorkflowCancelledError` will be
547
- * thrown from its next DBOS call.
548
- * @param workflowID - ID of the workflow
549
- */
550
- static async cancelWorkflow(workflowID) {
551
- (0, decorators_1.ensureDBOSIsLaunched)('cancelWorkflow');
552
- return await runInternalStep(async () => {
553
- return await DBOS.#executor.cancelWorkflow(workflowID);
554
- }, 'DBOS.cancelWorkflow');
555
- }
556
- /**
557
- * Resume a workflow given its ID.
558
- * @param workflowID - ID of the workflow
559
- */
560
- static async resumeWorkflow(workflowID) {
561
- (0, decorators_1.ensureDBOSIsLaunched)('resumeWorkflow');
562
- await runInternalStep(async () => {
563
- return await DBOS.#executor.resumeWorkflow(workflowID);
564
- }, 'DBOS.resumeWorkflow');
565
- return this.retrieveWorkflow(workflowID);
566
- }
567
- /**
568
- * Fork a workflow given its ID.
569
- * @param workflowID - ID of the workflow
570
- * @param startStep - Step ID to start the forked workflow from
571
- * @param applicationVersion - Version of the application to use for the forked workflow
572
- * @returns A handle to the forked workflow
573
- * @throws DBOSInvalidStepIDError if the `startStep` is greater than the maximum step ID of the workflow
574
- */
575
- static async forkWorkflow(workflowID, startStep, options) {
576
- (0, decorators_1.ensureDBOSIsLaunched)('forkWorkflow');
577
- const forkedID = await runInternalStep(async () => {
578
- return await DBOS.#executor.forkWorkflow(workflowID, startStep, options);
579
- }, 'DBOS.forkWorkflow');
580
- return this.retrieveWorkflow(forkedID);
581
- }
582
- /**
583
- * Sleep for the specified amount of time.
584
- * If called from within a workflow, the sleep is "durable",
585
- * meaning that the workflow will sleep until the wakeup time
586
- * (calculated by adding `durationMS` to the original invocation time),
587
- * regardless of workflow recovery.
588
- * @param durationMS - Length of sleep, in milliseconds.
589
- */
590
- static async sleepms(durationMS) {
591
- if (DBOS.isWithinWorkflow() && !DBOS.isInStep()) {
592
- if (DBOS.isInTransaction()) {
593
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.sleep` inside a `transaction`');
594
- }
595
- const functionID = (0, context_1.functionIDGetIncrement)();
596
- if (durationMS <= 0) {
597
- return;
598
- }
599
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.durableSleepms(DBOS.workflowID, functionID, durationMS);
600
- }
601
- await (0, utils_1.sleepms)(durationMS);
602
- }
603
- /** @see sleepms */
604
- static async sleepSeconds(durationSec) {
605
- return DBOS.sleepms(durationSec * 1000);
606
- }
607
- /** @see sleepms */
608
- static async sleep(durationMS) {
609
- return DBOS.sleepms(durationMS);
610
- }
611
- /**
612
- * Get the current time in milliseconds, similar to `Date.now()`.
613
- * This function is deterministic and can be used within workflows.
614
- */
615
- static async now() {
616
- if (DBOS.isInWorkflow()) {
617
- return runInternalStep(async () => Promise.resolve(Date.now()), 'DBOS.now');
618
- }
619
- return Date.now();
620
- }
621
- /**
622
- * Generate a random (v4) UUUID, similar to `node:crypto.randomUUID`.
623
- * This function is deterministic and can be used within workflows.
624
- */
625
- static async randomUUID() {
626
- if (DBOS.isInWorkflow()) {
627
- return runInternalStep(async () => Promise.resolve((0, node_crypto_1.randomUUID)()), 'DBOS.randomUUID');
628
- }
629
- return (0, node_crypto_1.randomUUID)();
630
- }
631
- /**
632
- * Use the provided `workflowID` as the identifier for first workflow started
633
- * within the `callback` function.
634
- * @param workflowID - ID to assign to the first workflow started
635
- * @param callback - Function to run, which would start a workflow
636
- * @returns - Return value from `callback`
637
- */
638
- static async withNextWorkflowID(workflowID, callback) {
639
- (0, decorators_1.ensureDBOSIsLaunched)('workflows');
640
- return DBOS.#withTopContext({ idAssignedForNextWorkflow: workflowID }, callback);
641
- }
642
- /**
643
- * Use the provided `authedUser` and `authedRoles` as the authenticated user for
644
- * any security checks or calls to `DBOS.authenticatedUser`
645
- * or `DBOS.authenticatedRoles` placed within the `callback` function.
646
- * @param authedUser - Authenticated user
647
- * @param authedRoles - Authenticated roles
648
- * @param callback - Function to run with authentication context in place
649
- * @returns - Return value from `callback`
650
- */
651
- static async withAuthedContext(authedUser, authedRoles, callback) {
652
- (0, decorators_1.ensureDBOSIsLaunched)('auth');
653
- return DBOS.#withTopContext({
654
- authenticatedUser: authedUser,
655
- authenticatedRoles: authedRoles,
656
- }, callback);
657
- }
658
- /**
659
- * This generic setter helps users calling DBOS operation to pass a name,
660
- * later used in seeding a parent OTel span for the operation.
661
- * @param callerName - Tracing caller name
662
- * @param callback - Function to run with tracing context in place
663
- * @returns - Return value from `callback`
664
- */
665
- static async withNamedContext(callerName, callback) {
666
- (0, decorators_1.ensureDBOSIsLaunched)('tracing');
667
- return DBOS.#withTopContext({ operationCaller: callerName }, callback);
668
- }
669
- /**
670
- * Specify workflow timeout for any workflows started within the `callback`.
671
- * @param timeoutMS - timeout length for all workflows started within `callback` will be run
672
- * @param callback - Function to run, which would call or start workflows
673
- * @returns - Return value from `callback`
674
- */
675
- static async withWorkflowTimeout(timeoutMS, callback) {
676
- (0, decorators_1.ensureDBOSIsLaunched)('workflows');
677
- return DBOS.#withTopContext({ workflowTimeoutMS: timeoutMS }, callback);
678
- }
679
- /**
680
- * Run a workflow with the option to set any of the contextual items
681
- *
682
- * @param options - Overrides for options
683
- * @param callback - Function to run, which would call or start workflows
684
- * @returns - Return value from `callback`
685
- */
686
- static async runWithContext(options, callback) {
687
- (0, decorators_1.ensureDBOSIsLaunched)('contexts');
688
- return DBOS.#withTopContext(options, callback);
689
- }
690
- static async #withTopContext(options, callback) {
691
- const pctx = (0, context_1.getCurrentContextStore)();
692
- if (pctx) {
693
- // Save existing values and overwrite with new; hard to do cleanly but is actually type correct
694
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
695
- const existing = {};
696
- for (const k of Object.keys(options)) {
697
- if (Object.hasOwn(pctx, k))
698
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
699
- existing[k] = options[k];
700
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
701
- pctx[k] = options[k];
702
- }
703
- try {
704
- return await callback();
705
- }
706
- finally {
707
- for (const k of Object.keys(options)) {
708
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
709
- if (Object.hasOwn(existing, k))
710
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
711
- pctx[k] = existing[k];
712
- else
713
- delete pctx[k];
714
- }
715
- }
716
- }
717
- else {
718
- return await (0, context_1.runWithTopContext)(options, callback);
719
- }
720
- }
721
- static startWorkflow(target, params) {
722
- (0, decorators_1.ensureDBOSIsLaunched)('workflows');
723
- const instance = typeof target === 'function' ? null : target;
724
- if (instance && typeof instance !== 'function' && !(instance instanceof decorators_1.ConfiguredInstance)) {
725
- throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call `startWorkflow` on an object that is not a `ConfiguredInstance`');
726
- }
727
- const regOps = (0, decorators_1.getRegisteredOperations)(target);
728
- const handler = {
729
- apply(target, _thisArg, args) {
730
- const regOp = (0, decorators_1.getFunctionRegistration)(target);
731
- if (!regOp) {
732
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
733
- const name = typeof target === 'function' ? target.name : target.toString();
734
- throw new error_1.DBOSNotRegisteredError(name, `${name} is not a registered DBOS workflow function`);
735
- }
736
- return DBOS.#invokeWorkflow(instance, regOp, args, params);
737
- },
738
- get(target, p, receiver) {
739
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
740
- const func = Reflect.get(target, p, receiver);
741
- const regOp = (0, decorators_1.getFunctionRegistration)(func) ?? regOps.find((op) => op.name === p);
742
- if (regOp) {
743
- return (...args) => DBOS.#invokeWorkflow(instance, regOp, args, params);
744
- }
745
- const name = typeof p === 'string' ? p : String(p);
746
- throw new error_1.DBOSNotRegisteredError(name, `${name} is not a registered DBOS workflow function`);
747
- },
748
- };
749
- return new Proxy(target, handler);
750
- }
751
- /**
752
- * Send `message` on optional `topic` to the workflow with `destinationID`
753
- * This can be done from inside or outside of DBOS workflow functions
754
- * Use the optional `idempotencyKey` to guarantee that the message is sent exactly once
755
- * @see `DBOS.recv`
756
- *
757
- * @param destinationID - ID of the workflow that will `recv` the message
758
- * @param message - Message to send, which must be serializable as JSON
759
- * @param topic - Optional topic; if specified the `recv` command can specify the same topic to receive selectively
760
- * @param idempotencyKey - Optional key for sending the message exactly once
761
- */
762
- static async send(destinationID, message, topic, idempotencyKey) {
763
- (0, decorators_1.ensureDBOSIsLaunched)('send');
764
- if (DBOS.isWithinWorkflow()) {
765
- if (!DBOS.isInWorkflow()) {
766
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.send` inside a `step` or `transaction`');
767
- }
768
- if (idempotencyKey) {
769
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.send` with an idempotency key from within a workflow');
770
- }
771
- const functionID = (0, context_1.functionIDGetIncrement)();
772
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.send(DBOS.workflowID, functionID, destinationID, DBOS.#executor.serializer.stringify(message), topic);
773
- }
774
- return DBOS.#executor.runSendTempWF(destinationID, message, topic, idempotencyKey); // Temp WF variant
775
- }
776
- /**
777
- * Receive a message on optional `topic` from within a workflow.
778
- * This must be called from within a workflow; this workflow's ID is used to check for messages sent by `DBOS.send`
779
- * This can be configured to time out.
780
- * Messages are received in the order in which they are sent (per-sender / causal order).
781
- * @see `DBOS.send`
782
- *
783
- * @param topic - Optional topic; if specified the `recv` command can specify the same topic to receive selectively
784
- * @param timeoutSeconds - Optional timeout; if no message is received before the timeout, `null` will be returned
785
- * @template T - The type of message that is expected to be received
786
- * @returns Any message received, or `null` if the timeout expires
787
- */
788
- static async recv(topic, timeoutSeconds) {
789
- (0, decorators_1.ensureDBOSIsLaunched)('recv');
790
- if (DBOS.isWithinWorkflow()) {
791
- if (!DBOS.isInWorkflow()) {
792
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.recv` inside a `step` or `transaction`');
793
- }
794
- const functionID = (0, context_1.functionIDGetIncrement)();
795
- const timeoutFunctionID = (0, context_1.functionIDGetIncrement)();
796
- return DBOS.#executor.serializer.parse(await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.recv(DBOS.workflowID, functionID, timeoutFunctionID, topic, timeoutSeconds));
797
- }
798
- throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call `DBOS.recv` outside of a workflow'); // Only workflows can recv
799
- }
800
- /**
801
- * Set an event, from within a DBOS workflow. This value can be retrieved with `DBOS.getEvent`.
802
- * If the event `key` already exists, its `value` is updated.
803
- * This function can only be called from within a workflow.
804
- * @see `DBOS.getEvent`
805
- *
806
- * @param key - The key for the event; at most one value is associated with a key at any given time.
807
- * @param value - The value to associate with `key`
808
- */
809
- static async setEvent(key, value) {
810
- (0, decorators_1.ensureDBOSIsLaunched)('setEvent');
811
- if (DBOS.isWithinWorkflow()) {
812
- if (!DBOS.isInWorkflow()) {
813
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.setEvent` inside a `step` or `transaction`');
814
- }
815
- const functionID = (0, context_1.functionIDGetIncrement)();
816
- return dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.setEvent(DBOS.workflowID, functionID, key, DBOS.#executor.serializer.stringify(value));
817
- }
818
- throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call `DBOS.setEvent` outside of a workflow'); // Only workflows can set event
819
- }
820
- /**
821
- * Get the value of a workflow event, or wait for it to be set.
822
- * This function can be called inside or outside of DBOS workflow functions.
823
- * If this function is called from within a workflow, its result is durably checkpointed.
824
- * @see `DBOS.setEvent`
825
- *
826
- * @param workflowID - The ID of the workflow with the corresponding `setEvent`
827
- * @param key - The key for the event; at most one value is associated with a key at any given time.
828
- * @param timeoutSeconds - Optional timeout; if a value for `key` is not set before the timeout, `null` will be returned
829
- * @template T - The expected type for the value assigned to `key`
830
- * @returns The value to associate with `key`, or `null` if the timeout is hit
831
- */
832
- static async getEvent(workflowID, key, timeoutSeconds) {
833
- (0, decorators_1.ensureDBOSIsLaunched)('getEvent');
834
- if (DBOS.isWithinWorkflow()) {
835
- if (!DBOS.isInWorkflow()) {
836
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.getEvent` inside a `step` or `transaction`');
837
- }
838
- const functionID = (0, context_1.functionIDGetIncrement)();
839
- const timeoutFunctionID = (0, context_1.functionIDGetIncrement)();
840
- const params = {
841
- workflowID: DBOS.workflowID,
842
- functionID,
843
- timeoutFunctionID,
844
- };
845
- return DBOS.#executor.serializer.parse(await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.getEvent(workflowID, key, timeoutSeconds ?? dbos_executor_1.SolidActionsExecutor.defaultNotificationTimeoutSec, params));
846
- }
847
- return DBOS.#executor.getEvent(workflowID, key, timeoutSeconds);
848
- }
849
- /**
850
- * Write a value to a stream.
851
- * @param key - The stream key/name within the workflow
852
- * @param value - A serializable value to write to the stream
853
- */
854
- static async writeStream(key, value) {
855
- (0, decorators_1.ensureDBOSIsLaunched)('writeStream');
856
- if (DBOS.isWithinWorkflow()) {
857
- if (DBOS.isInWorkflow()) {
858
- const functionID = (0, context_1.functionIDGetIncrement)();
859
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.writeStreamFromWorkflow(DBOS.workflowID, functionID, key, value);
860
- }
861
- else if (DBOS.isInStep()) {
862
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.writeStreamFromStep(DBOS.workflowID, key, value);
863
- }
864
- else {
865
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.writeStream` outside of a workflow or step');
866
- }
867
- }
868
- else {
869
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.writeStream` outside of a workflow or step');
870
- }
871
- }
872
- /**
873
- * Close a stream by writing a sentinel value.
874
- * @param key - The stream key/name within the workflow
875
- */
876
- static async closeStream(key) {
877
- (0, decorators_1.ensureDBOSIsLaunched)('closeStream');
878
- if (DBOS.isWithinWorkflow()) {
879
- if (DBOS.isInWorkflow()) {
880
- const functionID = (0, context_1.functionIDGetIncrement)();
881
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.closeStream(DBOS.workflowID, functionID, key);
882
- }
883
- else {
884
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.closeStream` outside of a workflow or step');
885
- }
886
- }
887
- else {
888
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to `DBOS.closeStream` outside of a workflow');
889
- }
890
- }
891
- /**
892
- * Read values from a stream as an async generator.
893
- * This function reads values from a stream identified by the workflowID and key,
894
- * yielding each value in order until the stream is closed or the workflow terminates.
895
- * @param workflowID - The workflow instance ID that owns the stream
896
- * @param key - The stream key/name within the workflow
897
- * @returns An async generator that yields each value in the stream until the stream is closed
898
- */
899
- static async *readStream(workflowID, key) {
900
- (0, decorators_1.ensureDBOSIsLaunched)('readStream');
901
- let offset = 0;
902
- while (true) {
903
- try {
904
- const value = await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.readStream(workflowID, key, offset);
905
- if (value === system_database_1.DBOS_STREAM_CLOSED_SENTINEL) {
906
- break;
907
- }
908
- yield value;
909
- offset += 1;
910
- }
911
- catch (error) {
912
- if (error instanceof Error && error.message.includes('No value found')) {
913
- // Poll the offset until a value arrives or the workflow terminates
914
- const status = await DBOS.getWorkflowStatus(workflowID);
915
- if (!status || !(0, workflow_1.isWorkflowActive)(status.status)) {
916
- break;
917
- }
918
- await (0, utils_1.sleepms)(1000); // 1 second polling interval
919
- continue;
920
- }
921
- throw error;
922
- }
923
- }
924
- }
925
- //////
926
- // Decorators
927
- //////
928
- /**
929
- * Allow a class to be assigned a name
930
- */
931
- static className(name) {
932
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
933
- function clsdec(ctor) {
934
- const clsreg = (0, decorators_1.getClassRegistration)(ctor, true);
935
- if (clsreg.reg?.name && clsreg.reg.name !== name && clsreg.reg.name !== ctor.name) {
936
- throw new error_1.DBOSConflictingRegistrationError(`Attempt to assign name ${name} to class ${ctor.name}, which has already been aliased to ${clsreg.reg.name}`);
937
- }
938
- clsreg.reg.name = name;
939
- }
940
- return clsdec;
941
- }
942
- /**
943
- * Decorator designating a method as a DBOS workflow
944
- * Durable execution will be applied within calls to the workflow function
945
- * This also registers the function so that it is available during recovery
946
- * @param config - Configuration information for the workflow
947
- */
948
- static workflow(config = {}) {
949
- function decorator(target, propertyKey, inDescriptor) {
950
- const { descriptor, registration } = (0, decorators_1.wrapDBOSFunctionAndRegisterByTarget)(target, propertyKey, config.name ?? propertyKey, inDescriptor);
951
- const invoker = DBOS.#getWorkflowInvoker(registration, config);
952
- descriptor.value = invoker;
953
- registration.wrappedFunction = invoker;
954
- (0, decorators_1.registerFunctionWrapper)(invoker, registration);
955
- return descriptor;
956
- }
957
- return decorator;
958
- }
959
- /**
960
- * Create a DBOS workflow function from a provided function.
961
- * Similar to the DBOS.workflow, but without requiring a decorator
962
- * Durable execution will be applied to calls to the function returned by registerWorkflow
963
- * This also registers the function so that it is available during recovery
964
- * @param func - The function to register as a workflow
965
- * @param name - The name of the registered workflow
966
- * @param options - Configuration information for the registered workflow
967
- */
968
- static registerWorkflow(func, config) {
969
- const registration = (0, decorators_1.wrapDBOSFunctionAndRegisterByUniqueName)(config?.ctorOrProto, config?.className, config?.name ?? func.name, config?.name ?? func.name, func);
970
- return DBOS.#getWorkflowInvoker(registration, config);
971
- }
972
- static async #invokeWorkflow($this, regOP, args, params = {}, startWfFuncId) {
973
- (0, decorators_1.ensureDBOSIsLaunched)('workflows');
974
- const wfId = (0, context_1.getNextWFID)(params.workflowID);
975
- const ppctx = (0, context_1.getCurrentContextStore)();
976
- const queueName = params.queueName ?? ppctx?.queueAssignedForWorkflows;
977
- const timeoutMS = params.timeoutMS ?? ppctx?.workflowTimeoutMS;
978
- const instance = $this === undefined || typeof $this === 'function' ? undefined : $this;
979
- if (instance && !(instance instanceof decorators_1.ConfiguredInstance)) {
980
- throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call a `workflow` function on an object that is not a `ConfiguredInstance`');
981
- }
982
- // If this is called from within a workflow, this is a child workflow,
983
- // For OAOO, we will need a consistent ID formed from the parent WF and call number
984
- if (DBOS.isWithinWorkflow()) {
985
- if (!DBOS.isInWorkflow()) {
986
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to a `workflow` function from within a `step` or `transaction`');
987
- }
988
- const funcId = startWfFuncId ?? (0, context_1.functionIDGetIncrement)();
989
- const pctx = (0, context_1.getCurrentContextStore)();
990
- const pwfid = pctx.workflowId;
991
- const wfParams = {
992
- workflowUUID: wfId || pwfid + '-' + funcId,
993
- configuredInstance: instance,
994
- queueName,
995
- timeoutMS,
996
- // Detach child deadline if a null timeout is configured
997
- deadlineEpochMS: params.timeoutMS === null || pctx?.workflowTimeoutMS === null ? undefined : pctx?.deadlineEpochMS,
998
- enqueueOptions: params.enqueueOptions,
999
- };
1000
- return await invokeRegOp(wfParams, pwfid, funcId);
1001
- }
1002
- else {
1003
- const wfParams = {
1004
- workflowUUID: wfId,
1005
- queueName,
1006
- enqueueOptions: params.enqueueOptions,
1007
- configuredInstance: instance,
1008
- timeoutMS,
1009
- };
1010
- return await invokeRegOp(wfParams, undefined, undefined);
1011
- }
1012
- function invokeRegOp(wfParams, workflowID, funcNum) {
1013
- if (regOP.workflowConfig) {
1014
- const func = regOP.registeredFunction;
1015
- return dbos_executor_1.SolidActionsExecutor.globalInstance.internalWorkflow(func, wfParams, workflowID, funcNum, ...args);
1016
- }
1017
- if (regOP.stepConfig) {
1018
- const func = regOP.registeredFunction;
1019
- return dbos_executor_1.SolidActionsExecutor.globalInstance.startStepTempWF(func, wfParams, workflowID, funcNum, ...args);
1020
- }
1021
- throw new error_1.DBOSNotRegisteredError(regOP.name, `${regOP.name} is not a registered DBOS workflow, step, or transaction function`);
1022
- }
1023
- }
1024
- static #getWorkflowInvoker(registration, config) {
1025
- registration.setWorkflowConfig(config ?? {});
1026
- const invoker = async function (...rawArgs) {
1027
- (0, decorators_1.ensureDBOSIsLaunched)('workflows');
1028
- if (DBOS.isInWorkflow()) {
1029
- const startWfFuncId = (0, context_1.functionIDGetIncrement)();
1030
- const getResFuncID = (0, context_1.functionIDGetIncrement)();
1031
- const handle = await DBOS.#invokeWorkflow(this, registration, rawArgs, undefined, startWfFuncId);
1032
- return await handle.getResult(getResFuncID);
1033
- }
1034
- const handle = await DBOS.#invokeWorkflow(this, registration, rawArgs);
1035
- return await handle.getResult();
1036
- };
1037
- (0, decorators_1.registerFunctionWrapper)(invoker, registration);
1038
- Object.defineProperty(invoker, 'name', {
1039
- value: registration.name,
1040
- });
1041
- return invoker;
1042
- }
1043
- /**
1044
- * Decorator designating a method as a DBOS step.
1045
- * A durable checkpoint will be made after the step completes
1046
- * This ensures "at least once" execution of the step, and that the step will not
1047
- * be executed again once the checkpoint is recorded
1048
- *
1049
- * @param config - Configuration information for the step, particularly the retry policy
1050
- */
1051
- static step(config = {}) {
1052
- function decorator(target, propertyKey, inDescriptor) {
1053
- const { descriptor, registration } = (0, decorators_1.wrapDBOSFunctionAndRegisterByTarget)(target, propertyKey, config.name, inDescriptor);
1054
- registration.setStepConfig(config);
1055
- const invokeWrapper = async function (...rawArgs) {
1056
- (0, decorators_1.ensureDBOSIsLaunched)('steps');
1057
- let inst = undefined;
1058
- if (this === undefined || typeof this === 'function') {
1059
- // This is static
1060
- }
1061
- else {
1062
- inst = this;
1063
- if (!(inst instanceof decorators_1.ConfiguredInstance)) {
1064
- throw new error_1.DBOSInvalidWorkflowTransitionError('Attempt to call a `step` function on an object that is not a `ConfiguredInstance`');
1065
- }
1066
- }
1067
- if (DBOS.isWithinWorkflow()) {
1068
- if (DBOS.isInTransaction()) {
1069
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to a `step` function from within a `transaction`');
1070
- }
1071
- if (DBOS.isInStep()) {
1072
- // There should probably be checks here about the compatibility of the StepConfig...
1073
- return registration.registeredFunction.call(this, ...rawArgs);
1074
- }
1075
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(registration.registeredFunction, undefined, undefined, inst ?? null, ...rawArgs);
1076
- }
1077
- const wfId = (0, context_1.getNextWFID)(undefined);
1078
- const wfParams = {
1079
- configuredInstance: inst,
1080
- workflowUUID: wfId,
1081
- };
1082
- return await DBOS.#executor.runStepTempWF(registration.registeredFunction, wfParams, ...rawArgs);
1083
- };
1084
- descriptor.value = invokeWrapper;
1085
- registration.wrappedFunction = invokeWrapper;
1086
- (0, decorators_1.registerFunctionWrapper)(invokeWrapper, registration);
1087
- Object.defineProperty(invokeWrapper, 'name', {
1088
- value: registration.name,
1089
- });
1090
- return descriptor;
1091
- }
1092
- return decorator;
1093
- }
1094
- /**
1095
- * Create a check pointed DBOS step function from a provided function
1096
- * Similar to the DBOS.step decorator, but without requiring a decorator
1097
- * A durable checkpoint will be made after the step completes
1098
- * This ensures "at least once" execution of the step, and that the step will not
1099
- * be executed again once the checkpoint is recorded
1100
- * @param func - The function to register as a step
1101
- * @param config - Configuration information for the step, particularly the retry policy and name
1102
- */
1103
- static registerStep(func, config = {}) {
1104
- const name = config.name ?? func.name;
1105
- const reg = (0, decorators_1.wrapDBOSFunctionAndRegister)(config?.ctorOrProto, config?.className, name, name, func);
1106
- const invokeWrapper = async function (...rawArgs) {
1107
- (0, decorators_1.ensureDBOSIsLaunched)('steps');
1108
- // eslint-disable-next-line @typescript-eslint/no-this-alias
1109
- const inst = this;
1110
- const callFunc = reg.registeredFunction ?? reg.origFunction;
1111
- if (DBOS.isWithinWorkflow()) {
1112
- if (DBOS.isInTransaction()) {
1113
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to a `step` function from within a `transaction`');
1114
- }
1115
- if (DBOS.isInStep()) {
1116
- // There should probably be checks here about the compatibility of the StepConfig...
1117
- return callFunc.call(this, ...rawArgs);
1118
- }
1119
- return await dbos_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(callFunc, name, config, inst ?? null, ...rawArgs);
1120
- }
1121
- if ((0, context_1.getNextWFID)(undefined)) {
1122
- throw new error_1.DBOSInvalidWorkflowTransitionError(`Invalid call to step '${name}' outside of a workflow; with directive to start a workflow.`);
1123
- }
1124
- return callFunc.call(this, ...rawArgs);
1125
- };
1126
- (0, decorators_1.registerFunctionWrapper)(invokeWrapper, reg);
1127
- Object.defineProperty(invokeWrapper, 'name', { value: name });
1128
- return invokeWrapper;
1129
- }
1130
- /**
1131
- * Run the enclosed `callback` as a checkpointed step within a DBOS workflow
1132
- * @param callback - function containing code to run
1133
- * @param config - Configuration information for the step, particularly the retry policy
1134
- * @param config.name - The name of the step; if not provided, the function name will be used
1135
- * @returns - result (either obtained from invoking function, or retrieved if run before)
1136
- */
1137
- static runStep(func, config = {}) {
1138
- (0, decorators_1.ensureDBOSIsLaunched)('steps');
1139
- const name = config.name ?? func.name;
1140
- if (DBOS.isWithinWorkflow()) {
1141
- if (DBOS.isInTransaction()) {
1142
- throw new error_1.DBOSInvalidWorkflowTransitionError('Invalid call to a runStep from within a `transaction`');
1143
- }
1144
- if (DBOS.isInStep()) {
1145
- // There should probably be checks here about the compatibility of the StepConfig...
1146
- return func();
1147
- }
1148
- return dbos_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(func, name, config, null);
1149
- }
1150
- if ((0, context_1.getNextWFID)(undefined)) {
1151
- throw new error_1.DBOSInvalidWorkflowTransitionError(`Invalid call to step '${name}' outside of a workflow; with directive to start a workflow.`);
1152
- }
1153
- return func();
1154
- }
1155
- /**
1156
- * Register serialization recipe; this is used to save/retrieve objects from the DBOS system
1157
- * database. This includes workflow inputs, function return values, messages, and events.
1158
- */
1159
- static registerSerialization(serReg) {
1160
- if (DBOS.isInitialized()) {
1161
- throw new TypeError(`Serializers/deserializers should not be registered after DBOS.launch()`);
1162
- }
1163
- (0, serialization_1.registerSerializationRecipe)(serReg);
1164
- }
1165
- /**
1166
- * Decorate a class with the default list of required roles.
1167
- * This class-level default can be overridden on a per-function basis with `requiredRole`.
1168
- * @param anyOf - The list of roles allowed access; authorization is granted if the authenticated user has any role on the list
1169
- */
1170
- static defaultRequiredRole(anyOf) {
1171
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1172
- function clsdec(ctor) {
1173
- const clsreg = (0, decorators_1.associateClassWithExternal)(decorators_1.DBOS_AUTH, ctor);
1174
- clsreg.requiredRole = anyOf;
1175
- (0, authdecorators_1.registerAuthChecker)();
1176
- }
1177
- return clsdec;
1178
- }
1179
- /**
1180
- * Decorate a method with the default list of required roles.
1181
- * @see `DBOS.defaultRequiredRole`
1182
- * @param anyOf - The list of roles allowed access; authorization is granted if the authenticated user has any role on the list
1183
- */
1184
- static requiredRole(anyOf) {
1185
- function apidec(target, propertyKey, inDescriptor) {
1186
- const rr = (0, decorators_1.associateMethodWithExternal)(decorators_1.DBOS_AUTH, target, undefined, propertyKey.toString(), inDescriptor.value);
1187
- rr.regInfo.requiredRole = anyOf;
1188
- (0, authdecorators_1.registerAuthChecker)();
1189
- inDescriptor.value = rr.registration.wrappedFunction ?? rr.registration.registeredFunction;
1190
- return inDescriptor;
1191
- }
1192
- return apidec;
1193
- }
1194
- /////
1195
- // Patching
1196
- /////
1197
- /**
1198
- * Check if a workflow execution has been patched.
1199
- *
1200
- * Patching allows reexecution of workflows to accommate changes to the workflow logic.
1201
- *
1202
- * Patches check the system database to see which code branch to take. As this adds overhead,
1203
- * they may eventually be removed; see `deprecatePatch`.
1204
- *
1205
- * @param patchName Name of the patch to check.
1206
- * @returns true if this is the patched(new) workflow variant, or false if the execution predates the patch
1207
- */
1208
- static async patch(patchName) {
1209
- if (!DBOS.isInWorkflow()) {
1210
- throw new error_1.DBOSInvalidWorkflowTransitionError('`DBOS.patch` must be called from a workflow, and not within a step');
1211
- }
1212
- if (!DBOS.#dbosConfig?.enablePatching) {
1213
- throw new error_1.DBOSInvalidWorkflowTransitionError('Patching is not enabled. See `enablePatching` in `DBOSConfig`');
1214
- }
1215
- const patched = await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.checkPatch(DBOS.workflowID, (0, context_1.functionIDGet)(), patchName, false);
1216
- if (patched.hasEntry) {
1217
- (0, context_1.functionIDGetIncrement)();
1218
- }
1219
- return patched.isPatched;
1220
- }
1221
- /**
1222
- * Check if a workflow execution has been patched, within a plan to eventually remove the unpatched (old) variant.
1223
- *
1224
- * `patch` may be changed to `deprecatePatch` after all unpatched workflows have completed and will not be reexecuted.
1225
- * Once all workflows started with `patch` have completed (in favor of those using `deprecatePatch`), the `deprecatePatch` may then be removed.
1226
- *
1227
- * @param patchName Name of the patch to check.
1228
- * @returns true if this is the patched(new) workflow variant, which it should always be if all unpatched workflows have been retired
1229
- */
1230
- static async deprecatePatch(patchName) {
1231
- if (!DBOS.isInWorkflow()) {
1232
- throw new error_1.DBOSInvalidWorkflowTransitionError('`DBOS.deprecatePatch` must be called from a workflow, and not within a step');
1233
- }
1234
- if (!DBOS.#dbosConfig?.enablePatching) {
1235
- throw new error_1.DBOSInvalidWorkflowTransitionError('Patching is not enabled. See `enablePatching` in `DBOSConfig`');
1236
- }
1237
- const patched = await dbos_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.checkPatch(DBOS.workflowID, (0, context_1.functionIDGet)(), patchName, true);
1238
- if (patched.hasEntry) {
1239
- (0, context_1.functionIDGetIncrement)();
1240
- }
1241
- return patched.isPatched;
1242
- }
1243
- /////
1244
- // Registration, etc
1245
- /////
1246
- /**
1247
- * Register a lifecycle listener
1248
- */
1249
- static registerLifecycleCallback(lcl) {
1250
- (0, decorators_1.registerLifecycleCallback)(lcl);
1251
- }
1252
- /**
1253
- * Register a middleware provider
1254
- */
1255
- static registerMiddlewareInstaller(mwp) {
1256
- (0, decorators_1.registerMiddlewareInstaller)(mwp);
1257
- }
1258
- /**
1259
- * Register information to be associated with a DBOS class
1260
- */
1261
- static associateClassWithInfo(external, cls) {
1262
- return (0, decorators_1.associateClassWithExternal)(external, cls);
1263
- }
1264
- /**
1265
- * Register information to be associated with a DBOS function
1266
- */
1267
- static associateFunctionWithInfo(external, func, target) {
1268
- return (0, decorators_1.associateMethodWithExternal)(external, target.ctorOrProto, target.className, target.name ?? func.name, func);
1269
- }
1270
- /**
1271
- * Register information to be associated with a DBOS function
1272
- */
1273
- static associateParamWithInfo(external, func, target) {
1274
- return (0, decorators_1.associateParameterWithExternal)(external, target.ctorOrProto, target.className, target.name ?? func?.name ?? '<unknown>', func, target.param);
1275
- }
1276
- /** Get registrations */
1277
- static getAssociatedInfo(external, cls, funcName) {
1278
- return (0, decorators_1.getRegistrationsForExternal)(external, cls, funcName);
1279
- }
1280
- }
1281
- exports.DBOS = DBOS;
1282
- //# sourceMappingURL=dbos.js.map