all repos — sz-chrome-remote-cdp @ 310d0a7324477079022b178f1a83161e9e61fb1d

pi-browser-harness, patched to drive a Chrome on another host over CDP, with castle_cdp_search web search

feat(util): add result, time, and paths primitives for v0.3 rewrite
amankumarsingh77 amankumarsingh7702@gmal.com
Sat, 02 May 2026 15:31:01 +0530
commit

310d0a7324477079022b178f1a83161e9e61fb1d

parent

6759b6fd0ab435aa1e9fdb4591bd828b7a861a33

3 files changed, 53 insertions(+), 0 deletions(-)

jump to
A src/util/paths.ts

@@ -0,0 +1,9 @@

+import { randomUUID } from "node:crypto"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +export const screenshotPath = (namespace: string, ext: "png" | "jpeg" = "png"): string => + join(tmpdir(), `pi-browser-screenshot-${namespace}-${randomUUID()}.${ext}`); + +export const pdfPath = (namespace: string): string => + join(tmpdir(), `pi-browser-pdf-${namespace}-${randomUUID()}.pdf`);
A src/util/result.ts

@@ -0,0 +1,15 @@

+export type Result<T, E> = + | { readonly success: true; readonly data: T } + | { readonly success: false; readonly error: E }; + +export const ok = <T>(data: T): Result<T, never> => ({ success: true, data }); +export const err = <E>(error: E): Result<never, E> => ({ success: false, error }); + +export const map = <T, U, E>(r: Result<T, E>, f: (t: T) => U): Result<U, E> => + r.success ? ok(f(r.data)) : r; + +export const andThen = <T, U, E>(r: Result<T, E>, f: (t: T) => Result<U, E>): Result<U, E> => + r.success ? f(r.data) : r; + +export const mapErr = <T, E, F>(r: Result<T, E>, f: (e: E) => F): Result<T, F> => + r.success ? r : err(f(r.error));
A src/util/time.ts

@@ -0,0 +1,29 @@

+export const sleep = (ms: number, signal?: AbortSignal): Promise<void> => + new Promise((resolve, reject) => { + if (signal?.aborted) { + reject(new Error("aborted")); + return; + } + const timer = setTimeout(() => { + signal?.removeEventListener("abort", onAbort); + resolve(); + }, ms); + const onAbort = (): void => { + clearTimeout(timer); + reject(new Error("aborted")); + }; + signal?.addEventListener("abort", onAbort, { once: true }); + }); + +export type Deadline = { + readonly remainingMs: () => number; + readonly expired: () => boolean; +}; + +export const deadline = (totalMs: number): Deadline => { + const end = Date.now() + totalMs; + return { + remainingMs: () => Math.max(0, end - Date.now()), + expired: () => Date.now() >= end, + }; +};