@stacksjs/bun-router 0.0.5 → 0.0.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 (269) hide show
  1. package/dist/auth.d.ts +134 -0
  2. package/dist/cache/lru-cache.d.ts +139 -0
  3. package/dist/cache/middleware-memoization.d.ts +151 -0
  4. package/dist/cache/route-cache-warmer.d.ts +161 -0
  5. package/dist/cache/sqlite-cache.d.ts +209 -0
  6. package/dist/cache/streaming-cache.d.ts +132 -0
  7. package/dist/cli/colors.d.ts +15 -0
  8. package/dist/cli/index.d.ts +10 -0
  9. package/dist/cli/middleware.d.ts +31 -0
  10. package/dist/cli/openapi.d.ts +17 -0
  11. package/dist/cli/router.d.ts +15 -0
  12. package/dist/cli/routes.d.ts +29 -0
  13. package/dist/cli/utils.d.ts +74 -0
  14. package/dist/cli.d.ts +1 -1
  15. package/dist/cli.js +1 -1
  16. package/dist/config.d.ts +7 -0
  17. package/dist/container/container.d.ts +273 -0
  18. package/dist/container/contextual-binding.d.ts +240 -0
  19. package/dist/container/decorators.d.ts +141 -0
  20. package/dist/container/service-provider.d.ts +285 -0
  21. package/dist/development/hot-reload.d.ts +187 -0
  22. package/dist/development/index.d.ts +167 -0
  23. package/dist/development/performance-profiler.d.ts +217 -0
  24. package/dist/development/route-debugger.d.ts +154 -0
  25. package/dist/development/route-inspector.d.ts +211 -0
  26. package/dist/development/typescript-utilities.d.ts +209 -0
  27. package/dist/docs.d.ts +11 -0
  28. package/dist/errors/circuit-breaker.d.ts +195 -0
  29. package/dist/errors/error-handler.d.ts +88 -0
  30. package/dist/errors/error-reporting.d.ts +144 -0
  31. package/dist/errors/exceptions.d.ts +184 -0
  32. package/dist/errors/graceful-degradation.d.ts +154 -0
  33. package/dist/errors/index.d.ts +9 -0
  34. package/dist/errors/router-errors.d.ts +110 -0
  35. package/dist/file-serving/static-files.d.ts +143 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +319 -205
  38. package/dist/middleware/auth.d.ts +63 -0
  39. package/dist/middleware/content_security_policy.d.ts +53 -0
  40. package/dist/middleware/cors.d.ts +5 -0
  41. package/dist/middleware/csrf.d.ts +7 -0
  42. package/dist/middleware/ddos_protection.d.ts +39 -0
  43. package/dist/middleware/file_security.d.ts +26 -0
  44. package/dist/middleware/file_upload.d.ts +38 -0
  45. package/dist/middleware/helmet.d.ts +54 -0
  46. package/dist/middleware/index.d.ts +44 -0
  47. package/dist/middleware/input_validation.d.ts +45 -0
  48. package/dist/middleware/json_body.d.ts +4 -0
  49. package/dist/middleware/performance_alerting.d.ts +87 -0
  50. package/dist/middleware/performance_dashboard.d.ts +87 -0
  51. package/dist/middleware/performance_monitor.d.ts +209 -0
  52. package/dist/middleware/pipeline.d.ts +131 -0
  53. package/dist/middleware/rate_limit.d.ts +36 -0
  54. package/dist/middleware/request_id.d.ts +4 -0
  55. package/dist/middleware/request_signing.d.ts +153 -0
  56. package/dist/middleware/request_tracer.d.ts +72 -0
  57. package/dist/middleware/response_cache.d.ts +97 -0
  58. package/dist/middleware/security.d.ts +69 -0
  59. package/dist/middleware/security_suite.d.ts +46 -0
  60. package/dist/middleware/session.d.ts +23 -0
  61. package/dist/model-binding/index.d.ts +2 -0
  62. package/dist/model-binding/model-middleware.d.ts +118 -0
  63. package/dist/model-binding/model-registry.d.ts +164 -0
  64. package/dist/model-binding.d.ts +185 -0
  65. package/dist/model-resolver-factory.d.ts +31 -0
  66. package/dist/observability/correlation.d.ts +187 -0
  67. package/dist/observability/health-checks.d.ts +186 -0
  68. package/dist/observability/index.d.ts +60 -0
  69. package/dist/observability/integration.d.ts +147 -0
  70. package/dist/observability/metrics.d.ts +184 -0
  71. package/dist/observability/tracing.d.ts +187 -0
  72. package/dist/optimization/bun-utilities.d.ts +224 -0
  73. package/dist/query-builder-integration.d.ts +36 -0
  74. package/dist/request/context.d.ts +34 -0
  75. package/dist/request/enhanced-request.d.ts +213 -0
  76. package/dist/request/macros.d.ts +300 -0
  77. package/dist/response/macros.d.ts +245 -0
  78. package/dist/response/response-factory.d.ts +133 -0
  79. package/dist/router/api-routes.d.ts +5 -0
  80. package/dist/router/file-based-routing.d.ts +118 -0
  81. package/dist/router/file-streaming.d.ts +5 -0
  82. package/dist/router/fluent-router.d.ts +315 -0
  83. package/dist/router/fluent-routing.d.ts +271 -0
  84. package/dist/router/group-organization.d.ts +5 -0
  85. package/dist/router/handler-resolver.d.ts +24 -0
  86. package/dist/router/http-methods.d.ts +10 -0
  87. package/dist/router/index.d.ts +113 -0
  88. package/dist/router/middleware-groups.d.ts +94 -0
  89. package/dist/router/middleware-integration.d.ts +112 -0
  90. package/dist/router/middleware.d.ts +5 -0
  91. package/dist/router/model-binding.d.ts +5 -0
  92. package/dist/router/optimized-route-matching.d.ts +6 -0
  93. package/dist/router/route-building.d.ts +5 -0
  94. package/dist/router/route-compiler.d.ts +153 -0
  95. package/dist/router/route-matching.d.ts +5 -0
  96. package/dist/router/route-trie.d.ts +114 -0
  97. package/dist/router/router.d.ts +276 -0
  98. package/dist/router/server.d.ts +5 -0
  99. package/dist/router/validation-integration.d.ts +170 -0
  100. package/dist/router/view-rendering.d.ts +5 -0
  101. package/dist/router/websocket.d.ts +5 -0
  102. package/dist/routing/route-caching.d.ts +129 -0
  103. package/dist/routing/route-throttling.d.ts +150 -0
  104. package/dist/routing/subdomain-routing.d.ts +206 -0
  105. package/dist/session/database-store.d.ts +51 -0
  106. package/dist/session/file-store.d.ts +22 -0
  107. package/dist/session/index.d.ts +106 -0
  108. package/dist/session/memory-store.d.ts +21 -0
  109. package/dist/session/redis-store.d.ts +31 -0
  110. package/dist/streaming/index.d.ts +3 -0
  111. package/dist/streaming/sse-handler.d.ts +138 -0
  112. package/dist/streaming/stream-handler.d.ts +114 -0
  113. package/dist/testing/auth-testing.d.ts +156 -0
  114. package/dist/testing/file-upload-testing.d.ts +190 -0
  115. package/dist/testing/index.d.ts +10 -0
  116. package/dist/testing/middleware-testing.d.ts +138 -0
  117. package/dist/testing/model-binding-testing.d.ts +186 -0
  118. package/dist/testing/performance-testing.d.ts +235 -0
  119. package/dist/testing/test-client.d.ts +117 -0
  120. package/dist/testing/test-request.d.ts +85 -0
  121. package/dist/testing/test-response.d.ts +90 -0
  122. package/dist/testing/types.d.ts +207 -0
  123. package/dist/testing/websocket-testing.d.ts +227 -0
  124. package/dist/types/controller-types.d.ts +208 -0
  125. package/dist/types/core.d.ts +543 -0
  126. package/dist/types/middleware-types.d.ts +227 -0
  127. package/dist/types/request-response-augmentation.d.ts +261 -0
  128. package/dist/types/route-inference.d.ts +168 -0
  129. package/dist/types.d.ts +1776 -0
  130. package/dist/url.d.ts +56 -0
  131. package/dist/utils/index.d.ts +1 -0
  132. package/dist/utils/query-preservation.d.ts +48 -0
  133. package/dist/utils.d.ts +69 -0
  134. package/dist/validation/validator.d.ts +140 -0
  135. package/dist/websocket/clustering.d.ts +185 -0
  136. package/package.json +3 -2
  137. package/src/auth.ts +469 -0
  138. package/src/cache/lru-cache.ts +457 -0
  139. package/src/cache/middleware-memoization.ts +531 -0
  140. package/src/cache/route-cache-warmer.ts +486 -0
  141. package/src/cache/sqlite-cache.ts +783 -0
  142. package/src/cache/streaming-cache.ts +572 -0
  143. package/src/cli/colors.ts +29 -0
  144. package/src/cli/index.ts +287 -0
  145. package/src/cli/middleware.ts +291 -0
  146. package/src/cli/openapi.ts +407 -0
  147. package/src/cli/router.ts +188 -0
  148. package/src/cli/routes.ts +265 -0
  149. package/src/cli/utils.ts +531 -0
  150. package/src/cli.ts +5 -0
  151. package/src/config.ts +322 -0
  152. package/src/container/container.ts +740 -0
  153. package/src/container/contextual-binding.ts +603 -0
  154. package/src/container/decorators.ts +359 -0
  155. package/src/container/service-provider.ts +596 -0
  156. package/src/development/hot-reload.ts +673 -0
  157. package/src/development/index.ts +499 -0
  158. package/src/development/performance-profiler.ts +717 -0
  159. package/src/development/route-debugger.ts +527 -0
  160. package/src/development/route-inspector.ts +749 -0
  161. package/src/development/typescript-utilities.ts +678 -0
  162. package/src/docs.ts +397 -0
  163. package/src/errors/circuit-breaker.ts +732 -0
  164. package/src/errors/error-handler.ts +569 -0
  165. package/src/errors/error-reporting.ts +672 -0
  166. package/src/errors/exceptions.ts +536 -0
  167. package/src/errors/graceful-degradation.ts +621 -0
  168. package/src/errors/index.ts +21 -0
  169. package/src/errors/router-errors.ts +632 -0
  170. package/src/file-serving/static-files.ts +581 -0
  171. package/src/index.ts +14 -0
  172. package/src/middleware/auth.ts +219 -0
  173. package/src/middleware/content_security_policy.ts +215 -0
  174. package/src/middleware/cors.ts +74 -0
  175. package/src/middleware/csrf.ts +108 -0
  176. package/src/middleware/ddos_protection.ts +255 -0
  177. package/src/middleware/file_security.ts +191 -0
  178. package/src/middleware/file_upload.ts +264 -0
  179. package/src/middleware/helmet.ts +268 -0
  180. package/src/middleware/index.ts +117 -0
  181. package/src/middleware/input_validation.ts +449 -0
  182. package/src/middleware/json_body.ts +37 -0
  183. package/src/middleware/performance_alerting.ts +538 -0
  184. package/src/middleware/performance_dashboard.ts +661 -0
  185. package/src/middleware/performance_monitor.ts +943 -0
  186. package/src/middleware/pipeline.ts +489 -0
  187. package/src/middleware/rate_limit.ts +245 -0
  188. package/src/middleware/request_id.ts +36 -0
  189. package/src/middleware/request_signing.ts +636 -0
  190. package/src/middleware/request_tracer.ts +636 -0
  191. package/src/middleware/response_cache.ts +743 -0
  192. package/src/middleware/security.ts +482 -0
  193. package/src/middleware/security_suite.ts +257 -0
  194. package/src/middleware/session.ts +91 -0
  195. package/src/model-binding/index.ts +18 -0
  196. package/src/model-binding/model-middleware.ts +425 -0
  197. package/src/model-binding/model-registry.ts +550 -0
  198. package/src/model-binding.ts +370 -0
  199. package/src/model-resolver-factory.ts +106 -0
  200. package/src/observability/correlation.ts +691 -0
  201. package/src/observability/health-checks.ts +729 -0
  202. package/src/observability/index.ts +184 -0
  203. package/src/observability/integration.ts +548 -0
  204. package/src/observability/metrics.ts +753 -0
  205. package/src/observability/tracing.ts +638 -0
  206. package/src/optimization/bun-utilities.ts +778 -0
  207. package/src/query-builder-integration.ts +137 -0
  208. package/src/request/context.ts +62 -0
  209. package/src/request/enhanced-request.ts +857 -0
  210. package/src/request/macros.ts +688 -0
  211. package/src/response/macros.ts +612 -0
  212. package/src/response/response-factory.ts +588 -0
  213. package/src/router/api-routes.ts +243 -0
  214. package/src/router/file-based-routing.ts +670 -0
  215. package/src/router/file-streaming.ts +372 -0
  216. package/src/router/fluent-router.ts +927 -0
  217. package/src/router/fluent-routing.ts +797 -0
  218. package/src/router/group-organization.ts +213 -0
  219. package/src/router/handler-resolver.ts +291 -0
  220. package/src/router/http-methods.ts +377 -0
  221. package/src/router/index.ts +185 -0
  222. package/src/router/middleware-groups.ts +222 -0
  223. package/src/router/middleware-integration.ts +399 -0
  224. package/src/router/middleware.ts +231 -0
  225. package/src/router/model-binding.ts +215 -0
  226. package/src/router/optimized-route-matching.ts +253 -0
  227. package/src/router/route-building.ts +212 -0
  228. package/src/router/route-compiler.ts +702 -0
  229. package/src/router/route-matching.ts +349 -0
  230. package/src/router/route-trie.ts +464 -0
  231. package/src/router/router.ts +1508 -0
  232. package/src/router/server.ts +406 -0
  233. package/src/router/validation-integration.ts +443 -0
  234. package/src/router/view-rendering.ts +233 -0
  235. package/src/router/websocket.ts +100 -0
  236. package/src/routing/route-caching.ts +402 -0
  237. package/src/routing/route-throttling.ts +469 -0
  238. package/src/routing/subdomain-routing.ts +492 -0
  239. package/src/session/database-store.ts +109 -0
  240. package/src/session/file-store.ts +148 -0
  241. package/src/session/index.ts +244 -0
  242. package/src/session/memory-store.ts +88 -0
  243. package/src/session/redis-store.ts +93 -0
  244. package/src/streaming/index.ts +17 -0
  245. package/src/streaming/sse-handler.ts +482 -0
  246. package/src/streaming/stream-handler.ts +552 -0
  247. package/src/testing/auth-testing.ts +446 -0
  248. package/src/testing/file-upload-testing.ts +543 -0
  249. package/src/testing/index.ts +10 -0
  250. package/src/testing/middleware-testing.ts +322 -0
  251. package/src/testing/model-binding-testing.ts +645 -0
  252. package/src/testing/performance-testing.ts +738 -0
  253. package/src/testing/test-client.ts +310 -0
  254. package/src/testing/test-request.ts +315 -0
  255. package/src/testing/test-response.ts +332 -0
  256. package/src/testing/types.ts +224 -0
  257. package/src/testing/websocket-testing.ts +590 -0
  258. package/src/types/controller-types.ts +385 -0
  259. package/src/types/core.ts +683 -0
  260. package/src/types/middleware-types.ts +419 -0
  261. package/src/types/request-response-augmentation.ts +489 -0
  262. package/src/types/route-inference.ts +357 -0
  263. package/src/types.ts +2049 -0
  264. package/src/url.ts +126 -0
  265. package/src/utils/index.ts +1 -0
  266. package/src/utils/query-preservation.ts +201 -0
  267. package/src/utils.ts +327 -0
  268. package/src/validation/validator.ts +685 -0
  269. package/src/websocket/clustering.ts +762 -0
