proteum 1.0.2 → 2.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 (185) hide show
  1. package/AGENTS.md +101 -0
  2. package/agents/codex/AGENTS.md +95 -0
  3. package/agents/codex/CODING_STYLE.md +71 -0
  4. package/agents/codex/agents.md.zip +0 -0
  5. package/agents/codex/client/AGENTS.md +102 -0
  6. package/agents/codex/client/pages/AGENTS.md +35 -0
  7. package/agents/codex/server/routes/AGENTS.md +12 -0
  8. package/agents/codex/server/services/AGENTS.md +137 -0
  9. package/agents/codex/tests/AGENTS.md +8 -0
  10. package/cli/app/config.ts +13 -11
  11. package/cli/app/index.ts +74 -82
  12. package/cli/bin.js +1 -1
  13. package/cli/commands/build.ts +51 -14
  14. package/cli/commands/check.ts +19 -0
  15. package/cli/commands/deploy/app.ts +4 -8
  16. package/cli/commands/deploy/web.ts +16 -20
  17. package/cli/commands/dev.ts +189 -64
  18. package/cli/commands/devEvents.ts +106 -0
  19. package/cli/commands/init.ts +63 -57
  20. package/cli/commands/lint.ts +21 -0
  21. package/cli/commands/refresh.ts +18 -0
  22. package/cli/commands/typecheck.ts +18 -0
  23. package/cli/compiler/client/identite.ts +80 -53
  24. package/cli/compiler/client/index.ts +139 -213
  25. package/cli/compiler/common/bundleAnalysis.ts +94 -0
  26. package/cli/compiler/common/clientManifest.ts +67 -0
  27. package/cli/compiler/common/controllers.ts +288 -0
  28. package/cli/compiler/common/files/autres.ts +7 -18
  29. package/cli/compiler/common/files/images.ts +40 -37
  30. package/cli/compiler/common/files/style.ts +11 -22
  31. package/cli/compiler/common/generatedRouteModules.ts +368 -0
  32. package/cli/compiler/common/index.ts +31 -65
  33. package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
  34. package/cli/compiler/common/rspackAliases.ts +13 -0
  35. package/cli/compiler/common/scripts.ts +37 -0
  36. package/cli/compiler/index.ts +781 -230
  37. package/cli/compiler/server/index.ts +59 -75
  38. package/cli/compiler/writeIfChanged.ts +21 -0
  39. package/cli/index.ts +71 -72
  40. package/cli/paths.ts +51 -57
  41. package/cli/print.ts +17 -11
  42. package/cli/tsconfig.json +5 -4
  43. package/cli/utils/agents.ts +100 -0
  44. package/cli/utils/check.ts +71 -0
  45. package/cli/utils/index.ts +1 -3
  46. package/cli/utils/keyboard.ts +8 -25
  47. package/cli/utils/runProcess.ts +30 -0
  48. package/client/app/component.tsx +29 -29
  49. package/client/app/index.ts +36 -57
  50. package/client/app/service.ts +7 -12
  51. package/client/app.tsconfig.json +2 -2
  52. package/client/components/Dialog/Manager.ssr.tsx +40 -0
  53. package/client/components/Dialog/Manager.tsx +119 -150
  54. package/client/components/Dialog/status.tsx +3 -3
  55. package/client/components/index.ts +1 -1
  56. package/client/components/types.d.ts +1 -3
  57. package/client/dev/hmr.ts +65 -0
  58. package/client/global.d.ts +2 -2
  59. package/client/hooks.ts +6 -9
  60. package/client/index.ts +2 -1
  61. package/client/islands/index.ts +7 -0
  62. package/client/islands/useDeferredModule.ts +199 -0
  63. package/client/pages/_layout/index.tsx +4 -12
  64. package/client/pages/useHeader.tsx +14 -21
  65. package/client/router.ts +27 -0
  66. package/client/services/router/components/Link.tsx +34 -27
  67. package/client/services/router/components/Page.tsx +6 -14
  68. package/client/services/router/components/router.ssr.tsx +36 -0
  69. package/client/services/router/components/router.tsx +63 -83
  70. package/client/services/router/index.tsx +185 -220
  71. package/client/services/router/request/api.ts +97 -119
  72. package/client/services/router/request/history.ts +2 -2
  73. package/client/services/router/request/index.ts +13 -12
  74. package/client/services/router/request/multipart.ts +72 -62
  75. package/client/services/router/response/index.tsx +68 -61
  76. package/client/services/router/response/page.ts +28 -32
  77. package/client/utils/dom.ts +17 -33
  78. package/common/app/index.ts +3 -3
  79. package/common/data/chaines/index.ts +22 -23
  80. package/common/data/dates.ts +35 -70
  81. package/common/data/markdown.ts +42 -39
  82. package/common/dev/serverHotReload.ts +26 -0
  83. package/common/errors/index.tsx +110 -142
  84. package/common/router/contracts.ts +29 -0
  85. package/common/router/index.ts +89 -108
  86. package/common/router/layouts.ts +34 -47
  87. package/common/router/pageSetup.ts +50 -0
  88. package/common/router/register.ts +53 -24
  89. package/common/router/request/api.ts +30 -36
  90. package/common/router/request/index.ts +2 -8
  91. package/common/router/response/index.ts +8 -15
  92. package/common/router/response/page.ts +70 -58
  93. package/common/utils.ts +1 -1
  94. package/doc/TODO.md +1 -1
  95. package/eslint.js +62 -0
  96. package/package.json +14 -49
  97. package/prettier.config.cjs +9 -0
  98. package/scripts/cleanup-generated-controllers.ts +62 -0
  99. package/scripts/fix-reference-app-typing.ts +490 -0
  100. package/scripts/refactor-client-app-imports.ts +244 -0
  101. package/scripts/refactor-client-pages.ts +587 -0
  102. package/scripts/refactor-server-controllers.ts +470 -0
  103. package/scripts/refactor-server-runtime-aliases.ts +360 -0
  104. package/scripts/restore-client-app-import-files.ts +41 -0
  105. package/scripts/restore-files-from-git-head.ts +20 -0
  106. package/scripts/update-codex-agents.ts +35 -0
  107. package/server/app/commands.ts +35 -64
  108. package/server/app/container/config.ts +48 -59
  109. package/server/app/container/console/index.ts +202 -248
  110. package/server/app/container/index.ts +33 -71
  111. package/server/app/controller/index.ts +61 -0
  112. package/server/app/index.ts +39 -105
  113. package/server/app/service/container.ts +41 -42
  114. package/server/app/service/index.ts +120 -147
  115. package/server/context.ts +1 -1
  116. package/server/index.ts +25 -1
  117. package/server/services/auth/index.ts +75 -115
  118. package/server/services/auth/router/index.ts +31 -32
  119. package/server/services/auth/router/request.ts +14 -16
  120. package/server/services/cron/CronTask.ts +13 -26
  121. package/server/services/cron/index.ts +14 -36
  122. package/server/services/disks/driver.ts +40 -58
  123. package/server/services/disks/drivers/local/index.ts +79 -90
  124. package/server/services/disks/drivers/s3/index.ts +116 -163
  125. package/server/services/disks/index.ts +23 -38
  126. package/server/services/email/index.ts +45 -104
  127. package/server/services/email/utils.ts +14 -27
  128. package/server/services/fetch/index.ts +53 -85
  129. package/server/services/prisma/Facet.ts +39 -91
  130. package/server/services/prisma/index.ts +74 -110
  131. package/server/services/router/generatedRuntime.ts +29 -0
  132. package/server/services/router/http/index.ts +78 -73
  133. package/server/services/router/http/multipart.ts +19 -42
  134. package/server/services/router/index.ts +378 -365
  135. package/server/services/router/request/api.ts +26 -25
  136. package/server/services/router/request/index.ts +44 -51
  137. package/server/services/router/request/service.ts +7 -11
  138. package/server/services/router/request/validation/zod.ts +111 -148
  139. package/server/services/router/response/index.ts +110 -125
  140. package/server/services/router/response/mask/Filter.ts +31 -72
  141. package/server/services/router/response/mask/index.ts +8 -15
  142. package/server/services/router/response/mask/selecteurs.ts +11 -25
  143. package/server/services/router/response/page/clientManifest.ts +25 -0
  144. package/server/services/router/response/page/document.tsx +199 -127
  145. package/server/services/router/response/page/index.tsx +89 -94
  146. package/server/services/router/service.ts +13 -15
  147. package/server/services/schema/index.ts +17 -26
  148. package/server/services/schema/request.ts +19 -33
  149. package/server/services/schema/router/index.ts +8 -11
  150. package/server/services/security/encrypt/aes/index.ts +15 -35
  151. package/server/utils/slug.ts +29 -35
  152. package/skills/clean-project-code/SKILL.md +63 -0
  153. package/skills/clean-project-code/agents/openai.yaml +4 -0
  154. package/tsconfig.common.json +4 -3
  155. package/tsconfig.json +4 -1
  156. package/types/aliases.d.ts +17 -21
  157. package/types/controller-input.test.ts +48 -0
  158. package/types/express-extra.d.ts +6 -0
  159. package/types/global/constants.d.ts +13 -0
  160. package/types/global/express-extra.d.ts +6 -0
  161. package/types/global/modules.d.ts +13 -16
  162. package/types/global/utils.d.ts +17 -49
  163. package/types/global/vendors.d.ts +62 -0
  164. package/types/icons.d.ts +65 -1
  165. package/types/uuid.d.ts +3 -0
  166. package/types/vendors.d.ts +62 -0
  167. package/cli/compiler/common/babel/index.ts +0 -170
  168. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  169. package/cli/compiler/common/babel/plugins/services.ts +0 -586
  170. package/cli/compiler/common/babel/routes/imports.ts +0 -127
  171. package/cli/compiler/common/babel/routes/routes.ts +0 -1130
  172. package/client/services/captcha/index.ts +0 -67
  173. package/client/services/socket/index.ts +0 -147
  174. package/common/data/rte/nodes.ts +0 -83
  175. package/common/data/stats.ts +0 -90
  176. package/common/utils/rte.ts +0 -183
  177. package/server/services/auth/old.ts +0 -277
  178. package/server/services/cache/commands.ts +0 -41
  179. package/server/services/cache/index.ts +0 -297
  180. package/server/services/cache/service.json +0 -6
  181. package/server/services/socket/index.ts +0 -162
  182. package/server/services/socket/scope.ts +0 -226
  183. package/server/services/socket/service.json +0 -6
  184. package/server/services_old/SocketClient.ts +0 -92
  185. package/server/services_old/Token.old.ts +0 -97
