node-beanstalk
Version:
The most comprehensive beanstalk client for nodejs
1,024 lines • 47.1 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Client = void 0;
var events_1 = __importDefault(require("events"));
var types_1 = require("./types");
var ClientError_1 = require("./error/ClientError");
var getCommandInstance_1 = require("./util/getCommandInstance");
var const_1 = require("./const");
var parseResponseHeaders_1 = require("./util/parseResponseHeaders");
var BeanstalkError_1 = require("./error/BeanstalkError");
var validator_1 = require("./util/validator");
var Connection_1 = require("./Connection");
var LinkedList_1 = require("./util/LinkedList");
var Client = /** @class */ (function (_super) {
__extends(Client, _super);
function Client(options, connection) {
if (options === void 0) { options = {}; }
if (connection === void 0) { connection = new Connection_1.Connection(); }
var _this = _super.call(this) || this;
_this._queue = new LinkedList_1.LinkedList();
_this._opt = __assign(__assign({}, const_1.DEFAULT_CLIENT_OPTIONS), options);
_this._conn = connection;
return _this;
}
Object.defineProperty(Client.prototype, "isWorking", {
/**
* Indicates whether client is waiting for server response.
*/
get: function () {
return !!this._queue.size;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Client.prototype, "queueSize", {
/**
* Amount of requests waiting in queue, including connect and disconnect.
*/
get: function () {
return this._queue.size;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Client.prototype, "isConnected", {
/**
* Indicates whether client is connected to the server.
*/
get: function () {
return this._conn.getState() === 'open';
},
enumerable: false,
configurable: true
});
/**
* Establish connection to the server
*
* @category Client
*/
Client.prototype.connect = function () {
return __awaiter(this, void 0, void 0, function () {
var _a, waitPromise, moveQueue;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this._conn.getState() !== 'closed') {
throw new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrConnectionNotClosed, "Unable to open non-closed connection, current state: ".concat(this._conn.getState()));
}
return [4 /*yield*/, this.waitQueue()];
case 1:
_a = _b.sent(), waitPromise = _a[0], moveQueue = _a[1];
_b.label = 2;
case 2:
_b.trys.push([2, , 5, 6]);
return [4 /*yield*/, waitPromise];
case 3:
_b.sent();
return [4 /*yield*/, this._conn.open(this._opt.port, this._opt.host)];
case 4:
_b.sent();
return [3 /*break*/, 6];
case 5:
moveQueue();
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
});
};
/**
* Disconnect the client from server after all pending requests performed.
*
* If {force} set to truthy value - only currently running request will be awaited.
*
* @category Client
*/
Client.prototype.disconnect = function (force) {
if (force === void 0) { force = false; }
return __awaiter(this, void 0, void 0, function () {
var head_1, _a, waitPromise, moveQueue;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this._conn.getState() !== 'open') {
throw new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrConnectionNotOpened, "Unable to close non-opened connection, current state: ".concat(this._conn.getState()));
}
if (force) {
head_1 = this._queue.head;
this._queue.truncate().forEach(function (i) {
if (i.reject === (head_1 === null || head_1 === void 0 ? void 0 : head_1.value.reject))
return;
i.reject(new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrDisconnecting, 'Client is disconnecting'));
});
if (head_1)
this._queue.pushNode(head_1);
}
return [4 /*yield*/, this.waitQueue()];
case 1:
_a = _b.sent(), waitPromise = _a[0], moveQueue = _a[1];
_b.label = 2;
case 2:
_b.trys.push([2, , 5, 6]);
return [4 /*yield*/, waitPromise];
case 3:
_b.sent();
return [4 /*yield*/, this._conn.close()];
case 4:
_b.sent();
return [3 /*break*/, 6];
case 5:
moveQueue();
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
});
};
// COMMANDS
/**
* Subsequent put commands will put jobs into the tube specified by this command. If no use
* command has been issued, jobs will be put into the tube named "default".
*
* @category Producer Commands
*/
Client.prototype.use = function (tubeName) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTubeName)(tubeName);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.use);
return [4 /*yield*/, this.dispatchCommand(cmd, [tubeName])];
case 1:
result = _a.sent();
return [2 /*return*/, result.headers[0]];
}
});
});
};
/**
* This command for any process that wants to insert a job into the queue.
*
* @param payload - Payload of the job. Non string or integer values will be serialized with
* [[IClientCtorOptions.serializer]]. Byte size of payload should be less than less than server's
* max-job-size (default: 2**16) and client's [[IClientCtorOptions.maxPayloadSize]].
*
* @param ttr - Time to run -- is an integer number of seconds to allow a worker
* to run this job. This time is counted from the moment a worker reserves
* this job. If the worker does not delete, release, or bury the job within
* <ttr> seconds, the job will time out and the server will release the job.
* The minimum ttr is 1. Maximum ttr is 2**32-1.
*
* @param priority - Integer < 2**32. Jobs with smaller priority values will be
* scheduled before jobs with larger priorities. The most urgent priority is 0;
* the least urgent priority is 4,294,967,295.
*
* @param delay - Integer number of seconds to wait before putting the job in
* the ready queue. The job will be in the "delayed" state during this time.
* Maximum delay is 2**32-1.
*
* @category Producer Commands
*/
Client.prototype.put = function (payload, ttr, priority, delay) {
if (ttr === void 0) { ttr = this._opt.defaultTTR; }
if (priority === void 0) { priority = this._opt.defaultPriority; }
if (delay === void 0) { delay = this._opt.defaultDelay; }
return __awaiter(this, void 0, void 0, function () {
var cmd, result, state;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTTR)(ttr);
(0, validator_1.validatePriority)(priority);
(0, validator_1.validateDelay)(delay);
if (typeof payload === 'undefined') {
throw new TypeError("payload has to be a non-undefined value");
}
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.put);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(priority), "".concat(delay), "".concat(ttr)], payload)];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.JOB_TOO_BIG) {
throw new BeanstalkError_1.BeanstalkError("Provided job payload exceeds maximal server's 'max-job-size' config", result.status);
}
if (result.status === types_1.BeanstalkResponseStatus.EXPECTED_CRLF) {
throw new BeanstalkError_1.BeanstalkError("Missing trailing CRLF", result.status);
}
if (result.status === types_1.BeanstalkResponseStatus.DRAINING) {
throw new BeanstalkError_1.BeanstalkError("Server is in 'drain mode' and no longer accepting new jobs.", result.status);
}
state = delay !== 0 ? types_1.BeanstalkJobState.delayed : types_1.BeanstalkJobState.ready;
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
state: result.status === types_1.BeanstalkResponseStatus.BURIED ? types_1.BeanstalkJobState.buried : state,
}];
}
});
});
};
/**
* This will return a newly-reserved job. If no job is available to be reserved,
* beanstalkd will wait to send a response until one becomes available. Once a
* job is reserved for the client, the client has limited time to run (TTR) the
* job before the job times out. When the job times out, the server will put the
* job back into the ready queue. Both the TTR and the actual time left can be
* found in response to the [[Client.statsJob]] command.
*
* If more than one job is ready, beanstalkd will choose the one with the
* smallest priority value. Within each priority, it will choose the one that
* was received first.
*
* During the TTR of a reserved job, the last second is kept by the server as a
* safety margin, during which the client will not be made to wait for another
* job. If the client issues a reserve command during the safety margin, or if
* the safety margin arrives while the client is waiting on a reserve command,
* the server will respond with: DEADLINE_SOON
*
* This gives the client a chance to delete or release its reserved job before
* the server automatically releases it.
*
* @category Worker Commands
*/
Client.prototype.reserve = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.reserve);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.TIMED_OUT) {
return [2 /*return*/, null];
}
if (result.status === types_1.BeanstalkResponseStatus.DEADLINE_SOON) {
throw new BeanstalkError_1.BeanstalkError('One of jobs reserved by this client will reach deadline soon, release it first.', result.status);
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* Same as [[Client.reserve]] but with limited amount of time to wait for the job.
*
* A timeout value of 0 will cause the server to immediately return either a
* response or TIMED_OUT. A positive value of timeout will limit the amount of
* time the client will block on the reserve request until a job becomes
* available.
*
* @category Worker Commands
*/
Client.prototype.reserveWithTimeout = function (timeout) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTimeout)(timeout);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['reserve-with-timeout']);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(timeout)])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.TIMED_OUT) {
return [2 /*return*/, null];
}
if (result.status === types_1.BeanstalkResponseStatus.DEADLINE_SOON) {
throw new BeanstalkError_1.BeanstalkError('One of jobs reserved by this client will reach deadline soon, release it first.', result.status);
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* A job can be reserved by its id. Once a job is reserved for the client,
* the client has limited time to run (TTR) the job before the job times out.
* When the job times out, the server will put the job back into the ready queue.
*
* @category Worker Commands
*/
Client.prototype.reserveJob = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['reserve-job']);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* The delete command removes a job from the server entirely. It is normally used
* by the client when the job has successfully run to completion. A client can
* delete jobs that it has reserved, ready jobs, delayed jobs, and jobs that are
* buried.
*
* @category Worker Commands
*/
Client.prototype.delete = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.delete);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
return [2 /*return*/, result.status === types_1.BeanstalkResponseStatus.DELETED];
}
});
});
};
/**
* The release command puts a reserved job back into the ready queue (and marks
* its state as "ready") to be run by any client. It is normally used when the job
* fails because of a transitory error.
*
* @param jobId - job id to release.
* @param priority - a new priority to assign to the job.
* @param delay - integer number of seconds to wait before putting the job in
* the ready queue. The job will be in the "delayed" state during this time.
*
* @category Worker Commands
*/
Client.prototype.release = function (jobId, priority, delay) {
if (priority === void 0) { priority = this._opt.defaultPriority; }
if (delay === void 0) { delay = this._opt.defaultDelay; }
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
(0, validator_1.validatePriority)(priority);
(0, validator_1.validateDelay)(delay);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.release);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId), "".concat(priority), "".concat(delay)])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
if (result.status === types_1.BeanstalkResponseStatus.BURIED) {
return [2 /*return*/, types_1.BeanstalkJobState.buried];
}
return [2 /*return*/, delay !== 0 ? types_1.BeanstalkJobState.delayed : types_1.BeanstalkJobState.ready];
}
});
});
};
/**
* The bury command puts a job into the "buried" state. Buried jobs are put into a
* FIFO linked list and will not be touched by the server again until a client
* kicks them with the [[Client.kick]] command
*
* @param jobId - job id to bury.
* @param priority - a new priority to assign to the job.
*
* @category Worker Commands
*/
Client.prototype.bury = function (jobId, priority) {
if (priority === void 0) { priority = this._opt.defaultPriority; }
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
(0, validator_1.validatePriority)(priority);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.bury);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId), "".concat(priority)])];
case 1:
result = _a.sent();
return [2 /*return*/, result.status === types_1.BeanstalkResponseStatus.BURIED];
}
});
});
};
/**
* The "touch" command allows a worker to request more time to work on a job.
* This is useful for jobs that potentially take a long time, but you still want
* the benefits of a TTR pulling a job away from an unresponsive worker. A worker
* may periodically tell the server that it's still alive and processing a job
* (e.g. it may do this on DEADLINE_SOON). The command postpones the auto
* release of a reserved job until TTR seconds from when the command is issued
*
* @category Worker Commands
*/
Client.prototype.touch = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.touch);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
return [2 /*return*/, result.status === types_1.BeanstalkResponseStatus.TOUCHED];
}
});
});
};
/**
* The "watch" command adds the named tube to the watch list for the current
* connection. A reserve command will take a job from any of the tubes in the
* watch list. For each new connection, the watch list initially consists of one
* tube, named "default".
*
* @category Worker Commands
*/
Client.prototype.watch = function (tubeName) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTubeName)(tubeName);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.watch);
return [4 /*yield*/, this.dispatchCommand(cmd, [tubeName])];
case 1:
result = _a.sent();
return [2 /*return*/, parseInt(result.headers[0], 10)];
}
});
});
};
/**
* Removes the named tube from the watch list for the current connection.
*
* False returned in case of attempt to ignore last tube watched
* (`NOT_IGNORED` returned from server).
*
* @category Worker Commands
*/
Client.prototype.ignore = function (tubeName) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTubeName)(tubeName);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.ignore);
return [4 /*yield*/, this.dispatchCommand(cmd, [tubeName])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.WATCHING) {
return [2 /*return*/, true];
}
return [2 /*return*/, false];
}
});
});
};
/**
* Inspect a job with given ID without reserving it.
*
* @category Other Commands
*/
Client.prototype.peek = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.peek);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* Inspect the next ready job. Operates only on the currently used tube.
*
* @category Other Commands
*/
Client.prototype.peekReady = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['peek-ready']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* Inspect the next delayed job. Operates only on the currently used tube.
*
* @category Other Commands
*/
Client.prototype.peekDelayed = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['peek-delayed']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* Inspect the next buried job. Operates only on the currently used tube.
*
* @category Other Commands
*/
Client.prototype.peekBuried = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['peek-buried']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, {
id: parseInt(result.headers[0], 10),
payload: result.data,
}];
}
});
});
};
/**
* The kick command applies only to the currently used tube. It moves jobs into
* the ready queue. If there are any buried jobs, it will only kick buried jobs.
* Otherwise it will kick delayed jobs.
*
* @param bound - integer upper bound on the number of jobs to kick. The server
* will kick no more than <bound> jobs.
*
* @category Other Commands
*/
Client.prototype.kick = function (bound) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.kick);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(bound)])];
case 1:
result = _a.sent();
return [2 /*return*/, parseInt(result.headers[0], 10)];
}
});
});
};
/**
* The kick-job command is a variant of kick that operates with a single job
* identified by its job id. If the given job id exists and is in a buried or
* delayed state, it will be moved to the ready queue of the the same tube where it
* currently belongs.
*
* @category Other Commands
*/
Client.prototype.kickJob = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['kick-job']);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
return [2 /*return*/, result.status === types_1.BeanstalkResponseStatus.KICKED];
}
});
});
};
/**
* The stats command gives statistical information about the system as a whole.
*
* @category Other Commands
*/
Client.prototype.stats = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand.stats);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
/**
* The stats-tube command gives statistical information about the specified tube
* if it exists.
*
* @category Other Commands
*/
Client.prototype.statsTube = function (tubeName) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTubeName)(tubeName);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['stats-tube']);
return [4 /*yield*/, this.dispatchCommand(cmd, [tubeName])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, result.data];
}
});
});
};
/**
* The stats-job command gives statistical information about the specified job if
* it exists.
*
* @category Other Commands
*/
Client.prototype.statsJob = function (jobId) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateJobId)(jobId);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['stats-job']);
return [4 /*yield*/, this.dispatchCommand(cmd, ["".concat(jobId)])];
case 1:
result = _a.sent();
if (result.status === types_1.BeanstalkResponseStatus.NOT_FOUND) {
return [2 /*return*/, null];
}
return [2 /*return*/, result.data];
}
});
});
};
/**
* The list-tubes command returns a list of all existing tubes.
*
* @category Other Commands
*/
Client.prototype.listTubes = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['list-tubes']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
/**
* The list-tube-used command returns the tube currently being used by the
* client.
*
* @category Other Commands
*/
Client.prototype.listTubeUsed = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['list-tube-used']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
return [2 /*return*/, result.headers[0]];
}
});
});
};
/**
* The list-tubes-watched command returns a list tubes currently being watched by
* the client.
*
* @category Other Commands
*/
Client.prototype.listTubesWatched = function () {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['list-tubes-watched']);
return [4 /*yield*/, this.dispatchCommand(cmd)];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
/**
* The pause-tube command can delay any new job being reserved for a given time.
*
* @param tubeName - tube to pause
* @param delay - integer number of seconds < 2**32 to wait before reserving any more
* jobs from the queue
*
* @category Other Commands
*/
Client.prototype.pauseTube = function (tubeName, delay) {
return __awaiter(this, void 0, void 0, function () {
var cmd, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
(0, validator_1.validateTubeName)(tubeName);
(0, validator_1.validateDelay)(delay);
cmd = (0, getCommandInstance_1.getCommandInstance)(types_1.BeanstalkCommand['pause-tube']);
return [4 /*yield*/, this.dispatchCommand(cmd, [tubeName, "".concat(delay)])];
case 1:
result = _a.sent();
return [2 /*return*/, result.status === types_1.BeanstalkResponseStatus.PAUSED];
}
});
});
};
/**
* @category Client
*/
Client.prototype.waitQueue = function () {
var _this = this;
var listNode;
var promise = new Promise(function (resolve, reject) {
listNode = _this._queue.push({ resolve: resolve, reject: reject });
if (_this._queue.head === listNode) {
resolve();
}
});
return [
promise,
function () {
var _a;
// remove current node from list
_this._queue.removeNode(listNode);
// resolve first promise waiting in queue if it exists
(_a = _this._queue.head) === null || _a === void 0 ? void 0 : _a.value.resolve();
},
];
};
/**
* Transforms payload to buffer. Also performs size and type checks.
*
* In case provided payload is not a [[string | number]] it
* will be serialized via [[IClientCtorOptions.serializer]]
*
* @throws {ClientError}
* @category Client
*/
Client.prototype.payloadToBuffer = function (payload) {
if (payload === undefined)
return undefined;
var _a = this._opt, serializer = _a.serializer, maxPayloadSize = _a.maxPayloadSize;
if (typeof payload !== 'string' && !serializer) {
throw new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrInvalidPayload, "Serializer not defined, payload has to be string, got ".concat(typeof payload, ". Configure serializer or serialize payload manually."));
}
var payloadBuffer;
if (serializer) {
payloadBuffer = serializer.serialize(payload);
}
else {
payloadBuffer = Buffer.from(payload);
}
if (payloadBuffer.length > maxPayloadSize) {
throw new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrPayloadTooBig, "".concat(serializer ? 'Serialized payload' : 'Payload', " is too big,") +
" maximum size is ".concat(maxPayloadSize, " bytes, got ").concat(payloadBuffer.length));
}
return payloadBuffer;
};
/**
*
* @category Client
*/
Client.prototype.readCommandResponse = function () {
var _this = this;
var conn = this._conn;
return new Promise(function (resolve, reject) {
var response = Buffer.alloc(0);
var headers = null;
var dataReadTimeout;
var dataListener = function (data) {
response = Buffer.concat([response, data]);
if (!headers) {
// check if headers already received
headers = (0, parseResponseHeaders_1.parseResponseHeaders)(response);
if (headers) {
response = response.slice(headers.headersLineLen);
if (headers.hasData) {
if (response.length < headers.dataLength) {
// if response data not read - start read timeout
dataReadTimeout = setTimeout(function () {
conn.off('data', dataListener);
reject(new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrResponseRead, "Failed to read response data after ".concat(_this._opt.dataReadTimeoutMs, " ms")));
}, _this._opt.dataReadTimeoutMs);
}
}
}
}
if (headers) {
if (headers.hasData) {
if (response.length >= headers.dataLength) {
// response data is read, we're done
clearTimeout(dataReadTimeout);
conn.off('data', dataListener);
resolve({
status: headers.status,
headers: headers.headers,
data: response.slice(0, headers.dataLength),
});
}
}
else {
conn.off('data', dataListener);
resolve({
status: headers.status,
headers: headers.headers,
});
}
}
};
conn.on('data', dataListener);
});
};
/**
* Sends command to the server and reads response which then passed to [[Command.handleResponse]].
*
* @category Client
*/
Client.prototype.dispatchCommand = function (cmd, args, payload) {
return __awaiter(this, void 0, void 0, function () {
var _a, waitPromise, moveQueue, response, conn, readPromise;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = this.waitQueue(), waitPromise = _a[0], moveQueue = _a[1];
return [4 /*yield*/, waitPromise];
case 1:
_b.sent();
_b.label = 2;
case 2:
_b.trys.push([2, , 5, 6]);
conn = this._conn;
if (conn.getState() !== 'open') {
throw new ClientError_1.ClientError(ClientError_1.ClientErrorCode.ErrConnectionNotOpened, "Unable to dispatch command on not opened connection, connection state is '".concat(conn.getState, "'"));
}
readPromise = this.readCommandResponse();
return [4 /*yield*/, conn.write(cmd.buildCommandBuffer(args, this.payloadToBuffer(payload)))];
case 3:
_b.sent();
return [4 /*yield*/, readPromise];
case 4:
response = _b.sent();
return [3 /*break*/, 6];
case 5:
// move queue forward
moveQueue();
return [7 /*endfinally*/];
case 6: return [2 /*return*/, cmd.handleResponse(response, this._opt.serializer)];
}
});
});
};
return Client;
}(events_1.default));
exports.Client = Client;
//# sourceMappingURL=Client.js.map