@rpcbase/worker 0.41.0 → 0.43.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/dist/index.js CHANGED
@@ -2,592 +2,504 @@ import domain from "node:domain";
2
2
  import { Queue, Worker } from "bullmq";
3
3
  import { MongoClient } from "mongodb";
4
4
  import mongoose from "mongoose";
5
- const DEFAULT_QUEUE_NAME = "rb-queue-default";
6
- const DEFAULT_CONCURRENCY = 2;
7
- const DEFAULT_RENTENTION = 128;
8
- const tasksByName = /* @__PURE__ */ Object.create(null);
9
- let queueInstance = null;
10
- let workerInstance = null;
11
- let hasStarted = false;
12
- const getRedisUrl = () => {
13
- const redisUrl = process.env.REDIS_URL?.trim();
14
- if (!redisUrl) {
15
- throw new Error("Missing REDIS_URL (required for @rpcbase/worker queue)");
16
- }
17
- return redisUrl;
5
+ //#region src/queue.ts
6
+ var DEFAULT_QUEUE_NAME = "rb-queue-default";
7
+ var DEFAULT_CONCURRENCY = 2;
8
+ var DEFAULT_RENTENTION = 128;
9
+ var tasksByName = Object.create(null);
10
+ var queueInstance = null;
11
+ var workerInstance = null;
12
+ var hasStarted = false;
13
+ var getRedisUrl = () => {
14
+ const redisUrl = process.env.REDIS_URL?.trim();
15
+ if (!redisUrl) throw new Error("Missing REDIS_URL (required for @rpcbase/worker queue)");
16
+ return redisUrl;
18
17
  };
19
- const getQueueName = () => process.env.RB_QUEUE_NAME?.trim() || DEFAULT_QUEUE_NAME;
20
- const getConcurrency = () => {
21
- const raw = process.env.RB_QUEUE_CONCURRENCY?.trim();
22
- const value = raw ? Number(raw) : DEFAULT_CONCURRENCY;
23
- if (!Number.isFinite(value) || value <= 0) return DEFAULT_CONCURRENCY;
24
- return Math.floor(value);
18
+ var getQueueName = () => process.env.RB_QUEUE_NAME?.trim() || DEFAULT_QUEUE_NAME;
19
+ var getConcurrency = () => {
20
+ const raw = process.env.RB_QUEUE_CONCURRENCY?.trim();
21
+ const value = raw ? Number(raw) : DEFAULT_CONCURRENCY;
22
+ if (!Number.isFinite(value) || value <= 0) return DEFAULT_CONCURRENCY;
23
+ return Math.floor(value);
25
24
  };
26
- const getConnection = () => ({
27
- url: getRedisUrl()
28
- });
29
- const toError = (error) => {
30
- if (error instanceof Error) return error;
31
- const message = typeof error === "string" ? error : String(error ?? "unknown error");
32
- return new Error(message);
25
+ var getConnection = () => ({ url: getRedisUrl() });
26
+ var toError = (error) => {
27
+ if (error instanceof Error) return error;
28
+ return new Error(typeof error === "string" ? error : String(error ?? "unknown error"));
33
29
  };
34
- const runTaskHandler = async (job, handler) => {
35
- return await new Promise((resolve, reject) => {
36
- const taskDomain = domain.create();
37
- let settled = false;
38
- const settle = (next, value) => {
39
- if (settled) return;
40
- settled = true;
41
- taskDomain.removeAllListeners();
42
- next(value);
43
- };
44
- taskDomain.on("error", (error) => {
45
- const normalizedError = toError(error);
46
- settle(reject, normalizedError);
47
- });
48
- taskDomain.run(() => {
49
- Promise.resolve(handler(job.data, job)).then((result) => settle(resolve, result), (error) => settle(reject, toError(error)));
50
- });
51
- });
30
+ var runTaskHandler = async (job, handler) => {
31
+ return await new Promise((resolve, reject) => {
32
+ const taskDomain = domain.create();
33
+ let settled = false;
34
+ const settle = (next, value) => {
35
+ if (settled) return;
36
+ settled = true;
37
+ taskDomain.removeAllListeners();
38
+ next(value);
39
+ };
40
+ taskDomain.on("error", (error) => {
41
+ settle(reject, toError(error));
42
+ });
43
+ taskDomain.run(() => {
44
+ Promise.resolve(handler(job.data, job)).then((result) => settle(resolve, result), (error) => settle(reject, toError(error)));
45
+ });
46
+ });
52
47
  };
53
- const ensureQueue = () => {
54
- if (queueInstance) return queueInstance;
55
- queueInstance = new Queue(getQueueName(), {
56
- connection: getConnection(),
57
- defaultJobOptions: {
58
- removeOnComplete: DEFAULT_RENTENTION,
59
- removeOnFail: DEFAULT_RENTENTION
60
- }
61
- });
62
- return queueInstance;
48
+ var ensureQueue = () => {
49
+ if (queueInstance) return queueInstance;
50
+ queueInstance = new Queue(getQueueName(), {
51
+ connection: getConnection(),
52
+ defaultJobOptions: {
53
+ removeOnComplete: DEFAULT_RENTENTION,
54
+ removeOnFail: DEFAULT_RENTENTION
55
+ }
56
+ });
57
+ return queueInstance;
63
58
  };
64
59
  function registerTask(name, handler) {
65
- tasksByName[name] = handler;
60
+ tasksByName[name] = handler;
66
61
  }
67
- const getTasks = () => tasksByName;
62
+ var getTasks = () => tasksByName;
68
63
  function add(taskName, payload, options) {
69
- return ensureQueue().add(taskName, payload, options);
64
+ return ensureQueue().add(taskName, payload, options);
70
65
  }
71
- const getJob = async (jobId) => await ensureQueue().getJob(jobId) ?? null;
72
- const getJobs = async (...args) => ensureQueue().getJobs(...args);
73
- const getInstance = () => ensureQueue();
74
- const start = async () => {
75
- if (hasStarted) return ensureQueue();
76
- hasStarted = true;
77
- const queue = ensureQueue();
78
- const concurrency = getConcurrency();
79
- console.log("start worker queue", {
80
- queue: queue.name,
81
- redisUrl: getRedisUrl(),
82
- concurrency
83
- });
84
- queue.on("error", (err) => {
85
- console.log(`queue error: ${err.message}`);
86
- });
87
- if (!workerInstance) {
88
- workerInstance = new Worker(queue.name, async (job) => {
89
- const taskName = job.name;
90
- const handler = tasksByName[taskName];
91
- if (!handler) {
92
- throw new Error(`No task registered for '${taskName}'`);
93
- }
94
- return await runTaskHandler(job, handler);
95
- }, {
96
- concurrency,
97
- connection: getConnection()
98
- });
99
- }
100
- workerInstance.on("error", (err) => {
101
- console.log(`worker error: ${err.message}`);
102
- });
103
- workerInstance.on("stalled", (jobId) => {
104
- console.log(`job ${jobId} stalled`);
105
- });
106
- workerInstance.on("failed", (job, err) => {
107
- console.log(`job ${job?.id ?? "unknown"} failed`, err);
108
- });
109
- await workerInstance.waitUntilReady();
110
- return queue;
66
+ var getJob = async (jobId) => await ensureQueue().getJob(jobId) ?? null;
67
+ var getJobs = async (...args) => ensureQueue().getJobs(...args);
68
+ var getInstance = () => ensureQueue();
69
+ var start = async () => {
70
+ if (hasStarted) return ensureQueue();
71
+ hasStarted = true;
72
+ const queue = ensureQueue();
73
+ const concurrency = getConcurrency();
74
+ console.log("start worker queue", {
75
+ queue: queue.name,
76
+ redisUrl: getRedisUrl(),
77
+ concurrency
78
+ });
79
+ queue.on("error", (err) => {
80
+ console.log(`queue error: ${err.message}`);
81
+ });
82
+ if (!workerInstance) workerInstance = new Worker(queue.name, async (job) => {
83
+ const taskName = job.name;
84
+ const handler = tasksByName[taskName];
85
+ if (!handler) throw new Error(`No task registered for '${taskName}'`);
86
+ return await runTaskHandler(job, handler);
87
+ }, {
88
+ concurrency,
89
+ connection: getConnection()
90
+ });
91
+ workerInstance.on("error", (err) => {
92
+ console.log(`worker error: ${err.message}`);
93
+ });
94
+ workerInstance.on("stalled", (jobId) => {
95
+ console.log(`job ${jobId} stalled`);
96
+ });
97
+ workerInstance.on("failed", (job, err) => {
98
+ console.log(`job ${job?.id ?? "unknown"} failed`, err);
99
+ });
100
+ await workerInstance.waitUntilReady();
101
+ return queue;
111
102
  };
112
- const close = async () => {
113
- if (!queueInstance && !workerInstance) return;
114
- try {
115
- await workerInstance?.close();
116
- await queueInstance?.close();
117
- } finally {
118
- workerInstance = null;
119
- queueInstance = null;
120
- hasStarted = false;
121
- }
103
+ var close = async () => {
104
+ if (!queueInstance && !workerInstance) return;
105
+ try {
106
+ await workerInstance?.close();
107
+ await queueInstance?.close();
108
+ } finally {
109
+ workerInstance = null;
110
+ queueInstance = null;
111
+ hasStarted = false;
112
+ }
122
113
  };
123
- const getUrl = () => getRedisUrl();
114
+ var getUrl = () => getRedisUrl();
124
115
  async function scheduleTask(taskName, payload, options) {
125
- const {
126
- jobId,
127
- repeat,
128
- ...rest
129
- } = options;
130
- return add(taskName, payload, {
131
- ...rest,
132
- jobId: jobId ?? `schedule|${taskName}`,
133
- repeat
134
- });
116
+ const { jobId, repeat, ...rest } = options;
117
+ return add(taskName, payload, {
118
+ ...rest,
119
+ jobId: jobId ?? `schedule|${taskName}`,
120
+ repeat
121
+ });
135
122
  }
136
- const queueApi = {
137
- start,
138
- close,
139
- registerTask,
140
- getTasks,
141
- add,
142
- scheduleTask,
143
- getJob,
144
- getJobs,
145
- getInstance,
146
- getUrl
123
+ var queueApi = {
124
+ start,
125
+ close,
126
+ registerTask,
127
+ getTasks,
128
+ add,
129
+ scheduleTask,
130
+ getJob,
131
+ getJobs,
132
+ getInstance,
133
+ getUrl
147
134
  };
148
- const RETRY_MAXIMUM_DELAY_MS = 3e3;
149
- const RETRY_MINIMUM_DELAY_MS = 50;
150
- const RETRY_DEFAULT_FACTOR = 2;
151
- const sleep = async (ms, signal) => new Promise((resolve) => {
152
- if (signal?.aborted) {
153
- resolve();
154
- return;
155
- }
156
- let timeout = null;
157
- const cleanup = () => {
158
- if (timeout) clearTimeout(timeout);
159
- signal?.removeEventListener("abort", onAbort);
160
- };
161
- const onAbort = () => {
162
- cleanup();
163
- resolve();
164
- };
165
- timeout = setTimeout(() => {
166
- cleanup();
167
- resolve();
168
- }, ms);
169
- timeout.unref?.();
170
- signal?.addEventListener("abort", onAbort);
135
+ //#endregion
136
+ //#region src/queueListener.ts
137
+ var RETRY_MAXIMUM_DELAY_MS = 3e3;
138
+ var RETRY_MINIMUM_DELAY_MS = 50;
139
+ var RETRY_DEFAULT_FACTOR = 2;
140
+ var sleep = async (ms, signal) => new Promise((resolve) => {
141
+ if (signal?.aborted) {
142
+ resolve();
143
+ return;
144
+ }
145
+ let timeout = null;
146
+ const cleanup = () => {
147
+ if (timeout) clearTimeout(timeout);
148
+ signal?.removeEventListener("abort", onAbort);
149
+ };
150
+ const onAbort = () => {
151
+ cleanup();
152
+ resolve();
153
+ };
154
+ timeout = setTimeout(() => {
155
+ cleanup();
156
+ resolve();
157
+ }, ms);
158
+ timeout.unref?.();
159
+ signal?.addEventListener("abort", onAbort);
171
160
  });
172
- const getMongoUrl = () => {
173
- const explicit = process.env.MONGODB_URL ?? process.env.MONGO_URL ?? process.env.MONGODB_URI ?? process.env.DB_URL;
174
- if (explicit && explicit.trim()) return explicit.trim();
175
- const port = process.env.DB_PORT?.trim();
176
- if (!port) throw new Error("Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)");
177
- const host = process.env.DB_HOST?.trim() || "localhost";
178
- return `mongodb://${host}:${port}`;
179
- };
180
- const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
181
- const isRecord = (value) => typeof value === "object" && value !== null;
182
- const getDocumentId = (doc) => {
183
- if (!isRecord(doc)) return void 0;
184
- return doc._id;
161
+ var getMongoUrl = () => {
162
+ const explicit = process.env.MONGODB_URL ?? process.env.MONGO_URL ?? process.env.MONGODB_URI ?? process.env.DB_URL;
163
+ if (explicit && explicit.trim()) return explicit.trim();
164
+ const port = process.env.DB_PORT?.trim();
165
+ if (!port) throw new Error("Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)");
166
+ return `mongodb://${process.env.DB_HOST?.trim() || "localhost"}:${port}`;
185
167
  };
186
- const resolveModelNameFromCollection = (collName) => {
187
- const models = mongoose.models;
188
- for (const modelName of Object.keys(models)) {
189
- const model = models[modelName];
190
- const collectionName = model.collection?.collectionName ?? model.collection?.name;
191
- if (collectionName === collName) {
192
- return modelName;
193
- }
194
- }
195
- return null;
168
+ var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
169
+ var isRecord = (value) => typeof value === "object" && value !== null;
170
+ var getDocumentId = (doc) => {
171
+ if (!isRecord(doc)) return void 0;
172
+ return doc._id;
196
173
  };
197
- const normalizeUpdateDescription = (updateDescription) => {
198
- if (!isRecord(updateDescription)) return updateDescription;
199
- if (isRecord(updateDescription.updatedFields)) {
200
- return {
201
- ...updateDescription,
202
- updatedFieldsKeys: Object.keys(updateDescription.updatedFields)
203
- };
204
- }
205
- return updateDescription;
174
+ var resolveModelNameFromCollection = (collName) => {
175
+ const models = mongoose.models;
176
+ for (const modelName of Object.keys(models)) {
177
+ const model = models[modelName];
178
+ if ((model.collection?.collectionName ?? model.collection?.name) === collName) return modelName;
179
+ }
180
+ return null;
206
181
  };
207
- const normalizeOpForTaskName = (op) => op === "replace" ? "update" : op;
208
- const getTenantIdFromDbName = (dbName, appName) => {
209
- const escapedAppName = escapeRegex(appName);
210
- const match = dbName.match(new RegExp(`^${escapedAppName}-(.+)-db$`));
211
- const tenantId = match?.[1]?.trim();
212
- return tenantId ? tenantId : null;
182
+ var normalizeUpdateDescription = (updateDescription) => {
183
+ if (!isRecord(updateDescription)) return updateDescription;
184
+ if (isRecord(updateDescription.updatedFields)) return {
185
+ ...updateDescription,
186
+ updatedFieldsKeys: Object.keys(updateDescription.updatedFields)
187
+ };
188
+ return updateDescription;
213
189
  };
214
- const dispatchWorkerQueue = async ({
215
- dbName,
216
- tenantId,
217
- modelName,
218
- op,
219
- doc,
220
- updateDescription
221
- }) => {
222
- const tasks = queueApi.getTasks();
223
- const taskOp = normalizeOpForTaskName(op);
224
- const handlerName = `on-${taskOp}-${modelName}`;
225
- if (!tasks[handlerName]) return;
226
- const normalizedUpdateDescription = normalizeUpdateDescription(updateDescription);
227
- await queueApi.add(handlerName, {
228
- tenantId,
229
- doc,
230
- updateDescription: normalizedUpdateDescription
231
- }, {
232
- jobId: `${dbName}|${taskOp}-${getDocumentId(doc) ?? "unknown"}`,
233
- removeOnComplete: true,
234
- removeOnFail: true
235
- });
190
+ var normalizeOpForTaskName = (op) => op === "replace" ? "update" : op;
191
+ var getTenantIdFromDbName = (dbName, appName) => {
192
+ const escapedAppName = escapeRegex(appName);
193
+ const tenantId = dbName.match(new RegExp(`^${escapedAppName}-(.+)-db$`))?.[1]?.trim();
194
+ return tenantId ? tenantId : null;
236
195
  };
237
- const shouldSkipCollection = (collName) => collName.endsWith(".files") || collName.endsWith(".chunks");
238
- const INTERNAL_IGNORED_MODEL_NAMES = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
239
- const INTERNAL_IGNORED_COLLECTION_NAMES = /* @__PURE__ */ new Set(["rtschanges", "rtscounters"]);
240
- const normalizeRetryDelays = (input) => {
241
- const minMs = Math.max(0, Math.floor(input?.minMs ?? RETRY_MINIMUM_DELAY_MS));
242
- const rawMaxMs = Math.max(0, Math.floor(input?.maxMs ?? RETRY_MAXIMUM_DELAY_MS));
243
- const maxMs = Math.max(minMs, rawMaxMs);
244
- const factor = Math.max(1, Number.isFinite(input?.factor) ? input?.factor ?? RETRY_DEFAULT_FACTOR : RETRY_DEFAULT_FACTOR);
245
- return {
246
- minMs,
247
- maxMs,
248
- factor
249
- };
196
+ var dispatchWorkerQueue = async ({ dbName, tenantId, modelName, op, doc, updateDescription }) => {
197
+ const tasks = queueApi.getTasks();
198
+ const taskOp = normalizeOpForTaskName(op);
199
+ const handlerName = `on-${taskOp}-${modelName}`;
200
+ if (!tasks[handlerName]) return;
201
+ const normalizedUpdateDescription = normalizeUpdateDescription(updateDescription);
202
+ await queueApi.add(handlerName, {
203
+ tenantId,
204
+ doc,
205
+ updateDescription: normalizedUpdateDescription
206
+ }, {
207
+ jobId: `${dbName}|${taskOp}-${getDocumentId(doc) ?? "unknown"}`,
208
+ removeOnComplete: true,
209
+ removeOnFail: true
210
+ });
250
211
  };
251
- const getRetryDelayMs = (attempt, delays) => Math.min(delays.maxMs, Math.round(delays.minMs * Math.pow(delays.factor, Math.max(0, attempt - 1))));
252
- const normalizeMaxRetries = (value) => {
253
- if (value === "infinite") return value;
254
- const parsed = Math.floor(value);
255
- return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
212
+ var shouldSkipCollection = (collName) => collName.endsWith(".files") || collName.endsWith(".chunks");
213
+ var INTERNAL_IGNORED_MODEL_NAMES = new Set(["RBRtsChange", "RBRtsCounter"]);
214
+ var INTERNAL_IGNORED_COLLECTION_NAMES = new Set(["rtschanges", "rtscounters"]);
215
+ var normalizeRetryDelays = (input) => {
216
+ const minMs = Math.max(0, Math.floor(input?.minMs ?? RETRY_MINIMUM_DELAY_MS));
217
+ const rawMaxMs = Math.max(0, Math.floor(input?.maxMs ?? RETRY_MAXIMUM_DELAY_MS));
218
+ return {
219
+ minMs,
220
+ maxMs: Math.max(minMs, rawMaxMs),
221
+ factor: Math.max(1, Number.isFinite(input?.factor) ? input?.factor ?? RETRY_DEFAULT_FACTOR : RETRY_DEFAULT_FACTOR)
222
+ };
256
223
  };
257
- const registerQueueListener = async (options = {}) => {
258
- const maxRetries = normalizeMaxRetries(options.maxRetries ?? "infinite");
259
- const fatalOnMaxRetries = options.fatalOnMaxRetries ?? false;
260
- const retryDelays = normalizeRetryDelays(options.retryDelays);
261
- const appName = process.env.APP_NAME?.trim();
262
- if (!appName) {
263
- throw new Error("Missing APP_NAME (required to configure the worker DB change listener)");
264
- }
265
- const mongoUrl = getMongoUrl();
266
- const mongoClientOptions = {
267
- family: 4,
268
- serverSelectionTimeoutMS: 2e3,
269
- connectTimeoutMS: 2e3,
270
- ...options.mongoClientOptions
271
- };
272
- let stopped = false;
273
- const abortController = new AbortController();
274
- let client = null;
275
- let stream = null;
276
- let resumeAfter = null;
277
- let processing = Promise.resolve();
278
- let status = {
279
- state: "connecting",
280
- attempt: 1
281
- };
282
- const setStatus = (next) => {
283
- status = next;
284
- try {
285
- options.onStateChange?.(next);
286
- } catch (err) {
287
- console.warn("queue listener onStateChange failed", err);
288
- }
289
- };
290
- let readySettled = false;
291
- let readyResolve = null;
292
- let readyReject = null;
293
- const ready = new Promise((resolve, reject) => {
294
- readyResolve = resolve;
295
- readyReject = reject;
296
- });
297
- const resolveReady = () => {
298
- if (readySettled) return;
299
- readySettled = true;
300
- readyResolve?.();
301
- };
302
- const rejectReady = (err) => {
303
- if (readySettled) return;
304
- readySettled = true;
305
- readyReject?.(err);
306
- };
307
- const isChangeStreamHistoryLost = (err) => {
308
- const maybeErr = err;
309
- const code = typeof maybeErr.code === "number" ? maybeErr.code : null;
310
- const codeName = typeof maybeErr.codeName === "string" ? maybeErr.codeName : "";
311
- const message = err instanceof Error ? err.message : String(err ?? "");
312
- return code === 286 || codeName === "ChangeStreamHistoryLost" || message.includes("ChangeStreamHistoryLost") || message.includes("resume token") || message.includes("Resume token") || message.includes("resume of change stream was not possible") || message.includes("cannot resume");
313
- };
314
- const close2 = async () => {
315
- stopped = true;
316
- abortController.abort();
317
- try {
318
- stream?.removeAllListeners();
319
- await stream?.close();
320
- } catch {
321
- }
322
- stream = null;
323
- try {
324
- await client?.close();
325
- } catch {
326
- }
327
- client = null;
328
- if (!readySettled) {
329
- rejectReady(new Error("queue listener closed before ready"));
330
- }
331
- setStatus({
332
- state: "closed"
333
- });
334
- };
335
- const closeResources = async () => {
336
- try {
337
- stream?.removeAllListeners();
338
- await stream?.close();
339
- } catch {
340
- }
341
- stream = null;
342
- try {
343
- await client?.close();
344
- } catch {
345
- }
346
- client = null;
347
- };
348
- const startStream = async () => {
349
- if (stopped) return {
350
- stoppedPromise: Promise.resolve({
351
- reason: "close"
352
- })
353
- };
354
- if (stream) {
355
- try {
356
- stream.removeAllListeners();
357
- await stream.close();
358
- } catch {
359
- }
360
- stream = null;
361
- }
362
- if (client) {
363
- try {
364
- await client.close();
365
- } catch {
366
- }
367
- client = null;
368
- }
369
- client = new MongoClient(mongoUrl, mongoClientOptions);
370
- await client.connect();
371
- const dbMatch = {
372
- "ns.db": {
373
- $regex: `^${escapeRegex(appName)}-.*-db$`
374
- }
375
- };
376
- const pipeline = [{
377
- $match: dbMatch
378
- }, {
379
- $match: {
380
- operationType: {
381
- $in: ["insert", "update", "replace", "delete"]
382
- }
383
- }
384
- }, {
385
- $match: {
386
- "ns.coll": {
387
- $nin: Array.from(INTERNAL_IGNORED_COLLECTION_NAMES)
388
- }
389
- }
390
- }];
391
- stream = client.watch(pipeline, {
392
- fullDocument: "updateLookup",
393
- ...resumeAfter ? {
394
- resumeAfter
395
- } : {}
396
- });
397
- const stoppedPromise = new Promise((resolve) => {
398
- let settled = false;
399
- const onAbort = () => settle({
400
- reason: "close"
401
- });
402
- const settle = (result) => {
403
- if (settled) return;
404
- settled = true;
405
- abortController.signal.removeEventListener("abort", onAbort);
406
- resolve(result);
407
- };
408
- abortController.signal.addEventListener("abort", onAbort);
409
- stream?.once("close", () => settle({
410
- reason: "close"
411
- }));
412
- stream?.once("error", (err) => settle({
413
- reason: "error",
414
- error: err
415
- }));
416
- });
417
- stream.on("change", (change) => {
418
- const streamRef = stream;
419
- processing = processing.then(async () => {
420
- const ns = "ns" in change ? change.ns : void 0;
421
- const dbName = String(ns?.db ?? "");
422
- if (!dbName) return;
423
- const tenantId = getTenantIdFromDbName(dbName, appName);
424
- if (!tenantId) return;
425
- const collName = String(ns && "coll" in ns ? ns.coll : "");
426
- if (!collName) return;
427
- if (shouldSkipCollection(collName)) return;
428
- if (INTERNAL_IGNORED_COLLECTION_NAMES.has(collName)) return;
429
- const modelName = resolveModelNameFromCollection(collName);
430
- if (!modelName) return;
431
- if (INTERNAL_IGNORED_MODEL_NAMES.has(modelName)) return;
432
- const op = String(change.operationType ?? "");
433
- if (!op) return;
434
- const normalizedOp = normalizeOpForTaskName(op);
435
- let doc = "fullDocument" in change ? change.fullDocument : void 0;
436
- if (!doc && normalizedOp === "delete") {
437
- doc = "documentKey" in change ? change.documentKey : void 0;
438
- }
439
- if (!doc) return;
440
- const updateDescription = "updateDescription" in change ? change.updateDescription : void 0;
441
- try {
442
- await dispatchWorkerQueue({
443
- dbName,
444
- tenantId,
445
- modelName,
446
- op: normalizedOp,
447
- doc,
448
- updateDescription
449
- });
450
- resumeAfter = change?._id ?? resumeAfter;
451
- } catch (err) {
452
- console.warn("queue listener failed to dispatch change", err);
453
- try {
454
- await streamRef?.close();
455
- } catch {
456
- }
457
- }
458
- }).catch((err) => {
459
- console.warn("queue listener change handler failed", err);
460
- });
461
- });
462
- stream.on("error", (err) => {
463
- if (stopped) return;
464
- if (resumeAfter && isChangeStreamHistoryLost(err)) {
465
- resumeAfter = null;
466
- }
467
- try {
468
- void Promise.resolve(stream?.close()).catch(() => {
469
- });
470
- } catch {
471
- }
472
- });
473
- return {
474
- stoppedPromise
475
- };
476
- };
477
- const run = async () => {
478
- let retryCounter = 0;
479
- while (!stopped) {
480
- try {
481
- setStatus({
482
- state: "connecting",
483
- attempt: retryCounter + 1
484
- });
485
- const {
486
- stoppedPromise
487
- } = await startStream();
488
- retryCounter = 0;
489
- setStatus({
490
- state: "ready"
491
- });
492
- resolveReady();
493
- const end = await stoppedPromise;
494
- if (stopped) return;
495
- retryCounter += 1;
496
- if (maxRetries !== "infinite" && retryCounter > maxRetries) {
497
- const err2 = end.reason === "error" ? end.error : new Error("queue listener closed");
498
- setStatus({
499
- state: "failed",
500
- attempt: retryCounter,
501
- error: err2
502
- });
503
- if (fatalOnMaxRetries) {
504
- try {
505
- options.onFatal?.(err2);
506
- } catch (fatalErr) {
507
- console.warn("queue listener onFatal failed", fatalErr);
508
- }
509
- }
510
- rejectReady(err2);
511
- abortController.abort();
512
- await closeResources();
513
- return;
514
- }
515
- const delayMs = getRetryDelayMs(retryCounter, retryDelays);
516
- const err = end.reason === "error" ? end.error : new Error("queue listener closed");
517
- console.warn("queue listener not ready, retrying in", delayMs, err);
518
- setStatus({
519
- state: "error",
520
- attempt: retryCounter,
521
- error: err,
522
- nextRetryInMs: delayMs
523
- });
524
- await closeResources();
525
- await sleep(delayMs, abortController.signal);
526
- } catch (err) {
527
- if (stopped) return;
528
- retryCounter += 1;
529
- if (maxRetries !== "infinite" && retryCounter > maxRetries) {
530
- setStatus({
531
- state: "failed",
532
- attempt: retryCounter,
533
- error: err
534
- });
535
- if (fatalOnMaxRetries) {
536
- try {
537
- options.onFatal?.(err);
538
- } catch (fatalErr) {
539
- console.warn("queue listener onFatal failed", fatalErr);
540
- }
541
- }
542
- rejectReady(err);
543
- abortController.abort();
544
- await closeResources();
545
- return;
546
- }
547
- const delayMs = getRetryDelayMs(retryCounter, retryDelays);
548
- console.warn("queue listener not ready, retrying in", delayMs, err);
549
- setStatus({
550
- state: "error",
551
- attempt: retryCounter,
552
- error: err,
553
- nextRetryInMs: delayMs
554
- });
555
- await closeResources();
556
- await sleep(delayMs, abortController.signal);
557
- }
558
- }
559
- };
560
- const handle = {
561
- ready,
562
- close: close2,
563
- getStatus: () => status
564
- };
565
- void run().catch(async (err) => {
566
- if (stopped) return;
567
- setStatus({
568
- state: "failed",
569
- attempt: 0,
570
- error: err
571
- });
572
- rejectReady(err);
573
- await closeResources();
574
- });
575
- return handle;
224
+ var getRetryDelayMs = (attempt, delays) => Math.min(delays.maxMs, Math.round(delays.minMs * Math.pow(delays.factor, Math.max(0, attempt - 1))));
225
+ var normalizeMaxRetries = (value) => {
226
+ if (value === "infinite") return value;
227
+ const parsed = Math.floor(value);
228
+ return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
576
229
  };
577
- const dbEventTaskName = (op, modelName) => `on-${op}-${modelName}`;
578
- export {
579
- add,
580
- close,
581
- dbEventTaskName,
582
- getInstance,
583
- getJob,
584
- getJobs,
585
- getTasks,
586
- getUrl,
587
- queueApi as queue,
588
- registerQueueListener,
589
- registerTask,
590
- scheduleTask,
591
- start
230
+ var registerQueueListener = async (options = {}) => {
231
+ const maxRetries = normalizeMaxRetries(options.maxRetries ?? "infinite");
232
+ const fatalOnMaxRetries = options.fatalOnMaxRetries ?? false;
233
+ const retryDelays = normalizeRetryDelays(options.retryDelays);
234
+ const appName = process.env.APP_NAME?.trim();
235
+ if (!appName) throw new Error("Missing APP_NAME (required to configure the worker DB change listener)");
236
+ const mongoUrl = getMongoUrl();
237
+ const mongoClientOptions = {
238
+ family: 4,
239
+ serverSelectionTimeoutMS: 2e3,
240
+ connectTimeoutMS: 2e3,
241
+ ...options.mongoClientOptions
242
+ };
243
+ let stopped = false;
244
+ const abortController = new AbortController();
245
+ let client = null;
246
+ let stream = null;
247
+ let resumeAfter = null;
248
+ let processing = Promise.resolve();
249
+ let status = {
250
+ state: "connecting",
251
+ attempt: 1
252
+ };
253
+ const setStatus = (next) => {
254
+ status = next;
255
+ try {
256
+ options.onStateChange?.(next);
257
+ } catch (err) {
258
+ console.warn("queue listener onStateChange failed", err);
259
+ }
260
+ };
261
+ let readySettled = false;
262
+ let readyResolve = null;
263
+ let readyReject = null;
264
+ const ready = new Promise((resolve, reject) => {
265
+ readyResolve = resolve;
266
+ readyReject = reject;
267
+ });
268
+ const resolveReady = () => {
269
+ if (readySettled) return;
270
+ readySettled = true;
271
+ readyResolve?.();
272
+ };
273
+ const rejectReady = (err) => {
274
+ if (readySettled) return;
275
+ readySettled = true;
276
+ readyReject?.(err);
277
+ };
278
+ const isChangeStreamHistoryLost = (err) => {
279
+ const maybeErr = err;
280
+ const code = typeof maybeErr.code === "number" ? maybeErr.code : null;
281
+ const codeName = typeof maybeErr.codeName === "string" ? maybeErr.codeName : "";
282
+ const message = err instanceof Error ? err.message : String(err ?? "");
283
+ return code === 286 || codeName === "ChangeStreamHistoryLost" || message.includes("ChangeStreamHistoryLost") || message.includes("resume token") || message.includes("Resume token") || message.includes("resume of change stream was not possible") || message.includes("cannot resume");
284
+ };
285
+ const close = async () => {
286
+ stopped = true;
287
+ abortController.abort();
288
+ try {
289
+ stream?.removeAllListeners();
290
+ await stream?.close();
291
+ } catch {}
292
+ stream = null;
293
+ try {
294
+ await client?.close();
295
+ } catch {}
296
+ client = null;
297
+ if (!readySettled) rejectReady(/* @__PURE__ */ new Error("queue listener closed before ready"));
298
+ setStatus({ state: "closed" });
299
+ };
300
+ const closeResources = async () => {
301
+ try {
302
+ stream?.removeAllListeners();
303
+ await stream?.close();
304
+ } catch {}
305
+ stream = null;
306
+ try {
307
+ await client?.close();
308
+ } catch {}
309
+ client = null;
310
+ };
311
+ const startStream = async () => {
312
+ if (stopped) return { stoppedPromise: Promise.resolve({ reason: "close" }) };
313
+ if (stream) {
314
+ try {
315
+ stream.removeAllListeners();
316
+ await stream.close();
317
+ } catch {}
318
+ stream = null;
319
+ }
320
+ if (client) {
321
+ try {
322
+ await client.close();
323
+ } catch {}
324
+ client = null;
325
+ }
326
+ client = new MongoClient(mongoUrl, mongoClientOptions);
327
+ await client.connect();
328
+ const pipeline = [
329
+ { $match: { "ns.db": { $regex: `^${escapeRegex(appName)}-.*-db$` } } },
330
+ { $match: { operationType: { $in: [
331
+ "insert",
332
+ "update",
333
+ "replace",
334
+ "delete"
335
+ ] } } },
336
+ { $match: { "ns.coll": { $nin: Array.from(INTERNAL_IGNORED_COLLECTION_NAMES) } } }
337
+ ];
338
+ stream = client.watch(pipeline, {
339
+ fullDocument: "updateLookup",
340
+ ...resumeAfter ? { resumeAfter } : {}
341
+ });
342
+ const stoppedPromise = new Promise((resolve) => {
343
+ let settled = false;
344
+ const onAbort = () => settle({ reason: "close" });
345
+ const settle = (result) => {
346
+ if (settled) return;
347
+ settled = true;
348
+ abortController.signal.removeEventListener("abort", onAbort);
349
+ resolve(result);
350
+ };
351
+ abortController.signal.addEventListener("abort", onAbort);
352
+ stream?.once("close", () => settle({ reason: "close" }));
353
+ stream?.once("error", (err) => settle({
354
+ reason: "error",
355
+ error: err
356
+ }));
357
+ });
358
+ stream.on("change", (change) => {
359
+ const streamRef = stream;
360
+ processing = processing.then(async () => {
361
+ const ns = "ns" in change ? change.ns : void 0;
362
+ const dbName = String(ns?.db ?? "");
363
+ if (!dbName) return;
364
+ const tenantId = getTenantIdFromDbName(dbName, appName);
365
+ if (!tenantId) return;
366
+ const collName = String(ns && "coll" in ns ? ns.coll : "");
367
+ if (!collName) return;
368
+ if (shouldSkipCollection(collName)) return;
369
+ if (INTERNAL_IGNORED_COLLECTION_NAMES.has(collName)) return;
370
+ const modelName = resolveModelNameFromCollection(collName);
371
+ if (!modelName) return;
372
+ if (INTERNAL_IGNORED_MODEL_NAMES.has(modelName)) return;
373
+ const op = String(change.operationType ?? "");
374
+ if (!op) return;
375
+ const normalizedOp = normalizeOpForTaskName(op);
376
+ let doc = "fullDocument" in change ? change.fullDocument : void 0;
377
+ if (!doc && normalizedOp === "delete") doc = "documentKey" in change ? change.documentKey : void 0;
378
+ if (!doc) return;
379
+ const updateDescription = "updateDescription" in change ? change.updateDescription : void 0;
380
+ try {
381
+ await dispatchWorkerQueue({
382
+ dbName,
383
+ tenantId,
384
+ modelName,
385
+ op: normalizedOp,
386
+ doc,
387
+ updateDescription
388
+ });
389
+ resumeAfter = change?._id ?? resumeAfter;
390
+ } catch (err) {
391
+ console.warn("queue listener failed to dispatch change", err);
392
+ try {
393
+ await streamRef?.close();
394
+ } catch {}
395
+ }
396
+ }).catch((err) => {
397
+ console.warn("queue listener change handler failed", err);
398
+ });
399
+ });
400
+ stream.on("error", (err) => {
401
+ if (stopped) return;
402
+ if (resumeAfter && isChangeStreamHistoryLost(err)) resumeAfter = null;
403
+ try {
404
+ Promise.resolve(stream?.close()).catch(() => {});
405
+ } catch {}
406
+ });
407
+ return { stoppedPromise };
408
+ };
409
+ const run = async () => {
410
+ let retryCounter = 0;
411
+ while (!stopped) try {
412
+ setStatus({
413
+ state: "connecting",
414
+ attempt: retryCounter + 1
415
+ });
416
+ const { stoppedPromise } = await startStream();
417
+ retryCounter = 0;
418
+ setStatus({ state: "ready" });
419
+ resolveReady();
420
+ const end = await stoppedPromise;
421
+ if (stopped) return;
422
+ retryCounter += 1;
423
+ if (maxRetries !== "infinite" && retryCounter > maxRetries) {
424
+ const err = end.reason === "error" ? end.error : /* @__PURE__ */ new Error("queue listener closed");
425
+ setStatus({
426
+ state: "failed",
427
+ attempt: retryCounter,
428
+ error: err
429
+ });
430
+ if (fatalOnMaxRetries) try {
431
+ options.onFatal?.(err);
432
+ } catch (fatalErr) {
433
+ console.warn("queue listener onFatal failed", fatalErr);
434
+ }
435
+ rejectReady(err);
436
+ abortController.abort();
437
+ await closeResources();
438
+ return;
439
+ }
440
+ const delayMs = getRetryDelayMs(retryCounter, retryDelays);
441
+ const err = end.reason === "error" ? end.error : /* @__PURE__ */ new Error("queue listener closed");
442
+ console.warn("queue listener not ready, retrying in", delayMs, err);
443
+ setStatus({
444
+ state: "error",
445
+ attempt: retryCounter,
446
+ error: err,
447
+ nextRetryInMs: delayMs
448
+ });
449
+ await closeResources();
450
+ await sleep(delayMs, abortController.signal);
451
+ } catch (err) {
452
+ if (stopped) return;
453
+ retryCounter += 1;
454
+ if (maxRetries !== "infinite" && retryCounter > maxRetries) {
455
+ setStatus({
456
+ state: "failed",
457
+ attempt: retryCounter,
458
+ error: err
459
+ });
460
+ if (fatalOnMaxRetries) try {
461
+ options.onFatal?.(err);
462
+ } catch (fatalErr) {
463
+ console.warn("queue listener onFatal failed", fatalErr);
464
+ }
465
+ rejectReady(err);
466
+ abortController.abort();
467
+ await closeResources();
468
+ return;
469
+ }
470
+ const delayMs = getRetryDelayMs(retryCounter, retryDelays);
471
+ console.warn("queue listener not ready, retrying in", delayMs, err);
472
+ setStatus({
473
+ state: "error",
474
+ attempt: retryCounter,
475
+ error: err,
476
+ nextRetryInMs: delayMs
477
+ });
478
+ await closeResources();
479
+ await sleep(delayMs, abortController.signal);
480
+ }
481
+ };
482
+ const handle = {
483
+ ready,
484
+ close,
485
+ getStatus: () => status
486
+ };
487
+ run().catch(async (err) => {
488
+ if (stopped) return;
489
+ setStatus({
490
+ state: "failed",
491
+ attempt: 0,
492
+ error: err
493
+ });
494
+ rejectReady(err);
495
+ await closeResources();
496
+ });
497
+ return handle;
592
498
  };
593
- //# sourceMappingURL=index.js.map
499
+ //#endregion
500
+ //#region src/taskNames.ts
501
+ var dbEventTaskName = (op, modelName) => `on-${op}-${modelName}`;
502
+ //#endregion
503
+ export { add, close, dbEventTaskName, getInstance, getJob, getJobs, getTasks, getUrl, queueApi as queue, registerQueueListener, registerTask, scheduleTask, start };
504
+
505
+ //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/queue.ts","../src/queueListener.ts","../src/taskNames.ts"],"sourcesContent":["import domain from \"node:domain\"\n\nimport { Queue, Worker, type Job, type JobsOptions as JobOptions } from \"bullmq\"\n\nimport type { WorkerTasksMap } from \"./tasksMap\"\n\n\nexport type TaskHandler<TPayload = unknown> = (payload: TPayload, job: Job) => unknown | Promise<unknown>\n\nconst DEFAULT_QUEUE_NAME = \"rb-queue-default\"\nconst DEFAULT_CONCURRENCY = 2\nconst DEFAULT_RENTENTION = 128\n\nconst tasksByName: Record<string, TaskHandler<unknown>> = Object.create(null)\n\nlet queueInstance: Queue | null = null\nlet workerInstance: Worker | null = null\nlet hasStarted = false\n\nconst getRedisUrl = (): string => {\n const redisUrl = process.env.REDIS_URL?.trim()\n if (!redisUrl) {\n throw new Error(\"Missing REDIS_URL (required for @rpcbase/worker queue)\")\n }\n return redisUrl\n}\n\nconst getQueueName = (): string => process.env.RB_QUEUE_NAME?.trim() || DEFAULT_QUEUE_NAME\n\nconst getConcurrency = (): number => {\n const raw = process.env.RB_QUEUE_CONCURRENCY?.trim()\n const value = raw ? Number(raw) : DEFAULT_CONCURRENCY\n if (!Number.isFinite(value) || value <= 0) return DEFAULT_CONCURRENCY\n return Math.floor(value)\n}\n\nconst getConnection = () => ({ url: getRedisUrl() })\n\nconst toError = (error: unknown): Error => {\n if (error instanceof Error) return error\n const message = typeof error === \"string\" ? error : String(error ?? \"unknown error\")\n return new Error(message)\n}\n\nconst runTaskHandler = async (job: Job, handler: TaskHandler<unknown>): Promise<unknown> => {\n return await new Promise((resolve, reject) => {\n const taskDomain = domain.create()\n let settled = false\n\n const settle = (next: (value: unknown) => void, value: unknown) => {\n if (settled) return\n settled = true\n taskDomain.removeAllListeners()\n next(value)\n }\n\n taskDomain.on(\"error\", (error: unknown) => {\n const normalizedError = toError(error)\n settle(reject, normalizedError)\n })\n\n taskDomain.run(() => {\n Promise.resolve(handler(job.data, job)).then(\n (result) => settle(resolve, result),\n (error) => settle(reject, toError(error)),\n )\n })\n })\n}\n\nconst ensureQueue = (): Queue => {\n if (queueInstance) return queueInstance\n queueInstance = new Queue(getQueueName(), {\n connection: getConnection(),\n defaultJobOptions: {\n removeOnComplete: DEFAULT_RENTENTION,\n removeOnFail: DEFAULT_RENTENTION\n },\n })\n\n return queueInstance\n}\n\nexport function registerTask<TName extends keyof WorkerTasksMap & string>(\n name: TName,\n handler: TaskHandler<WorkerTasksMap[TName]>,\n): void\nexport function registerTask(name: string, handler: TaskHandler<unknown>): void\nexport function registerTask(name: string, handler: TaskHandler<unknown>): void {\n tasksByName[name] = handler\n}\n\nexport const getTasks = (): Record<string, TaskHandler<unknown>> => tasksByName\n\nexport function add<TName extends keyof WorkerTasksMap & string>(\n taskName: TName,\n payload: WorkerTasksMap[TName],\n options?: JobOptions,\n): Promise<Job>\nexport function add(taskName: string, payload: unknown, options?: JobOptions): Promise<Job>\nexport function add(taskName: string, payload: unknown, options?: JobOptions): Promise<Job> {\n return ensureQueue().add(taskName, payload, options)\n}\n\nexport const getJob = async (jobId: string): Promise<Job | null> => (await ensureQueue().getJob(jobId)) ?? null\n\nexport const getJobs = async (...args: Parameters<Queue[\"getJobs\"]>): Promise<Job[]> =>\n ensureQueue().getJobs(...args) as unknown as Job[]\n\nexport const getInstance = (): Queue => ensureQueue()\n\nexport const start = async (): Promise<Queue> => {\n if (hasStarted) return ensureQueue()\n hasStarted = true\n\n const queue = ensureQueue()\n const concurrency = getConcurrency()\n\n console.log(\"start worker queue\", { queue: queue.name, redisUrl: getRedisUrl(), concurrency })\n\n queue.on(\"error\", (err) => {\n console.log(`queue error: ${err.message}`)\n })\n\n if (!workerInstance) {\n workerInstance = new Worker(\n queue.name,\n async (job) => {\n const taskName = job.name\n\n const handler = tasksByName[taskName]\n if (!handler) {\n throw new Error(`No task registered for '${taskName}'`)\n }\n\n return await runTaskHandler(job, handler)\n },\n {\n concurrency,\n connection: getConnection(),\n },\n )\n }\n\n workerInstance.on(\"error\", (err) => {\n console.log(`worker error: ${err.message}`)\n })\n\n workerInstance.on(\"stalled\", (jobId) => {\n console.log(`job ${jobId} stalled`)\n })\n\n workerInstance.on(\"failed\", (job, err) => {\n console.log(`job ${job?.id ?? \"unknown\"} failed`, err)\n })\n\n await workerInstance.waitUntilReady()\n\n return queue\n}\n\nexport const close = async (): Promise<void> => {\n if (!queueInstance && !workerInstance) return\n try {\n await workerInstance?.close()\n await queueInstance?.close()\n } finally {\n workerInstance = null\n queueInstance = null\n hasStarted = false\n }\n}\n\nexport const getUrl = (): string => getRedisUrl()\n\nexport type ScheduleTaskOptions = Omit<JobOptions, \"repeat\"> & {\n jobId?: string\n repeat: NonNullable<JobOptions[\"repeat\"]>\n}\n\nexport function scheduleTask<TName extends keyof WorkerTasksMap & string>(\n taskName: TName,\n payload: WorkerTasksMap[TName],\n options: ScheduleTaskOptions,\n): Promise<Job>\nexport function scheduleTask(taskName: string, payload: unknown, options: ScheduleTaskOptions): Promise<Job>\nexport async function scheduleTask(taskName: string, payload: unknown, options: ScheduleTaskOptions): Promise<Job> {\n const { jobId, repeat, ...rest } = options\n return add(taskName, payload, {\n ...rest,\n jobId: jobId ?? `schedule|${taskName}`,\n repeat,\n })\n}\n\nconst queueApi = {\n start,\n close,\n registerTask,\n getTasks,\n add,\n scheduleTask,\n getJob,\n getJobs,\n getInstance,\n getUrl,\n}\n\nexport default queueApi\n","import {\n MongoClient,\n type ChangeStream,\n type ChangeStreamDocument,\n type Document,\n type MongoClientOptions,\n} from \"mongodb\"\nimport mongoose from \"mongoose\"\n\nimport queue from \"./queue\"\n\n\nexport type QueueListenerRetryDelays = {\n minMs?: number\n maxMs?: number\n factor?: number\n}\n\nexport type QueueListenerStatus =\n | { state: \"connecting\"; attempt: number }\n | { state: \"ready\" }\n | { state: \"error\"; attempt: number; error: unknown; nextRetryInMs: number }\n | { state: \"failed\"; attempt: number; error: unknown }\n | { state: \"closed\" }\n\nexport type QueueListenerHandle = {\n ready: Promise<void>\n close: () => Promise<void>\n getStatus: () => QueueListenerStatus\n}\n\nexport type QueueListenerOptions = {\n maxRetries?: number | \"infinite\"\n fatalOnMaxRetries?: boolean\n onStateChange?: (status: QueueListenerStatus) => void\n onFatal?: (err: unknown) => void\n retryDelays?: QueueListenerRetryDelays\n mongoClientOptions?: MongoClientOptions\n}\n\nconst RETRY_MAXIMUM_DELAY_MS = 3000\nconst RETRY_MINIMUM_DELAY_MS = 50\nconst RETRY_DEFAULT_FACTOR = 2\n\nconst sleep = async (ms: number, signal?: AbortSignal): Promise<void> => new Promise((resolve) => {\n if (signal?.aborted) {\n resolve()\n return\n }\n\n let timeout: ReturnType<typeof setTimeout> | null = null\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout)\n signal?.removeEventListener(\"abort\", onAbort)\n }\n\n const onAbort = () => {\n cleanup()\n resolve()\n }\n\n timeout = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n timeout.unref?.()\n\n signal?.addEventListener(\"abort\", onAbort)\n})\n\nconst getMongoUrl = (): string => {\n const explicit =\n process.env.MONGODB_URL\n ?? process.env.MONGO_URL\n ?? process.env.MONGODB_URI\n ?? process.env.DB_URL\n\n if (explicit && explicit.trim()) return explicit.trim()\n\n const port = process.env.DB_PORT?.trim()\n if (!port) throw new Error(\"Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)\")\n\n const host = process.env.DB_HOST?.trim() || \"localhost\"\n return `mongodb://${host}:${port}`\n}\n\nconst escapeRegex = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\ntype ModelWithCollection = {\n collection?: {\n collectionName?: string\n name?: string\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null\n\nconst getDocumentId = (doc: unknown): unknown => {\n if (!isRecord(doc)) return undefined\n return doc._id\n}\n\nconst resolveModelNameFromCollection = (collName: string): string | null => {\n const models = mongoose.models\n\n for (const modelName of Object.keys(models)) {\n const model = models[modelName] as ModelWithCollection\n const collectionName =\n model.collection?.collectionName\n ?? model.collection?.name\n\n if (collectionName === collName) {\n return modelName\n }\n }\n\n return null\n}\n\nconst normalizeUpdateDescription = (updateDescription: unknown): unknown => {\n if (!isRecord(updateDescription)) return updateDescription\n if (isRecord(updateDescription.updatedFields)) {\n return {\n ...updateDescription,\n updatedFieldsKeys: Object.keys(updateDescription.updatedFields),\n }\n }\n\n return updateDescription\n}\n\nconst normalizeOpForTaskName = (op: string): string => (op === \"replace\" ? \"update\" : op)\n\nconst getTenantIdFromDbName = (dbName: string, appName: string): string | null => {\n const escapedAppName = escapeRegex(appName)\n const match = dbName.match(new RegExp(`^${escapedAppName}-(.+)-db$`))\n const tenantId = match?.[1]?.trim()\n return tenantId ? tenantId : null\n}\n\nconst dispatchWorkerQueue = async ({\n dbName,\n tenantId,\n modelName,\n op,\n doc,\n updateDescription,\n}: {\n dbName: string\n tenantId: string\n modelName: string\n op: string\n doc: unknown\n updateDescription?: unknown\n}): Promise<void> => {\n const tasks = queue.getTasks()\n const taskOp = normalizeOpForTaskName(op)\n const handlerName = `on-${taskOp}-${modelName}`\n\n if (!tasks[handlerName]) return\n\n const normalizedUpdateDescription = normalizeUpdateDescription(updateDescription)\n\n await queue.add(handlerName, { tenantId, doc, updateDescription: normalizedUpdateDescription }, {\n jobId: `${dbName}|${taskOp}-${getDocumentId(doc) ?? \"unknown\"}`,\n removeOnComplete: true,\n removeOnFail: true,\n })\n}\n\nconst shouldSkipCollection = (collName: string): boolean =>\n collName.endsWith(\".files\") || collName.endsWith(\".chunks\")\n\nconst INTERNAL_IGNORED_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\n\nconst INTERNAL_IGNORED_COLLECTION_NAMES = new Set([\"rtschanges\", \"rtscounters\"])\n\nconst normalizeRetryDelays = (input: QueueListenerRetryDelays | undefined): Required<QueueListenerRetryDelays> => {\n const minMs = Math.max(0, Math.floor(input?.minMs ?? RETRY_MINIMUM_DELAY_MS))\n const rawMaxMs = Math.max(0, Math.floor(input?.maxMs ?? RETRY_MAXIMUM_DELAY_MS))\n const maxMs = Math.max(minMs, rawMaxMs)\n const factor = Math.max(1, Number.isFinite(input?.factor) ? (input?.factor ?? RETRY_DEFAULT_FACTOR) : RETRY_DEFAULT_FACTOR)\n return { minMs, maxMs, factor }\n}\n\nconst getRetryDelayMs = (attempt: number, delays: Required<QueueListenerRetryDelays>): number =>\n Math.min(delays.maxMs, Math.round(delays.minMs * Math.pow(delays.factor, Math.max(0, attempt - 1))))\n\nconst normalizeMaxRetries = (value: number | \"infinite\"): number | \"infinite\" => {\n if (value === \"infinite\") return value\n const parsed = Math.floor(value)\n return Number.isFinite(parsed) ? Math.max(0, parsed) : 0\n}\n\nexport const registerQueueListener = async (options: QueueListenerOptions = {}): Promise<QueueListenerHandle> => {\n const maxRetries = normalizeMaxRetries(options.maxRetries ?? \"infinite\")\n const fatalOnMaxRetries = options.fatalOnMaxRetries ?? false\n const retryDelays = normalizeRetryDelays(options.retryDelays)\n\n const appName = process.env.APP_NAME?.trim()\n if (!appName) {\n throw new Error(\"Missing APP_NAME (required to configure the worker DB change listener)\")\n }\n\n const mongoUrl = getMongoUrl()\n const mongoClientOptions: MongoClientOptions = {\n family: 4,\n serverSelectionTimeoutMS: 2000,\n connectTimeoutMS: 2000,\n ...options.mongoClientOptions,\n }\n\n let stopped = false\n const abortController = new AbortController()\n let client: MongoClient | null = null\n let stream: ChangeStream<Document> | null = null\n let resumeAfter: Document | null = null\n let processing = Promise.resolve()\n\n let status: QueueListenerStatus = { state: \"connecting\", attempt: 1 }\n const setStatus = (next: QueueListenerStatus): void => {\n status = next\n try {\n options.onStateChange?.(next)\n } catch (err) {\n console.warn(\"queue listener onStateChange failed\", err)\n }\n }\n\n let readySettled = false\n let readyResolve: (() => void) | null = null\n let readyReject: ((err: unknown) => void) | null = null\n const ready = new Promise<void>((resolve, reject) => {\n readyResolve = resolve\n readyReject = reject\n })\n\n const resolveReady = (): void => {\n if (readySettled) return\n readySettled = true\n readyResolve?.()\n }\n\n const rejectReady = (err: unknown): void => {\n if (readySettled) return\n readySettled = true\n readyReject?.(err)\n }\n\n const isChangeStreamHistoryLost = (err: unknown): boolean => {\n const maybeErr = err as { code?: unknown; codeName?: unknown }\n const code = typeof maybeErr.code === \"number\" ? maybeErr.code : null\n const codeName = typeof maybeErr.codeName === \"string\" ? maybeErr.codeName : \"\"\n const message = err instanceof Error ? err.message : String(err ?? \"\")\n\n return (\n code === 286\n || codeName === \"ChangeStreamHistoryLost\"\n || message.includes(\"ChangeStreamHistoryLost\")\n || message.includes(\"resume token\")\n || message.includes(\"Resume token\")\n || message.includes(\"resume of change stream was not possible\")\n || message.includes(\"cannot resume\")\n )\n }\n\n const close = async (): Promise<void> => {\n stopped = true\n abortController.abort()\n try {\n stream?.removeAllListeners()\n await stream?.close()\n } catch {\n // ignore\n }\n stream = null\n\n try {\n await client?.close()\n } catch {\n // ignore\n }\n client = null\n\n if (!readySettled) {\n rejectReady(new Error(\"queue listener closed before ready\"))\n }\n\n setStatus({ state: \"closed\" })\n }\n\n const closeResources = async (): Promise<void> => {\n try {\n stream?.removeAllListeners()\n await stream?.close()\n } catch {\n // ignore\n }\n stream = null\n\n try {\n await client?.close()\n } catch {\n // ignore\n }\n client = null\n }\n\n const startStream = async (): Promise<{ stoppedPromise: Promise<{ reason: \"close\" | \"error\"; error?: unknown }> }> => {\n if (stopped) return { stoppedPromise: Promise.resolve({ reason: \"close\" }) }\n\n if (stream) {\n try {\n stream.removeAllListeners()\n await stream.close()\n } catch {\n // ignore\n }\n stream = null\n }\n\n if (client) {\n try {\n await client.close()\n } catch {\n // ignore\n }\n client = null\n }\n\n client = new MongoClient(mongoUrl, mongoClientOptions)\n\n await client.connect()\n\n const dbMatch = { \"ns.db\": { $regex: `^${escapeRegex(appName)}-.*-db$` } }\n\n const pipeline: Document[] = [\n { $match: dbMatch },\n {\n $match: {\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] },\n }\n },\n { $match: { \"ns.coll\": { $nin: Array.from(INTERNAL_IGNORED_COLLECTION_NAMES) } } },\n ]\n\n stream = client.watch(pipeline, {\n fullDocument: \"updateLookup\",\n ...(resumeAfter ? { resumeAfter } : {}),\n })\n\n const stoppedPromise = new Promise<{ reason: \"close\" | \"error\"; error?: unknown }>((resolve) => {\n let settled = false\n const onAbort = () => settle({ reason: \"close\" })\n\n const settle = (result: { reason: \"close\" | \"error\"; error?: unknown }) => {\n if (settled) return\n settled = true\n abortController.signal.removeEventListener(\"abort\", onAbort)\n resolve(result)\n }\n\n abortController.signal.addEventListener(\"abort\", onAbort)\n stream?.once(\"close\", () => settle({ reason: \"close\" }))\n stream?.once(\"error\", (err) => settle({ reason: \"error\", error: err }))\n })\n\n stream.on(\"change\", (change: ChangeStreamDocument<Document>) => {\n const streamRef = stream\n processing = processing.then(async () => {\n const ns = \"ns\" in change ? change.ns : undefined\n const dbName = String(ns?.db ?? \"\")\n if (!dbName) return\n const tenantId = getTenantIdFromDbName(dbName, appName)\n if (!tenantId) return\n\n const collName = String(ns && \"coll\" in ns ? ns.coll : \"\")\n if (!collName) return\n if (shouldSkipCollection(collName)) return\n if (INTERNAL_IGNORED_COLLECTION_NAMES.has(collName)) return\n\n const modelName = resolveModelNameFromCollection(collName)\n if (!modelName) return\n if (INTERNAL_IGNORED_MODEL_NAMES.has(modelName)) return\n\n const op = String(change.operationType ?? \"\")\n if (!op) return\n const normalizedOp = normalizeOpForTaskName(op)\n\n let doc = \"fullDocument\" in change ? change.fullDocument : undefined\n if (!doc && normalizedOp === \"delete\") {\n doc = \"documentKey\" in change ? change.documentKey : undefined\n }\n if (!doc) return\n\n const updateDescription = \"updateDescription\" in change ? change.updateDescription : undefined\n\n try {\n await dispatchWorkerQueue({\n dbName,\n tenantId,\n modelName,\n op: normalizedOp,\n doc,\n updateDescription,\n })\n\n resumeAfter = change?._id ?? resumeAfter\n } catch (err) {\n console.warn(\"queue listener failed to dispatch change\", err)\n try {\n await streamRef?.close()\n } catch {\n // ignore\n }\n }\n }).catch((err) => {\n console.warn(\"queue listener change handler failed\", err)\n })\n })\n\n stream.on(\"error\", (err) => {\n if (stopped) return\n if (resumeAfter && isChangeStreamHistoryLost(err)) {\n resumeAfter = null\n }\n try {\n void Promise.resolve(stream?.close()).catch(() => {})\n } catch {\n // ignore\n }\n })\n\n return { stoppedPromise }\n }\n\n const run = async (): Promise<void> => {\n let retryCounter = 0\n\n while (!stopped) {\n try {\n setStatus({ state: \"connecting\", attempt: retryCounter + 1 })\n\n const { stoppedPromise } = await startStream()\n retryCounter = 0\n setStatus({ state: \"ready\" })\n resolveReady()\n\n const end = await stoppedPromise\n if (stopped) return\n\n retryCounter += 1\n if (maxRetries !== \"infinite\" && retryCounter > maxRetries) {\n const err = end.reason === \"error\" ? end.error : new Error(\"queue listener closed\")\n setStatus({ state: \"failed\", attempt: retryCounter, error: err })\n if (fatalOnMaxRetries) {\n try {\n options.onFatal?.(err)\n } catch (fatalErr) {\n console.warn(\"queue listener onFatal failed\", fatalErr)\n }\n }\n rejectReady(err)\n abortController.abort()\n await closeResources()\n return\n }\n\n const delayMs = getRetryDelayMs(retryCounter, retryDelays)\n const err = end.reason === \"error\" ? end.error : new Error(\"queue listener closed\")\n console.warn(\"queue listener not ready, retrying in\", delayMs, err)\n setStatus({ state: \"error\", attempt: retryCounter, error: err, nextRetryInMs: delayMs })\n await closeResources()\n await sleep(delayMs, abortController.signal)\n } catch (err) {\n if (stopped) return\n\n retryCounter += 1\n if (maxRetries !== \"infinite\" && retryCounter > maxRetries) {\n setStatus({ state: \"failed\", attempt: retryCounter, error: err })\n if (fatalOnMaxRetries) {\n try {\n options.onFatal?.(err)\n } catch (fatalErr) {\n console.warn(\"queue listener onFatal failed\", fatalErr)\n }\n }\n rejectReady(err)\n abortController.abort()\n await closeResources()\n return\n }\n\n const delayMs = getRetryDelayMs(retryCounter, retryDelays)\n console.warn(\"queue listener not ready, retrying in\", delayMs, err)\n setStatus({ state: \"error\", attempt: retryCounter, error: err, nextRetryInMs: delayMs })\n await closeResources()\n await sleep(delayMs, abortController.signal)\n }\n }\n }\n\n const handle: QueueListenerHandle = {\n ready,\n close,\n getStatus: () => status,\n }\n\n void run().catch(async (err) => {\n if (stopped) return\n setStatus({ state: \"failed\", attempt: 0, error: err })\n rejectReady(err)\n await closeResources()\n })\n\n return handle\n}\n","export type DbEventOp = \"insert\" | \"update\" | \"delete\"\n\nexport type DbEventTaskName<TOp extends DbEventOp = DbEventOp, TModelName extends string = string> =\n `on-${TOp}-${TModelName}`\n\nexport type DbEventTaskPayload<TDoc = unknown, TUpdateDescription = unknown> = {\n tenantId: string\n doc: TDoc\n updateDescription?: TUpdateDescription\n}\n\nexport const dbEventTaskName = <TOp extends DbEventOp, TModelName extends string>(\n op: TOp,\n modelName: TModelName,\n): DbEventTaskName<TOp, TModelName> => `on-${op}-${modelName}`\n"],"names":["DEFAULT_QUEUE_NAME","DEFAULT_CONCURRENCY","DEFAULT_RENTENTION","tasksByName","Object","create","queueInstance","workerInstance","hasStarted","getRedisUrl","redisUrl","process","env","REDIS_URL","trim","Error","getQueueName","RB_QUEUE_NAME","getConcurrency","raw","RB_QUEUE_CONCURRENCY","value","Number","isFinite","Math","floor","getConnection","url","toError","error","message","String","runTaskHandler","job","handler","Promise","resolve","reject","taskDomain","domain","settled","settle","next","removeAllListeners","on","normalizedError","run","data","then","result","ensureQueue","Queue","connection","defaultJobOptions","removeOnComplete","removeOnFail","registerTask","name","getTasks","add","taskName","payload","options","getJob","jobId","getJobs","args","getInstance","start","queue","concurrency","console","log","err","Worker","id","waitUntilReady","close","getUrl","scheduleTask","repeat","rest","queueApi","RETRY_MAXIMUM_DELAY_MS","RETRY_MINIMUM_DELAY_MS","RETRY_DEFAULT_FACTOR","sleep","ms","signal","aborted","timeout","cleanup","removeEventListener","onAbort","setTimeout","unref","addEventListener","getMongoUrl","explicit","MONGODB_URL","MONGO_URL","MONGODB_URI","DB_URL","port","DB_PORT","host","DB_HOST","escapeRegex","replace","isRecord","getDocumentId","doc","undefined","_id","resolveModelNameFromCollection","collName","models","mongoose","modelName","keys","model","collectionName","collection","normalizeUpdateDescription","updateDescription","updatedFields","updatedFieldsKeys","normalizeOpForTaskName","op","getTenantIdFromDbName","dbName","appName","escapedAppName","match","RegExp","tenantId","dispatchWorkerQueue","tasks","taskOp","handlerName","normalizedUpdateDescription","shouldSkipCollection","endsWith","INTERNAL_IGNORED_MODEL_NAMES","Set","INTERNAL_IGNORED_COLLECTION_NAMES","normalizeRetryDelays","input","minMs","max","rawMaxMs","maxMs","factor","getRetryDelayMs","attempt","delays","min","round","pow","normalizeMaxRetries","parsed","registerQueueListener","maxRetries","fatalOnMaxRetries","retryDelays","APP_NAME","mongoUrl","mongoClientOptions","family","serverSelectionTimeoutMS","connectTimeoutMS","stopped","abortController","AbortController","client","stream","resumeAfter","processing","status","state","setStatus","onStateChange","warn","readySettled","readyResolve","readyReject","ready","resolveReady","rejectReady","isChangeStreamHistoryLost","maybeErr","code","codeName","includes","abort","closeResources","startStream","stoppedPromise","reason","MongoClient","connect","dbMatch","$regex","pipeline","$match","operationType","$in","$nin","Array","from","watch","fullDocument","once","change","streamRef","ns","db","coll","has","normalizedOp","documentKey","catch","retryCounter","end","onFatal","fatalErr","delayMs","nextRetryInMs","handle","getStatus","dbEventTaskName"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["domain","Queue","Worker","Job","JobsOptions","JobOptions","WorkerTasksMap","TaskHandler","payload","TPayload","job","Promise","DEFAULT_QUEUE_NAME","DEFAULT_CONCURRENCY","DEFAULT_RENTENTION","tasksByName","Record","Object","create","queueInstance","workerInstance","hasStarted","getRedisUrl","redisUrl","process","env","REDIS_URL","trim","Error","getQueueName","RB_QUEUE_NAME","getConcurrency","raw","RB_QUEUE_CONCURRENCY","value","Number","isFinite","Math","floor","getConnection","url","toError","error","message","String","runTaskHandler","handler","resolve","reject","taskDomain","settled","settle","next","removeAllListeners","on","normalizedError","run","data","then","result","ensureQueue","connection","defaultJobOptions","removeOnComplete","removeOnFail","registerTask","name","TName","getTasks","add","taskName","options","getJob","jobId","getJobs","args","Parameters","getInstance","start","queue","concurrency","console","log","err","id","waitUntilReady","close","getUrl","ScheduleTaskOptions","Omit","repeat","NonNullable","scheduleTask","rest","queueApi","MongoClient","ChangeStream","ChangeStreamDocument","Document","MongoClientOptions","mongoose","queue","QueueListenerRetryDelays","minMs","maxMs","factor","QueueListenerStatus","state","attempt","error","nextRetryInMs","QueueListenerHandle","ready","Promise","close","getStatus","QueueListenerOptions","maxRetries","fatalOnMaxRetries","onStateChange","status","onFatal","err","retryDelays","mongoClientOptions","RETRY_MAXIMUM_DELAY_MS","RETRY_MINIMUM_DELAY_MS","RETRY_DEFAULT_FACTOR","sleep","ms","signal","AbortSignal","resolve","aborted","timeout","ReturnType","setTimeout","cleanup","clearTimeout","removeEventListener","onAbort","unref","addEventListener","getMongoUrl","explicit","process","env","MONGODB_URL","MONGO_URL","MONGODB_URI","DB_URL","trim","port","DB_PORT","Error","host","DB_HOST","escapeRegex","value","replace","ModelWithCollection","collection","collectionName","name","isRecord","Record","getDocumentId","doc","undefined","_id","resolveModelNameFromCollection","collName","models","modelName","Object","keys","model","normalizeUpdateDescription","updateDescription","updatedFields","updatedFieldsKeys","normalizeOpForTaskName","op","getTenantIdFromDbName","dbName","appName","escapedAppName","match","RegExp","tenantId","dispatchWorkerQueue","tasks","getTasks","taskOp","handlerName","normalizedUpdateDescription","add","jobId","removeOnComplete","removeOnFail","shouldSkipCollection","endsWith","INTERNAL_IGNORED_MODEL_NAMES","Set","INTERNAL_IGNORED_COLLECTION_NAMES","normalizeRetryDelays","input","Required","Math","max","floor","rawMaxMs","Number","isFinite","getRetryDelayMs","delays","min","round","pow","normalizeMaxRetries","parsed","registerQueueListener","options","APP_NAME","mongoUrl","family","serverSelectionTimeoutMS","connectTimeoutMS","stopped","abortController","AbortController","client","stream","resumeAfter","processing","setStatus","next","console","warn","readySettled","readyResolve","readyReject","reject","resolveReady","rejectReady","isChangeStreamHistoryLost","maybeErr","code","codeName","message","String","includes","abort","removeAllListeners","closeResources","startStream","stoppedPromise","reason","connect","dbMatch","$regex","pipeline","$match","operationType","$in","$nin","Array","from","watch","fullDocument","settled","settle","result","once","on","change","streamRef","then","ns","db","coll","has","normalizedOp","documentKey","catch","run","retryCounter","end","fatalErr","delayMs","handle","DbEventOp","DbEventTaskName","TOp","TModelName","DbEventTaskPayload","tenantId","doc","TDoc","updateDescription","TUpdateDescription","dbEventTaskName","op","modelName"],"sources":["../src/queue.ts","../src/queueListener.ts","../src/taskNames.ts"],"sourcesContent":["import domain from \"node:domain\"\n\nimport { Queue, Worker, type Job, type JobsOptions as JobOptions } from \"bullmq\"\n\nimport type { WorkerTasksMap } from \"./tasksMap\"\n\n\nexport type TaskHandler<TPayload = unknown> = (payload: TPayload, job: Job) => unknown | Promise<unknown>\n\nconst DEFAULT_QUEUE_NAME = \"rb-queue-default\"\nconst DEFAULT_CONCURRENCY = 2\nconst DEFAULT_RENTENTION = 128\n\nconst tasksByName: Record<string, TaskHandler<unknown>> = Object.create(null)\n\nlet queueInstance: Queue | null = null\nlet workerInstance: Worker | null = null\nlet hasStarted = false\n\nconst getRedisUrl = (): string => {\n const redisUrl = process.env.REDIS_URL?.trim()\n if (!redisUrl) {\n throw new Error(\"Missing REDIS_URL (required for @rpcbase/worker queue)\")\n }\n return redisUrl\n}\n\nconst getQueueName = (): string => process.env.RB_QUEUE_NAME?.trim() || DEFAULT_QUEUE_NAME\n\nconst getConcurrency = (): number => {\n const raw = process.env.RB_QUEUE_CONCURRENCY?.trim()\n const value = raw ? Number(raw) : DEFAULT_CONCURRENCY\n if (!Number.isFinite(value) || value <= 0) return DEFAULT_CONCURRENCY\n return Math.floor(value)\n}\n\nconst getConnection = () => ({ url: getRedisUrl() })\n\nconst toError = (error: unknown): Error => {\n if (error instanceof Error) return error\n const message = typeof error === \"string\" ? error : String(error ?? \"unknown error\")\n return new Error(message)\n}\n\nconst runTaskHandler = async (job: Job, handler: TaskHandler<unknown>): Promise<unknown> => {\n return await new Promise((resolve, reject) => {\n const taskDomain = domain.create()\n let settled = false\n\n const settle = (next: (value: unknown) => void, value: unknown) => {\n if (settled) return\n settled = true\n taskDomain.removeAllListeners()\n next(value)\n }\n\n taskDomain.on(\"error\", (error: unknown) => {\n const normalizedError = toError(error)\n settle(reject, normalizedError)\n })\n\n taskDomain.run(() => {\n Promise.resolve(handler(job.data, job)).then(\n (result) => settle(resolve, result),\n (error) => settle(reject, toError(error)),\n )\n })\n })\n}\n\nconst ensureQueue = (): Queue => {\n if (queueInstance) return queueInstance\n queueInstance = new Queue(getQueueName(), {\n connection: getConnection(),\n defaultJobOptions: {\n removeOnComplete: DEFAULT_RENTENTION,\n removeOnFail: DEFAULT_RENTENTION\n },\n })\n\n return queueInstance\n}\n\nexport function registerTask<TName extends keyof WorkerTasksMap & string>(\n name: TName,\n handler: TaskHandler<WorkerTasksMap[TName]>,\n): void\nexport function registerTask(name: string, handler: TaskHandler<unknown>): void\nexport function registerTask(name: string, handler: TaskHandler<unknown>): void {\n tasksByName[name] = handler\n}\n\nexport const getTasks = (): Record<string, TaskHandler<unknown>> => tasksByName\n\nexport function add<TName extends keyof WorkerTasksMap & string>(\n taskName: TName,\n payload: WorkerTasksMap[TName],\n options?: JobOptions,\n): Promise<Job>\nexport function add(taskName: string, payload: unknown, options?: JobOptions): Promise<Job>\nexport function add(taskName: string, payload: unknown, options?: JobOptions): Promise<Job> {\n return ensureQueue().add(taskName, payload, options)\n}\n\nexport const getJob = async (jobId: string): Promise<Job | null> => (await ensureQueue().getJob(jobId)) ?? null\n\nexport const getJobs = async (...args: Parameters<Queue[\"getJobs\"]>): Promise<Job[]> =>\n ensureQueue().getJobs(...args) as unknown as Job[]\n\nexport const getInstance = (): Queue => ensureQueue()\n\nexport const start = async (): Promise<Queue> => {\n if (hasStarted) return ensureQueue()\n hasStarted = true\n\n const queue = ensureQueue()\n const concurrency = getConcurrency()\n\n console.log(\"start worker queue\", { queue: queue.name, redisUrl: getRedisUrl(), concurrency })\n\n queue.on(\"error\", (err) => {\n console.log(`queue error: ${err.message}`)\n })\n\n if (!workerInstance) {\n workerInstance = new Worker(\n queue.name,\n async (job) => {\n const taskName = job.name\n\n const handler = tasksByName[taskName]\n if (!handler) {\n throw new Error(`No task registered for '${taskName}'`)\n }\n\n return await runTaskHandler(job, handler)\n },\n {\n concurrency,\n connection: getConnection(),\n },\n )\n }\n\n workerInstance.on(\"error\", (err) => {\n console.log(`worker error: ${err.message}`)\n })\n\n workerInstance.on(\"stalled\", (jobId) => {\n console.log(`job ${jobId} stalled`)\n })\n\n workerInstance.on(\"failed\", (job, err) => {\n console.log(`job ${job?.id ?? \"unknown\"} failed`, err)\n })\n\n await workerInstance.waitUntilReady()\n\n return queue\n}\n\nexport const close = async (): Promise<void> => {\n if (!queueInstance && !workerInstance) return\n try {\n await workerInstance?.close()\n await queueInstance?.close()\n } finally {\n workerInstance = null\n queueInstance = null\n hasStarted = false\n }\n}\n\nexport const getUrl = (): string => getRedisUrl()\n\nexport type ScheduleTaskOptions = Omit<JobOptions, \"repeat\"> & {\n jobId?: string\n repeat: NonNullable<JobOptions[\"repeat\"]>\n}\n\nexport function scheduleTask<TName extends keyof WorkerTasksMap & string>(\n taskName: TName,\n payload: WorkerTasksMap[TName],\n options: ScheduleTaskOptions,\n): Promise<Job>\nexport function scheduleTask(taskName: string, payload: unknown, options: ScheduleTaskOptions): Promise<Job>\nexport async function scheduleTask(taskName: string, payload: unknown, options: ScheduleTaskOptions): Promise<Job> {\n const { jobId, repeat, ...rest } = options\n return add(taskName, payload, {\n ...rest,\n jobId: jobId ?? `schedule|${taskName}`,\n repeat,\n })\n}\n\nconst queueApi = {\n start,\n close,\n registerTask,\n getTasks,\n add,\n scheduleTask,\n getJob,\n getJobs,\n getInstance,\n getUrl,\n}\n\nexport default queueApi\n","import {\n MongoClient,\n type ChangeStream,\n type ChangeStreamDocument,\n type Document,\n type MongoClientOptions,\n} from \"mongodb\"\nimport mongoose from \"mongoose\"\n\nimport queue from \"./queue\"\n\n\nexport type QueueListenerRetryDelays = {\n minMs?: number\n maxMs?: number\n factor?: number\n}\n\nexport type QueueListenerStatus =\n | { state: \"connecting\"; attempt: number }\n | { state: \"ready\" }\n | { state: \"error\"; attempt: number; error: unknown; nextRetryInMs: number }\n | { state: \"failed\"; attempt: number; error: unknown }\n | { state: \"closed\" }\n\nexport type QueueListenerHandle = {\n ready: Promise<void>\n close: () => Promise<void>\n getStatus: () => QueueListenerStatus\n}\n\nexport type QueueListenerOptions = {\n maxRetries?: number | \"infinite\"\n fatalOnMaxRetries?: boolean\n onStateChange?: (status: QueueListenerStatus) => void\n onFatal?: (err: unknown) => void\n retryDelays?: QueueListenerRetryDelays\n mongoClientOptions?: MongoClientOptions\n}\n\nconst RETRY_MAXIMUM_DELAY_MS = 3000\nconst RETRY_MINIMUM_DELAY_MS = 50\nconst RETRY_DEFAULT_FACTOR = 2\n\nconst sleep = async (ms: number, signal?: AbortSignal): Promise<void> => new Promise((resolve) => {\n if (signal?.aborted) {\n resolve()\n return\n }\n\n let timeout: ReturnType<typeof setTimeout> | null = null\n\n const cleanup = () => {\n if (timeout) clearTimeout(timeout)\n signal?.removeEventListener(\"abort\", onAbort)\n }\n\n const onAbort = () => {\n cleanup()\n resolve()\n }\n\n timeout = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n timeout.unref?.()\n\n signal?.addEventListener(\"abort\", onAbort)\n})\n\nconst getMongoUrl = (): string => {\n const explicit =\n process.env.MONGODB_URL\n ?? process.env.MONGO_URL\n ?? process.env.MONGODB_URI\n ?? process.env.DB_URL\n\n if (explicit && explicit.trim()) return explicit.trim()\n\n const port = process.env.DB_PORT?.trim()\n if (!port) throw new Error(\"Missing Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_URL/DB_PORT)\")\n\n const host = process.env.DB_HOST?.trim() || \"localhost\"\n return `mongodb://${host}:${port}`\n}\n\nconst escapeRegex = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\ntype ModelWithCollection = {\n collection?: {\n collectionName?: string\n name?: string\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null\n\nconst getDocumentId = (doc: unknown): unknown => {\n if (!isRecord(doc)) return undefined\n return doc._id\n}\n\nconst resolveModelNameFromCollection = (collName: string): string | null => {\n const models = mongoose.models\n\n for (const modelName of Object.keys(models)) {\n const model = models[modelName] as ModelWithCollection\n const collectionName =\n model.collection?.collectionName\n ?? model.collection?.name\n\n if (collectionName === collName) {\n return modelName\n }\n }\n\n return null\n}\n\nconst normalizeUpdateDescription = (updateDescription: unknown): unknown => {\n if (!isRecord(updateDescription)) return updateDescription\n if (isRecord(updateDescription.updatedFields)) {\n return {\n ...updateDescription,\n updatedFieldsKeys: Object.keys(updateDescription.updatedFields),\n }\n }\n\n return updateDescription\n}\n\nconst normalizeOpForTaskName = (op: string): string => (op === \"replace\" ? \"update\" : op)\n\nconst getTenantIdFromDbName = (dbName: string, appName: string): string | null => {\n const escapedAppName = escapeRegex(appName)\n const match = dbName.match(new RegExp(`^${escapedAppName}-(.+)-db$`))\n const tenantId = match?.[1]?.trim()\n return tenantId ? tenantId : null\n}\n\nconst dispatchWorkerQueue = async ({\n dbName,\n tenantId,\n modelName,\n op,\n doc,\n updateDescription,\n}: {\n dbName: string\n tenantId: string\n modelName: string\n op: string\n doc: unknown\n updateDescription?: unknown\n}): Promise<void> => {\n const tasks = queue.getTasks()\n const taskOp = normalizeOpForTaskName(op)\n const handlerName = `on-${taskOp}-${modelName}`\n\n if (!tasks[handlerName]) return\n\n const normalizedUpdateDescription = normalizeUpdateDescription(updateDescription)\n\n await queue.add(handlerName, { tenantId, doc, updateDescription: normalizedUpdateDescription }, {\n jobId: `${dbName}|${taskOp}-${getDocumentId(doc) ?? \"unknown\"}`,\n removeOnComplete: true,\n removeOnFail: true,\n })\n}\n\nconst shouldSkipCollection = (collName: string): boolean =>\n collName.endsWith(\".files\") || collName.endsWith(\".chunks\")\n\nconst INTERNAL_IGNORED_MODEL_NAMES = new Set([\"RBRtsChange\", \"RBRtsCounter\"])\n\nconst INTERNAL_IGNORED_COLLECTION_NAMES = new Set([\"rtschanges\", \"rtscounters\"])\n\nconst normalizeRetryDelays = (input: QueueListenerRetryDelays | undefined): Required<QueueListenerRetryDelays> => {\n const minMs = Math.max(0, Math.floor(input?.minMs ?? RETRY_MINIMUM_DELAY_MS))\n const rawMaxMs = Math.max(0, Math.floor(input?.maxMs ?? RETRY_MAXIMUM_DELAY_MS))\n const maxMs = Math.max(minMs, rawMaxMs)\n const factor = Math.max(1, Number.isFinite(input?.factor) ? (input?.factor ?? RETRY_DEFAULT_FACTOR) : RETRY_DEFAULT_FACTOR)\n return { minMs, maxMs, factor }\n}\n\nconst getRetryDelayMs = (attempt: number, delays: Required<QueueListenerRetryDelays>): number =>\n Math.min(delays.maxMs, Math.round(delays.minMs * Math.pow(delays.factor, Math.max(0, attempt - 1))))\n\nconst normalizeMaxRetries = (value: number | \"infinite\"): number | \"infinite\" => {\n if (value === \"infinite\") return value\n const parsed = Math.floor(value)\n return Number.isFinite(parsed) ? Math.max(0, parsed) : 0\n}\n\nexport const registerQueueListener = async (options: QueueListenerOptions = {}): Promise<QueueListenerHandle> => {\n const maxRetries = normalizeMaxRetries(options.maxRetries ?? \"infinite\")\n const fatalOnMaxRetries = options.fatalOnMaxRetries ?? false\n const retryDelays = normalizeRetryDelays(options.retryDelays)\n\n const appName = process.env.APP_NAME?.trim()\n if (!appName) {\n throw new Error(\"Missing APP_NAME (required to configure the worker DB change listener)\")\n }\n\n const mongoUrl = getMongoUrl()\n const mongoClientOptions: MongoClientOptions = {\n family: 4,\n serverSelectionTimeoutMS: 2000,\n connectTimeoutMS: 2000,\n ...options.mongoClientOptions,\n }\n\n let stopped = false\n const abortController = new AbortController()\n let client: MongoClient | null = null\n let stream: ChangeStream<Document> | null = null\n let resumeAfter: Document | null = null\n let processing = Promise.resolve()\n\n let status: QueueListenerStatus = { state: \"connecting\", attempt: 1 }\n const setStatus = (next: QueueListenerStatus): void => {\n status = next\n try {\n options.onStateChange?.(next)\n } catch (err) {\n console.warn(\"queue listener onStateChange failed\", err)\n }\n }\n\n let readySettled = false\n let readyResolve: (() => void) | null = null\n let readyReject: ((err: unknown) => void) | null = null\n const ready = new Promise<void>((resolve, reject) => {\n readyResolve = resolve\n readyReject = reject\n })\n\n const resolveReady = (): void => {\n if (readySettled) return\n readySettled = true\n readyResolve?.()\n }\n\n const rejectReady = (err: unknown): void => {\n if (readySettled) return\n readySettled = true\n readyReject?.(err)\n }\n\n const isChangeStreamHistoryLost = (err: unknown): boolean => {\n const maybeErr = err as { code?: unknown; codeName?: unknown }\n const code = typeof maybeErr.code === \"number\" ? maybeErr.code : null\n const codeName = typeof maybeErr.codeName === \"string\" ? maybeErr.codeName : \"\"\n const message = err instanceof Error ? err.message : String(err ?? \"\")\n\n return (\n code === 286\n || codeName === \"ChangeStreamHistoryLost\"\n || message.includes(\"ChangeStreamHistoryLost\")\n || message.includes(\"resume token\")\n || message.includes(\"Resume token\")\n || message.includes(\"resume of change stream was not possible\")\n || message.includes(\"cannot resume\")\n )\n }\n\n const close = async (): Promise<void> => {\n stopped = true\n abortController.abort()\n try {\n stream?.removeAllListeners()\n await stream?.close()\n } catch {\n // ignore\n }\n stream = null\n\n try {\n await client?.close()\n } catch {\n // ignore\n }\n client = null\n\n if (!readySettled) {\n rejectReady(new Error(\"queue listener closed before ready\"))\n }\n\n setStatus({ state: \"closed\" })\n }\n\n const closeResources = async (): Promise<void> => {\n try {\n stream?.removeAllListeners()\n await stream?.close()\n } catch {\n // ignore\n }\n stream = null\n\n try {\n await client?.close()\n } catch {\n // ignore\n }\n client = null\n }\n\n const startStream = async (): Promise<{ stoppedPromise: Promise<{ reason: \"close\" | \"error\"; error?: unknown }> }> => {\n if (stopped) return { stoppedPromise: Promise.resolve({ reason: \"close\" }) }\n\n if (stream) {\n try {\n stream.removeAllListeners()\n await stream.close()\n } catch {\n // ignore\n }\n stream = null\n }\n\n if (client) {\n try {\n await client.close()\n } catch {\n // ignore\n }\n client = null\n }\n\n client = new MongoClient(mongoUrl, mongoClientOptions)\n\n await client.connect()\n\n const dbMatch = { \"ns.db\": { $regex: `^${escapeRegex(appName)}-.*-db$` } }\n\n const pipeline: Document[] = [\n { $match: dbMatch },\n {\n $match: {\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] },\n }\n },\n { $match: { \"ns.coll\": { $nin: Array.from(INTERNAL_IGNORED_COLLECTION_NAMES) } } },\n ]\n\n stream = client.watch(pipeline, {\n fullDocument: \"updateLookup\",\n ...(resumeAfter ? { resumeAfter } : {}),\n })\n\n const stoppedPromise = new Promise<{ reason: \"close\" | \"error\"; error?: unknown }>((resolve) => {\n let settled = false\n const onAbort = () => settle({ reason: \"close\" })\n\n const settle = (result: { reason: \"close\" | \"error\"; error?: unknown }) => {\n if (settled) return\n settled = true\n abortController.signal.removeEventListener(\"abort\", onAbort)\n resolve(result)\n }\n\n abortController.signal.addEventListener(\"abort\", onAbort)\n stream?.once(\"close\", () => settle({ reason: \"close\" }))\n stream?.once(\"error\", (err) => settle({ reason: \"error\", error: err }))\n })\n\n stream.on(\"change\", (change: ChangeStreamDocument<Document>) => {\n const streamRef = stream\n processing = processing.then(async () => {\n const ns = \"ns\" in change ? change.ns : undefined\n const dbName = String(ns?.db ?? \"\")\n if (!dbName) return\n const tenantId = getTenantIdFromDbName(dbName, appName)\n if (!tenantId) return\n\n const collName = String(ns && \"coll\" in ns ? ns.coll : \"\")\n if (!collName) return\n if (shouldSkipCollection(collName)) return\n if (INTERNAL_IGNORED_COLLECTION_NAMES.has(collName)) return\n\n const modelName = resolveModelNameFromCollection(collName)\n if (!modelName) return\n if (INTERNAL_IGNORED_MODEL_NAMES.has(modelName)) return\n\n const op = String(change.operationType ?? \"\")\n if (!op) return\n const normalizedOp = normalizeOpForTaskName(op)\n\n let doc = \"fullDocument\" in change ? change.fullDocument : undefined\n if (!doc && normalizedOp === \"delete\") {\n doc = \"documentKey\" in change ? change.documentKey : undefined\n }\n if (!doc) return\n\n const updateDescription = \"updateDescription\" in change ? change.updateDescription : undefined\n\n try {\n await dispatchWorkerQueue({\n dbName,\n tenantId,\n modelName,\n op: normalizedOp,\n doc,\n updateDescription,\n })\n\n resumeAfter = change?._id ?? resumeAfter\n } catch (err) {\n console.warn(\"queue listener failed to dispatch change\", err)\n try {\n await streamRef?.close()\n } catch {\n // ignore\n }\n }\n }).catch((err) => {\n console.warn(\"queue listener change handler failed\", err)\n })\n })\n\n stream.on(\"error\", (err) => {\n if (stopped) return\n if (resumeAfter && isChangeStreamHistoryLost(err)) {\n resumeAfter = null\n }\n try {\n void Promise.resolve(stream?.close()).catch(() => {})\n } catch {\n // ignore\n }\n })\n\n return { stoppedPromise }\n }\n\n const run = async (): Promise<void> => {\n let retryCounter = 0\n\n while (!stopped) {\n try {\n setStatus({ state: \"connecting\", attempt: retryCounter + 1 })\n\n const { stoppedPromise } = await startStream()\n retryCounter = 0\n setStatus({ state: \"ready\" })\n resolveReady()\n\n const end = await stoppedPromise\n if (stopped) return\n\n retryCounter += 1\n if (maxRetries !== \"infinite\" && retryCounter > maxRetries) {\n const err = end.reason === \"error\" ? end.error : new Error(\"queue listener closed\")\n setStatus({ state: \"failed\", attempt: retryCounter, error: err })\n if (fatalOnMaxRetries) {\n try {\n options.onFatal?.(err)\n } catch (fatalErr) {\n console.warn(\"queue listener onFatal failed\", fatalErr)\n }\n }\n rejectReady(err)\n abortController.abort()\n await closeResources()\n return\n }\n\n const delayMs = getRetryDelayMs(retryCounter, retryDelays)\n const err = end.reason === \"error\" ? end.error : new Error(\"queue listener closed\")\n console.warn(\"queue listener not ready, retrying in\", delayMs, err)\n setStatus({ state: \"error\", attempt: retryCounter, error: err, nextRetryInMs: delayMs })\n await closeResources()\n await sleep(delayMs, abortController.signal)\n } catch (err) {\n if (stopped) return\n\n retryCounter += 1\n if (maxRetries !== \"infinite\" && retryCounter > maxRetries) {\n setStatus({ state: \"failed\", attempt: retryCounter, error: err })\n if (fatalOnMaxRetries) {\n try {\n options.onFatal?.(err)\n } catch (fatalErr) {\n console.warn(\"queue listener onFatal failed\", fatalErr)\n }\n }\n rejectReady(err)\n abortController.abort()\n await closeResources()\n return\n }\n\n const delayMs = getRetryDelayMs(retryCounter, retryDelays)\n console.warn(\"queue listener not ready, retrying in\", delayMs, err)\n setStatus({ state: \"error\", attempt: retryCounter, error: err, nextRetryInMs: delayMs })\n await closeResources()\n await sleep(delayMs, abortController.signal)\n }\n }\n }\n\n const handle: QueueListenerHandle = {\n ready,\n close,\n getStatus: () => status,\n }\n\n void run().catch(async (err) => {\n if (stopped) return\n setStatus({ state: \"failed\", attempt: 0, error: err })\n rejectReady(err)\n await closeResources()\n })\n\n return handle\n}\n","export type DbEventOp = \"insert\" | \"update\" | \"delete\"\n\nexport type DbEventTaskName<TOp extends DbEventOp = DbEventOp, TModelName extends string = string> =\n `on-${TOp}-${TModelName}`\n\nexport type DbEventTaskPayload<TDoc = unknown, TUpdateDescription = unknown> = {\n tenantId: string\n doc: TDoc\n updateDescription?: TUpdateDescription\n}\n\nexport const dbEventTaskName = <TOp extends DbEventOp, TModelName extends string>(\n op: TOp,\n modelName: TModelName,\n): DbEventTaskName<TOp, TModelName> => `on-${op}-${modelName}`\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/worker",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"