ts-ioc-container 46.7.1 → 46.8.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
@@ -534,6 +534,107 @@ describe('Instances', function () {
534
534
 
535
535
  ```
536
536
 
537
+ ### Check Registration
538
+ Sometimes you want to check if a registration with a specific key exists in the container. This is useful for conditional registration logic, validation, and debugging.
539
+
540
+ - `hasRegistration(key)` checks if a registration exists in the current container or parent containers
541
+ - Checks both the current container's registrations and parent container registrations
542
+ - Works with string keys, symbol keys, and token keys
543
+ - Returns false after container disposal
544
+
545
+ ```typescript
546
+ import { Container, Registration as R, bindTo, register, SingleToken } from 'ts-ioc-container';
547
+
548
+ /**
549
+ * Container Registration Checking - hasRegistration
550
+ *
551
+ * The `hasRegistration` method allows you to check if a registration with a specific key
552
+ * exists in the current container. This is useful for conditional registration logic,
553
+ * validation, and debugging.
554
+ *
555
+ * Key points:
556
+ * - Checks only the current container's registrations (not parent containers)
557
+ * - Works with string keys, symbol keys, and token keys
558
+ * - Returns false after container disposal
559
+ * - Useful for conditional registration patterns
560
+ */
561
+ describe('hasRegistration', function () {
562
+ const createAppContainer = () => new Container({ tags: ['application'] });
563
+
564
+ it('should return true when registration exists with string key', function () {
565
+ const container = createAppContainer();
566
+ container.addRegistration(R.fromValue('production').bindToKey('Environment'));
567
+
568
+ expect(container.hasRegistration('Environment')).toBe(true);
569
+ });
570
+
571
+ it('should return false when registration does not exist', function () {
572
+ const container = createAppContainer();
573
+
574
+ expect(container.hasRegistration('NonExistentService')).toBe(false);
575
+ });
576
+
577
+ it('should work with symbol keys', function () {
578
+ const container = createAppContainer();
579
+ const serviceKey = Symbol('IService');
580
+ container.addRegistration(R.fromValue({ name: 'Service' }).bindToKey(serviceKey));
581
+
582
+ expect(container.hasRegistration(serviceKey)).toBe(true);
583
+ });
584
+
585
+ it('should work with token keys', function () {
586
+ const container = createAppContainer();
587
+ const loggerToken = new SingleToken<{ log: (msg: string) => void }>('ILogger');
588
+ container.addRegistration(R.fromValue({ log: () => {} }).bindTo(loggerToken));
589
+
590
+ expect(container.hasRegistration(loggerToken.token)).toBe(true);
591
+ });
592
+
593
+ it('should check current container and parent registrations', function () {
594
+ // Parent container has a registration
595
+ const parent = createAppContainer();
596
+ parent.addRegistration(R.fromValue('parent-config').bindToKey('Config'));
597
+
598
+ // Child scope does not have the registration
599
+ const child = parent.createScope();
600
+ child.addRegistration(R.fromValue('child-service').bindToKey('Service'));
601
+
602
+ // Child should see parent's registration (checks parent as well)
603
+ expect(child.hasRegistration('Config')).toBe(true);
604
+ // Child should see its own registration
605
+ expect(child.hasRegistration('Service')).toBe(true);
606
+ // Parent should see its own registration
607
+ expect(parent.hasRegistration('Config')).toBe(true);
608
+ });
609
+
610
+ it('should work with class-based registrations', function () {
611
+ @register(bindTo('ILogger'))
612
+ class Logger {}
613
+
614
+ const container = createAppContainer();
615
+ container.addRegistration(R.fromClass(Logger));
616
+
617
+ expect(container.hasRegistration('ILogger')).toBe(true);
618
+ });
619
+
620
+ it('should be useful for conditional registration patterns', function () {
621
+ const container = createAppContainer();
622
+
623
+ // Register a base service
624
+ container.addRegistration(R.fromValue('base-service').bindToKey('BaseService'));
625
+
626
+ // Conditionally register an extension only if base exists
627
+ if (container.hasRegistration('BaseService')) {
628
+ container.addRegistration(R.fromValue('extension-service').bindToKey('ExtensionService'));
629
+ }
630
+
631
+ expect(container.hasRegistration('BaseService')).toBe(true);
632
+ expect(container.hasRegistration('ExtensionService')).toBe(true);
633
+ });
634
+ });
635
+
636
+ ```
637
+
537
638
  ### Dispose
538
639
  Sometimes you want to dispose container and all its scopes. For example, when you want to prevent memory leaks. Or you want to ensure that nobody can use container after it was disposed.
539
640
 
@@ -1157,7 +1258,7 @@ describe('lazy registerPipe', () => {
1157
1258
 
1158
1259
  @register(bindTo('PremiumFeature'), lazy(), singleton())
1159
1260
  class PremiumFeature {
1160
- constructor(@inject('FeatureFlagService') private flags: FeatureFlagService) {
1261
+ constructor() {
1161
1262
  initLog.push('PremiumFeature initialized - expensive operation');
1162
1263
  }
1163
1264
 
@@ -63,7 +63,7 @@ class Container {
63
63
  }
64
64
  resolveOneByAlias(alias, { args, child = this, lazy } = {}) {
65
65
  this.validateContainer();
66
- const [key, ..._] = this.aliases.getKeysByAlias(alias);
66
+ const key = this.aliases.getKeysByAlias(alias)[0];
67
67
  const provider = key ? this.findProviderByKeyOrFail(key) : undefined;
68
68
  return provider?.hasAccess({ invocationScope: child, providerScope: this })
69
69
  ? provider.resolve(this, { args, lazy })
@@ -107,6 +107,9 @@ class Container {
107
107
  getRegistrations() {
108
108
  return [...this.parent.getRegistrations(), ...this.registrations];
109
109
  }
110
+ hasRegistration(key) {
111
+ return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
112
+ }
110
113
  addOnConstructHook(...hooks) {
111
114
  this.onConstructHookList.push(...hooks);
112
115
  return this;
@@ -35,6 +35,9 @@ class EmptyContainer {
35
35
  getRegistrations() {
36
36
  return [];
37
37
  }
38
+ hasRegistration(key) {
39
+ return false;
40
+ }
38
41
  removeScope() { }
39
42
  useModule(module) {
40
43
  throw new MethodNotImplementedError_1.MethodNotImplementedError();
@@ -79,5 +79,11 @@ class Registration {
79
79
  const provider = this.createProvider();
80
80
  container.register(this.key, provider.pipe(...this.mappers), { aliases: [...this.aliases] });
81
81
  }
82
+ getKeyOrFail() {
83
+ if (!this.key) {
84
+ throw new DependencyMissingKeyError_1.DependencyMissingKeyError('No key provided for registration');
85
+ }
86
+ return this.key;
87
+ }
82
88
  }
83
89
  exports.Registration = Registration;
@@ -60,7 +60,7 @@ export class Container {
60
60
  }
61
61
  resolveOneByAlias(alias, { args, child = this, lazy } = {}) {
62
62
  this.validateContainer();
63
- const [key, ..._] = this.aliases.getKeysByAlias(alias);
63
+ const key = this.aliases.getKeysByAlias(alias)[0];
64
64
  const provider = key ? this.findProviderByKeyOrFail(key) : undefined;
65
65
  return provider?.hasAccess({ invocationScope: child, providerScope: this })
66
66
  ? provider.resolve(this, { args, lazy })
@@ -104,6 +104,9 @@ export class Container {
104
104
  getRegistrations() {
105
105
  return [...this.parent.getRegistrations(), ...this.registrations];
106
106
  }
107
+ hasRegistration(key) {
108
+ return this.registrations.some((r) => r.getKeyOrFail() === key) || this.parent.hasRegistration(key);
109
+ }
107
110
  addOnConstructHook(...hooks) {
108
111
  this.onConstructHookList.push(...hooks);
109
112
  return this;
@@ -32,6 +32,9 @@ export class EmptyContainer {
32
32
  getRegistrations() {
33
33
  return [];
34
34
  }
35
+ hasRegistration(key) {
36
+ return false;
37
+ }
35
38
  removeScope() { }
36
39
  useModule(module) {
37
40
  throw new MethodNotImplementedError();
@@ -76,4 +76,10 @@ export class Registration {
76
76
  const provider = this.createProvider();
77
77
  container.register(this.key, provider.pipe(...this.mappers), { aliases: [...this.aliases] });
78
78
  }
79
+ getKeyOrFail() {
80
+ if (!this.key) {
81
+ throw new DependencyMissingKeyError('No key provided for registration');
82
+ }
83
+ return this.key;
84
+ }
79
85
  }
package/package.json CHANGED
@@ -1,10 +1,7 @@
1
1
  {
2
2
  "name": "ts-ioc-container",
3
- "version": "46.7.1",
3
+ "version": "46.8.1",
4
4
  "description": "Typescript IoC container",
5
- "workspaces": [
6
- "docs"
7
- ],
8
5
  "publishConfig": {
9
6
  "access": "public",
10
7
  "registry": "https://registry.npmjs.org/"
@@ -38,72 +35,39 @@
38
35
  ],
39
36
  "repository": {
40
37
  "type": "git",
41
- "url": "git+https://github.com/IgorBabkin/ts-ioc-container"
38
+ "url": "git+https://github.com/IgorBabkin/ts-ioc-container",
39
+ "directory": "packages/ts-ioc-container"
42
40
  },
43
41
  "scripts": {
44
42
  "build:cjm": "rimraf cjm && tsc -p tsconfig.production.json --outDir cjm --module CommonJS",
45
43
  "build:esm": "rimraf esm && tsc -p tsconfig.production.json --outDir esm",
46
44
  "build:types": "rimraf typings && tsc -p tsconfig.production.json --outDir typings --emitDeclarationOnly --declaration",
47
- "generate:docs": "scripts/generate-readme/generate-readme.ts && git add README.md",
48
- "build": "npm run build:cjm && npm run build:esm && npm run build:types",
49
- "test": "jest",
50
- "test:coverage": "jest --coverage --coverageReporters=lcov --coverageReporters=text-summary",
45
+ "build": "pnpm run build:cjm && pnpm run build:esm && pnpm run build:types",
46
+ "test": "jest --maxWorkers=2",
47
+ "test:coverage": "jest --maxWorkers=2 --coverage --coverageReporters=lcov --coverageReporters=text-summary",
51
48
  "type-check": "tsc --noEmit",
52
49
  "type-check:watch": "tsc --noEmit --watch",
53
- "commit": "cz",
50
+ "lint": "eslint lib/**/*.ts __tests__/**/*.ts scripts/**/*.ts",
51
+ "lint:fix": "pnpm run lint -- --fix",
54
52
  "format": "prettier --write \"**/*.ts\"",
55
- "lint": "eslint lib/**/*.ts __tests__/**/*.ts scripts/**/*.ts",
56
- "lint:fix": "npm run lint --fix",
57
- "audit": "npm audit",
58
- "prepare": "husky",
59
- "release": "npm run build && npm test && npm publish",
60
- "docs:dev": "pnpm --filter ts-ioc-container-docs run dev",
61
- "docs:serve": "pnpm --filter ts-ioc-container-docs run dev",
62
- "docs:build": "pnpm --filter ts-ioc-container-docs run build",
63
- "docs:preview": "pnpm --filter ts-ioc-container-docs run preview"
53
+ "generate:docs": "scripts/generate-readme/generate-readme.ts",
54
+ "release": "pnpm run build && pnpm run test && pnpm publish"
64
55
  },
65
56
  "devDependencies": {
66
- "@semantic-release/changelog": "^6.0.3",
67
- "@semantic-release/git": "^10.0.1",
68
- "@semantic-release/github": "^12.0.2",
69
- "@semantic-release/npm": "^13.1.2",
70
57
  "@types/jest": "29.5.14",
58
+ "@types/node": "^25.1.0",
71
59
  "@typescript-eslint/eslint-plugin": "8.29.1",
72
60
  "@typescript-eslint/parser": "8.29.1",
73
- "cz-conventional-changelog": "^3.3.0",
74
61
  "eslint": "9.24.0",
75
62
  "eslint-config-prettier": "10.1.1",
76
63
  "eslint-plugin-prettier": "5.2.6",
77
64
  "handlebars": "^4.7.8",
78
- "husky": "^9.1.7",
79
65
  "jest": "29.7.0",
80
- "lint-staged": "^15.5.0",
81
66
  "moq.ts": "^7.4.1",
82
67
  "prettier": "3.5.3",
83
- "prettier-plugin-astro": "^0.14.1",
84
68
  "reflect-metadata": "^0.2.2",
85
69
  "rimraf": "6.0.1",
86
- "semantic-release": "^25.0.2",
87
70
  "ts-jest": "29.3.1",
88
71
  "typescript": "5.8.3"
89
- },
90
- "lint-staged": {
91
- "*.{js,ts,tsx}": [
92
- "eslint --fix",
93
- "prettier --write"
94
- ],
95
- "docs/**/*.{js,ts,tsx,mjs,astro}": [
96
- "prettier --write"
97
- ],
98
- "docs/**/*.astro": [
99
- "eslint --fix"
100
- ]
101
- },
102
- "gitHead": "ae10f302c7e0f55196b42669040735112479a854",
103
- "config": {
104
- "commitizen": {
105
- "path": "./node_modules/cz-conventional-changelog"
106
- }
107
- },
108
- "packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd"
72
+ }
109
73
  }
@@ -30,6 +30,7 @@ export declare class Container implements IContainer {
30
30
  dispose(): void;
31
31
  addRegistration(registration: IRegistration): this;
32
32
  getRegistrations(): IRegistration[];
33
+ hasRegistration(key: DependencyKey): boolean;
33
34
  addOnConstructHook(...hooks: OnConstructHook[]): this;
34
35
  addOnDisposeHook(...hooks: OnDisposeHook[]): this;
35
36
  addInstance(instance: Instance): void;
@@ -16,6 +16,7 @@ export declare class EmptyContainer implements IContainer {
16
16
  hasTag(tag: Tag): boolean;
17
17
  addTags(...tags: Tag[]): void;
18
18
  getRegistrations(): never[];
19
+ hasRegistration(key: DependencyKey): boolean;
19
20
  removeScope(): void;
20
21
  useModule(module: IContainerModule): this;
21
22
  addRegistration(registration: IRegistration): this;
@@ -38,6 +38,7 @@ export interface IContainer extends Tagged {
38
38
  register(key: DependencyKey, value: IProvider, options?: RegisterOptions): this;
39
39
  addRegistration(registration: IRegistration): this;
40
40
  getRegistrations(): IRegistration[];
41
+ hasRegistration(key: DependencyKey): boolean;
41
42
  resolve<T>(target: constructor<T> | DependencyKey, options?: ResolveOneOptions): T;
42
43
  resolveByAlias<T>(alias: DependencyKey, options?: ResolveManyOptions): T[];
43
44
  resolveOneByAlias<T>(alias: DependencyKey, options?: ResolveOneOptions): T;
@@ -6,6 +6,7 @@ import { MapFn } from '../utils/fp';
6
6
  import { type constructor } from '../utils/basic';
7
7
  export type ScopeMatchRule = (s: IContainer, prev?: boolean) => boolean;
8
8
  export interface IRegistration<T = any> extends IContainerModule {
9
+ getKeyOrFail(): DependencyKey;
9
10
  when(...predicates: ScopeMatchRule[]): this;
10
11
  bindToKey(key: DependencyKey): this;
11
12
  bindTo(key: DependencyKey | BindToken): this;
@@ -7,7 +7,7 @@ import { type MapFn } from '../utils/fp';
7
7
  import { type constructor } from '../utils/basic';
8
8
  export declare class Registration<T = any> implements IRegistration<T> {
9
9
  private createProvider;
10
- private key?;
10
+ key?: DependencyKey | undefined;
11
11
  private scopeRules;
12
12
  static fromClass<T>(Target: constructor<T>): IRegistration<any>;
13
13
  static fromValue<T>(value: T): IRegistration<any> | Registration<T>;
@@ -23,4 +23,5 @@ export declare class Registration<T = any> implements IRegistration<T> {
23
23
  bindTo(key: DependencyKey | BindToken): this;
24
24
  private matchScope;
25
25
  applyTo(container: IContainer): void;
26
+ getKeyOrFail(): DependencyKey;
26
27
  }