UNPKG

node-beanstalk

Version:

The most comprehensive beanstalk client for nodejs

1,024 lines 47.1 kB
"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