@ublitzjs/core 0.0.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 +38 -0
- package/USAGE.md +319 -0
- package/cjs/http-codes.cjs +56 -0
- package/cjs/http-headers.cjs +63 -0
- package/cjs/index.cjs +61 -0
- package/logo.png +0 -0
- package/mjs/http-codes.mjs +52 -0
- package/mjs/http-headers.mjs +61 -0
- package/mjs/index.mjs +51 -0
- package/package.json +29 -0
- package/tsconfig.json +41 -0
- package/types/http-codes.d.ts +54 -0
- package/types/http-headers.d.ts +285 -0
- package/types/index.d.ts +170 -0
- package/types/uws-types.d.ts +123 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+

|
|
2
|
+
<br/>
|
|
3
|
+
|
|
4
|
+
# μBlitz.js - utility-library for uWebSockets.js
|
|
5
|
+
|
|
6
|
+
On NPM you can find such packages:
|
|
7
|
+
|
|
8
|
+
- @ublitzjs/core (THIS repo)
|
|
9
|
+
- @ublitzjs/logger (colorful console)
|
|
10
|
+
- @ublitzjs/static (send files)
|
|
11
|
+
- @ublitzjs/payload (handling POST-like requests)
|
|
12
|
+
- @ublitzjs/router (OpenAPI-like router for simple orientation)
|
|
13
|
+
- @ublitzjs/openapi
|
|
14
|
+
- @ublitzjs/dev-comments (Tool for removing CODE BETWEEN /\*\_START_DEV\_\*/ and /\*\_END_DEV\_\*/ comments)
|
|
15
|
+
|
|
16
|
+
# Installation
|
|
17
|
+
|
|
18
|
+
```ps1
|
|
19
|
+
npm install "@ublitzjs/core"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or with bun. Or whatever.
|
|
23
|
+
|
|
24
|
+
# Preferences
|
|
25
|
+
|
|
26
|
+
Supports CJS and ESM in ALL packages
|
|
27
|
+
|
|
28
|
+
# <a href="./USAGE.md" target="_blank">Go to usage.md</a>
|
|
29
|
+
|
|
30
|
+
# Comparison with others
|
|
31
|
+
|
|
32
|
+
It is acclaimed, that Express.js, Fastify, and other frameworks are built to be very extensible and developer-friendly. It is enough to download needed packages, put them as middlewares, which handle everything behind the scenes, and do your own job. <br>
|
|
33
|
+
|
|
34
|
+
But the "speed" of creating your app comes from so-called "abstractions". Their versatility is often considered an "overkill" and they usually overwhelm the RAM and slow down your app. Most people, however, still prefer "rapid typing" over speed of execution, under which I mean uWebSocket.js (which actually handles websockets AND http requests)<br>
|
|
35
|
+
|
|
36
|
+
Though it IS performant, it handles only core features. Now there are solutions like "hyper-express", "ultimate-express", "uwebsockets-express", but they are just abstractions, created to be somehow faster then express, but hide the most important (and difficult) aspects of uWS.<br>
|
|
37
|
+
|
|
38
|
+
This library is different. It doesn't take care of everything, but rather helps you do it yourself, using utilities.
|
package/USAGE.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
> some examples will be shown using typescript, all examples are esm (but can also be cjs)
|
|
2
|
+
|
|
3
|
+
# Setup main file
|
|
4
|
+
|
|
5
|
+
In this section each code sample will show more features, so be ready to see them one by one.
|
|
6
|
+
|
|
7
|
+
## no routers or other imports
|
|
8
|
+
|
|
9
|
+
### without package
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import uWS from "uWebSockets.js";
|
|
13
|
+
|
|
14
|
+
const server = uWS.App();
|
|
15
|
+
|
|
16
|
+
server.any("/*", (res, req) => {
|
|
17
|
+
// manually handle this
|
|
18
|
+
res.writeStatus("404").end("Nothing to see here!");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// though I don't use "run", it is needed for example
|
|
22
|
+
function run() {
|
|
23
|
+
server.listen(9001, (token) =>
|
|
24
|
+
token ? console.info("Start success") : console.error("Start failure")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
run();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### with core package (low deps)
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import uWS from "uWebSockets.js";
|
|
35
|
+
|
|
36
|
+
// c404 is an ArrayBuffer (not all codes are converted, don't worry), toAB - conversion helper
|
|
37
|
+
import { c404, toAB } from "@ublitzjs/core";
|
|
38
|
+
|
|
39
|
+
const server = uWS.App();
|
|
40
|
+
|
|
41
|
+
var noFoundMessage = toAB("Nothing to see here!");
|
|
42
|
+
server.any("/*", (res, req) => {
|
|
43
|
+
res.writeStatus(c404).end(notFoundMessage);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function run() {
|
|
47
|
+
server.listen(9001, (token) =>
|
|
48
|
+
token ? console.info("Start success") : console.error("Start failure")
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
run();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### with core package (better version)
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import uWS from "uWebSockets.js";
|
|
59
|
+
|
|
60
|
+
// extendApp - lets you use or write extensions, notFoundConstructor - send 404 with a message
|
|
61
|
+
import { extendApp, notFoundConstructor } from "@ublitzjs/core";
|
|
62
|
+
|
|
63
|
+
// all extensions will be typed in typescript, so server.run call is also typed.
|
|
64
|
+
const server = extendApp(
|
|
65
|
+
uWS.App(),
|
|
66
|
+
/* your extensions as a rest parameter. They will be bound to "server" */ {
|
|
67
|
+
run(/*for typescript*/ this: Server) {
|
|
68
|
+
this.listen(9001, (token) =>
|
|
69
|
+
token ? console.info("Start success") : console.error("Start failure")
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
} /*, you can put here as many as you want. */
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
server.any("/*", notFoundConstructor("Nothing to see here!"));
|
|
76
|
+
|
|
77
|
+
// use your extension
|
|
78
|
+
server.run();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
And last example, just to show real-world use-case for extendApp
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import uWS from "uWebSockets.js";
|
|
85
|
+
import { extendApp, notFoundConstructor } from "@ublitzjs/core";
|
|
86
|
+
|
|
87
|
+
import { serverExtension as openapiExt } from "@ublitzjs/openapi";
|
|
88
|
+
|
|
89
|
+
const server = extendApp(
|
|
90
|
+
uWS.App(),
|
|
91
|
+
// this is very basic, so for better examples - go find source repo
|
|
92
|
+
openapiExt({
|
|
93
|
+
openapi: "3.0.0",
|
|
94
|
+
info: {
|
|
95
|
+
title: "Some ublitzjs server",
|
|
96
|
+
version: "0.0.1",
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
// your extension can also be here
|
|
100
|
+
{
|
|
101
|
+
run(this: Server) {
|
|
102
|
+
this.listen(9001, (token) =>
|
|
103
|
+
token ? console.info("Start success") : console.error("Start failure")
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
// this function is given by the extension
|
|
109
|
+
await server.serveOpenApi("/docs", { build: false, path: "openapi.json" });
|
|
110
|
+
|
|
111
|
+
server.any("/*", notFoundConstructor("Nothing to see here!"));
|
|
112
|
+
|
|
113
|
+
server.run();
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
So in extendApp you put "extensions", as I call them.
|
|
117
|
+
|
|
118
|
+
## With routers (mainly not @ublitzjs/router) and other imports
|
|
119
|
+
|
|
120
|
+
### two modules with core package
|
|
121
|
+
|
|
122
|
+
_main.js_
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import uWS from "uWebSockets.js";
|
|
126
|
+
import { extendApp } from "@ublitzjs/core";
|
|
127
|
+
import router from "./router.js";
|
|
128
|
+
const server = extendApp(uWS.App());
|
|
129
|
+
// register "plugins", not "extensions"
|
|
130
|
+
server.register(router);
|
|
131
|
+
server.listen(9001, () => {});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
here you have imported your router and registered it as a plugin. So "extension" gives you NEW functionality, while "plugin" should use your current functionality
|
|
135
|
+
|
|
136
|
+
_router.js_
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import type { Server } from "@ublitzjs/core";
|
|
140
|
+
// for readability and
|
|
141
|
+
export default (server: Server) => {
|
|
142
|
+
server.get("/simple", (res) => {
|
|
143
|
+
res.end("hello");
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### example with @ublitzjs/router
|
|
149
|
+
|
|
150
|
+
_main.js_ - the same. Nothing changed at all <br>
|
|
151
|
+
_router.js_
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import type { Server } from "@ublitzjs/core";
|
|
155
|
+
import { Router } from "@ublitzjs/router";
|
|
156
|
+
// again - better explanations in source repo.
|
|
157
|
+
// Generally, router looks like OpenAPI
|
|
158
|
+
const router = new Router({
|
|
159
|
+
// route
|
|
160
|
+
"/simple": {
|
|
161
|
+
// method
|
|
162
|
+
get(res) {
|
|
163
|
+
res.end("hello");
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
export default (server: Server) => {
|
|
168
|
+
router.bind(server).define("/simple", "get");
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
So here I showed you how to setup a main file and split your code into modules
|
|
173
|
+
|
|
174
|
+
# Headers
|
|
175
|
+
|
|
176
|
+
## HeadersMap
|
|
177
|
+
|
|
178
|
+
This class is used to convert all headers to ArrayBuffers and quickly set them on each request.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { HeadersMap, definePlugin, toAB, type Server } from "@ublitzjs/core";
|
|
182
|
+
// init headers
|
|
183
|
+
const htmlHeaders = new HeadersMap({
|
|
184
|
+
"Content-Type": "text/html",
|
|
185
|
+
});
|
|
186
|
+
// convert strings to ArrayBuffers -> it returns a function
|
|
187
|
+
const setHeaders = htmlHeaders.prepare();
|
|
188
|
+
|
|
189
|
+
export default (server: Server) => {
|
|
190
|
+
const html = toAB("<h1>Hello</h1>");
|
|
191
|
+
server.get("/", (res) => {
|
|
192
|
+
// and this function returns "res" object
|
|
193
|
+
setHeaders(res).end(html);
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Also there is HeadersMap.default (see it in the code itself), HeadersMap.baseObj (also in the code), and HeadersMap.remove (you remove unwanted headers)
|
|
199
|
+
|
|
200
|
+
## setCSP
|
|
201
|
+
|
|
202
|
+
Creates a string of CSP, using array parameters.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// in response (not recommended)
|
|
206
|
+
res.writeHeader(
|
|
207
|
+
"Content-Security-Policy",
|
|
208
|
+
setCSP({ "default-src": ["'self'"] })
|
|
209
|
+
);
|
|
210
|
+
// in HeadersMap
|
|
211
|
+
import { setCSP, HeadersMap, /*basic settings*/ CSPDirs } from "@ublitzjs";
|
|
212
|
+
new HeadersMap({
|
|
213
|
+
"Content-Security-Policy": setCSP(
|
|
214
|
+
{
|
|
215
|
+
...CSPDirs,
|
|
216
|
+
"connect-src": ["'self'", "https://example.com"],
|
|
217
|
+
},
|
|
218
|
+
/*remove directives in a rest param*/
|
|
219
|
+
"img-src",
|
|
220
|
+
"object-src"
|
|
221
|
+
),
|
|
222
|
+
}).prepare();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## lowHeaders
|
|
226
|
+
|
|
227
|
+
Greatly fits when you need to use request.getHeader("sec-websocket-extensions"), but miss a letter or two. <br>
|
|
228
|
+
See example below in "uWS types / DocumentedWS and DocumentedWSBehavior"
|
|
229
|
+
|
|
230
|
+
# registerAbort
|
|
231
|
+
|
|
232
|
+
this utility lets you extend your handlers in a very asynchronous manner and nothing will ever brake.<br>
|
|
233
|
+
Emitter used here is "tseep", which is faster than node:events.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import type { HttpResponse } from "@ublitzjs/core";
|
|
237
|
+
function myOnAbort(res: HttpResponse) {
|
|
238
|
+
res.emitter.once("abort", () => {
|
|
239
|
+
/*do something*/
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
server.get("/", (res) => {
|
|
243
|
+
// use it
|
|
244
|
+
registerAbort(res);
|
|
245
|
+
console.log(/*added on the response*/ res.aborted, res.emitter);
|
|
246
|
+
|
|
247
|
+
res.emitter.once("abort", () => {
|
|
248
|
+
console.log("ABORTED");
|
|
249
|
+
// as an example
|
|
250
|
+
stopDatabaseTransaction();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
myOnAbort();
|
|
254
|
+
/*here you something*/
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
It is heavily used in @ublitzjs/req-body and @ublitzjs/static to stop file streams
|
|
259
|
+
If you want a more interesting use-case, <a href="./examples/AsyncFunction.mts">Go here</a>
|
|
260
|
+
|
|
261
|
+
# uWS types
|
|
262
|
+
|
|
263
|
+
Another benefit of using this package are additional typescript docs for uWS, that they haven't added yet (I mean index.d.ts has no description yet).
|
|
264
|
+
|
|
265
|
+
## DeclarativeResponse
|
|
266
|
+
|
|
267
|
+
This class helps you define faster and more optimized controllers, because they are prebuilt before execution.<br>
|
|
268
|
+
|
|
269
|
+
Instead of sending response dynamically each time with <code>res.end()</code> you generate the whole response with all headers and body right on the start of the application.<br>
|
|
270
|
+
|
|
271
|
+
It is not something, that ublitzjs created (only <code>.writeHeaders</code> method), but rather just description it gives you with the class
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { DeclarativeResponse } from "@ublitzjs";
|
|
275
|
+
server.get(
|
|
276
|
+
"/fast-response",
|
|
277
|
+
new Declarative()
|
|
278
|
+
.writeHeader("Content-Type", "text/plain")
|
|
279
|
+
/*spec method*/ .writeHeaders({ Allow: "GET" })
|
|
280
|
+
.end("HI")
|
|
281
|
+
);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## DocumentedWS and DocumentedWSBehavior
|
|
285
|
+
|
|
286
|
+
These types add 3 methods to websocket object: sendFirstFragment, sendFragment, sendLastFragment.<br>
|
|
287
|
+
More detailed description <a href="./types/uws-types.d.ts">here</a>
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import type { DocumentedWSBehavior, lowHeaders } from "@ublitzjs/core";
|
|
291
|
+
import uWS from "uWebSockets.js";
|
|
292
|
+
const server = uWS.App();
|
|
293
|
+
server.ws("/*", {
|
|
294
|
+
upgrade(res, req, context) {
|
|
295
|
+
res.upgrade(
|
|
296
|
+
{ url: req.getUrl() },
|
|
297
|
+
req.getHeader</*adds typescript support*/ lowHeaders>(
|
|
298
|
+
"sec-websocket-key"
|
|
299
|
+
),
|
|
300
|
+
req.getHeader<lowHeaders>("sec-websocket-protocol"),
|
|
301
|
+
req.getHeader<lowHeaders>("sec-websocket-extensions"),
|
|
302
|
+
context
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
async open(ws) {
|
|
306
|
+
ws.sendFirstFragment("hello1\n");
|
|
307
|
+
ws.sendFragment("hello2\n");
|
|
308
|
+
ws.sendLastFragment("end hello");
|
|
309
|
+
},
|
|
310
|
+
} as DocumentedWSBehavior<{}> as any);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
# Bundling
|
|
314
|
+
|
|
315
|
+
Best efficiency and start time can be achieved if the code is bundled and minified.<br>
|
|
316
|
+
For this purpose you can use "esbuild" (at least it was tested and worked for both cjs and esm formats).<br>
|
|
317
|
+
The only thing to remember: when you use it for bundling, don't forget to put "uWebSockets.js" to "external" array.<br>
|
|
318
|
+
<a href="./examples/esbuild.mjs">Example 1</a><br>
|
|
319
|
+
<a href="./tests/esbuild/build.mjs">Dynamic example 2</a>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var { toAB } = require("./index.cjs");
|
|
3
|
+
var c411 = toAB("411");
|
|
4
|
+
var c400 = toAB("400");
|
|
5
|
+
var c413 = toAB("413");
|
|
6
|
+
var c405 = toAB("405");
|
|
7
|
+
var c404 = toAB("404");
|
|
8
|
+
var c405Message = toAB("Method is not allowed");
|
|
9
|
+
var allowHeader = toAB("Allow");
|
|
10
|
+
function checkContentLength(res, req) {
|
|
11
|
+
var CL = Number(req.getHeader("content-length"));
|
|
12
|
+
if (!CL) {
|
|
13
|
+
res.finished = true;
|
|
14
|
+
res.cork(() => res.writeStatus(c411).endWithoutBody(0, true));
|
|
15
|
+
throw new Error("Wrong content-length", { cause: { CL } });
|
|
16
|
+
}
|
|
17
|
+
return CL;
|
|
18
|
+
}
|
|
19
|
+
function badRequest(res, error, causeForYou) {
|
|
20
|
+
res.finished = true;
|
|
21
|
+
if (!res.aborted) res.cork(() => res.writeStatus(c400).end(toAB(error)));
|
|
22
|
+
throw new Error("Bad request", { cause: causeForYou });
|
|
23
|
+
}
|
|
24
|
+
function tooLargeBody(res, limit) {
|
|
25
|
+
var message = toAB("Body is too large. Limit in bytes - " + limit);
|
|
26
|
+
if (!res.aborted) res.cork(() => res.writeStatus(c413).end(message));
|
|
27
|
+
res.finished = true;
|
|
28
|
+
throw new Error("body too large", { cause: { limit } });
|
|
29
|
+
}
|
|
30
|
+
function seeOtherMethods(methodsArr) {
|
|
31
|
+
var methods = toAB(
|
|
32
|
+
methodsArr
|
|
33
|
+
.map((method) => method.toUpperCase())
|
|
34
|
+
.join(", ")
|
|
35
|
+
.replace("DEL", "DELETE")
|
|
36
|
+
.replace(/ WS,*/g, "")
|
|
37
|
+
);
|
|
38
|
+
return (res) =>
|
|
39
|
+
res.writeStatus(c405).writeHeader(allowHeader, methods).end(c405Message);
|
|
40
|
+
}
|
|
41
|
+
function notFoundConstructor(message = "Not found") {
|
|
42
|
+
var mes = toAB(message);
|
|
43
|
+
return (res) => res.writeStatus(c404).end(mes, true);
|
|
44
|
+
}
|
|
45
|
+
module.exports = {
|
|
46
|
+
badRequest,
|
|
47
|
+
checkContentLength,
|
|
48
|
+
notFoundConstructor,
|
|
49
|
+
seeOtherMethods,
|
|
50
|
+
tooLargeBody,
|
|
51
|
+
c400,
|
|
52
|
+
c404,
|
|
53
|
+
c405,
|
|
54
|
+
c411,
|
|
55
|
+
c413,
|
|
56
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var { toAB } = require("./index.cjs");
|
|
3
|
+
var helmetHeaders = {
|
|
4
|
+
"X-Content-Type-Options": "nosniff",
|
|
5
|
+
"X-DNS-Prefetch-Control": "off",
|
|
6
|
+
"X-Frame-Options": "DENY",
|
|
7
|
+
"Referrer-Policy": "same-origin",
|
|
8
|
+
"X-Permitted-Cross-Domain-Policies": "none",
|
|
9
|
+
"X-Download-Options": "noopen",
|
|
10
|
+
"Cross-Origin-Resource-Policy": "same-origin",
|
|
11
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
12
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
13
|
+
"Origin-Agent-Cluster": "?1",
|
|
14
|
+
//"Content-Security-Policy-Report-Only":"",
|
|
15
|
+
//"Strict-Transport-Security":`max-age=${60 * 60 * 24 * 365}; includeSubDomains`,
|
|
16
|
+
};
|
|
17
|
+
class HeadersMap extends Map {
|
|
18
|
+
constructor(opts) {
|
|
19
|
+
super();
|
|
20
|
+
this.currentHeaders = opts;
|
|
21
|
+
}
|
|
22
|
+
remove(...keys) {
|
|
23
|
+
keys.forEach((key) => delete this.currentHeaders[key]);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
prepare() {
|
|
27
|
+
for (const key in this.currentHeaders)
|
|
28
|
+
this.set(toAB(key), toAB(this.currentHeaders[key]));
|
|
29
|
+
return (this.currentHeaders = void 0), (res) => this.toRes(res);
|
|
30
|
+
}
|
|
31
|
+
toRes = (res) =>
|
|
32
|
+
res.cork(() => this.forEach((value, key) => res.writeHeader(key, value)));
|
|
33
|
+
static baseObj = helmetHeaders;
|
|
34
|
+
static default = new HeadersMap({ ...HeadersMap.baseObj }).prepare();
|
|
35
|
+
}
|
|
36
|
+
function setCSP(mainCSP, ...remove) {
|
|
37
|
+
var key;
|
|
38
|
+
var CSPstring = "";
|
|
39
|
+
remove.forEach((dir) => delete mainCSP[dir]);
|
|
40
|
+
for (key in mainCSP) CSPstring += `${key} ${mainCSP[key].join(" ")}; `;
|
|
41
|
+
return CSPstring;
|
|
42
|
+
}
|
|
43
|
+
var CSPDirs = {
|
|
44
|
+
"default-src": ["'self'"],
|
|
45
|
+
"base-uri": ["'self'"],
|
|
46
|
+
"font-src": ["'self'"],
|
|
47
|
+
"form-action": ["'self'"],
|
|
48
|
+
"frame-ancestors": ["'none'"],
|
|
49
|
+
"img-src": ["'self'"],
|
|
50
|
+
"connect-src": ["'self'"],
|
|
51
|
+
"object-src": ["'none'"],
|
|
52
|
+
"script-src": ["'self'"],
|
|
53
|
+
"script-src-attr": ["'none'"],
|
|
54
|
+
"script-src-elem": ["'self'"],
|
|
55
|
+
"style-src": ["'self'"],
|
|
56
|
+
"style-src-attr": ["'none'"],
|
|
57
|
+
"style-src-elem": ["'self'"],
|
|
58
|
+
"trusted-types": ["'none'"],
|
|
59
|
+
"upgrade-insecure-requests": [],
|
|
60
|
+
"worker-src": ["'self'"],
|
|
61
|
+
"media-src": ["'self'"],
|
|
62
|
+
};
|
|
63
|
+
module.exports = { CSPDirs, HeadersMap, setCSP };
|
package/cjs/index.cjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var uWS = require("uWebSockets.js");
|
|
3
|
+
var { Buffer } = require("node:buffer");
|
|
4
|
+
|
|
5
|
+
var { EventEmitter } = require("tseep");
|
|
6
|
+
function registerAbort(res) {
|
|
7
|
+
if (typeof res.aborted === "boolean")
|
|
8
|
+
throw new Error("abort already registered");
|
|
9
|
+
res.aborted = false;
|
|
10
|
+
res.emitter = new EventEmitter();
|
|
11
|
+
return res.onAborted(() => {
|
|
12
|
+
res.aborted = true;
|
|
13
|
+
res.emitter.emit("abort");
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
var closure = (param) => param();
|
|
17
|
+
function extendApp(app, ...rest) {
|
|
18
|
+
app.register = function (plugin) {
|
|
19
|
+
return plugin(this), this;
|
|
20
|
+
};
|
|
21
|
+
app.onError = function (fn) {
|
|
22
|
+
return (this._errHandler = fn), this;
|
|
23
|
+
};
|
|
24
|
+
app._startPromises = [];
|
|
25
|
+
app.ready = function () {
|
|
26
|
+
return Promise.all(this._startPromises).then(
|
|
27
|
+
() => (this._startPromises = [])
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
app.route = function (opts, plugins) {
|
|
31
|
+
if (plugins) for (const p of plugins) p(opts, this);
|
|
32
|
+
return this[opts.method](opts.path, opts.controller);
|
|
33
|
+
};
|
|
34
|
+
for (const extension of rest) Object.assign(app, extension);
|
|
35
|
+
return app;
|
|
36
|
+
}
|
|
37
|
+
function toAB(data) {
|
|
38
|
+
var NodeBuf = data instanceof Buffer ? data : Buffer.from(data);
|
|
39
|
+
return NodeBuf.buffer.slice(
|
|
40
|
+
NodeBuf.byteOffset,
|
|
41
|
+
NodeBuf.byteOffset + NodeBuf.byteLength
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
var DeclarativeResponse = uWS.DeclarativeResponse;
|
|
45
|
+
DeclarativeResponse.prototype.writeHeaders = function (headers) {
|
|
46
|
+
for (const key in headers) this.writeHeader(key, headers[key]);
|
|
47
|
+
return this;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
DeclarativeResponse,
|
|
52
|
+
extendApp,
|
|
53
|
+
registerAbort,
|
|
54
|
+
toAB,
|
|
55
|
+
closure,
|
|
56
|
+
};
|
|
57
|
+
Object.assign(
|
|
58
|
+
module.exports,
|
|
59
|
+
require("./http-headers.cjs"),
|
|
60
|
+
require("./http-codes.cjs")
|
|
61
|
+
);
|
package/logo.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { toAB } from "./index.mjs";
|
|
3
|
+
export var c411 = toAB("411");
|
|
4
|
+
export var c400 = toAB("400");
|
|
5
|
+
export var c413 = toAB("413");
|
|
6
|
+
export var c405 = toAB("405");
|
|
7
|
+
export var c404 = toAB("404");
|
|
8
|
+
var c405Message = toAB("Method is not allowed");
|
|
9
|
+
var allowHeader = toAB("Allow");
|
|
10
|
+
var checkHeader = toAB("content-length");
|
|
11
|
+
var checkMessage = toAB("Content-Length required to be > 0");
|
|
12
|
+
function checkContentLength(res, req) {
|
|
13
|
+
var CL = Number(req.getHeader(checkHeader));
|
|
14
|
+
if (!CL) {
|
|
15
|
+
res.finished = true;
|
|
16
|
+
res.cork(() => res.writeStatus(c411).end(checkMessage));
|
|
17
|
+
throw new Error("Wrong content-length", { cause: { CL } });
|
|
18
|
+
}
|
|
19
|
+
return CL;
|
|
20
|
+
}
|
|
21
|
+
function badRequest(res, error, causeForYou) {
|
|
22
|
+
res.finished = true;
|
|
23
|
+
if (!res.aborted) res.cork(() => res.writeStatus(c400).end(toAB(error)));
|
|
24
|
+
throw new Error("Bad request", { cause: causeForYou });
|
|
25
|
+
}
|
|
26
|
+
function tooLargeBody(res, limit) {
|
|
27
|
+
var message = toAB("Body is too large. Limit in bytes - " + limit);
|
|
28
|
+
if (!res.aborted) res.cork(() => res.writeStatus(c413).end(message));
|
|
29
|
+
res.finished = true;
|
|
30
|
+
}
|
|
31
|
+
function seeOtherMethods(methodsArr) {
|
|
32
|
+
var methods = toAB(
|
|
33
|
+
methodsArr
|
|
34
|
+
.map((method) => method.toUpperCase())
|
|
35
|
+
.join(", ")
|
|
36
|
+
.replace("DEL", "DELETE")
|
|
37
|
+
.replace(/ WS,*/g, "")
|
|
38
|
+
);
|
|
39
|
+
return (res) =>
|
|
40
|
+
res.writeStatus(c405).writeHeader(allowHeader, methods).end(c405Message);
|
|
41
|
+
}
|
|
42
|
+
function notFoundConstructor(message = "Not found") {
|
|
43
|
+
var mes = toAB(message);
|
|
44
|
+
return (res) => res.writeStatus(c404).end(mes, true);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
badRequest,
|
|
48
|
+
checkContentLength,
|
|
49
|
+
notFoundConstructor,
|
|
50
|
+
seeOtherMethods,
|
|
51
|
+
tooLargeBody,
|
|
52
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { toAB } from "./index.mjs";
|
|
3
|
+
var helmetHeaders = {
|
|
4
|
+
"X-Content-Type-Options": "nosniff",
|
|
5
|
+
"X-DNS-Prefetch-Control": "off",
|
|
6
|
+
"X-Frame-Options": "DENY",
|
|
7
|
+
"Referrer-Policy": "same-origin",
|
|
8
|
+
"X-Permitted-Cross-Domain-Policies": "none",
|
|
9
|
+
"X-Download-Options": "noopen",
|
|
10
|
+
"Cross-Origin-Resource-Policy": "same-origin",
|
|
11
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
12
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
13
|
+
"Origin-Agent-Cluster": "?1",
|
|
14
|
+
//"Content-Security-Policy-Report-Only":"",
|
|
15
|
+
//"Strict-Transport-Security":`max-age=${60 * 60 * 24 * 365}; includeSubDomains`,
|
|
16
|
+
};
|
|
17
|
+
class HeadersMap extends Map {
|
|
18
|
+
constructor(opts) {
|
|
19
|
+
super();
|
|
20
|
+
this.currentHeaders = opts;
|
|
21
|
+
}
|
|
22
|
+
remove(...keys) {
|
|
23
|
+
return keys.forEach((key) => delete this.currentHeaders[key]), this;
|
|
24
|
+
}
|
|
25
|
+
prepare() {
|
|
26
|
+
for (const key in this.currentHeaders)
|
|
27
|
+
this.set(toAB(key), toAB(this.currentHeaders[key]));
|
|
28
|
+
return (this.currentHeaders = void 0), (res) => this.toRes(res);
|
|
29
|
+
}
|
|
30
|
+
toRes = (res) =>
|
|
31
|
+
res.cork(() => this.forEach((value, key) => res.writeHeader(key, value)));
|
|
32
|
+
static baseObj = helmetHeaders;
|
|
33
|
+
static default = new HeadersMap({ ...HeadersMap.baseObj }).prepare();
|
|
34
|
+
}
|
|
35
|
+
function setCSP(mainCSP, ...remove) {
|
|
36
|
+
var key;
|
|
37
|
+
var CSPstring = "";
|
|
38
|
+
remove.forEach((dir) => delete mainCSP[dir]);
|
|
39
|
+
for (key in mainCSP) CSPstring += `${key} ${mainCSP[key].join(" ")}; `;
|
|
40
|
+
return CSPstring;
|
|
41
|
+
}
|
|
42
|
+
var CSPDirs = {
|
|
43
|
+
"default-src": ["'self'"],
|
|
44
|
+
"base-uri": ["'self'"],
|
|
45
|
+
"font-src": ["'self'"],
|
|
46
|
+
"form-action": ["'self'"],
|
|
47
|
+
"frame-ancestors": ["'none'"],
|
|
48
|
+
"img-src": ["'self'"],
|
|
49
|
+
"connect-src": ["'self'"],
|
|
50
|
+
"object-src": ["'none'"],
|
|
51
|
+
"script-src": ["'self'"],
|
|
52
|
+
"script-src-attr": ["'none'"],
|
|
53
|
+
"script-src-elem": ["'self'"],
|
|
54
|
+
"style-src": ["'self'"],
|
|
55
|
+
"style-src-attr": ["'none'"],
|
|
56
|
+
"style-src-elem": ["'self'"],
|
|
57
|
+
"trusted-types": ["'none'"],
|
|
58
|
+
"worker-src": ["'self'"],
|
|
59
|
+
"media-src": ["'self'"],
|
|
60
|
+
};
|
|
61
|
+
export { CSPDirs, HeadersMap, setCSP };
|