npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

brunogen

v0.4.2

Published

Scan Laravel, Express.js, and Go API projects, normalize them into OpenAPI, and emit ready-to-try Bruno collections.

Readme

brunogen

npm version node >=20 CI

Brunogen scans a Laravel, Express.js, or Go API codebase, normalizes what it finds into OpenAPI, and emits a Bruno collection you can try immediately.

Brunogen is already useful for real codebases that stay close to common framework conventions. Laravel is the strongest path today, with materially richer request and response inference. Express.js and Go support are available and improving, but still more heuristic.

Try These Demos

  • Start with Laravel if you want the most complete inference path today.
  • Open Express if your codebase uses mounted routers and straightforward local helpers.
  • Open Go if you want to see the current Gin-based experimental path.

What You Get

  • openapi.yaml generated directly from your routes, handlers, controllers, and request/response patterns
  • A ready-to-open Bruno collection under .brunogen/bruno/
  • Fast feedback on what Brunogen understood well and what it skipped with warnings

Quick Start

Best first run: use the Laravel demo path below if you want to see the most complete inference flow in under a minute.

npm i -g brunogen
brunogen init
brunogen generate

Default output:

  • .brunogen/openapi.yaml
  • .brunogen/bruno/

How It Works

source code
  -> framework adapter
  -> normalized endpoint model
  -> openapi.yaml
  -> Bruno collection

OpenAPI is the internal source of truth after scanning. Bruno is the output target.

