queuebear 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -491,6 +491,76 @@ The signing secret is available in your QueueBear project settings. When configu
491
491
 
492
492
  ---
493
493
 
494
+ ## Local Development
495
+
496
+ When developing locally, your webhook endpoints run on `localhost` which isn't accessible from QueueBear's servers. Use a tunnel service to expose your local server.
497
+
498
+ ### Using ngrok (Recommended)
499
+
500
+ [ngrok](https://ngrok.com) provides stable URLs and a built-in request inspector.
501
+
502
+ **1. Install and authenticate:**
503
+
504
+ ```bash
505
+ # Install
506
+ brew install ngrok # or download from ngrok.com/download
507
+
508
+ # Authenticate (free account required)
509
+ ngrok config add-authtoken YOUR_AUTH_TOKEN
510
+ ```
511
+
512
+ **2. Start the tunnel:**
513
+
514
+ ```bash
515
+ ngrok http 3000
516
+ ```
517
+
518
+ **3. Use the forwarding URL:**
519
+
520
+ ```typescript
521
+ // Use ngrok URL instead of localhost
522
+ await qb.messages.publish("https://abc123.ngrok.io/api/webhooks", {
523
+ event: "user.created",
524
+ userId: "123"
525
+ });
526
+
527
+ // Works for workflows too
528
+ await qb.workflows.trigger(
529
+ "onboarding",
530
+ "https://abc123.ngrok.io/api/workflows/onboarding",
531
+ { userId: "123" }
532
+ );
533
+ ```
534
+
535
+ **Debugging:** ngrok provides a web inspector at `http://localhost:4040` to view requests, responses, and replay failed deliveries.
536
+
537
+ ### Using localtunnel
538
+
539
+ [localtunnel](https://localtunnel.me) is free and requires no signup.
540
+
541
+ ```bash
542
+ npx localtunnel --port 3000
543
+ # Output: your url is: https://good-months-leave.loca.lt
544
+ ```
545
+
546
+ **Note:** localtunnel shows a reminder page on first visit. Bypass it by adding a header:
547
+
548
+ ```typescript
549
+ await qb.messages.publish(
550
+ "https://good-months-leave.loca.lt/api/webhooks",
551
+ { event: "test" },
552
+ { headers: { "bypass-tunnel-reminder": "true" } }
553
+ );
554
+ ```
555
+
556
+ ### Tips
557
+
558
+ - Store your tunnel URL in `.env` for easy switching between local and production
559
+ - Both `callbackUrl` and `failureCallbackUrl` need public URLs for local testing
560
+ - ngrok URLs change on restart (stable URLs require a paid plan)
561
+
562
+ ---
563
+
494
564
  ## License
495
565
 
496
566
  MIT
package/dist/index.d.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * Base HTTP client for QueueBear API requests
3
3
  */
4
4
  interface BaseClientOptions {
5
- baseUrl: string;
6
5
  apiKey: string;
7
6
  projectId: string;
8
7
  }
@@ -14,7 +13,6 @@ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
14
13
  * Base client providing shared HTTP functionality
15
14
  */
16
15
  declare class BaseClient {
17
- protected baseUrl: string;
18
16
  protected apiKey: string;
19
17
  protected projectId: string;
20
18
  constructor(options: BaseClientOptions);
@@ -113,7 +111,6 @@ interface StepRetryOptions {
113
111
  * Options for serve() function
114
112
  */
115
113
  interface ServeOptions {
116
- baseUrl?: string;
117
114
  retries?: number;
118
115
  signingSecret?: string;
119
116
  }
@@ -919,8 +916,6 @@ declare class WorkflowsAPI extends BaseClient {
919
916
  * QueueBear client options
920
917
  */
921
918
  interface QueueBearOptions {
922
- /** Base URL of your QueueBear instance */
923
- baseUrl: string;
924
919
  /** API key (qb_live_* or qb_test_*) */
925
920
  apiKey: string;
926
921
  /** Project ID */
@@ -937,7 +932,6 @@ interface QueueBearOptions {
937
932
  * import { QueueBear, serve } from "@queuebear/workflow";
938
933
  *
939
934
  * const qb = new QueueBear({
940
- * baseUrl: "https://your-queuebear-instance.com",
941
935
  * apiKey: "qb_live_xxx",
942
936
  * projectId: "proj_xxx",
943
937
  * });
@@ -1109,6 +1103,22 @@ declare class WorkflowPausedError extends Error {
1109
1103
  reason: string;
1110
1104
  constructor(reason: string);
1111
1105
  }
1106
+ /**
1107
+ * Error thrown when parallel execution has failures
1108
+ */
1109
+ declare class ParallelExecutionError extends Error {
1110
+ errors: Array<{
1111
+ index: number;
1112
+ stepName: string;
1113
+ error: Error;
1114
+ }>;
1115
+ constructor(message: string, errors: Array<{
1116
+ index: number;
1117
+ stepName: string;
1118
+ error: Error;
1119
+ }>);
1120
+ get failedStepCount(): number;
1121
+ }
1112
1122
  /**
1113
1123
  * Context options for creating a new WorkflowContext
1114
1124
  */
@@ -1116,26 +1126,17 @@ interface WorkflowContextOptions<T> {
1116
1126
  runId: string;
1117
1127
  runToken: string;
1118
1128
  input: T;
1119
- baseUrl: string;
1120
1129
  authHeader: string;
1121
1130
  }
1122
1131
  /**
1123
1132
  * WorkflowContext provides methods for executing workflow steps
1124
1133
  */
1125
- declare class WorkflowContext<T> {
1134
+ interface WorkflowContext<T> {
1126
1135
  readonly runId: string;
1127
1136
  readonly input: T;
1128
- private runToken;
1129
- private baseUrl;
1130
- private authHeader;
1131
- private stepIndex;
1132
- constructor(options: WorkflowContextOptions<T>);
1133
1137
  /**
1134
1138
  * Execute a step with caching
1135
1139
  * Step names must be unique within a workflow run
1136
- * @param stepName - Unique name for this step
1137
- * @param fn - Function to execute
1138
- * @param options - Optional retry configuration
1139
1140
  */
1140
1141
  run<R>(stepName: string, fn: () => Promise<R>, options?: StepRetryOptions): Promise<R>;
1141
1142
  /**
@@ -1158,44 +1159,26 @@ declare class WorkflowContext<T> {
1158
1159
  call<R>(stepName: string, config: CallConfig): Promise<R>;
1159
1160
  /**
1160
1161
  * Wait for an external event
1161
- * @param stepName - Unique step name
1162
- * @param eventName - Name of the event to wait for
1163
- * @param options - Optional event key and timeout
1164
1162
  */
1165
- waitForEvent<T = unknown>(stepName: string, eventName: string, options?: WaitForEventOptions): Promise<T>;
1163
+ waitForEvent<E = unknown>(stepName: string, eventName: string, options?: WaitForEventOptions): Promise<E>;
1166
1164
  /**
1167
1165
  * Send a fire-and-forget notification event
1168
- * @param eventName - Name of the event to send
1169
- * @param payload - Optional payload to include
1170
1166
  */
1171
1167
  notify(eventName: string, payload?: unknown): Promise<void>;
1172
1168
  /**
1173
1169
  * Execute multiple steps in parallel
1174
- * @param steps - Array of step definitions with name and function
1175
1170
  */
1176
- parallel<T extends readonly unknown[]>(steps: {
1177
- [K in keyof T]: {
1171
+ parallel<R extends readonly unknown[]>(steps: {
1172
+ [K in keyof R]: {
1178
1173
  name: string;
1179
- fn: () => Promise<T[K]>;
1174
+ fn: () => Promise<R[K]>;
1180
1175
  };
1181
- }): Promise<T>;
1176
+ }): Promise<R>;
1182
1177
  }
1183
1178
  /**
1184
- * Error thrown when parallel execution has failures
1179
+ * Create a workflow context for executing workflow steps
1185
1180
  */
1186
- declare class ParallelExecutionError extends Error {
1187
- errors: Array<{
1188
- index: number;
1189
- stepName: string;
1190
- error: Error;
1191
- }>;
1192
- constructor(message: string, errors: Array<{
1193
- index: number;
1194
- stepName: string;
1195
- error: Error;
1196
- }>);
1197
- get failedStepCount(): number;
1198
- }
1181
+ declare function createWorkflowContext<T>(options: WorkflowContextOptions<T>): WorkflowContext<T>;
1199
1182
 
1200
1183
  /**
1201
1184
  * Workflow handler function type
@@ -1206,4 +1189,4 @@ type WorkflowHandler<TInput = unknown, TOutput = unknown> = (context: WorkflowCo
1206
1189
  */
1207
1190
  declare function serve<TInput = unknown, TOutput = unknown>(handler: WorkflowHandler<TInput, TOutput>, options?: ServeOptions): (request: Request) => Promise<Response>;
1208
1191
 
1209
- export { type CallConfig, type CancelMessageResponse, type CompletedStep, type CreateScheduleOptions, type CreateScheduleResponse, DLQAPI, type DLQEntry, type DLQEntrySummary, type DeleteDLQResponse, type DeleteScheduleResponse, type DeliveryLog, type LastResponse, type ListDLQOptions, type ListDLQResponse, type ListMessagesOptions, type ListMessagesResponse, type ListRunsOptions, type ListRunsResponse, type ListSchedulesOptions, type ListSchedulesResponse, type Message, type MessageMethod, type MessageStatus, type MessageSummary, MessagesAPI, type Pagination, ParallelExecutionError, type ParallelStepDefinition, type PauseScheduleResponse, type PublishOptions, type PublishResponse, type PurgeDLQResponse, QueueBear, QueueBearError, type QueueBearOptions, type ResumeScheduleResponse, type RetryDLQResponse, type Schedule, type ScheduleSummary, SchedulesAPI, type SendEventOptions, type SendEventResponse, type ServeOptions, type StatusResponse, type StepRetryOptions, type StepType, type TriggerOptions, type TriggerResponse, type WaitForEventOptions, WorkflowContext, type WorkflowContextOptions, type WorkflowHandler, WorkflowPausedError, type WorkflowRun, type WorkflowRunStatus, type WorkflowStep, WorkflowsAPI, serve };
1192
+ export { type CallConfig, type CancelMessageResponse, type CompletedStep, type CreateScheduleOptions, type CreateScheduleResponse, DLQAPI, type DLQEntry, type DLQEntrySummary, type DeleteDLQResponse, type DeleteScheduleResponse, type DeliveryLog, type LastResponse, type ListDLQOptions, type ListDLQResponse, type ListMessagesOptions, type ListMessagesResponse, type ListRunsOptions, type ListRunsResponse, type ListSchedulesOptions, type ListSchedulesResponse, type Message, type MessageMethod, type MessageStatus, type MessageSummary, MessagesAPI, type Pagination, ParallelExecutionError, type ParallelStepDefinition, type PauseScheduleResponse, type PublishOptions, type PublishResponse, type PurgeDLQResponse, QueueBear, QueueBearError, type QueueBearOptions, type ResumeScheduleResponse, type RetryDLQResponse, type Schedule, type ScheduleSummary, SchedulesAPI, type SendEventOptions, type SendEventResponse, type ServeOptions, type StatusResponse, type StepRetryOptions, type StepType, type TriggerOptions, type TriggerResponse, type WaitForEventOptions, type WorkflowContext, type WorkflowContextOptions, type WorkflowHandler, WorkflowPausedError, type WorkflowRun, type WorkflowRunStatus, type WorkflowStep, WorkflowsAPI, createWorkflowContext, serve };
package/dist/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  // src/api/base.ts
2
2
  var BaseClient = class {
3
- baseUrl;
4
3
  apiKey;
5
4
  projectId;
6
5
  constructor(options) {
7
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
8
6
  this.apiKey = options.apiKey;
9
7
  this.projectId = options.projectId;
10
8
  }
@@ -12,7 +10,9 @@ var BaseClient = class {
12
10
  * Build the full URL for a project-scoped endpoint
13
11
  */
14
12
  buildUrl(path, queryParams) {
15
- const url = new URL(`${this.baseUrl}/v1/projects/${this.projectId}${path}`);
13
+ const url = new URL(
14
+ `https://api.queuebear.com/v1/projects/${this.projectId}${path}`
15
+ );
16
16
  if (queryParams) {
17
17
  for (const [key, value] of Object.entries(queryParams)) {
18
18
  if (value !== void 0) {
@@ -80,36 +80,36 @@ var MessagesAPI = class extends BaseClient {
80
80
  * ```
81
81
  */
82
82
  async publish(destination, body, options) {
83
- const headers = {
84
- "Content-Type": "application/json"
83
+ const requestBody = {
84
+ destination,
85
+ body
85
86
  };
86
87
  if (options?.delay) {
87
- headers["queuebear-delay"] = options.delay;
88
+ requestBody.delay = options.delay;
88
89
  }
89
90
  if (options?.retries !== void 0) {
90
- headers["queuebear-retries"] = String(options.retries);
91
+ requestBody.retries = options.retries;
91
92
  }
92
93
  if (options?.method) {
93
- headers["queuebear-method"] = options.method;
94
+ requestBody.method = options.method;
94
95
  }
95
96
  if (options?.callbackUrl) {
96
- headers["queuebear-callback"] = options.callbackUrl;
97
+ requestBody.callbackUrl = options.callbackUrl;
97
98
  }
98
99
  if (options?.failureCallbackUrl) {
99
- headers["queuebear-failure-callback"] = options.failureCallbackUrl;
100
+ requestBody.failureCallbackUrl = options.failureCallbackUrl;
100
101
  }
101
102
  if (options?.deduplicationId) {
102
- headers["queuebear-deduplication-id"] = options.deduplicationId;
103
+ requestBody.deduplicationId = options.deduplicationId;
103
104
  }
104
105
  if (options?.headers) {
105
- for (const [key, value] of Object.entries(options.headers)) {
106
- headers[`queuebear-forward-${key}`] = value;
107
- }
106
+ requestBody.headers = options.headers;
108
107
  }
109
- const encodedDestination = encodeURIComponent(destination);
110
- return this.request("POST", `/publish/${encodedDestination}`, {
111
- body,
112
- headers
108
+ return this.request("POST", "/publish", {
109
+ body: requestBody,
110
+ headers: {
111
+ "Content-Type": "application/json"
112
+ }
113
113
  });
114
114
  }
115
115
  /**
@@ -748,7 +748,6 @@ var QueueBear = class {
748
748
  workflows;
749
749
  constructor(options) {
750
750
  this.options = {
751
- baseUrl: options.baseUrl.replace(/\/$/, ""),
752
751
  apiKey: options.apiKey,
753
752
  projectId: options.projectId
754
753
  };
@@ -803,11 +802,17 @@ var QueueBear = class {
803
802
  * ```
804
803
  */
805
804
  async triggerAndWait(workflowId, workflowUrl, input, options) {
806
- return this.workflows.triggerAndWait(workflowId, workflowUrl, input, options);
805
+ return this.workflows.triggerAndWait(
806
+ workflowId,
807
+ workflowUrl,
808
+ input,
809
+ options
810
+ );
807
811
  }
808
812
  };
809
813
 
810
814
  // src/context.ts
815
+ var BASE_URL = "https://api.queuebear.com/v1/workflows";
811
816
  var WorkflowPausedError = class extends Error {
812
817
  constructor(reason) {
813
818
  super(reason);
@@ -815,63 +820,55 @@ var WorkflowPausedError = class extends Error {
815
820
  this.name = "WorkflowPausedError";
816
821
  }
817
822
  };
818
- var WorkflowContext = class {
819
- runId;
820
- input;
821
- runToken;
822
- baseUrl;
823
- authHeader;
824
- stepIndex = 0;
825
- constructor(options) {
826
- this.runId = options.runId;
827
- this.runToken = options.runToken;
828
- this.input = options.input;
829
- this.baseUrl = options.baseUrl;
830
- this.authHeader = options.authHeader;
823
+ var ParallelExecutionError = class extends Error {
824
+ constructor(message, errors) {
825
+ super(message);
826
+ this.errors = errors;
827
+ this.name = "ParallelExecutionError";
831
828
  }
832
- /**
833
- * Execute a step with caching
834
- * Step names must be unique within a workflow run
835
- * @param stepName - Unique name for this step
836
- * @param fn - Function to execute
837
- * @param options - Optional retry configuration
838
- */
839
- async run(stepName, fn, options) {
829
+ get failedStepCount() {
830
+ return this.errors.length;
831
+ }
832
+ };
833
+ function createWorkflowContext(options) {
834
+ const { runId, runToken, input, authHeader } = options;
835
+ let stepIndex = 0;
836
+ const run = async (stepName, fn, retryOptions) => {
840
837
  const checkResponse = await fetch(
841
- `${this.baseUrl}/v1/workflows/internal/step/check`,
838
+ `${BASE_URL}/internal/step/check`,
842
839
  {
843
840
  method: "POST",
844
841
  headers: {
845
842
  "Content-Type": "application/json",
846
- Authorization: this.authHeader,
847
- "X-QueueBear-Run-Token": this.runToken
843
+ Authorization: authHeader,
844
+ "X-QueueBear-Run-Token": runToken
848
845
  },
849
846
  body: JSON.stringify({
850
- runId: this.runId,
847
+ runId,
851
848
  stepName
852
849
  })
853
850
  }
854
851
  );
855
852
  const checkData = await checkResponse.json();
856
853
  if (checkData.cached) {
857
- this.stepIndex++;
854
+ stepIndex++;
858
855
  return checkData.result;
859
856
  }
860
857
  const startResponse = await fetch(
861
- `${this.baseUrl}/v1/workflows/internal/step/start`,
858
+ `${BASE_URL}/internal/step/start`,
862
859
  {
863
860
  method: "POST",
864
861
  headers: {
865
862
  "Content-Type": "application/json",
866
- Authorization: this.authHeader,
867
- "X-QueueBear-Run-Token": this.runToken
863
+ Authorization: authHeader,
864
+ "X-QueueBear-Run-Token": runToken
868
865
  },
869
866
  body: JSON.stringify({
870
- runId: this.runId,
867
+ runId,
871
868
  stepName,
872
- stepIndex: this.stepIndex,
869
+ stepIndex,
873
870
  stepType: "run",
874
- retryOptions: options
871
+ retryOptions
875
872
  })
876
873
  }
877
874
  );
@@ -881,76 +878,72 @@ var WorkflowContext = class {
881
878
  }
882
879
  try {
883
880
  const result = await fn();
884
- await fetch(`${this.baseUrl}/v1/workflows/internal/step/complete`, {
881
+ await fetch(`${BASE_URL}/internal/step/complete`, {
885
882
  method: "POST",
886
883
  headers: {
887
884
  "Content-Type": "application/json",
888
- Authorization: this.authHeader,
889
- "X-QueueBear-Run-Token": this.runToken
885
+ Authorization: authHeader,
886
+ "X-QueueBear-Run-Token": runToken
890
887
  },
891
888
  body: JSON.stringify({
892
- runId: this.runId,
889
+ runId,
893
890
  stepName,
894
891
  result
895
892
  })
896
893
  });
897
- this.stepIndex++;
894
+ stepIndex++;
898
895
  return result;
899
896
  } catch (error) {
900
- await fetch(`${this.baseUrl}/v1/workflows/internal/step/fail`, {
897
+ await fetch(`${BASE_URL}/internal/step/fail`, {
901
898
  method: "POST",
902
899
  headers: {
903
900
  "Content-Type": "application/json",
904
- Authorization: this.authHeader,
905
- "X-QueueBear-Run-Token": this.runToken
901
+ Authorization: authHeader,
902
+ "X-QueueBear-Run-Token": runToken
906
903
  },
907
904
  body: JSON.stringify({
908
- runId: this.runId,
905
+ runId,
909
906
  stepName,
910
907
  error: error instanceof Error ? error.message : String(error)
911
908
  })
912
909
  });
913
910
  throw error;
914
911
  }
915
- }
916
- /**
917
- * Sleep for specified seconds
918
- * Step names must be unique within a workflow run
919
- */
920
- async sleep(stepName, seconds) {
912
+ };
913
+ const sleep = async (stepName, seconds) => {
921
914
  const checkResponse = await fetch(
922
- `${this.baseUrl}/v1/workflows/internal/step/check`,
915
+ `${BASE_URL}/internal/step/check`,
923
916
  {
924
917
  method: "POST",
925
918
  headers: {
926
919
  "Content-Type": "application/json",
927
- Authorization: this.authHeader,
928
- "X-QueueBear-Run-Token": this.runToken
920
+ Authorization: authHeader,
921
+ "X-QueueBear-Run-Token": runToken
929
922
  },
930
923
  body: JSON.stringify({
931
- runId: this.runId,
924
+ runId,
932
925
  stepName
933
926
  })
934
927
  }
935
928
  );
936
929
  const checkData = await checkResponse.json();
937
930
  if (checkData.cached) {
938
- this.stepIndex++;
931
+ stepIndex++;
939
932
  return;
940
933
  }
941
934
  const sleepResponse = await fetch(
942
- `${this.baseUrl}/v1/workflows/internal/sleep`,
935
+ `${BASE_URL}/internal/sleep`,
943
936
  {
944
937
  method: "POST",
945
938
  headers: {
946
939
  "Content-Type": "application/json",
947
- Authorization: this.authHeader,
948
- "X-QueueBear-Run-Token": this.runToken
940
+ Authorization: authHeader,
941
+ "X-QueueBear-Run-Token": runToken
949
942
  },
950
943
  body: JSON.stringify({
951
- runId: this.runId,
944
+ runId,
952
945
  stepName,
953
- stepIndex: this.stepIndex,
946
+ stepIndex,
954
947
  seconds
955
948
  })
956
949
  }
@@ -960,28 +953,21 @@ var WorkflowContext = class {
960
953
  throw new Error(errorData.error || "Failed to schedule sleep");
961
954
  }
962
955
  throw new WorkflowPausedError(`Sleeping for ${seconds}s`);
963
- }
964
- /**
965
- * Sleep until specific date
966
- */
967
- async sleepUntil(stepName, until) {
956
+ };
957
+ const sleepUntil = async (stepName, until) => {
968
958
  const seconds = Math.max(
969
959
  0,
970
960
  Math.ceil((until.getTime() - Date.now()) / 1e3)
971
961
  );
972
- return this.sleep(stepName, seconds);
973
- }
974
- /**
975
- * Get all completed steps for the current run
976
- * Useful for inspecting progress or debugging
977
- */
978
- async getCompletedSteps() {
962
+ return sleep(stepName, seconds);
963
+ };
964
+ const getCompletedSteps = async () => {
979
965
  const response = await fetch(
980
- `${this.baseUrl}/v1/workflows/internal/steps/${this.runId}`,
966
+ `${BASE_URL}/internal/steps/${runId}`,
981
967
  {
982
968
  headers: {
983
- Authorization: this.authHeader,
984
- "X-QueueBear-Run-Token": this.runToken
969
+ Authorization: authHeader,
970
+ "X-QueueBear-Run-Token": runToken
985
971
  }
986
972
  }
987
973
  );
@@ -990,12 +976,9 @@ var WorkflowContext = class {
990
976
  }
991
977
  const data = await response.json();
992
978
  return data.steps;
993
- }
994
- /**
995
- * Make HTTP call (executed as a step)
996
- */
997
- async call(stepName, config) {
998
- return this.run(stepName, async () => {
979
+ };
980
+ const call = async (stepName, config) => {
981
+ return run(stepName, async () => {
999
982
  const response = await fetch(config.url, {
1000
983
  method: config.method || "GET",
1001
984
  headers: {
@@ -1013,53 +996,47 @@ var WorkflowContext = class {
1013
996
  }
1014
997
  return response.text();
1015
998
  });
1016
- }
1017
- /**
1018
- * Wait for an external event
1019
- * @param stepName - Unique step name
1020
- * @param eventName - Name of the event to wait for
1021
- * @param options - Optional event key and timeout
1022
- */
1023
- async waitForEvent(stepName, eventName, options) {
999
+ };
1000
+ const waitForEvent = async (stepName, eventName, eventOptions) => {
1024
1001
  const checkResponse = await fetch(
1025
- `${this.baseUrl}/v1/workflows/internal/event/check`,
1002
+ `${BASE_URL}/internal/event/check`,
1026
1003
  {
1027
1004
  method: "POST",
1028
1005
  headers: {
1029
1006
  "Content-Type": "application/json",
1030
- Authorization: this.authHeader,
1031
- "X-QueueBear-Run-Token": this.runToken
1007
+ Authorization: authHeader,
1008
+ "X-QueueBear-Run-Token": runToken
1032
1009
  },
1033
1010
  body: JSON.stringify({
1034
- runId: this.runId,
1011
+ runId,
1035
1012
  stepName
1036
1013
  })
1037
1014
  }
1038
1015
  );
1039
1016
  const checkData = await checkResponse.json();
1040
1017
  if (checkData.received) {
1041
- this.stepIndex++;
1018
+ stepIndex++;
1042
1019
  return checkData.payload;
1043
1020
  }
1044
1021
  if (checkData.expired) {
1045
1022
  throw new Error(`Event "${eventName}" timed out`);
1046
1023
  }
1047
1024
  const waitResponse = await fetch(
1048
- `${this.baseUrl}/v1/workflows/internal/event/wait`,
1025
+ `${BASE_URL}/internal/event/wait`,
1049
1026
  {
1050
1027
  method: "POST",
1051
1028
  headers: {
1052
1029
  "Content-Type": "application/json",
1053
- Authorization: this.authHeader,
1054
- "X-QueueBear-Run-Token": this.runToken
1030
+ Authorization: authHeader,
1031
+ "X-QueueBear-Run-Token": runToken
1055
1032
  },
1056
1033
  body: JSON.stringify({
1057
- runId: this.runId,
1034
+ runId,
1058
1035
  stepName,
1059
- stepIndex: this.stepIndex,
1036
+ stepIndex,
1060
1037
  eventName,
1061
- eventKey: options?.eventKey,
1062
- timeoutSeconds: options?.timeoutSeconds
1038
+ eventKey: eventOptions?.eventKey,
1039
+ timeoutSeconds: eventOptions?.timeoutSeconds
1063
1040
  })
1064
1041
  }
1065
1042
  );
@@ -1068,20 +1045,15 @@ var WorkflowContext = class {
1068
1045
  throw new Error(errorData.error || "Failed to register event wait");
1069
1046
  }
1070
1047
  throw new WorkflowPausedError(`Waiting for event: ${eventName}`);
1071
- }
1072
- /**
1073
- * Send a fire-and-forget notification event
1074
- * @param eventName - Name of the event to send
1075
- * @param payload - Optional payload to include
1076
- */
1077
- async notify(eventName, payload) {
1048
+ };
1049
+ const notify = async (eventName, payload) => {
1078
1050
  const response = await fetch(
1079
- `${this.baseUrl}/v1/workflows/events/${encodeURIComponent(eventName)}`,
1051
+ `${BASE_URL}/events/${encodeURIComponent(eventName)}`,
1080
1052
  {
1081
1053
  method: "POST",
1082
1054
  headers: {
1083
1055
  "Content-Type": "application/json",
1084
- Authorization: this.authHeader
1056
+ Authorization: authHeader
1085
1057
  },
1086
1058
  body: JSON.stringify({ payload })
1087
1059
  }
@@ -1090,24 +1062,20 @@ var WorkflowContext = class {
1090
1062
  const errorData = await response.json();
1091
1063
  throw new Error(errorData.error || `Failed to send event: ${eventName}`);
1092
1064
  }
1093
- }
1094
- /**
1095
- * Execute multiple steps in parallel
1096
- * @param steps - Array of step definitions with name and function
1097
- */
1098
- async parallel(steps) {
1065
+ };
1066
+ const parallel = async (steps) => {
1099
1067
  const stepNames = steps.map((s) => s.name);
1100
1068
  const batchResponse = await fetch(
1101
- `${this.baseUrl}/v1/workflows/internal/steps/batch-check`,
1069
+ `${BASE_URL}/internal/steps/batch-check`,
1102
1070
  {
1103
1071
  method: "POST",
1104
1072
  headers: {
1105
1073
  "Content-Type": "application/json",
1106
- Authorization: this.authHeader,
1107
- "X-QueueBear-Run-Token": this.runToken
1074
+ Authorization: authHeader,
1075
+ "X-QueueBear-Run-Token": runToken
1108
1076
  },
1109
1077
  body: JSON.stringify({
1110
- runId: this.runId,
1078
+ runId,
1111
1079
  stepNames
1112
1080
  })
1113
1081
  }
@@ -1126,7 +1094,7 @@ var WorkflowContext = class {
1126
1094
  results[i] = cached.result;
1127
1095
  } else {
1128
1096
  pendingPromises.push(
1129
- this.run(step.name, step.fn).then((result) => {
1097
+ run(step.name, step.fn).then((result) => {
1130
1098
  results[i] = result;
1131
1099
  }).catch((error) => {
1132
1100
  errors.push({
@@ -1147,26 +1115,28 @@ var WorkflowContext = class {
1147
1115
  );
1148
1116
  }
1149
1117
  return results;
1150
- }
1151
- };
1152
- var ParallelExecutionError = class extends Error {
1153
- constructor(message, errors) {
1154
- super(message);
1155
- this.errors = errors;
1156
- this.name = "ParallelExecutionError";
1157
- }
1158
- get failedStepCount() {
1159
- return this.errors.length;
1160
- }
1161
- };
1118
+ };
1119
+ return {
1120
+ runId,
1121
+ input,
1122
+ run,
1123
+ sleep,
1124
+ sleepUntil,
1125
+ getCompletedSteps,
1126
+ call,
1127
+ waitForEvent,
1128
+ notify,
1129
+ parallel
1130
+ };
1131
+ }
1162
1132
 
1163
1133
  // src/serve.ts
1164
1134
  import { createHmac, timingSafeEqual } from "crypto";
1135
+ var BASE_URL2 = "https://api.queuebear.com/v1/workflows";
1165
1136
  function serve(handler, options) {
1166
1137
  return async (request) => {
1167
1138
  let runId;
1168
1139
  let runToken;
1169
- let baseUrl;
1170
1140
  try {
1171
1141
  if (options?.signingSecret) {
1172
1142
  const signature = request.headers.get("X-QueueBear-Signature");
@@ -1187,15 +1157,11 @@ function serve(handler, options) {
1187
1157
  headers: { "Content-Type": "application/json" }
1188
1158
  });
1189
1159
  }
1190
- baseUrl = options?.baseUrl || getBaseUrl(request);
1191
- const runResponse = await fetch(
1192
- `${baseUrl}/v1/workflows/runs/${runId}`,
1193
- {
1194
- headers: {
1195
- Authorization: request.headers.get("Authorization") || ""
1196
- }
1160
+ const runResponse = await fetch(`${BASE_URL2}/runs/${runId}`, {
1161
+ headers: {
1162
+ Authorization: request.headers.get("Authorization") || ""
1197
1163
  }
1198
- );
1164
+ });
1199
1165
  if (!runResponse.ok) {
1200
1166
  return new Response(
1201
1167
  JSON.stringify({ error: "Failed to fetch workflow run" }),
@@ -1206,15 +1172,14 @@ function serve(handler, options) {
1206
1172
  );
1207
1173
  }
1208
1174
  const runData = await runResponse.json();
1209
- const context = new WorkflowContext({
1175
+ const context = createWorkflowContext({
1210
1176
  runId,
1211
1177
  runToken,
1212
1178
  input: runData.input,
1213
- baseUrl,
1214
1179
  authHeader: request.headers.get("Authorization") || ""
1215
1180
  });
1216
1181
  const result = await handler(context);
1217
- await fetch(`${baseUrl}/v1/workflows/internal/complete`, {
1182
+ await fetch(`${BASE_URL2}/internal/complete`, {
1218
1183
  method: "POST",
1219
1184
  headers: {
1220
1185
  "Content-Type": "application/json",
@@ -1242,9 +1207,9 @@ function serve(handler, options) {
1242
1207
  );
1243
1208
  }
1244
1209
  console.error("[Workflow] Unhandled error - failing run:", error);
1245
- if (runId && runToken && baseUrl) {
1210
+ if (runId && runToken) {
1246
1211
  try {
1247
- await fetch(`${baseUrl}/v1/workflows/internal/fail`, {
1212
+ await fetch(`${BASE_URL2}/internal/fail`, {
1248
1213
  method: "POST",
1249
1214
  headers: {
1250
1215
  "Content-Type": "application/json",
@@ -1275,14 +1240,11 @@ function serve(handler, options) {
1275
1240
  }
1276
1241
  function verifySignature(body, signature, secret) {
1277
1242
  if (!signature) return false;
1278
- const parts = signature.split(",").reduce(
1279
- (acc, part) => {
1280
- const [key, value] = part.split("=");
1281
- acc[key] = value;
1282
- return acc;
1283
- },
1284
- {}
1285
- );
1243
+ const parts = signature.split(",").reduce((acc, part) => {
1244
+ const [key, value] = part.split("=");
1245
+ acc[key] = value;
1246
+ return acc;
1247
+ }, {});
1286
1248
  if (!parts.t || !parts.v1) return false;
1287
1249
  const timestamp = parseInt(parts.t, 10);
1288
1250
  const now = Math.floor(Date.now() / 1e3);
@@ -1295,10 +1257,6 @@ function verifySignature(body, signature, secret) {
1295
1257
  return false;
1296
1258
  }
1297
1259
  }
1298
- function getBaseUrl(request) {
1299
- const url = new URL(request.url);
1300
- return `${url.protocol}//${url.host}`;
1301
- }
1302
1260
  export {
1303
1261
  DLQAPI,
1304
1262
  MessagesAPI,
@@ -1306,8 +1264,8 @@ export {
1306
1264
  QueueBear,
1307
1265
  QueueBearError,
1308
1266
  SchedulesAPI,
1309
- WorkflowContext,
1310
1267
  WorkflowPausedError,
1311
1268
  WorkflowsAPI,
1269
+ createWorkflowContext,
1312
1270
  serve
1313
1271
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "queuebear",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "QueueBear SDK for message queues, scheduled jobs, and durable workflows",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",