Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 23 additions & 33 deletions .agents/skills/fusion-architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,32 @@

Only when the user needs to modify the CipherLogger package itself (not a project
that uses it) — for example, bug fixes, adding a new optional field, or writing an
adapter for another framework (like Fastify or Hono).
adapter for another framework.

## Folder structure

```
cipher-logger/
├── src/
│ ├── core/ # core — field configuration and log construction
│ │ ├── logger.ts
│ │ ├── types.ts
│ │ ├── build-request-log.ts
│ │ └── create-cipher-logger.ts
│ ├── express/ # Express adapter
│ │ └── middleware.ts
│ ├── next/ # Next.js adapter
│ │ └── middleware.ts
│ └── index.ts # public entry point
│ ├── core/ # field config, Logger, createCipherLogger
│ ├── adapters/
│ │ ├── express/
│ │ ├── next/
│ │ ├── nuxt/
│ │ ├── fastify/
│ │ ├── nest/
│ │ └── hono/
│ ├── index.ts # core public entry
│ ├── express.ts # cipher-logger/express
│ ├── next.ts # cipher-logger/next
│ └── … # other subpath entries
```

## Data flow

```
Core
fields config → buildRequestLog
┌─────────┴─────────┐
▼ ▼
Express Next.js
middleware middleware
```

Both adapters (`express/middleware.ts` and `next/middleware.ts`) rely on the
same `buildRequestLog` core — only the extraction of request/response fields from
the framework differs. The difference in `status`/`duration` behavior between the
two adapters stems from how each adapter calls the core, not from `buildRequestLog`
itself.
Core (`buildRequestLog`) is shared. Each adapter only extracts framework-specific
request/response fields. Subpath entries keep peer frameworks out of the main
`cipher-logger` bundle until that adapter is imported or lazy-loaded.

## Local development

Expand All @@ -48,17 +38,17 @@ pnpm install
pnpm run build
```

## Adding a new adapter (e.g. Fastify)
## Adding a new adapter

Create a new file such as `src/fastify/middleware.ts` that calls the same
`buildRequestLog` from `core/build-request-log.ts` and only differs in how fields
are extracted from the framework — do not reimplement the log-construction logic
inside the new adapter.
1. Add `src/adapters/<name>/middleware.ts` that calls `cipher.logRequest(...)`.
2. Add `src/<name>.ts` re-exporting the factory.
3. Register the entry in `tsup.config.ts` and `package.json` `exports`.
4. Wire a lazy method on `createCipherLogger` via `loadAdapter("<name>")`.

## Contribution rules (from README)

- Commits must follow [Conventional Commits](https://www.conventionalcommits.org/)
- Run `pnpm run build` before opening a PR
- Keep changes focused and small
- Update README for any API changes
- License: MIT © Cipher Unit
- Update README/docs for any API changes
- License: BSD-3-Clause © Cipher Unit
8 changes: 6 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ permissions:

jobs:
publish:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
# Only publish successful pushes to main — never PRs or other branches.
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -57,4 +61,4 @@ jobs:
exit 0
fi

npm publish --access public
npm publish --access public --provenance
Empty file modified .husky/commit-msg
100644 → 100755
Empty file.
Empty file modified .husky/pre-commit
100644 → 100755
Empty file.
13 changes: 10 additions & 3 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# Source & tooling (published package only ships dist + docs via "files")
# Backup ignore list; package.json "files" is the publish allowlist.
src/
tsconfig.json
tsup.config.ts
pnpm-lock.yaml
pnpm-workspace.yaml
CipherScope_Roadmap.md
eslint.config.mjs
mkdocs.yml
zensical.toml
docs/
.agents/
.github/
.husky/
assets/
examples/
test/

# Local artifacts
Expand All @@ -21,4 +29,3 @@ Thumbs.db
*.swp
*.swo
Backups
examples
32 changes: 23 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center">
<img
src="./assets/cipherlogger_logo_mono.png"
alt="Fusion Snippet"
alt="Cipher Logger"
width="120"
style="border-radius: 18px;"
/>
Expand All @@ -16,20 +16,20 @@

<p align="center">
<a href="https://www.npmjs.com/package/cipher-logger"><img src="https://img.shields.io/npm/v/cipher-logger.svg" alt="npm version"></a>
<a href="https://github.com/cipherunits/CipherLogger/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-blue.svg" alt="license"></a>
<a href="https://github.com/cipherunits/CipherLogger/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-blue.svg" alt="license"></a>
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg" alt="node version"></a>
<a href="https://www.typescriptlang.org"><img src="https://img.shields.io/badge/TypeScript-ready-3178C6.svg" alt="typescript"></a>
</p>

---

**Cipher Logger** is a lightweight, production-ready HTTP request logging library for Node.js. It captures every request in your app — **Express**, **Next.js**, and more frameworks on the way — with full control over which fields get logged.
**Cipher Logger** is a lightweight, production-ready HTTP request logging library for Node.js. It captures every request with adapters for **Express**, **Next.js**, **Fastify**, **Hono**, **NestJS**, and **Nuxt**, with full control over which fields get logged.

## Highlights

- **TypeScript-first**, fully typed API
- **Configurable fields** — required fields always logged, optional fields opt-in
- **Framework adapters** for Express and Next.js, with more in progress
- **Framework adapters** via subpath imports (`cipher-logger/express`, `cipher-logger/next`, …)
- **Zero heavy dependencies** — only your framework as an optional peer dependency
- **Node.js 18+**

Expand All @@ -47,20 +47,28 @@ yarn add cipher-logger

```ts
import { createCipherLogger } from "cipher-logger";
import { createExpressMiddleware } from "cipher-logger/express";

