ts-ioc-container 46.6.2 → 46.7.1

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
@@ -28,9 +28,11 @@
28
28
  - [Container](#container)
29
29
  - [Basic usage](#basic-usage)
30
30
  - [Scope](#scope) `tags`
31
+ - [Dynamic Tag Management](#dynamic-tag-management) `addTags`
31
32
  - [Instances](#instances)
32
33
  - [Dispose](#dispose)
33
34
  - [Lazy](#lazy) `lazy`
35
+ - [Lazy with registerPipe](#lazy-with-registerpipe) `lazy()`
34
36
  - [Injector](#injector)
35
37
  - [Metadata](#metadata) `@inject`
36
38
  - [Simple](#simple)
@@ -279,7 +281,6 @@ import {
279
281
  */
280
282
 
281
283
  // SessionService is only available in request scope - not at application level
282
- // This prevents accidental access to request-specific data from singletons
283
284
  @register(bindTo('ISessionService'), scope((s) => s.hasTag('request')), singleton())
284
285
  class SessionService {
285
286
  private userId: string | null = null;
@@ -342,6 +343,113 @@ describe('Scopes', function () {
342
343
 
343
344
  ```
344
345
 
346
+ ### Dynamic Tag Management
347
+ You can dynamically add tags to a container after it's been created using the `addTags()` method. This is useful for environment-based configuration, feature flags, and progressive container setup.
348
+
349
+ - Tags can be added one at a time or multiple at once
350
+ - Tags must be added **before** registrations are applied - scope matching happens at registration time
351
+ - Useful for conditional configuration based on `NODE_ENV` or runtime flags
352
+ - Container can be configured incrementally as the application initializes
353
+
354
+ ```typescript
355
+ import { bindTo, Container, register, Registration as R, scope } from 'ts-ioc-container';
356
+
357
+ describe('addTags', () => {
358
+ it('should dynamically add tags to enable environment-based registration', () => {
359
+ @register(bindTo('logger'), scope((s) => s.hasTag('development')))
360
+ class ConsoleLogger {
361
+ log(message: string) {
362
+ console.log(`[DEV] ${message}`);
363
+ }
364
+ }
365
+
366
+ @register(bindTo('logger'), scope((s) => s.hasTag('production')))
367
+ class FileLogger {
368
+ log(message: string) {
369
+ console.log(`[PROD] ${message}`);
370
+ }
371
+ }
372
+
373
+ // Create container and configure for environment
374
+ const container = new Container();
375
+ const environment = 'development';
376
+ container.addTags(environment); // Add tag dynamically based on environment
377
+
378
+ // Register services after tag is set
379
+ container.addRegistration(R.fromClass(ConsoleLogger)).addRegistration(R.fromClass(FileLogger));
380
+
381
+ // Resolve logger - gets ConsoleLogger because 'development' tag was added
382
+ const logger = container.resolve<ConsoleLogger>('logger');
383
+ expect(logger).toBeInstanceOf(ConsoleLogger);
384
+ });
385
+
386
+ it('should add multiple tags for feature-based configuration', () => {
387
+ @register(bindTo('premiumFeature'), scope((s) => s.hasTag('premium')))
388
+ class PremiumFeature {}
389
+
390
+ @register(bindTo('betaFeature'), scope((s) => s.hasTag('beta')))
391
+ class BetaFeature {}
392
+
393
+ const container = new Container();
394
+
395
+ // Add multiple tags at once
396
+ container.addTags('premium', 'beta', 'experimental');
397
+
398
+ // Verify all tags are present
399
+ expect(container.hasTag('premium')).toBe(true);
400
+ expect(container.hasTag('beta')).toBe(true);
401
+ expect(container.hasTag('experimental')).toBe(true);
402
+
403
+ // Register features after tags are added
404
+ container.addRegistration(R.fromClass(PremiumFeature)).addRegistration(R.fromClass(BetaFeature));
405
+
406
+ // Both features are available because container has both tags
407
+ expect(container.resolve('premiumFeature')).toBeInstanceOf(PremiumFeature);
408
+ expect(container.resolve('betaFeature')).toBeInstanceOf(BetaFeature);
409
+ });
410
+
411
+ it('should affect child scope creation', () => {
412
+ @register(bindTo('service'), scope((s) => s.hasTag('api')))
413
+ class ApiService {
414
+ handleRequest() {
415
+ return 'API response';
416
+ }
417
+ }
418
+
419
+ const appContainer = new Container();
420
+
421
+ // Add tag to parent
422
+ appContainer.addTags('api');
423
+ appContainer.addRegistration(R.fromClass(ApiService));
424
+
425
+ // Create child scopes - they inherit parent's registrations
426
+ const requestScope1 = appContainer.createScope({ tags: ['request'] });
427
+ const requestScope2 = appContainer.createScope({ tags: ['request'] });
428
+
429
+ // Both scopes can access the ApiService from parent
430
+ expect(requestScope1.resolve<ApiService>('service').handleRequest()).toBe('API response');
431
+ expect(requestScope2.resolve<ApiService>('service').handleRequest()).toBe('API response');
432
+ });
433
+
434
+ it('should enable incremental tag addition', () => {
435
+ const container = new Container();
436
+
437
+ // Start with basic tags
438
+ container.addTags('application');
439
+ expect(container.hasTag('application')).toBe(true);
440
+
441
+ // Add more tags as needed
442
+ container.addTags('monitoring', 'logging');
443
+ expect(container.hasTag('monitoring')).toBe(true);
444
+ expect(container.hasTag('logging')).toBe(true);
445
+
446
+ // All tags are retained
447
+ expect(container.hasTag('application')).toBe(true);
448
+ });
449
+ });
450
+
451
+ ```
452
+
345
453
  ### Instances
346
454
  Sometimes you want to get all instances from container and its scopes. For example, when you want to dispose all instances of container.
347
455
 
@@ -659,6 +767,453 @@ describe('lazy provider', () => {
659
767
 
660
768
  ```
661
769
 
770
+ ### Lazy with registerPipe
771
+ The `lazy()` registerPipe can be used in two ways: with the `@register` decorator or directly on the `Provider` pipe. This allows you to defer expensive service initialization until first access.
772
+
773
+ **Use cases:**
774
+ - Defer expensive initialization (database connections, SMTP, external APIs)
775
+ - Conditional features that may not be used
776
+ - Breaking circular dependencies
777
+ - Memory optimization for optional services
778
+
779
+ **Two approaches:**
780
+
781
+ 1. **With @register decorator**: Use `lazy()` as a registerPipe in the decorator
782
+ 2. **With Provider pipe**: Use `Provider.fromClass().pipe(lazy())` directly
783
+
784
+ ```typescript
785
+ import 'reflect-metadata';
786
+ import { bindTo, Container, inject, lazy, Provider, register, Registration as R, singleton } from 'ts-ioc-container';
787
+
788
+ /**
789
+ * Lazy Loading with registerPipe
790
+ *
791
+ * The lazy() registerPipe can be used in two ways:
792
+ * 1. With @register decorator - lazy()
793
+ * 2. Directly on provider - provider.lazy()
794
+ *
795
+ * Both approaches defer instantiation until first access,
796
+ * improving startup time and memory usage.
797
+ */
798
+ describe('lazy registerPipe', () => {
799
+ // Track initialization for testing
800
+ const initLog: string[] = [];
801
+
802
+ beforeEach(() => {
803
+ initLog.length = 0;
804
+ });
805
+
806
+ /**
807
+ * Example 1: Using lazy() with @register decorator
808
+ *
809
+ * The lazy() registerPipe defers service instantiation until first use.
810
+ * Perfect for expensive services that may not always be needed.
811
+ */
812
+ describe('with @register decorator', () => {
813
+ // Database connection pool - expensive to initialize
814
+ @register(bindTo('DatabasePool'), singleton())
815
+ class DatabasePool {
816
+ constructor() {
817
+ initLog.push('DatabasePool initialized');
818
+ }
819
+
820
+ query(sql: string): string[] {
821
+ return [`Results for: ${sql}`];
822
+ }
823
+ }
824
+
825
+ // Analytics service - expensive, but only used occasionally
826
+ @register(bindTo('AnalyticsService'), lazy(), singleton())
827
+ class AnalyticsService {
828
+ constructor(@inject('DatabasePool') private db: DatabasePool) {
829
+ initLog.push('AnalyticsService initialized');
830
+ }
831
+
832
+ trackEvent(event: string): void {
833
+ this.db.query(`INSERT INTO events VALUES ('${event}')`);
834
+ }
835
+
836
+ generateReport(): string {
837
+ return 'Analytics Report';
838
+ }
839
+ }
840
+
841
+ // Application service - always used
842
+ class AppService {
843
+ constructor(@inject('AnalyticsService') public analytics: AnalyticsService) {
844
+ initLog.push('AppService initialized');
845
+ }
846
+
847
+ handleRequest(path: string): void {
848
+ // Most requests don't need analytics
849
+ if (path.includes('/admin')) {
850
+ // Only admin requests use analytics
851
+ this.analytics.trackEvent(`Admin access: ${path}`);
852
+ }
853
+ }
854
+ }
855
+
856
+ it('should defer AnalyticsService initialization until first access', () => {
857
+ const container = new Container()
858
+ .addRegistration(R.fromClass(DatabasePool))
859
+ .addRegistration(R.fromClass(AnalyticsService))
860
+ .addRegistration(R.fromClass(AppService));
861
+
862
+ // Resolve AppService
863
+ const app = container.resolve<AppService>(AppService);
864
+
865
+ // AppService is initialized, but AnalyticsService is NOT (it's lazy)
866
+ // DatabasePool is also not initialized because AnalyticsService hasn't been accessed
867
+ expect(initLog).toEqual(['AppService initialized']);
868
+
869
+ // Handle non-admin request - analytics not used
870
+ app.handleRequest('/api/users');
871
+ expect(initLog).toEqual(['AppService initialized']);
872
+ });
873
+
874
+ it('should initialize lazy service when first accessed', () => {
875
+ const container = new Container()
876
+ .addRegistration(R.fromClass(DatabasePool))
877
+ .addRegistration(R.fromClass(AnalyticsService))
878
+ .addRegistration(R.fromClass(AppService));
879
+
880
+ const app = container.resolve<AppService>(AppService);
881
+
882
+ // Handle admin request - now analytics IS used
883
+ app.handleRequest('/admin/dashboard');
884
+
885
+ // AnalyticsService was initialized on first access (DatabasePool too, as a dependency)
886
+ expect(initLog).toEqual(['AppService initialized', 'DatabasePool initialized', 'AnalyticsService initialized']);
887
+ });
888
+
889
+ it('should create only one instance even with multiple accesses', () => {
890
+ const container = new Container()
891
+ .addRegistration(R.fromClass(DatabasePool))
892
+ .addRegistration(R.fromClass(AnalyticsService))
893
+ .addRegistration(R.fromClass(AppService));
894
+
895
+ const app = container.resolve<AppService>(AppService);
896
+
897
+ // Access analytics multiple times
898
+ app.handleRequest('/admin/dashboard');
899
+ app.analytics.generateReport();
900
+ app.analytics.trackEvent('test');
901
+
902
+ // AnalyticsService initialized only once (singleton + lazy)
903
+ const analyticsCount = initLog.filter((msg) => msg === 'AnalyticsService initialized').length;
904
+ expect(analyticsCount).toBe(1);
905
+ });
906
+ });
907
+
908
+ /**
909
+ * Example 2: Using lazy() directly on provider
910
+ *
911
+ * For manual registration, call .lazy() on the provider pipe.
912
+ * This gives fine-grained control over lazy loading per dependency.
913
+ */
914
+ describe('with pure provider', () => {
915
+ // Email service - expensive SMTP connection
916
+ class EmailService {
917
+ constructor() {
918
+ initLog.push('EmailService initialized - SMTP connected');
919
+ }
920
+
921
+ send(to: string, subject: string): string {
922
+ return `Email sent to ${to}: ${subject}`;
923
+ }
924
+ }
925
+
926
+ // SMS service - expensive gateway connection
927
+ class SmsService {
928
+ constructor() {
929
+ initLog.push('SmsService initialized - Gateway connected');
930
+ }
931
+
932
+ send(to: string, message: string): string {
933
+ return `SMS sent to ${to}: ${message}`;
934
+ }
935
+ }
936
+
937
+ // Notification service - uses email and SMS, but maybe not both
938
+ class NotificationService {
939
+ constructor(
940
+ @inject('EmailService') public email: EmailService,
941
+ @inject('SmsService') public sms: SmsService,
942
+ ) {
943
+ initLog.push('NotificationService initialized');
944
+ }
945
+
946
+ notifyByEmail(user: string, message: string): string {
947
+ return this.email.send(user, message);
948
+ }
949
+
950
+ notifyBySms(phone: string, message: string): string {
951
+ return this.sms.send(phone, message);
952
+ }
953
+ }
954
+
955
+ it('should allow selective lazy loading - email lazy, SMS eager', () => {
956
+ const container = new Container()
957
+ // EmailService is lazy - won't connect to SMTP until used
958
+ .addRegistration(
959
+ R.fromClass(EmailService)
960
+ .bindToKey('EmailService')
961
+ .pipe(singleton(), (p) => p.lazy()),
962
+ )
963
+ // SmsService is eager - connects to gateway immediately
964
+ .addRegistration(R.fromClass(SmsService).bindToKey('SmsService').pipe(singleton()))
965
+ .addRegistration(R.fromClass(NotificationService));
966
+
967
+ // Resolve NotificationService
968
+ const notifications = container.resolve<NotificationService>(NotificationService);
969
+
970
+ // SmsService initialized immediately (eager)
971
+ // EmailService NOT initialized yet (lazy)
972
+ expect(initLog).toEqual(['SmsService initialized - Gateway connected', 'NotificationService initialized']);
973
+
974
+ // Send SMS - already initialized
975
+ notifications.notifyBySms('555-1234', 'Test');
976
+ expect(initLog).toEqual(['SmsService initialized - Gateway connected', 'NotificationService initialized']);
977
+ });
978
+
979
+ it('should initialize lazy email service when first accessed', () => {
980
+ const container = new Container()
981
+ .addRegistration(
982
+ R.fromClass(EmailService)
983
+ .bindToKey('EmailService')
984
+ .pipe(singleton(), (p) => p.lazy()),
985
+ )
986
+ .addRegistration(R.fromClass(SmsService).bindToKey('SmsService').pipe(singleton()))
987
+ .addRegistration(R.fromClass(NotificationService));
988
+
989
+ const notifications = container.resolve<NotificationService>(NotificationService);
990
+
991
+ // Send email - NOW EmailService is initialized
992
+ const result = notifications.notifyByEmail('user@example.com', 'Welcome!');
993
+
994
+ expect(result).toBe('Email sent to user@example.com: Welcome!');
995
+ expect(initLog).toContain('EmailService initialized - SMTP connected');
996
+ });
997
+
998
+ it('should work with multiple lazy providers', () => {
999
+ const container = new Container()
1000
+ // Both services are lazy
1001
+ .addRegistration(
1002
+ R.fromClass(EmailService)
1003
+ .bindToKey('EmailService')
1004
+ .pipe(singleton(), (p) => p.lazy()),
1005
+ )
1006
+ .addRegistration(
1007
+ R.fromClass(SmsService)
1008
+ .bindToKey('SmsService')
1009
+ .pipe(singleton(), (p) => p.lazy()),
1010
+ )
1011
+ .addRegistration(R.fromClass(NotificationService));
1012
+
1013
+ const notifications = container.resolve<NotificationService>(NotificationService);
1014
+
1015
+ // Neither service initialized yet
1016
+ expect(initLog).toEqual(['NotificationService initialized']);
1017
+
1018
+ // Use SMS - only SMS initialized
1019
+ notifications.notifyBySms('555-1234', 'Test');
1020
+ expect(initLog).toEqual(['NotificationService initialized', 'SmsService initialized - Gateway connected']);
1021
+
1022
+ // Use Email - now Email initialized
1023
+ notifications.notifyByEmail('user@example.com', 'Test');
1024
+ expect(initLog).toEqual([
1025
+ 'NotificationService initialized',
1026
+ 'SmsService initialized - Gateway connected',
1027
+ 'EmailService initialized - SMTP connected',
1028
+ ]);
1029
+ });
1030
+ });
1031
+
1032
+ /**
1033
+ * Example 3: Pure Provider usage (without Registration)
1034
+ *
1035
+ * Use Provider.fromClass() directly with lazy() for maximum flexibility.
1036
+ */
1037
+ describe('with pure Provider', () => {
1038
+ class CacheService {
1039
+ constructor() {
1040
+ initLog.push('CacheService initialized - Redis connected');
1041
+ }
1042
+
1043
+ get(key: string): string | null {
1044
+ return `cached:${key}`;
1045
+ }
1046
+ }
1047
+
1048
+ class ApiService {
1049
+ constructor(@inject('CacheService') private cache: CacheService) {
1050
+ initLog.push('ApiService initialized');
1051
+ }
1052
+
1053
+ fetchData(id: string): string {
1054
+ const cached = this.cache.get(id);
1055
+ return cached || `fresh:${id}`;
1056
+ }
1057
+ }
1058
+
1059
+ it('should use Provider.fromClass with lazy() helper', () => {
1060
+ // Create pure provider with lazy loading
1061
+ const cacheProvider = Provider.fromClass(CacheService).pipe(lazy(), singleton());
1062
+
1063
+ const container = new Container();
1064
+ container.register('CacheService', cacheProvider);
1065
+ container.addRegistration(R.fromClass(ApiService));
1066
+
1067
+ const api = container.resolve<ApiService>(ApiService);
1068
+
1069
+ // CacheService not initialized yet (lazy)
1070
+ expect(initLog).toEqual(['ApiService initialized']);
1071
+
1072
+ // Access cache - NOW it's initialized
1073
+ api.fetchData('user:1');
1074
+ expect(initLog).toContain('CacheService initialized - Redis connected');
1075
+ });
1076
+
1077
+ it('should allow importing lazy as named export', () => {
1078
+ // Demonstrate that lazy() is imported from the library
1079
+ const cacheProvider = Provider.fromClass(CacheService).pipe(lazy());
1080
+
1081
+ const container = new Container();
1082
+ container.register('CacheService', cacheProvider);
1083
+
1084
+ const cache = container.resolve<CacheService>('CacheService');
1085
+
1086
+ // Not initialized until accessed
1087
+ expect(initLog).toEqual([]);
1088
+ cache.get('test');
1089
+ expect(initLog).toEqual(['CacheService initialized - Redis connected']);
1090
+ });
1091
+ });
1092
+
1093
+ /**
1094
+ * Example 4: Combining lazy with other pipes
1095
+ *
1096
+ * lazy() works seamlessly with other provider transformations.
1097
+ */
1098
+ describe('combining with other pipes', () => {
1099
+ class ConfigService {
1100
+ constructor(
1101
+ public apiUrl: string,
1102
+ public timeout: number,
1103
+ ) {
1104
+ initLog.push(`ConfigService initialized with ${apiUrl}`);
1105
+ }
1106
+ }
1107
+
1108
+ it('should combine lazy with args and singleton', () => {
1109
+ const container = new Container().addRegistration(
1110
+ R.fromClass(ConfigService)
1111
+ .bindToKey('Config')
1112
+ .pipe(
1113
+ (p) => p.setArgs(() => ['https://api.example.com', 5000]),
1114
+ (p) => p.lazy(),
1115
+ )
1116
+ .pipe(singleton()),
1117
+ );
1118
+
1119
+ // Config not initialized yet
1120
+ expect(initLog).toEqual([]);
1121
+
1122
+ // Resolve - still not initialized (lazy)
1123
+ const config1 = container.resolve<ConfigService>('Config');
1124
+ expect(initLog).toEqual([]);
1125
+
1126
+ // Access property - NOW initialized
1127
+ const url = config1.apiUrl;
1128
+ expect(url).toBe('https://api.example.com');
1129
+ expect(initLog).toEqual(['ConfigService initialized with https://api.example.com']);
1130
+
1131
+ // Resolve again - same instance (singleton)
1132
+ const config2 = container.resolve<ConfigService>('Config');
1133
+ expect(config2).toBe(config1);
1134
+ expect(initLog.length).toBe(1); // Still only one initialization
1135
+ });
1136
+ });
1137
+
1138
+ /**
1139
+ * Example 5: Real-world use case - Resource Management
1140
+ *
1141
+ * Lazy loading is ideal for:
1142
+ * - Database connections
1143
+ * - File handles
1144
+ * - External API clients
1145
+ * - Report generators
1146
+ */
1147
+ describe('real-world example - feature flags', () => {
1148
+ class FeatureFlagService {
1149
+ constructor() {
1150
+ initLog.push('FeatureFlagService initialized');
1151
+ }
1152
+
1153
+ isEnabled(feature: string): boolean {
1154
+ return feature === 'premium';
1155
+ }
1156
+ }
1157
+
1158
+ @register(bindTo('PremiumFeature'), lazy(), singleton())
1159
+ class PremiumFeature {
1160
+ constructor(@inject('FeatureFlagService') private flags: FeatureFlagService) {
1161
+ initLog.push('PremiumFeature initialized - expensive operation');
1162
+ }
1163
+
1164
+ execute(): string {
1165
+ return 'Premium feature executed';
1166
+ }
1167
+ }
1168
+
1169
+ class Application {
1170
+ constructor(
1171
+ @inject('FeatureFlagService') private flags: FeatureFlagService,
1172
+ @inject('PremiumFeature') private premium: PremiumFeature,
1173
+ ) {
1174
+ initLog.push('Application initialized');
1175
+ }
1176
+
1177
+ handleRequest(feature: string): string {
1178
+ if (this.flags.isEnabled(feature)) {
1179
+ return this.premium.execute();
1180
+ }
1181
+ return 'Standard feature';
1182
+ }
1183
+ }
1184
+
1185
+ it('should not initialize premium features for standard users', () => {
1186
+ const container = new Container()
1187
+ .addRegistration(R.fromClass(FeatureFlagService).bindToKey('FeatureFlagService').pipe(singleton()))
1188
+ .addRegistration(R.fromClass(PremiumFeature))
1189
+ .addRegistration(R.fromClass(Application));
1190
+
1191
+ const app = container.resolve<Application>(Application);
1192
+
1193
+ // Standard request - premium feature not initialized
1194
+ const result = app.handleRequest('standard');
1195
+ expect(result).toBe('Standard feature');
1196
+ expect(initLog).not.toContain('PremiumFeature initialized - expensive operation');
1197
+ });
1198
+
1199
+ it('should initialize premium features only for premium users', () => {
1200
+ const container = new Container()
1201
+ .addRegistration(R.fromClass(FeatureFlagService).bindToKey('FeatureFlagService').pipe(singleton()))
1202
+ .addRegistration(R.fromClass(PremiumFeature))
1203
+ .addRegistration(R.fromClass(Application));
1204
+
1205
+ const app = container.resolve<Application>(Application);
1206
+
1207
+ // Premium request - NOW premium feature is initialized
1208
+ const result = app.handleRequest('premium');
1209
+ expect(result).toBe('Premium feature executed');
1210
+ expect(initLog).toContain('PremiumFeature initialized - expensive operation');
1211
+ });
1212
+ });
1213
+ });
1214
+
1215
+ ```
1216
+
662
1217
  ## Injector
663
1218
  `IInjector` is used to describe how dependencies should be injected to constructor.
664
1219
 
@@ -144,6 +144,11 @@ class Container {
144
144
  hasTag(tag) {
145
145
  return this.tags.has(tag);
146
146
  }
147
+ addTags(...tags) {
148
+ for (const tag of tags) {
149
+ this.tags.add(tag);
150
+ }
151
+ }
147
152
  validateContainer() {
148
153
  if (this.isDisposed) {
149
154
  throw new ContainerDisposedError_1.ContainerDisposedError('Container is already disposed');
@@ -29,6 +29,9 @@ class EmptyContainer {
29
29
  hasTag(tag) {
30
30
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
31
31
  }
32
+ addTags(...tags) {
33
+ throw new MethodNotImplementedError_1.MethodNotImplementedError();
34
+ }
32
35
  getRegistrations() {
33
36
  return [];
34
37
  }
@@ -141,6 +141,11 @@ export class Container {
141
141
  hasTag(tag) {
142
142
  return this.tags.has(tag);
143
143
  }
144
+ addTags(...tags) {
145
+ for (const tag of tags) {
146
+ this.tags.add(tag);
147
+ }
148
+ }
144
149
  validateContainer() {
145
150
  if (this.isDisposed) {
146
151
  throw new ContainerDisposedError('Container is already disposed');
@@ -26,6 +26,9 @@ export class EmptyContainer {
26
26
  hasTag(tag) {
27
27
  throw new MethodNotImplementedError();
28
28
  }
29
+ addTags(...tags) {
30
+ throw new MethodNotImplementedError();
31
+ }
29
32
  getRegistrations() {
30
33
  return [];
31
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-ioc-container",
3
- "version": "46.6.2",
3
+ "version": "46.7.1",
4
4
  "description": "Typescript IoC container",
5
5
  "workspaces": [
6
6
  "docs"
@@ -39,6 +39,7 @@ export declare class Container implements IContainer {
39
39
  getParent(): IContainer;
40
40
  getInstances(cascade?: boolean): Instance<unknown>[];
41
41
  hasTag(tag: Tag): boolean;
42
+ addTags(...tags: Tag[]): void;
42
43
  private validateContainer;
43
44
  private findProviderByKeyOrFail;
44
45
  }
@@ -14,6 +14,7 @@ export declare class EmptyContainer implements IContainer {
14
14
  dispose(): void;
15
15
  register(key: DependencyKey, value: IProvider): this;
16
16
  hasTag(tag: Tag): boolean;
17
+ addTags(...tags: Tag[]): void;
17
18
  getRegistrations(): never[];
18
19
  removeScope(): void;
19
20
  useModule(module: IContainerModule): this;
@@ -17,6 +17,7 @@ type WithExcludedKeys = {
17
17
  };
18
18
  export interface Tagged {
19
19
  hasTag(tag: Tag): boolean;
20
+ addTags(...tags: Tag[]): void;
20
21
  }
21
22
  export type ResolveOneOptions = ProviderOptions & Partial<WithChild>;
22
23
  export type ResolveManyOptions = ResolveOneOptions & Partial<WithExcludedKeys>;