
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
@forgehive/task
Advanced tools
A powerful task management library for creating type-safe, boundary-separated tasks with validation.
A powerful task management library for creating type-safe, boundary-separated tasks with validation.
npm install @forgehive/task
The @forgehive/task
package provides a way to create strongly-typed tasks with:
Here's a simple example of creating a task:
import { createTask, Schema } from '@forgehive/task';
// Define a schema for task input validation
const schema = new Schema({
name: Schema.string(),
age: Schema.number().optional()
});
// Define boundaries (external dependencies)
const boundaries = {
saveToDatabase: async (data: any): Promise<void> => {
// Implementation...
},
sendEmail: async (to: string, subject: string): Promise<boolean> => {
// Implementation...
}
};
// Create the task with type inference
const registerUser = createTask(
schema,
boundaries,
async (argv, boundaries) => {
// argv is typed based on the schema (has name: string, age?: number)
console.log(`Registering user: ${argv.name}`);
// Call boundaries with type safety
await boundaries.saveToDatabase({ name: argv.name, age: argv.age });
const emailSent = await boundaries.sendEmail(
'admin@example.com',
`New user registered: ${argv.name}`
);
return {
success: true,
emailSent,
user: { name: argv.name, age: argv.age }
};
}
);
// Execute the task
const result = await registerUser.run({ name: 'John Doe', age: 30 });
The createTask
function provides full type inference:
You can add listeners to track task execution:
registerUser.addListener((record) => {
console.log('Task executed:', record);
// record contains:
// - input: The input arguments
// - output: The task result (if successful)
// - error: Error message (if failed)
// - boundaries: Boundary execution data
});
Tasks support different execution modes:
proxy
: Normal execution (default)proxy-pass
: Use recorded data if available, otherwise execute normallyproxy-catch
: Use recorded data if execution failsreplay
: Only use recorded data, fail if not available// Change execution mode
registerUser.setMode('replay');
You can provide pre-recorded boundary data:
registerUser.setBoundariesData({
saveToDatabase: [
{ input: [{ name: 'John', age: 30 }], output: undefined }
],
sendEmail: [
{ input: ['admin@example.com', 'New user registered: John'], output: true }
]
});
Here's an example of a task used in a CLI application:
import { createTask } from '@forgehive/task';
import { Schema } from '@forgehive/schema';
import path from 'path';
import fs from 'fs/promises';
// Define the schema with optional dryRun flag
const schema = new Schema({
dryRun: Schema.boolean().optional()
});
// Define boundaries for file operations
const boundaries = {
saveFile: async (path: string, content: string): Promise<void> => {
await fs.writeFile(path, content);
}
};
// Create the init task
export const init = createTask(
schema,
boundaries,
async (argv, boundaries) => {
// Handle the dryRun flag
const isDryRun = Boolean(argv.dryRun);
const shadowPath = path.join(process.cwd(), 'shadow.json');
const config = {
project: { name: 'MyProject' },
// ... other config properties
};
const content = JSON.stringify(config, null, 2);
// Conditionally create the file based on dryRun
if (!isDryRun) {
await boundaries.saveFile(shadowPath, content);
console.log(`Created shadow.json at ${shadowPath}`);
} else {
console.log('Dry run, not creating shadow.json');
console.log(content);
}
return config;
}
);
The boundary separation makes tasks easy to test:
import { init } from './tasks/init';
describe('Init task', () => {
it('should create a config file when dryRun is false', async () => {
// Mock the saveFile boundary
const saveFileMock = jest.fn();
// Override the boundaries
init.getBoundaries().saveFile = saveFileMock;
// Run the task
await init.run({ dryRun: false });
// Verify the boundary was called
expect(saveFileMock).toHaveBeenCalled();
});
it('should not create a file when dryRun is true', async () => {
// Mock the saveFile boundary
const saveFileMock = jest.fn();
// Override the boundaries
init.getBoundaries().saveFile = saveFileMock;
// Run the task
await init.run({ dryRun: true });
// Verify the boundary was not called
expect(saveFileMock).not.toHaveBeenCalled();
});
});
createTask<S, B, R>(schema, boundaries, fn, config?)
Creates a new task with type inference.
schema
: A Schema instance for input validationboundaries
: An object containing boundary functionsfn
: The task function that receives validated input and boundariesconfig
: Optional configuration (mode, boundariesData)Returns a TaskInstanceType
with methods for running and managing the task.
TaskInstanceType
Methodsrun(argv?)
: Executes the task with the given argumentsaddListener(fn)
: Adds a listener for task executionremoveListener()
: Removes the current listenergetMode()
/ setMode(mode)
: Get/set the execution modegetBoundaries()
: Get the wrapped boundary functionssetBoundariesData(data)
: Set pre-recorded boundary datavalidate(argv?)
: Validate input without running the taskisValid(argv?)
: Check if input is validasBoundary()
: Convert the task to a boundary functionTasks automatically provide a setMetadata
boundary that allows you to add custom metadata to execution records. This metadata is useful for tracking, debugging, and analytics.
import { createTask, Schema } from '@forgehive/task';
const schema = new Schema({
userId: Schema.string(),
operation: Schema.string()
});
const boundaries = {
processPayment: async (amount: number): Promise<string> => {
// Payment processing logic
return 'payment-id-123';
}
};
const processUserAction = createTask({
schema,
boundaries,
fn: async ({ userId, operation }, { processPayment, setMetadata }) => {
// Add metadata at the beginning
await setMetadata('userId', userId);
await setMetadata('environment', 'production');
if (operation === 'payment') {
await setMetadata('step', 'processing-payment');
const paymentId = await processPayment(100);
await setMetadata('step', 'payment-completed');
await setMetadata('paymentId', paymentId);
return { success: true, paymentId };
}
await setMetadata('step', 'completed');
return { success: true };
}
});
When you run a task, the metadata appears in the execution record:
const [result, error, record] = await processUserAction.safeRun({
userId: 'user-123',
operation: 'payment'
});
console.log(record.metadata);
// Output:
// {
// userId: 'user-123',
// environment: 'production',
// step: 'payment-completed',
// paymentId: 'payment-id-123'
// }
setMetadata
calls don't appear in boundary execution logsconst registerUser = createTask({
schema: new Schema({
email: Schema.string(),
plan: Schema.string()
}),
boundaries: {
validateEmail: async (email: string) => true,
createUser: async (userData: any) => ({ id: 'user-123' }),
sendWelcomeEmail: async (email: string) => true
},
fn: async ({ email, plan }, { validateEmail, createUser, sendWelcomeEmail, setMetadata }) => {
// Track the registration flow
await setMetadata('registrationFlow', 'started');
await setMetadata('plan', plan);
await setMetadata('timestamp', Date.now().toString());
// Validate email
await setMetadata('step', 'validating-email');
const isValidEmail = await validateEmail(email);
if (!isValidEmail) {
await setMetadata('step', 'validation-failed');
throw new Error('Invalid email');
}
// Create user
await setMetadata('step', 'creating-user');
const user = await createUser({ email, plan });
await setMetadata('userId', user.id);
// Send welcome email
await setMetadata('step', 'sending-welcome-email');
await sendWelcomeEmail(email);
await setMetadata('step', 'completed');
await setMetadata('registrationFlow', 'success');
return { userId: user.id, success: true };
}
});
Metadata is particularly useful for tracking task performance and user behavior:
// Add listener to track analytics
registerUser.addListener((record) => {
// Send metadata to analytics service
analytics.track('task_executed', {
taskName: record.taskName,
success: record.type === 'success',
metadata: record.metadata,
duration: Date.now() - parseInt(record.metadata?.timestamp || '0')
});
});
Tasks automatically receive environment metadata when executed in different contexts:
environment: 'cli'
environment: 'hive-lambda'
safeRun(args, context)
You can combine this with your custom metadata:
const [result, error, record] = await task.safeRun(
{ userId: 'user-123' },
{ executionContext: 'background-job', version: '1.2.3' }
);
// record.metadata will contain both your custom metadata and the context
MIT
FAQs
A powerful task management library for creating type-safe, boundary-separated tasks with validation.
We found that @forgehive/task demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.