cpeak 2.6.0 → 2.8.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 CHANGED
@@ -2,11 +2,15 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/cpeak.svg)](https://www.npmjs.com/package/cpeak)
4
4
 
5
- Cpeak is a minimal and fast Node.js framework inspired by Express.js.
5
+ Cpeak is a minimal and fast Node.js framework inspired by Express.js & Fastify.
6
6
 
7
- This project is designed to be improved until it's ready for use in complex production applications, aiming to be more performant and minimal than Express.js. This framework is intended for HTTP applications that primarily deal with JSON and file-based message bodies.
7
+ This project is designed to be improved until it's ready for use in complex production applications, aiming to be more performant and have a cleaner structure than Express.js and other common frameworks.
8
8
 
9
- This is an educational project that was started as part of the [Understanding Node.js: Core Concepts](https://www.udemy.com/course/understanding-nodejs-core-concepts/?referralCode=0BC21AC4DD6958AE6A95) course. If you want to learn how to build a framework like this, and get to a point where you can build things like this yourself, check out this course!
9
+ Cpeak replaces Express.js, body-parser, cookie-parser, compression, CORS, Passport and more, in one zero-dependency package. You can use it as a drop-in replacement for Express.js, and many npm packages that work with Express.js will also work with Cpeak.
10
+
11
+ Cpeak currently has almost all the features of Express.js, with roughly the same performance as Fastify. Additionally, it has a codebase that you can easily navigate, audit and change if you want. *Benchmarks and test results, and videos on navigating and understanding the codebase will be added soon.*
12
+
13
+ While Cpeak is being built for production, it is also an educational project that was started as part of the [Understanding Node.js: Core Concepts](https://www.udemy.com/course/understanding-nodejs-core-concepts/?referralCode=0BC21AC4DD6958AE6A95) course. If you want to learn how to build a framework like this, and get to a point where you can build things like this yourself and beyond, check out this course!
10
14
 
11
15
  ## Why Cpeak?
12
16
 
@@ -25,9 +29,12 @@ This is an educational project that was started as part of the [Understanding No
25
29
  - [Middleware](#middleware)
26
30
  - [Route Handling](#route-handling)
27
31
  - [Route Middleware](#route-middleware)
32
+ - [Fallback Handler](#fallback-handler)
28
33
  - [URL Variables & Parameters](#url-variables--parameters)
29
34
  - [Sending Files](#sending-files)
30
35
  - [Redirecting](#redirecting)
36
+ - [MIME Types](#mime-types)
37
+ - [Compression](#compression)
31
38
  - [Error Handling](#error-handling)
32
39
  - [Listening](#listening)
33
40
  - [Util Functions](#util-functions)
@@ -37,6 +44,7 @@ This is an educational project that was started as part of the [Understanding No
37
44
  - [cookieParser](#cookieparser)
38
45
  - [swagger](#swagger)
39
46
  - [auth](#auth)
47
+ - [cors](#cors)
40
48
  - [Complete Example](#complete-example)
41
49
  - [Versioning Notice](#versioning-notice)
42
50
 
@@ -58,7 +66,7 @@ import cpeak from "cpeak";
58
66
  const server = cpeak();
59
67
 
60
68
  server.route("get", "/", (req, res) => {
61
- res.json({ message: "Hi there!" });
69
+ return res.json({ message: "Hi there!" });
62
70
  });
63
71
 
64
72
  server.listen(3000, () => {
@@ -125,13 +133,13 @@ server.beforeEach((req, res, next) => {
125
133
  You can also add middleware functions for a particular route handler like this:
126
134
 
127
135
  ```javascript
128
- const requireAuth = (req, res, next, handleErr) => {
136
+ const requireAuth = (req, res, next) => {
129
137
  // Check if user is logged in, if so then:
130
138
  req.test = "this is a test value";
131
139
  next();
132
140
 
133
141
  // If user is not logged in:
134
- return handleErr({ status: 401, message: "Unauthorized" });
142
+ throw { status: 401, message: "Unauthorized" };
135
143
  };
136
144
 
137
145
  server.route("get", "/profile", requireAuth, (req, res) => {
@@ -166,6 +174,20 @@ server.route("patch", "/the-path-you-want", (req, res) => {
166
174
 
167
175
  First add the HTTP method name you want to handle, then the path, and finally, the callback. The `req` and `res` object types are the same as in the Node.js HTTP module (`http.IncomingMessage` and `http.ServerResponse`). You can read more about them in the [official Node.js documentation](https://nodejs.org/docs/latest/api/http.html).
168
176
 
177
+ Under the hood, Cpeak stores your routes in a radix tree, so finding the right route for an incoming request is roughly O(log n) in your total route count. In plain terms, that means matching stays fast even when your app has hundreds or thousands of routes. You won't pay a linear scan per request as your route table grows.
178
+
179
+ ### Fallback Handler
180
+
181
+ If no route or middleware matches an incoming request, Cpeak returns a default `404` message. You can replace it with your own handler using `server.fallback`:
182
+
183
+ ```javascript
184
+ server.fallback((req, res) => {
185
+ return res.status(404).json({ error: "not found" });
186
+ });
187
+ ```
188
+
189
+ Use this for custom 404 pages, JSON error envelopes, or serving an SPA's `index.html` for unknown paths. Static, param, and `*` wildcard routes still win when they match, the fallback only fires once the router has no match.
190
+
169
191
  ### URL Variables & Parameters
170
192
 
171
193
  To be more consistent with the broader Node.js community and frameworks, we call the HTTP URL parameters (query strings) '**query**', and the path variables (route parameters) '**params**'.
@@ -196,7 +218,15 @@ server.route("get", "/testing", (req, res) => {
196
218
  });
197
219
  ```
198
220
 
199
- The file’s binary content will be in the HTTP response body content. Make sure you specify a correct path relative to your CWD (use the `path` module for better compatibility) and also the correct HTTP MIME type for that file.
221
+ The file’s binary content will be in the HTTP response body content. Make sure you specify a correct path relative to your CWD (use the `path` module for better compatibility).
222
+
223
+ The MIME type argument is optional. When omitted, Cpeak infers it from the file extension using its built-in MIME registry (see [MIME Types](#mime-types)):
224
+
225
+ ```javascript
226
+ return res.status(200).sendFile("./images/sun.jpeg");
227
+ ```
228
+
229
+ Passing the MIME type explicitly is the fastest path, Cpeak skips the extension lookup entirely.
200
230
 
201
231
  ### Redirecting
202
232
 
@@ -206,50 +236,134 @@ If you want to redirect to a new URL, you can simply do:
206
236
  res.redirect("https://whatever.com");
207
237
  ```
208
238
 
209
- ### Error Handling
239
+ ### MIME Types
210
240
 
211
- If anywhere in your route functions or route middleware functions you want to return an error, you can just throw the error and let the automatic error handler catch it:
241
+ `serveStatic`, `res.sendFile`, and `res.render` all share a single MIME type registry. Out of the box it covers the common web types:
242
+
243
+ ```
244
+ html: "text/html",
245
+ css: "text/css",
246
+ js: "application/javascript",
247
+ jpg: "image/jpeg",
248
+ jpeg: "image/jpeg",
249
+ png: "image/png",
250
+ svg: "image/svg+xml",
251
+ gif: "image/gif",
252
+ ico: "image/x-icon",
253
+ txt: "text/plain",
254
+ json: "application/json",
255
+ webmanifest: "application/manifest+json",
256
+ eot: "application/vnd.ms-fontobject",
257
+ otf: "font/otf",
258
+ ttf: "font/ttf",
259
+ woff: "font/woff",
260
+ woff2: "font/woff2"
261
+ ```
262
+
263
+ If you need to serve something else, register it once on the `cpeak()` constructor:
212
264
 
213
265
  ```javascript
214
- server.route("get", "/api/document/:title", (req, res) => {
215
- const title = req.params.title;
266
+ const server = cpeak({
267
+ mimeTypes: { mp3: "audio/mpeg", m4a: "audio/mp4" }
268
+ });
269
+ ```
216
270
 
217
- if (title.length > 500) throw { status: 400, message: "Title too long." };
271
+ We keep this list small on purpose. Cpeak isn't going to load the [thousands of MIME types from the IANA registry](https://www.iana.org/assignments/media-types/media-types.xhtml) into your process memory on the off chance you might serve an `.apk` or a `.fla` one day. You tell us what you serve, we keep memory tight.
218
272
 
219
- // The rest of your logic...
273
+ ### Compression
274
+
275
+ You can enable HTTP response compression at construction time. Once enabled, `serveStatic`, `res.json()` and `res.sendFile()` will compress eligible responses automatically, and you also get a `res.compress()` method on the response for custom payloads.
276
+
277
+ Fire it up with the defaults like this:
278
+
279
+ ```javascript
280
+ const server = cpeak({ compression: true });
281
+ ```
282
+
283
+ Or pass options to tune the behavior:
284
+
285
+ ```javascript
286
+ const server = cpeak({
287
+ compression: {
288
+ threshold: 1024, // bytes — responses smaller than this are sent uncompressed. Default: 1024
289
+ brotli: {}, // node:zlib BrotliOptions
290
+ gzip: {}, // node:zlib ZlibOptions
291
+ deflate: {} // node:zlib ZlibOptions
292
+ }
293
+ });
294
+ ```
295
+
296
+ For arbitrary payloads, like a `Buffer`, `string`, or `Readable` stream, use `res.compress`:
297
+
298
+ ```javascript
299
+ server.route("get", "/report", async (req, res) => {
300
+ const csv = await buildCsvReport();
301
+ await res.compress("text/csv", csv);
302
+ });
303
+ ```
304
+
305
+ When you're streaming, you can pass a known size as the third argument. Cpeak will use it to decide eligibility against `threshold`, and to set `Content-Length` if the body ends up being sent uncompressed:
306
+
307
+ ```javascript
308
+ import { Readable } from "node:stream";
309
+
310
+ server.route("get", "/proxy/feed", async (req, res) => {
311
+ const upstream = await fetch("https://example.com/feed.xml");
312
+ const size = Number(upstream.headers.get("content-length"));
313
+ await res.compress("application/xml", Readable.fromWeb(upstream.body), size);
220
314
  });
221
315
  ```
222
316
 
223
- You can also make use of the `handleErr` callback function like this:
317
+ You must first enable compression at construction time to use `res.compress`.
318
+
319
+ ### Error Handling
320
+
321
+ If anywhere in your route functions or route middleware functions you want to return an error, you can just throw the error and let the automatic error handler catch it:
224
322
 
225
323
  ```javascript
226
- server.route("get", "/api/document/:title", (req, res, handleErr) => {
324
+ server.route("get", "/api/document/:title", (req, res) => {
227
325
  const title = req.params.title;
228
326
 
229
- if (title.length > 500)
230
- return handleErr({ status: 400, message: "Title too long." });
327
+ if (title.length > 500) throw { status: 400, message: "Title too long." };
231
328
 
232
329
  // The rest of your logic...
233
330
  });
234
331
  ```
235
332
 
236
- **Make sure** to call the `server.handleErr` and pass a function like this to have the automatic error handler work properly:
333
+ **Make sure** to call `server.handleErr` and pass a function like this to have the automatic error handler work properly:
237
334
 
238
335
  ```javascript
239
336
  server.handleErr((error, req, res) => {
240
337
  if (error && error.status) {
241
- res.status(error.status).json({ error: error.message });
338
+ return res.status(error.status).json({ error: error.message });
242
339
  } else {
243
340
  // Log the unexpected errors somewhere so you can keep track of them...
244
341
  console.error(error);
245
- res.status(500).json({
342
+ return res.status(500).json({
246
343
  error: "Sorry, something unexpected happened on our side."
247
344
  });
248
345
  }
249
346
  });
250
347
  ```
251
348
 
252
- _The error object is the object that you threw or passed to the `handleErr` function earlier in your routes._
349
+ _The error object is the object that you threw earlier in your routes or middleware._
350
+
351
+ One thing to keep in mind: `res.sendFile`, `res.render`, `res.compress`, and `res.json` all return a `Promise`. **Return** that promise (or `await` it in an `async` handler) so the framework can route any rejection to your `handleErr`:
352
+
353
+ ```javascript
354
+ server.route("get", "/file", (req, res) => {
355
+ return res.sendFile("./images/sun.jpeg", "image/jpeg");
356
+ });
357
+ ```
358
+
359
+ Or, in an `async` handler that does other work alongside the send:
360
+
361
+ ```javascript
362
+ server.route("get", "/avatars/:id", async (req, res) => {
363
+ const path = await db.lookupAvatarPath(req.params.id);
364
+ await res.sendFile(path);
365
+ });
366
+ ```
253
367
 
254
368
  ### Listening
255
369
 
@@ -261,6 +375,8 @@ server.listen(3000, () => {
261
375
  });
262
376
  ```
263
377
 
378
+ `server.listen` accepts the same arguments as Node.js's [http.Server.listen](https://nodejs.org/docs/latest/api/net.html#serverlistenoptions-callback), so you can also pass a host, an options object, or a Unix socket path.
379
+
264
380
  ### Util Functions
265
381
 
266
382
  There are utility functions that you can include and use as middleware functions. These are meant to make it easier for you to build HTTP applications. In the future, many more will be added, and you only move them into memory once you include them. No need to have many npm dependencies for simple applications!
@@ -273,6 +389,7 @@ The list of utility functions as of now:
273
389
  - cookieParser
274
390
  - swagger
275
391
  - auth
392
+ - cors
276
393
 
277
394
  Including any one of them is done like this:
278
395
 
@@ -285,44 +402,26 @@ import cpeak, { utilName } from "cpeak";
285
402
  With this middleware function, you can automatically set a folder in your project to be served by Cpeak. Here’s how to set it up:
286
403
 
287
404
  ```javascript
288
- server.beforeEach(
289
- serveStatic("./public", {
290
- mp3: "audio/mpeg"
291
- })
292
- );
405
+ server.beforeEach(serveStatic("./public"));
293
406
  ```
294
407
 
295
- If you have file types in your public folder that are not one of the following, make sure to add the MIME types manually as the second argument in the function as an object where each property key is the file extension, and each value is the correct MIME type for that. You can see all the available MIME types on the [IANA website](https://www.iana.org/assignments/media-types/media-types.xhtml).
296
-
297
- ```
298
- html: "text/html",
299
- css: "text/css",
300
- js: "application/javascript",
301
- jpg: "image/jpeg",
302
- jpeg: "image/jpeg",
303
- png: "image/png",
304
- svg: "image/svg+xml",
305
- gif: "image/gif",
306
- ico: "image/x-icon",
307
- txt: "text/plain",
308
- json: "application/json",
309
- webmanifest: "application/manifest+json",
310
- eot: "application/vnd.ms-fontobject",
311
- otf: "font/otf",
312
- ttf: "font/ttf",
313
- woff: "font/woff",
314
- woff2: "font/woff2"
315
- ```
408
+ `serveStatic` infers each file's MIME type from its extension. If your folder contains types that aren't in the built-in registry, register them on the `cpeak()` constructor (see [MIME Types](#mime-types)).
316
409
 
317
- You can also serve your static files under a URL prefix by passing a third argument with a `prefix` option. This is useful when you want all static assets to live under a specific path like `/static`:
410
+ You can also serve your static files under a URL prefix by passing a `prefix` option. This is useful when you want all static assets to live under a specific path like `/static`:
318
411
 
319
412
  ```javascript
320
413
  server.beforeEach(
321
- serveStatic("./public", null, { prefix: "/static" })
414
+ serveStatic("./public", { prefix: "/static" })
322
415
  );
323
416
  ```
324
417
 
325
- With this setup, a file at `./public/app.js` would be served at `/static/app.js` instead of `/app.js`. Pass `null` as the second argument if you don’t need any custom MIME types.
418
+ With this setup, a file at `./public/app.js` would be served at `/static/app.js` instead of `/app.js`.
419
+
420
+ If file names in the served folder change while the server is running (e.g. during development), pass `live: true` so the middleware stats the disk on each request instead of caching the file map at startup:
421
+
422
+ ```javascript
423
+ server.beforeEach(serveStatic("./public", { live: true }));
424
+ ```
326
425
 
327
426
  #### parseJSON
328
427
 
@@ -344,7 +443,7 @@ server.route("put", "/api/user", (req, res) => {
344
443
  // rest of your logic...
345
444
 
346
445
  // Sending JSON in the HTTP response:
347
- res.status(201).json({ message: "Something was created..." });
446
+ return res.status(201).json({ message: "Something was created..." });
348
447
  });
349
448
  ```
350
449
 
@@ -373,6 +472,8 @@ server.route("get", "/", (req, res, next) => {
373
472
  });
374
473
  ```
375
474
 
475
+ The third argument (the MIME type) is optional. When omitted, Cpeak infers it from the file extension using the registry on the `cpeak()` constructor.
476
+
376
477
  You can then inject the variables into your file in {{ variable_name }} like this:
377
478
 
378
479
  ```HTML
@@ -412,7 +513,7 @@ server.route("get", "/dashboard", (req, res) => {
412
513
  // Signed cookies — returns false if the signature is invalid or the value was tampered with
413
514
  const userId = req.signedCookies.userId;
414
515
 
415
- res.status(200).json({ theme, userId });
516
+ return res.status(200).json({ theme, userId });
416
517
  });
417
518
  ```
418
519
 
@@ -426,7 +527,7 @@ server.route("post", "/login", (req, res) => {
426
527
  // A signed cookie
427
528
  res.cookie("userId", "abc123", { signed: true, httpOnly: true, secure: true });
428
529
 
429
- res.status(200).json({ message: "Logged in" });
530
+ return res.status(200).json({ message: "Logged in" });
430
531
  });
431
532
  ```
432
533
 
@@ -571,20 +672,40 @@ For complete working examples, see:
571
672
  - [`examples/auth-localstorage.js`](examples/auth-localstorage.js) — token sent via the `Authorization` header (suited for SPAs and mobile clients)
572
673
  - [`examples/auth-cookies.js`](examples/auth-cookies.js) — token stored in an `httpOnly` cookie (protects against XSS)
573
674
 
675
+ #### cors
676
+ The CORS middleware allows you to enable Cross-Origin Resource Sharing in your application.
677
+
678
+ ```javascript
679
+ server.beforeEach(cors({
680
+ origin: "http://localhost:3000", // string, string[], RegExp, boolean, or async (origin) => boolean. Default: "*" (all origins)
681
+ methods: "GET,POST,PUT,DELETE", // allowed HTTP methods. Default: "GET,HEAD,PUT,PATCH,POST,DELETE"
682
+ allowedHeaders: "Content-Type", // headers the browser may send. Default: echoes request headers for origin:"*", else "Content-Type, Authorization"
683
+ exposedHeaders: "X-Request-Id", // response headers the browser may read. Default: none
684
+ credentials: true, // adds Access-Control-Allow-Credentials: true. Default: false
685
+ maxAge: 3600, // seconds to cache preflight result in the browser. Default: 86400
686
+ preflightContinue: false, // pass OPTIONS preflight to next middleware instead of auto-responding. Default: false
687
+ optionsSuccessStatus: 204 // status code for successful preflight responses. Default: 204
688
+ }));
689
+ ```
690
+
691
+ Or if you don't care and want to allow everything with the default settings, just do:
692
+
693
+ ```javascript
694
+ server.beforeEach(cors());
695
+ ```
696
+
574
697
  ## Complete Example
575
698
 
576
699
  Here you can see all the features that Cpeak offers (excluding the authentication features), in one small piece of code:
577
700
 
578
701
  ```javascript
579
- import cpeak, { serveStatic, parseJSON, render, cookieParser } from "cpeak";
702
+ import cpeak, { serveStatic, parseJSON, render, cookieParser, cors } from "cpeak";
580
703
 
581
- const server = cpeak();
704
+ const server = cpeak({
705
+ mimeTypes: { mp3: "audio/mpeg" }
706
+ });
582
707
 
583
- server.beforeEach(
584
- serveStatic("./public", {
585
- mp3: "audio/mpeg"
586
- })
587
- );
708
+ server.beforeEach(serveStatic("./public"));
588
709
 
589
710
  server.beforeEach(render());
590
711
 
@@ -594,6 +715,13 @@ server.beforeEach(parseJSON());
594
715
  // For reading and setting cookies
595
716
  server.beforeEach(cookieParser({ secret: "your-secret-key" }));
596
717
 
718
+ // For enabling CORS
719
+ server.beforeEach(cors({
720
+ origin: "http://localhost:3000",
721
+ credentials: true,
722
+ methods: "GET,POST,PUT,DELETE"
723
+ }));
724
+
597
725
  // Adding custom middleware functions
598
726
  server.beforeEach((req, res, next) => {
599
727
  req.custom = "This is some string";
@@ -601,11 +729,11 @@ server.beforeEach((req, res, next) => {
601
729
  });
602
730
 
603
731
  // A middleware function that can be specified to run before some particular routes
604
- const testRouteMiddleware = (req, res, next, handleErr) => {
732
+ const testRouteMiddleware = (req, res, next) => {
605
733
  req.whatever = "some calculated value maybe";
606
734
 
607
735
  if (req.params.test !== "something special") {
608
- return handleErr({ status: 400, message: "an error message" });
736
+ throw { status: 400, message: "an error message" };
609
737
  }
610
738
 
611
739
  next();
@@ -642,7 +770,7 @@ server.route("get", "/api/document/:title", testRouteMiddleware, (req, res) => {
642
770
  throw { status: 400, message: "Invalid property." };
643
771
 
644
772
  // Sending a JSON response
645
- res.status(200).json({ message: "This is a test response" });
773
+ return res.status(200).json({ message: "This is a test response" });
646
774
  });
647
775
 
648
776
  // Reading and setting cookies
@@ -652,22 +780,22 @@ server.route("post", "/login", (req, res) => {
652
780
 
653
781
  // Set a signed session cookie
654
782
  res.cookie("sessionId", "abc123", { signed: true, httpOnly: true, secure: true });
655
- res.status(200).json({ message: "Logged in" });
783
+ return res.status(200).json({ message: "Logged in" });
656
784
  });
657
785
 
658
786
  // Sending a file response
659
787
  server.route("get", "/file", (req, res) => {
660
788
  // Make sure to specify a correct path and MIME type...
661
- res.status(200).sendFile("<path-to-file-relative-to-cwd>", "<mime-type>");
789
+ return res.status(200).sendFile("<path-to-file-relative-to-cwd>", "<mime-type>");
662
790
  });
663
791
 
664
792
  // Handle all the errors that could happen in the routes
665
793
  server.handleErr((error, req, res) => {
666
794
  if (error && error.status) {
667
- res.status(error.status).json({ error: error.message });
795
+ return res.status(error.status).json({ error: error.message });
668
796
  } else {
669
797
  console.error(error);
670
- res.status(500).json({
798
+ return res.status(500).json({
671
799
  error: "Sorry, something unexpected happened from our side."
672
800
  });
673
801
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,39 @@
1
- import * as net from 'net';
2
- import http, { IncomingMessage, ServerResponse } from 'node:http';
1
+ import http, { IncomingMessage, ServerResponse, Server } from 'node:http';
2
+ import net from 'node:net';
3
+ import { Readable } from 'node:stream';
4
+ import { Buffer } from 'node:buffer';
5
+ import * as node_zlib from 'node:zlib';
3
6
 
7
+ declare function frameworkError(message: string, skipFn: Function, code?: string, status?: number): Error & {
8
+ code?: string;
9
+ cpeak_err?: boolean;
10
+ };
11
+ declare enum ErrorCode {
12
+ MISSING_MIME = "CPEAK_ERR_MISSING_MIME",
13
+ FILE_NOT_FOUND = "CPEAK_ERR_FILE_NOT_FOUND",
14
+ NOT_A_FILE = "CPEAK_ERR_NOT_A_FILE",
15
+ SEND_FILE_FAIL = "CPEAK_ERR_SEND_FILE_FAIL",
16
+ INVALID_JSON = "CPEAK_ERR_INVALID_JSON",
17
+ PAYLOAD_TOO_LARGE = "CPEAK_ERR_PAYLOAD_TOO_LARGE",
18
+ WEAK_SECRET = "CPEAK_ERR_WEAK_SECRET",
19
+ COMPRESSION_NOT_ENABLED = "CPEAK_ERR_COMPRESSION_NOT_ENABLED",
20
+ DUPLICATE_ROUTE = "CPEAK_ERR_DUPLICATE_ROUTE",
21
+ INVALID_ROUTE = "CPEAK_ERR_INVALID_ROUTE"
22
+ }
23
+
24
+ interface CompressionOptions {
25
+ threshold?: number;
26
+ brotli?: node_zlib.BrotliOptions;
27
+ gzip?: node_zlib.ZlibOptions;
28
+ deflate?: node_zlib.ZlibOptions;
29
+ }
30
+ type ResolvedCompressionConfig = Required<CompressionOptions>;
31
+
32
+ type CpeakHttpServer = Server<typeof CpeakIncomingMessage, typeof CpeakServerResponse>;
33
+ interface CpeakOptions {
34
+ compression?: boolean | CompressionOptions;
35
+ mimeTypes?: StringMap;
36
+ }
4
37
  type StringMap = Record<string, string>;
5
38
  interface CpeakRequest<ReqBody = any, ReqQueries = any> extends IncomingMessage {
6
39
  params: StringMap;
@@ -11,40 +44,32 @@ interface CpeakRequest<ReqBody = any, ReqQueries = any> extends IncomingMessage
11
44
  [key: string]: any;
12
45
  }
13
46
  interface CpeakResponse extends ServerResponse {
14
- sendFile: (path: string, mime: string) => Promise<void>;
47
+ sendFile: (path: string, mime?: string) => Promise<void>;
15
48
  status: (code: number) => CpeakResponse;
16
49
  attachment: (filename?: string) => CpeakResponse;
17
50
  cookie: (name: string, value: string, options?: any) => CpeakResponse;
18
51
  redirect: (location: string) => void;
19
- json: (data: any) => void;
52
+ json: (data: any) => Promise<void>;
53
+ compress: (mime: string, body: Buffer | string | Readable, size?: number) => Promise<void>;
20
54
  [key: string]: any;
21
55
  }
22
56
  type Next = (err?: any) => void;
23
- type HandleErr = (err: any) => void;
24
57
  type Middleware<ReqBody = any, ReqParams = any> = (req: CpeakRequest<ReqBody, ReqParams>, res: CpeakResponse, next: Next) => void;
25
- type RouteMiddleware<ReqBody = any, ReqParams = any> = (req: CpeakRequest<ReqBody, ReqParams>, res: CpeakResponse, next: Next, handleErr: HandleErr) => void | Promise<void>;
26
- type Handler<ReqBody = any, ReqParams = any> = (req: CpeakRequest<ReqBody, ReqParams>, res: CpeakResponse, handleErr: HandleErr) => void | Promise<void>;
27
- interface Route {
28
- path: string;
29
- regex: RegExp;
30
- middleware: RouteMiddleware[];
31
- cb: Handler;
32
- }
33
- interface RoutesMap {
34
- [method: string]: Route[];
35
- }
58
+ type RouteMiddleware<ReqBody = any, ReqParams = any> = (req: CpeakRequest<ReqBody, ReqParams>, res: CpeakResponse, next: Next) => void | Promise<void>;
59
+ type Handler<ReqBody = any, ReqParams = any> = (req: CpeakRequest<ReqBody, ReqParams>, res: CpeakResponse) => void | Promise<void>;
36
60
 
37
61
  declare const parseJSON: (options?: {
38
62
  limit?: number;
39
63
  }) => (req: CpeakRequest, res: CpeakResponse, next: Next) => void;
40
64
 
41
- declare const serveStatic: (folderPath: string, newMimeTypes?: StringMap, options?: {
65
+ declare const serveStatic: (folderPath: string, options?: {
42
66
  prefix?: string;
67
+ live?: boolean;
43
68
  }) => (req: CpeakRequest, res: CpeakResponse, next: Next) => void | Promise<void>;
44
69
 
45
70
  declare const render: () => (req: CpeakRequest, res: CpeakResponse, next: Next) => void;
46
71
 
47
- declare const swagger: (spec: object, prefix?: string) => (req: CpeakRequest, res: CpeakResponse, next: Next) => void;
72
+ declare const swagger: (spec: object, prefix?: string) => (req: CpeakRequest, res: CpeakResponse, next: Next) => Promise<void> | undefined;
48
73
 
49
74
  interface PbkdfOptions {
50
75
  iterations?: number;
@@ -64,10 +89,6 @@ interface AuthOptions extends PbkdfOptions {
64
89
  tokenIdSize?: number;
65
90
  revokeToken?: (tokenId: string) => Promise<void>;
66
91
  }
67
- declare function hashPassword(password: string, options?: PbkdfOptions): Promise<string>;
68
- declare function verifyPassword(password: string, stored: string): Promise<boolean>;
69
- declare function auth(options: AuthOptions): Middleware;
70
-
71
92
  interface CookieOptions {
72
93
  signed?: boolean;
73
94
  httpOnly?: boolean;
@@ -78,23 +99,28 @@ interface CookieOptions {
78
99
  path?: string;
79
100
  domain?: string;
80
101
  }
102
+ type OriginInput = string | string[] | RegExp | boolean | ((origin: string | undefined) => boolean | Promise<boolean>);
103
+ interface CorsOptions {
104
+ origin?: OriginInput;
105
+ methods?: string | string[];
106
+ allowedHeaders?: string | string[];
107
+ exposedHeaders?: string | string[];
108
+ credentials?: boolean;
109
+ maxAge?: number;
110
+ preflightContinue?: boolean;
111
+ optionsSuccessStatus?: number;
112
+ }
113
+
114
+ declare function hashPassword(password: string, options?: PbkdfOptions): Promise<string>;
115
+ declare function verifyPassword(password: string, stored: string): Promise<boolean>;
116
+ declare function auth(options: AuthOptions): Middleware;
117
+
81
118
  declare function cookieParser(options?: {
82
119
  secret?: string;
83
120
  }): (req: CpeakRequest, res: CpeakResponse, next: Next) => void;
84
121
 
85
- declare function frameworkError(message: string, skipFn: Function, code?: string, status?: number): Error & {
86
- code?: string;
87
- cpeak_err?: boolean;
88
- };
89
- declare enum ErrorCode {
90
- MISSING_MIME = "CPEAK_ERR_MISSING_MIME",
91
- FILE_NOT_FOUND = "CPEAK_ERR_FILE_NOT_FOUND",
92
- NOT_A_FILE = "CPEAK_ERR_NOT_A_FILE",
93
- SEND_FILE_FAIL = "CPEAK_ERR_SEND_FILE_FAIL",
94
- INVALID_JSON = "CPEAK_ERR_INVALID_JSON",
95
- PAYLOAD_TOO_LARGE = "CPEAK_ERR_PAYLOAD_TOO_LARGE",
96
- WEAK_SECRET = "CPEAK_ERR_WEAK_SECRET"
97
- }
122
+ declare const cors: (options?: CorsOptions) => (req: CpeakRequest, res: CpeakResponse, next: Next) => Promise<void>;
123
+
98
124
  declare class CpeakIncomingMessage extends http.IncomingMessage {
99
125
  #private;
100
126
  body: any;
@@ -102,23 +128,28 @@ declare class CpeakIncomingMessage extends http.IncomingMessage {
102
128
  get query(): StringMap;
103
129
  }
104
130
  declare class CpeakServerResponse extends http.ServerResponse<CpeakIncomingMessage> {
105
- sendFile(path: string, mime: string): Promise<void>;
131
+ _compression?: ResolvedCompressionConfig;
132
+ sendFile(path: string, mime?: string): Promise<void>;
106
133
  status(code: number): this;
107
134
  attachment(filename?: string): this;
108
135
  redirect(location: string): void;
109
- json(data: any): void;
136
+ json(data: any): Promise<void>;
137
+ compress(mime: string, body: Buffer | string | Readable, size?: number): Promise<void>;
110
138
  }
111
139
  declare class Cpeak {
112
140
  #private;
113
- constructor();
141
+ constructor(options?: CpeakOptions);
114
142
  route(method: string, path: string, ...args: (RouteMiddleware | Handler)[]): void;
115
143
  beforeEach(cb: Middleware): void;
116
144
  handleErr(cb: (err: unknown, req: CpeakRequest, res: CpeakResponse) => void): void;
117
- listen(port: number, cb?: () => void): http.Server<typeof CpeakIncomingMessage, typeof CpeakServerResponse>;
145
+ listen(port: number, cb?: () => void): CpeakHttpServer;
146
+ listen(port: number, host: string, cb?: () => void): CpeakHttpServer;
147
+ listen(options: net.ListenOptions, cb?: () => void): CpeakHttpServer;
118
148
  address(): string | net.AddressInfo | null;
119
- close(cb?: (err?: Error) => void): void;
149
+ close(cb?: (err?: Error) => void): CpeakHttpServer;
150
+ get server(): CpeakHttpServer;
120
151
  }
121
152
 
122
- declare function cpeak(): Cpeak;
153
+ declare function cpeak(options?: CpeakOptions): Cpeak;
123
154
 
124
- export { type AuthOptions, type CookieOptions, Cpeak, CpeakIncomingMessage, type CpeakRequest, type CpeakResponse, CpeakServerResponse, ErrorCode, type HandleErr, type Handler, type Middleware, type Next, type PbkdfOptions, type RouteMiddleware, type RoutesMap, auth, cookieParser, cpeak as default, frameworkError, hashPassword, parseJSON, render, serveStatic, swagger, verifyPassword };
155
+ export { type AuthOptions, type CompressionOptions, type CookieOptions, type CorsOptions, Cpeak, type CpeakHttpServer, CpeakIncomingMessage, type CpeakOptions, type CpeakRequest, type CpeakResponse, CpeakServerResponse, ErrorCode, type Handler, type Middleware, type Next, type PbkdfOptions, type RouteMiddleware, auth, cookieParser, cors, cpeak as default, frameworkError, hashPassword, parseJSON, render, serveStatic, swagger, verifyPassword };