mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 10:12:44 +00:00
93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
const TERMINAL_ATTACH_RETRYABLE_ERROR_PATTERNS = [
|
|
"terminal not found",
|
|
"timed out",
|
|
"timeout",
|
|
"connection",
|
|
"network",
|
|
"disconnected",
|
|
"stream ended",
|
|
] as const;
|
|
|
|
export type TerminalResumeOffsetStore = {
|
|
get: (input: { terminalId: string }) => number | undefined;
|
|
set: (input: { terminalId: string; offset: number }) => void;
|
|
};
|
|
|
|
export function getTerminalResumeOffset(input: {
|
|
terminalId: string;
|
|
resumeOffsetStore: TerminalResumeOffsetStore;
|
|
}): number | undefined {
|
|
const offset = input.resumeOffsetStore.get({ terminalId: input.terminalId });
|
|
if (typeof offset !== "number" || !Number.isFinite(offset)) {
|
|
return undefined;
|
|
}
|
|
|
|
const normalizedOffset = Math.max(0, Math.floor(offset));
|
|
return normalizedOffset;
|
|
}
|
|
|
|
export function updateTerminalResumeOffset(input: {
|
|
terminalId: string;
|
|
offset: number;
|
|
resumeOffsetStore: TerminalResumeOffsetStore;
|
|
}): void {
|
|
if (!Number.isFinite(input.offset)) {
|
|
return;
|
|
}
|
|
|
|
const normalizedOffset = Math.max(0, Math.floor(input.offset));
|
|
const previousOffset =
|
|
getTerminalResumeOffset({
|
|
terminalId: input.terminalId,
|
|
resumeOffsetStore: input.resumeOffsetStore,
|
|
}) ?? -1;
|
|
if (normalizedOffset <= previousOffset) {
|
|
return;
|
|
}
|
|
|
|
input.resumeOffsetStore.set({
|
|
terminalId: input.terminalId,
|
|
offset: normalizedOffset,
|
|
});
|
|
}
|
|
|
|
export function getTerminalAttachRetryDelayMs(input: { attempt: number }): number {
|
|
const clampedAttempt = Math.max(0, input.attempt);
|
|
const exponentialDelay = 250 * 2 ** clampedAttempt;
|
|
return Math.min(2_000, exponentialDelay);
|
|
}
|
|
|
|
export function isTerminalAttachRetryableError(input: { message: string }): boolean {
|
|
const normalized = input.message.toLowerCase();
|
|
return TERMINAL_ATTACH_RETRYABLE_ERROR_PATTERNS.some((pattern) => normalized.includes(pattern));
|
|
}
|
|
|
|
export async function waitForDuration(input: { durationMs: number }): Promise<void> {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(resolve, Math.max(0, input.durationMs));
|
|
});
|
|
}
|
|
|
|
export async function withPromiseTimeout<T>(input: {
|
|
promise: Promise<T>;
|
|
timeoutMs: number;
|
|
timeoutMessage: string;
|
|
}): Promise<T> {
|
|
let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
timeoutHandle = setTimeout(
|
|
() => {
|
|
reject(new Error(input.timeoutMessage));
|
|
},
|
|
Math.max(0, input.timeoutMs),
|
|
);
|
|
});
|
|
try {
|
|
return await Promise.race([input.promise, timeoutPromise]);
|
|
} finally {
|
|
if (timeoutHandle) {
|
|
clearTimeout(timeoutHandle);
|
|
}
|
|
}
|
|
}
|