cipher-logger 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: cipher-logger
3
+ description: >
4
+ Guidance for correct usage of the `cipher-logger` package (npm: cipher-logger,
5
+ organization: cipherunits) — an HTTP logging library for Node.js used with Express
6
+ and Next.js. Activate this skill whenever a user wants request logging, error
7
+ logging, middleware logs, or basic observability added to an Express or Next.js
8
+ project (especially cipherunits projects like rayyan-backend, Mananegar, Setad
9
+ Mahalle, Fusion GUI), or when imports of "cipher-logger" or `createCipherLogger`
10
+ are detected in code. Also use this skill to review/debug code that uses
11
+ cipher-logger (for example, incorrect status/duration in Next.js middleware or
12
+ optional fields not being logged) — even if the user simply asks "set up a
13
+ logger" or "add request logging" without naming the package.
14
+ ---
15
+
16
+ # Cipher Logger — Agent Skill
17
+
18
+ This skill is the official reference for installing, configuring, and correctly
19
+ using **cipher-logger** — a lightweight, production-ready HTTP logging library
20
+ for Node.js (organization: `cipherunits`, repository: `github.com/cipherunits/CipherLogger`).
21
+ Important: do not assume the API based on similarity to winston/pino/morgan — use
22
+ the exact API documented here.
23
+
24
+ ## Golden Rule
25
+
26
+ cipher-logger has three distinct parts that must not be mixed:
27
+
28
+ 1. **`Logger`** — the base application logger class (independent of HTTP). Use for
29
+ `logger.info/warn/error/debug`.
30
+ 2. **`createCipherLogger(config)`** — factory that creates the main instance,
31
+ configures optional fields and level, and exposes `.express()`, `.next()`, and
32
+ `.logRequest()`.
33
+ 3. **Framework adapters** (`cipher.express()` and `cipher.next()`) — ready-made
34
+ middleware; mount them rather than writing manual request logging.
35
+
36
+ If the user only asks to "log requests", they usually need steps 2 and 3, not 1.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pnpm add cipher-logger
42
+ # Framework packages are peer dependencies:
43
+ pnpm add express # if using Express
44
+ pnpm add next # if using Next.js
45
+ ```
46
+
47
+ Node.js 18+ is required. Install only the peer dependency actually used by the
48
+ project (e.g., `express` or `next`).
49
+
50
+ ## Quick Decision Map
51
+
52
+ | Project type | What to mount/call |
53
+ |---|---|
54
+ | Express API (e.g. rayyan-backend if the backend is Node) | `cipher.express()` mounted before routes |
55
+ | Next.js (e.g. Setad Mahalle, Mananegar) | `middleware.ts` using `cipher.next()` |
56
+ | Manual non-HTTP event logging | `new Logger({...})` and `.info/.warn/.error/.debug` |
57
+ | Manual request logging (e.g. inside a job/cron) | `cipher.logRequest(input)` |
58
+
59
+ ## Usage Steps (Summary)
60
+
61
+ 1. Create a logger with `createCipherLogger({ fields: {...}, level: "info", prefix: "..." })` —
62
+ enable only the fields the user actually needs; all optional fields default to `false`.
63
+ 2. For Express: mount `app.use(cipher.express())` **before** route definitions, otherwise
64
+ status/duration will be incorrect because the middleware must observe the request.
65
+ 3. For Next.js: use `cipher.next()` in `middleware.ts` (or `src/middleware.ts`) and
66
+ always restrict `config.matcher` to exclude static/image routes to avoid noisy logs.
67
+ 4. For more detailed guidance, read the related skill files below — only open the
68
+ section relevant to the user's request:
69
+ - `skills/fusion-references/SKILL.md` — configuration options and optional fields
70
+ - `skills/fusion-express-integration/SKILL.md` — Express integration and sample output
71
+ - `skills/fusion-nextjs-integration/SKILL.md` — Next.js middleware and the important
72
+ status/duration limitation
73
+ - `skills/fusion-log-schema/SKILL.md` — full log schema (required/optional fields)
74
+ - `skills/fusion-api-reference/SKILL.md` — function/class signatures and TypeScript exports
75
+ - `skills/fusion-architecture/SKILL.md` — internal package structure (for debugging/contrib)
76
+
77
+ ## Common Pitfalls (Follow these)
78
+
79
+ - **Check peer dependencies**: If the project is Express but `express` is not installed
80
+ (or vice versa), a runtime error will occur. Check `package.json` before suggesting code.
81
+ - **Default `level` is `"debug"`**, not `"info"` — in production `"info"` is usually
82
+ more appropriate; set it explicitly in config instead of relying on the package default.
83
+ - **In Next.js middleware, `status` and `duration` reflect the middleware's execution, not
84
+ the final route response** (because middleware runs before route handlers). Remind the
85
+ user of this when they expect the actual response status — route-handler wrappers are
86
+ planned for future versions but are not available now.
87
+ - **In Express the opposite is true**: `status` and `duration` are recorded after `res.finish`
88
+ and therefore reflect the real response — explain this difference when comparing adapters.
89
+ - **Optional fields are off by default**; the user must explicitly enable `ip`, `userAgent`,
90
+ `query`, etc.
91
+ - **This package is lightweight** — don't recommend adding heavy log frameworks like
92
+ winston/pino unless the user specifically asks; the package is intended to be minimal.
93
+ - **Exclude static routes in Next.js matcher** (example: `/((?!_next/static|_next/image|favicon.ico).*)`) to
94
+ avoid excessive noisy logs.
95
+
96
+ ## Before Suggesting Code
97
+
98
+ 1. Check whether the project is Express or Next.js (via `package.json` or folder structure).
99
+ 2. If `middleware.ts` already exists, merge the new code instead of replacing it (it may
100
+ contain other middleware like auth or i18n).
101
+ 3. If the project is a monorepo (e.g. Mananegar), determine which workspace/app the
102
+ package should be installed into, not necessarily the monorepo root.
103
+
@@ -0,0 +1,61 @@
1
+ # API Reference
2
+
3
+ ## `createCipherLogger(config?)`
4
+
5
+ instance اصلی cipher-logger را می‌سازد.
6
+
7
+ ```ts
8
+ const cipher = createCipherLogger(config);
9
+ ```
10
+
11
+ ### Instance methods
12
+
13
+ | Method | Description |
14
+ | ----------------------------- | ---------------------------------------- |
15
+ | `cipher.logRequest(input)` | Manually log an HTTP request |
16
+ | `cipher.express()` | Returns Express middleware |
17
+ | `cipher.next()` | Returns Next.js middleware |
18
+
19
+ Use `cipher.logRequest(input)` when the request cannot be captured by standard
20
+ middleware — for example inside a route handler when you need to log the actual
21
+ response status (not the middleware's status), subject to the limitation described
22
+ in `skills/fusion-nextjs-integration/SKILL.md`.
23
+
24
+ ## `Logger` — base application logger (non-HTTP)
25
+
26
+ For regular application logs (server startup, non-HTTP errors, deprecation warnings)
27
+ use `Logger` directly rather than `createCipherLogger`:
28
+
29
+ ```ts
30
+ import { Logger } from "cipher-logger";
31
+
32
+ const logger = new Logger({ level: "info", prefix: "app" });
33
+
34
+ logger.info("Server started");
35
+ logger.warn("Deprecated API used", { route: "/old" });
36
+ logger.error("Unhandled error", { err: "..." });
37
+ logger.debug("Debug info");
38
+ ```
39
+
40
+ Each method (`info` / `warn` / `error` / `debug`) accepts a text message and an
41
+ optional metadata object.
42
+
43
+ ## TypeScript Exports
44
+
45
+ ```ts
46
+ import type {
47
+ CipherLogger,
48
+ CipherLoggerConfig,
49
+ RequestLog,
50
+ RequestLogInput,
51
+ OptionalRequestField,
52
+ LogLevel,
53
+ LoggerOptions,
54
+ ExpressMiddleware,
55
+ NextMiddleware,
56
+ } from "cipher-logger";
57
+ ```
58
+
59
+ If the user wants to write a type-safe wrapper or factory over this package
60
+ (for example for Fusion GUI or a shared package in the Mananegar monorepo), use
61
+ the provided types rather than redefining them manually.
@@ -0,0 +1,64 @@
1
+ # Architecture Reference
2
+
3
+ Only when the user needs to modify the CipherLogger package itself (not a project
4
+ that uses it) — for example, bug fixes, adding a new optional field, or writing an
5
+ adapter for another framework (like Fastify or Hono).
6
+
7
+ ## Folder structure
8
+
9
+ ```
10
+ cipher-logger/
11
+ ├── src/
12
+ │ ├── core/ # core — field configuration and log construction
13
+ │ │ ├── logger.ts
14
+ │ │ ├── types.ts
15
+ │ │ ├── build-request-log.ts
16
+ │ │ └── create-cipher-logger.ts
17
+ │ ├── express/ # Express adapter
18
+ │ │ └── middleware.ts
19
+ │ ├── next/ # Next.js adapter
20
+ │ │ └── middleware.ts
21
+ │ └── index.ts # public entry point
22
+ ```
23
+
24
+ ## Data flow
25
+
26
+ ```
27
+ Core
28
+ fields config → buildRequestLog
29
+
30
+ ┌─────────┴─────────┐
31
+ ▼ ▼
32
+ Express Next.js
33
+ middleware middleware
34
+ ```
35
+
36
+ Both adapters (`express/middleware.ts` and `next/middleware.ts`) rely on the
37
+ same `buildRequestLog` core — only the extraction of request/response fields from
38
+ the framework differs. The difference in `status`/`duration` behavior between the
39
+ two adapters stems from how each adapter calls the core, not from `buildRequestLog`
40
+ itself.
41
+
42
+ ## Local development
43
+
44
+ ```bash
45
+ git clone https://github.com/cipherunits/CipherLogger.git
46
+ cd CipherLogger
47
+ pnpm install
48
+ pnpm run build
49
+ ```
50
+
51
+ ## Adding a new adapter (e.g. Fastify)
52
+
53
+ Create a new file such as `src/fastify/middleware.ts` that calls the same
54
+ `buildRequestLog` from `core/build-request-log.ts` and only differs in how fields
55
+ are extracted from the framework — do not reimplement the log-construction logic
56
+ inside the new adapter.
57
+
58
+ ## Contribution rules (from README)
59
+
60
+ - Commits must follow [Conventional Commits](https://www.conventionalcommits.org/)
61
+ - Run `pnpm run build` before opening a PR
62
+ - Keep changes focused and small
63
+ - Update README for any API changes
64
+ - License: MIT © Cipher Unit
@@ -0,0 +1,61 @@
1
+ # Express Integration Reference
2
+
3
+ ## Installation and mount
4
+
5
+ ```ts
6
+ import express from "express";
7
+ import { createCipherLogger } from "cipher-logger";
8
+
9
+ const app = express();
10
+
11
+ const cipher = createCipherLogger({
12
+ fields: {
13
+ ip: true,
14
+ userAgent: true,
15
+ query: true,
16
+ requestId: true,
17
+ },
18
+ level: "info",
19
+ prefix: "express",
20
+ });
21
+
22
+ // Mount before defining routes
23
+ app.use(cipher.express());
24
+
25
+ app.get("/users", (req, res) => {
26
+ res.json({ users: [] });
27
+ });
28
+
29
+ app.listen(3000, () => {
30
+ console.log("Server running on http://localhost:3000");
31
+ });
32
+ ```
33
+
34
+ ## Critical note: mount order
35
+
36
+ `app.use(cipher.express())` must be called **before any route handlers** (it is
37
+ usually fine to mount it after body-parser/CORS at the start of the chain) so the
38
+ middleware can observe `res.finish`.
39
+
40
+ ## Sample real output
41
+
42
+ ```
43
+ [2026-09-01T20:00:00.000Z] [INFO] [express] HTTP Request {
44
+ id: 'a1b2c3d4-...',
45
+ type: 'http',
46
+ timestamp: '2026-09-01T20:00:00.000Z',
47
+ method: 'GET',
48
+ path: '/users?page=1',
49
+ status: 200,
50
+ duration: 12,
51
+ ip: '::1',
52
+ userAgent: 'Mozilla/5.0 ...',
53
+ query: { page: '1' }
54
+ }
55
+ ```
56
+
57
+ ## Why Express is more accurate than Next.js
58
+
59
+ In Express, `status` and `duration` are recorded **after the `res.finish` event** —
60
+ so they reflect the actual response sent to the client. Mention this when explaining
61
+ the behavioral difference between adapters (see `skills/fusion-nextjs-integration/SKILL.md`).
@@ -0,0 +1,34 @@
1
+ # Log Schema Reference
2
+
3
+ ## Required fields (`required`) — always present in every HTTP log
4
+
5
+ | Field | Type | Description |
6
+ | ----------- | -------- | --------------------------- |
7
+ | `id` | `string` | Unique identifier (UUID) |
8
+ | `type` | `"http"` | Log type |
9
+ | `timestamp` | `string` | ISO 8601 timestamp |
10
+ | `method` | `string` | HTTP method |
11
+ | `path` | `string` | Request path |
12
+ | `status` | `number` | HTTP status code |
13
+ | `duration` | `number` | Duration in milliseconds |
14
+
15
+ ## Optional fields (`optional`) — enabled via `fields` in config
16
+
17
+ | Field | Type | Description |
18
+ | ----------- | ---------------------------- | ---------------------------- |
19
+ | `ip` | `string` | Client IP address |
20
+ | `userAgent` | `string` | User-Agent header |
21
+ | `referer` | `string` | Referer header |
22
+ | `protocol` | `string` | `http` / `https` |
23
+ | `host` | `string` | Host header |
24
+ | `query` | `Record<string, string>` | Query string parameters |
25
+ | `requestId` | `string` | From the `x-request-id` header |
26
+ | `metadata` | `Record<string, unknown>` | Arbitrary metadata |
27
+
28
+ See `skills/fusion-references/SKILL.md` for the full configuration details of these fields.
29
+
30
+ ## Note for log processing/ingestion (for example, when sending to a log pipeline)
31
+
32
+ Each HTTP log is a flat object with `type: "http"` — if you need to distinguish
33
+ between HTTP logs and manual logs (calls to `logger.info(...)` from the `Logger`
34
+ class), check the `type` field; manual logs do not include `type: "http"`.
@@ -0,0 +1,81 @@
1
+ # Next.js Integration Reference
2
+
3
+ ## Full approach — `middleware.ts` at project root (or `src/middleware.ts`)
4
+
5
+ ```ts
6
+ import { createCipherLogger } from "cipher-logger";
7
+ import type { NextRequest } from "next/server";
8
+
9
+ const cipher = createCipherLogger({
10
+ fields: {
11
+ ip: true,
12
+ userAgent: true,
13
+ host: true,
14
+ query: true,
15
+ },
16
+ level: "info",
17
+ });
18
+
19
+ export function middleware(request: NextRequest) {
20
+ return cipher.next()(request);
21
+ }
22
+
23
+ export const config = {
24
+ matcher: [
25
+ // all paths except static and image files
26
+ "/((?!_next/static|_next/image|favicon.ico).*)",
27
+ ],
28
+ };
29
+ ```
30
+
31
+ ## Short approach (when no other middleware exists in the project)
32
+
33
+ ```ts
34
+ import { createCipherLogger } from "cipher-logger";
35
+
36
+ const cipher = createCipherLogger({
37
+ fields: { ip: true, userAgent: true },
38
+ });
39
+
40
+ export default cipher.next();
41
+
42
+ export const config = {
43
+ matcher: ["/api/:path*", "/dashboard/:path*"],
44
+ };
45
+ ```
46
+
47
+ ⚠️ If the project already has `middleware.ts` (for example auth or i18n — like
48
+ Mananegar/Setad Mahalle which are multilingual), do not use the short approach; instead
49
+ merge the export function with the existing logic using the full method above, because
50
+ only one default middleware export is allowed per Next.js project.
51
+
52
+ ## ⚠️ Important limitation — read before explaining status/duration to the user
53
+
54
+ Next.js middleware runs **before** the route handler. Therefore:
55
+
56
+ - `status` and `duration` recorded in the logs refer to the **middleware's execution**,
57
+ not the final response returned by the route handler.
58
+ - If the user expects the actual API route status code (e.g. 404 or 500 from inside a
59
+ handler) to appear in this log, **that is not possible** with the current package.
60
+ - According to the official package docs, a "route handler wrapper for more accurate
61
+ logging" is planned for future versions but is not available now — do not claim
62
+ such an API exists at present.
63
+ - If the user needs accurate status/duration for a route, the current solution is to
64
+ manually call `cipher.logRequest(input)` inside the route handler or use the `Logger`
65
+ class (see `skills/fusion-api-reference/SKILL.md`), not the middleware.
66
+
67
+ ## Always restrict the matcher
68
+
69
+ Without a restricted `matcher`, static requests (`_next/static`, `_next/image`,
70
+ `favicon.ico`) will also be logged and produce a lot of noise. Official suggested
71
+ pattern:
72
+
73
+ ```ts
74
+ matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"];
75
+ ```
76
+
77
+ Or restrict to specific paths:
78
+
79
+ ```ts
80
+ matcher: ["/api/:path*", "/dashboard/:path*"];
81
+ ```
@@ -0,0 +1,51 @@
1
+ # Configuration Reference
2
+
3
+ ## `createCipherLogger(config?)`
4
+
5
+ ```ts
6
+ import { createCipherLogger } from "cipher-logger";
7
+
8
+ const cipher = createCipherLogger({
9
+ fields: {
10
+ ip: true,
11
+ userAgent: true,
12
+ referer: false,
13
+ protocol: true,
14
+ host: true,
15
+ query: true,
16
+ requestId: true,
17
+ metadata: false,
18
+ },
19
+ level: "info",
20
+ prefix: "api",
21
+ });
22
+ ```
23
+
24
+ ## Options Table
25
+
26
+ | Option | Type | Default | Description |
27
+ | -------- | -------------------------------------------------- | ------------ | ------------------------------------ |
28
+ | `fields` | `Partial<Record<OptionalRequestField, boolean>>` | all `false` | Which optional fields to include in logs |
29
+ | `level` | `"debug" \| "info" \| "warn" \| "error"` | `"debug"` | Minimum log level |
30
+ | `prefix` | `string` | — | Prefix used in console output |
31
+ > **Note:** The package default `level` is `"debug"`, which is often too verbose for
32
+ production. If the user doesn't specify a level, explicitly set `"info"` in the
33
+ config and document the reason rather than relying on the package default.
34
+
35
+ ## Optional fields (`fields`) — all default to `false`
36
+
37
+ | Field | Type | Description |
38
+ | ----------- | -------------------------- | ------------------------------ |
39
+ | `ip` | `string` | Client IP address |
40
+ | `userAgent` | `string` | User-Agent header |
41
+ | `referer` | `string` | Referer header |
42
+ | `protocol` | `string` | `http` or `https` |
43
+ | `host` | `string` | Host header |
44
+ | `query` | `Record<string, string>` | Query string parameters |
45
+ | `requestId` | `string` | From the `x-request-id` header |
46
+ | `metadata` | `Record<string, unknown>` | Arbitrary metadata |
47
+
48
+ Enable only the fields the user truly needs; turning on everything can produce
49
+ large logs and risk leaking sensitive data (for example IPs or query params that
50
+ contain tokens). If query params may contain secrets (passwords, tokens), warn the
51
+ user before enabling `query: true`.
package/README.md CHANGED
@@ -39,6 +39,10 @@
39
39
  - **Express middleware** — records real `status` and `duration` after the response finishes
40
40
  - **Next.js middleware** — drop-in support for `middleware.ts`
41
41
  - **Zero heavy dependencies** — only `express` or `next` as optional peer dependencies
42
+ - **Fastify middleware** — lightweight hook-compatible middleware for Fastify
43
+ - **NestJS middleware** — Express-compatible middleware for Nest apps
44
+ - **Hono middleware** — edge-friendly middleware for Hono apps
45
+ - **Zero heavy dependencies** — only framework peers are optional
42
46
  - **Node.js 18+** compatible
43
47
 
44
48
  ---
@@ -63,6 +67,15 @@ npm install express
63
67
 
64
68
  # Next.js
65
69
  npm install next
70
+
71
+ # Fastify
72
+ npm install fastify
73
+
74
+ # NestJS (core packages)
75
+ npm install @nestjs/core @nestjs/common
76
+
77
+ # Hono
78
+ npm install hono
66
79
  ```
