Review funkt
This commit is contained in:
392
node_modules/ioredis/built/utils/index.js
generated
vendored
Normal file
392
node_modules/ioredis/built/utils/index.js
generated
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.noop = exports.isArguments = exports.defaults = exports.Debug = exports.zipMap = exports.isResp2SubscriberMode = 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;
|
||||
const lodash_1 = require("./lodash");
|
||||
Object.defineProperty(exports, "defaults", { enumerable: true, get: function () { return lodash_1.defaults; } });
|
||||
Object.defineProperty(exports, "isArguments", { enumerable: true, get: function () { return lodash_1.isArguments; } });
|
||||
Object.defineProperty(exports, "noop", { enumerable: true, get: function () { return lodash_1.noop; } });
|
||||
const debug_1 = require("./debug");
|
||||
exports.Debug = debug_1.default;
|
||||
const TLSProfiles_1 = require("../constants/TLSProfiles");
|
||||
/**
|
||||
* Convert a buffer to string, supports buffer array and plain objects
|
||||
* (RESP3 map replies decoded with `replyMapping: "resp3"`)
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const input = [Buffer.from('foo'), [Buffer.from('bar')]]
|
||||
* const res = convertBufferToString(input, 'utf8')
|
||||
* expect(res).to.eql(['foo', ['bar']])
|
||||
* ```
|
||||
*/
|
||||
function convertBufferToString(value, encoding) {
|
||||
if (value instanceof Buffer) {
|
||||
return value.toString(encoding);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const length = value.length;
|
||||
const res = Array(length);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
res[i] =
|
||||
value[i] instanceof Buffer && encoding === "utf8"
|
||||
? value[i].toString()
|
||||
: convertBufferToString(value[i], encoding);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (isPlainObject(value)) {
|
||||
const keys = Object.keys(value);
|
||||
const res = {};
|
||||
for (const element of keys) {
|
||||
const item = value[element];
|
||||
const converted = item instanceof Buffer && encoding === "utf8"
|
||||
? item.toString()
|
||||
: convertBufferToString(item, encoding);
|
||||
// Guard "__proto__"/"constructor" via defineProperty, where a bare
|
||||
// assignment would tamper with the object. Issue #1267. Normal keys take
|
||||
// the fast plain-assignment path.
|
||||
if (element === "__proto__" || element === "constructor") {
|
||||
Object.defineProperty(res, element, {
|
||||
value: converted,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
res[element] = converted;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
exports.convertBufferToString = convertBufferToString;
|
||||
/**
|
||||
* Matches only the plain objects produced by RESP3 map replies, so other
|
||||
* object types (class instances etc.) pass through reply conversion untouched.
|
||||
*/
|
||||
function isPlainObject(value) {
|
||||
if (value === null || typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
const proto = Object.getPrototypeOf(value);
|
||||
return proto === Object.prototype || proto === null;
|
||||
}
|
||||
/**
|
||||
* Convert a list of results to node-style
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const input = ['a', 'b', new Error('c'), 'd']
|
||||
* const output = exports.wrapMultiResult(input)
|
||||
* expect(output).to.eql([[null, 'a'], [null, 'b'], [new Error('c')], [null, 'd'])
|
||||
* ```
|
||||
*/
|
||||
function wrapMultiResult(arr) {
|
||||
// When using WATCH/EXEC transactions, the EXEC will return
|
||||
// a null instead of an array
|
||||
if (!arr) {
|
||||
return null;
|
||||
}
|
||||
const result = [];
|
||||
const length = arr.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const item = arr[i];
|
||||
if (item instanceof Error) {
|
||||
result.push([item]);
|
||||
}
|
||||
else {
|
||||
result.push([null, item]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.wrapMultiResult = wrapMultiResult;
|
||||
/**
|
||||
* Detect if the argument is a int
|
||||
* @example
|
||||
* ```js
|
||||
* > isInt('123')
|
||||
* true
|
||||
* > isInt('123.3')
|
||||
* false
|
||||
* > isInt('1x')
|
||||
* false
|
||||
* > isInt(123)
|
||||
* true
|
||||
* > isInt(true)
|
||||
* false
|
||||
* ```
|
||||
*/
|
||||
function isInt(value) {
|
||||
const x = parseFloat(value);
|
||||
return !isNaN(value) && (x | 0) === x;
|
||||
}
|
||||
exports.isInt = isInt;
|
||||
/**
|
||||
* Pack an array to an Object
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* > packObject(['a', 'b', 'c', 'd'])
|
||||
* { a: 'b', c: 'd' }
|
||||
* ```
|
||||
*/
|
||||
function packObject(array) {
|
||||
const result = {};
|
||||
const length = array.length;
|
||||
for (let i = 1; i < length; i += 2) {
|
||||
result[array[i - 1]] = array[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.packObject = packObject;
|
||||
/**
|
||||
* Return a callback with timeout
|
||||
*/
|
||||
function timeout(callback, timeout) {
|
||||
let timer = null;
|
||||
const run = function () {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
callback.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
timer = setTimeout(run, timeout, new Error("timeout"));
|
||||
return run;
|
||||
}
|
||||
exports.timeout = timeout;
|
||||
/**
|
||||
* Convert an object to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertObjectToArray({ a: '1' })
|
||||
* ['a', '1']
|
||||
* ```
|
||||
*/
|
||||
function convertObjectToArray(obj) {
|
||||
const result = [];
|
||||
const keys = Object.keys(obj); // Object.entries requires node 7+
|
||||
for (let i = 0, l = keys.length; i < l; i++) {
|
||||
result.push(keys[i], obj[keys[i]]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.convertObjectToArray = convertObjectToArray;
|
||||
/**
|
||||
* Convert a map to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertMapToArray(new Map([[1, '2']]))
|
||||
* [1, '2']
|
||||
* ```
|
||||
*/
|
||||
function convertMapToArray(map) {
|
||||
const result = [];
|
||||
let pos = 0;
|
||||
map.forEach(function (value, key) {
|
||||
result[pos] = key;
|
||||
result[pos + 1] = value;
|
||||
pos += 2;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
exports.convertMapToArray = convertMapToArray;
|
||||
/**
|
||||
* Convert a non-string arg to a string
|
||||
*/
|
||||
function toArg(arg) {
|
||||
if (arg === null || typeof arg === "undefined") {
|
||||
return "";
|
||||
}
|
||||
if (typeof arg === "number" &&
|
||||
Number.isInteger(arg) &&
|
||||
!Number.isSafeInteger(arg)) {
|
||||
return BigInt(arg).toString();
|
||||
}
|
||||
return String(arg);
|
||||
}
|
||||
exports.toArg = toArg;
|
||||
/**
|
||||
* Optimize error stack
|
||||
*
|
||||
* @param error actually error
|
||||
* @param friendlyStack the stack that more meaningful
|
||||
* @param filterPath only show stacks with the specified path
|
||||
*/
|
||||
function optimizeErrorStack(error, friendlyStack, filterPath) {
|
||||
const stacks = friendlyStack.split("\n");
|
||||
let lines = "";
|
||||
let i;
|
||||
for (i = 1; i < stacks.length; ++i) {
|
||||
if (stacks[i].indexOf(filterPath) === -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let j = i; j < stacks.length; ++j) {
|
||||
lines += "\n" + stacks[j];
|
||||
}
|
||||
if (error.stack) {
|
||||
const pos = error.stack.indexOf("\n");
|
||||
error.stack = error.stack.slice(0, pos) + lines;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
exports.optimizeErrorStack = optimizeErrorStack;
|
||||
/**
|
||||
* Parse the redis protocol url
|
||||
*/
|
||||
function parseURL(url) {
|
||||
if (isInt(url)) {
|
||||
return { port: url };
|
||||
}
|
||||
// Re-assert string type: isInt's type guard (value is string) incorrectly
|
||||
// narrows url to 'never' in the false branch since url is already string.
|
||||
const rawUrl = url;
|
||||
// Determine if the URL has a known protocol (redis:// or rediss://)
|
||||
const hasProtocol = /^rediss?:\/\//i.test(rawUrl);
|
||||
const isProtocolRelative = rawUrl.startsWith("//");
|
||||
// TODO(next major): Consider rejecting protocol-relative Redis URLs early and
|
||||
// requiring explicit redis:// or rediss:// schemes instead. Keep them working
|
||||
// in 5.x for backward compatibility.
|
||||
// Unix socket paths (starting with "/") are not valid URLs — handle separately.
|
||||
// Preserve query params (e.g., /tmp/redis.sock?db=2)
|
||||
if (rawUrl[0] === "/" && !isProtocolRelative) {
|
||||
const qIdx = rawUrl.indexOf("?");
|
||||
const result = {
|
||||
path: qIdx === -1 ? rawUrl : rawUrl.slice(0, qIdx),
|
||||
};
|
||||
if (qIdx !== -1) {
|
||||
const options = {};
|
||||
const params = new URLSearchParams(rawUrl.slice(qIdx + 1));
|
||||
params.forEach((value, key) => {
|
||||
options[key] = parseURLQueryItem(key, value);
|
||||
});
|
||||
(0, lodash_1.defaults)(result, options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let parsed;
|
||||
if (hasProtocol) {
|
||||
parsed = new URL(rawUrl);
|
||||
}
|
||||
else if (isProtocolRelative) {
|
||||
parsed = new URL("redis:" + rawUrl);
|
||||
}
|
||||
else {
|
||||
// Prepend a dummy protocol so new URL() can parse "host:port?query" correctly
|
||||
parsed = new URL("redis://" + rawUrl);
|
||||
}
|
||||
// Convert searchParams to a plain object (equivalent to url.parse(url, true).query)
|
||||
const options = {};
|
||||
parsed.searchParams.forEach((value, key) => {
|
||||
options[key] = parseURLQueryItem(key, value);
|
||||
});
|
||||
const result = {};
|
||||
// Extract auth — WHATWG URL percent-encodes special chars, so decode them.
|
||||
// Check both username AND password: redis://:password@host has empty username but valid password.
|
||||
if (parsed.username || parsed.password) {
|
||||
result.username = decodeURIComponent(parsed.username);
|
||||
result.password = decodeURIComponent(parsed.password);
|
||||
}
|
||||
if (parsed.pathname && parsed.pathname !== "/") {
|
||||
if (hasProtocol || isProtocolRelative) {
|
||||
// For redis://, rediss://, and protocol-relative URLs, the path after /
|
||||
// is the db number
|
||||
if (parsed.pathname.length > 1) {
|
||||
result.db = parsed.pathname.slice(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.path = parsed.pathname;
|
||||
}
|
||||
}
|
||||
if (parsed.hostname) {
|
||||
// WHATWG URL keeps brackets around IPv6 addresses (e.g., "[::1]").
|
||||
// Strip them to match the old url.parse() behavior.
|
||||
result.host = parsed.hostname.replace(/^\[|\]$/g, "");
|
||||
}
|
||||
if (parsed.port) {
|
||||
result.port = parsed.port;
|
||||
}
|
||||
(0, lodash_1.defaults)(result, options);
|
||||
return result;
|
||||
}
|
||||
exports.parseURL = parseURL;
|
||||
function parseURLQueryItem(key, value) {
|
||||
if (key === "family") {
|
||||
const intFamily = Number.parseInt(value, 10);
|
||||
if (!Number.isNaN(intFamily)) {
|
||||
return intFamily;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Resolve TLS profile shortcut in connection options
|
||||
*/
|
||||
function resolveTLSProfile(options) {
|
||||
let tls = options?.tls;
|
||||
if (typeof tls === "string")
|
||||
tls = { profile: tls };
|
||||
const profile = TLSProfiles_1.default[tls?.profile];
|
||||
if (profile) {
|
||||
tls = Object.assign({}, profile, tls);
|
||||
delete tls.profile;
|
||||
options = Object.assign({}, options, { tls });
|
||||
}
|
||||
return options;
|
||||
}
|
||||
exports.resolveTLSProfile = resolveTLSProfile;
|
||||
/**
|
||||
* Get a random element from `array`
|
||||
*/
|
||||
function sample(array, from = 0) {
|
||||
const length = array.length;
|
||||
if (from >= length) {
|
||||
return null;
|
||||
}
|
||||
return array[from + Math.floor(Math.random() * (length - from))];
|
||||
}
|
||||
exports.sample = sample;
|
||||
/**
|
||||
* Shuffle the array using the Fisher-Yates Shuffle.
|
||||
* This method will mutate the original array.
|
||||
*/
|
||||
function shuffle(array) {
|
||||
let counter = array.length;
|
||||
// While there are elements in the array
|
||||
while (counter > 0) {
|
||||
// Pick a random index
|
||||
const index = Math.floor(Math.random() * counter);
|
||||
// Decrease counter by 1
|
||||
counter--;
|
||||
// And swap the last element with it
|
||||
[array[counter], array[index]] = [array[index], array[counter]];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
exports.shuffle = shuffle;
|
||||
/**
|
||||
* Error message for connection being disconnected
|
||||
*/
|
||||
exports.CONNECTION_CLOSED_ERROR_MSG = "Connection is closed.";
|
||||
/**
|
||||
* Whether a connection is restricted to subscriber commands.
|
||||
* Only RESP2 has a restricted subscriber mode; with RESP3 (HELLO 3),
|
||||
* regular commands remain allowed while subscribed.
|
||||
*/
|
||||
function isResp2SubscriberMode(condition) {
|
||||
return Boolean(condition?.subscriber) && condition?.protocol !== 3;
|
||||
}
|
||||
exports.isResp2SubscriberMode = isResp2SubscriberMode;
|
||||
function zipMap(keys, values) {
|
||||
const map = new Map();
|
||||
keys.forEach((key, index) => {
|
||||
map.set(key, values[index]);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
exports.zipMap = zipMap;
|
||||
Reference in New Issue
Block a user