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
@@ -11,201 +11,192 @@ import type {
11
11
  default as ServerRouter,
12
12
  Request as ServerRequest,
13
13
  Response as ServerResponse,
14
- TAnyRouter
14
+ TAnyRouter,
15
15
  } from '@server/services/router';
16
16
  import type { TBasicSSrData } from '@server/services/router/response';
17
17
 
18
18
  import BaseRouter, {
19
- defaultOptions, TRoute, TErrorRoute,
20
- TClientOrServerContextForPage, TRouteModule,
21
- matchRoute, buildUrl
22
- } from '@common/router'
19
+ defaultOptions,
20
+ TRoute,
21
+ TErrorRoute,
22
+ TRouteOptions,
23
+ TRouteModule,
24
+ TDomainsList,
25
+ matchRoute,
26
+ buildUrl,
27
+ } from '@common/router';
28
+ import type { TRegisterPageArgs, TSsrUnresolvedRoute } from '@common/router/contracts';
23
29
  import { getLayout } from '@common/router/layouts';
24
30
  import { getRegisterPageArgs, buildRegex } from '@common/router/register';
25
31
  import { TFetcherList } from '@common/router/request/api';
26
- import type { TFrontRenderer } from '@common/router/response/page';
32
+ import type { TFrontRenderer, TPageSetup } from '@common/router/response/page';
27
33
 
28
34
  import App from '@client/app/component';
29
35
  import type ClientApplication from '@client/app';
30
36
  import Service from '@client/app/service';
31
37
 
32
38
  // Specific
33
- import type { ClientContext } from '@/client/context';
34
- import ClientRequest from './request';
39
+ import ClientRequest, { isClientRequest } from './request';
35
40
  import { location, history } from './request/history';
36
- import ClientResponse from './response';
41
+ import ClientResponse, { type TRouterContext } from './response';
37
42
  import ClientPage from './response/page';
38
43
 
44
+ type AppPropsContext = Parameters<typeof App>[0]['context'];
45
+
39
46
  // 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';
47
+ import appRoutes from '@/client/.generated/routes';
44
48
 
45
49
  /*----------------------------------
46
50
  - CONFIG
47
51
  ----------------------------------*/
48
52
 
49
53
  const debug = false;
50
- const LogPrefix = '[router]'
54
+ const LogPrefix = '[router]';
55
+ const browserWindow = window as Window & { routes?: TSsrUnresolvedRoute[]; ssr?: TBasicSSrData };
51
56
 
52
57
  /*----------------------------------
53
58
  - TYPES
54
59
  ----------------------------------*/
55
60
 
56
61
  // Client router can handle Client requests AND Server requests (for pages only)
57
- export type { default as ClientResponse, TRouterContext } from "./response";
62
+ export type { default as ClientResponse, TRouterContext } from './response';
63
+
64
+ export type TAnyClientRouter = ClientRouter<any, any>;
58
65
 
59
- export type Router = ClientRouter | TAnyRouter;
66
+ export type Router = TAnyClientRouter | TAnyRouter;
60
67
 
61
- export type Request = ClientRequest<ClientRouter> | ServerRequest<TAnyRouter>;
68
+ export type Request = ClientRequest<TAnyClientRouter> | ServerRequest<TAnyRouter>;
62
69
 
63
- export type Response = ClientResponse<ClientRouter> | ServerResponse<TAnyRouter>;
70
+ export type Response = ClientResponse<TAnyClientRouter> | ServerResponse<TAnyRouter>;
64
71
 
65
72
  /*----------------------------------
66
73
  - TYPES: ROUTES LOADING
67
74
  ----------------------------------*/
68
75
 
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
-
76
+ // WARN: Keep this aligned with the generated route wrapper contract on both sides.
95
77
  // Route definition without having loaded the controller
96
78
  type TUnresolvedRoute = TUnresolvedErrorRoute | TUnresolvedNormalRoute;
97
79
 
80
+ type TClientPageRoute<TRouter extends TAnyClientRouter = TAnyClientRouter> = TRoute<
81
+ TRouterContext<TRouter, TRouter['app']>,
82
+ ClientPage<TRouter> | Promise<any>
83
+ >;
84
+
85
+ type TClientPageErrorRoute<TRouter extends TAnyClientRouter = TAnyClientRouter> = TErrorRoute<
86
+ TRouterContext<TRouter, TRouter['app']>,
87
+ ClientPage<TRouter> | Promise<any>
88
+ >;
89
+
98
90
  export type TUnresolvedErrorRoute = {
99
- index: number,
100
- chunk: string,
101
- code: number,
102
- load: TRouteLoader<TErrorRoute>,
103
- }
91
+ index: number;
92
+ chunk: string;
93
+ code: number;
94
+ load: TRouteLoader<TClientPageErrorRoute>;
95
+ };
104
96
 