67
80
 
68
81
  ---
@@ -222,6 +235,73 @@ export const config = {
222
235
 
223
236
  ---
224
237
 
238
+ ## Fastify
239
+
240
+ Register the Fastify-compatible middleware returned by `cipher.fastify()` using `addHook`:
241
+
242
+ ```typescript
243
+ import fastify from "fastify";
244
+ import { createCipherLogger } from "cipher-logger";
245
+
246
+ const app = fastify();
247
+
248
+ const cipher = createCipherLogger({
249
+ fields: { ip: true, userAgent: true, query: true },
250
+ level: "info",
251
+ });
252
+
253
+ // Register before your routes
254
+ app.addHook("onRequest", cipher.fastify());
255
+
256
+ app.get("/", async () => ({ hello: "world" }));
257
+
258
+ app.listen({ port: 3000 });
259
+ ```
260
+
261
+ ## NestJS
262
+
263
+ Use the Express-compatible middleware in Nest's runtime (works when Nest is using the Express platform):
264
+
265
+ ```typescript
266
+ import { NestFactory } from "@nestjs/core";
267
+ import { AppModule } from "./app.module";
268
+ import { createCipherLogger } from "cipher-logger";
269
+
270
+ async function bootstrap() {
271
+ const app = await NestFactory.create(AppModule);
272
+
273
+ const cipher = createCipherLogger({ fields: { ip: true, userAgent: true } });
274
+
275
+ // Mount as global middleware
276
+ app.use(cipher.nest());
277
+
278
+ await app.listen(3000);
279
+ }
280
+
281
+ bootstrap();
282
+ ```
283
+
284
+ ## Hono
285
+
286
+ Mount the Hono middleware using `app.use` (works on edge and Node runtimes):
287
+
288
+ ```typescript
289
+ import { Hono } from "hono";
290
+ import { createCipherLogger } from "cipher-logger";
291
+
292
+ const app = new Hono();
293
+
294
+ const cipher = createCipherLogger({ fields: { ip: true, userAgent: true } });
295
+
296
+ // Mount for all routes
297
+ app.use("*", cipher.hono());
298
+
299
+ app.get("/", (c) => c.text("ok"));
300
+
301
+ app.listen({ port: 3000 });
302
+ ```
303
+
304
+
225
305
  ## Log Schema
226
306
 
227
307
  ### Required Fields
@@ -301,6 +381,9 @@ import type {
301
381
  LoggerOptions,
302
382
  ExpressMiddleware,
303
383
  NextMiddleware,
384
+ FastifyMiddleware,
385
+ NestMiddleware,
386
+ HonoMiddleware,
304
387
  } from "cipher-logger";
305
388
  ```
306
389
 
@@ -383,16 +466,8 @@ Contributions are welcome and appreciated.
383
466
 
384
467
  ## License
385
468
 
386
- This project is licensed under the [BSD 3-Clause License](LICENSE).
387
-
388
- ```
389
- Copyright (c) 2026, Cipher Logger
390
- ```
391
-
392
- Free to use, modify, and distribute — provided the copyright notice and license terms are preserved. See [LICENSE](LICENSE) for full details.
393
-
394
- ---
469
+ [MIT](./LICENSE) © Cipher Unit
395
470
 
396
471
  <p align="center">
397
- Built with ❤️ by <a href="https://github.com/cipherunits">CipherUnits</a>
472
+ <i>Built by <a href="https://cipherunit.xyz">Cipher Unit</a></i>
398
473
  </p>
package/dist/index.d.mts CHANGED
@@ -1,5 +1,7 @@
1
- import { Request, Response, NextFunction } from 'express';
1
+ import { Request, Response as Response$1, NextFunction } from 'express';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
+ import { FastifyRequest, FastifyReply } from 'fastify';
4
+ import { Context } from 'hono';
3
5
 
4
6
  type LogLevel = "info" | "warn" | "error" | "debug";
5
7
  type LogMeta = Record<string, unknown>;
@@ -59,19 +61,31 @@ type CipherLoggerConfig = {
59
61
  prefix?: string;
60
62
  };
61
63
 
62
- type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
64
+ type ExpressMiddleware = (req: Request, res: Response$1, next: NextFunction) => void;
63
65
  declare function createExpressMiddleware(cipher: CipherLogger): ExpressMiddleware;
64
66
 
65
67
  type NextMiddleware = (request: NextRequest) => NextResponse | Promise<NextResponse>;
66
68
  declare function createNextMiddleware(cipher: CipherLogger): NextMiddleware;
67
69
 
70
+ type FastifyMiddleware = (request: FastifyRequest, reply: FastifyReply) => void;
71
+ declare function createFastifyMiddleware(cipher: CipherLogger): FastifyMiddleware;
72
+
73
+ type NestMiddleware = (req: Request, res: Response$1, next: NextFunction) => void;
74
+ declare function createNestMiddleware(cipher: CipherLogger): NestMiddleware;
75
+
76
+ type HonoMiddleware = (c: Context, next: () => Promise<void>) => Response | Promise<Response>;
77
+ declare function createHonoMiddleware(cipher: CipherLogger): HonoMiddleware;
78
+
68
79
  interface CipherLogger {
69
80
  logRequest(input: RequestLogInput): RequestLog;
70
81
  express(): ExpressMiddleware;
71
82
  next(): NextMiddleware;
83
+ fastify(): FastifyMiddleware;
84
+ nest(): NestMiddleware;
85
+ hono(): HonoMiddleware;
72
86
  }
73
87
  declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
74
88
 
75
89
  declare const logger: Logger;
76
90
 
77
- export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NextMiddleware, type OptionalRequestField, type RequestLog, type RequestLogInput, createCipherLogger, createExpressMiddleware, createNextMiddleware, logger, type RequestLog as request };
91
+ export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type FastifyMiddleware, type HonoMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NestMiddleware, type NextMiddleware, type OptionalRequestField, type RequestLog, type RequestLogInput, createCipherLogger, createExpressMiddleware, createFastifyMiddleware, createHonoMiddleware, createNestMiddleware, createNextMiddleware, logger, type RequestLog as request };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { Request, Response, NextFunction } from 'express';
1
+ import { Request, Response as Response$1, NextFunction } from 'express';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
+ import { FastifyRequest, FastifyReply } from 'fastify';
4
+ import { Context } from 'hono';
3
5
 
4
6
  type LogLevel = "info" | "warn" | "error" | "debug";
5
7
  type LogMeta = Record<string, unknown>;
@@ -59,19 +61,31 @@ type CipherLoggerConfig = {
59
61
  prefix?: string;
60
62
  };
61
63
 
62
- type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
64
+ type ExpressMiddleware = (req: Request, res: Response$1, next: NextFunction) => void;
63
65
  declare function createExpressMiddleware(cipher: CipherLogger): ExpressMiddleware;
64
66
 
65
67
  type NextMiddleware = (request: NextRequest) => NextResponse | Promise<NextResponse>;
66
68
  declare function createNextMiddleware(cipher: CipherLogger): NextMiddleware;
67
69
 
70
+ type FastifyMiddleware = (request: FastifyRequest, reply: FastifyReply) => void;
71
+ declare function createFastifyMiddleware(cipher: CipherLogger): FastifyMiddleware;
72
+
73
+ type NestMiddleware = (req: Request, res: Response$1, next: NextFunction) => void;
74
+ declare function createNestMiddleware(cipher: CipherLogger): NestMiddleware;
75
+
76
+ type HonoMiddleware = (c: Context, next: () => Promise<void>) => Response | Promise<Response>;
77
+ declare function createHonoMiddleware(cipher: CipherLogger): HonoMiddleware;
78
+
68
79
  interface CipherLogger {
69
80
  logRequest(input: RequestLogInput): RequestLog;
70
81
  express(): ExpressMiddleware;
71
82
  next(): NextMiddleware;
83
+ fastify(): FastifyMiddleware;
84
+ nest(): NestMiddleware;
85
+ hono(): HonoMiddleware;
72
86
  }
73
87
  declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
74
88
 
75
89
  declare const logger: Logger;
76
90
 
77
- export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NextMiddleware, type OptionalRequestField, type RequestLog, type RequestLogInput, createCipherLogger, createExpressMiddleware, createNextMiddleware, logger, type RequestLog as request };
91
+ export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type FastifyMiddleware, type HonoMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NestMiddleware, type NextMiddleware, type OptionalRequestField, type RequestLog, type RequestLogInput, createCipherLogger, createExpressMiddleware, createFastifyMiddleware, createHonoMiddleware, createNestMiddleware, createNextMiddleware, logger, type RequestLog as request };
package/dist/index.js CHANGED
@@ -23,6 +23,9 @@ __export(src_exports, {
23
23
  Logger: () => Logger,
24
24
  createCipherLogger: () => createCipherLogger,
25
25
  createExpressMiddleware: () => createExpressMiddleware,
26
+ createFastifyMiddleware: () => createFastifyMiddleware,
27
+ createHonoMiddleware: () => createHonoMiddleware,
28
+ createNestMiddleware: () => createNestMiddleware,
26
29
  createNextMiddleware: () => createNextMiddleware,
27
30
  logger: () => logger
28
31
  });
@@ -151,7 +154,7 @@ function buildRequestLog(fields, input) {
151
154
  return log;
152
155
  }
153
156
 
154
- // src/express/middleware.ts
157
+ // src/adapters/express/middleware.ts
155
158
  function parseQuery(query) {
156
159
  const result = {};
157
160
  for (const [key, value] of Object.entries(query)) {
@@ -191,7 +194,7 @@ function createExpressMiddleware(cipher) {
191
194
  };
192
195
  }
193
196
 
194
- // src/next/middleware.ts
197
+ // src/adapters/next/middleware.ts
195
198
  var import_server = require("next/server");
196
199
  function getHeader2(request, name) {
197
200
  return request.headers.get(name) ?? void 0;
@@ -224,6 +227,145 @@ function createNextMiddleware(cipher) {
224
227
  };
225
228
  }
226
229
 
230
+ // src/adapters/fastify/middleware.ts
231
+ function getHeader3(req, name) {
232
+ const value = req.headers[name];
233
+ if (value === void 0) {
234
+ return void 0;
235
+ }
236
+ return Array.isArray(value) ? String(value[0]) : String(value);
237
+ }
238
+ function parseQuery3(req) {
239
+ try {
240
+ const raw = req.raw.url ?? req.url ?? "";
241
+ const url = new URL(raw, "http://localhost");
242
+ const result = {};
243
+ for (const [key, value] of url.searchParams.entries()) {
244
+ result[key] = value;
245
+ }
246
+ return Object.keys(result).length > 0 ? result : void 0;
247
+ } catch {
248
+ return void 0;
249
+ }
250
+ }
251
+ function createFastifyMiddleware(cipher) {
252
+ return (request, reply) => {
253
+ const start = Date.now();
254
+ reply.raw.once("finish", () => {
255
+ const protocol = request.headers["x-forwarded-proto"] || (request.raw.socket && request.raw.socket.encrypted ? "https" : "http");
256
+ cipher.logRequest({
257
+ method: request.method,
258
+ path: request.raw.url ?? request.url,
259
+ status: reply.statusCode,
260
+ duration: Date.now() - start,
261
+ ip: request.headers["x-forwarded-for"] ? request.headers["x-forwarded-for"].split(",")[0].trim() : request.ip || request.socket?.remoteAddress || request.raw.socket?.remoteAddress,
262
+ userAgent: getHeader3(request, "user-agent"),
263
+ referer: getHeader3(request, "referer"),
264
+ protocol,
265
+ host: getHeader3(request, "host"),
266
+ query: parseQuery3(request),
267
+ requestId: getHeader3(request, "x-request-id")
268
+ });
269
+ });
270
+ };
271
+ }
272
+
273
+ // src/adapters/nest/middleware.ts
274
+ function parseQuery4(query) {
275
+ const result = {};
276
+ for (const [key, value] of Object.entries(query)) {
277
+ if (value === void 0) {
278
+ continue;
279
+ }
280
+ result[key] = typeof value === "string" ? value : Array.isArray(value) ? String(value[0] ?? "") : String(value);
281
+ }
282
+ return Object.keys(result).length > 0 ? result : void 0;
283
+ }
284
+ function getHeader4(req, name) {
285
+ const value = req.headers[name];
286
+ if (value === void 0) {
287
+ return void 0;
288
+ }
289
+ return Array.isArray(value) ? value[0] : value;
290
+ }
291
+ function createNestMiddleware(cipher) {
292
+ return (req, res, next) => {
293
+ const start = Date.now();
294
+ res.on("finish", () => {
295
+ cipher.logRequest({
296
+ method: req.method,
297
+ path: req.originalUrl || req.url,
298
+ status: res.statusCode,
299
+ duration: Date.now() - start,
300
+ ip: req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip || req.socket.remoteAddress,
301
+ userAgent: getHeader4(req, "user-agent"),
302
+ referer: getHeader4(req, "referer"),
303
+ protocol: req.protocol,
304
+ host: getHeader4(req, "host"),
305
+ query: parseQuery4(req.query),
306
+ requestId: getHeader4(req, "x-request-id")
307
+ });
308
+ });
309
+ next();
310
+ };
311
+ }
312
+
313
+ // src/adapters/hono/middleware.ts
314
+ function getHeader5(request, name) {
315
+ if (!request) return void 0;
316
+ if (typeof request.header === "function") {
317
+ return request.header(name) ?? void 0;
318
+ }
319
+ if (request.headers && typeof request.headers.get === "function") {
320
+ return request.headers.get(name) ?? void 0;
321
+ }
322
+ return void 0;
323
+ }
324
+ function parseQuery5(urlString) {
325
+ try {
326
+ const url = new URL(urlString);
327
+ const result = {};
328
+ for (const [key, value] of url.searchParams.entries()) {
329
+ result[key] = value;
330
+ }
331
+ return Object.keys(result).length > 0 ? result : void 0;
332
+ } catch {
333
+ return void 0;
334
+ }
335
+ }
336
+ function createHonoMiddleware(cipher) {
337
+ return async (c, next) => {
338
+ const start = Date.now();
339
+ await next();
340
+ const req = c.req ?? c.request;
341
+ const url = typeof req?.url === "string" ? req.url : String(req?.url ?? "");
342
+ const parsed = new URL(url, "http://localhost");
343
+ let status = 200;
344
+ const cres = c.res ?? c.response ?? void 0;
345
+ if (cres && typeof cres.status === "number") {
346
+ status = cres.status;
347
+ } else if (cres && typeof cres.statusCode === "number") {
348
+ status = cres.statusCode;
349
+ }
350
+ const xfwd = getHeader5(req, "x-forwarded-for");
351
+ const ip = xfwd ? String(xfwd).split(",")[0].trim() : getHeader5(req, "x-real-ip") ?? void 0;
352
+ cipher.logRequest({
353
+ method: req?.method,
354
+ path: parsed.pathname + parsed.search,
355
+ status,
356
+ duration: Date.now() - start,
357
+ ip,
358
+ userAgent: getHeader5(req, "user-agent"),
359
+ referer: getHeader5(req, "referer"),
360
+ protocol: parsed.protocol.replace(":", ""),
361
+ host: getHeader5(req, "host"),
362
+ query: parseQuery5(url),
363
+ requestId: getHeader5(req, "x-request-id")
364
+ });
365
+ return cres ?? new Response(null, { status });
366
+ };
367
+ }
368
+
227
369
  // src/core/create-cipher-logger.ts
228
370
  function createCipherLogger(config = {}) {
229
371
  const fields = resolveFieldConfig(config.fields);
@@ -242,6 +384,15 @@ function createCipherLogger(config = {}) {
242
384
  },
243
385
  next() {
244
386
  return createNextMiddleware(cipherLogger);
387
+ },
388
+ fastify() {
389
+ return createFastifyMiddleware(cipherLogger);
390
+ },
391
+ nest() {
392
+ return createNestMiddleware(cipherLogger);
393
+ },
394
+ hono() {
395
+ return createHonoMiddleware(cipherLogger);
245
396
  }
246
397
  };
247
398
  return cipherLogger;
@@ -254,6 +405,9 @@ var logger = new Logger();
254
405
  Logger,
255
406
  createCipherLogger,
256
407
  createExpressMiddleware,
408
+ createFastifyMiddleware,
409
+ createHonoMiddleware,
410
+ createNestMiddleware,
257
411
  createNextMiddleware,
258
412
  logger
259
413
  });
package/dist/index.mjs CHANGED
@@ -121,7 +121,7 @@ function buildRequestLog(fields, input) {
121
121
  return log;
122
122
  }
123
123
 
124
- // src/express/middleware.ts
124
+ // src/adapters/express/middleware.ts
125
125
  function parseQuery(query) {
126
126
  const result = {};
127
127
  for (const [key, value] of Object.entries(query)) {
@@ -161,7 +161,7 @@ function createExpressMiddleware(cipher) {
161
161
  };
162
162
  }
163
163
 
164
- // src/next/middleware.ts
164
+ // src/adapters/next/middleware.ts
165
165
  import { NextResponse } from "next/server";
166
166
  function getHeader2(request, name) {
167
167
  return request.headers.get(name) ?? void 0;
@@ -194,6 +194,145 @@ function createNextMiddleware(cipher) {
194
194
  };
195
195
  }
196
196
 
197
+ // src/adapters/fastify/middleware.ts
198
+ function getHeader3(req, name) {
199
+ const value = req.headers[name];
200
+ if (value === void 0) {
201
+ return void 0;
202
+ }
203
+ return Array.isArray(value) ? String(value[0]) : String(value);
204
+ }
205
+ function parseQuery3(req) {
206
+ try {
207
+ const raw = req.raw.url ?? req.url ?? "";
208
+ const url = new URL(raw, "http://localhost");
209
+ const result = {};
210
+ for (const [key, value] of url.searchParams.entries()) {
211
+ result[key] = value;
212
+ }
213
+ return Object.keys(result).length > 0 ? result : void 0;
214
+ } catch {
215
+ return void 0;
216
+ }
217
+ }
218
+ function createFastifyMiddleware(cipher) {
219
+ return (request, reply) => {
220
+ const start = Date.now();
221
+ reply.raw.once("finish", () => {
222
+ const protocol = request.headers["x-forwarded-proto"] || (request.raw.socket && request.raw.socket.encrypted ? "https" : "http");
223
+ cipher.logRequest({
224
+ method: request.method,
225
+ path: request.raw.url ?? request.url,
226
+ status: reply.statusCode,
227
+ duration: Date.now() - start,
228
+ ip: request.headers["x-forwarded-for"] ? request.headers["x-forwarded-for"].split(",")[0].trim() : request.ip || request.socket?.remoteAddress || request.raw.socket?.remoteAddress,
229
+ userAgent: getHeader3(request, "user-agent"),
230
+ referer: getHeader3(request, "referer"),
231
+ protocol,
232
+ host: getHeader3(request, "host"),
233
+ query: parseQuery3(request),
234
+ requestId: getHeader3(request, "x-request-id")
235
+ });
236
+ });
237
+ };
238
+ }
239
+
240
+ // src/adapters/nest/middleware.ts
241
+ function parseQuery4(query) {
242
+ const result = {};
243
+ for (const [key, value] of Object.entries(query)) {
244
+ if (value === void 0) {
245
+ continue;
246
+ }
247
+ result[key] = typeof value === "string" ? value : Array.isArray(value) ? String(value[0] ?? "") : String(value);
248
+ }
249
+ return Object.keys(result).length > 0 ? result : void 0;
250
+ }
251
+ function getHeader4(req, name) {
252
+ const value = req.headers[name];
253
+ if (value === void 0) {
254
+ return void 0;
255
+ }
256
+ return Array.isArray(value) ? value[0] : value;
257
+ }
258
+ function createNestMiddleware(cipher) {
259
+ return (req, res, next) => {
260
+ const start = Date.now();
261
+ res.on("finish", () => {
262
+ cipher.logRequest({
263
+ method: req.method,
264
+ path: req.originalUrl || req.url,
265
+ status: res.statusCode,
266
+ duration: Date.now() - start,
267
+ ip: req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip || req.socket.remoteAddress,
268
+ userAgent: getHeader4(req, "user-agent"),
269
+ referer: getHeader4(req, "referer"),
270
+ protocol: req.protocol,
271
+ host: getHeader4(req, "host"),
272
+ query: parseQuery4(req.query),
273
+ requestId: getHeader4(req, "x-request-id")
274
+ });
275
+ });
276
+ next();
277
+ };
278
+ }
279
+
280
+ // src/adapters/hono/middleware.ts
281
+ function getHeader5(request, name) {
282
+ if (!request) return void 0;
283
+ if (typeof request.header === "function") {
284
+ return request.header(name) ?? void 0;
285
+ }
286
+ if (request.headers && typeof request.headers.get === "function") {
287
+ return request.headers.get(name) ?? void 0;
288
+ }
289
+ return void 0;
290
+ }
291
+ function parseQuery5(urlString) {
292
+ try {
293
+ const url = new URL(urlString);
294
+ const result = {};
295
+ for (const [key, value] of url.searchParams.entries()) {
296
+ result[key] = value;
297
+ }
298
+ return Object.keys(result).length > 0 ? result : void 0;
299
+ } catch {
300
+ return void 0;
301
+ }
302
+ }
303
+ function createHonoMiddleware(cipher) {
304
+ return async (c, next) => {
305
+ const start = Date.now();
306
+ await next();
307
+ const req = c.req ?? c.request;
308
+ const url = typeof req?.url === "string" ? req.url : String(req?.url ?? "");
309
+ const parsed = new URL(url, "http://localhost");
310
+ let status = 200;
311
+ const cres = c.res ?? c.response ?? void 0;
312
+ if (cres && typeof cres.status === "number") {
313
+ status = cres.status;
314
+ } else if (cres && typeof cres.statusCode === "number") {
315
+ status = cres.statusCode;
316
+ }
317
+ const xfwd = getHeader5(req, "x-forwarded-for");
318
+ const ip = xfwd ? String(xfwd).split(",")[0].trim() : getHeader5(req, "x-real-ip") ?? void 0;
319
+ cipher.logRequest({
320
+ method: req?.method,
321
+ path: parsed.pathname + parsed.search,
322
+ status,
323
+ duration: Date.now() - start,
324
+ ip,
325
+ userAgent: getHeader5(req, "user-agent"),
326
+ referer: getHeader5(req, "referer"),
327
+ protocol: parsed.protocol.replace(":", ""),
328
+ host: getHeader5(req, "host"),
329
+ query: parseQuery5(url),
330
+ requestId: getHeader5(req, "x-request-id")
331
+ });
332
+ return cres ?? new Response(null, { status });
333
+ };
334
+ }
335
+
197
336
  // src/core/create-cipher-logger.ts
198
337
  function createCipherLogger(config = {}) {
199
338
  const fields = resolveFieldConfig(config.fields);
@@ -212,6 +351,15 @@ function createCipherLogger(config = {}) {
212
351
  },
213
352
  next() {
214
353
  return createNextMiddleware(cipherLogger);
354
+ },
355
+ fastify() {
356
+ return createFastifyMiddleware(cipherLogger);
357
+ },
358
+ nest() {
359
+ return createNestMiddleware(cipherLogger);
360
+ },
361
+ hono() {
362
+ return createHonoMiddleware(cipherLogger);
215
363
  }
216
364
  };
217
365
  return cipherLogger;
@@ -223,6 +371,9 @@ export {
223
371
  Logger,
224
372
  createCipherLogger,
225
373
  createExpressMiddleware,
374
+ createFastifyMiddleware,
375
+ createHonoMiddleware,
376
+ createNestMiddleware,
226
377
  createNextMiddleware,
227
378
  logger
228
379
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cipher-logger",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "See every request. Capture every error. Understand your application.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -43,6 +43,13 @@
43
43
  "nextjs-middleware",
44
44
  "express",
45
45
  "express-logger",
46
+ "fastify",
47
+ "fastify-logger",
48
+ "nest",
49
+ "nestjs",
50
+ "nestjs-logger",
51
+ "hono",
52
+ "hono-logger",
46
53
  "cli",
47
54
  "dashboard",
48
55
  "cipherlogger",
@@ -54,7 +61,11 @@
54
61
  "type": "commonjs",
55
62
  "peerDependencies": {
56
63
  "express": ">=4",
57
- "next": ">=13"
64
+ "next": ">=13",
65
+ "fastify": ">=4",
66
+ "hono": ">=1",
67
+ "@nestjs/common": ">=10",
68
+ "@nestjs/core": ">=10"
58
69
  },
59
70
  "peerDependenciesMeta": {
60
71
  "express": {
@@ -63,6 +74,19 @@
63
74
  "next": {
64
75
  "optional": true
65
76
  }
77
+ ,
78
+ "fastify": {
79
+ "optional": true
80
+ },
81
+ "hono": {
82
+ "optional": true
83
+ },
84
+ "@nestjs/common": {
85
+ "optional": true
86
+ },
87
+ "@nestjs/core": {
88
+ "optional": true
89
+ }
66
90
  },
67
91
  "devDependencies": {
68
92
  "@types/express": "^5.0.3",