@checkstack/queue-api 0.0.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,88 @@
1
+ # @checkstack/queue-api
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d20d274: Initial release of all @checkstack packages. Rebranded from Checkmate to Checkstack with new npm organization @checkstack and domain checkstack.dev.
8
+ - Updated dependencies [d20d274]
9
+ - @checkstack/backend-api@0.0.2
10
+
11
+ ## 1.0.1
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [b4eb432]
16
+ - Updated dependencies [a65e002]
17
+ - @checkstack/backend-api@1.1.0
18
+
19
+ ## 1.0.0
20
+
21
+ ### Major Changes
22
+
23
+ - 8e889b4: Add consumer group support to Queue API for distributed event system. BREAKING: consume() now requires ConsumeOptions with consumerGroup parameter.
24
+
25
+ ### Minor Changes
26
+
27
+ - e4d83fc: Add BullMQ queue plugin with orphaned job cleanup
28
+
29
+ - **queue-api**: Added `listRecurringJobs()` method to Queue interface for detecting orphaned jobs
30
+ - **queue-bullmq-backend**: New plugin implementing BullMQ (Redis) queue backend with job schedulers, consumer groups, and distributed job persistence
31
+ - **queue-bullmq-common**: New common package with queue permissions
32
+ - **queue-memory-backend**: Implemented `listRecurringJobs()` for in-memory queue
33
+ - **healthcheck-backend**: Enhanced `bootstrapHealthChecks` to clean up orphaned job schedulers using `listRecurringJobs()`
34
+ - **test-utils-backend**: Added `listRecurringJobs()` to mock queue factory
35
+
36
+ This enables production-ready distributed queue processing with Redis persistence and automatic cleanup of orphaned jobs when health checks are deleted.
37
+
38
+ ### Patch Changes
39
+
40
+ - 81f3f85: ## Breaking: Unified Versioned<T> Architecture
41
+
42
+ Refactored the versioning system to use a unified `Versioned<T>` class instead of separate `VersionedSchema`, `VersionedData`, and `VersionedConfig` types.
43
+
44
+ ### Breaking Changes
45
+
46
+ - **`VersionedSchema<T>`** is replaced by `Versioned<T>` class
47
+ - **`VersionedData<T>`** is replaced by `VersionedRecord<T>` interface
48
+ - **`VersionedConfig<T>`** is replaced by `VersionedPluginRecord<T>` interface
49
+ - **`ConfigMigration<F, T>`** is replaced by `Migration<F, T>` interface
50
+ - **`MigrationChain<T>`** is removed (use `Migration<unknown, unknown>[]`)
51
+ - **`migrateVersionedData()`** is removed (use `versioned.parse()`)
52
+ - **`ConfigMigrationRunner`** is removed (migrations are internal to Versioned)
53
+
54
+ ### Migration Guide
55
+
56
+ Before:
57
+
58
+ ```typescript
59
+ const strategy: HealthCheckStrategy = {
60
+ config: {
61
+ version: 1,
62
+ schema: mySchema,
63
+ migrations: [],
64
+ },
65
+ };
66
+ const data = await migrateVersionedData(stored, 1, migrations);
67
+ ```
68
+
69
+ After:
70
+
71
+ ```typescript
72
+ const strategy: HealthCheckStrategy = {
73
+ config: new Versioned({
74
+ version: 1,
75
+ schema: mySchema,
76
+ migrations: [],
77
+ }),
78
+ };
79
+ const data = await strategy.config.parse(stored);
80
+ ```
81
+
82
+ - Updated dependencies [ffc28f6]
83
+ - Updated dependencies [71275dd]
84
+ - Updated dependencies [ae19ff6]
85
+ - Updated dependencies [b55fae6]
86
+ - Updated dependencies [b354ab3]
87
+ - Updated dependencies [81f3f85]
88
+ - @checkstack/backend-api@1.0.0
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@checkstack/queue-api",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "main": "src/index.ts",
6
+ "dependencies": {
7
+ "@checkstack/backend-api": "workspace:*",
8
+ "zod": "^4.0.0"
9
+ },
10
+ "devDependencies": {
11
+ "@checkstack/tsconfig": "workspace:*",
12
+ "@checkstack/scripts": "workspace:*"
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit",
16
+ "lint": "bun run lint:code",
17
+ "lint:code": "eslint . --max-warnings 0"
18
+ }
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./queue";
2
+ export * from "./queue-plugin";
@@ -0,0 +1,104 @@
1
+ import { z } from "zod";
2
+ import type { Queue } from "./queue";
3
+ import type { Migration } from "@checkstack/backend-api";
4
+
5
+ export interface QueuePlugin<Config = unknown> {
6
+ id: string;
7
+ displayName: string;
8
+ description?: string;
9
+
10
+ /** Current version of the configuration schema */
11
+ configVersion: number;
12
+
13
+ /** Validation schema for the plugin-specific config */
14
+ configSchema: z.ZodType<Config>;
15
+
16
+ /** Optional migrations for backward compatibility */
17
+ migrations?: Migration<unknown, unknown>[];
18
+
19
+ createQueue<T>(name: string, config: Config): Queue<T>;
20
+ }
21
+
22
+ export interface QueuePluginRegistry {
23
+ register(plugin: QueuePlugin<unknown>): void;
24
+ getPlugin(id: string): QueuePlugin<unknown> | undefined;
25
+ getPlugins(): QueuePlugin<unknown>[];
26
+ }
27
+
28
+ /**
29
+ * Result of switching queue backends
30
+ */
31
+ export interface SwitchResult {
32
+ success: boolean;
33
+ migratedRecurringJobs: number;
34
+ warnings: string[];
35
+ }
36
+
37
+ /**
38
+ * Info about a recurring job across all queues
39
+ */
40
+ export interface RecurringJobInfo {
41
+ queueName: string;
42
+ jobId: string;
43
+ intervalSeconds: number;
44
+ nextRunAt?: Date;
45
+ }
46
+
47
+ /**
48
+ * QueueManager handles queue creation, backend switching, and multi-instance coordination.
49
+ *
50
+ * Key features:
51
+ * - Returns stable QueueProxy instances that survive backend switches
52
+ * - Polls config for changes to support multi-instance coordination
53
+ * - Handles graceful migration of recurring jobs when switching backends
54
+ */
55
+ export interface QueueManager {
56
+ /**
57
+ * Get or create a queue proxy.
58
+ * Returns a stable reference that survives backend switches.
59
+ * Subscriptions are automatically re-applied when backend changes.
60
+ */
61
+ getQueue<T>(name: string): Queue<T>;
62
+
63
+ /**
64
+ * Get the currently active queue plugin ID
65
+ */
66
+ getActivePlugin(): string;
67
+
68
+ /**
69
+ * Get the currently active queue plugin configuration
70
+ */
71
+ getActiveConfig(): unknown;
72
+
73
+ /**
74
+ * Switch to a different queue backend with job migration.
75
+ *
76
+ * @throws Error if connection test fails
77
+ * @throws Error if migration fails
78
+ */
79
+ setActiveBackend(pluginId: string, config: unknown): Promise<SwitchResult>;
80
+
81
+ /**
82
+ * Get total number of in-flight jobs across all queues.
83
+ * Used to warn users before switching backends.
84
+ */
85
+ getInFlightJobCount(): Promise<number>;
86
+
87
+ /**
88
+ * List all recurring jobs across all queues.
89
+ * Used for migration preview.
90
+ */
91
+ listAllRecurringJobs(): Promise<RecurringJobInfo[]>;
92
+
93
+ /**
94
+ * Start polling for configuration changes.
95
+ * Required for multi-instance coordination.
96
+ * @param intervalMs - Polling interval in milliseconds (default: 5000)
97
+ */
98
+ startPolling(intervalMs?: number): void;
99
+
100
+ /**
101
+ * Stop polling and gracefully shutdown all queues.
102
+ */
103
+ shutdown(): Promise<void>;
104
+ }
package/src/queue.ts ADDED
@@ -0,0 +1,160 @@
1
+ export interface QueueJob<T = unknown> {
2
+ id: string;
3
+ data: T;
4
+ priority?: number;
5
+ timestamp: Date;
6
+ /**
7
+ * Number of processing attempts (for retry logic)
8
+ */
9
+ attempts?: number;
10
+ }
11
+
12
+ /**
13
+ * Details of a recurring job for migration purposes
14
+ */
15
+ export interface RecurringJobDetails<T = unknown> {
16
+ jobId: string;
17
+ data: T;
18
+ intervalSeconds: number;
19
+ priority?: number;
20
+ nextRunAt?: Date;
21
+ }
22
+
23
+ export interface QueueConsumer<T = unknown> {
24
+ (job: QueueJob<T>): Promise<void>;
25
+ }
26
+
27
+ export interface ConsumeOptions {
28
+ /**
29
+ * Consumer group ID for load balancing.
30
+ *
31
+ * Messages are distributed across consumers in the same group (competing consumers).
32
+ * Each consumer group receives a copy of each message (broadcast).
33
+ *
34
+ * Example:
35
+ * - Same group ID = work queue (only one consumer per group gets the message)
36
+ * - Unique group ID per instance = broadcast (all instances get the message)
37
+ */
38
+ consumerGroup: string;
39
+
40
+ /**
41
+ * Maximum retry attempts on failure
42
+ * @default 3
43
+ */
44
+ maxRetries?: number;
45
+ }
46
+
47
+ export interface Queue<T = unknown> {
48
+ /**
49
+ * Enqueue a job for processing
50
+ */
51
+ enqueue(
52
+ data: T,
53
+ options?: {
54
+ priority?: number;
55
+ /**
56
+ * Delay in seconds before the job becomes available for processing
57
+ * Queue backends should not make this job available until the delay expires
58
+ */
59
+ startDelay?: number;
60
+ /**
61
+ * Optional unique job ID for deduplication
62
+ * If provided and a job with this ID already exists, behavior depends on queue implementation
63
+ */
64
+ jobId?: string;
65
+ }
66
+ ): Promise<string>;
67
+
68
+ /**
69
+ * Register a consumer to process jobs
70
+ *
71
+ * BREAKING CHANGE: Now requires ConsumeOptions with consumerGroup
72
+ */
73
+ consume(consumer: QueueConsumer<T>, options: ConsumeOptions): Promise<void>;
74
+
75
+ /**
76
+ * Schedule a recurring job that executes at regular intervals.
77
+ *
78
+ * UPDATE SEMANTICS: Calling this method with an existing jobId will UPDATE
79
+ * the recurring job configuration (interval, payload, priority, etc.).
80
+ * The queue implementation MUST:
81
+ * 1. Cancel any pending scheduled execution of the old job
82
+ * 2. Apply the new configuration
83
+ * 3. Schedule the next execution with the new startDelay (or immediately if not provided)
84
+ *
85
+ * @param data - Job payload
86
+ * @param options - Recurring job options
87
+ * @returns Job ID
88
+ */
89
+ scheduleRecurring(
90
+ data: T,
91
+ options: {
92
+ /**
93
+ * Unique job ID for deduplication and updates.
94
+ * Calling scheduleRecurring with the same jobId updates the existing job.
95
+ */
96
+ jobId: string;
97
+ /** Interval in seconds between executions */
98
+ intervalSeconds: number;
99
+ /** Optional delay before first execution (for delta-based scheduling) */
100
+ startDelay?: number;
101
+ /** Optional priority */
102
+ priority?: number;
103
+ }
104
+ ): Promise<string>;
105
+
106
+ /**
107
+ * Cancel a recurring job
108
+ * @param jobId - Job ID to cancel
109
+ */
110
+ cancelRecurring(jobId: string): Promise<void>;
111
+
112
+ /**
113
+ * List all recurring job IDs
114
+ * Used for reconciliation to detect orphaned jobs
115
+ * @returns Array of job IDs for all recurring jobs
116
+ */
117
+ listRecurringJobs(): Promise<string[]>;
118
+
119
+ /**
120
+ * Get details of a recurring job for migration purposes
121
+ * @param jobId - Job ID
122
+ * @returns Job details or undefined if not found
123
+ */
124
+ getRecurringJobDetails(
125
+ jobId: string
126
+ ): Promise<RecurringJobDetails<T> | undefined>;
127
+
128
+ /**
129
+ * Get the number of jobs currently being processed
130
+ * Used to warn users before switching backends
131
+ */
132
+ getInFlightCount(): Promise<number>;
133
+
134
+ /**
135
+ * Test connection to the queue backend
136
+ * @throws Error if connection fails
137
+ */
138
+ testConnection(): Promise<void>;
139
+
140
+ /**
141
+ * Stop consuming jobs and cleanup
142
+ */
143
+ stop(): Promise<void>;
144
+
145
+ /**
146
+ * Get queue statistics
147
+ */
148
+ getStats(): Promise<QueueStats>;
149
+ }
150
+
151
+ export interface QueueStats {
152
+ pending: number;
153
+ processing: number;
154
+ completed: number;
155
+ failed: number;
156
+ /**
157
+ * Number of active consumer groups
158
+ */
159
+ consumerGroups: number;
160
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/backend.json",
3
+ "include": [
4
+ "src"
5
+ ]
6
+ }