@@ -1,5 +1,4 @@
1
-
2
- // INSPIRATION:
1
+ // INSPIRATION:
3
2
  // https://adonisjs.com/docs/4.1/routing
4
3
  // https://laravel.com/docs/8.x/routing
5
4
  // https://github.com/adonisjs/http-server/blob/develop/src/ServerRouter/indexApi.ts
@@ -11,39 +10,44 @@
11
10
 
12
11
  // Node
13
12
  // Npm
13
+ const { v4: uuid } = require('uuid') as { v4: () => string };
14
14
  import got from 'got';
15
15
  import hInterval from 'human-interval';
16
16
  import type express from 'express';
17
17
  import type { Request, Response, NextFunction } from 'express';
18
- import { v4 as uuid } from 'uuid';
19
18
  import zod, { ZodError } from 'zod';
20
19
  export { default as schema } from 'zod';
21
- import type { GlobImportedWithMetas } from 'babel-plugin-glob-import';
22
20
 
23
21
  // Core
24
- import type { Application } from '@server/app';
25
22
  import Service, { AnyService, TServiceArgs } from '@server/app/service';
26
23
  import context from '@server/context';
27
24
  import type DisksManager from '@server/services/disks';
28
25
  import { CoreError, InputError, NotFound, toJson as errorToJson } from '@common/errors';
29
26
  import BaseRouter, {
30
- TRoute, TErrorRoute, TRouteModule,
31
- TRouteOptions, defaultOptions,
32
- matchRoute, buildUrl, TDomainsList
27
+ TMatchedRoute,
28
+ TRoute,
29
+ TErrorRoute,
30
+ TRouteModule,
31
+ TRouteOptions,
32
+ defaultOptions,
33
+ matchRoute,
34
+ buildUrl,
35
+ TDomainsList,
33
36
  } from '@common/router';
37
+ import type { TSsrUnresolvedRoute, TRegisterPageArgs } from '@common/router/contracts';
34
38
  import { buildRegex, getRegisterPageArgs } from '@common/router/register';
35
39
  import { layoutsList, getLayout } from '@common/router/layouts';
36
40
  import { TFetcherList } from '@common/router/request/api';
37
41
  import type { TFrontRenderer } from '@common/router/response/page';
38
- import type { TSsrUnresolvedRoute, TRegisterPageArgs } from '@client/services/router';
39
42
 
40
43
  // Specific
41
44
  import { AnyRouterService } from './service';
42
- import ServerRequest from "./request";
45
+ import ServerRequest from './request';
43
46
  import ServerResponse, { TRouterContext, TRouterContextServices } from './response';
44
47
  import Page from './response/page';
45
48
  import HTTP, { Config as HttpServiceConfig } from './http';
46
49
  import DocumentRenderer from './response/page/document';
50
+ import { loadGeneratedRuntimeBundle } from './generatedRuntime';
47
51
 
