@declaro/core 2.0.0-beta.9 → 2.0.0-beta.90

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 (232) hide show
  1. package/{LICENSE → LICENSE.md} +1 -1
  2. package/README.md +203 -0
  3. package/dist/browser/index.js +34 -0
  4. package/dist/browser/index.js.map +123 -0
  5. package/dist/browser/scope/index.js +4 -0
  6. package/dist/browser/scope/index.js.map +9 -0
  7. package/dist/node/index.cjs +17329 -0
  8. package/dist/node/index.cjs.map +123 -0
  9. package/dist/node/index.js +17303 -0
  10. package/dist/node/index.js.map +123 -0
  11. package/dist/node/scope/index.cjs +48 -0
  12. package/dist/node/scope/index.cjs.map +9 -0
  13. package/dist/node/scope/index.js +29 -0
  14. package/dist/node/scope/index.js.map +9 -0
  15. package/dist/ts/app/app-context.d.ts +9 -0
  16. package/dist/ts/app/app-context.d.ts.map +1 -0
  17. package/dist/ts/app/app-lifecycle.d.ts +6 -0
  18. package/dist/ts/app/app-lifecycle.d.ts.map +1 -0
  19. package/dist/ts/app/app.d.ts +24 -0
  20. package/dist/ts/app/app.d.ts.map +1 -0
  21. package/dist/ts/app/index.d.ts +4 -0
  22. package/dist/ts/app/index.d.ts.map +1 -0
  23. package/dist/ts/application/create-request-context.d.ts +4 -0
  24. package/dist/ts/application/create-request-context.d.ts.map +1 -0
  25. package/dist/ts/application/create-request-context.test.d.ts +2 -0
  26. package/dist/ts/application/create-request-context.test.d.ts.map +1 -0
  27. package/dist/ts/application/use-declaro.d.ts +3 -0
  28. package/dist/ts/application/use-declaro.d.ts.map +1 -0
  29. package/dist/ts/auth/permission-validator.d.ts +35 -0
  30. package/dist/ts/auth/permission-validator.d.ts.map +1 -0
  31. package/dist/ts/auth/permission-validator.test.d.ts +2 -0
  32. package/dist/ts/auth/permission-validator.test.d.ts.map +1 -0
  33. package/dist/{context → ts/context}/context-consumer.d.ts +4 -0
  34. package/dist/ts/context/context-consumer.d.ts.map +1 -0
  35. package/dist/ts/context/context.d.ts +193 -0
  36. package/dist/ts/context/context.d.ts.map +1 -0
  37. package/dist/ts/context/context.test.d.ts +2 -0
  38. package/dist/ts/context/context.test.d.ts.map +1 -0
  39. package/dist/ts/context/legacy-context.test.d.ts +2 -0
  40. package/dist/ts/context/legacy-context.test.d.ts.map +1 -0
  41. package/dist/{context → ts/context}/validators.d.ts +2 -1
  42. package/dist/ts/context/validators.d.ts.map +1 -0
  43. package/dist/ts/dataflow/index.d.ts +2 -0
  44. package/dist/ts/dataflow/index.d.ts.map +1 -0
  45. package/dist/ts/dataflow/objects.d.ts +7 -0
  46. package/dist/ts/dataflow/objects.d.ts.map +1 -0
  47. package/dist/ts/dataflow/objects.test.d.ts +2 -0
  48. package/dist/ts/dataflow/objects.test.d.ts.map +1 -0
  49. package/dist/ts/errors/errors.d.ts +49 -0
  50. package/dist/ts/errors/errors.d.ts.map +1 -0
  51. package/dist/ts/events/event-manager.d.ts +19 -0
  52. package/dist/ts/events/event-manager.d.ts.map +1 -0
  53. package/dist/ts/events/event-manager.spec.d.ts +2 -0
  54. package/dist/ts/events/event-manager.spec.d.ts.map +1 -0
  55. package/dist/ts/events/index.d.ts +2 -0
  56. package/dist/ts/events/index.d.ts.map +1 -0
  57. package/dist/ts/http/headers.d.ts +21 -0
  58. package/dist/ts/http/headers.d.ts.map +1 -0
  59. package/dist/ts/http/headers.spec.d.ts +2 -0
  60. package/dist/ts/http/headers.spec.d.ts.map +1 -0
  61. package/dist/ts/http/request-context.d.ts +17 -0
  62. package/dist/ts/http/request-context.d.ts.map +1 -0
  63. package/dist/ts/http/request-context.spec.d.ts +2 -0
  64. package/dist/ts/http/request-context.spec.d.ts.map +1 -0
  65. package/dist/ts/http/request.d.ts +31 -0
  66. package/dist/ts/http/request.d.ts.map +1 -0
  67. package/dist/ts/http/request.spec.d.ts +2 -0
  68. package/dist/ts/http/request.spec.d.ts.map +1 -0
  69. package/dist/ts/http/url.d.ts +9 -0
  70. package/dist/ts/http/url.d.ts.map +1 -0
  71. package/dist/ts/http/url.spec.d.ts +2 -0
  72. package/dist/ts/http/url.spec.d.ts.map +1 -0
  73. package/dist/ts/index.d.ts +45 -0
  74. package/dist/ts/index.d.ts.map +1 -0
  75. package/dist/{pipelines → ts/pipelines}/index.d.ts +1 -0
  76. package/dist/ts/pipelines/index.d.ts.map +1 -0
  77. package/dist/{pipelines → ts/pipelines}/pipeline-action.d.ts +1 -0
  78. package/dist/ts/pipelines/pipeline-action.d.ts.map +1 -0
  79. package/dist/ts/pipelines/pipeline-action.test.d.ts +2 -0
  80. package/dist/ts/pipelines/pipeline-action.test.d.ts.map +1 -0
  81. package/dist/{pipelines → ts/pipelines}/pipeline.d.ts +3 -2
  82. package/dist/ts/pipelines/pipeline.d.ts.map +1 -0
  83. package/dist/ts/pipelines/pipeline.test.d.ts +2 -0
  84. package/dist/ts/pipelines/pipeline.test.d.ts.map +1 -0
  85. package/dist/ts/schema/entity-schema.test.d.ts +1 -0
  86. package/dist/ts/schema/entity-schema.test.d.ts.map +1 -0
  87. package/dist/ts/schema/json-schema.d.ts +4 -0
  88. package/dist/ts/schema/json-schema.d.ts.map +1 -0
  89. package/dist/ts/schema/labels.d.ts +14 -0
  90. package/dist/ts/schema/labels.d.ts.map +1 -0
  91. package/dist/ts/schema/model-schema.d.ts +75 -0
  92. package/dist/ts/schema/model-schema.d.ts.map +1 -0
  93. package/dist/ts/schema/model-schema.test.d.ts +2 -0
  94. package/dist/ts/schema/model-schema.test.d.ts.map +1 -0
  95. package/dist/ts/schema/model.d.ts +30 -0
  96. package/dist/ts/schema/model.d.ts.map +1 -0
  97. package/dist/ts/schema/schema-mixin.d.ts +24 -0
  98. package/dist/ts/schema/schema-mixin.d.ts.map +1 -0
  99. package/dist/ts/schema/test/mock-model.d.ts +8 -0
  100. package/dist/ts/schema/test/mock-model.d.ts.map +1 -0
  101. package/dist/ts/scope/index.d.ts +34 -0
  102. package/dist/ts/scope/index.d.ts.map +1 -0
  103. package/dist/ts/shared/utils/action-descriptor.d.ts +28 -0
  104. package/dist/ts/shared/utils/action-descriptor.d.ts.map +1 -0
  105. package/dist/ts/shared/utils/action-descriptor.test.d.ts +2 -0
  106. package/dist/ts/shared/utils/action-descriptor.test.d.ts.map +1 -0
  107. package/dist/{timing.d.ts → ts/timing.d.ts} +1 -0
  108. package/dist/ts/timing.d.ts.map +1 -0
  109. package/dist/{typescript → ts/typescript}/arrays.d.ts +1 -0
  110. package/dist/ts/typescript/arrays.d.ts.map +1 -0
  111. package/dist/{typescript → ts/typescript}/baseModel.d.ts +1 -0
  112. package/dist/ts/typescript/baseModel.d.ts.map +1 -0
  113. package/dist/{typescript → ts/typescript}/classes.d.ts +1 -0
  114. package/dist/ts/typescript/classes.d.ts.map +1 -0
  115. package/dist/ts/typescript/constant-manipulation/snake-case.d.ts +23 -0
  116. package/dist/ts/typescript/constant-manipulation/snake-case.d.ts.map +1 -0
  117. package/dist/{typescript → ts/typescript}/errors.d.ts +1 -0
  118. package/dist/ts/typescript/errors.d.ts.map +1 -0
  119. package/dist/ts/typescript/fetch.d.ts +3 -0
  120. package/dist/ts/typescript/fetch.d.ts.map +1 -0
  121. package/dist/{typescript → ts/typescript}/generics.d.ts +1 -0
  122. package/dist/ts/typescript/generics.d.ts.map +1 -0
  123. package/dist/{typescript → ts/typescript}/index.d.ts +2 -0
  124. package/dist/ts/typescript/index.d.ts.map +1 -0
  125. package/dist/ts/typescript/objects.d.ts +26 -0
  126. package/dist/ts/typescript/objects.d.ts.map +1 -0
  127. package/dist/{typescript → ts/typescript}/promises.d.ts +1 -0
  128. package/dist/ts/typescript/promises.d.ts.map +1 -0
  129. package/dist/{validation → ts/validation}/index.d.ts +1 -0
  130. package/dist/ts/validation/index.d.ts.map +1 -0
  131. package/dist/{validation → ts/validation}/validation.d.ts +1 -0
  132. package/dist/ts/validation/validation.d.ts.map +1 -0
  133. package/dist/{validation → ts/validation}/validator.d.ts +1 -0
  134. package/dist/ts/validation/validator.d.ts.map +1 -0
  135. package/dist/ts/validation/validator.test.d.ts +2 -0
  136. package/dist/ts/validation/validator.test.d.ts.map +1 -0
  137. package/package.json +44 -14
  138. package/src/app/app-context.ts +13 -0
  139. package/src/app/app-lifecycle.ts +15 -0
  140. package/src/app/app.ts +47 -0
  141. package/src/app/index.ts +3 -34
  142. package/src/application/create-request-context.test.ts +351 -0
  143. package/src/application/create-request-context.ts +19 -0
  144. package/src/application/use-declaro.ts +19 -0
  145. package/src/auth/permission-validator.test.ts +445 -0
  146. package/src/auth/permission-validator.ts +135 -0
  147. package/src/context/context-consumer.ts +4 -4
  148. package/src/context/context.test.ts +851 -93
  149. package/src/context/context.ts +414 -33
  150. package/src/context/legacy-context.test.ts +141 -0
  151. package/src/dataflow/objects.test.ts +7 -7
  152. package/src/dataflow/objects.ts +10 -9
  153. package/src/errors/errors.ts +89 -0
  154. package/src/events/event-manager.spec.ts +183 -8
  155. package/src/events/event-manager.ts +58 -31
  156. package/src/http/headers.spec.ts +48 -0
  157. package/src/http/headers.ts +31 -0
  158. package/src/http/request-context.spec.ts +39 -0
  159. package/src/http/request-context.ts +54 -0
  160. package/src/http/request.spec.ts +52 -0
  161. package/src/http/request.ts +43 -0
  162. package/src/http/url.spec.ts +87 -0
  163. package/src/http/url.ts +48 -0
  164. package/src/index.ts +41 -6
  165. package/src/pipelines/pipeline.test.ts +11 -9
  166. package/src/schema/entity-schema.test.ts +0 -0
  167. package/src/schema/json-schema.ts +3 -0
  168. package/src/schema/labels.ts +30 -0
  169. package/src/schema/model-schema.test.ts +128 -0
  170. package/src/schema/model-schema.ts +197 -0
  171. package/src/schema/model.ts +112 -0
  172. package/src/schema/schema-mixin.ts +51 -0
  173. package/src/schema/test/mock-model.ts +15 -0
  174. package/src/scope/index.ts +33 -0
  175. package/src/shared/utils/action-descriptor.test.ts +182 -0
  176. package/src/shared/utils/action-descriptor.ts +102 -0
  177. package/src/typescript/constant-manipulation/snake-case.md +496 -0
  178. package/src/typescript/constant-manipulation/snake-case.ts +76 -0
  179. package/src/typescript/index.ts +1 -0
  180. package/src/typescript/objects.ts +39 -5
  181. package/src/validation/validator.test.ts +12 -20
  182. package/dist/app/index.d.ts +0 -20
  183. package/dist/context/context.d.ts +0 -86
  184. package/dist/context/context.test.d.ts +0 -1
  185. package/dist/context/index.d.ts +0 -3
  186. package/dist/dataflow/index.d.ts +0 -1
  187. package/dist/dataflow/objects.d.ts +0 -5
  188. package/dist/dataflow/objects.test.d.ts +0 -1
  189. package/dist/events/event-manager.d.ts +0 -11
  190. package/dist/events/event-manager.spec.d.ts +0 -1
  191. package/dist/events/index.d.ts +0 -1
  192. package/dist/helpers/index.d.ts +0 -1
  193. package/dist/helpers/ucfirst.d.ts +0 -1
  194. package/dist/index.d.ts +0 -13
  195. package/dist/interfaces/IDatastoreProvider.d.ts +0 -16
  196. package/dist/interfaces/IStore.d.ts +0 -4
  197. package/dist/interfaces/index.d.ts +0 -2
  198. package/dist/pipelines/pipeline-action.test.d.ts +0 -1
  199. package/dist/pipelines/pipeline.test.d.ts +0 -1
  200. package/dist/pkg.cjs +0 -2
  201. package/dist/pkg.mjs +0 -358
  202. package/dist/schema/define-model.d.ts +0 -7
  203. package/dist/schema/define-model.test.d.ts +0 -1
  204. package/dist/schema/formats.d.ts +0 -10
  205. package/dist/schema/index.d.ts +0 -3
  206. package/dist/schema/supported-types.d.ts +0 -12
  207. package/dist/schema/supported-types.test.d.ts +0 -1
  208. package/dist/schema/transform-model.d.ts +0 -4
  209. package/dist/schema/transform-model.test.d.ts +0 -1
  210. package/dist/schema/types.d.ts +0 -29
  211. package/dist/server/index.d.ts +0 -2
  212. package/dist/typescript/fetch.d.ts +0 -2
  213. package/dist/typescript/objects.d.ts +0 -6
  214. package/dist/validation/validator.test.d.ts +0 -1
  215. package/src/context/index.ts +0 -3
  216. package/src/helpers/index.ts +0 -1
  217. package/src/helpers/ucfirst.ts +0 -3
  218. package/src/interfaces/IDatastoreProvider.ts +0 -23
  219. package/src/interfaces/IStore.ts +0 -4
  220. package/src/interfaces/index.ts +0 -2
  221. package/src/schema/define-model.test.ts +0 -35
  222. package/src/schema/define-model.ts +0 -19
  223. package/src/schema/formats.ts +0 -23
  224. package/src/schema/index.ts +0 -3
  225. package/src/schema/supported-types.test.ts +0 -20
  226. package/src/schema/supported-types.ts +0 -15
  227. package/src/schema/transform-model.test.ts +0 -31
  228. package/src/schema/transform-model.ts +0 -24
  229. package/src/schema/types.ts +0 -43
  230. package/src/server/index.ts +0 -3
  231. package/tsconfig.json +0 -8
  232. package/vite.config.ts +0 -24
