exisjs 0.7.5 → 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/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +156 -85
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +54 -16
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/index.js +10 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +106 -13
- 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/types.d.ts +2 -0
- package/dist/config/types.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/cli/commands.mdx +1 -1
- package/dist/docs/content/controllers.mdx +127 -10
- package/dist/docs/content/decorators.mdx +22 -4
- package/dist/docs/content/introduction.mdx +7 -7
- package/dist/docs/content/middleware.mdx +269 -6
- package/dist/docs/content/requests.mdx +180 -6
- package/dist/docs/content/responses.mdx +283 -32
- package/dist/docs/content/routing.mdx +46 -0
- package/dist/docs/content/validation.mdx +190 -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 +33 -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/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 +17 -9
- 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.map +1 -1
- package/dist/router/route-scanner.js +24 -2
- 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 +8 -0
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +1 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +10 -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/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 +27 -17
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +18 -0
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +10 -1
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +154 -2
- 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 +31 -15
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +280 -42
- package/dist/validator/tex.js.map +1 -1
- package/package.json +4 -4
- 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,7 +8,7 @@ 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
13
|
if (opts?.nullable)
|
|
14
14
|
base += ' | nullable';
|
|
@@ -38,11 +38,11 @@ class TexBuilder {
|
|
|
38
38
|
base += ' | preventSql';
|
|
39
39
|
if (opts?.preventTraversal)
|
|
40
40
|
base += ' | preventTraversal';
|
|
41
|
-
return new tex_types_1.TexType(base);
|
|
41
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
42
42
|
}
|
|
43
43
|
number(opts) {
|
|
44
44
|
let base = 'number';
|
|
45
|
-
if (opts?.optional)
|
|
45
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
46
46
|
base += '?';
|
|
47
47
|
if (opts?.nullable)
|
|
48
48
|
base += ' | nullable';
|
|
@@ -54,13 +54,11 @@ class TexBuilder {
|
|
|
54
54
|
base += ` | min:${opts.min}`;
|
|
55
55
|
if (opts?.max !== undefined)
|
|
56
56
|
base += ` | max:${opts.max}`;
|
|
57
|
-
|
|
58
|
-
base += ` | default:${opts.default}`;
|
|
59
|
-
return new tex_types_1.TexType(base);
|
|
57
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
60
58
|
}
|
|
61
59
|
boolean(opts) {
|
|
62
60
|
let base = 'boolean';
|
|
63
|
-
if (opts?.optional)
|
|
61
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
64
62
|
base += '?';
|
|
65
63
|
if (opts?.nullable)
|
|
66
64
|
base += ' | nullable';
|
|
@@ -68,11 +66,11 @@ class TexBuilder {
|
|
|
68
66
|
base += ' | nullish';
|
|
69
67
|
if (opts?.coerce)
|
|
70
68
|
base += ' | coerce';
|
|
71
|
-
return new tex_types_1.TexType(base);
|
|
69
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
72
70
|
}
|
|
73
71
|
email(opts) {
|
|
74
72
|
let base = 'email';
|
|
75
|
-
if (opts?.optional)
|
|
73
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
76
74
|
base += '?';
|
|
77
75
|
if (opts?.nullable)
|
|
78
76
|
base += ' | nullable';
|
|
@@ -84,11 +82,11 @@ class TexBuilder {
|
|
|
84
82
|
base += ' | lowercase';
|
|
85
83
|
if (opts?.mask)
|
|
86
84
|
base += ' | mask';
|
|
87
|
-
return new tex_types_1.TexType(base);
|
|
85
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
88
86
|
}
|
|
89
87
|
uuid(opts) {
|
|
90
88
|
let base = 'uuid';
|
|
91
|
-
if (opts?.optional)
|
|
89
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
92
90
|
base += '?';
|
|
93
91
|
if (opts?.nullable)
|
|
94
92
|
base += ' | nullable';
|
|
@@ -96,21 +94,21 @@ class TexBuilder {
|
|
|
96
94
|
base += ' | nullish';
|
|
97
95
|
if (opts?.version)
|
|
98
96
|
base += ` | version:${opts.version}`;
|
|
99
|
-
return new tex_types_1.TexType(base);
|
|
97
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
100
98
|
}
|
|
101
99
|
cuid(opts) {
|
|
102
100
|
let base = 'cuid';
|
|
103
|
-
if (opts?.optional)
|
|
101
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
104
102
|
base += '?';
|
|
105
103
|
if (opts?.nullable)
|
|
106
104
|
base += ' | nullable';
|
|
107
105
|
if (opts?.nullish)
|
|
108
106
|
base += ' | nullish';
|
|
109
|
-
return new tex_types_1.TexType(base);
|
|
107
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
110
108
|
}
|
|
111
109
|
creditCard(opts) {
|
|
112
110
|
let base = 'creditcard';
|
|
113
|
-
if (opts?.optional)
|
|
111
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
114
112
|
base += '?';
|
|
115
113
|
if (opts?.nullable)
|
|
116
114
|
base += ' | nullable';
|
|
@@ -118,11 +116,11 @@ class TexBuilder {
|
|
|
118
116
|
base += ' | nullish';
|
|
119
117
|
if (opts?.mask)
|
|
120
118
|
base += ' | mask';
|
|
121
|
-
return new tex_types_1.TexType(base);
|
|
119
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
122
120
|
}
|
|
123
121
|
password(opts) {
|
|
124
122
|
let base = 'password';
|
|
125
|
-
if (opts?.optional)
|
|
123
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
126
124
|
base += '?';
|
|
127
125
|
if (opts?.nullable)
|
|
128
126
|
base += ' | nullable';
|
|
@@ -140,7 +138,7 @@ class TexBuilder {
|
|
|
140
138
|
base += ' | requireUppercase';
|
|
141
139
|
if (opts?.requireLowercase)
|
|
142
140
|
base += ' | requireLowercase';
|
|
143
|
-
return new tex_types_1.TexType(base);
|
|
141
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
144
142
|
}
|
|
145
143
|
array(schema, opts) {
|
|
146
144
|
let resolvedItem = schema;
|
|
@@ -156,7 +154,7 @@ class TexBuilder {
|
|
|
156
154
|
? resolvedItem._raw
|
|
157
155
|
: resolvedItem;
|
|
158
156
|
let base = `array<${itemSchema}>`;
|
|
159
|
-
if (opts?.optional)
|
|
157
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
160
158
|
base += '?';
|
|
161
159
|
if (opts?.nullable)
|
|
162
160
|
base += ' | nullable';
|
|
@@ -170,21 +168,19 @@ class TexBuilder {
|
|
|
170
168
|
base += ' | dedupe';
|
|
171
169
|
if (opts?.coerce)
|
|
172
170
|
base += ' | coerce';
|
|
173
|
-
const res = new tex_types_1.TexType(base);
|
|
171
|
+
const res = new tex_types_1.TexType(base, opts?.default);
|
|
174
172
|
res._item = resolvedItem;
|
|
175
173
|
return res;
|
|
176
174
|
}
|
|
177
175
|
enum(values, opts) {
|
|
178
176
|
let base = `enum:${values.join(',')}`;
|
|
179
|
-
if (opts?.optional)
|
|
177
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
180
178
|
base += '?';
|
|
181
179
|
if (opts?.nullable)
|
|
182
180
|
base += ' | nullable';
|
|
183
181
|
if (opts?.nullish)
|
|
184
182
|
base += ' | nullish';
|
|
185
|
-
|
|
186
|
-
base += ` | default:${opts.default}`;
|
|
187
|
-
return new tex_types_1.TexType(base);
|
|
183
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
188
184
|
}
|
|
189
185
|
literal(value, opts) {
|
|
190
186
|
let base = `literal:${value}`;
|
|
@@ -209,7 +205,7 @@ class TexBuilder {
|
|
|
209
205
|
}
|
|
210
206
|
date(opts) {
|
|
211
207
|
let base = 'date';
|
|
212
|
-
if (opts?.optional)
|
|
208
|
+
if (opts?.optional || opts?.default !== undefined)
|
|
213
209
|
base += '?';
|
|
214
210
|
if (opts?.nullable)
|
|
215
211
|
base += ' | nullable';
|
|
@@ -221,7 +217,7 @@ class TexBuilder {
|
|
|
221
217
|
base += ` | minDate:${opts.minDate}`;
|
|
222
218
|
if (opts?.maxDate)
|
|
223
219
|
base += ` | maxDate:${opts.maxDate}`;
|
|
224
|
-
return new tex_types_1.TexType(base);
|
|
220
|
+
return new tex_types_1.TexType(base, opts?.default);
|
|
225
221
|
}
|
|
226
222
|
record(schema, opts) {
|
|
227
223
|
const itemSchema = schema instanceof tex_types_1.TexType ? schema._raw : schema;
|
|
@@ -274,6 +270,53 @@ class TexBuilder {
|
|
|
274
270
|
}
|
|
275
271
|
return new TexEngine(compiledSchema, schema, opts?.strict);
|
|
276
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
|
+
}
|
|
277
320
|
pagination(options) {
|
|
278
321
|
const defaultLimit = options?.defaultLimit ?? 10;
|
|
279
322
|
const maxLimit = options?.maxLimit ?? 100;
|
|
@@ -292,12 +335,60 @@ class TexBuilder {
|
|
|
292
335
|
}
|
|
293
336
|
}
|
|
294
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
|
+
}
|
|
295
386
|
class ValidatorError extends Error {
|
|
296
387
|
constructor(errors) {
|
|
297
388
|
super('Validation Error: ' +
|
|
298
389
|
errors.map((e) => `${e.path}: ${e.message}`).join(', '));
|
|
299
|
-
this.errors = errors;
|
|
300
390
|
this.name = 'ValidatorError';
|
|
391
|
+
this.errors = errors;
|
|
301
392
|
}
|
|
302
393
|
}
|
|
303
394
|
exports.ValidatorError = ValidatorError;
|
|
@@ -359,9 +450,18 @@ class TexEngine {
|
|
|
359
450
|
return new TexEngine(newSchema, newRaw, this.strict, this._isOptional);
|
|
360
451
|
}
|
|
361
452
|
parse(data) {
|
|
362
|
-
// 1.
|
|
453
|
+
// 1. Apply defaults and run pre-validation sanitizers on the raw payload (if it's an object)
|
|
363
454
|
if (data && typeof data === 'object') {
|
|
364
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
|
+
}
|
|
365
465
|
if (typeof data[key] === 'string' && data[key].trim() === '') {
|
|
366
466
|
const raw = val instanceof tex_types_1.TexType ? val._raw : String(val);
|
|
367
467
|
if (raw.includes('optional') ||
|
|
@@ -376,6 +476,13 @@ class TexEngine {
|
|
|
376
476
|
}
|
|
377
477
|
else {
|
|
378
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
|
+
}
|
|
379
486
|
}
|
|
380
487
|
}
|
|
381
488
|
}
|
|
@@ -387,7 +494,13 @@ class TexEngine {
|
|
|
387
494
|
const d = new Date(data[key]);
|
|
388
495
|
if (isNaN(d.getTime())) {
|
|
389
496
|
throw new ValidatorError([
|
|
390
|
-
{
|
|
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
|
+
},
|
|
391
504
|
]);
|
|
392
505
|
}
|
|
393
506
|
data[key] = d;
|
|
@@ -397,12 +510,24 @@ class TexEngine {
|
|
|
397
510
|
typeof data[key] !== 'string' &&
|
|
398
511
|
typeof data[key] !== 'number') {
|
|
399
512
|
throw new ValidatorError([
|
|
400
|
-
{
|
|
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
|
+
},
|
|
401
520
|
]);
|
|
402
521
|
}
|
|
403
522
|
if (data[key] instanceof Date && isNaN(data[key].getTime())) {
|
|
404
523
|
throw new ValidatorError([
|
|
405
|
-
{
|
|
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
|
+
},
|
|
406
531
|
]);
|
|
407
532
|
}
|
|
408
533
|
if (val._raw.includes('minDate:')) {
|
|
@@ -410,7 +535,13 @@ class TexEngine {
|
|
|
410
535
|
if (minStr &&
|
|
411
536
|
new Date(data[key]).getTime() < new Date(minStr).getTime()) {
|
|
412
537
|
throw new ValidatorError([
|
|
413
|
-
{
|
|
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
|
+
},
|
|
414
545
|
]);
|
|
415
546
|
}
|
|
416
547
|
}
|
|
@@ -419,7 +550,13 @@ class TexEngine {
|
|
|
419
550
|
if (maxStr &&
|
|
420
551
|
new Date(data[key]).getTime() > new Date(maxStr).getTime()) {
|
|
421
552
|
throw new ValidatorError([
|
|
422
|
-
{
|
|
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
|
+
},
|
|
423
560
|
]);
|
|
424
561
|
}
|
|
425
562
|
}
|
|
@@ -530,32 +667,115 @@ class TexEngine {
|
|
|
530
667
|
}
|
|
531
668
|
let path = 'root';
|
|
532
669
|
let message = err.message || 'Validation failed';
|
|
670
|
+
let expected;
|
|
671
|
+
let code = 'VALIDATION_ERROR';
|
|
533
672
|
if (message.startsWith('Missing required field: ')) {
|
|
534
673
|
path = message.replace('Missing required field: ', '').trim();
|
|
535
674
|
message = 'Expected value, received undefined';
|
|
675
|
+
expected = 'defined';
|
|
676
|
+
code = 'MISSING_REQUIRED_FIELD';
|
|
536
677
|
}
|
|
537
678
|
else if (message.startsWith("Field '")) {
|
|
538
679
|
const match = message.match(/Field '([^']+)' (.+)/);
|
|
539
680
|
if (match) {
|
|
540
681
|
path = match[1];
|
|
541
|
-
|
|
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
|
+
}
|
|
542
747
|
}
|
|
543
748
|
}
|
|
544
749
|
else if (message.startsWith("Strict mode error: Unknown field '")) {
|
|
545
|
-
const match = message.match(/Strict mode error: Unknown field '([^']+)'
|
|
750
|
+
const match = message.match(/Strict mode error: Unknown field '([^']+)'(?: at path '([^']*)')?/);
|
|
546
751
|
if (match) {
|
|
547
|
-
|
|
752
|
+
const key = match[1];
|
|
753
|
+
const parentPath = match[2];
|
|
754
|
+
path =
|
|
755
|
+
parentPath && parentPath.length > 0 ? `${parentPath}.${key}` : key;
|
|
548
756
|
message = 'Unknown field not allowed in strict mode';
|
|
757
|
+
expected = 'undefined (field omitted)';
|
|
758
|
+
code = 'UNRECOGNIZED_KEYS';
|
|
549
759
|
}
|
|
550
760
|
}
|
|
551
|
-
|
|
761
|
+
const receivedRaw = getNestedValue(data, path);
|
|
762
|
+
const received = formatReceivedValue(receivedRaw);
|
|
763
|
+
throw new ValidatorError([{ path, message, expected, received, code }]);
|
|
552
764
|
}
|
|
553
|
-
// Preserve
|
|
765
|
+
// Preserve Date objects and ensure defaults are applied
|
|
554
766
|
if (parsedData && typeof parsedData === 'object') {
|
|
555
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
|
+
}
|
|
556
776
|
if (val instanceof tex_types_1.TexType &&
|
|
557
777
|
val._raw.startsWith('date') &&
|
|
558
|
-
|
|
778
|
+
data &&
|
|
559
779
|
data[key] instanceof Date) {
|
|
560
780
|
parsedData[key] = data[key];
|
|
561
781
|
}
|
|
@@ -577,7 +797,13 @@ class TexEngine {
|
|
|
577
797
|
const msg = typeof r.message === 'function'
|
|
578
798
|
? r.message(parsedData[key])
|
|
579
799
|
: r.message || 'Invalid value';
|
|
580
|
-
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
|
+
});
|
|
581
807
|
}
|
|
582
808
|
}
|
|
583
809
|
}
|
|
@@ -595,7 +821,13 @@ class TexEngine {
|
|
|
595
821
|
const msg = typeof r.message === 'function'
|
|
596
822
|
? r.message(parsedData[key][i])
|
|
597
823
|
: r.message || 'Invalid value';
|
|
598
|
-
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
|
+
});
|
|
599
831
|
}
|
|
600
832
|
}
|
|
601
833
|
}
|
|
@@ -626,7 +858,13 @@ class TexEngine {
|
|
|
626
858
|
const msg = typeof r.message === 'function'
|
|
627
859
|
? r.message(valData)
|
|
628
860
|
: r.message || 'Invalid value';
|
|
629
|
-
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
|
+
});
|
|
630
868
|
}
|
|
631
869
|
}
|
|
632
870
|
}
|