@dbos-inc/dbos-sdk 1.28.14-preview.g65f6845035 → 1.29.83-preview.g0c5a5d6c8e

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. package/dist/src/context.d.ts +36 -0
  2. package/dist/src/context.d.ts.map +1 -1
  3. package/dist/src/context.js +104 -1
  4. package/dist/src/context.js.map +1 -1
  5. package/dist/src/data_validation.d.ts.map +1 -1
  6. package/dist/src/data_validation.js +6 -2
  7. package/dist/src/data_validation.js.map +1 -1
  8. package/dist/src/dbos-executor.d.ts +36 -20
  9. package/dist/src/dbos-executor.d.ts.map +1 -1
  10. package/dist/src/dbos-executor.js +309 -21
  11. package/dist/src/dbos-executor.js.map +1 -1
  12. package/dist/src/dbos-runtime/runtime.d.ts +3 -8
  13. package/dist/src/dbos-runtime/runtime.d.ts.map +1 -1
  14. package/dist/src/dbos-runtime/runtime.js +7 -11
  15. package/dist/src/dbos-runtime/runtime.js.map +1 -1
  16. package/dist/src/dbos.d.ts +109 -0
  17. package/dist/src/dbos.d.ts.map +1 -0
  18. package/dist/src/dbos.js +551 -0
  19. package/dist/src/dbos.js.map +1 -0
  20. package/dist/src/debugger/debug_workflow.d.ts.map +1 -1
  21. package/dist/src/debugger/debug_workflow.js +5 -1
  22. package/dist/src/debugger/debug_workflow.js.map +1 -1
  23. package/dist/src/decorators.d.ts +11 -2
  24. package/dist/src/decorators.d.ts.map +1 -1
  25. package/dist/src/decorators.js +30 -8
  26. package/dist/src/decorators.js.map +1 -1
  27. package/dist/src/error.d.ts +6 -0
  28. package/dist/src/error.d.ts.map +1 -1
  29. package/dist/src/error.js +20 -2
  30. package/dist/src/error.js.map +1 -1
  31. package/dist/src/eventreceiver.d.ts +6 -2
  32. package/dist/src/eventreceiver.d.ts.map +1 -1
  33. package/dist/src/httpServer/handler.d.ts +0 -1
  34. package/dist/src/httpServer/handler.d.ts.map +1 -1
  35. package/dist/src/httpServer/handler.js +5 -13
  36. package/dist/src/httpServer/handler.js.map +1 -1
  37. package/dist/src/httpServer/middleware.d.ts +13 -2
  38. package/dist/src/httpServer/middleware.d.ts.map +1 -1
  39. package/dist/src/httpServer/middleware.js +101 -1
  40. package/dist/src/httpServer/middleware.js.map +1 -1
  41. package/dist/src/httpServer/server.d.ts +3 -2
  42. package/dist/src/httpServer/server.d.ts.map +1 -1
  43. package/dist/src/httpServer/server.js +40 -30
  44. package/dist/src/httpServer/server.js.map +1 -1
  45. package/dist/src/index.d.ts +2 -1
  46. package/dist/src/index.d.ts.map +1 -1
  47. package/dist/src/index.js +3 -3
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/procedure.d.ts +1 -0
  50. package/dist/src/procedure.d.ts.map +1 -1
  51. package/dist/src/procedure.js.map +1 -1
  52. package/dist/src/scheduler/scheduler.d.ts.map +1 -1
  53. package/dist/src/scheduler/scheduler.js +3 -1
  54. package/dist/src/scheduler/scheduler.js.map +1 -1
  55. package/dist/src/system_database.d.ts.map +1 -1
  56. package/dist/src/system_database.js.map +1 -1
  57. package/dist/src/telemetry/logs.d.ts +11 -5
  58. package/dist/src/telemetry/logs.d.ts.map +1 -1
  59. package/dist/src/telemetry/logs.js +8 -11
  60. package/dist/src/telemetry/logs.js.map +1 -1
  61. package/dist/src/testing/testing_runtime.d.ts.map +1 -1
  62. package/dist/src/testing/testing_runtime.js +10 -4
  63. package/dist/src/testing/testing_runtime.js.map +1 -1
  64. package/dist/src/user_database.d.ts +1 -1
  65. package/dist/src/user_database.d.ts.map +1 -1
  66. package/dist/src/wfqueue.d.ts.map +1 -1
  67. package/dist/src/wfqueue.js +17 -2
  68. package/dist/src/wfqueue.js.map +1 -1
  69. package/dist/src/workflow.d.ts +7 -6
  70. package/dist/src/workflow.d.ts.map +1 -1
  71. package/dist/src/workflow.js +10 -187
  72. package/dist/src/workflow.js.map +1 -1
  73. package/dist/tsconfig.build.tsbuildinfo +1 -1
  74. package/package.json +5 -1
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DBOSExecutor = exports.OperationType = exports.dbosNull = void 0;
7
- /* eslint-disable @typescript-eslint/no-explicit-any */
8
7
  const error_1 = require("./error");
