import { TaskCanceled, TaskFailed } from "./errors.js"; import { isFailed, isSucceeded, type Task } from "./models.js"; import { SQLiteStore } from "./store/sqlite.js"; import { PostgresStore } from "./store/postgres.js"; import type { ListInput, SubmitInput, TaskStore } from "./store/base.js"; import { type TaskDef, taskName } from "./task.js"; import { pollWait } from "./wait.js"; export type SubmitOptions = Omit; export interface CallOptions extends SubmitOptions { waitTimeoutMs?: number; pollMs?: number; } /** API-side handle. Thin wrapper over a TaskStore + SDK-orchestrated wait/call. */ export class CairnQ { constructor(private readonly _store: TaskStore) {} static sqlite(path: string, opts?: { busyTimeoutMs?: number }): CairnQ { return new CairnQ(new SQLiteStore(path, opts)); } /** Multi-host backend. `dsn` is a libpq connection string; requires the * optional `pg` package. */ static postgres(dsn: string, opts?: { max?: number }): CairnQ { return new CairnQ(new PostgresStore(dsn, opts)); } get store(): TaskStore { return this._store; } connect(): Promise { return this._store.connect(); } close(): Promise { return this._store.close(); } submit(name: string, payload?: unknown, opts?: SubmitOptions): Promise; submit(task: TaskDef, payload?: P, opts?: SubmitOptions): Promise; submit(task: string | TaskDef, payload?: unknown, opts: SubmitOptions = {}): Promise { return this._store.submit({ name: taskName(task), payload, ...opts }); } get(taskId: string): Promise { return this._store.get(taskId); } getByKey(key: string): Promise { return this._store.getByKey(key); } list(input?: ListInput): Promise { return this._store.list(input); } cancel(taskId: string): Promise { return this._store.cancel(taskId); } cancelByKey(key: string): Promise { return this._store.cancelByKey(key); } retry(taskId: string, opts?: { resetAttempt?: boolean }): Promise { return this._store.retry(taskId, opts); } retryByKey(key: string, opts?: { resetAttempt?: boolean }): Promise { return this._store.retryByKey(key, opts); } wait( taskId: string, opts: { timeoutMs?: number; pollMs?: number } = {}, ): Promise { return pollWait(this._store, taskId, { timeoutMs: opts.timeoutMs ?? 30_000, pollMs: opts.pollMs, }); } /** submit + wait. Resolves with the result on success; rejects with * TaskFailed / TaskCanceled / TaskTimeout otherwise. Pass a TaskDef and the * resolved value is typed as its Result. */ async call(name: string, payload?: unknown, opts?: CallOptions): Promise; async call(task: TaskDef, payload?: P, opts?: CallOptions): Promise; async call(task: string | TaskDef, payload?: unknown, opts: CallOptions = {}): Promise { const { waitTimeoutMs = 30_000, pollMs, ...submit } = opts; const created = await this.submit(taskName(task), payload, submit); const final = await pollWait(this._store, created.id, { timeoutMs: waitTimeoutMs, pollMs }); if (isSucceeded(final)) return final.result; if (isFailed(final)) throw new TaskFailed(final.error); throw new TaskCanceled(final.id); } }