@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.
- package/{LICENSE → LICENSE.md} +1 -1
- package/README.md +203 -0
- package/dist/browser/index.js +34 -0
- package/dist/browser/index.js.map +123 -0
- package/dist/browser/scope/index.js +4 -0
- package/dist/browser/scope/index.js.map +9 -0
- package/dist/node/index.cjs +17329 -0
- package/dist/node/index.cjs.map +123 -0
- package/dist/node/index.js +17303 -0
- package/dist/node/index.js.map +123 -0
- package/dist/node/scope/index.cjs +48 -0
- package/dist/node/scope/index.cjs.map +9 -0
- package/dist/node/scope/index.js +29 -0
- package/dist/node/scope/index.js.map +9 -0
- package/dist/ts/app/app-context.d.ts +9 -0
- package/dist/ts/app/app-context.d.ts.map +1 -0
- package/dist/ts/app/app-lifecycle.d.ts +6 -0
- package/dist/ts/app/app-lifecycle.d.ts.map +1 -0
- package/dist/ts/app/app.d.ts +24 -0
- package/dist/ts/app/app.d.ts.map +1 -0
- package/dist/ts/app/index.d.ts +4 -0
- package/dist/ts/app/index.d.ts.map +1 -0
- package/dist/ts/application/create-request-context.d.ts +4 -0
- package/dist/ts/application/create-request-context.d.ts.map +1 -0
- package/dist/ts/application/create-request-context.test.d.ts +2 -0
- package/dist/ts/application/create-request-context.test.d.ts.map +1 -0
- package/dist/ts/application/use-declaro.d.ts +3 -0
- package/dist/ts/application/use-declaro.d.ts.map +1 -0
- package/dist/ts/auth/permission-validator.d.ts +35 -0
- package/dist/ts/auth/permission-validator.d.ts.map +1 -0
- package/dist/ts/auth/permission-validator.test.d.ts +2 -0
- package/dist/ts/auth/permission-validator.test.d.ts.map +1 -0
- package/dist/{context → ts/context}/context-consumer.d.ts +4 -0
- package/dist/ts/context/context-consumer.d.ts.map +1 -0
- package/dist/ts/context/context.d.ts +193 -0
- package/dist/ts/context/context.d.ts.map +1 -0
- package/dist/ts/context/context.test.d.ts +2 -0
- package/dist/ts/context/context.test.d.ts.map +1 -0
- package/dist/ts/context/legacy-context.test.d.ts +2 -0
- package/dist/ts/context/legacy-context.test.d.ts.map +1 -0
- package/dist/{context → ts/context}/validators.d.ts +2 -1
- package/dist/ts/context/validators.d.ts.map +1 -0
- package/dist/ts/dataflow/index.d.ts +2 -0
- package/dist/ts/dataflow/index.d.ts.map +1 -0
- package/dist/ts/dataflow/objects.d.ts +7 -0
- package/dist/ts/dataflow/objects.d.ts.map +1 -0
- package/dist/ts/dataflow/objects.test.d.ts +2 -0
- package/dist/ts/dataflow/objects.test.d.ts.map +1 -0
- package/dist/ts/errors/errors.d.ts +49 -0
- package/dist/ts/errors/errors.d.ts.map +1 -0
- package/dist/ts/events/event-manager.d.ts +19 -0
- package/dist/ts/events/event-manager.d.ts.map +1 -0
- package/dist/ts/events/event-manager.spec.d.ts +2 -0
- package/dist/ts/events/event-manager.spec.d.ts.map +1 -0
- package/dist/ts/events/index.d.ts +2 -0
- package/dist/ts/events/index.d.ts.map +1 -0
- package/dist/ts/http/headers.d.ts +21 -0
- package/dist/ts/http/headers.d.ts.map +1 -0
- package/dist/ts/http/headers.spec.d.ts +2 -0
- package/dist/ts/http/headers.spec.d.ts.map +1 -0
- package/dist/ts/http/request-context.d.ts +17 -0
- package/dist/ts/http/request-context.d.ts.map +1 -0
- package/dist/ts/http/request-context.spec.d.ts +2 -0
- package/dist/ts/http/request-context.spec.d.ts.map +1 -0
- package/dist/ts/http/request.d.ts +31 -0
- package/dist/ts/http/request.d.ts.map +1 -0
- package/dist/ts/http/request.spec.d.ts +2 -0
- package/dist/ts/http/request.spec.d.ts.map +1 -0
- package/dist/ts/http/url.d.ts +9 -0
- package/dist/ts/http/url.d.ts.map +1 -0
- package/dist/ts/http/url.spec.d.ts +2 -0
- package/dist/ts/http/url.spec.d.ts.map +1 -0
- package/dist/ts/index.d.ts +45 -0
- package/dist/ts/index.d.ts.map +1 -0
- package/dist/{pipelines → ts/pipelines}/index.d.ts +1 -0
- package/dist/ts/pipelines/index.d.ts.map +1 -0
- package/dist/{pipelines → ts/pipelines}/pipeline-action.d.ts +1 -0
- package/dist/ts/pipelines/pipeline-action.d.ts.map +1 -0
- package/dist/ts/pipelines/pipeline-action.test.d.ts +2 -0
- package/dist/ts/pipelines/pipeline-action.test.d.ts.map +1 -0
- package/dist/{pipelines → ts/pipelines}/pipeline.d.ts +3 -2
- package/dist/ts/pipelines/pipeline.d.ts.map +1 -0
- package/dist/ts/pipelines/pipeline.test.d.ts +2 -0
- package/dist/ts/pipelines/pipeline.test.d.ts.map +1 -0
- package/dist/ts/schema/entity-schema.test.d.ts +1 -0
- package/dist/ts/schema/entity-schema.test.d.ts.map +1 -0
- package/dist/ts/schema/json-schema.d.ts +4 -0
- package/dist/ts/schema/json-schema.d.ts.map +1 -0
- package/dist/ts/schema/labels.d.ts +14 -0
- package/dist/ts/schema/labels.d.ts.map +1 -0
- package/dist/ts/schema/model-schema.d.ts +75 -0
- package/dist/ts/schema/model-schema.d.ts.map +1 -0
- package/dist/ts/schema/model-schema.test.d.ts +2 -0
- package/dist/ts/schema/model-schema.test.d.ts.map +1 -0
- package/dist/ts/schema/model.d.ts +30 -0
- package/dist/ts/schema/model.d.ts.map +1 -0
- package/dist/ts/schema/schema-mixin.d.ts +24 -0
- package/dist/ts/schema/schema-mixin.d.ts.map +1 -0
- package/dist/ts/schema/test/mock-model.d.ts +8 -0
- package/dist/ts/schema/test/mock-model.d.ts.map +1 -0
- package/dist/ts/scope/index.d.ts +34 -0
- package/dist/ts/scope/index.d.ts.map +1 -0
- package/dist/ts/shared/utils/action-descriptor.d.ts +28 -0
- package/dist/ts/shared/utils/action-descriptor.d.ts.map +1 -0
- package/dist/ts/shared/utils/action-descriptor.test.d.ts +2 -0
- package/dist/ts/shared/utils/action-descriptor.test.d.ts.map +1 -0
- package/dist/{timing.d.ts → ts/timing.d.ts} +1 -0
- package/dist/ts/timing.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/arrays.d.ts +1 -0
- package/dist/ts/typescript/arrays.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/baseModel.d.ts +1 -0
- package/dist/ts/typescript/baseModel.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/classes.d.ts +1 -0
- package/dist/ts/typescript/classes.d.ts.map +1 -0
- package/dist/ts/typescript/constant-manipulation/snake-case.d.ts +23 -0
- package/dist/ts/typescript/constant-manipulation/snake-case.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/errors.d.ts +1 -0
- package/dist/ts/typescript/errors.d.ts.map +1 -0
- package/dist/ts/typescript/fetch.d.ts +3 -0
- package/dist/ts/typescript/fetch.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/generics.d.ts +1 -0
- package/dist/ts/typescript/generics.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/index.d.ts +2 -0
- package/dist/ts/typescript/index.d.ts.map +1 -0
- package/dist/ts/typescript/objects.d.ts +26 -0
- package/dist/ts/typescript/objects.d.ts.map +1 -0
- package/dist/{typescript → ts/typescript}/promises.d.ts +1 -0
- package/dist/ts/typescript/promises.d.ts.map +1 -0
- package/dist/{validation → ts/validation}/index.d.ts +1 -0
- package/dist/ts/validation/index.d.ts.map +1 -0
- package/dist/{validation → ts/validation}/validation.d.ts +1 -0
- package/dist/ts/validation/validation.d.ts.map +1 -0
- package/dist/{validation → ts/validation}/validator.d.ts +1 -0
- package/dist/ts/validation/validator.d.ts.map +1 -0
- package/dist/ts/validation/validator.test.d.ts +2 -0
- package/dist/ts/validation/validator.test.d.ts.map +1 -0
- package/package.json +44 -14
- package/src/app/app-context.ts +13 -0
- package/src/app/app-lifecycle.ts +15 -0
- package/src/app/app.ts +47 -0
- package/src/app/index.ts +3 -34
- package/src/application/create-request-context.test.ts +351 -0
- package/src/application/create-request-context.ts +19 -0
- package/src/application/use-declaro.ts +19 -0
- package/src/auth/permission-validator.test.ts +445 -0
- package/src/auth/permission-validator.ts +135 -0
- package/src/context/context-consumer.ts +4 -4
- package/src/context/context.test.ts +851 -93
- package/src/context/context.ts +414 -33
- package/src/context/legacy-context.test.ts +141 -0
- package/src/dataflow/objects.test.ts +7 -7
- package/src/dataflow/objects.ts +10 -9
- package/src/errors/errors.ts +89 -0
- package/src/events/event-manager.spec.ts +183 -8
- package/src/events/event-manager.ts +58 -31
- package/src/http/headers.spec.ts +48 -0
- package/src/http/headers.ts +31 -0
- package/src/http/request-context.spec.ts +39 -0
- package/src/http/request-context.ts +54 -0
- package/src/http/request.spec.ts +52 -0
- package/src/http/request.ts +43 -0
- package/src/http/url.spec.ts +87 -0
- package/src/http/url.ts +48 -0
- package/src/index.ts +41 -6
- package/src/pipelines/pipeline.test.ts +11 -9
- package/src/schema/entity-schema.test.ts +0 -0
- package/src/schema/json-schema.ts +3 -0
- package/src/schema/labels.ts +30 -0
- package/src/schema/model-schema.test.ts +128 -0
- package/src/schema/model-schema.ts +197 -0
- package/src/schema/model.ts +112 -0
- package/src/schema/schema-mixin.ts +51 -0
- package/src/schema/test/mock-model.ts +15 -0
- package/src/scope/index.ts +33 -0
- package/src/shared/utils/action-descriptor.test.ts +182 -0
- package/src/shared/utils/action-descriptor.ts +102 -0
- package/src/typescript/constant-manipulation/snake-case.md +496 -0
- package/src/typescript/constant-manipulation/snake-case.ts +76 -0
- package/src/typescript/index.ts +1 -0
- package/src/typescript/objects.ts +39 -5
- package/src/validation/validator.test.ts +12 -20
- package/dist/app/index.d.ts +0 -20
- package/dist/context/context.d.ts +0 -86
- package/dist/context/context.test.d.ts +0 -1
- package/dist/context/index.d.ts +0 -3
- package/dist/dataflow/index.d.ts +0 -1
- package/dist/dataflow/objects.d.ts +0 -5
- package/dist/dataflow/objects.test.d.ts +0 -1
- package/dist/events/event-manager.d.ts +0 -11
- package/dist/events/event-manager.spec.d.ts +0 -1
- package/dist/events/index.d.ts +0 -1
- package/dist/helpers/index.d.ts +0 -1
- package/dist/helpers/ucfirst.d.ts +0 -1
- package/dist/index.d.ts +0 -13
- package/dist/interfaces/IDatastoreProvider.d.ts +0 -16
- package/dist/interfaces/IStore.d.ts +0 -4
- package/dist/interfaces/index.d.ts +0 -2
- package/dist/pipelines/pipeline-action.test.d.ts +0 -1
- package/dist/pipelines/pipeline.test.d.ts +0 -1
- package/dist/pkg.cjs +0 -2
- package/dist/pkg.mjs +0 -358
- package/dist/schema/define-model.d.ts +0 -7
- package/dist/schema/define-model.test.d.ts +0 -1
- package/dist/schema/formats.d.ts +0 -10
- package/dist/schema/index.d.ts +0 -3
- package/dist/schema/supported-types.d.ts +0 -12
- package/dist/schema/supported-types.test.d.ts +0 -1
- package/dist/schema/transform-model.d.ts +0 -4
- package/dist/schema/transform-model.test.d.ts +0 -1
- package/dist/schema/types.d.ts +0 -29
- package/dist/server/index.d.ts +0 -2
- package/dist/typescript/fetch.d.ts +0 -2
- package/dist/typescript/objects.d.ts +0 -6
- package/dist/validation/validator.test.d.ts +0 -1
- package/src/context/index.ts +0 -3
- package/src/helpers/index.ts +0 -1
- package/src/helpers/ucfirst.ts +0 -3
- package/src/interfaces/IDatastoreProvider.ts +0 -23
- package/src/interfaces/IStore.ts +0 -4
- package/src/interfaces/index.ts +0 -2
- package/src/schema/define-model.test.ts +0 -35
- package/src/schema/define-model.ts +0 -19
- package/src/schema/formats.ts +0 -23
- package/src/schema/index.ts +0 -3
- package/src/schema/supported-types.test.ts +0 -20
- package/src/schema/supported-types.ts +0 -15
- package/src/schema/transform-model.test.ts +0 -31
- package/src/schema/transform-model.ts +0 -24
- package/src/schema/types.ts +0 -43
- package/src/server/index.ts +0 -3
- package/tsconfig.json +0 -8
- 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¶m2=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¶m2=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
|
+
}
|