5htp-core 0.6.2-98 → 0.6.2-99
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.6.2-
|
|
4
|
+
"version": "0.6.2-99",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
package/server/app/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import ServicesContainer, {
|
|
|
17
17
|
// Built-in
|
|
18
18
|
import type { default as Router, Request as ServerRequest, TRoute } from '@server/services/router';
|
|
19
19
|
import { Anomaly } from '@common/errors';
|
|
20
|
+
import { preprocessSchema } from '@server/services/router/request/validation/zod';
|
|
20
21
|
|
|
21
22
|
export { default as Services } from './service/container';
|
|
22
23
|
export type { TEnvConfig as Environment } from './container/config';
|
|
@@ -208,12 +209,14 @@ export abstract class Application<
|
|
|
208
209
|
|
|
209
210
|
console.log('Attached service', service.constructor.name, 'to route', route.path);
|
|
210
211
|
|
|
212
|
+
const preprocessedSchema = route.schema ? preprocessSchema(route.schema) : undefined;
|
|
213
|
+
|
|
211
214
|
const origController = route.controller;
|
|
212
215
|
route.controller = (context: RouterContext) => {
|
|
213
216
|
|
|
214
217
|
// Filter data
|
|
215
|
-
const data =
|
|
216
|
-
?
|
|
218
|
+
const data = preprocessedSchema
|
|
219
|
+
? preprocessedSchema.parse( context.request.data )
|
|
217
220
|
: {};
|
|
218
221
|
|
|
219
222
|
// Run controller
|
|
@@ -5,42 +5,61 @@ export type TRichTextValidatorOptions = {
|
|
|
5
5
|
attachements?: boolean
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
|
|
8
|
+
export const preprocessSchema = (schema: zod.ZodObject): zod.ZodObject => {
|
|
10
9
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
throw new InputError("Invalid rich text value (3).");
|
|
10
|
+
// Not working, data is {}
|
|
11
|
+
return schema;
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
if (!(schema instanceof zod.ZodObject))
|
|
14
|
+
return schema;
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
if (schema.withPreprocessing)
|
|
17
|
+
return schema;
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const shape = schema.def.shape;
|
|
20
|
+
const newShape: Record<string, zod.ZodTypeAny> = {};
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
throw new InputError("Invalid rich text value (5).");
|
|
26
|
-
}
|
|
22
|
+
for (const key in shape) {
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
if (!['newEntity', 'email'].includes(key))
|
|
25
|
+
continue;
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
let current: zod.ZodTypeAny = shape[key];
|
|
28
|
+
while (current) {
|
|
29
|
+
|
|
30
|
+
const origType = current.type;
|
|
31
|
+
const preprocessor = toPreprocess[origType];
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
if (origType === 'object') {
|
|
34
|
+
newShape[key] = preprocessSchema(current as zod.ZodObject);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
36
37
|
|
|
38
|
+
if (preprocessor) {
|
|
39
|
+
newShape[key] = preprocessor(current);
|
|
40
|
+
console.log('====newShape', key, newShape[key]);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
current = current.def;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
39
47
|
|
|
48
|
+
const newSchema = zod.object(newShape);
|
|
49
|
+
newSchema.withPreprocessing = true;
|
|
50
|
+
return newSchema;
|
|
51
|
+
}
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
const toPreprocess = {
|
|
54
|
+
|
|
55
|
+
string: (zString: zod.ZodString) => zod.preprocess( val => {
|
|
56
|
+
return val === '' ? undefined : val;
|
|
57
|
+
}, zString),
|
|
58
|
+
|
|
59
|
+
int: (zInt: zod.ZodInt) => zod.preprocess( val => {
|
|
60
|
+
return typeof val === 'string' ? Number.parseInt(val) : val;
|
|
61
|
+
}, zInt),
|
|
42
62
|
|
|
43
|
-
return true;
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
export const schema = {
|
|
@@ -56,15 +75,6 @@ export const schema = {
|
|
|
56
75
|
return zod.file();
|
|
57
76
|
},
|
|
58
77
|
|
|
59
|
-
int: () => zod.preprocess( val => {
|
|
60
|
-
|
|
61
|
-
if (typeof val === "string")
|
|
62
|
-
return Number.parseInt(val);
|
|
63
|
-
|
|
64
|
-
return val;
|
|
65
|
-
|
|
66
|
-
}, zod.int()),
|
|
67
|
-
|
|
68
78
|
choice: ( choices: string[] | { value: any, label: string }[] | _ZodType, options: { multiple?: boolean } = {} ) => {
|
|
69
79
|
|
|
70
80
|
const normalizeValue = (value: any) => typeof value === 'object' ? value.value : value;
|
|
@@ -129,4 +139,42 @@ export const schema = {
|
|
|
129
139
|
})
|
|
130
140
|
}
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
// Recursive function to validate each node
|
|
143
|
+
function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
|
|
144
|
+
|
|
145
|
+
// Each node should be an object with a `type` property
|
|
146
|
+
if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
|
|
147
|
+
throw new InputError("Invalid rich text value (3).");
|
|
148
|
+
|
|
149
|
+
// Validate text nodes
|
|
150
|
+
if (node.type === 'text') {
|
|
151
|
+
|
|
152
|
+
if (typeof node.text !== 'string')
|
|
153
|
+
throw new InputError("Invalid rich text value (4).");
|
|
154
|
+
|
|
155
|
+
// Validate paragraph, heading, or other structural nodes that may contain children
|
|
156
|
+
} else if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
|
|
157
|
+
|
|
158
|
+
if (!Array.isArray(node.children) || !node.children.every(children => validateLexicalNode(children, opts))) {
|
|
159
|
+
throw new InputError("Invalid rich text value (5).");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Files upload
|
|
163
|
+
} else if (node.type === 'image') {
|
|
164
|
+
|
|
165
|
+
// Check if allowed
|
|
166
|
+
/*if (opts.attachements === undefined)
|
|
167
|
+
throw new InputError("Image attachments not allowed in this rich text field.");*/
|
|
168
|
+
|
|
169
|
+
// TODO: check mime
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
// Upload file
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export type { default as z } from 'zod';
|
|
@@ -313,11 +313,11 @@ export default class ServerResponse<
|
|
|
313
313
|
return this.end();
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
public redirect(url: string, code: number = 302) {
|
|
316
|
+
public redirect(url: string, code: number = 302, absolute: boolean = false) {
|
|
317
317
|
|
|
318
318
|
debug && console.log("[routeur][response] Redirect", url);
|
|
319
319
|
this.statusCode = code;
|
|
320
|
-
this.headers['Location'] = this.router.url( url );
|
|
320
|
+
this.headers['Location'] = this.router.url( url, {}, absolute );
|
|
321
321
|
return this.end();
|
|
322
322
|
}
|
|
323
323
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '@server/services/router';
|
|
13
13
|
|
|
14
14
|
// Ap
|
|
15
|
-
import { schema } from '@server/services/router/request/validation/zod';
|
|
15
|
+
import { preprocessSchema, schema } from '@server/services/router/request/validation/zod';
|
|
16
16
|
|
|
17
17
|
/*----------------------------------
|
|
18
18
|
- SERVICE CONFIG
|
|
@@ -42,6 +42,8 @@ export default(
|
|
|
42
42
|
|
|
43
43
|
const schema = typeof fields === 'object' ? zod.object(fields) : fields;
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const preprocessedSchema = preprocessSchema(schema);
|
|
46
|
+
|
|
47
|
+
return preprocessedSchema.parse(request.data);
|
|
46
48
|
},
|
|
47
49
|
})
|
package/types/icons.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type TIcones = "solid/spinner-third"|"
|
|
1
|
+
export type TIcones = "solid/spinner-third"|"rocket"|"user-circle"|"brands/linkedin"|"play"|"stop"|"trash"|"times"|"at"|"star"|"plus"|"minus"|"magnet"|"paper-plane"|"search"|"check"|"plus-circle"|"regular/shield-check"|"angle-down"|"clock"|"cog"|"ellipsis-h"|"long-arrow-right"|"lightbulb"|"long-arrow-left"|"phone"|"arrow-right"|"plane-departure"|"comments-alt"|"user-shield"|"shield-alt"|"chart-line"|"money-bill-wave"|"link"|"file-alt"|"solid/crown"|"eye"|"pen"|"file"|"envelope"|"angle-up"|"user-plus"|"sack-dollar"|"info-circle"|"mouse-pointer"|"thumbs-up"|"dollar-sign"|"download"|"brands/google"|"brands/whatsapp"|"crown"|"check-circle"|"exclamation-circle"|"times-circle"|"arrow-left"|"key"|"building"|"briefcase"|"map-marker-alt"|"graduation-cap"|"solid/check-circle"|"solid/exclamation-triangle"|"solid/times-circle"|"hourglass"|"angle-left"|"angle-right"|"broom"|"question-circle"|"coin"|"coins"|"plug"|"arrow-to-bottom"|"external-link"|"magic"|"minus-circle"|"user"|"meh-rolling-eyes"|"bold"|"italic"|"underline"|"strikethrough"|"subscript"|"superscript"|"code"|"unlink"|"font"|"empty-set"|"horizontal-rule"|"page-break"|"image"|"table"|"poll"|"columns"|"sticky-note"|"caret-right"|"align-left"|"align-center"|"align-right"|"align-justify"|"indent"|"outdent"|"list-ul"|"check-square"|"h1"|"h2"|"h3"|"h4"|"list-ol"|"paragraph"|"quote-left"
|