@stacksjs/bun-router 0.0.17 → 0.0.18

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 (146) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/container/service-provider.d.ts +3 -3
  3. package/dist/development/performance-profiler.d.ts +3 -3
  4. package/dist/development/route-debugger.d.ts +1 -1
  5. package/dist/development/route-inspector.d.ts +2 -2
  6. package/dist/observability/integration.d.ts +1 -1
  7. package/dist/response/macros.d.ts +1 -1
  8. package/dist/router/index.d.ts +7 -7
  9. package/dist/router/router.d.ts +0 -32
  10. package/dist/testing/performance-testing.d.ts +2 -2
  11. package/dist/testing/test-client.d.ts +1 -1
  12. package/dist/testing/websocket-testing.d.ts +1 -1
  13. package/package.json +2 -3
  14. package/src/auth.ts +0 -489
  15. package/src/cache/lru-cache.ts +0 -457
  16. package/src/cache/middleware-memoization.ts +0 -531
  17. package/src/cache/route-cache-warmer.ts +0 -486
  18. package/src/cache/sqlite-cache.ts +0 -783
  19. package/src/cache/streaming-cache.ts +0 -572
  20. package/src/cli/colors.ts +0 -29
  21. package/src/cli/index.ts +0 -287
  22. package/src/cli/middleware.ts +0 -291
  23. package/src/cli/openapi.ts +0 -407
  24. package/src/cli/router.ts +0 -188
  25. package/src/cli/routes.ts +0 -265
  26. package/src/cli/utils.ts +0 -531
  27. package/src/cli.ts +0 -5
  28. package/src/config.ts +0 -322
  29. package/src/container/container.ts +0 -786
  30. package/src/container/contextual-binding.ts +0 -606
  31. package/src/container/decorators.ts +0 -368
  32. package/src/container/index.ts +0 -49
  33. package/src/container/service-provider.ts +0 -596
  34. package/src/development/hot-reload.ts +0 -673
  35. package/src/development/index.ts +0 -499
  36. package/src/development/performance-profiler.ts +0 -717
  37. package/src/development/route-debugger.ts +0 -527
  38. package/src/development/route-inspector.ts +0 -749
  39. package/src/development/typescript-utilities.ts +0 -682
  40. package/src/docs.ts +0 -397
  41. package/src/errors/circuit-breaker.ts +0 -733
  42. package/src/errors/error-handler.ts +0 -569
  43. package/src/errors/error-reporting.ts +0 -672
  44. package/src/errors/exceptions.ts +0 -536
  45. package/src/errors/graceful-degradation.ts +0 -623
  46. package/src/errors/index.ts +0 -21
  47. package/src/errors/router-errors.ts +0 -632
  48. package/src/file-serving/static-files.ts +0 -718
  49. package/src/index.ts +0 -66
  50. package/src/middleware/auth.ts +0 -220
  51. package/src/middleware/content_security_policy.ts +0 -215
  52. package/src/middleware/cors.ts +0 -142
  53. package/src/middleware/csrf.ts +0 -166
  54. package/src/middleware/ddos_protection.ts +0 -279
  55. package/src/middleware/file_security.ts +0 -191
  56. package/src/middleware/file_upload.ts +0 -275
  57. package/src/middleware/helmet.ts +0 -268
  58. package/src/middleware/index.ts +0 -117
  59. package/src/middleware/input_validation.ts +0 -453
  60. package/src/middleware/json_body.ts +0 -37
  61. package/src/middleware/performance_alerting.ts +0 -540
  62. package/src/middleware/performance_dashboard.ts +0 -661
  63. package/src/middleware/performance_monitor.ts +0 -945
  64. package/src/middleware/pipeline.ts +0 -489
  65. package/src/middleware/rate_limit.ts +0 -251
  66. package/src/middleware/request_id.ts +0 -36
  67. package/src/middleware/request_signing.ts +0 -638
  68. package/src/middleware/request_tracer.ts +0 -653
  69. package/src/middleware/response_cache.ts +0 -778
  70. package/src/middleware/security.ts +0 -482
  71. package/src/middleware/security_suite.ts +0 -257
  72. package/src/middleware/session.ts +0 -91
  73. package/src/model-binding/index.ts +0 -18
  74. package/src/model-binding/model-middleware.ts +0 -425
  75. package/src/model-binding/model-registry.ts +0 -550
  76. package/src/model-binding.ts +0 -370
  77. package/src/model-resolver-factory.ts +0 -106
  78. package/src/observability/correlation.ts +0 -691
  79. package/src/observability/health-checks.ts +0 -729
  80. package/src/observability/index.ts +0 -184
  81. package/src/observability/integration.ts +0 -548
  82. package/src/observability/metrics.ts +0 -753
  83. package/src/observability/tracing.ts +0 -638
  84. package/src/optimization/bun-utilities.ts +0 -778
  85. package/src/query-builder-integration.ts +0 -137
  86. package/src/request/context.ts +0 -109
  87. package/src/request/enhanced-request.ts +0 -876
  88. package/src/request/macros.ts +0 -822
  89. package/src/response/macros.ts +0 -665
  90. package/src/response/response-factory.ts +0 -596
  91. package/src/router/api-routes.ts +0 -243
  92. package/src/router/file-based-routing.ts +0 -697
  93. package/src/router/file-streaming.ts +0 -383
  94. package/src/router/fluent-routing.ts +0 -866
  95. package/src/router/group-organization.ts +0 -229
  96. package/src/router/handler-resolver.ts +0 -327
  97. package/src/router/http-methods.ts +0 -407
  98. package/src/router/index.ts +0 -193
  99. package/src/router/middleware-groups.ts +0 -222
  100. package/src/router/middleware-integration.ts +0 -399
  101. package/src/router/middleware.ts +0 -234
  102. package/src/router/model-binding.ts +0 -215
  103. package/src/router/optimized-route-matching.ts +0 -302
  104. package/src/router/route-building.ts +0 -221
  105. package/src/router/route-compiler.ts +0 -691
  106. package/src/router/route-matching.ts +0 -368
  107. package/src/router/route-trie.ts +0 -450
  108. package/src/router/router.ts +0 -1616
  109. package/src/router/server.ts +0 -684
  110. package/src/router/validation-integration.ts +0 -449
  111. package/src/router/view-rendering.ts +0 -233
  112. package/src/router/websocket.ts +0 -100
  113. package/src/routing/route-caching.ts +0 -402
  114. package/src/routing/route-throttling.ts +0 -469
  115. package/src/routing/subdomain-routing.ts +0 -492
  116. package/src/session/database-store.ts +0 -109
  117. package/src/session/file-store.ts +0 -148
  118. package/src/session/index.ts +0 -244
  119. package/src/session/memory-store.ts +0 -88
  120. package/src/session/redis-store.ts +0 -93
  121. package/src/streaming/index.ts +0 -17
  122. package/src/streaming/sse-handler.ts +0 -482
  123. package/src/streaming/stream-handler.ts +0 -552
  124. package/src/testing/auth-testing.ts +0 -448
  125. package/src/testing/file-upload-testing.ts +0 -543
  126. package/src/testing/index.ts +0 -10
  127. package/src/testing/middleware-testing.ts +0 -322
  128. package/src/testing/model-binding-testing.ts +0 -645
  129. package/src/testing/performance-testing.ts +0 -738
  130. package/src/testing/test-client.ts +0 -310
  131. package/src/testing/test-request.ts +0 -315
  132. package/src/testing/test-response.ts +0 -332
  133. package/src/testing/types.ts +0 -224
  134. package/src/testing/websocket-testing.ts +0 -590
  135. package/src/types/controller-types.ts +0 -385
  136. package/src/types/core.ts +0 -683
  137. package/src/types/middleware-types.ts +0 -422
  138. package/src/types/request-response-augmentation.ts +0 -489
  139. package/src/types/route-inference.ts +0 -372
  140. package/src/types.ts +0 -2123
  141. package/src/url.ts +0 -131
  142. package/src/utils/index.ts +0 -1
  143. package/src/utils/query-preservation.ts +0 -201
  144. package/src/utils.ts +0 -395
  145. package/src/validation/validator.ts +0 -688
  146. package/src/websocket/clustering.ts +0 -769
