@dbos-inc/dbos-sdk 3.0.46-preview.g649d101bf0 → 3.0.51-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 (45) hide show
  1. package/dist/src/conductor/conductor.d.ts.map +1 -1
  2. package/dist/src/conductor/conductor.js +3 -1
  3. package/dist/src/conductor/conductor.js.map +1 -1
  4. package/dist/src/context.d.ts +2 -4
  5. package/dist/src/context.d.ts.map +1 -1
  6. package/dist/src/context.js +2 -4
  7. package/dist/src/context.js.map +1 -1
  8. package/dist/src/datasource.d.ts.map +1 -1
  9. package/dist/src/datasource.js +18 -14
  10. package/dist/src/datasource.js.map +1 -1
  11. package/dist/src/dbos-executor.d.ts +6 -14
  12. package/dist/src/dbos-executor.d.ts.map +1 -1
  13. package/dist/src/dbos-executor.js +178 -129
  14. package/dist/src/dbos-executor.js.map +1 -1
  15. package/dist/src/dbos-runtime/config.d.ts.map +1 -1
  16. package/dist/src/dbos-runtime/config.js +5 -2
  17. package/dist/src/dbos-runtime/config.js.map +1 -1
  18. package/dist/src/dbos-runtime/workflow_management.d.ts +2 -2
  19. package/dist/src/dbos-runtime/workflow_management.d.ts.map +1 -1
  20. package/dist/src/dbos-runtime/workflow_management.js +5 -3
  21. package/dist/src/dbos-runtime/workflow_management.js.map +1 -1
  22. package/dist/src/dbos.d.ts +0 -9
  23. package/dist/src/dbos.d.ts.map +1 -1
  24. package/dist/src/dbos.js +48 -134
  25. package/dist/src/dbos.js.map +1 -1
  26. package/dist/src/httpServer/middleware.d.ts +0 -8
  27. package/dist/src/httpServer/middleware.d.ts.map +1 -1
  28. package/dist/src/httpServer/middleware.js +1 -153
  29. package/dist/src/httpServer/middleware.js.map +1 -1
  30. package/dist/src/httpServer/server.d.ts.map +1 -1
  31. package/dist/src/httpServer/server.js +41 -38
  32. package/dist/src/httpServer/server.js.map +1 -1
  33. package/dist/src/telemetry/traces.d.ts +2 -0
  34. package/dist/src/telemetry/traces.d.ts.map +1 -1
  35. package/dist/src/telemetry/traces.js +22 -1
  36. package/dist/src/telemetry/traces.js.map +1 -1
  37. package/dist/src/user_database.d.ts +1 -1
  38. package/dist/src/user_database.d.ts.map +1 -1
  39. package/dist/src/user_database.js.map +1 -1
  40. package/dist/src/utils.d.ts +0 -1
  41. package/dist/src/utils.d.ts.map +1 -1
  42. package/dist/src/utils.js +0 -1
  43. package/dist/src/utils.js.map +1 -1
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +2 -5
@@ -72,30 +72,30 @@ class DBOSExecutor {
72
72
  config;
73
73
  initialized;
74
74
  // User Database
75
- userDatabase = null;
75
+ #userDatabase = undefined;
76
+ #procedurePool = undefined;
76
77
  // System Database
77
78
  systemDatabase;
78
- procedurePool;
79
79
  // Temporary workflows are created by calling transaction/send/recv directly from the executor class
80
- static tempWorkflowName = 'temp_workflow';
80
+ static #tempWorkflowName = 'temp_workflow';
81
81
  telemetryCollector;
82
82
  static defaultNotificationTimeoutSec = 60;
83
- debugMode;
83
+ #debugMode;
84
84
  static systemDBSchemaName = 'dbos';
85
85
  logger;
86
86
  ctxLogger;
87
87
  tracer;
88
88
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
89
- typeormEntities = [];
90
- drizzleEntities = {};
91
- scheduler = new scheduler_1.ScheduledReceiver();
92
- wfqEnded = undefined;
89
+ #typeormEntities = [];
90
+ #drizzleEntities = {};
91
+ #scheduler = new scheduler_1.ScheduledReceiver();
92
+ #wfqEnded = undefined;
93
93
  executorID = utils_1.globalParams.executorID;
94
94
  static globalInstance = undefined;
95
95
  /* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
96
96
  constructor(config, { systemDatabase, debugMode } = {}) {
97
97
  this.config = config;
98
- this.debugMode = debugMode ?? false;
98
+ this.#debugMode = debugMode ?? false;
99
99
  if (config.telemetry.OTLPExporter) {
100
100
  const OTLPExporter = new exporters_1.TelemetryExporter(config.telemetry.OTLPExporter);
101
101
  this.telemetryCollector = new collector_1.TelemetryCollector(OTLPExporter);
@@ -105,12 +105,12 @@ class DBOSExecutor {
105
105
  this.telemetryCollector = new collector_1.TelemetryCollector();
106
106
  }
107
107
  this.logger = new logs_1.GlobalLogger(this.telemetryCollector, this.config.telemetry.logs);
108
- this.ctxLogger = new logs_1.DBOSContextualLogger(this.logger, () => (0, context_1.getCurrentContextStore)().span);
108
+ this.ctxLogger = new logs_1.DBOSContextualLogger(this.logger, () => api_1.trace.getActiveSpan());
109
109
  this.tracer = new traces_1.Tracer(this.telemetryCollector);
110
- if (this.debugMode) {
110
+ if (this.#debugMode) {
111
111
  this.logger.info('Running in debug mode!');
112
112
  }
113
- this.procedurePool = new pg_1.Pool((0, utils_2.getClientConfig)(this.config.databaseUrl));
113
+ this.#procedurePool = this.config.userDbClient ? new pg_1.Pool((0, utils_2.getClientConfig)(this.config.databaseUrl)) : undefined;
114
114
  if (systemDatabase) {
115
115
  this.logger.debug('Using provided system database'); // XXX print the name or something
116
116
  this.systemDatabase = systemDatabase;
@@ -122,7 +122,10 @@ class DBOSExecutor {
122
122
  this.initialized = false;
123
123
  DBOSExecutor.globalInstance = this;
124
124
  }
125
- configureDbClient() {
125
+ get appName() {
126
+ return this.config.name;
127
+ }
128
+ #configureDbClient() {
126
129
  const userDbClient = this.config.userDbClient;
127
130
  const userDBConfig = (0, utils_2.getClientConfig)(this.config.databaseUrl);
128
131
  userDBConfig.max = this.config.userDbPoolSize ?? 20;
@@ -130,7 +133,7 @@ class DBOSExecutor {
130
133
  // TODO: make Prisma work with debugger proxy.
131
134
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
132
135
  const { PrismaClient } = require(node_path_1.default.join(process.cwd(), 'node_modules', '@prisma', 'client')); // Find the prisma client in the node_modules of the current project
133
- this.userDatabase = new user_database_1.PrismaUserDatabase(
136
+ this.#userDatabase = new user_database_1.PrismaUserDatabase(
134
137
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
135
138
  new PrismaClient({
136
139
  datasources: {
@@ -145,13 +148,13 @@ class DBOSExecutor {
145
148
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
146
149
  const DataSourceExports = require('typeorm');
147
150
  try {
148
- this.userDatabase = new user_database_1.TypeORMDatabase(
151
+ this.#userDatabase = new user_database_1.TypeORMDatabase(
149
152
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
150
153
  new DataSourceExports.DataSource({
151
154
  type: 'postgres',
152
155
  url: userDBConfig.connectionString,
153
156
  connectTimeoutMS: userDBConfig.connectionTimeoutMillis,
154
- entities: this.typeormEntities,
157
+ entities: this.#typeormEntities,
155
158
  poolSize: userDBConfig.max,
156
159
  }));
157
160
  }
@@ -170,7 +173,7 @@ class DBOSExecutor {
170
173
  max: userDBConfig.max,
171
174
  },
172
175
  };
173
- this.userDatabase = new user_database_1.KnexUserDatabase((0, knex_1.default)(knexConfig));
176
+ this.#userDatabase = new user_database_1.KnexUserDatabase((0, knex_1.default)(knexConfig));
174
177
  this.logger.debug('Loaded Knex user database');
175
178
  }
176
179
  else if (userDbClient === user_database_1.UserDatabaseName.DRIZZLE) {
@@ -178,13 +181,13 @@ class DBOSExecutor {
178
181
  const DrizzleExports = require('drizzle-orm/node-postgres');
179
182
  const drizzlePool = new pg_1.Pool(userDBConfig);
180
183
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
181
- const drizzle = DrizzleExports.drizzle(drizzlePool, { schema: this.drizzleEntities });
184
+ const drizzle = DrizzleExports.drizzle(drizzlePool, { schema: this.#drizzleEntities });
182
185
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
183
- this.userDatabase = new user_database_1.DrizzleUserDatabase(drizzlePool, drizzle);
186
+ this.#userDatabase = new user_database_1.DrizzleUserDatabase(drizzlePool, drizzle);
184
187
  this.logger.debug('Loaded Drizzle user database');
185
188
  }
186
189
  else {
187
- this.userDatabase = new user_database_1.PGNodeUserDatabase(userDBConfig);
190
+ this.#userDatabase = new user_database_1.PGNodeUserDatabase(userDBConfig);
188
191
  this.logger.debug('Loaded Postgres user database');
189
192
  }
190
193
  }
@@ -208,29 +211,31 @@ class DBOSExecutor {
208
211
  * With TSORM, we take an array of entities (Function[]) and add them to this.entities:
209
212
  */
210
213
  if (Array.isArray(reg.ormEntities)) {
211
- this.typeormEntities = this.typeormEntities.concat(reg.ormEntities);
214
+ this.#typeormEntities = this.#typeormEntities.concat(reg.ormEntities);
212
215
  length = reg.ormEntities.length;
213
216
  }
214
217
  else {
215
218
  /**
216
219
  * With Drizzle, we need to take an object of entities, since the object keys are used to access the entities from ctx.client.query:
217
220
  */
218
- this.drizzleEntities = { ...this.drizzleEntities, ...reg.ormEntities };
221
+ this.#drizzleEntities = { ...this.#drizzleEntities, ...reg.ormEntities };
219
222
  length = Object.keys(reg.ormEntities).length;
220
223
  }
221
224
  this.logger.debug(`Loaded ${length} ORM entities`);
222
225
  }
