proteum 1.0.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.dockerignore +10 -0
  2. package/Rte.zip +0 -0
  3. package/cli/app/config.ts +54 -0
  4. package/cli/app/index.ts +195 -0
  5. package/cli/bin.js +11 -0
  6. package/cli/commands/build.ts +34 -0
  7. package/cli/commands/deploy/app.ts +29 -0
  8. package/cli/commands/deploy/web.ts +60 -0
  9. package/cli/commands/dev.ts +109 -0
  10. package/cli/commands/init.ts +85 -0
  11. package/cli/compiler/client/identite.ts +72 -0
  12. package/cli/compiler/client/index.ts +334 -0
  13. package/cli/compiler/common/babel/index.ts +170 -0
  14. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  15. package/cli/compiler/common/babel/plugins/services.ts +579 -0
  16. package/cli/compiler/common/babel/routes/imports.ts +127 -0
  17. package/cli/compiler/common/babel/routes/routes.ts +1130 -0
  18. package/cli/compiler/common/files/autres.ts +39 -0
  19. package/cli/compiler/common/files/images.ts +35 -0
  20. package/cli/compiler/common/files/style.ts +78 -0
  21. package/cli/compiler/common/index.ts +154 -0
  22. package/cli/compiler/index.ts +532 -0
  23. package/cli/compiler/server/index.ts +211 -0
  24. package/cli/index.ts +189 -0
  25. package/cli/paths.ts +165 -0
  26. package/cli/print.ts +12 -0
  27. package/cli/tsconfig.json +38 -0
  28. package/cli/utils/index.ts +22 -0
  29. package/cli/utils/keyboard.ts +78 -0
  30. package/client/app/component.tsx +54 -0
  31. package/client/app/index.ts +142 -0
  32. package/client/app/service.ts +34 -0
  33. package/client/app.tsconfig.json +28 -0
  34. package/client/components/Button.tsx +298 -0
  35. package/client/components/Dialog/Manager.tsx +309 -0
  36. package/client/components/Dialog/card.tsx +208 -0
  37. package/client/components/Dialog/index.less +151 -0
  38. package/client/components/Dialog/status.less +176 -0
  39. package/client/components/Dialog/status.tsx +48 -0
  40. package/client/components/index.ts +2 -0
  41. package/client/components/types.d.ts +3 -0
  42. package/client/data/input.ts +32 -0
  43. package/client/global.d.ts +5 -0
  44. package/client/hooks.ts +22 -0
  45. package/client/index.ts +6 -0
  46. package/client/pages/_layout/index.less +6 -0
  47. package/client/pages/_layout/index.tsx +43 -0
  48. package/client/pages/bug.tsx.old +60 -0
  49. package/client/pages/useHeader.tsx +50 -0
  50. package/client/services/captcha/index.ts +67 -0
  51. package/client/services/router/components/Link.tsx +46 -0
  52. package/client/services/router/components/Page.tsx +55 -0
  53. package/client/services/router/components/router.tsx +218 -0
  54. package/client/services/router/index.tsx +521 -0
  55. package/client/services/router/request/api.ts +267 -0
  56. package/client/services/router/request/history.ts +5 -0
  57. package/client/services/router/request/index.ts +53 -0
  58. package/client/services/router/request/multipart.ts +147 -0
  59. package/client/services/router/response/index.tsx +128 -0
  60. package/client/services/router/response/page.ts +86 -0
  61. package/client/services/socket/index.ts +147 -0
  62. package/client/utils/dom.ts +77 -0
  63. package/common/app/index.ts +9 -0
  64. package/common/data/chaines/index.ts +54 -0
  65. package/common/data/dates.ts +179 -0
  66. package/common/data/markdown.ts +73 -0
  67. package/common/data/rte/nodes.ts +83 -0
  68. package/common/data/stats.ts +90 -0
  69. package/common/errors/index.tsx +326 -0
  70. package/common/router/index.ts +213 -0
  71. package/common/router/layouts.ts +93 -0
  72. package/common/router/register.ts +55 -0
  73. package/common/router/request/api.ts +77 -0
  74. package/common/router/request/index.ts +35 -0
  75. package/common/router/response/index.ts +45 -0
  76. package/common/router/response/page.ts +128 -0
  77. package/common/utils/rte.ts +183 -0
  78. package/common/utils.ts +7 -0
  79. package/doc/TODO.md +71 -0
  80. package/doc/front/router.md +27 -0
  81. package/doc/workspace/workspace.png +0 -0
  82. package/doc/workspace/workspace2.png +0 -0
  83. package/doc/workspace/workspace_26.01.22.png +0 -0
  84. package/package.json +171 -0
  85. package/server/app/commands.ts +141 -0
  86. package/server/app/container/config.ts +203 -0
  87. package/server/app/container/console/index.ts +550 -0
  88. package/server/app/container/index.ts +137 -0
  89. package/server/app/index.ts +273 -0
  90. package/server/app/service/container.ts +88 -0
  91. package/server/app/service/index.ts +235 -0
  92. package/server/app.tsconfig.json +28 -0
  93. package/server/context.ts +4 -0
  94. package/server/index.ts +4 -0
  95. package/server/services/auth/index.ts +250 -0
  96. package/server/services/auth/old.ts +277 -0
  97. package/server/services/auth/router/index.ts +95 -0
  98. package/server/services/auth/router/request.ts +54 -0
  99. package/server/services/auth/router/service.json +6 -0
  100. package/server/services/auth/service.json +6 -0
  101. package/server/services/cache/commands.ts +41 -0
  102. package/server/services/cache/index.ts +297 -0
  103. package/server/services/cache/service.json +6 -0
  104. package/server/services/cron/CronTask.ts +86 -0
  105. package/server/services/cron/index.ts +112 -0
  106. package/server/services/cron/service.json +6 -0
  107. package/server/services/disks/driver.ts +103 -0
  108. package/server/services/disks/drivers/local/index.ts +188 -0
  109. package/server/services/disks/drivers/local/service.json +6 -0
  110. package/server/services/disks/drivers/s3/index.ts +301 -0
  111. package/server/services/disks/drivers/s3/service.json +6 -0
  112. package/server/services/disks/index.ts +90 -0
  113. package/server/services/disks/service.json +6 -0
  114. package/server/services/email/index.ts +188 -0
  115. package/server/services/email/utils.ts +53 -0
  116. package/server/services/fetch/index.ts +201 -0
  117. package/server/services/fetch/service.json +7 -0
  118. package/server/services/models.7z +0 -0
  119. package/server/services/prisma/Facet.ts +142 -0
  120. package/server/services/prisma/index.ts +201 -0
  121. package/server/services/prisma/service.json +6 -0
  122. package/server/services/router/http/index.ts +217 -0
  123. package/server/services/router/http/multipart.ts +102 -0
  124. package/server/services/router/http/session.ts.old +40 -0
  125. package/server/services/router/index.ts +801 -0
  126. package/server/services/router/request/api.ts +87 -0
  127. package/server/services/router/request/index.ts +184 -0
  128. package/server/services/router/request/service.ts +21 -0
  129. package/server/services/router/request/validation/zod.ts +180 -0
  130. package/server/services/router/response/index.ts +338 -0
  131. package/server/services/router/response/mask/Filter.ts +323 -0
  132. package/server/services/router/response/mask/index.ts +60 -0
  133. package/server/services/router/response/mask/selecteurs.ts +92 -0
  134. package/server/services/router/response/page/document.tsx +160 -0
  135. package/server/services/router/response/page/index.tsx +196 -0
  136. package/server/services/router/service.json +6 -0
  137. package/server/services/router/service.ts +36 -0
  138. package/server/services/schema/index.ts +44 -0
  139. package/server/services/schema/request.ts +49 -0
  140. package/server/services/schema/router/index.ts +28 -0
  141. package/server/services/schema/router/service.json +6 -0
  142. package/server/services/schema/service.json +6 -0
  143. package/server/services/security/encrypt/aes/index.ts +85 -0
  144. package/server/services/security/encrypt/aes/service.json +6 -0
  145. package/server/services/socket/index.ts +162 -0
  146. package/server/services/socket/scope.ts +226 -0
  147. package/server/services/socket/service.json +6 -0
  148. package/server/services_old/SocketClient.ts +92 -0
  149. package/server/services_old/Token.old.ts +97 -0
  150. package/server/utils/slug.ts +79 -0
  151. package/tsconfig.common.json +45 -0
  152. package/tsconfig.json +3 -0
  153. package/types/aliases.d.ts +54 -0
  154. package/types/global/modules.d.ts +49 -0
  155. package/types/global/utils.d.ts +103 -0
  156. package/types/icons.d.ts +1 -0