48
52
  /*----------------------------------
49
53
  - TYPES
@@ -51,113 +55,120 @@ import DocumentRenderer from './response/page/document';
51
55
 
52
56
  export { type AnyRouterService, default as RouterService } from './service';
53
57
  export { default as RequestService } from './request/service';
54
- export type { default as Request, UploadedFile } from "./request";
55
- export type { default as Response, TRouterContext } from "./response";
58
+ export type { default as Request, UploadedFile } from './request';
59
+ export type { default as Response, TRouterContext } from './response';
56
60
  export type { TRoute, TAnyRoute } from '@common/router';
57
61
 
58
- export type TApiRegisterArgs<TRouter extends TServerRouter> = ([
59
- path: string,
60
- controller: TServerController<TRouter>
61
- ] | [
62
- path: string,
63
- options: Partial<TRouteOptions>,
64
- controller: TServerController<TRouter>
65
- ])
62
+ export type TApiRegisterArgs<TRouter extends TServerRouter> =
63
+ | [path: string, controller: TServerController<TRouter>]
64
+ | [path: string, options: Partial<TRouteOptions>, controller: TServerController<TRouter>];
65
+
66
+ type TGeneratedRouteModule = { filepath: string; register?: TRouteModule['__register'] };
67
+
68
+ type TGeneratedControllerDefinition = {
69
+ path: string;
70
+ Controller: new (request: TRouterContext<TServerRouter>) => { [method: string]: () => any };
71
+ method: string;
72
+ };
73
+
74
+ type TGeneratedDefinitionsSnapshot = {
75
+ routes: TMatchedRoute[];
76
+ errors: { [code: number]: TErrorRoute<any> };
77
+ controllers: { [path: string]: TRoute };
78
+ ssrRoutes: TSsrUnresolvedRoute[];
79
+ cache: { [pageId: string]: { rendered: any; expire: number | undefined; options: TRouteOptions['static'] } };
80
+ };
66
81
 
67
82
  export type TServerController<TRouter extends TServerRouter> = (context: TRouterContext<TRouter>) => any;
68
83
 
69
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
84
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
70
85
  export type TRouteHttpMethod = HttpMethod | '*';
71
86
 
72
- export type TApiResponseData = {
73
- data: any,
74
- triggers?: { [cle: string]: any }
75
- }
87
+ export type TApiResponseData = { data: any; triggers?: { [cle: string]: any } };
76
88
 
77
- export type HttpHeaders = { [cle: string]: string }
89
+ export type HttpHeaders = { [cle: string]: string };
78
90
 
79
91
  /*----------------------------------
80
92
  - SERVICE CONFIG
81
93
  ----------------------------------*/
82
94
 
83
- export type TAnyRouter = ServerRouter<Application, TRouterServicesList, Config<TRouterServicesList>>;
95
+ export type TAnyRouter = ServerRouter<
96
+ AnyService['app'],
97
+ TRouterServicesList,
98
+ Config<TRouterServicesList, AnyService['app']>
99
+ >;
84
100
 
85
101
  const LogPrefix = '[router]';
86
102
 
87
103
  export type Config<
88
- TServices extends TRouterServicesList
104
+ TServices extends TRouterServicesList,
105
+ TApplication extends AnyService['app'] = AnyService['app'],
89
106
  > = {
107
+ debug: boolean;
90
108
 
91
- debug: boolean,
92
-
93
- disk?: string, // Disk driver ID
109
+ disk?: string; // Disk driver ID
94
110
 
95
- domains: TDomainsList,
111
+ domains: TDomainsList;
96
112
 
97
- http: HttpServiceConfig,
113
+ http: HttpServiceConfig;
98
114
 
99
- context: (
100
- request: ServerRequest<TServerRouter>,
101
- app: Application
102
- ) => {},
115
+ context: (request: ServerRequest<TServerRouter>, app: TApplication) => {};
103
116
 
104
- plugins: TServices
105
- }
117
+ plugins: TServices;
118
+ };
106
119
 
107
120
  // Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
108
- type TRouterServicesList = {
109
- [serviceName: string]: AnyRouterService
110
- }
121
+ type TRouterServicesList = { [serviceName: string]: AnyRouterService };
111
122
 
112
- export type Hooks = {
113
-
114
- }
123
+ export type Hooks = {};
115
124
 
116
125
  export type TControllerDefinition = {
117
- path?: string,
118
- schema?: zod.ZodSchema,
119
- controller: TServerController<TServerRouter>,
120
- }
126
+ path?: string;
127
+ schema?: zod.ZodSchema;
128
+ controller: TServerController<TServerRouter>;
129
+ };
121
130
 
122
- export type TServerRouter = ServerRouter<Application, TRouterServicesList, Config<TRouterServicesList>>;
131
+ export type TServerRouter = ServerRouter<
132
+ AnyService['app'],
133
+ TRouterServicesList,
134
+ Config<TRouterServicesList, AnyService['app']>
135
+ >;
123
136
 
124
137
  /*----------------------------------
125
138
  - CLASSE
126
139
  ----------------------------------*/
127
140
  export default class ServerRouter<
