@loggerjs/node 0.5.0 → 0.5.2

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.
Files changed (102) hide show
  1. package/README.md +5 -1
  2. package/dist/bullmq-integration.cjs +169 -0
  3. package/dist/bullmq-integration.cjs.map +1 -0
  4. package/dist/bullmq-integration.js +168 -0
  5. package/dist/bullmq-integration.js.map +1 -0
  6. package/dist/cli-integration.cjs +116 -0
  7. package/dist/cli-integration.cjs.map +1 -0
  8. package/dist/cli-integration.js +115 -0
  9. package/dist/cli-integration.js.map +1 -0
  10. package/dist/context.cjs +37 -0
  11. package/dist/context.cjs.map +1 -0
  12. package/dist/context.js +35 -0
  13. package/dist/context.js.map +1 -0
  14. package/dist/database-integration.cjs +128 -0
  15. package/dist/database-integration.cjs.map +1 -0
  16. package/dist/database-integration.js +127 -0
  17. package/dist/database-integration.js.map +1 -0
  18. package/dist/diagnostics-channel-integration.cjs +66 -0
  19. package/dist/diagnostics-channel-integration.cjs.map +1 -0
  20. package/dist/diagnostics-channel-integration.js +65 -0
  21. package/dist/diagnostics-channel-integration.js.map +1 -0
  22. package/dist/express-integration.cjs +130 -0
  23. package/dist/express-integration.cjs.map +1 -0
  24. package/dist/express-integration.js +129 -0
  25. package/dist/express-integration.js.map +1 -0
  26. package/dist/fastify-integration.cjs +120 -0
  27. package/dist/fastify-integration.cjs.map +1 -0
  28. package/dist/fastify-integration.js +119 -0
  29. package/dist/fastify-integration.js.map +1 -0
  30. package/dist/file-transport.cjs +260 -0
  31. package/dist/file-transport.cjs.map +1 -0
  32. package/dist/file-transport.js +259 -0
  33. package/dist/file-transport.js.map +1 -0
  34. package/dist/hapi-integration.cjs +61 -0
  35. package/dist/hapi-integration.cjs.map +1 -0
  36. package/dist/hapi-integration.js +60 -0
  37. package/dist/hapi-integration.js.map +1 -0
  38. package/dist/http-transport.cjs +55 -0
  39. package/dist/http-transport.cjs.map +1 -0
  40. package/dist/http-transport.js +54 -0
  41. package/dist/http-transport.js.map +1 -0
  42. package/dist/koa-integration.cjs +57 -0
  43. package/dist/koa-integration.cjs.map +1 -0
  44. package/dist/koa-integration.js +56 -0
  45. package/dist/koa-integration.js.map +1 -0
  46. package/dist/logger-diagnostics.cjs +46 -0
  47. package/dist/logger-diagnostics.cjs.map +1 -0
  48. package/dist/logger-diagnostics.js +45 -0
  49. package/dist/logger-diagnostics.js.map +1 -0
  50. package/dist/nest-integration.cjs +138 -0
  51. package/dist/nest-integration.cjs.map +1 -0
  52. package/dist/nest-integration.js +137 -0
  53. package/dist/nest-integration.js.map +1 -0
  54. package/dist/node-fetch-integration.cjs +113 -0
  55. package/dist/node-fetch-integration.cjs.map +1 -0
  56. package/dist/node-fetch-integration.js +112 -0
  57. package/dist/node-fetch-integration.js.map +1 -0
  58. package/dist/node-http-client-integration.cjs +192 -0
  59. package/dist/node-http-client-integration.cjs.map +1 -0
  60. package/dist/node-http-client-integration.js +192 -0
  61. package/dist/node-http-client-integration.js.map +1 -0
  62. package/dist/payload-transforms.cjs +29 -0
  63. package/dist/payload-transforms.cjs.map +1 -0
  64. package/dist/payload-transforms.js +28 -0
  65. package/dist/payload-transforms.js.map +1 -0
  66. package/dist/prisma-integration.cjs +150 -0
  67. package/dist/prisma-integration.cjs.map +1 -0
  68. package/dist/prisma-integration.js +149 -0
  69. package/dist/prisma-integration.js.map +1 -0
  70. package/dist/process-integration.cjs +134 -0
  71. package/dist/process-integration.cjs.map +1 -0
  72. package/dist/process-integration.js +133 -0
  73. package/dist/process-integration.js.map +1 -0
  74. package/dist/queue-integration.cjs +148 -0
  75. package/dist/queue-integration.cjs.map +1 -0
  76. package/dist/queue-integration.js +147 -0
  77. package/dist/queue-integration.js.map +1 -0
  78. package/dist/redis-integration.cjs +154 -0
  79. package/dist/redis-integration.cjs.map +1 -0
  80. package/dist/redis-integration.js +153 -0
  81. package/dist/redis-integration.js.map +1 -0
  82. package/dist/rotating-file-transport.cjs +318 -0
  83. package/dist/rotating-file-transport.cjs.map +1 -0
  84. package/dist/rotating-file-transport.js +317 -0
  85. package/dist/rotating-file-transport.js.map +1 -0
  86. package/dist/serverless-integration.cjs +104 -0
  87. package/dist/serverless-integration.cjs.map +1 -0
  88. package/dist/serverless-integration.js +103 -0
  89. package/dist/serverless-integration.js.map +1 -0
  90. package/dist/stdout-transport.cjs +197 -0
  91. package/dist/stdout-transport.cjs.map +1 -0
  92. package/dist/stdout-transport.js +195 -0
  93. package/dist/stdout-transport.js.map +1 -0
  94. package/dist/syslog-transport.cjs +135 -0
  95. package/dist/syslog-transport.cjs.map +1 -0
  96. package/dist/syslog-transport.js +133 -0
  97. package/dist/syslog-transport.js.map +1 -0
  98. package/dist/worker-transport.cjs +285 -0
  99. package/dist/worker-transport.cjs.map +1 -0
  100. package/dist/worker-transport.js +284 -0
  101. package/dist/worker-transport.js.map +1 -0
  102. package/package.json +104 -77
