@xrundev/sdk
v0.0.2
Published
A modern, type-safe task execution framework with built-in middleware support, comprehensive testing capabilities, and production-ready features.
Downloads
6
Readme
XRun SDK
A modern, type-safe task execution framework with built-in middleware support, comprehensive testing capabilities, and production-ready features.
Features
✅ Type-Safe: Full TypeScript support with strict typing
✅ Testable: Comprehensive unit testing support with dependency injection
✅ Middleware System: Plugin-based architecture for cross-cutting concerns
✅ Production Ready: Built-in error handling, timeouts, and retries
✅ Docker Support: Containerized execution with environment-based configuration
✅ Monitoring: Optional metrics collection and structured logging
Quick Start
1. Installation
npm install @xrundev/sdk2. Define a Task
// tasks/hello-world.ts
import { task } from '@xrundev/sdk';
export const helloWorld = task(
{
name: 'hello-world',
description: 'A simple hello world task',
},
async (payload: { name: string }) => {
return { message: `Hello, ${payload.name}!` };
}
);3. Build and Package
# Build the task runner
npx xrun build
# Package as Docker image
npx xrun package4. Run the Task
docker run --rm \
-e TASK_ID=hello-world \
-e TASK_PAYLOAD='{"name": "World"}' \
-e JOB_ID=job-123 \
xrun-task-runner:latestTesting
The SDK includes comprehensive testing support with automatic console mocking to keep test output clean.
Test Setup
All tests automatically include console mocking to prevent log output from cluttering test results. The test setup file (src/test-setup.ts) mocks all console methods globally.
Writing Tests
import { describe, it, expect, vi } from 'vitest';
import { createConsoleSpy } from '../test-setup.js';
describe('My Task', () => {
it('should execute successfully', async () => {
// Console output is automatically mocked
console.log('This will not appear in test output');
// If you need to verify console calls, use the console spy
const consoleSpy = createConsoleSpy();
// Your test logic here
const result = await myTask({ input: 'test' });
// Verify console calls if needed
expect(consoleSpy.log).toHaveBeenCalledWith('Expected message');
});
});Console Mocking
The following console methods are automatically mocked in all tests:
console.log,console.error,console.warnconsole.info,console.debug,console.traceconsole.dir,console.table,console.groupconsole.time,console.count, and more
Architecture
Task Definition
Tasks are defined using the task() function with a configuration and handler:
import { task } from '@xrundev/sdk';
interface MyPayload {
input: string;
options?: {
uppercase?: boolean;
};
}
interface MyResult {
output: string;
metadata: {
processedAt: string;
};
}
export const myTask = task<MyPayload, MyResult>(
{
name: 'my-task',
description: 'Processes input with optional transformations',
runtime: {
size: 'm', // xs, s, m, l, xl
},
},
async (payload, context) => {
const { input, options = {} } = payload;
const { jobId, taskId, taskName } = context;
let output = input;
if (options.uppercase) {
output = input.toUpperCase();
}
return {
output,
metadata: {
processedAt: new Date().toISOString(),
},
};
}
);Middleware System
The SDK includes a powerful middleware system for handling cross-cutting concerns:
Built-in Middleware
import {
TaskRunner,
LoggingMiddleware,
TimeoutMiddleware,
RetryMiddleware,
MetricsMiddleware,
} from '@xrundev/sdk';
const runner = new TaskRunner({
middleware: [
new LoggingMiddleware(),
new TimeoutMiddleware(30000), // 30 seconds
new RetryMiddleware(3, 1000, 2), // 3 retries, 1s delay, 2x backoff
new MetricsMiddleware((metrics) => console.log(metrics)),
],
});Custom Middleware
import { TaskMiddleware, TaskExecutionContext } from '@xrundev/sdk';
export class AuthMiddleware implements TaskMiddleware {
name = 'auth';
async before(context: TaskExecutionContext): Promise<void> {
// Validate authentication
const auth = context.metadata?.auth;
if (!auth) {
throw new Error('Authentication required');
}
}
async after(context: TaskExecutionContext, result: unknown): Promise<void> {
// Log successful execution
console.log(
`Task ${context.taskId} completed by user ${context.metadata?.userId}`
);
}
async onError(context: TaskExecutionContext, error: Error): Promise<void> {
// Log authentication failures
if (error.message.includes('Authentication')) {
console.error(`Auth failure for task ${context.taskId}`);
}
}
}Configuration
Environment Variables
| Variable | Required | Default | Description |
| ---------------- | -------- | ------- | ----------------------------- |
| TASK_ID | ✅ | - | The ID of the task to execute |
| TASK_PAYLOAD | ✅ | - | JSON payload for the task |
| JOB_ID | ✅ | - | Job ID for tracking |
| TASK_TIMEOUT | ❌ | 300000 | Timeout in milliseconds |
| TASK_RETRIES | ❌ | 0 | Number of retries on failure |
| ENABLE_METRICS | ❌ | false | Enable metrics collection |
Runtime Configuration
import { TaskRunner, createDefaultMiddleware } from '@xrundev/sdk';
const runner = new TaskRunner({
timeout: 60000,
logger: new CustomLogger(),
middleware: createDefaultMiddleware({
timeout: 60000,
retries: 3,
enableMetrics: true,
}),
});Testing
Unit Testing Tasks
// __tests__/my-task.test.ts
import { describe, it, expect } from 'vitest';
import { myTask } from '../tasks/my-task.js';
describe('myTask', () => {
it('should process input correctly', async () => {
const payload = { input: 'hello' };
const result = await myTask.run(payload);
expect(result.output).toBe('hello');
expect(result.metadata.processedAt).toBeDefined();
});
it('should handle uppercase option', async () => {
const payload = { input: 'hello', options: { uppercase: true } };
const result = await myTask.run(payload);
expect(result.output).toBe('HELLO');
});
});Integration Testing
// __tests__/integration.test.ts
import { describe, it, expect } from 'vitest';
import { TaskRunner, TaskExecutionContext } from '@xrundev/sdk';
describe('Task Integration', () => {
it('should execute task with full context', async () => {
const context: TaskExecutionContext = {
jobId: 'job-123',
taskId: 'my-task',
payload: { input: 'test' },
config: {},
startTime: Date.now(),
};
const runner = new TaskRunner();
const result = await runner.execute(context);
expect(result).toBeDefined();
});
});Mocking Dependencies
import { vi } from 'vitest';
import { TaskRunner } from '@xrundev/sdk';
const mockMiddleware = {
name: 'mock',
before: vi.fn(),
after: vi.fn(),
onError: vi.fn(),
};
const runner = new TaskRunner({
middleware: [mockMiddleware],
});CLI Commands
Build
npx xrun build [options]
Options:
-s, --skip-cleanup Skip cleanup of temporary filesBuilds your tasks into a Docker-ready bundle with:
- Compiled and bundled task definitions
- Runtime entry point
- Docker configuration files
Package
npx xrun packageCreates a Docker image with your tasks:
- Builds a lightweight Alpine-based image
- Includes all dependencies
- Configures proper entry point and environment
Advanced Usage
Custom Runtime
For advanced scenarios, you can create a custom runtime:
// custom-runtime.ts
import {
main,
TaskRunner,
createContextFromEnv,
DefaultLogger,
} from '@xrundev/sdk';
import { CustomMiddleware } from './middleware.js';
// Custom configuration
const customMain = async () => {
const context = createContextFromEnv();
const runner = new TaskRunner({
logger: new DefaultLogger(),
middleware: [
new CustomMiddleware(),
// ... other middleware
],
});
return runner.execute(context);
};
// Use custom main instead of default
customMain().catch((error) => {
console.error('Custom runtime error:', error);
process.exit(1);
});Error Handling
import { TaskRunnerError } from '@xrundev/sdk';
export const myTask = task(
{ name: 'error-handling-task', description: 'Demonstrates error handling' },
async (payload: { shouldFail?: boolean }) => {
if (payload.shouldFail) {
throw new TaskRunnerError(
'Task intentionally failed',
'INTENTIONAL_FAILURE'
);
}
return { success: true };
}
);Migration
If you're migrating from the old template-based system, see our Migration Guide for step-by-step instructions.
Examples
Check out the examples directory for more comprehensive examples including:
- Data processing tasks
- API integration tasks
- Batch processing workflows
- Custom middleware implementations
Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
MIT License - see LICENSE for details.