const cipher = createCipherLogger({
fields: { ip: true, userAgent: true, query: true },
level: "info",
});

app.use(cipher.express());
app.use(createExpressMiddleware(cipher));
// or: app.use(cipher.express());
```

## Documentation

Full documentation — configuration reference, framework guides (Express, Next.js, and upcoming adapters), log schema, API reference, and architecture — lives on the docs site:
Full documentation — configuration reference, framework guides, log schema, API reference, and architecture — lives on the docs site:

**[docs.cipherunit.xyz](https://cipherunits.github.io/CipherLogger/)**
| Resource | URL |
|----------|-----|
| Npm Package | [npmjs.com](https://npmjs.com/package/cipher-logger) |
| Documentation | [cipherunits.github.io/CipherLogger](https://cipherunits.github.io/CipherLogger/) |
| GitHub Org | [github.com/cipherunits](https://github.com/cipherunits/CipherLogger) |

---

## Contributing

Expand All @@ -72,12 +80,18 @@ Contributions are welcome.
4. Run `pnpm run build` before submitting
5. Open a Pull Request

See the [full contributing guide](https://docs.cipherunit.xyz/contributing) on the docs site for details.
See the [full contributing guide](https://cipherunits.github.io/CipherLogger/) on the docs site for details.

## License

[BSD-3-Clause](./LICENSE) © [Cipher Unit](https://cipherunit.xyz)

</br>
</br>
</br>
</br>
</br>

<p align="center" style="margin-top: 100px;">
<b><i>Made with ❤️ for developers by CipherUnits</i></b>
</p>
</p>
62 changes: 38 additions & 24 deletions docs/advanced/architecture.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# Architecture

CipherLogger is split into a framework-agnostic **core** and thin, per-framework **adapters**. This keeps the dependency footprint small — you only pull in the adapter for the framework you actually use — and keeps the logging logic itself easy to test in isolation.
CipherLogger is split into a framework-agnostic **core** and thin, per-framework **adapters**. Adapters ship as separate entry points so importing the core package does not load Express, Next.js, or other peers.

## Package layout

```text
cipher-logger/
├── src/
│ ├── core/ # Core — field config & log building
│ │ ├── logger.ts
│ │ ├── types.ts
│ │ ├── build-request-log.ts
│ │ └── create-cipher-logger.ts
│ ├── express/ # Express adapter
│ │ └── middleware.ts
│ ├── next/ # Next.js adapter
│ │ └── middleware.ts
│ └── index.ts # Public entry point
│ ├── core/ # Field config, Logger, createCipherLogger
│ ├── adapters/
│ │ ├── express/
│ │ ├── next/
│ │ ├── nuxt/
│ │ ├── fastify/
│ │ ├── nest/
│ │ └── hono/
│ ├── index.ts # Public core entry
│ ├── express.ts # Subpath: cipher-logger/express
│ ├── next.ts # Subpath: cipher-logger/next
│ └── … # Other adapter entries
└── dist/ # Built CJS + ESM + types
```

## Data flow
Expand All @@ -25,22 +28,33 @@ cipher-logger/
flowchart TB
A[fields config] --> B[buildRequestLog]
B --> C{Adapter}
C --> D[Express middleware]
C --> D[Express / Nest]
C --> E[Next.js middleware]
D --> F[res.finish → accurate status/duration]
E --> G[middleware execution → see timing caveat]
C --> F[withCipherLogger route handler]
C --> G[Fastify / Hono / Nuxt]
D --> H[res.finish → accurate status/duration]
F --> I[real Response status/duration]
E --> J[middleware timing caveat]
```

