5htp-core 0.1.2 → 0.2.0
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 -8
- 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/medias.less +14 -0
- package/src/client/components/Card/index.tsx +2 -2
- package/src/client/components/Dialog/Manager.tsx +39 -12
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- 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 +8 -0
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- 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} +43 -74
- package/src/client/services/router/index.tsx +448 -0
- package/src/client/services/router/request/api.ts +229 -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 +95 -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 +40 -56
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +184 -0
- package/src/common/validation/validator.ts +88 -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/error/index.ts +13 -0
- 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 +206 -75
- package/src/server/services/database/datatypes.ts +63 -40
- 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 +28 -52
- 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 -203
- package/src/server/services/router/request/api.ts +73 -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 +125 -64
- 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/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/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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import { Location } from 'history';
|
|
7
|
+
import type Bowser from 'bowser';
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import BaseRequest from '@common/router/request';
|
|
11
|
+
|
|
12
|
+
// Specific
|
|
13
|
+
import type ClientRouter from '..';
|
|
14
|
+
import ApiClient from './api';
|
|
15
|
+
import type ClientResponse from '../response';
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- TYPES
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/*----------------------------------
|
|
23
|
+
- ROUTER
|
|
24
|
+
----------------------------------*/
|
|
25
|
+
// Since we do SSR, the server router can also be passed here
|
|
26
|
+
export default class ClientRequest<TRouter extends ClientRouter = ClientRouter> extends BaseRequest {
|
|
27
|
+
|
|
28
|
+
public api: ApiClient;
|
|
29
|
+
public response?: ClientResponse<TRouter>;
|
|
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.hash = location.hash;
|
|
42
|
+
|
|
43
|
+
this.api = new ApiClient(this.app, this);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// To move to a service
|
|
47
|
+
public device(): Bowser.Parser.ParsedResult | undefined {
|
|
48
|
+
// We load bowser only when required
|
|
49
|
+
const Bowser = require("bowser");
|
|
50
|
+
return Bowser.parse(window.navigator.userAgent);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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 { TRoute, TErrorRoute } from '@common/router';
|
|
10
|
+
import BaseResponse 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
|
+
import CommonPage from '@common/router/response/page';
|
|
19
|
+
|
|
20
|
+
/*----------------------------------
|
|
21
|
+
- TYPES
|
|
22
|
+
----------------------------------*/
|
|
23
|
+
|
|
24
|
+
export type TPageResponse<TRouter extends ClientRouter> = (
|
|
25
|
+
ClientResponse<TRouter, ClientPage>
|
|
26
|
+
|
|
|
27
|
+
ServerResponse<ServerRouter, ClientPage>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export type TRouterContext<TRouter extends ClientRouter = ClientRouter, TApplication extends ClientApplication = ClientApplication> = (
|
|
31
|
+
// ClientPage context
|
|
32
|
+
{
|
|
33
|
+
app: TApplication,
|
|
34
|
+
context: TRouterContext<TRouter, TApplication>,
|
|
35
|
+
request: ClientRequest<TRouter>,
|
|
36
|
+
route: TRoute<TRouter>,
|
|
37
|
+
api: ClientRequest<TRouter>["api"],
|
|
38
|
+
page: ClientPage<TRouter>,
|
|
39
|
+
user: User
|
|
40
|
+
}
|
|
41
|
+
&
|
|
42
|
+
// Expose client application services (api, socket, ...)
|
|
43
|
+
//TRouter["app"]
|
|
44
|
+
TApplication
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
/*----------------------------------
|
|
48
|
+
- ROUTER
|
|
49
|
+
----------------------------------*/
|
|
50
|
+
export default class ClientPageResponse<
|
|
51
|
+
TRouter extends ClientRouter,
|
|
52
|
+
TData extends TResponseData = TResponseData
|
|
53
|
+
> extends BaseResponse<TData> {
|
|
54
|
+
|
|
55
|
+
public context: TRouterContext<TRouter, TRouter["app"]>;
|
|
56
|
+
|
|
57
|
+
public constructor(
|
|
58
|
+
public request: ClientRequest<TRouter>,
|
|
59
|
+
public route: TRoute | TErrorRoute,
|
|
60
|
+
|
|
61
|
+
public app = request.app,
|
|
62
|
+
) {
|
|
63
|
+
|
|
64
|
+
super(request);
|
|
65
|
+
|
|
66
|
+
request.response = this;
|
|
67
|
+
|
|
68
|
+
// Create response context for controllers
|
|
69
|
+
this.context = this.createContext();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private createContext(): TRouterContext<TRouter, TRouter["app"]> {
|
|
73
|
+
|
|
74
|
+
const context: TRouterContext<TRouter, TRouter["app"]> = {
|
|
75
|
+
// App services (TODO: expose only services)
|
|
76
|
+
...this.request.app,
|
|
77
|
+
// Router context
|
|
78
|
+
app: this.app,
|
|
79
|
+
context: undefined as unknown as TRouterContext<TRouter, TRouter["app"]>,
|
|
80
|
+
request: this.request,
|
|
81
|
+
route: this.route,
|
|
82
|
+
api: this.request.api,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
context.context = context;
|
|
86
|
+
|
|
87
|
+
return context;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async runController( additionnalData: {} = {} ): Promise<ClientPage> {
|
|
91
|
+
|
|
92
|
+
// Run contoller
|
|
93
|
+
const result = this.route.controller(this.context);
|
|
94
|
+
|
|
95
|
+
// Default data type for `return <raw data>`
|
|
96
|
+
if (result instanceof ClientPage)
|
|
97
|
+
await result.render();
|
|
98
|
+
else
|
|
99
|
+
throw new Error(`Unsupported response format: ${result.constructor?.name}`);
|
|
100
|
+
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public redirect(url: string) {
|
|
105
|
+
history?.replace(url);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import type { TClientOrServerContext } from '@common/router';
|
|
7
|
+
import PageResponse, { TDataProvider, TFrontRenderer } from "@common/router/response/page";
|
|
8
|
+
|
|
9
|
+
// Specific
|
|
10
|
+
import type ClientRouter from '..';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- TYPES
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/*----------------------------------
|
|
19
|
+
- CLASS
|
|
20
|
+
----------------------------------*/
|
|
21
|
+
|
|
22
|
+
export default class ClientPage<TRouter = ClientRouter> extends PageResponse<TRouter> {
|
|
23
|
+
|
|
24
|
+
public isLoading: boolean = false;
|
|
25
|
+
public scrollToId: string;
|
|
26
|
+
|
|
27
|
+
public constructor(
|
|
28
|
+
public dataProvider: TDataProvider | null,
|
|
29
|
+
public component: TFrontRenderer,
|
|
30
|
+
public context: TClientOrServerContext,
|
|
31
|
+
|
|
32
|
+
public route = context.route
|
|
33
|
+
) {
|
|
34
|
+
|
|
35
|
+
super(dataProvider, component, context);
|
|
36
|
+
|
|
37
|
+
this.bodyId = context.route.options.bodyId;
|
|
38
|
+
this.scrollToId = context.request.hash;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async render( data?: TObjetDonnees ) {
|
|
42
|
+
|
|
43
|
+
// Add the page to the context
|
|
44
|
+
this.context.page = this;
|
|
45
|
+
|
|
46
|
+
// Load the fetchers list to load data if needed
|
|
47
|
+
if (this.dataProvider)
|
|
48
|
+
this.fetchers = this.dataProvider( this.context );
|
|
49
|
+
|
|
50
|
+
// Data succesfully loaded
|
|
51
|
+
if (data !== undefined) {
|
|
52
|
+
this.isLoading = false;
|
|
53
|
+
this.data = data;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fetch data
|
|
57
|
+
this.data = await this.fetchData();
|
|
58
|
+
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*----------------------------------
|
|
63
|
+
- ACTIONS
|
|
64
|
+
----------------------------------*/
|
|
65
|
+
// Should be called AFTER rendering the page
|
|
66
|
+
public updateClient() {
|
|
67
|
+
|
|
68
|
+
document.body.id = this.bodyId || this.id;
|
|
69
|
+
document.title = this.title || APP_NAME;
|
|
70
|
+
document.body.className = [...this.bodyClass].join(' ');
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public setAllData( callback: (data: {[k: string]: any}) => void) {
|
|
75
|
+
console.warn(`page.setAllData not yet attached to the page Reatc component.`);
|
|
76
|
+
}
|
|
77
|
+
public setData( key: string, value: ((value: any) => void) | any ) {
|
|
78
|
+
this.setAllData(old => ({
|
|
79
|
+
...old,
|
|
80
|
+
[key]: typeof value === 'function' ? value(old[key]) : value
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public loadIndicator;
|
|
85
|
+
public loading(state: boolean) {
|
|
86
|
+
|
|
87
|
+
if (state === true) {
|
|
88
|
+
if (!document.body.classList.contains("loading"))
|
|
89
|
+
document.body.classList.add("loading");
|
|
90
|
+
} else {
|
|
91
|
+
document.body.classList.remove("loading");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
- DEPENDANCES
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
-
import type { ClientContext } from '
|
|
5
|
+
import type { ClientContext } from '../../context';
|
|
6
6
|
|
|
7
7
|
import type { TDialogControls } from '@client/components/Dialog/Manager';
|
|
8
8
|
|
|
@@ -131,7 +131,7 @@ export default class SocketClient {
|
|
|
131
131
|
|
|
132
132
|
public scopes: { [name: string]: SocketScope } = {}
|
|
133
133
|
|
|
134
|
-
public constructor(public context: ClientContext ) {
|
|
134
|
+
public constructor( public context: ClientContext ) {
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
}
|
package/src/client/utils/dom.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type ClientApplication from '@client/app';
|
|
2
|
+
import type ServerApplication from '@server/app';
|
|
3
|
+
|
|
4
|
+
export type ClientOrServerApplication = ClientApplication | ServerApplication;
|
|
5
|
+
|
|
6
|
+
export type TAppArrowFunction<
|
|
7
|
+
TRegisteredData = void,
|
|
8
|
+
TApplication extends ClientOrServerApplication = ClientOrServerApplication
|
|
9
|
+
> = (app: TApplication) => TRegisteredData
|
|
@@ -11,6 +11,11 @@ export const nameToID = (name: string) => name.toLowerCase().replace(/[^a-z1-9]/
|
|
|
11
11
|
|
|
12
12
|
export const ucfirst = (chaine: string): string => {
|
|
13
13
|
return chaine.charAt(0).toUpperCase() + chaine.slice(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const linkify = (texte: string): string => {
|
|
17
|
+
const regex = /((http|https)\:\/\/([a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?))/gi;
|
|
18
|
+
return texte.replace(regex, '<a href="$1" target="_blank">$3</a>');
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export const trim = (s: string, c: string) => {
|
|
@@ -19,12 +24,7 @@ export const trim = (s: string, c: string) => {
|
|
|
19
24
|
return s.replace(new RegExp(
|
|
20
25
|
"^[" + c + "]+|[" + c + "]+$", "g"
|
|
21
26
|
), "");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const linkify = (texte: string): string => {
|
|
25
|
-
const regex = /((http|https)\:\/\/([a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?))/gi;
|
|
26
|
-
return texte.replace(regex, '<a href="$1" target="_blank">$3</a>');
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
|
|
29
29
|
export const trimLeft = (chaine: string, toTrim: string) => chaine.startsWith(toTrim)
|
|
30
30
|
? chaine.substring(toTrim.length) : chaine;
|
|
@@ -32,6 +32,9 @@ export const trimLeft = (chaine: string, toTrim: string) => chaine.startsWith(to
|
|
|
32
32
|
export const trimRight = (chaine: string, toTrim: string) => chaine.endsWith(toTrim)
|
|
33
33
|
? chaine.substring(0, -toTrim.length) : chaine;
|
|
34
34
|
|
|
35
|
+
export const escapeRegExp = (string: string) =>
|
|
36
|
+
string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
37
|
+
|
|
35
38
|
/*----------------------------------
|
|
36
39
|
- EXTRACT
|
|
37
40
|
----------------------------------*/
|
|
@@ -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);
|