@nextrush/di 3.0.7 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,145 +1,13 @@
1
+ import { Container, ServiceOptions, Constructor, Scope } from '@nextrush/types';
2
+ export { ClassProvider, Constructor, Container, FactoryProvider, Provider, RegisterOptions, Scope, ServiceOptions, Token, ValueProvider } from '@nextrush/types';
3
+
1
4
  /**
2
5
  * @nextrush/di - Types
3
6
  *
4
- * Type definitions for the dependency injection container.
5
- */
6
- /**
7
- * Token used to identify a dependency in the container.
8
- * Can be a class constructor, string, or symbol.
9
- */
10
- type Token<T = unknown> = Constructor<T> | string | symbol;
11
- /**
12
- * Constructor type for class-based tokens.
13
- */
14
- type Constructor<T = unknown> = new (...args: unknown[]) => T;
15
- /**
16
- * Provider that uses a class constructor.
17
- */
18
- interface ClassProvider<T> {
19
- useClass: Constructor<T>;
20
- }
21
- /**
22
- * Provider that uses a factory function.
23
- *
24
- * Without `inject`: factory receives the container.
25
- * With `inject`: factory receives resolved dependencies in order.
26
- *
27
- * @example Without inject
28
- * ```typescript
29
- * container.register('DB', {
30
- * useFactory: (c) => new Database(c.resolve(ConfigService).dbUrl),
31
- * });
32
- * ```
33
- *
34
- * @example With inject
35
- * ```typescript
36
- * container.register('DB', {
37
- * useFactory: (config: ConfigService) => new Database(config.dbUrl),
38
- * inject: [ConfigService],
39
- * });
40
- * ```
41
- *
42
- * @example Async factory (auto-resolved by bootstrap)
43
- * ```typescript
44
- * container.register('DB', {
45
- * useFactory: async (config: ConfigService) => {
46
- * const db = new Database(config.dbUrl);
47
- * await db.connect();
48
- * return db;
49
- * },
50
- * inject: [ConfigService],
51
- * });
52
- * // Controllers plugin calls bootstrap() automatically.
53
- * // For functional mode: await container.bootstrap()
54
- * ```
55
- */
56
- interface FactoryProvider<T> {
57
- useFactory: (...args: unknown[]) => T | Promise<T>;
58
- inject?: Token[];
59
- }
60
- /**
61
- * Provider that uses a constant value.
62
- */
63
- interface ValueProvider<T> {
64
- useValue: T;
65
- }
66
- /**
67
- * Union of all provider types.
68
- */
69
- type Provider<T> = ClassProvider<T> | FactoryProvider<T> | ValueProvider<T>;
70
- /**
71
- * Lifecycle scope for registered services.
7
+ * The container contract now lives in `@nextrush/types` (shared, lowest
8
+ * package). This module re-exports it and adds the DI-specific metadata types.
72
9
  */
