@workglow/job-queue 0.0.57 → 0.0.58

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.
@@ -1,20 +1,21 @@
1
1
  {
2
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"],
3
+ "sources": ["../src/job/Job.ts", "../src/job/JobError.ts", "../src/job/JobQueueClient.ts", "../src/job/JobQueueServer.ts", "../src/limiter/NullLimiter.ts", "../src/job/JobQueueWorker.ts", "../src/limiter/CompositeLimiter.ts", "../src/limiter/ConcurrencyLimiter.ts", "../src/limiter/DelayLimiter.ts", "../src/limiter/EvenlySpacedRateLimiter.ts", "../src/limiter/ILimiter.ts", "../src/limiter/RateLimiter.ts"],
4
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",
5
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { JobStatus } from \"@workglow/storage\";\nimport { JobError } from \"./JobError\";\nimport type { JobProgressListener } from \"./JobQueueEventListeners\";\n\nexport { JobStatus };\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\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 (for direct execution without a worker)\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 ): Promise<void> {\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\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",
6
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",
7
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { IQueueStorage, JobStatus, JobStorageFormat, QueueChangePayload } from \"@workglow/storage\";\nimport { EventEmitter } from \"@workglow/util\";\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\";\nimport type { JobQueueServer } from \"./JobQueueServer\";\n\n/**\n * Handle returned when submitting a job, providing methods to interact with the job\n */\nexport interface JobHandle<Output> {\n readonly id: unknown;\n waitFor(): Promise<Output>;\n abort(): Promise<void>;\n onProgress(callback: JobProgressListener): () => void;\n}\n\n/**\n * Options for creating a JobQueueClient\n */\nexport interface JobQueueClientOptions<Input, Output> {\n readonly storage: IQueueStorage<Input, Output>;\n readonly queueName: string;\n}\n\ntype JobClass<Input, Output> = new (\n param: JobConstructorParam<Input, Output>\n) => Job<Input, Output>;\n\n/**\n * Client for submitting jobs and monitoring their progress.\n * Connect to a JobQueueServer for same-process optimization,\n * or use storage subscriptions for cross-process communication.\n */\nexport class JobQueueClient<Input, Output> {\n public readonly queueName: string;\n protected readonly storage: IQueueStorage<Input, Output>;\n protected readonly events = new EventEmitter<JobQueueEventListeners<Input, Output>>();\n protected server: JobQueueServer<Input, Output> | null = null;\n protected storageUnsubscribe: (() => void) | null = null;\n\n /**\n * Map of job IDs to their pending promise resolvers\n */\n protected readonly activeJobPromises: Map<\n unknown,\n Array<{\n resolve: (value: Output) => void;\n reject: (err: JobError) => void;\n }>\n > = new Map();\n\n /**\n * Map of job IDs to their progress listeners\n */\n protected readonly jobProgressListeners: Map<unknown, Set<JobProgressListener>> = new Map();\n\n /**\n * Last known progress state for each job\n */\n protected readonly lastKnownProgress: Map<\n unknown,\n {\n readonly progress: number;\n readonly message: string;\n readonly details: Record<string, unknown> | null;\n }\n > = new Map();\n\n constructor(options: JobQueueClientOptions<Input, Output>) {\n this.queueName = options.queueName;\n this.storage = options.storage;\n }\n\n /**\n * Attach to a local JobQueueServer for same-process event optimization.\n * When attached, events flow directly from server without storage polling.\n */\n public attach(server: JobQueueServer<Input, Output>): void {\n if (this.server) {\n this.detach();\n }\n this.server = server;\n server.addClient(this);\n\n // Unsubscribe from storage if we were using it\n if (this.storageUnsubscribe) {\n this.storageUnsubscribe();\n this.storageUnsubscribe = null;\n }\n }\n\n /**\n * Detach from the current server\n */\n public detach(): void {\n if (this.server) {\n this.server.removeClient(this);\n this.server = null;\n }\n }\n\n /**\n * Connect to storage for cross-process communication (when no local server).\n * Uses storage subscriptions to receive job updates.\n */\n public connect(): void {\n if (this.server) {\n return; // Already connected via server\n }\n\n if (this.storageUnsubscribe) {\n return; // Already subscribed\n }\n\n this.storageUnsubscribe = this.storage.subscribeToChanges(\n (change: QueueChangePayload<Input, Output>) => {\n this.handleStorageChange(change);\n }\n );\n }\n\n /**\n * Disconnect from storage subscriptions\n */\n public disconnect(): void {\n if (this.storageUnsubscribe) {\n this.storageUnsubscribe();\n this.storageUnsubscribe = null;\n }\n this.detach();\n }\n\n /**\n * Submit a job to the queue\n */\n public async submit(\n input: Input,\n options?: {\n readonly jobRunId?: string;\n readonly fingerprint?: string;\n readonly maxRetries?: number;\n readonly runAfter?: Date;\n readonly deadlineAt?: Date;\n }\n ): Promise<JobHandle<Output>> {\n const job: JobStorageFormat<Input, Output> = {\n queue: this.queueName,\n input,\n job_run_id: options?.jobRunId,\n fingerprint: options?.fingerprint,\n max_retries: options?.maxRetries ?? 10,\n run_after: options?.runAfter?.toISOString() ?? new Date().toISOString(),\n deadline_at: options?.deadlineAt?.toISOString() ?? null,\n completed_at: null,\n status: JobStatus.PENDING,\n };\n\n const id = await this.storage.add(job);\n\n return this.createJobHandle(id);\n }\n\n /**\n * Submit multiple jobs to the queue\n */\n public async submitBatch(\n inputs: readonly Input[],\n options?: {\n readonly jobRunId?: string;\n readonly maxRetries?: number;\n }\n ): Promise<readonly JobHandle<Output>[]> {\n const handles: JobHandle<Output>[] = [];\n for (const input of inputs) {\n const handle = await this.submit(input, options);\n handles.push(handle);\n }\n return handles;\n }\n\n /**\n * Get a job by ID\n */\n public async getJob(id: unknown): Promise<Job<Input, Output> | undefined> {\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 * Get jobs by run ID\n */\n public async getJobsByRunId(runId: string): Promise<readonly 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 * Peek at jobs in the queue\n */\n public async peek(status?: JobStatus, num?: number): Promise<readonly Job<Input, Output>[]> {\n const jobs = await this.storage.peek(status, num);\n return jobs.map((job) => this.storageToClass(job));\n }\n\n /**\n * Get the size of the queue\n */\n public async size(status?: JobStatus): Promise<number> {\n return this.storage.size(status);\n }\n\n /**\n * Get the output for an input (if job completed)\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n if (!input) throw new JobNotFoundError(\"Cannot get output for undefined input\");\n return this.storage.outputForInput(input);\n }\n\n /**\n * Wait for a job to complete\n */\n public async waitFor(jobId: unknown): Promise<Output> {\n if (!jobId) throw new JobNotFoundError(\"Cannot wait for undefined job\");\n\n const job = await this.getJob(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 throw new JobDisabledError(`Job ${jobId} was disabled`);\n }\n if (job.status === JobStatus.FAILED) {\n throw this.buildErrorFromJob(job);\n }\n\n const { promise, resolve, reject } = Promise.withResolvers<Output>();\n promise.catch(() => {}); // Prevent unhandled rejection\n\n const promises = this.activeJobPromises.get(jobId) || [];\n promises.push({ resolve, reject });\n this.activeJobPromises.set(jobId, promises);\n\n return promise;\n }\n\n /**\n * Abort a job\n */\n public async abort(jobId: unknown): Promise<void> {\n if (!jobId) throw new JobNotFoundError(\"Cannot abort undefined job\");\n await this.storage.abort(jobId);\n this.events.emit(\"job_aborting\", this.queueName, jobId);\n }\n\n /**\n * Abort 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 return this.abort(job.id);\n }\n })\n );\n }\n\n /**\n * Subscribe to progress updates for a specific job\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 // Event handling\n // ========================================================================\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 public off<Event extends JobQueueEvents>(\n event: Event,\n listener: JobQueueEventListener<Event>\n ): void {\n this.events.off(event, listener);\n }\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 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 // Internal methods called by JobQueueServer for same-process optimization\n // ========================================================================\n\n /**\n * Called by server when a job starts processing\n * @internal\n */\n public handleJobStart(jobId: unknown): void {\n this.lastKnownProgress.set(jobId, {\n progress: 0,\n message: \"\",\n details: null,\n });\n this.events.emit(\"job_start\", this.queueName, jobId);\n }\n\n /**\n * Called by server when a job completes\n * @internal\n */\n public handleJobComplete(jobId: unknown, output: Output): void {\n this.events.emit(\"job_complete\", this.queueName, jobId, output);\n\n const promises = this.activeJobPromises.get(jobId);\n if (promises) {\n promises.forEach(({ resolve }) => resolve(output));\n }\n this.cleanupJob(jobId);\n }\n\n /**\n * Called by server when a job fails\n * @internal\n */\n public handleJobError(jobId: unknown, error: string, errorCode?: string): void {\n this.events.emit(\"job_error\", this.queueName, jobId, error);\n\n const promises = this.activeJobPromises.get(jobId);\n if (promises) {\n const jobError = this.buildErrorFromCode(error, errorCode);\n promises.forEach(({ reject }) => reject(jobError));\n }\n this.cleanupJob(jobId);\n }\n\n /**\n * Called by server when a job is disabled\n * @internal\n */\n public handleJobDisabled(jobId: unknown): void {\n this.events.emit(\"job_disabled\", this.queueName, jobId);\n\n const promises = this.activeJobPromises.get(jobId);\n if (promises) {\n promises.forEach(({ reject }) => reject(new JobDisabledError(\"Job was disabled\")));\n }\n this.cleanupJob(jobId);\n }\n\n /**\n * Called by server when a job is retried\n * @internal\n */\n public handleJobRetry(jobId: unknown, runAfter: Date): void {\n this.events.emit(\"job_retry\", this.queueName, jobId, runAfter);\n }\n\n /**\n * Called by server when job progress updates\n * @internal\n */\n public handleJobProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, unknown> | null\n ): void {\n this.lastKnownProgress.set(jobId, { progress, message, details });\n this.events.emit(\"job_progress\", this.queueName, jobId, progress, message, details);\n\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 // Private helpers\n // ========================================================================\n\n private createJobHandle(id: unknown): JobHandle<Output> {\n return {\n id,\n waitFor: () => this.waitFor(id),\n abort: () => this.abort(id),\n onProgress: (callback: JobProgressListener) => this.onJobProgress(id, callback),\n };\n }\n\n private cleanupJob(jobId: unknown): void {\n this.activeJobPromises.delete(jobId);\n this.lastKnownProgress.delete(jobId);\n this.jobProgressListeners.delete(jobId);\n }\n\n private handleStorageChange(change: QueueChangePayload<Input, Output>): void {\n if (!change.new && !change.old) return;\n\n const jobId = change.new?.id ?? change.old?.id;\n if (!jobId) return;\n\n // Only process changes for our queue\n const queueName = change.new?.queue ?? change.old?.queue;\n if (queueName !== this.queueName) return;\n\n if (change.type === \"UPDATE\" && change.new) {\n const newStatus = change.new.status;\n const oldStatus = change.old?.status;\n\n if (newStatus === JobStatus.PROCESSING && oldStatus === JobStatus.PENDING) {\n this.handleJobStart(jobId);\n } else if (newStatus === JobStatus.COMPLETED) {\n this.handleJobComplete(jobId, change.new.output as Output);\n } else if (newStatus === JobStatus.FAILED) {\n this.handleJobError(\n jobId,\n change.new.error ?? \"Job failed\",\n change.new.error_code ?? undefined\n );\n } else if (newStatus === JobStatus.DISABLED) {\n this.handleJobDisabled(jobId);\n } else if (newStatus === JobStatus.PENDING && oldStatus === JobStatus.PROCESSING) {\n // Retry\n const runAfter = change.new.run_after ? new Date(change.new.run_after) : new Date();\n this.handleJobRetry(jobId, runAfter);\n }\n\n // Progress update\n if (\n change.new.progress !== change.old?.progress ||\n change.new.progress_message !== change.old?.progress_message\n ) {\n this.handleJobProgress(\n jobId,\n change.new.progress ?? 0,\n change.new.progress_message ?? \"\",\n change.new.progress_details ?? null\n );\n }\n }\n }\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 return new Job<Input, Output>({\n id: details.id,\n jobRunId: details.job_run_id,\n queueName: details.queue,\n fingerprint: details.fingerprint,\n input: details.input as Input,\n output: details.output 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 }\n\n protected buildErrorFromJob(job: Job<Input, Output>): JobError {\n return this.buildErrorFromCode(job.error || \"Job failed\", job.errorCode ?? undefined);\n }\n\n protected buildErrorFromCode(message: string, errorCode?: string): JobError {\n if (errorCode === \"PermanentJobError\") {\n return new PermanentJobError(message);\n }\n if (errorCode === \"RetryableJobError\") {\n return new RetryableJobError(message);\n }\n if (errorCode === \"AbortSignalJobError\") {\n return new AbortSignalJobError(message);\n }\n if (errorCode === \"JobDisabledError\") {\n return new JobDisabledError(message);\n }\n return new JobError(message);\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 { IQueueStorage, JobStatus, JobStorageFormat } from \"@workglow/storage\";\nimport { EventEmitter } from \"@workglow/util\";\nimport { ILimiter } from \"../limiter/ILimiter\";\nimport { NullLimiter } from \"../limiter/NullLimiter\";\nimport { Job, JobConstructorParam } from \"./Job\";\nimport { JobQueueClient } from \"./JobQueueClient\";\nimport { JobQueueWorker } from \"./JobQueueWorker\";\n\n/**\n * Statistics tracked for the job queue\n */\nexport interface JobQueueStats {\n readonly totalJobs: number;\n readonly completedJobs: number;\n readonly failedJobs: number;\n readonly abortedJobs: number;\n readonly retriedJobs: number;\n readonly disabledJobs: number;\n readonly averageProcessingTime?: number;\n readonly lastUpdateTime: Date;\n}\n\n/**\n * Events emitted by JobQueueServer\n */\nexport type JobQueueServerEventListeners<Input, Output> = {\n server_start: (queueName: string) => void;\n server_stop: (queueName: string) => void;\n stats_update: (queueName: string, stats: JobQueueStats) => void;\n job_start: (queueName: string, jobId: unknown) => void;\n job_complete: (queueName: string, jobId: unknown, output: Output) => void;\n job_error: (queueName: string, jobId: unknown, error: string) => void;\n job_disabled: (queueName: string, jobId: unknown) => void;\n job_retry: (queueName: string, jobId: unknown, runAfter: Date) => void;\n job_progress: (\n queueName: string,\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, unknown> | null\n ) => void;\n};\n\nexport type JobQueueServerEvents = keyof JobQueueServerEventListeners<unknown, unknown>;\n\n/**\n * Options for creating a JobQueueServer\n */\nexport interface JobQueueServerOptions<Input, Output> {\n readonly storage: IQueueStorage<Input, Output>;\n readonly queueName: string;\n readonly limiter?: ILimiter;\n readonly workerCount?: number;\n readonly pollIntervalMs?: number;\n readonly deleteAfterCompletionMs?: number;\n readonly deleteAfterFailureMs?: number;\n readonly deleteAfterDisabledMs?: number;\n readonly cleanupIntervalMs?: number;\n}\n\ntype JobClass<Input, Output> = new (\n param: JobConstructorParam<Input, Output>\n) => Job<Input, Output>;\n\n/**\n * Server that coordinates multiple workers and manages the job queue lifecycle.\n * Handles stuck job recovery, cleanup, and aggregates statistics.\n */\nexport class JobQueueServer<\n Input,\n Output,\n QueueJob extends Job<Input, Output> = Job<Input, Output>,\n> {\n public readonly queueName: string;\n protected readonly storage: IQueueStorage<Input, Output>;\n protected readonly jobClass: JobClass<Input, Output>;\n protected readonly limiter: ILimiter;\n protected readonly workerCount: number;\n protected readonly pollIntervalMs: number;\n protected readonly deleteAfterCompletionMs?: number;\n protected readonly deleteAfterFailureMs?: number;\n protected readonly deleteAfterDisabledMs?: number;\n protected readonly cleanupIntervalMs: number;\n\n protected readonly events = new EventEmitter<JobQueueServerEventListeners<Input, Output>>();\n protected readonly workers: JobQueueWorker<Input, Output, QueueJob>[] = [];\n protected readonly clients: Set<JobQueueClient<Input, Output>> = new Set();\n\n protected running = false;\n protected cleanupTimer: ReturnType<typeof setTimeout> | null = null;\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 constructor(jobClass: JobClass<Input, Output>, options: JobQueueServerOptions<Input, Output>) {\n this.queueName = options.queueName;\n this.storage = options.storage;\n this.jobClass = jobClass;\n this.limiter = options.limiter ?? new NullLimiter();\n this.workerCount = options.workerCount ?? 1;\n this.pollIntervalMs = options.pollIntervalMs ?? 100;\n this.deleteAfterCompletionMs = options.deleteAfterCompletionMs;\n this.deleteAfterFailureMs = options.deleteAfterFailureMs;\n this.deleteAfterDisabledMs = options.deleteAfterDisabledMs;\n this.cleanupIntervalMs = options.cleanupIntervalMs ?? 10000;\n\n this.initializeWorkers();\n }\n\n /**\n * Start the server and all workers\n */\n public async start(): Promise<this> {\n if (this.running) {\n return this;\n }\n\n this.running = true;\n this.events.emit(\"server_start\", this.queueName);\n\n // Fix stuck jobs from previous runs\n await this.fixupJobs();\n\n // Start all workers\n await Promise.all(this.workers.map((worker) => worker.start()));\n\n // Start cleanup loop\n this.startCleanupLoop();\n\n return this;\n }\n\n /**\n * Stop the server and all workers\n */\n public async stop(): Promise<this> {\n if (!this.running) {\n return this;\n }\n\n this.running = false;\n\n // Stop cleanup loop\n if (this.cleanupTimer) {\n clearTimeout(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n\n // Stop all workers\n await Promise.all(this.workers.map((worker) => worker.stop()));\n\n this.events.emit(\"server_stop\", this.queueName);\n return this;\n }\n\n /**\n * Get the current queue statistics\n */\n public getStats(): JobQueueStats {\n return { ...this.stats };\n }\n\n /**\n * Get the storage instance (for client connection)\n */\n public getStorage(): IQueueStorage<Input, Output> {\n return this.storage;\n }\n\n /**\n * Scale the number of workers\n */\n public async scaleWorkers(count: number): Promise<void> {\n if (count < 1) {\n throw new Error(\"Worker count must be at least 1\");\n }\n\n const currentCount = this.workers.length;\n\n if (count > currentCount) {\n // Add more workers\n for (let i = currentCount; i < count; i++) {\n const worker = this.createWorker();\n this.workers.push(worker);\n if (this.running) {\n await worker.start();\n }\n }\n } else if (count < currentCount) {\n // Remove workers\n const toRemove = this.workers.splice(count);\n await Promise.all(toRemove.map((worker) => worker.stop()));\n }\n }\n\n /**\n * Check if the server is running\n */\n public isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get the number of workers\n */\n public getWorkerCount(): number {\n return this.workers.length;\n }\n\n // ========================================================================\n // Client management\n // ========================================================================\n\n /**\n * Add a client for same-process event forwarding\n * @internal\n */\n public addClient(client: JobQueueClient<Input, Output>): void {\n this.clients.add(client);\n }\n\n /**\n * Remove a client\n * @internal\n */\n public removeClient(client: JobQueueClient<Input, Output>): void {\n this.clients.delete(client);\n }\n\n // ========================================================================\n // Event handling\n // ========================================================================\n\n public on<Event extends JobQueueServerEvents>(\n event: Event,\n listener: JobQueueServerEventListeners<Input, Output>[Event]\n ): void {\n this.events.on(event, listener);\n }\n\n public off<Event extends JobQueueServerEvents>(\n event: Event,\n listener: JobQueueServerEventListeners<Input, Output>[Event]\n ): void {\n this.events.off(event, listener);\n }\n\n // ========================================================================\n // Protected methods\n // ========================================================================\n\n /**\n * Initialize workers\n */\n protected initializeWorkers(): void {\n for (let i = 0; i < this.workerCount; i++) {\n const worker = this.createWorker();\n this.workers.push(worker);\n }\n }\n\n /**\n * Create a new worker and wire up event forwarding\n */\n protected createWorker(): JobQueueWorker<Input, Output, QueueJob> {\n const worker = new JobQueueWorker<Input, Output, QueueJob>(this.jobClass, {\n storage: this.storage,\n queueName: this.queueName,\n limiter: this.limiter,\n pollIntervalMs: this.pollIntervalMs,\n });\n\n // Forward worker events to server and clients\n worker.on(\"job_start\", (jobId) => {\n this.stats = { ...this.stats, totalJobs: this.stats.totalJobs + 1 };\n this.events.emit(\"job_start\", this.queueName, jobId);\n this.forwardToClients(\"handleJobStart\", jobId);\n });\n\n worker.on(\"job_complete\", async (jobId, output) => {\n this.stats = { ...this.stats, completedJobs: this.stats.completedJobs + 1 };\n this.updateAverageProcessingTime();\n this.events.emit(\"job_complete\", this.queueName, jobId, output);\n this.forwardToClients(\"handleJobComplete\", jobId, output);\n\n // Immediate deletion when configured\n if (this.deleteAfterCompletionMs === 0) {\n await this.storage.delete(jobId);\n }\n });\n\n worker.on(\"job_error\", async (jobId, error, errorCode) => {\n this.stats = { ...this.stats, failedJobs: this.stats.failedJobs + 1 };\n this.events.emit(\"job_error\", this.queueName, jobId, error);\n this.forwardToClients(\"handleJobError\", jobId, error, errorCode);\n\n // Immediate deletion when configured\n if (this.deleteAfterFailureMs === 0) {\n await this.storage.delete(jobId);\n }\n });\n\n worker.on(\"job_disabled\", async (jobId) => {\n this.stats = { ...this.stats, disabledJobs: this.stats.disabledJobs + 1 };\n this.events.emit(\"job_disabled\", this.queueName, jobId);\n this.forwardToClients(\"handleJobDisabled\", jobId);\n\n // Immediate deletion when configured\n if (this.deleteAfterDisabledMs === 0) {\n await this.storage.delete(jobId);\n }\n });\n\n worker.on(\"job_retry\", (jobId, runAfter) => {\n this.stats = { ...this.stats, retriedJobs: this.stats.retriedJobs + 1 };\n this.events.emit(\"job_retry\", this.queueName, jobId, runAfter);\n this.forwardToClients(\"handleJobRetry\", jobId, runAfter);\n });\n\n worker.on(\"job_progress\", (jobId, progress, message, details) => {\n this.events.emit(\"job_progress\", this.queueName, jobId, progress, message, details);\n this.forwardToClients(\"handleJobProgress\", jobId, progress, message, details);\n });\n\n return worker;\n }\n\n /**\n * Forward events to all attached clients\n */\n protected forwardToClients(method: \"handleJobStart\", jobId: unknown): void;\n protected forwardToClients(method: \"handleJobComplete\", jobId: unknown, output: Output): void;\n protected forwardToClients(\n method: \"handleJobError\",\n jobId: unknown,\n error: string,\n errorCode?: string\n ): void;\n protected forwardToClients(method: \"handleJobDisabled\", jobId: unknown): void;\n protected forwardToClients(method: \"handleJobRetry\", jobId: unknown, runAfter: Date): void;\n protected forwardToClients(\n method: \"handleJobProgress\",\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, unknown> | null\n ): void;\n protected forwardToClients(method: string, ...args: unknown[]): void {\n for (const client of this.clients) {\n const fn = (client as any)[method];\n if (typeof fn === \"function\") {\n fn.apply(client, args);\n }\n }\n }\n\n /**\n * Update average processing time from all workers\n */\n protected updateAverageProcessingTime(): void {\n const times: number[] = [];\n for (const worker of this.workers) {\n const avgTime = worker.getAverageProcessingTime();\n if (avgTime !== undefined) {\n times.push(avgTime);\n }\n }\n if (times.length > 0) {\n const avg = times.reduce((a, b) => a + b, 0) / times.length;\n this.stats = {\n ...this.stats,\n averageProcessingTime: avg,\n lastUpdateTime: new Date(),\n };\n }\n }\n\n /**\n * Start the cleanup loop\n */\n protected startCleanupLoop(): void {\n if (!this.running) return;\n\n this.cleanupJobs().finally(() => {\n if (this.running) {\n this.cleanupTimer = setTimeout(() => this.startCleanupLoop(), this.cleanupIntervalMs);\n }\n });\n }\n\n /**\n * Clean up completed/failed jobs based on TTL settings\n */\n protected async cleanupJobs(): Promise<void> {\n try {\n // The workers will handle the abort via their abort controllers\n // We just need to ensure the jobs get marked as failed\n\n // Delete completed jobs after TTL\n if (this.deleteAfterCompletionMs !== undefined && this.deleteAfterCompletionMs > 0) {\n await this.storage.deleteJobsByStatusAndAge(\n JobStatus.COMPLETED,\n this.deleteAfterCompletionMs\n );\n }\n\n // Delete failed jobs after TTL\n if (this.deleteAfterFailureMs !== undefined && this.deleteAfterFailureMs > 0) {\n await this.storage.deleteJobsByStatusAndAge(JobStatus.FAILED, this.deleteAfterFailureMs);\n }\n\n // Delete disabled jobs after TTL\n if (this.deleteAfterDisabledMs !== undefined && this.deleteAfterDisabledMs > 0) {\n await this.storage.deleteJobsByStatusAndAge(JobStatus.DISABLED, this.deleteAfterDisabledMs);\n }\n } catch (error) {\n console.error(\"Error in cleanup:\", error);\n }\n }\n\n /**\n * Fix stuck jobs from previous server runs\n */\n protected async fixupJobs(): Promise<void> {\n try {\n const stuckProcessingJobs = await this.storage.peek(JobStatus.PROCESSING);\n const stuckAbortingJobs = await this.storage.peek(JobStatus.ABORTING);\n const stuckJobs = [...stuckProcessingJobs, ...stuckAbortingJobs];\n\n for (const jobData of stuckJobs) {\n const job = this.storageToClass(jobData);\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 = \"Server restarted\";\n\n await this.storage.complete(this.classToStorage(job));\n }\n } catch (error) {\n console.error(\"Error in fixupJobs:\", error);\n }\n }\n\n /**\n * Convert storage format to Job class\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 return 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 Input,\n output: details.output 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 }\n\n /**\n * Convert Job class to storage format\n */\n protected classToStorage(job: Job<Input, Output>): JobStorageFormat<Input, Output> {\n const dateToISOString = (date: Date | null | undefined): string | null => {\n if (!date) return null;\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",
10
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\";\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",
10
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { IQueueStorage, JobStatus, JobStorageFormat } from \"@workglow/storage\";\nimport { EventEmitter, sleep, uuid4 } from \"@workglow/util\";\nimport { ILimiter } from \"../limiter/ILimiter\";\nimport { NullLimiter } from \"../limiter/NullLimiter\";\nimport { Job, JobConstructorParam } from \"./Job\";\nimport {\n AbortSignalJobError,\n JobDisabledError,\n JobError,\n JobNotFoundError,\n PermanentJobError,\n RetryableJobError,\n} from \"./JobError\";\n\n/**\n * Events emitted by JobQueueWorker\n */\nexport type JobQueueWorkerEventListeners<Input, Output> = {\n job_start: (jobId: unknown) => void;\n job_complete: (jobId: unknown, output: Output) => void;\n job_error: (jobId: unknown, error: string, errorCode?: string) => void;\n job_disabled: (jobId: unknown) => void;\n job_retry: (jobId: unknown, runAfter: Date) => void;\n job_progress: (\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, unknown> | null\n ) => void;\n worker_start: () => void;\n worker_stop: () => void;\n};\n\nexport type JobQueueWorkerEvents = keyof JobQueueWorkerEventListeners<unknown, unknown>;\n\n/**\n * Options for creating a JobQueueWorker\n */\nexport interface JobQueueWorkerOptions<Input, Output> {\n readonly storage: IQueueStorage<Input, Output>;\n readonly queueName: string;\n readonly limiter?: ILimiter;\n readonly pollIntervalMs?: number;\n}\n\ntype JobClass<Input, Output> = new (\n param: JobConstructorParam<Input, Output>\n) => Job<Input, Output>;\n\n/**\n * Worker that processes jobs from the queue.\n * Reports progress and completion back to storage.\n */\nexport class JobQueueWorker<\n Input,\n Output,\n QueueJob extends Job<Input, Output> = Job<Input, Output>,\n> {\n public readonly queueName: string;\n public readonly workerId: string;\n protected readonly storage: IQueueStorage<Input, Output>;\n protected readonly jobClass: JobClass<Input, Output>;\n protected readonly limiter: ILimiter;\n protected readonly pollIntervalMs: number;\n protected readonly events = new EventEmitter<JobQueueWorkerEventListeners<Input, Output>>();\n\n protected running = false;\n\n /**\n * Abort controllers for active jobs\n */\n protected readonly activeJobAbortControllers: Map<unknown, AbortController> = new Map();\n\n /**\n * Processing times for statistics\n */\n protected readonly processingTimes: Map<unknown, number> = new Map();\n\n constructor(jobClass: JobClass<Input, Output>, options: JobQueueWorkerOptions<Input, Output>) {\n this.queueName = options.queueName;\n this.workerId = uuid4();\n this.storage = options.storage;\n this.jobClass = jobClass;\n this.limiter = options.limiter ?? new NullLimiter();\n this.pollIntervalMs = options.pollIntervalMs ?? 100;\n }\n\n /**\n * Start the worker processing loop\n */\n public async start(): Promise<this> {\n if (this.running) {\n return this;\n }\n this.running = true;\n this.events.emit(\"worker_start\");\n this.processJobs();\n return this;\n }\n\n /**\n * Stop the worker and abort any active jobs\n */\n public async stop(): Promise<this> {\n if (!this.running) {\n return this;\n }\n this.running = false;\n\n // Wait for pending operations to settle\n const size = await this.storage.size(JobStatus.PROCESSING);\n const sleepTime = Math.max(100, size * 2);\n await sleep(sleepTime);\n\n // Abort all active jobs\n for (const controller of this.activeJobAbortControllers.values()) {\n if (!controller.signal.aborted) {\n controller.abort();\n }\n }\n\n await sleep(sleepTime);\n this.events.emit(\"worker_stop\");\n return this;\n }\n\n /**\n * Process a single job manually (useful for testing or manual control)\n */\n public async processNext(): Promise<boolean> {\n const canProceed = await this.limiter.canProceed();\n if (!canProceed) {\n return false;\n }\n\n const job = await this.next();\n if (!job) {\n return false;\n }\n\n await this.processSingleJob(job);\n return true;\n }\n\n /**\n * Check if the worker is currently running\n */\n public isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get the number of active jobs being processed\n */\n public getActiveJobCount(): number {\n return this.activeJobAbortControllers.size;\n }\n\n /**\n * Get average processing time\n */\n public getAverageProcessingTime(): number | undefined {\n const times = Array.from(this.processingTimes.values());\n if (times.length === 0) return undefined;\n return times.reduce((a, b) => a + b, 0) / times.length;\n }\n\n // ========================================================================\n // Event handling\n // ========================================================================\n\n public on<Event extends JobQueueWorkerEvents>(\n event: Event,\n listener: JobQueueWorkerEventListeners<Input, Output>[Event]\n ): void {\n this.events.on(event, listener);\n }\n\n public off<Event extends JobQueueWorkerEvents>(\n event: Event,\n listener: JobQueueWorkerEventListeners<Input, Output>[Event]\n ): void {\n this.events.off(event, listener);\n }\n\n // ========================================================================\n // Protected methods\n // ========================================================================\n\n /**\n * Get the next job from the queue\n */\n protected async next(): Promise<QueueJob | undefined> {\n const job = await this.storage.next(this.workerId);\n if (!job) return undefined;\n return this.storageToClass(job) as QueueJob;\n }\n\n /**\n * Main job processing loop\n */\n protected async processJobs(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n try {\n // Check for aborting jobs\n await this.checkForAbortingJobs();\n\n const canProceed = await this.limiter.canProceed();\n if (canProceed) {\n const job = await this.next();\n if (job) {\n // Don't await - process in background to allow concurrent jobs\n this.processSingleJob(job);\n }\n }\n } finally {\n if (this.running) {\n setTimeout(() => this.processJobs(), this.pollIntervalMs);\n }\n }\n }\n\n /**\n * Check for jobs that have been marked for abort and trigger their abort controllers\n */\n protected async checkForAbortingJobs(): Promise<void> {\n const abortingJobs = await this.storage.peek(JobStatus.ABORTING);\n for (const jobData of abortingJobs) {\n const controller = this.activeJobAbortControllers.get(jobData.id);\n if (controller && !controller.signal.aborted) {\n controller.abort();\n }\n }\n }\n\n /**\n * Process a single job\n */\n protected async processSingleJob(job: Job<Input, Output>): Promise<void> {\n if (!job || !job.id) {\n throw new JobNotFoundError(\"Invalid job provided for processing\");\n }\n\n const startTime = Date.now();\n\n try {\n await this.validateJobState(job);\n await this.limiter.recordJobStart();\n\n const abortController = this.createAbortController(job.id);\n this.events.emit(\"job_start\", job.id);\n\n const output = await this.executeJob(job, abortController.signal);\n await this.completeJob(job, output);\n\n this.processingTimes.set(job.id, Date.now() - startTime);\n } catch (err: unknown) {\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 }\n\n /**\n * Execute a job with the provided abort signal\n */\n protected 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 * Update progress for a job\n */\n protected async updateProgress(\n jobId: unknown,\n progress: number,\n message: string = \"\",\n details: Record<string, unknown> | null = null\n ): Promise<void> {\n // Validate progress value\n progress = Math.max(0, Math.min(100, progress));\n\n await this.storage.saveProgress(jobId, progress, message, details);\n this.events.emit(\"job_progress\", jobId, progress, message, details);\n }\n\n /**\n * Mark a job as completed\n */\n protected async completeJob(job: Job<Input, Output>, output?: Output): Promise<void> {\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\n await this.storage.complete(this.classToStorage(job));\n this.events.emit(\"job_complete\", job.id, output as Output);\n } catch (err) {\n console.error(\"completeJob errored:\", err);\n } finally {\n this.cleanupJob(job.id);\n }\n }\n\n /**\n * Mark a job as failed\n */\n protected async failJob(job: Job<Input, Output>, error: JobError): Promise<void> {\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 this.events.emit(\"job_error\", job.id, error.message, error.constructor.name);\n } catch (err) {\n console.error(\"failJob errored:\", err);\n } finally {\n this.cleanupJob(job.id);\n }\n }\n\n /**\n * Mark a job as disabled\n */\n protected async disableJob(job: Job<Input, Output>): Promise<void> {\n try {\n job.status = JobStatus.DISABLED;\n job.progress = 100;\n job.completedAt = new Date();\n job.progressMessage = \"\";\n job.progressDetails = null;\n\n await this.storage.complete(this.classToStorage(job));\n this.events.emit(\"job_disabled\", job.id);\n } catch (err) {\n console.error(\"disableJob errored:\", err);\n } finally {\n this.cleanupJob(job.id);\n }\n }\n\n /**\n * Reschedule a job for retry\n */\n protected async rescheduleJob(job: Job<Input, Output>, retryDate?: Date): Promise<void> {\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\n await this.storage.complete(this.classToStorage(job));\n this.events.emit(\"job_retry\", job.id, job.runAfter);\n } catch (err) {\n console.error(\"rescheduleJob errored:\", err);\n }\n }\n\n /**\n * Create an abort controller for a job\n */\n protected createAbortController(jobId: unknown): AbortController {\n if (!jobId) throw new JobNotFoundError(\"Cannot create abort controller for undefined job\");\n\n if (this.activeJobAbortControllers.has(jobId)) {\n return this.activeJobAbortControllers.get(jobId)!;\n }\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 * Handle job abort\n */\n protected async handleAbort(jobId: unknown): Promise<void> {\n const job = await this.getJob(jobId);\n if (!job) {\n console.error(\"handleAbort: job not found\", jobId);\n return;\n }\n const error = new AbortSignalJobError(\"Job Aborted\");\n await this.failJob(job, error);\n }\n\n /**\n * Get a job by ID\n */\n protected async getJob(id: unknown): Promise<Job<Input, Output> | undefined> {\n const job = await this.storage.get(id);\n if (!job) return undefined;\n return this.storageToClass(job);\n }\n\n /**\n * Validate job state 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 * Normalize errors into JobError instances\n */\n protected normalizeError(err: unknown): JobError {\n if (err instanceof JobError) {\n return err;\n }\n if (err instanceof Error) {\n return new PermanentJobError(err.message);\n }\n return new PermanentJobError(String(err));\n }\n\n /**\n * Clean up job state after completion/failure\n */\n protected cleanupJob(jobId: unknown): void {\n this.activeJobAbortControllers.delete(jobId);\n }\n\n /**\n * Convert storage format to Job class\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 return 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 Input,\n output: details.output 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 }\n\n /**\n * Convert Job class to storage format\n */\n protected classToStorage(job: Job<Input, Output>): JobStorageFormat<Input, Output> {\n const dateToISOString = (date: Date | null | undefined): string | null => {\n if (!date) return null;\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",
11
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
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
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
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"
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\";\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",
16
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { IRateLimiterStorage } from \"@workglow/storage\";\nimport { ILimiter, RateLimiterWithBackoffOptions } from \"./ILimiter\";\n\n/**\n * Base rate limiter implementation that uses a storage backend.\n * Manages request counts and delays to control job execution.\n */\nexport class RateLimiter implements ILimiter {\n protected readonly windowSizeInMilliseconds: number;\n protected currentBackoffDelay: number;\n protected readonly maxExecutions: number;\n protected readonly initialBackoffDelay: number;\n protected readonly backoffMultiplier: number;\n protected readonly maxBackoffDelay: number;\n\n constructor(\n protected readonly storage: IRateLimiterStorage,\n protected readonly queueName: string,\n {\n maxExecutions,\n windowSizeInSeconds,\n initialBackoffDelay = 1_000,\n backoffMultiplier = 2,\n maxBackoffDelay = 600_000, // 10 minutes\n }: RateLimiterWithBackoffOptions\n ) {\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.windowSizeInMilliseconds = windowSizeInSeconds * 1000;\n this.maxExecutions = maxExecutions;\n this.initialBackoffDelay = initialBackoffDelay;\n this.backoffMultiplier = backoffMultiplier;\n this.maxBackoffDelay = maxBackoffDelay;\n this.currentBackoffDelay = initialBackoffDelay;\n }\n\n protected addJitter(base: number): number {\n // full jitter in [base, 2*base)\n return base + Math.random() * base;\n }\n\n protected increaseBackoff(): void {\n this.currentBackoffDelay = Math.min(\n this.currentBackoffDelay * this.backoffMultiplier,\n this.maxBackoffDelay\n );\n }\n\n /**\n * Checks if a job can proceed based on rate limiting rules.\n * @returns True if the job can proceed, false otherwise\n */\n async canProceed(): Promise<boolean> {\n // First check if the window allows more executions\n const windowStartTime = new Date(Date.now() - this.windowSizeInMilliseconds).toISOString();\n const attemptCount = await this.storage.getExecutionCount(this.queueName, windowStartTime);\n const canProceedNow = attemptCount < this.maxExecutions;\n\n // If the window allows more executions, clear any backoff and proceed\n if (canProceedNow) {\n // Clear any existing nextAvailableTime backoff since the window allows more executions\n const nextAvailableTime = await this.storage.getNextAvailableTime(this.queueName);\n if (nextAvailableTime && new Date(nextAvailableTime).getTime() > Date.now()) {\n // Clear the backoff by setting it to the past\n const pastTime = new Date(Date.now() - 1000);\n await this.storage.setNextAvailableTime(this.queueName, pastTime.toISOString());\n }\n this.currentBackoffDelay = this.initialBackoffDelay;\n return true;\n }\n\n // Window is full, check if there's a backoff delay\n const nextAvailableTime = await this.storage.getNextAvailableTime(this.queueName);\n if (nextAvailableTime && new Date(nextAvailableTime).getTime() > Date.now()) {\n this.increaseBackoff();\n return false;\n }\n\n // Window is full but no backoff delay, so we can't proceed\n this.increaseBackoff();\n return false;\n }\n\n /**\n * Records a new job attempt.\n */\n async recordJobStart(): Promise<void> {\n await this.storage.recordExecution(this.queueName);\n\n const windowStartTime = new Date(Date.now() - this.windowSizeInMilliseconds).toISOString();\n const attemptCount = await this.storage.getExecutionCount(this.queueName, windowStartTime);\n\n if (attemptCount >= this.maxExecutions) {\n const backoffExpires = new Date(Date.now() + this.addJitter(this.currentBackoffDelay));\n await this.setNextAvailableTime(backoffExpires);\n } else {\n // Window allows more executions, clear any existing nextAvailableTime by setting it to the past\n const nextAvailableTime = await this.storage.getNextAvailableTime(this.queueName);\n if (nextAvailableTime && new Date(nextAvailableTime).getTime() > Date.now()) {\n // Clear the backoff since the window now allows more executions\n // Set to a time in the past to effectively clear it\n const pastTime = new Date(Date.now() - 1000);\n await this.storage.setNextAvailableTime(this.queueName, pastTime.toISOString());\n }\n }\n }\n\n async recordJobCompletion(): Promise<void> {\n // Implementation can be no-op as completion doesn't affect rate limiting\n }\n\n /**\n * Retrieves the next available time for the specific queue.\n * @returns The next available time\n */\n async getNextAvailableTime(): Promise<Date> {\n // Get the time when the rate limit will allow the next job execution\n const oldestExecution = await this.storage.getOldestExecutionAtOffset(\n this.queueName,\n this.maxExecutions - 1\n );\n\n let rateLimitedTime = new Date();\n if (oldestExecution) {\n rateLimitedTime = new Date(oldestExecution);\n rateLimitedTime.setSeconds(\n rateLimitedTime.getSeconds() + this.windowSizeInMilliseconds / 1000\n );\n }\n\n // Get the next available time set externally, if any\n const nextAvailableStr = await this.storage.getNextAvailableTime(this.queueName);\n let nextAvailableTime = new Date();\n if (nextAvailableStr) {\n nextAvailableTime = new Date(nextAvailableStr);\n }\n\n // Return the later of the two times\n return nextAvailableTime > rateLimitedTime ? nextAvailableTime : rateLimitedTime;\n }\n\n /**\n * Sets the next available time for the specific queue.\n * @param date - The new next available time\n */\n async setNextAvailableTime(date: Date): Promise<void> {\n await this.storage.setNextAvailableTime(this.queueName, date.toISOString());\n }\n\n /**\n * Clears all rate limit entries for this queue.\n */\n async clear(): Promise<void> {\n await this.storage.clear(this.queueName);\n this.currentBackoffDelay = this.initialBackoffDelay;\n }\n}\n"
16
17
  ],
