s3si.ts/utils.ts

83 lines
1.8 KiB
TypeScript

import { APIError } from "./APIError.ts";
import { base64, io } from "./deps.ts";
const stdinLines = io.readLines(Deno.stdin);
export function urlBase64Encode(data: ArrayBuffer) {
return base64.encode(data)
.replaceAll("+", "_")
.replaceAll("/", "-")
.replaceAll("=", "");
}
export function urlBase64Decode(data: string) {
return base64.decode(
data
.replaceAll("_", "+")
.replaceAll("-", "/"),
);
}
export async function readline() {
for await (const line of stdinLines) {
if (line !== "") {
return line;
}
}
throw new Error("EOF");
}
type PromiseReturnType<T> = T extends () => Promise<infer R> ? R : never;
export async function retry<F extends () => Promise<unknown>>(
f: F,
times = 2,
): Promise<PromiseReturnType<F>> {
let lastError;
for (let i = 0; i < times; i++) {
try {
return await f() as PromiseReturnType<F>;
} catch (e) {
lastError = e;
}
}
throw lastError;
}
const GLOBAL_CACHE: Record<string, { ts: number; value: unknown }> = {};
export function cache<F extends () => Promise<unknown>>(
f: F,
{ key = f.name, expireIn = 3600 }: { key?: string; expireIn?: number } = {},
): () => Promise<PromiseReturnType<F>> {
return async () => {
const cached = GLOBAL_CACHE[key];
if (cached && cached.ts + expireIn * 1000 > Date.now()) {
return cached.value as PromiseReturnType<F>;
}
const value = await f();
GLOBAL_CACHE[key] = {
ts: Date.now(),
value,
};
return value as PromiseReturnType<F>;
};
}
export async function showError(p: Promise<void>) {
try {
await p;
} catch (e) {
if (e instanceof APIError) {
console.error(
`\n\nAPIError: ${e.message}`,
"\nResponse: ",
e.response,
"\nBody: ",
e.json,
);
} else {
console.error(e);
}
throw e;
}
}