index.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.noop = exports.defaults = exports.Debug = exports.getPackageMeta = exports.zipMap = exports.CONNECTION_CLOSED_ERROR_MSG = exports.shuffle = exports.sample = exports.resolveTLSProfile = exports.parseURL = exports.optimizeErrorStack = exports.toArg = exports.convertMapToArray = exports.convertObjectToArray = exports.timeout = exports.packObject = exports.isInt = exports.wrapMultiResult = exports.convertBufferToString = void 0;
  4. const fs_1 = require("fs");
  5. const path_1 = require("path");
  6. const url_1 = require("url");
  7. const lodash_1 = require("./lodash");
  8. Object.defineProperty(exports, "defaults", { enumerable: true, get: function () { return lodash_1.defaults; } });
  9. Object.defineProperty(exports, "noop", { enumerable: true, get: function () { return lodash_1.noop; } });
  10. const debug_1 = require("./debug");
  11. exports.Debug = debug_1.default;
  12. const TLSProfiles_1 = require("../constants/TLSProfiles");
  13. /**
  14. * Convert a buffer to string, supports buffer array
  15. *
  16. * @example
  17. * ```js
  18. * const input = [Buffer.from('foo'), [Buffer.from('bar')]]
  19. * const res = convertBufferToString(input, 'utf8')
  20. * expect(res).to.eql(['foo', ['bar']])
  21. * ```
  22. */
  23. function convertBufferToString(value, encoding) {
  24. if (value instanceof Buffer) {
  25. return value.toString(encoding);
  26. }
  27. if (Array.isArray(value)) {
  28. const length = value.length;
  29. const res = Array(length);
  30. for (let i = 0; i < length; ++i) {
  31. res[i] =
  32. value[i] instanceof Buffer && encoding === "utf8"
  33. ? value[i].toString()
  34. : convertBufferToString(value[i], encoding);
  35. }
  36. return res;
  37. }
  38. return value;
  39. }
  40. exports.convertBufferToString = convertBufferToString;
  41. /**
  42. * Convert a list of results to node-style
  43. *
  44. * @example
  45. * ```js
  46. * const input = ['a', 'b', new Error('c'), 'd']
  47. * const output = exports.wrapMultiResult(input)
  48. * expect(output).to.eql([[null, 'a'], [null, 'b'], [new Error('c')], [null, 'd'])
  49. * ```
  50. */
  51. function wrapMultiResult(arr) {
  52. // When using WATCH/EXEC transactions, the EXEC will return
  53. // a null instead of an array
  54. if (!arr) {
  55. return null;
  56. }
  57. const result = [];
  58. const length = arr.length;
  59. for (let i = 0; i < length; ++i) {
  60. const item = arr[i];
  61. if (item instanceof Error) {
  62. result.push([item]);
  63. }
  64. else {
  65. result.push([null, item]);
  66. }
  67. }
  68. return result;
  69. }
  70. exports.wrapMultiResult = wrapMultiResult;
  71. /**
  72. * Detect if the argument is a int
  73. * @example
  74. * ```js
  75. * > isInt('123')
  76. * true
  77. * > isInt('123.3')
  78. * false
  79. * > isInt('1x')
  80. * false
  81. * > isInt(123)
  82. * true
  83. * > isInt(true)
  84. * false
  85. * ```
  86. */
  87. function isInt(value) {
  88. const x = parseFloat(value);
  89. return !isNaN(value) && (x | 0) === x;
  90. }
  91. exports.isInt = isInt;
  92. /**
  93. * Pack an array to an Object
  94. *
  95. * @example
  96. * ```js
  97. * > packObject(['a', 'b', 'c', 'd'])
  98. * { a: 'b', c: 'd' }
  99. * ```
  100. */
  101. function packObject(array) {
  102. const result = {};
  103. const length = array.length;
  104. for (let i = 1; i < length; i += 2) {
  105. result[array[i - 1]] = array[i];
  106. }
  107. return result;
  108. }
  109. exports.packObject = packObject;
  110. /**
  111. * Return a callback with timeout
  112. */
  113. function timeout(callback, timeout) {
  114. let timer = null;
  115. const run = function () {
  116. if (timer) {
  117. clearTimeout(timer);
  118. timer = null;
  119. callback.apply(this, arguments);
  120. }
  121. };
  122. timer = setTimeout(run, timeout, new Error("timeout"));
  123. return run;
  124. }
  125. exports.timeout = timeout;
  126. /**
  127. * Convert an object to an array
  128. * @example
  129. * ```js
  130. * > convertObjectToArray({ a: '1' })
  131. * ['a', '1']
  132. * ```
  133. */
  134. function convertObjectToArray(obj) {
  135. const result = [];
  136. const keys = Object.keys(obj); // Object.entries requires node 7+
  137. for (let i = 0, l = keys.length; i < l; i++) {
  138. result.push(keys[i], obj[keys[i]]);
  139. }
  140. return result;
  141. }
  142. exports.convertObjectToArray = convertObjectToArray;
  143. /**
  144. * Convert a map to an array
  145. * @example
  146. * ```js
  147. * > convertMapToArray(new Map([[1, '2']]))
  148. * [1, '2']
  149. * ```
  150. */
  151. function convertMapToArray(map) {
  152. const result = [];
  153. let pos = 0;
  154. map.forEach(function (value, key) {
  155. result[pos] = key;
  156. result[pos + 1] = value;
  157. pos += 2;
  158. });
  159. return result;
  160. }
  161. exports.convertMapToArray = convertMapToArray;
  162. /**
  163. * Convert a non-string arg to a string
  164. */
  165. function toArg(arg) {
  166. if (arg === null || typeof arg === "undefined") {
  167. return "";
  168. }
  169. return String(arg);
  170. }
  171. exports.toArg = toArg;
  172. /**
  173. * Optimize error stack
  174. *
  175. * @param error actually error
  176. * @param friendlyStack the stack that more meaningful
  177. * @param filterPath only show stacks with the specified path
  178. */
  179. function optimizeErrorStack(error, friendlyStack, filterPath) {
  180. const stacks = friendlyStack.split("\n");
  181. let lines = "";
  182. let i;
  183. for (i = 1; i < stacks.length; ++i) {
  184. if (stacks[i].indexOf(filterPath) === -1) {
  185. break;
  186. }
  187. }
  188. for (let j = i; j < stacks.length; ++j) {
  189. lines += "\n" + stacks[j];
  190. }
  191. if (error.stack) {
  192. const pos = error.stack.indexOf("\n");
  193. error.stack = error.stack.slice(0, pos) + lines;
  194. }
  195. return error;
  196. }
  197. exports.optimizeErrorStack = optimizeErrorStack;
  198. /**
  199. * Parse the redis protocol url
  200. */
  201. function parseURL(url) {
  202. if (isInt(url)) {
  203. return { port: url };
  204. }
  205. let parsed = (0, url_1.parse)(url, true, true);
  206. if (!parsed.slashes && url[0] !== "/") {
  207. url = "//" + url;
  208. parsed = (0, url_1.parse)(url, true, true);
  209. }
  210. const options = parsed.query || {};
  211. const result = {};
  212. if (parsed.auth) {
  213. const index = parsed.auth.indexOf(":");
  214. result.username = index === -1 ? parsed.auth : parsed.auth.slice(0, index);
  215. result.password = index === -1 ? "" : parsed.auth.slice(index + 1);
  216. }
  217. if (parsed.pathname) {
  218. if (parsed.protocol === "redis:" || parsed.protocol === "rediss:") {
  219. if (parsed.pathname.length > 1) {
  220. result.db = parsed.pathname.slice(1);
  221. }
  222. }
  223. else {
  224. result.path = parsed.pathname;
  225. }
  226. }
  227. if (parsed.host) {
  228. result.host = parsed.hostname;
  229. }
  230. if (parsed.port) {
  231. result.port = parsed.port;
  232. }
  233. if (typeof options.family === "string") {
  234. const intFamily = Number.parseInt(options.family, 10);
  235. if (!Number.isNaN(intFamily)) {
  236. result.family = intFamily;
  237. }
  238. }
  239. (0, lodash_1.defaults)(result, options);
  240. return result;
  241. }
  242. exports.parseURL = parseURL;
  243. /**
  244. * Resolve TLS profile shortcut in connection options
  245. */
  246. function resolveTLSProfile(options) {
  247. let tls = options === null || options === void 0 ? void 0 : options.tls;
  248. if (typeof tls === "string")
  249. tls = { profile: tls };
  250. const profile = TLSProfiles_1.default[tls === null || tls === void 0 ? void 0 : tls.profile];
  251. if (profile) {
  252. tls = Object.assign({}, profile, tls);
  253. delete tls.profile;
  254. options = Object.assign({}, options, { tls });
  255. }
  256. return options;
  257. }
  258. exports.resolveTLSProfile = resolveTLSProfile;
  259. /**
  260. * Get a random element from `array`
  261. */
  262. function sample(array, from = 0) {
  263. const length = array.length;
  264. if (from >= length) {
  265. return null;
  266. }
  267. return array[from + Math.floor(Math.random() * (length - from))];
  268. }
  269. exports.sample = sample;
  270. /**
  271. * Shuffle the array using the Fisher-Yates Shuffle.
  272. * This method will mutate the original array.
  273. */
  274. function shuffle(array) {
  275. let counter = array.length;
  276. // While there are elements in the array
  277. while (counter > 0) {
  278. // Pick a random index
  279. const index = Math.floor(Math.random() * counter);
  280. // Decrease counter by 1
  281. counter--;
  282. // And swap the last element with it
  283. [array[counter], array[index]] = [array[index], array[counter]];
  284. }
  285. return array;
  286. }
  287. exports.shuffle = shuffle;
  288. /**
  289. * Error message for connection being disconnected
  290. */
  291. exports.CONNECTION_CLOSED_ERROR_MSG = "Connection is closed.";
  292. function zipMap(keys, values) {
  293. const map = new Map();
  294. keys.forEach((key, index) => {
  295. map.set(key, values[index]);
  296. });
  297. return map;
  298. }
  299. exports.zipMap = zipMap;
  300. /**
  301. * Memoized package metadata to avoid repeated file system reads.
  302. *
  303. * @internal
  304. */
  305. let cachedPackageMeta = null;
  306. /**
  307. * Retrieves cached package metadata from package.json.
  308. *
  309. * @internal
  310. * @returns {Promise<{version: string} | null>} Package metadata or null if unavailable
  311. */
  312. async function getPackageMeta() {
  313. if (cachedPackageMeta) {
  314. return cachedPackageMeta;
  315. }
  316. try {
  317. const filePath = (0, path_1.resolve)(__dirname, "..", "..", "package.json");
  318. const data = await fs_1.promises.readFile(filePath, "utf8");
  319. const parsed = JSON.parse(data);
  320. cachedPackageMeta = {
  321. version: parsed.version,
  322. };
  323. return cachedPackageMeta;
  324. }
  325. catch (err) {
  326. cachedPackageMeta = {
  327. version: "error-fetching-version",
  328. };
  329. return cachedPackageMeta;
  330. }
  331. }
  332. exports.getPackageMeta = getPackageMeta;