17
- "mappings": "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",
18
- "debugId": "4FA097AF2EE0650C64756E2164756E21",
18
+ "mappings": ";AAMA;;;ACAA;AAAA;AAEO,MAAM,iBAAiB,UAAU;AAAA,EAGnB;AAAA,SAFL,OAAe;AAAA,EACtB,YAAY;AAAA,EACnB,WAAW,CAAQ,SAAiB;AAAA,IAClC,MAAM,OAAO;AAAA,IADI;AAAA;AAGrB;AAAA;AAOO,MAAM,yBAAyB,SAAS;AAAA,SAC/B,OAAe;AAAA,EAC7B,WAAW,CAAC,UAAkB,iBAAiB;AAAA,IAC7C,MAAM,OAAO;AAAA;AAEjB;AAAA;AAOO,MAAM,0BAA0B,SAAS;AAAA,EAIrC;AAAA,SAHK,OAAe;AAAA,EAC7B,WAAW,CACT,SACO,WACP;AAAA,IACA,MAAM,OAAO;AAAA,IAFN;AAAA,IAGP,KAAK,YAAY;AAAA;AAErB;AAAA;AAQO,MAAM,0BAA0B,SAAS;AAAA,SAChC,OAAe;AAAA,EAC7B,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA;AAEjB;AAAA;AASO,MAAM,4BAA4B,kBAAkB;AAAA,SAC3C,OAAe;AAAA,EAC7B,WAAW,CAAC,SAAiB;AAAA,IAC3B,MAAM,OAAO;AAAA;AAEjB;AAAA;AAOO,MAAM,yBAAyB,kBAAkB;AAAA,SACxC,OAAe;AAC/B;;;ADvBO,MAAM,IAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAoB,UAAU;AAAA,EAC9B;AAAA,EACA,SAAwB;AAAA,EACxB,cAAsB;AAAA,EACtB,YAAyB;AAAA,EACzB,cAA2B;AAAA,EAC3B,aAA0B;AAAA,EAC1B,QAAuB;AAAA,EACvB,YAA2B;AAAA,EAC3B,WAAmB;AAAA,EACnB,kBAA0B;AAAA,EAC1B,kBAA8C;AAAA,EAErD,WAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,SAAS;AAAA,IACT,aAAa;AAAA,IACb,YAAY,IAAI;AAAA,IAChB,cAAc;AAAA,IACd,SAAS,UAAU;AAAA,IACnB,aAAa;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,KACmB;AAAA,IACrC,KAAK,WAAW,YAAY,IAAI;AAAA,IAChC,KAAK,YAAY,aAAa,IAAI;AAAA,IAClC,KAAK,YAAY,aAAa;AAAA,IAC9B,KAAK,aAAa,cAAc;AAAA,IAChC,KAAK,cAAc,eAAe;AAAA,IAElC,KAAK,YAAY;AAAA,IACjB,KAAK,KAAK;AAAA,IACV,KAAK,WAAW;AAAA,IAChB,KAAK,SAAS;AAAA,IACd,KAAK,cAAc;AAAA,IACnB,KAAK,QAAQ;AAAA,IACb,KAAK,aAAa;AAAA,IAClB,KAAK,cAAc;AAAA,IACnB,KAAK,SAAS;AAAA,IACd,KAAK,QAAQ;AAAA,IACb,KAAK,YAAY;AAAA,IACjB,KAAK,WAAW;AAAA,IAChB,KAAK,kBAAkB;AAAA,IACvB,KAAK,kBAAkB;AAAA;AAAA,OAGnB,QAAO,CAAC,OAAc,SAA8C;AAAA,IACxE,MAAM,IAAI,SAAS,yBAAyB;AAAA;AAAA,EAGtC,oBAA8C,IAAI;AAAA,OAQ7C,eAAc,CACzB,UACA,UAAkB,IAClB,UAAsC,MACvB;AAAA,IACf,KAAK,WAAW;AAAA,IAChB,KAAK,kBAAkB;AAAA,IACvB,KAAK,kBAAkB;AAAA,IAGvB,WAAW,YAAY,KAAK,mBAAmB;AAAA,MAC7C,SAAS,UAAU,SAAS,OAAO;AAAA,IACrC;AAAA;AAAA,EAWK,aAAa,CAAC,UAA2C;AAAA,IAC9D,KAAK,kBAAkB,IAAI,QAAQ;AAAA,IAEnC,OAAO,MAAM;AAAA,MACX,KAAK,kBAAkB,OAAO,QAAQ;AAAA;AAAA;AAG5C;;AE3JA,sBAAwB;AACxB;AA8CO,MAAM,eAA8B;AAAA,EACzB;AAAA,EACG;AAAA,EACA,SAAS,IAAI;AAAA,EACtB,SAA+C;AAAA,EAC/C,qBAA0C;AAAA,EAKjC,oBAMf,IAAI;AAAA,EAKW,uBAA+D,IAAI;AAAA,EAKnE,oBAOf,IAAI;AAAA,EAER,WAAW,CAAC,SAA+C;AAAA,IACzD,KAAK,YAAY,QAAQ;AAAA,IACzB,KAAK,UAAU,QAAQ;AAAA;AAAA,EAOlB,MAAM,CAAC,QAA6C;AAAA,IACzD,IAAI,KAAK,QAAQ;AAAA,MACf,KAAK,OAAO;AAAA,IACd;AAAA,IACA,KAAK,SAAS;AAAA,IACd,OAAO,UAAU,IAAI;AAAA,IAGrB,IAAI,KAAK,oBAAoB;AAAA,MAC3B,KAAK,mBAAmB;AAAA,MACxB,KAAK,qBAAqB;AAAA,IAC5B;AAAA;AAAA,EAMK,MAAM,GAAS;AAAA,IACpB,IAAI,KAAK,QAAQ;AAAA,MACf,KAAK,OAAO,aAAa,IAAI;AAAA,MAC7B,KAAK,SAAS;AAAA,IAChB;AAAA;AAAA,EAOK,OAAO,GAAS;AAAA,IACrB,IAAI,KAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAAA,IAEA,IAAI,KAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA,KAAK,qBAAqB,KAAK,QAAQ,mBACrC,CAAC,WAA8C;AAAA,MAC7C,KAAK,oBAAoB,MAAM;AAAA,KAEnC;AAAA;AAAA,EAMK,UAAU,GAAS;AAAA,IACxB,IAAI,KAAK,oBAAoB;AAAA,MAC3B,KAAK,mBAAmB;AAAA,MACxB,KAAK,qBAAqB;AAAA,IAC5B;AAAA,IACA,KAAK,OAAO;AAAA;AAAA,OAMD,OAAM,CACjB,OACA,SAO4B;AAAA,IAC5B,MAAM,MAAuC;AAAA,MAC3C,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,YAAY,SAAS;AAAA,MACrB,aAAa,SAAS;AAAA,MACtB,aAAa,SAAS,cAAc;AAAA,MACpC,WAAW,SAAS,UAAU,YAAY,KAAK,IAAI,KAAK,EAAE,YAAY;AAAA,MACtE,aAAa,SAAS,YAAY,YAAY,KAAK;AAAA,MACnD,cAAc;AAAA,MACd,QAAQ,WAAU;AAAA,IACpB;AAAA,IAEA,MAAM,KAAK,MAAM,KAAK,QAAQ,IAAI,GAAG;AAAA,IAErC,OAAO,KAAK,gBAAgB,EAAE;AAAA;AAAA,OAMnB,YAAW,CACtB,QACA,SAIuC;AAAA,IACvC,MAAM,UAA+B,CAAC;AAAA,IACtC,WAAW,SAAS,QAAQ;AAAA,MAC1B,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,OAAO;AAAA,MAC/C,QAAQ,KAAK,MAAM;AAAA,IACrB;AAAA,IACA,OAAO;AAAA;AAAA,OAMI,OAAM,CAAC,IAAsD;AAAA,IACxE,IAAI,CAAC;AAAA,MAAI,MAAM,IAAI,iBAAiB,0BAA0B;AAAA,IAC9D,MAAM,MAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAAA,IACrC,IAAI,CAAC;AAAA,MAAK;AAAA,IACV,OAAO,KAAK,eAAe,GAAG;AAAA;AAAA,OAMnB,eAAc,CAAC,OAAuD;AAAA,IACjF,IAAI,CAAC;AAAA,MAAO,MAAM,IAAI,iBAAiB,oCAAoC;AAAA,IAC3E,MAAM,OAAO,MAAM,KAAK,QAAQ,WAAW,KAAK;AAAA,IAChD,OAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,eAAe,GAAG,CAAC;AAAA;AAAA,OAMtC,KAAI,CAAC,QAAoB,KAAsD;AAAA,IAC1F,MAAM,OAAO,MAAM,KAAK,QAAQ,KAAK,QAAQ,GAAG;AAAA,IAChD,OAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,eAAe,GAAG,CAAC;AAAA;AAAA,OAMtC,KAAI,CAAC,QAAqC;AAAA,IACrD,OAAO,KAAK,QAAQ,KAAK,MAAM;AAAA;AAAA,OAMpB,eAAc,CAAC,OAAsC;AAAA,IAChE,IAAI,CAAC;AAAA,MAAO,MAAM,IAAI,iBAAiB,uCAAuC;AAAA,IAC9E,OAAO,KAAK,QAAQ,eAAe,KAAK;AAAA;AAAA,OAM7B,QAAO,CAAC,OAAiC;AAAA,IACpD,IAAI,CAAC;AAAA,MAAO,MAAM,IAAI,iBAAiB,+BAA+B;AAAA,IAEtE,MAAM,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,IACnC,IAAI,CAAC;AAAA,MAAK,MAAM,IAAI,iBAAiB,OAAO,iBAAiB;AAAA,IAE7D,IAAI,IAAI,WAAW,WAAU,WAAW;AAAA,MACtC,OAAO,IAAI;AAAA,IACb;AAAA,IACA,IAAI,IAAI,WAAW,WAAU,UAAU;AAAA,MACrC,MAAM,IAAI,iBAAiB,OAAO,oBAAoB;AAAA,IACxD;AAAA,IACA,IAAI,IAAI,WAAW,WAAU,QAAQ;AAAA,MACnC,MAAM,KAAK,kBAAkB,GAAG;AAAA,IAClC;AAAA,IAEA,QAAQ,SAAS,SAAS,WAAW,QAAQ,cAAsB;AAAA,IACnE,QAAQ,MAAM,MAAM,EAAE;AAAA,IAEtB,MAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK,KAAK,CAAC;AAAA,IACvD,SAAS,KAAK,EAAE,SAAS,OAAO,CAAC;AAAA,IACjC,KAAK,kBAAkB,IAAI,OAAO,QAAQ;AAAA,IAE1C,OAAO;AAAA;AAAA,OAMI,MAAK,CAAC,OAA+B;AAAA,IAChD,IAAI,CAAC;AAAA,MAAO,MAAM,IAAI,iBAAiB,4BAA4B;AAAA,IACnE,MAAM,KAAK,QAAQ,MAAM,KAAK;AAAA,IAC9B,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,KAAK;AAAA;AAAA,OAM3C,YAAW,CAAC,UAAiC;AAAA,IACxD,IAAI,CAAC;AAAA,MAAU,MAAM,IAAI,iBAAiB,8CAA8C;AAAA,IACxF,MAAM,OAAO,MAAM,KAAK,eAAe,QAAQ;AAAA,IAC/C,MAAM,QAAQ,WACZ,KAAK,IAAI,CAAC,QAAQ;AAAA,MAChB,IAAI,CAAC,WAAU,YAAY,WAAU,OAAO,EAAE,SAAS,IAAI,MAAM,GAAG;AAAA,QAClE,OAAO,KAAK,MAAM,IAAI,EAAE;AAAA,MAC1B;AAAA,KACD,CACH;AAAA;AAAA,EAMK,aAAa,CAAC,OAAgB,UAA2C;AAAA,IAC9E,IAAI,CAAC,KAAK,qBAAqB,IAAI,KAAK,GAAG;AAAA,MACzC,KAAK,qBAAqB,IAAI,OAAO,IAAI,GAAK;AAAA,IAChD;AAAA,IACA,MAAM,YAAY,KAAK,qBAAqB,IAAI,KAAK;AAAA,IACrD,UAAU,IAAI,QAAQ;AAAA,IAEtB,OAAO,MAAM;AAAA,MACX,MAAM,aAAY,KAAK,qBAAqB,IAAI,KAAK;AAAA,MACrD,IAAI,YAAW;AAAA,QACb,WAAU,OAAO,QAAQ;AAAA,QACzB,IAAI,WAAU,SAAS,GAAG;AAAA,UACxB,KAAK,qBAAqB,OAAO,KAAK;AAAA,QACxC;AAAA,MACF;AAAA;AAAA;AAAA,EAQG,EAAgC,CACrC,OACA,UACM;AAAA,IACN,KAAK,OAAO,GAAG,OAAO,QAAQ;AAAA;AAAA,EAGzB,GAAiC,CACtC,OACA,UACM;AAAA,IACN,KAAK,OAAO,IAAI,OAAO,QAAQ;AAAA;AAAA,EAG1B,IAAkC,CACvC,OACA,UACM;AAAA,IACN,KAAK,OAAO,KAAK,OAAO,QAAQ;AAAA;AAAA,EAG3B,MAAoC,CACzC,OACwD;AAAA,IACxD,OAAO,KAAK,OAAO,OAAO,KAAK;AAAA;AAAA,EAW1B,cAAc,CAAC,OAAsB;AAAA,IAC1C,KAAK,kBAAkB,IAAI,OAAO;AAAA,MAChC,UAAU;AAAA,MACV,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,IACD,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,KAAK;AAAA;AAAA,EAO9C,iBAAiB,CAAC,OAAgB,QAAsB;AAAA,IAC7D,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,OAAO,MAAM;AAAA,IAE9D,MAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK;AAAA,IACjD,IAAI,UAAU;AAAA,MACZ,SAAS,QAAQ,GAAG,cAAc,QAAQ,MAAM,CAAC;AAAA,IACnD;AAAA,IACA,KAAK,WAAW,KAAK;AAAA;AAAA,EAOhB,cAAc,CAAC,OAAgB,OAAe,WAA0B;AAAA,IAC7E,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,OAAO,KAAK;AAAA,IAE1D,MAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK;AAAA,IACjD,IAAI,UAAU;AAAA,MACZ,MAAM,WAAW,KAAK,mBAAmB,OAAO,SAAS;AAAA,MACzD,SAAS,QAAQ,GAAG,aAAa,OAAO,QAAQ,CAAC;AAAA,IACnD;AAAA,IACA,KAAK,WAAW,KAAK;AAAA;AAAA,EAOhB,iBAAiB,CAAC,OAAsB;AAAA,IAC7C,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,KAAK;AAAA,IAEtD,MAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK;AAAA,IACjD,IAAI,UAAU;AAAA,MACZ,SAAS,QAAQ,GAAG,aAAa,OAAO,IAAI,iBAAiB,kBAAkB,CAAC,CAAC;AAAA,IACnF;AAAA,IACA,KAAK,WAAW,KAAK;AAAA;AAAA,EAOhB,cAAc,CAAC,OAAgB,UAAsB;AAAA,IAC1D,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,OAAO,QAAQ;AAAA;AAAA,EAOxD,iBAAiB,CACtB,OACA,UACA,SACA,SACM;AAAA,IACN,KAAK,kBAAkB,IAAI,OAAO,EAAE,UAAU,SAAS,QAAQ,CAAC;AAAA,IAChE,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,OAAO,UAAU,SAAS,OAAO;AAAA,IAElF,MAAM,YAAY,KAAK,qBAAqB,IAAI,KAAK;AAAA,IACrD,IAAI,WAAW;AAAA,MACb,WAAW,YAAY,WAAW;AAAA,QAChC,SAAS,UAAU,SAAS,OAAO;AAAA,MACrC;AAAA,IACF;AAAA;AAAA,EAOM,eAAe,CAAC,IAAgC;AAAA,IACtD,OAAO;AAAA,MACL;AAAA,MACA,SAAS,MAAM,KAAK,QAAQ,EAAE;AAAA,MAC9B,OAAO,MAAM,KAAK,MAAM,EAAE;AAAA,MAC1B,YAAY,CAAC,aAAkC,KAAK,cAAc,IAAI,QAAQ;AAAA,IAChF;AAAA;AAAA,EAGM,UAAU,CAAC,OAAsB;AAAA,IACvC,KAAK,kBAAkB,OAAO,KAAK;AAAA,IACnC,KAAK,kBAAkB,OAAO,KAAK;AAAA,IACnC,KAAK,qBAAqB,OAAO,KAAK;AAAA;AAAA,EAGhC,mBAAmB,CAAC,QAAiD;AAAA,IAC3E,IAAI,CAAC,OAAO,OAAO,CAAC,OAAO;AAAA,MAAK;AAAA,IAEhC,MAAM,QAAQ,OAAO,KAAK,MAAM,OAAO,KAAK;AAAA,IAC5C,IAAI,CAAC;AAAA,MAAO;AAAA,IAGZ,MAAM,YAAY,OAAO,KAAK,SAAS,OAAO,KAAK;AAAA,IACnD,IAAI,cAAc,KAAK;AAAA,MAAW;AAAA,IAElC,IAAI,OAAO,SAAS,YAAY,OAAO,KAAK;AAAA,MAC1C,MAAM,YAAY,OAAO,IAAI;AAAA,MAC7B,MAAM,YAAY,OAAO,KAAK;AAAA,MAE9B,IAAI,cAAc,WAAU,cAAc,cAAc,WAAU,SAAS;AAAA,QACzE,KAAK,eAAe,KAAK;AAAA,MAC3B,EAAO,SAAI,cAAc,WAAU,WAAW;AAAA,QAC5C,KAAK,kBAAkB,OAAO,OAAO,IAAI,MAAgB;AAAA,MAC3D,EAAO,SAAI,cAAc,WAAU,QAAQ;AAAA,QACzC,KAAK,eACH,OACA,OAAO,IAAI,SAAS,cACpB,OAAO,IAAI,cAAc,SAC3B;AAAA,MACF,EAAO,SAAI,cAAc,WAAU,UAAU;AAAA,QAC3C,KAAK,kBAAkB,KAAK;AAAA,MAC9B,EAAO,SAAI,cAAc,WAAU,WAAW,cAAc,WAAU,YAAY;AAAA,QAEhF,MAAM,WAAW,OAAO,IAAI,YAAY,IAAI,KAAK,OAAO,IAAI,SAAS,IAAI,IAAI;AAAA,QAC7E,KAAK,eAAe,OAAO,QAAQ;AAAA,MACrC;AAAA,MAGA,IACE,OAAO,IAAI,aAAa,OAAO,KAAK,YACpC,OAAO,IAAI,qBAAqB,OAAO,KAAK,kBAC5C;AAAA,QACA,KAAK,kBACH,OACA,OAAO,IAAI,YAAY,GACvB,OAAO,IAAI,oBAAoB,IAC/B,OAAO,IAAI,oBAAoB,IACjC;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAGQ,cAAc,CAAC,SAA8D;AAAA,IACrF,MAAM,SAAS,CAAC,SAAiD;AAAA,MAC/D,IAAI,CAAC;AAAA,QAAM,OAAO;AAAA,MAClB,MAAM,IAAI,IAAI,KAAK,IAAI;AAAA,MACvB,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO;AAAA;AAAA,IAErC,OAAO,IAAI,IAAmB;AAAA,MAC5B,IAAI,QAAQ;AAAA,MACZ,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,UAAU,OAAO,QAAQ,SAAS;AAAA,MAClC,WAAW,OAAO,QAAQ,UAAU;AAAA,MACpC,YAAY,OAAO,QAAQ,WAAW;AAAA,MACtC,WAAW,OAAO,QAAQ,WAAW;AAAA,MACrC,aAAa,OAAO,QAAQ,YAAY;AAAA,MACxC,UAAU,QAAQ,YAAY;AAAA,MAC9B,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ,SAAS;AAAA,MACxB,WAAW,QAAQ,cAAc;AAAA,MACjC,aAAa,QAAQ,gBAAgB;AAAA,MACrC,YAAY,QAAQ,eAAe;AAAA,IACrC,CAAC;AAAA;AAAA,EAGO,iBAAiB,CAAC,KAAmC;AAAA,IAC7D,OAAO,KAAK,mBAAmB,IAAI,SAAS,cAAc,IAAI,aAAa,SAAS;AAAA;AAAA,EAG5E,kBAAkB,CAAC,SAAiB,WAA8B;AAAA,IAC1E,IAAI,cAAc,qBAAqB;AAAA,MACrC,OAAO,IAAI,kBAAkB,OAAO;AAAA,IACtC;AAAA,IACA,IAAI,cAAc,qBAAqB;AAAA,MACrC,OAAO,IAAI,kBAAkB,OAAO;AAAA,IACtC;AAAA,IACA,IAAI,cAAc,uBAAuB;AAAA,MACvC,OAAO,IAAI,oBAAoB,OAAO;AAAA,IACxC;AAAA,IACA,IAAI,cAAc,oBAAoB;AAAA,MACpC,OAAO,IAAI,iBAAiB,OAAO;AAAA,IACrC;AAAA,IACA,OAAO,IAAI,SAAS,OAAO;AAAA;AAE/B;;AChiBA,sBAAwB;AACxB,yBAAS;;;ACDT;AAGO,IAAM,mBAAmB,mBAA6B,uBAAuB;AAAA;AAK7E,MAAM,YAAgC;AAAA,OACrC,WAAU,GAAqB;AAAA,IACnC,OAAO;AAAA;AAAA,OAGH,eAAc,GAAkB;AAAA,OAIhC,oBAAmB,GAAkB;AAAA,OAIrC,qBAAoB,GAAkB;AAAA,IAC1C,OAAO,IAAI;AAAA;AAAA,OAGP,qBAAoB,CAAC,MAA2B;AAAA,OAIhD,MAAK,GAAkB;AAG/B;;;AChCA,sBAAwB;AACxB,yBAAS;AAoDF,MAAM,eAIX;AAAA,EACgB;AAAA,EACA;AAAA,EACG;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,IAAI;AAAA,EAEtB,UAAU;AAAA,EAKD,4BAA2D,IAAI;AAAA,EAK/D,kBAAwC,IAAI;AAAA,EAE/D,WAAW,CAAC,UAAmC,SAA+C;AAAA,IAC5F,KAAK,YAAY,QAAQ;AAAA,IACzB,KAAK,WAAW,MAAM;AAAA,IACtB,KAAK,UAAU,QAAQ;AAAA,IACvB,KAAK,WAAW;AAAA,IAChB,KAAK,UAAU,QAAQ,WAAW,IAAI;AAAA,IACtC,KAAK,iBAAiB,QAAQ,kBAAkB;AAAA;AAAA,OAMrC,MAAK,GAAkB;AAAA,IAClC,IAAI,KAAK,SAAS;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AAAA,IACf,KAAK,OAAO,KAAK,cAAc;AAAA,IAC/B,KAAK,YAAY;AAAA,IACjB,OAAO;AAAA;AAAA,OAMI,KAAI,GAAkB;AAAA,IACjC,IAAI,CAAC,KAAK,SAAS;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,KAAK,UAAU;AAAA,IAGf,MAAM,OAAO,MAAM,KAAK,QAAQ,KAAK,WAAU,UAAU;AAAA,IACzD,MAAM,YAAY,KAAK,IAAI,KAAK,OAAO,CAAC;AAAA,IACxC,MAAM,MAAM,SAAS;AAAA,IAGrB,WAAW,cAAc,KAAK,0BAA0B,OAAO,GAAG;AAAA,MAChE,IAAI,CAAC,WAAW,OAAO,SAAS;AAAA,QAC9B,WAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,IAEA,MAAM,MAAM,SAAS;AAAA,IACrB,KAAK,OAAO,KAAK,aAAa;AAAA,IAC9B,OAAO;AAAA;AAAA,OAMI,YAAW,GAAqB;AAAA,IAC3C,MAAM,aAAa,MAAM,KAAK,QAAQ,WAAW;AAAA,IACjD,IAAI,CAAC,YAAY;AAAA,MACf,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,MAAM,MAAM,KAAK,KAAK;AAAA,IAC5B,IAAI,CAAC,KAAK;AAAA,MACR,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,KAAK,iBAAiB,GAAG;AAAA,IAC/B,OAAO;AAAA;AAAA,EAMF,SAAS,GAAY;AAAA,IAC1B,OAAO,KAAK;AAAA;AAAA,EAMP,iBAAiB,GAAW;AAAA,IACjC,OAAO,KAAK,0BAA0B;AAAA;AAAA,EAMjC,wBAAwB,GAAuB;AAAA,IACpD,MAAM,QAAQ,MAAM,KAAK,KAAK,gBAAgB,OAAO,CAAC;AAAA,IACtD,IAAI,MAAM,WAAW;AAAA,MAAG;AAAA,IACxB,OAAO,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,MAAM;AAAA;AAAA,EAO3C,EAAsC,CAC3C,OACA,UACM;AAAA,IACN,KAAK,OAAO,GAAG,OAAO,QAAQ;AAAA;AAAA,EAGzB,GAAuC,CAC5C,OACA,UACM;AAAA,IACN,KAAK,OAAO,IAAI,OAAO,QAAQ;AAAA;AAAA,OAUjB,KAAI,GAAkC;AAAA,IACpD,MAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,KAAK,QAAQ;AAAA,IACjD,IAAI,CAAC;AAAA,MAAK;AAAA,IACV,OAAO,KAAK,eAAe,GAAG;AAAA;AAAA,OAMhB,YAAW,GAAkB;AAAA,IAC3C,IAAI,CAAC,KAAK,SAAS;AAAA,MACjB;AAAA,IACF;AAAA,IAEA,IAAI;AAAA,MAEF,MAAM,KAAK,qBAAqB;AAAA,MAEhC,MAAM,aAAa,MAAM,KAAK,QAAQ,WAAW;AAAA,MACjD,IAAI,YAAY;AAAA,QACd,MAAM,MAAM,MAAM,KAAK,KAAK;AAAA,QAC5B,IAAI,KAAK;AAAA,UAEP,KAAK,iBAAiB,GAAG;AAAA,QAC3B;AAAA,MACF;AAAA,cACA;AAAA,MACA,IAAI,KAAK,SAAS;AAAA,QAChB,WAAW,MAAM,KAAK,YAAY,GAAG,KAAK,cAAc;AAAA,MAC1D;AAAA;AAAA;AAAA,OAOY,qBAAoB,GAAkB;AAAA,IACpD,MAAM,eAAe,MAAM,KAAK,QAAQ,KAAK,WAAU,QAAQ;AAAA,IAC/D,WAAW,WAAW,cAAc;AAAA,MAClC,MAAM,aAAa,KAAK,0BAA0B,IAAI,QAAQ,EAAE;AAAA,MAChE,IAAI,cAAc,CAAC,WAAW,OAAO,SAAS;AAAA,QAC5C,WAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA;AAAA,OAMc,iBAAgB,CAAC,KAAwC;AAAA,IACvE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI;AAAA,MACnB,MAAM,IAAI,iBAAiB,qCAAqC;AAAA,IAClE;AAAA,IAEA,MAAM,YAAY,KAAK,IAAI;AAAA,IAE3B,IAAI;AAAA,MACF,MAAM,KAAK,iBAAiB,GAAG;AAAA,MAC/B,MAAM,KAAK,QAAQ,eAAe;AAAA,MAElC,MAAM,kBAAkB,KAAK,sBAAsB,IAAI,EAAE;AAAA,MACzD,KAAK,OAAO,KAAK,aAAa,IAAI,EAAE;AAAA,MAEpC,MAAM,SAAS,MAAM,KAAK,WAAW,KAAK,gBAAgB,MAAM;AAAA,MAChE,MAAM,KAAK,YAAY,KAAK,MAAM;AAAA,MAElC,KAAK,gBAAgB,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,SAAS;AAAA,MACvD,OAAO,KAAc;AAAA,MACrB,MAAM,QAAQ,KAAK,eAAe,GAAG;AAAA,MACrC,IAAI,iBAAiB,mBAAmB;AAAA,QACtC,IAAI,IAAI,cAAc,IAAI,YAAY;AAAA,UACpC,MAAM,KAAK,QAAQ,KAAK,KAAK;AAAA,QAC/B,EAAO;AAAA,UACL,MAAM,KAAK,cAAc,KAAK,MAAM,SAAS;AAAA;AAAA,MAEjD,EAAO;AAAA,QACL,MAAM,KAAK,QAAQ,KAAK,KAAK;AAAA;AAAA,cAE/B;AAAA,MACA,MAAM,KAAK,QAAQ,oBAAoB;AAAA;AAAA;AAAA,OAO3B,WAAU,CAAC,KAAyB,QAAsC;AAAA,IACxF,IAAI,CAAC;AAAA,MAAK,MAAM,IAAI,iBAAiB,sCAAsC;AAAA,IAC3E,OAAO,MAAM,IAAI,QAAQ,IAAI,OAAO;AAAA,MAClC;AAAA,MACA,gBAAgB,KAAK,eAAe,KAAK,MAAM,IAAI,EAAE;AAAA,IACvD,CAAC;AAAA;AAAA,OAMa,eAAc,CAC5B,OACA,UACA,UAAkB,IAClB,UAA0C,MAC3B;AAAA,IAEf,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,IAE9C,MAAM,KAAK,QAAQ,aAAa,OAAO,UAAU,SAAS,OAAO;AAAA,IACjE,KAAK,OAAO,KAAK,gBAAgB,OAAO,UAAU,SAAS,OAAO;AAAA;AAAA,OAMpD,YAAW,CAAC,KAAyB,QAAgC;AAAA,IACnF,IAAI;AAAA,MACF,IAAI,SAAS,WAAU;AAAA,MACvB,IAAI,WAAW;AAAA,MACf,IAAI,kBAAkB;AAAA,MACtB,IAAI,kBAAkB;AAAA,MACtB,IAAI,cAAc,IAAI;AAAA,MACtB,IAAI,SAAS,UAAU;AAAA,MACvB,IAAI,QAAQ;AAAA,MACZ,IAAI,YAAY;AAAA,MAEhB,MAAM,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG,CAAC;AAAA,MACpD,KAAK,OAAO,KAAK,gBAAgB,IAAI,IAAI,MAAgB;AAAA,MACzD,OAAO,KAAK;AAAA,MACZ,QAAQ,MAAM,wBAAwB,GAAG;AAAA,cACzC;AAAA,MACA,KAAK,WAAW,IAAI,EAAE;AAAA;AAAA;AAAA,OAOV,QAAO,CAAC,KAAyB,OAAgC;AAAA,IAC/E,IAAI;AAAA,MACF,IAAI,SAAS,WAAU;AAAA,MACvB,IAAI,WAAW;AAAA,MACf,IAAI,cAAc,IAAI;AAAA,MACtB,IAAI,kBAAkB;AAAA,MACtB,IAAI,kBAAkB;AAAA,MACtB,IAAI,QAAQ,MAAM;AAAA,MAClB,IAAI,YAAY,OAAO,aAAa,QAAQ;AAAA,MAE5C,MAAM,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG,CAAC;AAAA,MACpD,KAAK,OAAO,KAAK,aAAa,IAAI,IAAI,MAAM,SAAS,MAAM,YAAY,IAAI;AAAA,MAC3E,OAAO,KAAK;AAAA,MACZ,QAAQ,MAAM,oBAAoB,GAAG;AAAA,cACrC;AAAA,MACA,KAAK,WAAW,IAAI,EAAE;AAAA;AAAA;AAAA,OAOV,WAAU,CAAC,KAAwC;AAAA,IACjE,IAAI;AAAA,MACF,IAAI,SAAS,WAAU;AAAA,MACvB,IAAI,WAAW;AAAA,MACf,IAAI,cAAc,IAAI;AAAA,MACtB,IAAI,kBAAkB;AAAA,MACtB,IAAI,kBAAkB;AAAA,MAEtB,MAAM,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG,CAAC;AAAA,MACpD,KAAK,OAAO,KAAK,gBAAgB,IAAI,EAAE;AAAA,MACvC,OAAO,KAAK;AAAA,MACZ,QAAQ,MAAM,uBAAuB,GAAG;AAAA,cACxC;AAAA,MACA,KAAK,WAAW,IAAI,EAAE;AAAA;AAAA;AAAA,OAOV,cAAa,CAAC,KAAyB,WAAiC;AAAA,IACtF,IAAI;AAAA,MACF,IAAI,SAAS,WAAU;AAAA,MACvB,MAAM,oBAAoB,MAAM,KAAK,QAAQ,qBAAqB;AAAA,MAClE,IAAI,WAAW,qBAAqB,OAAO,YAAY;AAAA,MACvD,IAAI,WAAW;AAAA,MACf,IAAI,kBAAkB;AAAA,MACtB,IAAI,kBAAkB;AAAA,MAEtB,MAAM,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG,CAAC;AAAA,MACpD,KAAK,OAAO,KAAK,aAAa,IAAI,IAAI,IAAI,QAAQ;AAAA,MAClD,OAAO,KAAK;AAAA,MACZ,QAAQ,MAAM,0BAA0B,GAAG;AAAA;AAAA;AAAA,EAOrC,qBAAqB,CAAC,OAAiC;AAAA,IAC/D,IAAI,CAAC;AAAA,MAAO,MAAM,IAAI,iBAAiB,kDAAkD;AAAA,IAEzF,IAAI,KAAK,0BAA0B,IAAI,KAAK,GAAG;AAAA,MAC7C,OAAO,KAAK,0BAA0B,IAAI,KAAK;AAAA,IACjD;AAAA,IAEA,MAAM,kBAAkB,IAAI;AAAA,IAC5B,gBAAgB,OAAO,iBAAiB,SAAS,MAAM,KAAK,YAAY,KAAK,CAAC;AAAA,IAC9E,KAAK,0BAA0B,IAAI,OAAO,eAAe;AAAA,IACzD,OAAO;AAAA;AAAA,OAMO,YAAW,CAAC,OAA+B;AAAA,IACzD,MAAM,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,IACnC,IAAI,CAAC,KAAK;AAAA,MACR,QAAQ,MAAM,8BAA8B,KAAK;AAAA,MACjD;AAAA,IACF;AAAA,IACA,MAAM,QAAQ,IAAI,oBAAoB,aAAa;AAAA,IACnD,MAAM,KAAK,QAAQ,KAAK,KAAK;AAAA;AAAA,OAMf,OAAM,CAAC,IAAsD;AAAA,IAC3E,MAAM,MAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAAA,IACrC,IAAI,CAAC;AAAA,MAAK;AAAA,IACV,OAAO,KAAK,eAAe,GAAG;AAAA;AAAA,OAMhB,iBAAgB,CAAC,KAAwC;AAAA,IACvE,IAAI,IAAI,WAAW,WAAU,WAAW;AAAA,MACtC,MAAM,IAAI,kBAAkB,OAAO,IAAI,yBAAyB;AAAA,IAClE;AAAA,IACA,IAAI,IAAI,WAAW,WAAU,QAAQ;AAAA,MACnC,MAAM,IAAI,kBAAkB,OAAO,IAAI,eAAe;AAAA,IACxD;AAAA,IACA,IACE,IAAI,WAAW,WAAU,YACzB,KAAK,0BAA0B,IAAI,IAAI,EAAE,GAAG,OAAO,SACnD;AAAA,MACA,MAAM,IAAI,oBAAoB,OAAO,IAAI,qBAAqB;AAAA,IAChE;AAAA,IACA,IAAI,IAAI,cAAc,IAAI,aAAa,IAAI,MAAQ;AAAA,MACjD,MAAM,IAAI,kBAAkB,OAAO,IAAI,8BAA8B;AAAA,IACvE;AAAA,IACA,IAAI,IAAI,WAAW,WAAU,UAAU;AAAA,MACrC,MAAM,IAAI,iBAAiB,OAAO,IAAI,sBAAsB;AAAA,IAC9D;AAAA;AAAA,EAMQ,cAAc,CAAC,KAAwB;AAAA,IAC/C,IAAI,eAAe,UAAU;AAAA,MAC3B,OAAO;AAAA,IACT;AAAA,IACA,IAAI,eAAe,OAAO;AAAA,MACxB,OAAO,IAAI,kBAAkB,IAAI,OAAO;AAAA,IAC1C;AAAA,IACA,OAAO,IAAI,kBAAkB,OAAO,GAAG,CAAC;AAAA;AAAA,EAMhC,UAAU,CAAC,OAAsB;AAAA,IACzC,KAAK,0BAA0B,OAAO,KAAK;AAAA;AAAA,EAMnC,cAAc,CAAC,SAA8D;AAAA,IACrF,MAAM,SAAS,CAAC,SAAiD;AAAA,MAC/D,IAAI,CAAC;AAAA,QAAM,OAAO;AAAA,MAClB,MAAM,IAAI,IAAI,KAAK,IAAI;AAAA,MACvB,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO;AAAA;AAAA,IAErC,OAAO,IAAI,KAAK,SAAS;AAAA,MACvB,IAAI,QAAQ;AAAA,MACZ,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,UAAU,OAAO,QAAQ,SAAS;AAAA,MAClC,WAAW,OAAO,QAAQ,UAAU;AAAA,MACpC,YAAY,OAAO,QAAQ,WAAW;AAAA,MACtC,WAAW,OAAO,QAAQ,WAAW;AAAA,MACrC,aAAa,OAAO,QAAQ,YAAY;AAAA,MACxC,UAAU,QAAQ,YAAY;AAAA,MAC9B,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ,SAAS;AAAA,MACxB,WAAW,QAAQ,cAAc;AAAA,MACjC,aAAa,QAAQ,gBAAgB;AAAA,MACrC,YAAY,QAAQ,eAAe;AAAA,IACrC,CAAC;AAAA;AAAA,EAMO,cAAc,CAAC,KAA0D;AAAA,IACjF,MAAM,kBAAkB,CAAC,SAAiD;AAAA,MACxE,IAAI,CAAC;AAAA,QAAM,OAAO;AAAA,MAClB,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,YAAY;AAAA;AAAA,IAEzD,MAAM,MAAM,IAAI,KAAK,EAAE,YAAY;AAAA,IACnC,OAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,YAAY,IAAI;AAAA,MAChB,OAAO,IAAI,aAAa,KAAK;AAAA,MAC7B,aAAa,IAAI;AAAA,MACjB,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI,UAAU;AAAA,MACtB,OAAO,IAAI,UAAU,OAAO,OAAO,OAAO,IAAI,KAAK;AAAA,MACnD,YAAY,IAAI,aAAa;AAAA,MAC7B,cAAc,IAAI,eAAe;AAAA,MACjC,aAAa,IAAI,cAAc;AAAA,MAC/B,WAAW,gBAAgB,IAAI,QAAQ,KAAK;AAAA,MAC5C,YAAY,gBAAgB,IAAI,SAAS,KAAK;AAAA,MAC9C,aAAa,gBAAgB,IAAI,UAAU;AAAA,MAC3C,aAAa,gBAAgB,IAAI,SAAS;AAAA,MAC1C,cAAc,gBAAgB,IAAI,WAAW;AAAA,MAC7C,UAAU,IAAI,YAAY;AAAA,MAC1B,kBAAkB,IAAI,mBAAmB;AAAA,MACzC,kBAAkB,IAAI,mBAAmB;AAAA,IAC3C;AAAA;AAEJ;;;AF/cO,MAAM,eAIX;AAAA,EACgB;AAAA,EACG;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,SAAS,IAAI;AAAA,EACb,UAAqD,CAAC;AAAA,EACtD,UAA8C,IAAI;AAAA,EAE3D,UAAU;AAAA,EACV,eAAqD;AAAA,EAErD,QAAuB;AAAA,IAC/B,WAAW;AAAA,IACX,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,cAAc;AAAA,IACd,gBAAgB,IAAI;AAAA,EACtB;AAAA,EAEA,WAAW,CAAC,UAAmC,SAA+C;AAAA,IAC5F,KAAK,YAAY,QAAQ;AAAA,IACzB,KAAK,UAAU,QAAQ;AAAA,IACvB,KAAK,WAAW;AAAA,IAChB,KAAK,UAAU,QAAQ,WAAW,IAAI;AAAA,IACtC,KAAK,cAAc,QAAQ,eAAe;AAAA,IAC1C,KAAK,iBAAiB,QAAQ,kBAAkB;AAAA,IAChD,KAAK,0BAA0B,QAAQ;AAAA,IACvC,KAAK,uBAAuB,QAAQ;AAAA,IACpC,KAAK,wBAAwB,QAAQ;AAAA,IACrC,KAAK,oBAAoB,QAAQ,qBAAqB;AAAA,IAEtD,KAAK,kBAAkB;AAAA;AAAA,OAMZ,MAAK,GAAkB;AAAA,IAClC,IAAI,KAAK,SAAS;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,IAEA,KAAK,UAAU;AAAA,IACf,KAAK,OAAO,KAAK,gBAAgB,KAAK,SAAS;AAAA,IAG/C,MAAM,KAAK,UAAU;AAAA,IAGrB,MAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,MAAM,CAAC,CAAC;AAAA,IAG9D,KAAK,iBAAiB;AAAA,IAEtB,OAAO;AAAA;AAAA,OAMI,KAAI,GAAkB;AAAA,IACjC,IAAI,CAAC,KAAK,SAAS;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IAEA,KAAK,UAAU;AAAA,IAGf,IAAI,KAAK,cAAc;AAAA,MACrB,aAAa,KAAK,YAAY;AAAA,MAC9B,KAAK,eAAe;AAAA,IACtB;AAAA,IAGA,MAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,CAAC;AAAA,IAE7D,KAAK,OAAO,KAAK,eAAe,KAAK,SAAS;AAAA,IAC9C,OAAO;AAAA;AAAA,EAMF,QAAQ,GAAkB;AAAA,IAC/B,OAAO,KAAK,KAAK,MAAM;AAAA;AAAA,EAMlB,UAAU,GAAiC;AAAA,IAChD,OAAO,KAAK;AAAA;AAAA,OAMD,aAAY,CAAC,OAA8B;AAAA,IACtD,IAAI,QAAQ,GAAG;AAAA,MACb,MAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAAA,IAEA,MAAM,eAAe,KAAK,QAAQ;AAAA,IAElC,IAAI,QAAQ,cAAc;AAAA,MAExB,SAAS,IAAI,aAAc,IAAI,OAAO,KAAK;AAAA,QACzC,MAAM,SAAS,KAAK,aAAa;AAAA,QACjC,KAAK,QAAQ,KAAK,MAAM;AAAA,QACxB,IAAI,KAAK,SAAS;AAAA,UAChB,MAAM,OAAO,MAAM;AAAA,QACrB;AAAA,MACF;AAAA,IACF,EAAO,SAAI,QAAQ,cAAc;AAAA,MAE/B,MAAM,WAAW,KAAK,QAAQ,OAAO,KAAK;AAAA,MAC1C,MAAM,QAAQ,IAAI,SAAS,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,CAAC;AAAA,IAC3D;AAAA;AAAA,EAMK,SAAS,GAAY;AAAA,IAC1B,OAAO,KAAK;AAAA;AAAA,EAMP,cAAc,GAAW;AAAA,IAC9B,OAAO,KAAK,QAAQ;AAAA;AAAA,EAWf,SAAS,CAAC,QAA6C;AAAA,IAC5D,KAAK,QAAQ,IAAI,MAAM;AAAA;AAAA,EAOlB,YAAY,CAAC,QAA6C;AAAA,IAC/D,KAAK,QAAQ,OAAO,MAAM;AAAA;AAAA,EAOrB,EAAsC,CAC3C,OACA,UACM;AAAA,IACN,KAAK,OAAO,GAAG,OAAO,QAAQ;AAAA;AAAA,EAGzB,GAAuC,CAC5C,OACA,UACM;AAAA,IACN,KAAK,OAAO,IAAI,OAAO,QAAQ;AAAA;AAAA,EAUvB,iBAAiB,GAAS;AAAA,IAClC,SAAS,IAAI,EAAG,IAAI,KAAK,aAAa,KAAK;AAAA,MACzC,MAAM,SAAS,KAAK,aAAa;AAAA,MACjC,KAAK,QAAQ,KAAK,MAAM;AAAA,IAC1B;AAAA;AAAA,EAMQ,YAAY,GAA4C;AAAA,IAChE,MAAM,SAAS,IAAI,eAAwC,KAAK,UAAU;AAAA,MACxE,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,SAAS,KAAK;AAAA,MACd,gBAAgB,KAAK;AAAA,IACvB,CAAC;AAAA,IAGD,OAAO,GAAG,aAAa,CAAC,UAAU;AAAA,MAChC,KAAK,QAAQ,KAAK,KAAK,OAAO,WAAW,KAAK,MAAM,YAAY,EAAE;AAAA,MAClE,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,KAAK;AAAA,MACnD,KAAK,iBAAiB,kBAAkB,KAAK;AAAA,KAC9C;AAAA,IAED,OAAO,GAAG,gBAAgB,OAAO,OAAO,WAAW;AAAA,MACjD,KAAK,QAAQ,KAAK,KAAK,OAAO,eAAe,KAAK,MAAM,gBAAgB,EAAE;AAAA,MAC1E,KAAK,4BAA4B;AAAA,MACjC,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,OAAO,MAAM;AAAA,MAC9D,KAAK,iBAAiB,qBAAqB,OAAO,MAAM;AAAA,MAGxD,IAAI,KAAK,4BAA4B,GAAG;AAAA,QACtC,MAAM,KAAK,QAAQ,OAAO,KAAK;AAAA,MACjC;AAAA,KACD;AAAA,IAED,OAAO,GAAG,aAAa,OAAO,OAAO,OAAO,cAAc;AAAA,MACxD,KAAK,QAAQ,KAAK,KAAK,OAAO,YAAY,KAAK,MAAM,aAAa,EAAE;AAAA,MACpE,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,OAAO,KAAK;AAAA,MAC1D,KAAK,iBAAiB,kBAAkB,OAAO,OAAO,SAAS;AAAA,MAG/D,IAAI,KAAK,yBAAyB,GAAG;AAAA,QACnC,MAAM,KAAK,QAAQ,OAAO,KAAK;AAAA,MACjC;AAAA,KACD;AAAA,IAED,OAAO,GAAG,gBAAgB,OAAO,UAAU;AAAA,MACzC,KAAK,QAAQ,KAAK,KAAK,OAAO,cAAc,KAAK,MAAM,eAAe,EAAE;AAAA,MACxE,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,KAAK;AAAA,MACtD,KAAK,iBAAiB,qBAAqB,KAAK;AAAA,MAGhD,IAAI,KAAK,0BAA0B,GAAG;AAAA,QACpC,MAAM,KAAK,QAAQ,OAAO,KAAK;AAAA,MACjC;AAAA,KACD;AAAA,IAED,OAAO,GAAG,aAAa,CAAC,OAAO,aAAa;AAAA,MAC1C,KAAK,QAAQ,KAAK,KAAK,OAAO,aAAa,KAAK,MAAM,cAAc,EAAE;AAAA,MACtE,KAAK,OAAO,KAAK,aAAa,KAAK,WAAW,OAAO,QAAQ;AAAA,MAC7D,KAAK,iBAAiB,kBAAkB,OAAO,QAAQ;AAAA,KACxD;AAAA,IAED,OAAO,GAAG,gBAAgB,CAAC,OAAO,UAAU,SAAS,YAAY;AAAA,MAC/D,KAAK,OAAO,KAAK,gBAAgB,KAAK,WAAW,OAAO,UAAU,SAAS,OAAO;AAAA,MAClF,KAAK,iBAAiB,qBAAqB,OAAO,UAAU,SAAS,OAAO;AAAA,KAC7E;AAAA,IAED,OAAO;AAAA;AAAA,EAuBC,gBAAgB,CAAC,WAAmB,MAAuB;AAAA,IACnE,WAAW,UAAU,KAAK,SAAS;AAAA,MACjC,MAAM,KAAM,OAAe;AAAA,MAC3B,IAAI,OAAO,OAAO,YAAY;AAAA,QAC5B,GAAG,MAAM,QAAQ,IAAI;AAAA,MACvB;AAAA,IACF;AAAA;AAAA,EAMQ,2BAA2B,GAAS;AAAA,IAC5C,MAAM,QAAkB,CAAC;AAAA,IACzB,WAAW,UAAU,KAAK,SAAS;AAAA,MACjC,MAAM,UAAU,OAAO,yBAAyB;AAAA,MAChD,IAAI,YAAY,WAAW;AAAA,QACzB,MAAM,KAAK,OAAO;AAAA,MACpB;AAAA,IACF;AAAA,IACA,IAAI,MAAM,SAAS,GAAG;AAAA,MACpB,MAAM,MAAM,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,MAAM;AAAA,MACrD,KAAK,QAAQ;AAAA,WACR,KAAK;AAAA,QACR,uBAAuB;AAAA,QACvB,gBAAgB,IAAI;AAAA,MACtB;AAAA,IACF;AAAA;AAAA,EAMQ,gBAAgB,GAAS;AAAA,IACjC,IAAI,CAAC,KAAK;AAAA,MAAS;AAAA,IAEnB,KAAK,YAAY,EAAE,QAAQ,MAAM;AAAA,MAC/B,IAAI,KAAK,SAAS;AAAA,QAChB,KAAK,eAAe,WAAW,MAAM,KAAK,iBAAiB,GAAG,KAAK,iBAAiB;AAAA,MACtF;AAAA,KACD;AAAA;AAAA,OAMa,YAAW,GAAkB;AAAA,IAC3C,IAAI;AAAA,MAKF,IAAI,KAAK,4BAA4B,aAAa,KAAK,0BAA0B,GAAG;AAAA,QAClF,MAAM,KAAK,QAAQ,yBACjB,WAAU,WACV,KAAK,uBACP;AAAA,MACF;AAAA,MAGA,IAAI,KAAK,yBAAyB,aAAa,KAAK,uBAAuB,GAAG;AAAA,QAC5E,MAAM,KAAK,QAAQ,yBAAyB,WAAU,QAAQ,KAAK,oBAAoB;AAAA,MACzF;AAAA,MAGA,IAAI,KAAK,0BAA0B,aAAa,KAAK,wBAAwB,GAAG;AAAA,QAC9E,MAAM,KAAK,QAAQ,yBAAyB,WAAU,UAAU,KAAK,qBAAqB;AAAA,MAC5F;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,MAAM,qBAAqB,KAAK;AAAA;AAAA;AAAA,OAO5B,UAAS,GAAkB;AAAA,IACzC,IAAI;AAAA,MACF,MAAM,sBAAsB,MAAM,KAAK,QAAQ,KAAK,WAAU,UAAU;AAAA,MACxE,MAAM,oBAAoB,MAAM,KAAK,QAAQ,KAAK,WAAU,QAAQ;AAAA,MACpE,MAAM,YAAY,CAAC,GAAG,qBAAqB,GAAG,iBAAiB;AAAA,MAE/D,WAAW,WAAW,WAAW;AAAA,QAC/B,MAAM,MAAM,KAAK,eAAe,OAAO;AAAA,QACvC,IAAI,SAAS,WAAU;AAAA,QACvB,IAAI,WAAW,IAAI,aAAa,IAAI;AAAA,QACpC,IAAI,WAAW;AAAA,QACf,IAAI,kBAAkB;AAAA,QACtB,IAAI,kBAAkB;AAAA,QACtB,IAAI,QAAQ;AAAA,QAEZ,MAAM,KAAK,QAAQ,SAAS,KAAK,eAAe,GAAG,CAAC;AAAA,MACtD;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,MAAM,uBAAuB,KAAK;AAAA;AAAA;AAAA,EAOpC,cAAc,CAAC,SAA8D;AAAA,IACrF,MAAM,SAAS,CAAC,SAAiD;AAAA,MAC/D,IAAI,CAAC;AAAA,QAAM,OAAO;AAAA,MAClB,MAAM,IAAI,IAAI,KAAK,IAAI;AAAA,MACvB,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO;AAAA;AAAA,IAErC,OAAO,IAAI,KAAK,SAAS;AAAA,MACvB,IAAI,QAAQ;AAAA,MACZ,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,UAAU,OAAO,QAAQ,SAAS;AAAA,MAClC,WAAW,OAAO,QAAQ,UAAU;AAAA,MACpC,YAAY,OAAO,QAAQ,WAAW;AAAA,MACtC,WAAW,OAAO,QAAQ,WAAW;AAAA,MACrC,aAAa,OAAO,QAAQ,YAAY;AAAA,MACxC,UAAU,QAAQ,YAAY;AAAA,MAC9B,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,iBAAiB,QAAQ,oBAAoB;AAAA,MAC7C,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ,SAAS;AAAA,MACxB,WAAW,QAAQ,cAAc;AAAA,MACjC,aAAa,QAAQ,gBAAgB;AAAA,MACrC,YAAY,QAAQ,eAAe;AAAA,IACrC,CAAC;AAAA;AAAA,EAMO,cAAc,CAAC,KAA0D;AAAA,IACjF,MAAM,kBAAkB,CAAC,SAAiD;AAAA,MACxE,IAAI,CAAC;AAAA,QAAM,OAAO;AAAA,MAClB,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,YAAY;AAAA;AAAA,IAEzD,MAAM,MAAM,IAAI,KAAK,EAAE,YAAY;AAAA,IACnC,OAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,YAAY,IAAI;AAAA,MAChB,OAAO,IAAI,aAAa,KAAK;AAAA,MAC7B,aAAa,IAAI;AAAA,MACjB,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI,UAAU;AAAA,MACtB,OAAO,IAAI,UAAU,OAAO,OAAO,OAAO,IAAI,KAAK;AAAA,MACnD,YAAY,IAAI,aAAa;AAAA,MAC7B,cAAc,IAAI,eAAe;AAAA,MACjC,aAAa,IAAI,cAAc;AAAA,MAC/B,WAAW,gBAAgB,IAAI,QAAQ,KAAK;AAAA,MAC5C,YAAY,gBAAgB,IAAI,SAAS,KAAK;AAAA,MAC9C,aAAa,gBAAgB,IAAI,UAAU;AAAA,MAC3C,aAAa,gBAAgB,IAAI,SAAS;AAAA,MAC1C,cAAc,gBAAgB,IAAI,WAAW;AAAA,MAC7C,UAAU,IAAI,YAAY;AAAA,MAC1B,kBAAkB,IAAI,mBAAmB;AAAA,MACzC,kBAAkB,IAAI,mBAAmB;AAAA,IAC3C;AAAA;AAEJ;;AGjgBO,MAAM,iBAAqC;AAAA,EACxC,WAAuB,CAAC;AAAA,EAEhC,WAAW,CAAC,WAAuB,CAAC,GAAG;AAAA,IACrC,KAAK,WAAW;AAAA;AAAA,EAGlB,UAAU,CAAC,SAAyB;AAAA,IAClC,KAAK,SAAS,KAAK,OAAO;AAAA;AAAA,OAGtB,WAAU,GAAqB;AAAA,IACnC,WAAW,WAAW,KAAK,UAAU;AAAA,MACnC,IAAI,CAAE,MAAM,QAAQ,WAAW,GAAI;AAAA,QACjC,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,OAAO;AAAA;AAAA,OAGH,eAAc,GAAkB;AAAA,IACpC,KAAK,SAAS,QAAQ,CAAC,YAAY,QAAQ,eAAe,CAAC;AAAA;AAAA,OAGvD,oBAAmB,GAAkB;AAAA,IACzC,KAAK,SAAS,QAAQ,CAAC,YAAY,QAAQ,oBAAoB,CAAC;AAAA;AAAA,OAG5D,qBAAoB,GAAkB;AAAA,IAC1C,IAAI,UAAU,IAAI;AAAA,IAClB,WAAW,WAAW,KAAK,UAAU;AAAA,MACnC,MAAM,kBAAkB,MAAM,QAAQ,qBAAqB;AAAA,MAC3D,IAAI,kBAAkB,SAAS;AAAA,QAC7B,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,OAAO;AAAA;AAAA,OAGH,qBAAoB,CAAC,MAA2B;AAAA,IACpD,WAAW,WAAW,KAAK,UAAU;AAAA,MACnC,MAAM,QAAQ,qBAAqB,IAAI;AAAA,IACzC;AAAA;AAAA,OAGI,MAAK,GAAkB;AAAA,IAC3B,KAAK,SAAS,QAAQ,CAAC,YAAY,QAAQ,MAAM,CAAC;AAAA;AAEtD;;AClDA,+BAAS;AAGF,IAAM,yBAAyB,oBAA6B,6BAA6B;AAAA;AAKzF,MAAM,mBAAuC;AAAA,EAC1C,qBAA6B;AAAA,EACpB;AAAA,EACA;AAAA,EACT,uBAA6B,IAAI;AAAA,EAEzC,WAAW,CAAC,mBAA2B,0BAAkC,MAAM;AAAA,IAC7E,KAAK,oBAAoB;AAAA,IACzB,KAAK,0BAA0B;AAAA;AAAA,OAG3B,WAAU,GAAqB;AAAA,IACnC,OACE,KAAK,qBAAqB,KAAK,qBAC/B,KAAK,IAAI,KAAK,KAAK,qBAAqB,QAAQ;AAAA;AAAA,OAI9C,eAAc,GAAkB;AAAA,IACpC,IAAI,KAAK,qBAAqB,KAAK,mBAAmB;AAAA,MACpD,KAAK;AAAA,MACL,KAAK,uBAAuB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,uBAAuB;AAAA,IAChF;AAAA;AAAA,OAGI,oBAAmB,GAAkB;AAAA,IACzC,KAAK,qBAAqB,KAAK,IAAI,GAAG,KAAK,qBAAqB,CAAC;AAAA;AAAA,OAG7D,qBAAoB,GAAkB;AAAA,IAC1C,OAAO,KAAK,qBAAqB,KAAK,oBAClC,IAAI,OACJ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,uBAAuB;AAAA;AAAA,OAGlD,qBAAoB,CAAC,MAA2B;AAAA,IACpD,IAAI,OAAO,KAAK,sBAAsB;AAAA,MACpC,KAAK,uBAAuB;AAAA,IAC9B;AAAA;AAAA,OAGI,MAAK,GAAkB;AAAA,IAC3B,KAAK,qBAAqB;AAAA,IAC1B,KAAK,uBAAuB,IAAI;AAAA;AAEpC;;ACnDO,MAAM,aAAiC;AAAA,EAExB;AAAA,EADZ,oBAA0B,IAAI;AAAA,EACtC,WAAW,CAAS,sBAA8B,IAAI;AAAA,IAAlC;AAAA;AAAA,OAEd,WAAU,GAAqB;AAAA,IACnC,OAAO,KAAK,IAAI,KAAK,KAAK,kBAAkB,QAAQ;AAAA;AAAA,OAGhD,eAAc,GAAkB;AAAA,IACpC,KAAK,oBAAoB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,mBAAmB;AAAA;AAAA,OAGnE,oBAAmB,GAAkB;AAAA,OAIrC,qBAAoB,GAAkB;AAAA,IAC1C,OAAO,KAAK;AAAA;AAAA,OAGR,qBAAoB,CAAC,MAA2B;AAAA,IACpD,IAAI,OAAO,KAAK,mBAAmB;AAAA,MACjC,KAAK,oBAAoB;AAAA,IAC3B;AAAA;AAAA,OAEI,MAAK,GAAkB;AAAA,IAC3B,KAAK,oBAAoB,IAAI;AAAA;AAEjC;;AC9BA,+BAAS;AAGF,IAAM,iCAAiC,oBAC5C,oCACF;AAAA;AAOO,MAAM,wBAA4C;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACT,oBAA4B,KAAK,IAAI;AAAA,EACrC,gBAAwB;AAAA,EACxB,YAAsB,CAAC;AAAA,EAE/B,WAAW,GAAG,eAAe,uBAA2C;AAAA,IACtE,IAAI,iBAAiB,GAAG;AAAA,MACtB,MAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAAA,IACA,IAAI,uBAAuB,GAAG;AAAA,MAC5B,MAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAAA,IACA,KAAK,gBAAgB;AAAA,IACrB,KAAK,eAAe,sBAAsB;AAAA,IAE1C,KAAK,gBAAgB,KAAK,eAAe,KAAK;AAAA;AAAA,OAI1C,WAAU,GAAqB;AAAA,IACnC,MAAM,MAAM,KAAK,IAAI;AAAA,IACrB,OAAO,OAAO,KAAK;AAAA;AAAA,OAIf,eAAc,GAAkB;AAAA,IACpC,MAAM,MAAM,KAAK,IAAI;AAAA,IACrB,KAAK,gBAAgB;AAAA,IAGrB,IAAI,KAAK,UAAU,WAAW,GAAG;AAAA,MAC/B,KAAK,oBAAoB,MAAM,KAAK;AAAA,IACtC,EAAO;AAAA,MAEL,MAAM,MAAM,KAAK,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAAA,MACpD,MAAM,cAAc,MAAM,KAAK,UAAU;AAAA,MAEzC,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,gBAAgB,WAAW;AAAA,MAC3D,KAAK,oBAAoB,MAAM;AAAA;AAAA;AAAA,OAS7B,oBAAmB,GAAkB;AAAA,IACzC,MAAM,MAAM,KAAK,IAAI;AAAA,IACrB,MAAM,WAAW,MAAM,KAAK;AAAA,IAC5B,KAAK,UAAU,KAAK,QAAQ;AAAA,IAC5B,IAAI,KAAK,UAAU,SAAS,KAAK,eAAe;AAAA,MAC9C,KAAK,UAAU,MAAM;AAAA,IACvB;AAAA;AAAA,OAGI,qBAAoB,GAAkB;AAAA,IAC1C,OAAO,IAAI,KAAK,KAAK,iBAAiB;AAAA;AAAA,OAGlC,qBAAoB,CAAC,MAA2B;AAAA,IACpD,MAAM,IAAI,KAAK,QAAQ;AAAA,IACvB,IAAI,IAAI,KAAK,mBAAmB;AAAA,MAC9B,KAAK,oBAAoB;AAAA,IAC3B;AAAA;AAAA,OAGI,MAAK,GAAkB;AAAA,IAC3B,KAAK,YAAY,CAAC;AAAA,IAClB,KAAK,oBAAoB,KAAK,IAAI;AAAA,IAClC,KAAK,gBAAgB;AAAA;AAEzB;;ACvFA,+BAAS;AAEF,IAAM,cAAc,oBAA6B,kBAAkB;;ACKnE,MAAM,YAAgC;AAAA,EAStB;AAAA,EACA;AAAA,EATF;AAAA,EACT;AAAA,EACS;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEnB,WAAW,CACU,SACA;AAAA,IAEjB;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,KAEpB;AAAA,IATmB;AAAA,IACA;AAAA,IASnB,IAAI,iBAAiB,GAAG;AAAA,MACtB,MAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AAAA,IACA,IAAI,uBAAuB,GAAG;AAAA,MAC5B,MAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AAAA,IACA,IAAI,uBAAuB,GAAG;AAAA,MAC5B,MAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AAAA,IACA,IAAI,qBAAqB,GAAG;AAAA,MAC1B,MAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,IACA,IAAI,mBAAmB,qBAAqB;AAAA,MAC1C,MAAM,IAAI,MAAM,0DAA0D;AAAA,IAC5E;AAAA,IAEA,KAAK,2BAA2B,sBAAsB;AAAA,IACtD,KAAK,gBAAgB;AAAA,IACrB,KAAK,sBAAsB;AAAA,IAC3B,KAAK,oBAAoB;AAAA,IACzB,KAAK,kBAAkB;AAAA,IACvB,KAAK,sBAAsB;AAAA;AAAA,EAGnB,SAAS,CAAC,MAAsB;AAAA,IAExC,OAAO,OAAO,KAAK,OAAO,IAAI;AAAA;AAAA,EAGtB,eAAe,GAAS;AAAA,IAChC,KAAK,sBAAsB,KAAK,IAC9B,KAAK,sBAAsB,KAAK,mBAChC,KAAK,eACP;AAAA;AAAA,OAOI,WAAU,GAAqB;AAAA,IAEnC,MAAM,kBAAkB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,wBAAwB,EAAE,YAAY;AAAA,IACzF,MAAM,eAAe,MAAM,KAAK,QAAQ,kBAAkB,KAAK,WAAW,eAAe;AAAA,IACzF,MAAM,gBAAgB,eAAe,KAAK;AAAA,IAG1C,IAAI,eAAe;AAAA,MAEjB,MAAM,qBAAoB,MAAM,KAAK,QAAQ,qBAAqB,KAAK,SAAS;AAAA,MAChF,IAAI,sBAAqB,IAAI,KAAK,kBAAiB,EAAE,QAAQ,IAAI,KAAK,IAAI,GAAG;AAAA,QAE3E,MAAM,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI;AAAA,QAC3C,MAAM,KAAK,QAAQ,qBAAqB,KAAK,WAAW,SAAS,YAAY,CAAC;AAAA,MAChF;AAAA,MACA,KAAK,sBAAsB,KAAK;AAAA,MAChC,OAAO;AAAA,IACT;AAAA,IAGA,MAAM,oBAAoB,MAAM,KAAK,QAAQ,qBAAqB,KAAK,SAAS;AAAA,IAChF,IAAI,qBAAqB,IAAI,KAAK,iBAAiB,EAAE,QAAQ,IAAI,KAAK,IAAI,GAAG;AAAA,MAC3E,KAAK,gBAAgB;AAAA,MACrB,OAAO;AAAA,IACT;AAAA,IAGA,KAAK,gBAAgB;AAAA,IACrB,OAAO;AAAA;AAAA,OAMH,eAAc,GAAkB;AAAA,IACpC,MAAM,KAAK,QAAQ,gBAAgB,KAAK,SAAS;AAAA,IAEjD,MAAM,kBAAkB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,wBAAwB,EAAE,YAAY;AAAA,IACzF,MAAM,eAAe,MAAM,KAAK,QAAQ,kBAAkB,KAAK,WAAW,eAAe;AAAA,IAEzF,IAAI,gBAAgB,KAAK,eAAe;AAAA,MACtC,MAAM,iBAAiB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU,KAAK,mBAAmB,CAAC;AAAA,MACrF,MAAM,KAAK,qBAAqB,cAAc;AAAA,IAChD,EAAO;AAAA,MAEL,MAAM,oBAAoB,MAAM,KAAK,QAAQ,qBAAqB,KAAK,SAAS;AAAA,MAChF,IAAI,qBAAqB,IAAI,KAAK,iBAAiB,EAAE,QAAQ,IAAI,KAAK,IAAI,GAAG;AAAA,QAG3E,MAAM,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI;AAAA,QAC3C,MAAM,KAAK,QAAQ,qBAAqB,KAAK,WAAW,SAAS,YAAY,CAAC;AAAA,MAChF;AAAA;AAAA;AAAA,OAIE,oBAAmB,GAAkB;AAAA,OAQrC,qBAAoB,GAAkB;AAAA,IAE1C,MAAM,kBAAkB,MAAM,KAAK,QAAQ,2BACzC,KAAK,WACL,KAAK,gBAAgB,CACvB;AAAA,IAEA,IAAI,kBAAkB,IAAI;AAAA,IAC1B,IAAI,iBAAiB;AAAA,MACnB,kBAAkB,IAAI,KAAK,eAAe;AAAA,MAC1C,gBAAgB,WACd,gBAAgB,WAAW,IAAI,KAAK,2BAA2B,IACjE;AAAA,IACF;AAAA,IAGA,MAAM,mBAAmB,MAAM,KAAK,QAAQ,qBAAqB,KAAK,SAAS;AAAA,IAC/E,IAAI,oBAAoB,IAAI;AAAA,IAC5B,IAAI,kBAAkB;AAAA,MACpB,oBAAoB,IAAI,KAAK,gBAAgB;AAAA,IAC/C;AAAA,IAGA,OAAO,oBAAoB,kBAAkB,oBAAoB;AAAA;AAAA,OAO7D,qBAAoB,CAAC,MAA2B;AAAA,IACpD,MAAM,KAAK,QAAQ,qBAAqB,KAAK,WAAW,KAAK,YAAY,CAAC;AAAA;AAAA,OAMtE,MAAK,GAAkB;AAAA,IAC3B,MAAM,KAAK,QAAQ,MAAM,KAAK,SAAS;AAAA,IACvC,KAAK,sBAAsB,KAAK;AAAA;AAEpC;",
19
+ "debugId": "98D28D3ECAF6920F64756E2164756E21",
19
20
  "names": []
20
21
  }