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 +102 -1
- package/cjm/container/Container.js +4 -1
- package/cjm/container/EmptyContainer.js +3 -0
- package/cjm/registration/Registration.js +6 -0
- package/esm/container/Container.js +4 -1
- package/esm/container/EmptyContainer.js +3 -0
- package/esm/registration/Registration.js +6 -0
- package/package.json +12 -48
- package/typings/container/Container.d.ts +1 -0
- package/typings/container/EmptyContainer.d.ts +1 -0
- package/typings/container/IContainer.d.ts +1 -0
- package/typings/registration/IRegistration.d.ts +1 -0
- package/typings/registration/Registration.d.ts +2 -1
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(
|
|
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
|
|
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;
|
|
@@ -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
|
|
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;
|
|
@@ -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.
|
|
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
|
-
"
|
|
48
|
-
"
|
|
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
|
-
"
|
|
50
|
+
"lint": "eslint lib/**/*.ts __tests__/**/*.ts scripts/**/*.ts",
|
|
51
|
+
"lint:fix": "pnpm run lint -- --fix",
|
|
54
52
|
"format": "prettier --write \"**/*.ts\"",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
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
|
-
|
|
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
|
}
|