snapwyr 1.0.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/README.md +420 -0
- package/dist/dashboard.d.mts +1 -0
- package/dist/dashboard.d.ts +1 -0
- package/dist/dashboard.js +35 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/dashboard.mjs +8 -0
- package/dist/dashboard.mjs.map +1 -0
- package/dist/express.d.mts +26 -0
- package/dist/express.d.ts +26 -0
- package/dist/express.js +203 -0
- package/dist/express.js.map +1 -0
- package/dist/express.mjs +183 -0
- package/dist/express.mjs.map +1 -0
- package/dist/fastify.d.mts +13 -0
- package/dist/fastify.d.ts +13 -0
- package/dist/fastify.js +204 -0
- package/dist/fastify.js.map +1 -0
- package/dist/fastify.mjs +184 -0
- package/dist/fastify.mjs.map +1 -0
- package/dist/hono.d.mts +18 -0
- package/dist/hono.d.ts +18 -0
- package/dist/hono.js +190 -0
- package/dist/hono.js.map +1 -0
- package/dist/hono.mjs +170 -0
- package/dist/hono.mjs.map +1 -0
- package/dist/index.d.mts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +51 -0
- package/dist/index.mjs.map +1 -0
- package/dist/koa.d.mts +21 -0
- package/dist/koa.d.ts +21 -0
- package/dist/koa.js +185 -0
- package/dist/koa.js.map +1 -0
- package/dist/koa.mjs +165 -0
- package/dist/koa.mjs.map +1 -0
- package/dist/nestjs.d.mts +19 -0
- package/dist/nestjs.d.ts +19 -0
- package/dist/nestjs.js +211 -0
- package/dist/nestjs.js.map +1 -0
- package/dist/nestjs.mjs +191 -0
- package/dist/nestjs.mjs.map +1 -0
- package/dist/nextjs.d.mts +33 -0
- package/dist/nextjs.d.ts +33 -0
- package/dist/nextjs.js +191 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/nextjs.mjs +178 -0
- package/dist/nextjs.mjs.map +1 -0
- package/package.json +174 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fastify.ts","../src/utils.ts"],"sourcesContent":["import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport { generateRequestId } from '@snapwyr/core';\nimport { logRequest } from './utils.js';\n\nconst requestTimingMap = new WeakMap<\n object,\n { id: string; startTime: number }\n>();\n\n/**\n * @example\n * ```ts\n * await fastify.register(snapwyr, { logBody: true });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction snapwyrPlugin(fastify: any, config: SnapWyrConfig, done: () => void) {\n if (config.enabled === false) {\n done();\n return;\n }\n\n fastify.addHook(\n 'onRequest',\n (request: any, reply: any, hookDone: () => void) => {\n const id = generateRequestId();\n requestTimingMap.set(request, { id, startTime: Date.now() });\n\n if (config.requestId) {\n try {\n reply.header('X-Request-ID', id);\n } catch {}\n }\n hookDone();\n }\n );\n\n fastify.addHook(\n 'onResponse',\n (request: any, reply: any, hookDone: () => void) => {\n const timing = requestTimingMap.get(request) || {\n id: generateRequestId(),\n startTime: Date.now(),\n };\n const duration = Date.now() - timing.startTime;\n const status = reply.statusCode;\n\n requestTimingMap.delete(request);\n\n if (\n config.statusCodes &&\n config.statusCodes.length > 0 &&\n !config.statusCodes.includes(status)\n ) {\n hookDone();\n return;\n }\n\n if (config.errorsOnly && status < 400) {\n hookDone();\n return;\n }\n\n let requestBody: string | undefined;\n if (config.logBody && request.body) {\n try {\n const body =\n typeof request.body === 'string'\n ? request.body\n : JSON.stringify(request.body);\n requestBody = config.bodySizeLimit\n ? body.slice(0, config.bodySizeLimit)\n : body;\n } catch {}\n }\n\n logRequest({\n id: timing.id,\n method: request.method,\n status,\n duration,\n url: request.url,\n startTime: timing.startTime,\n config,\n requestBody: config.logBody ? requestBody : undefined,\n });\n\n hookDone();\n }\n );\n\n done();\n}\n\nObject.defineProperty(snapwyrPlugin, Symbol.for('skip-override'), {\n value: true,\n writable: false,\n});\n\nexport const snapwyr = snapwyrPlugin;\n\nexport type { SnapWyrConfig, LogEntry };\n","import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport {\n snapwyr as coreEmitter,\n getByteSize,\n formatBytes,\n redactSensitiveData,\n} from '@snapwyr/core';\n\nexport interface LogParams {\n id: string;\n method: string;\n status: number;\n duration: number;\n url: string;\n startTime: number;\n config: SnapWyrConfig;\n requestBody?: string;\n responseBody?: string;\n error?: string;\n}\n\nexport function logRequest(params: LogParams): void {\n const { id, method, status, duration, url, startTime, config, error } =\n params;\n let { requestBody, responseBody } = params;\n\n if (config.silent) return;\n\n const showTimestamp = config.showTimestamp !== false;\n const format = config.format || 'pretty';\n const slowThreshold = config.slowThreshold ?? 1000;\n const isSlow = duration >= slowThreshold;\n\n const requestSize = requestBody ? getByteSize(requestBody) : 0;\n const responseSize = responseBody ? getByteSize(responseBody) : 0;\n const totalSize = requestSize + responseSize;\n\n if (config.redact && config.redact.length > 0) {\n if (requestBody)\n requestBody = redactSensitiveData(requestBody, config.redact);\n if (responseBody)\n responseBody = redactSensitiveData(responseBody, config.redact);\n }\n\n const logEntry: LogEntry = {\n id,\n timestamp: new Date(startTime).toISOString(),\n method: method.toUpperCase(),\n url,\n status,\n duration,\n slow: isSlow,\n };\n\n if (config.prefix) logEntry.prefix = config.prefix;\n if (error) logEntry.error = error;\n if (requestBody) logEntry.requestBody = requestBody;\n if (responseBody) logEntry.responseBody = responseBody;\n if (config.sizeTracking) {\n logEntry.requestSize = requestSize;\n logEntry.responseSize = responseSize;\n logEntry.totalSize = totalSize;\n }\n\n try {\n coreEmitter.emit('request', {\n id,\n method: method.toUpperCase(),\n url,\n status,\n duration,\n timestamp: startTime,\n requestBody,\n responseBody,\n error,\n requestSize: config.sizeTracking ? requestSize : undefined,\n responseSize: config.sizeTracking ? responseSize : undefined,\n direction: 'incoming',\n });\n } catch {}\n\n if (config.transport) {\n config.transport(logEntry);\n }\n\n if (format === 'json') {\n console.log(JSON.stringify(logEntry));\n return;\n }\n\n const useEmoji = config.emoji === true;\n let statusEmoji = '';\n if (useEmoji) {\n if (error || status >= 500) statusEmoji = '✗ ';\n else if (status >= 400) statusEmoji = '⚠ ';\n else if (status >= 300) statusEmoji = '↪ ';\n else statusEmoji = '✓ ';\n }\n\n const statusColor =\n status >= 500\n ? '\\x1b[31m'\n : status >= 400\n ? '\\x1b[33m'\n : status >= 300\n ? '\\x1b[36m'\n : '\\x1b[32m';\n const durationColor = isSlow\n ? '\\x1b[31m'\n : duration < 100\n ? '\\x1b[32m'\n : '\\x1b[33m';\n const methodColors: Record<string, string> = {\n GET: '\\x1b[34m',\n POST: '\\x1b[32m',\n PUT: '\\x1b[33m',\n PATCH: '\\x1b[35m',\n DELETE: '\\x1b[31m',\n };\n const methodColor = methodColors[method] || '';\n const reset = '\\x1b[0m';\n const dim = '\\x1b[2m';\n const bold = '\\x1b[1m';\n const timestamp = showTimestamp\n ? new Date(startTime).toISOString().slice(11, 23) + ' '\n : '';\n const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : '';\n const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : '';\n const sizeDisplay = config.sizeTracking\n ? `${dim}${formatBytes(totalSize)}${reset} `\n : '';\n\n const parts = [\n config.prefix ? `${dim}${config.prefix} ${reset}` : '',\n requestIdDisplay,\n `${dim}${timestamp}${reset}`,\n `${methodColor}${method.padEnd(6)}${reset}`,\n `${statusColor}${statusEmoji}${status}${reset}`,\n `${durationColor}${duration}ms${reset}${slowIndicator}`,\n sizeDisplay,\n `${dim}${url}${reset}`,\n ].filter(Boolean);\n\n if (error) parts.push(`\\n ${dim}Error: ${error}${reset}`);\n if (requestBody) parts.push(`\\n ${dim}Request: ${requestBody}${reset}`);\n if (responseBody) parts.push(`\\n ${dim}Response: ${responseBody}${reset}`);\n\n console.log(parts.join(' '));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,IAAAA,eAAkC;;;ACAlC,kBAKO;AAeA,SAAS,WAAW,QAAyB;AAClD,QAAM,EAAE,IAAI,QAAQ,QAAQ,UAAU,KAAK,WAAW,QAAQ,MAAM,IAClE;AACF,MAAI,EAAE,aAAa,aAAa,IAAI;AAEpC,MAAI,OAAO,OAAQ;AAEnB,QAAM,gBAAgB,OAAO,kBAAkB;AAC/C,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,gBAAgB,OAAO,iBAAiB;AAC9C,QAAM,SAAS,YAAY;AAE3B,QAAM,cAAc,kBAAc,yBAAY,WAAW,IAAI;AAC7D,QAAM,eAAe,mBAAe,yBAAY,YAAY,IAAI;AAChE,QAAM,YAAY,cAAc;AAEhC,MAAI,OAAO,UAAU,OAAO,OAAO,SAAS,GAAG;AAC7C,QAAI;AACF,wBAAc,iCAAoB,aAAa,OAAO,MAAM;AAC9D,QAAI;AACF,yBAAe,iCAAoB,cAAc,OAAO,MAAM;AAAA,EAClE;AAEA,QAAM,WAAqB;AAAA,IACzB;AAAA,IACA,WAAW,IAAI,KAAK,SAAS,EAAE,YAAY;AAAA,IAC3C,QAAQ,OAAO,YAAY;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,MAAI,OAAO,OAAQ,UAAS,SAAS,OAAO;AAC5C,MAAI,MAAO,UAAS,QAAQ;AAC5B,MAAI,YAAa,UAAS,cAAc;AACxC,MAAI,aAAc,UAAS,eAAe;AAC1C,MAAI,OAAO,cAAc;AACvB,aAAS,cAAc;AACvB,aAAS,eAAe;AACxB,aAAS,YAAY;AAAA,EACvB;AAEA,MAAI;AACF,gBAAAC,QAAY,KAAK,WAAW;AAAA,MAC1B;AAAA,MACA,QAAQ,OAAO,YAAY;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,OAAO,eAAe,cAAc;AAAA,MACjD,cAAc,OAAO,eAAe,eAAe;AAAA,MACnD,WAAW;AAAA,IACb,CAAC;AAAA,EACH,QAAQ;AAAA,EAAC;AAET,MAAI,OAAO,WAAW;AACpB,WAAO,UAAU,QAAQ;AAAA,EAC3B;AAEA,MAAI,WAAW,QAAQ;AACrB,YAAQ,IAAI,KAAK,UAAU,QAAQ,CAAC;AACpC;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,UAAU;AAClC,MAAI,cAAc;AAClB,MAAI,UAAU;AACZ,QAAI,SAAS,UAAU,IAAK,eAAc;AAAA,aACjC,UAAU,IAAK,eAAc;AAAA,aAC7B,UAAU,IAAK,eAAc;AAAA,QACjC,eAAc;AAAA,EACrB;AAEA,QAAM,cACJ,UAAU,MACN,aACA,UAAU,MACR,aACA,UAAU,MACR,aACA;AACV,QAAM,gBAAgB,SAClB,aACA,WAAW,MACT,aACA;AACN,QAAM,eAAuC;AAAA,IAC3C,KAAK;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACA,QAAM,cAAc,aAAa,MAAM,KAAK;AAC5C,QAAM,QAAQ;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;AACb,QAAM,YAAY,gBACd,IAAI,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,IAAI,EAAE,IAAI,MAClD;AACJ,QAAM,gBAAgB,SAAS,IAAI,IAAI,SAAS,KAAK,KAAK;AAC1D,QAAM,mBAAmB,OAAO,YAAY,GAAG,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM;AACvE,QAAM,cAAc,OAAO,eACvB,GAAG,GAAG,OAAG,yBAAY,SAAS,CAAC,GAAG,KAAK,MACvC;AAEJ,QAAM,QAAQ;AAAA,IACZ,OAAO,SAAS,GAAG,GAAG,GAAG,OAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IACpD;AAAA,IACA,GAAG,GAAG,GAAG,SAAS,GAAG,KAAK;AAAA,IAC1B,GAAG,WAAW,GAAG,OAAO,OAAO,CAAC,CAAC,GAAG,KAAK;AAAA,IACzC,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK;AAAA,IAC7C,GAAG,aAAa,GAAG,QAAQ,KAAK,KAAK,GAAG,aAAa;AAAA,IACrD;AAAA,IACA,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK;AAAA,EACtB,EAAE,OAAO,OAAO;AAEhB,MAAI,MAAO,OAAM,KAAK;AAAA,IAAO,GAAG,UAAU,KAAK,GAAG,KAAK,EAAE;AACzD,MAAI,YAAa,OAAM,KAAK;AAAA,IAAO,GAAG,YAAY,WAAW,GAAG,KAAK,EAAE;AACvE,MAAI,aAAc,OAAM,KAAK;AAAA,IAAO,GAAG,aAAa,YAAY,GAAG,KAAK,EAAE;AAE1E,UAAQ,IAAI,MAAM,KAAK,GAAG,CAAC;AAC7B;;;ADhJA,IAAM,mBAAmB,oBAAI,QAG3B;AASF,SAAS,cAAc,SAAc,QAAuB,MAAkB;AAC5E,MAAI,OAAO,YAAY,OAAO;AAC5B,SAAK;AACL;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAc,OAAY,aAAyB;AAClD,YAAM,SAAK,gCAAkB;AAC7B,uBAAiB,IAAI,SAAS,EAAE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;AAE3D,UAAI,OAAO,WAAW;AACpB,YAAI;AACF,gBAAM,OAAO,gBAAgB,EAAE;AAAA,QACjC,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,eAAS;AAAA,IACX;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAc,OAAY,aAAyB;AAClD,YAAM,SAAS,iBAAiB,IAAI,OAAO,KAAK;AAAA,QAC9C,QAAI,gCAAkB;AAAA,QACtB,WAAW,KAAK,IAAI;AAAA,MACtB;AACA,YAAM,WAAW,KAAK,IAAI,IAAI,OAAO;AACrC,YAAM,SAAS,MAAM;AAErB,uBAAiB,OAAO,OAAO;AAE/B,UACE,OAAO,eACP,OAAO,YAAY,SAAS,KAC5B,CAAC,OAAO,YAAY,SAAS,MAAM,GACnC;AACA,iBAAS;AACT;AAAA,MACF;AAEA,UAAI,OAAO,cAAc,SAAS,KAAK;AACrC,iBAAS;AACT;AAAA,MACF;AAEA,UAAI;AACJ,UAAI,OAAO,WAAW,QAAQ,MAAM;AAClC,YAAI;AACF,gBAAM,OACJ,OAAO,QAAQ,SAAS,WACpB,QAAQ,OACR,KAAK,UAAU,QAAQ,IAAI;AACjC,wBAAc,OAAO,gBACjB,KAAK,MAAM,GAAG,OAAO,aAAa,IAClC;AAAA,QACN,QAAQ;AAAA,QAAC;AAAA,MACX;AAEA,iBAAW;AAAA,QACT,IAAI,OAAO;AAAA,QACX,QAAQ,QAAQ;AAAA,QAChB;AAAA,QACA;AAAA,QACA,KAAK,QAAQ;AAAA,QACb,WAAW,OAAO;AAAA,QAClB;AAAA,QACA,aAAa,OAAO,UAAU,cAAc;AAAA,MAC9C,CAAC;AAED,eAAS;AAAA,IACX;AAAA,EACF;AAEA,OAAK;AACP;AAEA,OAAO,eAAe,eAAe,uBAAO,IAAI,eAAe,GAAG;AAAA,EAChE,OAAO;AAAA,EACP,UAAU;AACZ,CAAC;AAEM,IAAM,UAAU;","names":["import_core","coreEmitter"]}
|
package/dist/fastify.mjs
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// src/fastify.ts
|
|
2
|
+
import { generateRequestId } from "@snapwyr/core";
|
|
3
|
+
|
|
4
|
+
// src/utils.ts
|
|
5
|
+
import {
|
|
6
|
+
snapwyr as coreEmitter,
|
|
7
|
+
getByteSize,
|
|
8
|
+
formatBytes,
|
|
9
|
+
redactSensitiveData
|
|
10
|
+
} from "@snapwyr/core";
|
|
11
|
+
function logRequest(params) {
|
|
12
|
+
const { id, method, status, duration, url, startTime, config, error } = params;
|
|
13
|
+
let { requestBody, responseBody } = params;
|
|
14
|
+
if (config.silent) return;
|
|
15
|
+
const showTimestamp = config.showTimestamp !== false;
|
|
16
|
+
const format = config.format || "pretty";
|
|
17
|
+
const slowThreshold = config.slowThreshold ?? 1e3;
|
|
18
|
+
const isSlow = duration >= slowThreshold;
|
|
19
|
+
const requestSize = requestBody ? getByteSize(requestBody) : 0;
|
|
20
|
+
const responseSize = responseBody ? getByteSize(responseBody) : 0;
|
|
21
|
+
const totalSize = requestSize + responseSize;
|
|
22
|
+
if (config.redact && config.redact.length > 0) {
|
|
23
|
+
if (requestBody)
|
|
24
|
+
requestBody = redactSensitiveData(requestBody, config.redact);
|
|
25
|
+
if (responseBody)
|
|
26
|
+
responseBody = redactSensitiveData(responseBody, config.redact);
|
|
27
|
+
}
|
|
28
|
+
const logEntry = {
|
|
29
|
+
id,
|
|
30
|
+
timestamp: new Date(startTime).toISOString(),
|
|
31
|
+
method: method.toUpperCase(),
|
|
32
|
+
url,
|
|
33
|
+
status,
|
|
34
|
+
duration,
|
|
35
|
+
slow: isSlow
|
|
36
|
+
};
|
|
37
|
+
if (config.prefix) logEntry.prefix = config.prefix;
|
|
38
|
+
if (error) logEntry.error = error;
|
|
39
|
+
if (requestBody) logEntry.requestBody = requestBody;
|
|
40
|
+
if (responseBody) logEntry.responseBody = responseBody;
|
|
41
|
+
if (config.sizeTracking) {
|
|
42
|
+
logEntry.requestSize = requestSize;
|
|
43
|
+
logEntry.responseSize = responseSize;
|
|
44
|
+
logEntry.totalSize = totalSize;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
coreEmitter.emit("request", {
|
|
48
|
+
id,
|
|
49
|
+
method: method.toUpperCase(),
|
|
50
|
+
url,
|
|
51
|
+
status,
|
|
52
|
+
duration,
|
|
53
|
+
timestamp: startTime,
|
|
54
|
+
requestBody,
|
|
55
|
+
responseBody,
|
|
56
|
+
error,
|
|
57
|
+
requestSize: config.sizeTracking ? requestSize : void 0,
|
|
58
|
+
responseSize: config.sizeTracking ? responseSize : void 0,
|
|
59
|
+
direction: "incoming"
|
|
60
|
+
});
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
if (config.transport) {
|
|
64
|
+
config.transport(logEntry);
|
|
65
|
+
}
|
|
66
|
+
if (format === "json") {
|
|
67
|
+
console.log(JSON.stringify(logEntry));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const useEmoji = config.emoji === true;
|
|
71
|
+
let statusEmoji = "";
|
|
72
|
+
if (useEmoji) {
|
|
73
|
+
if (error || status >= 500) statusEmoji = "\u2717 ";
|
|
74
|
+
else if (status >= 400) statusEmoji = "\u26A0 ";
|
|
75
|
+
else if (status >= 300) statusEmoji = "\u21AA ";
|
|
76
|
+
else statusEmoji = "\u2713 ";
|
|
77
|
+
}
|
|
78
|
+
const statusColor = status >= 500 ? "\x1B[31m" : status >= 400 ? "\x1B[33m" : status >= 300 ? "\x1B[36m" : "\x1B[32m";
|
|
79
|
+
const durationColor = isSlow ? "\x1B[31m" : duration < 100 ? "\x1B[32m" : "\x1B[33m";
|
|
80
|
+
const methodColors = {
|
|
81
|
+
GET: "\x1B[34m",
|
|
82
|
+
POST: "\x1B[32m",
|
|
83
|
+
PUT: "\x1B[33m",
|
|
84
|
+
PATCH: "\x1B[35m",
|
|
85
|
+
DELETE: "\x1B[31m"
|
|
86
|
+
};
|
|
87
|
+
const methodColor = methodColors[method] || "";
|
|
88
|
+
const reset = "\x1B[0m";
|
|
89
|
+
const dim = "\x1B[2m";
|
|
90
|
+
const bold = "\x1B[1m";
|
|
91
|
+
const timestamp = showTimestamp ? new Date(startTime).toISOString().slice(11, 23) + " " : "";
|
|
92
|
+
const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : "";
|
|
93
|
+
const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : "";
|
|
94
|
+
const sizeDisplay = config.sizeTracking ? `${dim}${formatBytes(totalSize)}${reset} ` : "";
|
|
95
|
+
const parts = [
|
|
96
|
+
config.prefix ? `${dim}${config.prefix} ${reset}` : "",
|
|
97
|
+
requestIdDisplay,
|
|
98
|
+
`${dim}${timestamp}${reset}`,
|
|
99
|
+
`${methodColor}${method.padEnd(6)}${reset}`,
|
|
100
|
+
`${statusColor}${statusEmoji}${status}${reset}`,
|
|
101
|
+
`${durationColor}${duration}ms${reset}${slowIndicator}`,
|
|
102
|
+
sizeDisplay,
|
|
103
|
+
`${dim}${url}${reset}`
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
if (error) parts.push(`
|
|
106
|
+
${dim}Error: ${error}${reset}`);
|
|
107
|
+
if (requestBody) parts.push(`
|
|
108
|
+
${dim}Request: ${requestBody}${reset}`);
|
|
109
|
+
if (responseBody) parts.push(`
|
|
110
|
+
${dim}Response: ${responseBody}${reset}`);
|
|
111
|
+
console.log(parts.join(" "));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/fastify.ts
|
|
115
|
+
var requestTimingMap = /* @__PURE__ */ new WeakMap();
|
|
116
|
+
function snapwyrPlugin(fastify, config, done) {
|
|
117
|
+
if (config.enabled === false) {
|
|
118
|
+
done();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
fastify.addHook(
|
|
122
|
+
"onRequest",
|
|
123
|
+
(request, reply, hookDone) => {
|
|
124
|
+
const id = generateRequestId();
|
|
125
|
+
requestTimingMap.set(request, { id, startTime: Date.now() });
|
|
126
|
+
if (config.requestId) {
|
|
127
|
+
try {
|
|
128
|
+
reply.header("X-Request-ID", id);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
hookDone();
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
fastify.addHook(
|
|
136
|
+
"onResponse",
|
|
137
|
+
(request, reply, hookDone) => {
|
|
138
|
+
const timing = requestTimingMap.get(request) || {
|
|
139
|
+
id: generateRequestId(),
|
|
140
|
+
startTime: Date.now()
|
|
141
|
+
};
|
|
142
|
+
const duration = Date.now() - timing.startTime;
|
|
143
|
+
const status = reply.statusCode;
|
|
144
|
+
requestTimingMap.delete(request);
|
|
145
|
+
if (config.statusCodes && config.statusCodes.length > 0 && !config.statusCodes.includes(status)) {
|
|
146
|
+
hookDone();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (config.errorsOnly && status < 400) {
|
|
150
|
+
hookDone();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
let requestBody;
|
|
154
|
+
if (config.logBody && request.body) {
|
|
155
|
+
try {
|
|
156
|
+
const body = typeof request.body === "string" ? request.body : JSON.stringify(request.body);
|
|
157
|
+
requestBody = config.bodySizeLimit ? body.slice(0, config.bodySizeLimit) : body;
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
logRequest({
|
|
162
|
+
id: timing.id,
|
|
163
|
+
method: request.method,
|
|
164
|
+
status,
|
|
165
|
+
duration,
|
|
166
|
+
url: request.url,
|
|
167
|
+
startTime: timing.startTime,
|
|
168
|
+
config,
|
|
169
|
+
requestBody: config.logBody ? requestBody : void 0
|
|
170
|
+
});
|
|
171
|
+
hookDone();
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
done();
|
|
175
|
+
}
|
|
176
|
+
Object.defineProperty(snapwyrPlugin, /* @__PURE__ */ Symbol.for("skip-override"), {
|
|
177
|
+
value: true,
|
|
178
|
+
writable: false
|
|
179
|
+
});
|
|
180
|
+
var snapwyr = snapwyrPlugin;
|
|
181
|
+
export {
|
|
182
|
+
snapwyr
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=fastify.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fastify.ts","../src/utils.ts"],"sourcesContent":["import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport { generateRequestId } from '@snapwyr/core';\nimport { logRequest } from './utils.js';\n\nconst requestTimingMap = new WeakMap<\n object,\n { id: string; startTime: number }\n>();\n\n/**\n * @example\n * ```ts\n * await fastify.register(snapwyr, { logBody: true });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction snapwyrPlugin(fastify: any, config: SnapWyrConfig, done: () => void) {\n if (config.enabled === false) {\n done();\n return;\n }\n\n fastify.addHook(\n 'onRequest',\n (request: any, reply: any, hookDone: () => void) => {\n const id = generateRequestId();\n requestTimingMap.set(request, { id, startTime: Date.now() });\n\n if (config.requestId) {\n try {\n reply.header('X-Request-ID', id);\n } catch {}\n }\n hookDone();\n }\n );\n\n fastify.addHook(\n 'onResponse',\n (request: any, reply: any, hookDone: () => void) => {\n const timing = requestTimingMap.get(request) || {\n id: generateRequestId(),\n startTime: Date.now(),\n };\n const duration = Date.now() - timing.startTime;\n const status = reply.statusCode;\n\n requestTimingMap.delete(request);\n\n if (\n config.statusCodes &&\n config.statusCodes.length > 0 &&\n !config.statusCodes.includes(status)\n ) {\n hookDone();\n return;\n }\n\n if (config.errorsOnly && status < 400) {\n hookDone();\n return;\n }\n\n let requestBody: string | undefined;\n if (config.logBody && request.body) {\n try {\n const body =\n typeof request.body === 'string'\n ? request.body\n : JSON.stringify(request.body);\n requestBody = config.bodySizeLimit\n ? body.slice(0, config.bodySizeLimit)\n : body;\n } catch {}\n }\n\n logRequest({\n id: timing.id,\n method: request.method,\n status,\n duration,\n url: request.url,\n startTime: timing.startTime,\n config,\n requestBody: config.logBody ? requestBody : undefined,\n });\n\n hookDone();\n }\n );\n\n done();\n}\n\nObject.defineProperty(snapwyrPlugin, Symbol.for('skip-override'), {\n value: true,\n writable: false,\n});\n\nexport const snapwyr = snapwyrPlugin;\n\nexport type { SnapWyrConfig, LogEntry };\n","import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport {\n snapwyr as coreEmitter,\n getByteSize,\n formatBytes,\n redactSensitiveData,\n} from '@snapwyr/core';\n\nexport interface LogParams {\n id: string;\n method: string;\n status: number;\n duration: number;\n url: string;\n startTime: number;\n config: SnapWyrConfig;\n requestBody?: string;\n responseBody?: string;\n error?: string;\n}\n\nexport function logRequest(params: LogParams): void {\n const { id, method, status, duration, url, startTime, config, error } =\n params;\n let { requestBody, responseBody } = params;\n\n if (config.silent) return;\n\n const showTimestamp = config.showTimestamp !== false;\n const format = config.format || 'pretty';\n const slowThreshold = config.slowThreshold ?? 1000;\n const isSlow = duration >= slowThreshold;\n\n const requestSize = requestBody ? getByteSize(requestBody) : 0;\n const responseSize = responseBody ? getByteSize(responseBody) : 0;\n const totalSize = requestSize + responseSize;\n\n if (config.redact && config.redact.length > 0) {\n if (requestBody)\n requestBody = redactSensitiveData(requestBody, config.redact);\n if (responseBody)\n responseBody = redactSensitiveData(responseBody, config.redact);\n }\n\n const logEntry: LogEntry = {\n id,\n timestamp: new Date(startTime).toISOString(),\n method: method.toUpperCase(),\n url,\n status,\n duration,\n slow: isSlow,\n };\n\n if (config.prefix) logEntry.prefix = config.prefix;\n if (error) logEntry.error = error;\n if (requestBody) logEntry.requestBody = requestBody;\n if (responseBody) logEntry.responseBody = responseBody;\n if (config.sizeTracking) {\n logEntry.requestSize = requestSize;\n logEntry.responseSize = responseSize;\n logEntry.totalSize = totalSize;\n }\n\n try {\n coreEmitter.emit('request', {\n id,\n method: method.toUpperCase(),\n url,\n status,\n duration,\n timestamp: startTime,\n requestBody,\n responseBody,\n error,\n requestSize: config.sizeTracking ? requestSize : undefined,\n responseSize: config.sizeTracking ? responseSize : undefined,\n direction: 'incoming',\n });\n } catch {}\n\n if (config.transport) {\n config.transport(logEntry);\n }\n\n if (format === 'json') {\n console.log(JSON.stringify(logEntry));\n return;\n }\n\n const useEmoji = config.emoji === true;\n let statusEmoji = '';\n if (useEmoji) {\n if (error || status >= 500) statusEmoji = '✗ ';\n else if (status >= 400) statusEmoji = '⚠ ';\n else if (status >= 300) statusEmoji = '↪ ';\n else statusEmoji = '✓ ';\n }\n\n const statusColor =\n status >= 500\n ? '\\x1b[31m'\n : status >= 400\n ? '\\x1b[33m'\n : status >= 300\n ? '\\x1b[36m'\n : '\\x1b[32m';\n const durationColor = isSlow\n ? '\\x1b[31m'\n : duration < 100\n ? '\\x1b[32m'\n : '\\x1b[33m';\n const methodColors: Record<string, string> = {\n GET: '\\x1b[34m',\n POST: '\\x1b[32m',\n PUT: '\\x1b[33m',\n PATCH: '\\x1b[35m',\n DELETE: '\\x1b[31m',\n };\n const methodColor = methodColors[method] || '';\n const reset = '\\x1b[0m';\n const dim = '\\x1b[2m';\n const bold = '\\x1b[1m';\n const timestamp = showTimestamp\n ? new Date(startTime).toISOString().slice(11, 23) + ' '\n : '';\n const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : '';\n const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : '';\n const sizeDisplay = config.sizeTracking\n ? `${dim}${formatBytes(totalSize)}${reset} `\n : '';\n\n const parts = [\n config.prefix ? `${dim}${config.prefix} ${reset}` : '',\n requestIdDisplay,\n `${dim}${timestamp}${reset}`,\n `${methodColor}${method.padEnd(6)}${reset}`,\n `${statusColor}${statusEmoji}${status}${reset}`,\n `${durationColor}${duration}ms${reset}${slowIndicator}`,\n sizeDisplay,\n `${dim}${url}${reset}`,\n ].filter(Boolean);\n\n if (error) parts.push(`\\n ${dim}Error: ${error}${reset}`);\n if (requestBody) parts.push(`\\n ${dim}Request: ${requestBody}${reset}`);\n if (responseBody) parts.push(`\\n ${dim}Response: ${responseBody}${reset}`);\n\n console.log(parts.join(' '));\n}\n"],"mappings":"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","names":[]}
|
package/dist/hono.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SnapWyrConfig } from '@snapwyr/core';
|
|
2
|
+
export { LogEntry, SnapWyrConfig } from '@snapwyr/core';
|
|
3
|
+
|
|
4
|
+
interface HonoContext {
|
|
5
|
+
req: {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
query: (key?: string) => string | Record<string, string> | undefined;
|
|
9
|
+
raw: Request;
|
|
10
|
+
};
|
|
11
|
+
res: Response | undefined;
|
|
12
|
+
header: (name: string, value: string) => void;
|
|
13
|
+
}
|
|
14
|
+
type HonoNext = () => Promise<void>;
|
|
15
|
+
type HonoMiddleware = (c: HonoContext, next: HonoNext) => Promise<Response | void>;
|
|
16
|
+
declare function snapwyr(config?: SnapWyrConfig): HonoMiddleware;
|
|
17
|
+
|
|
18
|
+
export { snapwyr };
|
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SnapWyrConfig } from '@snapwyr/core';
|
|
2
|
+
export { LogEntry, SnapWyrConfig } from '@snapwyr/core';
|
|
3
|
+
|
|
4
|
+
interface HonoContext {
|
|
5
|
+
req: {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
query: (key?: string) => string | Record<string, string> | undefined;
|
|
9
|
+
raw: Request;
|
|
10
|
+
};
|
|
11
|
+
res: Response | undefined;
|
|
12
|
+
header: (name: string, value: string) => void;
|
|
13
|
+
}
|
|
14
|
+
type HonoNext = () => Promise<void>;
|
|
15
|
+
type HonoMiddleware = (c: HonoContext, next: HonoNext) => Promise<Response | void>;
|
|
16
|
+
declare function snapwyr(config?: SnapWyrConfig): HonoMiddleware;
|
|
17
|
+
|
|
18
|
+
export { snapwyr };
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
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/hono.ts
|
|
21
|
+
var hono_exports = {};
|
|
22
|
+
__export(hono_exports, {
|
|
23
|
+
snapwyr: () => snapwyr
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(hono_exports);
|
|
26
|
+
var import_core2 = require("@snapwyr/core");
|
|
27
|
+
|
|
28
|
+
// src/utils.ts
|
|
29
|
+
var import_core = require("@snapwyr/core");
|
|
30
|
+
function logRequest(params) {
|
|
31
|
+
const { id, method, status, duration, url, startTime, config, error } = params;
|
|
32
|
+
let { requestBody, responseBody } = params;
|
|
33
|
+
if (config.silent) return;
|
|
34
|
+
const showTimestamp = config.showTimestamp !== false;
|
|
35
|
+
const format = config.format || "pretty";
|
|
36
|
+
const slowThreshold = config.slowThreshold ?? 1e3;
|
|
37
|
+
const isSlow = duration >= slowThreshold;
|
|
38
|
+
const requestSize = requestBody ? (0, import_core.getByteSize)(requestBody) : 0;
|
|
39
|
+
const responseSize = responseBody ? (0, import_core.getByteSize)(responseBody) : 0;
|
|
40
|
+
const totalSize = requestSize + responseSize;
|
|
41
|
+
if (config.redact && config.redact.length > 0) {
|
|
42
|
+
if (requestBody)
|
|
43
|
+
requestBody = (0, import_core.redactSensitiveData)(requestBody, config.redact);
|
|
44
|
+
if (responseBody)
|
|
45
|
+
responseBody = (0, import_core.redactSensitiveData)(responseBody, config.redact);
|
|
46
|
+
}
|
|
47
|
+
const logEntry = {
|
|
48
|
+
id,
|
|
49
|
+
timestamp: new Date(startTime).toISOString(),
|
|
50
|
+
method: method.toUpperCase(),
|
|
51
|
+
url,
|
|
52
|
+
status,
|
|
53
|
+
duration,
|
|
54
|
+
slow: isSlow
|
|
55
|
+
};
|
|
56
|
+
if (config.prefix) logEntry.prefix = config.prefix;
|
|
57
|
+
if (error) logEntry.error = error;
|
|
58
|
+
if (requestBody) logEntry.requestBody = requestBody;
|
|
59
|
+
if (responseBody) logEntry.responseBody = responseBody;
|
|
60
|
+
if (config.sizeTracking) {
|
|
61
|
+
logEntry.requestSize = requestSize;
|
|
62
|
+
logEntry.responseSize = responseSize;
|
|
63
|
+
logEntry.totalSize = totalSize;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
import_core.snapwyr.emit("request", {
|
|
67
|
+
id,
|
|
68
|
+
method: method.toUpperCase(),
|
|
69
|
+
url,
|
|
70
|
+
status,
|
|
71
|
+
duration,
|
|
72
|
+
timestamp: startTime,
|
|
73
|
+
requestBody,
|
|
74
|
+
responseBody,
|
|
75
|
+
error,
|
|
76
|
+
requestSize: config.sizeTracking ? requestSize : void 0,
|
|
77
|
+
responseSize: config.sizeTracking ? responseSize : void 0,
|
|
78
|
+
direction: "incoming"
|
|
79
|
+
});
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
if (config.transport) {
|
|
83
|
+
config.transport(logEntry);
|
|
84
|
+
}
|
|
85
|
+
if (format === "json") {
|
|
86
|
+
console.log(JSON.stringify(logEntry));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const useEmoji = config.emoji === true;
|
|
90
|
+
let statusEmoji = "";
|
|
91
|
+
if (useEmoji) {
|
|
92
|
+
if (error || status >= 500) statusEmoji = "\u2717 ";
|
|
93
|
+
else if (status >= 400) statusEmoji = "\u26A0 ";
|
|
94
|
+
else if (status >= 300) statusEmoji = "\u21AA ";
|
|
95
|
+
else statusEmoji = "\u2713 ";
|
|
96
|
+
}
|
|
97
|
+
const statusColor = status >= 500 ? "\x1B[31m" : status >= 400 ? "\x1B[33m" : status >= 300 ? "\x1B[36m" : "\x1B[32m";
|
|
98
|
+
const durationColor = isSlow ? "\x1B[31m" : duration < 100 ? "\x1B[32m" : "\x1B[33m";
|
|
99
|
+
const methodColors = {
|
|
100
|
+
GET: "\x1B[34m",
|
|
101
|
+
POST: "\x1B[32m",
|
|
102
|
+
PUT: "\x1B[33m",
|
|
103
|
+
PATCH: "\x1B[35m",
|
|
104
|
+
DELETE: "\x1B[31m"
|
|
105
|
+
};
|
|
106
|
+
const methodColor = methodColors[method] || "";
|
|
107
|
+
const reset = "\x1B[0m";
|
|
108
|
+
const dim = "\x1B[2m";
|
|
109
|
+
const bold = "\x1B[1m";
|
|
110
|
+
const timestamp = showTimestamp ? new Date(startTime).toISOString().slice(11, 23) + " " : "";
|
|
111
|
+
const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : "";
|
|
112
|
+
const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : "";
|
|
113
|
+
const sizeDisplay = config.sizeTracking ? `${dim}${(0, import_core.formatBytes)(totalSize)}${reset} ` : "";
|
|
114
|
+
const parts = [
|
|
115
|
+
config.prefix ? `${dim}${config.prefix} ${reset}` : "",
|
|
116
|
+
requestIdDisplay,
|
|
117
|
+
`${dim}${timestamp}${reset}`,
|
|
118
|
+
`${methodColor}${method.padEnd(6)}${reset}`,
|
|
119
|
+
`${statusColor}${statusEmoji}${status}${reset}`,
|
|
120
|
+
`${durationColor}${duration}ms${reset}${slowIndicator}`,
|
|
121
|
+
sizeDisplay,
|
|
122
|
+
`${dim}${url}${reset}`
|
|
123
|
+
].filter(Boolean);
|
|
124
|
+
if (error) parts.push(`
|
|
125
|
+
${dim}Error: ${error}${reset}`);
|
|
126
|
+
if (requestBody) parts.push(`
|
|
127
|
+
${dim}Request: ${requestBody}${reset}`);
|
|
128
|
+
if (responseBody) parts.push(`
|
|
129
|
+
${dim}Response: ${responseBody}${reset}`);
|
|
130
|
+
console.log(parts.join(" "));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/hono.ts
|
|
134
|
+
function snapwyr(config = {}) {
|
|
135
|
+
return async function(c, next) {
|
|
136
|
+
if (config.enabled === false) {
|
|
137
|
+
await next();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const id = (0, import_core2.generateRequestId)();
|
|
141
|
+
const startTime = Date.now();
|
|
142
|
+
const method = c.req.method;
|
|
143
|
+
const queryObj = c.req.query();
|
|
144
|
+
const queryString = typeof queryObj === "object" && queryObj ? new URLSearchParams(queryObj).toString() : "";
|
|
145
|
+
const url = c.req.path + (queryString ? "?" + queryString : "");
|
|
146
|
+
if (config.requestId)
|
|
147
|
+
try {
|
|
148
|
+
c.header("X-Request-ID", id);
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
let requestBody;
|
|
152
|
+
if (config.logBody) {
|
|
153
|
+
try {
|
|
154
|
+
const body = await c.req.raw.clone().text().catch(() => null);
|
|
155
|
+
if (body) requestBody = body.slice(0, config.bodySizeLimit || 500);
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
await next();
|
|
160
|
+
const duration = Date.now() - startTime;
|
|
161
|
+
const status = c.res?.status ?? 200;
|
|
162
|
+
if (config.statusCodes && config.statusCodes.length > 0 && !config.statusCodes.includes(status))
|
|
163
|
+
return;
|
|
164
|
+
let responseBody;
|
|
165
|
+
if (config.logBody && c.res) {
|
|
166
|
+
try {
|
|
167
|
+
const resBody = await c.res.clone().text().catch(() => null);
|
|
168
|
+
if (resBody)
|
|
169
|
+
responseBody = resBody.slice(0, config.bodySizeLimit || 500);
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
logRequest({
|
|
174
|
+
id,
|
|
175
|
+
method,
|
|
176
|
+
status,
|
|
177
|
+
duration,
|
|
178
|
+
url,
|
|
179
|
+
startTime,
|
|
180
|
+
config,
|
|
181
|
+
requestBody: config.logBody ? requestBody : void 0,
|
|
182
|
+
responseBody: config.logBody ? responseBody : void 0
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
187
|
+
0 && (module.exports = {
|
|
188
|
+
snapwyr
|
|
189
|
+
});
|
|
190
|
+
//# sourceMappingURL=hono.js.map
|
package/dist/hono.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hono.ts","../src/utils.ts"],"sourcesContent":["import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport { generateRequestId } from '@snapwyr/core';\nimport { logRequest } from './utils.js';\n\ninterface HonoContext {\n req: {\n method: string;\n path: string;\n query: (key?: string) => string | Record<string, string> | undefined;\n raw: Request;\n };\n res: Response | undefined;\n header: (name: string, value: string) => void;\n}\ntype HonoNext = () => Promise<void>;\ntype HonoMiddleware = (\n c: HonoContext,\n next: HonoNext\n) => Promise<Response | void>;\n\nexport function snapwyr(config: SnapWyrConfig = {}): HonoMiddleware {\n return async function (c, next) {\n if (config.enabled === false) {\n await next();\n return;\n }\n\n const id = generateRequestId();\n const startTime = Date.now();\n const method = c.req.method;\n const queryObj = c.req.query();\n const queryString =\n typeof queryObj === 'object' && queryObj\n ? new URLSearchParams(queryObj as Record<string, string>).toString()\n : '';\n const url = c.req.path + (queryString ? '?' + queryString : '');\n\n if (config.requestId)\n try {\n c.header('X-Request-ID', id);\n } catch {}\n\n let requestBody: string | undefined;\n if (config.logBody) {\n try {\n const body = await c.req.raw\n .clone()\n .text()\n .catch(() => null);\n if (body) requestBody = body.slice(0, config.bodySizeLimit || 500);\n } catch {}\n }\n\n await next();\n\n const duration = Date.now() - startTime;\n const status = c.res?.status ?? 200;\n\n if (\n config.statusCodes &&\n config.statusCodes.length > 0 &&\n !config.statusCodes.includes(status)\n )\n return;\n\n let responseBody: string | undefined;\n if (config.logBody && c.res) {\n try {\n const resBody = await c.res\n .clone()\n .text()\n .catch(() => null);\n if (resBody)\n responseBody = resBody.slice(0, config.bodySizeLimit || 500);\n } catch {}\n }\n\n logRequest({\n id,\n method,\n status,\n duration,\n url,\n startTime,\n config,\n requestBody: config.logBody ? requestBody : undefined,\n responseBody: config.logBody ? responseBody : undefined,\n });\n };\n}\n\nexport type { SnapWyrConfig, LogEntry };\n","import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport {\n snapwyr as coreEmitter,\n getByteSize,\n formatBytes,\n redactSensitiveData,\n} from '@snapwyr/core';\n\nexport interface LogParams {\n id: string;\n method: string;\n status: number;\n duration: number;\n url: string;\n startTime: number;\n config: SnapWyrConfig;\n requestBody?: string;\n responseBody?: string;\n error?: string;\n}\n\nexport function logRequest(params: LogParams): void {\n const { id, method, status, duration, url, startTime, config, error } =\n params;\n let { requestBody, responseBody } = params;\n\n if (config.silent) return;\n\n const showTimestamp = config.showTimestamp !== false;\n const format = config.format || 'pretty';\n const slowThreshold = config.slowThreshold ?? 1000;\n const isSlow = duration >= slowThreshold;\n\n const requestSize = requestBody ? getByteSize(requestBody) : 0;\n const responseSize = responseBody ? getByteSize(responseBody) : 0;\n const totalSize = requestSize + responseSize;\n\n if (config.redact && config.redact.length > 0) {\n if (requestBody)\n requestBody = redactSensitiveData(requestBody, config.redact);\n if (responseBody)\n responseBody = redactSensitiveData(responseBody, config.redact);\n }\n\n const logEntry: LogEntry = {\n id,\n timestamp: new Date(startTime).toISOString(),\n method: method.toUpperCase(),\n url,\n status,\n duration,\n slow: isSlow,\n };\n\n if (config.prefix) logEntry.prefix = config.prefix;\n if (error) logEntry.error = error;\n if (requestBody) logEntry.requestBody = requestBody;\n if (responseBody) logEntry.responseBody = responseBody;\n if (config.sizeTracking) {\n logEntry.requestSize = requestSize;\n logEntry.responseSize = responseSize;\n logEntry.totalSize = totalSize;\n }\n\n try {\n coreEmitter.emit('request', {\n id,\n method: method.toUpperCase(),\n url,\n status,\n duration,\n timestamp: startTime,\n requestBody,\n responseBody,\n error,\n requestSize: config.sizeTracking ? requestSize : undefined,\n responseSize: config.sizeTracking ? responseSize : undefined,\n direction: 'incoming',\n });\n } catch {}\n\n if (config.transport) {\n config.transport(logEntry);\n }\n\n if (format === 'json') {\n console.log(JSON.stringify(logEntry));\n return;\n }\n\n const useEmoji = config.emoji === true;\n let statusEmoji = '';\n if (useEmoji) {\n if (error || status >= 500) statusEmoji = '✗ ';\n else if (status >= 400) statusEmoji = '⚠ ';\n else if (status >= 300) statusEmoji = '↪ ';\n else statusEmoji = '✓ ';\n }\n\n const statusColor =\n status >= 500\n ? '\\x1b[31m'\n : status >= 400\n ? '\\x1b[33m'\n : status >= 300\n ? '\\x1b[36m'\n : '\\x1b[32m';\n const durationColor = isSlow\n ? '\\x1b[31m'\n : duration < 100\n ? '\\x1b[32m'\n : '\\x1b[33m';\n const methodColors: Record<string, string> = {\n GET: '\\x1b[34m',\n POST: '\\x1b[32m',\n PUT: '\\x1b[33m',\n PATCH: '\\x1b[35m',\n DELETE: '\\x1b[31m',\n };\n const methodColor = methodColors[method] || '';\n const reset = '\\x1b[0m';\n const dim = '\\x1b[2m';\n const bold = '\\x1b[1m';\n const timestamp = showTimestamp\n ? new Date(startTime).toISOString().slice(11, 23) + ' '\n : '';\n const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : '';\n const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : '';\n const sizeDisplay = config.sizeTracking\n ? `${dim}${formatBytes(totalSize)}${reset} `\n : '';\n\n const parts = [\n config.prefix ? `${dim}${config.prefix} ${reset}` : '',\n requestIdDisplay,\n `${dim}${timestamp}${reset}`,\n `${methodColor}${method.padEnd(6)}${reset}`,\n `${statusColor}${statusEmoji}${status}${reset}`,\n `${durationColor}${duration}ms${reset}${slowIndicator}`,\n sizeDisplay,\n `${dim}${url}${reset}`,\n ].filter(Boolean);\n\n if (error) parts.push(`\\n ${dim}Error: ${error}${reset}`);\n if (requestBody) parts.push(`\\n ${dim}Request: ${requestBody}${reset}`);\n if (responseBody) parts.push(`\\n ${dim}Response: ${responseBody}${reset}`);\n\n console.log(parts.join(' '));\n}\n"],"mappings":"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","names":["import_core","coreEmitter"]}
|
package/dist/hono.mjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/hono.ts
|
|
2
|
+
import { generateRequestId } from "@snapwyr/core";
|
|
3
|
+
|
|
4
|
+
// src/utils.ts
|
|
5
|
+
import {
|
|
6
|
+
snapwyr as coreEmitter,
|
|
7
|
+
getByteSize,
|
|
8
|
+
formatBytes,
|
|
9
|
+
redactSensitiveData
|
|
10
|
+
} from "@snapwyr/core";
|
|
11
|
+
function logRequest(params) {
|
|
12
|
+
const { id, method, status, duration, url, startTime, config, error } = params;
|
|
13
|
+
let { requestBody, responseBody } = params;
|
|
14
|
+
if (config.silent) return;
|
|
15
|
+
const showTimestamp = config.showTimestamp !== false;
|
|
16
|
+
const format = config.format || "pretty";
|
|
17
|
+
const slowThreshold = config.slowThreshold ?? 1e3;
|
|
18
|
+
const isSlow = duration >= slowThreshold;
|
|
19
|
+
const requestSize = requestBody ? getByteSize(requestBody) : 0;
|
|
20
|
+
const responseSize = responseBody ? getByteSize(responseBody) : 0;
|
|
21
|
+
const totalSize = requestSize + responseSize;
|
|
22
|
+
if (config.redact && config.redact.length > 0) {
|
|
23
|
+
if (requestBody)
|
|
24
|
+
requestBody = redactSensitiveData(requestBody, config.redact);
|
|
25
|
+
if (responseBody)
|
|
26
|
+
responseBody = redactSensitiveData(responseBody, config.redact);
|
|
27
|
+
}
|
|
28
|
+
const logEntry = {
|
|
29
|
+
id,
|
|
30
|
+
timestamp: new Date(startTime).toISOString(),
|
|
31
|
+
method: method.toUpperCase(),
|
|
32
|
+
url,
|
|
33
|
+
status,
|
|
34
|
+
duration,
|
|
35
|
+
slow: isSlow
|
|
36
|
+
};
|
|
37
|
+
if (config.prefix) logEntry.prefix = config.prefix;
|
|
38
|
+
if (error) logEntry.error = error;
|
|
39
|
+
if (requestBody) logEntry.requestBody = requestBody;
|
|
40
|
+
if (responseBody) logEntry.responseBody = responseBody;
|
|
41
|
+
if (config.sizeTracking) {
|
|
42
|
+
logEntry.requestSize = requestSize;
|
|
43
|
+
logEntry.responseSize = responseSize;
|
|
44
|
+
logEntry.totalSize = totalSize;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
coreEmitter.emit("request", {
|
|
48
|
+
id,
|
|
49
|
+
method: method.toUpperCase(),
|
|
50
|
+
url,
|
|
51
|
+
status,
|
|
52
|
+
duration,
|
|
53
|
+
timestamp: startTime,
|
|
54
|
+
requestBody,
|
|
55
|
+
responseBody,
|
|
56
|
+
error,
|
|
57
|
+
requestSize: config.sizeTracking ? requestSize : void 0,
|
|
58
|
+
responseSize: config.sizeTracking ? responseSize : void 0,
|
|
59
|
+
direction: "incoming"
|
|
60
|
+
});
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
if (config.transport) {
|
|
64
|
+
config.transport(logEntry);
|
|
65
|
+
}
|
|
66
|
+
if (format === "json") {
|
|
67
|
+
console.log(JSON.stringify(logEntry));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const useEmoji = config.emoji === true;
|
|
71
|
+
let statusEmoji = "";
|
|
72
|
+
if (useEmoji) {
|
|
73
|
+
if (error || status >= 500) statusEmoji = "\u2717 ";
|
|
74
|
+
else if (status >= 400) statusEmoji = "\u26A0 ";
|
|
75
|
+
else if (status >= 300) statusEmoji = "\u21AA ";
|
|
76
|
+
else statusEmoji = "\u2713 ";
|
|
77
|
+
}
|
|
78
|
+
const statusColor = status >= 500 ? "\x1B[31m" : status >= 400 ? "\x1B[33m" : status >= 300 ? "\x1B[36m" : "\x1B[32m";
|
|
79
|
+
const durationColor = isSlow ? "\x1B[31m" : duration < 100 ? "\x1B[32m" : "\x1B[33m";
|
|
80
|
+
const methodColors = {
|
|
81
|
+
GET: "\x1B[34m",
|
|
82
|
+
POST: "\x1B[32m",
|
|
83
|
+
PUT: "\x1B[33m",
|
|
84
|
+
PATCH: "\x1B[35m",
|
|
85
|
+
DELETE: "\x1B[31m"
|
|
86
|
+
};
|
|
87
|
+
const methodColor = methodColors[method] || "";
|
|
88
|
+
const reset = "\x1B[0m";
|
|
89
|
+
const dim = "\x1B[2m";
|
|
90
|
+
const bold = "\x1B[1m";
|
|
91
|
+
const timestamp = showTimestamp ? new Date(startTime).toISOString().slice(11, 23) + " " : "";
|
|
92
|
+
const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : "";
|
|
93
|
+
const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : "";
|
|
94
|
+
const sizeDisplay = config.sizeTracking ? `${dim}${formatBytes(totalSize)}${reset} ` : "";
|
|
95
|
+
const parts = [
|
|
96
|
+
config.prefix ? `${dim}${config.prefix} ${reset}` : "",
|
|
97
|
+
requestIdDisplay,
|
|
98
|
+
`${dim}${timestamp}${reset}`,
|
|
99
|
+
`${methodColor}${method.padEnd(6)}${reset}`,
|
|
100
|
+
`${statusColor}${statusEmoji}${status}${reset}`,
|
|
101
|
+
`${durationColor}${duration}ms${reset}${slowIndicator}`,
|
|
102
|
+
sizeDisplay,
|
|
103
|
+
`${dim}${url}${reset}`
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
if (error) parts.push(`
|
|
106
|
+
${dim}Error: ${error}${reset}`);
|
|
107
|
+
if (requestBody) parts.push(`
|
|
108
|
+
${dim}Request: ${requestBody}${reset}`);
|
|
109
|
+
if (responseBody) parts.push(`
|
|
110
|
+
${dim}Response: ${responseBody}${reset}`);
|
|
111
|
+
console.log(parts.join(" "));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/hono.ts
|
|
115
|
+
function snapwyr(config = {}) {
|
|
116
|
+
return async function(c, next) {
|
|
117
|
+
if (config.enabled === false) {
|
|
118
|
+
await next();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const id = generateRequestId();
|
|
122
|
+
const startTime = Date.now();
|
|
123
|
+
const method = c.req.method;
|
|
124
|
+
const queryObj = c.req.query();
|
|
125
|
+
const queryString = typeof queryObj === "object" && queryObj ? new URLSearchParams(queryObj).toString() : "";
|
|
126
|
+
const url = c.req.path + (queryString ? "?" + queryString : "");
|
|
127
|
+
if (config.requestId)
|
|
128
|
+
try {
|
|
129
|
+
c.header("X-Request-ID", id);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
let requestBody;
|
|
133
|
+
if (config.logBody) {
|
|
134
|
+
try {
|
|
135
|
+
const body = await c.req.raw.clone().text().catch(() => null);
|
|
136
|
+
if (body) requestBody = body.slice(0, config.bodySizeLimit || 500);
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await next();
|
|
141
|
+
const duration = Date.now() - startTime;
|
|
142
|
+
const status = c.res?.status ?? 200;
|
|
143
|
+
if (config.statusCodes && config.statusCodes.length > 0 && !config.statusCodes.includes(status))
|
|
144
|
+
return;
|
|
145
|
+
let responseBody;
|
|
146
|
+
if (config.logBody && c.res) {
|
|
147
|
+
try {
|
|
148
|
+
const resBody = await c.res.clone().text().catch(() => null);
|
|
149
|
+
if (resBody)
|
|
150
|
+
responseBody = resBody.slice(0, config.bodySizeLimit || 500);
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
logRequest({
|
|
155
|
+
id,
|
|
156
|
+
method,
|
|
157
|
+
status,
|
|
158
|
+
duration,
|
|
159
|
+
url,
|
|
160
|
+
startTime,
|
|
161
|
+
config,
|
|
162
|
+
requestBody: config.logBody ? requestBody : void 0,
|
|
163
|
+
responseBody: config.logBody ? responseBody : void 0
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
export {
|
|
168
|
+
snapwyr
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=hono.mjs.map
|