di-craft 0.0.10 → 0.0.11
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 -16
- package/dist/index.cjs +101 -27
- package/dist/index.d.cts +30 -13
- package/dist/index.d.mts +30 -13
- package/dist/index.mjs +100 -28
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
A tiny, type-safe dependency injection container for TypeScript.
|
|
4
4
|
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Quick start](#quick-start)
|
|
8
|
+
- [Philosophy](#philosophy)
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Install](#install)
|
|
11
|
+
- [Core concepts](#core-concepts)
|
|
12
|
+
- [Tokens](#tokens)
|
|
13
|
+
- [Providers](#providers)
|
|
14
|
+
- [Container](#container)
|
|
15
|
+
- [Scopes](#scopes)
|
|
16
|
+
- [Disposal](#disposal)
|
|
17
|
+
- [Child containers](#child-containers)
|
|
18
|
+
- [Cycle detection](#cycle-detection)
|
|
19
|
+
- [Example: per-request container](#example-per-request-container)
|
|
20
|
+
- [Error handling](#error-handling)
|
|
21
|
+
- [API reference](#api-reference)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
Declare typed tokens, describe how each one is built with a provider, then create
|
|
27
|
+
a container and resolve from it. Dependencies are wired explicitly through the
|
|
28
|
+
`deps` map and resolved for you.
|
|
29
|
+
|
|
5
30
|
```ts
|
|
6
31
|
import {
|
|
7
32
|
createContainer,
|
|
@@ -34,23 +59,17 @@ const users = container.get(USERS); // UserService, fully typed
|
|
|
34
59
|
|
|
35
60
|
## Philosophy
|
|
36
61
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- No `reflect-metadata`
|
|
41
|
-
- No framework dependencies
|
|
42
|
-
|
|
43
|
-
Just **tokens**, **providers**, a **container**, **scopes**, and **cycle detection**.
|
|
62
|
+
Dependency injection without the magic — no decorators, no `reflect-metadata`, no
|
|
63
|
+
framework coupling. You work with just **tokens**, **providers**, a **container**,
|
|
64
|
+
**scopes**, and **cycle detection**.
|
|
44
65
|
|
|
45
66
|
## Features
|
|
46
67
|
|
|
47
68
|
- Zero runtime dependencies
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- Explicit factories
|
|
53
|
-
- Singleton and transient scopes
|
|
69
|
+
- Type-safe tokens and factories
|
|
70
|
+
- Singleton, transient, and scoped lifetimes
|
|
71
|
+
- Hierarchical child containers
|
|
72
|
+
- Deterministic disposal with `onDispose` hooks
|
|
54
73
|
- Circular dependency detection
|
|
55
74
|
- Tree-shakable, tiny bundle size
|
|
56
75
|
- Ships both ESM and CommonJS builds
|
|
@@ -104,15 +123,34 @@ The keys in `deps` become the keys of the object passed to `useFactory`, each re
|
|
|
104
123
|
|
|
105
124
|
### Container
|
|
106
125
|
|
|
126
|
+
The container holds your providers and resolves values on demand. Create one
|
|
127
|
+
from a list of providers (all optional), then add more, check, resolve, and
|
|
128
|
+
dispose:
|
|
129
|
+
|
|
130
|
+
- `register(provider, options?)` — add a provider at any time.
|
|
131
|
+
- `has(token)` — whether a provider for the token is registered.
|
|
132
|
+
- `get(token)` — resolve the value, building and caching it as its scope dictates.
|
|
133
|
+
- `dispose()` — run `onDispose` hooks and release resolved instances.
|
|
134
|
+
|
|
107
135
|
```ts
|
|
108
136
|
const container = createContainer(providers); // providers are optional
|
|
109
137
|
|
|
110
138
|
container.register(provideValue(PORT, 3000)); // register more at any time
|
|
111
139
|
container.has(PORT); // true
|
|
112
140
|
container.get(PORT); // 3000
|
|
141
|
+
await container.dispose(); // release resolved singletons
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Registering the same token twice throws `DuplicateProviderError`. To replace an
|
|
145
|
+
existing provider on purpose (handy for tests, mocks, and environment-specific
|
|
146
|
+
overrides), pass `{ allowOverride: true }`:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
container.register(provideValue(API, fakeApi), { allowOverride: true });
|
|
113
150
|
```
|
|
114
151
|
|
|
115
|
-
|
|
152
|
+
Overriding a token whose value was already resolved as a singleton drops the
|
|
153
|
+
cached instance, so the next `get` rebuilds it from the new provider.
|
|
116
154
|
|
|
117
155
|
### Scopes
|
|
118
156
|
|
|
@@ -120,10 +158,15 @@ Registering the same token twice throws `DuplicateProviderError`.
|
|
|
120
158
|
| ---------------------- | ---------------------------------------------------------------- |
|
|
121
159
|
| `singleton` (default) | The factory runs once; the same instance is returned every time. |
|
|
122
160
|
| `transient` | The factory runs on every `get`, producing a fresh instance. |
|
|
161
|
+
| `scoped` | One instance per container. In a child container each child gets its own instance, while the provider can still be declared once on the parent. |
|
|
162
|
+
|
|
163
|
+
Use the `Scopes` helper for autocompletion, or pass the plain string — both work:
|
|
123
164
|
|
|
124
165
|
```ts
|
|
166
|
+
import { Scopes, provideFactory } from "di-craft";
|
|
167
|
+
|
|
125
168
|
provideFactory(ID, {
|
|
126
|
-
scope: "transient"
|
|
169
|
+
scope: Scopes.Transient, // or scope: "transient"
|
|
127
170
|
useFactory: () => crypto.randomUUID(),
|
|
128
171
|
});
|
|
129
172
|
|
|
@@ -132,6 +175,69 @@ container.get(ID) !== container.get(ID); // true
|
|
|
132
175
|
|
|
133
176
|
A transient provider that depends on a singleton still reuses the shared singleton instance.
|
|
134
177
|
|
|
178
|
+
### Disposal
|
|
179
|
+
|
|
180
|
+
Factory providers can declare an `onDispose` hook to release resources (database
|
|
181
|
+
pools, sockets, timers, subscriptions). Calling `container.dispose()` runs the
|
|
182
|
+
hooks for every resolved singleton and clears the cache:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
const DB = createToken<Pool>("db");
|
|
186
|
+
|
|
187
|
+
const container = createContainer([
|
|
188
|
+
provideFactory(DB, {
|
|
189
|
+
useFactory: () => createPool(url),
|
|
190
|
+
onDispose: (pool) => pool.end(), // may be sync or async
|
|
191
|
+
}),
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
container.get(DB);
|
|
195
|
+
|
|
196
|
+
await container.dispose(); // awaits async hooks, then clears instances
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Details:
|
|
200
|
+
|
|
201
|
+
- Hooks run in reverse creation order (dependents before their dependencies).
|
|
202
|
+
- `dispose()` returns a promise and awaits async hooks.
|
|
203
|
+
- It is idempotent — calling it again is a no-op.
|
|
204
|
+
- Only resolved singletons are disposed; transient and never-resolved instances are not tracked.
|
|
205
|
+
|
|
206
|
+
### Child containers
|
|
207
|
+
|
|
208
|
+
`createChildContainer(parent, providers?)` creates a child that inherits
|
|
209
|
+
everything from its parent but can add or override providers locally. This is the
|
|
210
|
+
typical pattern for per-request isolation on a server: shared services live on
|
|
211
|
+
the root, request-specific values live on a short-lived child.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const root = createContainer([
|
|
215
|
+
provideFactory(LOGGER, { useFactory: () => console }), // singleton, shared
|
|
216
|
+
provideFactory(HANDLER, {
|
|
217
|
+
scope: Scopes.Scoped, // one instance per child
|
|
218
|
+
deps: { request: REQUEST },
|
|
219
|
+
useFactory: ({ request }) => createHandler(request),
|
|
220
|
+
}),
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
function handle(request: Request) {
|
|
224
|
+
const child = createChildContainer(root, [provideValue(REQUEST, request)]);
|
|
225
|
+
|
|
226
|
+
child.get(LOGGER); // same logger as the root and every other child
|
|
227
|
+
child.get(HANDLER); // a fresh handler, unique to this child
|
|
228
|
+
|
|
229
|
+
return child.dispose(); // release only this child's instances
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
How resolution works across the chain:
|
|
234
|
+
|
|
235
|
+
- A token is looked up in the child first, then walks up to the parent.
|
|
236
|
+
- `singleton` is cached on the container that **owns** the provider, so it is shared by the whole subtree.
|
|
237
|
+
- `scoped` is cached on the **requesting** child, so each child gets its own instance — even when the provider is declared once on the parent.
|
|
238
|
+
- A `scoped` provider resolves its dependencies from the requesting child, so it can depend on values registered only in that child (like `REQUEST`).
|
|
239
|
+
- `dispose()` only releases the container it is called on; it does not cascade to parents or children.
|
|
240
|
+
|
|
135
241
|
### Cycle detection
|
|
136
242
|
|
|
137
243
|
If providers form a dependency cycle, resolution throws `CircularDependencyError` with the full path instead of overflowing the stack.
|
|
@@ -141,6 +247,87 @@ If providers form a dependency cycle, resolution throws `CircularDependencyError
|
|
|
141
247
|
container.get(A); // throws: Circular dependency detected: A -> B -> A
|
|
142
248
|
```
|
|
143
249
|
|
|
250
|
+
## Example: per-request container
|
|
251
|
+
|
|
252
|
+
A common server pattern: build one **root** container at startup with the
|
|
253
|
+
long-lived singletons (a database pool, clients, config), then fork a
|
|
254
|
+
short-lived **child** per request for request-specific state. `scoped` providers
|
|
255
|
+
give each request its own instance, and `dispose()` releases them once the
|
|
256
|
+
response is sent.
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import Fastify, { type FastifyRequest } from "fastify";
|
|
260
|
+
import {
|
|
261
|
+
type Container,
|
|
262
|
+
createContainer,
|
|
263
|
+
createChildContainer,
|
|
264
|
+
createToken,
|
|
265
|
+
provideValue,
|
|
266
|
+
provideFactory,
|
|
267
|
+
Scopes,
|
|
268
|
+
} from "di-craft";
|
|
269
|
+
|
|
270
|
+
const DB = createToken<Pool>("db");
|
|
271
|
+
const REQUEST = createToken<FastifyRequest>("request");
|
|
272
|
+
const REQUEST_ID = createToken<string>("request-id");
|
|
273
|
+
const USERS = createToken<UserService>("users");
|
|
274
|
+
|
|
275
|
+
const app = Fastify({ logger: true });
|
|
276
|
+
|
|
277
|
+
// Built once, shared by every request.
|
|
278
|
+
const root = createContainer([
|
|
279
|
+
provideFactory(DB, {
|
|
280
|
+
useFactory: () => createPool(process.env.DATABASE_URL),
|
|
281
|
+
onDispose: (pool) => pool.end(), // closed only on shutdown
|
|
282
|
+
}),
|
|
283
|
+
provideFactory(REQUEST_ID, {
|
|
284
|
+
scope: Scopes.Scoped, // one id per request
|
|
285
|
+
deps: { request: REQUEST }, // resolves the value registered on the child
|
|
286
|
+
useFactory: ({ request }) => request.id, // Fastify's built-in request id
|
|
287
|
+
}),
|
|
288
|
+
provideFactory(USERS, {
|
|
289
|
+
scope: Scopes.Scoped, // one service per request...
|
|
290
|
+
deps: { db: DB }, // ...but reuses the shared pool
|
|
291
|
+
useFactory: ({ db }) => new UserService(db),
|
|
292
|
+
}),
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
// Keep each request's child container without patching Fastify's types.
|
|
296
|
+
const containers = new WeakMap<FastifyRequest, Container>();
|
|
297
|
+
|
|
298
|
+
app.addHook("onRequest", async (request) => {
|
|
299
|
+
// A fresh child per request, seeded with the request object.
|
|
300
|
+
containers.set(request, createChildContainer(root, [provideValue(REQUEST, request)]));
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
app.addHook("onResponse", async (request) => {
|
|
304
|
+
// Release this request's scoped instances once the response is sent.
|
|
305
|
+
await containers.get(request)?.dispose();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
app.get<{ Params: { id: string } }>("/users/:id", async (request) => {
|
|
309
|
+
const container = containers.get(request)!;
|
|
310
|
+
const users = container.get(USERS);
|
|
311
|
+
const id = container.get(REQUEST_ID); // unique to this request
|
|
312
|
+
|
|
313
|
+
return users.findById(request.params.id, { traceId: id });
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// On shutdown, dispose the root to close the shared pool.
|
|
317
|
+
app.addHook("onClose", async () => {
|
|
318
|
+
await root.dispose();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await app.listen({ port: 3000 });
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
What each scope buys you here:
|
|
325
|
+
|
|
326
|
+
- `DB` is a **singleton** — created once on the root and reused by every request, so you don't open a new pool per call.
|
|
327
|
+
- `REQUEST_ID` and `USERS` are **scoped** — a new instance per child, so each request gets isolated state even though the providers are declared once on the root.
|
|
328
|
+
- `REQUEST` is a per-request **value** registered on the child, and the scoped `REQUEST_ID` resolves it from that same child.
|
|
329
|
+
- The child's `dispose()` releases only that request's scoped instances; the shared pool stays open until `root.dispose()` runs on `onClose`.
|
|
330
|
+
|
|
144
331
|
## Error handling
|
|
145
332
|
|
|
146
333
|
All errors extend the shared `DiError` base class, so you can catch any container error with a single check:
|
|
@@ -176,8 +363,10 @@ try {
|
|
|
176
363
|
| `provideValue(token, value)` | Provider that returns an existing value. |
|
|
177
364
|
| `provideFactory(token, options)` | Provider that builds a value via a factory. |
|
|
178
365
|
| `createContainer(providers?)` | Create a container, optionally seeded with providers. |
|
|
366
|
+
| `createChildContainer(parent, providers?)` | Create a child container that inherits from `parent`. |
|
|
367
|
+
| `Scopes` | Object of scope values (`Scopes.Singleton`, `Scopes.Transient`, `Scopes.Scoped`). |
|
|
179
368
|
|
|
180
|
-
Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Scope`.
|
|
369
|
+
Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Scope`, `DisposeHook`, `RegisterOptions`.
|
|
181
370
|
|
|
182
371
|
Exported errors: `DiError`, `MissingProviderError`, `DuplicateProviderError`, `CircularDependencyError`, `InvalidDependencyError`.
|
|
183
372
|
|
package/dist/index.cjs
CHANGED
|
@@ -19,8 +19,8 @@ var DuplicateProviderError = class extends DiError {
|
|
|
19
19
|
//#region src/registry/registry.ts
|
|
20
20
|
var RegistryClass = class {
|
|
21
21
|
providers = /* @__PURE__ */ new Map();
|
|
22
|
-
register(provider) {
|
|
23
|
-
if (this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
|
|
22
|
+
register(provider, options) {
|
|
23
|
+
if (!options?.allowOverride && this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
|
|
24
24
|
this.providers.set(provider.provide.id, provider);
|
|
25
25
|
}
|
|
26
26
|
get(token) {
|
|
@@ -52,6 +52,13 @@ var CircularDependencyError = class extends DiError {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
//#endregion
|
|
55
|
+
//#region src/scope/scope.ts
|
|
56
|
+
const Scopes = {
|
|
57
|
+
Singleton: "singleton",
|
|
58
|
+
Transient: "transient",
|
|
59
|
+
Scoped: "scoped"
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
55
62
|
//#region src/provider/provider.ts
|
|
56
63
|
const provideValue = (token, useValue) => ({
|
|
57
64
|
provide: token,
|
|
@@ -61,8 +68,9 @@ const provideFactory = (token, options) => {
|
|
|
61
68
|
return {
|
|
62
69
|
provide: token,
|
|
63
70
|
useFactory: options.useFactory,
|
|
71
|
+
scope: options.scope ?? Scopes.Singleton,
|
|
64
72
|
...options.deps ? { deps: options.deps } : {},
|
|
65
|
-
...options.
|
|
73
|
+
...options.onDispose ? { onDispose: options.onDispose } : {}
|
|
66
74
|
};
|
|
67
75
|
};
|
|
68
76
|
const isValueProvider = (provider) => {
|
|
@@ -72,44 +80,101 @@ const isFactoryProvider = (provider) => {
|
|
|
72
80
|
return "useFactory" in provider;
|
|
73
81
|
};
|
|
74
82
|
//#endregion
|
|
83
|
+
//#region src/store/store.ts
|
|
84
|
+
var StoreClass = class {
|
|
85
|
+
instances = /* @__PURE__ */ new Map();
|
|
86
|
+
get(token) {
|
|
87
|
+
return this.instances.get(token.id);
|
|
88
|
+
}
|
|
89
|
+
set(token, record) {
|
|
90
|
+
this.instances.set(token.id, record);
|
|
91
|
+
}
|
|
92
|
+
delete(token) {
|
|
93
|
+
this.instances.delete(token.id);
|
|
94
|
+
}
|
|
95
|
+
async dispose() {
|
|
96
|
+
const records = [...this.instances.values()].reverse();
|
|
97
|
+
this.instances.clear();
|
|
98
|
+
for (const record of records) if (record.onDispose) await record.onDispose(record.value);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const createStore = () => new StoreClass();
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/resolver/context.ts
|
|
104
|
+
var ResolutionContext = class {
|
|
105
|
+
resolving = /* @__PURE__ */ new Set();
|
|
106
|
+
path = [];
|
|
107
|
+
enter(token) {
|
|
108
|
+
if (this.resolving.has(token.id)) {
|
|
109
|
+
const cycleStartIndex = this.path.findIndex((pathToken) => pathToken.id === token.id);
|
|
110
|
+
throw new CircularDependencyError([...this.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
|
|
111
|
+
}
|
|
112
|
+
this.resolving.add(token.id);
|
|
113
|
+
this.path.push(token);
|
|
114
|
+
}
|
|
115
|
+
exit(token) {
|
|
116
|
+
this.path.pop();
|
|
117
|
+
this.resolving.delete(token.id);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
75
121
|
//#region src/resolver/resolver.ts
|
|
76
122
|
var ResolverClass = class {
|
|
77
123
|
registry;
|
|
78
|
-
|
|
79
|
-
|
|
124
|
+
store = createStore();
|
|
125
|
+
parent;
|
|
126
|
+
constructor(registry, parent) {
|
|
80
127
|
this.registry = registry;
|
|
128
|
+
this.parent = parent;
|
|
81
129
|
}
|
|
82
130
|
resolve(token) {
|
|
83
|
-
return this.resolveToken(token,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
131
|
+
return this.resolveToken(token, new ResolutionContext());
|
|
132
|
+
}
|
|
133
|
+
invalidate(token) {
|
|
134
|
+
this.store.delete(token);
|
|
135
|
+
}
|
|
136
|
+
dispose() {
|
|
137
|
+
return this.store.dispose();
|
|
87
138
|
}
|
|
88
139
|
resolveToken(token, context) {
|
|
89
|
-
const
|
|
140
|
+
const owner = this.findOwner(token);
|
|
141
|
+
if (!owner) throw new MissingProviderError(token.name);
|
|
142
|
+
const provider = owner.registry.get(token);
|
|
90
143
|
if (!provider) throw new MissingProviderError(token.name);
|
|
91
144
|
if (isValueProvider(provider)) return provider.useValue;
|
|
92
145
|
if (isFactoryProvider(provider)) {
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
throw new CircularDependencyError([...context.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
|
|
146
|
+
const host = this.selectHost(provider.scope, owner);
|
|
147
|
+
if (host) {
|
|
148
|
+
const cached = host.store.get(token);
|
|
149
|
+
if (cached) return cached.value;
|
|
98
150
|
}
|
|
99
|
-
context.
|
|
100
|
-
context.path.push(token);
|
|
151
|
+
context.enter(token);
|
|
101
152
|
try {
|
|
102
|
-
const deps = this.resolveDeps(provider.deps, context);
|
|
153
|
+
const deps = (host ?? this).resolveDeps(provider.deps, context);
|
|
103
154
|
const value = provider.useFactory(deps);
|
|
104
|
-
if (
|
|
155
|
+
if (host) host.store.set(token, {
|
|
156
|
+
value,
|
|
157
|
+
...provider.onDispose ? { onDispose: provider.onDispose } : {}
|
|
158
|
+
});
|
|
105
159
|
return value;
|
|
106
160
|
} finally {
|
|
107
|
-
context.
|
|
108
|
-
context.resolving.delete(token.id);
|
|
161
|
+
context.exit(token);
|
|
109
162
|
}
|
|
110
163
|
}
|
|
111
164
|
throw new MissingProviderError(token.name);
|
|
112
165
|
}
|
|
166
|
+
findOwner(token) {
|
|
167
|
+
let resolver = this;
|
|
168
|
+
while (resolver) {
|
|
169
|
+
if (resolver.registry.has(token)) return resolver;
|
|
170
|
+
resolver = resolver.parent;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
selectHost(scope, owner) {
|
|
174
|
+
if (scope === Scopes.Transient) return;
|
|
175
|
+
if (scope === Scopes.Scoped) return this;
|
|
176
|
+
return owner;
|
|
177
|
+
}
|
|
113
178
|
resolveDeps(deps, context) {
|
|
114
179
|
if (!deps) return {};
|
|
115
180
|
const resolvedDeps = {};
|
|
@@ -121,28 +186,35 @@ var ResolverClass = class {
|
|
|
121
186
|
return resolvedDeps;
|
|
122
187
|
}
|
|
123
188
|
};
|
|
124
|
-
const createResolver = (registry) => new ResolverClass(registry);
|
|
189
|
+
const createResolver = (registry, parent) => new ResolverClass(registry, parent);
|
|
125
190
|
//#endregion
|
|
126
191
|
//#region src/container/container.ts
|
|
127
192
|
var ContainerClass = class {
|
|
128
193
|
registry;
|
|
129
194
|
resolver;
|
|
130
|
-
|
|
195
|
+
parent;
|
|
196
|
+
constructor(providers = [], parent) {
|
|
197
|
+
this.parent = parent;
|
|
131
198
|
this.registry = createRegistry();
|
|
132
199
|
for (const provider of providers) this.registry.register(provider);
|
|
133
|
-
this.resolver = createResolver(this.registry);
|
|
200
|
+
this.resolver = createResolver(this.registry, parent?.resolver);
|
|
134
201
|
}
|
|
135
|
-
register(provider) {
|
|
136
|
-
this.registry.register(provider);
|
|
202
|
+
register(provider, options) {
|
|
203
|
+
this.registry.register(provider, options);
|
|
204
|
+
if (options?.allowOverride) this.resolver.invalidate(provider.provide);
|
|
137
205
|
}
|
|
138
206
|
get(token) {
|
|
139
207
|
return this.resolver.resolve(token);
|
|
140
208
|
}
|
|
141
209
|
has(token) {
|
|
142
|
-
return this.registry.has(token);
|
|
210
|
+
return this.registry.has(token) || (this.parent?.has(token) ?? false);
|
|
211
|
+
}
|
|
212
|
+
dispose() {
|
|
213
|
+
return this.resolver.dispose();
|
|
143
214
|
}
|
|
144
215
|
};
|
|
145
216
|
const createContainer = (providers = []) => new ContainerClass(providers);
|
|
217
|
+
const createChildContainer = (parent, providers = []) => new ContainerClass(providers, parent);
|
|
146
218
|
//#endregion
|
|
147
219
|
//#region src/token/token.ts
|
|
148
220
|
var TokenClass = class {
|
|
@@ -160,6 +232,8 @@ exports.DiError = DiError;
|
|
|
160
232
|
exports.DuplicateProviderError = DuplicateProviderError;
|
|
161
233
|
exports.InvalidDependencyError = InvalidDependencyError;
|
|
162
234
|
exports.MissingProviderError = MissingProviderError;
|
|
235
|
+
exports.Scopes = Scopes;
|
|
236
|
+
exports.createChildContainer = createChildContainer;
|
|
163
237
|
exports.createContainer = createContainer;
|
|
164
238
|
exports.createToken = createToken;
|
|
165
239
|
exports.provideFactory = provideFactory;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
//#region src/scope/scope.d.ts
|
|
2
|
+
declare const Scopes: {
|
|
3
|
+
readonly Singleton: "singleton";
|
|
4
|
+
readonly Transient: "transient";
|
|
5
|
+
readonly Scoped: "scoped";
|
|
6
|
+
};
|
|
7
|
+
type Scope = (typeof Scopes)[keyof typeof Scopes];
|
|
8
|
+
//#endregion
|
|
1
9
|
//#region src/token/types.d.ts
|
|
2
10
|
type Token<T> = {
|
|
3
11
|
readonly id: symbol;
|
|
@@ -13,6 +21,7 @@ type DepsMap = Record<string, Token<unknown>>;
|
|
|
13
21
|
type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
|
|
14
22
|
type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: TokenValue<TDeps[TKey]> };
|
|
15
23
|
type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
|
|
24
|
+
type DisposeHook<T> = (instance: T) => void | Promise<void>;
|
|
16
25
|
type ValueProvider<T> = {
|
|
17
26
|
readonly provide: Token<T>;
|
|
18
27
|
readonly useValue: T;
|
|
@@ -22,10 +31,10 @@ type FactoryProvider<T, TDeps extends DepsMap = Record<never, never>> = {
|
|
|
22
31
|
readonly deps?: TDeps;
|
|
23
32
|
readonly scope?: Scope;
|
|
24
33
|
readonly useFactory: Factory<T, TDeps>;
|
|
34
|
+
readonly onDispose?: DisposeHook<T>;
|
|
25
35
|
};
|
|
26
|
-
type AnyFactoryProvider = FactoryProvider<
|
|
36
|
+
type AnyFactoryProvider = FactoryProvider<any, any>;
|
|
27
37
|
type Provider = ValueProvider<unknown> | AnyFactoryProvider;
|
|
28
|
-
type Scope = "singleton" | "transient";
|
|
29
38
|
//#endregion
|
|
30
39
|
//#region src/provider/provider.d.ts
|
|
31
40
|
declare const provideValue: <T>(token: Token<T>, useValue: T) => ValueProvider<T>;
|
|
@@ -33,18 +42,9 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
|
|
|
33
42
|
readonly deps?: TDeps;
|
|
34
43
|
readonly scope?: Scope;
|
|
35
44
|
readonly useFactory: Factory<T, TDeps>;
|
|
45
|
+
readonly onDispose?: DisposeHook<T>;
|
|
36
46
|
}) => FactoryProvider<T, TDeps>;
|
|
37
47
|
//#endregion
|
|
38
|
-
//#region src/container/types.d.ts
|
|
39
|
-
type Container = {
|
|
40
|
-
register(provider: Provider): void;
|
|
41
|
-
get<T>(token: Token<T>): T;
|
|
42
|
-
has(token: Token<unknown>): boolean;
|
|
43
|
-
};
|
|
44
|
-
//#endregion
|
|
45
|
-
//#region src/container/container.d.ts
|
|
46
|
-
declare const createContainer: (providers?: readonly Provider[]) => Container;
|
|
47
|
-
//#endregion
|
|
48
48
|
//#region src/error/error.d.ts
|
|
49
49
|
declare class DiError extends Error {
|
|
50
50
|
constructor(message: string);
|
|
@@ -55,6 +55,23 @@ declare class DuplicateProviderError extends DiError {
|
|
|
55
55
|
constructor(tokenName: string);
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
|
+
//#region src/registry/types.d.ts
|
|
59
|
+
type RegisterOptions = {
|
|
60
|
+
readonly allowOverride?: boolean;
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/container/types.d.ts
|
|
64
|
+
type Container = {
|
|
65
|
+
register(provider: Provider, options?: RegisterOptions): void;
|
|
66
|
+
get<T>(token: Token<T>): T;
|
|
67
|
+
has(token: Token<unknown>): boolean;
|
|
68
|
+
dispose(): Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/container/container.d.ts
|
|
72
|
+
declare const createContainer: (providers?: readonly Provider[]) => Container;
|
|
73
|
+
declare const createChildContainer: (parent: Container, providers?: readonly Provider[]) => Container;
|
|
74
|
+
//#endregion
|
|
58
75
|
//#region src/resolver/errors.d.ts
|
|
59
76
|
declare class MissingProviderError extends DiError {
|
|
60
77
|
constructor(tokenName: string);
|
|
@@ -66,4 +83,4 @@ declare class CircularDependencyError extends DiError {
|
|
|
66
83
|
constructor(tokenNames: string[]);
|
|
67
84
|
}
|
|
68
85
|
//#endregion
|
|
69
|
-
export { CircularDependencyError, type Container, DiError, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type Scope, type Token, type ValueProvider, createContainer, createToken, provideFactory, provideValue };
|
|
86
|
+
export { CircularDependencyError, type Container, DiError, type DisposeHook, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type RegisterOptions, type Scope, Scopes, type Token, type ValueProvider, createChildContainer, createContainer, createToken, provideFactory, provideValue };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
//#region src/scope/scope.d.ts
|
|
2
|
+
declare const Scopes: {
|
|
3
|
+
readonly Singleton: "singleton";
|
|
4
|
+
readonly Transient: "transient";
|
|
5
|
+
readonly Scoped: "scoped";
|
|
6
|
+
};
|
|
7
|
+
type Scope = (typeof Scopes)[keyof typeof Scopes];
|
|
8
|
+
//#endregion
|
|
1
9
|
//#region src/token/types.d.ts
|
|
2
10
|
type Token<T> = {
|
|
3
11
|
readonly id: symbol;
|
|
@@ -13,6 +21,7 @@ type DepsMap = Record<string, Token<unknown>>;
|
|
|
13
21
|
type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
|
|
14
22
|
type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: TokenValue<TDeps[TKey]> };
|
|
15
23
|
type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
|
|
24
|
+
type DisposeHook<T> = (instance: T) => void | Promise<void>;
|
|
16
25
|
type ValueProvider<T> = {
|
|
17
26
|
readonly provide: Token<T>;
|
|
18
27
|
readonly useValue: T;
|
|
@@ -22,10 +31,10 @@ type FactoryProvider<T, TDeps extends DepsMap = Record<never, never>> = {
|
|
|
22
31
|
readonly deps?: TDeps;
|
|
23
32
|
readonly scope?: Scope;
|
|
24
33
|
readonly useFactory: Factory<T, TDeps>;
|
|
34
|
+
readonly onDispose?: DisposeHook<T>;
|
|
25
35
|
};
|
|
26
|
-
type AnyFactoryProvider = FactoryProvider<
|
|
36
|
+
type AnyFactoryProvider = FactoryProvider<any, any>;
|
|
27
37
|
type Provider = ValueProvider<unknown> | AnyFactoryProvider;
|
|
28
|
-
type Scope = "singleton" | "transient";
|
|
29
38
|
//#endregion
|
|
30
39
|
//#region src/provider/provider.d.ts
|
|
31
40
|
declare const provideValue: <T>(token: Token<T>, useValue: T) => ValueProvider<T>;
|
|
@@ -33,18 +42,9 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
|
|
|
33
42
|
readonly deps?: TDeps;
|
|
34
43
|
readonly scope?: Scope;
|
|
35
44
|
readonly useFactory: Factory<T, TDeps>;
|
|
45
|
+
readonly onDispose?: DisposeHook<T>;
|
|
36
46
|
}) => FactoryProvider<T, TDeps>;
|
|
37
47
|
//#endregion
|
|
38
|
-
//#region src/container/types.d.ts
|
|
39
|
-
type Container = {
|
|
40
|
-
register(provider: Provider): void;
|
|
41
|
-
get<T>(token: Token<T>): T;
|
|
42
|
-
has(token: Token<unknown>): boolean;
|
|
43
|
-
};
|
|
44
|
-
//#endregion
|
|
45
|
-
//#region src/container/container.d.ts
|
|
46
|
-
declare const createContainer: (providers?: readonly Provider[]) => Container;
|
|
47
|
-
//#endregion
|
|
48
48
|
//#region src/error/error.d.ts
|
|
49
49
|
declare class DiError extends Error {
|
|
50
50
|
constructor(message: string);
|
|
@@ -55,6 +55,23 @@ declare class DuplicateProviderError extends DiError {
|
|
|
55
55
|
constructor(tokenName: string);
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
|
+
//#region src/registry/types.d.ts
|
|
59
|
+
type RegisterOptions = {
|
|
60
|
+
readonly allowOverride?: boolean;
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/container/types.d.ts
|
|
64
|
+
type Container = {
|
|
65
|
+
register(provider: Provider, options?: RegisterOptions): void;
|
|
66
|
+
get<T>(token: Token<T>): T;
|
|
67
|
+
has(token: Token<unknown>): boolean;
|
|
68
|
+
dispose(): Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/container/container.d.ts
|
|
72
|
+
declare const createContainer: (providers?: readonly Provider[]) => Container;
|
|
73
|
+
declare const createChildContainer: (parent: Container, providers?: readonly Provider[]) => Container;
|
|
74
|
+
//#endregion
|
|
58
75
|
//#region src/resolver/errors.d.ts
|
|
59
76
|
declare class MissingProviderError extends DiError {
|
|
60
77
|
constructor(tokenName: string);
|
|
@@ -66,4 +83,4 @@ declare class CircularDependencyError extends DiError {
|
|
|
66
83
|
constructor(tokenNames: string[]);
|
|
67
84
|
}
|
|
68
85
|
//#endregion
|
|
69
|
-
export { CircularDependencyError, type Container, DiError, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type Scope, type Token, type ValueProvider, createContainer, createToken, provideFactory, provideValue };
|
|
86
|
+
export { CircularDependencyError, type Container, DiError, type DisposeHook, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type RegisterOptions, type Scope, Scopes, type Token, type ValueProvider, createChildContainer, createContainer, createToken, provideFactory, provideValue };
|
package/dist/index.mjs
CHANGED
|
@@ -18,8 +18,8 @@ var DuplicateProviderError = class extends DiError {
|
|
|
18
18
|
//#region src/registry/registry.ts
|
|
19
19
|
var RegistryClass = class {
|
|
20
20
|
providers = /* @__PURE__ */ new Map();
|
|
21
|
-
register(provider) {
|
|
22
|
-
if (this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
|
|
21
|
+
register(provider, options) {
|
|
22
|
+
if (!options?.allowOverride && this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
|
|
23
23
|
this.providers.set(provider.provide.id, provider);
|
|
24
24
|
}
|
|
25
25
|
get(token) {
|
|
@@ -51,6 +51,13 @@ var CircularDependencyError = class extends DiError {
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
//#endregion
|
|
54
|
+
//#region src/scope/scope.ts
|
|
55
|
+
const Scopes = {
|
|
56
|
+
Singleton: "singleton",
|
|
57
|
+
Transient: "transient",
|
|
58
|
+
Scoped: "scoped"
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
54
61
|
//#region src/provider/provider.ts
|
|
55
62
|
const provideValue = (token, useValue) => ({
|
|
56
63
|
provide: token,
|
|
@@ -60,8 +67,9 @@ const provideFactory = (token, options) => {
|
|
|
60
67
|
return {
|
|
61
68
|
provide: token,
|
|
62
69
|
useFactory: options.useFactory,
|
|
70
|
+
scope: options.scope ?? Scopes.Singleton,
|
|
63
71
|
...options.deps ? { deps: options.deps } : {},
|
|
64
|
-
...options.
|
|
72
|
+
...options.onDispose ? { onDispose: options.onDispose } : {}
|
|
65
73
|
};
|
|
66
74
|
};
|
|
67
75
|
const isValueProvider = (provider) => {
|
|
@@ -71,44 +79,101 @@ const isFactoryProvider = (provider) => {
|
|
|
71
79
|
return "useFactory" in provider;
|
|
72
80
|
};
|
|
73
81
|
//#endregion
|
|
82
|
+
//#region src/store/store.ts
|
|
83
|
+
var StoreClass = class {
|
|
84
|
+
instances = /* @__PURE__ */ new Map();
|
|
85
|
+
get(token) {
|
|
86
|
+
return this.instances.get(token.id);
|
|
87
|
+
}
|
|
88
|
+
set(token, record) {
|
|
89
|
+
this.instances.set(token.id, record);
|
|
90
|
+
}
|
|
91
|
+
delete(token) {
|
|
92
|
+
this.instances.delete(token.id);
|
|
93
|
+
}
|
|
94
|
+
async dispose() {
|
|
95
|
+
const records = [...this.instances.values()].reverse();
|
|
96
|
+
this.instances.clear();
|
|
97
|
+
for (const record of records) if (record.onDispose) await record.onDispose(record.value);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const createStore = () => new StoreClass();
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/resolver/context.ts
|
|
103
|
+
var ResolutionContext = class {
|
|
104
|
+
resolving = /* @__PURE__ */ new Set();
|
|
105
|
+
path = [];
|
|
106
|
+
enter(token) {
|
|
107
|
+
if (this.resolving.has(token.id)) {
|
|
108
|
+
const cycleStartIndex = this.path.findIndex((pathToken) => pathToken.id === token.id);
|
|
109
|
+
throw new CircularDependencyError([...this.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
|
|
110
|
+
}
|
|
111
|
+
this.resolving.add(token.id);
|
|
112
|
+
this.path.push(token);
|
|
113
|
+
}
|
|
114
|
+
exit(token) {
|
|
115
|
+
this.path.pop();
|
|
116
|
+
this.resolving.delete(token.id);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
74
120
|
//#region src/resolver/resolver.ts
|
|
75
121
|
var ResolverClass = class {
|
|
76
122
|
registry;
|
|
77
|
-
|
|
78
|
-
|
|
123
|
+
store = createStore();
|
|
124
|
+
parent;
|
|
125
|
+
constructor(registry, parent) {
|
|
79
126
|
this.registry = registry;
|
|
127
|
+
this.parent = parent;
|
|
80
128
|
}
|
|
81
129
|
resolve(token) {
|
|
82
|
-
return this.resolveToken(token,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
130
|
+
return this.resolveToken(token, new ResolutionContext());
|
|
131
|
+
}
|
|
132
|
+
invalidate(token) {
|
|
133
|
+
this.store.delete(token);
|
|
134
|
+
}
|
|
135
|
+
dispose() {
|
|
136
|
+
return this.store.dispose();
|
|
86
137
|
}
|
|
87
138
|
resolveToken(token, context) {
|
|
88
|
-
const
|
|
139
|
+
const owner = this.findOwner(token);
|
|
140
|
+
if (!owner) throw new MissingProviderError(token.name);
|
|
141
|
+
const provider = owner.registry.get(token);
|
|
89
142
|
if (!provider) throw new MissingProviderError(token.name);
|
|
90
143
|
if (isValueProvider(provider)) return provider.useValue;
|
|
91
144
|
if (isFactoryProvider(provider)) {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
throw new CircularDependencyError([...context.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
|
|
145
|
+
const host = this.selectHost(provider.scope, owner);
|
|
146
|
+
if (host) {
|
|
147
|
+
const cached = host.store.get(token);
|
|
148
|
+
if (cached) return cached.value;
|
|
97
149
|
}
|
|
98
|
-
context.
|
|
99
|
-
context.path.push(token);
|
|
150
|
+
context.enter(token);
|
|
100
151
|
try {
|
|
101
|
-
const deps = this.resolveDeps(provider.deps, context);
|
|
152
|
+
const deps = (host ?? this).resolveDeps(provider.deps, context);
|
|
102
153
|
const value = provider.useFactory(deps);
|
|
103
|
-
if (
|
|
154
|
+
if (host) host.store.set(token, {
|
|
155
|
+
value,
|
|
156
|
+
...provider.onDispose ? { onDispose: provider.onDispose } : {}
|
|
157
|
+
});
|
|
104
158
|
return value;
|
|
105
159
|
} finally {
|
|
106
|
-
context.
|
|
107
|
-
context.resolving.delete(token.id);
|
|
160
|
+
context.exit(token);
|
|
108
161
|
}
|
|
109
162
|
}
|
|
110
163
|
throw new MissingProviderError(token.name);
|
|
111
164
|
}
|
|
165
|
+
findOwner(token) {
|
|
166
|
+
let resolver = this;
|
|
167
|
+
while (resolver) {
|
|
168
|
+
if (resolver.registry.has(token)) return resolver;
|
|
169
|
+
resolver = resolver.parent;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
selectHost(scope, owner) {
|
|
173
|
+
if (scope === Scopes.Transient) return;
|
|
174
|
+
if (scope === Scopes.Scoped) return this;
|
|
175
|
+
return owner;
|
|
176
|
+
}
|
|
112
177
|
resolveDeps(deps, context) {
|
|
113
178
|
if (!deps) return {};
|
|
114
179
|
const resolvedDeps = {};
|
|
@@ -120,28 +185,35 @@ var ResolverClass = class {
|
|
|
120
185
|
return resolvedDeps;
|
|
121
186
|
}
|
|
122
187
|
};
|
|
123
|
-
const createResolver = (registry) => new ResolverClass(registry);
|
|
188
|
+
const createResolver = (registry, parent) => new ResolverClass(registry, parent);
|
|
124
189
|
//#endregion
|
|
125
190
|
//#region src/container/container.ts
|
|
126
191
|
var ContainerClass = class {
|
|
127
192
|
registry;
|
|
128
193
|
resolver;
|
|
129
|
-
|
|
194
|
+
parent;
|
|
195
|
+
constructor(providers = [], parent) {
|
|
196
|
+
this.parent = parent;
|
|
130
197
|
this.registry = createRegistry();
|
|
131
198
|
for (const provider of providers) this.registry.register(provider);
|
|
132
|
-
this.resolver = createResolver(this.registry);
|
|
199
|
+
this.resolver = createResolver(this.registry, parent?.resolver);
|
|
133
200
|
}
|
|
134
|
-
register(provider) {
|
|
135
|
-
this.registry.register(provider);
|
|
201
|
+
register(provider, options) {
|
|
202
|
+
this.registry.register(provider, options);
|
|
203
|
+
if (options?.allowOverride) this.resolver.invalidate(provider.provide);
|
|
136
204
|
}
|
|
137
205
|
get(token) {
|
|
138
206
|
return this.resolver.resolve(token);
|
|
139
207
|
}
|
|
140
208
|
has(token) {
|
|
141
|
-
return this.registry.has(token);
|
|
209
|
+
return this.registry.has(token) || (this.parent?.has(token) ?? false);
|
|
210
|
+
}
|
|
211
|
+
dispose() {
|
|
212
|
+
return this.resolver.dispose();
|
|
142
213
|
}
|
|
143
214
|
};
|
|
144
215
|
const createContainer = (providers = []) => new ContainerClass(providers);
|
|
216
|
+
const createChildContainer = (parent, providers = []) => new ContainerClass(providers, parent);
|
|
145
217
|
//#endregion
|
|
146
218
|
//#region src/token/token.ts
|
|
147
219
|
var TokenClass = class {
|
|
@@ -154,4 +226,4 @@ var TokenClass = class {
|
|
|
154
226
|
};
|
|
155
227
|
const createToken = (name) => new TokenClass(name);
|
|
156
228
|
//#endregion
|
|
157
|
-
export { CircularDependencyError, DiError, DuplicateProviderError, InvalidDependencyError, MissingProviderError, createContainer, createToken, provideFactory, provideValue };
|
|
229
|
+
export { CircularDependencyError, DiError, DuplicateProviderError, InvalidDependencyError, MissingProviderError, Scopes, createChildContainer, createContainer, createToken, provideFactory, provideValue };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "di-craft",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A tiny
|
|
3
|
+
"version": "0.0.11",
|
|
4
|
+
"description": "A tiny, type-safe dependency injection container for TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Egor Bezmen",
|