@navios/core 1.0.0-alpha.2 → 1.0.0-alpha.4
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/CHANGELOG.md +90 -0
- package/lib/{index-BJjk2X1S.d.mts → index-6S7veHKD.d.mts} +845 -294
- package/lib/index-6S7veHKD.d.mts.map +1 -0
- package/lib/{index-DZ6NU03y.d.cts → index-r0i2txmg.d.cts} +845 -294
- package/lib/index-r0i2txmg.d.cts.map +1 -0
- package/lib/index.cjs +4420 -84
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +2 -2
- package/lib/index.d.mts +2 -2
- package/lib/index.mjs +4328 -3
- package/lib/index.mjs.map +1 -0
- package/lib/legacy-compat/index.cjs +41 -126
- package/lib/legacy-compat/index.cjs.map +1 -1
- package/lib/legacy-compat/index.d.cts +4 -60
- package/lib/legacy-compat/index.d.cts.map +1 -1
- package/lib/legacy-compat/index.d.mts +4 -60
- package/lib/legacy-compat/index.d.mts.map +1 -1
- package/lib/legacy-compat/index.mjs +14 -119
- package/lib/legacy-compat/index.mjs.map +1 -1
- package/lib/navios.factory-BanZIvtR.cjs +4134 -0
- package/lib/navios.factory-BanZIvtR.cjs.map +1 -0
- package/lib/navios.factory-C75yZCoD.mjs +3831 -0
- package/lib/navios.factory-C75yZCoD.mjs.map +1 -0
- package/lib/testing/index.cjs +3 -3
- package/lib/testing/index.cjs.map +1 -1
- package/lib/testing/index.d.cts +1 -1
- package/lib/testing/index.d.mts +1 -1
- package/lib/testing/index.mjs +2 -2
- package/lib/tokens-4J9sredA.mjs +100 -0
- package/lib/tokens-4J9sredA.mjs.map +1 -0
- package/lib/tokens-BuXXB01L.cjs +196 -0
- package/lib/tokens-BuXXB01L.cjs.map +1 -0
- package/lib/{use-guards.decorator-Be_QUx6b.mjs → use-guards.decorator-BecoQSmE.mjs} +3 -70
- package/lib/use-guards.decorator-BecoQSmE.mjs.map +1 -0
- package/lib/{use-guards.decorator-B6tghdxM.cjs → use-guards.decorator-DgD-kxF5.cjs} +7 -158
- package/lib/use-guards.decorator-DgD-kxF5.cjs.map +1 -0
- package/package.json +4 -4
- package/src/__tests__/attribute.factory.spec.mts +300 -0
- package/src/__tests__/console-logger.service.spec.mts +312 -0
- package/src/__tests__/guard-runner.service.spec.mts +399 -0
- package/src/__tests__/logger.service.spec.mts +147 -0
- package/src/__tests__/responders.spec.mts +6 -5
- package/src/factories/adapter.factory.mts +20 -0
- package/src/factories/endpoint-adapter.factory.mts +1 -1
- package/src/factories/http-adapter.factory.mts +1 -1
- package/src/factories/index.mts +1 -0
- package/src/factories/multipart-adapter.factory.mts +1 -1
- package/src/factories/reply.factory.mts +1 -1
- package/src/factories/request.factory.mts +1 -1
- package/src/factories/stream-adapter.factory.mts +1 -1
- package/src/factories/xml-stream-adapter.factory.mts +1 -1
- package/src/index.mts +1 -0
- package/src/interfaces/abstract-adapter.interface.mts +32 -0
- package/src/interfaces/abstract-http-adapter.interface.mts +27 -20
- package/src/interfaces/abstract-http-handler-adapter.interface.mts +86 -2
- package/src/interfaces/adapter-environment.interface.mts +74 -0
- package/src/interfaces/index.mts +2 -0
- package/src/interfaces/plugin.interface.mts +50 -16
- package/src/legacy-compat/attribute.factory.mts +2 -2
- package/src/legacy-compat/decorators/controller.decorator.mts +1 -1
- package/src/legacy-compat/decorators/endpoint.decorator.mts +1 -1
- package/src/legacy-compat/decorators/header.decorator.mts +2 -1
- package/src/legacy-compat/decorators/http-code.decorator.mts +2 -1
- package/src/legacy-compat/decorators/index.mts +2 -2
- package/src/legacy-compat/decorators/module.decorator.mts +1 -1
- package/src/legacy-compat/decorators/multipart.decorator.mts +1 -1
- package/src/legacy-compat/decorators/stream.decorator.mts +1 -1
- package/src/legacy-compat/decorators/use-guards.decorator.mts +1 -1
- package/src/legacy-compat/index.mts +10 -5
- package/src/logger/console-logger.service.mts +97 -7
- package/src/metadata/module.metadata.mts +43 -0
- package/src/navios.application.mts +172 -60
- package/src/navios.environment.mts +22 -12
- package/src/navios.factory.mts +31 -10
- package/src/services/abstract-handler-adapter.service.mts +366 -0
- package/src/services/index.mts +1 -0
- package/src/services/module-loader.service.mts +1 -0
- package/src/tokens/adapter.token.mts +6 -0
- package/src/tokens/http-adapter.token.mts +1 -1
- package/src/tokens/index.mts +1 -0
- package/src/utils/adapter-supports.util.mts +47 -0
- package/src/utils/index.mts +1 -0
- package/lib/index-BJjk2X1S.d.mts.map +0 -1
- package/lib/index-DZ6NU03y.d.cts.map +0 -1
- package/lib/src-C46ePe3d.cjs +0 -8022
- package/lib/src-C46ePe3d.cjs.map +0 -1
- package/lib/src-K2k0riYJ.mjs +0 -7587
- package/lib/src-K2k0riYJ.mjs.map +0 -1
- package/lib/use-guards.decorator-B6tghdxM.cjs.map +0 -1
- package/lib/use-guards.decorator-Be_QUx6b.mjs.map +0 -1
- package/src/legacy-compat/context-compat.mts +0 -95
- package/src/legacy-compat/decorators/factory.decorator.mts +0 -37
- package/src/legacy-compat/decorators/injectable.decorator.mts +0 -41
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import type { ScopedContainer } from '@navios/di'
|
|
2
|
+
|
|
3
|
+
import { Container, Injectable, InjectionToken } from '@navios/di'
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
AbstractExecutionContext,
|
|
9
|
+
CanActivate,
|
|
10
|
+
} from '../interfaces/index.mjs'
|
|
11
|
+
import type {
|
|
12
|
+
ControllerMetadata,
|
|
13
|
+
HandlerMetadata,
|
|
14
|
+
ModuleMetadata,
|
|
15
|
+
} from '../metadata/index.mjs'
|
|
16
|
+
|
|
17
|
+
import { HttpException } from '../exceptions/index.mjs'
|
|
18
|
+
import { LoggerOutput } from '../logger/logger.tokens.mjs'
|
|
19
|
+
import {
|
|
20
|
+
ForbiddenResponderToken,
|
|
21
|
+
InternalServerErrorResponderToken,
|
|
22
|
+
NotFoundResponderToken,
|
|
23
|
+
ValidationErrorResponderToken,
|
|
24
|
+
} from '../responders/tokens/responder.tokens.mjs'
|
|
25
|
+
import { GuardRunnerService } from '../services/guard-runner.service.mjs'
|
|
26
|
+
|
|
27
|
+
// Mock responders
|
|
28
|
+
const createMockResponder = (statusCode: number, message: string) => ({
|
|
29
|
+
getResponse: vi.fn().mockReturnValue({
|
|
30
|
+
statusCode,
|
|
31
|
+
payload: { message },
|
|
32
|
+
headers: { 'content-type': 'application/problem+json' },
|
|
33
|
+
}),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Mock logger output
|
|
37
|
+
const mockLoggerOutput = {
|
|
38
|
+
log: vi.fn(),
|
|
39
|
+
error: vi.fn(),
|
|
40
|
+
warn: vi.fn(),
|
|
41
|
+
debug: vi.fn(),
|
|
42
|
+
verbose: vi.fn(),
|
|
43
|
+
fatal: vi.fn(),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Mock execution context
|
|
47
|
+
const createMockExecutionContext = (): AbstractExecutionContext => {
|
|
48
|
+
const mockReply = {
|
|
49
|
+
status: vi.fn().mockReturnThis(),
|
|
50
|
+
send: vi.fn().mockReturnThis(),
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
getRequest: vi.fn().mockReturnValue({}),
|
|
54
|
+
getReply: vi.fn().mockReturnValue(mockReply),
|
|
55
|
+
getHandler: vi.fn(),
|
|
56
|
+
getModule: vi.fn(),
|
|
57
|
+
getController: vi.fn(),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Mock guard
|
|
62
|
+
const createMockGuard = (
|
|
63
|
+
canActivateResult: boolean | Promise<boolean>,
|
|
64
|
+
): CanActivate => ({
|
|
65
|
+
canActivate: vi.fn().mockImplementation(() => canActivateResult),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('GuardRunnerService', () => {
|
|
69
|
+
let container: Container
|
|
70
|
+
let mockForbiddenResponder: ReturnType<typeof createMockResponder>
|
|
71
|
+
let mockInternalErrorResponder: ReturnType<typeof createMockResponder>
|
|
72
|
+
let mockNotFoundResponder: ReturnType<typeof createMockResponder>
|
|
73
|
+
let mockValidationErrorResponder: ReturnType<typeof createMockResponder>
|
|
74
|
+
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
container = new Container()
|
|
77
|
+
vi.clearAllMocks()
|
|
78
|
+
|
|
79
|
+
// Create mock responders
|
|
80
|
+
mockForbiddenResponder = createMockResponder(403, 'Forbidden')
|
|
81
|
+
mockInternalErrorResponder = createMockResponder(
|
|
82
|
+
500,
|
|
83
|
+
'Internal Server Error',
|
|
84
|
+
)
|
|
85
|
+
mockNotFoundResponder = createMockResponder(404, 'Not Found')
|
|
86
|
+
mockValidationErrorResponder = createMockResponder(400, 'Validation Error')
|
|
87
|
+
|
|
88
|
+
// Register all required dependencies
|
|
89
|
+
container.addInstance(ForbiddenResponderToken, mockForbiddenResponder)
|
|
90
|
+
container.addInstance(
|
|
91
|
+
InternalServerErrorResponderToken,
|
|
92
|
+
mockInternalErrorResponder,
|
|
93
|
+
)
|
|
94
|
+
container.addInstance(NotFoundResponderToken, mockNotFoundResponder)
|
|
95
|
+
container.addInstance(
|
|
96
|
+
ValidationErrorResponderToken,
|
|
97
|
+
mockValidationErrorResponder,
|
|
98
|
+
)
|
|
99
|
+
container.addInstance(LoggerOutput, mockLoggerOutput)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
afterEach(async () => {
|
|
103
|
+
await container.dispose()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('runGuardsStatic', () => {
|
|
107
|
+
it('should return true when all guards pass', async () => {
|
|
108
|
+
const service = await container.get(GuardRunnerService)
|
|
109
|
+
const guards = [createMockGuard(true), createMockGuard(true)]
|
|
110
|
+
const context = createMockExecutionContext()
|
|
111
|
+
|
|
112
|
+
const result = await service.runGuardsStatic(guards, context)
|
|
113
|
+
|
|
114
|
+
expect(result).toBe(true)
|
|
115
|
+
expect(guards[0].canActivate).toHaveBeenCalledWith(context)
|
|
116
|
+
expect(guards[1].canActivate).toHaveBeenCalledWith(context)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should return false when a guard returns false', async () => {
|
|
120
|
+
const service = await container.get(GuardRunnerService)
|
|
121
|
+
const guards = [createMockGuard(true), createMockGuard(false)]
|
|
122
|
+
const context = createMockExecutionContext()
|
|
123
|
+
|
|
124
|
+
const result = await service.runGuardsStatic(guards, context)
|
|
125
|
+
|
|
126
|
+
expect(result).toBe(false)
|
|
127
|
+
expect(context.getReply().status).toHaveBeenCalledWith(403)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should stop execution when a guard fails', async () => {
|
|
131
|
+
const service = await container.get(GuardRunnerService)
|
|
132
|
+
const firstGuard = createMockGuard(false)
|
|
133
|
+
const secondGuard = createMockGuard(true)
|
|
134
|
+
const guards = [firstGuard, secondGuard]
|
|
135
|
+
const context = createMockExecutionContext()
|
|
136
|
+
|
|
137
|
+
await service.runGuardsStatic(guards, context)
|
|
138
|
+
|
|
139
|
+
expect(firstGuard.canActivate).toHaveBeenCalled()
|
|
140
|
+
expect(secondGuard.canActivate).not.toHaveBeenCalled()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should handle HttpException from guard', async () => {
|
|
144
|
+
const service = await container.get(GuardRunnerService)
|
|
145
|
+
const httpException = new HttpException(401, 'Unauthorized')
|
|
146
|
+
const guard = {
|
|
147
|
+
canActivate: vi.fn().mockRejectedValue(httpException),
|
|
148
|
+
}
|
|
149
|
+
const context = createMockExecutionContext()
|
|
150
|
+
|
|
151
|
+
const result = await service.runGuardsStatic([guard], context)
|
|
152
|
+
|
|
153
|
+
expect(result).toBe(false)
|
|
154
|
+
expect(context.getReply().status).toHaveBeenCalledWith(401)
|
|
155
|
+
expect(context.getReply().send).toHaveBeenCalledWith('Unauthorized')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should handle unknown errors from guard', async () => {
|
|
159
|
+
const service = await container.get(GuardRunnerService)
|
|
160
|
+
const guard = {
|
|
161
|
+
canActivate: vi.fn().mockRejectedValue(new Error('Unknown error')),
|
|
162
|
+
}
|
|
163
|
+
const context = createMockExecutionContext()
|
|
164
|
+
|
|
165
|
+
const result = await service.runGuardsStatic([guard], context)
|
|
166
|
+
|
|
167
|
+
expect(result).toBe(false)
|
|
168
|
+
expect(mockLoggerOutput.error).toHaveBeenCalled()
|
|
169
|
+
expect(context.getReply().status).toHaveBeenCalledWith(500)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should handle async guards', async () => {
|
|
173
|
+
const service = await container.get(GuardRunnerService)
|
|
174
|
+
const asyncGuard = createMockGuard(Promise.resolve(true))
|
|
175
|
+
const context = createMockExecutionContext()
|
|
176
|
+
|
|
177
|
+
const result = await service.runGuardsStatic([asyncGuard], context)
|
|
178
|
+
|
|
179
|
+
expect(result).toBe(true)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should work with empty guard array', async () => {
|
|
183
|
+
const service = await container.get(GuardRunnerService)
|
|
184
|
+
const context = createMockExecutionContext()
|
|
185
|
+
|
|
186
|
+
const result = await service.runGuardsStatic([], context)
|
|
187
|
+
|
|
188
|
+
expect(result).toBe(true)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('runGuards', () => {
|
|
193
|
+
it('should resolve guards from scoped container', async () => {
|
|
194
|
+
@Injectable()
|
|
195
|
+
class TestGuard implements CanActivate {
|
|
196
|
+
canActivate = vi.fn().mockReturnValue(true)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const service = await container.get(GuardRunnerService)
|
|
200
|
+
const testGuardInstance = new TestGuard()
|
|
201
|
+
|
|
202
|
+
const mockScopedContainer = {
|
|
203
|
+
get: vi.fn().mockResolvedValue(testGuardInstance),
|
|
204
|
+
} as unknown as ScopedContainer
|
|
205
|
+
|
|
206
|
+
const guards = new Set([TestGuard])
|
|
207
|
+
const context = createMockExecutionContext()
|
|
208
|
+
|
|
209
|
+
const result = await service.runGuards(
|
|
210
|
+
guards,
|
|
211
|
+
context,
|
|
212
|
+
mockScopedContainer,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
expect(result).toBe(true)
|
|
216
|
+
expect(mockScopedContainer.get).toHaveBeenCalled()
|
|
217
|
+
expect(testGuardInstance.canActivate).toHaveBeenCalledWith(context)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('should throw error for guard without canActivate', async () => {
|
|
221
|
+
@Injectable()
|
|
222
|
+
class InvalidGuard {
|
|
223
|
+
// Missing canActivate
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const service = await container.get(GuardRunnerService)
|
|
227
|
+
const mockScopedContainer = {
|
|
228
|
+
get: vi.fn().mockResolvedValue(new InvalidGuard()),
|
|
229
|
+
} as unknown as ScopedContainer
|
|
230
|
+
|
|
231
|
+
const guards = new Set([InvalidGuard as any])
|
|
232
|
+
const context = createMockExecutionContext()
|
|
233
|
+
|
|
234
|
+
await expect(
|
|
235
|
+
service.runGuards(guards, context, mockScopedContainer),
|
|
236
|
+
).rejects.toThrow('does not implement canActivate')
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('should reverse guard order (module -> controller -> endpoint)', async () => {
|
|
240
|
+
const callOrder: string[] = []
|
|
241
|
+
|
|
242
|
+
@Injectable()
|
|
243
|
+
class Guard1 implements CanActivate {
|
|
244
|
+
canActivate() {
|
|
245
|
+
callOrder.push('guard1')
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@Injectable()
|
|
251
|
+
class Guard2 implements CanActivate {
|
|
252
|
+
canActivate() {
|
|
253
|
+
callOrder.push('guard2')
|
|
254
|
+
return true
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@Injectable()
|
|
259
|
+
class Guard3 implements CanActivate {
|
|
260
|
+
canActivate() {
|
|
261
|
+
callOrder.push('guard3')
|
|
262
|
+
return true
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const service = await container.get(GuardRunnerService)
|
|
267
|
+
|
|
268
|
+
const mockScopedContainer = {
|
|
269
|
+
get: vi.fn().mockImplementation((token) => {
|
|
270
|
+
if (token === Guard1) return new Guard1()
|
|
271
|
+
if (token === Guard2) return new Guard2()
|
|
272
|
+
if (token === Guard3) return new Guard3()
|
|
273
|
+
}),
|
|
274
|
+
} as unknown as ScopedContainer
|
|
275
|
+
|
|
276
|
+
// Order in Set: Guard1, Guard2, Guard3
|
|
277
|
+
// Should execute in reverse: Guard3, Guard2, Guard1
|
|
278
|
+
const guards = new Set([Guard1, Guard2, Guard3])
|
|
279
|
+
const context = createMockExecutionContext()
|
|
280
|
+
|
|
281
|
+
await service.runGuards(guards, context, mockScopedContainer)
|
|
282
|
+
|
|
283
|
+
expect(callOrder).toEqual(['guard3', 'guard2', 'guard1'])
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('makeContext', () => {
|
|
288
|
+
it('should merge guards from module, controller, and endpoint', async () => {
|
|
289
|
+
const service = await container.get(GuardRunnerService)
|
|
290
|
+
|
|
291
|
+
class ModuleGuard {}
|
|
292
|
+
class ControllerGuard {}
|
|
293
|
+
class EndpointGuard {}
|
|
294
|
+
|
|
295
|
+
const moduleMetadata = {
|
|
296
|
+
guards: new Set([ModuleGuard]),
|
|
297
|
+
} as unknown as ModuleMetadata
|
|
298
|
+
|
|
299
|
+
const controllerMetadata = {
|
|
300
|
+
guards: new Set([ControllerGuard]),
|
|
301
|
+
} as unknown as ControllerMetadata
|
|
302
|
+
|
|
303
|
+
const endpointMetadata = {
|
|
304
|
+
guards: new Set([EndpointGuard]),
|
|
305
|
+
} as unknown as HandlerMetadata<any>
|
|
306
|
+
|
|
307
|
+
const result = service.makeContext(
|
|
308
|
+
moduleMetadata,
|
|
309
|
+
controllerMetadata,
|
|
310
|
+
endpointMetadata,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
expect(result.size).toBe(3)
|
|
314
|
+
expect(result.has(ModuleGuard as any)).toBe(true)
|
|
315
|
+
expect(result.has(ControllerGuard as any)).toBe(true)
|
|
316
|
+
expect(result.has(EndpointGuard as any)).toBe(true)
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('should handle empty guards at each level', async () => {
|
|
320
|
+
const service = await container.get(GuardRunnerService)
|
|
321
|
+
|
|
322
|
+
const moduleMetadata = {
|
|
323
|
+
guards: new Set(),
|
|
324
|
+
} as unknown as ModuleMetadata
|
|
325
|
+
|
|
326
|
+
const controllerMetadata = {
|
|
327
|
+
guards: new Set(),
|
|
328
|
+
} as unknown as ControllerMetadata
|
|
329
|
+
|
|
330
|
+
const endpointMetadata = {
|
|
331
|
+
guards: new Set(),
|
|
332
|
+
} as unknown as HandlerMetadata<any>
|
|
333
|
+
|
|
334
|
+
const result = service.makeContext(
|
|
335
|
+
moduleMetadata,
|
|
336
|
+
controllerMetadata,
|
|
337
|
+
endpointMetadata,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
expect(result.size).toBe(0)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('should deduplicate guards', async () => {
|
|
344
|
+
const service = await container.get(GuardRunnerService)
|
|
345
|
+
|
|
346
|
+
class SharedGuard {}
|
|
347
|
+
|
|
348
|
+
const moduleMetadata = {
|
|
349
|
+
guards: new Set([SharedGuard]),
|
|
350
|
+
} as unknown as ModuleMetadata
|
|
351
|
+
|
|
352
|
+
const controllerMetadata = {
|
|
353
|
+
guards: new Set([SharedGuard]), // Same guard
|
|
354
|
+
} as unknown as ControllerMetadata
|
|
355
|
+
|
|
356
|
+
const endpointMetadata = {
|
|
357
|
+
guards: new Set([SharedGuard]), // Same guard
|
|
358
|
+
} as unknown as HandlerMetadata<any>
|
|
359
|
+
|
|
360
|
+
const result = service.makeContext(
|
|
361
|
+
moduleMetadata,
|
|
362
|
+
controllerMetadata,
|
|
363
|
+
endpointMetadata,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
// Set deduplicates automatically
|
|
367
|
+
expect(result.size).toBe(1)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('should handle injection tokens as guards', async () => {
|
|
371
|
+
const service = await container.get(GuardRunnerService)
|
|
372
|
+
|
|
373
|
+
const GuardToken = InjectionToken.create<CanActivate>(
|
|
374
|
+
Symbol.for('GuardToken'),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
const moduleMetadata = {
|
|
378
|
+
guards: new Set([GuardToken]),
|
|
379
|
+
} as unknown as ModuleMetadata
|
|
380
|
+
|
|
381
|
+
const controllerMetadata = {
|
|
382
|
+
guards: new Set(),
|
|
383
|
+
} as unknown as ControllerMetadata
|
|
384
|
+
|
|
385
|
+
const endpointMetadata = {
|
|
386
|
+
guards: new Set(),
|
|
387
|
+
} as unknown as HandlerMetadata<any>
|
|
388
|
+
|
|
389
|
+
const result = service.makeContext(
|
|
390
|
+
moduleMetadata,
|
|
391
|
+
controllerMetadata,
|
|
392
|
+
endpointMetadata,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
expect(result.size).toBe(1)
|
|
396
|
+
expect(result.has(GuardToken)).toBe(true)
|
|
397
|
+
})
|
|
398
|
+
})
|
|
399
|
+
})
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Container } from '@navios/di'
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import type { LoggerService } from '../index.mjs'
|
|
6
|
+
|
|
7
|
+
import { LoggerInstance } from '../logger/logger.service.mjs'
|
|
8
|
+
import { LoggerOutput } from '../logger/logger.tokens.mjs'
|
|
9
|
+
|
|
10
|
+
describe('LoggerInstance', () => {
|
|
11
|
+
let container: Container
|
|
12
|
+
let mockLoggerOutput: {
|
|
13
|
+
log: ReturnType<typeof vi.fn>
|
|
14
|
+
error: ReturnType<typeof vi.fn>
|
|
15
|
+
warn: ReturnType<typeof vi.fn>
|
|
16
|
+
debug: ReturnType<typeof vi.fn>
|
|
17
|
+
verbose: ReturnType<typeof vi.fn>
|
|
18
|
+
fatal: ReturnType<typeof vi.fn>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
container = new Container()
|
|
23
|
+
mockLoggerOutput = {
|
|
24
|
+
log: vi.fn(),
|
|
25
|
+
error: vi.fn(),
|
|
26
|
+
warn: vi.fn(),
|
|
27
|
+
debug: vi.fn(),
|
|
28
|
+
verbose: vi.fn(),
|
|
29
|
+
fatal: vi.fn(),
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await container.dispose()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('log', () => {
|
|
38
|
+
it('should call localInstance.log with message', async () => {
|
|
39
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
40
|
+
const logger = await container.get(LoggerInstance)
|
|
41
|
+
|
|
42
|
+
logger.log('Test message')
|
|
43
|
+
|
|
44
|
+
expect(mockLoggerOutput.log).toHaveBeenCalledWith('Test message')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('error', () => {
|
|
49
|
+
it('should call localInstance.error with message', async () => {
|
|
50
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
51
|
+
const logger = await container.get(LoggerInstance)
|
|
52
|
+
|
|
53
|
+
logger.error('Error message')
|
|
54
|
+
|
|
55
|
+
expect(mockLoggerOutput.error).toHaveBeenCalledWith('Error message')
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('warn', () => {
|
|
60
|
+
it('should call localInstance.warn with message', async () => {
|
|
61
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
62
|
+
const logger = await container.get(LoggerInstance)
|
|
63
|
+
|
|
64
|
+
logger.warn('Warning message')
|
|
65
|
+
|
|
66
|
+
expect(mockLoggerOutput.warn).toHaveBeenCalledWith('Warning message')
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('debug', () => {
|
|
71
|
+
it('should call localInstance.debug with message', async () => {
|
|
72
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
73
|
+
const logger = await container.get(LoggerInstance)
|
|
74
|
+
|
|
75
|
+
logger.debug('Debug message')
|
|
76
|
+
|
|
77
|
+
expect(mockLoggerOutput.debug).toHaveBeenCalledWith('Debug message')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should handle missing debug method gracefully', async () => {
|
|
81
|
+
const loggerWithoutDebug = {
|
|
82
|
+
log: vi.fn(),
|
|
83
|
+
error: vi.fn(),
|
|
84
|
+
warn: vi.fn(),
|
|
85
|
+
// No debug method
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
container.addInstance(LoggerOutput, loggerWithoutDebug as LoggerService)
|
|
89
|
+
const logger = await container.get(LoggerInstance)
|
|
90
|
+
|
|
91
|
+
// Should not throw
|
|
92
|
+
expect(() => logger.debug('Debug message')).not.toThrow()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('verbose', () => {
|
|
97
|
+
it('should call localInstance.verbose with message', async () => {
|
|
98
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
99
|
+
const logger = await container.get(LoggerInstance)
|
|
100
|
+
|
|
101
|
+
logger.verbose('Verbose message')
|
|
102
|
+
|
|
103
|
+
expect(mockLoggerOutput.verbose).toHaveBeenCalledWith('Verbose message')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should handle missing verbose method gracefully', async () => {
|
|
107
|
+
const loggerWithoutVerbose = {
|
|
108
|
+
log: vi.fn(),
|
|
109
|
+
error: vi.fn(),
|
|
110
|
+
warn: vi.fn(),
|
|
111
|
+
// No verbose method
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
container.addInstance(LoggerOutput, loggerWithoutVerbose as LoggerService)
|
|
115
|
+
const logger = await container.get(LoggerInstance)
|
|
116
|
+
|
|
117
|
+
// Should not throw
|
|
118
|
+
expect(() => logger.verbose('Verbose message')).not.toThrow()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('fatal', () => {
|
|
123
|
+
it('should call localInstance.fatal with message', async () => {
|
|
124
|
+
container.addInstance(LoggerOutput, mockLoggerOutput as LoggerService)
|
|
125
|
+
const logger = await container.get(LoggerInstance)
|
|
126
|
+
|
|
127
|
+
logger.fatal('Fatal message')
|
|
128
|
+
|
|
129
|
+
expect(mockLoggerOutput.fatal).toHaveBeenCalledWith('Fatal message')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should handle missing fatal method gracefully', async () => {
|
|
133
|
+
const loggerWithoutFatal = {
|
|
134
|
+
log: vi.fn(),
|
|
135
|
+
error: vi.fn(),
|
|
136
|
+
warn: vi.fn(),
|
|
137
|
+
// No fatal method
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
container.addInstance(LoggerOutput, loggerWithoutFatal as LoggerService)
|
|
141
|
+
const logger = await container.get(LoggerInstance)
|
|
142
|
+
|
|
143
|
+
// Should not throw
|
|
144
|
+
expect(() => logger.fatal('Fatal message')).not.toThrow()
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
})
|
|
@@ -9,10 +9,11 @@ import type { ErrorResponse } from '../responders/interfaces/error-response.inte
|
|
|
9
9
|
import { NotFoundException } from '../exceptions/not-found.exception.mjs'
|
|
10
10
|
import { FrameworkError } from '../responders/enums/framework-error.enum.mjs'
|
|
11
11
|
import { ErrorResponseProducerService } from '../responders/services/error-response-producer.service.mjs'
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
12
|
+
// Import services for side-effects (registers @Injectable decorators)
|
|
13
|
+
import '../responders/services/forbidden-responder.service.mjs'
|
|
14
|
+
import '../responders/services/internal-server-error-responder.service.mjs'
|
|
15
|
+
import '../responders/services/not-found-responder.service.mjs'
|
|
16
|
+
import '../responders/services/validation-error-responder.service.mjs'
|
|
16
17
|
import {
|
|
17
18
|
ForbiddenResponderToken,
|
|
18
19
|
InternalServerErrorResponderToken,
|
|
@@ -309,7 +310,7 @@ describe('Responders', () => {
|
|
|
309
310
|
token: NotFoundResponderToken,
|
|
310
311
|
priority: 0, // Higher than default -10
|
|
311
312
|
})
|
|
312
|
-
class
|
|
313
|
+
class _CustomNotFoundResponder implements ErrorResponder {
|
|
313
314
|
getResponse(_error: unknown, description?: string): ErrorResponse {
|
|
314
315
|
return {
|
|
315
316
|
statusCode: 404,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FactoryContext } from '@navios/di'
|
|
2
|
+
|
|
3
|
+
import { Factory, inject, InjectionToken } from '@navios/di'
|
|
4
|
+
|
|
5
|
+
import { NaviosEnvironment } from '../navios.environment.mjs'
|
|
6
|
+
import { AdapterToken } from '../tokens/index.mjs'
|
|
7
|
+
|
|
8
|
+
@Factory({
|
|
9
|
+
token: AdapterToken,
|
|
10
|
+
})
|
|
11
|
+
export class AdapterFactory {
|
|
12
|
+
private readonly environment = inject(NaviosEnvironment)
|
|
13
|
+
create(ctx: FactoryContext) {
|
|
14
|
+
const service = this.environment.getToken(AdapterToken)
|
|
15
|
+
if (!service) {
|
|
16
|
+
throw new Error('AdapterToken service not found in environment')
|
|
17
|
+
}
|
|
18
|
+
return ctx.inject(service as InjectionToken<any, undefined>)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -12,7 +12,7 @@ export class EndpointAdapterFactory {
|
|
|
12
12
|
private readonly environment = inject(NaviosEnvironment)
|
|
13
13
|
|
|
14
14
|
create(ctx: FactoryContext) {
|
|
15
|
-
const service = this.environment.
|
|
15
|
+
const service = this.environment.getToken(EndpointAdapterToken)
|
|
16
16
|
if (!service) {
|
|
17
17
|
throw new Error('EndpointAdapterToken service not found in environment')
|
|
18
18
|
}
|
|
@@ -11,7 +11,7 @@ import { HttpAdapterToken } from '../tokens/index.mjs'
|
|
|
11
11
|
export class HttpAdapterFactory {
|
|
12
12
|
private readonly environment = inject(NaviosEnvironment)
|
|
13
13
|
create(ctx: FactoryContext) {
|
|
14
|
-
const service = this.environment.
|
|
14
|
+
const service = this.environment.getToken(HttpAdapterToken)
|
|
15
15
|
if (!service) {
|
|
16
16
|
throw new Error('HttpAdapterToken service not found in environment')
|
|
17
17
|
}
|
package/src/factories/index.mts
CHANGED
|
@@ -12,7 +12,7 @@ export class MultipartAdapterFactory {
|
|
|
12
12
|
private readonly environment = inject(NaviosEnvironment)
|
|
13
13
|
|
|
14
14
|
create(ctx: FactoryContext) {
|
|
15
|
-
const service = this.environment.
|
|
15
|
+
const service = this.environment.getToken(MultipartAdapterToken)
|
|
16
16
|
if (!service) {
|
|
17
17
|
throw new Error('MultipartAdapterToken service not found in environment')
|
|
18
18
|
}
|
|
@@ -12,7 +12,7 @@ import { Reply } from '../tokens/index.mjs'
|
|
|
12
12
|
export class ReplyFactory {
|
|
13
13
|
private readonly environment = inject(NaviosEnvironment)
|
|
14
14
|
create(ctx: FactoryContext) {
|
|
15
|
-
const service = this.environment.
|
|
15
|
+
const service = this.environment.getToken(Reply)
|
|
16
16
|
if (!service) {
|
|
17
17
|
throw new Error('ReplyToken service not found in environment')
|
|
18
18
|
}
|
|
@@ -12,7 +12,7 @@ import { Request } from '../tokens/index.mjs'
|
|
|
12
12
|
export class RequestFactory {
|
|
13
13
|
private readonly environment = inject(NaviosEnvironment)
|
|
14
14
|
create(ctx: FactoryContext) {
|
|
15
|
-
const service = this.environment.
|
|
15
|
+
const service = this.environment.getToken(Request)
|
|
16
16
|
if (!service) {
|
|
17
17
|
throw new Error('RequestToken service not found in environment')
|
|
18
18
|
}
|
|
@@ -11,7 +11,7 @@ import { StreamAdapterToken } from '../tokens/index.mjs'
|
|
|
11
11
|
export class StreamAdapterFactory {
|
|
12
12
|
private readonly environment = inject(NaviosEnvironment)
|
|
13
13
|
create(ctx: FactoryContext) {
|
|
14
|
-
const service = this.environment.
|
|
14
|
+
const service = this.environment.getToken(StreamAdapterToken)
|
|
15
15
|
if (!service) {
|
|
16
16
|
throw new Error('StreamAdapterToken service not found in environment')
|
|
17
17
|
}
|
|
@@ -11,7 +11,7 @@ import { XmlStreamAdapterToken } from '../tokens/index.mjs'
|
|
|
11
11
|
export class XmlStreamAdapterFactory {
|
|
12
12
|
private readonly environment = inject(NaviosEnvironment)
|
|
13
13
|
create(ctx: FactoryContext) {
|
|
14
|
-
const service = this.environment.
|
|
14
|
+
const service = this.environment.getToken(XmlStreamAdapterToken)
|
|
15
15
|
if (!service) {
|
|
16
16
|
throw new Error('XmlStreamAdapterToken service not found in environment')
|
|
17
17
|
}
|
package/src/index.mts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './responders/index.mjs'
|
|
|
9
9
|
export * from './services/index.mjs'
|
|
10
10
|
export * from './stores/index.mjs'
|
|
11
11
|
export * from './tokens/index.mjs'
|
|
12
|
+
export * from './utils/index.mjs'
|
|
12
13
|
export * from './attribute.factory.mjs'
|
|
13
14
|
export * from './factories/index.mjs'
|
|
14
15
|
export * from './navios.application.mjs'
|