@supergrowthai/tq 1.0.13 → 1.1.1

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 (38) hide show
  1. package/README.md +149 -8
  2. package/dist/{AsyncActions-CZYO8ShR.js → AsyncActions-B8ImDgTo.js} +39 -3
  3. package/dist/AsyncActions-B8ImDgTo.js.map +1 -0
  4. package/dist/{AsyncActions-BOO1ikWz.cjs → AsyncActions-BsxMX_Ib.cjs} +39 -3
  5. package/dist/AsyncActions-BsxMX_Ib.cjs.map +1 -0
  6. package/dist/core/Actions.cjs +23 -1
  7. package/dist/core/Actions.cjs.map +1 -1
  8. package/dist/core/Actions.mjs +23 -1
  9. package/dist/core/Actions.mjs.map +1 -1
  10. package/dist/core/async/AsyncActions.cjs +1 -1
  11. package/dist/core/async/AsyncActions.mjs +1 -1
  12. package/dist/index.cjs +459 -226
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.mjs +459 -226
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/src/core/Actions.d.cts +5 -1
  17. package/dist/src/core/Actions.d.ts +5 -1
  18. package/dist/src/core/TaskHandler.d.cts +6 -0
  19. package/dist/src/core/TaskHandler.d.ts +6 -0
  20. package/dist/src/core/TaskRunner.d.cts +22 -5
  21. package/dist/src/core/TaskRunner.d.ts +22 -5
  22. package/dist/src/core/async/AsyncActions.d.cts +1 -0
  23. package/dist/src/core/async/AsyncActions.d.ts +1 -0
  24. package/dist/src/core/flow/FlowMiddleware.d.cts +6 -1
  25. package/dist/src/core/flow/FlowMiddleware.d.ts +6 -1
  26. package/dist/src/core/flow/IFlowBarrierProvider.d.cts +4 -0
  27. package/dist/src/core/flow/IFlowBarrierProvider.d.ts +4 -0
  28. package/dist/src/core/flow/InMemoryFlowBarrierProvider.d.cts +1 -0
  29. package/dist/src/core/flow/InMemoryFlowBarrierProvider.d.ts +1 -0
  30. package/dist/src/core/lifecycle.d.cts +98 -3
  31. package/dist/src/core/lifecycle.d.ts +98 -3
  32. package/dist/src/providers/ConsoleHealthProvider.d.cts +42 -2
  33. package/dist/src/providers/ConsoleHealthProvider.d.ts +42 -2
  34. package/dist/src/test/lifecycle-events.test.d.cts +31 -0
  35. package/dist/src/test/lifecycle-events.test.d.ts +31 -0
  36. package/package.json +2 -2
  37. package/dist/AsyncActions-BOO1ikWz.cjs.map +0 -1
  38. package/dist/AsyncActions-CZYO8ShR.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -50,7 +50,7 @@ const mq = require("@supergrowthai/mq");
50
50
  const client = require("./client-BxG7LzLv.cjs");
51
51
  const utils_taskIdGen = require("./utils/task-id-gen.cjs");
52
52
  const core_Actions = require("./core/Actions.cjs");
53
- const core_async_AsyncActions = require("./AsyncActions-BOO1ikWz.cjs");
53
+ const core_async_AsyncActions = require("./AsyncActions-BsxMX_Ib.cjs");
54
54
  const node_async_hooks = require("node:async_hooks");
55
55
  const moment = require("moment");
56
56
  const os = require("os");
@@ -682,7 +682,22 @@ function getLogContext() {
682
682
  return als.getStore();
683
683
  }
