@superfunctions/queue 0.0.1 → 0.2.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.
package/dist/index.js CHANGED
@@ -1,136 +1,7 @@
1
- function normalizeQueueName(queueName) {
2
- const normalized = queueName.trim();
3
- if (!normalized || normalized.includes('/') || normalized.includes('\\') || normalized.includes('..')) {
4
- throw new Error('FLOWFN_QUEUE_NAME_INVALID');
5
- }
6
- return normalized;
7
- }
8
- export class MemoryQueueAdapter {
9
- queues = new Map();
10
- async enqueue(queueName, payload) {
11
- const normalizedQueueName = normalizeQueueName(queueName);
12
- const queue = this.queues.get(normalizedQueueName) ?? [];
13
- queue.push(payload);
14
- this.queues.set(normalizedQueueName, queue);
15
- }
16
- async dequeue(queueName) {
17
- const normalizedQueueName = normalizeQueueName(queueName);
18
- const queue = this.queues.get(normalizedQueueName);
19
- if (!queue || queue.length === 0) {
20
- return null;
21
- }
22
- return queue.shift();
23
- }
24
- async dequeueMatching(queueName, matcher) {
25
- const normalizedQueueName = normalizeQueueName(queueName);
26
- const queue = this.queues.get(normalizedQueueName);
27
- if (!queue || queue.length === 0) {
28
- return null;
29
- }
30
- const index = queue.findIndex(matcher);
31
- if (index === -1) {
32
- return null;
33
- }
34
- const [payload] = queue.splice(index, 1);
35
- return payload;
36
- }
37
- peek(queueName) {
38
- const normalizedQueueName = normalizeQueueName(queueName);
39
- return [...(this.queues.get(normalizedQueueName) ?? [])];
40
- }
41
- size(queueName) {
42
- const normalizedQueueName = normalizeQueueName(queueName);
43
- return this.queues.get(normalizedQueueName)?.length ?? 0;
44
- }
45
- }
46
- function normalizeBaseUrl(baseUrl) {
47
- let normalized = baseUrl;
48
- while (normalized.endsWith('/')) {
49
- normalized = normalized.slice(0, -1);
50
- }
51
- return normalized;
52
- }
53
- function encodeQueueName(queueName) {
54
- return encodeURIComponent(normalizeQueueName(queueName));
55
- }
56
- function normalizeTimeoutMs(value) {
57
- if (value === undefined) {
58
- return 15_000;
59
- }
60
- if (!Number.isFinite(value)) {
61
- throw new Error('FLOWFN_QUEUE_TIMEOUT_INVALID');
62
- }
63
- return Math.max(0, value);
64
- }
65
- export class FlowFnQueueAdapter {
66
- config;
67
- fetcher;
68
- timeoutMs;
69
- baseUrl;
70
- constructor(config) {
71
- this.config = config;
72
- this.fetcher = config.fetch ?? globalThis.fetch;
73
- this.timeoutMs = normalizeTimeoutMs(config.timeoutMs);
74
- this.baseUrl = normalizeBaseUrl(config.baseUrl);
75
- }
76
- async enqueue(queueName, payload) {
77
- const encodedQueueName = encodeQueueName(queueName);
78
- const response = await this.fetchWithTimeout(`${this.baseUrl}/queues/${encodedQueueName}/enqueue`, {
79
- method: 'POST',
80
- headers: {
81
- 'Content-Type': 'application/json',
82
- Authorization: `Bearer ${this.config.apiKey}`,
83
- },
84
- body: JSON.stringify(payload),
85
- });
86
- if (!response.ok) {
87
- throw new Error(`FlowFn enqueue failed: ${response.status} ${response.statusText}`);
88
- }
89
- }
90
- async dequeue(queueName) {
91
- const encodedQueueName = encodeQueueName(queueName);
92
- const response = await this.fetchWithTimeout(`${this.baseUrl}/queues/${encodedQueueName}/dequeue`, {
93
- method: 'POST',
94
- headers: {
95
- Authorization: `Bearer ${this.config.apiKey}`,
96
- },
97
- });
98
- if (response.status === 404) {
99
- return null;
100
- }
101
- if (!response.ok) {
102
- throw new Error(`FlowFn dequeue failed: ${response.status} ${response.statusText}`);
103
- }
104
- const data = (await response.json());
105
- return 'payload' in data ? data.payload : null;
106
- }
107
- async dequeueMatching(queueName, matcher) {
108
- void matcher;
109
- encodeQueueName(queueName);
110
- throw new Error('FLOWFN_QUEUE_MATCHING_UNSUPPORTED');
111
- }
112
- peek(queueName) {
113
- encodeQueueName(queueName);
114
- throw new Error('FLOWFN_QUEUE_PEEK_UNSUPPORTED');
115
- }
116
- size(queueName) {
117
- encodeQueueName(queueName);
118
- throw new Error('FLOWFN_QUEUE_SIZE_UNSUPPORTED');
119
- }
120
- async fetchWithTimeout(url, init) {
121
- const controller = typeof AbortController === 'function' ? new AbortController() : undefined;
122
- const timeoutId = controller && this.timeoutMs > 0 ? setTimeout(() => controller.abort(), this.timeoutMs) : undefined;
123
- try {
124
- return await this.fetcher(url, {
125
- ...init,
126
- signal: controller?.signal,
127
- });
128
- }
129
- finally {
130
- if (timeoutId) {
131
- clearTimeout(timeoutId);
132
- }
133
- }
134
- }
135
- }
1
+ export { QueueError } from './validation.js';
2
+ export { MemoryQueueAdapter } from './memory.js';
3
+ export { DatabaseQueueAdapter } from './database.js';
4
+ export { createD1QueueClient, createPostgresQueueClient, createSqliteQueueClient } from './sql-client.js';
5
+ export { DEFAULT_QUEUE_JOBS_TABLE, queueJobsMigrationSql, queueJobsMigrationStatements } from './migrations.js';
6
+ export { FlowFnQueueAdapter } from './flowfn.js';
136
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAW1G,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { ClaimOptions, ClaimedJob, EnqueueOptions, EnqueueResult, FailOptions, LeaseFence, QueueAdapter, QueueJob } from './types.js';
2
+ export interface MemoryQueueAdapterOptions {
3
+ /**
4
+ * Maximum number of pending and leased jobs retained per queue. When
5
+ * reached, further `enqueue` calls reject with `FLOWFN_QUEUE_FULL` instead
6
+ * of growing without bound. Unset means unbounded.
7
+ */
8
+ maxSize?: number;
9
+ /** Clock in epoch milliseconds. Defaults to `Date.now`. */
10
+ now?: () => number;
11
+ }
12
+ /**
13
+ * Process-local queue for tests and single-process use. Jobs do not survive a
14
+ * restart, and dead-lettered jobs are discarded; use `DatabaseQueueAdapter`
15
+ * for durable storage.
16
+ */
17
+ export declare class MemoryQueueAdapter<TPayload> implements QueueAdapter<TPayload> {
18
+ private readonly jobs;
19
+ private readonly queues;
20
+ private readonly maxSize?;
21
+ private readonly now;
22
+ private nextId;
23
+ constructor(options?: MemoryQueueAdapterOptions);
24
+ enqueue(queueName: string, payload: TPayload, options?: EnqueueOptions): Promise<EnqueueResult>;
25
+ claim(queueName: string, options: ClaimOptions): Promise<ClaimedJob<TPayload> | null>;
26
+ ack(id: string, fence?: LeaseFence): Promise<void>;
27
+ fail(id: string, options: FailOptions): Promise<void>;
28
+ extendLease(id: string, leaseMs: number, fence?: LeaseFence): Promise<void>;
29
+ peek(queueName: string, limit: number): Promise<QueueJob<TPayload>[]>;
30
+ size(queueName: string): Promise<number>;
31
+ private remove;
32
+ private requireLeased;
33
+ }
34
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,YAAY,EACZ,QAAQ,EAET,MAAM,YAAY,CAAC;AAapB,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AA+BD;;;;GAIG;AACH,qBAAa,kBAAkB,CAAC,QAAQ,CAAE,YAAW,YAAY,CAAC,QAAQ,CAAC;IACzE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0C;IAE/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiD;IACxE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,MAAM,CAAK;gBAEP,OAAO,GAAE,yBAA8B;IAU7C,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAsC/F,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAsBrF,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrD,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3E,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;IAqBrE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,aAAa;CAOtB"}
package/dist/memory.js ADDED
@@ -0,0 +1,153 @@
1
+ import { QueueError, normalizeEnqueueOptions, normalizeFailOptions, normalizeFence, normalizeJobId, normalizeLeaseMs, normalizeLimit, normalizeQueueName, readClock, } from './validation.js';
2
+ function isClaimable(job, now) {
3
+ return job.state === 'pending' ? job.availableAt <= now : job.leaseUntil <= now;
4
+ }
5
+ function compareClaimOrder(a, b) {
6
+ return a.availableAt - b.availableAt || a.id - b.id;
7
+ }
8
+ /**
9
+ * Process-local queue for tests and single-process use. Jobs do not survive a
10
+ * restart, and dead-lettered jobs are discarded; use `DatabaseQueueAdapter`
11
+ * for durable storage.
12
+ */
13
+ export class MemoryQueueAdapter {
14
+ jobs = new Map();
15
+ // Per-queue indexes keep enqueue O(1) and queues isolated from each other's backlogs.
16
+ queues = new Map();
17
+ maxSize;
18
+ now;
19
+ nextId = 1;
20
+ constructor(options = {}) {
21
+ if (options.maxSize !== undefined) {
22
+ if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) {
23
+ throw new QueueError('FLOWFN_QUEUE_MAX_SIZE_INVALID');
24
+ }
25
+ }
26
+ this.maxSize = options.maxSize;
27
+ this.now = options.now ?? Date.now;
28
+ }
29
+ async enqueue(queueName, payload, options) {
30
+ const queue = normalizeQueueName(queueName);
31
+ const { delayMs, dedupeKey } = normalizeEnqueueOptions(options);
32
+ const now = readClock(this.now);
33
+ const existing = this.queues.get(queue);
34
+ const duplicate = dedupeKey === null ? undefined : existing?.dedupe.get(dedupeKey);
35
+ if (duplicate) {
36
+ return { id: String(duplicate.id), deduplicated: true };
37
+ }
38
+ if (this.maxSize !== undefined && (existing?.jobs.size ?? 0) >= this.maxSize) {
39
+ throw new QueueError('FLOWFN_QUEUE_FULL');
40
+ }
41
+ const id = this.nextId++;
42
+ const job = {
43
+ id,
44
+ queue,
45
+ payload,
46
+ state: 'pending',
47
+ attempt: 0,
48
+ availableAt: now + delayMs,
49
+ leaseUntil: null,
50
+ dedupeKey,
51
+ lastError: null,
52
+ createdAt: now,
53
+ updatedAt: now,
54
+ };
55
+ const index = existing ?? { jobs: new Map(), dedupe: new Map() };
56
+ this.queues.set(queue, index);
57
+ this.jobs.set(id, job);
58
+ index.jobs.set(id, job);
59
+ if (dedupeKey !== null) {
60
+ index.dedupe.set(dedupeKey, job);
61
+ }
62
+ return { id: String(id), deduplicated: false };
63
+ }
64
+ async claim(queueName, options) {
65
+ const queue = normalizeQueueName(queueName);
66
+ const leaseMs = normalizeLeaseMs(options?.leaseMs);
67
+ const now = readClock(this.now);
68
+ let next;
69
+ for (const job of this.queues.get(queue)?.jobs.values() ?? []) {
70
+ if (isClaimable(job, now) && (!next || compareClaimOrder(job, next) < 0)) {
71
+ next = job;
72
+ }
73
+ }
74
+ if (!next) {
75
+ return null;
76
+ }
77
+ next.state = 'leased';
78
+ next.attempt += 1;
79
+ next.leaseUntil = now + leaseMs;
80
+ next.updatedAt = now;
81
+ return { id: String(next.id), payload: next.payload, attempt: next.attempt };
82
+ }
83
+ async ack(id, fence) {
84
+ const job = this.requireLeased(id, normalizeFence(fence));
85
+ this.remove(job);
86
+ }
87
+ async fail(id, options) {
88
+ const normalized = normalizeFailOptions(options);
89
+ const job = this.requireLeased(id, normalized.attempt);
90
+ if (normalized.kind === 'dead') {
91
+ this.remove(job);
92
+ return;
93
+ }
94
+ job.state = 'pending';
95
+ job.availableAt = normalized.retryAt;
96
+ job.leaseUntil = null;
97
+ job.lastError = normalized.reason;
98
+ job.updatedAt = readClock(this.now);
99
+ }
100
+ async extendLease(id, leaseMs, fence) {
101
+ const normalizedLeaseMs = normalizeLeaseMs(leaseMs);
102
+ const job = this.requireLeased(id, normalizeFence(fence));
103
+ const now = readClock(this.now);
104
+ job.leaseUntil = now + normalizedLeaseMs;
105
+ job.updatedAt = now;
106
+ }
107
+ async peek(queueName, limit) {
108
+ const queue = normalizeQueueName(queueName);
109
+ const normalizedLimit = normalizeLimit(limit);
110
+ return [...(this.queues.get(queue)?.jobs.values() ?? [])]
111
+ .sort(compareClaimOrder)
112
+ .slice(0, normalizedLimit)
113
+ .map((job) => ({
114
+ id: String(job.id),
115
+ queue: job.queue,
116
+ payload: job.payload,
117
+ state: job.state,
118
+ attempt: job.attempt,
119
+ availableAt: new Date(job.availableAt),
120
+ leaseUntil: job.leaseUntil === null ? null : new Date(job.leaseUntil),
121
+ dedupeKey: job.dedupeKey,
122
+ lastError: job.lastError,
123
+ createdAt: new Date(job.createdAt),
124
+ updatedAt: new Date(job.updatedAt),
125
+ }));
126
+ }
127
+ async size(queueName) {
128
+ const queue = normalizeQueueName(queueName);
129
+ return this.queues.get(queue)?.jobs.size ?? 0;
130
+ }
131
+ remove(job) {
132
+ this.jobs.delete(job.id);
133
+ const index = this.queues.get(job.queue);
134
+ if (!index) {
135
+ return;
136
+ }
137
+ index.jobs.delete(job.id);
138
+ if (job.dedupeKey !== null) {
139
+ index.dedupe.delete(job.dedupeKey);
140
+ }
141
+ if (index.jobs.size === 0) {
142
+ this.queues.delete(job.queue);
143
+ }
144
+ }
145
+ requireLeased(id, attempt) {
146
+ const job = this.jobs.get(normalizeJobId(id));
147
+ if (!job || job.state !== 'leased' || (attempt !== null && job.attempt !== attempt)) {
148
+ throw new QueueError('FLOWFN_QUEUE_JOB_NOT_LEASED');
149
+ }
150
+ return job;
151
+ }
152
+ }
153
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { QueueSqlDialect } from './sql-client.js';
2
+ export declare const DEFAULT_QUEUE_JOBS_TABLE = "queue_jobs";
3
+ export declare function quoteQueueTableName(tableName: string): string;
4
+ /**
5
+ * DDL for the jobs table, one statement per entry, safe to re-run.
6
+ *
7
+ * Times are epoch milliseconds. `id` never repeats (AUTOINCREMENT / identity),
8
+ * so a stale id cannot address a later job. The partial unique index makes a
9
+ * `dedupe_key` unique only among pending and leased jobs.
10
+ */
11
+ export declare function queueJobsMigrationStatements(dialect: QueueSqlDialect, tableName?: string): string[];
12
+ /** The migration as one SQL script, matching the files shipped under `migrations/`. */
13
+ export declare function queueJobsMigrationSql(dialect: QueueSqlDialect, tableName?: string): string;
14
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAIrD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,eAAe,EACxB,SAAS,GAAE,MAAiC,GAC3C,MAAM,EAAE,CA4BV;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,GAAE,MAAiC,GAAG,MAAM,CAEpH"}
@@ -0,0 +1,48 @@
1
+ import { QueueError } from './validation.js';
2
+ export const DEFAULT_QUEUE_JOBS_TABLE = 'queue_jobs';
3
+ const TABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,47}$/;
4
+ export function quoteQueueTableName(tableName) {
5
+ if (typeof tableName !== 'string' || !TABLE_NAME_RE.test(tableName)) {
6
+ throw new QueueError('FLOWFN_QUEUE_TABLE_NAME_INVALID');
7
+ }
8
+ return `"${tableName}"`;
9
+ }
10
+ /**
11
+ * DDL for the jobs table, one statement per entry, safe to re-run.
12
+ *
13
+ * Times are epoch milliseconds. `id` never repeats (AUTOINCREMENT / identity),
14
+ * so a stale id cannot address a later job. The partial unique index makes a
15
+ * `dedupe_key` unique only among pending and leased jobs.
16
+ */
17
+ export function queueJobsMigrationStatements(dialect, tableName = DEFAULT_QUEUE_JOBS_TABLE) {
18
+ const table = quoteQueueTableName(tableName);
19
+ const index = (suffix) => `"${tableName}_${suffix}"`;
20
+ const id = dialect === 'postgres'
21
+ ? 'id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY'
22
+ : 'id INTEGER PRIMARY KEY AUTOINCREMENT';
23
+ const payload = dialect === 'postgres' ? 'payload JSON NOT NULL' : 'payload TEXT NOT NULL';
24
+ const time = dialect === 'postgres' ? 'BIGINT' : 'INTEGER';
25
+ return [
26
+ `CREATE TABLE IF NOT EXISTS ${table} (
27
+ ${id},
28
+ queue TEXT NOT NULL,
29
+ ${payload},
30
+ state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'leased', 'dead')),
31
+ attempt INTEGER NOT NULL DEFAULT 0,
32
+ available_at ${time} NOT NULL,
33
+ lease_until ${time},
34
+ dedupe_key TEXT,
35
+ last_error TEXT,
36
+ created_at ${time} NOT NULL,
37
+ updated_at ${time} NOT NULL
38
+ )`,
39
+ `CREATE INDEX IF NOT EXISTS ${index('available_idx')} ON ${table} (queue, state, available_at, id)`,
40
+ `CREATE INDEX IF NOT EXISTS ${index('lease_idx')} ON ${table} (queue, state, lease_until)`,
41
+ `CREATE UNIQUE INDEX IF NOT EXISTS ${index('dedupe_idx')} ON ${table} (queue, dedupe_key) WHERE dedupe_key IS NOT NULL AND state IN ('pending', 'leased')`,
42
+ ];
43
+ }
44
+ /** The migration as one SQL script, matching the files shipped under `migrations/`. */
45
+ export function queueJobsMigrationSql(dialect, tableName = DEFAULT_QUEUE_JOBS_TABLE) {
46
+ return `${queueJobsMigrationStatements(dialect, tableName).join(';\n\n')};\n`;
47
+ }
48
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,aAAa,GAAG,+BAA+B,CAAC;AAEtD,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,UAAU,CAAC,iCAAiC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,SAAS,GAAG,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAwB,EACxB,YAAoB,wBAAwB;IAE5C,MAAM,KAAK,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC;IAC7D,MAAM,EAAE,GACN,OAAO,KAAK,UAAU;QACpB,CAAC,CAAC,oDAAoD;QACtD,CAAC,CAAC,sCAAsC,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAC3F,MAAM,IAAI,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3D,OAAO;QACL,8BAA8B,KAAK;IACnC,EAAE;;IAEF,OAAO;;;iBAGM,IAAI;gBACL,IAAI;;;eAGL,IAAI;eACJ,IAAI;EACjB;QACE,8BAA8B,KAAK,CAAC,eAAe,CAAC,OAAO,KAAK,mCAAmC;QACnG,8BAA8B,KAAK,CAAC,WAAW,CAAC,OAAO,KAAK,8BAA8B;QAC1F,qCAAqC,KAAK,CAAC,YAAY,CAAC,OAAO,KAAK,sFAAsF;KAC3J,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,qBAAqB,CAAC,OAAwB,EAAE,YAAoB,wBAAwB;IAC1G,OAAO,GAAG,4BAA4B,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;AAChF,CAAC"}
@@ -0,0 +1,63 @@
1
+ export type QueueSqlDialect = 'sqlite' | 'postgres';
2
+ export type QueueSqlValue = string | number | null;
3
+ /**
4
+ * The SQL primitive `DatabaseQueueAdapter` runs on. It has the shape of the
5
+ * `rawSql` executor on `@superfunctions/db` SQL adapters: each `?` marks one
6
+ * positional parameter, which the client renders for its dialect. The
7
+ * adapter's statements contain no other `?`.
8
+ */
9
+ export interface QueueSqlClient {
10
+ readonly dialect: QueueSqlDialect;
11
+ /** Runs a statement that returns rows (`SELECT`, `… RETURNING`). */
12
+ query(sql: string, params: readonly QueueSqlValue[]): Promise<Record<string, unknown>[]>;
13
+ /** Runs a statement without a result set (the migration DDL). */
14
+ execute(sql: string, params: readonly QueueSqlValue[]): Promise<void>;
15
+ }
16
+ /**
17
+ * Structural form of a `@superfunctions/db` adapter's optional `rawSql`
18
+ * executor. It is declared here, not imported, because `@superfunctions/db`
19
+ * is an optional peer and the published declarations must not reference it.
20
+ */
21
+ export interface QueueRawSqlExecutor {
22
+ readonly dialect: 'postgres' | 'mysql' | 'sqlite';
23
+ query(sql: string, params?: readonly QueueSqlValue[]): Promise<Record<string, unknown>[]>;
24
+ execute(sql: string, params?: readonly QueueSqlValue[]): Promise<void>;
25
+ }
26
+ /** Any `@superfunctions/db` adapter, seen only through its `rawSql`. */
27
+ export interface QueueRawSqlSource {
28
+ readonly rawSql?: QueueRawSqlExecutor;
29
+ }
30
+ /** Structural subset of a better-sqlite3 `Database`. */
31
+ export interface BetterSqlite3DatabaseLike {
32
+ prepare(sql: string): {
33
+ all(...params: unknown[]): unknown[];
34
+ run(...params: unknown[]): unknown;
35
+ };
36
+ }
37
+ /** Wraps a better-sqlite3 database (SQLite >= 3.35 for `RETURNING`). */
38
+ export declare function createSqliteQueueClient(db: BetterSqlite3DatabaseLike): QueueSqlClient;
39
+ /** Structural subset of a Cloudflare `D1Database`. */
40
+ export interface D1DatabaseLike {
41
+ prepare(sql: string): {
42
+ bind(...values: unknown[]): {
43
+ all(): Promise<{
44
+ results?: unknown[];
45
+ }>;
46
+ run(): Promise<unknown>;
47
+ };
48
+ };
49
+ }
50
+ /** Wraps a Cloudflare D1 binding. */
51
+ export declare function createD1QueueClient(db: D1DatabaseLike): QueueSqlClient;
52
+ /** Structural subset of a node-postgres `Pool`/`Client`, also satisfied by PGlite. */
53
+ export interface PostgresQueryableLike {
54
+ query(text: string, values?: unknown[]): Promise<{
55
+ rows: unknown[];
56
+ }>;
57
+ }
58
+ /**
59
+ * Wraps a Postgres connection or pool. With a pool, every statement may run
60
+ * on a different connection; the adapter never relies on session state.
61
+ */
62
+ export declare function createPostgresQueueClient(db: PostgresQueryableLike): QueueSqlClient;
63
+ //# sourceMappingURL=sql-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-client.d.ts","sourceRoot":"","sources":["../src/sql-client.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEpD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oEAAoE;IACpE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACzF,iEAAiE;IACjE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAClD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1F,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;CACvC;AAED,wDAAwD;AACxD,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QACrC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;KACpC,CAAC;CACH;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,yBAAyB,GAAG,cAAc,CAUrF;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,IAAI,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG;YAC1B,GAAG,IAAI,OAAO,CAAC;gBAAE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;aAAE,CAAC,CAAC;YACxC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;SACzB,CAAC;KACH,CAAC;CACH;AAED,qCAAqC;AACrC,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,cAAc,CAWtE;AAED,sFAAsF;AACtF,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;CACvE;AAOD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,qBAAqB,GAAG,cAAc,CAWnF"}
@@ -0,0 +1,46 @@
1
+ /** Wraps a better-sqlite3 database (SQLite >= 3.35 for `RETURNING`). */
2
+ export function createSqliteQueueClient(db) {
3
+ return {
4
+ dialect: 'sqlite',
5
+ async query(sql, params) {
6
+ return db.prepare(sql).all(...params);
7
+ },
8
+ async execute(sql, params) {
9
+ db.prepare(sql).run(...params);
10
+ },
11
+ };
12
+ }
13
+ /** Wraps a Cloudflare D1 binding. */
14
+ export function createD1QueueClient(db) {
15
+ return {
16
+ dialect: 'sqlite',
17
+ async query(sql, params) {
18
+ const result = await db.prepare(sql).bind(...params).all();
19
+ return (result.results ?? []);
20
+ },
21
+ async execute(sql, params) {
22
+ await db.prepare(sql).bind(...params).run();
23
+ },
24
+ };
25
+ }
26
+ function numberPlaceholders(sql) {
27
+ let index = 0;
28
+ return sql.replace(/\?/g, () => `$${++index}`);
29
+ }
30
+ /**
31
+ * Wraps a Postgres connection or pool. With a pool, every statement may run
32
+ * on a different connection; the adapter never relies on session state.
33
+ */
34
+ export function createPostgresQueueClient(db) {
35
+ return {
36
+ dialect: 'postgres',
37
+ async query(sql, params) {
38
+ const result = await db.query(numberPlaceholders(sql), [...params]);
39
+ return result.rows;
40
+ },
41
+ async execute(sql, params) {
42
+ await db.query(numberPlaceholders(sql), [...params]);
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=sql-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-client.js","sourceRoot":"","sources":["../src/sql-client.ts"],"names":[],"mappings":"AA0CA,wEAAwE;AACxE,MAAM,UAAU,uBAAuB,CAAC,EAA6B;IACnE,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM;YACrB,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAA8B,CAAC;QACrE,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;YACvB,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC;AAYD,qCAAqC;AACrC,MAAM,UAAU,mBAAmB,CAAC,EAAkB;IACpD,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM;YACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;YAC3D,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAA8B,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;YACvB,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC;AAOD,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAyB;IACjE,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM;YACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YACpE,OAAO,MAAM,CAAC,IAAiC,CAAC;QAClD,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;YACvB,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,71 @@
1
+ /** Lifecycle state of a stored job. Acknowledged jobs are removed, so they have no state. */
2
+ export type QueueJobState = 'pending' | 'leased' | 'dead';
3
+ export interface EnqueueOptions {
4
+ /** Milliseconds to wait before the job becomes claimable. Defaults to 0. */
5
+ delayMs?: number;
6
+ /**
7
+ * While a job with the same key is pending or leased in the same queue, a
8
+ * second enqueue is a no-op that returns the existing job's id.
9
+ */
10
+ dedupeKey?: string;
11
+ }
12
+ export interface EnqueueResult {
13
+ id: string;
14
+ /** True when the enqueue was a no-op because of `dedupeKey`. */
15
+ deduplicated: boolean;
16
+ }
17
+ export interface ClaimOptions {
18
+ /** How long the claimer holds the job before it becomes claimable again. */
19
+ leaseMs: number;
20
+ }
21
+ export interface ClaimedJob<TPayload> {
22
+ id: string;
23
+ payload: TPayload;
24
+ /** 1 on the first claim; incremented by every later claim, including one after an expired lease. */
25
+ attempt: number;
26
+ }
27
+ /**
28
+ * Optional fence for `ack`, `fail` and `extendLease`. When `attempt` is the
29
+ * value returned by `claim`, the call only succeeds while that same claim
30
+ * still holds the job, so a claimer whose lease expired and was reclaimed
31
+ * cannot act on the new claimer's lease.
32
+ */
33
+ export interface LeaseFence {
34
+ attempt?: number;
35
+ }
36
+ export type FailOptions = ({
37
+ retryAt: Date | number;
38
+ reason?: string;
39
+ } & LeaseFence) | ({
40
+ deadLetter: true;
41
+ reason: string;
42
+ } & LeaseFence);
43
+ export interface QueueJob<TPayload> {
44
+ id: string;
45
+ queue: string;
46
+ payload: TPayload;
47
+ state: QueueJobState;
48
+ attempt: number;
49
+ availableAt: Date;
50
+ leaseUntil: Date | null;
51
+ dedupeKey: string | null;
52
+ lastError: string | null;
53
+ createdAt: Date;
54
+ updatedAt: Date;
55
+ }
56
+ export interface QueueAdapter<TPayload> {
57
+ enqueue(queueName: string, payload: TPayload, options?: EnqueueOptions): Promise<EnqueueResult>;
58
+ /** Atomically leases the next available job, or resolves `null` when none is available. */
59
+ claim(queueName: string, options: ClaimOptions): Promise<ClaimedJob<TPayload> | null>;
60
+ /** Completes a leased job and removes it. Rejects with `FLOWFN_QUEUE_JOB_NOT_LEASED` otherwise. */
61
+ ack(id: string, fence?: LeaseFence): Promise<void>;
62
+ /** Releases a leased job for a retry at `retryAt`, or moves it to the dead-letter state. */
63
+ fail(id: string, options: FailOptions): Promise<void>;
64
+ /** Sets the lease of a leased job to expire `leaseMs` from now. */
65
+ extendLease(id: string, leaseMs: number, fence?: LeaseFence): Promise<void>;
66
+ /** Lists up to `limit` pending or leased jobs in claim order without changing them. */
67
+ peek(queueName: string, limit: number): Promise<QueueJob<TPayload>[]>;
68
+ /** Counts pending and leased jobs. */
69
+ size(queueName: string): Promise<number>;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU,CAAC,QAAQ;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,QAAQ,CAAC;IAClB,oGAAoG;IACpG,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GACnB,CAAC;IAAE,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,GAC1D,CAAC;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,CAAC;AAExD,MAAM,WAAW,QAAQ,CAAC,QAAQ;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,QAAQ,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,YAAY,CAAC,QAAQ;IACpC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAChG,2FAA2F;IAC3F,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;IACtF,mGAAmG;IACnG,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,4FAA4F;IAC5F,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,mEAAmE;IACnE,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,uFAAuF;IACvF,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtE,sCAAsC;IACtC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}