@@ -0,0 +1,521 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import React from 'react';
7
+ import ReactDOM from 'react-dom';
8
+
9
+ // Core
10
+ import type {
11
+ default as ServerRouter,
12
+ Request as ServerRequest,
13
+ Response as ServerResponse
14
+ } from '@server/services/router';
15
+ import type { TBasicSSrData } from '@server/services/router/response';
16
+
17
+ import BaseRouter, {
18
+ defaultOptions, TRoute, TErrorRoute,
19
+ TClientOrServerContextForPage, TRouteModule,
20
+ matchRoute, buildUrl
21
+ } from '@common/router'
22
+ import { getLayout } from '@common/router/layouts';
23
+ import { getRegisterPageArgs, buildRegex } from '@common/router/register';
24
+ import { TFetcherList } from '@common/router/request/api';
25
+ import type { TFrontRenderer } from '@common/router/response/page';
26
+ import Button from '../../components/Button';
27
+
28
+ import App from '@client/app/component';
29
+ import type ClientApplication from '@client/app';
30
+ import Service from '@client/app/service';
31
+
32
+ // Specific
33
+ import type { ClientContext } from '@/client/context';
34
+ import ClientRequest from './request';
35
+ import { location, history } from './request/history';
36
+ import ClientResponse from './response';
37
+ import ClientPage from './response/page';
38
+
39
+ // Routes (import __register)
40
+ // We exclude files starting wiht uppercase, as they're considered as components
41
+ // WARN: The routes babel plugin must be updated with the glob path
42
+ //import * as coreRoutes from '@client/pages/**/([a-z0-9]*).tsx';
43
+ import * as appRoutes from '@/client/pages/**/([a-z0-9]*).tsx';
44
+
45
+ /*----------------------------------
46
+ - CONFIG
47
+ ----------------------------------*/
48
+
49
+ const debug = false;
50
+ const LogPrefix = '[router]'
51
+
52
+ /*----------------------------------
53
+ - TYPES
54
+ ----------------------------------*/
55
+
56
+ // Client router can handle Client requests AND Server requests (for pages only)
57
+ export type { default as ClientResponse, TRouterContext } from "./response";
58
+
59
+ export type Router = ClientRouter | ServerRouter;
60
+
61
+ export type Request = ClientRequest<ClientRouter> | ServerRequest<ServerRouter>;
62
+
63
+ export type Response = ClientResponse<ClientRouter> | ServerResponse<ServerRouter>;
64
+
65
+ /*----------------------------------
66
+ - TYPES: ROUTES LOADING
67
+ ----------------------------------*/
68
+
69
+ // WARN: To be updated with the mplemenations list of Router.page AND the routes babel plugin
70
+ // (both server and client side)
71
+ export type TRegisterPageArgs<
72
+ TProvidedData extends TFetcherList = TFetcherList,
73
+ TRouter extends Router = Router
74
+ > = ([
75
+ path: string,
76
+ renderer: TFrontRenderer<TProvidedData>
77
+ ] | [
78
+ path: string,
79
+ options: Partial<TRoute["options"]>,
80
+ renderer: TFrontRenderer<TProvidedData>
81
+ ])
82
+
83
+ // Route definition passed by the server
84
+ export type TSsrUnresolvedRoute = {
85
+ chunk: string,
86
+ } & ({
87
+ // Normal route
88
+ regex: string,
89
+ keys: TRoute["keys"]
90
+ } | {
91
+ // Error
92
+ code: number
93
+ })
94
+
95
+ // Route definition without having loaded the controller
96
+ type TUnresolvedRoute = TUnresolvedErrorRoute | TUnresolvedNormalRoute;
97
+
98
+ export type TUnresolvedErrorRoute = {
99
+ index: number,
100
+ chunk: string,
101
+ code: number,
102
+ load: TRouteLoader<TErrorRoute>,
103
+ }
104
+
105
+ export type TUnresolvedNormalRoute = {
106
+ index: number,
107
+ chunk: string,
108
+ code: number,
109
+ load: TRouteLoader<TErrorRoute>,
110
+ }
111
+
112
+ type TRouteLoader<Route extends TRoute | TErrorRoute = TRoute | TErrorRoute> = () => Promise<TRouteModule<Route>>;
113
+
114
+ export type TFetchedRoute = Pick<TRoute, 'path' | 'options' | 'controller' | 'method'>
115
+
116
+ export type TRoutesLoaders = {
117
+ [chunkId: string]: () => Promise</* Preloaded via require() */TFetchedRoute | /* Loader via import() */TRouteLoader/* | undefined*/>
118
+ }
119
+
120
+ /*----------------------------------
121
+ - SERVICE TYPES
122
+ ----------------------------------*/
123
+
124
+ export type THookCallback<TRouter extends ClientRouter> = (request: ClientRequest<TRouter>) => void;
125
+
126
+ type THookName = 'page.change' | 'page.changed' | 'page.rendered'
127
+
128
+ type Config<TAdditionnalContext extends {} = {}> = {
129
+ preload: string[], // List of globs
130
+ context: (context: {}, router: ClientRouter) => TAdditionnalContext,
131
+ }
132
+
133
+ /*----------------------------------
134
+ - ROUTER
135
+ ----------------------------------*/
136
+ export default class ClientRouter<
137
+ TApplication extends ClientApplication = ClientApplication,
138
+ TAdditionnalContext extends {} = {}
139
+ > extends Service<Config<TAdditionnalContext>, ClientApplication> implements BaseRouter {
140
+
141
+ // Context data
142
+ public ssrRoutes = window["routes"] as TSsrUnresolvedRoute[];
143
+ public ssrContext = window["ssr"] as (TBasicSSrData | undefined);
144
+ public domains = window["ssr"].domains;
145
+ public context!: ClientContext;
146
+
147
+ public setLoading!: React.Dispatch< React.SetStateAction<boolean> >;
148
+ public navigate!: (page: ClientPage, data?: {}) => void;
149
+
150
+ public constructor(app: TApplication, config: Config<TAdditionnalContext>) {
151
+
152
+ super(app, config);
153
+ }
154
+
155
+ public async start() {
156
+
157
+ const currentRoute = await this.registerRoutes();
158
+
159
+ this.initialRender(currentRoute);
160
+ }
161
+
162
+ public url = (path: string, params: {} = {}, absolute: boolean = true) =>
163
+ buildUrl(path, params, this.domains, absolute);
164
+
165
+ public go( url: string | number, data: {} = {}, opt: {
166
+ newTab?: boolean
167
+ } = {}) {
168
+
169
+ // Error code
170
+ if (typeof url === 'number') {
171
+ this.createResponse( this.errors[url], this.context.request, data ).then(( page ) => {
172
+ this.navigate(page, data);
173
+ })
174
+ return;
175
+ }
176
+
177
+ url = this.url(url, data, false);
178
+
179
+ if (opt.newTab)
180
+ window.open(url)
181
+ // Same domain = history url replacement
182
+ else if (url[0] === '/')
183
+ history?.replace( url );
184
+ // Different domain = hard navigation
185
+ else
186
+ window.location.href = url;
187
+ }
188
+
189
+ /*----------------------------------
190
+ - REGISTRATION
191
+ ----------------------------------*/
192
+
193
+ public routes: (TRoute | TUnresolvedNormalRoute)[] = [];
194
+ public errors: { [code: number]: TErrorRoute | TUnresolvedErrorRoute } = {};
195
+
196
+ public async registerRoutes() {
197
+
198
+ const loaders: TRoutesLoaders = { ...appRoutes }
199
+ let currentRoute: TUnresolvedRoute | undefined;
200
+ debug && console.log(LogPrefix, `Indexing routes and finding the current route from ssr data:`, this.context);
201
+
202
+ // Associe la liste des routes (obtenue via ssr) à leur loader
203
+ for (let routeIndex = 0; routeIndex < this.ssrRoutes.length; routeIndex++) {
204
+
205
+ const ssrRoute = this.ssrRoutes[routeIndex];
206
+
207
+ if (loaders[ssrRoute.chunk] === undefined) {
208
+ console.error("Chunk id not found for ssr route:", ssrRoute, "Searched in:", loaders);
209
+ continue;
210
+ }
211
+
212
+ // TODO: Fix types
213
+ const loader = loaders[ssrRoute.chunk];
214
+
215
+ // Register the route
216
+ let route: TUnresolvedRoute;
217
+ if ('code' in ssrRoute)
218
+ route = this.errors[ssrRoute.code] = {
219
+ code: ssrRoute.code,
220
+ chunk: ssrRoute.chunk,
221
+ load: loader,
222
+ }
223
+ else
224
+ route = this.routes[routeIndex] = {
225
+ index: routeIndex,
226
+ chunk: ssrRoute.chunk,
227
+ regex: new RegExp(ssrRoute.regex),
228
+ keys: ssrRoute.keys,
229
+ load: loader,
230
+ }
231
+
232
+ debug && console.log(LogPrefix, `${route.chunk}`, route);
233
+
234
+ // Detect if it's the current route
235
+ if (currentRoute === undefined) {
236
+
237
+ const isCurrentRoute = (
238
+ this.ssrContext !== undefined
239
+ &&
240
+ route.chunk === this.ssrContext.page.chunkId
241
+ );
242
+
243
+ if (isCurrentRoute) {
244
+ currentRoute = route;
245
+ continue;
246
+ }
247
+ }
248
+ }
249
+
250
+ return currentRoute;
251
+ }
252
+
253
+ public page<TProvidedData extends TFetcherList = TFetcherList>(
254
+ path: string,
255
+ renderer: TFrontRenderer<TProvidedData>
256
+ ): TRoute;
257
+
258
+ public page<TProvidedData extends TFetcherList = TFetcherList>(
259
+ path: string,
260
+ options: Partial<TRoute["options"]>,
261
+ renderer: TFrontRenderer<TProvidedData>
262
+ ): TRoute;
263
+
264
+ public page(...args: TRegisterPageArgs): TRoute {
265
+
266
+ const { path, options, renderer, layout } = getRegisterPageArgs(...args);
267
+
268
+ // S'il s'agit d'une page, son id doit avoir été injecté via le plugin babel
269
+ const id = options["id"];
270
+ if (id === undefined)
271
+ throw new Error(`ID had not been injected into page options via the routes babel plugin for route ${path}.`);
272
+
273
+ const { regex, keys } = buildRegex(path);
274
+
275
+ const route: TRoute = {
276
+ method: 'GET',
277
+ path,
278
+ regex,
279
+ keys,
280
+ options: {
281
+ ...defaultOptions,
282
+ ...options
283
+ },
284
+ controller: (context: TClientOrServerContextForPage) => new ClientPage(route, renderer, context, layout)
285
+ };
286
+
287
+ this.routes.push(route);
288
+
289
+ return route;
290
+ }
291
+
292
+ public error(
293
+ code: number,
294
+ options: Partial<TRoute["options"]>,
295
+ renderer: TFrontRenderer<{}, { message: string }>
296
+ ) {
297
+
298
+ // Automatic layout form the nearest _layout folder
299
+ const layout = getLayout('Error ' + code, options);
300
+
301
+ const route: TErrorRoute = {
302
+ code,
303
+ controller: (context: TClientOrServerContextForPage) => new ClientPage(route, renderer, context, layout),
304
+ options
305
+ };
306
+
307
+ this.errors[code] = route;
308
+
309
+ return route;
310
+ }
311
+
312
+
313
+ /*----------------------------------
314
+ - RESOLUTION
315
+ ----------------------------------*/
316
+ public async resolve(request: ClientRequest<this>): Promise<ClientPage> {
317
+
318
+ debug && console.log(LogPrefix, 'Resolving request', request.path, Object.keys(request.data));
319
+
320
+ for (let iRoute = 0; iRoute < this.routes.length; iRoute++) {
321
+
322
+ let route = this.routes[iRoute];
323
+ if (!('regex' in route))
324
+ continue;
325
+
326
+ const isMatching = matchRoute(route, request);
327
+ if (!isMatching)
328
+ continue;
329
+
330
+ // Create response
331
+ debug && console.log(LogPrefix, 'Resolved request', request.path, '| Route:', route);
332
+ const page = await this.createResponse(route, request);
333
+
334
+ return page;
335
+
336
+ };
337
+
338
+ const notFoundRoute = this.errors[404];
339
+ return await this.createResponse(notFoundRoute, request, {
340
+ error: new Error("Page not found")
341
+ });
342
+ }
343
+
344
+ private async load(route: TUnresolvedNormalRoute): Promise<TRoute>;
345
+ private async load(route: TUnresolvedErrorRoute): Promise<TErrorRoute>;
346
+ private async load(route: TUnresolvedNormalRoute | TUnresolvedErrorRoute): Promise<TRoute | TErrorRoute> {
347
+
348
+ //throw new Error(`Failed to load route: ${route.chunk}`);
349
+
350
+ let fetched: TFetchedRoute;
351
+ if (typeof route.load === 'function') {
352
+
353
+ debug && console.log(`Fetching route ${route.chunk} ...`, route);
354
+ try {
355
+
356
+ const loaded = await route.load();
357
+
358
+ fetched = loaded.__register(this.app);
359
+
360
+ } catch (e) {
361
+ console.error(`Failed to fetch the route ${route.chunk}`, e);
362
+ try {
363
+ this.context.modal.show(() => (
364
+ <div class="card col bg white w-3">
365
+ <h2>New Update Available!</h2>
366
+ <p>
367
+ A new version of the website is available. Please refresh the page to continue.
368
+ </p>
369
+ <Button type="primary" onClick={() => window.location.reload()}>
370
+ Reload
371
+ </Button>
372
+ </div>
373
+ ));
374
+ } catch (error) {}
375
+ throw new Error("A new version of the website is available. Please refresh the page.");
376
+ }
377
+
378
+ } else {
379
+
380
+ debug && console.log(`Route already fetched: ${route.chunk}`, route.load);
381
+ fetched = route.load;
382
+
383
+ }
384
+
385
+ debug && console.log(`Route fetched: ${route.chunk}`, fetched);
386
+ return {
387
+ ...fetched,
388
+ regex: route.regex,
389
+ keys: route.keys
390
+ }
391
+ }
392
+
393
+ public set(data: TObjetDonnees) {
394
+ throw new Error(`router.set was not attached to the router component.`);
395
+ }
396
+
397
+ private async initialRender(route: TUnresolvedRoute | undefined) {
398
+
399
+ debug && console.log(LogPrefix, `Initial render route`, route);
400
+
401
+ if (!location)
402
+ throw new Error(`Unable to retrieve current location.`);
403
+
404
+ if (!route)
405
+ throw new Error(`Unable to resolve route.`);
406
+
407
+ const request = new ClientRequest(location, this);
408
+
409
+ // Restituate SSR response
410
+ let apiData: {} = {}
411
+ if (this.ssrContext) {
412
+
413
+ request.user = this.ssrContext.user || null;
414
+
415
+ request.data = this.ssrContext.request.data;
416
+
417
+ apiData = this.ssrContext.page.data || {};
418
+ }
419
+
420
+ // Replacer api data par ssr data
421
+
422
+ const response = await this.createResponse(route, request, apiData);
423
+
424
+ ReactDOM.hydrate((
425
+ <App context={response.context} />
426
+ ), document.body, () => {
427
+
428
+ console.log(`Render complete`);
429
+
430
+ this.runHook('page.rendered', request);
431
+ });
432
+ }
433
+
434
+ private async createResponse(
435
+ route: TUnresolvedRoute | TErrorRoute | TRoute,
436
+ request: ClientRequest<this>,
437
+ pageData: {} = {}
438
+ ): Promise<ClientPage> {
439
+
440
+ // Load the route if not done before
441
+ if ('load' in route)
442
+ route = this.routes[route.index] = await this.load(route);
443
+
444
+ // Run controller
445
+ // TODO: tell that ruController on the client side always returns pages
446
+ try {
447
+
448
+ const response = new ClientResponse<this, ClientPage>(request, route);
449
+ return await response.runController(pageData);
450
+
451
+ } catch (error) {
452
+
453
+ return await this.createErrorResponse(error, request);
454
+ }
455
+ }
456
+
457
+ private async createErrorResponse(
458
+ e: any,
459
+ request: ClientRequest<this>,
460
+ pageData: {} = {}
461
+ ): Promise<ClientPage> {
462
+
463
+ const code = 'http' in e ? e.http : 500;
464
+ console.log(`Loading error page ` + code);
465
+ let route = this.errors[code];
466
+
467
+ // Nor page configurated for this error
468
+ if (route === undefined) {
469
+ console.error(`Error page for http error code ${code} not found.`, this.errors, this.routes);
470
+ e.http = 404;
471
+ this.app.handleError(e);
472
+ throw new Error(`Error page for http error code ${code} not found.`);
473
+ }
474
+
475
+ // Load if not done before
476
+ if ('load' in route)
477
+ route = this.errors[code] = await this.load(route);
478
+
479
+ const response = new ClientResponse<this, ClientPage>(request, route);
480
+ return await response.runController(pageData);
481
+ }
482
+
483
+ /*----------------------------------
484
+ - HOOKS
485
+ ----------------------------------*/
486
+ private hooks: {
487
+ [hookname in THookName]?: (THookCallback<this> | null)[]
488
+ } = {}
489
+
490
+ public on(hookName: THookName, callback: THookCallback<this>) {
491
+
492
+ debug && console.info(LogPrefix, `Register hook ${hookName}`);
493
+
494
+ let cbIndex: number;
495
+ let callbacks = this.hooks[hookName];
496
+ if (!callbacks) {
497
+ cbIndex = 0;
498
+ callbacks = this.hooks[hookName] = [callback]
499
+ } else {
500
+ cbIndex = callbacks.length;
501
+ callbacks.push(callback);
502
+ }
503
+
504
+ // Listener remover
505
+ return () => {
506
+ debug && console.info(LogPrefix, `De-register hook ${hookName} (index ${cbIndex})`);
507
+ this.hooks[hookName] = this.hooks[hookName]?.filter(
508
+ (_, index) => index !== cbIndex
509
+ );
510
+ }
511
+
512
+ }
513
+
514
+ public runHook(hookName: THookName, request: ClientRequest<this>) {
515
+ const callbacks = this.hooks[hookName];
516
+ if (callbacks)
517
+ for (const callback of callbacks)
518
+ // callback can be null since we use delete to unregister
519
+ callback && callback(request);
520
+ }
521
+ }