@socketsecurity/lib
Version:
Core utilities and infrastructure for Socket.dev security tools
273 lines (272 loc) • 8.15 kB
JavaScript
;
/* Socket Lib - Built with esbuild */
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var promises_exports = {};
__export(promises_exports, {
normalizeIterationOptions: () => normalizeIterationOptions,
normalizeRetryOptions: () => normalizeRetryOptions,
pEach: () => pEach,
pEachChunk: () => pEachChunk,
pFilter: () => pFilter,
pFilterChunk: () => pFilterChunk,
pRetry: () => pRetry,
resolveRetryOptions: () => resolveRetryOptions
});
module.exports = __toCommonJS(promises_exports);
var import_core = require("./constants/core");
var import_process = require("./constants/process");
var import_arrays = require("./arrays");
const abortSignal = (0, import_process.getAbortSignal)();
let _timers;
// @__NO_SIDE_EFFECTS__
function getTimers() {
if (_timers === void 0) {
_timers = require("node:timers/promises");
}
return _timers;
}
// @__NO_SIDE_EFFECTS__
function normalizeIterationOptions(options) {
const opts = typeof options === "number" ? { concurrency: options } : options;
const {
// The number of concurrent executions performed at one time.
concurrency = 1,
// Retries as a number or options object.
retries,
// AbortSignal used to support cancellation.
signal = abortSignal
} = { __proto__: null, ...opts };
const normalizedConcurrency = Math.max(1, concurrency);
const retryOpts = /* @__PURE__ */ resolveRetryOptions(retries);
return {
__proto__: null,
concurrency: normalizedConcurrency,
retries: /* @__PURE__ */ normalizeRetryOptions({ signal, ...retryOpts }),
signal
};
}
// @__NO_SIDE_EFFECTS__
function normalizeRetryOptions(options) {
const resolved = /* @__PURE__ */ resolveRetryOptions(options);
const {
// Arguments to pass to the callback function.
args = [],
// Multiplier for exponential backoff (e.g., 2 doubles delay each retry).
backoffFactor = 2,
// Initial delay before the first retry (in milliseconds).
baseDelayMs = 200,
// Whether to apply randomness to spread out retries.
jitter = true,
// Upper limit for any backoff delay (in milliseconds).
maxDelayMs = 1e4,
// Optional callback invoked on each retry attempt:
// (attempt: number, error: unknown, delay: number) => void
onRetry,
// Whether onRetry can cancel retries by returning `false`.
onRetryCancelOnFalse = false,
// Whether onRetry will rethrow errors.
onRetryRethrow = false,
// Number of retry attempts (0 = no retries, only initial attempt).
retries = 0,
// AbortSignal used to support cancellation.
signal = abortSignal
} = resolved;
return {
args,
backoffFactor,
baseDelayMs,
jitter,
maxDelayMs,
onRetry,
onRetryCancelOnFalse,
onRetryRethrow,
retries,
signal
};
}
// @__NO_SIDE_EFFECTS__
function resolveRetryOptions(options) {
const defaults = {
__proto__: null,
retries: 0,
baseDelayMs: 200,
maxDelayMs: 1e4,
backoffFactor: 2
};
if (typeof options === "number") {
return { ...defaults, retries: options };
}
return options ? { ...defaults, ...options } : defaults;
}
// @__NO_SIDE_EFFECTS__
async function pEach(array, callbackFn, options) {
const iterOpts = /* @__PURE__ */ normalizeIterationOptions(options);
const { concurrency, retries, signal } = iterOpts;
const chunks = (0, import_arrays.arrayChunk)(array, concurrency);
for (const chunk of chunks) {
if (signal?.aborted) {
return;
}
await Promise.allSettled(
chunk.map(
(item) => /* @__PURE__ */ pRetry((...args) => callbackFn(args[0]), {
...retries,
args: [item],
signal
})
)
);
}
}
// @__NO_SIDE_EFFECTS__
async function pFilter(array, callbackFn, options) {
const iterOpts = /* @__PURE__ */ normalizeIterationOptions(options);
return (await /* @__PURE__ */ pFilterChunk(
(0, import_arrays.arrayChunk)(array, iterOpts.concurrency),
callbackFn,
iterOpts.retries
)).flat();
}
// @__NO_SIDE_EFFECTS__
async function pEachChunk(array, callbackFn, options) {
const { chunkSize = 100, ...retryOpts } = options || {};
const chunks = (0, import_arrays.arrayChunk)(array, chunkSize);
const normalizedRetryOpts = /* @__PURE__ */ normalizeRetryOptions(retryOpts);
const { signal } = normalizedRetryOpts;
for (const chunk of chunks) {
if (signal?.aborted) {
return;
}
await /* @__PURE__ */ pRetry((...args) => callbackFn(args[0]), {
...normalizedRetryOpts,
args: [chunk]
});
}
}
// @__NO_SIDE_EFFECTS__
async function pFilterChunk(chunks, callbackFn, options) {
const retryOpts = /* @__PURE__ */ normalizeRetryOptions(options);
const { signal } = retryOpts;
const { length } = chunks;
const filteredChunks = Array(length);
for (let i = 0; i < length; i += 1) {
if (signal?.aborted) {
filteredChunks[i] = [];
} else {
const chunk = chunks[i];
const settled = await Promise.allSettled(
chunk.map(
(value) => /* @__PURE__ */ pRetry((...args) => callbackFn(args[0]), {
...retryOpts,
args: [value]
})
)
);
const predicateResults = settled.map(
(r) => r.status === "fulfilled" ? r.value : false
);
filteredChunks[i] = chunk.filter((_v, i2) => predicateResults[i2]);
}
}
return filteredChunks;
}
// @__NO_SIDE_EFFECTS__
async function pRetry(callbackFn, options) {
const {
args,
backoffFactor,
baseDelayMs,
jitter,
maxDelayMs,
onRetry,
onRetryCancelOnFalse,
onRetryRethrow,
retries,
signal
} = /* @__PURE__ */ normalizeRetryOptions(options);
if (signal?.aborted) {
return void 0;
}
if (retries === 0) {
return await callbackFn(...args || [], { signal });
}
const timers = /* @__PURE__ */ getTimers();
let attempts = retries;
let delay = baseDelayMs;
let error = import_core.UNDEFINED_TOKEN;
while (attempts-- >= 0) {
if (signal?.aborted) {
return void 0;
}
try {
return await callbackFn(...args || [], { signal });
} catch (e) {
if (error === import_core.UNDEFINED_TOKEN) {
error = e;
}
if (attempts < 0) {
break;
}
let waitTime = delay;
if (jitter) {
waitTime += Math.floor(Math.random() * delay);
}
waitTime = Math.min(waitTime, maxDelayMs);
if (typeof onRetry === "function") {
try {
const result = onRetry(retries - attempts, e, waitTime);
if (result === false && onRetryCancelOnFalse) {
break;
}
if (typeof result === "number" && result >= 0) {
waitTime = Math.min(result, maxDelayMs);
}
} catch (e2) {
if (onRetryRethrow) {
throw e2;
}
}
}
try {
await timers.setTimeout(waitTime, void 0, { signal });
} catch {
return void 0;
}
if (signal?.aborted) {
return void 0;
}
delay = Math.min(delay * backoffFactor, maxDelayMs);
}
}
if (error !== import_core.UNDEFINED_TOKEN) {
throw error;
}
return void 0;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
normalizeIterationOptions,
normalizeRetryOptions,
pEach,
pEachChunk,
pFilter,
pFilterChunk,
pRetry,
resolveRetryOptions
});