5htp-core 0.1.2 → 0.2.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 (148) hide show
  1. package/changelog.md +5 -0
  2. package/doc/TODO.md +71 -0
  3. package/package.json +5 -4
  4. package/src/client/{App.tsx → app/component.tsx} +15 -11
  5. package/src/client/app/index.ts +128 -0
  6. package/src/client/app/service.ts +34 -0
  7. package/src/client/app.tsconfig.json +0 -4
  8. package/src/client/assets/css/components.less +52 -0
  9. package/src/client/assets/css/core.less +7 -28
  10. package/src/client/assets/css/theme.less +1 -1
  11. package/src/client/assets/css/{borders.less → utils/borders.less} +0 -0
  12. package/src/client/assets/css/{layouts.less → utils/layouts.less} +0 -0
  13. package/src/client/assets/css/{medias.less → utils/medias.less} +14 -1
  14. package/src/client/assets/css/{sizing.less → utils/sizing.less} +0 -0
  15. package/src/client/assets/css/{spacing.less → utils/spacing.less} +0 -0
  16. package/src/client/components/Card/index.tsx +13 -7
  17. package/src/client/components/Dialog/Manager.tsx +41 -14
  18. package/src/client/components/Dialog/index.less +2 -4
  19. package/src/client/components/Form/index.tsx +1 -1
  20. package/src/client/components/Row/index.less +0 -2
  21. package/src/client/components/Table/index.tsx +3 -2
  22. package/src/client/components/button.tsx +2 -2
  23. package/src/client/components/containers/Popover/index.tsx +1 -1
  24. package/src/client/components/containers/champs.less +0 -2
  25. package/src/client/components/data/spintext/index.tsx +1 -1
  26. package/src/client/components/dropdown/index.tsx +1 -1
  27. package/src/client/components/index.ts +23 -0
  28. package/src/client/components/input/BaseV2/index.less +0 -2
  29. package/src/client/components/input/BaseV2/index.tsx +1 -1
  30. package/src/client/components/input/Date/index.less +0 -2
  31. package/src/client/components/input/Periode/index.less +0 -2
  32. package/src/client/components/input/Radio/index.less +0 -2
  33. package/src/client/components/input/UploadImage/index.less +0 -2
  34. package/src/client/components/input/UploadImage/index.tsx +1 -1
  35. package/src/client/hooks/index.ts +5 -0
  36. package/src/client/hooks/useState/index.tsx +2 -2
  37. package/src/client/hooks.ts +22 -0
  38. package/src/client/index.ts +5 -0
  39. package/src/client/pages/_layout/landing/index.tsx +0 -2
  40. package/src/client/pages/_messages/400.tsx +2 -2
  41. package/src/client/pages/_messages/401.tsx +2 -2
  42. package/src/client/pages/_messages/403.tsx +2 -2
  43. package/src/client/pages/_messages/404.tsx +2 -2
  44. package/src/client/pages/_messages/500.tsx +2 -2
  45. package/src/client/pages/bug.tsx +1 -1
  46. package/src/client/pages/useHeader.tsx +1 -1
  47. package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
  48. package/src/client/services/metrics/index.ts +37 -0
  49. package/src/client/{router → services/router/components}/Link.tsx +1 -1
  50. package/src/client/services/router/components/Page.tsx +59 -0
  51. package/src/client/{router/component.tsx → services/router/components/router.tsx} +52 -74
  52. package/src/client/services/router/index.tsx +453 -0
  53. package/src/client/services/router/request/api.ts +227 -0
  54. package/src/client/{router → services/router}/request/history.ts +0 -0
  55. package/src/client/services/router/request/index.ts +52 -0
  56. package/src/client/services/router/response/index.tsx +107 -0
  57. package/src/client/services/router/response/page.ts +90 -0
  58. package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
  59. package/src/client/utils/dom.ts +1 -1
  60. package/src/common/app/index.ts +9 -0
  61. package/src/common/data/chaines/index.ts +9 -6
  62. package/src/common/data/input/validate.ts +3 -166
  63. package/src/common/data/objets.ts +25 -0
  64. package/src/common/data/tableaux.ts +8 -0
  65. package/src/common/errors/index.ts +3 -1
  66. package/src/common/router/index.ts +67 -88
  67. package/src/common/router/layouts.ts +50 -0
  68. package/src/common/router/register.ts +62 -0
  69. package/src/common/router/request/api.ts +72 -0
  70. package/src/common/router/request/index.ts +31 -0
  71. package/src/common/router/{response.ts → response/index.ts} +9 -13
  72. package/src/common/router/response/page.ts +46 -54
  73. package/src/common/validation/index.ts +3 -0
  74. package/src/common/validation/schema.ts +185 -0
  75. package/src/common/validation/validator.ts +95 -0
  76. package/src/common/validation/validators.ts +313 -0
  77. package/src/server/app/config.ts +9 -27
  78. package/src/server/app/index.ts +81 -124
  79. package/src/server/app/service.ts +98 -0
  80. package/src/server/app.tsconfig.json +0 -8
  81. package/src/server/index.ts +5 -0
  82. package/src/server/patch.ts +0 -6
  83. package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
  84. package/src/server/services/console/bugReporter.ts +26 -16
  85. package/src/server/services/console/index.ts +59 -51
  86. package/src/server/services/cron/index.ts +12 -26
  87. package/src/server/services/database/bucket.ts +40 -0
  88. package/src/server/services/database/connection.ts +213 -80
  89. package/src/server/services/database/datatypes.ts +63 -40
  90. package/src/server/services/database/debug.ts +20 -0
  91. package/src/server/services/database/index.ts +295 -272
  92. package/src/server/services/database/metas.ts +246 -135
  93. package/src/server/services/database/stats.ts +151 -126
  94. package/src/server/services/email/index.ts +30 -62
  95. package/src/server/services/email/transporter.ts +38 -0
  96. package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
  97. package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
  98. package/src/server/services/{http → router/http}/index.ts +28 -70
  99. package/src/server/services/{http → router/http}/multipart.ts +0 -0
  100. package/src/server/services/{http → router/http}/session.ts.old +0 -0
  101. package/src/server/services/router/index.ts +273 -202
  102. package/src/server/services/router/request/api.ts +76 -0
  103. package/src/server/services/router/request/index.ts +16 -97
  104. package/src/server/services/router/request/service.ts +21 -0
  105. package/src/server/services/router/response/index.ts +131 -65
  106. package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
  107. package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
  108. package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
  109. package/src/server/services/router/response/page/document.tsx +194 -0
  110. package/src/server/services/router/response/page/index.tsx +157 -0
  111. package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
  112. package/src/server/services/router/service.ts +48 -0
  113. package/src/server/services/schema/index.ts +47 -0
  114. package/src/server/services/schema/request.ts +55 -0
  115. package/src/server/services/schema/router.ts +33 -0
  116. package/src/server/services/socket/index.ts +38 -43
  117. package/src/server/services/socket/scope.ts +6 -4
  118. package/src/server/services/users/index.ts +203 -0
  119. package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
  120. package/src/server/services/users/router/index.ts +72 -0
  121. package/src/server/services/users/router/request.ts +49 -0
  122. package/src/server/{data → services_old}/SocketClient.ts +0 -0
  123. package/src/server/{data/Token.olg.ts → services_old/Token.old.ts} +0 -0
  124. package/src/server/{data → services_old}/aes.ts +0 -0
  125. package/src/types/aliases.d.ts +43 -2
  126. package/templates/composant.tsx +1 -1
  127. package/templates/modal.tsx +1 -1
  128. package/templates/page.tsx +1 -1
  129. package/tsconfig.common.json +0 -4
  130. package/src/client/assets/css/components/components.less +0 -31
  131. package/src/client/context/api.ts +0 -92
  132. package/src/client/context/index.ts +0 -246
  133. package/src/client/index.tsx +0 -129
  134. package/src/client/router/index.ts +0 -286
  135. package/src/client/router/request/index.ts +0 -106
  136. package/src/client/router/response/index.ts +0 -38
  137. package/src/client/router/route.ts +0 -75
  138. package/src/common/data/input/validators/basic.ts +0 -299
  139. package/src/common/data/input/validators/build.ts +0 -63
  140. package/src/common/router/request.ts +0 -83
  141. package/src/server/data/ApiClient.ts +0 -119
  142. package/src/server/data/input.ts +0 -41
  143. package/src/server/libs/pages/document.static.tsx +0 -41
  144. package/src/server/libs/pages/document.tsx +0 -203
  145. package/src/server/libs/pages/render.tsx +0 -90
  146. package/src/server/routes/auth.ts +0 -151
  147. package/src/server/services/redis/index.ts +0 -71
  148. package/src/server/services/router/request/services/auth.ts +0 -177
