proteum 1.0.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.dockerignore +10 -0
  2. package/Rte.zip +0 -0
  3. package/cli/app/config.ts +54 -0
  4. package/cli/app/index.ts +195 -0
  5. package/cli/bin.js +11 -0
  6. package/cli/commands/build.ts +34 -0
  7. package/cli/commands/deploy/app.ts +29 -0
  8. package/cli/commands/deploy/web.ts +60 -0
  9. package/cli/commands/dev.ts +109 -0
  10. package/cli/commands/init.ts +85 -0
  11. package/cli/compiler/client/identite.ts +72 -0
  12. package/cli/compiler/client/index.ts +334 -0
  13. package/cli/compiler/common/babel/index.ts +170 -0
  14. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  15. package/cli/compiler/common/babel/plugins/services.ts +579 -0
  16. package/cli/compiler/common/babel/routes/imports.ts +127 -0
  17. package/cli/compiler/common/babel/routes/routes.ts +1130 -0
  18. package/cli/compiler/common/files/autres.ts +39 -0
  19. package/cli/compiler/common/files/images.ts +35 -0
  20. package/cli/compiler/common/files/style.ts +78 -0
  21. package/cli/compiler/common/index.ts +154 -0
  22. package/cli/compiler/index.ts +532 -0
  23. package/cli/compiler/server/index.ts +211 -0
  24. package/cli/index.ts +189 -0
  25. package/cli/paths.ts +165 -0
  26. package/cli/print.ts +12 -0
  27. package/cli/tsconfig.json +38 -0
  28. package/cli/utils/index.ts +22 -0
  29. package/cli/utils/keyboard.ts +78 -0
  30. package/client/app/component.tsx +54 -0
  31. package/client/app/index.ts +142 -0
  32. package/client/app/service.ts +34 -0
  33. package/client/app.tsconfig.json +28 -0
  34. package/client/components/Button.tsx +298 -0
  35. package/client/components/Dialog/Manager.tsx +309 -0
  36. package/client/components/Dialog/card.tsx +208 -0
  37. package/client/components/Dialog/index.less +151 -0
  38. package/client/components/Dialog/status.less +176 -0
  39. package/client/components/Dialog/status.tsx +48 -0
  40. package/client/components/index.ts +2 -0
  41. package/client/components/types.d.ts +3 -0
  42. package/client/data/input.ts +32 -0
  43. package/client/global.d.ts +5 -0
  44. package/client/hooks.ts +22 -0
  45. package/client/index.ts +6 -0
  46. package/client/pages/_layout/index.less +6 -0
  47. package/client/pages/_layout/index.tsx +43 -0
  48. package/client/pages/bug.tsx.old +60 -0
  49. package/client/pages/useHeader.tsx +50 -0
  50. package/client/services/captcha/index.ts +67 -0
  51. package/client/services/router/components/Link.tsx +46 -0
  52. package/client/services/router/components/Page.tsx +55 -0
  53. package/client/services/router/components/router.tsx +218 -0
  54. package/client/services/router/index.tsx +521 -0
  55. package/client/services/router/request/api.ts +267 -0
  56. package/client/services/router/request/history.ts +5 -0
  57. package/client/services/router/request/index.ts +53 -0
  58. package/client/services/router/request/multipart.ts +147 -0
  59. package/client/services/router/response/index.tsx +128 -0
  60. package/client/services/router/response/page.ts +86 -0
  61. package/client/services/socket/index.ts +147 -0
  62. package/client/utils/dom.ts +77 -0
  63. package/common/app/index.ts +9 -0
  64. package/common/data/chaines/index.ts +54 -0
  65. package/common/data/dates.ts +179 -0
  66. package/common/data/markdown.ts +73 -0
  67. package/common/data/rte/nodes.ts +83 -0
  68. package/common/data/stats.ts +90 -0
  69. package/common/errors/index.tsx +326 -0
  70. package/common/router/index.ts +213 -0
  71. package/common/router/layouts.ts +93 -0
  72. package/common/router/register.ts +55 -0
  73. package/common/router/request/api.ts +77 -0
  74. package/common/router/request/index.ts +35 -0
  75. package/common/router/response/index.ts +45 -0
  76. package/common/router/response/page.ts +128 -0
  77. package/common/utils/rte.ts +183 -0
  78. package/common/utils.ts +7 -0
  79. package/doc/TODO.md +71 -0
  80. package/doc/front/router.md +27 -0
  81. package/doc/workspace/workspace.png +0 -0
  82. package/doc/workspace/workspace2.png +0 -0
  83. package/doc/workspace/workspace_26.01.22.png +0 -0
  84. package/package.json +171 -0
  85. package/server/app/commands.ts +141 -0
  86. package/server/app/container/config.ts +203 -0
  87. package/server/app/container/console/index.ts +550 -0
  88. package/server/app/container/index.ts +137 -0
  89. package/server/app/index.ts +273 -0
  90. package/server/app/service/container.ts +88 -0
  91. package/server/app/service/index.ts +235 -0
  92. package/server/app.tsconfig.json +28 -0
  93. package/server/context.ts +4 -0
  94. package/server/index.ts +4 -0
  95. package/server/services/auth/index.ts +250 -0
  96. package/server/services/auth/old.ts +277 -0
  97. package/server/services/auth/router/index.ts +95 -0
  98. package/server/services/auth/router/request.ts +54 -0
  99. package/server/services/auth/router/service.json +6 -0
  100. package/server/services/auth/service.json +6 -0
  101. package/server/services/cache/commands.ts +41 -0
  102. package/server/services/cache/index.ts +297 -0
  103. package/server/services/cache/service.json +6 -0
  104. package/server/services/cron/CronTask.ts +86 -0
  105. package/server/services/cron/index.ts +112 -0
  106. package/server/services/cron/service.json +6 -0
  107. package/server/services/disks/driver.ts +103 -0
  108. package/server/services/disks/drivers/local/index.ts +188 -0
  109. package/server/services/disks/drivers/local/service.json +6 -0
  110. package/server/services/disks/drivers/s3/index.ts +301 -0
  111. package/server/services/disks/drivers/s3/service.json +6 -0
  112. package/server/services/disks/index.ts +90 -0
  113. package/server/services/disks/service.json +6 -0
  114. package/server/services/email/index.ts +188 -0
  115. package/server/services/email/utils.ts +53 -0
  116. package/server/services/fetch/index.ts +201 -0
  117. package/server/services/fetch/service.json +7 -0
  118. package/server/services/models.7z +0 -0
  119. package/server/services/prisma/Facet.ts +142 -0
  120. package/server/services/prisma/index.ts +201 -0
  121. package/server/services/prisma/service.json +6 -0
  122. package/server/services/router/http/index.ts +217 -0
  123. package/server/services/router/http/multipart.ts +102 -0
  124. package/server/services/router/http/session.ts.old +40 -0
  125. package/server/services/router/index.ts +801 -0
  126. package/server/services/router/request/api.ts +87 -0
  127. package/server/services/router/request/index.ts +184 -0
  128. package/server/services/router/request/service.ts +21 -0
  129. package/server/services/router/request/validation/zod.ts +180 -0
  130. package/server/services/router/response/index.ts +338 -0
  131. package/server/services/router/response/mask/Filter.ts +323 -0
  132. package/server/services/router/response/mask/index.ts +60 -0
  133. package/server/services/router/response/mask/selecteurs.ts +92 -0
  134. package/server/services/router/response/page/document.tsx +160 -0
  135. package/server/services/router/response/page/index.tsx +196 -0
  136. package/server/services/router/service.json +6 -0
  137. package/server/services/router/service.ts +36 -0
  138. package/server/services/schema/index.ts +44 -0
  139. package/server/services/schema/request.ts +49 -0
  140. package/server/services/schema/router/index.ts +28 -0
  141. package/server/services/schema/router/service.json +6 -0
  142. package/server/services/schema/service.json +6 -0
  143. package/server/services/security/encrypt/aes/index.ts +85 -0
  144. package/server/services/security/encrypt/aes/service.json +6 -0
  145. package/server/services/socket/index.ts +162 -0
  146. package/server/services/socket/scope.ts +226 -0
  147. package/server/services/socket/service.json +6 -0
  148. package/server/services_old/SocketClient.ts +92 -0
  149. package/server/services_old/Token.old.ts +97 -0
  150. package/server/utils/slug.ts +79 -0
  151. package/tsconfig.common.json +45 -0
  152. package/tsconfig.json +3 -0
  153. package/types/aliases.d.ts +54 -0
  154. package/types/global/modules.d.ts +49 -0
  155. package/types/global/utils.d.ts +103 -0
  156. package/types/icons.d.ts +1 -0
