@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 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?: any): Promise<T> | T;
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(ctx: any, args: z.output<A>): Promise<T> | T;
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<S extends InjectionTokenSchemaType>(options: {
203
+ declare function Injectable<Type, Schema>(options: {
196
204
  scope?: InjectableScope;
197
205
  type?: InjectableType.Class;
198
- token: InjectionToken<undefined, S>;
199
- }): <T extends ClassTypeWithArgument<z.output<S>>>(target: T, context: ClassDecoratorContext) => T;
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 extends InjectionTokenSchemaType>(options: {
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?: any): Promise<T> | T;
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(ctx: any, args: z.output<A>): Promise<T> | T;
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<S extends InjectionTokenSchemaType>(options: {
203
+ declare function Injectable<Type, Schema>(options: {
196
204
  scope?: InjectableScope;
197
205
  type?: InjectableType.Class;
198
- token: InjectionToken<undefined, S>;
199
- }): <T extends ClassTypeWithArgument<z.output<S>>>(target: T, context: ClassDecoratorContext) => T;
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 extends InjectionTokenSchemaType>(options: {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navios/di",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "author": {
5
5
  "name": "Oleksandr Hanzha",
6
6
  "email": "alex@granted.name"
@@ -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
- export function Injectable<S extends InjectionTokenSchemaType>(options: {
40
- scope?: InjectableScope
41
- type?: InjectableType.Class
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<R, S>
51
- }): <T extends ClassTypeWithInstanceAndArgument<R, z.output<S>>>(
52
- target: T,
53
- context: ClassDecoratorContext,
54
- ) => T
55
- export function Injectable<T extends ClassType>(options: {
56
- scope?: InjectableScope
57
- token: InjectionToken<T, undefined>
58
- }): (target: T, context: ClassDecoratorContext) => T
59
- export function Injectable<R, S extends InjectionTokenSchemaType>(options: {
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
- }): <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>(
64
- target: T,
65
- context: ClassDecoratorContext,
66
- ) => T
67
- export function Injectable<R>(options: {
68
- scope?: InjectableScope
69
- type: InjectableType.Factory
70
- token: InjectionToken<R, undefined>
71
- }): <T extends ClassTypeWithInstance<Factory<R>>>(
72
- target: T,
73
- context: ClassDecoratorContext,
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,
@@ -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?: any): Promise<T> | T
7
+ create(ctx?: FactoryContext): Promise<T> | T
7
8
  }
8
9
 
9
10
  export interface FactoryWithArgs<T, A extends InjectionTokenSchemaType> {
10
- create(ctx: any, args: z.output<A>): Promise<T> | T
11
+ create(...args: [FactoryContext, z.output<A>]): Promise<T> | T
11
12
  }