@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,372 @@
1
+ /**
2
+ * Composable, lazy query builders.
3
+ *
4
+ * Exposes two chainable, immutable builders:
5
+ *
6
+ * - {@link JobQuery} — for filtering, iterating, updating, and deleting
7
+ * jobs. Created via `client.jobs()`.
8
+ * - {@link ErrorQuery} — for iterating error records attached to a job.
9
+ * Created via `job.errors()`.
10
+ *
11
+ * Both share the same iteration shape (`Symbol.asyncIterator`, `pages()`,
12
+ * `first()`, `last()`, `toArray()`) and honour `limit` and `inPagesOf`.
13
+ *
14
+ * Every builder method returns a new instance, so queries are safe to
15
+ * share and compose.
16
+ *
17
+ * Iteration is lazy: no HTTP requests are made until the async iterator is
18
+ * consumed or a terminal method is called.
19
+ *
20
+ * @module
21
+ */
22
+ import type { Client, JobStatus, SortDirection, UpdateJobOptions } from "./client.ts";
23
+ import type { Job, JobPage, ErrorRecord, ErrorPage } from "./resources.ts";
24
+ /**
25
+ * Base class for lazy, async-iterable collections.
26
+ *
27
+ * Subclasses only need to implement `[Symbol.asyncIterator]`. All of the
28
+ * terminal helpers (`toArray`, `count`, `first`, `isEmpty`, `forEach`) and
29
+ * the lazy transforms (`map`, `filter`) are provided and work against the
30
+ * iterator.
31
+ *
32
+ * `map` and `filter` return a fresh `Lazy<U>` wrapper so that callers can
33
+ * continue chaining helpers (e.g. `query.map(...).filter(...).toArray()`)
34
+ * without having to reach for `[Symbol.asyncIterator]()` or `Array.fromAsync`
35
+ * manually.
36
+ */
37
+ export declare abstract class Lazy<T> implements AsyncIterable<T> {
38
+ abstract [Symbol.asyncIterator](): AsyncGenerator<T>;
39
+ /**
40
+ * Collect all items into an array.
41
+ *
42
+ * Delegates to the built-in `Array.fromAsync()` helper.
43
+ */
44
+ toArray(): Promise<T[]>;
45
+ /**
46
+ * Count all items.
47
+ *
48
+ * Iterates lazily, holding only one item at a time in memory. The
49
+ * underlying iterator is responsible for doing this efficiently — for
50
+ * paginated queries this means `ceil(N / pageSize)` HTTP requests.
51
+ */
52
+ count(): Promise<number>;
53
+ /** Return the first item, or `undefined` if the collection is empty. */
54
+ first(): Promise<T | undefined>;
55
+ /** Return `true` if there are no items. */
56
+ isEmpty(): Promise<boolean>;
57
+ /** Invoke `fn` for each item, awaiting async results sequentially. */
58
+ forEach(fn: (item: T) => void | Promise<void>): Promise<void>;
59
+ /** Lazily transform each item. Returns a new `Lazy`. */
60
+ map<U>(fn: (item: T) => U | Promise<U>): Lazy<U>;
61
+ /** Lazily keep only items for which `fn` returns a truthy value. */
62
+ filter(fn: (item: T) => boolean | Promise<boolean>): Lazy<T>;
63
+ }
64
+ /** Options for configuring a {@link JobQuery}. */
65
+ export interface JobQueryOptions {
66
+ /** Filter by job ID. */
67
+ id?: string | string[];
68
+ /** Filter by queue name. */
69
+ queue?: string | string[];
70
+ /** Filter by job type. */
71
+ type?: string | string[];
72
+ /** Filter by status. */
73
+ status?: JobStatus | JobStatus[];
74
+ /** jq expression applied to the payload. */
75
+ jqFilter?: string;
76
+ /** Sort order. Default: "asc" (oldest first). */
77
+ order?: SortDirection;
78
+ /** Maximum total number of jobs to return across all pages. */
79
+ limit?: number;
80
+ /** Number of jobs to fetch per page. */
81
+ pageSize?: number;
82
+ }
83
+ /**
84
+ * Lazy, paginating query builder for jobs.
85
+ *
86
+ * Created by `client.jobs()`. Each builder method returns a new instance
87
+ * (immutable). Iteration is lazy — no HTTP requests are made until the
88
+ * iterator is consumed or a terminal method is called.
89
+ *
90
+ * Implements `Symbol.asyncIterator`, so a `JobQuery` can be used directly
91
+ * in `for await...of` loops, or handed to `Array.fromAsync()` (with an
92
+ * optional mapper). To use the Node 22+ async iterator helpers
93
+ * (`.map()`, `.filter()`, etc.), obtain the iterator explicitly with
94
+ * `[Symbol.asyncIterator]()` first — those helpers live on
95
+ * `AsyncIterator.prototype`, not on async iterables.
96
+ *
97
+ * @example Iterating, mapping, and collecting
98
+ * ```ts
99
+ * // Plain loop
100
+ * for await (const job of client.jobs().byQueue("emails")) {
101
+ * console.log(job.id);
102
+ * }
103
+ *
104
+ * // Array.fromAsync with a mapper
105
+ * const ids = await Array.fromAsync(
106
+ * client.jobs().byQueue("emails"),
107
+ * (job) => job.id,
108
+ * );
109
+ *
110
+ * // Iterator helpers via [Symbol.asyncIterator]()
111
+ * const urgent = await client.jobs()
112
+ * .byQueue("emails")
113
+ * [Symbol.asyncIterator]()
114
+ * .filter((job) => (job.payload as any).urgent)
115
+ * .toArray();
116
+ * ```
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * // Count ready jobs on a queue (paginates server-side; O(1) memory)
121
+ * const n = await client.jobs()
122
+ * .byQueue("emails")
123
+ * .byStatus("ready")
124
+ * .count();
125
+ *
126
+ * // Move all jobs from one queue to another
127
+ * await client.jobs().byQueue("old").updateAll({ queue: "new" });
128
+ *
129
+ * // Delete dead jobs matching a payload subset
130
+ * await client.jobs()
131
+ * .byStatus("dead")
132
+ * .withPayloadSubset({ userId: 42 })
133
+ * .deleteAll();
134
+ *
135
+ * // Iterate in batches
136
+ * for await (const page of client.jobs().inPagesOf(100).pages()) {
137
+ * for (const job of page.jobs) console.log(job.id);
138
+ * }
139
+ * ```
140
+ */
141
+ export declare class JobQuery extends Lazy<Job> {
142
+ private client;
143
+ private _id?;
144
+ private _queue?;
145
+ private _type?;
146
+ private _status?;
147
+ private _jqFilter?;
148
+ private _order?;
149
+ private _limit?;
150
+ private _pageSize?;
151
+ /** @internal */
152
+ constructor(client: Client, options?: JobQueryOptions);
153
+ /** Filter by job ID. Replaces any existing ID filter. */
154
+ byId(id: string | string[] | undefined): JobQuery;
155
+ /** Add an ID to the existing ID filter (unions with existing). */
156
+ addId(id: string | string[]): JobQuery;
157
+ /** Filter by queue name. Replaces any existing queue filter. */
158
+ byQueue(queue: string | string[] | undefined): JobQuery;
159
+ /** Add a queue to the existing queue filter. */
160
+ addQueue(queue: string | string[]): JobQuery;
161
+ /** Filter by job type. Replaces any existing type filter. */
162
+ byType(type: string | string[] | undefined): JobQuery;
163
+ /** Add a type to the existing type filter. */
164
+ addType(type: string | string[]): JobQuery;
165
+ /** Filter by status. Replaces any existing status filter. */
166
+ byStatus(status: JobStatus | JobStatus[] | undefined): JobQuery;
167
+ /** Add a status to the existing status filter. */
168
+ addStatus(status: JobStatus | JobStatus[]): JobQuery;
169
+ /** Replace the jq payload filter expression. */
170
+ byJqFilter(jqFilter: string | undefined): JobQuery;
171
+ /**
172
+ * Add a jq payload filter. Combines with any existing filter via `and`.
173
+ */
174
+ addJqFilter(jqFilter: string): JobQuery;
175
+ /**
176
+ * Constrain results by an exact payload match.
177
+ *
178
+ * Layers onto any existing jq filter via `and`. The emitted jq
179
+ * expression is `. == <JSON>`, so matching is structural and
180
+ * key-order independent (jq's object equality sorts keys).
181
+ *
182
+ * This is syntactic sugar around `addJqFilter()` so it can be combined with
183
+ * other jq filter methods.
184
+ */
185
+ withPayload(payload: unknown): JobQuery;
186
+ /**
187
+ * Constrain results by a payload subset.
188
+ *
189
+ * Layers onto any existing jq filter via `and`. The emitted jq
190
+ * expression depends on the shape of `payload`:
191
+ *
192
+ * - **Objects**: `. | contains(<JSON>)` — deep subset check; every key in
193
+ * `payload` must exist in the job payload with a contained value.
194
+ * - **Arrays**: `.[0:N] == <JSON>` — prefix match against the first `N`
195
+ * elements of the job payload.
196
+ * - **Scalars** (number, string, boolean, null): `. == <JSON>` — equality.
197
+ *
198
+ * This is syntactic sugar around `addJqFilter()` so it can be combined with
199
+ * other jq filter methods.
200
+ */
201
+ withPayloadSubset(payload: unknown): JobQuery;
202
+ /** Set the sort order. */
203
+ order(direction: SortDirection): JobQuery;
204
+ /** Reverse the sort order. Defaults to "desc" if no order was set. */
205
+ reverseOrder(): JobQuery;
206
+ /**
207
+ * Set the maximum total number of jobs to return across all pages.
208
+ *
209
+ * Also caps page fetches and limits the number of jobs touched by
210
+ * `updateAll` / `deleteAll`.
211
+ */
212
+ limit(n: number): JobQuery;
213
+ /**
214
+ * Set the page size for pagination.
215
+ *
216
+ * Affects how many jobs are fetched per HTTP request during iteration,
217
+ * and also causes `updateAll` / `deleteAll` to batch per page using
218
+ * ID-scoped bulk operations.
219
+ */
220
+ inPagesOf(n: number): JobQuery;
221
+ /**
222
+ * Return the first matching job, or `undefined` if none.
223
+ *
224
+ * Optimised override: pushes `limit=1` to the server instead of
225
+ * iterating a full default page.
226
+ */
227
+ first(): Promise<Job | undefined>;
228
+ /**
229
+ * Return the last matching job, or `undefined` if none.
230
+ *
231
+ * Optimised: reverses the order and fetches a single job.
232
+ */
233
+ last(): Promise<Job | undefined>;
234
+ /**
235
+ * Update all matching jobs.
236
+ *
237
+ * Behaviour depends on whether `limit` or `pageSize` is set:
238
+ *
239
+ * - **Unbounded**: a single bulk update request is sent with the query
240
+ * filters as the scope.
241
+ * - **Bounded**: pages are iterated and a separate bulk update is issued
242
+ * per page, scoped to both the query filters and the IDs on that page.
243
+ * This is safe against races with newly enqueued jobs and lets callers
244
+ * throttle large update operations via `inPagesOf`.
245
+ *
246
+ * Returns the total number of updated jobs.
247
+ */
248
+ updateAll(apply: UpdateJobOptions): Promise<number>;
249
+ /**
250
+ * Update the first matching job.
251
+ *
252
+ * Returns 1 if a job was updated, 0 if no jobs matched.
253
+ */
254
+ updateOne(apply: UpdateJobOptions): Promise<number>;
255
+ /**
256
+ * Delete all matching jobs.
257
+ *
258
+ * Behaviour depends on whether `limit` or `pageSize` is set:
259
+ *
260
+ * - **Unbounded**: a single bulk delete request is sent with the query
261
+ * filters as the scope. A query with no filters will delete *all* jobs
262
+ * on the server — useful in tests, dangerous in production.
263
+ * - **Bounded**: pages are iterated and a separate bulk delete is issued
264
+ * per page, scoped to both the query filters and the IDs on that page.
265
+ *
266
+ * Returns the total number of deleted jobs.
267
+ */
268
+ deleteAll(): Promise<number>;
269
+ /**
270
+ * Delete the first matching job.
271
+ *
272
+ * Returns 1 if a job was deleted, 0 if no jobs matched.
273
+ */
274
+ deleteOne(): Promise<number>;
275
+ /**
276
+ * Async iterator over individual jobs.
277
+ *
278
+ * Lazily paginates through results, respecting `limit` if set. Enables
279
+ * `for await...of` loops and is also consumable by `Array.fromAsync()`.
280
+ */
281
+ [Symbol.asyncIterator](): AsyncGenerator<Job>;
282
+ /**
283
+ * Async iterator over pages of jobs.
284
+ *
285
+ * Each yielded value is a `JobPage`. Useful when you want to process jobs
286
+ * in batches. Terminates once the query's `limit` has been reached (does
287
+ * not truncate the final page).
288
+ */
289
+ pages(): AsyncGenerator<JobPage>;
290
+ private toWhere;
291
+ private rebuild;
292
+ }
293
+ /** Options for configuring an {@link ErrorQuery}. */
294
+ export interface ErrorQueryOptions {
295
+ /** Sort order. Default: "asc" (oldest first). */
296
+ order?: SortDirection;
297
+ /** Maximum total number of errors to return across all pages. */
298
+ limit?: number;
299
+ /** Number of errors to fetch per page. */
300
+ pageSize?: number;
301
+ }
302
+ /**
303
+ * Lazy async iterator over error records with a chainable builder API.
304
+ *
305
+ * Created by `Job.errors()`. Each builder method returns a new instance
306
+ * (immutable). Iteration is lazy — no HTTP requests are made until the
307
+ * iterator is consumed.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * // Iterate all errors
312
+ * for await (const error of job.errors()) {
313
+ * console.log(`Attempt ${error.attempt}: ${error.message}`);
314
+ * }
315
+ *
316
+ * // With builder options
317
+ * const recent = await job.errors()
318
+ * .order("desc")
319
+ * .limit(5)
320
+ * .toArray();
321
+ * ```
322
+ */
323
+ export declare class ErrorQuery extends Lazy<ErrorRecord> {
324
+ private client;
325
+ private jobId;
326
+ private _order?;
327
+ private _limit?;
328
+ private _pageSize?;
329
+ /** @internal */
330
+ constructor(client: Client, jobId: string, options?: ErrorQueryOptions);
331
+ /** Set the sort order. Returns a new query. */
332
+ order(direction: SortDirection): ErrorQuery;
333
+ /**
334
+ * Set the maximum total number of errors to return across all pages.
335
+ *
336
+ * Also used to optimise the page size — if the limit is smaller than
337
+ * the page size, only the needed amount is fetched.
338
+ */
339
+ limit(n: number): ErrorQuery;
340
+ /** Set the page size for pagination. Returns a new query. */
341
+ inPagesOf(n: number): ErrorQuery;
342
+ /** Reverse the sort order. Returns a new query. */
343
+ reverseOrder(): ErrorQuery;
344
+ /**
345
+ * Return the first error, or `undefined` if none.
346
+ *
347
+ * Optimised override: pushes `limit=1` to the server instead of
348
+ * iterating a full default page.
349
+ */
350
+ first(): Promise<ErrorRecord | undefined>;
351
+ /**
352
+ * Return the last error, or `undefined` if none.
353
+ *
354
+ * Optimised: reverses the order and fetches a single error.
355
+ */
356
+ last(): Promise<ErrorRecord | undefined>;
357
+ /**
358
+ * Async iterator over individual error records.
359
+ *
360
+ * Lazily paginates through results, respecting `limit` if set.
361
+ * Enables `for await...of` loops and is consumable by `Array.fromAsync()`.
362
+ */
363
+ [Symbol.asyncIterator](): AsyncGenerator<ErrorRecord>;
364
+ /**
365
+ * Async iterator over pages of error records.
366
+ *
367
+ * Each yielded value is an `ErrorPage`. Useful when you want to
368
+ * process errors in batches.
369
+ */
370
+ pages(): AsyncGenerator<ErrorPage>;
371
+ private rebuild;
372
+ }