@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,876 +0,0 @@
1
- /**
2
- * Enhanced Request Methods
3
- *
4
- * Implements fluent request methods with full TypeScript support
5
- */
6
-
7
- import type {
8
- EnhancedRequest,
9
- InputValue,
10
- RequestInput,
11
- ValidatedData,
12
- } from '../types'
13
- import { Validator } from '../validation/validator'
14
-
15
- // ============================================================================
16
- // Types
17
- // ============================================================================
18
-
19
- /**
20
- * Validation rules type
21
- */
22
- export type ValidationRules = Record<string, string | ValidationRule[]>
23
-
24
- /**
25
- * Validation rule interface
26
- */
27
- export interface ValidationRule {
28
- name: string
29
- validate: (value: unknown, parameters: string[], field: string, data: Record<string, unknown>) => Promise<boolean> | boolean
30
- message: string | ((_field: string, _parameters: string[]) => string)
31
- }
32
-
33
- /**
34
- * Safe validated data wrapper
35
- */
36
- export interface SafeData<T extends Record<string, unknown> = Record<string, unknown>> {
37
- only: <K extends keyof T>(keys: K[]) => Pick<T, K>
38
- except: <K extends keyof T>(keys: K[]) => Omit<T, K>
39
- merge: <U extends Record<string, unknown>>(data: U) => T & U
40
- all: () => T
41
- has: (key: keyof T) => boolean
42
- get: <K extends keyof T>(key: K) => T[K]
43
- }
44
-
45
- /**
46
- * Collection interface for array data
47
- */
48
- export interface Collection<T> {
49
- items: T[]
50
- count: () => number
51
- first: () => T | undefined
52
- last: () => T | undefined
53
- map: <U>(fn: (item: T, index: number) => U) => Collection<U>
54
- filter: (fn: (item: T, index: number) => boolean) => Collection<T>
55
- find: (fn: (item: T, index: number) => boolean) => T | undefined
56
- reduce: <U>(fn: (acc: U, item: T, index: number) => U, initial: U) => U
57
- forEach: (fn: (item: T, index: number) => void) => void
58
- toArray: () => T[]
59
- isEmpty: () => boolean
60
- isNotEmpty: () => boolean
61
- pluck: <K extends keyof T>(key: K) => Collection<T[K]>
62
- unique: () => Collection<T>
63
- sortBy: <K extends keyof T>(key: K, direction?: 'asc' | 'desc') => Collection<T>
64
- groupBy: <K extends keyof T>(key: K) => Map<T[K], Collection<T>>
65
- chunk: (size: number) => Collection<Collection<T>>
66
- take: (count: number) => Collection<T>
67
- skip: (count: number) => Collection<T>
68
- reverse: () => Collection<T>
69
- sum: (key?: keyof T) => number
70
- avg: (key?: keyof T) => number
71
- min: (key?: keyof T) => T | undefined
72
- max: (key?: keyof T) => T | undefined
73
- }
74
-
75
- // ============================================================================
76
- // Collection Implementation
77
- // ============================================================================
78
-
79
- /**
80
- * Collection class for array manipulation
81
- */
82
- export class CollectionImpl<T> implements Collection<T> {
83
- items: T[]
84
-
85
- constructor(items: T[]) {
86
- this.items = [...items]
87
- }
88
-
89
- count(): number {
90
- return this.items.length
91
- }
92
-
93
- first(): T | undefined {
94
- return this.items[0]
95
- }
96
-
97
- last(): T | undefined {
98
- return this.items[this.items.length - 1]
99
- }
100
-
101
- map<U>(fn: (item: T, index: number) => U): Collection<U> {
102
- return new CollectionImpl(this.items.map(fn))
103
- }
104
-
105
- filter(fn: (item: T, index: number) => boolean): Collection<T> {
106
- return new CollectionImpl(this.items.filter(fn))
107
- }
108
-
109
- find(fn: (item: T, index: number) => boolean): T | undefined {
110
- return this.items.find(fn)
111
- }
112
-
113
- reduce<U>(fn: (acc: U, item: T, index: number) => U, initial: U): U {
114
- return this.items.reduce(fn, initial)
115
- }
116
-
117
- forEach(fn: (item: T, index: number) => void): void {
118
- this.items.forEach(fn)
119
- }
120
-
121
- toArray(): T[] {
122
- return [...this.items]
123
- }
124
-
125
- isEmpty(): boolean {
126
- return this.items.length === 0
127
- }
128
-
129
- isNotEmpty(): boolean {
130
- return this.items.length > 0
131
- }
132
-
133
- pluck<K extends keyof T>(key: K): Collection<T[K]> {
134
- return new CollectionImpl(this.items.map(item => item[key]))
135
- }
136
-
137
- unique(): Collection<T> {
138
- return new CollectionImpl([...new Set(this.items)])
139
- }
140
-
141
- sortBy<K extends keyof T>(key: K, direction: 'asc' | 'desc' = 'asc'): Collection<T> {
142
- const sorted = [...this.items].sort((a, b) => {
143
- const aVal = a[key]
144
- const bVal = b[key]
145
- if (aVal < bVal)
146
- return direction === 'asc' ? -1 : 1
147
- if (aVal > bVal)
148
- return direction === 'asc' ? 1 : -1
149
- return 0
150
- })
151
- return new CollectionImpl(sorted)
152
- }
153
-
154
- groupBy<K extends keyof T>(key: K): Map<T[K], Collection<T>> {
155
- const groups = new Map<T[K], T[]>()
156
- for (const item of this.items) {
157
- const groupKey = item[key]
158
- if (!groups.has(groupKey)) {
159
- groups.set(groupKey, [])
160
- }
161
- groups.get(groupKey)!.push(item)
162
- }
163
- const result = new Map<T[K], Collection<T>>()
164
- for (const [k, v] of groups) {
165
- result.set(k, new CollectionImpl(v))
166
- }
167
- return result
168
- }
169
-
170
- chunk(size: number): Collection<Collection<T>> {
171
- const chunks: Collection<T>[] = []
172
- for (let i = 0; i < this.items.length; i += size) {
173
- chunks.push(new CollectionImpl(this.items.slice(i, i + size)))
174
- }
175
- return new CollectionImpl(chunks)
176
- }
177
-
178
- take(count: number): Collection<T> {
179
- return new CollectionImpl(this.items.slice(0, count))
180
- }
181
-
182
- skip(count: number): Collection<T> {
183
- return new CollectionImpl(this.items.slice(count))
184
- }
185
-
186
- reverse(): Collection<T> {
187
- return new CollectionImpl([...this.items].reverse())
188
- }
189
-
190
- sum(key?: keyof T): number {
191
- if (key) {
192
- return this.items.reduce((acc, item) => acc + (Number(item[key]) || 0), 0)
193
- }
194
- return this.items.reduce((acc, item) => acc + (Number(item) || 0), 0)
195
- }
196
-
197
- avg(key?: keyof T): number {
198
- if (this.items.length === 0)
199
- return 0
200
- return this.sum(key) / this.items.length
201
- }
202
-
203
- min(key?: keyof T): T | undefined {
204
- if (this.items.length === 0)
205
- return undefined
206
- if (key) {
207
- return this.items.reduce((min, item) =>
208
- item[key] < min[key] ? item : min,
209
- )
210
- }
211
- return this.items.reduce((min, item) => (item < min ? item : min))
212
- }
213
-
214
- max(key?: keyof T): T | undefined {
215
- if (this.items.length === 0)
216
- return undefined
217
- if (key) {
218
- return this.items.reduce((max, item) =>
219
- item[key] > max[key] ? item : max,
220
- )
221
- }
222
- return this.items.reduce((max, item) => (item > max ? item : max))
223
- }
224
- }
225
-
226
- // ============================================================================
227
- // Safe Data Implementation
228
- // ============================================================================
229
-
230
- /**
231
- * Create a safe data wrapper
232
- */
233
- export function createSafeData<T extends Record<string, unknown>>(data: T): SafeData<T> {
234
- return {
235
- only: <K extends keyof T>(keys: K[]): Pick<T, K> => {
236
- const result = {} as Pick<T, K>
237
- for (const key of keys) {
238
- if (key in data) {
239
- result[key] = data[key]
240
- }
241
- }
242
- return result
243
- },
244
-
245
- except: <K extends keyof T>(keys: K[]): Omit<T, K> => {
246
- const result = { ...data }
247
- for (const key of keys) {
248
- delete result[key]
249
- }
250
- return result as Omit<T, K>
251
- },
252
-
253
- // eslint-disable-next-line pickier/no-unused-vars
254
- merge: <U extends Record<string, unknown>>(mergeData: U): T & U => {
255
- return { ...data, ...mergeData }
256
- },
257
-
258
- all: (): T => ({ ...data }),
259
-
260
- has: (key: keyof T): boolean => key in data,
261
-
262
- get: <K extends keyof T>(key: K): T[K] => data[key],
263
- }
264
- }
265
-
266
- // ============================================================================
267
- // Enhanced Request Methods
268
- // ============================================================================
269
-
270
- /**
271
- * Enhanced request methods interface
272
- */
273
- export interface EnhancedRequestMethods {
274
- /**
275
- * Validate the request data
276
- */
277
- validate: <T extends Record<string, unknown> = Record<string, unknown>>(
278
- rules: ValidationRules,
279
- messages?: Record<string, string>,
280
- ) => Promise<T>
281
-
282
- /**
283
- * Get validated data as typed object
284
- */
285
- getValidated: <T extends Record<string, unknown> = Record<string, unknown>>() => T
286
-
287
- /**
288
- * Get safe validated data wrapper
289
- */
290
- safe: <T extends Record<string, unknown> = Record<string, unknown>>() => SafeData<T>
291
-
292
- /**
293
- * Execute callback when input has a value
294
- */
295
- whenHas: <T>(
296
- key: string,
297
- callback: (value: T) => void,
298
- defaultCallback?: () => void,
299
- ) => void
300
-
301
- /**
302
- * Execute callback when input is filled (not empty)
303
- */
304
- whenFilled: <T>(
305
- key: string,
306
- callback: (value: T) => void,
307
- defaultCallback?: () => void,
308
- ) => void
309
-
310
- /**
311
- * Parse date input
312
- */
313
- date: (key: string, format?: string) => Date | null
314
-
315
- /**
316
- * Parse enum input
317
- */
318
- enum: <T extends Record<string, string | number>>(
319
- key: string,
320
- enumType: T,
321
- ) => T[keyof T] | null
322
-
323
- /**
324
- * Get input as collection
325
- */
326
- collect: <T = unknown>(key: string) => Collection<T>
327
-
328
- /**
329
- * Get all input keys
330
- */
331
- keys: () => string[]
332
-
333
- /**
334
- * Get input with type casting
335
- */
336
- string: (key: string, defaultValue?: string) => string
337
- integer: (key: string, defaultValue?: number) => number
338
- float: (key: string, defaultValue?: number) => number
339
- boolean: (key: string, defaultValue?: boolean) => boolean
340
-
341
- /**
342
- * Check if input matches a value
343
- */
344
- isValue: (key: string, value: unknown) => boolean
345
-
346
- /**
347
- * Get old input (for form repopulation)
348
- */
349
- old: <T = unknown>(key: string, defaultValue?: T) => T
350
-
351
- /**
352
- * Flash input to session for form repopulation
353
- */
354
- flashInput: (keys?: string[]) => void
355
-
356
- /**
357
- * Flash only specific keys
358
- */
359
- flashInputOnly: (keys: string[]) => void
360
-
361
- /**
362
- * Flash except specific keys
363
- */
364
- flashInputExcept: (keys: string[]) => void
365
-
366
- /**
367
- * Get input as array
368
- */
369
- array: <T = unknown>(key: string) => T[]
370
-
371
- /**
372
- * Get input value from any source (query, body, params)
373
- * Laravel-style request.get()
374
- */
375
- get: <T = unknown>(key: string, defaultValue?: T) => T
376
-
377
- /**
378
- * Get all input data
379
- */
380
- all: () => RequestInput
381
-
382
- /**
383
- * Get only specified keys
384
- */
385
- only: <T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]) => T
386
-
387
- /**
388
- * Get all except specified keys
389
- */
390
- except: <T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]) => T
391
-
392
- /**
393
- * Check if input has a key
394
- */
395
- has: (key: string | string[]) => boolean
396
-
397
- /**
398
- * Check if input has any of the keys
399
- */
400
- hasAny: (keys: string[]) => boolean
401
-
402
- /**
403
- * Check if input is filled (not empty)
404
- */
405
- filled: (key: string | string[]) => boolean
406
-
407
- /**
408
- * Check if input is missing
409
- */
410
- missing: (key: string | string[]) => boolean
411
-
412
- /**
413
- * Merge additional data into input
414
- */
415
- merge: (data: Record<string, unknown>) => void
416
-
417
- /**
418
- * Get input value or fail
419
- */
420
- input: <T = unknown>(key: string, defaultValue?: T) => T
421
- }
422
-
423
- // ============================================================================
424
- // Request Enhancement
425
- // ============================================================================
426
-
427
- /**
428
- * Enhance request with additional methods
429
- */
430
- export function enhanceRequestWithMethods(request: EnhancedRequest): EnhancedRequest & EnhancedRequestMethods {
431
- const enhanced = request as EnhancedRequest & EnhancedRequestMethods & {
432
- _validatedData?: ValidatedData
433
- _oldInput?: RequestInput
434
- }
435
-
436
- // Merged input (query + JSON body + form body + route params).
437
- // Memoized: many of the helpers below call this several times per
438
- // request, and the merge result only changes when a body is parsed
439
- // later — so the cache keys off the body object identities.
440
- let cachedInput: RequestInput | null = null
441
- let cachedJsonBody: unknown
442
- let cachedFormBody: unknown
443
-
444
- const getAllInput = (): RequestInput => {
445
- if (cachedInput
446
- && cachedJsonBody === request.jsonBody
447
- && cachedFormBody === request.formBody) {
448
- return cachedInput
449
- }
450
-
451
- const input: RequestInput = {}
452
-
453
- // Query parameters
454
- if (request.query) {
455
- for (const [key, value] of Object.entries(request.query)) {
456
- input[key] = value as InputValue
457
- }
458
- }
459
-
460
- // JSON body
461
- if (request.jsonBody) {
462
- for (const [key, value] of Object.entries(request.jsonBody as Record<string, unknown>)) {
463
- input[key] = value as InputValue
464
- }
465
- }
466
-
467
- // Form body
468
- if (request.formBody) {
469
- for (const [key, value] of Object.entries(request.formBody)) {
470
- input[key] = value as InputValue
471
- }
472
- }
473
-
474
- // Route params
475
- if (request.params) {
476
- for (const [key, value] of Object.entries(request.params)) {
477
- input[key] = value
478
- }
479
- }
480
-
481
- cachedInput = input
482
- cachedJsonBody = request.jsonBody
483
- cachedFormBody = request.formBody
484
- return input
485
- }
486
-
487
- /**
488
- * Validate the request
489
- */
490
- enhanced.validate = async <T extends Record<string, unknown>>(
491
- rules: ValidationRules,
492
- messages?: Record<string, string>,
493
- ): Promise<T> => {
494
- const input = getAllInput()
495
- const validator = new Validator({
496
- customMessages: messages,
497
- })
498
-
499
- const errors = await validator.validate(input as Record<string, unknown>, rules as Record<string, string>)
500
-
501
- if (Object.keys(errors).length > 0) {
502
- const error = new Error('Validation failed') as Error & { errors: Record<string, string[]> }
503
- error.errors = errors
504
- throw error
505
- }
506
-
507
- enhanced._validatedData = input as ValidatedData
508
- return input as T
509
- }
510
-
511
- /**
512
- * Get validated data as typed object
513
- */
514
- enhanced.getValidated = <T extends Record<string, unknown>>(): T => {
515
- return (enhanced._validatedData || {}) as T
516
- }
517
-
518
- /**
519
- * Get safe validated data wrapper
520
- */
521
- enhanced.safe = <T extends Record<string, unknown>>(): SafeData<T> => {
522
- return createSafeData(enhanced.getValidated<T>())
523
- }
524
-
525
- /**
526
- * Execute callback when input has a value
527
- */
528
- enhanced.whenHas = <T>(
529
- key: string,
530
- callback: (value: T) => void,
531
- defaultCallback?: () => void,
532
- ): void => {
533
- const input = getAllInput()
534
- if (key in input && input[key] !== undefined) {
535
- callback(input[key] as T)
536
- }
537
- else if (defaultCallback) {
538
- defaultCallback()
539
- }
540
- }
541
-
542
- /**
543
- * Execute callback when input is filled
544
- */
545
- enhanced.whenFilled = <T>(
546
- key: string,
547
- callback: (value: T) => void,
548
- defaultCallback?: () => void,
549
- ): void => {
550
- const input = getAllInput()
551
- const value = input[key]
552
- const isFilled = value !== undefined
553
- && value !== null
554
- && value !== ''
555
- && !(Array.isArray(value) && value.length === 0)
556
-
557
- if (isFilled) {
558
- callback(value as T)
559
- }
560
- else if (defaultCallback) {
561
- defaultCallback()
562
- }
563
- }
564
-
565
- /**
566
- * Parse date input
567
- */
568
- enhanced.date = (key: string, _format?: string): Date | null => {
569
- const input = getAllInput()
570
- const value = input[key]
571
-
572
- if (!value)
573
- return null
574
-
575
- const date = new Date(String(value))
576
- return Number.isNaN(date.getTime()) ? null : date
577
- }
578
-
579
- /**
580
- * Parse enum input
581
- */
582
- enhanced.enum = <T extends Record<string, string | number>>(
583
- key: string,
584
- enumType: T,
585
- ): T[keyof T] | null => {
586
- const input = getAllInput()
587
- const value = input[key]
588
-
589
- if (value === undefined || value === null)
590
- return null
591
-
592
- const enumValues = Object.values(enumType)
593
- if (enumValues.includes(value as T[keyof T])) {
594
- return value as T[keyof T]
595
- }
596
-
597
- // Try to match by key
598
- const stringValue = String(value)
599
- if (stringValue in enumType) {
600
- return enumType[stringValue as keyof T]
601
- }
602
-
603
- return null
604
- }
605
-
606
- /**
607
- * Get input as collection
608
- */
609
- enhanced.collect = <T = unknown>(key: string): Collection<T> => {
610
- const input = getAllInput()
611
- const value = input[key]
612
-
613
- if (Array.isArray(value)) {
614
- return new CollectionImpl(value as T[])
615
- }
616
-
617
- return new CollectionImpl(value ? [value as T] : [])
618
- }
619
-
620
- /**
621
- * Get all input keys
622
- */
623
- enhanced.keys = (): string[] => {
624
- return Object.keys(getAllInput())
625
- }
626
-
627
- /**
628
- * Get string input
629
- */
630
- enhanced.string = (key: string, defaultValue: string = ''): string => {
631
- const input = getAllInput()
632
- const value = input[key]
633
- return value !== undefined && value !== null ? String(value) : defaultValue
634
- }
635
-
636
- /**
637
- * Get integer input
638
- */
639
- enhanced.integer = (key: string, defaultValue: number = 0): number => {
640
- const input = getAllInput()
641
- const value = input[key]
642
- const parsed = Number.parseInt(String(value), 10)
643
- return Number.isNaN(parsed) ? defaultValue : parsed
644
- }
645
-
646
- /**
647
- * Get float input
648
- */
649
- enhanced.float = (key: string, defaultValue: number = 0): number => {
650
- const input = getAllInput()
651
- const value = input[key]
652
- const parsed = Number.parseFloat(String(value))
653
- return Number.isNaN(parsed) ? defaultValue : parsed
654
- }
655
-
656
- /**
657
- * Get boolean input
658
- */
659
- enhanced.boolean = (key: string, defaultValue: boolean = false): boolean => {
660
- const input = getAllInput()
661
- const value = input[key]
662
-
663
- if (value === undefined || value === null)
664
- return defaultValue
665
- if (typeof value === 'boolean')
666
- return value
667
- if (value === 'true' || value === '1' || value === 1)
668
- return true
669
- if (value === 'false' || value === '0' || value === 0)
670
- return false
671
-
672
- return defaultValue
673
- }
674
-
675
- /**
676
- * Check if input matches a value
677
- */
678
- enhanced.isValue = (key: string, value: unknown): boolean => {
679
- const input = getAllInput()
680
- return input[key] === value
681
- }
682
-
683
- /**
684
- * Get old input
685
- */
686
- enhanced.old = <T = unknown>(key: string, defaultValue?: T): T => {
687
- const oldInput = enhanced._oldInput || {}
688
- return (oldInput[key] as T) ?? (defaultValue as T)
689
- }
690
-
691
- /**
692
- * Flash input to session for form repopulation
693
- */
694
- enhanced.flashInput = (keys?: string[]): void => {
695
- const input = getAllInput()
696
- if (keys) {
697
- const filtered: RequestInput = {}
698
- for (const key of keys) {
699
- if (key in input) {
700
- filtered[key] = input[key]
701
- }
702
- }
703
- enhanced._oldInput = filtered
704
- }
705
- else {
706
- enhanced._oldInput = input
707
- }
708
- }
709
-
710
- /**
711
- * Flash only specific keys
712
- */
713
- enhanced.flashInputOnly = (keys: string[]): void => {
714
- enhanced.flashInput(keys)
715
- }
716
-
717
- /**
718
- * Flash except specific keys
719
- */
720
- enhanced.flashInputExcept = (keys: string[]): void => {
721
- const input = getAllInput()
722
- const filtered: RequestInput = {}
723
- for (const [key, value] of Object.entries(input)) {
724
- if (!keys.includes(key)) {
725
- filtered[key] = value
726
- }
727
- }
728
- enhanced._oldInput = filtered
729
- }
730
-
731
- /**
732
- * Get input as array
733
- */
734
- enhanced.array = <T = unknown>(key: string): T[] => {
735
- const input = getAllInput()
736
- const value = input[key]
737
-
738
- if (Array.isArray(value)) {
739
- return value as T[]
740
- }
741
-
742
- return value !== undefined && value !== null ? [value as T] : []
743
- }
744
-
745
- /**
746
- * Get input value from any source (query, body, params) - Laravel style
747
- */
748
- enhanced.get = <T = unknown>(key: string, defaultValue?: T): T => {
749
- const input = getAllInput()
750
- const value = input[key]
751
- return (value !== undefined ? value : defaultValue) as T
752
- }
753
-
754
- /**
755
- * Get all input data
756
- */
757
- enhanced.all = (): RequestInput => {
758
- return getAllInput()
759
- }
760
-
761
- /**
762
- * Get only specified keys
763
- */
764
- enhanced.only = <T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): T => {
765
- const input = getAllInput()
766
- const result = {} as T
767
- for (const key of keys) {
768
- if (key in input) {
769
- (result as Record<string, unknown>)[key] = input[key]
770
- }
771
- }
772
- return result
773
- }
774
-
775
- /**
776
- * Get all except specified keys
777
- */
778
- enhanced.except = <T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): T => {
779
- const input = getAllInput()
780
- const result = { ...input } as T
781
- for (const key of keys) {
782
- delete (result as Record<string, unknown>)[key]
783
- }
784
- return result
785
- }
786
-
787
- /**
788
- * Check if input has a key (or all keys if array)
789
- */
790
- enhanced.has = (key: string | string[]): boolean => {
791
- const input = getAllInput()
792
- if (Array.isArray(key)) {
793
- return key.every(k => k in input && input[k] !== undefined)
794
- }
795
- return key in input && input[key] !== undefined
796
- }
797
-
798
- /**
799
- * Check if input has any of the keys
800
- */
801
- enhanced.hasAny = (keys: string[]): boolean => {
802
- const input = getAllInput()
803
- return keys.some(k => k in input && input[k] !== undefined)
804
- }
805
-
806
- /**
807
- * Check if input is filled (not empty)
808
- */
809
- enhanced.filled = (key: string | string[]): boolean => {
810
- const input = getAllInput()
811
- const isFilled = (k: string): boolean => {
812
- const value = input[k]
813
- return value !== undefined
814
- && value !== null
815
- && value !== ''
816
- && !(Array.isArray(value) && value.length === 0)
817
- }
818
-
819
- if (Array.isArray(key)) {
820
- return key.every(isFilled)
821
- }
822
- return isFilled(key)
823
- }
824
-
825
- /**
826
- * Check if input is missing
827
- */
828
- enhanced.missing = (key: string | string[]): boolean => {
829
- const input = getAllInput()
830
- if (Array.isArray(key)) {
831
- return key.every(k => !(k in input) || input[k] === undefined)
832
- }
833
- return !(key in input) || input[key] === undefined
834
- }
835
-
836
- /**
837
- * Merge additional data into input
838
- */
839
- enhanced.merge = (data: Record<string, unknown>): void => {
840
- if (request.jsonBody && typeof request.jsonBody === 'object') {
841
- Object.assign(request.jsonBody, data)
842
- }
843
- else {
844
- (request as any).jsonBody = data
845
- }
846
- // In-place mutation doesn't change jsonBody's identity, so drop the
847
- // memoized merge explicitly
848
- cachedInput = null
849
- }
850
-
851
- /**
852
- * Get input value - alias for get() for Laravel compatibility
853
- */
854
- enhanced.input = <T = unknown>(key: string, defaultValue?: T): T => {
855
- return enhanced.get(key, defaultValue)
856
- }
857
-
858
- return enhanced
859
- }
860
-
861
- // ============================================================================
862
- // Middleware for Request Enhancement
863
- // ============================================================================
864
-
865
- /**
866
- * Middleware to enhance request with additional methods
867
- */
868
- export function enhancedRequestMiddleware() {
869
- return async (req: EnhancedRequest, next: () => Promise<Response | null>): Promise<Response | null> => {
870
- enhanceRequestWithMethods(req)
871
- return next()
872
- }
873
- }
874
-
875
- // Note: Collection interface and CollectionImpl class are already exported above
876
- // enhanceRequestWithMethods is already exported above