@qkitt/queue 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.
Files changed (38) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +723 -0
  3. package/dist/config/config-freeze.util.d.ts +7 -0
  4. package/dist/config/from-config.d.ts +33 -0
  5. package/dist/config/index.d.ts +4 -0
  6. package/dist/config/parse.util.d.ts +12 -0
  7. package/dist/config/store-resolve.util.d.ts +11 -0
  8. package/dist/config/types.d.ts +205 -0
  9. package/dist/config/validate.d.ts +36 -0
  10. package/dist/events/index.d.ts +48 -0
  11. package/dist/index.d.ts +7 -0
  12. package/dist/index.js +1552 -0
  13. package/dist/persist/index.d.ts +3 -0
  14. package/dist/persist/json-codec.util.d.ts +13 -0
  15. package/dist/persist/memory.d.ts +14 -0
  16. package/dist/persist/web-storage-access.util.d.ts +10 -0
  17. package/dist/persist/web-storage.d.ts +59 -0
  18. package/dist/queue/core/forward.util.d.ts +21 -0
  19. package/dist/queue/core/layers.util.d.ts +13 -0
  20. package/dist/queue/core/queue.d.ts +66 -0
  21. package/dist/queue/index.d.ts +7 -0
  22. package/dist/queue/persist/create-id.util.d.ts +10 -0
  23. package/dist/queue/persist/hydrate-gate.util.d.ts +9 -0
  24. package/dist/queue/persist/persist.support.d.ts +19 -0
  25. package/dist/queue/persist/persist.types.d.ts +23 -0
  26. package/dist/queue/persist/row-ids.util.d.ts +14 -0
  27. package/dist/queue/persist/with-row-persist.d.ts +72 -0
  28. package/dist/queue/persist/with-snapshot-persist.d.ts +45 -0
  29. package/dist/queue/persist/write-chain.util.d.ts +12 -0
  30. package/dist/queue/worker/with-worker.d.ts +56 -0
  31. package/dist/router/index.d.ts +3 -0
  32. package/dist/router/match.util.d.ts +24 -0
  33. package/dist/router/router.d.ts +107 -0
  34. package/dist/worker/index.d.ts +4 -0
  35. package/dist/worker/pipeline.d.ts +22 -0
  36. package/dist/worker/retry.d.ts +31 -0
  37. package/dist/worker/types.d.ts +5 -0
  38. package/package.json +64 -0