Works Today

  • Global CLI with init, generate, watch, validate, and doctor
  • Laravel route scanning from routes/*.php, including groups, prefixes, middleware-based auth hints, and apiResource expansion
  • Laravel request inference from FormRequest rules, inline validation, and common manual accessors such as query, header, typed accessors, has, filled, safe()->only(...), and enum(...)
  • Laravel response inference for direct arrays, response()->json(...), noContent(), same-controller helpers, JsonResource, ->additional(...), and common abort/error/not-found paths
  • Bruno collection generation with environments, baseline bearer/basic/api-key auth support, and native response example {} blocks
  • OpenAPI generation and validation before export
  • Express.js scanning in experimental mode for mounted routers, straightforward request access patterns, and local response helpers
  • Go Gin, Fiber, and Echo scanning in experimental mode with stronger direct JSON response inference

For a more detailed per-feature status, see the Support Matrix below.

Laravel-First Quickstart

The current canonical happy path is the minimal Laravel fixture in tests/fixtures/laravel. Curated generated snapshots for that path live in docs/demo/laravel-happy-path.

cd tests/fixtures/laravel
brunogen init
brunogen generate

To refresh the checked-in Laravel demo snapshots after an intentional output change:

npm run demo:laravel

Expected result:

Generated 6 endpoints.
OpenAPI: .../tests/fixtures/laravel/.brunogen/openapi.yaml
Bruno: .../tests/fixtures/laravel/.brunogen/bruno

Express Quickstart

The Express fixture used by the test suite lives in tests/fixtures/express. It covers mounted routers, route chains, middleware-based auth hints, request access patterns, and local response helper inference. Curated generated snapshots for that path live in docs/demo/express-happy-path.

cd tests/fixtures/express
brunogen init
brunogen generate

To refresh the checked-in Express demo snapshots after an intentional output change:

npm run demo:express

Expected result:

Generated 13 endpoints.
OpenAPI: .../tests/fixtures/express/.brunogen/openapi.yaml
Bruno: .../tests/fixtures/express/.brunogen/bruno

Go Quickstart

The Go fixtures used by the test suite live in tests/fixtures/gin, tests/fixtures/fiber, and tests/fixtures/echo. The Gin fixture is the simplest place to try the current Go adapter behavior end to end. Curated generated snapshots for that path live in docs/demo/go-happy-path.

cd tests/fixtures/gin
brunogen init
brunogen generate

To refresh the checked-in Go demo snapshots after an intentional output change:

npm run demo:go

Expected result:

Generated 8 endpoints.
OpenAPI: .../tests/fixtures/gin/.brunogen/openapi.yaml
Bruno: .../tests/fixtures/gin/.brunogen/bruno

Go support is still experimental, so prefer Laravel or Express if you want the most complete inference today.

If you are testing from this repository checkout instead of an installed package, run npm install, npm run build, and npm link once from the repository root first.

Supported Patterns

These are the current code shapes Brunogen reads most reliably.

Laravel

Request inference is strongest when controllers or FormRequest classes use patterns like:

$request->validate([...]);
$request->string('device_name');
$request->boolean('remember_me');
$request->array('scopes');
$request->query('page');
$request->header('TTOKEN');
$request->has('profile_photo');
$request->filled('nickname');
$request->safe()->only(['locale']);
$request->enum('role', UserRole::class);

Response inference is strongest when controllers use patterns like:

return response()->json([...], 201);
return [...];
return ProjectResource::make($project)->additional([...]);
return $this->createdResponse($payload);
abort_if(!$enabled, 403, 'Forbidden');
Model::query()->findOrFail($id);
throw ValidationException::withMessages([...]);

Express

Request inference is strongest when handlers use patterns like:

const { name, email, age = 18 } = req.body;
const page = req.query.page;
const { page: currentPage = 1 } = req.query;
const traceId = req.get("X-Trace-Id");
const auth = req.headers.authorization;
const trace = req.headers["x-trace-id"];

Response inference is strongest when handlers use patterns like:

return res.status(201).json({ message: "created", data: payload });
return res.json({ data: { id, name } });
return res.send("ok");
return res.sendStatus(204);
return sendCreated(res, payload);
return responseHelpers.sendCreated(res, payload);

Go

Request inference is strongest when handlers use patterns like:

var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil { return }
if err := ctx.Bind(&req); err != nil { return err }
page := c.Query("page")
id := c.Param("id")
token := c.Get("TTOKEN")

Response inference is strongest when handlers use patterns like:

c.JSON(http.StatusCreated, gin.H{"message": "created", "data": req})
return ctx.JSON(http.StatusOK, map[string]any{"data": payload})
return fiberHelper(c, payload)

Example Input Project Shape

This is the minimal Laravel shape Brunogen currently handles well:

app/
  Http/
    Controllers/
      SessionController.php
      UserController.php
    Requests/
      StoreUserRequest.php
routes/
  api.php
artisan
composer.json

Example Output Tree

Generated from the Laravel fixture:

.brunogen/
  openapi.yaml
  bruno/
    bruno.json
    environments/
      local.bru
    session/
      sessioncontrollercheck.bru
      sessioncontrollerstore.bru
    user/
      usercontrollerindex.bru
      usercontrollerindexgetapiprojects.bru
      usercontrollershow.bru
      usercontrollerstore.bru

The same snapshot is also checked into:

Example Generated OpenAPI

Real snippet from the generated Laravel fixture output:

openapi: 3.1.0
paths:
  /api/users:
    post:
      operationId: usercontrollerStore
      summary: UserController::store
      tags:
        - User
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  maxLength: 255
                  type: string
                email:
                  format: email
                  type: string
                age:
                  nullable: true
                  minimum: 18
                  type: integer
              required:
                - name
                - email
      responses:
        "201":
          description: Inferred JSON response
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      email:
                        type: string
              example:
                message: User created
                data:
                  id: 1
                  name: Jane Doe
                  email: [email protected]
      security:
        - bearerAuth: []

Example Generated Bruno Request

Real snippet from the generated Laravel fixture output:

meta {
  name: usercontrollerStore
  type: http
  seq: 4
  tags: [
    User
  ]
}

post {
  url: {{baseUrl}}/api/users
  body: json
  auth: bearer
}

headers {
  accept: application/json
  content-type: application/json
}

auth:bearer {
  token: {{authToken}}
}

body:json {
  {
    "name": "",
    "email": "[email protected]",
    "age": 1
  }
}

example {
  name: "201 Response"
  description: "Inferred JSON response"

  request: {
    url: {{baseUrl}}/api/users
    method: post
    mode: json
    headers: {
      accept: application/json
      content-type: application/json
    }

    body:json: {
      {
        "name": "",
        "email": "[email protected]",
        "age": 1
      }
    }
  }

  response: {
    headers: {
      Content-Type: application/json
    }

    status: {
      code: 201
      text: Created
    }

    body: {
      type: json
      content: '''
        {
          "message": "User created",
          "data": {
            "id": 1,
            "name": "Jane Doe",
            "email": "[email protected]"
          }
        }
      '''
    }
  }
}

Example Config

{
  "version": 1,
  "framework": "auto",
  "inputRoot": ".",
  "output": {
    "openapiFile": ".brunogen/openapi.yaml",
    "brunoDir": ".brunogen/bruno"
  },
  "project": {
    "version": "1.0.0",
    "serverUrl": "{{baseUrl}}"
  },
  "environments": [
    {
      "name": "local",
      "variables": {
        "baseUrl": "http://localhost:3000",
        "authToken": ""
      }
    },
    {
      "name": "prod",
      "variables": {
        "baseUrl": "https://api.example.com",
        "authToken": ""
      }
    }
  ],
  "auth": {
    "default": "auto",
    "bearerTokenVar": "authToken",
    "basicUsernameVar": "username",
    "basicPasswordVar": "password",
    "apiKeyVar": "apiKey",
    "apiKeyName": "X-API-Key",
    "apiKeyLocation": "header"
  }
}

Support Matrix

| Area | Status | Notes | | ----------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Laravel route scanning | Supported | Reads routes/*.php declarations | | Laravel route groups and prefixes | Supported | Handles common prefix, middleware, and grouped routes | | Laravel apiResource expansion | Supported | Common REST actions are expanded | | Laravel FormRequest inference | Partial | rules() arrays are supported; complex dynamic rules are not | | Laravel manual request inference | Strong partial | Common query, header, input, typed accessors, has, filled, only([...]), safe()->only([...]), and enum(...) patterns are inferred | | Laravel inline validation inference | Partial | Simple $request->validate() and Validator::make() arrays | | Auth inference | Partial | Middleware and OpenAPI security are inferred heuristically | | OpenAPI generation | Supported | OpenAPI is the normalized intermediate output | | Bruno export | Supported | Collection, requests, environments, baseline auth blocks, and response example {} blocks | | Express route scanning | Experimental | Handles express() / Router(), use() mounts, and route() chains | | Express handler inference | Experimental | Heuristic request and response inference from straightforward handlers and local response helpers | | Go Fiber scanning | Experimental | Route and request inference are heuristic | | Go Gin scanning | Experimental | Route and request inference are heuristic | | Go Echo scanning | Experimental | Route and request inference are heuristic | | Go request schema inference | Experimental | Works for straightforward bind/body-parser patterns | | Laravel response inference | Strong partial | Covers direct arrays, response()->json(...), noContent(), same-controller wrapper helpers, JsonResource, ->additional(...), and common abort/error/not-found paths | | Express response inference | Partial | Straightforward res.json(), res.send(), res.status(...).json(), sendStatus(), and local helper wrappers | | Go response inference | Partial | Covers common direct JSON responses plus existing helper-based patterns, but remains heuristic | | Watch mode | Supported | Regenerates on .php, .go, .js, .cjs, .mjs, and .ts changes |

Known Limitations

  • Brunogen is optimized for conventional code, not heavily dynamic or meta-programmed applications.
  • Laravel and Express parsing are regex-driven rather than full AST analysis, so unusual declarations can still be missed.
  • Complex route factories, indirect exports, custom router abstractions, and highly dynamic middleware composition may be skipped with warnings.
  • Complex Laravel validation rules, custom rule objects, and conditional validation are only partially inferred.
  • Laravel response inference is strongest for controller-local patterns and common resource usage; cross-class service wrappers and highly dynamic composition are still best-effort.
  • Express request and response inference is strongest for direct req.body / req.query / req.headers access and local res.*() helper wrappers.
  • Go support is still experimental, especially around indirect helpers, nested wrappers, and custom response builders.
  • Generated Bruno auth gives you a usable starting point, not a complete auth flow engine.

Roadmap

  • Keep Laravel as the most reliable path and harden it around the patterns teams use most in real controllers
  • Push Express closer to Laravel in day-to-day usefulness without losing the current lightweight scanner model
  • Improve response inference across Laravel and Go while keeping OpenAPI as the stable internal contract
  • Broaden support for reusable wrappers and helper-driven response patterns where the code is still statically readable
  • Reduce false positives and make supported code patterns more explicit in docs and fixtures
  • Add more canonical fixtures and checked-in demos before broadening framework claims further

Project Docs