128
- TApplication extends Application,
129
- TServices extends TRouterServicesList,
130
- TConfig extends Config<TServices>,
131
- >
132
- extends Service<TConfig, Hooks, TApplication, TApplication> implements BaseRouter {
141
+ TApplication extends AnyService['app'] = AnyService['app'],
142
+ TServices extends TRouterServicesList = TRouterServicesList,
143
+ TConfig extends Config<TServices, TApplication> = Config<TServices, TApplication>,
144
+ >
145
+ extends Service<TConfig, Hooks, TApplication, TApplication>
146
+ implements BaseRouter
147
+ {
148
+ public disks = this.use<DisksManager<any, any, TApplication>>('Core/Disks', { optional: true });
133
149
 
134
- public disks = this.use<DisksManager>('Core/Disks', { optional: true });
135
-
136
150
  // Services
137
151
  public http: HTTP;
138
152
  public render: DocumentRenderer<this>;
139
153
 
140
154
  // Indexed
141
- public routes: TRoute[] = []; // API + pages front front
155
+ public routes: TMatchedRoute[] = []; // API + pages front front
142
156
  public errors: { [code: number]: TErrorRoute } = {};
143
- public controllers: {[path: string]: TRoute} = {};
157
+ public controllers: { [path: string]: TRoute } = {};
144
158
  public ssrRoutes: TSsrUnresolvedRoute[] = [];
145
159
 
146
160
  // Cache (ex: for static pages)
147
161
  public cache: {
148
- [pageId: string]: {
149
- rendered: any,
150
- expire: number | undefined,
151
- options: TRouteOptions["static"]
152
- }
153
- } = {}
162
+ [pageId: string]: { rendered: any; expire: number | undefined; options: TRouteOptions['static'] };
163
+ } = {};
164
+
165
+ private staticRoutesRefreshInterval?: NodeJS.Timeout;
154
166
 
155
167
  /*----------------------------------
156
168
  - SERVICE
157
169
  ----------------------------------*/
158
170
 
159
- public constructor( ...args: TServiceArgs< ServerRouter<TApplication, TServices, TConfig> >) {
160
-
171
+ public constructor(...args: TServiceArgs<ServerRouter<TApplication, TServices, TConfig>>) {
161
172
  super(...args);
162
173
 
163
174
  this.http = new HTTP(this.config.http, this);
@@ -169,20 +180,16 @@ export default class ServerRouter<
169
180
  ----------------------------------*/
170
181
 
171
182
  public async ready() {
172
-
173
183
  // Detect router services
174
184
  for (const serviceName in this.config.plugins) {
175
185
  const service = this.config.plugins[serviceName];
176
186
  service.parent = this;
177
- this.app.register( service )
187
+ this.app.register(service);
178
188
  }
179
189
 
180
- // Use require to avoid circular references
181
- this.registerRoutes([
182
- ...require("metas:@/server/routes/**/*.ts"),
183
- ...require("metas:@/client/pages/**/([a-z0-9]*).tsx"),
184
- //...require("metas:@/client/pages/**/([a-z0-9]*).tsx")
185
- ]);
190
+ this.registerControllers(this.loadGeneratedControllerDefinitions());
191
+
192
+ this.registerRoutes(this.loadGeneratedRouteModules());
186
193
 
187
194
  // Start HTTP server
188
195
  await this.http.start();
@@ -190,71 +197,78 @@ export default class ServerRouter<
190
197
  // override
191
198
  const originalLog = console.log;
192
199
  console.log = (...args: any[]) => {
193
-
194
200
  // parse stack trace: skip this function and the console.log call
195
201
  /*const stackLine = (new Error()).stack?.split('\n')[2] || '';
196
202
  const match = stackLine.match(/at (\w+)\.(\w+) /);
197
203
  const className = match ? match[1] : '<global>';
198
204
  const methodName = match ? match[2] : '<anonymous>';*/
199
205
 
200
- const contextData = context.getStore() || {
201
- channelType: 'master',
202
- }
206
+ const contextData = context.getStore() || { channelType: 'master' };
203
207
 
204
- const requestPrefix = contextData.channelType === 'request'
205
- ? `[${contextData.user ? contextData.user : 'guest'}] ${contextData.method} ${contextData.path} |`
206
- : 'master';
208
+ const requestPrefix =
209
+ contextData.channelType === 'request'
210
+ ? `[${contextData.user ? contextData.user : 'guest'}] ${contextData.method} ${contextData.path} |`
211
+ : 'master';
207
212
 
208
213
  // prefix and forward
209
214
  originalLog.call(
210
- console,
215
+ console,
211
216
  `${requestPrefix}`, // ${className}.${methodName}
212
- ...args
217
+ ...args,
213
218
  );
214
219
  };
215
220
 
216
-
217
221
  // When all the services are ready, initialize static routes
218
- this.app.on('ready', () => {
222
+ this.app.on('ready', async () => {
219
223
  this.initStaticRoutes();
220
224
  });
221
-
222
225
  }
223
226
 
224
- public async shutdown() {
227
+ public async shutdown() {}
228
+
229
+ public async reloadGeneratedDefinitions(changedFiles: string[] = []) {
230
+ const changeSummary = changedFiles.length > 0 ? `\n${changedFiles.join('\n')}` : '';
231
+ console.info(`[router] Hot reloading generated definitions ...${changeSummary}`);
225
232
 
233
+ const controllerDefinitions = this.loadGeneratedControllerDefinitions();
234
+ const routeModules = this.loadGeneratedRouteModules();
235
+ const previousState = this.snapshotGeneratedDefinitions();
236
+
237
+ this.resetGeneratedDefinitions();
238
+
239
+ try {
240
+ this.registerControllers(controllerDefinitions);
241
+ this.registerRoutes(routeModules);
242
+ this.initStaticRoutes();
243
+
244
+ console.info('[router] Generated definitions hot reloaded.');
245
+ } catch (error) {
246
+ console.error('[router] Failed to hot reload generated definitions. Restoring previous router state.');
247
+ this.restoreGeneratedDefinitions(previousState);
248
+ throw error;
249
+ }
226
250
  }
227
251
 
228
252
  /*----------------------------------
229
253
  - ACTIONS
230
254
  ----------------------------------*/
231
255
 
232
- public async renderStatic(
233
- url: string,
234
- options: TRouteOptions["static"],
235
- rendered?: any
236
- ) {
237
-
256
+ public async renderStatic(url: string, options: TRouteOptions['static'], rendered?: any) {
238
257
  // Wildcard: tell that the newly rendered pages should be cached
239
- if (url === '*' || !url)
240
- return;
258
+ if (url === '*' || !url) return;
241
259
 
242
260
  if (!rendered) {
243
-
244
261
  console.log('[router] renderStatic: url', url);
245
262
 
246
263
  const fullUrl = this.url(url, {}, true);
247
- const response = await got( fullUrl, {
264
+ const response = await got(fullUrl, {
248
265
  method: 'GET',
249
- headers: {
250
- 'Accept': 'text/html',
251
- 'bypasscache': '1'
252
- },
266
+ headers: { Accept: 'text/html', bypasscache: '1' },
253
267
  throwHttpErrors: false,
254
268
  });
255
269
 
256
270
  if (response.statusCode !== 200) {
257
- console.error("[router] renderStatic: page returned code", response.statusCode, fullUrl);
271
+ console.error('[router] renderStatic: page returned code', response.statusCode, fullUrl);
258
272
  return;
259
273
  }
260
274
 
@@ -264,19 +278,15 @@ export default class ServerRouter<
264
278
  this.cache[url] = {
265
279
  rendered: rendered,
266
280
  options: options,
267
- expire: typeof options === 'object'
268
- ? Date.now() + (hInterval(options.refresh) || 3600)
269
- : undefined
281
+ expire: typeof options === 'object' ? Date.now() + (hInterval(options.refresh) || 3600) : undefined,
270
282
  };
271
-
272
283
  }
273
284
 
274
285
  private initStaticRoutes() {
286
+ this.clearStaticRoutesRefreshInterval();
275
287
 
276
288
  for (const route of this.routes) {
277
-
278
- if (!route.options.static)
279
- continue;
289
+ if (!route.options.static) continue;
280
290
 
281
291
  // Add to static pages
282
292
  // Should be a GET oage that don't take any parameter
@@ -286,36 +296,35 @@ export default class ServerRouter<
286
296
  }
287
297
 
288
298
  // Every hours, refresh static pages
289
- setInterval(() => {
290
- this.refreshStaticPages();
291
- }, 1000 * 60 * 60);
299
+ this.staticRoutesRefreshInterval = setInterval(
300
+ () => {
301
+ this.refreshStaticPages();
302
+ },
303
+ 1000 * 60 * 60,
304
+ );
292
305
  }
293
306
 
294
307
  private refreshStaticPages() {
295
-
296
308
  console.log('[router] refreshStaticPages');
297
-
309
+
298
310
  for (const pageUrl in this.cache) {
299
311
  const page = this.cache[pageUrl];
300
312
  if (page.expire && page.expire < Date.now()) {
301
-
302
313
  this.renderStatic(pageUrl, page.options);
303
314
  }
304
315
  }
305
316
  }
306
317
 
307
- private registerRoutes(defModules: GlobImportedWithMetas<TRouteModule>) {
318
+ private registerRoutes(defModules: TGeneratedRouteModule[]) {
308
319
  for (const routeModule of defModules) {
320
+ const register = routeModule.register;
321
+ if (!register) continue;
309
322
 
310
- const register = routeModule.exports.__register;
311
- if (!register)
312
- continue;
313
-
314
- this.config.debug && console.log(LogPrefix, `Register file:`, routeModule.matches.join('/'));
323
+ this.config.debug && console.log(LogPrefix, `Register file:`, routeModule.filepath);
315
324
  try {
316
325
  register(this.app);
317
326
  } catch (error) {
318
- console.error("Failed to register route file:", routeModule);
327
+ console.error('Failed to register route file:', routeModule);
319
328
  console.error('Register function:', register.toString());
320
329
  throw error;
321
330
  }
@@ -324,20 +333,35 @@ export default class ServerRouter<
324
333
  this.afterRegister();
325
334
  }
326
335
 
327
- public url = (path: string, params: {} = {}, absolute: boolean = true) =>
336
+ private registerControllers(definitions: TGeneratedControllerDefinition[]) {
337
+ for (const definition of definitions) {
338
+ const route: TRoute<TRouterContext<this>> = {
339
+ method: 'POST',
340
+ path: definition.path,
341
+ controller: (requestContext: TRouterContext<this>) => {
342
+ const controller = new definition.Controller(requestContext);
343
+ return controller[definition.method]();
344
+ },
345
+ options: { ...defaultOptions },
346
+ };
347
+
348
+ this.controllers[route.path] = route;
349
+ }
350
+ }
351
+
352
+ public url = (path: string, params: {} = {}, absolute: boolean = true) =>
328
353
  buildUrl(path, params, this.config.domains, absolute);
329
354
 
330
355
  /*----------------------------------
331
356
  - REGISTER
332
357
  ----------------------------------*/
333
358
 
334
- public page(...args: TRegisterPageArgs) {
335
-
336
- const { path, options, renderer, layout } = getRegisterPageArgs(...args);
359
+ public page(...args: TRegisterPageArgs<any, TRouteOptions>) {
360
+ const { path, options, setup, renderer, layout } = getRegisterPageArgs(...args);
337
361
 
338
362
  const { regex, keys } = buildRegex(path);
339
363
 
340
- const route: TRoute = {
364
+ const route: TMatchedRoute<TRouterContext<this>> = {
341
365
  method: 'GET',
342
366
  path,
343
367
  regex,
@@ -346,29 +370,30 @@ export default class ServerRouter<
346
370
  options: {
347
371
  ...defaultOptions,
348
372
  accept: 'html', // Les pages retournent forcémment du html
349
- ...options
373
+ setup,
374
+ ...options,
350
375
  },
351
- }
376
+ };
352
377
 
353
378
  this.routes.push(route);
354
379
 
355
380
  return this;
356
-
357
381
  }
358
382
 
359
383
  public error(
360
384
  code: number,
361
- options: TRoute["options"],
362
- renderer: TFrontRenderer<{}, { message: string }>
385
+ options: Partial<TRouteOptions>,
386
+ renderer: TFrontRenderer<{}, { message: string }>,
363
387
  ) {
388
+ const finalOptions = { ...defaultOptions, ...options };
364
389
 
365
390
  // Automatic layout form the nearest _layout folder
366
- const layout = getLayout('Error ' + code, options);
391
+ const layout = getLayout('Error ' + code, finalOptions);
367
392
 
368
- const route = {
393
+ const route: TErrorRoute<TRouterContext<this>> = {
369
394
  code,
370
395
  controller: (context: TRouterContext<this>) => new Page(route, renderer, context, layout),
371
- options
396
+ options: finalOptions,
372
397
  };
373
398
 
374
399
  this.errors[code] = route;
@@ -382,144 +407,160 @@ export default class ServerRouter<
382
407
  public patch = (...args: TApiRegisterArgs<this>) => this.registerApi('PATCH', ...args);
383
408
  public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args);
384
409
 
385
- public express(
386
- middleware: (
387
- req: Request,
388
- res: Response,
389
- next: NextFunction,
390
- requestContext: TRouterContext<this>
391
- ) => void
410
+ public express(
411
+ middleware: (req: Request, res: Response, next: NextFunction, requestContext: TRouterContext<this>) => void,
392
412
  ) {
393
- return (context: TRouterContext<this>) => new Promise((resolve) => {
394
-
395
- context.request.res.on('finish', function() {
396
- //console.log('the response has been sent', request.res.statusCode);
397
- resolve(true);
413
+ return (context: TRouterContext<this>) =>
414
+ new Promise((resolve) => {
415
+ context.request.res.on('finish', function () {
416
+ //console.log('the response has been sent', request.res.statusCode);
417
+ resolve(true);
418
+ });
419
+
420
+ middleware(
421
+ context.request.req,
422
+ context.request.res,
423
+ () => {
424
+ resolve(true);
425
+ },
426
+ context,
427
+ );
398
428
  });
399
-
400
- middleware(
401
- context.request.req,
402
- context.request.res,
403
- () => { resolve(true); },
404
- context
405
- )
406
- })
407
429
  }
408
430
 
409
431
  protected registerApi(method: TRouteHttpMethod, ...args: TApiRegisterArgs<this>): this {
410
-
411
432
  let path: string;
412
433
  let options: Partial<TRouteOptions> = {};
413
434
  let controller: TServerController<this>;
414
435
 
415
- if (args.length === 2)
416
- ([path, controller] = args)
417
- else
418
- ([path, options, controller] = args)
436
+ if (args.length === 2) [path, controller] = args;
437
+ else [path, options, controller] = args;
419
438
 
420
439
  const { regex, keys } = buildRegex(path);
421
440
 
422
- const route: TRoute = {
423
-
441
+ const route: TMatchedRoute<TRouterContext<this>> = {
424
442
  method: method,
425
443
  path: path,
426
444
  regex,
427
445
  keys: keys,
428
- options: {
429
- ...defaultOptions,
430
- ...options
431
- },
432
- controller
433
- }
446
+ options: { ...defaultOptions, ...options },
447
+ controller,
448
+ };
434
449
 
435
450
  this.routes.push(route);
436
451
 
437
452
  return this;
438
453
  }
439
454
 
440
- private async afterRegister() {
455
+ private clearStaticRoutesRefreshInterval() {
456
+ if (this.staticRoutesRefreshInterval) {
457
+ clearInterval(this.staticRoutesRefreshInterval);
458
+ this.staticRoutesRefreshInterval = undefined;
459
+ }
460
+ }
461
+
462
+ private resetGeneratedDefinitions() {
463
+ this.clearStaticRoutesRefreshInterval();
464
+ this.routes = [];
465
+ this.errors = {};
466
+ this.controllers = {};
467
+ this.ssrRoutes = [];
468
+ this.cache = {};
469
+ }
470
+
471
+ private snapshotGeneratedDefinitions(): TGeneratedDefinitionsSnapshot {
472
+ return {
473
+ routes: [...this.routes],
474
+ errors: { ...this.errors },
475
+ controllers: { ...this.controllers },
476
+ ssrRoutes: [...this.ssrRoutes],
477
+ cache: { ...this.cache },
478
+ };
479
+ }
480
+
481
+ private restoreGeneratedDefinitions(snapshot: TGeneratedDefinitionsSnapshot) {
482
+ this.routes = snapshot.routes;
483
+ this.errors = snapshot.errors;
484
+ this.controllers = snapshot.controllers;
485
+ this.ssrRoutes = snapshot.ssrRoutes;
486
+ this.cache = snapshot.cache;
487
+ this.initStaticRoutes();
488
+ }
441
489
 
490
+ private loadGeneratedControllerDefinitions() {
491
+ return (
492
+ loadGeneratedRuntimeBundle<TGeneratedControllerDefinition[]>('controllers') ||
493
+ require('@/server/.generated/controllers').default ||
494
+ []
495
+ );
496
+ }
497
+
498
+ private loadGeneratedRouteModules() {
499
+ return (
500
+ loadGeneratedRuntimeBundle<TGeneratedRouteModule[]>('routes') ||
501
+ require('@/server/.generated/routes').default ||
502
+ []
503
+ );
504
+ }
505
+
506
+ private async afterRegister() {
442
507
  // Ordonne par ordre de priorité
443
- this.config.debug && console.info("Loading routes ...");
508
+ this.config.debug && console.info('Loading routes ...');
444
509
  this.routes.sort((r1, r2) => {
445
-
446
510
  const prioDelta = r2.options.priority - r1.options.priority;
447
- if (prioDelta !== 0)
448
- return prioDelta;
511
+ if (prioDelta !== 0) return prioDelta;
449
512
 
450
513
  // HTML avant json
451
- if (r1.options.accept === 'html' && r2.options.accept !== 'html')
452
- return -1;
514
+ if (r1.options.accept === 'html' && r2.options.accept !== 'html') return -1;
453
515
 
454
516
  // Unchanged
455
517
  return 0;
456
- })
518
+ });
457
519
  // - Génère les définitions de route pour le client
458
520
  this.config.debug && console.info(`Registered routes:`);
459
521
  for (const route of this.routes) {
522
+ const chunkId = route.options.id;
460
523
 
461
- const chunkId = route.options["id"];
462
-
463
- this.config.debug && console.info('-',
464
- route.method,
465
- route.path,
466
- ' :: ', JSON.stringify(route.options)
467
- );
468
-
469
- if (chunkId)
470
- this.ssrRoutes.push({
471
- regex: route.regex.source,
472
- keys: route.keys,
473
- chunk: chunkId
474
- });
524
+ this.config.debug && console.info('-', route.method, route.path, ' :: ', JSON.stringify(route.options));
475
525
 
526
+ if (chunkId) this.ssrRoutes.push({ regex: route.regex.source, keys: route.keys, chunk: chunkId });
476
527
  }
477
528
 
478
529
  this.config.debug && console.info(`Registered error pages:`);
479
530
  for (const code in this.errors) {
480
-
481
531
  const route = this.errors[code];
482
- const chunkId = route.options["id"];
532
+ const chunkId = route.options.id;
483
533
 
484
- this.config.debug && console.info('-', code,
485
- ' :: ', JSON.stringify(route.options)
486
- );
534
+ this.config.debug && console.info('-', code, ' :: ', JSON.stringify(route.options));
487
535
 
488
- this.ssrRoutes.push({
489
- code: parseInt(code),
490
- chunk: chunkId,
491
- });
536
+ if (chunkId) this.ssrRoutes.push({ code: parseInt(code), chunk: chunkId });
492
537
  }
493
538
 
494
539
  this.config.debug && console.info(`Registered layouts:`);
495
540
  for (const layoutId in layoutsList) {
496
-
497
541
  const layout = layoutsList[layoutId];
498
542
 
499
543
  this.config.debug && console.info('-', layoutId, layout);
500
544
  }
501
545
 
502
- this.config.debug && console.info(this.routes.length + " routes where registered.");
546
+ this.config.debug && console.info(this.routes.length + ' routes where registered.');
503
547
  }
504
548
 
505
549
  /*----------------------------------
506
550
  - RESOLUTION
507
551
  ----------------------------------*/
508
552
  public async middleware(req: express.Request, res: express.Response) {
509
-
510
553
  // Don't cache HTML, because in case of update, assets file name will change (hash.ext)
511
554
  // https://github.com/helmetjs/nocache/blob/main/index.ts
512
- res.setHeader("Surrogate-Control", "no-store");
513
- res.setHeader(
514
- "Cache-Control",
515
- "no-store, no-cache, must-revalidate, proxy-revalidate"
516
- );
555
+ res.setHeader('Surrogate-Control', 'no-store');
556
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
517
557
 
518
558
  // Create request
519
559
  let requestId = uuid();
520
- const cachedPage = req.headers['bypasscache']
521
- ? undefined
522
- : this.cache[req.path];
560
+ const cachedPage = req.headers['bypasscache'] ? undefined : this.cache[req.path];
561
+ const headers: HttpHeaders = Object.fromEntries(
562
+ Object.entries(req.headers).map(([key, value]) => [key, Array.isArray(value) ? value.join(', ') : value || '']),
563
+ );
523
564
 
524
565
  const request = new ServerRequest(
525
566
  requestId,
@@ -528,28 +569,25 @@ export default class ServerRouter<
528
569
  req.path, // url sans params
529
570
  // Exclusion de req.files, car le middleware multipart les a normalisé dans req.body
530
571
  { ...req.query, ...req.body },
531
- req.headers,
572
+ headers,
532
573
 
533
574
  res,
534
- this
575
+ this,
535
576
  );
536
577
 
537
578
  let response: ServerResponse<this>;
538
579
  try {
539
-
540
580
  // Hook
541
581
  await this.runHook('request', request);
542
582
 
543
583
  // Bulk API Requests
544
- if (request.path === '/api' && typeof request.data.fetchers === "object") {
545
-
584
+ if (request.path === '/api' && typeof request.data.fetchers === 'object') {
546
585
  return await this.resolveApiBatch(request.data.fetchers, request);
547
-
548
586
  } else {
549
587
  response = await this.resolve(
550
- request,
588
+ request,
551
589
  // If cached page, we only run routes with priority >= 10
552
- cachedPage ? true : false
590
+ cachedPage ? true : false,
553
591
  );
554
592
  }
555
593
  } catch (e) {
@@ -557,13 +595,12 @@ export default class ServerRouter<
557
595
  }
558
596
 
559
597
  if (!res.headersSent) {
560
-
561
598
  // Static pages
562
599
  if (cachedPage) {
563
600
  console.log('[router] Get static page from cache', req.path);
564
- res.send( cachedPage.rendered );
601
+ res.send(cachedPage.rendered);
565
602
  return;
566
- }
603
+ }
567
604
 
568
605
  // Status
569
606
  res.status(response.statusCode);
@@ -576,130 +613,117 @@ export default class ServerRouter<
576
613
  }
577
614
  }
578
615
 
579
- public createContextServices( request: ServerRequest<this> ) {
580
-
581
- const contextServices: Partial<TRouterContextServices<this>> = {}
616
+ public createContextServices(request: ServerRequest<this>) {
617
+ const contextServices: Partial<TRouterContextServices<this>> = {};
582
618
  for (const serviceName in this.config.plugins) {
583
-
584
619
  const routerService = this.config.plugins[serviceName];
585
620
  if (!routerService)
586
- throw new Error(`Could not access router service ${serviceName}. Maybe the referenced service is not started yet? Try to reduce its priority.`);
621
+ throw new Error(
622
+ `Could not access router service ${serviceName}. Maybe the referenced service is not started yet? Try to reduce its priority.`,
623
+ );
587
624
 
588
625
  if (!routerService.requestService)
589
- throw new Error(`Router service ${serviceName} is not implementing the requestService method from the RouterService interface.`);
626
+ throw new Error(
627
+ `Router service ${serviceName} is not implementing the requestService method from the RouterService interface.`,
628
+ );
590
629
 
591
- const requestService = routerService.requestService( request );
630
+ const requestService = routerService.requestService(request);
592
631
  if (requestService !== null)
593
- contextServices[ serviceName ] = requestService;
594
-
632
+ contextServices[serviceName as keyof TRouterContextServices<this>] =
633
+ requestService as TRouterContextServices<this>[keyof TRouterContextServices<this>];
595
634
  }
596
635
 
597
636
  return contextServices;
598
637
  }
599
638
 
600
- public resolve = (
601
- request: ServerRequest<this>,
602
- isStatic?: boolean
603
- ) => new Promise<ServerResponse<this>>((resolve, reject) => {
604
-
605
- // Create request context so we can access request context across all the request-triggered libs
606
- context.run({
607
- // This is for debugging
608
- channelType: 'request',
609
- channelId: request.id,
610
- method: request.method,
611
- path: request.path,
612
- }, async () => {
613
-
614
- const timeStart = Date.now();
615
-
616
- if (this.status === 'starting') {
617
- console.log(LogPrefix, `Waiting for servert to be resdy before resolving request`);
618
- await this.started;
619
- }
620
-
621
- try {
622
-
623
- const response = new ServerResponse<this>(request);
624
-
625
- await this.runHook('resolve', request);
626
-
627
- // Controller route
628
- let route = this.controllers[request.path];
629
- if (route !== undefined) {
630
-
631
- // Create response
632
- await this.resolvedRoute(route, response, timeStart);
633
- if (response.wasProvided)
634
- return resolve(response);
635
- }
636
-
637
- const contextStore = context.getStore();
638
- if (contextStore)
639
- contextStore.user = request.user?.email;
640
-
641
- // Classic routes
642
- for (route of this.routes) {
643
-
644
- if (isStatic && !route.options.whenStatic)
645
- continue;
646
-
647
- // Match Method
648
- if (request.method !== route.method && route.method !== '*')
649
- continue;
650
-
651
- // Match Response format
652
- if (!request.accepts(route.options.accept))
653
- continue;
654
-
655
- const isMatching = matchRoute(route, request);
656
- if (!isMatching)
657
- continue;
658
-
659
- await this.resolvedRoute(route, response, timeStart);
660
- if (response.wasProvided)
661
- return resolve(response);
662
- }
663
-
664
- reject( new NotFound() );
665
-
666
- } catch (error) {
667
-
668
- if (this.app.env.profile === 'dev') {
669
- console.log('API batch error:', request.method, request.path, error);
670
- const errOrigin = request.method + ' ' + request.path;
671
- if (error.details === undefined)
672
- error.details = { origin: errOrigin }
673
- else
674
- error.details.origin = errOrigin;
675
- }
676
-
677
- this.printTakenTime(timeStart);
678
- reject( error );
679
- }
639
+ public resolve = (request: ServerRequest<this>, isStatic?: boolean) =>
640
+ new Promise<ServerResponse<this>>((resolve, reject) => {
641
+ // Create request context so we can access request context across all the request-triggered libs
642
+ context.run(
643
+ {
644
+ // This is for debugging
645
+ channelType: 'request',
646
+ channelId: request.id,
647
+ method: request.method,
648
+ path: request.path,
649
+ },
650
+ async () => {
651
+ const timeStart = Date.now();
652
+
653
+ if (this.status === 'starting') {
654
+ console.log(LogPrefix, `Waiting for servert to be resdy before resolving request`);
655
+ await this.started;
656
+ }
657
+
658
+ try {
659
+ const response = new ServerResponse<this>(request);
660
+
661
+ await this.runHook('resolve', request);
662
+
663
+ // Controller route
664
+ const controllerRoute = this.controllers[request.path];
665
+ if (controllerRoute !== undefined) {
666
+ // Create response
667
+ await response.runController(controllerRoute);
668
+ if (response.wasProvided) return resolve(response);
669
+ }
670
+
671
+ const contextStore = context.getStore();
672
+ if (contextStore) contextStore.user = request.user?.email;
673
+
674
+ // Classic routes
675
+ for (const route of this.routes) {
676
+ if (isStatic && !route.options.whenStatic) continue;
677
+
678
+ // Match Method
679
+ if (request.method !== route.method && route.method !== '*') continue;
680
+
681
+ // Match Response format
682
+ if (!request.accepts(route.options.accept)) continue;
683
+
684
+ const isMatching = matchRoute(route, request);
685
+ if (!isMatching) continue;
686
+
687
+ await this.resolvedRoute(route, response, timeStart);
688
+ if (response.wasProvided) return resolve(response);
689
+ }
690
+
691
+ reject(new NotFound());
692
+ } catch (error) {
693
+ const typedError =
694
+ error instanceof Error
695
+ ? error
696
+ : new Error(typeof error === 'string' ? error : 'Unknown router error');
697
+
698
+ if (this.app.env.profile === 'dev') {
699
+ console.log('API batch error:', request.method, request.path, typedError);
700
+ const errOrigin = request.method + ' ' + request.path;
701
+ if ('details' in typedError) {
702
+ const routerError = typedError as Error & { details?: { origin?: string } };
703
+ if (routerError.details === undefined) routerError.details = { origin: errOrigin };
704
+ else routerError.details.origin = errOrigin;
705
+ }
706
+ }
707
+
708
+ this.printTakenTime(timeStart);
709
+ reject(typedError);
710
+ }
711
+ },
712
+ );
680
713
  });
681
- });
682
714
 
683
- private async resolvedRoute(
684
- route: TRoute,
685
- response: ServerResponse<this>,
686
- timeStart: number
687
- ) {
715
+ private async resolvedRoute(route: TMatchedRoute, response: ServerResponse<this>, timeStart: number) {
716
+ route = await response.resolveRouteOptions(route);
688
717
 
689
718
  // Run on resolution hooks. Ex: authentication check
690
719
  await this.runHook('resolved', route, response.request, response);
691
720
 
692
721
  // Create response
693
722
  await response.runController(route);
694
- if (!response.wasProvided)
695
- return;
723
+ if (!response.wasProvided) return;
696
724
 
697
725
  // Set in cache
698
- if (
699
- response.request.path
700
- && route.options.static
701
- && route.options.static.urls.includes('*')
702
- ) {
726
+ if (response.request.path && route.options.static && route.options.static.urls.includes('*')) {
703
727
  console.log('[router] Set in cache', response.request.path);
704
728
  this.renderStatic(response.request.path, route.options.static, response.data);
705
729
  }
@@ -709,26 +733,26 @@ export default class ServerRouter<
709
733
  }
710
734
 
711
735
  private printTakenTime = (timeStart: number, timeEndResolving?: number) => {
712
-
713
736
  if (this.app.env.name === 'server') return;
714
737
 
715
- console.log( Math.round(Date.now() - timeStart) + 'ms' +
716
- (timeEndResolving === undefined ? '' : ' | Routing: ' + Math.round(timeEndResolving - timeStart))
738
+ console.log(
739
+ Math.round(Date.now() - timeStart) +
740
+ 'ms' +
741
+ (timeEndResolving === undefined ? '' : ' | Routing: ' + Math.round(timeEndResolving - timeStart)),
717
742
  );
718
- }
719
-
720
- private async resolveApiBatch( fetchers: TFetcherList, request: ServerRequest<this> ) {
743
+ };
721
744
 
745
+ private async resolveApiBatch(fetchers: TFetcherList, request: ServerRequest<this>) {
722
746
  // TODO: use api.fetchSync instead
723
747
 
724
748
  const responseData: TObjetDonnees = {};
725
749
  for (const id in fetchers) {
750
+ const fetcher = fetchers[id];
751
+ if (!fetcher || !('method' in fetcher)) continue;
726
752
 
727
- const { method, path, data } = fetchers[id];
753
+ const { method, path, data } = fetcher;
728
754
 
729
- const response = await this.resolve(
730
- request.children(method, path, data)
731
- );
755
+ const response = await this.resolve(request.children(method, path, data));
732
756
 
733
757
  responseData[id] = response.data;
734
758
 
@@ -741,61 +765,50 @@ export default class ServerRouter<
741
765
  request.res.json(responseData);
742
766
  }
743
767
 
744
- private async handleError( e: Error | CoreError | ZodError, request: ServerRequest<ServerRouter> ) {
745
-
768
+ private async handleError(e: unknown, request: ServerRequest<this>) {
769
+ let error: Error | CoreError;
746
770
  if (e instanceof ZodError)
747
- e = new InputError(
748
- e.issues.map((e) => e.path.join('.') + ': ' + e.message).join(', ')
749
- );
771
+ error = new InputError(e.issues.map((issue) => issue.path.join('.') + ': ' + issue.message).join(', '));
772
+ else if (e instanceof Error) error = e;
773
+ else error = new Error(typeof e === 'string' ? e : 'Unknown error');
750
774
 
751
- const code = 'http' in e ? e.http : 500;
775
+ const code = 'http' in error ? error.http : 500;
752
776
 
753
- const response = new ServerResponse(request).status(code)
777
+ const response = new ServerResponse(request).status(code);
754
778
 
755
779
  // Rapport / debug
756
780
  if (code === 500) {
757
-
758
781
  // Print the error here so the stacktrace appears in the bug report logs
759
- console.log(LogPrefix, "Error catched from the router:", e);
782
+ console.log(LogPrefix, 'Error catched from the router:', error);
760
783
 
761
784
  // Report error
762
- await this.app.runHook('error', e, request);
785
+ await this.app.runHook('error', error, request);
763
786
 
764
787
  // Don't exose technical errors to users
765
788
  if (this.app.env.profile === 'prod')
766
- e = new Error(
767
- "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience."
789
+ error = new Error(
790
+ 'We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.',
768
791
  );
769
-
770
792
  } else {
771
-
772
793
  // For debugging HTTP errors
773
794
  /*if (this.app.env.profile === "dev")
774
795
  console.warn(e);*/
775
796
 
776
- await this.app.runHook('error.' + code, e, request);
797
+ await this.app.runHook('error.' + code, error, request);
777
798
  }
778
799
 
779
800
  // Return error based on the request format
780
- if (request.accepts("html")) {
781
-
801
+ if (request.accepts('html')) {
782
802
  const route = this.errors[code];
783
- if (route === undefined)
784
- throw new Error(`No route for error code ${code}`);
785
-
786
- const jsonError = errorToJson(e);
787
- await response.setRoute(route).runController(route, {
788
- error: jsonError
789
- });
803
+ if (route === undefined) throw new Error(`No route for error code ${code}`);
790
804
 
791
- } else if (request.accepts("json")) {
792
- const jsonError = errorToJson(e);
805
+ const jsonError = errorToJson(error);
806
+ await response.setRoute(route).runController(route, { error: jsonError });
807
+ } else if (request.accepts('json')) {
808
+ const jsonError = errorToJson(error);
793
809
  await response.json(jsonError);
794
- } else
795
- await response.text(e.message);
810
+ } else await response.text(error.message);
796
811
 
797
812
  return response;
798
-
799
813
  }
800
-
801
- }
814
+ }