Skip to content

Instantly share code, notes, and snippets.

@Siedrix
Last active April 23, 2025 00:18
Show Gist options
  • Save Siedrix/c70f1297f96f77668ef4fbe289eb731a to your computer and use it in GitHub Desktop.
Save Siedrix/c70f1297f96f77668ef4fbe289eb731a to your computer and use it in GitHub Desktop.
Mini forge
import { TaskInstanceType } from './lib/task';
import { getPrice } from './tasks/stocks/getPrice';
const tasks: Record<string, TaskInstanceType> = {
foo: getPrice
}
async function main() {
const params = process.argv.slice(2);
const taskName = params[0];
const args = JSON.parse(params[1]);
console.log('Running with:', taskName, args);
const task = tasks[taskName];
if (!task) {
console.error('Task not found');
process.exit(1);
}
try {
const result = await task.run(args);
console.log('Result:', result);
} catch (error) {
console.error('Error:', error);
process.exit(1);
}
const log = task.getLog();
console.log('================================');
console.log('Log:', log);
console.log('================================');
}
main().catch(console.error);
import { createTask } from '../../lib/task-with-boundaries'
import { z } from 'zod'
const zodSchema = z.object({
ticker: z.string()
})
const boundaries = {
// Add your boundary functions here
fetchYahooFinance: async (ticker: string) => {
const url = `https://query1.finance.yahoo.com/v8/finance/chart/${ticker}`
const response = await fetch(url)
const data = await response.json()
const quote = data.chart.result[0].meta
const price = quote.regularMarketPrice
const currency = quote.currency
return {
price,
currency
}
}
}
export const getPrice = createTask(
zodSchema,
boundaries,
async function ({ ticker }, { fetchYahooFinance }) {
const { price, currency } = await fetchYahooFinance(ticker)
console.log('================================');
console.log('Price:', price);
console.log('================================');
return {
status: 'Ok',
ticker,
price,
currency,
timestamp: new Date().toISOString()
}
}
)
Add the task and task with boundaries on the src/lib folder
Add the cli on the src folder
Create a tsconfig of something like
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"declaration": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Then do `ts-node src/cli.ts foo '{"ticker": "AAPL"}'`
import { z } from 'zod';
// Basic type definitions
export type BaseFunction = (...args: any[]) => any;
// Simplified boundary types
export type BoundaryFunction<TReturn = any> = (...args: any[]) => Promise<TReturn>;
export type Boundaries = Record<string, BoundaryFunction>;
/**
* Represents a record of a boundary function call
*/
export interface BoundaryRecord<TInput = any[], TOutput = any> {
input: TInput;
output?: TOutput;
error?: string;
}
/**
* Represents a wrapped boundary function with additional methods
*/
export interface WrappedBoundaryFunction<Func extends BoundaryFunction = BoundaryFunction> {
(...args: Parameters<Func>): Promise<ReturnType<Func>>;
getRunData: () => Array<BoundaryRecord<Parameters<Func>, Awaited<ReturnType<Func>>>>;
startRun: () => void;
}
/**
* Represents a collection of wrapped boundary functions
*/
export type WrappedBoundaries<B extends Boundaries = Boundaries> = {
[K in keyof B]: WrappedBoundaryFunction<B[K]>
};
/**
* Creates a simplified boundary wrapper
*/
export function createBoundary<Func extends BoundaryFunction>(fn: Func): WrappedBoundaryFunction<Func> {
type FuncInput = Parameters<Func>;
type FuncOutput = Awaited<ReturnType<Func>>;
type RecordType = BoundaryRecord<FuncInput, FuncOutput>;
let runLog: RecordType[] = [];
let hasRun: boolean = false;
const wrappedFn = async (...args: Parameters<Func>): Promise<ReturnType<Func>> => {
const record: RecordType = {
input: args
};
try {
const result = await fn(...args);
record.output = result as FuncOutput;
if (hasRun) {
runLog.push(record);
}
return result;
} catch (error: any) {
record.error = error.message;
if (hasRun) {
runLog.push(record);
}
throw error;
}
};
// Run log management
wrappedFn.startRun = function(): void {
runLog = [];
hasRun = true;
};
wrappedFn.getRunData = function(): Array<RecordType> {
return runLog;
};
return wrappedFn;
}
/**
* Represents the record of a task execution
*/
export interface TaskRecord<InputType = unknown, OutputType = unknown> {
/** The input arguments passed to the task */
input: InputType;
/** The output returned by the task (if successful) */
output?: OutputType;
/** The error message if the task failed */
error?: string;
/** Timestamp of when this task was executed */
timestamp: number;
/** Boundary execution data */
boundaries?: Record<string, unknown>;
}
export interface TaskInstanceType<Func extends BaseFunction = BaseFunction, B extends Boundaries = Boundaries> {
// Run the task
run: (argv?: Parameters<Func>[0]) => Promise<ReturnType<Func>>;
// Validation methods
validate: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => z.SafeParseReturnType<T, T> | undefined;
isValid: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => boolean;
// Logging methods
getLog: () => TaskRecord<Parameters<Func>[0], ReturnType<Func>> | undefined;
// Listener methods
addListener: <I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: TaskRecord<I, O>) => void) => void;
removeListener: () => void;
// Boundary methods
getBoundaries: () => WrappedBoundaries<B>;
setBoundariesData: (data: Record<string, unknown>) => void;
getBoundariesRunLog: () => Record<string, unknown>;
}
export class Task<
Func extends BaseFunction = BaseFunction,
B extends Boundaries = Boundaries
> implements TaskInstanceType<Func, B> {
private _fn: Func;
private _schema?: z.ZodType;
private _listener?: (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void;
private _lastLog?: TaskRecord<Parameters<Func>[0], ReturnType<Func>>;
private _boundaries: WrappedBoundaries<B>;
constructor(
fn: Func,
config: {
schema?: z.ZodType;
boundaries?: B;
boundariesData?: Record<string, unknown>;
} = {}
) {
this._fn = fn;
this._schema = config.schema;
this._boundaries = this._createBoundaries(config.boundaries || {} as B);
// Set boundaries data if provided
if (config.boundariesData) {
this.setBoundariesData(config.boundariesData);
}
}
/**
* Create wrapped boundaries from boundary definitions
*/
private _createBoundaries(definition: B): WrappedBoundaries<B> {
const boundariesFns: Record<string, WrappedBoundaryFunction> = {};
for (const name in definition) {
boundariesFns[name] = createBoundary(definition[name]);
}
return boundariesFns as WrappedBoundaries<B>;
}
/**
* Get the wrapped boundaries
*/
getBoundaries(): WrappedBoundaries<B> {
return this._boundaries;
}
/**
* Set boundaries data (for backward compatibility)
* Note: In this simplified version, this is a no-op
*/
setBoundariesData(data: Record<string, unknown>): void {
// This is kept for backward compatibility
// In a simplified version, we don't need to do anything with this data
}
/**
* Get the run log data from all boundaries
*/
getBoundariesRunLog(): Record<string, unknown> {
const boundaries = this._boundaries;
const boundariesRunLog: Record<string, unknown> = {};
for (const name in boundaries) {
const boundary = boundaries[name];
boundariesRunLog[name] = boundary.getRunData();
}
return boundariesRunLog;
}
/**
* Start the run log for all boundaries
*/
private startRunLog(): void {
const boundaries = this._boundaries;
for (const name in boundaries) {
const boundary = boundaries[name];
boundary.startRun();
}
}
/**
* Validate input against the schema
*/
validate<T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T): z.SafeParseReturnType<T, T> | undefined {
if (!this._schema) {
return undefined;
}
return this._schema.safeParse(argv) as z.SafeParseReturnType<T, T>;
}
/**
* Check if the input is valid according to the schema
*/
isValid<T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T): boolean {
if (!this._schema) {
return true;
}
const result = this._schema.safeParse(argv);
return result.success;
}
/**
* Add a listener for task execution events
*/
addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: TaskRecord<I, O>) => void): void {
this._listener = fn as (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void;
}
/**
* Remove the current listener
*/
removeListener(): void {
this._listener = undefined;
}
/**
* Get the last execution log
*/
getLog(): TaskRecord<Parameters<Func>[0], ReturnType<Func>> | undefined {
return this._lastLog;
}
/**
* Emit a task execution event
*/
private emit(data: Partial<TaskRecord>): void {
const record = {
...data,
timestamp: Date.now(),
boundaries: this.getBoundariesRunLog()
} as TaskRecord<Parameters<Func>[0], ReturnType<Func>>;
// Store the log
this._lastLog = record;
// Notify listener if exists
if (this._listener) {
this._listener(record);
}
}
/**
* Run the task with the given arguments
*/
async run(argv?: Parameters<Func>[0]): Promise<ReturnType<Func>> {
// Start run log for boundaries
this.startRunLog();
try {
// Validate input if schema exists
if (this._schema) {
const validation = this._schema.safeParse(argv);
if (!validation.success) {
const formattedErrors = validation.error.errors.map(err =>
`${err.path.join('.')}: ${err.message}`
).join(', ');
const errorMessage = formattedErrors
? `Invalid input on: ${formattedErrors}`
: 'Invalid input';
this.emit({
input: argv,
error: errorMessage
});
throw new Error(errorMessage);
}
}
// Execute the task function with boundaries
const output = await this._fn(
argv as Parameters<Func>[0],
this._boundaries as unknown as Parameters<Func>[1]
);
// Log success
this.emit({
input: argv,
output
});
return output;
} catch (error: any) {
// Log error
this.emit({
input: argv,
error: error.message
});
throw error;
}
}
}
/**
* Helper function to create a task with proper type inference
*/
export function createTask<
TSchema extends z.ZodType,
B extends Boundaries,
TReturn
>(
schema: TSchema,
boundaries: B,
fn: (argv: z.infer<TSchema>, boundaries: WrappedBoundaries<B>) => Promise<TReturn>,
config?: {
boundariesData?: Record<string, unknown>;
}
): TaskInstanceType<(argv: z.infer<TSchema>, boundaries: WrappedBoundaries<B>) => Promise<TReturn>, B> {
return new Task(
fn,
{
schema,
boundaries,
...config
}
);
}
import { z } from 'zod';
// Basic type definitions
export type BaseFunction = (...args: any[]) => any;
/**
* Represents the record of a task execution
*/
export interface TaskRecord<InputType = unknown, OutputType = unknown> {
/** The input arguments passed to the task */
input: InputType;
/** The output returned by the task (if successful) */
output?: OutputType;
/** The error message if the task failed */
error?: string;
/** Timestamp of when this task was executed */
timestamp: number;
}
export interface TaskInstanceType<Func extends BaseFunction = BaseFunction> {
// Run the task
run: (argv?: Parameters<Func>[0]) => Promise<ReturnType<Func>>;
// Validation methods
validate: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => z.SafeParseReturnType<T, T> | undefined;
isValid: <T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T) => boolean;
// Logging methods
getLog: () => TaskRecord<Parameters<Func>[0], ReturnType<Func>> | undefined;
// Listener methods
addListener: <I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: TaskRecord<I, O>) => void) => void;
removeListener: () => void;
setBoundariesData: (data: Record<string, unknown>) => void;
}
export class Task<Func extends BaseFunction = BaseFunction> implements TaskInstanceType<Func> {
private _fn: Func;
private _schema?: z.ZodType;
private _listener?: (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void;
private _lastLog?: TaskRecord<Parameters<Func>[0], ReturnType<Func>>;
constructor(
fn: Func,
config: {
schema?: z.ZodType;
} = {}
) {
this._fn = fn;
this._schema = config.schema;
}
/**
* Validate input against the schema
*/
validate<T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T): z.SafeParseReturnType<T, T> | undefined {
if (!this._schema) {
return undefined;
}
return this._schema.safeParse(argv) as z.SafeParseReturnType<T, T>;
}
/**
* Check if the input is valid according to the schema
*/
isValid<T extends Record<string, unknown> = Parameters<Func>[0]>(argv?: T): boolean {
if (!this._schema) {
return true;
}
const result = this._schema.safeParse(argv);
return result.success;
}
/**
* Add a listener for task execution events
*/
addListener<I = Parameters<Func>[0], O = ReturnType<Func>>(fn: (record: TaskRecord<I, O>) => void): void {
this._listener = fn as (record: TaskRecord<Parameters<Func>[0], ReturnType<Func>>) => void;
}
/**
* Remove the current listener
*/
removeListener(): void {
this._listener = undefined;
}
setBoundariesData(data: Record<string, unknown>): void {
// just a mock for now
}
/**
* Get the last execution log
*/
getLog(): TaskRecord<Parameters<Func>[0], ReturnType<Func>> | undefined {
return this._lastLog;
}
/**
* Emit a task execution event
*/
private emit(data: Partial<TaskRecord>): void {
const record = {
...data,
timestamp: Date.now()
} as TaskRecord<Parameters<Func>[0], ReturnType<Func>>;
// Store the log
this._lastLog = record;
// Notify listener if exists
if (this._listener) {
this._listener(record);
}
}
/**
* Run the task with the given arguments
*/
async run(argv?: Parameters<Func>[0]): Promise<ReturnType<Func>> {
try {
// Validate input if schema exists
if (this._schema) {
const validation = this._schema.safeParse(argv);
if (!validation.success) {
const formattedErrors = validation.error.errors.map(err =>
`${err.path.join('.')}: ${err.message}`
).join(', ');
const errorMessage = formattedErrors
? `Invalid input on: ${formattedErrors}`
: 'Invalid input';
this.emit({
input: argv,
error: errorMessage
});
throw new Error(errorMessage);
}
}
// Execute the task function
const output = await this._fn(argv as Parameters<Func>[0]);
// Log success
this.emit({
input: argv,
output
});
return output;
} catch (e) {
const error = e as Error;
// Log error
this.emit({
input: argv,
error: error.message
});
throw error;
}
}
}
/**
* Helper function to create a task with proper type inference
*/
export function createTask<
TSchema extends z.ZodType,
TReturn
>(
schema: TSchema,
fn: (argv: z.infer<TSchema>) => Promise<TReturn>
): TaskInstanceType<(argv: z.infer<TSchema>) => Promise<TReturn>> {
return new Task(
fn,
{
schema
}
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment