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.
- package/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -11
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/assets/css/components.less +52 -0
- package/src/client/assets/css/core.less +7 -28
- package/src/client/assets/css/theme.less +1 -1
- package/src/client/assets/css/{borders.less → utils/borders.less} +0 -0
- package/src/client/assets/css/{layouts.less → utils/layouts.less} +0 -0
- package/src/client/assets/css/{medias.less → utils/medias.less} +14 -1
- package/src/client/assets/css/{sizing.less → utils/sizing.less} +0 -0
- package/src/client/assets/css/{spacing.less → utils/spacing.less} +0 -0
- package/src/client/components/Card/index.tsx +13 -7
- package/src/client/components/Dialog/Manager.tsx +41 -14
- package/src/client/components/Dialog/index.less +2 -4
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/Row/index.less +0 -2
- package/src/client/components/Table/index.tsx +3 -2
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/containers/champs.less +0 -2
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +23 -0
- package/src/client/components/input/BaseV2/index.less +0 -2
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/Date/index.less +0 -2
- package/src/client/components/input/Periode/index.less +0 -2
- package/src/client/components/input/Radio/index.less +0 -2
- package/src/client/components/input/UploadImage/index.less +0 -2
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +52 -74
- package/src/client/services/router/index.tsx +453 -0
- package/src/client/services/router/request/api.ts +227 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +90 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +46 -54
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +185 -0
- package/src/common/validation/validator.ts +95 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +9 -27
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +59 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +213 -80
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/debug.ts +20 -0
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +30 -62
- package/src/server/services/email/transporter.ts +38 -0
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -202
- package/src/server/services/router/request/api.ts +76 -0
- package/src/server/services/router/request/index.ts +16 -97
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +131 -65
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/server/{data → services_old}/SocketClient.ts +0 -0
- package/src/server/{data/Token.olg.ts → services_old/Token.old.ts} +0 -0
- package/src/server/{data → services_old}/aes.ts +0 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/assets/css/components/components.less +0 -31
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- 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<
|
|
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<
|
|
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 =
|
|
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);
|
|
@@ -2,70 +2,89 @@
|
|
|
2
2
|
- DEPENDANCES
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from '@client/router';
|
|
9
|
+
default as ClientRouter,
|
|
10
|
+
TRouterContext as ClientRouterContext,
|
|
11
|
+
TRegisterPageArgs
|
|
12
|
+
} from '@client/services/router';
|
|
15
13
|
|
|
16
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
default as ServerRouter,
|
|
16
|
+
TRouterContext as ServerRouterContext,
|
|
17
|
+
TRouteHttpMethod
|
|
18
|
+
} from '@server/services/router';
|
|
17
19
|
|
|
18
|
-
import type {
|
|
20
|
+
import type { TUserRole } from '@server/services/users';
|
|
19
21
|
|
|
20
|
-
import type {
|
|
22
|
+
import type { TAppArrowFunction } from '@common/app';
|
|
21
23
|
|
|
22
|
-
|
|
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:
|
|
31
|
+
- TYPES: ROUTES
|
|
26
32
|
----------------------------------*/
|
|
27
33
|
|
|
28
|
-
|
|
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
|
-
|
|
46
|
-
----------------------------------*/
|
|
36
|
+
export type { default as Request } from './request';
|
|
37
|
+
export type { default as Response } from './response';
|
|
47
38
|
|
|
48
|
-
export type
|
|
49
|
-
|
|
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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|