feat: centralize application framework primitives
Quality / quality (ubuntu-latest) (push) Failing after 14m38s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-22 23:07:46 +05:30
parent 96e082b943
commit a3ddd39b7b
73 changed files with 1429 additions and 84 deletions
+25 -4
View File
@@ -14,6 +14,8 @@ export interface QueueStore {
remove(id: string): Promise<void>;
due(now: number, limit: number): Promise<Job[]>;
list(name?: string): Promise<Job[]>;
findByIdempotencyKey?(name: string, key: string): Promise<Job | null>;
size?(): Promise<number>;
claim?(id: string, worker: string, leaseUntil: number): Promise<boolean>;
}
@@ -48,6 +50,15 @@ export function memoryQueueStore(): QueueStore {
.filter((job) => !name || job.name === name)
.map((job) => structuredClone(job));
},
async findByIdempotencyKey(name, key) {
const job = [...jobs.values()].find(
(candidate) => candidate.name === name && candidate.idempotencyKey === key,
);
return job ? structuredClone(job) : null;
},
async size() {
return jobs.size;
},
};
}
@@ -73,6 +84,10 @@ export interface DurableQueue {
list(name?: string): Promise<Job[]>;
failed(): Job[];
retry(id: string): Promise<boolean>;
addBatch<T>(
name: string,
entries: readonly { data: T; options?: AddOptions }[],
): Promise<Job<T>[]>;
}
function positiveInteger(value: number, label: string): number {
@@ -178,12 +193,12 @@ export function createDurableQueue(options: DurableQueueOptions = {}): DurableQu
}
if (add.idempotencyKey) {
const existing = (await store.list(name)).find(
(job) => job.idempotencyKey === add.idempotencyKey,
);
const existing = store.findByIdempotencyKey
? await store.findByIdempotencyKey(name, add.idempotencyKey)
: (await store.list(name)).find((job) => job.idempotencyKey === add.idempotencyKey);
if (existing) return existing as Job<T>;
}
if ((await store.list()).length >= capacity) {
if ((store.size ? await store.size() : (await store.list()).length) >= capacity) {
throw new Error(`WRN-QUEUE-CAPACITY: queue capacity of ${capacity} reached`);
}
@@ -204,6 +219,12 @@ export function createDurableQueue(options: DurableQueueOptions = {}): DurableQu
return structuredClone(job);
},
async addBatch<T>(name: string, entries: readonly { data: T; options?: AddOptions }[]) {
const output: Job<T>[] = [];
for (const entry of entries) output.push(await this.add(name, entry.data, entry.options));
return output;
},
process(name, handler) {
if (!name.trim()) throw new TypeError("queue worker name cannot be empty");
handlers.set(name, handler as JobHandler);