@navios/core 0.1.2 → 0.1.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/dist/index.d.mts +54 -13
- package/dist/index.d.ts +54 -13
- package/dist/index.js +336 -203
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +336 -209
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/config/config.provider.mts +10 -20
- package/src/decorators/endpoint.decorator.mts +7 -2
- package/src/decorators/header.decorator.mts +18 -0
- package/src/decorators/http-code.decorator.mts +18 -0
- package/src/decorators/index.mts +2 -0
- package/src/logger/logger.service.mts +0 -1
- package/src/logger/pino-wrapper.mts +6 -5
- package/src/metadata/endpoint.metadata.mts +13 -0
- package/src/navios.application.mts +36 -1
- package/src/service-locator/__tests__/injectable.spec.mts +2 -1
- package/src/service-locator/__tests__/injection-token.spec.mts +124 -0
- package/src/service-locator/decorators/get-injectable-token.mts +2 -6
- package/src/service-locator/decorators/injectable.decorator.mts +7 -46
- package/src/service-locator/errors/errors.enum.mts +1 -0
- package/src/service-locator/errors/factory-token-not-resolved.mts +10 -0
- package/src/service-locator/errors/index.mts +1 -0
- package/src/service-locator/index.mts +1 -0
- package/src/service-locator/inject.mts +4 -18
- package/src/service-locator/injection-token.mts +57 -6
- package/src/service-locator/resolve-service.mts +46 -0
- package/src/service-locator/service-locator.mts +87 -35
- package/src/service-locator/sync-injector.mts +0 -8
- package/src/services/controller-adapter.service.mts +116 -71
package/package.json
CHANGED
|
@@ -4,8 +4,6 @@ import type { ConfigService } from './config-service.interface.mjs'
|
|
|
4
4
|
|
|
5
5
|
import { Logger } from '../logger/index.mjs'
|
|
6
6
|
import {
|
|
7
|
-
getInjectableToken,
|
|
8
|
-
inject,
|
|
9
7
|
Injectable,
|
|
10
8
|
InjectableType,
|
|
11
9
|
InjectionToken,
|
|
@@ -13,28 +11,27 @@ import {
|
|
|
13
11
|
} from '../service-locator/index.mjs'
|
|
14
12
|
import { ConfigServiceInstance } from './config.service.mjs'
|
|
15
13
|
|
|
16
|
-
export const ConfigProviderInjectionToken = 'ConfigProvider'
|
|
17
|
-
|
|
18
14
|
export const ConfigProviderOptions = z.object({
|
|
19
15
|
load: z.function(),
|
|
20
16
|
})
|
|
17
|
+
|
|
21
18
|
export const ConfigProvider = InjectionToken.create<
|
|
22
19
|
ConfigService,
|
|
23
20
|
typeof ConfigProviderOptions
|
|
24
|
-
>(
|
|
21
|
+
>(ConfigServiceInstance, ConfigProviderOptions)
|
|
25
22
|
|
|
26
23
|
@Injectable({
|
|
27
24
|
token: ConfigProvider,
|
|
28
25
|
type: InjectableType.Factory,
|
|
29
26
|
})
|
|
30
27
|
export class ConfigProviderFactory {
|
|
31
|
-
logger =
|
|
28
|
+
logger = syncInject(Logger, {
|
|
32
29
|
context: 'ConfigService',
|
|
33
30
|
})
|
|
34
31
|
|
|
35
32
|
async create(ctx: any, args: z.infer<typeof ConfigProviderOptions>) {
|
|
36
33
|
const { load } = args
|
|
37
|
-
const logger =
|
|
34
|
+
const logger = this.logger
|
|
38
35
|
try {
|
|
39
36
|
const config = await load()
|
|
40
37
|
|
|
@@ -46,18 +43,11 @@ export class ConfigProviderFactory {
|
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
export function
|
|
46
|
+
export function provideConfig<ConfigMap extends Record<string, unknown>>(
|
|
50
47
|
options: z.input<typeof ConfigProviderOptions>,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
configService = inject(ConfigProvider, options)
|
|
57
|
-
|
|
58
|
-
create() {
|
|
59
|
-
return this.configService
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return getInjectableToken(ConfigServiceImpl)
|
|
48
|
+
) {
|
|
49
|
+
return InjectionToken.bound(ConfigProvider, options) as InjectionToken<
|
|
50
|
+
ConfigServiceInstance<ConfigMap>,
|
|
51
|
+
undefined
|
|
52
|
+
>
|
|
63
53
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BaseEndpointConfig,
|
|
3
|
+
EndpointFunctionArgs,
|
|
4
|
+
HttpMethod,
|
|
5
|
+
} from '@navios/common'
|
|
2
6
|
import type { AnyZodObject, z, ZodType } from 'zod'
|
|
3
7
|
|
|
4
|
-
import { getEndpointMetadata } from '../metadata/index.mjs'
|
|
8
|
+
import { EndpointType, getEndpointMetadata } from '../metadata/index.mjs'
|
|
5
9
|
|
|
6
10
|
export type EndpointParams<
|
|
7
11
|
EndpointDeclaration extends {
|
|
@@ -72,6 +76,7 @@ export function Endpoint<
|
|
|
72
76
|
}
|
|
73
77
|
// @ts-expect-error We don't need to set correctly in the metadata
|
|
74
78
|
endpointMetadata.config = config
|
|
79
|
+
endpointMetadata.type = EndpointType.Config
|
|
75
80
|
endpointMetadata.classMethod = target.name
|
|
76
81
|
endpointMetadata.httpMethod = config.method
|
|
77
82
|
endpointMetadata.url = config.url
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HttpHeader } from 'fastify/types/utils.js'
|
|
2
|
+
|
|
3
|
+
import { getEndpointMetadata } from '../metadata/index.mjs'
|
|
4
|
+
|
|
5
|
+
export function Header(name: HttpHeader, value: string | number | string[]) {
|
|
6
|
+
return <T extends Function>(
|
|
7
|
+
target: T,
|
|
8
|
+
context: ClassMethodDecoratorContext,
|
|
9
|
+
) => {
|
|
10
|
+
if (context.kind !== 'method') {
|
|
11
|
+
throw new Error('[Navios] Header decorator can only be used on methods.')
|
|
12
|
+
}
|
|
13
|
+
const metadata = getEndpointMetadata(target, context)
|
|
14
|
+
metadata.headers[name] = value
|
|
15
|
+
|
|
16
|
+
return target
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getEndpointMetadata } from '../metadata/index.mjs'
|
|
2
|
+
|
|
3
|
+
export function HttpCode(code: number) {
|
|
4
|
+
return <T extends Function>(
|
|
5
|
+
target: T,
|
|
6
|
+
context: ClassMethodDecoratorContext,
|
|
7
|
+
) => {
|
|
8
|
+
if (context.kind !== 'method') {
|
|
9
|
+
throw new Error(
|
|
10
|
+
'[Navios] HttpCode decorator can only be used on methods.',
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
const metadata = getEndpointMetadata(target, context)
|
|
14
|
+
metadata.successStatusCode = code
|
|
15
|
+
|
|
16
|
+
return target
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/decorators/index.mts
CHANGED
|
@@ -184,7 +184,6 @@ export class LoggerInstance implements LoggerService {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
static overrideLogger(logger: LoggerService | LogLevel[] | boolean) {
|
|
187
|
-
console.log(logger)
|
|
188
187
|
if (Array.isArray(logger)) {
|
|
189
188
|
LoggerInstance.logLevels = logger
|
|
190
189
|
return this.staticInstanceRef?.setLogLevels?.(logger)
|
|
@@ -20,8 +20,9 @@ export class PinoWrapper {
|
|
|
20
20
|
this.logger.warn(message, ...optionalParams)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
info(
|
|
24
|
-
|
|
23
|
+
info() {
|
|
24
|
+
// We don't want to populate the logs with the original fastify logs
|
|
25
|
+
// this.logger.debug?.('INFO', message, ...optionalParams)
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
debug(message: any, ...optionalParams: any[]) {
|
|
@@ -32,7 +33,7 @@ export class PinoWrapper {
|
|
|
32
33
|
this.logger.verbose?.(message, ...optionalParams)
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
silent(
|
|
36
|
+
silent() {
|
|
36
37
|
// noop
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -56,8 +57,8 @@ export class PinoWrapper {
|
|
|
56
57
|
}
|
|
57
58
|
const levels = LoggerInstance['logLevels']
|
|
58
59
|
if (levels) {
|
|
59
|
-
return levels
|
|
60
|
+
return levels.find((level) => level !== 'verbose')
|
|
60
61
|
}
|
|
61
|
-
return '
|
|
62
|
+
return 'warn'
|
|
62
63
|
}
|
|
63
64
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BaseEndpointConfig, HttpMethod } from '@navios/common'
|
|
2
|
+
import type { HttpHeader } from 'fastify/types/utils.js'
|
|
2
3
|
|
|
3
4
|
import type { CanActivate } from '../interfaces/index.mjs'
|
|
4
5
|
import type {
|
|
@@ -8,9 +9,18 @@ import type {
|
|
|
8
9
|
|
|
9
10
|
export const EndpointMetadataKey = Symbol('EndpointMetadataKey')
|
|
10
11
|
|
|
12
|
+
export enum EndpointType {
|
|
13
|
+
Unknown = 'unknown',
|
|
14
|
+
Config = 'config',
|
|
15
|
+
Handler = 'handler',
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
export interface EndpointMetadata {
|
|
12
19
|
classMethod: string
|
|
13
20
|
url: string
|
|
21
|
+
successStatusCode: number
|
|
22
|
+
type: EndpointType
|
|
23
|
+
headers: Partial<Record<HttpHeader, number | string | string[] | undefined>>
|
|
14
24
|
httpMethod: HttpMethod
|
|
15
25
|
config: BaseEndpointConfig | null
|
|
16
26
|
guards: Set<
|
|
@@ -52,6 +62,9 @@ export function getEndpointMetadata(
|
|
|
52
62
|
const newMetadata: EndpointMetadata = {
|
|
53
63
|
classMethod: target.name,
|
|
54
64
|
url: '',
|
|
65
|
+
successStatusCode: 200,
|
|
66
|
+
headers: {},
|
|
67
|
+
type: EndpointType.Unknown,
|
|
55
68
|
httpMethod: 'GET',
|
|
56
69
|
config: null,
|
|
57
70
|
guards: new Set<
|
|
@@ -16,6 +16,7 @@ import type { NaviosModule } from './interfaces/index.mjs'
|
|
|
16
16
|
import type { LoggerService, LogLevel } from './logger/index.mjs'
|
|
17
17
|
import type { ClassTypeWithInstance } from './service-locator/index.mjs'
|
|
18
18
|
|
|
19
|
+
import { HttpException } from './exceptions/index.mjs'
|
|
19
20
|
import { Logger, PinoWrapper } from './logger/index.mjs'
|
|
20
21
|
import {
|
|
21
22
|
getServiceLocator,
|
|
@@ -68,6 +69,7 @@ export class NaviosApplication {
|
|
|
68
69
|
}
|
|
69
70
|
await this.moduleLoader.loadModules(this.appModule)
|
|
70
71
|
this.server = await this.getFastifyInstance(this.options)
|
|
72
|
+
this.configureFastifyInstance(this.server)
|
|
71
73
|
getServiceLocator().registerInstance(Application, this.server)
|
|
72
74
|
// Add schema validator and serializer
|
|
73
75
|
this.server.setValidatorCompiler(validatorCompiler)
|
|
@@ -78,6 +80,7 @@ export class NaviosApplication {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
await this.initModules()
|
|
83
|
+
await this.server.ready()
|
|
81
84
|
|
|
82
85
|
this.logger.debug('Navios application initialized')
|
|
83
86
|
}
|
|
@@ -110,6 +113,37 @@ export class NaviosApplication {
|
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
116
|
+
private configureFastifyInstance(fastifyInstance: FastifyInstance) {
|
|
117
|
+
fastifyInstance.setErrorHandler((error, request, reply) => {
|
|
118
|
+
if (error instanceof HttpException) {
|
|
119
|
+
return reply.status(error.statusCode).send(error.response)
|
|
120
|
+
} else {
|
|
121
|
+
const statusCode = error.statusCode || 500
|
|
122
|
+
const message = error.message || 'Internal Server Error'
|
|
123
|
+
const response = {
|
|
124
|
+
statusCode,
|
|
125
|
+
message,
|
|
126
|
+
error: error.name || 'InternalServerError',
|
|
127
|
+
}
|
|
128
|
+
this.logger.error(
|
|
129
|
+
`Error occurred: ${error.message} on ${request.url}`,
|
|
130
|
+
error,
|
|
131
|
+
)
|
|
132
|
+
return reply.status(statusCode).send(response)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
fastifyInstance.setNotFoundHandler((req, reply) => {
|
|
137
|
+
const response = {
|
|
138
|
+
statusCode: 404,
|
|
139
|
+
message: 'Not Found',
|
|
140
|
+
error: 'NotFound',
|
|
141
|
+
}
|
|
142
|
+
this.logger.error(`Route not found: ${req.url}`)
|
|
143
|
+
return reply.status(404).send(response)
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
113
147
|
private async initModules() {
|
|
114
148
|
const modules = this.moduleLoader.getAllModules()
|
|
115
149
|
const promises: PromiseLike<any>[] = []
|
|
@@ -161,6 +195,7 @@ export class NaviosApplication {
|
|
|
161
195
|
if (!this.server) {
|
|
162
196
|
throw new Error('Server is not initialized. Call init() first.')
|
|
163
197
|
}
|
|
164
|
-
await this.server.listen(options)
|
|
198
|
+
const res = await this.server.listen(options)
|
|
199
|
+
this.logger.debug(`Navios is listening on ${res}`)
|
|
165
200
|
}
|
|
166
201
|
}
|
|
@@ -2,11 +2,12 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
getInjectableToken,
|
|
5
6
|
Injectable,
|
|
6
7
|
InjectableType,
|
|
7
8
|
} from '../decorators/index.mjs'
|
|
8
9
|
import { InjectableScope } from '../enums/index.mjs'
|
|
9
|
-
import {
|
|
10
|
+
import { syncInject } from '../index.mjs'
|
|
10
11
|
import { inject } from '../inject.mjs'
|
|
11
12
|
import { InjectionToken } from '../injection-token.mjs'
|
|
12
13
|
import { getServiceLocator } from '../injector.mjs'
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { Injectable, InjectableType } from '../decorators/index.mjs'
|
|
5
|
+
import { inject } from '../inject.mjs'
|
|
6
|
+
import { InjectionToken } from '../injection-token.mjs'
|
|
7
|
+
|
|
8
|
+
describe('InjectToken', () => {
|
|
9
|
+
it('should work with class', async () => {
|
|
10
|
+
const token = InjectionToken.create('Test')
|
|
11
|
+
@Injectable({
|
|
12
|
+
token,
|
|
13
|
+
})
|
|
14
|
+
class Test {}
|
|
15
|
+
|
|
16
|
+
const value = await inject(Test)
|
|
17
|
+
expect(value).toBeInstanceOf(Test)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should work with class and schema', async () => {
|
|
21
|
+
const schema = z.object({
|
|
22
|
+
test: z.string(),
|
|
23
|
+
})
|
|
24
|
+
const token = InjectionToken.create('Test', schema)
|
|
25
|
+
|
|
26
|
+
@Injectable({
|
|
27
|
+
token,
|
|
28
|
+
})
|
|
29
|
+
class Test {
|
|
30
|
+
makeFoo() {
|
|
31
|
+
return 'foo'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const value = await inject(token, {
|
|
35
|
+
test: 'test',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
expect(value).toBeInstanceOf(Test)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should work with factory', async () => {
|
|
42
|
+
const token = InjectionToken.create('Test')
|
|
43
|
+
@Injectable({
|
|
44
|
+
token,
|
|
45
|
+
type: InjectableType.Factory,
|
|
46
|
+
})
|
|
47
|
+
class Test {
|
|
48
|
+
create() {
|
|
49
|
+
return 'foo'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const value = await inject(Test)
|
|
54
|
+
expect(value).toBe('foo')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should work with factory and schema', async () => {
|
|
58
|
+
const schema = z.object({
|
|
59
|
+
test: z.string(),
|
|
60
|
+
})
|
|
61
|
+
const token = InjectionToken.create<string, typeof schema>('Test', schema)
|
|
62
|
+
|
|
63
|
+
@Injectable({
|
|
64
|
+
token,
|
|
65
|
+
type: InjectableType.Factory,
|
|
66
|
+
})
|
|
67
|
+
class Test {
|
|
68
|
+
create(ctx: any, args: { test: string }) {
|
|
69
|
+
return args.test
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const value = await inject(token, {
|
|
73
|
+
test: 'test',
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(value).toBe('test')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should work with bound token', async () => {
|
|
80
|
+
const schema = z.object({
|
|
81
|
+
test: z.string(),
|
|
82
|
+
})
|
|
83
|
+
const token = InjectionToken.create('Test', schema)
|
|
84
|
+
const boundToken = InjectionToken.bound(token, {
|
|
85
|
+
test: 'test',
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
@Injectable({
|
|
89
|
+
token,
|
|
90
|
+
})
|
|
91
|
+
class Test {
|
|
92
|
+
makeFoo() {
|
|
93
|
+
return 'foo'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const value = await inject(boundToken)
|
|
97
|
+
|
|
98
|
+
expect(value).toBeInstanceOf(Test)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should work with factory token', async () => {
|
|
102
|
+
const schema = z.object({
|
|
103
|
+
test: z.string(),
|
|
104
|
+
})
|
|
105
|
+
const token = InjectionToken.create('Test', schema)
|
|
106
|
+
const factoryInjectionToken = InjectionToken.factory(token, () =>
|
|
107
|
+
Promise.resolve({
|
|
108
|
+
test: 'test',
|
|
109
|
+
}),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
@Injectable({
|
|
113
|
+
token,
|
|
114
|
+
})
|
|
115
|
+
class Test {
|
|
116
|
+
makeFoo() {
|
|
117
|
+
return 'foo'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const value = await inject(factoryInjectionToken)
|
|
121
|
+
|
|
122
|
+
expect(value).toBeInstanceOf(Test)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ClassType,
|
|
3
|
-
ClassTypeWithInstance,
|
|
4
|
-
InjectionToken,
|
|
5
|
-
} from '../injection-token.mjs'
|
|
1
|
+
import type { ClassType, InjectionToken } from '../injection-token.mjs'
|
|
6
2
|
|
|
7
3
|
import { InjectableTokenMeta } from './injectable.decorator.mjs'
|
|
8
4
|
|
|
9
|
-
export function getInjectableToken<R
|
|
5
|
+
export function getInjectableToken<R>(
|
|
10
6
|
target: ClassType,
|
|
11
7
|
): R extends { create(...args: any[]): infer V }
|
|
12
8
|
? InjectionToken<V>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { NaviosException } from '@navios/common'
|
|
2
|
+
|
|
1
3
|
import type { ClassType } from '../injection-token.mjs'
|
|
2
4
|
|
|
3
5
|
import { InjectableScope } from '../enums/index.mjs'
|
|
4
6
|
import { InjectionToken } from '../injection-token.mjs'
|
|
5
|
-
import { getServiceLocator
|
|
6
|
-
import {
|
|
7
|
-
import { setPromiseCollector } from '../sync-injector.mjs'
|
|
7
|
+
import { getServiceLocator } from '../injector.mjs'
|
|
8
|
+
import { resolveService } from '../resolve-service.mjs'
|
|
8
9
|
|
|
9
10
|
export enum InjectableType {
|
|
10
11
|
Class = 'Class',
|
|
@@ -33,62 +34,22 @@ export function Injectable({
|
|
|
33
34
|
let injectableToken: InjectionToken<any, any> =
|
|
34
35
|
token ?? InjectionToken.create(target)
|
|
35
36
|
const locator = getServiceLocator()
|
|
36
|
-
if (!locator) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
'[ServiceLocator] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator.',
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
37
|
if (type === InjectableType.Class) {
|
|
42
38
|
locator.registerAbstractFactory(
|
|
43
39
|
injectableToken,
|
|
44
|
-
async (ctx) =>
|
|
45
|
-
if (scope === InjectableScope.Instance) {
|
|
46
|
-
ctx.setTtl(0)
|
|
47
|
-
}
|
|
48
|
-
const proxyServiceLocator = makeProxyServiceLocator(
|
|
49
|
-
getServiceLocator(),
|
|
50
|
-
ctx,
|
|
51
|
-
)
|
|
52
|
-
const promises: Promise<any>[] = []
|
|
53
|
-
const promiseCollector = (promise: Promise<any>) => {
|
|
54
|
-
promises.push(promise)
|
|
55
|
-
}
|
|
56
|
-
const originalPromiseCollector = setPromiseCollector(promiseCollector)
|
|
57
|
-
const tryInit = () => {
|
|
58
|
-
const original = provideServiceLocator(proxyServiceLocator)
|
|
59
|
-
let result = new target()
|
|
60
|
-
provideServiceLocator(original)
|
|
61
|
-
return result
|
|
62
|
-
}
|
|
63
|
-
const result = tryInit()
|
|
64
|
-
setPromiseCollector(originalPromiseCollector)
|
|
65
|
-
if (promises.length > 0) {
|
|
66
|
-
await Promise.all(promises)
|
|
67
|
-
return tryInit()
|
|
68
|
-
}
|
|
69
|
-
return result
|
|
70
|
-
},
|
|
40
|
+
async (ctx) => resolveService(ctx, target),
|
|
71
41
|
scope,
|
|
72
42
|
)
|
|
73
43
|
} else if (type === InjectableType.Factory) {
|
|
74
44
|
locator.registerAbstractFactory(
|
|
75
45
|
injectableToken,
|
|
76
46
|
async (ctx, args: any) => {
|
|
77
|
-
|
|
78
|
-
ctx.setTtl(0)
|
|
79
|
-
}
|
|
80
|
-
const proxyServiceLocator = makeProxyServiceLocator(
|
|
81
|
-
getServiceLocator(),
|
|
82
|
-
ctx,
|
|
83
|
-
)
|
|
84
|
-
const original = provideServiceLocator(proxyServiceLocator)
|
|
85
|
-
const builder = new target()
|
|
47
|
+
const builder = await resolveService(ctx, target)
|
|
86
48
|
if (typeof builder.create !== 'function') {
|
|
87
|
-
throw new
|
|
49
|
+
throw new NaviosException(
|
|
88
50
|
`[ServiceLocator] Factory ${target.name} does not implement the create method.`,
|
|
89
51
|
)
|
|
90
52
|
}
|
|
91
|
-
provideServiceLocator(original)
|
|
92
53
|
return builder.create(ctx, args)
|
|
93
54
|
},
|
|
94
55
|
scope,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClassType } from '../injection-token.mjs'
|
|
2
|
+
|
|
3
|
+
import { ErrorsEnum } from './errors.enum.mjs'
|
|
4
|
+
|
|
5
|
+
export class FactoryTokenNotResolved extends Error {
|
|
6
|
+
code = ErrorsEnum.FactoryTokenNotResolved
|
|
7
|
+
constructor(name: string | symbol | ClassType) {
|
|
8
|
+
super(`Factory token not resolved: ${name.toString()}`)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -17,26 +17,12 @@ export function inject<T, S extends ZodOptional<AnyZodObject>>(
|
|
|
17
17
|
): Promise<T>
|
|
18
18
|
|
|
19
19
|
export function inject<T>(token: InjectionToken<T, undefined>): Promise<T>
|
|
20
|
-
export function inject<
|
|
21
|
-
|
|
22
|
-
Token extends InjectionToken<T>,
|
|
23
|
-
S extends AnyZodObject | unknown = Token['schema'],
|
|
24
|
-
>(
|
|
25
|
-
token: Token,
|
|
26
|
-
args?: S extends AnyZodObject ? z.input<S> : never,
|
|
27
|
-
): Promise<T> {
|
|
28
|
-
if (token.schema) {
|
|
29
|
-
const parsed = token.schema.safeParse(args)
|
|
30
|
-
if (!parsed.success) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`[ServiceLocator] Invalid arguments for ${token.name.toString()}: ${parsed.error}`,
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
let realToken: InjectionToken<T, S> = token
|
|
20
|
+
export function inject(token: InjectionToken<any>, args?: unknown) {
|
|
21
|
+
let realToken = token
|
|
37
22
|
if (!(token instanceof InjectionToken)) {
|
|
38
|
-
realToken = getInjectableToken(token)
|
|
23
|
+
realToken = getInjectableToken(token)
|
|
39
24
|
}
|
|
40
25
|
|
|
26
|
+
// @ts-expect-error We chek the type in overload
|
|
41
27
|
return getServiceLocator().getOrThrowInstance(realToken, args)
|
|
42
28
|
}
|
|
@@ -2,7 +2,7 @@ import type { AnyZodObject } from 'zod'
|
|
|
2
2
|
|
|
3
3
|
import { randomUUID } from 'crypto'
|
|
4
4
|
|
|
5
|
-
import { ZodOptional } from 'zod'
|
|
5
|
+
import { z, ZodOptional } from 'zod'
|
|
6
6
|
|
|
7
7
|
export type ClassType = new (...args: any[]) => any
|
|
8
8
|
|
|
@@ -25,17 +25,68 @@ export class InjectionToken<
|
|
|
25
25
|
T extends ClassType,
|
|
26
26
|
Schema extends AnyZodObject | ZodOptional<AnyZodObject>,
|
|
27
27
|
>(name: T, schema: Schema): InjectionToken<InstanceType<T>, Schema>
|
|
28
|
-
static create<T>(name: string): InjectionToken<T, undefined>
|
|
28
|
+
static create<T>(name: string | symbol): InjectionToken<T, undefined>
|
|
29
29
|
static create<T, Schema extends AnyZodObject | ZodOptional<AnyZodObject>>(
|
|
30
|
-
name: string,
|
|
30
|
+
name: string | any,
|
|
31
31
|
schema: Schema,
|
|
32
32
|
): InjectionToken<T, Schema>
|
|
33
|
-
static create(name: string, schema?: unknown) {
|
|
33
|
+
static create(name: string | symbol, schema?: unknown) {
|
|
34
34
|
// @ts-expect-error
|
|
35
35
|
return new InjectionToken(name, schema)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
static bound<T, S extends AnyZodObject | ZodOptional<AnyZodObject>>(
|
|
39
|
+
token: InjectionToken<T, S>,
|
|
40
|
+
value: z.input<S>,
|
|
41
|
+
): BoundInjectionToken<T, S> {
|
|
42
|
+
return new BoundInjectionToken(token, value)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static factory<T, S extends AnyZodObject | ZodOptional<AnyZodObject>>(
|
|
46
|
+
token: InjectionToken<T, S>,
|
|
47
|
+
factory: () => Promise<z.input<S>>,
|
|
48
|
+
): FactoryInjectionToken<T, S> {
|
|
49
|
+
return new FactoryInjectionToken(token, factory)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static refineType<T>(
|
|
53
|
+
token: BoundInjectionToken<any, any>,
|
|
54
|
+
): BoundInjectionToken<T, any> {
|
|
55
|
+
return token as BoundInjectionToken<T, any>
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class BoundInjectionToken<
|
|
60
|
+
T,
|
|
61
|
+
S extends AnyZodObject | ZodOptional<AnyZodObject>,
|
|
62
|
+
> extends InjectionToken<T, undefined> {
|
|
63
|
+
constructor(
|
|
64
|
+
public readonly token: InjectionToken<T, S>,
|
|
65
|
+
public readonly value: z.input<S>,
|
|
66
|
+
) {
|
|
67
|
+
super(token.name, token.schema)
|
|
68
|
+
this.id = token.id
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class FactoryInjectionToken<
|
|
73
|
+
T,
|
|
74
|
+
S extends AnyZodObject | ZodOptional<AnyZodObject>,
|
|
75
|
+
> extends InjectionToken<T, S> {
|
|
76
|
+
public value?: z.input<S>
|
|
77
|
+
public resolved = false
|
|
78
|
+
constructor(
|
|
79
|
+
public readonly token: InjectionToken<T, S>,
|
|
80
|
+
public readonly factory: () => Promise<z.input<S>>,
|
|
81
|
+
) {
|
|
82
|
+
super(token.name, token.schema)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async resolve(): Promise<z.input<S>> {
|
|
86
|
+
if (!this.value) {
|
|
87
|
+
this.value = await this.factory()
|
|
88
|
+
this.resolved = true
|
|
89
|
+
}
|
|
90
|
+
return this.value
|
|
40
91
|
}
|
|
41
92
|
}
|