105
97
  export type TUnresolvedNormalRoute = {
106
- index: number,
107
- chunk: string,
108
- code: number,
109
- load: TRouteLoader<TErrorRoute>,
110
- }
98
+ index: number;
99
+ chunk: string;
100
+ regex: RegExp;
101
+ keys: (number | string)[];
102
+ load: TRouteLoader<TClientPageRoute>;
103
+ };
111
104
 
112
- type TRouteLoader<Route extends TRoute | TErrorRoute = TRoute | TErrorRoute> = () => Promise<TRouteModule<Route>>;
105
+ type TRouteLoader<
106
+ Route extends TClientPageRoute | TClientPageErrorRoute = TClientPageRoute | TClientPageErrorRoute,
107
+ > = () => Promise<
108
+ TRouteModule<Route>
109
+ >;
113
110
 
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
- }
111
+ export type TRoutesLoaders = { [chunkId: string]: TRouteLoader<TClientPageRoute | TClientPageErrorRoute> };
119
112
 
120
113
  /*----------------------------------
121
114
  - SERVICE TYPES
122
115
  ----------------------------------*/
123
116
 
124
- export type THookCallback<TRouter extends ClientRouter> = (request: ClientRequest<TRouter>) => void;
117
+ export type THookCallback<TRouter extends TAnyClientRouter> = (request: ClientRequest<TRouter>) => void;
125
118
 
126
- type THookName = 'page.change' | 'page.changed' | 'page.rendered'
119
+ type THookName = 'page.change' | 'page.changed' | 'page.rendered';
127
120
 
128
- type Config<TAdditionnalContext extends {} = {}> = {
129
- preload: string[], // List of globs
130
- context: (context: {}, router: ClientRouter) => TAdditionnalContext,
131
- }
121
+ type Config = {
122
+ preload: string[]; // List of globs
123
+ context: (context: {}, router: TAnyClientRouter) => any;
124
+ };
132
125
 
133
126
  /*----------------------------------
134
127
  - ROUTER
135
128
  ----------------------------------*/
136
129
  export default class ClientRouter<
