@dbos-inc/dbos-sdk 3.6.5-preview → 3.6.8-preview

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 (53) hide show
  1. package/dist/src/adminserver.d.ts +26 -40
  2. package/dist/src/adminserver.d.ts.map +1 -1
  3. package/dist/src/adminserver.js +381 -276
  4. package/dist/src/adminserver.js.map +1 -1
  5. package/dist/src/cli/cli.d.ts.map +1 -1
  6. package/dist/src/cli/cli.js +41 -13
  7. package/dist/src/cli/cli.js.map +1 -1
  8. package/dist/src/config.d.ts +0 -5
  9. package/dist/src/config.d.ts.map +1 -1
  10. package/dist/src/config.js +1 -3
  11. package/dist/src/config.js.map +1 -1
  12. package/dist/src/context.d.ts +0 -1
  13. package/dist/src/context.d.ts.map +1 -1
  14. package/dist/src/datasource.d.ts.map +1 -1
  15. package/dist/src/datasource.js +7 -7
  16. package/dist/src/datasource.js.map +1 -1
  17. package/dist/src/dbos-executor.d.ts +15 -2
  18. package/dist/src/dbos-executor.d.ts.map +1 -1
  19. package/dist/src/dbos-executor.js +10 -11
  20. package/dist/src/dbos-executor.js.map +1 -1
  21. package/dist/src/dbos.d.ts +2 -17
  22. package/dist/src/dbos.d.ts.map +1 -1
  23. package/dist/src/dbos.js +2 -42
  24. package/dist/src/dbos.js.map +1 -1
  25. package/dist/src/system_database.d.ts +0 -2
  26. package/dist/src/system_database.d.ts.map +1 -1
  27. package/dist/src/system_database.js +147 -121
  28. package/dist/src/system_database.js.map +1 -1
  29. package/dist/src/telemetry/collector.d.ts +1 -4
  30. package/dist/src/telemetry/collector.d.ts.map +1 -1
  31. package/dist/src/telemetry/collector.js.map +1 -1
  32. package/dist/src/telemetry/exporters.d.ts +3 -7
  33. package/dist/src/telemetry/exporters.d.ts.map +1 -1
  34. package/dist/src/telemetry/exporters.js +27 -10
  35. package/dist/src/telemetry/exporters.js.map +1 -1
  36. package/dist/src/telemetry/logs.d.ts +5 -16
  37. package/dist/src/telemetry/logs.d.ts.map +1 -1
  38. package/dist/src/telemetry/logs.js +44 -15
  39. package/dist/src/telemetry/logs.js.map +1 -1
  40. package/dist/src/telemetry/traces.d.ts +42 -6
  41. package/dist/src/telemetry/traces.d.ts.map +1 -1
  42. package/dist/src/telemetry/traces.js +95 -31
  43. package/dist/src/telemetry/traces.js.map +1 -1
  44. package/dist/src/utils.d.ts +2 -0
  45. package/dist/src/utils.d.ts.map +1 -1
  46. package/dist/src/utils.js +7 -1
  47. package/dist/src/utils.js.map +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +2 -24
  50. package/dist/src/telemetry/index.d.ts +0 -12
  51. package/dist/src/telemetry/index.d.ts.map +0 -1
  52. package/dist/src/telemetry/index.js +0 -14
  53. package/dist/src/telemetry/index.js.map +0 -1
@@ -22,15 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.DBOSAdminServer = exports.WorkflowQueuesMetadataUrl = exports.DeactivateUrl = exports.PerfUrl = exports.HealthUrl = exports.WorkflowRecoveryUrl = exports.WorkflowUUIDHeader = void 0;
30
- const koa_1 = __importDefault(require("koa"));
31
- const router_1 = __importDefault(require("@koa/router"));
32
- const bodyparser_1 = require("@koa/bodyparser");
33
- const cors_1 = __importDefault(require("@koa/cors"));
27
+ const http = __importStar(require("http"));
28
+ const url = __importStar(require("url"));
34
29
  const error_1 = require("./error");
35
30
  const net = __importStar(require("net"));
36
31
  const perf_hooks_1 = require("perf_hooks");
@@ -45,46 +40,131 @@ exports.HealthUrl = '/dbos-healthz';
45
40
  exports.PerfUrl = '/dbos-perf';
46
41
  exports.DeactivateUrl = '/deactivate';
47
42
  exports.WorkflowQueuesMetadataUrl = '/dbos-workflow-queues-metadata';
