owebjs 1.4.8 → 1.5.0-dev
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/allahbeler2.png +0 -0
- package/allahbeyler.png +0 -0
- package/avatar.png +0 -0
- package/dist/index.d.ts +45 -1
- package/dist/index.js +1 -0
- package/dist/structures/FastifyWebSocketAdapter.js +80 -0
- package/dist/structures/Oweb.js +28 -0
- package/dist/structures/WebSocketRoute.js +19 -0
- package/dist/utils/assignRoutes.js +256 -21
- package/dist/utils/utils.js +13 -0
- package/dist/utils/walk.js +8 -3
- package/dist/uwebsocket/request.js +28 -9
- package/dist/uwebsocket/response.js +13 -13
- package/dist/uwebsocket/server.js +148 -10
- package/package.json +70 -68
package/allahbeler2.png
ADDED
|
Binary file
|
package/allahbeyler.png
ADDED
|
Binary file
|
package/avatar.png
ADDED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -40,10 +40,12 @@ declare class _FastifyInstance {
|
|
|
40
40
|
}
|
|
41
41
|
declare class Oweb extends _FastifyInstance {
|
|
42
42
|
_options: OwebOptions;
|
|
43
|
+
_internalKV: Map<string, any>;
|
|
43
44
|
private hmrDirectory;
|
|
44
45
|
private hmrMatchersDirectory;
|
|
45
46
|
private directory;
|
|
46
47
|
private matchersDirectory;
|
|
48
|
+
uServer: any;
|
|
47
49
|
routes: Map<string, any>;
|
|
48
50
|
constructor(options?: OwebOptions);
|
|
49
51
|
/**
|
|
@@ -51,6 +53,7 @@ declare class Oweb extends _FastifyInstance {
|
|
|
51
53
|
* Returns a fastify instance with the Oweb prototype methods
|
|
52
54
|
*/
|
|
53
55
|
setup(): Promise<Oweb>;
|
|
56
|
+
ws(url: string, behavior: any, hooks?: any[]): void;
|
|
54
57
|
/**
|
|
55
58
|
* Loads routes from a directory.
|
|
56
59
|
* @param options.directory The directory to load routes from.
|
|
@@ -92,4 +95,45 @@ declare interface Hook {
|
|
|
92
95
|
declare abstract class Hook {
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
interface WebSocketAdapter {
|
|
99
|
+
send(message: string | ArrayBuffer, isBinary?: boolean, compress?: boolean): number;
|
|
100
|
+
close(code?: number, shortMessage?: string | ArrayBuffer): void;
|
|
101
|
+
end(code?: number, shortMessage?: string | ArrayBuffer): void;
|
|
102
|
+
cork(cb: () => void): void;
|
|
103
|
+
getUserData(): any;
|
|
104
|
+
getRemoteAddressAsText(): ArrayBuffer;
|
|
105
|
+
getRemoteAddress(): ArrayBuffer;
|
|
106
|
+
subscribe(topic: string): boolean;
|
|
107
|
+
unsubscribe(topic: string): boolean;
|
|
108
|
+
publish(topic: string, message: string | ArrayBuffer, isBinary?: boolean, compress?: boolean): boolean;
|
|
109
|
+
}
|
|
110
|
+
interface WebSocketRouteOptions {
|
|
111
|
+
compression?: number;
|
|
112
|
+
maxPayloadLength?: number;
|
|
113
|
+
idleTimeout?: number;
|
|
114
|
+
sendPingsAutomatically?: boolean;
|
|
115
|
+
}
|
|
116
|
+
declare abstract class WebSocketRoute {
|
|
117
|
+
_options: WebSocketRouteOptions;
|
|
118
|
+
constructor(options?: WebSocketRouteOptions);
|
|
119
|
+
/**
|
|
120
|
+
* Called when a client connects.
|
|
121
|
+
*/
|
|
122
|
+
open?(ws: WebSocketAdapter, req: any): Awaitable<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Called when a message is received.
|
|
125
|
+
*/
|
|
126
|
+
message?(ws: WebSocketAdapter, message: ArrayBuffer, isBinary: boolean): Awaitable<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Called when the connection is closed.
|
|
129
|
+
*/
|
|
130
|
+
close?(ws: WebSocketAdapter, code: number, message: ArrayBuffer): Awaitable<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Called when the socket buffer is empty (backpressure).
|
|
133
|
+
*/
|
|
134
|
+
drain?(ws: WebSocketAdapter): Awaitable<void>;
|
|
135
|
+
ping?(ws: WebSocketAdapter, message: ArrayBuffer): Awaitable<void>;
|
|
136
|
+
pong?(ws: WebSocketAdapter, message: ArrayBuffer): Awaitable<void>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { type Awaitable, Hook, type LoadRoutesOptions, Oweb, type OwebOptions, Route, type WebSocketAdapter, WebSocketRoute, type WebSocketRouteOptions, Oweb as default };
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "../chunk-SHUYVCID.js";
|
|
4
|
+
const topicSubscribers = /* @__PURE__ */ new Map();
|
|
5
|
+
class FastifyWebSocketAdapter {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "FastifyWebSocketAdapter");
|
|
8
|
+
}
|
|
9
|
+
ws;
|
|
10
|
+
req;
|
|
11
|
+
userData;
|
|
12
|
+
constructor(ws, req) {
|
|
13
|
+
this.ws = ws;
|
|
14
|
+
this.req = req;
|
|
15
|
+
this.userData = {};
|
|
16
|
+
}
|
|
17
|
+
send(message, isBinary, compress) {
|
|
18
|
+
if (this.ws.readyState === 1) {
|
|
19
|
+
this.ws.send(message, {
|
|
20
|
+
binary: isBinary,
|
|
21
|
+
compress
|
|
22
|
+
});
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
close(code, shortMessage) {
|
|
28
|
+
this.ws.close(code, shortMessage);
|
|
29
|
+
}
|
|
30
|
+
end(code, shortMessage) {
|
|
31
|
+
this.ws.close(code, shortMessage);
|
|
32
|
+
}
|
|
33
|
+
cork(cb) {
|
|
34
|
+
cb();
|
|
35
|
+
}
|
|
36
|
+
getUserData() {
|
|
37
|
+
return this.userData;
|
|
38
|
+
}
|
|
39
|
+
getRemoteAddressAsText() {
|
|
40
|
+
return Buffer.from(this.req.socket?.remoteAddress || "");
|
|
41
|
+
}
|
|
42
|
+
getRemoteAddress() {
|
|
43
|
+
return this.getRemoteAddressAsText();
|
|
44
|
+
}
|
|
45
|
+
subscribe(topic) {
|
|
46
|
+
if (!topicSubscribers.has(topic)) {
|
|
47
|
+
topicSubscribers.set(topic, /* @__PURE__ */ new Set());
|
|
48
|
+
}
|
|
49
|
+
topicSubscribers.get(topic).add(this);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
unsubscribe(topic) {
|
|
53
|
+
const set = topicSubscribers.get(topic);
|
|
54
|
+
if (set) {
|
|
55
|
+
set.delete(this);
|
|
56
|
+
if (set.size === 0) topicSubscribers.delete(topic);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
publish(topic, message, isBinary, compress) {
|
|
62
|
+
const set = topicSubscribers.get(topic);
|
|
63
|
+
if (!set) return false;
|
|
64
|
+
set.forEach((client) => {
|
|
65
|
+
if (client !== this && client.ws.readyState === 1) {
|
|
66
|
+
client.send(message, isBinary, compress);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
cleanup() {
|
|
72
|
+
topicSubscribers.forEach((set, topic) => {
|
|
73
|
+
set.delete(this);
|
|
74
|
+
if (set.size === 0) topicSubscribers.delete(topic);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
FastifyWebSocketAdapter
|
|
80
|
+
};
|
package/dist/structures/Oweb.js
CHANGED
|
@@ -5,6 +5,7 @@ import Fastify from "fastify";
|
|
|
5
5
|
import { applyMatcherHMR, applyRouteHMR, assignRoutes } from '../utils/assignRoutes.js';
|
|
6
6
|
import { watchDirectory } from '../utils/watcher.js';
|
|
7
7
|
import { info, success, warn } from '../utils/logger.js';
|
|
8
|
+
import websocketPlugin from "@fastify/websocket";
|
|
8
9
|
let _FastifyInstance = class _FastifyInstance2 {
|
|
9
10
|
static {
|
|
10
11
|
__name(this, "_FastifyInstance");
|
|
@@ -15,10 +16,12 @@ class Oweb extends _FastifyInstance {
|
|
|
15
16
|
__name(this, "Oweb");
|
|
16
17
|
}
|
|
17
18
|
_options = {};
|
|
19
|
+
_internalKV = /* @__PURE__ */ new Map();
|
|
18
20
|
hmrDirectory;
|
|
19
21
|
hmrMatchersDirectory;
|
|
20
22
|
directory;
|
|
21
23
|
matchersDirectory;
|
|
24
|
+
uServer = null;
|
|
22
25
|
routes = /* @__PURE__ */ new Map();
|
|
23
26
|
constructor(options) {
|
|
24
27
|
super();
|
|
@@ -38,12 +41,18 @@ class Oweb extends _FastifyInstance {
|
|
|
38
41
|
if (this._options.uWebSocketsEnabled) {
|
|
39
42
|
const serverimp = (await import("../uwebsocket/server.js")).default;
|
|
40
43
|
const server = await serverimp({});
|
|
44
|
+
this.uServer = server;
|
|
41
45
|
this._options.serverFactory = (handler) => {
|
|
42
46
|
server.on("request", handler);
|
|
43
47
|
return server;
|
|
44
48
|
};
|
|
49
|
+
} else {
|
|
50
|
+
this.uServer = null;
|
|
45
51
|
}
|
|
46
52
|
const fastify = Fastify(this._options);
|
|
53
|
+
if (!this._options.uWebSocketsEnabled) {
|
|
54
|
+
await fastify.register(websocketPlugin);
|
|
55
|
+
}
|
|
47
56
|
fastify.addHook("onRequest", (_, res, done) => {
|
|
48
57
|
res.header("X-Powered-By", "Oweb");
|
|
49
58
|
done();
|
|
@@ -52,6 +61,17 @@ class Oweb extends _FastifyInstance {
|
|
|
52
61
|
if (key === "constructor") continue;
|
|
53
62
|
Object.defineProperty(fastify, key, Object.getOwnPropertyDescriptor(Oweb.prototype, key));
|
|
54
63
|
}
|
|
64
|
+
Object.defineProperty(fastify, "_internalKV", {
|
|
65
|
+
value: this._internalKV,
|
|
66
|
+
writable: true,
|
|
67
|
+
enumerable: false
|
|
68
|
+
});
|
|
69
|
+
Object.defineProperty(fastify, "uServer", {
|
|
70
|
+
value: this.uServer,
|
|
71
|
+
writable: true,
|
|
72
|
+
enumerable: false,
|
|
73
|
+
configurable: false
|
|
74
|
+
});
|
|
55
75
|
Object.defineProperty(fastify, "_options", {
|
|
56
76
|
value: this._options,
|
|
57
77
|
writable: true,
|
|
@@ -60,6 +80,13 @@ class Oweb extends _FastifyInstance {
|
|
|
60
80
|
});
|
|
61
81
|
return fastify;
|
|
62
82
|
}
|
|
83
|
+
ws(url, behavior, hooks = []) {
|
|
84
|
+
if (this.uServer) {
|
|
85
|
+
this.uServer.ws(url, behavior, hooks);
|
|
86
|
+
} else {
|
|
87
|
+
warn("Oweb#ws is only available when uWebSockets is enabled. For Fastify instances, Oweb automatically handles registrations.");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
63
90
|
/**
|
|
64
91
|
* Loads routes from a directory.
|
|
65
92
|
* @param options.directory The directory to load routes from.
|
|
@@ -77,6 +104,7 @@ class Oweb extends _FastifyInstance {
|
|
|
77
104
|
if (hmr?.enabled) {
|
|
78
105
|
this.hmrDirectory = hmr.directory;
|
|
79
106
|
this.hmrMatchersDirectory = hmr.matchersDirectory;
|
|
107
|
+
this._internalKV.set("hmr", true);
|
|
80
108
|
success(`Hot Module Replacement enabled. Watching changes in ${hmr.directory}`, "HMR");
|
|
81
109
|
} else {
|
|
82
110
|
warn('Hot Module Replacement is disabled. Use "await app.loadRoutes({ hmr: { enabled: true, directory: path } })" to enable it.', "HMR");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "../chunk-SHUYVCID.js";
|
|
4
|
+
class WebSocketRoute {
|
|
5
|
+
static {
|
|
6
|
+
__name(this, "WebSocketRoute");
|
|
7
|
+
}
|
|
8
|
+
_options = {};
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this._options = options ?? {
|
|
11
|
+
compression: 0,
|
|
12
|
+
maxPayloadLength: 16 * 1024 * 1024,
|
|
13
|
+
idleTimeout: 120
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
WebSocketRoute
|
|
19
|
+
};
|
|
@@ -5,10 +5,15 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { buildRoutePath, buildRouteURL } from './utils.js';
|
|
7
7
|
import { walk } from './walk.js';
|
|
8
|
-
import { success, warn } from './logger.js';
|
|
8
|
+
import { error, success, warn } from './logger.js';
|
|
9
9
|
import { match } from "path-to-regexp";
|
|
10
10
|
import generateFunctionFromTypescript from './generateFunctionFromTypescript.js';
|
|
11
11
|
import { readdirSync } from "node:fs";
|
|
12
|
+
import { WebSocketRoute } from '../structures/WebSocketRoute.js';
|
|
13
|
+
import { FastifyWebSocketAdapter } from '../structures/FastifyWebSocketAdapter.js';
|
|
14
|
+
import { formatSSE } from './utils.js';
|
|
15
|
+
const websocketRoutes = {};
|
|
16
|
+
const registeredWebSockets = /* @__PURE__ */ new Set();
|
|
12
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
18
|
let matcherOverrides = {};
|
|
14
19
|
let routeFunctions = {
|
|
@@ -37,6 +42,40 @@ function removeExtension(filePath) {
|
|
|
37
42
|
return filePath;
|
|
38
43
|
}
|
|
39
44
|
__name(removeExtension, "removeExtension");
|
|
45
|
+
function createWebSocketProxy(url) {
|
|
46
|
+
const getHandler = /* @__PURE__ */ __name(() => websocketRoutes[url], "getHandler");
|
|
47
|
+
return {
|
|
48
|
+
compression: getHandler()?._options.compression,
|
|
49
|
+
maxPayloadLength: getHandler()?._options.maxPayloadLength,
|
|
50
|
+
idleTimeout: getHandler()?._options.idleTimeout,
|
|
51
|
+
sendPingsAutomatically: getHandler()?._options.sendPingsAutomatically,
|
|
52
|
+
open: /* @__PURE__ */ __name((ws, req) => {
|
|
53
|
+
const handler = getHandler();
|
|
54
|
+
if (handler?.open) handler.open(ws, req);
|
|
55
|
+
}, "open"),
|
|
56
|
+
message: /* @__PURE__ */ __name((ws, message, isBinary) => {
|
|
57
|
+
const handler = getHandler();
|
|
58
|
+
if (handler?.message) handler.message(ws, message, isBinary);
|
|
59
|
+
}, "message"),
|
|
60
|
+
drain: /* @__PURE__ */ __name((ws) => {
|
|
61
|
+
const handler = getHandler();
|
|
62
|
+
if (handler?.drain) handler.drain(ws);
|
|
63
|
+
}, "drain"),
|
|
64
|
+
close: /* @__PURE__ */ __name((ws, code, message) => {
|
|
65
|
+
const handler = getHandler();
|
|
66
|
+
if (handler?.close) handler.close(ws, code, message);
|
|
67
|
+
}, "close"),
|
|
68
|
+
ping: /* @__PURE__ */ __name((ws, message) => {
|
|
69
|
+
const handler = getHandler();
|
|
70
|
+
if (handler?.ping) handler.ping(ws, message);
|
|
71
|
+
}, "ping"),
|
|
72
|
+
pong: /* @__PURE__ */ __name((ws, message) => {
|
|
73
|
+
const handler = getHandler();
|
|
74
|
+
if (handler?.pong) handler.pong(ws, message);
|
|
75
|
+
}, "pong")
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
__name(createWebSocketProxy, "createWebSocketProxy");
|
|
40
79
|
const applyMatcherHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallbackDir, filePath, content) => {
|
|
41
80
|
let def;
|
|
42
81
|
const fileName = path.basename(filePath);
|
|
@@ -80,6 +119,12 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
|
|
|
80
119
|
const routes = await generateRoutes(files, path2);
|
|
81
120
|
routesCache = routes;
|
|
82
121
|
const f = routes.find((x) => x.fileInfo.filePath == path2);
|
|
122
|
+
if (f?.fn?.prototype instanceof WebSocketRoute) {
|
|
123
|
+
assignSpecificRoute(oweb, f);
|
|
124
|
+
const end2 = Date.now() - start;
|
|
125
|
+
success(`WebSocket Route ${f.url} created in ${end2}ms`, "HMR");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
83
128
|
temporaryRequests[f.method.toLowerCase()][f.url] = inner(oweb, f);
|
|
84
129
|
const end = Date.now() - start;
|
|
85
130
|
success(`Route ${f.method.toUpperCase()}:${f.url} created in ${end}ms`, "HMR");
|
|
@@ -89,6 +134,12 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
|
|
|
89
134
|
const routes = await generateRoutes(files, path2);
|
|
90
135
|
routesCache = routes;
|
|
91
136
|
const f = routes.find((x) => x.fileInfo.filePath == path2);
|
|
137
|
+
if (f?.fn?.prototype instanceof WebSocketRoute) {
|
|
138
|
+
websocketRoutes[f.url] = new f.fn();
|
|
139
|
+
const end2 = Date.now() - start;
|
|
140
|
+
success(`WebSocket Route ${f.url} reloaded in ${end2}ms`, "HMR");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
92
143
|
if (f.url in temporaryRequests[f.method.toLowerCase()]) {
|
|
93
144
|
temporaryRequests[f.method.toLowerCase()][f.url] = inner(oweb, f);
|
|
94
145
|
} else {
|
|
@@ -103,14 +154,22 @@ const applyRouteHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, fallba
|
|
|
103
154
|
if (builded.url.endsWith("/index")) {
|
|
104
155
|
builded.url = builded.url.slice(0, -"/index".length);
|
|
105
156
|
}
|
|
157
|
+
if (websocketRoutes[builded.url]) {
|
|
158
|
+
delete websocketRoutes[builded.url];
|
|
159
|
+
const end = Date.now() - start;
|
|
160
|
+
success(`WebSocket Route ${builded.url} removed (shimmed) in ${end}ms`, "HMR");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
106
163
|
const f = routesCache.find((x) => x.method == builded.method && x.url == builded.url);
|
|
107
|
-
if (f
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
164
|
+
if (f) {
|
|
165
|
+
if (f.url in temporaryRequests[f.method.toLowerCase()]) {
|
|
166
|
+
delete temporaryRequests[f.method.toLowerCase()][f.url];
|
|
167
|
+
} else {
|
|
168
|
+
delete routeFunctions[f.method.toLowerCase()][f.url];
|
|
169
|
+
}
|
|
170
|
+
const end = Date.now() - start;
|
|
171
|
+
success(`Route ${f.method.toUpperCase()}:${f.url} removed in ${end}ms`, "HMR");
|
|
111
172
|
}
|
|
112
|
-
const end = Date.now() - start;
|
|
113
|
-
success(`Route ${f.method.toUpperCase()}:${f.url} removed in ${end}ms`, "HMR");
|
|
114
173
|
}
|
|
115
174
|
}, "applyRouteHMR");
|
|
116
175
|
const generateRoutes = /* @__PURE__ */ __name(async (files, onlyGenerateFn) => {
|
|
@@ -153,7 +212,16 @@ function inner(oweb, route) {
|
|
|
153
212
|
}
|
|
154
213
|
const routeFunc = new route.fn();
|
|
155
214
|
const matchers = route.matchers;
|
|
215
|
+
const isParametric = route.url.includes(":") || route.url.includes("*");
|
|
156
216
|
return function(req, res) {
|
|
217
|
+
if (oweb._internalKV.get("hmr") && isParametric) {
|
|
218
|
+
const currentPath = req.raw.url.split("?")[0];
|
|
219
|
+
const method = req.method.toLowerCase();
|
|
220
|
+
const specificHandler = temporaryRequests[method]?.[currentPath];
|
|
221
|
+
if (specificHandler && specificHandler !== temporaryRequests[route.method][route.url]) {
|
|
222
|
+
return specificHandler(req, res);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
157
225
|
const checkMatchers = /* @__PURE__ */ __name(() => {
|
|
158
226
|
for (const matcher of matchers) {
|
|
159
227
|
const param = req.params[matcher.paramName];
|
|
@@ -164,31 +232,98 @@ function inner(oweb, route) {
|
|
|
164
232
|
}
|
|
165
233
|
return true;
|
|
166
234
|
}, "checkMatchers");
|
|
167
|
-
const handle = /* @__PURE__ */ __name(() => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
235
|
+
const handle = /* @__PURE__ */ __name(async () => {
|
|
236
|
+
let result;
|
|
237
|
+
try {
|
|
238
|
+
if (routeFunc.handle.constructor.name === "AsyncFunction") {
|
|
239
|
+
result = await routeFunc.handle(req, res);
|
|
240
|
+
} else {
|
|
241
|
+
result = routeFunc.handle(req, res);
|
|
242
|
+
if (result instanceof Promise) {
|
|
243
|
+
result = await result;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} catch (error2) {
|
|
247
|
+
if (routeFunc?.handleError) {
|
|
248
|
+
routeFunc.handleError(req, res, error2);
|
|
249
|
+
} else {
|
|
250
|
+
oweb._options.OWEB_INTERNAL_ERROR_HANDLER(req, res, error2);
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const isIterable = result && typeof result[Symbol.iterator] === "function";
|
|
255
|
+
const isAsyncIterable = result && typeof result[Symbol.asyncIterator] === "function";
|
|
256
|
+
if ((isIterable || isAsyncIterable) && !res.sent) {
|
|
257
|
+
const rawObj = res.raw;
|
|
258
|
+
const uwsRes = rawObj.res && typeof rawObj.res.cork === "function" ? rawObj.res : null;
|
|
259
|
+
const corkedOp = /* @__PURE__ */ __name((op) => {
|
|
260
|
+
if (uwsRes) {
|
|
261
|
+
uwsRes.cork(op);
|
|
172
262
|
} else {
|
|
173
|
-
|
|
263
|
+
op();
|
|
264
|
+
}
|
|
265
|
+
}, "corkedOp");
|
|
266
|
+
corkedOp(() => {
|
|
267
|
+
res.raw.writeHead(200, {
|
|
268
|
+
"Content-Type": "text/event-stream",
|
|
269
|
+
"Cache-Control": "no-cache",
|
|
270
|
+
Connection: "keep-alive",
|
|
271
|
+
"Access-Control-Allow-Origin": "*"
|
|
272
|
+
});
|
|
273
|
+
if (res.raw.flushHeaders) {
|
|
274
|
+
res.raw.flushHeaders();
|
|
174
275
|
}
|
|
175
276
|
});
|
|
176
|
-
|
|
277
|
+
let aborted = false;
|
|
278
|
+
const onAborted = /* @__PURE__ */ __name(() => {
|
|
279
|
+
aborted = true;
|
|
280
|
+
}, "onAborted");
|
|
281
|
+
if (res.raw.on) {
|
|
282
|
+
res.raw.on("close", onAborted);
|
|
283
|
+
res.raw.on("aborted", onAborted);
|
|
284
|
+
} else if (rawObj["onAborted"]) {
|
|
285
|
+
rawObj["onAborted"](onAborted);
|
|
286
|
+
}
|
|
177
287
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
288
|
+
if (isAsyncIterable) {
|
|
289
|
+
for await (const chunk of result) {
|
|
290
|
+
if (aborted || res.raw.destroyed) break;
|
|
291
|
+
corkedOp(() => {
|
|
292
|
+
res.raw.write(formatSSE(chunk));
|
|
293
|
+
});
|
|
294
|
+
}
|
|
182
295
|
} else {
|
|
183
|
-
|
|
296
|
+
for (const chunk of result) {
|
|
297
|
+
if (aborted || res.raw.destroyed) break;
|
|
298
|
+
corkedOp(() => {
|
|
299
|
+
res.raw.write(formatSSE(chunk));
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} catch (err) {
|
|
304
|
+
error(`Error while streaming response for ${route.method.toUpperCase()}:${route.url} - ${err.message}`, "SSE");
|
|
305
|
+
} finally {
|
|
306
|
+
if (res.raw.off) {
|
|
307
|
+
res.raw.off("close", onAborted);
|
|
308
|
+
res.raw.off("aborted", onAborted);
|
|
309
|
+
}
|
|
310
|
+
if (!aborted && !res.raw.destroyed) {
|
|
311
|
+
corkedOp(() => {
|
|
312
|
+
res.raw.end();
|
|
313
|
+
});
|
|
184
314
|
}
|
|
185
315
|
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!res.sent && result !== void 0) {
|
|
319
|
+
res.send(result);
|
|
186
320
|
}
|
|
187
321
|
}, "handle");
|
|
188
322
|
if (route.fileInfo.hooks.length) {
|
|
189
323
|
for (let index = 0; index < route.fileInfo.hooks.length; index++) {
|
|
190
324
|
const hookFun = route.fileInfo.hooks[index];
|
|
191
|
-
hookFun
|
|
325
|
+
const hookInstance = typeof hookFun === "function" ? new hookFun() : hookFun;
|
|
326
|
+
hookInstance.handle(req, res, () => {
|
|
192
327
|
if (index + 1 == route.fileInfo.hooks.length) {
|
|
193
328
|
if (!checkMatchers()) {
|
|
194
329
|
send404(req, res);
|
|
@@ -217,7 +352,107 @@ function send404(req, res) {
|
|
|
217
352
|
}
|
|
218
353
|
__name(send404, "send404");
|
|
219
354
|
function assignSpecificRoute(oweb, route) {
|
|
220
|
-
if (!route
|
|
355
|
+
if (!route?.fn) return;
|
|
356
|
+
if (route?.fn?.prototype instanceof WebSocketRoute) {
|
|
357
|
+
const wsInstance = new route.fn();
|
|
358
|
+
websocketRoutes[route.url] = wsInstance;
|
|
359
|
+
if (!registeredWebSockets.has(route.url)) {
|
|
360
|
+
registeredWebSockets.add(route.url);
|
|
361
|
+
if (oweb._options.uWebSocketsEnabled && oweb.uServer) {
|
|
362
|
+
const proxy = createWebSocketProxy(route.url);
|
|
363
|
+
oweb.ws(route.url, proxy, route.fileInfo.hooks);
|
|
364
|
+
} else {
|
|
365
|
+
oweb.get(route.url, {
|
|
366
|
+
websocket: true
|
|
367
|
+
}, (arg1, arg2) => {
|
|
368
|
+
(async () => {
|
|
369
|
+
let socket;
|
|
370
|
+
let req;
|
|
371
|
+
if (arg1 && arg1.socket) {
|
|
372
|
+
socket = arg1.socket;
|
|
373
|
+
req = arg2;
|
|
374
|
+
} else if (arg1 && arg1.raw && arg1.raw.socket) {
|
|
375
|
+
socket = arg1.raw.socket;
|
|
376
|
+
req = arg1;
|
|
377
|
+
} else {
|
|
378
|
+
socket = arg1;
|
|
379
|
+
req = arg2 || {};
|
|
380
|
+
}
|
|
381
|
+
if (!socket || typeof socket.on !== "function") {
|
|
382
|
+
error(`Could not find underlying socket for route ${route.url}. Arg1 type: ${typeof arg1}`, "WS");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const adapter = new FastifyWebSocketAdapter(socket, req.raw || req);
|
|
386
|
+
socket.on("error", (err) => {
|
|
387
|
+
error(`${route.url}: ${err.message}`, "WS");
|
|
388
|
+
});
|
|
389
|
+
const hooks = route.fileInfo.hooks || [];
|
|
390
|
+
try {
|
|
391
|
+
for (const HookClass of hooks) {
|
|
392
|
+
await new Promise((resolve, reject) => {
|
|
393
|
+
const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
|
|
394
|
+
hookInstance.handle(req, {
|
|
395
|
+
status: /* @__PURE__ */ __name((c) => ({
|
|
396
|
+
send: /* @__PURE__ */ __name((m) => {
|
|
397
|
+
socket.close(c, m);
|
|
398
|
+
reject("closed");
|
|
399
|
+
}, "send")
|
|
400
|
+
}), "status"),
|
|
401
|
+
header: /* @__PURE__ */ __name(() => {
|
|
402
|
+
}, "header"),
|
|
403
|
+
send: /* @__PURE__ */ __name((m) => {
|
|
404
|
+
socket.close(1e3, m);
|
|
405
|
+
reject("closed");
|
|
406
|
+
}, "send")
|
|
407
|
+
}, (err) => {
|
|
408
|
+
if (err) reject(err);
|
|
409
|
+
else resolve();
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
} catch (e) {
|
|
414
|
+
if (e !== "closed") {
|
|
415
|
+
error(`WebSocket Hook Error: ${e}`, "WS");
|
|
416
|
+
socket.close(1011);
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const getHandler = /* @__PURE__ */ __name(() => websocketRoutes[route.url], "getHandler");
|
|
421
|
+
socket.on("message", (message, isBinary) => {
|
|
422
|
+
const h = getHandler();
|
|
423
|
+
if (h?.message) h.message(adapter, message, isBinary);
|
|
424
|
+
});
|
|
425
|
+
socket.on("close", (code, reason) => {
|
|
426
|
+
const h = getHandler();
|
|
427
|
+
adapter.cleanup();
|
|
428
|
+
if (h?.close) h.close(adapter, code, reason);
|
|
429
|
+
});
|
|
430
|
+
socket.on("ping", (data) => {
|
|
431
|
+
const h = getHandler();
|
|
432
|
+
if (h?.ping) h.ping(adapter, data);
|
|
433
|
+
});
|
|
434
|
+
socket.on("pong", (data) => {
|
|
435
|
+
const h = getHandler();
|
|
436
|
+
if (h?.pong) h.pong(adapter, data);
|
|
437
|
+
});
|
|
438
|
+
const handler = getHandler();
|
|
439
|
+
if (handler?.open) {
|
|
440
|
+
await handler.open(adapter, req);
|
|
441
|
+
}
|
|
442
|
+
})().catch((err) => {
|
|
443
|
+
error(`Internaal Error on ${route.url}: ${err.message}`, "WS");
|
|
444
|
+
if (arg1 && arg1.socket) {
|
|
445
|
+
try {
|
|
446
|
+
arg1.socket.close(1011);
|
|
447
|
+
} catch {
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
221
456
|
const routeFunc = new route.fn();
|
|
222
457
|
routeFunctions[route.method][route.url] = inner(oweb, route);
|
|
223
458
|
oweb[route.method](route.url, routeFunc._options || {}, function(req, res) {
|
package/dist/utils/utils.js
CHANGED
|
@@ -12,6 +12,18 @@ const convertParamSyntax = /* @__PURE__ */ __name((path) => {
|
|
|
12
12
|
return mergePaths(...subpaths);
|
|
13
13
|
}, "convertParamSyntax");
|
|
14
14
|
const convertCatchallSyntax = /* @__PURE__ */ __name((url) => url.replace(/:\.\.\.\w+/g, "*"), "convertCatchallSyntax");
|
|
15
|
+
const formatSSE = /* @__PURE__ */ __name((chunk) => {
|
|
16
|
+
if (typeof chunk === "object") {
|
|
17
|
+
try {
|
|
18
|
+
chunk = JSON.stringify(chunk);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
chunk = String(chunk);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return `data: ${chunk}
|
|
24
|
+
|
|
25
|
+
`;
|
|
26
|
+
}, "formatSSE");
|
|
15
27
|
const buildRoutePath = /* @__PURE__ */ __name((parsedFile) => {
|
|
16
28
|
const directory = parsedFile.dir === parsedFile.root ? "" : parsedFile.dir;
|
|
17
29
|
const name = parsedFile.name.startsWith("index") ? parsedFile.name.replace("index", "") : `/${parsedFile.name}`;
|
|
@@ -64,5 +76,6 @@ export {
|
|
|
64
76
|
buildRouteURL,
|
|
65
77
|
convertCatchallSyntax,
|
|
66
78
|
convertParamSyntax,
|
|
79
|
+
formatSSE,
|
|
67
80
|
mergePaths
|
|
68
81
|
};
|
package/dist/utils/walk.js
CHANGED
|
@@ -69,9 +69,14 @@ const walk = /* @__PURE__ */ __name(async (directory, tree = [], fallbackDir) =>
|
|
|
69
69
|
}
|
|
70
70
|
const hooksImport = useHook.map((hookPath) => {
|
|
71
71
|
if (fallbackDir) {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
72
|
+
const relativeToRoot = path.relative(process.cwd(), hookPath);
|
|
73
|
+
const normalizedFallback = fallbackDir.replace(/\\/g, "/").replace(/\/$/, "");
|
|
74
|
+
const normalizedRel = relativeToRoot.replace(/\\/g, "/");
|
|
75
|
+
let finalPath = normalizedRel;
|
|
76
|
+
if (!normalizedRel.startsWith(normalizedFallback)) {
|
|
77
|
+
finalPath = path.posix.join(normalizedFallback, normalizedRel);
|
|
78
|
+
}
|
|
79
|
+
return new URL(`file://${path.join(process.cwd(), finalPath)}`).pathname + "/_hooks.js";
|
|
75
80
|
} else {
|
|
76
81
|
return new URL(hookPath, `file://${__dirname}`).pathname.replaceAll("\\", "/") + "/_hooks.js";
|
|
77
82
|
}
|
|
@@ -9,6 +9,7 @@ class HttpRequest extends Readable {
|
|
|
9
9
|
}
|
|
10
10
|
req;
|
|
11
11
|
res;
|
|
12
|
+
uResponse;
|
|
12
13
|
url;
|
|
13
14
|
method;
|
|
14
15
|
statusCode;
|
|
@@ -16,19 +17,31 @@ class HttpRequest extends Readable {
|
|
|
16
17
|
body;
|
|
17
18
|
headers;
|
|
18
19
|
socket;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// https://nodejs.org/api/http.html#class-httpincomingmessage
|
|
21
|
+
complete = false;
|
|
22
|
+
connection;
|
|
23
|
+
constructor(uRequest, uResponse) {
|
|
24
|
+
super({
|
|
25
|
+
highWaterMark: 64 * 1024
|
|
26
|
+
});
|
|
27
|
+
this.uResponse = uResponse;
|
|
22
28
|
this.req = uRequest;
|
|
29
|
+
const q = uRequest.getQuery();
|
|
23
30
|
this.url = uRequest.getUrl() + (q ? "?" + q : "");
|
|
24
31
|
this.method = uRequest.getMethod().toUpperCase();
|
|
25
|
-
this.statusCode = null;
|
|
26
|
-
this.statusMessage = null;
|
|
27
32
|
this.body = {};
|
|
28
33
|
this.headers = {};
|
|
29
|
-
this.socket = {
|
|
34
|
+
this.socket = {
|
|
35
|
+
destroy: /* @__PURE__ */ __name(() => {
|
|
36
|
+
}, "destroy"),
|
|
37
|
+
on: /* @__PURE__ */ __name(() => {
|
|
38
|
+
}, "on"),
|
|
39
|
+
removeListener: /* @__PURE__ */ __name(() => {
|
|
40
|
+
}, "removeListener")
|
|
41
|
+
};
|
|
42
|
+
this.connection = this.socket;
|
|
30
43
|
uRequest.forEach((header, value) => {
|
|
31
|
-
this.headers[header] = value;
|
|
44
|
+
this.headers[header.toLowerCase()] = value;
|
|
32
45
|
});
|
|
33
46
|
}
|
|
34
47
|
getRawHeaders() {
|
|
@@ -41,8 +54,14 @@ class HttpRequest extends Readable {
|
|
|
41
54
|
getRaw() {
|
|
42
55
|
return this.req;
|
|
43
56
|
}
|
|
44
|
-
_read(
|
|
45
|
-
|
|
57
|
+
_read(_) {
|
|
58
|
+
if (this.uResponse) {
|
|
59
|
+
setImmediate(() => {
|
|
60
|
+
if (this.uResponse && !this.uResponse.aborted) {
|
|
61
|
+
this.uResponse.resume();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
46
65
|
}
|
|
47
66
|
}
|
|
48
67
|
export {
|
|
@@ -71,8 +71,10 @@ class HttpResponse extends Writable {
|
|
|
71
71
|
//@ts-ignore
|
|
72
72
|
write(data) {
|
|
73
73
|
if (this.finished) return;
|
|
74
|
-
this.
|
|
75
|
-
|
|
74
|
+
this.res.cork(() => {
|
|
75
|
+
this._flushHeaders();
|
|
76
|
+
this.res.write(data);
|
|
77
|
+
});
|
|
76
78
|
}
|
|
77
79
|
writeHead(statusCode) {
|
|
78
80
|
if (this.finished) return;
|
|
@@ -93,17 +95,15 @@ class HttpResponse extends Writable {
|
|
|
93
95
|
//@ts-ignore
|
|
94
96
|
end(data) {
|
|
95
97
|
if (this.finished) return;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
return doWrite();
|
|
98
|
+
this.res.cork(() => {
|
|
99
|
+
this._flushHeaders();
|
|
100
|
+
this.finished = true;
|
|
101
|
+
if (!data) {
|
|
102
|
+
this.res.end();
|
|
103
|
+
} else {
|
|
104
|
+
this.res.end(data);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
107
|
}
|
|
108
108
|
getRaw() {
|
|
109
109
|
return this.res;
|
|
@@ -23,33 +23,51 @@ async function server_default({ cert_file_name, key_file_name }) {
|
|
|
23
23
|
};
|
|
24
24
|
const uServer = uWS[appType](config).any("/*", (res, req) => {
|
|
25
25
|
res.finished = false;
|
|
26
|
-
|
|
26
|
+
res.aborted = false;
|
|
27
|
+
res.isPaused = false;
|
|
28
|
+
res.onAborted(() => {
|
|
29
|
+
res.aborted = true;
|
|
30
|
+
res.finished = true;
|
|
31
|
+
});
|
|
32
|
+
const reqWrapper = new HttpRequest(req, res);
|
|
27
33
|
const resWrapper = new HttpResponse(res, uServer);
|
|
28
34
|
reqWrapper.res = resWrapper;
|
|
29
35
|
resWrapper.req = reqWrapper;
|
|
30
36
|
reqWrapper.socket = resWrapper.socket;
|
|
37
|
+
const originalResume = res.resume;
|
|
38
|
+
res.resume = function() {
|
|
39
|
+
if (res.isPaused && !res.finished && !res.aborted) {
|
|
40
|
+
res.isPaused = false;
|
|
41
|
+
originalResume.call(res);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
handler(reqWrapper, resWrapper);
|
|
31
45
|
const method = reqWrapper.method;
|
|
32
|
-
if (method !== "HEAD") {
|
|
46
|
+
if (method !== "HEAD" && method !== "GET" && !resWrapper.finished) {
|
|
33
47
|
res.onData((bytes, isLast) => {
|
|
34
|
-
|
|
48
|
+
if (res.finished || res.aborted) return;
|
|
49
|
+
const chunk = Buffer.from(bytes.slice(0));
|
|
50
|
+
const streamReady = reqWrapper.push(chunk);
|
|
35
51
|
if (isLast) {
|
|
36
|
-
reqWrapper.
|
|
52
|
+
reqWrapper.complete = true;
|
|
37
53
|
reqWrapper.push(null);
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
} else if (!streamReady) {
|
|
55
|
+
if (!res.isPaused) {
|
|
56
|
+
res.isPaused = true;
|
|
57
|
+
res.pause();
|
|
40
58
|
}
|
|
41
|
-
return;
|
|
42
59
|
}
|
|
43
|
-
return reqWrapper.push(chunk);
|
|
44
60
|
});
|
|
45
|
-
} else if (!
|
|
46
|
-
|
|
61
|
+
} else if (!resWrapper.finished) {
|
|
62
|
+
reqWrapper.complete = true;
|
|
63
|
+
reqWrapper.push(null);
|
|
47
64
|
}
|
|
48
65
|
});
|
|
49
66
|
let uServerClass = class uServerClass extends EventEmitter {
|
|
50
67
|
static {
|
|
51
68
|
__name(this, "uServerClass");
|
|
52
69
|
}
|
|
70
|
+
_socket;
|
|
53
71
|
constructor() {
|
|
54
72
|
super();
|
|
55
73
|
const oldThisOn = this.on.bind(this);
|
|
@@ -113,6 +131,126 @@ async function server_default({ cert_file_name, key_file_name }) {
|
|
|
113
131
|
}
|
|
114
132
|
}
|
|
115
133
|
}
|
|
134
|
+
ws(pattern, behaviors, hooks = []) {
|
|
135
|
+
uServer.ws(pattern, {
|
|
136
|
+
compression: behaviors.compression,
|
|
137
|
+
maxPayloadLength: behaviors.maxPayloadLength,
|
|
138
|
+
idleTimeout: behaviors.idleTimeout,
|
|
139
|
+
sendPingsAutomatically: behaviors.sendPingsAutomatically,
|
|
140
|
+
upgrade: /* @__PURE__ */ __name(async (res, req, context) => {
|
|
141
|
+
const url = req.getUrl();
|
|
142
|
+
const query = req.getQuery();
|
|
143
|
+
const method = req.getMethod().toUpperCase();
|
|
144
|
+
const headers = {};
|
|
145
|
+
req.forEach((key, value) => {
|
|
146
|
+
headers[key] = value;
|
|
147
|
+
});
|
|
148
|
+
const params = {};
|
|
149
|
+
if (pattern.includes(":") || pattern.includes("*")) {
|
|
150
|
+
const parts = pattern.split("/");
|
|
151
|
+
let paramIndex = 0;
|
|
152
|
+
for (const part of parts) {
|
|
153
|
+
if (part.startsWith(":")) {
|
|
154
|
+
const name = part.slice(1);
|
|
155
|
+
params[name] = req.getParameter(paramIndex);
|
|
156
|
+
paramIndex++;
|
|
157
|
+
} else if (part === "*") {
|
|
158
|
+
params["*"] = req.getParameter(paramIndex);
|
|
159
|
+
paramIndex++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const secKey = headers["sec-websocket-key"];
|
|
164
|
+
const secProtocol = headers["sec-websocket-protocol"];
|
|
165
|
+
const secExtensions = headers["sec-websocket-extensions"];
|
|
166
|
+
let aborted = false;
|
|
167
|
+
res.onAborted(() => {
|
|
168
|
+
aborted = true;
|
|
169
|
+
});
|
|
170
|
+
const reqWrapper = {
|
|
171
|
+
url: url + (query ? "?" + query : ""),
|
|
172
|
+
routerPath: pattern,
|
|
173
|
+
query: new URLSearchParams(query),
|
|
174
|
+
headers,
|
|
175
|
+
method,
|
|
176
|
+
params,
|
|
177
|
+
raw: {
|
|
178
|
+
url,
|
|
179
|
+
method,
|
|
180
|
+
headers
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const resWrapper = {
|
|
184
|
+
statusCode: 200,
|
|
185
|
+
_headers: {},
|
|
186
|
+
finished: false,
|
|
187
|
+
header(key, value) {
|
|
188
|
+
this._headers[key.toLowerCase()] = value;
|
|
189
|
+
return this;
|
|
190
|
+
},
|
|
191
|
+
status(code) {
|
|
192
|
+
this.statusCode = code;
|
|
193
|
+
return this;
|
|
194
|
+
},
|
|
195
|
+
send(payload) {
|
|
196
|
+
if (aborted || this.finished) return;
|
|
197
|
+
this.finished = true;
|
|
198
|
+
res.writeStatus(`${this.statusCode} Response`);
|
|
199
|
+
for (const [k, v] of Object.entries(this._headers)) {
|
|
200
|
+
res.writeHeader(k, String(v));
|
|
201
|
+
}
|
|
202
|
+
res.end(typeof payload === "object" ? JSON.stringify(payload) : String(payload));
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
try {
|
|
207
|
+
for (const HookClass of hooks) {
|
|
208
|
+
if (aborted || resWrapper.finished) return;
|
|
209
|
+
await new Promise((resolve, reject) => {
|
|
210
|
+
try {
|
|
211
|
+
const hookInstance = typeof HookClass === "function" ? new HookClass() : HookClass;
|
|
212
|
+
hookInstance.handle(reqWrapper, resWrapper, (err) => {
|
|
213
|
+
if (err) reject(err);
|
|
214
|
+
else resolve(true);
|
|
215
|
+
});
|
|
216
|
+
} catch (err) {
|
|
217
|
+
reject(err);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
} catch (err) {
|
|
222
|
+
if (!aborted && !resWrapper.finished) {
|
|
223
|
+
console.error("WebSocket Hook Error:", err);
|
|
224
|
+
res.writeStatus("500 Internal Server Error");
|
|
225
|
+
res.end(JSON.stringify({
|
|
226
|
+
error: "Internal Server Error",
|
|
227
|
+
message: err.message
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (aborted || resWrapper.finished) return;
|
|
233
|
+
const reqData = {
|
|
234
|
+
...reqWrapper,
|
|
235
|
+
query
|
|
236
|
+
};
|
|
237
|
+
res.upgrade({
|
|
238
|
+
req: reqData
|
|
239
|
+
}, secKey, secProtocol, secExtensions, context);
|
|
240
|
+
}, "upgrade"),
|
|
241
|
+
open: /* @__PURE__ */ __name((ws) => {
|
|
242
|
+
if (behaviors.open) {
|
|
243
|
+
const data = ws.getUserData();
|
|
244
|
+
behaviors.open(ws, data.req);
|
|
245
|
+
}
|
|
246
|
+
}, "open"),
|
|
247
|
+
message: behaviors.message,
|
|
248
|
+
drain: behaviors.drain,
|
|
249
|
+
close: behaviors.close,
|
|
250
|
+
ping: behaviors.ping,
|
|
251
|
+
pong: behaviors.pong
|
|
252
|
+
});
|
|
253
|
+
}
|
|
116
254
|
get uwsApp() {
|
|
117
255
|
return uServer;
|
|
118
256
|
}
|
package/package.json
CHANGED
|
@@ -1,68 +1,70 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "owebjs",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A flexible and modern web framework built on top of Fastify",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
-
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": {
|
|
12
|
-
"types": "./dist/index.d.ts",
|
|
13
|
-
"import": "./dist/index.js",
|
|
14
|
-
"default": "./dist/index.js"
|
|
15
|
-
},
|
|
16
|
-
"./dist/plugins": {
|
|
17
|
-
"types": "./dist/plugins/index.d.ts",
|
|
18
|
-
"import": "./dist/plugins/index.js",
|
|
19
|
-
"default": "./dist/plugins/index.js"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"start": "node .",
|
|
24
|
-
"build": "tsup",
|
|
25
|
-
"dev": "tsup && node .",
|
|
26
|
-
"test": "tsup && node test/index.js",
|
|
27
|
-
"format": "prettier --write . --ignore-path .gitignore"
|
|
28
|
-
},
|
|
29
|
-
"homepage": "https://github.com/owebjs/oweb",
|
|
30
|
-
"repository": {
|
|
31
|
-
"type": "git",
|
|
32
|
-
"url": "https://github.com/owebjs/oweb"
|
|
33
|
-
},
|
|
34
|
-
"keywords": [],
|
|
35
|
-
"author": "owebjs",
|
|
36
|
-
"license": "MIT",
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"@babel/core": "^7.28.0",
|
|
39
|
-
"@babel/generator": "^7.28.0",
|
|
40
|
-
"@babel/parser": "^7.28.0",
|
|
41
|
-
"@babel/preset-typescript": "^7.27.1",
|
|
42
|
-
"@babel/traverse": "^7.28.0",
|
|
43
|
-
"@babel/types": "^7.28.2",
|
|
44
|
-
"@fastify/websocket": "^
|
|
45
|
-
"chalk": "^5.4.1",
|
|
46
|
-
"fastify": "4.23.2",
|
|
47
|
-
"path-to-regexp": "^8.2.0",
|
|
48
|
-
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.52.0"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"chokidar": "^
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "owebjs",
|
|
3
|
+
"version": "1.5.0-dev",
|
|
4
|
+
"description": "A flexible and modern web framework built on top of Fastify",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./dist/plugins": {
|
|
17
|
+
"types": "./dist/plugins/index.d.ts",
|
|
18
|
+
"import": "./dist/plugins/index.js",
|
|
19
|
+
"default": "./dist/plugins/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"start": "node .",
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup && node .",
|
|
26
|
+
"test": "tsup && node test/index.js",
|
|
27
|
+
"format": "prettier --write . --ignore-path .gitignore"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/owebjs/oweb",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/owebjs/oweb"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [],
|
|
35
|
+
"author": "owebjs",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@babel/core": "^7.28.0",
|
|
39
|
+
"@babel/generator": "^7.28.0",
|
|
40
|
+
"@babel/parser": "^7.28.0",
|
|
41
|
+
"@babel/preset-typescript": "^7.27.1",
|
|
42
|
+
"@babel/traverse": "^7.28.0",
|
|
43
|
+
"@babel/types": "^7.28.2",
|
|
44
|
+
"@fastify/websocket": "^10.0.1",
|
|
45
|
+
"chalk": "^5.4.1",
|
|
46
|
+
"fastify": "4.23.2",
|
|
47
|
+
"path-to-regexp": "^8.2.0",
|
|
48
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.52.0",
|
|
49
|
+
"ws": "^8.19.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@fastify/cors": "^9.0.1",
|
|
53
|
+
"@fastify/multipart": "^8.1.0",
|
|
54
|
+
"@swc/core": "^1.3.85",
|
|
55
|
+
"@types/chokidar": "^2.1.3",
|
|
56
|
+
"@types/node": "^24.1.0",
|
|
57
|
+
"@types/ws": "^8.18.1",
|
|
58
|
+
"chokidar": "^3.5.3",
|
|
59
|
+
"prettier": "^3.0.3",
|
|
60
|
+
"tslib": "^2.6.2",
|
|
61
|
+
"tsup": "^8.5.0",
|
|
62
|
+
"typescript": "^5.2.2"
|
|
63
|
+
},
|
|
64
|
+
"type": "module",
|
|
65
|
+
"pnpm": {
|
|
66
|
+
"onlyBuiltDependencies": [
|
|
67
|
+
"esbuild"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|