1. **Core** owns the `fields` configuration and `buildRequestLog`, which assembles a `RequestLog` object from raw request/response data and whatever optional fields are enabled.
2. **Adapters** are responsible only for extracting framework-specific data (headers, timing hooks, request/response objects) and handing it to core in a normalized shape.
3. Each adapter decides *when* logging happens — Express logs on `res.finish` (after the real response), while the Next.js adapter currently logs during middleware execution (see the [timing caveat](../guide/nextjs.md#timing-caveat)).
1. **Core** owns `fields` configuration and `buildRequestLog`.
2. **Adapters** extract framework-specific data and call `cipher.logRequest(...)`.
3. Convenience methods like `cipher.express()` lazy-load the matching `dist/<adapter>` chunk at call time so unused peers are never required.

## Why this split?

- **Small surface area per adapter.** Adding a new framework (Fastify, Hono, NestJS, Nuxt — see the [Roadmap](roadmap.md)) means writing a thin file that maps that framework's request lifecycle onto core, not reimplementing field logic.
- **Zero unnecessary dependencies.** `express` and `next` are optional peer dependencies — installing CipherLogger doesn't pull in either unless you import that adapter.
- **Testable core.** `buildRequestLog` and `Logger` have no framework dependencies, so they're covered by plain unit tests independent of any HTTP server.

## Public entry point

`src/index.ts` re-exports everything documented in the [API Reference](../reference/api.md): `createCipherLogger`, `Logger`, and every public type. Adapters are not imported eagerly — `cipher.express()` and `cipher.next()` are resolved lazily so that, for example, requiring `next` doesn't happen in a pure-Express project.
- **Small surface area per adapter.** Adding a framework means a thin adapter file plus a subpath entry.
- **Optional peers stay optional.** `require("cipher-logger")` / `import "cipher-logger"` does not load `next` or `express`.
- **Typed imports when you need them.** Prefer `import { createExpressMiddleware } from "cipher-logger/express"` for full framework types.

## Public entry points

| Import | Contents |
| ------ | -------- |
| `cipher-logger` | `createCipherLogger`, `Logger`, core types |
| `cipher-logger/express` | `createExpressMiddleware` |
| `cipher-logger/next` | `createNextMiddleware`, `withCipherLogger` |
| `cipher-logger/fastify` | `createFastifyMiddleware` |
| `cipher-logger/hono` | `createHonoMiddleware` |
| `cipher-logger/nest` | `createNestMiddleware` |
| `cipher-logger/nuxt` | `createNuxtMiddleware` |
18 changes: 7 additions & 11 deletions docs/advanced/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ CipherLogger is under active development. This page tracks what's planned so you
| Framework | Status |
| --------- | ------ |
| Express | :material-check-circle:{ style="color: #4caf50" } Stable |
| Next.js | :material-check-circle:{ style="color: #4caf50" } Stable (see [timing caveat](../guide/nextjs.md#timing-caveat)) |
| Fastify | :material-clock-outline: Planned |
| Hono | :material-clock-outline: Planned |
| NestJS | :material-clock-outline: Planned |
| Nuxt | :material-clock-outline: Planned |

## Accurate Next.js response logging

Today, the Next.js adapter logs during middleware execution, so `status` and `duration` don't reflect the final route response (full explanation in the [Next.js guide](../guide/nextjs.md#timing-caveat)). Route-handler wrappers that log the *actual* final response are planned, mirroring how the Express adapter already works via `res.finish`.
| Next.js | :material-check-circle:{ style="color: #4caf50" } Stable — middleware + [`withCipherLogger`](../guide/nextjs.md#accurate-route-handler-logging) |
| Fastify | :material-check-circle:{ style="color: #4caf50" } Available (`cipher-logger/fastify`) |
| Hono | :material-check-circle:{ style="color: #4caf50" } Available (`cipher-logger/hono`) |
| NestJS | :material-check-circle:{ style="color: #4caf50" } Available — Express-compatible middleware (`cipher-logger/nest`) |
| Nuxt | :material-check-circle:{ style="color: #4caf50" } Available — Node-style middleware (`cipher-logger/nuxt`) |

## Package structure

A restructure of `src/` into clearer `core/` and `adapters/` directories is planned, so that adding a new framework adapter is a self-contained addition rather than a change scattered across the package. Subpath exports (e.g. `cipher-logger/express`, `cipher-logger/next`) are also being considered, so importing one adapter doesn't pull in code for frameworks you don't use.
Core and adapters live under `src/core/` and `src/adapters/`. Subpath exports (`cipher-logger/express`, `cipher-logger/next`, …) keep peer framework code out of the main entry until you import (or call) that adapter.

## Contributing to the roadmap

Have a framework you'd like supported, or a field you think should be built in? Open an issue on [GitHub](https://github.com/cipherunits/CipherLogger/issues) — see the [Contributing](../index.md) section of the README for the process.
Have a framework you'd like supported, or a field you think should be built in? Open an issue on [GitHub](https://github.com/cipherunits/CipherLogger/issues).
4 changes: 2 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Not really — they solve different problems. Winston and Pino are general-purpo

## Why is `duration` wrong in my Next.js logs?

This is expected with the current Next.js adapter — see the [timing caveat](guide/nextjs.md#timing-caveat). Middleware runs before your route handler, so the adapter can only measure its own execution time, not the full response. A fix (route-handler wrappers) is on the [Roadmap](advanced/roadmap.md).
This is expected with `createNextMiddleware` / `cipher.next()` — see the [timing caveat](guide/nextjs.md#timing-caveat). Middleware runs before your route handler. Use [`withCipherLogger`](guide/nextjs.md#accurate-route-handler-logging) on App Router handlers for accurate status/duration.

## Can I use CipherLogger without Express or Next.js?

Expand All @@ -22,7 +22,7 @@ Each enabled field does a small amount of extra work per request (header read, q

## Is CommonJS supported, or only ESM?

Both. CipherLogger ships dual ESM/CJS builds, so `import` and `require` both work out of the box.
Both. CipherLogger ships dual ESM/CJS builds with an `exports` map, so `import` and `require` both resolve correctly — including subpaths like `cipher-logger/express`.

## Where do I report a bug or request a framework adapter?

Expand Down
10 changes: 8 additions & 2 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

## Peer dependencies

CipherLogger only requires the framework you actually use — everything else stays out of your `node_modules`.
CipherLogger only requires the framework you actually use — everything else stays out of your `node_modules`. Import the matching subpath (for example `cipher-logger/express`) or call the lazy convenience method (`cipher.express()`).

=== "Express"

Expand All @@ -36,14 +36,20 @@ CipherLogger only requires the framework you actually use — everything else st
npm install next
```

=== "Fastify / Hono / Nest / Nuxt"

```bash
npm install fastify # or hono / @nestjs/common / nuxt
```

If you only use the framework-agnostic [`Logger`](../reference/api.md#logger) class, no peer dependency is required at all.

## Requirements

| Requirement | Version |
| ----------- | ------- |
| Node.js | 18 or later |
| TypeScript | 5.x (optional — CipherLogger ships its own `.d.ts` files) |
| TypeScript | 5.x or 6.x (optional — CipherLogger ships its own `.d.ts` files) |
| Module system | ESM and CommonJS both supported |

## Verifying the install
Expand Down
Loading
Loading