diff options
Diffstat (limited to 'u/process')
-rw-r--r-- | u/process/env.ts | 36 | ||||
-rw-r--r-- | u/process/mod.ts | 3 | ||||
-rw-r--r-- | u/process/run.ts | 64 | ||||
-rw-r--r-- | u/process/validate_identifier.ts | 24 |
4 files changed, 127 insertions, 0 deletions
diff --git a/u/process/env.ts b/u/process/env.ts new file mode 100644 index 0000000..0e41b4f --- /dev/null +++ b/u/process/env.ts @@ -0,0 +1,36 @@ +import { Either, type IEither } from "@emprespresso/pengueno"; + +export const getRequiredEnv = <V extends string>(name: V): IEither<Error, V> => + Either + .fromFailable<Error, V>(() => Deno.env.get(name) as V) // could throw when no permission. + .flatMap((v) => + (v && Either.right(v)) || + Either.left( + new Error(`environment variable "${name}" is required D:`), + ) + ); + +type ObjectFromList<T extends ReadonlyArray<string>, V = string> = { + [K in (T extends ReadonlyArray<infer U> ? U : never)]: V; +}; + +export const getRequiredEnvVars = <V extends string>(vars: ReadonlyArray<V>) => + vars + .map((envVar) => [envVar, getRequiredEnv(envVar)] as [V, IEither<Error, V>]) + .reduce( + ( + acc: IEither<Error, ObjectFromList<typeof vars>>, + x: [V, IEither<Error, V>], + ) => { + const [envVar, eitherVal] = x; + return acc.flatMap((args) => { + return eitherVal.mapRight((envValue) => + ({ + ...args, + [envVar]: envValue, + }) as ObjectFromList<typeof vars> + ); + }); + }, + Either.right({} as ObjectFromList<typeof vars>), + ); diff --git a/u/process/mod.ts b/u/process/mod.ts new file mode 100644 index 0000000..3f02d46 --- /dev/null +++ b/u/process/mod.ts @@ -0,0 +1,3 @@ +export * from "./env.ts"; +export * from "./run.ts"; +export * from "./validate_identifier.ts"; diff --git a/u/process/run.ts b/u/process/run.ts new file mode 100644 index 0000000..4954438 --- /dev/null +++ b/u/process/run.ts @@ -0,0 +1,64 @@ +import { + Either, + type IEither, + type ITraceable, + LogLevel, + TraceUtil, +} from "@emprespresso/pengueno"; + +export type Command = string[] | string; +type CommandOutputDecoded = { + code: number; + stdoutText: string; + stderrText: string; +}; + +export const getStdout = <Trace>( + c: ITraceable<Command, Trace>, + options: Deno.CommandOptions = {}, +): Promise<IEither<Error, string>> => + c.bimap(TraceUtil.withFunctionTrace(getStdout)) + .map((tCmd) => { + const cmd = tCmd.get(); + tCmd.trace.trace(`:> im gonna run this command! ${cmd}`); + const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; + return new Deno.Command(exec, { + args, + stdout: "piped", + stderr: "piped", + ...options, + }).output(); + }) + .map((tOut) => + Either.fromFailableAsync<Error, Deno.CommandOutput>(tOut.get()) + ) + .map( + TraceUtil.promiseify((tEitherOut) => + tEitherOut.get().flatMap(({ code, stderr, stdout }) => + Either + .fromFailable<Error, CommandOutputDecoded>(() => { + const stdoutText = new TextDecoder().decode(stdout); + const stderrText = new TextDecoder().decode(stderr); + return { code, stdoutText, stderrText }; + }) + .mapLeft((e) => { + tEitherOut.trace.addTrace(LogLevel.ERROR).trace(`o.o wat ${e}`); + return new Error(`${e}`); + }) + .flatMap((decodedOutput): Either<Error, string> => { + const { code, stdoutText, stderrText } = decodedOutput; + tEitherOut.trace.addTrace(LogLevel.DEBUG).trace( + `stderr hehehe ${stderrText}`, + ); + if (code !== 0) { + const msg = + `i weceived an exit code of ${code} i wanna zewoooo :<`; + tEitherOut.trace.addTrace(LogLevel.ERROR).trace(msg); + return Either.left(new Error(msg)); + } + return Either.right(stdoutText); + }) + ) + ), + ) + .get(); diff --git a/u/process/validate_identifier.ts b/u/process/validate_identifier.ts new file mode 100644 index 0000000..32952a6 --- /dev/null +++ b/u/process/validate_identifier.ts @@ -0,0 +1,24 @@ +import { Either, type IEither } from "@emprespresso/pengueno"; + +export const validateIdentifier = (token: string) => { + return (/^[a-zA-Z0-9_\-:. \/]+$/).test(token) && !token.includes(".."); +}; + +// ensure {@param obj} is a Record<string, string> with stuff that won't +// have the potential for shell injection, just to be super safe. +type InvalidEntry<K, T> = [K, T]; +export const validateExecutionEntries = < + T, + K extends symbol | number | string = symbol | number | string, +>( + obj: Record<K, T>, +): IEither< + Array<InvalidEntry<K, T>>, + Record<string, string> +> => { + const invalidEntries = <Array<InvalidEntry<K, T>>> Object.entries(obj).filter( + (e) => !e.every((x) => typeof x === "string" && validateIdentifier(x)), + ); + if (invalidEntries.length > 0) return Either.left(invalidEntries); + return Either.right(<Record<string, string>> obj); +}; |