@navios/di 0.1.7 → 0.1.8
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/README.md +78 -0
- package/dist/_tsup-dts-rollup.d.mts +15 -24
- package/dist/_tsup-dts-rollup.d.ts +15 -24
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/__type-tests__/injectable.spec-d.mts +289 -0
- package/src/decorators/injectable.decorator.mts +57 -32
- package/src/injection-token.mts +4 -0
- package/src/interfaces/factory.interface.mts +3 -2
package/README.md
CHANGED
|
@@ -1,2 +1,80 @@
|
|
|
1
1
|
# Navios DI
|
|
2
2
|
|
|
3
|
+
Navios DI is a library that implements the Dependency Injection pattern. It provides you with basic tools to create and manage your services
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @navios/di
|
|
9
|
+
# or
|
|
10
|
+
yarn add @navios/di
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Simple Usage example
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Injectable, inject, syncInject } from '@navios/di';
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
class GreeterService {
|
|
20
|
+
getFoo(user: string): string {
|
|
21
|
+
return `Hello ${user}`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
class UserService {
|
|
27
|
+
private readonly greeterService = syncInject(GreeterService);
|
|
28
|
+
|
|
29
|
+
greet(user: string): string {
|
|
30
|
+
return this.greeterService.getFoo(user);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const userService = await inject(UserService);
|
|
35
|
+
|
|
36
|
+
console.log(userService.greet('World')); // Hello World
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage with Injection Token
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { Injectable, inject, syncInject, InjectionToken } from '@navios/di';
|
|
43
|
+
import { z } from 'zod';
|
|
44
|
+
|
|
45
|
+
const schema = z.object({
|
|
46
|
+
user: z.string(),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
interface GreeterInterface {
|
|
50
|
+
getFoo(): string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const token = new InjectionToken.create<GreeterInterface, typeof schema>(
|
|
54
|
+
Symbol.for('user'),
|
|
55
|
+
schema,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@Injectable({
|
|
59
|
+
token,
|
|
60
|
+
})
|
|
61
|
+
class GreeterService {
|
|
62
|
+
constructor(private readonly config: z.infer<typeof schema>) {}
|
|
63
|
+
|
|
64
|
+
getFoo(): string {
|
|
65
|
+
return `Hello ${this.config.user}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const greetWorld = await inject(token, {
|
|
70
|
+
user: 'World'
|
|
71
|
+
})
|
|
72
|
+
const BoundGreeterService = InjectionToken.bound(token, {
|
|
73
|
+
user: 'Earth'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const greetEarth = await inject(BoundGreeterService);
|
|
77
|
+
|
|
78
|
+
console.log(greetWorld.getFoo()); // Hello World
|
|
79
|
+
console.log(greetEarth.getFoo()); // Hello Earth
|
|
80
|
+
```
|
|
@@ -41,6 +41,14 @@ declare type ClassTypeWithInstanceAndArgument<T, Arg> = new (arg: Arg) => T;
|
|
|
41
41
|
export { ClassTypeWithInstanceAndArgument }
|
|
42
42
|
export { ClassTypeWithInstanceAndArgument as ClassTypeWithInstanceAndArgument_alias_1 }
|
|
43
43
|
|
|
44
|
+
declare type ClassTypeWithInstanceAndOptionalArgument<T, Arg> = new (arg?: Arg) => T;
|
|
45
|
+
export { ClassTypeWithInstanceAndOptionalArgument }
|
|
46
|
+
export { ClassTypeWithInstanceAndOptionalArgument as ClassTypeWithInstanceAndOptionalArgument_alias_1 }
|
|
47
|
+
|
|
48
|
+
declare type ClassTypeWithOptionalArgument<Arg> = new (arg?: Arg) => any;
|
|
49
|
+
export { ClassTypeWithOptionalArgument }
|
|
50
|
+
export { ClassTypeWithOptionalArgument as ClassTypeWithOptionalArgument_alias_1 }
|
|
51
|
+
|
|
44
52
|
declare interface CreateInjectorsOptions {
|
|
45
53
|
baseLocator: ServiceLocator;
|
|
46
54
|
}
|
|
@@ -94,7 +102,7 @@ export { EventsNames }
|
|
|
94
102
|
export { EventsNames as EventsNames_alias_1 }
|
|
95
103
|
|
|
96
104
|
declare interface Factory<T> {
|
|
97
|
-
create(ctx?:
|
|
105
|
+
create(ctx?: FactoryContext): Promise<T> | T;
|
|
98
106
|
}
|
|
99
107
|
export { Factory }
|
|
100
108
|
export { Factory as Factory_alias_1 }
|
|
@@ -155,7 +163,7 @@ export { FactoryTokenNotResolved as FactoryTokenNotResolved_alias_1 }
|
|
|
155
163
|
export { FactoryTokenNotResolved as FactoryTokenNotResolved_alias_2 }
|
|
156
164
|
|
|
157
165
|
declare interface FactoryWithArgs<T, A extends InjectionTokenSchemaType> {
|
|
158
|
-
create(
|
|
166
|
+
create(...args: [FactoryContext, z.output<A>]): Promise<T> | T;
|
|
159
167
|
}
|
|
160
168
|
export { FactoryWithArgs }
|
|
161
169
|
export { FactoryWithArgs as FactoryWithArgs_alias_1 }
|
|
@@ -192,34 +200,17 @@ declare function Injectable<R>(options: {
|
|
|
192
200
|
type: InjectableType.Factory;
|
|
193
201
|
}): <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T;
|
|
194
202
|
|
|
195
|
-
declare function Injectable<
|
|
203
|
+
declare function Injectable<Type, Schema>(options: {
|
|
196
204
|
scope?: InjectableScope;
|
|
197
205
|
type?: InjectableType.Class;
|
|
198
|
-
token: InjectionToken<
|
|
199
|
-
}): <T extends ClassTypeWithArgument<z.output<
|
|
206
|
+
token: InjectionToken<Type, Schema>;
|
|
207
|
+
}): Schema extends BaseInjectionTokenSchemaType ? Type extends undefined ? <T extends ClassTypeWithArgument<z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : <T extends ClassTypeWithInstanceAndArgument<Type, z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : Schema extends OptionalInjectionTokenSchemaType ? Type extends undefined ? <T extends ClassTypeWithOptionalArgument<z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : <T extends ClassTypeWithInstanceAndOptionalArgument<Type, z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : Schema extends undefined ? <R extends ClassTypeWithInstance<Type>>(target: R, context: ClassDecoratorContext) => R : never;
|
|
200
208
|
|
|
201
|
-
declare function Injectable<R, S
|
|
202
|
-
scope?: InjectableScope;
|
|
203
|
-
type?: InjectableType.Class;
|
|
204
|
-
token: InjectionToken<R, S>;
|
|
205
|
-
}): <T extends ClassTypeWithInstanceAndArgument<R, z.output<S>>>(target: T, context: ClassDecoratorContext) => T;
|
|
206
|
-
|
|
207
|
-
declare function Injectable<T extends ClassType>(options: {
|
|
208
|
-
scope?: InjectableScope;
|
|
209
|
-
token: InjectionToken<T, undefined>;
|
|
210
|
-
}): (target: T, context: ClassDecoratorContext) => T;
|
|
211
|
-
|
|
212
|
-
declare function Injectable<R, S extends InjectionTokenSchemaType>(options: {
|
|
209
|
+
declare function Injectable<R, S>(options: {
|
|
213
210
|
scope?: InjectableScope;
|
|
214
211
|
type: InjectableType.Factory;
|
|
215
212
|
token: InjectionToken<R, S>;
|
|
216
|
-
}): <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(target: T, context: ClassDecoratorContext) => T;
|
|
217
|
-
|
|
218
|
-
declare function Injectable<R>(options: {
|
|
219
|
-
scope?: InjectableScope;
|
|
220
|
-
type: InjectableType.Factory;
|
|
221
|
-
token: InjectionToken<R, undefined>;
|
|
222
|
-
}): <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T;
|
|
213
|
+
}): R extends undefined ? never : S extends InjectionTokenSchemaType ? <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(target: T, context: ClassDecoratorContext) => T : S extends undefined ? <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T : never;
|
|
223
214
|
export { Injectable }
|
|
224
215
|
export { Injectable as Injectable_alias_1 }
|
|
225
216
|
export { Injectable as Injectable_alias_2 }
|
|
@@ -41,6 +41,14 @@ declare type ClassTypeWithInstanceAndArgument<T, Arg> = new (arg: Arg) => T;
|
|
|
41
41
|
export { ClassTypeWithInstanceAndArgument }
|
|
42
42
|
export { ClassTypeWithInstanceAndArgument as ClassTypeWithInstanceAndArgument_alias_1 }
|
|
43
43
|
|
|
44
|
+
declare type ClassTypeWithInstanceAndOptionalArgument<T, Arg> = new (arg?: Arg) => T;
|
|
45
|
+
export { ClassTypeWithInstanceAndOptionalArgument }
|
|
46
|
+
export { ClassTypeWithInstanceAndOptionalArgument as ClassTypeWithInstanceAndOptionalArgument_alias_1 }
|
|
47
|
+
|
|
48
|
+
declare type ClassTypeWithOptionalArgument<Arg> = new (arg?: Arg) => any;
|
|
49
|
+
export { ClassTypeWithOptionalArgument }
|
|
50
|
+
export { ClassTypeWithOptionalArgument as ClassTypeWithOptionalArgument_alias_1 }
|
|
51
|
+
|
|
44
52
|
declare interface CreateInjectorsOptions {
|
|
45
53
|
baseLocator: ServiceLocator;
|
|
46
54
|
}
|
|
@@ -94,7 +102,7 @@ export { EventsNames }
|
|
|
94
102
|
export { EventsNames as EventsNames_alias_1 }
|
|
95
103
|
|
|
96
104
|
declare interface Factory<T> {
|
|
97
|
-
create(ctx?:
|
|
105
|
+
create(ctx?: FactoryContext): Promise<T> | T;
|
|
98
106
|
}
|
|
99
107
|
export { Factory }
|
|
100
108
|
export { Factory as Factory_alias_1 }
|
|
@@ -155,7 +163,7 @@ export { FactoryTokenNotResolved as FactoryTokenNotResolved_alias_1 }
|
|
|
155
163
|
export { FactoryTokenNotResolved as FactoryTokenNotResolved_alias_2 }
|
|
156
164
|
|
|
157
165
|
declare interface FactoryWithArgs<T, A extends InjectionTokenSchemaType> {
|
|
158
|
-
create(
|
|
166
|
+
create(...args: [FactoryContext, z.output<A>]): Promise<T> | T;
|
|
159
167
|
}
|
|
160
168
|
export { FactoryWithArgs }
|
|
161
169
|
export { FactoryWithArgs as FactoryWithArgs_alias_1 }
|
|
@@ -192,34 +200,17 @@ declare function Injectable<R>(options: {
|
|
|
192
200
|
type: InjectableType.Factory;
|
|
193
201
|
}): <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T;
|
|
194
202
|
|
|
195
|
-
declare function Injectable<
|
|
203
|
+
declare function Injectable<Type, Schema>(options: {
|
|
196
204
|
scope?: InjectableScope;
|
|
197
205
|
type?: InjectableType.Class;
|
|
198
|
-
token: InjectionToken<
|
|
199
|
-
}): <T extends ClassTypeWithArgument<z.output<
|
|
206
|
+
token: InjectionToken<Type, Schema>;
|
|
207
|
+
}): Schema extends BaseInjectionTokenSchemaType ? Type extends undefined ? <T extends ClassTypeWithArgument<z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : <T extends ClassTypeWithInstanceAndArgument<Type, z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : Schema extends OptionalInjectionTokenSchemaType ? Type extends undefined ? <T extends ClassTypeWithOptionalArgument<z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : <T extends ClassTypeWithInstanceAndOptionalArgument<Type, z.output<Schema>>>(target: T, context: ClassDecoratorContext) => T : Schema extends undefined ? <R extends ClassTypeWithInstance<Type>>(target: R, context: ClassDecoratorContext) => R : never;
|
|
200
208
|
|
|
201
|
-
declare function Injectable<R, S
|
|
202
|
-
scope?: InjectableScope;
|
|
203
|
-
type?: InjectableType.Class;
|
|
204
|
-
token: InjectionToken<R, S>;
|
|
205
|
-
}): <T extends ClassTypeWithInstanceAndArgument<R, z.output<S>>>(target: T, context: ClassDecoratorContext) => T;
|
|
206
|
-
|
|
207
|
-
declare function Injectable<T extends ClassType>(options: {
|
|
208
|
-
scope?: InjectableScope;
|
|
209
|
-
token: InjectionToken<T, undefined>;
|
|
210
|
-
}): (target: T, context: ClassDecoratorContext) => T;
|
|
211
|
-
|
|
212
|
-
declare function Injectable<R, S extends InjectionTokenSchemaType>(options: {
|
|
209
|
+
declare function Injectable<R, S>(options: {
|
|
213
210
|
scope?: InjectableScope;
|
|
214
211
|
type: InjectableType.Factory;
|
|
215
212
|
token: InjectionToken<R, S>;
|
|
216
|
-
}): <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(target: T, context: ClassDecoratorContext) => T;
|
|
217
|
-
|
|
218
|
-
declare function Injectable<R>(options: {
|
|
219
|
-
scope?: InjectableScope;
|
|
220
|
-
type: InjectableType.Factory;
|
|
221
|
-
token: InjectionToken<R, undefined>;
|
|
222
|
-
}): <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T;
|
|
213
|
+
}): R extends undefined ? never : S extends InjectionTokenSchemaType ? <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(target: T, context: ClassDecoratorContext) => T : S extends undefined ? <T extends ClassTypeWithInstance<Factory<R>>>(target: T, context: ClassDecoratorContext) => T : never;
|
|
223
214
|
export { Injectable }
|
|
224
215
|
export { Injectable as Injectable_alias_1 }
|
|
225
216
|
export { Injectable as Injectable_alias_2 }
|
package/dist/index.d.mts
CHANGED
|
@@ -25,8 +25,10 @@ export { EventEmitter_alias_1 as EventEmitter } from './_tsup-dts-rollup.mjs';
|
|
|
25
25
|
export { FactoryContext_alias_1 as FactoryContext } from './_tsup-dts-rollup.mjs';
|
|
26
26
|
export { ClassType } from './_tsup-dts-rollup.mjs';
|
|
27
27
|
export { ClassTypeWithArgument } from './_tsup-dts-rollup.mjs';
|
|
28
|
+
export { ClassTypeWithOptionalArgument } from './_tsup-dts-rollup.mjs';
|
|
28
29
|
export { ClassTypeWithInstance } from './_tsup-dts-rollup.mjs';
|
|
29
30
|
export { ClassTypeWithInstanceAndArgument } from './_tsup-dts-rollup.mjs';
|
|
31
|
+
export { ClassTypeWithInstanceAndOptionalArgument } from './_tsup-dts-rollup.mjs';
|
|
30
32
|
export { BaseInjectionTokenSchemaType } from './_tsup-dts-rollup.mjs';
|
|
31
33
|
export { OptionalInjectionTokenSchemaType } from './_tsup-dts-rollup.mjs';
|
|
32
34
|
export { InjectionTokenSchemaType } from './_tsup-dts-rollup.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,10 @@ export { EventEmitter_alias_1 as EventEmitter } from './_tsup-dts-rollup.js';
|
|
|
25
25
|
export { FactoryContext_alias_1 as FactoryContext } from './_tsup-dts-rollup.js';
|
|
26
26
|
export { ClassType } from './_tsup-dts-rollup.js';
|
|
27
27
|
export { ClassTypeWithArgument } from './_tsup-dts-rollup.js';
|
|
28
|
+
export { ClassTypeWithOptionalArgument } from './_tsup-dts-rollup.js';
|
|
28
29
|
export { ClassTypeWithInstance } from './_tsup-dts-rollup.js';
|
|
29
30
|
export { ClassTypeWithInstanceAndArgument } from './_tsup-dts-rollup.js';
|
|
31
|
+
export { ClassTypeWithInstanceAndOptionalArgument } from './_tsup-dts-rollup.js';
|
|
30
32
|
export { BaseInjectionTokenSchemaType } from './_tsup-dts-rollup.js';
|
|
31
33
|
export { OptionalInjectionTokenSchemaType } from './_tsup-dts-rollup.js';
|
|
32
34
|
export { InjectionTokenSchemaType } from './_tsup-dts-rollup.js';
|
package/package.json
CHANGED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { expectTypeOf, test } from 'vitest'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { Injectable } from '../decorators/index.mjs'
|
|
5
|
+
import { InjectableType } from '../enums/index.mjs'
|
|
6
|
+
import { InjectionToken } from '../injection-token.mjs'
|
|
7
|
+
|
|
8
|
+
interface FooService {
|
|
9
|
+
makeFoo(): string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const simpleObjectSchema = z.object({
|
|
13
|
+
foo: z.string(),
|
|
14
|
+
})
|
|
15
|
+
const simpleOptionalObjectSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
foo: z.string(),
|
|
18
|
+
})
|
|
19
|
+
.optional()
|
|
20
|
+
const simpleRecordSchema = z.record(z.string())
|
|
21
|
+
const simpleOptionalRecordSchema = z.record(z.string()).optional()
|
|
22
|
+
|
|
23
|
+
const typelessObjectToken = InjectionToken.create(
|
|
24
|
+
Symbol.for('Typeless object token'),
|
|
25
|
+
simpleObjectSchema,
|
|
26
|
+
)
|
|
27
|
+
const typelessOptionalObjectToken = InjectionToken.create(
|
|
28
|
+
Symbol.for('Typeless optional object token'),
|
|
29
|
+
simpleOptionalObjectSchema,
|
|
30
|
+
)
|
|
31
|
+
const typelessRecordToken = InjectionToken.create(
|
|
32
|
+
Symbol.for('Typeless record token'),
|
|
33
|
+
simpleRecordSchema,
|
|
34
|
+
)
|
|
35
|
+
const typelessOptionalRecordToken = InjectionToken.create(
|
|
36
|
+
Symbol.for('Typeless optional record token'),
|
|
37
|
+
simpleOptionalRecordSchema,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const typedObjectToken = InjectionToken.create<
|
|
41
|
+
FooService,
|
|
42
|
+
typeof simpleObjectSchema
|
|
43
|
+
>(Symbol.for('Typed object token'), simpleObjectSchema)
|
|
44
|
+
const typedOptionalObjectToken = InjectionToken.create<
|
|
45
|
+
FooService,
|
|
46
|
+
typeof simpleOptionalObjectSchema
|
|
47
|
+
>(Symbol.for('Typed optional object token'), simpleOptionalObjectSchema)
|
|
48
|
+
const typedRecordToken = InjectionToken.create<
|
|
49
|
+
FooService,
|
|
50
|
+
typeof simpleRecordSchema
|
|
51
|
+
>(Symbol.for('Typed record token'), simpleRecordSchema)
|
|
52
|
+
const typedOptionalRecordToken = InjectionToken.create<
|
|
53
|
+
FooService,
|
|
54
|
+
typeof simpleOptionalRecordSchema
|
|
55
|
+
>(Symbol.for('Typed optional record token'), simpleOptionalRecordSchema)
|
|
56
|
+
|
|
57
|
+
const typedToken = InjectionToken.create<FooService>(Symbol.for('Typed token'))
|
|
58
|
+
|
|
59
|
+
test('Injectable types', () => {
|
|
60
|
+
// #1
|
|
61
|
+
expectTypeOf(
|
|
62
|
+
@Injectable()
|
|
63
|
+
class {},
|
|
64
|
+
).toBeConstructibleWith()
|
|
65
|
+
// #2
|
|
66
|
+
expectTypeOf(
|
|
67
|
+
@Injectable({
|
|
68
|
+
type: InjectableType.Factory,
|
|
69
|
+
})
|
|
70
|
+
class {
|
|
71
|
+
create() {}
|
|
72
|
+
},
|
|
73
|
+
).toBeConstructibleWith()
|
|
74
|
+
expectTypeOf(
|
|
75
|
+
// @ts-expect-error should check that the class implements the factory
|
|
76
|
+
@Injectable({
|
|
77
|
+
type: InjectableType.Factory,
|
|
78
|
+
})
|
|
79
|
+
class {},
|
|
80
|
+
).toBeConstructibleWith()
|
|
81
|
+
|
|
82
|
+
// #3 required argument
|
|
83
|
+
expectTypeOf(
|
|
84
|
+
@Injectable({
|
|
85
|
+
token: typelessObjectToken,
|
|
86
|
+
})
|
|
87
|
+
class {
|
|
88
|
+
constructor(public arg: z.infer<typeof simpleObjectSchema>) {}
|
|
89
|
+
},
|
|
90
|
+
).toBeConstructibleWith({
|
|
91
|
+
foo: 'something',
|
|
92
|
+
})
|
|
93
|
+
// #3 it's required in token but optional in class allowed
|
|
94
|
+
expectTypeOf(
|
|
95
|
+
@Injectable({
|
|
96
|
+
token: typelessObjectToken,
|
|
97
|
+
})
|
|
98
|
+
class {
|
|
99
|
+
constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
|
|
100
|
+
},
|
|
101
|
+
).toBeConstructibleWith({
|
|
102
|
+
foo: 'something',
|
|
103
|
+
})
|
|
104
|
+
// #3 optional value but class accepts it
|
|
105
|
+
expectTypeOf(
|
|
106
|
+
@Injectable({
|
|
107
|
+
token: typelessOptionalObjectToken,
|
|
108
|
+
})
|
|
109
|
+
class {
|
|
110
|
+
constructor(public arg: z.infer<typeof simpleOptionalObjectSchema>) {}
|
|
111
|
+
},
|
|
112
|
+
).toBeConstructibleWith({
|
|
113
|
+
foo: 'something',
|
|
114
|
+
})
|
|
115
|
+
// #3 optional value and class accepts it
|
|
116
|
+
expectTypeOf(
|
|
117
|
+
@Injectable({
|
|
118
|
+
token: typelessOptionalObjectToken,
|
|
119
|
+
})
|
|
120
|
+
class {
|
|
121
|
+
constructor(public arg: z.infer<typeof simpleOptionalObjectSchema>) {}
|
|
122
|
+
},
|
|
123
|
+
).toBeConstructibleWith(undefined)
|
|
124
|
+
// #3 compatible schemas
|
|
125
|
+
expectTypeOf(
|
|
126
|
+
@Injectable({
|
|
127
|
+
token: typelessOptionalObjectToken,
|
|
128
|
+
})
|
|
129
|
+
class {
|
|
130
|
+
constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
|
|
131
|
+
},
|
|
132
|
+
).toBeConstructibleWith(undefined)
|
|
133
|
+
// #3 compatible schemas
|
|
134
|
+
expectTypeOf(
|
|
135
|
+
// @ts-expect-error token has optional schema, but Class has required, should fail
|
|
136
|
+
@Injectable({
|
|
137
|
+
token: typelessOptionalObjectToken,
|
|
138
|
+
})
|
|
139
|
+
class {
|
|
140
|
+
constructor(public arg: z.infer<typeof simpleObjectSchema>) {}
|
|
141
|
+
},
|
|
142
|
+
).toBeConstructibleWith({
|
|
143
|
+
foo: 'something',
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// #3 typed token and required argument
|
|
147
|
+
expectTypeOf(
|
|
148
|
+
@Injectable({
|
|
149
|
+
token: typedObjectToken,
|
|
150
|
+
})
|
|
151
|
+
class {
|
|
152
|
+
constructor(public arg: z.infer<typeof simpleObjectSchema>) {}
|
|
153
|
+
|
|
154
|
+
makeFoo() {
|
|
155
|
+
return this.arg.foo
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
).toBeConstructibleWith({
|
|
159
|
+
foo: 'something',
|
|
160
|
+
})
|
|
161
|
+
// #3 typed token and required argument
|
|
162
|
+
expectTypeOf(
|
|
163
|
+
@Injectable({
|
|
164
|
+
token: typedOptionalObjectToken,
|
|
165
|
+
})
|
|
166
|
+
class {
|
|
167
|
+
constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
|
|
168
|
+
|
|
169
|
+
makeFoo() {
|
|
170
|
+
return this.arg?.foo ?? 'default'
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
).toBeConstructibleWith({
|
|
174
|
+
foo: 'something',
|
|
175
|
+
})
|
|
176
|
+
// #3 should fail if not compatible
|
|
177
|
+
expectTypeOf(
|
|
178
|
+
// @ts-expect-error class doesn't implement the token type
|
|
179
|
+
@Injectable({
|
|
180
|
+
token: typedOptionalObjectToken,
|
|
181
|
+
})
|
|
182
|
+
class {
|
|
183
|
+
constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
|
|
184
|
+
},
|
|
185
|
+
).toBeConstructibleWith({
|
|
186
|
+
foo: 'something',
|
|
187
|
+
})
|
|
188
|
+
// #3 should fail if not compatible
|
|
189
|
+
expectTypeOf(
|
|
190
|
+
// @ts-expect-error class doesn't implement the token type
|
|
191
|
+
@Injectable({
|
|
192
|
+
token: typedOptionalObjectToken,
|
|
193
|
+
})
|
|
194
|
+
class {
|
|
195
|
+
constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
|
|
196
|
+
|
|
197
|
+
makeFoo() {
|
|
198
|
+
return this.arg?.foo
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
).toBeConstructibleWith({
|
|
202
|
+
foo: 'something',
|
|
203
|
+
})
|
|
204
|
+
// #3 typed token without schema
|
|
205
|
+
expectTypeOf(
|
|
206
|
+
@Injectable({
|
|
207
|
+
token: typedToken,
|
|
208
|
+
})
|
|
209
|
+
class {
|
|
210
|
+
constructor() {}
|
|
211
|
+
makeFoo() {
|
|
212
|
+
return 'foo'
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
).toBeConstructibleWith()
|
|
216
|
+
// #3 typed token without schema fail if not compatible
|
|
217
|
+
expectTypeOf(
|
|
218
|
+
// @ts-expect-error class doesn't implement the token type
|
|
219
|
+
@Injectable({
|
|
220
|
+
token: typedToken,
|
|
221
|
+
})
|
|
222
|
+
class {
|
|
223
|
+
constructor() {}
|
|
224
|
+
},
|
|
225
|
+
).toBeConstructibleWith()
|
|
226
|
+
|
|
227
|
+
// #4 factory with typed token
|
|
228
|
+
expectTypeOf(
|
|
229
|
+
@Injectable({
|
|
230
|
+
type: InjectableType.Factory,
|
|
231
|
+
token: typedToken,
|
|
232
|
+
})
|
|
233
|
+
class {
|
|
234
|
+
constructor() {}
|
|
235
|
+
create() {
|
|
236
|
+
return {
|
|
237
|
+
makeFoo: () => 'foo',
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
).toBeConstructibleWith()
|
|
242
|
+
// #4 factory with typed token without schema should fail if not compatible
|
|
243
|
+
expectTypeOf(
|
|
244
|
+
// @ts-expect-error factory doesn't implement the token type
|
|
245
|
+
@Injectable({
|
|
246
|
+
type: InjectableType.Factory,
|
|
247
|
+
token: typedToken,
|
|
248
|
+
})
|
|
249
|
+
class {
|
|
250
|
+
constructor() {}
|
|
251
|
+
create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
|
|
252
|
+
return {
|
|
253
|
+
makeFoo: () => 'foo',
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
).toBeConstructibleWith()
|
|
258
|
+
// #4 factory with typed token fail if not compatible
|
|
259
|
+
expectTypeOf(
|
|
260
|
+
// @ts-expect-error class doesn't implement the token type
|
|
261
|
+
@Injectable({
|
|
262
|
+
type: InjectableType.Factory,
|
|
263
|
+
token: typedToken,
|
|
264
|
+
})
|
|
265
|
+
class {
|
|
266
|
+
constructor() {}
|
|
267
|
+
create() {
|
|
268
|
+
return {
|
|
269
|
+
// makeFoo: () => 'foo',
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
).toBeConstructibleWith()
|
|
274
|
+
// #4 factory with typed token and schema
|
|
275
|
+
expectTypeOf(
|
|
276
|
+
@Injectable({
|
|
277
|
+
type: InjectableType.Factory,
|
|
278
|
+
token: typedObjectToken,
|
|
279
|
+
})
|
|
280
|
+
class {
|
|
281
|
+
constructor() {}
|
|
282
|
+
create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
|
|
283
|
+
return {
|
|
284
|
+
makeFoo: () => 'foo',
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
)
|
|
289
|
+
})
|
|
@@ -3,11 +3,15 @@ import { NaviosException } from '@navios/common'
|
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
+
BaseInjectionTokenSchemaType,
|
|
6
7
|
ClassType,
|
|
7
8
|
ClassTypeWithArgument,
|
|
8
9
|
ClassTypeWithInstance,
|
|
9
10
|
ClassTypeWithInstanceAndArgument,
|
|
11
|
+
ClassTypeWithInstanceAndOptionalArgument,
|
|
12
|
+
ClassTypeWithOptionalArgument,
|
|
10
13
|
InjectionTokenSchemaType,
|
|
14
|
+
OptionalInjectionTokenSchemaType,
|
|
11
15
|
} from '../injection-token.mjs'
|
|
12
16
|
import type { Factory, FactoryWithArgs } from '../interfaces/index.mjs'
|
|
13
17
|
import type { Registry } from '../registry.mjs'
|
|
@@ -24,11 +28,12 @@ export interface InjectableOptions {
|
|
|
24
28
|
token?: InjectionToken<any, any>
|
|
25
29
|
registry?: Registry
|
|
26
30
|
}
|
|
27
|
-
|
|
31
|
+
// #1 Simple constructorless class
|
|
28
32
|
export function Injectable(): <T extends ClassType>(
|
|
29
33
|
target: T,
|
|
30
34
|
context: ClassDecoratorContext,
|
|
31
35
|
) => T
|
|
36
|
+
// #2 Factory class without arguments
|
|
32
37
|
export function Injectable<R>(options: {
|
|
33
38
|
scope?: InjectableScope
|
|
34
39
|
type: InjectableType.Factory
|
|
@@ -36,42 +41,62 @@ export function Injectable<R>(options: {
|
|
|
36
41
|
target: T,
|
|
37
42
|
context: ClassDecoratorContext,
|
|
38
43
|
) => T
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
token: InjectionToken<undefined, S>
|
|
43
|
-
}): <T extends ClassTypeWithArgument<z.output<S>>>(
|
|
44
|
-
target: T,
|
|
45
|
-
context: ClassDecoratorContext,
|
|
46
|
-
) => T
|
|
47
|
-
export function Injectable<R, S extends InjectionTokenSchemaType>(options: {
|
|
44
|
+
|
|
45
|
+
// #3 Class with typeless token and schema
|
|
46
|
+
export function Injectable<Type, Schema>(options: {
|
|
48
47
|
scope?: InjectableScope
|
|
49
48
|
type?: InjectableType.Class
|
|
50
|
-
token: InjectionToken<
|
|
51
|
-
}):
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
token: InjectionToken<Type, Schema>
|
|
50
|
+
}): Schema extends BaseInjectionTokenSchemaType
|
|
51
|
+
? Type extends undefined
|
|
52
|
+
? <T extends ClassTypeWithArgument<z.output<Schema>>>(
|
|
53
|
+
target: T,
|
|
54
|
+
context: ClassDecoratorContext,
|
|
55
|
+
) => T
|
|
56
|
+
: <T extends ClassTypeWithInstanceAndArgument<Type, z.output<Schema>>>(
|
|
57
|
+
target: T,
|
|
58
|
+
context: ClassDecoratorContext,
|
|
59
|
+
) => T
|
|
60
|
+
: Schema extends OptionalInjectionTokenSchemaType
|
|
61
|
+
? Type extends undefined
|
|
62
|
+
? <T extends ClassTypeWithOptionalArgument<z.output<Schema>>>(
|
|
63
|
+
target: T,
|
|
64
|
+
context: ClassDecoratorContext,
|
|
65
|
+
) => T
|
|
66
|
+
: <
|
|
67
|
+
T extends ClassTypeWithInstanceAndOptionalArgument<
|
|
68
|
+
Type,
|
|
69
|
+
z.output<Schema>
|
|
70
|
+
>,
|
|
71
|
+
>(
|
|
72
|
+
target: T,
|
|
73
|
+
context: ClassDecoratorContext,
|
|
74
|
+
) => T
|
|
75
|
+
: Schema extends undefined
|
|
76
|
+
? <R extends ClassTypeWithInstance<Type>>(
|
|
77
|
+
target: R,
|
|
78
|
+
context: ClassDecoratorContext,
|
|
79
|
+
) => R
|
|
80
|
+
: never
|
|
81
|
+
|
|
82
|
+
// #4 Factory with typed token
|
|
83
|
+
export function Injectable<R, S>(options: {
|
|
60
84
|
scope?: InjectableScope
|
|
61
85
|
type: InjectableType.Factory
|
|
62
86
|
token: InjectionToken<R, S>
|
|
63
|
-
}):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
) => T
|
|
87
|
+
}): R extends undefined
|
|
88
|
+
? never
|
|
89
|
+
: S extends InjectionTokenSchemaType
|
|
90
|
+
? <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(
|
|
91
|
+
target: T,
|
|
92
|
+
context: ClassDecoratorContext,
|
|
93
|
+
) => T
|
|
94
|
+
: S extends undefined
|
|
95
|
+
? <T extends ClassTypeWithInstance<Factory<R>>>(
|
|
96
|
+
target: T,
|
|
97
|
+
context: ClassDecoratorContext,
|
|
98
|
+
) => T
|
|
99
|
+
: never
|
|
75
100
|
export function Injectable({
|
|
76
101
|
scope = InjectableScope.Singleton,
|
|
77
102
|
type = InjectableType.Class,
|
package/src/injection-token.mts
CHANGED
|
@@ -6,9 +6,13 @@ import { z, ZodOptional, ZodRecord } from 'zod'
|
|
|
6
6
|
|
|
7
7
|
export type ClassType = new (...args: any[]) => any
|
|
8
8
|
export type ClassTypeWithArgument<Arg> = new (arg: Arg) => any
|
|
9
|
+
export type ClassTypeWithOptionalArgument<Arg> = new (arg?: Arg) => any
|
|
9
10
|
|
|
10
11
|
export type ClassTypeWithInstance<T> = new (...args: any[]) => T
|
|
11
12
|
export type ClassTypeWithInstanceAndArgument<T, Arg> = new (arg: Arg) => T
|
|
13
|
+
export type ClassTypeWithInstanceAndOptionalArgument<T, Arg> = new (
|
|
14
|
+
arg?: Arg,
|
|
15
|
+
) => T
|
|
12
16
|
|
|
13
17
|
export type BaseInjectionTokenSchemaType = AnyZodObject | ZodRecord
|
|
14
18
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import type { FactoryContext } from '../factory-context.mjs'
|
|
3
4
|
import type { InjectionTokenSchemaType } from '../injection-token.mjs'
|
|
4
5
|
|
|
5
6
|
export interface Factory<T> {
|
|
6
|
-
create(ctx?:
|
|
7
|
+
create(ctx?: FactoryContext): Promise<T> | T
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export interface FactoryWithArgs<T, A extends InjectionTokenSchemaType> {
|
|
10
|
-
create(
|
|
11
|
+
create(...args: [FactoryContext, z.output<A>]): Promise<T> | T
|
|
11
12
|
}
|