@dbos-inc/dbos-sdk 3.6.3-preview → 3.6.7-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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/src/{httpServer/server.d.ts → adminserver.d.ts} +30 -53
  3. package/dist/src/adminserver.d.ts.map +1 -0
  4. package/dist/src/adminserver.js +559 -0
  5. package/dist/src/adminserver.js.map +1 -0
  6. package/dist/src/{dbos-runtime → cli}/cli.d.ts +1 -1
  7. package/dist/src/cli/cli.d.ts.map +1 -0
  8. package/dist/src/{dbos-runtime → cli}/cli.js +44 -25
  9. package/dist/src/cli/cli.js.map +1 -0
  10. package/dist/src/cli/commands.d.ts.map +1 -0
  11. package/dist/src/cli/commands.js.map +1 -0
  12. package/dist/src/cli/docker_pg_helper.d.ts.map +1 -0
  13. package/dist/src/cli/docker_pg_helper.js.map +1 -0
  14. package/dist/src/cli/migrate.d.ts +3 -0
  15. package/dist/src/cli/migrate.d.ts.map +1 -0
  16. package/dist/src/{dbos-runtime → cli}/migrate.js +1 -29
  17. package/dist/src/cli/migrate.js.map +1 -0
  18. package/dist/src/client.d.ts +2 -4
  19. package/dist/src/client.d.ts.map +1 -1
  20. package/dist/src/client.js +6 -19
  21. package/dist/src/client.js.map +1 -1
  22. package/dist/src/conductor/conductor.js +1 -1
  23. package/dist/src/conductor/conductor.js.map +1 -1
  24. package/dist/src/{dbos-runtime/config.d.ts → config.d.ts} +2 -13
  25. package/dist/src/config.d.ts.map +1 -0
  26. package/dist/src/{dbos-runtime/config.js → config.js} +12 -72
  27. package/dist/src/config.js.map +1 -0
  28. package/dist/src/context.d.ts +0 -4
  29. package/dist/src/context.d.ts.map +1 -1
  30. package/dist/src/context.js.map +1 -1
  31. package/dist/src/datasource.js +1 -1
  32. package/dist/src/datasource.js.map +1 -1
  33. package/dist/src/dbos-executor.d.ts +2 -28
  34. package/dist/src/dbos-executor.d.ts.map +1 -1
  35. package/dist/src/dbos-executor.js +14 -765
  36. package/dist/src/dbos-executor.js.map +1 -1
  37. package/dist/src/dbos.d.ts +3 -180
  38. package/dist/src/dbos.d.ts.map +1 -1
  39. package/dist/src/dbos.js +10 -385
  40. package/dist/src/dbos.js.map +1 -1
  41. package/dist/src/decorators.d.ts +3 -21
  42. package/dist/src/decorators.d.ts.map +1 -1
  43. package/dist/src/decorators.js +12 -46
  44. package/dist/src/decorators.js.map +1 -1
  45. package/dist/src/error.d.ts +0 -11
  46. package/dist/src/error.d.ts.map +1 -1
  47. package/dist/src/error.js +1 -21
  48. package/dist/src/error.js.map +1 -1
  49. package/dist/src/index.d.ts +4 -12
  50. package/dist/src/index.d.ts.map +1 -1
  51. package/dist/src/index.js +6 -43
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/src/system_database.d.ts +0 -2
  54. package/dist/src/system_database.d.ts.map +1 -1
  55. package/dist/src/system_database.js +147 -121
  56. package/dist/src/system_database.js.map +1 -1
  57. package/dist/src/utils.d.ts +0 -1
  58. package/dist/src/utils.d.ts.map +1 -1
  59. package/dist/src/utils.js +18 -20
  60. package/dist/src/utils.js.map +1 -1
  61. package/dist/src/wfqueue.d.ts +0 -2
  62. package/dist/src/wfqueue.d.ts.map +1 -1
  63. package/dist/src/wfqueue.js.map +1 -1
  64. package/dist/src/workflow.d.ts +0 -8
  65. package/dist/src/workflow.d.ts.map +1 -1
  66. package/dist/src/workflow.js.map +1 -1
  67. package/dist/src/{dbos-runtime/workflow_management.d.ts → workflow_management.d.ts} +4 -6
  68. package/dist/src/workflow_management.d.ts.map +1 -0
  69. package/dist/src/{dbos-runtime/workflow_management.js → workflow_management.js} +5 -26
  70. package/dist/src/workflow_management.js.map +1 -0
  71. package/dist/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +4 -25
  73. package/dist/schemas/user_db_schema.d.ts +0 -15
  74. package/dist/schemas/user_db_schema.d.ts.map +0 -1
  75. package/dist/schemas/user_db_schema.js +0 -33
  76. package/dist/schemas/user_db_schema.js.map +0 -1
  77. package/dist/src/dbos-runtime/cli.d.ts.map +0 -1
  78. package/dist/src/dbos-runtime/cli.js.map +0 -1
  79. package/dist/src/dbos-runtime/commands.d.ts.map +0 -1
  80. package/dist/src/dbos-runtime/commands.js.map +0 -1
  81. package/dist/src/dbos-runtime/config.d.ts.map +0 -1
  82. package/dist/src/dbos-runtime/config.js.map +0 -1
  83. package/dist/src/dbos-runtime/docker_pg_helper.d.ts.map +0 -1
  84. package/dist/src/dbos-runtime/docker_pg_helper.js.map +0 -1
  85. package/dist/src/dbos-runtime/migrate.d.ts +0 -3
  86. package/dist/src/dbos-runtime/migrate.d.ts.map +0 -1
  87. package/dist/src/dbos-runtime/migrate.js.map +0 -1
  88. package/dist/src/dbos-runtime/workflow_management.d.ts.map +0 -1
  89. package/dist/src/dbos-runtime/workflow_management.js.map +0 -1
  90. package/dist/src/httpServer/handler.d.ts +0 -13
  91. package/dist/src/httpServer/handler.d.ts.map +0 -1
  92. package/dist/src/httpServer/handler.js +0 -25
  93. package/dist/src/httpServer/handler.js.map +0 -1
  94. package/dist/src/httpServer/handlerTypes.d.ts +0 -14
  95. package/dist/src/httpServer/handlerTypes.d.ts.map +0 -1
  96. package/dist/src/httpServer/handlerTypes.js +0 -22
  97. package/dist/src/httpServer/handlerTypes.js.map +0 -1
  98. package/dist/src/httpServer/middleware.d.ts +0 -74
  99. package/dist/src/httpServer/middleware.d.ts.map +0 -1
  100. package/dist/src/httpServer/middleware.js +0 -108
  101. package/dist/src/httpServer/middleware.js.map +0 -1
  102. package/dist/src/httpServer/server.d.ts.map +0 -1
  103. package/dist/src/httpServer/server.js +0 -737
  104. package/dist/src/httpServer/server.js.map +0 -1
  105. package/dist/src/paramdecorators.d.ts +0 -39
  106. package/dist/src/paramdecorators.d.ts.map +0 -1
  107. package/dist/src/paramdecorators.js +0 -365
  108. package/dist/src/paramdecorators.js.map +0 -1
  109. package/dist/src/procedure.d.ts +0 -18
  110. package/dist/src/procedure.d.ts.map +0 -1
  111. package/dist/src/procedure.js +0 -3
  112. package/dist/src/procedure.js.map +0 -1
  113. package/dist/src/transaction.d.ts +0 -19
  114. package/dist/src/transaction.d.ts.map +0 -1
  115. package/dist/src/transaction.js +0 -11
  116. package/dist/src/transaction.js.map +0 -1
  117. package/dist/src/user_database.d.ts +0 -176
  118. package/dist/src/user_database.d.ts.map +0 -1
  119. package/dist/src/user_database.js +0 -540
  120. package/dist/src/user_database.js.map +0 -1
  121. /package/dist/src/{dbos-runtime → cli}/commands.d.ts +0 -0
  122. /package/dist/src/{dbos-runtime → cli}/commands.js +0 -0
  123. /package/dist/src/{dbos-runtime → cli}/docker_pg_helper.d.ts +0 -0
  124. /package/dist/src/{dbos-runtime → cli}/docker_pg_helper.js +0 -0
