@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.
- package/README.md +25 -4
- package/dist/src/cli/cli.js +0 -0
- package/dist/src/testing/index.d.ts +13 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +19 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/mock_server.d.ts +134 -0
- package/dist/src/testing/mock_server.d.ts.map +1 -0
- package/dist/src/testing/mock_server.js +613 -0
- package/dist/src/testing/mock_server.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/sdk-reference.md +1207 -0
- package/package.json +6 -1
- package/.claude/settings.local.json +0 -7
- package/dist/dbos-config.schema.json +0 -132
- package/dist/src/cli/commands.d.ts +0 -3
- package/dist/src/cli/commands.d.ts.map +0 -1
- package/dist/src/cli/commands.js +0 -46
- package/dist/src/cli/commands.js.map +0 -1
- package/dist/src/datasource.d.ts +0 -109
- package/dist/src/datasource.d.ts.map +0 -1
- package/dist/src/datasource.js +0 -204
- package/dist/src/datasource.js.map +0 -1
- package/dist/src/dbos-executor.d.ts +0 -189
- package/dist/src/dbos-executor.d.ts.map +0 -1
- package/dist/src/dbos-executor.js +0 -817
- package/dist/src/dbos-executor.js.map +0 -1
- package/dist/src/dbos.d.ts +0 -519
- package/dist/src/dbos.d.ts.map +0 -1
- package/dist/src/dbos.js +0 -1282
- package/dist/src/dbos.js.map +0 -1
- package/dist/src/debouncer.d.ts +0 -33
- package/dist/src/debouncer.d.ts.map +0 -1
- package/dist/src/debouncer.js +0 -170
- package/dist/src/debouncer.js.map +0 -1
- package/dist/src/scheduler/crontab.d.ts +0 -14
- package/dist/src/scheduler/crontab.d.ts.map +0 -1
- package/dist/src/scheduler/crontab.js +0 -308
- package/dist/src/scheduler/crontab.js.map +0 -1
- package/dist/src/scheduler/scheduler.d.ts +0 -41
- package/dist/src/scheduler/scheduler.d.ts.map +0 -1
- package/dist/src/scheduler/scheduler.js +0 -165
- package/dist/src/scheduler/scheduler.js.map +0 -1
- package/dist/src/wfqueue.d.ts +0 -64
- package/dist/src/wfqueue.d.ts.map +0 -1
- package/dist/src/wfqueue.js +0 -147
- package/dist/src/wfqueue.js.map +0 -1
- package/docs/api-schema.md +0 -1441
- package/docs/migration-guide.md +0 -460
- package/docs/phase-14-changes.md +0 -156
- package/docs/solidsteps-ai-prompt.md +0 -534
- 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
|