73
- type Scope = 'singleton' | 'transient';
74
- /**
75
- * Options for service registration.
76
- */
77
- interface ServiceOptions {
78
- scope?: Scope;
79
- }
80
- /**
81
- * Container interface for dependency injection operations.
82
- */
83
- interface ContainerInterface {
84
- /**
85
- * Register a dependency with the container.
86
- *
87
- * @param token - The token to register
88
- * @param provider - The provider (class, value, or factory)
89
- * @param options - Optional registration options
90
- */
91
- register<T>(token: Token<T>, provider: Provider<T>, options?: RegisterOptions): void;
92
- /**
93
- * Resolve a dependency from the container (synchronous).
94
- */
95
- resolve<T>(token: Token<T>): T;
96
- /**
97
- * Resolve a dependency that may have been registered with an async factory.
98
- * Awaits the result if the factory returns a Promise.
99
- *
100
- * Prefer `bootstrap()` over calling this for each async provider.
101
- */
102
- resolveAsync<T>(token: Token<T>): Promise<T>;
103
- /**
104
- * Bootstrap all factory providers.
105
- *
106
- * Resolves every factory-registered provider, awaiting any Promises,
107
- * and caches results for synchronous access via `resolve()`.
108
- *
109
- * Called automatically by the controllers plugin during `install()`.
110
- * For functional mode, call once before handling requests.
111
- *
112
- * @example
113
- * ```typescript
114
- * container.register('DB', {
115
- * useFactory: async () => { const db = new Database(); await db.connect(); return db; },
116
- * });
117
- * await container.bootstrap(); // All async providers resolved
118
- * container.resolve('DB'); // Works synchronously now
119
- * ```
120
- */
121
- bootstrap(): Promise<void>;
122
- /**
123
- * Resolve all dependencies registered under a token.
124
- */
125
- resolveAll<T>(token: Token<T>): T[];
126
- /**
127
- * Check if a token is registered.
128
- */
129
- isRegistered<T>(token: Token<T>): boolean;
130
- /**
131
- * Clear all registered instances (useful for testing).
132
- */
133
- clearInstances(): void;
134
- /**
135
- * Reset the container completely (useful for testing).
136
- */
137
- reset(): void;
138
- /**
139
- * Create a child container with isolated scope.
140
- */
141
- createChild(): ContainerInterface;
142
- }
10
+
143
11
  /**
144
12
  * Metadata key constants.
145
13
  */
@@ -158,8 +26,6 @@ interface ConfigOptions {
158
26
  /**
159
27
  * Environment variable prefix.
160
28
  *
161
- * When set, the configuration class can be used with prefix-based env loading.
162
- *
163
29
  * @example
164
30
  * ```typescript
165
31
  * @Config({ prefix: 'DB' })
@@ -171,13 +37,6 @@ interface ConfigOptions {
171
37
  */
172
38
  prefix?: string;
173
39
  }
174
- /**
175
- * Registration options for container.register().
176
- */
177
- interface RegisterOptions {
178
- /** Lifecycle scope — defaults to 'transient' if not specified */
179
- scope?: Scope;
180
- }
181
40
 
182
41
  /**
183
42
  * @nextrush/di - Container
@@ -199,10 +58,10 @@ interface RegisterOptions {
199
58
  * const userService = container.resolve(UserService);
200
59
  *
201
60
  * // Register with value
202
- * container.register('CONFIG', { useValue: { port: 3000 } });
61
+ * container.register('CONFIG', { useValue: { port: 8080 } });
203
62
  * ```
204
63
  */
205
- declare const container: ContainerInterface;
64
+ declare const container: Container;
206
65
  /**
207
66
  * Create a new isolated container.
208
67
  *
@@ -216,10 +75,10 @@ declare const container: ContainerInterface;
216
75
  * testContainer.register(UserService, { useClass: MockUserService });
217
76
  * ```
218
77
  */
219
- declare function createContainer(): ContainerInterface;
78
+ declare function createContainer(): Container;
220
79
 
221
80
  /** Decorator-compatible constructor type. Uses `never[]` so any class is structurally assignable. */
222
- type DecoratorTarget = new (...args: never[]) => unknown;
81
+ type DecoratorTarget$1 = new (...args: never[]) => unknown;
223
82
  /**
224
83
  * Mark a class as injectable service.
225
84
  *
@@ -245,7 +104,7 @@ type DecoratorTarget = new (...args: never[]) => unknown;
245
104
  * }
246
105
  * ```
247
106
  */
248
- declare function Service(options?: ServiceOptions): (target: DecoratorTarget) => void;
107
+ declare function Service(options?: ServiceOptions): (target: DecoratorTarget$1) => void;
249
108
  /**
250
109
  * Mark a class as a repository (data access layer).
251
110
  *
@@ -264,7 +123,54 @@ declare function Service(options?: ServiceOptions): (target: DecoratorTarget) =>
264
123
  * }
265
124
  * ```
266
125
  */