@@ -1,737 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.DBOSHttpServer = 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"));
34
- const handlerTypes_1 = require("./handlerTypes");
35
- const error_1 = require("../error");
36
- const dbos_executor_1 = require("../dbos-executor");
37
- const middleware_1 = require("./middleware");
38
- const api_1 = require("@opentelemetry/api");
39
- const net = __importStar(require("net"));
40
- const perf_hooks_1 = require("perf_hooks");
41
- const utils_1 = require("../utils");
42
- const context_1 = require("../context");
43
- const wfqueue_1 = require("../wfqueue");
44
- const serialize_error_1 = require("serialize-error");
45
- const workflow_management_1 = require("../dbos-runtime/workflow_management");
46
- const core_1 = require("@opentelemetry/core");
47
- const dbos_1 = require("../dbos");
48
- const protocol = __importStar(require("../conductor/protocol"));
49
- const decorators_1 = require("../decorators");
50
- exports.WorkflowUUIDHeader = 'dbos-idempotency-key';
51
- exports.WorkflowRecoveryUrl = '/dbos-workflow-recovery';
52
- exports.HealthUrl = '/dbos-healthz';
53
- exports.PerfUrl = '/dbos-perf';
54
- // FIXME this should be /dbos-deactivate to be consistent with other endpoints.
55
- exports.DeactivateUrl = '/deactivate';
56
- exports.WorkflowQueuesMetadataUrl = '/dbos-workflow-queues-metadata';
57
- class DBOSHttpServer {
58
- dbosExec;
59
- app;
60
- adminApp;
61
- applicationRouter;
62
- logger;
63
- static nRegisteredEndpoints = 0;
64
- static instance = undefined;
65
- /**
66
- * Create a Koa app.
67
- * @param dbosExec User pass in an DBOS workflow executor instance.
68
- * TODO: maybe call dbosExec.init() somewhere in this class?
69
- */
70
- constructor(dbosExec) {
71
- this.dbosExec = dbosExec;
72
- this.applicationRouter = new router_1.default();
73
- this.logger = dbosExec.logger;
74
- this.app = new koa_1.default();
75
- this.adminApp = DBOSHttpServer.setupAdminApp(this.dbosExec);
76
- DBOSHttpServer.registerDecoratedEndpoints(this.dbosExec, this.applicationRouter, this.app);
77
- this.app.use(this.applicationRouter.routes()).use(this.applicationRouter.allowedMethods());
78
- DBOSHttpServer.instance = this;
79
- }
80
- static setupAdminApp(dbosExec) {
81
- const adminRouter = new router_1.default();
82
- const adminApp = new koa_1.default();
83
- adminApp.use((0, bodyparser_1.bodyParser)());
84
- adminApp.use((0, cors_1.default)());
85
- // Register HTTP endpoints.
86
- DBOSHttpServer.registerHealthEndpoint(dbosExec, adminRouter);
87
- DBOSHttpServer.registerRecoveryEndpoint(dbosExec, adminRouter);
88
- DBOSHttpServer.registerPerfEndpoint(dbosExec, adminRouter);
89
- DBOSHttpServer.registerDeactivateEndpoint(dbosExec, adminRouter);
90
- DBOSHttpServer.registerCancelWorkflowEndpoint(dbosExec, adminRouter);
91
- DBOSHttpServer.registerResumeWorkflowEndpoint(dbosExec, adminRouter);
92
- DBOSHttpServer.registerRestartWorkflowEndpoint(dbosExec, adminRouter);
93
- DBOSHttpServer.registerQueueMetadataEndpoint(dbosExec, adminRouter);
94
- DBOSHttpServer.registerListWorkflowStepsEndpoint(dbosExec, adminRouter);
95
- DBOSHttpServer.registerListWorkflowsEndpoint(dbosExec, adminRouter);
96
- DBOSHttpServer.registerListQueuedWorkflowsEndpoint(dbosExec, adminRouter);
97
- DBOSHttpServer.registerGetWorkflowEndpoint(dbosExec, adminRouter);
98
- DBOSHttpServer.registerForkWorkflowEndpoint(dbosExec, adminRouter);
99
- DBOSHttpServer.registerGarbageCollectEndpoint(dbosExec, adminRouter);
100
- DBOSHttpServer.registerGlobalTimeoutEndpoint(dbosExec, adminRouter);
101
- adminApp.use(adminRouter.routes()).use(adminRouter.allowedMethods());
102
- return adminApp;
103
- }
104
- async appListen(port) {
105
- await DBOSHttpServer.checkPortAvailabilityIPv4Ipv6(port, this.logger);
106
- const appServer = DBOSHttpServer.nRegisteredEndpoints === 0
107
- ? undefined
108
- : this.app.listen(port, () => {
109
- this.logger.info(`DBOS Server is running at http://localhost:${port}`);
110
- });
111
- return appServer;
112
- }
113
- static async checkPortAvailabilityIPv4Ipv6(port, logger) {
114
- try {
115
- await this.checkPortAvailability(port, '127.0.0.1');
116
- }
117
- catch (error) {
118
- const err = error;
119
- if (err.code === 'EADDRINUSE') {
120
- logger.warn(`Port ${port} is already used for IPv4 address "127.0.0.1". Please use the -p option to choose another port.\n${err.message}`);
121
- throw error;
122
- }
123
- else {
124
- logger.warn(`Error occurred while checking port availability for IPv4 address "127.0.0.1" : ${err.code}\n${err.message}`);
125
- }
126
- }
127
- try {
128
- await this.checkPortAvailability(port, '::1');
129
- }
130
- catch (error) {
131
- const err = error;
132
- if (err.code === 'EADDRINUSE') {
133
- logger.warn(`Port ${port} is already used for IPv6 address "::1". Please use the -p option to choose another port.\n${err.message}`);
134
- throw error;
135
- }
136
- else {
137
- logger.warn(`Error occurred while checking port availability for IPv6 address "::1" : ${err.code}\n${err.message}`);
138
- }
139
- }
140
- }
141
- static async checkPortAvailability(port, host) {
142
- return new Promise((resolve, reject) => {
143
- const server = new net.Server();
144
- server.on('error', (error) => {
145
- reject(error);
146
- });
147
- server.on('listening', () => {
148
- server.close();
149
- resolve();
150
- });
151
- server.listen({ port: port, host: host }, () => {
152
- resolve();
153
- });
154
- });
155
- }
156
- /**
157
- * Health check endpoint.
158
- */
159
- static registerHealthEndpoint(dbosExec, router) {
160
- // Handler function that parses request for recovery.
161
- const healthHandler = async (koaCtxt, koaNext) => {
162
- koaCtxt.body = 'healthy';
163
- await koaNext();
164
- };
165
- router.get(exports.HealthUrl, healthHandler);
166
- dbosExec.logger.debug(`DBOS Server Registered Healthz GET ${exports.HealthUrl}`);
167
- }
168
- /**
169
- * Register workflow queue metadata endpoint.
170
- */
171
- static registerQueueMetadataEndpoint(dbosExec, router) {
172
- const queueMetadataHandler = async (koaCtxt, koaNext) => {
173
- const queueDetailsArray = [];
174
- wfqueue_1.wfQueueRunner.wfQueuesByName.forEach((q, qn) => {
175
- queueDetailsArray.push({
176
- name: qn,
177
- concurrency: q.concurrency,
178
- workerConcurrency: q.workerConcurrency,
179
- rateLimit: q.rateLimit,
180
- });
181
- });
182
- koaCtxt.body = queueDetailsArray;
183
- await koaNext();
184
- };
185
- router.get(exports.WorkflowQueuesMetadataUrl, queueMetadataHandler);
186
- dbosExec.logger.debug(`DBOS Server Registered Queue Metadata GET ${exports.WorkflowQueuesMetadataUrl}`);
187
- }
188
- /**
189
- * Register workflow recovery endpoint.
190
- * Receives a list of executor IDs and returns a list of workflowUUIDs.
191
- */
192
- static registerRecoveryEndpoint(dbosExec, router) {
193
- // Handler function that parses request for recovery.
194
- const recoveryHandler = async (koaCtxt, koaNext) => {
195
- const executorIDs = koaCtxt.request.body;
196
- dbosExec.logger.info('Recovering workflows for executors: ' + executorIDs.toString());
197
- const recoverHandles = await dbosExec.recoverPendingWorkflows(executorIDs);
198
- // Return a list of workflowUUIDs being recovered.
199
- koaCtxt.body = await Promise.allSettled(recoverHandles.map((i) => i.workflowID)).then((results) => results.filter((i) => i.status === 'fulfilled').map((i) => i.value));
200
- await koaNext();
201
- };
202
- router.post(exports.WorkflowRecoveryUrl, recoveryHandler);
203
- dbosExec.logger.debug(`DBOS Server Registered Recovery POST ${exports.WorkflowRecoveryUrl}`);
204
- }
205
- /**
206
- * Register performance endpoint.
207
- * Returns information on VM performance since last call.
208
- */
209
- static registerPerfEndpoint(dbosExec, router) {
210
- let lastELU = perf_hooks_1.performance.eventLoopUtilization();
211
- const perfHandler = async (koaCtxt, koaNext) => {
212
- const currELU = perf_hooks_1.performance.eventLoopUtilization();
213
- const elu = perf_hooks_1.performance.eventLoopUtilization(currELU, lastELU);
214
- koaCtxt.body = elu;
215
- lastELU = currELU;
216
- await koaNext();
217
- };
218
- router.get(exports.PerfUrl, perfHandler);
219
- dbosExec.logger.debug(`DBOS Server Registered Perf GET ${exports.PerfUrl}`);
220
- }
221
- /**
222
- * Register Deactivate endpoint.
223
- * Deactivate consumers so that they don't start new workflows.
224
- *
225
- */
226
- static isDeactivated = false;
227
- static registerDeactivateEndpoint(dbosExec, router) {
228
- const deactivateHandler = async (koaCtxt, koaNext) => {
229
- if (!DBOSHttpServer.isDeactivated) {
230
- 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.`);
231
- DBOSHttpServer.isDeactivated = true;
232
- }
233
- await dbosExec.deactivateEventReceivers(false);
234
- koaCtxt.body = 'Deactivated';
235
- await koaNext();
236
- };
237
- router.get(exports.DeactivateUrl, deactivateHandler);
238
- dbosExec.logger.debug(`DBOS Server Registered Deactivate GET ${exports.DeactivateUrl}`);
239
- }
240
- static registerGarbageCollectEndpoint(dbosExec, router) {
241
- const url = '/dbos-garbage-collect';
242
- const handler = async (koaCtxt) => {
243
- const body = koaCtxt.request.body;
244
- await dbosExec.systemDatabase.garbageCollect(body.cutoff_epoch_timestamp_ms, body.rows_threshold);
245
- koaCtxt.status = 204;
246
- };
247
- router.post(url, handler);
248
- }
249
- static registerGlobalTimeoutEndpoint(dbosExec, router) {
250
- const url = '/dbos-global-timeout';
251
- const handler = async (koaCtxt) => {
252
- const body = koaCtxt.request.body;
253
- await (0, workflow_management_1.globalTimeout)(dbosExec.systemDatabase, body.cutoff_epoch_timestamp_ms);
254
- koaCtxt.status = 204;
255
- };
256
- router.post(url, handler);
257
- }
258
- /**
259
- *
260
- * Register Cancel Workflow endpoint.
261
- * Cancels a workflow by setting its status to CANCELLED.
262
- */
263
- static registerCancelWorkflowEndpoint(dbosExec, router) {
264
- const workflowCancelUrl = '/workflows/:workflow_id/cancel';
265
- const workflowCancelHandler = async (koaCtxt) => {
266
- const workflowId = koaCtxt.params.workflow_id;
267
- await dbosExec.cancelWorkflow(workflowId);
268
- koaCtxt.status = 204;
269
- };
270
- router.post(workflowCancelUrl, workflowCancelHandler);
271
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowCancelUrl}`);
272
- }
273
- /**
274
- *
275
- * Register Resume Workflow endpoint.
276
- * Resume a workflow.
277
- */
278
- static registerResumeWorkflowEndpoint(dbosExec, router) {
279
- const workflowResumeUrl = '/workflows/:workflow_id/resume';
280
- const workflowResumeHandler = async (koaCtxt) => {
281
- const workflowId = koaCtxt.params.workflow_id;
282
- dbosExec.logger.info(`Resuming workflow with ID: ${workflowId}`);
283
- try {
284
- await dbosExec.resumeWorkflow(workflowId);
285
- }
286
- catch (e) {
287
- let errorMessage = '';
288
- if (e instanceof error_1.DBOSError) {
289
- errorMessage = e.message;
290
- }
291
- else {
292
- errorMessage = `Unknown error`;
293
- }
294
- dbosExec.logger.error(`Error resuming workflow ${workflowId}: ${errorMessage}`);
295
- koaCtxt.status = 500;
296
- koaCtxt.body = {
297
- error: `Error resuming workflow ${workflowId}: ${errorMessage}`,
298
- };
299
- return;
300
- }
301
- koaCtxt.status = 204;
302
- };
303
- router.post(workflowResumeUrl, workflowResumeHandler);
304
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
305
- }
306
- /**
307
- *
308
- * Register Restart Workflow endpoint.
309
- * Restart a workflow.
310
- */
311
- static registerRestartWorkflowEndpoint(dbosExec, router) {
312
- const workflowResumeUrl = '/workflows/:workflow_id/restart';
313
- const workflowRestartHandler = async (koaCtxt) => {
314
- const workflowId = koaCtxt.params.workflow_id;
315
- dbosExec.logger.info(`Restarting workflow: ${workflowId} with a new id`);
316
- const workflowID = await dbosExec.forkWorkflow(workflowId, 0);
317
- koaCtxt.body = {
318
- workflow_id: workflowID,
319
- };
320
- koaCtxt.status = 200;
321
- };
322
- router.post(workflowResumeUrl, workflowRestartHandler);
323
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
324
- }
325
- /**
326
- *
327
- * Register Fork Workflow endpoint.
328
- *
329
- */
330
- static registerForkWorkflowEndpoint(dbosExec, router) {
331
- const workflowResumeUrl = '/workflows/:workflow_id/fork';
332
- const workflowForkHandler = async (koaCtxt) => {
333
- const workflowId = koaCtxt.params.workflow_id;
334
- const body = koaCtxt.request.body;
335
- if (body.start_step === undefined) {
336
- throw new error_1.DBOSDataValidationError('Missing start_step in request body');
337
- }
338
- dbosExec.logger.info(`Forking workflow: ${workflowId} from step ${body.start_step} with a new id`);
339
- try {
340
- const workflowID = await dbosExec.forkWorkflow(workflowId, body.start_step, {
341
- newWorkflowID: body.new_workflow_id,
342
- applicationVersion: body.application_version,
343
- timeoutMS: body.timeout_ms,
344
- });
345
- koaCtxt.body = {
346
- workflow_id: workflowID,
347
- };
348
- }
349
- catch (e) {
350
- let errorMessage = '';
351
- if (e instanceof error_1.DBOSError) {
352
- errorMessage = e.message;
353
- }
354
- else {
355
- errorMessage = `Unknown error`;
356
- }
357
- dbosExec.logger.error(`Error forking workflow ${workflowId}: ${errorMessage}`);
358
- koaCtxt.status = 500;
359
- koaCtxt.body = {
360
- error: `Error forking workflow ${workflowId}: ${errorMessage}`,
361
- };
362
- return;
363
- }
364
- dbosExec.logger.info(`Forked workflow: ${workflowId} with a new id`);
365
- koaCtxt.status = 200;
366
- };
367
- router.post(workflowResumeUrl, workflowForkHandler);
368
- dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
369
- }
370
- /**
371
- *
372
- * Register List Workflow Steps endpoint.
373
- * List steps for a given workflow.
374
- */
375
- static registerListWorkflowStepsEndpoint(dbosExec, router) {
376
- const workflowStepsUrl = '/workflows/:workflow_id/steps';
377
- const workflowStepsHandler = async (koaCtxt) => {
378
- const workflowId = koaCtxt.params.workflow_id;
379
- const steps = await dbosExec.listWorkflowSteps(workflowId);
380
- koaCtxt.body = steps?.map((step) => ({
381
- function_name: step.name,
382
- function_id: step.functionID,
383
- output: step.output ? utils_1.DBOSJSON.stringify(step.output) : undefined,
384
- error: step.error ? utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(step.error)) : undefined,
385
- child_workflow_id: step.childWorkflowID,
386
- }));
387
- koaCtxt.status = 200;
388
- };
389
- router.get(workflowStepsUrl, workflowStepsHandler);
390
- dbosExec.logger.debug(`DBOS Server Registered List Workflow steps Get ${workflowStepsUrl}`);
391
- }
392
- /**
393
- *
394
- * Register List Workflows endpoint.
395
- * List workflows with optional filtering via request body.
396
- */
397
- static registerListWorkflowsEndpoint(dbosExec, router) {
398
- const listWorkflowsUrl = '/workflows';
399
- const listWorkflowsHandler = async (koaCtxt) => {
400
- const body = koaCtxt.request.body;
401
- // Map request body keys to GetWorkflowsInput properties
402
- const input = {
403
- workflowIDs: body.workflow_uuids,
404
- workflowName: body.workflow_name,
405
- authenticatedUser: body.authenticated_user,
406
- startTime: body.start_time,
407
- endTime: body.end_time,
408
- status: body.status,
409
- applicationVersion: body.application_version,
410
- limit: body.limit,
411
- offset: body.offset,
412
- sortDesc: body.sort_desc,
413
- workflow_id_prefix: body.workflow_id_prefix,
414
- loadInput: body.load_input ?? false, // Default to false
415
- loadOutput: body.load_output ?? false, // Default to false
416
- };
417
- const workflows = await dbosExec.listWorkflows(input);
418
- // Map result to the underscore format.
419
- koaCtxt.body = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
420
- koaCtxt.status = 200;
421
- };
422
- router.post(listWorkflowsUrl, listWorkflowsHandler);
423
- dbosExec.logger.debug(`DBOS Server Registered List Workflows POST ${listWorkflowsUrl}`);
424
- }
425
- /**
426
- *
427
- * Register List Queued Workflows endpoint.
428
- * List queued workflows with optional filtering via request body.
429
- */
430
- static registerListQueuedWorkflowsEndpoint(dbosExec, router) {
431
- const listQueuedWorkflowsUrl = '/queues';
432
- const listQueuedWorkflowsHandler = async (koaCtxt) => {
433
- const body = koaCtxt.request.body;
434
- // Map request body keys to GetQueuedWorkflowsInput properties
435
- const input = {
436
- workflowName: body.workflow_name,
437
- startTime: body.start_time,
438
- endTime: body.end_time,
439
- status: body.status,
440
- queueName: body.queue_name,
441
- limit: body.limit,
442
- offset: body.offset,
443
- sortDesc: body.sort_desc,
444
- loadInput: body.load_input ?? false, // Default to false
445
- };
446
- const workflows = await dbosExec.listQueuedWorkflows(input);
447
- // Map result to the underscore format.
448
- koaCtxt.body = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
449
- koaCtxt.status = 200;
450
- };
451
- router.post(listQueuedWorkflowsUrl, listQueuedWorkflowsHandler);
452
- dbosExec.logger.debug(`DBOS Server Registered List Queued Workflows POST ${listQueuedWorkflowsUrl}`);
453
- }
454
- /**
455
- *
456
- * Register Get Workflow endpoint.
457
- * Get detailed information about a specific workflow by ID.
458
- */
459
- static registerGetWorkflowEndpoint(dbosExec, router) {
460
- const getWorkflowUrl = '/workflows/:workflow_id';
461
- const getWorkflowHandler = async (koaCtxt) => {
462
- const workflowId = koaCtxt.params.workflow_id;
463
- const workflow = await dbosExec.getWorkflowStatus(workflowId);
464
- if (workflow) {
465
- koaCtxt.body = new protocol.WorkflowsOutput(workflow);
466
- koaCtxt.status = 200;
467
- }
468
- else {
469
- koaCtxt.status = 404;
470
- koaCtxt.body = { error: `Workflow ${workflowId} not found` };
471
- }
472
- };
473
- router.get(getWorkflowUrl, getWorkflowHandler);
474
- dbosExec.logger.debug(`DBOS Server Registered Get Workflow GET ${getWorkflowUrl}`);
475
- }
476
- /**
477
- * Register decorated functions as HTTP endpoints.
478
- */
479
- static registerDecoratedEndpoints(dbosExec, router, app) {
480
- const globalMiddlewares = new Set();
481
- // Register user declared endpoints, wrap around the endpoint with request parsing and response.
482
- DBOSHttpServer.nRegisteredEndpoints = 0;
483
- (0, decorators_1.getAllRegisteredFunctions)().forEach((registeredOperation) => {
484
- const ro = registeredOperation;
485
- if (!ro.apiURL)
486
- return;
487
- if (ro.isInstance) {
488
- dbosExec.logger.warn(`Operation ${ro.className}/${ro.name} is registered with an endpoint (${ro.apiURL}) but cannot be invoked.`);
489
- return;
490
- }
491
- ++DBOSHttpServer.nRegisteredEndpoints;
492
- const defaults = ro.defaults;
493
- // Check if we need to apply a custom CORS
494
- if (defaults.koaCors) {
495
- router.all(ro.apiURL, defaults.koaCors); // Use router.all to register with all methods including preflight requests
496
- }
497
- else {
498
- if (dbosExec.config.http?.cors_middleware ?? true) {
499
- router.all(ro.apiURL, (0, cors_1.default)({
500
- credentials: dbosExec.config.http?.credentials ?? true,
501
- origin: (o) => {
502
- const whitelist = dbosExec.config.http?.allowed_origins;
503
- const origin = o.request.header.origin ?? '*';
504
- if (whitelist && whitelist.length > 0) {
505
- return whitelist.includes(origin) ? origin : '';
506
- }
507
- return o.request.header.origin || '*';
508
- },
509
- allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
510
- allowHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization'],
511
- }));
512
- }
513
- }
514
- // Check if we need to apply any Koa global middleware.
515
- if (defaults?.koaGlobalMiddlewares) {
516
- defaults.koaGlobalMiddlewares.forEach((koaMiddleware) => {
517
- if (globalMiddlewares.has(koaMiddleware)) {
518
- return;
519
- }
520
- dbosExec.logger.debug(`DBOS Server applying middleware ${koaMiddleware.name} globally`);
521
- globalMiddlewares.add(koaMiddleware);
522
- app.use(koaMiddleware);
523
- });
524
- }
525
- // Wrapper function that parses request and send response.
526
- const wrappedHandler = async (koaCtxt, koaNext) => {
527
- const requestID = (0, middleware_1.getOrGenerateRequestID)(koaCtxt.request.headers);
528
- koaCtxt.set(middleware_1.RequestIDHeader, requestID);
529
- const httpTracer = new core_1.W3CTraceContextPropagator();
530
- const extractedSpanContext = api_1.trace.getSpanContext(httpTracer.extract(api_1.ROOT_CONTEXT, koaCtxt.request.headers, api_1.defaultTextMapGetter));
531
- let span;
532
- const spanAttributes = {
533
- operationType: dbos_executor_1.OperationType.HANDLER,
534
- requestID: requestID,
535
- requestIP: koaCtxt.request.ip,
536
- requestURL: koaCtxt.request.url,
537
- requestMethod: koaCtxt.request.method,
538
- };
539
- if (extractedSpanContext === undefined) {
540
- span = dbosExec.tracer.startSpan(koaCtxt.url, spanAttributes);
541
- }
542
- else {
543
- extractedSpanContext.isRemote = true;
544
- span = dbosExec.tracer.startSpanWithContext(extractedSpanContext, koaCtxt.url, spanAttributes);
545
- }
546
- const dctx = {
547
- request: {
548
- headers: koaCtxt.request.headers,
549
- rawHeaders: koaCtxt.req.rawHeaders,
550
- params: koaCtxt.params,
551
- body: koaCtxt.request.body,
552
- rawBody: koaCtxt.request.rawBody,
553
- query: koaCtxt.request.query,
554
- querystring: koaCtxt.request.querystring,
555
- url: koaCtxt.request.url,
556
- ip: koaCtxt.request.ip,
557
- requestID: requestID,
558
- },
559
- koaContext: koaCtxt,
560
- operationType: dbos_executor_1.OperationType.HANDLER,
561
- };
562
- try {
563
- // Check for auth first
564
- if (defaults?.authMiddleware) {
565
- await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
566
- await (0, context_1.runWithTopContext)(dctx, async () => {
567
- const res = await defaults.authMiddleware({
568
- name: ro.name,
569
- requiredRole: ro.getRequiredRoles(),
570
- koaContext: koaCtxt,
571
- logger: dbosExec.ctxLogger,
572
- span,
573
- query: (query, ...args) => {
574
- return dbosExec.queryUserDbFunction(query, ...args);
575
- },
576
- });
577
- if (res) {
578
- dctx.authenticatedUser = res.authenticatedUser;
579
- dctx.authenticatedRoles = res.authenticatedRoles;
580
- }
581
- });
582
- });
583
- }
584
- // Parse the arguments.
585
- const args = [];
586
- ro.args.forEach((marg) => {
587
- marg.argSource = marg.argSource ?? handlerTypes_1.ArgSources.DEFAULT; // Assign a default value.
588
- let foundArg = undefined;
589
- const isQueryMethod = ro.apiType === handlerTypes_1.APITypes.GET || ro.apiType === handlerTypes_1.APITypes.DELETE;
590
- const isBodyMethod = ro.apiType === handlerTypes_1.APITypes.POST || ro.apiType === handlerTypes_1.APITypes.PUT || ro.apiType === handlerTypes_1.APITypes.PATCH;
591
- if ((isQueryMethod && marg.argSource === handlerTypes_1.ArgSources.DEFAULT) || marg.argSource === handlerTypes_1.ArgSources.QUERY) {
592
- foundArg = koaCtxt.request.query[marg.name];
593
- if (foundArg !== undefined) {
594
- args.push(foundArg);
595
- }
596
- else {
597
- if (marg.argSource === handlerTypes_1.ArgSources.DEFAULT && koaCtxt.request.body) {
598
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
599
- foundArg = koaCtxt.request.body[marg.name];
600
- if (foundArg !== undefined) {
601
- args.push(foundArg);
602
- }
603
- }
604
- }
605
- }
606
- else if ((isBodyMethod && marg.argSource === handlerTypes_1.ArgSources.DEFAULT) || marg.argSource === handlerTypes_1.ArgSources.BODY) {
607
- if (koaCtxt.request.body) {
608
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
609
- foundArg = koaCtxt.request.body[marg.name];
610
- if (foundArg !== undefined) {
611
- args.push(foundArg);
612
- }
613
- }
614
- if (!foundArg && marg.argSource === handlerTypes_1.ArgSources.DEFAULT) {
615
- foundArg = koaCtxt.request.query[marg.name];
616
- if (foundArg !== undefined) {
617
- args.push(foundArg);
618
- }
619
- }
620
- }
621
- // Try to parse the argument from the URL if nothing found.
622
- if (foundArg === undefined) {
623
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
624
- args.push(koaCtxt.params[marg.name]);
625
- }
626
- //console.log(`found arg ${marg.name} ${idx} ${args[idx-1]}`);
627
- });
628
- // Extract workflow ID from headers (if any).
629
- // We pass in the specified workflow ID to workflows and transactions, but doesn't restrict how handlers use it.
630
- dctx.idAssignedForNextWorkflow = koaCtxt.get(exports.WorkflowUUIDHeader);
631
- // Finally, invoke the transaction/workflow/plain function and properly set HTTP response.
632
- // If functions return successfully and hasn't set the body, we set the body to the function return value. The status code will be automatically set to 200 or 204 (if the body is null/undefined).
633
- // In case of an exception:
634
- // - If a client-side error is thrown, we return 400.
635
- // - If an error contains a `status` field, we return the specified status code.
636
- // - Otherwise, we return 500.
637
- // configuredInstance is currently null; we don't allow configured handlers now.
638
- const wfParams = {
639
- workflowUUID: dctx.idAssignedForNextWorkflow,
640
- configuredInstance: null,
641
- };
642
- await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
643
- await (0, context_1.runWithTopContext)(dctx, async () => {
644
- if (ro.txnConfig) {
645
- koaCtxt.body = await dbosExec.runTransactionTempWF(ro.registeredFunction, wfParams, ...args);
646
- }
647
- else if (ro.workflowConfig) {
648
- koaCtxt.body = await (await dbosExec.workflow(ro.registeredFunction, wfParams, ...args)).getResult();
649
- }
650
- else if (ro.stepConfig) {
651
- koaCtxt.body = await dbosExec.runStepTempWF(ro.registeredFunction, wfParams, ...args);
652
- }
653
- else {
654
- // Directly invoke the handler code.
655
- const cresult = await ro.invoke(undefined, [...args]);
656
- const retValue = cresult;
657
- // Set the body to the return value unless the body is already set by the handler.
658
- if (koaCtxt.body === undefined) {
659
- koaCtxt.body = retValue;
660
- }
661
- }
662
- });
663
- });
664
- span?.setStatus({ code: api_1.SpanStatusCode.OK });
665
- }
666
- catch (e) {
667
- if (e instanceof Error) {
668
- const annotated_e = e;
669
- if (annotated_e.dbos_already_logged !== true) {
670
- dbos_1.DBOS.logger.error(e);
671
- }
672
- span?.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
673
- let st = e?.status || 500;
674
- if ((0, error_1.isDataValidationError)(e)) {
675
- st = 400; // Set to 400: client-side error.
676
- }
677
- koaCtxt.status = st;
678
- koaCtxt.message = e.message;
679
- koaCtxt.body = {
680
- status: st,
681
- message: e.message,
682
- details: e,
683
- };
684
- }
685
- else {
686
- // FIXME we should have a standard, user friendly message for errors that are not instances of Error.
687
- // using stringify() will not produce a pretty output, because our format function uses stringify() too.
688
- dbos_1.DBOS.logger.error(utils_1.DBOSJSON.stringify(e));
689
- span?.setStatus({ code: api_1.SpanStatusCode.ERROR, message: utils_1.DBOSJSON.stringify(e) });
690
- koaCtxt.body = e;
691
- koaCtxt.status = 500;
692
- }
693
- }
694
- finally {
695
- httpTracer.inject(api_1.trace.setSpanContext(api_1.ROOT_CONTEXT, span.spanContext()), {
696
- context: koaCtxt,
697
- }, {
698
- set: (carrier, key, value) => {
699
- carrier.context.set(key, value);
700
- },
701
- });
702
- dbosExec.tracer.endSpan(span);
703
- await koaNext();
704
- }
705
- };
706
- // Actually register the endpoint.
707
- // Middleware functions are applied directly to router verb methods to prevent duplicate calls.
708
- const routeMiddlewares = [defaults.koaBodyParser ?? (0, bodyparser_1.bodyParser)()].concat(defaults.koaMiddlewares ?? []);
709
- switch (ro.apiType) {
710
- case handlerTypes_1.APITypes.GET:
711
- router.get(ro.apiURL, ...routeMiddlewares, wrappedHandler);
712
- dbosExec.logger.debug(`DBOS Server Registered GET ${ro.apiURL}`);
713
- break;
714
- case handlerTypes_1.APITypes.POST:
715
- router.post(ro.apiURL, ...routeMiddlewares, wrappedHandler);
716
- dbosExec.logger.debug(`DBOS Server Registered POST ${ro.apiURL}`);
717
- break;
718
- case handlerTypes_1.APITypes.PUT:
719
- router.put(ro.apiURL, ...routeMiddlewares, wrappedHandler);
720
- dbosExec.logger.debug(`DBOS Server Registered PUT ${ro.apiURL}`);
721
- break;
722
- case handlerTypes_1.APITypes.PATCH:
723
- router.patch(ro.apiURL, ...routeMiddlewares, wrappedHandler);
724
- dbosExec.logger.debug(`DBOS Server Registered PATCH ${ro.apiURL}`);
725
- break;
726
- case handlerTypes_1.APITypes.DELETE:
727
- router.delete(ro.apiURL, ...routeMiddlewares, wrappedHandler);
728
- dbosExec.logger.debug(`DBOS Server Registered DELETE ${ro.apiURL}`);
729
- break;
730
- default:
731
- (0, utils_1.exhaustiveCheckGuard)(ro.apiType);
732
- }
733
- });
734
- }
735
- }
736
- exports.DBOSHttpServer = DBOSHttpServer;
737
- //# sourceMappingURL=server.js.map