exisjs 0.7.4 → 0.7.6
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/dist/cli/commands/build.d.ts +4 -2
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +80 -22
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +229 -64
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +5 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/generate.d.ts +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +25 -0
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +65 -15
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/index.js +20 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +118 -17
- package/dist/cli/manifest.js.map +1 -1
- package/dist/cli/resolve-aliases.d.ts +1 -1
- package/dist/cli/resolve-aliases.d.ts.map +1 -1
- package/dist/cli/resolve-aliases.js +30 -13
- package/dist/cli/resolve-aliases.js.map +1 -1
- package/dist/config/config.d.ts +3 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +8 -0
- package/dist/config/env.js.map +1 -1
- package/dist/config/types.d.ts +2 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/decorators/constants.d.ts +2 -0
- package/dist/decorators/constants.d.ts.map +1 -1
- package/dist/decorators/middleware.d.ts +65 -2
- package/dist/decorators/middleware.d.ts.map +1 -1
- package/dist/decorators/middleware.js +148 -62
- package/dist/decorators/middleware.js.map +1 -1
- package/dist/decorators/params.d.ts +5 -0
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +11 -1
- package/dist/decorators/params.js.map +1 -1
- package/dist/decorators/response.d.ts.map +1 -1
- package/dist/decorators/response.js +32 -22
- package/dist/decorators/response.js.map +1 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +6 -1
- package/dist/di/container.js.map +1 -1
- package/dist/docs/content/boundaries.mdx +33 -3
- package/dist/docs/content/cli/commands.mdx +1 -1
- package/dist/docs/content/config.mdx +26 -0
- package/dist/docs/content/controllers.mdx +127 -10
- package/dist/docs/content/decorators.mdx +22 -4
- package/dist/docs/content/errors.mdx +38 -2
- package/dist/docs/content/introduction.mdx +7 -7
- package/dist/docs/content/middleware.mdx +269 -6
- package/dist/docs/content/requests.mdx +217 -10
- package/dist/docs/content/responses.mdx +323 -34
- package/dist/docs/content/routing.mdx +46 -0
- package/dist/docs/content/security.mdx +17 -4
- package/dist/docs/content/structure.mdx +91 -12
- package/dist/docs/content/validation.mdx +226 -11
- package/dist/docs/reference/context.mdx +11 -0
- package/dist/docs/reference/decorators.mdx +14 -4
- package/dist/docs/reference/error.mdx +44 -0
- package/dist/docs/reference/logger.mdx +175 -43
- package/dist/docs/reference/middleware.mdx +31 -0
- package/dist/docs/reference/router.mdx +22 -0
- package/dist/docs/reference/validator.mdx +83 -23
- package/dist/error/errors.d.ts.map +1 -1
- package/dist/error/errors.js +95 -43
- package/dist/error/errors.js.map +1 -1
- package/dist/error/index.d.ts +1 -0
- package/dist/error/index.d.ts.map +1 -1
- package/dist/error/index.js +11 -1
- package/dist/error/index.js.map +1 -1
- package/dist/error/overlay.d.ts +42 -0
- package/dist/error/overlay.d.ts.map +1 -0
- package/dist/error/overlay.js +306 -0
- package/dist/error/overlay.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/start-server.js +49 -7
- package/dist/lib/start-server.js.map +1 -1
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +9 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/guard.d.ts +2 -2
- package/dist/middleware/guard.d.ts.map +1 -1
- package/dist/middleware/guard.js.map +1 -1
- package/dist/middleware/ip-filter.d.ts +1 -0
- package/dist/middleware/ip-filter.d.ts.map +1 -1
- package/dist/middleware/ip-filter.js +2 -0
- package/dist/middleware/ip-filter.js.map +1 -1
- package/dist/middleware/middleware.d.ts +2 -2
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/middleware.js +20 -2
- package/dist/middleware/middleware.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +9 -0
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/middleware/security.d.ts +43 -1
- package/dist/middleware/security.d.ts.map +1 -1
- package/dist/middleware/security.js +101 -0
- package/dist/middleware/security.js.map +1 -1
- package/dist/response/index.d.ts +229 -6
- package/dist/response/index.d.ts.map +1 -1
- package/dist/response/index.js +272 -2
- package/dist/response/index.js.map +1 -1
- package/dist/router/boundary.d.ts +25 -5
- package/dist/router/boundary.d.ts.map +1 -1
- package/dist/router/boundary.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +56 -1
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/index.d.ts +2 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/route-builder.d.ts +70 -36
- package/dist/router/route-builder.d.ts.map +1 -1
- package/dist/router/route-builder.js +13 -15
- package/dist/router/route-builder.js.map +1 -1
- package/dist/router/route-scanner.d.ts +1 -0
- package/dist/router/route-scanner.d.ts.map +1 -1
- package/dist/router/route-scanner.js +217 -10
- package/dist/router/route-scanner.js.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +35 -17
- package/dist/router/router.js.map +1 -1
- package/dist/router/types.d.ts +11 -1
- package/dist/router/types.d.ts.map +1 -1
- package/dist/sanitize/index.d.ts +6 -6
- package/dist/sanitize/index.d.ts.map +1 -1
- package/dist/sanitize/index.js +8 -8
- package/dist/sanitize/index.js.map +1 -1
- package/dist/server/app.d.ts +2 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +14 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +91 -30
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/cluster.d.ts.map +1 -1
- package/dist/server/cluster.js +13 -2
- package/dist/server/cluster.js.map +1 -1
- package/dist/server/context.d.ts +8 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +50 -6
- package/dist/server/context.js.map +1 -1
- package/dist/server/engines/node-engine.d.ts.map +1 -1
- package/dist/server/engines/node-engine.js +1 -0
- package/dist/server/engines/node-engine.js.map +1 -1
- package/dist/server/lifecycle.d.ts +22 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/lifecycle.js +106 -0
- package/dist/server/lifecycle.js.map +1 -0
- package/dist/server/request-handler.d.ts +1 -0
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-handler.js +42 -18
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts +6 -0
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +112 -33
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +13 -2
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +250 -10
- package/dist/server/response.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +6 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mocks.d.ts.map +1 -1
- package/dist/testing/mocks.js +15 -3
- package/dist/testing/mocks.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +184 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/validator/tex-types.d.ts +2 -1
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +2 -1
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +38 -22
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +490 -40
- package/dist/validator/tex.js.map +1 -1
- package/package.json +10 -10
- package/dist/dev/error-overlay.d.ts +0 -3
- package/dist/dev/error-overlay.d.ts.map +0 -1
- package/dist/dev/error-overlay.js +0 -118
- package/dist/dev/error-overlay.js.map +0 -1
package/dist/validator/tex.js
CHANGED
|
@@ -8,8 +8,12 @@ const tex_types_1 = require("./tex-types");
|
|
|
8
8
|
class TexBuilder {
|
|
9
9
|
string(opts) {
|
|
10
10
|
let base = 'string';
|
|
11
|
-
if (opts?.optional)
|
|
11
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
12
12
|
base += '?';
|
|
13
|
+
if (opts?.nullable)
|
|
14
|
+
base += ' | nullable';
|
|
15
|
+
if (opts?.nullish)
|
|
16
|
+
base += ' | nullish';
|
|
13
17
|
if (opts?.min !== undefined)
|
|
14
18
|
base += ` | min:${opts.min}`;
|
|
15
19
|
if (opts?.max !== undefined)
|
|
@@ -34,68 +38,94 @@ class TexBuilder {
|
|
|
34
38
|
base += ' | preventSql';
|
|
35
39
|
if (opts?.preventTraversal)
|
|
36
40
|
base += ' | preventTraversal';
|
|
37
|
-
return new tex_types_1.TexType(base);
|
|
41
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
38
42
|
}
|
|
39
43
|
number(opts) {
|
|
40
44
|
let base = 'number';
|
|
41
|
-
if (opts?.optional)
|
|
45
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
42
46
|
base += '?';
|
|
47
|
+
if (opts?.nullable)
|
|
48
|
+
base += ' | nullable';
|
|
49
|
+
if (opts?.nullish)
|
|
50
|
+
base += ' | nullish';
|
|
43
51
|
if (opts?.coerce)
|
|
44
52
|
base += ' | coerce';
|
|
45
53
|
if (opts?.min !== undefined)
|
|
46
54
|
base += ` | min:${opts.min}`;
|
|
47
55
|
if (opts?.max !== undefined)
|
|
48
56
|
base += ` | max:${opts.max}`;
|
|
49
|
-
|
|
50
|
-
base += ` | default:${opts.default}`;
|
|
51
|
-
return new tex_types_1.TexType(base);
|
|
57
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
52
58
|
}
|
|
53
59
|
boolean(opts) {
|
|
54
60
|
let base = 'boolean';
|
|
55
|
-
if (opts?.optional)
|
|
61
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
56
62
|
base += '?';
|
|
63
|
+
if (opts?.nullable)
|
|
64
|
+
base += ' | nullable';
|
|
65
|
+
if (opts?.nullish)
|
|
66
|
+
base += ' | nullish';
|
|
57
67
|
if (opts?.coerce)
|
|
58
68
|
base += ' | coerce';
|
|
59
|
-
return new tex_types_1.TexType(base);
|
|
69
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
60
70
|
}
|
|
61
71
|
email(opts) {
|
|
62
72
|
let base = 'email';
|
|
63
|
-
if (opts?.optional)
|
|
73
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
64
74
|
base += '?';
|
|
75
|
+
if (opts?.nullable)
|
|
76
|
+
base += ' | nullable';
|
|
77
|
+
if (opts?.nullish)
|
|
78
|
+
base += ' | nullish';
|
|
65
79
|
if (opts?.trim)
|
|
66
80
|
base += ' | trim';
|
|
67
81
|
if (opts?.toLowerCase)
|
|
68
82
|
base += ' | lowercase';
|
|
69
83
|
if (opts?.mask)
|
|
70
84
|
base += ' | mask';
|
|
71
|
-
return new tex_types_1.TexType(base);
|
|
85
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
72
86
|
}
|
|
73
87
|
uuid(opts) {
|
|
74
88
|
let base = 'uuid';
|
|
75
|
-
if (opts?.optional)
|
|
89
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
76
90
|
base += '?';
|
|
91
|
+
if (opts?.nullable)
|
|
92
|
+
base += ' | nullable';
|
|
93
|
+
if (opts?.nullish)
|
|
94
|
+
base += ' | nullish';
|
|
77
95
|
if (opts?.version)
|
|
78
96
|
base += ` | version:${opts.version}`;
|
|
79
|
-
return new tex_types_1.TexType(base);
|
|
97
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
80
98
|
}
|
|
81
99
|
cuid(opts) {
|
|
82
100
|
let base = 'cuid';
|
|
83
|
-
if (opts?.optional)
|
|
101
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
84
102
|
base += '?';
|
|
85
|
-
|
|
103
|
+
if (opts?.nullable)
|
|
104
|
+
base += ' | nullable';
|
|
105
|
+
if (opts?.nullish)
|
|
106
|
+
base += ' | nullish';
|
|
107
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
86
108
|
}
|
|
87
109
|
creditCard(opts) {
|
|
88
110
|
let base = 'creditcard';
|
|
89
|
-
if (opts?.optional)
|
|
111
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
90
112
|
base += '?';
|
|
113
|
+
if (opts?.nullable)
|
|
114
|
+
base += ' | nullable';
|
|
115
|
+
if (opts?.nullish)
|
|
116
|
+
base += ' | nullish';
|
|
91
117
|
if (opts?.mask)
|
|
92
118
|
base += ' | mask';
|
|
93
|
-
return new tex_types_1.TexType(base);
|
|
119
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
94
120
|
}
|
|
95
121
|
password(opts) {
|
|
96
122
|
let base = 'password';
|
|
97
|
-
if (opts?.optional)
|
|
123
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
98
124
|
base += '?';
|
|
125
|
+
if (opts?.nullable)
|
|
126
|
+
base += ' | nullable';
|
|
127
|
+
if (opts?.nullish)
|
|
128
|
+
base += ' | nullish';
|
|
99
129
|
if (opts?.min !== undefined)
|
|
100
130
|
base += ` | min:${opts.min}`;
|
|
101
131
|
if (opts?.max !== undefined)
|
|
@@ -108,7 +138,7 @@ class TexBuilder {
|
|
|
108
138
|
base += ' | requireUppercase';
|
|
109
139
|
if (opts?.requireLowercase)
|
|
110
140
|
base += ' | requireLowercase';
|
|
111
|
-
return new tex_types_1.TexType(base);
|
|
141
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
112
142
|
}
|
|
113
143
|
array(schema, opts) {
|
|
114
144
|
let resolvedItem = schema;
|
|
@@ -124,7 +154,7 @@ class TexBuilder {
|
|
|
124
154
|
? resolvedItem._raw
|
|
125
155
|
: resolvedItem;
|
|
126
156
|
let base = `array<${itemSchema}>`;
|
|
127
|
-
if (opts?.optional)
|
|
157
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
128
158
|
base += '?';
|
|
129
159
|
if (opts?.nullable)
|
|
130
160
|
base += ' | nullable';
|
|
@@ -136,22 +166,30 @@ class TexBuilder {
|
|
|
136
166
|
base += ` | max:${opts.max}`;
|
|
137
167
|
if (opts?.dedupe)
|
|
138
168
|
base += ' | dedupe';
|
|
139
|
-
|
|
169
|
+
if (opts?.coerce)
|
|
170
|
+
base += ' | coerce';
|
|
171
|
+
const res = new tex_types_1.TexType(base, opts?.default);
|
|
140
172
|
res._item = resolvedItem;
|
|
141
173
|
return res;
|
|
142
174
|
}
|
|
143
175
|
enum(values, opts) {
|
|
144
176
|
let base = `enum:${values.join(',')}`;
|
|
145
|
-
if (opts?.optional)
|
|
177
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
146
178
|
base += '?';
|
|
147
|
-
if (opts?.
|
|
148
|
-
base +=
|
|
149
|
-
|
|
179
|
+
if (opts?.nullable)
|
|
180
|
+
base += ' | nullable';
|
|
181
|
+
if (opts?.nullish)
|
|
182
|
+
base += ' | nullish';
|
|
183
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
150
184
|
}
|
|
151
185
|
literal(value, opts) {
|
|
152
186
|
let base = `literal:${value}`;
|
|
153
187
|
if (opts?.optional)
|
|
154
188
|
base += '?';
|
|
189
|
+
if (opts?.nullable)
|
|
190
|
+
base += ' | nullable';
|
|
191
|
+
if (opts?.nullish)
|
|
192
|
+
base += ' | nullish';
|
|
155
193
|
return new tex_types_1.TexType(base);
|
|
156
194
|
}
|
|
157
195
|
union(schemas, opts) {
|
|
@@ -159,35 +197,57 @@ class TexBuilder {
|
|
|
159
197
|
let base = items.join(' || ');
|
|
160
198
|
if (opts?.optional)
|
|
161
199
|
base = `(${base})?`;
|
|
200
|
+
if (opts?.nullable)
|
|
201
|
+
base += ' | nullable';
|
|
202
|
+
if (opts?.nullish)
|
|
203
|
+
base += ' | nullish';
|
|
162
204
|
return new tex_types_1.TexType(base);
|
|
163
205
|
}
|
|
164
206
|
date(opts) {
|
|
165
207
|
let base = 'date';
|
|
166
|
-
if (opts?.optional)
|
|
208
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
167
209
|
base += '?';
|
|
210
|
+
if (opts?.nullable)
|
|
211
|
+
base += ' | nullable';
|
|
212
|
+
if (opts?.nullish)
|
|
213
|
+
base += ' | nullish';
|
|
214
|
+
if (opts?.coerce)
|
|
215
|
+
base += ' | coerce';
|
|
168
216
|
if (opts?.minDate)
|
|
169
217
|
base += ` | minDate:${opts.minDate}`;
|
|
170
218
|
if (opts?.maxDate)
|
|
171
219
|
base += ` | maxDate:${opts.maxDate}`;
|
|
172
|
-
return new tex_types_1.TexType(base);
|
|
220
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
173
221
|
}
|
|
174
222
|
record(schema, opts) {
|
|
175
223
|
const itemSchema = schema instanceof tex_types_1.TexType ? schema._raw : schema;
|
|
176
224
|
let base = `record<${itemSchema}>`;
|
|
177
225
|
if (opts?.optional)
|
|
178
226
|
base += '?';
|
|
227
|
+
if (opts?.nullable)
|
|
228
|
+
base += ' | nullable';
|
|
229
|
+
if (opts?.nullish)
|
|
230
|
+
base += ' | nullish';
|
|
179
231
|
return new tex_types_1.TexType(base);
|
|
180
232
|
}
|
|
181
233
|
any(opts) {
|
|
182
234
|
let base = 'any';
|
|
183
235
|
if (opts?.optional)
|
|
184
236
|
base += '?';
|
|
237
|
+
if (opts?.nullable)
|
|
238
|
+
base += ' | nullable';
|
|
239
|
+
if (opts?.nullish)
|
|
240
|
+
base += ' | nullish';
|
|
185
241
|
return new tex_types_1.TexType(base);
|
|
186
242
|
}
|
|
187
243
|
file(opts) {
|
|
188
244
|
let base = 'file';
|
|
189
245
|
if (opts?.optional)
|
|
190
246
|
base += '?';
|
|
247
|
+
if (opts?.nullable)
|
|
248
|
+
base += ' | nullable';
|
|
249
|
+
if (opts?.nullish)
|
|
250
|
+
base += ' | nullish';
|
|
191
251
|
if (opts?.maxSize)
|
|
192
252
|
base += ` | maxSize:${opts.maxSize}`;
|
|
193
253
|
if (opts?.mimeTypes)
|
|
@@ -210,6 +270,53 @@ class TexBuilder {
|
|
|
210
270
|
}
|
|
211
271
|
return new TexEngine(compiledSchema, schema, opts?.strict);
|
|
212
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Dedicated Environment Schema parser (tex.env).
|
|
275
|
+
* Automatically coerces numbers, booleans, dates, and arrays from process.env string values,
|
|
276
|
+
* while preserving strict type validation and defaults.
|
|
277
|
+
*/
|
|
278
|
+
env(schema, opts) {
|
|
279
|
+
const coercedSchema = {};
|
|
280
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
281
|
+
if (value instanceof tex_types_1.TexType) {
|
|
282
|
+
let raw = value._raw;
|
|
283
|
+
// Auto-inject coercion if not already present
|
|
284
|
+
if ((raw.startsWith('number') ||
|
|
285
|
+
raw.startsWith('boolean') ||
|
|
286
|
+
raw.startsWith('date') ||
|
|
287
|
+
raw.startsWith('array<')) &&
|
|
288
|
+
!raw.includes('coerce')) {
|
|
289
|
+
raw = `${raw} | coerce`;
|
|
290
|
+
}
|
|
291
|
+
const cloned = new tex_types_1.TexType(raw, value.defaultValue);
|
|
292
|
+
cloned.sanitizers = [...value.sanitizers];
|
|
293
|
+
cloned.refinements = [...value.refinements];
|
|
294
|
+
if (value._item) {
|
|
295
|
+
;
|
|
296
|
+
cloned._item = value._item;
|
|
297
|
+
}
|
|
298
|
+
coercedSchema[key] = cloned;
|
|
299
|
+
}
|
|
300
|
+
else if (value instanceof TexEngine) {
|
|
301
|
+
coercedSchema[key] = value;
|
|
302
|
+
}
|
|
303
|
+
else if (typeof value === 'string') {
|
|
304
|
+
let raw = value;
|
|
305
|
+
if ((raw.startsWith('number') ||
|
|
306
|
+
raw.startsWith('boolean') ||
|
|
307
|
+
raw.startsWith('date') ||
|
|
308
|
+
raw.startsWith('array<')) &&
|
|
309
|
+
!raw.includes('coerce')) {
|
|
310
|
+
raw = `${raw} | coerce`;
|
|
311
|
+
}
|
|
312
|
+
coercedSchema[key] = new tex_types_1.TexType(raw);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
coercedSchema[key] = value;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return this.object(coercedSchema, opts);
|
|
319
|
+
}
|
|
213
320
|
pagination(options) {
|
|
214
321
|
const defaultLimit = options?.defaultLimit ?? 10;
|
|
215
322
|
const maxLimit = options?.maxLimit ?? 100;
|
|
@@ -228,12 +335,60 @@ class TexBuilder {
|
|
|
228
335
|
}
|
|
229
336
|
}
|
|
230
337
|
exports.TexBuilder = TexBuilder;
|
|
338
|
+
function getNestedValue(obj, path) {
|
|
339
|
+
if (!obj || typeof obj !== 'object' || !path)
|
|
340
|
+
return undefined;
|
|
341
|
+
if (path in obj)
|
|
342
|
+
return obj[path];
|
|
343
|
+
try {
|
|
344
|
+
const keys = path.replace(/\[(\w+)\]/g, '.$1').split('.');
|
|
345
|
+
let curr = obj;
|
|
346
|
+
for (const k of keys) {
|
|
347
|
+
if (curr === undefined || curr === null)
|
|
348
|
+
return undefined;
|
|
349
|
+
curr = curr[k];
|
|
350
|
+
}
|
|
351
|
+
return curr;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function formatReceivedValue(val) {
|
|
358
|
+
if (val === undefined)
|
|
359
|
+
return 'undefined';
|
|
360
|
+
if (val === null)
|
|
361
|
+
return 'null';
|
|
362
|
+
if (typeof val === 'string')
|
|
363
|
+
return JSON.stringify(val);
|
|
364
|
+
if (typeof val === 'number' || typeof val === 'boolean')
|
|
365
|
+
return String(val);
|
|
366
|
+
if (Array.isArray(val)) {
|
|
367
|
+
try {
|
|
368
|
+
const str = JSON.stringify(val);
|
|
369
|
+
return str.length > 30 ? `Array(${val.length})` : str;
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return `Array(${val.length})`;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (typeof val === 'object') {
|
|
376
|
+
try {
|
|
377
|
+
const str = JSON.stringify(val);
|
|
378
|
+
return str.length > 30 ? '[object Object]' : str;
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return '[object Object]';
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return String(val);
|
|
385
|
+
}
|
|
231
386
|
class ValidatorError extends Error {
|
|
232
387
|
constructor(errors) {
|
|
233
388
|
super('Validation Error: ' +
|
|
234
389
|
errors.map((e) => `${e.path}: ${e.message}`).join(', '));
|
|
235
|
-
this.errors = errors;
|
|
236
390
|
this.name = 'ValidatorError';
|
|
391
|
+
this.errors = errors;
|
|
237
392
|
}
|
|
238
393
|
}
|
|
239
394
|
exports.ValidatorError = ValidatorError;
|
|
@@ -295,9 +450,18 @@ class TexEngine {
|
|
|
295
450
|
return new TexEngine(newSchema, newRaw, this.strict, this._isOptional);
|
|
296
451
|
}
|
|
297
452
|
parse(data) {
|
|
298
|
-
// 1.
|
|
453
|
+
// 1. Apply defaults and run pre-validation sanitizers on the raw payload (if it's an object)
|
|
299
454
|
if (data && typeof data === 'object') {
|
|
300
455
|
for (const [key, val] of Object.entries(this.rawSchema)) {
|
|
456
|
+
// Apply default value / lazy factory function if field is omitted or undefined
|
|
457
|
+
if (data[key] === undefined) {
|
|
458
|
+
if (val instanceof tex_types_1.TexType && val.defaultValue !== undefined) {
|
|
459
|
+
data[key] =
|
|
460
|
+
typeof val.defaultValue === 'function'
|
|
461
|
+
? val.defaultValue()
|
|
462
|
+
: val.defaultValue;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
301
465
|
if (typeof data[key] === 'string' && data[key].trim() === '') {
|
|
302
466
|
const raw = val instanceof tex_types_1.TexType ? val._raw : String(val);
|
|
303
467
|
if (raw.includes('optional') ||
|
|
@@ -312,12 +476,149 @@ class TexEngine {
|
|
|
312
476
|
}
|
|
313
477
|
else {
|
|
314
478
|
delete data[key];
|
|
479
|
+
// If deleted but default is defined, apply default
|
|
480
|
+
if (val instanceof tex_types_1.TexType && val.defaultValue !== undefined) {
|
|
481
|
+
data[key] =
|
|
482
|
+
typeof val.defaultValue === 'function'
|
|
483
|
+
? val.defaultValue()
|
|
484
|
+
: val.defaultValue;
|
|
485
|
+
}
|
|
315
486
|
}
|
|
316
487
|
}
|
|
317
488
|
}
|
|
318
489
|
if (data[key] !== undefined && data[key] !== null) {
|
|
490
|
+
if (val instanceof tex_types_1.TexType && val._raw.startsWith('date')) {
|
|
491
|
+
if (val._raw.includes('coerce')) {
|
|
492
|
+
if (typeof data[key] === 'string' ||
|
|
493
|
+
typeof data[key] === 'number') {
|
|
494
|
+
const d = new Date(data[key]);
|
|
495
|
+
if (isNaN(d.getTime())) {
|
|
496
|
+
throw new ValidatorError([
|
|
497
|
+
{
|
|
498
|
+
path: key,
|
|
499
|
+
message: 'Must be a valid date',
|
|
500
|
+
expected: 'valid date string or timestamp',
|
|
501
|
+
received: formatReceivedValue(data[key]),
|
|
502
|
+
code: 'INVALID_DATE',
|
|
503
|
+
},
|
|
504
|
+
]);
|
|
505
|
+
}
|
|
506
|
+
data[key] = d;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (!(data[key] instanceof Date) &&
|
|
510
|
+
typeof data[key] !== 'string' &&
|
|
511
|
+
typeof data[key] !== 'number') {
|
|
512
|
+
throw new ValidatorError([
|
|
513
|
+
{
|
|
514
|
+
path: key,
|
|
515
|
+
message: 'Must be a valid date',
|
|
516
|
+
expected: 'valid date or date string',
|
|
517
|
+
received: formatReceivedValue(data[key]),
|
|
518
|
+
code: 'INVALID_TYPE',
|
|
519
|
+
},
|
|
520
|
+
]);
|
|
521
|
+
}
|
|
522
|
+
if (data[key] instanceof Date && isNaN(data[key].getTime())) {
|
|
523
|
+
throw new ValidatorError([
|
|
524
|
+
{
|
|
525
|
+
path: key,
|
|
526
|
+
message: 'Must be a valid date',
|
|
527
|
+
expected: 'valid date string or timestamp',
|
|
528
|
+
received: formatReceivedValue(data[key]),
|
|
529
|
+
code: 'INVALID_DATE',
|
|
530
|
+
},
|
|
531
|
+
]);
|
|
532
|
+
}
|
|
533
|
+
if (val._raw.includes('minDate:')) {
|
|
534
|
+
const minStr = val._raw.match(/minDate:([^\s|]+)/)?.[1];
|
|
535
|
+
if (minStr &&
|
|
536
|
+
new Date(data[key]).getTime() < new Date(minStr).getTime()) {
|
|
537
|
+
throw new ValidatorError([
|
|
538
|
+
{
|
|
539
|
+
path: key,
|
|
540
|
+
message: `Date must be after ${minStr}`,
|
|
541
|
+
expected: `>= ${minStr}`,
|
|
542
|
+
received: formatReceivedValue(data[key]),
|
|
543
|
+
code: 'DATE_TOO_EARLY',
|
|
544
|
+
},
|
|
545
|
+
]);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (val._raw.includes('maxDate:')) {
|
|
549
|
+
const maxStr = val._raw.match(/maxDate:([^\s|]+)/)?.[1];
|
|
550
|
+
if (maxStr &&
|
|
551
|
+
new Date(data[key]).getTime() > new Date(maxStr).getTime()) {
|
|
552
|
+
throw new ValidatorError([
|
|
553
|
+
{
|
|
554
|
+
path: key,
|
|
555
|
+
message: `Date must be before ${maxStr}`,
|
|
556
|
+
expected: `<= ${maxStr}`,
|
|
557
|
+
received: formatReceivedValue(data[key]),
|
|
558
|
+
code: 'DATE_TOO_LATE',
|
|
559
|
+
},
|
|
560
|
+
]);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (val instanceof tex_types_1.TexType &&
|
|
565
|
+
val._raw.startsWith('array<') &&
|
|
566
|
+
val._raw.includes('coerce')) {
|
|
567
|
+
if (!Array.isArray(data[key])) {
|
|
568
|
+
if (typeof data[key] === 'string') {
|
|
569
|
+
const parts = data[key].includes(',')
|
|
570
|
+
? data[key].split(',').map((s) => s.trim())
|
|
571
|
+
: [data[key]];
|
|
572
|
+
if (val._raw.startsWith('array<number')) {
|
|
573
|
+
data[key] = parts.map((p) => {
|
|
574
|
+
const n = Number(p);
|
|
575
|
+
return isNaN(n) ? p : n;
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else if (val._raw.startsWith('array<boolean')) {
|
|
579
|
+
data[key] = parts.map((p) => {
|
|
580
|
+
if (p.toLowerCase() === 'true')
|
|
581
|
+
return true;
|
|
582
|
+
if (p.toLowerCase() === 'false')
|
|
583
|
+
return false;
|
|
584
|
+
return p;
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
data[key] = parts;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
data[key] = [data[key]];
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
if (val._raw.startsWith('array<number')) {
|
|
597
|
+
data[key] = data[key].map((item) => {
|
|
598
|
+
if (typeof item === 'string') {
|
|
599
|
+
const n = Number(item);
|
|
600
|
+
return isNaN(n) ? item : n;
|
|
601
|
+
}
|
|
602
|
+
return item;
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
else if (val._raw.startsWith('array<boolean')) {
|
|
606
|
+
data[key] = data[key].map((item) => {
|
|
607
|
+
if (typeof item === 'string') {
|
|
608
|
+
if (item.toLowerCase() === 'true')
|
|
609
|
+
return true;
|
|
610
|
+
if (item.toLowerCase() === 'false')
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
return item;
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
319
618
|
if (val instanceof tex_types_1.TexType && val.sanitizers.length > 0) {
|
|
320
619
|
for (const s of val.sanitizers) {
|
|
620
|
+
if (data[key] === undefined || data[key] === null)
|
|
621
|
+
break;
|
|
321
622
|
data[key] = s(data[key]);
|
|
322
623
|
}
|
|
323
624
|
}
|
|
@@ -326,8 +627,12 @@ class TexEngine {
|
|
|
326
627
|
if (itemSchema instanceof tex_types_1.TexType &&
|
|
327
628
|
itemSchema.sanitizers.length > 0) {
|
|
328
629
|
for (let i = 0; i < data[key].length; i++) {
|
|
329
|
-
|
|
330
|
-
|
|
630
|
+
if (data[key][i] !== undefined && data[key][i] !== null) {
|
|
631
|
+
for (const s of itemSchema.sanitizers) {
|
|
632
|
+
if (data[key][i] === undefined || data[key][i] === null)
|
|
633
|
+
break;
|
|
634
|
+
data[key][i] = s(data[key][i]);
|
|
635
|
+
}
|
|
331
636
|
}
|
|
332
637
|
}
|
|
333
638
|
}
|
|
@@ -341,40 +646,164 @@ class TexEngine {
|
|
|
341
646
|
parsedData = this.validator.parse(data);
|
|
342
647
|
}
|
|
343
648
|
catch (err) {
|
|
649
|
+
if (data === process.env &&
|
|
650
|
+
(process.env.__EXIS_SKIP_ENV_CHECK === 'true' ||
|
|
651
|
+
process.env.EXIS_SKIP_ENV_CHECK === 'true')) {
|
|
652
|
+
const mock = { ...data };
|
|
653
|
+
for (const [key, val] of Object.entries(this.rawSchema)) {
|
|
654
|
+
if (mock[key] === undefined) {
|
|
655
|
+
const raw = val instanceof tex_types_1.TexType ? val._raw : String(val);
|
|
656
|
+
if (raw.includes('number'))
|
|
657
|
+
mock[key] = 0;
|
|
658
|
+
else if (raw.includes('boolean'))
|
|
659
|
+
mock[key] = false;
|
|
660
|
+
else if (raw.includes('array'))
|
|
661
|
+
mock[key] = [];
|
|
662
|
+
else
|
|
663
|
+
mock[key] = `mock_${key}`;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return mock;
|
|
667
|
+
}
|
|
344
668
|
let path = 'root';
|
|
345
669
|
let message = err.message || 'Validation failed';
|
|
670
|
+
let expected;
|
|
671
|
+
let code = 'VALIDATION_ERROR';
|
|
346
672
|
if (message.startsWith('Missing required field: ')) {
|
|
347
673
|
path = message.replace('Missing required field: ', '').trim();
|
|
348
674
|
message = 'Expected value, received undefined';
|
|
675
|
+
expected = 'defined';
|
|
676
|
+
code = 'MISSING_REQUIRED_FIELD';
|
|
349
677
|
}
|
|
350
678
|
else if (message.startsWith("Field '")) {
|
|
351
679
|
const match = message.match(/Field '([^']+)' (.+)/);
|
|
352
680
|
if (match) {
|
|
353
681
|
path = match[1];
|
|
354
|
-
|
|
682
|
+
const constraint = match[2];
|
|
683
|
+
message = constraint.charAt(0).toUpperCase() + constraint.slice(1);
|
|
684
|
+
if (constraint.startsWith('must be a string')) {
|
|
685
|
+
expected = 'string';
|
|
686
|
+
code = 'INVALID_TYPE';
|
|
687
|
+
}
|
|
688
|
+
else if (constraint.startsWith('must be a number')) {
|
|
689
|
+
expected = 'number';
|
|
690
|
+
code = 'INVALID_TYPE';
|
|
691
|
+
}
|
|
692
|
+
else if (constraint.startsWith('must be a boolean')) {
|
|
693
|
+
expected = 'boolean';
|
|
694
|
+
code = 'INVALID_TYPE';
|
|
695
|
+
}
|
|
696
|
+
else if (constraint.startsWith('must be an array') ||
|
|
697
|
+
constraint.includes('array must have') ||
|
|
698
|
+
constraint.includes('array exceeds')) {
|
|
699
|
+
expected = 'array';
|
|
700
|
+
code = constraint.includes('array must have')
|
|
701
|
+
? 'ARRAY_TOO_SHORT'
|
|
702
|
+
: constraint.includes('array exceeds')
|
|
703
|
+
? 'ARRAY_TOO_LONG'
|
|
704
|
+
: 'INVALID_TYPE';
|
|
705
|
+
}
|
|
706
|
+
else if (constraint.startsWith('must be an object')) {
|
|
707
|
+
expected = 'object';
|
|
708
|
+
code = 'INVALID_TYPE';
|
|
709
|
+
}
|
|
710
|
+
else if (constraint.startsWith('must be a valid email')) {
|
|
711
|
+
expected = 'valid email address';
|
|
712
|
+
code = 'INVALID_EMAIL';
|
|
713
|
+
}
|
|
714
|
+
else if (constraint.startsWith('must be one of')) {
|
|
715
|
+
expected = constraint.replace('must be one of', '').trim();
|
|
716
|
+
code = 'INVALID_ENUM_VALUE';
|
|
717
|
+
}
|
|
718
|
+
else if (constraint.startsWith('must be at least') ||
|
|
719
|
+
constraint.startsWith('must be >=')) {
|
|
720
|
+
expected = constraint;
|
|
721
|
+
code = 'VALUE_TOO_SMALL';
|
|
722
|
+
}
|
|
723
|
+
else if (constraint.startsWith('must be at most') ||
|
|
724
|
+
constraint.startsWith('must be <=')) {
|
|
725
|
+
expected = constraint;
|
|
726
|
+
code = 'VALUE_TOO_LARGE';
|
|
727
|
+
}
|
|
728
|
+
else if (constraint.startsWith('must contain a number')) {
|
|
729
|
+
expected = 'must contain at least one digit';
|
|
730
|
+
code = 'PASSWORD_REQUIRES_NUMBER';
|
|
731
|
+
}
|
|
732
|
+
else if (constraint.startsWith('must contain an uppercase')) {
|
|
733
|
+
expected = 'must contain at least one uppercase letter';
|
|
734
|
+
code = 'PASSWORD_REQUIRES_UPPERCASE';
|
|
735
|
+
}
|
|
736
|
+
else if (constraint.startsWith('must contain a lowercase')) {
|
|
737
|
+
expected = 'must contain at least one lowercase letter';
|
|
738
|
+
code = 'PASSWORD_REQUIRES_LOWERCASE';
|
|
739
|
+
}
|
|
740
|
+
else if (constraint.startsWith('must contain a symbol')) {
|
|
741
|
+
expected = 'must contain at least one symbol';
|
|
742
|
+
code = 'PASSWORD_REQUIRES_SYMBOL';
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
expected = constraint;
|
|
746
|
+
}
|
|
355
747
|
}
|
|
356
748
|
}
|
|
357
749
|
else if (message.startsWith("Strict mode error: Unknown field '")) {
|
|
358
|
-
const match = message.match(/Strict mode error: Unknown field '([^']+)'
|
|
750
|
+
const match = message.match(/Strict mode error: Unknown field '([^']+)'(?: at path '([^']*)')?/);
|
|
359
751
|
if (match) {
|
|
360
|
-
|
|
752
|
+
const key = match[1];
|
|
753
|
+
const parentPath = match[2];
|
|
754
|
+
path =
|
|
755
|
+
parentPath && parentPath.length > 0 ? `${parentPath}.${key}` : key;
|
|
361
756
|
message = 'Unknown field not allowed in strict mode';
|
|
757
|
+
expected = 'undefined (field omitted)';
|
|
758
|
+
code = 'UNRECOGNIZED_KEYS';
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const receivedRaw = getNestedValue(data, path);
|
|
762
|
+
const received = formatReceivedValue(receivedRaw);
|
|
763
|
+
throw new ValidatorError([{ path, message, expected, received, code }]);
|
|
764
|
+
}
|
|
765
|
+
// Preserve Date objects and ensure defaults are applied
|
|
766
|
+
if (parsedData && typeof parsedData === 'object') {
|
|
767
|
+
for (const [key, val] of Object.entries(this.rawSchema)) {
|
|
768
|
+
if (parsedData[key] === undefined &&
|
|
769
|
+
val instanceof tex_types_1.TexType &&
|
|
770
|
+
val.defaultValue !== undefined) {
|
|
771
|
+
parsedData[key] =
|
|
772
|
+
typeof val.defaultValue === 'function'
|
|
773
|
+
? val.defaultValue()
|
|
774
|
+
: val.defaultValue;
|
|
775
|
+
}
|
|
776
|
+
if (val instanceof tex_types_1.TexType &&
|
|
777
|
+
val._raw.startsWith('date') &&
|
|
778
|
+
data &&
|
|
779
|
+
data[key] instanceof Date) {
|
|
780
|
+
parsedData[key] = data[key];
|
|
362
781
|
}
|
|
363
782
|
}
|
|
364
|
-
throw new ValidatorError([{ path, message }]);
|
|
365
783
|
}
|
|
366
784
|
// 3. Post-validation synchronous refinements
|
|
367
785
|
const errors = [];
|
|
368
786
|
if (parsedData && typeof parsedData === 'object') {
|
|
369
787
|
for (const [key, val] of Object.entries(this.rawSchema)) {
|
|
370
788
|
if (parsedData[key] !== undefined) {
|
|
789
|
+
const isNullableField = val instanceof tex_types_1.TexType &&
|
|
790
|
+
(val._raw.includes('nullable') || val._raw.includes('nullish'));
|
|
791
|
+
if (parsedData[key] === null && isNullableField) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
371
794
|
if (val instanceof tex_types_1.TexType && val.refinements.length > 0) {
|
|
372
795
|
for (const r of val.refinements) {
|
|
373
796
|
if (!r.async && !r.fn(parsedData[key])) {
|
|
374
797
|
const msg = typeof r.message === 'function'
|
|
375
798
|
? r.message(parsedData[key])
|
|
376
799
|
: r.message || 'Invalid value';
|
|
377
|
-
errors.push({
|
|
800
|
+
errors.push({
|
|
801
|
+
path: key,
|
|
802
|
+
message: msg,
|
|
803
|
+
expected: 'custom refinement rule',
|
|
804
|
+
received: formatReceivedValue(parsedData[key]),
|
|
805
|
+
code: 'CUSTOM_VALIDATION',
|
|
806
|
+
});
|
|
378
807
|
}
|
|
379
808
|
}
|
|
380
809
|
}
|
|
@@ -382,13 +811,23 @@ class TexEngine {
|
|
|
382
811
|
if (itemSchema && Array.isArray(parsedData[key])) {
|
|
383
812
|
if (itemSchema instanceof tex_types_1.TexType &&
|
|
384
813
|
itemSchema.refinements.length > 0) {
|
|
814
|
+
const isItemNullable = itemSchema._raw.includes('nullable') ||
|
|
815
|
+
itemSchema._raw.includes('nullish');
|
|
385
816
|
for (let i = 0; i < parsedData[key].length; i++) {
|
|
817
|
+
if (parsedData[key][i] === null && isItemNullable)
|
|
818
|
+
continue;
|
|
386
819
|
for (const r of itemSchema.refinements) {
|
|
387
820
|
if (!r.async && !r.fn(parsedData[key][i])) {
|
|
388
821
|
const msg = typeof r.message === 'function'
|
|
389
822
|
? r.message(parsedData[key][i])
|
|
390
823
|
: r.message || 'Invalid value';
|
|
391
|
-
errors.push({
|
|
824
|
+
errors.push({
|
|
825
|
+
path: `${key}[${i}]`,
|
|
826
|
+
message: msg,
|
|
827
|
+
expected: 'custom refinement rule',
|
|
828
|
+
received: formatReceivedValue(parsedData[key][i]),
|
|
829
|
+
code: 'CUSTOM_VALIDATION',
|
|
830
|
+
});
|
|
392
831
|
}
|
|
393
832
|
}
|
|
394
833
|
}
|
|
@@ -406,15 +845,26 @@ class TexEngine {
|
|
|
406
845
|
const errors = [];
|
|
407
846
|
if (parsed && typeof parsed === 'object') {
|
|
408
847
|
await Promise.all(Object.entries(this.rawSchema).map(async ([key, val]) => {
|
|
409
|
-
|
|
848
|
+
const valData = parsed[key];
|
|
849
|
+
const isNullableField = val instanceof tex_types_1.TexType &&
|
|
850
|
+
(val._raw.includes('nullable') || val._raw.includes('nullish'));
|
|
851
|
+
if (valData === null && isNullableField)
|
|
852
|
+
return;
|
|
853
|
+
if (valData !== undefined &&
|
|
410
854
|
val instanceof tex_types_1.TexType &&
|
|
411
855
|
val.refinements.length > 0) {
|
|
412
856
|
for (const r of val.refinements) {
|
|
413
|
-
if (r.async && !(await r.fn(
|
|
857
|
+
if (r.async && !(await r.fn(valData))) {
|
|
414
858
|
const msg = typeof r.message === 'function'
|
|
415
|
-
? r.message(
|
|
859
|
+
? r.message(valData)
|
|
416
860
|
: r.message || 'Invalid value';
|
|
417
|
-
errors.push({
|
|
861
|
+
errors.push({
|
|
862
|
+
path: key,
|
|
863
|
+
message: msg,
|
|
864
|
+
expected: 'custom async refinement rule',
|
|
865
|
+
received: formatReceivedValue(valData),
|
|
866
|
+
code: 'CUSTOM_VALIDATION',
|
|
867
|
+
});
|
|
418
868
|
}
|
|
419
869
|
}
|
|
420
870
|
}
|