pg-workflows 0.1.3 → 0.3.0

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.
package/dist/index.cjs CHANGED
@@ -5,38 +5,59 @@ var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
8
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
- var __moduleCache = /* @__PURE__ */ new WeakMap;
20
33
  var __toCommonJS = (from) => {
21
- var entry = __moduleCache.get(from), desc;
34
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
22
35
  if (entry)
23
36
  return entry;
24
37
  entry = __defProp({}, "__esModule", { value: true });
25
- if (from && typeof from === "object" || typeof from === "function")
26
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
- get: () => from[key],
28
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
- }));
38
+ if (from && typeof from === "object" || typeof from === "function") {
39
+ for (var key of __getOwnPropNames(from))
40
+ if (!__hasOwnProp.call(entry, key))
41
+ __defProp(entry, key, {
42
+ get: __accessProp.bind(from, key),
43
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
44
+ });
45
+ }
30
46
  __moduleCache.set(from, entry);
31
47
  return entry;
32
48
  };
49
+ var __moduleCache;
50
+ var __returnValue = (v) => v;
51
+ function __exportSetter(name, newValue) {
52
+ this[name] = __returnValue.bind(null, newValue);
53
+ }
33
54
  var __export = (target, all) => {
34
55
  for (var name in all)
35
56
  __defProp(target, name, {
36
57
  get: all[name],
37
58
  enumerable: true,
38
59
  configurable: true,
39
- set: (newValue) => all[name] = () => newValue
60
+ set: __exportSetter.bind(all, name)
40
61
  });
41
62
  };
42
63
 
