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.
Files changed (197) hide show
  1. package/dist/cli/commands/build.d.ts +4 -2
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +80 -22
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/dev.d.ts.map +1 -1
  6. package/dist/cli/commands/dev.js +229 -64
  7. package/dist/cli/commands/dev.js.map +1 -1
  8. package/dist/cli/commands/doctor.d.ts.map +1 -1
  9. package/dist/cli/commands/doctor.js +5 -2
  10. package/dist/cli/commands/doctor.js.map +1 -1
  11. package/dist/cli/commands/generate.d.ts +1 -0
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +25 -0
  14. package/dist/cli/commands/generate.js.map +1 -1
  15. package/dist/cli/commands/start.d.ts.map +1 -1
  16. package/dist/cli/commands/start.js +65 -15
  17. package/dist/cli/commands/start.js.map +1 -1
  18. package/dist/cli/index.js +20 -4
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/manifest.d.ts.map +1 -1
  21. package/dist/cli/manifest.js +118 -17
  22. package/dist/cli/manifest.js.map +1 -1
  23. package/dist/cli/resolve-aliases.d.ts +1 -1
  24. package/dist/cli/resolve-aliases.d.ts.map +1 -1
  25. package/dist/cli/resolve-aliases.js +30 -13
  26. package/dist/cli/resolve-aliases.js.map +1 -1
  27. package/dist/config/config.d.ts +3 -1
  28. package/dist/config/config.d.ts.map +1 -1
  29. package/dist/config/config.js.map +1 -1
  30. package/dist/config/env.d.ts.map +1 -1
  31. package/dist/config/env.js +8 -0
  32. package/dist/config/env.js.map +1 -1
  33. package/dist/config/types.d.ts +2 -0
  34. package/dist/config/types.d.ts.map +1 -1
  35. package/dist/decorators/constants.d.ts +2 -0
  36. package/dist/decorators/constants.d.ts.map +1 -1
  37. package/dist/decorators/middleware.d.ts +65 -2
  38. package/dist/decorators/middleware.d.ts.map +1 -1
  39. package/dist/decorators/middleware.js +148 -62
  40. package/dist/decorators/middleware.js.map +1 -1
  41. package/dist/decorators/params.d.ts +5 -0
  42. package/dist/decorators/params.d.ts.map +1 -1
  43. package/dist/decorators/params.js +11 -1
  44. package/dist/decorators/params.js.map +1 -1
  45. package/dist/decorators/response.d.ts.map +1 -1
  46. package/dist/decorators/response.js +32 -22
  47. package/dist/decorators/response.js.map +1 -1
  48. package/dist/di/container.d.ts.map +1 -1
  49. package/dist/di/container.js +6 -1
  50. package/dist/di/container.js.map +1 -1
  51. package/dist/docs/content/boundaries.mdx +33 -3
  52. package/dist/docs/content/cli/commands.mdx +1 -1
  53. package/dist/docs/content/config.mdx +26 -0
  54. package/dist/docs/content/controllers.mdx +127 -10
  55. package/dist/docs/content/decorators.mdx +22 -4
  56. package/dist/docs/content/errors.mdx +38 -2
  57. package/dist/docs/content/introduction.mdx +7 -7
  58. package/dist/docs/content/middleware.mdx +269 -6
  59. package/dist/docs/content/requests.mdx +217 -10
  60. package/dist/docs/content/responses.mdx +323 -34
  61. package/dist/docs/content/routing.mdx +46 -0
  62. package/dist/docs/content/security.mdx +17 -4
  63. package/dist/docs/content/structure.mdx +91 -12
  64. package/dist/docs/content/validation.mdx +226 -11
  65. package/dist/docs/reference/context.mdx +11 -0
  66. package/dist/docs/reference/decorators.mdx +14 -4
  67. package/dist/docs/reference/error.mdx +44 -0
  68. package/dist/docs/reference/logger.mdx +175 -43
  69. package/dist/docs/reference/middleware.mdx +31 -0
  70. package/dist/docs/reference/router.mdx +22 -0
  71. package/dist/docs/reference/validator.mdx +83 -23
  72. package/dist/error/errors.d.ts.map +1 -1
  73. package/dist/error/errors.js +95 -43
  74. package/dist/error/errors.js.map +1 -1
  75. package/dist/error/index.d.ts +1 -0
  76. package/dist/error/index.d.ts.map +1 -1
  77. package/dist/error/index.js +11 -1
  78. package/dist/error/index.js.map +1 -1
  79. package/dist/error/overlay.d.ts +42 -0
  80. package/dist/error/overlay.d.ts.map +1 -0
  81. package/dist/error/overlay.js +306 -0
  82. package/dist/error/overlay.js.map +1 -0
  83. package/dist/index.d.ts +1 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +22 -1
  86. package/dist/index.js.map +1 -1
  87. package/dist/lib/start-server.js +49 -7
  88. package/dist/lib/start-server.js.map +1 -1
  89. package/dist/logger/index.d.ts +1 -1
  90. package/dist/logger/index.d.ts.map +1 -1
  91. package/dist/logger/index.js +9 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/middleware/guard.d.ts +2 -2
  94. package/dist/middleware/guard.d.ts.map +1 -1
  95. package/dist/middleware/guard.js.map +1 -1
  96. package/dist/middleware/ip-filter.d.ts +1 -0
  97. package/dist/middleware/ip-filter.d.ts.map +1 -1
  98. package/dist/middleware/ip-filter.js +2 -0
  99. package/dist/middleware/ip-filter.js.map +1 -1
  100. package/dist/middleware/middleware.d.ts +2 -2
  101. package/dist/middleware/middleware.d.ts.map +1 -1
  102. package/dist/middleware/middleware.js +20 -2
  103. package/dist/middleware/middleware.js.map +1 -1
  104. package/dist/middleware/rate-limit.d.ts.map +1 -1
  105. package/dist/middleware/rate-limit.js +9 -0
  106. package/dist/middleware/rate-limit.js.map +1 -1
  107. package/dist/middleware/security.d.ts +43 -1
  108. package/dist/middleware/security.d.ts.map +1 -1
  109. package/dist/middleware/security.js +101 -0
  110. package/dist/middleware/security.js.map +1 -1
  111. package/dist/response/index.d.ts +229 -6
  112. package/dist/response/index.d.ts.map +1 -1
  113. package/dist/response/index.js +272 -2
  114. package/dist/response/index.js.map +1 -1
  115. package/dist/router/boundary.d.ts +25 -5
  116. package/dist/router/boundary.d.ts.map +1 -1
  117. package/dist/router/boundary.js.map +1 -1
  118. package/dist/router/controller-registrar.d.ts.map +1 -1
  119. package/dist/router/controller-registrar.js +56 -1
  120. package/dist/router/controller-registrar.js.map +1 -1
  121. package/dist/router/index.d.ts +2 -2
  122. package/dist/router/index.d.ts.map +1 -1
  123. package/dist/router/index.js +2 -1
  124. package/dist/router/index.js.map +1 -1
  125. package/dist/router/route-builder.d.ts +70 -36
  126. package/dist/router/route-builder.d.ts.map +1 -1
  127. package/dist/router/route-builder.js +13 -15
  128. package/dist/router/route-builder.js.map +1 -1
  129. package/dist/router/route-scanner.d.ts +1 -0
  130. package/dist/router/route-scanner.d.ts.map +1 -1
  131. package/dist/router/route-scanner.js +217 -10
  132. package/dist/router/route-scanner.js.map +1 -1
  133. package/dist/router/router.d.ts.map +1 -1
  134. package/dist/router/router.js +35 -17
  135. package/dist/router/router.js.map +1 -1
  136. package/dist/router/types.d.ts +11 -1
  137. package/dist/router/types.d.ts.map +1 -1
  138. package/dist/sanitize/index.d.ts +6 -6
  139. package/dist/sanitize/index.d.ts.map +1 -1
  140. package/dist/sanitize/index.js +8 -8
  141. package/dist/sanitize/index.js.map +1 -1
  142. package/dist/server/app.d.ts +2 -0
  143. package/dist/server/app.d.ts.map +1 -1
  144. package/dist/server/app.js +14 -0
  145. package/dist/server/app.js.map +1 -1
  146. package/dist/server/bootstrapper.d.ts.map +1 -1
  147. package/dist/server/bootstrapper.js +91 -30
  148. package/dist/server/bootstrapper.js.map +1 -1
  149. package/dist/server/cluster.d.ts.map +1 -1
  150. package/dist/server/cluster.js +13 -2
  151. package/dist/server/cluster.js.map +1 -1
  152. package/dist/server/context.d.ts +8 -0
  153. package/dist/server/context.d.ts.map +1 -1
  154. package/dist/server/context.js +50 -6
  155. package/dist/server/context.js.map +1 -1
  156. package/dist/server/engines/node-engine.d.ts.map +1 -1
  157. package/dist/server/engines/node-engine.js +1 -0
  158. package/dist/server/engines/node-engine.js.map +1 -1
  159. package/dist/server/lifecycle.d.ts +22 -0
  160. package/dist/server/lifecycle.d.ts.map +1 -0
  161. package/dist/server/lifecycle.js +106 -0
  162. package/dist/server/lifecycle.js.map +1 -0
  163. package/dist/server/request-handler.d.ts +1 -0
  164. package/dist/server/request-handler.d.ts.map +1 -1
  165. package/dist/server/request-handler.js +42 -18
  166. package/dist/server/request-handler.js.map +1 -1
  167. package/dist/server/request.d.ts +6 -0
  168. package/dist/server/request.d.ts.map +1 -1
  169. package/dist/server/request.js +112 -33
  170. package/dist/server/request.js.map +1 -1
  171. package/dist/server/response.d.ts +13 -2
  172. package/dist/server/response.d.ts.map +1 -1
  173. package/dist/server/response.js +250 -10
  174. package/dist/server/response.js.map +1 -1
  175. package/dist/testing/index.d.ts.map +1 -1
  176. package/dist/testing/index.js +6 -1
  177. package/dist/testing/index.js.map +1 -1
  178. package/dist/testing/mocks.d.ts.map +1 -1
  179. package/dist/testing/mocks.js +15 -3
  180. package/dist/testing/mocks.js.map +1 -1
  181. package/dist/utils/logger.d.ts +8 -0
  182. package/dist/utils/logger.d.ts.map +1 -1
  183. package/dist/utils/logger.js +184 -1
  184. package/dist/utils/logger.js.map +1 -1
  185. package/dist/validator/tex-types.d.ts +2 -1
  186. package/dist/validator/tex-types.d.ts.map +1 -1
  187. package/dist/validator/tex-types.js +2 -1
  188. package/dist/validator/tex-types.js.map +1 -1
  189. package/dist/validator/tex.d.ts +38 -22
  190. package/dist/validator/tex.d.ts.map +1 -1
  191. package/dist/validator/tex.js +490 -40
  192. package/dist/validator/tex.js.map +1 -1
  193. package/package.json +10 -10
  194. package/dist/dev/error-overlay.d.ts +0 -3
  195. package/dist/dev/error-overlay.d.ts.map +0 -1
  196. package/dist/dev/error-overlay.js +0 -118
  197. package/dist/dev/error-overlay.js.map +0 -1
@@ -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
- if (opts?.default !== undefined)
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
- return new tex_types_1.TexType(base);
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
- const res = new tex_types_1.TexType(base);
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?.default)
148
- base += ` | default:${opts.default}`;
149
- return new tex_types_1.TexType(base);
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. Run pre-validation sanitizers on the raw payload (if it's an object)
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
- for (const s of itemSchema.sanitizers) {
330
- data[key][i] = s(data[key][i]);
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
- message = match[2].charAt(0).toUpperCase() + match[2].slice(1);
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
- path = match[1];
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({ path: key, message: msg });
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({ path: `${key}[${i}]`, message: msg });
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
- if (parsed[key] !== undefined &&
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(parsed[key]))) {
857
+ if (r.async && !(await r.fn(valData))) {
414
858
  const msg = typeof r.message === 'function'
415
- ? r.message(parsed[key])
859
+ ? r.message(valData)
416
860
  : r.message || 'Invalid value';
417
- errors.push({ path: key, message: msg });
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
  }