bdmgrdts/instance/src/lib/utils.ts

95 lines
2.4 KiB
TypeScript

import { TextLineStream } from "https://deno.land/std@0.208.0/streams/mod.ts";
export type Channel<T> = ReturnType<typeof channel<T>>;
export function channel<T>() {
const inner = {
queue: [] as T[],
resolver: null as (() => void) | null,
};
const send = (item: T) => {
inner.queue.push(item);
if (inner.resolver === null) return;
inner.resolver();
inner.resolver = null;
};
const receive = async () => {
if (inner.queue.length < 1) await new Promise<void>((res) => inner.resolver = res);
const [head] = inner.queue.splice(0, 1);
return head;
};
return { send, receive };
}
export function* range(from: number, to: number) {
while (from < to) yield from++;
}
export async function exists(path: string) {
try {
await Deno.stat(path);
return true;
} catch (_) {
return false;
}
}
export async function next<T>(readable: ReadableStream<T>) {
for await (const item of readable) return item;
}
export function lines(readable: ReadableStream<Uint8Array>) {
return readable
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream());
}
const async_noop = async () => {};
export type LoopProcess = ReturnType<typeof loop_process>;
export function loop_process(
command: Deno.Command,
opts?: { delay?: number; on_start?: () => unknown; on_stop?: () => unknown },
) {
const events = {
on_start: opts?.on_start ?? async_noop,
on_stop: opts?.on_stop ?? async_noop,
};
const kill_sig = channel<"kill">();
async function launch() {
while (true) {
await events.on_start();
const child_process = command.spawn();
const result = await Promise.any([kill_sig.receive(), child_process.output()]);
if (result === "kill") {
await events.on_stop();
child_process.kill();
break;
}
await events.on_stop();
await sleep(opts?.delay ?? 500);
}
}
const looping = launch();
return {
kill: async () => {
kill_sig.send("kill"), await looping;
},
events,
looping,
};
}
export function sleep(ms: number) {
return new Promise((resolver) => setTimeout(resolver, ms));
}
export async function run(command: string, ...args: string[]) {
const { code } = await new Deno.Command(command, { args }).output();
if (code != 0) throw new Error(`command ${command} failed`);
}
export function log_from(...prefixes: string[]) {
const prefix = prefixes.map((p) => `[${p}]`).join("").padStart(10);
return function (...args: unknown[]) {
console.log(prefix, ...args);
};
}