9
8
  const workflow_1 = require("./workflow");
10
9
  const transaction_1 = require("./transaction");
10
+ const step_1 = require("./step");
11
11
  const collector_1 = require("./telemetry/collector");
12
12
  const traces_1 = require("./telemetry/traces");
13
13
  const logs_1 = require("./telemetry/logs");
@@ -84,7 +84,9 @@ class DBOSExecutor {
84
84
  typeormEntities = [];
85
85
  drizzleEntities = {};
86
86
  eventReceivers = [];
87
- scheduler = null;
87
+ scheduler = undefined;
88
+ wfqEnded = undefined;
89
+ static globalInstance = undefined;
88
90
  /* WORKFLOW EXECUTOR LIFE CYCLE MANAGEMENT */
89
91
  constructor(config, systemDatabase) {
90
92
  this.config = config;
@@ -143,6 +145,7 @@ class DBOSExecutor {
143
145
  }, this.flushBufferIntervalMs);
144
146
  this.logger.debug("Started workflow status buffer worker");
145
147
  this.initialized = false;
148
+ DBOSExecutor.globalInstance = this;
146
149
  }
147
150
  configureDbClient() {
148
151
  const userDbClient = this.config.userDbclient;
@@ -387,6 +390,9 @@ class DBOSExecutor {
387
390
  }
388
391
  await this.procedurePool.end();
389
392
  await this.logger.destroy();
393
+ if (DBOSExecutor.globalInstance === this) {
394
+ DBOSExecutor.globalInstance = undefined;
395
+ }
390
396
  }
391
397
  /* WORKFLOW OPERATIONS */
392
398
  #registerWorkflow(ro) {