@@ -44,6 +65,7 @@ var __export = (target, all) => {
44
65
  var exports_src = {};
45
66
  __export(exports_src, {
46
67
  workflow: () => workflow,
68
+ parseDuration: () => parseDuration,
47
69
  WorkflowStatus: () => WorkflowStatus,
48
70
  WorkflowRunNotFoundError: () => WorkflowRunNotFoundError,
49
71
  WorkflowEngineError: () => WorkflowEngineError,
@@ -53,20 +75,100 @@ __export(exports_src, {
53
75
  module.exports = __toCommonJS(exports_src);
54
76
 
55
77
  // src/definition.ts
56
- function workflow(id, handler, { inputSchema, timeout, retries } = {}) {
57
- return {
78
+ function createWorkflowFactory(plugins = []) {
79
+ const factory = (id, handler, { inputSchema, timeout, retries } = {}) => ({
58
80
  id,
59
81
  handler,
60
82
  inputSchema,
61
83
  timeout,
62
- retries
63
- };
84
+ retries,
85
+ plugins: plugins.length > 0 ? plugins : undefined
86
+ });
87
+ factory.use = (plugin) => createWorkflowFactory([
88
+ ...plugins,
89
+ plugin
90
+ ]);
91
+ return factory;
92
+ }
93
+ var workflow = createWorkflowFactory();
94
+ // src/duration.ts
95
+ var import_parse_duration = __toESM(require("parse-duration"));
96
+
97
+ // src/error.ts
98
+ class WorkflowEngineError extends Error {
99
+ workflowId;
100
+ runId;
101
+ cause;
102
+ constructor(message, workflowId, runId, cause = undefined) {
103
+ super(message);
104
+ this.workflowId = workflowId;
105
+ this.runId = runId;
106
+ this.cause = cause;
107
+ this.name = "WorkflowEngineError";
108
+ if (Error.captureStackTrace) {
109
+ Error.captureStackTrace(this, WorkflowEngineError);
110
+ }
111
+ }
112
+ }
113
+
114
+ class WorkflowRunNotFoundError extends WorkflowEngineError {
115
+ constructor(runId, workflowId) {
116
+ super("Workflow run not found", workflowId, runId);
117
+ this.name = "WorkflowRunNotFoundError";
118
+ }
119
+ }
120
+
121
+ // src/duration.ts
122
+ var MS_PER_SECOND = 1000;
123
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
124
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
125
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
126
+ var MS_PER_WEEK = 7 * MS_PER_DAY;
127
+ function parseDuration(duration) {
128
+ if (typeof duration === "string") {
129
+ if (duration.trim() === "") {
130
+ throw new WorkflowEngineError("Invalid duration: empty string");
131
+ }
132
+ const ms2 = import_parse_duration.default(duration);
133
+ if (ms2 == null || ms2 <= 0) {
134
+ throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
135
+ }
136
+ return ms2;
137
+ }
138
+ const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
139
+ const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
140
+ if (ms <= 0) {
141
+ throw new WorkflowEngineError("Invalid duration: must be a positive value");
142
+ }
143
+ return ms;
64
144
  }
65
145
  // src/engine.ts
66
146
  var import_es_toolkit = require("es-toolkit");
67
147
 
68
148
  // src/ast-parser.ts
69
149
  var ts = __toESM(require("typescript"));
150
+
151
+ // src/types.ts
152
+ var WorkflowStatus;
153
+ ((WorkflowStatus2) => {
154
+ WorkflowStatus2["PENDING"] = "pending";
155
+ WorkflowStatus2["RUNNING"] = "running";
156
+ WorkflowStatus2["PAUSED"] = "paused";
157
+ WorkflowStatus2["COMPLETED"] = "completed";
158
+ WorkflowStatus2["FAILED"] = "failed";
159
+ WorkflowStatus2["CANCELLED"] = "cancelled";
160
+ })(WorkflowStatus ||= {});
161
+ var StepType;
162
+ ((StepType2) => {
163
+ StepType2["PAUSE"] = "pause";
164
+ StepType2["RUN"] = "run";
165
+ StepType2["WAIT_FOR"] = "waitFor";
166
+ StepType2["WAIT_UNTIL"] = "waitUntil";
167
+ StepType2["DELAY"] = "delay";
168
+ StepType2["POLL"] = "poll";
169
+ })(StepType ||= {});
170
+
171
+ // src/ast-parser.ts
70
172
  function parseWorkflowHandler(handler) {
71
173
  const handlerSource = handler.toString();
72
174
  const sourceFile = ts.createSourceFile("handler.ts", handlerSource, ts.ScriptTarget.Latest, true);
@@ -110,13 +212,14 @@ function parseWorkflowHandler(handler) {
110
212
  const propertyAccess = node.expression;
111
213
  const objectName = propertyAccess.expression.getText(sourceFile);
112
214
  const methodName = propertyAccess.name.text;
113
- if (objectName === "step" && (methodName === "run" || methodName === "waitFor" || methodName === "pause")) {
215
+ if (objectName === "step" && (methodName === "run" || methodName === "waitFor" || methodName === "pause" || methodName === "waitUntil" || methodName === "delay" || methodName === "sleep" || methodName === "poll")) {
114
216
  const firstArg = node.arguments[0];
115
217
  if (firstArg) {
116
218
  const { id, isDynamic } = extractStepId(firstArg);
219
+ const stepType = methodName === "sleep" ? "delay" /* DELAY */ : methodName;
117
220
  const stepDefinition = {
118
221
  id,
119
- type: methodName,
222
+ type: stepType,
120
223
  conditional: isInConditional(node),
121
224
  loop: isInLoop(node),
122
225
  isDynamic
@@ -417,48 +520,6 @@ async function withPostgresTransaction(db, callback) {
417
520
  }
418
521
  }
419
522
 
420
- // src/error.ts
421
- class WorkflowEngineError extends Error {
422
- workflowId;
423
- runId;
424
- cause;
425
- constructor(message, workflowId, runId, cause = undefined) {
426
- super(message);
427
- this.workflowId = workflowId;
428
- this.runId = runId;
429
- this.cause = cause;
430
- this.name = "WorkflowEngineError";
431
- if (Error.captureStackTrace) {
432
- Error.captureStackTrace(this, WorkflowEngineError);
433
- }
434
- }
435
- }
436
-
437
- class WorkflowRunNotFoundError extends WorkflowEngineError {
438
- constructor(runId, workflowId) {
439
- super("Workflow run not found", workflowId, runId);
440
- this.name = "WorkflowRunNotFoundError";
441
- }
442
- }
443
-
444
- // src/types.ts
445
- var WorkflowStatus;
446
- ((WorkflowStatus2) => {
447
- WorkflowStatus2["PENDING"] = "pending";
448
- WorkflowStatus2["RUNNING"] = "running";
449
- WorkflowStatus2["PAUSED"] = "paused";
450
- WorkflowStatus2["COMPLETED"] = "completed";
451
- WorkflowStatus2["FAILED"] = "failed";
452
- WorkflowStatus2["CANCELLED"] = "cancelled";
453
- })(WorkflowStatus ||= {});
454
- var StepType;
455
- ((StepType2) => {
456
- StepType2["PAUSE"] = "pause";
457
- StepType2["RUN"] = "run";
458
- StepType2["WAIT_FOR"] = "waitFor";
459
- StepType2["WAIT_UNTIL"] = "waitUntil";
460
- })(StepType ||= {});
461
-
462
523
  // src/engine.ts
463
524
  var PAUSE_EVENT_NAME = "__internal_pause";
464
525
  var WORKFLOW_RUN_QUEUE_NAME = "workflow-run";
@@ -467,7 +528,9 @@ var StepTypeToIcon = {
467
528
  ["run" /* RUN */]: "λ",
468
529
  ["waitFor" /* WAIT_FOR */]: "○",
469
530
  ["pause" /* PAUSE */]: "⏸",
470
- ["waitUntil" /* WAIT_UNTIL */]: "⏲"
531
+ ["waitUntil" /* WAIT_UNTIL */]: "⏲",
532
+ ["delay" /* DELAY */]: "⏱",
533
+ ["poll" /* POLL */]: "↻"
471
534
  };
472
535
  var defaultLogger = {
473
536
  log: (_message) => console.warn(_message),
@@ -567,7 +630,9 @@ class WorkflowEngine {
567
630
  if (!workflow2) {
568
631
  throw new WorkflowEngineError(`Unknown workflow ${workflowId}`);
569
632
  }
570
- if (workflow2.steps.length === 0 || !workflow2.steps[0]) {
633
+ const hasSteps = workflow2.steps.length > 0 && workflow2.steps[0];
634
+ const hasPlugins = (workflow2.plugins?.length ?? 0) > 0;
635
+ if (!hasSteps && !hasPlugins) {
571
636
  throw new WorkflowEngineError(`Workflow ${workflowId} has no steps`, workflowId);
572
637
  }
573
638
  if (workflow2.inputSchema) {
@@ -576,7 +641,7 @@ class WorkflowEngine {
576
641
  throw new WorkflowEngineError(result.error.message, workflowId);
577
642
  }
578
643
  }
579
- const initialStepId = workflow2.steps[0]?.id;
644
+ const initialStepId = workflow2.steps[0]?.id ?? "__start__";
580
645
  const run = await withPostgresTransaction(this.boss.getDb(), async (_db) => {
581
646
  const timeoutAt = options?.timeout ? new Date(Date.now() + options.timeout) : workflow2.timeout ? new Date(Date.now() + workflow2.timeout) : null;
582
647
  const insertedRun = await insertWorkflowRun({
@@ -763,11 +828,13 @@ class WorkflowEngine {
763
828
  if (run.status === "paused" /* PAUSED */) {
764
829
  const waitForStepEntry = run.timeline[`${run.currentStepId}-wait-for`];
765
830
  const waitForStep = waitForStepEntry && typeof waitForStepEntry === "object" && "waitFor" in waitForStepEntry ? waitForStepEntry : null;
766
- const currentStepEntry = run.timeline[run.currentStepId];
767
- const currentStep = currentStepEntry && typeof currentStepEntry === "object" && "output" in currentStepEntry ? currentStepEntry : null;
831
+ const currentStep = this.getCachedStepEntry(run.timeline, run.currentStepId);
768
832
  const waitFor = waitForStep?.waitFor;
769
833
  const hasCurrentStepOutput = currentStep?.output !== undefined;
770
- if (waitFor && waitFor.eventName === event?.name && !hasCurrentStepOutput) {
834
+ const eventMatches = waitFor && event?.name && (event.name === waitFor.eventName || event.name === waitFor.timeoutEvent) && !hasCurrentStepOutput;
835
+ if (eventMatches) {
836
+ const isTimeout = event?.name === waitFor?.timeoutEvent;
837
+ const skipOutput = waitFor?.skipOutput;
771
838
  run = await this.updateRun({
772
839
  runId,
773
840
  resourceId,
@@ -775,13 +842,16 @@ class WorkflowEngine {
775
842
  status: "running" /* RUNNING */,
776
843
  pausedAt: null,
777
844
  resumedAt: new Date,
778
- timeline: import_es_toolkit.merge(run.timeline, {
779
- [run.currentStepId]: {
780
- output: event?.data ?? {},
781
- timestamp: new Date
782
- }
783
- }),
784
- jobId: job?.id
845
+ jobId: job?.id,
846
+ ...skipOutput ? {} : {
847
+ timeline: import_es_toolkit.merge(run.timeline, {
848
+ [run.currentStepId]: {
849
+ output: event?.data ?? {},
850
+ ...isTimeout ? { timedOut: true } : {},
851
+ timestamp: new Date
852
+ }
853
+ })
854
+ }
785
855
  }
786
856
  });
787
857
  } else {
@@ -797,51 +867,79 @@ class WorkflowEngine {
797
867
  });
798
868
  }
799
869
  }
870
+ const baseStep = {
871
+ run: async (stepId, handler) => {
872
+ if (!run) {
873
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
874
+ }
875
+ return this.runStep({ stepId, run, handler });
876
+ },
877
+ waitFor: async (stepId, { eventName, timeout }) => {
878
+ if (!run) {
879
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
880
+ }
881
+ const timeoutDate = timeout ? new Date(Date.now() + timeout) : undefined;
882
+ return this.waitStep({ run, stepId, eventName, timeoutDate });
883
+ },
884
+ waitUntil: async (stepId, dateOrOptions) => {
885
+ if (!run) {
886
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
887
+ }
888
+ const date = dateOrOptions instanceof Date ? dateOrOptions : typeof dateOrOptions === "string" ? new Date(dateOrOptions) : dateOrOptions.date instanceof Date ? dateOrOptions.date : new Date(dateOrOptions.date);
889
+ await this.waitStep({ run, stepId, timeoutDate: date });
890
+ },
891
+ pause: async (stepId) => {
892
+ if (!run) {
893
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
894
+ }
895
+ await this.waitStep({ run, stepId, eventName: PAUSE_EVENT_NAME });
896
+ },
897
+ delay: async (stepId, duration) => {
898
+ if (!run) {
899
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
900
+ }
901
+ await this.waitStep({
902
+ run,
903
+ stepId,
904
+ timeoutDate: new Date(Date.now() + parseDuration(duration))
905
+ });
906
+ },
907
+ get sleep() {
908
+ return this.delay;
909
+ },
910
+ poll: async (stepId, conditionFn, options) => {
911
+ if (!run) {
912
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
913
+ }
914
+ const intervalMs = parseDuration(options?.interval ?? "30s");
915
+ if (intervalMs < 30000) {
916
+ throw new WorkflowEngineError(`step.poll interval must be at least 30s (got ${intervalMs}ms)`, workflowId, runId);
917
+ }
918
+ const timeoutMs = options?.timeout ? parseDuration(options.timeout) : undefined;
919
+ return this.pollStep({ run, stepId, conditionFn, intervalMs, timeoutMs });
920
+ }
921
+ };
922
+ let step = { ...baseStep };
923
+ const plugins = workflow2.plugins ?? [];
924
+ for (const plugin of plugins) {
925
+ const extra = plugin.methods(step);
926
+ step = { ...step, ...extra };
927
+ }
800
928
  const context = {
801
929
  input: run.input,
802
930
  workflowId: run.workflowId,
803
931
  runId: run.id,
804
932
  timeline: run.timeline,
805
933
  logger: this.logger,
806
- step: {
807
- run: async (stepId, handler) => {
808
- if (!run) {
809
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
810
- }
811
- return this.runStep({
812
- stepId,
813
- run,
814
- handler
815
- });
816
- },
817
- waitFor: async (stepId, { eventName, timeout }) => {
818
- if (!run) {
819
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
820
- }
821
- return this.waitForEvent({
822
- run,
823
- stepId,
824
- eventName,
825
- timeout
826
- });
827
- },
828
- waitUntil: async ({ date }) => {
829
- return this.waitUntil(runId, date);
830
- },
831
- pause: async (stepId) => {
832
- if (!run) {
833
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
834
- }
835
- return this.pauseStep({
836
- stepId,
837
- run
838
- });
839
- }
840
- }
934
+ step
841
935
  };
842
936
  const result = await workflow2.handler(context);
843
937
  run = await this.getRun({ runId, resourceId });
844
- if (run.status === "running" /* RUNNING */ && run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id) {
938
+ const isLastParsedStep = run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id;
939
+ const hasPluginSteps = (workflow2.plugins?.length ?? 0) > 0;
940
+ const noParsedSteps = workflow2.steps.length === 0;
941
+ const shouldComplete = run.status === "running" /* RUNNING */ && (noParsedSteps || isLastParsedStep || hasPluginSteps && result !== undefined);
942
+ if (shouldComplete) {
845
943
  const normalizedResult = result === undefined ? {} : result;
846
944
  await this.updateRun({
847
945
  runId,
@@ -893,6 +991,10 @@ class WorkflowEngine {
893
991
  throw error;
894
992
  }
895
993
  }
994
+ getCachedStepEntry(timeline, stepId) {
995
+ const stepEntry = timeline[stepId];
996
+ return stepEntry && typeof stepEntry === "object" && "output" in stepEntry ? stepEntry : null;
997
+ }
896
998
  async runStep({
897
999
  stepId,
898
1000
  run,
@@ -911,39 +1013,38 @@ class WorkflowEngine {
911
1013
  return;
912
1014
  }
913
1015
  try {
914
- let result;
915
- const timelineStepEntry = persistedRun.timeline[stepId];
916
- const timelineStep = timelineStepEntry && typeof timelineStepEntry === "object" && "output" in timelineStepEntry ? timelineStepEntry : null;
917
- if (timelineStep?.output !== undefined) {
918
- result = timelineStep.output;
919
- } else {
920
- await this.updateRun({
921
- runId: run.id,
922
- resourceId: run.resourceId ?? undefined,
923
- data: {
924
- currentStepId: stepId
925
- }
926
- }, { db });
927
- this.logger.log(`Running step ${stepId}...`, {
928
- runId: run.id,
929
- workflowId: run.workflowId
930
- });
931
- result = await handler();
932
- run = await this.updateRun({
933
- runId: run.id,
934
- resourceId: run.resourceId ?? undefined,
935
- data: {
936
- timeline: import_es_toolkit.merge(run.timeline, {
937
- [stepId]: {
938
- output: result === undefined ? {} : result,
939
- timestamp: new Date
940
- }
941
- })
942
- }
943
- }, { db });
1016
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
1017
+ if (cached?.output !== undefined) {
1018
+ return cached.output;
1019
+ }
1020
+ await this.updateRun({
1021
+ runId: run.id,
1022
+ resourceId: run.resourceId ?? undefined,
1023
+ data: {
1024
+ currentStepId: stepId
1025
+ }
1026
+ }, { db });
1027
+ this.logger.log(`Running step ${stepId}...`, {
1028
+ runId: run.id,
1029
+ workflowId: run.workflowId
1030
+ });
1031
+ let output = await handler();
1032
+ if (output === undefined) {
1033
+ output = {};
944
1034
  }
945
- const finalResult = result === undefined ? {} : result;
946
- return finalResult;
1035
+ run = await this.updateRun({
1036
+ runId: run.id,
1037
+ resourceId: run.resourceId ?? undefined,
1038
+ data: {
1039
+ timeline: import_es_toolkit.merge(run.timeline, {
1040
+ [stepId]: {
1041
+ output,
1042
+ timestamp: new Date
1043
+ }
1044
+ })
1045
+ }
1046
+ }, { db });
1047
+ return output;
947
1048
  } catch (error) {
948
1049
  this.logger.error(`Step ${stepId} failed:`, error, {
949
1050
  runId: run.id,
@@ -962,60 +1063,133 @@ ${error.stack}` : String(error)
962
1063
  }
963
1064
  });
964
1065
  }
965
- async waitForEvent({
1066
+ async waitStep({
966
1067
  run,
967
1068
  stepId,
968
1069
  eventName,
969
- timeout
1070
+ timeoutDate
970
1071
  }) {
971
1072
  const persistedRun = await this.getRun({
972
1073
  runId: run.id,
973
1074
  resourceId: run.resourceId ?? undefined
974
1075
  });
975
1076
  if (persistedRun.status === "cancelled" /* CANCELLED */ || persistedRun.status === "paused" /* PAUSED */ || persistedRun.status === "failed" /* FAILED */) {
976
- this.logger.log(`Step ${stepId} skipped, workflow run is ${persistedRun.status}`, {
977
- runId: run.id,
978
- workflowId: run.workflowId
979
- });
980
1077
  return;
981
1078
  }
982
- const timelineStepCheckEntry = persistedRun.timeline[stepId];
983
- const timelineStepCheck = timelineStepCheckEntry && typeof timelineStepCheckEntry === "object" && "output" in timelineStepCheckEntry ? timelineStepCheckEntry : null;
984
- if (timelineStepCheck?.output !== undefined) {
985
- return timelineStepCheck.output;
1079
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
1080
+ if (cached?.output !== undefined) {
1081
+ return cached.timedOut ? undefined : cached.output;
986
1082
  }
1083
+ const timeoutEvent = timeoutDate ? `__timeout_${stepId}` : undefined;
987
1084
  await this.updateRun({
988
1085
  runId: run.id,
989
1086
  resourceId: run.resourceId ?? undefined,
990
1087
  data: {
991
1088
  status: "paused" /* PAUSED */,
992
1089
  currentStepId: stepId,
1090
+ pausedAt: new Date,
993
1091
  timeline: import_es_toolkit.merge(run.timeline, {
994
1092
  [`${stepId}-wait-for`]: {
995
- waitFor: {
996
- eventName,
997
- timeout
998
- },
1093
+ waitFor: { eventName, timeoutEvent },
999
1094
  timestamp: new Date
1000
1095
  }
1001
- }),
1002
- pausedAt: new Date
1096
+ })
1097
+ }
1098
+ });
1099
+ if (timeoutDate && timeoutEvent) {
1100
+ const job = {
1101
+ runId: run.id,
1102
+ resourceId: run.resourceId ?? undefined,
1103
+ workflowId: run.workflowId,
1104
+ input: run.input,
1105
+ event: { name: timeoutEvent, data: { date: timeoutDate.toISOString() } }
1106
+ };
1107
+ await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
1108
+ startAfter: timeoutDate.getTime() <= Date.now() ? new Date : timeoutDate,
1109
+ expireInSeconds: defaultExpireInSeconds
1110
+ });
1111
+ }
1112
+ this.logger.log(`Step ${stepId} waiting${eventName ? ` for event "${eventName}"` : ""}${timeoutDate ? ` until ${timeoutDate.toISOString()}` : ""}`, { runId: run.id, workflowId: run.workflowId });
1113
+ }
1114
+ async pollStep({
1115
+ run,
1116
+ stepId,
1117
+ conditionFn,
1118
+ intervalMs,
1119
+ timeoutMs
1120
+ }) {
1121
+ const persistedRun = await this.getRun({
1122
+ runId: run.id,
1123
+ resourceId: run.resourceId ?? undefined
1124
+ });
1125
+ if (persistedRun.status === "cancelled" /* CANCELLED */ || persistedRun.status === "paused" /* PAUSED */ || persistedRun.status === "failed" /* FAILED */) {
1126
+ return { timedOut: true };
1127
+ }
1128
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
1129
+ if (cached?.output !== undefined) {
1130
+ return cached.timedOut ? { timedOut: true } : { timedOut: false, data: cached.output };
1131
+ }
1132
+ const pollStateEntry = persistedRun.timeline[`${stepId}-poll`];
1133
+ const startedAt = pollStateEntry && typeof pollStateEntry === "object" && "startedAt" in pollStateEntry ? new Date(pollStateEntry.startedAt) : new Date;
1134
+ if (timeoutMs !== undefined && Date.now() >= startedAt.getTime() + timeoutMs) {
1135
+ await this.updateRun({
1136
+ runId: run.id,
1137
+ resourceId: run.resourceId ?? undefined,
1138
+ data: {
1139
+ currentStepId: stepId,
1140
+ timeline: import_es_toolkit.merge(persistedRun.timeline, {
1141
+ [stepId]: { output: {}, timedOut: true, timestamp: new Date }
1142
+ })
1143
+ }
1144
+ });
1145
+ return { timedOut: true };
1146
+ }
1147
+ const result = await conditionFn();
1148
+ if (result !== false) {
1149
+ await this.updateRun({
1150
+ runId: run.id,
1151
+ resourceId: run.resourceId ?? undefined,
1152
+ data: {
1153
+ currentStepId: stepId,
1154
+ timeline: import_es_toolkit.merge(persistedRun.timeline, {
1155
+ [stepId]: { output: result, timestamp: new Date }
1156
+ })
1157
+ }
1158
+ });
1159
+ return { timedOut: false, data: result };
1160
+ }
1161
+ const pollEvent = `__poll_${stepId}`;
1162
+ await this.updateRun({
1163
+ runId: run.id,
1164
+ resourceId: run.resourceId ?? undefined,
1165
+ data: {
1166
+ status: "paused" /* PAUSED */,
1167
+ currentStepId: stepId,
1168
+ pausedAt: new Date,
1169
+ timeline: import_es_toolkit.merge(persistedRun.timeline, {
1170
+ [`${stepId}-poll`]: { startedAt: startedAt.toISOString() },
1171
+ [`${stepId}-wait-for`]: {
1172
+ waitFor: { timeoutEvent: pollEvent, skipOutput: true },
1173
+ timestamp: new Date
1174
+ }
1175
+ })
1003
1176
  }
1004
1177
  });
1005
- this.logger.log(`Running step ${stepId}, waiting for event ${eventName}...`, {
1178
+ await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, {
1006
1179
  runId: run.id,
1007
- workflowId: run.workflowId
1180
+ resourceId: run.resourceId ?? undefined,
1181
+ workflowId: run.workflowId,
1182
+ input: run.input,
1183
+ event: { name: pollEvent, data: {} }
1184
+ }, {
1185
+ startAfter: new Date(Date.now() + intervalMs),
1186
+ expireInSeconds: defaultExpireInSeconds
1008
1187
  });
1009
- }
1010
- async pauseStep({ stepId, run }) {
1011
- await this.waitForEvent({
1012
- run,
1013
- stepId,
1014
- eventName: PAUSE_EVENT_NAME
1188
+ this.logger.log(`Step ${stepId} polling every ${intervalMs}ms...`, {
1189
+ runId: run.id,
1190
+ workflowId: run.workflowId
1015
1191
  });
1016
- }
1017
- async waitUntil(runId, _date) {
1018
- throw new WorkflowEngineError("Not implemented yet", undefined, runId);
1192
+ return { timedOut: false, data: undefined };
1019
1193
  }
1020
1194
  async checkIfHasStarted() {
1021
1195
  if (!this._started) {
@@ -1055,5 +1229,5 @@ ${error.stack}` : String(error)
1055
1229
  }
1056
1230
  }
1057
1231
 
1058
- //# debugId=B37EEDCFA5F6F16F64756E2164756E21
1232
+ //# debugId=11B08B71DE539A4764756E2164756E21
1059
1233
  //# sourceMappingURL=index.js.map