This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
import { runPolling } from "./poller";
import { validateWaiterOptions } from "./utils";
import { waiterServiceDefaults, WaiterState } from "./waiter";
const abortTimeout = async (abortSignal) => {
return new Promise((resolve) => {
abortSignal.onabort = () => resolve({ state: WaiterState.ABORTED });
});
};
export const createWaiter = async (options, input, acceptorChecks) => {
const params = {
...waiterServiceDefaults,
...options,
};
validateWaiterOptions(params);
const exitConditions = [runPolling(params, input, acceptorChecks)];
if (options.abortController) {
exitConditions.push(abortTimeout(options.abortController.signal));
}
if (options.abortSignal) {
exitConditions.push(abortTimeout(options.abortSignal));
}
return Promise.race(exitConditions);
};

View File

@@ -0,0 +1,2 @@
export * from "./createWaiter";
export * from "./waiter";

View File

@@ -0,0 +1,33 @@
import { sleep } from "./utils/sleep";
import { WaiterState } from "./waiter";
const exponentialBackoffWithJitter = (minDelay, maxDelay, attemptCeiling, attempt) => {
if (attempt > attemptCeiling)
return maxDelay;
const delay = minDelay * 2 ** (attempt - 1);
return randomInRange(minDelay, delay);
};
const randomInRange = (min, max) => min + Math.random() * (max - min);
export const runPolling = async ({ minDelay, maxDelay, maxWaitTime, abortController, client, abortSignal }, input, acceptorChecks) => {
const { state, reason } = await acceptorChecks(client, input);
if (state !== WaiterState.RETRY) {
return { state, reason };
}
let currentAttempt = 1;
const waitUntil = Date.now() + maxWaitTime * 1000;
const attemptCeiling = Math.log(maxDelay / minDelay) / Math.log(2) + 1;
while (true) {
if (abortController?.signal?.aborted || abortSignal?.aborted) {
return { state: WaiterState.ABORTED };
}
const delay = exponentialBackoffWithJitter(minDelay, maxDelay, attemptCeiling, currentAttempt);
if (Date.now() + delay * 1000 > waitUntil) {
return { state: WaiterState.TIMEOUT };
}
await sleep(delay);
const { state, reason } = await acceptorChecks(client, input);
if (state !== WaiterState.RETRY) {
return { state, reason };
}
currentAttempt += 1;
}
};

View File

@@ -0,0 +1,2 @@
export * from "./sleep";
export * from "./validate";

View File

@@ -0,0 +1,3 @@
export const sleep = (seconds) => {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
};

View File

@@ -0,0 +1,17 @@
export const validateWaiterOptions = (options) => {
if (options.maxWaitTime < 1) {
throw new Error(`WaiterConfiguration.maxWaitTime must be greater than 0`);
}
else if (options.minDelay < 1) {
throw new Error(`WaiterConfiguration.minDelay must be greater than 0`);
}
else if (options.maxDelay < 1) {
throw new Error(`WaiterConfiguration.maxDelay must be greater than 0`);
}
else if (options.maxWaitTime <= options.minDelay) {
throw new Error(`WaiterConfiguration.maxWaitTime [${options.maxWaitTime}] must be greater than WaiterConfiguration.minDelay [${options.minDelay}] for this waiter`);
}
else if (options.maxDelay < options.minDelay) {
throw new Error(`WaiterConfiguration.maxDelay [${options.maxDelay}] must be greater than WaiterConfiguration.minDelay [${options.minDelay}] for this waiter`);
}
};

View File

@@ -0,0 +1,34 @@
export const waiterServiceDefaults = {
minDelay: 2,
maxDelay: 120,
};
export var WaiterState;
(function (WaiterState) {
WaiterState["ABORTED"] = "ABORTED";
WaiterState["FAILURE"] = "FAILURE";
WaiterState["SUCCESS"] = "SUCCESS";
WaiterState["RETRY"] = "RETRY";
WaiterState["TIMEOUT"] = "TIMEOUT";
})(WaiterState || (WaiterState = {}));
export const checkExceptions = (result) => {
if (result.state === WaiterState.ABORTED) {
const abortError = new Error(`${JSON.stringify({
...result,
reason: "Request was aborted",
})}`);
abortError.name = "AbortError";
throw abortError;
}
else if (result.state === WaiterState.TIMEOUT) {
const timeoutError = new Error(`${JSON.stringify({
...result,
reason: "Waiter has timed out",
})}`);
timeoutError.name = "TimeoutError";
throw timeoutError;
}
else if (result.state !== WaiterState.SUCCESS) {
throw new Error(`${JSON.stringify({ result })}`);
}
return result;
};