@@ -401,6 +407,7 @@ class DBOSExecutor {
401
407
  const workflowInfo = {
402
408
  workflow: wf,
403
409
  config: { ...ro.workflowConfig },
410
+ registration: ro,
404
411
  };
405
412
  this.workflowInfoMap.set(wfn, workflowInfo);
406
413
  this.logger.debug(`Registered workflow ${wfn}`);
@@ -414,6 +421,7 @@ class DBOSExecutor {
414
421
  const txnInfo = {
415
422
  transaction: txf,
416
423
  config: { ...ro.txnConfig },
424
+ registration: ro,
417
425
  };
418
426
  this.transactionInfoMap.set(tfn, txnInfo);
419
427
  this.logger.debug(`Registered transaction ${tfn}`);
@@ -424,11 +432,12 @@ class DBOSExecutor {
424
432
  if (this.stepInfoMap.has(cfn)) {
425
433
  throw new error_1.DBOSError(`Repeated Commmunicator name: ${cfn}`);
426
434
  }
427
- const commInfo = {
435
+ const stepInfo = {
428
436
  step: comm,
429
437
  config: { ...ro.commConfig },
438
+ registration: ro,
430
439
  };
431
- this.stepInfoMap.set(cfn, commInfo);
440
+ this.stepInfoMap.set(cfn, stepInfo);
432
441
  this.logger.debug(`Registered step ${cfn}`);
433
442
  }
434
443
  #registerProcedure(ro) {
@@ -440,6 +449,7 @@ class DBOSExecutor {
440
449
  const procInfo = {
441
450
  procedure: proc,
442
451
  config: { ...ro.procConfig },
452
+ registration: ro,
443
453
  };
444
454
  this.procedureInfoMap.set(cfn, procInfo);
445
455
  this.logger.debug(`Registered stored proc ${cfn}`);
@@ -532,6 +542,28 @@ class DBOSExecutor {
532
542
  throw new error_1.DBOSNotRegisteredError(wf.name);
533
543
  }
534
544
  const wConfig = wInfo.config;
545
+ // Compatibility with the old way of calling workflows, which would include a parentCtx
546
+ const pctx = (0, context_1.getCurrentContextStore)();
547
+ const passContext = wInfo.registration?.passContext ?? true;
548
+ if (!passContext && pctx) {
549
+ const span = this.tracer.startSpan(wf.name, {
550
+ operationUUID: workflowUUID,
551
+ operationType: exports.OperationType.WORKFLOW,
552
+ status: workflow_1.StatusString.PENDING,
553
+ authenticatedUser: pctx.authenticatedUser,
554
+ assumedRole: pctx.assumedRole,
555
+ authenticatedRoles: pctx.authenticatedRoles,
556
+ }
557
+ // TODO the span should be taken from pctx
558
+ //pctx.span
559
+ );
560
+ params.parentCtx = new context_1.DBOSContextImpl(wf.name, span, this.logger);
561
+ params.parentCtx.request = pctx.request || {};
562
+ params.parentCtx.authenticatedUser = pctx.authenticatedUser || "";
563
+ params.parentCtx.assumedRole = pctx.assumedRole || "";
564
+ params.parentCtx.authenticatedRoles = pctx.authenticatedRoles || [];
565
+ params.parentCtx.workflowUUID = workflowUUID;
566
+ }
535
567
  const wCtxt = new workflow_1.WorkflowContextImpl(this, params.parentCtx, workflowUUID, wConfig, wf.name, presetUUID, params.tempWfType, params.tempWfName);
536
568
  const internalStatus = {
537
569
  workflowUUID: workflowUUID,
@@ -570,7 +602,16 @@ class DBOSExecutor {
570
602
  let result;
571
603
  // Execute the workflow.
572
604
  try {
573
- result = await wf.call(params.configuredInstance, wCtxt, ...args);
605
+ let cresult;
606
+ await (0, context_1.runWithWorkflowContext)(wCtxt, async () => {
607
+ if (passContext) {
608
+ cresult = await wf.call(params.configuredInstance, wCtxt, ...args);
609
+ }
610
+ else {
611
+ cresult = await wf.call(params.configuredInstance, ...args);
612
+ }
613
+ });
614
+ result = cresult;
574
615
  internalStatus.output = result;
575
616
  internalStatus.status = workflow_1.StatusString.SUCCESS;
576
617
  if (internalStatus.queueName) {
@@ -675,17 +716,19 @@ class DBOSExecutor {
675
716
  if (utils_1.DBOSJSON.stringify(args) !== utils_1.DBOSJSON.stringify(recordedInputs)) {
676
717
  throw new error_1.DBOSDebuggerError(`Detect different input for the workflow UUID ${workflowUUID}!\n Received: ${utils_1.DBOSJSON.stringify(args)}\n Original: ${utils_1.DBOSJSON.stringify(recordedInputs)}`);
677
718
  }
678
- const workflowPromise = wf.call(params.configuredInstance, wCtxt, ...args)
679
- .then(async (result) => {
680
- // Check if the result is the same.
681
- const recordedResult = await this.systemDatabase.getWorkflowResult(workflowUUID);
682
- if (result === undefined && !recordedResult) {
683
- return result;
684
- }
685
- if (utils_1.DBOSJSON.stringify(result) !== utils_1.DBOSJSON.stringify(recordedResult)) {
686
- this.logger.error(`Detect different output for the workflow UUID ${workflowUUID}!\n Received: ${utils_1.DBOSJSON.stringify(result)}\n Original: ${utils_1.DBOSJSON.stringify(recordedResult)}`);
687
- }
688
- return recordedResult; // Always return the recorded result.
719
+ const workflowPromise = (0, context_1.runWithWorkflowContext)(wCtxt, async () => {
720
+ return await wf.call(params.configuredInstance, wCtxt, ...args)
721
+ .then(async (result) => {
722
+ // Check if the result is the same.
723
+ const recordedResult = await this.systemDatabase.getWorkflowResult(workflowUUID);
724
+ if (result === undefined && !recordedResult) {
725
+ return result;
726
+ }
727
+ if (utils_1.DBOSJSON.stringify(result) !== utils_1.DBOSJSON.stringify(recordedResult)) {
728
+ this.logger.error(`Detect different output for the workflow UUID ${workflowUUID}!\n Received: ${utils_1.DBOSJSON.stringify(result)}\n Original: ${utils_1.DBOSJSON.stringify(recordedResult)}`);
729
+ }
730
+ return recordedResult; // Always return the recorded result.
731
+ });
689
732
  });
690
733
  return new workflow_1.InvokedHandle(this.systemDatabase, workflowPromise, workflowUUID, wf.name, callerUUID, callerFunctionID);
691
734
  }
@@ -702,6 +745,122 @@ class DBOSExecutor {
702
745
  tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(txn),
703
746
  }, ...args)).getResult();
704
747
  }
748
+ async callTransactionFunction(txn, clsinst, wfCtx, ...args) {
749
+ const txnInfo = this.getTransactionInfo(txn);
750
+ if (txnInfo === undefined) {
751
+ throw new error_1.DBOSNotRegisteredError(txn.name);
752
+ }
753
+ const readOnly = txnInfo.config.readOnly ?? false;
754
+ let retryWaitMillis = 1;
755
+ const backoffFactor = 1.5;
756
+ const maxRetryWaitMs = 2000; // Maximum wait 2 seconds.
757
+ const funcId = wfCtx.functionIDGetIncrement();
758
+ const span = this.tracer.startSpan(txn.name, {
759
+ operationUUID: wfCtx.workflowUUID,
760
+ operationType: exports.OperationType.TRANSACTION,
761
+ authenticatedUser: wfCtx.authenticatedUser,
762
+ assumedRole: wfCtx.assumedRole,
763
+ authenticatedRoles: wfCtx.authenticatedRoles,
764
+ readOnly: readOnly,
765
+ isolationLevel: txnInfo.config.isolationLevel,
766
+ }, wfCtx.span);
767
+ while (true) {
768
+ let txn_snapshot = "invalid";
769
+ const workflowUUID = wfCtx.workflowUUID;
770
+ const wrappedTransaction = async (client) => {
771
+ const tCtxt = new transaction_1.TransactionContextImpl(this.userDatabase.getName(), client, wfCtx, span, this.logger, funcId, txn.name);
772
+ // If the UUID is preset, it is possible this execution previously happened. Check, and return its original result if it did.
773
+ // 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.
774
+ if (wfCtx.presetUUID) {
775
+ const check = await wfCtx.checkTxExecution(client, funcId);
776
+ txn_snapshot = check.txn_snapshot;
777
+ if (check.output !== exports.dbosNull) {
778
+ tCtxt.span.setAttribute("cached", true);
779
+ tCtxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
780
+ this.tracer.endSpan(tCtxt.span);
781
+ return check.output;
782
+ }
783
+ }
784
+ else {
785
+ // Collect snapshot information for read-only transactions and non-preset UUID transactions, if not already collected above
786
+ txn_snapshot = await wfCtx.retrieveTxSnapshot(client);
787
+ }
788
+ // For non-read-only transactions, flush the result buffer.
789
+ if (!readOnly) {
790
+ await wfCtx.flushResultBuffer(client);
791
+ }
792
+ // Execute the user's transaction.
793
+ let cresult;
794
+ if (txnInfo.registration.passContext) {
795
+ await (0, context_1.runWithTransactionContext)(tCtxt, async () => {
796
+ cresult = await txn.call(clsinst, tCtxt, ...args);
797
+ });
798
+ }
799
+ else {
800
+ await (0, context_1.runWithTransactionContext)(tCtxt, async () => {
801
+ const tf = txn;
802
+ cresult = await tf.call(clsinst, ...args);
803
+ });
804
+ }
805
+ const result = cresult;
806
+ // Record the execution, commit, and return.
807
+ if (readOnly) {
808
+ // Buffer the output of read-only transactions instead of synchronously writing it.
809
+ const readOutput = {
810
+ output: result,
811
+ txn_snapshot: txn_snapshot,
812
+ created_at: Date.now(),
813
+ };
814
+ wfCtx.resultBuffer.set(funcId, readOutput);
815
+ }
816
+ else {
817
+ try {
818
+ // Synchronously record the output of write transactions and obtain the transaction ID.
819
+ const pg_txn_id = await wfCtx.recordOutputTx(client, funcId, txn_snapshot, result);
820
+ tCtxt.span.setAttribute("pg_txn_id", pg_txn_id);
821
+ wfCtx.resultBuffer.clear();
822
+ }
823
+ catch (error) {
824
+ if (this.userDatabase.isFailedSqlTransactionError(error)) {
825
+ this.logger.error(`Postgres aborted the ${txn.name} @Transaction of Workflow ${workflowUUID}, but the function did not raise an exception. Please ensure that the @Transaction method raises an exception if the database transaction is aborted.`);
826
+ throw new error_1.DBOSFailedSqlTransactionError(workflowUUID, txn.name);
827
+ }
828
+ else {
829
+ throw error;
830
+ }
831
+ }
832
+ }
833
+ return result;
834
+ };
835
+ try {
836
+ const result = await this.userDatabase.transaction(wrappedTransaction, txnInfo.config);
837
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
838
+ this.tracer.endSpan(span);
839
+ return result;
840
+ }
841
+ catch (err) {
842
+ if (this.userDatabase.isRetriableTransactionError(err)) {
843
+ // serialization_failure in PostgreSQL
844
+ span.addEvent("TXN SERIALIZATION FAILURE", { "retryWaitMillis": retryWaitMillis }, performance.now());
845
+ // Retry serialization failures.
846
+ await (0, utils_1.sleepms)(retryWaitMillis);
847
+ retryWaitMillis *= backoffFactor;
848
+ retryWaitMillis = retryWaitMillis < maxRetryWaitMs ? retryWaitMillis : maxRetryWaitMs;
849
+ continue;
850
+ }
851
+ // Record and throw other errors.
852
+ const e = err;
853
+ await this.userDatabase.transaction(async (client) => {
854
+ await wfCtx.flushResultBuffer(client);
855
+ await wfCtx.recordErrorTx(client, funcId, txn_snapshot, e);
856
+ }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
857
+ wfCtx.resultBuffer.clear();
858
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message });
859
+ this.tracer.endSpan(span);
860
+ throw err;
861
+ }
862
+ }
863
+ }
705
864
  async procedure(proc, params, ...args) {
706
865
  // Create a workflow and call procedure.
707
866
  const temp_workflow = async (ctxt, ...args) => {
@@ -748,13 +907,128 @@ class DBOSExecutor {
748
907
  tempWfClass: (0, decorators_1.getRegisteredMethodClassName)(stepFn),
749
908
  }, ...args)).getResult();
750
909
  }
910
+ /**
911
+ * Execute a step function.
912
+ * If it encounters any error, retry according to its configured retry policy until the maximum number of attempts is reached, then throw an DBOSError.
913
+ * The step may execute many times, but once it is complete, it will not re-execute.
914
+ */
915
+ async callStepFunction(stepFn, clsInst, wfCtx, ...args) {
916
+ const commInfo = this.getStepInfo(stepFn);
917
+ if (commInfo === undefined) {
918
+ throw new error_1.DBOSNotRegisteredError(stepFn.name);
919
+ }
920
+ const funcID = wfCtx.functionIDGetIncrement();
921
+ const maxRetryIntervalSec = 3600; // Maximum retry interval: 1 hour
922
+ const span = this.tracer.startSpan(stepFn.name, {
923
+ operationUUID: wfCtx.workflowUUID,
924
+ operationType: exports.OperationType.COMMUNICATOR,
925
+ authenticatedUser: wfCtx.authenticatedUser,
926
+ assumedRole: wfCtx.assumedRole,
927
+ authenticatedRoles: wfCtx.authenticatedRoles,
928
+ retriesAllowed: commInfo.config.retriesAllowed,
929
+ intervalSeconds: commInfo.config.intervalSeconds,
930
+ maxAttempts: commInfo.config.maxAttempts,
931
+ backoffRate: commInfo.config.backoffRate,
932
+ }, wfCtx.span);
933
+ const ctxt = new step_1.StepContextImpl(wfCtx, funcID, span, this.logger, commInfo.config, stepFn.name);
934
+ await this.userDatabase.transaction(async (client) => {
935
+ await wfCtx.flushResultBuffer(client);
936
+ }, { isolationLevel: transaction_1.IsolationLevel.ReadCommitted });
937
+ wfCtx.resultBuffer.clear();
938
+ // Check if this execution previously happened, returning its original result if it did.
939
+ const check = await this.systemDatabase.checkOperationOutput(wfCtx.workflowUUID, ctxt.functionID);
940
+ if (check !== exports.dbosNull) {
941
+ ctxt.span.setAttribute("cached", true);
942
+ ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
943
+ this.tracer.endSpan(ctxt.span);
944
+ return check;
945
+ }
946
+ // Execute the step function. If it throws an exception, retry with exponential backoff.
947
+ // After reaching the maximum number of retries, throw an DBOSError.
948
+ let result = exports.dbosNull;
949
+ let err = exports.dbosNull;
950
+ if (ctxt.retriesAllowed) {
951
+ let numAttempts = 0;
952
+ let intervalSeconds = ctxt.intervalSeconds;
953
+ if (intervalSeconds > maxRetryIntervalSec) {
954
+ this.logger.warn(`Step config interval exceeds maximum allowed interval, capped to ${maxRetryIntervalSec} seconds!`);
955
+ }
956
+ while (result === exports.dbosNull && numAttempts++ < ctxt.maxAttempts) {
957
+ try {
958
+ let cresult;
959
+ if (commInfo.registration.passContext) {
960
+ await (0, context_1.runWithStepContext)(ctxt, async () => {
961
+ cresult = await stepFn.call(clsInst, ctxt, ...args);
962
+ });
963
+ }
964
+ else {
965
+ await (0, context_1.runWithStepContext)(ctxt, async () => {
966
+ const sf = stepFn;
967
+ cresult = await sf.call(clsInst, ...args);
968
+ });
969
+ }
970
+ result = cresult;
971
+ }
972
+ catch (error) {
973
+ const e = error;
974
+ this.logger.warn(`Error in step being automatically retried. Attempt ${numAttempts} of ${ctxt.maxAttempts}. ${e.stack}`);
975
+ span.addEvent(`Step attempt ${numAttempts + 1} failed`, { "retryIntervalSeconds": intervalSeconds, "error": error.message }, performance.now());
976
+ if (numAttempts < ctxt.maxAttempts) {
977
+ // Sleep for an interval, then increase the interval by backoffRate.
978
+ // Cap at the maximum allowed retry interval.
979
+ await (0, utils_1.sleepms)(intervalSeconds * 1000);
980
+ intervalSeconds *= ctxt.backoffRate;
981
+ intervalSeconds = intervalSeconds < maxRetryIntervalSec ? intervalSeconds : maxRetryIntervalSec;
982
+ }
983
+ }
984
+ }
985
+ }
986
+ else {
987
+ try {
988
+ let cresult;
989
+ if (commInfo.registration.passContext) {
990
+ await (0, context_1.runWithStepContext)(ctxt, async () => {
991
+ cresult = await stepFn.call(clsInst, ctxt, ...args);
992
+ });
993
+ }
994
+ else {
995
+ await (0, context_1.runWithStepContext)(ctxt, async () => {
996
+ const sf = stepFn;
997
+ cresult = await sf.call(clsInst, ...args);
998
+ });
999
+ }
1000
+ result = cresult;
1001
+ }
1002
+ catch (error) {
1003
+ err = error;
1004
+ }
1005
+ }
1006
+ // `result` can only be dbosNull when the step timed out
1007
+ if (result === exports.dbosNull) {
1008
+ // Record the error, then throw it.
1009
+ err = err === exports.dbosNull ? new error_1.DBOSError("Step reached maximum retries.", 1) : err;
1010
+ await this.systemDatabase.recordOperationError(wfCtx.workflowUUID, ctxt.functionID, err);
1011
+ ctxt.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
1012
+ this.tracer.endSpan(ctxt.span);
1013
+ throw err;
1014
+ }
1015
+ else {
1016
+ // Record the execution and return.
1017
+ await this.systemDatabase.recordOperationOutput(wfCtx.workflowUUID, ctxt.functionID, result);
1018
+ ctxt.span.setStatus({ code: api_1.SpanStatusCode.OK });
1019
+ this.tracer.endSpan(ctxt.span);
1020
+ return result;
1021
+ }
1022
+ }
751
1023
  async send(destinationUUID, message, topic, idempotencyKey) {
752
1024
  // Create a workflow and call send.
753
1025
  const temp_workflow = async (ctxt, destinationUUID, message, topic) => {
754
1026
  return await ctxt.send(destinationUUID, message, topic);
755
1027
  };
756
1028
  const workflowUUID = idempotencyKey ? destinationUUID + idempotencyKey : undefined;
757
- return (await this.workflow(temp_workflow, { workflowUUID: workflowUUID, tempWfType: TempWorkflowType.send, configuredInstance: null }, destinationUUID, message, topic)).getResult();
1029
+ return (await this.workflow(temp_workflow, {
1030
+ workflowUUID: workflowUUID, tempWfType: TempWorkflowType.send, configuredInstance: null,
1031
+ }, destinationUUID, message, topic)).getResult();
758
1032
  }
759
1033
  /**
760
1034
  * Wait for a workflow to emit an event, then return its value.
@@ -765,8 +1039,17 @@ class DBOSExecutor {
765
1039
  /**
766
1040
  * Retrieve a handle for a workflow UUID.
767
1041
  */
768
- retrieveWorkflow(workflowUUID) {
769
- return new workflow_1.RetrievedHandle(this.systemDatabase, workflowUUID);
1042
+ retrieveWorkflow(workflowID) {
1043
+ return new workflow_1.RetrievedHandle(this.systemDatabase, workflowID);
1044
+ }
1045
+ getWorkflowStatus(workflowID) {
1046
+ return this.systemDatabase.getWorkflowStatus(workflowID);
1047
+ }
1048
+ getWorkflows(input) {
1049
+ return this.systemDatabase.getWorkflows(input);
1050
+ }
1051
+ getWorkflowQueue(input) {
1052
+ return this.systemDatabase.getWorkflowQueue(input);
770
1053
  }
771
1054
  async queryUserDB(sql, params) {
772
1055
  if (params !== undefined) {
@@ -833,6 +1116,7 @@ class DBOSExecutor {
833
1116
  }
834
1117
  await this.scheduler?.destroyScheduler();
835
1118
  wfqueue_1.wfQueueRunner.stop();
1119
+ await this.wfqEnded;
836
1120
  }
837
1121
  async executeWorkflowUUID(workflowUUID, startNewWorkflow = false) {
838
1122
  const wfStatus = await this.systemDatabase.getWorkflowStatus(workflowUUID);
@@ -848,7 +1132,7 @@ class DBOSExecutor {
848
1132
  if (wfInfo) {
849
1133
  return this.workflow(wfInfo.workflow, {
850
1134
  workflowUUID: workflowStartUUID, parentCtx: parentCtx, configuredInstance: configuredInst, recovery: true,
851
- queueName: wfStatus.queueName, executeWorkflow: true
1135
+ queueName: wfStatus.queueName, executeWorkflow: true,
852
1136
  },
853
1137
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
854
1138
  ...inputs);
@@ -906,8 +1190,12 @@ class DBOSExecutor {
906
1190
  this.logger.error(`Unrecognized temporary workflow! UUID ${workflowUUID}, name ${wfName}`);
907
1191
  throw new error_1.DBOSNotRegisteredError(wfName);
908
1192
  }
1193
+ return this.workflow(temp_workflow, {
1194
+ workflowUUID: workflowStartUUID, parentCtx: parentCtx ?? undefined, configuredInstance: clsinst,
1195
+ recovery: true, tempWfType, tempWfClass, tempWfName,
1196
+ },
909
1197
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
910
- return this.workflow(temp_workflow, { workflowUUID: workflowStartUUID, parentCtx: parentCtx ?? undefined, configuredInstance: clsinst, recovery: true, tempWfType, tempWfClass, tempWfName }, ...inputs);
1198
+ ...inputs);
911
1199
  }
912
1200
  async getEventDispatchState(svc, wfn, key) {
913
1201
  return await this.systemDatabase.getEventDispatchState(svc, wfn, key);