tendryl
v1.0.1
Published
A workflow orchestrator with chainable async interfaces, ledger integration for operation tracking, and race condition detection
Maintainers
Readme
Tendryl
A workflow orchestrator with chainable async interfaces, ledger integration for operation tracking, and race condition detection.
Features
- Chain: Class-based and function-based chainable async workflows
- Ledger: Automatic operation tracking with collision detection
- Logger: Structured logging with Pino integration
- Chain with Ledger: Integrated chain and ledger functionality
Installation
npm install tendrylUsage
Core Chain
The core chain provides a simple way to create chainable async workflows:
import { ChainStep, chainstep } from 'tendryl';
// Class-based approach
class FetchData extends ChainStep<{ url: string; data?: any }> {
async execute(state) {
const response = await fetch(state.url);
state.data = await response.json();
return state;
}
}
class ProcessData extends ChainStep<{ data?: any; processed?: any }> {
async execute(state) {
state.processed = state.data.map(item => ({ ...item, processed: true }));
return state;
}
}
// Chain them together
FetchData.then(ProcessData);
// Execute the chain
const result = await FetchData.invoke({ url: 'https://api.example.com/data' });Function-based approach
import { chainstep } from 'tendryl';
const fetchData = chainstep(async (state) => {
const response = await fetch(state.url);
state.data = await response.json();
return state;
}, 'FetchData');
const processData = chainstep(async (state) => {
state.processed = state.data.map(item => ({ ...item, processed: true }));
return state;
}, 'ProcessData');
// Chain them together
fetchData.then(processData);
// Execute
const result = await fetchData.invoke({ url: 'https://api.example.com/data' });Ledger System
The ledger system tracks all operations on an object and detects race conditions:
import { createLedger, getLedgerStats, printLedger } from 'tendryl/ledger';
// Create a ledger-wrapped object
const state = createLedger({ count: 0, name: 'test' });
// All operations are tracked
state.count = 1;
state.name = 'updated';
const currentCount = state.count;
// Get statistics
const stats = getLedgerStats(state);
console.log(stats); // { totalOperations: 3, totalReads: 1, totalWrites: 2, collisions: 0, duration: 123 }
// Print detailed ledger
printLedger(state);Logger
Structured logging with Pino:
import { logger, createChildLogger, setLogLevel } from 'tendryl/logger';
// Use the main logger
logger.info('Application started');
// Create a child logger for specific context
const userLogger = createChildLogger('user-service');
userLogger.info('User created', { userId: 123 });
// Set log level
setLogLevel('debug');Chain with Ledger
Combines chain functionality with automatic ledger tracking:
import { ChainStep } from 'tendryl/chain-with-ledger';
class UpdateUser extends ChainStep<{ userId: number; user?: any }> {
async execute(state) {
// All operations on state are automatically tracked
state.user = { id: state.userId, name: 'John Doe' };
state.user.lastUpdated = new Date();
return state;
}
}
class ValidateUser extends ChainStep<{ user?: any; valid?: boolean }> {
async execute(state) {
state.valid = state.user && state.user.name;
return state;
}
}
// Chain with ledger enabled
UpdateUser.then(ValidateUser);
// Execute with ledger tracking
const result = await UpdateUser.invoke(
{ userId: 123 },
{ enableLedger: true }
);
// Access ledger information
console.log(result.ledger); // Ledger log
console.log(result.reconciliation); // Conflict detection resultsAPI Reference
Chain
ChainStep<S>- Abstract base class for chain stepschainstep(fn, name?)- Create chainable steps from functions.then(next)- Chain steps sequentially.when(condition, target, label?)- Conditional branching.retries(n)- Set retry attempts.catch(errorStep)- Error handling.invoke(initialState)- Execute the chain.graph(options?)- Generate Mermaid flowchart
Ledger
createLedger(initial, options?)- Create ledger-wrapped objectgetLedger(proxy)- Get ledger from proxygetLedgerStats(proxy)- Get operation statisticsgetCollisions(proxy)- Get detected collisionsreconcileLedger(proxy)- Reconcile pending operationsprintLedger(proxy)- Print detailed ledger report
Logger
logger- Main Pino logger instancecreateChildLogger(name)- Create child loggersetLogLevel(level)- Set log levelgetLogLevel()- Get current log level
Examples
Workflow with Error Handling
import { ChainStep } from 'tendryl/chain-with-ledger';
class FetchData extends ChainStep<{ url: string; data?: any }> {
async execute(state) {
const response = await fetch(state.url);
if (!response.ok) throw new Error('Fetch failed');
state.data = await response.json();
return state;
}
}
class HandleError extends ChainStep<{ error?: string }> {
async execute(state) {
state.error = 'Data fetch failed, using fallback';
state.data = [{ fallback: true }];
return state;
}
}
FetchData.retries(3).catch(HandleError);Parallel Execution
import { ChainStep } from 'tendryl/chain-with-ledger';
class ProcessA extends ChainStep<{ resultA?: string }> {
async execute(state) {
state.resultA = 'Processed A';
return state;
}
}
class ProcessB extends ChainStep<{ resultB?: string }> {
async execute(state) {
state.resultB = 'Processed B';
return state;
}
}
class Start extends ChainStep<{}> {
async execute(state) {
return state;
}
}
// Execute ProcessA and ProcessB in parallel
Start.then([ProcessA, ProcessB]);Development
GitHub Actions
This repository includes GitHub Actions workflows for automated testing and publishing:
Test Workflow (
test.yml): Runs on every push to main and pull request- Type checking
- Unit tests
- Build verification
Publish Workflow (
publish.yml): Runs when a new release is published- Automatically publishes to npm
- Requires
NPM_TOKENsecret to be configured
Setting up NPM Token
To enable automatic publishing, you need to:
- Create an npm access token at https://www.npmjs.com/settings/tokens
- Add the token as a repository secret named
NPM_TOKENin your GitHub repository settings - Create a new release on GitHub to trigger the publish workflow
Local Development
# Install dependencies
pnpm install
# Run tests
pnpm test
# Build package
pnpm build
# Type check
pnpm type-checkLicense
MIT
