katagami 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -584
- package/dist/chunk-J2NYR3SH.js +6 -0
- package/dist/container/index.d.cts +108 -0
- package/dist/container/index.d.ts +2 -2
- package/dist/disposable/index.cjs +16 -25
- package/dist/disposable/index.d.cts +69 -0
- package/dist/disposable/index.d.ts +13 -5
- package/dist/disposable/index.js +1 -1
- package/dist/error/index.d.cts +11 -0
- package/dist/index.cjs +91 -55
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +76 -31
- package/dist/internal.d.cts +29 -0
- package/dist/internal.d.ts +3 -1
- package/dist/lazy/index.cjs +16 -25
- package/dist/lazy/index.d.cts +33 -0
- package/dist/lazy/index.d.ts +3 -3
- package/dist/lazy/index.js +1 -1
- package/dist/resolver/index.d.cts +93 -0
- package/dist/scope/index.d.cts +120 -0
- package/dist/scope/index.d.ts +4 -4
- package/docs/README.de.md +84 -0
- package/docs/README.es.md +84 -0
- package/docs/README.fr.md +84 -0
- package/docs/README.ja.md +105 -0
- package/docs/README.ko.md +84 -0
- package/docs/README.zh-CN.md +84 -0
- package/docs/README.zh-TW.md +84 -0
- package/docs/ai-coding-agents.md +78 -0
- package/docs/articles/ai-coding-agents.ja.md +83 -0
- package/docs/articles/ai-coding-agents.md +70 -0
- package/docs/articles/request-scope.md +48 -0
- package/docs/articles/without-decorators.md +54 -0
- package/docs/choosing-di.md +143 -0
- package/docs/growth/baseline-2026-09-11.json +68 -0
- package/docs/growth/github-metadata.json +13 -0
- package/docs/growth/rollout.md +77 -0
- package/docs/guide.md +186 -0
- package/docs/type-safety.md +126 -0
- package/examples/request-scope/README.md +37 -0
- package/examples/request-scope/app.ts +31 -0
- package/examples/request-scope/demo.ts +10 -0
- package/examples/request-scope/tsconfig.json +11 -0
- package/llms.txt +16 -0
- package/package.json +56 -23
- package/dist/index-jx8b52m0.js +0 -4
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type ContainerInternals, INTERNALS } from '../internal.cjs';
|
|
2
|
+
import type { AbstractConstructor, Resolver } from '../resolver/index.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* Create a new DI container.
|
|
5
|
+
*
|
|
6
|
+
* Pass an interface as generic T to fix the PropertyKey token type map upfront (order-independent).
|
|
7
|
+
* Class tokens are accumulated via registerSingleton/registerTransient method chaining (order-dependent).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* interface Services { SampleController: string }
|
|
12
|
+
* const c = createContainer<Services>()
|
|
13
|
+
* .registerSingleton(TextGenerationService, () => new MastraTextGenerationService())
|
|
14
|
+
* .registerTransient(GenerateTextUseCase, r => new GenerateTextUseCase(r.resolve(TextGenerationService)));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function createContainer<T = Record<never, never>, ScopedT = Record<never, never>>(): Container<T, never, never, ScopedT>;
|
|
18
|
+
/**
|
|
19
|
+
* Lightweight DI container — registration only.
|
|
20
|
+
*
|
|
21
|
+
* Provides type inference through method chaining with registerSingleton/registerTransient/registerScoped.
|
|
22
|
+
* Resolution is performed through a Scope created via `createScope(container)`.
|
|
23
|
+
*
|
|
24
|
+
* Registering the same token multiple times accumulates all factories.
|
|
25
|
+
*
|
|
26
|
+
* @template T PropertyKey-based token type map (defined via interface, order-independent)
|
|
27
|
+
* @template Sync Union of registered sync class constructors (accumulated via chaining, order-dependent)
|
|
28
|
+
* @template Async Union of registered async class constructors (accumulated via chaining, order-dependent)
|
|
29
|
+
* @template ScopedT PropertyKey-based token type map for scoped registrations
|
|
30
|
+
* @template ScopedSync Union of scoped sync class constructors (accumulated via chaining, order-dependent)
|
|
31
|
+
* @template ScopedAsync Union of scoped async class constructors (accumulated via chaining, order-dependent)
|
|
32
|
+
*/
|
|
33
|
+
export declare class Container<T = Record<never, never>, Sync extends AbstractConstructor = never, Async extends AbstractConstructor = never, ScopedT = Record<never, never>, ScopedSync extends AbstractConstructor = never, ScopedAsync extends AbstractConstructor = never> {
|
|
34
|
+
private readonly registrations;
|
|
35
|
+
private readonly singletonCache;
|
|
36
|
+
private disposed;
|
|
37
|
+
/**
|
|
38
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
readonly [INTERNALS]: ContainerInternals;
|
|
43
|
+
constructor();
|
|
44
|
+
/**
|
|
45
|
+
* Register a factory function as a singleton for the given token.
|
|
46
|
+
*
|
|
47
|
+
* Creates the instance on the first resolve and returns the cached value thereafter.
|
|
48
|
+
* If the same token is registered multiple times, all factories are accumulated.
|
|
49
|
+
* `resolve()` returns the last registered instance; `resolveAll()` returns all.
|
|
50
|
+
*
|
|
51
|
+
* @param token Any value to use as a token
|
|
52
|
+
* @param factory Factory function that receives a resolver and returns an instance
|
|
53
|
+
* @returns The container for method chaining
|
|
54
|
+
*/
|
|
55
|
+
registerSingleton<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T, Sync, Async>) => Promise<V>): Container<T, Sync, Async | AbstractConstructor<V>, ScopedT, ScopedSync, ScopedAsync>;
|
|
56
|
+
registerSingleton<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<T, Sync | AbstractConstructor<V>, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
57
|
+
registerSingleton<K extends PropertyKey, V>(token: K, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<Record<K, V> & T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
58
|
+
registerSingleton<V>(token: unknown, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
59
|
+
/**
|
|
60
|
+
* Register a factory function as transient for the given token.
|
|
61
|
+
*
|
|
62
|
+
* Creates a new instance via the factory function on every resolve.
|
|
63
|
+
* If the same token is registered multiple times, all factories are accumulated.
|
|
64
|
+
* `resolve()` returns the last registered instance; `resolveAll()` returns all.
|
|
65
|
+
*
|
|
66
|
+
* @param token Any value to use as a token
|
|
67
|
+
* @param factory Factory function that receives a resolver and returns an instance
|
|
68
|
+
* @returns The container for method chaining
|
|
69
|
+
*/
|
|
70
|
+
registerTransient<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T, Sync, Async>) => Promise<V>): Container<T, Sync, Async | AbstractConstructor<V>, ScopedT, ScopedSync, ScopedAsync>;
|
|
71
|
+
registerTransient<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<T, Sync | AbstractConstructor<V>, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
72
|
+
registerTransient<K extends PropertyKey, V>(token: K, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<Record<K, V> & T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
73
|
+
registerTransient<V>(token: unknown, factory: (resolver: Resolver<T, Sync, Async>) => V): Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
74
|
+
/**
|
|
75
|
+
* Register a factory function as scoped for the given token.
|
|
76
|
+
*
|
|
77
|
+
* Within a scope, creates the instance on the first resolve and returns the cached value thereafter.
|
|
78
|
+
* Each scope maintains its own cache, so different scopes produce different instances.
|
|
79
|
+
* Scoped tokens cannot be resolved from the root container — use createScope() first.
|
|
80
|
+
*
|
|
81
|
+
* @param token Any value to use as a token
|
|
82
|
+
* @param factory Factory function that receives a resolver and returns an instance
|
|
83
|
+
* @returns The container for method chaining
|
|
84
|
+
*/
|
|
85
|
+
registerScoped<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>) => Promise<V>): Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync | AbstractConstructor<V>>;
|
|
86
|
+
registerScoped<V>(token: AbstractConstructor<V>, factory: (resolver: Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>) => V): Container<T, Sync, Async, ScopedT, ScopedSync | AbstractConstructor<V>, ScopedAsync>;
|
|
87
|
+
registerScoped<K extends PropertyKey, V>(token: K, factory: (resolver: Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>) => V): Container<T, Sync, Async, Record<K, V> & ScopedT, ScopedSync, ScopedAsync>;
|
|
88
|
+
registerScoped<V>(token: unknown, factory: (resolver: Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>) => V): Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
89
|
+
/**
|
|
90
|
+
* Apply all registrations from another container (module) to this container.
|
|
91
|
+
*
|
|
92
|
+
* Copies registration entries (factory + lifetime) by replacing existing entries for each token.
|
|
93
|
+
* Singleton instance caches are not shared — each container manages its own.
|
|
94
|
+
*
|
|
95
|
+
* @param source A container whose registrations will be copied into this container
|
|
96
|
+
* @returns The container for method chaining
|
|
97
|
+
*/
|
|
98
|
+
use<MT, MSync extends AbstractConstructor, MAsync extends AbstractConstructor, MScopedT, MScopedSync extends AbstractConstructor, MScopedAsync extends AbstractConstructor>(source: Container<MT, MSync, MAsync, MScopedT, MScopedSync, MScopedAsync>): Container<T & MT, Sync | MSync, Async | MAsync, ScopedT & MScopedT, ScopedSync | MScopedSync, ScopedAsync | MScopedAsync>;
|
|
99
|
+
/**
|
|
100
|
+
* Add a registration entry. Accumulates registrations for the same token.
|
|
101
|
+
*
|
|
102
|
+
* @param token Token
|
|
103
|
+
* @param factory Factory function
|
|
104
|
+
* @param lifetime Lifetime of the registration
|
|
105
|
+
* @returns The container for method chaining
|
|
106
|
+
*/
|
|
107
|
+
private addRegistration;
|
|
108
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type ContainerInternals, INTERNALS } from '../internal';
|
|
2
|
-
import type { AbstractConstructor, Resolver } from '../resolver';
|
|
1
|
+
import { type ContainerInternals, INTERNALS } from '../internal.js';
|
|
2
|
+
import type { AbstractConstructor, Resolver } from '../resolver/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Create a new DI container.
|
|
5
5
|
*
|
|
@@ -1,40 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
-
var __toCommonJS = (from) => {
|
|
7
|
-
var entry = __moduleCache.get(from), desc;
|
|
8
|
-
if (entry)
|
|
9
|
-
return entry;
|
|
10
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
-
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
-
get: () => from[key],
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
}));
|
|
16
|
-
__moduleCache.set(from, entry);
|
|
17
|
-
return entry;
|
|
18
|
-
};
|
|
19
6
|
var __export = (target, all) => {
|
|
20
7
|
for (var name in all)
|
|
21
|
-
__defProp(target, name, {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
27
17
|
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
19
|
|
|
29
20
|
// src/disposable/index.ts
|
|
30
|
-
var
|
|
31
|
-
__export(
|
|
21
|
+
var disposable_exports = {};
|
|
22
|
+
__export(disposable_exports, {
|
|
32
23
|
disposable: () => disposable
|
|
33
24
|
});
|
|
34
|
-
module.exports = __toCommonJS(
|
|
25
|
+
module.exports = __toCommonJS(disposable_exports);
|
|
35
26
|
|
|
36
27
|
// src/internal.ts
|
|
37
|
-
var INTERNALS = Symbol("katagami.internals");
|
|
28
|
+
var INTERNALS = /* @__PURE__ */ Symbol.for("katagami.internals.v3");
|
|
38
29
|
|
|
39
30
|
// src/disposable/index.ts
|
|
40
31
|
function disposable(container) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Container } from '../container/index.cjs';
|
|
2
|
+
import { type ContainerInternals, INTERNALS, type TYPE_STATE } from '../internal.cjs';
|
|
3
|
+
import type { AbstractConstructor, Resolver } from '../resolver/index.cjs';
|
|
4
|
+
import type { Scope } from '../scope/index.cjs';
|
|
5
|
+
/**
|
|
6
|
+
* A container wrapped with `disposable()`.
|
|
7
|
+
*
|
|
8
|
+
* Registration methods (`registerSingleton`, `registerTransient`, `registerScoped`, `use`)
|
|
9
|
+
* are excluded, preventing accidental registration on a potentially-disposed container.
|
|
10
|
+
* Use `createScope()` to create a scope for resolution.
|
|
11
|
+
*
|
|
12
|
+
* @template T PropertyKey-based token type map
|
|
13
|
+
* @template Sync Union of registered sync class constructors
|
|
14
|
+
* @template Async Union of registered async class constructors
|
|
15
|
+
* @template ScopedT PropertyKey-based token type map for scoped registrations
|
|
16
|
+
* @template ScopedSync Union of scoped sync class constructors
|
|
17
|
+
* @template ScopedAsync Union of scoped async class constructors
|
|
18
|
+
*/
|
|
19
|
+
export interface DisposableContainer<T = Record<never, never>, Sync extends AbstractConstructor = never, Async extends AbstractConstructor = never, ScopedT = Record<never, never>, ScopedSync extends AbstractConstructor = never, ScopedAsync extends AbstractConstructor = never> extends AsyncDisposable {
|
|
20
|
+
readonly [INTERNALS]: ContainerInternals;
|
|
21
|
+
readonly [TYPE_STATE]?: {
|
|
22
|
+
readonly kind: 'container';
|
|
23
|
+
readonly registrations: readonly [T, Sync, Async, ScopedT, ScopedSync, ScopedAsync];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A scope wrapped with `disposable()`.
|
|
28
|
+
*
|
|
29
|
+
* Only `resolve`, `tryResolve`, `resolveAll`, and `tryResolveAll` are available at the type level.
|
|
30
|
+
*
|
|
31
|
+
* @template T PropertyKey-based token type map
|
|
32
|
+
* @template Sync Union of registered sync class constructors
|
|
33
|
+
* @template Async Union of registered async class constructors
|
|
34
|
+
* @template ScopedT PropertyKey-based token type map for scoped registrations
|
|
35
|
+
* @template ScopedSync Union of scoped sync class constructors
|
|
36
|
+
* @template ScopedAsync Union of scoped async class constructors
|
|
37
|
+
*/
|
|
38
|
+
export interface DisposableScope<T = Record<never, never>, Sync extends AbstractConstructor = never, Async extends AbstractConstructor = never, ScopedT = Record<never, never>, ScopedSync extends AbstractConstructor = never, ScopedAsync extends AbstractConstructor = never> extends Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>, AsyncDisposable {
|
|
39
|
+
readonly [INTERNALS]: ContainerInternals;
|
|
40
|
+
readonly [TYPE_STATE]?: {
|
|
41
|
+
readonly kind: 'scope';
|
|
42
|
+
readonly registrations: readonly [T, Sync, Async, ScopedT, ScopedSync, ScopedAsync];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add async disposal capability to a container or scope.
|
|
47
|
+
*
|
|
48
|
+
* Enables `await using` syntax by attaching `[Symbol.asyncDispose]` to the target.
|
|
49
|
+
* Disposes owned instances in reverse creation order (LIFO), calling
|
|
50
|
+
* `[Symbol.asyncDispose]()` or `[Symbol.dispose]()` on each instance that implements them.
|
|
51
|
+
*
|
|
52
|
+
* The returned type prevents registration methods from being called on a potentially-disposed container.
|
|
53
|
+
* For scopes, `resolve`, `tryResolve`, `resolveAll`, and `tryResolveAll` remain available.
|
|
54
|
+
*
|
|
55
|
+
* @param container A Container or Scope to make disposable
|
|
56
|
+
* @returns The same object with `AsyncDisposable` capability added and registration methods removed from the type
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { createContainer } from 'katagami';
|
|
61
|
+
* import { disposable } from 'katagami/disposable';
|
|
62
|
+
*
|
|
63
|
+
* await using container = disposable(
|
|
64
|
+
* createContainer().registerSingleton(DB, () => new Database())
|
|
65
|
+
* );
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function disposable<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(container: Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): DisposableContainer<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
69
|
+
export declare function disposable<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(scope: Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): DisposableScope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Container } from '../container';
|
|
2
|
-
import { type ContainerInternals, INTERNALS } from '../internal';
|
|
3
|
-
import type { AbstractConstructor, Resolver } from '../resolver';
|
|
4
|
-
import type { Scope } from '../scope';
|
|
1
|
+
import type { Container } from '../container/index.js';
|
|
2
|
+
import { type ContainerInternals, INTERNALS, type TYPE_STATE } from '../internal.js';
|
|
3
|
+
import type { AbstractConstructor, Resolver } from '../resolver/index.js';
|
|
4
|
+
import type { Scope } from '../scope/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* A container wrapped with `disposable()`.
|
|
7
7
|
*
|
|
@@ -16,8 +16,12 @@ import type { Scope } from '../scope';
|
|
|
16
16
|
* @template ScopedSync Union of scoped sync class constructors
|
|
17
17
|
* @template ScopedAsync Union of scoped async class constructors
|
|
18
18
|
*/
|
|
19
|
-
export interface DisposableContainer<
|
|
19
|
+
export interface DisposableContainer<T = Record<never, never>, Sync extends AbstractConstructor = never, Async extends AbstractConstructor = never, ScopedT = Record<never, never>, ScopedSync extends AbstractConstructor = never, ScopedAsync extends AbstractConstructor = never> extends AsyncDisposable {
|
|
20
20
|
readonly [INTERNALS]: ContainerInternals;
|
|
21
|
+
readonly [TYPE_STATE]?: {
|
|
22
|
+
readonly kind: 'container';
|
|
23
|
+
readonly registrations: readonly [T, Sync, Async, ScopedT, ScopedSync, ScopedAsync];
|
|
24
|
+
};
|
|
21
25
|
}
|
|
22
26
|
/**
|
|
23
27
|
* A scope wrapped with `disposable()`.
|
|
@@ -33,6 +37,10 @@ export interface DisposableContainer<_T = Record<never, never>, _Sync extends Ab
|
|
|
33
37
|
*/
|
|
34
38
|
export interface DisposableScope<T = Record<never, never>, Sync extends AbstractConstructor = never, Async extends AbstractConstructor = never, ScopedT = Record<never, never>, ScopedSync extends AbstractConstructor = never, ScopedAsync extends AbstractConstructor = never> extends Resolver<T & ScopedT, Sync | ScopedSync, Async | ScopedAsync>, AsyncDisposable {
|
|
35
39
|
readonly [INTERNALS]: ContainerInternals;
|
|
40
|
+
readonly [TYPE_STATE]?: {
|
|
41
|
+
readonly kind: 'scope';
|
|
42
|
+
readonly registrations: readonly [T, Sync, Async, ScopedT, ScopedSync, ScopedAsync];
|
|
43
|
+
};
|
|
36
44
|
}
|
|
37
45
|
/**
|
|
38
46
|
* Add async disposal capability to a container or scope.
|
package/dist/disposable/index.js
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown by the DI container.
|
|
3
|
+
*
|
|
4
|
+
* Represents failures in container operations such as resolving an unregistered token.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ContainerError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* @param message Error message
|
|
9
|
+
*/
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -1,58 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
-
var __toCommonJS = (from) => {
|
|
7
|
-
var entry = __moduleCache.get(from), desc;
|
|
8
|
-
if (entry)
|
|
9
|
-
return entry;
|
|
10
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
-
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
-
get: () => from[key],
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
}));
|
|
16
|
-
__moduleCache.set(from, entry);
|
|
17
|
-
return entry;
|
|
18
|
-
};
|
|
19
6
|
var __export = (target, all) => {
|
|
20
7
|
for (var name in all)
|
|
21
|
-
__defProp(target, name, {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
27
17
|
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
19
|
|
|
29
20
|
// src/index.ts
|
|
30
|
-
var
|
|
31
|
-
__export(
|
|
32
|
-
|
|
33
|
-
createContainer: () => createContainer,
|
|
34
|
-
Scope: () => Scope,
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Container: () => Container,
|
|
35
24
|
ContainerError: () => ContainerError,
|
|
36
|
-
|
|
25
|
+
Scope: () => Scope,
|
|
26
|
+
createContainer: () => createContainer,
|
|
27
|
+
createScope: () => createScope
|
|
37
28
|
});
|
|
38
|
-
module.exports = __toCommonJS(
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
30
|
|
|
40
31
|
// src/internal.ts
|
|
41
|
-
var INTERNALS = Symbol("katagami.internals");
|
|
32
|
+
var INTERNALS = /* @__PURE__ */ Symbol.for("katagami.internals.v3");
|
|
42
33
|
|
|
43
34
|
// src/container/index.ts
|
|
44
35
|
function createContainer() {
|
|
45
|
-
return new Container;
|
|
36
|
+
return new Container();
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
class Container {
|
|
38
|
+
var Container = class {
|
|
49
39
|
registrations;
|
|
50
40
|
singletonCache;
|
|
51
41
|
disposed = false;
|
|
42
|
+
/**
|
|
43
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
52
47
|
[INTERNALS];
|
|
53
48
|
constructor() {
|
|
54
|
-
this.registrations = new Map;
|
|
55
|
-
this.singletonCache = new Map;
|
|
49
|
+
this.registrations = /* @__PURE__ */ new Map();
|
|
50
|
+
this.singletonCache = /* @__PURE__ */ new Map();
|
|
56
51
|
this[INTERNALS] = {
|
|
57
52
|
isDisposed: () => this.disposed,
|
|
58
53
|
markDisposed: () => {
|
|
@@ -78,23 +73,36 @@ class Container {
|
|
|
78
73
|
}
|
|
79
74
|
return this;
|
|
80
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Add a registration entry. Accumulates registrations for the same token.
|
|
78
|
+
*
|
|
79
|
+
* @param token Token
|
|
80
|
+
* @param factory Factory function
|
|
81
|
+
* @param lifetime Lifetime of the registration
|
|
82
|
+
* @returns The container for method chaining
|
|
83
|
+
*/
|
|
81
84
|
addRegistration(token, factory, lifetime) {
|
|
82
85
|
const existing = this.registrations.get(token);
|
|
83
|
-
if (existing !==
|
|
86
|
+
if (existing !== void 0) {
|
|
84
87
|
existing.push({ factory, lifetime });
|
|
85
88
|
} else {
|
|
86
89
|
this.registrations.set(token, [{ factory, lifetime }]);
|
|
87
90
|
}
|
|
88
91
|
return this;
|
|
89
92
|
}
|
|
90
|
-
}
|
|
93
|
+
};
|
|
94
|
+
|
|
91
95
|
// src/error/index.ts
|
|
92
|
-
|
|
96
|
+
var ContainerError = class extends Error {
|
|
97
|
+
/**
|
|
98
|
+
* @param message Error message
|
|
99
|
+
*/
|
|
93
100
|
constructor(message) {
|
|
94
101
|
super(message);
|
|
95
102
|
this.name = "ContainerError";
|
|
96
103
|
}
|
|
97
|
-
}
|
|
104
|
+
};
|
|
105
|
+
|
|
98
106
|
// src/resolver/index.ts
|
|
99
107
|
function tokenToString(token) {
|
|
100
108
|
if (typeof token === "function") {
|
|
@@ -128,20 +136,24 @@ function createScope(source) {
|
|
|
128
136
|
}
|
|
129
137
|
return new Scope(internals.registrations, internals.singletonCache);
|
|
130
138
|
}
|
|
131
|
-
|
|
132
|
-
class Scope {
|
|
139
|
+
var Scope = class {
|
|
133
140
|
registrations;
|
|
134
141
|
singletonCache;
|
|
135
142
|
scopedCache;
|
|
136
143
|
resolvingTokens;
|
|
137
144
|
singletonDepth = 0;
|
|
138
145
|
disposed = false;
|
|
146
|
+
/**
|
|
147
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
148
|
+
*
|
|
149
|
+
* @internal
|
|
150
|
+
*/
|
|
139
151
|
[INTERNALS];
|
|
140
152
|
constructor(registrations, singletonCache) {
|
|
141
153
|
this.registrations = registrations;
|
|
142
154
|
this.singletonCache = singletonCache;
|
|
143
|
-
this.scopedCache = new Map;
|
|
144
|
-
this.resolvingTokens = new Set;
|
|
155
|
+
this.scopedCache = /* @__PURE__ */ new Map();
|
|
156
|
+
this.resolvingTokens = /* @__PURE__ */ new Set();
|
|
145
157
|
this[INTERNALS] = {
|
|
146
158
|
isDisposed: () => this.disposed,
|
|
147
159
|
markDisposed: () => {
|
|
@@ -164,27 +176,37 @@ class Scope {
|
|
|
164
176
|
tryResolveAll(token) {
|
|
165
177
|
return this.resolveAllTokens(token, false);
|
|
166
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Internal resolution logic shared by resolve and tryResolve.
|
|
181
|
+
* Resolves the last registered factory for the token.
|
|
182
|
+
*
|
|
183
|
+
* @param token Token to resolve
|
|
184
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
185
|
+
* @returns The resolved instance, or undefined if not registered and required is false
|
|
186
|
+
*/
|
|
167
187
|
resolveToken(token, required) {
|
|
168
188
|
if (this.disposed) {
|
|
169
189
|
throw new ContainerError("Cannot resolve from a disposed scope.");
|
|
170
190
|
}
|
|
171
191
|
const registrations = this.registrations.get(token);
|
|
172
|
-
if (registrations ===
|
|
192
|
+
if (registrations === void 0 || registrations.length === 0) {
|
|
173
193
|
if (required) {
|
|
174
194
|
throw new ContainerError(`Token "${tokenToString(token)}" is not registered.`);
|
|
175
195
|
}
|
|
176
|
-
return;
|
|
196
|
+
return void 0;
|
|
177
197
|
}
|
|
178
198
|
const registration = registrations[registrations.length - 1];
|
|
179
199
|
const singletonCached = this.singletonCache.get(registration);
|
|
180
|
-
if (singletonCached !==
|
|
200
|
+
if (singletonCached !== void 0) {
|
|
181
201
|
return singletonCached;
|
|
182
202
|
}
|
|
183
203
|
if (registration.lifetime === "scoped" && this.singletonDepth > 0) {
|
|
184
|
-
throw new ContainerError(
|
|
204
|
+
throw new ContainerError(
|
|
205
|
+
`Captive dependency detected: scoped token "${tokenToString(token)}" cannot be resolved inside a singleton factory. Scoped instances must not be captured by singletons.`
|
|
206
|
+
);
|
|
185
207
|
}
|
|
186
208
|
const scopedCached = this.scopedCache.get(registration);
|
|
187
|
-
if (scopedCached !==
|
|
209
|
+
if (scopedCached !== void 0) {
|
|
188
210
|
return scopedCached;
|
|
189
211
|
}
|
|
190
212
|
if (this.resolvingTokens.has(token)) {
|
|
@@ -195,7 +217,9 @@ class Scope {
|
|
|
195
217
|
this.singletonDepth++;
|
|
196
218
|
}
|
|
197
219
|
try {
|
|
198
|
-
const instance = registration.factory(
|
|
220
|
+
const instance = registration.factory(
|
|
221
|
+
this
|
|
222
|
+
);
|
|
199
223
|
if (registration.lifetime === "singleton") {
|
|
200
224
|
this.singletonCache.set(registration, instance);
|
|
201
225
|
} else if (registration.lifetime === "scoped") {
|
|
@@ -209,16 +233,24 @@ class Scope {
|
|
|
209
233
|
this.resolvingTokens.delete(token);
|
|
210
234
|
}
|
|
211
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Internal resolution logic shared by resolveAll and tryResolveAll.
|
|
238
|
+
* Resolves all registered factories for the token.
|
|
239
|
+
*
|
|
240
|
+
* @param token Token to resolve
|
|
241
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
242
|
+
* @returns An array of resolved instances, or undefined if not registered and required is false
|
|
243
|
+
*/
|
|
212
244
|
resolveAllTokens(token, required) {
|
|
213
245
|
if (this.disposed) {
|
|
214
246
|
throw new ContainerError("Cannot resolve from a disposed scope.");
|
|
215
247
|
}
|
|
216
248
|
const registrations = this.registrations.get(token);
|
|
217
|
-
if (registrations ===
|
|
249
|
+
if (registrations === void 0 || registrations.length === 0) {
|
|
218
250
|
if (required) {
|
|
219
251
|
throw new ContainerError(`Token "${tokenToString(token)}" is not registered.`);
|
|
220
252
|
}
|
|
221
|
-
return;
|
|
253
|
+
return void 0;
|
|
222
254
|
}
|
|
223
255
|
if (this.resolvingTokens.has(token)) {
|
|
224
256
|
throw new ContainerError(`Circular dependency detected: ${buildCircularPath(this.resolvingTokens, token)}`);
|
|
@@ -228,21 +260,25 @@ class Scope {
|
|
|
228
260
|
return registrations.map((registration) => {
|
|
229
261
|
const reg = registration;
|
|
230
262
|
const singletonCached = this.singletonCache.get(registration);
|
|
231
|
-
if (singletonCached !==
|
|
263
|
+
if (singletonCached !== void 0) {
|
|
232
264
|
return singletonCached;
|
|
233
265
|
}
|
|
234
266
|
if (reg.lifetime === "scoped" && this.singletonDepth > 0) {
|
|
235
|
-
throw new ContainerError(
|
|
267
|
+
throw new ContainerError(
|
|
268
|
+
`Captive dependency detected: scoped token "${tokenToString(token)}" cannot be resolved inside a singleton factory. Scoped instances must not be captured by singletons.`
|
|
269
|
+
);
|
|
236
270
|
}
|
|
237
271
|
const scopedCached = this.scopedCache.get(registration);
|
|
238
|
-
if (scopedCached !==
|
|
272
|
+
if (scopedCached !== void 0) {
|
|
239
273
|
return scopedCached;
|
|
240
274
|
}
|
|
241
275
|
if (reg.lifetime === "singleton") {
|
|
242
276
|
this.singletonDepth++;
|
|
243
277
|
}
|
|
244
278
|
try {
|
|
245
|
-
const instance = reg.factory(
|
|
279
|
+
const instance = reg.factory(
|
|
280
|
+
this
|
|
281
|
+
);
|
|
246
282
|
if (reg.lifetime === "singleton") {
|
|
247
283
|
this.singletonCache.set(registration, instance);
|
|
248
284
|
} else if (reg.lifetime === "scoped") {
|
|
@@ -259,4 +295,4 @@ class Scope {
|
|
|
259
295
|
this.resolvingTokens.delete(token);
|
|
260
296
|
}
|
|
261
297
|
}
|
|
262
|
-
}
|
|
298
|
+
};
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Container, createContainer } from './container/index.cjs';
|
|
2
|
+
export type { DisposableContainer, DisposableScope, disposable } from './disposable/index.cjs';
|
|
3
|
+
export { ContainerError } from './error/index.cjs';
|
|
4
|
+
export type { lazy } from './lazy/index.cjs';
|
|
5
|
+
export type { Resolver } from './resolver/index.cjs';
|
|
6
|
+
export { createScope, Scope } from './scope/index.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { Container, createContainer } from './container';
|
|
2
|
-
export type { DisposableContainer, DisposableScope, disposable } from './disposable';
|
|
3
|
-
export { ContainerError } from './error';
|
|
4
|
-
export type { lazy } from './lazy';
|
|
5
|
-
export type { Resolver } from './resolver';
|
|
6
|
-
export { createScope, Scope } from './scope';
|
|
1
|
+
export { Container, createContainer } from './container/index.js';
|
|
2
|
+
export type { DisposableContainer, DisposableScope, disposable } from './disposable/index.js';
|
|
3
|
+
export { ContainerError } from './error/index.js';
|
|
4
|
+
export type { lazy } from './lazy/index.js';
|
|
5
|
+
export type { Resolver } from './resolver/index.js';
|
|
6
|
+
export { createScope, Scope } from './scope/index.js';
|