137
- TApplication extends ClientApplication = ClientApplication,
138
- TAdditionnalContext extends {} = {}
139
- > extends Service<Config<TAdditionnalContext>, ClientApplication> implements BaseRouter {
140
-
130
+ TApplication extends ClientApplication = ClientApplication,
131
+ TConfig extends Config = Config,
132
+ >
133
+ extends Service<TConfig, TApplication>
134
+ implements BaseRouter
135
+ {
141
136
  // 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;
137
+ public ssrRoutes = browserWindow.routes || [];
138
+ public ssrContext = browserWindow.ssr;
139
+ public domains: TDomainsList = browserWindow.ssr?.domains || ({ current: window.location.origin } as TDomainsList);
140
+ public context!: TRouterContext<this, this['app']>;
149
141
 
150
- public constructor(app: TApplication, config: Config<TAdditionnalContext>) {
142
+ public setLoading!: React.Dispatch<React.SetStateAction<boolean>>;
143
+ public navigate!: (page: ClientPage<this>, data?: {}) => void;
151
144
 
145
+ public constructor(app: TApplication, config: TConfig) {
152
146
  super(app, config);
153
147
  }
154
148
 
155
149
  public async start() {
156
-
157
150
  const currentRoute = await this.registerRoutes();
158
151
 
159
152
  this.initialRender(currentRoute);
160
153
  }
161
154
 
162
- public url = (path: string, params: {} = {}, absolute: boolean = true) =>
155
+ public url = (path: string, params: {} = {}, absolute: boolean = true) =>
163
156
  buildUrl(path, params, this.domains, absolute);
164
157
 
165
- public go( url: string | number, data: {} = {}, opt: {
166
- newTab?: boolean
167
- } = {}) {
168
-
158
+ public go(url: string | number, data: {} = {}, opt: { newTab?: boolean } = {}) {
169
159
  // Error code
170
160
  if (typeof url === 'number') {
171
- this.createResponse( this.errors[url], this.context.request, data ).then(( page ) => {
161
+ const currentRequest = this.context.request;
162
+ if (!isClientRequest<this>(currentRequest))
163
+ throw new Error(`Client router cannot resolve an error page from a non-client request.`);
164
+
165
+ this.createResponse(this.errors[url], currentRequest, data).then((page) => {
172
166
  this.navigate(page, data);
173
- })
167
+ });
174
168
  return;
175
169
  }
176
170
 
177
171
  url = this.url(url, data, false);
178
172
 
179
- if (opt.newTab)
180
- window.open(url)
173
+ if (opt.newTab) window.open(url);
181
174
  // Same domain = history url replacement
182
- else if (url[0] === '/')
183
- history?.replace( url );
175
+ else if (url[0] === '/') history?.replace(url);
184
176
  // Different domain = hard navigation
185
- else
186
- window.location.href = url;
177
+ else window.location.href = url;
187
178
  }
188
179
 
189
180
  /*----------------------------------
190
181
  - REGISTRATION
191
182
  ----------------------------------*/
192
183
 
193
- public routes: (TRoute | TUnresolvedNormalRoute)[] = [];
194
- public errors: { [code: number]: TErrorRoute | TUnresolvedErrorRoute } = {};
184
+ public routes: Array<TClientPageRoute<ClientRouter<TApplication, TConfig>> | TUnresolvedNormalRoute> = [];
185
+ public errors: {
186
+ [code: number]: TClientPageErrorRoute<ClientRouter<TApplication, TConfig>> | TUnresolvedErrorRoute;
187
+ } = {};
195
188
 
196
189
  public async registerRoutes() {
197
-
198
- const loaders: TRoutesLoaders = { ...appRoutes }
190
+ const loaders = appRoutes as TRoutesLoaders;
199
191
  let currentRoute: TUnresolvedRoute | undefined;
200
192
  debug && console.log(LogPrefix, `Indexing routes and finding the current route from ssr data:`, this.context);
201
193
 
202
194
  // Associe la liste des routes (obtenue via ssr) à leur loader
203
195
  for (let routeIndex = 0; routeIndex < this.ssrRoutes.length; routeIndex++) {
204
-
205
196
  const ssrRoute = this.ssrRoutes[routeIndex];
206
197
 
207
198
  if (loaders[ssrRoute.chunk] === undefined) {
208
- console.error("Chunk id not found for ssr route:", ssrRoute, "Searched in:", loaders);
199
+ console.error('Chunk id not found for ssr route:', ssrRoute, 'Searched in:', loaders);
209
200
  continue;
210
201
  }
211
202
 
@@ -216,29 +207,25 @@ export default class ClientRouter<
216
207
  let route: TUnresolvedRoute;
217
208
  if ('code' in ssrRoute)
218
209
  route = this.errors[ssrRoute.code] = {
210
+ index: routeIndex,
219
211
  code: ssrRoute.code,
220
212
  chunk: ssrRoute.chunk,
221
- load: loader,
222
- }
213
+ load: loader as TRouteLoader<TClientPageErrorRoute>,
214
+ };
223
215
  else
224
216
  route = this.routes[routeIndex] = {
225
217
  index: routeIndex,
226
218
  chunk: ssrRoute.chunk,
227
219
  regex: new RegExp(ssrRoute.regex),
228
220
  keys: ssrRoute.keys,
229
- load: loader,
230
- }
221
+ load: loader as TRouteLoader<TClientPageRoute>,
222
+ };
231
223
 
232
224
  debug && console.log(LogPrefix, `${route.chunk}`, route);
233
225
 
234
226
  // Detect if it's the current route
235
227
  if (currentRoute === undefined) {
236
-
237
- const isCurrentRoute = (
238
- this.ssrContext !== undefined
239
- &&
240
- route.chunk === this.ssrContext.page.chunkId
241
- );
228
+ const isCurrentRoute = this.ssrContext !== undefined && route.chunk === this.ssrContext.page.chunkId;
242
229
 
243
230
  if (isCurrentRoute) {
244
231
  currentRoute = route;
@@ -250,38 +237,46 @@ export default class ClientRouter<
250
237
  return currentRoute;
251
238
  }
252
239
 
253
- public page<TProvidedData extends TFetcherList = TFetcherList>(
240
+ public page<TProvidedData extends {} = {}>(
254
241
  path: string,
255
- renderer: TFrontRenderer<TProvidedData>
256
- ): TRoute;
242
+ renderer: TFrontRenderer<TProvidedData>,
243
+ ): TClientPageRoute<this>;
257
244
 
258
- public page<TProvidedData extends TFetcherList = TFetcherList>(
245
+ public page<TProvidedData extends {} = {}>(
259
246
  path: string,
260
- options: Partial<TRoute["options"]>,
261
- renderer: TFrontRenderer<TProvidedData>
262
- ): TRoute;
247
+ setup: TPageSetup<TProvidedData>,
248
+ renderer: TFrontRenderer<TProvidedData>,
249
+ ): TClientPageRoute<this>;
263
250
 
264
- public page(...args: TRegisterPageArgs): TRoute {
251
+ public page<TProvidedData extends {} = {}>(
252
+ path: string,
253
+ options: Partial<TRouteOptions>,
254
+ renderer: TFrontRenderer<TProvidedData>,
255
+ ): TClientPageRoute<this>;
265
256
 
266
- const { path, options, renderer, layout } = getRegisterPageArgs(...args);
257
+ public page<TProvidedData extends {} = {}>(
258
+ path: string,
259
+ options: Partial<TRouteOptions>,
260
+ setup: TPageSetup<TProvidedData>,
261
+ renderer: TFrontRenderer<TProvidedData>,
262
+ ): TClientPageRoute<this>;
267
263
 
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}.`);
264
+ public page(...args: TRegisterPageArgs<any, TRouteOptions>): TClientPageRoute<this> {
265
+ const { path, options, setup, renderer, layout } = getRegisterPageArgs(...args);
266
+
267
+ // Page ids are injected by the generated route wrapper modules.
268
+ const id = options.id;
269
+ if (id === undefined) throw new Error(`Page route ${path} is missing its generated id metadata.`);
272
270
 
273
271
  const { regex, keys } = buildRegex(path);
274
272
 
275
- const route: TRoute = {
273
+ const route: TClientPageRoute<this> = {
276
274
  method: 'GET',
277
275
  path,
278
276
  regex,
279
277
  keys,
280
- options: {
281
- ...defaultOptions,
282
- ...options
283
- },
284
- controller: (context: TClientOrServerContextForPage) => new ClientPage(route, renderer, context, layout)
278
+ options: { ...defaultOptions, setup, ...options },
279
+ controller: (context) => new ClientPage(route, renderer, context as any, layout),
285
280
  };
286
281
 
287
282
  this.routes.push(route);
@@ -290,18 +285,19 @@ export default class ClientRouter<
290
285
  }
291
286
 
292
287
  public error(
293
- code: number,
294
- options: Partial<TRoute["options"]>,
295
- renderer: TFrontRenderer<{}, { message: string }>
296
- ) {
288
+ code: number,
289
+ options: Partial<TRouteOptions>,
290
+ renderer: TFrontRenderer<{}, { message: string }>,
291
+ ): TClientPageErrorRoute<this> {
292
+ const finalOptions = { ...defaultOptions, ...options };
297
293
 
298
294
  // Automatic layout form the nearest _layout folder
299
- const layout = getLayout('Error ' + code, options);
295
+ const layout = getLayout('Error ' + code, finalOptions);
300
296
 
301
- const route: TErrorRoute = {
297
+ const route: TClientPageErrorRoute<this> = {
302
298
  code,
303
- controller: (context: TClientOrServerContextForPage) => new ClientPage(route, renderer, context, layout),
304
- options
299
+ controller: (context) => new ClientPage(route, renderer, context as any, layout),
300
+ options: finalOptions,
305
301
  };
306
302
 
307
303
  this.errors[code] = route;
@@ -309,74 +305,54 @@ export default class ClientRouter<
309
305
  return route;
310
306
  }
311
307
 
312
-
313
308
  /*----------------------------------
314
309
  - RESOLUTION
315
310
  ----------------------------------*/
316
- public async resolve(request: ClientRequest<this>): Promise<ClientPage> {
317
-
311
+ public async resolve(request: ClientRequest<this>): Promise<ClientPage<this>> {
318
312
  debug && console.log(LogPrefix, 'Resolving request', request.path, Object.keys(request.data));
319
313
 
320
314
  for (let iRoute = 0; iRoute < this.routes.length; iRoute++) {
321
-
322
315
  let route = this.routes[iRoute];
323
- if (!('regex' in route))
316
+ if (!('regex' in route) || !(route.regex instanceof RegExp) || !('keys' in route) || !Array.isArray(route.keys))
324
317
  continue;
325
318
 
326
- const isMatching = matchRoute(route, request);
327
- if (!isMatching)
328
- continue;
319
+ const isMatching = matchRoute({ regex: route.regex, keys: route.keys }, request);
320
+ if (!isMatching) continue;
329
321
 
330
322
  // Create response
331
323
  debug && console.log(LogPrefix, 'Resolved request', request.path, '| Route:', route);
332
324
  const page = await this.createResponse(route, request);
333
325
 
334
326
  return page;
335
-
336
- };
327
+ }
337
328
 
338
329
  const notFoundRoute = this.errors[404];
339
- return await this.createResponse(notFoundRoute, request, {
340
- error: new Error("Page not found")
341
- });
330
+ return await this.createResponse(notFoundRoute, request, { error: new Error('Page not found') });
342
331
  }
343
332
 
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
-
333
+ private async load(route: TUnresolvedNormalRoute): Promise<TClientPageRoute<this>>;
334
+ private async load(route: TUnresolvedErrorRoute): Promise<TClientPageErrorRoute<this>>;
335
+ private async load(
336
+ route: TUnresolvedNormalRoute | TUnresolvedErrorRoute,
337
+ ): Promise<TClientPageRoute<this> | TClientPageErrorRoute<this>> {
348
338
  //throw new Error(`Failed to load route: ${route.chunk}`);
349
339
 
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.app.handleUpdate();
364
- } catch (error) {}
365
- throw new Error("A new version of the website is available. Please refresh the page.");
366
- }
340
+ debug && console.log(`Fetching route ${route.chunk} ...`, route);
341
+ try {
342
+ const loaded = await route.load();
343
+ const fetched = loaded.__register(this.app);
367
344
 
368
- } else {
345
+ debug && console.log(`Route fetched: ${route.chunk}`, fetched);
369
346
 
370
- debug && console.log(`Route already fetched: ${route.chunk}`, route.load);
371
- fetched = route.load;
347
+ if ('code' in route) return fetched as TClientPageErrorRoute<this>;
372
348
 
373
- }
374
-
375
- debug && console.log(`Route fetched: ${route.chunk}`, fetched);
376
- return {
377
- ...fetched,
378
- regex: route.regex,
379
- keys: route.keys
349
+ return { ...(fetched as TClientPageRoute<this>), regex: route.regex, keys: route.keys };
350
+ } catch (e) {
351
+ console.error(`Failed to fetch the route ${route.chunk}`, e);
352
+ try {
353
+ this.app.handleUpdate();
354
+ } catch (error) {}
355
+ throw new Error('A new version of the website is available. Please refresh the page.');
380
356
  }
381
357
  }
382
358
 
@@ -385,21 +361,17 @@ export default class ClientRouter<
385
361
  }
386
362
 
387
363
  private async initialRender(route: TUnresolvedRoute | undefined) {
388
-
389
364
  debug && console.log(LogPrefix, `Initial render route`, route);
390
365
 
391
- if (!location)
392
- throw new Error(`Unable to retrieve current location.`);
366
+ if (!location) throw new Error(`Unable to retrieve current location.`);
393
367
 
394
- if (!route)
395
- throw new Error(`Unable to resolve route.`);
368
+ if (!route) throw new Error(`Unable to resolve route.`);
396
369
 
397
370
  const request = new ClientRequest(location, this);
398
371
 
399
372
  // Restituate SSR response
400
- let apiData: {} = {}
373
+ let apiData: {} = {};
401
374
  if (this.ssrContext) {
402
-
403
375
  request.user = this.ssrContext.user || null;
404
376
 
405
377
  request.data = this.ssrContext.request.data;
@@ -411,10 +383,7 @@ export default class ClientRouter<
411
383
 
412
384
  const response = await this.createResponse(route, request, apiData);
413
385
 
414
- ReactDOM.hydrate((
415
- <App context={response.context} />
416
- ), document.body, () => {
417
-
386
+ ReactDOM.hydrate(<App context={response.context as AppPropsContext} />, document.body, () => {
418
387
  console.log(`Render complete`);
419
388
 
420
389
  this.runHook('page.rendered', request);
@@ -422,24 +391,29 @@ export default class ClientRouter<
422
391
  }
423
392
 
424
393
  private async createResponse(
425
- route: TUnresolvedRoute | TErrorRoute | TRoute,
394
+ route: TUnresolvedRoute | TClientPageErrorRoute<this> | TClientPageRoute<this>,
426
395
  request: ClientRequest<this>,
427
- pageData: {} = {}
428
- ): Promise<ClientPage> {
429
-
396
+ pageData: {} = {},
397
+ ): Promise<ClientPage<this>> {
430
398
  // Load the route if not done before
431
- if ('load' in route)
432
- route = this.routes[route.index] = await this.load(route);
399
+ if ('load' in route) {
400
+ if ('code' in route) {
401
+ const loadedRoute = await this.load(route);
402
+ this.errors[route.code] = loadedRoute;
403
+ route = loadedRoute;
404
+ } else {
405
+ const loadedRoute = await this.load(route);
406
+ this.routes[route.index] = loadedRoute;
407
+ route = loadedRoute;
408
+ }
409
+ }
433
410
 
434
411
  // Run controller
435
412
  // TODO: tell that ruController on the client side always returns pages
436
413
  try {
437
-
438
- const response = new ClientResponse<this, ClientPage>(request, route);
414
+ const response = new ClientResponse<this, ClientPage<this>>(request, route);
439
415
  return await response.runController(pageData);
440
-
441
416
  } catch (error) {
442
-
443
417
  return await this.createErrorResponse(error, request);
444
418
  }
445
419
  }
@@ -447,9 +421,8 @@ export default class ClientRouter<
447
421
  private async createErrorResponse(
448
422
  e: any,
449
423
  request: ClientRequest<this>,
450
- pageData: {} = {}
451
- ): Promise<ClientPage> {
452
-
424
+ pageData: {} = {},
425
+ ): Promise<ClientPage<this>> {
453
426
  const code = 'http' in e ? e.http : 500;
454
427
  console.log(`Loading error page ` + code);
455
428
  let route = this.errors[code];
@@ -463,29 +436,25 @@ export default class ClientRouter<
463
436
  }
464
437
 
465
438
  // Load if not done before
466
- if ('load' in route)
467
- route = this.errors[code] = await this.load(route);
439
+ if ('load' in route) route = this.errors[code] = await this.load(route);
468
440
 
469
- const response = new ClientResponse<this, ClientPage>(request, route);
441
+ const response = new ClientResponse<this, ClientPage<this>>(request, route);
470
442
  return await response.runController(pageData);
471
443
  }
472
444
 
473
445
  /*----------------------------------
474
446
  - HOOKS
475
447
  ----------------------------------*/
476
- private hooks: {
477
- [hookname in THookName]?: (THookCallback<this> | null)[]
478
- } = {}
448
+ private hooks: { [hookname in THookName]?: (THookCallback<this> | null)[] } = {};
479
449
 
480
450
  public on(hookName: THookName, callback: THookCallback<this>) {
481
-
482
451
  debug && console.info(LogPrefix, `Register hook ${hookName}`);
483
452
 
484
453
  let cbIndex: number;
485
454
  let callbacks = this.hooks[hookName];
486
455
  if (!callbacks) {
487
456
  cbIndex = 0;
488
- callbacks = this.hooks[hookName] = [callback]
457
+ callbacks = this.hooks[hookName] = [callback];
489
458
  } else {
490
459
  cbIndex = callbacks.length;
491
460
  callbacks.push(callback);
@@ -494,18 +463,14 @@ export default class ClientRouter<
494
463
  // Listener remover
495
464
  return () => {
496
465
  debug && console.info(LogPrefix, `De-register hook ${hookName} (index ${cbIndex})`);
497
- this.hooks[hookName] = this.hooks[hookName]?.filter(
498
- (_, index) => index !== cbIndex
499
- );
500
- }
501
-
466
+ this.hooks[hookName] = this.hooks[hookName]?.filter((_, index) => index !== cbIndex);
467
+ };
502
468
  }
503
469
 
504
470
  public runHook(hookName: THookName, request: ClientRequest<this>) {
505
471
  const callbacks = this.hooks[hookName];
506
472
  if (callbacks)
507
- for (const callback of callbacks)
508
- // callback can be null since we use delete to unregister
509
- callback && callback(request);
473
+ // callback can be null since we use delete to unregister
474
+ for (const callback of callbacks) callback && callback(request);
510
475
  }
511
- }
476
+ }