@logtape/elysia 2.4.0-dev.879 → 2.4.0-dev.881
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 +54 -2
- package/dist/compat.cjs +279 -0
- package/dist/compat.js +276 -0
- package/dist/compat.js.map +1 -0
- package/dist/mod.cjs +20 -11
- package/dist/mod.d.cts.map +1 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +20 -11
- package/dist/mod.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -29,6 +29,55 @@ bun add @logtape/elysia # for Bun
|
|
|
29
29
|
~~~~
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
Compatibility
|
|
33
|
+
-------------
|
|
34
|
+
|
|
35
|
+
Since LogTape 2.4.0, this adapter supports Elysia 1.4 and Elysia 2 starting
|
|
36
|
+
with *2.0.0-beta.12*. Elysia 2 is still in beta. The `elysiaLogger()` options
|
|
37
|
+
are the same for both versions; application routes and hooks use the native
|
|
38
|
+
API of the installed Elysia version.
|
|
39
|
+
|
|
40
|
+
The application and adapter must resolve the same Elysia package copy and
|
|
41
|
+
module format. Use ESM throughout or CommonJS throughout, including imported
|
|
42
|
+
child plugins. Local request context relies on Elysia 2's internal hook and
|
|
43
|
+
route representation, which the compatibility tests check against the pinned
|
|
44
|
+
beta version.
|
|
45
|
+
|
|
46
|
+
When using `scope: "local"` with request context enabled, the adapter registers
|
|
47
|
+
an internal macro in Elysia 2. Elysia 2 rejects adding macros to the parent app
|
|
48
|
+
from an async plugin function after `await`. Register the logger before that
|
|
49
|
+
`await`, or return a separate logger plugin for Elysia to merge:
|
|
50
|
+
|
|
51
|
+
~~~~ typescript
|
|
52
|
+
const app = new Elysia().use(async () => {
|
|
53
|
+
await initializeDatabase();
|
|
54
|
+
return elysiaLogger({ scope: "local", context: true })
|
|
55
|
+
.get("/", () => "Hello");
|
|
56
|
+
});
|
|
57
|
+
~~~~
|
|
58
|
+
|
|
59
|
+
Do not use `return app.use(elysiaLogger(...))` after `await` inside
|
|
60
|
+
`.use(async (app) => { ... })` with these options. Returning the separate
|
|
61
|
+
plugin lets Elysia merge it in its ordered async plugin queue. This restriction
|
|
62
|
+
does not affect `await` inside request handlers.
|
|
63
|
+
|
|
64
|
+
For Elysia 2 on Deno, add these overrides to your *deno.json* `imports` map,
|
|
65
|
+
alongside the LogTape imports. The exact npm specifiers also redirect the
|
|
66
|
+
adapter's JSR dependencies, so the application and adapter share Elysia:
|
|
67
|
+
|
|
68
|
+
~~~~ json
|
|
69
|
+
{
|
|
70
|
+
"imports": {
|
|
71
|
+
"elysia": "npm:elysia@2.0.0-beta.12",
|
|
72
|
+
"npm:elysia@^1.4.0": "npm:elysia@2.0.0-beta.12",
|
|
73
|
+
"npm:elysia@^1.4.0/utils": "npm:elysia@2.0.0-beta.12/utils"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
~~~~
|
|
77
|
+
|
|
78
|
+
Keep all three mappings on the same Elysia version when upgrading.
|
|
79
|
+
|
|
80
|
+
|
|
32
81
|
Usage
|
|
33
82
|
-----
|
|
34
83
|
|
|
@@ -122,6 +171,7 @@ Elysia supports plugin scoping to control how lifecycle hooks propagate:
|
|
|
122
171
|
|
|
123
172
|
- `"global"`: Hooks apply to all routes in the application (default)
|
|
124
173
|
- `"scoped"`: Hooks apply to the parent instance where the plugin is used
|
|
174
|
+
(Elysia 2 calls this scope `"plugin"`)
|
|
125
175
|
- `"local"`: Hooks only apply within the plugin itself
|
|
126
176
|
|
|
127
177
|
|
|
@@ -168,8 +218,10 @@ Error logging
|
|
|
168
218
|
-------------
|
|
169
219
|
|
|
170
220
|
The plugin automatically logs errors at the error level using Elysia's
|
|
171
|
-
`onError` hook
|
|
172
|
-
in addition to standard request properties.
|
|
221
|
+
`onError` hook in Elysia 1 or `error` hook in Elysia 2. Error logs include
|
|
222
|
+
`errorMessage` in addition to standard request properties. `errorCode` is
|
|
223
|
+
Elysia 1's context code, or Elysia 2's `error.code` when it is a string or
|
|
224
|
+
number; it is omitted when Elysia 2 supplies no code.
|
|
173
225
|
|
|
174
226
|
|
|
175
227
|
Structured logging output
|
package/dist/compat.cjs
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const elysia = require_rolldown_runtime.__toESM(require("elysia"));
|
|
3
|
+
const elysia_utils = require_rolldown_runtime.__toESM(require("elysia/utils"));
|
|
4
|
+
|
|
5
|
+
//#region src/compat.ts
|
|
6
|
+
/** The adapter only needs these lifecycle registration capabilities. */
|
|
7
|
+
function createCompatibility(instance) {
|
|
8
|
+
const plugin = instance;
|
|
9
|
+
const v2 = typeof plugin.onRequest !== "function" && typeof plugin.request === "function";
|
|
10
|
+
const register = (event, callback) => {
|
|
11
|
+
const name = v2 ? event[0].toLowerCase() + event.slice(1) : `on${event}`;
|
|
12
|
+
const method = plugin[name];
|
|
13
|
+
if (typeof method !== "function") throw unsupported();
|
|
14
|
+
method.call(instance, (raw) => callback(v2 ? normalizeContext(raw, event === "Error") : raw));
|
|
15
|
+
};
|
|
16
|
+
const as = (scope) => {
|
|
17
|
+
plugin.as.call(instance, v2 && scope === "scoped" ? "plugin" : scope);
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
v2,
|
|
21
|
+
register,
|
|
22
|
+
as
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function unsupported() {
|
|
26
|
+
return new Error("@logtape/elysia: unsupported Elysia 2 internals for local request context. Use a supported Elysia version and the same Elysia copy and module format for the application and plugin (including utils).");
|
|
27
|
+
}
|
|
28
|
+
/** Elysia 2 shares a frozen default header map until its first write. */
|
|
29
|
+
function materializeHeaders(set) {
|
|
30
|
+
if (set.headers["\0"] === set.headers) set.headers = Object.assign(Object.create(null), set.headers);
|
|
31
|
+
}
|
|
32
|
+
function numericStatus(value, fallback) {
|
|
33
|
+
if (typeof value === "number") return value;
|
|
34
|
+
if (typeof value === "string") return elysia.StatusMap[value] ?? fallback;
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
function normalizeContext(raw, error) {
|
|
38
|
+
const rawSet = raw.set;
|
|
39
|
+
materializeHeaders(rawSet);
|
|
40
|
+
const response = raw.responseValue;
|
|
41
|
+
const statusClass = elysia.ElysiaStatus;
|
|
42
|
+
const responseStatus = !error && (response instanceof Response || typeof statusClass === "function" && response instanceof statusClass) ? response.status : void 0;
|
|
43
|
+
const set = Object.create(rawSet, {
|
|
44
|
+
status: {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
get: () => responseStatus ?? numericStatus(rawSet.status, error ? 500 : 200),
|
|
47
|
+
set: (value) => {
|
|
48
|
+
rawSet.status = value;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
headers: {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
get: () => {
|
|
54
|
+
materializeHeaders(rawSet);
|
|
55
|
+
return rawSet.headers;
|
|
56
|
+
},
|
|
57
|
+
set: (value) => {
|
|
58
|
+
rawSet.headers = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return Object.assign(Object.create(raw), { set });
|
|
63
|
+
}
|
|
64
|
+
function nativeErrorCode(error) {
|
|
65
|
+
if (error == null || typeof error !== "object") return void 0;
|
|
66
|
+
const code = error.code;
|
|
67
|
+
return typeof code === "string" || typeof code === "number" ? code : void 0;
|
|
68
|
+
}
|
|
69
|
+
const hookKeys = [
|
|
70
|
+
"beforeHandle",
|
|
71
|
+
"afterHandle",
|
|
72
|
+
"parse",
|
|
73
|
+
"transform",
|
|
74
|
+
"derive",
|
|
75
|
+
"mapDerive",
|
|
76
|
+
"mapResponse",
|
|
77
|
+
"afterResponse",
|
|
78
|
+
"error"
|
|
79
|
+
];
|
|
80
|
+
const routeMethods = [
|
|
81
|
+
"get",
|
|
82
|
+
"post",
|
|
83
|
+
"put",
|
|
84
|
+
"patch",
|
|
85
|
+
"delete",
|
|
86
|
+
"options",
|
|
87
|
+
"head",
|
|
88
|
+
"all",
|
|
89
|
+
"method",
|
|
90
|
+
"use",
|
|
91
|
+
"group",
|
|
92
|
+
"guard",
|
|
93
|
+
"mount"
|
|
94
|
+
];
|
|
95
|
+
/**
|
|
96
|
+
* Elysia 2 has no around-handler lifecycle. Its declared route tuples and
|
|
97
|
+
* immutable hook-chain links are private integration points, verified by the
|
|
98
|
+
* pinned compatibility matrix. Only this logger's copied rows are rewritten;
|
|
99
|
+
* an imported child must remain safe to reuse in another application.
|
|
100
|
+
*/
|
|
101
|
+
function wrapV2LocalContext(instance, wrapCallback, macroName) {
|
|
102
|
+
const plugin = instance;
|
|
103
|
+
const origins = elysia_utils.fnOrigin;
|
|
104
|
+
if (!(origins instanceof WeakMap)) throw unsupported();
|
|
105
|
+
const probe = () => {};
|
|
106
|
+
const probeApp = new elysia.Elysia({ name: "@logtape/elysia/probe" });
|
|
107
|
+
if (typeof probeApp.beforeHandle !== "function") throw unsupported();
|
|
108
|
+
probeApp.beforeHandle(probe);
|
|
109
|
+
if (!origins.has(probe)) throw unsupported();
|
|
110
|
+
const replacements = /* @__PURE__ */ new WeakMap();
|
|
111
|
+
const wrap = (value) => {
|
|
112
|
+
if (Array.isArray(value)) {
|
|
113
|
+
const present = new Set(value);
|
|
114
|
+
return value.filter((item) => {
|
|
115
|
+
if (typeof item !== "function") return true;
|
|
116
|
+
const replacement = replacements.get(item);
|
|
117
|
+
return replacement == null || !present.has(replacement);
|
|
118
|
+
}).map(wrap);
|
|
119
|
+
}
|
|
120
|
+
const wrapped = wrapCallback(value);
|
|
121
|
+
if (typeof value === "function" && typeof wrapped === "function" && value !== wrapped) replacements.set(value, wrapped);
|
|
122
|
+
if (typeof value === "function" && typeof wrapped === "function" && origins.has(value)) origins.set(wrapped, origins.get(value));
|
|
123
|
+
return wrapped;
|
|
124
|
+
};
|
|
125
|
+
const resolveParse = (value) => {
|
|
126
|
+
if (Array.isArray(value)) return value.map(resolveParse);
|
|
127
|
+
const parsers = plugin["~ext"]?.parser;
|
|
128
|
+
return wrap(typeof value === "string" ? parsers?.[value] ?? value : value);
|
|
129
|
+
};
|
|
130
|
+
const hooks = (value, final = false) => {
|
|
131
|
+
if (value == null) return final ? value : { [macroName]: true };
|
|
132
|
+
if (typeof value !== "object" || Array.isArray(value)) throw unsupported();
|
|
133
|
+
const source = value;
|
|
134
|
+
const copy = { ...source };
|
|
135
|
+
for (const key of hookKeys) if (final && key in source) copy[key] = key === "parse" ? resolveParse(source[key]) : wrap(source[key]);
|
|
136
|
+
if (final && "~deriveEntries" in source) {
|
|
137
|
+
if (!Array.isArray(source["~deriveEntries"])) throw unsupported();
|
|
138
|
+
copy["~deriveEntries"] = source["~deriveEntries"].map((entry) => Array.isArray(entry) ? [wrap(entry[0]), ...entry.slice(1)] : wrap(entry));
|
|
139
|
+
}
|
|
140
|
+
delete copy[macroName];
|
|
141
|
+
if (!final) copy[macroName] = true;
|
|
142
|
+
return copy;
|
|
143
|
+
};
|
|
144
|
+
if (typeof plugin.macro !== "function") throw unsupported();
|
|
145
|
+
plugin.macro({ [macroName]: { introspect(resolved) {
|
|
146
|
+
Object.assign(resolved, hooks(resolved, true));
|
|
147
|
+
} } });
|
|
148
|
+
const protectedNodes = /* @__PURE__ */ new WeakSet();
|
|
149
|
+
const protect = (value) => {
|
|
150
|
+
if (value == null) return;
|
|
151
|
+
if (typeof value !== "object") throw unsupported();
|
|
152
|
+
if (protectedNodes.has(value)) return;
|
|
153
|
+
protectedNodes.add(value);
|
|
154
|
+
const node = value;
|
|
155
|
+
protect(node.parent);
|
|
156
|
+
protect(node.over);
|
|
157
|
+
};
|
|
158
|
+
const chain = (value, memo = /* @__PURE__ */ new WeakMap(), final = false) => {
|
|
159
|
+
if (value == null) return value;
|
|
160
|
+
if (typeof value !== "object") throw unsupported();
|
|
161
|
+
if (protectedNodes.has(value)) return value;
|
|
162
|
+
const node = value;
|
|
163
|
+
const existing = memo.get(value);
|
|
164
|
+
if (existing != null) return existing;
|
|
165
|
+
const copy = { ...node };
|
|
166
|
+
memo.set(value, copy);
|
|
167
|
+
if ("combine" in node) {
|
|
168
|
+
copy.combine = chain(node.combine, memo, final);
|
|
169
|
+
copy.over = chain(node.over, memo, final);
|
|
170
|
+
} else {
|
|
171
|
+
if (!("added" in node)) throw unsupported();
|
|
172
|
+
copy.added = node.propagated === true ? node.added : hooks(node.added, final);
|
|
173
|
+
copy.parent = chain(node.parent, memo, final);
|
|
174
|
+
}
|
|
175
|
+
if (node.owner?.["~scopeChild"] === true) copy.owner = owner(node.owner);
|
|
176
|
+
return copy;
|
|
177
|
+
};
|
|
178
|
+
const owners = /* @__PURE__ */ new WeakMap();
|
|
179
|
+
const projections = /* @__PURE__ */ new WeakSet();
|
|
180
|
+
const owner = (source) => {
|
|
181
|
+
if (source === instance) return source;
|
|
182
|
+
if (!(source instanceof elysia.Elysia)) throw unsupported();
|
|
183
|
+
if (projections.has(source)) return source;
|
|
184
|
+
const cached = owners.get(source);
|
|
185
|
+
if (cached != null) return cached;
|
|
186
|
+
const nativeApplyMacro = plugin["~applyMacro"];
|
|
187
|
+
if (typeof nativeApplyMacro !== "function") throw unsupported();
|
|
188
|
+
const scopeViews = /* @__PURE__ */ new WeakMap();
|
|
189
|
+
const scopeView = (target) => {
|
|
190
|
+
const cached$1 = scopeViews.get(target);
|
|
191
|
+
if (cached$1 != null) return cached$1;
|
|
192
|
+
const view = new Proxy(target, { get(target$1, key) {
|
|
193
|
+
const value = Reflect.get(target$1, key, target$1);
|
|
194
|
+
if (key === "~hookChain") return chain(value, /* @__PURE__ */ new WeakMap(), true);
|
|
195
|
+
if (source["~scopeChild"] !== true) return value;
|
|
196
|
+
if (key === "~generation" && value != null) return scopeView(value);
|
|
197
|
+
if (key === "~ext" && value?.macro != null) return {
|
|
198
|
+
...value,
|
|
199
|
+
macro: new Proxy(value.macro, { has: (table, name) => name !== macroName && Reflect.has(table, name) })
|
|
200
|
+
};
|
|
201
|
+
if (key === "~applyMacro") return (...args) => nativeApplyMacro.apply(view, args);
|
|
202
|
+
return value;
|
|
203
|
+
} });
|
|
204
|
+
scopeViews.set(target, view);
|
|
205
|
+
return view;
|
|
206
|
+
};
|
|
207
|
+
const projection = scopeView(source);
|
|
208
|
+
owners.set(source, projection);
|
|
209
|
+
projections.add(projection);
|
|
210
|
+
return projection;
|
|
211
|
+
};
|
|
212
|
+
const rows = () => {
|
|
213
|
+
const value = plugin.declaredRoutes;
|
|
214
|
+
if (value === void 0) return [];
|
|
215
|
+
if (!Array.isArray(value)) throw unsupported();
|
|
216
|
+
return value;
|
|
217
|
+
};
|
|
218
|
+
for (const name of routeMethods) {
|
|
219
|
+
const original = plugin[name];
|
|
220
|
+
if (typeof original !== "function") throw unsupported();
|
|
221
|
+
plugin[name] = function(...args) {
|
|
222
|
+
protect(plugin["~hookChain"]);
|
|
223
|
+
const start = rows().length;
|
|
224
|
+
const result = original.apply(this, args);
|
|
225
|
+
const routes = rows();
|
|
226
|
+
for (let i = start; i < routes.length; i++) {
|
|
227
|
+
const source = routes[i];
|
|
228
|
+
if (!Array.isArray(source) || source.length < 4) throw unsupported();
|
|
229
|
+
if (source[0] === "WS" || source[2] != null && source[2]["~mount"]) continue;
|
|
230
|
+
const copy = source.slice();
|
|
231
|
+
copy[2] = wrap(source[2]);
|
|
232
|
+
copy[3] = owner(source[3]);
|
|
233
|
+
copy[4] = hooks(source[4]);
|
|
234
|
+
copy[5] = chain(source[5]);
|
|
235
|
+
copy[6] = chain(source[6]);
|
|
236
|
+
if (source[7]?.["~scopeChild"] === true) copy[7] = owner(source[7]);
|
|
237
|
+
routes[i] = copy;
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return () => {
|
|
243
|
+
protect(plugin["~hookChain"]);
|
|
244
|
+
for (const name of [...hookKeys, "guard"]) {
|
|
245
|
+
const original = plugin[name];
|
|
246
|
+
if (typeof original !== "function") throw unsupported();
|
|
247
|
+
plugin[name] = function(...args) {
|
|
248
|
+
const mapped = args.slice();
|
|
249
|
+
if (name === "guard") {
|
|
250
|
+
if (typeof args.at(-1) !== "function") {
|
|
251
|
+
const index = typeof args[0] === "string" ? 1 : 0;
|
|
252
|
+
const hook = hooks(args[index]);
|
|
253
|
+
if (hook.error != null) hook.error = wrap(hook.error);
|
|
254
|
+
mapped[index] = hook;
|
|
255
|
+
}
|
|
256
|
+
} else if (name === "parse") {
|
|
257
|
+
const index = args.length > 1 && typeof args[0] === "string" ? 1 : 0;
|
|
258
|
+
mapped[index] = resolveParse(args[index]);
|
|
259
|
+
} else if (name === "error") {
|
|
260
|
+
if (args.length === 1 && typeof args[0] === "object") return original.apply(this, args);
|
|
261
|
+
const index = args.length - 1;
|
|
262
|
+
mapped[index] = wrap(args[index]);
|
|
263
|
+
} else {
|
|
264
|
+
const index = typeof args[0] === "string" ? 1 : 0;
|
|
265
|
+
mapped[index] = wrap(args[index]);
|
|
266
|
+
}
|
|
267
|
+
const result = original.apply(this, mapped);
|
|
268
|
+
protect(plugin["~hookChain"]);
|
|
269
|
+
return result;
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
exports.createCompatibility = createCompatibility;
|
|
277
|
+
exports.materializeHeaders = materializeHeaders;
|
|
278
|
+
exports.nativeErrorCode = nativeErrorCode;
|
|
279
|
+
exports.wrapV2LocalContext = wrapV2LocalContext;
|
package/dist/compat.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import * as elysia from "elysia";
|
|
2
|
+
import * as utils from "elysia/utils";
|
|
3
|
+
|
|
4
|
+
//#region src/compat.ts
|
|
5
|
+
/** The adapter only needs these lifecycle registration capabilities. */
|
|
6
|
+
function createCompatibility(instance) {
|
|
7
|
+
const plugin = instance;
|
|
8
|
+
const v2 = typeof plugin.onRequest !== "function" && typeof plugin.request === "function";
|
|
9
|
+
const register = (event, callback) => {
|
|
10
|
+
const name = v2 ? event[0].toLowerCase() + event.slice(1) : `on${event}`;
|
|
11
|
+
const method = plugin[name];
|
|
12
|
+
if (typeof method !== "function") throw unsupported();
|
|
13
|
+
method.call(instance, (raw) => callback(v2 ? normalizeContext(raw, event === "Error") : raw));
|
|
14
|
+
};
|
|
15
|
+
const as = (scope) => {
|
|
16
|
+
plugin.as.call(instance, v2 && scope === "scoped" ? "plugin" : scope);
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
v2,
|
|
20
|
+
register,
|
|
21
|
+
as
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function unsupported() {
|
|
25
|
+
return new Error("@logtape/elysia: unsupported Elysia 2 internals for local request context. Use a supported Elysia version and the same Elysia copy and module format for the application and plugin (including utils).");
|
|
26
|
+
}
|
|
27
|
+
/** Elysia 2 shares a frozen default header map until its first write. */
|
|
28
|
+
function materializeHeaders(set) {
|
|
29
|
+
if (set.headers["\0"] === set.headers) set.headers = Object.assign(Object.create(null), set.headers);
|
|
30
|
+
}
|
|
31
|
+
function numericStatus(value, fallback) {
|
|
32
|
+
if (typeof value === "number") return value;
|
|
33
|
+
if (typeof value === "string") return elysia.StatusMap[value] ?? fallback;
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
function normalizeContext(raw, error) {
|
|
37
|
+
const rawSet = raw.set;
|
|
38
|
+
materializeHeaders(rawSet);
|
|
39
|
+
const response = raw.responseValue;
|
|
40
|
+
const statusClass = elysia.ElysiaStatus;
|
|
41
|
+
const responseStatus = !error && (response instanceof Response || typeof statusClass === "function" && response instanceof statusClass) ? response.status : void 0;
|
|
42
|
+
const set = Object.create(rawSet, {
|
|
43
|
+
status: {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: () => responseStatus ?? numericStatus(rawSet.status, error ? 500 : 200),
|
|
46
|
+
set: (value) => {
|
|
47
|
+
rawSet.status = value;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
headers: {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
get: () => {
|
|
53
|
+
materializeHeaders(rawSet);
|
|
54
|
+
return rawSet.headers;
|
|
55
|
+
},
|
|
56
|
+
set: (value) => {
|
|
57
|
+
rawSet.headers = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return Object.assign(Object.create(raw), { set });
|
|
62
|
+
}
|
|
63
|
+
function nativeErrorCode(error) {
|
|
64
|
+
if (error == null || typeof error !== "object") return void 0;
|
|
65
|
+
const code = error.code;
|
|
66
|
+
return typeof code === "string" || typeof code === "number" ? code : void 0;
|
|
67
|
+
}
|
|
68
|
+
const hookKeys = [
|
|
69
|
+
"beforeHandle",
|
|
70
|
+
"afterHandle",
|
|
71
|
+
"parse",
|
|
72
|
+
"transform",
|
|
73
|
+
"derive",
|
|
74
|
+
"mapDerive",
|
|
75
|
+
"mapResponse",
|
|
76
|
+
"afterResponse",
|
|
77
|
+
"error"
|
|
78
|
+
];
|
|
79
|
+
const routeMethods = [
|
|
80
|
+
"get",
|
|
81
|
+
"post",
|
|
82
|
+
"put",
|
|
83
|
+
"patch",
|
|
84
|
+
"delete",
|
|
85
|
+
"options",
|
|
86
|
+
"head",
|
|
87
|
+
"all",
|
|
88
|
+
"method",
|
|
89
|
+
"use",
|
|
90
|
+
"group",
|
|
91
|
+
"guard",
|
|
92
|
+
"mount"
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* Elysia 2 has no around-handler lifecycle. Its declared route tuples and
|
|
96
|
+
* immutable hook-chain links are private integration points, verified by the
|
|
97
|
+
* pinned compatibility matrix. Only this logger's copied rows are rewritten;
|
|
98
|
+
* an imported child must remain safe to reuse in another application.
|
|
99
|
+
*/
|
|
100
|
+
function wrapV2LocalContext(instance, wrapCallback, macroName) {
|
|
101
|
+
const plugin = instance;
|
|
102
|
+
const origins = utils.fnOrigin;
|
|
103
|
+
if (!(origins instanceof WeakMap)) throw unsupported();
|
|
104
|
+
const probe = () => {};
|
|
105
|
+
const probeApp = new elysia.Elysia({ name: "@logtape/elysia/probe" });
|
|
106
|
+
if (typeof probeApp.beforeHandle !== "function") throw unsupported();
|
|
107
|
+
probeApp.beforeHandle(probe);
|
|
108
|
+
if (!origins.has(probe)) throw unsupported();
|
|
109
|
+
const replacements = /* @__PURE__ */ new WeakMap();
|
|
110
|
+
const wrap = (value) => {
|
|
111
|
+
if (Array.isArray(value)) {
|
|
112
|
+
const present = new Set(value);
|
|
113
|
+
return value.filter((item) => {
|
|
114
|
+
if (typeof item !== "function") return true;
|
|
115
|
+
const replacement = replacements.get(item);
|
|
116
|
+
return replacement == null || !present.has(replacement);
|
|
117
|
+
}).map(wrap);
|
|
118
|
+
}
|
|
119
|
+
const wrapped = wrapCallback(value);
|
|
120
|
+
if (typeof value === "function" && typeof wrapped === "function" && value !== wrapped) replacements.set(value, wrapped);
|
|
121
|
+
if (typeof value === "function" && typeof wrapped === "function" && origins.has(value)) origins.set(wrapped, origins.get(value));
|
|
122
|
+
return wrapped;
|
|
123
|
+
};
|
|
124
|
+
const resolveParse = (value) => {
|
|
125
|
+
if (Array.isArray(value)) return value.map(resolveParse);
|
|
126
|
+
const parsers = plugin["~ext"]?.parser;
|
|
127
|
+
return wrap(typeof value === "string" ? parsers?.[value] ?? value : value);
|
|
128
|
+
};
|
|
129
|
+
const hooks = (value, final = false) => {
|
|
130
|
+
if (value == null) return final ? value : { [macroName]: true };
|
|
131
|
+
if (typeof value !== "object" || Array.isArray(value)) throw unsupported();
|
|
132
|
+
const source = value;
|
|
133
|
+
const copy = { ...source };
|
|
134
|
+
for (const key of hookKeys) if (final && key in source) copy[key] = key === "parse" ? resolveParse(source[key]) : wrap(source[key]);
|
|
135
|
+
if (final && "~deriveEntries" in source) {
|
|
136
|
+
if (!Array.isArray(source["~deriveEntries"])) throw unsupported();
|
|
137
|
+
copy["~deriveEntries"] = source["~deriveEntries"].map((entry) => Array.isArray(entry) ? [wrap(entry[0]), ...entry.slice(1)] : wrap(entry));
|
|
138
|
+
}
|
|
139
|
+
delete copy[macroName];
|
|
140
|
+
if (!final) copy[macroName] = true;
|
|
141
|
+
return copy;
|
|
142
|
+
};
|
|
143
|
+
if (typeof plugin.macro !== "function") throw unsupported();
|
|
144
|
+
plugin.macro({ [macroName]: { introspect(resolved) {
|
|
145
|
+
Object.assign(resolved, hooks(resolved, true));
|
|
146
|
+
} } });
|
|
147
|
+
const protectedNodes = /* @__PURE__ */ new WeakSet();
|
|
148
|
+
const protect = (value) => {
|
|
149
|
+
if (value == null) return;
|
|
150
|
+
if (typeof value !== "object") throw unsupported();
|
|
151
|
+
if (protectedNodes.has(value)) return;
|
|
152
|
+
protectedNodes.add(value);
|
|
153
|
+
const node = value;
|
|
154
|
+
protect(node.parent);
|
|
155
|
+
protect(node.over);
|
|
156
|
+
};
|
|
157
|
+
const chain = (value, memo = /* @__PURE__ */ new WeakMap(), final = false) => {
|
|
158
|
+
if (value == null) return value;
|
|
159
|
+
if (typeof value !== "object") throw unsupported();
|
|
160
|
+
if (protectedNodes.has(value)) return value;
|
|
161
|
+
const node = value;
|
|
162
|
+
const existing = memo.get(value);
|
|
163
|
+
if (existing != null) return existing;
|
|
164
|
+
const copy = { ...node };
|
|
165
|
+
memo.set(value, copy);
|
|
166
|
+
if ("combine" in node) {
|
|
167
|
+
copy.combine = chain(node.combine, memo, final);
|
|
168
|
+
copy.over = chain(node.over, memo, final);
|
|
169
|
+
} else {
|
|
170
|
+
if (!("added" in node)) throw unsupported();
|
|
171
|
+
copy.added = node.propagated === true ? node.added : hooks(node.added, final);
|
|
172
|
+
copy.parent = chain(node.parent, memo, final);
|
|
173
|
+
}
|
|
174
|
+
if (node.owner?.["~scopeChild"] === true) copy.owner = owner(node.owner);
|
|
175
|
+
return copy;
|
|
176
|
+
};
|
|
177
|
+
const owners = /* @__PURE__ */ new WeakMap();
|
|
178
|
+
const projections = /* @__PURE__ */ new WeakSet();
|
|
179
|
+
const owner = (source) => {
|
|
180
|
+
if (source === instance) return source;
|
|
181
|
+
if (!(source instanceof elysia.Elysia)) throw unsupported();
|
|
182
|
+
if (projections.has(source)) return source;
|
|
183
|
+
const cached = owners.get(source);
|
|
184
|
+
if (cached != null) return cached;
|
|
185
|
+
const nativeApplyMacro = plugin["~applyMacro"];
|
|
186
|
+
if (typeof nativeApplyMacro !== "function") throw unsupported();
|
|
187
|
+
const scopeViews = /* @__PURE__ */ new WeakMap();
|
|
188
|
+
const scopeView = (target) => {
|
|
189
|
+
const cached$1 = scopeViews.get(target);
|
|
190
|
+
if (cached$1 != null) return cached$1;
|
|
191
|
+
const view = new Proxy(target, { get(target$1, key) {
|
|
192
|
+
const value = Reflect.get(target$1, key, target$1);
|
|
193
|
+
if (key === "~hookChain") return chain(value, /* @__PURE__ */ new WeakMap(), true);
|
|
194
|
+
if (source["~scopeChild"] !== true) return value;
|
|
195
|
+
if (key === "~generation" && value != null) return scopeView(value);
|
|
196
|
+
if (key === "~ext" && value?.macro != null) return {
|
|
197
|
+
...value,
|
|
198
|
+
macro: new Proxy(value.macro, { has: (table, name) => name !== macroName && Reflect.has(table, name) })
|
|
199
|
+
};
|
|
200
|
+
if (key === "~applyMacro") return (...args) => nativeApplyMacro.apply(view, args);
|
|
201
|
+
return value;
|
|
202
|
+
} });
|
|
203
|
+
scopeViews.set(target, view);
|
|
204
|
+
return view;
|
|
205
|
+
};
|
|
206
|
+
const projection = scopeView(source);
|
|
207
|
+
owners.set(source, projection);
|
|
208
|
+
projections.add(projection);
|
|
209
|
+
return projection;
|
|
210
|
+
};
|
|
211
|
+
const rows = () => {
|
|
212
|
+
const value = plugin.declaredRoutes;
|
|
213
|
+
if (value === void 0) return [];
|
|
214
|
+
if (!Array.isArray(value)) throw unsupported();
|
|
215
|
+
return value;
|
|
216
|
+
};
|
|
217
|
+
for (const name of routeMethods) {
|
|
218
|
+
const original = plugin[name];
|
|
219
|
+
if (typeof original !== "function") throw unsupported();
|
|
220
|
+
plugin[name] = function(...args) {
|
|
221
|
+
protect(plugin["~hookChain"]);
|
|
222
|
+
const start = rows().length;
|
|
223
|
+
const result = original.apply(this, args);
|
|
224
|
+
const routes = rows();
|
|
225
|
+
for (let i = start; i < routes.length; i++) {
|
|
226
|
+
const source = routes[i];
|
|
227
|
+
if (!Array.isArray(source) || source.length < 4) throw unsupported();
|
|
228
|
+
if (source[0] === "WS" || source[2] != null && source[2]["~mount"]) continue;
|
|
229
|
+
const copy = source.slice();
|
|
230
|
+
copy[2] = wrap(source[2]);
|
|
231
|
+
copy[3] = owner(source[3]);
|
|
232
|
+
copy[4] = hooks(source[4]);
|
|
233
|
+
copy[5] = chain(source[5]);
|
|
234
|
+
copy[6] = chain(source[6]);
|
|
235
|
+
if (source[7]?.["~scopeChild"] === true) copy[7] = owner(source[7]);
|
|
236
|
+
routes[i] = copy;
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return () => {
|
|
242
|
+
protect(plugin["~hookChain"]);
|
|
243
|
+
for (const name of [...hookKeys, "guard"]) {
|
|
244
|
+
const original = plugin[name];
|
|
245
|
+
if (typeof original !== "function") throw unsupported();
|
|
246
|
+
plugin[name] = function(...args) {
|
|
247
|
+
const mapped = args.slice();
|
|
248
|
+
if (name === "guard") {
|
|
249
|
+
if (typeof args.at(-1) !== "function") {
|
|
250
|
+
const index = typeof args[0] === "string" ? 1 : 0;
|
|
251
|
+
const hook = hooks(args[index]);
|
|
252
|
+
if (hook.error != null) hook.error = wrap(hook.error);
|
|
253
|
+
mapped[index] = hook;
|
|
254
|
+
}
|
|
255
|
+
} else if (name === "parse") {
|
|
256
|
+
const index = args.length > 1 && typeof args[0] === "string" ? 1 : 0;
|
|
257
|
+
mapped[index] = resolveParse(args[index]);
|
|
258
|
+
} else if (name === "error") {
|
|
259
|
+
if (args.length === 1 && typeof args[0] === "object") return original.apply(this, args);
|
|
260
|
+
const index = args.length - 1;
|
|
261
|
+
mapped[index] = wrap(args[index]);
|
|
262
|
+
} else {
|
|
263
|
+
const index = typeof args[0] === "string" ? 1 : 0;
|
|
264
|
+
mapped[index] = wrap(args[index]);
|
|
265
|
+
}
|
|
266
|
+
const result = original.apply(this, mapped);
|
|
267
|
+
protect(plugin["~hookChain"]);
|
|
268
|
+
return result;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
//#endregion
|
|
275
|
+
export { createCompatibility, materializeHeaders, nativeErrorCode, wrapV2LocalContext };
|
|
276
|
+
//# sourceMappingURL=compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compat.js","names":["instance: unknown","event: \"Request\" | \"BeforeHandle\" | \"AfterHandle\" | \"Error\"","callback: (context: HookContext) => unknown","raw: HookContext","scope: \"scoped\" | \"global\"","set: ElysiaContext[\"set\"]","value: unknown","fallback: number","error: boolean","value: number","value: ElysiaContext[\"set\"][\"headers\"]","error: unknown","wrapCallback: (value: unknown) => unknown","macroName: string","resolved: Fields","source: unknown","target: object","cached","target"],"sources":["../src/compat.ts"],"sourcesContent":["import * as elysia from \"elysia\";\nimport * as utils from \"elysia/utils\";\nimport type { ElysiaContext } from \"./mod.ts\";\n\ntype Callback = (this: unknown, ...args: unknown[]) => unknown;\ntype Fields = Record<string, unknown>;\n\nexport interface HookContext extends ElysiaContext {\n store: { startTime: number };\n error?: unknown;\n code?: string;\n}\n\n/** The adapter only needs these lifecycle registration capabilities. */\nexport function createCompatibility(instance: unknown) {\n const plugin = instance as Fields;\n const v2 = typeof plugin.onRequest !== \"function\" &&\n typeof plugin.request === \"function\";\n const register = (\n event: \"Request\" | \"BeforeHandle\" | \"AfterHandle\" | \"Error\",\n callback: (context: HookContext) => unknown,\n ): void => {\n const name = v2 ? event[0].toLowerCase() + event.slice(1) : `on${event}`;\n const method = plugin[name];\n if (typeof method !== \"function\") throw unsupported();\n method.call(instance, (raw: HookContext) =>\n callback(\n v2 ? normalizeContext(raw, event === \"Error\") : raw,\n ));\n };\n const as = (scope: \"scoped\" | \"global\"): void => {\n (plugin.as as Callback).call(\n instance,\n v2 && scope === \"scoped\" ? \"plugin\" : scope,\n );\n };\n return { v2, register, as };\n}\n\nfunction unsupported(): Error {\n return new Error(\n \"@logtape/elysia: unsupported Elysia 2 internals for local request \" +\n \"context. Use a supported Elysia version and the same Elysia copy \" +\n \"and module format for the application and plugin (including utils).\",\n );\n}\n\n/** Elysia 2 shares a frozen default header map until its first write. */\nexport function materializeHeaders(set: ElysiaContext[\"set\"]): void {\n if ((set.headers as Record<string, unknown>)[\"\\0\"] === set.headers) {\n set.headers = Object.assign(Object.create(null), set.headers);\n }\n}\n\nfunction numericStatus(value: unknown, fallback: number): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n return (elysia.StatusMap as Record<string, number>)[value] ?? fallback;\n }\n return fallback;\n}\n\nfunction normalizeContext(raw: HookContext, error: boolean): HookContext {\n const rawSet = raw.set;\n materializeHeaders(rawSet);\n const response = (raw as unknown as Fields).responseValue;\n const statusClass = (elysia as unknown as Fields).ElysiaStatus;\n const responseStatus = !error &&\n (response instanceof Response ||\n (typeof statusClass === \"function\" && response instanceof statusClass))\n ? (response as { status: number }).status\n : undefined;\n const set = Object.create(rawSet, {\n status: {\n enumerable: true,\n get: () =>\n responseStatus ?? numericStatus(rawSet.status, error ? 500 : 200),\n set: (value: number) => {\n rawSet.status = value;\n },\n },\n headers: {\n enumerable: true,\n get: () => {\n materializeHeaders(rawSet);\n return rawSet.headers;\n },\n set: (value: ElysiaContext[\"set\"][\"headers\"]) => {\n rawSet.headers = value;\n },\n },\n });\n return Object.assign(Object.create(raw), { set });\n}\n\nexport function nativeErrorCode(error: unknown): string | number | undefined {\n if (error == null || typeof error !== \"object\") return undefined;\n const code = (error as Fields).code;\n return typeof code === \"string\" || typeof code === \"number\"\n ? code\n : undefined;\n}\n\nconst hookKeys = [\n \"beforeHandle\",\n \"afterHandle\",\n \"parse\",\n \"transform\",\n \"derive\",\n \"mapDerive\",\n \"mapResponse\",\n \"afterResponse\",\n \"error\",\n];\nconst routeMethods = [\n \"get\",\n \"post\",\n \"put\",\n \"patch\",\n \"delete\",\n \"options\",\n \"head\",\n \"all\",\n \"method\",\n \"use\",\n \"group\",\n \"guard\",\n \"mount\",\n];\n\n/**\n * Elysia 2 has no around-handler lifecycle. Its declared route tuples and\n * immutable hook-chain links are private integration points, verified by the\n * pinned compatibility matrix. Only this logger's copied rows are rewritten;\n * an imported child must remain safe to reuse in another application.\n */\nexport function wrapV2LocalContext(\n instance: unknown,\n wrapCallback: (value: unknown) => unknown,\n macroName: string,\n): () => void {\n const plugin = instance as Fields;\n const origins = (utils as unknown as Fields).fnOrigin;\n if (!(origins instanceof WeakMap)) throw unsupported();\n const probe = () => {};\n const probeApp = new elysia.Elysia({\n name: \"@logtape/elysia/probe\",\n }) as unknown as Fields;\n if (typeof probeApp.beforeHandle !== \"function\") throw unsupported();\n probeApp.beforeHandle(probe);\n // A different ESM/CJS copy or a bundled utils module has a different map.\n if (!origins.has(probe)) throw unsupported();\n\n const replacements = new WeakMap<object, object>();\n const wrap = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n const present = new Set(value);\n // Early-wrapped guard errors can meet their original macro callback.\n // Native macro dedupe would retain the existing (wrapped) slot. Remove\n // only that mixed representation, not deliberate [fn, fn] repetitions.\n return value.filter((item) => {\n if (typeof item !== \"function\") return true;\n const replacement = replacements.get(item);\n return replacement == null || !present.has(replacement);\n }).map(wrap);\n }\n const wrapped = wrapCallback(value);\n if (\n typeof value === \"function\" && typeof wrapped === \"function\" &&\n value !== wrapped\n ) {\n replacements.set(value, wrapped);\n }\n if (\n typeof value === \"function\" && typeof wrapped === \"function\" &&\n origins.has(value)\n ) origins.set(wrapped, origins.get(value));\n return wrapped;\n };\n const resolveParse = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(resolveParse);\n const parsers = (plugin[\"~ext\"] as Fields | undefined)?.parser as\n | Fields\n | undefined;\n return wrap(typeof value === \"string\" ? parsers?.[value] ?? value : value);\n };\n const hooks = (value: unknown, final = false): unknown => {\n if (value == null) return final ? value : { [macroName]: true };\n if (typeof value !== \"object\" || Array.isArray(value)) throw unsupported();\n const source = value as Fields;\n const copy = { ...source };\n for (const key of hookKeys) {\n if (final && key in source) {\n copy[key] = key === \"parse\"\n ? resolveParse(source[key])\n : wrap(source[key]);\n }\n }\n if (final && \"~deriveEntries\" in source) {\n if (!Array.isArray(source[\"~deriveEntries\"])) throw unsupported();\n copy[\"~deriveEntries\"] = source[\"~deriveEntries\"].map((entry) =>\n Array.isArray(entry) ? [wrap(entry[0]), ...entry.slice(1)] : wrap(entry)\n );\n }\n // Run after the route's own macro options have expanded. Reinsert last\n // under reentrant use/group calls to retain native expansion order.\n delete copy[macroName];\n if (!final) copy[macroName] = true;\n return copy;\n };\n if (typeof plugin.macro !== \"function\") throw unsupported();\n plugin.macro({\n [macroName]: {\n introspect(resolved: Fields): void {\n // Native macro expansion is deferred until composition. Only copied\n // local hooks carry this private option; children and unrelated parent\n // routes stay untouched. Elysia removes the option after introspection.\n Object.assign(resolved, hooks(resolved, true));\n },\n },\n });\n // Own links are compiler stop points: preserve even earlier snapshots.\n const protectedNodes = new WeakSet<object>();\n const protect = (value: unknown): void => {\n if (value == null) return;\n if (typeof value !== \"object\") throw unsupported();\n if (protectedNodes.has(value)) return;\n protectedNodes.add(value);\n const node = value as Fields;\n protect(node.parent);\n protect(node.over);\n // combine contains imported child links; these must still be wrapped.\n };\n const chain = (\n value: unknown,\n memo = new WeakMap<object, Fields>(),\n final = false,\n ): unknown => {\n if (value == null) return value;\n if (typeof value !== \"object\") throw unsupported();\n if (protectedNodes.has(value)) return value;\n const node = value as Fields;\n const existing = memo.get(value);\n if (existing != null) return existing;\n const copy = { ...node };\n memo.set(value, copy);\n if (\"combine\" in node) {\n copy.combine = chain(node.combine, memo, final);\n // Nested imported chains can have child hooks on either branch.\n // protectedNodes preserves actual logger stop points by identity.\n copy.over = chain(node.over, memo, final);\n } else {\n if (!(\"added\" in node)) throw unsupported();\n // Preserve propagated callback identities for native deduplication,\n // but still visit local ancestors below the propagated link.\n copy.added = node.propagated === true\n ? node.added\n : hooks(node.added, final);\n copy.parent = chain(node.parent, memo, final);\n }\n if ((node.owner as Fields | undefined)?.[\"~scopeChild\"] === true) {\n copy.owner = owner(node.owner);\n }\n return copy;\n };\n const owners = new WeakMap<object, object>();\n const projections = new WeakSet<object>();\n const owner = (source: unknown): unknown => {\n if (source === instance) return source;\n if (!(source instanceof elysia.Elysia)) throw unsupported();\n if (projections.has(source)) return source;\n const cached = owners.get(source);\n if (cached != null) return cached;\n // Error compilation reads the owner's live chain, not only route[5].\n // Both views share callback identities so native includes() dedupes them.\n const nativeApplyMacro = plugin[\"~applyMacro\"];\n if (typeof nativeApplyMacro !== \"function\") throw unsupported();\n const scopeViews = new WeakMap<object, object>();\n const scopeView = (target: object): object => {\n const cached = scopeViews.get(target);\n if (cached != null) return cached;\n const view = new Proxy(target, {\n get(target, key) {\n const value = Reflect.get(target, key, target);\n if (key === \"~hookChain\") return chain(value, new WeakMap(), true);\n if ((source as unknown as Fields)[\"~scopeChild\"] !== true) {\n return value;\n }\n if (key === \"~generation\" && value != null) return scopeView(value);\n if (key === \"~ext\" && value?.macro != null) {\n // A group resolves its scope's macros before the root's macros.\n // Hide only our terminal macro in that first phase, retaining the\n // option until the final root expansion. User macros stay native.\n return {\n ...value,\n macro: new Proxy(value.macro, {\n has: (table, name) =>\n name !== macroName && Reflect.has(table, name),\n }),\n };\n }\n if (key === \"~applyMacro\") {\n return (...args: unknown[]) => nativeApplyMacro.apply(view, args);\n }\n return value;\n },\n });\n scopeViews.set(target, view);\n return view;\n };\n const projection = scopeView(source);\n owners.set(source, projection);\n projections.add(projection);\n return projection;\n };\n const rows = (): unknown[][] => {\n const value = plugin.declaredRoutes;\n if (value === undefined) return [];\n if (!Array.isArray(value)) throw unsupported();\n return value;\n };\n for (const name of routeMethods) {\n const original = plugin[name];\n if (typeof original !== \"function\") throw unsupported();\n plugin[name] = function (this: unknown, ...args: unknown[]): unknown {\n protect(plugin[\"~hookChain\"]);\n const start = rows().length;\n const result = original.apply(this, args);\n const routes = rows();\n for (let i = start; i < routes.length; i++) {\n const source = routes[i];\n if (!Array.isArray(source) || source.length < 4) throw unsupported();\n if (\n source[0] === \"WS\" ||\n (source[2] != null && (source[2] as Fields)[\"~mount\"])\n ) continue;\n const copy = source.slice();\n copy[2] = wrap(source[2]);\n copy[3] = owner(source[3]);\n copy[4] = hooks(source[4]);\n copy[5] = chain(source[5]);\n copy[6] = chain(source[6]);\n if ((source[7] as Fields | undefined)?.[\"~scopeChild\"] === true) {\n copy[7] = owner(source[7]);\n }\n routes[i] = copy;\n }\n return result;\n };\n }\n // Install only after internal logging hooks, which must not be rewrapped.\n return () => {\n protect(plugin[\"~hookChain\"]);\n for (const name of [...hookKeys, \"guard\"]) {\n const original = plugin[name];\n if (typeof original !== \"function\") throw unsupported();\n plugin[name] = function (this: unknown, ...args: unknown[]): unknown {\n const mapped = args.slice();\n if (name === \"guard\") {\n // Callback-form guard creates a child; the route wrapper handles it.\n if (typeof args.at(-1) !== \"function\") {\n const index = typeof args[0] === \"string\" ? 1 : 0;\n const hook = hooks(args[index]) as Fields;\n // Native error collection also reads the owner's raw guard chain.\n // Keep that callback identical to the expanded error callback.\n if (hook.error != null) hook.error = wrap(hook.error);\n mapped[index] = hook;\n }\n } else if (name === \"parse\") {\n const index = args.length > 1 && typeof args[0] === \"string\" ? 1 : 0;\n mapped[index] = resolveParse(args[index]);\n } else if (name === \"error\") {\n // Error dictionaries contain constructors, not lifecycle callbacks.\n if (args.length === 1 && typeof args[0] === \"object\") {\n return original.apply(this, args);\n }\n const index = args.length - 1;\n mapped[index] = wrap(args[index]);\n } else {\n const index = typeof args[0] === \"string\" ? 1 : 0;\n mapped[index] = wrap(args[index]);\n }\n const result = original.apply(this, mapped);\n protect(plugin[\"~hookChain\"]);\n return result;\n };\n }\n };\n}\n"],"mappings":"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"}
|
package/dist/mod.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_compat = require('./compat.cjs');
|
|
2
3
|
const elysia = require_rolldown_runtime.__toESM(require("elysia"));
|
|
3
4
|
const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/logtape"));
|
|
4
5
|
|
|
@@ -576,6 +577,7 @@ function elysiaLogger(options = {}) {
|
|
|
576
577
|
return requestContext;
|
|
577
578
|
};
|
|
578
579
|
const applyRequestContextToSet = (set, requestContext) => {
|
|
580
|
+
require_compat.materializeHeaders(set);
|
|
579
581
|
if (requestContext?.responseHeader != null) set.headers[requestContext.responseHeader.name] = requestContext.responseHeader.value;
|
|
580
582
|
if (requestContext?.set != null) {
|
|
581
583
|
if (requestContext.set.status !== 200) set.status = requestContext.set.status;
|
|
@@ -590,14 +592,20 @@ function elysiaLogger(options = {}) {
|
|
|
590
592
|
name: "@logtape/elysia",
|
|
591
593
|
seed: options
|
|
592
594
|
});
|
|
593
|
-
|
|
595
|
+
const compatibility = require_compat.createCompatibility(plugin);
|
|
596
|
+
let finishLocalSetup;
|
|
597
|
+
if (shouldWrapContext && scope === "local") if (compatibility.v2) {
|
|
598
|
+
const wrappers = createElysiaRequestWrappers(buildAndStoreRequestContext, applyRequestContextToSet);
|
|
599
|
+
finishLocalSetup = require_compat.wrapV2LocalContext(plugin, wrappers.wrapRequestCallback, `@logtape/elysia/context/${generateRequestId()}`);
|
|
600
|
+
} else wrapLocalRouteHandlers(plugin, buildAndStoreRequestContext, applyRequestContextToSet);
|
|
594
601
|
else if (shouldWrapContext) plugin = plugin.wrap((handle) => {
|
|
595
602
|
return async (request) => {
|
|
596
603
|
const requestContext = await buildAndStoreRequestContext(request);
|
|
597
604
|
return await (0, __logtape_logtape.withContext)(requestContext?.context ?? {}, () => handle(request));
|
|
598
605
|
};
|
|
599
606
|
});
|
|
600
|
-
plugin
|
|
607
|
+
plugin.state("startTime", 0);
|
|
608
|
+
compatibility.register("Request", ({ request, set, store }) => {
|
|
601
609
|
const requestContext = requestContextStates.get(request);
|
|
602
610
|
if (requestContext == null && shouldWrapAllRoutes) {
|
|
603
611
|
store.startTime = performance.now();
|
|
@@ -607,7 +615,7 @@ function elysiaLogger(options = {}) {
|
|
|
607
615
|
}
|
|
608
616
|
applyRequestContextToRequest(store, set, requestContext);
|
|
609
617
|
});
|
|
610
|
-
if (scope === "local" && (contextOptions != null || logRequest))
|
|
618
|
+
if (scope === "local" && (contextOptions != null || logRequest)) compatibility.register("BeforeHandle", async (ctx) => {
|
|
611
619
|
ctx.store.startTime = performance.now();
|
|
612
620
|
const requestContext = await buildAndStoreRequestContext(ctx.request);
|
|
613
621
|
ctx.store.startTime = requestContext?.startTime ?? ctx.store.startTime;
|
|
@@ -620,7 +628,7 @@ function elysiaLogger(options = {}) {
|
|
|
620
628
|
}
|
|
621
629
|
});
|
|
622
630
|
if (logRequest) {
|
|
623
|
-
if (scope !== "local")
|
|
631
|
+
if (scope !== "local") compatibility.register("Request", (ctx) => {
|
|
624
632
|
if (!skip(ctx)) {
|
|
625
633
|
const requestContext = requestContextStates.get(ctx.request)?.context ?? {};
|
|
626
634
|
const result = withRequestLogContext(formatFn(ctx, 0), requestContext);
|
|
@@ -628,7 +636,7 @@ function elysiaLogger(options = {}) {
|
|
|
628
636
|
else logMethod("{method} {url}", result);
|
|
629
637
|
}
|
|
630
638
|
});
|
|
631
|
-
} else
|
|
639
|
+
} else compatibility.register("AfterHandle", (ctx) => {
|
|
632
640
|
if (skip(ctx)) return;
|
|
633
641
|
const store = ctx.store;
|
|
634
642
|
const responseTime = performance.now() - store.startTime;
|
|
@@ -637,14 +645,15 @@ function elysiaLogger(options = {}) {
|
|
|
637
645
|
if (typeof result === "string") logMethod(result, requestContext);
|
|
638
646
|
else logMethod("{method} {url} {status} - {responseTime} ms", result);
|
|
639
647
|
});
|
|
640
|
-
|
|
648
|
+
compatibility.register("Error", (ctx) => {
|
|
641
649
|
const store = ctx.store;
|
|
642
650
|
const responseTime = performance.now() - store.startTime;
|
|
643
651
|
const elysiaCtx = ctx;
|
|
644
652
|
if (skip(elysiaCtx)) return;
|
|
645
653
|
const props = buildProperties(elysiaCtx, responseTime);
|
|
646
654
|
const requestContext = requestContextStates.get(ctx.request)?.context ?? {};
|
|
647
|
-
const status = getErrorStatus(ctx.code, ctx.error, elysiaCtx.set.status);
|
|
655
|
+
const status = compatibility.v2 ? elysiaCtx.set.status : getErrorStatus(ctx.code ?? "UNKNOWN", ctx.error, elysiaCtx.set.status);
|
|
656
|
+
const errorCode = compatibility.v2 ? require_compat.nativeErrorCode(ctx.error) : ctx.code;
|
|
648
657
|
const error = ctx.error;
|
|
649
658
|
const errorMessage = error?.message ?? "Unknown error";
|
|
650
659
|
errorLogMethod("Error: {method} {url} {status} - {responseTime} ms - {errorMessage}", {
|
|
@@ -652,12 +661,12 @@ function elysiaLogger(options = {}) {
|
|
|
652
661
|
...requestContext,
|
|
653
662
|
status,
|
|
654
663
|
errorMessage,
|
|
655
|
-
errorCode:
|
|
664
|
+
...errorCode === void 0 ? {} : { errorCode }
|
|
656
665
|
});
|
|
657
666
|
});
|
|
658
|
-
if (shouldWrapContext && scope === "local")
|
|
659
|
-
|
|
660
|
-
|
|
667
|
+
if (shouldWrapContext && scope === "local") if (finishLocalSetup != null) finishLocalSetup();
|
|
668
|
+
else wrapLocalLifecycleHooks(plugin, buildAndStoreRequestContext, applyRequestContextToSet);
|
|
669
|
+
if (scope !== "local") compatibility.as(scope);
|
|
661
670
|
return plugin;
|
|
662
671
|
}
|
|
663
672
|
|
package/dist/mod.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAYK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;AAyB5B;;;;AAGoB;AAMH,UArDA,aAAA,CAqDoB;EAyBzB,OAAA,EA7ED,aA6EoB;EAad,IAAA,EAAA,MAAA;EAqCA,GAAA,EAAA;IAAqB,MAAA,EAAA,MAAA;IAKL,OAAA,EAhIpB,MAgIoB,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA;EAAgB,CAAA;;;;;AAaT;AAOvB,KA5IL,WAAA,GA4IK,QAAoB,GAAA,QAAA,GAAA,OAAA;;;;;AAgDb,KAtLZ,gBAAA;;;AAyNwC;AAy0BpD;;;MAAwE,QAAA,GAAA,qBAAA,GAAA,mBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,KAAA,GAAA,OAAA,GAAA,MAAA;;;;;;;;;KAzgC5D,cAAA,SACL,iDAEO;;;;;UAMG,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;KAyBL,mBAAA;;;;;UAaK,gBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAqCA,qBAAA;;;;;iCAKgB;;;;;8BAMH;;;;0BAMrB,kBACF,0BAA0B,QAAQ;;;;;;UAOxB,oBAAA;;;;;;;;;;mBAWE;;;;;;;;;;;;;;;;;;;;;oBAsBC,mBAAmB;;;;;;;;;;;;;;wBAef;;;;;;;;;;;;;;;;;;;;mBAsBL;;;;;;;;;;;;+BAaY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAy0Bf,YAAA,WAAsB,uBAA4B"}
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;KAYK,oBAAA,GAAuB;EAAvB,SAAA,OAAA,EAAA,KAAoB,eAAA;CAAA,GAErB,cAFqB,GAAA;EAAA,GAAG,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;CAAO,GAAA;EAEjB,GAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;AAWlB,CAAA;;;;AAA8C;AAa7B,UAbA,aAAA,SAAsB,OAaT,CAAA;EAAA;EAAA,SACnB,MAAA,EAAA,MAAA;EAAa;EAIL,SAAA,GAAA,EAAA,MAAA;EAQP;EAMA,SAAA,OAAA,EA1BQ,oBA0BQ;AAyB5B;;;;AAGoB;AAMH,UArDA,aAAA,CAqDoB;EAyBzB,OAAA,EA7ED,aA6EoB;EAad,IAAA,EAAA,MAAA;EAqCA,GAAA,EAAA;IAAqB,MAAA,EAAA,MAAA;IAKL,OAAA,EAhIpB,MAgIoB,CAAA,MAAA,EAAA,MAAA,GAAA,SAAA,CAAA;EAAgB,CAAA;;;;;AAaT;AAOvB,KA5IL,WAAA,GA4IK,QAAoB,GAAA,QAAA,GAAA,OAAA;;;;;AAgDb,KAtLZ,gBAAA;;;AAyNwC;AAy0BpD;;;MAAwE,QAAA,GAAA,qBAAA,GAAA,mBAAA,GAAA,iBAAA,GAAA,eAAA,GAAA,KAAA,GAAA,OAAA,GAAA,MAAA;;;;;;;;;KAzgC5D,cAAA,SACL,iDAEO;;;;;UAMG,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;KAyBL,mBAAA;;;;;UAaK,gBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAqCA,qBAAA;;;;;iCAKgB;;;;;8BAMH;;;;0BAMrB,kBACF,0BAA0B,QAAQ;;;;;;UAOxB,oBAAA;;;;;;;;;;mBAWE;;;;;;;;;;;;;;;;;;;;;oBAsBC,mBAAmB;;;;;;;;;;;;;;wBAef;;;;;;;;;;;;;;;;;;;;mBAsBL;;;;;;;;;;;;+BAaY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAy0Bf,YAAA,WAAsB,uBAA4B"}
|
package/dist/mod.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCompatibility, materializeHeaders, nativeErrorCode, wrapV2LocalContext } from "./compat.js";
|
|
1
2
|
import { Elysia } from "elysia";
|
|
2
3
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
3
4
|
|
|
@@ -575,6 +576,7 @@ function elysiaLogger(options = {}) {
|
|
|
575
576
|
return requestContext;
|
|
576
577
|
};
|
|
577
578
|
const applyRequestContextToSet = (set, requestContext) => {
|
|
579
|
+
materializeHeaders(set);
|
|
578
580
|
if (requestContext?.responseHeader != null) set.headers[requestContext.responseHeader.name] = requestContext.responseHeader.value;
|
|
579
581
|
if (requestContext?.set != null) {
|
|
580
582
|
if (requestContext.set.status !== 200) set.status = requestContext.set.status;
|
|
@@ -589,14 +591,20 @@ function elysiaLogger(options = {}) {
|
|
|
589
591
|
name: "@logtape/elysia",
|
|
590
592
|
seed: options
|
|
591
593
|
});
|
|
592
|
-
|
|
594
|
+
const compatibility = createCompatibility(plugin);
|
|
595
|
+
let finishLocalSetup;
|
|
596
|
+
if (shouldWrapContext && scope === "local") if (compatibility.v2) {
|
|
597
|
+
const wrappers = createElysiaRequestWrappers(buildAndStoreRequestContext, applyRequestContextToSet);
|
|
598
|
+
finishLocalSetup = wrapV2LocalContext(plugin, wrappers.wrapRequestCallback, `@logtape/elysia/context/${generateRequestId()}`);
|
|
599
|
+
} else wrapLocalRouteHandlers(plugin, buildAndStoreRequestContext, applyRequestContextToSet);
|
|
593
600
|
else if (shouldWrapContext) plugin = plugin.wrap((handle) => {
|
|
594
601
|
return async (request) => {
|
|
595
602
|
const requestContext = await buildAndStoreRequestContext(request);
|
|
596
603
|
return await withContext(requestContext?.context ?? {}, () => handle(request));
|
|
597
604
|
};
|
|
598
605
|
});
|
|
599
|
-
plugin
|
|
606
|
+
plugin.state("startTime", 0);
|
|
607
|
+
compatibility.register("Request", ({ request, set, store }) => {
|
|
600
608
|
const requestContext = requestContextStates.get(request);
|
|
601
609
|
if (requestContext == null && shouldWrapAllRoutes) {
|
|
602
610
|
store.startTime = performance.now();
|
|
@@ -606,7 +614,7 @@ function elysiaLogger(options = {}) {
|
|
|
606
614
|
}
|
|
607
615
|
applyRequestContextToRequest(store, set, requestContext);
|
|
608
616
|
});
|
|
609
|
-
if (scope === "local" && (contextOptions != null || logRequest))
|
|
617
|
+
if (scope === "local" && (contextOptions != null || logRequest)) compatibility.register("BeforeHandle", async (ctx) => {
|
|
610
618
|
ctx.store.startTime = performance.now();
|
|
611
619
|
const requestContext = await buildAndStoreRequestContext(ctx.request);
|
|
612
620
|
ctx.store.startTime = requestContext?.startTime ?? ctx.store.startTime;
|
|
@@ -619,7 +627,7 @@ function elysiaLogger(options = {}) {
|
|
|
619
627
|
}
|
|
620
628
|
});
|
|
621
629
|
if (logRequest) {
|
|
622
|
-
if (scope !== "local")
|
|
630
|
+
if (scope !== "local") compatibility.register("Request", (ctx) => {
|
|
623
631
|
if (!skip(ctx)) {
|
|
624
632
|
const requestContext = requestContextStates.get(ctx.request)?.context ?? {};
|
|
625
633
|
const result = withRequestLogContext(formatFn(ctx, 0), requestContext);
|
|
@@ -627,7 +635,7 @@ function elysiaLogger(options = {}) {
|
|
|
627
635
|
else logMethod("{method} {url}", result);
|
|
628
636
|
}
|
|
629
637
|
});
|
|
630
|
-
} else
|
|
638
|
+
} else compatibility.register("AfterHandle", (ctx) => {
|
|
631
639
|
if (skip(ctx)) return;
|
|
632
640
|
const store = ctx.store;
|
|
633
641
|
const responseTime = performance.now() - store.startTime;
|
|
@@ -636,14 +644,15 @@ function elysiaLogger(options = {}) {
|
|
|
636
644
|
if (typeof result === "string") logMethod(result, requestContext);
|
|
637
645
|
else logMethod("{method} {url} {status} - {responseTime} ms", result);
|
|
638
646
|
});
|
|
639
|
-
|
|
647
|
+
compatibility.register("Error", (ctx) => {
|
|
640
648
|
const store = ctx.store;
|
|
641
649
|
const responseTime = performance.now() - store.startTime;
|
|
642
650
|
const elysiaCtx = ctx;
|
|
643
651
|
if (skip(elysiaCtx)) return;
|
|
644
652
|
const props = buildProperties(elysiaCtx, responseTime);
|
|
645
653
|
const requestContext = requestContextStates.get(ctx.request)?.context ?? {};
|
|
646
|
-
const status = getErrorStatus(ctx.code, ctx.error, elysiaCtx.set.status);
|
|
654
|
+
const status = compatibility.v2 ? elysiaCtx.set.status : getErrorStatus(ctx.code ?? "UNKNOWN", ctx.error, elysiaCtx.set.status);
|
|
655
|
+
const errorCode = compatibility.v2 ? nativeErrorCode(ctx.error) : ctx.code;
|
|
647
656
|
const error = ctx.error;
|
|
648
657
|
const errorMessage = error?.message ?? "Unknown error";
|
|
649
658
|
errorLogMethod("Error: {method} {url} {status} - {responseTime} ms - {errorMessage}", {
|
|
@@ -651,12 +660,12 @@ function elysiaLogger(options = {}) {
|
|
|
651
660
|
...requestContext,
|
|
652
661
|
status,
|
|
653
662
|
errorMessage,
|
|
654
|
-
errorCode:
|
|
663
|
+
...errorCode === void 0 ? {} : { errorCode }
|
|
655
664
|
});
|
|
656
665
|
});
|
|
657
|
-
if (shouldWrapContext && scope === "local")
|
|
658
|
-
|
|
659
|
-
|
|
666
|
+
if (shouldWrapContext && scope === "local") if (finishLocalSetup != null) finishLocalSetup();
|
|
667
|
+
else wrapLocalLifecycleHooks(plugin, buildAndStoreRequestContext, applyRequestContextToSet);
|
|
668
|
+
if (scope !== "local") compatibility.as(scope);
|
|
660
669
|
return plugin;
|
|
661
670
|
}
|
|
662
671
|
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","names":["options: boolean | RequestContextOptions | undefined","options: boolean | RequestIdOptions | undefined","value: string","request: ElysiaRequest","options: RequestIdOptions","responseHeader","resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined","include: readonly RequestContextField[]","context: Record<string, unknown>","options: RequestContextOptions","set: ElysiaContext[\"set\"] | undefined","headers: Record<string, string | undefined>","ctx: ElysiaContext","responseTime: number","result: string | Record<string, unknown>","value: number","timestamp: number","value: unknown","escapeSpaces: boolean","predefinedFormats: Record<PredefinedFormat, FormatFunction>","category: string | readonly string[]","errorCodeToStatus: Record<string, number>","code: string | number","error: unknown","setStatus: number","elysiaRouteMethods: readonly ElysiaRouteMethod[]","plugin: unknown","buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>","applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void","callback: unknown","hooks: unknown","plugin: ElysiaRoutePlugin","routePlugin: unknown","restore: (() => void)[]","route","path: string","handler: unknown","hook?: unknown","method: string","childPlugin: unknown","args: readonly unknown[]","options: ElysiaLogTapeOptions","formatFn: FormatFunction","request: Request","set: ElysiaContext[\"set\"]","requestContext: ElysiaRequestContextState | undefined","store: LoggerStore","plugin: Elysia<any, any, any, any, any, any, any>"],"sources":["../src/mod.ts"],"sourcesContent":["import { Elysia } from \"elysia\";\nimport { getLogger, type LogLevel, withContext } from \"@logtape/logtape\";\n\nexport type { LogLevel } from \"@logtape/logtape\";\n\ntype ElysiaRequestHeaders = Request extends {\n readonly headers: infer RequestHeaders;\n} ? RequestHeaders & {\n get(name: string): string | null;\n }\n : {\n get(name: string): string | null;\n };\n\n/**\n * Web Request interface exposed to Elysia request hooks.\n * @since 2.2.0\n */\nexport interface ElysiaRequest extends Request {\n /** HTTP request method */\n readonly method: string;\n /** Request URL */\n readonly url: string;\n /** Request headers */\n readonly headers: ElysiaRequestHeaders;\n}\n\n/**\n * Minimal Elysia Context interface for compatibility.\n * @since 2.0.0\n */\nexport interface ElysiaContext {\n request: ElysiaRequest;\n path: string;\n set: {\n status: number;\n headers: Record<string, string | undefined>;\n };\n}\n\n/**\n * Plugin scope options for controlling hook propagation.\n * @since 2.0.0\n */\nexport type PluginScope = \"global\" | \"scoped\" | \"local\";\n\n/**\n * Predefined log format names.\n * @since 2.0.0\n */\nexport type PredefinedFormat =\n /**\n * @deprecated Use `\"structured-combined\"` instead.\n */\n | \"combined\"\n /**\n * @deprecated Use `\"structured-common\"` instead.\n */\n | \"common\"\n | \"structured-combined\"\n | \"structured-common\"\n | \"morgan-combined\"\n | \"morgan-common\"\n | \"dev\"\n | \"short\"\n | \"tiny\";\n\n/**\n * Custom format function for request logging.\n *\n * @param ctx The Elysia context object.\n * @param responseTime The response time in milliseconds.\n * @returns A string message or an object with structured properties.\n * @since 2.0.0\n */\nexport type FormatFunction = (\n ctx: ElysiaContext,\n responseTime: number,\n) => string | Record<string, unknown>;\n\n/**\n * Structured log properties for HTTP requests.\n * @since 2.0.0\n */\nexport interface RequestLogProperties {\n /** HTTP request method */\n method: string;\n /** Request URL */\n url: string;\n /** Request path */\n path: string;\n /** HTTP response status code */\n status: number;\n /** Response time in milliseconds */\n responseTime: number;\n /** Response content-length header value */\n contentLength: string | undefined;\n /** Remote client address (from X-Forwarded-For header) */\n remoteAddr: string | undefined;\n /** User-Agent header value */\n userAgent: string | undefined;\n /** Referrer header value */\n referrer: string | undefined;\n}\n\n/**\n * Request fields that can be added to the implicit request context.\n * @since 2.2.0\n */\nexport type RequestContextField =\n | \"requestId\"\n | \"method\"\n | \"url\"\n | \"path\"\n | \"userAgent\"\n | \"remoteAddr\"\n | \"referrer\";\n\n/**\n * Options for extracting, generating, and propagating a request ID.\n * @since 2.2.0\n */\nexport interface RequestIdOptions {\n /**\n * The property name used in implicit context and request log records.\n * @default \"requestId\"\n */\n readonly property?: string;\n\n /**\n * Incoming request headers to inspect in order.\n * @default [\"x-request-id\"]\n */\n readonly headerNames?: readonly string[];\n\n /**\n * Response header that receives the resolved request ID.\n * Set to `false` to disable response header propagation.\n * @default \"x-request-id\"\n */\n readonly responseHeader?: string | false;\n\n /**\n * Generates a request ID when no incoming header is present.\n * @default crypto.randomUUID()\n */\n readonly generate?: () => string;\n\n /**\n * Normalizes an incoming request ID. Return `null` to reject the value and\n * keep looking for another header or generate a new ID.\n */\n readonly normalize?: (value: string) => string | null;\n}\n\n/**\n * Options for request-scoped implicit context.\n * @since 2.2.0\n */\nexport interface RequestContextOptions {\n /**\n * Enables request ID extraction, generation, and response propagation.\n * @default true\n */\n readonly requestId?: boolean | RequestIdOptions;\n\n /**\n * Fields to add to the implicit context.\n * @default [\"requestId\"]\n */\n readonly include?: readonly RequestContextField[];\n\n /**\n * Adds application-specific fields to the implicit request context.\n */\n readonly enrich?: (\n ctx: ElysiaContext,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>;\n}\n\n/**\n * Options for configuring the Elysia LogTape middleware.\n * @since 2.0.0\n */\nexport interface ElysiaLogTapeOptions {\n /**\n * The LogTape category to use for logging.\n * @default [\"elysia\"]\n */\n readonly category?: string | readonly string[];\n\n /**\n * The log level to use for request logging.\n * @default \"info\"\n */\n readonly level?: LogLevel;\n\n /**\n * The format for log output.\n * Can be a predefined format name or a custom format function.\n *\n * Structured formats:\n * - `\"structured-combined\"` - Structured request properties (default)\n * - `\"structured-common\"` - Structured request properties without\n * referrer/userAgent\n * - `\"combined\"` - Deprecated alias for `\"structured-combined\"`\n * - `\"common\"` - Deprecated alias for `\"structured-common\"`\n *\n * Text formats:\n * - `\"morgan-combined\"` - Morgan-compatible combined access log (string)\n * - `\"morgan-common\"` - Morgan-compatible common access log (string)\n * - `\"dev\"` - Concise colored output for development (string)\n * - `\"short\"` - Shorter than common (string)\n * - `\"tiny\"` - Minimal output (string)\n *\n * @default \"structured-combined\"\n */\n readonly format?: PredefinedFormat | FormatFunction;\n\n /**\n * Function to determine whether logging should be skipped.\n * Return `true` to skip logging for a request.\n *\n * @example Skip logging for health check endpoint\n * ```typescript\n * app.use(elysiaLogger({\n * skip: (ctx) => ctx.path === \"/health\",\n * }));\n * ```\n *\n * @default () => false\n */\n readonly skip?: (ctx: ElysiaContext) => boolean;\n\n /**\n * If `true`, logs are written immediately when the request is received.\n * If `false` (default), logs are written after the response is sent.\n *\n * Note: When `logRequest` is `true`, response-related properties\n * (status, responseTime, contentLength) will not be available.\n *\n * @default false\n */\n readonly logRequest?: boolean;\n\n /**\n * The plugin scope for controlling how lifecycle hooks are propagated.\n *\n * - `\"global\"` - Hooks apply to all routes in the application\n * - `\"scoped\"` - Hooks apply to the parent instance where the plugin is used\n * - `\"local\"` - Hooks only apply within the plugin itself\n *\n * @default \"global\"\n */\n readonly scope?: PluginScope;\n\n /**\n * Enables request-scoped implicit context and request ID correlation.\n *\n * When set to `true`, the plugin reads the `x-request-id` header, generates\n * one when it is absent, writes it to the `x-request-id` response header,\n * and adds `requestId` to all LogTape records emitted while handling the\n * request.\n *\n * @default false\n * @since 2.2.0\n */\n readonly context?: boolean | RequestContextOptions;\n}\n\nconst defaultRequestIdHeader = \"x-request-id\";\n\n/**\n * Per-request context state stored outside Elysia's shared store.\n */\ninterface ElysiaRequestContextState {\n readonly context: Record<string, unknown>;\n readonly startTime?: number;\n readonly responseHeader?: {\n readonly name: string;\n readonly value: string;\n };\n readonly set?: ElysiaContext[\"set\"];\n}\n\n/**\n * Normalize request context options.\n */\nfunction normalizeRequestContextOptions(\n options: boolean | RequestContextOptions | undefined,\n): RequestContextOptions | undefined {\n if (options === true) return {};\n if (options === false || options == null) return undefined;\n return options;\n}\n\n/**\n * Normalize request ID options.\n */\nfunction normalizeRequestIdOptions(\n options: boolean | RequestIdOptions | undefined,\n): RequestIdOptions | undefined {\n if (options === false) return undefined;\n if (options === true || options == null) return {};\n return options;\n}\n\n/**\n * Generate a request ID with Web Crypto when possible.\n */\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Normalize an incoming request ID.\n */\nfunction defaultNormalizeRequestId(value: string): string | null {\n const trimmed = value.trim();\n return trimmed === \"\" ? null : trimmed;\n}\n\n/**\n * Resolve a request path from a request URL.\n */\nfunction getPath(request: ElysiaRequest): string {\n return new URL(request.url, \"http://localhost\").pathname;\n}\n\n/**\n * Resolve the request ID for a request.\n */\nfunction resolveRequestId(\n request: ElysiaRequest,\n options: RequestIdOptions,\n): {\n readonly property: string;\n readonly value: string;\n readonly responseHeader?: string;\n} {\n const property = options.property ?? \"requestId\";\n const normalize = options.normalize ?? defaultNormalizeRequestId;\n const headerNames = options.headerNames ?? [defaultRequestIdHeader];\n for (const headerName of headerNames) {\n const headerValue = request.headers.get(headerName);\n if (headerValue == null) continue;\n const normalized = normalize(headerValue);\n if (normalized != null) {\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: normalized,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n }\n }\n const generated = (options.generate ?? generateRequestId)();\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: generated,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n}\n\n/**\n * Get referrer from request headers.\n */\nfunction getReferrer(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"referrer\") ??\n request.headers.get(\"referer\") ??\n undefined;\n}\n\n/**\n * Get user agent from request headers.\n */\nfunction getUserAgent(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"user-agent\") ?? undefined;\n}\n\n/**\n * Get remote address from X-Forwarded-For header.\n */\nfunction getRemoteAddr(request: ElysiaRequest): string | undefined {\n const forwarded = request.headers.get(\"x-forwarded-for\");\n if (forwarded) {\n // X-Forwarded-For can contain multiple IPs, take the first one\n const firstIp = forwarded.split(\",\")[0].trim();\n return firstIp || undefined;\n }\n return undefined;\n}\n\n/**\n * Build request context fields from a request.\n */\nfunction buildIncludedContext(\n request: ElysiaRequest,\n resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined,\n include: readonly RequestContextField[],\n): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n for (const field of include) {\n switch (field) {\n case \"requestId\":\n if (resolvedRequestId != null) {\n context[resolvedRequestId.property] = resolvedRequestId.value;\n }\n break;\n case \"method\":\n context.method = request.method;\n break;\n case \"url\":\n context.url = request.url;\n break;\n case \"path\":\n context.path = getPath(request);\n break;\n case \"userAgent\":\n context.userAgent = getUserAgent(request);\n break;\n case \"remoteAddr\":\n context.remoteAddr = getRemoteAddr(request);\n break;\n case \"referrer\":\n context.referrer = getReferrer(request);\n break;\n }\n }\n return context;\n}\n\n/**\n * Build the implicit context for a request.\n */\nasync function buildRequestContext(\n request: ElysiaRequest,\n options: RequestContextOptions,\n): Promise<ElysiaRequestContextState> {\n const requestIdOptions = normalizeRequestIdOptions(options.requestId);\n const resolvedRequestId = requestIdOptions == null\n ? undefined\n : resolveRequestId(request, requestIdOptions);\n const include = options.include ??\n (resolvedRequestId == null ? [] : [\"requestId\"] as const);\n const context = buildIncludedContext(request, resolvedRequestId, include);\n let set: ElysiaContext[\"set\"] | undefined;\n if (options.enrich != null) {\n set = { status: 200, headers: {} };\n Object.assign(\n context,\n await options.enrich({\n request,\n path: getPath(request),\n set,\n }),\n );\n }\n const responseHeader = resolvedRequestId?.responseHeader == null\n ? undefined\n : {\n name: resolvedRequestId.responseHeader,\n value: resolvedRequestId.value,\n };\n return { context, responseHeader, set };\n}\n\n/**\n * Get content length from response headers.\n */\nfunction getContentLength(\n headers: Record<string, string | undefined>,\n): string | undefined {\n const contentLength = headers[\"content-length\"];\n if (contentLength === undefined || contentLength === null) return undefined;\n return contentLength;\n}\n\n/**\n * Build structured log properties from context.\n */\nfunction buildProperties(\n ctx: ElysiaContext,\n responseTime: number,\n): RequestLogProperties {\n return {\n method: ctx.request.method,\n url: ctx.request.url,\n path: ctx.path,\n status: ctx.set.status,\n responseTime,\n contentLength: getContentLength(ctx.set.headers),\n remoteAddr: getRemoteAddr(ctx.request),\n userAgent: getUserAgent(ctx.request),\n referrer: getReferrer(ctx.request),\n };\n}\n\n/**\n * Add request context fields to a request log result.\n */\nfunction withRequestLogContext(\n result: string | Record<string, unknown>,\n context: Record<string, unknown>,\n): string | Record<string, unknown> {\n if (typeof result === \"string\") return result;\n return { ...result, ...context };\n}\n\nconst clfMonths = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n] as const;\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, \"0\");\n}\n\nfunction formatClfDate(timestamp: number = Date.now()): string {\n const date = new Date(timestamp);\n return `${pad2(date.getUTCDate())}/${\n clfMonths[date.getUTCMonth()]\n }/${date.getUTCFullYear()}:${pad2(date.getUTCHours())}:${\n pad2(date.getUTCMinutes())\n }:${pad2(date.getUTCSeconds())} +0000`;\n}\n\nfunction escapeAccessLogValue(value: unknown, escapeSpaces: boolean): string {\n const escaped = String(value)\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\t/g, \"\\\\t\");\n return escapeSpaces ? escaped.replace(/ /g, \"\\\\x20\") : escaped;\n}\n\nfunction formatAccessLogToken(value: unknown): string {\n if (value == null || value === \"\") return \"-\";\n return escapeAccessLogValue(value, true);\n}\n\nfunction formatAccessLogQuotedToken(value: unknown): string {\n if (value == null || value === \"\") return '\"-\"';\n return `\"${escapeAccessLogValue(value, false)}\"`;\n}\n\nfunction getRequestTarget(request: ElysiaRequest): string {\n const url = new URL(request.url, \"http://localhost\");\n return `${url.pathname}${url.search}`;\n}\n\nfunction getRemoteUser(request: ElysiaRequest): string | undefined {\n const authorization = request.headers.get(\"authorization\");\n if (authorization == null || authorization === \"\") return undefined;\n const match = /^Basic\\s+(.+)$/i.exec(authorization);\n if (match == null || typeof globalThis.atob !== \"function\") {\n return undefined;\n }\n try {\n const decoded = globalThis.atob(match[1]);\n const colonIndex = decoded.indexOf(\":\");\n const user = colonIndex < 0 ? decoded : decoded.slice(0, colonIndex);\n return user === \"\" ? undefined : user;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Structured combined format.\n * Returns all structured properties.\n */\nfunction formatCombined(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n return { ...buildProperties(ctx, responseTime) };\n}\n\n/**\n * Structured common format.\n * Like structured combined but without referrer and userAgent.\n */\nfunction formatCommon(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n const props = buildProperties(ctx, responseTime);\n const { referrer: _referrer, userAgent: _userAgent, ...rest } = props;\n return rest;\n}\n\n/**\n * Morgan combined format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n * \":referrer\" \":user-agent\"\n */\nfunction formatMorganCombined(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n } ${formatAccessLogQuotedToken(getReferrer(ctx.request))} ${\n formatAccessLogQuotedToken(getUserAgent(ctx.request))\n }`;\n}\n\n/**\n * Morgan common format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n */\nfunction formatMorganCommon(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n }`;\n}\n\n/**\n * Dev format (colored output for development).\n * :method :path :status :response-time ms - :res[content-length]\n */\nfunction formatDev(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${\n responseTime.toFixed(3)\n } ms - ${contentLength}`;\n}\n\n/**\n * Short format.\n * :remote-addr :method :url :status :res[content-length] - :response-time ms\n */\nfunction formatShort(ctx: ElysiaContext, responseTime: number): string {\n const remoteAddr = getRemoteAddr(ctx.request) ?? \"-\";\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${remoteAddr} ${ctx.request.method} ${ctx.request.url} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Tiny format (minimal output).\n * :method :path :status :res[content-length] - :response-time ms\n */\nfunction formatTiny(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Map of predefined format functions.\n */\nconst predefinedFormats: Record<PredefinedFormat, FormatFunction> = {\n combined: formatCombined,\n common: formatCommon,\n \"structured-combined\": formatCombined,\n \"structured-common\": formatCommon,\n \"morgan-combined\": formatMorganCombined,\n \"morgan-common\": formatMorganCommon,\n dev: formatDev,\n short: formatShort,\n tiny: formatTiny,\n};\n\n/**\n * Normalize category to array format.\n */\nfunction normalizeCategory(\n category: string | readonly string[],\n): readonly string[] {\n return typeof category === \"string\" ? [category] : category;\n}\n\n/**\n * Mapping of Elysia error codes to HTTP status codes.\n */\nconst errorCodeToStatus: Record<string, number> = {\n NOT_FOUND: 404,\n VALIDATION: 422,\n PARSE: 400,\n INTERNAL_SERVER_ERROR: 500,\n INVALID_COOKIE_SIGNATURE: 400,\n UNKNOWN: 500,\n};\n\n/**\n * Get the HTTP status code from an error context.\n * Checks error.status first, then falls back to error code mapping.\n */\nfunction getErrorStatus(\n code: string | number,\n error: unknown,\n setStatus: number,\n): number {\n // If code is already a number, use it as status\n if (typeof code === \"number\") {\n return code;\n }\n // Check if error has a status property (Elysia custom errors)\n if (\n error != null &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n return error.status;\n }\n // Fall back to error code mapping\n if (code in errorCodeToStatus) {\n return errorCodeToStatus[code];\n }\n // Use set.status as last resort\n return setStatus;\n}\n\n/**\n * Internal store type for timing.\n */\ninterface LoggerStore {\n startTime: number;\n}\n\ntype ElysiaRouteContext = ElysiaContext & {\n readonly store: LoggerStore;\n};\n\ntype ElysiaRequestCallback = (\n this: unknown,\n ...args: unknown[]\n) => unknown;\n\ntype ElysiaRouteRegistrar = (\n path: string,\n handler: unknown,\n hook?: unknown,\n) => unknown;\n\ntype ElysiaUseRegistrar = (\n plugin: unknown,\n ...options: unknown[]\n) => unknown;\n\ntype ElysiaOnRegistrar = (...args: unknown[]) => unknown;\n\ntype ElysiaRouteMethod =\n | \"all\"\n | \"connect\"\n | \"delete\"\n | \"get\"\n | \"head\"\n | \"options\"\n | \"patch\"\n | \"post\"\n | \"put\";\n\nconst elysiaRouteMethods: readonly ElysiaRouteMethod[] = [\n \"all\",\n \"connect\",\n \"delete\",\n \"get\",\n \"head\",\n \"options\",\n \"patch\",\n \"post\",\n \"put\",\n];\n\nconst elysiaRouteHookKeys = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\nconst elysiaPluginLifecycleHookTypes = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\ntype ElysiaRoutePlugin = Record<ElysiaRouteMethod, ElysiaRouteRegistrar> & {\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n on: ElysiaOnRegistrar;\n route: (\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) => unknown;\n use: ElysiaUseRegistrar;\n};\n\ninterface ElysiaRouteEntry {\n handler?: unknown;\n hooks?: unknown;\n}\n\nfunction getElysiaRouteEntries(\n plugin: unknown,\n visited = new Set<object>(),\n): ElysiaRouteEntry[] {\n if (plugin == null || typeof plugin !== \"object\") return [];\n if (visited.has(plugin)) return [];\n visited.add(plugin);\n const routePlugin = plugin as {\n readonly default?: unknown;\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n };\n const history = routePlugin.router?.history;\n const entries = Array.isArray(history)\n ? history\n : history == null\n ? []\n : Object.values(history);\n return entries.concat(getElysiaRouteEntries(routePlugin.default, visited));\n}\n\nfunction createElysiaRequestWrappers(\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n) {\n const wrappedHandlers = new WeakSet<ElysiaRequestCallback>();\n const wrappedHandlerCache = new WeakMap<\n ElysiaRequestCallback,\n ElysiaRequestCallback\n >();\n\n const getRouteContext = (\n value: unknown,\n ): ElysiaRouteContext | undefined => {\n if (value == null || typeof value !== \"object\") return undefined;\n const context = value as { readonly request?: unknown };\n if (!(context.request instanceof Request)) return undefined;\n return value as ElysiaRouteContext;\n };\n\n const wrapRequestCallback = (callback: unknown): unknown => {\n if (typeof callback !== \"function\") return callback;\n const requestCallback = callback as ElysiaRequestCallback;\n if (wrappedHandlers.has(requestCallback)) return callback;\n const cached = wrappedHandlerCache.get(requestCallback);\n if (cached != null) return cached;\n const wrapped = async function (\n this: unknown,\n ...args: unknown[]\n ): Promise<unknown> {\n const ctx = getRouteContext(args[0]);\n if (ctx == null) return await requestCallback.apply(this, args);\n ctx.store.startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n ctx.store.startTime = requestContext?.startTime ??\n ctx.store.startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n return await withContext(\n requestContext?.context ?? {},\n () => requestCallback.apply(this, args),\n );\n };\n wrappedHandlers.add(wrapped);\n wrappedHandlerCache.set(requestCallback, wrapped);\n return wrapped;\n };\n\n const wrapRouteHookValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(wrapRouteHookValue);\n if (typeof value === \"function\") return wrapRequestCallback(value);\n if (value == null || typeof value !== \"object\") return value;\n const hookContainer = value as { readonly fn?: unknown };\n if (typeof hookContainer.fn !== \"function\") return value;\n return {\n ...hookContainer,\n fn: wrapRequestCallback(hookContainer.fn),\n };\n };\n\n const wrapRouteHooks = (hooks: unknown): unknown => {\n if (hooks == null || typeof hooks !== \"object\") return hooks;\n const routeHooks = hooks as Record<string, unknown>;\n let changed = false;\n const wrappedHooks = { ...routeHooks };\n for (const key of elysiaRouteHookKeys) {\n const value = routeHooks[key];\n const wrappedValue = wrapRouteHookValue(value);\n if (wrappedValue === value) continue;\n wrappedHooks[key] = wrappedValue;\n changed = true;\n }\n return changed ? wrappedHooks : hooks;\n };\n\n return { wrapRequestCallback, wrapRouteHookValue, wrapRouteHooks };\n}\n\n/**\n * Wrap routes registered on a local plugin with implicit LogTape context.\n */\nfunction wrapLocalRouteHandlers(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRequestCallback, wrapRouteHooks } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const wrapRouteEntries = (routePlugin: unknown): (() => void)[] => {\n const restore: (() => void)[] = [];\n for (const route of getElysiaRouteEntries(routePlugin)) {\n const handler = route.handler;\n const wrappedHandler = wrapRequestCallback(handler);\n if (wrappedHandler !== handler) {\n route.handler = wrappedHandler;\n restore.push(() => {\n route.handler = handler;\n });\n }\n const hooks = route.hooks;\n const wrappedHooks = wrapRouteHooks(hooks);\n if (wrappedHooks !== hooks) {\n route.hooks = wrappedHooks;\n restore.push(() => {\n route.hooks = hooks;\n });\n }\n }\n return restore;\n };\n\n for (const method of elysiaRouteMethods) {\n const register = plugin[method].bind(plugin);\n plugin[method] = ((path: string, handler: unknown, hook?: unknown) =>\n register(\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRouteRegistrar;\n }\n\n const route = plugin.route.bind(plugin);\n plugin.route = ((\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) =>\n route(\n method,\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRoutePlugin[\"route\"];\n\n const use = plugin.use.bind(plugin);\n plugin.use = ((childPlugin: unknown, ...options: unknown[]) => {\n const restore = wrapRouteEntries(childPlugin);\n try {\n return use(childPlugin, ...options);\n } finally {\n for (const restoreRouteHandler of restore) restoreRouteHandler();\n }\n }) as ElysiaUseRegistrar;\n}\n\n/**\n * Wrap local plugin lifecycle hooks registered by downstream code.\n */\nfunction wrapLocalLifecycleHooks(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRouteHookValue } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const isLifecycleHookType = (value: unknown): boolean =>\n typeof value === \"string\" &&\n (elysiaPluginLifecycleHookTypes as readonly string[]).includes(value);\n\n const wrapLifecycleHookArgs = (args: readonly unknown[]): unknown[] => {\n const wrappedArgs = [...args];\n if (isLifecycleHookType(args[0])) {\n if (args.length > 1) wrappedArgs[1] = wrapRouteHookValue(args[1]);\n if (args.length > 2) wrappedArgs[2] = wrapRouteHookValue(args[2]);\n } else if (isLifecycleHookType(args[1]) && args.length > 2) {\n wrappedArgs[2] = wrapRouteHookValue(args[2]);\n }\n return wrappedArgs;\n };\n\n const on = plugin.on.bind(plugin);\n plugin.on =\n ((...args: unknown[]) =>\n on(...wrapLifecycleHookArgs(args))) as ElysiaOnRegistrar;\n}\n\n/**\n * Creates Elysia plugin for HTTP request logging using LogTape.\n *\n * This plugin provides Morgan-compatible request logging with LogTape\n * as the backend, supporting structured logging and customizable formats.\n *\n * @example Basic usage\n * ```typescript\n * import { Elysia } from \"elysia\";\n * import { configure, getConsoleSink } from \"@logtape/logtape\";\n * import { elysiaLogger } from \"@logtape/elysia\";\n *\n * await configure({\n * sinks: { console: getConsoleSink() },\n * loggers: [\n * { category: [\"elysia\"], sinks: [\"console\"], lowestLevel: \"info\" }\n * ],\n * });\n *\n * const app = new Elysia()\n * .use(elysiaLogger())\n * .get(\"/\", () => ({ hello: \"world\" }))\n * .listen(3000);\n * ```\n *\n * @example With custom options\n * ```typescript\n * app.use(elysiaLogger({\n * category: [\"myapp\", \"http\"],\n * level: \"debug\",\n * format: \"dev\",\n * skip: (ctx) => ctx.path === \"/health\",\n * scope: \"scoped\",\n * }));\n * ```\n *\n * @example With custom format function\n * ```typescript\n * app.use(elysiaLogger({\n * format: (ctx, responseTime) => ({\n * method: ctx.request.method,\n * path: ctx.path,\n * status: ctx.set.status,\n * duration: responseTime,\n * }),\n * }));\n * ```\n *\n * @param options Configuration options for the plugin.\n * @returns Elysia plugin instance.\n * @since 2.0.0\n */\n// deno-lint-ignore no-explicit-any\nexport function elysiaLogger(options: ElysiaLogTapeOptions = {}): Elysia<any> {\n const category = normalizeCategory(options.category ?? [\"elysia\"]);\n const logger = getLogger(category);\n const level = options.level ?? \"info\";\n const formatOption = options.format ?? \"structured-combined\";\n const skip = options.skip ?? (() => false);\n const logRequest = options.logRequest ?? false;\n const scope = options.scope ?? \"global\";\n const contextOptions = normalizeRequestContextOptions(options.context);\n const requestContextStates = new WeakMap<\n Request,\n ElysiaRequestContextState\n >();\n const shouldWrapContext = contextOptions != null;\n const shouldWrapAllRoutes = contextOptions != null && scope !== \"local\";\n\n // Resolve format function\n const formatFn: FormatFunction = typeof formatOption === \"string\"\n ? predefinedFormats[formatOption]\n : formatOption;\n\n const logMethod = logger[level].bind(logger);\n const errorLogMethod = logger.error.bind(logger);\n\n const buildAndStoreRequestContext = async (\n request: Request,\n ): Promise<ElysiaRequestContextState | undefined> => {\n if (contextOptions == null) return undefined;\n const existingContext = requestContextStates.get(request);\n if (existingContext != null) return existingContext;\n const startTime = performance.now();\n const requestContext = {\n ...await buildRequestContext(request, contextOptions),\n startTime,\n };\n requestContextStates.set(request, requestContext);\n return requestContext;\n };\n\n const applyRequestContextToSet = (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n if (requestContext?.responseHeader != null) {\n set.headers[requestContext.responseHeader.name] =\n requestContext.responseHeader.value;\n }\n if (requestContext?.set != null) {\n if (requestContext.set.status !== 200) {\n set.status = requestContext.set.status;\n }\n Object.assign(set.headers, requestContext.set.headers);\n }\n };\n\n const applyRequestContextToRequest = (\n store: LoggerStore,\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n store.startTime = requestContext?.startTime ?? performance.now();\n applyRequestContextToSet(set, requestContext);\n };\n\n // deno-lint-ignore no-explicit-any\n let plugin: Elysia<any, any, any, any, any, any, any> = new Elysia({\n name: \"@logtape/elysia\",\n seed: options,\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalRouteHandlers(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n } else if (shouldWrapContext) {\n // Elysia lifecycle hooks cannot wrap downstream handlers, so use wrap()\n // to keep AsyncLocalStorage active for the whole route execution.\n plugin = plugin.wrap((handle) => {\n return async (request: Request) => {\n const requestContext = await buildAndStoreRequestContext(request);\n return await withContext(\n requestContext?.context ?? {},\n () => handle(request),\n );\n };\n });\n }\n\n plugin = plugin\n .state(\"startTime\", 0)\n .onRequest(({ request, set, store }) => {\n const requestContext = requestContextStates.get(request);\n if (requestContext == null && shouldWrapAllRoutes) {\n (store as LoggerStore).startTime = performance.now();\n return buildAndStoreRequestContext(request).then((resolvedContext) => {\n applyRequestContextToRequest(\n store as LoggerStore,\n set,\n resolvedContext,\n );\n });\n }\n applyRequestContextToRequest(store as LoggerStore, set, requestContext);\n });\n\n if (scope === \"local\" && (contextOptions != null || logRequest)) {\n plugin = plugin.onBeforeHandle(async (ctx) => {\n (ctx.store as LoggerStore).startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n (ctx.store as LoggerStore).startTime = requestContext?.startTime ??\n (ctx.store as LoggerStore).startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n if (logRequest && !skip(ctx as unknown as ElysiaContext)) {\n const context = requestContext?.context ?? {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n context,\n );\n if (typeof result === \"string\") {\n logMethod(result, context);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n\n if (logRequest) {\n // Log immediately when request arrives\n if (scope !== \"local\") {\n plugin = plugin.onRequest((ctx) => {\n if (!skip(ctx as unknown as ElysiaContext)) {\n const requestContext =\n requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n requestContext,\n );\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n } else {\n // Log after handler completes\n plugin = plugin.onAfterHandle((ctx) => {\n if (skip(ctx as unknown as ElysiaContext)) return;\n\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const requestContext = requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, responseTime),\n requestContext,\n );\n\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url} {status} - {responseTime} ms\", result);\n }\n });\n }\n\n // Add error logging\n plugin = plugin.onError((ctx) => {\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const elysiaCtx = ctx as unknown as ElysiaContext;\n\n if (skip(elysiaCtx)) return;\n\n const props = buildProperties(elysiaCtx, responseTime);\n const requestContext = requestContextStates.get(ctx.request)?.context ?? {};\n // Get the correct HTTP status code from error context\n const status = getErrorStatus(ctx.code, ctx.error, elysiaCtx.set.status);\n // Extract error message safely\n const error = ctx.error as { message?: string } | undefined;\n const errorMessage = error?.message ?? \"Unknown error\";\n errorLogMethod(\n \"Error: {method} {url} {status} - {responseTime} ms - {errorMessage}\",\n {\n ...props,\n ...requestContext,\n status,\n errorMessage,\n errorCode: ctx.code,\n },\n );\n });\n\n if (shouldWrapContext && scope === \"local\") {\n wrapLocalLifecycleHooks(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n }\n\n // Apply scope\n if (scope === \"global\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"global\") as unknown as Elysia<any>;\n } else if (scope === \"scoped\") {\n // deno-lint-ignore no-explicit-any\n return plugin.as(\"scoped\") as unknown as Elysia<any>;\n }\n\n // deno-lint-ignore no-explicit-any\n return plugin as unknown as Elysia<any>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mod.js","names":["options: boolean | RequestContextOptions | undefined","options: boolean | RequestIdOptions | undefined","value: string","request: ElysiaRequest","options: RequestIdOptions","responseHeader","resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined","include: readonly RequestContextField[]","context: Record<string, unknown>","options: RequestContextOptions","set: ElysiaContext[\"set\"] | undefined","headers: Record<string, string | undefined>","ctx: ElysiaContext","responseTime: number","result: string | Record<string, unknown>","value: number","timestamp: number","value: unknown","escapeSpaces: boolean","predefinedFormats: Record<PredefinedFormat, FormatFunction>","category: string | readonly string[]","errorCodeToStatus: Record<string, number>","code: string | number","error: unknown","setStatus: number","elysiaRouteMethods: readonly ElysiaRouteMethod[]","plugin: unknown","buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>","applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void","callback: unknown","hooks: unknown","plugin: ElysiaRoutePlugin","routePlugin: unknown","restore: (() => void)[]","route","path: string","handler: unknown","hook?: unknown","method: string","childPlugin: unknown","args: readonly unknown[]","options: ElysiaLogTapeOptions","formatFn: FormatFunction","request: Request","set: ElysiaContext[\"set\"]","requestContext: ElysiaRequestContextState | undefined","store: LoggerStore","plugin: Elysia<any>","finishLocalSetup: (() => void) | undefined"],"sources":["../src/mod.ts"],"sourcesContent":["import { Elysia } from \"elysia\";\nimport { getLogger, type LogLevel, withContext } from \"@logtape/logtape\";\n\nimport {\n createCompatibility,\n materializeHeaders,\n nativeErrorCode,\n wrapV2LocalContext,\n} from \"./compat.ts\";\n\nexport type { LogLevel } from \"@logtape/logtape\";\n\ntype ElysiaRequestHeaders = Request extends {\n readonly headers: infer RequestHeaders;\n} ? RequestHeaders & {\n get(name: string): string | null;\n }\n : {\n get(name: string): string | null;\n };\n\n/**\n * Web Request interface exposed to Elysia request hooks.\n * @since 2.2.0\n */\nexport interface ElysiaRequest extends Request {\n /** HTTP request method */\n readonly method: string;\n /** Request URL */\n readonly url: string;\n /** Request headers */\n readonly headers: ElysiaRequestHeaders;\n}\n\n/**\n * Minimal Elysia Context interface for compatibility.\n * @since 2.0.0\n */\nexport interface ElysiaContext {\n request: ElysiaRequest;\n path: string;\n set: {\n status: number;\n headers: Record<string, string | undefined>;\n };\n}\n\n/**\n * Plugin scope options for controlling hook propagation.\n * @since 2.0.0\n */\nexport type PluginScope = \"global\" | \"scoped\" | \"local\";\n\n/**\n * Predefined log format names.\n * @since 2.0.0\n */\nexport type PredefinedFormat =\n /**\n * @deprecated Use `\"structured-combined\"` instead.\n */\n | \"combined\"\n /**\n * @deprecated Use `\"structured-common\"` instead.\n */\n | \"common\"\n | \"structured-combined\"\n | \"structured-common\"\n | \"morgan-combined\"\n | \"morgan-common\"\n | \"dev\"\n | \"short\"\n | \"tiny\";\n\n/**\n * Custom format function for request logging.\n *\n * @param ctx The Elysia context object.\n * @param responseTime The response time in milliseconds.\n * @returns A string message or an object with structured properties.\n * @since 2.0.0\n */\nexport type FormatFunction = (\n ctx: ElysiaContext,\n responseTime: number,\n) => string | Record<string, unknown>;\n\n/**\n * Structured log properties for HTTP requests.\n * @since 2.0.0\n */\nexport interface RequestLogProperties {\n /** HTTP request method */\n method: string;\n /** Request URL */\n url: string;\n /** Request path */\n path: string;\n /** HTTP response status code */\n status: number;\n /** Response time in milliseconds */\n responseTime: number;\n /** Response content-length header value */\n contentLength: string | undefined;\n /** Remote client address (from X-Forwarded-For header) */\n remoteAddr: string | undefined;\n /** User-Agent header value */\n userAgent: string | undefined;\n /** Referrer header value */\n referrer: string | undefined;\n}\n\n/**\n * Request fields that can be added to the implicit request context.\n * @since 2.2.0\n */\nexport type RequestContextField =\n | \"requestId\"\n | \"method\"\n | \"url\"\n | \"path\"\n | \"userAgent\"\n | \"remoteAddr\"\n | \"referrer\";\n\n/**\n * Options for extracting, generating, and propagating a request ID.\n * @since 2.2.0\n */\nexport interface RequestIdOptions {\n /**\n * The property name used in implicit context and request log records.\n * @default \"requestId\"\n */\n readonly property?: string;\n\n /**\n * Incoming request headers to inspect in order.\n * @default [\"x-request-id\"]\n */\n readonly headerNames?: readonly string[];\n\n /**\n * Response header that receives the resolved request ID.\n * Set to `false` to disable response header propagation.\n * @default \"x-request-id\"\n */\n readonly responseHeader?: string | false;\n\n /**\n * Generates a request ID when no incoming header is present.\n * @default crypto.randomUUID()\n */\n readonly generate?: () => string;\n\n /**\n * Normalizes an incoming request ID. Return `null` to reject the value and\n * keep looking for another header or generate a new ID.\n */\n readonly normalize?: (value: string) => string | null;\n}\n\n/**\n * Options for request-scoped implicit context.\n * @since 2.2.0\n */\nexport interface RequestContextOptions {\n /**\n * Enables request ID extraction, generation, and response propagation.\n * @default true\n */\n readonly requestId?: boolean | RequestIdOptions;\n\n /**\n * Fields to add to the implicit context.\n * @default [\"requestId\"]\n */\n readonly include?: readonly RequestContextField[];\n\n /**\n * Adds application-specific fields to the implicit request context.\n */\n readonly enrich?: (\n ctx: ElysiaContext,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>;\n}\n\n/**\n * Options for configuring the Elysia LogTape middleware.\n * @since 2.0.0\n */\nexport interface ElysiaLogTapeOptions {\n /**\n * The LogTape category to use for logging.\n * @default [\"elysia\"]\n */\n readonly category?: string | readonly string[];\n\n /**\n * The log level to use for request logging.\n * @default \"info\"\n */\n readonly level?: LogLevel;\n\n /**\n * The format for log output.\n * Can be a predefined format name or a custom format function.\n *\n * Structured formats:\n * - `\"structured-combined\"` - Structured request properties (default)\n * - `\"structured-common\"` - Structured request properties without\n * referrer/userAgent\n * - `\"combined\"` - Deprecated alias for `\"structured-combined\"`\n * - `\"common\"` - Deprecated alias for `\"structured-common\"`\n *\n * Text formats:\n * - `\"morgan-combined\"` - Morgan-compatible combined access log (string)\n * - `\"morgan-common\"` - Morgan-compatible common access log (string)\n * - `\"dev\"` - Concise colored output for development (string)\n * - `\"short\"` - Shorter than common (string)\n * - `\"tiny\"` - Minimal output (string)\n *\n * @default \"structured-combined\"\n */\n readonly format?: PredefinedFormat | FormatFunction;\n\n /**\n * Function to determine whether logging should be skipped.\n * Return `true` to skip logging for a request.\n *\n * @example Skip logging for health check endpoint\n * ```typescript\n * app.use(elysiaLogger({\n * skip: (ctx) => ctx.path === \"/health\",\n * }));\n * ```\n *\n * @default () => false\n */\n readonly skip?: (ctx: ElysiaContext) => boolean;\n\n /**\n * If `true`, logs are written immediately when the request is received.\n * If `false` (default), logs are written after the response is sent.\n *\n * Note: When `logRequest` is `true`, response-related properties\n * (status, responseTime, contentLength) will not be available.\n *\n * @default false\n */\n readonly logRequest?: boolean;\n\n /**\n * The plugin scope for controlling how lifecycle hooks are propagated.\n *\n * - `\"global\"` - Hooks apply to all routes in the application\n * - `\"scoped\"` - Hooks apply to the parent instance where the plugin is used\n * - `\"local\"` - Hooks only apply within the plugin itself\n *\n * @default \"global\"\n */\n readonly scope?: PluginScope;\n\n /**\n * Enables request-scoped implicit context and request ID correlation.\n *\n * When set to `true`, the plugin reads the `x-request-id` header, generates\n * one when it is absent, writes it to the `x-request-id` response header,\n * and adds `requestId` to all LogTape records emitted while handling the\n * request.\n *\n * @default false\n * @since 2.2.0\n */\n readonly context?: boolean | RequestContextOptions;\n}\n\nconst defaultRequestIdHeader = \"x-request-id\";\n\n/**\n * Per-request context state stored outside Elysia's shared store.\n */\ninterface ElysiaRequestContextState {\n readonly context: Record<string, unknown>;\n readonly startTime?: number;\n readonly responseHeader?: {\n readonly name: string;\n readonly value: string;\n };\n readonly set?: ElysiaContext[\"set\"];\n}\n\n/**\n * Normalize request context options.\n */\nfunction normalizeRequestContextOptions(\n options: boolean | RequestContextOptions | undefined,\n): RequestContextOptions | undefined {\n if (options === true) return {};\n if (options === false || options == null) return undefined;\n return options;\n}\n\n/**\n * Normalize request ID options.\n */\nfunction normalizeRequestIdOptions(\n options: boolean | RequestIdOptions | undefined,\n): RequestIdOptions | undefined {\n if (options === false) return undefined;\n if (options === true || options == null) return {};\n return options;\n}\n\n/**\n * Generate a request ID with Web Crypto when possible.\n */\nfunction generateRequestId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Normalize an incoming request ID.\n */\nfunction defaultNormalizeRequestId(value: string): string | null {\n const trimmed = value.trim();\n return trimmed === \"\" ? null : trimmed;\n}\n\n/**\n * Resolve a request path from a request URL.\n */\nfunction getPath(request: ElysiaRequest): string {\n return new URL(request.url, \"http://localhost\").pathname;\n}\n\n/**\n * Resolve the request ID for a request.\n */\nfunction resolveRequestId(\n request: ElysiaRequest,\n options: RequestIdOptions,\n): {\n readonly property: string;\n readonly value: string;\n readonly responseHeader?: string;\n} {\n const property = options.property ?? \"requestId\";\n const normalize = options.normalize ?? defaultNormalizeRequestId;\n const headerNames = options.headerNames ?? [defaultRequestIdHeader];\n for (const headerName of headerNames) {\n const headerValue = request.headers.get(headerName);\n if (headerValue == null) continue;\n const normalized = normalize(headerValue);\n if (normalized != null) {\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: normalized,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n }\n }\n const generated = (options.generate ?? generateRequestId)();\n const responseHeader = options.responseHeader ?? defaultRequestIdHeader;\n return {\n property,\n value: generated,\n responseHeader: responseHeader === false ? undefined : responseHeader,\n };\n}\n\n/**\n * Get referrer from request headers.\n */\nfunction getReferrer(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"referrer\") ??\n request.headers.get(\"referer\") ??\n undefined;\n}\n\n/**\n * Get user agent from request headers.\n */\nfunction getUserAgent(request: ElysiaRequest): string | undefined {\n return request.headers.get(\"user-agent\") ?? undefined;\n}\n\n/**\n * Get remote address from X-Forwarded-For header.\n */\nfunction getRemoteAddr(request: ElysiaRequest): string | undefined {\n const forwarded = request.headers.get(\"x-forwarded-for\");\n if (forwarded) {\n // X-Forwarded-For can contain multiple IPs, take the first one\n const firstIp = forwarded.split(\",\")[0].trim();\n return firstIp || undefined;\n }\n return undefined;\n}\n\n/**\n * Build request context fields from a request.\n */\nfunction buildIncludedContext(\n request: ElysiaRequest,\n resolvedRequestId:\n | { readonly property: string; readonly value: string }\n | undefined,\n include: readonly RequestContextField[],\n): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n for (const field of include) {\n switch (field) {\n case \"requestId\":\n if (resolvedRequestId != null) {\n context[resolvedRequestId.property] = resolvedRequestId.value;\n }\n break;\n case \"method\":\n context.method = request.method;\n break;\n case \"url\":\n context.url = request.url;\n break;\n case \"path\":\n context.path = getPath(request);\n break;\n case \"userAgent\":\n context.userAgent = getUserAgent(request);\n break;\n case \"remoteAddr\":\n context.remoteAddr = getRemoteAddr(request);\n break;\n case \"referrer\":\n context.referrer = getReferrer(request);\n break;\n }\n }\n return context;\n}\n\n/**\n * Build the implicit context for a request.\n */\nasync function buildRequestContext(\n request: ElysiaRequest,\n options: RequestContextOptions,\n): Promise<ElysiaRequestContextState> {\n const requestIdOptions = normalizeRequestIdOptions(options.requestId);\n const resolvedRequestId = requestIdOptions == null\n ? undefined\n : resolveRequestId(request, requestIdOptions);\n const include = options.include ??\n (resolvedRequestId == null ? [] : [\"requestId\"] as const);\n const context = buildIncludedContext(request, resolvedRequestId, include);\n let set: ElysiaContext[\"set\"] | undefined;\n if (options.enrich != null) {\n set = { status: 200, headers: {} };\n Object.assign(\n context,\n await options.enrich({\n request,\n path: getPath(request),\n set,\n }),\n );\n }\n const responseHeader = resolvedRequestId?.responseHeader == null\n ? undefined\n : {\n name: resolvedRequestId.responseHeader,\n value: resolvedRequestId.value,\n };\n return { context, responseHeader, set };\n}\n\n/**\n * Get content length from response headers.\n */\nfunction getContentLength(\n headers: Record<string, string | undefined>,\n): string | undefined {\n const contentLength = headers[\"content-length\"];\n if (contentLength === undefined || contentLength === null) return undefined;\n return contentLength;\n}\n\n/**\n * Build structured log properties from context.\n */\nfunction buildProperties(\n ctx: ElysiaContext,\n responseTime: number,\n): RequestLogProperties {\n return {\n method: ctx.request.method,\n url: ctx.request.url,\n path: ctx.path,\n status: ctx.set.status,\n responseTime,\n contentLength: getContentLength(ctx.set.headers),\n remoteAddr: getRemoteAddr(ctx.request),\n userAgent: getUserAgent(ctx.request),\n referrer: getReferrer(ctx.request),\n };\n}\n\n/**\n * Add request context fields to a request log result.\n */\nfunction withRequestLogContext(\n result: string | Record<string, unknown>,\n context: Record<string, unknown>,\n): string | Record<string, unknown> {\n if (typeof result === \"string\") return result;\n return { ...result, ...context };\n}\n\nconst clfMonths = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n] as const;\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, \"0\");\n}\n\nfunction formatClfDate(timestamp: number = Date.now()): string {\n const date = new Date(timestamp);\n return `${pad2(date.getUTCDate())}/${\n clfMonths[date.getUTCMonth()]\n }/${date.getUTCFullYear()}:${pad2(date.getUTCHours())}:${\n pad2(date.getUTCMinutes())\n }:${pad2(date.getUTCSeconds())} +0000`;\n}\n\nfunction escapeAccessLogValue(value: unknown, escapeSpaces: boolean): string {\n const escaped = String(value)\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\r/g, \"\\\\r\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\t/g, \"\\\\t\");\n return escapeSpaces ? escaped.replace(/ /g, \"\\\\x20\") : escaped;\n}\n\nfunction formatAccessLogToken(value: unknown): string {\n if (value == null || value === \"\") return \"-\";\n return escapeAccessLogValue(value, true);\n}\n\nfunction formatAccessLogQuotedToken(value: unknown): string {\n if (value == null || value === \"\") return '\"-\"';\n return `\"${escapeAccessLogValue(value, false)}\"`;\n}\n\nfunction getRequestTarget(request: ElysiaRequest): string {\n const url = new URL(request.url, \"http://localhost\");\n return `${url.pathname}${url.search}`;\n}\n\nfunction getRemoteUser(request: ElysiaRequest): string | undefined {\n const authorization = request.headers.get(\"authorization\");\n if (authorization == null || authorization === \"\") return undefined;\n const match = /^Basic\\s+(.+)$/i.exec(authorization);\n if (match == null || typeof globalThis.atob !== \"function\") {\n return undefined;\n }\n try {\n const decoded = globalThis.atob(match[1]);\n const colonIndex = decoded.indexOf(\":\");\n const user = colonIndex < 0 ? decoded : decoded.slice(0, colonIndex);\n return user === \"\" ? undefined : user;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Structured combined format.\n * Returns all structured properties.\n */\nfunction formatCombined(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n return { ...buildProperties(ctx, responseTime) };\n}\n\n/**\n * Structured common format.\n * Like structured combined but without referrer and userAgent.\n */\nfunction formatCommon(\n ctx: ElysiaContext,\n responseTime: number,\n): Record<string, unknown> {\n const props = buildProperties(ctx, responseTime);\n const { referrer: _referrer, userAgent: _userAgent, ...rest } = props;\n return rest;\n}\n\n/**\n * Morgan combined format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n * \":referrer\" \":user-agent\"\n */\nfunction formatMorganCombined(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n } ${formatAccessLogQuotedToken(getReferrer(ctx.request))} ${\n formatAccessLogQuotedToken(getUserAgent(ctx.request))\n }`;\n}\n\n/**\n * Morgan common format.\n * :remote-addr - :remote-user [:date[clf]]\n * \":method :url HTTP/:http-version\" :status :res[content-length]\n */\nfunction formatMorganCommon(ctx: ElysiaContext): string {\n return `${formatAccessLogToken(getRemoteAddr(ctx.request))} - ${\n formatAccessLogToken(getRemoteUser(ctx.request))\n } [${formatClfDate()}] \"${formatAccessLogToken(ctx.request.method)} ${\n formatAccessLogToken(getRequestTarget(ctx.request))\n } HTTP/-\" ${formatAccessLogToken(ctx.set.status)} ${\n formatAccessLogToken(getContentLength(ctx.set.headers))\n }`;\n}\n\n/**\n * Dev format (colored output for development).\n * :method :path :status :response-time ms - :res[content-length]\n */\nfunction formatDev(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${\n responseTime.toFixed(3)\n } ms - ${contentLength}`;\n}\n\n/**\n * Short format.\n * :remote-addr :method :url :status :res[content-length] - :response-time ms\n */\nfunction formatShort(ctx: ElysiaContext, responseTime: number): string {\n const remoteAddr = getRemoteAddr(ctx.request) ?? \"-\";\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${remoteAddr} ${ctx.request.method} ${ctx.request.url} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Tiny format (minimal output).\n * :method :path :status :res[content-length] - :response-time ms\n */\nfunction formatTiny(ctx: ElysiaContext, responseTime: number): string {\n const contentLength = getContentLength(ctx.set.headers) ?? \"-\";\n return `${ctx.request.method} ${ctx.path} ${ctx.set.status} ${contentLength} - ${\n responseTime.toFixed(3)\n } ms`;\n}\n\n/**\n * Map of predefined format functions.\n */\nconst predefinedFormats: Record<PredefinedFormat, FormatFunction> = {\n combined: formatCombined,\n common: formatCommon,\n \"structured-combined\": formatCombined,\n \"structured-common\": formatCommon,\n \"morgan-combined\": formatMorganCombined,\n \"morgan-common\": formatMorganCommon,\n dev: formatDev,\n short: formatShort,\n tiny: formatTiny,\n};\n\n/**\n * Normalize category to array format.\n */\nfunction normalizeCategory(\n category: string | readonly string[],\n): readonly string[] {\n return typeof category === \"string\" ? [category] : category;\n}\n\n/**\n * Mapping of Elysia error codes to HTTP status codes.\n */\nconst errorCodeToStatus: Record<string, number> = {\n NOT_FOUND: 404,\n VALIDATION: 422,\n PARSE: 400,\n INTERNAL_SERVER_ERROR: 500,\n INVALID_COOKIE_SIGNATURE: 400,\n UNKNOWN: 500,\n};\n\n/**\n * Get the HTTP status code from an error context.\n * Checks error.status first, then falls back to error code mapping.\n */\nfunction getErrorStatus(\n code: string | number,\n error: unknown,\n setStatus: number,\n): number {\n // If code is already a number, use it as status\n if (typeof code === \"number\") {\n return code;\n }\n // Check if error has a status property (Elysia custom errors)\n if (\n error != null &&\n typeof error === \"object\" &&\n \"status\" in error &&\n typeof error.status === \"number\"\n ) {\n return error.status;\n }\n // Fall back to error code mapping\n if (code in errorCodeToStatus) {\n return errorCodeToStatus[code];\n }\n // Use set.status as last resort\n return setStatus;\n}\n\n/**\n * Internal store type for timing.\n */\ninterface LoggerStore {\n startTime: number;\n}\n\ntype ElysiaRouteContext = ElysiaContext & {\n readonly store: LoggerStore;\n};\n\ntype ElysiaRequestCallback = (\n this: unknown,\n ...args: unknown[]\n) => unknown;\n\ntype ElysiaRouteRegistrar = (\n path: string,\n handler: unknown,\n hook?: unknown,\n) => unknown;\n\ntype ElysiaUseRegistrar = (\n plugin: unknown,\n ...options: unknown[]\n) => unknown;\n\ntype ElysiaOnRegistrar = (...args: unknown[]) => unknown;\n\ntype ElysiaRouteMethod =\n | \"all\"\n | \"connect\"\n | \"delete\"\n | \"get\"\n | \"head\"\n | \"options\"\n | \"patch\"\n | \"post\"\n | \"put\";\n\nconst elysiaRouteMethods: readonly ElysiaRouteMethod[] = [\n \"all\",\n \"connect\",\n \"delete\",\n \"get\",\n \"head\",\n \"options\",\n \"patch\",\n \"post\",\n \"put\",\n];\n\nconst elysiaRouteHookKeys = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\nconst elysiaPluginLifecycleHookTypes = [\n \"afterHandle\",\n \"afterResponse\",\n \"beforeHandle\",\n \"derive\",\n \"error\",\n \"mapResponse\",\n \"parse\",\n \"resolve\",\n \"transform\",\n] as const;\n\ntype ElysiaRoutePlugin = Record<ElysiaRouteMethod, ElysiaRouteRegistrar> & {\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n on: ElysiaOnRegistrar;\n route: (\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) => unknown;\n use: ElysiaUseRegistrar;\n};\n\ninterface ElysiaRouteEntry {\n handler?: unknown;\n hooks?: unknown;\n}\n\nfunction getElysiaRouteEntries(\n plugin: unknown,\n visited = new Set<object>(),\n): ElysiaRouteEntry[] {\n if (plugin == null || typeof plugin !== \"object\") return [];\n if (visited.has(plugin)) return [];\n visited.add(plugin);\n const routePlugin = plugin as {\n readonly default?: unknown;\n readonly router?: {\n readonly history?: Record<string, ElysiaRouteEntry> | ElysiaRouteEntry[];\n };\n };\n const history = routePlugin.router?.history;\n const entries = Array.isArray(history)\n ? history\n : history == null\n ? []\n : Object.values(history);\n return entries.concat(getElysiaRouteEntries(routePlugin.default, visited));\n}\n\nfunction createElysiaRequestWrappers(\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n) {\n const wrappedHandlers = new WeakSet<ElysiaRequestCallback>();\n const wrappedHandlerCache = new WeakMap<\n ElysiaRequestCallback,\n ElysiaRequestCallback\n >();\n\n const getRouteContext = (\n value: unknown,\n ): ElysiaRouteContext | undefined => {\n if (value == null || typeof value !== \"object\") return undefined;\n const context = value as { readonly request?: unknown };\n if (!(context.request instanceof Request)) return undefined;\n return value as ElysiaRouteContext;\n };\n\n const wrapRequestCallback = (callback: unknown): unknown => {\n if (typeof callback !== \"function\") return callback;\n const requestCallback = callback as ElysiaRequestCallback;\n if (wrappedHandlers.has(requestCallback)) return callback;\n const cached = wrappedHandlerCache.get(requestCallback);\n if (cached != null) return cached;\n const wrapped = async function (\n this: unknown,\n ...args: unknown[]\n ): Promise<unknown> {\n const ctx = getRouteContext(args[0]);\n if (ctx == null) return await requestCallback.apply(this, args);\n ctx.store.startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n ctx.store.startTime = requestContext?.startTime ??\n ctx.store.startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n return await withContext(\n requestContext?.context ?? {},\n () => requestCallback.apply(this, args),\n );\n };\n wrappedHandlers.add(wrapped);\n wrappedHandlerCache.set(requestCallback, wrapped);\n return wrapped;\n };\n\n const wrapRouteHookValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(wrapRouteHookValue);\n if (typeof value === \"function\") return wrapRequestCallback(value);\n if (value == null || typeof value !== \"object\") return value;\n const hookContainer = value as { readonly fn?: unknown };\n if (typeof hookContainer.fn !== \"function\") return value;\n return {\n ...hookContainer,\n fn: wrapRequestCallback(hookContainer.fn),\n };\n };\n\n const wrapRouteHooks = (hooks: unknown): unknown => {\n if (hooks == null || typeof hooks !== \"object\") return hooks;\n const routeHooks = hooks as Record<string, unknown>;\n let changed = false;\n const wrappedHooks = { ...routeHooks };\n for (const key of elysiaRouteHookKeys) {\n const value = routeHooks[key];\n const wrappedValue = wrapRouteHookValue(value);\n if (wrappedValue === value) continue;\n wrappedHooks[key] = wrappedValue;\n changed = true;\n }\n return changed ? wrappedHooks : hooks;\n };\n\n return { wrapRequestCallback, wrapRouteHookValue, wrapRouteHooks };\n}\n\n/**\n * Wrap routes registered on a local plugin with implicit LogTape context.\n */\nfunction wrapLocalRouteHandlers(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRequestCallback, wrapRouteHooks } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const wrapRouteEntries = (routePlugin: unknown): (() => void)[] => {\n const restore: (() => void)[] = [];\n for (const route of getElysiaRouteEntries(routePlugin)) {\n const handler = route.handler;\n const wrappedHandler = wrapRequestCallback(handler);\n if (wrappedHandler !== handler) {\n route.handler = wrappedHandler;\n restore.push(() => {\n route.handler = handler;\n });\n }\n const hooks = route.hooks;\n const wrappedHooks = wrapRouteHooks(hooks);\n if (wrappedHooks !== hooks) {\n route.hooks = wrappedHooks;\n restore.push(() => {\n route.hooks = hooks;\n });\n }\n }\n return restore;\n };\n\n for (const method of elysiaRouteMethods) {\n const register = plugin[method].bind(plugin);\n plugin[method] = ((path: string, handler: unknown, hook?: unknown) =>\n register(\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRouteRegistrar;\n }\n\n const route = plugin.route.bind(plugin);\n plugin.route = ((\n method: string,\n path: string,\n handler: unknown,\n hook?: unknown,\n ) =>\n route(\n method,\n path,\n wrapRequestCallback(handler),\n wrapRouteHooks(hook),\n )) as ElysiaRoutePlugin[\"route\"];\n\n const use = plugin.use.bind(plugin);\n plugin.use = ((childPlugin: unknown, ...options: unknown[]) => {\n const restore = wrapRouteEntries(childPlugin);\n try {\n return use(childPlugin, ...options);\n } finally {\n for (const restoreRouteHandler of restore) restoreRouteHandler();\n }\n }) as ElysiaUseRegistrar;\n}\n\n/**\n * Wrap local plugin lifecycle hooks registered by downstream code.\n */\nfunction wrapLocalLifecycleHooks(\n plugin: ElysiaRoutePlugin,\n buildAndStoreRequestContext: (\n request: Request,\n ) => Promise<ElysiaRequestContextState | undefined>,\n applyRequestContextToSet: (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ) => void,\n): void {\n const { wrapRouteHookValue } = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n\n const isLifecycleHookType = (value: unknown): boolean =>\n typeof value === \"string\" &&\n (elysiaPluginLifecycleHookTypes as readonly string[]).includes(value);\n\n const wrapLifecycleHookArgs = (args: readonly unknown[]): unknown[] => {\n const wrappedArgs = [...args];\n if (isLifecycleHookType(args[0])) {\n if (args.length > 1) wrappedArgs[1] = wrapRouteHookValue(args[1]);\n if (args.length > 2) wrappedArgs[2] = wrapRouteHookValue(args[2]);\n } else if (isLifecycleHookType(args[1]) && args.length > 2) {\n wrappedArgs[2] = wrapRouteHookValue(args[2]);\n }\n return wrappedArgs;\n };\n\n const on = plugin.on.bind(plugin);\n plugin.on =\n ((...args: unknown[]) =>\n on(...wrapLifecycleHookArgs(args))) as ElysiaOnRegistrar;\n}\n\n/**\n * Creates Elysia plugin for HTTP request logging using LogTape.\n *\n * This plugin provides Morgan-compatible request logging with LogTape\n * as the backend, supporting structured logging and customizable formats.\n *\n * @example Basic usage\n * ```typescript\n * import { Elysia } from \"elysia\";\n * import { configure, getConsoleSink } from \"@logtape/logtape\";\n * import { elysiaLogger } from \"@logtape/elysia\";\n *\n * await configure({\n * sinks: { console: getConsoleSink() },\n * loggers: [\n * { category: [\"elysia\"], sinks: [\"console\"], lowestLevel: \"info\" }\n * ],\n * });\n *\n * const app = new Elysia()\n * .use(elysiaLogger())\n * .get(\"/\", () => ({ hello: \"world\" }))\n * .listen(3000);\n * ```\n *\n * @example With custom options\n * ```typescript\n * app.use(elysiaLogger({\n * category: [\"myapp\", \"http\"],\n * level: \"debug\",\n * format: \"dev\",\n * skip: (ctx) => ctx.path === \"/health\",\n * scope: \"scoped\",\n * }));\n * ```\n *\n * @example With custom format function\n * ```typescript\n * app.use(elysiaLogger({\n * format: (ctx, responseTime) => ({\n * method: ctx.request.method,\n * path: ctx.path,\n * status: ctx.set.status,\n * duration: responseTime,\n * }),\n * }));\n * ```\n *\n * @param options Configuration options for the plugin.\n * @returns Elysia plugin instance.\n * @since 2.0.0\n */\n// deno-lint-ignore no-explicit-any\nexport function elysiaLogger(options: ElysiaLogTapeOptions = {}): Elysia<any> {\n const category = normalizeCategory(options.category ?? [\"elysia\"]);\n const logger = getLogger(category);\n const level = options.level ?? \"info\";\n const formatOption = options.format ?? \"structured-combined\";\n const skip = options.skip ?? (() => false);\n const logRequest = options.logRequest ?? false;\n const scope = options.scope ?? \"global\";\n const contextOptions = normalizeRequestContextOptions(options.context);\n const requestContextStates = new WeakMap<\n Request,\n ElysiaRequestContextState\n >();\n const shouldWrapContext = contextOptions != null;\n const shouldWrapAllRoutes = contextOptions != null && scope !== \"local\";\n\n // Resolve format function\n const formatFn: FormatFunction = typeof formatOption === \"string\"\n ? predefinedFormats[formatOption]\n : formatOption;\n\n const logMethod = logger[level].bind(logger);\n const errorLogMethod = logger.error.bind(logger);\n\n const buildAndStoreRequestContext = async (\n request: Request,\n ): Promise<ElysiaRequestContextState | undefined> => {\n if (contextOptions == null) return undefined;\n const existingContext = requestContextStates.get(request);\n if (existingContext != null) return existingContext;\n const startTime = performance.now();\n const requestContext = {\n ...await buildRequestContext(request, contextOptions),\n startTime,\n };\n requestContextStates.set(request, requestContext);\n return requestContext;\n };\n\n const applyRequestContextToSet = (\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n materializeHeaders(set);\n if (requestContext?.responseHeader != null) {\n set.headers[requestContext.responseHeader.name] =\n requestContext.responseHeader.value;\n }\n if (requestContext?.set != null) {\n if (requestContext.set.status !== 200) {\n set.status = requestContext.set.status;\n }\n Object.assign(set.headers, requestContext.set.headers);\n }\n };\n\n const applyRequestContextToRequest = (\n store: LoggerStore,\n set: ElysiaContext[\"set\"],\n requestContext: ElysiaRequestContextState | undefined,\n ): void => {\n store.startTime = requestContext?.startTime ?? performance.now();\n applyRequestContextToSet(set, requestContext);\n };\n\n // deno-lint-ignore no-explicit-any\n let plugin: Elysia<any> = new Elysia({\n name: \"@logtape/elysia\",\n seed: options,\n });\n\n const compatibility = createCompatibility(plugin);\n let finishLocalSetup: (() => void) | undefined;\n\n if (shouldWrapContext && scope === \"local\") {\n if (compatibility.v2) {\n const wrappers = createElysiaRequestWrappers(\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n finishLocalSetup = wrapV2LocalContext(\n plugin,\n wrappers.wrapRequestCallback,\n `@logtape/elysia/context/${generateRequestId()}`,\n );\n } else {\n wrapLocalRouteHandlers(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n }\n } else if (shouldWrapContext) {\n // Elysia lifecycle hooks cannot wrap downstream handlers, so use wrap()\n // to keep AsyncLocalStorage active for the whole route execution.\n plugin = plugin.wrap((handle) => {\n return async (request: Request) => {\n const requestContext = await buildAndStoreRequestContext(request);\n return await withContext(\n requestContext?.context ?? {},\n () => handle(request),\n );\n };\n });\n }\n\n plugin.state(\"startTime\", 0);\n compatibility.register(\"Request\", ({ request, set, store }) => {\n const requestContext = requestContextStates.get(request);\n if (requestContext == null && shouldWrapAllRoutes) {\n (store as LoggerStore).startTime = performance.now();\n return buildAndStoreRequestContext(request).then((resolvedContext) => {\n applyRequestContextToRequest(\n store as LoggerStore,\n set,\n resolvedContext,\n );\n });\n }\n applyRequestContextToRequest(store as LoggerStore, set, requestContext);\n });\n\n if (scope === \"local\" && (contextOptions != null || logRequest)) {\n compatibility.register(\"BeforeHandle\", async (ctx) => {\n (ctx.store as LoggerStore).startTime = performance.now();\n const requestContext = await buildAndStoreRequestContext(ctx.request);\n (ctx.store as LoggerStore).startTime = requestContext?.startTime ??\n (ctx.store as LoggerStore).startTime;\n applyRequestContextToSet(ctx.set, requestContext);\n if (logRequest && !skip(ctx as unknown as ElysiaContext)) {\n const context = requestContext?.context ?? {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n context,\n );\n if (typeof result === \"string\") {\n logMethod(result, context);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n\n if (logRequest) {\n // Log immediately when request arrives\n if (scope !== \"local\") {\n compatibility.register(\"Request\", (ctx) => {\n if (!skip(ctx as unknown as ElysiaContext)) {\n const requestContext =\n requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, 0),\n requestContext,\n );\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url}\", result);\n }\n }\n });\n }\n } else {\n // Log after handler completes\n compatibility.register(\"AfterHandle\", (ctx) => {\n if (skip(ctx as unknown as ElysiaContext)) return;\n\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const requestContext = requestContextStates.get(ctx.request)?.context ??\n {};\n const result = withRequestLogContext(\n formatFn(ctx as unknown as ElysiaContext, responseTime),\n requestContext,\n );\n\n if (typeof result === \"string\") {\n logMethod(result, requestContext);\n } else {\n logMethod(\"{method} {url} {status} - {responseTime} ms\", result);\n }\n });\n }\n\n // Add error logging\n compatibility.register(\"Error\", (ctx) => {\n const store = ctx.store as LoggerStore;\n const responseTime = performance.now() - store.startTime;\n const elysiaCtx = ctx as unknown as ElysiaContext;\n\n if (skip(elysiaCtx)) return;\n\n const props = buildProperties(elysiaCtx, responseTime);\n const requestContext = requestContextStates.get(ctx.request)?.context ?? {};\n // Get the correct HTTP status code from error context\n const status = compatibility.v2\n ? elysiaCtx.set.status\n : getErrorStatus(ctx.code ?? \"UNKNOWN\", ctx.error, elysiaCtx.set.status);\n const errorCode = compatibility.v2 ? nativeErrorCode(ctx.error) : ctx.code;\n // Extract error message safely\n const error = ctx.error as { message?: string } | undefined;\n const errorMessage = error?.message ?? \"Unknown error\";\n errorLogMethod(\n \"Error: {method} {url} {status} - {responseTime} ms - {errorMessage}\",\n {\n ...props,\n ...requestContext,\n status,\n errorMessage,\n ...(errorCode === undefined ? {} : { errorCode }),\n },\n );\n });\n\n if (shouldWrapContext && scope === \"local\") {\n if (finishLocalSetup != null) finishLocalSetup();\n else {\n wrapLocalLifecycleHooks(\n plugin as unknown as ElysiaRoutePlugin,\n buildAndStoreRequestContext,\n applyRequestContextToSet,\n );\n }\n }\n\n // Apply the native scope while keeping the public v1/v2 API identical.\n if (scope !== \"local\") compatibility.as(scope);\n\n // deno-lint-ignore no-explicit-any\n return plugin as unknown as Elysia<any>;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/elysia",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.881+28c1f35f",
|
|
4
4
|
"description": "Elysia adapter for LogTape logging library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logging",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"dist/"
|
|
54
54
|
],
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"elysia": "^1.4.0",
|
|
57
|
-
"@logtape/logtape": "^2.4.0-dev.
|
|
56
|
+
"elysia": "^1.4.0 || ^2.0.0-beta.12",
|
|
57
|
+
"@logtape/logtape": "^2.4.0-dev.881+28c1f35f"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@alinea/suite": "^0.6.3",
|