@@ -0,0 +1,267 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Core
6
+ import type ClientApplication from '@client/app';
7
+ import { fromJson as errorFromJson, NetworkError } from '@common/errors';
8
+ import ApiClientService, {
9
+ TPostData, TPostDataWithoutFile,
10
+ TApiFetchOptions, TFetcherList, TFetcherArgs, TFetcher,
11
+ TDataReturnedByFetchers
12
+ } from '@common/router/request/api';
13
+
14
+
15
+ // Specific
16
+ import type { default as Router, Request } from '..';
17
+ import { toMultipart } from './multipart';
18
+
19
+ /*----------------------------------
20
+ - TYPES
21
+ ----------------------------------*/
22
+
23
+ const debug = false;
24
+
25
+ export type Config = {
26
+
27
+ }
28
+
29
+ /*----------------------------------
30
+ - FUNCTION
31
+ ----------------------------------*/
32
+ export default class ApiClient implements ApiClientService {
33
+
34
+ // APO Client needs to know the current request so we can monitor which api request is made from which page
35
+ public constructor(
36
+ public app: ClientApplication,
37
+ public request: Request,
38
+ public router = request.router,
39
+ ) {
40
+
41
+ }
42
+
43
+ /*----------------------------------
44
+ - HIGH LEVEL
45
+ ----------------------------------*/
46
+
47
+ public fetch<FetchersList extends TFetcherList = TFetcherList>(
48
+ fetchers: FetchersList
49
+ ): TDataReturnedByFetchers<FetchersList> {
50
+ throw new Error("api.fetch shouldn't be called here.");
51
+ }
52
+
53
+ public post = <TData extends unknown = unknown>(path: string, data?: TPostData, opts?: TApiFetchOptions) =>
54
+ this.createFetcher<TData>('POST', path, data, opts);
55
+
56
+ public set( newData: TObjetDonnees ) {
57
+
58
+ if (!('context' in this.router))
59
+ throw new Error("api.set is not available on server side.");
60
+
61
+ if (this.router.context.page)
62
+ this.router.context.page.setAllData(curData => ({ ...curData, ...newData }));
63
+ else
64
+ throw new Error(`[api] this.router.context.page undefined`)
65
+ }
66
+
67
+ public reload( ids?: string | string[], params?: TObjetDonnees ) {
68
+
69
+ if (!('context' in this.router))
70
+ throw new Error("api.reload is not available on server side.");
71
+
72
+ const page = this.router.context.page;
73
+
74
+ if (ids === undefined)
75
+ ids = Object.keys(page.fetchers);
76
+ else if (typeof ids === 'string')
77
+ ids = [ids];
78
+
79
+ debug && console.log("[api] Reload data", ids, params, page.fetchers);
80
+
81
+ for (const id of ids) {
82
+
83
+ const fetcher = page.fetchers[id];
84
+ if (fetcher === undefined)
85
+ return console.error(`Unable to reload ${id}: Request not found in fetchers list.`);
86
+
87
+ if (params !== undefined)
88
+ fetcher.data = { ...(fetcher.data || {}), ...params };
89
+
90
+ debug && console.log("[api][reload]", id, fetcher.method, fetcher.path, fetcher.data);
91
+
92
+ this.fetchAsync(fetcher.method, fetcher.path, fetcher.data).then((data) => {
93
+
94
+ this.set({ [id]: data });
95
+
96
+ })
97
+ }
98
+ }
99
+
100
+ /*----------------------------------
101
+ - LOW LEVEL
102
+ ----------------------------------*/
103
+ public createFetcher<TData extends unknown = unknown>(...args: TFetcherArgs): TFetcher<TData> {
104
+ const [method, path, data, options] = args;
105
+
106
+ // Lazily create (and cache) the underlying promise so the fetcher behaves like a real promise instance.
107
+ let promise: Promise<TData> | undefined;
108
+
109
+ const fetcher = {
110
+ method, path, data, options,
111
+ } as TFetcher<TData>;
112
+
113
+ const getPromise = () => {
114
+ if (!promise)
115
+ promise = this.fetchAsync<TData>(fetcher.method, fetcher.path, fetcher.data, fetcher.options);
116
+
117
+ return promise;
118
+ };
119
+
120
+ // For async calls: api.post(...).then((data) => ...)
121
+ fetcher.then = (onfulfilled?: any, onrejected?: any) =>
122
+ getPromise().then(onfulfilled, onrejected) as any;
123
+
124
+ fetcher.catch = (onrejected?: any) =>
125
+ getPromise().catch(onrejected) as any;
126
+
127
+ fetcher.finally = (onfinally?: any) =>
128
+ getPromise().finally(onfinally) as any;
129
+
130
+ fetcher.run = () => getPromise();
131
+
132
+ return fetcher;
133
+ }
134
+
135
+ public async fetchAsync<TData extends unknown = unknown>(...[
136
+ method, path, data, options
137
+ ]: TFetcherArgs): Promise<TData> {
138
+
139
+ /*if (options?.captcha !== undefined)
140
+ await this.gui.captcha.check(options?.captcha);*/
141
+
142
+ return await this.execute<TData>(method, path, data, options);
143
+ }
144
+
145
+ public async fetchSync(fetchers: TFetcherList, alreadyLoadedData: {}): Promise<TObjetDonnees> {
146
+
147
+ // Pick the fetchers where the data is needed
148
+ const fetchersToRun: TFetcherList = {};
149
+ let fetchersCount: number = 0;
150
+ for (const fetcherId in fetchers)
151
+ // The fetcher can be undefined
152
+ if (!( fetcherId in alreadyLoadedData ) && fetchers[ fetcherId ]) {
153
+ fetchersToRun[ fetcherId ] = fetchers[ fetcherId ]
154
+ fetchersCount++;
155
+ }
156
+
157
+ // Fetch all the api data thanks to one http request
158
+ const fetchedData = fetchersCount === 0
159
+ ? 0
160
+ : await this.execute("POST", "/api", {
161
+ fetchers: fetchersToRun
162
+ }).then((res) => {
163
+
164
+ const data: TObjetDonnees = {};
165
+ for (const id in res)
166
+ data[id] = res[id];
167
+
168
+ return data;
169
+
170
+ }).catch(e => {
171
+
172
+ // API Error hook
173
+ this.app.handleError(e);
174
+
175
+ throw e;
176
+ })
177
+
178
+ // Errors will be catched in the caller
179
+
180
+ return { ...alreadyLoadedData, ...fetchedData }
181
+ }
182
+
183
+ public configure = (...[method, path, data, options = {}]: TFetcherArgs) => {
184
+
185
+ let url = this.router.url(path, {}, false);
186
+
187
+ debug && console.log(`[api] Sending request`, method, url, data);
188
+
189
+ // Create Fetch config
190
+ const config: With<RequestInit, 'headers'> = {
191
+ method: method,
192
+ headers: {
193
+ 'Accept': "application/json",
194
+ }
195
+ };
196
+
197
+ // Update options depending on data
198
+ if (data) {
199
+
200
+ // If file included in data, need to use multipart
201
+ // TODO: deep check
202
+ const hasFile = Object.values(data).some((value) => value instanceof File);
203
+ if (hasFile) {
204
+ // GET request = Can't send files
205
+ if (method === "GET")
206
+ throw new Error("Cannot send file in GET request");
207
+ // Auto switch to multiplart
208
+ else if (options.encoding === undefined)
209
+ options.encoding = 'multipart';
210
+ else if (options.encoding !== 'multipart')
211
+ // Encoding set to JSON = Can't send files
212
+ throw new Error("Cannot send file in non-multipart request");
213
+ }
214
+
215
+ // Data encoding
216
+ if (method === "GET") {
217
+
218
+ const params = new URLSearchParams( data as unknown as TPostDataWithoutFile ).toString();
219
+ url = `${url}?${params}`;
220
+
221
+ } else if (options.encoding === 'multipart') {
222
+
223
+ debug && console.log("[api] Multipart request", data);
224
+ // Browser will automatically choose the right headers
225
+ config.body = toMultipart(data);
226
+
227
+ } else {
228
+ config.headers["Content-Type"] = "application/json";
229
+ config.body = JSON.stringify(data);
230
+ }
231
+ }
232
+
233
+ return { url, config };
234
+ }
235
+
236
+ public execute<TData = unknown>(...args: TFetcherArgs): Promise<TData> {
237
+ const { url, config } = this.configure(...args);
238
+
239
+ console.log(`[api] Fetching`, url, config);
240
+
241
+ return fetch(url, config)
242
+ .then(async (response) => {
243
+ if (!response.ok) {
244
+
245
+ const errorData = await response.json();
246
+ console.warn(`[api] Failure:`, response.status, errorData);
247
+ const error = errorFromJson(errorData);
248
+ throw error;
249
+ }
250
+ const json = await response.json() as TData;
251
+ debug && console.log(`[api] Success:`, json);
252
+ return json;
253
+ })
254
+ .catch((error) => {
255
+ if (error instanceof TypeError) {
256
+ // Network error
257
+ console.warn(`[api] Network Failure:`, error);
258
+ const networkError = new NetworkError(error.message);
259
+ this.app.handleError(networkError);
260
+ throw networkError;
261
+ } else {
262
+ throw error;
263
+ }
264
+ });
265
+ }
266
+
267
+ }
@@ -0,0 +1,5 @@
1
+ import { createBrowserHistory } from 'history';
2
+ export type { Update } from 'history';
3
+
4
+ export const history = (typeof window !== 'undefined') ? createBrowserHistory() : undefined;
5
+ export const location = history?.location;
@@ -0,0 +1,53 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import { Location } from 'history';
7
+
8
+ // Core
9
+ import BaseRequest from '@common/router/request';
10
+
11
+ // Specific
12
+ import type ClientRouter from '..';
13
+ import ApiClient from './api';
14
+ import type ClientResponse from '../response';
15
+
16
+ /*----------------------------------
17
+ - TYPES
18
+ ----------------------------------*/
19
+
20
+
21
+ /*----------------------------------
22
+ - ROUTER
23
+ ----------------------------------*/
24
+ // Since we do SSR, the server router can also be passed here
25
+ export default class ClientRequest<TRouter extends ClientRouter = ClientRouter> extends BaseRequest {
26
+
27
+ public api: ApiClient;
28
+ public response?: ClientResponse<TRouter>;
29
+
30
+ public hash?: string;
31
+
32
+ public constructor(
33
+ location: Location,
34
+ public router: TRouter,
35
+ public app = router.app
36
+ ) {
37
+
38
+ super(location.pathname);
39
+
40
+ this.host = window.location.host;
41
+ this.url = window.location.protocol + '//' + window.location.host + this.path;
42
+ this.hash = location.hash;
43
+
44
+ // Extract search params
45
+ if (location.search) {
46
+ this.url += location.search;
47
+ this.data = Object.fromEntries( new URLSearchParams( location.search ));
48
+ }
49
+
50
+ // Request services
51
+ this.api = new ApiClient(this.app, this);
52
+ }
53
+ }
@@ -0,0 +1,147 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Core
6
+ import { TPostData } from '@common/router/request/api';
7
+
8
+ /*----------------------------------
9
+ - TYPES
10
+ ----------------------------------*/
11
+
12
+ function mergeObjects(object1, object2) {
13
+ return [object1, object2].reduce(function (carry, objectToMerge) {
14
+ Object.keys(objectToMerge).forEach(function (objectKey) {
15
+ carry[objectKey] = objectToMerge[objectKey];
16
+ });
17
+ return carry;
18
+ }, {});
19
+ }
20
+
21
+ function isArray(val) {
22
+
23
+ return ({}).toString.call(val) === '[object Array]';
24
+ }
25
+
26
+ function isJsonObject(val) {
27
+
28
+ return !isArray(val) && typeof val === 'object' && !!val && !(val instanceof Blob) && !(val instanceof Date);
29
+ }
30
+
31
+ function isAppendFunctionPresent(formData) {
32
+
33
+ return typeof formData.append === 'function';
34
+ }
35
+
36
+ function isGlobalFormDataPresent() {
37
+
38
+ return typeof FormData === 'function';
39
+ }
40
+
41
+ function getDefaultFormData() {
42
+
43
+ if (isGlobalFormDataPresent()) {
44
+ return new FormData();
45
+ }
46
+ }
47
+
48
+ function convertRecursively(
49
+ jsonObject: {},
50
+ options: TOptions,
51
+ formData: FormData,
52
+ parentKey: string
53
+ ) {
54
+
55
+ var index = 0;
56
+
57
+ for (var key in jsonObject) {
58
+
59
+ if (jsonObject.hasOwnProperty(key)) {
60
+
61
+ var propName = parentKey || key;
62
+ var value = options.mapping(jsonObject[key]);
63
+
64
+ if (parentKey && isJsonObject(jsonObject)) {
65
+ propName = parentKey + '[' + key + ']';
66
+ }
67
+
68
+ if (parentKey && isArray(jsonObject)) {
69
+
70
+ if (isArray(value) || options.showLeafArrayIndexes ) {
71
+ propName = parentKey + '[' + index + ']';
72
+ } else {
73
+ propName = parentKey + '[]';
74
+ }
75
+ }
76
+
77
+ if (isArray(value) || isJsonObject(value)) {
78
+
79
+ convertRecursively(value, options, formData, propName);
80
+
81
+ } else if (value instanceof FileList) {
82
+
83
+ for (var j = 0; j < value.length; j++) {
84
+ formData.append(propName + '[' + j + ']', value.item(j));
85
+ }
86
+ } else if (value instanceof Blob) {
87
+
88
+ formData.append(propName, value, value.name);
89
+
90
+ } else if (value instanceof Date) {
91
+
92
+ formData.append(propName, value.toISOString());
93
+
94
+ } else if (((value === null && options.includeNullValues) || value !== null) && value !== undefined) {
95
+
96
+ formData.append(propName, value);
97
+ }
98
+ }
99
+ index++;
100
+ }
101
+ return formData;
102
+ }
103
+
104
+ /*----------------------------------
105
+ - UTILS
106
+ ----------------------------------*/
107
+ /* Based on https://github.com/hyperatom/json-form-data
108
+ Changes:
109
+ - Add support for FileToUpload
110
+ */
111
+
112
+ // options type
113
+ type TOptions = {
114
+ initialFormData: FormData,
115
+ showLeafArrayIndexes: boolean,
116
+ includeNullValues: boolean,
117
+ mapping: (value: any) => any
118
+ }
119
+
120
+ export const toMultipart = (jsonObject: TPostData, options?: TOptions) => {
121
+
122
+ if (options && options.initialFormData) {
123
+
124
+ if (!isAppendFunctionPresent(options.initialFormData)) {
125
+ throw 'initialFormData must have an append function.';
126
+ }
127
+ } else if (!isGlobalFormDataPresent()) {
128
+
129
+ throw 'This environment does not have global form data. options.initialFormData must be specified.';
130
+ }
131
+
132
+ var defaultOptions = {
133
+ initialFormData: getDefaultFormData(),
134
+ showLeafArrayIndexes: true,
135
+ includeNullValues: false,
136
+ mapping: function(value) {
137
+ if (typeof value === 'boolean') {
138
+ return +value ? '1': '0';
139
+ }
140
+ return value;
141
+ }
142
+ };
143
+
144
+ var mergedOptions = mergeObjects(defaultOptions, options || {});
145
+
146
+ return convertRecursively(jsonObject, mergedOptions, mergedOptions.initialFormData);
147
+ }
@@ -0,0 +1,128 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Libs
6
+ import type ServerRouter from '@server/services/router';
7
+ import type ServerResponse from '@server/services/router/response';
8
+
9
+ import type { TAnyRoute, TErrorRoute } from '@common/router';
10
+ import BaseResponse, { TResponseData } from '@common/router/response';
11
+
12
+ import type ClientApplication from '@client/app';
13
+ import type { default as ClientRouter } from '@client/services/router'
14
+ import type ClientResponse from '@client/services/router/response'
15
+ import ClientRequest from '@client/services/router/request'
16
+ import ClientPage from '@client/services/router/response/page'
17
+ import { history } from '@client/services/router/request/history';
18
+
19
+ /*----------------------------------
20
+ - TYPES
21
+ ----------------------------------*/
22
+
23
+ export type TPageResponse<TRouter extends ClientRouter> = (
24
+ ClientResponse<TRouter, ClientPage>
25
+ |
26
+ ServerResponse<ServerRouter, ClientPage>
27
+ );
28
+
29
+ export type TRouterContext<
30
+ TRouter extends ClientRouter = ClientRouter,
31
+ TApplication extends ClientApplication = ClientApplication
32
+ > = (
33
+ // ClientPage context
34
+ {
35
+ app: TApplication,
36
+ request: ClientRequest<TRouter>,
37
+ route: TAnyRoute<TRouterContext>,
38
+ api: ClientRequest<TRouter>["api"],
39
+ page: ClientPage<TRouter>,
40
+ data: TObjetDonnees
41
+ }
42
+ // Expose client application services (api, socket, ...)
43
+ //TRouter["app"]
44
+ & TApplication
45
+ & ReturnType<TRouter["config"]["context"]>
46
+ )
47
+
48
+ /*----------------------------------
49
+ - ROUTER
50
+ ----------------------------------*/
51
+ export default class ClientPageResponse<
52
+ TRouter extends ClientRouter,
53
+ TData extends TResponseData = TResponseData
54
+ > extends BaseResponse<TData> {
55
+
56
+ public context: TRouterContext<TRouter, TRouter["app"]>;
57
+
58
+ public constructor(
59
+ public request: ClientRequest<TRouter>,
60
+ public route: TAnyRoute | TErrorRoute,
61
+
62
+ public app = request.app,
63
+ ) {
64
+
65
+ super(request);
66
+
67
+ request.response = this;
68
+
69
+ // Create response context for controllers
70
+ this.context = this.createContext();
71
+ }
72
+
73
+ private createContext(): TRouterContext<TRouter, TRouter["app"]> {
74
+
75
+ const basicContext: TRouterContext<TRouter, TRouter["app"]> = {
76
+
77
+ // App services (TODO: expose only services)
78
+ ...this.request.app,
79
+
80
+ // Router context
81
+ app: this.app,
82
+ request: this.request,
83
+ route: this.route,
84
+ api: this.request.api,
85
+ // Will be assigned when the controller will be runned
86
+ page: undefined as unknown as ClientPage<TRouter>,
87
+ data: {},
88
+ }
89
+
90
+ const newContext: TRouterContext<TRouter, TRouter["app"]> = {
91
+ ...basicContext,
92
+ // Custom context
93
+ ...this.request.router.config.context( basicContext, this.request.router )
94
+ }
95
+
96
+ newContext.context = newContext;
97
+
98
+ // Update context object if already exists
99
+ // NOTE: we don't create a nex instance of context because we don't want to rereder the full page (inc layout) to update the context given by thr react context provider
100
+ const existingContext = this.request.router.context;
101
+ if (existingContext === undefined) {
102
+
103
+ this.request.router.context = newContext
104
+
105
+ } else for(const key in newContext)
106
+ existingContext[ key ] = newContext[ key ];
107
+
108
+ return newContext
109
+ }
110
+
111
+ public async runController( additionnalData: {} = {} ): Promise<ClientPage> {
112
+
113
+ // Run contoller
114
+ const result = this.route.controller(this.context);
115
+
116
+ // Default data type for `return <raw data>`
117
+ if (result instanceof ClientPage)
118
+ await result.preRender(additionnalData);
119
+ else
120
+ throw new Error(`Unsupported response format: ${result.constructor?.name}`);
121
+
122
+ return result;
123
+ }
124
+
125
+ public redirect(url: string) {
126
+ history?.replace(url);
127
+ }
128
+ }
@@ -0,0 +1,86 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import type { ComponentChild } from 'preact';
7
+
8
+ // Core
9
+ import type { TClientOrServerContextForPage, Layout, TRoute, TErrorRoute } from '@common/router';
10
+ import PageResponse, { TFrontRenderer } from "@common/router/response/page";
11
+
12
+ // Specific
13
+ import type ClientRouter from '..';
14
+
15
+ /*----------------------------------
16
+ - TYPES
17
+ ----------------------------------*/
18
+
19
+
20
+
21
+ /*----------------------------------
22
+ - CLASS
23
+ ----------------------------------*/
24
+
25
+ export default class ClientPage<TRouter = ClientRouter> extends PageResponse<TRouter> {
26
+
27
+ public scrollToId: string;
28
+
29
+ public constructor(
30
+ public route: TRoute | TErrorRoute,
31
+ public component: TFrontRenderer,
32
+ public context: TClientOrServerContextForPage,
33
+ public layout?: Layout
34
+ ) {
35
+
36
+ super(route, component, context);
37
+
38
+ this.bodyId = context.route.options.bodyId;
39
+ this.scrollToId = context.request.hash;
40
+
41
+ }
42
+
43
+ public async preRender( data?: TObjetDonnees ) {
44
+
45
+ // Add the page to the context
46
+ this.context.page = this;
47
+
48
+ // Data succesfully loaded
49
+ this.context.data = this.data = data || await this.fetchData();
50
+
51
+ return this;
52
+ }
53
+
54
+ /*----------------------------------
55
+ - ACTIONS
56
+ ----------------------------------*/
57
+ // Should be called AFTER rendering the page
58
+ public updateClient() {
59
+
60
+ document.body.id = this.bodyId || this.id;
61
+ document.title = this.title || APP_NAME;
62
+ document.body.className = [...this.bodyClass].join(' ');
63
+
64
+ }
65
+
66
+ public setAllData( callback: (data: {[k: string]: any}) => void) {
67
+ console.warn(`page.setAllData not yet attached to the page Reatc component.`);
68
+ }
69
+ public setData( key: string, value: ((value: any) => void) | any ) {
70
+ this.setAllData(old => ({
71
+ ...old,
72
+ [key]: typeof value === 'function' ? value(old[key]) : value
73
+ }));
74
+ }
75
+
76
+ public setLoading(state: boolean) {
77
+
78
+ if (state === true) {
79
+ if (!document.body.classList.contains("loading"))
80
+ document.body.classList.add("loading");
81
+ } else {
82
+ document.body.classList.remove("loading");
83
+ }
84
+
85
+ }
86
+ }