zero-http 0.2.5 → 0.3.1
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 +1250 -283
- package/documentation/config/db.js +25 -0
- package/documentation/config/middleware.js +44 -0
- package/documentation/config/tls.js +12 -0
- package/documentation/controllers/cookies.js +34 -0
- package/documentation/controllers/tasks.js +108 -0
- package/documentation/full-server.js +25 -184
- package/documentation/models/Task.js +21 -0
- package/documentation/public/data/api.json +404 -24
- package/documentation/public/data/docs.json +1139 -0
- package/documentation/public/data/examples.json +80 -2
- package/documentation/public/data/options.json +23 -8
- package/documentation/public/index.html +138 -99
- package/documentation/public/scripts/app.js +1 -3
- package/documentation/public/scripts/custom-select.js +189 -0
- package/documentation/public/scripts/data-sections.js +233 -250
- package/documentation/public/scripts/playground.js +270 -0
- package/documentation/public/scripts/ui.js +4 -3
- package/documentation/public/styles.css +56 -5
- package/documentation/public/vendor/icons/compress.svg +17 -17
- package/documentation/public/vendor/icons/database.svg +21 -0
- package/documentation/public/vendor/icons/env.svg +21 -0
- package/documentation/public/vendor/icons/fetch.svg +11 -14
- package/documentation/public/vendor/icons/security.svg +15 -0
- package/documentation/public/vendor/icons/sse.svg +12 -13
- package/documentation/public/vendor/icons/static.svg +12 -26
- package/documentation/public/vendor/icons/stream.svg +7 -13
- package/documentation/public/vendor/icons/validate.svg +17 -0
- package/documentation/routes/api.js +41 -0
- package/documentation/routes/core.js +20 -0
- package/documentation/routes/playground.js +29 -0
- package/documentation/routes/realtime.js +49 -0
- package/documentation/routes/uploads.js +71 -0
- package/index.js +62 -1
- package/lib/app.js +200 -8
- package/lib/body/json.js +28 -5
- package/lib/body/multipart.js +29 -1
- package/lib/body/raw.js +1 -1
- package/lib/body/sendError.js +1 -0
- package/lib/body/text.js +1 -1
- package/lib/body/typeMatch.js +6 -2
- package/lib/body/urlencoded.js +5 -2
- package/lib/debug.js +345 -0
- package/lib/env/index.js +440 -0
- package/lib/errors.js +231 -0
- package/lib/http/request.js +219 -1
- package/lib/http/response.js +410 -6
- package/lib/middleware/compress.js +39 -6
- package/lib/middleware/cookieParser.js +237 -0
- package/lib/middleware/cors.js +13 -2
- package/lib/middleware/csrf.js +135 -0
- package/lib/middleware/errorHandler.js +90 -0
- package/lib/middleware/helmet.js +176 -0
- package/lib/middleware/index.js +7 -2
- package/lib/middleware/rateLimit.js +12 -1
- package/lib/middleware/requestId.js +54 -0
- package/lib/middleware/static.js +95 -11
- package/lib/middleware/timeout.js +72 -0
- package/lib/middleware/validator.js +257 -0
- package/lib/orm/adapters/json.js +215 -0
- package/lib/orm/adapters/memory.js +383 -0
- package/lib/orm/adapters/mongo.js +444 -0
- package/lib/orm/adapters/mysql.js +272 -0
- package/lib/orm/adapters/postgres.js +394 -0
- package/lib/orm/adapters/sql-base.js +142 -0
- package/lib/orm/adapters/sqlite.js +311 -0
- package/lib/orm/index.js +276 -0
- package/lib/orm/model.js +895 -0
- package/lib/orm/query.js +807 -0
- package/lib/orm/schema.js +172 -0
- package/lib/router/index.js +136 -47
- package/lib/sse/stream.js +15 -3
- package/lib/ws/connection.js +19 -3
- package/lib/ws/handshake.js +3 -0
- package/lib/ws/index.js +3 -1
- package/lib/ws/room.js +222 -0
- package/package.json +15 -5
- package/types/app.d.ts +120 -0
- package/types/env.d.ts +80 -0
- package/types/errors.d.ts +147 -0
- package/types/fetch.d.ts +43 -0
- package/types/index.d.ts +135 -0
- package/types/middleware.d.ts +292 -0
- package/types/orm.d.ts +610 -0
- package/types/request.d.ts +99 -0
- package/types/response.d.ts +142 -0
- package/types/router.d.ts +78 -0
- package/types/sse.d.ts +78 -0
- package/types/websocket.d.ts +119 -0
package/lib/ws/room.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ws/room
|
|
3
|
+
* @description WebSocket room/channel manager.
|
|
4
|
+
* Provides broadcast, room-based messaging, and connection
|
|
5
|
+
* registry for WebSocket connections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages a pool of WebSocket connections with room-based grouping.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const pool = new WebSocketPool();
|
|
13
|
+
* app.ws('/chat', (ws, req) => {
|
|
14
|
+
* pool.add(ws);
|
|
15
|
+
* pool.join(ws, 'general');
|
|
16
|
+
* ws.on('message', msg => pool.toRoom('general', msg));
|
|
17
|
+
* ws.on('close', () => pool.remove(ws));
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
class WebSocketPool
|
|
21
|
+
{
|
|
22
|
+
constructor()
|
|
23
|
+
{
|
|
24
|
+
/** @type {Set<import('./connection')>} All active connections. */
|
|
25
|
+
this._connections = new Set();
|
|
26
|
+
/** @type {Map<string, Set<import('./connection')>>} Room → connection sets. */
|
|
27
|
+
this._rooms = new Map();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Add a connection to the pool.
|
|
32
|
+
* @param {import('./connection')} ws
|
|
33
|
+
* @returns {WebSocketPool} this
|
|
34
|
+
*/
|
|
35
|
+
add(ws)
|
|
36
|
+
{
|
|
37
|
+
this._connections.add(ws);
|
|
38
|
+
|
|
39
|
+
// Auto-remove on close
|
|
40
|
+
ws.once('close', () => this.remove(ws));
|
|
41
|
+
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Remove a connection from the pool and all rooms.
|
|
47
|
+
* @param {import('./connection')} ws
|
|
48
|
+
* @returns {WebSocketPool} this
|
|
49
|
+
*/
|
|
50
|
+
remove(ws)
|
|
51
|
+
{
|
|
52
|
+
this._connections.delete(ws);
|
|
53
|
+
for (const [room, members] of this._rooms)
|
|
54
|
+
{
|
|
55
|
+
members.delete(ws);
|
|
56
|
+
if (members.size === 0) this._rooms.delete(room);
|
|
57
|
+
}
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Join a connection to a room.
|
|
63
|
+
* @param {import('./connection')} ws
|
|
64
|
+
* @param {string} room - Room name.
|
|
65
|
+
* @returns {WebSocketPool} this
|
|
66
|
+
*/
|
|
67
|
+
join(ws, room)
|
|
68
|
+
{
|
|
69
|
+
if (!this._rooms.has(room)) this._rooms.set(room, new Set());
|
|
70
|
+
this._rooms.get(room).add(ws);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Remove a connection from a room.
|
|
76
|
+
* @param {import('./connection')} ws
|
|
77
|
+
* @param {string} room - Room name.
|
|
78
|
+
* @returns {WebSocketPool} this
|
|
79
|
+
*/
|
|
80
|
+
leave(ws, room)
|
|
81
|
+
{
|
|
82
|
+
const members = this._rooms.get(room);
|
|
83
|
+
if (members)
|
|
84
|
+
{
|
|
85
|
+
members.delete(ws);
|
|
86
|
+
if (members.size === 0) this._rooms.delete(room);
|
|
87
|
+
}
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get all rooms a connection belongs to.
|
|
93
|
+
* @param {import('./connection')} ws
|
|
94
|
+
* @returns {string[]}
|
|
95
|
+
*/
|
|
96
|
+
roomsOf(ws)
|
|
97
|
+
{
|
|
98
|
+
const result = [];
|
|
99
|
+
for (const [room, members] of this._rooms)
|
|
100
|
+
{
|
|
101
|
+
if (members.has(ws)) result.push(room);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Broadcast a message to ALL connected clients.
|
|
108
|
+
* @param {string|Buffer} data - Payload.
|
|
109
|
+
* @param {import('./connection')} [exclude] - Optional connection to exclude (e.g. the sender).
|
|
110
|
+
*/
|
|
111
|
+
broadcast(data, exclude)
|
|
112
|
+
{
|
|
113
|
+
for (const ws of this._connections)
|
|
114
|
+
{
|
|
115
|
+
if (ws !== exclude && ws.readyState === 1) ws.send(data);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Broadcast a JSON message to ALL connected clients.
|
|
121
|
+
* @param {*} obj - Value to serialise.
|
|
122
|
+
* @param {import('./connection')} [exclude]
|
|
123
|
+
*/
|
|
124
|
+
broadcastJSON(obj, exclude)
|
|
125
|
+
{
|
|
126
|
+
const msg = JSON.stringify(obj);
|
|
127
|
+
this.broadcast(msg, exclude);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Send a message to all connections in a specific room.
|
|
132
|
+
* @param {string} room - Room name.
|
|
133
|
+
* @param {string|Buffer} data - Payload.
|
|
134
|
+
* @param {import('./connection')} [exclude]
|
|
135
|
+
*/
|
|
136
|
+
toRoom(room, data, exclude)
|
|
137
|
+
{
|
|
138
|
+
const members = this._rooms.get(room);
|
|
139
|
+
if (!members) return;
|
|
140
|
+
for (const ws of members)
|
|
141
|
+
{
|
|
142
|
+
if (ws !== exclude && ws.readyState === 1) ws.send(data);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Send a JSON message to all connections in a specific room.
|
|
148
|
+
* @param {string} room
|
|
149
|
+
* @param {*} obj
|
|
150
|
+
* @param {import('./connection')} [exclude]
|
|
151
|
+
*/
|
|
152
|
+
toRoomJSON(room, obj, exclude)
|
|
153
|
+
{
|
|
154
|
+
this.toRoom(room, JSON.stringify(obj), exclude);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get all connections in a room.
|
|
159
|
+
* @param {string} room
|
|
160
|
+
* @returns {import('./connection')[]}
|
|
161
|
+
*/
|
|
162
|
+
in(room)
|
|
163
|
+
{
|
|
164
|
+
const members = this._rooms.get(room);
|
|
165
|
+
return members ? Array.from(members) : [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Total number of active connections.
|
|
170
|
+
* @type {number}
|
|
171
|
+
*/
|
|
172
|
+
get size()
|
|
173
|
+
{
|
|
174
|
+
return this._connections.size;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Number of connections in a specific room.
|
|
179
|
+
* @param {string} room
|
|
180
|
+
* @returns {number}
|
|
181
|
+
*/
|
|
182
|
+
roomSize(room)
|
|
183
|
+
{
|
|
184
|
+
const members = this._rooms.get(room);
|
|
185
|
+
return members ? members.size : 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* List all active room names.
|
|
190
|
+
* @returns {string[]}
|
|
191
|
+
*/
|
|
192
|
+
get rooms()
|
|
193
|
+
{
|
|
194
|
+
return Array.from(this._rooms.keys());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get all active connections.
|
|
199
|
+
* @returns {import('./connection')[]}
|
|
200
|
+
*/
|
|
201
|
+
get clients()
|
|
202
|
+
{
|
|
203
|
+
return Array.from(this._connections);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Close all connections gracefully.
|
|
208
|
+
* @param {number} [code=1001] - Close code.
|
|
209
|
+
* @param {string} [reason] - Close reason.
|
|
210
|
+
*/
|
|
211
|
+
closeAll(code = 1001, reason = 'Server shutdown')
|
|
212
|
+
{
|
|
213
|
+
for (const ws of this._connections)
|
|
214
|
+
{
|
|
215
|
+
ws.close(code, reason);
|
|
216
|
+
}
|
|
217
|
+
this._connections.clear();
|
|
218
|
+
this._rooms.clear();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = WebSocketPool;
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-http",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Zero-dependency,
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Zero-dependency backend framework with routing, ORM, WebSocket, SSE, security middleware, and more",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "types/index.d.ts",
|
|
6
7
|
"files": [
|
|
7
8
|
"lib",
|
|
9
|
+
"types",
|
|
8
10
|
"documentation",
|
|
9
11
|
"index.js",
|
|
10
12
|
"README.md",
|
|
@@ -13,7 +15,8 @@
|
|
|
13
15
|
"scripts": {
|
|
14
16
|
"start": "node documentation/full-server.js",
|
|
15
17
|
"docs": "node documentation/full-server.js",
|
|
16
|
-
"test": "
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest"
|
|
17
20
|
},
|
|
18
21
|
"keywords": [
|
|
19
22
|
"http",
|
|
@@ -29,10 +32,10 @@
|
|
|
29
32
|
"license": "MIT",
|
|
30
33
|
"repository": {
|
|
31
34
|
"type": "git",
|
|
32
|
-
"url": "git+https://github.com/tonywied17/zero-http
|
|
35
|
+
"url": "git+https://github.com/tonywied17/zero-http.git"
|
|
33
36
|
},
|
|
34
37
|
"bugs": {
|
|
35
|
-
"url": "https://github.com/tonywied17/zero-http
|
|
38
|
+
"url": "https://github.com/tonywied17/zero-http/issues"
|
|
36
39
|
},
|
|
37
40
|
"homepage": "https://z-http.com",
|
|
38
41
|
"engines": {
|
|
@@ -40,5 +43,12 @@
|
|
|
40
43
|
},
|
|
41
44
|
"publishConfig": {
|
|
42
45
|
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"better-sqlite3": "^12.8.0",
|
|
49
|
+
"mongodb": "^7.1.1",
|
|
50
|
+
"mysql2": "^3.20.0",
|
|
51
|
+
"pg": "^8.20.0",
|
|
52
|
+
"vitest": "^4.1.2"
|
|
43
53
|
}
|
|
44
54
|
}
|
package/types/app.d.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { Server as HttpServer } from 'http';
|
|
4
|
+
import { Server as HttpsServer, ServerOptions as TlsOptions } from 'https';
|
|
5
|
+
import { Request } from './request';
|
|
6
|
+
import { Response } from './response';
|
|
7
|
+
import { RouterInstance, RouteChain, RouteInfo, RouteOptions, RouteHandler } from './router';
|
|
8
|
+
import { MiddlewareFunction, ErrorHandlerFunction, NextFunction } from './middleware';
|
|
9
|
+
import { WebSocketHandler, WebSocketOptions } from './websocket';
|
|
10
|
+
|
|
11
|
+
export interface App {
|
|
12
|
+
/** Internal router instance. */
|
|
13
|
+
router: RouterInstance;
|
|
14
|
+
/** Middleware stack. */
|
|
15
|
+
middlewares: MiddlewareFunction[];
|
|
16
|
+
/** Application-level locals, merged into every request/response locals. */
|
|
17
|
+
locals: Record<string, any>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register middleware or mount a sub-router.
|
|
21
|
+
*/
|
|
22
|
+
use(fn: MiddlewareFunction): App;
|
|
23
|
+
use(path: string, fn: MiddlewareFunction): App;
|
|
24
|
+
use(path: string, router: RouterInstance): App;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register a global error handler.
|
|
28
|
+
*/
|
|
29
|
+
onError(fn: ErrorHandlerFunction): void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Core request handler for use with `http.createServer()`.
|
|
33
|
+
*/
|
|
34
|
+
handler(req: import('http').IncomingMessage, res: import('http').ServerResponse): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Start listening for connections.
|
|
38
|
+
*/
|
|
39
|
+
listen(port?: number, cb?: () => void): HttpServer;
|
|
40
|
+
listen(port: number, opts: TlsOptions, cb?: () => void): HttpsServer;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Gracefully close the server.
|
|
44
|
+
*/
|
|
45
|
+
close(cb?: (err?: Error) => void): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register a WebSocket upgrade handler.
|
|
49
|
+
*/
|
|
50
|
+
ws(path: string, handler: WebSocketHandler): void;
|
|
51
|
+
ws(path: string, opts: WebSocketOptions, handler: WebSocketHandler): void;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Return a flat list of all registered routes.
|
|
55
|
+
*/
|
|
56
|
+
routes(): RouteInfo[];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Register a route with a specific HTTP method.
|
|
60
|
+
*/
|
|
61
|
+
route(method: string, path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get a setting value (1 arg) or set a setting value (2 args).
|
|
65
|
+
*/
|
|
66
|
+
set(key: string): any;
|
|
67
|
+
set(key: string, val: any): App;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get a setting value, or register a GET route.
|
|
71
|
+
* With 1 string arg: returns the setting value.
|
|
72
|
+
* With path + handlers: registers a GET route.
|
|
73
|
+
*/
|
|
74
|
+
get(key: string): any;
|
|
75
|
+
get(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Enable a boolean setting (set to `true`).
|
|
79
|
+
*/
|
|
80
|
+
enable(key: string): App;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Disable a boolean setting (set to `false`).
|
|
84
|
+
*/
|
|
85
|
+
disable(key: string): App;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if a setting is truthy.
|
|
89
|
+
*/
|
|
90
|
+
enabled(key: string): boolean;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if a setting is falsy.
|
|
94
|
+
*/
|
|
95
|
+
disabled(key: string): boolean;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Register a parameter pre-processing handler.
|
|
99
|
+
*/
|
|
100
|
+
param(name: string, fn: (req: Request, res: Response, next: NextFunction, value: string) => void): App;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create a route group under a prefix with shared middleware.
|
|
104
|
+
*/
|
|
105
|
+
group(prefix: string, ...args: [...MiddlewareFunction[], (router: RouterInstance) => void]): App;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create a chainable route builder for the given path.
|
|
109
|
+
*/
|
|
110
|
+
chain(path: string): RouteChain;
|
|
111
|
+
|
|
112
|
+
// HTTP method shortcuts
|
|
113
|
+
post(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
114
|
+
put(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
115
|
+
delete(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
116
|
+
patch(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
117
|
+
options(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
118
|
+
head(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
119
|
+
all(path: string, ...handlers: (RouteOptions | RouteHandler)[]): App;
|
|
120
|
+
}
|
package/types/env.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// --- Env Field Definition ----------------------------------------
|
|
2
|
+
|
|
3
|
+
export interface EnvFieldDef {
|
|
4
|
+
/** Value type for coercion. */
|
|
5
|
+
type?: 'string' | 'number' | 'integer' | 'boolean' | 'port' | 'array' | 'json' | 'url' | 'enum';
|
|
6
|
+
/** Field is required. */
|
|
7
|
+
required?: boolean;
|
|
8
|
+
/** Default value (or function returning a value). */
|
|
9
|
+
default?: any | (() => any);
|
|
10
|
+
/** Min value (number/integer) or min length (string). */
|
|
11
|
+
min?: number;
|
|
12
|
+
/** Max value (number/integer) or max length (string). */
|
|
13
|
+
max?: number;
|
|
14
|
+
/** Pattern match (string type). */
|
|
15
|
+
match?: RegExp;
|
|
16
|
+
/** Delimiter for array type. Default: ','. */
|
|
17
|
+
separator?: string;
|
|
18
|
+
/** Allowed values for enum type. */
|
|
19
|
+
values?: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type EnvSchema = Record<string, EnvFieldDef>;
|
|
23
|
+
|
|
24
|
+
export interface EnvLoadOptions {
|
|
25
|
+
/** Directory to load .env files from. Default: `process.cwd()`. */
|
|
26
|
+
path?: string;
|
|
27
|
+
/** Write file values into `process.env`. Default: false. */
|
|
28
|
+
override?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// --- Env Proxy ---------------------------------------------------
|
|
32
|
+
|
|
33
|
+
export interface Env {
|
|
34
|
+
/**
|
|
35
|
+
* Get a variable by key.
|
|
36
|
+
*/
|
|
37
|
+
(key: string): any;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load environment variables, optionally with a typed schema.
|
|
41
|
+
*/
|
|
42
|
+
load(schema?: EnvSchema, options?: EnvLoadOptions): Record<string, any>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get a variable by key.
|
|
46
|
+
*/
|
|
47
|
+
get(key: string): any;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get a variable by key, throwing if missing.
|
|
51
|
+
*/
|
|
52
|
+
require(key: string): any;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a variable exists (in store or `process.env`).
|
|
56
|
+
*/
|
|
57
|
+
has(key: string): boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return all loaded variables.
|
|
61
|
+
*/
|
|
62
|
+
all(): Record<string, any>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clear all loaded variables from the internal store.
|
|
66
|
+
*/
|
|
67
|
+
reset(): void;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parse a raw `.env` string into key-value pairs.
|
|
71
|
+
*/
|
|
72
|
+
parse(src: string): Record<string, string>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Property access for variables (e.g., `env.PORT`).
|
|
76
|
+
*/
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const env: Env;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// --- Error Classes -----------------------------------------------
|
|
2
|
+
|
|
3
|
+
export interface HttpErrorOptions {
|
|
4
|
+
code?: string;
|
|
5
|
+
details?: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class HttpError extends Error {
|
|
9
|
+
readonly statusCode: number;
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
details?: any;
|
|
13
|
+
constructor(statusCode: number, message?: string, opts?: HttpErrorOptions);
|
|
14
|
+
toJSON(): { error: string; code: string; statusCode: number; details?: any };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class BadRequestError extends HttpError {
|
|
18
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class UnauthorizedError extends HttpError {
|
|
22
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ForbiddenError extends HttpError {
|
|
26
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class NotFoundError extends HttpError {
|
|
30
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class MethodNotAllowedError extends HttpError {
|
|
34
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class ConflictError extends HttpError {
|
|
38
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class GoneError extends HttpError {
|
|
42
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class PayloadTooLargeError extends HttpError {
|
|
46
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class UnprocessableEntityError extends HttpError {
|
|
50
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class ValidationError extends HttpError {
|
|
54
|
+
readonly errors: Record<string, string> | string[];
|
|
55
|
+
constructor(message?: string, errors?: Record<string, string> | string[], opts?: HttpErrorOptions);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class TooManyRequestsError extends HttpError {
|
|
59
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class InternalError extends HttpError {
|
|
63
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class NotImplementedError extends HttpError {
|
|
67
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class BadGatewayError extends HttpError {
|
|
71
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class ServiceUnavailableError extends HttpError {
|
|
75
|
+
constructor(message?: string, opts?: HttpErrorOptions);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createError(statusCode: number, message?: string, opts?: HttpErrorOptions): HttpError;
|
|
79
|
+
export function isHttpError(err: any): err is HttpError;
|
|
80
|
+
|
|
81
|
+
// --- Error Handler Middleware ------------------------------------
|
|
82
|
+
|
|
83
|
+
import { Request } from './request';
|
|
84
|
+
import { Response } from './response';
|
|
85
|
+
|
|
86
|
+
export interface ErrorHandlerOptions {
|
|
87
|
+
/** Include stack traces in responses (default: true when NODE_ENV !== 'production'). */
|
|
88
|
+
stack?: boolean;
|
|
89
|
+
/** Log errors to console (default: true). */
|
|
90
|
+
log?: boolean;
|
|
91
|
+
/** Custom log function (default: console.error). */
|
|
92
|
+
logger?: (...args: any[]) => void;
|
|
93
|
+
/** Custom response formatter: (err, req, isDev) => responseBody. */
|
|
94
|
+
formatter?: (err: Error, req: Request, isDev: boolean) => any;
|
|
95
|
+
/** Callback on every error. */
|
|
96
|
+
onError?: (err: Error, req: Request, res: Response) => void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function errorHandler(opts?: ErrorHandlerOptions): (err: any, req: Request, res: Response, next: (err?: any) => void) => void;
|
|
100
|
+
|
|
101
|
+
// --- Debug Logger ------------------------------------------------
|
|
102
|
+
|
|
103
|
+
export interface DebugLogger {
|
|
104
|
+
(...args: any[]): void;
|
|
105
|
+
trace(...args: any[]): void;
|
|
106
|
+
debug(...args: any[]): void;
|
|
107
|
+
info(...args: any[]): void;
|
|
108
|
+
warn(...args: any[]): void;
|
|
109
|
+
error(...args: any[]): void;
|
|
110
|
+
fatal(...args: any[]): void;
|
|
111
|
+
readonly enabled: boolean;
|
|
112
|
+
readonly namespace: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DebugLevels {
|
|
116
|
+
trace: 0;
|
|
117
|
+
debug: 1;
|
|
118
|
+
info: 2;
|
|
119
|
+
warn: 3;
|
|
120
|
+
error: 4;
|
|
121
|
+
fatal: 5;
|
|
122
|
+
silent: 6;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface Debug {
|
|
126
|
+
(namespace: string): DebugLogger;
|
|
127
|
+
/** Set minimum log level globally. */
|
|
128
|
+
level(level: keyof DebugLevels | number): void;
|
|
129
|
+
/** Enable/disable namespaces (same syntax as DEBUG env var). */
|
|
130
|
+
enable(patterns: string): void;
|
|
131
|
+
/** Disable all debug output. */
|
|
132
|
+
disable(): void;
|
|
133
|
+
/** Enable structured JSON output. */
|
|
134
|
+
json(on?: boolean): void;
|
|
135
|
+
/** Enable/disable timestamps. */
|
|
136
|
+
timestamps(on?: boolean): void;
|
|
137
|
+
/** Enable/disable colors. */
|
|
138
|
+
colors(on?: boolean): void;
|
|
139
|
+
/** Set custom output stream. */
|
|
140
|
+
output(stream: { write(s: string): void }): void;
|
|
141
|
+
/** Reset all settings to defaults. */
|
|
142
|
+
reset(): void;
|
|
143
|
+
/** Level constants. */
|
|
144
|
+
readonly LEVELS: DebugLevels;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const debug: Debug;
|
package/types/fetch.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
|
|
3
|
+
export interface FetchOptions {
|
|
4
|
+
method?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
body?: string | Buffer | object | Readable | URLSearchParams | ArrayBuffer | Uint8Array;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
agent?: any;
|
|
10
|
+
onDownloadProgress?: (progress: { loaded: number; total: number | null }) => void;
|
|
11
|
+
onUploadProgress?: (progress: { loaded: number; total: number | null }) => void;
|
|
12
|
+
// TLS options
|
|
13
|
+
rejectUnauthorized?: boolean;
|
|
14
|
+
ca?: string | Buffer | Array<string | Buffer>;
|
|
15
|
+
cert?: string | Buffer;
|
|
16
|
+
key?: string | Buffer;
|
|
17
|
+
pfx?: string | Buffer;
|
|
18
|
+
passphrase?: string;
|
|
19
|
+
servername?: string;
|
|
20
|
+
ciphers?: string;
|
|
21
|
+
secureProtocol?: string;
|
|
22
|
+
minVersion?: string;
|
|
23
|
+
maxVersion?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FetchHeaders {
|
|
27
|
+
get(name: string): string | undefined;
|
|
28
|
+
raw: Record<string, string | string[]>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FetchResponse {
|
|
32
|
+
status: number;
|
|
33
|
+
statusText: string;
|
|
34
|
+
ok: boolean;
|
|
35
|
+
secure: boolean;
|
|
36
|
+
url: string;
|
|
37
|
+
headers: FetchHeaders;
|
|
38
|
+
arrayBuffer(): Promise<Buffer>;
|
|
39
|
+
text(): Promise<string>;
|
|
40
|
+
json(): Promise<any>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function fetch(url: string, opts?: FetchOptions): Promise<FetchResponse>;
|