@parsrun/queue 0.1.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/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # @parsrun/queue
2
+
3
+ Edge-compatible message queues for Pars with multiple adapter support.
4
+
5
+ ## Features
6
+
7
+ - **Multi-Adapter**: Memory, Cloudflare Queues, Upstash QStash
8
+ - **Edge-Compatible**: Works on all runtimes
9
+ - **Delayed Jobs**: Schedule jobs for later
10
+ - **Retries**: Automatic retry with backoff
11
+ - **Dead Letter Queue**: Failed job handling
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add @parsrun/queue
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { createQueue } from '@parsrun/queue';
23
+
24
+ const queue = createQueue({
25
+ adapter: 'memory', // or 'cloudflare', 'qstash'
26
+ });
27
+
28
+ // Publish message
29
+ await queue.publish('email:send', {
30
+ to: 'user@example.com',
31
+ subject: 'Hello',
32
+ });
33
+
34
+ // Subscribe to messages
35
+ queue.subscribe('email:send', async (message) => {
36
+ await sendEmail(message.data);
37
+ });
38
+ ```
39
+
40
+ ## API Overview
41
+
42
+ ### Adapters
43
+
44
+ #### Memory (Development)
45
+
46
+ ```typescript
47
+ import { createMemoryQueue } from '@parsrun/queue/adapters/memory';
48
+
49
+ const queue = createMemoryQueue({
50
+ concurrency: 5,
51
+ });
52
+ ```
53
+
54
+ #### Cloudflare Queues
55
+
56
+ ```typescript
57
+ import { createCloudflareQueue } from '@parsrun/queue/adapters/cloudflare';
58
+
59
+ // In Cloudflare Worker
60
+ const queue = createCloudflareQueue({
61
+ queue: env.MY_QUEUE,
62
+ });
63
+ ```
64
+
65
+ #### Upstash QStash
66
+
67
+ ```typescript
68
+ import { createQStashQueue } from '@parsrun/queue/adapters/qstash';
69
+
70
+ const queue = createQStashQueue({
71
+ token: process.env.QSTASH_TOKEN,
72
+ currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY,
73
+ nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY,
74
+ });
75
+ ```
76
+
77
+ ### Publishing Messages
78
+
79
+ ```typescript
80
+ // Simple publish
81
+ await queue.publish('topic', { data: 'value' });
82
+
83
+ // With options
84
+ await queue.publish('topic', data, {
85
+ delay: 60, // Delay in seconds
86
+ retries: 3, // Max retries
87
+ deduplicationId: 'unique-id',
88
+ });
89
+
90
+ // Batch publish
91
+ await queue.publishBatch('topic', [
92
+ { data: 'item1' },
93
+ { data: 'item2' },
94
+ ]);
95
+ ```
96
+
97
+ ### Subscribing to Messages
98
+
99
+ ```typescript
100
+ queue.subscribe('topic', async (message) => {
101
+ console.log(message.id, message.data);
102
+
103
+ // Throw to retry
104
+ if (shouldRetry) {
105
+ throw new Error('Retry later');
106
+ }
107
+ });
108
+
109
+ // With options
110
+ queue.subscribe('topic', handler, {
111
+ concurrency: 5,
112
+ maxRetries: 3,
113
+ retryDelay: 1000,
114
+ });
115
+ ```
116
+
117
+ ### Scheduled Jobs
118
+
119
+ ```typescript
120
+ // Schedule for specific time
121
+ await queue.schedule('cleanup', { type: 'daily' }, {
122
+ runAt: new Date('2024-01-01T00:00:00Z'),
123
+ });
124
+
125
+ // Cron-like scheduling (QStash)
126
+ await queue.schedule('reports', { type: 'weekly' }, {
127
+ cron: '0 9 * * 1', // Every Monday at 9am
128
+ });
129
+ ```
130
+
131
+ ### Dead Letter Queue
132
+
133
+ ```typescript
134
+ const queue = createQueue({
135
+ adapter: 'memory',
136
+ deadLetterQueue: {
137
+ enabled: true,
138
+ maxRetries: 5,
139
+ onDeadLetter: async (message) => {
140
+ // Handle permanently failed message
141
+ await notifyAdmin(message);
142
+ },
143
+ },
144
+ });
145
+ ```
146
+
147
+ ## Exports
148
+
149
+ ```typescript
150
+ import { ... } from '@parsrun/queue'; // Main exports
151
+ import { ... } from '@parsrun/queue/adapters/memory'; // Memory adapter
152
+ import { ... } from '@parsrun/queue/adapters/cloudflare'; // Cloudflare Queues
153
+ import { ... } from '@parsrun/queue/adapters/qstash'; // Upstash QStash
154
+ ```
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,94 @@
1
+ import { QueueAdapter, CloudflareQueueConfig, SendMessageOptions, BatchSendResult, CloudflareMessageBatch, QueueMessage, CloudflareMessage } from '../types.js';
2
+ import '@parsrun/types';
3
+
4
+ /**
5
+ * @parsrun/queue - Cloudflare Queues Adapter
6
+ * Adapter for Cloudflare Workers Queues
7
+ */
8
+
9
+ /**
10
+ * Cloudflare Queue Adapter
11
+ * Uses Cloudflare Workers Queues for serverless message processing
12
+ *
13
+ * Cloudflare Queues uses a push-based model where messages are delivered
14
+ * to queue consumers via Workers.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In your Worker
19
+ * export default {
20
+ * async fetch(request, env) {
21
+ * const queue = new CloudflareQueueAdapter({
22
+ * queue: env.MY_QUEUE,
23
+ * });
24
+ *
25
+ * await queue.send({ userId: '123', action: 'welcome-email' });
26
+ * return new Response('Queued');
27
+ * },
28
+ *
29
+ * async queue(batch, env) {
30
+ * // Process messages delivered by Cloudflare
31
+ * const processor = new CloudflareQueueProcessor();
32
+ * await processor.processBatch(batch, async (msg) => {
33
+ * console.log('Processing:', msg.body);
34
+ * });
35
+ * }
36
+ * }
37
+ * ```
38
+ */
39
+ declare class CloudflareQueueAdapter<T = unknown> implements QueueAdapter<T> {
40
+ readonly type: "cloudflare";
41
+ readonly name = "cloudflare-queue";
42
+ private queue;
43
+ constructor(config: CloudflareQueueConfig);
44
+ send(body: T, options?: SendMessageOptions): Promise<string>;
45
+ sendBatch(messages: Array<{
46
+ body: T;
47
+ options?: SendMessageOptions;
48
+ }>): Promise<BatchSendResult>;
49
+ }
50
+ /**
51
+ * Cloudflare Queue Processor
52
+ * Helper for processing queue batches in Workers queue handlers
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * export default {
57
+ * async queue(batch, env) {
58
+ * const processor = new CloudflareQueueProcessor<MyMessageType>();
59
+ * await processor.processBatch(batch, async (msg) => {
60
+ * // Process each message
61
+ * await handleMessage(msg.body);
62
+ * });
63
+ * }
64
+ * }
65
+ * ```
66
+ */
67
+ declare class CloudflareQueueProcessor<T = unknown> {
68
+ /**
69
+ * Process a batch of messages from Cloudflare Queues
70
+ */
71
+ processBatch(batch: CloudflareMessageBatch<T>, handler: (message: QueueMessage<T>) => void | Promise<void>, options?: {
72
+ /** Whether to ack all messages at once (default: false - ack individually) */
73
+ ackAll?: boolean;
74
+ /** Whether to retry all on any failure (default: false) */
75
+ retryAllOnFailure?: boolean;
76
+ }): Promise<{
77
+ processed: number;
78
+ failed: number;
79
+ }>;
80
+ /**
81
+ * Convert a Cloudflare message to QueueMessage format
82
+ */
83
+ toQueueMessage(msg: CloudflareMessage<T>): QueueMessage<T>;
84
+ }
85
+ /**
86
+ * Create a Cloudflare Queue adapter
87
+ */
88
+ declare function createCloudflareQueueAdapter<T = unknown>(config: CloudflareQueueConfig): CloudflareQueueAdapter<T>;
89
+ /**
90
+ * Create a Cloudflare Queue processor
91
+ */
92
+ declare function createCloudflareQueueProcessor<T = unknown>(): CloudflareQueueProcessor<T>;
93
+
94
+ export { CloudflareQueueAdapter, CloudflareQueueProcessor, createCloudflareQueueAdapter, createCloudflareQueueProcessor };
@@ -0,0 +1,141 @@
1
+ // src/types.ts
2
+ import {
3
+ type,
4
+ jobStatus,
5
+ job,
6
+ jobOptions,
7
+ addJobRequest,
8
+ jobProgressUpdate,
9
+ queueStats,
10
+ queueListOptions,
11
+ redisQueueConfig,
12
+ workerOptions,
13
+ queueConfig
14
+ } from "@parsrun/types";
15
+ var QueueError = class extends Error {
16
+ constructor(message, code, cause) {
17
+ super(message);
18
+ this.code = code;
19
+ this.cause = cause;
20
+ this.name = "QueueError";
21
+ }
22
+ };
23
+ var QueueErrorCodes = {
24
+ SEND_FAILED: "SEND_FAILED",
25
+ RECEIVE_FAILED: "RECEIVE_FAILED",
26
+ ACK_FAILED: "ACK_FAILED",
27
+ INVALID_CONFIG: "INVALID_CONFIG",
28
+ QUEUE_FULL: "QUEUE_FULL",
29
+ MESSAGE_NOT_FOUND: "MESSAGE_NOT_FOUND",
30
+ NOT_IMPLEMENTED: "NOT_IMPLEMENTED"
31
+ };
32
+
33
+ // src/adapters/cloudflare.ts
34
+ var CloudflareQueueAdapter = class {
35
+ type = "cloudflare";
36
+ name = "cloudflare-queue";
37
+ queue;
38
+ constructor(config) {
39
+ this.queue = config.queue;
40
+ }
41
+ async send(body, options) {
42
+ try {
43
+ await this.queue.send(body, {
44
+ delaySeconds: options?.delaySeconds
45
+ });
46
+ return `cf-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
47
+ } catch (err) {
48
+ throw new QueueError(
49
+ `Cloudflare Queue send failed: ${err instanceof Error ? err.message : "Unknown error"}`,
50
+ QueueErrorCodes.SEND_FAILED,
51
+ err
52
+ );
53
+ }
54
+ }
55
+ async sendBatch(messages) {
56
+ try {
57
+ const batchMessages = messages.map((m) => ({
58
+ body: m.body,
59
+ delaySeconds: m.options?.delaySeconds
60
+ }));
61
+ await this.queue.sendBatch(batchMessages);
62
+ const messageIds = messages.map(
63
+ () => `cf-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
64
+ );
65
+ return {
66
+ total: messages.length,
67
+ successful: messages.length,
68
+ failed: 0,
69
+ messageIds,
70
+ errors: []
71
+ };
72
+ } catch (err) {
73
+ throw new QueueError(
74
+ `Cloudflare Queue batch send failed: ${err instanceof Error ? err.message : "Unknown error"}`,
75
+ QueueErrorCodes.SEND_FAILED,
76
+ err
77
+ );
78
+ }
79
+ }
80
+ // Cloudflare Queues are push-based, so receive is not applicable
81
+ // Messages are delivered to queue handlers via Workers
82
+ };
83
+ var CloudflareQueueProcessor = class {
84
+ /**
85
+ * Process a batch of messages from Cloudflare Queues
86
+ */
87
+ async processBatch(batch, handler, options) {
88
+ let processed = 0;
89
+ let failed = 0;
90
+ for (const msg of batch.messages) {
91
+ try {
92
+ const queueMessage = {
93
+ id: msg.id,
94
+ body: msg.body,
95
+ timestamp: msg.timestamp,
96
+ attempts: msg.attempts
97
+ };
98
+ await handler(queueMessage);
99
+ if (!options?.ackAll) {
100
+ msg.ack();
101
+ }
102
+ processed++;
103
+ } catch (err) {
104
+ failed++;
105
+ if (options?.retryAllOnFailure) {
106
+ batch.retryAll();
107
+ return { processed, failed: batch.messages.length };
108
+ }
109
+ msg.retry();
110
+ }
111
+ }
112
+ if (options?.ackAll && failed === 0) {
113
+ batch.ackAll();
114
+ }
115
+ return { processed, failed };
116
+ }
117
+ /**
118
+ * Convert a Cloudflare message to QueueMessage format
119
+ */
120
+ toQueueMessage(msg) {
121
+ return {
122
+ id: msg.id,
123
+ body: msg.body,
124
+ timestamp: msg.timestamp,
125
+ attempts: msg.attempts
126
+ };
127
+ }
128
+ };
129
+ function createCloudflareQueueAdapter(config) {
130
+ return new CloudflareQueueAdapter(config);
131
+ }
132
+ function createCloudflareQueueProcessor() {
133
+ return new CloudflareQueueProcessor();
134
+ }
135
+ export {
136
+ CloudflareQueueAdapter,
137
+ CloudflareQueueProcessor,
138
+ createCloudflareQueueAdapter,
139
+ createCloudflareQueueProcessor
140
+ };
141
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/adapters/cloudflare.ts"],"sourcesContent":["/**\n * @parsrun/queue - Type Definitions\n * Queue types and interfaces\n */\n\n// Re-export types from @parsrun/types for convenience\nexport {\n type,\n jobStatus,\n job,\n jobOptions,\n addJobRequest,\n jobProgressUpdate,\n queueStats as parsQueueStats,\n queueListOptions,\n redisQueueConfig,\n workerOptions,\n queueConfig,\n type JobStatus,\n type Job,\n type JobOptions,\n type AddJobRequest,\n type JobProgressUpdate,\n type QueueStats as ParsQueueStats,\n type QueueListOptions,\n type RedisQueueConfig,\n type WorkerOptions,\n type QueueConfig,\n} from \"@parsrun/types\";\n\n/**\n * Queue adapter type\n */\nexport type QueueAdapterType = \"memory\" | \"cloudflare\" | \"qstash\";\n\n/**\n * Message payload\n */\nexport interface QueueMessage<T = unknown> {\n /** Unique message ID */\n id: string;\n /** Message payload */\n body: T;\n /** Message timestamp */\n timestamp: Date;\n /** Number of delivery attempts */\n attempts: number;\n /** Optional delay before processing (seconds) */\n delaySeconds?: number | undefined;\n /** Optional deduplication ID */\n deduplicationId?: string | undefined;\n /** Custom metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Send message options\n */\nexport interface SendMessageOptions {\n /** Delay before message is available (seconds) */\n delaySeconds?: number | undefined;\n /** Deduplication ID (prevents duplicate processing) */\n deduplicationId?: string | undefined;\n /** Custom metadata */\n metadata?: Record<string, unknown> | undefined;\n /** Priority (higher = more important) */\n priority?: number | undefined;\n}\n\n/**\n * Batch send result\n */\nexport interface BatchSendResult {\n /** Total messages sent */\n total: number;\n /** Successfully sent */\n successful: number;\n /** Failed to send */\n failed: number;\n /** Individual message IDs */\n messageIds: string[];\n /** Failed messages with errors */\n errors: Array<{ index: number; error: string }>;\n}\n\n/**\n * Message handler function\n */\nexport type MessageHandler<T = unknown> = (\n message: QueueMessage<T>\n) => void | Promise<void>;\n\n/**\n * Consumer options\n */\nexport interface ConsumerOptions {\n /** Maximum messages to process per batch */\n batchSize?: number | undefined;\n /** Visibility timeout (seconds) - how long a message is hidden while processing */\n visibilityTimeout?: number | undefined;\n /** Polling interval (ms) for pull-based queues */\n pollingInterval?: number | undefined;\n /** Maximum retries before dead-letter */\n maxRetries?: number | undefined;\n /** Concurrency - how many messages to process in parallel */\n concurrency?: number | undefined;\n}\n\n/**\n * Queue adapter interface\n */\nexport interface QueueAdapter<T = unknown> {\n /** Adapter type */\n readonly type: QueueAdapterType;\n\n /** Queue name */\n readonly name: string;\n\n /**\n * Send a message to the queue\n */\n send(body: T, options?: SendMessageOptions): Promise<string>;\n\n /**\n * Send multiple messages at once\n */\n sendBatch?(messages: Array<{ body: T; options?: SendMessageOptions }>): Promise<BatchSendResult>;\n\n /**\n * Receive messages from the queue (pull-based)\n * Used for manual message processing\n */\n receive?(maxMessages?: number, visibilityTimeout?: number): Promise<QueueMessage<T>[]>;\n\n /**\n * Acknowledge message processing (mark as complete)\n */\n ack?(messageId: string): Promise<void>;\n\n /**\n * Acknowledge multiple messages\n */\n ackBatch?(messageIds: string[]): Promise<void>;\n\n /**\n * Return message to queue (negative acknowledgement)\n * Optionally with delay\n */\n nack?(messageId: string, delaySeconds?: number): Promise<void>;\n\n /**\n * Start consuming messages (push-based)\n * For adapters that support push-based processing\n */\n consume?(handler: MessageHandler<T>, options?: ConsumerOptions): Promise<void>;\n\n /**\n * Stop consuming messages\n */\n stopConsuming?(): Promise<void>;\n\n /**\n * Get queue statistics\n */\n getStats?(): Promise<QueueStats>;\n\n /**\n * Purge all messages from queue\n */\n purge?(): Promise<void>;\n\n /**\n * Close/cleanup adapter resources\n */\n close?(): Promise<void>;\n}\n\n/**\n * Queue statistics\n */\nexport interface QueueStats {\n /** Approximate number of messages in queue */\n messageCount: number;\n /** Messages currently being processed */\n inFlightCount?: number | undefined;\n /** Messages in dead-letter queue */\n deadLetterCount?: number | undefined;\n}\n\n/**\n * Queue service configuration\n */\nexport interface QueueServiceConfig<T = unknown> {\n /** Queue adapter to use */\n adapter: QueueAdapter<T>;\n /** Enable debug logging */\n debug?: boolean | undefined;\n}\n\n/**\n * Memory queue configuration\n */\nexport interface MemoryQueueConfig {\n /** Queue name */\n name: string;\n /** Maximum queue size (default: unlimited) */\n maxSize?: number | undefined;\n /** Default visibility timeout (seconds) */\n visibilityTimeout?: number | undefined;\n}\n\n/**\n * Cloudflare Queue configuration\n */\nexport interface CloudflareQueueConfig {\n /** Queue binding from environment */\n queue: CloudflareQueue;\n}\n\n/**\n * Cloudflare Queue interface (from Workers runtime)\n */\nexport interface CloudflareQueue<T = unknown> {\n send(message: T, options?: { delaySeconds?: number | undefined; contentType?: string | undefined }): Promise<void>;\n sendBatch(messages: Array<{ body: T; delaySeconds?: number | undefined; contentType?: string | undefined }>): Promise<void>;\n}\n\n/**\n * Cloudflare Queue batch\n */\nexport interface CloudflareMessageBatch<T = unknown> {\n readonly queue: string;\n readonly messages: Array<CloudflareMessage<T>>;\n ackAll(): void;\n retryAll(): void;\n}\n\n/**\n * Cloudflare Queue message\n */\nexport interface CloudflareMessage<T = unknown> {\n readonly id: string;\n readonly timestamp: Date;\n readonly body: T;\n readonly attempts: number;\n ack(): void;\n retry(): void;\n}\n\n/**\n * QStash configuration\n */\nexport interface QStashConfig {\n /** QStash token */\n token: string;\n /** Destination URL for message delivery */\n destinationUrl: string;\n /** Current request URL (for signature verification) */\n currentSigningKey?: string | undefined;\n /** Next signing key (for signature verification) */\n nextSigningKey?: string | undefined;\n}\n\n/**\n * Queue error\n */\nexport class QueueError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"QueueError\";\n }\n}\n\n/**\n * Common queue error codes\n */\nexport const QueueErrorCodes = {\n SEND_FAILED: \"SEND_FAILED\",\n RECEIVE_FAILED: \"RECEIVE_FAILED\",\n ACK_FAILED: \"ACK_FAILED\",\n INVALID_CONFIG: \"INVALID_CONFIG\",\n QUEUE_FULL: \"QUEUE_FULL\",\n MESSAGE_NOT_FOUND: \"MESSAGE_NOT_FOUND\",\n NOT_IMPLEMENTED: \"NOT_IMPLEMENTED\",\n} as const;\n","/**\n * @parsrun/queue - Cloudflare Queues Adapter\n * Adapter for Cloudflare Workers Queues\n */\n\nimport type {\n BatchSendResult,\n CloudflareMessage,\n CloudflareMessageBatch,\n CloudflareQueue,\n CloudflareQueueConfig,\n QueueAdapter,\n QueueMessage,\n SendMessageOptions,\n} from \"../types.js\";\nimport { QueueError, QueueErrorCodes } from \"../types.js\";\n\n/**\n * Cloudflare Queue Adapter\n * Uses Cloudflare Workers Queues for serverless message processing\n *\n * Cloudflare Queues uses a push-based model where messages are delivered\n * to queue consumers via Workers.\n *\n * @example\n * ```typescript\n * // In your Worker\n * export default {\n * async fetch(request, env) {\n * const queue = new CloudflareQueueAdapter({\n * queue: env.MY_QUEUE,\n * });\n *\n * await queue.send({ userId: '123', action: 'welcome-email' });\n * return new Response('Queued');\n * },\n *\n * async queue(batch, env) {\n * // Process messages delivered by Cloudflare\n * const processor = new CloudflareQueueProcessor();\n * await processor.processBatch(batch, async (msg) => {\n * console.log('Processing:', msg.body);\n * });\n * }\n * }\n * ```\n */\nexport class CloudflareQueueAdapter<T = unknown> implements QueueAdapter<T> {\n readonly type = \"cloudflare\" as const;\n readonly name = \"cloudflare-queue\";\n\n private queue: CloudflareQueue<T>;\n\n constructor(config: CloudflareQueueConfig) {\n this.queue = config.queue as CloudflareQueue<T>;\n }\n\n async send(body: T, options?: SendMessageOptions): Promise<string> {\n try {\n await this.queue.send(body, {\n delaySeconds: options?.delaySeconds,\n });\n\n // Cloudflare Queues don't return message IDs on send\n // Generate a client-side ID for tracking\n return `cf-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n } catch (err) {\n throw new QueueError(\n `Cloudflare Queue send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n QueueErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n async sendBatch(\n messages: Array<{ body: T; options?: SendMessageOptions }>\n ): Promise<BatchSendResult> {\n try {\n const batchMessages = messages.map((m) => ({\n body: m.body,\n delaySeconds: m.options?.delaySeconds,\n }));\n\n await this.queue.sendBatch(batchMessages);\n\n // Generate client-side IDs\n const messageIds = messages.map(\n () => `cf-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n );\n\n return {\n total: messages.length,\n successful: messages.length,\n failed: 0,\n messageIds,\n errors: [],\n };\n } catch (err) {\n throw new QueueError(\n `Cloudflare Queue batch send failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n QueueErrorCodes.SEND_FAILED,\n err\n );\n }\n }\n\n // Cloudflare Queues are push-based, so receive is not applicable\n // Messages are delivered to queue handlers via Workers\n}\n\n/**\n * Cloudflare Queue Processor\n * Helper for processing queue batches in Workers queue handlers\n *\n * @example\n * ```typescript\n * export default {\n * async queue(batch, env) {\n * const processor = new CloudflareQueueProcessor<MyMessageType>();\n * await processor.processBatch(batch, async (msg) => {\n * // Process each message\n * await handleMessage(msg.body);\n * });\n * }\n * }\n * ```\n */\nexport class CloudflareQueueProcessor<T = unknown> {\n /**\n * Process a batch of messages from Cloudflare Queues\n */\n async processBatch(\n batch: CloudflareMessageBatch<T>,\n handler: (message: QueueMessage<T>) => void | Promise<void>,\n options?: {\n /** Whether to ack all messages at once (default: false - ack individually) */\n ackAll?: boolean;\n /** Whether to retry all on any failure (default: false) */\n retryAllOnFailure?: boolean;\n }\n ): Promise<{ processed: number; failed: number }> {\n let processed = 0;\n let failed = 0;\n\n for (const msg of batch.messages) {\n try {\n const queueMessage: QueueMessage<T> = {\n id: msg.id,\n body: msg.body,\n timestamp: msg.timestamp,\n attempts: msg.attempts,\n };\n\n await handler(queueMessage);\n\n if (!options?.ackAll) {\n msg.ack();\n }\n processed++;\n } catch (err) {\n failed++;\n\n if (options?.retryAllOnFailure) {\n batch.retryAll();\n return { processed, failed: batch.messages.length };\n }\n\n // Retry individual message\n msg.retry();\n }\n }\n\n if (options?.ackAll && failed === 0) {\n batch.ackAll();\n }\n\n return { processed, failed };\n }\n\n /**\n * Convert a Cloudflare message to QueueMessage format\n */\n toQueueMessage(msg: CloudflareMessage<T>): QueueMessage<T> {\n return {\n id: msg.id,\n body: msg.body,\n timestamp: msg.timestamp,\n attempts: msg.attempts,\n };\n }\n}\n\n/**\n * Create a Cloudflare Queue adapter\n */\nexport function createCloudflareQueueAdapter<T = unknown>(\n config: CloudflareQueueConfig\n): CloudflareQueueAdapter<T> {\n return new CloudflareQueueAdapter<T>(config);\n}\n\n/**\n * Create a Cloudflare Queue processor\n */\nexport function createCloudflareQueueProcessor<T = unknown>(): CloudflareQueueProcessor<T> {\n return new CloudflareQueueProcessor<T>();\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ export { MemoryQueueAdapter, createMemoryQueueAdapter } from './memory.js';
2
+ export { CloudflareQueueAdapter, CloudflareQueueProcessor, createCloudflareQueueAdapter, createCloudflareQueueProcessor } from './cloudflare.js';
3
+ export { QStashAdapter, QStashReceiver, createQStashAdapter, createQStashReceiver } from './qstash.js';
4
+ import '../types.js';
5
+ import '@parsrun/types';