move stuff around until it feels right

This commit is contained in:
MapleLeaf
2021-12-26 12:06:06 -06:00
parent d10618e3c1
commit 894e6abb26
35 changed files with 30 additions and 22 deletions

View File

@@ -0,0 +1,5 @@
import { raise } from "./raise.js"
export function getEnvironmentValue(name: string) {
return process.env[name] ?? raise(`Missing environment variable: ${name}`)
}

3
helpers/last.ts Normal file
View File

@@ -0,0 +1,3 @@
export function last<T>(array: T[]): T | undefined {
return array[array.length - 1]
}

12
helpers/log-pretty.ts Normal file
View File

@@ -0,0 +1,12 @@
import { inspect } from "node:util"
// eslint-disable-next-line import/no-unused-modules
export function logPretty(value: unknown) {
console.info(
inspect(value, {
// depth: Number.POSITIVE_INFINITY,
depth: 10,
colors: true,
}),
)
}

15
helpers/omit.ts Normal file
View File

@@ -0,0 +1,15 @@
import type { Except } from "type-fest"
// eslint-disable-next-line import/no-unused-modules
export function omit<Subject extends object, Key extends keyof Subject>(
subject: Subject,
keys: Key[],
): Except<Subject, Key> {
const result: any = {}
for (const key in subject) {
if (!keys.includes(key as unknown as Key)) {
result[key] = subject[key]
}
}
return result
}

14
helpers/pick.ts Normal file
View File

@@ -0,0 +1,14 @@
// eslint-disable-next-line import/no-unused-modules
export function pick<T, K extends keyof T>(
object: T,
...keys: K[]
): Pick<T, K> {
const result: any = {}
for (const key of keys) {
const value = object[key]
if (value !== undefined) {
result[key] = value
}
}
return result
}

5
helpers/raise.ts Normal file
View File

@@ -0,0 +1,5 @@
import { toError } from "./to-error.js"
export function raise(error: unknown): never {
throw toError(error)
}

10
helpers/reject-after.ts Normal file
View File

@@ -0,0 +1,10 @@
import { setTimeout } from "node:timers/promises"
import { toError } from "./to-error.js"
export async function rejectAfter(
timeMs: number,
error: unknown = `rejected after ${timeMs}ms`,
): Promise<never> {
await setTimeout(timeMs)
return Promise.reject(toError(error))
}

View File

@@ -0,0 +1,21 @@
import { setTimeout } from "node:timers/promises"
const maxTime = 500
const waitPeriod = 50
export async function retryWithTimeout<T>(
callback: () => Promise<T> | T,
): Promise<T> {
const startTime = Date.now()
// eslint-disable-next-line no-constant-condition
while (true) {
try {
return await callback()
} catch (error) {
if (Date.now() - startTime > maxTime) {
throw error
}
await setTimeout(waitPeriod)
}
}
}

3
helpers/to-error.ts Normal file
View File

@@ -0,0 +1,3 @@
export function toError(value: unknown) {
return value instanceof Error ? value : new Error(String(value))
}

4
helpers/to-upper.ts Normal file
View File

@@ -0,0 +1,4 @@
/** A typesafe version of toUpperCase */
export function toUpper<S extends string>(string: S) {
return string.toUpperCase() as Uppercase<S>
}

6
helpers/types.ts Normal file
View File

@@ -0,0 +1,6 @@
/* eslint-disable import/no-unused-modules */
export type MaybePromise<T> = T | Promise<T>
export type ValueOf<Type> = Type extends ReadonlyArray<infer Value>
? Value
: Type[keyof Type]

View File

@@ -0,0 +1,25 @@
import { inspect } from "node:util"
// eslint-disable-next-line import/no-unused-modules
export function withLoggedMethodCalls<T extends object>(value: T) {
return new Proxy(value as Record<string | symbol, unknown>, {
get(target, property) {
const value = target[property]
if (typeof value !== "function") {
return value
}
return (...values: any[]) => {
console.info(
`${String(property)}(${values
.map((value) =>
typeof value === "object" && value !== null
? value.constructor.name
: inspect(value, { colors: true }),
)
.join(", ")})`,
)
return value.apply(target, values)
}
},
}) as T
}