@workglow/job-queue 0.0.52

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.
Files changed (43) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +694 -0
  3. package/dist/browser.js +1075 -0
  4. package/dist/browser.js.map +20 -0
  5. package/dist/bun.js +1375 -0
  6. package/dist/bun.js.map +22 -0
  7. package/dist/common-server.d.ts +9 -0
  8. package/dist/common-server.d.ts.map +1 -0
  9. package/dist/common.d.ts +18 -0
  10. package/dist/common.d.ts.map +1 -0
  11. package/dist/job/IJobQueue.d.ts +160 -0
  12. package/dist/job/IJobQueue.d.ts.map +1 -0
  13. package/dist/job/Job.d.ts +87 -0
  14. package/dist/job/Job.d.ts.map +1 -0
  15. package/dist/job/JobError.d.ts +61 -0
  16. package/dist/job/JobError.d.ts.map +1 -0
  17. package/dist/job/JobQueue.d.ts +272 -0
  18. package/dist/job/JobQueue.d.ts.map +1 -0
  19. package/dist/job/JobQueueEventListeners.d.ts +30 -0
  20. package/dist/job/JobQueueEventListeners.d.ts.map +1 -0
  21. package/dist/limiter/CompositeLimiter.d.ts +18 -0
  22. package/dist/limiter/CompositeLimiter.d.ts.map +1 -0
  23. package/dist/limiter/ConcurrencyLimiter.d.ts +24 -0
  24. package/dist/limiter/ConcurrencyLimiter.d.ts.map +1 -0
  25. package/dist/limiter/DelayLimiter.d.ts +18 -0
  26. package/dist/limiter/DelayLimiter.d.ts.map +1 -0
  27. package/dist/limiter/EvenlySpacedRateLimiter.d.ts +35 -0
  28. package/dist/limiter/EvenlySpacedRateLimiter.d.ts.map +1 -0
  29. package/dist/limiter/ILimiter.d.ts +27 -0
  30. package/dist/limiter/ILimiter.d.ts.map +1 -0
  31. package/dist/limiter/InMemoryRateLimiter.d.ts +32 -0
  32. package/dist/limiter/InMemoryRateLimiter.d.ts.map +1 -0
  33. package/dist/limiter/NullLimiter.d.ts +19 -0
  34. package/dist/limiter/NullLimiter.d.ts.map +1 -0
  35. package/dist/limiter/PostgresRateLimiter.d.ts +53 -0
  36. package/dist/limiter/PostgresRateLimiter.d.ts.map +1 -0
  37. package/dist/limiter/SqliteRateLimiter.d.ts +44 -0
  38. package/dist/limiter/SqliteRateLimiter.d.ts.map +1 -0
  39. package/dist/node.js +1374 -0
  40. package/dist/node.js.map +22 -0
  41. package/dist/types.d.ts +7 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/package.json +61 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/job/IJobQueue.ts", "../src/job/JobError.ts", "../src/job/Job.ts", "../src/job/JobQueue.ts", "../src/limiter/ILimiter.ts", "../src/limiter/NullLimiter.ts", "../src/limiter/CompositeLimiter.ts", "../src/limiter/ConcurrencyLimiter.ts", "../src/limiter/DelayLimiter.ts", "../src/limiter/EvenlySpacedRateLimiter.ts", "../src/limiter/InMemoryRateLimiter.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { IQueueStorage, JobStatus } from \"@workglow/storage\";\nimport { ILimiter } from \"../limiter/ILimiter\";\nimport { Job } from \"./Job\";\nimport { JobQueueStats } from \"./JobQueue\";\nimport { JobProgressListener } from \"./JobQueueEventListeners\";\n\nexport { JobStatus };\n/**\n * Common options for all job queues\n */\n\nexport interface JobQueueOptions<Input, Output> {\n /**\n * Time in milliseconds after which completed jobs should be deleted\n * Set to 0 to delete immediately, undefined to never delete\n */\n deleteAfterCompletionMs?: number;\n /**\n * Time in milliseconds after which failed jobs should be deleted\n * Set to 0 to delete immediately, undefined to never delete\n */\n deleteAfterFailureMs?: number;\n /**\n * Time in milliseconds after which disabled jobs should be deleted\n * Set to 0 to delete immediately, undefined to never delete\n */\n deleteAfterDisabledMs?: number;\n /**\n * How often to check for new jobs in milliseconds\n */\n waitDurationInMilliseconds?: number;\n /**\n * Rate limiter to control job execution\n */\n limiter?: ILimiter;\n\n /**\n * Storage to use for the job queue\n */\n storage?: IQueueStorage<Input, Output>;\n} /**\n * Defines how a job queue operates in different contexts\n */\n\nexport enum QueueMode {\n /**\n * Queue operates in client mode only - can submit jobs and receive progress updates\n * but does not process jobs\n */\n CLIENT = \"CLIENT\",\n\n /**\n * Queue operates in server mode only - processes jobs but does not accept new submissions\n */\n SERVER = \"SERVER\",\n\n /**\n * Queue operates in both client and server mode - can submit and process jobs\n */\n BOTH = \"BOTH\",\n}\n\n/**\n * Interface for a job queue\n */\nexport interface IJobQueue<Input, Output> {\n /**\n * The name of the queue\n */\n queueName: string;\n\n /**\n * Add a job to the queue\n * @param job The job to add\n * @returns A promise that resolves to the job ID\n */\n add(job: Job<Input, Output>): Promise<unknown>;\n\n /**\n * Get a job by its ID\n * @param id The job ID\n * @returns A promise that resolves to the job, or undefined if not found\n */\n get(id: unknown): Promise<Job<Input, Output> | undefined>;\n\n /**\n * Wait for a job to complete\n * @param jobId The job ID to wait for\n * @returns A promise that resolves to the job output, or rejects with an error\n */\n waitFor(jobId: unknown): Promise<Output | undefined>;\n\n /**\n * Abort a job\n * @param jobId The job ID to abort\n * @returns A promise that resolves to true if the job was aborted, false otherwise\n */\n abort(jobId: unknown): Promise<void>;\n\n /**\n * Peek at jobs in the queue\n * @param status Optional status to filter by\n * @param num Optional number of jobs to return\n * @returns A promise that resolves to an array of jobs\n */\n peek(status?: JobStatus, num?: number): Promise<Job<Input, Output>[]>;\n\n /**\n * Get the size of the queue\n * @param status Optional status to filter by\n * @returns A promise that resolves to the number of jobs\n */\n size(status?: JobStatus): Promise<number>;\n\n /**\n * Get the output for a job by its input\n * @param input The job input\n * @returns A promise that resolves to the job output, or null if not found\n */\n outputForInput(input: Input): Promise<Output | null>;\n\n /**\n * Get jobs by their run ID\n * @param jobRunId The job run ID\n * @returns A promise that resolves to an array of jobs\n */\n getJobsByRunId(jobRunId: string): Promise<Job<Input, Output>[]>;\n\n /**\n * Get the stats for the job queue\n * @returns The job queue stats\n */\n getStats(): JobQueueStats;\n\n /**\n * Update the progress of a job\n * @param jobId The job ID\n * @param progress The progress value\n * @param message Optional message to set\n * @param details Optional details to set\n * @returns A promise that resolves when the progress is updated\n */\n updateProgress(\n jobId: unknown,\n progress: number,\n message?: string,\n details?: Record<string, any> | null\n ): Promise<void>;\n onJobProgress(jobId: unknown, listener: JobProgressListener): () => void;\n\n /**\n * Start the job queue\n * @param mode Optional mode to set\n * @returns A promise that resolves to the job queue\n */\n start(mode?: QueueMode): Promise<this>;\n\n /**\n * Stop the job queue\n * @returns A promise that resolves to the job queue\n */\n stop(): Promise<this>;\n\n /**\n * Clear the job queue\n * @returns A promise that resolves to the job queue\n */\n clear(): Promise<this>;\n\n /**\n * Restart the job queue\n * @returns A promise that resolves to the job queue\n */\n restart(): Promise<this>;\n\n /**\n * Get the next job from the queue\n * @returns A promise that resolves to the next job, or undefined if the queue is empty\n */\n next(): Promise<Job<Input, Output> | undefined>;\n}\n",
