UNPKG

@quiltjs/quilt

Version:

Lightweight, type-safe handler and router abstraction for Node HTTP servers.

83 lines 2.91 kB
function now() { if (typeof performance !== 'undefined' && typeof performance.now === 'function') { return performance.now(); } return Date.now(); } /** * Executes a handler graph for a given context. * * Each handler runs at most once per execution, and its output is cached * and provided to dependants via the `deps` parameter. The final return * value is ignored; callers should rely on handler side-effects (for * example, writing to an HTTP response) or on dependency outputs. */ export async function executeHandler(handler, ctx, hooks) { // Cache to store handler outputs const cache = new Map(); /** * Flattens the dependency tree into an ordered array of handlers. * Ensures dependencies are executed before their dependents. * Also checks for cyclic dependencies. */ function flattenDependencies(currentHandler) { const flatOrder = []; const visited = new Set(); const visiting = new Set(); function visit(h) { if (visiting.has(h)) { throw new Error('Cyclic dependency detected in handler graph'); } if (visited.has(h)) return; visiting.add(h); for (const dep of Object.values(h.dependencies)) { visit(dep); } visiting.delete(h); visited.add(h); flatOrder.push(h); } visit(currentHandler); return flatOrder; } // Flatten the dependency tree (with cycle detection) and execute handlers in order const flatOrder = flattenDependencies(handler); for (const currentHandler of flatOrder) { const depsOutputs = {}; for (const [depKey, depHandler] of Object.entries(currentHandler.dependencies)) { depsOutputs[depKey] = cache.get(depHandler); } const start = hooks ? now() : 0; if (hooks?.onHandlerStart) { await hooks.onHandlerStart({ handler: currentHandler, ctx }); } try { const outputs = await currentHandler.execute(ctx, depsOutputs); cache.set(currentHandler, outputs); if (hooks?.onHandlerSuccess) { const end = now(); await hooks.onHandlerSuccess({ handler: currentHandler, ctx, durationMs: end - start, output: outputs, }); } } catch (error) { if (hooks?.onHandlerError) { const end = now(); await hooks.onHandlerError({ handler: currentHandler, ctx, durationMs: end - start, error, }); } throw error; } } } //# sourceMappingURL=executeHandler.js.map