@pegasusheavy/nestjs-platform-deno 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/dist/index.cjs +1708 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +786 -0
- package/dist/index.d.ts +786 -0
- package/dist/index.js +1673 -0
- package/dist/index.js.map +1 -0
- package/package.json +96 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1708 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DenoAdapter: () => DenoAdapter,
|
|
24
|
+
createExpressRequest: () => createExpressRequest,
|
|
25
|
+
createExpressResponse: () => createExpressResponse,
|
|
26
|
+
createFastifyLogger: () => createFastifyLogger,
|
|
27
|
+
createFastifyReply: () => createFastifyReply,
|
|
28
|
+
createFastifyRequest: () => createFastifyRequest,
|
|
29
|
+
wrapExpressMiddleware: () => wrapExpressMiddleware,
|
|
30
|
+
wrapFastifyHook: () => wrapFastifyHook,
|
|
31
|
+
wrapFastifyPlugin: () => wrapFastifyPlugin
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/adapters/deno-adapter.ts
|
|
36
|
+
var import_core = require("@nestjs/core");
|
|
37
|
+
var import_common = require("@nestjs/common");
|
|
38
|
+
|
|
39
|
+
// src/compat/express-compat.ts
|
|
40
|
+
var STATUS_MESSAGES = {
|
|
41
|
+
100: "Continue",
|
|
42
|
+
101: "Switching Protocols",
|
|
43
|
+
102: "Processing",
|
|
44
|
+
200: "OK",
|
|
45
|
+
201: "Created",
|
|
46
|
+
202: "Accepted",
|
|
47
|
+
204: "No Content",
|
|
48
|
+
206: "Partial Content",
|
|
49
|
+
301: "Moved Permanently",
|
|
50
|
+
302: "Found",
|
|
51
|
+
303: "See Other",
|
|
52
|
+
304: "Not Modified",
|
|
53
|
+
307: "Temporary Redirect",
|
|
54
|
+
308: "Permanent Redirect",
|
|
55
|
+
400: "Bad Request",
|
|
56
|
+
401: "Unauthorized",
|
|
57
|
+
403: "Forbidden",
|
|
58
|
+
404: "Not Found",
|
|
59
|
+
405: "Method Not Allowed",
|
|
60
|
+
406: "Not Acceptable",
|
|
61
|
+
408: "Request Timeout",
|
|
62
|
+
409: "Conflict",
|
|
63
|
+
410: "Gone",
|
|
64
|
+
413: "Payload Too Large",
|
|
65
|
+
415: "Unsupported Media Type",
|
|
66
|
+
422: "Unprocessable Entity",
|
|
67
|
+
429: "Too Many Requests",
|
|
68
|
+
500: "Internal Server Error",
|
|
69
|
+
501: "Not Implemented",
|
|
70
|
+
502: "Bad Gateway",
|
|
71
|
+
503: "Service Unavailable",
|
|
72
|
+
504: "Gateway Timeout"
|
|
73
|
+
};
|
|
74
|
+
function headersToObject(headers) {
|
|
75
|
+
const result = {};
|
|
76
|
+
headers.forEach((value, key) => {
|
|
77
|
+
const existing = result[key.toLowerCase()];
|
|
78
|
+
if (existing) {
|
|
79
|
+
if (Array.isArray(existing)) {
|
|
80
|
+
existing.push(value);
|
|
81
|
+
} else {
|
|
82
|
+
result[key.toLowerCase()] = [existing, value];
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
result[key.toLowerCase()] = value;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
function parseAccept(acceptHeader, types) {
|
|
91
|
+
if (!acceptHeader || types.length === 0) return false;
|
|
92
|
+
const accepts = acceptHeader.split(",").map((part) => {
|
|
93
|
+
const [type, ...params] = part.trim().split(";");
|
|
94
|
+
let q = 1;
|
|
95
|
+
params.forEach((p) => {
|
|
96
|
+
const [key, value] = p.trim().split("=");
|
|
97
|
+
if (key === "q") q = parseFloat(value) || 1;
|
|
98
|
+
});
|
|
99
|
+
return { type: type.trim(), q };
|
|
100
|
+
}).sort((a, b) => b.q - a.q);
|
|
101
|
+
for (const accept of accepts) {
|
|
102
|
+
for (const type of types) {
|
|
103
|
+
if (accept.type === "*/*" || accept.type === type || accept.type.endsWith("/*") && type.startsWith(accept.type.slice(0, -1))) {
|
|
104
|
+
return type;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
function serializeCookie(name, value, options = {}) {
|
|
111
|
+
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
112
|
+
if (options.maxAge !== void 0) {
|
|
113
|
+
cookie += `; Max-Age=${options.maxAge}`;
|
|
114
|
+
}
|
|
115
|
+
if (options.domain) {
|
|
116
|
+
cookie += `; Domain=${options.domain}`;
|
|
117
|
+
}
|
|
118
|
+
if (options.path) {
|
|
119
|
+
cookie += `; Path=${options.path}`;
|
|
120
|
+
} else {
|
|
121
|
+
cookie += "; Path=/";
|
|
122
|
+
}
|
|
123
|
+
if (options.expires) {
|
|
124
|
+
cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
125
|
+
}
|
|
126
|
+
if (options.httpOnly) {
|
|
127
|
+
cookie += "; HttpOnly";
|
|
128
|
+
}
|
|
129
|
+
if (options.secure) {
|
|
130
|
+
cookie += "; Secure";
|
|
131
|
+
}
|
|
132
|
+
if (options.sameSite) {
|
|
133
|
+
if (options.sameSite === true) {
|
|
134
|
+
cookie += "; SameSite=Strict";
|
|
135
|
+
} else {
|
|
136
|
+
cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return cookie;
|
|
140
|
+
}
|
|
141
|
+
function createExpressRequest(denoReq) {
|
|
142
|
+
const headersObj = headersToObject(denoReq.headers);
|
|
143
|
+
const cookieHeader = denoReq.headers.get("cookie") || "";
|
|
144
|
+
const cookies = {};
|
|
145
|
+
cookieHeader.split(";").forEach((cookie) => {
|
|
146
|
+
const [name, ...valueParts] = cookie.trim().split("=");
|
|
147
|
+
if (name) {
|
|
148
|
+
cookies[decodeURIComponent(name.trim())] = decodeURIComponent(valueParts.join("="));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
const req = {
|
|
152
|
+
// Core properties
|
|
153
|
+
method: denoReq.method,
|
|
154
|
+
url: denoReq.originalUrl || denoReq.path || "/",
|
|
155
|
+
originalUrl: denoReq.originalUrl || denoReq.path || "/",
|
|
156
|
+
baseUrl: denoReq.baseUrl || "",
|
|
157
|
+
path: denoReq.path || "/",
|
|
158
|
+
hostname: denoReq.hostname || "",
|
|
159
|
+
ip: denoReq.ip,
|
|
160
|
+
protocol: denoReq.protocol || "http",
|
|
161
|
+
secure: denoReq.secure || false,
|
|
162
|
+
headers: headersObj,
|
|
163
|
+
// Parsed data
|
|
164
|
+
params: denoReq.params,
|
|
165
|
+
query: denoReq.query,
|
|
166
|
+
body: denoReq.body,
|
|
167
|
+
// Raw access
|
|
168
|
+
raw: denoReq.raw,
|
|
169
|
+
// Cookies
|
|
170
|
+
cookies,
|
|
171
|
+
signedCookies: {},
|
|
172
|
+
// Computed properties
|
|
173
|
+
xhr: denoReq.headers.get("x-requested-with")?.toLowerCase() === "xmlhttprequest",
|
|
174
|
+
subdomains: denoReq.hostname?.split(".").slice(0, -2).reverse() || [],
|
|
175
|
+
// Methods
|
|
176
|
+
get(name) {
|
|
177
|
+
const key = name.toLowerCase();
|
|
178
|
+
const value = headersObj[key];
|
|
179
|
+
return Array.isArray(value) ? value[0] : value;
|
|
180
|
+
},
|
|
181
|
+
header(name) {
|
|
182
|
+
return this.get(name);
|
|
183
|
+
},
|
|
184
|
+
is(type) {
|
|
185
|
+
const contentType = denoReq.headers.get("content-type");
|
|
186
|
+
if (!contentType) return null;
|
|
187
|
+
const types = Array.isArray(type) ? type : [type];
|
|
188
|
+
for (const t of types) {
|
|
189
|
+
if (contentType.includes(t) || contentType.includes(t.replace("/", ""))) {
|
|
190
|
+
return t;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
},
|
|
195
|
+
accepts(...types) {
|
|
196
|
+
return parseAccept(denoReq.headers.get("accept") || void 0, types);
|
|
197
|
+
},
|
|
198
|
+
acceptsEncodings(...encodings) {
|
|
199
|
+
return parseAccept(denoReq.headers.get("accept-encoding") || void 0, encodings);
|
|
200
|
+
},
|
|
201
|
+
acceptsCharsets(...charsets) {
|
|
202
|
+
return parseAccept(denoReq.headers.get("accept-charset") || void 0, charsets);
|
|
203
|
+
},
|
|
204
|
+
acceptsLanguages(...langs) {
|
|
205
|
+
return parseAccept(denoReq.headers.get("accept-language") || void 0, langs);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const modifiedSince = denoReq.headers.get("if-modified-since");
|
|
209
|
+
const noneMatch = denoReq.headers.get("if-none-match");
|
|
210
|
+
req.fresh = !!(modifiedSince || noneMatch);
|
|
211
|
+
req.stale = !req.fresh;
|
|
212
|
+
return req;
|
|
213
|
+
}
|
|
214
|
+
function createExpressResponse(denoRes) {
|
|
215
|
+
let statusMessage = "OK";
|
|
216
|
+
const cookies = [];
|
|
217
|
+
const res = {
|
|
218
|
+
// Properties
|
|
219
|
+
get statusCode() {
|
|
220
|
+
return denoRes.statusCode;
|
|
221
|
+
},
|
|
222
|
+
set statusCode(code) {
|
|
223
|
+
denoRes.statusCode = code;
|
|
224
|
+
statusMessage = STATUS_MESSAGES[code] || "Unknown";
|
|
225
|
+
},
|
|
226
|
+
get statusMessage() {
|
|
227
|
+
return statusMessage;
|
|
228
|
+
},
|
|
229
|
+
set statusMessage(msg) {
|
|
230
|
+
statusMessage = msg;
|
|
231
|
+
},
|
|
232
|
+
get headersSent() {
|
|
233
|
+
return denoRes.headersSent;
|
|
234
|
+
},
|
|
235
|
+
// Status methods
|
|
236
|
+
status(code) {
|
|
237
|
+
denoRes.status(code);
|
|
238
|
+
statusMessage = STATUS_MESSAGES[code] || "Unknown";
|
|
239
|
+
return this;
|
|
240
|
+
},
|
|
241
|
+
sendStatus(code) {
|
|
242
|
+
this.status(code);
|
|
243
|
+
denoRes.send(STATUS_MESSAGES[code] || String(code));
|
|
244
|
+
return this;
|
|
245
|
+
},
|
|
246
|
+
// Header methods
|
|
247
|
+
set(field, value) {
|
|
248
|
+
if (typeof field === "object") {
|
|
249
|
+
Object.entries(field).forEach(([key, val]) => {
|
|
250
|
+
if (Array.isArray(val)) {
|
|
251
|
+
val.forEach((v) => denoRes.headers.append(key, v));
|
|
252
|
+
} else {
|
|
253
|
+
denoRes.setHeader(key, val);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
} else if (value !== void 0) {
|
|
257
|
+
if (Array.isArray(value)) {
|
|
258
|
+
value.forEach((v) => denoRes.headers.append(field, v));
|
|
259
|
+
} else {
|
|
260
|
+
denoRes.setHeader(field, value);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return this;
|
|
264
|
+
},
|
|
265
|
+
header(field, value) {
|
|
266
|
+
if (value === void 0) {
|
|
267
|
+
return denoRes.getHeader(field) || void 0;
|
|
268
|
+
}
|
|
269
|
+
return this.set(field, value);
|
|
270
|
+
},
|
|
271
|
+
get(field) {
|
|
272
|
+
return denoRes.getHeader(field) || void 0;
|
|
273
|
+
},
|
|
274
|
+
append(field, value) {
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
value.forEach((v) => denoRes.headers.append(field, v));
|
|
277
|
+
} else {
|
|
278
|
+
denoRes.headers.append(field, value);
|
|
279
|
+
}
|
|
280
|
+
return this;
|
|
281
|
+
},
|
|
282
|
+
// Body methods
|
|
283
|
+
send(body) {
|
|
284
|
+
if (body === void 0) {
|
|
285
|
+
denoRes.end();
|
|
286
|
+
} else if (typeof body === "string") {
|
|
287
|
+
if (!denoRes.getHeader("Content-Type")) {
|
|
288
|
+
denoRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
289
|
+
}
|
|
290
|
+
denoRes.send(body);
|
|
291
|
+
} else if (Buffer.isBuffer(body)) {
|
|
292
|
+
if (!denoRes.getHeader("Content-Type")) {
|
|
293
|
+
denoRes.setHeader("Content-Type", "application/octet-stream");
|
|
294
|
+
}
|
|
295
|
+
denoRes.send(body);
|
|
296
|
+
} else if (typeof body === "object") {
|
|
297
|
+
return this.json(body);
|
|
298
|
+
} else {
|
|
299
|
+
denoRes.send(String(body));
|
|
300
|
+
}
|
|
301
|
+
return this;
|
|
302
|
+
},
|
|
303
|
+
json(body) {
|
|
304
|
+
denoRes.json(body);
|
|
305
|
+
return this;
|
|
306
|
+
},
|
|
307
|
+
jsonp(body) {
|
|
308
|
+
denoRes.json(body);
|
|
309
|
+
return this;
|
|
310
|
+
},
|
|
311
|
+
end(data) {
|
|
312
|
+
if (data !== void 0) {
|
|
313
|
+
denoRes.end(typeof data === "string" ? data : JSON.stringify(data));
|
|
314
|
+
} else {
|
|
315
|
+
denoRes.end();
|
|
316
|
+
}
|
|
317
|
+
return this;
|
|
318
|
+
},
|
|
319
|
+
// Redirect
|
|
320
|
+
redirect(statusOrUrl, url) {
|
|
321
|
+
if (typeof statusOrUrl === "number" && url) {
|
|
322
|
+
denoRes.redirect(url, statusOrUrl);
|
|
323
|
+
} else if (typeof statusOrUrl === "string") {
|
|
324
|
+
denoRes.redirect(statusOrUrl, 302);
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
// Content type
|
|
328
|
+
type(type) {
|
|
329
|
+
const mimeTypes = {
|
|
330
|
+
html: "text/html",
|
|
331
|
+
json: "application/json",
|
|
332
|
+
xml: "application/xml",
|
|
333
|
+
text: "text/plain",
|
|
334
|
+
js: "application/javascript",
|
|
335
|
+
css: "text/css"
|
|
336
|
+
};
|
|
337
|
+
const contentType = mimeTypes[type] || type;
|
|
338
|
+
denoRes.setHeader("Content-Type", contentType);
|
|
339
|
+
return this;
|
|
340
|
+
},
|
|
341
|
+
contentType(type) {
|
|
342
|
+
return this.type(type);
|
|
343
|
+
},
|
|
344
|
+
// Cookies
|
|
345
|
+
cookie(name, value, options = {}) {
|
|
346
|
+
const cookieStr = serializeCookie(name, value, options);
|
|
347
|
+
cookies.push(cookieStr);
|
|
348
|
+
denoRes.headers.append("Set-Cookie", cookieStr);
|
|
349
|
+
return this;
|
|
350
|
+
},
|
|
351
|
+
clearCookie(name, options = {}) {
|
|
352
|
+
const clearOptions = { ...options, expires: /* @__PURE__ */ new Date(0), maxAge: 0 };
|
|
353
|
+
return this.cookie(name, "", clearOptions);
|
|
354
|
+
},
|
|
355
|
+
// Other methods
|
|
356
|
+
location(url) {
|
|
357
|
+
denoRes.setHeader("Location", url);
|
|
358
|
+
return this;
|
|
359
|
+
},
|
|
360
|
+
links(links) {
|
|
361
|
+
const linkHeader = Object.entries(links).map(([rel, href]) => `<${href}>; rel="${rel}"`).join(", ");
|
|
362
|
+
denoRes.setHeader("Link", linkHeader);
|
|
363
|
+
return this;
|
|
364
|
+
},
|
|
365
|
+
vary(field) {
|
|
366
|
+
const existing = denoRes.getHeader("Vary");
|
|
367
|
+
if (existing) {
|
|
368
|
+
denoRes.setHeader("Vary", `${existing}, ${field}`);
|
|
369
|
+
} else {
|
|
370
|
+
denoRes.setHeader("Vary", field);
|
|
371
|
+
}
|
|
372
|
+
return this;
|
|
373
|
+
},
|
|
374
|
+
format(obj) {
|
|
375
|
+
const accept = denoRes.headers.get?.("Accept") || "*/*";
|
|
376
|
+
const types = Object.keys(obj);
|
|
377
|
+
const matched = parseAccept(accept, types);
|
|
378
|
+
if (matched && obj[matched]) {
|
|
379
|
+
obj[matched]();
|
|
380
|
+
} else if (obj.default) {
|
|
381
|
+
obj.default();
|
|
382
|
+
}
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
return res;
|
|
387
|
+
}
|
|
388
|
+
function wrapExpressMiddleware(middleware) {
|
|
389
|
+
return async (denoReq, denoRes, next) => {
|
|
390
|
+
const expressReq = createExpressRequest(denoReq);
|
|
391
|
+
const expressRes = createExpressResponse(denoRes);
|
|
392
|
+
return new Promise((resolve, reject) => {
|
|
393
|
+
const expressNext = (err) => {
|
|
394
|
+
if (err) {
|
|
395
|
+
if (middleware.length === 4) {
|
|
396
|
+
try {
|
|
397
|
+
const result = middleware(err, expressReq, expressRes, expressNext);
|
|
398
|
+
if (result instanceof Promise) {
|
|
399
|
+
result.catch(reject);
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
reject(e);
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
reject(err);
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
next().then(resolve).catch(reject);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
try {
|
|
412
|
+
const result = middleware(expressReq, expressRes, expressNext);
|
|
413
|
+
if (result instanceof Promise) {
|
|
414
|
+
result.catch(reject);
|
|
415
|
+
}
|
|
416
|
+
} catch (err) {
|
|
417
|
+
reject(err);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// src/compat/fastify-compat.ts
|
|
424
|
+
var requestIdCounter = 0;
|
|
425
|
+
function generateRequestId() {
|
|
426
|
+
return `req-${Date.now()}-${++requestIdCounter}`;
|
|
427
|
+
}
|
|
428
|
+
function headersToObject2(headers) {
|
|
429
|
+
const result = {};
|
|
430
|
+
headers.forEach((value, key) => {
|
|
431
|
+
const lowerKey = key.toLowerCase();
|
|
432
|
+
const existing = result[lowerKey];
|
|
433
|
+
if (existing) {
|
|
434
|
+
if (Array.isArray(existing)) {
|
|
435
|
+
existing.push(value);
|
|
436
|
+
} else {
|
|
437
|
+
result[lowerKey] = [existing, value];
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
result[lowerKey] = value;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
return result;
|
|
444
|
+
}
|
|
445
|
+
function createFastifyRequest(denoReq) {
|
|
446
|
+
const headersObj = headersToObject2(denoReq.headers);
|
|
447
|
+
const req = {
|
|
448
|
+
id: generateRequestId(),
|
|
449
|
+
params: denoReq.params,
|
|
450
|
+
query: denoReq.query,
|
|
451
|
+
body: denoReq.body,
|
|
452
|
+
headers: headersObj,
|
|
453
|
+
raw: denoReq.raw,
|
|
454
|
+
url: denoReq.originalUrl || denoReq.path || "/",
|
|
455
|
+
originalUrl: denoReq.originalUrl || denoReq.path || "/",
|
|
456
|
+
method: denoReq.method,
|
|
457
|
+
hostname: denoReq.hostname || "",
|
|
458
|
+
ip: denoReq.ip,
|
|
459
|
+
protocol: denoReq.secure ? "https" : "http",
|
|
460
|
+
routerPath: denoReq.path,
|
|
461
|
+
routerMethod: denoReq.method
|
|
462
|
+
};
|
|
463
|
+
return req;
|
|
464
|
+
}
|
|
465
|
+
function createFastifyReply(denoRes) {
|
|
466
|
+
let serializer = JSON.stringify;
|
|
467
|
+
const startTime = Date.now();
|
|
468
|
+
const reply = {
|
|
469
|
+
get statusCode() {
|
|
470
|
+
return denoRes.statusCode;
|
|
471
|
+
},
|
|
472
|
+
set statusCode(code) {
|
|
473
|
+
denoRes.statusCode = code;
|
|
474
|
+
},
|
|
475
|
+
get sent() {
|
|
476
|
+
return denoRes.headersSent;
|
|
477
|
+
},
|
|
478
|
+
raw: denoRes,
|
|
479
|
+
code(statusCode) {
|
|
480
|
+
denoRes.status(statusCode);
|
|
481
|
+
return this;
|
|
482
|
+
},
|
|
483
|
+
status(statusCode) {
|
|
484
|
+
return this.code(statusCode);
|
|
485
|
+
},
|
|
486
|
+
header(key, value) {
|
|
487
|
+
denoRes.setHeader(key, String(value));
|
|
488
|
+
return this;
|
|
489
|
+
},
|
|
490
|
+
headers(headers) {
|
|
491
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
492
|
+
denoRes.setHeader(key, String(value));
|
|
493
|
+
});
|
|
494
|
+
return this;
|
|
495
|
+
},
|
|
496
|
+
getHeader(key) {
|
|
497
|
+
return denoRes.getHeader(key) || void 0;
|
|
498
|
+
},
|
|
499
|
+
getHeaders() {
|
|
500
|
+
const result = {};
|
|
501
|
+
denoRes.headers.forEach((value, key) => {
|
|
502
|
+
result[key] = value;
|
|
503
|
+
});
|
|
504
|
+
return result;
|
|
505
|
+
},
|
|
506
|
+
removeHeader(key) {
|
|
507
|
+
denoRes.removeHeader(key);
|
|
508
|
+
return this;
|
|
509
|
+
},
|
|
510
|
+
hasHeader(key) {
|
|
511
|
+
return denoRes.getHeader(key) !== null;
|
|
512
|
+
},
|
|
513
|
+
send(payload) {
|
|
514
|
+
if (payload === void 0) {
|
|
515
|
+
denoRes.end();
|
|
516
|
+
} else if (typeof payload === "string") {
|
|
517
|
+
if (!denoRes.getHeader("Content-Type")) {
|
|
518
|
+
denoRes.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
519
|
+
}
|
|
520
|
+
denoRes.send(payload);
|
|
521
|
+
} else if (payload instanceof Uint8Array || payload instanceof ArrayBuffer) {
|
|
522
|
+
if (!denoRes.getHeader("Content-Type")) {
|
|
523
|
+
denoRes.setHeader("Content-Type", "application/octet-stream");
|
|
524
|
+
}
|
|
525
|
+
denoRes.send(payload);
|
|
526
|
+
} else if (typeof payload === "object") {
|
|
527
|
+
if (!denoRes.getHeader("Content-Type")) {
|
|
528
|
+
denoRes.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
529
|
+
}
|
|
530
|
+
denoRes.send(serializer(payload));
|
|
531
|
+
} else {
|
|
532
|
+
denoRes.send(String(payload));
|
|
533
|
+
}
|
|
534
|
+
return this;
|
|
535
|
+
},
|
|
536
|
+
serialize(payload) {
|
|
537
|
+
return serializer(payload);
|
|
538
|
+
},
|
|
539
|
+
serializer(fn) {
|
|
540
|
+
serializer = fn;
|
|
541
|
+
return this;
|
|
542
|
+
},
|
|
543
|
+
type(contentType) {
|
|
544
|
+
denoRes.setHeader("Content-Type", contentType);
|
|
545
|
+
return this;
|
|
546
|
+
},
|
|
547
|
+
redirect(statusCodeOrUrl, url) {
|
|
548
|
+
if (typeof statusCodeOrUrl === "number" && url) {
|
|
549
|
+
denoRes.redirect(url, statusCodeOrUrl);
|
|
550
|
+
} else if (typeof statusCodeOrUrl === "string") {
|
|
551
|
+
denoRes.redirect(statusCodeOrUrl, 302);
|
|
552
|
+
}
|
|
553
|
+
return this;
|
|
554
|
+
},
|
|
555
|
+
callNotFound() {
|
|
556
|
+
denoRes.status(404).json({
|
|
557
|
+
statusCode: 404,
|
|
558
|
+
error: "Not Found",
|
|
559
|
+
message: "Route not found"
|
|
560
|
+
});
|
|
561
|
+
},
|
|
562
|
+
getResponseTime() {
|
|
563
|
+
return Date.now() - startTime;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
return reply;
|
|
567
|
+
}
|
|
568
|
+
function isAsyncHook(hook) {
|
|
569
|
+
return hook.length <= 2;
|
|
570
|
+
}
|
|
571
|
+
function wrapFastifyHook(hook) {
|
|
572
|
+
return async (denoReq, denoRes, next) => {
|
|
573
|
+
const fastifyReq = createFastifyRequest(denoReq);
|
|
574
|
+
const fastifyReply = createFastifyReply(denoRes);
|
|
575
|
+
if (isAsyncHook(hook)) {
|
|
576
|
+
await hook(fastifyReq, fastifyReply);
|
|
577
|
+
if (!fastifyReply.sent) {
|
|
578
|
+
await next();
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
return new Promise((resolve, reject) => {
|
|
582
|
+
const done = (err) => {
|
|
583
|
+
if (err) {
|
|
584
|
+
reject(err);
|
|
585
|
+
} else if (!fastifyReply.sent) {
|
|
586
|
+
next().then(resolve).catch(reject);
|
|
587
|
+
} else {
|
|
588
|
+
resolve();
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
try {
|
|
592
|
+
hook(fastifyReq, fastifyReply, done);
|
|
593
|
+
} catch (err) {
|
|
594
|
+
reject(err);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function wrapFastifyPlugin(plugin, instance, opts = {}) {
|
|
601
|
+
return new Promise((resolve, reject) => {
|
|
602
|
+
if (plugin.length <= 2) {
|
|
603
|
+
const result = plugin(instance, opts);
|
|
604
|
+
if (result instanceof Promise) {
|
|
605
|
+
result.then(resolve).catch(reject);
|
|
606
|
+
} else {
|
|
607
|
+
resolve();
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
try {
|
|
611
|
+
plugin(instance, opts, (err) => {
|
|
612
|
+
if (err) {
|
|
613
|
+
reject(err);
|
|
614
|
+
} else {
|
|
615
|
+
resolve();
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
} catch (err) {
|
|
619
|
+
reject(err);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
function createFastifyLogger() {
|
|
625
|
+
const createLogFn = (level) => (msg, ...args) => {
|
|
626
|
+
console.log(`[${level.toUpperCase()}] ${msg}`, ...args);
|
|
627
|
+
};
|
|
628
|
+
return {
|
|
629
|
+
info: createLogFn("info"),
|
|
630
|
+
error: createLogFn("error"),
|
|
631
|
+
debug: createLogFn("debug"),
|
|
632
|
+
warn: createLogFn("warn"),
|
|
633
|
+
trace: createLogFn("trace"),
|
|
634
|
+
fatal: createLogFn("fatal"),
|
|
635
|
+
child(bindings) {
|
|
636
|
+
const prefix = Object.entries(bindings).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
637
|
+
const childLog = createFastifyLogger();
|
|
638
|
+
const wrap = (fn) => (msg, ...args) => fn(`[${prefix}] ${msg}`, ...args);
|
|
639
|
+
return {
|
|
640
|
+
...childLog,
|
|
641
|
+
info: wrap(childLog.info),
|
|
642
|
+
error: wrap(childLog.error),
|
|
643
|
+
debug: wrap(childLog.debug),
|
|
644
|
+
warn: wrap(childLog.warn),
|
|
645
|
+
trace: wrap(childLog.trace),
|
|
646
|
+
fatal: wrap(childLog.fatal)
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/adapters/deno-adapter.ts
|
|
653
|
+
var DenoAdapter = class _DenoAdapter extends import_core.AbstractHttpAdapter {
|
|
654
|
+
routes = [];
|
|
655
|
+
middlewares = [];
|
|
656
|
+
server;
|
|
657
|
+
abortController;
|
|
658
|
+
corsOptions;
|
|
659
|
+
errorHandler;
|
|
660
|
+
notFoundHandler;
|
|
661
|
+
staticAssetsPath;
|
|
662
|
+
staticAssetsOptions;
|
|
663
|
+
constructor(instance) {
|
|
664
|
+
super(instance || {});
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Create a new DenoAdapter instance
|
|
668
|
+
*/
|
|
669
|
+
static create() {
|
|
670
|
+
return new _DenoAdapter();
|
|
671
|
+
}
|
|
672
|
+
async listen(port, hostnameOrCallback, callback) {
|
|
673
|
+
const portNum = typeof port === "string" ? parseInt(port, 10) : port;
|
|
674
|
+
const hostname = typeof hostnameOrCallback === "string" ? hostnameOrCallback : "0.0.0.0";
|
|
675
|
+
const cb = typeof hostnameOrCallback === "function" ? hostnameOrCallback : callback;
|
|
676
|
+
this.abortController = new AbortController();
|
|
677
|
+
const serveOptions = {
|
|
678
|
+
port: portNum,
|
|
679
|
+
hostname,
|
|
680
|
+
signal: this.abortController.signal,
|
|
681
|
+
onListen: () => {
|
|
682
|
+
cb?.();
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
this.server = Deno.serve(
|
|
686
|
+
serveOptions,
|
|
687
|
+
this.handleRequest.bind(this)
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Handle incoming HTTP requests
|
|
692
|
+
*/
|
|
693
|
+
async handleRequest(request) {
|
|
694
|
+
const url = new URL(request.url);
|
|
695
|
+
const path = url.pathname;
|
|
696
|
+
const method = request.method.toUpperCase();
|
|
697
|
+
const req = await this.createRequest(request, url);
|
|
698
|
+
const res = this.createResponse();
|
|
699
|
+
try {
|
|
700
|
+
if (this.corsOptions && method === "OPTIONS") {
|
|
701
|
+
this.handleCors(req, res);
|
|
702
|
+
return this.buildResponse(res);
|
|
703
|
+
}
|
|
704
|
+
if (this.corsOptions) {
|
|
705
|
+
this.applyCorsHeaders(req, res);
|
|
706
|
+
}
|
|
707
|
+
if (this.staticAssetsPath && path.startsWith(this.staticAssetsOptions?.prefix || "/")) {
|
|
708
|
+
const staticResponse = await this.serveStaticAsset(path);
|
|
709
|
+
if (staticResponse) {
|
|
710
|
+
return staticResponse;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
await this.runMiddlewares(req, res, path);
|
|
714
|
+
if (res.headersSent) {
|
|
715
|
+
return this.buildResponse(res);
|
|
716
|
+
}
|
|
717
|
+
const route = this.findRoute(path, method);
|
|
718
|
+
if (route) {
|
|
719
|
+
req.params = this.extractParams(route, path);
|
|
720
|
+
await route.handler(req, res);
|
|
721
|
+
} else if (this.notFoundHandler) {
|
|
722
|
+
this.notFoundHandler(req, res);
|
|
723
|
+
} else {
|
|
724
|
+
res.status(import_common.HttpStatus.NOT_FOUND).json({
|
|
725
|
+
statusCode: import_common.HttpStatus.NOT_FOUND,
|
|
726
|
+
message: "Cannot " + method + " " + path,
|
|
727
|
+
error: "Not Found"
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
return this.buildResponse(res);
|
|
731
|
+
} catch (error) {
|
|
732
|
+
if (this.errorHandler) {
|
|
733
|
+
this.errorHandler(error, req, res);
|
|
734
|
+
return this.buildResponse(res);
|
|
735
|
+
}
|
|
736
|
+
res.status(import_common.HttpStatus.INTERNAL_SERVER_ERROR).json({
|
|
737
|
+
statusCode: import_common.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
738
|
+
message: error.message || "Internal Server Error",
|
|
739
|
+
error: "Internal Server Error"
|
|
740
|
+
});
|
|
741
|
+
return this.buildResponse(res);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Create a DenoRequest object from a native Request
|
|
746
|
+
*/
|
|
747
|
+
async createRequest(request, url) {
|
|
748
|
+
let body = void 0;
|
|
749
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(request.method.toUpperCase())) {
|
|
750
|
+
const contentType = request.headers.get("content-type") || "";
|
|
751
|
+
if (contentType.includes("application/json")) {
|
|
752
|
+
try {
|
|
753
|
+
body = await request.json();
|
|
754
|
+
} catch {
|
|
755
|
+
body = void 0;
|
|
756
|
+
}
|
|
757
|
+
} else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
758
|
+
try {
|
|
759
|
+
const formData = await request.formData();
|
|
760
|
+
const entries = {};
|
|
761
|
+
formData.forEach((value, key) => {
|
|
762
|
+
entries[key] = value;
|
|
763
|
+
});
|
|
764
|
+
body = entries;
|
|
765
|
+
} catch {
|
|
766
|
+
body = void 0;
|
|
767
|
+
}
|
|
768
|
+
} else if (contentType.includes("text/")) {
|
|
769
|
+
try {
|
|
770
|
+
body = await request.text();
|
|
771
|
+
} catch {
|
|
772
|
+
body = void 0;
|
|
773
|
+
}
|
|
774
|
+
} else if (contentType.includes("multipart/form-data")) {
|
|
775
|
+
try {
|
|
776
|
+
body = await request.formData();
|
|
777
|
+
} catch {
|
|
778
|
+
body = void 0;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const query = {};
|
|
783
|
+
url.searchParams.forEach((value, key) => {
|
|
784
|
+
query[key] = value;
|
|
785
|
+
});
|
|
786
|
+
return {
|
|
787
|
+
raw: request,
|
|
788
|
+
url: request.url,
|
|
789
|
+
method: request.method,
|
|
790
|
+
headers: request.headers,
|
|
791
|
+
params: {},
|
|
792
|
+
query,
|
|
793
|
+
body,
|
|
794
|
+
ip: void 0,
|
|
795
|
+
// Deno doesn't expose client IP in the same way
|
|
796
|
+
hostname: url.hostname,
|
|
797
|
+
protocol: url.protocol.replace(":", ""),
|
|
798
|
+
secure: url.protocol === "https:",
|
|
799
|
+
originalUrl: url.pathname + url.search,
|
|
800
|
+
baseUrl: "",
|
|
801
|
+
path: url.pathname
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Create a DenoResponse object
|
|
806
|
+
*/
|
|
807
|
+
createResponse() {
|
|
808
|
+
const headers = new Headers();
|
|
809
|
+
let statusCode = 200;
|
|
810
|
+
let body = null;
|
|
811
|
+
let headersSent = false;
|
|
812
|
+
const res = {
|
|
813
|
+
get statusCode() {
|
|
814
|
+
return statusCode;
|
|
815
|
+
},
|
|
816
|
+
set statusCode(code) {
|
|
817
|
+
statusCode = code;
|
|
818
|
+
},
|
|
819
|
+
headers,
|
|
820
|
+
get body() {
|
|
821
|
+
return body;
|
|
822
|
+
},
|
|
823
|
+
set body(b) {
|
|
824
|
+
body = b ?? null;
|
|
825
|
+
},
|
|
826
|
+
get headersSent() {
|
|
827
|
+
return headersSent;
|
|
828
|
+
},
|
|
829
|
+
set headersSent(sent) {
|
|
830
|
+
headersSent = sent;
|
|
831
|
+
},
|
|
832
|
+
status(code) {
|
|
833
|
+
statusCode = code;
|
|
834
|
+
return this;
|
|
835
|
+
},
|
|
836
|
+
setHeader(name, value) {
|
|
837
|
+
headers.set(name, value);
|
|
838
|
+
return this;
|
|
839
|
+
},
|
|
840
|
+
getHeader(name) {
|
|
841
|
+
return headers.get(name);
|
|
842
|
+
},
|
|
843
|
+
removeHeader(name) {
|
|
844
|
+
headers.delete(name);
|
|
845
|
+
return this;
|
|
846
|
+
},
|
|
847
|
+
send(responseBody) {
|
|
848
|
+
headersSent = true;
|
|
849
|
+
if (responseBody === void 0 || responseBody === null) {
|
|
850
|
+
body = null;
|
|
851
|
+
} else if (typeof responseBody === "object" && !(responseBody instanceof Blob) && !(responseBody instanceof ReadableStream) && !(responseBody instanceof FormData) && !(responseBody instanceof URLSearchParams) && !(responseBody instanceof ArrayBuffer)) {
|
|
852
|
+
headers.set("Content-Type", "application/json");
|
|
853
|
+
body = JSON.stringify(responseBody);
|
|
854
|
+
} else {
|
|
855
|
+
body = responseBody;
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
json(responseBody) {
|
|
859
|
+
headersSent = true;
|
|
860
|
+
headers.set("Content-Type", "application/json");
|
|
861
|
+
body = JSON.stringify(responseBody);
|
|
862
|
+
},
|
|
863
|
+
redirect(url, code = 302) {
|
|
864
|
+
headersSent = true;
|
|
865
|
+
statusCode = code;
|
|
866
|
+
headers.set("Location", url);
|
|
867
|
+
body = null;
|
|
868
|
+
},
|
|
869
|
+
end(responseBody) {
|
|
870
|
+
headersSent = true;
|
|
871
|
+
body = responseBody ?? null;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
return res;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Build a Response object from DenoResponse
|
|
878
|
+
*/
|
|
879
|
+
buildResponse(res) {
|
|
880
|
+
return new Response(res.body, {
|
|
881
|
+
status: res.statusCode,
|
|
882
|
+
headers: res.headers
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Run all matching middlewares
|
|
887
|
+
*/
|
|
888
|
+
async runMiddlewares(req, res, path) {
|
|
889
|
+
const matchingMiddlewares = this.middlewares.filter(
|
|
890
|
+
(m) => path.startsWith(m.path) || m.path === "*" || m.path === "/"
|
|
891
|
+
);
|
|
892
|
+
let index = 0;
|
|
893
|
+
const next = async () => {
|
|
894
|
+
if (index < matchingMiddlewares.length && !res.headersSent) {
|
|
895
|
+
const middleware = matchingMiddlewares[index++];
|
|
896
|
+
await middleware.handler(req, res, next);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
await next();
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Find a matching route
|
|
903
|
+
*/
|
|
904
|
+
findRoute(path, method) {
|
|
905
|
+
return this.routes.find((route) => {
|
|
906
|
+
const methodMatch = route.method === method || route.method === "ALL";
|
|
907
|
+
if (typeof route.path === "string") {
|
|
908
|
+
const pattern = this.pathToRegex(route.path);
|
|
909
|
+
return methodMatch && pattern.test(path);
|
|
910
|
+
}
|
|
911
|
+
return methodMatch && route.path.test(path);
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Extract route parameters from path
|
|
916
|
+
*/
|
|
917
|
+
extractParams(route, path) {
|
|
918
|
+
const params = {};
|
|
919
|
+
if (typeof route.path === "string") {
|
|
920
|
+
const pattern = this.pathToRegex(route.path);
|
|
921
|
+
const match = path.match(pattern);
|
|
922
|
+
if (match) {
|
|
923
|
+
route.keys.forEach((key, index) => {
|
|
924
|
+
params[key] = match[index + 1] || "";
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return params;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Convert a path pattern to a RegExp
|
|
932
|
+
*/
|
|
933
|
+
pathToRegex(path) {
|
|
934
|
+
const escaped = path.replace(/([.+?^${}()|[\]\\])/g, "\\$1").replace(/:(\w+)/g, "([^/]+)").replace(/\*/g, ".*");
|
|
935
|
+
return new RegExp(`^${escaped}$`);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Extract parameter keys from path pattern
|
|
939
|
+
*/
|
|
940
|
+
extractKeys(path) {
|
|
941
|
+
const keys = [];
|
|
942
|
+
const regex = /:(\w+)/g;
|
|
943
|
+
let match;
|
|
944
|
+
while ((match = regex.exec(path)) !== null) {
|
|
945
|
+
keys.push(match[1]);
|
|
946
|
+
}
|
|
947
|
+
return keys;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Register a route handler
|
|
951
|
+
*/
|
|
952
|
+
registerRoute(method, path, handler) {
|
|
953
|
+
this.routes.push({
|
|
954
|
+
path,
|
|
955
|
+
method,
|
|
956
|
+
handler,
|
|
957
|
+
keys: this.extractKeys(path)
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
get(pathOrHandler, handler) {
|
|
961
|
+
if (typeof pathOrHandler === "function") {
|
|
962
|
+
this.registerRoute("GET", "/", pathOrHandler);
|
|
963
|
+
} else if (handler) {
|
|
964
|
+
this.registerRoute("GET", pathOrHandler, handler);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
post(pathOrHandler, handler) {
|
|
968
|
+
if (typeof pathOrHandler === "function") {
|
|
969
|
+
this.registerRoute("POST", "/", pathOrHandler);
|
|
970
|
+
} else if (handler) {
|
|
971
|
+
this.registerRoute("POST", pathOrHandler, handler);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
put(pathOrHandler, handler) {
|
|
975
|
+
if (typeof pathOrHandler === "function") {
|
|
976
|
+
this.registerRoute("PUT", "/", pathOrHandler);
|
|
977
|
+
} else if (handler) {
|
|
978
|
+
this.registerRoute("PUT", pathOrHandler, handler);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
delete(pathOrHandler, handler) {
|
|
982
|
+
if (typeof pathOrHandler === "function") {
|
|
983
|
+
this.registerRoute("DELETE", "/", pathOrHandler);
|
|
984
|
+
} else if (handler) {
|
|
985
|
+
this.registerRoute("DELETE", pathOrHandler, handler);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
patch(pathOrHandler, handler) {
|
|
989
|
+
if (typeof pathOrHandler === "function") {
|
|
990
|
+
this.registerRoute("PATCH", "/", pathOrHandler);
|
|
991
|
+
} else if (handler) {
|
|
992
|
+
this.registerRoute("PATCH", pathOrHandler, handler);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
options(pathOrHandler, handler) {
|
|
996
|
+
if (typeof pathOrHandler === "function") {
|
|
997
|
+
this.registerRoute("OPTIONS", "/", pathOrHandler);
|
|
998
|
+
} else if (handler) {
|
|
999
|
+
this.registerRoute("OPTIONS", pathOrHandler, handler);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
head(pathOrHandler, handler) {
|
|
1003
|
+
if (typeof pathOrHandler === "function") {
|
|
1004
|
+
this.registerRoute("HEAD", "/", pathOrHandler);
|
|
1005
|
+
} else if (handler) {
|
|
1006
|
+
this.registerRoute("HEAD", pathOrHandler, handler);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
all(pathOrHandler, handler) {
|
|
1010
|
+
if (typeof pathOrHandler === "function") {
|
|
1011
|
+
this.registerRoute("ALL", "/", pathOrHandler);
|
|
1012
|
+
} else if (handler) {
|
|
1013
|
+
this.registerRoute("ALL", pathOrHandler, handler);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
use(pathOrHandler, handler) {
|
|
1017
|
+
if (typeof pathOrHandler === "function") {
|
|
1018
|
+
this.middlewares.push({
|
|
1019
|
+
path: "*",
|
|
1020
|
+
handler: pathOrHandler
|
|
1021
|
+
});
|
|
1022
|
+
} else if (handler) {
|
|
1023
|
+
this.middlewares.push({
|
|
1024
|
+
path: pathOrHandler,
|
|
1025
|
+
handler
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
useExpressMiddleware(pathOrMiddleware, middleware) {
|
|
1030
|
+
if (typeof pathOrMiddleware === "function") {
|
|
1031
|
+
const wrappedMiddleware = wrapExpressMiddleware(pathOrMiddleware);
|
|
1032
|
+
this.middlewares.push({
|
|
1033
|
+
path: "*",
|
|
1034
|
+
handler: wrappedMiddleware
|
|
1035
|
+
});
|
|
1036
|
+
} else if (middleware) {
|
|
1037
|
+
const wrappedMiddleware = wrapExpressMiddleware(middleware);
|
|
1038
|
+
this.middlewares.push({
|
|
1039
|
+
path: pathOrMiddleware,
|
|
1040
|
+
handler: wrappedMiddleware
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Create an Express-like app instance for middleware that requires app.use()
|
|
1046
|
+
*
|
|
1047
|
+
* Some Express middleware (like express-session) require an Express app instance.
|
|
1048
|
+
* This creates a compatible shim that routes middleware through the Deno adapter.
|
|
1049
|
+
*
|
|
1050
|
+
* @example
|
|
1051
|
+
* ```typescript
|
|
1052
|
+
* import session from 'express-session';
|
|
1053
|
+
*
|
|
1054
|
+
* const adapter = new DenoAdapter();
|
|
1055
|
+
* const expressApp = adapter.getExpressApp();
|
|
1056
|
+
*
|
|
1057
|
+
* expressApp.use(session({ secret: 'keyboard cat' }));
|
|
1058
|
+
* ```
|
|
1059
|
+
*/
|
|
1060
|
+
getExpressApp() {
|
|
1061
|
+
const self = this;
|
|
1062
|
+
const settings = {};
|
|
1063
|
+
const app = {
|
|
1064
|
+
locals: {},
|
|
1065
|
+
settings,
|
|
1066
|
+
use(...args) {
|
|
1067
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
1068
|
+
self.useExpressMiddleware(args[0]);
|
|
1069
|
+
} else if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "function") {
|
|
1070
|
+
self.useExpressMiddleware(args[0], args[1]);
|
|
1071
|
+
} else if (args.length >= 2) {
|
|
1072
|
+
const path = typeof args[0] === "string" ? args[0] : "*";
|
|
1073
|
+
const handlers = typeof args[0] === "string" ? args.slice(1) : args;
|
|
1074
|
+
handlers.forEach((handler) => {
|
|
1075
|
+
if (typeof handler === "function") {
|
|
1076
|
+
self.useExpressMiddleware(path, handler);
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
get(path, ...handlers) {
|
|
1082
|
+
handlers.forEach((handler) => {
|
|
1083
|
+
self.get(path, async (req, res) => {
|
|
1084
|
+
const expressReq = createExpressRequest(req);
|
|
1085
|
+
const expressRes = createExpressResponse(res);
|
|
1086
|
+
await handler(expressReq, expressRes, () => {
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
});
|
|
1090
|
+
},
|
|
1091
|
+
post(path, ...handlers) {
|
|
1092
|
+
handlers.forEach((handler) => {
|
|
1093
|
+
self.post(path, async (req, res) => {
|
|
1094
|
+
const expressReq = createExpressRequest(req);
|
|
1095
|
+
const expressRes = createExpressResponse(res);
|
|
1096
|
+
await handler(expressReq, expressRes, () => {
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
},
|
|
1101
|
+
put(path, ...handlers) {
|
|
1102
|
+
handlers.forEach((handler) => {
|
|
1103
|
+
self.put(path, async (req, res) => {
|
|
1104
|
+
const expressReq = createExpressRequest(req);
|
|
1105
|
+
const expressRes = createExpressResponse(res);
|
|
1106
|
+
await handler(expressReq, expressRes, () => {
|
|
1107
|
+
});
|
|
1108
|
+
});
|
|
1109
|
+
});
|
|
1110
|
+
},
|
|
1111
|
+
delete(path, ...handlers) {
|
|
1112
|
+
handlers.forEach((handler) => {
|
|
1113
|
+
self.delete(path, async (req, res) => {
|
|
1114
|
+
const expressReq = createExpressRequest(req);
|
|
1115
|
+
const expressRes = createExpressResponse(res);
|
|
1116
|
+
await handler(expressReq, expressRes, () => {
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
});
|
|
1120
|
+
},
|
|
1121
|
+
patch(path, ...handlers) {
|
|
1122
|
+
handlers.forEach((handler) => {
|
|
1123
|
+
self.patch(path, async (req, res) => {
|
|
1124
|
+
const expressReq = createExpressRequest(req);
|
|
1125
|
+
const expressRes = createExpressResponse(res);
|
|
1126
|
+
await handler(expressReq, expressRes, () => {
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
},
|
|
1131
|
+
options(path, ...handlers) {
|
|
1132
|
+
handlers.forEach((handler) => {
|
|
1133
|
+
self.options(path, async (req, res) => {
|
|
1134
|
+
const expressReq = createExpressRequest(req);
|
|
1135
|
+
const expressRes = createExpressResponse(res);
|
|
1136
|
+
await handler(expressReq, expressRes, () => {
|
|
1137
|
+
});
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
},
|
|
1141
|
+
head(path, ...handlers) {
|
|
1142
|
+
handlers.forEach((handler) => {
|
|
1143
|
+
self.head(path, async (req, res) => {
|
|
1144
|
+
const expressReq = createExpressRequest(req);
|
|
1145
|
+
const expressRes = createExpressResponse(res);
|
|
1146
|
+
await handler(expressReq, expressRes, () => {
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
});
|
|
1150
|
+
},
|
|
1151
|
+
all(path, ...handlers) {
|
|
1152
|
+
handlers.forEach((handler) => {
|
|
1153
|
+
self.all(path, async (req, res) => {
|
|
1154
|
+
const expressReq = createExpressRequest(req);
|
|
1155
|
+
const expressRes = createExpressResponse(res);
|
|
1156
|
+
await handler(expressReq, expressRes, () => {
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
},
|
|
1161
|
+
set(key, value) {
|
|
1162
|
+
settings[key] = value;
|
|
1163
|
+
},
|
|
1164
|
+
enable(key) {
|
|
1165
|
+
settings[key] = true;
|
|
1166
|
+
},
|
|
1167
|
+
disable(key) {
|
|
1168
|
+
settings[key] = false;
|
|
1169
|
+
},
|
|
1170
|
+
enabled(key) {
|
|
1171
|
+
return Boolean(settings[key]);
|
|
1172
|
+
},
|
|
1173
|
+
disabled(key) {
|
|
1174
|
+
return !settings[key];
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
return app;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Use Fastify middleware/hooks with the Deno adapter
|
|
1181
|
+
*
|
|
1182
|
+
* This method wraps Fastify hooks to be compatible with the Deno adapter,
|
|
1183
|
+
* allowing you to use Fastify-style middleware.
|
|
1184
|
+
*
|
|
1185
|
+
* @example
|
|
1186
|
+
* ```typescript
|
|
1187
|
+
* const adapter = new DenoAdapter();
|
|
1188
|
+
*
|
|
1189
|
+
* // Use a Fastify hook
|
|
1190
|
+
* adapter.useFastifyHook('onRequest', async (request, reply) => {
|
|
1191
|
+
* console.log('Request received:', request.url);
|
|
1192
|
+
* });
|
|
1193
|
+
*
|
|
1194
|
+
* // Use with callback style
|
|
1195
|
+
* adapter.useFastifyHook('preHandler', (request, reply, done) => {
|
|
1196
|
+
* // Do something
|
|
1197
|
+
* done();
|
|
1198
|
+
* });
|
|
1199
|
+
* ```
|
|
1200
|
+
*/
|
|
1201
|
+
useFastifyHook(_name, hook) {
|
|
1202
|
+
const wrappedHook = wrapFastifyHook(hook);
|
|
1203
|
+
this.middlewares.push({
|
|
1204
|
+
path: "*",
|
|
1205
|
+
handler: wrappedHook
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Register a Fastify plugin with the Deno adapter
|
|
1210
|
+
*
|
|
1211
|
+
* This allows using Fastify plugins that add hooks, decorators, or routes.
|
|
1212
|
+
*
|
|
1213
|
+
* @example
|
|
1214
|
+
* ```typescript
|
|
1215
|
+
* import fastifyCors from '@fastify/cors';
|
|
1216
|
+
* import fastifyHelmet from '@fastify/helmet';
|
|
1217
|
+
*
|
|
1218
|
+
* const adapter = new DenoAdapter();
|
|
1219
|
+
* const fastify = adapter.getFastifyInstance();
|
|
1220
|
+
*
|
|
1221
|
+
* // Register plugins
|
|
1222
|
+
* await adapter.registerFastifyPlugin(fastifyCors, { origin: '*' });
|
|
1223
|
+
* await adapter.registerFastifyPlugin(fastifyHelmet);
|
|
1224
|
+
* ```
|
|
1225
|
+
*/
|
|
1226
|
+
async registerFastifyPlugin(plugin, opts) {
|
|
1227
|
+
const instance = this.getFastifyInstance();
|
|
1228
|
+
await wrapFastifyPlugin(plugin, instance, opts);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Get a Fastify-like instance for plugins that require it
|
|
1232
|
+
*
|
|
1233
|
+
* This creates a Fastify-compatible interface that routes hooks and routes
|
|
1234
|
+
* through the Deno adapter.
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* const adapter = new DenoAdapter();
|
|
1239
|
+
* const fastify = adapter.getFastifyInstance();
|
|
1240
|
+
*
|
|
1241
|
+
* // Add hooks
|
|
1242
|
+
* fastify.addHook('onRequest', async (request, reply) => {
|
|
1243
|
+
* console.log('Request:', request.method, request.url);
|
|
1244
|
+
* });
|
|
1245
|
+
*
|
|
1246
|
+
* // Add decorators
|
|
1247
|
+
* fastify.decorateRequest('user', null);
|
|
1248
|
+
* ```
|
|
1249
|
+
*/
|
|
1250
|
+
getFastifyInstance() {
|
|
1251
|
+
const self = this;
|
|
1252
|
+
const decorators = {};
|
|
1253
|
+
const requestDecorators = {};
|
|
1254
|
+
const replyDecorators = {};
|
|
1255
|
+
const instance = {
|
|
1256
|
+
log: createFastifyLogger(),
|
|
1257
|
+
prefix: "",
|
|
1258
|
+
// Decorators
|
|
1259
|
+
decorate(name, value) {
|
|
1260
|
+
decorators[name] = value;
|
|
1261
|
+
return this;
|
|
1262
|
+
},
|
|
1263
|
+
decorateRequest(name, value) {
|
|
1264
|
+
requestDecorators[name] = value;
|
|
1265
|
+
return this;
|
|
1266
|
+
},
|
|
1267
|
+
decorateReply(name, value) {
|
|
1268
|
+
replyDecorators[name] = value;
|
|
1269
|
+
return this;
|
|
1270
|
+
},
|
|
1271
|
+
hasDecorator(name) {
|
|
1272
|
+
return name in decorators;
|
|
1273
|
+
},
|
|
1274
|
+
hasRequestDecorator(name) {
|
|
1275
|
+
return name in requestDecorators;
|
|
1276
|
+
},
|
|
1277
|
+
hasReplyDecorator(name) {
|
|
1278
|
+
return name in replyDecorators;
|
|
1279
|
+
},
|
|
1280
|
+
// Hooks
|
|
1281
|
+
addHook(name, hook) {
|
|
1282
|
+
if (["onRequest", "preParsing", "preValidation", "preHandler", "onResponse"].includes(name)) {
|
|
1283
|
+
self.useFastifyHook(name, hook);
|
|
1284
|
+
}
|
|
1285
|
+
return this;
|
|
1286
|
+
},
|
|
1287
|
+
// Plugin registration
|
|
1288
|
+
register(plugin, opts) {
|
|
1289
|
+
wrapFastifyPlugin(plugin, this, opts).catch(console.error);
|
|
1290
|
+
return this;
|
|
1291
|
+
},
|
|
1292
|
+
// Routes
|
|
1293
|
+
route(opts) {
|
|
1294
|
+
const methods = Array.isArray(opts.method) ? opts.method : [opts.method];
|
|
1295
|
+
methods.forEach((method) => {
|
|
1296
|
+
const handler = async (req, res) => {
|
|
1297
|
+
const fastifyReq = createFastifyRequest(req);
|
|
1298
|
+
const fastifyReply = createFastifyReply(res);
|
|
1299
|
+
Object.entries(requestDecorators).forEach(([key, value]) => {
|
|
1300
|
+
fastifyReq[key] = typeof value === "function" ? value() : value;
|
|
1301
|
+
});
|
|
1302
|
+
Object.entries(replyDecorators).forEach(([key, value]) => {
|
|
1303
|
+
fastifyReply[key] = typeof value === "function" ? value() : value;
|
|
1304
|
+
});
|
|
1305
|
+
const hooks = [
|
|
1306
|
+
...opts.onRequest ? Array.isArray(opts.onRequest) ? opts.onRequest : [opts.onRequest] : [],
|
|
1307
|
+
...opts.preValidation ? Array.isArray(opts.preValidation) ? opts.preValidation : [opts.preValidation] : [],
|
|
1308
|
+
...opts.preHandler ? Array.isArray(opts.preHandler) ? opts.preHandler : [opts.preHandler] : []
|
|
1309
|
+
];
|
|
1310
|
+
for (const hook of hooks) {
|
|
1311
|
+
if (fastifyReply.sent) break;
|
|
1312
|
+
await new Promise((resolve, reject) => {
|
|
1313
|
+
if (hook.length <= 2) {
|
|
1314
|
+
hook(fastifyReq, fastifyReply).then(resolve).catch(reject);
|
|
1315
|
+
} else {
|
|
1316
|
+
hook(
|
|
1317
|
+
fastifyReq,
|
|
1318
|
+
fastifyReply,
|
|
1319
|
+
(err) => err ? reject(err) : resolve()
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
if (!fastifyReply.sent) {
|
|
1325
|
+
const result = await opts.handler(fastifyReq, fastifyReply);
|
|
1326
|
+
if (result !== void 0 && !fastifyReply.sent) {
|
|
1327
|
+
fastifyReply.send(result);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
switch (method.toUpperCase()) {
|
|
1332
|
+
case "GET":
|
|
1333
|
+
self.get(opts.url, handler);
|
|
1334
|
+
break;
|
|
1335
|
+
case "POST":
|
|
1336
|
+
self.post(opts.url, handler);
|
|
1337
|
+
break;
|
|
1338
|
+
case "PUT":
|
|
1339
|
+
self.put(opts.url, handler);
|
|
1340
|
+
break;
|
|
1341
|
+
case "DELETE":
|
|
1342
|
+
self.delete(opts.url, handler);
|
|
1343
|
+
break;
|
|
1344
|
+
case "PATCH":
|
|
1345
|
+
self.patch(opts.url, handler);
|
|
1346
|
+
break;
|
|
1347
|
+
case "OPTIONS":
|
|
1348
|
+
self.options(opts.url, handler);
|
|
1349
|
+
break;
|
|
1350
|
+
case "HEAD":
|
|
1351
|
+
self.head(opts.url, handler);
|
|
1352
|
+
break;
|
|
1353
|
+
default:
|
|
1354
|
+
self.all(opts.url, handler);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
return this;
|
|
1358
|
+
},
|
|
1359
|
+
// HTTP method shortcuts
|
|
1360
|
+
get(path, optsOrHandler, handler) {
|
|
1361
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1362
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1363
|
+
return this.route({ ...opts, method: "GET", url: path, handler: h });
|
|
1364
|
+
},
|
|
1365
|
+
post(path, optsOrHandler, handler) {
|
|
1366
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1367
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1368
|
+
return this.route({ ...opts, method: "POST", url: path, handler: h });
|
|
1369
|
+
},
|
|
1370
|
+
put(path, optsOrHandler, handler) {
|
|
1371
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1372
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1373
|
+
return this.route({ ...opts, method: "PUT", url: path, handler: h });
|
|
1374
|
+
},
|
|
1375
|
+
delete(path, optsOrHandler, handler) {
|
|
1376
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1377
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1378
|
+
return this.route({ ...opts, method: "DELETE", url: path, handler: h });
|
|
1379
|
+
},
|
|
1380
|
+
patch(path, optsOrHandler, handler) {
|
|
1381
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1382
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1383
|
+
return this.route({ ...opts, method: "PATCH", url: path, handler: h });
|
|
1384
|
+
},
|
|
1385
|
+
options(path, optsOrHandler, handler) {
|
|
1386
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1387
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1388
|
+
return this.route({ ...opts, method: "OPTIONS", url: path, handler: h });
|
|
1389
|
+
},
|
|
1390
|
+
head(path, optsOrHandler, handler) {
|
|
1391
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1392
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1393
|
+
return this.route({ ...opts, method: "HEAD", url: path, handler: h });
|
|
1394
|
+
},
|
|
1395
|
+
all(path, optsOrHandler, handler) {
|
|
1396
|
+
const h = typeof optsOrHandler === "function" ? optsOrHandler : handler;
|
|
1397
|
+
const opts = typeof optsOrHandler === "object" ? optsOrHandler : {};
|
|
1398
|
+
return this.route({ ...opts, method: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"], url: path, handler: h });
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
return instance;
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Get the underlying HTTP server
|
|
1405
|
+
*/
|
|
1406
|
+
getHttpServer() {
|
|
1407
|
+
return this.server;
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Set the HTTP server instance
|
|
1411
|
+
*/
|
|
1412
|
+
setHttpServer(server) {
|
|
1413
|
+
this.server = server;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Close the server
|
|
1417
|
+
*/
|
|
1418
|
+
async close() {
|
|
1419
|
+
if (this.abortController) {
|
|
1420
|
+
this.abortController.abort();
|
|
1421
|
+
await this.server?.finished;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Set error handler
|
|
1426
|
+
*/
|
|
1427
|
+
setErrorHandler(handler) {
|
|
1428
|
+
this.errorHandler = handler;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Set 404 handler
|
|
1432
|
+
*/
|
|
1433
|
+
setNotFoundHandler(handler) {
|
|
1434
|
+
this.notFoundHandler = handler;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Enable CORS
|
|
1438
|
+
*/
|
|
1439
|
+
enableCors(options) {
|
|
1440
|
+
this.corsOptions = options || {
|
|
1441
|
+
origin: "*",
|
|
1442
|
+
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
|
|
1443
|
+
credentials: false
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Handle CORS preflight requests
|
|
1448
|
+
*/
|
|
1449
|
+
handleCors(req, res) {
|
|
1450
|
+
this.applyCorsHeaders(req, res);
|
|
1451
|
+
res.status(this.corsOptions?.optionsSuccessStatus || 204).end();
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Apply CORS headers to response
|
|
1455
|
+
*/
|
|
1456
|
+
applyCorsHeaders(req, res) {
|
|
1457
|
+
if (!this.corsOptions) return;
|
|
1458
|
+
const origin = req.headers.get("origin") || "*";
|
|
1459
|
+
let allowOrigin = "*";
|
|
1460
|
+
if (typeof this.corsOptions.origin === "string") {
|
|
1461
|
+
allowOrigin = this.corsOptions.origin;
|
|
1462
|
+
} else if (typeof this.corsOptions.origin === "boolean") {
|
|
1463
|
+
allowOrigin = this.corsOptions.origin ? origin : "";
|
|
1464
|
+
} else if (Array.isArray(this.corsOptions.origin)) {
|
|
1465
|
+
allowOrigin = this.corsOptions.origin.includes(origin) ? origin : "";
|
|
1466
|
+
} else if (typeof this.corsOptions.origin === "function") {
|
|
1467
|
+
const result = this.corsOptions.origin(origin);
|
|
1468
|
+
allowOrigin = typeof result === "string" ? result : result ? origin : "";
|
|
1469
|
+
}
|
|
1470
|
+
res.setHeader("Access-Control-Allow-Origin", allowOrigin);
|
|
1471
|
+
if (this.corsOptions.credentials) {
|
|
1472
|
+
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
1473
|
+
}
|
|
1474
|
+
const methods = Array.isArray(this.corsOptions.methods) ? this.corsOptions.methods.join(",") : this.corsOptions.methods || "GET,HEAD,PUT,PATCH,POST,DELETE";
|
|
1475
|
+
res.setHeader("Access-Control-Allow-Methods", methods);
|
|
1476
|
+
if (this.corsOptions.allowedHeaders) {
|
|
1477
|
+
const headers = Array.isArray(this.corsOptions.allowedHeaders) ? this.corsOptions.allowedHeaders.join(",") : this.corsOptions.allowedHeaders;
|
|
1478
|
+
res.setHeader("Access-Control-Allow-Headers", headers);
|
|
1479
|
+
} else {
|
|
1480
|
+
const requestHeaders = req.headers.get("access-control-request-headers");
|
|
1481
|
+
if (requestHeaders) {
|
|
1482
|
+
res.setHeader("Access-Control-Allow-Headers", requestHeaders);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
if (this.corsOptions.exposedHeaders) {
|
|
1486
|
+
const exposed = Array.isArray(this.corsOptions.exposedHeaders) ? this.corsOptions.exposedHeaders.join(",") : this.corsOptions.exposedHeaders;
|
|
1487
|
+
res.setHeader("Access-Control-Expose-Headers", exposed);
|
|
1488
|
+
}
|
|
1489
|
+
if (this.corsOptions.maxAge) {
|
|
1490
|
+
res.setHeader("Access-Control-Max-Age", String(this.corsOptions.maxAge));
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Use static assets
|
|
1495
|
+
*/
|
|
1496
|
+
useStaticAssets(path, options) {
|
|
1497
|
+
this.staticAssetsPath = path;
|
|
1498
|
+
this.staticAssetsOptions = options;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Serve static asset
|
|
1502
|
+
*/
|
|
1503
|
+
async serveStaticAsset(urlPath) {
|
|
1504
|
+
if (!this.staticAssetsPath) return null;
|
|
1505
|
+
const prefix = this.staticAssetsOptions?.prefix || "/";
|
|
1506
|
+
const relativePath = urlPath.replace(prefix, "").replace(/^\//, "");
|
|
1507
|
+
const filePath = `${this.staticAssetsPath}/${relativePath}`;
|
|
1508
|
+
try {
|
|
1509
|
+
const file = await Deno.open(filePath, { read: true });
|
|
1510
|
+
const stat = await file.stat();
|
|
1511
|
+
if (stat.isDirectory) {
|
|
1512
|
+
file.close();
|
|
1513
|
+
if (this.staticAssetsOptions?.index !== false) {
|
|
1514
|
+
const indexFile = typeof this.staticAssetsOptions?.index === "string" ? this.staticAssetsOptions.index : "index.html";
|
|
1515
|
+
return this.serveStaticAsset(`${urlPath}/${indexFile}`);
|
|
1516
|
+
}
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
const headers = new Headers();
|
|
1520
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
1521
|
+
const mimeTypes = {
|
|
1522
|
+
html: "text/html",
|
|
1523
|
+
css: "text/css",
|
|
1524
|
+
js: "application/javascript",
|
|
1525
|
+
json: "application/json",
|
|
1526
|
+
png: "image/png",
|
|
1527
|
+
jpg: "image/jpeg",
|
|
1528
|
+
jpeg: "image/jpeg",
|
|
1529
|
+
gif: "image/gif",
|
|
1530
|
+
svg: "image/svg+xml",
|
|
1531
|
+
ico: "image/x-icon",
|
|
1532
|
+
woff: "font/woff",
|
|
1533
|
+
woff2: "font/woff2",
|
|
1534
|
+
ttf: "font/ttf",
|
|
1535
|
+
eot: "application/vnd.ms-fontobject",
|
|
1536
|
+
txt: "text/plain",
|
|
1537
|
+
xml: "application/xml",
|
|
1538
|
+
pdf: "application/pdf",
|
|
1539
|
+
mp4: "video/mp4",
|
|
1540
|
+
webm: "video/webm",
|
|
1541
|
+
mp3: "audio/mpeg",
|
|
1542
|
+
wav: "audio/wav"
|
|
1543
|
+
};
|
|
1544
|
+
headers.set("Content-Type", mimeTypes[ext || ""] || "application/octet-stream");
|
|
1545
|
+
if (this.staticAssetsOptions?.etag !== false) {
|
|
1546
|
+
headers.set("ETag", `"${stat.size}-${stat.mtime?.getTime() || 0}"`);
|
|
1547
|
+
}
|
|
1548
|
+
if (this.staticAssetsOptions?.lastModified !== false && stat.mtime) {
|
|
1549
|
+
headers.set("Last-Modified", stat.mtime.toUTCString());
|
|
1550
|
+
}
|
|
1551
|
+
if (this.staticAssetsOptions?.maxAge) {
|
|
1552
|
+
let cacheControl = `max-age=${this.staticAssetsOptions.maxAge}`;
|
|
1553
|
+
if (this.staticAssetsOptions.immutable) {
|
|
1554
|
+
cacheControl += ", immutable";
|
|
1555
|
+
}
|
|
1556
|
+
headers.set("Cache-Control", cacheControl);
|
|
1557
|
+
}
|
|
1558
|
+
return new Response(file.readable, {
|
|
1559
|
+
status: 200,
|
|
1560
|
+
headers
|
|
1561
|
+
});
|
|
1562
|
+
} catch (error) {
|
|
1563
|
+
if (error.name === "NotFound") {
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
throw error;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Set view engine (not implemented for base adapter)
|
|
1571
|
+
*/
|
|
1572
|
+
setViewEngine(_engine) {
|
|
1573
|
+
console.warn("View engine is not supported in the base Deno adapter");
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Render view (not implemented for base adapter)
|
|
1577
|
+
*/
|
|
1578
|
+
render(_response, _view, _options) {
|
|
1579
|
+
console.warn("Render is not supported in the base Deno adapter");
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Get request hostname
|
|
1583
|
+
*/
|
|
1584
|
+
getRequestHostname(request) {
|
|
1585
|
+
return request.hostname || request.headers.get("host") || "";
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Get request method
|
|
1589
|
+
*/
|
|
1590
|
+
getRequestMethod(request) {
|
|
1591
|
+
return request.method;
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Get request URL
|
|
1595
|
+
*/
|
|
1596
|
+
getRequestUrl(request) {
|
|
1597
|
+
return request.path || new URL(request.url).pathname;
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Send a reply
|
|
1601
|
+
*/
|
|
1602
|
+
reply(response, body, statusCode) {
|
|
1603
|
+
if (statusCode) {
|
|
1604
|
+
response.status(statusCode);
|
|
1605
|
+
}
|
|
1606
|
+
if (body === void 0 || body === null) {
|
|
1607
|
+
response.end();
|
|
1608
|
+
} else if (typeof body === "object") {
|
|
1609
|
+
response.json(body);
|
|
1610
|
+
} else {
|
|
1611
|
+
response.send(String(body));
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Set response status
|
|
1616
|
+
*/
|
|
1617
|
+
status(response, statusCode) {
|
|
1618
|
+
response.status(statusCode);
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Redirect response
|
|
1622
|
+
*/
|
|
1623
|
+
redirect(response, statusCode, url) {
|
|
1624
|
+
response.redirect(url, statusCode);
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Set response header
|
|
1628
|
+
*/
|
|
1629
|
+
setHeader(response, name, value) {
|
|
1630
|
+
response.setHeader(name, value);
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Get response header
|
|
1634
|
+
*/
|
|
1635
|
+
getHeader(response, name) {
|
|
1636
|
+
return response.getHeader(name);
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Append value to header
|
|
1640
|
+
*/
|
|
1641
|
+
appendHeader(response, name, value) {
|
|
1642
|
+
const existing = response.getHeader(name);
|
|
1643
|
+
if (existing) {
|
|
1644
|
+
response.setHeader(name, `${existing}, ${value}`);
|
|
1645
|
+
} else {
|
|
1646
|
+
response.setHeader(name, value);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* End response
|
|
1651
|
+
*/
|
|
1652
|
+
end(response, message) {
|
|
1653
|
+
response.end(message);
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Check if headers have been sent
|
|
1657
|
+
*/
|
|
1658
|
+
isHeadersSent(response) {
|
|
1659
|
+
return response.headersSent;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Register body parser middleware
|
|
1663
|
+
*/
|
|
1664
|
+
registerParserMiddleware() {
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Create middleware factory
|
|
1668
|
+
*/
|
|
1669
|
+
createMiddlewareFactory(_requestMethod) {
|
|
1670
|
+
return (path, callback) => {
|
|
1671
|
+
this.use(path, async (req, res, next) => {
|
|
1672
|
+
await callback(req, res, next);
|
|
1673
|
+
});
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Initialize the adapter
|
|
1678
|
+
*/
|
|
1679
|
+
initHttpServer() {
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Get the adapter type
|
|
1683
|
+
*/
|
|
1684
|
+
getType() {
|
|
1685
|
+
return "deno";
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Apply version filter
|
|
1689
|
+
*/
|
|
1690
|
+
applyVersionFilter(handler, _version, _versioningOptions) {
|
|
1691
|
+
return (_req, _res, _next) => {
|
|
1692
|
+
return handler;
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1697
|
+
0 && (module.exports = {
|
|
1698
|
+
DenoAdapter,
|
|
1699
|
+
createExpressRequest,
|
|
1700
|
+
createExpressResponse,
|
|
1701
|
+
createFastifyLogger,
|
|
1702
|
+
createFastifyReply,
|
|
1703
|
+
createFastifyRequest,
|
|
1704
|
+
wrapExpressMiddleware,
|
|
1705
|
+
wrapFastifyHook,
|
|
1706
|
+
wrapFastifyPlugin
|
|
1707
|
+
});
|
|
1708
|
+
//# sourceMappingURL=index.cjs.map
|