6
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { BaseError } from \"@workglow/util\";\n\nexport class JobError extends BaseError {\n public static type: string = \"JobError\";\n public retryable = false;\n constructor(public message: string) {\n super(message);\n }\n}\n\n/**\n * A job error that is caused by a job not being found\n *\n * Examples: job.id is undefined, job.id is not found in the storage, etc.\n */\nexport class JobNotFoundError extends JobError {\n public static type: string = \"JobNotFoundError\";\n constructor(message: string = \"Job not found\") {\n super(message);\n }\n}\n\n/**\n * A job error that is retryable\n *\n * Examples: network timeouts, temporary unavailability of an external service, or rate-limiting\n */\nexport class RetryableJobError extends JobError {\n public static type: string = \"RetryableJobError\";\n constructor(\n message: string,\n public retryDate?: Date\n ) {\n super(message);\n this.retryable = true;\n }\n}\n\n/**\n * A job error that is not retryable\n *\n * Examples: invalid input, missing required parameters, or a permanent failure of\n * an external service, permission errors, running out of money for an API, etc.\n */\nexport class PermanentJobError extends JobError {\n public static type: string = \"PermanentJobError\";\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * A job error that is caused by an abort signal,\n * meaning the client aborted the job on purpose,\n * not by the queue going down or similar.\n *\n * Example: job.abort()\n */\nexport class AbortSignalJobError extends PermanentJobError {\n public static type: string = \"AbortSignalJobError\";\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * A job error that is caused by a job being disabled\n *\n * Examples: job.disable()\n */\nexport class JobDisabledError extends PermanentJobError {\n public static type: string = \"JobDisabledError\";\n}\n",
7
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { JobStatus } from \"./IJobQueue\";\nimport { JobError } from \"./JobError\";\nimport type { JobQueue } from \"./JobQueue\";\nimport type { JobProgressListener } from \"./JobQueueEventListeners\";\n\n/**\n * Context for job execution\n */\nexport interface IJobExecuteContext {\n signal: AbortSignal;\n updateProgress: (\n progress: number,\n message?: string,\n details?: Record<string, any> | null\n ) => Promise<void>;\n}\n\n/**\n * Details about a job that reflect the structure in the database.\n */\nexport type JobConstructorParam<Input, Output> = {\n id?: unknown;\n jobRunId?: string;\n queueName?: string;\n input: Input;\n output?: Output | null;\n error?: string | null;\n errorCode?: string | null;\n fingerprint?: string;\n maxRetries?: number;\n status?: JobStatus;\n createdAt?: Date;\n deadlineAt?: Date | null;\n lastRanAt?: Date | null;\n runAfter?: Date | null;\n completedAt?: Date | null;\n runAttempts?: number;\n progress?: number;\n progressMessage?: string;\n progressDetails?: Record<string, any> | null;\n};\n\n/**\n * A job that can be executed by a JobQueue.\n *\n * @template Input - The type of the job's input\n * @template Output - The type of the job's output\n */\nexport class Job<Input, Output> {\n public id: unknown;\n public jobRunId: string | undefined;\n public queueName: string | undefined;\n public input: Input;\n public maxRetries: number;\n public createdAt: Date;\n public fingerprint: string | undefined;\n public status: JobStatus = JobStatus.PENDING;\n public runAfter: Date;\n public output: Output | null = null;\n public runAttempts: number = 0;\n public lastRanAt: Date | null = null;\n public completedAt: Date | null = null;\n public deadlineAt: Date | null = null;\n public error: string | null = null;\n public errorCode: string | null = null;\n public progress: number = 0;\n public progressMessage: string = \"\";\n public progressDetails: Record<string, any> | null = null;\n public queue: JobQueue<Input, Output> | undefined;\n\n constructor({\n queueName,\n input,\n jobRunId,\n id,\n error = null,\n errorCode = null,\n fingerprint = undefined,\n output = null,\n maxRetries = 10,\n createdAt = new Date(),\n completedAt = null,\n status = JobStatus.PENDING,\n deadlineAt = null,\n runAttempts = 0,\n lastRanAt = null,\n runAfter = new Date(),\n progress = 0,\n progressMessage = \"\",\n progressDetails = null,\n }: JobConstructorParam<Input, Output>) {\n this.runAfter = runAfter ?? new Date();\n this.createdAt = createdAt ?? new Date();\n this.lastRanAt = lastRanAt ?? null;\n this.deadlineAt = deadlineAt ?? null;\n this.completedAt = completedAt ?? null;\n\n this.queueName = queueName;\n this.id = id;\n this.jobRunId = jobRunId;\n this.status = status;\n this.fingerprint = fingerprint;\n this.input = input;\n this.maxRetries = maxRetries;\n this.runAttempts = runAttempts;\n this.output = output;\n this.error = error;\n this.errorCode = errorCode;\n this.progress = progress;\n this.progressMessage = progressMessage;\n this.progressDetails = progressDetails;\n }\n\n async execute(input: Input, context: IJobExecuteContext): Promise<Output> {\n throw new JobError(\"Method not implemented.\");\n }\n\n private progressListeners: Set<JobProgressListener> = new Set();\n\n /**\n * Update the job's progress\n * @param progress - Progress value between 0 and 100\n * @param message - Optional progress message\n * @param details - Optional progress details\n */\n public async updateProgress(\n progress: number,\n message: string = \"\",\n details: Record<string, any> | null = null\n ) {\n this.progress = progress;\n this.progressMessage = message;\n this.progressDetails = details;\n\n // Notify direct listeners\n for (const listener of this.progressListeners) {\n listener(progress, message, details);\n }\n\n await this.queue?.updateProgress(this.id, progress, message, details);\n }\n\n /**\n * Adds a progress listener for this job\n *\n * Only used if run directly (not via a queue)\n *\n * @param listener - The callback function to be called when progress updates occur\n * @returns A cleanup function to remove the listener\n */\n public onJobProgress(listener: JobProgressListener): () => void {\n this.progressListeners.add(listener);\n\n return () => {\n this.progressListeners.delete(listener);\n };\n }\n}\n",
8
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n InMemoryQueueStorage,\n IQueueStorage,\n JobStorageFormat,\n QUEUE_STORAGE,\n} from \"@workglow/storage\";\nimport { EventEmitter, globalServiceRegistry, sleep } from \"@workglow/util\";\nimport { ILimiter, JOB_LIMITER } from \"../limiter/ILimiter\";\nimport { NullLimiter } from \"../limiter/NullLimiter\";\nimport { IJobQueue, JobQueueOptions, JobStatus, QueueMode } from \"./IJobQueue\";\nimport { Job, JobConstructorParam } from \"./Job\";\nimport {\n AbortSignalJobError,\n JobDisabledError,\n JobError,\n JobNotFoundError,\n PermanentJobError,\n RetryableJobError,\n} from \"./JobError\";\nimport {\n JobProgressListener,\n JobQueueEventListener,\n JobQueueEventListeners,\n JobQueueEventParameters,\n JobQueueEvents,\n} from \"./JobQueueEventListeners\";\n\n/**\n * Statistics tracked for the job queue\n */\nexport interface JobQueueStats {\n totalJobs: number;\n completedJobs: number;\n failedJobs: number;\n abortedJobs: number;\n retriedJobs: number;\n disabledJobs: number;\n averageProcessingTime?: number;\n lastUpdateTime: Date;\n}\n\ntype JobClass<Input, Output> = new (\n param: JobConstructorParam<Input, Output>\n) => Job<Input, Output>;\n\n/**\n * Base class for implementing job queues with different storage backends.\n */\nexport class JobQueue<Input, Output, QueueJob extends Job<Input, Output> = Job<Input, Output>>\n implements IJobQueue<Input, Output>\n{\n /**\n * The name of the queue\n */\n public readonly queueName: string;\n\n /**\n * The class of the job to be used in the queue\n */\n public readonly jobClass: JobClass<Input, Output>;\n\n /**\n * The options for the queue\n */\n protected options: JobQueueOptions<Input, Output>;\n\n constructor(\n queueName: string,\n jobClass: JobClass<Input, Output>,\n options: JobQueueOptions<Input, Output>\n ) {\n this.queueName = queueName;\n this.jobClass = jobClass;\n const { limiter, storage, ...rest } = options;\n this.options = {\n waitDurationInMilliseconds: 100,\n ...rest,\n };\n\n if (limiter) {\n this.limiter = limiter;\n } else {\n try {\n this.limiter = globalServiceRegistry.get(JOB_LIMITER);\n } catch (err) {\n console.warn(\"Warning: did not find job limiter in global DI\", err);\n this.limiter = new NullLimiter();\n }\n }\n\n if (storage) {\n this.storage = storage;\n } else {\n try {\n this.storage = globalServiceRegistry.get(QUEUE_STORAGE);\n } catch (err) {\n console.warn(\"Warning: did not find queue storage in global DI\", err);\n this.storage = new InMemoryQueueStorage<Input, Output>(queueName);\n }\n }\n\n this.stats = {\n totalJobs: 0,\n completedJobs: 0,\n failedJobs: 0,\n abortedJobs: 0,\n retriedJobs: 0,\n disabledJobs: 0,\n lastUpdateTime: new Date(),\n };\n }\n\n // ========================================================================\n // Job Management public methods\n // ========================================================================\n\n /**\n * Gets a job from the queue\n * @param id The ID of the job to get\n */\n public async get(id: unknown) {\n if (!id) throw new JobNotFoundError(\"Cannot get undefined job\");\n const job = await this.storage.get(id);\n if (!job) return undefined;\n return this.storageToClass(job);\n }\n\n /**\n * Adds a job to the queue\n * @param job The job to add\n */\n public async add(job: QueueJob) {\n const jobId = await this.storage.add(this.classToStorage(job));\n return jobId;\n }\n\n /**\n * Gets the next job from the queue\n */\n public async next() {\n const job = await this.storage.next();\n if (!job) return undefined;\n return this.storageToClass(job);\n }\n\n /**\n * Peek into the queue\n * @param status The status of the job to peek at\n * @param num The number of jobs to peek at\n */\n public async peek(status?: JobStatus, num?: number) {\n const jobs = await this.storage.peek(status, num);\n return jobs.map((job) => this.storageToClass(job));\n }\n\n /**\n * Gets the size of the queue\n * @param status The status of the jobs to get the size of\n */\n public async size(status?: JobStatus) {\n return this.storage.size(status);\n }\n\n /**\n * Deletes a job from the queue\n * @param id The ID of the job to delete\n */\n public async delete(id: unknown) {\n if (!id) throw new JobNotFoundError(\"Cannot delete undefined job\");\n await this.storage.delete(id);\n }\n\n public async disable(id: unknown) {\n if (!id) throw new JobNotFoundError(\"Cannot disable undefined job\");\n const job = await this.get(id);\n if (!job) throw new JobNotFoundError(`Job ${id} not found`);\n await this.disableJob(job);\n }\n\n /**\n * Returns current queue statistics\n */\n public getStats(): JobQueueStats {\n return { ...this.stats };\n }\n\n /**\n * Gets the output for an input\n * @param input The input to get the output for\n */\n public async outputForInput(input: Input) {\n if (!input) throw new JobNotFoundError(\"Cannot get output for undefined input\");\n return this.storage.outputForInput(input);\n }\n\n /**\n * Executes a job with the provided abort signal.\n * Can be overridden by implementations to add custom execution logic.\n */\n public async executeJob(job: Job<Input, Output>, signal: AbortSignal): Promise<Output> {\n if (!job) throw new JobNotFoundError(\"Cannot execute null or undefined job\");\n return await job.execute(job.input, {\n signal,\n updateProgress: this.updateProgress.bind(this, job.id),\n });\n }\n\n /**\n * Returns a promise that resolves when the job completes\n */\n public async waitFor(jobId: unknown): Promise<Output | undefined> {\n if (!jobId) throw new JobNotFoundError(\"Cannot wait for undefined job\");\n\n const { promise, resolve, reject } = Promise.withResolvers<Output>();\n promise.catch(() => {});\n const promises = this.activeJobPromises.get(jobId) || [];\n promises.push({ resolve, reject });\n this.activeJobPromises.set(jobId, promises);\n\n const job = await this.get(jobId);\n if (!job) throw new JobNotFoundError(`Job ${jobId} not found`);\n\n if (job.status === JobStatus.COMPLETED) {\n return job.output as Output;\n }\n if (job.status === JobStatus.DISABLED) {\n return undefined;\n }\n if (job.status === JobStatus.FAILED) {\n throw this.buildErrorFromJob(job);\n }\n return promise as Promise<Output>;\n }\n\n /**\n * Reconstructs an error instance from a stored failed job\n */\n protected buildErrorFromJob(job: Job<Input, Output>): JobError {\n const errorMessage = job.error || \"Job failed\";\n if (job.errorCode === \"PermanentJobError\") {\n return new PermanentJobError(errorMessage);\n }\n if (job.errorCode === \"RetryableJobError\") {\n return new RetryableJobError(errorMessage);\n }\n if (job.errorCode === \"AbortSignalJobError\") {\n return new AbortSignalJobError(errorMessage);\n }\n if (job.errorCode === \"JobDisabledError\") {\n return new JobDisabledError(errorMessage);\n }\n return new JobError(errorMessage);\n }\n\n /**\n * Updates the progress of a job\n * @param jobId - The ID of the job to update\n * @param progress - Progress value between 0 and 100\n * @param message - Optional message describing the current progress state\n * @param details - Optional structured data about the progress\n * @returns A promise that resolves when the progress is updated\n * @note If the job is completed, failed, or aborted, the progress will not be updated\n */\n public async updateProgress(\n jobId: unknown,\n progress: number,\n message: string = \"\",\n details: Record<string, any> | null = null\n ): Promise<void> {\n const job = await this.get(jobId);\n if (!job) throw new JobNotFoundError(`Job ${jobId} not found`);\n\n if (\n [JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.ABORTING, JobStatus.DISABLED].includes(\n job.status\n )\n ) {\n return;\n }\n\n // Validate progress value\n progress = Math.max(0, Math.min(100, progress));\n\n job.progress = progress;\n job.progressMessage = message;\n job.progressDetails = details;\n\n await this.saveProgress(jobId, progress, message, details ?? null);\n this.announceProgress(jobId, progress, message, details ?? null);\n }\n\n /**\n * Adds a progress listener for a specific job\n * @param jobId - The ID of the job to listen to\n * @param listener - The callback function to be called when progress updates occur\n * @returns A cleanup function to remove the listener\n */\n public onJobProgress(jobId: unknown, listener: JobProgressListener): () => void {\n if (!this.jobProgressListeners.has(jobId)) {\n this.jobProgressListeners.set(jobId, new Set());\n }\n const listeners = this.jobProgressListeners.get(jobId)!;\n listeners.add(listener);\n\n return () => {\n const listeners = this.jobProgressListeners.get(jobId);\n if (listeners) {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.jobProgressListeners.delete(jobId);\n }\n }\n };\n }\n\n /**\n * Starts the job queue based on its operating mode\n */\n public async start(mode: QueueMode = QueueMode.BOTH) {\n if (this.running) {\n return this;\n }\n this.mode = mode;\n\n this.running = true;\n this.events.emit(\"queue_start\", this.queueName);\n\n // Start job processing if in SERVER or BOTH mode\n if (this.mode === QueueMode.SERVER || this.mode === QueueMode.BOTH) {\n await this.fixupJobs();\n await this.processJobs();\n }\n\n // Start job monitoring if in CLIENT or BOTH mode\n if (this.mode === QueueMode.CLIENT || this.mode === QueueMode.BOTH) {\n await this.monitorJobs();\n }\n\n return this;\n }\n\n /**\n * Stops the job queue and aborts all active jobs\n */\n public async stop() {\n if (!this.running) return this;\n this.running = false;\n\n // Wait for pending operations to settle\n const size = await this.size(JobStatus.PROCESSING);\n const sleepTime = Math.max(100, size * 2);\n await sleep(sleepTime);\n\n // Abort all active jobs\n for (const [jobId] of this.activeJobAbortControllers.entries()) {\n this.abort(jobId);\n }\n\n // Reject all waiting promises\n this.activeJobPromises.forEach((promises) =>\n promises.forEach(({ reject }) => reject(new PermanentJobError(\"Queue Stopped\")))\n );\n\n // Wait for abort operations to settle\n await sleep(sleepTime);\n\n this.events.emit(\"queue_stop\", this.queueName);\n return this;\n }\n\n /**\n * Clears all jobs and resets queue state\n */\n public async clear() {\n await this.storage.deleteAll();\n this.activeJobAbortControllers.clear();\n this.activeJobPromises.clear();\n this.processingTimes.clear();\n this.lastKnownProgress.clear();\n this.jobProgressListeners.clear();\n this.stats = {\n totalJobs: 0,\n completedJobs: 0,\n failedJobs: 0,\n abortedJobs: 0,\n retriedJobs: 0,\n disabledJobs: 0,\n lastUpdateTime: new Date(),\n };\n this.emitStatsUpdate();\n return this;\n }\n\n /**\n * Restarts the job queue\n */\n public async restart() {\n await this.stop();\n await this.clear();\n await this.start();\n return this;\n }\n\n // -------------- Job Run Management --------------\n\n /**\n * Aborts all jobs in a job run\n */\n public async abortJobRun(jobRunId: string): Promise<void> {\n if (!jobRunId) throw new JobNotFoundError(\"Cannot abort job run with undefined jobRunId\");\n const jobs = await this.getJobsByRunId(jobRunId);\n await Promise.allSettled(\n jobs.map((job) => {\n if ([JobStatus.PROCESSING, JobStatus.PENDING].includes(job.status)) {\n this.abort(job.id);\n }\n })\n );\n }\n\n /**\n * Gets jobs by run ID\n * @param runId The ID of the run to get jobs for\n */\n public async getJobsByRunId(runId: string): Promise<Job<Input, Output>[]> {\n if (!runId) throw new JobNotFoundError(\"Cannot get jobs by undefined runId\");\n const jobs = await this.storage.getByRunId(runId);\n return jobs.map((job) => this.storageToClass(job));\n }\n\n // ========================================================================\n // Event handling\n // ========================================================================\n\n /**\n * Registers an event listener for job queue events\n */\n public on<Event extends JobQueueEvents>(\n event: Event,\n listener: JobQueueEventListener<Event>\n ): void {\n this.events.on(event, listener);\n }\n\n /**\n * Removes an event listener for job queue events\n */\n public off<Event extends JobQueueEvents>(\n event: Event,\n listener: JobQueueEventListener<Event>\n ): void {\n this.events.off(event, listener);\n }\n\n /**\n * Adds an event listener for job queue events that will be called only once\n */\n public once<Event extends JobQueueEvents>(\n event: Event,\n listener: JobQueueEventListener<Event>\n ): void {\n this.events.once(event, listener);\n }\n\n /**\n * Returns a promise that resolves when the event is emitted\n */\n public waitOn<Event extends JobQueueEvents>(\n event: Event\n ): Promise<JobQueueEventParameters<Event, Input, Output>> {\n return this.events.waitOn(event) as Promise<JobQueueEventParameters<Event, Input, Output>>;\n }\n\n // ========================================================================\n // Protected properties\n // ========================================================================\n\n /**\n * The limiter for the queue\n */\n protected readonly limiter: ILimiter;\n\n /**\n * The storage for the queue\n */\n protected readonly storage: IQueueStorage<Input, Output>;\n\n /**\n * Whether the queue is running\n */\n protected running: boolean = false;\n\n /**\n * The stats for the queue\n */\n protected stats: JobQueueStats = {\n totalJobs: 0,\n completedJobs: 0,\n failedJobs: 0,\n abortedJobs: 0,\n retriedJobs: 0,\n disabledJobs: 0,\n lastUpdateTime: new Date(),\n };\n\n /**\n * The event emitter for the queue\n */\n protected events = new EventEmitter<JobQueueEventListeners<Input, Output>>();\n\n /**\n * The map of jobs to their promises for this worker instance\n */\n protected activeJobAbortControllers: Map<unknown, AbortController> = new Map();\n /**\n * Map of jobs to their promises for this worker instance\n * If this is both a server and client we can short-circuit\n * job aborting, wait on job completion, etc.\n */\n protected activeJobPromises: Map<\n unknown,\n Array<{\n resolve: (value?: any) => void;\n reject: (err: JobError) => void;\n }>\n > = new Map();\n protected processingTimes: Map<unknown, number> = new Map();\n protected mode: QueueMode = QueueMode.BOTH;\n protected jobProgressListeners: Map<unknown, Set<JobProgressListener>> = new Map();\n protected lastKnownProgress: Map<\n unknown,\n {\n progress: number;\n message: string;\n details: Record<string, any> | null;\n }\n > = new Map();\n\n // ========================================================================\n // Protected methods\n // ========================================================================\n\n /**\n * Saves the progress for a job\n * @param id The ID of the job to save progress for\n * @param progress The progress of the job\n * @param message The message of the job\n * @param details The details of the job\n */\n private async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ) {\n if (!id) throw new JobNotFoundError(\"Cannot save progress for undefined job\");\n await this.storage.saveProgress(id, progress, message, details);\n }\n\n /**\n * Creates an abort controller for a job and adds it to the activeJobSignals map\n */\n protected createAbortController(jobId: unknown): AbortController {\n if (!jobId) throw new JobNotFoundError(\"Cannot create abort controller for undefined job\");\n if (this.activeJobAbortControllers.has(jobId)) {\n // retries reuse the same abort controller\n return this.activeJobAbortControllers.get(jobId)!;\n }\n const abortController = new AbortController();\n abortController.signal.addEventListener(\"abort\", () => this.handleAbort(jobId));\n this.activeJobAbortControllers.set(jobId, abortController);\n return abortController;\n }\n\n /**\n * Validates the state of a job before processing\n */\n protected async validateJobState(job: Job<Input, Output>): Promise<void> {\n if (job.status === JobStatus.COMPLETED) {\n throw new PermanentJobError(`Job ${job.id} is already completed`);\n }\n if (job.status === JobStatus.FAILED) {\n throw new PermanentJobError(`Job ${job.id} has failed`);\n }\n if (\n job.status === JobStatus.ABORTING ||\n this.activeJobAbortControllers.get(job.id)?.signal.aborted\n ) {\n throw new AbortSignalJobError(`Job ${job.id} is being aborted`);\n }\n if (job.deadlineAt && job.deadlineAt < new Date()) {\n throw new PermanentJobError(`Job ${job.id} has exceeded its deadline`);\n }\n if (job.status === JobStatus.DISABLED) {\n throw new JobDisabledError(`Job ${job.id} has been disabled`);\n }\n }\n\n /**\n * Normalizes different types of errors into JobError instances\n */\n protected normalizeError(err: any): JobError {\n if (err instanceof JobError) {\n return err;\n }\n if (err instanceof Error) {\n return err as JobError;\n }\n return new PermanentJobError(String(err));\n }\n\n /**\n * Updates average processing time statistics\n */\n protected updateAverageProcessingTime(): void {\n const times = Array.from(this.processingTimes.values());\n if (times.length > 0) {\n this.stats.averageProcessingTime = times.reduce((a, b) => a + b, 0) / times.length;\n }\n }\n\n /**\n * Emits updated statistics\n */\n protected emitStatsUpdate(): void {\n this.stats.lastUpdateTime = new Date();\n this.events.emit(\"queue_stats_update\", this.queueName, { ...this.stats });\n }\n\n protected announceProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ) {\n this.lastKnownProgress.set(jobId, {\n progress,\n message,\n details,\n });\n\n // Emit the general event\n this.events.emit(\"job_progress\", this.queueName, jobId, progress, message, details);\n\n // Notify job-specific listeners\n const listeners = this.jobProgressListeners.get(jobId);\n if (listeners) {\n for (const listener of listeners) {\n listener(progress, message, details);\n }\n }\n }\n\n /**\n * Creates a new job instance from the provided database results.\n * @param details - The job data from the database\n * @returns A new Job instance with populated properties\n */\n protected storageToClass(details: JobStorageFormat<Input, Output>): Job<Input, Output> {\n const toDate = (date: string | null | undefined): Date | null => {\n if (!date) return null;\n const d = new Date(date);\n return isNaN(d.getTime()) ? null : d;\n };\n const job = new this.jobClass({\n id: details.id,\n jobRunId: details.job_run_id,\n queueName: details.queue,\n fingerprint: details.fingerprint,\n input: details.input as unknown as Input,\n output: details.output as unknown as Output,\n runAfter: toDate(details.run_after),\n createdAt: toDate(details.created_at)!,\n deadlineAt: toDate(details.deadline_at),\n lastRanAt: toDate(details.last_ran_at),\n completedAt: toDate(details.completed_at),\n progress: details.progress || 0,\n progressMessage: details.progress_message || \"\",\n progressDetails: details.progress_details ?? null,\n status: details.status as JobStatus,\n error: details.error ?? null,\n errorCode: details.error_code ?? null,\n runAttempts: details.run_attempts ?? 0,\n maxRetries: details.max_retries ?? 10,\n });\n job.queue = this;\n return job;\n }\n\n /**\n * Converts a Job instance to a JobDetails object\n * @param job - The Job instance to convert\n * @returns A JobDetails object with the same properties as the Job instance\n */\n public classToStorage(job: Job<Input, Output>): JobStorageFormat<Input, Output> {\n // Helper to safely convert Date to ISO string\n const dateToISOString = (date: Date | null | undefined): string | null => {\n if (!date) return null;\n // Check if date is valid before converting\n return isNaN(date.getTime()) ? null : date.toISOString();\n };\n const now = new Date().toISOString();\n return {\n id: job.id,\n job_run_id: job.jobRunId,\n queue: job.queueName || this.queueName,\n fingerprint: job.fingerprint,\n input: job.input,\n status: job.status,\n output: job.output ?? null,\n error: job.error === null ? null : String(job.error),\n error_code: job.errorCode || null,\n run_attempts: job.runAttempts ?? 0,\n max_retries: job.maxRetries ?? 10,\n run_after: dateToISOString(job.runAfter) ?? now,\n created_at: dateToISOString(job.createdAt) ?? now,\n deadline_at: dateToISOString(job.deadlineAt),\n last_ran_at: dateToISOString(job.lastRanAt),\n completed_at: dateToISOString(job.completedAt),\n progress: job.progress ?? 0,\n progress_message: job.progressMessage ?? \"\",\n progress_details: job.progressDetails ?? null,\n };\n }\n\n protected async rescheduleJob(job: Job<Input, Output>, retryDate?: Date) {\n try {\n job.status = JobStatus.PENDING;\n const nextAvailableTime = await this.limiter.getNextAvailableTime();\n job.runAfter = retryDate instanceof Date ? retryDate : nextAvailableTime;\n job.progress = 0;\n job.progressMessage = \"\";\n job.progressDetails = null;\n await this.storage.complete(this.classToStorage(job));\n\n this.stats.retriedJobs++;\n this.events.emit(\"job_retry\", this.queueName, job.id, job.runAfter);\n // console.log(`Rescheduled job ${job.id} to ${job.runAfter}`);\n } catch (err) {\n console.error(\"rescheduleJob\", err);\n }\n }\n\n protected async disableJob(job: Job<Input, Output>) {\n try {\n job.status = JobStatus.DISABLED;\n job.progress = 100;\n job.completedAt = new Date();\n job.progressMessage = \"\";\n job.progressDetails = null;\n await this.storage.complete(this.classToStorage(job));\n if (this.options.deleteAfterDisabledMs === 0) {\n await this.delete(job.id);\n }\n this.stats.disabledJobs++;\n this.events.emit(\"job_disabled\", this.queueName, job.id);\n const promises = this.activeJobPromises.get(job.id) || [];\n promises.forEach(({ resolve }) => resolve(undefined));\n this.activeJobPromises.delete(job.id);\n } catch (err) {\n console.error(\"disableJob\", err);\n }\n }\n\n protected async failJob(job: Job<Input, Output>, error: JobError) {\n try {\n job.status = JobStatus.FAILED;\n job.progress = 100;\n job.completedAt = new Date();\n job.progressMessage = \"\";\n job.progressDetails = null;\n job.error = error.message;\n job.errorCode = error?.constructor?.name ?? null;\n\n await this.storage.complete(this.classToStorage(job));\n if (this.options.deleteAfterFailureMs === 0) {\n await this.delete(job.id);\n }\n\n this.stats.failedJobs++;\n this.events.emit(\"job_error\", this.queueName, job.id, `${error!.name}: ${error!.message}`);\n\n const promises = this.activeJobPromises.get(job.id) || [];\n promises.forEach(({ reject }) => reject(error!));\n this.activeJobPromises.delete(job.id);\n } catch (err) {\n console.error(\"failJob errored out?\", err);\n }\n\n // Clear any remaining state\n this.activeJobAbortControllers.delete(job.id);\n this.lastKnownProgress.delete(job.id);\n this.jobProgressListeners.delete(job.id);\n this.activeJobPromises.delete(job.id);\n }\n\n protected async completeJob(job: Job<Input, Output>, output?: Output) {\n try {\n job.status = JobStatus.COMPLETED;\n job.progress = 100;\n job.progressMessage = \"\";\n job.progressDetails = null;\n job.completedAt = new Date();\n job.output = output ?? null;\n job.error = null;\n job.errorCode = null;\n await this.storage.complete(this.classToStorage(job));\n if (job && this.options.deleteAfterCompletionMs === 0) {\n await this.delete(job.id);\n }\n\n this.stats.completedJobs++;\n this.events.emit(\"job_complete\", this.queueName, job.id, output!);\n\n const promises = this.activeJobPromises.get(job.id);\n if (promises) {\n promises.forEach(({ resolve }) => resolve(output!));\n }\n this.activeJobPromises.delete(job.id);\n } catch (err) {\n console.error(\"completeJob errored out?\", err);\n }\n // Clear any remaining state\n this.activeJobAbortControllers.delete(job.id);\n this.lastKnownProgress.delete(job.id);\n this.jobProgressListeners.delete(job.id);\n this.activeJobPromises.delete(job.id);\n }\n\n /**\n * Signals the abort of a job\n * @param jobId The ID of the job to abort\n */\n public async abort(jobId: unknown) {\n if (!jobId) throw new JobNotFoundError(\"Cannot abort undefined job\");\n // we \"store the abort signal\" in the storage in case we are client\n // and not server for this job. we could avoid this is we were definitely\n // both, but better to have durability here.\n await this.storage.abort(jobId);\n\n // if we are server too, we need to abort the job\n // the abort controller will issue a a call to handleAbort\n let controller = this.activeJobAbortControllers.get(jobId);\n if (!controller) {\n controller = this.createAbortController(jobId);\n }\n if (!controller.signal.aborted) {\n controller.abort();\n }\n\n this.events.emit(\"job_aborting\", this.queueName, jobId);\n }\n\n /**\n * Handles the abort of a job\n * @param jobId The ID of the job to abort\n */\n protected async handleAbort(jobId: unknown) {\n const promises = this.activeJobPromises.get(jobId);\n if (promises) {\n // we are both the client and the server, so we handle ourselfs\n // right away. (a server will poll the storage for abort signals)\n const job = await this.get(jobId);\n if (!job) {\n console.error(\"handleAbort: job not found\", jobId);\n return;\n }\n const error = new AbortSignalJobError(\"Job Aborted\");\n this.failJob(job, error);\n }\n this.stats.abortedJobs++;\n }\n\n /**\n * Processes a job and handles its lifecycle including runAttempts and error handling\n */\n protected async processSingleJob(job: Job<Input, Output>): Promise<void> {\n if (!job || !job.id) throw new JobNotFoundError(\"Invalid job provided for processing\");\n\n const startTime = Date.now();\n\n try {\n await this.validateJobState(job);\n await this.limiter.recordJobStart();\n this.emitStatsUpdate();\n\n const abortController = this.createAbortController(job.id);\n this.lastKnownProgress.set(job.id, {\n progress: 0,\n message: \"\",\n details: null,\n });\n this.events.emit(\"job_start\", this.queueName, job.id);\n const output = await this.executeJob(job, abortController.signal);\n await this.completeJob(job, output);\n this.processingTimes.set(job.id, Date.now() - startTime);\n this.updateAverageProcessingTime();\n } catch (err: any) {\n const error = this.normalizeError(err);\n if (error instanceof RetryableJobError) {\n if (job.runAttempts > job.maxRetries) {\n await this.failJob(job, error);\n } else {\n await this.rescheduleJob(job, error.retryDate);\n }\n } else {\n await this.failJob(job, error);\n }\n } finally {\n await this.limiter.recordJobCompletion();\n\n this.emitStatsUpdate();\n }\n }\n\n /**\n * Main job processing loop\n */\n protected async processJobs(): Promise<void> {\n if (!this.running) {\n return;\n }\n try {\n // clean up any completed or failed jobs\n await this.cleanUpJobs();\n\n // process the jobs\n const canProceed = await this.limiter.canProceed();\n if (canProceed) {\n const job = await this.next();\n if (job) {\n // NOTE: We don't await the processJob here because we want to continue\n // to process other jobs in the background\n this.processSingleJob(job);\n }\n }\n } finally {\n setTimeout(() => this.processJobs(), this.options.waitDurationInMilliseconds);\n }\n }\n\n protected async cleanUpJobs(): Promise<void> {\n const abortingJobs = await this.peek(JobStatus.ABORTING);\n for (const job of abortingJobs) {\n await this.handleAbort(job.id);\n }\n // delete any completed or failed jobs\n if (this.options.deleteAfterCompletionMs) {\n await this.storage.deleteJobsByStatusAndAge(\n JobStatus.COMPLETED,\n this.options.deleteAfterCompletionMs\n );\n }\n if (this.options.deleteAfterFailureMs) {\n await this.storage.deleteJobsByStatusAndAge(\n JobStatus.FAILED,\n this.options.deleteAfterFailureMs\n );\n }\n }\n\n /**\n * Monitors jobs that have progress listeners attached\n * Polls for updates to jobs being processed elsewhere\n * Only emits events when progress state has changed\n */\n protected async monitorJobs(): Promise<void> {\n if (!this.running) {\n return;\n }\n try {\n // Get all jobs that have listeners\n const jobIds = Array.from(this.jobProgressListeners.keys());\n\n // For each job with listeners, check its current state\n for (const jobId of jobIds) {\n const job = await this.get(jobId);\n if (job) {\n const currentProgress = {\n progress: job.progress,\n message: job.progressMessage,\n details: job.progressDetails || null,\n };\n\n const lastProgress = this.lastKnownProgress.get(jobId);\n\n // Check if progress has changed\n const hasChanged =\n !lastProgress ||\n lastProgress.progress !== currentProgress.progress ||\n lastProgress.message !== currentProgress.message;\n // || JSON.stringify(lastProgress.details) !== JSON.stringify(currentProgress.details);\n\n if (hasChanged && currentProgress.progress !== 0 && currentProgress.message !== \"\") {\n this.announceProgress(\n jobId,\n currentProgress.progress,\n currentProgress.message,\n currentProgress.details\n );\n }\n }\n }\n\n // Clean up completed jobs from lastKnownProgress\n for (const jobId of this.lastKnownProgress.keys()) {\n const job = await this.get(jobId);\n if (!job || job.status === JobStatus.COMPLETED || job.status === JobStatus.FAILED) {\n this.lastKnownProgress.delete(jobId);\n }\n }\n } catch (error) {\n console.error(`Error in monitorJobs: ${error}`);\n }\n\n // Schedule next monitoring iteration\n setTimeout(() => this.monitorJobs(), this.options.waitDurationInMilliseconds);\n }\n\n /**\n * Fixes stuck jobs when the server restarts\n */\n protected async fixupJobs() {\n const stuckProcessingJobs = await this.peek(JobStatus.PROCESSING);\n const stuckAbortingJobs = await this.peek(JobStatus.ABORTING);\n const stuckJobs = [...stuckProcessingJobs, ...stuckAbortingJobs];\n for (const job of stuckJobs) {\n job.status = JobStatus.PENDING;\n job.runAfter = job.lastRanAt || new Date();\n job.progress = 0;\n job.progressMessage = \"\";\n job.progressDetails = null;\n job.error = \"Restarting server\";\n await this.storage.complete(this.classToStorage(job));\n\n await this.rescheduleJob(job, job.lastRanAt!);\n }\n }\n}\n",
9
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\n\nexport const JOB_LIMITER = createServiceToken<ILimiter>(\"jobqueue.limiter\");\n\n/**\n * Interface for a job limiter.\n */\nexport interface ILimiter {\n canProceed(): Promise<boolean>;\n recordJobStart(): Promise<void>;\n recordJobCompletion(): Promise<void>;\n getNextAvailableTime(): Promise<Date>;\n setNextAvailableTime(date: Date): Promise<void>;\n clear(): Promise<void>;\n}\n\nexport interface RateLimiterOptions {\n readonly maxExecutions: number;\n readonly windowSizeInSeconds: number;\n}\n\nexport interface RateLimiterWithBackoffOptions extends RateLimiterOptions {\n readonly initialBackoffDelay?: number;\n readonly backoffMultiplier?: number;\n readonly maxBackoffDelay?: number;\n}\n",
10
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport { ILimiter } from \"./ILimiter\";\n\nexport const NULL_JOB_LIMITER = createServiceToken<ILimiter>(\"jobqueue.limiter.null\");\n\n/**\n * Null limiter that does nothing.\n */\nexport class NullLimiter implements ILimiter {\n async canProceed(): Promise<boolean> {\n return true;\n }\n\n async recordJobStart(): Promise<void> {\n // Do nothing\n }\n\n async recordJobCompletion(): Promise<void> {\n // Do nothing\n }\n\n async getNextAvailableTime(): Promise<Date> {\n return new Date();\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n // Do nothing\n }\n\n async clear(): Promise<void> {\n // Do nothing\n }\n}\n",
11
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ILimiter } from \"./ILimiter\";\n\nexport class CompositeLimiter implements ILimiter {\n private limiters: ILimiter[] = [];\n\n constructor(limiters: ILimiter[] = []) {\n this.limiters = limiters;\n }\n\n addLimiter(limiter: ILimiter): void {\n this.limiters.push(limiter);\n }\n\n async canProceed(): Promise<boolean> {\n for (const limiter of this.limiters) {\n if (!(await limiter.canProceed())) {\n return false; // If any limiter says \"no\", proceed no further\n }\n }\n return true; // All limiters agree\n }\n\n async recordJobStart(): Promise<void> {\n this.limiters.forEach((limiter) => limiter.recordJobStart());\n }\n\n async recordJobCompletion(): Promise<void> {\n this.limiters.forEach((limiter) => limiter.recordJobCompletion());\n }\n\n async getNextAvailableTime(): Promise<Date> {\n let maxDate = new Date(); // Assume now as the default\n for (const limiter of this.limiters) {\n const limiterNextTime = await limiter.getNextAvailableTime();\n if (limiterNextTime > maxDate) {\n maxDate = limiterNextTime; // Find the latest time among limiters\n }\n }\n return maxDate;\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n for (const limiter of this.limiters) {\n await limiter.setNextAvailableTime(date);\n }\n }\n\n async clear(): Promise<void> {\n this.limiters.forEach((limiter) => limiter.clear());\n }\n}\n",
12
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport { ILimiter } from \"./ILimiter\";\n\nexport const CONCURRENT_JOB_LIMITER = createServiceToken<ILimiter>(\"jobqueue.limiter.concurrent\");\n\n/**\n * Concurrency limiter that limits the number of concurrent jobs.\n */\nexport class ConcurrencyLimiter implements ILimiter {\n private currentRunningJobs: number = 0;\n private readonly maxConcurrentJobs: number;\n private readonly timeSliceInMilliseconds: number;\n private nextAllowedStartTime: Date = new Date();\n\n constructor(maxConcurrentJobs: number, timeSliceInMilliseconds: number = 1000) {\n this.maxConcurrentJobs = maxConcurrentJobs;\n this.timeSliceInMilliseconds = timeSliceInMilliseconds;\n }\n\n async canProceed(): Promise<boolean> {\n return (\n this.currentRunningJobs < this.maxConcurrentJobs &&\n Date.now() >= this.nextAllowedStartTime.getTime()\n );\n }\n\n async recordJobStart(): Promise<void> {\n if (this.currentRunningJobs < this.maxConcurrentJobs) {\n this.currentRunningJobs++;\n this.nextAllowedStartTime = new Date(Date.now() + this.timeSliceInMilliseconds);\n }\n }\n\n async recordJobCompletion(): Promise<void> {\n this.currentRunningJobs = Math.max(0, this.currentRunningJobs - 1);\n }\n\n async getNextAvailableTime(): Promise<Date> {\n return this.currentRunningJobs < this.maxConcurrentJobs\n ? new Date()\n : new Date(Date.now() + this.timeSliceInMilliseconds);\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n if (date > this.nextAllowedStartTime) {\n this.nextAllowedStartTime = date;\n }\n }\n\n async clear(): Promise<void> {\n this.currentRunningJobs = 0;\n this.nextAllowedStartTime = new Date();\n }\n}\n",
13
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { ILimiter } from \"./ILimiter\";\n\nexport class DelayLimiter implements ILimiter {\n private nextAvailableTime: Date = new Date();\n constructor(private delayInMilliseconds: number = 50) {}\n\n async canProceed(): Promise<boolean> {\n return Date.now() >= this.nextAvailableTime.getTime();\n }\n\n async recordJobStart(): Promise<void> {\n this.nextAvailableTime = new Date(Date.now() + this.delayInMilliseconds);\n }\n\n async recordJobCompletion(): Promise<void> {\n // No action needed.\n }\n\n async getNextAvailableTime(): Promise<Date> {\n return this.nextAvailableTime;\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n if (date > this.nextAvailableTime) {\n this.nextAvailableTime = date;\n }\n }\n async clear(): Promise<void> {\n this.nextAvailableTime = new Date();\n }\n}\n",
14
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport { ILimiter, RateLimiterOptions } from \"./ILimiter\";\n\nexport const EVENLY_SPACED_JOB_RATE_LIMITER = createServiceToken<ILimiter>(\n \"jobqueue.limiter.rate.evenlyspaced\"\n);\n\n/**\n * Rate limiter that spreads requests evenly across a time window.\n * Instead of allowing all requests up to the limit and then waiting,\n * this limiter spaces out the requests evenly across the window.\n */\nexport class EvenlySpacedRateLimiter implements ILimiter {\n private readonly maxExecutions: number;\n private readonly windowSizeMs: number;\n private readonly idealInterval: number;\n private nextAvailableTime: number = Date.now();\n private lastStartTime: number = 0;\n private durations: number[] = [];\n\n constructor({ maxExecutions, windowSizeInSeconds }: RateLimiterOptions) {\n if (maxExecutions <= 0) {\n throw new Error(\"maxExecutions must be > 0\");\n }\n if (windowSizeInSeconds <= 0) {\n throw new Error(\"windowSizeInSeconds must be > 0\");\n }\n this.maxExecutions = maxExecutions;\n this.windowSizeMs = windowSizeInSeconds * 1_000;\n // If you want exactly maxExecutions in windowSize, start one every this many ms:\n this.idealInterval = this.windowSizeMs / this.maxExecutions;\n }\n\n /** Can we start a new job right now? */\n async canProceed(): Promise<boolean> {\n const now = Date.now();\n return now >= this.nextAvailableTime;\n }\n\n /** Record that a job is starting now. */\n async recordJobStart(): Promise<void> {\n const now = Date.now();\n this.lastStartTime = now;\n\n // If no timing data yet, assume zero run-time (ideal interval)\n if (this.durations.length === 0) {\n this.nextAvailableTime = now + this.idealInterval;\n } else {\n // Compute average run duration\n const sum = this.durations.reduce((a, b) => a + b, 0);\n const avgDuration = sum / this.durations.length;\n // Schedule next start: ideal spacing minus average duration\n const waitMs = Math.max(0, this.idealInterval - avgDuration);\n this.nextAvailableTime = now + waitMs;\n }\n }\n\n /**\n * Call this when a job finishes.\n * We measure its duration, update our running-average,\n * and then compute how long to wait before the next job start.\n */\n async recordJobCompletion(): Promise<void> {\n const now = Date.now();\n const duration = now - this.lastStartTime;\n this.durations.push(duration);\n if (this.durations.length > this.maxExecutions) {\n this.durations.shift();\n }\n }\n\n async getNextAvailableTime(): Promise<Date> {\n return new Date(this.nextAvailableTime);\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n const t = date.getTime();\n if (t > this.nextAvailableTime) {\n this.nextAvailableTime = t;\n }\n }\n\n async clear(): Promise<void> {\n this.durations = [];\n this.nextAvailableTime = Date.now();\n this.lastStartTime = 0;\n }\n}\n",
15
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport { ILimiter, RateLimiterWithBackoffOptions } from \"./ILimiter\";\n\nexport const MEMORY_JOB_RATE_LIMITER = createServiceToken<ILimiter>(\"jobqueue.limiter.rate.memory\");\n\n/**\n * In-memory implementation of a rate limiter.\n * Manages request counts and delays to control job execution.\n */\nexport class InMemoryRateLimiter implements ILimiter {\n private requests: Date[] = [];\n private nextAvailableTime = new Date();\n private currentBackoffDelay: number;\n\n private readonly maxExecutions: number;\n private readonly windowSizeInMilliseconds: number;\n private readonly initialBackoffDelay: number;\n private readonly backoffMultiplier: number;\n private readonly maxBackoffDelay: number;\n\n constructor({\n maxExecutions,\n windowSizeInSeconds,\n initialBackoffDelay = 1_000,\n backoffMultiplier = 2,\n maxBackoffDelay = 600_000, // 10 minutes\n }: RateLimiterWithBackoffOptions) {\n if (maxExecutions <= 0) {\n throw new Error(\"maxExecutions must be greater than 0\");\n }\n if (windowSizeInSeconds <= 0) {\n throw new Error(\"windowSizeInSeconds must be greater than 0\");\n }\n if (initialBackoffDelay <= 0) {\n throw new Error(\"initialBackoffDelay must be greater than 0\");\n }\n if (backoffMultiplier <= 1) {\n throw new Error(\"backoffMultiplier must be greater than 1\");\n }\n if (maxBackoffDelay <= initialBackoffDelay) {\n throw new Error(\"maxBackoffDelay must be greater than initialBackoffDelay\");\n }\n\n this.maxExecutions = maxExecutions;\n this.windowSizeInMilliseconds = windowSizeInSeconds * 1_000;\n this.initialBackoffDelay = initialBackoffDelay;\n this.backoffMultiplier = backoffMultiplier;\n this.maxBackoffDelay = maxBackoffDelay;\n this.currentBackoffDelay = initialBackoffDelay;\n }\n\n private removeOldRequests() {\n const now = Date.now();\n const cutoff = now - this.windowSizeInMilliseconds;\n this.requests = this.requests.filter((d) => d.getTime() > cutoff);\n\n // if our scheduled time is in the past, reset it to now\n if (this.nextAvailableTime.getTime() < now) {\n this.nextAvailableTime = new Date(now);\n }\n }\n\n private increaseBackoff() {\n this.currentBackoffDelay = Math.min(\n this.currentBackoffDelay * this.backoffMultiplier,\n this.maxBackoffDelay\n );\n }\n\n private addJitter(base: number): number {\n // full jitter in [base, 2*base)\n return base + Math.random() * base;\n }\n\n async canProceed(): Promise<boolean> {\n this.removeOldRequests();\n\n const now = Date.now();\n const okRequestCount = this.requests.length < this.maxExecutions;\n const okTime = now >= this.nextAvailableTime.getTime();\n const canProceedNow = okRequestCount && okTime;\n\n if (!canProceedNow) {\n this.increaseBackoff();\n } else {\n this.currentBackoffDelay = this.initialBackoffDelay;\n }\n\n return canProceedNow;\n }\n\n async recordJobStart(): Promise<void> {\n this.requests.push(new Date());\n\n if (this.requests.length >= this.maxExecutions) {\n const earliest = this.requests[0].getTime();\n const windowExpires = earliest + this.windowSizeInMilliseconds;\n const backoffExpires = Date.now() + this.addJitter(this.currentBackoffDelay);\n this.nextAvailableTime = new Date(Math.max(windowExpires, backoffExpires));\n }\n }\n\n async recordJobCompletion(): Promise<void> {\n // no-op\n }\n\n async getNextAvailableTime(): Promise<Date> {\n this.removeOldRequests();\n return new Date(Math.max(Date.now(), this.nextAvailableTime.getTime()));\n }\n\n async setNextAvailableTime(date: Date): Promise<void> {\n if (date.getTime() > this.nextAvailableTime.getTime()) {\n this.nextAvailableTime = date;\n }\n }\n\n async clear(): Promise<void> {\n this.requests = [];\n this.nextAvailableTime = new Date();\n this.currentBackoffDelay = this.initialBackoffDelay;\n }\n}\n"
16
+ ],
17
+ "mappings": "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",
18
+ "debugId": "4FA097AF2EE0650C64756E2164756E21",
19
+ "names": []
20
+ }