@cloudnux/local-cloud-provider 0.20.0 → 0.21.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/dev-console-plugin/index.d.ts +6 -0
- package/dist/dev-console-plugin/index.js +1048 -16
- package/dist/dev-console-plugin/index.js.map +1 -1
- package/dist/index.js +192 -176
- package/dist/index.js.map +1 -1
- package/dist/queue-plugin/index.d.ts +7 -0
- package/dist/queue-plugin/index.js +37 -2
- package/dist/queue-plugin/index.js.map +1 -1
- package/dist/schedule-plugin/index.js +1 -1
- package/dist/schedule-plugin/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -117,10 +117,17 @@ interface QueueManager {
|
|
|
117
117
|
purged: number;
|
|
118
118
|
} | null>;
|
|
119
119
|
getMessageHistory: (queueName: string, limit?: number) => QueueMessageHistoryEntry[];
|
|
120
|
+
subscribeTopic: (topicName: string, queueName: string) => void;
|
|
121
|
+
publishTopic: (topicName: string, body: any, attributes: any) => Promise<{
|
|
122
|
+
id: string;
|
|
123
|
+
queueName: string;
|
|
124
|
+
}[] | null>;
|
|
125
|
+
listTopics: () => Record<string, string[]>;
|
|
120
126
|
}
|
|
121
127
|
interface QueueRuntime {
|
|
122
128
|
config: QueueConfig;
|
|
123
129
|
queues: Record<string, QueueService>;
|
|
130
|
+
topics: Record<string, string[]>;
|
|
124
131
|
dirtyQueues: Set<string>;
|
|
125
132
|
lastSavedAt: number;
|
|
126
133
|
messageHistory: QueueMessageHistoryEntry[];
|
|
@@ -13,7 +13,7 @@ var DEFAULT_CONFIG = {
|
|
|
13
13
|
tickIntervalMs: 50,
|
|
14
14
|
persistence: {
|
|
15
15
|
enabled: true,
|
|
16
|
-
directory: "./.develop
|
|
16
|
+
directory: "./.develop/.queue-data",
|
|
17
17
|
saveInterval: 6e4,
|
|
18
18
|
saveOnShutdown: false
|
|
19
19
|
}
|
|
@@ -495,6 +495,26 @@ var purgeDlq = async (runtime, queueName) => {
|
|
|
495
495
|
var getMessageHistory = (runtime, queueName, limit = 50) => {
|
|
496
496
|
return runtime.messageHistory.filter((entry) => entry.queueName === queueName).slice(-limit).reverse();
|
|
497
497
|
};
|
|
498
|
+
var subscribeTopic = (runtime, topicName, queueName) => {
|
|
499
|
+
const subscribers = runtime.topics[topicName] ?? (runtime.topics[topicName] = []);
|
|
500
|
+
if (!subscribers.includes(queueName)) {
|
|
501
|
+
subscribers.push(queueName);
|
|
502
|
+
}
|
|
503
|
+
runtime.logging.info({ topicName, queueName }, `Subscribed queue '${queueName}' to topic '${topicName}'`);
|
|
504
|
+
};
|
|
505
|
+
var publishTopic = async (runtime, topicName, body, attributes) => {
|
|
506
|
+
const subscribers = runtime.topics[topicName];
|
|
507
|
+
if (!subscribers || subscribers.length === 0) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const results = await Promise.all(
|
|
511
|
+
subscribers.map((queueName) => enqueueMessage(runtime, queueName, body, attributes))
|
|
512
|
+
);
|
|
513
|
+
return results.filter((result) => result !== null);
|
|
514
|
+
};
|
|
515
|
+
var listTopics = (runtime) => {
|
|
516
|
+
return Object.fromEntries(Object.entries(runtime.topics).map(([name, queues]) => [name, [...queues]]));
|
|
517
|
+
};
|
|
498
518
|
var createQueueManager = (runtime) => ({
|
|
499
519
|
addQueue: (queueName, handler, module) => addQueue(runtime, queueName, handler, module),
|
|
500
520
|
removeQueue: (queueName) => removeQueue(runtime, queueName),
|
|
@@ -508,7 +528,10 @@ var createQueueManager = (runtime) => ({
|
|
|
508
528
|
enqueueMessage: (queueName, body, attributes) => enqueueMessage(runtime, queueName, body, attributes),
|
|
509
529
|
processDlq: (queueName) => processDlq(runtime, queueName),
|
|
510
530
|
purgeDlq: (queueName) => purgeDlq(runtime, queueName),
|
|
511
|
-
getMessageHistory: (queueName, limit) => getMessageHistory(runtime, queueName, limit)
|
|
531
|
+
getMessageHistory: (queueName, limit) => getMessageHistory(runtime, queueName, limit),
|
|
532
|
+
subscribeTopic: (topicName, queueName) => subscribeTopic(runtime, topicName, queueName),
|
|
533
|
+
publishTopic: (topicName, body, attributes) => publishTopic(runtime, topicName, body, attributes),
|
|
534
|
+
listTopics: () => listTopics(runtime)
|
|
512
535
|
});
|
|
513
536
|
|
|
514
537
|
// src/queue-plugin/routes.ts
|
|
@@ -542,6 +565,17 @@ var registerQueueRoutes = (app, prefix = "") => {
|
|
|
542
565
|
return reply.status(200).send(result);
|
|
543
566
|
}
|
|
544
567
|
);
|
|
568
|
+
app.post(
|
|
569
|
+
`/${prefix}/topics/:topic`,
|
|
570
|
+
async (request, reply) => {
|
|
571
|
+
const topicName = request.params.topic;
|
|
572
|
+
const results = await app.queues.publishTopic(topicName, request.body, request.headers);
|
|
573
|
+
if (results === null) {
|
|
574
|
+
return reply.status(404).send({ error: "Topic not found" });
|
|
575
|
+
}
|
|
576
|
+
return reply.status(200).send({ topicName, results });
|
|
577
|
+
}
|
|
578
|
+
);
|
|
545
579
|
app.get(
|
|
546
580
|
`/${prefix}/:queue/process-dlq`,
|
|
547
581
|
async (request, reply) => {
|
|
@@ -571,6 +605,7 @@ var queuesPlugin = fsPlugin(async (app, options) => {
|
|
|
571
605
|
const runtime = {
|
|
572
606
|
config: mergeConfig(DEFAULT_CONFIG, options.config),
|
|
573
607
|
queues: {},
|
|
608
|
+
topics: {},
|
|
574
609
|
dirtyQueues: /* @__PURE__ */ new Set(),
|
|
575
610
|
lastSavedAt: Date.now(),
|
|
576
611
|
messageHistory: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/queue-plugin/plugin.ts","../../src/queue-plugin/core.ts","../../src/queue-plugin/persistence.ts","../../src/queue-plugin/processing.ts","../../src/queue-plugin/decorator.ts","../../src/queue-plugin/routes.ts"],"sourcesContent":["import { FastifyInstance, FastifyPluginAsync } from \"fastify\";\nimport fsPlugin from \"fastify-plugin\";\n\nimport { QueuePluginOptions, QueueRuntime } from \"./types\";\nimport { DEFAULT_CONFIG, mergeConfig } from \"./core\";\nimport { tick } from \"./processing\";\nimport { initializePersistence } from \"./persistence\";\nimport { createQueueManager } from \"./decorator\";\nimport { registerQueueRoutes } from \"./routes\";\n\nexport const queuesPlugin: FastifyPluginAsync<QueuePluginOptions> =\n fsPlugin(async (\n app: FastifyInstance,\n options: QueuePluginOptions\n ) => {\n const runtime: QueueRuntime = {\n config: mergeConfig(DEFAULT_CONFIG, options.config),\n queues: {},\n dirtyQueues: new Set<string>(),\n lastSavedAt: Date.now(),\n messageHistory: [],\n logging: app.logging,\n };\n\n // One timer for the entire plugin: every tickIntervalMs, check every\n // queue for batches/retries that are due, and save persistence if\n // its interval has elapsed. See processing.ts:tick.\n const tickIntervalId = setInterval(() => tick(runtime), runtime.config.tickIntervalMs);\n app.addHook('onClose', async () => clearInterval(tickIntervalId));\n\n app.decorate('queues', createQueueManager(runtime));\n registerQueueRoutes(app, options.prefix);\n\n await initializePersistence(runtime);\n });","import { EventHandler, QueueConfig, QueueMessage, QueueMessageHistoryEntry, QueueService, QueueSummary } from \"./types\";\n\nconst MAX_MESSAGE_HISTORY = 500;\n\n// default config\nexport const DEFAULT_CONFIG: QueueConfig = {\n batchSize: 10,\n batchWindowMs: 500,\n maxRetries: 3,\n parallel: true,\n maxConcurrent: 5,\n retryBackoff: true,\n tickIntervalMs: 50,\n persistence: {\n enabled: true,\n directory: './.develop/queue-data',\n saveInterval: 60000,\n saveOnShutdown: false\n }\n};\n\n// Pure function to merge configs\nexport const mergeConfig = (defaultConfig: QueueConfig, userConfig?: Partial<QueueConfig>): QueueConfig => ({\n ...defaultConfig,\n ...userConfig,\n persistence: {\n ...defaultConfig.persistence,\n ...(userConfig?.persistence || {})\n }\n});\n\n// Pure function to create a new queue service\nexport const createQueueService = (handler: EventHandler, module?: string): QueueService => ({\n handler,\n incoming: [],\n processing: [],\n dlq: [],\n processingBatch: false,\n module\n});\n\n// Pure function to create a queue message\nexport const createQueueMessage = (body: any, headers: Record<string, any>): QueueMessage => {\n const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);\n const message: QueueMessage = {\n id,\n timestamp: new Date(),\n attempts: 0,\n payload: body,\n attributes: headers,\n };\n\n const delaySeconds = Number(headers?.['x-delay-seconds']);\n if (delaySeconds > 0) {\n message.nextAttempt = new Date(Date.now() + delaySeconds * 1000);\n }\n\n return message;\n};\n\n// Pure function to schedule a retry for a message\n// Puts a failed message back into `incoming` to wait out its backoff delay,\n// same as any delayed message - so the next batch dispatch picks it up\n// mixed in with whatever else is ready, instead of retrying it alone.\nexport const scheduleRetry = (queueService: QueueService, message: QueueMessage, delayMs: number): void => {\n removeFromProcessing(queueService, message.id);\n message.attempts += 1;\n message.nextAttempt = new Date(Date.now() + delayMs);\n queueService.incoming.push(message);\n};\n\n\n// Pure function to check whether a message is ready to be processed\nexport const isMessageReady = (message: QueueMessage, now: number = Date.now()): boolean => {\n return !message.nextAttempt || message.nextAttempt.getTime() <= now;\n};\n\n// Pure function to move ready messages from incoming to processing, leaving delayed messages in place\nexport const moveToProcessing = (queueService: QueueService, batchSize: number): QueueMessage[] => {\n const now = Date.now();\n const ready: QueueMessage[] = [];\n const remaining: QueueMessage[] = [];\n\n for (const message of queueService.incoming) {\n if (ready.length < batchSize && isMessageReady(message, now)) {\n ready.push(message);\n } else {\n remaining.push(message);\n }\n }\n\n queueService.incoming = remaining;\n return ready;\n};\n\n// Pure function to remove message from processing queue\nexport const removeFromProcessing = (queueService: QueueService, messageId: string): void => {\n queueService.processing = queueService.processing.filter(msg => msg.id !== messageId);\n};\n\n// Pure function to move message to DLQ\nexport const moveToDLQ = (queueService: QueueService, message: QueueMessage, error: string): void => {\n queueService.dlq.push({\n ...message,\n error,\n failedAt: new Date()\n });\n removeFromProcessing(queueService, message.id);\n};\n\n// Pure function to calculate backoff delay\nexport const calculateBackoffDelay = (attempts: number): number => {\n return Math.pow(2, attempts) * 100;\n};\n\n// Pure function to create queue summary\nexport const createQueueSummary = (queueService: QueueService, config: QueueConfig): QueueSummary => ({\n incoming: queueService.incoming.length,\n processing: queueService.processing.length,\n dlq: queueService.dlq.length,\n isProcessing: queueService.processingBatch,\n configuration: {\n batchSize: config.batchSize,\n batchWindowMs: config.batchWindowMs,\n parallel: config.parallel,\n maxConcurrent: config.maxConcurrent\n }\n});\n\n// Pure function to create dashboard summary\nexport const createDashboardSummary = (queues: Record<string, QueueService>, config: QueueConfig): Record<string, QueueSummary> => {\n return Object.entries(queues).reduce((summary, [queueName, queue]) => {\n summary[queueName] = createQueueSummary(queue, config);\n return summary;\n }, {} as Record<string, QueueSummary>);\n};\n\n// Pure function to move DLQ messages back to incoming\nexport const moveDLQToIncoming = (queueService: QueueService): number => {\n const dlqCount = queueService.dlq.length;\n\n if (dlqCount === 0) return 0;\n\n const movedMessages = queueService.dlq.map(message => ({\n ...message,\n attempts: 0,\n error: undefined,\n failedAt: undefined,\n reprocessed: true,\n originalId: message.id,\n id: Date.now().toString() + Math.random().toString(36).substring(2, 7)\n }));\n\n queueService.incoming.push(...movedMessages);\n queueService.dlq = [];\n\n return dlqCount;\n};\n\n// Pure function to purge DLQ\nexport const purgeDLQ = (queueService: QueueService): number => {\n const dlqCount = queueService.dlq.length;\n queueService.dlq = [];\n return dlqCount;\n};\n\n// Pure function to build a message-history record for a completed or\n// terminally-failed message. This is what powers the dev console's\n// per-queue history view for messages that already left incoming/processing/dlq.\nexport const createHistoryEntry = (\n queueName: string,\n message: QueueMessage,\n status: 'completed' | 'failed',\n error?: string\n): QueueMessageHistoryEntry => ({\n id: message.id,\n queueName,\n status,\n timestamp: message.timestamp,\n completedAt: new Date(),\n attempts: message.attempts,\n payload: message.payload,\n attributes: message.attributes,\n error,\n});\n\n// Appends to the bounded history log in place, mirroring schedule-plugin's\n// executionHistory cap so long-running processes don't grow this unbounded.\nexport const recordMessageHistory = (\n history: QueueMessageHistoryEntry[],\n entry: QueueMessageHistoryEntry\n): void => {\n history.push(entry);\n if (history.length > MAX_MESSAGE_HISTORY) {\n history.splice(0, history.length - MAX_MESSAGE_HISTORY);\n }\n};","import * as fs from \"fs/promises\";\nimport * as path from \"path\";\n\nimport { QueueService, QueueRuntime, QueueMessage } from \"./types\";\n\nconst createQueueData = (queueService: QueueService) => ({\n incoming: queueService.incoming,\n processing: queueService.processing,\n dlq: queueService.dlq,\n savedAt: new Date()\n});\n\nconst restoreDates = (messages: QueueMessage[]): void => {\n messages.forEach((msg: QueueMessage) => {\n msg.timestamp = new Date(msg.timestamp);\n if (msg.nextAttempt) msg.nextAttempt = new Date(msg.nextAttempt);\n if (msg.failedAt) msg.failedAt = new Date(msg.failedAt);\n });\n};\n\nexport const saveQueueState = async (runtime: QueueRuntime, queueName: string, queueService: QueueService): Promise<void> => {\n try {\n const queueData = createQueueData(queueService);\n const now = Date.now();\n const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);\n const tempFilePath = path.join(runtime.config.persistence.directory, `${queueName}.${now}temp.json`);\n\n // Write to a temp file first, then rename - rename is atomic, so a\n // reader never sees a half-written file.\n await fs.writeFile(tempFilePath, JSON.stringify(queueData, null, 2), 'utf8');\n await fs.rename(tempFilePath, queueFilePath);\n\n runtime.logging.debug({ module: queueService.module }, `Queue state saved: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error(\n { module: queueService.module, error: error?.message },\n `Failed to save queue state for ${queueName}`\n );\n }\n};\n\nexport const saveAllQueueStates = async (runtime: QueueRuntime): Promise<void> => {\n try {\n for (const [queueName, queueService] of Object.entries(runtime.queues)) {\n await saveQueueState(runtime, queueName, queueService);\n }\n runtime.logging.debug(`All queue states saved to ${runtime.config.persistence.directory}`);\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to save all queue states`);\n }\n};\n\n// Only saves queues that changed since the last save (tracked via\n// runtime.dirtyQueues). Called from processing.ts's tick once\n// persistence.saveInterval has elapsed.\nexport const saveDirtyQueueStates = async (runtime: QueueRuntime): Promise<void> => {\n if (runtime.dirtyQueues.size === 0) return;\n\n try {\n const queueNames = [...runtime.dirtyQueues];\n runtime.dirtyQueues.clear();\n\n for (const queueName of queueNames) {\n if (runtime.queues[queueName]) {\n await saveQueueState(runtime, queueName, runtime.queues[queueName]);\n }\n }\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to save dirty queue states`);\n }\n};\n\n// Called from addQueue, right after a queue is registered, to restore\n// whatever was on disk for it from a previous run.\nexport const loadQueueState = async (runtime: QueueRuntime, queueName: string): Promise<void> => {\n try {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n runtime.logging.warn(`Skipping load for non-existent queue: ${queueName}`);\n return;\n }\n\n const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);\n\n try {\n const data = await fs.readFile(queueFilePath, 'utf8');\n const queueData = JSON.parse(data);\n\n restoreDates(queueData.incoming);\n restoreDates(queueData.processing);\n restoreDates(queueData.dlq);\n\n queueService.incoming = queueData.incoming;\n queueService.dlq = queueData.dlq;\n queueService.processing = [];\n\n // Nothing is actually in flight right after a restart - anything\n // that was mid-handling goes back into the ready pool like any\n // other message (the tick's next batch picks it up), or straight\n // to the DLQ if it had already used up its retries before the\n // shutdown.\n for (const message of queueData.processing as QueueMessage[]) {\n if (message.attempts >= runtime.config.maxRetries) {\n queueService.dlq.push({ ...message, error: 'max retries exceeded before restart', failedAt: new Date() });\n } else {\n message.nextAttempt = new Date();\n queueService.incoming.push(message);\n }\n }\n\n runtime.logging.debug(\n { module: queueService.module },\n `Queue state loaded: ${queueName} (saved at ${queueData.savedAt})`\n );\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n runtime.logging.debug({ module: queueService.module }, `No saved state found for queue: ${queueName}`);\n } else {\n throw error;\n }\n }\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to load queue state for ${queueName}`);\n }\n};\n\nexport const initializePersistence = async (runtime: QueueRuntime): Promise<void> => {\n try {\n await fs.mkdir(runtime.config.persistence.directory, { recursive: true });\n\n if (runtime.config.persistence.saveOnShutdown) {\n const shutdownHandler = async () => {\n runtime.logging.debug('Saving queue state before shutdown...');\n await saveAllQueueStates(runtime);\n process.exit(0);\n };\n\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n }\n\n runtime.logging.debug(`Queue persistence initialized: ${runtime.config.persistence.directory}`);\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to initialize queue persistence`);\n }\n};","import { QueueMessage, QueueService, QueueRuntime } from \"./types\";\nimport {\n isMessageReady,\n moveToProcessing,\n removeFromProcessing,\n moveToDLQ,\n scheduleRetry,\n calculateBackoffDelay,\n createHistoryEntry,\n recordMessageHistory,\n} from \"./core\";\nimport { saveDirtyQueueStates } from \"./persistence\";\n\n// Sends one message through the user's handler. Success removes it from\n// `processing`; failure either schedules a retry or moves it to the DLQ.\n// Used for both freshly-batched messages and individually-retried ones.\n// Wrapped in runWithLogContext by processBatch, so every log call below\n// (and anything the user's handler itself logs) carries this message's\n// module/reqId without having to pass them through explicitly.\nconst processMessage = async (\n runtime: QueueRuntime,\n queueName: string,\n message: QueueMessage,\n queueService: QueueService\n): Promise<void> => {\n try {\n const result = await queueService.handler(message);\n if (result?.failureId) {\n handleFailure(runtime, queueName, message, queueService);\n } else {\n removeFromProcessing(queueService, message.id);\n recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, 'completed'));\n runtime.logging.debug(`Successfully processed message ${message.id} in queue ${queueName}`);\n }\n } catch {\n handleFailure(runtime, queueName, message, queueService);\n }\n};\n\nconst handleFailure = (\n runtime: QueueRuntime,\n queueName: string,\n message: QueueMessage,\n queueService: QueueService\n): void => {\n runtime.logging.error(`Error processing message ${message.id} in queue ${queueName}: handler returned failure`);\n\n if (message.attempts >= runtime.config.maxRetries) {\n moveToDLQ(queueService, message, 'max retries exceeded');\n recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, 'failed', 'max retries exceeded'));\n return;\n }\n\n const delayMs = runtime.config.retryBackoff ? calculateBackoffDelay(message.attempts + 1) : 0;\n scheduleRetry(queueService, message, delayMs);\n runtime.logging.debug(`Scheduling retry for message ${message.id} in ${delayMs}ms`);\n};\n\n// Pulls up to batchSize ready messages out of `incoming` and runs them\n// through processMessage concurrently. Guarded by `processingBatch` so the\n// tick never starts a second batch for a queue while one is still running.\nconst processBatch = async (\n runtime: QueueRuntime,\n queueName: string,\n queueService: QueueService\n): Promise<void> => {\n if (queueService.processingBatch) {\n return;\n }\n\n try {\n queueService.processingBatch = true;\n\n const messagesToProcess = moveToProcessing(queueService, runtime.config.batchSize);\n if (messagesToProcess.length === 0) {\n return;\n }\n\n queueService.processing.push(...messagesToProcess);\n\n await Promise.all(\n messagesToProcess.map(message => runtime.logging.runWithLogContext(\n { module: queueService.module, reqId: message.id },\n () => processMessage(runtime, queueName, message, queueService)\n ))\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n } finally {\n queueService.processingBatch = false;\n }\n};\n\nconst dispatchBatchIfDue = (\n runtime: QueueRuntime,\n queueName: string,\n queueService: QueueService,\n now: number\n): void => {\n if (queueService.processingBatch) {\n return;\n }\n const ready = queueService.incoming.filter(message => isMessageReady(message, now));\n if (ready.length === 0) {\n return;\n }\n\n const dueBySize = ready.length >= runtime.config.batchSize;\n const oldestReadyAge = now - Math.min(...ready.map(message => message.timestamp.getTime()));\n const dueByWindow = oldestReadyAge >= runtime.config.batchWindowMs;\n\n if (dueBySize || dueByWindow) {\n processBatch(runtime, queueName, queueService);\n }\n};\n\nconst maybeSavePersistence = (runtime: QueueRuntime, now: number): void => {\n if (!runtime.config.persistence.enabled) {\n return;\n }\n\n const saveInterval = runtime.config.persistence.saveInterval ?? 0;\n if (saveInterval > 0 && now - runtime.lastSavedAt >= saveInterval) {\n runtime.lastSavedAt = now;\n saveDirtyQueueStates(runtime);\n }\n};\n\n\n// The one timer for the whole plugin. Every tickIntervalMs: for every queue,\n// dispatch a batch if one is due (this is also how retried messages get\n// picked up - they wait in `incoming` like any delayed message); then save\n// persistence if its interval has elapsed.\nexport const tick = (runtime: QueueRuntime): void => {\n const now = Date.now();\n for (const [queueName, queueService] of Object.entries(runtime.queues)) {\n dispatchBatchIfDue(runtime, queueName, queueService, now);\n }\n maybeSavePersistence(runtime, now);\n};","import { QueueService, QueueManager, QueueRuntime, EventHandler } from \"./types\";\nimport {\n createQueueService,\n createQueueMessage,\n createDashboardSummary,\n createQueueSummary,\n moveDLQToIncoming,\n purgeDLQ,\n} from \"./core\";\nimport { loadQueueState, saveQueueState } from \"./persistence\";\n\nconst isValidQueueName = (queueName: string): boolean => {\n return typeof queueName === 'string' && queueName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(queueName);\n};\n\nconst validateQueueName = (queueName: string): void => {\n if (!queueName || typeof queueName !== 'string') {\n throw new Error('Queue name must be a non-empty string');\n }\n\n if (!isValidQueueName(queueName)) {\n throw new Error('Queue name must contain only alphanumeric characters, hyphens, and underscores');\n }\n};\n\nconst validateHandler = (handler: any): void => {\n if (typeof handler !== 'function') {\n throw new Error('Handler must be a function');\n }\n};\n\nexport const addQueue = async (runtime: QueueRuntime, queueName: string, handler: EventHandler, module?: string): Promise<void> => {\n try {\n validateQueueName(queueName);\n validateHandler(handler);\n\n if (runtime.queues[queueName]) {\n runtime.logging.warn({ module }, `Queue already exists: ${queueName}.`);\n return;\n }\n\n runtime.queues[queueName] = createQueueService(handler, module);\n\n if (runtime.config.persistence.enabled) {\n await loadQueueState(runtime, queueName);\n }\n\n runtime.logging.info({ module }, `Queue added: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error({ module, error: error.message }, `Failed to add queue ${queueName}`);\n throw error;\n }\n};\n\nexport const removeQueue = async (runtime: QueueRuntime, queueName: string): Promise<void> => {\n try {\n validateQueueName(queueName);\n\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n throw new Error(`Queue '${queueName}' does not exist`);\n }\n\n const totalMessages = queueService.incoming.length + queueService.processing.length;\n if (totalMessages > 0) {\n runtime.logging.warn(\n { module: queueService.module },\n `Removing queue with ${totalMessages} pending messages: ${queueName}`\n );\n }\n\n if (runtime.config.persistence.enabled) {\n await saveQueueState(runtime, queueName, queueService);\n }\n\n delete runtime.queues[queueName];\n\n runtime.logging.info({ module: queueService.module }, `Queue removed: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error({ error: error.message }, `Failed to remove queue ${queueName}`);\n throw error;\n }\n};\n\nexport const hasQueue = (runtime: QueueRuntime, queueName: string): boolean => {\n try {\n validateQueueName(queueName);\n return queueName in runtime.queues;\n } catch {\n return false;\n }\n};\n\nexport const listQueues = (runtime: QueueRuntime, module?: string): string[] => {\n return Object.keys(runtime.queues)\n .filter(queueName => !module || runtime.queues[queueName].module === module)\n .sort();\n};\n\nexport const getQueueStats = (runtime: QueueRuntime, queueName: string): QueueService | null => {\n try {\n validateQueueName(queueName);\n return runtime.queues[queueName] || null;\n } catch {\n return null;\n }\n};\n\nexport const getQueuesMap = (runtime: QueueRuntime): Record<string, QueueService> => {\n // Return a copy to prevent external modification\n return { ...runtime.queues };\n};\n\nexport const getDashboardSummary = (runtime: QueueRuntime) =>\n createDashboardSummary(runtime.queues, runtime.config);\n\nexport const getQueueSummary = (runtime: QueueRuntime, queueName: string) => {\n const queue = runtime.queues[queueName];\n if (!queue) {\n return null;\n }\n\n return {\n stats: createQueueSummary(queue, runtime.config),\n messages: {\n incoming: queue.incoming,\n processing: queue.processing,\n dlq: queue.dlq\n }\n };\n};\n\nexport const enqueueMessage = async (runtime: QueueRuntime, queueName: string, body: any, attributes: any) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const message = createQueueMessage(body, attributes);\n queueService.incoming.push(message);\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return { id: message.id, queueName };\n};\n\nexport const processDlq = async (runtime: QueueRuntime, queueName: string) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const processedCount = moveDLQToIncoming(queueService);\n if (processedCount === 0) {\n return { status: \"success\", message: \"No messages in DLQ to process\", processed: 0 };\n }\n\n runtime.logging.warn(\n { module: queueService.module },\n `Moving ${processedCount} messages from DLQ for ${queueName}`\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return {\n status: \"success\",\n message: `Moved ${processedCount} messages from DLQ to processing queue`,\n processed: processedCount\n };\n};\n\nexport const purgeDlq = async (runtime: QueueRuntime, queueName: string) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const purgedCount = purgeDLQ(queueService);\n if (purgedCount === 0) {\n return { status: \"success\", message: \"No messages in DLQ to purge\", purged: 0 };\n }\n\n runtime.logging.warn(\n { module: queueService.module },\n `Purging ${purgedCount} messages from DLQ for ${queueName}`\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return {\n status: \"success\",\n message: `Purged ${purgedCount} messages from DLQ`,\n purged: purgedCount\n };\n};\n\nexport const getMessageHistory = (runtime: QueueRuntime, queueName: string, limit = 50) => {\n return runtime.messageHistory\n .filter(entry => entry.queueName === queueName)\n .slice(-limit)\n .reverse();\n};\n\n// The one place runtime gets bound into the public app.queues shape -\n// Fastify needs a plain object of methods with no runtime param, so this\n// wraps each plain function above with `runtime` pre-applied. Everything\n// above this line is a normal function you can call directly with an\n// explicit runtime, e.g. in tests.\nexport const createQueueManager = (runtime: QueueRuntime): QueueManager => ({\n addQueue: (queueName, handler, module) => addQueue(runtime, queueName, handler, module),\n removeQueue: (queueName) => removeQueue(runtime, queueName),\n hasQueue: (queueName) => hasQueue(runtime, queueName),\n listQueues: (module) => listQueues(runtime, module),\n getQueueStats: (queueName) => getQueueStats(runtime, queueName),\n getQueuesMap: () => getQueuesMap(runtime),\n getConfig: () => runtime.config,\n getDashboardSummary: () => getDashboardSummary(runtime),\n getQueueSummary: (queueName) => getQueueSummary(runtime, queueName),\n enqueueMessage: (queueName, body, attributes) => enqueueMessage(runtime, queueName, body, attributes),\n processDlq: (queueName) => processDlq(runtime, queueName),\n purgeDlq: (queueName) => purgeDlq(runtime, queueName),\n getMessageHistory: (queueName, limit) => getMessageHistory(runtime, queueName, limit),\n});","import { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\n\n// Function to register all routes\nexport const registerQueueRoutes = (\n app: FastifyInstance,\n prefix: string = \"\"\n): void => {\n app.get(`/${prefix}/dashboard`,\n async (_: FastifyRequest, reply: FastifyReply) => {\n const summary = app.queues.getDashboardSummary();\n return reply.status(200).send(summary);\n });\n\n app.get<{ Params: { queue: string } }>(`/${prefix}/:queue`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const summary = app.queues.getQueueSummary(queueName);\n if (summary === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(summary);\n });\n\n app.post<{ Params: { queue: string }; Body: any }>(`/${prefix}/:queue`,\n async (request: FastifyRequest<{ Params: { queue: string }; Body: any }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.enqueueMessage(queueName, request.body, request.headers as Record<string, any>);\n if (result === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(result);\n });\n\n app.get<{ Params: { queue: string } }>(`/${prefix}/:queue/process-dlq`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.processDlq(queueName);\n if (result === null) {\n return reply.status(404).send(result);\n }\n return reply.status(200).send(result);\n });\n\n app.post<{ Params: { queue: string } }>(`/${prefix}/:queue/purge-dlq`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.purgeDlq(queueName);\n if (result === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(result);\n });\n};"],"mappings":";AACA,OAAO,cAAc;;;ACCrB,IAAM,sBAAsB;AAGrB,IAAM,iBAA8B;AAAA,EACvC,WAAW;AAAA,EACX,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,aAAa;AAAA,IACT,SAAS;AAAA,IACT,WAAW;AAAA,IACX,cAAc;AAAA,IACd,gBAAgB;AAAA,EACpB;AACJ;AAGO,IAAM,cAAc,CAAC,eAA4B,gBAAoD;AAAA,EACxG,GAAG;AAAA,EACH,GAAG;AAAA,EACH,aAAa;AAAA,IACT,GAAG,cAAc;AAAA,IACjB,GAAI,YAAY,eAAe,CAAC;AAAA,EACpC;AACJ;AAGO,IAAM,qBAAqB,CAAC,SAAuB,YAAmC;AAAA,EACzF;AAAA,EACA,UAAU,CAAC;AAAA,EACX,YAAY,CAAC;AAAA,EACb,KAAK,CAAC;AAAA,EACN,iBAAiB;AAAA,EACjB;AACJ;AAGO,IAAM,qBAAqB,CAAC,MAAW,YAA+C;AACzF,QAAM,KAAK,KAAK,IAAI,EAAE,SAAS,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AAC5E,QAAM,UAAwB;AAAA,IAC1B;AAAA,IACA,WAAW,oBAAI,KAAK;AAAA,IACpB,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,EAChB;AAEA,QAAM,eAAe,OAAO,UAAU,iBAAiB,CAAC;AACxD,MAAI,eAAe,GAAG;AAClB,YAAQ,cAAc,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAI;AAAA,EACnE;AAEA,SAAO;AACX;AAMO,IAAM,gBAAgB,CAAC,cAA4B,SAAuB,YAA0B;AACvG,uBAAqB,cAAc,QAAQ,EAAE;AAC7C,UAAQ,YAAY;AACpB,UAAQ,cAAc,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO;AACnD,eAAa,SAAS,KAAK,OAAO;AACtC;AAIO,IAAM,iBAAiB,CAAC,SAAuB,MAAc,KAAK,IAAI,MAAe;AACxF,SAAO,CAAC,QAAQ,eAAe,QAAQ,YAAY,QAAQ,KAAK;AACpE;AAGO,IAAM,mBAAmB,CAAC,cAA4B,cAAsC;AAC/F,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,QAAwB,CAAC;AAC/B,QAAM,YAA4B,CAAC;AAEnC,aAAW,WAAW,aAAa,UAAU;AACzC,QAAI,MAAM,SAAS,aAAa,eAAe,SAAS,GAAG,GAAG;AAC1D,YAAM,KAAK,OAAO;AAAA,IACtB,OAAO;AACH,gBAAU,KAAK,OAAO;AAAA,IAC1B;AAAA,EACJ;AAEA,eAAa,WAAW;AACxB,SAAO;AACX;AAGO,IAAM,uBAAuB,CAAC,cAA4B,cAA4B;AACzF,eAAa,aAAa,aAAa,WAAW,OAAO,SAAO,IAAI,OAAO,SAAS;AACxF;AAGO,IAAM,YAAY,CAAC,cAA4B,SAAuB,UAAwB;AACjG,eAAa,IAAI,KAAK;AAAA,IAClB,GAAG;AAAA,IACH;AAAA,IACA,UAAU,oBAAI,KAAK;AAAA,EACvB,CAAC;AACD,uBAAqB,cAAc,QAAQ,EAAE;AACjD;AAGO,IAAM,wBAAwB,CAAC,aAA6B;AAC/D,SAAO,KAAK,IAAI,GAAG,QAAQ,IAAI;AACnC;AAGO,IAAM,qBAAqB,CAAC,cAA4B,YAAuC;AAAA,EAClG,UAAU,aAAa,SAAS;AAAA,EAChC,YAAY,aAAa,WAAW;AAAA,EACpC,KAAK,aAAa,IAAI;AAAA,EACtB,cAAc,aAAa;AAAA,EAC3B,eAAe;AAAA,IACX,WAAW,OAAO;AAAA,IAClB,eAAe,OAAO;AAAA,IACtB,UAAU,OAAO;AAAA,IACjB,eAAe,OAAO;AAAA,EAC1B;AACJ;AAGO,IAAM,yBAAyB,CAAC,QAAsC,WAAsD;AAC/H,SAAO,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,KAAK,MAAM;AAClE,YAAQ,SAAS,IAAI,mBAAmB,OAAO,MAAM;AACrD,WAAO;AAAA,EACX,GAAG,CAAC,CAAiC;AACzC;AAGO,IAAM,oBAAoB,CAAC,iBAAuC;AACrE,QAAM,WAAW,aAAa,IAAI;AAElC,MAAI,aAAa,EAAG,QAAO;AAE3B,QAAM,gBAAgB,aAAa,IAAI,IAAI,cAAY;AAAA,IACnD,GAAG;AAAA,IACH,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY,QAAQ;AAAA,IACpB,IAAI,KAAK,IAAI,EAAE,SAAS,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AAAA,EACzE,EAAE;AAEF,eAAa,SAAS,KAAK,GAAG,aAAa;AAC3C,eAAa,MAAM,CAAC;AAEpB,SAAO;AACX;AAGO,IAAM,WAAW,CAAC,iBAAuC;AAC5D,QAAM,WAAW,aAAa,IAAI;AAClC,eAAa,MAAM,CAAC;AACpB,SAAO;AACX;AAKO,IAAM,qBAAqB,CAC9B,WACA,SACA,QACA,WAC4B;AAAA,EAC5B,IAAI,QAAQ;AAAA,EACZ;AAAA,EACA;AAAA,EACA,WAAW,QAAQ;AAAA,EACnB,aAAa,oBAAI,KAAK;AAAA,EACtB,UAAU,QAAQ;AAAA,EAClB,SAAS,QAAQ;AAAA,EACjB,YAAY,QAAQ;AAAA,EACpB;AACJ;AAIO,IAAM,uBAAuB,CAChC,SACA,UACO;AACP,UAAQ,KAAK,KAAK;AAClB,MAAI,QAAQ,SAAS,qBAAqB;AACtC,YAAQ,OAAO,GAAG,QAAQ,SAAS,mBAAmB;AAAA,EAC1D;AACJ;;;ACpMA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAItB,IAAM,kBAAkB,CAAC,kBAAgC;AAAA,EACrD,UAAU,aAAa;AAAA,EACvB,YAAY,aAAa;AAAA,EACzB,KAAK,aAAa;AAAA,EAClB,SAAS,oBAAI,KAAK;AACtB;AAEA,IAAM,eAAe,CAAC,aAAmC;AACrD,WAAS,QAAQ,CAAC,QAAsB;AACpC,QAAI,YAAY,IAAI,KAAK,IAAI,SAAS;AACtC,QAAI,IAAI,YAAa,KAAI,cAAc,IAAI,KAAK,IAAI,WAAW;AAC/D,QAAI,IAAI,SAAU,KAAI,WAAW,IAAI,KAAK,IAAI,QAAQ;AAAA,EAC1D,CAAC;AACL;AAEO,IAAM,iBAAiB,OAAO,SAAuB,WAAmB,iBAA8C;AACzH,MAAI;AACA,UAAM,YAAY,gBAAgB,YAAY;AAC9C,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAqB,UAAK,QAAQ,OAAO,YAAY,WAAW,GAAG,SAAS,OAAO;AACzF,UAAM,eAAoB,UAAK,QAAQ,OAAO,YAAY,WAAW,GAAG,SAAS,IAAI,GAAG,WAAW;AAInG,UAAS,aAAU,cAAc,KAAK,UAAU,WAAW,MAAM,CAAC,GAAG,MAAM;AAC3E,UAAS,UAAO,cAAc,aAAa;AAE3C,YAAQ,QAAQ,MAAM,EAAE,QAAQ,aAAa,OAAO,GAAG,sBAAsB,SAAS,EAAE;AAAA,EAC5F,SAAS,OAAY;AACjB,YAAQ,QAAQ;AAAA,MACZ,EAAE,QAAQ,aAAa,QAAQ,OAAO,OAAO,QAAQ;AAAA,MACrD,kCAAkC,SAAS;AAAA,IAC/C;AAAA,EACJ;AACJ;AAEO,IAAM,qBAAqB,OAAO,YAAyC;AAC9E,MAAI;AACA,eAAW,CAAC,WAAW,YAAY,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACpE,YAAM,eAAe,SAAS,WAAW,YAAY;AAAA,IACzD;AACA,YAAQ,QAAQ,MAAM,6BAA6B,QAAQ,OAAO,YAAY,SAAS,EAAE;AAAA,EAC7F,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,OAAO,OAAO,QAAQ,GAAG,iCAAiC;AAAA,EACtF;AACJ;AAKO,IAAM,uBAAuB,OAAO,YAAyC;AAChF,MAAI,QAAQ,YAAY,SAAS,EAAG;AAEpC,MAAI;AACA,UAAM,aAAa,CAAC,GAAG,QAAQ,WAAW;AAC1C,YAAQ,YAAY,MAAM;AAE1B,eAAW,aAAa,YAAY;AAChC,UAAI,QAAQ,OAAO,SAAS,GAAG;AAC3B,cAAM,eAAe,SAAS,WAAW,QAAQ,OAAO,SAAS,CAAC;AAAA,MACtE;AAAA,IACJ;AAAA,EACJ,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,OAAO,OAAO,QAAQ,GAAG,mCAAmC;AAAA,EACxF;AACJ;AAIO,IAAM,iBAAiB,OAAO,SAAuB,cAAqC;AAC7F,MAAI;AACA,UAAM,eAAe,QAAQ,OAAO,SAAS;AAC7C,QAAI,CAAC,cAAc;AACf,cAAQ,QAAQ,KAAK,yCAAyC,SAAS,EAAE;AACzE;AAAA,IACJ;AAEA,UAAM,gBAAqB,UAAK,QAAQ,OAAO,YAAY,WAAW,GAAG,SAAS,OAAO;AAEzF,QAAI;AACA,YAAM,OAAO,MAAS,YAAS,eAAe,MAAM;AACpD,YAAM,YAAY,KAAK,MAAM,IAAI;AAEjC,mBAAa,UAAU,QAAQ;AAC/B,mBAAa,UAAU,UAAU;AACjC,mBAAa,UAAU,GAAG;AAE1B,mBAAa,WAAW,UAAU;AAClC,mBAAa,MAAM,UAAU;AAC7B,mBAAa,aAAa,CAAC;AAO3B,iBAAW,WAAW,UAAU,YAA8B;AAC1D,YAAI,QAAQ,YAAY,QAAQ,OAAO,YAAY;AAC/C,uBAAa,IAAI,KAAK,EAAE,GAAG,SAAS,OAAO,uCAAuC,UAAU,oBAAI,KAAK,EAAE,CAAC;AAAA,QAC5G,OAAO;AACH,kBAAQ,cAAc,oBAAI,KAAK;AAC/B,uBAAa,SAAS,KAAK,OAAO;AAAA,QACtC;AAAA,MACJ;AAEA,cAAQ,QAAQ;AAAA,QACZ,EAAE,QAAQ,aAAa,OAAO;AAAA,QAC9B,uBAAuB,SAAS,cAAc,UAAU,OAAO;AAAA,MACnE;AAAA,IACJ,SAAS,OAAY;AACjB,UAAI,MAAM,SAAS,UAAU;AACzB,gBAAQ,QAAQ,MAAM,EAAE,QAAQ,aAAa,OAAO,GAAG,mCAAmC,SAAS,EAAE;AAAA,MACzG,OAAO;AACH,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,OAAO,OAAO,QAAQ,GAAG,kCAAkC,SAAS,EAAE;AAAA,EAClG;AACJ;AAEO,IAAM,wBAAwB,OAAO,YAAyC;AACjF,MAAI;AACA,UAAS,SAAM,QAAQ,OAAO,YAAY,WAAW,EAAE,WAAW,KAAK,CAAC;AAExE,QAAI,QAAQ,OAAO,YAAY,gBAAgB;AAC3C,YAAM,kBAAkB,YAAY;AAChC,gBAAQ,QAAQ,MAAM,uCAAuC;AAC7D,cAAM,mBAAmB,OAAO;AAChC,gBAAQ,KAAK,CAAC;AAAA,MAClB;AAEA,cAAQ,GAAG,UAAU,eAAe;AACpC,cAAQ,GAAG,WAAW,eAAe;AAAA,IACzC;AAEA,YAAQ,QAAQ,MAAM,kCAAkC,QAAQ,OAAO,YAAY,SAAS,EAAE;AAAA,EAClG,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,OAAO,OAAO,QAAQ,GAAG,wCAAwC;AAAA,EAC7F;AACJ;;;AC9HA,IAAM,iBAAiB,OACnB,SACA,WACA,SACA,iBACgB;AAChB,MAAI;AACA,UAAM,SAAS,MAAM,aAAa,QAAQ,OAAO;AACjD,QAAI,QAAQ,WAAW;AACnB,oBAAc,SAAS,WAAW,SAAS,YAAY;AAAA,IAC3D,OAAO;AACH,2BAAqB,cAAc,QAAQ,EAAE;AAC7C,2BAAqB,QAAQ,gBAAgB,mBAAmB,WAAW,SAAS,WAAW,CAAC;AAChG,cAAQ,QAAQ,MAAM,kCAAkC,QAAQ,EAAE,aAAa,SAAS,EAAE;AAAA,IAC9F;AAAA,EACJ,QAAQ;AACJ,kBAAc,SAAS,WAAW,SAAS,YAAY;AAAA,EAC3D;AACJ;AAEA,IAAM,gBAAgB,CAClB,SACA,WACA,SACA,iBACO;AACP,UAAQ,QAAQ,MAAM,4BAA4B,QAAQ,EAAE,aAAa,SAAS,4BAA4B;AAE9G,MAAI,QAAQ,YAAY,QAAQ,OAAO,YAAY;AAC/C,cAAU,cAAc,SAAS,sBAAsB;AACvD,yBAAqB,QAAQ,gBAAgB,mBAAmB,WAAW,SAAS,UAAU,sBAAsB,CAAC;AACrH;AAAA,EACJ;AAEA,QAAM,UAAU,QAAQ,OAAO,eAAe,sBAAsB,QAAQ,WAAW,CAAC,IAAI;AAC5F,gBAAc,cAAc,SAAS,OAAO;AAC5C,UAAQ,QAAQ,MAAM,gCAAgC,QAAQ,EAAE,OAAO,OAAO,IAAI;AACtF;AAKA,IAAM,eAAe,OACjB,SACA,WACA,iBACgB;AAChB,MAAI,aAAa,iBAAiB;AAC9B;AAAA,EACJ;AAEA,MAAI;AACA,iBAAa,kBAAkB;AAE/B,UAAM,oBAAoB,iBAAiB,cAAc,QAAQ,OAAO,SAAS;AACjF,QAAI,kBAAkB,WAAW,GAAG;AAChC;AAAA,IACJ;AAEA,iBAAa,WAAW,KAAK,GAAG,iBAAiB;AAEjD,UAAM,QAAQ;AAAA,MACV,kBAAkB,IAAI,aAAW,QAAQ,QAAQ;AAAA,QAC7C,EAAE,QAAQ,aAAa,QAAQ,OAAO,QAAQ,GAAG;AAAA,QACjD,MAAM,eAAe,SAAS,WAAW,SAAS,YAAY;AAAA,MAClE,CAAC;AAAA,IACL;AAEA,QAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,cAAQ,YAAY,IAAI,SAAS;AAAA,IACrC;AAAA,EACJ,UAAE;AACE,iBAAa,kBAAkB;AAAA,EACnC;AACJ;AAEA,IAAM,qBAAqB,CACvB,SACA,WACA,cACA,QACO;AACP,MAAI,aAAa,iBAAiB;AAC9B;AAAA,EACJ;AACA,QAAM,QAAQ,aAAa,SAAS,OAAO,aAAW,eAAe,SAAS,GAAG,CAAC;AAClF,MAAI,MAAM,WAAW,GAAG;AACpB;AAAA,EACJ;AAEA,QAAM,YAAY,MAAM,UAAU,QAAQ,OAAO;AACjD,QAAM,iBAAiB,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,aAAW,QAAQ,UAAU,QAAQ,CAAC,CAAC;AAC1F,QAAM,cAAc,kBAAkB,QAAQ,OAAO;AAErD,MAAI,aAAa,aAAa;AAC1B,iBAAa,SAAS,WAAW,YAAY;AAAA,EACjD;AACJ;AAEA,IAAM,uBAAuB,CAAC,SAAuB,QAAsB;AACvE,MAAI,CAAC,QAAQ,OAAO,YAAY,SAAS;AACrC;AAAA,EACJ;AAEA,QAAM,eAAe,QAAQ,OAAO,YAAY,gBAAgB;AAChE,MAAI,eAAe,KAAK,MAAM,QAAQ,eAAe,cAAc;AAC/D,YAAQ,cAAc;AACtB,yBAAqB,OAAO;AAAA,EAChC;AACJ;AAOO,IAAM,OAAO,CAAC,YAAgC;AACjD,QAAM,MAAM,KAAK,IAAI;AACrB,aAAW,CAAC,WAAW,YAAY,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACpE,uBAAmB,SAAS,WAAW,cAAc,GAAG;AAAA,EAC5D;AACA,uBAAqB,SAAS,GAAG;AACrC;;;AClIA,IAAM,mBAAmB,CAAC,cAA+B;AACrD,SAAO,OAAO,cAAc,YAAY,UAAU,SAAS,KAAK,mBAAmB,KAAK,SAAS;AACrG;AAEA,IAAM,oBAAoB,CAAC,cAA4B;AACnD,MAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC7C,UAAM,IAAI,MAAM,uCAAuC;AAAA,EAC3D;AAEA,MAAI,CAAC,iBAAiB,SAAS,GAAG;AAC9B,UAAM,IAAI,MAAM,gFAAgF;AAAA,EACpG;AACJ;AAEA,IAAM,kBAAkB,CAAC,YAAuB;AAC5C,MAAI,OAAO,YAAY,YAAY;AAC/B,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAChD;AACJ;AAEO,IAAM,WAAW,OAAO,SAAuB,WAAmB,SAAuB,WAAmC;AAC/H,MAAI;AACA,sBAAkB,SAAS;AAC3B,oBAAgB,OAAO;AAEvB,QAAI,QAAQ,OAAO,SAAS,GAAG;AAC3B,cAAQ,QAAQ,KAAK,EAAE,OAAO,GAAG,yBAAyB,SAAS,GAAG;AACtE;AAAA,IACJ;AAEA,YAAQ,OAAO,SAAS,IAAI,mBAAmB,SAAS,MAAM;AAE9D,QAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,YAAM,eAAe,SAAS,SAAS;AAAA,IAC3C;AAEA,YAAQ,QAAQ,KAAK,EAAE,OAAO,GAAG,gBAAgB,SAAS,EAAE;AAAA,EAChE,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,QAAQ,OAAO,MAAM,QAAQ,GAAG,uBAAuB,SAAS,EAAE;AAC1F,UAAM;AAAA,EACV;AACJ;AAEO,IAAM,cAAc,OAAO,SAAuB,cAAqC;AAC1F,MAAI;AACA,sBAAkB,SAAS;AAE3B,UAAM,eAAe,QAAQ,OAAO,SAAS;AAC7C,QAAI,CAAC,cAAc;AACf,YAAM,IAAI,MAAM,UAAU,SAAS,kBAAkB;AAAA,IACzD;AAEA,UAAM,gBAAgB,aAAa,SAAS,SAAS,aAAa,WAAW;AAC7E,QAAI,gBAAgB,GAAG;AACnB,cAAQ,QAAQ;AAAA,QACZ,EAAE,QAAQ,aAAa,OAAO;AAAA,QAC9B,uBAAuB,aAAa,sBAAsB,SAAS;AAAA,MACvE;AAAA,IACJ;AAEA,QAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,YAAM,eAAe,SAAS,WAAW,YAAY;AAAA,IACzD;AAEA,WAAO,QAAQ,OAAO,SAAS;AAE/B,YAAQ,QAAQ,KAAK,EAAE,QAAQ,aAAa,OAAO,GAAG,kBAAkB,SAAS,EAAE;AAAA,EACvF,SAAS,OAAY;AACjB,YAAQ,QAAQ,MAAM,EAAE,OAAO,MAAM,QAAQ,GAAG,0BAA0B,SAAS,EAAE;AACrF,UAAM;AAAA,EACV;AACJ;AAEO,IAAM,WAAW,CAAC,SAAuB,cAA+B;AAC3E,MAAI;AACA,sBAAkB,SAAS;AAC3B,WAAO,aAAa,QAAQ;AAAA,EAChC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,IAAM,aAAa,CAAC,SAAuB,WAA8B;AAC5E,SAAO,OAAO,KAAK,QAAQ,MAAM,EAC5B,OAAO,eAAa,CAAC,UAAU,QAAQ,OAAO,SAAS,EAAE,WAAW,MAAM,EAC1E,KAAK;AACd;AAEO,IAAM,gBAAgB,CAAC,SAAuB,cAA2C;AAC5F,MAAI;AACA,sBAAkB,SAAS;AAC3B,WAAO,QAAQ,OAAO,SAAS,KAAK;AAAA,EACxC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,IAAM,eAAe,CAAC,YAAwD;AAEjF,SAAO,EAAE,GAAG,QAAQ,OAAO;AAC/B;AAEO,IAAM,sBAAsB,CAAC,YAChC,uBAAuB,QAAQ,QAAQ,QAAQ,MAAM;AAElD,IAAM,kBAAkB,CAAC,SAAuB,cAAsB;AACzE,QAAM,QAAQ,QAAQ,OAAO,SAAS;AACtC,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AAEA,SAAO;AAAA,IACH,OAAO,mBAAmB,OAAO,QAAQ,MAAM;AAAA,IAC/C,UAAU;AAAA,MACN,UAAU,MAAM;AAAA,MAChB,YAAY,MAAM;AAAA,MAClB,KAAK,MAAM;AAAA,IACf;AAAA,EACJ;AACJ;AAEO,IAAM,iBAAiB,OAAO,SAAuB,WAAmB,MAAW,eAAoB;AAC1G,QAAM,eAAe,QAAQ,OAAO,SAAS;AAC7C,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,EACX;AAEA,QAAM,UAAU,mBAAmB,MAAM,UAAU;AACnD,eAAa,SAAS,KAAK,OAAO;AAElC,MAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,YAAQ,YAAY,IAAI,SAAS;AAAA,EACrC;AAEA,SAAO,EAAE,IAAI,QAAQ,IAAI,UAAU;AACvC;AAEO,IAAM,aAAa,OAAO,SAAuB,cAAsB;AAC1E,QAAM,eAAe,QAAQ,OAAO,SAAS;AAC7C,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,EACX;AAEA,QAAM,iBAAiB,kBAAkB,YAAY;AACrD,MAAI,mBAAmB,GAAG;AACtB,WAAO,EAAE,QAAQ,WAAW,SAAS,iCAAiC,WAAW,EAAE;AAAA,EACvF;AAEA,UAAQ,QAAQ;AAAA,IACZ,EAAE,QAAQ,aAAa,OAAO;AAAA,IAC9B,UAAU,cAAc,0BAA0B,SAAS;AAAA,EAC/D;AAEA,MAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,YAAQ,YAAY,IAAI,SAAS;AAAA,EACrC;AAEA,SAAO;AAAA,IACH,QAAQ;AAAA,IACR,SAAS,SAAS,cAAc;AAAA,IAChC,WAAW;AAAA,EACf;AACJ;AAEO,IAAM,WAAW,OAAO,SAAuB,cAAsB;AACxE,QAAM,eAAe,QAAQ,OAAO,SAAS;AAC7C,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,EACX;AAEA,QAAM,cAAc,SAAS,YAAY;AACzC,MAAI,gBAAgB,GAAG;AACnB,WAAO,EAAE,QAAQ,WAAW,SAAS,+BAA+B,QAAQ,EAAE;AAAA,EAClF;AAEA,UAAQ,QAAQ;AAAA,IACZ,EAAE,QAAQ,aAAa,OAAO;AAAA,IAC9B,WAAW,WAAW,0BAA0B,SAAS;AAAA,EAC7D;AAEA,MAAI,QAAQ,OAAO,YAAY,SAAS;AACpC,YAAQ,YAAY,IAAI,SAAS;AAAA,EACrC;AAEA,SAAO;AAAA,IACH,QAAQ;AAAA,IACR,SAAS,UAAU,WAAW;AAAA,IAC9B,QAAQ;AAAA,EACZ;AACJ;AAEO,IAAM,oBAAoB,CAAC,SAAuB,WAAmB,QAAQ,OAAO;AACvF,SAAO,QAAQ,eACV,OAAO,WAAS,MAAM,cAAc,SAAS,EAC7C,MAAM,CAAC,KAAK,EACZ,QAAQ;AACjB;AAOO,IAAM,qBAAqB,CAAC,aAAyC;AAAA,EACxE,UAAU,CAAC,WAAW,SAAS,WAAW,SAAS,SAAS,WAAW,SAAS,MAAM;AAAA,EACtF,aAAa,CAAC,cAAc,YAAY,SAAS,SAAS;AAAA,EAC1D,UAAU,CAAC,cAAc,SAAS,SAAS,SAAS;AAAA,EACpD,YAAY,CAAC,WAAW,WAAW,SAAS,MAAM;AAAA,EAClD,eAAe,CAAC,cAAc,cAAc,SAAS,SAAS;AAAA,EAC9D,cAAc,MAAM,aAAa,OAAO;AAAA,EACxC,WAAW,MAAM,QAAQ;AAAA,EACzB,qBAAqB,MAAM,oBAAoB,OAAO;AAAA,EACtD,iBAAiB,CAAC,cAAc,gBAAgB,SAAS,SAAS;AAAA,EAClE,gBAAgB,CAAC,WAAW,MAAM,eAAe,eAAe,SAAS,WAAW,MAAM,UAAU;AAAA,EACpG,YAAY,CAAC,cAAc,WAAW,SAAS,SAAS;AAAA,EACxD,UAAU,CAAC,cAAc,SAAS,SAAS,SAAS;AAAA,EACpD,mBAAmB,CAAC,WAAW,UAAU,kBAAkB,SAAS,WAAW,KAAK;AACxF;;;ACjOO,IAAM,sBAAsB,CAC/B,KACA,SAAiB,OACV;AACP,MAAI;AAAA,IAAI,IAAI,MAAM;AAAA,IACd,OAAO,GAAmB,UAAwB;AAC9C,YAAM,UAAU,IAAI,OAAO,oBAAoB;AAC/C,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,OAAO;AAAA,IACzC;AAAA,EAAC;AAEL,MAAI;AAAA,IAAmC,IAAI,MAAM;AAAA,IAC7C,OAAO,SAAwD,UAAwB;AACnF,YAAM,YAAY,QAAQ,OAAO;AACjC,YAAM,UAAU,IAAI,OAAO,gBAAgB,SAAS;AACpD,UAAI,YAAY,MAAM;AAClB,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,MAC9D;AACA,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,OAAO;AAAA,IACzC;AAAA,EAAC;AAEL,MAAI;AAAA,IAA+C,IAAI,MAAM;AAAA,IACzD,OAAO,SAAmE,UAAwB;AAC9F,YAAM,YAAY,QAAQ,OAAO;AACjC,YAAM,SAAS,MAAM,IAAI,OAAO,eAAe,WAAW,QAAQ,MAAM,QAAQ,OAA8B;AAC9G,UAAI,WAAW,MAAM;AACjB,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,MAC9D;AACA,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,IACxC;AAAA,EAAC;AAEL,MAAI;AAAA,IAAmC,IAAI,MAAM;AAAA,IAC7C,OAAO,SAAwD,UAAwB;AACnF,YAAM,YAAY,QAAQ,OAAO;AACjC,YAAM,SAAS,MAAM,IAAI,OAAO,WAAW,SAAS;AACpD,UAAI,WAAW,MAAM;AACjB,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,MACxC;AACA,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,IACxC;AAAA,EAAC;AAEL,MAAI;AAAA,IAAoC,IAAI,MAAM;AAAA,IAC9C,OAAO,SAAwD,UAAwB;AACnF,YAAM,YAAY,QAAQ,OAAO;AACjC,YAAM,SAAS,MAAM,IAAI,OAAO,SAAS,SAAS;AAClD,UAAI,WAAW,MAAM;AACjB,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,MAC9D;AACA,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM;AAAA,IACxC;AAAA,EAAC;AACT;;;AL1CO,IAAM,eACT,SAAS,OACL,KACA,YACC;AACD,QAAM,UAAwB;AAAA,IAC1B,QAAQ,YAAY,gBAAgB,QAAQ,MAAM;AAAA,IAClD,QAAQ,CAAC;AAAA,IACT,aAAa,oBAAI,IAAY;AAAA,IAC7B,aAAa,KAAK,IAAI;AAAA,IACtB,gBAAgB,CAAC;AAAA,IACjB,SAAS,IAAI;AAAA,EACjB;AAKA,QAAM,iBAAiB,YAAY,MAAM,KAAK,OAAO,GAAG,QAAQ,OAAO,cAAc;AACrF,MAAI,QAAQ,WAAW,YAAY,cAAc,cAAc,CAAC;AAEhE,MAAI,SAAS,UAAU,mBAAmB,OAAO,CAAC;AAClD,sBAAoB,KAAK,QAAQ,MAAM;AAEvC,QAAM,sBAAsB,OAAO;AACvC,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/queue-plugin/plugin.ts","../../src/queue-plugin/core.ts","../../src/queue-plugin/persistence.ts","../../src/queue-plugin/processing.ts","../../src/queue-plugin/decorator.ts","../../src/queue-plugin/routes.ts"],"sourcesContent":["import { FastifyInstance, FastifyPluginAsync } from \"fastify\";\nimport fsPlugin from \"fastify-plugin\";\n\nimport { QueuePluginOptions, QueueRuntime } from \"./types\";\nimport { DEFAULT_CONFIG, mergeConfig } from \"./core\";\nimport { tick } from \"./processing\";\nimport { initializePersistence } from \"./persistence\";\nimport { createQueueManager } from \"./decorator\";\nimport { registerQueueRoutes } from \"./routes\";\n\nexport const queuesPlugin: FastifyPluginAsync<QueuePluginOptions> =\n fsPlugin(async (\n app: FastifyInstance,\n options: QueuePluginOptions\n ) => {\n const runtime: QueueRuntime = {\n config: mergeConfig(DEFAULT_CONFIG, options.config),\n queues: {},\n topics: {},\n dirtyQueues: new Set<string>(),\n lastSavedAt: Date.now(),\n messageHistory: [],\n logging: app.logging,\n };\n\n // One timer for the entire plugin: every tickIntervalMs, check every\n // queue for batches/retries that are due, and save persistence if\n // its interval has elapsed. See processing.ts:tick.\n const tickIntervalId = setInterval(() => tick(runtime), runtime.config.tickIntervalMs);\n app.addHook('onClose', async () => clearInterval(tickIntervalId));\n\n app.decorate('queues', createQueueManager(runtime));\n registerQueueRoutes(app, options.prefix);\n\n await initializePersistence(runtime);\n });","import { EventHandler, QueueConfig, QueueMessage, QueueMessageHistoryEntry, QueueService, QueueSummary } from \"./types\";\n\nconst MAX_MESSAGE_HISTORY = 500;\n\n// default config\nexport const DEFAULT_CONFIG: QueueConfig = {\n batchSize: 10,\n batchWindowMs: 500,\n maxRetries: 3,\n parallel: true,\n maxConcurrent: 5,\n retryBackoff: true,\n tickIntervalMs: 50,\n persistence: {\n enabled: true,\n directory: './.develop/.queue-data',\n saveInterval: 60000,\n saveOnShutdown: false\n }\n};\n\n// Pure function to merge configs\nexport const mergeConfig = (defaultConfig: QueueConfig, userConfig?: Partial<QueueConfig>): QueueConfig => ({\n ...defaultConfig,\n ...userConfig,\n persistence: {\n ...defaultConfig.persistence,\n ...(userConfig?.persistence || {})\n }\n});\n\n// Pure function to create a new queue service\nexport const createQueueService = (handler: EventHandler, module?: string): QueueService => ({\n handler,\n incoming: [],\n processing: [],\n dlq: [],\n processingBatch: false,\n module\n});\n\n// Pure function to create a queue message\nexport const createQueueMessage = (body: any, headers: Record<string, any>): QueueMessage => {\n const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);\n const message: QueueMessage = {\n id,\n timestamp: new Date(),\n attempts: 0,\n payload: body,\n attributes: headers,\n };\n\n const delaySeconds = Number(headers?.['x-delay-seconds']);\n if (delaySeconds > 0) {\n message.nextAttempt = new Date(Date.now() + delaySeconds * 1000);\n }\n\n return message;\n};\n\n// Pure function to schedule a retry for a message\n// Puts a failed message back into `incoming` to wait out its backoff delay,\n// same as any delayed message - so the next batch dispatch picks it up\n// mixed in with whatever else is ready, instead of retrying it alone.\nexport const scheduleRetry = (queueService: QueueService, message: QueueMessage, delayMs: number): void => {\n removeFromProcessing(queueService, message.id);\n message.attempts += 1;\n message.nextAttempt = new Date(Date.now() + delayMs);\n queueService.incoming.push(message);\n};\n\n\n// Pure function to check whether a message is ready to be processed\nexport const isMessageReady = (message: QueueMessage, now: number = Date.now()): boolean => {\n return !message.nextAttempt || message.nextAttempt.getTime() <= now;\n};\n\n// Pure function to move ready messages from incoming to processing, leaving delayed messages in place\nexport const moveToProcessing = (queueService: QueueService, batchSize: number): QueueMessage[] => {\n const now = Date.now();\n const ready: QueueMessage[] = [];\n const remaining: QueueMessage[] = [];\n\n for (const message of queueService.incoming) {\n if (ready.length < batchSize && isMessageReady(message, now)) {\n ready.push(message);\n } else {\n remaining.push(message);\n }\n }\n\n queueService.incoming = remaining;\n return ready;\n};\n\n// Pure function to remove message from processing queue\nexport const removeFromProcessing = (queueService: QueueService, messageId: string): void => {\n queueService.processing = queueService.processing.filter(msg => msg.id !== messageId);\n};\n\n// Pure function to move message to DLQ\nexport const moveToDLQ = (queueService: QueueService, message: QueueMessage, error: string): void => {\n queueService.dlq.push({\n ...message,\n error,\n failedAt: new Date()\n });\n removeFromProcessing(queueService, message.id);\n};\n\n// Pure function to calculate backoff delay\nexport const calculateBackoffDelay = (attempts: number): number => {\n return Math.pow(2, attempts) * 100;\n};\n\n// Pure function to create queue summary\nexport const createQueueSummary = (queueService: QueueService, config: QueueConfig): QueueSummary => ({\n incoming: queueService.incoming.length,\n processing: queueService.processing.length,\n dlq: queueService.dlq.length,\n isProcessing: queueService.processingBatch,\n configuration: {\n batchSize: config.batchSize,\n batchWindowMs: config.batchWindowMs,\n parallel: config.parallel,\n maxConcurrent: config.maxConcurrent\n }\n});\n\n// Pure function to create dashboard summary\nexport const createDashboardSummary = (queues: Record<string, QueueService>, config: QueueConfig): Record<string, QueueSummary> => {\n return Object.entries(queues).reduce((summary, [queueName, queue]) => {\n summary[queueName] = createQueueSummary(queue, config);\n return summary;\n }, {} as Record<string, QueueSummary>);\n};\n\n// Pure function to move DLQ messages back to incoming\nexport const moveDLQToIncoming = (queueService: QueueService): number => {\n const dlqCount = queueService.dlq.length;\n\n if (dlqCount === 0) return 0;\n\n const movedMessages = queueService.dlq.map(message => ({\n ...message,\n attempts: 0,\n error: undefined,\n failedAt: undefined,\n reprocessed: true,\n originalId: message.id,\n id: Date.now().toString() + Math.random().toString(36).substring(2, 7)\n }));\n\n queueService.incoming.push(...movedMessages);\n queueService.dlq = [];\n\n return dlqCount;\n};\n\n// Pure function to purge DLQ\nexport const purgeDLQ = (queueService: QueueService): number => {\n const dlqCount = queueService.dlq.length;\n queueService.dlq = [];\n return dlqCount;\n};\n\n// Pure function to build a message-history record for a completed or\n// terminally-failed message. This is what powers the dev console's\n// per-queue history view for messages that already left incoming/processing/dlq.\nexport const createHistoryEntry = (\n queueName: string,\n message: QueueMessage,\n status: 'completed' | 'failed',\n error?: string\n): QueueMessageHistoryEntry => ({\n id: message.id,\n queueName,\n status,\n timestamp: message.timestamp,\n completedAt: new Date(),\n attempts: message.attempts,\n payload: message.payload,\n attributes: message.attributes,\n error,\n});\n\n// Appends to the bounded history log in place, mirroring schedule-plugin's\n// executionHistory cap so long-running processes don't grow this unbounded.\nexport const recordMessageHistory = (\n history: QueueMessageHistoryEntry[],\n entry: QueueMessageHistoryEntry\n): void => {\n history.push(entry);\n if (history.length > MAX_MESSAGE_HISTORY) {\n history.splice(0, history.length - MAX_MESSAGE_HISTORY);\n }\n};","import * as fs from \"fs/promises\";\nimport * as path from \"path\";\n\nimport { QueueService, QueueRuntime, QueueMessage } from \"./types\";\n\nconst createQueueData = (queueService: QueueService) => ({\n incoming: queueService.incoming,\n processing: queueService.processing,\n dlq: queueService.dlq,\n savedAt: new Date()\n});\n\nconst restoreDates = (messages: QueueMessage[]): void => {\n messages.forEach((msg: QueueMessage) => {\n msg.timestamp = new Date(msg.timestamp);\n if (msg.nextAttempt) msg.nextAttempt = new Date(msg.nextAttempt);\n if (msg.failedAt) msg.failedAt = new Date(msg.failedAt);\n });\n};\n\nexport const saveQueueState = async (runtime: QueueRuntime, queueName: string, queueService: QueueService): Promise<void> => {\n try {\n const queueData = createQueueData(queueService);\n const now = Date.now();\n const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);\n const tempFilePath = path.join(runtime.config.persistence.directory, `${queueName}.${now}temp.json`);\n\n // Write to a temp file first, then rename - rename is atomic, so a\n // reader never sees a half-written file.\n await fs.writeFile(tempFilePath, JSON.stringify(queueData, null, 2), 'utf8');\n await fs.rename(tempFilePath, queueFilePath);\n\n runtime.logging.debug({ module: queueService.module }, `Queue state saved: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error(\n { module: queueService.module, error: error?.message },\n `Failed to save queue state for ${queueName}`\n );\n }\n};\n\nexport const saveAllQueueStates = async (runtime: QueueRuntime): Promise<void> => {\n try {\n for (const [queueName, queueService] of Object.entries(runtime.queues)) {\n await saveQueueState(runtime, queueName, queueService);\n }\n runtime.logging.debug(`All queue states saved to ${runtime.config.persistence.directory}`);\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to save all queue states`);\n }\n};\n\n// Only saves queues that changed since the last save (tracked via\n// runtime.dirtyQueues). Called from processing.ts's tick once\n// persistence.saveInterval has elapsed.\nexport const saveDirtyQueueStates = async (runtime: QueueRuntime): Promise<void> => {\n if (runtime.dirtyQueues.size === 0) return;\n\n try {\n const queueNames = [...runtime.dirtyQueues];\n runtime.dirtyQueues.clear();\n\n for (const queueName of queueNames) {\n if (runtime.queues[queueName]) {\n await saveQueueState(runtime, queueName, runtime.queues[queueName]);\n }\n }\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to save dirty queue states`);\n }\n};\n\n// Called from addQueue, right after a queue is registered, to restore\n// whatever was on disk for it from a previous run.\nexport const loadQueueState = async (runtime: QueueRuntime, queueName: string): Promise<void> => {\n try {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n runtime.logging.warn(`Skipping load for non-existent queue: ${queueName}`);\n return;\n }\n\n const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);\n\n try {\n const data = await fs.readFile(queueFilePath, 'utf8');\n const queueData = JSON.parse(data);\n\n restoreDates(queueData.incoming);\n restoreDates(queueData.processing);\n restoreDates(queueData.dlq);\n\n queueService.incoming = queueData.incoming;\n queueService.dlq = queueData.dlq;\n queueService.processing = [];\n\n // Nothing is actually in flight right after a restart - anything\n // that was mid-handling goes back into the ready pool like any\n // other message (the tick's next batch picks it up), or straight\n // to the DLQ if it had already used up its retries before the\n // shutdown.\n for (const message of queueData.processing as QueueMessage[]) {\n if (message.attempts >= runtime.config.maxRetries) {\n queueService.dlq.push({ ...message, error: 'max retries exceeded before restart', failedAt: new Date() });\n } else {\n message.nextAttempt = new Date();\n queueService.incoming.push(message);\n }\n }\n\n runtime.logging.debug(\n { module: queueService.module },\n `Queue state loaded: ${queueName} (saved at ${queueData.savedAt})`\n );\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n runtime.logging.debug({ module: queueService.module }, `No saved state found for queue: ${queueName}`);\n } else {\n throw error;\n }\n }\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to load queue state for ${queueName}`);\n }\n};\n\nexport const initializePersistence = async (runtime: QueueRuntime): Promise<void> => {\n try {\n await fs.mkdir(runtime.config.persistence.directory, { recursive: true });\n\n if (runtime.config.persistence.saveOnShutdown) {\n const shutdownHandler = async () => {\n runtime.logging.debug('Saving queue state before shutdown...');\n await saveAllQueueStates(runtime);\n process.exit(0);\n };\n\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n }\n\n runtime.logging.debug(`Queue persistence initialized: ${runtime.config.persistence.directory}`);\n } catch (error: any) {\n runtime.logging.error({ error: error?.message }, `Failed to initialize queue persistence`);\n }\n};","import { QueueMessage, QueueService, QueueRuntime } from \"./types\";\nimport {\n isMessageReady,\n moveToProcessing,\n removeFromProcessing,\n moveToDLQ,\n scheduleRetry,\n calculateBackoffDelay,\n createHistoryEntry,\n recordMessageHistory,\n} from \"./core\";\nimport { saveDirtyQueueStates } from \"./persistence\";\n\n// Sends one message through the user's handler. Success removes it from\n// `processing`; failure either schedules a retry or moves it to the DLQ.\n// Used for both freshly-batched messages and individually-retried ones.\n// Wrapped in runWithLogContext by processBatch, so every log call below\n// (and anything the user's handler itself logs) carries this message's\n// module/reqId without having to pass them through explicitly.\nconst processMessage = async (\n runtime: QueueRuntime,\n queueName: string,\n message: QueueMessage,\n queueService: QueueService\n): Promise<void> => {\n try {\n const result = await queueService.handler(message);\n if (result?.failureId) {\n handleFailure(runtime, queueName, message, queueService);\n } else {\n removeFromProcessing(queueService, message.id);\n recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, 'completed'));\n runtime.logging.debug(`Successfully processed message ${message.id} in queue ${queueName}`);\n }\n } catch {\n handleFailure(runtime, queueName, message, queueService);\n }\n};\n\nconst handleFailure = (\n runtime: QueueRuntime,\n queueName: string,\n message: QueueMessage,\n queueService: QueueService\n): void => {\n runtime.logging.error(`Error processing message ${message.id} in queue ${queueName}: handler returned failure`);\n\n if (message.attempts >= runtime.config.maxRetries) {\n moveToDLQ(queueService, message, 'max retries exceeded');\n recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, 'failed', 'max retries exceeded'));\n return;\n }\n\n const delayMs = runtime.config.retryBackoff ? calculateBackoffDelay(message.attempts + 1) : 0;\n scheduleRetry(queueService, message, delayMs);\n runtime.logging.debug(`Scheduling retry for message ${message.id} in ${delayMs}ms`);\n};\n\n// Pulls up to batchSize ready messages out of `incoming` and runs them\n// through processMessage concurrently. Guarded by `processingBatch` so the\n// tick never starts a second batch for a queue while one is still running.\nconst processBatch = async (\n runtime: QueueRuntime,\n queueName: string,\n queueService: QueueService\n): Promise<void> => {\n if (queueService.processingBatch) {\n return;\n }\n\n try {\n queueService.processingBatch = true;\n\n const messagesToProcess = moveToProcessing(queueService, runtime.config.batchSize);\n if (messagesToProcess.length === 0) {\n return;\n }\n\n queueService.processing.push(...messagesToProcess);\n\n await Promise.all(\n messagesToProcess.map(message => runtime.logging.runWithLogContext(\n { module: queueService.module, reqId: message.id },\n () => processMessage(runtime, queueName, message, queueService)\n ))\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n } finally {\n queueService.processingBatch = false;\n }\n};\n\nconst dispatchBatchIfDue = (\n runtime: QueueRuntime,\n queueName: string,\n queueService: QueueService,\n now: number\n): void => {\n if (queueService.processingBatch) {\n return;\n }\n const ready = queueService.incoming.filter(message => isMessageReady(message, now));\n if (ready.length === 0) {\n return;\n }\n\n const dueBySize = ready.length >= runtime.config.batchSize;\n const oldestReadyAge = now - Math.min(...ready.map(message => message.timestamp.getTime()));\n const dueByWindow = oldestReadyAge >= runtime.config.batchWindowMs;\n\n if (dueBySize || dueByWindow) {\n processBatch(runtime, queueName, queueService);\n }\n};\n\nconst maybeSavePersistence = (runtime: QueueRuntime, now: number): void => {\n if (!runtime.config.persistence.enabled) {\n return;\n }\n\n const saveInterval = runtime.config.persistence.saveInterval ?? 0;\n if (saveInterval > 0 && now - runtime.lastSavedAt >= saveInterval) {\n runtime.lastSavedAt = now;\n saveDirtyQueueStates(runtime);\n }\n};\n\n\n// The one timer for the whole plugin. Every tickIntervalMs: for every queue,\n// dispatch a batch if one is due (this is also how retried messages get\n// picked up - they wait in `incoming` like any delayed message); then save\n// persistence if its interval has elapsed.\nexport const tick = (runtime: QueueRuntime): void => {\n const now = Date.now();\n for (const [queueName, queueService] of Object.entries(runtime.queues)) {\n dispatchBatchIfDue(runtime, queueName, queueService, now);\n }\n maybeSavePersistence(runtime, now);\n};","import { QueueService, QueueManager, QueueRuntime, EventHandler } from \"./types\";\nimport {\n createQueueService,\n createQueueMessage,\n createDashboardSummary,\n createQueueSummary,\n moveDLQToIncoming,\n purgeDLQ,\n} from \"./core\";\nimport { loadQueueState, saveQueueState } from \"./persistence\";\n\nconst isValidQueueName = (queueName: string): boolean => {\n return typeof queueName === 'string' && queueName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(queueName);\n};\n\nconst validateQueueName = (queueName: string): void => {\n if (!queueName || typeof queueName !== 'string') {\n throw new Error('Queue name must be a non-empty string');\n }\n\n if (!isValidQueueName(queueName)) {\n throw new Error('Queue name must contain only alphanumeric characters, hyphens, and underscores');\n }\n};\n\nconst validateHandler = (handler: any): void => {\n if (typeof handler !== 'function') {\n throw new Error('Handler must be a function');\n }\n};\n\nexport const addQueue = async (runtime: QueueRuntime, queueName: string, handler: EventHandler, module?: string): Promise<void> => {\n try {\n validateQueueName(queueName);\n validateHandler(handler);\n\n if (runtime.queues[queueName]) {\n runtime.logging.warn({ module }, `Queue already exists: ${queueName}.`);\n return;\n }\n\n runtime.queues[queueName] = createQueueService(handler, module);\n\n if (runtime.config.persistence.enabled) {\n await loadQueueState(runtime, queueName);\n }\n\n runtime.logging.info({ module }, `Queue added: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error({ module, error: error.message }, `Failed to add queue ${queueName}`);\n throw error;\n }\n};\n\nexport const removeQueue = async (runtime: QueueRuntime, queueName: string): Promise<void> => {\n try {\n validateQueueName(queueName);\n\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n throw new Error(`Queue '${queueName}' does not exist`);\n }\n\n const totalMessages = queueService.incoming.length + queueService.processing.length;\n if (totalMessages > 0) {\n runtime.logging.warn(\n { module: queueService.module },\n `Removing queue with ${totalMessages} pending messages: ${queueName}`\n );\n }\n\n if (runtime.config.persistence.enabled) {\n await saveQueueState(runtime, queueName, queueService);\n }\n\n delete runtime.queues[queueName];\n\n runtime.logging.info({ module: queueService.module }, `Queue removed: ${queueName}`);\n } catch (error: any) {\n runtime.logging.error({ error: error.message }, `Failed to remove queue ${queueName}`);\n throw error;\n }\n};\n\nexport const hasQueue = (runtime: QueueRuntime, queueName: string): boolean => {\n try {\n validateQueueName(queueName);\n return queueName in runtime.queues;\n } catch {\n return false;\n }\n};\n\nexport const listQueues = (runtime: QueueRuntime, module?: string): string[] => {\n return Object.keys(runtime.queues)\n .filter(queueName => !module || runtime.queues[queueName].module === module)\n .sort();\n};\n\nexport const getQueueStats = (runtime: QueueRuntime, queueName: string): QueueService | null => {\n try {\n validateQueueName(queueName);\n return runtime.queues[queueName] || null;\n } catch {\n return null;\n }\n};\n\nexport const getQueuesMap = (runtime: QueueRuntime): Record<string, QueueService> => {\n // Return a copy to prevent external modification\n return { ...runtime.queues };\n};\n\nexport const getDashboardSummary = (runtime: QueueRuntime) =>\n createDashboardSummary(runtime.queues, runtime.config);\n\nexport const getQueueSummary = (runtime: QueueRuntime, queueName: string) => {\n const queue = runtime.queues[queueName];\n if (!queue) {\n return null;\n }\n\n return {\n stats: createQueueSummary(queue, runtime.config),\n messages: {\n incoming: queue.incoming,\n processing: queue.processing,\n dlq: queue.dlq\n }\n };\n};\n\nexport const enqueueMessage = async (runtime: QueueRuntime, queueName: string, body: any, attributes: any) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const message = createQueueMessage(body, attributes);\n queueService.incoming.push(message);\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return { id: message.id, queueName };\n};\n\nexport const processDlq = async (runtime: QueueRuntime, queueName: string) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const processedCount = moveDLQToIncoming(queueService);\n if (processedCount === 0) {\n return { status: \"success\", message: \"No messages in DLQ to process\", processed: 0 };\n }\n\n runtime.logging.warn(\n { module: queueService.module },\n `Moving ${processedCount} messages from DLQ for ${queueName}`\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return {\n status: \"success\",\n message: `Moved ${processedCount} messages from DLQ to processing queue`,\n processed: processedCount\n };\n};\n\nexport const purgeDlq = async (runtime: QueueRuntime, queueName: string) => {\n const queueService = runtime.queues[queueName];\n if (!queueService) {\n return null;\n }\n\n const purgedCount = purgeDLQ(queueService);\n if (purgedCount === 0) {\n return { status: \"success\", message: \"No messages in DLQ to purge\", purged: 0 };\n }\n\n runtime.logging.warn(\n { module: queueService.module },\n `Purging ${purgedCount} messages from DLQ for ${queueName}`\n );\n\n if (runtime.config.persistence.enabled) {\n runtime.dirtyQueues.add(queueName);\n }\n\n return {\n status: \"success\",\n message: `Purged ${purgedCount} messages from DLQ`,\n purged: purgedCount\n };\n};\n\nexport const getMessageHistory = (runtime: QueueRuntime, queueName: string, limit = 50) => {\n return runtime.messageHistory\n .filter(entry => entry.queueName === queueName)\n .slice(-limit)\n .reverse();\n};\n\nexport const subscribeTopic = (runtime: QueueRuntime, topicName: string, queueName: string): void => {\n const subscribers = runtime.topics[topicName] ?? (runtime.topics[topicName] = []);\n if (!subscribers.includes(queueName)) {\n subscribers.push(queueName);\n }\n\n runtime.logging.info({ topicName, queueName }, `Subscribed queue '${queueName}' to topic '${topicName}'`);\n};\n\n// Fans a published message out to every queue subscribed to the topic, reusing\n// enqueueMessage so each subscriber gets its own independent backlog/DLQ/retries.\nexport const publishTopic = async (runtime: QueueRuntime, topicName: string, body: any, attributes: any) => {\n const subscribers = runtime.topics[topicName];\n if (!subscribers || subscribers.length === 0) {\n return null;\n }\n\n const results = await Promise.all(\n subscribers.map(queueName => enqueueMessage(runtime, queueName, body, attributes))\n );\n\n return results.filter((result): result is { id: string; queueName: string } => result !== null);\n};\n\nexport const listTopics = (runtime: QueueRuntime): Record<string, string[]> => {\n // Return a copy to prevent external modification\n return Object.fromEntries(Object.entries(runtime.topics).map(([name, queues]) => [name, [...queues]]));\n};\n\n// The one place runtime gets bound into the public app.queues shape -\n// Fastify needs a plain object of methods with no runtime param, so this\n// wraps each plain function above with `runtime` pre-applied. Everything\n// above this line is a normal function you can call directly with an\n// explicit runtime, e.g. in tests.\nexport const createQueueManager = (runtime: QueueRuntime): QueueManager => ({\n addQueue: (queueName, handler, module) => addQueue(runtime, queueName, handler, module),\n removeQueue: (queueName) => removeQueue(runtime, queueName),\n hasQueue: (queueName) => hasQueue(runtime, queueName),\n listQueues: (module) => listQueues(runtime, module),\n getQueueStats: (queueName) => getQueueStats(runtime, queueName),\n getQueuesMap: () => getQueuesMap(runtime),\n getConfig: () => runtime.config,\n getDashboardSummary: () => getDashboardSummary(runtime),\n getQueueSummary: (queueName) => getQueueSummary(runtime, queueName),\n enqueueMessage: (queueName, body, attributes) => enqueueMessage(runtime, queueName, body, attributes),\n processDlq: (queueName) => processDlq(runtime, queueName),\n purgeDlq: (queueName) => purgeDlq(runtime, queueName),\n getMessageHistory: (queueName, limit) => getMessageHistory(runtime, queueName, limit),\n subscribeTopic: (topicName, queueName) => subscribeTopic(runtime, topicName, queueName),\n publishTopic: (topicName, body, attributes) => publishTopic(runtime, topicName, body, attributes),\n listTopics: () => listTopics(runtime),\n});","import { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\n\n// Function to register all routes\nexport const registerQueueRoutes = (\n app: FastifyInstance,\n prefix: string = \"\"\n): void => {\n app.get(`/${prefix}/dashboard`,\n async (_: FastifyRequest, reply: FastifyReply) => {\n const summary = app.queues.getDashboardSummary();\n return reply.status(200).send(summary);\n });\n\n app.get<{ Params: { queue: string } }>(`/${prefix}/:queue`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const summary = app.queues.getQueueSummary(queueName);\n if (summary === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(summary);\n });\n\n app.post<{ Params: { queue: string }; Body: any }>(`/${prefix}/:queue`,\n async (request: FastifyRequest<{ Params: { queue: string }; Body: any }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.enqueueMessage(queueName, request.body, request.headers as Record<string, any>);\n if (result === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(result);\n });\n\n // Topic publish: fans the message out to every queue subscribed to the\n // topic (see queue-plugin/decorator.ts:publishTopic). Registered as its\n // own static path segment, so it never collides with POST /:queue.\n app.post<{ Params: { topic: string }; Body: any }>(`/${prefix}/topics/:topic`,\n async (request: FastifyRequest<{ Params: { topic: string }; Body: any }>, reply: FastifyReply) => {\n const topicName = request.params.topic;\n const results = await app.queues.publishTopic(topicName, request.body, request.headers as Record<string, any>);\n if (results === null) {\n return reply.status(404).send({ error: \"Topic not found\" });\n }\n return reply.status(200).send({ topicName, results });\n });\n\n app.get<{ Params: { queue: string } }>(`/${prefix}/:queue/process-dlq`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.processDlq(queueName);\n if (result === null) {\n return reply.status(404).send(result);\n }\n return reply.status(200).send(result);\n });\n\n app.post<{ Params: { queue: string } }>(`/${prefix}/:queue/purge-dlq`,\n async (request: FastifyRequest<{ Params: { queue: string } }>, reply: FastifyReply) => {\n const queueName = request.params.queue;\n const result = await app.queues.purgeDlq(queueName);\n if (result === null) {\n return reply.status(404).send({ error: \"Queue not found\" });\n }\n return reply.status(200).send(result);\n });\n};"],"mappings":"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","names":[]}
|