@coderule/qulite
Version:
SQLite queue for outbox/synchronization: UPSERT, dedup, leases, transactions.
169 lines (163 loc) • 4.58 kB
TypeScript
import Database from 'better-sqlite3';
declare enum JobStatus {
Pending = 0,
Processing = 1,
Done = 2,
Failed = 3
}
type FsEventKind = 'add' | 'modify' | 'unlink' | 'move' | 'snapshot' | 'heartbeat';
interface Logger {
error(message: string, ...meta: unknown[]): void;
warn(message: string, ...meta: unknown[]): void;
info(message: string, ...meta: unknown[]): void;
debug(message: string, ...meta: unknown[]): void;
}
interface PersistedJob {
id: number;
type: string;
status: JobStatus;
priority: number;
run_after: number;
created_at: number;
updated_at: number;
attempts: number;
max_attempts: number;
lease_owner?: string | null;
lease_expires_at?: number | null;
last_error?: string | null;
done_at?: number | null;
failed_at?: number | null;
dedupe_key?: string | null;
root_id?: string | null;
rel_path?: string | null;
kind?: FsEventKind | null;
to_path?: string | null;
size?: number | null;
mtime_ns?: number | null;
sha256?: string | null;
data?: string | null;
}
interface QuliteOptions {
logger?: Logger;
defaultLeaseMs?: number;
defaultMaxAttempts?: number;
busyTimeoutMs?: number;
}
interface ClaimOptions {
type?: string;
now?: number;
leaseOwner?: string;
leaseMs?: number;
}
type Ack = {
kind: 'ack';
};
type Retry = {
kind: 'retry';
delayMs?: number;
};
type Fail = {
kind: 'fail';
error?: string;
};
type ProcessResult = Ack | Retry | Fail;
declare const nullLogger: Logger;
declare class Qulite {
private db;
private defaultLeaseMs;
private defaultMaxAttempts;
private stmtInsertFsEvent;
private stmtInsertGeneric;
private stmtSelectPendingAny;
private stmtSelectPendingByType;
private stmtUpdateClaimById;
private stmtGetById;
private stmtAckByOwner;
private stmtFailByOwner;
private stmtNackByOwner;
private stmtGetAttemptsById;
private stmtRequeueTimedOutUnderLimit;
private stmtFailTimedOutAtLimit;
private stmtCountByStatus;
private stmtDeleteDoneOlder;
private stmtDeleteFailedOlder;
constructor(db: Database.Database, opts?: QuliteOptions);
private ensureSchema;
private prepareStatements;
upsertFsEvent(params: {
root_id: string;
rel_path: string;
kind: 'add' | 'modify' | 'unlink' | 'move';
to_path?: string | null;
size?: number | null;
mtime_ns?: number | null;
sha256?: string | null;
priority?: number;
delayMs?: number;
data?: unknown;
maxAttempts?: number;
}): {
id?: number;
changes: number;
};
upsertGeneric(params: {
type: string;
dedupe_key: string;
data?: unknown;
priority?: number;
delayMs?: number;
maxAttempts?: number;
}): {
id?: number;
changes: number;
};
claimNext(opts?: ClaimOptions): PersistedJob | undefined;
ack(id: number, leaseOwner: string): boolean;
fail(id: number, leaseOwner: string, error?: string): boolean;
retry(id: number, leaseOwner: string, delayMs?: number): boolean;
requeueTimedOut(): number;
countByStatus(status: JobStatus): number;
cleanupDone(olderThanMs: number): number;
cleanupFailed(olderThanMs: number): number;
}
interface WorkerOptions {
queue: Qulite;
type?: string;
pollIntervalMs?: number;
logger?: Logger;
processor: (job: PersistedJob, ctx: {
leaseOwner: string;
ack: () => boolean;
fail: (err?: string) => boolean;
retry: (delayMs?: number) => boolean;
}) => Promise<ProcessResult> | ProcessResult;
}
declare class Worker {
private queue;
private type?;
private poll;
private log;
private processor;
private leaseOwner;
private running;
constructor(opts: WorkerOptions);
start(): Promise<void>;
stop(): void;
}
declare function enqueueFsEvent(q: Qulite, params: {
root_id: string;
rel_path: string;
kind: FsEventKind;
to_path?: string | null;
size?: number | null;
mtime_ns?: number | null;
sha256?: string | null;
priority?: number;
delayMs?: number;
data?: unknown;
maxAttempts?: number;
}): {
id?: number;
changes: number;
};
export { type Ack, type ClaimOptions, type Fail, type FsEventKind, JobStatus, type Logger, type PersistedJob, type ProcessResult, Qulite, type QuliteOptions, type Retry, Worker, type WorkerOptions, enqueueFsEvent, nullLogger };