Files
url_tracker_tool/node_modules/bullmq/dist/cjs/classes/scripts.js
Andrei 58f8093689 Rebrand from 'Redirect Intelligence v2' to 'URL Tracker Tool V2' throughout UI
- Updated all component headers and documentation
- Changed navbar and footer branding
- Updated homepage hero badge
- Modified page title in index.html
- Simplified footer text to 'Built with ❤️'
- Consistent V2 capitalization across all references
2025-08-19 19:12:23 +00:00

782 lines
28 KiB
JavaScript

/**
* Includes all the scripts needed by the queue and jobs.
*/
/*eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.raw2NextJobData = exports.Scripts = void 0;
const msgpackr_1 = require("msgpackr");
const packer = new msgpackr_1.Packr({
useRecords: false,
encodeUndefinedAsNil: true,
});
const pack = packer.pack;
const enums_1 = require("../enums");
const utils_1 = require("../utils");
const version_1 = require("../version");
class Scripts {
constructor(queue) {
this.queue = queue;
const queueKeys = this.queue.keys;
this.version = version_1.version;
this.moveToFinishedKeys = [
queueKeys.wait,
queueKeys.active,
queueKeys.prioritized,
queueKeys.events,
queueKeys.stalled,
queueKeys.limiter,
queueKeys.delayed,
queueKeys.paused,
queueKeys.meta,
queueKeys.pc,
undefined,
undefined,
undefined,
];
}
execCommand(client, commandName, args) {
const commandNameWithVersion = `${commandName}:${this.version}`;
return client[commandNameWithVersion](args);
}
async isJobInList(listKey, jobId) {
const client = await this.queue.client;
let result;
if ((0, utils_1.isRedisVersionLowerThan)(this.queue.redisVersion, '6.0.6')) {
result = await this.execCommand(client, 'isJobInList', [listKey, jobId]);
}
else {
result = await client.lpos(listKey, jobId);
}
return Number.isInteger(result);
}
async addDelayedJob(client, job, encodedOpts, args) {
const queueKeys = this.queue.keys;
const keys = [
queueKeys.wait,
queueKeys.paused,
queueKeys.meta,
queueKeys.id,
queueKeys.delayed,
queueKeys.completed,
queueKeys.events,
];
keys.push(pack(args), job.data, encodedOpts);
return this.execCommand(client, 'addDelayedJob', keys);
}
async addPrioritizedJob(client, job, encodedOpts, args) {
const queueKeys = this.queue.keys;
const keys = [
queueKeys.wait,
queueKeys.paused,
queueKeys.meta,
queueKeys.id,
queueKeys.prioritized,
queueKeys.completed,
queueKeys.events,
queueKeys.pc,
];
keys.push(pack(args), job.data, encodedOpts);
return this.execCommand(client, 'addPrioritizedJob', keys);
}
async addParentJob(client, job, encodedOpts, args) {
const queueKeys = this.queue.keys;
const keys = [
queueKeys.meta,
queueKeys.id,
queueKeys.completed,
queueKeys.events,
];
keys.push(pack(args), job.data, encodedOpts);
return this.execCommand(client, 'addParentJob', keys);
}
async addJob(client, job, opts, jobId, parentOpts = {}) {
const queueKeys = this.queue.keys;
const parent = job.parent
? Object.assign(Object.assign({}, job.parent), { fpof: opts.fpof, rdof: opts.rdof }) : null;
const args = [
queueKeys[''],
typeof jobId !== 'undefined' ? jobId : '',
job.name,
job.timestamp,
job.parentKey || null,
parentOpts.waitChildrenKey || null,
parentOpts.parentDependenciesKey || null,
parent,
job.repeatJobKey,
];
let encodedOpts;
if (opts.repeat) {
const repeat = Object.assign({}, opts.repeat);
if (repeat.startDate) {
repeat.startDate = +new Date(repeat.startDate);
}
if (repeat.endDate) {
repeat.endDate = +new Date(repeat.endDate);
}
encodedOpts = pack(Object.assign(Object.assign({}, opts), { repeat }));
}
else {
encodedOpts = pack(opts);
}
let result;
if (parentOpts.waitChildrenKey) {
result = await this.addParentJob(client, job, encodedOpts, args);
}
else if (opts.delay) {
result = await this.addDelayedJob(client, job, encodedOpts, args);
}
else if (opts.priority) {
result = await this.addPrioritizedJob(client, job, encodedOpts, args);
}
else {
const keys = [
queueKeys.wait,
queueKeys.paused,
queueKeys.meta,
queueKeys.id,
queueKeys.completed,
queueKeys.events,
];
keys.push(pack(args), job.data, encodedOpts);
result = await this.execCommand(client, 'addStandardJob', keys);
}
if (result < 0) {
throw this.finishedErrors(result, parentOpts.parentKey, 'addJob');
}
return result;
}
async pause(pause) {
const client = await this.queue.client;
let src = 'wait', dst = 'paused';
if (!pause) {
src = 'paused';
dst = 'wait';
}
const keys = [src, dst, 'meta', 'prioritized'].map((name) => this.queue.toKey(name));
keys.push(this.queue.keys.events);
return this.execCommand(client, 'pause', keys.concat([pause ? 'paused' : 'resumed']));
}
removeRepeatableArgs(repeatJobId, repeatJobKey) {
const queueKeys = this.queue.keys;
const keys = [queueKeys.repeat, queueKeys.delayed];
const args = [repeatJobId, repeatJobKey, queueKeys['']];
return keys.concat(args);
}
async removeRepeatable(repeatJobId, repeatJobKey) {
const client = await this.queue.client;
const args = this.removeRepeatableArgs(repeatJobId, repeatJobKey);
return this.execCommand(client, 'removeRepeatable', args);
}
async remove(jobId, removeChildren) {
const client = await this.queue.client;
const keys = [''].map(name => this.queue.toKey(name));
return this.execCommand(client, 'removeJob', keys.concat([jobId, removeChildren ? 1 : 0]));
}
async extendLock(jobId, token, duration, client) {
client = client || (await this.queue.client);
const args = [
this.queue.toKey(jobId) + ':lock',
this.queue.keys.stalled,
token,
duration,
jobId,
];
return this.execCommand(client, 'extendLock', args);
}
async updateData(job, data) {
const client = await this.queue.client;
const keys = [this.queue.toKey(job.id)];
const dataJson = JSON.stringify(data);
const result = await this.execCommand(client, 'updateData', keys.concat([dataJson]));
if (result < 0) {
throw this.finishedErrors(result, job.id, 'updateData');
}
}
async updateProgress(jobId, progress) {
const client = await this.queue.client;
const keys = [
this.queue.toKey(jobId),
this.queue.keys.events,
this.queue.keys.meta,
];
const progressJson = JSON.stringify(progress);
const result = await this.execCommand(client, 'updateProgress', keys.concat([jobId, progressJson]));
if (result < 0) {
throw this.finishedErrors(result, jobId, 'updateProgress');
}
}
moveToFinishedArgs(job, val, propVal, shouldRemove, target, token, timestamp, fetchNext = true) {
var _a, _b, _c, _d, _e;
const queueKeys = this.queue.keys;
const opts = this.queue.opts;
const workerKeepJobs = target === 'completed' ? opts.removeOnComplete : opts.removeOnFail;
const metricsKey = this.queue.toKey(`metrics:${target}`);
const keys = this.moveToFinishedKeys;
keys[10] = queueKeys[target];
keys[11] = this.queue.toKey((_a = job.id) !== null && _a !== void 0 ? _a : '');
keys[12] = metricsKey;
const keepJobs = this.getKeepJobs(shouldRemove, workerKeepJobs);
const args = [
job.id,
timestamp,
propVal,
typeof val === 'undefined' ? 'null' : val,
target,
JSON.stringify({ jobId: job.id, val: val }),
!fetchNext || this.queue.closing ? 0 : 1,
queueKeys[''],
pack({
token,
keepJobs,
limiter: opts.limiter,
lockDuration: opts.lockDuration,
attempts: job.opts.attempts,
attemptsMade: job.attemptsMade,
maxMetricsSize: ((_b = opts.metrics) === null || _b === void 0 ? void 0 : _b.maxDataPoints)
? (_c = opts.metrics) === null || _c === void 0 ? void 0 : _c.maxDataPoints
: '',
fpof: !!((_d = job.opts) === null || _d === void 0 ? void 0 : _d.failParentOnFailure),
rdof: !!((_e = job.opts) === null || _e === void 0 ? void 0 : _e.removeDependencyOnFailure),
}),
];
return keys.concat(args);
}
getKeepJobs(shouldRemove, workerKeepJobs) {
if (typeof shouldRemove === 'undefined') {
return workerKeepJobs || { count: shouldRemove ? 0 : -1 };
}
return typeof shouldRemove === 'object'
? shouldRemove
: typeof shouldRemove === 'number'
? { count: shouldRemove }
: { count: shouldRemove ? 0 : -1 };
}
async moveToFinished(jobId, args) {
const client = await this.queue.client;
const result = await this.execCommand(client, 'moveToFinished', args);
if (result < 0) {
throw this.finishedErrors(result, jobId, 'moveToFinished', 'active');
}
else {
if (typeof result !== 'undefined') {
return raw2NextJobData(result);
}
}
}
finishedErrors(code, jobId, command, state) {
switch (code) {
case enums_1.ErrorCode.JobNotExist:
return new Error(`Missing key for job ${jobId}. ${command}`);
case enums_1.ErrorCode.JobLockNotExist:
return new Error(`Missing lock for job ${jobId}. ${command}`);
case enums_1.ErrorCode.JobNotInState:
return new Error(`Job ${jobId} is not in the ${state} state. ${command}`);
case enums_1.ErrorCode.JobPendingDependencies:
return new Error(`Job ${jobId} has pending dependencies. ${command}`);
case enums_1.ErrorCode.ParentJobNotExist:
return new Error(`Missing key for parent job ${jobId}. ${command}`);
case enums_1.ErrorCode.JobLockMismatch:
return new Error(`Lock mismatch for job ${jobId}. Cmd ${command} from ${state}`);
default:
return new Error(`Unknown code ${code} error for ${jobId}. ${command}`);
}
}
drainArgs(delayed) {
const queueKeys = this.queue.keys;
const keys = [
queueKeys.wait,
queueKeys.paused,
delayed ? queueKeys.delayed : '',
queueKeys.prioritized,
];
const args = [queueKeys['']];
return keys.concat(args);
}
async drain(delayed) {
const client = await this.queue.client;
const args = this.drainArgs(delayed);
return this.execCommand(client, 'drain', args);
}
getRangesArgs(types, start, end, asc) {
const queueKeys = this.queue.keys;
const transformedTypes = types.map(type => {
return type === 'waiting' ? 'wait' : type;
});
const keys = [queueKeys['']];
const args = [start, end, asc ? '1' : '0', ...transformedTypes];
return keys.concat(args);
}
async getRanges(types, start = 0, end = 1, asc = false) {
const client = await this.queue.client;
const args = this.getRangesArgs(types, start, end, asc);
return this.execCommand(client, 'getRanges', args);
}
getCountsArgs(types) {
const queueKeys = this.queue.keys;
const transformedTypes = types.map(type => {
return type === 'waiting' ? 'wait' : type;
});
const keys = [queueKeys['']];
const args = [...transformedTypes];
return keys.concat(args);
}
async getCounts(types) {
const client = await this.queue.client;
const args = this.getCountsArgs(types);
return this.execCommand(client, 'getCounts', args);
}
moveToCompletedArgs(job, returnvalue, removeOnComplete, token, fetchNext = false) {
const timestamp = Date.now();
return this.moveToFinishedArgs(job, returnvalue, 'returnvalue', removeOnComplete, 'completed', token, timestamp, fetchNext);
}
moveToFailedArgs(job, failedReason, removeOnFailed, token, fetchNext = false) {
const timestamp = Date.now();
return this.moveToFinishedArgs(job, failedReason, 'failedReason', removeOnFailed, 'failed', token, timestamp, fetchNext);
}
async isFinished(jobId, returnValue = false) {
const client = await this.queue.client;
const keys = ['completed', 'failed', jobId].map((key) => {
return this.queue.toKey(key);
});
return this.execCommand(client, 'isFinished', keys.concat([jobId, returnValue ? '1' : '']));
}
async getState(jobId) {
const client = await this.queue.client;
const keys = [
'completed',
'failed',
'delayed',
'active',
'wait',
'paused',
'waiting-children',
'prioritized',
].map((key) => {
return this.queue.toKey(key);
});
if ((0, utils_1.isRedisVersionLowerThan)(this.queue.redisVersion, '6.0.6')) {
return this.execCommand(client, 'getState', keys.concat([jobId]));
}
return this.execCommand(client, 'getStateV2', keys.concat([jobId]));
}
async changeDelay(jobId, delay) {
const client = await this.queue.client;
const args = this.changeDelayArgs(jobId, delay);
const result = await this.execCommand(client, 'changeDelay', args);
if (result < 0) {
throw this.finishedErrors(result, jobId, 'changeDelay', 'delayed');
}
}
changeDelayArgs(jobId, delay) {
//
// Bake in the job id first 12 bits into the timestamp
// to guarantee correct execution order of delayed jobs
// (up to 4096 jobs per given timestamp or 4096 jobs apart per timestamp)
//
// WARNING: Jobs that are so far apart that they wrap around will cause FIFO to fail
//
let timestamp = Date.now() + delay;
if (timestamp > 0) {
timestamp = timestamp * 0x1000 + (+jobId & 0xfff);
}
const keys = ['delayed', jobId].map(name => {
return this.queue.toKey(name);
});
keys.push.apply(keys, [this.queue.keys.events]);
return keys.concat([delay, JSON.stringify(timestamp), jobId]);
}
async changePriority(jobId, priority = 0, lifo = false) {
const client = await this.queue.client;
const args = this.changePriorityArgs(jobId, priority, lifo);
const result = await this.execCommand(client, 'changePriority', args);
if (result < 0) {
throw this.finishedErrors(result, jobId, 'changePriority');
}
}
changePriorityArgs(jobId, priority = 0, lifo = false) {
const keys = [
this.queue.keys.wait,
this.queue.keys.paused,
this.queue.keys.meta,
this.queue.keys.prioritized,
this.queue.keys.pc,
];
return keys.concat([
priority,
this.queue.toKey(jobId),
jobId,
lifo ? 1 : 0,
]);
}
// Note: We have an issue here with jobs using custom job ids
moveToDelayedArgs(jobId, timestamp, token, delay) {
//
// Bake in the job id first 12 bits into the timestamp
// to guarantee correct execution order of delayed jobs
// (up to 4096 jobs per given timestamp or 4096 jobs apart per timestamp)
//
// WARNING: Jobs that are so far apart that they wrap around will cause FIFO to fail
//
timestamp = Math.max(0, timestamp !== null && timestamp !== void 0 ? timestamp : 0);
if (timestamp > 0) {
timestamp = timestamp * 0x1000 + (+jobId & 0xfff);
}
const keys = [
'wait',
'active',
'prioritized',
'delayed',
jobId,
].map(name => {
return this.queue.toKey(name);
});
keys.push.apply(keys, [
this.queue.keys.events,
this.queue.keys.paused,
this.queue.keys.meta,
]);
return keys.concat([
this.queue.keys[''],
Date.now(),
JSON.stringify(timestamp),
jobId,
token,
delay,
]);
}
saveStacktraceArgs(jobId, stacktrace, failedReason) {
const keys = [this.queue.toKey(jobId)];
return keys.concat([stacktrace, failedReason]);
}
moveToWaitingChildrenArgs(jobId, token, opts) {
const timestamp = Date.now();
const childKey = (0, utils_1.getParentKey)(opts.child);
const keys = [`${jobId}:lock`, 'active', 'waiting-children', jobId].map(name => {
return this.queue.toKey(name);
});
return keys.concat([
token,
childKey !== null && childKey !== void 0 ? childKey : '',
JSON.stringify(timestamp),
jobId,
]);
}
async moveToDelayed(jobId, timestamp, delay, token = '0') {
const client = await this.queue.client;
const args = this.moveToDelayedArgs(jobId, timestamp, token, delay);
const result = await this.execCommand(client, 'moveToDelayed', args);
if (result < 0) {
throw this.finishedErrors(result, jobId, 'moveToDelayed', 'active');
}
}
/**
* Move parent job to waiting-children state.
*
* @returns true if job is successfully moved, false if there are pending dependencies.
* @throws JobNotExist
* This exception is thrown if jobId is missing.
* @throws JobLockNotExist
* This exception is thrown if job lock is missing.
* @throws JobNotInState
* This exception is thrown if job is not in active state.
*/
async moveToWaitingChildren(jobId, token, opts = {}) {
const client = await this.queue.client;
const args = this.moveToWaitingChildrenArgs(jobId, token, opts);
const result = await this.execCommand(client, 'moveToWaitingChildren', args);
switch (result) {
case 0:
return true;
case 1:
return false;
default:
throw this.finishedErrors(result, jobId, 'moveToWaitingChildren', 'active');
}
}
/**
* Remove jobs in a specific state.
*
* @returns Id jobs from the deleted records.
*/
async cleanJobsInSet(set, timestamp, limit = 0) {
const client = await this.queue.client;
return this.execCommand(client, 'cleanJobsInSet', [
this.queue.toKey(set),
this.queue.toKey('events'),
this.queue.toKey(''),
timestamp,
limit,
set,
]);
}
retryJobArgs(jobId, lifo, token) {
const keys = [
'active',
'wait',
'paused',
jobId,
'meta',
].map(name => {
return this.queue.toKey(name);
});
keys.push(this.queue.keys.events, this.queue.keys.delayed, this.queue.keys.prioritized, this.queue.keys.pc);
const pushCmd = (lifo ? 'R' : 'L') + 'PUSH';
return keys.concat([
this.queue.toKey(''),
Date.now(),
pushCmd,
jobId,
token,
]);
}
moveJobsToWaitArgs(state, count, timestamp) {
const keys = [
this.queue.toKey(''),
this.queue.keys.events,
this.queue.toKey(state),
this.queue.toKey('wait'),
this.queue.toKey('paused'),
this.queue.toKey('meta'),
];
const args = [count, timestamp, state];
return keys.concat(args);
}
async retryJobs(state = 'failed', count = 1000, timestamp = new Date().getTime()) {
const client = await this.queue.client;
const args = this.moveJobsToWaitArgs(state, count, timestamp);
return this.execCommand(client, 'moveJobsToWait', args);
}
async promoteJobs(count = 1000) {
const client = await this.queue.client;
const args = this.moveJobsToWaitArgs('delayed', count, Number.MAX_VALUE);
return this.execCommand(client, 'moveJobsToWait', args);
}
/**
* Attempts to reprocess a job
*
* @param job -
* @param state - The expected job state. If the job is not found
* on the provided state, then it's not reprocessed. Supported states: 'failed', 'completed'
*
* @returns Returns a promise that evaluates to a return code:
* 1 means the operation was a success
* 0 means the job does not exist
* -1 means the job is currently locked and can't be retried.
* -2 means the job was not found in the expected set
*/
async reprocessJob(job, state) {
const client = await this.queue.client;
const keys = [
this.queue.toKey(job.id),
this.queue.keys.events,
this.queue.toKey(state),
this.queue.keys.wait,
this.queue.keys.meta,
this.queue.keys.paused,
];
const args = [
job.id,
(job.opts.lifo ? 'R' : 'L') + 'PUSH',
state === 'failed' ? 'failedReason' : 'returnvalue',
state,
];
const result = await this.execCommand(client, 'reprocessJob', keys.concat(args));
switch (result) {
case 1:
return;
default:
throw this.finishedErrors(result, job.id, 'reprocessJob', state);
}
}
async moveToActive(client, token, jobId) {
const opts = this.queue.opts;
const queueKeys = this.queue.keys;
const keys = [
queueKeys.wait,
queueKeys.active,
queueKeys.prioritized,
queueKeys.events,
queueKeys.stalled,
queueKeys.limiter,
queueKeys.delayed,
queueKeys.paused,
queueKeys.meta,
queueKeys.pc,
];
const args = [
queueKeys[''],
Date.now(),
jobId || '',
pack({
token,
lockDuration: opts.lockDuration,
limiter: opts.limiter,
}),
];
const result = await this.execCommand(client, 'moveToActive', keys.concat(args));
return raw2NextJobData(result);
}
async promote(jobId) {
const client = await this.queue.client;
const keys = [
this.queue.keys.delayed,
this.queue.keys.wait,
this.queue.keys.paused,
this.queue.keys.meta,
this.queue.keys.prioritized,
this.queue.keys.pc,
this.queue.keys.events,
];
const args = [this.queue.toKey(''), jobId];
const code = await this.execCommand(client, 'promote', keys.concat(args));
if (code < 0) {
throw this.finishedErrors(code, jobId, 'promote', 'delayed');
}
}
/**
* Looks for unlocked jobs in the active queue.
*
* The job was being worked on, but the worker process died and it failed to renew the lock.
* We call these jobs 'stalled'. This is the most common case. We resolve these by moving them
* back to wait to be re-processed. To prevent jobs from cycling endlessly between active and wait,
* (e.g. if the job handler keeps crashing),
* we limit the number stalled job recoveries to settings.maxStalledCount.
*/
async moveStalledJobsToWait() {
const client = await this.queue.client;
const opts = this.queue.opts;
const keys = [
this.queue.keys.stalled,
this.queue.keys.wait,
this.queue.keys.active,
this.queue.keys.failed,
this.queue.keys['stalled-check'],
this.queue.keys.meta,
this.queue.keys.paused,
this.queue.keys.events,
];
const args = [
opts.maxStalledCount,
this.queue.toKey(''),
Date.now(),
opts.stalledInterval,
];
return this.execCommand(client, 'moveStalledJobsToWait', keys.concat(args));
}
/**
* Moves a job back from Active to Wait.
* This script is used when a job has been manually rate limited and needs
* to be moved back to wait from active status.
*
* @param client - Redis client
* @param jobId - Job id
* @returns
*/
async moveJobFromActiveToWait(jobId, token) {
const client = await this.queue.client;
const lockKey = `${this.queue.toKey(jobId)}:lock`;
const keys = [
this.queue.keys.active,
this.queue.keys.wait,
this.queue.keys.stalled,
lockKey,
this.queue.keys.paused,
this.queue.keys.meta,
this.queue.keys.limiter,
this.queue.keys.prioritized,
this.queue.keys.events,
];
const args = [jobId, token, this.queue.toKey(jobId)];
const pttl = await this.execCommand(client, 'moveJobFromActiveToWait', keys.concat(args));
return pttl < 0 ? 0 : pttl;
}
async obliterate(opts) {
const client = await this.queue.client;
const keys = [
this.queue.keys.meta,
this.queue.toKey(''),
];
const args = [opts.count, opts.force ? 'force' : null];
const result = await this.execCommand(client, 'obliterate', keys.concat(args));
if (result < 0) {
switch (result) {
case -1:
throw new Error('Cannot obliterate non-paused queue');
case -2:
throw new Error('Cannot obliterate queue with active jobs');
}
}
return result;
}
/**
* Paginate a set or hash keys.
* @param opts
*
*/
async paginate(key, opts) {
const client = await this.queue.client;
const keys = [key];
const maxIterations = 5;
const pageSize = opts.end >= 0 ? opts.end - opts.start + 1 : Infinity;
let cursor = '0', offset = 0, items, total, rawJobs, page = [], jobs = [];
do {
const args = [
opts.start + page.length,
opts.end,
cursor,
offset,
maxIterations,
];
if (opts.fetchJobs) {
args.push(1);
}
[cursor, offset, items, total, rawJobs] = await this.execCommand(client, 'paginate', keys.concat(args));
page = page.concat(items);
if (rawJobs && rawJobs.length) {
jobs = jobs.concat(rawJobs.map(utils_1.array2obj));
}
// Important to keep this coercive inequality (!=) instead of strict inequality (!==)
} while (cursor != '0' && page.length < pageSize);
// If we get an array of arrays, it means we are paginating a hash
if (page.length && Array.isArray(page[0])) {
const result = [];
for (let index = 0; index < page.length; index++) {
const [id, value] = page[index];
try {
result.push({ id, v: JSON.parse(value) });
}
catch (err) {
result.push({ id, err: err.message });
}
}
return {
cursor,
items: result,
total,
jobs,
};
}
else {
return {
cursor,
items: page.map(item => ({ id: item })),
total,
jobs,
};
}
}
}
exports.Scripts = Scripts;
function raw2NextJobData(raw) {
if (raw) {
const result = [null, raw[1], raw[2], raw[3]];
if (raw[0]) {
result[0] = (0, utils_1.array2obj)(raw[0]);
}
return result;
}
return [];
}
exports.raw2NextJobData = raw2NextJobData;
//# sourceMappingURL=scripts.js.map