@@ -0,0 +1,260 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _loggerjs_core = require("@loggerjs/core");
3
+ let fs = require("fs");
4
+ let path = require("path");
5
+ //#region src/node-destination.ts
6
+ const encoder = new TextEncoder();
7
+ const normalizeError = (error) => error instanceof Error ? error : new Error(String(error));
8
+ function byteLength(payload) {
9
+ return typeof payload === "string" ? encoder.encode(payload).byteLength : payload.byteLength;
10
+ }
11
+ function coalescePayloads(payloads) {
12
+ if (payloads.every((payload) => typeof payload === "string")) return payloads.join("");
13
+ const buffers = payloads.map((payload) => typeof payload === "string" ? Buffer.from(payload) : Buffer.from(payload));
14
+ return Buffer.concat(buffers);
15
+ }
16
+ function isEpipe(error) {
17
+ return Boolean(error) && typeof error === "object" && error.code === "EPIPE";
18
+ }
19
+ function normalizeMinLength(value) {
20
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) return 0;
21
+ return Math.floor(value);
22
+ }
23
+ function createNodeStreamDestination(options) {
24
+ const minLength = normalizeMinLength(options.minLength);
25
+ const ignoreEpipe = options.ignoreEpipe ?? false;
26
+ const waiters = [];
27
+ const buffer = [];
28
+ const pendingPayloads = [];
29
+ let bufferedBytes = 0;
30
+ let pendingWrites = 0;
31
+ let pendingDrains = 0;
32
+ let lastError;
33
+ let lastContext;
34
+ let closed = false;
35
+ const reportInternalError = (error, operation) => {
36
+ if (ignoreEpipe && isEpipe(error)) return;
37
+ lastContext?.reportInternalError(error, {
38
+ phase: "transport",
39
+ transport: options.name,
40
+ operation
41
+ });
42
+ };
43
+ const settleWaitersIfIdle = () => {
44
+ if (pendingWrites + pendingDrains > 0) return;
45
+ if (waiters.length === 0) return;
46
+ const error = lastError;
47
+ lastError = void 0;
48
+ for (const waiter of waiters.splice(0)) if (error) waiter.reject(error);
49
+ else waiter.resolve();
50
+ };
51
+ const recordError = (error, operation) => {
52
+ if (ignoreEpipe && isEpipe(error)) {
53
+ closed = true;
54
+ return;
55
+ }
56
+ lastError = normalizeError(error);
57
+ reportInternalError(lastError, operation);
58
+ };
59
+ const onStreamError = (error) => {
60
+ recordError(error, "stream-error");
61
+ settleWaitersIfIdle();
62
+ };
63
+ const offError = () => options.stream.off?.("error", onStreamError);
64
+ options.stream.on?.("error", onStreamError);
65
+ const waitForDrain = () => {
66
+ if (!options.stream.once) return;
67
+ pendingDrains += 1;
68
+ options.stream.once("drain", () => {
69
+ pendingDrains -= 1;
70
+ settleWaitersIfIdle();
71
+ });
72
+ };
73
+ const removePending = (payload) => {
74
+ const index = pendingPayloads.indexOf(payload);
75
+ if (index >= 0) pendingPayloads.splice(index, 1);
76
+ };
77
+ const writeNow = (payload) => {
78
+ if (closed) return;
79
+ pendingPayloads.push(payload);
80
+ pendingWrites += 1;
81
+ try {
82
+ if (options.stream.write(payload, (error) => {
83
+ removePending(payload);
84
+ pendingWrites -= 1;
85
+ if (error) recordError(error, options.reportOperation ?? "write");
86
+ settleWaitersIfIdle();
87
+ }) === false) waitForDrain();
88
+ } catch (error) {
89
+ removePending(payload);
90
+ pendingWrites -= 1;
91
+ recordError(error, options.reportOperation ?? "write");
92
+ settleWaitersIfIdle();
93
+ }
94
+ };
95
+ const flushBuffered = () => {
96
+ if (buffer.length === 0) return;
97
+ const payload = coalescePayloads(buffer.splice(0));
98
+ bufferedBytes = 0;
99
+ writeNow(payload);
100
+ };
101
+ const destination = {
102
+ write(payload, context) {
103
+ if (closed) return;
104
+ lastContext = context;
105
+ if (minLength > 0) {
106
+ buffer.push(payload);
107
+ bufferedBytes += byteLength(payload);
108
+ if (bufferedBytes >= minLength) flushBuffered();
109
+ return;
110
+ }
111
+ writeNow(payload);
112
+ },
113
+ flush() {
114
+ flushBuffered();
115
+ if (pendingWrites + pendingDrains === 0) {
116
+ const error = lastError;
117
+ lastError = void 0;
118
+ if (error) return Promise.reject(error);
119
+ return Promise.resolve();
120
+ }
121
+ return new Promise((resolve, reject) => {
122
+ waiters.push({
123
+ resolve,
124
+ reject
125
+ });
126
+ });
127
+ },
128
+ flushSync() {
129
+ if (!options.syncWrite) {
130
+ flushBuffered();
131
+ return;
132
+ }
133
+ for (const payload of buffer.splice(0)) options.syncWrite(payload);
134
+ bufferedBytes = 0;
135
+ for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);
136
+ },
137
+ close() {
138
+ return destination.flush().then(() => new Promise((resolve, reject) => {
139
+ if (!options.stream.end) {
140
+ closed = true;
141
+ offError();
142
+ resolve();
143
+ return;
144
+ }
145
+ options.stream.end((error) => {
146
+ closed = true;
147
+ offError();
148
+ if (error && !(ignoreEpipe && isEpipe(error))) reject(error);
149
+ else resolve();
150
+ });
151
+ }), (error) => {
152
+ offError();
153
+ return Promise.reject(error);
154
+ });
155
+ },
156
+ closeSync() {
157
+ destination.flushSync();
158
+ closed = true;
159
+ offError();
160
+ }
161
+ };
162
+ return destination;
163
+ }
164
+ function createNodeFileDestination(options) {
165
+ const flags = options.flags ?? (options.append === false ? "w" : "a");
166
+ if (options.mkdir) (0, fs.mkdirSync)((0, path.dirname)(options.path), { recursive: true });
167
+ let fd;
168
+ const getFd = () => {
169
+ fd ??= (0, fs.openSync)(options.path, flags);
170
+ return fd;
171
+ };
172
+ const syncWrite = (payload) => {
173
+ if (typeof payload === "string") (0, fs.writeSync)(getFd(), payload);
174
+ else (0, fs.writeSync)(getFd(), payload);
175
+ };
176
+ if (options.sync) {
177
+ let closed = false;
178
+ const releaseSync = () => {
179
+ if (fd !== void 0) {
180
+ (0, fs.closeSync)(fd);
181
+ fd = void 0;
182
+ }
183
+ };
184
+ return {
185
+ write(payload) {
186
+ if (closed) return;
187
+ syncWrite(payload);
188
+ },
189
+ flush() {
190
+ return Promise.resolve();
191
+ },
192
+ flushSync() {},
193
+ close() {
194
+ releaseSync();
195
+ closed = true;
196
+ return Promise.resolve();
197
+ },
198
+ closeSync() {
199
+ releaseSync();
200
+ closed = true;
201
+ },
202
+ releaseSync
203
+ };
204
+ }
205
+ const stream = (0, fs.createWriteStream)(options.path, { flags });
206
+ const destination = createNodeStreamDestination({
207
+ name: options.name,
208
+ stream,
209
+ minLength: options.minLength,
210
+ syncWrite
211
+ });
212
+ const releaseSync = () => {
213
+ if (fd !== void 0) {
214
+ (0, fs.closeSync)(fd);
215
+ fd = void 0;
216
+ }
217
+ };
218
+ return {
219
+ ...destination,
220
+ stream,
221
+ close() {
222
+ return destination.close().finally(releaseSync);
223
+ },
224
+ closeSync() {
225
+ destination.closeSync();
226
+ releaseSync();
227
+ },
228
+ releaseSync
229
+ };
230
+ }
231
+ //#endregion
232
+ //#region src/file-transport.ts
233
+ function fileTransport(options) {
234
+ const codec = options.codec ?? (0, _loggerjs_core.ndjsonCodec)();
235
+ const destination = createNodeFileDestination({
236
+ name: options.name ?? "file",
237
+ path: options.path,
238
+ flags: options.flags,
239
+ append: options.append,
240
+ mkdir: options.mkdir,
241
+ sync: options.sync,
242
+ minLength: options.minLength
243
+ });
244
+ return {
245
+ name: options.name ?? "file",
246
+ minLevel: options.minLevel,
247
+ stream: destination.stream,
248
+ log(event, context) {
249
+ if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
250
+ destination.write(codec.encode(event), context);
251
+ },
252
+ flush: destination.flush,
253
+ flushSync: destination.flushSync,
254
+ close: destination.close
255
+ };
256
+ }
257
+ //#endregion
258
+ exports.fileTransport = fileTransport;
259
+
260
+ //# sourceMappingURL=file-transport.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transport.cjs","names":[],"sources":["../src/node-destination.ts","../src/file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WriteStream } from \"fs\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface FileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\nexport interface FileTransport extends Transport {\n stream?: WriteStream;\n flushSync: () => void;\n}\n\nexport function fileTransport(options: FileTransportOptions): FileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const destination = createNodeFileDestination({\n name: options.name ?? \"file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: options.sync,\n minLength: options.minLength,\n });\n\n return {\n name: options.name ?? \"file\",\n minLevel: options.minLevel,\n stream: destination.stream,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ import { ndjsonCodec, toLevelValue } from "@loggerjs/core";
2
+ import { closeSync, createWriteStream, mkdirSync, openSync, writeSync } from "fs";
3
+ import { dirname } from "path";
4
+ //#region src/node-destination.ts
5
+ const encoder = new TextEncoder();
6
+ const normalizeError = (error) => error instanceof Error ? error : new Error(String(error));
7
+ function byteLength(payload) {
8
+ return typeof payload === "string" ? encoder.encode(payload).byteLength : payload.byteLength;
9
+ }
10
+ function coalescePayloads(payloads) {
11
+ if (payloads.every((payload) => typeof payload === "string")) return payloads.join("");
12
+ const buffers = payloads.map((payload) => typeof payload === "string" ? Buffer.from(payload) : Buffer.from(payload));
13
+ return Buffer.concat(buffers);
14
+ }
15
+ function isEpipe(error) {
16
+ return Boolean(error) && typeof error === "object" && error.code === "EPIPE";
17
+ }
18
+ function normalizeMinLength(value) {
19
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) return 0;
20
+ return Math.floor(value);
21
+ }
22
+ function createNodeStreamDestination(options) {
23
+ const minLength = normalizeMinLength(options.minLength);
24
+ const ignoreEpipe = options.ignoreEpipe ?? false;
25
+ const waiters = [];
26
+ const buffer = [];
27
+ const pendingPayloads = [];
28
+ let bufferedBytes = 0;
29
+ let pendingWrites = 0;
30
+ let pendingDrains = 0;
31
+ let lastError;
32
+ let lastContext;
33
+ let closed = false;
34
+ const reportInternalError = (error, operation) => {
35
+ if (ignoreEpipe && isEpipe(error)) return;
36
+ lastContext?.reportInternalError(error, {
37
+ phase: "transport",
38
+ transport: options.name,
39
+ operation
40
+ });
41
+ };
42
+ const settleWaitersIfIdle = () => {
43
+ if (pendingWrites + pendingDrains > 0) return;
44
+ if (waiters.length === 0) return;
45
+ const error = lastError;
46
+ lastError = void 0;
47
+ for (const waiter of waiters.splice(0)) if (error) waiter.reject(error);
48
+ else waiter.resolve();
49
+ };
50
+ const recordError = (error, operation) => {
51
+ if (ignoreEpipe && isEpipe(error)) {
52
+ closed = true;
53
+ return;
54
+ }
55
+ lastError = normalizeError(error);
56
+ reportInternalError(lastError, operation);
57
+ };
58
+ const onStreamError = (error) => {
59
+ recordError(error, "stream-error");
60
+ settleWaitersIfIdle();
61
+ };
62
+ const offError = () => options.stream.off?.("error", onStreamError);
63
+ options.stream.on?.("error", onStreamError);
64
+ const waitForDrain = () => {
65
+ if (!options.stream.once) return;
66
+ pendingDrains += 1;
67
+ options.stream.once("drain", () => {
68
+ pendingDrains -= 1;
69
+ settleWaitersIfIdle();
70
+ });
71
+ };
72
+ const removePending = (payload) => {
73
+ const index = pendingPayloads.indexOf(payload);
74
+ if (index >= 0) pendingPayloads.splice(index, 1);
75
+ };
76
+ const writeNow = (payload) => {
77
+ if (closed) return;
78
+ pendingPayloads.push(payload);
79
+ pendingWrites += 1;
80
+ try {
81
+ if (options.stream.write(payload, (error) => {
82
+ removePending(payload);
83
+ pendingWrites -= 1;
84
+ if (error) recordError(error, options.reportOperation ?? "write");
85
+ settleWaitersIfIdle();
86
+ }) === false) waitForDrain();
87
+ } catch (error) {
88
+ removePending(payload);
89
+ pendingWrites -= 1;
90
+ recordError(error, options.reportOperation ?? "write");
91
+ settleWaitersIfIdle();
92
+ }
93
+ };
94
+ const flushBuffered = () => {
95
+ if (buffer.length === 0) return;
96
+ const payload = coalescePayloads(buffer.splice(0));
97
+ bufferedBytes = 0;
98
+ writeNow(payload);
99
+ };
100
+ const destination = {
101
+ write(payload, context) {
102
+ if (closed) return;
103
+ lastContext = context;
104
+ if (minLength > 0) {
105
+ buffer.push(payload);
106
+ bufferedBytes += byteLength(payload);
107
+ if (bufferedBytes >= minLength) flushBuffered();
108
+ return;
109
+ }
110
+ writeNow(payload);
111
+ },
112
+ flush() {
113
+ flushBuffered();
114
+ if (pendingWrites + pendingDrains === 0) {
115
+ const error = lastError;
116
+ lastError = void 0;
117
+ if (error) return Promise.reject(error);
118
+ return Promise.resolve();
119
+ }
120
+ return new Promise((resolve, reject) => {
121
+ waiters.push({
122
+ resolve,
123
+ reject
124
+ });
125
+ });
126
+ },
127
+ flushSync() {
128
+ if (!options.syncWrite) {
129
+ flushBuffered();
130
+ return;
131
+ }
132
+ for (const payload of buffer.splice(0)) options.syncWrite(payload);
133
+ bufferedBytes = 0;
134
+ for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);
135
+ },
136
+ close() {
137
+ return destination.flush().then(() => new Promise((resolve, reject) => {
138
+ if (!options.stream.end) {
139
+ closed = true;
140
+ offError();
141
+ resolve();
142
+ return;
143
+ }
144
+ options.stream.end((error) => {
145
+ closed = true;
146
+ offError();
147
+ if (error && !(ignoreEpipe && isEpipe(error))) reject(error);
148
+ else resolve();
149
+ });
150
+ }), (error) => {
151
+ offError();
152
+ return Promise.reject(error);
153
+ });
154
+ },
155
+ closeSync() {
156
+ destination.flushSync();
157
+ closed = true;
158
+ offError();
159
+ }
160
+ };
161
+ return destination;
162
+ }
163
+ function createNodeFileDestination(options) {
164
+ const flags = options.flags ?? (options.append === false ? "w" : "a");
165
+ if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });
166
+ let fd;
167
+ const getFd = () => {
168
+ fd ??= openSync(options.path, flags);
169
+ return fd;
170
+ };
171
+ const syncWrite = (payload) => {
172
+ if (typeof payload === "string") writeSync(getFd(), payload);
173
+ else writeSync(getFd(), payload);
174
+ };
175
+ if (options.sync) {
176
+ let closed = false;
177
+ const releaseSync = () => {
178
+ if (fd !== void 0) {
179
+ closeSync(fd);
180
+ fd = void 0;
181
+ }
182
+ };
183
+ return {
184
+ write(payload) {
185
+ if (closed) return;
186
+ syncWrite(payload);
187
+ },
188
+ flush() {
189
+ return Promise.resolve();
190
+ },
191
+ flushSync() {},
192
+ close() {
193
+ releaseSync();
194
+ closed = true;
195
+ return Promise.resolve();
196
+ },
197
+ closeSync() {
198
+ releaseSync();
199
+ closed = true;
200
+ },
201
+ releaseSync
202
+ };
203
+ }
204
+ const stream = createWriteStream(options.path, { flags });
205
+ const destination = createNodeStreamDestination({
206
+ name: options.name,
207
+ stream,
208
+ minLength: options.minLength,
209
+ syncWrite
210
+ });
211
+ const releaseSync = () => {
212
+ if (fd !== void 0) {
213
+ closeSync(fd);
214
+ fd = void 0;
215
+ }
216
+ };
217
+ return {
218
+ ...destination,
219
+ stream,
220
+ close() {
221
+ return destination.close().finally(releaseSync);
222
+ },
223
+ closeSync() {
224
+ destination.closeSync();
225
+ releaseSync();
226
+ },
227
+ releaseSync
228
+ };
229
+ }
230
+ //#endregion
231
+ //#region src/file-transport.ts
232
+ function fileTransport(options) {
233
+ const codec = options.codec ?? ndjsonCodec();
234
+ const destination = createNodeFileDestination({
235
+ name: options.name ?? "file",
236
+ path: options.path,
237
+ flags: options.flags,
238
+ append: options.append,
239
+ mkdir: options.mkdir,
240
+ sync: options.sync,
241
+ minLength: options.minLength
242
+ });
243
+ return {
244
+ name: options.name ?? "file",
245
+ minLevel: options.minLevel,
246
+ stream: destination.stream,
247
+ log(event, context) {
248
+ if (options.minLevel !== void 0 && event.level < toLevelValue(options.minLevel)) return;
249
+ destination.write(codec.encode(event), context);
250
+ },
251
+ flush: destination.flush,
252
+ flushSync: destination.flushSync,
253
+ close: destination.close
254
+ };
255
+ }
256
+ //#endregion
257
+ export { fileTransport };
258
+
259
+ //# sourceMappingURL=file-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transport.js","names":[],"sources":["../src/node-destination.ts","../src/file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WriteStream } from \"fs\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface FileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\nexport interface FileTransport extends Transport {\n stream?: WriteStream;\n flushSync: () => void;\n}\n\nexport function fileTransport(options: FileTransportOptions): FileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const destination = createNodeFileDestination({\n name: options.name ?? \"file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: options.sync,\n minLength: options.minLength,\n });\n\n return {\n name: options.name ?? \"file\",\n minLevel: options.minLevel,\n stream: destination.stream,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _loggerjs_core = require("@loggerjs/core");
3
+ //#region src/hapi-integration.ts
4
+ function responseStatus(response) {
5
+ if (!response) return 200;
6
+ if ("statusCode" in response && typeof response.statusCode === "number") return response.statusCode;
7
+ if ("output" in response && typeof response.output?.statusCode === "number") return response.output.statusCode;
8
+ return 200;
9
+ }
10
+ function levelFor(status) {
11
+ if (status >= 500) return "error";
12
+ if (status >= 400) return "warn";
13
+ return "info";
14
+ }
15
+ function hapiIntegration(logger, options = {}) {
16
+ const minStatus = options.minStatus ?? 400;
17
+ const captureAll = options.captureAll ?? false;
18
+ const bindContext = options.bindContext ?? true;
19
+ const getLevel = options.level ?? levelFor;
20
+ return {
21
+ name: "loggerjs-hapi",
22
+ register(server) {
23
+ server.ext("onRequest", (request, h) => {
24
+ const context = {
25
+ httpMethod: (request.method ?? "GET").toUpperCase(),
26
+ httpUrl: request.url?.pathname ?? request.path ?? "/",
27
+ requestId: options.getRequestId?.(request) ?? request.info?.id,
28
+ ...options.context?.(request)
29
+ };
30
+ request.app = {
31
+ ...request.app,
32
+ loggerjsStartedAt: Date.now()
33
+ };
34
+ if (bindContext) return (0, _loggerjs_core.withContext)(context, () => h.continue);
35
+ return h.continue;
36
+ });
37
+ server.events?.on?.("response", (request) => {
38
+ const status = responseStatus(request.response);
39
+ if (!captureAll && status < minStatus) return;
40
+ const method = (request.method ?? "GET").toUpperCase();
41
+ const url = request.url?.pathname ?? request.path ?? "/";
42
+ const requestId = options.getRequestId?.(request) ?? request.info?.id;
43
+ const started = request.app?.loggerjsStartedAt;
44
+ logger.log(getLevel(status, request), `Hapi ${status} ${method} ${url}`, { http: {
45
+ framework: "hapi",
46
+ kind: "hapi",
47
+ method,
48
+ url,
49
+ status,
50
+ durationMs: started ? Date.now() - started : void 0,
51
+ requestId,
52
+ remoteAddress: request.info?.remoteAddress
53
+ } });
54
+ });
55
+ }
56
+ };
57
+ }
58
+ //#endregion
59
+ exports.hapiIntegration = hapiIntegration;
60
+
61
+ //# sourceMappingURL=hapi-integration.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hapi-integration.cjs","names":[],"sources":["../src/hapi-integration.ts"],"sourcesContent":["import { withContext, type LoggerLevel, type LoggerLike, type LogData } from \"@loggerjs/core\";\n\nexport interface HapiRequestLike {\n method?: string;\n path?: string;\n url?: { pathname?: string };\n info?: { id?: string; received?: number; remoteAddress?: string };\n response?: { statusCode?: number } | { isBoom?: boolean; output?: { statusCode?: number } };\n app?: Record<string, unknown>;\n}\n\nexport interface HapiToolkitLike {\n continue: unknown;\n}\n\nexport interface HapiServerLike {\n ext: (\n event: \"onRequest\",\n handler: (request: HapiRequestLike, h: HapiToolkitLike) => unknown,\n ) => unknown;\n events?: {\n on?: (event: \"response\", handler: (request: HapiRequestLike) => void) => unknown;\n };\n}\n\nexport interface HapiIntegrationOptions {\n minStatus?: number;\n captureAll?: boolean;\n bindContext?: boolean;\n getRequestId?: (request: HapiRequestLike) => string | undefined;\n context?: (request: HapiRequestLike) => Record<string, unknown> | undefined;\n level?: (status: number, request: HapiRequestLike) => LoggerLevel;\n}\n\nfunction responseStatus(response: HapiRequestLike[\"response\"]): number {\n if (!response) return 200;\n if (\"statusCode\" in response && typeof response.statusCode === \"number\")\n return response.statusCode;\n if (\"output\" in response && typeof response.output?.statusCode === \"number\") {\n return response.output.statusCode;\n }\n return 200;\n}\n\nfunction levelFor(status: number): LoggerLevel {\n if (status >= 500) return \"error\";\n if (status >= 400) return \"warn\";\n return \"info\";\n}\n\nexport function hapiIntegration(logger: LoggerLike, options: HapiIntegrationOptions = {}) {\n const minStatus = options.minStatus ?? 400;\n const captureAll = options.captureAll ?? false;\n const bindContext = options.bindContext ?? true;\n const getLevel = options.level ?? levelFor;\n\n return {\n name: \"loggerjs-hapi\",\n register(server: HapiServerLike) {\n server.ext(\"onRequest\", (request, h) => {\n const method = (request.method ?? \"GET\").toUpperCase();\n const url = request.url?.pathname ?? request.path ?? \"/\";\n const requestId = options.getRequestId?.(request) ?? request.info?.id;\n const context = {\n httpMethod: method,\n httpUrl: url,\n requestId,\n ...options.context?.(request),\n };\n request.app = { ...request.app, loggerjsStartedAt: Date.now() };\n if (bindContext) return withContext(context, () => h.continue);\n return h.continue;\n });\n\n server.events?.on?.(\"response\", (request) => {\n const status = responseStatus(request.response);\n if (!captureAll && status < minStatus) return;\n const method = (request.method ?? \"GET\").toUpperCase();\n const url = request.url?.pathname ?? request.path ?? \"/\";\n const requestId = options.getRequestId?.(request) ?? request.info?.id;\n const started = request.app?.loggerjsStartedAt as number | undefined;\n logger.log(getLevel(status, request), `Hapi ${status} ${method} ${url}`, {\n http: {\n framework: \"hapi\",\n kind: \"hapi\",\n method,\n url,\n status,\n durationMs: started ? Date.now() - started : undefined,\n requestId,\n remoteAddress: request.info?.remoteAddress,\n },\n } as LogData);\n });\n },\n };\n}\n"],"mappings":";;;AAkCA,SAAS,eAAe,UAA+C;CACrE,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI,gBAAgB,YAAY,OAAO,SAAS,eAAe,UAC7D,OAAO,SAAS;CAClB,IAAI,YAAY,YAAY,OAAO,SAAS,QAAQ,eAAe,UACjE,OAAO,SAAS,OAAO;CAEzB,OAAO;AACT;AAEA,SAAS,SAAS,QAA6B;CAC7C,IAAI,UAAU,KAAK,OAAO;CAC1B,IAAI,UAAU,KAAK,OAAO;CAC1B,OAAO;AACT;AAEA,SAAgB,gBAAgB,QAAoB,UAAkC,CAAC,GAAG;CACxF,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,WAAW,QAAQ,SAAS;CAElC,OAAO;EACL,MAAM;EACN,SAAS,QAAwB;GAC/B,OAAO,IAAI,cAAc,SAAS,MAAM;IAItC,MAAM,UAAU;KACd,aAJc,QAAQ,UAAU,MAAA,CAAO,YAItB;KACjB,SAJU,QAAQ,KAAK,YAAY,QAAQ,QAAQ;KAKnD,WAJgB,QAAQ,eAAe,OAAO,KAAK,QAAQ,MAAM;KAKjE,GAAG,QAAQ,UAAU,OAAO;IAC9B;IACA,QAAQ,MAAM;KAAE,GAAG,QAAQ;KAAK,mBAAmB,KAAK,IAAI;IAAE;IAC9D,IAAI,aAAa,QAAA,GAAA,eAAA,YAAA,CAAmB,eAAe,EAAE,QAAQ;IAC7D,OAAO,EAAE;GACX,CAAC;GAED,OAAO,QAAQ,KAAK,aAAa,YAAY;IAC3C,MAAM,SAAS,eAAe,QAAQ,QAAQ;IAC9C,IAAI,CAAC,cAAc,SAAS,WAAW;IACvC,MAAM,UAAU,QAAQ,UAAU,MAAA,CAAO,YAAY;IACrD,MAAM,MAAM,QAAQ,KAAK,YAAY,QAAQ,QAAQ;IACrD,MAAM,YAAY,QAAQ,eAAe,OAAO,KAAK,QAAQ,MAAM;IACnE,MAAM,UAAU,QAAQ,KAAK;IAC7B,OAAO,IAAI,SAAS,QAAQ,OAAO,GAAG,QAAQ,OAAO,GAAG,OAAO,GAAG,OAAO,EACvE,MAAM;KACJ,WAAW;KACX,MAAM;KACN;KACA;KACA;KACA,YAAY,UAAU,KAAK,IAAI,IAAI,UAAU,KAAA;KAC7C;KACA,eAAe,QAAQ,MAAM;IAC/B,EACF,CAAY;GACd,CAAC;EACH;CACF;AACF"}