@@ -1,688 +0,0 @@
1
- /**
2
- * Request/Response Enhancements - Validation System
3
- *
4
- * Laravel-style validation with custom rules and error handling
5
- */
6
-
7
- import type { EnhancedRequest, NextFunction } from '../types'
8
- import { ValidationException } from '../errors/exceptions'
9
-
10
- // Hoisted email check. Pragmatic RFC 5322 approximation: requires one `@`,
11
- // a non-empty local part without surrounding/consecutive dots, and a domain
12
- // with at least one label dot and a 2+ char TLD. The previous regex
13
- // accepted addresses like `a@.com` and consecutive dots.
14
- const EMAIL_REGEX = /^[A-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?)*\.[A-Z]{2,}$/i
15
-
16
- export interface ValidationRule {
17
- name: string
18
- validate: (value: any, parameters: string[], field: string, data: Record<string, any>) => Promise<boolean> | boolean
19
- message: string | ((_field: string, _parameters: string[]) => string)
20
- }
21
-
22
- export interface ValidationRules {
23
- [field: string]: string | ValidationRule[]
24
- }
25
-
26
- export interface ValidationErrors {
27
- [field: string]: string[]
28
- }
29
-
30
- export interface ValidatorConfig {
31
- stopOnFirstFailure?: boolean
32
- customMessages?: Record<string, string>
33
- customAttributes?: Record<string, string>
34
- bail?: boolean
35
- }
36
-
37
- /**
38
- * Built-in validation rules
39
- */
40
- export const BuiltInRules: Record<string, ValidationRule> = {
41
- required: {
42
- name: 'required',
43
- validate: (value) => {
44
- if (value === null || value === undefined)
45
- return false
46
- if (typeof value === 'string')
47
- return value.trim().length > 0
48
- if (Array.isArray(value))
49
- return value.length > 0
50
- if (typeof value === 'object')
51
- return Object.keys(value).length > 0
52
- return true
53
- },
54
- message: 'The :field field is required.',
55
- },
56
-
57
- string: {
58
- name: 'string',
59
- validate: value => typeof value === 'string',
60
- message: 'The :field field must be a string.',
61
- },
62
-
63
- number: {
64
- name: 'number',
65
- validate: value => typeof value === 'number' && !Number.isNaN(value),
66
- message: 'The :field field must be a number.',
67
- },
68
-
69
- integer: {
70
- name: 'integer',
71
- validate: value => Number.isInteger(Number(value)),
72
- message: 'The :field field must be an integer.',
73
- },
74
-
75
- boolean: {
76
- name: 'boolean',
77
- validate: value => typeof value === 'boolean' || value === 'true' || value === 'false' || value === '1' || value === '0',
78
- message: 'The :field field must be true or false.',
79
- },
80
-
81
- email: {
82
- name: 'email',
83
- validate: value => typeof value === 'string' && EMAIL_REGEX.test(value),
84
- message: 'The :field field must be a valid email address.',
85
- },
86
-
87
- url: {
88
- name: 'url',
89
- validate: (value) => {
90
- try {
91
- const url = new URL(value)
92
- return Boolean(url)
93
- }
94
- catch {
95
- return false
96
- }
97
- },
98
- message: 'The :field field must be a valid URL.',
99
- },
100
-
101
- min: {
102
- name: 'min',
103
- validate: (value, parameters) => {
104
- const min = Number(parameters[0])
105
- if (typeof value === 'string')
106
- return value.length >= min
107
- if (typeof value === 'number')
108
- return value >= min
109
- if (Array.isArray(value))
110
- return value.length >= min
111
- return false
112
- },
113
- message: (field, parameters) => `The ${field} field must be at least ${parameters[0]} characters.`,
114
- },
115
-
116
- max: {
117
- name: 'max',
118
- validate: (value, parameters) => {
119
- const max = Number(parameters[0])
120
- if (typeof value === 'string')
121
- return value.length <= max
122
- if (typeof value === 'number')
123
- return value <= max
124
- if (Array.isArray(value))
125
- return value.length <= max
126
- return false
127
- },
128
- message: (field, parameters) => `The ${field} field must not exceed ${parameters[0]} characters.`,
129
- },
130
-
131
- between: {
132
- name: 'between',
133
- validate: (value, parameters) => {
134
- const min = Number(parameters[0])
135
- const max = Number(parameters[1])
136
- if (typeof value === 'string')
137
- return value.length >= min && value.length <= max
138
- if (typeof value === 'number')
139
- return value >= min && value <= max
140
- if (Array.isArray(value))
141
- return value.length >= min && value.length <= max
142
- return false
143
- },
144
- message: (field, parameters) => `The ${field} field must be between ${parameters[0]} and ${parameters[1]}.`,
145
- },
146
-
147
- in: {
148
- name: 'in',
149
- validate: (value, parameters) => parameters.includes(String(value)),
150
- message: (field, parameters) => `The ${field} field must be one of: ${parameters.join(', ')}.`,
151
- },
152
-
153
- notIn: {
154
- name: 'not_in',
155
- validate: (value, parameters) => !parameters.includes(String(value)),
156
- message: (field, parameters) => `The ${field} field must not be one of: ${parameters.join(', ')}.`,
157
- },
158
-
159
- regex: {
160
- name: 'regex',
161
- validate: (value, parameters) => {
162
- const pattern = new RegExp(parameters[0])
163
- return typeof value === 'string' && pattern.test(value)
164
- },
165
- message: 'The :field field format is invalid.',
166
- },
167
-
168
- alpha: {
169
- name: 'alpha',
170
- validate: value => typeof value === 'string' && /^[a-z]+$/i.test(value),
171
- message: 'The :field field must contain only letters.',
172
- },
173
-
174
- alphaNum: {
175
- name: 'alpha_num',
176
- validate: value => typeof value === 'string' && /^[a-z0-9]+$/i.test(value),
177
- message: 'The :field field must contain only letters and numbers.',
178
- },
179
-
180
- alphaDash: {
181
- name: 'alpha_dash',
182
- validate: value => typeof value === 'string' && /^[\w-]+$/.test(value),
183
- message: 'The :field field must contain only letters, numbers, dashes, and underscores.',
184
- },
185
-
186
- date: {
187
- name: 'date',
188
- validate: (value) => {
189
- const date = new Date(value)
190
- return !Number.isNaN(date.getTime())
191
- },
192
- message: 'The :field field must be a valid date.',
193
- },
194
-
195
- dateAfter: {
196
- name: 'after',
197
- validate: (value, parameters) => {
198
- const date = new Date(value)
199
- const afterDate = new Date(parameters[0])
200
- return !Number.isNaN(date.getTime()) && !Number.isNaN(afterDate.getTime()) && date > afterDate
201
- },
202
- message: (field, parameters) => `The ${field} field must be after ${parameters[0]}.`,
203
- },
204
-
205
- dateBefore: {
206
- name: 'before',
207
- validate: (value, parameters) => {
208
- const date = new Date(value)
209
- const beforeDate = new Date(parameters[0])
210
- return !Number.isNaN(date.getTime()) && !Number.isNaN(beforeDate.getTime()) && date < beforeDate
211
- },
212
- message: (field, parameters) => `The ${field} field must be before ${parameters[0]}.`,
213
- },
214
-
215
- confirmed: {
216
- name: 'confirmed',
217
- validate: (value, parameters, field, data) => {
218
- const confirmationField = parameters[0] || `${field}_confirmation`
219
- return value === data[confirmationField]
220
- },
221
- message: 'The :field confirmation does not match.',
222
- },
223
-
224
- same: {
225
- name: 'same',
226
- validate: (value, parameters, field, data) => {
227
- return value === data[parameters[0]]
228
- },
229
- message: (field, parameters) => `The ${field} field must match ${parameters[0]}.`,
230
- },
231
-
232
- different: {
233
- name: 'different',
234
- validate: (value, parameters, field, data) => {
235
- return value !== data[parameters[0]]
236
- },
237
- message: (field, parameters) => `The ${field} field must be different from ${parameters[0]}.`,
238
- },
239
-
240
- json: {
241
- name: 'json',
242
- validate: (value) => {
243
- try {
244
- JSON.parse(value)
245
- return true
246
- }
247
- catch {
248
- return false
249
- }
250
- },
251
- message: 'The :field field must be valid JSON.',
252
- },
253
-
254
- uuid: {
255
- name: 'uuid',
256
- validate: (value) => {
257
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
258
- return typeof value === 'string' && uuidRegex.test(value)
259
- },
260
- message: 'The :field field must be a valid UUID.',
261
- },
262
-
263
- array: {
264
- name: 'array',
265
- validate: value => Array.isArray(value),
266
- message: 'The :field field must be an array.',
267
- },
268
-
269
- object: {
270
- name: 'object',
271
- validate: value => typeof value === 'object' && value !== null && !Array.isArray(value),
272
- message: 'The :field field must be an object.',
273
- },
274
- }
275
-
276
- /**
277
- * Database validation rules (require external implementation)
278
- */
279
- export const DatabaseRules: Record<string, ValidationRule> = {
280
- unique: {
281
- name: 'unique',
282
- validate: async (_value, _parameters) => {
283
- // This would need to be implemented with actual database queries
284
- // For now, return true as a placeholder
285
- console.warn('Database validation rule "unique" requires external implementation')
286
- return true
287
- },
288
- message: 'The :field field must be unique.',
289
- },
290
-
291
- exists: {
292
- name: 'exists',
293
- validate: async (_value, _parameters) => {
294
- // This would need to be implemented with actual database queries
295
- // For now, return true as a placeholder
296
- console.warn('Database validation rule "exists" requires external implementation')
297
- return true
298
- },
299
- message: 'The selected :field is invalid.',
300
- },
301
- }
302
-
303
- /**
304
- * Main validator class
305
- */
306
- export class Validator {
307
- private rules: Record<string, ValidationRule> = {}
308
- private config: ValidatorConfig
309
-
310
- constructor(config: ValidatorConfig = {}) {
311
- this.config = {
312
- stopOnFirstFailure: false,
313
- bail: false,
314
- ...config,
315
- }
316
-
317
- // Register built-in rules
318
- this.registerRules(BuiltInRules)
319
- }
320
-
321
- /**
322
- * Register validation rules
323
- */
324
- registerRule(rule: ValidationRule): void {
325
- this.rules[rule.name] = rule
326
- }
327
-
328
- /**
329
- * Register multiple validation rules
330
- */
331
- registerRules(rules: Record<string, ValidationRule>): void {
332
- Object.values(rules).forEach(rule => this.registerRule(rule))
333
- }
334
-
335
- /**
336
- * Parse validation rule string
337
- */
338
- private parseRuleString(ruleString: string): Array<{ name: string, parameters: string[] }> {
339
- return ruleString.split('|').map((rule) => {
340
- const [name, ...paramParts] = rule.split(':')
341
- const parameters = paramParts.length > 0 ? paramParts.join(':').split(',') : []
342
- return { name: name.trim(), parameters }
343
- })
344
- }
345
-
346
- /**
347
- * Validate data against rules
348
- */
349
- async validate(data: Record<string, any>, rules: ValidationRules): Promise<ValidationErrors> {
350
- const errors: ValidationErrors = {}
351
-
352
- for (const [field, fieldRules] of Object.entries(rules)) {
353
- const fieldErrors: string[] = []
354
- const value = this.getNestedValue(data, field)
355
-
356
- // Parse rules if they're in string format
357
- const parsedRules = typeof fieldRules === 'string'
358
- ? this.parseRuleString(fieldRules)
359
- : fieldRules.map(rule => ({ name: rule.name, parameters: [] }))
360
-
361
- for (const { name, parameters } of parsedRules) {
362
- const rule = this.rules[name]
363
- if (!rule) {
364
- console.warn(`Validation rule "${name}" not found`)
365
- continue
366
- }
367
-
368
- try {
369
- const isValid = await rule.validate(value, parameters, field, data)
370
-
371
- if (!isValid) {
372
- const message = this.formatErrorMessage(rule, field, parameters)
373
- fieldErrors.push(message)
374
-
375
- if (this.config.stopOnFirstFailure || this.config.bail) {
376
- break
377
- }
378
- }
379
- }
380
- catch (error) {
381
- console.error(`Error validating rule "${name}" for field "${field}":`, error)
382
- fieldErrors.push(`Validation error for ${field}`)
383
- }
384
- }
385
-
386
- if (fieldErrors.length > 0) {
387
- errors[field] = fieldErrors
388
- }
389
- }
390
-
391
- return errors
392
- }
393
-
394
- /**
395
- * Validate and throw exception if validation fails
396
- */
397
- async validateOrFail(data: Record<string, any>, rules: ValidationRules): Promise<void> {
398
- const errors = await this.validate(data, rules)
399
-
400
- if (Object.keys(errors).length > 0) {
401
- throw new ValidationException('Validation failed', errors)
402
- }
403
- }
404
-
405
- /**
406
- * Get nested value from object using dot notation
407
- */
408
- private getNestedValue(obj: Record<string, any>, path: string): any {
409
- return path.split('.').reduce((current, key) => {
410
- return current && current[key] !== undefined ? current[key] : undefined
411
- }, obj)
412
- }
413
-
414
- /**
415
- * Format error message
416
- */
417
- private formatErrorMessage(rule: ValidationRule, field: string, parameters: string[]): string {
418
- const customMessage = this.config.customMessages?.[`${field}.${rule.name}`]
419
- || this.config.customMessages?.[rule.name]
420
-
421
- if (customMessage) {
422
- return this.replacePlaceholders(customMessage, field, parameters)
423
- }
424
-
425
- const message = typeof rule.message === 'function'
426
- ? rule.message(field, parameters)
427
- : rule.message
428
-
429
- return this.replacePlaceholders(message, field, parameters)
430
- }
431
-
432
- /**
433
- * Replace placeholders in error messages
434
- */
435
- private replacePlaceholders(message: string, field: string, parameters: string[]): string {
436
- const fieldName = this.config.customAttributes?.[field] || field.replace(/_/g, ' ')
437
-
438
- return message
439
- .replace(/:field/g, fieldName)
440
- .replace(/:attribute/g, fieldName)
441
- .replace(/:value/g, parameters[0] || '')
442
- .replace(/:other/g, parameters[1] || '')
443
- }
444
- }
445
-
446
- /**
447
- * Global validator instance
448
- */
449
- export const globalValidator: Validator = new Validator()
450
-
451
- /**
452
- * Validation middleware factory
453
- */
454
- export function createValidationMiddleware(rules: ValidationRules, config?: ValidatorConfig) {
455
- const validator = new Validator(config)
456
-
457
- return async (req: EnhancedRequest, next: NextFunction): Promise<Response | null> => {
458
- try {
459
- // Get data from request body, query, and params
460
- const data = {
461
- ...req.query,
462
- ...req.params,
463
- ...(req.jsonBody || {}),
464
- ...(req.formBody || {}),
465
- }
466
-
467
- await validator.validateOrFail(data, rules)
468
-
469
- // Add validated data to request
470
- req.validated = data
471
-
472
- return await next()
473
- }
474
- catch (error) {
475
- if (error instanceof ValidationException) {
476
- throw error
477
- }
478
- throw new ValidationException('Validation error occurred')
479
- }
480
- }
481
- }
482
-
483
- /**
484
- * Validation rule builder for fluent API
485
- */
486
- export class ValidationRuleBuilder {
487
- private rules: string[] = []
488
-
489
- required(): this {
490
- this.rules.push('required')
491
- return this
492
- }
493
-
494
- string(): this {
495
- this.rules.push('string')
496
- return this
497
- }
498
-
499
- number(): this {
500
- this.rules.push('number')
501
- return this
502
- }
503
-
504
- integer(): this {
505
- this.rules.push('integer')
506
- return this
507
- }
508
-
509
- boolean(): this {
510
- this.rules.push('boolean')
511
- return this
512
- }
513
-
514
- email(): this {
515
- this.rules.push('email')
516
- return this
517
- }
518
-
519
- url(): this {
520
- this.rules.push('url')
521
- return this
522
- }
523
-
524
- min(value: number): this {
525
- this.rules.push(`min:${value}`)
526
- return this
527
- }
528
-
529
- max(value: number): this {
530
- this.rules.push(`max:${value}`)
531
- return this
532
- }
533
-
534
- between(min: number, max: number): this {
535
- this.rules.push(`between:${min},${max}`)
536
- return this
537
- }
538
-
539
- in(...values: string[]): this {
540
- this.rules.push(`in:${values.join(',')}`)
541
- return this
542
- }
543
-
544
- notIn(...values: string[]): this {
545
- this.rules.push(`not_in:${values.join(',')}`)
546
- return this
547
- }
548
-
549
- regex(pattern: string): this {
550
- this.rules.push(`regex:${pattern}`)
551
- return this
552
- }
553
-
554
- alpha(): this {
555
- this.rules.push('alpha')
556
- return this
557
- }
558
-
559
- alphaNum(): this {
560
- this.rules.push('alpha_num')
561
- return this
562
- }
563
-
564
- alphaDash(): this {
565
- this.rules.push('alpha_dash')
566
- return this
567
- }
568
-
569
- date(): this {
570
- this.rules.push('date')
571
- return this
572
- }
573
-
574
- after(date: string): this {
575
- this.rules.push(`after:${date}`)
576
- return this
577
- }
578
-
579
- before(date: string): this {
580
- this.rules.push(`before:${date}`)
581
- return this
582
- }
583
-
584
- confirmed(field?: string): this {
585
- this.rules.push(field ? `confirmed:${field}` : 'confirmed')
586
- return this
587
- }
588
-
589
- same(field: string): this {
590
- this.rules.push(`same:${field}`)
591
- return this
592
- }
593
-
594
- different(field: string): this {
595
- this.rules.push(`different:${field}`)
596
- return this
597
- }
598
-
599
- json(): this {
600
- this.rules.push('json')
601
- return this
602
- }
603
-
604
- uuid(): this {
605
- this.rules.push('uuid')
606
- return this
607
- }
608
-
609
- array(): this {
610
- this.rules.push('array')
611
- return this
612
- }
613
-
614
- object(): this {
615
- this.rules.push('object')
616
- return this
617
- }
618
-
619
- unique(table: string, column?: string): this {
620
- this.rules.push(column ? `unique:${table},${column}` : `unique:${table}`)
621
- return this
622
- }
623
-
624
- exists(table: string, column?: string): this {
625
- this.rules.push(column ? `exists:${table},${column}` : `exists:${table}`)
626
- return this
627
- }
628
-
629
- custom(ruleName: string, ...parameters: string[]): this {
630
- this.rules.push(parameters.length > 0 ? `${ruleName}:${parameters.join(',')}` : ruleName)
631
- return this
632
- }
633
-
634
- build(): string {
635
- return this.rules.join('|')
636
- }
637
- }
638
-
639
- /**
640
- * Factory function to create validation rule builder
641
- */
642
- export function rule(): ValidationRuleBuilder {
643
- return new ValidationRuleBuilder()
644
- }
645
-
646
- /**
647
- * Validation helper functions
648
- */
649
- export const ValidationHelpers = {
650
- /**
651
- * Create validation rules object
652
- */
653
- rules(rules: Record<string, string | ValidationRuleBuilder>): ValidationRules {
654
- const result: ValidationRules = {}
655
-
656
- for (const [field, fieldRules] of Object.entries(rules)) {
657
- result[field] = fieldRules instanceof ValidationRuleBuilder
658
- ? fieldRules.build()
659
- : fieldRules
660
- }
661
-
662
- return result
663
- },
664
-
665
- /**
666
- * Validate single value
667
- */
668
- async validateValue(value: any, rules: string, field = 'value'): Promise<string[]> {
669
- const validator = new Validator()
670
- const errors = await validator.validate({ [field]: value }, { [field]: rules })
671
- return errors[field] || []
672
- },
673
-
674
- /**
675
- * Check if value passes validation
676
- */
677
- async passes(value: any, rules: string): Promise<boolean> {
678
- const errors = await this.validateValue(value, rules)
679
- return errors.length === 0
680
- },
681
-
682
- /**
683
- * Check if value fails validation
684
- */
685
- async fails(value: any, rules: string): Promise<boolean> {
686
- return !(await this.passes(value, rules))
687
- },
688
- }