@navios/core 0.7.0 → 0.8.0
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 +53 -0
- package/lib/{index-BFwNx9WQ.d.cts → index-BDNl7j1G.d.cts} +666 -292
- package/lib/index-BDNl7j1G.d.cts.map +1 -0
- package/lib/{index-D657ijFO.d.mts → index-BoP0cWT6.d.mts} +666 -292
- package/lib/index-BoP0cWT6.d.mts.map +1 -0
- package/lib/index.cjs +15 -6
- package/lib/index.d.cts +2 -2
- package/lib/index.d.mts +2 -2
- package/lib/index.mjs +3 -3
- package/lib/legacy-compat/index.cjs +1 -1
- package/lib/legacy-compat/index.cjs.map +1 -1
- package/lib/legacy-compat/index.d.cts +1 -1
- package/lib/legacy-compat/index.d.cts.map +1 -1
- package/lib/legacy-compat/index.d.mts +1 -1
- package/lib/legacy-compat/index.d.mts.map +1 -1
- package/lib/legacy-compat/index.mjs +1 -1
- package/lib/legacy-compat/index.mjs.map +1 -1
- package/lib/{src-Cb1aTjl0.cjs → src-B6eISODM.cjs} +622 -114
- package/lib/src-B6eISODM.cjs.map +1 -0
- package/lib/{src-DzPY5s6d.mjs → src-gBAChVRL.mjs} +563 -73
- package/lib/src-gBAChVRL.mjs.map +1 -0
- package/lib/testing/index.cjs +6 -6
- 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/{use-guards.decorator-DdvUhB03.cjs → use-guards.decorator-COR-9mZY.cjs} +34 -108
- package/lib/use-guards.decorator-COR-9mZY.cjs.map +1 -0
- package/lib/{use-guards.decorator-kZ3lNK8v.mjs → use-guards.decorator-CUww54Nt.mjs} +14 -94
- package/lib/use-guards.decorator-CUww54Nt.mjs.map +1 -0
- package/package.json +12 -12
- package/src/__tests__/controller-resolver.spec.mts +223 -0
- package/src/__tests__/controller.spec.mts +1 -1
- package/src/decorators/controller.decorator.mts +11 -6
- package/src/decorators/endpoint.decorator.mts +60 -12
- package/src/decorators/multipart.decorator.mts +67 -24
- package/src/decorators/stream.decorator.mts +65 -24
- package/src/interfaces/abstract-http-adapter.interface.mts +52 -0
- package/src/interfaces/abstract-http-handler-adapter.interface.mts +31 -1
- package/src/interfaces/index.mts +1 -0
- package/src/interfaces/plugin.interface.mts +105 -0
- package/src/legacy-compat/decorators/endpoint.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/logger/logger.service.mts +3 -3
- package/src/navios.application.mts +91 -27
- package/src/navios.factory.mts +29 -9
- package/src/services/guard-runner.service.mts +46 -9
- package/src/services/index.mts +1 -0
- package/src/services/instance-resolver.service.mts +186 -0
- package/src/services/module-loader.service.mts +102 -1
- package/src/stores/request-id.store.mts +45 -3
- package/src/tokens/index.mts +1 -0
- package/src/tokens/navios-options.token.mts +6 -0
- package/lib/index-BFwNx9WQ.d.cts.map +0 -1
- package/lib/index-D657ijFO.d.mts.map +0 -1
- package/lib/src-Cb1aTjl0.cjs.map +0 -1
- package/lib/src-DzPY5s6d.mjs.map +0 -1
- package/lib/use-guards.decorator-DdvUhB03.cjs.map +0 -1
- package/lib/use-guards.decorator-kZ3lNK8v.mjs.map +0 -1
- package/src/attribute.factory.d.mts +0 -175
- package/src/attribute.factory.d.mts.map +0 -1
- package/src/config/config-service.interface.d.mts +0 -39
- package/src/config/config-service.interface.d.mts.map +0 -1
- package/src/config/config.provider.d.mts +0 -46
- package/src/config/config.provider.d.mts.map +0 -1
- package/src/config/config.service.d.mts +0 -108
- package/src/config/config.service.d.mts.map +0 -1
- package/src/config/index.d.mts +0 -6
- package/src/config/index.d.mts.map +0 -1
- package/src/config/types.d.mts +0 -13
- package/src/config/types.d.mts.map +0 -1
- package/src/config/utils/helpers.d.mts +0 -3
- package/src/config/utils/helpers.d.mts.map +0 -1
- package/src/config/utils/index.d.mts +0 -2
- package/src/config/utils/index.d.mts.map +0 -1
- package/src/decorators/controller.decorator.d.mts +0 -33
- package/src/decorators/controller.decorator.d.mts.map +0 -1
- package/src/decorators/endpoint.decorator.d.mts +0 -89
- package/src/decorators/endpoint.decorator.d.mts.map +0 -1
- package/src/decorators/header.decorator.d.mts +0 -22
- package/src/decorators/header.decorator.d.mts.map +0 -1
- package/src/decorators/http-code.decorator.d.mts +0 -22
- package/src/decorators/http-code.decorator.d.mts.map +0 -1
- package/src/decorators/index.d.mts +0 -9
- package/src/decorators/index.d.mts.map +0 -1
- package/src/decorators/module.decorator.d.mts +0 -42
- package/src/decorators/module.decorator.d.mts.map +0 -1
- package/src/decorators/multipart.decorator.d.mts +0 -54
- package/src/decorators/multipart.decorator.d.mts.map +0 -1
- package/src/decorators/stream.decorator.d.mts +0 -42
- package/src/decorators/stream.decorator.d.mts.map +0 -1
- package/src/decorators/use-guards.decorator.d.mts +0 -33
- package/src/decorators/use-guards.decorator.d.mts.map +0 -1
- package/src/exceptions/bad-request.exception.d.mts +0 -26
- package/src/exceptions/bad-request.exception.d.mts.map +0 -1
- package/src/exceptions/conflict.exception.d.mts +0 -29
- package/src/exceptions/conflict.exception.d.mts.map +0 -1
- package/src/exceptions/forbidden.exception.d.mts +0 -28
- package/src/exceptions/forbidden.exception.d.mts.map +0 -1
- package/src/exceptions/http.exception.d.mts +0 -33
- package/src/exceptions/http.exception.d.mts.map +0 -1
- package/src/exceptions/index.d.mts +0 -8
- package/src/exceptions/index.d.mts.map +0 -1
- package/src/exceptions/internal-server-error.exception.d.mts +0 -31
- package/src/exceptions/internal-server-error.exception.d.mts.map +0 -1
- package/src/exceptions/not-found.exception.d.mts +0 -30
- package/src/exceptions/not-found.exception.d.mts.map +0 -1
- package/src/exceptions/unauthorized.exception.d.mts +0 -28
- package/src/exceptions/unauthorized.exception.d.mts.map +0 -1
- package/src/factories/endpoint-adapter.factory.d.mts +0 -6
- package/src/factories/endpoint-adapter.factory.d.mts.map +0 -1
- package/src/factories/http-adapter.factory.d.mts +0 -6
- package/src/factories/http-adapter.factory.d.mts.map +0 -1
- package/src/factories/index.d.mts +0 -8
- package/src/factories/index.d.mts.map +0 -1
- package/src/factories/multipart-adapter.factory.d.mts +0 -6
- package/src/factories/multipart-adapter.factory.d.mts.map +0 -1
- package/src/factories/reply.factory.d.mts +0 -6
- package/src/factories/reply.factory.d.mts.map +0 -1
- package/src/factories/request.factory.d.mts +0 -6
- package/src/factories/request.factory.d.mts.map +0 -1
- package/src/factories/stream-adapter.factory.d.mts +0 -6
- package/src/factories/stream-adapter.factory.d.mts.map +0 -1
- package/src/factories/xml-stream-adapter.factory.d.mts +0 -6
- package/src/factories/xml-stream-adapter.factory.d.mts.map +0 -1
- package/src/index.d.mts +0 -15
- package/src/index.d.mts.map +0 -1
- package/src/interfaces/abstract-execution-context.inteface.d.mts +0 -44
- package/src/interfaces/abstract-execution-context.inteface.d.mts.map +0 -1
- package/src/interfaces/abstract-http-adapter.interface.d.mts +0 -15
- package/src/interfaces/abstract-http-adapter.interface.d.mts.map +0 -1
- package/src/interfaces/abstract-http-cors-options.interface.d.mts +0 -58
- package/src/interfaces/abstract-http-cors-options.interface.d.mts.map +0 -1
- package/src/interfaces/abstract-http-handler-adapter.interface.d.mts +0 -7
- package/src/interfaces/abstract-http-handler-adapter.interface.d.mts.map +0 -1
- package/src/interfaces/abstract-http-listen-options.interface.d.mts +0 -5
- package/src/interfaces/abstract-http-listen-options.interface.d.mts.map +0 -1
- package/src/interfaces/can-activate.d.mts +0 -36
- package/src/interfaces/can-activate.d.mts.map +0 -1
- package/src/interfaces/http-header.d.mts +0 -10
- package/src/interfaces/http-header.d.mts.map +0 -1
- package/src/interfaces/index.d.mts +0 -9
- package/src/interfaces/index.d.mts.map +0 -1
- package/src/interfaces/navios-module.d.mts +0 -29
- package/src/interfaces/navios-module.d.mts.map +0 -1
- package/src/legacy-compat/context-compat.d.mts +0 -19
- package/src/legacy-compat/context-compat.d.mts.map +0 -1
- package/src/legacy-compat/decorators/controller.decorator.d.mts +0 -21
- package/src/legacy-compat/decorators/controller.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/endpoint.decorator.d.mts +0 -33
- package/src/legacy-compat/decorators/endpoint.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/header.decorator.d.mts +0 -24
- package/src/legacy-compat/decorators/header.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/http-code.decorator.d.mts +0 -22
- package/src/legacy-compat/decorators/http-code.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/index.d.mts +0 -9
- package/src/legacy-compat/decorators/index.d.mts.map +0 -1
- package/src/legacy-compat/decorators/module.decorator.d.mts +0 -22
- package/src/legacy-compat/decorators/module.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/multipart.decorator.d.mts +0 -34
- package/src/legacy-compat/decorators/multipart.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/stream.decorator.d.mts +0 -34
- package/src/legacy-compat/decorators/stream.decorator.d.mts.map +0 -1
- package/src/legacy-compat/decorators/use-guards.decorator.d.mts +0 -29
- package/src/legacy-compat/decorators/use-guards.decorator.d.mts.map +0 -1
- package/src/legacy-compat/index.d.mts +0 -19
- package/src/legacy-compat/index.d.mts.map +0 -1
- package/src/logger/console-logger.service.d.mts +0 -196
- package/src/logger/console-logger.service.d.mts.map +0 -1
- package/src/logger/index.d.mts +0 -7
- package/src/logger/index.d.mts.map +0 -1
- package/src/logger/log-levels.d.mts +0 -14
- package/src/logger/log-levels.d.mts.map +0 -1
- package/src/logger/logger-service.interface.d.mts +0 -36
- package/src/logger/logger-service.interface.d.mts.map +0 -1
- package/src/logger/logger.service.d.mts +0 -57
- package/src/logger/logger.service.d.mts.map +0 -1
- package/src/logger/logger.tokens.d.mts +0 -36
- package/src/logger/logger.tokens.d.mts.map +0 -1
- package/src/logger/utils/cli-colors.util.d.mts +0 -10
- package/src/logger/utils/cli-colors.util.d.mts.map +0 -1
- package/src/logger/utils/filter-log-levelts.util.d.mts +0 -6
- package/src/logger/utils/filter-log-levelts.util.d.mts.map +0 -1
- package/src/logger/utils/index.d.mts +0 -6
- package/src/logger/utils/index.d.mts.map +0 -1
- package/src/logger/utils/is-log-level-enabled.d.mts +0 -8
- package/src/logger/utils/is-log-level-enabled.d.mts.map +0 -1
- package/src/logger/utils/is-log-level.util.d.mts +0 -6
- package/src/logger/utils/is-log-level.util.d.mts.map +0 -1
- package/src/logger/utils/shared.utils.d.mts +0 -14
- package/src/logger/utils/shared.utils.d.mts.map +0 -1
- package/src/metadata/controller.metadata.d.mts +0 -13
- package/src/metadata/controller.metadata.d.mts.map +0 -1
- package/src/metadata/handler.metadata.d.mts +0 -18
- package/src/metadata/handler.metadata.d.mts.map +0 -1
- package/src/metadata/index.d.mts +0 -4
- package/src/metadata/index.d.mts.map +0 -1
- package/src/metadata/module.metadata.d.mts +0 -13
- package/src/metadata/module.metadata.d.mts.map +0 -1
- package/src/navios.application.d.mts +0 -210
- package/src/navios.application.d.mts.map +0 -1
- package/src/navios.environment.d.mts +0 -11
- package/src/navios.environment.d.mts.map +0 -1
- package/src/navios.factory.d.mts +0 -68
- package/src/navios.factory.d.mts.map +0 -1
- package/src/services/guard-runner.service.d.mts +0 -10
- package/src/services/guard-runner.service.d.mts.map +0 -1
- package/src/services/index.d.mts +0 -3
- package/src/services/index.d.mts.map +0 -1
- package/src/services/module-loader.service.d.mts +0 -17
- package/src/services/module-loader.service.d.mts.map +0 -1
- package/src/stores/index.d.mts +0 -2
- package/src/stores/index.d.mts.map +0 -1
- package/src/stores/request-id.store.d.mts +0 -37
- package/src/stores/request-id.store.d.mts.map +0 -1
- package/src/tokens/endpoint-adapter.token.d.mts +0 -4
- package/src/tokens/endpoint-adapter.token.d.mts.map +0 -1
- package/src/tokens/execution-context.token.d.mts +0 -5
- package/src/tokens/execution-context.token.d.mts.map +0 -1
- package/src/tokens/http-adapter.token.d.mts +0 -4
- package/src/tokens/http-adapter.token.d.mts.map +0 -1
- package/src/tokens/index.d.mts +0 -9
- package/src/tokens/index.d.mts.map +0 -1
- package/src/tokens/multipart-adapter.token.d.mts +0 -4
- package/src/tokens/multipart-adapter.token.d.mts.map +0 -1
- package/src/tokens/reply.token.d.mts +0 -3
- package/src/tokens/reply.token.d.mts.map +0 -1
- package/src/tokens/request.token.d.mts +0 -3
- package/src/tokens/request.token.d.mts.map +0 -1
- package/src/tokens/stream-adapter.token.d.mts +0 -4
- package/src/tokens/stream-adapter.token.d.mts.map +0 -1
- package/src/tokens/xml-stream-adapter.token.d.mts +0 -4
- package/src/tokens/xml-stream-adapter.token.d.mts.map +0 -1
package/src/navios.factory.mts
CHANGED
|
@@ -15,24 +15,26 @@ import type {
|
|
|
15
15
|
import { ConsoleLogger, isNil, LoggerOutput } from './logger/index.mjs'
|
|
16
16
|
import { NaviosApplication } from './navios.application.mjs'
|
|
17
17
|
import { NaviosEnvironment } from './navios.environment.mjs'
|
|
18
|
+
import { setRequestIdEnabled } from './stores/index.mjs'
|
|
19
|
+
import { NaviosOptionsToken } from './tokens/index.mjs'
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Factory class for creating and configuring Navios applications.
|
|
21
|
-
*
|
|
23
|
+
*
|
|
22
24
|
* This is the main entry point for bootstrapping a Navios application.
|
|
23
25
|
* It handles dependency injection container setup, adapter registration,
|
|
24
26
|
* and logger configuration.
|
|
25
|
-
*
|
|
27
|
+
*
|
|
26
28
|
* @example
|
|
27
29
|
* ```typescript
|
|
28
30
|
* import { NaviosFactory } from '@navios/core'
|
|
29
31
|
* import { defineFastifyEnvironment } from '@navios/adapter-fastify'
|
|
30
|
-
*
|
|
32
|
+
*
|
|
31
33
|
* const app = await NaviosFactory.create(AppModule, {
|
|
32
34
|
* adapter: defineFastifyEnvironment(),
|
|
33
35
|
* logger: ['log', 'error', 'warn'],
|
|
34
36
|
* })
|
|
35
|
-
*
|
|
37
|
+
*
|
|
36
38
|
* await app.init()
|
|
37
39
|
* await app.listen({ port: 3000 })
|
|
38
40
|
* ```
|
|
@@ -40,10 +42,10 @@ import { NaviosEnvironment } from './navios.environment.mjs'
|
|
|
40
42
|
export class NaviosFactory {
|
|
41
43
|
/**
|
|
42
44
|
* Creates a new Navios application instance.
|
|
43
|
-
*
|
|
45
|
+
*
|
|
44
46
|
* This method sets up the dependency injection container, registers the HTTP adapter,
|
|
45
47
|
* configures logging, and initializes the application with the provided module.
|
|
46
|
-
*
|
|
48
|
+
*
|
|
47
49
|
* @param appModule - The root application module class decorated with @Module()
|
|
48
50
|
* @param options - Configuration options for the application
|
|
49
51
|
* @param options.adapter - HTTP adapter environment (required for HTTP server functionality)
|
|
@@ -53,20 +55,20 @@ export class NaviosFactory {
|
|
|
53
55
|
* - `false` to disable logging
|
|
54
56
|
* @param options.container - Optional custom dependency injection container (useful for testing)
|
|
55
57
|
* @returns A configured NaviosApplication instance ready to be initialized
|
|
56
|
-
*
|
|
58
|
+
*
|
|
57
59
|
* @example
|
|
58
60
|
* ```typescript
|
|
59
61
|
* // Basic setup with Fastify adapter
|
|
60
62
|
* const app = await NaviosFactory.create(AppModule, {
|
|
61
63
|
* adapter: defineFastifyEnvironment(),
|
|
62
64
|
* })
|
|
63
|
-
*
|
|
65
|
+
*
|
|
64
66
|
* // With custom logger configuration
|
|
65
67
|
* const app = await NaviosFactory.create(AppModule, {
|
|
66
68
|
* adapter: defineFastifyEnvironment(),
|
|
67
69
|
* logger: ['error', 'warn', 'log'],
|
|
68
70
|
* })
|
|
69
|
-
*
|
|
71
|
+
*
|
|
70
72
|
* // With custom container for testing
|
|
71
73
|
* const container = new Container()
|
|
72
74
|
* const app = await NaviosFactory.create(AppModule, {
|
|
@@ -82,6 +84,23 @@ export class NaviosFactory {
|
|
|
82
84
|
},
|
|
83
85
|
) {
|
|
84
86
|
const container = options.container ?? new Container()
|
|
87
|
+
|
|
88
|
+
// Set request ID flag early, before any adapters are registered
|
|
89
|
+
if (options.enableRequestId === true) {
|
|
90
|
+
setRequestIdEnabled(true)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Store options in container for DI access by adapters
|
|
94
|
+
container
|
|
95
|
+
.getServiceLocator()
|
|
96
|
+
.getManager()
|
|
97
|
+
.storeCreatedHolder(
|
|
98
|
+
NaviosOptionsToken.toString(),
|
|
99
|
+
options,
|
|
100
|
+
InjectableType.Class,
|
|
101
|
+
InjectableScope.Singleton,
|
|
102
|
+
)
|
|
103
|
+
|
|
85
104
|
await this.registerLoggerConfiguration(container, options)
|
|
86
105
|
const adapters = Array.isArray(options.adapter)
|
|
87
106
|
? options.adapter
|
|
@@ -119,6 +138,7 @@ export class NaviosFactory {
|
|
|
119
138
|
loggerInstance?.setup({
|
|
120
139
|
logLevels: logger,
|
|
121
140
|
})
|
|
141
|
+
return
|
|
122
142
|
}
|
|
123
143
|
if ((logger as boolean) !== true && !isNil(logger)) {
|
|
124
144
|
container
|
|
@@ -20,6 +20,11 @@ export class GuardRunnerService {
|
|
|
20
20
|
private readonly logger = inject(Logger, {
|
|
21
21
|
context: GuardRunnerService.name,
|
|
22
22
|
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs guards that need to be resolved from a scoped container.
|
|
26
|
+
* Use this when guards have request-scoped dependencies.
|
|
27
|
+
*/
|
|
23
28
|
async runGuards(
|
|
24
29
|
allGuards: Set<
|
|
25
30
|
| ClassTypeWithInstance<CanActivate>
|
|
@@ -28,16 +33,48 @@ export class GuardRunnerService {
|
|
|
28
33
|
executionContext: AbstractExecutionContext,
|
|
29
34
|
context: ScopedContainer,
|
|
30
35
|
) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
// Reverse order: module guards run first, then controller, then endpoint
|
|
37
|
+
const guardsArray = Array.from(allGuards).reverse()
|
|
38
|
+
|
|
39
|
+
// Resolve all guards in parallel
|
|
40
|
+
const guardInstances = await Promise.all(
|
|
41
|
+
guardsArray.map(async (guard) => {
|
|
42
|
+
const guardInstance = await context.get(
|
|
43
|
+
guard as InjectionToken<CanActivate, undefined>,
|
|
39
44
|
)
|
|
40
|
-
|
|
45
|
+
if (!guardInstance.canActivate) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`[Navios] Guard ${guard.name as string} does not implement canActivate()`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
return guardInstance
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return this.executeGuards(guardInstances, executionContext)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Runs pre-resolved guard instances.
|
|
59
|
+
* Use this when all guards are singletons and have been pre-resolved at startup.
|
|
60
|
+
*/
|
|
61
|
+
async runGuardsStatic(
|
|
62
|
+
guardInstances: CanActivate[],
|
|
63
|
+
executionContext: AbstractExecutionContext,
|
|
64
|
+
) {
|
|
65
|
+
return this.executeGuards(guardInstances, executionContext)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Shared guard execution logic.
|
|
70
|
+
* Iterates through guard instances and calls canActivate on each.
|
|
71
|
+
*/
|
|
72
|
+
private async executeGuards(
|
|
73
|
+
guardInstances: CanActivate[],
|
|
74
|
+
executionContext: AbstractExecutionContext,
|
|
75
|
+
): Promise<boolean> {
|
|
76
|
+
let canActivate = true
|
|
77
|
+
for (const guardInstance of guardInstances) {
|
|
41
78
|
try {
|
|
42
79
|
canActivate = await guardInstance.canActivate(executionContext)
|
|
43
80
|
if (!canActivate) {
|
package/src/services/index.mts
CHANGED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ClassType, ScopedContainer } from '@navios/di'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Container,
|
|
5
|
+
getInjectableToken,
|
|
6
|
+
inject,
|
|
7
|
+
Injectable,
|
|
8
|
+
InjectableScope,
|
|
9
|
+
} from '@navios/di'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Result of instance resolution attempt.
|
|
13
|
+
* Contains either a cached singleton instance or a resolver function
|
|
14
|
+
* that can be used to get a fresh instance per request.
|
|
15
|
+
*/
|
|
16
|
+
export interface InstanceResolution<T = any> {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the instance was successfully cached as a singleton.
|
|
19
|
+
* If true, `instance` contains the cached instance.
|
|
20
|
+
* If false, the class has request-scoped dependencies and
|
|
21
|
+
* must be resolved per-request using `resolve()`.
|
|
22
|
+
*/
|
|
23
|
+
cached: boolean
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The cached instance (only available if `cached` is true).
|
|
27
|
+
*/
|
|
28
|
+
instance: T | null
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the instance from a scoped container.
|
|
32
|
+
* Use this when `cached` is false to get a fresh instance per request.
|
|
33
|
+
*/
|
|
34
|
+
resolve: (scoped: ScopedContainer) => Promise<T>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Result of resolving multiple instances.
|
|
39
|
+
* Contains either all cached singleton instances or a resolver function.
|
|
40
|
+
*/
|
|
41
|
+
export interface MultiInstanceResolution<T = any> {
|
|
42
|
+
/**
|
|
43
|
+
* Whether ALL instances were successfully cached as singletons.
|
|
44
|
+
* If true, `instances` contains all cached instances.
|
|
45
|
+
* If false, at least one class has request-scoped dependencies.
|
|
46
|
+
*/
|
|
47
|
+
cached: boolean
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The cached instances (only available if `cached` is true).
|
|
51
|
+
* Order matches the input array order.
|
|
52
|
+
*/
|
|
53
|
+
instances: T[] | null
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The original class types for dynamic resolution.
|
|
57
|
+
*/
|
|
58
|
+
classTypes: ClassType[]
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolves all instances from a scoped container.
|
|
62
|
+
* Use this when `cached` is false to get fresh instances per request.
|
|
63
|
+
*/
|
|
64
|
+
resolve: (scoped: ScopedContainer) => Promise<T[]>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Service responsible for resolving class instances with automatic scope detection.
|
|
69
|
+
*
|
|
70
|
+
* This service attempts to resolve classes as singletons from the root container.
|
|
71
|
+
* If resolution fails (because the class has request-scoped dependencies),
|
|
72
|
+
* it automatically updates the class's scope to Request and provides a
|
|
73
|
+
* resolver function for per-request instantiation.
|
|
74
|
+
*
|
|
75
|
+
* This enables optimal performance:
|
|
76
|
+
* - Classes without request-scoped deps stay as singletons (faster)
|
|
77
|
+
* - Classes with request-scoped deps are automatically promoted to request scope
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* const resolution = await instanceResolver.resolve(UserController)
|
|
82
|
+
*
|
|
83
|
+
* if (resolution.cached) {
|
|
84
|
+
* // Use cached singleton
|
|
85
|
+
* return resolution.instance.handleRequest(req)
|
|
86
|
+
* } else {
|
|
87
|
+
* // Resolve per request
|
|
88
|
+
* const controller = await resolution.resolve(scopedContainer)
|
|
89
|
+
* return controller.handleRequest(req)
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
@Injectable()
|
|
94
|
+
export class InstanceResolverService {
|
|
95
|
+
private container = inject(Container)
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Attempts to resolve a class instance, automatically detecting if it needs
|
|
99
|
+
* request scope based on its dependencies.
|
|
100
|
+
*
|
|
101
|
+
* @param classType - The class to resolve
|
|
102
|
+
* @returns A resolution result containing either a cached instance or resolver function
|
|
103
|
+
*/
|
|
104
|
+
async resolve<T>(classType: ClassType): Promise<InstanceResolution<T>> {
|
|
105
|
+
let cachedInstance: T | null = null
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
cachedInstance = await this.container.get(classType)
|
|
109
|
+
} catch {
|
|
110
|
+
// Class has request-scoped dependencies, update its scope to Request
|
|
111
|
+
// so it will be resolved per-request from the scoped container
|
|
112
|
+
const token = getInjectableToken(classType)
|
|
113
|
+
this.container
|
|
114
|
+
.getRegistry()
|
|
115
|
+
.updateScope(token, InjectableScope.Request)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
cached: cachedInstance !== null,
|
|
120
|
+
instance: cachedInstance,
|
|
121
|
+
resolve: (scoped: ScopedContainer) =>
|
|
122
|
+
scoped.get(classType) as Promise<T>,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Attempts to resolve multiple class instances, automatically detecting if any need
|
|
128
|
+
* request scope based on their dependencies.
|
|
129
|
+
*
|
|
130
|
+
* Returns `cached: true` only if ALL classes can be resolved as singletons.
|
|
131
|
+
* If any class has request-scoped dependencies, returns `cached: false`.
|
|
132
|
+
*
|
|
133
|
+
* @param classTypes - The classes to resolve
|
|
134
|
+
* @returns A resolution result containing either all cached instances or resolver function
|
|
135
|
+
*/
|
|
136
|
+
async resolveMany<T>(classTypes: ClassType[]): Promise<MultiInstanceResolution<T>> {
|
|
137
|
+
if (classTypes.length === 0) {
|
|
138
|
+
return {
|
|
139
|
+
cached: true,
|
|
140
|
+
instances: [],
|
|
141
|
+
classTypes: [],
|
|
142
|
+
resolve: async () => [],
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Resolve all classes in parallel
|
|
147
|
+
const results = await Promise.all(
|
|
148
|
+
classTypes.map(async (classType) => {
|
|
149
|
+
try {
|
|
150
|
+
const instance = await this.container.get(classType)
|
|
151
|
+
return { success: true, instance: instance as T }
|
|
152
|
+
} catch {
|
|
153
|
+
// Class has request-scoped dependencies, update its scope to Request
|
|
154
|
+
const token = getInjectableToken(classType)
|
|
155
|
+
this.container
|
|
156
|
+
.getRegistry()
|
|
157
|
+
.updateScope(token, InjectableScope.Request)
|
|
158
|
+
return { success: false, instance: null }
|
|
159
|
+
}
|
|
160
|
+
}),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const allCached = results.every((r) => r.success)
|
|
164
|
+
const cachedInstances = allCached
|
|
165
|
+
? results.map((r) => r.instance as T)
|
|
166
|
+
: null
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
cached: allCached,
|
|
170
|
+
instances: cachedInstances,
|
|
171
|
+
classTypes,
|
|
172
|
+
resolve: (scoped: ScopedContainer) =>
|
|
173
|
+
Promise.all(classTypes.map((classType) => scoped.get(classType) as Promise<T>)),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @deprecated Use InstanceResolverService instead
|
|
180
|
+
*/
|
|
181
|
+
export const ControllerResolverService = InstanceResolverService
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @deprecated Use InstanceResolution instead
|
|
185
|
+
*/
|
|
186
|
+
export type ControllerResolution<T = any> = InstanceResolution<T>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClassTypeWithInstance } from '@navios/di'
|
|
1
|
+
import type { ClassType, ClassTypeWithInstance } from '@navios/di'
|
|
2
2
|
|
|
3
3
|
import { Container, inject, Injectable } from '@navios/di'
|
|
4
4
|
|
|
@@ -8,6 +8,30 @@ import type { ModuleMetadata } from '../metadata/index.mjs'
|
|
|
8
8
|
import { Logger } from '../logger/index.mjs'
|
|
9
9
|
import { extractModuleMetadata } from '../metadata/index.mjs'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Extension definition for dynamically adding to the module tree.
|
|
13
|
+
* Used by plugins to inject controllers or entire modules.
|
|
14
|
+
*/
|
|
15
|
+
export interface ModuleExtension {
|
|
16
|
+
/**
|
|
17
|
+
* Module class to add. If provided, the module and all its
|
|
18
|
+
* controllers/imports will be processed.
|
|
19
|
+
*/
|
|
20
|
+
module?: ClassTypeWithInstance<NaviosModule>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Controllers to add directly without a wrapper module.
|
|
24
|
+
* Will be registered under a synthetic module named after the plugin.
|
|
25
|
+
*/
|
|
26
|
+
controllers?: ClassType[]
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Name for the synthetic module when using controllers directly.
|
|
30
|
+
* Required if `controllers` is provided without `module`.
|
|
31
|
+
*/
|
|
32
|
+
moduleName?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
11
35
|
@Injectable()
|
|
12
36
|
export class ModuleLoaderService {
|
|
13
37
|
private logger = inject(Logger, {
|
|
@@ -26,6 +50,83 @@ export class ModuleLoaderService {
|
|
|
26
50
|
this.initialized = true
|
|
27
51
|
}
|
|
28
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Extends the module tree with additional modules or controllers.
|
|
55
|
+
*
|
|
56
|
+
* This method is designed to be called by plugins during registration,
|
|
57
|
+
* which happens after initial module loading but before route registration.
|
|
58
|
+
*
|
|
59
|
+
* @param extensions - Array of module extensions to add
|
|
60
|
+
* @throws Error if not initialized (loadModules must be called first)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // In plugin registration
|
|
65
|
+
* const moduleLoader = await context.container.get(ModuleLoaderService)
|
|
66
|
+
* await moduleLoader.extendModules([{
|
|
67
|
+
* controllers: [OpenApiJsonController, OpenApiYamlController],
|
|
68
|
+
* moduleName: 'OpenApiBunModule',
|
|
69
|
+
* }])
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
async extendModules(extensions: ModuleExtension[]): Promise<void> {
|
|
73
|
+
if (!this.initialized) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
'ModuleLoaderService must be initialized before extending. Call loadModules() first.',
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const extension of extensions) {
|
|
80
|
+
if (extension.module) {
|
|
81
|
+
// Process a full module with its imports and controllers
|
|
82
|
+
await this.traverseModules(extension.module)
|
|
83
|
+
} else if (extension.controllers && extension.moduleName) {
|
|
84
|
+
// Create synthetic module metadata for loose controllers
|
|
85
|
+
await this.registerControllers(
|
|
86
|
+
extension.controllers,
|
|
87
|
+
extension.moduleName,
|
|
88
|
+
)
|
|
89
|
+
} else if (extension.controllers) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'moduleName is required when providing controllers without a module',
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Registers controllers under a synthetic module.
|
|
99
|
+
* Used when plugins want to add controllers without a full module class.
|
|
100
|
+
*/
|
|
101
|
+
private async registerControllers(
|
|
102
|
+
controllers: ClassType[],
|
|
103
|
+
moduleName: string,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
if (this.modulesMetadata.has(moduleName)) {
|
|
106
|
+
// Merge controllers into existing module
|
|
107
|
+
const existing = this.modulesMetadata.get(moduleName)!
|
|
108
|
+
for (const controller of controllers) {
|
|
109
|
+
existing.controllers.add(controller)
|
|
110
|
+
}
|
|
111
|
+
this.logger.debug(
|
|
112
|
+
`Extended module ${moduleName} with ${controllers.length} controllers`,
|
|
113
|
+
)
|
|
114
|
+
} else {
|
|
115
|
+
// Create new synthetic module metadata
|
|
116
|
+
const metadata: ModuleMetadata = {
|
|
117
|
+
controllers: new Set(controllers),
|
|
118
|
+
imports: new Set(),
|
|
119
|
+
guards: new Set(),
|
|
120
|
+
customAttributes: new Map(),
|
|
121
|
+
}
|
|
122
|
+
this.modulesMetadata.set(moduleName, metadata)
|
|
123
|
+
|
|
124
|
+
this.logger.debug(
|
|
125
|
+
`Created module ${moduleName} with ${controllers.length} controllers`,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
29
130
|
private async traverseModules(
|
|
30
131
|
module: ClassTypeWithInstance<NaviosModule>,
|
|
31
132
|
parentMetadata?: ModuleMetadata,
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
2
|
|
|
3
|
+
let requestCounter = 0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates a simple incremental request ID.
|
|
7
|
+
* Much faster than crypto.randomUUID() and sufficient for request tracking.
|
|
8
|
+
*
|
|
9
|
+
* @returns A unique request ID string (e.g., "req-1", "req-2", ...)
|
|
10
|
+
*/
|
|
11
|
+
export function generateRequestId(): string {
|
|
12
|
+
return `req-${++requestCounter}`
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* AsyncLocalStorage store for the current request ID.
|
|
5
17
|
*
|
|
@@ -20,17 +32,44 @@ import { AsyncLocalStorage } from 'node:async_hooks'
|
|
|
20
32
|
* const currentId = getRequestId()
|
|
21
33
|
* ```
|
|
22
34
|
*/
|
|
23
|
-
|
|
35
|
+
let requestIdStore: AsyncLocalStorage<string> | null = null
|
|
36
|
+
|
|
37
|
+
function getRequestIdStore(): AsyncLocalStorage<string> {
|
|
38
|
+
if (!requestIdStore) {
|
|
39
|
+
requestIdStore = new AsyncLocalStorage<string>()
|
|
40
|
+
}
|
|
41
|
+
return requestIdStore!
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whether request ID propagation is enabled.
|
|
45
|
+
* When disabled, runWithRequestId is a pass-through for better performance.
|
|
46
|
+
*/
|
|
47
|
+
let requestIdEnabled = false
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Enables or disables request ID propagation.
|
|
51
|
+
* Called by NaviosFactory based on the enableRequestId option.
|
|
52
|
+
*
|
|
53
|
+
* @param enabled - Whether to enable request ID propagation
|
|
54
|
+
*/
|
|
55
|
+
export function setRequestIdEnabled(enabled: boolean): void {
|
|
56
|
+
requestIdEnabled = enabled
|
|
57
|
+
}
|
|
24
58
|
|
|
25
59
|
/**
|
|
26
60
|
* Runs a function with a request ID in the async local storage context.
|
|
61
|
+
* If request ID propagation is disabled, the function is called directly
|
|
62
|
+
* without AsyncLocalStorage overhead.
|
|
27
63
|
*
|
|
28
64
|
* @param requestId - The request ID to set for this context
|
|
29
65
|
* @param fn - The function to run within this context
|
|
30
66
|
* @returns The return value of the function
|
|
31
67
|
*/
|
|
32
68
|
export function runWithRequestId<R>(requestId: string, fn: () => R): R {
|
|
33
|
-
|
|
69
|
+
if (!requestIdEnabled) {
|
|
70
|
+
return fn()
|
|
71
|
+
}
|
|
72
|
+
return getRequestIdStore().run(requestId, fn)
|
|
34
73
|
}
|
|
35
74
|
|
|
36
75
|
/**
|
|
@@ -39,5 +78,8 @@ export function runWithRequestId<R>(requestId: string, fn: () => R): R {
|
|
|
39
78
|
* @returns The current request ID, or undefined if not in a request context
|
|
40
79
|
*/
|
|
41
80
|
export function getRequestId(): string | undefined {
|
|
42
|
-
|
|
81
|
+
if (!requestIdEnabled) {
|
|
82
|
+
return undefined
|
|
83
|
+
}
|
|
84
|
+
return getRequestIdStore().getStore()
|
|
43
85
|
}
|
package/src/tokens/index.mts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './endpoint-adapter.token.mjs'
|
|
|
2
2
|
export * from './execution-context.token.mjs'
|
|
3
3
|
export * from './http-adapter.token.mjs'
|
|
4
4
|
export * from './multipart-adapter.token.mjs'
|
|
5
|
+
export * from './navios-options.token.mjs'
|
|
5
6
|
export * from './reply.token.mjs'
|
|
6
7
|
export * from './request.token.mjs'
|
|
7
8
|
export * from './stream-adapter.token.mjs'
|