queuebear 0.1.6 → 0.1.8
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.d.ts +59 -3
- package/dist/index.js +92 -8
- package/package.json +4 -5
package/dist/index.d.ts
CHANGED
|
@@ -468,6 +468,26 @@ interface PurgeDLQResponse {
|
|
|
468
468
|
purged: true;
|
|
469
469
|
count: number;
|
|
470
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Options for serveMessage() function
|
|
473
|
+
*/
|
|
474
|
+
interface ServeMessageOptions {
|
|
475
|
+
/** Signing secret for verifying message authenticity */
|
|
476
|
+
signingSecret?: string;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Context provided to message handlers by serveMessage()
|
|
480
|
+
*/
|
|
481
|
+
interface MessageContext<T> {
|
|
482
|
+
/** The parsed request body */
|
|
483
|
+
body: T;
|
|
484
|
+
/** QueueBear message ID (from X-QueueBear-Message-Id header) */
|
|
485
|
+
messageId?: string;
|
|
486
|
+
/** Current attempt number (0-indexed) */
|
|
487
|
+
attemptNumber: number;
|
|
488
|
+
/** All request headers */
|
|
489
|
+
headers: Record<string, string>;
|
|
490
|
+
}
|
|
471
491
|
|
|
472
492
|
/**
|
|
473
493
|
* Messages API client for publishing and managing webhook messages
|
|
@@ -495,12 +515,12 @@ declare class MessagesAPI extends BaseClient {
|
|
|
495
515
|
/**
|
|
496
516
|
* Get message details and delivery history
|
|
497
517
|
*
|
|
498
|
-
* @param messageId - The message ID (e.g., "
|
|
518
|
+
* @param messageId - The message ID (e.g., "abc123...")
|
|
499
519
|
* @returns Full message details including delivery logs
|
|
500
520
|
*
|
|
501
521
|
* @example
|
|
502
522
|
* ```typescript
|
|
503
|
-
* const message = await qb.messages.get("
|
|
523
|
+
* const message = await qb.messages.get("abc123");
|
|
504
524
|
* console.log(message.status); // "completed" | "pending" | "failed"
|
|
505
525
|
* console.log(message.deliveryLogs); // Delivery attempt history
|
|
506
526
|
* ```
|
|
@@ -1189,4 +1209,40 @@ type WorkflowHandler<TInput = unknown, TOutput = unknown> = (context: WorkflowCo
|
|
|
1189
1209
|
*/
|
|
1190
1210
|
declare function serve<TInput = unknown, TOutput = unknown>(handler: WorkflowHandler<TInput, TOutput>, options?: ServeOptions): (request: Request) => Promise<Response>;
|
|
1191
1211
|
|
|
1192
|
-
|
|
1212
|
+
/**
|
|
1213
|
+
* Message handler function type for serveMessage()
|
|
1214
|
+
*/
|
|
1215
|
+
type MessageHandler<TBody = unknown, TResult = unknown> = (context: MessageContext<TBody>) => Promise<TResult>;
|
|
1216
|
+
/**
|
|
1217
|
+
* Create a message webhook handler for simple message delivery.
|
|
1218
|
+
*
|
|
1219
|
+
* Unlike serve() which is designed for durable workflows with steps and state,
|
|
1220
|
+
* serveMessage() is for simple message webhooks published via messages.publish().
|
|
1221
|
+
*
|
|
1222
|
+
* @param handler - The handler function to process incoming messages
|
|
1223
|
+
* @param options - Optional configuration (e.g., signing secret for verification)
|
|
1224
|
+
* @returns A Request handler function for use with any HTTP framework
|
|
1225
|
+
*
|
|
1226
|
+
* @example
|
|
1227
|
+
* ```typescript
|
|
1228
|
+
* import { serveMessage } from 'queuebear';
|
|
1229
|
+
*
|
|
1230
|
+
* const handler = serveMessage<{ userId: string }>(
|
|
1231
|
+
* async (context) => {
|
|
1232
|
+
* console.log(`Processing message ${context.messageId} for user ${context.body.userId}`);
|
|
1233
|
+
* await processUser(context.body.userId);
|
|
1234
|
+
* return { success: true };
|
|
1235
|
+
* },
|
|
1236
|
+
* { signingSecret: process.env.QB_SIGNING_SECRET }
|
|
1237
|
+
* );
|
|
1238
|
+
*
|
|
1239
|
+
* // With Hono
|
|
1240
|
+
* app.post('/webhook', (c) => handler(c.req.raw));
|
|
1241
|
+
*
|
|
1242
|
+
* // With Express (using raw Request)
|
|
1243
|
+
* app.post('/webhook', (req, res) => handler(req).then(r => res.send(r)));
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
declare function serveMessage<TBody = unknown, TResult = unknown>(handler: MessageHandler<TBody, TResult>, options?: ServeMessageOptions): (request: Request) => Promise<Response>;
|
|
1247
|
+
|
|
1248
|
+
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 MessageContext, type MessageHandler, 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 ServeMessageOptions, 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, serveMessage };
|
package/dist/index.js
CHANGED
|
@@ -115,12 +115,12 @@ var MessagesAPI = class extends BaseClient {
|
|
|
115
115
|
/**
|
|
116
116
|
* Get message details and delivery history
|
|
117
117
|
*
|
|
118
|
-
* @param messageId - The message ID (e.g., "
|
|
118
|
+
* @param messageId - The message ID (e.g., "abc123...")
|
|
119
119
|
* @returns Full message details including delivery logs
|
|
120
120
|
*
|
|
121
121
|
* @example
|
|
122
122
|
* ```typescript
|
|
123
|
-
* const message = await qb.messages.get("
|
|
123
|
+
* const message = await qb.messages.get("abc123");
|
|
124
124
|
* console.log(message.status); // "completed" | "pending" | "failed"
|
|
125
125
|
* console.log(message.deliveryLogs); // Delivery attempt history
|
|
126
126
|
* ```
|
|
@@ -164,7 +164,10 @@ var MessagesAPI = class extends BaseClient {
|
|
|
164
164
|
* ```
|
|
165
165
|
*/
|
|
166
166
|
async cancel(messageId) {
|
|
167
|
-
return this.request(
|
|
167
|
+
return this.request(
|
|
168
|
+
"DELETE",
|
|
169
|
+
`/messages/${messageId}`
|
|
170
|
+
);
|
|
168
171
|
}
|
|
169
172
|
/**
|
|
170
173
|
* Publish a message and wait for delivery completion
|
|
@@ -186,9 +189,18 @@ var MessagesAPI = class extends BaseClient {
|
|
|
186
189
|
* ```
|
|
187
190
|
*/
|
|
188
191
|
async publishAndWait(destination, body, options) {
|
|
189
|
-
const {
|
|
192
|
+
const {
|
|
193
|
+
pollIntervalMs = 1e3,
|
|
194
|
+
timeoutMs = 6e4,
|
|
195
|
+
...publishOptions
|
|
196
|
+
} = options || {};
|
|
190
197
|
const { messageId } = await this.publish(destination, body, publishOptions);
|
|
191
|
-
const terminalStates = [
|
|
198
|
+
const terminalStates = [
|
|
199
|
+
"completed",
|
|
200
|
+
"failed",
|
|
201
|
+
"cancelled",
|
|
202
|
+
"dlq"
|
|
203
|
+
];
|
|
192
204
|
const startTime = Date.now();
|
|
193
205
|
while (Date.now() - startTime < timeoutMs) {
|
|
194
206
|
const message = await this.get(messageId);
|
|
@@ -197,7 +209,9 @@ var MessagesAPI = class extends BaseClient {
|
|
|
197
209
|
}
|
|
198
210
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
199
211
|
}
|
|
200
|
-
throw new Error(
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Message ${messageId} did not complete within ${timeoutMs}ms`
|
|
214
|
+
);
|
|
201
215
|
}
|
|
202
216
|
};
|
|
203
217
|
|
|
@@ -1149,7 +1163,7 @@ function serve(handler, options) {
|
|
|
1149
1163
|
}
|
|
1150
1164
|
}
|
|
1151
1165
|
const body = await request.json();
|
|
1152
|
-
runId = body.
|
|
1166
|
+
runId = body.id || request.headers.get("X-QueueBear-Workflow-Run") || "";
|
|
1153
1167
|
runToken = request.headers.get("X-QueueBear-Run-Token") || "";
|
|
1154
1168
|
if (!runId) {
|
|
1155
1169
|
return new Response(JSON.stringify({ error: "Missing runId" }), {
|
|
@@ -1257,6 +1271,75 @@ function verifySignature(body, signature, secret) {
|
|
|
1257
1271
|
return false;
|
|
1258
1272
|
}
|
|
1259
1273
|
}
|
|
1274
|
+
|
|
1275
|
+
// src/serve-message.ts
|
|
1276
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1277
|
+
function serveMessage(handler, options) {
|
|
1278
|
+
return async (request) => {
|
|
1279
|
+
try {
|
|
1280
|
+
if (options?.signingSecret) {
|
|
1281
|
+
const signature = request.headers.get("X-QueueBear-Signature");
|
|
1282
|
+
const bodyText = await request.clone().text();
|
|
1283
|
+
if (!verifySignature2(bodyText, signature, options.signingSecret)) {
|
|
1284
|
+
return new Response(JSON.stringify({ error: "Invalid signature" }), {
|
|
1285
|
+
status: 401,
|
|
1286
|
+
headers: { "Content-Type": "application/json" }
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const messageId = request.headers.get("X-QueueBear-Message-Id") || void 0;
|
|
1291
|
+
const attemptNumber = parseInt(
|
|
1292
|
+
request.headers.get("X-QueueBear-Retry-Count") || "0",
|
|
1293
|
+
10
|
|
1294
|
+
);
|
|
1295
|
+
const body = await request.json();
|
|
1296
|
+
const context = {
|
|
1297
|
+
body,
|
|
1298
|
+
messageId,
|
|
1299
|
+
attemptNumber,
|
|
1300
|
+
headers: Object.fromEntries(request.headers.entries())
|
|
1301
|
+
};
|
|
1302
|
+
const result = await handler(context);
|
|
1303
|
+
return new Response(JSON.stringify({ success: true, result }), {
|
|
1304
|
+
status: 200,
|
|
1305
|
+
headers: { "Content-Type": "application/json" }
|
|
1306
|
+
});
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
console.error("[serveMessage] Error:", error);
|
|
1309
|
+
return new Response(
|
|
1310
|
+
JSON.stringify({
|
|
1311
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1312
|
+
}),
|
|
1313
|
+
{
|
|
1314
|
+
status: 500,
|
|
1315
|
+
headers: { "Content-Type": "application/json" }
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
function verifySignature2(body, signature, secret) {
|
|
1322
|
+
if (!signature) return false;
|
|
1323
|
+
const parts = signature.split(",").reduce(
|
|
1324
|
+
(acc, part) => {
|
|
1325
|
+
const [key, value] = part.split("=");
|
|
1326
|
+
acc[key] = value;
|
|
1327
|
+
return acc;
|
|
1328
|
+
},
|
|
1329
|
+
{}
|
|
1330
|
+
);
|
|
1331
|
+
if (!parts.t || !parts.v1) return false;
|
|
1332
|
+
const timestamp = parseInt(parts.t, 10);
|
|
1333
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1334
|
+
if (Math.abs(now - timestamp) > 300) return false;
|
|
1335
|
+
const payload = `${parts.t}.${body}`;
|
|
1336
|
+
const expected = createHmac2("sha256", secret).update(payload).digest("hex");
|
|
1337
|
+
try {
|
|
1338
|
+
return timingSafeEqual2(Buffer.from(parts.v1), Buffer.from(expected));
|
|
1339
|
+
} catch {
|
|
1340
|
+
return false;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1260
1343
|
export {
|
|
1261
1344
|
DLQAPI,
|
|
1262
1345
|
MessagesAPI,
|
|
@@ -1267,5 +1350,6 @@ export {
|
|
|
1267
1350
|
WorkflowPausedError,
|
|
1268
1351
|
WorkflowsAPI,
|
|
1269
1352
|
createWorkflowContext,
|
|
1270
|
-
serve
|
|
1353
|
+
serve,
|
|
1354
|
+
serveMessage
|
|
1271
1355
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "queuebear",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "QueueBear SDK for message queues, scheduled jobs, and durable workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,13 +18,12 @@
|
|
|
18
18
|
"type-check": "tsc --noEmit",
|
|
19
19
|
"clean": "rm -rf dist"
|
|
20
20
|
},
|
|
21
|
-
"dependencies": {},
|
|
22
21
|
"devDependencies": {
|
|
23
22
|
"@types/node": "^22.10.2",
|
|
24
23
|
"tsup": "^8.3.5",
|
|
25
|
-
"typescript": "^5.7.2"
|
|
24
|
+
"typescript": "^5.7.2",
|
|
25
|
+
"vitest": "^4.0.16"
|
|
26
26
|
},
|
|
27
|
-
"peerDependencies": {},
|
|
28
27
|
"files": [
|
|
29
28
|
"dist"
|
|
30
29
|
],
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
"author": {
|
|
44
43
|
"name": "Robert Marshall",
|
|
45
44
|
"email": "hello@robertmarshall.dev",
|
|
46
|
-
"url": "https://
|
|
45
|
+
"url": "https://queuebear.com"
|
|
47
46
|
},
|
|
48
47
|
"license": "MIT",
|
|
49
48
|
"repository": {
|