skyguard-js 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/dist/app.d.ts +123 -0
- package/dist/app.js +198 -0
- package/dist/container/container.d.ts +60 -0
- package/dist/container/container.js +71 -0
- package/dist/exceptions/baseException.d.ts +6 -0
- package/dist/exceptions/baseException.js +15 -0
- package/dist/exceptions/contentDispositionException.d.ts +4 -0
- package/dist/exceptions/contentDispositionException.js +11 -0
- package/dist/exceptions/contentParserException.d.ts +10 -0
- package/dist/exceptions/contentParserException.js +21 -0
- package/dist/exceptions/fileDownloadException.d.ts +4 -0
- package/dist/exceptions/fileDownloadException.js +11 -0
- package/dist/exceptions/fileExistsException.d.ts +4 -0
- package/dist/exceptions/fileExistsException.js +11 -0
- package/dist/exceptions/helperExceptions.d.ts +10 -0
- package/dist/exceptions/helperExceptions.js +25 -0
- package/dist/exceptions/httpNotFoundException.d.ts +7 -0
- package/dist/exceptions/httpNotFoundException.js +14 -0
- package/dist/exceptions/index.d.ts +9 -0
- package/dist/exceptions/index.js +25 -0
- package/dist/exceptions/invalidHttpStatusException.d.ts +4 -0
- package/dist/exceptions/invalidHttpStatusException.js +11 -0
- package/dist/exceptions/sessionException.d.ts +4 -0
- package/dist/exceptions/sessionException.js +11 -0
- package/dist/exceptions/validationException.d.ts +13 -0
- package/dist/exceptions/validationException.js +32 -0
- package/dist/helpers/app.d.ts +4 -0
- package/dist/helpers/app.js +12 -0
- package/dist/helpers/http.d.ts +59 -0
- package/dist/helpers/http.js +77 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +9 -0
- package/dist/http/httpAdapter.d.ts +26 -0
- package/dist/http/httpAdapter.js +2 -0
- package/dist/http/httpMethods.d.ts +14 -0
- package/dist/http/httpMethods.js +18 -0
- package/dist/http/index.d.ts +6 -0
- package/dist/http/index.js +13 -0
- package/dist/http/logger.d.ts +8 -0
- package/dist/http/logger.js +36 -0
- package/dist/http/nodeNativeHttp.d.ts +41 -0
- package/dist/http/nodeNativeHttp.js +73 -0
- package/dist/http/request.d.ts +85 -0
- package/dist/http/request.js +127 -0
- package/dist/http/response.d.ts +118 -0
- package/dist/http/response.js +179 -0
- package/dist/http/statusCodes.d.ts +1 -0
- package/dist/http/statusCodes.js +38 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -0
- package/dist/middlewares/cors.d.ts +103 -0
- package/dist/middlewares/cors.js +91 -0
- package/dist/middlewares/session.d.ts +26 -0
- package/dist/middlewares/session.js +88 -0
- package/dist/parsers/contentParser.d.ts +27 -0
- package/dist/parsers/contentParser.js +2 -0
- package/dist/parsers/contentParserManager.d.ts +50 -0
- package/dist/parsers/contentParserManager.js +91 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.js +30 -0
- package/dist/parsers/jsonParser.d.ts +10 -0
- package/dist/parsers/jsonParser.js +24 -0
- package/dist/parsers/multipartParser.d.ts +35 -0
- package/dist/parsers/multipartParser.js +120 -0
- package/dist/parsers/parserInterface.d.ts +37 -0
- package/dist/parsers/parserInterface.js +2 -0
- package/dist/parsers/textParser.d.ts +11 -0
- package/dist/parsers/textParser.js +19 -0
- package/dist/parsers/urlEncodedParser.d.ts +10 -0
- package/dist/parsers/urlEncodedParser.js +19 -0
- package/dist/parsers/xmlParser.d.ts +47 -0
- package/dist/parsers/xmlParser.js +158 -0
- package/dist/routing/index.d.ts +3 -0
- package/dist/routing/index.js +9 -0
- package/dist/routing/layer.d.ts +85 -0
- package/dist/routing/layer.js +117 -0
- package/dist/routing/router.d.ts +143 -0
- package/dist/routing/router.js +210 -0
- package/dist/routing/routerGroup.d.ts +79 -0
- package/dist/routing/routerGroup.js +103 -0
- package/dist/server/nodeNativeServer.d.ts +29 -0
- package/dist/server/nodeNativeServer.js +42 -0
- package/dist/sessions/cookieOptions.d.ts +72 -0
- package/dist/sessions/cookieOptions.js +2 -0
- package/dist/sessions/index.d.ts +4 -0
- package/dist/sessions/index.js +7 -0
- package/dist/sessions/memorySessionStorage.d.ts +112 -0
- package/dist/sessions/memorySessionStorage.js +170 -0
- package/dist/sessions/session.d.ts +80 -0
- package/dist/sessions/session.js +101 -0
- package/dist/sessions/sessionStorage.d.ts +105 -0
- package/dist/sessions/sessionStorage.js +2 -0
- package/dist/static/contentDisposition.d.ts +71 -0
- package/dist/static/contentDisposition.js +159 -0
- package/dist/static/fileDownload.d.ts +44 -0
- package/dist/static/fileDownload.js +88 -0
- package/dist/static/fileStaticHandler.d.ts +61 -0
- package/dist/static/fileStaticHandler.js +110 -0
- package/dist/static/index.d.ts +4 -0
- package/dist/static/index.js +11 -0
- package/dist/static/mimeTypes.d.ts +1 -0
- package/dist/static/mimeTypes.js +40 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +122 -0
- package/dist/types/index.js +2 -0
- package/dist/validators/index.d.ts +5 -0
- package/dist/validators/index.js +22 -0
- package/dist/validators/rules/booleanRule.d.ts +11 -0
- package/dist/validators/rules/booleanRule.js +22 -0
- package/dist/validators/rules/dateRule.d.ts +16 -0
- package/dist/validators/rules/dateRule.js +44 -0
- package/dist/validators/rules/emailRule.d.ts +12 -0
- package/dist/validators/rules/emailRule.js +24 -0
- package/dist/validators/rules/index.d.ts +6 -0
- package/dist/validators/rules/index.js +15 -0
- package/dist/validators/rules/numberRule.d.ts +17 -0
- package/dist/validators/rules/numberRule.js +30 -0
- package/dist/validators/rules/requiredRule.d.ts +11 -0
- package/dist/validators/rules/requiredRule.js +21 -0
- package/dist/validators/rules/stringRule.d.ts +18 -0
- package/dist/validators/rules/stringRule.js +32 -0
- package/dist/validators/types.d.ts +55 -0
- package/dist/validators/types.js +2 -0
- package/dist/validators/validationRule.d.ts +53 -0
- package/dist/validators/validationRule.js +37 -0
- package/dist/validators/validationSchema.d.ts +145 -0
- package/dist/validators/validationSchema.js +198 -0
- package/dist/validators/validator.d.ts +58 -0
- package/dist/validators/validator.js +91 -0
- package/dist/views/helpersTemplate.d.ts +104 -0
- package/dist/views/helpersTemplate.js +186 -0
- package/dist/views/index.d.ts +4 -0
- package/dist/views/index.js +9 -0
- package/dist/views/raptorEngine.d.ts +127 -0
- package/dist/views/raptorEngine.js +165 -0
- package/dist/views/templateEngine.d.ts +80 -0
- package/dist/views/templateEngine.js +204 -0
- package/dist/views/view.d.ts +55 -0
- package/dist/views/view.js +2 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pipe930
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# 🛡️✈️ Skyguard.js — TypeScript Web Framework
|
|
2
|
+
|
|
3
|
+
Skyguard.js is a **lightweight and experimental web framework**, inspired by **Express**, written entirely in **TypeScript**.
|
|
4
|
+
|
|
5
|
+
The main goal of this project is to **learn, experiment, and build a solid foundation** for a more complete backend framework in the future.
|
|
6
|
+
|
|
7
|
+
At its current stage, the framework focuses on **routing**, **internal architecture**, **type safety**, and **core HTTP abstractions**, leaving advanced features for later iterations.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🎯 Current Goals
|
|
12
|
+
|
|
13
|
+
* Provide a simple and expressive API to register and handle HTTP routes
|
|
14
|
+
* Maintain a clean, extensible, and framework-agnostic architecture
|
|
15
|
+
* Leverage TypeScript for strong typing and better developer experience
|
|
16
|
+
* Serve as a learning project with progressive evolution
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## ✨ Current Features
|
|
21
|
+
|
|
22
|
+
* TypeScript-first design
|
|
23
|
+
* HTTP routing by method (GET, POST, PUT, PATCH, DELETE)
|
|
24
|
+
* Route groups with prefixes
|
|
25
|
+
* Global, group, and route-level middlewares
|
|
26
|
+
* Request / Response abstractions
|
|
27
|
+
* Declarative data validation
|
|
28
|
+
* Simple template engine with layouts and helpers
|
|
29
|
+
* Static file serving
|
|
30
|
+
* Session handling (via middleware)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📦 Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install skyguard-js
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🏁 Basic Usage
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createApp } from "skyguard-js";
|
|
46
|
+
import { Response } from "skyguard-js/http";
|
|
47
|
+
|
|
48
|
+
const app = createApp();
|
|
49
|
+
|
|
50
|
+
app.get("/health", () => {
|
|
51
|
+
return Response.json({ status: "ok" });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
app.listen(3000);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🛣️ Routing
|
|
60
|
+
|
|
61
|
+
Routes are registered using HTTP methods on the `app` instance.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
app.get("/posts/{id}", (request: Request) => {
|
|
65
|
+
return Response.json(request.getParams());
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
app.post("/posts", (request: Request) => {
|
|
69
|
+
return Response.json(request.getData());
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Internally, the framework maps HTTP methods to route layers using an optimized routing table.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🧩 Route Groups
|
|
78
|
+
|
|
79
|
+
Route groups allow you to organize endpoints under a shared prefix.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
app.group("/api", (api) => {
|
|
83
|
+
api.get("/users", () => Response.json({ message: "Users" }));
|
|
84
|
+
api.get("/products", () => Response.json({ message: "Products" }));
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🛠️ Middlewares
|
|
91
|
+
|
|
92
|
+
Middlewares can be registered **globally**, **per group**, or **per route**.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { Request, Response } from "skyguard-js/http";
|
|
96
|
+
import { RouteHandler } from "skyguard-js/types";
|
|
97
|
+
|
|
98
|
+
const authMiddleware = async (
|
|
99
|
+
request: Request,
|
|
100
|
+
next: RouteHandler,
|
|
101
|
+
): Promise<Response> => {
|
|
102
|
+
if (request.getHeaders["authorization"] !== "secret") {
|
|
103
|
+
return Response.json({ message: "Unauthorized" }).setStatus(401);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return next(request);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Global middleware
|
|
110
|
+
app.middlewares([authMiddleware]);
|
|
111
|
+
|
|
112
|
+
// Group middleware
|
|
113
|
+
app.group("/admin", (admin) => {
|
|
114
|
+
admin.middlewares([authMiddleware]);
|
|
115
|
+
admin.get("/dashboard", () => Response.json({ ok: true }));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Route-level middleware
|
|
119
|
+
app.get(
|
|
120
|
+
"/secure",
|
|
121
|
+
() => Response.json({ secure: true }),
|
|
122
|
+
[authMiddleware],
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 📦 Data Validation
|
|
129
|
+
|
|
130
|
+
Skyguard.js provides a **declarative validation system** using schemas.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { ValidationSchema } from "skyguard-js/validation";
|
|
134
|
+
|
|
135
|
+
export const userSchema = ValidationSchema.create()
|
|
136
|
+
.field("name")
|
|
137
|
+
.required("Name is required")
|
|
138
|
+
.string({ maxLength: 60 })
|
|
139
|
+
.field("email")
|
|
140
|
+
.required()
|
|
141
|
+
.email()
|
|
142
|
+
.field("age")
|
|
143
|
+
.number({ min: 18, max: 99 })
|
|
144
|
+
.field("active")
|
|
145
|
+
.boolean()
|
|
146
|
+
.build();
|
|
147
|
+
|
|
148
|
+
app.post("/users", (request: Request) => {
|
|
149
|
+
const validatedData = request.validateData(userSchema);
|
|
150
|
+
|
|
151
|
+
return Response.json({
|
|
152
|
+
success: true,
|
|
153
|
+
data: validatedData,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Validation is:
|
|
159
|
+
|
|
160
|
+
* Fail-fast per field
|
|
161
|
+
* Fully typed
|
|
162
|
+
* Reusable
|
|
163
|
+
* Decoupled from transport layer
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 📄 Views & Template Engine
|
|
168
|
+
|
|
169
|
+
To render HTML views, use the `render` helper.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { render } from "skyguard-js/helpers";
|
|
173
|
+
|
|
174
|
+
app.get("/home", () => {
|
|
175
|
+
return render(
|
|
176
|
+
"home",
|
|
177
|
+
{
|
|
178
|
+
title: "Products",
|
|
179
|
+
products: [
|
|
180
|
+
{ name: "Laptop", price: 999.99 },
|
|
181
|
+
{ name: "Mouse", price: 29.99 },
|
|
182
|
+
],
|
|
183
|
+
user: { name: "John", role: "admin" },
|
|
184
|
+
},
|
|
185
|
+
"main",
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Supported features
|
|
191
|
+
|
|
192
|
+
* Variable interpolation (`{{ variable }}`)
|
|
193
|
+
* Conditionals (`{{#if}}`)
|
|
194
|
+
* Loops (`{{#each}}`)
|
|
195
|
+
* Layouts
|
|
196
|
+
* Partials
|
|
197
|
+
* Built-in helpers (`upper`, `lower`, `date`)
|
|
198
|
+
* Custom helpers
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 🧱 Project Status
|
|
203
|
+
|
|
204
|
+
⚠️ **Early-stage project**
|
|
205
|
+
|
|
206
|
+
* Not production-ready
|
|
207
|
+
* API may change
|
|
208
|
+
* Features are still evolving
|
|
209
|
+
* Intended primarily for learning and experimentation
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 🔮 Roadmap (Tentative)
|
|
214
|
+
|
|
215
|
+
* Middleware system (✅)
|
|
216
|
+
* Template engine (✅)
|
|
217
|
+
* Request / Response abstraction (✅)
|
|
218
|
+
* Data validation (✅)
|
|
219
|
+
* Error handling improvements
|
|
220
|
+
* Database & ORM integration
|
|
221
|
+
* Authentication & authorization
|
|
222
|
+
* Sessions & cookies (in progress)
|
|
223
|
+
* Plugin system
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🧠 Motivation
|
|
228
|
+
|
|
229
|
+
This project was created to deeply understand how frameworks like **Express**, **Fastify**, and **Koa** work internally, by reimplementing their core ideas with a **modern TypeScript-first approach**.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 📄 License
|
|
234
|
+
|
|
235
|
+
MIT License
|
|
236
|
+
|
|
237
|
+
---
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { RouterGroup } from "./routing";
|
|
2
|
+
import { type HttpAdapter } from "./http";
|
|
3
|
+
import { type View } from "./views";
|
|
4
|
+
import type { Middleware, RouteHandler } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* The `App` class acts as the **execution kernel** and **lifecycle orchestrator**
|
|
7
|
+
* of the framework.
|
|
8
|
+
*
|
|
9
|
+
* It is responsible for:
|
|
10
|
+
* - Bootstrapping and exposing the routing system
|
|
11
|
+
* - Receiving normalized HTTP requests through adapters
|
|
12
|
+
* - Resolving the matching route
|
|
13
|
+
* - Executing the associated controller
|
|
14
|
+
* - Dispatching the final response to the client
|
|
15
|
+
*
|
|
16
|
+
* This class implements the **Singleton pattern** to guarantee
|
|
17
|
+
* a single application instance during the process lifecycle.
|
|
18
|
+
*
|
|
19
|
+
* The architecture fully decouples the core framework
|
|
20
|
+
* from the runtime platform (Node, Bun, Deno, etc.)
|
|
21
|
+
* through {@link HttpAdapter} and {@link Server}.
|
|
22
|
+
*/
|
|
23
|
+
export declare class App {
|
|
24
|
+
/** Main routing system */
|
|
25
|
+
private router;
|
|
26
|
+
/** Underlying HTTP server implementation */
|
|
27
|
+
private server;
|
|
28
|
+
/**
|
|
29
|
+
* View engine used to render templates.
|
|
30
|
+
*
|
|
31
|
+
* Typically consumed inside controllers to generate HTML responses.
|
|
32
|
+
*/
|
|
33
|
+
view: View;
|
|
34
|
+
/** Static file handler (optional) */
|
|
35
|
+
private staticFileHandler;
|
|
36
|
+
/**
|
|
37
|
+
* Bootstraps and configures the application.
|
|
38
|
+
*
|
|
39
|
+
* Acts as the **Composition Root** of the framework:
|
|
40
|
+
* this is the only place where concrete infrastructure
|
|
41
|
+
* implementations are instantiated and wired together.
|
|
42
|
+
*
|
|
43
|
+
* @returns The singleton `App` instance
|
|
44
|
+
*/
|
|
45
|
+
static bootstrap(): App;
|
|
46
|
+
/**
|
|
47
|
+
* Main execution pipeline.
|
|
48
|
+
*
|
|
49
|
+
* Execution flow:
|
|
50
|
+
* 1. Retrieves the normalized request from the adapter
|
|
51
|
+
* 2. Attempts to serve a static file (if enabled)
|
|
52
|
+
* 3. Resolves the matching route
|
|
53
|
+
* 4. Executes the controller
|
|
54
|
+
* 5. Sends the response back to the client
|
|
55
|
+
*
|
|
56
|
+
* This method is platform-agnostic.
|
|
57
|
+
*
|
|
58
|
+
* @param adapter - HTTP adapter bridging the runtime with the framework
|
|
59
|
+
*/
|
|
60
|
+
handle(adapter: HttpAdapter): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Enables static file serving.
|
|
63
|
+
*
|
|
64
|
+
* @param publicPath - Absolute or relative public directory path
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* app.staticFiles("public");
|
|
68
|
+
* // /public/css/style.css → /css/style.css
|
|
69
|
+
*/
|
|
70
|
+
staticFiles(publicPath: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Starts the HTTP server on the given port.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* app.listen(3000);
|
|
76
|
+
*
|
|
77
|
+
* @param port - TCP port to listen on
|
|
78
|
+
*/
|
|
79
|
+
listen(port: number): void;
|
|
80
|
+
/**
|
|
81
|
+
* Sets a global prefix for all routes.
|
|
82
|
+
*
|
|
83
|
+
* @param prefix - Route prefix (e.g. "api", "/v1")
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* app.setPrefix("api");
|
|
87
|
+
* app.get("/users", handler); // → /api/users
|
|
88
|
+
*/
|
|
89
|
+
setPrefix(prefix: string): void;
|
|
90
|
+
/** Registers a GET route */
|
|
91
|
+
get(path: string, action: RouteHandler, middlewares?: Middleware[]): void;
|
|
92
|
+
/** Registers a POST route */
|
|
93
|
+
post(path: string, action: RouteHandler, middlewares?: Middleware[]): void;
|
|
94
|
+
/** Registers a PUT route */
|
|
95
|
+
put(path: string, action: RouteHandler, middlewares?: Middleware[]): void;
|
|
96
|
+
/** Registers a PATCH route */
|
|
97
|
+
patch(path: string, action: RouteHandler, middlewares?: Middleware[]): void;
|
|
98
|
+
/** Registers a DELETE route */
|
|
99
|
+
delete(path: string, action: RouteHandler, middlewares?: Middleware[]): void;
|
|
100
|
+
/**
|
|
101
|
+
* Registers global middlewares.
|
|
102
|
+
*
|
|
103
|
+
* These are executed for every route.
|
|
104
|
+
*/
|
|
105
|
+
middlewares(middlewares: Middleware[]): void;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a route group with a shared prefix.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* app.group("/api", (api) => {
|
|
111
|
+
* api.get("/users", listUsers);
|
|
112
|
+
* api.post("/users", createUser);
|
|
113
|
+
* });
|
|
114
|
+
*/
|
|
115
|
+
group(prefix: string, callback: (group: RouterGroup) => void): void;
|
|
116
|
+
/**
|
|
117
|
+
* Translates domain-level exceptions into HTTP responses.
|
|
118
|
+
*
|
|
119
|
+
* This method centralizes error handling and response mapping.
|
|
120
|
+
*/
|
|
121
|
+
private handleError;
|
|
122
|
+
}
|
|
123
|
+
export declare const createApp: () => App;
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createApp = exports.App = void 0;
|
|
4
|
+
const routing_1 = require("./routing");
|
|
5
|
+
const http_1 = require("./http");
|
|
6
|
+
const exceptions_1 = require("./exceptions");
|
|
7
|
+
const nodeNativeServer_1 = require("./server/nodeNativeServer");
|
|
8
|
+
const views_1 = require("./views");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const app_1 = require("./helpers/app");
|
|
11
|
+
const fileStaticHandler_1 = require("./static/fileStaticHandler");
|
|
12
|
+
/**
|
|
13
|
+
* The `App` class acts as the **execution kernel** and **lifecycle orchestrator**
|
|
14
|
+
* of the framework.
|
|
15
|
+
*
|
|
16
|
+
* It is responsible for:
|
|
17
|
+
* - Bootstrapping and exposing the routing system
|
|
18
|
+
* - Receiving normalized HTTP requests through adapters
|
|
19
|
+
* - Resolving the matching route
|
|
20
|
+
* - Executing the associated controller
|
|
21
|
+
* - Dispatching the final response to the client
|
|
22
|
+
*
|
|
23
|
+
* This class implements the **Singleton pattern** to guarantee
|
|
24
|
+
* a single application instance during the process lifecycle.
|
|
25
|
+
*
|
|
26
|
+
* The architecture fully decouples the core framework
|
|
27
|
+
* from the runtime platform (Node, Bun, Deno, etc.)
|
|
28
|
+
* through {@link HttpAdapter} and {@link Server}.
|
|
29
|
+
*/
|
|
30
|
+
class App {
|
|
31
|
+
/** Main routing system */
|
|
32
|
+
router;
|
|
33
|
+
/** Underlying HTTP server implementation */
|
|
34
|
+
server;
|
|
35
|
+
/**
|
|
36
|
+
* View engine used to render templates.
|
|
37
|
+
*
|
|
38
|
+
* Typically consumed inside controllers to generate HTML responses.
|
|
39
|
+
*/
|
|
40
|
+
view;
|
|
41
|
+
/** Static file handler (optional) */
|
|
42
|
+
staticFileHandler = null;
|
|
43
|
+
/**
|
|
44
|
+
* Bootstraps and configures the application.
|
|
45
|
+
*
|
|
46
|
+
* Acts as the **Composition Root** of the framework:
|
|
47
|
+
* this is the only place where concrete infrastructure
|
|
48
|
+
* implementations are instantiated and wired together.
|
|
49
|
+
*
|
|
50
|
+
* @returns The singleton `App` instance
|
|
51
|
+
*/
|
|
52
|
+
static bootstrap() {
|
|
53
|
+
const app = (0, app_1.singleton)(App);
|
|
54
|
+
app.router = new routing_1.Router();
|
|
55
|
+
app.server = new nodeNativeServer_1.NodeServer(app);
|
|
56
|
+
app.view = new views_1.RaptorEngine((0, node_path_1.join)(__dirname, "..", "views"));
|
|
57
|
+
return app;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Main execution pipeline.
|
|
61
|
+
*
|
|
62
|
+
* Execution flow:
|
|
63
|
+
* 1. Retrieves the normalized request from the adapter
|
|
64
|
+
* 2. Attempts to serve a static file (if enabled)
|
|
65
|
+
* 3. Resolves the matching route
|
|
66
|
+
* 4. Executes the controller
|
|
67
|
+
* 5. Sends the response back to the client
|
|
68
|
+
*
|
|
69
|
+
* This method is platform-agnostic.
|
|
70
|
+
*
|
|
71
|
+
* @param adapter - HTTP adapter bridging the runtime with the framework
|
|
72
|
+
*/
|
|
73
|
+
async handle(adapter) {
|
|
74
|
+
try {
|
|
75
|
+
const request = await adapter.getRequest();
|
|
76
|
+
if (this.staticFileHandler && request.getMethod === http_1.HttpMethods.get) {
|
|
77
|
+
const staticResponse = await this.staticFileHandler.tryServeFile(request.getUrl);
|
|
78
|
+
if (staticResponse) {
|
|
79
|
+
adapter.sendResponse(staticResponse);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const response = await this.router.resolve(request);
|
|
84
|
+
adapter.sendResponse(response);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.handleError(error, adapter);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Enables static file serving.
|
|
92
|
+
*
|
|
93
|
+
* @param publicPath - Absolute or relative public directory path
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* app.staticFiles("public");
|
|
97
|
+
* // /public/css/style.css → /css/style.css
|
|
98
|
+
*/
|
|
99
|
+
staticFiles(publicPath) {
|
|
100
|
+
this.staticFileHandler = new fileStaticHandler_1.StaticFileHandler(publicPath);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Starts the HTTP server on the given port.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* app.listen(3000);
|
|
107
|
+
*
|
|
108
|
+
* @param port - TCP port to listen on
|
|
109
|
+
*/
|
|
110
|
+
listen(port) {
|
|
111
|
+
this.server.listen(port);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sets a global prefix for all routes.
|
|
115
|
+
*
|
|
116
|
+
* @param prefix - Route prefix (e.g. "api", "/v1")
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* app.setPrefix("api");
|
|
120
|
+
* app.get("/users", handler); // → /api/users
|
|
121
|
+
*/
|
|
122
|
+
setPrefix(prefix) {
|
|
123
|
+
this.router.setPrefix(prefix);
|
|
124
|
+
}
|
|
125
|
+
/** Registers a GET route */
|
|
126
|
+
get(path, action, middlewares) {
|
|
127
|
+
this.router.get(path, action, middlewares);
|
|
128
|
+
}
|
|
129
|
+
/** Registers a POST route */
|
|
130
|
+
post(path, action, middlewares) {
|
|
131
|
+
this.router.post(path, action, middlewares);
|
|
132
|
+
}
|
|
133
|
+
/** Registers a PUT route */
|
|
134
|
+
put(path, action, middlewares) {
|
|
135
|
+
this.router.put(path, action, middlewares);
|
|
136
|
+
}
|
|
137
|
+
/** Registers a PATCH route */
|
|
138
|
+
patch(path, action, middlewares) {
|
|
139
|
+
this.router.patch(path, action, middlewares);
|
|
140
|
+
}
|
|
141
|
+
/** Registers a DELETE route */
|
|
142
|
+
delete(path, action, middlewares) {
|
|
143
|
+
this.router.delete(path, action, middlewares);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Registers global middlewares.
|
|
147
|
+
*
|
|
148
|
+
* These are executed for every route.
|
|
149
|
+
*/
|
|
150
|
+
middlewares(middlewares) {
|
|
151
|
+
this.router.middlewares(middlewares);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Creates a route group with a shared prefix.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* app.group("/api", (api) => {
|
|
158
|
+
* api.get("/users", listUsers);
|
|
159
|
+
* api.post("/users", createUser);
|
|
160
|
+
* });
|
|
161
|
+
*/
|
|
162
|
+
group(prefix, callback) {
|
|
163
|
+
this.router.group(prefix, callback);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Translates domain-level exceptions into HTTP responses.
|
|
167
|
+
*
|
|
168
|
+
* This method centralizes error handling and response mapping.
|
|
169
|
+
*/
|
|
170
|
+
handleError(error, adapter) {
|
|
171
|
+
if (error instanceof exceptions_1.HttpNotFoundException) {
|
|
172
|
+
adapter.sendResponse(http_1.Response.text("Not Found").setStatus(404));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (error instanceof exceptions_1.ContentParserException) {
|
|
176
|
+
adapter.sendResponse(http_1.Response.json({ message: error.message }).setStatus(422));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (error instanceof exceptions_1.SessionException) {
|
|
180
|
+
adapter.sendResponse(http_1.Response.json({ message: error.message }).setStatus(401));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (error instanceof exceptions_1.ValidationException) {
|
|
184
|
+
adapter.sendResponse(http_1.Response.json({
|
|
185
|
+
success: false,
|
|
186
|
+
errors: error.getErrorsByField(),
|
|
187
|
+
}).setStatus(400));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
adapter.sendResponse(http_1.Response.text("Internal Server Error").setStatus(500));
|
|
191
|
+
console.error(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.App = App;
|
|
195
|
+
const createApp = () => {
|
|
196
|
+
return App.bootstrap();
|
|
197
|
+
};
|
|
198
|
+
exports.createApp = createApp;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Constructor } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Dependency Injection container.
|
|
4
|
+
*
|
|
5
|
+
* Stores and resolves application-wide singletons by constructor reference.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* class App {}
|
|
9
|
+
*
|
|
10
|
+
* // Create (or return) the singleton instance
|
|
11
|
+
* const app = Container.singleton(App);
|
|
12
|
+
*
|
|
13
|
+
* // Resolve an already-registered singleton
|
|
14
|
+
* const resolved = Container.resolve(App);
|
|
15
|
+
*/
|
|
16
|
+
export declare class Container {
|
|
17
|
+
/**
|
|
18
|
+
* Singleton instances indexed by class constructor.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
private static instances;
|
|
23
|
+
/**
|
|
24
|
+
* Get or create a singleton instance for the given class.
|
|
25
|
+
*
|
|
26
|
+
* If no instance exists yet, the container will instantiate the class with
|
|
27
|
+
* `new classConstructor()` (no constructor arguments) and cache it.
|
|
28
|
+
*
|
|
29
|
+
* @typeParam T - Instance type produced by the constructor
|
|
30
|
+
* @param classConstructor - Class constructor used as the registration key
|
|
31
|
+
* @returns The singleton instance for the given class
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* class Database {
|
|
35
|
+
* connect() {
|
|
36
|
+
* console.log("Connected");
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* const db1 = Container.singleton(Database);
|
|
41
|
+
* const db2 = Container.singleton(Database);
|
|
42
|
+
* console.log(db1 === db2); // true
|
|
43
|
+
*/
|
|
44
|
+
static singleton<T>(classConstructor: Constructor<T>): T;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a previously created singleton instance.
|
|
47
|
+
*
|
|
48
|
+
* This method does not create instances. If the class was never registered
|
|
49
|
+
* via {@link Container.singleton}, it returns `null`.
|
|
50
|
+
*
|
|
51
|
+
* @typeParam T - Instance type produced by the constructor
|
|
52
|
+
* @param classConstructor - Class constructor used as the registration key
|
|
53
|
+
* @returns The singleton instance, or `null` if it is not registered
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* const db = Container.resolve(Database);
|
|
57
|
+
* if (db) db.connect();
|
|
58
|
+
*/
|
|
59
|
+
static resolve<T>(classConstructor: Constructor<T>): T | null;
|
|
60
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Container = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Dependency Injection container.
|
|
6
|
+
*
|
|
7
|
+
* Stores and resolves application-wide singletons by constructor reference.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* class App {}
|
|
11
|
+
*
|
|
12
|
+
* // Create (or return) the singleton instance
|
|
13
|
+
* const app = Container.singleton(App);
|
|
14
|
+
*
|
|
15
|
+
* // Resolve an already-registered singleton
|
|
16
|
+
* const resolved = Container.resolve(App);
|
|
17
|
+
*/
|
|
18
|
+
class Container {
|
|
19
|
+
/**
|
|
20
|
+
* Singleton instances indexed by class constructor.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
static instances = new Map();
|
|
25
|
+
/**
|
|
26
|
+
* Get or create a singleton instance for the given class.
|
|
27
|
+
*
|
|
28
|
+
* If no instance exists yet, the container will instantiate the class with
|
|
29
|
+
* `new classConstructor()` (no constructor arguments) and cache it.
|
|
30
|
+
*
|
|
31
|
+
* @typeParam T - Instance type produced by the constructor
|
|
32
|
+
* @param classConstructor - Class constructor used as the registration key
|
|
33
|
+
* @returns The singleton instance for the given class
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* class Database {
|
|
37
|
+
* connect() {
|
|
38
|
+
* console.log("Connected");
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* const db1 = Container.singleton(Database);
|
|
43
|
+
* const db2 = Container.singleton(Database);
|
|
44
|
+
* console.log(db1 === db2); // true
|
|
45
|
+
*/
|
|
46
|
+
static singleton(classConstructor) {
|
|
47
|
+
if (!this.instances.has(classConstructor)) {
|
|
48
|
+
const instance = new classConstructor();
|
|
49
|
+
this.instances.set(classConstructor, instance);
|
|
50
|
+
}
|
|
51
|
+
return this.instances.get(classConstructor);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a previously created singleton instance.
|
|
55
|
+
*
|
|
56
|
+
* This method does not create instances. If the class was never registered
|
|
57
|
+
* via {@link Container.singleton}, it returns `null`.
|
|
58
|
+
*
|
|
59
|
+
* @typeParam T - Instance type produced by the constructor
|
|
60
|
+
* @param classConstructor - Class constructor used as the registration key
|
|
61
|
+
* @returns The singleton instance, or `null` if it is not registered
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* const db = Container.resolve(Database);
|
|
65
|
+
* if (db) db.connect();
|
|
66
|
+
*/
|
|
67
|
+
static resolve(classConstructor) {
|
|
68
|
+
return this.instances.get(classConstructor) ?? null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.Container = Container;
|