684
684
  class TaskRunner {
685
- constructor(messageQueue, taskQueue, taskStore, cacheProvider, generateId, lifecycleProvider, lifecycleConfig, entityProjection, entityProjectionConfig, flowMiddleware) {
685
+ constructor(opts) {
686
+ this.taskStartTimes = /* @__PURE__ */ new Map();
687
+ const {
688
+ messageQueue,
689
+ taskQueue,
690
+ taskStore,
691
+ cacheProvider,
692
+ generateId,
693
+ lifecycleProvider,
694
+ lifecycleConfig,
695
+ entityProjection,
696
+ entityProjectionConfig,
697
+ flowMiddleware,
698
+ flowLifecycleProvider,
699
+ workerId = ""
700
+ } = opts;
686
701
  this.messageQueue = messageQueue;
687
702
  this.taskQueue = taskQueue;
688
703
  this.taskStore = taskStore;
@@ -692,7 +707,8 @@ class TaskRunner {
692
707
  this.entityProjection = entityProjection;
693
708
  this.entityProjectionConfig = entityProjectionConfig;
694
709
  this.flowMiddleware = flowMiddleware;
695
- this.taskStartTimes = /* @__PURE__ */ new Map();
710
+ this.flowLifecycleProvider = flowLifecycleProvider;
711
+ this.workerId = workerId;
696
712
  this.logger = new client.Logger("TaskRunner", client.LogLevel.INFO);
697
713
  this.lockManager = new LockManager(cacheProvider, {
698
714
  prefix: "task_lock_",
@@ -759,7 +775,7 @@ class TaskRunner {
759
775
  this.logger.error(`[TQ] Entity projection failed (non-fatal): ${err}`);
760
776
  }
761
777
  }
762
- const actions = new core_Actions.Actions(taskRunnerId);
778
+ const actions = new core_Actions.Actions(taskRunnerId, this.flowLifecycleProvider, this.workerId);
763
779
  const asyncTasks = [];
764
780
  const processedTaskIds = /* @__PURE__ */ new Set();
765
781
  for (let i = 0; i < groupedTasksArray.length; i++) {
@@ -795,7 +811,20 @@ class TaskRunner {
795
811
  taskGroup.tasks.forEach((task) => processedTaskIds.add(utils_taskIdGen.tId(task)));
796
812
  this.logger.info(`[${taskRunnerId}] Processing ${taskGroup.tasks.length} tasks of type: ${taskGroup.type}`);
797
813
  if (executor.multiple) {
798
- const batchStore = this.buildBatchLogStore(taskGroup.tasks, taskRunnerId);
814
+ const batchStore = this.buildBatchLogStore(taskGroup.tasks, this.workerId);
815
+ const batchTaskContexts = taskGroup.tasks.map((t) => this.buildTaskContext(t, this.workerId, taskRunnerId));
816
+ const batchStartedAt = Date.now();
817
+ this.emitLifecycleEvent(
818
+ this.lifecycleProvider?.onTaskBatchStarted,
819
+ {
820
+ task_type: taskGroup.type,
821
+ queue_id: firstTask.queue_id,
822
+ tasks: batchTaskContexts,
823
+ worker_id: this.workerId,
824
+ consumer_id: taskRunnerId,
825
+ started_at: new Date(batchStartedAt)
826
+ }
827
+ );
799
828
  await runWithLogContext(
800
829
  batchStore,
801
830
  () => executor.onTasks(taskGroup.tasks, actions).catch((err) => {
@@ -807,19 +836,40 @@ class TaskRunner {
807
836
  }
808
837
  })
809
838
  );
839
+ const succeeded = [];
840
+ const failed = [];
841
+ for (const task of taskGroup.tasks) {
842
+ const status = actions.getTaskResultStatus(utils_taskIdGen.tId(task));
843
+ if (status === "success") succeeded.push(utils_taskIdGen.tId(task));
844
+ else if (status === "fail") failed.push(utils_taskIdGen.tId(task));
845
+ else failed.push(utils_taskIdGen.tId(task));
846
+ }
847
+ this.emitLifecycleEvent(
848
+ this.lifecycleProvider?.onTaskBatchCompleted,
849
+ {
850
+ task_type: taskGroup.type,
851
+ queue_id: firstTask.queue_id,
852
+ tasks: batchTaskContexts,
853
+ worker_id: this.workerId,
854
+ consumer_id: taskRunnerId,
855
+ succeeded,
856
+ failed,
857
+ duration_ms: Date.now() - batchStartedAt
858
+ }
859
+ );
810
860
  } else {
811
861
  if (executor.parallel) {
812
862
  const chunks = chunk(taskGroup.tasks, executor.chunkSize);
813
863
  this.logger.info(`[${taskRunnerId}] Processing in parallel chunks of ${executor.chunkSize}`);
814
864
  for (const taskChunk of chunks) {
815
865
  for (const task of taskChunk) {
816
- this.emitTaskStarted(task, taskRunnerId);
866
+ this.emitTaskStarted(task, this.workerId, taskRunnerId);
817
867
  }
818
868
  const chunkPromises = [];
819
869
  for (let j = 0; j < taskChunk.length; j++) {
820
870
  const task = taskChunk[j];
821
871
  const taskActions = actions.forkForTask(task);
822
- const logStore = this.buildLogStore(task, taskRunnerId);
872
+ const logStore = this.buildLogStore(task, this.workerId);
823
873
  chunkPromises.push(runWithLogContext(
824
874
  logStore,
825
875
  () => executor.onTask(task, taskActions).catch((err) => {
@@ -834,12 +884,12 @@ class TaskRunner {
834
884
  for (const task of taskChunk) {
835
885
  const resultStatus = actions.getTaskResultStatus(utils_taskIdGen.tId(task));
836
886
  if (resultStatus === "success") {
837
- this.emitTaskCompleted(task, taskRunnerId, actions.getTaskResult(utils_taskIdGen.tId(task)));
887
+ this.emitTaskCompleted(task, this.workerId, actions.getTaskResult(utils_taskIdGen.tId(task)), taskRunnerId);
838
888
  } else if (resultStatus === "fail") {
839
889
  const retryCount = task.execution_stats?.retry_count || 0;
840
890
  const maxRetries = task.retries ?? executor.default_retries ?? 0;
841
891
  const willRetry = retryCount < maxRetries;
842
- this.emitTaskFailed(task, taskRunnerId, new Error("Task failed"), willRetry);
892
+ this.emitTaskFailed(task, this.workerId, new Error("Task failed"), willRetry, void 0, taskRunnerId);
843
893
  }
844
894
  }
845
895
  }
@@ -848,9 +898,9 @@ class TaskRunner {
848
898
  for (let j = 0; j < taskGroup.tasks.length; j++) {
849
899
  const task = taskGroup.tasks[j];
850
900
  if (!timeoutMs) {
851
- this.emitTaskStarted(task, taskRunnerId);
901
+ this.emitTaskStarted(task, this.workerId, taskRunnerId);
852
902
  const taskActions = actions.forkForTask(task);
853
- const logStore = this.buildLogStore(task, taskRunnerId);
903
+ const logStore = this.buildLogStore(task, this.workerId);
854
904
  await runWithLogContext(
855
905
  logStore,
856
906
  () => executor.onTask(task, taskActions).catch((err) => {
@@ -862,18 +912,18 @@ class TaskRunner {
862
912
  );
863
913
  const resultStatus = actions.getTaskResultStatus(utils_taskIdGen.tId(task));
864
914
  if (resultStatus === "success") {
865
- this.emitTaskCompleted(task, taskRunnerId, actions.getTaskResult(utils_taskIdGen.tId(task)));
915
+ this.emitTaskCompleted(task, this.workerId, actions.getTaskResult(utils_taskIdGen.tId(task)), taskRunnerId);
866
916
  } else if (resultStatus === "fail") {
867
917
  const retryCount = task.execution_stats?.retry_count || 0;
868
918
  const maxRetries = task.retries ?? executor.default_retries ?? 0;
869
919
  const willRetry = retryCount < maxRetries;
870
- this.emitTaskFailed(task, taskRunnerId, new Error("Task failed"), willRetry);
920
+ this.emitTaskFailed(task, this.workerId, new Error("Task failed"), willRetry, void 0, taskRunnerId);
871
921
  }
872
922
  } else {
873
- this.emitTaskStarted(task, taskRunnerId);
923
+ this.emitTaskStarted(task, this.workerId, taskRunnerId);
874
924
  const startTime = Date.now();
875
925
  const taskActions = actions.forkForTask(task);
876
- const logStore = this.buildLogStore(task, taskRunnerId);
926
+ const logStore = this.buildLogStore(task, this.workerId);
877
927
  const taskPromise = runWithLogContext(
878
928
  logStore,
879
929
  () => executor.onTask(task, taskActions).catch((err) => {
@@ -896,12 +946,12 @@ class TaskRunner {
896
946
  if (result !== "~~~timeout") {
897
947
  const resultStatus = actions.getTaskResultStatus(utils_taskIdGen.tId(task));
898
948
  if (resultStatus === "success") {
899
- this.emitTaskCompleted(task, taskRunnerId, actions.getTaskResult(utils_taskIdGen.tId(task)));
949
+ this.emitTaskCompleted(task, this.workerId, actions.getTaskResult(utils_taskIdGen.tId(task)), taskRunnerId);
900
950
  } else if (resultStatus === "fail") {
901
951
  const retryCount = task.execution_stats?.retry_count || 0;
902
952
  const maxRetries = task.retries ?? executor.default_retries ?? 0;
903
953
  const willRetry = retryCount < maxRetries;
904
- this.emitTaskFailed(task, taskRunnerId, new Error("Task failed"), willRetry);
954
+ this.emitTaskFailed(task, this.workerId, new Error("Task failed"), willRetry, void 0, taskRunnerId);
905
955
  }
906
956
  }
907
957
  if (result === "~~~timeout") {
@@ -914,10 +964,18 @@ class TaskRunner {
914
964
  } else {
915
965
  const asyncLifecycleEmitter = this.lifecycleProvider ? {
916
966
  onCompleted: (t, result2) => {
917
- this.emitTaskCompleted(t, taskRunnerId, result2);
967
+ this.emitTaskCompleted(t, this.workerId, result2, taskRunnerId);
918
968
  },
919
969
  onFailed: (t, error, willRetry) => {
920
- this.emitTaskFailed(t, taskRunnerId, error, willRetry);
970
+ this.emitTaskFailed(t, this.workerId, error, willRetry, void 0, taskRunnerId);
971
+ },
972
+ onScheduled: (t) => {
973
+ if (this.lifecycleProvider?.onTaskScheduled) {
974
+ this.emitLifecycleEvent(
975
+ this.lifecycleProvider.onTaskScheduled,
976
+ this.buildTaskContext(t, this.workerId, taskRunnerId)
977
+ );
978
+ }
921
979
  }
922
980
  } : void 0;
923
981
  const asyncActions = new core_async_AsyncActions.AsyncActions(this.messageQueue, this.taskStore, this.taskQueue, actions, task, this.generateId, asyncLifecycleEmitter, this.entityProjection, this.entityProjectionConfig, this.flowMiddleware);
@@ -965,7 +1023,7 @@ class TaskRunner {
965
1023
  this.logger.error(`[TQ] Lifecycle callback error: ${err}`);
966
1024
  }
967
1025
  }
968
- buildTaskContext(task, workerId) {
1026
+ buildTaskContext(task, workerId, consumerId) {
969
1027
  const retryCount = task.execution_stats && typeof task.execution_stats.retry_count === "number" ? task.execution_stats.retry_count : 0;
970
1028
  const executor = this.taskQueue.getExecutor(task.queue_id, task.type);
971
1029
  const maxRetries = task.retries ?? executor?.default_retries ?? 0;
@@ -980,14 +1038,15 @@ class TaskRunner {
980
1038
  max_retries: maxRetries,
981
1039
  scheduled_at: task.created_at || /* @__PURE__ */ new Date(),
982
1040
  worker_id: workerId,
1041
+ consumer_id: consumerId,
983
1042
  log_context: task.metadata?.log_context
984
1043
  };
985
1044
  }
986
- emitTaskStarted(task, workerId) {
1045
+ emitTaskStarted(task, workerId, consumerId) {
987
1046
  const startedAt = Date.now();
988
1047
  this.taskStartTimes.set(utils_taskIdGen.tId(task), startedAt);
989
1048
  if (this.lifecycleProvider?.onTaskStarted) {
990
- const ctx = this.buildTaskContext(task, workerId);
1049
+ const ctx = this.buildTaskContext(task, workerId, consumerId);
991
1050
  const queuedDuration = startedAt - (task.created_at?.getTime() || startedAt);
992
1051
  this.emitLifecycleEvent(
993
1052
  this.lifecycleProvider.onTaskStarted,
@@ -999,12 +1058,12 @@ class TaskRunner {
999
1058
  );
1000
1059
  }
1001
1060
  }
1002
- emitTaskCompleted(task, workerId, result) {
1061
+ emitTaskCompleted(task, workerId, result, consumerId) {
1003
1062
  const completedAt = Date.now();
1004
1063
  const startedAt = this.taskStartTimes.get(utils_taskIdGen.tId(task)) || completedAt;
1005
1064
  this.taskStartTimes.delete(utils_taskIdGen.tId(task));
1006
1065
  if (this.lifecycleProvider?.onTaskCompleted) {
1007
- const ctx = this.buildTaskContext(task, workerId);
1066
+ const ctx = this.buildTaskContext(task, workerId, consumerId);
1008
1067
  const timing = {
1009
1068
  queued_duration_ms: startedAt - (task.created_at?.getTime() || startedAt),
1010
1069
  processing_duration_ms: completedAt - startedAt,
@@ -1016,12 +1075,12 @@ class TaskRunner {
1016
1075
  );
1017
1076
  }
1018
1077
  }
1019
- emitTaskFailed(task, workerId, error, willRetry, nextAttemptAt) {
1078
+ emitTaskFailed(task, workerId, error, willRetry, nextAttemptAt, consumerId) {
1020
1079
  const completedAt = Date.now();
1021
1080
  const startedAt = this.taskStartTimes.get(utils_taskIdGen.tId(task)) || completedAt;
1022
1081
  this.taskStartTimes.delete(utils_taskIdGen.tId(task));
1023
1082
  if (this.lifecycleProvider?.onTaskFailed) {
1024
- const ctx = this.buildTaskContext(task, workerId);
1083
+ const ctx = this.buildTaskContext(task, workerId, consumerId);
1025
1084
  const timing = {
1026
1085
  queued_duration_ms: startedAt - (task.created_at?.getTime() || startedAt),
1027
1086
  processing_duration_ms: completedAt - startedAt,
@@ -1039,6 +1098,247 @@ function getEnabledQueues() {
1039
1098
  if (enabledQueues.length === 0) throw new Error("No queues enabled");
1040
1099
  return enabledQueues.map(mq.getEnvironmentQueueName);
1041
1100
  }
1101
+ function getFlowMeta(task) {
1102
+ return task.metadata?.flow_meta;
1103
+ }
1104
+ function getFlowMetaRequired(task) {
1105
+ return task.metadata.flow_meta;
1106
+ }
1107
+ class FlowMiddleware {
1108
+ constructor(barrierProvider, generateId, flowLifecycleProvider, workerId = "") {
1109
+ this.barrierProvider = barrierProvider;
1110
+ this.generateId = generateId;
1111
+ this.flowLifecycleProvider = flowLifecycleProvider;
1112
+ this.workerId = workerId;
1113
+ }
1114
+ emitFlowEvent(callback, ctx) {
1115
+ if (!callback) return;
1116
+ try {
1117
+ const result = callback(ctx);
1118
+ if (result instanceof Promise) {
1119
+ result.catch(() => {
1120
+ });
1121
+ }
1122
+ } catch {
1123
+ }
1124
+ }
1125
+ buildFlowContext(flowMeta) {
1126
+ return {
1127
+ flow_id: flowMeta.flow_id,
1128
+ total_steps: flowMeta.total_steps,
1129
+ join: flowMeta.join,
1130
+ failure_policy: flowMeta.failure_policy,
1131
+ entity: flowMeta.entity,
1132
+ worker_id: this.workerId
1133
+ };
1134
+ }
1135
+ /**
1136
+ * Process completed tasks for flow orchestration.
1137
+ * Called from TaskHandler.postProcessTasks after markFailed/markSuccess.
1138
+ *
1139
+ * @param input Categorized terminal tasks — success and final-failed (no retries left)
1140
+ * @returns Join tasks to dispatch and entity projections to sync
1141
+ */
1142
+ async onPostProcess(input) {
1143
+ const joinTasks = [];
1144
+ const projections = [];
1145
+ const successTaskSet = new Set(input.successTasks);
1146
+ const allTasks = [...input.successTasks, ...input.failedTasks];
1147
+ const joinCompletions = [];
1148
+ const timeoutTasks = [];
1149
+ const stepTasks = [];
1150
+ for (const task of allTasks) {
1151
+ const flowMeta = getFlowMeta(task);
1152
+ if (!flowMeta) continue;
1153
+ const isSuccess = successTaskSet.has(task);
1154
+ if (flowMeta.is_join) {
1155
+ joinCompletions.push(task);
1156
+ } else if (flowMeta.is_timeout) {
1157
+ timeoutTasks.push(task);
1158
+ } else {
1159
+ stepTasks.push({ task, isSuccess });
1160
+ }
1161
+ }
1162
+ for (const task of joinCompletions) {
1163
+ const flowMeta = getFlowMetaRequired(task);
1164
+ const isSuccess = successTaskSet.has(task);
1165
+ if (flowMeta.entity) {
1166
+ try {
1167
+ const status = isSuccess ? "executed" : "failed";
1168
+ const error = !isSuccess ? task.execution_stats?.last_error || "Join task failed" : void 0;
1169
+ const p = core_async_AsyncActions.buildProjection(
1170
+ {
1171
+ ...task,
1172
+ id: flowMeta.flow_id,
1173
+ entity: flowMeta.entity
1174
+ },
1175
+ status,
1176
+ { result: task.execution_result, error }
1177
+ );
1178
+ if (p) projections.push(p);
1179
+ } catch {
1180
+ }
1181
+ }
1182
+ }
1183
+ for (const task of timeoutTasks) {
1184
+ const flowMeta = getFlowMetaRequired(task);
1185
+ const flowId = flowMeta.flow_id;
1186
+ await this.barrierProvider.initBarrier(flowId, flowMeta.total_steps);
1187
+ const isComplete = await this.barrierProvider.isComplete(flowId);
1188
+ if (isComplete) continue;
1189
+ const isFirstAbort = await this.barrierProvider.markAborted(flowId);
1190
+ if (!isFirstAbort) continue;
1191
+ const partialResults = await this.barrierProvider.getStepResults(flowId);
1192
+ const flowResults = {
1193
+ flow_id: flowId,
1194
+ steps: partialResults,
1195
+ timed_out: true
1196
+ };
1197
+ const joinTask = this.buildJoinTask(flowMeta, flowResults);
1198
+ joinTasks.push(joinTask);
1199
+ if (this.flowLifecycleProvider?.onFlowTimedOut) {
1200
+ const startedAt = await this.barrierProvider.getStartedAt(flowId);
1201
+ const durationMs = startedAt ? Date.now() - startedAt.getTime() : 0;
1202
+ this.emitFlowEvent(this.flowLifecycleProvider.onFlowTimedOut, {
1203
+ ...this.buildFlowContext(flowMeta),
1204
+ duration_ms: durationMs,
1205
+ steps_completed: partialResults.length
1206
+ });
1207
+ }
1208
+ if (flowMeta.entity) {
1209
+ try {
1210
+ const p = core_async_AsyncActions.buildProjection(
1211
+ {
1212
+ ...joinTask,
1213
+ id: flowId,
1214
+ entity: flowMeta.entity
1215
+ },
1216
+ "failed",
1217
+ { error: "flow_timeout" }
1218
+ );
1219
+ if (p) projections.push(p);
1220
+ } catch {
1221
+ }
1222
+ }
1223
+ }
1224
+ const stepsByFlow = /* @__PURE__ */ new Map();
1225
+ for (const entry of stepTasks) {
1226
+ const flowMeta = getFlowMetaRequired(entry.task);
1227
+ const group = stepsByFlow.get(flowMeta.flow_id) || [];
1228
+ group.push(entry);
1229
+ stepsByFlow.set(flowMeta.flow_id, group);
1230
+ }
1231
+ for (const [flowId, entries] of stepsByFlow) {
1232
+ const firstFlowMeta = getFlowMetaRequired(entries[0].task);
1233
+ await this.barrierProvider.initBarrier(flowId, firstFlowMeta.total_steps);
1234
+ if (firstFlowMeta.failure_policy === "abort") {
1235
+ const hasFailure = entries.some((e) => !e.isSuccess);
1236
+ if (hasFailure) {
1237
+ const isFirstAbort = await this.barrierProvider.markAborted(flowId);
1238
+ if (isFirstAbort) {
1239
+ const stepResults2 = this.buildStepResults(entries);
1240
+ await this.barrierProvider.batchDecrementAndCheck(flowId, stepResults2);
1241
+ const allResults = await this.barrierProvider.getStepResults(flowId);
1242
+ const flowResults = {
1243
+ flow_id: flowId,
1244
+ steps: allResults,
1245
+ aborted: true
1246
+ };
1247
+ const joinTask = this.buildJoinTask(firstFlowMeta, flowResults);
1248
+ joinTasks.push(joinTask);
1249
+ if (this.flowLifecycleProvider?.onFlowAborted) {
1250
+ const startedAt = await this.barrierProvider.getStartedAt(flowId);
1251
+ const durationMs = startedAt ? Date.now() - startedAt.getTime() : 0;
1252
+ const failedEntry = entries.find((e) => !e.isSuccess);
1253
+ const triggerIndex = failedEntry ? failedEntry.task.metadata?.flow_meta?.step_index ?? -1 : -1;
1254
+ this.emitFlowEvent(this.flowLifecycleProvider.onFlowAborted, {
1255
+ ...this.buildFlowContext(firstFlowMeta),
1256
+ duration_ms: durationMs,
1257
+ steps_completed: allResults.length,
1258
+ trigger_step_index: triggerIndex
1259
+ });
1260
+ }
1261
+ if (firstFlowMeta.entity) {
1262
+ try {
1263
+ const p = core_async_AsyncActions.buildProjection(
1264
+ {
1265
+ ...joinTask,
1266
+ id: flowId,
1267
+ entity: firstFlowMeta.entity
1268
+ },
1269
+ "failed",
1270
+ { error: "flow_aborted" }
1271
+ );
1272
+ if (p) projections.push(p);
1273
+ } catch {
1274
+ }
1275
+ }
1276
+ }
1277
+ continue;
1278
+ }
1279
+ }
1280
+ const stepResults = this.buildStepResults(entries);
1281
+ const { remaining } = await this.barrierProvider.batchDecrementAndCheck(flowId, stepResults);
1282
+ if (remaining === 0) {
1283
+ const allResults = await this.barrierProvider.getStepResults(flowId);
1284
+ const flowResults = {
1285
+ flow_id: flowId,
1286
+ steps: allResults
1287
+ };
1288
+ const joinTask = this.buildJoinTask(firstFlowMeta, flowResults);
1289
+ joinTasks.push(joinTask);
1290
+ if (this.flowLifecycleProvider?.onFlowCompleted) {
1291
+ const startedAt = await this.barrierProvider.getStartedAt(flowId);
1292
+ const durationMs = startedAt ? Date.now() - startedAt.getTime() : 0;
1293
+ const stepsSucceeded = allResults.filter((r) => r.status === "success").length;
1294
+ const stepsFailed = allResults.filter((r) => r.status === "fail").length;
1295
+ this.emitFlowEvent(this.flowLifecycleProvider.onFlowCompleted, {
1296
+ ...this.buildFlowContext(firstFlowMeta),
1297
+ duration_ms: durationMs,
1298
+ steps_succeeded: stepsSucceeded,
1299
+ steps_failed: stepsFailed
1300
+ });
1301
+ }
1302
+ }
1303
+ }
1304
+ return { joinTasks, projections };
1305
+ }
1306
+ buildStepResults(entries) {
1307
+ return entries.map(({ task, isSuccess }) => {
1308
+ const flowMeta = getFlowMetaRequired(task);
1309
+ return {
1310
+ step_index: flowMeta.step_index,
1311
+ status: isSuccess ? "success" : "fail",
1312
+ result: task.execution_result,
1313
+ error: !isSuccess ? task.execution_stats?.last_error || "Step failed" : void 0
1314
+ };
1315
+ });
1316
+ }
1317
+ buildJoinTask(flowMeta, flowResults) {
1318
+ const now = /* @__PURE__ */ new Date();
1319
+ const joinFlowMeta = {
1320
+ ...flowMeta,
1321
+ is_join: true,
1322
+ is_timeout: void 0,
1323
+ step_index: -1
1324
+ };
1325
+ return {
1326
+ id: this.generateId(),
1327
+ type: flowMeta.join.type,
1328
+ queue_id: flowMeta.join.queue_id,
1329
+ payload: { flow_results: flowResults },
1330
+ execute_at: now,
1331
+ status: "scheduled",
1332
+ created_at: now,
1333
+ updated_at: now,
1334
+ force_store: true,
1335
+ metadata: {
1336
+ flow_meta: joinFlowMeta
1337
+ },
1338
+ ...flowMeta.entity ? { entity: flowMeta.entity } : {}
1339
+ };
1340
+ }
1341
+ }
1042
1342
  const METRICS_KEY_PREFIX = "task_metrics:";
1043
1343
  const DISCARDED_TASKS_KEY = `${METRICS_KEY_PREFIX}discarded_tasks`;
1044
1344
  const STATS_THRESHOLD = parseInt(process.env.TQ_STATS_THRESHOLD || "1000");
@@ -1065,23 +1365,35 @@ class TaskHandler {
1065
1365
  };
1066
1366
  this.totalProcessingMs = 0;
1067
1367
  this.queueStats = /* @__PURE__ */ new Map();
1368
+ this.consumerStatsMap = /* @__PURE__ */ new Map();
1068
1369
  this.logger = new client.Logger("TaskHandler", client.LogLevel.INFO);
1069
1370
  this.config = config || {};
1070
1371
  this.workerId = `${os__namespace.hostname()}-${process.pid}-${Date.now()}`;
1071
1372
  this.workerStartedAt = /* @__PURE__ */ new Date();
1072
1373
  this.taskStore = new TaskStore(databaseAdapter);
1073
- this.taskRunner = new TaskRunner(
1074
- messageQueue,
1075
- taskQueuesManager,
1076
- this.taskStore,
1077
- this.cacheAdapter,
1374
+ if (this.config.flowLifecycleProvider && !this.config.flowBarrierProvider) {
1375
+ throw new Error("[TQ] flowLifecycleProvider requires flowBarrierProvider — flow lifecycle events need flow orchestration enabled");
1376
+ }
1377
+ this.flowMiddleware = this.config.flowBarrierProvider ? new FlowMiddleware(
1378
+ this.config.flowBarrierProvider,
1078
1379
  databaseAdapter.generateId.bind(databaseAdapter),
1079
- this.config.lifecycleProvider,
1080
- this.config.lifecycle,
1081
- this.config.entityProjection,
1082
- this.config.entityProjectionConfig,
1083
- this.config.flowMiddleware
1084
- );
1380
+ this.config.flowLifecycleProvider,
1381
+ this.workerId
1382
+ ) : void 0;
1383
+ this.taskRunner = new TaskRunner({
1384
+ messageQueue,
1385
+ taskQueue: taskQueuesManager,
1386
+ taskStore: this.taskStore,
1387
+ cacheProvider: this.cacheAdapter,
1388
+ generateId: databaseAdapter.generateId.bind(databaseAdapter),
1389
+ lifecycleProvider: this.config.lifecycleProvider,
1390
+ lifecycleConfig: this.config.lifecycle,
1391
+ entityProjection: this.config.entityProjection,
1392
+ entityProjectionConfig: this.config.entityProjectionConfig,
1393
+ flowMiddleware: this.flowMiddleware,
1394
+ flowLifecycleProvider: this.config.flowLifecycleProvider,
1395
+ workerId: this.workerId
1396
+ });
1085
1397
  }
1086
1398
  // ============ Lifecycle Event Helpers ============
1087
1399
  get lifecycleProvider() {
@@ -1386,9 +1698,9 @@ class TaskHandler {
1386
1698
  this.logger.error(`[TQ] Entity projection failed (non-fatal): ${err}`);
1387
1699
  }
1388
1700
  }
1389
- if (this.config.flowMiddleware) {
1701
+ if (this.flowMiddleware) {
1390
1702
  try {
1391
- const flowResult = await this.config.flowMiddleware.onPostProcess({ successTasks, failedTasks: finalFailedTasks });
1703
+ const flowResult = await this.flowMiddleware.onPostProcess({ successTasks, failedTasks: finalFailedTasks });
1392
1704
  if (flowResult.projections.length > 0 && this.entityProjectionProvider) {
1393
1705
  await core_async_AsyncActions.syncProjections(flowResult.projections, this.entityProjectionProvider, this.logger);
1394
1706
  }
@@ -1411,6 +1723,7 @@ class TaskHandler {
1411
1723
  }
1412
1724
  const batchStartTime = Date.now();
1413
1725
  const taskTypes = [...new Set(tasks.map((t) => t.type))];
1726
+ this.registerConsumerIfNew(id, streamName);
1414
1727
  if (this.workerProvider?.onBatchStarted) {
1415
1728
  this.emitLifecycleEvent(
1416
1729
  this.workerProvider.onBatchStarted,
@@ -1487,6 +1800,7 @@ class TaskHandler {
1487
1800
  await this.reportQueueStats(streamName);
1488
1801
  const batchDuration = Date.now() - batchStartTime;
1489
1802
  this.updateWorkerStats(successTasks.length, failedTasks.length, batchDuration);
1803
+ this.updateConsumerStats(id, successTasks.length, failedTasks.length);
1490
1804
  if (this.workerProvider?.onBatchCompleted) {
1491
1805
  this.emitLifecycleEvent(
1492
1806
  this.workerProvider.onBatchCompleted,
@@ -1519,6 +1833,7 @@ class TaskHandler {
1519
1833
  this.processMatureTasks(abortSignal);
1520
1834
  abortSignal?.addEventListener("abort", () => {
1521
1835
  this.stopHeartbeat();
1836
+ this.emitAllConsumersStopped("shutdown");
1522
1837
  this.emitWorkerStopped("shutdown");
1523
1838
  });
1524
1839
  }
@@ -1554,7 +1869,8 @@ class TaskHandler {
1554
1869
  {
1555
1870
  ...this.buildWorkerInfo(),
1556
1871
  stats: { ...this.workerStats },
1557
- memory_usage_mb: memUsage.heapUsed / 1024 / 1024
1872
+ memory_usage_mb: memUsage.heapUsed / 1024 / 1024,
1873
+ active_consumers: this.getActiveConsumerStats()
1558
1874
  }
1559
1875
  );
1560
1876
  }
@@ -1580,6 +1896,58 @@ class TaskHandler {
1580
1896
  this.workerStats.avg_processing_ms = this.totalProcessingMs / this.workerStats.tasks_processed;
1581
1897
  }
1582
1898
  }
1899
+ // ============ Consumer Tracking ============
1900
+ registerConsumerIfNew(consumerId, queueId) {
1901
+ if (this.consumerStatsMap.has(consumerId)) return;
1902
+ const now = /* @__PURE__ */ new Date();
1903
+ this.consumerStatsMap.set(consumerId, {
1904
+ consumer_id: consumerId,
1905
+ queue_id: queueId,
1906
+ tasks_processed: 0,
1907
+ tasks_succeeded: 0,
1908
+ tasks_failed: 0,
1909
+ started_at: now
1910
+ });
1911
+ if (this.workerProvider?.onConsumerStarted) {
1912
+ this.emitLifecycleEvent(
1913
+ this.workerProvider.onConsumerStarted,
1914
+ {
1915
+ consumer_id: consumerId,
1916
+ queue_id: queueId,
1917
+ worker_id: this.workerId,
1918
+ started_at: now
1919
+ }
1920
+ );
1921
+ }
1922
+ }
1923
+ updateConsumerStats(consumerId, succeeded, failed) {
1924
+ const stats = this.consumerStatsMap.get(consumerId);
1925
+ if (!stats) return;
1926
+ stats.tasks_processed += succeeded + failed;
1927
+ stats.tasks_succeeded += succeeded;
1928
+ stats.tasks_failed += failed;
1929
+ stats.last_task_at = /* @__PURE__ */ new Date();
1930
+ }
1931
+ getActiveConsumerStats() {
1932
+ return Array.from(this.consumerStatsMap.values()).map(({ started_at, ...stats }) => stats);
1933
+ }
1934
+ emitAllConsumersStopped(reason) {
1935
+ if (!this.workerProvider?.onConsumerStopped) return;
1936
+ for (const entry of this.consumerStatsMap.values()) {
1937
+ const { started_at, ...stats } = entry;
1938
+ this.emitLifecycleEvent(
1939
+ this.workerProvider.onConsumerStopped,
1940
+ {
1941
+ consumer_id: entry.consumer_id,
1942
+ queue_id: entry.queue_id,
1943
+ worker_id: this.workerId,
1944
+ started_at,
1945
+ reason,
1946
+ stats
1947
+ }
1948
+ );
1949
+ }
1950
+ }
1583
1951
  emitLifecycleEvent(callback, ctx) {
1584
1952
  if (!callback) return;
1585
1953
  try {
@@ -1675,6 +2043,7 @@ class TaskHandler {
1675
2043
  attempt: retryCount + 1,
1676
2044
  max_retries: maxRetries,
1677
2045
  scheduled_at: task.created_at || /* @__PURE__ */ new Date(),
2046
+ worker_id: this.workerId,
1678
2047
  log_context: task.metadata?.log_context
1679
2048
  };
1680
2049
  }
@@ -1854,7 +2223,8 @@ class InMemoryFlowBarrierProvider {
1854
2223
  this.barriers.set(flowId, {
1855
2224
  remaining: totalSteps,
1856
2225
  status: "active",
1857
- results: /* @__PURE__ */ new Map()
2226
+ results: /* @__PURE__ */ new Map(),
2227
+ started_at: /* @__PURE__ */ new Date()
1858
2228
  });
1859
2229
  }
1860
2230
  async batchDecrementAndCheck(flowId, results) {
@@ -1898,190 +2268,10 @@ class InMemoryFlowBarrierProvider {
1898
2268
  if (!state) return false;
1899
2269
  return state.status === "complete";
1900
2270
  }
1901
- }
1902
- function getFlowMeta(task) {
1903
- return task.metadata?.flow_meta;
1904
- }
1905
- function getFlowMetaRequired(task) {
1906
- return task.metadata.flow_meta;
1907
- }
1908
- class FlowMiddleware {
1909
- constructor(barrierProvider, generateId) {
1910
- this.barrierProvider = barrierProvider;
1911
- this.generateId = generateId;
1912
- }
1913
- /**
1914
- * Process completed tasks for flow orchestration.
1915
- * Called from TaskHandler.postProcessTasks after markFailed/markSuccess.
1916
- *
1917
- * @param input Categorized terminal tasks — success and final-failed (no retries left)
1918
- * @returns Join tasks to dispatch and entity projections to sync
1919
- */
1920
- async onPostProcess(input) {
1921
- const joinTasks = [];
1922
- const projections = [];
1923
- const successTaskSet = new Set(input.successTasks);
1924
- const allTasks = [...input.successTasks, ...input.failedTasks];
1925
- const joinCompletions = [];
1926
- const timeoutTasks = [];
1927
- const stepTasks = [];
1928
- for (const task of allTasks) {
1929
- const flowMeta = getFlowMeta(task);
1930
- if (!flowMeta) continue;
1931
- const isSuccess = successTaskSet.has(task);
1932
- if (flowMeta.is_join) {
1933
- joinCompletions.push(task);
1934
- } else if (flowMeta.is_timeout) {
1935
- timeoutTasks.push(task);
1936
- } else {
1937
- stepTasks.push({ task, isSuccess });
1938
- }
1939
- }
1940
- for (const task of joinCompletions) {
1941
- const flowMeta = getFlowMetaRequired(task);
1942
- const isSuccess = successTaskSet.has(task);
1943
- if (flowMeta.entity) {
1944
- try {
1945
- const status = isSuccess ? "executed" : "failed";
1946
- const error = !isSuccess ? task.execution_stats?.last_error || "Join task failed" : void 0;
1947
- const p = core_async_AsyncActions.buildProjection(
1948
- {
1949
- ...task,
1950
- id: flowMeta.flow_id,
1951
- entity: flowMeta.entity
1952
- },
1953
- status,
1954
- { result: task.execution_result, error }
1955
- );
1956
- if (p) projections.push(p);
1957
- } catch {
1958
- }
1959
- }
1960
- }
1961
- for (const task of timeoutTasks) {
1962
- const flowMeta = getFlowMetaRequired(task);
1963
- const flowId = flowMeta.flow_id;
1964
- await this.barrierProvider.initBarrier(flowId, flowMeta.total_steps);
1965
- const isComplete = await this.barrierProvider.isComplete(flowId);
1966
- if (isComplete) continue;
1967
- const isFirstAbort = await this.barrierProvider.markAborted(flowId);
1968
- if (!isFirstAbort) continue;
1969
- const partialResults = await this.barrierProvider.getStepResults(flowId);
1970
- const flowResults = {
1971
- flow_id: flowId,
1972
- steps: partialResults,
1973
- timed_out: true
1974
- };
1975
- const joinTask = this.buildJoinTask(flowMeta, flowResults);
1976
- joinTasks.push(joinTask);
1977
- if (flowMeta.entity) {
1978
- try {
1979
- const p = core_async_AsyncActions.buildProjection(
1980
- {
1981
- ...joinTask,
1982
- id: flowId,
1983
- entity: flowMeta.entity
1984
- },
1985
- "failed",
1986
- { error: "flow_timeout" }
1987
- );
1988
- if (p) projections.push(p);
1989
- } catch {
1990
- }
1991
- }
1992
- }
1993
- const stepsByFlow = /* @__PURE__ */ new Map();
1994
- for (const entry of stepTasks) {
1995
- const flowMeta = getFlowMetaRequired(entry.task);
1996
- const group = stepsByFlow.get(flowMeta.flow_id) || [];
1997
- group.push(entry);
1998
- stepsByFlow.set(flowMeta.flow_id, group);
1999
- }
2000
- for (const [flowId, entries] of stepsByFlow) {
2001
- const firstFlowMeta = getFlowMetaRequired(entries[0].task);
2002
- await this.barrierProvider.initBarrier(flowId, firstFlowMeta.total_steps);
2003
- if (firstFlowMeta.failure_policy === "abort") {
2004
- const hasFailure = entries.some((e) => !e.isSuccess);
2005
- if (hasFailure) {
2006
- const isFirstAbort = await this.barrierProvider.markAborted(flowId);
2007
- if (isFirstAbort) {
2008
- const stepResults2 = this.buildStepResults(entries);
2009
- await this.barrierProvider.batchDecrementAndCheck(flowId, stepResults2);
2010
- const allResults = await this.barrierProvider.getStepResults(flowId);
2011
- const flowResults = {
2012
- flow_id: flowId,
2013
- steps: allResults,
2014
- aborted: true
2015
- };
2016
- const joinTask = this.buildJoinTask(firstFlowMeta, flowResults);
2017
- joinTasks.push(joinTask);
2018
- if (firstFlowMeta.entity) {
2019
- try {
2020
- const p = core_async_AsyncActions.buildProjection(
2021
- {
2022
- ...joinTask,
2023
- id: flowId,
2024
- entity: firstFlowMeta.entity
2025
- },
2026
- "failed",
2027
- { error: "flow_aborted" }
2028
- );
2029
- if (p) projections.push(p);
2030
- } catch {
2031
- }
2032
- }
2033
- }
2034
- continue;
2035
- }
2036
- }
2037
- const stepResults = this.buildStepResults(entries);
2038
- const { remaining } = await this.barrierProvider.batchDecrementAndCheck(flowId, stepResults);
2039
- if (remaining === 0) {
2040
- const allResults = await this.barrierProvider.getStepResults(flowId);
2041
- const flowResults = {
2042
- flow_id: flowId,
2043
- steps: allResults
2044
- };
2045
- const joinTask = this.buildJoinTask(firstFlowMeta, flowResults);
2046
- joinTasks.push(joinTask);
2047
- }
2048
- }
2049
- return { joinTasks, projections };
2050
- }
2051
- buildStepResults(entries) {
2052
- return entries.map(({ task, isSuccess }) => {
2053
- const flowMeta = getFlowMetaRequired(task);
2054
- return {
2055
- step_index: flowMeta.step_index,
2056
- status: isSuccess ? "success" : "fail",
2057
- result: task.execution_result,
2058
- error: !isSuccess ? task.execution_stats?.last_error || "Step failed" : void 0
2059
- };
2060
- });
2061
- }
2062
- buildJoinTask(flowMeta, flowResults) {
2063
- const now = /* @__PURE__ */ new Date();
2064
- const joinFlowMeta = {
2065
- ...flowMeta,
2066
- is_join: true,
2067
- is_timeout: void 0,
2068
- step_index: -1
2069
- };
2070
- return {
2071
- id: this.generateId(),
2072
- type: flowMeta.join.type,
2073
- queue_id: flowMeta.join.queue_id,
2074
- payload: { flow_results: flowResults },
2075
- execute_at: now,
2076
- status: "scheduled",
2077
- created_at: now,
2078
- updated_at: now,
2079
- force_store: true,
2080
- metadata: {
2081
- flow_meta: joinFlowMeta
2082
- },
2083
- ...flowMeta.entity ? { entity: flowMeta.entity } : {}
2084
- };
2271
+ async getStartedAt(flowId) {
2272
+ const state = this.barriers.get(flowId);
2273
+ if (!state) return null;
2274
+ return state.started_at;
2085
2275
  }
2086
2276
  }
2087
2277
  class ConsoleHealthProvider {
@@ -2121,7 +2311,7 @@ class ConsoleHealthProvider {
2121
2311
  }
2122
2312
  onWorkerHeartbeat(info) {
2123
2313
  console.log(
2124
- `${this.prefix} Worker heartbeat: ${info.worker_id} - processed: ${info.stats.tasks_processed}, success: ${info.stats.tasks_succeeded}, failed: ${info.stats.tasks_failed}, avg: ${info.stats.avg_processing_ms.toFixed(2)}ms, memory: ${info.memory_usage_mb.toFixed(2)}MB`
2314
+ `${this.prefix} Worker heartbeat: ${info.worker_id} - processed: ${info.stats.tasks_processed}, success: ${info.stats.tasks_succeeded}, failed: ${info.stats.tasks_failed}, avg: ${info.stats.avg_processing_ms.toFixed(2)}ms, memory: ${info.memory_usage_mb.toFixed(2)}MB, consumers: ${info.active_consumers.length}`
2125
2315
  );
2126
2316
  }
2127
2317
  onWorkerStopped(info) {
@@ -2139,6 +2329,49 @@ class ConsoleHealthProvider {
2139
2329
  `${this.prefix} Batch completed: ${info.batch_size} tasks in ${info.duration_ms}ms - succeeded: ${info.succeeded}, failed: ${info.failed}`
2140
2330
  );
2141
2331
  }
2332
+ // ============ Consumer Lifecycle ============
2333
+ onConsumerStarted(info) {
2334
+ console.log(
2335
+ `${this.prefix} Consumer started: ${info.consumer_id} on worker ${info.worker_id} - queue: ${info.queue_id}`
2336
+ );
2337
+ }
2338
+ onConsumerStopped(info) {
2339
+ console.log(
2340
+ `${this.prefix} Consumer stopped: ${info.consumer_id} on worker ${info.worker_id} - reason: ${info.reason}, processed: ${info.stats.tasks_processed}, succeeded: ${info.stats.tasks_succeeded}, failed: ${info.stats.tasks_failed}`
2341
+ );
2342
+ }
2343
+ // ============ Task Batch Lifecycle ============
2344
+ onTaskBatchStarted(ctx) {
2345
+ console.log(
2346
+ `${this.prefix} Task batch started: ${ctx.task_type} - ${ctx.tasks.length} tasks in queue ${ctx.queue_id}`
2347
+ );
2348
+ }
2349
+ onTaskBatchCompleted(ctx) {
2350
+ console.log(
2351
+ `${this.prefix} Task batch completed: ${ctx.task_type} in ${ctx.duration_ms}ms - succeeded: ${ctx.succeeded.length}, failed: ${ctx.failed.length}`
2352
+ );
2353
+ }
2354
+ // ============ Flow Lifecycle ============
2355
+ onFlowStarted(ctx) {
2356
+ console.log(
2357
+ `${this.prefix} Flow started: ${ctx.flow_id} - ${ctx.total_steps} steps [${ctx.step_types.join(", ")}], join: ${ctx.join.type}, policy: ${ctx.failure_policy}`
2358
+ );
2359
+ }
2360
+ onFlowCompleted(ctx) {
2361
+ console.log(
2362
+ `${this.prefix} Flow completed: ${ctx.flow_id} in ${ctx.duration_ms}ms - succeeded: ${ctx.steps_succeeded}, failed: ${ctx.steps_failed}`
2363
+ );
2364
+ }
2365
+ onFlowAborted(ctx) {
2366
+ console.log(
2367
+ `${this.prefix} Flow aborted: ${ctx.flow_id} in ${ctx.duration_ms}ms - steps completed: ${ctx.steps_completed}, trigger: step ${ctx.trigger_step_index}`
2368
+ );
2369
+ }
2370
+ onFlowTimedOut(ctx) {
2371
+ console.log(
2372
+ `${this.prefix} Flow timed out: ${ctx.flow_id} in ${ctx.duration_ms}ms - steps completed: ${ctx.steps_completed}`
2373
+ );
2374
+ }
2142
2375
  }
2143
2376
  exports.InMemoryAdapter = PrismaAdapter.InMemoryAdapter;
2144
2377
  exports.MongoDbAdapter = PrismaAdapter.MongoDbAdapter;