@dbos-inc/dbos-sdk 3.0.20-preview → 3.0.27-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 (87) hide show
  1. package/dist/src/authdecorators.d.ts +0 -7
  2. package/dist/src/authdecorators.d.ts.map +1 -1
  3. package/dist/src/authdecorators.js +1 -29
  4. package/dist/src/authdecorators.js.map +1 -1
  5. package/dist/src/client.d.ts.map +1 -1
  6. package/dist/src/client.js.map +1 -1
  7. package/dist/src/conductor/conductor.d.ts.map +1 -1
  8. package/dist/src/conductor/conductor.js +3 -0
  9. package/dist/src/conductor/conductor.js.map +1 -1
  10. package/dist/src/conductor/protocol.d.ts +3 -0
  11. package/dist/src/conductor/protocol.d.ts.map +1 -1
  12. package/dist/src/conductor/protocol.js.map +1 -1
  13. package/dist/src/context.d.ts +10 -44
  14. package/dist/src/context.d.ts.map +1 -1
  15. package/dist/src/context.js +34 -114
  16. package/dist/src/context.js.map +1 -1
  17. package/dist/src/datasource.d.ts.map +1 -1
  18. package/dist/src/datasource.js +37 -17
  19. package/dist/src/datasource.js.map +1 -1
  20. package/dist/src/dbos-executor.d.ts +32 -37
  21. package/dist/src/dbos-executor.d.ts.map +1 -1
  22. package/dist/src/dbos-executor.js +263 -267
  23. package/dist/src/dbos-executor.js.map +1 -1
  24. package/dist/src/dbos-runtime/cli.d.ts.map +1 -1
  25. package/dist/src/dbos-runtime/cli.js +1 -3
  26. package/dist/src/dbos-runtime/cli.js.map +1 -1
  27. package/dist/src/dbos-runtime/debug.d.ts +1 -1
  28. package/dist/src/dbos-runtime/debug.d.ts.map +1 -1
  29. package/dist/src/dbos-runtime/debug.js +2 -3
  30. package/dist/src/dbos-runtime/debug.js.map +1 -1
  31. package/dist/src/dbos.d.ts +6 -21
  32. package/dist/src/dbos.d.ts.map +1 -1
  33. package/dist/src/dbos.js +98 -268
  34. package/dist/src/dbos.js.map +1 -1
  35. package/dist/src/decorators.d.ts +7 -7
  36. package/dist/src/decorators.d.ts.map +1 -1
  37. package/dist/src/decorators.js +6 -30
  38. package/dist/src/decorators.js.map +1 -1
  39. package/dist/src/eventreceiver.d.ts +7 -10
  40. package/dist/src/eventreceiver.d.ts.map +1 -1
  41. package/dist/src/httpServer/handler.d.ts +1 -9
  42. package/dist/src/httpServer/handler.d.ts.map +1 -1
  43. package/dist/src/httpServer/handler.js.map +1 -1
  44. package/dist/src/httpServer/middleware.d.ts +2 -2
  45. package/dist/src/httpServer/middleware.d.ts.map +1 -1
  46. package/dist/src/httpServer/server.d.ts +3 -3
  47. package/dist/src/httpServer/server.d.ts.map +1 -1
  48. package/dist/src/httpServer/server.js +22 -21
  49. package/dist/src/httpServer/server.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -4
  51. package/dist/src/index.d.ts.map +1 -1
  52. package/dist/src/index.js +7 -13
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/paramdecorators.d.ts.map +1 -1
  55. package/dist/src/paramdecorators.js +0 -6
  56. package/dist/src/paramdecorators.js.map +1 -1
  57. package/dist/src/procedure.d.ts +0 -22
  58. package/dist/src/procedure.d.ts.map +1 -1
  59. package/dist/src/procedure.js +0 -16
  60. package/dist/src/procedure.js.map +1 -1
  61. package/dist/src/scheduler/scheduler.d.ts.map +1 -1
  62. package/dist/src/scheduler/scheduler.js.map +1 -1
  63. package/dist/src/step.d.ts +0 -25
  64. package/dist/src/step.d.ts.map +1 -1
  65. package/dist/src/step.js +0 -20
  66. package/dist/src/step.js.map +1 -1
  67. package/dist/src/system_database.d.ts +5 -5
  68. package/dist/src/system_database.d.ts.map +1 -1
  69. package/dist/src/system_database.js +56 -3
  70. package/dist/src/system_database.js.map +1 -1
  71. package/dist/src/telemetry/logs.d.ts +4 -8
  72. package/dist/src/telemetry/logs.d.ts.map +1 -1
  73. package/dist/src/telemetry/logs.js +25 -13
  74. package/dist/src/telemetry/logs.js.map +1 -1
  75. package/dist/src/transaction.d.ts +0 -25
  76. package/dist/src/transaction.d.ts.map +1 -1
  77. package/dist/src/transaction.js +1 -15
  78. package/dist/src/transaction.js.map +1 -1
  79. package/dist/src/user_database.d.ts +2 -2
  80. package/dist/src/user_database.d.ts.map +1 -1
  81. package/dist/src/user_database.js.map +1 -1
  82. package/dist/src/workflow.d.ts +7 -148
  83. package/dist/src/workflow.d.ts.map +1 -1
  84. package/dist/src/workflow.js +7 -226
  85. package/dist/src/workflow.js.map +1 -1
  86. package/dist/tsconfig.tsbuildinfo +1 -1
  87. package/package.json +2 -2
@@ -26,11 +26,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.DBOSExecutor = exports.TempWorkflowType = exports.OperationType = exports.DebugMode = exports.isDeprecatedDBOSConfig = exports.DBOS_QUEUE_MAX_PRIORITY = exports.DBOS_QUEUE_MIN_PRIORITY = exports.dbosNull = void 0;
29
+ exports.DBOSExecutor = exports.TempWorkflowType = exports.OperationType = exports.isDeprecatedDBOSConfig = exports.DBOS_QUEUE_MAX_PRIORITY = exports.DBOS_QUEUE_MIN_PRIORITY = exports.dbosNull = void 0;
30
30
  const error_1 = require("./error");
31
31
  const workflow_1 = require("./workflow");
32
32
  const transaction_1 = require("./transaction");
33
- const step_1 = require("./step");
34
33
  const collector_1 = require("./telemetry/collector");
35
34
  const traces_1 = require("./telemetry/traces");
36
35
  const logs_1 = require("./telemetry/logs");
@@ -46,7 +45,6 @@ const context_1 = require("./context");
46
45
  const serialize_error_1 = require("serialize-error");
47
46
  const utils_1 = require("./utils");
48
47
  const node_path_1 = __importDefault(require("node:path"));
49
- const procedure_1 = require("./procedure");
50
48
  const _1 = require(".");
51
49
  const lodash_1 = require("lodash");
52
50
  const wfqueue_1 = require("./wfqueue");
@@ -67,17 +65,11 @@ function isDeprecatedDBOSConfig(config) {
67
65
  return isDeprecated;
68
66
  }
69
67
  exports.isDeprecatedDBOSConfig = isDeprecatedDBOSConfig;
70
- var DebugMode;
71
- (function (DebugMode) {
72
- DebugMode[DebugMode["DISABLED"] = 0] = "DISABLED";
73
- DebugMode[DebugMode["ENABLED"] = 1] = "ENABLED";
74
- DebugMode[DebugMode["TIME_TRAVEL"] = 2] = "TIME_TRAVEL";
75
- })(DebugMode || (exports.DebugMode = DebugMode = {}));
76
68
  exports.OperationType = {
77
69
  HANDLER: 'handler',
78
70
  WORKFLOW: 'workflow',
79
71
  TRANSACTION: 'transaction',
80
- COMMUNICATOR: 'communicator',
72
+ STEP: 'step',
81
73
  PROCEDURE: 'procedure',
82
74
  };
