di-craft 0.0.10 → 0.0.12

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
@@ -2,6 +2,33 @@
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
+ - [Optional dependencies](#optional-dependencies)
15
+ - [Container](#container)
16
+ - [Scopes](#scopes)
17
+ - [Disposal](#disposal)
18
+ - [Child containers](#child-containers)
19
+ - [Cycle detection](#cycle-detection)
20
+ - [Async dependencies](#async-dependencies)
21
+ - [Dependency injection vs service location](#dependency-injection-vs-service-location)
22
+ - [Error handling](#error-handling)
23
+ - [API reference](#api-reference)
24
+ - [License](#license)
25
+
26
+ ## Quick start
27
+
28
+ Declare typed tokens, describe how each one is built with a provider, then create
29
+ a container and resolve from it. Dependencies are wired explicitly through the
30
+ `deps` map and resolved for you.
31
+
5
32
  ```ts
6
33
  import {
7
34
  createContainer,
@@ -34,23 +61,18 @@ const users = container.get(USERS); // UserService, fully typed
34
61
 
35
62
  ## Philosophy
36
63
 
37
- `di-craft` is a small DI container with no magic.
38
-
39
- - No decorators
40
- - No `reflect-metadata`
41
- - No framework dependencies
42
-
43
- Just **tokens**, **providers**, a **container**, **scopes**, and **cycle detection**.
64
+ Dependency injection without the magic no decorators, no `reflect-metadata`, no
65
+ framework coupling. You work with just **tokens**, **providers**, a **container**,
66
+ **scopes**, and **cycle detection**.
44
67
 
45
68
  ## Features
46
69
 
47
70
  - Zero runtime dependencies
48
- - No decorators
49
- - No `reflect-metadata`
50
- - Framework agnostic
51
- - Type-safe tokens
52
- - Explicit factories
53
- - Singleton and transient scopes
71
+ - Type-safe tokens and factories
72
+ - Optional dependencies via `optional()`
73
+ - Singleton, transient, and scoped lifetimes
74
+ - Hierarchical child containers
75
+ - Deterministic disposal with `onDispose` hooks
54
76
  - Circular dependency detection
55
77
  - Tree-shakable, tiny bundle size
56
78
  - Ships both ESM and CommonJS builds
@@ -102,17 +124,70 @@ provideFactory(HTTP, {
102
124
 
103
125
  The keys in `deps` become the keys of the object passed to `useFactory`, each resolved to its token's type.
104
126
 
127
+ ### Optional dependencies
128
+
129
+ Wrap a token with `optional` to mark a dependency as not required. When no
130
+ provider for it is registered anywhere in the container chain, the factory
131
+ receives `undefined` instead of the resolution throwing `MissingProviderError`.
132
+ The inferred type is widened to `T | undefined`, so TypeScript forces you to
133
+ handle the absent case:
134
+
135
+ ```ts
136
+ import { optional, provideFactory } from "di-craft";
137
+
138
+ provideFactory(USERS, {
139
+ deps: { logger: optional(LOGGER) }, // LOGGER may or may not be registered
140
+ useFactory: ({ logger }) => {
141
+ logger?.info("creating users service"); // logger: Logger | undefined
142
+ return new UsersService();
143
+ },
144
+ });
145
+ ```
146
+
147
+ The same descriptor works at the top level — `optional` can be passed anywhere a
148
+ dependency is accepted, including `container.get`:
149
+
150
+ ```ts
151
+ const logger = container.get(optional(LOGGER)); // Logger | undefined
152
+ ```
153
+
154
+ Optional only affects the token itself: if a provider _is_ registered, it is
155
+ resolved normally and its own errors (cycles, missing nested deps) still
156
+ surface.
157
+
105
158
  ### Container
106
159
 
160
+ The container holds your providers and resolves values on demand. Create one
161
+ from a list of providers (all optional), then add more, check, resolve, and
162
+ dispose:
163
+
164
+ - `register(provider, options?)` — add a provider at any time.
165
+ - `has(token)` — whether a provider for the token is registered.
166
+ - `get(token)` — resolve the value, building and caching it as its scope dictates. Accepts `optional(token)` to get `undefined` instead of throwing when absent.
167
+ - `dispose()` — run `onDispose` hooks and release resolved instances.
168
+
107
169
  ```ts
108
170
  const container = createContainer(providers); // providers are optional
109
171
 
110
172
  container.register(provideValue(PORT, 3000)); // register more at any time
111
173
  container.has(PORT); // true
112
174
  container.get(PORT); // 3000
175
+ await container.dispose(); // release resolved singletons
176
+ ```
177
+
178
+ Registering the same token twice throws `DuplicateProviderError`. To replace an
179
+ existing provider on purpose (handy for tests, mocks, and environment-specific
180
+ overrides), pass `{ allowOverride: true }`:
181
+
182
+ ```ts
183
+ container.register(provideValue(API, fakeApi), { allowOverride: true });
113
184
  ```
114
185
 
115
- Registering the same token twice throws `DuplicateProviderError`.
186
+ Overriding a token whose value was already resolved as a singleton drops the
187
+ cached instance, so the next `get` rebuilds it from the new provider. If that
188
+ resolved instance has an `onDispose` hook, the override throws
189
+ `InvalidProviderError` instead of silently dropping it — call `dispose()` first
190
+ so the resource is released, then register the replacement.
116
191
 
117
192
  ### Scopes
118
193
 
@@ -120,17 +195,91 @@ Registering the same token twice throws `DuplicateProviderError`.
120
195
  | ---------------------- | ---------------------------------------------------------------- |
121
196
  | `singleton` (default) | The factory runs once; the same instance is returned every time. |
122
197
  | `transient` | The factory runs on every `get`, producing a fresh instance. |
198
+ | `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. |
199
+
200
+ Use the `Scopes` helper for autocompletion, or pass the plain string — both work:
123
201
 
124
202
  ```ts
203
+ import { Scopes, provideFactory } from "di-craft";
204
+
125
205
  provideFactory(ID, {
126
- scope: "transient",
206
+ scope: Scopes.Transient, // or scope: "transient"
127
207
  useFactory: () => crypto.randomUUID(),
128
208
  });
129
209
 
130
210
  container.get(ID) !== container.get(ID); // true
131
211
  ```
132
212
 
133
- A transient provider that depends on a singleton still reuses the shared singleton instance.
213
+ A provider may only depend on dependencies that live **at least as long** as
214
+ itself, so a longer-lived instance never captures a shorter-lived one. A
215
+ transient may depend on anything; a scoped may depend on scoped or singleton; a
216
+ singleton may depend only on singletons (and values). Violating this throws
217
+ `InvalidProviderError` at resolution. A transient that depends on a singleton, for
218
+ example, reuses the shared singleton instance.
219
+
220
+ ### Disposal
221
+
222
+ Factory providers can declare an `onDispose` hook to release resources (database
223
+ pools, sockets, timers, subscriptions). Calling `container.dispose()` runs the
224
+ hooks for every resolved singleton and clears the cache:
225
+
226
+ ```ts
227
+ const DB = createToken<Pool>("db");
228
+
229
+ const container = createContainer([
230
+ provideFactory(DB, {
231
+ useFactory: () => createPool(url),
232
+ onDispose: (pool) => pool.end(), // may be sync or async
233
+ }),
234
+ ]);
235
+
236
+ container.get(DB);
237
+
238
+ await container.dispose(); // awaits async hooks, then clears instances
239
+ ```
240
+
241
+ Details:
242
+
243
+ - Hooks run in reverse creation order (dependents before their dependencies).
244
+ - `dispose()` returns a promise and awaits async hooks.
245
+ - It is idempotent — calling it again is a no-op.
246
+ - Only resolved singletons (and scoped instances on their container) are disposed; never-resolved instances are not tracked.
247
+ - `onDispose` is only meaningful for cached instances. Declaring it on a `transient` provider throws `InvalidProviderError`, since transient instances are never tracked and the hook could never run.
248
+
249
+ ### Child containers
250
+
251
+ `createChildContainer(parent, providers?)` creates a child that inherits
252
+ everything from its parent but can add or override providers locally. This is the
253
+ typical pattern for per-request isolation on a server: shared services live on
254
+ the root, request-specific values live on a short-lived child.
255
+
256
+ ```ts
257
+ const root = createContainer([
258
+ provideFactory(LOGGER, { useFactory: () => console }), // singleton, shared
259
+ provideFactory(HANDLER, {
260
+ scope: Scopes.Scoped, // one instance per child
261
+ deps: { request: REQUEST },
262
+ useFactory: ({ request }) => createHandler(request),
263
+ }),
264
+ ]);
265
+
266
+ function handle(request: Request) {
267
+ const child = createChildContainer(root, [provideValue(REQUEST, request)]);
268
+
269
+ child.get(LOGGER); // same logger as the root and every other child
270
+ child.get(HANDLER); // a fresh handler, unique to this child
271
+
272
+ return child.dispose(); // release only this child's instances
273
+ }
274
+ ```
275
+
276
+ How resolution works across the chain:
277
+
278
+ - A token is looked up in the child first, then walks up to the parent.
279
+ - `singleton` is cached on the container that **owns** the provider, so it is shared by the whole subtree.
280
+ - `scoped` is cached on the **requesting** child, so each child gets its own instance — even when the provider is declared once on the parent.
281
+ - A `scoped` provider resolves its dependencies from the requesting child, so it can depend on values registered only in that child (like `REQUEST`).
282
+ - `dispose()` only releases the container it is called on; it does not cascade to parents or children.
134
283
 
135
284
  ### Cycle detection
136
285
 
@@ -141,6 +290,80 @@ If providers form a dependency cycle, resolution throws `CircularDependencyError
141
290
  container.get(A); // throws: Circular dependency detected: A -> B -> A
142
291
  ```
143
292
 
293
+ ### Async dependencies
294
+
295
+ di-craft resolves synchronously by design — there is no `getAsync`, and async
296
+ never colors the rest of your graph. Asynchronous values are handled with one of
297
+ two patterns, which together cover the vast majority of cases.
298
+
299
+ **Resolve first, then register.** Do the async work at your composition root and
300
+ register the resolved value. Simplest and most common:
301
+
302
+ ```ts
303
+ const db = await connectDatabase(config);
304
+ container.register(provideValue(DB, db));
305
+ ```
306
+
307
+ **Promise as value (lazy).** Register a factory that returns a promise. The
308
+ container caches it like any other singleton, so the async work runs once and
309
+ every consumer awaits the same promise:
310
+
311
+ ```ts
312
+ const POOL = createToken<Promise<Pool>>("pool");
313
+
314
+ container.register(provideFactory(POOL, { useFactory: () => createPool() }));
315
+
316
+ const pool = await container.get(POOL);
317
+ ```
318
+
319
+ A factory that depends on `POOL` receives the promise and awaits it itself:
320
+
321
+ ```ts
322
+ provideFactory(USERS, {
323
+ deps: { pool: POOL },
324
+ useFactory: async ({ pool }) => new UsersRepo(await pool),
325
+ });
326
+ // USERS is now Token<Promise<UsersRepo>> — consumers await it too.
327
+ ```
328
+
329
+ ## Dependency injection vs service location
330
+
331
+ di-craft is built for **dependency injection**: dependencies are declared up front
332
+ and handed to your code. The opposite is **service location**, where code reaches
333
+ into a container at runtime to pull what it needs, hiding its real dependencies.
334
+
335
+ Two habits keep usage canonical: call `container.get()` only at the **composition
336
+ root** (entrypoint, framework hooks, route handlers), and never pass the container
337
+ into your classes or functions. di-craft enforces the key half for you — **a
338
+ factory only ever receives its declared `deps`, never the container** — so a
339
+ provider physically cannot locate arbitrary services.
340
+
341
+ ```ts
342
+ // Dependency injection — deps are explicit, the class never sees the container.
343
+ provideFactory(USERS, {
344
+ deps: { repo: REPO, logger: LOGGER },
345
+ useFactory: ({ repo, logger }) => new UserService(repo, logger),
346
+ });
347
+
348
+ const users = container.get(USERS); // resolved at the root, then injected down
349
+ ```
350
+
351
+ ```ts
352
+ // Service location (anti-pattern) — the container is smuggled into domain code.
353
+ class UserService {
354
+ constructor(private container: Container) {}
355
+
356
+ list() {
357
+ const repo = this.container.get(REPO); // hidden, runtime-only dependency
358
+ }
359
+ }
360
+ ```
361
+
362
+ The second form compiles, but it hides dependencies and defeats DI. No runtime flag
363
+ can forbid it — `get()` is the same call the composition root relies on — so keep
364
+ resolution at the edges by convention, or enforce it with a lint rule that allows
365
+ `.get()` only in your composition-root files.
366
+
144
367
  ## Error handling
145
368
 
146
369
  All errors extend the shared `DiError` base class, so you can catch any container error with a single check:
@@ -167,6 +390,7 @@ try {
167
390
  | `DuplicateProviderError` | A token is registered more than once. |
168
391
  | `CircularDependencyError` | Providers form a dependency cycle. |
169
392
  | `InvalidDependencyError` | A declared dependency token is missing/undefined. |
393
+ | `InvalidProviderError` | A provider is misconfigured (`onDispose` on a transient, or a dependency that outlives its consumer) or an override would drop a live disposable instance. |
170
394
 
171
395
  ## API reference
172
396
 
@@ -175,11 +399,14 @@ try {
175
399
  | `createToken<T>(name)` | Create a unique, typed token. |
176
400
  | `provideValue(token, value)` | Provider that returns an existing value. |
177
401
  | `provideFactory(token, options)` | Provider that builds a value via a factory. |
402
+ | `optional(token)` | Mark a dependency as optional (resolves to `undefined` when absent). |
178
403
  | `createContainer(providers?)` | Create a container, optionally seeded with providers. |
404
+ | `createChildContainer(parent, providers?)` | Create a child container that inherits from `parent`. |
405
+ | `Scopes` | Object of scope values (`Scopes.Singleton`, `Scopes.Transient`, `Scopes.Scoped`). |
179
406
 
180
- Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Scope`.
407
+ Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Dependency`, `OptionalDependency`, `Scope`, `DisposeHook`, `RegisterOptions`.
181
408
 
182
- Exported errors: `DiError`, `MissingProviderError`, `DuplicateProviderError`, `CircularDependencyError`, `InvalidDependencyError`.
409
+ Exported errors: `DiError`, `MissingProviderError`, `DuplicateProviderError`, `CircularDependencyError`, `InvalidDependencyError`, `InvalidProviderError`.
183
410
 
184
411
  ## License
185
412
 
package/dist/index.cjs CHANGED
@@ -1,166 +1,2 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/error/error.ts
3
- var DiError = class extends Error {
4
- constructor(message) {
5
- super(message);
6
- this.name = "DiError";
7
- if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
8
- }
9
- };
10
- //#endregion
11
- //#region src/registry/errors.ts
12
- var DuplicateProviderError = class extends DiError {
13
- constructor(tokenName) {
14
- super(`Provider for token "${tokenName}" is already registered`);
15
- this.name = "DuplicateProviderError";
16
- }
17
- };
18
- //#endregion
19
- //#region src/registry/registry.ts
20
- var RegistryClass = class {
21
- providers = /* @__PURE__ */ new Map();
22
- register(provider) {
23
- if (this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
24
- this.providers.set(provider.provide.id, provider);
25
- }
26
- get(token) {
27
- return this.providers.get(token.id);
28
- }
29
- has(token) {
30
- return this.providers.has(token.id);
31
- }
32
- };
33
- const createRegistry = () => new RegistryClass();
34
- //#endregion
35
- //#region src/resolver/errors.ts
36
- var MissingProviderError = class extends DiError {
37
- constructor(tokenName) {
38
- super(`Provider for token "${tokenName}" is not registered`);
39
- this.name = "MissingProviderError";
40
- }
41
- };
42
- var InvalidDependencyError = class extends DiError {
43
- constructor(dependencyKey) {
44
- super(`Invalid dependency "${dependencyKey}"`);
45
- this.name = "InvalidDependencyError";
46
- }
47
- };
48
- var CircularDependencyError = class extends DiError {
49
- constructor(tokenNames) {
50
- super(`Circular dependency detected: ${tokenNames.join(" -> ")}`);
51
- this.name = "CircularDependencyError";
52
- }
53
- };
54
- //#endregion
55
- //#region src/provider/provider.ts
56
- const provideValue = (token, useValue) => ({
57
- provide: token,
58
- useValue
59
- });
60
- const provideFactory = (token, options) => {
61
- return {
62
- provide: token,
63
- useFactory: options.useFactory,
64
- ...options.deps ? { deps: options.deps } : {},
65
- ...options.scope ? { scope: options.scope } : {}
66
- };
67
- };
68
- const isValueProvider = (provider) => {
69
- return "useValue" in provider;
70
- };
71
- const isFactoryProvider = (provider) => {
72
- return "useFactory" in provider;
73
- };
74
- //#endregion
75
- //#region src/resolver/resolver.ts
76
- var ResolverClass = class {
77
- registry;
78
- instances = /* @__PURE__ */ new Map();
79
- constructor(registry) {
80
- this.registry = registry;
81
- }
82
- resolve(token) {
83
- return this.resolveToken(token, {
84
- resolving: /* @__PURE__ */ new Set(),
85
- path: []
86
- });
87
- }
88
- resolveToken(token, context) {
89
- const provider = this.registry.get(token);
90
- if (!provider) throw new MissingProviderError(token.name);
91
- if (isValueProvider(provider)) return provider.useValue;
92
- if (isFactoryProvider(provider)) {
93
- const scope = provider.scope ?? "singleton";
94
- if (scope === "singleton" && this.instances.has(token.id)) return this.instances.get(token.id);
95
- if (context.resolving.has(token.id)) {
96
- const cycleStartIndex = context.path.findIndex((pathToken) => pathToken.id === token.id);
97
- throw new CircularDependencyError([...context.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
98
- }
99
- context.resolving.add(token.id);
100
- context.path.push(token);
101
- try {
102
- const deps = this.resolveDeps(provider.deps, context);
103
- const value = provider.useFactory(deps);
104
- if (scope === "singleton") this.instances.set(token.id, value);
105
- return value;
106
- } finally {
107
- context.path.pop();
108
- context.resolving.delete(token.id);
109
- }
110
- }
111
- throw new MissingProviderError(token.name);
112
- }
113
- resolveDeps(deps, context) {
114
- if (!deps) return {};
115
- const resolvedDeps = {};
116
- for (const key of Object.keys(deps)) {
117
- const token = deps[key];
118
- if (token === void 0) throw new InvalidDependencyError(String(key));
119
- resolvedDeps[key] = this.resolveToken(token, context);
120
- }
121
- return resolvedDeps;
122
- }
123
- };
124
- const createResolver = (registry) => new ResolverClass(registry);
125
- //#endregion
126
- //#region src/container/container.ts
127
- var ContainerClass = class {
128
- registry;
129
- resolver;
130
- constructor(providers = []) {
131
- this.registry = createRegistry();
132
- for (const provider of providers) this.registry.register(provider);
133
- this.resolver = createResolver(this.registry);
134
- }
135
- register(provider) {
136
- this.registry.register(provider);
137
- }
138
- get(token) {
139
- return this.resolver.resolve(token);
140
- }
141
- has(token) {
142
- return this.registry.has(token);
143
- }
144
- };
145
- const createContainer = (providers = []) => new ContainerClass(providers);
146
- //#endregion
147
- //#region src/token/token.ts
148
- var TokenClass = class {
149
- name;
150
- id;
151
- constructor(name) {
152
- this.name = name;
153
- this.id = Symbol(name);
154
- }
155
- };
156
- const createToken = (name) => new TokenClass(name);
157
- //#endregion
158
- exports.CircularDependencyError = CircularDependencyError;
159
- exports.DiError = DiError;
160
- exports.DuplicateProviderError = DuplicateProviderError;
161
- exports.InvalidDependencyError = InvalidDependencyError;
162
- exports.MissingProviderError = MissingProviderError;
163
- exports.createContainer = createContainer;
164
- exports.createToken = createToken;
165
- exports.provideFactory = provideFactory;
166
- exports.provideValue = provideValue;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{constructor(e){super(e),this.name=`DiError`,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},t=class extends e{constructor(e){super(e),this.name=`InvalidProviderError`}};const n={Singleton:`singleton`,Transient:`transient`,Scoped:`scoped`},r=(e,t)=>({provide:e,useValue:t}),i=(e,r)=>{if(r.scope===n.Transient&&r.onDispose)throw new t(`onDispose is not supported for transient providers (token "${e.name}"): transient instances are not tracked, so the hook would never run.`);return{provide:e,useFactory:r.useFactory,scope:r.scope??n.Singleton,...r.deps?{deps:r.deps}:{},...r.onDispose?{onDispose:r.onDispose}:{}}},a=e=>({token:e,optional:!0}),o=e=>e.optional===!0,s=e=>`useValue`in e;var c=class extends e{constructor(e){super(`Provider for token "${e}" is already registered`),this.name=`DuplicateProviderError`}},l=class{providers=new Map;register(e,t){let n=this.providers.has(e.provide.id);if(n&&!t?.allowOverride)throw new c(e.provide.name);return this.providers.set(e.provide.id,e),n}get(e){return this.providers.get(e.id)}has(e){return this.providers.has(e.id)}};const u=()=>new l;var d=class extends e{constructor(e){super(`Provider for token "${e}" is not registered`),this.name=`MissingProviderError`}},f=class extends e{constructor(e){super(`Invalid dependency "${e}"`),this.name=`InvalidDependencyError`}},p=class extends e{constructor(e){super(`Circular dependency detected: ${e.join(` -> `)}`),this.name=`CircularDependencyError`}},m=class{instances=new Map;get(e){return this.instances.get(e.id)}set(e,t){this.instances.set(e.id,t)}delete(e){this.instances.delete(e.id)}async dispose(){let e=[...this.instances.values()].reverse();this.instances.clear();for(let t of e)t.onDispose&&await t.onDispose(t.value)}};const h=()=>new m;var g=class{resolving=new Set;path=[];enter(e){if(this.resolving.has(e.id)){let t=this.path.findIndex(t=>t.id===e.id);throw new p([...this.path.slice(t),e].map(e=>e.name))}this.resolving.add(e.id),this.path.push(e)}exit(e){this.path.pop(),this.resolving.delete(e.id)}};const _=e=>e===n.Scoped?1:e===n.Transient?2:0;var v=class{registry;store=h();parent;constructor(e,t){this.registry=e,this.parent=t}resolve(e){return this.resolveToken(e,void 0)}resolveOptional(e){return this.lookupOptional(e,void 0)}invalidate(e){this.store.delete(e)}hasDisposableInstance(e){return this.store.get(e)?.onDispose!==void 0}dispose(){return this.store.dispose()}resolveToken(e,r,i,a){let o=this,c;for(;o&&(c=o.registry.get(e),!c);)o=o.parent;if(!c||!o)throw new d(e.name);if(s(c))return c.useValue;if(i!==void 0&&_(c.scope)>_(i))throw new t(`"${a}" (${i}) cannot depend on "${e.name}" (${c.scope??n.Singleton}): a longer-lived provider would capture a shorter-lived one. Widen the dependency's scope or narrow the consumer's.`);let l=this.selectHost(c.scope,o);if(l){let t=l.store.get(e);if(t)return t.value}let u=r??new g;u.enter(e);try{let t=(l??this).resolveDeps(c.deps,u,c.scope,e.name),n=c.useFactory(t);return l&&l.store.set(e,{value:n,...c.onDispose?{onDispose:c.onDispose}:{}}),n}finally{u.exit(e)}}selectHost(e,t){if(e!==n.Transient)return e===n.Scoped?this:t}resolveDeps(e,t,n,r){if(!e)return{};let i={};for(let a of Object.keys(e)){let s=e[a];if(s===void 0)throw new f(String(a));i[a]=o(s)?this.lookupOptional(s.token,t,n,r):this.resolveToken(s,t,n,r)}return i}lookupOptional(e,t,n,r){let i=this;for(;i;){if(i.registry.has(e))return this.resolveToken(e,t,n,r);i=i.parent}}};const y=(e,t)=>new v(e,t);var b=class{registry;resolver;parent;constructor(e=[],t){this.parent=t,this.registry=u();for(let t of e)this.registry.register(t);this.resolver=y(this.registry,t?.resolver)}register(e,n){if(n?.allowOverride&&this.resolver.hasDisposableInstance(e.provide))throw new t(`Cannot override token "${e.provide.name}": its instance was already created and has an onDispose hook. Dispose the container before replacing it.`);this.registry.register(e,n)&&this.resolver.invalidate(e.provide)}get(e){return o(e)?this.resolver.resolveOptional(e.token):this.resolver.resolve(e)}has(e){return this.registry.has(e)||(this.parent?.has(e)??!1)}dispose(){return this.resolver.dispose()}};const x=(e=[])=>new b(e),S=(e,t=[])=>new b(t,e);var C=class{name;id;constructor(e){this.name=e,this.id=Symbol(e)}};const w=e=>new C(e);exports.CircularDependencyError=p,exports.DiError=e,exports.DuplicateProviderError=c,exports.InvalidDependencyError=f,exports.InvalidProviderError=t,exports.MissingProviderError=d,exports.Scopes=n,exports.createChildContainer=S,exports.createContainer=x,exports.createToken=w,exports.optional=a,exports.provideFactory=i,exports.provideValue=r;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/error/error.ts","../src/provider/errors.ts","../src/scope/scope.ts","../src/provider/provider.ts","../src/registry/errors.ts","../src/registry/registry.ts","../src/resolver/errors.ts","../src/store/store.ts","../src/resolver/context.ts","../src/resolver/resolver.ts","../src/container/container.ts","../src/token/token.ts"],"sourcesContent":["export class DiError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\n\t\tthis.name = \"DiError\";\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, this.constructor);\n\t\t}\n\t}\n}\n","import { DiError } from \"../error\";\n\nexport class InvalidProviderError extends DiError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\n\t\tthis.name = \"InvalidProviderError\";\n\t}\n}\n","export const Scopes = {\n\tSingleton: \"singleton\",\n\tTransient: \"transient\",\n\tScoped: \"scoped\",\n} as const;\n\nexport type Scope = (typeof Scopes)[keyof typeof Scopes];\n","import { type Scope, Scopes } from \"../scope\";\nimport type { Token } from \"../token\";\nimport { InvalidProviderError } from \"./errors\";\nimport type {\n\tAnyFactoryProvider,\n\tDependency,\n\tDepsMap,\n\tDisposeHook,\n\tFactory,\n\tFactoryProvider,\n\tOptionalDependency,\n\tProvider,\n\tValueProvider,\n} from \"./types\";\n\nexport const provideValue = <T>(\n\ttoken: Token<T>,\n\tuseValue: T,\n): ValueProvider<T> => ({\n\tprovide: token,\n\tuseValue,\n});\n\nexport const provideFactory = <T, TDeps extends DepsMap = Record<never, never>>(\n\ttoken: Token<T>,\n\toptions: {\n\t\treadonly deps?: TDeps;\n\t\treadonly scope?: Scope;\n\t\treadonly useFactory: Factory<T, TDeps>;\n\t\treadonly onDispose?: DisposeHook<T>;\n\t},\n): FactoryProvider<T, TDeps> => {\n\t// Transient instances aren't cached, so onDispose could never run.\n\tif (options.scope === Scopes.Transient && options.onDispose) {\n\t\tthrow new InvalidProviderError(\n\t\t\t`onDispose is not supported for transient providers (token \"${token.name}\"): transient instances are not tracked, so the hook would never run.`,\n\t\t);\n\t}\n\n\treturn {\n\t\tprovide: token,\n\t\tuseFactory: options.useFactory,\n\t\tscope: options.scope ?? Scopes.Singleton,\n\t\t...(options.deps ? { deps: options.deps } : {}),\n\t\t...(options.onDispose ? { onDispose: options.onDispose } : {}),\n\t};\n};\n\n// Marks a dependency optional: resolves to undefined when no provider exists.\nexport const optional = <T>(token: Token<T>): OptionalDependency<T> => ({\n\ttoken,\n\toptional: true,\n});\n\nexport const isOptionalDependency = <T>(\n\tdependency: Dependency<T>,\n): dependency is OptionalDependency<T> => {\n\treturn (dependency as OptionalDependency<T>).optional === true;\n};\n\nexport const isValueProvider = (\n\tprovider: Provider,\n): provider is ValueProvider<unknown> => {\n\treturn \"useValue\" in provider;\n};\n\nexport const isFactoryProvider = (\n\tprovider: Provider,\n): provider is AnyFactoryProvider => {\n\treturn \"useFactory\" in provider;\n};\n","import { DiError } from \"../error\";\n\nexport class DuplicateProviderError extends DiError {\n\tconstructor(tokenName: string) {\n\t\tsuper(`Provider for token \"${tokenName}\" is already registered`);\n\n\t\tthis.name = \"DuplicateProviderError\";\n\t}\n}\n","import type { Provider } from \"../provider\";\nimport type { Token } from \"../token\";\nimport { DuplicateProviderError } from \"./errors\";\nimport type { RegisterOptions, Registry } from \"./types\";\n\nclass RegistryClass implements Registry {\n\tprivate readonly providers = new Map<symbol, Provider>();\n\n\tregister(provider: Provider, options?: RegisterOptions): boolean {\n\t\tconst existed = this.providers.has(provider.provide.id);\n\n\t\tif (existed && !options?.allowOverride) {\n\t\t\tthrow new DuplicateProviderError(provider.provide.name);\n\t\t}\n\n\t\tthis.providers.set(provider.provide.id, provider);\n\n\t\t// true if an existing provider was replaced (override).\n\t\treturn existed;\n\t}\n\n\tget(token: Token<unknown>): Provider | undefined {\n\t\treturn this.providers.get(token.id);\n\t}\n\n\thas(token: Token<unknown>): boolean {\n\t\treturn this.providers.has(token.id);\n\t}\n}\n\nexport const createRegistry = (): Registry => new RegistryClass();\n","import { DiError } from \"../error\";\n\nexport class MissingProviderError extends DiError {\n\tconstructor(tokenName: string) {\n\t\tsuper(`Provider for token \"${tokenName}\" is not registered`);\n\n\t\tthis.name = \"MissingProviderError\";\n\t}\n}\n\nexport class InvalidDependencyError extends DiError {\n\tconstructor(dependencyKey: string) {\n\t\tsuper(`Invalid dependency \"${dependencyKey}\"`);\n\n\t\tthis.name = \"InvalidDependencyError\";\n\t}\n}\n\nexport class CircularDependencyError extends DiError {\n\tconstructor(tokenNames: string[]) {\n\t\tsuper(`Circular dependency detected: ${tokenNames.join(\" -> \")}`);\n\n\t\tthis.name = \"CircularDependencyError\";\n\t}\n}\n","import type { Token } from \"../token\";\nimport type { InstanceRecord, Store } from \"./types\";\n\nclass StoreClass implements Store {\n\tprivate readonly instances = new Map<symbol, InstanceRecord>();\n\n\tget(token: Token<unknown>): InstanceRecord | undefined {\n\t\treturn this.instances.get(token.id);\n\t}\n\n\tset(token: Token<unknown>, record: InstanceRecord): void {\n\t\tthis.instances.set(token.id, record);\n\t}\n\n\tdelete(token: Token<unknown>): void {\n\t\tthis.instances.delete(token.id);\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\t// Snapshot and clear first so dispose() is idempotent and re-entrancy safe.\n\t\tconst records = [...this.instances.values()].reverse();\n\n\t\tthis.instances.clear();\n\n\t\tfor (const record of records) {\n\t\t\tif (record.onDispose) {\n\t\t\t\tawait record.onDispose(record.value);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const createStore = (): Store => new StoreClass();\n","import type { Token } from \"../token\";\nimport { CircularDependencyError } from \"./errors\";\n\nexport class ResolutionContext {\n\tprivate readonly resolving = new Set<symbol>();\n\tprivate readonly path: Token<unknown>[] = [];\n\n\tenter(token: Token<unknown>): void {\n\t\tif (this.resolving.has(token.id)) {\n\t\t\tconst cycleStartIndex = this.path.findIndex(\n\t\t\t\t(pathToken) => pathToken.id === token.id,\n\t\t\t);\n\n\t\t\tconst cycle = [...this.path.slice(cycleStartIndex), token];\n\n\t\t\tthrow new CircularDependencyError(\n\t\t\t\tcycle.map((cycleToken) => cycleToken.name),\n\t\t\t);\n\t\t}\n\n\t\tthis.resolving.add(token.id);\n\t\tthis.path.push(token);\n\t}\n\n\texit(token: Token<unknown>): void {\n\t\tthis.path.pop();\n\t\tthis.resolving.delete(token.id);\n\t}\n}\n","import type { DepsMap, Provider, ResolveDeps } from \"../provider\";\nimport {\n\tInvalidProviderError,\n\tisOptionalDependency,\n\tisValueProvider,\n} from \"../provider\";\nimport type { Registry } from \"../registry\";\nimport { type Scope, Scopes } from \"../scope\";\nimport { createStore, type Store } from \"../store\";\nimport type { Token } from \"../token\";\nimport { ResolutionContext } from \"./context\";\nimport { InvalidDependencyError, MissingProviderError } from \"./errors\";\nimport type { Resolver } from \"./types\";\n\n// Lifetime ordering: longer-lived scopes rank lower. A provider may only depend\n// on dependencies that live at least as long as itself.\nconst lifetimeRank = (scope: Scope | undefined): number =>\n\tscope === Scopes.Scoped ? 1 : scope === Scopes.Transient ? 2 : 0;\n\nclass ResolverClass implements Resolver {\n\tprivate readonly registry: Registry;\n\tprivate readonly store: Store = createStore();\n\tprivate readonly parent: ResolverClass | undefined;\n\n\tconstructor(registry: Registry, parent?: ResolverClass) {\n\t\tthis.registry = registry;\n\t\tthis.parent = parent;\n\t}\n\n\tresolve<T>(token: Token<T>): T {\n\t\treturn this.resolveToken(token, undefined);\n\t}\n\n\tresolveOptional<T>(token: Token<T>): T | undefined {\n\t\treturn this.lookupOptional(token, undefined);\n\t}\n\n\tinvalidate(token: Token<unknown>): void {\n\t\tthis.store.delete(token);\n\t}\n\n\t// True if a locally-cached instance has an onDispose hook.\n\thasDisposableInstance(token: Token<unknown>): boolean {\n\t\treturn this.store.get(token)?.onDispose !== undefined;\n\t}\n\n\tdispose(): Promise<void> {\n\t\treturn this.store.dispose();\n\t}\n\n\t// context is allocated lazily by the first building factory (zero-alloc cache hits).\n\t// consumerScope/consumerName describe the provider depending on this token, if any.\n\tprivate resolveToken<T>(\n\t\ttoken: Token<T>,\n\t\tcontext: ResolutionContext | undefined,\n\t\tconsumerScope?: Scope,\n\t\tconsumerName?: string,\n\t): T {\n\t\t// Single walk up the parent chain, fetching the provider directly.\n\t\tlet owner: ResolverClass | undefined = this;\n\t\tlet provider: Provider | undefined;\n\n\t\twhile (owner) {\n\t\t\tprovider = owner.registry.get(token);\n\n\t\t\tif (provider) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\towner = owner.parent;\n\t\t}\n\n\t\tif (!provider || !owner) {\n\t\t\tthrow new MissingProviderError(token.name);\n\t\t}\n\n\t\t// Values outlive every scope, so they are always a safe dependency.\n\t\tif (isValueProvider(provider)) {\n\t\t\treturn provider.useValue as T;\n\t\t}\n\n\t\t// Otherwise it is a factory provider (the only remaining variant).\n\t\t// Reject capturing a shorter-lived dependency into a longer-lived consumer.\n\t\tif (\n\t\t\tconsumerScope !== undefined &&\n\t\t\tlifetimeRank(provider.scope) > lifetimeRank(consumerScope)\n\t\t) {\n\t\t\tthrow new InvalidProviderError(\n\t\t\t\t`\"${consumerName}\" (${consumerScope}) cannot depend on \"${token.name}\" (${provider.scope ?? Scopes.Singleton}): a longer-lived provider would capture a shorter-lived one. Widen the dependency's scope or narrow the consumer's.`,\n\t\t\t);\n\t\t}\n\n\t\t// Scope decides the host: singleton on owner, scoped on this, transient nowhere.\n\t\tconst host = this.selectHost(provider.scope, owner);\n\n\t\tif (host) {\n\t\t\tconst cached = host.store.get(token);\n\n\t\t\tif (cached) {\n\t\t\t\treturn cached.value as T;\n\t\t\t}\n\t\t}\n\n\t\t// Allocate cycle detection only now, threading it through the build.\n\t\tconst ctx = context ?? new ResolutionContext();\n\t\tctx.enter(token);\n\n\t\ttry {\n\t\t\t// Resolve deps from the host: scoped sees this container, singleton sees owner.\n\t\t\tconst deps = (host ?? this).resolveDeps(\n\t\t\t\tprovider.deps,\n\t\t\t\tctx,\n\t\t\t\tprovider.scope,\n\t\t\t\ttoken.name,\n\t\t\t);\n\t\t\tconst value = provider.useFactory(deps) as T;\n\n\t\t\tif (host) {\n\t\t\t\thost.store.set(token, {\n\t\t\t\t\tvalue,\n\t\t\t\t\t...(provider.onDispose ? { onDispose: provider.onDispose } : {}),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn value;\n\t\t} finally {\n\t\t\tctx.exit(token);\n\t\t}\n\t}\n\n\tprivate selectHost(\n\t\tscope: Scope | undefined,\n\t\towner: ResolverClass,\n\t): ResolverClass | undefined {\n\t\tif (scope === Scopes.Transient) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (scope === Scopes.Scoped) {\n\t\t\treturn this;\n\t\t}\n\n\t\treturn owner;\n\t}\n\n\tprivate resolveDeps<TDeps extends DepsMap>(\n\t\tdeps: TDeps | undefined,\n\t\tcontext: ResolutionContext,\n\t\tconsumerScope: Scope | undefined,\n\t\tconsumerName: string,\n\t): ResolveDeps<TDeps> {\n\t\tif (!deps) {\n\t\t\treturn {} as ResolveDeps<TDeps>;\n\t\t}\n\n\t\tconst resolvedDeps: Partial<ResolveDeps<TDeps>> = {};\n\n\t\tfor (const key of Object.keys(deps) as Array<keyof TDeps>) {\n\t\t\tconst dependency = deps[key];\n\n\t\t\tif (dependency === undefined) {\n\t\t\t\tthrow new InvalidDependencyError(String(key));\n\t\t\t}\n\n\t\t\tresolvedDeps[key] = (\n\t\t\t\tisOptionalDependency(dependency)\n\t\t\t\t\t? this.lookupOptional(\n\t\t\t\t\t\t\tdependency.token,\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tconsumerScope,\n\t\t\t\t\t\t\tconsumerName,\n\t\t\t\t\t\t)\n\t\t\t\t\t: this.resolveToken(dependency, context, consumerScope, consumerName)\n\t\t\t) as ResolveDeps<TDeps>[typeof key];\n\t\t}\n\n\t\treturn resolvedDeps as ResolveDeps<TDeps>;\n\t}\n\n\t// Absent provider -> undefined; a present one resolves normally (its errors surface).\n\tprivate lookupOptional<T>(\n\t\ttoken: Token<T>,\n\t\tcontext: ResolutionContext | undefined,\n\t\tconsumerScope?: Scope,\n\t\tconsumerName?: string,\n\t): T | undefined {\n\t\tlet owner: ResolverClass | undefined = this;\n\n\t\twhile (owner) {\n\t\t\tif (owner.registry.has(token)) {\n\t\t\t\treturn this.resolveToken(token, context, consumerScope, consumerName);\n\t\t\t}\n\n\t\t\towner = owner.parent;\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n\nexport const createResolver = (\n\tregistry: Registry,\n\tparent?: Resolver,\n): Resolver => new ResolverClass(registry, parent as ResolverClass | undefined);\n","import {\n\ttype Dependency,\n\tInvalidProviderError,\n\tisOptionalDependency,\n\ttype OptionalDependency,\n\ttype Provider,\n} from \"../provider\";\nimport {\n\tcreateRegistry,\n\ttype RegisterOptions,\n\ttype Registry,\n} from \"../registry\";\nimport { createResolver, type Resolver } from \"../resolver\";\nimport type { Token } from \"../token\";\nimport type { Container } from \"./types\";\n\nclass ContainerClass implements Container {\n\tprivate readonly registry: Registry;\n\tprivate readonly resolver: Resolver;\n\tprivate readonly parent: ContainerClass | undefined;\n\n\tconstructor(providers: readonly Provider[] = [], parent?: ContainerClass) {\n\t\tthis.parent = parent;\n\t\tthis.registry = createRegistry();\n\n\t\tfor (const provider of providers) {\n\t\t\tthis.registry.register(provider);\n\t\t}\n\n\t\tthis.resolver = createResolver(this.registry, parent?.resolver);\n\t}\n\n\tregister(provider: Provider, options?: RegisterOptions): void {\n\t\t// Don't silently drop a live disposable instance; require explicit dispose.\n\t\tif (\n\t\t\toptions?.allowOverride &&\n\t\t\tthis.resolver.hasDisposableInstance(provider.provide)\n\t\t) {\n\t\t\tthrow new InvalidProviderError(\n\t\t\t\t`Cannot override token \"${provider.provide.name}\": its instance was already created and has an onDispose hook. Dispose the container before replacing it.`,\n\t\t\t);\n\t\t}\n\n\t\tconst overridden = this.registry.register(provider, options);\n\n\t\tif (overridden) {\n\t\t\tthis.resolver.invalidate(provider.provide);\n\t\t}\n\t}\n\n\tget<T>(token: Token<T>): T;\n\tget<T>(dependency: OptionalDependency<T>): T | undefined;\n\tget<T>(dependency: Dependency<T>): T | undefined {\n\t\tif (isOptionalDependency(dependency)) {\n\t\t\treturn this.resolver.resolveOptional(dependency.token);\n\t\t}\n\n\t\treturn this.resolver.resolve(dependency);\n\t}\n\n\thas(token: Token<unknown>): boolean {\n\t\treturn this.registry.has(token) || (this.parent?.has(token) ?? false);\n\t}\n\n\tdispose(): Promise<void> {\n\t\treturn this.resolver.dispose();\n\t}\n}\n\nexport const createContainer = (\n\tproviders: readonly Provider[] = [],\n): Container => new ContainerClass(providers);\n\nexport const createChildContainer = (\n\tparent: Container,\n\tproviders: readonly Provider[] = [],\n): Container => new ContainerClass(providers, parent as ContainerClass);\n","import type { Token } from \"./types\";\n\nclass TokenClass<T> implements Token<T> {\n\treadonly name: string;\n\treadonly id: symbol;\n\n\tdeclare readonly __type?: T;\n\n\tconstructor(name: string) {\n\t\tthis.name = name;\n\t\tthis.id = Symbol(name);\n\t}\n}\n\nexport const createToken = <T>(name: string): Token<T> =>\n\tnew TokenClass<T>(name);\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,21 @@
1
+ //#region src/error/error.d.ts
2
+ declare class DiError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ //#endregion
6
+ //#region src/provider/errors.d.ts
7
+ declare class InvalidProviderError extends DiError {
8
+ constructor(message: string);
9
+ }
10
+ //#endregion
11
+ //#region src/scope/scope.d.ts
12
+ declare const Scopes: {
13
+ readonly Singleton: "singleton";
14
+ readonly Transient: "transient";
15
+ readonly Scoped: "scoped";
16
+ };
17
+ type Scope = (typeof Scopes)[keyof typeof Scopes];
18
+ //#endregion
1
19
  //#region src/token/types.d.ts
2
20
  type Token<T> = {
3
21
  readonly id: symbol;
@@ -9,10 +27,16 @@ type Token<T> = {
9
27
  declare const createToken: <T>(name: string) => Token<T>;
10
28
  //#endregion
11
29
  //#region src/provider/types.d.ts
12
- type DepsMap = Record<string, Token<unknown>>;
13
- type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
14
- type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: TokenValue<TDeps[TKey]> };
30
+ type OptionalDependency<T> = {
31
+ readonly token: Token<T>;
32
+ readonly optional: true;
33
+ };
34
+ type Dependency<T> = Token<T> | OptionalDependency<T>;
35
+ type DepsMap = Record<string, Dependency<unknown>>;
36
+ type DependencyValue<TDep> = TDep extends OptionalDependency<infer T> ? T | undefined : TDep extends Token<infer T> ? T : never;
37
+ type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: DependencyValue<TDeps[TKey]> };
15
38
  type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
39
+ type DisposeHook<T> = (instance: T) => void | Promise<void>;
16
40
  type ValueProvider<T> = {
17
41
  readonly provide: Token<T>;
18
42
  readonly useValue: T;
@@ -22,10 +46,10 @@ type FactoryProvider<T, TDeps extends DepsMap = Record<never, never>> = {
22
46
  readonly deps?: TDeps;
23
47
  readonly scope?: Scope;
24
48
  readonly useFactory: Factory<T, TDeps>;
49
+ readonly onDispose?: DisposeHook<T>;
25
50
  };
26
- type AnyFactoryProvider = FactoryProvider<unknown, any>;
51
+ type AnyFactoryProvider = FactoryProvider<any, any>;
27
52
  type Provider = ValueProvider<unknown> | AnyFactoryProvider;
28
- type Scope = "singleton" | "transient";
29
53
  //#endregion
30
54
  //#region src/provider/provider.d.ts
31
55
  declare const provideValue: <T>(token: Token<T>, useValue: T) => ValueProvider<T>;
@@ -33,27 +57,32 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
33
57
  readonly deps?: TDeps;
34
58
  readonly scope?: Scope;
35
59
  readonly useFactory: Factory<T, TDeps>;
60
+ readonly onDispose?: DisposeHook<T>;
36
61
  }) => FactoryProvider<T, TDeps>;
62
+ declare const optional: <T>(token: Token<T>) => OptionalDependency<T>;
63
+ //#endregion
64
+ //#region src/registry/errors.d.ts
65
+ declare class DuplicateProviderError extends DiError {
66
+ constructor(tokenName: string);
67
+ }
68
+ //#endregion
69
+ //#region src/registry/types.d.ts
70
+ type RegisterOptions = {
71
+ readonly allowOverride?: boolean;
72
+ };
37
73
  //#endregion
38
74
  //#region src/container/types.d.ts
39
75
  type Container = {
40
- register(provider: Provider): void;
76
+ register(provider: Provider, options?: RegisterOptions): void;
41
77
  get<T>(token: Token<T>): T;
78
+ get<T>(dependency: OptionalDependency<T>): T | undefined;
42
79
  has(token: Token<unknown>): boolean;
80
+ dispose(): Promise<void>;
43
81
  };
44
82
  //#endregion
45
83
  //#region src/container/container.d.ts
46
84
  declare const createContainer: (providers?: readonly Provider[]) => Container;
47
- //#endregion
48
- //#region src/error/error.d.ts
49
- declare class DiError extends Error {
50
- constructor(message: string);
51
- }
52
- //#endregion
53
- //#region src/registry/errors.d.ts
54
- declare class DuplicateProviderError extends DiError {
55
- constructor(tokenName: string);
56
- }
85
+ declare const createChildContainer: (parent: Container, providers?: readonly Provider[]) => Container;
57
86
  //#endregion
58
87
  //#region src/resolver/errors.d.ts
59
88
  declare class MissingProviderError extends DiError {
@@ -66,4 +95,5 @@ declare class CircularDependencyError extends DiError {
66
95
  constructor(tokenNames: string[]);
67
96
  }
68
97
  //#endregion
69
- export { CircularDependencyError, type Container, DiError, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type Scope, type Token, type ValueProvider, createContainer, createToken, provideFactory, provideValue };
98
+ export { CircularDependencyError, type Container, type Dependency, DiError, type DisposeHook, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, InvalidProviderError, MissingProviderError, type OptionalDependency, type Provider, type RegisterOptions, type Scope, Scopes, type Token, type ValueProvider, createChildContainer, createContainer, createToken, optional, provideFactory, provideValue };
99
+ //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -1,3 +1,21 @@
1
+ //#region src/error/error.d.ts
2
+ declare class DiError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ //#endregion
6
+ //#region src/provider/errors.d.ts
7
+ declare class InvalidProviderError extends DiError {
8
+ constructor(message: string);
9
+ }
10
+ //#endregion
11
+ //#region src/scope/scope.d.ts
12
+ declare const Scopes: {
13
+ readonly Singleton: "singleton";
14
+ readonly Transient: "transient";
15
+ readonly Scoped: "scoped";
16
+ };
17
+ type Scope = (typeof Scopes)[keyof typeof Scopes];
18
+ //#endregion
1
19
  //#region src/token/types.d.ts
2
20
  type Token<T> = {
3
21
  readonly id: symbol;
@@ -9,10 +27,16 @@ type Token<T> = {
9
27
  declare const createToken: <T>(name: string) => Token<T>;
10
28
  //#endregion
11
29
  //#region src/provider/types.d.ts
12
- type DepsMap = Record<string, Token<unknown>>;
13
- type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
14
- type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: TokenValue<TDeps[TKey]> };
30
+ type OptionalDependency<T> = {
31
+ readonly token: Token<T>;
32
+ readonly optional: true;
33
+ };
34
+ type Dependency<T> = Token<T> | OptionalDependency<T>;
35
+ type DepsMap = Record<string, Dependency<unknown>>;
36
+ type DependencyValue<TDep> = TDep extends OptionalDependency<infer T> ? T | undefined : TDep extends Token<infer T> ? T : never;
37
+ type ResolveDeps<TDeps extends DepsMap> = { readonly [TKey in keyof TDeps]: DependencyValue<TDeps[TKey]> };
15
38
  type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
39
+ type DisposeHook<T> = (instance: T) => void | Promise<void>;
16
40
  type ValueProvider<T> = {
17
41
  readonly provide: Token<T>;
18
42
  readonly useValue: T;
@@ -22,10 +46,10 @@ type FactoryProvider<T, TDeps extends DepsMap = Record<never, never>> = {
22
46
  readonly deps?: TDeps;
23
47
  readonly scope?: Scope;
24
48
  readonly useFactory: Factory<T, TDeps>;
49
+ readonly onDispose?: DisposeHook<T>;
25
50
  };
26
- type AnyFactoryProvider = FactoryProvider<unknown, any>;
51
+ type AnyFactoryProvider = FactoryProvider<any, any>;
27
52
  type Provider = ValueProvider<unknown> | AnyFactoryProvider;
28
- type Scope = "singleton" | "transient";
29
53
  //#endregion
30
54
  //#region src/provider/provider.d.ts
31
55
  declare const provideValue: <T>(token: Token<T>, useValue: T) => ValueProvider<T>;
@@ -33,27 +57,32 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
33
57
  readonly deps?: TDeps;
34
58
  readonly scope?: Scope;
35
59
  readonly useFactory: Factory<T, TDeps>;
60
+ readonly onDispose?: DisposeHook<T>;
36
61
  }) => FactoryProvider<T, TDeps>;
62
+ declare const optional: <T>(token: Token<T>) => OptionalDependency<T>;
63
+ //#endregion
64
+ //#region src/registry/errors.d.ts
65
+ declare class DuplicateProviderError extends DiError {
66
+ constructor(tokenName: string);
67
+ }
68
+ //#endregion
69
+ //#region src/registry/types.d.ts
70
+ type RegisterOptions = {
71
+ readonly allowOverride?: boolean;
72
+ };
37
73
  //#endregion
38
74
  //#region src/container/types.d.ts
39
75
  type Container = {
40
- register(provider: Provider): void;
76
+ register(provider: Provider, options?: RegisterOptions): void;
41
77
  get<T>(token: Token<T>): T;
78
+ get<T>(dependency: OptionalDependency<T>): T | undefined;
42
79
  has(token: Token<unknown>): boolean;
80
+ dispose(): Promise<void>;
43
81
  };
44
82
  //#endregion
45
83
  //#region src/container/container.d.ts
46
84
  declare const createContainer: (providers?: readonly Provider[]) => Container;
47
- //#endregion
48
- //#region src/error/error.d.ts
49
- declare class DiError extends Error {
50
- constructor(message: string);
51
- }
52
- //#endregion
53
- //#region src/registry/errors.d.ts
54
- declare class DuplicateProviderError extends DiError {
55
- constructor(tokenName: string);
56
- }
85
+ declare const createChildContainer: (parent: Container, providers?: readonly Provider[]) => Container;
57
86
  //#endregion
58
87
  //#region src/resolver/errors.d.ts
59
88
  declare class MissingProviderError extends DiError {
@@ -66,4 +95,5 @@ declare class CircularDependencyError extends DiError {
66
95
  constructor(tokenNames: string[]);
67
96
  }
68
97
  //#endregion
69
- export { CircularDependencyError, type Container, DiError, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, MissingProviderError, type Provider, type Scope, type Token, type ValueProvider, createContainer, createToken, provideFactory, provideValue };
98
+ export { CircularDependencyError, type Container, type Dependency, DiError, type DisposeHook, DuplicateProviderError, type FactoryProvider, InvalidDependencyError, InvalidProviderError, MissingProviderError, type OptionalDependency, type Provider, type RegisterOptions, type Scope, Scopes, type Token, type ValueProvider, createChildContainer, createContainer, createToken, optional, provideFactory, provideValue };
99
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,157 +1,2 @@
1
- //#region src/error/error.ts
2
- var DiError = class extends Error {
3
- constructor(message) {
4
- super(message);
5
- this.name = "DiError";
6
- if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
7
- }
8
- };
9
- //#endregion
10
- //#region src/registry/errors.ts
11
- var DuplicateProviderError = class extends DiError {
12
- constructor(tokenName) {
13
- super(`Provider for token "${tokenName}" is already registered`);
14
- this.name = "DuplicateProviderError";
15
- }
16
- };
17
- //#endregion
18
- //#region src/registry/registry.ts
19
- var RegistryClass = class {
20
- providers = /* @__PURE__ */ new Map();
21
- register(provider) {
22
- if (this.providers.has(provider.provide.id)) throw new DuplicateProviderError(provider.provide.name);
23
- this.providers.set(provider.provide.id, provider);
24
- }
25
- get(token) {
26
- return this.providers.get(token.id);
27
- }
28
- has(token) {
29
- return this.providers.has(token.id);
30
- }
31
- };
32
- const createRegistry = () => new RegistryClass();
33
- //#endregion
34
- //#region src/resolver/errors.ts
35
- var MissingProviderError = class extends DiError {
36
- constructor(tokenName) {
37
- super(`Provider for token "${tokenName}" is not registered`);
38
- this.name = "MissingProviderError";
39
- }
40
- };
41
- var InvalidDependencyError = class extends DiError {
42
- constructor(dependencyKey) {
43
- super(`Invalid dependency "${dependencyKey}"`);
44
- this.name = "InvalidDependencyError";
45
- }
46
- };
47
- var CircularDependencyError = class extends DiError {
48
- constructor(tokenNames) {
49
- super(`Circular dependency detected: ${tokenNames.join(" -> ")}`);
50
- this.name = "CircularDependencyError";
51
- }
52
- };
53
- //#endregion
54
- //#region src/provider/provider.ts
55
- const provideValue = (token, useValue) => ({
56
- provide: token,
57
- useValue
58
- });
59
- const provideFactory = (token, options) => {
60
- return {
61
- provide: token,
62
- useFactory: options.useFactory,
63
- ...options.deps ? { deps: options.deps } : {},
64
- ...options.scope ? { scope: options.scope } : {}
65
- };
66
- };
67
- const isValueProvider = (provider) => {
68
- return "useValue" in provider;
69
- };
70
- const isFactoryProvider = (provider) => {
71
- return "useFactory" in provider;
72
- };
73
- //#endregion
74
- //#region src/resolver/resolver.ts
75
- var ResolverClass = class {
76
- registry;
77
- instances = /* @__PURE__ */ new Map();
78
- constructor(registry) {
79
- this.registry = registry;
80
- }
81
- resolve(token) {
82
- return this.resolveToken(token, {
83
- resolving: /* @__PURE__ */ new Set(),
84
- path: []
85
- });
86
- }
87
- resolveToken(token, context) {
88
- const provider = this.registry.get(token);
89
- if (!provider) throw new MissingProviderError(token.name);
90
- if (isValueProvider(provider)) return provider.useValue;
91
- if (isFactoryProvider(provider)) {
92
- const scope = provider.scope ?? "singleton";
93
- if (scope === "singleton" && this.instances.has(token.id)) return this.instances.get(token.id);
94
- if (context.resolving.has(token.id)) {
95
- const cycleStartIndex = context.path.findIndex((pathToken) => pathToken.id === token.id);
96
- throw new CircularDependencyError([...context.path.slice(cycleStartIndex), token].map((cycleToken) => cycleToken.name));
97
- }
98
- context.resolving.add(token.id);
99
- context.path.push(token);
100
- try {
101
- const deps = this.resolveDeps(provider.deps, context);
102
- const value = provider.useFactory(deps);
103
- if (scope === "singleton") this.instances.set(token.id, value);
104
- return value;
105
- } finally {
106
- context.path.pop();
107
- context.resolving.delete(token.id);
108
- }
109
- }
110
- throw new MissingProviderError(token.name);
111
- }
112
- resolveDeps(deps, context) {
113
- if (!deps) return {};
114
- const resolvedDeps = {};
115
- for (const key of Object.keys(deps)) {
116
- const token = deps[key];
117
- if (token === void 0) throw new InvalidDependencyError(String(key));
118
- resolvedDeps[key] = this.resolveToken(token, context);
119
- }
120
- return resolvedDeps;
121
- }
122
- };
123
- const createResolver = (registry) => new ResolverClass(registry);
124
- //#endregion
125
- //#region src/container/container.ts
126
- var ContainerClass = class {
127
- registry;
128
- resolver;
129
- constructor(providers = []) {
130
- this.registry = createRegistry();
131
- for (const provider of providers) this.registry.register(provider);
132
- this.resolver = createResolver(this.registry);
133
- }
134
- register(provider) {
135
- this.registry.register(provider);
136
- }
137
- get(token) {
138
- return this.resolver.resolve(token);
139
- }
140
- has(token) {
141
- return this.registry.has(token);
142
- }
143
- };
144
- const createContainer = (providers = []) => new ContainerClass(providers);
145
- //#endregion
146
- //#region src/token/token.ts
147
- var TokenClass = class {
148
- name;
149
- id;
150
- constructor(name) {
151
- this.name = name;
152
- this.id = Symbol(name);
153
- }
154
- };
155
- const createToken = (name) => new TokenClass(name);
156
- //#endregion
157
- export { CircularDependencyError, DiError, DuplicateProviderError, InvalidDependencyError, MissingProviderError, createContainer, createToken, provideFactory, provideValue };
1
+ var e=class extends Error{constructor(e){super(e),this.name=`DiError`,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},t=class extends e{constructor(e){super(e),this.name=`InvalidProviderError`}};const n={Singleton:`singleton`,Transient:`transient`,Scoped:`scoped`},r=(e,t)=>({provide:e,useValue:t}),i=(e,r)=>{if(r.scope===n.Transient&&r.onDispose)throw new t(`onDispose is not supported for transient providers (token "${e.name}"): transient instances are not tracked, so the hook would never run.`);return{provide:e,useFactory:r.useFactory,scope:r.scope??n.Singleton,...r.deps?{deps:r.deps}:{},...r.onDispose?{onDispose:r.onDispose}:{}}},a=e=>({token:e,optional:!0}),o=e=>e.optional===!0,s=e=>`useValue`in e;var c=class extends e{constructor(e){super(`Provider for token "${e}" is already registered`),this.name=`DuplicateProviderError`}},l=class{providers=new Map;register(e,t){let n=this.providers.has(e.provide.id);if(n&&!t?.allowOverride)throw new c(e.provide.name);return this.providers.set(e.provide.id,e),n}get(e){return this.providers.get(e.id)}has(e){return this.providers.has(e.id)}};const u=()=>new l;var d=class extends e{constructor(e){super(`Provider for token "${e}" is not registered`),this.name=`MissingProviderError`}},f=class extends e{constructor(e){super(`Invalid dependency "${e}"`),this.name=`InvalidDependencyError`}},p=class extends e{constructor(e){super(`Circular dependency detected: ${e.join(` -> `)}`),this.name=`CircularDependencyError`}},m=class{instances=new Map;get(e){return this.instances.get(e.id)}set(e,t){this.instances.set(e.id,t)}delete(e){this.instances.delete(e.id)}async dispose(){let e=[...this.instances.values()].reverse();this.instances.clear();for(let t of e)t.onDispose&&await t.onDispose(t.value)}};const h=()=>new m;var g=class{resolving=new Set;path=[];enter(e){if(this.resolving.has(e.id)){let t=this.path.findIndex(t=>t.id===e.id);throw new p([...this.path.slice(t),e].map(e=>e.name))}this.resolving.add(e.id),this.path.push(e)}exit(e){this.path.pop(),this.resolving.delete(e.id)}};const _=e=>e===n.Scoped?1:e===n.Transient?2:0;var v=class{registry;store=h();parent;constructor(e,t){this.registry=e,this.parent=t}resolve(e){return this.resolveToken(e,void 0)}resolveOptional(e){return this.lookupOptional(e,void 0)}invalidate(e){this.store.delete(e)}hasDisposableInstance(e){return this.store.get(e)?.onDispose!==void 0}dispose(){return this.store.dispose()}resolveToken(e,r,i,a){let o=this,c;for(;o&&(c=o.registry.get(e),!c);)o=o.parent;if(!c||!o)throw new d(e.name);if(s(c))return c.useValue;if(i!==void 0&&_(c.scope)>_(i))throw new t(`"${a}" (${i}) cannot depend on "${e.name}" (${c.scope??n.Singleton}): a longer-lived provider would capture a shorter-lived one. Widen the dependency's scope or narrow the consumer's.`);let l=this.selectHost(c.scope,o);if(l){let t=l.store.get(e);if(t)return t.value}let u=r??new g;u.enter(e);try{let t=(l??this).resolveDeps(c.deps,u,c.scope,e.name),n=c.useFactory(t);return l&&l.store.set(e,{value:n,...c.onDispose?{onDispose:c.onDispose}:{}}),n}finally{u.exit(e)}}selectHost(e,t){if(e!==n.Transient)return e===n.Scoped?this:t}resolveDeps(e,t,n,r){if(!e)return{};let i={};for(let a of Object.keys(e)){let s=e[a];if(s===void 0)throw new f(String(a));i[a]=o(s)?this.lookupOptional(s.token,t,n,r):this.resolveToken(s,t,n,r)}return i}lookupOptional(e,t,n,r){let i=this;for(;i;){if(i.registry.has(e))return this.resolveToken(e,t,n,r);i=i.parent}}};const y=(e,t)=>new v(e,t);var b=class{registry;resolver;parent;constructor(e=[],t){this.parent=t,this.registry=u();for(let t of e)this.registry.register(t);this.resolver=y(this.registry,t?.resolver)}register(e,n){if(n?.allowOverride&&this.resolver.hasDisposableInstance(e.provide))throw new t(`Cannot override token "${e.provide.name}": its instance was already created and has an onDispose hook. Dispose the container before replacing it.`);this.registry.register(e,n)&&this.resolver.invalidate(e.provide)}get(e){return o(e)?this.resolver.resolveOptional(e.token):this.resolver.resolve(e)}has(e){return this.registry.has(e)||(this.parent?.has(e)??!1)}dispose(){return this.resolver.dispose()}};const x=(e=[])=>new b(e),S=(e,t=[])=>new b(t,e);var C=class{name;id;constructor(e){this.name=e,this.id=Symbol(e)}};const w=e=>new C(e);export{p as CircularDependencyError,e as DiError,c as DuplicateProviderError,f as InvalidDependencyError,t as InvalidProviderError,d as MissingProviderError,n as Scopes,S as createChildContainer,x as createContainer,w as createToken,a as optional,i as provideFactory,r as provideValue};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/error/error.ts","../src/provider/errors.ts","../src/scope/scope.ts","../src/provider/provider.ts","../src/registry/errors.ts","../src/registry/registry.ts","../src/resolver/errors.ts","../src/store/store.ts","../src/resolver/context.ts","../src/resolver/resolver.ts","../src/container/container.ts","../src/token/token.ts"],"sourcesContent":["export class DiError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\n\t\tthis.name = \"DiError\";\n\n\t\tif (Error.captureStackTrace) {\n\t\t\tError.captureStackTrace(this, this.constructor);\n\t\t}\n\t}\n}\n","import { DiError } from \"../error\";\n\nexport class InvalidProviderError extends DiError {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\n\t\tthis.name = \"InvalidProviderError\";\n\t}\n}\n","export const Scopes = {\n\tSingleton: \"singleton\",\n\tTransient: \"transient\",\n\tScoped: \"scoped\",\n} as const;\n\nexport type Scope = (typeof Scopes)[keyof typeof Scopes];\n","import { type Scope, Scopes } from \"../scope\";\nimport type { Token } from \"../token\";\nimport { InvalidProviderError } from \"./errors\";\nimport type {\n\tAnyFactoryProvider,\n\tDependency,\n\tDepsMap,\n\tDisposeHook,\n\tFactory,\n\tFactoryProvider,\n\tOptionalDependency,\n\tProvider,\n\tValueProvider,\n} from \"./types\";\n\nexport const provideValue = <T>(\n\ttoken: Token<T>,\n\tuseValue: T,\n): ValueProvider<T> => ({\n\tprovide: token,\n\tuseValue,\n});\n\nexport const provideFactory = <T, TDeps extends DepsMap = Record<never, never>>(\n\ttoken: Token<T>,\n\toptions: {\n\t\treadonly deps?: TDeps;\n\t\treadonly scope?: Scope;\n\t\treadonly useFactory: Factory<T, TDeps>;\n\t\treadonly onDispose?: DisposeHook<T>;\n\t},\n): FactoryProvider<T, TDeps> => {\n\t// Transient instances aren't cached, so onDispose could never run.\n\tif (options.scope === Scopes.Transient && options.onDispose) {\n\t\tthrow new InvalidProviderError(\n\t\t\t`onDispose is not supported for transient providers (token \"${token.name}\"): transient instances are not tracked, so the hook would never run.`,\n\t\t);\n\t}\n\n\treturn {\n\t\tprovide: token,\n\t\tuseFactory: options.useFactory,\n\t\tscope: options.scope ?? Scopes.Singleton,\n\t\t...(options.deps ? { deps: options.deps } : {}),\n\t\t...(options.onDispose ? { onDispose: options.onDispose } : {}),\n\t};\n};\n\n// Marks a dependency optional: resolves to undefined when no provider exists.\nexport const optional = <T>(token: Token<T>): OptionalDependency<T> => ({\n\ttoken,\n\toptional: true,\n});\n\nexport const isOptionalDependency = <T>(\n\tdependency: Dependency<T>,\n): dependency is OptionalDependency<T> => {\n\treturn (dependency as OptionalDependency<T>).optional === true;\n};\n\nexport const isValueProvider = (\n\tprovider: Provider,\n): provider is ValueProvider<unknown> => {\n\treturn \"useValue\" in provider;\n};\n\nexport const isFactoryProvider = (\n\tprovider: Provider,\n): provider is AnyFactoryProvider => {\n\treturn \"useFactory\" in provider;\n};\n","import { DiError } from \"../error\";\n\nexport class DuplicateProviderError extends DiError {\n\tconstructor(tokenName: string) {\n\t\tsuper(`Provider for token \"${tokenName}\" is already registered`);\n\n\t\tthis.name = \"DuplicateProviderError\";\n\t}\n}\n","import type { Provider } from \"../provider\";\nimport type { Token } from \"../token\";\nimport { DuplicateProviderError } from \"./errors\";\nimport type { RegisterOptions, Registry } from \"./types\";\n\nclass RegistryClass implements Registry {\n\tprivate readonly providers = new Map<symbol, Provider>();\n\n\tregister(provider: Provider, options?: RegisterOptions): boolean {\n\t\tconst existed = this.providers.has(provider.provide.id);\n\n\t\tif (existed && !options?.allowOverride) {\n\t\t\tthrow new DuplicateProviderError(provider.provide.name);\n\t\t}\n\n\t\tthis.providers.set(provider.provide.id, provider);\n\n\t\t// true if an existing provider was replaced (override).\n\t\treturn existed;\n\t}\n\n\tget(token: Token<unknown>): Provider | undefined {\n\t\treturn this.providers.get(token.id);\n\t}\n\n\thas(token: Token<unknown>): boolean {\n\t\treturn this.providers.has(token.id);\n\t}\n}\n\nexport const createRegistry = (): Registry => new RegistryClass();\n","import { DiError } from \"../error\";\n\nexport class MissingProviderError extends DiError {\n\tconstructor(tokenName: string) {\n\t\tsuper(`Provider for token \"${tokenName}\" is not registered`);\n\n\t\tthis.name = \"MissingProviderError\";\n\t}\n}\n\nexport class InvalidDependencyError extends DiError {\n\tconstructor(dependencyKey: string) {\n\t\tsuper(`Invalid dependency \"${dependencyKey}\"`);\n\n\t\tthis.name = \"InvalidDependencyError\";\n\t}\n}\n\nexport class CircularDependencyError extends DiError {\n\tconstructor(tokenNames: string[]) {\n\t\tsuper(`Circular dependency detected: ${tokenNames.join(\" -> \")}`);\n\n\t\tthis.name = \"CircularDependencyError\";\n\t}\n}\n","import type { Token } from \"../token\";\nimport type { InstanceRecord, Store } from \"./types\";\n\nclass StoreClass implements Store {\n\tprivate readonly instances = new Map<symbol, InstanceRecord>();\n\n\tget(token: Token<unknown>): InstanceRecord | undefined {\n\t\treturn this.instances.get(token.id);\n\t}\n\n\tset(token: Token<unknown>, record: InstanceRecord): void {\n\t\tthis.instances.set(token.id, record);\n\t}\n\n\tdelete(token: Token<unknown>): void {\n\t\tthis.instances.delete(token.id);\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\t// Snapshot and clear first so dispose() is idempotent and re-entrancy safe.\n\t\tconst records = [...this.instances.values()].reverse();\n\n\t\tthis.instances.clear();\n\n\t\tfor (const record of records) {\n\t\t\tif (record.onDispose) {\n\t\t\t\tawait record.onDispose(record.value);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const createStore = (): Store => new StoreClass();\n","import type { Token } from \"../token\";\nimport { CircularDependencyError } from \"./errors\";\n\nexport class ResolutionContext {\n\tprivate readonly resolving = new Set<symbol>();\n\tprivate readonly path: Token<unknown>[] = [];\n\n\tenter(token: Token<unknown>): void {\n\t\tif (this.resolving.has(token.id)) {\n\t\t\tconst cycleStartIndex = this.path.findIndex(\n\t\t\t\t(pathToken) => pathToken.id === token.id,\n\t\t\t);\n\n\t\t\tconst cycle = [...this.path.slice(cycleStartIndex), token];\n\n\t\t\tthrow new CircularDependencyError(\n\t\t\t\tcycle.map((cycleToken) => cycleToken.name),\n\t\t\t);\n\t\t}\n\n\t\tthis.resolving.add(token.id);\n\t\tthis.path.push(token);\n\t}\n\n\texit(token: Token<unknown>): void {\n\t\tthis.path.pop();\n\t\tthis.resolving.delete(token.id);\n\t}\n}\n","import type { DepsMap, Provider, ResolveDeps } from \"../provider\";\nimport {\n\tInvalidProviderError,\n\tisOptionalDependency,\n\tisValueProvider,\n} from \"../provider\";\nimport type { Registry } from \"../registry\";\nimport { type Scope, Scopes } from \"../scope\";\nimport { createStore, type Store } from \"../store\";\nimport type { Token } from \"../token\";\nimport { ResolutionContext } from \"./context\";\nimport { InvalidDependencyError, MissingProviderError } from \"./errors\";\nimport type { Resolver } from \"./types\";\n\n// Lifetime ordering: longer-lived scopes rank lower. A provider may only depend\n// on dependencies that live at least as long as itself.\nconst lifetimeRank = (scope: Scope | undefined): number =>\n\tscope === Scopes.Scoped ? 1 : scope === Scopes.Transient ? 2 : 0;\n\nclass ResolverClass implements Resolver {\n\tprivate readonly registry: Registry;\n\tprivate readonly store: Store = createStore();\n\tprivate readonly parent: ResolverClass | undefined;\n\n\tconstructor(registry: Registry, parent?: ResolverClass) {\n\t\tthis.registry = registry;\n\t\tthis.parent = parent;\n\t}\n\n\tresolve<T>(token: Token<T>): T {\n\t\treturn this.resolveToken(token, undefined);\n\t}\n\n\tresolveOptional<T>(token: Token<T>): T | undefined {\n\t\treturn this.lookupOptional(token, undefined);\n\t}\n\n\tinvalidate(token: Token<unknown>): void {\n\t\tthis.store.delete(token);\n\t}\n\n\t// True if a locally-cached instance has an onDispose hook.\n\thasDisposableInstance(token: Token<unknown>): boolean {\n\t\treturn this.store.get(token)?.onDispose !== undefined;\n\t}\n\n\tdispose(): Promise<void> {\n\t\treturn this.store.dispose();\n\t}\n\n\t// context is allocated lazily by the first building factory (zero-alloc cache hits).\n\t// consumerScope/consumerName describe the provider depending on this token, if any.\n\tprivate resolveToken<T>(\n\t\ttoken: Token<T>,\n\t\tcontext: ResolutionContext | undefined,\n\t\tconsumerScope?: Scope,\n\t\tconsumerName?: string,\n\t): T {\n\t\t// Single walk up the parent chain, fetching the provider directly.\n\t\tlet owner: ResolverClass | undefined = this;\n\t\tlet provider: Provider | undefined;\n\n\t\twhile (owner) {\n\t\t\tprovider = owner.registry.get(token);\n\n\t\t\tif (provider) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\towner = owner.parent;\n\t\t}\n\n\t\tif (!provider || !owner) {\n\t\t\tthrow new MissingProviderError(token.name);\n\t\t}\n\n\t\t// Values outlive every scope, so they are always a safe dependency.\n\t\tif (isValueProvider(provider)) {\n\t\t\treturn provider.useValue as T;\n\t\t}\n\n\t\t// Otherwise it is a factory provider (the only remaining variant).\n\t\t// Reject capturing a shorter-lived dependency into a longer-lived consumer.\n\t\tif (\n\t\t\tconsumerScope !== undefined &&\n\t\t\tlifetimeRank(provider.scope) > lifetimeRank(consumerScope)\n\t\t) {\n\t\t\tthrow new InvalidProviderError(\n\t\t\t\t`\"${consumerName}\" (${consumerScope}) cannot depend on \"${token.name}\" (${provider.scope ?? Scopes.Singleton}): a longer-lived provider would capture a shorter-lived one. Widen the dependency's scope or narrow the consumer's.`,\n\t\t\t);\n\t\t}\n\n\t\t// Scope decides the host: singleton on owner, scoped on this, transient nowhere.\n\t\tconst host = this.selectHost(provider.scope, owner);\n\n\t\tif (host) {\n\t\t\tconst cached = host.store.get(token);\n\n\t\t\tif (cached) {\n\t\t\t\treturn cached.value as T;\n\t\t\t}\n\t\t}\n\n\t\t// Allocate cycle detection only now, threading it through the build.\n\t\tconst ctx = context ?? new ResolutionContext();\n\t\tctx.enter(token);\n\n\t\ttry {\n\t\t\t// Resolve deps from the host: scoped sees this container, singleton sees owner.\n\t\t\tconst deps = (host ?? this).resolveDeps(\n\t\t\t\tprovider.deps,\n\t\t\t\tctx,\n\t\t\t\tprovider.scope,\n\t\t\t\ttoken.name,\n\t\t\t);\n\t\t\tconst value = provider.useFactory(deps) as T;\n\n\t\t\tif (host) {\n\t\t\t\thost.store.set(token, {\n\t\t\t\t\tvalue,\n\t\t\t\t\t...(provider.onDispose ? { onDispose: provider.onDispose } : {}),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn value;\n\t\t} finally {\n\t\t\tctx.exit(token);\n\t\t}\n\t}\n\n\tprivate selectHost(\n\t\tscope: Scope | undefined,\n\t\towner: ResolverClass,\n\t): ResolverClass | undefined {\n\t\tif (scope === Scopes.Transient) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (scope === Scopes.Scoped) {\n\t\t\treturn this;\n\t\t}\n\n\t\treturn owner;\n\t}\n\n\tprivate resolveDeps<TDeps extends DepsMap>(\n\t\tdeps: TDeps | undefined,\n\t\tcontext: ResolutionContext,\n\t\tconsumerScope: Scope | undefined,\n\t\tconsumerName: string,\n\t): ResolveDeps<TDeps> {\n\t\tif (!deps) {\n\t\t\treturn {} as ResolveDeps<TDeps>;\n\t\t}\n\n\t\tconst resolvedDeps: Partial<ResolveDeps<TDeps>> = {};\n\n\t\tfor (const key of Object.keys(deps) as Array<keyof TDeps>) {\n\t\t\tconst dependency = deps[key];\n\n\t\t\tif (dependency === undefined) {\n\t\t\t\tthrow new InvalidDependencyError(String(key));\n\t\t\t}\n\n\t\t\tresolvedDeps[key] = (\n\t\t\t\tisOptionalDependency(dependency)\n\t\t\t\t\t? this.lookupOptional(\n\t\t\t\t\t\t\tdependency.token,\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\tconsumerScope,\n\t\t\t\t\t\t\tconsumerName,\n\t\t\t\t\t\t)\n\t\t\t\t\t: this.resolveToken(dependency, context, consumerScope, consumerName)\n\t\t\t) as ResolveDeps<TDeps>[typeof key];\n\t\t}\n\n\t\treturn resolvedDeps as ResolveDeps<TDeps>;\n\t}\n\n\t// Absent provider -> undefined; a present one resolves normally (its errors surface).\n\tprivate lookupOptional<T>(\n\t\ttoken: Token<T>,\n\t\tcontext: ResolutionContext | undefined,\n\t\tconsumerScope?: Scope,\n\t\tconsumerName?: string,\n\t): T | undefined {\n\t\tlet owner: ResolverClass | undefined = this;\n\n\t\twhile (owner) {\n\t\t\tif (owner.registry.has(token)) {\n\t\t\t\treturn this.resolveToken(token, context, consumerScope, consumerName);\n\t\t\t}\n\n\t\t\towner = owner.parent;\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n\nexport const createResolver = (\n\tregistry: Registry,\n\tparent?: Resolver,\n): Resolver => new ResolverClass(registry, parent as ResolverClass | undefined);\n","import {\n\ttype Dependency,\n\tInvalidProviderError,\n\tisOptionalDependency,\n\ttype OptionalDependency,\n\ttype Provider,\n} from \"../provider\";\nimport {\n\tcreateRegistry,\n\ttype RegisterOptions,\n\ttype Registry,\n} from \"../registry\";\nimport { createResolver, type Resolver } from \"../resolver\";\nimport type { Token } from \"../token\";\nimport type { Container } from \"./types\";\n\nclass ContainerClass implements Container {\n\tprivate readonly registry: Registry;\n\tprivate readonly resolver: Resolver;\n\tprivate readonly parent: ContainerClass | undefined;\n\n\tconstructor(providers: readonly Provider[] = [], parent?: ContainerClass) {\n\t\tthis.parent = parent;\n\t\tthis.registry = createRegistry();\n\n\t\tfor (const provider of providers) {\n\t\t\tthis.registry.register(provider);\n\t\t}\n\n\t\tthis.resolver = createResolver(this.registry, parent?.resolver);\n\t}\n\n\tregister(provider: Provider, options?: RegisterOptions): void {\n\t\t// Don't silently drop a live disposable instance; require explicit dispose.\n\t\tif (\n\t\t\toptions?.allowOverride &&\n\t\t\tthis.resolver.hasDisposableInstance(provider.provide)\n\t\t) {\n\t\t\tthrow new InvalidProviderError(\n\t\t\t\t`Cannot override token \"${provider.provide.name}\": its instance was already created and has an onDispose hook. Dispose the container before replacing it.`,\n\t\t\t);\n\t\t}\n\n\t\tconst overridden = this.registry.register(provider, options);\n\n\t\tif (overridden) {\n\t\t\tthis.resolver.invalidate(provider.provide);\n\t\t}\n\t}\n\n\tget<T>(token: Token<T>): T;\n\tget<T>(dependency: OptionalDependency<T>): T | undefined;\n\tget<T>(dependency: Dependency<T>): T | undefined {\n\t\tif (isOptionalDependency(dependency)) {\n\t\t\treturn this.resolver.resolveOptional(dependency.token);\n\t\t}\n\n\t\treturn this.resolver.resolve(dependency);\n\t}\n\n\thas(token: Token<unknown>): boolean {\n\t\treturn this.registry.has(token) || (this.parent?.has(token) ?? false);\n\t}\n\n\tdispose(): Promise<void> {\n\t\treturn this.resolver.dispose();\n\t}\n}\n\nexport const createContainer = (\n\tproviders: readonly Provider[] = [],\n): Container => new ContainerClass(providers);\n\nexport const createChildContainer = (\n\tparent: Container,\n\tproviders: readonly Provider[] = [],\n): Container => new ContainerClass(providers, parent as ContainerClass);\n","import type { Token } from \"./types\";\n\nclass TokenClass<T> implements Token<T> {\n\treadonly name: string;\n\treadonly id: symbol;\n\n\tdeclare readonly __type?: T;\n\n\tconstructor(name: string) {\n\t\tthis.name = name;\n\t\tthis.id = Symbol(name);\n\t}\n}\n\nexport const createToken = <T>(name: string): Token<T> =>\n\tnew TokenClass<T>(name);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "di-craft",
3
- "version": "0.0.10",
4
- "description": "A tiny TypeScript dependency injection container",
3
+ "version": "0.0.12",
4
+ "description": "A tiny, type-safe dependency injection container for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Egor Bezmen",
@@ -51,8 +51,12 @@
51
51
  "dependency-injection",
52
52
  "di",
53
53
  "ioc",
54
+ "inversion-of-control",
54
55
  "typescript",
55
- "container"
56
+ "type-safe",
57
+ "container",
58
+ "scoped",
59
+ "tree-shakable"
56
60
  ],
57
61
  "homepage": "https://github.com/bezmen-e/di-craft",
58
62
  "repository": {