48
- class DBOSAdminServer {
49
- dbosExec;
50
- adminApp;
51
- logger;
52
- static nRegisteredEndpoints = 0;
53
- static instance = undefined;
54
- /**
55
- * Create a Koa app.
56
- * @param dbosExec User pass in an DBOS workflow executor instance.
57
- * TODO: maybe call dbosExec.init() somewhere in this class?
58
- */
59
- constructor(dbosExec) {
60
- this.dbosExec = dbosExec;
61
- this.logger = dbosExec.logger;
62
- this.adminApp = DBOSAdminServer.setupAdminApp(this.dbosExec);
63
- DBOSAdminServer.instance = this;
43
+ // Helper to parse JSON body
44
+ async function parseJsonBody(req) {
45
+ return new Promise((resolve, reject) => {
46
+ let body = '';
47
+ req.on('data', (chunk) => {
48
+ body += String(chunk);
49
+ });
50
+ req.on('end', () => {
51
+ try {
52
+ resolve(body ? JSON.parse(body) : {});
53
+ }
54
+ catch (e) {
55
+ reject(new Error('Invalid JSON'));
56
+ }
57
+ });
58
+ req.on('error', reject);
59
+ });
60
+ }
61
+ // Helper to send JSON response
62
+ function sendJson(res, statusCode, data) {
63
+ res.writeHead(statusCode, {
64
+ 'Content-Type': 'application/json',
65
+ 'Access-Control-Allow-Origin': '*',
66
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
67
+ 'Access-Control-Allow-Headers': 'Content-Type',
68
+ });
69
+ res.end(JSON.stringify(data));
70
+ }
71
+ // Helper to send text response
72
+ function sendText(res, statusCode, text) {
73
+ res.writeHead(statusCode, {
74
+ 'Content-Type': 'text/plain',
75
+ 'Access-Control-Allow-Origin': '*',
76
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
77
+ 'Access-Control-Allow-Headers': 'Content-Type',
78
+ });
79
+ res.end(text);
80
+ }
81
+ // Helper to send no content response
82
+ function sendNoContent(res) {
83
+ res.writeHead(204, {
84
+ 'Access-Control-Allow-Origin': '*',
85
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
86
+ 'Access-Control-Allow-Headers': 'Content-Type',
87
+ });
88
+ res.end();
89
+ }
90
+ // Helper to extract path params
91
+ function matchPath(pattern, pathname) {
92
+ const patternParts = pattern.split('/');
93
+ const pathParts = pathname.split('/');
94
+ if (patternParts.length !== pathParts.length)
95
+ return null;
96
+ const params = {};
97
+ for (let i = 0; i < patternParts.length; i++) {
98
+ const patternPart = patternParts[i];
99
+ const pathPart = pathParts[i];
100
+ if (patternPart.startsWith(':')) {
101
+ const paramName = patternPart.substring(1);
102
+ params[paramName] = pathPart;
103
+ }
104
+ else if (patternPart !== pathPart) {
105
+ return null;
106
+ }
64
107
  }
108
+ return params;
109
+ }
110
+ class DBOSAdminServer {
65
111
  static setupAdminApp(dbosExec) {
66
- const adminRouter = new router_1.default();
67
- const adminApp = new koa_1.default();
68
- adminApp.use((0, bodyparser_1.bodyParser)());
69
- adminApp.use((0, cors_1.default)());
112
+ const routes = [];
70
113
  // Register HTTP endpoints.
71
- DBOSAdminServer.registerHealthEndpoint(dbosExec, adminRouter);
72
- DBOSAdminServer.registerRecoveryEndpoint(dbosExec, adminRouter);
73
- DBOSAdminServer.registerPerfEndpoint(dbosExec, adminRouter);
74
- DBOSAdminServer.registerDeactivateEndpoint(dbosExec, adminRouter);
75
- DBOSAdminServer.registerCancelWorkflowEndpoint(dbosExec, adminRouter);
76
- DBOSAdminServer.registerResumeWorkflowEndpoint(dbosExec, adminRouter);
77
- DBOSAdminServer.registerRestartWorkflowEndpoint(dbosExec, adminRouter);
78
- DBOSAdminServer.registerQueueMetadataEndpoint(dbosExec, adminRouter);
79
- DBOSAdminServer.registerListWorkflowStepsEndpoint(dbosExec, adminRouter);
80
- DBOSAdminServer.registerListWorkflowsEndpoint(dbosExec, adminRouter);
81
- DBOSAdminServer.registerListQueuedWorkflowsEndpoint(dbosExec, adminRouter);
82
- DBOSAdminServer.registerGetWorkflowEndpoint(dbosExec, adminRouter);
83
- DBOSAdminServer.registerForkWorkflowEndpoint(dbosExec, adminRouter);
84
- DBOSAdminServer.registerGarbageCollectEndpoint(dbosExec, adminRouter);
85
- DBOSAdminServer.registerGlobalTimeoutEndpoint(dbosExec, adminRouter);
86
- adminApp.use(adminRouter.routes()).use(adminRouter.allowedMethods());
87
- return adminApp;
114
+ DBOSAdminServer.registerHealthEndpoint(dbosExec, routes);
115
+ DBOSAdminServer.registerRecoveryEndpoint(dbosExec, routes);
116
+ DBOSAdminServer.registerPerfEndpoint(dbosExec, routes);
117
+ DBOSAdminServer.registerDeactivateEndpoint(dbosExec, routes);
118
+ DBOSAdminServer.registerCancelWorkflowEndpoint(dbosExec, routes);
119
+ DBOSAdminServer.registerResumeWorkflowEndpoint(dbosExec, routes);
120
+ DBOSAdminServer.registerRestartWorkflowEndpoint(dbosExec, routes);
121
+ DBOSAdminServer.registerQueueMetadataEndpoint(dbosExec, routes);
122
+ DBOSAdminServer.registerListWorkflowStepsEndpoint(dbosExec, routes);
123
+ DBOSAdminServer.registerListWorkflowsEndpoint(dbosExec, routes);
124
+ DBOSAdminServer.registerListQueuedWorkflowsEndpoint(dbosExec, routes);
125
+ DBOSAdminServer.registerGetWorkflowEndpoint(dbosExec, routes);
126
+ DBOSAdminServer.registerForkWorkflowEndpoint(dbosExec, routes);
127
+ DBOSAdminServer.registerGarbageCollectEndpoint(dbosExec, routes);
128
+ DBOSAdminServer.registerGlobalTimeoutEndpoint(dbosExec, routes);
129
+ // Create the HTTP server
130
+ const server = http.createServer(async (req, res) => {
131
+ // Handle CORS preflight requests
132
+ if (req.method === 'OPTIONS') {
133
+ res.writeHead(200, {
134
+ 'Access-Control-Allow-Origin': '*',
135
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
136
+ 'Access-Control-Allow-Headers': 'Content-Type',
137
+ 'Access-Control-Max-Age': '86400',
138
+ });
139
+ res.end();
140
+ return;
141
+ }
142
+ const parsedUrl = url.parse(req.url || '', true);
143
+ const pathname = parsedUrl.pathname || '';
144
+ // Find matching route
145
+ let routeFound = false;
146
+ for (const route of routes) {
147
+ if (req.method === route.method) {
148
+ const params = matchPath(route.path, pathname);
149
+ if (params !== null) {
150
+ routeFound = true;
151
+ try {
152
+ await route.handler(req, res, params);
153
+ }
154
+ catch (error) {
155
+ dbosExec.logger.error(`Request handler error: ${String(error)}`);
156
+ sendJson(res, 500, { error: 'Internal server error' });
157
+ }
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ if (!routeFound) {
163
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
164
+ res.end('Not Found');
165
+ }
166
+ });
167
+ return server;
88
168
  }
89
169
  static async checkPortAvailabilityIPv4Ipv6(port, logger) {
90
170
  try {
@@ -132,321 +212,346 @@ class DBOSAdminServer {
132
212
  /**
133
213
  * Health check endpoint.
134
214
  */
135
- static registerHealthEndpoint(dbosExec, router) {
136
- // Handler function that parses request for recovery.
137
- const healthHandler = async (koaCtxt, koaNext) => {
138
- koaCtxt.body = 'healthy';
139
- await koaNext();
140
- };
141
- router.get(exports.HealthUrl, healthHandler);
215
+ static registerHealthEndpoint(dbosExec, routes) {
216
+ routes.push({
217
+ method: 'GET',
218
+ path: exports.HealthUrl,
219
+ handler: async (req, res) => {
220
+ sendText(res, 200, 'healthy');
221
+ return Promise.resolve();
222
+ },
223
+ });
142
224
  dbosExec.logger.debug(`DBOS Server Registered Healthz GET ${exports.HealthUrl}`);
143
225
  }
144
226
  /**
145
227
  * Register workflow queue metadata endpoint.
146
228
  */
147
- static registerQueueMetadataEndpoint(dbosExec, router) {
148
- const queueMetadataHandler = async (koaCtxt, koaNext) => {
149
- const queueDetailsArray = [];
150
- wfqueue_1.wfQueueRunner.wfQueuesByName.forEach((q, qn) => {
151
- queueDetailsArray.push({
152
- name: qn,
153
- concurrency: q.concurrency,
154
- workerConcurrency: q.workerConcurrency,
155
- rateLimit: q.rateLimit,
229
+ static registerQueueMetadataEndpoint(dbosExec, routes) {
230
+ routes.push({
231
+ method: 'GET',
232
+ path: exports.WorkflowQueuesMetadataUrl,
233
+ handler: async (req, res) => {
234
+ const queueDetailsArray = [];
235
+ wfqueue_1.wfQueueRunner.wfQueuesByName.forEach((q, qn) => {
236
+ queueDetailsArray.push({
237
+ name: qn,
238
+ concurrency: q.concurrency,
239
+ workerConcurrency: q.workerConcurrency,
240
+ rateLimit: q.rateLimit,
241
+ });
156
242
  });
157
- });
158
- koaCtxt.body = queueDetailsArray;
159
- await koaNext();
160
- };
161
- router.get(exports.WorkflowQueuesMetadataUrl, queueMetadataHandler);
243
+ sendJson(res, 200, queueDetailsArray);
244
+ return Promise.resolve();
245
+ },
246
+ });
162
247
  dbosExec.logger.debug(`DBOS Server Registered Queue Metadata GET ${exports.WorkflowQueuesMetadataUrl}`);
163
248
  }
164
249
  /**
165
250
  * Register workflow recovery endpoint.
166
251
  * Receives a list of executor IDs and returns a list of workflowUUIDs.
167
252
  */
168
- static registerRecoveryEndpoint(dbosExec, router) {
169
- // Handler function that parses request for recovery.
170
- const recoveryHandler = async (koaCtxt, koaNext) => {
171
- const executorIDs = koaCtxt.request.body;
172
- dbosExec.logger.info('Recovering workflows for executors: ' + executorIDs.toString());
173
- const recoverHandles = await dbosExec.recoverPendingWorkflows(executorIDs);
174
- // Return a list of workflowUUIDs being recovered.
175
- koaCtxt.body = await Promise.allSettled(recoverHandles.map((i) => i.workflowID)).then((results) => results.filter((i) => i.status === 'fulfilled').map((i) => i.value));
176
- await koaNext();
177
- };
178
- router.post(exports.WorkflowRecoveryUrl, recoveryHandler);
253
+ static registerRecoveryEndpoint(dbosExec, routes) {
254
+ routes.push({
255
+ method: 'POST',
256
+ path: exports.WorkflowRecoveryUrl,
257
+ handler: async (req, res) => {
258
+ const executorIDs = (await parseJsonBody(req));
259
+ dbosExec.logger.info('Recovering workflows for executors: ' + executorIDs.toString());
260
+ const recoverHandles = await dbosExec.recoverPendingWorkflows(executorIDs);
261
+ // Return a list of workflowUUIDs being recovered.
262
+ const result = await Promise.allSettled(recoverHandles.map((i) => i.workflowID)).then((results) => results.filter((i) => i.status === 'fulfilled').map((i) => i.value));
263
+ sendJson(res, 200, result);
264
+ },
265
+ });
179
266
  dbosExec.logger.debug(`DBOS Server Registered Recovery POST ${exports.WorkflowRecoveryUrl}`);
180
267
  }
181
268
  /**
182
269
  * Register performance endpoint.
183
270
  * Returns information on VM performance since last call.
184
271
  */
185
- static registerPerfEndpoint(dbosExec, router) {
186
- let lastELU = perf_hooks_1.performance.eventLoopUtilization();
187
- const perfHandler = async (koaCtxt, koaNext) => {
188
- const currELU = perf_hooks_1.performance.eventLoopUtilization();
189
- const elu = perf_hooks_1.performance.eventLoopUtilization(currELU, lastELU);
190
- koaCtxt.body = elu;
191
- lastELU = currELU;
192
- await koaNext();
193
- };
194
- router.get(exports.PerfUrl, perfHandler);
272
+ static lastELU = perf_hooks_1.performance.eventLoopUtilization();
273
+ static registerPerfEndpoint(dbosExec, routes) {
274
+ routes.push({
275
+ method: 'GET',
276
+ path: exports.PerfUrl,
277
+ handler: async (req, res) => {
278
+ const currELU = perf_hooks_1.performance.eventLoopUtilization();
279
+ const elu = perf_hooks_1.performance.eventLoopUtilization(currELU, DBOSAdminServer.lastELU);
280
+ sendJson(res, 200, elu);
281
+ DBOSAdminServer.lastELU = currELU;
282
+ return Promise.resolve();
283
+ },
284
+ });
195
285
  dbosExec.logger.debug(`DBOS Server Registered Perf GET ${exports.PerfUrl}`);
196
286
  }
197
287
  /**
198
288
  * Register Deactivate endpoint.
199
289
  * Deactivate consumers so that they don't start new workflows.
200
- *
201
290
  */
202
291
  static isDeactivated = false;
203
- static registerDeactivateEndpoint(dbosExec, router) {
204
- const deactivateHandler = async (koaCtxt, koaNext) => {
205
- if (!DBOSAdminServer.isDeactivated) {
206
- dbosExec.logger.info(`Deactivating DBOS executor ${utils_1.globalParams.executorID} with version ${utils_1.globalParams.appVersion}. This executor will complete existing workflows but will not create new workflows.`);
207
- DBOSAdminServer.isDeactivated = true;
208
- }
209
- await dbosExec.deactivateEventReceivers(false);
210
- koaCtxt.body = 'Deactivated';
211
- await koaNext();
212
- };
213
- router.get(exports.DeactivateUrl, deactivateHandler);
292
+ static registerDeactivateEndpoint(dbosExec, routes) {
293
+ routes.push({
294
+ method: 'GET',
295
+ path: exports.DeactivateUrl,
296
+ handler: async (req, res) => {
297
+ if (!DBOSAdminServer.isDeactivated) {
298
+ dbosExec.logger.info(`Deactivating DBOS executor ${utils_1.globalParams.executorID} with version ${utils_1.globalParams.appVersion}. This executor will complete existing workflows but will not create new workflows.`);
299
+ DBOSAdminServer.isDeactivated = true;
300
+ }
301
+ await dbosExec.deactivateEventReceivers(false);
302
+ sendText(res, 200, 'Deactivated');
303
+ },
304
+ });
214
305
  dbosExec.logger.debug(`DBOS Server Registered Deactivate GET ${exports.DeactivateUrl}`);
215
306
  }
216
- static registerGarbageCollectEndpoint(dbosExec, router) {
307
+ static registerGarbageCollectEndpoint(dbosExec, routes) {
217
308
  const url = '/dbos-garbage-collect';
218
- const handler = async (koaCtxt) => {
219
- const body = koaCtxt.request.body;
220
- await dbosExec.systemDatabase.garbageCollect(body.cutoff_epoch_timestamp_ms, body.rows_threshold);
221
- koaCtxt.status = 204;
222
- };
223
- router.post(url, handler);
309
+ routes.push({
310
+ method: 'POST',
311
+ path: url,
312
+ handler: async (req, res) => {
313
+ const body = (await parseJsonBody(req));
314
+ await dbosExec.systemDatabase.garbageCollect(body.cutoff_epoch_timestamp_ms, body.rows_threshold);
315
+ sendNoContent(res);
316
+ },
317
+ });
224
318
  }
225
- static registerGlobalTimeoutEndpoint(dbosExec, router) {
319
+ static registerGlobalTimeoutEndpoint(dbosExec, routes) {
226
320
  const url = '/dbos-global-timeout';
227
- const handler = async (koaCtxt) => {
228
- const body = koaCtxt.request.body;
229
- await (0, workflow_management_1.globalTimeout)(dbosExec.systemDatabase, body.cutoff_epoch_timestamp_ms);
230
- koaCtxt.status = 204;
231
- };
232
- router.post(url, handler);
321
+ routes.push({
322
+ method: 'POST',
323
+ path: url,
324
+ handler: async (req, res) => {
325
+ const body = (await parseJsonBody(req));
326
+ await (0, workflow_management_1.globalTimeout)(dbosExec.systemDatabase, body.cutoff_epoch_timestamp_ms);
327
+ sendNoContent(res);
328
+ },
329
+ });
233
330
  }
234
331
  /**
235
- *
236
332
  * Register Cancel Workflow endpoint.
237
333
  * Cancels a workflow by setting its status to CANCELLED.
238
334
  */
239
- static registerCancelWorkflowEndpoint(dbosExec, router) {
335
+ static registerCancelWorkflowEndpoint(dbosExec, routes) {
240
336
  const workflowCancelUrl = '/workflows/:workflow_id/cancel';
241
- const workflowCancelHandler = async (koaCtxt) => {
242
- const workflowId = koaCtxt.params.workflow_id;
243
- await dbosExec.cancelWorkflow(workflowId);
244
- koaCtxt.status = 204;
245
- };
246
- router.post(workflowCancelUrl, workflowCancelHandler);
337
+ routes.push({
338
+ method: 'POST',
339
+ path: workflowCancelUrl,
340
+ handler: async (req, res, params) => {
341
+ const workflowId = params.workflow_id;
342
+ await dbosExec.cancelWorkflow(workflowId);
343
+ sendNoContent(res);
344
+ },
345
+ });
247
346
  dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowCancelUrl}`);
248
347
  }
249
348
  /**
250
- *
251
349
  * Register Resume Workflow endpoint.
252
350
  * Resume a workflow.
253
351
  */
254
- static registerResumeWorkflowEndpoint(dbosExec, router) {
352
+ static registerResumeWorkflowEndpoint(dbosExec, routes) {
255
353
  const workflowResumeUrl = '/workflows/:workflow_id/resume';
256
- const workflowResumeHandler = async (koaCtxt) => {
257
- const workflowId = koaCtxt.params.workflow_id;
258
- dbosExec.logger.info(`Resuming workflow with ID: ${workflowId}`);
259
- try {
260
- await dbosExec.resumeWorkflow(workflowId);
261
- }
262
- catch (e) {
263
- let errorMessage = '';
264
- if (e instanceof error_1.DBOSError) {
265
- errorMessage = e.message;
354
+ routes.push({
355
+ method: 'POST',
356
+ path: workflowResumeUrl,
357
+ handler: async (req, res, params) => {
358
+ const workflowId = params.workflow_id;
359
+ dbosExec.logger.info(`Resuming workflow with ID: ${workflowId}`);
360
+ try {
361
+ await dbosExec.resumeWorkflow(workflowId);
362
+ sendNoContent(res);
266
363
  }
267
- else {
268
- errorMessage = `Unknown error`;
364
+ catch (e) {
365
+ let errorMessage = '';
366
+ if (e instanceof error_1.DBOSError) {
367
+ errorMessage = e.message;
368
+ }
369
+ else {
370
+ errorMessage = `Unknown error`;
371
+ }
372
+ dbosExec.logger.error(`Error resuming workflow ${workflowId}: ${errorMessage}`);
373
+ sendJson(res, 500, {
374
+ error: `Error resuming workflow ${workflowId}: ${errorMessage}`,
375
+ });
269
376
  }
270
- dbosExec.logger.error(`Error resuming workflow ${workflowId}: ${errorMessage}`);
271
- koaCtxt.status = 500;
272
- koaCtxt.body = {
273
- error: `Error resuming workflow ${workflowId}: ${errorMessage}`,
274
- };
275
- return;
276
- }
277
- koaCtxt.status = 204;
278
- };
279
- router.post(workflowResumeUrl, workflowResumeHandler);
280
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
377
+ },
378
+ });
379
+ dbosExec.logger.debug(`DBOS Server Registered Resume Workflow POST ${workflowResumeUrl}`);
281
380
  }
282
381
  /**
283
- *
284
382
  * Register Restart Workflow endpoint.
285
383
  * Restart a workflow.
286
384
  */
287
- static registerRestartWorkflowEndpoint(dbosExec, router) {
288
- const workflowResumeUrl = '/workflows/:workflow_id/restart';
289
- const workflowRestartHandler = async (koaCtxt) => {
290
- const workflowId = koaCtxt.params.workflow_id;
291
- dbosExec.logger.info(`Restarting workflow: ${workflowId} with a new id`);
292
- const workflowID = await dbosExec.forkWorkflow(workflowId, 0);
293
- koaCtxt.body = {
294
- workflow_id: workflowID,
295
- };
296
- koaCtxt.status = 200;
297
- };
298
- router.post(workflowResumeUrl, workflowRestartHandler);
299
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
385
+ static registerRestartWorkflowEndpoint(dbosExec, routes) {
386
+ const workflowRestartUrl = '/workflows/:workflow_id/restart';
387
+ routes.push({
388
+ method: 'POST',
389
+ path: workflowRestartUrl,
390
+ handler: async (req, res, params) => {
391
+ const workflowId = params.workflow_id;
392
+ dbosExec.logger.info(`Restarting workflow: ${workflowId} with a new id`);
393
+ const workflowID = await dbosExec.forkWorkflow(workflowId, 0);
394
+ sendJson(res, 200, {
395
+ workflow_id: workflowID,
396
+ });
397
+ },
398
+ });
399
+ dbosExec.logger.debug(`DBOS Server Registered Restart Workflow POST ${workflowRestartUrl}`);
300
400
  }
301
401
  /**
302
- *
303
402
  * Register Fork Workflow endpoint.
304
- *
305
403
  */
306
- static registerForkWorkflowEndpoint(dbosExec, router) {
307
- const workflowResumeUrl = '/workflows/:workflow_id/fork';
308
- const workflowForkHandler = async (koaCtxt) => {
309
- const workflowId = koaCtxt.params.workflow_id;
310
- const body = koaCtxt.request.body;
311
- if (body.start_step === undefined) {
312
- throw new error_1.DBOSDataValidationError('Missing start_step in request body');
313
- }
314
- dbosExec.logger.info(`Forking workflow: ${workflowId} from step ${body.start_step} with a new id`);
315
- try {
316
- const workflowID = await dbosExec.forkWorkflow(workflowId, body.start_step, {
317
- newWorkflowID: body.new_workflow_id,
318
- applicationVersion: body.application_version,
319
- timeoutMS: body.timeout_ms,
320
- });
321
- koaCtxt.body = {
322
- workflow_id: workflowID,
323
- };
324
- }
325
- catch (e) {
326
- let errorMessage = '';
327
- if (e instanceof error_1.DBOSError) {
328
- errorMessage = e.message;
404
+ static registerForkWorkflowEndpoint(dbosExec, routes) {
405
+ const workflowForkUrl = '/workflows/:workflow_id/fork';
406
+ routes.push({
407
+ method: 'POST',
408
+ path: workflowForkUrl,
409
+ handler: async (req, res, params) => {
410
+ const workflowId = params.workflow_id;
411
+ const body = (await parseJsonBody(req));
412
+ if (body.start_step === undefined) {
413
+ sendJson(res, 400, { error: 'Missing start_step in request body' });
414
+ return;
329
415
  }
330
- else {
331
- errorMessage = `Unknown error`;
416
+ dbosExec.logger.info(`Forking workflow: ${workflowId} from step ${body.start_step} with a new id`);
417
+ try {
418
+ const workflowID = await dbosExec.forkWorkflow(workflowId, body.start_step, {
419
+ newWorkflowID: body.new_workflow_id,
420
+ applicationVersion: body.application_version,
421
+ timeoutMS: body.timeout_ms,
422
+ });
423
+ sendJson(res, 200, {
424
+ workflow_id: workflowID,
425
+ });
332
426
  }
333
- dbosExec.logger.error(`Error forking workflow ${workflowId}: ${errorMessage}`);
334
- koaCtxt.status = 500;
335
- koaCtxt.body = {
336
- error: `Error forking workflow ${workflowId}: ${errorMessage}`,
337
- };
338
- return;
339
- }
340
- dbosExec.logger.info(`Forked workflow: ${workflowId} with a new id`);
341
- koaCtxt.status = 200;
342
- };
343
- router.post(workflowResumeUrl, workflowForkHandler);
344
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
427
+ catch (e) {
428
+ let errorMessage = '';
429
+ if (e instanceof error_1.DBOSError) {
430
+ errorMessage = e.message;
431
+ }
432
+ else {
433
+ errorMessage = `Unknown error`;
434
+ }
435
+ dbosExec.logger.error(`Error forking workflow ${workflowId}: ${errorMessage}`);
436
+ sendJson(res, 500, {
437
+ error: `Error forking workflow ${workflowId}: ${errorMessage}`,
438
+ });
439
+ }
440
+ },
441
+ });
442
+ dbosExec.logger.debug(`DBOS Server Registered Fork Workflow POST ${workflowForkUrl}`);
345
443
  }
346
444
  /**
347
- *
348
445
  * Register List Workflow Steps endpoint.
349
446
  * List steps for a given workflow.
350
447
  */
351
- static registerListWorkflowStepsEndpoint(dbosExec, router) {
448
+ static registerListWorkflowStepsEndpoint(dbosExec, routes) {
352
449
  const workflowStepsUrl = '/workflows/:workflow_id/steps';
353
- const workflowStepsHandler = async (koaCtxt) => {
354
- const workflowId = koaCtxt.params.workflow_id;
355
- const steps = await dbosExec.listWorkflowSteps(workflowId);
356
- koaCtxt.body = steps?.map((step) => ({
357
- function_name: step.name,
358
- function_id: step.functionID,
359
- output: step.output ? utils_1.DBOSJSON.stringify(step.output) : undefined,
360
- error: step.error ? utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(step.error)) : undefined,
361
- child_workflow_id: step.childWorkflowID,
362
- }));
363
- koaCtxt.status = 200;
364
- };
365
- router.get(workflowStepsUrl, workflowStepsHandler);
450
+ routes.push({
451
+ method: 'GET',
452
+ path: workflowStepsUrl,
453
+ handler: async (req, res, params) => {
454
+ const workflowId = params.workflow_id;
455
+ const steps = await dbosExec.listWorkflowSteps(workflowId);
456
+ const result = steps?.map((step) => ({
457
+ function_name: step.name,
458
+ function_id: step.functionID,
459
+ output: step.output ? utils_1.DBOSJSON.stringify(step.output) : undefined,
460
+ error: step.error ? utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(step.error)) : undefined,
461
+ child_workflow_id: step.childWorkflowID,
462
+ }));
463
+ sendJson(res, 200, result);
464
+ },
465
+ });
366
466
  dbosExec.logger.debug(`DBOS Server Registered List Workflow steps Get ${workflowStepsUrl}`);
367
467
  }
368
468
  /**
369
- *
370
469
  * Register List Workflows endpoint.
371
470
  * List workflows with optional filtering via request body.
372
471
  */
373
- static registerListWorkflowsEndpoint(dbosExec, router) {
472
+ static registerListWorkflowsEndpoint(dbosExec, routes) {
374
473
  const listWorkflowsUrl = '/workflows';
375
- const listWorkflowsHandler = async (koaCtxt) => {
376
- const body = koaCtxt.request.body;
377
- // Map request body keys to GetWorkflowsInput properties
378
- const input = {
379
- workflowIDs: body.workflow_uuids,
380
- workflowName: body.workflow_name,
381
- authenticatedUser: body.authenticated_user,
382
- startTime: body.start_time,
383
- endTime: body.end_time,
384
- status: body.status,
385
- applicationVersion: body.application_version,
386
- limit: body.limit,
387
- offset: body.offset,
388
- sortDesc: body.sort_desc,
389
- workflow_id_prefix: body.workflow_id_prefix,
390
- loadInput: body.load_input ?? false, // Default to false
391
- loadOutput: body.load_output ?? false, // Default to false
392
- };
393
- const workflows = await dbosExec.listWorkflows(input);
394
- // Map result to the underscore format.
395
- koaCtxt.body = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
396
- koaCtxt.status = 200;
397
- };
398
- router.post(listWorkflowsUrl, listWorkflowsHandler);
474
+ routes.push({
475
+ method: 'POST',
476
+ path: listWorkflowsUrl,
477
+ handler: async (req, res) => {
478
+ const body = (await parseJsonBody(req));
479
+ // Map request body keys to GetWorkflowsInput properties
480
+ const input = {
481
+ workflowIDs: body.workflow_uuids,
482
+ workflowName: body.workflow_name,
483
+ authenticatedUser: body.authenticated_user,
484
+ startTime: body.start_time,
485
+ endTime: body.end_time,
486
+ status: body.status,
487
+ applicationVersion: body.application_version,
488
+ limit: body.limit,
489
+ offset: body.offset,
490
+ sortDesc: body.sort_desc,
491
+ workflow_id_prefix: body.workflow_id_prefix,
492
+ loadInput: body.load_input ?? false,
493
+ loadOutput: body.load_output ?? false,
494
+ };
495
+ const workflows = await dbosExec.listWorkflows(input);
496
+ // Map result to the underscore format.
497
+ const result = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
498
+ sendJson(res, 200, result);
499
+ },
500
+ });
399
501
  dbosExec.logger.debug(`DBOS Server Registered List Workflows POST ${listWorkflowsUrl}`);
400
502
  }
401
503
  /**
402
- *
403
504
  * Register List Queued Workflows endpoint.
404
505
  * List queued workflows with optional filtering via request body.
405
506
  */
406
- static registerListQueuedWorkflowsEndpoint(dbosExec, router) {
507
+ static registerListQueuedWorkflowsEndpoint(dbosExec, routes) {
407
508
  const listQueuedWorkflowsUrl = '/queues';
408
- const listQueuedWorkflowsHandler = async (koaCtxt) => {
409
- const body = koaCtxt.request.body;
410
- // Map request body keys to GetQueuedWorkflowsInput properties
411
- const input = {
412
- workflowName: body.workflow_name,
413
- startTime: body.start_time,
414
- endTime: body.end_time,
415
- status: body.status,
416
- queueName: body.queue_name,
417
- limit: body.limit,
418
- offset: body.offset,
419
- sortDesc: body.sort_desc,
420
- loadInput: body.load_input ?? false, // Default to false
421
- };
422
- const workflows = await dbosExec.listQueuedWorkflows(input);
423
- // Map result to the underscore format.
424
- koaCtxt.body = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
425
- koaCtxt.status = 200;
426
- };
427
- router.post(listQueuedWorkflowsUrl, listQueuedWorkflowsHandler);
509
+ routes.push({
510
+ method: 'POST',
511
+ path: listQueuedWorkflowsUrl,
512
+ handler: async (req, res) => {
513
+ const body = (await parseJsonBody(req));
514
+ // Map request body keys to GetQueuedWorkflowsInput properties
515
+ const input = {
516
+ workflowName: body.workflow_name,
517
+ startTime: body.start_time,
518
+ endTime: body.end_time,
519
+ status: body.status,
520
+ queueName: body.queue_name,
521
+ limit: body.limit,
522
+ offset: body.offset,
523
+ sortDesc: body.sort_desc,
524
+ loadInput: body.load_input ?? false,
525
+ };
526
+ const workflows = await dbosExec.listQueuedWorkflows(input);
527
+ // Map result to the underscore format.
528
+ const result = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
529
+ sendJson(res, 200, result);
530
+ },
531
+ });
428
532
  dbosExec.logger.debug(`DBOS Server Registered List Queued Workflows POST ${listQueuedWorkflowsUrl}`);
429
533
  }
430
534
  /**
431
- *
432
535
  * Register Get Workflow endpoint.
433
536
  * Get detailed information about a specific workflow by ID.
434
537
  */
435
- static registerGetWorkflowEndpoint(dbosExec, router) {
538
+ static registerGetWorkflowEndpoint(dbosExec, routes) {
436
539
  const getWorkflowUrl = '/workflows/:workflow_id';
437
- const getWorkflowHandler = async (koaCtxt) => {
438
- const workflowId = koaCtxt.params.workflow_id;
439
- const workflow = await dbosExec.getWorkflowStatus(workflowId);
440
- if (workflow) {
441
- koaCtxt.body = new protocol.WorkflowsOutput(workflow);
442
- koaCtxt.status = 200;
443
- }
444
- else {
445
- koaCtxt.status = 404;
446
- koaCtxt.body = { error: `Workflow ${workflowId} not found` };
447
- }
448
- };
449
- router.get(getWorkflowUrl, getWorkflowHandler);
540
+ routes.push({
541
+ method: 'GET',
542
+ path: getWorkflowUrl,
543
+ handler: async (req, res, params) => {
544
+ const workflowId = params.workflow_id;
545
+ const workflow = await dbosExec.getWorkflowStatus(workflowId);
546
+ if (workflow) {
547
+ const result = new protocol.WorkflowsOutput(workflow);
548
+ sendJson(res, 200, result);
549
+ }
550
+ else {
551
+ sendJson(res, 404, { error: `Workflow ${workflowId} not found` });
552
+ }
553
+ },
554
+ });
450
555
  dbosExec.logger.debug(`DBOS Server Registered Get Workflow GET ${getWorkflowUrl}`);
451
556
  }
452
557
  }