@@ -24,53 +24,6 @@ const debug = false;
24
24
 
25
25
  //import type { Choix } from '@client/components/Champs/Base/Choix';
26
26
 
27
- export type TSchema = { [champ: string]: TSchema | TSchemaChampComplet }
28
-
29
- export type TSchemaChamp<TValeur> = {
30
-
31
- rendu?: TBaseComposantChamp,
32
- activer?: (donnees: TObjetDonnees) => boolean,
33
- onglet?: string, // Sert juste d'identifiant secondaire. Ex: nom onglet correspondant
34
-
35
- // Executé après le validateur propre au type
36
- valider?: (val: TValeur, donneesSaisie: TObjetDonnees, donneesRetour: TObjetDonnees, corriger?: boolean) => Promise<TValeur>,
37
- dependances?: string[],
38
- opt?: true,
39
- defaut?: TValeur,
40
- as?: string[], // Mapping personnalisé
41
-
42
- }
43
-
44
- export type TBaseComposantChamp = (props: any) => ComponentChild;
45
-
46
- export type TSchemaChampComplet<TValeur = unknown> = TSchemaChamp<TValeur> & {
47
- type: string,
48
- valider: (val: any, formData?: unknown) => Promise<TValeur | undefined>
49
- }
50
-
51
- /*----------------------------------
52
- - TYPES: VALIDATION SCHEMA
53
- ----------------------------------*/
54
- type TOptsValider = {
55
- critique?: boolean,
56
- validationComplete?: boolean,
57
- avecDependances?: boolean,
58
- corriger?: boolean,
59
- }
60
-
61
- export type TDonneesValidees<TSchemaA extends TSchema> = {
62
- [cle in keyof TSchemaA]: TSchemaA[cle]["type"] extends string
63
- // Champ
64
- ? ThenArg<ReturnType<TSchemaA[cle]["valider"]>>
65
- // Schema
66
- : TDonneesValidees<TSchemaA[cle]>
67
- }
68
-
69
- export type TRetourValidation<TSchemaA extends TSchema> = {
70
- valeurs: TDonneesValidees<TSchemaA>,
71
- nbErreurs: number,
72
- erreurs: TListeErreursSaisie
73
- }
74
27
 
