@bool-ts/core 1.9.28 → 2.0.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/dist/decorators/arguments.d.ts +19 -20
- package/dist/decorators/container.d.ts +10 -11
- package/dist/decorators/http.d.ts +11 -11
- package/dist/decorators/index.d.ts +1 -2
- package/dist/entities/application.d.ts +111 -0
- package/dist/entities/context.d.ts +14 -0
- package/dist/entities/httpRoute.d.ts +18 -9
- package/dist/entities/httpRouter.d.ts +13 -0
- package/dist/entities/httpRouterGroup.d.ts +6 -0
- package/dist/entities/index.d.ts +2 -0
- package/dist/entities/injector.d.ts +29 -0
- package/dist/entities/validationFailed.d.ts +5 -0
- package/dist/entities/webSocketRoute.d.ts +1 -0
- package/dist/entities/webSocketRouter.d.ts +1 -0
- package/dist/entities/webSocketRouterGroup.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +22 -21
- package/dist/interfaces/customValidator.d.ts +4 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/producers/factory.d.ts +3 -1
- package/dist/producers/index.d.ts +1 -1
- package/package.json +2 -2
- package/src/decorators/arguments.ts +44 -39
- package/src/decorators/container.ts +12 -12
- package/src/decorators/http.ts +8 -8
- package/src/decorators/index.ts +1 -2
- package/src/entities/application.ts +2236 -0
- package/src/{producers → entities}/context.ts +2 -0
- package/src/entities/httpRoute.ts +18 -9
- package/src/entities/httpRouter.ts +13 -0
- package/src/entities/httpRouterGroup.ts +10 -4
- package/src/entities/index.ts +2 -0
- package/src/entities/validationFailed.ts +5 -0
- package/src/entities/webSocketRoute.ts +2 -0
- package/src/entities/webSocketRouter.ts +2 -0
- package/src/entities/webSocketRouterGroup.ts +2 -0
- package/src/index.ts +1 -0
- package/src/interfaces/customValidator.ts +10 -0
- package/src/interfaces/index.ts +1 -0
- package/src/producers/factory.ts +7 -2037
- package/src/producers/index.ts +1 -1
- package/src/decorators/zodSchema.ts +0 -21
- /package/src/{producers → entities}/injector.ts +0 -0
|
@@ -0,0 +1,2236 @@
|
|
|
1
|
+
import type { BunFile, Server } from "bun";
|
|
2
|
+
import type {
|
|
3
|
+
TArgumentsMetadataCollection,
|
|
4
|
+
TContainerMetadata,
|
|
5
|
+
TControllerMetadata,
|
|
6
|
+
TModuleMetadata,
|
|
7
|
+
TWebSocketEventHandlerMetadata,
|
|
8
|
+
TWebSocketMetadata
|
|
9
|
+
} from "../decorators";
|
|
10
|
+
import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
|
|
11
|
+
import type { IGuard, IInterceptor, IMiddleware } from "../interfaces";
|
|
12
|
+
import type { TConstructor } from "../ultils";
|
|
13
|
+
|
|
14
|
+
import { ETimeUnit, add as TimeAdd } from "@bool-ts/date-time";
|
|
15
|
+
import { parse as QsParse } from "qs";
|
|
16
|
+
import type { ICustomValidator } from "../interfaces/customValidator";
|
|
17
|
+
import {
|
|
18
|
+
argumentsKey,
|
|
19
|
+
configKey,
|
|
20
|
+
containerKey,
|
|
21
|
+
contextArgsKey,
|
|
22
|
+
controllerKey,
|
|
23
|
+
httpServerArgsKey,
|
|
24
|
+
moduleKey,
|
|
25
|
+
paramArgsKey,
|
|
26
|
+
paramsArgsKey,
|
|
27
|
+
queryArgsKey,
|
|
28
|
+
requestArgsKey,
|
|
29
|
+
requestBodyArgsKey,
|
|
30
|
+
requestHeaderArgsKey,
|
|
31
|
+
requestHeadersArgsKey,
|
|
32
|
+
responseBodyArgsKey,
|
|
33
|
+
responseHeadersArgsKey,
|
|
34
|
+
responseStatusArgsKey,
|
|
35
|
+
responseStatusTextArgsKey,
|
|
36
|
+
routeModelArgsKey,
|
|
37
|
+
webSocketCloseCodeArgsKey,
|
|
38
|
+
webSocketCloseReasonArgsKey,
|
|
39
|
+
webSocketConnectionArgsKey,
|
|
40
|
+
webSocketKey,
|
|
41
|
+
webSocketMessageArgsKey,
|
|
42
|
+
webSocketServerArgsKey
|
|
43
|
+
} from "../keys";
|
|
44
|
+
import { ansiText, isWebSocketUpgrade } from "../ultils";
|
|
45
|
+
import { Context } from "./context";
|
|
46
|
+
import { HttpRouter } from "./httpRouter";
|
|
47
|
+
import { HttpRouterGroup } from "./httpRouterGroup";
|
|
48
|
+
import { Injector } from "./injector";
|
|
49
|
+
import ValidationFailed from "./validationFailed";
|
|
50
|
+
import { WebSocketRoute } from "./webSocketRoute";
|
|
51
|
+
import { WebSocketRouter } from "./webSocketRouter";
|
|
52
|
+
import { WebSocketRouterGroup } from "./webSocketRouterGroup";
|
|
53
|
+
|
|
54
|
+
type TParamsType = Record<string, string>;
|
|
55
|
+
|
|
56
|
+
type TApplicationOptions = Required<{
|
|
57
|
+
port: number;
|
|
58
|
+
}> &
|
|
59
|
+
Partial<{
|
|
60
|
+
config: Record<string | symbol, any> | (() => Record<string | symbol, any>);
|
|
61
|
+
prefix: string;
|
|
62
|
+
debug: boolean;
|
|
63
|
+
log: Partial<{
|
|
64
|
+
methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
|
|
65
|
+
}>;
|
|
66
|
+
queryParser: Parameters<typeof QsParse>[1];
|
|
67
|
+
static: Required<{
|
|
68
|
+
path: string;
|
|
69
|
+
}> &
|
|
70
|
+
Partial<{
|
|
71
|
+
headers: TParamsType;
|
|
72
|
+
cacheTimeInSeconds: number;
|
|
73
|
+
}>;
|
|
74
|
+
cors: Partial<{
|
|
75
|
+
credentials: boolean;
|
|
76
|
+
origins: string | Array<string>;
|
|
77
|
+
methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
|
|
78
|
+
headers: Array<string>;
|
|
79
|
+
}>;
|
|
80
|
+
}>;
|
|
81
|
+
|
|
82
|
+
type TGroupElementModel<
|
|
83
|
+
TFuncName extends keyof TClass,
|
|
84
|
+
TClass extends Object = Object,
|
|
85
|
+
TFunc = TClass[TFuncName]
|
|
86
|
+
> = Readonly<{
|
|
87
|
+
class: TClass;
|
|
88
|
+
func: TFunc;
|
|
89
|
+
funcName: TFuncName;
|
|
90
|
+
argumentsMetadata: TArgumentsMetadataCollection;
|
|
91
|
+
}>;
|
|
92
|
+
|
|
93
|
+
type TStaticMap = Map<
|
|
94
|
+
string,
|
|
95
|
+
Readonly<{
|
|
96
|
+
expiredAt: Date;
|
|
97
|
+
file: BunFile;
|
|
98
|
+
}>
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
type TResolutedOptions = Readonly<{
|
|
102
|
+
allowLogsMethods: ("GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS")[];
|
|
103
|
+
allowOrigins: string[];
|
|
104
|
+
allowMethods: string[];
|
|
105
|
+
allowHeaders: string[];
|
|
106
|
+
allowCredentials: boolean;
|
|
107
|
+
staticOption?: Required<{
|
|
108
|
+
path: string;
|
|
109
|
+
}> &
|
|
110
|
+
Partial<{
|
|
111
|
+
headers: TParamsType;
|
|
112
|
+
cacheTimeInSeconds: number;
|
|
113
|
+
}>;
|
|
114
|
+
}>;
|
|
115
|
+
|
|
116
|
+
type TWebSocketUpgradeData = {
|
|
117
|
+
pathname: string;
|
|
118
|
+
method: string;
|
|
119
|
+
query: Record<string, unknown>;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type TPreLaunch =
|
|
123
|
+
| undefined
|
|
124
|
+
| Readonly<{
|
|
125
|
+
containerMetadata: TContainerMetadata;
|
|
126
|
+
modulesConverted: Array<TConstructor<unknown>>;
|
|
127
|
+
resolutedContainer?: Awaited<ReturnType<Application["containerResolution"]>>;
|
|
128
|
+
resolutedModules: Array<Awaited<ReturnType<Application["moduleResolution"]>>>;
|
|
129
|
+
webSocketsMap: Map<string, TWebSocketEventHandlerMetadata>;
|
|
130
|
+
}>;
|
|
131
|
+
|
|
132
|
+
type TValidator = undefined | ICustomValidator;
|
|
133
|
+
|
|
134
|
+
export class Application<TRootClass extends Object = Object> {
|
|
135
|
+
#preLaunchData: TPreLaunch;
|
|
136
|
+
#inputedConstructorKeys: any[];
|
|
137
|
+
#injector = new Injector();
|
|
138
|
+
#staticMap: TStaticMap = new Map();
|
|
139
|
+
#resolutedOptions: TResolutedOptions;
|
|
140
|
+
#staticCacheTimeInSecond: number = 900;
|
|
141
|
+
#customValidator: TValidator;
|
|
142
|
+
|
|
143
|
+
constructor(
|
|
144
|
+
private readonly classConstructor: TConstructor<TRootClass>,
|
|
145
|
+
private readonly options: TApplicationOptions
|
|
146
|
+
) {
|
|
147
|
+
this.#inputedConstructorKeys = Reflect.getOwnMetadataKeys(classConstructor);
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
!this.#inputedConstructorKeys.includes(containerKey) &&
|
|
151
|
+
!this.#inputedConstructorKeys.includes(moduleKey)
|
|
152
|
+
) {
|
|
153
|
+
throw Error(
|
|
154
|
+
`Can not detect! ${classConstructor.name} class is not a container or module.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.#staticCacheTimeInSecond =
|
|
159
|
+
typeof options.static?.cacheTimeInSeconds !== "number"
|
|
160
|
+
? 900
|
|
161
|
+
: options.static.cacheTimeInSeconds;
|
|
162
|
+
|
|
163
|
+
this.#resolutedOptions = Object.freeze({
|
|
164
|
+
allowLogsMethods: this.options?.log?.methods || [],
|
|
165
|
+
staticOption: this.options.static,
|
|
166
|
+
allowOrigins: !this.options.cors?.origins
|
|
167
|
+
? ["*"]
|
|
168
|
+
: typeof this.options.cors.origins !== "string"
|
|
169
|
+
? this.options.cors.origins.includes("*") || this.options.cors.origins.length < 1
|
|
170
|
+
? ["*"]
|
|
171
|
+
: this.options.cors.origins
|
|
172
|
+
: [this.options.cors.origins !== "*" ? this.options.cors.origins : "*"],
|
|
173
|
+
allowMethods: this.options.cors?.methods || [
|
|
174
|
+
"GET",
|
|
175
|
+
"POST",
|
|
176
|
+
"PUT",
|
|
177
|
+
"PATCH",
|
|
178
|
+
"DELETE",
|
|
179
|
+
"OPTIONS"
|
|
180
|
+
],
|
|
181
|
+
allowCredentials: !this.options.cors?.credentials ? false : true,
|
|
182
|
+
allowHeaders:
|
|
183
|
+
!this.options.cors?.headers || this.options.cors.headers.includes("*")
|
|
184
|
+
? ["*"]
|
|
185
|
+
: this.options.cors.headers
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public useValidator(validator: ICustomValidator) {
|
|
190
|
+
this.#customValidator = validator;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
*
|
|
195
|
+
* @returns
|
|
196
|
+
*/
|
|
197
|
+
public async preLaunch(): Promise<NonNullable<TPreLaunch>> {
|
|
198
|
+
if (this.#preLaunchData) {
|
|
199
|
+
return this.#preLaunchData;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const containerMetadata: TContainerMetadata = !this.#inputedConstructorKeys.includes(
|
|
203
|
+
containerKey
|
|
204
|
+
)
|
|
205
|
+
? undefined
|
|
206
|
+
: Reflect.getOwnMetadata(containerKey, this.classConstructor);
|
|
207
|
+
|
|
208
|
+
const modulesConverted = !this.#inputedConstructorKeys.includes(containerKey)
|
|
209
|
+
? [this.classConstructor]
|
|
210
|
+
: containerMetadata?.modules || [];
|
|
211
|
+
|
|
212
|
+
const resolutedContainer = !containerMetadata
|
|
213
|
+
? undefined
|
|
214
|
+
: await this.containerResolution({
|
|
215
|
+
containerClass: this.classConstructor,
|
|
216
|
+
options: this.options,
|
|
217
|
+
extendInjector: this.#injector
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const resolutedModules = await Promise.all(
|
|
221
|
+
modulesConverted.map((moduleConverted) =>
|
|
222
|
+
this.moduleResolution({
|
|
223
|
+
moduleClass: moduleConverted,
|
|
224
|
+
options: this.options,
|
|
225
|
+
extendInjector: !resolutedContainer
|
|
226
|
+
? this.#injector
|
|
227
|
+
: resolutedContainer.injector
|
|
228
|
+
})
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const prefixs = [
|
|
233
|
+
...new Set(
|
|
234
|
+
resolutedModules.map(
|
|
235
|
+
(availableModuleResolution) => availableModuleResolution.prefix
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
if (prefixs.length !== resolutedModules.length) {
|
|
241
|
+
throw Error("Module prefix should be unique.");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const webSocketsMap = new Map<string, TWebSocketEventHandlerMetadata>();
|
|
245
|
+
|
|
246
|
+
for (const availableModuleResolution of resolutedModules) {
|
|
247
|
+
const webSocketMap = availableModuleResolution.webSocketRouterGroup.execute();
|
|
248
|
+
|
|
249
|
+
for (const [key, metadata] of webSocketMap.entries()) {
|
|
250
|
+
webSocketsMap.set(key, metadata);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const preLaunchData = Object.freeze({
|
|
255
|
+
containerMetadata,
|
|
256
|
+
modulesConverted,
|
|
257
|
+
resolutedContainer,
|
|
258
|
+
resolutedModules,
|
|
259
|
+
webSocketsMap
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
this.#preLaunchData = preLaunchData;
|
|
263
|
+
|
|
264
|
+
return preLaunchData;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Start listen app on a port
|
|
269
|
+
* @param port
|
|
270
|
+
*/
|
|
271
|
+
public async listen() {
|
|
272
|
+
const {
|
|
273
|
+
allowLogsMethods,
|
|
274
|
+
allowOrigins,
|
|
275
|
+
allowMethods,
|
|
276
|
+
allowHeaders,
|
|
277
|
+
allowCredentials,
|
|
278
|
+
staticOption
|
|
279
|
+
} = this.#resolutedOptions;
|
|
280
|
+
|
|
281
|
+
const { resolutedContainer, resolutedModules, webSocketsMap } = await this.preLaunch();
|
|
282
|
+
|
|
283
|
+
const server = Bun.serve<TWebSocketUpgradeData, {}>({
|
|
284
|
+
port: this.options.port,
|
|
285
|
+
fetch: async (request, server) => {
|
|
286
|
+
const start = performance.now(),
|
|
287
|
+
url = new URL(request.url),
|
|
288
|
+
query = QsParse(url.searchParams.toString(), this.options.queryParser),
|
|
289
|
+
origin = request.headers.get("origin") || "*",
|
|
290
|
+
method = request.method.toUpperCase(),
|
|
291
|
+
responseHeaders = new Headers();
|
|
292
|
+
|
|
293
|
+
let context = new Context()
|
|
294
|
+
.setOptions({ isStatic: true })
|
|
295
|
+
.set(httpServerArgsKey, server)
|
|
296
|
+
.set(requestArgsKey, request)
|
|
297
|
+
.set(requestHeaderArgsKey, request.headers)
|
|
298
|
+
.set(responseHeadersArgsKey, responseHeaders)
|
|
299
|
+
.set(queryArgsKey, query);
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const isUpgradable = isWebSocketUpgrade(request);
|
|
303
|
+
|
|
304
|
+
let collection:
|
|
305
|
+
| undefined
|
|
306
|
+
| Required<{
|
|
307
|
+
route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
|
|
308
|
+
resolution: NonNullable<
|
|
309
|
+
Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>
|
|
310
|
+
>;
|
|
311
|
+
}>;
|
|
312
|
+
|
|
313
|
+
if (isUpgradable) {
|
|
314
|
+
for (const availableModuleResolution of resolutedModules) {
|
|
315
|
+
const routeResult =
|
|
316
|
+
availableModuleResolution.webSocketHttpRouterGroup.find(
|
|
317
|
+
url.pathname,
|
|
318
|
+
request.method as keyof THttpMethods
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
if (routeResult) {
|
|
322
|
+
collection = Object.freeze({
|
|
323
|
+
route: routeResult,
|
|
324
|
+
resolution: availableModuleResolution
|
|
325
|
+
});
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!collection) {
|
|
331
|
+
return this.finalizeResponse(
|
|
332
|
+
new Response(
|
|
333
|
+
JSON.stringify({
|
|
334
|
+
httpCode: 404,
|
|
335
|
+
message: "Route not found",
|
|
336
|
+
data: undefined
|
|
337
|
+
}),
|
|
338
|
+
{
|
|
339
|
+
status: 404,
|
|
340
|
+
statusText: "Not found.",
|
|
341
|
+
headers: responseHeaders
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const upgradeResult = await this.webSocketFetcher(
|
|
348
|
+
{
|
|
349
|
+
request,
|
|
350
|
+
server
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
query: query,
|
|
354
|
+
responseHeaders: responseHeaders,
|
|
355
|
+
route: collection.route,
|
|
356
|
+
moduleResolution: collection.resolution
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
return upgradeResult instanceof Response ? upgradeResult : undefined;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
[
|
|
364
|
+
...(!allowCredentials
|
|
365
|
+
? []
|
|
366
|
+
: [
|
|
367
|
+
{
|
|
368
|
+
key: "Access-Control-Allow-Credentials",
|
|
369
|
+
value: "true"
|
|
370
|
+
}
|
|
371
|
+
]),
|
|
372
|
+
{
|
|
373
|
+
key: "Access-Control-Allow-Origin",
|
|
374
|
+
value: allowOrigins.includes("*")
|
|
375
|
+
? "*"
|
|
376
|
+
: !allowOrigins.includes(origin)
|
|
377
|
+
? allowOrigins[0]
|
|
378
|
+
: origin
|
|
379
|
+
},
|
|
380
|
+
{ key: "Access-Control-Allow-Methods", value: allowMethods.join(", ") },
|
|
381
|
+
{ key: "Access-Control-Allow-Headers", value: allowHeaders.join(", ") }
|
|
382
|
+
].forEach(({ key, value }) => responseHeaders.set(key, value));
|
|
383
|
+
|
|
384
|
+
if (!allowMethods.includes(method)) {
|
|
385
|
+
return this.finalizeResponse(
|
|
386
|
+
new Response(undefined, {
|
|
387
|
+
status: 405,
|
|
388
|
+
statusText: "Method Not Allowed.",
|
|
389
|
+
headers: responseHeaders
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (request.method.toUpperCase() === "OPTIONS") {
|
|
395
|
+
return this.finalizeResponse(
|
|
396
|
+
allowOrigins.includes("*") || allowOrigins.includes(origin)
|
|
397
|
+
? new Response(undefined, {
|
|
398
|
+
status: 204,
|
|
399
|
+
statusText: "No Content.",
|
|
400
|
+
headers: responseHeaders
|
|
401
|
+
})
|
|
402
|
+
: new Response(undefined, {
|
|
403
|
+
status: 417,
|
|
404
|
+
statusText: "Expectation Failed.",
|
|
405
|
+
headers: responseHeaders
|
|
406
|
+
})
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (staticOption) {
|
|
411
|
+
const { path, headers } = staticOption;
|
|
412
|
+
const pathname = `${path}/${url.pathname}`;
|
|
413
|
+
const cachedFile = this.#staticMap.get(pathname);
|
|
414
|
+
|
|
415
|
+
if (!cachedFile) {
|
|
416
|
+
const file = Bun.file(pathname);
|
|
417
|
+
const isFileExists = await file.exists();
|
|
418
|
+
|
|
419
|
+
if (isFileExists) {
|
|
420
|
+
if (headers) {
|
|
421
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
422
|
+
responseHeaders.set(key, value);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
responseHeaders.set("Content-Type", file.type);
|
|
427
|
+
|
|
428
|
+
return this.finalizeResponse(
|
|
429
|
+
new Response(await file.arrayBuffer(), {
|
|
430
|
+
status: 200,
|
|
431
|
+
statusText: "SUCCESS",
|
|
432
|
+
headers: responseHeaders
|
|
433
|
+
})
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
const isExpired = new Date() > cachedFile.expiredAt;
|
|
438
|
+
|
|
439
|
+
if (isExpired) {
|
|
440
|
+
this.#staticMap.delete(pathname);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const file = !isExpired ? cachedFile.file : Bun.file(pathname);
|
|
444
|
+
const isFileExists = await file.exists();
|
|
445
|
+
|
|
446
|
+
if (isFileExists) {
|
|
447
|
+
this.#staticMap.set(
|
|
448
|
+
pathname,
|
|
449
|
+
Object.freeze({
|
|
450
|
+
expiredAt: TimeAdd(
|
|
451
|
+
new Date(),
|
|
452
|
+
this.#staticCacheTimeInSecond,
|
|
453
|
+
ETimeUnit.seconds
|
|
454
|
+
),
|
|
455
|
+
file: file
|
|
456
|
+
})
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
if (headers) {
|
|
460
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
461
|
+
responseHeaders.set(key, value);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
responseHeaders.set("Content-Type", file.type);
|
|
466
|
+
|
|
467
|
+
return this.finalizeResponse(
|
|
468
|
+
new Response(await file.arrayBuffer(), {
|
|
469
|
+
status: 200,
|
|
470
|
+
statusText: "SUCCESS",
|
|
471
|
+
headers: responseHeaders
|
|
472
|
+
})
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (resolutedContainer) {
|
|
479
|
+
const { context: newContext } = await this.httpFetcher({
|
|
480
|
+
context: context,
|
|
481
|
+
resolutedMap: {
|
|
482
|
+
injector: resolutedContainer.injector,
|
|
483
|
+
startMiddlewareGroup: resolutedContainer.startMiddlewareGroup,
|
|
484
|
+
guardGroup: resolutedContainer.guardGroup
|
|
485
|
+
},
|
|
486
|
+
options: {
|
|
487
|
+
isContainer: true
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
context = newContext;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
for (const availableModuleResolution of resolutedModules) {
|
|
495
|
+
const routeResult = availableModuleResolution.controllerRouterGroup.find(
|
|
496
|
+
url.pathname,
|
|
497
|
+
method as keyof THttpMethods
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
if (routeResult) {
|
|
501
|
+
collection = Object.freeze({
|
|
502
|
+
route: routeResult,
|
|
503
|
+
resolution: availableModuleResolution
|
|
504
|
+
});
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!collection) {
|
|
510
|
+
context
|
|
511
|
+
.setOptions({ isStatic: false })
|
|
512
|
+
.set(responseStatusArgsKey, 404)
|
|
513
|
+
.set(responseStatusTextArgsKey, "Not found.")
|
|
514
|
+
.set(
|
|
515
|
+
responseBodyArgsKey,
|
|
516
|
+
JSON.stringify({
|
|
517
|
+
httpCode: 404,
|
|
518
|
+
message: "Route not found",
|
|
519
|
+
data: undefined
|
|
520
|
+
})
|
|
521
|
+
);
|
|
522
|
+
} else {
|
|
523
|
+
const { context: newContext } = await this.httpFetcher({
|
|
524
|
+
context: context,
|
|
525
|
+
route: collection.route,
|
|
526
|
+
resolutedMap: collection.resolution
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
context = newContext;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (resolutedContainer) {
|
|
533
|
+
const { context: newContext } = await this.httpFetcher({
|
|
534
|
+
context: context,
|
|
535
|
+
resolutedMap: {
|
|
536
|
+
injector: resolutedContainer.injector,
|
|
537
|
+
endMiddlewareGroup: resolutedContainer.endMiddlewareGroup
|
|
538
|
+
},
|
|
539
|
+
options: {
|
|
540
|
+
isContainer: true
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
context = newContext;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const latestResponseHeaders =
|
|
548
|
+
context.get<Headers | null | undefined>(responseHeadersArgsKey, {
|
|
549
|
+
isStatic: true
|
|
550
|
+
}) || new Headers(),
|
|
551
|
+
latestResponseBody =
|
|
552
|
+
context.get<unknown>(responseBodyArgsKey, { isStatic: false }) ||
|
|
553
|
+
undefined,
|
|
554
|
+
latestResponseStatus = context.get<unknown>(responseStatusArgsKey, {
|
|
555
|
+
isStatic: false
|
|
556
|
+
}),
|
|
557
|
+
latestResponseStatusText = context.get<unknown>(responseStatusArgsKey, {
|
|
558
|
+
isStatic: false
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
return this.serializeResponse({
|
|
562
|
+
status:
|
|
563
|
+
typeof latestResponseStatus !== "number"
|
|
564
|
+
? undefined
|
|
565
|
+
: latestResponseStatus,
|
|
566
|
+
statusText:
|
|
567
|
+
typeof latestResponseStatusText !== "string"
|
|
568
|
+
? undefined
|
|
569
|
+
: latestResponseStatusText,
|
|
570
|
+
headers: latestResponseHeaders,
|
|
571
|
+
data: latestResponseBody
|
|
572
|
+
});
|
|
573
|
+
} catch (error) {
|
|
574
|
+
this.options.debug && console.error(error);
|
|
575
|
+
|
|
576
|
+
return this.finalizeResponse(jsonErrorInfer(error, responseHeaders));
|
|
577
|
+
} finally {
|
|
578
|
+
if (allowLogsMethods) {
|
|
579
|
+
const end = performance.now();
|
|
580
|
+
const pathname = ansiText(url.pathname, { color: "blue" });
|
|
581
|
+
const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
|
|
582
|
+
const convertedMethod = ansiText(request.method, {
|
|
583
|
+
color: "yellow",
|
|
584
|
+
backgroundColor: "blue"
|
|
585
|
+
});
|
|
586
|
+
const convertedReqIp = ansiText(
|
|
587
|
+
`${
|
|
588
|
+
request.headers.get("x-forwarded-for") ||
|
|
589
|
+
request.headers.get("x-real-ip") ||
|
|
590
|
+
server.requestIP(request)?.address ||
|
|
591
|
+
"<Unknown>"
|
|
592
|
+
}`,
|
|
593
|
+
{
|
|
594
|
+
color: "yellow"
|
|
595
|
+
}
|
|
596
|
+
);
|
|
597
|
+
const convertedTime = ansiText(
|
|
598
|
+
`${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`,
|
|
599
|
+
{
|
|
600
|
+
color: "yellow",
|
|
601
|
+
backgroundColor: "blue"
|
|
602
|
+
}
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
allowLogsMethods.includes(
|
|
606
|
+
request.method.toUpperCase() as (typeof allowLogsMethods)[number]
|
|
607
|
+
) &&
|
|
608
|
+
console.info(
|
|
609
|
+
`PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
websocket: {
|
|
615
|
+
open: (connection) => {
|
|
616
|
+
const pathnameKey = `${connection.data.pathname}:::open`;
|
|
617
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
618
|
+
|
|
619
|
+
if (!handlerMetadata) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
624
|
+
const args: Array<unknown> = [];
|
|
625
|
+
|
|
626
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
627
|
+
switch (argumentMetadata.type) {
|
|
628
|
+
case webSocketConnectionArgsKey:
|
|
629
|
+
args[argumentMetadata.index] = connection;
|
|
630
|
+
break;
|
|
631
|
+
case webSocketServerArgsKey:
|
|
632
|
+
args[argumentMetadata.index] = server;
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
handlerMetadata.descriptor.value(...args);
|
|
638
|
+
},
|
|
639
|
+
close: (connection, code, reason) => {
|
|
640
|
+
const pathnameKey = `${connection.data.pathname}:::close`;
|
|
641
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
642
|
+
|
|
643
|
+
if (!handlerMetadata) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
648
|
+
const args: Array<unknown> = [];
|
|
649
|
+
|
|
650
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
651
|
+
switch (argumentMetadata.type) {
|
|
652
|
+
case webSocketConnectionArgsKey:
|
|
653
|
+
args[argumentMetadata.index] = connection;
|
|
654
|
+
break;
|
|
655
|
+
case webSocketServerArgsKey:
|
|
656
|
+
args[argumentMetadata.index] = server;
|
|
657
|
+
break;
|
|
658
|
+
case webSocketCloseCodeArgsKey:
|
|
659
|
+
args[argumentMetadata.index] = code;
|
|
660
|
+
break;
|
|
661
|
+
case webSocketCloseReasonArgsKey:
|
|
662
|
+
args[argumentMetadata.index] = reason;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
handlerMetadata.descriptor.value(...args);
|
|
668
|
+
},
|
|
669
|
+
message: (connection, message) => {
|
|
670
|
+
const pathnameKey = `${connection.data.pathname}:::message`;
|
|
671
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
672
|
+
|
|
673
|
+
if (!handlerMetadata) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
678
|
+
const args: Array<unknown> = [];
|
|
679
|
+
|
|
680
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
681
|
+
switch (argumentMetadata.type) {
|
|
682
|
+
case webSocketConnectionArgsKey:
|
|
683
|
+
args[argumentMetadata.index] = connection;
|
|
684
|
+
break;
|
|
685
|
+
case webSocketMessageArgsKey:
|
|
686
|
+
args[argumentMetadata.index] = message;
|
|
687
|
+
break;
|
|
688
|
+
case webSocketServerArgsKey:
|
|
689
|
+
args[argumentMetadata.index] = server;
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
handlerMetadata.descriptor.value(...args);
|
|
695
|
+
},
|
|
696
|
+
drain: (connection) => {
|
|
697
|
+
const pathnameKey = `${connection.data.pathname}:::drain`;
|
|
698
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
699
|
+
|
|
700
|
+
if (!handlerMetadata) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
705
|
+
const args: Array<unknown> = [];
|
|
706
|
+
|
|
707
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
708
|
+
switch (argumentMetadata.type) {
|
|
709
|
+
case webSocketConnectionArgsKey:
|
|
710
|
+
args[argumentMetadata.index] = connection;
|
|
711
|
+
break;
|
|
712
|
+
case webSocketServerArgsKey:
|
|
713
|
+
args[argumentMetadata.index] = server;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
handlerMetadata.descriptor.value(...args);
|
|
719
|
+
},
|
|
720
|
+
ping: (connection, data) => {
|
|
721
|
+
const pathnameKey = `${connection.data.pathname}:::ping`;
|
|
722
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
723
|
+
|
|
724
|
+
if (!handlerMetadata) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
729
|
+
const args: Array<unknown> = [];
|
|
730
|
+
|
|
731
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
732
|
+
switch (argumentMetadata.type) {
|
|
733
|
+
case webSocketConnectionArgsKey:
|
|
734
|
+
args[argumentMetadata.index] = connection;
|
|
735
|
+
break;
|
|
736
|
+
case webSocketServerArgsKey:
|
|
737
|
+
args[argumentMetadata.index] = server;
|
|
738
|
+
break;
|
|
739
|
+
case webSocketMessageArgsKey:
|
|
740
|
+
args[argumentMetadata.index] = data;
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
handlerMetadata.descriptor.value(...args);
|
|
746
|
+
},
|
|
747
|
+
pong: (connection, data) => {
|
|
748
|
+
const pathnameKey = `${connection.data.pathname}:::pong`;
|
|
749
|
+
const handlerMetadata = webSocketsMap.get(pathnameKey);
|
|
750
|
+
|
|
751
|
+
if (!handlerMetadata) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const argumentsMetadata = handlerMetadata.arguments || {};
|
|
756
|
+
const args: Array<unknown> = [];
|
|
757
|
+
|
|
758
|
+
for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
|
|
759
|
+
switch (argumentMetadata.type) {
|
|
760
|
+
case webSocketConnectionArgsKey:
|
|
761
|
+
args[argumentMetadata.index] = connection;
|
|
762
|
+
break;
|
|
763
|
+
case webSocketServerArgsKey:
|
|
764
|
+
args[argumentMetadata.index] = server;
|
|
765
|
+
break;
|
|
766
|
+
case webSocketMessageArgsKey:
|
|
767
|
+
args[argumentMetadata.index] = data;
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
handlerMetadata.descriptor.value(...args);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
*
|
|
780
|
+
* @param param0
|
|
781
|
+
* @returns
|
|
782
|
+
*/
|
|
783
|
+
private async containerResolution({
|
|
784
|
+
containerClass,
|
|
785
|
+
options,
|
|
786
|
+
extendInjector
|
|
787
|
+
}: {
|
|
788
|
+
containerClass: TConstructor<unknown>;
|
|
789
|
+
options: TApplicationOptions;
|
|
790
|
+
extendInjector: Injector;
|
|
791
|
+
}) {
|
|
792
|
+
if (!Reflect.getOwnMetadataKeys(containerClass).includes(containerKey)) {
|
|
793
|
+
throw Error(`${containerClass.name} is not a container.`);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const injector = new Injector(extendInjector);
|
|
797
|
+
const containerMetadata: TContainerMetadata = Reflect.getOwnMetadata(
|
|
798
|
+
containerKey,
|
|
799
|
+
containerClass
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
const {
|
|
803
|
+
loaders,
|
|
804
|
+
middlewares,
|
|
805
|
+
guards,
|
|
806
|
+
dependencies,
|
|
807
|
+
config: containerConfig
|
|
808
|
+
} = containerMetadata || {};
|
|
809
|
+
|
|
810
|
+
//#region [Configuration(s)]
|
|
811
|
+
const { config } = Object.freeze({
|
|
812
|
+
config: {
|
|
813
|
+
...(typeof options.config !== "function" ? options.config : await options.config()),
|
|
814
|
+
...(typeof containerConfig !== "function"
|
|
815
|
+
? typeof containerConfig !== "object"
|
|
816
|
+
? undefined
|
|
817
|
+
: "key" in containerConfig &&
|
|
818
|
+
"value" in containerConfig &&
|
|
819
|
+
typeof containerConfig.key === "symbol"
|
|
820
|
+
? typeof containerConfig.value !== "function"
|
|
821
|
+
? containerConfig.value
|
|
822
|
+
: await containerConfig.value()
|
|
823
|
+
: containerConfig
|
|
824
|
+
: await containerConfig())
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
//#endregion
|
|
828
|
+
|
|
829
|
+
//#region [Register config like an injection]
|
|
830
|
+
injector.set(
|
|
831
|
+
containerConfig &&
|
|
832
|
+
"key" in containerConfig &&
|
|
833
|
+
"value" in containerConfig &&
|
|
834
|
+
typeof containerConfig.key === "symbol"
|
|
835
|
+
? containerConfig.key
|
|
836
|
+
: configKey,
|
|
837
|
+
config
|
|
838
|
+
);
|
|
839
|
+
//#endregion
|
|
840
|
+
|
|
841
|
+
//#region [Run loader(s)]
|
|
842
|
+
if (loaders) {
|
|
843
|
+
const loaderFunctions = [];
|
|
844
|
+
|
|
845
|
+
for (const [key, func] of Object.entries(loaders)) {
|
|
846
|
+
loaderFunctions.push(async () => {
|
|
847
|
+
try {
|
|
848
|
+
const result = await func({ config });
|
|
849
|
+
|
|
850
|
+
console.info(
|
|
851
|
+
`${ansiText(" INFO ", {
|
|
852
|
+
color: "white",
|
|
853
|
+
backgroundColor: "blue",
|
|
854
|
+
bold: true
|
|
855
|
+
})} Loader [${key}] initialized successfully.`
|
|
856
|
+
);
|
|
857
|
+
|
|
858
|
+
return result;
|
|
859
|
+
} catch (error) {
|
|
860
|
+
console.error(
|
|
861
|
+
`${ansiText(" WARN ", {
|
|
862
|
+
color: "yellow",
|
|
863
|
+
backgroundColor: "red",
|
|
864
|
+
bold: true
|
|
865
|
+
})} Loader [${key}] initialization failed.`
|
|
866
|
+
);
|
|
867
|
+
options.debug && console.error(error);
|
|
868
|
+
throw error;
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const results = await Promise.all(loaderFunctions.map((func) => func()));
|
|
874
|
+
|
|
875
|
+
for (const [key, value] of results) {
|
|
876
|
+
injector.set(key, value);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
//#endregion
|
|
880
|
+
|
|
881
|
+
//#region [Dependencies]
|
|
882
|
+
!dependencies || dependencies.map((dependency) => injector.get(dependency));
|
|
883
|
+
//#endregion
|
|
884
|
+
|
|
885
|
+
//#region [Middleware(s)]
|
|
886
|
+
const startMiddlewareGroup: Array<
|
|
887
|
+
TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>
|
|
888
|
+
> = [];
|
|
889
|
+
const endMiddlewareGroup: Array<
|
|
890
|
+
TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>
|
|
891
|
+
> = [];
|
|
892
|
+
|
|
893
|
+
!middlewares ||
|
|
894
|
+
middlewares.forEach((middleware) => {
|
|
895
|
+
const instance = injector.get<IMiddleware>(middleware);
|
|
896
|
+
|
|
897
|
+
if (instance.start && typeof instance.start === "function") {
|
|
898
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
899
|
+
Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
|
|
900
|
+
|
|
901
|
+
startMiddlewareGroup.push(
|
|
902
|
+
Object.freeze({
|
|
903
|
+
class: middleware as IMiddleware,
|
|
904
|
+
funcName: "start",
|
|
905
|
+
func: instance.start.bind(instance),
|
|
906
|
+
argumentsMetadata: argumentsMetadata
|
|
907
|
+
})
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (instance.end && typeof instance.end === "function") {
|
|
912
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
913
|
+
Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
|
|
914
|
+
|
|
915
|
+
endMiddlewareGroup.push(
|
|
916
|
+
Object.freeze({
|
|
917
|
+
class: middleware as IMiddleware,
|
|
918
|
+
funcName: "end",
|
|
919
|
+
func: instance.end.bind(instance),
|
|
920
|
+
argumentsMetadata: argumentsMetadata
|
|
921
|
+
})
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
//#endregion
|
|
926
|
+
|
|
927
|
+
//#region [Guard(s)]
|
|
928
|
+
const guardGroup: Array<
|
|
929
|
+
TGroupElementModel<"enforce", IGuard, NonNullable<IGuard["enforce"]>>
|
|
930
|
+
> = !guards
|
|
931
|
+
? []
|
|
932
|
+
: guards.map((guard) => {
|
|
933
|
+
const guardInstance = injector.get<IGuard>(guard);
|
|
934
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
935
|
+
Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
|
|
936
|
+
|
|
937
|
+
return Object.freeze({
|
|
938
|
+
class: guard as unknown as IGuard,
|
|
939
|
+
funcName: "enforce",
|
|
940
|
+
func: guardInstance.enforce.bind(guardInstance),
|
|
941
|
+
argumentsMetadata: argumentsMetadata
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
//#endregion
|
|
945
|
+
|
|
946
|
+
return Object.freeze({
|
|
947
|
+
injector,
|
|
948
|
+
startMiddlewareGroup,
|
|
949
|
+
endMiddlewareGroup,
|
|
950
|
+
guardGroup
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
*
|
|
956
|
+
* @param param0
|
|
957
|
+
* @returns
|
|
958
|
+
*/
|
|
959
|
+
private async moduleResolution({
|
|
960
|
+
moduleClass,
|
|
961
|
+
options,
|
|
962
|
+
extendInjector
|
|
963
|
+
}: {
|
|
964
|
+
moduleClass: TConstructor<unknown>;
|
|
965
|
+
options: TApplicationOptions;
|
|
966
|
+
extendInjector: Injector;
|
|
967
|
+
}) {
|
|
968
|
+
if (!Reflect.getOwnMetadataKeys(moduleClass).includes(moduleKey)) {
|
|
969
|
+
throw Error(`${moduleClass.name} is not a module.`);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const injector = new Injector(extendInjector);
|
|
973
|
+
const moduleMetadata: TModuleMetadata = Reflect.getOwnMetadata(moduleKey, moduleClass);
|
|
974
|
+
|
|
975
|
+
const {
|
|
976
|
+
loaders,
|
|
977
|
+
middlewares,
|
|
978
|
+
guards,
|
|
979
|
+
interceptors,
|
|
980
|
+
controllers,
|
|
981
|
+
dependencies,
|
|
982
|
+
webSockets,
|
|
983
|
+
prefix: modulePrefix,
|
|
984
|
+
config: moduleConfig
|
|
985
|
+
} = moduleMetadata || {};
|
|
986
|
+
|
|
987
|
+
const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
|
|
988
|
+
|
|
989
|
+
//#region [Configuration(s)]
|
|
990
|
+
const { config } = Object.freeze({
|
|
991
|
+
config: {
|
|
992
|
+
...(typeof options.config !== "function" ? options.config : await options.config()),
|
|
993
|
+
...(typeof moduleConfig !== "function"
|
|
994
|
+
? typeof moduleConfig !== "object"
|
|
995
|
+
? undefined
|
|
996
|
+
: "key" in moduleConfig &&
|
|
997
|
+
"value" in moduleConfig &&
|
|
998
|
+
typeof moduleConfig.key === "symbol"
|
|
999
|
+
? typeof moduleConfig.value !== "function"
|
|
1000
|
+
? moduleConfig.value
|
|
1001
|
+
: await moduleConfig.value()
|
|
1002
|
+
: moduleConfig
|
|
1003
|
+
: await moduleConfig())
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
//#endregion
|
|
1007
|
+
|
|
1008
|
+
//#region [Register config like an injection]
|
|
1009
|
+
injector.set(
|
|
1010
|
+
moduleConfig &&
|
|
1011
|
+
"key" in moduleConfig &&
|
|
1012
|
+
"value" in moduleConfig &&
|
|
1013
|
+
typeof moduleConfig.key === "symbol"
|
|
1014
|
+
? moduleConfig.key
|
|
1015
|
+
: configKey,
|
|
1016
|
+
config
|
|
1017
|
+
);
|
|
1018
|
+
//#endregion
|
|
1019
|
+
|
|
1020
|
+
//#region [Run loader(s)]
|
|
1021
|
+
if (loaders) {
|
|
1022
|
+
const loaderFunctions = [];
|
|
1023
|
+
|
|
1024
|
+
for (const [key, func] of Object.entries(loaders)) {
|
|
1025
|
+
loaderFunctions.push(async () => {
|
|
1026
|
+
try {
|
|
1027
|
+
const result = await func({ config });
|
|
1028
|
+
|
|
1029
|
+
console.info(
|
|
1030
|
+
`${ansiText(" INFO ", {
|
|
1031
|
+
color: "white",
|
|
1032
|
+
backgroundColor: "blue",
|
|
1033
|
+
bold: true
|
|
1034
|
+
})} Loader [${key}] initialized successfully.`
|
|
1035
|
+
);
|
|
1036
|
+
|
|
1037
|
+
return result;
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
console.error(
|
|
1040
|
+
`${ansiText(" WARN ", {
|
|
1041
|
+
color: "yellow",
|
|
1042
|
+
backgroundColor: "red",
|
|
1043
|
+
bold: true
|
|
1044
|
+
})} Loader [${key}] initialization failed.`
|
|
1045
|
+
);
|
|
1046
|
+
options.debug && console.error(error);
|
|
1047
|
+
throw error;
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const results = await Promise.all(loaderFunctions.map((func) => func()));
|
|
1053
|
+
|
|
1054
|
+
for (let i = 0; i < results.length; i++) {
|
|
1055
|
+
const [key, value] = results[i];
|
|
1056
|
+
injector.set(key, value);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
//#endregion
|
|
1060
|
+
|
|
1061
|
+
//#region [Dependencies]
|
|
1062
|
+
!dependencies || dependencies.map((dependency) => injector.get(dependency));
|
|
1063
|
+
//#endregion
|
|
1064
|
+
|
|
1065
|
+
//#region [Middleware(s)]
|
|
1066
|
+
const startMiddlewareGroup: Array<
|
|
1067
|
+
TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>
|
|
1068
|
+
> = [];
|
|
1069
|
+
const endMiddlewareGroup: Array<
|
|
1070
|
+
TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>
|
|
1071
|
+
> = [];
|
|
1072
|
+
|
|
1073
|
+
!middlewares ||
|
|
1074
|
+
middlewares.forEach((middleware) => {
|
|
1075
|
+
const instance = injector.get<IMiddleware>(middleware);
|
|
1076
|
+
|
|
1077
|
+
if (instance.start && typeof instance.start === "function") {
|
|
1078
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
1079
|
+
Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
|
|
1080
|
+
|
|
1081
|
+
startMiddlewareGroup.push(
|
|
1082
|
+
Object.freeze({
|
|
1083
|
+
class: middleware as IMiddleware,
|
|
1084
|
+
funcName: "start",
|
|
1085
|
+
func: instance.start.bind(instance),
|
|
1086
|
+
argumentsMetadata: argumentsMetadata
|
|
1087
|
+
})
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (instance.end && typeof instance.end === "function") {
|
|
1092
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
1093
|
+
Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
|
|
1094
|
+
|
|
1095
|
+
endMiddlewareGroup.push(
|
|
1096
|
+
Object.freeze({
|
|
1097
|
+
class: middleware as IMiddleware,
|
|
1098
|
+
funcName: "end",
|
|
1099
|
+
func: instance.end.bind(instance),
|
|
1100
|
+
argumentsMetadata: argumentsMetadata
|
|
1101
|
+
})
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
//#endregion
|
|
1106
|
+
|
|
1107
|
+
//#region [Guard(s)]
|
|
1108
|
+
const guardGroup: Array<
|
|
1109
|
+
TGroupElementModel<"enforce", IGuard, NonNullable<IGuard["enforce"]>>
|
|
1110
|
+
> = !guards
|
|
1111
|
+
? []
|
|
1112
|
+
: guards.map((guard) => {
|
|
1113
|
+
const guardInstance = injector.get<IGuard>(guard);
|
|
1114
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
1115
|
+
Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
|
|
1116
|
+
|
|
1117
|
+
return Object.freeze({
|
|
1118
|
+
class: guard as unknown as IGuard,
|
|
1119
|
+
funcName: "enforce",
|
|
1120
|
+
func: guardInstance.enforce.bind(guardInstance),
|
|
1121
|
+
argumentsMetadata: argumentsMetadata
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
//#endregion
|
|
1125
|
+
|
|
1126
|
+
//#region [Before interceptor(s)]
|
|
1127
|
+
const openInterceptorGroup: Array<
|
|
1128
|
+
TGroupElementModel<"open", IInterceptor, NonNullable<IInterceptor["open"]>>
|
|
1129
|
+
> = [];
|
|
1130
|
+
const closeInterceptorGroup: Array<
|
|
1131
|
+
TGroupElementModel<"close", IInterceptor, NonNullable<IInterceptor["close"]>>
|
|
1132
|
+
> = [];
|
|
1133
|
+
|
|
1134
|
+
!interceptors ||
|
|
1135
|
+
interceptors.forEach((interceptor) => {
|
|
1136
|
+
const instance = injector.get<IInterceptor>(interceptor);
|
|
1137
|
+
|
|
1138
|
+
if (instance.open && typeof instance.open === "function") {
|
|
1139
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
1140
|
+
Reflect.getOwnMetadata(argumentsKey, interceptor, "open") || {};
|
|
1141
|
+
|
|
1142
|
+
openInterceptorGroup.push(
|
|
1143
|
+
Object.freeze({
|
|
1144
|
+
class: interceptor as IInterceptor,
|
|
1145
|
+
funcName: "open",
|
|
1146
|
+
func: instance.open.bind(instance),
|
|
1147
|
+
argumentsMetadata: argumentsMetadata
|
|
1148
|
+
})
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (instance.close && typeof instance.close === "function") {
|
|
1153
|
+
const argumentsMetadata: TArgumentsMetadataCollection =
|
|
1154
|
+
Reflect.getOwnMetadata(argumentsKey, interceptor, "close") || {};
|
|
1155
|
+
|
|
1156
|
+
closeInterceptorGroup.push(
|
|
1157
|
+
Object.freeze({
|
|
1158
|
+
class: interceptor as IInterceptor,
|
|
1159
|
+
funcName: "close",
|
|
1160
|
+
func: instance.close.bind(instance),
|
|
1161
|
+
argumentsMetadata: argumentsMetadata
|
|
1162
|
+
})
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
//#endregion
|
|
1167
|
+
|
|
1168
|
+
//#region [Controller(s)]
|
|
1169
|
+
const controllerRouterGroup = new HttpRouterGroup();
|
|
1170
|
+
|
|
1171
|
+
!controllers ||
|
|
1172
|
+
controllers.forEach((controllerConstructor) =>
|
|
1173
|
+
this.initControllerInstance({
|
|
1174
|
+
controllerConstructor,
|
|
1175
|
+
httpRouterGroup: controllerRouterGroup,
|
|
1176
|
+
injector: injector,
|
|
1177
|
+
prefix: fullPrefix
|
|
1178
|
+
})
|
|
1179
|
+
);
|
|
1180
|
+
//#endregion
|
|
1181
|
+
|
|
1182
|
+
//#region [WebSocket(s)]
|
|
1183
|
+
const webSocketHttpRouterGroup = new HttpRouterGroup();
|
|
1184
|
+
const webSocketRouterGroup = new WebSocketRouterGroup();
|
|
1185
|
+
|
|
1186
|
+
webSockets &&
|
|
1187
|
+
webSockets.forEach((webSocket) =>
|
|
1188
|
+
this.initWebSocketInstance({
|
|
1189
|
+
webSocketConstructor: webSocket,
|
|
1190
|
+
httpRouterGroup: webSocketHttpRouterGroup,
|
|
1191
|
+
webSocketRouterGroup: webSocketRouterGroup,
|
|
1192
|
+
injector: injector,
|
|
1193
|
+
prefix: fullPrefix
|
|
1194
|
+
})
|
|
1195
|
+
);
|
|
1196
|
+
//#endregion
|
|
1197
|
+
|
|
1198
|
+
return Object.freeze({
|
|
1199
|
+
prefix: modulePrefix || "",
|
|
1200
|
+
injector: injector,
|
|
1201
|
+
startMiddlewareGroup: startMiddlewareGroup,
|
|
1202
|
+
endMiddlewareGroup: endMiddlewareGroup,
|
|
1203
|
+
guardGroup: guardGroup,
|
|
1204
|
+
openInterceptorGroup: openInterceptorGroup,
|
|
1205
|
+
closeInterceptorGroup: closeInterceptorGroup,
|
|
1206
|
+
controllerRouterGroup: controllerRouterGroup,
|
|
1207
|
+
webSocketHttpRouterGroup: webSocketHttpRouterGroup,
|
|
1208
|
+
webSocketRouterGroup: webSocketRouterGroup
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
*
|
|
1214
|
+
* @param data
|
|
1215
|
+
* @param zodSchema
|
|
1216
|
+
* @param argumentIndex
|
|
1217
|
+
* @param funcName
|
|
1218
|
+
* @returns
|
|
1219
|
+
*/
|
|
1220
|
+
private async argumentsResolution<TValidationSchema = unknown>(
|
|
1221
|
+
data: unknown,
|
|
1222
|
+
validationSchema: TValidationSchema,
|
|
1223
|
+
argumentIndex: number,
|
|
1224
|
+
funcName: string | symbol
|
|
1225
|
+
) {
|
|
1226
|
+
if (!this.#customValidator) {
|
|
1227
|
+
return data;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
try {
|
|
1231
|
+
const validation = await this.#customValidator.validate(
|
|
1232
|
+
data,
|
|
1233
|
+
validationSchema,
|
|
1234
|
+
argumentIndex,
|
|
1235
|
+
funcName
|
|
1236
|
+
);
|
|
1237
|
+
|
|
1238
|
+
if (!(validation instanceof ValidationFailed)) {
|
|
1239
|
+
return validation;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
throw new HttpClientError({
|
|
1243
|
+
httpCode: 400,
|
|
1244
|
+
message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
|
|
1245
|
+
data: validation.error
|
|
1246
|
+
});
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
if (error instanceof HttpClientError) {
|
|
1249
|
+
throw error;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
throw new HttpServerError({
|
|
1253
|
+
httpCode: 500,
|
|
1254
|
+
message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
|
|
1255
|
+
data: !(error instanceof Error)
|
|
1256
|
+
? error
|
|
1257
|
+
: [
|
|
1258
|
+
{
|
|
1259
|
+
message: error.message,
|
|
1260
|
+
code: error.name,
|
|
1261
|
+
cause: error.cause
|
|
1262
|
+
}
|
|
1263
|
+
]
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
*
|
|
1270
|
+
* @param param0
|
|
1271
|
+
* @returns
|
|
1272
|
+
*/
|
|
1273
|
+
private initControllerInstance({
|
|
1274
|
+
controllerConstructor,
|
|
1275
|
+
httpRouterGroup,
|
|
1276
|
+
injector,
|
|
1277
|
+
prefix
|
|
1278
|
+
}: Readonly<{
|
|
1279
|
+
controllerConstructor: TConstructor<unknown>;
|
|
1280
|
+
httpRouterGroup: HttpRouterGroup;
|
|
1281
|
+
injector: Injector;
|
|
1282
|
+
prefix?: string;
|
|
1283
|
+
}>) {
|
|
1284
|
+
if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
|
|
1285
|
+
throw Error(`${controllerConstructor.name} is not a controller.`);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const controller = injector.get(controllerConstructor);
|
|
1289
|
+
|
|
1290
|
+
if (!controller) {
|
|
1291
|
+
throw Error("Can not initialize controller.");
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(
|
|
1295
|
+
controllerKey,
|
|
1296
|
+
controllerConstructor
|
|
1297
|
+
) || {
|
|
1298
|
+
prefix: "/",
|
|
1299
|
+
httpMetadata: []
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
const router = new HttpRouter(`/${prefix || ""}/${controllerMetadata.prefix}`);
|
|
1303
|
+
|
|
1304
|
+
controllerMetadata.httpMetadata.forEach((routeMetadata) => {
|
|
1305
|
+
if (typeof routeMetadata.descriptor.value !== "function") {
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
const route = router.route(routeMetadata.path);
|
|
1310
|
+
const handler = routeMetadata.descriptor.value.bind(controller);
|
|
1311
|
+
const routeArgument = Object.freeze({
|
|
1312
|
+
class: controllerConstructor,
|
|
1313
|
+
funcName: routeMetadata.methodName,
|
|
1314
|
+
func: handler,
|
|
1315
|
+
argumentsMetadata: routeMetadata.argumentsMetadata
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
switch (routeMetadata.httpMethod) {
|
|
1319
|
+
case "GET":
|
|
1320
|
+
return route.get(routeArgument);
|
|
1321
|
+
case "POST":
|
|
1322
|
+
return route.post(routeArgument);
|
|
1323
|
+
case "PUT":
|
|
1324
|
+
return route.put(routeArgument);
|
|
1325
|
+
case "PATCH":
|
|
1326
|
+
return route.patch(routeArgument);
|
|
1327
|
+
case "DELETE":
|
|
1328
|
+
return route.delete(routeArgument);
|
|
1329
|
+
case "OPTIONS":
|
|
1330
|
+
return route.options(routeArgument);
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
return httpRouterGroup.add(router);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
*
|
|
1339
|
+
* @param param0
|
|
1340
|
+
* @returns
|
|
1341
|
+
*/
|
|
1342
|
+
private initWebSocketInstance({
|
|
1343
|
+
injector,
|
|
1344
|
+
httpRouterGroup,
|
|
1345
|
+
prefix,
|
|
1346
|
+
webSocketRouterGroup,
|
|
1347
|
+
webSocketConstructor
|
|
1348
|
+
}: Readonly<{
|
|
1349
|
+
webSocketConstructor: TConstructor<unknown>;
|
|
1350
|
+
httpRouterGroup: HttpRouterGroup;
|
|
1351
|
+
webSocketRouterGroup: WebSocketRouterGroup;
|
|
1352
|
+
injector: Injector;
|
|
1353
|
+
prefix?: string;
|
|
1354
|
+
}>): Readonly<{
|
|
1355
|
+
httpRouterGroup: HttpRouterGroup;
|
|
1356
|
+
webSocketRouterGroup: WebSocketRouterGroup;
|
|
1357
|
+
}> {
|
|
1358
|
+
if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
|
|
1359
|
+
throw Error(`${webSocketConstructor.name} is not a websocket route.`);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const webSocket = injector.get(webSocketConstructor);
|
|
1363
|
+
|
|
1364
|
+
if (!webSocket) {
|
|
1365
|
+
throw Error("Can not initialize webSocket.");
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const webSocketMetadata: TWebSocketMetadata = Reflect.getOwnMetadata(
|
|
1369
|
+
webSocketKey,
|
|
1370
|
+
webSocketConstructor
|
|
1371
|
+
) || {
|
|
1372
|
+
prefix: "/",
|
|
1373
|
+
events: [],
|
|
1374
|
+
http: []
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
|
|
1378
|
+
|
|
1379
|
+
//#region [HTTP ROUTER]
|
|
1380
|
+
const router = new HttpRouter(fullPrefix);
|
|
1381
|
+
|
|
1382
|
+
for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
|
|
1383
|
+
if (typeof httpMetadata.descriptor?.value !== "function") {
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
const route = router.route(httpMetadata.path);
|
|
1388
|
+
const handler = httpMetadata.descriptor.value.bind(webSocket);
|
|
1389
|
+
const routeArgument = Object.freeze({
|
|
1390
|
+
class: webSocketConstructor,
|
|
1391
|
+
funcName: httpMetadata.methodName,
|
|
1392
|
+
func: handler,
|
|
1393
|
+
argumentsMetadata: httpMetadata.argumentsMetadata
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
switch (httpMetadata.httpMethod) {
|
|
1397
|
+
case "GET":
|
|
1398
|
+
route.get(routeArgument);
|
|
1399
|
+
break;
|
|
1400
|
+
case "POST":
|
|
1401
|
+
route.post(routeArgument);
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
httpRouterGroup.add(router);
|
|
1407
|
+
//#endregion
|
|
1408
|
+
|
|
1409
|
+
//#region [WEBSOCKET ROUTER]
|
|
1410
|
+
const webSocketRouter = new WebSocketRouter(fullPrefix);
|
|
1411
|
+
|
|
1412
|
+
for (const [key, event] of Object.entries(webSocketMetadata.events)) {
|
|
1413
|
+
const webSocketRoute = new WebSocketRoute({
|
|
1414
|
+
eventName: key,
|
|
1415
|
+
metadata: event
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
webSocketRouter.addRoutes(webSocketRoute);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
webSocketRouter.bind(webSocket);
|
|
1422
|
+
webSocketRouterGroup.addRouters(webSocketRouter);
|
|
1423
|
+
//#endregion
|
|
1424
|
+
|
|
1425
|
+
return Object.freeze({
|
|
1426
|
+
httpRouterGroup: httpRouterGroup,
|
|
1427
|
+
webSocketRouterGroup: webSocketRouterGroup
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
*
|
|
1433
|
+
* @param param0
|
|
1434
|
+
* @returns
|
|
1435
|
+
*/
|
|
1436
|
+
private serializeResponse({
|
|
1437
|
+
status,
|
|
1438
|
+
statusText,
|
|
1439
|
+
headers,
|
|
1440
|
+
data
|
|
1441
|
+
}: {
|
|
1442
|
+
status?: number;
|
|
1443
|
+
statusText?: string;
|
|
1444
|
+
headers: Headers;
|
|
1445
|
+
data: unknown;
|
|
1446
|
+
}): Response {
|
|
1447
|
+
const contentType = headers.get("Content-Type") || "text/plain";
|
|
1448
|
+
|
|
1449
|
+
if (contentType.includes("application/json")) {
|
|
1450
|
+
return this.finalizeResponse(
|
|
1451
|
+
new Response(data instanceof ReadableStream ? data : JSON.stringify(data), {
|
|
1452
|
+
status: status,
|
|
1453
|
+
statusText: statusText,
|
|
1454
|
+
headers: headers
|
|
1455
|
+
})
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (contentType.includes("text/plain") || contentType.includes("text/html")) {
|
|
1460
|
+
return this.finalizeResponse(
|
|
1461
|
+
new Response(data instanceof ReadableStream ? data : String(data), {
|
|
1462
|
+
status: status,
|
|
1463
|
+
statusText: statusText,
|
|
1464
|
+
headers: headers
|
|
1465
|
+
})
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (contentType.includes("application/octet-stream")) {
|
|
1470
|
+
if (
|
|
1471
|
+
data instanceof Uint8Array ||
|
|
1472
|
+
data instanceof ArrayBuffer ||
|
|
1473
|
+
data instanceof Blob ||
|
|
1474
|
+
data instanceof ReadableStream
|
|
1475
|
+
) {
|
|
1476
|
+
return this.finalizeResponse(
|
|
1477
|
+
new Response(data, { status: status, statusText: statusText, headers: headers })
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
throw new Error("Invalid data type for application/octet-stream");
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
if (contentType.includes("multipart/form-data")) {
|
|
1485
|
+
if (data instanceof FormData) {
|
|
1486
|
+
return this.finalizeResponse(
|
|
1487
|
+
new Response(data, { status: status, statusText: statusText, headers: headers })
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
throw new Error("multipart/form-data requires FormData object");
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
return this.finalizeResponse(
|
|
1495
|
+
new Response(String(data), { status: status, statusText: statusText, headers: headers })
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
/**
|
|
1500
|
+
*
|
|
1501
|
+
* @param response
|
|
1502
|
+
* @returns
|
|
1503
|
+
*/
|
|
1504
|
+
private finalizeResponse(response: Response) {
|
|
1505
|
+
response.headers.set("X-Powered-By", "Bool Typescript");
|
|
1506
|
+
|
|
1507
|
+
return response;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
*
|
|
1512
|
+
* @param param0
|
|
1513
|
+
* @returns
|
|
1514
|
+
*/
|
|
1515
|
+
private async httpFetcher({
|
|
1516
|
+
context: outerContext,
|
|
1517
|
+
route,
|
|
1518
|
+
options,
|
|
1519
|
+
resolutedMap
|
|
1520
|
+
}: Partial<{
|
|
1521
|
+
route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
|
|
1522
|
+
resolutedMap:
|
|
1523
|
+
| Partial<
|
|
1524
|
+
NonNullable<Awaited<ReturnType<Application<TRootClass>["containerResolution"]>>>
|
|
1525
|
+
>
|
|
1526
|
+
| Partial<
|
|
1527
|
+
NonNullable<Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>>
|
|
1528
|
+
>;
|
|
1529
|
+
context: Context;
|
|
1530
|
+
options: Partial<{
|
|
1531
|
+
isContainer: boolean;
|
|
1532
|
+
}>;
|
|
1533
|
+
}>) {
|
|
1534
|
+
const contextOptions = { isStatic: true };
|
|
1535
|
+
const context = (!outerContext ? new Context() : new Context(outerContext)).setOptions(
|
|
1536
|
+
contextOptions
|
|
1537
|
+
);
|
|
1538
|
+
|
|
1539
|
+
if (route) {
|
|
1540
|
+
context.set(paramsArgsKey, route.parameters).set(routeModelArgsKey, route.model);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
const httpServer =
|
|
1544
|
+
context.get<Server | null | undefined>(httpServerArgsKey, contextOptions) ||
|
|
1545
|
+
undefined,
|
|
1546
|
+
request =
|
|
1547
|
+
context.get<Request | null | undefined>(requestArgsKey, contextOptions) ||
|
|
1548
|
+
undefined,
|
|
1549
|
+
requestHeaders =
|
|
1550
|
+
context.get<Headers | null | undefined>(requestHeaderArgsKey, contextOptions) ||
|
|
1551
|
+
undefined,
|
|
1552
|
+
responseHeaders =
|
|
1553
|
+
context.get<Headers | null | undefined>(responseHeadersArgsKey, contextOptions) ||
|
|
1554
|
+
undefined,
|
|
1555
|
+
parameters =
|
|
1556
|
+
context.get<TParamsType | null | undefined>(paramsArgsKey, contextOptions) ||
|
|
1557
|
+
undefined,
|
|
1558
|
+
routeModel =
|
|
1559
|
+
context.get<
|
|
1560
|
+
NonNullable<ReturnType<HttpRouterGroup["find"]>>["model"] | null | undefined
|
|
1561
|
+
>(routeModelArgsKey, contextOptions) || undefined;
|
|
1562
|
+
|
|
1563
|
+
if (resolutedMap) {
|
|
1564
|
+
const { startMiddlewareGroup, guardGroup } = resolutedMap;
|
|
1565
|
+
|
|
1566
|
+
// Execute start middleware(s)
|
|
1567
|
+
if (startMiddlewareGroup) {
|
|
1568
|
+
for (let i = 0; i < startMiddlewareGroup.length; i++) {
|
|
1569
|
+
const args = [];
|
|
1570
|
+
const {
|
|
1571
|
+
func: handler,
|
|
1572
|
+
funcName: functionName,
|
|
1573
|
+
argumentsMetadata
|
|
1574
|
+
} = startMiddlewareGroup[i];
|
|
1575
|
+
|
|
1576
|
+
for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
|
|
1577
|
+
switch (argMetadata.type) {
|
|
1578
|
+
case contextArgsKey:
|
|
1579
|
+
args[argMetadata.index] = !argMetadata.key
|
|
1580
|
+
? context
|
|
1581
|
+
: context.get(argMetadata.key, contextOptions);
|
|
1582
|
+
break;
|
|
1583
|
+
case requestArgsKey:
|
|
1584
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1585
|
+
? request
|
|
1586
|
+
: await this.argumentsResolution(
|
|
1587
|
+
request,
|
|
1588
|
+
argMetadata.validationSchema,
|
|
1589
|
+
argMetadata.index,
|
|
1590
|
+
functionName
|
|
1591
|
+
);
|
|
1592
|
+
break;
|
|
1593
|
+
case requestBodyArgsKey:
|
|
1594
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1595
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
1596
|
+
: await this.argumentsResolution(
|
|
1597
|
+
await request?.[argMetadata.parser || "json"](),
|
|
1598
|
+
argMetadata.validationSchema,
|
|
1599
|
+
argMetadata.index,
|
|
1600
|
+
functionName
|
|
1601
|
+
);
|
|
1602
|
+
break;
|
|
1603
|
+
case requestHeadersArgsKey:
|
|
1604
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1605
|
+
? requestHeaders
|
|
1606
|
+
: await this.argumentsResolution(
|
|
1607
|
+
requestHeaders?.toJSON(),
|
|
1608
|
+
argMetadata.validationSchema,
|
|
1609
|
+
argMetadata.index,
|
|
1610
|
+
functionName
|
|
1611
|
+
);
|
|
1612
|
+
break;
|
|
1613
|
+
case requestHeaderArgsKey:
|
|
1614
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1615
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
1616
|
+
: await this.argumentsResolution(
|
|
1617
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
1618
|
+
argMetadata.validationSchema,
|
|
1619
|
+
argMetadata.index,
|
|
1620
|
+
functionName
|
|
1621
|
+
);
|
|
1622
|
+
break;
|
|
1623
|
+
case paramArgsKey:
|
|
1624
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1625
|
+
? parameters?.[argMetadata.key] || undefined
|
|
1626
|
+
: await this.argumentsResolution(
|
|
1627
|
+
parameters?.[argMetadata.key] || undefined,
|
|
1628
|
+
argMetadata.validationSchema,
|
|
1629
|
+
argMetadata.index,
|
|
1630
|
+
functionName
|
|
1631
|
+
);
|
|
1632
|
+
break;
|
|
1633
|
+
case routeModelArgsKey:
|
|
1634
|
+
args[argMetadata.index] = routeModel;
|
|
1635
|
+
break;
|
|
1636
|
+
case responseHeadersArgsKey:
|
|
1637
|
+
args[argMetadata.index] = responseHeaders;
|
|
1638
|
+
break;
|
|
1639
|
+
case httpServerArgsKey:
|
|
1640
|
+
args[argMetadata.index] = httpServer;
|
|
1641
|
+
break;
|
|
1642
|
+
default:
|
|
1643
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1644
|
+
? !context.has(argMetadata.type, contextOptions)
|
|
1645
|
+
? undefined
|
|
1646
|
+
: context.get(argMetadata.type, contextOptions)
|
|
1647
|
+
: await this.argumentsResolution(
|
|
1648
|
+
!(argMetadata.type in context)
|
|
1649
|
+
? undefined
|
|
1650
|
+
: context.get(argMetadata.type, contextOptions),
|
|
1651
|
+
argMetadata.validationSchema,
|
|
1652
|
+
argMetadata.index,
|
|
1653
|
+
functionName
|
|
1654
|
+
);
|
|
1655
|
+
break;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
await handler(...args);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Execute guard(s)
|
|
1664
|
+
if (guardGroup) {
|
|
1665
|
+
for (let i = 0; i < guardGroup.length; i++) {
|
|
1666
|
+
const args = [];
|
|
1667
|
+
const {
|
|
1668
|
+
func: handler,
|
|
1669
|
+
funcName: functionName,
|
|
1670
|
+
argumentsMetadata
|
|
1671
|
+
} = guardGroup[i];
|
|
1672
|
+
|
|
1673
|
+
for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
|
|
1674
|
+
switch (argMetadata.type) {
|
|
1675
|
+
case requestArgsKey:
|
|
1676
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1677
|
+
? request
|
|
1678
|
+
: await this.argumentsResolution(
|
|
1679
|
+
request,
|
|
1680
|
+
argMetadata.validationSchema,
|
|
1681
|
+
argMetadata.index,
|
|
1682
|
+
functionName
|
|
1683
|
+
);
|
|
1684
|
+
break;
|
|
1685
|
+
case requestBodyArgsKey:
|
|
1686
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1687
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
1688
|
+
: await this.argumentsResolution(
|
|
1689
|
+
await request?.[argMetadata.parser || "json"](),
|
|
1690
|
+
argMetadata.validationSchema,
|
|
1691
|
+
argMetadata.index,
|
|
1692
|
+
functionName
|
|
1693
|
+
);
|
|
1694
|
+
break;
|
|
1695
|
+
case contextArgsKey:
|
|
1696
|
+
args[argMetadata.index] = !argMetadata.key
|
|
1697
|
+
? context
|
|
1698
|
+
: context.get(argMetadata.key);
|
|
1699
|
+
break;
|
|
1700
|
+
case requestHeadersArgsKey:
|
|
1701
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1702
|
+
? requestHeaders
|
|
1703
|
+
: await this.argumentsResolution(
|
|
1704
|
+
requestHeaders?.toJSON(),
|
|
1705
|
+
argMetadata.validationSchema,
|
|
1706
|
+
argMetadata.index,
|
|
1707
|
+
functionName
|
|
1708
|
+
);
|
|
1709
|
+
break;
|
|
1710
|
+
case responseHeadersArgsKey:
|
|
1711
|
+
args[argMetadata.index] = responseHeaders;
|
|
1712
|
+
break;
|
|
1713
|
+
case requestHeaderArgsKey:
|
|
1714
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1715
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
1716
|
+
: await this.argumentsResolution(
|
|
1717
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
1718
|
+
argMetadata.validationSchema,
|
|
1719
|
+
argMetadata.index,
|
|
1720
|
+
functionName
|
|
1721
|
+
);
|
|
1722
|
+
break;
|
|
1723
|
+
case paramArgsKey:
|
|
1724
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1725
|
+
? parameters?.[argMetadata.key] || undefined
|
|
1726
|
+
: await this.argumentsResolution(
|
|
1727
|
+
parameters?.[argMetadata.key],
|
|
1728
|
+
argMetadata.validationSchema,
|
|
1729
|
+
argMetadata.index,
|
|
1730
|
+
functionName
|
|
1731
|
+
);
|
|
1732
|
+
break;
|
|
1733
|
+
case routeModelArgsKey:
|
|
1734
|
+
args[argMetadata.index] = routeModel;
|
|
1735
|
+
break;
|
|
1736
|
+
case httpServerArgsKey:
|
|
1737
|
+
args[argMetadata.index] = httpServer;
|
|
1738
|
+
break;
|
|
1739
|
+
default:
|
|
1740
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1741
|
+
? !context.has(argMetadata.type)
|
|
1742
|
+
? undefined
|
|
1743
|
+
: context.get(argMetadata.type)
|
|
1744
|
+
: await this.argumentsResolution(
|
|
1745
|
+
context.get(argMetadata.type),
|
|
1746
|
+
argMetadata.validationSchema,
|
|
1747
|
+
argMetadata.index,
|
|
1748
|
+
functionName
|
|
1749
|
+
);
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
const guardResult = await handler(...args);
|
|
1755
|
+
|
|
1756
|
+
if (typeof guardResult !== "boolean" || !guardResult) {
|
|
1757
|
+
throw new HttpClientError({
|
|
1758
|
+
httpCode: 401,
|
|
1759
|
+
message: "Unauthorization.",
|
|
1760
|
+
data: undefined
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
if (routeModel && !options?.isContainer) {
|
|
1768
|
+
if (
|
|
1769
|
+
resolutedMap &&
|
|
1770
|
+
"openInterceptorGroup" in resolutedMap &&
|
|
1771
|
+
resolutedMap.openInterceptorGroup
|
|
1772
|
+
) {
|
|
1773
|
+
const { openInterceptorGroup } = resolutedMap;
|
|
1774
|
+
|
|
1775
|
+
// Execute open interceptor(s)
|
|
1776
|
+
for (let i = 0; i < openInterceptorGroup.length; i++) {
|
|
1777
|
+
const args = [];
|
|
1778
|
+
const {
|
|
1779
|
+
func: handler,
|
|
1780
|
+
funcName: functionName,
|
|
1781
|
+
argumentsMetadata
|
|
1782
|
+
} = openInterceptorGroup[i];
|
|
1783
|
+
|
|
1784
|
+
for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
|
|
1785
|
+
switch (argMetadata.type) {
|
|
1786
|
+
case requestArgsKey:
|
|
1787
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1788
|
+
? request
|
|
1789
|
+
: await this.argumentsResolution(
|
|
1790
|
+
request,
|
|
1791
|
+
argMetadata.validationSchema,
|
|
1792
|
+
argMetadata.index,
|
|
1793
|
+
functionName
|
|
1794
|
+
);
|
|
1795
|
+
break;
|
|
1796
|
+
case requestBodyArgsKey:
|
|
1797
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1798
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
1799
|
+
: await this.argumentsResolution(
|
|
1800
|
+
await request?.[argMetadata.parser || "json"](),
|
|
1801
|
+
argMetadata.validationSchema,
|
|
1802
|
+
argMetadata.index,
|
|
1803
|
+
functionName
|
|
1804
|
+
);
|
|
1805
|
+
break;
|
|
1806
|
+
case contextArgsKey:
|
|
1807
|
+
args[argMetadata.index] = !argMetadata.key
|
|
1808
|
+
? context
|
|
1809
|
+
: context.get(argMetadata.key);
|
|
1810
|
+
break;
|
|
1811
|
+
case requestHeadersArgsKey:
|
|
1812
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1813
|
+
? requestHeaders
|
|
1814
|
+
: await this.argumentsResolution(
|
|
1815
|
+
requestHeaders?.toJSON(),
|
|
1816
|
+
argMetadata.validationSchema,
|
|
1817
|
+
argMetadata.index,
|
|
1818
|
+
functionName
|
|
1819
|
+
);
|
|
1820
|
+
break;
|
|
1821
|
+
case requestHeaderArgsKey:
|
|
1822
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1823
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
1824
|
+
: await this.argumentsResolution(
|
|
1825
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
1826
|
+
argMetadata.validationSchema,
|
|
1827
|
+
argMetadata.index,
|
|
1828
|
+
functionName
|
|
1829
|
+
);
|
|
1830
|
+
break;
|
|
1831
|
+
case responseHeadersArgsKey:
|
|
1832
|
+
args[argMetadata.index] = responseHeaders;
|
|
1833
|
+
break;
|
|
1834
|
+
case paramArgsKey:
|
|
1835
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1836
|
+
? parameters?.[argMetadata.key] || undefined
|
|
1837
|
+
: await this.argumentsResolution(
|
|
1838
|
+
parameters?.[argMetadata.key] || undefined,
|
|
1839
|
+
argMetadata.validationSchema,
|
|
1840
|
+
argMetadata.index,
|
|
1841
|
+
functionName
|
|
1842
|
+
);
|
|
1843
|
+
break;
|
|
1844
|
+
case routeModelArgsKey:
|
|
1845
|
+
args[argMetadata.index] = routeModel;
|
|
1846
|
+
break;
|
|
1847
|
+
case httpServerArgsKey:
|
|
1848
|
+
args[argMetadata.index] = httpServer;
|
|
1849
|
+
break;
|
|
1850
|
+
default:
|
|
1851
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1852
|
+
? !context.has(argMetadata.type)
|
|
1853
|
+
? undefined
|
|
1854
|
+
: context.get(argMetadata.type)
|
|
1855
|
+
: await this.argumentsResolution(
|
|
1856
|
+
context.get(argMetadata.type),
|
|
1857
|
+
argMetadata.validationSchema,
|
|
1858
|
+
argMetadata.index,
|
|
1859
|
+
functionName
|
|
1860
|
+
);
|
|
1861
|
+
break;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
await handler(...args);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// Execute controller action
|
|
1870
|
+
const controllerActionArguments: any[] = [];
|
|
1871
|
+
const {
|
|
1872
|
+
func: controllerAction,
|
|
1873
|
+
funcName: controllerActionName,
|
|
1874
|
+
argumentsMetadata: controllerActionArgumentsMetadata
|
|
1875
|
+
} = routeModel;
|
|
1876
|
+
|
|
1877
|
+
for (const [_key, argMetadata] of Object.entries(controllerActionArgumentsMetadata)) {
|
|
1878
|
+
switch (argMetadata.type) {
|
|
1879
|
+
case requestArgsKey:
|
|
1880
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1881
|
+
? request
|
|
1882
|
+
: await this.argumentsResolution(
|
|
1883
|
+
request,
|
|
1884
|
+
argMetadata.validationSchema,
|
|
1885
|
+
argMetadata.index,
|
|
1886
|
+
controllerActionName
|
|
1887
|
+
);
|
|
1888
|
+
break;
|
|
1889
|
+
case requestBodyArgsKey:
|
|
1890
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1891
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
1892
|
+
: await this.argumentsResolution(
|
|
1893
|
+
await request?.[argMetadata.parser || "json"](),
|
|
1894
|
+
argMetadata.validationSchema,
|
|
1895
|
+
argMetadata.index,
|
|
1896
|
+
controllerActionName
|
|
1897
|
+
);
|
|
1898
|
+
break;
|
|
1899
|
+
case contextArgsKey:
|
|
1900
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.key
|
|
1901
|
+
? context
|
|
1902
|
+
: context.get(argMetadata.key);
|
|
1903
|
+
break;
|
|
1904
|
+
case requestHeadersArgsKey:
|
|
1905
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1906
|
+
? requestHeaders
|
|
1907
|
+
: await this.argumentsResolution(
|
|
1908
|
+
requestHeaders?.toJSON(),
|
|
1909
|
+
argMetadata.validationSchema,
|
|
1910
|
+
argMetadata.index,
|
|
1911
|
+
controllerActionName
|
|
1912
|
+
);
|
|
1913
|
+
break;
|
|
1914
|
+
case requestHeaderArgsKey:
|
|
1915
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1916
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
1917
|
+
: await this.argumentsResolution(
|
|
1918
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
1919
|
+
argMetadata.validationSchema,
|
|
1920
|
+
argMetadata.index,
|
|
1921
|
+
controllerActionName
|
|
1922
|
+
);
|
|
1923
|
+
break;
|
|
1924
|
+
case responseHeadersArgsKey:
|
|
1925
|
+
controllerActionArguments[argMetadata.index] = responseHeaders;
|
|
1926
|
+
break;
|
|
1927
|
+
case paramArgsKey:
|
|
1928
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1929
|
+
? parameters?.[argMetadata.key] || undefined
|
|
1930
|
+
: await this.argumentsResolution(
|
|
1931
|
+
parameters?.[argMetadata.key] || undefined,
|
|
1932
|
+
argMetadata.validationSchema,
|
|
1933
|
+
argMetadata.index,
|
|
1934
|
+
controllerActionName
|
|
1935
|
+
);
|
|
1936
|
+
break;
|
|
1937
|
+
case routeModelArgsKey:
|
|
1938
|
+
controllerActionArguments[argMetadata.index] = routeModel;
|
|
1939
|
+
break;
|
|
1940
|
+
case httpServerArgsKey:
|
|
1941
|
+
controllerActionArguments[argMetadata.index] = httpServer;
|
|
1942
|
+
break;
|
|
1943
|
+
default:
|
|
1944
|
+
controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
|
|
1945
|
+
? !context.has(argMetadata.type)
|
|
1946
|
+
? undefined
|
|
1947
|
+
: context.get(argMetadata.type)
|
|
1948
|
+
: await this.argumentsResolution(
|
|
1949
|
+
context.get(argMetadata.type),
|
|
1950
|
+
argMetadata.validationSchema,
|
|
1951
|
+
argMetadata.index,
|
|
1952
|
+
controllerActionName
|
|
1953
|
+
);
|
|
1954
|
+
break;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
context.set(responseBodyArgsKey, await controllerAction(...controllerActionArguments), {
|
|
1959
|
+
isStatic: false
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
if (
|
|
1963
|
+
resolutedMap &&
|
|
1964
|
+
"closeInterceptorGroup" in resolutedMap &&
|
|
1965
|
+
resolutedMap.closeInterceptorGroup
|
|
1966
|
+
) {
|
|
1967
|
+
const { closeInterceptorGroup } = resolutedMap;
|
|
1968
|
+
|
|
1969
|
+
// Execute close interceptor(s)
|
|
1970
|
+
for (let i = 0; i < closeInterceptorGroup.length; i++) {
|
|
1971
|
+
const args = [];
|
|
1972
|
+
const {
|
|
1973
|
+
func: handler,
|
|
1974
|
+
funcName: functionName,
|
|
1975
|
+
argumentsMetadata
|
|
1976
|
+
} = closeInterceptorGroup[i];
|
|
1977
|
+
|
|
1978
|
+
for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
|
|
1979
|
+
switch (argMetadata.type) {
|
|
1980
|
+
case requestArgsKey:
|
|
1981
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1982
|
+
? request
|
|
1983
|
+
: await this.argumentsResolution(
|
|
1984
|
+
request,
|
|
1985
|
+
argMetadata.validationSchema,
|
|
1986
|
+
argMetadata.index,
|
|
1987
|
+
functionName
|
|
1988
|
+
);
|
|
1989
|
+
break;
|
|
1990
|
+
case requestBodyArgsKey:
|
|
1991
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
1992
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
1993
|
+
: await this.argumentsResolution(
|
|
1994
|
+
await request?.[argMetadata.parser || "json"](),
|
|
1995
|
+
argMetadata.validationSchema,
|
|
1996
|
+
argMetadata.index,
|
|
1997
|
+
functionName
|
|
1998
|
+
);
|
|
1999
|
+
break;
|
|
2000
|
+
case contextArgsKey:
|
|
2001
|
+
args[argMetadata.index] = !argMetadata.key
|
|
2002
|
+
? context
|
|
2003
|
+
: context.get(argMetadata.key);
|
|
2004
|
+
break;
|
|
2005
|
+
case requestHeadersArgsKey:
|
|
2006
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2007
|
+
? requestHeaders
|
|
2008
|
+
: await this.argumentsResolution(
|
|
2009
|
+
requestHeaders?.toJSON(),
|
|
2010
|
+
argMetadata.validationSchema,
|
|
2011
|
+
argMetadata.index,
|
|
2012
|
+
functionName
|
|
2013
|
+
);
|
|
2014
|
+
break;
|
|
2015
|
+
case responseHeadersArgsKey:
|
|
2016
|
+
args[argMetadata.index] = context.get(argMetadata.type);
|
|
2017
|
+
break;
|
|
2018
|
+
case requestHeaderArgsKey:
|
|
2019
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2020
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
2021
|
+
: await this.argumentsResolution(
|
|
2022
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
2023
|
+
argMetadata.validationSchema,
|
|
2024
|
+
argMetadata.index,
|
|
2025
|
+
functionName
|
|
2026
|
+
);
|
|
2027
|
+
break;
|
|
2028
|
+
case paramArgsKey:
|
|
2029
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2030
|
+
? parameters?.[argMetadata.key] || undefined
|
|
2031
|
+
: await this.argumentsResolution(
|
|
2032
|
+
parameters?.[argMetadata.key] || undefined,
|
|
2033
|
+
argMetadata.validationSchema,
|
|
2034
|
+
argMetadata.index,
|
|
2035
|
+
functionName
|
|
2036
|
+
);
|
|
2037
|
+
break;
|
|
2038
|
+
case routeModelArgsKey:
|
|
2039
|
+
args[argMetadata.index] = routeModel;
|
|
2040
|
+
break;
|
|
2041
|
+
case httpServerArgsKey:
|
|
2042
|
+
args[argMetadata.index] = httpServer;
|
|
2043
|
+
break;
|
|
2044
|
+
default:
|
|
2045
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2046
|
+
? !context.has(argMetadata.type)
|
|
2047
|
+
? undefined
|
|
2048
|
+
: context.get(argMetadata.type)
|
|
2049
|
+
: await this.argumentsResolution(
|
|
2050
|
+
context.get(argMetadata.type),
|
|
2051
|
+
argMetadata.validationSchema,
|
|
2052
|
+
argMetadata.index,
|
|
2053
|
+
functionName
|
|
2054
|
+
);
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
await handler(...args);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
if (resolutedMap) {
|
|
2065
|
+
const { endMiddlewareGroup } = resolutedMap;
|
|
2066
|
+
|
|
2067
|
+
// Execute end middleware(s)
|
|
2068
|
+
if (endMiddlewareGroup) {
|
|
2069
|
+
for (let i = 0; i < endMiddlewareGroup.length; i++) {
|
|
2070
|
+
const args = [];
|
|
2071
|
+
const {
|
|
2072
|
+
func: handler,
|
|
2073
|
+
funcName: functionName,
|
|
2074
|
+
argumentsMetadata
|
|
2075
|
+
} = endMiddlewareGroup[i];
|
|
2076
|
+
|
|
2077
|
+
for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
|
|
2078
|
+
switch (argMetadata.type) {
|
|
2079
|
+
case requestArgsKey:
|
|
2080
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2081
|
+
? request
|
|
2082
|
+
: await this.argumentsResolution(
|
|
2083
|
+
request,
|
|
2084
|
+
argMetadata.validationSchema,
|
|
2085
|
+
argMetadata.index,
|
|
2086
|
+
functionName
|
|
2087
|
+
);
|
|
2088
|
+
break;
|
|
2089
|
+
case requestBodyArgsKey:
|
|
2090
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2091
|
+
? await request?.[argMetadata.parser || "json"]()
|
|
2092
|
+
: await this.argumentsResolution(
|
|
2093
|
+
await request?.[argMetadata.parser || "json"](),
|
|
2094
|
+
argMetadata.validationSchema,
|
|
2095
|
+
argMetadata.index,
|
|
2096
|
+
functionName
|
|
2097
|
+
);
|
|
2098
|
+
break;
|
|
2099
|
+
case contextArgsKey:
|
|
2100
|
+
args[argMetadata.index] = !argMetadata.key
|
|
2101
|
+
? context
|
|
2102
|
+
: context.get(argMetadata.key);
|
|
2103
|
+
break;
|
|
2104
|
+
case requestHeadersArgsKey:
|
|
2105
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2106
|
+
? requestHeaders
|
|
2107
|
+
: await this.argumentsResolution(
|
|
2108
|
+
requestHeaders?.toJSON(),
|
|
2109
|
+
argMetadata.validationSchema,
|
|
2110
|
+
argMetadata.index,
|
|
2111
|
+
functionName
|
|
2112
|
+
);
|
|
2113
|
+
break;
|
|
2114
|
+
case responseHeadersArgsKey:
|
|
2115
|
+
args[argMetadata.index] = context.get(argMetadata.type);
|
|
2116
|
+
break;
|
|
2117
|
+
case requestHeaderArgsKey:
|
|
2118
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2119
|
+
? requestHeaders?.get(argMetadata.key) || undefined
|
|
2120
|
+
: await this.argumentsResolution(
|
|
2121
|
+
requestHeaders?.get(argMetadata.key) || undefined,
|
|
2122
|
+
argMetadata.validationSchema,
|
|
2123
|
+
argMetadata.index,
|
|
2124
|
+
functionName
|
|
2125
|
+
);
|
|
2126
|
+
break;
|
|
2127
|
+
case paramArgsKey:
|
|
2128
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2129
|
+
? parameters?.[argMetadata.key] || undefined
|
|
2130
|
+
: await this.argumentsResolution(
|
|
2131
|
+
parameters?.[argMetadata.key] || undefined,
|
|
2132
|
+
argMetadata.validationSchema,
|
|
2133
|
+
argMetadata.index,
|
|
2134
|
+
functionName
|
|
2135
|
+
);
|
|
2136
|
+
break;
|
|
2137
|
+
case routeModelArgsKey:
|
|
2138
|
+
args[argMetadata.index] = routeModel;
|
|
2139
|
+
break;
|
|
2140
|
+
case httpServerArgsKey:
|
|
2141
|
+
args[argMetadata.index] = httpServer;
|
|
2142
|
+
break;
|
|
2143
|
+
default:
|
|
2144
|
+
args[argMetadata.index] = !argMetadata.validationSchema
|
|
2145
|
+
? !context.has(argMetadata.type)
|
|
2146
|
+
? undefined
|
|
2147
|
+
: context.get(argMetadata.type)
|
|
2148
|
+
: await this.argumentsResolution(
|
|
2149
|
+
!(argMetadata.type in context)
|
|
2150
|
+
? undefined
|
|
2151
|
+
: context.get(argMetadata.type),
|
|
2152
|
+
argMetadata.validationSchema,
|
|
2153
|
+
argMetadata.index,
|
|
2154
|
+
functionName
|
|
2155
|
+
);
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
await handler(...args);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
return Object.freeze({
|
|
2166
|
+
context: context
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
*
|
|
2172
|
+
* @param bun
|
|
2173
|
+
* @param bool
|
|
2174
|
+
* @returns
|
|
2175
|
+
*/
|
|
2176
|
+
private async webSocketFetcher(
|
|
2177
|
+
bun: Required<{
|
|
2178
|
+
request: Request;
|
|
2179
|
+
server: Server;
|
|
2180
|
+
}>,
|
|
2181
|
+
bool: Required<{
|
|
2182
|
+
responseHeaders: Headers;
|
|
2183
|
+
query: Record<string, unknown>;
|
|
2184
|
+
route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
|
|
2185
|
+
moduleResolution: NonNullable<
|
|
2186
|
+
Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>
|
|
2187
|
+
>;
|
|
2188
|
+
}>
|
|
2189
|
+
) {
|
|
2190
|
+
const { request, server } = bun;
|
|
2191
|
+
const {
|
|
2192
|
+
query,
|
|
2193
|
+
responseHeaders,
|
|
2194
|
+
route: { model }
|
|
2195
|
+
} = bool;
|
|
2196
|
+
|
|
2197
|
+
// Execute controller action
|
|
2198
|
+
const isUpgrade = await model.func(...[server, request, query]);
|
|
2199
|
+
|
|
2200
|
+
if (typeof isUpgrade !== "boolean") {
|
|
2201
|
+
return this.finalizeResponse(
|
|
2202
|
+
new Response(
|
|
2203
|
+
JSON.stringify({
|
|
2204
|
+
httpCode: 500,
|
|
2205
|
+
message: "Can not detect webSocket upgrade result.",
|
|
2206
|
+
data: undefined
|
|
2207
|
+
}),
|
|
2208
|
+
{
|
|
2209
|
+
status: 500,
|
|
2210
|
+
statusText: "Internal server error.",
|
|
2211
|
+
headers: responseHeaders
|
|
2212
|
+
}
|
|
2213
|
+
)
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
if (!isUpgrade) {
|
|
2218
|
+
return this.finalizeResponse(
|
|
2219
|
+
new Response(
|
|
2220
|
+
JSON.stringify({
|
|
2221
|
+
httpCode: 500,
|
|
2222
|
+
message: "Can not upgrade.",
|
|
2223
|
+
data: undefined
|
|
2224
|
+
}),
|
|
2225
|
+
{
|
|
2226
|
+
status: 500,
|
|
2227
|
+
statusText: "Internal server error.",
|
|
2228
|
+
headers: responseHeaders
|
|
2229
|
+
}
|
|
2230
|
+
)
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
return isUpgrade;
|
|
2235
|
+
}
|
|
2236
|
+
}
|