@boringnode/queue 0.0.1-alpha.3 → 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.
- package/README.md +255 -35
- package/build/chunk-NPQKBCCY.js +26 -0
- package/build/chunk-NPQKBCCY.js.map +1 -0
- package/build/chunk-SMOKFZ46.js +117 -0
- package/build/chunk-SMOKFZ46.js.map +1 -0
- package/build/chunk-US7THLSZ.js +357 -0
- package/build/chunk-US7THLSZ.js.map +1 -0
- package/build/index-2Ng_OpVK.d.ts +1013 -0
- package/build/index.d.ts +429 -4
- package/build/index.js +725 -118
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.d.ts +9 -1
- package/build/src/drivers/knex_adapter.js +253 -42
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +8 -2
- package/build/src/drivers/redis_adapter.js +265 -27
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +13 -3
- package/build/src/drivers/sync_adapter.js +43 -8
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/types/index.d.ts +1 -0
- package/build/src/types/index.js +1 -0
- package/build/src/types/index.js.map +1 -0
- package/build/src/types/main.d.ts +1 -1
- package/package.json +16 -3
- package/build/chunk-Y6KR3UIR.js +0 -99
- package/build/chunk-Y6KR3UIR.js.map +0 -1
- package/build/job-Bd_c2lFK.d.ts +0 -149
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import {
|
|
2
|
+
E_ADAPTER_INIT_ERROR,
|
|
3
|
+
E_CONFIGURATION_ERROR,
|
|
4
|
+
E_JOB_NOT_FOUND,
|
|
5
|
+
E_QUEUE_NOT_INITIALIZED
|
|
6
|
+
} from "./chunk-SMOKFZ46.js";
|
|
7
|
+
|
|
8
|
+
// src/debug.ts
|
|
9
|
+
import { debuglog } from "util";
|
|
10
|
+
var debug_default = debuglog("boringnode:queue");
|
|
11
|
+
|
|
12
|
+
// src/locator.ts
|
|
13
|
+
import { glob } from "fs/promises";
|
|
14
|
+
import { resolve } from "path";
|
|
15
|
+
var LocatorSingleton = class {
|
|
16
|
+
#registry = /* @__PURE__ */ new Map();
|
|
17
|
+
/**
|
|
18
|
+
* Register a job class with a given name.
|
|
19
|
+
*
|
|
20
|
+
* @param name - The job name (usually the class name)
|
|
21
|
+
* @param JobClass - The job class constructor
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* Locator.register('SendEmailJob', SendEmailJob)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
register(name, JobClass) {
|
|
29
|
+
debug_default("registering job: %s", name);
|
|
30
|
+
this.#registry.set(name, JobClass);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Auto-register job classes from files matching glob patterns.
|
|
34
|
+
*
|
|
35
|
+
* Each file should have a default export that is a Job class.
|
|
36
|
+
* The class name is used as the registration name.
|
|
37
|
+
*
|
|
38
|
+
* @param patterns - Glob patterns to match job files
|
|
39
|
+
* @returns Number of jobs successfully registered
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const count = await Locator.registerFromGlob([
|
|
44
|
+
* './jobs/**\/*.js',
|
|
45
|
+
* './app/jobs/**\/*.ts'
|
|
46
|
+
* ])
|
|
47
|
+
* console.log(`Registered ${count} jobs`)
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
async registerFromGlob(patterns) {
|
|
51
|
+
let registered = 0;
|
|
52
|
+
for (const pattern of patterns) {
|
|
53
|
+
debug_default("registering jobs from glob pattern: %s", pattern);
|
|
54
|
+
for await (const file of glob(pattern)) {
|
|
55
|
+
debug_default("found job file: %s", file);
|
|
56
|
+
try {
|
|
57
|
+
const absolutePath = resolve(file);
|
|
58
|
+
const module = await import(`file://${absolutePath}`);
|
|
59
|
+
const JobClass = module.default;
|
|
60
|
+
if (JobClass && typeof JobClass === "function" && JobClass.name) {
|
|
61
|
+
this.register(JobClass.name, JobClass);
|
|
62
|
+
registered++;
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.warn(`Failed to load job from ${file}:`, error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return registered;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get a job class by name.
|
|
73
|
+
*
|
|
74
|
+
* @param name - The job name to look up
|
|
75
|
+
* @returns The job class, or undefined if not found
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const JobClass = Locator.get('SendEmailJob')
|
|
80
|
+
* if (JobClass) {
|
|
81
|
+
* const instance = new JobClass(payload)
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
get(name) {
|
|
86
|
+
return this.#registry.get(name);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get a job class by name, throwing if not found.
|
|
90
|
+
*
|
|
91
|
+
* @param name - The job name to look up
|
|
92
|
+
* @returns The job class
|
|
93
|
+
* @throws {E_JOB_NOT_FOUND} If the job is not registered
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const JobClass = Locator.getOrThrow('SendEmailJob')
|
|
98
|
+
* const instance = new JobClass(payload)
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
getOrThrow(name) {
|
|
102
|
+
const JobClass = this.get(name);
|
|
103
|
+
if (!JobClass) {
|
|
104
|
+
throw new E_JOB_NOT_FOUND([name]);
|
|
105
|
+
}
|
|
106
|
+
return JobClass;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Remove all registered jobs.
|
|
110
|
+
*
|
|
111
|
+
* Primarily useful for testing.
|
|
112
|
+
*/
|
|
113
|
+
clear() {
|
|
114
|
+
this.#registry.clear();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var Locator = new LocatorSingleton();
|
|
118
|
+
|
|
119
|
+
// src/logger.ts
|
|
120
|
+
var ConsoleLogger = class _ConsoleLogger {
|
|
121
|
+
#prefix;
|
|
122
|
+
constructor(prefix = "queue") {
|
|
123
|
+
this.#prefix = prefix;
|
|
124
|
+
}
|
|
125
|
+
#format(level, msgOrObj, msg) {
|
|
126
|
+
const prefix = `[${this.#prefix}] ${level}:`;
|
|
127
|
+
if (typeof msgOrObj === "object") {
|
|
128
|
+
return [`${prefix} ${msg}`, msgOrObj];
|
|
129
|
+
}
|
|
130
|
+
return [`${prefix} ${msgOrObj}`];
|
|
131
|
+
}
|
|
132
|
+
trace(msgOrObj, msg) {
|
|
133
|
+
const [message, obj] = this.#format("TRACE", msgOrObj, msg);
|
|
134
|
+
if (obj) {
|
|
135
|
+
return console.log(message, obj);
|
|
136
|
+
}
|
|
137
|
+
console.log(message);
|
|
138
|
+
}
|
|
139
|
+
debug(msgOrObj, msg) {
|
|
140
|
+
const [message, obj] = this.#format("DEBUG", msgOrObj, msg);
|
|
141
|
+
if (obj) {
|
|
142
|
+
return console.log(message, obj);
|
|
143
|
+
}
|
|
144
|
+
console.log(message);
|
|
145
|
+
}
|
|
146
|
+
info(msgOrObj, msg) {
|
|
147
|
+
const [message, obj] = this.#format("INFO", msgOrObj, msg);
|
|
148
|
+
if (obj) {
|
|
149
|
+
return console.log(message, obj);
|
|
150
|
+
}
|
|
151
|
+
console.log(message);
|
|
152
|
+
}
|
|
153
|
+
warn(msgOrObj, msg) {
|
|
154
|
+
const [message, obj] = this.#format("WARN", msgOrObj, msg);
|
|
155
|
+
if (obj) {
|
|
156
|
+
return console.warn(message, obj);
|
|
157
|
+
}
|
|
158
|
+
console.warn(message);
|
|
159
|
+
}
|
|
160
|
+
error(msgOrObj, msg) {
|
|
161
|
+
const [message, obj] = this.#format("ERROR", msgOrObj, msg);
|
|
162
|
+
if (obj) {
|
|
163
|
+
return console.error(message, obj);
|
|
164
|
+
}
|
|
165
|
+
console.error(message);
|
|
166
|
+
}
|
|
167
|
+
child(obj) {
|
|
168
|
+
const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix;
|
|
169
|
+
return new _ConsoleLogger(childPrefix);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
var consoleLogger = new ConsoleLogger();
|
|
173
|
+
|
|
174
|
+
// src/queue_manager.ts
|
|
175
|
+
var QueueManagerSingleton = class {
|
|
176
|
+
#initialized = false;
|
|
177
|
+
#defaultAdapter;
|
|
178
|
+
#adapters = {};
|
|
179
|
+
#adapterInstances = /* @__PURE__ */ new Map();
|
|
180
|
+
#globalRetryConfig;
|
|
181
|
+
#queueConfigs = /* @__PURE__ */ new Map();
|
|
182
|
+
#logger = consoleLogger;
|
|
183
|
+
#jobFactory;
|
|
184
|
+
/**
|
|
185
|
+
* Initialize the queue system with the given configuration.
|
|
186
|
+
*
|
|
187
|
+
* This must be called before using the queue system. It:
|
|
188
|
+
* - Validates the configuration
|
|
189
|
+
* - Registers adapters
|
|
190
|
+
* - Auto-discovers and registers job classes from `locations`
|
|
191
|
+
*
|
|
192
|
+
* @param config - The queue configuration
|
|
193
|
+
* @returns This instance for chaining
|
|
194
|
+
* @throws {E_CONFIGURATION_ERROR} If the configuration is invalid
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* await QueueManager.init({
|
|
199
|
+
* default: 'redis',
|
|
200
|
+
* adapters: {
|
|
201
|
+
* redis: redis(),
|
|
202
|
+
* postgres: knex(pgConfig),
|
|
203
|
+
* },
|
|
204
|
+
* locations: ['./jobs/**\/*.js'],
|
|
205
|
+
* })
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
async init(config) {
|
|
209
|
+
debug_default("initializing queue manager with config: %O", config);
|
|
210
|
+
this.#validateConfig(config);
|
|
211
|
+
this.#adapterInstances.clear();
|
|
212
|
+
this.#defaultAdapter = config.default;
|
|
213
|
+
this.#adapters = config.adapters;
|
|
214
|
+
this.#globalRetryConfig = config.retry;
|
|
215
|
+
this.#logger = config.logger ?? consoleLogger;
|
|
216
|
+
this.#jobFactory = config.jobFactory;
|
|
217
|
+
if (config.queues) {
|
|
218
|
+
for (const [queue, queueConfig] of Object.entries(config.queues)) {
|
|
219
|
+
this.#queueConfigs.set(queue, queueConfig);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (config.locations && config.locations.length > 0) {
|
|
223
|
+
const registered = await Locator.registerFromGlob(config.locations);
|
|
224
|
+
if (registered === 0) {
|
|
225
|
+
this.#logger.warn(
|
|
226
|
+
`No jobs found for locations: ${config.locations.join(", ")}. Verify your glob patterns match your job files.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.#initialized = true;
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get an adapter instance by name.
|
|
235
|
+
*
|
|
236
|
+
* Adapter instances are cached and reused. If no name is provided,
|
|
237
|
+
* the default adapter is returned.
|
|
238
|
+
*
|
|
239
|
+
* @param adapter - Adapter name (optional, defaults to the default adapter)
|
|
240
|
+
* @returns The adapter instance
|
|
241
|
+
* @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called
|
|
242
|
+
* @throws {E_CONFIGURATION_ERROR} If the adapter is not registered
|
|
243
|
+
* @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* // Get default adapter
|
|
248
|
+
* const adapter = QueueManager.use()
|
|
249
|
+
*
|
|
250
|
+
* // Get specific adapter
|
|
251
|
+
* const redisAdapter = QueueManager.use('redis')
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
use(adapter) {
|
|
255
|
+
if (!this.#initialized) {
|
|
256
|
+
throw new E_QUEUE_NOT_INITIALIZED();
|
|
257
|
+
}
|
|
258
|
+
if (!adapter) {
|
|
259
|
+
adapter = this.#defaultAdapter;
|
|
260
|
+
}
|
|
261
|
+
const cached = this.#adapterInstances.get(adapter);
|
|
262
|
+
if (cached) {
|
|
263
|
+
return cached;
|
|
264
|
+
}
|
|
265
|
+
const adapterFactory = this.#adapters[adapter];
|
|
266
|
+
if (!adapterFactory) {
|
|
267
|
+
throw new E_CONFIGURATION_ERROR([`Adapter "${adapter}" is not registered`]);
|
|
268
|
+
}
|
|
269
|
+
debug_default('using adapter "%s"', adapter);
|
|
270
|
+
try {
|
|
271
|
+
const instance = adapterFactory();
|
|
272
|
+
this.#adapterInstances.set(adapter, instance);
|
|
273
|
+
return instance;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
throw new E_ADAPTER_INIT_ERROR([adapter, message]);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the merged retry configuration for a job.
|
|
281
|
+
*
|
|
282
|
+
* Configuration is merged with priority: job > queue > global.
|
|
283
|
+
* This allows specific jobs or queues to override global defaults.
|
|
284
|
+
*
|
|
285
|
+
* @param queue - The queue name
|
|
286
|
+
* @param jobRetryConfig - Optional job-level retry config
|
|
287
|
+
* @returns The merged retry configuration
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1
|
|
292
|
+
* // Result: maxRetries=1 (job wins)
|
|
293
|
+
* const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
getMergedRetryConfig(queue, jobRetryConfig) {
|
|
297
|
+
const queueConfig = this.#queueConfigs.get(queue);
|
|
298
|
+
const queueRetryConfig = queueConfig?.retry || {};
|
|
299
|
+
let maxRetries = jobRetryConfig?.maxRetries || queueRetryConfig.maxRetries || this.#globalRetryConfig?.maxRetries || 0;
|
|
300
|
+
let backoff = jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff;
|
|
301
|
+
return { maxRetries, backoff };
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get the configured job factory for custom instantiation.
|
|
305
|
+
*
|
|
306
|
+
* @returns The job factory function, or undefined if not configured
|
|
307
|
+
*/
|
|
308
|
+
getJobFactory() {
|
|
309
|
+
return this.#jobFactory;
|
|
310
|
+
}
|
|
311
|
+
#validateConfig(config) {
|
|
312
|
+
if (!config.adapters || Object.keys(config.adapters).length === 0) {
|
|
313
|
+
throw new E_CONFIGURATION_ERROR(["At least one adapter must be configured"]);
|
|
314
|
+
}
|
|
315
|
+
if (!config.default) {
|
|
316
|
+
throw new E_CONFIGURATION_ERROR(["Default adapter must be specified"]);
|
|
317
|
+
}
|
|
318
|
+
if (!config.adapters[config.default]) {
|
|
319
|
+
throw new E_CONFIGURATION_ERROR([
|
|
320
|
+
`Default adapter "${config.default}" not found in adapters configuration`
|
|
321
|
+
]);
|
|
322
|
+
}
|
|
323
|
+
for (const [name, factory] of Object.entries(config.adapters)) {
|
|
324
|
+
if (typeof factory !== "function") {
|
|
325
|
+
throw new E_CONFIGURATION_ERROR([`Adapter "${name}" must be a factory function`]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Clean up all adapter instances and reset state.
|
|
331
|
+
*
|
|
332
|
+
* Call this when shutting down the application or when
|
|
333
|
+
* you need to reinitialize with a new configuration.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* // On application shutdown
|
|
338
|
+
* await QueueManager.destroy()
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
async destroy() {
|
|
342
|
+
for (const [name, adapter] of this.#adapterInstances) {
|
|
343
|
+
debug_default('destroying adapter "%s"', name);
|
|
344
|
+
await adapter.destroy();
|
|
345
|
+
}
|
|
346
|
+
this.#adapterInstances.clear();
|
|
347
|
+
this.#initialized = false;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
var QueueManager = new QueueManagerSingleton();
|
|
351
|
+
|
|
352
|
+
export {
|
|
353
|
+
debug_default,
|
|
354
|
+
Locator,
|
|
355
|
+
QueueManager
|
|
356
|
+
};
|
|
357
|
+
//# sourceMappingURL=chunk-US7THLSZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/debug.ts","../src/locator.ts","../src/logger.ts","../src/queue_manager.ts"],"sourcesContent":["import { debuglog } from 'node:util'\n\nexport default debuglog('boringnode:queue')\n","import { Job } from './job.js'\nimport * as errors from './exceptions.js'\nimport type { JobClass } from './types/main.js'\nimport debug from './debug.js'\nimport { glob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\n\n/**\n * Job class registry.\n *\n * The Locator maintains a mapping of job names to their classes,\n * allowing the Worker to instantiate jobs by name when processing.\n *\n * Jobs are typically registered automatically via `QueueManager.init()`\n * using the `locations` config option, but can also be registered manually.\n *\n * @example\n * ```typescript\n * import { Locator } from '@boringnode/queue'\n * import SendEmailJob from './jobs/send_email_job.js'\n *\n * // Manual registration\n * Locator.register('SendEmailJob', SendEmailJob)\n *\n * // Auto-registration via glob (used by QueueManager.init)\n * await Locator.registerFromGlob(['./jobs/**\\/*.js'])\n *\n * // Retrieve a job class\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * ```\n */\nclass LocatorSingleton {\n #registry = new Map<string, JobClass>()\n\n /**\n * Register a job class with a given name.\n *\n * @param name - The job name (usually the class name)\n * @param JobClass - The job class constructor\n *\n * @example\n * ```typescript\n * Locator.register('SendEmailJob', SendEmailJob)\n * ```\n */\n register<T extends Job>(name: string, JobClass: JobClass<T>) {\n debug('registering job: %s', name)\n\n this.#registry.set(name, JobClass)\n }\n\n /**\n * Auto-register job classes from files matching glob patterns.\n *\n * Each file should have a default export that is a Job class.\n * The class name is used as the registration name.\n *\n * @param patterns - Glob patterns to match job files\n * @returns Number of jobs successfully registered\n *\n * @example\n * ```typescript\n * const count = await Locator.registerFromGlob([\n * './jobs/**\\/*.js',\n * './app/jobs/**\\/*.ts'\n * ])\n * console.log(`Registered ${count} jobs`)\n * ```\n */\n async registerFromGlob(patterns: string[]): Promise<number> {\n let registered = 0\n\n for (const pattern of patterns) {\n debug('registering jobs from glob pattern: %s', pattern)\n for await (const file of glob(pattern)) {\n debug('found job file: %s', file)\n\n try {\n const absolutePath = resolve(file)\n const module = await import(`file://${absolutePath}`)\n const JobClass = module.default as JobClass\n\n if (JobClass && typeof JobClass === 'function' && JobClass.name) {\n this.register(JobClass.name, JobClass)\n registered++\n }\n } catch (error) {\n console.warn(`Failed to load job from ${file}:`, error)\n }\n }\n }\n\n return registered\n }\n\n /**\n * Get a job class by name.\n *\n * @param name - The job name to look up\n * @returns The job class, or undefined if not found\n *\n * @example\n * ```typescript\n * const JobClass = Locator.get('SendEmailJob')\n * if (JobClass) {\n * const instance = new JobClass(payload)\n * }\n * ```\n */\n get<T extends Job = Job>(name: string): JobClass<T> | undefined {\n return this.#registry.get(name) as JobClass<T> | undefined\n }\n\n /**\n * Get a job class by name, throwing if not found.\n *\n * @param name - The job name to look up\n * @returns The job class\n * @throws {E_JOB_NOT_FOUND} If the job is not registered\n *\n * @example\n * ```typescript\n * const JobClass = Locator.getOrThrow('SendEmailJob')\n * const instance = new JobClass(payload)\n * ```\n */\n getOrThrow<T extends Job = Job>(name: string): JobClass<T> {\n const JobClass = this.get<T>(name)\n\n if (!JobClass) {\n throw new errors.E_JOB_NOT_FOUND([name])\n }\n\n return JobClass\n }\n\n /**\n * Remove all registered jobs.\n *\n * Primarily useful for testing.\n */\n clear(): void {\n this.#registry.clear()\n }\n}\n\n/** Global job class registry singleton */\nexport const Locator = new LocatorSingleton()\n","export interface LogObject {\n [key: string]: unknown\n}\n\nexport interface ErrorObject extends LogObject {\n err?: Error\n}\n\nexport interface Logger {\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n\n child(obj: LogObject): Logger\n}\n\n/**\n * A simple logger that writes to console.\n */\nclass ConsoleLogger implements Logger {\n #prefix: string\n\n constructor(prefix: string = 'queue') {\n this.#prefix = prefix\n }\n\n #format(level: string, msgOrObj: string | LogObject, msg?: string): [string, LogObject?] {\n const prefix = `[${this.#prefix}] ${level}:`\n\n if (typeof msgOrObj === 'object') {\n return [`${prefix} ${msg}`, msgOrObj]\n }\n\n return [`${prefix} ${msgOrObj}`]\n }\n\n trace(msg: string): void\n trace(obj: LogObject, msg: string): void\n trace(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('TRACE', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n debug(msg: string): void\n debug(obj: LogObject, msg: string): void\n debug(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('DEBUG', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n info(msg: string): void\n info(obj: LogObject, msg: string): void\n info(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('INFO', msgOrObj, msg)\n\n if (obj) {\n return console.log(message, obj)\n }\n\n console.log(message)\n }\n\n warn(msg: string): void\n warn(obj: LogObject, msg: string): void\n warn(msgOrObj: string | LogObject, msg?: string): void {\n const [message, obj] = this.#format('WARN', msgOrObj, msg)\n\n if (obj) {\n return console.warn(message, obj)\n }\n\n console.warn(message)\n }\n\n error(msg: string): void\n error(obj: ErrorObject, msg: string): void\n error(msgOrObj: string | ErrorObject, msg?: string): void {\n const [message, obj] = this.#format('ERROR', msgOrObj, msg)\n\n if (obj) {\n return console.error(message, obj)\n }\n\n console.error(message)\n }\n\n child(obj: LogObject): Logger {\n const childPrefix = obj.pkg ? String(obj.pkg) : this.#prefix\n return new ConsoleLogger(childPrefix)\n }\n}\n\nexport const consoleLogger = new ConsoleLogger()\n","import * as errors from './exceptions.js'\nimport debug from './debug.js'\nimport { Locator } from './locator.js'\nimport { consoleLogger, type Logger } from './logger.js'\nimport type { Adapter } from './contracts/adapter.js'\nimport type {\n AdapterFactory,\n JobFactory,\n QueueConfig,\n QueueManagerConfig,\n RetryConfig,\n} from './types/main.js'\n\n/**\n * Central configuration and adapter management for the queue system.\n *\n * The QueueManager is responsible for:\n * - Initializing adapters and job registration\n * - Providing adapter instances to workers and dispatchers\n * - Managing retry configuration across global, queue, and job levels\n *\n * @example\n * ```typescript\n * import { QueueManager, redis } from '@boringnode/queue'\n *\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis({ host: 'localhost' }),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * retry: {\n * maxRetries: 3,\n * backoff: exponentialBackoff(),\n * },\n * })\n *\n * // Get the default adapter\n * const adapter = QueueManager.use()\n *\n * // Clean up when done\n * await QueueManager.destroy()\n * ```\n */\nclass QueueManagerSingleton {\n #initialized = false\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #adapterInstances: Map<string, Adapter> = new Map()\n #globalRetryConfig?: RetryConfig\n #queueConfigs: Map<string, QueueConfig> = new Map()\n #logger: Logger = consoleLogger\n #jobFactory?: JobFactory\n\n /**\n * Initialize the queue system with the given configuration.\n *\n * This must be called before using the queue system. It:\n * - Validates the configuration\n * - Registers adapters\n * - Auto-discovers and registers job classes from `locations`\n *\n * @param config - The queue configuration\n * @returns This instance for chaining\n * @throws {E_CONFIGURATION_ERROR} If the configuration is invalid\n *\n * @example\n * ```typescript\n * await QueueManager.init({\n * default: 'redis',\n * adapters: {\n * redis: redis(),\n * postgres: knex(pgConfig),\n * },\n * locations: ['./jobs/**\\/*.js'],\n * })\n * ```\n */\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#adapterInstances.clear()\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n this.#logger = config.logger ?? consoleLogger\n this.#jobFactory = config.jobFactory\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n if (config.locations && config.locations.length > 0) {\n const registered = await Locator.registerFromGlob(config.locations)\n\n if (registered === 0) {\n this.#logger.warn(\n `No jobs found for locations: ${config.locations.join(', ')}. ` +\n 'Verify your glob patterns match your job files.'\n )\n }\n }\n\n this.#initialized = true\n\n return this\n }\n\n /**\n * Get an adapter instance by name.\n *\n * Adapter instances are cached and reused. If no name is provided,\n * the default adapter is returned.\n *\n * @param adapter - Adapter name (optional, defaults to the default adapter)\n * @returns The adapter instance\n * @throws {E_QUEUE_NOT_INITIALIZED} If `init()` hasn't been called\n * @throws {E_CONFIGURATION_ERROR} If the adapter is not registered\n * @throws {E_ADAPTER_INIT_ERROR} If the adapter factory throws\n *\n * @example\n * ```typescript\n * // Get default adapter\n * const adapter = QueueManager.use()\n *\n * // Get specific adapter\n * const redisAdapter = QueueManager.use('redis')\n * ```\n */\n use(adapter?: string): Adapter {\n if (!this.#initialized) {\n throw new errors.E_QUEUE_NOT_INITIALIZED()\n }\n\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n // Return cached instance if exists\n const cached = this.#adapterInstances.get(adapter)\n if (cached) {\n return cached\n }\n\n const adapterFactory = this.#adapters[adapter]\n\n if (!adapterFactory) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n const instance = adapterFactory()\n this.#adapterInstances.set(adapter, instance)\n return instance\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new errors.E_ADAPTER_INIT_ERROR([adapter, message])\n }\n }\n\n /**\n * Get the merged retry configuration for a job.\n *\n * Configuration is merged with priority: job > queue > global.\n * This allows specific jobs or queues to override global defaults.\n *\n * @param queue - The queue name\n * @param jobRetryConfig - Optional job-level retry config\n * @returns The merged retry configuration\n *\n * @example\n * ```typescript\n * // Global: maxRetries=3, Queue: maxRetries=5, Job: maxRetries=1\n * // Result: maxRetries=1 (job wins)\n * const config = QueueManager.getMergedRetryConfig('emails', { maxRetries: 1 })\n * ```\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ||\n queueRetryConfig.maxRetries ||\n this.#globalRetryConfig?.maxRetries ||\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n /**\n * Get the configured job factory for custom instantiation.\n *\n * @returns The job factory function, or undefined if not configured\n */\n getJobFactory(): JobFactory | undefined {\n return this.#jobFactory\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n /**\n * Clean up all adapter instances and reset state.\n *\n * Call this when shutting down the application or when\n * you need to reinitialize with a new configuration.\n *\n * @example\n * ```typescript\n * // On application shutdown\n * await QueueManager.destroy()\n * ```\n */\n async destroy() {\n for (const [name, adapter] of this.#adapterInstances) {\n debug('destroying adapter \"%s\"', name)\n await adapter.destroy()\n }\n this.#adapterInstances.clear()\n this.#initialized = false\n }\n}\n\n/** Global queue manager singleton */\nexport const QueueManager = new QueueManagerSingleton()\n"],"mappings":"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","names":[]}
|