75
28
  /*----------------------------------
76
29
  - FONCTIONS
@@ -81,10 +34,10 @@ export const initDonnees = <TSchemaA extends TSchema>(
81
34
  schema: TSchemaA,
82
35
  donnees: TObjetDonnees,
83
36
  toutConserver: boolean = false
84
- ): Partial<TDonneesValidees<TSchemaA>> => {
37
+ ): Partial<TValidatedData<TSchemaA>> => {
85
38
 
86
39
  // toutConserver = true: on conserve toutes les données, y compris celles n'étant pas été définies dans le schéma
87
- let retour: Partial<TDonneesValidees<TSchemaA>> = toutConserver ? { ...donnees } : {}
40
+ let retour: Partial<TValidatedData<TSchemaA>> = toutConserver ? { ...donnees } : {}
88
41
 
89
42
  for (const nomChamp in schema) {
90
43
  const elem = schema[nomChamp];
@@ -107,120 +60,4 @@ export const initDonnees = <TSchemaA extends TSchema>(
107
60
 
108
61
  }
109
62
 
110
- export const validate = async <TSchemaA extends TSchema, TDonnees extends TObjetDonnees>(
111
-
112
- schema: TSchemaA,
113
-
114
- inputAvalider: Partial<TDonnees>,
115
- inputComplet?: TDonnees,
116
- output: TObjetDonnees = {},
117
-
118
- opts: TOptsValider = {},
119
- chemin: string[] = []
120
-
121
- ): Promise<TRetourValidation<TSchemaA>> => {
122
-
123
- opts = {
124
- critique: false,
125
- validationComplete: false,
126
- avecDependances: true,
127
- corriger: false,
128
- ...opts,
129
- }
130
-
131
- const clesAvalider = Object.keys(opts.validationComplete === true ? schema : inputAvalider);
132
-
133
- let outputSchema = output;
134
- for (const branche of chemin)
135
- outputSchema = outputSchema[branche];
136
-
137
- // Validation de chacune d'entre elles
138
- let erreurs: TListeErreursSaisie = {};
139
- let nbErreurs = 0;
140
- for (const champ of clesAvalider) {
141
-
142
- // La donnée est répertoriée dans le schema
143
- const metas = schema[champ];
144
- if (metas === undefined) {
145
- debug && console.warn('[schema][valider][' + champ + ']', 'Exclusion (pas présent dans le schéma)');
146
- continue;
147
- }
148
-
149
- const cheminA = [...chemin, champ].join('.')
150
-
151
- // Sous-schema
152
- if (isSchema(metas)) {
153
-
154
- // Initialise la structure pour permettre l'assignement d'outputSchema
155
- if (outputSchema[champ] === undefined)
156
- outputSchema[champ] = {}
157
-
158
- const validationSchema = await validate(
159
- metas,
160
- inputAvalider[champ],
161
- inputComplet,
162
- output,
163
- opts,
164
- cheminA
165
- );
166
- erreurs = { ...erreurs, ...validationSchema.erreurs };
167
- nbErreurs += validationSchema.nbErreurs;
168
-
169
- // Pas besoin d'assigner, car output est passé en référence
170
- //output[champ] = validationSchema.valeurs;
171
-
172
-
173
-
174
- } else if (metas.activer !== undefined && metas.activer(inputComplet) === false) {
175
-
176
- delete outputSchema[champ];
177
-
178
- } else {
179
-
180
- // Champ composé de plusieurs valeurs
181
- const valOrigine = metas.as === undefined
182
- ? inputAvalider[champ]
183
- // Le champ regroupe plusieurs valeurs (ex: Periode)
184
- : metas.as.map((nomVal: string) => inputAvalider[nomVal])
185
-
186
- // Validation
187
- try {
188
-
189
- const val = await metas.valider(valOrigine, inputComplet, output, opts.corriger);
190
-
191
- // Exclusion seulement si explicitement demandé
192
- // IMPORTANT: Conserver les valeurs undefined
193
- // La présence d'un valeur undefined peut être utile, par exemple, pour indiquer qu'on souhaite supprimer une donnée
194
- // Exemple: undefinec = suppression fichier | Absende donnée = conservation fihcier actuel
195
- if (val === EXCLURE_VALEUR)
196
- debug && console.log('[schema][valider][' + cheminA + '] Exclusion demandée');
197
- else
198
- outputSchema[champ] = val;
199
-
200
- debug && console.log('[schema][valider][' + cheminA + ']', valOrigine, '=>', val);
201
-
202
- } catch (error) {
203
-
204
- debug && console.warn('[schema][valider][' + cheminA + ']', valOrigine, '|| Erreur:', error);
205
-
206
- if (error instanceof Erreur) {
207
-
208
- // Référencement erreur
209
- erreurs[cheminA] = [error.message]
210
- nbErreurs++;
211
-
212
- } else
213
- throw error;
214
- }
215
- }
216
- }
217
-
218
- if (nbErreurs !== 0 && opts.critique === true) {
219
- throw new InputErrorSchema(erreurs);
220
- }
221
-
222
- debug && console.log('[schema][valider]', inputAvalider, '=>', output);
223
-
224
- return { erreurs, nbErreurs, valeurs: output, };
225
-
226
- }
63
+ export const validate =
@@ -118,3 +118,28 @@ export const chemin = {
118
118
 
119
119
  }
120
120
  }
121
+
122
+ export const callableInstance = <TInstance extends object, TCallableName extends keyof TInstance>(
123
+ instance: TInstance,
124
+ funcName: TCallableName
125
+ ): TInstance[TCallableName] & TInstance => {
126
+
127
+ const callableFunc = instance[funcName];
128
+ if (typeof callableFunc !== 'function')
129
+ throw new Error(`instance[funcName] isn't callable.`);
130
+
131
+ const callable = callableFunc.bind(instance);
132
+
133
+ const methods = [
134
+ ...Object.getOwnPropertyNames( Object.getPrototypeOf( instance )),
135
+ ...Object.getOwnPropertyNames( instance )
136
+ ];
137
+
138
+ for (const method of methods)
139
+ if (method !== 'constructor')
140
+ callable[ method ] = typeof instance[ method ] === 'function'
141
+ ? instance[ method ].bind( instance )
142
+ : instance[ method ];
143
+
144
+ return callable as TInstance[TCallableName] & TInstance;
145
+ }
@@ -40,6 +40,14 @@ export function arrayToObj<Ttbl extends {[cle: string]: any}, Tcle extends strin
40
40
 
41
41
  export const somme = (tbl: number[]) => tbl.reduce((a: number, b: number) => a + b);
42
42
 
43
+ export const arrayChunks = <TArray extends any[]>(array: TArray, size: number) => {
44
+
45
+ const arrays: TArray[] = [];
46
+ while (array.length > 0)
47
+ arrays.push( array.splice(0, size) as TArray );
48
+
49
+ return arrays;
50
+ }
43
51
 
44
52
  export function array_sum( tbl: number[] ): number {
45
53
  return tbl.reduce((a: number, b: number) => a + b);
@@ -161,4 +161,6 @@ export const instancierViaCode = (
161
161
  case 404: return new NotFound( message, details);
162
162
  default: return new Anomaly( message, details);
163
163
  }
164
- }
164
+ }
165
+
166
+ export default Erreur;
@@ -2,70 +2,89 @@
2
2
  - DEPENDANCES
3
3
  ----------------------------------*/
