di-craft 0.0.11 → 0.0.13

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
@@ -11,12 +11,14 @@ A tiny, type-safe dependency injection container for TypeScript.
11
11
  - [Core concepts](#core-concepts)
12
12
  - [Tokens](#tokens)
13
13
  - [Providers](#providers)
14
+ - [Optional dependencies](#optional-dependencies)
14
15
  - [Container](#container)
15
16
  - [Scopes](#scopes)
16
17
  - [Disposal](#disposal)
17
18
  - [Child containers](#child-containers)
18
19
  - [Cycle detection](#cycle-detection)
19
- - [Example: per-request container](#example-per-request-container)
20
+ - [Async dependencies](#async-dependencies)
21
+ - [Dependency injection vs service location](#dependency-injection-vs-service-location)
20
22
  - [Error handling](#error-handling)
21
23
  - [API reference](#api-reference)
22
24
  - [License](#license)
@@ -67,6 +69,7 @@ framework coupling. You work with just **tokens**, **providers**, a **container*
67
69
 
68
70
  - Zero runtime dependencies
69
71
  - Type-safe tokens and factories
72
+ - Optional dependencies via `optional()`
70
73
  - Singleton, transient, and scoped lifetimes
71
74
  - Hierarchical child containers
72
75
  - Deterministic disposal with `onDispose` hooks
@@ -121,6 +124,37 @@ provideFactory(HTTP, {
121
124
 
122
125
  The keys in `deps` become the keys of the object passed to `useFactory`, each resolved to its token's type.
123
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
+
124
158
  ### Container
125
159
 
126
160
  The container holds your providers and resolves values on demand. Create one
@@ -129,7 +163,7 @@ dispose:
129
163
 
130
164
  - `register(provider, options?)` — add a provider at any time.
131
165
  - `has(token)` — whether a provider for the token is registered.
132
- - `get(token)` — resolve the value, building and caching it as its scope dictates.
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.
133
167
  - `dispose()` — run `onDispose` hooks and release resolved instances.
134
168
 
135
169
  ```ts
@@ -150,7 +184,10 @@ container.register(provideValue(API, fakeApi), { allowOverride: true });
150
184
  ```
151
185
 
152
186
  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.
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.
154
191
 
155
192
  ### Scopes
156
193
 
@@ -173,7 +210,12 @@ provideFactory(ID, {
173
210
  container.get(ID) !== container.get(ID); // true
174
211
  ```
175
212
 
176
- 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.
177
219
 
178
220
  ### Disposal
179
221
 
@@ -201,7 +243,8 @@ Details:
201
243
  - Hooks run in reverse creation order (dependents before their dependencies).
202
244
  - `dispose()` returns a promise and awaits async hooks.
203
245
  - It is idempotent — calling it again is a no-op.
204
- - Only resolved singletons are disposed; transient and never-resolved instances are not tracked.
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.
205
248
 
206
249
  ### Child containers
207
250
 
@@ -247,86 +290,79 @@ If providers form a dependency cycle, resolution throws `CircularDependencyError
247
290
  container.get(A); // throws: Circular dependency detected: A -> B -> A
248
291
  ```
249
292
 
250
- ## Example: per-request container
293
+ ### Async dependencies
251
294
 
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.
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:
257
301
 
258
302
  ```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";
303
+ const db = await connectDatabase(config);
304
+ container.register(provideValue(DB, db));
305
+ ```
269
306
 
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");
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:
274
310
 
275
- const app = Fastify({ logger: true });
311
+ ```ts
312
+ const POOL = createToken<Promise<Pool>>("pool");
276
313
 
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
- ]);
314
+ container.register(provideFactory(POOL, { useFactory: () => createPool() }));
294
315
 
295
- // Keep each request's child container without patching Fastify's types.
296
- const containers = new WeakMap<FastifyRequest, Container>();
316
+ const pool = await container.get(POOL);
317
+ ```
297
318
 
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
- });
319
+ A factory that depends on `POOL` receives the promise and awaits it itself:
302
320
 
303
- app.addHook("onResponse", async (request) => {
304
- // Release this request's scoped instances once the response is sent.
305
- await containers.get(request)?.dispose();
321
+ ```ts
322
+ provideFactory(USERS, {
323
+ deps: { pool: POOL },
324
+ useFactory: async ({ pool }) => new UsersRepo(await pool),
306
325
  });
326
+ // USERS is now Token<Promise<UsersRepo>> — consumers await it too.
327
+ ```
307
328
 
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
329
+ ## Dependency injection vs service location
312
330
 
313
- return users.findById(request.params.id, { traceId: id });
314
- });
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.
315
334
 
316
- // On shutdown, dispose the root to close the shared pool.
317
- app.addHook("onClose", async () => {
318
- await root.dispose();
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),
319
346
  });
320
347
 
321
- await app.listen({ port: 3000 });
348
+ const users = container.get(USERS); // resolved at the root, then injected down
322
349
  ```
323
350
 
324
- What each scope buys you here:
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
+ ```
325
361
 
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`.
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.
330
366
 
331
367
  ## Error handling
332
368
 
@@ -354,6 +390,7 @@ try {
354
390
  | `DuplicateProviderError` | A token is registered more than once. |
355
391
  | `CircularDependencyError` | Providers form a dependency cycle. |
356
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. |
357
394
 
358
395
  ## API reference
359
396
 
@@ -362,13 +399,14 @@ try {
362
399
  | `createToken<T>(name)` | Create a unique, typed token. |
363
400
  | `provideValue(token, value)` | Provider that returns an existing value. |
364
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). |
365
403
  | `createContainer(providers?)` | Create a container, optionally seeded with providers. |
366
404
  | `createChildContainer(parent, providers?)` | Create a child container that inherits from `parent`. |
367
405
  | `Scopes` | Object of scope values (`Scopes.Singleton`, `Scopes.Transient`, `Scopes.Scoped`). |
368
406
 
369
- Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Scope`, `DisposeHook`, `RegisterOptions`.
407
+ Exported types: `Container`, `Token`, `Provider`, `ValueProvider`, `FactoryProvider`, `Dependency`, `OptionalDependency`, `Scope`, `DisposeHook`, `RegisterOptions`.
370
408
 
371
- Exported errors: `DiError`, `MissingProviderError`, `DuplicateProviderError`, `CircularDependencyError`, `InvalidDependencyError`.
409
+ Exported errors: `DiError`, `MissingProviderError`, `DuplicateProviderError`, `CircularDependencyError`, `InvalidDependencyError`, `InvalidProviderError`.
372
410
 
373
411
  ## License
374
412
 
package/dist/index.cjs CHANGED
@@ -1,240 +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, options) {
23
- if (!options?.allowOverride && 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/scope/scope.ts
56
- const Scopes = {
57
- Singleton: "singleton",
58
- Transient: "transient",
59
- Scoped: "scoped"
60
- };
61
- //#endregion
62
- //#region src/provider/provider.ts
63
- const provideValue = (token, useValue) => ({
64
- provide: token,
65
- useValue
66
- });
67
- const provideFactory = (token, options) => {
68
- return {
69
- provide: token,
70
- useFactory: options.useFactory,
71
- scope: options.scope ?? Scopes.Singleton,
72
- ...options.deps ? { deps: options.deps } : {},
73
- ...options.onDispose ? { onDispose: options.onDispose } : {}
74
- };
75
- };
76
- const isValueProvider = (provider) => {
77
- return "useValue" in provider;
78
- };
79
- const isFactoryProvider = (provider) => {
80
- return "useFactory" in provider;
81
- };
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
121
- //#region src/resolver/resolver.ts
122
- var ResolverClass = class {
123
- registry;
124
- store = createStore();
125
- parent;
126
- constructor(registry, parent) {
127
- this.registry = registry;
128
- this.parent = parent;
129
- }
130
- resolve(token) {
131
- return this.resolveToken(token, new ResolutionContext());
132
- }
133
- invalidate(token) {
134
- this.store.delete(token);
135
- }
136
- dispose() {
137
- return this.store.dispose();
138
- }
139
- resolveToken(token, context) {
140
- const owner = this.findOwner(token);
141
- if (!owner) throw new MissingProviderError(token.name);
142
- const provider = owner.registry.get(token);
143
- if (!provider) throw new MissingProviderError(token.name);
144
- if (isValueProvider(provider)) return provider.useValue;
145
- if (isFactoryProvider(provider)) {
146
- const host = this.selectHost(provider.scope, owner);
147
- if (host) {
148
- const cached = host.store.get(token);
149
- if (cached) return cached.value;
150
- }
151
- context.enter(token);
152
- try {
153
- const deps = (host ?? this).resolveDeps(provider.deps, context);
154
- const value = provider.useFactory(deps);
155
- if (host) host.store.set(token, {
156
- value,
157
- ...provider.onDispose ? { onDispose: provider.onDispose } : {}
158
- });
159
- return value;
160
- } finally {
161
- context.exit(token);
162
- }
163
- }
164
- throw new MissingProviderError(token.name);
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
- }
178
- resolveDeps(deps, context) {
179
- if (!deps) return {};
180
- const resolvedDeps = {};
181
- for (const key of Object.keys(deps)) {
182
- const token = deps[key];
183
- if (token === void 0) throw new InvalidDependencyError(String(key));
184
- resolvedDeps[key] = this.resolveToken(token, context);
185
- }
186
- return resolvedDeps;
187
- }
188
- };
189
- const createResolver = (registry, parent) => new ResolverClass(registry, parent);
190
- //#endregion
191
- //#region src/container/container.ts
192
- var ContainerClass = class {
193
- registry;
194
- resolver;
195
- parent;
196
- constructor(providers = [], parent) {
197
- this.parent = parent;
198
- this.registry = createRegistry();
199
- for (const provider of providers) this.registry.register(provider);
200
- this.resolver = createResolver(this.registry, parent?.resolver);
201
- }
202
- register(provider, options) {
203
- this.registry.register(provider, options);
204
- if (options?.allowOverride) this.resolver.invalidate(provider.provide);
205
- }
206
- get(token) {
207
- return this.resolver.resolve(token);
208
- }
209
- has(token) {
210
- return this.registry.has(token) || (this.parent?.has(token) ?? false);
211
- }
212
- dispose() {
213
- return this.resolver.dispose();
214
- }
215
- };
216
- const createContainer = (providers = []) => new ContainerClass(providers);
217
- const createChildContainer = (parent, providers = []) => new ContainerClass(providers, parent);
218
- //#endregion
219
- //#region src/token/token.ts
220
- var TokenClass = class {
221
- name;
222
- id;
223
- constructor(name) {
224
- this.name = name;
225
- this.id = Symbol(name);
226
- }
227
- };
228
- const createToken = (name) => new TokenClass(name);
229
- //#endregion
230
- exports.CircularDependencyError = CircularDependencyError;
231
- exports.DiError = DiError;
232
- exports.DuplicateProviderError = DuplicateProviderError;
233
- exports.InvalidDependencyError = InvalidDependencyError;
234
- exports.MissingProviderError = MissingProviderError;
235
- exports.Scopes = Scopes;
236
- exports.createChildContainer = createChildContainer;
237
- exports.createContainer = createContainer;
238
- exports.createToken = createToken;
239
- exports.provideFactory = provideFactory;
240
- 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,13 @@
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
1
11
  //#region src/scope/scope.d.ts
2
12
  declare const Scopes: {
3
13
  readonly Singleton: "singleton";
@@ -17,9 +27,14 @@ type Token<T> = {
17
27
  declare const createToken: <T>(name: string) => Token<T>;
18
28
  //#endregion
19
29
  //#region src/provider/types.d.ts
20
- type DepsMap = Record<string, Token<unknown>>;
21
- type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
22
- 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]> };
23
38
  type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
24
39
  type DisposeHook<T> = (instance: T) => void | Promise<void>;
25
40
  type ValueProvider<T> = {
@@ -44,11 +59,7 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
44
59
  readonly useFactory: Factory<T, TDeps>;
45
60
  readonly onDispose?: DisposeHook<T>;
46
61
  }) => FactoryProvider<T, TDeps>;
47
- //#endregion
48
- //#region src/error/error.d.ts
49
- declare class DiError extends Error {
50
- constructor(message: string);
51
- }
62
+ declare const optional: <T>(token: Token<T>) => OptionalDependency<T>;
52
63
  //#endregion
53
64
  //#region src/registry/errors.d.ts
54
65
  declare class DuplicateProviderError extends DiError {
@@ -64,6 +75,7 @@ type RegisterOptions = {
64
75
  type Container = {
65
76
  register(provider: Provider, options?: RegisterOptions): void;
66
77
  get<T>(token: Token<T>): T;
78
+ get<T>(dependency: OptionalDependency<T>): T | undefined;
67
79
  has(token: Token<unknown>): boolean;
68
80
  dispose(): Promise<void>;
69
81
  };
@@ -83,4 +95,5 @@ declare class CircularDependencyError extends DiError {
83
95
  constructor(tokenNames: string[]);
84
96
  }
85
97
  //#endregion
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 };
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,13 @@
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
1
11
  //#region src/scope/scope.d.ts
2
12
  declare const Scopes: {
3
13
  readonly Singleton: "singleton";
@@ -17,9 +27,14 @@ type Token<T> = {
17
27
  declare const createToken: <T>(name: string) => Token<T>;
18
28
  //#endregion
19
29
  //#region src/provider/types.d.ts
20
- type DepsMap = Record<string, Token<unknown>>;
21
- type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
22
- 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]> };
23
38
  type Factory<T, TDeps extends DepsMap> = (deps: ResolveDeps<TDeps>) => T;
24
39
  type DisposeHook<T> = (instance: T) => void | Promise<void>;
25
40
  type ValueProvider<T> = {
@@ -44,11 +59,7 @@ declare const provideFactory: <T, TDeps extends DepsMap = Record<never, never>>(
44
59
  readonly useFactory: Factory<T, TDeps>;
45
60
  readonly onDispose?: DisposeHook<T>;
46
61
  }) => FactoryProvider<T, TDeps>;
47
- //#endregion
48
- //#region src/error/error.d.ts
49
- declare class DiError extends Error {
50
- constructor(message: string);
51
- }
62
+ declare const optional: <T>(token: Token<T>) => OptionalDependency<T>;
52
63
  //#endregion
53
64
  //#region src/registry/errors.d.ts
54
65
  declare class DuplicateProviderError extends DiError {
@@ -64,6 +75,7 @@ type RegisterOptions = {
64
75
  type Container = {
65
76
  register(provider: Provider, options?: RegisterOptions): void;
66
77
  get<T>(token: Token<T>): T;
78
+ get<T>(dependency: OptionalDependency<T>): T | undefined;
67
79
  has(token: Token<unknown>): boolean;
68
80
  dispose(): Promise<void>;
69
81
  };
@@ -83,4 +95,5 @@ declare class CircularDependencyError extends DiError {
83
95
  constructor(tokenNames: string[]);
84
96
  }
85
97
  //#endregion
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 };
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,229 +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, options) {
22
- if (!options?.allowOverride && 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/scope/scope.ts
55
- const Scopes = {
56
- Singleton: "singleton",
57
- Transient: "transient",
58
- Scoped: "scoped"
59
- };
60
- //#endregion
61
- //#region src/provider/provider.ts
62
- const provideValue = (token, useValue) => ({
63
- provide: token,
64
- useValue
65
- });
66
- const provideFactory = (token, options) => {
67
- return {
68
- provide: token,
69
- useFactory: options.useFactory,
70
- scope: options.scope ?? Scopes.Singleton,
71
- ...options.deps ? { deps: options.deps } : {},
72
- ...options.onDispose ? { onDispose: options.onDispose } : {}
73
- };
74
- };
75
- const isValueProvider = (provider) => {
76
- return "useValue" in provider;
77
- };
78
- const isFactoryProvider = (provider) => {
79
- return "useFactory" in provider;
80
- };
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
120
- //#region src/resolver/resolver.ts
121
- var ResolverClass = class {
122
- registry;
123
- store = createStore();
124
- parent;
125
- constructor(registry, parent) {
126
- this.registry = registry;
127
- this.parent = parent;
128
- }
129
- resolve(token) {
130
- return this.resolveToken(token, new ResolutionContext());
131
- }
132
- invalidate(token) {
133
- this.store.delete(token);
134
- }
135
- dispose() {
136
- return this.store.dispose();
137
- }
138
- resolveToken(token, context) {
139
- const owner = this.findOwner(token);
140
- if (!owner) throw new MissingProviderError(token.name);
141
- const provider = owner.registry.get(token);
142
- if (!provider) throw new MissingProviderError(token.name);
143
- if (isValueProvider(provider)) return provider.useValue;
144
- if (isFactoryProvider(provider)) {
145
- const host = this.selectHost(provider.scope, owner);
146
- if (host) {
147
- const cached = host.store.get(token);
148
- if (cached) return cached.value;
149
- }
150
- context.enter(token);
151
- try {
152
- const deps = (host ?? this).resolveDeps(provider.deps, context);
153
- const value = provider.useFactory(deps);
154
- if (host) host.store.set(token, {
155
- value,
156
- ...provider.onDispose ? { onDispose: provider.onDispose } : {}
157
- });
158
- return value;
159
- } finally {
160
- context.exit(token);
161
- }
162
- }
163
- throw new MissingProviderError(token.name);
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
- }
177
- resolveDeps(deps, context) {
178
- if (!deps) return {};
179
- const resolvedDeps = {};
180
- for (const key of Object.keys(deps)) {
181
- const token = deps[key];
182
- if (token === void 0) throw new InvalidDependencyError(String(key));
183
- resolvedDeps[key] = this.resolveToken(token, context);
184
- }
185
- return resolvedDeps;
186
- }
187
- };
188
- const createResolver = (registry, parent) => new ResolverClass(registry, parent);
189
- //#endregion
190
- //#region src/container/container.ts
191
- var ContainerClass = class {
192
- registry;
193
- resolver;
194
- parent;
195
- constructor(providers = [], parent) {
196
- this.parent = parent;
197
- this.registry = createRegistry();
198
- for (const provider of providers) this.registry.register(provider);
199
- this.resolver = createResolver(this.registry, parent?.resolver);
200
- }
201
- register(provider, options) {
202
- this.registry.register(provider, options);
203
- if (options?.allowOverride) this.resolver.invalidate(provider.provide);
204
- }
205
- get(token) {
206
- return this.resolver.resolve(token);
207
- }
208
- has(token) {
209
- return this.registry.has(token) || (this.parent?.has(token) ?? false);
210
- }
211
- dispose() {
212
- return this.resolver.dispose();
213
- }
214
- };
215
- const createContainer = (providers = []) => new ContainerClass(providers);
216
- const createChildContainer = (parent, providers = []) => new ContainerClass(providers, parent);
217
- //#endregion
218
- //#region src/token/token.ts
219
- var TokenClass = class {
220
- name;
221
- id;
222
- constructor(name) {
223
- this.name = name;
224
- this.id = Symbol(name);
225
- }
226
- };
227
- const createToken = (name) => new TokenClass(name);
228
- //#endregion
229
- export { CircularDependencyError, DiError, DuplicateProviderError, InvalidDependencyError, MissingProviderError, Scopes, createChildContainer, 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":"AAAA,IAAa,EAAb,cAA6B,KAAM,CAClC,YAAY,EAAiB,CAC5B,MAAM,CAAO,EAEb,KAAK,KAAO,UAER,MAAM,mBACT,MAAM,kBAAkB,KAAM,KAAK,WAAW,CAEhD,CACD,ECRa,EAAb,cAA0C,CAAQ,CACjD,YAAY,EAAiB,CAC5B,MAAM,CAAO,EAEb,KAAK,KAAO,sBACb,CACD,ECRA,MAAa,EAAS,CACrB,UAAW,YACX,UAAW,YACX,OAAQ,QACT,ECWa,GACZ,EACA,KACuB,CACvB,QAAS,EACT,UACD,GAEa,GACZ,EACA,IAM+B,CAE/B,GAAI,EAAQ,QAAU,EAAO,WAAa,EAAQ,UACjD,MAAM,IAAI,EACT,8DAA8D,EAAM,KAAK,sEAC1E,EAGD,MAAO,CACN,QAAS,EACT,WAAY,EAAQ,WACpB,MAAO,EAAQ,OAAS,EAAO,UAC/B,GAAI,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,EAC7C,GAAI,EAAQ,UAAY,CAAE,UAAW,EAAQ,SAAU,EAAI,CAAC,CAC7D,CACD,EAGa,EAAe,IAA4C,CACvE,QACA,SAAU,EACX,GAEa,EACZ,GAEQ,EAAqC,WAAa,GAG9C,EACZ,GAEO,aAAc,EC7DtB,IAAa,EAAb,cAA4C,CAAQ,CACnD,YAAY,EAAmB,CAC9B,MAAM,uBAAuB,EAAU,wBAAwB,EAE/D,KAAK,KAAO,wBACb,CACD,ECHM,EAAN,KAAwC,CACvC,UAA6B,IAAI,IAEjC,SAAS,EAAoB,EAAoC,CAChE,IAAM,EAAU,KAAK,UAAU,IAAI,EAAS,QAAQ,EAAE,EAEtD,GAAI,GAAW,CAAC,GAAS,cACxB,MAAM,IAAI,EAAuB,EAAS,QAAQ,IAAI,EAMvD,OAHA,KAAK,UAAU,IAAI,EAAS,QAAQ,GAAI,CAAQ,EAGzC,CACR,CAEA,IAAI,EAA6C,CAChD,OAAO,KAAK,UAAU,IAAI,EAAM,EAAE,CACnC,CAEA,IAAI,EAAgC,CACnC,OAAO,KAAK,UAAU,IAAI,EAAM,EAAE,CACnC,CACD,EAEA,MAAa,MAAiC,IAAI,EC5BlD,IAAa,EAAb,cAA0C,CAAQ,CACjD,YAAY,EAAmB,CAC9B,MAAM,uBAAuB,EAAU,oBAAoB,EAE3D,KAAK,KAAO,sBACb,CACD,EAEa,EAAb,cAA4C,CAAQ,CACnD,YAAY,EAAuB,CAClC,MAAM,uBAAuB,EAAc,EAAE,EAE7C,KAAK,KAAO,wBACb,CACD,EAEa,EAAb,cAA6C,CAAQ,CACpD,YAAY,EAAsB,CACjC,MAAM,iCAAiC,EAAW,KAAK,MAAM,GAAG,EAEhE,KAAK,KAAO,yBACb,CACD,ECrBM,EAAN,KAAkC,CACjC,UAA6B,IAAI,IAEjC,IAAI,EAAmD,CACtD,OAAO,KAAK,UAAU,IAAI,EAAM,EAAE,CACnC,CAEA,IAAI,EAAuB,EAA8B,CACxD,KAAK,UAAU,IAAI,EAAM,GAAI,CAAM,CACpC,CAEA,OAAO,EAA6B,CACnC,KAAK,UAAU,OAAO,EAAM,EAAE,CAC/B,CAEA,MAAM,SAAyB,CAE9B,IAAM,EAAU,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,EAAE,QAAQ,EAErD,KAAK,UAAU,MAAM,EAErB,IAAK,IAAM,KAAU,EAChB,EAAO,WACV,MAAM,EAAO,UAAU,EAAO,KAAK,CAGtC,CACD,EAEA,MAAa,MAA2B,IAAI,EC7B5C,IAAa,EAAb,KAA+B,CAC9B,UAA6B,IAAI,IACjC,KAA0C,CAAC,EAE3C,MAAM,EAA6B,CAClC,GAAI,KAAK,UAAU,IAAI,EAAM,EAAE,EAAG,CACjC,IAAM,EAAkB,KAAK,KAAK,UAChC,GAAc,EAAU,KAAO,EAAM,EACvC,EAIA,MAAM,IAAI,EACT,CAHc,GAAG,KAAK,KAAK,MAAM,CAAe,EAAG,CAG/C,EAAE,IAAK,GAAe,EAAW,IAAI,CAC1C,CACD,CAEA,KAAK,UAAU,IAAI,EAAM,EAAE,EAC3B,KAAK,KAAK,KAAK,CAAK,CACrB,CAEA,KAAK,EAA6B,CACjC,KAAK,KAAK,IAAI,EACd,KAAK,UAAU,OAAO,EAAM,EAAE,CAC/B,CACD,ECZA,MAAM,EAAgB,GACrB,IAAU,EAAO,OAAS,EAAI,IAAU,EAAO,UAAY,EAAI,EAEhE,IAAM,EAAN,KAAwC,CACvC,SACA,MAAgC,EAAY,EAC5C,OAEA,YAAY,EAAoB,EAAwB,CACvD,KAAK,SAAW,EAChB,KAAK,OAAS,CACf,CAEA,QAAW,EAAoB,CAC9B,OAAO,KAAK,aAAa,EAAO,IAAA,EAAS,CAC1C,CAEA,gBAAmB,EAAgC,CAClD,OAAO,KAAK,eAAe,EAAO,IAAA,EAAS,CAC5C,CAEA,WAAW,EAA6B,CACvC,KAAK,MAAM,OAAO,CAAK,CACxB,CAGA,sBAAsB,EAAgC,CACrD,OAAO,KAAK,MAAM,IAAI,CAAK,GAAG,YAAc,IAAA,EAC7C,CAEA,SAAyB,CACxB,OAAO,KAAK,MAAM,QAAQ,CAC3B,CAIA,aACC,EACA,EACA,EACA,EACI,CAEJ,IAAI,EAAmC,KACnC,EAEJ,KAAO,IACN,EAAW,EAAM,SAAS,IAAI,CAAK,EAE/B,KAIJ,EAAQ,EAAM,OAGf,GAAI,CAAC,GAAY,CAAC,EACjB,MAAM,IAAI,EAAqB,EAAM,IAAI,EAI1C,GAAI,EAAgB,CAAQ,EAC3B,OAAO,EAAS,SAKjB,GACC,IAAkB,IAAA,IAClB,EAAa,EAAS,KAAK,EAAI,EAAa,CAAa,EAEzD,MAAM,IAAI,EACT,IAAI,EAAa,KAAK,EAAc,sBAAsB,EAAM,KAAK,KAAK,EAAS,OAAS,EAAO,UAAU,qHAC9G,EAID,IAAM,EAAO,KAAK,WAAW,EAAS,MAAO,CAAK,EAElD,GAAI,EAAM,CACT,IAAM,EAAS,EAAK,MAAM,IAAI,CAAK,EAEnC,GAAI,EACH,OAAO,EAAO,KAEhB,CAGA,IAAM,EAAM,GAAW,IAAI,EAC3B,EAAI,MAAM,CAAK,EAEf,GAAI,CAEH,IAAM,GAAQ,GAAQ,MAAM,YAC3B,EAAS,KACT,EACA,EAAS,MACT,EAAM,IACP,EACM,EAAQ,EAAS,WAAW,CAAI,EAStC,OAPI,GACH,EAAK,MAAM,IAAI,EAAO,CACrB,QACA,GAAI,EAAS,UAAY,CAAE,UAAW,EAAS,SAAU,EAAI,CAAC,CAC/D,CAAC,EAGK,CACR,QAAU,CACT,EAAI,KAAK,CAAK,CACf,CACD,CAEA,WACC,EACA,EAC4B,CACxB,OAAU,EAAO,UAQrB,OAJI,IAAU,EAAO,OACb,KAGD,CACR,CAEA,YACC,EACA,EACA,EACA,EACqB,CACrB,GAAI,CAAC,EACJ,MAAO,CAAC,EAGT,IAAM,EAA4C,CAAC,EAEnD,IAAK,IAAM,KAAO,OAAO,KAAK,CAAI,EAAyB,CAC1D,IAAM,EAAa,EAAK,GAExB,GAAI,IAAe,IAAA,GAClB,MAAM,IAAI,EAAuB,OAAO,CAAG,CAAC,EAG7C,EAAa,GACZ,EAAqB,CAAU,EAC5B,KAAK,eACL,EAAW,MACX,EACA,EACA,CACD,EACC,KAAK,aAAa,EAAY,EAAS,EAAe,CAAY,CAEvE,CAEA,OAAO,CACR,CAGA,eACC,EACA,EACA,EACA,EACgB,CAChB,IAAI,EAAmC,KAEvC,KAAO,GAAO,CACb,GAAI,EAAM,SAAS,IAAI,CAAK,EAC3B,OAAO,KAAK,aAAa,EAAO,EAAS,EAAe,CAAY,EAGrE,EAAQ,EAAM,MACf,CAGD,CACD,EAEA,MAAa,GACZ,EACA,IACc,IAAI,EAAc,EAAU,CAAmC,EC3L9E,IAAM,EAAN,KAA0C,CACzC,SACA,SACA,OAEA,YAAY,EAAiC,CAAC,EAAG,EAAyB,CACzE,KAAK,OAAS,EACd,KAAK,SAAW,EAAe,EAE/B,IAAK,IAAM,KAAY,EACtB,KAAK,SAAS,SAAS,CAAQ,EAGhC,KAAK,SAAW,EAAe,KAAK,SAAU,GAAQ,QAAQ,CAC/D,CAEA,SAAS,EAAoB,EAAiC,CAE7D,GACC,GAAS,eACT,KAAK,SAAS,sBAAsB,EAAS,OAAO,EAEpD,MAAM,IAAI,EACT,0BAA0B,EAAS,QAAQ,KAAK,0GACjD,EAGkB,KAAK,SAAS,SAAS,EAAU,CAEvC,GACZ,KAAK,SAAS,WAAW,EAAS,OAAO,CAE3C,CAIA,IAAO,EAA0C,CAKhD,OAJI,EAAqB,CAAU,EAC3B,KAAK,SAAS,gBAAgB,EAAW,KAAK,EAG/C,KAAK,SAAS,QAAQ,CAAU,CACxC,CAEA,IAAI,EAAgC,CACnC,OAAO,KAAK,SAAS,IAAI,CAAK,IAAM,KAAK,QAAQ,IAAI,CAAK,GAAK,GAChE,CAEA,SAAyB,CACxB,OAAO,KAAK,SAAS,QAAQ,CAC9B,CACD,EAEA,MAAa,GACZ,EAAiC,CAAC,IACnB,IAAI,EAAe,CAAS,EAE/B,GACZ,EACA,EAAiC,CAAC,IACnB,IAAI,EAAe,EAAW,CAAwB,EC1EtE,IAAM,EAAN,KAAwC,CACvC,KACA,GAIA,YAAY,EAAc,CACzB,KAAK,KAAO,EACZ,KAAK,GAAK,OAAO,CAAI,CACtB,CACD,EAEA,MAAa,EAAkB,GAC9B,IAAI,EAAc,CAAI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "di-craft",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A tiny, type-safe dependency injection container for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -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": {