diff --git a/README.md b/README.md index 6bed3cf..03c1563 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,10 @@ Methods: - `start()` - `stop()` -- `optimize(buffer, width, quality, allowSVG?)` (auto-starts on first use) -- `fetchAndOptimize(url, width, quality, allowSVG?)` (auto-starts on first use) +- `optimize(buffer, width, quality, allowSVG?, enqueueOptions?)` (auto-starts on first use) +- `fetchAndOptimize(url, width, quality, allowSVG?, enqueueOptions?)` (auto-starts on first use) + +`enqueueOptions` is forwarded to `@platformatic/job-queue` `enqueueAndWait()` (for example: `timeout`, `maxAttempts`, `resultTTL`). ### `createQueue(options?)` diff --git a/src/queue.ts b/src/queue.ts index 9d0aa11..bdcb005 100644 --- a/src/queue.ts +++ b/src/queue.ts @@ -1,4 +1,4 @@ -import type { Queue as JobQueue, MemoryStorage } from '@platformatic/job-queue' +import type { EnqueueAndWaitOptions, Queue as JobQueue, MemoryStorage } from '@platformatic/job-queue' import { InternalServerError } from 'http-errors-enhanced' import { randomUUID } from 'node:crypto' import { fetchAndOptimize, optimize } from './operations.ts' @@ -86,26 +86,44 @@ export class Queue { this.#started = false } - async optimize (buffer: Buffer, width: number, quality: number, allowSVG = false): Promise { - const result = await this.#enqueueAndWait({ - type: 'optimize', - buffer: buffer.toString('base64'), - width, - quality, - allowSVG - }) + async optimize ( + buffer: Buffer, + width: number, + quality: number, + allowSVG = false, + options?: EnqueueAndWaitOptions + ): Promise { + const result = await this.#enqueueAndWait( + { + type: 'optimize', + buffer: buffer.toString('base64'), + width, + quality, + allowSVG + }, + options + ) return Buffer.from(result.buffer, 'base64') } - async fetchAndOptimize (url: string, width: number, quality: number, allowSVG = false): Promise> { - const result = await this.#enqueueAndWait({ - type: 'fetchAndOptimize', - url, - width, - quality, - allowSVG - }) + async fetchAndOptimize ( + url: string, + width: number, + quality: number, + allowSVG = false, + options?: EnqueueAndWaitOptions + ): Promise> { + const result = await this.#enqueueAndWait( + { + type: 'fetchAndOptimize', + url, + width, + quality, + allowSVG + }, + options + ) return { buffer: Buffer.from(result.buffer, 'base64'), @@ -114,12 +132,12 @@ export class Queue { } } - async #enqueueAndWait (payload: QueuePayload): Promise> { + async #enqueueAndWait (payload: QueuePayload, options?: EnqueueAndWaitOptions): Promise> { if (!this.#queue || !this.#started) { await this.start() } - return this.#queue!.enqueueAndWait(randomUUID(), payload) + return this.#queue!.enqueueAndWait(randomUUID(), payload, options) } async #execute ({ payload }: Job): Promise> { diff --git a/test/queue.test.ts b/test/queue.test.ts index 3579afd..fac68b8 100644 --- a/test/queue.test.ts +++ b/test/queue.test.ts @@ -29,6 +29,39 @@ class BrokenJobQueueModuleQueue extends Queue { } } +class EnqueueOptionsSpyJobQueue { + static calls: Array<{ payload: any; options: any }> = [] + + execute (): void {} + + async start (): Promise {} + + async stop (): Promise {} + + async enqueueAndWait (_id: string, payload: any, options?: any): Promise { + EnqueueOptionsSpyJobQueue.calls.push({ payload, options }) + + if (payload.type === 'optimize') { + return { buffer: payload.buffer } + } + + return { + buffer: Buffer.from('queued-image').toString('base64'), + contentType: 'image/webp', + cacheControl: 'public, max-age=10' + } + } +} + +class EnqueueOptionsQueue extends Queue { + protected static async loadJobQueueModule (): Promise { + return { + Queue: EnqueueOptionsSpyJobQueue, + MemoryStorage: class {} + } + } +} + const fixtures = join(import.meta.dirname, 'fixtures', 'before') const width = 120 const quality = 60 @@ -130,6 +163,25 @@ test('Queue optimizes images through queue jobs', async () => { await optimizer.stop() }) +test('Queue optimize and fetchAndOptimize forward enqueue options', async () => { + EnqueueOptionsSpyJobQueue.calls = [] + + const optimizer = new EnqueueOptionsQueue() + await optimizer.start() + + const optimizeOptions = { timeout: 1200, maxAttempts: 4, resultTTL: 30_000 } + await optimizer.optimize(Buffer.from('source'), width, quality, false, optimizeOptions) + + const fetchOptions = { timeout: 2400, maxAttempts: 2, resultTTL: 60_000 } + await optimizer.fetchAndOptimize('https://queue-images.example/source.webp', width, quality, false, fetchOptions) + + equal(EnqueueOptionsSpyJobQueue.calls.length, 2) + deepEqual(EnqueueOptionsSpyJobQueue.calls[0].options, optimizeOptions) + deepEqual(EnqueueOptionsSpyJobQueue.calls[1].options, fetchOptions) + + await optimizer.stop() +}) + test('Queue fetchAndOptimize returns buffer and response metadata', async () => { const optimizer = new Queue() await optimizer.start()