@zizq-labs/zizq 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ import { Client, type BackoffConfig, Job, type RetentionConfig, type UniqueScope } from "./client.ts";
2
+ import type { JobFunction } from "./handler.ts";
3
+ /**
4
+ * Input for enqueueing a job.
5
+ *
6
+ * The `type` field accepts either a string job type name or a function
7
+ * reference with optional attached `zizqOptions`. When a function is provided,
8
+ * its `zizqOptions` supplies defaults for `queue`, `priority`, etc. These
9
+ * defaults can be overridden by inputs specified at enqueue-time.
10
+ *
11
+ * When `type` is a string, `queue` is required in the inputs.
12
+ *
13
+ * @example Function reference
14
+ * ```ts
15
+ * await enqueue(client, { type: sendEmail, payload: { to: "a@b.com" } });
16
+ * ```
17
+ *
18
+ * @example String type with explicit config
19
+ * ```ts
20
+ * await enqueue(client, {
21
+ * type: "send_email",
22
+ * queue: "emails",
23
+ * payload: { to: "a@b.com" },
24
+ * priority: 100,
25
+ * });
26
+ * ```
27
+ */
28
+ export interface EnqueueInput {
29
+ /** Job type — a function reference or a string type name. */
30
+ type: JobFunction | string;
31
+ /** Arbitrary payload delivered to the worker. */
32
+ payload: unknown;
33
+ /**
34
+ * Target queue name.
35
+ *
36
+ * Required when `type` is a string and no `zizqOptions.queue` default
37
+ * is available.
38
+ */
39
+ queue?: string;
40
+ /**
41
+ * Priority (lower = higher priority).
42
+ *
43
+ * Valid range is 0 to 65536. Default: 32768.
44
+ */
45
+ priority?: number;
46
+ /**
47
+ * Timestamp (ms since epoch) when the job becomes eligible.
48
+ *
49
+ * When set to a future timestamp the job is created in the "scheduled"
50
+ * status. Otherwise the job is created in the "ready" status.
51
+ */
52
+ readyAt?: number;
53
+ /**
54
+ * Per-job retry limit.
55
+ *
56
+ * When not set the server default value applies.
57
+ */
58
+ retryLimit?: number;
59
+ /** Per-job backoff configuration. */
60
+ backoff?: BackoffConfig;
61
+ /** Per-job retention configuration. */
62
+ retention?: RetentionConfig;
63
+ /**
64
+ * Unique key for enqueue-time deduplication.
65
+ *
66
+ * Requires a pro license on the server.
67
+ *
68
+ * The key is global across all queues and job types. Prefix with the job
69
+ * type to make it unique per job type.
70
+ */
71
+ uniqueKey?: string;
72
+ /** Uniqueness scope. Only valid when `uniqueKey` is set. */
73
+ uniqueWhile?: UniqueScope;
74
+ }
75
+ /**
76
+ * Enqueue a single job.
77
+ *
78
+ * @param client - The Zizq client to use for the HTTP request.
79
+ * @param input - Job type, payload, and optional configuration.
80
+ * @returns The created job, including its server-assigned `id` and `status`.
81
+ *
82
+ * @example Function reference
83
+ * ```ts
84
+ * async function sendEmail(payload) { ... }
85
+ * sendEmail.zizqOptions = { queue: "emails", priority: 100 };
86
+ *
87
+ * const job = await enqueue(client, {
88
+ * type: sendEmail,
89
+ * payload: { to: "user@example.com" },
90
+ * });
91
+ * ```
92
+ *
93
+ * @example String type
94
+ * ```ts
95
+ * const job = await enqueue(client, {
96
+ * type: "send_email",
97
+ * queue: "emails",
98
+ * payload: { to: "user@example.com" },
99
+ * });
100
+ * ```
101
+ */
102
+ export declare function enqueue(client: Client, input: EnqueueInput): Promise<Job>;
103
+ /**
104
+ * Enqueue multiple jobs in a single request.
105
+ *
106
+ * @param client - The Zizq client to use for the HTTP request.
107
+ * @param inputs - Array of job inputs.
108
+ * @returns An array of created jobs in the same order as the input.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const jobs = await enqueueBulk(client, [
113
+ * { type: sendEmail, payload: { to: "a@b.com" } },
114
+ * { type: sendEmail, payload: { to: "c@d.com" }, priority: 1 },
115
+ * { type: "manual_job", queue: "ops", payload: {} },
116
+ * ]);
117
+ * ```
118
+ */
119
+ export declare function enqueueBulk(client: Client, inputs: EnqueueInput[]): Promise<Job[]>;
@@ -0,0 +1,110 @@
1
+ // Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+ /**
4
+ * Enqueue a single job.
5
+ *
6
+ * @param client - The Zizq client to use for the HTTP request.
7
+ * @param input - Job type, payload, and optional configuration.
8
+ * @returns The created job, including its server-assigned `id` and `status`.
9
+ *
10
+ * @example Function reference
11
+ * ```ts
12
+ * async function sendEmail(payload) { ... }
13
+ * sendEmail.zizqOptions = { queue: "emails", priority: 100 };
14
+ *
15
+ * const job = await enqueue(client, {
16
+ * type: sendEmail,
17
+ * payload: { to: "user@example.com" },
18
+ * });
19
+ * ```
20
+ *
21
+ * @example String type
22
+ * ```ts
23
+ * const job = await enqueue(client, {
24
+ * type: "send_email",
25
+ * queue: "emails",
26
+ * payload: { to: "user@example.com" },
27
+ * });
28
+ * ```
29
+ */
30
+ export async function enqueue(client, input) {
31
+ return client.enqueue(resolveInput(input));
32
+ }
33
+ /**
34
+ * Enqueue multiple jobs in a single request.
35
+ *
36
+ * @param client - The Zizq client to use for the HTTP request.
37
+ * @param inputs - Array of job inputs.
38
+ * @returns An array of created jobs in the same order as the input.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const jobs = await enqueueBulk(client, [
43
+ * { type: sendEmail, payload: { to: "a@b.com" } },
44
+ * { type: sendEmail, payload: { to: "c@d.com" }, priority: 1 },
45
+ * { type: "manual_job", queue: "ops", payload: {} },
46
+ * ]);
47
+ * ```
48
+ */
49
+ export async function enqueueBulk(client, inputs) {
50
+ return client.enqueueBulk(inputs.map(resolveInput));
51
+ }
52
+ // --- Internal ---
53
+ // Compute the unique key from a job function's ZizqOptions + payload.
54
+ //
55
+ // The function form is called with `(jobFn, payload)` so resolvers
56
+ // can read metadata (like the type name) from the job function itself.
57
+ function computeUniqueKey(jobFn, payload) {
58
+ const uniqueKey = jobFn.zizqOptions?.uniqueKey;
59
+ if (!uniqueKey)
60
+ return undefined;
61
+ if (typeof uniqueKey === "function")
62
+ return uniqueKey(jobFn, payload);
63
+ return uniqueKey;
64
+ }
65
+ // Resolve an EnqueueInput into a low-level EnqueueOptions by taking the
66
+ // result of zizqOptions (if provided), optionally transformed, and then
67
+ // merging over the top any overrides present in the input.
68
+ function resolveInput(input) {
69
+ let jobType;
70
+ let defaults;
71
+ if (typeof input.type === "function") {
72
+ defaults = input.type.zizqOptions;
73
+ jobType = defaults?.type ?? input.type.name;
74
+ if (!jobType) {
75
+ throw new Error("Job function must have a name or zizqOptions.type");
76
+ }
77
+ }
78
+ else {
79
+ jobType = input.type;
80
+ }
81
+ const queue = input.queue ?? defaults?.queue;
82
+ if (!queue) {
83
+ throw new Error(`No queue specified for job type "${jobType}"`);
84
+ }
85
+ const uniqueKey = input.uniqueKey ??
86
+ (typeof input.type === "function"
87
+ ? computeUniqueKey(input.type, input.payload)
88
+ : undefined);
89
+ const uniqueWhile = input.uniqueWhile ?? defaults?.uniqueWhile;
90
+ const opts = {
91
+ type: jobType,
92
+ queue,
93
+ payload: input.payload,
94
+ priority: input.priority ?? defaults?.priority,
95
+ readyAt: input.readyAt,
96
+ retryLimit: input.retryLimit ?? defaults?.retryLimit,
97
+ backoff: input.backoff ?? defaults?.backoff,
98
+ retention: input.retention,
99
+ uniqueKey,
100
+ uniqueWhile: uniqueKey ? uniqueWhile : undefined,
101
+ };
102
+ // Apply the transform hook (if any). Transforms can change the options based
103
+ // on the payload or other factors, and can mutate `opts` in place, or return
104
+ // a new object to use instead.
105
+ if (defaults?.transform) {
106
+ const result = defaults.transform(opts, input.payload);
107
+ return result ?? opts;
108
+ }
109
+ return opts;
110
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Job handler types and configuration.
3
+ *
4
+ * These types define how handlers and job functions are structured and
5
+ * configured. They are used both at enqueue time (to read defaults fro
6
+ * `zizqOptions`) and when building a handler for the worker (to build the
7
+ * dispatch table).
8
+ *
9
+ * @module
10
+ */
11
+ import type { BackoffConfig, EnqueueOptions, RetentionConfig, UniqueScope } from "./client.ts";
12
+ import type { Job } from "./resources.ts";
13
+ /**
14
+ * Configuration that can be attached to a job function via `fn.zizqOptions`.
15
+ *
16
+ * These options provide defaults when the function is used with `enqueue()`
17
+ * or registered with `buildHandler()`. All fields are optional.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * async function sendEmail(payload) { ... }
22
+ *
23
+ * sendEmail.zizqOptions = {
24
+ * queue: "emails",
25
+ * priority: 100,
26
+ * retryLimit: 5,
27
+ * };
28
+ * ```
29
+ */
30
+ export interface ZizqOptions {
31
+ /**
32
+ * Explicit job type name.
33
+ *
34
+ * When omitted, the function's `.name` property is used instead. Set this
35
+ * if the function name could be mangled by minification.
36
+ */
37
+ type?: string;
38
+ /** Default queue for this job. */
39
+ queue?: string;
40
+ /** Default priority (lower = higher priority). */
41
+ priority?: number;
42
+ /** Default retry limit before the job is killed. */
43
+ retryLimit?: number;
44
+ /** Default backoff configuration. */
45
+ backoff?: BackoffConfig;
46
+ /** Default retention configuration. */
47
+ retention?: RetentionConfig;
48
+ /**
49
+ * Unique key for deduplication.
50
+ *
51
+ * Can be a static string or a function that computes the key from the
52
+ * job function (for metadata like the type name) and the payload at
53
+ * enqueue time.
54
+ *
55
+ * For common cases, use the `uniqueKey(...fields)` helper from this
56
+ * module, which returns a resolver that picks fields from the payload
57
+ * and prefixes the job type automatically.
58
+ */
59
+ uniqueKey?: string | ((fn: JobFunction, payload: unknown) => string);
60
+ /** Uniqueness scope. Only used when `uniqueKey` is set. */
61
+ uniqueWhile?: UniqueScope;
62
+ /**
63
+ * Final transform applied to the resolved enqueue options before it is sent
64
+ * to the server.
65
+ *
66
+ * Receives the fully-resolved `EnqueueOptions` (with defaults and
67
+ * inline overrides already merged) along with the job payload. The transform
68
+ * can mutate the options in place, or return a new object.
69
+ *
70
+ * Useful for dynamic adjustments that need to see the already-computed
71
+ * values e.g. lowering priority for "important" payloads relative
72
+ * to the default priority.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * sendEmail.zizqOptions = {
77
+ * queue: "emails",
78
+ * priority: 100,
79
+ * transform: (opts, payload) => {
80
+ * if ((payload as any).urgent) {
81
+ * opts.priority = Math.floor(opts.priority! / 2);
82
+ * }
83
+ * },
84
+ * };
85
+ * ```
86
+ *
87
+ * @example Composing multiple transforms
88
+ * ```ts
89
+ * function compose(...transforms: EnqueueTransform[]): EnqueueTransform {
90
+ * return (opts, payload) => {
91
+ * for (const t of transforms) opts = t(opts, payload);
92
+ * return opts;
93
+ * };
94
+ * }
95
+ *
96
+ * sendEmail.zizqOptions = {
97
+ * transform: compose(doublePriority, tagWithUser),
98
+ * };
99
+ * ```
100
+ */
101
+ transform?: EnqueueTransform;
102
+ }
103
+ /**
104
+ * A final transform applied to a resolved enqueue options before it is
105
+ * sent to the server. May mutate `opts` in place, or return a new optsuest.
106
+ */
107
+ export type EnqueueTransform = (opts: EnqueueOptions, payload: unknown) => EnqueueOptions | void;
108
+ /**
109
+ * Top-level function that processes a single job dispatched by the worker.
110
+ *
111
+ * Receives the full job, with `job.payload` for the user-supplied payload
112
+ * and metadata like `job.id`, `job.type`, `job.attempts`, etc.
113
+ *
114
+ * This would typically be implemented with an internal lookup table or switch
115
+ * statement based on the queue/type.
116
+ *
117
+ * - Returning (or resolving) normally signals success.
118
+ * - Throwing (or rejecting) signals failure.
119
+ */
120
+ export type JobHandler = (job: Job) => Promise<void> | void;
121
+ /**
122
+ * A user-defined job function.
123
+ *
124
+ * Use the `job` argument to access metadata like `id`, `type`, or `attempts`.
125
+ *
126
+ * The worker always passes both `payload` and `job`, but most handlers
127
+ * likely only declare `payload` unless they explicitly need full access to the
128
+ * job.
129
+ *
130
+ * Most handlers only need the payload:
131
+ *
132
+ * ```ts
133
+ * const sendEmail: JobFunction = async (payload) => {
134
+ * await mailer.send(payload.to, payload.subject);
135
+ * };
136
+ * sendEmail.zizqOptions = { queue: "emails" };
137
+ * ```
138
+ *
139
+ * Handlers that need job metadata can take both:
140
+ *
141
+ * ```ts
142
+ * const retryWithBackoff: JobFunction = async (payload, job) => {
143
+ * if (job.attempts > 3) { ... }
144
+ * };
145
+ * ```
146
+ *
147
+ * Use `buildHandler([...])` to wrap an array of `JobFunction`s
148
+ * into a single `JobHandler` for the `Worker`.
149
+ */
150
+ export interface JobFunction {
151
+ /** Function signature for the handler function. */
152
+ (payload: unknown, job: Job): Promise<void> | void;
153
+ /** Optional defaults and configuration for this job */
154
+ zizqOptions?: ZizqOptions;
155
+ }
156
+ /**
157
+ * Build a `JobHandler` that dispatches incoming jobs to the matching
158
+ * function from an array, looking up by `zizqOptions.type` (or `.name`).
159
+ *
160
+ * Works with plain JS functions, named functions, and `JobFunction`s with
161
+ * attached `zizqOptions`. Each function is called with `(payload, job)`.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { Worker, buildHandler } from "@zizq-labs/zizq";
166
+ *
167
+ * async function sendEmail(payload) { ... }
168
+ * sendEmail.zizqOptions = { queue: "emails" };
169
+ *
170
+ * const worker = new Worker({
171
+ * client,
172
+ * handler: buildHandler([sendEmail, generateReport]),
173
+ * });
174
+ * ```
175
+ *
176
+ * @throws If any function lacks a name or `zizqOptions.type`, or if two
177
+ * functions resolve to the same type.
178
+ */
179
+ export declare function buildHandler(jobs: JobFunction[]): JobHandler;
@@ -0,0 +1,45 @@
1
+ // Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+ /**
4
+ * Build a `JobHandler` that dispatches incoming jobs to the matching
5
+ * function from an array, looking up by `zizqOptions.type` (or `.name`).
6
+ *
7
+ * Works with plain JS functions, named functions, and `JobFunction`s with
8
+ * attached `zizqOptions`. Each function is called with `(payload, job)`.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { Worker, buildHandler } from "@zizq-labs/zizq";
13
+ *
14
+ * async function sendEmail(payload) { ... }
15
+ * sendEmail.zizqOptions = { queue: "emails" };
16
+ *
17
+ * const worker = new Worker({
18
+ * client,
19
+ * handler: buildHandler([sendEmail, generateReport]),
20
+ * });
21
+ * ```
22
+ *
23
+ * @throws If any function lacks a name or `zizqOptions.type`, or if two
24
+ * functions resolve to the same type.
25
+ */
26
+ export function buildHandler(jobs) {
27
+ const handlers = new Map();
28
+ for (const fn of jobs) {
29
+ const typeName = fn.zizqOptions?.type ?? fn.name;
30
+ if (!typeName) {
31
+ throw new Error("Job function must have a name or zizqOptions.type");
32
+ }
33
+ if (handlers.has(typeName)) {
34
+ throw new Error(`Duplicate job type registered: "${typeName}"`);
35
+ }
36
+ handlers.set(typeName, fn);
37
+ }
38
+ return async (job) => {
39
+ const fn = handlers.get(job.type);
40
+ if (!fn) {
41
+ throw new Error(`No handler registered for job type: ${job.type}`);
42
+ }
43
+ await fn(job.payload, job);
44
+ };
45
+ }
@@ -0,0 +1,11 @@
1
+ export { Client, Job, JobPage, ErrorRecord, ErrorPage, ZizqError, ConnectionError, ResponseError, ClientError, NotFoundError, ServerError, } from "./client.ts";
2
+ export type { ClientOptions, JobData, EnqueueOptions, ListJobsOptions, JobFilter, DeleteAllJobsOptions, UpdateJobOptions, UpdateAllJobsOptions, ListErrorsOptions, ErrorRecordData, FailureOptions, TakeOptions, BackoffConfig, RetentionConfig, JobStatus, UniqueScope, SortDirection, TlsOptions, Format, } from "./client.ts";
3
+ export { Worker } from "./worker.ts";
4
+ export type { WorkerOptions, Logger, RequestRetryOptions } from "./worker.ts";
5
+ export { buildHandler } from "./handler.ts";
6
+ export type { JobFunction, JobHandler, ZizqOptions, EnqueueTransform, } from "./handler.ts";
7
+ export { Lazy, ErrorQuery, JobQuery } from "./query.ts";
8
+ export type { ErrorQueryOptions, JobQueryOptions } from "./query.ts";
9
+ export { enqueue, enqueueBulk } from "./enqueue.ts";
10
+ export type { EnqueueInput } from "./enqueue.ts";
11
+ export { uniqueKey } from "./unique-key.ts";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
2
+ // Licensed under the MIT License. See LICENSE file for details.
3
+ export { Client, Job, JobPage, ErrorRecord, ErrorPage, ZizqError, ConnectionError, ResponseError, ClientError, NotFoundError, ServerError, } from "./client.js";
4
+ export { Worker } from "./worker.js";
5
+ export { buildHandler } from "./handler.js";
6
+ export { Lazy, ErrorQuery, JobQuery } from "./query.js";
7
+ export { enqueue, enqueueBulk } from "./enqueue.js";
8
+ export { uniqueKey } from "./unique-key.js";