@stacksjs/bun-router 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2mx7fq49.js +4 -0
- package/dist/chunk-a8p83068.js +103 -0
- package/dist/{chunk-cgptvjdf.js → chunk-y3xq6wq6.js} +1 -1
- package/dist/cli.js +4 -3
- package/dist/container/index.js +1149 -211
- package/dist/container/service-provider.d.ts +3 -3
- package/dist/development/performance-profiler.d.ts +3 -3
- package/dist/development/route-debugger.d.ts +1 -1
- package/dist/development/route-inspector.d.ts +2 -2
- package/dist/index.js +369 -2209
- package/dist/observability/integration.d.ts +1 -1
- package/dist/response/macros.d.ts +1 -1
- package/dist/router/index.d.ts +7 -7
- package/dist/router/router.d.ts +0 -32
- package/dist/testing/performance-testing.d.ts +2 -2
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/websocket-testing.d.ts +1 -1
- package/package.json +7 -7
- package/dist/chunk-1ahs68ys.js +0 -18
- package/dist/chunk-g3ybefhg.js +0 -1923
- package/src/auth.ts +0 -489
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -786
- package/src/container/contextual-binding.ts +0 -606
- package/src/container/decorators.ts +0 -368
- package/src/container/index.ts +0 -49
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -733
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -623
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -718
- package/src/index.ts +0 -66
- package/src/middleware/auth.ts +0 -220
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -142
- package/src/middleware/csrf.ts +0 -166
- package/src/middleware/ddos_protection.ts +0 -279
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -275
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -453
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -540
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -945
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -251
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -638
- package/src/middleware/request_tracer.ts +0 -653
- package/src/middleware/response_cache.ts +0 -778
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -109
- package/src/request/enhanced-request.ts +0 -876
- package/src/request/macros.ts +0 -822
- package/src/response/macros.ts +0 -665
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -697
- package/src/router/file-streaming.ts +0 -383
- package/src/router/fluent-routing.ts +0 -866
- package/src/router/group-organization.ts +0 -229
- package/src/router/handler-resolver.ts +0 -327
- package/src/router/http-methods.ts +0 -407
- package/src/router/index.ts +0 -193
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -234
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -302
- package/src/router/route-building.ts +0 -221
- package/src/router/route-compiler.ts +0 -691
- package/src/router/route-matching.ts +0 -368
- package/src/router/route-trie.ts +0 -450
- package/src/router/router.ts +0 -1616
- package/src/router/server.ts +0 -684
- package/src/router/validation-integration.ts +0 -449
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -448
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -422
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -372
- package/src/types.ts +0 -2123
- package/src/url.ts +0 -131
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -395
- package/src/validation/validator.ts +0 -688
- package/src/websocket/clustering.ts +0 -769
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
import type { CookieAccessor, CookieOptions, EnhancedRequest } from '../types'
|
|
2
|
-
import type { JWTTestOptions, SessionTestOptions, TestUser } from './types'
|
|
3
|
-
import { mock } from 'bun:test'
|
|
4
|
-
import { createMockRequest } from './test-request'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Creates a CookieAccessor from a plain Record<string, string>
|
|
8
|
-
*/
|
|
9
|
-
function createCookieAccessor(cookies: Record<string, string>): CookieAccessor {
|
|
10
|
-
const cookieStore = { ...cookies }
|
|
11
|
-
return {
|
|
12
|
-
get: (name: string) => cookieStore[name],
|
|
13
|
-
set: (name: string, value: string, _options?: CookieOptions) => {
|
|
14
|
-
cookieStore[name] = value
|
|
15
|
-
},
|
|
16
|
-
delete: (name: string, _options?: CookieOptions) => {
|
|
17
|
-
delete cookieStore[name]
|
|
18
|
-
},
|
|
19
|
-
getAll: () => ({ ...cookieStore }),
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Authentication testing utilities
|
|
25
|
-
*/
|
|
26
|
-
export class AuthTester {
|
|
27
|
-
private request: EnhancedRequest
|
|
28
|
-
|
|
29
|
-
constructor(request?: EnhancedRequest) {
|
|
30
|
-
this.request = request || createMockRequest()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Set authenticated user
|
|
35
|
-
*/
|
|
36
|
-
as(user: TestUser): AuthTester {
|
|
37
|
-
this.request.user = user
|
|
38
|
-
return this
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Set JWT token
|
|
43
|
-
*/
|
|
44
|
-
withJWT(token: string, type: 'Bearer' | 'Basic' = 'Bearer'): AuthTester {
|
|
45
|
-
const headers = new Headers(this.request.headers)
|
|
46
|
-
headers.set('Authorization', `${type} ${token}`)
|
|
47
|
-
// Create a new request object with updated headers. The spread drops
|
|
48
|
-
// prototype methods (e.g. `clone`) from the inferred type, so re-assert
|
|
49
|
-
// the shape — the mock still carries them at runtime.
|
|
50
|
-
this.request = {
|
|
51
|
-
...this.request,
|
|
52
|
-
headers,
|
|
53
|
-
} as EnhancedRequest
|
|
54
|
-
return this
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Set session data
|
|
59
|
-
*/
|
|
60
|
-
withSession(sessionData: any): AuthTester {
|
|
61
|
-
this.request.session = sessionData
|
|
62
|
-
return this
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Set API key
|
|
67
|
-
*/
|
|
68
|
-
withApiKey(apiKey: string, headerName: string = 'X-API-Key'): AuthTester {
|
|
69
|
-
const headers = new Headers(this.request.headers)
|
|
70
|
-
headers.set(headerName, apiKey)
|
|
71
|
-
this.request = { ...this.request, headers } as EnhancedRequest
|
|
72
|
-
return this
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Set basic auth credentials
|
|
77
|
-
*/
|
|
78
|
-
withBasicAuth(username: string, password: string): AuthTester {
|
|
79
|
-
const credentials = btoa(`${username}:${password}`)
|
|
80
|
-
const headers = new Headers(this.request.headers)
|
|
81
|
-
headers.set('Authorization', `Basic ${credentials}`)
|
|
82
|
-
this.request = { ...this.request, headers } as EnhancedRequest
|
|
83
|
-
return this
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Set cookies for authentication
|
|
88
|
-
*/
|
|
89
|
-
withAuthCookies(cookies: Record<string, string>): AuthTester {
|
|
90
|
-
const existingCookies = this.request.cookies?.getAll() || {}
|
|
91
|
-
this.request.cookies = createCookieAccessor({ ...existingCookies, ...cookies })
|
|
92
|
-
return this
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Remove authentication
|
|
97
|
-
*/
|
|
98
|
-
unauthenticated(): AuthTester {
|
|
99
|
-
this.request.user = undefined
|
|
100
|
-
this.request.session = undefined
|
|
101
|
-
if (this.request.headers) {
|
|
102
|
-
this.request.headers.delete('Authorization')
|
|
103
|
-
}
|
|
104
|
-
return this
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Get the configured request
|
|
109
|
-
*/
|
|
110
|
-
getRequest(): EnhancedRequest {
|
|
111
|
-
return this.request
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* JWT testing utilities
|
|
117
|
-
*/
|
|
118
|
-
export class JWTTester {
|
|
119
|
-
private options: JWTTestOptions
|
|
120
|
-
|
|
121
|
-
constructor(options: JWTTestOptions = {}) {
|
|
122
|
-
this.options = {
|
|
123
|
-
secret: 'test-secret',
|
|
124
|
-
expiresIn: '1h',
|
|
125
|
-
algorithm: 'HS256',
|
|
126
|
-
...options,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Generate a test JWT token
|
|
132
|
-
*/
|
|
133
|
-
generateToken(payload: Record<string, any> = {}): string {
|
|
134
|
-
const header = {
|
|
135
|
-
alg: this.options.algorithm,
|
|
136
|
-
typ: 'JWT',
|
|
137
|
-
...this.options.headers,
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const now = Math.floor(Date.now() / 1000)
|
|
141
|
-
const exp = now + this.parseExpiresIn(this.options.expiresIn || '1h')
|
|
142
|
-
|
|
143
|
-
const jwtPayload = {
|
|
144
|
-
iat: now,
|
|
145
|
-
exp,
|
|
146
|
-
...this.options.payload,
|
|
147
|
-
...payload,
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Simple JWT generation for testing (not cryptographically secure)
|
|
151
|
-
const encodedHeader = btoa(JSON.stringify(header)).replace(/=/g, '')
|
|
152
|
-
const encodedPayload = btoa(JSON.stringify(jwtPayload)).replace(/=/g, '')
|
|
153
|
-
const signature = btoa(`${encodedHeader}.${encodedPayload}.${this.options.secret}`).replace(/=/g, '')
|
|
154
|
-
|
|
155
|
-
return `${encodedHeader}.${encodedPayload}.${signature}`
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Generate an expired token
|
|
160
|
-
*/
|
|
161
|
-
generateExpiredToken(payload: Record<string, any> = {}): string {
|
|
162
|
-
const header = {
|
|
163
|
-
alg: this.options.algorithm,
|
|
164
|
-
typ: 'JWT',
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const now = Math.floor(Date.now() / 1000)
|
|
168
|
-
const jwtPayload = {
|
|
169
|
-
iat: now - 7200, // 2 hours ago
|
|
170
|
-
exp: now - 3600, // 1 hour ago (expired)
|
|
171
|
-
...payload,
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const encodedHeader = btoa(JSON.stringify(header)).replace(/=/g, '')
|
|
175
|
-
const encodedPayload = btoa(JSON.stringify(jwtPayload)).replace(/=/g, '')
|
|
176
|
-
const signature = btoa(`${encodedHeader}.${encodedPayload}.${this.options.secret}`).replace(/=/g, '')
|
|
177
|
-
|
|
178
|
-
return `${encodedHeader}.${encodedPayload}.${signature}`
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Generate an invalid token
|
|
183
|
-
*/
|
|
184
|
-
generateInvalidToken(): string {
|
|
185
|
-
return 'invalid.jwt.token'
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Decode a JWT token (for testing purposes)
|
|
190
|
-
*/
|
|
191
|
-
decodeToken(token: string): { header: any, payload: any, signature: string } {
|
|
192
|
-
const [header, payload, signature] = token.split('.')
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
header: JSON.parse(atob(header)),
|
|
196
|
-
payload: JSON.parse(atob(payload)),
|
|
197
|
-
signature,
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private parseExpiresIn(expiresIn: string): number {
|
|
202
|
-
const match = expiresIn.match(/^(\d+)([smhd])$/)
|
|
203
|
-
if (!match)
|
|
204
|
-
return 3600 // Default 1 hour
|
|
205
|
-
|
|
206
|
-
const value = Number.parseInt(match[1])
|
|
207
|
-
const unit = match[2]
|
|
208
|
-
|
|
209
|
-
switch (unit) {
|
|
210
|
-
case 's': return value
|
|
211
|
-
case 'm': return value * 60
|
|
212
|
-
case 'h': return value * 3600
|
|
213
|
-
case 'd': return value * 86400
|
|
214
|
-
default: return 3600
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Session testing utilities
|
|
221
|
-
*/
|
|
222
|
-
export class SessionTester {
|
|
223
|
-
private options: SessionTestOptions
|
|
224
|
-
|
|
225
|
-
constructor(options: SessionTestOptions = {}) {
|
|
226
|
-
this.options = {
|
|
227
|
-
sessionId: `test-session-${Date.now()}`,
|
|
228
|
-
maxAge: 3600000, // 1 hour
|
|
229
|
-
secure: false,
|
|
230
|
-
httpOnly: true,
|
|
231
|
-
...options,
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Create a test session
|
|
237
|
-
*/
|
|
238
|
-
createSession(data: Record<string, any> = {}): {
|
|
239
|
-
sessionId: string
|
|
240
|
-
data: Record<string, any>
|
|
241
|
-
cookie: string
|
|
242
|
-
} {
|
|
243
|
-
const sessionData = {
|
|
244
|
-
...this.options.data,
|
|
245
|
-
...data,
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const cookie = this.generateSessionCookie()
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
sessionId: this.options.sessionId!,
|
|
252
|
-
data: sessionData,
|
|
253
|
-
cookie,
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Generate session cookie string
|
|
259
|
-
*/
|
|
260
|
-
generateSessionCookie(name: string = 'session'): string {
|
|
261
|
-
const options = []
|
|
262
|
-
|
|
263
|
-
if (this.options.maxAge) {
|
|
264
|
-
options.push(`Max-Age=${Math.floor(this.options.maxAge / 1000)}`)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (this.options.secure) {
|
|
268
|
-
options.push('Secure')
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (this.options.httpOnly) {
|
|
272
|
-
options.push('HttpOnly')
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const optionsStr = options.length > 0 ? `; ${options.join('; ')}` : ''
|
|
276
|
-
return `${name}=${this.options.sessionId}${optionsStr}`
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Create an expired session
|
|
281
|
-
*/
|
|
282
|
-
createExpiredSession(): {
|
|
283
|
-
sessionId: string
|
|
284
|
-
data: Record<string, any>
|
|
285
|
-
cookie: string
|
|
286
|
-
} {
|
|
287
|
-
const expiredOptions = {
|
|
288
|
-
...this.options,
|
|
289
|
-
maxAge: -1, // Expired
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const tester = new SessionTester(expiredOptions)
|
|
293
|
-
return tester.createSession()
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Authentication mock factories
|
|
299
|
-
*/
|
|
300
|
-
export const authMocks = {
|
|
301
|
-
/**
|
|
302
|
-
* Create a mock user
|
|
303
|
-
*/
|
|
304
|
-
user: (overrides: Partial<TestUser> = {}): TestUser => ({
|
|
305
|
-
id: 1,
|
|
306
|
-
email: 'test@example.com',
|
|
307
|
-
username: 'testuser',
|
|
308
|
-
roles: ['user'],
|
|
309
|
-
permissions: ['read'],
|
|
310
|
-
...overrides,
|
|
311
|
-
}),
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Create an admin user
|
|
315
|
-
*/
|
|
316
|
-
adminUser: (overrides: Partial<TestUser> = {}): TestUser => ({
|
|
317
|
-
id: 1,
|
|
318
|
-
email: 'admin@example.com',
|
|
319
|
-
username: 'admin',
|
|
320
|
-
roles: ['admin'],
|
|
321
|
-
permissions: ['read', 'write', 'delete'],
|
|
322
|
-
...overrides,
|
|
323
|
-
}),
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Create a guest user
|
|
327
|
-
*/
|
|
328
|
-
guestUser: (): TestUser => ({
|
|
329
|
-
id: 0,
|
|
330
|
-
email: 'guest@example.com',
|
|
331
|
-
username: 'guest',
|
|
332
|
-
roles: ['guest'],
|
|
333
|
-
permissions: [],
|
|
334
|
-
}),
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Mock authentication middleware
|
|
338
|
-
*/
|
|
339
|
-
authMiddleware: (user?: TestUser): any => mock(async (req: EnhancedRequest, next: any): Promise<Response> => {
|
|
340
|
-
if (user) {
|
|
341
|
-
req.user = user
|
|
342
|
-
return await next()
|
|
343
|
-
}
|
|
344
|
-
return new Response('Unauthorized', { status: 401 })
|
|
345
|
-
}),
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Mock role-based middleware
|
|
349
|
-
*/
|
|
350
|
-
roleMiddleware: (requiredRoles: string[]): any => mock(async (req: EnhancedRequest, next: any): Promise<Response> => {
|
|
351
|
-
if (!req.user) {
|
|
352
|
-
return new Response('Unauthorized', { status: 401 })
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const userRoles = req.user.roles || []
|
|
356
|
-
const hasRole = requiredRoles.some(role => userRoles.includes(role))
|
|
357
|
-
|
|
358
|
-
if (!hasRole) {
|
|
359
|
-
return new Response('Forbidden', { status: 403 })
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return await next()
|
|
363
|
-
}),
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Mock permission-based middleware
|
|
367
|
-
*/
|
|
368
|
-
permissionMiddleware: (requiredPermissions: string[]): any => mock(async (req: EnhancedRequest, next: any): Promise<Response> => {
|
|
369
|
-
if (!req.user) {
|
|
370
|
-
return new Response('Unauthorized', { status: 401 })
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const userPermissions = req.user.permissions || []
|
|
374
|
-
const hasPermission = requiredPermissions.every(permission =>
|
|
375
|
-
userPermissions.includes(permission),
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
if (!hasPermission) {
|
|
379
|
-
return new Response('Forbidden', { status: 403 })
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return await next()
|
|
383
|
-
}),
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Helper functions
|
|
388
|
-
*/
|
|
389
|
-
export const authHelpers = {
|
|
390
|
-
/**
|
|
391
|
-
* Create an authenticated request
|
|
392
|
-
*/
|
|
393
|
-
authenticatedRequest: (user: TestUser, request?: EnhancedRequest): EnhancedRequest => {
|
|
394
|
-
return new AuthTester(request).as(user).getRequest()
|
|
395
|
-
},
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Create a request with JWT
|
|
399
|
-
*/
|
|
400
|
-
jwtRequest: (payload: Record<string, any> = {}, request?: EnhancedRequest): EnhancedRequest => {
|
|
401
|
-
const jwt = new JWTTester()
|
|
402
|
-
const token = jwt.generateToken(payload)
|
|
403
|
-
return new AuthTester(request).withJWT(token).getRequest()
|
|
404
|
-
},
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Create a request with session
|
|
408
|
-
*/
|
|
409
|
-
sessionRequest: (sessionData: Record<string, any> = {}, request?: EnhancedRequest): EnhancedRequest => {
|
|
410
|
-
return new AuthTester(request).withSession(sessionData).getRequest()
|
|
411
|
-
},
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Verify JWT token structure
|
|
415
|
-
*/
|
|
416
|
-
verifyJWTStructure: (token: string): boolean => {
|
|
417
|
-
const parts = token.split('.')
|
|
418
|
-
return parts.length === 3 && parts.every(part => part.length > 0)
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Extract payload from JWT (for testing)
|
|
423
|
-
*/
|
|
424
|
-
extractJWTPayload: (token: string): any => {
|
|
425
|
-
try {
|
|
426
|
-
const [, payload] = token.split('.')
|
|
427
|
-
return JSON.parse(atob(payload))
|
|
428
|
-
}
|
|
429
|
-
catch {
|
|
430
|
-
return null
|
|
431
|
-
}
|
|
432
|
-
},
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Factory functions
|
|
437
|
-
*/
|
|
438
|
-
export function createAuthTester(request?: EnhancedRequest): AuthTester {
|
|
439
|
-
return new AuthTester(request)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
export function createJWTTester(options?: JWTTestOptions): JWTTester {
|
|
443
|
-
return new JWTTester(options)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
export function createSessionTester(options?: SessionTestOptions): SessionTester {
|
|
447
|
-
return new SessionTester(options)
|
|
448
|
-
}
|