267
- declare function Repository(options?: ServiceOptions): (target: DecoratorTarget) => void;
126
+ declare function Repository(options?: ServiceOptions): (target: DecoratorTarget$1) => void;
127
+ /**
128
+ * Mark a class as a configuration holder.
129
+ *
130
+ * Configuration classes are always singletons and serve as centralized
131
+ * configuration containers for your application. Similar to Spring Boot's
132
+ * `@Configuration` / `@ConfigurationProperties`.
133
+ *
134
+ * @param options - Optional configuration (e.g., env prefix)
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // Simple configuration class
139
+ * @Config()
140
+ * export class AppConfig {
141
+ * readonly port = Number(process.env.PORT ?? 8080);
142
+ * readonly host = process.env.HOST ?? 'localhost';
143
+ * }
144
+ *
145
+ * // With env prefix — documents that this config reads DB_* vars
146
+ * @Config({ prefix: 'DB' })
147
+ * export class DatabaseConfig {
148
+ * readonly host = process.env.DB_HOST ?? 'localhost';
149
+ * readonly port = Number(process.env.DB_PORT ?? 5432);
150
+ * readonly name = process.env.DB_NAME ?? 'mydb';
151
+ * }
152
+ *
153
+ * // Inject into services
154
+ * @Service()
155
+ * export class UserService {
156
+ * constructor(private config: DatabaseConfig) {}
157
+ *
158
+ * getConnectionString() {
159
+ * return `postgres://${this.config.host}:${this.config.port}/${this.config.name}`;
160
+ * }
161
+ * }
162
+ * ```
163
+ */
164
+ declare function Config(options?: ConfigOptions): (target: DecoratorTarget$1) => void;
165
+
166
+ /**
167
+ * @nextrush/di - Injection Decorators & Adapters
168
+ *
169
+ * Parameter decorators for injection, and adapters to tsyringe's internals.
170
+ */
171
+
172
+ /** Decorator-compatible constructor type. Uses `never[]` so any class is structurally assignable. */
173
+ type DecoratorTarget = new (...args: never[]) => unknown;
268
174
  /**
269
175
  * Explicitly inject a dependency by token.
270
176
  *
@@ -290,40 +196,6 @@ declare function Repository(options?: ServiceOptions): (target: DecoratorTarget)
290
196
  * ```
291
197
  */
292
198
  declare function inject(token: unknown): ParameterDecorator;
293
- /**
294
- * Make a class injectable without singleton scope.
295
- *
296
- * Unlike @Service() which defaults to singleton, @Injectable() creates
297
- * a transient registration that can be resolved from the container.
298
- *
299
- * @example
300
- * ```typescript
301
- * @Injectable()
302
- * export class FeatureService {
303
- * constructor(private logger: Logger) {}
304
- * }
305
- *
306
- * // Resolve from container
307
- * const service = container.resolve(FeatureService);
308
- * ```
309
- */
310
- declare function Injectable(): (target: DecoratorTarget) => void;
311
- /**
312
- * @deprecated Use `@Injectable()` instead. Will be removed in v4.
313
- */
314
- declare const AutoInjectable: typeof Injectable;
315
- /**
316
- * Make a class resolvable by the DI container without adding service metadata.
317
- *
318
- * Used internally by `@Controller()` to enable constructor injection
319
- * without marking the class as a service. This is the abstraction boundary
320
- * that prevents `@nextrush/decorators` from depending directly on tsyringe.
321
- *
322
- * @param target - The class constructor to make injectable
323
- *
324
- * @internal
325
- */
326
- declare function markInjectable(target: Constructor): void;
327
199
  /**
328
200
  * Delay resolution of a dependency.
329
201
  *
@@ -350,71 +222,35 @@ declare function markInjectable(target: Constructor): void;
350
222
  */
351
223
  declare function delay(tokenFactory: () => Constructor): unknown;
