@ublitzjs/core 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +3 -1
- package/dist/cjs/http-codes.js +24 -17
- package/dist/cjs/http-headers.js +100 -12
- package/dist/cjs/index.js +6 -1
- package/dist/esm/http-codes.js +24 -17
- package/dist/esm/http-headers.js +90 -10
- package/dist/esm/index.js +6 -1
- package/dist/types/http-codes.d.ts +15 -6
- package/dist/types/http-headers.d.ts +76 -16
- package/dist/types/index.d.ts +34 -5
- package/package.json +10 -2
- package/USAGE.md +0 -330
- package/babel.config.json +0 -8
- package/bun.lock +0 -627
- package/logo.png +0 -0
- package/src/http-codes.ts +0 -94
- package/src/http-headers.ts +0 -1136
- package/src/index.ts +0 -360
- package/tmp/cjs.cjs +0 -75
- package/tmp/esm.mjs +0 -75
- package/tsconfig.esm.json +0 -9
- package/tsconfig.json +0 -30
- package/tsconfig.types.json +0 -11
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import { type HttpMethods } from "./index.js";
|
|
2
|
+
export declare var helmentHeaders: {
|
|
3
|
+
readonly "X-Content-Type-Options": "nosniff";
|
|
4
|
+
readonly "X-DNS-Prefetch-Control": "off";
|
|
5
|
+
readonly "X-Frame-Options": "DENY";
|
|
6
|
+
readonly "Referrer-Policy": "same-origin";
|
|
7
|
+
readonly "X-Permitted-Cross-Domain-Policies": "none";
|
|
8
|
+
readonly "X-Download-Options": "noopen";
|
|
9
|
+
readonly "Cross-Origin-Resource-Policy": "same-origin";
|
|
10
|
+
readonly "Cross-Origin-Opener-Policy": "same-origin";
|
|
11
|
+
readonly "Cross-Origin-Embedder-Policy": "require-corp";
|
|
12
|
+
readonly "Origin-Agent-Cluster": "?1";
|
|
13
|
+
};
|
|
1
14
|
import type { HttpResponse as uwsHttpResponse } from "uWebSockets.js";
|
|
2
|
-
type helmetHeadersT = {
|
|
15
|
+
export type helmetHeadersT = {
|
|
3
16
|
/**
|
|
4
17
|
* By adding this header you can declare that your site should only load resources that have explicitly opted-in to being loaded across origin.
|
|
5
18
|
* "require-corp" LETS YOU USE new SharedArrayBuffer()
|
|
@@ -49,7 +62,7 @@ type helmetHeadersT = {
|
|
|
49
62
|
* */
|
|
50
63
|
"X-XSS-Protection"?: string;
|
|
51
64
|
};
|
|
52
|
-
type CORSHeader = {
|
|
65
|
+
export type CORSHeader = {
|
|
53
66
|
/**
|
|
54
67
|
* The HTTP Access-Control-Allow-Credentials response header tells browsers whether the server allows credentials to be included in cross-origin HTTP requests.
|
|
55
68
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials
|
|
@@ -96,7 +109,7 @@ type CORSHeader = {
|
|
|
96
109
|
*/
|
|
97
110
|
"Access-Control-Request-Method": string;
|
|
98
111
|
};
|
|
99
|
-
type experimentalHeaders = {
|
|
112
|
+
export type experimentalHeaders = {
|
|
100
113
|
/**
|
|
101
114
|
* @experimental
|
|
102
115
|
* The HTTP Attribution-Reporting-Eligible request header indicates that the corresponding response is eligible to register an attribution source or trigger.
|
|
@@ -350,7 +363,7 @@ type experimentalHeaders = {
|
|
|
350
363
|
* */
|
|
351
364
|
"Use-As-Dictionary": string;
|
|
352
365
|
};
|
|
353
|
-
type simpleHeaders = {
|
|
366
|
+
export type simpleHeaders = {
|
|
354
367
|
/**
|
|
355
368
|
* indicates which content types, expressed as MIME types, the sender is able to understand
|
|
356
369
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept
|
|
@@ -521,7 +534,7 @@ type simpleHeaders = {
|
|
|
521
534
|
It should only be included in 206 Partial Content or 416 Range Not Satisfiable responses.
|
|
522
535
|
@see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Range
|
|
523
536
|
*/
|
|
524
|
-
"Content-Range":
|
|
537
|
+
"Content-Range": `bytes ${number}-${number}/*` | `bytes ${number}-${number}/${number}` | `bytes */${number}`;
|
|
525
538
|
/**
|
|
526
539
|
* The HTTP Content-Security-Policy response header allows website administrators to control resources the user agent is allowed to load for a given page.
|
|
527
540
|
*
|
|
@@ -544,7 +557,7 @@ type simpleHeaders = {
|
|
|
544
557
|
* You know this one. It is "text/html" or "video/mp4" or whatever
|
|
545
558
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Type
|
|
546
559
|
*/
|
|
547
|
-
"Content-Type": string;
|
|
560
|
+
"Content-Type": `${string}/${string}` | `${string}/${string}; ${string}`;
|
|
548
561
|
/**
|
|
549
562
|
* The HTTP Cookie request header contains stored HTTP cookies associated with the server (i.e., previously sent by the server with the Set-Cookie header or set in JavaScript using Document.cookie)
|
|
550
563
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cookie
|
|
@@ -565,7 +578,7 @@ type simpleHeaders = {
|
|
|
565
578
|
* @see
|
|
566
579
|
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
|
|
567
580
|
* */
|
|
568
|
-
Etag: string
|
|
581
|
+
Etag: `W/"${string}"` | `"${string}"`;
|
|
569
582
|
/**
|
|
570
583
|
*The HTTP Expect request header indicates that there are expectations that need to be met by the server in order to handle the complete request successfully.
|
|
571
584
|
|
|
@@ -609,7 +622,7 @@ type simpleHeaders = {
|
|
|
609
622
|
* The HTTP If-Match request header makes a request conditional. A server will return resources for GET and HEAD methods, or upload resource for PUT and other non-safe methods, only if the resource matches one of the ETag values in the If-Match request header. If the conditional does not match, the 412 Precondition Failed response is returned instead.
|
|
610
623
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/If-Match
|
|
611
624
|
* */
|
|
612
|
-
"If-Match": string;
|
|
625
|
+
"If-Match": `W/"${string}"` | `"${string}"` | "*";
|
|
613
626
|
/**
|
|
614
627
|
* The HTTP If-Modified-Since request header makes a request conditional. The server sends back the requested resource, with a 200 status, only if it has been modified after the date in the If-Modified-Since header. If the resource has not been modified since, the response is a 304 without any body, and the Last-Modified response header of the previous request contains the date of the last modification.
|
|
615
628
|
|
|
@@ -739,7 +752,7 @@ type simpleHeaders = {
|
|
|
739
752
|
If the server sends back ranges, it uses the 206 Partial Content status code for the response. If the ranges are invalid, the server returns the 416 Range Not Satisfiable error.
|
|
740
753
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Range
|
|
741
754
|
* */
|
|
742
|
-
Range:
|
|
755
|
+
Range: `bytes=${number}-${number}` | `bytes=${number}-` | `bytes=-${number}`;
|
|
743
756
|
/**
|
|
744
757
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referer
|
|
745
758
|
* */
|
|
@@ -912,10 +925,33 @@ export type BaseHeaders = Partial<simpleHeaders & helmetHeadersT & experimentalH
|
|
|
912
925
|
[key: string]: string;
|
|
913
926
|
}>;
|
|
914
927
|
/**
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
928
|
+
* This function is created as a replacement for "HeadersMap" (which became deprecated in 1.1.0).
|
|
929
|
+
* The most overhead when sending request doesn't come from utf16 -> utf8 conversion, but from many subsequent res.writeHeader, which is JS->C++ switch.
|
|
930
|
+
* To reduce an amount of calling headers, you can use manual string concatenation and HTTP header separator - CRLF.
|
|
931
|
+
* @notice this function doesn't create completely ready data to send. Look a the example below
|
|
932
|
+
* @notice2 its return-type LIES. In fact it is just a concatenated string, but TS lies just for LSP help for the second param and, as a result, last header, value to which should be written in res.writeHeader
|
|
933
|
+
* @example
|
|
934
|
+
* //if all headers don't change
|
|
935
|
+
* var headers = staticHeaders({
|
|
936
|
+
* "Content-Type": "text/plain"
|
|
937
|
+
* }, "Etag") // However "Etag" doesn't have value yet
|
|
938
|
+
* * var littleFasterHeaders = Buffer.from(headers)
|
|
939
|
+
* server.get("/", (res)=>{
|
|
940
|
+
* // we put ETag's value to the right. This way we can combine static + dynamic headers in one single call
|
|
941
|
+
* res.writeHeader(littleFasterHeaders, 'W/"' + 123 + '"')
|
|
942
|
+
* res.end("ok")
|
|
943
|
+
*
|
|
944
|
+
* // underneath this is the same (gain speed but lose TypeScript. Not bad if your head is a compiler)
|
|
945
|
+
* res.writeHeader("Content-Type: text/plain\r\nETag", 'W/"' + 123 + '"')
|
|
946
|
+
* // you can make right side look same as well
|
|
947
|
+
* res.writeHeader<string>("Content-Type", 'text/plain\r\nETag: W/"' + 123 + '"')
|
|
948
|
+
* })
|
|
949
|
+
*
|
|
950
|
+
* */
|
|
951
|
+
export declare function staticHeaders<T extends keyof RequiredBaseHeaders | (string & {})>(headers: BaseHeaders, likelyDynamicHeader: T): T;
|
|
952
|
+
/**
|
|
953
|
+
* @deprecated because using ArrayBuffers vs strings in headers doesn't bring much benefit, as it turned out after some benchmarks (+ it uses Map class, so that's even slower). It is going to be removed in 2.0.0. When is it coming? Who knows.
|
|
954
|
+
* And YES, I removed description of it. Stick to "staticHeaders" or CRLF manipulations
|
|
919
955
|
*/
|
|
920
956
|
export declare class HeadersMap<Opts extends BaseHeaders> extends Map {
|
|
921
957
|
currentHeaders: undefined | Opts;
|
|
@@ -963,8 +999,6 @@ type CSP = Partial<typeof CSPDirs & {
|
|
|
963
999
|
* Usual CSP directories. If you want more dirs:
|
|
964
1000
|
* 1) I will put more in soon
|
|
965
1001
|
* 2) use string concatenation (use BASE)
|
|
966
|
-
* @example
|
|
967
|
-
* new HeadersMap({...HeadersMap.baseObj, "Content-Security-Policy":setCSP({...CSPDirs}) + " your-dir: 'self';"})
|
|
968
1002
|
*/
|
|
969
1003
|
export declare var CSPDirs: {
|
|
970
1004
|
/**
|
|
@@ -1036,5 +1070,31 @@ export declare var CSPDirs: {
|
|
|
1036
1070
|
*/
|
|
1037
1071
|
"media-src": string[];
|
|
1038
1072
|
};
|
|
1039
|
-
export type
|
|
1073
|
+
export type RequiredBaseHeaders = (simpleHeaders & helmetHeadersT & experimentalHeaders & CORSHeader);
|
|
1074
|
+
export type lowHeaders = Lowercase<keyof RequiredBaseHeaders>;
|
|
1075
|
+
/**
|
|
1076
|
+
* "Range" http header requires validation, so here you get it
|
|
1077
|
+
* @example
|
|
1078
|
+
* var data = Buffer.allocUnsafe(1024*1024)
|
|
1079
|
+
* server.get("/largeData", (res, req)=>{
|
|
1080
|
+
* var range = req.getHeader("range")
|
|
1081
|
+
* if(!range) return res.end(data)
|
|
1082
|
+
* // so parseRange might modify "range" to suit requirements + check if header is right
|
|
1083
|
+
* var parsedRange = parseRange(range, data.length - 1, 64*1024)
|
|
1084
|
+
* // might be
|
|
1085
|
+
* if(!parsedRange.ok) return res.writeStatus(parsedRange.code).end("bad")
|
|
1086
|
+
* })
|
|
1087
|
+
* */
|
|
1088
|
+
export declare function parseRange(range: string, maxEnd: number, maxChunk: number): {
|
|
1089
|
+
ok: false;
|
|
1090
|
+
code: "400" | "416";
|
|
1091
|
+
} | {
|
|
1092
|
+
ok: true;
|
|
1093
|
+
start: number;
|
|
1094
|
+
end: number;
|
|
1095
|
+
};
|
|
1096
|
+
/**
|
|
1097
|
+
* This function takes methods that you registered on an endpoint and formats them to suit "Allow" header.
|
|
1098
|
+
* */
|
|
1099
|
+
export declare function typedAllowHeader(methodsArr: HttpMethods[]): string;
|
|
1040
1100
|
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import type { BaseHeaders, lowHeaders } from "./http-headers.js";
|
|
1
|
+
import type { BaseHeaders, lowHeaders, RequiredBaseHeaders } from "./http-headers.js";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
3
|
import { EventEmitter } from "tseep";
|
|
4
4
|
import type { CompressOptions, WebSocket, HttpRequest as uwsHttpRequest, TemplatedApp, HttpResponse as uwsHttpResponse, RecognizedString, us_socket_context_t } from "uWebSockets.js";
|
|
5
5
|
/**
|
|
6
6
|
* function to effortlessly mark response as aborted AND to attach an event emitter, so that you can easily scale the handler. If you don't need event emitter and only some res.aborted - set it by yourself (no overkill for the handler)
|
|
7
|
+
* If some utility expect import("@ublitzjs/core").HttpResponse, it means that they response to first go through this registerAbort
|
|
7
8
|
* @param res
|
|
9
|
+
* @example
|
|
10
|
+
* console.log(Boolean(res.emitter)) // false
|
|
11
|
+
* registerAbort(res)
|
|
12
|
+
* console.log(Boolean(res.emitter)) // true
|
|
8
13
|
*/
|
|
9
14
|
export declare function registerAbort(res: uwsHttpResponse): HttpResponse;
|
|
10
15
|
/**
|
|
@@ -88,26 +93,47 @@ export interface HttpResponse<UserDataForWS = {}> extends uwsHttpResponse {
|
|
|
88
93
|
*/
|
|
89
94
|
collect: (...any: any[]) => any;
|
|
90
95
|
/**
|
|
91
|
-
* An event emitter, which lets you subscribe several listeners to "abort" event OR your own events, defined with Symbol()
|
|
96
|
+
* An event emitter, which lets you subscribe several listeners to "abort" event OR your own events, defined with Symbol() | other string
|
|
97
|
+
* @example
|
|
98
|
+
* res.emitter.once("abort", ()=>{
|
|
99
|
+
* console.log("I have to clean up some random file descriptor")
|
|
100
|
+
* // do cleanup
|
|
101
|
+
* })
|
|
92
102
|
*/
|
|
93
103
|
emitter: EventEmitter<{
|
|
94
104
|
abort: () => void;
|
|
95
105
|
[k: symbol]: (...any: any[]) => void;
|
|
96
106
|
}>;
|
|
97
107
|
/**
|
|
98
|
-
* changes when res.onAborted fires
|
|
108
|
+
* changes when res.onAborted fires (you have to use registerAbort for this)
|
|
99
109
|
*/
|
|
100
110
|
aborted?: boolean;
|
|
101
111
|
/**
|
|
102
112
|
* You should set it manually when ending the response. Particularly useful if some error has fired and you are doubting whether res.aborted is a sufficient flag.
|
|
113
|
+
* @example
|
|
114
|
+
* function endResponse(res) {
|
|
115
|
+
* if(res.aborted) return;
|
|
116
|
+
* res.end("alright")
|
|
117
|
+
* res.finished = true;
|
|
118
|
+
* }
|
|
103
119
|
*/
|
|
104
120
|
finished?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Writes key and value to HTTP response. See writeStatus and corking.
|
|
123
|
+
* It can actually write several values in a row, just be careful with string concatenation. If headers never change - use staticHeaders (and save typescript safety)
|
|
124
|
+
* @example
|
|
125
|
+
* res.writeHeader("Content-Type", "text/plain").writeHeader("Accept-Ranges", "bytes")
|
|
126
|
+
* // and a faster trick
|
|
127
|
+
* res.writeHeader("Content-Type: text/plain\r\nAccept-Ranges", "bytes")
|
|
128
|
+
* */
|
|
129
|
+
writeHeader<Key extends keyof RequiredBaseHeaders | Omit<RecognizedString, string> | (string & {})>(key: Key, val: Key extends keyof RequiredBaseHeaders ? RequiredBaseHeaders[Key] : RecognizedString): HttpResponse;
|
|
105
130
|
}
|
|
106
131
|
/**This HttpRequest is same as original uWS.HttpRequest, but getHeader method is typed for additional tips
|
|
107
132
|
* @example
|
|
108
133
|
* import {lowHeaders} from "@ublitzjs/core"
|
|
109
|
-
* // some handler later
|
|
134
|
+
* // some handler later (not compulsory to specify lowHeaders. It is a default behaviour)
|
|
110
135
|
* req.getHeader<lowHeaders>("content-type")
|
|
136
|
+
*
|
|
111
137
|
*/
|
|
112
138
|
export interface HttpRequest extends uwsHttpRequest {
|
|
113
139
|
getHeader<T extends RecognizedString = lowHeaders>(a: T): string;
|
|
@@ -128,7 +154,7 @@ type MergeObjects<T extends any[]> = T extends [infer First, ...infer Rest] ? Fi
|
|
|
128
154
|
*/
|
|
129
155
|
export declare function extendApp<T extends object[]>(app: TemplatedApp, ...rest: T): Server & MergeObjects<T>;
|
|
130
156
|
/**
|
|
131
|
-
* conversion to
|
|
157
|
+
* @deprecated this conversion to arrayBuffer is not really necessary. You just have Buffer.from(""). Slicing that is rarely needed, and even if it is, that takes 4 lines of code
|
|
132
158
|
*/
|
|
133
159
|
export declare function toAB(data: Buffer | string): ArrayBuffer;
|
|
134
160
|
/**
|
|
@@ -220,6 +246,9 @@ export interface DocumentedWSBehavior<UserData> {
|
|
|
220
246
|
*/
|
|
221
247
|
upgrade?: (res: HttpResponse<UserData>, req: HttpRequest, context: us_socket_context_t) => void | Promise<void>;
|
|
222
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* let's believe, that in 20.61.0+ versions we get writeStatus.
|
|
251
|
+
* */
|
|
223
252
|
export type DeclarativeResType = {
|
|
224
253
|
writeHeader(key: string, value: string): DeclarativeResType;
|
|
225
254
|
writeQueryValue(key: string): DeclarativeResType;
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ublitzjs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"types": "./dist/types/index.d.ts",
|
|
5
|
+
"files": ["dist", "LICENSE"],
|
|
6
|
+
"typesVersions": {
|
|
7
|
+
"*": {
|
|
8
|
+
".": ["./dist/types/index.d.ts"]
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
5
12
|
"exports": {
|
|
6
13
|
"types": "./dist/types/index.d.ts",
|
|
7
14
|
"require": "./dist/cjs/index.js",
|
|
@@ -20,6 +27,7 @@
|
|
|
20
27
|
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
|
21
28
|
"@types/node": "^25.2.1",
|
|
22
29
|
"@types/ws": "^8.18.1",
|
|
30
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
23
31
|
"esbuild": "^0.25.12",
|
|
24
32
|
"tsd": "^0.33.0",
|
|
25
33
|
"vitest": "^4.0.8",
|
|
@@ -31,7 +39,7 @@
|
|
|
31
39
|
"build:cjs": "babel dist/esm --out-dir dist/cjs",
|
|
32
40
|
"build:types": "tsc -p tsconfig.types.json",
|
|
33
41
|
"build": "npm run build:types && npm run build:esm && npm run build:cjs",
|
|
34
|
-
"test": "tsd && vitest run"
|
|
42
|
+
"test": "tsd && vitest run tests/index.test.ts --coverage"
|
|
35
43
|
},
|
|
36
44
|
"tsd": {
|
|
37
45
|
"directory": "tests"
|
package/USAGE.md
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
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 in the code), 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 { extendApp, DocumentedWSBehavior, lowHeaders } from "@ublitzjs/core";
|
|
291
|
-
import uWS from "uWebSockets.js";
|
|
292
|
-
const server = extendApp(uWS.App()) // better for ts
|
|
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
|
-
close(ws){
|
|
311
|
-
//typed safety flag
|
|
312
|
-
ws.closed = true;
|
|
313
|
-
}
|
|
314
|
-
message(ws){
|
|
315
|
-
setTimeout(()=>{
|
|
316
|
-
//acts like res.aborted
|
|
317
|
-
if(ws.closed) return;
|
|
318
|
-
ws.send("hello")
|
|
319
|
-
}, 100);
|
|
320
|
-
},
|
|
321
|
-
});
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
# Bundling
|
|
325
|
-
|
|
326
|
-
Best efficiency and start time can be achieved if the code is bundled and minified.<br>
|
|
327
|
-
For this purpose you can use "esbuild" (at least it was tested and worked for both cjs and esm formats).<br>
|
|
328
|
-
The only thing to remember: when you use it for bundling, don't forget to put "uWebSockets.js" to "external" array.<br>
|
|
329
|
-
<a href="./examples/esbuild.mjs">Example 1</a><br>
|
|
330
|
-
<a href="./tests/esbuild.test.ts">Dynamic example 2</a>
|