diff options
author | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-12 09:40:12 -0700 |
---|---|---|
committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-12 09:54:58 -0700 |
commit | 723fa00cb14513eb1a517728d4464c4f148a29cc (patch) | |
tree | d32e2f725397d41b3ad7f886d61c16458dde5b37 /utils | |
parent | 30729a0cf707d9022bae0a7baaba77379dc31fd5 (diff) | |
download | ci-723fa00cb14513eb1a517728d4464c4f148a29cc.tar.gz ci-723fa00cb14513eb1a517728d4464c4f148a29cc.zip |
The big refactor
Diffstat (limited to 'utils')
-rw-r--r-- | utils/env.ts | 2 | ||||
-rw-r--r-- | utils/logger.ts | 6 | ||||
-rw-r--r-- | utils/mod.ts | 2 | ||||
-rw-r--r-- | utils/run.ts | 2 | ||||
-rw-r--r-- | utils/secret.ts | 42 | ||||
-rw-r--r-- | utils/trace.ts | 111 | ||||
-rw-r--r-- | utils/validate_identifier.ts | 8 |
7 files changed, 151 insertions, 22 deletions
diff --git a/utils/env.ts b/utils/env.ts index c0cf447..31b7ccf 100644 --- a/utils/env.ts +++ b/utils/env.ts @@ -1,5 +1,5 @@ export const getRequiredEnv = (name: string): string => { const value = Deno.env.get(name); - if (!value) throw new Error(`${name} environment variable is required`); + if (!value) throw new Error(`environment variable "${name}" is required D:`); return value; }; diff --git a/utils/logger.ts b/utils/logger.ts deleted file mode 100644 index e36d249..0000000 --- a/utils/logger.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const loggerWithPrefix = (prefixSupplier: () => string) => { - return { - log: (...args: unknown[]) => console.log(prefixSupplier(), ...args), - error: (...args: unknown[]) => console.error(prefixSupplier(), ...args), - }; -}; diff --git a/utils/mod.ts b/utils/mod.ts index 4e907df..a8a0751 100644 --- a/utils/mod.ts +++ b/utils/mod.ts @@ -1,4 +1,4 @@ -export * from "./logger.ts"; +export * from "./trace.ts"; export * from "./env.ts"; export * from "./run.ts"; export * from "./secret.ts"; diff --git a/utils/run.ts b/utils/run.ts index f06ef97..60ae1e6 100644 --- a/utils/run.ts +++ b/utils/run.ts @@ -15,7 +15,7 @@ export const getStdout = async ( const stdoutText = new TextDecoder().decode(stdout); const stderrText = new TextDecoder().decode(stderr); - if (code !== 0) throw new Error(`Command failed\n${stderrText}`); + if (code !== 0) throw new Error(`command failed\n${stderrText}`); return stdoutText; }; diff --git a/utils/secret.ts b/utils/secret.ts index eb2054b..0faa97c 100644 --- a/utils/secret.ts +++ b/utils/secret.ts @@ -1,35 +1,47 @@ -import { getRequiredEnv, getStdout, loggerWithPrefix } from "./mod.ts"; +import { + getRequiredEnv, + getStdout, + loggerWithPrefix, + type PrefixLogger, +} from "./mod.ts"; -const logger = loggerWithPrefix(() => - `[${new Date().toISOString()}] [BitwardenSession]` -); export class BitwardenSession { private readonly sessionInitializer: Promise<string>; + private readonly logger: PrefixLogger; - constructor(server = getRequiredEnv("BW_SERVER")) { + constructor( + server = getRequiredEnv("BW_SERVER"), + logger = loggerWithPrefix(), + ) { ["BW_CLIENTID", "BW_CLIENTSECRET"].forEach(getRequiredEnv); + const instanceId = crypto.randomUUID().split("-").at(0); + this.logger = logger.withAdditionalPrefix(() => + `[BitwardenSession.instance.${instanceId}]` + ); + this.sessionInitializer = getStdout( `bw config server ${server} --quiet`, ) .then(() => { - logger.log("Logging in via API"); + this.logger.log("logging in via api (˘ω˘)"); return getStdout(`bw login --apikey --quiet`); }) .then(() => { - logger.log("Unlocking vault in session"); + this.logger.log("unlocking the secret vault~ (◕ᴗ◕✿)"); return getStdout(`bw unlock --passwordenv BW_PASSWORD --raw`); }) .then((session) => { - logger.log(`Session ${session}`); - return session.trim(); + const _session = session.trim(); + this.logger.log(`got my session key (>ᴗ<) ${_session}`); + return _session; }); } public async getItem<T extends LoginItem | SecureNote>( secretName: string, ): Promise<T> { - logger.log(`Finding secret ${secretName}`); + this.logger.log(`looking for your secret ${secretName} (⑅˘꒳˘)`); return await this.sessionInitializer.then((session) => getStdout(`bw list items`, { env: { @@ -39,8 +51,12 @@ export class BitwardenSession { ).then((items) => JSON.parse(items)).then((items) => items.find(({ name }: { name: string }) => name === secretName) ).then((item) => { - if (!item) throw new Error("Could not find bitwarden item " + secretName); - logger.log(`Found secret: ${secretName}`); + if (!item) { + throw new Error( + "couldn't find the bitwarden item " + secretName + " (。•́︿•̀。)", + ); + } + this.logger.log(`yay! found secret: ${secretName} (*ˊᗜˋ*)`); return item; }); } @@ -49,7 +65,7 @@ export class BitwardenSession { return await this.sessionInitializer.then((session) => getStdout(`bw lock`, { env: { BW_SESSION: session } }) ).then(() => { - logger.log("Locked session"); + this.logger.log("all locked up and secure now~ (。•̀ᴗ-)✧"); }); } } diff --git a/utils/trace.ts b/utils/trace.ts new file mode 100644 index 0000000..737aa60 --- /dev/null +++ b/utils/trace.ts @@ -0,0 +1,111 @@ +export interface Logger { + log: (...args: unknown[]) => void; + debug: (...args: unknown[]) => void; + warn: (...args: unknown[]) => void; + error: (...args: unknown[]) => void; +} + +type Supplier<T> = () => T; +type TraceSupplier = Supplier<string>; +export interface ITraceableLogger<L extends ITraceableLogger<L>> + extends Logger { + addTracer: (traceSupplier: TraceSupplier) => L; +} + +export type ITraceableTuple<T> = [T, TraceSupplier]; +export type ITraceableMapper<T, L extends ITraceableLogger<L>, U> = ( + t: ITraceable<T, L>, +) => U; +export interface ITraceable<T, L extends ITraceableLogger<L>> { + item: T; + logger: L; + + map: <U>(mapper: ITraceableMapper<T, L, U>) => ITraceable<U, L>; + bimap: <U>( + mapper: ITraceableMapper<T, L, ITraceableTuple<U>>, + ) => ITraceable<U, L>; + peek: (peek: ITraceableMapper<T, L, void>) => ITraceable<T, L>; + flatMap: <U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>) => ITraceable<U, L>; + flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L>; +} + +export class TraceableLogger + implements ITraceableLogger<TraceableLogger> { + private readonly logger: Logger = console; + constructor( + private readonly traces = [() => `[${new Date().toISOString()}]`], + ) { + } + + public debug(...args: unknown[]) { + this.logger.debug("[DEBUG]", ...this.getPrefix(), args); + } + + public log(...args: unknown[]) { + this.logger.log("[INFO]", ...this.getPrefix(), args); + } + + public warn(...args: unknown[]) { + this.logger.warn("[WARN]", ...this.getPrefix(), args); + } + + public error(...args: unknown[]) { + this.logger.error("[ERROR]", ...this.getPrefix(), args); + } + + public addTracer(traceSupplier: TraceSupplier) { + return new TraceableLogger(this.traces.concat(traceSupplier)); + } + + private getPrefix() { + return this.traces.map((tracer) => tracer()); + } +} + +export class TraceableImpl< + T, + L extends ITraceableLogger<L>, +> implements ITraceable<T, L> { + private constructor(readonly item: T, readonly logger: L) {} + + public map<U>(mapper: ITraceableMapper<T, L, U>) { + const result = mapper(this); + return new TraceableImpl(result, this.logger); + } + + public flatMap<U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>): ITraceable<U, L> { + return mapper(this); + } + + public flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L> { + return new TraceableImpl(mapper(this).then((i) => ) + } + + public peek(peek: ITraceableMapper<T, L, void>) { + peek(this); + return this; + } + + public bimap<U>(mapper: ITraceableMapper<T, L, ITraceableTuple<U>>) { + const [item, trace] = mapper(this); + return new TraceableImpl(item, this.logger.addTracer(trace)); + } + + static promiseify<T, L extends ITraceableLogger<L>, U>( + mapper: ITraceableMapper<T, L, U>, + ): ITraceableMapper<Promise<T>, L, Promise<U>> { + return (traceablePromise) => traceablePromise.map( + async ({ item: promise }) => { + const t = await promise; + return traceablePromise.map(() => t).map(mapper).item; + }); +// return (traceable) => +// traceable.item.then((item) => mapper(new TraceableImpl(item, traceable.logger))); + } + + static from<T>(t: T) { + return new TraceableImpl(t, new TraceableLogger()); + } +} + +export interface Traceable<T> extends ITraceable<T, TraceableLogger> diff --git a/utils/validate_identifier.ts b/utils/validate_identifier.ts index 0c9242c..c204497 100644 --- a/utils/validate_identifier.ts +++ b/utils/validate_identifier.ts @@ -1,3 +1,11 @@ export const validateIdentifier = (token: string) => { return (/^[a-zA-Z0-9_\-:. \/]+$/).test(token) && !token.includes(".."); }; + +export const invalidExecutionEntriesOf = ( + obj: Record<string, string>, +): Array<[string, string]> => { + return Object.entries(obj).filter((e) => + !e.every((x) => typeof x === "string" && validateIdentifier(x)) + ); +}; |