247 lines
9.3 KiB
JavaScript
247 lines
9.3 KiB
JavaScript
"use strict";
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.CronJob = void 0;
|
|
const child_process_1 = require("child_process");
|
|
const errors_1 = require("./errors");
|
|
const time_1 = require("./time");
|
|
class CronJob {
|
|
get isActive() {
|
|
return this._isActive;
|
|
}
|
|
get isCallbackRunning() {
|
|
return this._isCallbackRunning;
|
|
}
|
|
constructor(cronTime, onTick, onComplete, start, timeZone, context, runOnInit, utcOffset, unrefTimeout, waitForCompletion, errorHandler, name, threshold) {
|
|
this.unrefTimeout = false;
|
|
this.lastExecution = null;
|
|
this.runOnce = false;
|
|
this.waitForCompletion = false;
|
|
this.threshold = 250;
|
|
this._isActive = false;
|
|
this._isCallbackRunning = false;
|
|
this._callbacks = [];
|
|
this.context = (context !== null && context !== void 0 ? context : this);
|
|
this.waitForCompletion = Boolean(waitForCompletion);
|
|
this.errorHandler = errorHandler;
|
|
if (timeZone != null && utcOffset != null) {
|
|
throw new errors_1.ExclusiveParametersError('timeZone', 'utcOffset');
|
|
}
|
|
if (timeZone != null) {
|
|
this.cronTime = new time_1.CronTime(cronTime, timeZone, null);
|
|
}
|
|
else if (utcOffset != null) {
|
|
this.cronTime = new time_1.CronTime(cronTime, null, utcOffset);
|
|
}
|
|
else {
|
|
this.cronTime = new time_1.CronTime(cronTime, timeZone, utcOffset);
|
|
}
|
|
if (unrefTimeout != null) {
|
|
this.unrefTimeout = unrefTimeout;
|
|
}
|
|
if (onComplete != null) {
|
|
this.onComplete = this._fnWrap(onComplete);
|
|
}
|
|
if (threshold != null) {
|
|
this.threshold = Math.abs(threshold);
|
|
}
|
|
if (name != null) {
|
|
this.name = name;
|
|
}
|
|
if (this.cronTime.realDate) {
|
|
this.runOnce = true;
|
|
}
|
|
this.addCallback(this._fnWrap(onTick));
|
|
if (runOnInit) {
|
|
this.lastExecution = new Date();
|
|
void this.fireOnTick();
|
|
}
|
|
if (start)
|
|
this.start();
|
|
}
|
|
static from(params) {
|
|
if (params.timeZone != null && params.utcOffset != null) {
|
|
throw new errors_1.ExclusiveParametersError('timeZone', 'utcOffset');
|
|
}
|
|
if (params.timeZone != null) {
|
|
return new CronJob(params.cronTime, params.onTick, params.onComplete, params.start, params.timeZone, params.context, params.runOnInit, params.utcOffset, params.unrefTimeout, params.waitForCompletion, params.errorHandler, params.name, params.threshold);
|
|
}
|
|
else if (params.utcOffset != null) {
|
|
return new CronJob(params.cronTime, params.onTick, params.onComplete, params.start, null, params.context, params.runOnInit, params.utcOffset, params.unrefTimeout, params.waitForCompletion, params.errorHandler, params.name, params.threshold);
|
|
}
|
|
else {
|
|
return new CronJob(params.cronTime, params.onTick, params.onComplete, params.start, params.timeZone, params.context, params.runOnInit, params.utcOffset, params.unrefTimeout, params.waitForCompletion, params.errorHandler, params.name, params.threshold);
|
|
}
|
|
}
|
|
_fnWrap(cmd) {
|
|
var _a, _b;
|
|
switch (typeof cmd) {
|
|
case 'function': {
|
|
return cmd;
|
|
}
|
|
case 'string': {
|
|
const [command, ...args] = cmd.split(' ');
|
|
return child_process_1.spawn.bind(undefined, command !== null && command !== void 0 ? command : cmd, args, {});
|
|
}
|
|
case 'object': {
|
|
return child_process_1.spawn.bind(undefined, cmd.command, (_a = cmd.args) !== null && _a !== void 0 ? _a : [], (_b = cmd.options) !== null && _b !== void 0 ? _b : {});
|
|
}
|
|
}
|
|
}
|
|
addCallback(callback) {
|
|
if (typeof callback === 'function') {
|
|
this._callbacks.push(callback);
|
|
}
|
|
}
|
|
setTime(time) {
|
|
if (!(time instanceof time_1.CronTime)) {
|
|
throw new errors_1.CronError('time must be an instance of CronTime.');
|
|
}
|
|
const wasRunning = this._isActive;
|
|
this.stop();
|
|
this.cronTime = time;
|
|
if (time.realDate)
|
|
this.runOnce = true;
|
|
if (wasRunning)
|
|
this.start();
|
|
}
|
|
nextDate() {
|
|
return this.cronTime.sendAt();
|
|
}
|
|
fireOnTick() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (this.waitForCompletion && this._isCallbackRunning)
|
|
return;
|
|
this._isCallbackRunning = true;
|
|
try {
|
|
for (const callback of this._callbacks) {
|
|
const result = callback.call(this.context, this.onComplete);
|
|
if (this.waitForCompletion)
|
|
yield result;
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (this.errorHandler != null)
|
|
this.errorHandler(error);
|
|
else
|
|
console.error('[Cron] error in callback', error);
|
|
}
|
|
finally {
|
|
this._isCallbackRunning = false;
|
|
}
|
|
});
|
|
}
|
|
nextDates(i) {
|
|
return this.cronTime.sendAt(i !== null && i !== void 0 ? i : 0);
|
|
}
|
|
start() {
|
|
if (this._isActive)
|
|
return;
|
|
this._isActive = true;
|
|
const MAXDELAY = 2147483647;
|
|
let timeout = this.cronTime.getTimeout();
|
|
let remaining = 0;
|
|
let startTime;
|
|
const setCronTimeout = (t) => {
|
|
startTime = Date.now();
|
|
this._timeout = setTimeout(callbackWrapper, t);
|
|
if (this.unrefTimeout && typeof this._timeout.unref === 'function') {
|
|
this._timeout.unref();
|
|
}
|
|
};
|
|
const callbackWrapper = () => {
|
|
const diff = startTime + timeout - Date.now();
|
|
if (diff > 0) {
|
|
let newTimeout = this.cronTime.getTimeout();
|
|
if (newTimeout > diff) {
|
|
newTimeout = diff;
|
|
}
|
|
remaining += newTimeout;
|
|
}
|
|
if (remaining) {
|
|
if (remaining > MAXDELAY) {
|
|
remaining -= MAXDELAY;
|
|
timeout = MAXDELAY;
|
|
}
|
|
else {
|
|
timeout = remaining;
|
|
remaining = 0;
|
|
}
|
|
setCronTimeout(timeout);
|
|
}
|
|
else {
|
|
this.lastExecution = new Date();
|
|
this._isActive = false;
|
|
if (!this.runOnce)
|
|
this.start();
|
|
void this.fireOnTick();
|
|
}
|
|
};
|
|
if (timeout >= 0) {
|
|
if (timeout > MAXDELAY) {
|
|
remaining = timeout - MAXDELAY;
|
|
timeout = MAXDELAY;
|
|
}
|
|
setCronTimeout(timeout);
|
|
}
|
|
else {
|
|
const absoluteTimeout = Math.abs(timeout);
|
|
const message = `[Cron] Missed execution deadline by ${absoluteTimeout}ms for job${this.name ? ` "${this.name}"` : ''} with cron expression '${String(this.cronTime.source)}'`;
|
|
if (absoluteTimeout <= this.threshold) {
|
|
console.warn(`${message}. Executing immediately.`);
|
|
this.lastExecution = new Date();
|
|
void this.fireOnTick();
|
|
}
|
|
else {
|
|
console.warn(`${message}. Skipping execution as it exceeds threshold (${this.threshold}ms).`);
|
|
}
|
|
timeout = this.cronTime.getTimeout();
|
|
setCronTimeout(timeout);
|
|
}
|
|
}
|
|
lastDate() {
|
|
return this.lastExecution;
|
|
}
|
|
_executeOnComplete() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (typeof this.onComplete !== 'function')
|
|
return;
|
|
try {
|
|
yield this.onComplete.call(this.context);
|
|
}
|
|
catch (error) {
|
|
console.error('[Cron] error in onComplete callback:', error);
|
|
}
|
|
});
|
|
}
|
|
_waitForJobCompletion() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
while (this._isCallbackRunning) {
|
|
yield new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
});
|
|
}
|
|
stop() {
|
|
if (this._timeout)
|
|
clearTimeout(this._timeout);
|
|
this._isActive = false;
|
|
if (!this.waitForCompletion) {
|
|
void this._executeOnComplete();
|
|
return;
|
|
}
|
|
return Promise.resolve().then(() => __awaiter(this, void 0, void 0, function* () {
|
|
yield this._waitForJobCompletion();
|
|
yield this._executeOnComplete();
|
|
}));
|
|
}
|
|
}
|
|
exports.CronJob = CronJob;
|