From harness-claude
Implements Chain of Responsibility pattern in TypeScript for linked handler chains with short-circuiting and Express-style async middleware pipelines. Use for HTTP middleware, validation, or event handling.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Pass requests along a handler chain with short-circuit and async chain support.
Guides JavaScript implementation of Chain of Responsibility pattern to pass requests along handler chains for auth pipelines, validation, or middleware. Decouples senders from receivers.
Generates Chain of Responsibility pattern for PHP 8.4, creating handler chains for middleware-style request processing like validation and approvals. Includes unit tests.
Implements practical async error handling in TypeScript using fp-ts TaskEither for clean composable pipelines instead of nested try/catch, with fetch API examples.
Share bugs, ideas, or general feedback.
Pass requests along a handler chain with short-circuit and async chain support.
Linked handler chain:
interface Handler<T> {
setNext(handler: Handler<T>): Handler<T>;
handle(request: T): T | null;
}
abstract class AbstractHandler<T> implements Handler<T> {
private nextHandler: Handler<T> | null = null;
setNext(handler: Handler<T>): Handler<T> {
this.nextHandler = handler;
return handler; // enables chaining: a.setNext(b).setNext(c)
}
handle(request: T): T | null {
if (this.nextHandler) return this.nextHandler.handle(request);
return null;
}
}
// Concrete handlers
interface SupportTicket {
level: 'basic' | 'advanced' | 'expert';
description: string;
}
class BasicSupportHandler extends AbstractHandler<SupportTicket> {
handle(ticket: SupportTicket): SupportTicket | null {
if (ticket.level === 'basic') {
console.log(`BasicSupport handling: ${ticket.description}`);
return ticket;
}
return super.handle(ticket); // pass to next
}
}
class AdvancedSupportHandler extends AbstractHandler<SupportTicket> {
handle(ticket: SupportTicket): SupportTicket | null {
if (ticket.level === 'advanced') {
console.log(`AdvancedSupport handling: ${ticket.description}`);
return ticket;
}
return super.handle(ticket);
}
}
class ExpertSupportHandler extends AbstractHandler<SupportTicket> {
handle(ticket: SupportTicket): SupportTicket | null {
console.log(`ExpertSupport handling: ${ticket.description}`);
return ticket; // always handles — final in chain
}
}
// Build the chain
const basic = new BasicSupportHandler();
const advanced = new AdvancedSupportHandler();
const expert = new ExpertSupportHandler();
basic.setNext(advanced).setNext(expert);
// Route tickets
basic.handle({ level: 'advanced', description: 'DB crash' });
// → AdvancedSupport handling: DB crash
Async middleware chain (Express-style):
type Middleware<T> = (ctx: T, next: () => Promise<void>) => Promise<void>;
class Pipeline<T> {
private middlewares: Middleware<T>[] = [];
use(middleware: Middleware<T>): this {
this.middlewares.push(middleware);
return this;
}
async execute(ctx: T): Promise<void> {
const run = async (index: number): Promise<void> => {
if (index >= this.middlewares.length) return;
await this.middlewares[index](ctx, () => run(index + 1));
};
await run(0);
}
}
// Usage
interface RequestContext {
userId?: string;
body: unknown;
response?: unknown;
errors: string[];
}
const pipeline = new Pipeline<RequestContext>()
.use(async (ctx, next) => {
// Auth middleware
ctx.userId = 'user-123'; // decode JWT in reality
if (!ctx.userId) {
ctx.errors.push('Unauthorized');
return;
} // short-circuit
await next();
})
.use(async (ctx, next) => {
// Validation middleware
if (!ctx.body) {
ctx.errors.push('Body required');
return;
} // short-circuit
await next();
})
.use(async (ctx, next) => {
// Handler middleware
ctx.response = { message: 'OK', userId: ctx.userId };
await next();
})
.use(async (ctx, _next) => {
// Logging middleware — always runs
console.log(`Request by ${ctx.userId}, errors: ${ctx.errors.length}`);
});
const ctx: RequestContext = { body: { name: 'test' }, errors: [] };
await pipeline.execute(ctx);
Short-circuiting: Handlers can stop the chain by not calling next() or returning without calling super.handle(). This is the key advantage over Decorator — the chain can terminate early based on conditions.
Difference from Decorator: Decorator always delegates to the wrapped object. Chain of Responsibility can stop delegation. If all handlers must run, use Decorator. If any handler might absorb the request, use Chain.
Anti-patterns:
Request logging for debuggability:
abstract class LoggingHandler<T> extends AbstractHandler<T> {
private readonly name: string;
constructor(name: string) {
super();
this.name = name;
}
handle(request: T): T | null {
console.log(`[${this.name}] Received request`);
const result = this.handleRequest(request);
if (result !== null) {
console.log(`[${this.name}] Handled request`);
return result;
}
console.log(`[${this.name}] Passing to next`);
return super.handle(request);
}
protected abstract handleRequest(request: T): T | null;
}
refactoring.guru/design-patterns/chain-of-responsibility