83
75
  exports.TempWorkflowType = {
@@ -120,22 +112,9 @@ class DBOSExecutor {
120
112
  telemetryCollector;
121
113
  static defaultNotificationTimeoutSec = 60;
122
114
  debugMode;
123
- get isDebugging() {
124
- switch (this.debugMode) {
125
- case DebugMode.DISABLED:
126
- return false;
127
- case DebugMode.ENABLED:
128
- case DebugMode.TIME_TRAVEL:
129
- return true;
130
- default: {
131
- const _never = this.debugMode;
132
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
133
- throw new Error(`Unexpected DBOS debug mode: ${this.debugMode}`);
134
- }
135
- }
136
- }
137
115
  static systemDBSchemaName = 'dbos';
138
116
  logger;
117
+ ctxLogger;
139
118
  tracer;
140
119
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
141
120
  typeormEntities = [];
@@ -148,7 +127,7 @@ class DBOSExecutor {
148
127
  /* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
149
128
  constructor(config, { systemDatabase, debugMode } = {}) {
150
129
  this.config = config;
151
- this.debugMode = debugMode ?? DebugMode.DISABLED;
130
+ this.debugMode = debugMode ?? false;
152
131
  // Set configured environment variables
153
132
  if (config.env) {
154
133
  for (const [key, value] of Object.entries(config.env)) {
@@ -169,8 +148,9 @@ class DBOSExecutor {
169
148
  this.telemetryCollector = new collector_1.TelemetryCollector();
170
149
  }
171
150
  this.logger = new logs_1.GlobalLogger(this.telemetryCollector, this.config.telemetry.logs);
151
+ this.ctxLogger = new logs_1.DBOSContextualLogger(this.logger, () => (0, context_1.getCurrentContextStore)().span);
172
152
  this.tracer = new traces_1.Tracer(this.telemetryCollector);
173
- if (this.isDebugging) {
153
+ if (this.debugMode) {
174
154
  this.logger.info('Running in debug mode!');
175
155
  }
176
156
  this.procedurePool = new pg_1.Pool(this.config.poolConfig);
@@ -318,7 +298,7 @@ class DBOSExecutor {
318
298
  }
319
299
  this.logger.debug(`Loaded ${length} ORM entities`);
320
300
  }
321
- if (!this.isDebugging) {
301
+ if (!this.debugMode) {
322
302
  await (0, user_database_1.createDBIfDoesNotExist)(this.config.poolConfig, this.logger);
323
303
  }
324
304
  this.configureDbClient();
@@ -330,8 +310,8 @@ class DBOSExecutor {
330
310
  this.#registerClass(cls);
331
311
  }
332
312
  // Debug mode doesn't need to initialize the DBs. Everything should appear to be read-only.
333
- await this.userDatabase.init(this.isDebugging);
334
- if (!this.isDebugging) {
313
+ await this.userDatabase.init(this.debugMode);
314
+ if (!this.debugMode) {
335
315
  await this.systemDatabase.init();
336
316
  }
337
317
  }
@@ -354,7 +334,7 @@ class DBOSExecutor {
354
334
  }
355
335
  this.initialized = true;
356
336
  // Only execute init code if under non-debug mode
357
- if (!this.isDebugging) {
337
+ if (!this.debugMode) {
358
338
  for (const cls of classnames) {
359
339
  // Init its configurations
360
340
  const creg = (0, decorators_1.getClassRegistrationByName)(cls);
@@ -536,7 +516,7 @@ class DBOSExecutor {
536
516
  async workflow(wf, params, ...args) {
537
517
  return this.internalWorkflow(wf, params, undefined, undefined, ...args);
538
518
  }
539
- // If callerUUID and functionID are set, it means the workflow is invoked from within a workflow.
519
+ // If callerWFID and functionID are set, it means the workflow is invoked from within a workflow.
540
520
  async internalWorkflow(wf, params, callerID, callerFunctionID, ...args) {
541
521
  const workflowID = params.workflowUUID ? params.workflowUUID : (0, node_crypto_1.randomUUID)();
542
522
  const presetID = params.workflowUUID ? true : false;
@@ -560,29 +540,40 @@ class DBOSExecutor {
560
540
  this.logger.warn(`Priority is not enabled for queue ${params.queueName}. Setting priority will not have any effect.`);
561
541
  }
562
542
  }
543
+ const pctx = (0, context_1.getCurrentContextStore)();
563
544
  const wInfo = this.getWorkflowInfo(wf);
564
545
  if (wInfo === undefined) {
565
546
  throw new error_1.DBOSNotRegisteredError(wf.name);
566
547
  }
567
548
  const wConfig = wInfo.config;
568
- const passContext = wInfo.registration?.passContext ?? true;
569
- const wCtxt = new workflow_1.WorkflowContextImpl(this, params.parentCtx, workflowID, wConfig, wf.name, presetID, timeoutMS, deadlineEpochMS, params.tempWfType, params.tempWfName);
549
+ const maxRecoveryAttempts = wConfig.maxRecoveryAttempts ? wConfig.maxRecoveryAttempts : 50;
550
+ const wfname = wf.name; // TODO: Should be what was registered in wfInfo...
551
+ const span = this.tracer.startSpan(wfname, {
552
+ status: workflow_1.StatusString.PENDING,
553
+ operationUUID: workflowID,
554
+ operationType: exports.OperationType.WORKFLOW,
555
+ operationName: wInfo.registration?.name ?? wf.name,
556
+ authenticatedUser: pctx?.authenticatedUser ?? '',
557
+ authenticatedRoles: pctx?.authenticatedRoles ?? [],
558
+ assumedRole: pctx?.assumedRole ?? '',
559
+ }, pctx?.span);
560
+ const isTempWorkflow = DBOSExecutor.tempWorkflowName === wfname;
570
561
  const internalStatus = {
571
562
  workflowUUID: workflowID,
572
563
  status: params.queueName !== undefined ? workflow_1.StatusString.ENQUEUED : workflow_1.StatusString.PENDING,
573
- workflowName: wf.name,
574
- workflowClassName: wCtxt.isTempWorkflow ? '' : (0, decorators_1.getRegisteredMethodClassName)(wf),
564
+ workflowName: wfname,
565
+ workflowClassName: isTempWorkflow ? '' : (0, decorators_1.getRegisteredMethodClassName)(wf),
575
566
  workflowConfigName: params.configuredInstance?.name || '',
576
567
  queueName: params.queueName,
577
- authenticatedUser: wCtxt.authenticatedUser,
578
568
  output: null,
579
569
  error: null,
580
- assumedRole: wCtxt.assumedRole,
581
- authenticatedRoles: wCtxt.authenticatedRoles,
582
- request: wCtxt.request,
583
- executorId: wCtxt.executorID,
570
+ authenticatedUser: pctx?.authenticatedUser || '',
571
+ assumedRole: pctx?.assumedRole || '',
572
+ authenticatedRoles: pctx?.authenticatedRoles || [],
573
+ request: pctx?.request || {},
574
+ executorId: utils_1.globalParams.executorID,
584
575
  applicationVersion: utils_1.globalParams.appVersion,
585
- applicationID: wCtxt.applicationID,
576
+ applicationID: utils_1.globalParams.appID,
586
577
  createdAt: Date.now(), // Remember the start time of this workflow,
587
578
  timeoutMS: timeoutMS,
588
579
  deadlineEpochMS: deadlineEpochMS,
@@ -590,15 +581,15 @@ class DBOSExecutor {
590
581
  deduplicationID: params.enqueueOptions?.deduplicationID,
591
582
  priority: priority ?? 0,
592
583
  };
593
- if (wCtxt.isTempWorkflow) {
594
- internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${wCtxt.tempWfOperationType}-${wCtxt.tempWfOperationName}`;
584
+ if (isTempWorkflow) {
585
+ internalStatus.workflowName = `${DBOSExecutor.tempWorkflowName}-${params.tempWfType}-${params.tempWfName}`;
595
586
  internalStatus.workflowClassName = params.tempWfClass ?? '';
596
587
  }
597
588
  let status = undefined;
598
589
  let $deadlineEpochMS = undefined;
599
590
  // Synchronously set the workflow's status to PENDING and record workflow inputs.
600
591
  // We have to do it for all types of workflows because operation_outputs table has a foreign key constraint on workflow status table.
601
- if (this.isDebugging) {
592
+ if (this.debugMode) {
602
593
  const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowID);
603
594
  if (!wfStatus) {
604
595
  throw new error_1.DBOSDebuggerError(`Failed to find inputs for workflow UUID ${workflowID}`);
@@ -616,7 +607,7 @@ class DBOSExecutor {
616
607
  return new workflow_1.RetrievedHandle(this.systemDatabase, result.childWorkflowID, callerID, callerFunctionID);
617
608
  }
618
609
  }
619
- const ires = await this.systemDatabase.initWorkflowStatus(internalStatus, wCtxt.maxRecoveryAttempts);
610
+ const ires = await this.systemDatabase.initWorkflowStatus(internalStatus, maxRecoveryAttempts);
620
611
  if (callerFunctionID !== undefined && callerID !== undefined) {
621
612
  await this.systemDatabase.recordOperationResult(callerID, callerFunctionID, internalStatus.workflowName, true, {
622
613
  childWorkflowID: workflowID,
@@ -654,19 +645,24 @@ class DBOSExecutor {
654
645
  e.dbos_already_logged = true;
655
646
  internalStatus.error = utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(e));
656
647
  internalStatus.status = workflow_1.StatusString.ERROR;
657
- if (!exec.isDebugging) {
648
+ if (!exec.debugMode) {
658
649
  await exec.systemDatabase.recordWorkflowError(workflowID, internalStatus);
659
650
  }
660
- wCtxt.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
651
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
661
652
  }
662
653
  const runWorkflow = async () => {
663
654
  let result;
664
655
  // Execute the workflow.
665
656
  try {
666
- const callResult = await (0, context_1.runWithWorkflowContext)(wCtxt, () => {
667
- const callPromise = passContext
668
- ? wf.call(params.configuredInstance, wCtxt, ...args)
669
- : wf.call(params.configuredInstance, ...args);
657
+ const callResult = await (0, context_1.runWithParentContext)(pctx, {
658
+ presetID,
659
+ timeoutMS,
660
+ deadlineEpochMS,
661
+ workflowId: workflowID,
662
+ span,
663
+ logger: this.ctxLogger,
664
+ }, () => {
665
+ const callPromise = wf.call(params.configuredInstance, ...args);
670
666
  if ($deadlineEpochMS === undefined) {
671
667
  return callPromise;
672
668
  }
@@ -674,7 +670,7 @@ class DBOSExecutor {
674
670
  return callPromiseWithTimeout(callPromise, $deadlineEpochMS, this.systemDatabase);
675
671
  }
676
672
  });
677
- if (this.isDebugging) {
673
+ if (this.debugMode) {
678
674
  const recordedResult = DBOSExecutor.reviveResultOrError((await this.systemDatabase.awaitWorkflowResult(workflowID)));
679
675
  if (!resultsMatch(recordedResult, callResult)) {
680
676
  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)}`);
@@ -692,21 +688,21 @@ class DBOSExecutor {
692
688
  }
693
689
  internalStatus.output = utils_1.DBOSJSON.stringify(result);
694
690
  internalStatus.status = workflow_1.StatusString.SUCCESS;
695
- if (!this.isDebugging) {
691
+ if (!this.debugMode) {
696
692
  await this.systemDatabase.recordWorkflowOutput(workflowID, internalStatus);
697
693
  }
698
- wCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
694
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
699
695
  }
700
696
  catch (err) {
701
697
  if (err instanceof error_1.DBOSWorkflowConflictError) {
702
698
  // Retrieve the handle and wait for the result.
703
699
  const retrievedHandle = this.retrieveWorkflow(workflowID);
704
700
  result = await retrievedHandle.getResult();
705
- wCtxt.span.setAttribute('cached', true);
706
- wCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
701
+ span.setAttribute('cached', true);
702
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
707
703
  }
708
704
  else if (err instanceof error_1.DBOSWorkflowCancelledError) {
709
- wCtxt.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
705
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
710
706
  internalStatus.error = err.message;
711
707
  if (err.workflowID === workflowID) {
712
708
  internalStatus.status = workflow_1.StatusString.CANCELLED;
@@ -724,11 +720,11 @@ class DBOSExecutor {
724
720
  }
725
721
  }
726
722
  finally {
727
- this.tracer.endSpan(wCtxt.span);
723
+ this.tracer.endSpan(span);
728
724
  }
729
725
  return result;
730
726
  };
731
- if (this.isDebugging ||
727
+ if (this.debugMode ||
732
728
  (status !== 'SUCCESS' && status !== 'ERROR' && (params.queueName === undefined || params.executeWorkflow))) {
733
729
  const workflowPromise = runWorkflow();
734
730
  this.systemDatabase.registerRunningWorkflow(workflowID, workflowPromise);
@@ -794,7 +790,7 @@ class DBOSExecutor {
794
790
  * Write a operation's output to the database.
795
791
  */
796
792
  async #recordOutput(query, workflowUUID, funcID, txnSnapshot, output, isKeyConflict, function_name) {
797
- if (this.isDebugging) {
793
+ if (this.debugMode) {
798
794
  throw new error_1.DBOSDebuggerError('Cannot record output in debug mode.');
799
795
  }
800
796
  try {
@@ -816,7 +812,7 @@ class DBOSExecutor {
816
812
  * Record an error in an operation to the database.
817
813
  */
818
814
  async #recordError(query, workflowUUID, funcID, txnSnapshot, err, isKeyConflict, function_name) {
819
- if (this.isDebugging) {
815
+ if (this.debugMode) {
820
816
  throw new error_1.DBOSDebuggerError('Cannot record error in debug mode.');
821
817
  }
822
818
  try {
@@ -844,67 +840,61 @@ class DBOSExecutor {
844
840
  async transaction(txn, params, ...args) {
845
841
  return await (await this.startTransactionTempWF(txn, params, undefined, undefined, ...args)).getResult();
846
842
  }
847
- async startTransactionTempWF(txn, params, callerUUID, callerFunctionID, ...args) {
843
+ async startTransactionTempWF(txn, params, callerWFID, callerFunctionID, ...args) {
848
844
  // Create a workflow and call transaction.
849
- const temp_workflow = async (ctxt, ...args) => {
850
- const ctxtImpl = ctxt;
851
- return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ctxtImpl, ...args);
845
+ const temp_workflow = async (...args) => {
846
+ return await this.callTransactionFunction(txn, params.configuredInstance ?? null, ...args);
852
847
  };
853
848
  return await this.internalWorkflow(temp_workflow, {
854
849
  ...params,
855
850
  tempWfType: exports.TempWorkflowType.transaction,
856
851
  tempWfName: (0, decorators_1.getRegisteredMethodName)(txn),
857
852
  tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(txn),
858
- }, callerUUID, callerFunctionID, ...args);
853
+ }, callerWFID, callerFunctionID, ...args);
859
854
  }
860
- async callTransactionFunction(txn, clsinst, wfCtx, ...args) {
855
+ async callTransactionFunction(txn, clsinst, ...args) {
861
856
  const txnInfo = this.getTransactionInfo(txn);
862
857
  if (txnInfo === undefined) {
863
858
  throw new error_1.DBOSNotRegisteredError(txn.name);
864
859
  }
865
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
860
+ const pctx = (0, context_1.getCurrentContextStore)();
861
+ const wfid = pctx.workflowId;
862
+ await this.systemDatabase.checkIfCanceled(wfid);
866
863
  let retryWaitMillis = 1;
867
864
  const backoffFactor = 1.5;
868
865
  const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
869
- const funcId = wfCtx.functionIDGetIncrement();
866
+ const funcId = (0, context_1.functionIDGetIncrement)();
870
867
  const span = this.tracer.startSpan(txn.name, {
871
- operationUUID: wfCtx.workflowUUID,
868
+ operationUUID: wfid,
872
869
  operationType: exports.OperationType.TRANSACTION,
873
- authenticatedUser: wfCtx.authenticatedUser,
874
- assumedRole: wfCtx.assumedRole,
875
- authenticatedRoles: wfCtx.authenticatedRoles,
870
+ operationName: txn.name,
871
+ authenticatedUser: pctx.authenticatedUser ?? '',
872
+ assumedRole: pctx.assumedRole ?? '',
873
+ authenticatedRoles: pctx.authenticatedRoles ?? [],
876
874
  isolationLevel: txnInfo.config.isolationLevel,
877
- }, wfCtx.span);
875
+ }, pctx.span);
878
876
  while (true) {
879
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
877
+ await this.systemDatabase.checkIfCanceled(wfid);
880
878
  let txn_snapshot = 'invalid';
881
879
  let prevResultFound = false;
882
- const workflowUUID = wfCtx.workflowUUID;
883
880
  const wrappedTransaction = async (client) => {
884
- const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
885
881
  // If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
886
882
  // 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.
887
883
  let prevResult = exports.dbosNull;
888
884
  const queryFunc = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
889
- if (wfCtx.presetUUID) {
890
- const executionResult = await this.#checkExecution(queryFunc, workflowUUID, funcId, txn.name);
885
+ if (pctx.presetID) {
886
+ const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, txn.name);
891
887
  prevResult = executionResult.result;
892
888
  txn_snapshot = executionResult.txn_snapshot;
893
889
  if (prevResult !== exports.dbosNull) {
894
890
  prevResultFound = true;
895
- tCtxt.span.setAttribute('cached', true);
896
- if (this.debugMode === DebugMode.TIME_TRAVEL) {
897
- // for time travel debugging, navigate the proxy to the time of this transaction's snapshot
898
- await queryFunc(`--proxy:${executionResult.txn_id ?? ''}:${txn_snapshot}`, []);
891
+ span.setAttribute('cached', true);
892
+ // Return/throw the previous result
893
+ if (prevResult instanceof Error) {
894
+ throw prevResult;
899
895
  }
900
896
  else {
901
- // otherwise, return/throw the previous result
902
- if (prevResult instanceof Error) {
903
- throw prevResult;
904
- }
905
- else {
906
- return prevResult;
907
- }
897
+ return prevResult;
908
898
  }
909
899
  }
910
900
  }
@@ -912,27 +902,32 @@ class DBOSExecutor {
912
902
  // Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
913
903
  txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
914
904
  }
915
- if (this.isDebugging && prevResult === exports.dbosNull) {
916
- throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${workflowUUID}, step number ${funcId}`);
905
+ if (this.debugMode && prevResult === exports.dbosNull) {
906
+ throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the transaction: workflow UUID ${wfid}, step number ${funcId}`);
917
907
  }
918
908
  // Execute the user's transaction.
909
+ const ctxlog = this.ctxLogger;
919
910
  const result = await (async function () {
920
911
  try {
921
- return await (0, context_1.runWithTransactionContext)(tCtxt, async () => {
922
- if (txnInfo.registration.passContext) {
923
- return await txn.call(clsinst, tCtxt, ...args);
924
- }
925
- else {
926
- const tf = txn;
927
- return await tf.call(clsinst, ...args);
928
- }
912
+ return await (0, context_1.runWithParentContext)(pctx, {
913
+ authenticatedRoles: pctx?.authenticatedRoles,
914
+ authenticatedUser: pctx?.authenticatedUser,
915
+ workflowId: wfid,
916
+ curTxFunctionId: funcId,
917
+ parentCtx: pctx,
918
+ sqlClient: client,
919
+ logger: ctxlog,
920
+ span,
921
+ }, async () => {
922
+ const tf = txn;
923
+ return await tf.call(clsinst, ...args);
929
924
  });
930
925
  }
931
926
  catch (e) {
932
927
  return e instanceof Error ? e : new Error(`${e}`);
933
928
  }
934
929
  })();
935
- if (this.isDebugging) {
930
+ if (this.debugMode) {
936
931
  if (prevResult instanceof Error) {
937
932
  throw prevResult;
938
933
  }
@@ -949,13 +944,13 @@ class DBOSExecutor {
949
944
  // Record the execution, commit, and return.
950
945
  try {
951
946
  // Synchronously record the output of write transactions and obtain the transaction ID.
952
- const pg_txn_id = await this.#recordOutput(queryFunc, wfCtx.workflowUUID, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
953
- tCtxt.span.setAttribute('pg_txn_id', pg_txn_id);
947
+ const pg_txn_id = await this.#recordOutput(queryFunc, wfid, funcId, txn_snapshot, result, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
948
+ span.setAttribute('pg_txn_id', pg_txn_id);
954
949
  }
955
950
  catch (error) {
956
951
  if (this.userDatabase.isFailedSqlTransactionError(error)) {
957
- this.logger.error(`Postgres aborted the ${txn.name} @DBOS.transaction of Workflow ${workflowUUID}, but the function did not raise an exception. Please ensure that the @DBOS.transaction method raises an exception if the database transaction is aborted.`);
958
- throw new error_1.DBOSFailedSqlTransactionError(workflowUUID, txn.name);
952
+ 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.`);
953
+ throw new error_1.DBOSFailedSqlTransactionError(wfid, txn.name);
959
954
  }
960
955
  else {
961
956
  throw error;
@@ -985,7 +980,7 @@ class DBOSExecutor {
985
980
  const e = err;
986
981
  await this.userDatabase.transaction(async (client) => {
987
982
  const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
988
- await this.#recordError(func, wfCtx.workflowUUID, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
983
+ await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), txn.name);
989
984
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
990
985
  }
991
986
  span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
@@ -996,9 +991,8 @@ class DBOSExecutor {
996
991
  }
997
992
  async procedure(proc, params, ...args) {
998
993
  // Create a workflow and call procedure.
999
- const temp_workflow = async (ctxt, ...args) => {
1000
- const ctxtImpl = ctxt;
1001
- return this.callProcedureFunction(proc, ctxtImpl, ...args);
994
+ const temp_workflow = async (...args) => {
995
+ return this.callProcedureFunction(proc, ...args);
1002
996
  };
1003
997
  return await (await this.workflow(temp_workflow, {
1004
998
  ...params,
@@ -1007,27 +1001,30 @@ class DBOSExecutor {
1007
1001
  tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(proc),
1008
1002
  }, ...args)).getResult();
1009
1003
  }
1010
- async callProcedureFunction(proc, wfCtx, ...args) {
1004
+ async callProcedureFunction(proc, ...args) {
1011
1005
  const procInfo = this.getProcedureInfo(proc);
1012
1006
  if (procInfo === undefined) {
1013
1007
  throw new error_1.DBOSNotRegisteredError(proc.name);
1014
1008
  }
1015
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
1016
- const executeLocally = this.isDebugging || (procInfo.config.executeLocally ?? false);
1017
- const funcId = wfCtx.functionIDGetIncrement();
1009
+ const pctx = (0, context_1.getCurrentContextStore)();
1010
+ const wfid = pctx.workflowId;
1011
+ await this.systemDatabase.checkIfCanceled(wfid);
1012
+ const executeLocally = this.debugMode || (procInfo.config.executeLocally ?? false);
1013
+ const funcId = (0, context_1.functionIDGetIncrement)();
1018
1014
  const span = this.tracer.startSpan(proc.name, {
1019
- operationUUID: wfCtx.workflowUUID,
1015
+ operationUUID: wfid,
1020
1016
  operationType: exports.OperationType.PROCEDURE,
1021
- authenticatedUser: wfCtx.authenticatedUser,
1022
- assumedRole: wfCtx.assumedRole,
1023
- authenticatedRoles: wfCtx.authenticatedRoles,
1017
+ operationName: proc.name,
1018
+ authenticatedUser: pctx.authenticatedUser ?? '',
1019
+ assumedRole: pctx.assumedRole ?? '',
1020
+ authenticatedRoles: pctx.authenticatedRoles ?? [],
1024
1021
  isolationLevel: procInfo.config.isolationLevel,
1025
1022
  executeLocally,
1026
- }, wfCtx.span);
1023
+ }, pctx.span);
1027
1024
  try {
1028
1025
  const result = executeLocally
1029
- ? await this.#callProcedureFunctionLocal(proc, args, wfCtx, span, procInfo, funcId)
1030
- : await this.#callProcedureFunctionRemote(proc, args, wfCtx, span, procInfo.config, funcId);
1026
+ ? await this.#callProcedureFunctionLocal(proc, args, span, procInfo, funcId)
1027
+ : await this.#callProcedureFunctionRemote(proc, args, span, procInfo.config, funcId);
1031
1028
  span.setStatus({ code: api_1.SpanStatusCode.OK });
1032
1029
  return result;
1033
1030
  }
@@ -1040,35 +1037,30 @@ class DBOSExecutor {
1040
1037
  this.tracer.endSpan(span);
1041
1038
  }
1042
1039
  }
1043
- async #callProcedureFunctionLocal(proc, args, wfCtx, span, procInfo, funcId) {
1040
+ async #callProcedureFunctionLocal(proc, args, span, procInfo, funcId) {
1044
1041
  let retryWaitMillis = 1;
1045
1042
  const backoffFactor = 1.5;
1046
1043
  const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
1044
+ const pctx = (0, context_1.getCurrentContextStore)();
1045
+ const wfid = pctx.workflowId;
1047
1046
  while (true) {
1048
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
1047
+ await this.systemDatabase.checkIfCanceled(wfid);
1049
1048
  let txn_snapshot = 'invalid';
1050
1049
  const wrappedProcedure = async (client) => {
1051
- const ctxt = new procedure_1.StoredProcedureContextImpl(client, wfCtx, span, this.logger, funcId, proc.name);
1052
1050
  let prevResult = exports.dbosNull;
1053
1051
  const queryFunc = (sql, args) => this.procedurePool.query(sql, args).then((v) => v.rows);
1054
- if (wfCtx.presetUUID) {
1055
- const executionResult = await this.#checkExecution(queryFunc, wfCtx.workflowUUID, funcId, wfCtx.operationName);
1052
+ if (pctx.presetID) {
1053
+ const executionResult = await this.#checkExecution(queryFunc, wfid, funcId, proc.name);
1056
1054
  prevResult = executionResult.result;
1057
1055
  txn_snapshot = executionResult.txn_snapshot;
1058
1056
  if (prevResult !== exports.dbosNull) {
1059
- ctxt.span.setAttribute('cached', true);
1060
- if (this.debugMode === DebugMode.TIME_TRAVEL) {
1061
- // for time travel debugging, navigate the proxy to the time of this transaction's snapshot
1062
- await queryFunc(`--proxy:${executionResult.txn_id ?? ''}:${txn_snapshot}`, []);
1057
+ span.setAttribute('cached', true);
1058
+ // Return/throw the previous result
1059
+ if (prevResult instanceof Error) {
1060
+ throw prevResult;
1063
1061
  }
1064
1062
  else {
1065
- // otherwise, return/throw the previous result
1066
- if (prevResult instanceof Error) {
1067
- throw prevResult;
1068
- }
1069
- else {
1070
- return prevResult;
1071
- }
1063
+ return prevResult;
1072
1064
  }
1073
1065
  }
1074
1066
  }
@@ -1076,27 +1068,36 @@ class DBOSExecutor {
1076
1068
  // Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
1077
1069
  txn_snapshot = await DBOSExecutor.#retrieveSnapshot(queryFunc);
1078
1070
  }
1079
- if (this.isDebugging && prevResult === exports.dbosNull) {
1080
- throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${wfCtx.workflowUUID}, step number ${funcId}`);
1071
+ if (this.debugMode && prevResult === exports.dbosNull) {
1072
+ throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the procedure: workflow UUID ${wfid}, step number ${funcId}`);
1081
1073
  }
1082
1074
  // Execute the user's transaction.
1075
+ const ctxlog = this.ctxLogger;
1083
1076
  const result = await (async function () {
1084
1077
  try {
1085
- return await (0, context_1.runWithStoredProcContext)(ctxt, async () => {
1086
- if (procInfo.registration.passContext) {
1087
- return await proc(ctxt, ...args);
1088
- }
1089
- else {
1090
- const pf = proc;
1091
- return await pf(...args);
1092
- }
1078
+ // Check we are in a workflow context and not in a step / transaction already
1079
+ const pctx = (0, context_1.getCurrentContextStore)();
1080
+ if (!pctx)
1081
+ throw new error_1.DBOSInvalidWorkflowTransitionError();
1082
+ if (!(0, context_1.isInWorkflowCtx)(pctx))
1083
+ throw new error_1.DBOSInvalidWorkflowTransitionError();
1084
+ return await (0, context_1.runWithParentContext)(pctx, {
1085
+ curTxFunctionId: funcId,
1086
+ parentCtx: pctx,
1087
+ isInStoredProc: true,
1088
+ sqlClient: client,
1089
+ logger: ctxlog,
1090
+ span,
1091
+ }, async () => {
1092
+ const pf = proc;
1093
+ return await pf(...args);
1093
1094
  });
1094
1095
  }
1095
1096
  catch (e) {
1096
1097
  return e instanceof Error ? e : new Error(`${e}`);
1097
1098
  }
1098
1099
  })();
1099
- if (this.isDebugging) {
1100
+ if (this.debugMode) {
1100
1101
  if (prevResult instanceof Error) {
1101
1102
  throw prevResult;
1102
1103
  }
@@ -1112,9 +1113,9 @@ class DBOSExecutor {
1112
1113
  }
1113
1114
  // Synchronously record the output of write transactions and obtain the transaction ID.
1114
1115
  const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
1115
- const pg_txn_id = await this.#recordOutput(func, wfCtx.workflowUUID, funcId, txn_snapshot, result, user_database_1.pgNodeIsKeyConflictError, wfCtx.operationName);
1116
+ const pg_txn_id = await this.#recordOutput(func, wfid, funcId, txn_snapshot, result, user_database_1.pgNodeIsKeyConflictError, proc.name);
1116
1117
  // const pg_txn_id = await wfCtx.recordOutputProc<R>(client, funcId, txn_snapshot, result);
1117
- ctxt.span.setAttribute('pg_txn_id', pg_txn_id);
1118
+ span.setAttribute('pg_txn_id', pg_txn_id);
1118
1119
  return result;
1119
1120
  };
1120
1121
  try {
@@ -1125,7 +1126,7 @@ class DBOSExecutor {
1125
1126
  return result;
1126
1127
  }
1127
1128
  catch (err) {
1128
- if (!this.isDebugging) {
1129
+ if (!this.debugMode) {
1129
1130
  if (this.userDatabase.isRetriableTransactionError(err)) {
1130
1131
  // serialization_failure in PostgreSQL
1131
1132
  span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
@@ -1139,32 +1140,34 @@ class DBOSExecutor {
1139
1140
  const e = err;
1140
1141
  await this.invokeStoredProcFunction(async (client) => {
1141
1142
  const func = (sql, args) => client.query(sql, args).then((v) => v.rows);
1142
- await this.#recordError(func, wfCtx.workflowUUID, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError, wfCtx.operationName);
1143
+ await this.#recordError(func, wfid, funcId, txn_snapshot, e, user_database_1.pgNodeIsKeyConflictError, proc.name);
1143
1144
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
1144
1145
  await this.userDatabase.transaction(async (client) => {
1145
1146
  const func = (sql, args) => this.userDatabase.queryWithClient(client, sql, ...args);
1146
- await this.#recordError(func, wfCtx.workflowUUID, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), wfCtx.operationName);
1147
+ await this.#recordError(func, wfid, funcId, txn_snapshot, e, (error) => this.userDatabase.isKeyConflictError(error), proc.name);
1147
1148
  }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
1148
1149
  }
1149
1150
  throw err;
1150
1151
  }
1151
1152
  }
1152
1153
  }
1153
- async #callProcedureFunctionRemote(proc, args, wfCtx, span, config, funcId) {
1154
- if (this.isDebugging) {
1154
+ async #callProcedureFunctionRemote(proc, args, span, config, funcId) {
1155
+ if (this.debugMode) {
1155
1156
  throw new error_1.DBOSDebuggerError("Can't invoke stored procedure in debug mode.");
1156
1157
  }
1157
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
1158
+ const pctx = (0, context_1.getCurrentContextStore)();
1159
+ const wfid = pctx.workflowId;
1160
+ await this.systemDatabase.checkIfCanceled(wfid);
1158
1161
  const $jsonCtx = {
1159
- request: wfCtx.request,
1160
- authenticatedUser: wfCtx.authenticatedUser,
1161
- authenticatedRoles: wfCtx.authenticatedRoles,
1162
- assumedRole: wfCtx.assumedRole,
1162
+ request: pctx.request,
1163
+ authenticatedUser: pctx.authenticatedUser,
1164
+ authenticatedRoles: pctx.authenticatedRoles,
1165
+ assumedRole: pctx.assumedRole,
1163
1166
  };
1164
1167
  // TODO (Qian/Harry): remove this unshift when we remove the resultBuffer argument
1165
1168
  // Note, node-pg converts JS arrays to postgres array literals, so must call JSON.strigify on
1166
1169
  // args and bufferedResults before being passed to #invokeStoredProc
1167
- const $args = [wfCtx.workflowUUID, funcId, wfCtx.presetUUID, $jsonCtx, null, JSON.stringify(args)];
1170
+ const $args = [wfid, funcId, pctx.presetID, $jsonCtx, null, JSON.stringify(args)];
1168
1171
  const readonly = config.readOnly ?? false;
1169
1172
  if (!readonly) {
1170
1173
  $args.unshift(null);
@@ -1222,103 +1225,96 @@ class DBOSExecutor {
1222
1225
  async external(stepFn, params, ...args) {
1223
1226
  return await (await this.startStepTempWF(stepFn, params, undefined, undefined, ...args)).getResult();
1224
1227
  }
1225
- async startStepTempWF(stepFn, params, callerUUID, callerFunctionID, ...args) {
1228
+ async startStepTempWF(stepFn, params, callerWFID, callerFunctionID, ...args) {
1226
1229
  // Create a workflow and call external.
1227
- const temp_workflow = async (ctxt, ...args) => {
1228
- const ctxtImpl = ctxt;
1229
- return await this.callStepFunction(stepFn, undefined, undefined, params.configuredInstance ?? null, ctxtImpl, ...args);
1230
+ const temp_workflow = async (...args) => {
1231
+ return await this.callStepFunction(stepFn, undefined, undefined, params.configuredInstance ?? null, ...args);
1230
1232
  };
1231
1233
  return await this.internalWorkflow(temp_workflow, {
1232
1234
  ...params,
1233
1235
  tempWfType: exports.TempWorkflowType.step,
1234
1236
  tempWfName: (0, decorators_1.getRegisteredMethodName)(stepFn),
1235
1237
  tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(stepFn),
1236
- }, callerUUID, callerFunctionID, ...args);
1238
+ }, callerWFID, callerFunctionID, ...args);
1237
1239
  }
1238
1240
  /**
1239
1241
  * Execute a step function.
1240
1242
  * If it encounters any error, retry according to its configured retry policy until the maximum number of attempts is reached, then throw an DBOSError.
1241
1243
  * The step may execute many times, but once it is complete, it will not re-execute.
1242
1244
  */
1243
- async callStepFunction(stepFn, stepFnName, stepConfig, clsInst, wfCtx, ...args) {
1245
+ async callStepFunction(stepFn, stepFnName, stepConfig, clsInst, ...args) {
1244
1246
  stepFnName = stepFnName ?? stepFn.name ?? '<unnamed>';
1245
- let passContext = false;
1246
1247
  if (!stepConfig) {
1247
1248
  const stepReg = this.getStepInfo(stepFn);
1248
1249
  stepConfig = stepReg?.config;
1249
- passContext = stepReg?.registration.passContext ?? true;
1250
1250
  }
1251
1251
  if (stepConfig === undefined) {
1252
1252
  throw new error_1.DBOSNotRegisteredError(stepFnName);
1253
1253
  }
1254
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
1255
- const funcID = wfCtx.functionIDGetIncrement();
1254
+ const lctx = (0, context_1.getCurrentContextStore)();
1255
+ const wfid = lctx.workflowId;
1256
+ await this.systemDatabase.checkIfCanceled(wfid);
1257
+ const funcID = (0, context_1.functionIDGetIncrement)();
1256
1258
  const maxRetryIntervalSec = 3600; // Maximum retry interval: 1 hour
1257
1259
  const span = this.tracer.startSpan(stepFnName, {
1258
- operationUUID: wfCtx.workflowUUID,
1259
- operationType: exports.OperationType.COMMUNICATOR,
1260
- authenticatedUser: wfCtx.authenticatedUser,
1261
- assumedRole: wfCtx.assumedRole,
1262
- authenticatedRoles: wfCtx.authenticatedRoles,
1260
+ operationUUID: wfid,
1261
+ operationType: exports.OperationType.STEP,
1262
+ operationName: stepFnName,
1263
+ authenticatedUser: lctx.authenticatedUser ?? '',
1264
+ assumedRole: lctx.assumedRole ?? '',
1265
+ authenticatedRoles: lctx.authenticatedRoles ?? [],
1263
1266
  retriesAllowed: stepConfig.retriesAllowed,
1264
1267
  intervalSeconds: stepConfig.intervalSeconds,
1265
1268
  maxAttempts: stepConfig.maxAttempts,
1266
1269
  backoffRate: stepConfig.backoffRate,
1267
- }, wfCtx.span);
1268
- const ctxt = new step_1.StepContextImpl(wfCtx, funcID, span, this.logger, stepConfig, stepFnName);
1270
+ }, lctx.span);
1269
1271
  // Check if this execution previously happened, returning its original result if it did.
1270
- const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(wfCtx.workflowUUID, ctxt.functionID);
1272
+ const checkr = await this.systemDatabase.getOperationResultAndThrowIfCancelled(wfid, funcID);
1271
1273
  if (checkr) {
1272
- if (checkr.functionName !== ctxt.operationName) {
1273
- throw new error_1.DBOSUnexpectedStepError(ctxt.workflowUUID, ctxt.functionID, ctxt.operationName, checkr.functionName ?? '?');
1274
+ if (checkr.functionName !== stepFnName) {
1275
+ throw new error_1.DBOSUnexpectedStepError(wfid, funcID, stepFnName, checkr.functionName ?? '?');
1274
1276
  }
1275
1277
  const check = DBOSExecutor.reviveResultOrError(checkr);
1276
- ctxt.span.setAttribute('cached', true);
1277
- ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
1278
- this.tracer.endSpan(ctxt.span);
1278
+ span.setAttribute('cached', true);
1279
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
1280
+ this.tracer.endSpan(span);
1279
1281
  return check;
1280
1282
  }
1281
- if (this.isDebugging) {
1282
- throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${wfCtx.workflowUUID}, step number: ${funcID}`);
1283
+ if (this.debugMode) {
1284
+ throw new error_1.DBOSDebuggerError(`Failed to find the recorded output for the step: workflow UUID: ${wfid}, step number: ${funcID}`);
1283
1285
  }
1286
+ const maxAttempts = stepConfig.maxAttempts ?? 3;
1284
1287
  // Execute the step function. If it throws an exception, retry with exponential backoff.
1285
1288
  // After reaching the maximum number of retries, throw an DBOSError.
1286
1289
  let result = exports.dbosNull;
1287
1290
  let err = exports.dbosNull;
1288
1291
  const errors = [];
1289
- if (ctxt.retriesAllowed) {
1290
- let numAttempts = 0;
1291
- let intervalSeconds = ctxt.intervalSeconds;
1292
+ if (stepConfig.retriesAllowed) {
1293
+ let attemptNum = 0;
1294
+ let intervalSeconds = stepConfig.intervalSeconds ?? 1;
1292
1295
  if (intervalSeconds > maxRetryIntervalSec) {
1293
1296
  this.logger.warn(`Step config interval exceeds maximum allowed interval, capped to ${maxRetryIntervalSec} seconds!`);
1294
1297
  }
1295
- while (result === exports.dbosNull && numAttempts++ < ctxt.maxAttempts) {
1298
+ while (result === exports.dbosNull && attemptNum++ < (maxAttempts ?? 3)) {
1296
1299
  try {
1297
- await this.systemDatabase.checkIfCanceled(wfCtx.workflowUUID);
1300
+ await this.systemDatabase.checkIfCanceled(wfid);
1298
1301
  let cresult;
1299
- if (passContext) {
1300
- await (0, context_1.runWithStepContext)(ctxt, numAttempts, async () => {
1301
- cresult = await stepFn.call(clsInst, ctxt, ...args);
1302
- });
1303
- }
1304
- else {
1305
- await (0, context_1.runWithStepContext)(ctxt, numAttempts, async () => {
1306
- const sf = stepFn;
1307
- cresult = await sf.call(clsInst, ...args);
1308
- });
1309
- }
1302
+ await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, attemptNum, async () => {
1303
+ const sf = stepFn;
1304
+ cresult = await sf.call(clsInst, ...args);
1305
+ });
1310
1306
  result = cresult;
1311
1307
  }
1312
1308
  catch (error) {
1313
1309
  const e = error;
1314
1310
  errors.push(e);
1315
- this.logger.warn(`Error in step being automatically retried. Attempt ${numAttempts} of ${ctxt.maxAttempts}. ${e.stack}`);
1316
- span.addEvent(`Step attempt ${numAttempts + 1} failed`, { retryIntervalSeconds: intervalSeconds, error: error.message }, performance.now());
1317
- if (numAttempts < ctxt.maxAttempts) {
1311
+ this.logger.warn(`Error in step being automatically retried. Attempt ${attemptNum} of ${maxAttempts}. ${e.stack}`);
1312
+ span.addEvent(`Step attempt ${attemptNum + 1} failed`, { retryIntervalSeconds: intervalSeconds, error: error.message }, performance.now());
1313
+ if (attemptNum < maxAttempts) {
1318
1314
  // Sleep for an interval, then increase the interval by backoffRate.
1319
1315
  // Cap at the maximum allowed retry interval.
1320
1316
  await (0, utils_1.sleepms)(intervalSeconds * 1000);
1321
- intervalSeconds *= ctxt.backoffRate;
1317
+ intervalSeconds *= stepConfig.backoffRate ?? 2;
1322
1318
  intervalSeconds = intervalSeconds < maxRetryIntervalSec ? intervalSeconds : maxRetryIntervalSec;
1323
1319
  }
1324
1320
  }
@@ -1327,17 +1323,10 @@ class DBOSExecutor {
1327
1323
  else {
1328
1324
  try {
1329
1325
  let cresult;
1330
- if (passContext) {
1331
- await (0, context_1.runWithStepContext)(ctxt, undefined, async () => {
1332
- cresult = await stepFn.call(clsInst, ctxt, ...args);
1333
- });
1334
- }
1335
- else {
1336
- await (0, context_1.runWithStepContext)(ctxt, undefined, async () => {
1337
- const sf = stepFn;
1338
- cresult = await sf.call(clsInst, ...args);
1339
- });
1340
- }
1326
+ await (0, context_1.runInStepContext)(lctx, funcID, span, maxAttempts, undefined, async () => {
1327
+ const sf = stepFn;
1328
+ cresult = await sf.call(clsInst, ...args);
1329
+ });
1341
1330
  result = cresult;
1342
1331
  }
1343
1332
  catch (error) {
@@ -1347,35 +1336,37 @@ class DBOSExecutor {
1347
1336
  // `result` can only be dbosNull when the step timed out
1348
1337
  if (result === exports.dbosNull) {
1349
1338
  // Record the error, then throw it.
1350
- err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName, ctxt.maxAttempts, errors) : err;
1351
- await this.systemDatabase.recordOperationResult(wfCtx.workflowUUID, ctxt.functionID, ctxt.operationName, true, {
1339
+ err = err === exports.dbosNull ? new error_1.DBOSMaxStepRetriesError(stepFnName, maxAttempts, errors) : err;
1340
+ await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
1352
1341
  error: utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(err)),
1353
1342
  });
1354
- ctxt.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
1355
- this.tracer.endSpan(ctxt.span);
1343
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
1344
+ this.tracer.endSpan(span);
1356
1345
  throw err;
1357
1346
  }
1358
1347
  else {
1359
1348
  // Record the execution and return.
1360
- await this.systemDatabase.recordOperationResult(wfCtx.workflowUUID, ctxt.functionID, ctxt.operationName, true, {
1349
+ await this.systemDatabase.recordOperationResult(wfid, funcID, stepFnName, true, {
1361
1350
  output: utils_1.DBOSJSON.stringify(result),
1362
1351
  });
1363
- ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
1364
- this.tracer.endSpan(ctxt.span);
1352
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
1353
+ this.tracer.endSpan(span);
1365
1354
  return result;
1366
1355
  }
1367
1356
  }
1368
- async send(destinationUUID, message, topic, idempotencyKey) {
1357
+ async send(destinationId, message, topic, idempotencyKey) {
1369
1358
  // Create a workflow and call send.
1370
- const temp_workflow = async (ctxt, destinationUUID, message, topic) => {
1371
- return await ctxt.send(destinationUUID, message, topic);
1359
+ const temp_workflow = async (destinationId, message, topic) => {
1360
+ const ctx = (0, context_1.getCurrentContextStore)();
1361
+ const functionID = (0, context_1.functionIDGetIncrement)();
1362
+ await this.systemDatabase.send(ctx.workflowId, functionID, destinationId, utils_1.DBOSJSON.stringify(message), topic);
1372
1363
  };
1373
- const workflowUUID = idempotencyKey ? destinationUUID + idempotencyKey : undefined;
1364
+ const workflowUUID = idempotencyKey ? destinationId + idempotencyKey : undefined;
1374
1365
  return (await this.workflow(temp_workflow, {
1375
1366
  workflowUUID: workflowUUID,
1376
1367
  tempWfType: exports.TempWorkflowType.send,
1377
1368
  configuredInstance: null,
1378
- }, destinationUUID, message, topic)).getResult();
1369
+ }, destinationId, message, topic)).getResult();
1379
1370
  }
1380
1371
  /**
1381
1372
  * Wait for a workflow to emit an event, then return its value.
@@ -1469,7 +1460,7 @@ class DBOSExecutor {
1469
1460
  * It runs to completion all pending workflows that were executing when the previous executor failed.
1470
1461
  */
1471
1462
  async recoverPendingWorkflows(executorIDs = ['local']) {
1472
- if (this.isDebugging) {
1463
+ if (this.debugMode) {
1473
1464
  throw new error_1.DBOSDebuggerError('Cannot recover pending workflows in debug mode.');
1474
1465
  }
1475
1466
  const handlerArray = [];
@@ -1514,14 +1505,14 @@ class DBOSExecutor {
1514
1505
  await evtRcvr.initialize(this);
1515
1506
  }
1516
1507
  for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
1517
- await lcl.initialize();
1508
+ await lcl.initialize?.();
1518
1509
  }
1519
1510
  }
1520
1511
  async deactivateEventReceivers(stopQueueThread = true) {
1521
1512
  this.logger.debug('Deactivating lifecycle listeners');
1522
1513
  for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
1523
1514
  try {
1524
- await lcl.destroy();
1515
+ await lcl.destroy?.();
1525
1516
  }
1526
1517
  catch (err) {
1527
1518
  const e = err;
@@ -1567,19 +1558,20 @@ class DBOSExecutor {
1567
1558
  throw new error_1.DBOSError(`Failed to find inputs for workflow UUID: ${workflowID}`);
1568
1559
  }
1569
1560
  const inputs = utils_1.DBOSJSON.parse(wfStatus.input);
1570
- const parentCtx = this.#getRecoveryContext(workflowID, wfStatus);
1561
+ const recoverCtx = this.#getRecoveryContext(workflowID, wfStatus);
1571
1562
  const { wfInfo, configuredInst } = this.getWorkflowInfoByStatus(wfStatus);
1572
1563
  // If starting a new workflow, assign a new UUID. Otherwise, use the workflow's original UUID.
1573
1564
  const workflowStartID = startNewWorkflow ? undefined : workflowID;
1574
1565
  if (wfInfo) {
1575
- return this.workflow(wfInfo.workflow, {
1576
- workflowUUID: workflowStartID,
1577
- parentCtx: parentCtx,
1578
- configuredInstance: configuredInst,
1579
- queueName: wfStatus.queueName,
1580
- executeWorkflow: true,
1581
- deadlineEpochMS: wfStatus.deadlineEpochMS,
1582
- }, ...inputs);
1566
+ return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
1567
+ return await this.workflow(wfInfo.workflow, {
1568
+ workflowUUID: workflowStartID,
1569
+ configuredInstance: configuredInst,
1570
+ queueName: wfStatus.queueName,
1571
+ executeWorkflow: true,
1572
+ deadlineEpochMS: wfStatus.deadlineEpochMS,
1573
+ }, ...inputs);
1574
+ });
1583
1575
  }
1584
1576
  // Should be temporary workflows. Parse the name of the workflow.
1585
1577
  const wfName = wfStatus.workflowName;
@@ -1588,20 +1580,20 @@ class DBOSExecutor {
1588
1580
  // CB - Doesn't this happen if the user changed the function name in their code?
1589
1581
  throw new error_1.DBOSError(`This should never happen! Cannot find workflow info for a non-temporary workflow! UUID ${workflowID}, name ${wfName}`);
1590
1582
  }
1591
- let temp_workflow;
1592
1583
  if (nameArr[1] === exports.TempWorkflowType.transaction) {
1593
1584
  const { txnInfo, clsInst } = this.getTransactionInfoByNames(wfStatus.workflowClassName, nameArr[2], wfStatus.workflowConfigName);
1594
1585
  if (!txnInfo) {
1595
1586
  this.logger.error(`Cannot find transaction info for UUID ${workflowID}, name ${nameArr[2]}`);
1596
1587
  throw new error_1.DBOSNotRegisteredError(nameArr[2]);
1597
1588
  }
1598
- return await this.startTransactionTempWF(txnInfo.transaction, {
1599
- workflowUUID: workflowStartID,
1600
- parentCtx: parentCtx ?? undefined,
1601
- configuredInstance: clsInst,
1602
- queueName: wfStatus.queueName,
1603
- executeWorkflow: true,
1604
- }, undefined, undefined, ...inputs);
1589
+ return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
1590
+ return await this.startTransactionTempWF(txnInfo.transaction, {
1591
+ workflowUUID: workflowStartID,
1592
+ configuredInstance: clsInst,
1593
+ queueName: wfStatus.queueName,
1594
+ executeWorkflow: true,
1595
+ }, undefined, undefined, ...inputs);
1596
+ });
1605
1597
  }
1606
1598
  else if (nameArr[1] === exports.TempWorkflowType.step) {
1607
1599
  const { commInfo, clsInst } = this.getStepInfoByNames(wfStatus.workflowClassName, nameArr[2], wfStatus.workflowConfigName);
@@ -1609,25 +1601,30 @@ class DBOSExecutor {
1609
1601
  this.logger.error(`Cannot find step info for UUID ${workflowID}, name ${nameArr[2]}`);
1610
1602
  throw new error_1.DBOSNotRegisteredError(nameArr[2]);
1611
1603
  }
1612
- return await this.startStepTempWF(commInfo.step, {
1613
- workflowUUID: workflowStartID,
1614
- parentCtx: parentCtx ?? undefined,
1615
- configuredInstance: clsInst,
1616
- queueName: wfStatus.queueName, // Probably null
1617
- executeWorkflow: true,
1618
- }, undefined, undefined, ...inputs);
1604
+ return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
1605
+ return await this.startStepTempWF(commInfo.step, {
1606
+ workflowUUID: workflowStartID,
1607
+ configuredInstance: clsInst,
1608
+ queueName: wfStatus.queueName, // Probably null
1609
+ executeWorkflow: true,
1610
+ }, undefined, undefined, ...inputs);
1611
+ });
1619
1612
  }
1620
1613
  else if (nameArr[1] === exports.TempWorkflowType.send) {
1621
- temp_workflow = async (ctxt, ...args) => {
1622
- return await ctxt.send(args[0], args[1], args[2]); // id, value, topic
1614
+ const swf = async (destinationID, message, topic) => {
1615
+ const ctx = (0, context_1.getCurrentContextStore)();
1616
+ const functionID = (0, context_1.functionIDGetIncrement)();
1617
+ await this.systemDatabase.send(ctx.workflowId, functionID, destinationID, utils_1.DBOSJSON.stringify(message), topic);
1623
1618
  };
1624
- return this.workflow(temp_workflow, {
1625
- workflowUUID: workflowStartID,
1626
- parentCtx: parentCtx ?? undefined,
1627
- tempWfType: exports.TempWorkflowType.send,
1628
- queueName: wfStatus.queueName,
1629
- executeWorkflow: true,
1630
- }, ...inputs);
1619
+ const temp_workflow = swf;
1620
+ return await (0, context_1.runWithTopContext)(recoverCtx, async () => {
1621
+ return this.workflow(temp_workflow, {
1622
+ workflowUUID: workflowStartID,
1623
+ tempWfType: exports.TempWorkflowType.send,
1624
+ queueName: wfStatus.queueName,
1625
+ executeWorkflow: true,
1626
+ }, ...inputs);
1627
+ });
1631
1628
  }
1632
1629
  else {
1633
1630
  this.logger.error(`Unrecognized temporary workflow! UUID ${workflowID}, name ${wfName}`);
@@ -1640,14 +1637,13 @@ class DBOSExecutor {
1640
1637
  async upsertEventDispatchState(state) {
1641
1638
  return await this.systemDatabase.upsertEventDispatchState(state);
1642
1639
  }
1643
- #getRecoveryContext(workflowUUID, status) {
1640
+ #getRecoveryContext(_workflowID, status) {
1644
1641
  // Note: this doesn't inherit the original parent context's span.
1645
- const oc = new context_1.DBOSContextImpl(status.workflowName, undefined, this.logger);
1642
+ const oc = {};
1646
1643
  oc.request = status.request;
1647
1644
  oc.authenticatedUser = status.authenticatedUser;
1648
1645
  oc.authenticatedRoles = status.authenticatedRoles;
1649
1646
  oc.assumedRole = status.assumedRole;
1650
- oc.workflowUUID = workflowUUID;
1651
1647
  return oc;
1652
1648
  }
1653
1649
  async cancelWorkflow(workflowID) {