223
- if (!this.debugMode) {
224
- await (0, user_database_1.createDBIfDoesNotExist)(this.config.databaseUrl, this.logger);
225
- }
226
- this.configureDbClient();
227
- if (!this.userDatabase) {
228
- this.logger.error('No user database configured!');
229
- throw new error_1.DBOSInitializationError('No user database configured!');
226
+ if (this.config.userDbClient) {
227
+ if (!this.#debugMode) {
228
+ await (0, user_database_1.createDBIfDoesNotExist)(this.config.databaseUrl, this.logger);
229
+ }
230
+ this.#configureDbClient();
231
+ if (!this.#userDatabase) {
232
+ this.logger.error('No user database configured!');
233
+ throw new error_1.DBOSInitializationError('No user database configured!');
234
+ }
235
+ // Debug mode doesn't need to initialize the DBs. Everything should appear to be read-only.
236
+ await this.#userDatabase.init(this.#debugMode);
230
237
  }
231
- // Debug mode doesn't need to initialize the DBs. Everything should appear to be read-only.
232
- await this.userDatabase.init(this.debugMode);
233
- if (!this.debugMode) {
238
+ if (!this.#debugMode) {
234
239
  await this.systemDatabase.init();
235
240
  }
236
241
  }
@@ -253,7 +258,7 @@ class DBOSExecutor {
253
258
  }
254
259
  this.initialized = true;
255
260
  // Only execute init code if under non-debug mode
256
- if (!this.debugMode) {
261
+ if (!this.#debugMode) {
257
262
  for (const cls of classnames) {
258
263
  // Init its configurations
259
264
  const creg = (0, decorators_1.getClassRegistrationByName)(cls);
@@ -306,10 +311,8 @@ class DBOSExecutor {
306
311
  try {
307
312
  await this.systemDatabase.awaitRunningWorkflows();
308
313
  await this.systemDatabase.destroy();
309
- if (this.userDatabase) {
310
- await this.userDatabase.destroy();
311
- }
312
- await this.procedurePool.end();
314
+ await this.#userDatabase?.destroy();
315
+ await this.#procedurePool?.end();
313
316
  await this.logger.destroy();
314
317
  if (DBOSExecutor.globalInstance === this) {
315
318
  DBOSExecutor.globalInstance = undefined;
@@ -321,6 +324,24 @@ class DBOSExecutor {
321
324
  throw err;
322
325
  }
323
326
  }
327
+ async createUserSchema() {
328
+ if (!this.#userDatabase) {
329
+ throw new Error('User database not enabled.');
330
+ }
331
+ await this.#userDatabase.createSchema();
332
+ }
333
+ async dropUserSchema() {
334
+ if (!this.#userDatabase) {
335
+ throw new Error('User database not enabled.');
336
+ }
337
+ await this.#userDatabase.dropSchema();
338
+ }
339
+ async queryUserDbFunction(queryFunction, ...params) {
340
+ if (!this.#userDatabase) {
341
+ throw new Error('User database not enabled.');
342
+ }
343
+ return await this.#userDatabase.queryFunction(queryFunction, ...params);
344
+ }
324
345
  // This could return WF, or the function underlying a temp wf
325
346
  #getFunctionInfoFromWFStatus(wf) {
326
347
  const methReg = (0, decorators_1.getFunctionRegistrationByName)(wf.workflowClassName, wf.workflowName);
@@ -364,7 +385,7 @@ class DBOSExecutor {
364
385
  const pctx = (0, context_1.getCurrentContextStore)();
365
386
  let wConfig = {};
366
387
  const wInfo = (0, decorators_1.getFunctionRegistration)(wf);
367
- if (wf.name !== DBOSExecutor.tempWorkflowName) {
388
+ if (wf.name !== DBOSExecutor.#tempWorkflowName) {
368
389
  if (!wInfo || !wInfo.workflowConfig) {
369
390
  throw new error_1.DBOSNotRegisteredError(wf.name);
370
391
  }
@@ -382,8 +403,8 @@ class DBOSExecutor {
382
403
  authenticatedUser: pctx?.authenticatedUser ?? '',
383
404
  authenticatedRoles: pctx?.authenticatedRoles ?? [],
384
405
  assumedRole: pctx?.assumedRole ?? '',
385
- }, pctx?.span);
386
- const isTempWorkflow = DBOSExecutor.tempWorkflowName === wfname;
406
+ });
407
+ const isTempWorkflow = DBOSExecutor.#tempWorkflowName === wfname;
387
408
  const internalStatus = {
388
409
  workflowUUID: workflowID,
389
410
  status: params.queueName !== undefined ? workflow_1.StatusString.ENQUEUED : workflow_1.StatusString.PENDING,
@@ -408,14 +429,14 @@ class DBOSExecutor {
408
429
  priority: priority ?? 0,
409
430
  };
410
431
  if (isTempWorkflow) {
411
- internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${params.tempWfType}-${params.tempWfName}`;
432
+ internalStatus.workflowName = `${DBOSExecutor.#tempWorkflowName}-${params.tempWfType}-${params.tempWfName}`;
412
433
  internalStatus.workflowClassName = params.tempWfClass ?? '';
413
434
  }
414
435
  let status = undefined;
415
436
  let $deadlineEpochMS = undefined;
416
437
  // Synchronously set the workflow's status to PENDING and record workflow inputs.
417
438
  // We have to do it for all types of workflows because operation_outputs table has a foreign key constraint on workflow status table.
418
- if (this.debugMode) {
439
+ if (this.#debugMode) {
419
440
  const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowID);
420
441
  if (!wfStatus) {
421
442
  throw new error_1.DBOSDebuggerError(`Failed to find inputs for workflow UUID ${workflowID}`);
@@ -471,7 +492,7 @@ class DBOSExecutor {
471
492
  e.dbos_already_logged = true;
472
493
  internalStatus.error = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(e));
473
494
  internalStatus.status = workflow_1.StatusString.ERROR;
474
- if (!exec.debugMode) {
495
+ if (!exec.#debugMode) {
475
496
  await exec.systemDatabase.recordWorkflowError(workflowID, internalStatus);
476
497
  }
477
498
  span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
@@ -480,23 +501,24 @@ class DBOSExecutor {
480
501
  let result;
481
502
  // Execute the workflow.
482
503
  try {
483
- const callResult = await (0, context_1.runWithParentContext)(pctx, {
484
- presetID,
485
- timeoutMS,
486
- deadlineEpochMS,
487
- workflowId: workflowID,
488
- span,
489
- logger: this.ctxLogger,
490
- }, () => {
491
- const callPromise = wf.call(params.configuredInstance, ...args);
492
- if ($deadlineEpochMS === undefined) {
493
- return callPromise;
494
- }
495
- else {
496
- return callPromiseWithTimeout(callPromise, $deadlineEpochMS, this.systemDatabase);
497
- }
504
+ const callResult = await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
505
+ return await (0, context_1.runWithParentContext)(pctx, {
506
+ presetID,
507
+ timeoutMS,
508
+ deadlineEpochMS,
509
+ workflowId: workflowID,
510
+ logger: this.ctxLogger,
511
+ }, () => {
512
+ const callPromise = wf.call(params.configuredInstance, ...args);
513
+ if ($deadlineEpochMS === undefined) {
514
+ return callPromise;
515
+ }
516
+ else {
517
+ return callPromiseWithTimeout(callPromise, $deadlineEpochMS, this.systemDatabase);
518
+ }
519
+ });
498
520
  });
499
- if (this.debugMode) {
521
+ if (this.#debugMode) {
500
522
  const recordedResult = DBOSExecutor.reviveResultOrError((await this.systemDatabase.awaitWorkflowResult(workflowID)));
501
523
  if (!resultsMatch(recordedResult, callResult)) {
502
524
  this.logger.error(`Detect different output for the workflow UUID ${workflowID}!\n Received: ${utils_1.DBOSJSON.stringify(callResult)}\n Original: ${utils_1.DBOSJSON.stringify(recordedResult)}`);
@@ -514,7 +536,7 @@ class DBOSExecutor {
514
536
  }
515
537
  internalStatus.output = utils_1.DBOSJSON.stringify(result);
516
538
  internalStatus.status = workflow_1.StatusString.SUCCESS;
517
- if (!this.debugMode) {
539
+ if (!this.#debugMode) {
518
540
  await this.systemDatabase.recordWorkflowOutput(workflowID, internalStatus);
519
541
  }
520
542
  span.setStatus({ code: api_1.SpanStatusCode.OK });
@@ -550,7 +572,7 @@ class DBOSExecutor {
550
572
  }
551
573
  return result;
552
574
  };
553
- if (this.debugMode ||
575
+ if (this.#debugMode ||
554
576
  (status !== 'SUCCESS' && status !== 'ERROR' && (params.queueName === undefined || params.executeWorkflow))) {
555
577
  const workflowPromise = runWorkflow();
556
578
  this.systemDatabase.registerRunningWorkflow(workflowID, workflowPromise);
@@ -616,7 +638,7 @@ class DBOSExecutor {
616
638
  * Write a operation's output to the database.
617
639
  */
618
640
  async #recordOutput(query, workflowUUID, funcID, txnSnapshot, output, isKeyConflict, function_name) {
619
- if (this.debugMode) {
641
+ if (this.#debugMode) {
620
642
  throw new error_1.DBOSDebuggerError('Cannot record output in debug mode.');
621
643
  }
622
644
  try {
@@ -638,7 +660,7 @@ class DBOSExecutor {
638
660
  * Record an error in an operation to the database.
639
661
  */
640
662
  async #recordError(query, workflowUUID, funcID, txnSnapshot, err, isKeyConflict, function_name) {
641
- if (this.debugMode) {
663
+ if (this.#debugMode) {
642
664
  throw new error_1.DBOSDebuggerError('Cannot record error in debug mode.');
643
665
  }
644
666
  try {
@@ -656,12 +678,17 @@ class DBOSExecutor {
656
678
  }
657
679
  }
658
680
  async getTransactions(workflowUUID) {
659
- const rows = await this.userDatabase.query(`SELECT function_id, function_name, output, error FROM ${DBOSExecutor.systemDBSchemaName}.transaction_outputs WHERE workflow_uuid=$1`, workflowUUID);
660
- for (const row of rows) {
661
- row.output = row.output !== null ? utils_1.DBOSJSON.parse(row.output) : null;
662
- row.error = row.error !== null ? (0, serialize_error_1.deserializeError)(utils_1.DBOSJSON.parse(row.error)) : null;
681
+ if (this.#userDatabase) {
682
+ const rows = await this.#userDatabase.query(`SELECT function_id, function_name, output, error FROM ${DBOSExecutor.systemDBSchemaName}.transaction_outputs WHERE workflow_uuid=$1`, workflowUUID);
683
+ for (const row of rows) {
684
+ row.output = row.output !== null ? utils_1.DBOSJSON.parse(row.output) : null;
685
+ row.error = row.error !== null ? (0, serialize_error_1.deserializeError)(utils_1.DBOSJSON.parse(row.error)) : null;
686
+ }
687
+ return rows;
688
+ }
689
+ else {
690
+ return [];
663
691
  }
664
- return rows;
665
692
  }
666
693
  async runTransactionTempWF(txn, params, ...args) {
667
694
  return await (await this.startTransactionTempWF(txn, params, undefined, undefined, ...args)).getResult();
@@ -679,6 +706,10 @@ class DBOSExecutor {
679
706
  }, callerWFID, callerFunctionID, ...args);
680
707
  }
681
708
  async callTransactionFunction(txn, clsinst, ...args) {
709
+ const userDB = this.#userDatabase;
710
+ if (!userDB) {
711
+ throw new Error('No user database configured for transactions.');
712
+ }
682
713
  const txnReg = (0, decorators_1.getFunctionRegistration)(txn);
683
714
  if (!txnReg || !txnReg.txnConfig) {
684
715
  throw new error_1.DBOSNotRegisteredError(txn.name);
@@ -698,7 +729,7 @@ class DBOSExecutor {
698
729
  assumedRole: pctx.assumedRole ?? '',
699
730
  authenticatedRoles: pctx.authenticatedRoles ?? [],
700
731
  isolationLevel: txnReg.txnConfig.isolationLevel,
701
- }, pctx.span);
732
+ });
702
733
  while (true) {
703
734
  await this.systemDatabase.checkIfCanceled(wfid);
704
735
  let txn_snapshot = 'invalid';
@@ -707,7 +738,7 @@ class DBOSExecutor {
707
738
  // If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
708
739
  // Note: It is possible to retrieve a generated ID from a workflow handle, run a concurrent execution, and cause trouble for yourself. We recommend against this.
709
740
  let prevResult = exports.dbosNull;
710
- const queryFunc = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
741
+ const queryFunc = (sql, args) => userDB.queryWithClient(client, sql, ...args);
711
742
  if (pctx.presetID) {
712
743
  const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, txn.name);
713
744
  prevResult = executionResult.result;
@@ -728,32 +759,33 @@ class DBOSExecutor {
728
759
  // Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
729
760
  txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
730
761
  }
731
- if (this.debugMode && prevResult === exports.dbosNull) {
762
+ if (this.#debugMode && prevResult === exports.dbosNull) {
732
763
  throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${wfid}, step number ${funcId}`);
733
764
  }
734
765
  // Execute the user's transaction.
735
766
  const ctxlog = this.ctxLogger;
736
767
  const result = await (async function () {
737
768
  try {
738
- return await (0, context_1.runWithParentContext)(pctx, {
739
- authenticatedRoles: pctx?.authenticatedRoles,
740
- authenticatedUser: pctx?.authenticatedUser,
741
- workflowId: wfid,
742
- curTxFunctionId: funcId,
743
- parentCtx: pctx,
744
- sqlClient: client,
745
- logger: ctxlog,
746
- span,
747
- }, async () => {
748
- const tf = txn;
749
- return await tf.call(clsinst, ...args);
769
+ return await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
770
+ return await (0, context_1.runWithParentContext)(pctx, {
771
+ authenticatedRoles: pctx?.authenticatedRoles,
772
+ authenticatedUser: pctx?.authenticatedUser,
773
+ workflowId: wfid,
774
+ curTxFunctionId: funcId,
775
+ parentCtx: pctx,
776
+ sqlClient: client,
777
+ logger: ctxlog,
778
+ }, async () => {
779
+ const tf = txn;
780
+ return await tf.call(clsinst, ...args);
781
+ });
750
782
  });
751
783
  }
752
784
  catch (e) {
753
785
  return e instanceof Error ? e : new Error(`${e}`);
754
786
  }
755
787
  })();
756
- if (this.debugMode) {
788
+ if (this.#debugMode) {
757
789
  if (prevResult instanceof Error) {
758
790
  throw prevResult;
759
791
  }
@@ -770,11 +802,11 @@ class DBOSExecutor {
770
802
  // Record the execution, commit, and return.
771
803
  try {
772
804
  // Synchronously record the output of write transactions and obtain the transaction ID.
773
- const pg_txn_id = await this.#recordOutput(queryFunc, wfid, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
805
+ const pg_txn_id = await this.#recordOutput(queryFunc, wfid, funcId, txn_snapshot, result, (error) => userDB.isKeyConflictError(error), txn.name);
774
806
  span.setAttribute('pg_txn_id', pg_txn_id);
775
807
  }
776
808
  catch (error) {
777
- if (this.userDatabase.isFailedSqlTransactionError(error)) {
809
+ if (userDB.isFailedSqlTransactionError(error)) {
778
810
  this.logger.error(`Postgres aborted the ${txn.name} @DBOS.transaction of Workflow ${wfid}, but the function did not raise an exception. Please ensure that the @DBOS.transaction method raises an exception if the database transaction is aborted.`);
779
811
  throw new error_1.DBOSFailedSqlTransactionError(wfid, txn.name);
780
812
  }
@@ -785,15 +817,15 @@ class DBOSExecutor {
785
817
  return result;
786
818
  };
787
819
  try {
788
- const result = await this.userDatabase.transaction(wrappedTransaction, txnReg.txnConfig);
820
+ const result = await userDB.transaction(wrappedTransaction, txnReg.txnConfig);
789
821
  span.setStatus({ code: api_1.SpanStatusCode.OK });
790
822
  this.tracer.endSpan(span);
791
823
  return result;
792
824
  }
793
825
  catch (err) {
794
826
  const e = err;
795
- if (!prevResultFound && !this.debugMode && !(e instanceof error_1.DBOSUnexpectedStepError)) {
796
- if (this.userDatabase.isRetriableTransactionError(err)) {
827
+ if (!prevResultFound && !this.#debugMode && !(e instanceof error_1.DBOSUnexpectedStepError)) {
828
+ if (userDB.isRetriableTransactionError(err)) {
797
829
  // serialization_failure in PostgreSQL
798
830
  span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
799
831
  // Retry serialization failures.
@@ -804,9 +836,9 @@ class DBOSExecutor {
804
836
  }
805
837
  // Record and throw other errors.
806
838
  const e = err;
807
- await this.userDatabase.transaction(async (client) => {
808
- const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
809
- await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
839
+ await userDB.transaction(async (client) => {
840
+ const func = (sql, args) => userDB.queryWithClient(client, sql, ...args);
841
+ await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => userDB.isKeyConflictError(error), txn.name);
810
842
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
811
843
  }
812
844
  span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
@@ -836,7 +868,7 @@ class DBOSExecutor {
836
868
  const pctx = (0, context_1.getCurrentContextStore)();
837
869
  const wfid = pctx.workflowId;
838
870
  await this.systemDatabase.checkIfCanceled(wfid);
839
- const executeLocally = this.debugMode || (procConfig.executeLocally ?? false);
871
+ const executeLocally = this.#debugMode || (procConfig.executeLocally ?? false);
840
872
  const funcId = (0, context_1.functionIDGetIncrement)();
841
873
  const span = this.tracer.startSpan(proc.name, {
842
874
  operationUUID: wfid,
@@ -847,7 +879,7 @@ class DBOSExecutor {
847
879
  authenticatedRoles: pctx.authenticatedRoles ?? [],
848
880
  isolationLevel: procInfo.procConfig.isolationLevel,
849
881
  executeLocally,
850
- }, pctx.span);
882
+ });
851
883
  try {
852
884
  const result = executeLocally
853
885
  ? await this.#callProcedureFunctionLocal(proc, args, span, procInfo, funcId)
@@ -865,6 +897,11 @@ class DBOSExecutor {
865
897
  }
866
898
  }
867
899
  async #callProcedureFunctionLocal(proc, args, span, procInfo, funcId) {
900
+ const procPool = this.#procedurePool;
901
+ const userDB = this.#userDatabase;
902
+ if (!procPool || !userDB) {
903
+ throw new Error('User database not enabled.');
904
+ }
868
905
  let retryWaitMillis = 1;
869
906
  const backoffFactor = 1.5;
870
907
  const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
@@ -875,7 +912,7 @@ class DBOSExecutor {
875
912
  let txn_snapshot = 'invalid';
876
913
  const wrappedProcedure = async (client) => {
877
914
  let prevResult = exports.dbosNull;
878
- const queryFunc = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
915
+ const queryFunc = (sql, args) => procPool.query(sql, args).then((v) => v.rows);
879
916
  if (pctx.presetID) {
880
917
  const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, proc.name);
881
918
  prevResult = executionResult.result;
@@ -895,7 +932,7 @@ class DBOSExecutor {
895
932
  // Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
896
933
  txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
897
934
  }
898
- if (this.debugMode && prevResult === exports.dbosNull) {
935
+ if (this.#debugMode && prevResult === exports.dbosNull) {
899
936
  throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${wfid}, step number ${funcId}`);
900
937
  }
901
938
  // Execute the user's transaction.
@@ -908,23 +945,24 @@ class DBOSExecutor {
908
945
  throw new error_1.DBOSInvalidWorkflowTransitionError();
909
946
  if (!(0, context_1.isInWorkflowCtx)(pctx))
910
947
  throw new error_1.DBOSInvalidWorkflowTransitionError();
911
- return await (0, context_1.runWithParentContext)(pctx, {
912
- curTxFunctionId: funcId,
913
- parentCtx: pctx,
914
- isInStoredProc: true,
915
- sqlClient: client,
916
- logger: ctxlog,
917
- span,
918
- }, async () => {
919
- const pf = proc;
920
- return await pf(...args);
948
+ return await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
949
+ return await (0, context_1.runWithParentContext)(pctx, {
950
+ curTxFunctionId: funcId,
951
+ parentCtx: pctx,
952
+ isInStoredProc: true,
953
+ sqlClient: client,
954
+ logger: ctxlog,
955
+ }, async () => {
956
+ const pf = proc;
957
+ return await pf(...args);
958
+ });
921
959
  });
922
960
  }
923
961
  catch (e) {
924
962
  return e instanceof Error ? e : new Error(`${e}`);
925
963
  }
926
964
  })();
927
- if (this.debugMode) {
965
+ if (this.#debugMode) {
928
966
  if (prevResult instanceof Error) {
929
967
  throw prevResult;
930
968
  }
@@ -953,8 +991,8 @@ class DBOSExecutor {
953
991
  return result;
954
992
  }
955
993
  catch (err) {
956
- if (!this.debugMode) {
957
- if (this.userDatabase.isRetriableTransactionError(err)) {
994
+ if (!this.#debugMode) {
995
+ if (userDB.isRetriableTransactionError(err)) {
958
996
  // serialization_failure in PostgreSQL
959
997
  span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
960
998
  // Retry serialization failures.
@@ -969,9 +1007,9 @@ class DBOSExecutor {
969
1007
  const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
970
1008
  await this.#recordError(func, wfid, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError, proc.name);
971
1009
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
972
- await this.userDatabase.transaction(async (client) => {
973
- const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
974
- await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), proc.name);
1010
+ await userDB.transaction(async (client) => {
1011
+ const func = (sql, args) => userDB.queryWithClient(client, sql, ...args);
1012
+ await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => userDB.isKeyConflictError(error), proc.name);
975
1013
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
976
1014
  }
977
1015
  throw err;
@@ -979,7 +1017,7 @@ class DBOSExecutor {
979
1017
  }
980
1018
  }
981
1019
  async #callProcedureFunctionRemote(proc, args, span, config, funcId) {
982
- if (this.debugMode) {
1020
+ if (this.#debugMode) {
983
1021
  throw new error_1.DBOSDebuggerError("Can't invoke stored procedure in debug mode.");
984
1022
  }
985
1023
  const pctx = (0, context_1.getCurrentContextStore)();
@@ -1013,7 +1051,10 @@ class DBOSExecutor {
1013
1051
  return output;
1014
1052
  }
1015
1053
  async #invokeStoredProc(proc, args) {
1016
- const client = await this.procedurePool.connect();
1054
+ if (!this.#procedurePool) {
1055
+ throw new Error('User Database not enabled.');
1056
+ }
1057
+ const client = await this.#procedurePool.connect();
1017
1058
  const log = (msg) => this.#logNotice(msg);
1018
1059
  const procname = (0, decorators_1.getRegisteredFunctionFullName)(proc);
1019
1060
  const plainProcName = `${procname.className}_${procname.name}_p`;
@@ -1029,7 +1070,10 @@ class DBOSExecutor {
1029
1070
  }
1030
1071
  }
1031
1072
  async invokeStoredProcFunction(func, config) {
1032
- const client = await this.procedurePool.connect();
1073
+ if (!this.#procedurePool) {
1074
+ throw new Error('User Database not enabled.');
1075
+ }
1076
+ const client = await this.#procedurePool.connect();
1033
1077
  try {
1034
1078
  const readOnly = config.readOnly ?? false;
1035
1079
  const isolationLevel = config.isolationLevel ?? transaction_1.IsolationLevel.Serializable;
@@ -1094,7 +1138,7 @@ class DBOSExecutor {
1094
1138
  intervalSeconds: stepConfig.intervalSeconds,
1095
1139
  maxAttempts: stepConfig.maxAttempts,
1096
1140
  backoffRate: stepConfig.backoffRate,
1097
- }, lctx.span);
1141
+ });
1098
1142
  // Check if this execution previously happened, returning its original result if it did.
1099
1143
  const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(wfid, funcID);
1100
1144
  if (checkr) {
@@ -1107,7 +1151,7 @@ class DBOSExecutor {
1107
1151
  this.tracer.endSpan(span);
1108
1152
  return check;
1109
1153
  }
1110
- if (this.debugMode) {
1154
+ if (this.#debugMode) {
1111
1155
  throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${wfid}, step number: ${funcID}`);
1112
1156
  }
1113
1157
  const maxAttempts = stepConfig.maxAttempts ?? 3;
@@ -1126,7 +1170,7 @@ class DBOSExecutor {
1126
1170
  try {
1127
1171
  await this.systemDatabase.checkIfCanceled(wfid);
1128
1172
  let cresult;
1129
- await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, attemptNum, async () => {
1173
+ await (0, context_1.runInStepContext)(lctx, funcID, maxAttempts, attemptNum, async () => {
1130
1174
  const sf = stepFn;
1131
1175
  cresult = await sf.call(clsInst, ...args);
1132
1176
  });
@@ -1150,9 +1194,11 @@ class DBOSExecutor {
1150
1194
  else {
1151
1195
  try {
1152
1196
  let cresult;
1153
- await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, undefined, async () => {
1154
- const sf = stepFn;
1155
- cresult = await sf.call(clsInst, ...args);
1197
+ await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
1198
+ await (0, context_1.runInStepContext)(lctx, funcID, maxAttempts, undefined, async () => {
1199
+ const sf = stepFn;
1200
+ cresult = await sf.call(clsInst, ...args);
1201
+ });
1156
1202
  });
1157
1203
  result = cresult;
1158
1204
  }
@@ -1207,7 +1253,7 @@ class DBOSExecutor {
1207
1253
  */
1208
1254
  forkWorkflow(workflowID, startStep, options = {}) {
1209
1255
  const newWorkflowID = options.newWorkflowID ?? (0, context_1.getNextWFID)(undefined);
1210
- return (0, workflow_management_1.forkWorkflow)(this.systemDatabase, this.userDatabase, workflowID, startStep, { ...options, newWorkflowID });
1256
+ return (0, workflow_management_1.forkWorkflow)(this.systemDatabase, this.#userDatabase, workflowID, startStep, { ...options, newWorkflowID });
1211
1257
  }
1212
1258
  /**
1213
1259
  * Retrieve a handle for a workflow UUID.
@@ -1251,14 +1297,17 @@ class DBOSExecutor {
1251
1297
  return (0, workflow_management_1.listQueuedWorkflows)(this.systemDatabase, input);
1252
1298
  }
1253
1299
  async listWorkflowSteps(workflowID) {
1254
- return (0, workflow_management_1.listWorkflowSteps)(this.systemDatabase, this.userDatabase, workflowID);
1300
+ return (0, workflow_management_1.listWorkflowSteps)(this.systemDatabase, this.#userDatabase, workflowID);
1255
1301
  }
1256
1302
  async queryUserDB(sql, params) {
1303
+ if (!this.#userDatabase) {
1304
+ throw new Error('User database not enabled.');
1305
+ }
1257
1306
  if (params !== undefined) {
1258
- return await this.userDatabase.query(sql, ...params);
1307
+ return await this.#userDatabase.query(sql, ...params);
1259
1308
  }
1260
1309
  else {
1261
- return await this.userDatabase.query(sql);
1310
+ return await this.#userDatabase.query(sql);
1262
1311
  }
1263
1312
  }
1264
1313
  /* INTERNAL HELPERS */
@@ -1267,7 +1316,7 @@ class DBOSExecutor {
1267
1316
  * It runs to completion all pending workflows that were executing when the previous executor failed.
1268
1317
  */
1269
1318
  async recoverPendingWorkflows(executorIDs = ['local']) {
1270
- if (this.debugMode) {
1319
+ if (this.#debugMode) {
1271
1320
  throw new error_1.DBOSDebuggerError('Cannot recover pending workflows in debug mode.');
1272
1321
  }
1273
1322
  const handlerArray = [];
@@ -1305,7 +1354,7 @@ class DBOSExecutor {
1305
1354
  return handlerArray;
1306
1355
  }
1307
1356
  async initEventReceivers() {
1308
- this.wfqEnded = wfqueue_1.wfQueueRunner.dispatchLoop(this);
1357
+ this.#wfqEnded = wfqueue_1.wfQueueRunner.dispatchLoop(this);
1309
1358
  for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
1310
1359
  await lcl.initialize?.();
1311
1360
  }
@@ -1325,7 +1374,7 @@ class DBOSExecutor {
1325
1374
  if (stopQueueThread) {
1326
1375
  try {
1327
1376
  wfqueue_1.wfQueueRunner.stop();
1328
- await this.wfqEnded;
1377
+ await this.#wfqEnded;
1329
1378
  }
1330
1379
  catch (err) {
1331
1380
  const e = err;
@@ -1362,7 +1411,7 @@ class DBOSExecutor {
1362
1411
  // Should be temporary workflows. Parse the name of the workflow.
1363
1412
  const wfName = wfStatus.workflowName;
1364
1413
  const nameArr = wfName.split('-');
1365
- if (!nameArr[0].startsWith(DBOSExecutor.tempWorkflowName)) {
1414
+ if (!nameArr[0].startsWith(DBOSExecutor.#tempWorkflowName)) {
1366
1415
  throw new error_1.DBOSError(`Cannot find workflow function for a non-temporary workflow, ID ${workflowID}, class '${wfStatus.workflowClassName}', function '${wfName}'; did you change your code?`);
1367
1416
  }
1368
1417
  if (nameArr[1] === exports.TempWorkflowType.transaction) {