@ublitzjs/core 0.1.1 → 1.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 +17 -14
- package/dist/cjs/http-codes.js +99 -0
- package/dist/cjs/http-headers.js +118 -0
- package/dist/cjs/index.js +104 -0
- package/dist/esm/http-codes.js +84 -0
- package/dist/esm/http-headers.js +119 -0
- package/dist/esm/index.js +71 -0
- package/dist/esm/package.json +1 -0
- package/dist/types/http-codes.d.ts +43 -0
- package/dist/types/http-headers.d.ts +1040 -0
- package/dist/types/index.d.ts +249 -0
- package/package.json +33 -7
- package/USAGE.md +0 -319
- package/cjs/http-codes.cjs +0 -56
- package/cjs/http-headers.cjs +0 -64
- package/cjs/index.cjs +0 -61
- package/logo.png +0 -0
- package/mjs/http-codes.mjs +0 -52
- package/mjs/http-headers.mjs +0 -63
- package/mjs/index.mjs +0 -51
- package/tsconfig.json +0 -41
- package/types/http-codes.d.ts +0 -54
- package/types/http-headers.d.ts +0 -1079
- package/types/index.d.ts +0 -170
- package/types/uws-types.d.ts +0 -123
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import type { BaseHeaders, lowHeaders } from "./http-headers.js";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { EventEmitter } from "tseep";
|
|
4
|
+
import type { CompressOptions, WebSocket, HttpRequest as uwsHttpRequest, TemplatedApp, HttpResponse as uwsHttpResponse, RecognizedString, us_socket_context_t } from "uWebSockets.js";
|
|
5
|
+
/**
|
|
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
|
+
* @param res
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerAbort(res: uwsHttpResponse): HttpResponse;
|
|
10
|
+
/**
|
|
11
|
+
* An extended version of uWS.App . It provides you with several features:
|
|
12
|
+
* 1) plugin registration (just like in Fastify);
|
|
13
|
+
* 2) "ready" function and _startPromises array (use it to make your code more asynchronous and safe)
|
|
14
|
+
* 3) "route" function for more descriptive adn extensible way of registering handlers
|
|
15
|
+
* 4) "onError" function (mainly used by @ublitzjs/router)
|
|
16
|
+
*/
|
|
17
|
+
export interface Server extends TemplatedApp {
|
|
18
|
+
/**
|
|
19
|
+
* It is same as plugins in Fastify -> you register some functionality in separate function
|
|
20
|
+
* @param plugin
|
|
21
|
+
* @returns itself (for chaining methods)
|
|
22
|
+
*/
|
|
23
|
+
register(plugin: (server: this) => void): this;
|
|
24
|
+
/** set global _errHandler (in fact it is used only by @ublitzjs/router) */
|
|
25
|
+
onError(fn: (error: Error, res: HttpResponse, data: any) => any): this;
|
|
26
|
+
_errHandler?(error: Error, res: HttpResponse, data: any): any;
|
|
27
|
+
/**some undocumented property in uWS - get it here */
|
|
28
|
+
addChildAppDescriptor(...any: any[]): this;
|
|
29
|
+
/**a function, which pushes promises to be awaited using "await server.ready()"*/
|
|
30
|
+
awaitLater(...promises: Promise<any>[]): void;
|
|
31
|
+
/**a function, which awaits all promises inside _startPromises array and then clears it*/
|
|
32
|
+
ready: () => Promise<any[]>;
|
|
33
|
+
/**
|
|
34
|
+
* simple array of promises. You can push several inside and await all of them using server.ready() method
|
|
35
|
+
*/
|
|
36
|
+
_startPromises: Promise<any>[];
|
|
37
|
+
/**
|
|
38
|
+
* this function allows you to create new handlers but more dynamically than uWS. Best use case - development. By default, the first param you pass includes a method, path, and a controller. However you can configure it for additional properties, which can be consumed on startup by second param - plugins
|
|
39
|
+
* @example
|
|
40
|
+
* server.route<onlyHttpMethods, {deprecated:boolean}>({
|
|
41
|
+
* method:"any",
|
|
42
|
+
* path: "/",
|
|
43
|
+
* controller(res){ res.end("hello") },
|
|
44
|
+
* deprecated:true
|
|
45
|
+
* },
|
|
46
|
+
* [
|
|
47
|
+
* (opts)=>{
|
|
48
|
+
* if(opts.deprecated) console.error("DEPRECATION FOUND", opts.path)
|
|
49
|
+
* }
|
|
50
|
+
* ])
|
|
51
|
+
*/
|
|
52
|
+
route<T extends HttpMethods, obj extends object = {}>(opts: routeFNOpts<T> & obj, plugins?: ((param: typeof opts, server: this) => void)[]): this;
|
|
53
|
+
get(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
54
|
+
post(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
55
|
+
options(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
56
|
+
del(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
57
|
+
patch(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
58
|
+
put(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
59
|
+
head(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
60
|
+
connect(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
61
|
+
trace(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
62
|
+
any(pattern: RecognizedString, handler: HttpControllerFn): this;
|
|
63
|
+
ws<UserData>(pattern: RecognizedString, behavior: DocumentedWSBehavior<UserData>): this;
|
|
64
|
+
}
|
|
65
|
+
export type routeFNOpts<T extends HttpMethods> = {
|
|
66
|
+
method: T;
|
|
67
|
+
path: RecognizedString;
|
|
68
|
+
controller: T extends "ws" ? DocumentedWSBehavior<any> : HttpControllerFn;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Utility for making closures. Exists for organizing code and caching values in non-global scope.
|
|
72
|
+
* @param param
|
|
73
|
+
* @returns what you passed
|
|
74
|
+
*/
|
|
75
|
+
export declare var closure: <T>(param: () => T) => T;
|
|
76
|
+
/**
|
|
77
|
+
* little more typed response which has:
|
|
78
|
+
* 1) "emitter", that comes from "tseep" package
|
|
79
|
+
* 2) "aborted" flag
|
|
80
|
+
* 3) "finished" flag
|
|
81
|
+
* 4) "collect" function, which comes from original uWS (and isn't documented), but the purpose remains unknown
|
|
82
|
+
* 5) "upgrade" function, though comes from original uWS, must not contain any names, which "DocumentedWS" has (or websocket object). ws.getUserData() return "ws" itself, so setting any data, which overlaps with already existing will impact your workflow. µBlitz.js doesn't add any lsp help here because it will brake "extends uwsHttpResponse" part.
|
|
83
|
+
*/
|
|
84
|
+
export interface HttpResponse<UserDataForWS = {}> extends uwsHttpResponse {
|
|
85
|
+
upgrade<UserData = UserDataForWS>(userData: UserData, secWebSocketKey: RecognizedString, secWebSocketProtocol: RecognizedString, secWebSocketExtensions: RecognizedString, context: us_socket_context_t): void;
|
|
86
|
+
/**
|
|
87
|
+
* This method actually exists in original uWebSockets.js, but is undocumented. I'll put it here for your IDE to be happy
|
|
88
|
+
*/
|
|
89
|
+
collect: (...any: any[]) => any;
|
|
90
|
+
/**
|
|
91
|
+
* An event emitter, which lets you subscribe several listeners to "abort" event OR your own events, defined with Symbol().
|
|
92
|
+
*/
|
|
93
|
+
emitter: EventEmitter<{
|
|
94
|
+
abort: () => void;
|
|
95
|
+
[k: symbol]: (...any: any[]) => void;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* changes when res.onAborted fires.
|
|
99
|
+
*/
|
|
100
|
+
aborted?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* 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.
|
|
103
|
+
*/
|
|
104
|
+
finished?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**This HttpRequest is same as original uWS.HttpRequest, but getHeader method is typed for additional tips
|
|
107
|
+
* @example
|
|
108
|
+
* import {lowHeaders} from "@ublitzjs/core"
|
|
109
|
+
* // some handler later
|
|
110
|
+
* req.getHeader<lowHeaders>("content-type")
|
|
111
|
+
*/
|
|
112
|
+
export interface HttpRequest extends uwsHttpRequest {
|
|
113
|
+
getHeader<T extends RecognizedString = lowHeaders>(a: T): string;
|
|
114
|
+
}
|
|
115
|
+
export type HttpControllerFn = (res: HttpResponse, req: HttpRequest) => any | Promise<any>;
|
|
116
|
+
/**
|
|
117
|
+
* only http methods without "ws"
|
|
118
|
+
*/
|
|
119
|
+
export type onlyHttpMethods = "get" | "post" | "del" | "patch" | "put" | "head" | "trace" | "options" | "connect" | "any";
|
|
120
|
+
/**
|
|
121
|
+
* all httpMethods with "ws" method
|
|
122
|
+
*/
|
|
123
|
+
export type HttpMethods = onlyHttpMethods | "ws";
|
|
124
|
+
type MergeObjects<T extends any[]> = T extends [infer First, ...infer Rest] ? First & MergeObjects<Rest extends any[] ? Rest : []> : {};
|
|
125
|
+
/**
|
|
126
|
+
* extends uWS.App() or uWS.SSLApp. See interface Server
|
|
127
|
+
* @param app uWS.App()
|
|
128
|
+
*/
|
|
129
|
+
export declare function extendApp<T extends object[]>(app: TemplatedApp, ...rest: T): Server & MergeObjects<T>;
|
|
130
|
+
/**
|
|
131
|
+
* conversion to ArrayBuffer ('cause transferring strings to uWS is really slow)
|
|
132
|
+
*/
|
|
133
|
+
export declare function toAB(data: Buffer | string): ArrayBuffer;
|
|
134
|
+
/**
|
|
135
|
+
* Thanks to "acarstoiu" github user for listing such methods here: "https://github.com/uNetworking/uWebSockets.js/issues/1165".
|
|
136
|
+
*/
|
|
137
|
+
type WebSocketFragmentation = {
|
|
138
|
+
/** Sends the first frame of a multi-frame message. More fragments are expected (possibly none), followed by a last fragment. Care must be taken so as not to interleave these fragments with whole messages (sent with WebSocket.send) or with other messages' fragments.
|
|
139
|
+
*
|
|
140
|
+
* Returns 1 for success, 2 for dropped due to backpressure limit, and 0 for built up backpressure that will drain over time. You can check backpressure before or after sending by calling getBufferedAmount().
|
|
141
|
+
*
|
|
142
|
+
* Make sure you properly understand the concept of backpressure. Check the backpressure example file.
|
|
143
|
+
*/
|
|
144
|
+
sendFirstFragment(message: RecognizedString, isBinary?: boolean, compress?: boolean): number;
|
|
145
|
+
/** Sends a continuation frame of a multi-frame message. More fragments are expected (possibly none), followed by a last fragment. In terms of sending data, a call to this method must follow a call to WebSocket.sendFirstFragment.
|
|
146
|
+
*
|
|
147
|
+
* Returns 1 for success, 2 for dropped due to backpressure limit, and 0 for built up backpressure that will drain over time. You can check backpressure before or after sending by calling getBufferedAmount().
|
|
148
|
+
*
|
|
149
|
+
* Make sure you properly understand the concept of backpressure. Check the backpressure example file.
|
|
150
|
+
*/
|
|
151
|
+
sendFragment(message: RecognizedString, compress?: boolean): number;
|
|
152
|
+
/** Sends the last continuation frame of a multi-frame message. In terms of sending data, a call to this method must follow a call to WebSocket.sendFirstFragment or WebSocket.sendFragment.
|
|
153
|
+
*
|
|
154
|
+
* Returns 1 for success, 2 for dropped due to backpressure limit, and 0 for built up backpressure that will drain over time. You can check backpressure before or after sending by calling getBufferedAmount().
|
|
155
|
+
*
|
|
156
|
+
* Make sure you properly understand the concept of backpressure. Check the backpressure example file.
|
|
157
|
+
*/
|
|
158
|
+
sendLastFragment(message: RecognizedString, compress?: boolean): number;
|
|
159
|
+
};
|
|
160
|
+
export interface DocumentedWS<UserData> extends WebSocket<UserData>, WebSocketFragmentation {
|
|
161
|
+
/**
|
|
162
|
+
* this is an additional property, which saves you from server crashing. In "close" handler you should add "ws.closed = true" and each time you perform any asynchronous work before sending a message run a check "if(ws.closed) { cleanup and quit }"
|
|
163
|
+
* @example
|
|
164
|
+
* import {setTimeout} from "node:timers/promises"
|
|
165
|
+
* server.ws("/", {
|
|
166
|
+
* close(ws){
|
|
167
|
+
* ws.closed = true;
|
|
168
|
+
* },
|
|
169
|
+
* async message(ws){
|
|
170
|
+
* await setTimeout(100); // simulate some db query
|
|
171
|
+
* if(!ws.closed) ws.send("handled message", false)
|
|
172
|
+
* }
|
|
173
|
+
* })
|
|
174
|
+
* */
|
|
175
|
+
closed: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* an additional property which might save websocket from overexhaustion
|
|
178
|
+
* */
|
|
179
|
+
drainEvent: Promise<void> | undefined;
|
|
180
|
+
/**
|
|
181
|
+
* an additional property to monitor queued messages, which can't be sent due to overexhaustion (need to wait for drain).
|
|
182
|
+
**/
|
|
183
|
+
queue: number;
|
|
184
|
+
}
|
|
185
|
+
export interface DocumentedWSBehavior<UserData> {
|
|
186
|
+
/** Maximum length of received message. If a client tries to send you a message larger than this, the connection is immediately closed. Defaults to 16 * 1024. */
|
|
187
|
+
maxPayloadLength?: number;
|
|
188
|
+
/** Whether or not we should automatically close the socket when a message is dropped due to backpressure. Defaults to false. */
|
|
189
|
+
closeOnBackpressureLimit?: boolean;
|
|
190
|
+
/** Maximum number of minutes a WebSocket may be connected before being closed by the server. 0 disables the feature. */
|
|
191
|
+
maxLifetime?: number;
|
|
192
|
+
/** Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest.
|
|
193
|
+
* Disable by using 0. Defaults to 120.
|
|
194
|
+
*/
|
|
195
|
+
idleTimeout?: number;
|
|
196
|
+
/** What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB. Defaults to uWS.DISABLED. */
|
|
197
|
+
compression?: CompressOptions;
|
|
198
|
+
/** Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout. Defaults to 64 * 1024. */
|
|
199
|
+
maxBackpressure?: number;
|
|
200
|
+
/** Whether or not we should automatically send pings to uphold a stable connection given whatever idleTimeout. */
|
|
201
|
+
sendPingsAutomatically?: boolean;
|
|
202
|
+
/** Handler for new WebSocket connection. WebSocket is valid from open to close, no errors. */
|
|
203
|
+
open?: (ws: DocumentedWS<UserData>) => void | Promise<void>;
|
|
204
|
+
/** Handler for a WebSocket message. Messages are given as ArrayBuffer no matter if they are binary or not. Given ArrayBuffer is valid during the lifetime of this callback (until first await or return) and will be neutered. */
|
|
205
|
+
message?: (ws: DocumentedWS<UserData>, message: ArrayBuffer, isBinary: boolean) => void | Promise<void>;
|
|
206
|
+
/** Handler for a dropped WebSocket message. Messages can be dropped due to specified backpressure settings. Messages are given as ArrayBuffer no matter if they are binary or not. Given ArrayBuffer is valid during the lifetime of this callback (until first await or return) and will be neutered. */
|
|
207
|
+
dropped?: (ws: DocumentedWS<UserData>, message: ArrayBuffer, isBinary: boolean) => void | Promise<void>;
|
|
208
|
+
/** Handler for when WebSocket backpressure drains. Check ws.getBufferedAmount(). Use this to guide / drive your backpressure throttling. */
|
|
209
|
+
drain?: (ws: DocumentedWS<UserData>) => void;
|
|
210
|
+
/** Handler for close event, no matter if error, timeout or graceful close. You may not use WebSocket after this event. Do not send on this WebSocket from within here, it is closed. */
|
|
211
|
+
close?: (ws: DocumentedWS<UserData>, code: number, message: ArrayBuffer) => void;
|
|
212
|
+
/** Handler for received ping control message. You do not need to handle this, pong messages are automatically sent as per the standard. */
|
|
213
|
+
ping?: (ws: DocumentedWS<UserData>, message: ArrayBuffer) => void;
|
|
214
|
+
/** Handler for received pong control message. */
|
|
215
|
+
pong?: (ws: DocumentedWS<UserData>, message: ArrayBuffer) => void;
|
|
216
|
+
/** Handler for subscription changes. */
|
|
217
|
+
subscription?: (ws: DocumentedWS<UserData>, topic: ArrayBuffer, newCount: number, oldCount: number) => void;
|
|
218
|
+
/** Upgrade handler used to intercept HTTP upgrade requests and potentially upgrade to WebSocket.
|
|
219
|
+
* See UpgradeAsync and UpgradeSync example files.
|
|
220
|
+
*/
|
|
221
|
+
upgrade?: (res: HttpResponse<UserData>, req: HttpRequest, context: us_socket_context_t) => void | Promise<void>;
|
|
222
|
+
}
|
|
223
|
+
export type DeclarativeResType = {
|
|
224
|
+
writeHeader(key: string, value: string): DeclarativeResType;
|
|
225
|
+
writeQueryValue(key: string): DeclarativeResType;
|
|
226
|
+
writeHeaderValue(key: string): DeclarativeResType;
|
|
227
|
+
/**
|
|
228
|
+
* Write a chunk to the precompiled response
|
|
229
|
+
*/
|
|
230
|
+
write(value: string): DeclarativeResType;
|
|
231
|
+
writeParameterValue(key: string): DeclarativeResType;
|
|
232
|
+
/**
|
|
233
|
+
* Method to finalize forming a response.
|
|
234
|
+
*/
|
|
235
|
+
end(value: string): any;
|
|
236
|
+
writeBody(): DeclarativeResType;
|
|
237
|
+
/**
|
|
238
|
+
* additional method from µBlitz.js
|
|
239
|
+
*/
|
|
240
|
+
writeHeaders<Opts extends BaseHeaders>(headers: Opts): DeclarativeResType;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Almost nothing different from uWS.DeclarativeResponse. The only modification - writeHeaders method (several methods, typescript intellisense)
|
|
244
|
+
*/
|
|
245
|
+
export declare var DeclarativeResponse: {
|
|
246
|
+
new (): DeclarativeResType;
|
|
247
|
+
};
|
|
248
|
+
export * from "./http-codes.js";
|
|
249
|
+
export * from "./http-headers.js";
|
package/package.json
CHANGED
|
@@ -1,24 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ublitzjs/core",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"types": "./dist/types/index.d.ts",
|
|
5
|
+
"files": ["dist"],
|
|
6
|
+
"typesVersions": {
|
|
7
|
+
"*": {
|
|
8
|
+
".": ["./dist/types/index.d.ts"]
|
|
9
|
+
}
|
|
10
|
+
},
|
|
4
11
|
"exports": {
|
|
5
|
-
"types": "./types/index.d.ts",
|
|
6
|
-
"require": "./cjs/index.
|
|
7
|
-
"import": "./
|
|
12
|
+
"types": "./dist/types/index.d.ts",
|
|
13
|
+
"require": "./dist/cjs/index.js",
|
|
14
|
+
"import": "./dist/esm/index.js"
|
|
8
15
|
},
|
|
9
16
|
"dependencies": {
|
|
10
17
|
"tseep": "^1.3.1",
|
|
11
|
-
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.
|
|
18
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0"
|
|
12
19
|
},
|
|
13
20
|
"publishConfig": {
|
|
14
21
|
"access": "public"
|
|
15
22
|
},
|
|
16
23
|
"devDependencies": {
|
|
17
|
-
"
|
|
24
|
+
"@babel/cli": "^7.28.6",
|
|
25
|
+
"@babel/core": "^7.29.0",
|
|
26
|
+
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
|
27
|
+
"@types/node": "^25.2.1",
|
|
28
|
+
"@types/ws": "^8.18.1",
|
|
18
29
|
"esbuild": "^0.25.12",
|
|
30
|
+
"tsd": "^0.33.0",
|
|
19
31
|
"vitest": "^4.0.8",
|
|
20
32
|
"ws": "^8.18.3"
|
|
21
33
|
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"trick-tsd": "bun link && bun link @ublitzjs/core",
|
|
36
|
+
"build:esm": "tsc -p tsconfig.esm.json && echo '{\"type\": \"module\"}' > dist/esm/package.json",
|
|
37
|
+
"build:cjs": "babel dist/esm --out-dir dist/cjs",
|
|
38
|
+
"build:types": "tsc -p tsconfig.types.json",
|
|
39
|
+
"build": "npm run build:types && npm run build:esm && npm run build:cjs",
|
|
40
|
+
"test": "tsd && vitest run"
|
|
41
|
+
},
|
|
42
|
+
"tsd": {
|
|
43
|
+
"directory": "tests"
|
|
44
|
+
},
|
|
22
45
|
"repository": {
|
|
23
46
|
"type": "git",
|
|
24
47
|
"url": "https://github.com/ublitzjs/core.git"
|
|
@@ -32,11 +55,14 @@
|
|
|
32
55
|
"ublitzjs",
|
|
33
56
|
"uWebSockets.js",
|
|
34
57
|
"uws",
|
|
58
|
+
"typescript",
|
|
35
59
|
"documentation",
|
|
36
60
|
"http",
|
|
61
|
+
"websockets",
|
|
37
62
|
"headers",
|
|
38
63
|
"codes",
|
|
39
64
|
"development",
|
|
40
|
-
"asynchronous code"
|
|
65
|
+
"asynchronous code",
|
|
66
|
+
"µBlitz.js"
|
|
41
67
|
]
|
|
42
68
|
}
|
package/USAGE.md
DELETED
|
@@ -1,319 +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 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>
|