### Minimal PlainJob Example Source: https://github.com/justplainstuff/plainjob/blob/main/README.md A basic example demonstrating queue creation, worker definition, adding a job, and starting the worker. ```typescript import { bun, defineQueue, defineWorker } from "plainjob"; import Database from "bun:sqlite"; const connection = bun(new Database("data.db", { strict: true })); const queue = defineQueue({ connection }); // Define a worker const worker = defineWorker( "print", async (job) => { console.log(`Processing job ${job.id}: ${job.data}`); }, { queue } ); // Add a job queue.add("print", "Hello, plainjob!"); // Start the worker worker.start(); ``` -------------------------------- ### Install PlainJob with Bun Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Install the plainjob package using Bun. ```bash bun add plainjob ``` -------------------------------- ### Multi-process Worker Setup (Main Process) Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Example of forking multiple worker processes to utilize multiple CPU cores for job processing. ```typescript import { fork } from "node:child_process"; import os from "node:os"; const numCPUs = os.cpus().length; const dbUrl = "queue.db"; for (let i = 0; i < numCPUs; i++) { const worker = fork("./worker.ts", [dbUrl]); worker.on("exit", (code) => { console.log(`Worker ${i} exited with code ${code}`); }); } ``` -------------------------------- ### Install PlainJob with Node.js Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Install the plainjob package and its Node.js dependency better-sqlite3 using npm. ```bash npm install plainjob better-sqlite3 ``` -------------------------------- ### Manage Jobs in the Queue Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Examples of how to query job counts, retrieve job types, and list scheduled jobs. ```typescript // Count pending jobs const pendingCount = queue.countJobs({ status: JobStatus.Pending }); // Get job types const types = queue.getJobTypes(); // Get scheduled jobs const scheduledJobs = queue.getScheduledJobs(); ``` -------------------------------- ### Multi-process Worker Setup (Worker Process) Source: https://github.com/justplainstuff/plainjob/blob/main/README.md The worker script that is forked, setting up its own database connection, queue, and worker definition. ```typescript import Database from "better-sqlite3"; import { better, defineQueue, defineWorker, processAll } from "plainjob"; const dbUrl = process.argv[2]; const connection = better(new Database(dbUrl)); const queue = defineQueue({ connection }); const worker = defineWorker( "bench", async (job) => { // Process job }, { queue } ); void worker.start().catch((error) => { console.error(error); process.exit(1); }); ``` -------------------------------- ### Define PlainJob Queue with Options Source: https://context7.com/justplainstuff/plainjob/llms.txt Initializes the SQLite schema, applies performance pragmas, and starts background maintenance tasks. Configurable with timeouts, intervals, and cleanup settings. ```typescript import Database from "better-sqlite3"; import { better, defineQueue, JobStatus } from "plainjob"; const queue = defineQueue({ connection: better(new Database("jobs.db")), // Re-queue a job if it has been in "Processing" state for longer than this (ms). // Default: 30 minutes. timeout: 5 * 60 * 1000, // How often to run the maintenance task (ms). Default: 1 minute. maintenanceInterval: 30 * 1000, // Purge completed jobs older than this (ms). Default: 7 days. removeDoneJobsOlderThan: 2 * 24 * 60 * 60 * 1000, // Purge failed jobs older than this (ms). Default: 30 days. removeFailedJobsOlderThan: 14 * 24 * 60 * 60 * 1000, // Custom serialiser — defaults to JSON.stringify. serializer: (data) => JSON.stringify(data), // Maintenance callbacks for observability. onDoneJobsRemoved: (n) => console.log(`Purged ${n} done jobs`), onFailedJobsRemoved: (n) => console.log(`Purged ${n} failed jobs`), onProcessingJobsRequeued: (n) => console.log(`Re-queued ${n} timed-out jobs`), // Winston-compatible logger. Defaults to console. logger: { error: console.error, warn: console.warn, info: console.info, debug: () => {}, }, }); // The queue is ready; always close it before the process exits. process.on("SIGTERM", () => { queue.close(); process.exit(0); }); ``` -------------------------------- ### Multi-process worker pattern setup Source: https://context7.com/justplainstuff/plainjob/llms.txt This pattern demonstrates how to leverage multiple processes for handling jobs, with each process acting as a worker. It's designed for scalability and resilience, using SQLite transactions for safe concurrent access. ```typescript // main.ts — spawn one worker per CPU core import { fork } from "node:child_process"; import os from "node:os"; const dbPath = "jobs.db"; for (let i = 0; i < os.cpus().length; i++) { const child = fork("./worker-process.ts", [dbPath]); child.on("exit", (code) => console.log(`Worker ${i} exited (code ${code})`)); } // worker-process.ts — runs in each child process import Database from "better-sqlite3"; import { better, defineQueue, defineWorker } from "plainjob"; const dbPath = process.argv[2]!; const queue = defineQueue({ connection: better(new Database(dbPath)) }); const worker = defineWorker("heavy-task", async (job) => { const { payload } = JSON.parse(job.data); await doWork(payload); }, { queue }); void worker.start().catch((err) => { console.error(err); process.exit(1); }); ``` -------------------------------- ### defineQueue(opts) - Queue Definition Source: https://context7.com/justplainstuff/plainjob/llms.txt Initialises the SQLite schema, applies performance pragmas, and starts a background maintenance interval. Returns a Queue object configured with the provided options. ```APIDOC ## defineQueue(opts) - Create a queue Initialises the SQLite schema (`plainjob_jobs` and `plainjob_scheduled_jobs` tables with indexes), applies performance pragmas, and starts a background maintenance interval that re-queues timed-out jobs and removes stale records. Returns a `Queue` object. ```typescript import Database from "better-sqlite3"; import { better, defineQueue, JobStatus } from "plainjob"; const queue = defineQueue({ connection: better(new Database("jobs.db")), // Re-queue a job if it has been in "Processing" state for longer than this (ms). // Default: 30 minutes. timeout: 5 * 60 * 1000, // How often to run the maintenance task (ms). Default: 1 minute. maintenanceInterval: 30 * 1000, // Purge completed jobs older than this (ms). Default: 7 days. removeDoneJobsOlderThan: 2 * 24 * 60 * 60 * 1000, // Purge failed jobs older than this (ms). Default: 30 days. removeFailedJobsOlderThan: 14 * 24 * 60 * 60 * 1000, // Custom serialiser — defaults to JSON.stringify. serializer: (data) => JSON.stringify(data), // Maintenance callbacks for observability. onDoneJobsRemoved: (n) => console.log(`Purged ${n} done jobs`), onFailedJobsRemoved: (n) => console.log(`Purged ${n} failed jobs`), onProcessingJobsRequeued: (n) => console.log(`Re-queued ${n} timed-out jobs`), // Winston-compatible logger. Defaults to console. logger: { error: console.error, warn: console.warn, info: console.info, debug: () => {}, }, }); // The queue is ready; always close it before the process exits. process.on("SIGTERM", () => { queue.close(); process.exit(0); }); ``` ``` -------------------------------- ### worker.start() Source: https://context7.com/justplainstuff/plainjob/llms.txt Starts the worker's polling loop, which alternately checks for due cron triggers and regular jobs. The worker sleeps between checks if no jobs are found. ```APIDOC ## `worker.start()` — Begin processing Starts the polling loop. The worker alternately checks for due cron triggers (via `getAndMarkScheduledJobAsProcessing`) and regular jobs (via `getAndMarkJobAsProcessing`). When neither returns a job the worker sleeps for `pollIntervall` ms. Returns a `Promise` that resolves once `stop()` is called. ```typescript // Fire and forget — worker runs until stopped void worker.start().catch((err) => { console.error("Worker crashed:", err); process.exit(1); }); ``` ``` -------------------------------- ### processAll(queue, worker, opts?) Source: https://context7.com/justplainstuff/plainjob/llms.txt Drains a queue by starting the worker, waiting for all Pending and Processing jobs to reach a terminal state, and then stopping the worker. It throws a timeout error if jobs are not processed within the specified timeout. ```APIDOC ## `processAll(queue, worker, opts?)` — Drain a queue in tests or scripts (exported from `src/worker`) Starts the worker, waits until all `Pending` and `Processing` jobs reach a terminal state (`Done` or `Failed`), then stops the worker. Throws a timeout error if the jobs are not all processed within `opts.timeout` ms (default: 1000 ms). ```typescript import { processAll } from "plainjob/src/worker"; // or direct import in tests const queue = defineQueue({ connection }); const worker = defineWorker("resize", async (job) => { const { imageId } = JSON.parse(job.data); await resizeImage(imageId); }, { queue }); queue.add("resize", { imageId: 101 }); queue.add("resize", { imageId: 102 }); queue.add("resize", { imageId: 103 }); // Process all three jobs then stop automatically await processAll(queue, worker, { timeout: 5000 }); console.log(queue.countJobs({ status: JobStatus.Done })); // => 3 queue.close(); ``` ``` -------------------------------- ### Count jobs with `queue.countJobs()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Use `queue.countJobs` to get the number of jobs matching specific criteria. Omitting options counts all jobs. Supports filtering by job type and status. ```typescript import { JobStatus } from "plainjob"; // Count everything console.log(queue.countJobs()); // => 12 // Count by status only console.log(queue.countJobs({ status: JobStatus.Pending })); // => 5 console.log(queue.countJobs({ status: JobStatus.Processing })); // => 1 console.log(queue.countJobs({ status: JobStatus.Done })); // => 4 console.log(queue.countJobs({ status: JobStatus.Failed })); // => 2 // Count by type only console.log(queue.countJobs({ type: "send-email" })); // => 7 // Count by both type and status console.log(queue.countJobs({ type: "send-email", status: JobStatus.Pending })); // => 3 ``` -------------------------------- ### Process all jobs in a queue Source: https://context7.com/justplainstuff/plainjob/llms.txt The `processAll` function is useful for tests or scripts to drain a queue. It starts the worker, waits for all jobs to reach a terminal state, and then stops the worker. A timeout can be specified to prevent indefinite waiting. ```typescript import { processAll } from "plainjob/src/worker"; // or direct import in tests const queue = defineQueue({ connection }); const worker = defineWorker("resize", async (job) => { const { imageId } = JSON.parse(job.data); await resizeImage(imageId); }, { queue }); queue.add("resize", { imageId: 101 }); queue.add("resize", { imageId: 102 }); queue.add("resize", { imageId: 103 }); // Process all three jobs then stop automatically await processAll(queue, worker, { timeout: 5000 }); console.log(queue.countJobs({ status: JobStatus.Done })); // => 3 queue.close(); ``` -------------------------------- ### Define and Start Worker Source: https://context7.com/justplainstuff/plainjob/llms.txt Creates a worker that polls the queue for jobs of a specific type. The processor handles job execution, and lifecycle callbacks allow for custom logic during processing, completion, and failure. Ensure proper error handling within the processor. ```typescript import { defineWorker } from "plainjob"; const worker = defineWorker( "send-email", // job type to process async (job) => { // processor — receives { id, type, data } const { to, subject, body } = JSON.parse(job.data); await sendEmail(to, subject, body); // throws on SMTP error }, { queue, // Poll every 500 ms instead of the default 1000 ms. pollIntervall: 500, // Called just before the processor runs. onProcessing: (job) => console.log(`[${job.id}] starting`), // Called after markJobAsDone. onCompleted: (job) => console.log(`[${job.id}] done`), // Called after markJobAsFailed — error includes the full stack trace. onFailed: (job, error) => console.error(`[${job.id}] failed: ${error}`), logger: { error: console.error, warn: console.warn, info: console.info, debug: () => {}, }, } ); worker.start(); // returns a Promise that resolves when stopped // Graceful shutdown process.on("SIGTERM", async () => { await worker.stop(); // waits for the current job to finish queue.close(); process.exit(0); }); ``` -------------------------------- ### Fetch a single cron schedule by ID with `queue.getScheduledJobById()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Get a specific scheduled job record using its ID. Returns `undefined` if no schedule with the given ID exists. Provides access to the schedule's configuration like cron expression and status. ```typescript const { id } = queue.schedule("nightly", { cron: "0 3 * * *" }); const job = queue.getScheduledJobById(id); console.log(job?.cronExpression); // => "0 3 * * *" console.log(job?.status); // => 0 (ScheduledJobStatus.Idle) ``` -------------------------------- ### Start Worker Polling Source: https://context7.com/justplainstuff/plainjob/llms.txt Initiates the worker's polling loop to check for and process jobs. The worker alternates between checking for scheduled triggers and regular jobs. It sleeps for `pollIntervall` ms when no jobs are available. The returned Promise resolves when `stop()` is called. ```typescript // Fire and forget — worker runs until stopped void worker.start().catch((err) => { console.error("Worker crashed:", err); process.exit(1); }); ``` -------------------------------- ### Create Queue with Bun:sqlite Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Initialize a PlainJob queue using Bun:sqlite. Ensure strict mode is enabled for the database connection. ```typescript import { bun, defineQueue } from "plainjob"; import Database from "bun:sqlite"; const connection = bun(new Database("data.db", { strict: true })); const queue = defineQueue({ connection }); ``` -------------------------------- ### Create Queue with better-sqlite3 Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Initialize a PlainJob queue using the better-sqlite3 driver. ```typescript import { better, defineQueue } from "plainjob"; import Database from "better-sqlite3"; const connection = better(new Database("data.db")); const queue = defineQueue({ connection }); ``` -------------------------------- ### Create Queue with Custom Options Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Initialize a PlainJob queue with custom configurations for job timeouts and cleanup. ```typescript import { bun, defineQueue } from "plainjob"; import Database from "bun:sqlite"; const connection = bun(new Database("data.db", { strict: true })); const queue = defineQueue({ connection, timeout: 30 * 60 * 1000, // 30 minutes removeDoneJobsOlderThan: 7 * 24 * 60 * 60 * 1000, // 7 days removeFailedJobsOlderThan: 30 * 24 * 60 * 60 * 1000, // 30 days }); ``` -------------------------------- ### Create Node.js Connection with better-sqlite3 Source: https://context7.com/justplainstuff/plainjob/llms.txt Wraps a `better-sqlite3` Database instance for use with `defineQueue`. Automatically applies necessary pragmas like WAL mode. ```typescript import Database from "better-sqlite3"; import { better, defineQueue } from "plainjob"; const connection = better(new Database("jobs.db")); // connection.driver === "better-sqlite3" const queue = defineQueue({ connection }); ``` -------------------------------- ### bun(database) - Bun Connection Adapter Source: https://context7.com/justplainstuff/plainjob/llms.txt Wraps a bun:sqlite Database instance into the unified Connection interface. Strict mode must be enabled on the Bun database instance before passing it to this function. ```APIDOC ## bun(database) - Create a Bun connection Wraps a `bun:sqlite` `Database` instance in the same `Connection` interface. **Strict mode must be enabled** on the Bun database. ```typescript import Database from "bun:sqlite"; import { bun, defineQueue } from "plainjob"; const connection = bun(new Database("jobs.db", { strict: true })); // connection.driver === "bun:sqlite" const queue = defineQueue({ connection }); ``` ``` -------------------------------- ### Create Bun Connection with bun:sqlite Source: https://context7.com/justplainstuff/plainjob/llms.txt Wraps a `bun:sqlite` Database instance, ensuring strict mode is enabled. Provides a unified `Connection` interface for `defineQueue`. ```typescript import Database from "bun:sqlite"; import { bun, defineQueue } from "plainjob"; const connection = bun(new Database("jobs.db", { strict: true })); // connection.driver === "bun:sqlite" const queue = defineQueue({ connection }); ``` -------------------------------- ### better(database) - Node.js Connection Adapter Source: https://context7.com/justplainstuff/plainjob/llms.txt Wraps a better-sqlite3 Database instance into the unified Connection interface for use with defineQueue. It automatically applies necessary pragmas for optimal performance. ```APIDOC ## better(database) - Create a Node.js connection Wraps an existing `better-sqlite3` `Database` instance in the unified `Connection` interface expected by `defineQueue`. WAL mode, synchronous, and busy-timeout pragmas are applied automatically when the queue is created. ```typescript import Database from "better-sqlite3"; import { better, defineQueue } from "plainjob"; const connection = better(new Database("jobs.db")); // connection.driver === "better-sqlite3" const queue = defineQueue({ connection }); ``` ``` -------------------------------- ### Add Jobs to the Queue Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Demonstrates adding different types of jobs: a one-time job, a delayed job, and a scheduled recurring job. ```typescript // Enqueue a one-time job queue.add("send-email", { to: "user@example.com", subject: "Hello" }); // Run a job a after 1 second queue.add( "send-email", { to: "user@example.com", subject: "Hello" }, { delay: 1000 } ); // Schedule a recurring job queue.schedule("daily-report", { cron: "0 0 * * *" }); ``` -------------------------------- ### List all cron schedules with `queue.getScheduledJobs()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Retrieve all scheduled job entries from the `plainjob_scheduled_jobs` table, ordered by their creation time. Each entry includes details like type, cron expression, and next run time. ```typescript queue.schedule("job1", { cron: "* * * * *" }); queue.schedule("job2", { cron: "0 0 * * *" }); const scheduled = queue.getScheduledJobs(); console.log(scheduled[0]); // { // id: 1, // type: "job1", // status: 0, // ScheduledJobStatus.Idle // cronExpression: "* * * * *", // nextRunAt: 0, // 0 = fire immediately on first worker tick // createdAt: 1720000000000 // } ``` -------------------------------- ### queue.add(type, data, options?) - Enqueue a Job Source: https://context7.com/justplainstuff/plainjob/llms.txt Inserts a single job into the database with the 'Pending' status. Supports an optional `delay` to defer execution. Returns an object containing the job ID. ```APIDOC ## queue.add(type, data, options?) - Enqueue a one-time job Inserts one job into the database in the `Pending` state. Returns `{ id: number }`. An optional `delay` (milliseconds) defers the job so it will not be picked up until the delay has elapsed. ```typescript // Immediate job const { id } = queue.add("send-email", { to: "alice@example.com", subject: "Welcome!", body: "Thanks for signing up.", }); console.log("Queued job", id); // => Queued job 1 // Delayed job — executes after 10 seconds queue.add( "send-email", { to: "bob@example.com", subject: "Reminder" }, { delay: 10_000 } ); // Retrieve and inspect before processing const job = queue.getJobById(id); console.log(job?.status); // 0 (JobStatus.Pending) console.log(JSON.parse(job!.data)); // => { to: 'alice@example.com', subject: 'Welcome!', body: 'Thanks for signing up.' } ``` ``` -------------------------------- ### Graceful Shutdown with PlainJob Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Implement a graceful shutdown process by stopping the worker and closing the queue connection. ```typescript import { processAll } from "plainjob"; process.on("SIGTERM", async () => { console.log("Shutting down..."); await worker.stop(); // <-- finishes processing jobs queue.close(); process.exit(0); }); ``` -------------------------------- ### List all distinct job types with `queue.getJobTypes()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Obtain a list of all unique job type names currently stored in the system. This is useful for understanding the range of jobs being processed. ```typescript queue.add("send-email", {}); queue.add("send-sms", {}); queue.add("send-email", {}); queue.add("resize-image", {}); const types = queue.getJobTypes(); console.log(types); // => ["send-email", "send-sms", "resize-image"] ``` -------------------------------- ### queue.countJobs(opts?) Source: https://context7.com/justplainstuff/plainjob/llms.txt Counts the number of jobs that match the provided filters. If no options are provided, it counts all jobs in the table. ```APIDOC ## `queue.countJobs(opts?)` — Count jobs by type and/or status Returns the number of jobs matching the provided filters. All parameters are optional; omitting them counts every job in the table. ### Parameters #### Query Parameters - **opts** (object) - Optional - An object containing filter criteria. - **type** (string) - Optional - Filter jobs by their type. - **status** (JobStatus) - Optional - Filter jobs by their status (e.g., Pending, Processing, Done, Failed). ### Request Example ```typescript import { JobStatus } from "plainjob"; // Count everything console.log(queue.countJobs()); // => 12 // Count by status only console.log(queue.countJobs({ status: JobStatus.Pending })); // => 5 console.log(queue.countJobs({ status: JobStatus.Processing })); // => 1 console.log(queue.countJobs({ status: JobStatus.Done })); // => 4 console.log(queue.countJobs({ status: JobStatus.Failed })); // => 2 // Count by type only console.log(queue.countJobs({ type: "send-email" })); // => 7 // Count by both type and status console.log(queue.countJobs({ type: "send-email", status: JobStatus.Pending })); // => 3 ``` ``` -------------------------------- ### queue.markJobAsDone(id) / queue.markJobAsFailed(id, error) Source: https://context7.com/justplainstuff/plainjob/llms.txt Directly transitions a job to Done or Failed status. markJobAsFailed also records the error string and a failedAt timestamp. ```APIDOC ## `queue.markJobAsDone(id)` / `queue.markJobAsFailed(id, error)` — Update job status (advanced) Directly transition a job to `Done` or `Failed`. `markJobAsFailed` also records the error string and a `failedAt` timestamp. ```typescript queue.markJobAsDone(42); queue.markJobAsFailed(43, "SMTP connection refused"); const failed = queue.getJobById(43); console.log(failed?.status); // => 3 (JobStatus.Failed) console.log(failed?.error); // => "SMTP connection refused" console.log(failed?.failedAt); // Unix ms timestamp ``` ``` -------------------------------- ### queue.getScheduledJobs() Source: https://context7.com/justplainstuff/plainjob/llms.txt Lists all scheduled jobs, ordered by their creation time. Returns all rows from the scheduled jobs table. ```APIDOC ## `queue.getScheduledJobs()` — List all cron schedules Returns all rows from `plainjob_scheduled_jobs`, ordered by `created_at`. ### Response #### Success Response (200) - **scheduledJobs** (array) - An array of scheduled job objects. - **id** (number) - The unique identifier of the scheduled job. - **type** (string) - The type of the job to be scheduled. - **status** (number) - The status of the scheduled job (e.g., 0 for Idle). - **cronExpression** (string) - The cron expression defining the schedule. - **nextRunAt** (number) - Unix ms timestamp for the next run, 0 means fire immediately on first worker tick. - **createdAt** (number) - Unix ms timestamp when the schedule was created. ### Request Example ```typescript queue.schedule("job1", { cron: "* * * * *" }); queue.schedule("job2", { cron: "0 0 * * *" }); const scheduled = queue.getScheduledJobs(); console.log(scheduled[0]); // { // id: 1, // type: "job1", // status: 0, // ScheduledJobStatus.Idle // cronExpression: "* * * * *", // nextRunAt: 0, // 0 = fire immediately on first worker tick // createdAt: 1720000000000 // } ``` ``` -------------------------------- ### Schedule a recurring job with `queue.schedule()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Use `queue.schedule` to create or update recurring jobs based on cron expressions. It handles upserts automatically, ensuring only one schedule exists per job type. Throws an error for invalid cron syntax. ```typescript import { defineQueue } from "plainjob"; // Every day at midnight queue.schedule("daily-cleanup", { cron: "0 0 * * *" }); // Every Monday–Friday at noon queue.schedule("weekday-report", { cron: "0 12 * * MON-FRI" }); // Every minute (useful for testing) queue.schedule("heartbeat", { cron: "* * * * *" }); // Update the cron expression — returns the same id const { id: first } = queue.schedule("daily-cleanup", { cron: "0 0 * * *" }); const { id: second } = queue.schedule("daily-cleanup", { cron: "0 2 * * *" }); console.log(first === second); // => true // Invalid expression throws try { queue.schedule("bad", { cron: "not a cron" }); } catch (e) { console.error((e as Error).message); // => "invalid cron expression provided: not a cron ..." } ``` -------------------------------- ### queue.close() Source: https://context7.com/justplainstuff/plainjob/llms.txt Tears down the queue by clearing the maintenance interval and closing the underlying SQLite connection. This should always be called before process exit. ```APIDOC ## `queue.close()` — Tear down the queue Clears the maintenance interval and closes the underlying SQLite connection. Always call this before process exit. ```typescript process.on("SIGTERM", async () => { await worker.stop(); queue.close(); process.exit(0); }); ``` ``` -------------------------------- ### queue.getJobTypes() Source: https://context7.com/justplainstuff/plainjob/llms.txt Retrieves a list of all distinct job type names currently present in the system. ```APIDOC ## `queue.getJobTypes()` — List all distinct job type names Returns every unique `type` string currently in the `plainjob_jobs` table. ### Response #### Success Response (200) - **types** (array) - An array of unique job type names. ### Request Example ```typescript queue.add("send-email", {}); queue.add("send-sms", {}); queue.add("send-email", {}); queue.add("resize-image", {}); const types = queue.getJobTypes(); console.log(types); // => ["send-email", "send-sms", "resize-image"] ``` ``` -------------------------------- ### Enqueue a One-Time Job Source: https://context7.com/justplainstuff/plainjob/llms.txt Adds a single job to the queue. Supports immediate execution or delayed execution using the `delay` option. Allows retrieval and inspection of job status and data. ```typescript // Immediate job const { id } = queue.add("send-email", { to: "alice@example.com", subject: "Welcome!", body: "Thanks for signing up.", }); console.log("Queued job", id); // => Queued job 1 // Delayed job — executes after 10 seconds queue.add( "send-email", { to: "bob@example.com", subject: "Reminder" }, { delay: 10_000 } ); // Retrieve and inspect before processing const job = queue.getJobById(id); console.log(job?.status); // 0 (JobStatus.Pending) console.log(JSON.parse(job!.data)); // => { to: 'alice@example.com', subject: 'Welcome!', body: 'Thanks for signing up.' } ``` -------------------------------- ### queue.addMany(type, dataArray, options?) - Bulk Enqueue Jobs Source: https://context7.com/justplainstuff/plainjob/llms.txt Inserts multiple jobs of the same type into the database within a single SQLite transaction for efficiency. Returns an object containing an array of job IDs. ```APIDOC ## queue.addMany(type, dataArray, options?) - Bulk enqueue jobs Inserts multiple jobs of the same type in a single SQLite transaction. Returns `{ ids: number[] }`. ```typescript const payloads = [ { userId: 1, report: "monthly" }, { userId: 2, report: "monthly" }, { userId: 3, report: "monthly" }, ]; const { ids } = queue.addMany("generate-report", payloads); console.log(ids); // => [2, 3, 4] // Verify all were enqueued console.log(queue.countJobs({ type: "generate-report", status: JobStatus.Pending })); // => 3 ``` ``` -------------------------------- ### Bulk Enqueue Jobs Source: https://context7.com/justplainstuff/plainjob/llms.txt Efficiently inserts multiple jobs of the same type within a single SQLite transaction. Returns an array of the newly created job IDs. ```typescript const payloads = [ { userId: 1, report: "monthly" }, { userId: 2, report: "monthly" }, { userId: 3, report: "monthly" }, ]; const { ids } = queue.addMany("generate-report", payloads); console.log(ids); // => [2, 3, 4] // Verify all were enqueued console.log(queue.countJobs({ type: "generate-report", status: JobStatus.Pending })); // => 3 ``` -------------------------------- ### Fetch a job by ID with `queue.getJobById()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Retrieve a specific job's full details using its unique ID. Returns `undefined` if the job does not exist. The returned object contains job properties like type, data, status, and timestamps. ```typescript const job = queue.getJobById(1); if (job) { console.log(job.id); // => 1 console.log(job.type); // => "send-email" console.log(JSON.parse(job.data)); // => { to: "alice@example.com", ... } console.log(job.status); // => 0 (JobStatus.Pending) console.log(job.createdAt); // Unix ms timestamp console.log(job.nextRunAt); // Unix ms — when the job becomes eligible console.log(job.failedAt); // undefined if not failed console.log(job.error); // undefined if not failed } ``` -------------------------------- ### queue.schedule(type, { cron }) Source: https://context7.com/justplainstuff/plainjob/llms.txt Registers or updates a recurring job. The worker will enqueue a job of the specified type each time the cron expression fires. Calling schedule with the same type and a different expression updates the existing schedule. ```APIDOC ## `queue.schedule(type, { cron })` — Register a recurring job Upserts a scheduled job record. The worker will enqueue a regular job of the given type each time the cron expression fires. Calling `schedule` again with the same type but a different expression updates the expression without creating a duplicate. Throws if the cron expression is invalid. ### Parameters #### Path Parameters - **type** (string) - Required - The type of the job to schedule. - **cron** (string) - Required - The cron expression defining the schedule. ### Request Example ```typescript import { defineQueue } from "plainjob"; // Every day at midnight queue.schedule("daily-cleanup", { cron: "0 0 * * *" }); // Every Monday–Friday at noon queue.schedule("weekday-report", { cron: "0 12 * * MON-FRI" }); // Every minute (useful for testing) queue.schedule("heartbeat", { cron: "* * * * *" }); // Update the cron expression — returns the same id const { id: first } = queue.schedule("daily-cleanup", { cron: "0 0 * * *" }); const { id: second } = queue.schedule("daily-cleanup", { cron: "0 2 * * *" }); console.log(first === second); // => true // Invalid expression throws try { queue.schedule("bad", { cron: "not a cron" }); } catch (e) { console.error((e as Error).message); // => "invalid cron expression provided: not a cron ..." } ``` ``` -------------------------------- ### Define a Worker with Callbacks Source: https://github.com/justplainstuff/plainjob/blob/main/README.md Define a worker for the 'send-email' job type, including handlers for job completion and failure. ```typescript import { defineWorker } from "plainjob"; const worker = defineWorker( "send-email", async (job) => { const { to, subject } = JSON.parse(job.data); await sendEmail(to, subject); }, { queue, onCompleted: (job) => console.log(`Job ${job.id} completed`), onFailed: (job, error) => console.error(`Job ${job.id} failed: ${error}`), } ); worker.start(); ``` -------------------------------- ### Claim and Process Scheduled Job Source: https://context7.com/justplainstuff/plainjob/llms.txt Atomically claims the next due scheduled job and marks it as processing. This is used internally by the worker to trigger scheduled tasks. After claiming, the actual work job is enqueued, and the schedule is reset. ```typescript const triggered = queue.getAndMarkScheduledJobAsProcessing(); if (triggered) { console.log(triggered.type); // => "send-email" console.log(triggered.cronExpression); // => "0 0 * * *" // Enqueue the actual work job and reset the schedule queue.add(triggered.type, {}); const nextRunAt = /* compute next date from cron */ Date.now() + 60_000; queue.markScheduledJobAsIdle(triggered.id, nextRunAt); } ``` -------------------------------- ### Define Queue with Custom Serializer Source: https://context7.com/justplainstuff/plainjob/llms.txt Configure a PlainJob queue to use a custom serializer, such as SuperJSON, for stringifying job data. Ensure the corresponding deserializer is used in the worker. ```typescript import { defineQueue } from "plainjob"; import superjson from "superjson"; const queue = defineQueue({ connection, serializer: (data) => superjson.stringify(data), }); queue.add("process-dates", { at: new Date("2024-12-31") }); const worker = defineWorker("process-dates", async (job) => { const { at } = superjson.parse<{ at: Date }>(job.data); console.log(at instanceof Date); // => true console.log(at.toISOString()); // => "2024-12-31T00:00:00.000Z" }, { queue }); ``` -------------------------------- ### JobStatus Enum Source: https://context7.com/justplainstuff/plainjob/llms.txt Represents the lifecycle states of a job within the PlainJob system. ```APIDOC ## `JobStatus` enum — Job lifecycle states | Value | Integer | Meaning | |---|---|---| | `Pending` | `0` | Waiting to be picked up | | `Processing` | `1` | Currently being executed by a worker | | `Done` | `2` | Completed successfully | | `Failed` | `3` | Processor threw an error | ```typescript import { JobStatus } from "plainjob"; const pending = queue.countJobs({ status: JobStatus.Pending }); const active = queue.countJobs({ status: JobStatus.Processing }); const completed = queue.countJobs({ status: JobStatus.Done }); const errors = queue.countJobs({ status: JobStatus.Failed }); console.log({ pending, active, completed, errors }); // => { pending: 5, active: 1, completed: 40, errors: 2 } ``` ``` -------------------------------- ### queue.getAndMarkScheduledJobAsProcessing() Source: https://context7.com/justplainstuff/plainjob/llms.txt Atomically selects the next Idle scheduled job whose next_run_at is less than or equal to the current time and marks it as Processing. Used internally by the worker. ```APIDOC ## `queue.getAndMarkScheduledJobAsProcessing()` — Claim the next due cron trigger (advanced) Atomically selects the next `Idle` scheduled job whose `next_run_at <= Date.now()` and marks it `Processing`. Used internally by the worker. ```typescript const triggered = queue.getAndMarkScheduledJobAsProcessing(); if (triggered) { console.log(triggered.type); // => "send-email" console.log(triggered.cronExpression); // => "0 0 * * *" // Enqueue the actual work job and reset the schedule queue.add(triggered.type, {}); const nextRunAt = /* compute next date from cron */ Date.now() + 60_000; queue.markScheduledJobAsIdle(triggered.id, nextRunAt); } ``` ``` -------------------------------- ### queue.getJobById(id) Source: https://context7.com/justplainstuff/plainjob/llms.txt Fetches a persisted job record by its unique identifier. Returns the full job details or undefined if the job does not exist. ```APIDOC ## `queue.getJobById(id)` — Fetch a persisted job Returns the full `PersistedJob` record for the given id, or `undefined` if it does not exist. ### Parameters #### Path Parameters - **id** (number) - Required - The unique identifier of the job. ### Response #### Success Response (200) - **PersistedJob** (object) - The job record if found. - **id** (number) - The job's unique identifier. - **type** (string) - The type of the job. - **data** (string) - The job's data, stored as a JSON string. - **status** (number) - The current status of the job (e.g., 0 for Pending). - **createdAt** (number) - Unix ms timestamp when the job was created. - **nextRunAt** (number) - Unix ms timestamp indicating when the job becomes eligible to run. - **failedAt** (number | undefined) - Unix ms timestamp if the job failed. - **error** (string | undefined) - Error message if the job failed. ### Request Example ```typescript const job = queue.getJobById(1); if (job) { console.log(job.id); // => 1 console.log(job.type); // => "send-email" console.log(JSON.parse(job.data)); // => { to: "alice@example.com", ... } console.log(job.status); // => 0 (JobStatus.Pending) console.log(job.createdAt); // Unix ms timestamp console.log(job.nextRunAt); // Unix ms — when the job becomes eligible console.log(job.failedAt); // undefined if not failed console.log(job.error); // undefined if not failed } ``` ``` -------------------------------- ### defineWorker(jobType, processor, options) Source: https://context7.com/justplainstuff/plainjob/llms.txt Creates a Worker that polls the queue for scheduled triggers and regular jobs of a specified jobType. The processor runs one job at a time, handling unhandled errors by marking the job as Failed and continuing. ```APIDOC ## `defineWorker(jobType, processor, options)` — Create a worker Returns a `Worker` that polls the queue for scheduled triggers and regular jobs of `jobType`. The processor runs one job at a time; unhandled errors are caught, the job is marked `Failed`, and the worker continues. Lifecycle callbacks let you hook into processing, completion, and failure events. ```typescript import { defineWorker } from "plainjob"; const worker = defineWorker( "send-email", // job type to process async (job) => { // processor — receives { id, type, data } const { to, subject, body } = JSON.parse(job.data); await sendEmail(to, subject, body); }, { queue, // Poll every 500 ms instead of the default 1000 ms. pollIntervall: 500, // Called just before the processor runs. onProcessing: (job) => console.log(`[${job.id}] starting`), // Called after markJobAsDone. onCompleted: (job) => console.log(`[${job.id}] done`), // Called after markJobAsFailed — error includes the full stack trace. onFailed: (job, error) => console.error(`[${job.id}] failed: ${error}`), logger: { error: console.error, warn: console.warn, info: console.info, debug: () => {}, }, } ); worker.start(); // returns a Promise that resolves when stopped // Graceful shutdown process.on("SIGTERM", async () => { await worker.stop(); queue.close(); process.exit(0); }); ``` ``` -------------------------------- ### Update Job Status Source: https://context7.com/justplainstuff/plainjob/llms.txt Directly transitions a job to 'Done' or 'Failed'. `markJobAsFailed` also records the error message and a failure timestamp. Use these for manual status updates or after processing attempts. ```typescript queue.markJobAsDone(42); queue.markJobAsFailed(43, "SMTP connection refused"); const failed = queue.getJobById(43); console.log(failed?.status); // => 3 (JobStatus.Failed) console.log(failed?.error); // => "SMTP connection refused" console.log(failed?.failedAt); // Unix ms timestamp ``` -------------------------------- ### Re-queue timed-out jobs with `queue.requeueTimedOutJobs()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Manually reset jobs stuck in the 'Processing' state back to 'Pending'. This function is also called automatically by the maintenance interval. Specify a timeout in milliseconds to define 'stuck'. ```typescript // Re-queue any job stuck in Processing for more than 5 minutes queue.requeueTimedOutJobs(5 * 60 * 1000); ``` -------------------------------- ### Count jobs by status Source: https://context7.com/justplainstuff/plainjob/llms.txt Use the `JobStatus` enum to filter and count jobs based on their lifecycle state. This is helpful for monitoring queue health and job completion. ```typescript import { JobStatus } from "plainjob"; const pending = queue.countJobs({ status: JobStatus.Pending }); const active = queue.countJobs({ status: JobStatus.Processing }); const completed = queue.countJobs({ status: JobStatus.Done }); const errors = queue.countJobs({ status: JobStatus.Failed }); console.log({ pending, active, completed, errors }); // => { pending: 5, active: 1, completed: 40, errors: 2 } ``` -------------------------------- ### Tear Down Queue Source: https://context7.com/justplainstuff/plainjob/llms.txt Clears the maintenance interval and closes the underlying SQLite connection. This should always be called before the process exits to ensure resources are released properly. ```typescript process.on("SIGTERM", async () => { await worker.stop(); queue.close(); process.exit(0); }); ``` -------------------------------- ### Claim and Process Job Source: https://context7.com/justplainstuff/plainjob/llms.txt Atomically claims the oldest pending job of a specific type and marks it as processing. Use this for custom worker implementations. Ensure to handle potential errors during processing and update the job status accordingly. ```typescript const claimed = queue.getAndMarkJobAsProcessing("send-email"); if (claimed) { const job = queue.getJobById(claimed.id)!; try { await sendEmail(JSON.parse(job.data)); queue.markJobAsDone(job.id); } catch (err) { queue.markJobAsFailed(job.id, (err as Error).message); } } ``` -------------------------------- ### queue.markScheduledJobAsIdle(id, nextRunAt) Source: https://context7.com/justplainstuff/plainjob/llms.txt Resets a scheduled job to Idle status and updates its next_run_at timestamp. This is called by the worker after it enqueues the corresponding regular job. ```APIDOC ## `queue.markScheduledJobAsIdle(id, nextRunAt)` — Reset a cron trigger (advanced) Sets a scheduled job back to `Idle` and updates its `next_run_at`. Called by the worker after it enqueues the corresponding regular job. ```typescript import cronParser from "cron-parser"; const { id } = queue.schedule("hourly", { cron: "0 * * * *" }); // After processing, compute and set the next run time const nextRunAt = cronParser.parseExpression("0 * * * *").next().toDate().getTime(); queue.markScheduledJobAsIdle(id, nextRunAt); ``` ``` -------------------------------- ### Purge completed or failed jobs with `queue.removeDoneJobs()` and `queue.removeFailedJobs()` Source: https://context7.com/justplainstuff/plainjob/llms.txt Delete old jobs that have completed or failed. These functions are also invoked automatically during maintenance. Specify an `olderThan` duration in milliseconds to define which jobs to purge. ```typescript // Manually purge done jobs older than 1 day queue.removeDoneJobs(24 * 60 * 60 * 1000); // Manually purge failed jobs older than 3 days queue.removeFailedJobs(3 * 24 * 60 * 60 * 1000); ```