syrinject 0.1.0 → 0.1.2
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 +205 -2
- package/assets/Syringect-Black.png +0 -0
- package/dist/container.test.js +102 -0
- package/dist/test/container.test.js +102 -0
- package/dist/test/test/container.test.js +101 -0
- package/package.json +4 -2
- package/dist/package.json +0 -20
package/README.md
CHANGED
|
@@ -1,5 +1,208 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://cdn.jsdelivr.net/npm/syrinject@latest/assets/
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/npm/syrinject@latest/assets/Syringect-Black.png" alt="Syrinject Logo" width="500" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>A tiny, modern dependency injection container for TypeScript.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<hr>
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Syrinject is a lightweight Dependency Injection (DI) container built for TypeScript. It focuses on clarity and explicitness while remaining flexible enough for small apps and larger codebases. You can register providers and resolve dependencies by token.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- ✅ Simple DI container API.
|
|
18
|
+
- ✅ Typed tokens for safe resolution.
|
|
19
|
+
- ✅ Class, value, and factory providers.
|
|
20
|
+
- ✅ Singleton or transient lifetimes.
|
|
21
|
+
- ✅ Optional logging decorator for class usage.
|
|
22
|
+
- ✅ Clear error messages for invalid providers, missing dependencies, and circular graphs.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install syrinject
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { Container } from "syrinject";
|
|
34
|
+
|
|
35
|
+
// Tokens can be string, symbol, or class constructors
|
|
36
|
+
const LoggerToken = "Logger";
|
|
37
|
+
|
|
38
|
+
interface Logger {
|
|
39
|
+
log(message: string): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class ConsoleLogger implements Logger {
|
|
43
|
+
log(message: string) {
|
|
44
|
+
console.log(message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const container = new Container();
|
|
49
|
+
|
|
50
|
+
// Register a token with a class provider
|
|
51
|
+
container.register(LoggerToken, {
|
|
52
|
+
useClass: ConsoleLogger,
|
|
53
|
+
singleton: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const logger = container.resolve<Logger>(LoggerToken);
|
|
57
|
+
logger.log("Hello from Syrinject!");
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Core Concepts
|
|
61
|
+
|
|
62
|
+
### Tokens
|
|
63
|
+
|
|
64
|
+
Tokens are identifiers for dependencies. Use a `string`, `symbol`, or a class constructor.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
export interface HttpClient {
|
|
68
|
+
get(url: string): Promise<string>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const HttpClientToken = "HttpClient";
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Providers
|
|
75
|
+
|
|
76
|
+
Providers define how a dependency is created.
|
|
77
|
+
|
|
78
|
+
#### Class Provider
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
container.register(HttpClientToken, {
|
|
82
|
+
useClass: FetchHttpClient,
|
|
83
|
+
singleton: false,
|
|
84
|
+
deps: [LoggerToken],
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Value Provider
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
container.register(HttpClientToken, {
|
|
92
|
+
useValue: { get: async (url) => fetch(url).then((r) => r.text()) },
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Factory Provider
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
container.register(HttpClientToken, {
|
|
100
|
+
useFactory: (c) => new FetchHttpClient(c.resolve(LoggerToken)),
|
|
101
|
+
singleton: true,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Lifetimes
|
|
106
|
+
|
|
107
|
+
- `singleton: true` creates and caches a single instance.
|
|
108
|
+
- `singleton: false` creates a new instance on each resolve (default).
|
|
109
|
+
|
|
110
|
+
### Decorator: Logger
|
|
111
|
+
|
|
112
|
+
Syrinject includes a `Logger` decorator that logs instantiation and public method calls.
|
|
113
|
+
To show dependencies and singleton status in logs, set static `deps` (or `dependencies`) and `singleton` on the class.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { Container, Logger } from "syrinject";
|
|
117
|
+
|
|
118
|
+
const LoggerToken = "Logger";
|
|
119
|
+
|
|
120
|
+
interface LoggerService {
|
|
121
|
+
log(message: string): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class ConsoleLogger implements LoggerService {
|
|
125
|
+
log(message: string) {
|
|
126
|
+
console.log(message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Logger()
|
|
131
|
+
class UserService {
|
|
132
|
+
static deps = [LoggerToken];
|
|
133
|
+
static singleton = true;
|
|
134
|
+
|
|
135
|
+
constructor(private readonly logger: LoggerService) {}
|
|
136
|
+
|
|
137
|
+
createUser(name: string) {
|
|
138
|
+
this.logger.log(`Creating user: ${name}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const container = new Container();
|
|
143
|
+
container.register(LoggerToken, { useClass: ConsoleLogger, singleton: true });
|
|
144
|
+
container.register(UserService, { useClass: UserService, deps: UserService.deps, singleton: true });
|
|
145
|
+
|
|
146
|
+
const service = container.resolve(UserService);
|
|
147
|
+
service.createUser("Ada");
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Container API
|
|
151
|
+
|
|
152
|
+
### `register(token, provider)`
|
|
153
|
+
|
|
154
|
+
Registers a provider for a token.
|
|
155
|
+
|
|
156
|
+
### `resolve(token)`
|
|
157
|
+
|
|
158
|
+
Resolves an instance for the given token.
|
|
159
|
+
|
|
160
|
+
### `unregister(token)`
|
|
161
|
+
|
|
162
|
+
Removes a token from the container.
|
|
163
|
+
|
|
164
|
+
### `clear()`
|
|
165
|
+
|
|
166
|
+
Clears all registrations.
|
|
167
|
+
|
|
168
|
+
### `has(token)`
|
|
169
|
+
|
|
170
|
+
Checks if a token is registered.
|
|
171
|
+
|
|
172
|
+
### `registerClass(...)`
|
|
173
|
+
|
|
174
|
+
Registers a class provider. You can pass a token + class, or only a class.
|
|
175
|
+
When only a class is provided, the token will be the class name (string).
|
|
176
|
+
|
|
177
|
+
### `registerValue(token, value)`
|
|
178
|
+
|
|
179
|
+
Registers a value provider.
|
|
180
|
+
|
|
181
|
+
### `registerFactory(token, factory, options?)`
|
|
182
|
+
|
|
183
|
+
Registers a factory provider.
|
|
184
|
+
|
|
185
|
+
## Errors
|
|
186
|
+
|
|
187
|
+
Syrinject throws descriptive errors to simplify debugging:
|
|
188
|
+
|
|
189
|
+
- `DependencyNotFoundError` when a token is not registered.
|
|
190
|
+
- `InvalidProviderError` when a provider is malformed.
|
|
191
|
+
- `CircularDependencyError` when a dependency graph loops.
|
|
192
|
+
|
|
193
|
+
## TypeScript Configuration
|
|
194
|
+
|
|
195
|
+
If you use the `Logger` decorator, enable decorators in `tsconfig.json`:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"compilerOptions": {
|
|
200
|
+
"experimentalDecorators": true,
|
|
201
|
+
"emitDecoratorMetadata": false
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
Binary file
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const { Container, CircularDependencyError, DependencyNotFoundError, InvalidProviderError } = require(node_path_1.default.resolve(__dirname, '..', '..', 'dist'));
|
|
10
|
+
(0, node_test_1.default)('resolve class with deps from options', () => {
|
|
11
|
+
class Engine {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.kind = 'v8';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class Car {
|
|
17
|
+
constructor(engine) {
|
|
18
|
+
this.engine = engine;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const container = new Container();
|
|
22
|
+
container.register(Engine, Engine);
|
|
23
|
+
container.register(Car, Car, { deps: [Engine] });
|
|
24
|
+
const car = container.resolve(Car);
|
|
25
|
+
strict_1.default.equal(car.engine.kind, 'v8');
|
|
26
|
+
});
|
|
27
|
+
(0, node_test_1.default)('registerClass infers token by class name', () => {
|
|
28
|
+
class Service {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.value = 42;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const container = new Container();
|
|
34
|
+
container.registerClass(Service);
|
|
35
|
+
const resolved = container.resolve('Service');
|
|
36
|
+
strict_1.default.ok(resolved instanceof Service);
|
|
37
|
+
strict_1.default.equal(resolved.value, 42);
|
|
38
|
+
});
|
|
39
|
+
(0, node_test_1.default)('singleton class provider returns same instance', () => {
|
|
40
|
+
class Counter {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.count = 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const container = new Container();
|
|
46
|
+
container.register(Counter, Counter, { singleton: true });
|
|
47
|
+
const a = container.resolve(Counter);
|
|
48
|
+
const b = container.resolve(Counter);
|
|
49
|
+
strict_1.default.equal(a, b);
|
|
50
|
+
});
|
|
51
|
+
(0, node_test_1.default)('value provider returns same value', () => {
|
|
52
|
+
const container = new Container();
|
|
53
|
+
container.registerValue('config', { port: 3000 });
|
|
54
|
+
const config = container.resolve('config');
|
|
55
|
+
strict_1.default.deepEqual(config, { port: 3000 });
|
|
56
|
+
});
|
|
57
|
+
(0, node_test_1.default)('factory provider builds instance', () => {
|
|
58
|
+
const container = new Container();
|
|
59
|
+
container.registerFactory('random', () => ({ value: Math.random() }));
|
|
60
|
+
const a = container.resolve('random');
|
|
61
|
+
const b = container.resolve('random');
|
|
62
|
+
strict_1.default.notEqual(a, b);
|
|
63
|
+
strict_1.default.equal(typeof a.value, 'number');
|
|
64
|
+
});
|
|
65
|
+
(0, node_test_1.default)('factory provider respects singleton', () => {
|
|
66
|
+
const container = new Container();
|
|
67
|
+
container.registerFactory('singleton', () => ({ value: Math.random() }), { singleton: true });
|
|
68
|
+
const a = container.resolve('singleton');
|
|
69
|
+
const b = container.resolve('singleton');
|
|
70
|
+
strict_1.default.equal(a, b);
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.default)('throws DependencyNotFoundError for missing token', () => {
|
|
73
|
+
const container = new Container();
|
|
74
|
+
strict_1.default.throws(() => container.resolve('missing'), (err) => err instanceof DependencyNotFoundError);
|
|
75
|
+
});
|
|
76
|
+
(0, node_test_1.default)('throws CircularDependencyError when cycle exists', () => {
|
|
77
|
+
class A {
|
|
78
|
+
}
|
|
79
|
+
class B {
|
|
80
|
+
}
|
|
81
|
+
const container = new Container();
|
|
82
|
+
container.register(A, A, { deps: [B] });
|
|
83
|
+
container.register(B, B, { deps: [A] });
|
|
84
|
+
strict_1.default.throws(() => container.resolve(A), (err) => err instanceof CircularDependencyError);
|
|
85
|
+
});
|
|
86
|
+
(0, node_test_1.default)('throws InvalidProviderError for invalid provider', () => {
|
|
87
|
+
const container = new Container();
|
|
88
|
+
container.register('invalid', {});
|
|
89
|
+
strict_1.default.throws(() => container.resolve('invalid'), (err) => err instanceof InvalidProviderError);
|
|
90
|
+
});
|
|
91
|
+
(0, node_test_1.default)('has, unregister, clear work as expected', () => {
|
|
92
|
+
const container = new Container();
|
|
93
|
+
container.registerValue('token', 123);
|
|
94
|
+
strict_1.default.equal(container.has('token'), true);
|
|
95
|
+
container.unregister('token');
|
|
96
|
+
strict_1.default.equal(container.has('token'), false);
|
|
97
|
+
container.registerValue('a', 1);
|
|
98
|
+
container.registerValue('b', 2);
|
|
99
|
+
container.clear();
|
|
100
|
+
strict_1.default.equal(container.has('a'), false);
|
|
101
|
+
strict_1.default.equal(container.has('b'), false);
|
|
102
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const { Container, CircularDependencyError, DependencyNotFoundError, InvalidProviderError } = require(node_path_1.default.resolve(__dirname, '..', '..', 'dist'));
|
|
10
|
+
(0, node_test_1.default)('resolve class with deps from options', () => {
|
|
11
|
+
class Engine {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.kind = 'v8';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class Car {
|
|
17
|
+
constructor(engine) {
|
|
18
|
+
this.engine = engine;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const container = new Container();
|
|
22
|
+
container.register(Engine, Engine);
|
|
23
|
+
container.register(Car, Car, { deps: [Engine] });
|
|
24
|
+
const car = container.resolve(Car);
|
|
25
|
+
strict_1.default.equal(car.engine.kind, 'v8');
|
|
26
|
+
});
|
|
27
|
+
(0, node_test_1.default)('registerClass infers token by class name', () => {
|
|
28
|
+
class Service {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.value = 42;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const container = new Container();
|
|
34
|
+
container.registerClass(Service);
|
|
35
|
+
const resolved = container.resolve('Service');
|
|
36
|
+
strict_1.default.ok(resolved instanceof Service);
|
|
37
|
+
strict_1.default.equal(resolved.value, 42);
|
|
38
|
+
});
|
|
39
|
+
(0, node_test_1.default)('singleton class provider returns same instance', () => {
|
|
40
|
+
class Counter {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.count = 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const container = new Container();
|
|
46
|
+
container.register(Counter, Counter, { singleton: true });
|
|
47
|
+
const a = container.resolve(Counter);
|
|
48
|
+
const b = container.resolve(Counter);
|
|
49
|
+
strict_1.default.equal(a, b);
|
|
50
|
+
});
|
|
51
|
+
(0, node_test_1.default)('value provider returns same value', () => {
|
|
52
|
+
const container = new Container();
|
|
53
|
+
container.registerValue('config', { port: 3000 });
|
|
54
|
+
const config = container.resolve('config');
|
|
55
|
+
strict_1.default.deepEqual(config, { port: 3000 });
|
|
56
|
+
});
|
|
57
|
+
(0, node_test_1.default)('factory provider builds instance', () => {
|
|
58
|
+
const container = new Container();
|
|
59
|
+
container.registerFactory('random', () => ({ value: Math.random() }));
|
|
60
|
+
const a = container.resolve('random');
|
|
61
|
+
const b = container.resolve('random');
|
|
62
|
+
strict_1.default.notEqual(a, b);
|
|
63
|
+
strict_1.default.equal(typeof a.value, 'number');
|
|
64
|
+
});
|
|
65
|
+
(0, node_test_1.default)('factory provider respects singleton', () => {
|
|
66
|
+
const container = new Container();
|
|
67
|
+
container.registerFactory('singleton', () => ({ value: Math.random() }), { singleton: true });
|
|
68
|
+
const a = container.resolve('singleton');
|
|
69
|
+
const b = container.resolve('singleton');
|
|
70
|
+
strict_1.default.equal(a, b);
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.default)('throws DependencyNotFoundError for missing token', () => {
|
|
73
|
+
const container = new Container();
|
|
74
|
+
strict_1.default.throws(() => container.resolve('missing'), (err) => err instanceof DependencyNotFoundError);
|
|
75
|
+
});
|
|
76
|
+
(0, node_test_1.default)('throws CircularDependencyError when cycle exists', () => {
|
|
77
|
+
class A {
|
|
78
|
+
}
|
|
79
|
+
class B {
|
|
80
|
+
}
|
|
81
|
+
const container = new Container();
|
|
82
|
+
container.register(A, A, { deps: [B] });
|
|
83
|
+
container.register(B, B, { deps: [A] });
|
|
84
|
+
strict_1.default.throws(() => container.resolve(A), (err) => err instanceof CircularDependencyError);
|
|
85
|
+
});
|
|
86
|
+
(0, node_test_1.default)('throws InvalidProviderError for invalid provider', () => {
|
|
87
|
+
const container = new Container();
|
|
88
|
+
container.register('invalid', {});
|
|
89
|
+
strict_1.default.throws(() => container.resolve('invalid'), (err) => err instanceof InvalidProviderError);
|
|
90
|
+
});
|
|
91
|
+
(0, node_test_1.default)('has, unregister, clear work as expected', () => {
|
|
92
|
+
const container = new Container();
|
|
93
|
+
container.registerValue('token', 123);
|
|
94
|
+
strict_1.default.equal(container.has('token'), true);
|
|
95
|
+
container.unregister('token');
|
|
96
|
+
strict_1.default.equal(container.has('token'), false);
|
|
97
|
+
container.registerValue('a', 1);
|
|
98
|
+
container.registerValue('b', 2);
|
|
99
|
+
container.clear();
|
|
100
|
+
strict_1.default.equal(container.has('a'), false);
|
|
101
|
+
strict_1.default.equal(container.has('b'), false);
|
|
102
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const dist_1 = require("../dist");
|
|
9
|
+
(0, node_test_1.default)('resolve class with deps from options', () => {
|
|
10
|
+
class Engine {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.kind = 'v8';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class Car {
|
|
16
|
+
constructor(engine) {
|
|
17
|
+
this.engine = engine;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const container = new dist_1.Container();
|
|
21
|
+
container.register(Engine, Engine);
|
|
22
|
+
container.register(Car, Car, { deps: [Engine] });
|
|
23
|
+
const car = container.resolve(Car);
|
|
24
|
+
strict_1.default.equal(car.engine.kind, 'v8');
|
|
25
|
+
});
|
|
26
|
+
(0, node_test_1.default)('registerClass infers token by class name', () => {
|
|
27
|
+
class Service {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.value = 42;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const container = new dist_1.Container();
|
|
33
|
+
container.registerClass(Service);
|
|
34
|
+
const resolved = container.resolve('Service');
|
|
35
|
+
strict_1.default.ok(resolved instanceof Service);
|
|
36
|
+
strict_1.default.equal(resolved.value, 42);
|
|
37
|
+
});
|
|
38
|
+
(0, node_test_1.default)('singleton class provider returns same instance', () => {
|
|
39
|
+
class Counter {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.count = 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const container = new dist_1.Container();
|
|
45
|
+
container.register(Counter, Counter, { singleton: true });
|
|
46
|
+
const a = container.resolve(Counter);
|
|
47
|
+
const b = container.resolve(Counter);
|
|
48
|
+
strict_1.default.equal(a, b);
|
|
49
|
+
});
|
|
50
|
+
(0, node_test_1.default)('value provider returns same value', () => {
|
|
51
|
+
const container = new dist_1.Container();
|
|
52
|
+
container.registerValue('config', { port: 3000 });
|
|
53
|
+
const config = container.resolve('config');
|
|
54
|
+
strict_1.default.deepEqual(config, { port: 3000 });
|
|
55
|
+
});
|
|
56
|
+
(0, node_test_1.default)('factory provider builds instance', () => {
|
|
57
|
+
const container = new dist_1.Container();
|
|
58
|
+
container.registerFactory('random', () => ({ value: Math.random() }));
|
|
59
|
+
const a = container.resolve('random');
|
|
60
|
+
const b = container.resolve('random');
|
|
61
|
+
strict_1.default.notEqual(a, b);
|
|
62
|
+
strict_1.default.equal(typeof a.value, 'number');
|
|
63
|
+
});
|
|
64
|
+
(0, node_test_1.default)('factory provider respects singleton', () => {
|
|
65
|
+
const container = new dist_1.Container();
|
|
66
|
+
container.registerFactory('singleton', () => ({ value: Math.random() }), { singleton: true });
|
|
67
|
+
const a = container.resolve('singleton');
|
|
68
|
+
const b = container.resolve('singleton');
|
|
69
|
+
strict_1.default.equal(a, b);
|
|
70
|
+
});
|
|
71
|
+
(0, node_test_1.default)('throws DependencyNotFoundError for missing token', () => {
|
|
72
|
+
const container = new dist_1.Container();
|
|
73
|
+
strict_1.default.throws(() => container.resolve('missing'), (err) => err instanceof dist_1.DependencyNotFoundError);
|
|
74
|
+
});
|
|
75
|
+
(0, node_test_1.default)('throws CircularDependencyError when cycle exists', () => {
|
|
76
|
+
class A {
|
|
77
|
+
}
|
|
78
|
+
class B {
|
|
79
|
+
}
|
|
80
|
+
const container = new dist_1.Container();
|
|
81
|
+
container.register(A, A, { deps: [B] });
|
|
82
|
+
container.register(B, B, { deps: [A] });
|
|
83
|
+
strict_1.default.throws(() => container.resolve(A), (err) => err instanceof dist_1.CircularDependencyError);
|
|
84
|
+
});
|
|
85
|
+
(0, node_test_1.default)('throws InvalidProviderError for invalid provider', () => {
|
|
86
|
+
const container = new dist_1.Container();
|
|
87
|
+
container.register('invalid', {});
|
|
88
|
+
strict_1.default.throws(() => container.resolve('invalid'), (err) => err instanceof dist_1.InvalidProviderError);
|
|
89
|
+
});
|
|
90
|
+
(0, node_test_1.default)('has, unregister, clear work as expected', () => {
|
|
91
|
+
const container = new dist_1.Container();
|
|
92
|
+
container.registerValue('token', 123);
|
|
93
|
+
strict_1.default.equal(container.has('token'), true);
|
|
94
|
+
container.unregister('token');
|
|
95
|
+
strict_1.default.equal(container.has('token'), false);
|
|
96
|
+
container.registerValue('a', 1);
|
|
97
|
+
container.registerValue('b', 2);
|
|
98
|
+
container.clear();
|
|
99
|
+
strict_1.default.equal(container.has('a'), false);
|
|
100
|
+
strict_1.default.equal(container.has('b'), false);
|
|
101
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syrinject",
|
|
3
3
|
"description": "Simple dependency injection container",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc -p tsconfig.build.json",
|
|
13
13
|
"dev": "tsc -w",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"test": "npm run build && tsc -p tsconfig.test.json && node --test \"dist/test/*.test.js\""
|
|
15
16
|
},
|
|
16
17
|
"keywords": [],
|
|
17
18
|
"author": "andersonreges",
|
|
18
19
|
"license": "ISC",
|
|
19
20
|
"type": "commonjs",
|
|
20
21
|
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.2.0",
|
|
21
23
|
"typescript": "^5.9.3"
|
|
22
24
|
}
|
|
23
25
|
}
|
package/dist/package.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "simple-di-container",
|
|
3
|
-
"description": "Simple dependency injection container",
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"files": ["dist"],
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsc -p tsconfig.build.json",
|
|
10
|
-
"dev": "tsc -w",
|
|
11
|
-
"prepublishOnly": "npm run build"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [],
|
|
14
|
-
"author": "andersonreges",
|
|
15
|
-
"license": "ISC",
|
|
16
|
-
"type": "commonjs",
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"typescript": "^5.9.3"
|
|
19
|
-
}
|
|
20
|
-
}
|