@@ -0,0 +1,3 @@
1
+ export { createMemoryRowStore, createMemorySnapshotStore, type MemoryRowStore, type MemorySnapshotStore, } from './memory';
2
+ export { createLocalStorageRowStore, createLocalStorageSnapshotStore, createSessionStorageRowStore, createSessionStorageSnapshotStore, createWebRowStore, createWebSnapshotStore, StorageCodecError, type JsonCodec, type WebRowStoreOptions, type WebSnapshotStoreOptions, type WebStorageLike, } from './web-storage';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ export type JsonCodec<T> = {
2
+ serialize: (value: T) => string;
3
+ deserialize: (raw: string) => T;
4
+ };
5
+ /** Thrown when storage JSON/codec deserialize fails (corrupt or hostile data). */
6
+ export declare class StorageCodecError extends Error {
7
+ readonly name = "StorageCodecError";
8
+ readonly cause: unknown;
9
+ constructor(message: string, cause?: unknown);
10
+ }
11
+ export declare const defaultJsonCodec: <T>() => JsonCodec<T>;
12
+ export declare const decodeWithCodec: <T>(label: string, raw: string, deserialize: (raw: string) => T) => T;
13
+ //# sourceMappingURL=json-codec.util.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { RowRecord, RowStore, SnapshotStore } from '../queue/persist/persist.types';
2
+ export type MemorySnapshotStore<T> = SnapshotStore<T> & {
3
+ /** Live in-memory snapshot (mutated by `save`). */
4
+ readonly data: T[];
5
+ };
6
+ export type MemoryRowStore<T> = RowStore<T> & {
7
+ /** Live rows head → tail (mutated by insert/remove/clear). */
8
+ readonly rows: RowRecord<T>[];
9
+ };
10
+ /** In-process snapshot store. Useful for tests and non-durable queues. */
11
+ export declare const createMemorySnapshotStore: <T>(initial?: readonly T[]) => MemorySnapshotStore<T>;
12
+ /** In-process row store with stable ids. */
13
+ export declare const createMemoryRowStore: <T>(initial?: readonly RowRecord<T>[]) => MemoryRowStore<T>;
14
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1,10 @@
1
+ /** Minimal Web Storage surface (`localStorage` / `sessionStorage` / mocks). */
2
+ export type WebStorageLike = {
3
+ getItem: (key: string) => string | null;
4
+ setItem: (key: string, value: string) => void;
5
+ removeItem: (key: string) => void;
6
+ };
7
+ /** Lazy proxy so storage is resolved on each call (SSR / late availability). */
8
+ export declare const lazyGlobalStorage: (name: 'localStorage' | 'sessionStorage') => WebStorageLike;
9
+ export declare const resolveStorage: (storage?: WebStorageLike) => WebStorageLike;
10
+ //# sourceMappingURL=web-storage-access.util.d.ts.map
@@ -0,0 +1,59 @@
1
+ import type { RowStore, SnapshotStore } from '../queue/persist/persist.types';
2
+ import { type JsonCodec } from './json-codec.util';
3
+ import { type WebStorageLike } from './web-storage-access.util';
4
+ export { StorageCodecError, type JsonCodec } from './json-codec.util';
5
+ export type { WebStorageLike } from './web-storage-access.util';
6
+ export type WebSnapshotStoreOptions<T> = {
7
+ /** Storage key for the full JSON array snapshot. */
8
+ key: string;
9
+ /** Defaults to `globalThis.localStorage`. */
10
+ storage?: WebStorageLike;
11
+ codec?: JsonCodec<T[]>;
12
+ };
13
+ /**
14
+ * Snapshot store backed by Web Storage (localStorage / sessionStorage).
15
+ * Entire queue is one JSON value under `key`.
16
+ *
17
+ * Corrupt data throws {@link StorageCodecError}. Supply a validating `codec`
18
+ * when storage may contain untrusted or versioned payloads.
19
+ *
20
+ * **Limits (not multi-tab safe):**
21
+ * - Reads/writes are not transactional; a failed `setItem` can leave a partial
22
+ * or stale snapshot (e.g. quota exceeded).
23
+ * - Concurrent tabs race on the same key — last write wins; no locking.
24
+ * - Prefer a single tab owner, or a server-side store for shared durability.
25
+ */
26
+ export declare const createWebSnapshotStore: <T>(options: WebSnapshotStoreOptions<T>) => SnapshotStore<T>;
27
+ export type WebRowStoreOptions<T> = {
28
+ /**
29
+ * Key prefix. Uses:
30
+ * - `${key}:order` → id list head → tail
31
+ * - `${key}:row:${id}` → serialized item
32
+ */
33
+ key: string;
34
+ storage?: WebStorageLike;
35
+ itemCodec?: JsonCodec<T>;
36
+ };
37
+ /**
38
+ * Row-level store on Web Storage.
39
+ * Each job is its own key; order is a separate id list (true row ops).
40
+ *
41
+ * Corrupt order/row payloads throw {@link StorageCodecError}.
42
+ * Prefer a validating `itemCodec` for untrusted storage.
43
+ *
44
+ * **Limits (not multi-tab safe):**
45
+ * - `insert` / `remove` / `clear` are multi-key and not atomic — a crash or
46
+ * quota error mid-op can leave order list and row keys inconsistent.
47
+ * - Concurrent tabs race on the same prefix; last writer wins without merge.
48
+ * - Use one owning tab, or a real DB/backend when durability must be shared.
49
+ */
50
+ export declare const createWebRowStore: <T>(options: WebRowStoreOptions<T>) => RowStore<T>;
51
+ /** Convenience: snapshot store on `localStorage` (resolved lazily on use). */
52
+ export declare const createLocalStorageSnapshotStore: <T>(key: string, options?: Omit<WebSnapshotStoreOptions<T>, 'key' | 'storage'>) => SnapshotStore<T>;
53
+ /** Convenience: row store on `localStorage` (resolved lazily on use). */
54
+ export declare const createLocalStorageRowStore: <T>(key: string, options?: Omit<WebRowStoreOptions<T>, 'key' | 'storage'>) => RowStore<T>;
55
+ /** Convenience: snapshot store on `sessionStorage` (resolved lazily on use). */
56
+ export declare const createSessionStorageSnapshotStore: <T>(key: string, options?: Omit<WebSnapshotStoreOptions<T>, 'key' | 'storage'>) => SnapshotStore<T>;
57
+ /** Convenience: row store on `sessionStorage` (resolved lazily on use). */
58
+ export declare const createSessionStorageRowStore: <T>(key: string, options?: Omit<WebRowStoreOptions<T>, 'key' | 'storage'>) => RowStore<T>;
59
+ //# sourceMappingURL=web-storage.d.ts.map
@@ -0,0 +1,21 @@
1
+ import type { EventMap } from '../../events';
2
+ import type { Queue } from './queue';
3
+ /**
4
+ * Build a queue decorator surface: start from `queue` (keeps stacked extras
5
+ * such as `hydrate` / `flush`), then overlay `extra` (overrides win).
6
+ *
7
+ * Prefer this over re-listing every {@link Queue} method so new base methods
8
+ * and inner-wrapper APIs flow through automatically (OCP).
9
+ *
10
+ * Non-enumerable layer brands from `queue` are reapplied on the result
11
+ * (object spread does not copy non-enumerable symbols).
12
+ */
13
+ export declare const forwardQueue: <TQueue extends object, TExtra extends object>(queue: TQueue, extra: TExtra) => Omit<TQueue, keyof TExtra> & TExtra;
14
+ /** Keys that are part of the core {@link Queue} contract (not decorator extras). */
15
+ export type QueueCoreKeys = keyof Queue<unknown, EventMap>;
16
+ /**
17
+ * Preserve non-core methods from an inner queue when typing an outer decorator.
18
+ * e.g. `withWorker(withRowPersist(...))` keeps `flush` / `hydrate` / `rowIds`.
19
+ */
20
+ export type PreserveQueueExtras<TQueue extends object> = Omit<TQueue, QueueCoreKeys>;
21
+ //# sourceMappingURL=forward.util.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Decorator layer brands for composition guards.
3
+ * Uses `Symbol.for` so checks remain valid across duplicate package copies.
4
+ */
5
+ export declare const WORKER_LAYER: unique symbol;
6
+ export declare const PERSIST_LAYER: unique symbol;
7
+ export type QueueLayerBrand = typeof WORKER_LAYER | typeof PERSIST_LAYER;
8
+ /** Non-enumerable brand on a queue decorator object (idempotent). */
9
+ export declare const markQueueLayer: <T extends object>(queue: T, layer: QueueLayerBrand) => T;
10
+ export declare const hasQueueLayer: (queue: object, layer: QueueLayerBrand) => boolean;
11
+ /** Copy known layer brands from an inner queue onto an outer decorator object. */
12
+ export declare const copyQueueLayers: <T extends object>(from: object, to: T) => T;
13
+ //# sourceMappingURL=layers.util.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { type EventEmitter, type EventMap, type MergeEventMaps } from '../../events';
2
+ export type QueueEvents<T> = {
3
+ /** Fired after an item is added to the tail. */
4
+ 'queue:enqueued': {
5
+ item: T;
6
+ size: number;
7
+ };
8
+ /** Fired after an item is removed from the head. */
9
+ 'queue:dequeued': {
10
+ item: T;
11
+ size: number;
12
+ };
13
+ /** Fired when the last item is dequeued (queue becomes empty). */
14
+ 'queue:emptied': undefined;
15
+ /** Fired after clear() removes all items. */
16
+ 'queue:cleared': {
17
+ removed: number;
18
+ };
19
+ };
20
+ export type Queue<T, TEvents extends EventMap = QueueEvents<T>> = {
21
+ /** Add an item to the tail (FIFO). Throws {@link QueueFullError} when at `maxSize`. */
22
+ enqueue: (item: T) => void;
23
+ /** Remove and return the head item, or `undefined` if empty. */
24
+ dequeue: () => T | undefined;
25
+ /** Return the head item without removing it. */
26
+ peek: () => T | undefined;
27
+ /** Current number of items. */
28
+ size: () => number;
29
+ /** Whether the queue has no items. */
30
+ isEmpty: () => boolean;
31
+ /** Remove all items and emit `queue:cleared`. */
32
+ clear: () => void;
33
+ /**
34
+ * Replace all items without emitting queue events.
35
+ * Used by persist hydrate/rollback so workers are not mid-stream during rebuild.
36
+ * Throws {@link QueueFullError} when `items.length` exceeds `maxSize`.
37
+ */
38
+ replaceAll: (items: readonly T[]) => void;
39
+ /** Snapshot of items from head to tail (does not mutate). */
40
+ toArray: () => T[];
41
+ on: EventEmitter<TEvents>['on'];
42
+ once: EventEmitter<TEvents>['once'];
43
+ off: EventEmitter<TEvents>['off'];
44
+ /** Emit an event (built-in or added via expand). */
45
+ emit: EventEmitter<TEvents>['emit'];
46
+ /**
47
+ * Widen the queue event map with additional event types.
48
+ * Same queue instance; existing listeners are preserved.
49
+ */
50
+ expand: <TExtra extends EventMap>() => Queue<T, MergeEventMaps<TEvents, TExtra>>;
51
+ };
52
+ export type BuildQueueOptions = {
53
+ /**
54
+ * Maximum items allowed in the queue.
55
+ * `enqueue` / `replaceAll` throw {@link QueueFullError} when exceeded.
56
+ */
57
+ maxSize?: number;
58
+ };
59
+ /** Thrown when enqueue/replaceAll would exceed {@link BuildQueueOptions.maxSize}. */
60
+ export declare class QueueFullError extends Error {
61
+ readonly name = "QueueFullError";
62
+ readonly maxSize: number;
63
+ constructor(maxSize: number);
64
+ }
65
+ export declare const buildQueue: <T>(options?: BuildQueueOptions) => Queue<T>;
66
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1,7 @@
1
+ export { buildQueue, QueueFullError, type BuildQueueOptions, type Queue, type QueueEvents, } from './core/queue';
2
+ export { withWorker, type QueueWithWorker, type WithWorkerOptions, type WorkerControls, type WorkerEvents, } from './worker/with-worker';
3
+ export { createId } from './persist/create-id.util';
4
+ export type { RowRecord, RowStore, SnapshotStore } from './persist/persist.types';
5
+ export { withRowPersist, type QueueWithRowPersist, type RowPersistEvents, type RowPersistOptions, } from './persist/with-row-persist';
6
+ export { withSnapshotPersist, type QueueWithSnapshotPersist, type SnapshotPersistEvents, type SnapshotPersistOptions, } from './persist/with-snapshot-persist';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Compact URL-safe ids (nanoid-style alphabet + random bytes).
3
+ * Used as the default row id factory for {@link withRowPersist}.
4
+ */
5
+ /**
6
+ * Generate a compact URL-safe id.
7
+ * Default length 21 ≈ 126 bits of entropy under `crypto.getRandomValues`.
8
+ */
9
+ export declare const createId: (size?: number) => string;
10
+ //# sourceMappingURL=create-id.util.d.ts.map
@@ -0,0 +1,9 @@
1
+ /** Suppress side effects while restoring from the store. */
2
+ export type HydrateGate = {
3
+ isSuppressing: () => boolean;
4
+ run: <R>(fn: () => Promise<R>) => Promise<R>;
5
+ };
6
+ export declare const createHydrateGate: () => HydrateGate;
7
+ /** Reject user mutations while hydrate() is replacing memory from the store. */
8
+ export declare const assertNotHydrating: (gate: HydrateGate) => void;
9
+ //# sourceMappingURL=hydrate-gate.util.d.ts.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared checks and post-hydrate hooks for queue persistence decorators.
3
+ */
4
+ /**
5
+ * Fail fast when persist is stacked incorrectly.
6
+ * Correct order: `withWorker(withRowPersist(queue, store), worker)`.
7
+ * Do not wrap a worker queue or an already-persisted queue.
8
+ */
9
+ export declare const assertBareQueueForPersist: (queue: object, wrapperName: string) => void;
10
+ /**
11
+ * After a silent hydrate rebuild, kick stacked workers that pump on
12
+ * `queue:enqueued` without re-inserting items into the store.
13
+ */
14
+ export declare const notifyQueueRestored: <T>(queue: {
15
+ size: () => number;
16
+ peek: () => T | undefined;
17
+ emit: (eventName: string, data: unknown) => void;
18
+ }) => void;
19
+ //# sourceMappingURL=persist.support.d.ts.map
@@ -0,0 +1,23 @@
1
+ /** One durable row, stable id + payload, ordered head → tail when loaded. */
2
+ export type RowRecord<T> = {
3
+ id: string;
4
+ item: T;
5
+ };
6
+ /**
7
+ * Row-level backend (SQL table, KV with per-job keys, etc.).
8
+ * `loadAll` must return rows in FIFO order (head first).
9
+ */
10
+ export type RowStore<T> = {
11
+ loadAll: () => readonly RowRecord<T>[] | Promise<readonly RowRecord<T>[]>;
12
+ insert: (record: RowRecord<T>) => void | Promise<void>;
13
+ remove: (id: string) => void | Promise<void>;
14
+ clear: () => void | Promise<void>;
15
+ };
16
+ /** Whole-queue dump/restore backend (file, redis key, etc.). */
17
+ export type SnapshotStore<T> = {
18
+ /** Load items head → tail. */
19
+ load: () => readonly T[] | Promise<readonly T[]>;
20
+ /** Replace the stored snapshot with the full queue. */
21
+ save: (items: readonly T[]) => void | Promise<void>;
22
+ };
23
+ //# sourceMappingURL=persist.types.d.ts.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parallel id list for row-persist (head at `idHead`, same FIFO order as the queue).
3
+ * Mirrors the queue's head-index buffer so dequeue stays O(1) amortized.
4
+ */
5
+ export type RowIdList = {
6
+ push: (id: string) => void;
7
+ shift: () => string | undefined;
8
+ reset: (next: readonly string[]) => void;
9
+ /** Live ids head → tail (aligned with `queue.toArray()`). */
10
+ live: () => string[];
11
+ liveCount: () => number;
12
+ };
13
+ export declare const createRowIdList: () => RowIdList;
14
+ //# sourceMappingURL=row-ids.util.d.ts.map
@@ -0,0 +1,72 @@
1
+ import { type EventMap, type MergeEventMaps } from '../../events';
2
+ import type { Queue, QueueEvents } from '../core/queue';
3
+ import type { RowStore } from './persist.types';
4
+ export { createId } from './create-id.util';
5
+ export type { RowRecord, RowStore } from './persist.types';
6
+ export type RowPersistEvents<T> = {
7
+ 'persist:loaded': {
8
+ size: number;
9
+ };
10
+ 'persist:inserted': {
11
+ id: string;
12
+ item: T;
13
+ };
14
+ 'persist:removed': {
15
+ id: string;
16
+ item: T;
17
+ };
18
+ 'persist:cleared': {
19
+ removed: number;
20
+ };
21
+ 'persist:error': {
22
+ operation: 'load' | 'insert' | 'remove' | 'clear';
23
+ error: unknown;
24
+ id?: string;
25
+ };
26
+ };
27
+ export type RowPersistOptions = {
28
+ /**
29
+ * Custom id factory for new rows (enqueue).
30
+ * Defaults to {@link createId} (nanoid-style URL-safe alphabet).
31
+ * Must return unique ids under concurrent enqueue.
32
+ *
33
+ * @example
34
+ * withRowPersist(queue, store, { createId: () => crypto.randomUUID() })
35
+ */
36
+ createId?: () => string;
37
+ };
38
+ type RowQueueEvents<T, TEvents extends EventMap> = MergeEventMaps<TEvents, RowPersistEvents<T>>;
39
+ export type QueueWithRowPersist<T, TEvents extends EventMap = RowQueueEvents<T, QueueEvents<T>>> = Queue<T, TEvents> & {
40
+ /** Replace in-memory queue from store rows (head → tail). */
41
+ hydrate: () => Promise<void>;
42
+ /** Ids currently in the queue, head → tail (aligned with `toArray()`). */
43
+ rowIds: () => string[];
44
+ /**
45
+ * Wait for pending store mutations to settle.
46
+ * Store writes are async (enqueue/dequeue stay sync); use this when you
47
+ * need durability before continuing (e.g. before process exit).
48
+ */
49
+ flush: () => Promise<void>;
50
+ };
51
+ /**
52
+ * Persist each queue mutation as a row operation.
53
+ * Good for DB-style backends where enqueue/dequeue map to insert/delete.
54
+ *
55
+ * **Composition (required):** wrap the bare queue, then the worker:
56
+ * `withWorker(withRowPersist(buildQueue(), store), worker)`.
57
+ * Reverse order silently skips store removes — this helper throws if it
58
+ * detects a worker already on the queue.
59
+ *
60
+ * Durability:
61
+ * - Memory updates are optimistic and synchronous (API stays sync).
62
+ * - Store ops are serialized on a write chain.
63
+ * - Failed **insert** rolls back that row from memory if it is still present.
64
+ * - Failed **remove** / **clear** emit `persist:error` (memory already changed;
65
+ * call `hydrate` to resync if needed).
66
+ * - `hydrate` uses a silent rebuild (no mid-hydrate worker drain of the store),
67
+ * then emits one `queue:enqueued` so stacked workers pump after the gate opens.
68
+ * - Concurrent mutations during `hydrate` throw.
69
+ * - Call {@link QueueWithRowPersist.flush} to await pending writes.
70
+ */
71
+ export declare const withRowPersist: <T, TEvents extends QueueEvents<T> = QueueEvents<T>>(queue: Queue<T, TEvents>, store: RowStore<T>, options?: RowPersistOptions) => QueueWithRowPersist<T, RowQueueEvents<T, TEvents>>;
72
+ //# sourceMappingURL=with-row-persist.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { type EventMap, type MergeEventMaps } from '../../events';
2
+ import type { Queue, QueueEvents } from '../core/queue';
3
+ import type { SnapshotStore } from './persist.types';
4
+ export type { SnapshotStore } from './persist.types';
5
+ export type SnapshotPersistEvents = {
6
+ 'persist:loaded': {
7
+ size: number;
8
+ };
9
+ 'persist:saved': {
10
+ size: number;
11
+ };
12
+ 'persist:error': {
13
+ operation: 'load' | 'save';
14
+ error: unknown;
15
+ };
16
+ };
17
+ export type SnapshotPersistOptions = {
18
+ /**
19
+ * Automatically `save` after enqueue / dequeue / clear.
20
+ * Defaults to `true`.
21
+ */
22
+ autoSave?: boolean;
23
+ };
24
+ type SnapshotQueueEvents<T, TEvents extends EventMap> = MergeEventMaps<TEvents, SnapshotPersistEvents>;
25
+ export type QueueWithSnapshotPersist<T, TEvents extends EventMap = SnapshotQueueEvents<T, QueueEvents<T>>> = Queue<T, TEvents> & {
26
+ /** Replace in-memory queue contents from the store. */
27
+ hydrate: () => Promise<void>;
28
+ /** Write the current queue (head → tail) to the store. */
29
+ persist: () => Promise<void>;
30
+ /** Wait for pending auto-saves (and in-flight `persist`) to settle. */
31
+ flush: () => Promise<void>;
32
+ };
33
+ /**
34
+ * Persist the whole queue as one snapshot.
35
+ * Good for simple backends where you rewrite the full list each time.
36
+ *
37
+ * **Composition (required):** wrap the bare queue, then the worker:
38
+ * `withWorker(withSnapshotPersist(buildQueue(), store), worker)`.
39
+ *
40
+ * Uses silent hydrate rebuild + a post-gate `queue:enqueued` kick so stacked
41
+ * workers process restored items only after auto-save is allowed again.
42
+ * Concurrent mutations during `hydrate` throw.
43
+ */
44
+ export declare const withSnapshotPersist: <T, TEvents extends QueueEvents<T> = QueueEvents<T>>(queue: Queue<T, TEvents>, store: SnapshotStore<T>, options?: SnapshotPersistOptions) => QueueWithSnapshotPersist<T, SnapshotQueueEvents<T, TEvents>>;
45
+ //# sourceMappingURL=with-snapshot-persist.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Serialize async store mutations so concurrent enqueue/dequeue/clear
3
+ * cannot race the backend.
4
+ */
5
+ export type WriteChain = {
6
+ /** Enqueue an operation; returned promise settles when this op finishes. */
7
+ push: (op: () => Promise<void>) => Promise<void>;
8
+ /** Wait until all currently queued ops have settled. */
9
+ flush: () => Promise<void>;
10
+ };
11
+ export declare const createWriteChain: () => WriteChain;
12
+ //# sourceMappingURL=write-chain.util.d.ts.map
@@ -0,0 +1,56 @@
1
+ import { type EventMap, type MergeEventMaps } from '../../events';
2
+ import type { WorkerFn } from '../../worker/types';
3
+ import { type PreserveQueueExtras } from '../core/forward.util';
4
+ import type { Queue, QueueEvents } from '../core/queue';
5
+ export type { WorkerFn };
6
+ export type WorkerEvents<T, R = unknown> = {
7
+ /** Fired just before the worker runs an item. */
8
+ 'worker:started': {
9
+ item: T;
10
+ };
11
+ /** Fired when the worker resolves successfully. */
12
+ 'worker:completed': {
13
+ item: T;
14
+ result: R;
15
+ };
16
+ /** Fired when the worker throws or rejects. */
17
+ 'worker:failed': {
18
+ item: T;
19
+ error: unknown;
20
+ };
21
+ /** Fired when nothing is in-flight and the queue is empty. */
22
+ 'worker:idle': undefined;
23
+ };
24
+ export type WithWorkerOptions = {
25
+ /** Max items processed at the same time. Defaults to 1. Must be a finite number ≥ 1. */
26
+ concurrency?: number;
27
+ /** Start pumping immediately. Defaults to true. */
28
+ autoStart?: boolean;
29
+ };
30
+ type WorkerQueueEvents<T, R, TEvents extends EventMap> = MergeEventMaps<TEvents, WorkerEvents<T, R>>;
31
+ export type WorkerControls = {
32
+ /** Begin processing queued items. */
33
+ start: () => void;
34
+ /** Stop taking new items. In-flight work still finishes. */
35
+ stop: () => void;
36
+ /** Whether the worker is allowed to take new items. */
37
+ isRunning: () => boolean;
38
+ /** Whether any items are currently being processed. */
39
+ isProcessing: () => boolean;
40
+ /** Number of items currently being processed. */
41
+ activeCount: () => number;
42
+ };
43
+ export type QueueWithWorker<T, R = unknown, TEvents extends EventMap = WorkerQueueEvents<T, R, QueueEvents<T>>> = Queue<T, TEvents> & WorkerControls;
44
+ /**
45
+ * Wrap a queue with a worker that dequeues and processes items FIFO-style.
46
+ * Listens for `queue:enqueued` and pumps work up to `concurrency`.
47
+ *
48
+ * **Composition (required when using persist):** worker must be the **outer**
49
+ * decorator so `dequeue` hits the persist override:
50
+ * `withWorker(withRowPersist(buildQueue(), store), worker)` — not the reverse.
51
+ *
52
+ * Inner decorator extras (e.g. `flush` from row/snapshot persist) are preserved
53
+ * at runtime and in the return type via {@link PreserveQueueExtras}.
54
+ */
55
+ export declare const withWorker: <T, R = unknown, TEvents extends QueueEvents<T> = QueueEvents<T>, TQueue extends Queue<T, TEvents> = Queue<T, TEvents>>(queue: TQueue & Queue<T, TEvents>, worker: WorkerFn<T, R>, options?: WithWorkerOptions) => QueueWithWorker<T, R, WorkerQueueEvents<T, R, TEvents>> & PreserveQueueExtras<TQueue>;
56
+ //# sourceMappingURL=with-worker.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { buildRouter, type Binding, type BuildRouterOptions, type RouteMessage, type RouteTarget, type Router, type RouterEvents, type UnmatchedRecord, } from './router';
2
+ export { isValidPattern, isValidTopic, matchTopic, MULTI_WILDCARD, SINGLE_WILDCARD, TOPIC_SEPARATOR, } from './match.util';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,24 @@
1
+ /** Dot-separated topic segments, e.g. `orders.created.eu`. */
2
+ export declare const TOPIC_SEPARATOR = ".";
3
+ /** Matches exactly one non-empty segment. */
4
+ export declare const SINGLE_WILDCARD = "*";
5
+ /** Matches zero or more segments; only valid as the final pattern token. */
6
+ export declare const MULTI_WILDCARD = "#";
7
+ /**
8
+ * Validate a concrete publish topic (no wildcards, no empty segments).
9
+ */
10
+ export declare const isValidTopic: (topic: string) => boolean;
11
+ /**
12
+ * Validate a bind pattern (`*`, `#` allowed; `#` only as last segment).
13
+ */
14
+ export declare const isValidPattern: (pattern: string) => boolean;
15
+ /**
16
+ * MQTT / AMQP-style topic match.
17
+ *
18
+ * - `orders.created` matches only that topic
19
+ * - `orders.*` matches `orders.created`, not `orders.a.b`
20
+ * - `orders.#` matches `orders`, `orders.created`, `orders.a.b`
21
+ * - `#` matches everything
22
+ */
23
+ export declare const matchTopic: (pattern: string, topic: string) => boolean;
24
+ //# sourceMappingURL=match.util.d.ts.map
@@ -0,0 +1,107 @@
1
+ import { type EventEmitter, type EventMap, type MergeEventMaps } from '../events';
2
+ /**
3
+ * Envelope enqueued into bound queues.
4
+ * `topic` is the concrete published topic (wildcards resolved).
5
+ */
6
+ export type RouteMessage<T = unknown> = {
7
+ topic: string;
8
+ data: T;
9
+ };
10
+ /** Minimal queue surface the router needs. */
11
+ export type RouteTarget<T = unknown> = {
12
+ enqueue: (item: RouteMessage<T>) => void;
13
+ };
14
+ /** Snapshot of the most recent unrouted publish. */
15
+ export type UnmatchedRecord = {
16
+ topic: string;
17
+ data: unknown;
18
+ };
19
+ export type BuildRouterOptions = {
20
+ /**
21
+ * When a publish matches **no** bindings, enqueue a {@link RouteMessage}
22
+ * here (same shape as a normal route). Optional — stats + events still
23
+ * track unmatched publishes without a sink.
24
+ */
25
+ unmatchedTarget?: RouteTarget;
26
+ };
27
+ export type RouterEvents = {
28
+ 'router:bound': {
29
+ pattern: string;
30
+ };
31
+ 'router:unbound': {
32
+ pattern: string;
33
+ removed: number;
34
+ };
35
+ 'router:published': {
36
+ topic: string;
37
+ data: unknown;
38
+ matched: number;
39
+ };
40
+ /**
41
+ * Fired when no binding matched.
42
+ * `delivered` is true only if {@link BuildRouterOptions.unmatchedTarget}
43
+ * (or a target set via {@link Router.setUnmatchedTarget}) accepted the message.
44
+ */
45
+ 'router:unmatched': {
46
+ topic: string;
47
+ data: unknown;
48
+ delivered: boolean;
49
+ };
50
+ 'router:error': {
51
+ operation: 'publish' | 'bind' | 'unmatched';
52
+ error: unknown;
53
+ topic?: string;
54
+ pattern?: string;
55
+ };
56
+ };
57
+ export type Binding<T = unknown> = {
58
+ pattern: string;
59
+ target: RouteTarget<T>;
60
+ };
61
+ export type Router<TEvents extends EventMap = RouterEvents> = {
62
+ /**
63
+ * Bind a queue (or any `enqueue` target) to a topic pattern.
64
+ * Returns an unbind function for this binding only.
65
+ *
66
+ * Patterns use `.` segments:
67
+ * - `orders.created` exact
68
+ * - `orders.*` one segment
69
+ * - `orders.#` zero or more trailing segments
70
+ */
71
+ bind: <T = unknown>(pattern: string, target: RouteTarget<T>) => () => void;
72
+ /** Remove one binding, or all bindings for a pattern if target omitted. */
73
+ unbind: <T = unknown>(pattern: string, target?: RouteTarget<T>) => void;
74
+ /**
75
+ * Publish an event on a concrete topic. Enqueues a {@link RouteMessage}
76
+ * into every matching target. Returns the number of matched bindings
77
+ * (0 when unrouted; the unmatched sink does not count as a match).
78
+ */
79
+ publish: <T = unknown>(topic: string, data: T) => number;
80
+ /** Snapshot of current pattern → target bindings. */
81
+ bindings: () => Binding[];
82
+ /** Clear all bindings (does not clear unmatched stats or the sink target). */
83
+ clear: () => void;
84
+ /**
85
+ * Optional sink for unrouted publishes (`matched === 0`).
86
+ * Pass `undefined` to clear.
87
+ */
88
+ setUnmatchedTarget: (target: RouteTarget | undefined) => void;
89
+ /** Current unmatched sink, if any. */
90
+ getUnmatchedTarget: () => RouteTarget | undefined;
91
+ /** How many publishes have been unrouted since the last {@link clearUnmatched}. */
92
+ unmatchedCount: () => number;
93
+ /** Most recent unrouted publish, if any. */
94
+ lastUnmatched: () => UnmatchedRecord | undefined;
95
+ /** Reset unmatched count and last record (does not drain the sink queue). */
96
+ clearUnmatched: () => void;
97
+ on: EventEmitter<TEvents>['on'];
98
+ once: EventEmitter<TEvents>['once'];
99
+ off: EventEmitter<TEvents>['off'];
100
+ emit: EventEmitter<TEvents>['emit'];
101
+ expand: <TExtra extends EventMap>() => Router<MergeEventMaps<TEvents, TExtra>>;
102
+ };
103
+ /**
104
+ * Topic router / controller: publish events, route into queues by pattern.
105
+ */
106
+ export declare const buildRouter: (options?: BuildRouterOptions) => Router;
107
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1,4 @@
1
+ export type { StepFn, WorkerFn } from './types';
2
+ export { RetryExhaustedError, withRetry, type RetryOptions, } from './retry';
3
+ export { pipeline } from './pipeline';
4
+ //# sourceMappingURL=index.d.ts.map