4
4
 
5
- // Npm
6
- import { pathToRegexp, Key } from 'path-to-regexp';
7
- import type { ComponentChild } from 'preact';
5
+ import { Layout } from './layouts';
8
6
 
9
7
  // types
10
8
  import type {
11
- TRegisterPageArgs,
12
- TClientRoute,
13
- TFrontRenderer
14
- } from '@client/router';
9
+ default as ClientRouter,
10
+ TRouterContext as ClientRouterContext,
11
+ TRegisterPageArgs
12
+ } from '@client/services/router';
15
13
 
16
- import type { ClientContext } from '@client/context';
14
+ import type {
15
+ default as ServerRouter,
16
+ TRouterContext as ServerRouterContext,
17
+ TRouteHttpMethod
18
+ } from '@server/services/router';
17
19
 
18
- import type { TSchema } from '@common/data/input/validate';
20
+ import type { TUserRole } from '@server/services/users';
19
21
 
20
- import type { TApiServerRoute } from '@server/services/router';
22
+ import type { TAppArrowFunction } from '@common/app';
21
23
 
22
- import type { TUserRole } from '@server/services/auth/base';
24
+ // Specfic
25
+ import type ApiClient from './request/api';
26
+ import type Request from './request';
27
+ import type Response from './response';
28
+ import type { default as Page, TFrontRenderer } from './response/page';
23
29
 
