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
package/dist/index.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
INTERNALS
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./chunk-J2NYR3SH.js";
|
|
4
4
|
|
|
5
5
|
// src/container/index.ts
|
|
6
6
|
function createContainer() {
|
|
7
|
-
return new Container;
|
|
7
|
+
return new Container();
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
class Container {
|
|
9
|
+
var Container = class {
|
|
11
10
|
registrations;
|
|
12
11
|
singletonCache;
|
|
13
12
|
disposed = false;
|
|
13
|
+
/**
|
|
14
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
14
18
|
[INTERNALS];
|
|
15
19
|
constructor() {
|
|
16
|
-
this.registrations = new Map;
|
|
17
|
-
this.singletonCache = new Map;
|
|
20
|
+
this.registrations = /* @__PURE__ */ new Map();
|
|
21
|
+
this.singletonCache = /* @__PURE__ */ new Map();
|
|
18
22
|
this[INTERNALS] = {
|
|
19
23
|
isDisposed: () => this.disposed,
|
|
20
24
|
markDisposed: () => {
|
|
@@ -40,23 +44,36 @@ class Container {
|
|
|
40
44
|
}
|
|
41
45
|
return this;
|
|
42
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Add a registration entry. Accumulates registrations for the same token.
|
|
49
|
+
*
|
|
50
|
+
* @param token Token
|
|
51
|
+
* @param factory Factory function
|
|
52
|
+
* @param lifetime Lifetime of the registration
|
|
53
|
+
* @returns The container for method chaining
|
|
54
|
+
*/
|
|
43
55
|
addRegistration(token, factory, lifetime) {
|
|
44
56
|
const existing = this.registrations.get(token);
|
|
45
|
-
if (existing !==
|
|
57
|
+
if (existing !== void 0) {
|
|
46
58
|
existing.push({ factory, lifetime });
|
|
47
59
|
} else {
|
|
48
60
|
this.registrations.set(token, [{ factory, lifetime }]);
|
|
49
61
|
}
|
|
50
62
|
return this;
|
|
51
63
|
}
|
|
52
|
-
}
|
|
64
|
+
};
|
|
65
|
+
|
|
53
66
|
// src/error/index.ts
|
|
54
|
-
|
|
67
|
+
var ContainerError = class extends Error {
|
|
68
|
+
/**
|
|
69
|
+
* @param message Error message
|
|
70
|
+
*/
|
|
55
71
|
constructor(message) {
|
|
56
72
|
super(message);
|
|
57
73
|
this.name = "ContainerError";
|
|
58
74
|
}
|
|
59
|
-
}
|
|
75
|
+
};
|
|
76
|
+
|
|
60
77
|
// src/resolver/index.ts
|
|
61
78
|
function tokenToString(token) {
|
|
62
79
|
if (typeof token === "function") {
|
|
@@ -90,20 +107,24 @@ function createScope(source) {
|
|
|
90
107
|
}
|
|
91
108
|
return new Scope(internals.registrations, internals.singletonCache);
|
|
92
109
|
}
|
|
93
|
-
|
|
94
|
-
class Scope {
|
|
110
|
+
var Scope = class {
|
|
95
111
|
registrations;
|
|
96
112
|
singletonCache;
|
|
97
113
|
scopedCache;
|
|
98
114
|
resolvingTokens;
|
|
99
115
|
singletonDepth = 0;
|
|
100
116
|
disposed = false;
|
|
117
|
+
/**
|
|
118
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
119
|
+
*
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
101
122
|
[INTERNALS];
|
|
102
123
|
constructor(registrations, singletonCache) {
|
|
103
124
|
this.registrations = registrations;
|
|
104
125
|
this.singletonCache = singletonCache;
|
|
105
|
-
this.scopedCache = new Map;
|
|
106
|
-
this.resolvingTokens = new Set;
|
|
126
|
+
this.scopedCache = /* @__PURE__ */ new Map();
|
|
127
|
+
this.resolvingTokens = /* @__PURE__ */ new Set();
|
|
107
128
|
this[INTERNALS] = {
|
|
108
129
|
isDisposed: () => this.disposed,
|
|
109
130
|
markDisposed: () => {
|
|
@@ -126,27 +147,37 @@ class Scope {
|
|
|
126
147
|
tryResolveAll(token) {
|
|
127
148
|
return this.resolveAllTokens(token, false);
|
|
128
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Internal resolution logic shared by resolve and tryResolve.
|
|
152
|
+
* Resolves the last registered factory for the token.
|
|
153
|
+
*
|
|
154
|
+
* @param token Token to resolve
|
|
155
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
156
|
+
* @returns The resolved instance, or undefined if not registered and required is false
|
|
157
|
+
*/
|
|
129
158
|
resolveToken(token, required) {
|
|
130
159
|
if (this.disposed) {
|
|
131
160
|
throw new ContainerError("Cannot resolve from a disposed scope.");
|
|
132
161
|
}
|
|
133
162
|
const registrations = this.registrations.get(token);
|
|
134
|
-
if (registrations ===
|
|
163
|
+
if (registrations === void 0 || registrations.length === 0) {
|
|
135
164
|
if (required) {
|
|
136
165
|
throw new ContainerError(`Token "${tokenToString(token)}" is not registered.`);
|
|
137
166
|
}
|
|
138
|
-
return;
|
|
167
|
+
return void 0;
|
|
139
168
|
}
|
|
140
169
|
const registration = registrations[registrations.length - 1];
|
|
141
170
|
const singletonCached = this.singletonCache.get(registration);
|
|
142
|
-
if (singletonCached !==
|
|
171
|
+
if (singletonCached !== void 0) {
|
|
143
172
|
return singletonCached;
|
|
144
173
|
}
|
|
145
174
|
if (registration.lifetime === "scoped" && this.singletonDepth > 0) {
|
|
146
|
-
throw new ContainerError(
|
|
175
|
+
throw new ContainerError(
|
|
176
|
+
`Captive dependency detected: scoped token "${tokenToString(token)}" cannot be resolved inside a singleton factory. Scoped instances must not be captured by singletons.`
|
|
177
|
+
);
|
|
147
178
|
}
|
|
148
179
|
const scopedCached = this.scopedCache.get(registration);
|
|
149
|
-
if (scopedCached !==
|
|
180
|
+
if (scopedCached !== void 0) {
|
|
150
181
|
return scopedCached;
|
|
151
182
|
}
|
|
152
183
|
if (this.resolvingTokens.has(token)) {
|
|
@@ -157,7 +188,9 @@ class Scope {
|
|
|
157
188
|
this.singletonDepth++;
|
|
158
189
|
}
|
|
159
190
|
try {
|
|
160
|
-
const instance = registration.factory(
|
|
191
|
+
const instance = registration.factory(
|
|
192
|
+
this
|
|
193
|
+
);
|
|
161
194
|
if (registration.lifetime === "singleton") {
|
|
162
195
|
this.singletonCache.set(registration, instance);
|
|
163
196
|
} else if (registration.lifetime === "scoped") {
|
|
@@ -171,16 +204,24 @@ class Scope {
|
|
|
171
204
|
this.resolvingTokens.delete(token);
|
|
172
205
|
}
|
|
173
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Internal resolution logic shared by resolveAll and tryResolveAll.
|
|
209
|
+
* Resolves all registered factories for the token.
|
|
210
|
+
*
|
|
211
|
+
* @param token Token to resolve
|
|
212
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
213
|
+
* @returns An array of resolved instances, or undefined if not registered and required is false
|
|
214
|
+
*/
|
|
174
215
|
resolveAllTokens(token, required) {
|
|
175
216
|
if (this.disposed) {
|
|
176
217
|
throw new ContainerError("Cannot resolve from a disposed scope.");
|
|
177
218
|
}
|
|
178
219
|
const registrations = this.registrations.get(token);
|
|
179
|
-
if (registrations ===
|
|
220
|
+
if (registrations === void 0 || registrations.length === 0) {
|
|
180
221
|
if (required) {
|
|
181
222
|
throw new ContainerError(`Token "${tokenToString(token)}" is not registered.`);
|
|
182
223
|
}
|
|
183
|
-
return;
|
|
224
|
+
return void 0;
|
|
184
225
|
}
|
|
185
226
|
if (this.resolvingTokens.has(token)) {
|
|
186
227
|
throw new ContainerError(`Circular dependency detected: ${buildCircularPath(this.resolvingTokens, token)}`);
|
|
@@ -190,21 +231,25 @@ class Scope {
|
|
|
190
231
|
return registrations.map((registration) => {
|
|
191
232
|
const reg = registration;
|
|
192
233
|
const singletonCached = this.singletonCache.get(registration);
|
|
193
|
-
if (singletonCached !==
|
|
234
|
+
if (singletonCached !== void 0) {
|
|
194
235
|
return singletonCached;
|
|
195
236
|
}
|
|
196
237
|
if (reg.lifetime === "scoped" && this.singletonDepth > 0) {
|
|
197
|
-
throw new ContainerError(
|
|
238
|
+
throw new ContainerError(
|
|
239
|
+
`Captive dependency detected: scoped token "${tokenToString(token)}" cannot be resolved inside a singleton factory. Scoped instances must not be captured by singletons.`
|
|
240
|
+
);
|
|
198
241
|
}
|
|
199
242
|
const scopedCached = this.scopedCache.get(registration);
|
|
200
|
-
if (scopedCached !==
|
|
243
|
+
if (scopedCached !== void 0) {
|
|
201
244
|
return scopedCached;
|
|
202
245
|
}
|
|
203
246
|
if (reg.lifetime === "singleton") {
|
|
204
247
|
this.singletonDepth++;
|
|
205
248
|
}
|
|
206
249
|
try {
|
|
207
|
-
const instance = reg.factory(
|
|
250
|
+
const instance = reg.factory(
|
|
251
|
+
this
|
|
252
|
+
);
|
|
208
253
|
if (reg.lifetime === "singleton") {
|
|
209
254
|
this.singletonCache.set(registration, instance);
|
|
210
255
|
} else if (reg.lifetime === "scoped") {
|
|
@@ -221,11 +266,11 @@ class Scope {
|
|
|
221
266
|
this.resolvingTokens.delete(token);
|
|
222
267
|
}
|
|
223
268
|
}
|
|
224
|
-
}
|
|
269
|
+
};
|
|
225
270
|
export {
|
|
226
|
-
|
|
227
|
-
createContainer,
|
|
228
|
-
Scope,
|
|
271
|
+
Container,
|
|
229
272
|
ContainerError,
|
|
230
|
-
|
|
273
|
+
Scope,
|
|
274
|
+
createContainer,
|
|
275
|
+
createScope
|
|
231
276
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Registration } from './resolver/index.cjs';
|
|
2
|
+
/**
|
|
3
|
+
* Symbol used by extension modules (scope, disposable) to access container/scope internals.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export declare const INTERNALS: unique symbol;
|
|
8
|
+
/** Type-only registration state retained by disposable views. No runtime property is emitted. */
|
|
9
|
+
export declare const TYPE_STATE: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Internal state exposed via the INTERNALS symbol.
|
|
12
|
+
*
|
|
13
|
+
* Both Container and Scope implement this interface so that extension modules
|
|
14
|
+
* (scope, disposable) can operate on either without importing the concrete class.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export interface ContainerInternals {
|
|
19
|
+
/** All registrations (singleton / transient / scoped). Each token maps to an array of registrations. */
|
|
20
|
+
readonly registrations: Map<unknown, Registration[]>;
|
|
21
|
+
/** Singleton cache keyed by Registration object (Container: singletons, Scope: shared with parent). */
|
|
22
|
+
readonly singletonCache: Map<Registration, unknown>;
|
|
23
|
+
/** Instances owned by this container / scope (disposal target), keyed by Registration object. */
|
|
24
|
+
readonly ownCache: Map<Registration, unknown>;
|
|
25
|
+
/** Whether this container / scope has been disposed. */
|
|
26
|
+
isDisposed(): boolean;
|
|
27
|
+
/** Mark this container / scope as disposed. */
|
|
28
|
+
markDisposed(): void;
|
|
29
|
+
}
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { Registration } from './resolver';
|
|
1
|
+
import type { Registration } from './resolver/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Symbol used by extension modules (scope, disposable) to access container/scope internals.
|
|
4
4
|
*
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
7
|
export declare const INTERNALS: unique symbol;
|
|
8
|
+
/** Type-only registration state retained by disposable views. No runtime property is emitted. */
|
|
9
|
+
export declare const TYPE_STATE: unique symbol;
|
|
8
10
|
/**
|
|
9
11
|
* Internal state exposed via the INTERNALS symbol.
|
|
10
12
|
*
|
package/dist/lazy/index.cjs
CHANGED
|
@@ -1,37 +1,28 @@
|
|
|
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/lazy/index.ts
|
|
30
|
-
var
|
|
31
|
-
__export(
|
|
21
|
+
var lazy_exports = {};
|
|
22
|
+
__export(lazy_exports, {
|
|
32
23
|
lazy: () => lazy
|
|
33
24
|
});
|
|
34
|
-
module.exports = __toCommonJS(
|
|
25
|
+
module.exports = __toCommonJS(lazy_exports);
|
|
35
26
|
function lazy(source, token) {
|
|
36
27
|
let instance;
|
|
37
28
|
let resolved = false;
|
|
@@ -42,7 +33,7 @@ function lazy(source, token) {
|
|
|
42
33
|
}
|
|
43
34
|
return instance;
|
|
44
35
|
};
|
|
45
|
-
const proxyTarget = Object.create(null);
|
|
36
|
+
const proxyTarget = /* @__PURE__ */ Object.create(null);
|
|
46
37
|
return new Proxy(proxyTarget, {
|
|
47
38
|
defineProperty(_, prop, desc) {
|
|
48
39
|
return Reflect.defineProperty(ensureResolved(), prop, desc);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DisposableScope } from '../disposable/index.cjs';
|
|
2
|
+
import type { AbstractConstructor } from '../resolver/index.cjs';
|
|
3
|
+
import type { Scope } from '../scope/index.cjs';
|
|
4
|
+
/**
|
|
5
|
+
* Create a lazy proxy that defers resolution until the first property access.
|
|
6
|
+
*
|
|
7
|
+
* The returned object looks and behaves like `V`, but the underlying instance
|
|
8
|
+
* is not created until a property is read, written, or otherwise accessed.
|
|
9
|
+
* Once resolved the instance is cached — subsequent accesses hit the cache.
|
|
10
|
+
*
|
|
11
|
+
* Only **sync class tokens** are supported. Async tokens and PropertyKey tokens
|
|
12
|
+
* are rejected at the type level.
|
|
13
|
+
*
|
|
14
|
+
* @param source A Scope or DisposableScope
|
|
15
|
+
* @param token A sync class constructor token
|
|
16
|
+
* @returns A proxy that transparently forwards to the lazily-resolved instance
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createContainer, createScope } from 'katagami';
|
|
21
|
+
* import { lazy } from 'katagami/lazy';
|
|
22
|
+
*
|
|
23
|
+
* const container = createContainer()
|
|
24
|
+
* .registerSingleton(HeavyService, () => new HeavyService());
|
|
25
|
+
*
|
|
26
|
+
* const scope = createScope(container);
|
|
27
|
+
* const service = lazy(scope, HeavyService);
|
|
28
|
+
* // Instance is NOT created yet
|
|
29
|
+
* service.doSomething(); // resolved here, then cached
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function lazy<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor, V>(source: Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>, token: AbstractConstructor<V> & (Sync | ScopedSync)): V;
|
|
33
|
+
export declare function lazy<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor, V>(source: DisposableScope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>, token: AbstractConstructor<V> & (Sync | ScopedSync)): V;
|
package/dist/lazy/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { DisposableScope } from '../disposable';
|
|
2
|
-
import type { AbstractConstructor } from '../resolver';
|
|
3
|
-
import type { Scope } from '../scope';
|
|
1
|
+
import type { DisposableScope } from '../disposable/index.js';
|
|
2
|
+
import type { AbstractConstructor } from '../resolver/index.js';
|
|
3
|
+
import type { Scope } from '../scope/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Create a lazy proxy that defers resolution until the first property access.
|
|
6
6
|
*
|
package/dist/lazy/index.js
CHANGED
|
@@ -9,7 +9,7 @@ function lazy(source, token) {
|
|
|
9
9
|
}
|
|
10
10
|
return instance;
|
|
11
11
|
};
|
|
12
|
-
const proxyTarget = Object.create(null);
|
|
12
|
+
const proxyTarget = /* @__PURE__ */ Object.create(null);
|
|
13
13
|
return new Proxy(proxyTarget, {
|
|
14
14
|
defineProperty(_, prop, desc) {
|
|
15
15
|
return Reflect.defineProperty(ensureResolved(), prop, desc);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type AbstractConstructor<T = unknown> = abstract new (...args: never[]) => T;
|
|
2
|
+
/**
|
|
3
|
+
* Resolver passed to factory callbacks.
|
|
4
|
+
*
|
|
5
|
+
* @template T PropertyKey-based type map (defined via interface, order-independent)
|
|
6
|
+
* @template Sync Union of registered sync class constructors (order-dependent)
|
|
7
|
+
* @template Async Union of registered async class constructors (order-dependent)
|
|
8
|
+
*/
|
|
9
|
+
export interface Resolver<T, Sync extends AbstractConstructor = AbstractConstructor, Async extends AbstractConstructor = never> {
|
|
10
|
+
/**
|
|
11
|
+
* Resolve an instance for the given token.
|
|
12
|
+
*
|
|
13
|
+
* @param token A registered token
|
|
14
|
+
* @returns The instance associated with the token
|
|
15
|
+
*/
|
|
16
|
+
resolve<V>(token: AbstractConstructor<V> & Async): Promise<V>;
|
|
17
|
+
resolve<V>(token: AbstractConstructor<V> & Sync): V;
|
|
18
|
+
resolve<K extends keyof T>(token: K): T[K];
|
|
19
|
+
/**
|
|
20
|
+
* Try to resolve an instance for the given token.
|
|
21
|
+
*
|
|
22
|
+
* Returns `undefined` instead of throwing when the token is not registered.
|
|
23
|
+
* Other errors (circular dependency, disposed container) are still thrown.
|
|
24
|
+
*
|
|
25
|
+
* @param token A token to resolve
|
|
26
|
+
* @returns The instance associated with the token, or `undefined` if not registered
|
|
27
|
+
*/
|
|
28
|
+
tryResolve<V>(token: AbstractConstructor<V> & Async): Promise<V> | undefined;
|
|
29
|
+
tryResolve<V>(token: AbstractConstructor<V> & Sync): V | undefined;
|
|
30
|
+
tryResolve<K extends keyof T>(token: K): T[K] | undefined;
|
|
31
|
+
tryResolve<V>(token: AbstractConstructor<V>): V | Promise<V> | undefined;
|
|
32
|
+
tryResolve(token: PropertyKey): unknown;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve all instances for the given token.
|
|
35
|
+
*
|
|
36
|
+
* Returns an array of instances from all registrations for the token.
|
|
37
|
+
*
|
|
38
|
+
* @param token A registered token
|
|
39
|
+
* @returns An array of instances associated with the token
|
|
40
|
+
*/
|
|
41
|
+
resolveAll<V>(token: AbstractConstructor<V> & Async): Promise<V>[];
|
|
42
|
+
resolveAll<V>(token: AbstractConstructor<V> & Sync): V[];
|
|
43
|
+
resolveAll<K extends keyof T>(token: K): T[K][];
|
|
44
|
+
/**
|
|
45
|
+
* Try to resolve all instances for the given token.
|
|
46
|
+
*
|
|
47
|
+
* Returns `undefined` instead of throwing when the token is not registered.
|
|
48
|
+
* Other errors (circular dependency, disposed container) are still thrown.
|
|
49
|
+
*
|
|
50
|
+
* @param token A token to resolve
|
|
51
|
+
* @returns An array of instances associated with the token, or `undefined` if not registered
|
|
52
|
+
*/
|
|
53
|
+
tryResolveAll<V>(token: AbstractConstructor<V> & Async): Promise<V>[] | undefined;
|
|
54
|
+
tryResolveAll<V>(token: AbstractConstructor<V> & Sync): V[] | undefined;
|
|
55
|
+
tryResolveAll<K extends keyof T>(token: K): T[K][] | undefined;
|
|
56
|
+
tryResolveAll<V>(token: AbstractConstructor<V>): (V | Promise<V>)[] | undefined;
|
|
57
|
+
tryResolveAll(token: PropertyKey): unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Lifetime of a registration.
|
|
61
|
+
*/
|
|
62
|
+
export type Lifetime = 'singleton' | 'transient' | 'scoped';
|
|
63
|
+
/**
|
|
64
|
+
* Factory registration entry.
|
|
65
|
+
*/
|
|
66
|
+
export interface Registration {
|
|
67
|
+
/**
|
|
68
|
+
* Factory function.
|
|
69
|
+
*
|
|
70
|
+
* @param resolver Resolver
|
|
71
|
+
* @returns Instance
|
|
72
|
+
*/
|
|
73
|
+
readonly factory: (resolver: Resolver<never, never>) => unknown;
|
|
74
|
+
/**
|
|
75
|
+
* Lifetime of the registration.
|
|
76
|
+
*/
|
|
77
|
+
readonly lifetime: Lifetime;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Convert a token to a human-readable string.
|
|
81
|
+
*/
|
|
82
|
+
export declare function tokenToString(token: unknown): string;
|
|
83
|
+
/**
|
|
84
|
+
* Build a human-readable circular dependency path from the resolving tokens.
|
|
85
|
+
*
|
|
86
|
+
* Uses the insertion order of Set to extract only the cycle portion.
|
|
87
|
+
* e.g. if resolvingTokens is [X, A, B, C] and token is A, returns "A -> B -> C -> A"
|
|
88
|
+
*
|
|
89
|
+
* @param resolvingTokens The set of tokens currently being resolved
|
|
90
|
+
* @param token The token that caused the circular dependency
|
|
91
|
+
* @returns Formatted cycle path string
|
|
92
|
+
*/
|
|
93
|
+
export declare function buildCircularPath(resolvingTokens: Set<unknown>, token: unknown): string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Container } from '../container/index.cjs';
|
|
2
|
+
import type { DisposableContainer, DisposableScope } from '../disposable/index.cjs';
|
|
3
|
+
import { type ContainerInternals, INTERNALS } from '../internal.cjs';
|
|
4
|
+
import type { AbstractConstructor, Registration } from '../resolver/index.cjs';
|
|
5
|
+
/**
|
|
6
|
+
* Create a new scope (child container) from a Container, Scope, or their disposable variants.
|
|
7
|
+
*
|
|
8
|
+
* The scope inherits all registrations from the source.
|
|
9
|
+
* Singleton instances are shared with the parent, while scoped instances are local to the scope.
|
|
10
|
+
*
|
|
11
|
+
* @param source A Container, Scope, DisposableContainer, or DisposableScope to create a child scope from
|
|
12
|
+
* @returns A new Scope instance
|
|
13
|
+
* @throws ContainerError if the source has been disposed
|
|
14
|
+
*/
|
|
15
|
+
export declare function createScope<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(source: Container<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
16
|
+
export declare function createScope<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(source: Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
17
|
+
export declare function createScope<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(source: DisposableContainer<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
18
|
+
export declare function createScope<T, Sync extends AbstractConstructor, Async extends AbstractConstructor, ScopedT, ScopedSync extends AbstractConstructor, ScopedAsync extends AbstractConstructor>(source: DisposableScope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>): Scope<T, Sync, Async, ScopedT, ScopedSync, ScopedAsync>;
|
|
19
|
+
/**
|
|
20
|
+
* Scoped child container.
|
|
21
|
+
*
|
|
22
|
+
* Inherits all registrations from the parent container.
|
|
23
|
+
* Singleton instances are shared with the parent, while scoped instances are local to this scope.
|
|
24
|
+
* Transient instances are always newly created.
|
|
25
|
+
*
|
|
26
|
+
* @template T PropertyKey-based token type map
|
|
27
|
+
* @template Sync Union of registered sync class constructors
|
|
28
|
+
* @template Async Union of registered async class constructors
|
|
29
|
+
* @template ScopedT PropertyKey-based token type map for scoped registrations
|
|
30
|
+
* @template ScopedSync Union of scoped sync class constructors
|
|
31
|
+
* @template ScopedAsync Union of scoped async class constructors
|
|
32
|
+
*/
|
|
33
|
+
export declare class Scope<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 readonly scopedCache;
|
|
37
|
+
private readonly resolvingTokens;
|
|
38
|
+
private singletonDepth;
|
|
39
|
+
private disposed;
|
|
40
|
+
/**
|
|
41
|
+
* Internal state accessor for extension modules (scope, disposable).
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
readonly [INTERNALS]: ContainerInternals;
|
|
46
|
+
constructor(registrations: Map<unknown, Registration[]>, singletonCache: Map<Registration, unknown>);
|
|
47
|
+
/**
|
|
48
|
+
* Resolve an instance for the given token.
|
|
49
|
+
*
|
|
50
|
+
* - Singleton: Returns the shared instance from the parent container (creates and caches on first access).
|
|
51
|
+
* - Scoped: Returns an instance local to this scope (creates and caches on first access within the scope).
|
|
52
|
+
* - Transient: Creates a new instance on every call.
|
|
53
|
+
*
|
|
54
|
+
* @param token A registered token
|
|
55
|
+
* @returns The instance associated with the token
|
|
56
|
+
* @throws ContainerError if the token is not registered
|
|
57
|
+
*/
|
|
58
|
+
resolve<V>(token: AbstractConstructor<V> & (Async | ScopedAsync)): Promise<V>;
|
|
59
|
+
resolve<V>(token: AbstractConstructor<V> & (Sync | ScopedSync)): V;
|
|
60
|
+
resolve<K extends keyof (T & ScopedT)>(token: K): (T & ScopedT)[K];
|
|
61
|
+
/**
|
|
62
|
+
* Try to resolve an instance for the given token.
|
|
63
|
+
*
|
|
64
|
+
* Returns `undefined` instead of throwing when the token is not registered.
|
|
65
|
+
* Other errors (circular dependency, disposed scope) are still thrown.
|
|
66
|
+
*
|
|
67
|
+
* @param token A token to resolve
|
|
68
|
+
* @returns The instance associated with the token, or `undefined` if not registered
|
|
69
|
+
*/
|
|
70
|
+
tryResolve<V>(token: AbstractConstructor<V> & (Async | ScopedAsync)): Promise<V> | undefined;
|
|
71
|
+
tryResolve<V>(token: AbstractConstructor<V> & (Sync | ScopedSync)): V | undefined;
|
|
72
|
+
tryResolve<K extends keyof (T & ScopedT)>(token: K): (T & ScopedT)[K] | undefined;
|
|
73
|
+
tryResolve<V>(token: AbstractConstructor<V>): V | Promise<V> | undefined;
|
|
74
|
+
tryResolve(token: PropertyKey): unknown;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve all instances for the given token.
|
|
77
|
+
*
|
|
78
|
+
* Returns an array of instances from all registered factories for the token,
|
|
79
|
+
* in registration order.
|
|
80
|
+
*
|
|
81
|
+
* @param token A registered token
|
|
82
|
+
* @returns An array of instances associated with the token
|
|
83
|
+
* @throws ContainerError if the token is not registered
|
|
84
|
+
*/
|
|
85
|
+
resolveAll<V>(token: AbstractConstructor<V> & (Async | ScopedAsync)): Promise<V>[];
|
|
86
|
+
resolveAll<V>(token: AbstractConstructor<V> & (Sync | ScopedSync)): V[];
|
|
87
|
+
resolveAll<K extends keyof (T & ScopedT)>(token: K): (T & ScopedT)[K][];
|
|
88
|
+
/**
|
|
89
|
+
* Try to resolve all instances for the given token.
|
|
90
|
+
*
|
|
91
|
+
* Returns `undefined` instead of throwing when the token is not registered.
|
|
92
|
+
* Other errors (circular dependency, disposed scope) are still thrown.
|
|
93
|
+
*
|
|
94
|
+
* @param token A token to resolve
|
|
95
|
+
* @returns An array of instances associated with the token, or `undefined` if not registered
|
|
96
|
+
*/
|
|
97
|
+
tryResolveAll<V>(token: AbstractConstructor<V> & (Async | ScopedAsync)): Promise<V>[] | undefined;
|
|
98
|
+
tryResolveAll<V>(token: AbstractConstructor<V> & (Sync | ScopedSync)): V[] | undefined;
|
|
99
|
+
tryResolveAll<K extends keyof (T & ScopedT)>(token: K): (T & ScopedT)[K][] | undefined;
|
|
100
|
+
tryResolveAll<V>(token: AbstractConstructor<V>): (V | Promise<V>)[] | undefined;
|
|
101
|
+
tryResolveAll(token: PropertyKey): unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Internal resolution logic shared by resolve and tryResolve.
|
|
104
|
+
* Resolves the last registered factory for the token.
|
|
105
|
+
*
|
|
106
|
+
* @param token Token to resolve
|
|
107
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
108
|
+
* @returns The resolved instance, or undefined if not registered and required is false
|
|
109
|
+
*/
|
|
110
|
+
private resolveToken;
|
|
111
|
+
/**
|
|
112
|
+
* Internal resolution logic shared by resolveAll and tryResolveAll.
|
|
113
|
+
* Resolves all registered factories for the token.
|
|
114
|
+
*
|
|
115
|
+
* @param token Token to resolve
|
|
116
|
+
* @param required If true, throws when the token is not registered. If false, returns undefined.
|
|
117
|
+
* @returns An array of resolved instances, or undefined if not registered and required is false
|
|
118
|
+
*/
|
|
119
|
+
private resolveAllTokens;
|
|
120
|
+
}
|
package/dist/scope/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Container } from '../container';
|
|
2
|
-
import type { DisposableContainer, DisposableScope } from '../disposable';
|
|
3
|
-
import { type ContainerInternals, INTERNALS } from '../internal';
|
|
4
|
-
import type { AbstractConstructor, Registration } from '../resolver';
|
|
1
|
+
import type { Container } from '../container/index.js';
|
|
2
|
+
import type { DisposableContainer, DisposableScope } from '../disposable/index.js';
|
|
3
|
+
import { type ContainerInternals, INTERNALS } from '../internal.js';
|
|
4
|
+
import type { AbstractConstructor, Registration } from '../resolver/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Create a new scope (child container) from a Container, Scope, or their disposable variants.
|
|
7
7
|
*
|