job-planner
v2.0.0
Published
Simple and powerful execution flow control lib. Manage your processes' execution order with zero pain.
Maintainers
Readme
Job Planner
Simple and powerful execution flow control library. Manage your processes' execution order with zero pain.
Table of Contents
- Installation
- Quick Start
- API Reference
- Configuration
- Error Handling
- TypeScript Support
- Common Patterns
- Migration from v1
- License
Installation
npm install job-planner
# or
yarn add job-plannerQuick Start
import { createPlan } from 'job-planner';
const planner = createPlan(({ sync }) =>
sync(connectToDB, runMigrations, seedData)
);
await planner();API Reference
createPlan(planCreator)
Creates a plan executor from a plan definition.
import { createPlan } from 'job-planner';
const planner = createPlan(({ sync, parallel }) =>
sync(
initializeApp,
parallel(loadUserData, loadSettings),
startServer
)
);
await planner({ stopOnError: true });Parameters:
planCreator- Function that receives{ sync, parallel }helpers and returns a Job
Returns: A function that executes the plan with optional configuration
sync(...processes)
Creates a synchronous job where processes execute sequentially.
import { sync, createPlan } from 'job-planner';
// Via createPlan callback
createPlan(({ sync }) => sync(step1, step2, step3));
// Direct import for composition
const setupDB = sync(connectToDB, runMigrations);Throws:
EmptyJobError- If no processes are providedInvalidProcessError- If any process is not a function or Job
parallel(...processes)
Creates a parallel job where processes execute concurrently.
import { parallel, createPlan } from 'job-planner';
createPlan(({ parallel }) =>
parallel(fetchUserData, fetchProducts, fetchSettings)
);Throws:
EmptyJobError- If no processes are providedInvalidProcessError- If any process is not a function or Job
Type Guards
import { isJob, isProcess } from 'job-planner';
isJob(someValue); // true if value is a Job object
isProcess(someValue); // true if value is a callable functionConfiguration
The planner accepts a configuration object:
interface Config {
executor?(process: Process): Promise<unknown> | unknown;
stopOnError?: boolean;
errorHandler?(error: unknown): void;
}Default Configuration
const defaultConfig = {
async executor(process) {
return process();
},
stopOnError: false,
errorHandler(error) {
throw error;
},
};executor
Custom function that wraps process execution. Useful for logging, metrics, or dependency injection.
await planner({
async executor(process) {
console.log('Starting process...');
const result = await process();
console.log('Process complete');
return result;
},
});stopOnError
When true, stops plan execution after the first error. The errorHandler is still called before stopping.
await planner({
stopOnError: true,
errorHandler(error) {
console.error('Process failed:', error);
},
});errorHandler
Handler for errors thrown by processes. Called for each error before deciding whether to continue.
await planner({
errorHandler(error) {
logError(error);
// Don't rethrow - continue execution
},
});Error Handling
Built-in Error Classes
import {
EmptyJobError,
InvalidProcessError,
NotJobError,
UnknownJobTypeError,
} from 'job-planner';| Error | When Thrown |
|-------|-------------|
| EmptyJobError | sync() or parallel() called with no arguments |
| InvalidProcessError | Non-function/non-Job passed to sync() or parallel() |
| NotJobError | createPlan callback doesn't return a valid Job |
| UnknownJobTypeError | Job has an unrecognized type |
Error Handling in Parallel Jobs
In parallel execution, errors are handled per-process:
const planner = createPlan(({ parallel }) =>
parallel(
() => { throw new Error('Error 1'); },
() => { throw new Error('Error 2'); },
() => console.log('Success')
)
);
const errors: Error[] = [];
await planner({
stopOnError: false,
errorHandler(e) {
errors.push(e as Error);
},
});
// errors contains both Error 1 and Error 2
// 'Success' was still loggedTypeScript Support
Full TypeScript support with exported types:
import {
createPlan,
sync,
parallel,
// Types
Job,
SyncJob,
ParallelJob,
Process,
Config,
PlanCreator,
// Type guards
isJob,
isProcess,
// Errors
EmptyJobError,
InvalidProcessError,
NotJobError,
UnknownJobTypeError,
// Enum
JOB_TYPES,
} from 'job-planner';Common Patterns
Database Setup
const planner = createPlan(({ sync }) =>
sync(connectToDB, runMigrations, seedData)
);
await planner({ stopOnError: true });Parallel Data Fetching
const planner = createPlan(({ parallel }) =>
parallel(fetchUsers, fetchProducts, fetchOrders)
);
await planner();Mixed Execution
const planner = createPlan(({ sync, parallel }) =>
sync(
initialize,
parallel(
loadConfig,
loadTranslations,
sync(connectDB, warmCache)
),
startServer
)
);Job Composition
import { sync, parallel, createPlan } from 'job-planner';
const dbSetup = sync(connectToDB, runMigrations);
const cacheSetup = sync(connectRedis, warmCache);
const startup = parallel(dbSetup, cacheSetup);
const planner = createPlan(() => sync(startup, startServer));Migration from v1
Breaking Changes in v2
Empty jobs now throw
EmptyJobError// v1: Silently did nothing sync(); // v2: Throws EmptyJobError sync(); // Error: Cannot create sync job with no processesInvalid processes now throw
InvalidProcessError// v1: Failed at runtime during execution sync('not a function'); // v2: Throws immediately sync('not a function'); // Error: Invalid process at index 0errorHandlernow receivesunknowninstead ofError// v1 errorHandler(error: Error) { } // v2 errorHandler(error: unknown) { }
License
MIT
