skyguard-js 1.2.2 → 1.2.4
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 +7 -15
- package/dist/app.js +13 -9
- package/dist/http/index.d.ts +2 -1
- package/dist/http/index.js +3 -1
- package/dist/http/logger.d.ts +9 -2
- package/dist/http/logger.js +49 -1
- package/dist/http/nodeNativeHttp.d.ts +1 -0
- package/dist/http/nodeNativeHttp.js +14 -3
- package/dist/http/response.js +2 -2
- package/dist/http/webHttp.d.ts +19 -0
- package/dist/http/webHttp.js +95 -0
- package/dist/parsers/contentParserManager.d.ts +4 -2
- package/dist/parsers/contentParserManager.js +22 -3
- package/dist/server/bunRuntimeServer.d.ts +7 -0
- package/dist/server/bunRuntimeServer.js +28 -0
- package/dist/server/createRuntimeServer.d.ts +4 -0
- package/dist/server/createRuntimeServer.js +23 -0
- package/dist/server/denoRuntimeServer.d.ts +7 -0
- package/dist/server/denoRuntimeServer.js +27 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +19 -0
- package/dist/server/modulePath.d.ts +6 -0
- package/dist/server/modulePath.js +18 -0
- package/dist/server/nodeRuntimeServer.d.ts +7 -0
- package/dist/server/nodeRuntimeServer.js +21 -0
- package/dist/server/runtimeDetector.d.ts +4 -0
- package/dist/server/runtimeDetector.js +15 -0
- package/dist/server/types.d.ts +11 -0
- package/dist/server/types.js +2 -0
- package/dist/sessions/cookies.js +6 -6
- package/dist/sessions/index.d.ts +2 -2
- package/dist/validators/index.d.ts +2 -2
- package/dist/validators/rules/index.d.ts +5 -5
- package/package.json +9 -10
package/README.md
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/skyguard-js)
|
|
8
8
|
[](https://github.com/Pipe930/Skyguard-js/actions/workflows/pipeline.yml)
|
|
9
|
-
[](https://badge.socket.dev/npm/package/skyguard-js/1.1.8)
|
|
10
9
|
|
|
11
10
|
**Skyguard.js** is a **lightweight, dependency-free web framework** built entirely with **TypeScript**.
|
|
12
11
|
|
|
@@ -41,6 +40,7 @@ Skyguard.js currently delivers a solid core that includes **routing**, **type-sa
|
|
|
41
40
|
- File uploads (via middleware)
|
|
42
41
|
- Static file serving
|
|
43
42
|
- Session handling (via middleware)
|
|
43
|
+
- Supported runtimes: [Bun](https://bun.com/) and [Deno](https://deno.com/)
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
@@ -84,20 +84,12 @@ app.run();
|
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
87
|
-
##
|
|
87
|
+
## 📚 Documentation
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
- Template engines supported (✅)
|
|
91
|
-
- Context abstraction (✅)
|
|
92
|
-
- Data validation (✅)
|
|
93
|
-
- Error handling improvements (✅)
|
|
94
|
-
- Sessions & cookies (✅)
|
|
95
|
-
- Passoword hashing & JWT tokens (✅)
|
|
96
|
-
- File uploads (✅)
|
|
97
|
-
- Database & ORM integration
|
|
98
|
-
- Authentication & authorization
|
|
99
|
-
- WebSockets
|
|
89
|
+
Full documentation is available on the [Official Website](https://pipe930.github.io/skyguard-documentation/). A section with examples or use cases will follow.
|
|
100
90
|
|
|
101
|
-
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📜 License
|
|
102
94
|
|
|
103
|
-
MIT
|
|
95
|
+
Licensed under the [MIT License](https://github.com/Pipe930/Skyguard-js/blob/main/LICENSE).
|
package/dist/app.js
CHANGED
|
@@ -8,10 +8,10 @@ const validationException_1 = require("./exceptions/validationException");
|
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const node_fs_1 = require("node:fs");
|
|
10
10
|
const fileStaticHandler_1 = require("./static/fileStaticHandler");
|
|
11
|
-
const node_http_1 = require("node:http");
|
|
12
11
|
const httpExceptions_1 = require("./exceptions/httpExceptions");
|
|
13
12
|
const engineTemplate_1 = require("./views/engineTemplate");
|
|
14
13
|
const container_1 = require("./container/container");
|
|
14
|
+
const createRuntimeServer_1 = require("./server/createRuntimeServer");
|
|
15
15
|
/**
|
|
16
16
|
* The `App` class acts as the **execution kernel** and **lifecycle orchestrator**
|
|
17
17
|
* of the framework.
|
|
@@ -148,13 +148,17 @@ class App {
|
|
|
148
148
|
*
|
|
149
149
|
* @param port - TCP port to listen on
|
|
150
150
|
*/
|
|
151
|
-
run(port = 3000, callback, hostname
|
|
152
|
-
(0,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
run(port = 3000, callback, hostname) {
|
|
152
|
+
const runtimeServer = (0, createRuntimeServer_1.createRuntimeServer)(this.loggerOptions);
|
|
153
|
+
runtimeServer.listen(async (adapter) => {
|
|
154
|
+
await this.handle(adapter);
|
|
155
|
+
}, {
|
|
156
|
+
port,
|
|
157
|
+
hostname,
|
|
158
|
+
callback: () => {
|
|
159
|
+
if (callback)
|
|
160
|
+
callback();
|
|
161
|
+
},
|
|
158
162
|
});
|
|
159
163
|
}
|
|
160
164
|
/**
|
|
@@ -171,7 +175,7 @@ class App {
|
|
|
171
175
|
* app.logger("common");
|
|
172
176
|
* app.logger("combined", "./logs/http.log");
|
|
173
177
|
*/
|
|
174
|
-
logger(format
|
|
178
|
+
logger(format, filePath) {
|
|
175
179
|
this.loggerOptions = {
|
|
176
180
|
...this.loggerOptions,
|
|
177
181
|
format,
|
package/dist/http/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { Response } from "./response";
|
|
|
2
2
|
export { Request } from "./request";
|
|
3
3
|
export { Context } from "./context";
|
|
4
4
|
export { NodeHttpAdapter } from "./nodeNativeHttp";
|
|
5
|
+
export { WebHttpAdapter } from "./webHttp";
|
|
5
6
|
export { HttpMethods } from "./httpMethods";
|
|
6
|
-
export { HttpAdapter } from "./httpAdapter";
|
|
7
|
+
export type { HttpAdapter } from "./httpAdapter";
|
|
7
8
|
export { Logger } from "./logger";
|
|
8
9
|
export type { LogFormat, LoggerOptions } from "./logger";
|
package/dist/http/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Logger = exports.HttpMethods = exports.NodeHttpAdapter = exports.Context = exports.Request = exports.Response = void 0;
|
|
3
|
+
exports.Logger = exports.HttpMethods = exports.WebHttpAdapter = exports.NodeHttpAdapter = exports.Context = exports.Request = exports.Response = void 0;
|
|
4
4
|
var response_1 = require("./response");
|
|
5
5
|
Object.defineProperty(exports, "Response", { enumerable: true, get: function () { return response_1.Response; } });
|
|
6
6
|
var request_1 = require("./request");
|
|
@@ -9,6 +9,8 @@ var context_1 = require("./context");
|
|
|
9
9
|
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
|
|
10
10
|
var nodeNativeHttp_1 = require("./nodeNativeHttp");
|
|
11
11
|
Object.defineProperty(exports, "NodeHttpAdapter", { enumerable: true, get: function () { return nodeNativeHttp_1.NodeHttpAdapter; } });
|
|
12
|
+
var webHttp_1 = require("./webHttp");
|
|
13
|
+
Object.defineProperty(exports, "WebHttpAdapter", { enumerable: true, get: function () { return webHttp_1.WebHttpAdapter; } });
|
|
12
14
|
var httpMethods_1 = require("./httpMethods");
|
|
13
15
|
Object.defineProperty(exports, "HttpMethods", { enumerable: true, get: function () { return httpMethods_1.HttpMethods; } });
|
|
14
16
|
var logger_1 = require("./logger");
|
package/dist/http/logger.d.ts
CHANGED
|
@@ -2,8 +2,12 @@ import { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
export type LogFormat = "combined" | "common" | "dev" | "short" | "tiny";
|
|
3
3
|
export type LoggerOptions = {
|
|
4
4
|
format?: LogFormat;
|
|
5
|
-
stream?:
|
|
6
|
-
|
|
5
|
+
stream?: {
|
|
6
|
+
write(chunk: string): unknown;
|
|
7
|
+
};
|
|
8
|
+
fileStream?: {
|
|
9
|
+
write(chunk: string): unknown;
|
|
10
|
+
};
|
|
7
11
|
};
|
|
8
12
|
/**
|
|
9
13
|
* Minimal HTTP request logger.
|
|
@@ -45,7 +49,9 @@ export declare class Logger {
|
|
|
45
49
|
* @param startTime - High-resolution timestamp captured at request start using `process.hrtime.bigint()`.
|
|
46
50
|
*/
|
|
47
51
|
log(req: IncomingMessage, res: ServerResponse, startTime: bigint): void;
|
|
52
|
+
logWeb(req: globalThis.Request, res: globalThis.Response, startTimeMs: number): void;
|
|
48
53
|
private buildLogLine;
|
|
54
|
+
private buildWebLogLine;
|
|
49
55
|
/**
|
|
50
56
|
* Applies ANSI color codes to an HTTP status code for terminal output.
|
|
51
57
|
*
|
|
@@ -60,4 +66,5 @@ export declare class Logger {
|
|
|
60
66
|
* @returns Colorized status code string suitable for terminal output.
|
|
61
67
|
*/
|
|
62
68
|
private colorizeStatus;
|
|
69
|
+
private getDefaultStream;
|
|
63
70
|
}
|
package/dist/http/logger.js
CHANGED
|
@@ -22,7 +22,7 @@ class Logger {
|
|
|
22
22
|
fileStream;
|
|
23
23
|
format;
|
|
24
24
|
constructor(options = {}) {
|
|
25
|
-
this.stream = options.stream ||
|
|
25
|
+
this.stream = options.stream || this.getDefaultStream();
|
|
26
26
|
this.fileStream = options.fileStream;
|
|
27
27
|
this.format = options.format || "dev";
|
|
28
28
|
}
|
|
@@ -54,6 +54,15 @@ class Logger {
|
|
|
54
54
|
this.fileStream.write(fileLine + "\n");
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
logWeb(req, res, startTimeMs) {
|
|
58
|
+
const responseTime = (performance.now() - startTimeMs).toFixed(3);
|
|
59
|
+
const consoleLine = this.buildWebLogLine(req, res, responseTime, true);
|
|
60
|
+
const fileLine = this.buildWebLogLine(req, res, responseTime, false);
|
|
61
|
+
this.stream.write(consoleLine + "\n");
|
|
62
|
+
if (this.fileStream) {
|
|
63
|
+
this.fileStream.write(fileLine + "\n");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
57
66
|
buildLogLine(req, res, responseTime, useColor) {
|
|
58
67
|
const method = req.method || "-";
|
|
59
68
|
const url = req.url || "-";
|
|
@@ -86,6 +95,32 @@ class Logger {
|
|
|
86
95
|
}
|
|
87
96
|
return `${method} ${url} ${statusCode} ${responseTime} ms - ${contentLength}`;
|
|
88
97
|
}
|
|
98
|
+
buildWebLogLine(req, res, responseTime, useColor) {
|
|
99
|
+
const method = req.method || "-";
|
|
100
|
+
const url = new URL(req.url).pathname || "-";
|
|
101
|
+
const statusCode = useColor
|
|
102
|
+
? this.colorizeStatus(res.status)
|
|
103
|
+
: String(res.status);
|
|
104
|
+
const contentLength = res.headers.get("content-length") || "-";
|
|
105
|
+
const remoteAddr = req.headers.get("x-forwarded-for") || "-";
|
|
106
|
+
const httpVersion = "1.1";
|
|
107
|
+
const referrer = req.headers.get("referer") || req.headers.get("referrer") || "-";
|
|
108
|
+
const userAgent = req.headers.get("user-agent") || "-";
|
|
109
|
+
const date = new Date().toUTCString();
|
|
110
|
+
if (this.format === "tiny") {
|
|
111
|
+
return `${method} ${url} ${statusCode} ${contentLength} - ${responseTime} ms`;
|
|
112
|
+
}
|
|
113
|
+
if (this.format === "short") {
|
|
114
|
+
return `${remoteAddr} ${method} ${url} ${statusCode} ${contentLength} - ${responseTime} ms`;
|
|
115
|
+
}
|
|
116
|
+
if (this.format === "common") {
|
|
117
|
+
return `${remoteAddr} - - [${date}] "${method} ${url} HTTP/${httpVersion}" ${statusCode} ${contentLength}`;
|
|
118
|
+
}
|
|
119
|
+
if (this.format === "combined") {
|
|
120
|
+
return `${remoteAddr} - - [${date}] "${method} ${url} HTTP/${httpVersion}" ${statusCode} ${contentLength} "${referrer}" "${userAgent}"`;
|
|
121
|
+
}
|
|
122
|
+
return `${method} ${url} ${statusCode} ${responseTime} ms - ${contentLength}`;
|
|
123
|
+
}
|
|
89
124
|
/**
|
|
90
125
|
* Applies ANSI color codes to an HTTP status code for terminal output.
|
|
91
126
|
*
|
|
@@ -115,5 +150,18 @@ class Logger {
|
|
|
115
150
|
}
|
|
116
151
|
return statusStr;
|
|
117
152
|
}
|
|
153
|
+
getDefaultStream() {
|
|
154
|
+
const maybeProcess = globalThis;
|
|
155
|
+
if (maybeProcess.process?.stdout?.write) {
|
|
156
|
+
return maybeProcess.process.stdout;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
write: (chunk) => {
|
|
160
|
+
// Use console fallback on runtimes without Node process/stdout.
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.log(chunk.trimEnd());
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
118
166
|
}
|
|
119
167
|
exports.Logger = Logger;
|
|
@@ -42,12 +42,13 @@ class NodeHttpAdapter {
|
|
|
42
42
|
* @returns A fully constructed {@link Context} instance
|
|
43
43
|
*/
|
|
44
44
|
async getContext() {
|
|
45
|
-
const
|
|
45
|
+
const protocol = this.getProtocol();
|
|
46
|
+
const url = new URL(this.req.url ?? "/", `${protocol}://${this.req.headers.host}`);
|
|
46
47
|
const request = new request_1.Request(url.pathname);
|
|
47
|
-
request.setMethod(this.req.method
|
|
48
|
+
request.setMethod(this.req.method ?? httpMethods_1.HttpMethods.get);
|
|
48
49
|
request.setQuery(Object.fromEntries(url.searchParams.entries()));
|
|
49
50
|
request.setHeaders(this.req.headers);
|
|
50
|
-
request.setRemoteAddress(this.req.socket
|
|
51
|
+
request.setRemoteAddress(this.req.socket.remoteAddress);
|
|
51
52
|
if (request.method === httpMethods_1.HttpMethods.post ||
|
|
52
53
|
request.method === httpMethods_1.HttpMethods.put ||
|
|
53
54
|
request.method === httpMethods_1.HttpMethods.patch) {
|
|
@@ -81,5 +82,15 @@ class NodeHttpAdapter {
|
|
|
81
82
|
}
|
|
82
83
|
this.res.end(response.content);
|
|
83
84
|
}
|
|
85
|
+
getProtocol() {
|
|
86
|
+
const forwarded = this.req.headers["x-forwarded-proto"];
|
|
87
|
+
if (typeof forwarded === "string") {
|
|
88
|
+
return forwarded.split(",")[0].trim();
|
|
89
|
+
}
|
|
90
|
+
if (this.req.socket.encrypted) {
|
|
91
|
+
return "https";
|
|
92
|
+
}
|
|
93
|
+
return "http";
|
|
94
|
+
}
|
|
84
95
|
}
|
|
85
96
|
exports.NodeHttpAdapter = NodeHttpAdapter;
|
package/dist/http/response.js
CHANGED
|
@@ -71,7 +71,7 @@ class Response {
|
|
|
71
71
|
/**
|
|
72
72
|
* Sets a cookie in the `Set-Cookie` response header.
|
|
73
73
|
*/
|
|
74
|
-
setCookie(name, value, options
|
|
74
|
+
setCookie(name, value, options) {
|
|
75
75
|
const cookie = (0, cookies_1.serializeCookie)(name, value, options);
|
|
76
76
|
const current = this._headers["Set-Cookie"];
|
|
77
77
|
if (!current) {
|
|
@@ -88,7 +88,7 @@ class Response {
|
|
|
88
88
|
/**
|
|
89
89
|
* Clears a cookie by setting an empty value and immediate expiration.
|
|
90
90
|
*/
|
|
91
|
-
removeCookie(name, options
|
|
91
|
+
removeCookie(name, options) {
|
|
92
92
|
return this.setCookie(name, "", {
|
|
93
93
|
...options,
|
|
94
94
|
maxAge: 0,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HttpAdapter } from "./httpAdapter";
|
|
2
|
+
import { Response } from "./response";
|
|
3
|
+
import { Context } from "./context";
|
|
4
|
+
import { type LoggerOptions } from "./logger";
|
|
5
|
+
type WebRequest = globalThis.Request;
|
|
6
|
+
type WebResponse = globalThis.Response;
|
|
7
|
+
export declare class WebHttpAdapter implements HttpAdapter {
|
|
8
|
+
private readonly req;
|
|
9
|
+
private contentParser;
|
|
10
|
+
private logger;
|
|
11
|
+
private startTimeMs;
|
|
12
|
+
private runtimeResponse;
|
|
13
|
+
constructor(req: WebRequest, loggerOptions?: LoggerOptions);
|
|
14
|
+
getContext(): Promise<Context>;
|
|
15
|
+
sendResponse(response: Response): void;
|
|
16
|
+
toWebResponse(): WebResponse;
|
|
17
|
+
private buildHeaders;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebHttpAdapter = void 0;
|
|
4
|
+
const httpMethods_1 = require("./httpMethods");
|
|
5
|
+
const request_1 = require("./request");
|
|
6
|
+
const context_1 = require("./context");
|
|
7
|
+
const contentParserManager_1 = require("../parsers/contentParserManager");
|
|
8
|
+
const httpExceptions_1 = require("../exceptions/httpExceptions");
|
|
9
|
+
const node_stream_1 = require("node:stream");
|
|
10
|
+
const logger_1 = require("./logger");
|
|
11
|
+
class WebHttpAdapter {
|
|
12
|
+
req;
|
|
13
|
+
contentParser;
|
|
14
|
+
logger;
|
|
15
|
+
startTimeMs;
|
|
16
|
+
runtimeResponse = null;
|
|
17
|
+
constructor(req, loggerOptions = {}) {
|
|
18
|
+
this.req = req;
|
|
19
|
+
this.contentParser = new contentParserManager_1.ContentParserManager();
|
|
20
|
+
this.logger = new logger_1.Logger(loggerOptions);
|
|
21
|
+
this.startTimeMs = performance.now();
|
|
22
|
+
}
|
|
23
|
+
async getContext() {
|
|
24
|
+
const url = new URL(this.req.url);
|
|
25
|
+
const request = new request_1.Request(url.pathname);
|
|
26
|
+
request.setMethod((this.req.method || "GET").toUpperCase() ||
|
|
27
|
+
httpMethods_1.HttpMethods.get);
|
|
28
|
+
request.setQuery(Object.fromEntries(url.searchParams.entries()));
|
|
29
|
+
request.setHeaders(this.buildHeaders());
|
|
30
|
+
if (request.method === httpMethods_1.HttpMethods.post ||
|
|
31
|
+
request.method === httpMethods_1.HttpMethods.put ||
|
|
32
|
+
request.method === httpMethods_1.HttpMethods.patch) {
|
|
33
|
+
const parsedData = await this.contentParser.parse(this.req);
|
|
34
|
+
request.setBody(parsedData);
|
|
35
|
+
}
|
|
36
|
+
return new context_1.Context(request);
|
|
37
|
+
}
|
|
38
|
+
sendResponse(response) {
|
|
39
|
+
response.prepare();
|
|
40
|
+
const headers = new Headers();
|
|
41
|
+
for (const [header, value] of Object.entries(response.headers)) {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
for (const entry of value) {
|
|
44
|
+
headers.append(header, entry);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (typeof value !== "undefined") {
|
|
49
|
+
headers.set(header, String(value));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!response.content) {
|
|
53
|
+
headers.delete("content-type");
|
|
54
|
+
this.runtimeResponse = new globalThis.Response(null, {
|
|
55
|
+
status: response.statusCode,
|
|
56
|
+
headers,
|
|
57
|
+
});
|
|
58
|
+
this.logger.logWeb(this.req, this.runtimeResponse, this.startTimeMs);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (response.content instanceof node_stream_1.Readable) {
|
|
62
|
+
throw new httpExceptions_1.NotImplementedError("Node.js Readable stream responses are not supported in this runtime");
|
|
63
|
+
}
|
|
64
|
+
const body = typeof response.content === "string"
|
|
65
|
+
? response.content
|
|
66
|
+
: new Uint8Array(response.content);
|
|
67
|
+
this.runtimeResponse = new globalThis.Response(body, {
|
|
68
|
+
status: response.statusCode,
|
|
69
|
+
headers,
|
|
70
|
+
});
|
|
71
|
+
this.logger.logWeb(this.req, this.runtimeResponse, this.startTimeMs);
|
|
72
|
+
}
|
|
73
|
+
toWebResponse() {
|
|
74
|
+
return (this.runtimeResponse ||
|
|
75
|
+
new globalThis.Response("No response generated", {
|
|
76
|
+
status: 500,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
buildHeaders() {
|
|
80
|
+
const headers = Object.create(null);
|
|
81
|
+
for (const [key, value] of this.req.headers.entries()) {
|
|
82
|
+
if (typeof headers[key] === "undefined") {
|
|
83
|
+
headers[key] = value;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(headers[key])) {
|
|
87
|
+
headers[key] = [...headers[key], value];
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
headers[key] = [headers[key], value];
|
|
91
|
+
}
|
|
92
|
+
return headers;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.WebHttpAdapter = WebHttpAdapter;
|
|
@@ -26,14 +26,16 @@ export declare class ContentParserManager {
|
|
|
26
26
|
* @param req - Native incoming HTTP request
|
|
27
27
|
* @returns Parsed body content or raw body
|
|
28
28
|
*/
|
|
29
|
-
parse(req: IncomingMessage): Promise<unknown>;
|
|
29
|
+
parse(req: IncomingMessage | globalThis.Request): Promise<unknown>;
|
|
30
30
|
/**
|
|
31
31
|
* Reads the raw request body.
|
|
32
32
|
*
|
|
33
33
|
* @param req - Native incoming HTTP request
|
|
34
34
|
* @returns A promise that resolves to the full body buffer
|
|
35
35
|
*/
|
|
36
|
-
private
|
|
36
|
+
private readNodeBody;
|
|
37
|
+
private readWebBody;
|
|
38
|
+
private isWebRequest;
|
|
37
39
|
/**
|
|
38
40
|
* Finds a parser capable of handling the given content type.
|
|
39
41
|
*
|
|
@@ -43,10 +43,15 @@ class ContentParserManager {
|
|
|
43
43
|
* @returns Parsed body content or raw body
|
|
44
44
|
*/
|
|
45
45
|
async parse(req) {
|
|
46
|
-
const
|
|
46
|
+
const isWebRequest = this.isWebRequest(req);
|
|
47
|
+
const body = isWebRequest
|
|
48
|
+
? await this.readWebBody(req)
|
|
49
|
+
: await this.readNodeBody(req);
|
|
47
50
|
if (body.length <= 0)
|
|
48
51
|
return {};
|
|
49
|
-
const contentType =
|
|
52
|
+
const contentType = isWebRequest
|
|
53
|
+
? req.headers.get("content-type") || "text/plain"
|
|
54
|
+
: req.headers["content-type"] || "text/plain";
|
|
50
55
|
const parser = this.findParser(contentType);
|
|
51
56
|
if (!parser)
|
|
52
57
|
return Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
@@ -58,7 +63,7 @@ class ContentParserManager {
|
|
|
58
63
|
* @param req - Native incoming HTTP request
|
|
59
64
|
* @returns A promise that resolves to the full body buffer
|
|
60
65
|
*/
|
|
61
|
-
|
|
66
|
+
readNodeBody(req) {
|
|
62
67
|
return new Promise((resolve, reject) => {
|
|
63
68
|
const chunks = [];
|
|
64
69
|
req.on("data", (chunk) => {
|
|
@@ -72,6 +77,20 @@ class ContentParserManager {
|
|
|
72
77
|
});
|
|
73
78
|
});
|
|
74
79
|
}
|
|
80
|
+
async readWebBody(req) {
|
|
81
|
+
try {
|
|
82
|
+
const arrayBuffer = await req.arrayBuffer();
|
|
83
|
+
return Buffer.from(arrayBuffer);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new httpExceptions_1.UnprocessableContentError("Failed to read request body");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
isWebRequest(req) {
|
|
90
|
+
const candidate = req;
|
|
91
|
+
return (typeof candidate.arrayBuffer === "function" &&
|
|
92
|
+
typeof candidate.headers?.get === "function");
|
|
93
|
+
}
|
|
75
94
|
/**
|
|
76
95
|
* Finds a parser capable of handling the given content type.
|
|
77
96
|
*
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LoggerOptions } from "../http/logger";
|
|
2
|
+
import type { AdapterHandler, RuntimeListenOptions, RuntimeServer } from "./types";
|
|
3
|
+
export declare class BunRuntimeServer implements RuntimeServer {
|
|
4
|
+
private readonly loggerOptions;
|
|
5
|
+
constructor(loggerOptions?: LoggerOptions);
|
|
6
|
+
listen(handler: AdapterHandler, options: RuntimeListenOptions): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BunRuntimeServer = void 0;
|
|
4
|
+
const webHttp_1 = require("../http/webHttp");
|
|
5
|
+
class BunRuntimeServer {
|
|
6
|
+
loggerOptions;
|
|
7
|
+
constructor(loggerOptions = {}) {
|
|
8
|
+
this.loggerOptions = loggerOptions;
|
|
9
|
+
}
|
|
10
|
+
listen(handler, options) {
|
|
11
|
+
const bun = globalThis.Bun;
|
|
12
|
+
if (!bun) {
|
|
13
|
+
throw new Error("Bun runtime not detected");
|
|
14
|
+
}
|
|
15
|
+
bun.serve({
|
|
16
|
+
hostname: options.hostname,
|
|
17
|
+
port: options.port,
|
|
18
|
+
fetch: async (request) => {
|
|
19
|
+
const adapter = new webHttp_1.WebHttpAdapter(request, this.loggerOptions);
|
|
20
|
+
await handler(adapter);
|
|
21
|
+
return adapter.toWebResponse();
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
if (options.callback)
|
|
25
|
+
options.callback();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.BunRuntimeServer = BunRuntimeServer;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LoggerOptions } from "../http/logger";
|
|
2
|
+
import type { RuntimeKind, RuntimeServer } from "./types";
|
|
3
|
+
export declare const createRuntimeServer: (loggerOptions?: LoggerOptions, runtime?: RuntimeKind) => RuntimeServer;
|
|
4
|
+
export declare const createRuntimeServerByKind: (runtime: RuntimeKind, loggerOptions?: LoggerOptions) => RuntimeServer;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRuntimeServerByKind = exports.createRuntimeServer = void 0;
|
|
4
|
+
const bunRuntimeServer_1 = require("./bunRuntimeServer");
|
|
5
|
+
const denoRuntimeServer_1 = require("./denoRuntimeServer");
|
|
6
|
+
const nodeRuntimeServer_1 = require("./nodeRuntimeServer");
|
|
7
|
+
const runtimeDetector_1 = require("./runtimeDetector");
|
|
8
|
+
const createRuntimeServer = (loggerOptions = {}, runtime = (0, runtimeDetector_1.detectRuntime)()) => {
|
|
9
|
+
switch (runtime) {
|
|
10
|
+
case "bun":
|
|
11
|
+
return new bunRuntimeServer_1.BunRuntimeServer(loggerOptions);
|
|
12
|
+
case "deno":
|
|
13
|
+
return new denoRuntimeServer_1.DenoRuntimeServer(loggerOptions);
|
|
14
|
+
case "node":
|
|
15
|
+
default:
|
|
16
|
+
return new nodeRuntimeServer_1.NodeRuntimeServer(loggerOptions);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
exports.createRuntimeServer = createRuntimeServer;
|
|
20
|
+
const createRuntimeServerByKind = (runtime, loggerOptions = {}) => {
|
|
21
|
+
return (0, exports.createRuntimeServer)(loggerOptions, runtime);
|
|
22
|
+
};
|
|
23
|
+
exports.createRuntimeServerByKind = createRuntimeServerByKind;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LoggerOptions } from "../http/logger";
|
|
2
|
+
import type { AdapterHandler, RuntimeListenOptions, RuntimeServer } from "./types";
|
|
3
|
+
export declare class DenoRuntimeServer implements RuntimeServer {
|
|
4
|
+
private readonly loggerOptions;
|
|
5
|
+
constructor(loggerOptions?: LoggerOptions);
|
|
6
|
+
listen(handler: AdapterHandler, options: RuntimeListenOptions): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DenoRuntimeServer = void 0;
|
|
4
|
+
const webHttp_1 = require("../http/webHttp");
|
|
5
|
+
class DenoRuntimeServer {
|
|
6
|
+
loggerOptions;
|
|
7
|
+
constructor(loggerOptions = {}) {
|
|
8
|
+
this.loggerOptions = loggerOptions;
|
|
9
|
+
}
|
|
10
|
+
listen(handler, options) {
|
|
11
|
+
const deno = globalThis.Deno;
|
|
12
|
+
if (!deno) {
|
|
13
|
+
throw new Error("Deno runtime not detected");
|
|
14
|
+
}
|
|
15
|
+
deno.serve({
|
|
16
|
+
hostname: options.hostname,
|
|
17
|
+
port: options.port,
|
|
18
|
+
}, async (request) => {
|
|
19
|
+
const adapter = new webHttp_1.WebHttpAdapter(request, this.loggerOptions);
|
|
20
|
+
await handler(adapter);
|
|
21
|
+
return adapter.toWebResponse();
|
|
22
|
+
});
|
|
23
|
+
if (options.callback)
|
|
24
|
+
options.callback();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.DenoRuntimeServer = DenoRuntimeServer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createRuntimeServer, createRuntimeServerByKind, } from "./createRuntimeServer";
|
|
2
|
+
export { detectRuntime, isBunRuntime, isDenoRuntime } from "./runtimeDetector";
|
|
3
|
+
export { NodeRuntimeServer } from "./nodeRuntimeServer";
|
|
4
|
+
export { BunRuntimeServer } from "./bunRuntimeServer";
|
|
5
|
+
export { DenoRuntimeServer } from "./denoRuntimeServer";
|
|
6
|
+
export type { AdapterHandler, RuntimeKind, RuntimeListenOptions, RuntimeServer, } from "./types";
|
|
7
|
+
export { getModulePathInfo, resolveFromModuleUrl, type ModulePathInfo, } from "./modulePath";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveFromModuleUrl = exports.getModulePathInfo = exports.DenoRuntimeServer = exports.BunRuntimeServer = exports.NodeRuntimeServer = exports.isDenoRuntime = exports.isBunRuntime = exports.detectRuntime = exports.createRuntimeServerByKind = exports.createRuntimeServer = void 0;
|
|
4
|
+
var createRuntimeServer_1 = require("./createRuntimeServer");
|
|
5
|
+
Object.defineProperty(exports, "createRuntimeServer", { enumerable: true, get: function () { return createRuntimeServer_1.createRuntimeServer; } });
|
|
6
|
+
Object.defineProperty(exports, "createRuntimeServerByKind", { enumerable: true, get: function () { return createRuntimeServer_1.createRuntimeServerByKind; } });
|
|
7
|
+
var runtimeDetector_1 = require("./runtimeDetector");
|
|
8
|
+
Object.defineProperty(exports, "detectRuntime", { enumerable: true, get: function () { return runtimeDetector_1.detectRuntime; } });
|
|
9
|
+
Object.defineProperty(exports, "isBunRuntime", { enumerable: true, get: function () { return runtimeDetector_1.isBunRuntime; } });
|
|
10
|
+
Object.defineProperty(exports, "isDenoRuntime", { enumerable: true, get: function () { return runtimeDetector_1.isDenoRuntime; } });
|
|
11
|
+
var nodeRuntimeServer_1 = require("./nodeRuntimeServer");
|
|
12
|
+
Object.defineProperty(exports, "NodeRuntimeServer", { enumerable: true, get: function () { return nodeRuntimeServer_1.NodeRuntimeServer; } });
|
|
13
|
+
var bunRuntimeServer_1 = require("./bunRuntimeServer");
|
|
14
|
+
Object.defineProperty(exports, "BunRuntimeServer", { enumerable: true, get: function () { return bunRuntimeServer_1.BunRuntimeServer; } });
|
|
15
|
+
var denoRuntimeServer_1 = require("./denoRuntimeServer");
|
|
16
|
+
Object.defineProperty(exports, "DenoRuntimeServer", { enumerable: true, get: function () { return denoRuntimeServer_1.DenoRuntimeServer; } });
|
|
17
|
+
var modulePath_1 = require("./modulePath");
|
|
18
|
+
Object.defineProperty(exports, "getModulePathInfo", { enumerable: true, get: function () { return modulePath_1.getModulePathInfo; } });
|
|
19
|
+
Object.defineProperty(exports, "resolveFromModuleUrl", { enumerable: true, get: function () { return modulePath_1.resolveFromModuleUrl; } });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveFromModuleUrl = exports.getModulePathInfo = void 0;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const node_url_1 = require("node:url");
|
|
6
|
+
const getModulePathInfo = (metaUrl) => {
|
|
7
|
+
const filename = (0, node_url_1.fileURLToPath)(metaUrl);
|
|
8
|
+
return {
|
|
9
|
+
filename,
|
|
10
|
+
dirname: (0, node_path_1.dirname)(filename),
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
exports.getModulePathInfo = getModulePathInfo;
|
|
14
|
+
const resolveFromModuleUrl = (metaUrl, ...segments) => {
|
|
15
|
+
const { dirname: moduleDirname } = (0, exports.getModulePathInfo)(metaUrl);
|
|
16
|
+
return (0, node_path_1.join)(moduleDirname, ...segments);
|
|
17
|
+
};
|
|
18
|
+
exports.resolveFromModuleUrl = resolveFromModuleUrl;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LoggerOptions } from "../http/logger";
|
|
2
|
+
import type { RuntimeListenOptions, RuntimeServer, AdapterHandler } from "./types";
|
|
3
|
+
export declare class NodeRuntimeServer implements RuntimeServer {
|
|
4
|
+
private readonly loggerOptions;
|
|
5
|
+
constructor(loggerOptions?: LoggerOptions);
|
|
6
|
+
listen(handler: AdapterHandler, options: RuntimeListenOptions): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NodeRuntimeServer = void 0;
|
|
4
|
+
const node_http_1 = require("node:http");
|
|
5
|
+
const nodeNativeHttp_1 = require("../http/nodeNativeHttp");
|
|
6
|
+
class NodeRuntimeServer {
|
|
7
|
+
loggerOptions;
|
|
8
|
+
constructor(loggerOptions = {}) {
|
|
9
|
+
this.loggerOptions = loggerOptions;
|
|
10
|
+
}
|
|
11
|
+
listen(handler, options) {
|
|
12
|
+
(0, node_http_1.createServer)((req, res) => {
|
|
13
|
+
const adapter = new nodeNativeHttp_1.NodeHttpAdapter(req, res, this.loggerOptions);
|
|
14
|
+
void handler(adapter);
|
|
15
|
+
}).listen(options.port, options.hostname, () => {
|
|
16
|
+
if (options.callback)
|
|
17
|
+
options.callback();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.NodeRuntimeServer = NodeRuntimeServer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectRuntime = exports.isDenoRuntime = exports.isBunRuntime = void 0;
|
|
4
|
+
const isBunRuntime = () => typeof globalThis.Bun !== "undefined";
|
|
5
|
+
exports.isBunRuntime = isBunRuntime;
|
|
6
|
+
const isDenoRuntime = () => typeof globalThis.Deno !== "undefined";
|
|
7
|
+
exports.isDenoRuntime = isDenoRuntime;
|
|
8
|
+
const detectRuntime = () => {
|
|
9
|
+
if ((0, exports.isBunRuntime)())
|
|
10
|
+
return "bun";
|
|
11
|
+
if ((0, exports.isDenoRuntime)())
|
|
12
|
+
return "deno";
|
|
13
|
+
return "node";
|
|
14
|
+
};
|
|
15
|
+
exports.detectRuntime = detectRuntime;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HttpAdapter } from "../http/httpAdapter";
|
|
2
|
+
export type RuntimeKind = "node" | "bun" | "deno";
|
|
3
|
+
export interface RuntimeListenOptions {
|
|
4
|
+
port: number;
|
|
5
|
+
hostname?: string;
|
|
6
|
+
callback?: VoidFunction;
|
|
7
|
+
}
|
|
8
|
+
export type AdapterHandler = (adapter: HttpAdapter) => Promise<void>;
|
|
9
|
+
export interface RuntimeServer {
|
|
10
|
+
listen(handler: AdapterHandler, options: RuntimeListenOptions): void;
|
|
11
|
+
}
|
package/dist/sessions/cookies.js
CHANGED
|
@@ -57,17 +57,17 @@ function parseCookies(cookieHeader) {
|
|
|
57
57
|
*
|
|
58
58
|
* @returns A properly formatted `Set-Cookie` header string.
|
|
59
59
|
*/
|
|
60
|
-
function serializeCookie(name, value, options
|
|
60
|
+
function serializeCookie(name, value, options) {
|
|
61
61
|
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
62
|
-
if (typeof options
|
|
62
|
+
if (typeof options?.maxAge === "number")
|
|
63
63
|
parts.push(`Max-Age=${options.maxAge}`);
|
|
64
|
-
if (options
|
|
64
|
+
if (options?.path)
|
|
65
65
|
parts.push(`Path=${options.path}`);
|
|
66
|
-
if (options
|
|
66
|
+
if (options?.sameSite)
|
|
67
67
|
parts.push(`SameSite=${options.sameSite}`);
|
|
68
|
-
if (options
|
|
68
|
+
if (options?.httpOnly)
|
|
69
69
|
parts.push("HttpOnly");
|
|
70
|
-
if (options
|
|
70
|
+
if (options?.secure)
|
|
71
71
|
parts.push("Secure");
|
|
72
72
|
return parts.join("; ");
|
|
73
73
|
}
|
package/dist/sessions/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { MemorySessionStorage } from "./memorySessionStorage";
|
|
2
2
|
export { FileSessionStorage } from "./fileSessionStorage";
|
|
3
|
-
export { DatabaseSessionStorage, SessionDatabaseAdapter, } from "./databaseSessionStorage";
|
|
3
|
+
export { DatabaseSessionStorage, type SessionDatabaseAdapter, } from "./databaseSessionStorage";
|
|
4
4
|
export { Session } from "./session";
|
|
5
|
-
export { SessionStorage } from "./sessionStorage";
|
|
5
|
+
export type { SessionStorage } from "./sessionStorage";
|
|
6
6
|
export { parseCookies, serializeCookie } from "./cookies";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ValidationRule } from "./validationRule";
|
|
1
|
+
export type { ValidationRule } from "./validationRule";
|
|
2
2
|
export { Validator } from "./validator";
|
|
3
3
|
export * from "./rules";
|
|
4
|
-
export { RuleOptions, ValidationContext, ValidationError, ValidationResult, FieldDefinition, } from "./types";
|
|
4
|
+
export type { RuleOptions, ValidationContext, ValidationError, ValidationResult, FieldDefinition, } from "./types";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { BooleanRule } from "./booleanRule";
|
|
2
|
-
export { DateRule, DateRuleOptions } from "./dateRule";
|
|
3
|
-
export { NumberRule, NumberRuleOptions } from "./numberRule";
|
|
4
|
-
export { StringRule, StringRuleOptions } from "./stringRule";
|
|
5
|
-
export { ArrayRule, ArrayRuleOptions } from "./arrayRule";
|
|
2
|
+
export { DateRule, type DateRuleOptions } from "./dateRule";
|
|
3
|
+
export { NumberRule, type NumberRuleOptions } from "./numberRule";
|
|
4
|
+
export { StringRule, type StringRuleOptions } from "./stringRule";
|
|
5
|
+
export { ArrayRule, type ArrayRuleOptions } from "./arrayRule";
|
|
6
6
|
export { LiteralRule } from "./literalRule";
|
|
7
7
|
export { ObjectRule } from "./objectRule";
|
|
8
8
|
export { UnionRule } from "./unionRule";
|
|
9
|
-
export { BigIntRule, BigIntRuleOptions } from "./bigIntRule";
|
|
9
|
+
export { BigIntRule, type BigIntRuleOptions } from "./bigIntRule";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skyguard-js",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "A lightweight, dependency-free TypeScript backend framework",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -57,18 +57,17 @@
|
|
|
57
57
|
"license": "MIT",
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@eslint/js": "10.0.1",
|
|
60
|
-
"@jest/globals": "30.
|
|
60
|
+
"@jest/globals": "30.3.0",
|
|
61
61
|
"@types/jest": "30.0.0",
|
|
62
|
-
"@types/node": "25.
|
|
63
|
-
"eslint": "10.0
|
|
62
|
+
"@types/node": "25.6.0",
|
|
63
|
+
"eslint": "10.2.0",
|
|
64
64
|
"eslint-config-prettier": "10.1.8",
|
|
65
|
-
"globals": "17.
|
|
66
|
-
"jest": "30.
|
|
65
|
+
"globals": "17.5.0",
|
|
66
|
+
"jest": "30.3.0",
|
|
67
67
|
"nodemon": "3.1.14",
|
|
68
|
-
"
|
|
69
|
-
"ts-jest": "29.4.6",
|
|
68
|
+
"ts-jest": "29.4.9",
|
|
70
69
|
"ts-node": "10.9.2",
|
|
71
|
-
"typescript": "
|
|
72
|
-
"typescript-eslint": "8.
|
|
70
|
+
"typescript": "6.0.2",
|
|
71
|
+
"typescript-eslint": "8.58.2"
|
|
73
72
|
}
|
|
74
73
|
}
|