cipher-logger 0.0.1 → 0.1.1

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,60 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Setup pnpm
21
+ uses: pnpm/action-setup@v4
22
+ with:
23
+ version: 10
24
+
25
+ - name: Setup Node.js
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 24
29
+ registry-url: https://registry.npmjs.org
30
+ cache: pnpm
31
+
32
+ - name: Update npm
33
+ run: npm install -g npm@latest
34
+
35
+ - name: Check versions
36
+ run: |
37
+ echo "Node: $(node --version)"
38
+ echo "npm: $(npm --version)"
39
+ echo "pnpm: $(pnpm --version)"
40
+
41
+ - name: Install dependencies
42
+ run: pnpm install --frozen-lockfile
43
+
44
+ - name: Build
45
+ run: pnpm run build
46
+
47
+ - name: Publish
48
+ run: |
49
+ NAME=$(node -p "require('./package.json').name")
50
+ VERSION=$(node -p "require('./package.json').version")
51
+
52
+ echo "Package: ${NAME}"
53
+ echo "Version: ${VERSION}"
54
+
55
+ if npm view "${NAME}@${VERSION}" version >/dev/null 2>&1; then
56
+ echo "Skipping publish: ${NAME}@${VERSION} already exists on npm"
57
+ exit 0
58
+ fi
59
+
60
+ npm publish --access public
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Cipher Logger
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,398 @@
1
+ # Cipher Logger
2
+
3
+ <p align="center">
4
+ <strong>See every request. Capture every error. Understand your application.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/cipher-logger"><img src="https://img.shields.io/npm/v/cipher-logger.svg" alt="npm version"></a>
9
+ <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>
10
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg" alt="node version"></a>
11
+ <a href="https://www.typescriptlang.org"><img src="https://img.shields.io/badge/TypeScript-ready-3178C6.svg" alt="typescript"></a>
12
+ </p>
13
+
14
+ ---
15
+
16
+ **Cipher Logger** is a lightweight, production-ready HTTP logging library for Node.js. It captures every request in **Express** and **Next.js** applications. You decide exactly which fields appear in each log — required fields are always recorded, optional fields are opt-in.
17
+
18
+ ## Table of Contents
19
+
20
+ - [Features](#features)
21
+ - [Installation](#installation)
22
+ - [Quick Start](#quick-start)
23
+ - [Configuration](#configuration)
24
+ - [Express](#express)
25
+ - [Next.js](#nextjs)
26
+ - [Log Schema](#log-schema)
27
+ - [API Reference](#api-reference)
28
+ - [Architecture](#architecture)
29
+ - [Local Development](#local-development)
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+
33
+ ---
34
+
35
+ ## Features
36
+
37
+ - **TypeScript-first** — full type coverage across the entire API
38
+ - **Configurable fields** — fine-grained control over optional log fields
39
+ - **Express middleware** — records real `status` and `duration` after the response finishes
40
+ - **Next.js middleware** — drop-in support for `middleware.ts`
41
+ - **Zero heavy dependencies** — only `express` or `next` as optional peer dependencies
42
+ - **Node.js 18+** compatible
43
+
44
+ ---
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ npm install cipher-logger
50
+ # or
51
+ pnpm add cipher-logger
52
+ # or
53
+ yarn add cipher-logger
54
+ ```
55
+
56
+ ### Peer Dependencies
57
+
58
+ Install the framework you use:
59
+
60
+ ```bash
61
+ # Express
62
+ npm install express
63
+
64
+ # Next.js
65
+ npm install next
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ ```typescript
73
+ import { createCipherLogger } from "cipher-logger";
74
+
75
+ const cipher = createCipherLogger({
76
+ fields: {
77
+ ip: true,
78
+ userAgent: true,
79
+ query: true,
80
+ },
81
+ level: "info",
82
+ });
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Configuration
88
+
89
+ ```typescript
90
+ import { createCipherLogger } from "cipher-logger";
91
+
92
+ const cipher = createCipherLogger({
93
+ // Optional fields — default: false (disabled)
94
+ fields: {
95
+ ip: true,
96
+ userAgent: true,
97
+ referer: false,
98
+ protocol: true,
99
+ host: true,
100
+ query: true,
101
+ requestId: true,
102
+ metadata: false,
103
+ },
104
+
105
+ // Log level: debug | info | warn | error
106
+ level: "info",
107
+
108
+ // Optional prefix in console output
109
+ prefix: "api",
110
+ });
111
+ ```
112
+
113
+ | Option | Type | Default | Description |
114
+ |--------|------|---------|-------------|
115
+ | `fields` | `Partial<Record<OptionalRequestField, boolean>>` | all `false` | Optional log fields to include |
116
+ | `level` | `"debug" \| "info" \| "warn" \| "error"` | `"debug"` | Minimum log level |
117
+ | `prefix` | `string` | — | Prefix in console output |
118
+
119
+ ---
120
+
121
+ ## Express
122
+
123
+ ```typescript
124
+ import express from "express";
125
+ import { createCipherLogger } from "cipher-logger";
126
+
127
+ const app = express();
128
+
129
+ const cipher = createCipherLogger({
130
+ fields: {
131
+ ip: true,
132
+ userAgent: true,
133
+ query: true,
134
+ requestId: true,
135
+ },
136
+ level: "info",
137
+ prefix: "express",
138
+ });
139
+
140
+ // Mount before your routes
141
+ app.use(cipher.express());
142
+
143
+ app.get("/users", (req, res) => {
144
+ res.json({ users: [] });
145
+ });
146
+
147
+ app.listen(3000, () => {
148
+ console.log("Server running on http://localhost:3000");
149
+ });
150
+ ```
151
+
152
+ ### Sample Output
153
+
154
+ ```text
155
+ [2026-09-01T20:00:00.000Z] [INFO] [express] HTTP Request {
156
+ id: 'a1b2c3d4-...',
157
+ type: 'http',
158
+ timestamp: '2026-09-01T20:00:00.000Z',
159
+ method: 'GET',
160
+ path: '/users?page=1',
161
+ status: 200,
162
+ duration: 12,
163
+ ip: '::1',
164
+ userAgent: 'Mozilla/5.0 ...',
165
+ query: { page: '1' }
166
+ }
167
+ ```
168
+
169
+ > In Express, `status` and `duration` are recorded after `res.finish` and reflect the actual response.
170
+
171
+ ---
172
+
173
+ ## Next.js
174
+
175
+ Create `middleware.ts` at the project root (or `src/middleware.ts`):
176
+
177
+ ```typescript
178
+ import { createCipherLogger } from "cipher-logger";
179
+ import type { NextRequest } from "next/server";
180
+
181
+ const cipher = createCipherLogger({
182
+ fields: {
183
+ ip: true,
184
+ userAgent: true,
185
+ host: true,
186
+ query: true,
187
+ },
188
+ level: "info",
189
+ });
190
+
191
+ export function middleware(request: NextRequest) {
192
+ return cipher.next()(request);
193
+ }
194
+
195
+ export const config = {
196
+ matcher: [
197
+ /*
198
+ * Match all routes except static files and images
199
+ */
200
+ "/((?!_next/static|_next/image|favicon.ico).*)",
201
+ ],
202
+ };
203
+ ```
204
+
205
+ Or more concisely:
206
+
207
+ ```typescript
208
+ import { createCipherLogger } from "cipher-logger";
209
+
210
+ const cipher = createCipherLogger({
211
+ fields: { ip: true, userAgent: true },
212
+ });
213
+
214
+ export default cipher.next();
215
+
216
+ export const config = {
217
+ matcher: ["/api/:path*", "/dashboard/:path*"],
218
+ };
219
+ ```
220
+
221
+ > **Note:** Next.js middleware runs before the route handler, so `status` and `duration` reflect the middleware execution, not the final route response. Route handler wrappers for more accurate logging are planned for future releases.
222
+
223
+ ---
224
+
225
+ ## Log Schema
226
+
227
+ ### Required Fields
228
+
229
+ Always included in every HTTP log:
230
+
231
+ | Field | Type | Description |
232
+ |-------|------|-------------|
233
+ | `id` | `string` | Unique identifier (UUID) |
234
+ | `type` | `"http"` | Log type |
235
+ | `timestamp` | `string` | ISO 8601 timestamp |
236
+ | `method` | `string` | HTTP method |
237
+ | `path` | `string` | Request path |
238
+ | `status` | `number` | HTTP status code |
239
+ | `duration` | `number` | Duration in milliseconds |
240
+
241
+ ### Optional Fields
242
+
243
+ Enabled via the `fields` config:
244
+
245
+ | Field | Type | Description |
246
+ |-------|------|-------------|
247
+ | `ip` | `string` | Client IP address |
248
+ | `userAgent` | `string` | User-Agent header |
249
+ | `referer` | `string` | Referer header |
250
+ | `protocol` | `string` | Protocol (`http` / `https`) |
251
+ | `host` | `string` | Host header |
252
+ | `query` | `Record<string, string>` | Query string parameters |
253
+ | `requestId` | `string` | From `x-request-id` header |
254
+ | `metadata` | `Record<string, unknown>` | Custom metadata |
255
+
256
+ ---
257
+
258
+ ## API Reference
259
+
260
+ ### `createCipherLogger(config?)`
261
+
262
+ Creates a Cipher Logger instance.
263
+
264
+ ```typescript
265
+ const cipher = createCipherLogger(config);
266
+ ```
267
+
268
+ **Methods:**
269
+
270
+ | Method | Description |
271
+ |--------|-------------|
272
+ | `cipher.logRequest(input)` | Manually log an HTTP request |
273
+ | `cipher.express()` | Returns Express middleware |
274
+ | `cipher.next()` | Returns Next.js middleware |
275
+
276
+ ### `Logger`
277
+
278
+ Base logging class (independent of HTTP):
279
+
280
+ ```typescript
281
+ import { Logger } from "cipher-logger";
282
+
283
+ const logger = new Logger({ level: "info", prefix: "app" });
284
+
285
+ logger.info("Server started");
286
+ logger.warn("Deprecated API used", { route: "/old" });
287
+ logger.error("Unhandled error", { err: "..." });
288
+ logger.debug("Debug info");
289
+ ```
290
+
291
+ ### TypeScript Exports
292
+
293
+ ```typescript
294
+ import type {
295
+ CipherLogger,
296
+ CipherLoggerConfig,
297
+ RequestLog,
298
+ RequestLogInput,
299
+ OptionalRequestField,
300
+ LogLevel,
301
+ LoggerOptions,
302
+ ExpressMiddleware,
303
+ NextMiddleware,
304
+ } from "cipher-logger";
305
+ ```
306
+
307
+ ---
308
+
309
+ ## Architecture
310
+
311
+ ```
312
+ cipher-logger/
313
+ ├── src/
314
+ │ ├── core/ # Core — field config & log building
315
+ │ │ ├── logger.ts
316
+ │ │ ├── types.ts
317
+ │ │ ├── build-request-log.ts
318
+ │ │ └── create-cipher-logger.ts
319
+ │ ├── express/ # Express adapter
320
+ │ │ └── middleware.ts
321
+ │ ├── next/ # Next.js adapter
322
+ │ │ └── middleware.ts
323
+ │ └── index.ts # Public entry point
324
+ ```
325
+
326
+ ```
327
+ ┌─────────────────────────────────────────┐
328
+ │ Core │
329
+ │ fields config → buildRequestLog │
330
+ └──────────────────┬──────────────────────┘
331
+
332
+ ┌─────────┴─────────┐
333
+ ▼ ▼
334
+ ┌───────────┐ ┌───────────┐
335
+ │ Express │ │ Next.js │
336
+ │ middleware│ │ middleware│
337
+ └───────────┘ └───────────┘
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Local Development
343
+
344
+ ```bash
345
+ git clone https://github.com/cipherunits/CipherLogger.git
346
+ cd CipherLogger
347
+ pnpm install
348
+ pnpm run build
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Contributing
354
+
355
+ Contributions are welcome and appreciated.
356
+
357
+ ### Report a Bug or Request a Feature
358
+
359
+ 1. Check [Issues](https://github.com/cipherunits/CipherLogger/issues) first
360
+ 2. If no existing issue matches, open a new one with a clear description
361
+
362
+ ### Submit a Pull Request
363
+
364
+ 1. Fork the repository
365
+ 2. Create a branch: `git checkout -b feature/my-feature`
366
+ 3. Commit your changes: `git commit -m "feat: add something useful"`
367
+ 4. Push the branch: `git push origin feature/my-feature`
368
+ 5. Open a Pull Request
369
+
370
+ ### Guidelines
371
+
372
+ - Use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages
373
+ - Run `pnpm run build` before submitting a PR
374
+ - Keep changes focused and scoped
375
+ - Update the README for any API changes
376
+
377
+ ### Contact
378
+
379
+ - **GitHub Issues:** [cipherunits/CipherLogger/issues](https://github.com/cipherunits/CipherLogger/issues)
380
+ - **Organization:** [CipherUnits](https://github.com/cipherunits)
381
+
382
+ ---
383
+
384
+ ## License
385
+
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
+ ---
395
+
396
+ <p align="center">
397
+ Built with ❤️ by <a href="https://github.com/cipherunits">CipherUnits</a>
398
+ </p>
@@ -0,0 +1,77 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+
4
+ type LogLevel = "info" | "warn" | "error" | "debug";
5
+ type LogMeta = Record<string, unknown>;
6
+ interface LoggerOptions {
7
+ level?: LogLevel;
8
+ prefix?: string;
9
+ }
10
+ declare class Logger {
11
+ private readonly level;
12
+ private readonly prefix?;
13
+ constructor(options?: LoggerOptions);
14
+ private shouldLog;
15
+ private format;
16
+ private log;
17
+ info(message: string, meta?: LogMeta): void;
18
+ warn(message: string, meta?: LogMeta): void;
19
+ error(message: string, meta?: LogMeta): void;
20
+ debug(message: string, meta?: LogMeta): void;
21
+ }
22
+
23
+ declare const OPTIONAL_REQUEST_FIELDS: readonly ["ip", "userAgent", "referer", "protocol", "host", "query", "requestId", "metadata"];
24
+ type OptionalRequestField = (typeof OPTIONAL_REQUEST_FIELDS)[number];
25
+ type RequestLog = {
26
+ id: string;
27
+ type: "http";
28
+ timestamp: string;
29
+ method: string;
30
+ path: string;
31
+ status: number;
32
+ duration: number;
33
+ ip?: string;
34
+ userAgent?: string;
35
+ referer?: string;
36
+ protocol?: string;
37
+ host?: string;
38
+ query?: Record<string, string>;
39
+ requestId?: string;
40
+ metadata?: Record<string, unknown>;
41
+ };
42
+ type RequestLogInput = {
43
+ method: string;
44
+ path: string;
45
+ status: number;
46
+ duration: number;
47
+ ip?: string;
48
+ userAgent?: string;
49
+ referer?: string;
50
+ protocol?: string;
51
+ host?: string;
52
+ query?: Record<string, string>;
53
+ requestId?: string;
54
+ metadata?: Record<string, unknown>;
55
+ };
56
+ type CipherLoggerConfig = {
57
+ fields?: Partial<Record<OptionalRequestField, boolean>>;
58
+ level?: LogLevel;
59
+ prefix?: string;
60
+ };
61
+
62
+ type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
63
+ declare function createExpressMiddleware(cipher: CipherLogger): ExpressMiddleware;
64
+
65
+ type NextMiddleware = (request: NextRequest) => NextResponse | Promise<NextResponse>;
66
+ declare function createNextMiddleware(cipher: CipherLogger): NextMiddleware;
67
+
68
+ interface CipherLogger {
69
+ logRequest(input: RequestLogInput): RequestLog;
70
+ express(): ExpressMiddleware;
71
+ next(): NextMiddleware;
72
+ }
73
+ declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
74
+
75
+ declare const logger: Logger;
76
+
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 };
@@ -0,0 +1,77 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+
4
+ type LogLevel = "info" | "warn" | "error" | "debug";
5
+ type LogMeta = Record<string, unknown>;
6
+ interface LoggerOptions {
7
+ level?: LogLevel;
8
+ prefix?: string;
9
+ }
10
+ declare class Logger {
11
+ private readonly level;
12
+ private readonly prefix?;
13
+ constructor(options?: LoggerOptions);
14
+ private shouldLog;
15
+ private format;
16
+ private log;
17
+ info(message: string, meta?: LogMeta): void;
18
+ warn(message: string, meta?: LogMeta): void;
19
+ error(message: string, meta?: LogMeta): void;
20
+ debug(message: string, meta?: LogMeta): void;
21
+ }
22
+
23
+ declare const OPTIONAL_REQUEST_FIELDS: readonly ["ip", "userAgent", "referer", "protocol", "host", "query", "requestId", "metadata"];
24
+ type OptionalRequestField = (typeof OPTIONAL_REQUEST_FIELDS)[number];
25
+ type RequestLog = {
26
+ id: string;
27
+ type: "http";
28
+ timestamp: string;
29
+ method: string;
30
+ path: string;
31
+ status: number;
32
+ duration: number;
33
+ ip?: string;
34
+ userAgent?: string;
35
+ referer?: string;
36
+ protocol?: string;
37
+ host?: string;
38
+ query?: Record<string, string>;
39
+ requestId?: string;
40
+ metadata?: Record<string, unknown>;
41
+ };
42
+ type RequestLogInput = {
43
+ method: string;
44
+ path: string;
45
+ status: number;
46
+ duration: number;
47
+ ip?: string;
48
+ userAgent?: string;
49
+ referer?: string;
50
+ protocol?: string;
51
+ host?: string;
52
+ query?: Record<string, string>;
53
+ requestId?: string;
54
+ metadata?: Record<string, unknown>;
55
+ };
56
+ type CipherLoggerConfig = {
57
+ fields?: Partial<Record<OptionalRequestField, boolean>>;
58
+ level?: LogLevel;
59
+ prefix?: string;
60
+ };
61
+
62
+ type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void;
63
+ declare function createExpressMiddleware(cipher: CipherLogger): ExpressMiddleware;
64
+
65
+ type NextMiddleware = (request: NextRequest) => NextResponse | Promise<NextResponse>;
66
+ declare function createNextMiddleware(cipher: CipherLogger): NextMiddleware;
67
+
68
+ interface CipherLogger {
69
+ logRequest(input: RequestLogInput): RequestLog;
70
+ express(): ExpressMiddleware;
71
+ next(): NextMiddleware;
72
+ }
73
+ declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
74
+
75
+ declare const logger: Logger;
76
+
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 };
package/dist/index.js ADDED
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ Logger: () => Logger,
24
+ createCipherLogger: () => createCipherLogger,
25
+ createExpressMiddleware: () => createExpressMiddleware,
26
+ createNextMiddleware: () => createNextMiddleware,
27
+ logger: () => logger
28
+ });
29
+ module.exports = __toCommonJS(src_exports);
30
+
31
+ // src/core/logger.ts
32
+ var LEVEL_PRIORITY = {
33
+ debug: 0,
34
+ info: 1,
35
+ warn: 2,
36
+ error: 3
37
+ };
38
+ var Logger = class {
39
+ level;
40
+ prefix;
41
+ constructor(options = {}) {
42
+ this.level = options.level ?? "debug";
43
+ this.prefix = options.prefix;
44
+ }
45
+ shouldLog(level) {
46
+ return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.level];
47
+ }
48
+ format(level, message) {
49
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
50
+ const prefix = this.prefix ? ` [${this.prefix}]` : "";
51
+ return `[${timestamp}] [${level.toUpperCase()}]${prefix} ${message}`;
52
+ }
53
+ log(level, message, meta) {
54
+ if (!this.shouldLog(level)) {
55
+ return;
56
+ }
57
+ const output = this.format(level, message);
58
+ switch (level) {
59
+ case "error":
60
+ meta ? console.error(output, meta) : console.error(output);
61
+ break;
62
+ case "warn":
63
+ meta ? console.warn(output, meta) : console.warn(output);
64
+ break;
65
+ case "debug":
66
+ meta ? console.debug(output, meta) : console.debug(output);
67
+ break;
68
+ case "info":
69
+ meta ? console.info(output, meta) : console.info(output);
70
+ break;
71
+ }
72
+ }
73
+ info(message, meta) {
74
+ this.log("info", message, meta);
75
+ }
76
+ warn(message, meta) {
77
+ this.log("warn", message, meta);
78
+ }
79
+ error(message, meta) {
80
+ this.log("error", message, meta);
81
+ }
82
+ debug(message, meta) {
83
+ this.log("debug", message, meta);
84
+ }
85
+ };
86
+
87
+ // src/core/build-request-log.ts
88
+ var import_node_crypto = require("crypto");
89
+
90
+ // src/core/types.ts
91
+ var OPTIONAL_REQUEST_FIELDS = [
92
+ "ip",
93
+ "userAgent",
94
+ "referer",
95
+ "protocol",
96
+ "host",
97
+ "query",
98
+ "requestId",
99
+ "metadata"
100
+ ];
101
+
102
+ // src/core/build-request-log.ts
103
+ function resolveFieldConfig(fields) {
104
+ const resolved = {};
105
+ for (const field of OPTIONAL_REQUEST_FIELDS) {
106
+ resolved[field] = fields?.[field] ?? false;
107
+ }
108
+ return resolved;
109
+ }
110
+ function buildRequestLog(fields, input) {
111
+ const log = {
112
+ id: (0, import_node_crypto.randomUUID)(),
113
+ type: "http",
114
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
115
+ method: input.method,
116
+ path: input.path,
117
+ status: input.status,
118
+ duration: input.duration
119
+ };
120
+ for (const field of OPTIONAL_REQUEST_FIELDS) {
121
+ if (!fields[field] || input[field] === void 0) {
122
+ continue;
123
+ }
124
+ switch (field) {
125
+ case "ip":
126
+ log.ip = input.ip;
127
+ break;
128
+ case "userAgent":
129
+ log.userAgent = input.userAgent;
130
+ break;
131
+ case "referer":
132
+ log.referer = input.referer;
133
+ break;
134
+ case "protocol":
135
+ log.protocol = input.protocol;
136
+ break;
137
+ case "host":
138
+ log.host = input.host;
139
+ break;
140
+ case "query":
141
+ log.query = input.query;
142
+ break;
143
+ case "requestId":
144
+ log.requestId = input.requestId;
145
+ break;
146
+ case "metadata":
147
+ log.metadata = input.metadata;
148
+ break;
149
+ }
150
+ }
151
+ return log;
152
+ }
153
+
154
+ // src/express/middleware.ts
155
+ function parseQuery(query) {
156
+ const result = {};
157
+ for (const [key, value] of Object.entries(query)) {
158
+ if (value === void 0) {
159
+ continue;
160
+ }
161
+ result[key] = typeof value === "string" ? value : Array.isArray(value) ? String(value[0] ?? "") : String(value);
162
+ }
163
+ return Object.keys(result).length > 0 ? result : void 0;
164
+ }
165
+ function getHeader(req, name) {
166
+ const value = req.headers[name];
167
+ if (value === void 0) {
168
+ return void 0;
169
+ }
170
+ return Array.isArray(value) ? value[0] : value;
171
+ }
172
+ function createExpressMiddleware(cipher) {
173
+ return (req, res, next) => {
174
+ const start = Date.now();
175
+ res.on("finish", () => {
176
+ cipher.logRequest({
177
+ method: req.method,
178
+ path: req.originalUrl || req.url,
179
+ status: res.statusCode,
180
+ duration: Date.now() - start,
181
+ ip: req.ip || req.socket.remoteAddress,
182
+ userAgent: getHeader(req, "user-agent"),
183
+ referer: getHeader(req, "referer"),
184
+ protocol: req.protocol,
185
+ host: getHeader(req, "host"),
186
+ query: parseQuery(req.query),
187
+ requestId: getHeader(req, "x-request-id")
188
+ });
189
+ });
190
+ next();
191
+ };
192
+ }
193
+
194
+ // src/next/middleware.ts
195
+ var import_server = require("next/server");
196
+ function getHeader2(request, name) {
197
+ return request.headers.get(name) ?? void 0;
198
+ }
199
+ function parseQuery2(request) {
200
+ const result = {};
201
+ for (const [key, value] of request.nextUrl.searchParams.entries()) {
202
+ result[key] = value;
203
+ }
204
+ return Object.keys(result).length > 0 ? result : void 0;
205
+ }
206
+ function createNextMiddleware(cipher) {
207
+ return (request) => {
208
+ const start = Date.now();
209
+ const response = import_server.NextResponse.next();
210
+ cipher.logRequest({
211
+ method: request.method,
212
+ path: request.nextUrl.pathname + request.nextUrl.search,
213
+ status: response.status,
214
+ duration: Date.now() - start,
215
+ ip: getHeader2(request, "x-forwarded-for")?.split(",")[0]?.trim() || getHeader2(request, "x-real-ip"),
216
+ userAgent: getHeader2(request, "user-agent"),
217
+ referer: getHeader2(request, "referer"),
218
+ protocol: request.nextUrl.protocol.replace(":", ""),
219
+ host: getHeader2(request, "host"),
220
+ query: parseQuery2(request),
221
+ requestId: getHeader2(request, "x-request-id")
222
+ });
223
+ return response;
224
+ };
225
+ }
226
+
227
+ // src/core/create-cipher-logger.ts
228
+ function createCipherLogger(config = {}) {
229
+ const fields = resolveFieldConfig(config.fields);
230
+ const logger2 = new Logger({
231
+ level: config.level,
232
+ prefix: config.prefix
233
+ });
234
+ const cipherLogger = {
235
+ logRequest(input) {
236
+ const log = buildRequestLog(fields, input);
237
+ logger2.info("HTTP Request", log);
238
+ return log;
239
+ },
240
+ express() {
241
+ return createExpressMiddleware(cipherLogger);
242
+ },
243
+ next() {
244
+ return createNextMiddleware(cipherLogger);
245
+ }
246
+ };
247
+ return cipherLogger;
248
+ }
249
+
250
+ // src/index.ts
251
+ var logger = new Logger();
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ Logger,
255
+ createCipherLogger,
256
+ createExpressMiddleware,
257
+ createNextMiddleware,
258
+ logger
259
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,228 @@
1
+ // src/core/logger.ts
2
+ var LEVEL_PRIORITY = {
3
+ debug: 0,
4
+ info: 1,
5
+ warn: 2,
6
+ error: 3
7
+ };
8
+ var Logger = class {
9
+ level;
10
+ prefix;
11
+ constructor(options = {}) {
12
+ this.level = options.level ?? "debug";
13
+ this.prefix = options.prefix;
14
+ }
15
+ shouldLog(level) {
16
+ return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.level];
17
+ }
18
+ format(level, message) {
19
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
20
+ const prefix = this.prefix ? ` [${this.prefix}]` : "";
21
+ return `[${timestamp}] [${level.toUpperCase()}]${prefix} ${message}`;
22
+ }
23
+ log(level, message, meta) {
24
+ if (!this.shouldLog(level)) {
25
+ return;
26
+ }
27
+ const output = this.format(level, message);
28
+ switch (level) {
29
+ case "error":
30
+ meta ? console.error(output, meta) : console.error(output);
31
+ break;
32
+ case "warn":
33
+ meta ? console.warn(output, meta) : console.warn(output);
34
+ break;
35
+ case "debug":
36
+ meta ? console.debug(output, meta) : console.debug(output);
37
+ break;
38
+ case "info":
39
+ meta ? console.info(output, meta) : console.info(output);
40
+ break;
41
+ }
42
+ }
43
+ info(message, meta) {
44
+ this.log("info", message, meta);
45
+ }
46
+ warn(message, meta) {
47
+ this.log("warn", message, meta);
48
+ }
49
+ error(message, meta) {
50
+ this.log("error", message, meta);
51
+ }
52
+ debug(message, meta) {
53
+ this.log("debug", message, meta);
54
+ }
55
+ };
56
+
57
+ // src/core/build-request-log.ts
58
+ import { randomUUID } from "crypto";
59
+
60
+ // src/core/types.ts
61
+ var OPTIONAL_REQUEST_FIELDS = [
62
+ "ip",
63
+ "userAgent",
64
+ "referer",
65
+ "protocol",
66
+ "host",
67
+ "query",
68
+ "requestId",
69
+ "metadata"
70
+ ];
71
+
72
+ // src/core/build-request-log.ts
73
+ function resolveFieldConfig(fields) {
74
+ const resolved = {};
75
+ for (const field of OPTIONAL_REQUEST_FIELDS) {
76
+ resolved[field] = fields?.[field] ?? false;
77
+ }
78
+ return resolved;
79
+ }
80
+ function buildRequestLog(fields, input) {
81
+ const log = {
82
+ id: randomUUID(),
83
+ type: "http",
84
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
85
+ method: input.method,
86
+ path: input.path,
87
+ status: input.status,
88
+ duration: input.duration
89
+ };
90
+ for (const field of OPTIONAL_REQUEST_FIELDS) {
91
+ if (!fields[field] || input[field] === void 0) {
92
+ continue;
93
+ }
94
+ switch (field) {
95
+ case "ip":
96
+ log.ip = input.ip;
97
+ break;
98
+ case "userAgent":
99
+ log.userAgent = input.userAgent;
100
+ break;
101
+ case "referer":
102
+ log.referer = input.referer;
103
+ break;
104
+ case "protocol":
105
+ log.protocol = input.protocol;
106
+ break;
107
+ case "host":
108
+ log.host = input.host;
109
+ break;
110
+ case "query":
111
+ log.query = input.query;
112
+ break;
113
+ case "requestId":
114
+ log.requestId = input.requestId;
115
+ break;
116
+ case "metadata":
117
+ log.metadata = input.metadata;
118
+ break;
119
+ }
120
+ }
121
+ return log;
122
+ }
123
+
124
+ // src/express/middleware.ts
125
+ function parseQuery(query) {
126
+ const result = {};
127
+ for (const [key, value] of Object.entries(query)) {
128
+ if (value === void 0) {
129
+ continue;
130
+ }
131
+ result[key] = typeof value === "string" ? value : Array.isArray(value) ? String(value[0] ?? "") : String(value);
132
+ }
133
+ return Object.keys(result).length > 0 ? result : void 0;
134
+ }
135
+ function getHeader(req, name) {
136
+ const value = req.headers[name];
137
+ if (value === void 0) {
138
+ return void 0;
139
+ }
140
+ return Array.isArray(value) ? value[0] : value;
141
+ }
142
+ function createExpressMiddleware(cipher) {
143
+ return (req, res, next) => {
144
+ const start = Date.now();
145
+ res.on("finish", () => {
146
+ cipher.logRequest({
147
+ method: req.method,
148
+ path: req.originalUrl || req.url,
149
+ status: res.statusCode,
150
+ duration: Date.now() - start,
151
+ ip: req.ip || req.socket.remoteAddress,
152
+ userAgent: getHeader(req, "user-agent"),
153
+ referer: getHeader(req, "referer"),
154
+ protocol: req.protocol,
155
+ host: getHeader(req, "host"),
156
+ query: parseQuery(req.query),
157
+ requestId: getHeader(req, "x-request-id")
158
+ });
159
+ });
160
+ next();
161
+ };
162
+ }
163
+
164
+ // src/next/middleware.ts
165
+ import { NextResponse } from "next/server";
166
+ function getHeader2(request, name) {
167
+ return request.headers.get(name) ?? void 0;
168
+ }
169
+ function parseQuery2(request) {
170
+ const result = {};
171
+ for (const [key, value] of request.nextUrl.searchParams.entries()) {
172
+ result[key] = value;
173
+ }
174
+ return Object.keys(result).length > 0 ? result : void 0;
175
+ }
176
+ function createNextMiddleware(cipher) {
177
+ return (request) => {
178
+ const start = Date.now();
179
+ const response = NextResponse.next();
180
+ cipher.logRequest({
181
+ method: request.method,
182
+ path: request.nextUrl.pathname + request.nextUrl.search,
183
+ status: response.status,
184
+ duration: Date.now() - start,
185
+ ip: getHeader2(request, "x-forwarded-for")?.split(",")[0]?.trim() || getHeader2(request, "x-real-ip"),
186
+ userAgent: getHeader2(request, "user-agent"),
187
+ referer: getHeader2(request, "referer"),
188
+ protocol: request.nextUrl.protocol.replace(":", ""),
189
+ host: getHeader2(request, "host"),
190
+ query: parseQuery2(request),
191
+ requestId: getHeader2(request, "x-request-id")
192
+ });
193
+ return response;
194
+ };
195
+ }
196
+
197
+ // src/core/create-cipher-logger.ts
198
+ function createCipherLogger(config = {}) {
199
+ const fields = resolveFieldConfig(config.fields);
200
+ const logger2 = new Logger({
201
+ level: config.level,
202
+ prefix: config.prefix
203
+ });
204
+ const cipherLogger = {
205
+ logRequest(input) {
206
+ const log = buildRequestLog(fields, input);
207
+ logger2.info("HTTP Request", log);
208
+ return log;
209
+ },
210
+ express() {
211
+ return createExpressMiddleware(cipherLogger);
212
+ },
213
+ next() {
214
+ return createNextMiddleware(cipherLogger);
215
+ }
216
+ };
217
+ return cipherLogger;
218
+ }
219
+
220
+ // src/index.ts
221
+ var logger = new Logger();
222
+ export {
223
+ Logger,
224
+ createCipherLogger,
225
+ createExpressMiddleware,
226
+ createNextMiddleware,
227
+ logger
228
+ };
package/package.json CHANGED
@@ -1,12 +1,75 @@
1
1
  {
2
2
  "name": "cipher-logger",
3
- "version": "0.0.1",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "0.1.1",
4
+ "description": "See every request. Capture every error. Understand your application.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
6
8
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
9
+ "build": "tsup",
10
+ "prepublishOnly": "npm run build"
8
11
  },
9
- "keywords": [],
10
- "author": "",
11
- "license": "ISC"
12
- }
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/cipherunits/CipherLogger.git"
15
+ },
16
+ "author": "CipherUnits",
17
+ "license": "BSD-3-Clause",
18
+ "bugs": {
19
+ "url": "https://github.com/cipherunits/CipherLogger/issues"
20
+ },
21
+ "homepage": "https://github.com/cipherunits/CipherLogger#readme",
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "keywords": [
26
+ "logger",
27
+ "logging",
28
+ "log",
29
+ "logging-library",
30
+ "node-logger",
31
+ "nodejs-logger",
32
+ "javascript-logger",
33
+ "typescript-logger",
34
+ "http-logger",
35
+ "request-logger",
36
+ "error-logger",
37
+ "request-logging",
38
+ "error-logging",
39
+ "monitoring",
40
+ "observability",
41
+ "nextjs",
42
+ "nextjs-logger",
43
+ "nextjs-middleware",
44
+ "express",
45
+ "express-logger",
46
+ "cli",
47
+ "dashboard",
48
+ "cipherlogger",
49
+ "cipherunits"
50
+ ],
51
+ "engines": {
52
+ "node": ">=18"
53
+ },
54
+ "type": "commonjs",
55
+ "peerDependencies": {
56
+ "express": ">=4",
57
+ "next": ">=13"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "express": {
61
+ "optional": true
62
+ },
63
+ "next": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@types/express": "^5.0.3",
69
+ "@types/node": "^26.1.2",
70
+ "express": "^5.1.0",
71
+ "next": "^15.5.2",
72
+ "tsup": "^8.5.1",
73
+ "typescript": "^6.0.3"
74
+ }
75
+ }