cipher-logger 1.1.0 → 1.3.0
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.
- package/.editorconfig +19 -0
- package/.gitattributes +0 -0
- package/.github/workflows/pre-publish.yml +36 -0
- package/.github/workflows/publish.yml +6 -6
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +4 -0
- package/.prettierignore +11 -0
- package/README.md +15 -6
- package/commitlint.config.mjs +26 -0
- package/dist/index.d.mts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +148 -2
- package/dist/index.mjs +145 -2
- package/eslint.config.mjs +69 -0
- package/package.json +33 -5
- package/prettier.config.mjs +18 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# http://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
charset = utf-8
|
|
6
|
+
end_of_line = lf
|
|
7
|
+
insert_final_newline = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 2
|
|
10
|
+
trim_trailing_whitespace = true
|
|
11
|
+
|
|
12
|
+
[*.md]
|
|
13
|
+
trim_trailing_whitespace = false
|
|
14
|
+
|
|
15
|
+
[Makefile]
|
|
16
|
+
indent_style = tab
|
|
17
|
+
|
|
18
|
+
[*.{yml,yaml}]
|
|
19
|
+
indent_size = 2
|
package/.gitattributes
ADDED
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Pre-publish validation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
validate:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup pnpm
|
|
19
|
+
uses: pnpm/action-setup@v4
|
|
20
|
+
with:
|
|
21
|
+
version: 10
|
|
22
|
+
|
|
23
|
+
- name: Setup Node.js
|
|
24
|
+
uses: actions/setup-node@v4
|
|
25
|
+
with:
|
|
26
|
+
node-version: 24
|
|
27
|
+
cache: pnpm
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: pnpm install --frozen-lockfile
|
|
31
|
+
|
|
32
|
+
- name: Run validation suite
|
|
33
|
+
run: pnpm run test
|
|
34
|
+
|
|
35
|
+
- name: Dry run package publish
|
|
36
|
+
run: npm pack --dry-run
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
name: Publish to npm
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["Pre-publish validation"]
|
|
6
|
+
types: [completed]
|
|
7
7
|
|
|
8
8
|
permissions:
|
|
9
9
|
contents: read
|
|
@@ -11,11 +11,14 @@ permissions:
|
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
13
13
|
publish:
|
|
14
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
14
15
|
runs-on: ubuntu-latest
|
|
15
16
|
|
|
16
17
|
steps:
|
|
17
18
|
- name: Checkout
|
|
18
19
|
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
19
22
|
|
|
20
23
|
- name: Setup pnpm
|
|
21
24
|
uses: pnpm/action-setup@v4
|
|
@@ -41,9 +44,6 @@ jobs:
|
|
|
41
44
|
- name: Install dependencies
|
|
42
45
|
run: pnpm install --frozen-lockfile
|
|
43
46
|
|
|
44
|
-
- name: Build
|
|
45
|
-
run: pnpm run build
|
|
46
|
-
|
|
47
47
|
- name: Publish
|
|
48
48
|
run: |
|
|
49
49
|
NAME=$(node -p "require('./package.json').name")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pnpm exec commitlint --edit "$1"
|
package/.prettierignore
ADDED
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
**Cipher Logger** is a lightweight, production-ready HTTP logging library for Node.js. It captures every request in **Express
|
|
26
|
+
**Cipher Logger** is a lightweight, production-ready HTTP logging library for Node.js. It captures every request in **Express**, **Next.js**, **React**, **Vue**, and **Nuxt** applications. You decide exactly which fields appear in each log — required fields are always recorded, optional fields are opt-in.
|
|
27
27
|
|
|
28
28
|
## Table of Contents
|
|
29
29
|
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
- **Configurable fields** — fine-grained control over optional log fields
|
|
49
49
|
- **Express middleware** — records real `status` and `duration` after the response finishes
|
|
50
50
|
- **Next.js middleware** — drop-in support for `middleware.ts`
|
|
51
|
-
- **
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
- **React**, **Vue**, and **Nuxt** support — modern app stacks alongside the server frameworks
|
|
52
|
+
- **Zero heavy dependencies** — only framework peers are optional
|
|
53
|
+
- **Fastify middleware** — lightweight hook-compatible middleware for Fastify
|
|
54
|
+
- **NestJS middleware** — Express-compatible middleware for Nest apps
|
|
55
|
+
- **Hono middleware** — edge-friendly middleware for Hono apps
|
|
56
56
|
- **Node.js 18+** compatible
|
|
57
57
|
|
|
58
58
|
---
|
|
@@ -78,6 +78,15 @@ npm install express
|
|
|
78
78
|
# Next.js
|
|
79
79
|
npm install next
|
|
80
80
|
|
|
81
|
+
# React
|
|
82
|
+
npm install react
|
|
83
|
+
|
|
84
|
+
# Vue
|
|
85
|
+
npm install vue
|
|
86
|
+
|
|
87
|
+
# Nuxt
|
|
88
|
+
npm install nuxt
|
|
89
|
+
|
|
81
90
|
# Fastify
|
|
82
91
|
npm install fastify
|
|
83
92
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** @type {import('@commitlint/types').UserConfig} */
|
|
2
|
+
export default {
|
|
3
|
+
extends: ['@commitlint/config-conventional'],
|
|
4
|
+
rules: {
|
|
5
|
+
'type-enum': [
|
|
6
|
+
2,
|
|
7
|
+
'always',
|
|
8
|
+
[
|
|
9
|
+
'feat',
|
|
10
|
+
'fix',
|
|
11
|
+
'docs',
|
|
12
|
+
'i18n',
|
|
13
|
+
'style',
|
|
14
|
+
'refactor',
|
|
15
|
+
'perf',
|
|
16
|
+
'test',
|
|
17
|
+
'build',
|
|
18
|
+
'ci',
|
|
19
|
+
'chore',
|
|
20
|
+
'revert',
|
|
21
|
+
],
|
|
22
|
+
],
|
|
23
|
+
'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']],
|
|
24
|
+
'header-max-length': [2, 'always', 100],
|
|
25
|
+
},
|
|
26
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -76,10 +76,49 @@ declare function createNestMiddleware(cipher: CipherLogger): NestMiddleware;
|
|
|
76
76
|
type HonoMiddleware = (c: Context, next: () => Promise<void>) => Response | Promise<Response>;
|
|
77
77
|
declare function createHonoMiddleware(cipher: CipherLogger): HonoMiddleware;
|
|
78
78
|
|
|
79
|
+
type ReactRequest = {
|
|
80
|
+
method: string;
|
|
81
|
+
url: string;
|
|
82
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
83
|
+
};
|
|
84
|
+
type ReactResponse = {
|
|
85
|
+
status?: number;
|
|
86
|
+
end: (...args: unknown[]) => unknown;
|
|
87
|
+
};
|
|
88
|
+
type ReactMiddleware = (req: ReactRequest, res: ReactResponse) => void;
|
|
89
|
+
declare function createReactMiddleware(cipher: CipherLogger): ReactMiddleware;
|
|
90
|
+
|
|
91
|
+
type VueRequest = {
|
|
92
|
+
method: string;
|
|
93
|
+
url: string;
|
|
94
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
95
|
+
};
|
|
96
|
+
type VueResponse = {
|
|
97
|
+
status?: number;
|
|
98
|
+
end: (...args: unknown[]) => unknown;
|
|
99
|
+
};
|
|
100
|
+
type VueMiddleware = (req: VueRequest, res: VueResponse) => void;
|
|
101
|
+
declare function createVueMiddleware(cipher: CipherLogger): VueMiddleware;
|
|
102
|
+
|
|
103
|
+
type NuxtRequest = {
|
|
104
|
+
method: string;
|
|
105
|
+
url: string;
|
|
106
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
107
|
+
};
|
|
108
|
+
type NuxtResponse = {
|
|
109
|
+
status?: number;
|
|
110
|
+
end: (...args: unknown[]) => unknown;
|
|
111
|
+
};
|
|
112
|
+
type NuxtMiddleware = (req: NuxtRequest, res: NuxtResponse) => void;
|
|
113
|
+
declare function createNuxtMiddleware(cipher: CipherLogger): NuxtMiddleware;
|
|
114
|
+
|
|
79
115
|
interface CipherLogger {
|
|
80
116
|
logRequest(input: RequestLogInput): RequestLog;
|
|
81
117
|
express(): ExpressMiddleware;
|
|
82
118
|
next(): NextMiddleware;
|
|
119
|
+
react(): ReactMiddleware;
|
|
120
|
+
vue(): VueMiddleware;
|
|
121
|
+
nuxt(): NuxtMiddleware;
|
|
83
122
|
fastify(): FastifyMiddleware;
|
|
84
123
|
nest(): NestMiddleware;
|
|
85
124
|
hono(): HonoMiddleware;
|
|
@@ -88,4 +127,4 @@ declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
|
|
|
88
127
|
|
|
89
128
|
declare const logger: Logger;
|
|
90
129
|
|
|
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 };
|
|
130
|
+
export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type FastifyMiddleware, type HonoMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NestMiddleware, type NextMiddleware, type NuxtMiddleware, type OptionalRequestField, type ReactMiddleware, type RequestLog, type RequestLogInput, type VueMiddleware, createCipherLogger, createExpressMiddleware, createFastifyMiddleware, createHonoMiddleware, createNestMiddleware, createNextMiddleware, createNuxtMiddleware, createReactMiddleware, createVueMiddleware, logger, type RequestLog as request };
|
package/dist/index.d.ts
CHANGED
|
@@ -76,10 +76,49 @@ declare function createNestMiddleware(cipher: CipherLogger): NestMiddleware;
|
|
|
76
76
|
type HonoMiddleware = (c: Context, next: () => Promise<void>) => Response | Promise<Response>;
|
|
77
77
|
declare function createHonoMiddleware(cipher: CipherLogger): HonoMiddleware;
|
|
78
78
|
|
|
79
|
+
type ReactRequest = {
|
|
80
|
+
method: string;
|
|
81
|
+
url: string;
|
|
82
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
83
|
+
};
|
|
84
|
+
type ReactResponse = {
|
|
85
|
+
status?: number;
|
|
86
|
+
end: (...args: unknown[]) => unknown;
|
|
87
|
+
};
|
|
88
|
+
type ReactMiddleware = (req: ReactRequest, res: ReactResponse) => void;
|
|
89
|
+
declare function createReactMiddleware(cipher: CipherLogger): ReactMiddleware;
|
|
90
|
+
|
|
91
|
+
type VueRequest = {
|
|
92
|
+
method: string;
|
|
93
|
+
url: string;
|
|
94
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
95
|
+
};
|
|
96
|
+
type VueResponse = {
|
|
97
|
+
status?: number;
|
|
98
|
+
end: (...args: unknown[]) => unknown;
|
|
99
|
+
};
|
|
100
|
+
type VueMiddleware = (req: VueRequest, res: VueResponse) => void;
|
|
101
|
+
declare function createVueMiddleware(cipher: CipherLogger): VueMiddleware;
|
|
102
|
+
|
|
103
|
+
type NuxtRequest = {
|
|
104
|
+
method: string;
|
|
105
|
+
url: string;
|
|
106
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
107
|
+
};
|
|
108
|
+
type NuxtResponse = {
|
|
109
|
+
status?: number;
|
|
110
|
+
end: (...args: unknown[]) => unknown;
|
|
111
|
+
};
|
|
112
|
+
type NuxtMiddleware = (req: NuxtRequest, res: NuxtResponse) => void;
|
|
113
|
+
declare function createNuxtMiddleware(cipher: CipherLogger): NuxtMiddleware;
|
|
114
|
+
|
|
79
115
|
interface CipherLogger {
|
|
80
116
|
logRequest(input: RequestLogInput): RequestLog;
|
|
81
117
|
express(): ExpressMiddleware;
|
|
82
118
|
next(): NextMiddleware;
|
|
119
|
+
react(): ReactMiddleware;
|
|
120
|
+
vue(): VueMiddleware;
|
|
121
|
+
nuxt(): NuxtMiddleware;
|
|
83
122
|
fastify(): FastifyMiddleware;
|
|
84
123
|
nest(): NestMiddleware;
|
|
85
124
|
hono(): HonoMiddleware;
|
|
@@ -88,4 +127,4 @@ declare function createCipherLogger(config?: CipherLoggerConfig): CipherLogger;
|
|
|
88
127
|
|
|
89
128
|
declare const logger: Logger;
|
|
90
129
|
|
|
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 };
|
|
130
|
+
export { type CipherLogger, type CipherLoggerConfig, type ExpressMiddleware, type FastifyMiddleware, type HonoMiddleware, type LogLevel, type LogMeta, Logger, type LoggerOptions, type NestMiddleware, type NextMiddleware, type NuxtMiddleware, type OptionalRequestField, type ReactMiddleware, type RequestLog, type RequestLogInput, type VueMiddleware, createCipherLogger, createExpressMiddleware, createFastifyMiddleware, createHonoMiddleware, createNestMiddleware, createNextMiddleware, createNuxtMiddleware, createReactMiddleware, createVueMiddleware, logger, type RequestLog as request };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,9 @@ __export(src_exports, {
|
|
|
27
27
|
createHonoMiddleware: () => createHonoMiddleware,
|
|
28
28
|
createNestMiddleware: () => createNestMiddleware,
|
|
29
29
|
createNextMiddleware: () => createNextMiddleware,
|
|
30
|
+
createNuxtMiddleware: () => createNuxtMiddleware,
|
|
31
|
+
createReactMiddleware: () => createReactMiddleware,
|
|
32
|
+
createVueMiddleware: () => createVueMiddleware,
|
|
30
33
|
logger: () => logger
|
|
31
34
|
});
|
|
32
35
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -312,7 +315,9 @@ function createNestMiddleware(cipher) {
|
|
|
312
315
|
|
|
313
316
|
// src/adapters/hono/middleware.ts
|
|
314
317
|
function getHeader5(request, name) {
|
|
315
|
-
if (!request)
|
|
318
|
+
if (!request) {
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
316
321
|
if (typeof request.header === "function") {
|
|
317
322
|
return request.header(name) ?? void 0;
|
|
318
323
|
}
|
|
@@ -321,6 +326,9 @@ function getHeader5(request, name) {
|
|
|
321
326
|
}
|
|
322
327
|
return void 0;
|
|
323
328
|
}
|
|
329
|
+
function hasHeader(request, name) {
|
|
330
|
+
return Boolean(getHeader5(request, name));
|
|
331
|
+
}
|
|
324
332
|
function parseQuery5(urlString) {
|
|
325
333
|
try {
|
|
326
334
|
const url = new URL(urlString);
|
|
@@ -348,7 +356,7 @@ function createHonoMiddleware(cipher) {
|
|
|
348
356
|
status = cres.statusCode;
|
|
349
357
|
}
|
|
350
358
|
const xfwd = getHeader5(req, "x-forwarded-for");
|
|
351
|
-
const ip =
|
|
359
|
+
const ip = hasHeader(req, "x-forwarded-for") ? String(xfwd).split(",")[0].trim() : getHeader5(req, "x-real-ip") ?? void 0;
|
|
352
360
|
cipher.logRequest({
|
|
353
361
|
method: req?.method,
|
|
354
362
|
path: parsed.pathname + parsed.search,
|
|
@@ -366,6 +374,132 @@ function createHonoMiddleware(cipher) {
|
|
|
366
374
|
};
|
|
367
375
|
}
|
|
368
376
|
|
|
377
|
+
// src/adapters/react/middleware.ts
|
|
378
|
+
function getHeader6(request, name) {
|
|
379
|
+
if (request.headers instanceof Headers) {
|
|
380
|
+
return request.headers.get(name) ?? void 0;
|
|
381
|
+
}
|
|
382
|
+
const value = request.headers[name.toLowerCase()];
|
|
383
|
+
if (Array.isArray(value)) {
|
|
384
|
+
return value[0];
|
|
385
|
+
}
|
|
386
|
+
return value ?? void 0;
|
|
387
|
+
}
|
|
388
|
+
function parseQuery6(url) {
|
|
389
|
+
const result = {};
|
|
390
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
391
|
+
result[key] = value;
|
|
392
|
+
}
|
|
393
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
394
|
+
}
|
|
395
|
+
function createReactMiddleware(cipher) {
|
|
396
|
+
return (req, res) => {
|
|
397
|
+
const start = Date.now();
|
|
398
|
+
const originalEnd = res.end.bind(res);
|
|
399
|
+
res.end = ((...args) => {
|
|
400
|
+
const url = new URL(req.url, "http://localhost");
|
|
401
|
+
cipher.logRequest({
|
|
402
|
+
method: req.method,
|
|
403
|
+
path: url.pathname + url.search,
|
|
404
|
+
status: res.status ?? 200,
|
|
405
|
+
duration: Date.now() - start,
|
|
406
|
+
ip: getHeader6(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader6(req, "x-real-ip"),
|
|
407
|
+
userAgent: getHeader6(req, "user-agent"),
|
|
408
|
+
referer: getHeader6(req, "referer"),
|
|
409
|
+
protocol: url.protocol.replace(":", ""),
|
|
410
|
+
host: getHeader6(req, "host"),
|
|
411
|
+
query: parseQuery6(url),
|
|
412
|
+
requestId: getHeader6(req, "x-request-id")
|
|
413
|
+
});
|
|
414
|
+
return originalEnd(...args);
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/adapters/vue/middleware.ts
|
|
420
|
+
function getHeader7(request, name) {
|
|
421
|
+
if (request.headers instanceof Headers) {
|
|
422
|
+
return request.headers.get(name) ?? void 0;
|
|
423
|
+
}
|
|
424
|
+
const value = request.headers[name.toLowerCase()];
|
|
425
|
+
if (Array.isArray(value)) {
|
|
426
|
+
return value[0];
|
|
427
|
+
}
|
|
428
|
+
return value ?? void 0;
|
|
429
|
+
}
|
|
430
|
+
function parseQuery7(url) {
|
|
431
|
+
const result = {};
|
|
432
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
433
|
+
result[key] = value;
|
|
434
|
+
}
|
|
435
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
436
|
+
}
|
|
437
|
+
function createVueMiddleware(cipher) {
|
|
438
|
+
return (req, res) => {
|
|
439
|
+
const start = Date.now();
|
|
440
|
+
const originalEnd = res.end.bind(res);
|
|
441
|
+
res.end = ((...args) => {
|
|
442
|
+
const url = new URL(req.url, "http://localhost");
|
|
443
|
+
cipher.logRequest({
|
|
444
|
+
method: req.method,
|
|
445
|
+
path: url.pathname + url.search,
|
|
446
|
+
status: res.status ?? 200,
|
|
447
|
+
duration: Date.now() - start,
|
|
448
|
+
ip: getHeader7(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader7(req, "x-real-ip"),
|
|
449
|
+
userAgent: getHeader7(req, "user-agent"),
|
|
450
|
+
referer: getHeader7(req, "referer"),
|
|
451
|
+
protocol: url.protocol.replace(":", ""),
|
|
452
|
+
host: getHeader7(req, "host"),
|
|
453
|
+
query: parseQuery7(url),
|
|
454
|
+
requestId: getHeader7(req, "x-request-id")
|
|
455
|
+
});
|
|
456
|
+
return originalEnd(...args);
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/adapters/nuxt/middleware.ts
|
|
462
|
+
function getHeader8(request, name) {
|
|
463
|
+
if (request.headers instanceof Headers) {
|
|
464
|
+
return request.headers.get(name) ?? void 0;
|
|
465
|
+
}
|
|
466
|
+
const value = request.headers[name.toLowerCase()];
|
|
467
|
+
if (Array.isArray(value)) {
|
|
468
|
+
return value[0];
|
|
469
|
+
}
|
|
470
|
+
return value ?? void 0;
|
|
471
|
+
}
|
|
472
|
+
function parseQuery8(url) {
|
|
473
|
+
const result = {};
|
|
474
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
475
|
+
result[key] = value;
|
|
476
|
+
}
|
|
477
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
478
|
+
}
|
|
479
|
+
function createNuxtMiddleware(cipher) {
|
|
480
|
+
return (req, res) => {
|
|
481
|
+
const start = Date.now();
|
|
482
|
+
const originalEnd = res.end.bind(res);
|
|
483
|
+
res.end = ((...args) => {
|
|
484
|
+
const url = new URL(req.url, "http://localhost");
|
|
485
|
+
cipher.logRequest({
|
|
486
|
+
method: req.method,
|
|
487
|
+
path: url.pathname + url.search,
|
|
488
|
+
status: res.status ?? 200,
|
|
489
|
+
duration: Date.now() - start,
|
|
490
|
+
ip: getHeader8(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader8(req, "x-real-ip"),
|
|
491
|
+
userAgent: getHeader8(req, "user-agent"),
|
|
492
|
+
referer: getHeader8(req, "referer"),
|
|
493
|
+
protocol: url.protocol.replace(":", ""),
|
|
494
|
+
host: getHeader8(req, "host"),
|
|
495
|
+
query: parseQuery8(url),
|
|
496
|
+
requestId: getHeader8(req, "x-request-id")
|
|
497
|
+
});
|
|
498
|
+
return originalEnd(...args);
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
369
503
|
// src/core/create-cipher-logger.ts
|
|
370
504
|
function createCipherLogger(config = {}) {
|
|
371
505
|
const fields = resolveFieldConfig(config.fields);
|
|
@@ -385,6 +519,15 @@ function createCipherLogger(config = {}) {
|
|
|
385
519
|
next() {
|
|
386
520
|
return createNextMiddleware(cipherLogger);
|
|
387
521
|
},
|
|
522
|
+
react() {
|
|
523
|
+
return createReactMiddleware(cipherLogger);
|
|
524
|
+
},
|
|
525
|
+
vue() {
|
|
526
|
+
return createVueMiddleware(cipherLogger);
|
|
527
|
+
},
|
|
528
|
+
nuxt() {
|
|
529
|
+
return createNuxtMiddleware(cipherLogger);
|
|
530
|
+
},
|
|
388
531
|
fastify() {
|
|
389
532
|
return createFastifyMiddleware(cipherLogger);
|
|
390
533
|
},
|
|
@@ -409,5 +552,8 @@ var logger = new Logger();
|
|
|
409
552
|
createHonoMiddleware,
|
|
410
553
|
createNestMiddleware,
|
|
411
554
|
createNextMiddleware,
|
|
555
|
+
createNuxtMiddleware,
|
|
556
|
+
createReactMiddleware,
|
|
557
|
+
createVueMiddleware,
|
|
412
558
|
logger
|
|
413
559
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -279,7 +279,9 @@ function createNestMiddleware(cipher) {
|
|
|
279
279
|
|
|
280
280
|
// src/adapters/hono/middleware.ts
|
|
281
281
|
function getHeader5(request, name) {
|
|
282
|
-
if (!request)
|
|
282
|
+
if (!request) {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
283
285
|
if (typeof request.header === "function") {
|
|
284
286
|
return request.header(name) ?? void 0;
|
|
285
287
|
}
|
|
@@ -288,6 +290,9 @@ function getHeader5(request, name) {
|
|
|
288
290
|
}
|
|
289
291
|
return void 0;
|
|
290
292
|
}
|
|
293
|
+
function hasHeader(request, name) {
|
|
294
|
+
return Boolean(getHeader5(request, name));
|
|
295
|
+
}
|
|
291
296
|
function parseQuery5(urlString) {
|
|
292
297
|
try {
|
|
293
298
|
const url = new URL(urlString);
|
|
@@ -315,7 +320,7 @@ function createHonoMiddleware(cipher) {
|
|
|
315
320
|
status = cres.statusCode;
|
|
316
321
|
}
|
|
317
322
|
const xfwd = getHeader5(req, "x-forwarded-for");
|
|
318
|
-
const ip =
|
|
323
|
+
const ip = hasHeader(req, "x-forwarded-for") ? String(xfwd).split(",")[0].trim() : getHeader5(req, "x-real-ip") ?? void 0;
|
|
319
324
|
cipher.logRequest({
|
|
320
325
|
method: req?.method,
|
|
321
326
|
path: parsed.pathname + parsed.search,
|
|
@@ -333,6 +338,132 @@ function createHonoMiddleware(cipher) {
|
|
|
333
338
|
};
|
|
334
339
|
}
|
|
335
340
|
|
|
341
|
+
// src/adapters/react/middleware.ts
|
|
342
|
+
function getHeader6(request, name) {
|
|
343
|
+
if (request.headers instanceof Headers) {
|
|
344
|
+
return request.headers.get(name) ?? void 0;
|
|
345
|
+
}
|
|
346
|
+
const value = request.headers[name.toLowerCase()];
|
|
347
|
+
if (Array.isArray(value)) {
|
|
348
|
+
return value[0];
|
|
349
|
+
}
|
|
350
|
+
return value ?? void 0;
|
|
351
|
+
}
|
|
352
|
+
function parseQuery6(url) {
|
|
353
|
+
const result = {};
|
|
354
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
355
|
+
result[key] = value;
|
|
356
|
+
}
|
|
357
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
358
|
+
}
|
|
359
|
+
function createReactMiddleware(cipher) {
|
|
360
|
+
return (req, res) => {
|
|
361
|
+
const start = Date.now();
|
|
362
|
+
const originalEnd = res.end.bind(res);
|
|
363
|
+
res.end = ((...args) => {
|
|
364
|
+
const url = new URL(req.url, "http://localhost");
|
|
365
|
+
cipher.logRequest({
|
|
366
|
+
method: req.method,
|
|
367
|
+
path: url.pathname + url.search,
|
|
368
|
+
status: res.status ?? 200,
|
|
369
|
+
duration: Date.now() - start,
|
|
370
|
+
ip: getHeader6(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader6(req, "x-real-ip"),
|
|
371
|
+
userAgent: getHeader6(req, "user-agent"),
|
|
372
|
+
referer: getHeader6(req, "referer"),
|
|
373
|
+
protocol: url.protocol.replace(":", ""),
|
|
374
|
+
host: getHeader6(req, "host"),
|
|
375
|
+
query: parseQuery6(url),
|
|
376
|
+
requestId: getHeader6(req, "x-request-id")
|
|
377
|
+
});
|
|
378
|
+
return originalEnd(...args);
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/adapters/vue/middleware.ts
|
|
384
|
+
function getHeader7(request, name) {
|
|
385
|
+
if (request.headers instanceof Headers) {
|
|
386
|
+
return request.headers.get(name) ?? void 0;
|
|
387
|
+
}
|
|
388
|
+
const value = request.headers[name.toLowerCase()];
|
|
389
|
+
if (Array.isArray(value)) {
|
|
390
|
+
return value[0];
|
|
391
|
+
}
|
|
392
|
+
return value ?? void 0;
|
|
393
|
+
}
|
|
394
|
+
function parseQuery7(url) {
|
|
395
|
+
const result = {};
|
|
396
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
397
|
+
result[key] = value;
|
|
398
|
+
}
|
|
399
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
400
|
+
}
|
|
401
|
+
function createVueMiddleware(cipher) {
|
|
402
|
+
return (req, res) => {
|
|
403
|
+
const start = Date.now();
|
|
404
|
+
const originalEnd = res.end.bind(res);
|
|
405
|
+
res.end = ((...args) => {
|
|
406
|
+
const url = new URL(req.url, "http://localhost");
|
|
407
|
+
cipher.logRequest({
|
|
408
|
+
method: req.method,
|
|
409
|
+
path: url.pathname + url.search,
|
|
410
|
+
status: res.status ?? 200,
|
|
411
|
+
duration: Date.now() - start,
|
|
412
|
+
ip: getHeader7(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader7(req, "x-real-ip"),
|
|
413
|
+
userAgent: getHeader7(req, "user-agent"),
|
|
414
|
+
referer: getHeader7(req, "referer"),
|
|
415
|
+
protocol: url.protocol.replace(":", ""),
|
|
416
|
+
host: getHeader7(req, "host"),
|
|
417
|
+
query: parseQuery7(url),
|
|
418
|
+
requestId: getHeader7(req, "x-request-id")
|
|
419
|
+
});
|
|
420
|
+
return originalEnd(...args);
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// src/adapters/nuxt/middleware.ts
|
|
426
|
+
function getHeader8(request, name) {
|
|
427
|
+
if (request.headers instanceof Headers) {
|
|
428
|
+
return request.headers.get(name) ?? void 0;
|
|
429
|
+
}
|
|
430
|
+
const value = request.headers[name.toLowerCase()];
|
|
431
|
+
if (Array.isArray(value)) {
|
|
432
|
+
return value[0];
|
|
433
|
+
}
|
|
434
|
+
return value ?? void 0;
|
|
435
|
+
}
|
|
436
|
+
function parseQuery8(url) {
|
|
437
|
+
const result = {};
|
|
438
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
439
|
+
result[key] = value;
|
|
440
|
+
}
|
|
441
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
442
|
+
}
|
|
443
|
+
function createNuxtMiddleware(cipher) {
|
|
444
|
+
return (req, res) => {
|
|
445
|
+
const start = Date.now();
|
|
446
|
+
const originalEnd = res.end.bind(res);
|
|
447
|
+
res.end = ((...args) => {
|
|
448
|
+
const url = new URL(req.url, "http://localhost");
|
|
449
|
+
cipher.logRequest({
|
|
450
|
+
method: req.method,
|
|
451
|
+
path: url.pathname + url.search,
|
|
452
|
+
status: res.status ?? 200,
|
|
453
|
+
duration: Date.now() - start,
|
|
454
|
+
ip: getHeader8(req, "x-forwarded-for")?.split(",")[0]?.trim() ?? getHeader8(req, "x-real-ip"),
|
|
455
|
+
userAgent: getHeader8(req, "user-agent"),
|
|
456
|
+
referer: getHeader8(req, "referer"),
|
|
457
|
+
protocol: url.protocol.replace(":", ""),
|
|
458
|
+
host: getHeader8(req, "host"),
|
|
459
|
+
query: parseQuery8(url),
|
|
460
|
+
requestId: getHeader8(req, "x-request-id")
|
|
461
|
+
});
|
|
462
|
+
return originalEnd(...args);
|
|
463
|
+
});
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
336
467
|
// src/core/create-cipher-logger.ts
|
|
337
468
|
function createCipherLogger(config = {}) {
|
|
338
469
|
const fields = resolveFieldConfig(config.fields);
|
|
@@ -352,6 +483,15 @@ function createCipherLogger(config = {}) {
|
|
|
352
483
|
next() {
|
|
353
484
|
return createNextMiddleware(cipherLogger);
|
|
354
485
|
},
|
|
486
|
+
react() {
|
|
487
|
+
return createReactMiddleware(cipherLogger);
|
|
488
|
+
},
|
|
489
|
+
vue() {
|
|
490
|
+
return createVueMiddleware(cipherLogger);
|
|
491
|
+
},
|
|
492
|
+
nuxt() {
|
|
493
|
+
return createNuxtMiddleware(cipherLogger);
|
|
494
|
+
},
|
|
355
495
|
fastify() {
|
|
356
496
|
return createFastifyMiddleware(cipherLogger);
|
|
357
497
|
},
|
|
@@ -375,5 +515,8 @@ export {
|
|
|
375
515
|
createHonoMiddleware,
|
|
376
516
|
createNestMiddleware,
|
|
377
517
|
createNextMiddleware,
|
|
518
|
+
createNuxtMiddleware,
|
|
519
|
+
createReactMiddleware,
|
|
520
|
+
createVueMiddleware,
|
|
378
521
|
logger
|
|
379
522
|
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
// ===== JavaScript =====
|
|
12
|
+
eqeqeq: ["error", "always"],
|
|
13
|
+
curly: ["error", "all"],
|
|
14
|
+
"no-var": "error",
|
|
15
|
+
"prefer-const": "error",
|
|
16
|
+
"no-console":
|
|
17
|
+
process.env.NODE_ENV === "production"
|
|
18
|
+
? ["warn", { allow: ["warn", "error"] }]
|
|
19
|
+
: "off",
|
|
20
|
+
|
|
21
|
+
// ===== TypeScript =====
|
|
22
|
+
"@typescript-eslint/no-unused-vars": [
|
|
23
|
+
"warn",
|
|
24
|
+
{
|
|
25
|
+
argsIgnorePattern: "^_",
|
|
26
|
+
varsIgnorePattern: "^_",
|
|
27
|
+
ignoreRestSiblings: true,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
31
|
+
"error",
|
|
32
|
+
{
|
|
33
|
+
prefer: "type-imports",
|
|
34
|
+
fixStyle: "inline-type-imports",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
38
|
+
"@typescript-eslint/no-non-null-assertion": "warn",
|
|
39
|
+
|
|
40
|
+
// ===== Import =====
|
|
41
|
+
"sort-imports": [
|
|
42
|
+
"warn",
|
|
43
|
+
{
|
|
44
|
+
ignoreDeclarationSort: true,
|
|
45
|
+
ignoreCase: true,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
// ===== React =====
|
|
50
|
+
"react/jsx-key": "error",
|
|
51
|
+
"react/self-closing-comp": "warn",
|
|
52
|
+
|
|
53
|
+
// ===== Next =====
|
|
54
|
+
"@next/next/no-img-element": "warn",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
globalIgnores([
|
|
59
|
+
".next/**",
|
|
60
|
+
"out/**",
|
|
61
|
+
"build/**",
|
|
62
|
+
"next-env.d.ts",
|
|
63
|
+
"coverage/**",
|
|
64
|
+
"dist/**",
|
|
65
|
+
"*.config.js",
|
|
66
|
+
"*.config.cjs",
|
|
67
|
+
"*.config.mjs",
|
|
68
|
+
]),
|
|
69
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cipher-logger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "See every request. Capture every error. Understand your application.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsup",
|
|
10
|
-
"
|
|
10
|
+
"lint": "eslint .",
|
|
11
|
+
"lint:fix": "eslint --fix .",
|
|
12
|
+
"test": "pnpm run lint && pnpm run build",
|
|
13
|
+
"prepublishOnly": "pnpm run test",
|
|
14
|
+
"commitlint": "commitlint --edit",
|
|
15
|
+
"clean": "rm -rf out dist node_modules/.cache",
|
|
16
|
+
"prepare": "husky"
|
|
11
17
|
},
|
|
12
18
|
"repository": {
|
|
13
19
|
"type": "git",
|
|
@@ -41,6 +47,12 @@
|
|
|
41
47
|
"nextjs",
|
|
42
48
|
"nextjs-logger",
|
|
43
49
|
"nextjs-middleware",
|
|
50
|
+
"react",
|
|
51
|
+
"react-logger",
|
|
52
|
+
"vue",
|
|
53
|
+
"vue-logger",
|
|
54
|
+
"nuxt",
|
|
55
|
+
"nuxt-logger",
|
|
44
56
|
"express",
|
|
45
57
|
"express-logger",
|
|
46
58
|
"fastify",
|
|
@@ -62,6 +74,9 @@
|
|
|
62
74
|
"peerDependencies": {
|
|
63
75
|
"express": ">=4",
|
|
64
76
|
"next": ">=13",
|
|
77
|
+
"react": ">=18",
|
|
78
|
+
"vue": ">=3",
|
|
79
|
+
"nuxt": ">=3",
|
|
65
80
|
"fastify": ">=4",
|
|
66
81
|
"hono": ">=1",
|
|
67
82
|
"@nestjs/common": ">=10",
|
|
@@ -73,8 +88,16 @@
|
|
|
73
88
|
},
|
|
74
89
|
"next": {
|
|
75
90
|
"optional": true
|
|
76
|
-
}
|
|
77
|
-
|
|
91
|
+
},
|
|
92
|
+
"react": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"vue": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"nuxt": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
78
101
|
"fastify": {
|
|
79
102
|
"optional": true
|
|
80
103
|
},
|
|
@@ -89,6 +112,8 @@
|
|
|
89
112
|
}
|
|
90
113
|
},
|
|
91
114
|
"devDependencies": {
|
|
115
|
+
"@commitlint/cli": "^20.1.0",
|
|
116
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
92
117
|
"@types/express": "^5.0.3",
|
|
93
118
|
"@types/node": "^26.1.2",
|
|
94
119
|
"express": "^5.1.0",
|
|
@@ -96,6 +121,9 @@
|
|
|
96
121
|
"fastify": "^4.27.0",
|
|
97
122
|
"hono": "^3.3.0",
|
|
98
123
|
"tsup": "^8.5.1",
|
|
124
|
+
"eslint": "^9",
|
|
125
|
+
"eslint-config-next": "16.3.0",
|
|
126
|
+
"husky": "^9.1.7",
|
|
99
127
|
"typescript": "^6.0.3"
|
|
100
128
|
}
|
|
101
|
-
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @type {import("prettier").Config} */
|
|
2
|
+
const config = {
|
|
3
|
+
semi: true,
|
|
4
|
+
singleQuote: false,
|
|
5
|
+
jsxSingleQuote: false,
|
|
6
|
+
trailingComma: "all",
|
|
7
|
+
tabWidth: 2,
|
|
8
|
+
useTabs: false,
|
|
9
|
+
printWidth: 80,
|
|
10
|
+
bracketSpacing: true,
|
|
11
|
+
bracketSameLine: false,
|
|
12
|
+
arrowParens: "always",
|
|
13
|
+
endOfLine: "lf",
|
|
14
|
+
|
|
15
|
+
plugins: ["prettier-plugin-tailwindcss"],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default config;
|