352
224
  /**
353
- * Check if a class has DI metadata (is decorated with @Service or @Repository).
354
- *
355
- * @param target - The class to check
356
- * @returns True if the class has DI metadata
357
- */
358
- declare function hasServiceMetadata(target: object): boolean;
359
- /**
360
- * Get the service type from a decorated class.
361
- *
362
- * @param target - The class to check
363
- * @returns The service type ('service' | 'repository') or undefined
364
- */
365
- declare function getServiceType(target: object): string | undefined;
366
- /**
367
- * Get the scope from a decorated class.
368
- *
369
- * @param target - The class to check
370
- * @returns The scope ('singleton' | 'transient') or undefined
371
- */
372
- declare function getServiceScope(target: object): Scope | undefined;
373
- /**
374
- * Mark a class as a configuration holder.
375
- *
376
- * Configuration classes are always singletons and serve as centralized
377
- * configuration containers for your application. Similar to Spring Boot's
378
- * `@Configuration` / `@ConfigurationProperties`.
225
+ * Make a class injectable without singleton scope.
379
226
  *
380
- * @param options - Optional configuration (e.g., env prefix)
227
+ * Unlike @Service() which defaults to singleton, @Injectable() creates
228
+ * a transient registration that can be resolved from the container.
381
229
  *
382
230
  * @example
383
231
  * ```typescript
384
- * // Simple configuration class
385
- * @Config()
386
- * export class AppConfig {
387
- * readonly port = Number(process.env.PORT ?? 3000);
388
- * readonly host = process.env.HOST ?? 'localhost';
389
- * }
390
- *
391
- * // With env prefix — documents that this config reads DB_* vars
392
- * @Config({ prefix: 'DB' })
393
- * export class DatabaseConfig {
394
- * readonly host = process.env.DB_HOST ?? 'localhost';
395
- * readonly port = Number(process.env.DB_PORT ?? 5432);
396
- * readonly name = process.env.DB_NAME ?? 'mydb';
232
+ * @Injectable()
233
+ * export class FeatureService {
234
+ * constructor(private logger: Logger) {}
397
235
  * }
398
236
  *
399
- * // Inject into services
400
- * @Service()
401
- * export class UserService {
402
- * constructor(private config: DatabaseConfig) {}
403
- *
404
- * getConnectionString() {
405
- * return `postgres://${this.config.host}:${this.config.port}/${this.config.name}`;
406
- * }
407
- * }
237
+ * // Resolve from container
238
+ * const service = container.resolve(FeatureService);
408
239
  * ```
409
240
  */
410
- declare function Config(options?: ConfigOptions): (target: DecoratorTarget) => void;
241
+ declare function Injectable(): (target: DecoratorTarget) => void;
411
242
  /**
412
- * Get the config prefix from a @Config-decorated class.
243
+ * Make a class resolvable by the DI container without adding service metadata.
413
244
  *
414
- * @param target - The class to check
415
- * @returns The prefix string or undefined
245
+ * Used internally by `@Controller()` to enable constructor injection
246
+ * without marking the class as a service. This is the abstraction boundary
247
+ * that prevents `@nextrush/decorators` from depending directly on tsyringe.
248
+ *
249
+ * @param target - The class constructor to make injectable
250
+ *
251
+ * @internal
416
252
  */
417
- declare function getConfigPrefix(target: object): string | undefined;
253
+ declare function markInjectable(target: Constructor): void;
418
254
  /**
419
255
  * Mark a constructor parameter as optional.
420
256
  *
@@ -450,6 +286,47 @@ declare function isParameterOptional(target: object, parameterIndex: number): bo
450
286
  */
451
287
  declare function getOptionalParams(target: object): ReadonlySet<number>;
452
288
 
