pg-workflows 0.2.0 → 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.js CHANGED
@@ -1,18 +1,98 @@
1
1
  // src/definition.ts
2
- function workflow(id, handler, { inputSchema, timeout, retries } = {}) {
3
- return {
2
+ function createWorkflowFactory(plugins = []) {
3
+ const factory = (id, handler, { inputSchema, timeout, retries } = {}) => ({
4
4
  id,
5
5
  handler,
6
6
  inputSchema,
7
7
  timeout,
8
- retries
9
- };
8
+ retries,
9
+ plugins: plugins.length > 0 ? plugins : undefined
10
+ });
11
+ factory.use = (plugin) => createWorkflowFactory([
12
+ ...plugins,
13
+ plugin
14
+ ]);
15
+ return factory;
16
+ }
17
+ var workflow = createWorkflowFactory();
18
+ // src/duration.ts
19
+ import parse from "parse-duration";
20
+
21
+ // src/error.ts
22
+ class WorkflowEngineError extends Error {
23
+ workflowId;
24
+ runId;
25
+ cause;
26
+ constructor(message, workflowId, runId, cause = undefined) {
27
+ super(message);
28
+ this.workflowId = workflowId;
29
+ this.runId = runId;
30
+ this.cause = cause;
31
+ this.name = "WorkflowEngineError";
32
+ if (Error.captureStackTrace) {
33
+ Error.captureStackTrace(this, WorkflowEngineError);
34
+ }
35
+ }
36
+ }
37
+
38
+ class WorkflowRunNotFoundError extends WorkflowEngineError {
39
+ constructor(runId, workflowId) {
40
+ super("Workflow run not found", workflowId, runId);
41
+ this.name = "WorkflowRunNotFoundError";
42
+ }
43
+ }
44
+
45
+ // src/duration.ts
46
+ var MS_PER_SECOND = 1000;
47
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
48
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
49
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
50
+ var MS_PER_WEEK = 7 * MS_PER_DAY;
51
+ function parseDuration(duration) {
52
+ if (typeof duration === "string") {
53
+ if (duration.trim() === "") {
54
+ throw new WorkflowEngineError("Invalid duration: empty string");
55
+ }
56
+ const ms2 = parse(duration);
57
+ if (ms2 == null || ms2 <= 0) {
58
+ throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
59
+ }
60
+ return ms2;
61
+ }
62
+ const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
63
+ const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
64
+ if (ms <= 0) {
65
+ throw new WorkflowEngineError("Invalid duration: must be a positive value");
66
+ }
67
+ return ms;
10
68
  }
11
69
  // src/engine.ts
12
70
  import { merge } from "es-toolkit";
13
71
 
14
72
  // src/ast-parser.ts
15
73
  import * as ts from "typescript";
74
+
75
+ // src/types.ts
76
+ var WorkflowStatus;
77
+ ((WorkflowStatus2) => {
78
+ WorkflowStatus2["PENDING"] = "pending";
79
+ WorkflowStatus2["RUNNING"] = "running";
80
+ WorkflowStatus2["PAUSED"] = "paused";
81
+ WorkflowStatus2["COMPLETED"] = "completed";
82
+ WorkflowStatus2["FAILED"] = "failed";
83
+ WorkflowStatus2["CANCELLED"] = "cancelled";
84
+ })(WorkflowStatus ||= {});
85
+ var StepType;
86
+ ((StepType2) => {
87
+ StepType2["PAUSE"] = "pause";
88
+ StepType2["RUN"] = "run";
89
+ StepType2["WAIT_FOR"] = "waitFor";
90
+ StepType2["WAIT_UNTIL"] = "waitUntil";
91
+ StepType2["DELAY"] = "delay";
92
+ StepType2["POLL"] = "poll";
93
+ })(StepType ||= {});
94
+
95
+ // src/ast-parser.ts
16
96
  function parseWorkflowHandler(handler) {
17
97
  const handlerSource = handler.toString();
18
98
  const sourceFile = ts.createSourceFile("handler.ts", handlerSource, ts.ScriptTarget.Latest, true);
@@ -56,13 +136,14 @@ function parseWorkflowHandler(handler) {
56
136
  const propertyAccess = node.expression;
57
137
  const objectName = propertyAccess.expression.getText(sourceFile);
58
138
  const methodName = propertyAccess.name.text;
59
- if (objectName === "step" && (methodName === "run" || methodName === "waitFor" || methodName === "pause" || methodName === "waitUntil")) {
139
+ if (objectName === "step" && (methodName === "run" || methodName === "waitFor" || methodName === "pause" || methodName === "waitUntil" || methodName === "delay" || methodName === "sleep" || methodName === "poll")) {
60
140
  const firstArg = node.arguments[0];
61
141
  if (firstArg) {
62
142
  const { id, isDynamic } = extractStepId(firstArg);
143
+ const stepType = methodName === "sleep" ? "delay" /* DELAY */ : methodName;
63
144
  const stepDefinition = {
64
145
  id,
65
- type: methodName,
146
+ type: stepType,
66
147
  conditional: isInConditional(node),
67
148
  loop: isInLoop(node),
68
149
  isDynamic
@@ -363,48 +444,6 @@ async function withPostgresTransaction(db, callback) {
363
444
  }
364
445
  }
365
446
 
366
- // src/error.ts
367
- class WorkflowEngineError extends Error {
368
- workflowId;
369
- runId;
370
- cause;
371
- constructor(message, workflowId, runId, cause = undefined) {
372
- super(message);
373
- this.workflowId = workflowId;
374
- this.runId = runId;
375
- this.cause = cause;
376
- this.name = "WorkflowEngineError";
377
- if (Error.captureStackTrace) {
378
- Error.captureStackTrace(this, WorkflowEngineError);
379
- }
380
- }
381
- }
382
-
383
- class WorkflowRunNotFoundError extends WorkflowEngineError {
384
- constructor(runId, workflowId) {
385
- super("Workflow run not found", workflowId, runId);
386
- this.name = "WorkflowRunNotFoundError";
387
- }
388
- }
389
-
390
- // src/types.ts
391
- var WorkflowStatus;
392
- ((WorkflowStatus2) => {
393
- WorkflowStatus2["PENDING"] = "pending";
394
- WorkflowStatus2["RUNNING"] = "running";
395
- WorkflowStatus2["PAUSED"] = "paused";
396
- WorkflowStatus2["COMPLETED"] = "completed";
397
- WorkflowStatus2["FAILED"] = "failed";
398
- WorkflowStatus2["CANCELLED"] = "cancelled";
399
- })(WorkflowStatus ||= {});
400
- var StepType;
401
- ((StepType2) => {
402
- StepType2["PAUSE"] = "pause";
403
- StepType2["RUN"] = "run";
404
- StepType2["WAIT_FOR"] = "waitFor";
405
- StepType2["WAIT_UNTIL"] = "waitUntil";
406
- })(StepType ||= {});
407
-
408
447
  // src/engine.ts
409
448
  var PAUSE_EVENT_NAME = "__internal_pause";
410
449
  var WORKFLOW_RUN_QUEUE_NAME = "workflow-run";
@@ -413,7 +452,9 @@ var StepTypeToIcon = {
413
452
  ["run" /* RUN */]: "λ",
414
453
  ["waitFor" /* WAIT_FOR */]: "○",
415
454
  ["pause" /* PAUSE */]: "⏸",
416
- ["waitUntil" /* WAIT_UNTIL */]: "⏲"
455
+ ["waitUntil" /* WAIT_UNTIL */]: "⏲",
456
+ ["delay" /* DELAY */]: "⏱",
457
+ ["poll" /* POLL */]: "↻"
417
458
  };
418
459
  var defaultLogger = {
419
460
  log: (_message) => console.warn(_message),
@@ -513,7 +554,9 @@ class WorkflowEngine {
513
554
  if (!workflow2) {
514
555
  throw new WorkflowEngineError(`Unknown workflow ${workflowId}`);
515
556
  }
516
- if (workflow2.steps.length === 0 || !workflow2.steps[0]) {
557
+ const hasSteps = workflow2.steps.length > 0 && workflow2.steps[0];
558
+ const hasPlugins = (workflow2.plugins?.length ?? 0) > 0;
559
+ if (!hasSteps && !hasPlugins) {
517
560
  throw new WorkflowEngineError(`Workflow ${workflowId} has no steps`, workflowId);
518
561
  }
519
562
  if (workflow2.inputSchema) {
@@ -522,7 +565,7 @@ class WorkflowEngine {
522
565
  throw new WorkflowEngineError(result.error.message, workflowId);
523
566
  }
524
567
  }
525
- const initialStepId = workflow2.steps[0]?.id;
568
+ const initialStepId = workflow2.steps[0]?.id ?? "__start__";
526
569
  const run = await withPostgresTransaction(this.boss.getDb(), async (_db) => {
527
570
  const timeoutAt = options?.timeout ? new Date(Date.now() + options.timeout) : workflow2.timeout ? new Date(Date.now() + workflow2.timeout) : null;
528
571
  const insertedRun = await insertWorkflowRun({
@@ -709,11 +752,13 @@ class WorkflowEngine {
709
752
  if (run.status === "paused" /* PAUSED */) {
710
753
  const waitForStepEntry = run.timeline[`${run.currentStepId}-wait-for`];
711
754
  const waitForStep = waitForStepEntry && typeof waitForStepEntry === "object" && "waitFor" in waitForStepEntry ? waitForStepEntry : null;
712
- const currentStepEntry = run.timeline[run.currentStepId];
713
- const currentStep = currentStepEntry && typeof currentStepEntry === "object" && "output" in currentStepEntry ? currentStepEntry : null;
755
+ const currentStep = this.getCachedStepEntry(run.timeline, run.currentStepId);
714
756
  const waitFor = waitForStep?.waitFor;
715
757
  const hasCurrentStepOutput = currentStep?.output !== undefined;
716
- if (waitFor && waitFor.eventName === event?.name && !hasCurrentStepOutput) {
758
+ const eventMatches = waitFor && event?.name && (event.name === waitFor.eventName || event.name === waitFor.timeoutEvent) && !hasCurrentStepOutput;
759
+ if (eventMatches) {
760
+ const isTimeout = event?.name === waitFor?.timeoutEvent;
761
+ const skipOutput = waitFor?.skipOutput;
717
762
  run = await this.updateRun({
718
763
  runId,
719
764
  resourceId,
@@ -721,13 +766,16 @@ class WorkflowEngine {
721
766
  status: "running" /* RUNNING */,
722
767
  pausedAt: null,
723
768
  resumedAt: new Date,
724
- timeline: merge(run.timeline, {
725
- [run.currentStepId]: {
726
- output: event?.data ?? {},
727
- timestamp: new Date
728
- }
729
- }),
730
- jobId: job?.id
769
+ jobId: job?.id,
770
+ ...skipOutput ? {} : {
771
+ timeline: merge(run.timeline, {
772
+ [run.currentStepId]: {
773
+ output: event?.data ?? {},
774
+ ...isTimeout ? { timedOut: true } : {},
775
+ timestamp: new Date
776
+ }
777
+ })
778
+ }
731
779
  }
732
780
  });
733
781
  } else {
@@ -743,58 +791,79 @@ class WorkflowEngine {
743
791
  });
744
792
  }
745
793
  }
794
+ const baseStep = {
795
+ run: async (stepId, handler) => {
796
+ if (!run) {
797
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
798
+ }
799
+ return this.runStep({ stepId, run, handler });
800
+ },
801
+ waitFor: async (stepId, { eventName, timeout }) => {
802
+ if (!run) {
803
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
804
+ }
805
+ const timeoutDate = timeout ? new Date(Date.now() + timeout) : undefined;
806
+ return this.waitStep({ run, stepId, eventName, timeoutDate });
807
+ },
808
+ waitUntil: async (stepId, dateOrOptions) => {
809
+ if (!run) {
810
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
811
+ }
812
+ const date = dateOrOptions instanceof Date ? dateOrOptions : typeof dateOrOptions === "string" ? new Date(dateOrOptions) : dateOrOptions.date instanceof Date ? dateOrOptions.date : new Date(dateOrOptions.date);
813
+ await this.waitStep({ run, stepId, timeoutDate: date });
814
+ },
815
+ pause: async (stepId) => {
816
+ if (!run) {
817
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
818
+ }
819
+ await this.waitStep({ run, stepId, eventName: PAUSE_EVENT_NAME });
820
+ },
821
+ delay: async (stepId, duration) => {
822
+ if (!run) {
823
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
824
+ }
825
+ await this.waitStep({
826
+ run,
827
+ stepId,
828
+ timeoutDate: new Date(Date.now() + parseDuration(duration))
829
+ });
830
+ },
831
+ get sleep() {
832
+ return this.delay;
833
+ },
834
+ poll: async (stepId, conditionFn, options) => {
835
+ if (!run) {
836
+ throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
837
+ }
838
+ const intervalMs = parseDuration(options?.interval ?? "30s");
839
+ if (intervalMs < 30000) {
840
+ throw new WorkflowEngineError(`step.poll interval must be at least 30s (got ${intervalMs}ms)`, workflowId, runId);
841
+ }
842
+ const timeoutMs = options?.timeout ? parseDuration(options.timeout) : undefined;
843
+ return this.pollStep({ run, stepId, conditionFn, intervalMs, timeoutMs });
844
+ }
845
+ };
846
+ let step = { ...baseStep };
847
+ const plugins = workflow2.plugins ?? [];
848
+ for (const plugin of plugins) {
849
+ const extra = plugin.methods(step);
850
+ step = { ...step, ...extra };
851
+ }
746
852
  const context = {
747
853
  input: run.input,
748
854
  workflowId: run.workflowId,
749
855
  runId: run.id,
750
856
  timeline: run.timeline,
751
857
  logger: this.logger,
752
- step: {
753
- run: async (stepId, handler) => {
754
- if (!run) {
755
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
756
- }
757
- return this.runStep({
758
- stepId,
759
- run,
760
- handler
761
- });
762
- },
763
- waitFor: async (stepId, { eventName, timeout }) => {
764
- if (!run) {
765
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
766
- }
767
- return this.waitForEvent({
768
- run,
769
- stepId,
770
- eventName,
771
- timeout
772
- });
773
- },
774
- waitUntil: async (stepId, { date }) => {
775
- if (!run) {
776
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
777
- }
778
- await this.waitUntilDate({
779
- run,
780
- stepId,
781
- date
782
- });
783
- },
784
- pause: async (stepId) => {
785
- if (!run) {
786
- throw new WorkflowEngineError("Missing workflow run", workflowId, runId);
787
- }
788
- return this.pauseStep({
789
- stepId,
790
- run
791
- });
792
- }
793
- }
858
+ step
794
859
  };
795
860
  const result = await workflow2.handler(context);
796
861
  run = await this.getRun({ runId, resourceId });
797
- if (run.status === "running" /* RUNNING */ && run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id) {
862
+ const isLastParsedStep = run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id;
863
+ const hasPluginSteps = (workflow2.plugins?.length ?? 0) > 0;
864
+ const noParsedSteps = workflow2.steps.length === 0;
865
+ const shouldComplete = run.status === "running" /* RUNNING */ && (noParsedSteps || isLastParsedStep || hasPluginSteps && result !== undefined);
866
+ if (shouldComplete) {
798
867
  const normalizedResult = result === undefined ? {} : result;
799
868
  await this.updateRun({
800
869
  runId,
@@ -846,6 +915,10 @@ class WorkflowEngine {
846
915
  throw error;
847
916
  }
848
917
  }
918
+ getCachedStepEntry(timeline, stepId) {
919
+ const stepEntry = timeline[stepId];
920
+ return stepEntry && typeof stepEntry === "object" && "output" in stepEntry ? stepEntry : null;
921
+ }
849
922
  async runStep({
850
923
  stepId,
851
924
  run,
@@ -864,39 +937,38 @@ class WorkflowEngine {
864
937
  return;
865
938
  }
866
939
  try {
867
- let result;
868
- const timelineStepEntry = persistedRun.timeline[stepId];
869
- const timelineStep = timelineStepEntry && typeof timelineStepEntry === "object" && "output" in timelineStepEntry ? timelineStepEntry : null;
870
- if (timelineStep?.output !== undefined) {
871
- result = timelineStep.output;
872
- } else {
873
- await this.updateRun({
874
- runId: run.id,
875
- resourceId: run.resourceId ?? undefined,
876
- data: {
877
- currentStepId: stepId
878
- }
879
- }, { db });
880
- this.logger.log(`Running step ${stepId}...`, {
881
- runId: run.id,
882
- workflowId: run.workflowId
883
- });
884
- result = await handler();
885
- run = await this.updateRun({
886
- runId: run.id,
887
- resourceId: run.resourceId ?? undefined,
888
- data: {
889
- timeline: merge(run.timeline, {
890
- [stepId]: {
891
- output: result === undefined ? {} : result,
892
- timestamp: new Date
893
- }
894
- })
895
- }
896
- }, { db });
940
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
941
+ if (cached?.output !== undefined) {
942
+ return cached.output;
897
943
  }
898
- const finalResult = result === undefined ? {} : result;
899
- return finalResult;
944
+ await this.updateRun({
945
+ runId: run.id,
946
+ resourceId: run.resourceId ?? undefined,
947
+ data: {
948
+ currentStepId: stepId
949
+ }
950
+ }, { db });
951
+ this.logger.log(`Running step ${stepId}...`, {
952
+ runId: run.id,
953
+ workflowId: run.workflowId
954
+ });
955
+ let output = await handler();
956
+ if (output === undefined) {
957
+ output = {};
958
+ }
959
+ run = await this.updateRun({
960
+ runId: run.id,
961
+ resourceId: run.resourceId ?? undefined,
962
+ data: {
963
+ timeline: merge(run.timeline, {
964
+ [stepId]: {
965
+ output,
966
+ timestamp: new Date
967
+ }
968
+ })
969
+ }
970
+ }, { db });
971
+ return output;
900
972
  } catch (error) {
901
973
  this.logger.error(`Step ${stepId} failed:`, error, {
902
974
  runId: run.id,
@@ -915,87 +987,133 @@ ${error.stack}` : String(error)
915
987
  }
916
988
  });
917
989
  }
918
- async waitForEvent({
990
+ async waitStep({
919
991
  run,
920
992
  stepId,
921
993
  eventName,
922
- timeout
994
+ timeoutDate
923
995
  }) {
924
996
  const persistedRun = await this.getRun({
925
997
  runId: run.id,
926
998
  resourceId: run.resourceId ?? undefined
927
999
  });
928
1000
  if (persistedRun.status === "cancelled" /* CANCELLED */ || persistedRun.status === "paused" /* PAUSED */ || persistedRun.status === "failed" /* FAILED */) {
929
- this.logger.log(`Step ${stepId} skipped, workflow run is ${persistedRun.status}`, {
930
- runId: run.id,
931
- workflowId: run.workflowId
932
- });
933
1001
  return;
934
1002
  }
935
- const timelineStepCheckEntry = persistedRun.timeline[stepId];
936
- const timelineStepCheck = timelineStepCheckEntry && typeof timelineStepCheckEntry === "object" && "output" in timelineStepCheckEntry ? timelineStepCheckEntry : null;
937
- if (timelineStepCheck?.output !== undefined) {
938
- return timelineStepCheck.output;
1003
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
1004
+ if (cached?.output !== undefined) {
1005
+ return cached.timedOut ? undefined : cached.output;
939
1006
  }
1007
+ const timeoutEvent = timeoutDate ? `__timeout_${stepId}` : undefined;
940
1008
  await this.updateRun({
941
1009
  runId: run.id,
942
1010
  resourceId: run.resourceId ?? undefined,
943
1011
  data: {
944
1012
  status: "paused" /* PAUSED */,
945
1013
  currentStepId: stepId,
1014
+ pausedAt: new Date,
946
1015
  timeline: merge(run.timeline, {
947
1016
  [`${stepId}-wait-for`]: {
948
- waitFor: {
949
- eventName,
950
- timeout
951
- },
1017
+ waitFor: { eventName, timeoutEvent },
952
1018
  timestamp: new Date
953
1019
  }
954
- }),
955
- pausedAt: new Date
1020
+ })
956
1021
  }
957
1022
  });
958
- this.logger.log(`Running step ${stepId}, waiting for event ${eventName}...`, {
959
- runId: run.id,
960
- workflowId: run.workflowId
961
- });
962
- }
963
- async pauseStep({ stepId, run }) {
964
- await this.waitForEvent({
965
- run,
966
- stepId,
967
- eventName: PAUSE_EVENT_NAME
968
- });
1023
+ if (timeoutDate && timeoutEvent) {
1024
+ const job = {
1025
+ runId: run.id,
1026
+ resourceId: run.resourceId ?? undefined,
1027
+ workflowId: run.workflowId,
1028
+ input: run.input,
1029
+ event: { name: timeoutEvent, data: { date: timeoutDate.toISOString() } }
1030
+ };
1031
+ await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
1032
+ startAfter: timeoutDate.getTime() <= Date.now() ? new Date : timeoutDate,
1033
+ expireInSeconds: defaultExpireInSeconds
1034
+ });
1035
+ }
1036
+ this.logger.log(`Step ${stepId} waiting${eventName ? ` for event "${eventName}"` : ""}${timeoutDate ? ` until ${timeoutDate.toISOString()}` : ""}`, { runId: run.id, workflowId: run.workflowId });
969
1037
  }
970
- async waitUntilDate({
1038
+ async pollStep({
971
1039
  run,
972
1040
  stepId,
973
- date
1041
+ conditionFn,
1042
+ intervalMs,
1043
+ timeoutMs
974
1044
  }) {
975
- const eventName = `__wait_until_${stepId}`;
976
- await this.waitForEvent({
977
- run,
978
- stepId,
979
- eventName
1045
+ const persistedRun = await this.getRun({
1046
+ runId: run.id,
1047
+ resourceId: run.resourceId ?? undefined
980
1048
  });
981
- const job = {
1049
+ if (persistedRun.status === "cancelled" /* CANCELLED */ || persistedRun.status === "paused" /* PAUSED */ || persistedRun.status === "failed" /* FAILED */) {
1050
+ return { timedOut: true };
1051
+ }
1052
+ const cached = this.getCachedStepEntry(persistedRun.timeline, stepId);
1053
+ if (cached?.output !== undefined) {
1054
+ return cached.timedOut ? { timedOut: true } : { timedOut: false, data: cached.output };
1055
+ }
1056
+ const pollStateEntry = persistedRun.timeline[`${stepId}-poll`];
1057
+ const startedAt = pollStateEntry && typeof pollStateEntry === "object" && "startedAt" in pollStateEntry ? new Date(pollStateEntry.startedAt) : new Date;
1058
+ if (timeoutMs !== undefined && Date.now() >= startedAt.getTime() + timeoutMs) {
1059
+ await this.updateRun({
1060
+ runId: run.id,
1061
+ resourceId: run.resourceId ?? undefined,
1062
+ data: {
1063
+ currentStepId: stepId,
1064
+ timeline: merge(persistedRun.timeline, {
1065
+ [stepId]: { output: {}, timedOut: true, timestamp: new Date }
1066
+ })
1067
+ }
1068
+ });
1069
+ return { timedOut: true };
1070
+ }
1071
+ const result = await conditionFn();
1072
+ if (result !== false) {
1073
+ await this.updateRun({
1074
+ runId: run.id,
1075
+ resourceId: run.resourceId ?? undefined,
1076
+ data: {
1077
+ currentStepId: stepId,
1078
+ timeline: merge(persistedRun.timeline, {
1079
+ [stepId]: { output: result, timestamp: new Date }
1080
+ })
1081
+ }
1082
+ });
1083
+ return { timedOut: false, data: result };
1084
+ }
1085
+ const pollEvent = `__poll_${stepId}`;
1086
+ await this.updateRun({
1087
+ runId: run.id,
1088
+ resourceId: run.resourceId ?? undefined,
1089
+ data: {
1090
+ status: "paused" /* PAUSED */,
1091
+ currentStepId: stepId,
1092
+ pausedAt: new Date,
1093
+ timeline: merge(persistedRun.timeline, {
1094
+ [`${stepId}-poll`]: { startedAt: startedAt.toISOString() },
1095
+ [`${stepId}-wait-for`]: {
1096
+ waitFor: { timeoutEvent: pollEvent, skipOutput: true },
1097
+ timestamp: new Date
1098
+ }
1099
+ })
1100
+ }
1101
+ });
1102
+ await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, {
982
1103
  runId: run.id,
983
1104
  resourceId: run.resourceId ?? undefined,
984
1105
  workflowId: run.workflowId,
985
1106
  input: run.input,
986
- event: {
987
- name: eventName,
988
- data: { date: date.toISOString() }
989
- }
990
- };
991
- await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
992
- startAfter: date,
1107
+ event: { name: pollEvent, data: {} }
1108
+ }, {
1109
+ startAfter: new Date(Date.now() + intervalMs),
993
1110
  expireInSeconds: defaultExpireInSeconds
994
1111
  });
995
- this.logger.log(`Running step ${stepId}, waiting until ${date.toISOString()}...`, {
1112
+ this.logger.log(`Step ${stepId} polling every ${intervalMs}ms...`, {
996
1113
  runId: run.id,
997
1114
  workflowId: run.workflowId
998
1115
  });
1116
+ return { timedOut: false, data: undefined };
999
1117
  }
1000
1118
  async checkIfHasStarted() {
1001
1119
  if (!this._started) {
@@ -1036,6 +1154,7 @@ ${error.stack}` : String(error)
1036
1154
  }
1037
1155
  export {
1038
1156
  workflow,
1157
+ parseDuration,
1039
1158
  WorkflowStatus,
1040
1159
  WorkflowRunNotFoundError,
1041
1160
  WorkflowEngineError,
@@ -1043,5 +1162,5 @@ export {
1043
1162
  StepType
1044
1163
  };
1045
1164
 
1046
- //# debugId=83CCFC149D5B47CD64756E2164756E21
1165
+ //# debugId=4ADA0797255A878364756E2164756E21
1047
1166
  //# sourceMappingURL=index.js.map