24
30
  /*----------------------------------
25
- - TYPES: layouts
31
+ - TYPES: ROUTES
26
32
  ----------------------------------*/
27
33
 
28
- import layouts from '@/client/pages/**/_layout/index.tsx';
29
-
30
- type LayoutComponent = (attributes: { context: ClientContext }) => ComponentChild;
31
- export type Layout = { path: string, Component: LayoutComponent }
32
- const getLayout = (routePath: string | undefined): Layout | undefined => {
33
-
34
- let layout: Layout | undefined = layouts[''];
35
- if (routePath) {
36
- for (const layoutPath in layouts)
37
- if (routePath === layoutPath || routePath.startsWith( layoutPath + '/' ))
38
- layout = { path: layoutPath, Component: layouts[layoutPath] };
39
- }
40
- //layout && console.log(`${routePath}: Using Layout: ${layout.path}`);
41
- return layout;
42
- }
34
+ export type { Layout } from './layouts';
43
35
 
44
- /*----------------------------------
45
- - TYPES: ROUTES
46
- ----------------------------------*/
36
+ export type { default as Request } from './request';
37
+ export type { default as Response } from './response';
47
38
 
48
- export type TBaseRoute = {
49
- path: string,
39
+ export type ClientOrServerRouter = ClientRouter | ServerRouter;
40
+
41
+ export type TRoute<RouterContext extends TClientOrServerContext = TClientOrServerContext> = {
50
42
 
43
+ // Match
44
+ method: TRouteHttpMethod,
45
+ path: string,
51
46
  regex: RegExp,
52
47
  keys: (number | string)[],
53
48
 
49
+ // Execute
50
+ controller: TRouteController<RouterContext>,//TServerController<TRouter> | TFrontRenderer<TRouter>,
54
51
  options: TRouteOptions
55
52
  }
56
53
 
57
- export type TRoute = TApiServerRoute | TClientRoute;
58
-
59
- export type TErrorRoute = {
60
- type: 'PAGE',
61
- renderer: TFrontRenderer,
62
- options: {}
54
+ export type TErrorRoute<RouterContext extends TClientOrServerContext = TClientOrServerContext> = {
55
+ code,
56
+ controller: TRouteController<RouterContext>,
57
+ options: TRouteOptions
63
58
  }
64
59
 
60
+ export type TClientOrServerContext<
61
+ TClientOnlyContextKeys extends keyof ClientRouterContext = keyof (ClientRouterContext | ServerRouterContext)
62
+ > = (
63
+ (
64
+ {[serverContextKey in keyof ServerRouterContext/*Omit<ClientRouterContext, TClientOnlyContextKeys>*/]: undefined}
65
+ &
66
+ ClientRouterContext
67
+ )
68
+ |
69
+ (
70
+ // Tell that all the keys of client context are existing but undefined
71
+ // This avoids errors: "Property 'page' is optional in type '{ app: Application; ..."
72
+ // When we destructure the context from the page controller
73
+ // While making reference to a key only available in client context
74
+ // So here, we put the
75
+ {[clientContextKey in keyof ClientRouterContext/*Omit<ClientRouterContext, TClientOnlyContextKeys>*/]: undefined}
76
+ &
77
+ ServerRouterContext
78
+ )
79
+ )
80
+
81
+ export type TRouteController<RouterContext extends TClientOrServerContext = TClientOrServerContext> =
82
+ (context: RouterContext) => /* Page to render */Page | /* Any data (html, json) */Promise<any>
83
+
65
84
  export type TRouteOptions = {
66
85
 
67
86
  // Injected by the page plugin
68
- filepath: string,
87
+ filepath?: string,
69
88
 
70
89
  // Indexing
71
90
  bodyId?: string,
@@ -78,7 +97,7 @@ export type TRouteOptions = {
78
97
 
79
98
  // Access Restriction
80
99
  auth?: TUserRole | boolean,
81
- form?: TSchema,
100
+ //form?: TSchema,
82
101
  layout?: false | Layout,
83
102
 
84
103
  TESTING?: boolean,
@@ -86,6 +105,11 @@ export type TRouteOptions = {
86
105
 
87
106
  }
88
107
 
108
+ export type TRouteModule<TRegisteredRoute = any> = {
109
+ // exporing __register is a way to know we axport a TAppArrowFunction
110
+ __register?: TAppArrowFunction<TRegisteredRoute>
111
+ }
112
+
89
113
  export const defaultOptions = {
90
114
  priority: 0,
91
115
  }
@@ -94,55 +118,10 @@ export const defaultOptions = {
94
118
  - BASE ROUTER
95
119
  ----------------------------------*/
96
120
 
97
- export default abstract class BaseRouter {
98
-
99
- public page = <TControllerData extends TObjetDonnees = {}>(...args: TRegisterPageArgs<TControllerData>) =>
100
- this.registerPage(...args);
101
-
102
- public error = (code: number, options, renderer: TFrontRenderer<{ message: string }>) =>
103
- this.registerErrorPage(code, options, renderer);
104
-
105
- protected abstract registerPage(...page: TRegisterPageArgs ): any;
106
-
107
- public errors: { [code: number]: TErrorRoute } = {};
108
- protected registerErrorPage( code: number, options, renderer: TFrontRenderer ) {
109
- return this.errors[code] = {
110
- type: 'PAGE',
111
- renderer,
112
- options
113
- };
114
- }
115
-
116
- protected getRegisterPageArgs(...args: TRegisterPageArgs) {
117
-
118
- const [path, options, controller, renderer] = args;
119
-
120
- // S'il s'agit d'une page, son id doit avoir été injecté via le plugin babel
121
- if (options["id"] === undefined)
122
- throw new Error(`ID not found for the following page route: ${path}`);
123
-
124
- // Bind layout
125
- if (options.layout !== false)
126
- options.layout = getLayout(options.filepath);
127
-
128
- return { path, options, controller, renderer }
129
-
130
- }
131
-
132
- protected buildRegex( path: string ) {
133
-
134
- // pathToRegexp ne supporte plus les wildcards depuis 4.0
135
- if (path.endsWith('*'))
136
- path = path.substring(0, path.length - 1) + '(.*)';
137
-
138
- // path => regex
139
- const keys: Key[] = []
140
- const regex = pathToRegexp(path, keys, {
141
- sensitive: true
142
- });
121
+ export default abstract class RouterInterface {
143
122
 
144
- return { keys, regex }
123
+ public abstract page<TControllerData extends TObjetDonnees = {}>(...args: TRegisterPageArgs<TControllerData>);
145
124
 
146
- }
125
+ public abstract error(code: number, options, renderer: TFrontRenderer<{}, { message: string }>);
147
126
 
148
127
  }
@@ -0,0 +1,50 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import type { ComponentChild } from 'preact';
7
+ // Core
8
+ import type { ClientContext } from '@/client/context';
9
+ // App
10
+ import layouts from '@/client/pages/**/_layout/index.tsx';
11
+
12
+ /*----------------------------------
13
+ - CONST
14
+ ----------------------------------*/
15
+
16
+ export const layoutsList = layouts as ImportedLayouts;
17
+
18
+ /*----------------------------------
19
+ - TYPES
20
+ ----------------------------------*/
21
+ type LayoutComponent = (attributes: { context: ClientContext }) => ComponentChild;
22
+
23
+ export type Layout = { path: string, Component: LayoutComponent }
24
+
25
+ export type ImportedLayouts = {
26
+ [chunkId: string]: Layout["Component"]
27
+ }
28
+
29
+ /*----------------------------------
30
+ - UTILS
31
+ ----------------------------------*/
32
+ // TODO: getLayot only on server side, and pass the layout chunk id
33
+ export const getLayout = (routePath: string | undefined): Layout | undefined => {
34
+
35
+ //console.log(`[router][layouts] Get layout for "${routePath}".`);
36
+
37
+ let layout: Layout | undefined;
38
+ if (routePath) {
39
+ for (const layoutPath in layouts)
40
+ if (routePath === layoutPath || routePath.startsWith( layoutPath + '_' ))
41
+ layout = {
42
+ path: layoutPath,
43
+ Component: layouts[layoutPath]
44
+ };
45
+ }
46
+
47
+ //console.log(`[router][layouts] Get layout for "${routePath}". Found:`, layout);
48
+
49
+ return layout;
50
+ }
@@ -0,0 +1,62 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import { pathToRegexp, Key } from 'path-to-regexp';
7
+
8
+ // Core
9
+ import { getLayout } from './layouts';
10
+
11
+ // types
12
+ import type { TRouteOptions } from '.';
13
+ import type { TDataProvider, TFrontRenderer } from './response/page';
14
+ import type { TRegisterPageArgs } from '@client/services/router';
15
+
16
+ /*----------------------------------
17
+ - UTILS
18
+ ----------------------------------*/
19
+
20
+ export const getRegisterPageArgs = (...args: TRegisterPageArgs) => {
21
+
22
+ let path: string;
23
+ let options: Partial<TRouteOptions> = {};
24
+ let controller: TDataProvider|null;
25
+ let renderer: TFrontRenderer;
26
+
27
+ if (args.length === 3)
28
+ ([path, controller, renderer] = args)
29
+ else
30
+ ([path, options, controller, renderer] = args)
31
+
32
+ // S'il s'agit d'une page, son id doit avoir été injecté via le plugin babel
33
+ const chunkId = options["id"];
34
+ if (chunkId === undefined)
35
+ throw new Error(`ID has not injected for the following page route: ${path}`);
36
+
37
+ // Bind layout
38
+ if (options.layout !== false)
39
+ options.layout = getLayout(chunkId);
40
+
41
+ return { path, options, controller, renderer }
42
+
43
+ }
44
+
45
+ export const buildRegex = ( path: string ) => {
46
+
47
+ // pathToRegexp ne supporte plus les wildcards depuis 4.0
48
+ if (path.endsWith('*'))
49
+ path = path.substring(0, path.length - 1) + '(.*)';
50
+
51
+ // path => regex
52
+ const keys: Key[] = []
53
+ const regex = pathToRegexp(path, keys, {
54
+ sensitive: true
55
+ });
56
+
57
+ return {
58
+ keys: keys.map(k => k.name),
59
+ regex
60
+ }
61
+
62
+ }
@@ -0,0 +1,72 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ import type { HttpMethod } from '@server/services/router';
6
+
7
+ /*----------------------------------
8
+ - TYPES
9
+ ----------------------------------*/
10
+
11
+ export type TFetcherList = { [id: string]: TFetcher }
12
+
13
+ export type TFetcher<TData extends unknown = unknown> = {
14
+
15
+ // For async calls: api.post(...).then((data) => ...)
16
+ then: (callback: (data: TData) => void) => Promise<TData>,
17
+ run: () => Promise<TData>,
18
+
19
+ method: HttpMethod,
20
+ path: string,
21
+ data?: object,
22
+ options?: TApiFetchOptions
23
+ }
24
+
25
+ export type TFetcherArgs = [
26
+ method: HttpMethod,
27
+ path: string,
28
+ data?: object,
29
+ options?: TApiFetchOptions
30
+ ]
31
+
32
+ export type TApiFetchOptions = {
33
+ captcha?: string, // Action id (required by recaptcha)
34
+ onProgress?: (percent: number) => void
35
+ }
36
+
37
+ // https://stackoverflow.com/questions/44851268/typescript-how-to-extract-the-generic-parameter-from-a-type
38
+ type TypeWithGeneric<T> = TFetcher<T>
39
+ type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never
40
+
41
+ export type TDataReturnedByFetchers<TProvidedData extends TFetcherList = {}> = {
42
+ [Property in keyof TProvidedData]: undefined | (extractGeneric<TProvidedData[Property]> extends ((...args: any[]) => any)
43
+ ? ThenArg<ReturnType< extractGeneric<TProvidedData[Property]> >>
44
+ : extractGeneric<TProvidedData[Property]>
45
+ )
46
+ }
47
+
48
+ /*----------------------------------
49
+ - CLASS
50
+ ----------------------------------*/
51
+ export default abstract class ApiClient {
52
+
53
+ /*----------------------------------
54
+ - TOP LEVEL
55
+ ----------------------------------*/
56
+
57
+ public abstract get<TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions): TFetcher<TData>;
58
+
59
+ public abstract post<TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions): TFetcher<TData>;
60
+
61
+ public abstract put<TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions): TFetcher<TData>;
62
+
63
+ public abstract delete<TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions): TFetcher<TData>;
64
+
65
+ /*----------------------------------
66
+ - LOW LEVEL
67
+ ----------------------------------*/
68
+
69
+ public abstract createFetcher<TData extends unknown = unknown>(...args: TFetcherArgs): TFetcher<TData>;
70
+
71
+ public abstract fetchSync(fetchers: TFetcherList, alreadyLoadedData: {}): Promise<TObjetDonnees>;
72
+ }
@@ -0,0 +1,31 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Core
6
+ import Response from '../response';
7
+
8
+ /*----------------------------------
9
+ - TYPES
10
+ ----------------------------------*/
11
+
12
+
13
+ /*----------------------------------
14
+ - CONTEXT
15
+ ----------------------------------*/
16
+ export default abstract class BaseRequest {
17
+
18
+ // Permet d'accèder à l'instance complète via spread
19
+ public request: this = this;
20
+ public host!: string;
21
+
22
+ public data: TObjetDonnees = {};
23
+ public abstract response?: Response;
24
+ public user: User | null = null;
25
+
26
+ public constructor(
27
+ public path: string,
28
+ ) {
29
+
30
+ }
31
+ }