289
+ /**
290
+ * @nextrush/di - Service Metadata Readers
291
+ *
292
+ * Functions to read service metadata written by decorators.
293
+ */
294
+
295
+ /**
296
+ * Check if a class has DI metadata (is decorated with @Service or @Repository).
297
+ *
298
+ * @param target - The class to check
299
+ * @returns True if the class has DI metadata
300
+ */
301
+ declare function hasServiceMetadata(target: object): boolean;
302
+ /**
303
+ * Get the service type from a decorated class.
304
+ *
305
+ * @param target - The class to check
306
+ * @returns The service type ('service' | 'repository') or undefined
307
+ */
308
+ declare function getServiceType(target: object): string | undefined;
309
+ /**
310
+ * Get the declared scope of a class — the single source of truth for service scope.
311
+ *
312
+ * Scope defaults are unified on `'singleton'`: `@Service()`, `@Repository()`, and
313
+ * `@Config()` all declare `singleton` unless `{ scope: 'transient' }` is passed, and an
314
+ * undecorated class (no `di:scope` metadata) is treated as `singleton` too. The
315
+ * low-level {@link Container.register} reads this value so a class's declared scope —
316
+ * not the call site — decides singleton vs transient.
317
+ *
318
+ * @param target - The class to check
319
+ * @returns The declared scope (`'singleton'` | `'transient'`); `'singleton'` when undeclared
320
+ */
321
+ declare function getServiceScope(target: object): Scope;
322
+ /**
323
+ * Get the config prefix from a @Config-decorated class.
324
+ *
325
+ * @param target - The class to check
326
+ * @returns The prefix string or undefined
327
+ */
328
+ declare function getConfigPrefix(target: object): string | undefined;
329
+
453
330
  /**
454
331
  * @nextrush/di - Errors
455
332
  *
@@ -480,16 +357,6 @@ declare class CircularDependencyError extends DIError {
480
357
  readonly cycle: string[];
481
358
  constructor(cycle: string[]);
482
359
  }
483
- /**
484
- * Error thrown when TypeScript cannot infer constructor parameter types.
485
- *
486
- * Usually caused by missing type annotations or incorrect tsconfig.
487
- */
488
- declare class TypeInferenceError extends DIError {
489
- readonly className: string;
490
- readonly parameterIndex: number;
491
- constructor(className: string, parameterIndex: number);
492
- }
493
360
  /**
494
361
  * Error thrown when an invalid provider configuration is given.
495
362
  */
@@ -497,21 +364,5 @@ declare class InvalidProviderError extends DIError {
497
364
  readonly token: string;
498
365
  constructor(token: string);
499
366
  }
500
- /**
501
- * Error thrown when trying to resolve from a disposed container.
502
- */
503
- declare class ContainerDisposedError extends DIError {
504
- constructor();
505
- }
506
- /**
507
- * Error thrown when a required dependency is not found.
508
- *
509
- * @deprecated Use {@link DependencyResolutionError} instead — it provides
510
- * the same information plus the resolution chain context.
511
- */
512
- declare class MissingDependencyError extends DIError {
513
- readonly token: string;
514
- constructor(token: string);
515
- }
516
367
 
517
- export { AutoInjectable, CircularDependencyError, type ClassProvider, Config, type ConfigOptions, type Constructor, ContainerDisposedError, type ContainerInterface, DIError, DependencyResolutionError, type FactoryProvider, Injectable, InvalidProviderError, METADATA_KEYS, MissingDependencyError, Optional, type Provider, type RegisterOptions, Repository, type Scope, Service, type ServiceOptions, type Token, TypeInferenceError, type ValueProvider, container, createContainer, delay, getConfigPrefix, getOptionalParams, getServiceScope, getServiceType, hasServiceMetadata, inject, isParameterOptional, markInjectable };
368
+ export { CircularDependencyError, Config, type ConfigOptions, DIError, DependencyResolutionError, Injectable, InvalidProviderError, METADATA_KEYS, Optional, Repository, Service, container, createContainer, delay, getConfigPrefix, getOptionalParams, getServiceScope, getServiceType, hasServiceMetadata, inject, isParameterOptional, markInjectable };