@bool-ts/core 1.7.16 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +1 -1
- package/__test/controller.ts +1 -1
- package/__test/index.ts +4 -1
- package/__test/module.ts +3 -1
- package/__test/tsconfig.json +109 -0
- package/__test/webSocket.ts +37 -0
- package/__test/webSocketClient.ts +23 -0
- package/dist/decorators/controller.d.ts +3 -3
- package/dist/decorators/controller.js +1 -2
- package/dist/decorators/dispatcher.d.ts +1 -1
- package/dist/decorators/http.d.ts +12 -12
- package/dist/decorators/http.js +1 -1
- package/dist/decorators/index.d.ts +5 -0
- package/dist/decorators/index.js +3 -0
- package/dist/decorators/module.d.ts +3 -1
- package/dist/decorators/module.js +9 -2
- package/dist/decorators/webSocket.d.ts +25 -0
- package/dist/decorators/webSocket.js +40 -0
- package/dist/decorators/webSocketArguments.d.ts +22 -0
- package/dist/decorators/webSocketArguments.js +49 -0
- package/dist/decorators/webSocketEvent.d.ts +15 -0
- package/dist/decorators/webSocketEvent.js +24 -0
- package/dist/decorators/zodSchema.js +3 -3
- package/dist/entities/{route.d.ts → httpRoute.d.ts} +12 -12
- package/dist/entities/{route.js → httpRoute.js} +27 -25
- package/dist/entities/httpRouter.d.ts +10 -0
- package/dist/entities/{router.js → httpRouter.js} +7 -5
- package/dist/entities/{routerGroup.d.ts → httpRouterGroup.d.ts} +4 -4
- package/dist/entities/{routerGroup.js → httpRouterGroup.js} +1 -1
- package/dist/entities/index.d.ts +7 -4
- package/dist/entities/index.js +6 -3
- package/dist/entities/webSocketRoute.d.ts +10 -0
- package/dist/entities/webSocketRoute.js +15 -0
- package/dist/entities/webSocketRouter.d.ts +31 -0
- package/dist/entities/webSocketRouter.js +54 -0
- package/dist/entities/webSocketRouterGroup.d.ts +25 -0
- package/dist/entities/webSocketRouterGroup.js +51 -0
- package/dist/hooks/factory.d.ts +1 -39
- package/dist/hooks/factory.js +450 -76
- package/dist/hooks/injector.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/webSocket.d.ts +2 -0
- package/dist/interfaces/webSocket.js +1 -0
- package/dist/keys/index.d.ts +10 -1
- package/dist/keys/index.js +21 -12
- package/dist/ultils/colors.d.ts +30 -0
- package/dist/ultils/colors.js +41 -0
- package/dist/ultils/index.d.ts +2 -0
- package/dist/ultils/index.js +2 -0
- package/dist/ultils/socket.d.ts +1 -0
- package/dist/ultils/socket.js +7 -0
- package/package.json +8 -7
- package/src/decorators/controller.ts +5 -4
- package/src/decorators/dispatcher.ts +2 -1
- package/src/decorators/guard.ts +1 -0
- package/src/decorators/http.ts +3 -3
- package/src/decorators/index.ts +10 -0
- package/src/decorators/middleware.ts +1 -0
- package/src/decorators/module.ts +14 -3
- package/src/decorators/webSocket.ts +81 -0
- package/src/decorators/webSocketArguments.ts +144 -0
- package/src/decorators/webSocketEvent.ts +56 -0
- package/src/decorators/zodSchema.ts +5 -3
- package/src/entities/{route.ts → httpRoute.ts} +71 -57
- package/src/entities/{router.ts → httpRouter.ts} +8 -6
- package/src/entities/{routerGroup.ts → httpRouterGroup.ts} +4 -4
- package/src/entities/index.ts +7 -4
- package/src/entities/webSocketRoute.ts +27 -0
- package/src/entities/webSocketRouter.ts +64 -0
- package/src/entities/webSocketRouterGroup.ts +64 -0
- package/src/hooks/factory.ts +704 -112
- package/src/hooks/injector.ts +2 -2
- package/src/index.ts +1 -1
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/webSocket.ts +1 -0
- package/src/keys/index.ts +22 -12
- package/src/ultils/colors.ts +56 -0
- package/src/ultils/index.ts +3 -1
- package/src/ultils/socket.ts +9 -0
- package/test.ts +0 -0
- package/tsconfig.json +3 -2
- package/bun.lockb +0 -0
- package/dist/entities/router.d.ts +0 -10
package/dist/hooks/factory.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import "colors";
|
|
2
1
|
import "reflect-metadata";
|
|
3
2
|
import Qs from "qs";
|
|
4
3
|
import * as Zod from "zod";
|
|
5
|
-
import {
|
|
4
|
+
import { ETimeUnit, add as TimeAdd } from "@bool-ts/date-time";
|
|
5
|
+
import { HttpRouter, HttpRouterGroup, WebSocketRoute, WebSocketRouter, WebSocketRouterGroup } from "../entities";
|
|
6
6
|
import { HttpClientError, HttpServerError, jsonErrorInfer } from "../http";
|
|
7
|
-
import { argumentsKey, configKey, contextArgsKey, controllerHttpKey, controllerKey, moduleKey, paramArgsKey, paramsArgsKey, queryArgsKey, requestArgsKey, requestBodyArgsKey, requestHeaderArgsKey, requestHeadersArgsKey, responseBodyArgsKey, responseHeadersArgsKey, routeModelArgsKey } from "../keys";
|
|
7
|
+
import { argumentsKey, configKey, contextArgsKey, controllerHttpKey, controllerKey, moduleKey, paramArgsKey, paramsArgsKey, queryArgsKey, requestArgsKey, requestBodyArgsKey, requestHeaderArgsKey, requestHeadersArgsKey, responseBodyArgsKey, responseHeadersArgsKey, routeModelArgsKey, webSocketCloseCodeArgsKey, webSocketCloseReasonArgsKey, webSocketConnectionArgsKey, webSocketKey, webSocketMessageArgsKey, webSocketServerArgsKey } from "../keys";
|
|
8
|
+
import { ansiText, isWebSocketUpgrade } from "../ultils";
|
|
8
9
|
import { Injector } from "./injector";
|
|
9
|
-
|
|
10
|
+
const DEFAULT_STATIC_CACHE_TIME_IN_SECONDS = 900;
|
|
11
|
+
const responseConverter = (response) => {
|
|
10
12
|
response.headers.set("X-Powered-By", "Bool Typescript");
|
|
11
13
|
return response;
|
|
12
14
|
};
|
|
13
|
-
|
|
15
|
+
const controllerCreator = ({ controllerConstructor, httpRouterGroup, injector, prefix }) => {
|
|
14
16
|
if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
|
|
15
17
|
throw Error(`${controllerConstructor.name} is not a controller.`);
|
|
16
18
|
}
|
|
@@ -22,8 +24,9 @@ export const controllerCreator = (controllerConstructor, group, injector, prefix
|
|
|
22
24
|
prefix: "/",
|
|
23
25
|
httpMetadata: []
|
|
24
26
|
};
|
|
25
|
-
const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) ||
|
|
26
|
-
|
|
27
|
+
const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) ||
|
|
28
|
+
[]);
|
|
29
|
+
const router = new HttpRouter(`/${prefix || ""}/${controllerMetadata.prefix}`);
|
|
27
30
|
routesMetadata.forEach((routeMetadata) => {
|
|
28
31
|
if (typeof routeMetadata.descriptor.value !== "function") {
|
|
29
32
|
return;
|
|
@@ -50,9 +53,64 @@ export const controllerCreator = (controllerConstructor, group, injector, prefix
|
|
|
50
53
|
return route.options(routeArgument);
|
|
51
54
|
}
|
|
52
55
|
});
|
|
53
|
-
return
|
|
56
|
+
return httpRouterGroup.add(router);
|
|
54
57
|
};
|
|
55
|
-
|
|
58
|
+
const webSocketCreator = ({ injector, httpRouterGroup, prefix, webSocketRouterGroup, webSocketConstructor }) => {
|
|
59
|
+
if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
|
|
60
|
+
throw Error(`${webSocketConstructor.name} is not a controller.`);
|
|
61
|
+
}
|
|
62
|
+
const webSocket = injector.get(webSocketConstructor);
|
|
63
|
+
if (!webSocket) {
|
|
64
|
+
throw Error("Can not initialize webSocket.");
|
|
65
|
+
}
|
|
66
|
+
const webSocketMetadata = Reflect.getOwnMetadata(webSocketKey, webSocketConstructor) || {
|
|
67
|
+
prefix: "/",
|
|
68
|
+
events: [],
|
|
69
|
+
http: []
|
|
70
|
+
};
|
|
71
|
+
const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
|
|
72
|
+
//#region [HTTP ROUTER]
|
|
73
|
+
const router = new HttpRouter(fullPrefix);
|
|
74
|
+
for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
|
|
75
|
+
if (typeof httpMetadata.descriptor?.value !== "function") {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const route = router.route(httpMetadata.path);
|
|
79
|
+
const handler = httpMetadata.descriptor.value.bind(webSocket);
|
|
80
|
+
const routeArgument = Object.freeze({
|
|
81
|
+
class: webSocketConstructor,
|
|
82
|
+
funcName: httpMetadata.methodName,
|
|
83
|
+
func: handler
|
|
84
|
+
});
|
|
85
|
+
switch (httpMetadata.httpMethod) {
|
|
86
|
+
case "GET":
|
|
87
|
+
route.get(routeArgument);
|
|
88
|
+
break;
|
|
89
|
+
case "POST":
|
|
90
|
+
route.post(routeArgument);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
httpRouterGroup.add(router);
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region [WEBSOCKET ROUTER]
|
|
97
|
+
const webSocketRouter = new WebSocketRouter(fullPrefix);
|
|
98
|
+
for (const [key, event] of Object.entries(webSocketMetadata.events)) {
|
|
99
|
+
const webSocketRoute = new WebSocketRoute({
|
|
100
|
+
eventName: key,
|
|
101
|
+
metadata: event
|
|
102
|
+
});
|
|
103
|
+
webSocketRouter.addRoutes(webSocketRoute);
|
|
104
|
+
}
|
|
105
|
+
webSocketRouter.bind(webSocket);
|
|
106
|
+
webSocketRouterGroup.addRouters(webSocketRouter);
|
|
107
|
+
//#endregion
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
httpRouterGroup: httpRouterGroup,
|
|
110
|
+
webSocketRouterGroup: webSocketRouterGroup
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
const argumentsResolution = async (data, zodSchema, argumentIndex, funcName) => {
|
|
56
114
|
try {
|
|
57
115
|
const validation = await zodSchema.safeParseAsync(data);
|
|
58
116
|
if (!validation.success) {
|
|
@@ -83,7 +141,7 @@ export const argumentsResolution = async (data, zodSchema, argumentIndex, funcNa
|
|
|
83
141
|
});
|
|
84
142
|
}
|
|
85
143
|
};
|
|
86
|
-
|
|
144
|
+
const moduleResolution = async (module, options) => {
|
|
87
145
|
if (!Reflect.getOwnMetadataKeys(module).includes(moduleKey)) {
|
|
88
146
|
throw Error(`${module.name} is not a module.`);
|
|
89
147
|
}
|
|
@@ -92,8 +150,9 @@ export const moduleResolution = async (module, options) => {
|
|
|
92
150
|
if (!moduleMetadata) {
|
|
93
151
|
return;
|
|
94
152
|
}
|
|
95
|
-
const { loaders, middlewares, guards, dispatchers, controllers, dependencies, prefix: modulePrefix, config: moduleConfig } = moduleMetadata;
|
|
96
|
-
|
|
153
|
+
const { loaders, middlewares, guards, dispatchers, controllers, dependencies, webSockets, prefix: modulePrefix, config: moduleConfig } = moduleMetadata;
|
|
154
|
+
const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
|
|
155
|
+
//#region [Configuration(s)]
|
|
97
156
|
const { config } = Object.freeze({
|
|
98
157
|
config: {
|
|
99
158
|
...(typeof options.config !== "function" ? options.config : await options.config()),
|
|
@@ -104,19 +163,30 @@ export const moduleResolution = async (module, options) => {
|
|
|
104
163
|
: await moduleConfig())
|
|
105
164
|
}
|
|
106
165
|
});
|
|
107
|
-
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region [Register config like an injection]
|
|
108
168
|
injector.set(configKey, config);
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region [Run loader(s)]
|
|
109
171
|
if (loaders) {
|
|
110
172
|
const loaderFunctions = [];
|
|
111
173
|
for (const [key, func] of Object.entries(loaders)) {
|
|
112
174
|
loaderFunctions.push(async () => {
|
|
113
175
|
try {
|
|
114
176
|
const result = await func({ config });
|
|
115
|
-
console.info(
|
|
177
|
+
console.info(`${ansiText(" INFO ", {
|
|
178
|
+
color: "white",
|
|
179
|
+
backgroundColor: "blue",
|
|
180
|
+
bold: true
|
|
181
|
+
})} Loader [${key}] initialized successfully.`);
|
|
116
182
|
return result;
|
|
117
183
|
}
|
|
118
184
|
catch (error) {
|
|
119
|
-
console.error(
|
|
185
|
+
console.error(`${ansiText(" WARN ", {
|
|
186
|
+
color: "yellow",
|
|
187
|
+
backgroundColor: "red",
|
|
188
|
+
bold: true
|
|
189
|
+
})} Loader [${key}] initialization failed.`);
|
|
120
190
|
options.debug && console.error(error);
|
|
121
191
|
throw error;
|
|
122
192
|
}
|
|
@@ -128,31 +198,33 @@ export const moduleResolution = async (module, options) => {
|
|
|
128
198
|
injector.set(key, value);
|
|
129
199
|
}
|
|
130
200
|
}
|
|
131
|
-
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region [Dependencies]
|
|
132
203
|
!dependencies || dependencies.map((dependency) => injector.get(dependency));
|
|
133
|
-
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region [Middleware(s)]
|
|
134
206
|
const startMiddlewareGroup = [];
|
|
135
207
|
const endMiddlewareGroup = [];
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const instance = injector.get(
|
|
208
|
+
middlewares &&
|
|
209
|
+
middlewares.forEach((middleware) => {
|
|
210
|
+
const instance = injector.get(middleware);
|
|
139
211
|
if (instance.start && typeof instance.start === "function") {
|
|
140
212
|
startMiddlewareGroup.push(Object.freeze({
|
|
141
|
-
class:
|
|
213
|
+
class: middleware,
|
|
142
214
|
funcName: "start",
|
|
143
215
|
func: instance.start.bind(instance)
|
|
144
216
|
}));
|
|
145
217
|
}
|
|
146
218
|
if (instance.end && typeof instance.end === "function") {
|
|
147
219
|
endMiddlewareGroup.push(Object.freeze({
|
|
148
|
-
class:
|
|
220
|
+
class: middleware,
|
|
149
221
|
funcName: "end",
|
|
150
222
|
func: instance.end.bind(instance)
|
|
151
223
|
}));
|
|
152
224
|
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
225
|
+
});
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region [Guard(s)]
|
|
156
228
|
const guardGroup = !guards
|
|
157
229
|
? []
|
|
158
230
|
: guards.map((guard) => {
|
|
@@ -163,32 +235,51 @@ export const moduleResolution = async (module, options) => {
|
|
|
163
235
|
func: guardInstance.enforce.bind(guardInstance)
|
|
164
236
|
});
|
|
165
237
|
});
|
|
166
|
-
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region [Before dispatcher(s)]
|
|
167
240
|
const openDispatcherGroup = [];
|
|
168
241
|
const closeDispatcherGroup = [];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const instance = injector.get(
|
|
242
|
+
dispatchers &&
|
|
243
|
+
dispatchers.forEach((dispatcher) => {
|
|
244
|
+
const instance = injector.get(dispatcher);
|
|
172
245
|
if (instance.open && typeof instance.open === "function") {
|
|
173
246
|
openDispatcherGroup.push(Object.freeze({
|
|
174
|
-
class:
|
|
247
|
+
class: dispatcher,
|
|
175
248
|
funcName: "open",
|
|
176
249
|
func: instance.open.bind(instance)
|
|
177
250
|
}));
|
|
178
251
|
}
|
|
179
252
|
if (instance.close && typeof instance.close === "function") {
|
|
180
253
|
closeDispatcherGroup.push(Object.freeze({
|
|
181
|
-
class:
|
|
254
|
+
class: dispatcher,
|
|
182
255
|
funcName: "close",
|
|
183
256
|
func: instance.close.bind(instance)
|
|
184
257
|
}));
|
|
185
258
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
259
|
+
});
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region [Controller(s)]
|
|
262
|
+
const controllerRouterGroup = new HttpRouterGroup();
|
|
190
263
|
controllers &&
|
|
191
|
-
controllers.
|
|
264
|
+
controllers.forEach((controllerConstructor) => controllerCreator({
|
|
265
|
+
controllerConstructor,
|
|
266
|
+
httpRouterGroup: controllerRouterGroup,
|
|
267
|
+
injector: injector,
|
|
268
|
+
prefix: fullPrefix
|
|
269
|
+
}));
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region [WebSocket(s)]
|
|
272
|
+
const webSocketHttpRouterGroup = new HttpRouterGroup();
|
|
273
|
+
const webSocketRouterGroup = new WebSocketRouterGroup();
|
|
274
|
+
webSockets &&
|
|
275
|
+
webSockets.forEach((webSocket) => webSocketCreator({
|
|
276
|
+
webSocketConstructor: webSocket,
|
|
277
|
+
httpRouterGroup: webSocketHttpRouterGroup,
|
|
278
|
+
webSocketRouterGroup: webSocketRouterGroup,
|
|
279
|
+
injector,
|
|
280
|
+
prefix: fullPrefix
|
|
281
|
+
}));
|
|
282
|
+
//#endregion
|
|
192
283
|
return Object.freeze({
|
|
193
284
|
prefix: moduleMetadata.prefix,
|
|
194
285
|
injector: injector,
|
|
@@ -197,12 +288,43 @@ export const moduleResolution = async (module, options) => {
|
|
|
197
288
|
guardGroup,
|
|
198
289
|
openDispatcherGroup,
|
|
199
290
|
closeDispatcherGroup,
|
|
200
|
-
|
|
291
|
+
controllerRouterGroup,
|
|
292
|
+
webSocketHttpRouterGroup,
|
|
293
|
+
webSocketRouterGroup
|
|
201
294
|
});
|
|
202
295
|
};
|
|
203
|
-
const
|
|
204
|
-
const {
|
|
296
|
+
const webSocketFetcher = async (bun, bool) => {
|
|
297
|
+
const { request, server } = bun;
|
|
298
|
+
const { query, responseHeaders, route: { model } } = bool;
|
|
299
|
+
// Execute controller action
|
|
300
|
+
const isUpgrade = await model.func(...[server, request, query]);
|
|
301
|
+
if (typeof isUpgrade !== "boolean") {
|
|
302
|
+
return responseConverter(new Response(JSON.stringify({
|
|
303
|
+
httpCode: 500,
|
|
304
|
+
message: "Can not detect webSocket upgrade result.",
|
|
305
|
+
data: undefined
|
|
306
|
+
}), {
|
|
307
|
+
status: 500,
|
|
308
|
+
statusText: "Internal server error.",
|
|
309
|
+
headers: responseHeaders
|
|
310
|
+
}));
|
|
311
|
+
}
|
|
312
|
+
if (!isUpgrade) {
|
|
313
|
+
return responseConverter(new Response(JSON.stringify({
|
|
314
|
+
httpCode: 500,
|
|
315
|
+
message: "Can not upgrade.",
|
|
316
|
+
data: undefined
|
|
317
|
+
}), {
|
|
318
|
+
status: 500,
|
|
319
|
+
statusText: "Internal server error.",
|
|
320
|
+
headers: responseHeaders
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
return isUpgrade;
|
|
324
|
+
};
|
|
325
|
+
const httpFetcher = async (bun, bool) => {
|
|
205
326
|
const { request, server: _server } = bun;
|
|
327
|
+
const { query, responseHeaders, route: { parameters, model }, moduleResolution: { startMiddlewareGroup, endMiddlewareGroup, guardGroup, openDispatcherGroup, closeDispatcherGroup } } = bool;
|
|
206
328
|
const context = {
|
|
207
329
|
[requestHeadersArgsKey]: request.headers,
|
|
208
330
|
[responseHeadersArgsKey]: responseHeaders,
|
|
@@ -240,7 +362,9 @@ const fetcher = async (bun, bool) => {
|
|
|
240
362
|
: await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
241
363
|
break;
|
|
242
364
|
case contextArgsKey:
|
|
243
|
-
args[argsMetadata.index] = !argsMetadata.key
|
|
365
|
+
args[argsMetadata.index] = !argsMetadata.key
|
|
366
|
+
? contextHook
|
|
367
|
+
: contextHook.get(argsMetadata.key);
|
|
244
368
|
break;
|
|
245
369
|
case requestHeadersArgsKey:
|
|
246
370
|
args[argsMetadata.index] = !argsMetadata.zodSchema
|
|
@@ -268,7 +392,9 @@ const fetcher = async (bun, bool) => {
|
|
|
268
392
|
? !(argsMetadata.type in context)
|
|
269
393
|
? undefined
|
|
270
394
|
: context[argsMetadata.type]
|
|
271
|
-
: await argumentsResolution(!(argsMetadata.type in context)
|
|
395
|
+
: await argumentsResolution(!(argsMetadata.type in context)
|
|
396
|
+
? undefined
|
|
397
|
+
: context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
272
398
|
break;
|
|
273
399
|
}
|
|
274
400
|
}
|
|
@@ -297,7 +423,9 @@ const fetcher = async (bun, bool) => {
|
|
|
297
423
|
: await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
298
424
|
break;
|
|
299
425
|
case contextArgsKey:
|
|
300
|
-
args[argsMetadata.index] = !argsMetadata.key
|
|
426
|
+
args[argsMetadata.index] = !argsMetadata.key
|
|
427
|
+
? contextHook
|
|
428
|
+
: contextHook.get(argsMetadata.key);
|
|
301
429
|
break;
|
|
302
430
|
case requestHeadersArgsKey:
|
|
303
431
|
args[argsMetadata.index] = !argsMetadata.zodSchema
|
|
@@ -356,7 +484,9 @@ const fetcher = async (bun, bool) => {
|
|
|
356
484
|
: await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
357
485
|
break;
|
|
358
486
|
case contextArgsKey:
|
|
359
|
-
args[argsMetadata.index] = !argsMetadata.key
|
|
487
|
+
args[argsMetadata.index] = !argsMetadata.key
|
|
488
|
+
? contextHook
|
|
489
|
+
: contextHook.get(argsMetadata.key);
|
|
360
490
|
break;
|
|
361
491
|
case requestHeadersArgsKey:
|
|
362
492
|
args[argsMetadata.index] = !argsMetadata.zodSchema
|
|
@@ -460,7 +590,9 @@ const fetcher = async (bun, bool) => {
|
|
|
460
590
|
: await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
461
591
|
break;
|
|
462
592
|
case contextArgsKey:
|
|
463
|
-
args[argsMetadata.index] = !argsMetadata.key
|
|
593
|
+
args[argsMetadata.index] = !argsMetadata.key
|
|
594
|
+
? contextHook
|
|
595
|
+
: contextHook.get(argsMetadata.key);
|
|
464
596
|
break;
|
|
465
597
|
case requestHeadersArgsKey:
|
|
466
598
|
args[argsMetadata.index] = !argsMetadata.zodSchema
|
|
@@ -515,7 +647,9 @@ const fetcher = async (bun, bool) => {
|
|
|
515
647
|
: await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
516
648
|
break;
|
|
517
649
|
case contextArgsKey:
|
|
518
|
-
args[argsMetadata.index] = !argsMetadata.key
|
|
650
|
+
args[argsMetadata.index] = !argsMetadata.key
|
|
651
|
+
? contextHook
|
|
652
|
+
: contextHook.get(argsMetadata.key);
|
|
519
653
|
break;
|
|
520
654
|
case requestHeadersArgsKey:
|
|
521
655
|
args[argsMetadata.index] = !argsMetadata.zodSchema
|
|
@@ -543,7 +677,9 @@ const fetcher = async (bun, bool) => {
|
|
|
543
677
|
? !(argsMetadata.type in context)
|
|
544
678
|
? undefined
|
|
545
679
|
: context[argsMetadata.type]
|
|
546
|
-
: await argumentsResolution(!(argsMetadata.type in context)
|
|
680
|
+
: await argumentsResolution(!(argsMetadata.type in context)
|
|
681
|
+
? undefined
|
|
682
|
+
: context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
|
|
547
683
|
break;
|
|
548
684
|
}
|
|
549
685
|
}
|
|
@@ -567,6 +703,7 @@ const fetcher = async (bun, bool) => {
|
|
|
567
703
|
};
|
|
568
704
|
export const BoolFactory = async (modules, options) => {
|
|
569
705
|
try {
|
|
706
|
+
const staticMap = new Map();
|
|
570
707
|
const modulesConverted = !Array.isArray(modules) ? [modules] : modules;
|
|
571
708
|
const { allowLogsMethods, staticOption, allowOrigins, allowMethods, allowCredentials, allowHeaders } = Object.freeze({
|
|
572
709
|
allowLogsMethods: options?.log?.methods,
|
|
@@ -578,9 +715,18 @@ export const BoolFactory = async (modules, options) => {
|
|
|
578
715
|
? ["*"]
|
|
579
716
|
: options.cors.origins
|
|
580
717
|
: [options.cors.origins !== "*" ? options.cors.origins : "*"],
|
|
581
|
-
allowMethods: options.cors?.methods || [
|
|
718
|
+
allowMethods: options.cors?.methods || [
|
|
719
|
+
"GET",
|
|
720
|
+
"POST",
|
|
721
|
+
"PUT",
|
|
722
|
+
"PATCH",
|
|
723
|
+
"DELETE",
|
|
724
|
+
"OPTIONS"
|
|
725
|
+
],
|
|
582
726
|
allowCredentials: !options.cors?.credentials ? false : true,
|
|
583
|
-
allowHeaders: !options.cors?.headers || options.cors.headers.includes("*")
|
|
727
|
+
allowHeaders: !options.cors?.headers || options.cors.headers.includes("*")
|
|
728
|
+
? ["*"]
|
|
729
|
+
: options.cors.headers
|
|
584
730
|
});
|
|
585
731
|
const moduleResolutions = await Promise.all(modulesConverted.map((moduleConverted) => moduleResolution(moduleConverted, options)));
|
|
586
732
|
const availableModuleResolutions = moduleResolutions.filter((moduleResolution) => typeof moduleResolution !== "undefined");
|
|
@@ -588,9 +734,16 @@ export const BoolFactory = async (modules, options) => {
|
|
|
588
734
|
...new Set(availableModuleResolutions.map((availableModuleResolution) => availableModuleResolution.prefix))
|
|
589
735
|
];
|
|
590
736
|
if (prefixs.length !== availableModuleResolutions.length) {
|
|
591
|
-
throw Error(
|
|
737
|
+
throw Error("Module prefix should be unique.");
|
|
592
738
|
}
|
|
593
|
-
|
|
739
|
+
const webSocketsMap = new Map();
|
|
740
|
+
for (const availableModuleResolution of availableModuleResolutions) {
|
|
741
|
+
const webSocketMap = availableModuleResolution.webSocketRouterGroup.execute();
|
|
742
|
+
for (const [key, metadata] of webSocketMap.entries()) {
|
|
743
|
+
webSocketsMap.set(key, metadata);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const server = Bun.serve({
|
|
594
747
|
port: options.port,
|
|
595
748
|
fetch: async (request, server) => {
|
|
596
749
|
const start = performance.now();
|
|
@@ -600,10 +753,50 @@ export const BoolFactory = async (modules, options) => {
|
|
|
600
753
|
const method = request.method.toUpperCase();
|
|
601
754
|
const responseHeaders = new Headers();
|
|
602
755
|
try {
|
|
603
|
-
|
|
756
|
+
const isUpgradable = isWebSocketUpgrade(request);
|
|
757
|
+
let collection;
|
|
758
|
+
if (isUpgradable) {
|
|
759
|
+
for (const availableModuleResolution of availableModuleResolutions) {
|
|
760
|
+
const routeResult = availableModuleResolution.webSocketHttpRouterGroup.find(url.pathname, request.method);
|
|
761
|
+
if (routeResult) {
|
|
762
|
+
collection = Object.freeze({
|
|
763
|
+
route: routeResult,
|
|
764
|
+
resolution: availableModuleResolution
|
|
765
|
+
});
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (!collection) {
|
|
770
|
+
return responseConverter(new Response(JSON.stringify({
|
|
771
|
+
httpCode: 404,
|
|
772
|
+
message: "Route not found",
|
|
773
|
+
data: undefined
|
|
774
|
+
}), {
|
|
775
|
+
status: 404,
|
|
776
|
+
statusText: "Not found.",
|
|
777
|
+
headers: responseHeaders
|
|
778
|
+
}));
|
|
779
|
+
}
|
|
780
|
+
const upgradeResult = await webSocketFetcher({
|
|
781
|
+
request,
|
|
782
|
+
server
|
|
783
|
+
}, {
|
|
784
|
+
query: query,
|
|
785
|
+
responseHeaders: responseHeaders,
|
|
786
|
+
route: collection.route,
|
|
787
|
+
moduleResolution: collection.resolution
|
|
788
|
+
});
|
|
789
|
+
return upgradeResult instanceof Response ? upgradeResult : undefined;
|
|
790
|
+
}
|
|
791
|
+
allowCredentials &&
|
|
792
|
+
responseHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
604
793
|
responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(", "));
|
|
605
794
|
responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
|
|
606
|
-
responseHeaders.set("Access-Control-Allow-Origin", allowOrigins.includes("*")
|
|
795
|
+
responseHeaders.set("Access-Control-Allow-Origin", allowOrigins.includes("*")
|
|
796
|
+
? "*"
|
|
797
|
+
: !allowOrigins.includes(origin)
|
|
798
|
+
? allowOrigins[0]
|
|
799
|
+
: origin);
|
|
607
800
|
if (!allowMethods.includes(method)) {
|
|
608
801
|
return responseConverter(new Response(undefined, {
|
|
609
802
|
status: 405,
|
|
@@ -625,29 +818,60 @@ export const BoolFactory = async (modules, options) => {
|
|
|
625
818
|
}));
|
|
626
819
|
}
|
|
627
820
|
if (staticOption) {
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
821
|
+
const { path, headers, cacheTimeInSeconds } = staticOption;
|
|
822
|
+
const pathname = `${path}/${url.pathname}`;
|
|
823
|
+
const cachedFile = staticMap.get(pathname);
|
|
824
|
+
if (!cachedFile) {
|
|
825
|
+
const file = Bun.file(pathname);
|
|
826
|
+
const isFileExists = await file.exists();
|
|
827
|
+
if (isFileExists) {
|
|
828
|
+
if (headers) {
|
|
829
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
830
|
+
responseHeaders.set(key, value);
|
|
831
|
+
}
|
|
634
832
|
}
|
|
833
|
+
responseHeaders.set("Content-Type", file.type);
|
|
834
|
+
return responseConverter(new Response(await file.arrayBuffer(), {
|
|
835
|
+
status: 200,
|
|
836
|
+
statusText: "SUCCESS",
|
|
837
|
+
headers: responseHeaders
|
|
838
|
+
}));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
const isExpired = new Date() > cachedFile.expiredAt;
|
|
843
|
+
if (isExpired) {
|
|
844
|
+
staticMap.delete(pathname);
|
|
845
|
+
}
|
|
846
|
+
const file = !isExpired ? cachedFile.file : Bun.file(pathname);
|
|
847
|
+
const isFileExists = await file.exists();
|
|
848
|
+
if (isFileExists) {
|
|
849
|
+
staticMap.set(pathname, Object.freeze({
|
|
850
|
+
expiredAt: TimeAdd(new Date(), typeof cacheTimeInSeconds !== "number"
|
|
851
|
+
? DEFAULT_STATIC_CACHE_TIME_IN_SECONDS
|
|
852
|
+
: cacheTimeInSeconds, ETimeUnit.seconds),
|
|
853
|
+
file: file
|
|
854
|
+
}));
|
|
855
|
+
if (headers) {
|
|
856
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
857
|
+
responseHeaders.set(key, value);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
responseHeaders.set("Content-Type", file.type);
|
|
861
|
+
return responseConverter(new Response(await file.arrayBuffer(), {
|
|
862
|
+
status: 200,
|
|
863
|
+
statusText: "SUCCESS",
|
|
864
|
+
headers: responseHeaders
|
|
865
|
+
}));
|
|
635
866
|
}
|
|
636
|
-
responseHeaders.set("Content-Type", file.type);
|
|
637
|
-
return responseConverter(new Response(await file.arrayBuffer(), {
|
|
638
|
-
status: 200,
|
|
639
|
-
statusText: "SUCCESS",
|
|
640
|
-
headers: responseHeaders
|
|
641
|
-
}));
|
|
642
867
|
}
|
|
643
868
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const routeResult = availableModuleResolutions[i].routerGroup.find(url.pathname, request.method);
|
|
869
|
+
for (const availableModuleResolution of availableModuleResolutions) {
|
|
870
|
+
const routeResult = availableModuleResolution.controllerRouterGroup.find(url.pathname, request.method);
|
|
647
871
|
if (routeResult) {
|
|
648
872
|
collection = Object.freeze({
|
|
649
873
|
route: routeResult,
|
|
650
|
-
resolution:
|
|
874
|
+
resolution: availableModuleResolution
|
|
651
875
|
});
|
|
652
876
|
break;
|
|
653
877
|
}
|
|
@@ -657,9 +881,13 @@ export const BoolFactory = async (modules, options) => {
|
|
|
657
881
|
httpCode: 404,
|
|
658
882
|
message: "Route not found",
|
|
659
883
|
data: undefined
|
|
660
|
-
})
|
|
884
|
+
}), {
|
|
885
|
+
status: 404,
|
|
886
|
+
statusText: "Not found.",
|
|
887
|
+
headers: responseHeaders
|
|
888
|
+
}));
|
|
661
889
|
}
|
|
662
|
-
return await
|
|
890
|
+
return await httpFetcher({
|
|
663
891
|
request,
|
|
664
892
|
server
|
|
665
893
|
}, {
|
|
@@ -676,16 +904,162 @@ export const BoolFactory = async (modules, options) => {
|
|
|
676
904
|
finally {
|
|
677
905
|
if (allowLogsMethods) {
|
|
678
906
|
const end = performance.now();
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
const
|
|
907
|
+
const pathname = ansiText(url.pathname, { color: "blue" });
|
|
908
|
+
const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
|
|
909
|
+
const convertedMethod = ansiText(request.method, {
|
|
910
|
+
color: "yellow",
|
|
911
|
+
backgroundColor: "blue"
|
|
912
|
+
});
|
|
913
|
+
const convertedReqIp = ansiText(`${request.headers.get("x-forwarded-for") ||
|
|
682
914
|
request.headers.get("x-real-ip") ||
|
|
683
915
|
server.requestIP(request)?.address ||
|
|
684
|
-
"<Unknown>"}
|
|
685
|
-
|
|
916
|
+
"<Unknown>"}`, {
|
|
917
|
+
color: "yellow"
|
|
918
|
+
});
|
|
919
|
+
const convertedTime = ansiText(`${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`, {
|
|
920
|
+
color: "yellow",
|
|
921
|
+
backgroundColor: "blue"
|
|
922
|
+
});
|
|
686
923
|
allowLogsMethods.includes(request.method.toUpperCase()) &&
|
|
687
|
-
console.info(`PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${
|
|
924
|
+
console.info(`PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
websocket: {
|
|
929
|
+
open: (connection) => {
|
|
930
|
+
const pathnameKey = `${connection.data.pathname}:::open`;
|
|
931
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
932
|
+
if (!handlerMetadata) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
936
|
+
const args = [];
|
|
937
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
938
|
+
switch (argumentMetadata.type) {
|
|
939
|
+
case webSocketConnectionArgsKey:
|
|
940
|
+
args[argumentMetadata.index] = connection;
|
|
941
|
+
break;
|
|
942
|
+
case webSocketServerArgsKey:
|
|
943
|
+
args[argumentMetadata.index] = server;
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
handlerMetadata.descriptor.value(...args);
|
|
948
|
+
},
|
|
949
|
+
close: (connection, code, reason) => {
|
|
950
|
+
const pathnameKey = `${connection.data.pathname}:::close`;
|
|
951
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
952
|
+
if (!handlerMetadata) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
956
|
+
const args = [];
|
|
957
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
958
|
+
switch (argumentMetadata.type) {
|
|
959
|
+
case webSocketConnectionArgsKey:
|
|
960
|
+
args[argumentMetadata.index] = connection;
|
|
961
|
+
break;
|
|
962
|
+
case webSocketServerArgsKey:
|
|
963
|
+
args[argumentMetadata.index] = server;
|
|
964
|
+
break;
|
|
965
|
+
case webSocketCloseCodeArgsKey:
|
|
966
|
+
args[argumentMetadata.index] = code;
|
|
967
|
+
break;
|
|
968
|
+
case webSocketCloseReasonArgsKey:
|
|
969
|
+
args[argumentMetadata.index] = reason;
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
handlerMetadata.descriptor.value(...args);
|
|
974
|
+
},
|
|
975
|
+
message: (connection, message) => {
|
|
976
|
+
const pathnameKey = `${connection.data.pathname}:::message`;
|
|
977
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
978
|
+
if (!handlerMetadata) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
982
|
+
const args = [];
|
|
983
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
984
|
+
switch (argumentMetadata.type) {
|
|
985
|
+
case webSocketConnectionArgsKey:
|
|
986
|
+
args[argumentMetadata.index] = connection;
|
|
987
|
+
break;
|
|
988
|
+
case webSocketMessageArgsKey:
|
|
989
|
+
args[argumentMetadata.index] = message;
|
|
990
|
+
break;
|
|
991
|
+
case webSocketServerArgsKey:
|
|
992
|
+
args[argumentMetadata.index] = server;
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
handlerMetadata.descriptor.value(...args);
|
|
997
|
+
},
|
|
998
|
+
drain: (connection) => {
|
|
999
|
+
const pathnameKey = `${connection.data.pathname}:::drain`;
|
|
1000
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
1001
|
+
if (!handlerMetadata) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
1005
|
+
const args = [];
|
|
1006
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
1007
|
+
switch (argumentMetadata.type) {
|
|
1008
|
+
case webSocketConnectionArgsKey:
|
|
1009
|
+
args[argumentMetadata.index] = connection;
|
|
1010
|
+
break;
|
|
1011
|
+
case webSocketServerArgsKey:
|
|
1012
|
+
args[argumentMetadata.index] = server;
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
handlerMetadata.descriptor.value(...args);
|
|
1017
|
+
},
|
|
1018
|
+
ping: (connection, data) => {
|
|
1019
|
+
const pathnameKey = `${connection.data.pathname}:::ping`;
|
|
1020
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
1021
|
+
if (!handlerMetadata) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
1025
|
+
const args = [];
|
|
1026
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
1027
|
+
switch (argumentMetadata.type) {
|
|
1028
|
+
case webSocketConnectionArgsKey:
|
|
1029
|
+
args[argumentMetadata.index] = connection;
|
|
1030
|
+
break;
|
|
1031
|
+
case webSocketServerArgsKey:
|
|
1032
|
+
args[argumentMetadata.index] = server;
|
|
1033
|
+
break;
|
|
1034
|
+
case webSocketMessageArgsKey:
|
|
1035
|
+
args[argumentMetadata.index] = data;
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
handlerMetadata.descriptor.value(...args);
|
|
1040
|
+
},
|
|
1041
|
+
pong: (connection, data) => {
|
|
1042
|
+
const pathnameKey = `${connection.data.pathname}:::pong`;
|
|
1043
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
1044
|
+
if (!handlerMetadata) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
1048
|
+
const args = [];
|
|
1049
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
1050
|
+
switch (argumentMetadata.type) {
|
|
1051
|
+
case webSocketConnectionArgsKey:
|
|
1052
|
+
args[argumentMetadata.index] = connection;
|
|
1053
|
+
break;
|
|
1054
|
+
case webSocketServerArgsKey:
|
|
1055
|
+
args[argumentMetadata.index] = server;
|
|
1056
|
+
break;
|
|
1057
|
+
case webSocketMessageArgsKey:
|
|
1058
|
+
args[argumentMetadata.index] = data;
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
688
1061
|
}
|
|
1062
|
+
handlerMetadata.descriptor.value(...args);
|
|
689
1063
|
}
|
|
690
1064
|
}
|
|
691
1065
|
});
|