@navios/core 0.1.3 → 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 +34 -11
- package/dist/index.d.ts +34 -11
- package/dist/index.js +155 -121
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +158 -127
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/config/config.provider.mts +11 -20
- 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/package.json
CHANGED
|
@@ -4,36 +4,34 @@ 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,
|
|
10
|
+
syncInject,
|
|
12
11
|
} from '../service-locator/index.mjs'
|
|
13
12
|
import { ConfigServiceInstance } from './config.service.mjs'
|
|
14
13
|
|
|
15
|
-
export const ConfigProviderInjectionToken = 'ConfigProvider'
|
|
16
|
-
|
|
17
14
|
export const ConfigProviderOptions = z.object({
|
|
18
15
|
load: z.function(),
|
|
19
16
|
})
|
|
17
|
+
|
|
20
18
|
export const ConfigProvider = InjectionToken.create<
|
|
21
19
|
ConfigService,
|
|
22
20
|
typeof ConfigProviderOptions
|
|
23
|
-
>(
|
|
21
|
+
>(ConfigServiceInstance, ConfigProviderOptions)
|
|
24
22
|
|
|
25
23
|
@Injectable({
|
|
26
24
|
token: ConfigProvider,
|
|
27
25
|
type: InjectableType.Factory,
|
|
28
26
|
})
|
|
29
27
|
export class ConfigProviderFactory {
|
|
30
|
-
logger =
|
|
28
|
+
logger = syncInject(Logger, {
|
|
31
29
|
context: 'ConfigService',
|
|
32
30
|
})
|
|
33
31
|
|
|
34
32
|
async create(ctx: any, args: z.infer<typeof ConfigProviderOptions>) {
|
|
35
33
|
const { load } = args
|
|
36
|
-
const logger =
|
|
34
|
+
const logger = this.logger
|
|
37
35
|
try {
|
|
38
36
|
const config = await load()
|
|
39
37
|
|
|
@@ -45,18 +43,11 @@ export class ConfigProviderFactory {
|
|
|
45
43
|
}
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
export function
|
|
46
|
+
export function provideConfig<ConfigMap extends Record<string, unknown>>(
|
|
49
47
|
options: z.input<typeof ConfigProviderOptions>,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
configService = inject(ConfigProvider, options)
|
|
56
|
-
|
|
57
|
-
create() {
|
|
58
|
-
return this.configService
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return getInjectableToken(ConfigServiceImpl)
|
|
48
|
+
) {
|
|
49
|
+
return InjectionToken.bound(ConfigProvider, options) as InjectionToken<
|
|
50
|
+
ConfigServiceInstance<ConfigMap>,
|
|
51
|
+
undefined
|
|
52
|
+
>
|
|
62
53
|
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NaviosException } from '@navios/common'
|
|
2
|
+
|
|
3
|
+
import type { ClassType } from './injection-token.mjs'
|
|
4
|
+
import type { ServiceLocatorAbstractFactoryContext } from './service-locator-abstract-factory-context.mjs'
|
|
5
|
+
|
|
6
|
+
import { getServiceLocator, provideServiceLocator } from './injector.mjs'
|
|
7
|
+
import { makeProxyServiceLocator } from './proxy-service-locator.mjs'
|
|
8
|
+
import { setPromiseCollector } from './sync-injector.mjs'
|
|
9
|
+
|
|
10
|
+
export async function resolveService<T extends ClassType>(
|
|
11
|
+
ctx: ServiceLocatorAbstractFactoryContext,
|
|
12
|
+
target: T,
|
|
13
|
+
args: any[] = [],
|
|
14
|
+
): Promise<InstanceType<T>> {
|
|
15
|
+
const proxyServiceLocator = makeProxyServiceLocator(getServiceLocator(), ctx)
|
|
16
|
+
let promises: Promise<any>[] = []
|
|
17
|
+
const promiseCollector = (promise: Promise<any>) => {
|
|
18
|
+
promises.push(promise)
|
|
19
|
+
}
|
|
20
|
+
const originalPromiseCollector = setPromiseCollector(promiseCollector)
|
|
21
|
+
const tryLoad = () => {
|
|
22
|
+
const original = provideServiceLocator(proxyServiceLocator)
|
|
23
|
+
let result = new target(...args)
|
|
24
|
+
provideServiceLocator(original)
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
let instance = tryLoad()
|
|
28
|
+
setPromiseCollector(originalPromiseCollector)
|
|
29
|
+
if (promises.length > 0) {
|
|
30
|
+
await Promise.all(promises)
|
|
31
|
+
promises = []
|
|
32
|
+
instance = tryLoad()
|
|
33
|
+
}
|
|
34
|
+
if (promises.length > 0) {
|
|
35
|
+
console.error(`[ServiceLocator] ${target.name} has problem with it's definition.
|
|
36
|
+
|
|
37
|
+
One or more of the dependencies are registered as a InjectableScope.Instance and are used with syncInject.
|
|
38
|
+
|
|
39
|
+
Please use inject instead of syncInject to load those dependencies.`)
|
|
40
|
+
throw new NaviosException(
|
|
41
|
+
`[ServiceLocator] Service ${target.name} cannot be instantiated.`,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return instance
|
|
46
|
+
}
|