cpeak 2.4.3 → 2.6.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/README.md +265 -39
- package/dist/index.d.ts +75 -16
- package/dist/index.js +515 -169
- package/dist/index.js.map +1 -1
- package/lib/index.ts +262 -217
- package/lib/types.ts +12 -10
- package/lib/utils/auth.ts +170 -0
- package/lib/utils/cookieParser.ts +189 -0
- package/lib/utils/index.ts +16 -1
- package/lib/utils/parseJSON.ts +77 -24
- package/lib/utils/serveStatic.ts +16 -5
- package/lib/utils/swagger.ts +31 -0
- package/package.json +1 -1
package/lib/index.ts
CHANGED
|
@@ -10,19 +10,26 @@ import type {
|
|
|
10
10
|
Middleware,
|
|
11
11
|
RouteMiddleware,
|
|
12
12
|
Handler,
|
|
13
|
-
RoutesMap
|
|
13
|
+
RoutesMap
|
|
14
14
|
} from "./types";
|
|
15
15
|
|
|
16
16
|
// A utility function to create an error with a custom stack trace
|
|
17
17
|
export function frameworkError(
|
|
18
18
|
message: string,
|
|
19
19
|
skipFn: Function,
|
|
20
|
-
code?: string
|
|
20
|
+
code?: string,
|
|
21
|
+
status?: number
|
|
21
22
|
) {
|
|
22
|
-
const err = new Error(message) as Error & {
|
|
23
|
+
const err = new Error(message) as Error & {
|
|
24
|
+
code?: string;
|
|
25
|
+
cpeak_err?: boolean;
|
|
26
|
+
};
|
|
23
27
|
Error.captureStackTrace(err, skipFn);
|
|
24
28
|
|
|
29
|
+
err.cpeak_err = true;
|
|
30
|
+
|
|
25
31
|
if (code) err.code = code;
|
|
32
|
+
if (status) (err as any).status = status;
|
|
26
33
|
|
|
27
34
|
return err;
|
|
28
35
|
}
|
|
@@ -32,207 +39,241 @@ export enum ErrorCode {
|
|
|
32
39
|
FILE_NOT_FOUND = "CPEAK_ERR_FILE_NOT_FOUND",
|
|
33
40
|
NOT_A_FILE = "CPEAK_ERR_NOT_A_FILE",
|
|
34
41
|
SEND_FILE_FAIL = "CPEAK_ERR_SEND_FILE_FAIL",
|
|
42
|
+
INVALID_JSON = "CPEAK_ERR_INVALID_JSON",
|
|
43
|
+
PAYLOAD_TOO_LARGE = "CPEAK_ERR_PAYLOAD_TOO_LARGE",
|
|
44
|
+
WEAK_SECRET = "CPEAK_ERR_WEAK_SECRET"
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
class
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
private _handleErr?: (
|
|
42
|
-
err: unknown,
|
|
43
|
-
req: CpeakRequest,
|
|
44
|
-
res: CpeakResponse
|
|
45
|
-
) => void;
|
|
47
|
+
export class CpeakIncomingMessage extends http.IncomingMessage {
|
|
48
|
+
// We define body and params here for better V8 optimization (not changing the shape of the object at runtime)
|
|
49
|
+
public body: any = undefined;
|
|
50
|
+
public params: StringMap = {};
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
this.server = http.createServer();
|
|
49
|
-
this.routes = {};
|
|
50
|
-
this.middleware = [];
|
|
51
|
-
|
|
52
|
-
this.server.on("request", (req: CpeakRequest, res: CpeakResponse) => {
|
|
53
|
-
// Send a file back to the client
|
|
54
|
-
res.sendFile = async (path: string, mime: string) => {
|
|
55
|
-
if (!mime) {
|
|
56
|
-
throw frameworkError(
|
|
57
|
-
'MIME type is missing. Use res.sendFile(path, "mime-type").',
|
|
58
|
-
res.sendFile,
|
|
59
|
-
ErrorCode.MISSING_MIME
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const stat = await fs.stat(path);
|
|
65
|
-
if (!stat.isFile()) {
|
|
66
|
-
throw frameworkError(
|
|
67
|
-
`Not a file: ${path}`,
|
|
68
|
-
res.sendFile,
|
|
69
|
-
ErrorCode.NOT_A_FILE
|
|
70
|
-
);
|
|
71
|
-
}
|
|
52
|
+
#query?: StringMap;
|
|
72
53
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (err?.code === "ENOENT") {
|
|
80
|
-
throw frameworkError(
|
|
81
|
-
`File not found: ${path}`,
|
|
82
|
-
res.sendFile,
|
|
83
|
-
ErrorCode.FILE_NOT_FOUND
|
|
84
|
-
);
|
|
85
|
-
}
|
|
54
|
+
// Parse the URL parameters (like /users?key1=value1&key2=value2)
|
|
55
|
+
// We will call this query to be more familiar with other node.js frameworks.
|
|
56
|
+
// This is a getter method (accessed like a property)
|
|
57
|
+
get query(): StringMap {
|
|
58
|
+
// This way if a developer writes req.query multiple times, we don't parse it multiple times
|
|
59
|
+
if (this.#query) return this.#query;
|
|
86
60
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
61
|
+
const url = this.url || "";
|
|
62
|
+
const qIndex = url.indexOf("?");
|
|
63
|
+
|
|
64
|
+
if (qIndex === -1) {
|
|
65
|
+
this.#query = {};
|
|
66
|
+
} else {
|
|
67
|
+
const searchParams = new URLSearchParams(url.substring(qIndex + 1));
|
|
68
|
+
this.#query = Object.fromEntries(searchParams.entries());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return this.#query;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessage> {
|
|
76
|
+
// Send a file back to the client
|
|
77
|
+
async sendFile(path: string, mime: string) {
|
|
78
|
+
if (!mime) {
|
|
79
|
+
throw frameworkError(
|
|
80
|
+
'MIME type is missing. Use res.sendFile(path, "mime-type").',
|
|
81
|
+
this.sendFile,
|
|
82
|
+
ErrorCode.MISSING_MIME
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const stat = await fs.stat(path);
|
|
88
|
+
if (!stat.isFile()) {
|
|
89
|
+
throw frameworkError(
|
|
90
|
+
`Not a file: ${path}`,
|
|
91
|
+
this.sendFile,
|
|
92
|
+
ErrorCode.NOT_A_FILE
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.setHeader("Content-Type", mime);
|
|
97
|
+
this.setHeader("Content-Length", String(stat.size));
|
|
98
|
+
|
|
99
|
+
// Properly propagate stream errors and respect backpressure
|
|
100
|
+
await pipeline(createReadStream(path), this);
|
|
101
|
+
} catch (err: any) {
|
|
102
|
+
if (err?.code === "ENOENT") {
|
|
103
|
+
throw frameworkError(
|
|
104
|
+
`File not found: ${path}`,
|
|
105
|
+
this.sendFile,
|
|
106
|
+
ErrorCode.FILE_NOT_FOUND
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw frameworkError(
|
|
111
|
+
`Failed to send file: ${path}`,
|
|
112
|
+
this.sendFile,
|
|
113
|
+
ErrorCode.SEND_FILE_FAIL
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Set the status code of the response
|
|
119
|
+
status(code: number) {
|
|
120
|
+
this.statusCode = code;
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Set the Content-Disposition header to prompt the user to download a file
|
|
125
|
+
attachment(filename?: string) {
|
|
126
|
+
const contentDisposition = filename
|
|
127
|
+
? `attachment; filename="${filename}"`
|
|
128
|
+
: "attachment";
|
|
129
|
+
this.setHeader("Content-Disposition", contentDisposition);
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
151
132
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
133
|
+
// Redirects to a new URL
|
|
134
|
+
redirect(location: string) {
|
|
135
|
+
this.writeHead(302, { Location: location });
|
|
136
|
+
this.end();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Send a json data back to the client (for small json data, less than the highWaterMark)
|
|
140
|
+
json(data: any) {
|
|
141
|
+
// This is only good for bodies that their size is less than the highWaterMark value
|
|
142
|
+
this.setHeader("Content-Type", "application/json");
|
|
143
|
+
this.end(JSON.stringify(data));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export class Cpeak {
|
|
148
|
+
#server: http.Server<typeof CpeakIncomingMessage, typeof CpeakServerResponse>;
|
|
149
|
+
#routes: RoutesMap;
|
|
150
|
+
#middleware: Middleware[];
|
|
151
|
+
#handleErr?: (err: unknown, req: CpeakRequest, res: CpeakResponse) => void;
|
|
152
|
+
|
|
153
|
+
constructor() {
|
|
154
|
+
this.#server = http.createServer({
|
|
155
|
+
IncomingMessage: CpeakIncomingMessage,
|
|
156
|
+
ServerResponse: CpeakServerResponse
|
|
157
|
+
});
|
|
158
|
+
this.#routes = {};
|
|
159
|
+
this.#middleware = [];
|
|
160
|
+
|
|
161
|
+
this.#server.on(
|
|
162
|
+
"request",
|
|
163
|
+
async (req: CpeakRequest, res: CpeakResponse) => {
|
|
164
|
+
// Get the url without the URL parameters (query strings)
|
|
165
|
+
const qIndex = req.url?.indexOf("?");
|
|
166
|
+
const urlWithoutQueries =
|
|
167
|
+
qIndex === -1 ? req.url || "" : req.url?.substring(0, qIndex);
|
|
168
|
+
|
|
169
|
+
const dispatchError = (error: unknown) => {
|
|
170
|
+
if (res.headersSent) {
|
|
171
|
+
req.socket?.destroy();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
res.setHeader("Connection", "close");
|
|
175
|
+
this.#handleErr?.(error, req, res);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Run all the specific middleware functions for that router only and then run the handler
|
|
179
|
+
const runHandler = async (
|
|
180
|
+
req: CpeakRequest,
|
|
181
|
+
res: CpeakResponse,
|
|
182
|
+
middleware: RouteMiddleware[],
|
|
183
|
+
cb: Handler,
|
|
184
|
+
index: number
|
|
185
|
+
) => {
|
|
186
|
+
// Our exit point...
|
|
187
|
+
if (index === middleware.length) {
|
|
188
|
+
// Call the route handler with the modified req and res objects.
|
|
189
|
+
// Also handle the promise errors by passing them to the handleErr to save developers from having to manually wrap every handler in try catch.
|
|
190
|
+
try {
|
|
191
|
+
await cb(req, res, dispatchError);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
dispatchError(error);
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
// Handle the promise errors by passing them to the handleErr to save developers from having to manually wrap every handler middleware in try catch.
|
|
197
|
+
try {
|
|
198
|
+
await middleware[index](
|
|
199
|
+
req,
|
|
200
|
+
res,
|
|
201
|
+
// The next function
|
|
202
|
+
async (error) => {
|
|
203
|
+
// this function only accepts an error argument to be more compatible with NPM modules that are built for express
|
|
204
|
+
if (error) {
|
|
205
|
+
return dispatchError(error);
|
|
206
|
+
}
|
|
207
|
+
await runHandler(req, res, middleware, cb, index + 1);
|
|
208
|
+
},
|
|
209
|
+
// Error handler for a route middleware
|
|
210
|
+
dispatchError
|
|
211
|
+
);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
dispatchError(error);
|
|
214
|
+
}
|
|
156
215
|
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Run all the middleware functions (beforeEach functions) before we run the corresponding route
|
|
219
|
+
const runMiddleware = async (
|
|
220
|
+
req: CpeakRequest,
|
|
221
|
+
res: CpeakResponse,
|
|
222
|
+
middleware: Middleware[],
|
|
223
|
+
index: number
|
|
224
|
+
) => {
|
|
225
|
+
// Our exit point...
|
|
226
|
+
if (index === middleware.length) {
|
|
227
|
+
const routes = this.#routes[req.method?.toLowerCase() || ""];
|
|
228
|
+
if (routes && typeof routes[Symbol.iterator] === "function")
|
|
229
|
+
for (const route of routes) {
|
|
230
|
+
const match = urlWithoutQueries?.match(route.regex);
|
|
231
|
+
|
|
232
|
+
if (match) {
|
|
233
|
+
// Parse the URL path variables from the matched route (like /users/:id)
|
|
234
|
+
const pathVariables = this.#extractPathVariables(
|
|
235
|
+
route.path,
|
|
236
|
+
match
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// We will call this params to be more familiar with other node.js frameworks.
|
|
240
|
+
req.params = pathVariables;
|
|
241
|
+
|
|
242
|
+
return await runHandler(
|
|
243
|
+
req,
|
|
244
|
+
res,
|
|
245
|
+
route.middleware,
|
|
246
|
+
route.cb,
|
|
247
|
+
0
|
|
248
|
+
);
|
|
169
249
|
}
|
|
170
|
-
runHandler(req, res, middleware, cb, index + 1);
|
|
171
|
-
},
|
|
172
|
-
// Error handler for a route middleware
|
|
173
|
-
(error) => {
|
|
174
|
-
res.setHeader("Connection", "close");
|
|
175
|
-
this._handleErr?.(error, req, res);
|
|
176
250
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
251
|
+
|
|
252
|
+
// If the requested route dose not exist, return 404
|
|
253
|
+
return res
|
|
254
|
+
.status(404)
|
|
255
|
+
.json({ error: `Cannot ${req.method} ${urlWithoutQueries}` });
|
|
256
|
+
} else {
|
|
257
|
+
try {
|
|
258
|
+
await middleware[index](req, res, async (err?: unknown) => {
|
|
259
|
+
if (err) {
|
|
260
|
+
return dispatchError(err);
|
|
261
|
+
}
|
|
262
|
+
await runMiddleware(req, res, middleware, index + 1);
|
|
187
263
|
});
|
|
264
|
+
} catch (error) {
|
|
265
|
+
dispatchError(error);
|
|
188
266
|
}
|
|
189
|
-
} catch (error) {
|
|
190
|
-
res.setHeader("Connection", "close");
|
|
191
|
-
this._handleErr?.(error, req, res);
|
|
192
267
|
}
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// Run all the middleware functions (beforeEach functions) before we run the corresponding route
|
|
197
|
-
const runMiddleware = (
|
|
198
|
-
req: CpeakRequest,
|
|
199
|
-
res: CpeakResponse,
|
|
200
|
-
middleware: Middleware[],
|
|
201
|
-
index: number
|
|
202
|
-
) => {
|
|
203
|
-
// Our exit point...
|
|
204
|
-
if (index === middleware.length) {
|
|
205
|
-
const routes = this.routes[req.method?.toLowerCase() || ""];
|
|
206
|
-
if (routes && typeof routes[Symbol.iterator] === "function")
|
|
207
|
-
for (const route of routes) {
|
|
208
|
-
const match = urlWithoutParams?.match(route.regex);
|
|
209
|
-
|
|
210
|
-
if (match) {
|
|
211
|
-
// Parse the URL variables from the matched route (like /users/:id)
|
|
212
|
-
const vars = this.#extractVars(route.path, match);
|
|
213
|
-
req.vars = vars;
|
|
214
|
-
|
|
215
|
-
return runHandler(req, res, route.middleware, route.cb, 0);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
268
|
+
};
|
|
218
269
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.json({ error: `Cannot ${req.method} ${urlWithoutParams}` });
|
|
223
|
-
} else {
|
|
224
|
-
middleware[index](req, res, () => {
|
|
225
|
-
runMiddleware(req, res, middleware, index + 1);
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
runMiddleware(req, res, this.middleware, 0);
|
|
231
|
-
});
|
|
270
|
+
await runMiddleware(req, res, this.#middleware, 0);
|
|
271
|
+
}
|
|
272
|
+
);
|
|
232
273
|
}
|
|
233
274
|
|
|
234
275
|
route(method: string, path: string, ...args: (RouteMiddleware | Handler)[]) {
|
|
235
|
-
if (!this
|
|
276
|
+
if (!this.#routes[method]) this.#routes[method] = [];
|
|
236
277
|
|
|
237
278
|
// The last argument should always be our handler
|
|
238
279
|
const cb = args.pop() as Handler;
|
|
@@ -245,62 +286,66 @@ class Cpeak {
|
|
|
245
286
|
const middleware = args.flat() as RouteMiddleware[];
|
|
246
287
|
|
|
247
288
|
const regex = this.#pathToRegex(path);
|
|
248
|
-
this
|
|
289
|
+
this.#routes[method].push({ path, regex, middleware, cb });
|
|
249
290
|
}
|
|
250
291
|
|
|
251
292
|
beforeEach(cb: Middleware) {
|
|
252
|
-
this
|
|
293
|
+
this.#middleware.push(cb);
|
|
253
294
|
}
|
|
254
295
|
|
|
255
296
|
handleErr(cb: (err: unknown, req: CpeakRequest, res: CpeakResponse) => void) {
|
|
256
|
-
this
|
|
297
|
+
this.#handleErr = cb;
|
|
257
298
|
}
|
|
258
299
|
|
|
259
300
|
listen(port: number, cb?: () => void) {
|
|
260
|
-
return this
|
|
301
|
+
return this.#server.listen(port, cb);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
address() {
|
|
305
|
+
return this.#server.address();
|
|
261
306
|
}
|
|
262
307
|
|
|
263
308
|
close(cb?: (err?: Error) => void) {
|
|
264
|
-
this
|
|
309
|
+
this.#server.close(cb);
|
|
265
310
|
}
|
|
266
311
|
|
|
267
312
|
// ------------------------------
|
|
268
313
|
// PRIVATE METHODS:
|
|
269
314
|
// ------------------------------
|
|
270
315
|
#pathToRegex(path: string) {
|
|
271
|
-
const varNames: string[] = [];
|
|
272
316
|
const regexString =
|
|
273
|
-
"^" +
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return "([^/]+)";
|
|
277
|
-
}) +
|
|
278
|
-
"$";
|
|
279
|
-
|
|
280
|
-
const regex = new RegExp(regexString);
|
|
281
|
-
return regex;
|
|
317
|
+
"^" + path.replace(/:\w+/g, "([^/]+)").replace(/\*/g, ".*") + "$";
|
|
318
|
+
|
|
319
|
+
return new RegExp(regexString);
|
|
282
320
|
}
|
|
283
321
|
|
|
284
|
-
#
|
|
285
|
-
// Extract url variable values from the matched route
|
|
286
|
-
const
|
|
287
|
-
|
|
322
|
+
#extractPathVariables(path: string, match: RegExpMatchArray) {
|
|
323
|
+
// Extract path url variable values from the matched route
|
|
324
|
+
const paramNames = (path.match(/:\w+/g) || []).map((param) =>
|
|
325
|
+
param.slice(1)
|
|
288
326
|
);
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
327
|
+
const params: StringMap = {};
|
|
328
|
+
paramNames.forEach((name, index) => {
|
|
329
|
+
params[name] = match[index + 1];
|
|
292
330
|
});
|
|
293
|
-
return
|
|
331
|
+
return params;
|
|
294
332
|
}
|
|
295
333
|
}
|
|
296
334
|
|
|
297
335
|
// Util functions
|
|
298
|
-
export {
|
|
299
|
-
|
|
300
|
-
|
|
336
|
+
export {
|
|
337
|
+
serveStatic,
|
|
338
|
+
parseJSON,
|
|
339
|
+
render,
|
|
340
|
+
swagger,
|
|
341
|
+
auth,
|
|
342
|
+
hashPassword,
|
|
343
|
+
verifyPassword,
|
|
344
|
+
cookieParser
|
|
345
|
+
} from "./utils";
|
|
346
|
+
export type { AuthOptions, PbkdfOptions, CookieOptions } from "./utils";
|
|
301
347
|
|
|
302
348
|
export type {
|
|
303
|
-
Cpeak,
|
|
304
349
|
CpeakRequest,
|
|
305
350
|
CpeakResponse,
|
|
306
351
|
Next,
|
|
@@ -308,9 +353,9 @@ export type {
|
|
|
308
353
|
Middleware,
|
|
309
354
|
RouteMiddleware,
|
|
310
355
|
Handler,
|
|
311
|
-
RoutesMap
|
|
356
|
+
RoutesMap
|
|
312
357
|
} from "./types";
|
|
313
358
|
|
|
314
|
-
export default function cpeak() {
|
|
359
|
+
export default function cpeak(): Cpeak {
|
|
315
360
|
return new Cpeak();
|
|
316
361
|
}
|
package/lib/types.ts
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import cpeak from "./index";
|
|
3
2
|
|
|
4
|
-
export type Cpeak
|
|
3
|
+
export type { Cpeak } from "./index";
|
|
5
4
|
|
|
6
5
|
// Extending Node.js's Request and Response objects to add our custom properties
|
|
7
6
|
export type StringMap = Record<string, string>;
|
|
8
7
|
|
|
9
|
-
export interface CpeakRequest<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
export interface CpeakRequest<
|
|
9
|
+
ReqBody = any,
|
|
10
|
+
ReqQueries = any
|
|
11
|
+
> extends IncomingMessage {
|
|
12
|
+
params: StringMap;
|
|
13
|
+
query: ReqQueries;
|
|
13
14
|
body?: ReqBody;
|
|
15
|
+
cookies?: StringMap;
|
|
16
|
+
signedCookies?: Record<string, string | false>;
|
|
14
17
|
[key: string]: any; // allow developers to add their onw extensions (e.g. req.test)
|
|
15
|
-
|
|
16
|
-
// For express frameworks compatibility:
|
|
17
|
-
query: ReqParams;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface CpeakResponse extends ServerResponse {
|
|
21
21
|
sendFile: (path: string, mime: string) => Promise<void>;
|
|
22
22
|
status: (code: number) => CpeakResponse;
|
|
23
|
-
|
|
23
|
+
attachment: (filename?: string) => CpeakResponse;
|
|
24
|
+
cookie: (name: string, value: string, options?: any) => CpeakResponse;
|
|
25
|
+
redirect: (location: string) => void;
|
|
24
26
|
json: (data: any) => void;
|
|
25
27
|
[key: string]: any; // allow developers to add their onw extensions (e.g. res.test)
|
|
26
28
|
}
|