decopin-cli

↑ ↓ to navigate · Enter to open · Esc to close

Inheritedmiddleware.tsx

middleware.tsx

Wrap the execution of every command below a directory. Nothing runs until you call next().

middleware.tsx wraps the execution. It is inherited like layout.tsx, and next is a function, so nothing inside runs until you call it. That is what makes "after the command" possible: timing, cleanup, or deciding not to run at all.

// app/user/middleware.tsx
import { Line, Stderr, Text, type MiddlewareProps } from 'decopin-cli';

export default async function Middleware({ next, options }: MiddlewareProps) {
  const started = performance.now();
  const output = await next();
  if (options.verbose !== true) return output;
  return (
    <>
      {output}
      <Stderr>
        <Line>
          <Text dim>took {Math.round(performance.now() - started)}ms</Text>
        </Line>
      </Stderr>
    </>
  );
}

Middleware receives the same validated args, options, env and dryRun the command does, typed loosely (Record<string, unknown>) because one file serves many commands.

Order

Outer directories run first. app/middleware.tsx calls next(), which runs app/user/middleware.tsx, which calls next(), which runs the command. Returning without calling next() skips everything inside.

Why next() and not children

A children prop would mean the command has already run by the time the middleware sees it. The decision is recorded as ADR 13 in docs/decisions.md.