@@ -0,0 +1,148 @@
1
+ import { existsSync, mkdirSync, readdirSync, unlinkSync, writeFileSync, readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { tmpdir } from 'node:os'
4
+ import type { SessionData, SessionStore } from '../types/core'
5
+
6
+ interface FileSession {
7
+ data: SessionData
8
+ expiresAt: number
9
+ }
10
+
11
+ /**
12
+ * File-based session store.
13
+ * Persists sessions to disk as JSON files.
14
+ * Suitable for single-server deployments.
15
+ */
16
+ export class FileSessionStore implements SessionStore<SessionData> {
17
+ private dir: string
18
+ private cleanupInterval: ReturnType<typeof setInterval> | null = null
19
+
20
+ constructor(
21
+ private defaultTtl: number = 86400,
22
+ dir?: string,
23
+ ) {
24
+ this.dir = dir || join(tmpdir(), 'bun-router-sessions')
25
+ if (!existsSync(this.dir)) {
26
+ mkdirSync(this.dir, { recursive: true })
27
+ }
28
+
29
+ // Cleanup every 10 minutes
30
+ this.cleanupInterval = setInterval(() => this.cleanup(), 10 * 60 * 1000)
31
+ }
32
+
33
+ private filePath(sid: string): string {
34
+ // Sanitize session ID to prevent path traversal
35
+ const safeSid = sid.replace(/[^a-zA-Z0-9_-]/g, '')
36
+ return join(this.dir, `${safeSid}.json`)
37
+ }
38
+
39
+ async get(sid: string): Promise<SessionData | null> {
40
+ const path = this.filePath(sid)
41
+ if (!existsSync(path)) return null
42
+
43
+ try {
44
+ const content = readFileSync(path, 'utf-8')
45
+ const stored: FileSession = JSON.parse(content)
46
+
47
+ if (Date.now() > stored.expiresAt) {
48
+ unlinkSync(path)
49
+ return null
50
+ }
51
+
52
+ return stored.data
53
+ }
54
+ catch {
55
+ return null
56
+ }
57
+ }
58
+
59
+ async set(sid: string, session: SessionData, ttl?: number): Promise<void> {
60
+ const stored: FileSession = {
61
+ data: session,
62
+ expiresAt: Date.now() + (ttl ?? this.defaultTtl) * 1000,
63
+ }
64
+
65
+ writeFileSync(this.filePath(sid), JSON.stringify(stored), 'utf-8')
66
+ }
67
+
68
+ async destroy(sid: string): Promise<void> {
69
+ const path = this.filePath(sid)
70
+ if (existsSync(path)) {
71
+ unlinkSync(path)
72
+ }
73
+ }
74
+
75
+ async touch(sid: string, session: SessionData, ttl?: number): Promise<void> {
76
+ const path = this.filePath(sid)
77
+ if (!existsSync(path)) return
78
+
79
+ const stored: FileSession = {
80
+ data: session,
81
+ expiresAt: Date.now() + (ttl ?? this.defaultTtl) * 1000,
82
+ }
83
+
84
+ writeFileSync(path, JSON.stringify(stored), 'utf-8')
85
+ }
86
+
87
+ async all(): Promise<Record<string, SessionData>> {
88
+ const result: Record<string, SessionData> = {}
89
+ const now = Date.now()
90
+
91
+ try {
92
+ const files = readdirSync(this.dir).filter(f => f.endsWith('.json'))
93
+ for (const file of files) {
94
+ const content = readFileSync(join(this.dir, file), 'utf-8')
95
+ const stored: FileSession = JSON.parse(content)
96
+ if (now <= stored.expiresAt) {
97
+ const sid = file.replace('.json', '')
98
+ result[sid] = stored.data
99
+ }
100
+ }
101
+ }
102
+ catch { /* ignore errors */ }
103
+
104
+ return result
105
+ }
106
+
107
+ async length(): Promise<number> {
108
+ try {
109
+ return readdirSync(this.dir).filter(f => f.endsWith('.json')).length
110
+ }
111
+ catch {
112
+ return 0
113
+ }
114
+ }
115
+
116
+ async clear(): Promise<void> {
117
+ try {
118
+ const files = readdirSync(this.dir).filter(f => f.endsWith('.json'))
119
+ for (const file of files) {
120
+ unlinkSync(join(this.dir, file))
121
+ }
122
+ }
123
+ catch { /* ignore errors */ }
124
+ }
125
+
126
+ private cleanup(): void {
127
+ const now = Date.now()
128
+ try {
129
+ const files = readdirSync(this.dir).filter(f => f.endsWith('.json'))
130
+ for (const file of files) {
131
+ const path = join(this.dir, file)
132
+ const content = readFileSync(path, 'utf-8')
133
+ const stored: FileSession = JSON.parse(content)
134
+ if (now > stored.expiresAt) {
135
+ unlinkSync(path)
136
+ }
137
+ }
138
+ }
139
+ catch { /* ignore errors */ }
140
+ }
141
+
142
+ dispose(): void {
143
+ if (this.cleanupInterval) {
144
+ clearInterval(this.cleanupInterval)
145
+ this.cleanupInterval = null
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,244 @@
1
+ import type { SessionData, SessionStore } from '../types/core'
2
+ import { MemorySessionStore } from './memory-store'
3
+ import { FileSessionStore } from './file-store'
4
+ import { RedisSessionStore } from './redis-store'
5
+ import type { RedisClient } from './redis-store'
6
+ import { DatabaseSessionStore } from './database-store'
7
+ import type { SessionDatabaseAdapter } from './database-store'
8
+
9
+ export { MemorySessionStore } from './memory-store'
10
+ export { FileSessionStore } from './file-store'
11
+ export { RedisSessionStore } from './redis-store'
12
+ export type { RedisClient } from './redis-store'
13
+ export { DatabaseSessionStore } from './database-store'
14
+ export type { SessionDatabaseAdapter } from './database-store'
15
+
16
+ export interface SessionConfig {
17
+ driver: 'memory' | 'file' | 'redis' | 'database'
18
+ ttl?: number
19
+ cookie?: {
20
+ name?: string
21
+ maxAge?: number
22
+ path?: string
23
+ httpOnly?: boolean
24
+ secure?: boolean
25
+ sameSite?: 'strict' | 'lax' | 'none'
26
+ }
27
+ // Driver-specific options
28
+ file?: {
29
+ dir?: string
30
+ }
31
+ redis?: {
32
+ client: RedisClient
33
+ prefix?: string
34
+ }
35
+ database?: {
36
+ adapter: SessionDatabaseAdapter
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Create a session store based on configuration
42
+ */
43
+ export function createSessionStore(config: SessionConfig): SessionStore<SessionData> {
44
+ const ttl = config.ttl ?? 86400
45
+
46
+ switch (config.driver) {
47
+ case 'memory':
48
+ return new MemorySessionStore(ttl)
49
+
50
+ case 'file':
51
+ return new FileSessionStore(ttl, config.file?.dir)
52
+
53
+ case 'redis': {
54
+ if (!config.redis?.client) {
55
+ throw new Error('Redis client is required for redis session driver')
56
+ }
57
+ return new RedisSessionStore(config.redis.client, ttl, config.redis.prefix)
58
+ }
59
+
60
+ case 'database': {
61
+ if (!config.database?.adapter) {
62
+ throw new Error('Database adapter is required for database session driver')
63
+ }
64
+ return new DatabaseSessionStore(config.database.adapter, ttl)
65
+ }
66
+
67
+ default:
68
+ return new MemorySessionStore(ttl)
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Session manager for request handling.
74
+ * Wraps a SessionStore and provides flash data, regeneration, etc.
75
+ */
76
+ export class SessionManager {
77
+ private sessionId: string
78
+ private data: SessionData
79
+ private store: SessionStore<SessionData>
80
+ private flashedKeys: string[] = []
81
+ private previousFlash: Record<string, unknown> = {}
82
+ private dirty = false
83
+
84
+ constructor(sessionId: string, data: SessionData, store: SessionStore<SessionData>) {
85
+ this.sessionId = sessionId
86
+ this.data = data
87
+ this.store = store
88
+
89
+ // Capture previous flash data to clear after this request
90
+ this.previousFlash = { ...(data.flash || {}) }
91
+ }
92
+
93
+ get id(): string {
94
+ return this.sessionId
95
+ }
96
+
97
+ /**
98
+ * Get a value from the session
99
+ */
100
+ get<T = unknown>(key: string): T | undefined {
101
+ // Check flash data first
102
+ if (this.data.flash && key in this.data.flash) {
103
+ return this.data.flash[key] as T
104
+ }
105
+ if (this.data.data && key in this.data.data) {
106
+ return this.data.data[key] as T
107
+ }
108
+ return undefined
109
+ }
110
+
111
+ /**
112
+ * Set a value in the session
113
+ */
114
+ set(key: string, value: unknown): void {
115
+ if (!this.data.data) this.data.data = {}
116
+ this.data.data[key] = value
117
+ this.dirty = true
118
+ }
119
+
120
+ /**
121
+ * Check if a key exists in the session
122
+ */
123
+ has(key: string): boolean {
124
+ return (this.data.data && key in this.data.data) || (this.data.flash && key in this.data.flash) || false
125
+ }
126
+
127
+ /**
128
+ * Remove a value from the session
129
+ */
130
+ forget(key: string): void {
131
+ if (this.data.data) {
132
+ delete this.data.data[key]
133
+ this.dirty = true
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Get all session data
139
+ */
140
+ all(): Record<string, unknown> {
141
+ return { ...(this.data.data || {}), ...(this.data.flash || {}) }
142
+ }
143
+
144
+ /**
145
+ * Flash a key-value pair for the next request only
146
+ */
147
+ flash(key: string, value: unknown): void {
148
+ if (!this.data.flash) this.data.flash = {}
149
+ this.data.flash[key] = value
150
+ this.flashedKeys.push(key)
151
+ this.dirty = true
152
+ }
153
+
154
+ /**
155
+ * Get a flash value (available for current request)
156
+ */
157
+ getFlash<T = unknown>(key: string): T | undefined {
158
+ return this.previousFlash[key] as T | undefined
159
+ }
160
+
161
+ /**
162
+ * Keep specific flash data for another request
163
+ */
164
+ keep(keys: string[]): void {
165
+ for (const key of keys) {
166
+ if (key in this.previousFlash) {
167
+ this.flash(key, this.previousFlash[key])
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Reflash all flash data for another request
174
+ */
175
+ reflash(): void {
176
+ for (const [key, value] of Object.entries(this.previousFlash)) {
177
+ this.flash(key, value)
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Regenerate session ID (keeps data)
183
+ */
184
+ async regenerate(): Promise<string> {
185
+ const oldId = this.sessionId
186
+ this.sessionId = generateSessionId()
187
+
188
+ // Move data to new session
189
+ await this.store.set(this.sessionId, this.data)
190
+ await this.store.destroy(oldId)
191
+
192
+ return this.sessionId
193
+ }
194
+
195
+ /**
196
+ * Invalidate the session (new ID, clear data)
197
+ */
198
+ async invalidate(): Promise<string> {
199
+ const oldId = this.sessionId
200
+ this.sessionId = generateSessionId()
201
+ this.data = { id: this.sessionId }
202
+
203
+ await this.store.destroy(oldId)
204
+ await this.store.set(this.sessionId, this.data)
205
+
206
+ return this.sessionId
207
+ }
208
+
209
+ /**
210
+ * Save session data to the store.
211
+ * Call this at the end of the request.
212
+ */
213
+ async save(): Promise<void> {
214
+ // Clear old flash data (data that was available this request but not re-flashed)
215
+ if (this.data.flash) {
216
+ const newFlash: Record<string, unknown> = {}
217
+ for (const key of this.flashedKeys) {
218
+ if (this.data.flash[key] !== undefined) {
219
+ newFlash[key] = this.data.flash[key]
220
+ }
221
+ }
222
+ this.data.flash = Object.keys(newFlash).length > 0 ? newFlash : undefined
223
+ }
224
+
225
+ await this.store.set(this.sessionId, this.data)
226
+ }
227
+
228
+ /**
229
+ * Destroy the session completely
230
+ */
231
+ async destroy(): Promise<void> {
232
+ await this.store.destroy(this.sessionId)
233
+ this.data = {}
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Generate a cryptographically secure session ID
239
+ */
240
+ export function generateSessionId(): string {
241
+ const bytes = new Uint8Array(32)
242
+ crypto.getRandomValues(bytes)
243
+ return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('')
244
+ }
@@ -0,0 +1,88 @@
1
+ import type { SessionData, SessionStore } from '../types/core'
2
+
3
+ interface StoredSession {
4
+ data: SessionData
5
+ expiresAt: number
6
+ }
7
+
8
+ /**
9
+ * In-memory session store.
10
+ * Fast but data is lost on server restart.
11
+ * Suitable for development or single-process deployments.
12
+ */
13
+ export class MemorySessionStore implements SessionStore<SessionData> {
14
+ private sessions = new Map<string, StoredSession>()
15
+ private cleanupInterval: ReturnType<typeof setInterval> | null = null
16
+
17
+ constructor(private defaultTtl: number = 86400) {
18
+ // Run cleanup every 5 minutes
19
+ this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000)
20
+ }
21
+
22
+ async get(sid: string): Promise<SessionData | null> {
23
+ const stored = this.sessions.get(sid)
24
+ if (!stored) return null
25
+
26
+ if (Date.now() > stored.expiresAt) {
27
+ this.sessions.delete(sid)
28
+ return null
29
+ }
30
+
31
+ return stored.data
32
+ }
33
+
34
+ async set(sid: string, session: SessionData, ttl?: number): Promise<void> {
35
+ const expiresAt = Date.now() + (ttl ?? this.defaultTtl) * 1000
36
+ this.sessions.set(sid, { data: session, expiresAt })
37
+ }
38
+
39
+ async destroy(sid: string): Promise<void> {
40
+ this.sessions.delete(sid)
41
+ }
42
+
43
+ async touch(sid: string, session: SessionData, ttl?: number): Promise<void> {
44
+ const stored = this.sessions.get(sid)
45
+ if (stored) {
46
+ stored.expiresAt = Date.now() + (ttl ?? this.defaultTtl) * 1000
47
+ stored.data = session
48
+ }
49
+ }
50
+
51
+ async all(): Promise<Record<string, SessionData>> {
52
+ const result: Record<string, SessionData> = {}
53
+ const now = Date.now()
54
+
55
+ for (const [sid, stored] of this.sessions) {
56
+ if (now <= stored.expiresAt) {
57
+ result[sid] = stored.data
58
+ }
59
+ }
60
+
61
+ return result
62
+ }
63
+
64
+ async length(): Promise<number> {
65
+ return this.sessions.size
66
+ }
67
+
68
+ async clear(): Promise<void> {
69
+ this.sessions.clear()
70
+ }
71
+
72
+ private cleanup(): void {
73
+ const now = Date.now()
74
+ for (const [sid, stored] of this.sessions) {
75
+ if (now > stored.expiresAt) {
76
+ this.sessions.delete(sid)
77
+ }
78
+ }
79
+ }
80
+
81
+ dispose(): void {
82
+ if (this.cleanupInterval) {
83
+ clearInterval(this.cleanupInterval)
84
+ this.cleanupInterval = null
85
+ }
86
+ this.sessions.clear()
87
+ }
88
+ }
@@ -0,0 +1,93 @@
1
+ import type { SessionData, SessionStore } from '../types/core'
2
+
3
+ /**
4
+ * Minimal Redis client interface.
5
+ * Compatible with ioredis, node-redis, or any client implementing these methods.
6
+ */
7
+ export interface RedisClient {
8
+ get(key: string): Promise<string | null>
9
+ set(key: string, value: string, ...args: any[]): Promise<any>
10
+ del(key: string | string[]): Promise<number>
11
+ expire(key: string, seconds: number): Promise<number>
12
+ keys(pattern: string): Promise<string[]>
13
+ }
14
+
15
+ /**
16
+ * Redis-backed session store.
17
+ * Ideal for distributed/multi-server deployments.
18
+ * Requires a Redis client to be passed in.
19
+ */
20
+ export class RedisSessionStore implements SessionStore<SessionData> {
21
+ private prefix: string
22
+
23
+ constructor(
24
+ private client: RedisClient,
25
+ private defaultTtl: number = 86400,
26
+ prefix: string = 'sess:',
27
+ ) {
28
+ this.prefix = prefix
29
+ }
30
+
31
+ private key(sid: string): string {
32
+ return `${this.prefix}${sid}`
33
+ }
34
+
35
+ async get(sid: string): Promise<SessionData | null> {
36
+ const data = await this.client.get(this.key(sid))
37
+ if (!data) return null
38
+
39
+ try {
40
+ return JSON.parse(data) as SessionData
41
+ }
42
+ catch {
43
+ return null
44
+ }
45
+ }
46
+
47
+ async set(sid: string, session: SessionData, ttl?: number): Promise<void> {
48
+ const key = this.key(sid)
49
+ const seconds = ttl ?? this.defaultTtl
50
+ await this.client.set(key, JSON.stringify(session), 'EX', seconds)
51
+ }
52
+
53
+ async destroy(sid: string): Promise<void> {
54
+ await this.client.del(this.key(sid))
55
+ }
56
+
57
+ async touch(sid: string, session: SessionData, ttl?: number): Promise<void> {
58
+ const key = this.key(sid)
59
+ const seconds = ttl ?? this.defaultTtl
60
+ // Reset TTL and update data
61
+ await this.client.set(key, JSON.stringify(session), 'EX', seconds)
62
+ }
63
+
64
+ async all(): Promise<Record<string, SessionData>> {
65
+ const keys = await this.client.keys(`${this.prefix}*`)
66
+ const result: Record<string, SessionData> = {}
67
+
68
+ for (const key of keys) {
69
+ const data = await this.client.get(key)
70
+ if (data) {
71
+ try {
72
+ const sid = key.slice(this.prefix.length)
73
+ result[sid] = JSON.parse(data)
74
+ }
75
+ catch { /* skip invalid data */ }
76
+ }
77
+ }
78
+
79
+ return result
80
+ }
81
+
82
+ async length(): Promise<number> {
83
+ const keys = await this.client.keys(`${this.prefix}*`)
84
+ return keys.length
85
+ }
86
+
87
+ async clear(): Promise<void> {
88
+ const keys = await this.client.keys(`${this.prefix}*`)
89
+ if (keys.length > 0) {
90
+ await this.client.del(keys)
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,17 @@
1
+ export type { SSEConfig, SSEEvent } from '../types'
2
+
3
+ export {
4
+ createSSEMiddleware,
5
+ SSEConnectionManager,
6
+ SSEHandler,
7
+ sseManager,
8
+ SSEUtils,
9
+ } from './sse-handler'
10
+
11
+ export {
12
+ type BaseStreamConfig,
13
+ FileStreamHandler,
14
+ StreamHandler,
15
+ StreamResponse,
16
+ StreamUtils,
17
+ } from './stream-handler'