@@ -0,0 +1,351 @@
1
+ import { createRequest } from 'node-mocks-http'
2
+ import { beforeEach, describe, expect, it } from 'vitest'
3
+ import { Context, type DeclaroRequestScope, type DeclaroScope } from '../context/context'
4
+ import type { Request } from '../http/request'
5
+ import { createRequestContext } from './create-request-context'
6
+ import { useDeclaro } from './use-declaro'
7
+
8
+ // Extended scopes for testing
9
+ interface TestAppScope extends DeclaroScope {
10
+ testAppValue?: string
11
+ sharedKey?: string
12
+ appValue?: string
13
+ appFactory?: string
14
+ appClass?: TestClass
15
+ eagerDep?: string
16
+ }
17
+
18
+ interface TestRequestScope extends DeclaroRequestScope {
19
+ middlewareTest?: string
20
+ asyncTest?: string
21
+ middleware1?: string
22
+ middleware2?: string
23
+ sharedKey?: string
24
+ appValue?: string
25
+ appFactory?: string
26
+ appClass?: TestClass
27
+ }
28
+
29
+ class TestClass {
30
+ getValue() {
31
+ return 'class-result'
32
+ }
33
+ }
34
+
35
+ describe('createRequestContext', () => {
36
+ let appContext: Context<TestAppScope>
37
+ let mockRequest: Request
38
+
39
+ beforeEach(async () => {
40
+ // Create app context with Declaro middleware
41
+ appContext = new Context<TestAppScope>()
42
+ await appContext.use(useDeclaro())
43
+
44
+ // Add middleware to provide headers (this is typically done by the framework)
45
+ appContext.scope.requestMiddleware.push((context) => {
46
+ const request = context.resolve('request')
47
+ context.registerValue('headers', request?.headers ?? {})
48
+ })
49
+
50
+ // Create mock request
51
+ mockRequest = createRequest({
52
+ method: 'POST',
53
+ url: 'https://example.com/api/test?param1=value1&param2=value2',
54
+ headers: {
55
+ 'Content-Type': 'application/json',
56
+ Authorization: 'Bearer test-token-123',
57
+ 'X-Custom-Header': 'custom-value',
58
+ 'User-Agent': 'test-agent/1.0',
59
+ },
60
+ body: {
61
+ test: 'data',
62
+ },
63
+ }) as Request
64
+ })
65
+
66
+ describe('basic functionality', () => {
67
+ it('should create a request context from app context', async () => {
68
+ const requestContext = await createRequestContext(appContext, mockRequest)
69
+
70
+ expect(requestContext).toBeInstanceOf(Context)
71
+ expect(requestContext).not.toBe(appContext)
72
+ })
73
+
74
+ it('should extend the app context', async () => {
75
+ // Add a value to app context
76
+ appContext.registerValue('testAppValue', 'app-test-value')
77
+
78
+ const requestContext = (await createRequestContext(appContext, mockRequest)) as Context<TestRequestScope>
79
+
80
+ // Should be able to access app context values
81
+ expect((requestContext as any).resolve('testAppValue')).toBe('app-test-value')
82
+ })
83
+
84
+ it('should run request middleware', async () => {
85
+ let middlewareRan = false
86
+
87
+ appContext.scope.requestMiddleware.push((context) => {
88
+ middlewareRan = true
89
+ ;(context as any).registerValue('middlewareTest', 'middleware-value')
90
+ })
91
+
92
+ const requestContext = await createRequestContext(appContext, mockRequest)
93
+
94
+ expect(middlewareRan).toBe(true)
95
+ expect((requestContext as any).resolve('middlewareTest')).toBe('middleware-value')
96
+ })
97
+
98
+ it('should initialize eager dependencies', async () => {
99
+ let eagerFactoryRan = false
100
+
101
+ appContext.registerFactory(
102
+ 'eagerDep',
103
+ () => {
104
+ eagerFactoryRan = true
105
+ return 'eager-value'
106
+ },
107
+ [],
108
+ { eager: true },
109
+ )
110
+
111
+ await createRequestContext(appContext, mockRequest)
112
+
113
+ expect(eagerFactoryRan).toBe(true)
114
+ })
115
+ })
116
+
117
+ describe('request injection', () => {
118
+ it('should inject the request object', async () => {
119
+ const requestContext = await createRequestContext(appContext, mockRequest)
120
+
121
+ const injectedRequest = requestContext.resolve('request')
122
+
123
+ expect(injectedRequest).toBe(mockRequest)
124
+ expect(injectedRequest.method).toBe('POST')
125
+ expect(injectedRequest.url).toBe('https://example.com/api/test?param1=value1&param2=value2')
126
+ })
127
+
128
+ it('should provide request via scope property', async () => {
129
+ const requestContext = await createRequestContext(appContext, mockRequest)
130
+
131
+ expect(requestContext.scope.request).toBe(mockRequest)
132
+ })
133
+ })
134
+
135
+ describe('headers injection', () => {
136
+ it('should inject headers object', async () => {
137
+ const requestContext = await createRequestContext(appContext, mockRequest)
138
+
139
+ const headers = requestContext.resolve('headers')
140
+
141
+ expect(headers).toBeDefined()
142
+ expect(headers['content-type']).toBe('application/json')
143
+ expect(headers['authorization']).toBe('Bearer test-token-123')
144
+ expect(headers['x-custom-header']).toBe('custom-value')
145
+ expect(headers['user-agent']).toBe('test-agent/1.0')
146
+ })
147
+
148
+ it('should provide headers via scope property', async () => {
149
+ const requestContext = await createRequestContext(appContext, mockRequest)
150
+
151
+ expect(requestContext.scope.headers).toBeDefined()
152
+ expect(requestContext.scope.headers).toBe(mockRequest.headers)
153
+ })
154
+
155
+ it('should handle missing headers gracefully', async () => {
156
+ const requestWithoutHeaders = createRequest({
157
+ method: 'GET',
158
+ url: '/test',
159
+ // No headers
160
+ }) as Request
161
+
162
+ const requestContext = await createRequestContext(appContext, requestWithoutHeaders)
163
+
164
+ const headers = requestContext.resolve('headers')
165
+ expect(headers).toBeDefined()
166
+ expect(Object.keys(headers).length).toBeGreaterThanOrEqual(0)
167
+ })
168
+ })
169
+
170
+ describe('header function injection', () => {
171
+ it('should inject header function', async () => {
172
+ const requestContext = await createRequestContext(appContext, mockRequest)
173
+
174
+ const headerFunction = requestContext.resolve('header')
175
+
176
+ expect(typeof headerFunction).toBe('function')
177
+ })
178
+
179
+ it('should provide header function via scope property', async () => {
180
+ const requestContext = await createRequestContext(appContext, mockRequest)
181
+
182
+ expect(typeof requestContext.scope.header).toBe('function')
183
+ })
184
+
185
+ it('should retrieve specific headers', async () => {
186
+ const requestContext = await createRequestContext(appContext, mockRequest)
187
+
188
+ const header = requestContext.scope.header
189
+
190
+ expect(header('content-type')).toBe('application/json')
191
+ expect(header('authorization')).toBe('Bearer test-token-123')
192
+ expect(header('x-custom-header')).toBe('custom-value')
193
+ expect(header('user-agent')).toBe('test-agent/1.0')
194
+ })
195
+
196
+ it('should return undefined for non-existent headers', async () => {
197
+ const requestContext = await createRequestContext(appContext, mockRequest)
198
+
199
+ const header = requestContext.scope.header
200
+
201
+ expect(header('non-existent-header' as any)).toBeUndefined()
202
+ })
203
+
204
+ it('should handle case-insensitive header names', async () => {
205
+ const requestContext = await createRequestContext(appContext, mockRequest)
206
+
207
+ const header = requestContext.scope.header
208
+
209
+ // HTTP headers are case-insensitive, Node.js typically lowercases them
210
+ expect(header('Content-Type' as any)).toBe('application/json')
211
+ })
212
+ })
213
+
214
+ describe('middleware arrays injection', () => {
215
+ it('should inject requestMiddleware array', async () => {
216
+ const requestContext = await createRequestContext(appContext, mockRequest)
217
+
218
+ const requestMiddleware = requestContext.resolve('requestMiddleware')
219
+
220
+ expect(Array.isArray(requestMiddleware)).toBe(true)
221
+ })
222
+
223
+ it('should inject nodeMiddleware array', async () => {
224
+ const requestContext = await createRequestContext(appContext, mockRequest)
225
+
226
+ const nodeMiddleware = requestContext.resolve('nodeMiddleware')
227
+
228
+ expect(Array.isArray(nodeMiddleware)).toBe(true)
229
+ })
230
+
231
+ it('should provide middleware arrays via scope properties', async () => {
232
+ const requestContext = await createRequestContext(appContext, mockRequest)
233
+
234
+ expect(Array.isArray(requestContext.scope.requestMiddleware)).toBe(true)
235
+ expect(Array.isArray(requestContext.scope.nodeMiddleware)).toBe(true)
236
+ })
237
+ })
238
+
239
+ describe('type safety and scope validation', () => {
240
+ it('should satisfy RequestScope interface', async () => {
241
+ const requestContext = await createRequestContext(appContext, mockRequest)
242
+
243
+ // Test that all RequestScope properties are available
244
+ expect(requestContext.scope.request).toBeDefined()
245
+ expect(requestContext.scope.headers).toBeDefined()
246
+ expect(typeof requestContext.scope.header).toBe('function')
247
+ expect(Array.isArray(requestContext.scope.requestMiddleware)).toBe(true)
248
+ expect(Array.isArray(requestContext.scope.nodeMiddleware)).toBe(true)
249
+ })
250
+
251
+ it('should allow injection of all RequestScope dependencies', async () => {
252
+ const requestContext = await createRequestContext(appContext, mockRequest)
253
+
254
+ // Test direct resolution of all expected dependencies
255
+ expect(() => requestContext.resolve('request')).not.toThrow()
256
+ expect(() => requestContext.resolve('headers')).not.toThrow()
257
+ expect(() => requestContext.resolve('header')).not.toThrow()
258
+ expect(() => requestContext.resolve('requestMiddleware')).not.toThrow()
259
+ expect(() => requestContext.resolve('nodeMiddleware')).not.toThrow()
260
+ })
261
+ })
262
+
263
+ describe('dependency inheritance', () => {
264
+ it('should inherit all app dependencies', async () => {
265
+ // Register various types of dependencies in app context
266
+ appContext.registerValue('appValue', 'test-value')
267
+ appContext.registerFactory('appFactory', () => 'factory-result', [])
268
+ ;(appContext as any).registerClass('appClass', TestClass, [])
269
+
270
+ const requestContext = (await createRequestContext(appContext, mockRequest)) as Context<TestRequestScope>
271
+
272
+ // All should be accessible in request context
273
+ expect((requestContext as any).resolve('appValue')).toBe('test-value')
274
+ expect((requestContext as any).resolve('appFactory')).toBe('factory-result')
275
+ expect(((requestContext as any).resolve('appClass') as TestClass).getValue()).toBe('class-result')
276
+ })
277
+
278
+ it('should allow request context to override app dependencies', async () => {
279
+ appContext.registerValue('sharedKey', 'app-value')
280
+
281
+ // Add middleware that overrides the value
282
+ appContext.scope.requestMiddleware.push((context) => {
283
+ ;(context as any).registerValue('sharedKey', 'request-value')
284
+ })
285
+
286
+ const requestContext = await createRequestContext(appContext, mockRequest)
287
+
288
+ expect((requestContext as any).resolve('sharedKey')).toBe('request-value')
289
+ })
290
+ })
291
+
292
+ describe('error handling', () => {
293
+ it('should handle middleware errors gracefully', async () => {
294
+ // Add middleware that throws
295
+ appContext.scope.requestMiddleware.push(() => {
296
+ throw new Error('Middleware error')
297
+ })
298
+
299
+ await expect(createRequestContext(appContext, mockRequest)).rejects.toThrow('Middleware error')
300
+ })
301
+
302
+ it('should handle null/undefined request', async () => {
303
+ // The current implementation doesn't validate the request parameter
304
+ // It will set request to null and continue processing
305
+ const requestContext = await createRequestContext(appContext, null as any)
306
+
307
+ expect(requestContext.resolve('request')).toBeNull()
308
+ expect(requestContext.resolve('headers')).toEqual({})
309
+ })
310
+ })
311
+
312
+ describe('async middleware support', () => {
313
+ it('should handle async middleware', async () => {
314
+ let asyncMiddlewareRan = false
315
+
316
+ appContext.scope.requestMiddleware.push(async (context) => {
317
+ await new Promise((resolve) => setTimeout(resolve, 10))
318
+ asyncMiddlewareRan = true
319
+ ;(context as any).registerValue('asyncTest', 'async-result')
320
+ })
321
+
322
+ const requestContext = await createRequestContext(appContext, mockRequest)
323
+
324
+ expect(asyncMiddlewareRan).toBe(true)
325
+ expect((requestContext as any).resolve('asyncTest')).toBe('async-result')
326
+ })
327
+
328
+ it('should run multiple async middleware in sequence', async () => {
329
+ const executionOrder: number[] = []
330
+
331
+ appContext.scope.requestMiddleware.push(
332
+ async (context) => {
333
+ await new Promise((resolve) => setTimeout(resolve, 20))
334
+ executionOrder.push(1)
335
+ ;(context as any).registerValue('middleware1', 'first')
336
+ },
337
+ async (context) => {
338
+ await new Promise((resolve) => setTimeout(resolve, 10))
339
+ executionOrder.push(2)
340
+ ;(context as any).registerValue('middleware2', 'second')
341
+ },
342
+ )
343
+
344
+ const requestContext = await createRequestContext(appContext, mockRequest)
345
+
346
+ expect(executionOrder).toEqual([1, 2])
347
+ expect((requestContext as any).resolve('middleware1')).toBe('first')
348
+ expect((requestContext as any).resolve('middleware2')).toBe('second')
349
+ })
350
+ })
351
+ })
@@ -0,0 +1,19 @@
1
+ import { Context, type DeclaroRequestScope, type DeclaroScope } from '../context/context'
2
+ import { provideRequest, type Request } from '../http/request'
3
+
4
+ export async function createRequestContext<S extends DeclaroScope>(
5
+ appContext: Context<S>,
6
+ request: Request,
7
+ ): Promise<Context<DeclaroRequestScope>> {
8
+ const context = new Context<DeclaroRequestScope>()
9
+ context.extend(appContext)
10
+
11
+ provideRequest(context, request)
12
+
13
+ const requestMiddleware = appContext.scope.requestMiddleware
14
+ await context.use(...requestMiddleware)
15
+
16
+ await context.initializeEagerDependencies()
17
+
18
+ return context
19
+ }
@@ -0,0 +1,19 @@
1
+ import type { IncomingHttpHeaders } from 'http'
2
+ import type { Context, DeclaroScope } from '../context/context'
3
+ import { provideRequestMiddleware } from '../http/request-context'
4
+
5
+ export function useDeclaro() {
6
+ return async <S extends DeclaroScope>(context: Context<S>) => {
7
+ context.registerValue('requestMiddleware', [])
8
+ context.registerValue('nodeMiddleware', [])
9
+
10
+ provideRequestMiddleware(context, async (context) => {
11
+ // TODO: Register headers
12
+ // TODO: Support modern web Request type, and Headers instance for full fetch compatibility
13
+ context.registerValue('header', (header: keyof IncomingHttpHeaders) => {
14
+ const headers = context.resolve('headers')
15
+ return headers[header]
16
+ })
17
+ })
18
+ }
19
+ }