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.
- package/AGENTS.md +101 -0
- package/agents/codex/AGENTS.md +95 -0
- package/agents/codex/CODING_STYLE.md +71 -0
- package/agents/codex/agents.md.zip +0 -0
- package/agents/codex/client/AGENTS.md +102 -0
- package/agents/codex/client/pages/AGENTS.md +35 -0
- package/agents/codex/server/routes/AGENTS.md +12 -0
- package/agents/codex/server/services/AGENTS.md +137 -0
- package/agents/codex/tests/AGENTS.md +8 -0
- package/cli/app/config.ts +13 -11
- package/cli/app/index.ts +74 -82
- package/cli/bin.js +1 -1
- package/cli/commands/build.ts +51 -14
- package/cli/commands/check.ts +19 -0
- package/cli/commands/deploy/app.ts +4 -8
- package/cli/commands/deploy/web.ts +16 -20
- package/cli/commands/dev.ts +189 -64
- package/cli/commands/devEvents.ts +106 -0
- package/cli/commands/init.ts +63 -57
- package/cli/commands/lint.ts +21 -0
- package/cli/commands/refresh.ts +18 -0
- package/cli/commands/typecheck.ts +18 -0
- package/cli/compiler/client/identite.ts +80 -53
- package/cli/compiler/client/index.ts +139 -213
- package/cli/compiler/common/bundleAnalysis.ts +94 -0
- package/cli/compiler/common/clientManifest.ts +67 -0
- package/cli/compiler/common/controllers.ts +288 -0
- package/cli/compiler/common/files/autres.ts +7 -18
- package/cli/compiler/common/files/images.ts +40 -37
- package/cli/compiler/common/files/style.ts +11 -22
- package/cli/compiler/common/generatedRouteModules.ts +368 -0
- package/cli/compiler/common/index.ts +31 -65
- package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
- package/cli/compiler/common/rspackAliases.ts +13 -0
- package/cli/compiler/common/scripts.ts +37 -0
- package/cli/compiler/index.ts +781 -230
- package/cli/compiler/server/index.ts +59 -75
- package/cli/compiler/writeIfChanged.ts +21 -0
- package/cli/index.ts +71 -72
- package/cli/paths.ts +51 -57
- package/cli/print.ts +17 -11
- package/cli/tsconfig.json +5 -4
- package/cli/utils/agents.ts +100 -0
- package/cli/utils/check.ts +71 -0
- package/cli/utils/index.ts +1 -3
- package/cli/utils/keyboard.ts +8 -25
- package/cli/utils/runProcess.ts +30 -0
- package/client/app/component.tsx +29 -29
- package/client/app/index.ts +36 -57
- package/client/app/service.ts +7 -12
- package/client/app.tsconfig.json +2 -2
- package/client/components/Dialog/Manager.ssr.tsx +40 -0
- package/client/components/Dialog/Manager.tsx +119 -150
- package/client/components/Dialog/status.tsx +3 -3
- package/client/components/index.ts +1 -1
- package/client/components/types.d.ts +1 -3
- package/client/dev/hmr.ts +65 -0
- package/client/global.d.ts +2 -2
- package/client/hooks.ts +6 -9
- package/client/index.ts +2 -1
- package/client/islands/index.ts +7 -0
- package/client/islands/useDeferredModule.ts +199 -0
- package/client/pages/_layout/index.tsx +4 -12
- package/client/pages/useHeader.tsx +14 -21
- package/client/router.ts +27 -0
- package/client/services/router/components/Link.tsx +34 -27
- package/client/services/router/components/Page.tsx +6 -14
- package/client/services/router/components/router.ssr.tsx +36 -0
- package/client/services/router/components/router.tsx +63 -83
- package/client/services/router/index.tsx +185 -220
- package/client/services/router/request/api.ts +97 -119
- package/client/services/router/request/history.ts +2 -2
- package/client/services/router/request/index.ts +13 -12
- package/client/services/router/request/multipart.ts +72 -62
- package/client/services/router/response/index.tsx +68 -61
- package/client/services/router/response/page.ts +28 -32
- package/client/utils/dom.ts +17 -33
- package/common/app/index.ts +3 -3
- package/common/data/chaines/index.ts +22 -23
- package/common/data/dates.ts +35 -70
- package/common/data/markdown.ts +42 -39
- package/common/dev/serverHotReload.ts +26 -0
- package/common/errors/index.tsx +110 -142
- package/common/router/contracts.ts +29 -0
- package/common/router/index.ts +89 -108
- package/common/router/layouts.ts +34 -47
- package/common/router/pageSetup.ts +50 -0
- package/common/router/register.ts +53 -24
- package/common/router/request/api.ts +30 -36
- package/common/router/request/index.ts +2 -8
- package/common/router/response/index.ts +8 -15
- package/common/router/response/page.ts +70 -58
- package/common/utils.ts +1 -1
- package/doc/TODO.md +1 -1
- package/eslint.js +62 -0
- package/package.json +14 -49
- package/prettier.config.cjs +9 -0
- package/scripts/cleanup-generated-controllers.ts +62 -0
- package/scripts/fix-reference-app-typing.ts +490 -0
- package/scripts/refactor-client-app-imports.ts +244 -0
- package/scripts/refactor-client-pages.ts +587 -0
- package/scripts/refactor-server-controllers.ts +470 -0
- package/scripts/refactor-server-runtime-aliases.ts +360 -0
- package/scripts/restore-client-app-import-files.ts +41 -0
- package/scripts/restore-files-from-git-head.ts +20 -0
- package/scripts/update-codex-agents.ts +35 -0
- package/server/app/commands.ts +35 -64
- package/server/app/container/config.ts +48 -59
- package/server/app/container/console/index.ts +202 -248
- package/server/app/container/index.ts +33 -71
- package/server/app/controller/index.ts +61 -0
- package/server/app/index.ts +39 -105
- package/server/app/service/container.ts +41 -42
- package/server/app/service/index.ts +120 -147
- package/server/context.ts +1 -1
- package/server/index.ts +25 -1
- package/server/services/auth/index.ts +75 -115
- package/server/services/auth/router/index.ts +31 -32
- package/server/services/auth/router/request.ts +14 -16
- package/server/services/cron/CronTask.ts +13 -26
- package/server/services/cron/index.ts +14 -36
- package/server/services/disks/driver.ts +40 -58
- package/server/services/disks/drivers/local/index.ts +79 -90
- package/server/services/disks/drivers/s3/index.ts +116 -163
- package/server/services/disks/index.ts +23 -38
- package/server/services/email/index.ts +45 -104
- package/server/services/email/utils.ts +14 -27
- package/server/services/fetch/index.ts +53 -85
- package/server/services/prisma/Facet.ts +39 -91
- package/server/services/prisma/index.ts +74 -110
- package/server/services/router/generatedRuntime.ts +29 -0
- package/server/services/router/http/index.ts +78 -73
- package/server/services/router/http/multipart.ts +19 -42
- package/server/services/router/index.ts +378 -365
- package/server/services/router/request/api.ts +26 -25
- package/server/services/router/request/index.ts +44 -51
- package/server/services/router/request/service.ts +7 -11
- package/server/services/router/request/validation/zod.ts +111 -148
- package/server/services/router/response/index.ts +110 -125
- package/server/services/router/response/mask/Filter.ts +31 -72
- package/server/services/router/response/mask/index.ts +8 -15
- package/server/services/router/response/mask/selecteurs.ts +11 -25
- package/server/services/router/response/page/clientManifest.ts +25 -0
- package/server/services/router/response/page/document.tsx +199 -127
- package/server/services/router/response/page/index.tsx +89 -94
- package/server/services/router/service.ts +13 -15
- package/server/services/schema/index.ts +17 -26
- package/server/services/schema/request.ts +19 -33
- package/server/services/schema/router/index.ts +8 -11
- package/server/services/security/encrypt/aes/index.ts +15 -35
- package/server/utils/slug.ts +29 -35
- package/skills/clean-project-code/SKILL.md +63 -0
- package/skills/clean-project-code/agents/openai.yaml +4 -0
- package/tsconfig.common.json +4 -3
- package/tsconfig.json +4 -1
- package/types/aliases.d.ts +17 -21
- package/types/controller-input.test.ts +48 -0
- package/types/express-extra.d.ts +6 -0
- package/types/global/constants.d.ts +13 -0
- package/types/global/express-extra.d.ts +6 -0
- package/types/global/modules.d.ts +13 -16
- package/types/global/utils.d.ts +17 -49
- package/types/global/vendors.d.ts +62 -0
- package/types/icons.d.ts +65 -1
- package/types/uuid.d.ts +3 -0
- package/types/vendors.d.ts +62 -0
- package/cli/compiler/common/babel/index.ts +0 -170
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +0 -586
- package/cli/compiler/common/babel/routes/imports.ts +0 -127
- package/cli/compiler/common/babel/routes/routes.ts +0 -1130
- package/client/services/captcha/index.ts +0 -67
- package/client/services/socket/index.ts +0 -147
- package/common/data/rte/nodes.ts +0 -83
- package/common/data/stats.ts +0 -90
- package/common/utils/rte.ts +0 -183
- package/server/services/auth/old.ts +0 -277
- package/server/services/cache/commands.ts +0 -41
- package/server/services/cache/index.ts +0 -297
- package/server/services/cache/service.json +0 -6
- package/server/services/socket/index.ts +0 -162
- package/server/services/socket/scope.ts +0 -226
- package/server/services/socket/service.json +0 -6
- package/server/services_old/SocketClient.ts +0 -92
- 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,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
62
|
+
export type { default as ClientResponse, TRouterContext } from './response';
|
|
63
|
+
|
|
64
|
+
export type TAnyClientRouter = ClientRouter<any, any>;
|
|
58
65
|
|
|
59
|
-
export type Router =
|
|
66
|
+
export type Router = TAnyClientRouter | TAnyRouter;
|
|
60
67
|
|
|
61
|
-
export type Request = ClientRequest<
|
|
68
|
+
export type Request = ClientRequest<TAnyClientRouter> | ServerRequest<TAnyRouter>;
|
|
62
69
|
|
|
63
|
-
export type Response = ClientResponse<
|
|
70
|
+
export type Response = ClientResponse<TAnyClientRouter> | ServerResponse<TAnyRouter>;
|
|
64
71
|
|
|
65
72
|
/*----------------------------------
|
|
66
73
|
- TYPES: ROUTES LOADING
|
|
67
74
|
----------------------------------*/
|
|
68
75
|
|
|
69
|
-
// WARN:
|
|
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<
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
index: number;
|
|
99
|
+
chunk: string;
|
|
100
|
+
regex: RegExp;
|
|
101
|
+
keys: (number | string)[];
|
|
102
|
+
load: TRouteLoader<TClientPageRoute>;
|
|
103
|
+
};
|
|
111
104
|
|
|
112
|
-
type TRouteLoader<
|
|
105
|
+
type TRouteLoader<
|
|
106
|
+
Route extends TClientPageRoute | TClientPageErrorRoute = TClientPageRoute | TClientPageErrorRoute,
|
|
107
|
+
> = () => Promise<
|
|
108
|
+
TRouteModule<Route>
|
|
109
|
+
>;
|
|
113
110
|
|
|
114
|
-
export type
|
|
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
|
|
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
|
|
129
|
-
preload: string[]
|
|
130
|
-
context: (context: {}, router:
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
>
|
|
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 =
|
|
143
|
-
public ssrContext =
|
|
144
|
-
public domains = window
|
|
145
|
-
public context!:
|
|
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
|
|
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(
|
|
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
|
-
|
|
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:
|
|
194
|
-
public errors: {
|
|
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(
|
|
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
|
|
240
|
+
public page<TProvidedData extends {} = {}>(
|
|
254
241
|
path: string,
|
|
255
|
-
renderer: TFrontRenderer<TProvidedData
|
|
256
|
-
):
|
|
242
|
+
renderer: TFrontRenderer<TProvidedData>,
|
|
243
|
+
): TClientPageRoute<this>;
|
|
257
244
|
|
|
258
|
-
public page<TProvidedData extends
|
|
245
|
+
public page<TProvidedData extends {} = {}>(
|
|
259
246
|
path: string,
|
|
260
|
-
|
|
261
|
-
renderer: TFrontRenderer<TProvidedData
|
|
262
|
-
):
|
|
247
|
+
setup: TPageSetup<TProvidedData>,
|
|
248
|
+
renderer: TFrontRenderer<TProvidedData>,
|
|
249
|
+
): TClientPageRoute<this>;
|
|
263
250
|
|
|
264
|
-
public page
|
|
251
|
+
public page<TProvidedData extends {} = {}>(
|
|
252
|
+
path: string,
|
|
253
|
+
options: Partial<TRouteOptions>,
|
|
254
|
+
renderer: TFrontRenderer<TProvidedData>,
|
|
255
|
+
): TClientPageRoute<this>;
|
|
265
256
|
|
|
266
|
-
|
|
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
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
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:
|
|
273
|
+
const route: TClientPageRoute<this> = {
|
|
276
274
|
method: 'GET',
|
|
277
275
|
path,
|
|
278
276
|
regex,
|
|
279
277
|
keys,
|
|
280
|
-
options: {
|
|
281
|
-
|
|
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<
|
|
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,
|
|
295
|
+
const layout = getLayout('Error ' + code, finalOptions);
|
|
300
296
|
|
|
301
|
-
const route:
|
|
297
|
+
const route: TClientPageErrorRoute<this> = {
|
|
302
298
|
code,
|
|
303
|
-
controller: (context
|
|
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<
|
|
345
|
-
private async load(route: TUnresolvedErrorRoute): Promise<
|
|
346
|
-
private async load(
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
345
|
+
debug && console.log(`Route fetched: ${route.chunk}`, fetched);
|
|
369
346
|
|
|
370
|
-
|
|
371
|
-
fetched = route.load;
|
|
347
|
+
if ('code' in route) return fetched as TClientPageErrorRoute<this>;
|
|
372
348
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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 |
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
508
|
-
|
|
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
|
+
}
|