summaryrefslogtreecommitdiff
path: root/u/process
diff options
context:
space:
mode:
Diffstat (limited to 'u/process')
-rw-r--r--u/process/argv.ts77
-rw-r--r--u/process/env.ts53
-rw-r--r--u/process/index.ts4
-rw-r--r--u/process/mod.ts4
-rw-r--r--u/process/run.ts102
-rw-r--r--u/process/validate_identifier.ts23
6 files changed, 114 insertions, 149 deletions
diff --git a/u/process/argv.ts b/u/process/argv.ts
index 45f8ff0..dcdba85 100644
--- a/u/process/argv.ts
+++ b/u/process/argv.ts
@@ -1,7 +1,6 @@
-import { Either, type Mapper, type IEither } from "@emprespresso/pengueno";
+import { Either, type Mapper, type IEither } from '@emprespresso/pengueno';
-export const isArgKey = <K extends string>(k: string): k is K =>
- k.startsWith("--");
+export const isArgKey = <K extends string>(k: string): k is K => k.startsWith('--');
interface ArgHandler<V> {
absent?: V;
@@ -10,42 +9,42 @@ interface ArgHandler<V> {
}
export const getArg = <K extends string, V>(
- arg: K,
- argv: Array<string>,
- whenValue: ArgHandler<V>,
+ arg: K,
+ argv: Array<string>,
+ whenValue: ArgHandler<V>,
): IEither<Error, V> => {
- const value = argv.filter((_argv) => isArgKey(_argv) && _argv.split("=")[0] === arg).map((_argv, i) => {
- const next = _argv.includes("=") ? _argv.split("=")[1] : argv.at(i + 1);
- if (next) {
- if (isArgKey(next)) return whenValue.unspecified;
- return whenValue.present(next);
- }
- return whenValue.unspecified;
- }).find(x => x) ?? whenValue.absent;
+ const value =
+ argv
+ .filter((_argv) => isArgKey(_argv) && _argv.split('=')[0] === arg)
+ .map((_argv, i) => {
+ const next = _argv.includes('=') ? _argv.split('=')[1] : argv.at(i + 1);
+ if (next) {
+ if (isArgKey(next)) return whenValue.unspecified;
+ return whenValue.present(next);
+ }
+ return whenValue.unspecified;
+ })
+ .find((x) => x) ?? whenValue.absent;
if (value === undefined) {
- return Either.left(new Error("no value specified for " + arg));
+ return Either.left(new Error('no value specified for ' + arg));
}
return Either.right(value);
};
type MappedArgs<
- Args extends ReadonlyArray<string>,
- Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
+ Args extends ReadonlyArray<string>,
+ Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>,
> = {
- [K in Args[number]]: K extends keyof Handlers
- ? Handlers[K] extends ArgHandler<infer T>
- ? T
- : string
- : string;
+ [K in Args[number]]: K extends keyof Handlers ? (Handlers[K] extends ArgHandler<infer T> ? T : string) : string;
};
export const argv = <
- const Args extends ReadonlyArray<string>,
- const Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
+ const Args extends ReadonlyArray<string>,
+ const Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>,
>(
args: Args,
handlers?: Handlers,
- argv = Deno.args,
+ argv = process.argv.slice(2),
): IEither<Error, MappedArgs<Args, Handlers>> => {
type Result = MappedArgs<Args, Handlers>;
@@ -53,20 +52,20 @@ export const argv = <
const processArg = (arg: Args[number]): IEither<Error, [Args[number], unknown]> => {
const handler = handlers?.[arg] ?? defaultHandler;
- return getArg(arg, argv, handler).mapRight(value => [arg, value] as const);
+ return getArg(arg, argv, handler).mapRight((value) => [arg, value] as const);
};
- const argResults = args.map(processArg);
-
- return argResults.reduce(
- (acc: IEither<Error, Partial<Result>>, current: IEither<Error, readonly [Args[number], unknown]>) => {
- return acc.flatMap(accValue =>
- current.mapRight(([key, value]) => ({
- ...accValue,
- [key]: value
- }))
- );
- },
- Either.right({} as Partial<Result>)
- ).mapRight(result => result as Result);
+ return args
+ .map(processArg)
+ .reduce(
+ (acc: IEither<Error, Partial<Result>>, current: IEither<Error, readonly [Args[number], unknown]>) =>
+ acc.flatMap((accValue) =>
+ current.mapRight(([key, value]) => ({
+ ...accValue,
+ [key]: value,
+ })),
+ ),
+ Either.right(<Partial<Result>>{}),
+ )
+ .mapRight((result) => <Result>result);
};
diff --git a/u/process/env.ts b/u/process/env.ts
index 5ba4189..1e4fd32 100644
--- a/u/process/env.ts
+++ b/u/process/env.ts
@@ -1,37 +1,30 @@
-import { Either, type IEither } from "@emprespresso/pengueno";
+import { Either, type IEither } from '@emprespresso/pengueno';
export const getRequiredEnv = <V extends string>(name: V): IEither<Error, V> =>
- Either.fromFailable<Error, V | undefined>(
- () => Deno.env.get(name) as V | undefined,
- ) // could throw when no permission.
- .flatMap(
- (v) =>
- (v && Either.right(v)) ||
- Either.left(new Error(`environment variable "${name}" is required D:`)),
- );
+ Either.fromFailable<Error, V | undefined>(() => process.env[name] as V | undefined) // 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;
+ [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>),
- );
+ 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/index.ts b/u/process/index.ts
new file mode 100644
index 0000000..4ffbf2a
--- /dev/null
+++ b/u/process/index.ts
@@ -0,0 +1,4 @@
+export * from './env.js';
+export * from './run.js';
+export * from './validate_identifier.js';
+export * from './argv.js';
diff --git a/u/process/mod.ts b/u/process/mod.ts
deleted file mode 100644
index 211e9a7..0000000
--- a/u/process/mod.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from "./env.ts";
-export * from "./run.ts";
-export * from "./validate_identifier.ts";
-export * from "./argv.ts";
diff --git a/u/process/run.ts b/u/process/run.ts
index abe143c..e3c4c3d 100644
--- a/u/process/run.ts
+++ b/u/process/run.ts
@@ -1,68 +1,46 @@
import {
- Either,
- type IEither,
- type ITraceable,
- LogLevel,
- type LogTraceSupplier,
- TraceUtil,
-} from "@emprespresso/pengueno";
+ Either,
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ type LogTraceSupplier,
+ TraceUtil,
+} from '@emprespresso/pengueno';
+import { promisify } from 'node:util';
+import { exec as execCallback } from 'node:child_process';
+const exec = promisify(execCallback);
export type Command = string[] | string;
-type CommandOutputDecoded = {
- code: number;
- stdoutText: string;
- stderrText: string;
-};
+export type StdStreams = { stdout: string; stderr: string };
export const getStdout = (
- c: ITraceable<Command, LogTraceSupplier>,
- options: Deno.CommandOptions = {},
+ c: ITraceable<Command, LogTraceSupplier>,
+ options: { env?: Record<string, string>; clearEnv?: boolean } = {},
): Promise<IEither<Error, string>> =>
- c
- .bimap(TraceUtil.withFunctionTrace(getStdout))
- .map((tCmd) => {
- const cmd = tCmd.get();
- tCmd.trace.trace(`Command = ${cmd} :> im gonna run this command! `);
- const [exec, ...args] = typeof cmd === "string" ? cmd.split(" ") : cmd;
- return new Deno.Command(exec, {
- args,
- stdout: "piped",
- stderr: "piped",
- ...options,
- });
- })
- .map((tCmd) =>
- Either.fromFailableAsync<Error, Deno.CommandOutput>(() =>
- tCmd.get().output(),
- ),
- )
- .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(e);
- return e;
- })
- .flatMap((decodedOutput): IEither<Error, string> => {
- const { code, stdoutText, stderrText } = decodedOutput;
- if (stderrText) {
- tEitherOut.trace
- .addTrace(LogLevel.DEBUG)
- .trace(`stderr: ${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();
+ c
+ .bimap(TraceUtil.withFunctionTrace(getStdout))
+ .bimap((tCmd) => {
+ const cmd = tCmd.get();
+ tCmd.trace.trace(`Command = ${cmd} :> im gonna run this command! `);
+
+ const _exec = typeof cmd === 'string' ? cmd : cmd.join(' ');
+ const env = options.clearEnv ? options.env : { ...process.env, ...options.env };
+
+ const p: Promise<IEither<Error, StdStreams>> = Either.fromFailableAsync(exec(_exec, { env }));
+ return [p, `Command = ${_exec}`];
+ })
+ .map(
+ TraceUtil.promiseify(
+ (tEitherProcess): IEither<Error, string> =>
+ tEitherProcess.get().fold(({ isLeft, value }) => {
+ if (isLeft) {
+ return Either.left(value);
+ }
+ if (value.stderr) {
+ tEitherProcess.trace.addTrace(LogLevel.DEBUG).trace(`StdErr = ${value.stderr}`);
+ }
+ return Either.right(value.stdout);
+ }),
+ ),
+ )
+ .get();
diff --git a/u/process/validate_identifier.ts b/u/process/validate_identifier.ts
index 4e93728..1ff3791 100644
--- a/u/process/validate_identifier.ts
+++ b/u/process/validate_identifier.ts
@@ -1,23 +1,18 @@
-import { Either, type IEither } from "@emprespresso/pengueno";
+import { Either, type IEither } from '@emprespresso/pengueno';
export const validateIdentifier = (token: string) => {
- return /^[a-zA-Z0-9_\-:. \/]+$/.test(token) && !token.includes("..");
+ 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>,
+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);
+ 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);
};