@tiberjs/di 0.1.0

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 ADDED
@@ -0,0 +1,123 @@
1
+ # @tiberjs/di
2
+
3
+ A hierarchical dependency container. It builds objects on demand, caches each one, and disposes what it built when the container closes.
4
+
5
+ Requires **Node.js 24+**.
6
+
7
+ ```sh
8
+ pnpm add @tiberjs/di @tiberjs/runner
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { Container, inject, onDispose, token } from "@tiberjs/di";
15
+
16
+ const Config = token<{ url: string }>("config");
17
+
18
+ class Db {
19
+ readonly config = inject(Config);
20
+
21
+ constructor() {
22
+ onDispose(() => this.close());
23
+ }
24
+
25
+ close(): void {}
26
+ }
27
+
28
+ await using container = new Container();
29
+ container.provide(Config, () => ({ url: "postgres://localhost" }));
30
+
31
+ const db = container.resolve(Db);
32
+ ```
33
+
34
+ `Db` never mentions the container: `inject()` reads whichever container is constructing the object, and `onDispose()` registers cleanup with that same one.
35
+
36
+ ## The four things you will do
37
+
38
+ **Register a value or interface.** A class needs no registration — it is its own token.
39
+
40
+ ```ts
41
+ container.provide(Config, () => loadConfig());
42
+ ```
43
+
44
+ **Resolve.** Constructed on first use, cached after that.
45
+
46
+ ```ts
47
+ const db = container.resolve(Db);
48
+ ```
49
+
50
+ **Scope.** A child sees its parent's providers and disposes only what it built itself.
51
+
52
+ ```ts
53
+ const request = container.child();
54
+ request.provide(CurrentUser, () => user);
55
+ ```
56
+
57
+ **Dispose.** `await using`, or `await container[Symbol.asyncDispose]()`. Cleanup runs in reverse construction order.
58
+
59
+ ## Three things that will bite you
60
+
61
+ **A token is built by the nearest container that provides it.** A class nobody provides ends up at the root and is shared. So overriding `Config` in a child does not change a `Db` the root already owns — provide `Db` in the child too:
62
+
63
+ ```ts
64
+ const test = container.child();
65
+ test.provide(Config, () => ({ url: "postgres://test" }));
66
+ test.provide(Db, () => new Db()); // without this, Db stays the root's
67
+ ```
68
+
69
+ **Register before you resolve.** Replacing a provider whose instance this container already handed out raises `ProviderConflictError`, because the cached instance would keep winning. Override in a child instead.
70
+
71
+ **`inject()` needs an ambient container.** It works while an object is being constructed or disposed, inside `withContainer()`, and inside a Runner execution bound to `ContainerKey`. A method called later with none of those raises. Capture what you need during construction, or use `container.resolve()` directly.
72
+
73
+ ## API
74
+
75
+ ### Tokens
76
+
77
+ | | |
78
+ | ----------------------- | -------------------------------------------------------------------------- |
79
+ | `token<T>(description)` | A typed token for a value or interface. Same description, different token. |
80
+ | a class | Its own token, default-constructed when nothing provides it. |
81
+
82
+ ### `Container`
83
+
84
+ | | |
85
+ | ------------------------------- | ------------------------------------------------------------------------------------------ |
86
+ | `provide(token, factory)` | Register a factory; it receives the resolving container. |
87
+ | `resolve(token)` | Cached instance, or construct once and cache. |
88
+ | `use(token, factory, dispose?)` | Acquire an inline resource once per container, with optional explicit cleanup. |
89
+ | `defer(cleanup)` | Register cleanup, including during this container's own teardown. |
90
+ | `child()` | A container that resolves through this one. |
91
+ | `has(token)` | An explicit provider or a cached instance here or above — `false` for an unprovided class. |
92
+ | `resolutionGraph()` | Who resolved what in this tree, for diagnostics. |
93
+ | `disposeSync()` | Close a container that built nothing, without an `await`; `false` if it holds anything. |
94
+
95
+ ### Ambient access
96
+
97
+ Each reads the ambient container, so classes stay free of container plumbing.
98
+
99
+ | | |
100
+ | ----------------------------------- | --------------------------------------------------------------------- |
101
+ | `inject(token)` | Resolve. |
102
+ | `scoped(token, factory, dispose?)` | Acquire an inline resource. |
103
+ | `onDispose(cleanup)` | Register cleanup. |
104
+ | `currentContainer()` | The ambient container; raises when there is none. |
105
+ | `withContainer(container, handler)` | Run `handler` with `container` ambient. |
106
+ | `ContainerKey` | Runner context key, for binding a container to an execution yourself. |
107
+
108
+ ## Cleanup
109
+
110
+ For each constructed value, the first of: the `dispose` you passed, `Symbol.asyncDispose`/`Symbol.dispose`, `onClose()`. A value with none is just cached.
111
+
112
+ All cleanups run even if some fail; one failure is rethrown as-is, several arrive as an `AggregateError`.
113
+
114
+ ## Errors
115
+
116
+ | | |
117
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------ |
118
+ | `ResolutionError` | No provider, or a cycle. Carries `reason` and `token`. |
119
+ | `ProviderConflictError` | `provide()` after that token was resolved here. Carries `token`. |
120
+ | `ContainerClosedError` | Used while `closing` or after `disposed`. Carries `state`. |
121
+ | `DisposalConflictError` | Two cleanup protocols on one value, or an explicit disposer for a value someone else owns. Carries `reason`. |
122
+
123
+ Errors from your own factories propagate unchanged.
@@ -0,0 +1,22 @@
1
+ import { type ContextKey } from "@tiberjs/runner";
2
+ import type { Container } from "./container.js";
3
+ import type { Factory, InjectionToken } from "./tokens.js";
4
+ /** The execution-context binding that carries a container across executions. */
5
+ export declare const ContainerKey: ContextKey<Container>;
6
+ /** Construction container first, otherwise the current execution's binding. */
7
+ export declare function currentContainer(): Container;
8
+ /** Resolve a dependency during construction or inside a bound execution. */
9
+ export declare function inject<T>(token: InjectionToken<T>): T;
10
+ /** Acquire a resource once per container and release it when that container closes. */
11
+ export declare function scoped<T>(token: InjectionToken<T>, factory: Factory<T>, dispose?: (value: T) => unknown | Promise<unknown>): T;
12
+ /** Register LIFO cleanup in the ambient container. */
13
+ export declare function onDispose(cleanup: () => unknown | Promise<unknown>): void;
14
+ /**
15
+ * Bind `container` as the ambient container for `handler`.
16
+ *
17
+ * The binding is always installed locally, so nested calls override an outer
18
+ * construction container. With an active execution it is additionally published
19
+ * on the context so derived executions observe the same container.
20
+ */
21
+ export declare function withContainer<T>(container: Container, handler: () => T): T;
22
+ //# sourceMappingURL=ambient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient.d.ts","sourceRoot":"","sources":["../src/ambient.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3D,gFAAgF;AAChF,eAAO,MAAM,YAAY,EAAE,UAAU,CAAC,SAAS,CAAyC,CAAC;AAEzF,+EAA+E;AAC/E,wBAAgB,gBAAgB,IAAI,SAAS,CAgB5C;AAED,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAErD;AAED,uFAAuF;AACvF,wBAAgB,MAAM,CAAC,CAAC,EACtB,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACjD,CAAC,CAEH;AAED,sDAAsD;AACtD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAEzE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ1E"}
@@ -0,0 +1,38 @@
1
+ import { type ResolutionGraph } from "./resolution/graph.js";
2
+ import type { Factory, InjectionToken } from "./tokens.js";
3
+ /**
4
+ * A hierarchical dependency container and resource owner. A child resolves its
5
+ * ancestors' providers but owns and disposes only what it constructed itself.
6
+ */
7
+ export declare class Container {
8
+ #private;
9
+ constructor(parent?: Container);
10
+ /** A child container resolves application singletons through its parent. */
11
+ child(): Container;
12
+ /** Includes failed attempts and active children, but never disposed containers. */
13
+ resolutionGraph(): ResolutionGraph;
14
+ /** Register a provider before the token is resolved here. */
15
+ provide<T>(token: InjectionToken<T>, factory: Factory<T>): void;
16
+ has(token: InjectionToken<unknown>): boolean;
17
+ /** Resolve local cache/provider, then ancestors; default classes live at root. */
18
+ resolve<T>(token: InjectionToken<T>): T;
19
+ /**
20
+ * Acquire inline resources once per container, with explicit or automatic
21
+ * disposal. Like a provider factory, `factory` receives this container.
22
+ */
23
+ use<T>(token: InjectionToken<T>, factory: Factory<T>, dispose?: (value: T) => unknown | Promise<unknown>): T;
24
+ /**
25
+ * Register LIFO cleanup. Blind to the phase on purpose: a resource released
26
+ * mid-drain may still register its own cleanup, and only the queue knows
27
+ * whether anything is left to run it.
28
+ */
29
+ defer(cleanup: () => unknown | Promise<unknown>): void;
30
+ /**
31
+ * Close a container that never constructed or deferred anything, avoiding an
32
+ * `await`. Returns `false` and changes nothing otherwise.
33
+ */
34
+ disposeSync(): boolean;
35
+ /** Close acquisition synchronously, then clear resolution storage after teardown. */
36
+ [Symbol.asyncDispose](): Promise<void>;
37
+ }
38
+ //# sourceMappingURL=container.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAKhF,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI3D;;;GAGG;AACH,qBAAa,SAAS;;gBAWR,MAAM,CAAC,EAAE,SAAS;IAwB9B,4EAA4E;IAC5E,KAAK,IAAI,SAAS;IAKlB,mFAAmF;IACnF,eAAe,IAAI,eAAe;IAIlC,6DAA6D;IAC7D,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAK/D,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO;IAI5C,kFAAkF;IAClF,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC;IA6BvC;;;OAGG;IACH,GAAG,CAAC,CAAC,EACH,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACjD,CAAC;IAaJ;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI;IAItD;;;OAGG;IACH,WAAW,IAAI,OAAO;IAUtB,qFAAqF;IACrF,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAuDvC"}
@@ -0,0 +1,23 @@
1
+ import { type InjectionToken } from "./tokens.js";
2
+ /** A container resolution failure; provider exceptions propagate unchanged. */
3
+ export declare class ResolutionError extends Error {
4
+ readonly reason: "missing-provider" | "circular-dependency";
5
+ readonly token: InjectionToken<unknown>;
6
+ constructor(reason: "missing-provider" | "circular-dependency", token: InjectionToken<unknown>, options?: ErrorOptions);
7
+ }
8
+ /** Resource admission failed because this container's teardown has begun. */
9
+ export declare class ContainerClosedError extends Error {
10
+ readonly state: "closing" | "disposed";
11
+ constructor(state: "closing" | "disposed", options?: ErrorOptions);
12
+ }
13
+ /** A provider cannot replace an instance this container has already handed out. */
14
+ export declare class ProviderConflictError extends Error {
15
+ readonly token: InjectionToken<unknown>;
16
+ constructor(token: InjectionToken<unknown>);
17
+ }
18
+ /** An object must have exactly one disposal owner and one automatic close protocol. */
19
+ export declare class DisposalConflictError extends Error {
20
+ readonly reason: "multiple-hooks" | "already-owned";
21
+ constructor(reason: "multiple-hooks" | "already-owned");
22
+ }
23
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEjE,+EAA+E;AAC/E,qBAAa,eAAgB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,qBAAqB;IAC3D,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC;gBAD9B,MAAM,EAAE,kBAAkB,GAAG,qBAAqB,EAClD,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EACvC,OAAO,CAAC,EAAE,YAAY;CAUzB;AAED,6EAA6E;AAC7E,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU;gBAA7B,KAAK,EAAE,SAAS,GAAG,UAAU,EACtC,OAAO,CAAC,EAAE,YAAY;CAKzB;AAED,mFAAmF;AACnF,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC;gBAA9B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC;CAMpD;AAED,uFAAuF;AACvF,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe;gBAA1C,MAAM,EAAE,gBAAgB,GAAG,eAAe;CAQhE"}
@@ -0,0 +1,9 @@
1
+ /** Hierarchical dependency resolution and resource ownership. */
2
+ export { ContainerKey, currentContainer, inject, onDispose, scoped, withContainer, } from "./ambient.js";
3
+ export type { ContainerObject } from "./resources/cleanup.js";
4
+ export { Container } from "./container.js";
5
+ export { ContainerClosedError, DisposalConflictError, ProviderConflictError, ResolutionError, } from "./errors.js";
6
+ export type { ResolutionGraph } from "./resolution/graph.js";
7
+ export { token } from "./tokens.js";
8
+ export type { Constructor, Factory, InjectionToken, Token } from "./tokens.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,MAAM,EACN,SAAS,EACT,MAAM,EACN,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,634 @@
1
+ import { combinedError, contextKey, peekState, provide, withContext } from "@tiberjs/runner";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ // The require scope
4
+ var __webpack_require__ = {};
5
+
6
+ // webpack/runtime/define_property_getters
7
+ (() => {
8
+ __webpack_require__.d = (exports, getters, values) => {
9
+ var define = (defs, kind) => {
10
+ for(var key in defs) {
11
+ if(__webpack_require__.o(defs, key) && !__webpack_require__.o(exports, key)) {
12
+ Object.defineProperty(exports, key, { enumerable: true, [kind]: defs[key] });
13
+ }
14
+ }
15
+ };
16
+ define(getters, "get");
17
+ define(values, "value");
18
+ };
19
+ })();
20
+ // webpack/runtime/has_own_property
21
+ (() => {
22
+ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
23
+ })();
24
+ var __webpack_exports__ = {};
25
+
26
+ // EXPORTS
27
+ __webpack_require__.d(__webpack_exports__, {
28
+ mc: () => (/* reexport */ Container),
29
+ a$: () => (/* reexport */ ContainerClosedError),
30
+ jc: () => (/* reexport */ ContainerKey),
31
+ uP: () => (/* reexport */ DisposalConflictError),
32
+ kb: () => (/* reexport */ ProviderConflictError),
33
+ fe: () => (/* reexport */ ResolutionError),
34
+ zt: () => (/* reexport */ currentContainer),
35
+ WQ: () => (/* reexport */ inject),
36
+ zp: () => (/* reexport */ onDispose),
37
+ P1: () => (/* reexport */ scoped),
38
+ Sh: () => (/* reexport */ tokens_token),
39
+ Ub: () => (/* reexport */ withContainer)
40
+ });
41
+
42
+ ;// CONCATENATED MODULE: external "@tiberjs/runner"
43
+
44
+ ;// CONCATENATED MODULE: external "node:async_hooks"
45
+
46
+ ;// CONCATENATED MODULE: ./src/resources/active-container.ts
47
+
48
+ /**
49
+ * The container bound as ambient while one of its resources is constructed or
50
+ * torn down. Nested construction overrides it for the inner call only.
51
+ */ const activeContainer = new AsyncLocalStorage();
52
+
53
+ ;// CONCATENATED MODULE: ./src/ambient.ts
54
+
55
+
56
+ /** The execution-context binding that carries a container across executions. */ const ContainerKey = contextKey("di.container");
57
+ /** Construction container first, otherwise the current execution's binding. */ function currentContainer() {
58
+ const constructing = activeContainer.getStore();
59
+ if (constructing) {
60
+ return constructing;
61
+ }
62
+ // One state read and one frame walk: inject() runs on request paths, and a
63
+ // bound container is never undefined, so absence needs no separate probe.
64
+ const bound = peekState()?.context.values.get(ContainerKey.id);
65
+ if (!bound) {
66
+ throw new Error("No active container. This API requires construction, disposal, withContainer(), or an execution bound to ContainerKey.");
67
+ }
68
+ return bound;
69
+ }
70
+ /** Resolve a dependency during construction or inside a bound execution. */ function inject(token) {
71
+ return currentContainer().resolve(token);
72
+ }
73
+ /** Acquire a resource once per container and release it when that container closes. */ function scoped(token, factory, dispose) {
74
+ return currentContainer().use(token, factory, dispose);
75
+ }
76
+ /** Register LIFO cleanup in the ambient container. */ function onDispose(cleanup) {
77
+ currentContainer().defer(cleanup);
78
+ }
79
+ /**
80
+ * Bind `container` as the ambient container for `handler`.
81
+ *
82
+ * The binding is always installed locally, so nested calls override an outer
83
+ * construction container. With an active execution it is additionally published
84
+ * on the context so derived executions observe the same container.
85
+ */ function withContainer(container, handler) {
86
+ if (peekState()) {
87
+ return withContext([
88
+ provide(ContainerKey, container)
89
+ ], ()=>activeContainer.run(container, handler));
90
+ }
91
+ return activeContainer.run(container, handler);
92
+ }
93
+
94
+ ;// CONCATENATED MODULE: ./src/tokens.ts
95
+ function tokens_token(description) {
96
+ return {
97
+ key: Symbol(description)
98
+ };
99
+ }
100
+ function describeToken(token) {
101
+ if (typeof token === "function") {
102
+ return token.name || "anonymous class";
103
+ }
104
+ return token.key.description ?? "token";
105
+ }
106
+
107
+ ;// CONCATENATED MODULE: ./src/errors.ts
108
+
109
+ /** A container resolution failure; provider exceptions propagate unchanged. */ class ResolutionError extends Error {
110
+ reason;
111
+ token;
112
+ constructor(reason, token, options){
113
+ super(reason === "missing-provider" ? `No provider registered for token "${describeToken(token)}". Use provide(token, factory) for values/interfaces.` : `Circular dependency while resolving "${describeToken(token)}".`, options), this.reason = reason, this.token = token;
114
+ this.name = "ResolutionError";
115
+ }
116
+ }
117
+ /** Resource admission failed because this container's teardown has begun. */ class ContainerClosedError extends Error {
118
+ state;
119
+ constructor(state, options){
120
+ super(state === "closing" ? "Container is closing." : "Container has been disposed.", options), this.state = state;
121
+ this.name = "ContainerClosedError";
122
+ }
123
+ }
124
+ /** A provider cannot replace an instance this container has already handed out. */ class ProviderConflictError extends Error {
125
+ token;
126
+ constructor(token){
127
+ super(`"${describeToken(token)}" is already resolved in this container. Register providers before resolving, or override the token in a child container.`), this.token = token;
128
+ this.name = "ProviderConflictError";
129
+ }
130
+ }
131
+ /** An object must have exactly one disposal owner and one automatic close protocol. */ class DisposalConflictError extends Error {
132
+ reason;
133
+ constructor(reason){
134
+ super(reason === "multiple-hooks" ? "ContainerObject.onClose cannot coexist with Symbol.asyncDispose or Symbol.dispose." : "An explicit disposer cannot take ownership of an already-owned resource."), this.reason = reason;
135
+ this.name = "DisposalConflictError";
136
+ }
137
+ }
138
+
139
+ ;// CONCATENATED MODULE: ./src/resolution/graph.ts
140
+
141
+ /**
142
+ * Root-local resolution diagnostics. Node identity is per container, and both
143
+ * edge directions are indexed so removing a disposed container costs its own
144
+ * nodes rather than a full scan.
145
+ */ class ResolutionTracker {
146
+ #nextId = 0;
147
+ #idsByOwner = new WeakMap();
148
+ #nodes = new Map();
149
+ #outgoing = new Map();
150
+ #incoming = new Map();
151
+ /** Records a resolution as a dependency of the construction that requested it. */ record(owner, token, parent) {
152
+ let ids = this.#idsByOwner.get(owner);
153
+ if (!ids) {
154
+ this.#idsByOwner.set(owner, ids = new Map());
155
+ }
156
+ let id = ids.get(token);
157
+ if (id === undefined) {
158
+ id = this.#nextId++;
159
+ ids.set(token, id);
160
+ this.#nodes.set(id, describeToken(token));
161
+ }
162
+ // A node never depends on itself, and a removed owner's frame links nothing.
163
+ if (parent && (parent.owner !== owner || parent.token !== token)) {
164
+ const from = this.#idsByOwner.get(parent.owner)?.get(parent.token);
165
+ if (from !== undefined) {
166
+ this.#link(from, id);
167
+ }
168
+ }
169
+ }
170
+ snapshot() {
171
+ const edges = [];
172
+ for (const [from, targets] of this.#outgoing){
173
+ for (const to of targets){
174
+ edges.push({
175
+ from,
176
+ to
177
+ });
178
+ }
179
+ }
180
+ return {
181
+ nodes: Array.from(this.#nodes, ([id, name])=>({
182
+ id,
183
+ name
184
+ })),
185
+ edges
186
+ };
187
+ }
188
+ /** Drops a disposed owner's nodes and every edge that touched them. */ remove(owner) {
189
+ const ids = this.#idsByOwner.get(owner);
190
+ if (!ids) {
191
+ return;
192
+ }
193
+ for (const id of ids.values()){
194
+ this.#nodes.delete(id);
195
+ this.#unlink(id);
196
+ }
197
+ this.#idsByOwner.delete(owner);
198
+ }
199
+ #link(from, to) {
200
+ let targets = this.#outgoing.get(from);
201
+ if (!targets) {
202
+ this.#outgoing.set(from, targets = new Set());
203
+ }
204
+ targets.add(to);
205
+ let sources = this.#incoming.get(to);
206
+ if (!sources) {
207
+ this.#incoming.set(to, sources = new Set());
208
+ }
209
+ sources.add(from);
210
+ }
211
+ #unlink(id) {
212
+ const targets = this.#outgoing.get(id);
213
+ if (targets) {
214
+ for (const to of targets){
215
+ this.#detach(this.#incoming, to, id);
216
+ }
217
+ this.#outgoing.delete(id);
218
+ }
219
+ const sources = this.#incoming.get(id);
220
+ if (sources) {
221
+ for (const from of sources){
222
+ this.#detach(this.#outgoing, from, id);
223
+ }
224
+ this.#incoming.delete(id);
225
+ }
226
+ }
227
+ #detach(index, node, peer) {
228
+ const peers = index.get(node);
229
+ if (peers?.delete(peer) && peers.size === 0) {
230
+ index.delete(node);
231
+ }
232
+ }
233
+ }
234
+
235
+ ;// CONCATENATED MODULE: ./src/resolution/path.ts
236
+
237
+ /**
238
+ * The chain of constructions in flight under one container tree, newest last.
239
+ *
240
+ * A frame is a container and a token together, so the same token may be in
241
+ * flight in two containers while a child decorates an ancestor's
242
+ * implementation; only a container re-entering its own token is a cycle.
243
+ */ class ResolutionPath {
244
+ #frames = [];
245
+ /** The construction that whatever resolves next belongs to. */ get current() {
246
+ return this.#frames[this.#frames.length - 1];
247
+ }
248
+ /** Rejects a container that re-enters a token it is already constructing. */ enter(owner, token) {
249
+ for (const frame of this.#frames){
250
+ if (frame.owner === owner && frame.token === token) {
251
+ throw new ResolutionError("circular-dependency", token);
252
+ }
253
+ }
254
+ this.#frames.push({
255
+ owner,
256
+ token
257
+ });
258
+ }
259
+ /** A failed attempt leaves no trace, so the token stays resolvable. */ exit() {
260
+ this.#frames.pop();
261
+ }
262
+ }
263
+
264
+ ;// CONCATENATED MODULE: ./src/resolution/providers.ts
265
+
266
+ /** One container's providers and the instances it has constructed. */ class ProviderRegistry {
267
+ #factories;
268
+ #instances;
269
+ /** Whether this container has ever cached an instance of its own. */ get hasInstances() {
270
+ return this.#instances !== undefined;
271
+ }
272
+ /** An explicit provider or a cached instance, never a constructibility probe. */ has(token) {
273
+ return (this.#instances?.has(token) ?? false) || (this.#factories?.has(token) ?? false);
274
+ }
275
+ /** Distinguishes a cached `undefined` from a missing instance. */ hasInstance(token) {
276
+ return this.#instances?.has(token) ?? false;
277
+ }
278
+ instance(token) {
279
+ return this.#instances?.get(token);
280
+ }
281
+ hasFactory(token) {
282
+ return this.#factories?.has(token) ?? false;
283
+ }
284
+ factory(token) {
285
+ return this.#factories?.get(token);
286
+ }
287
+ /**
288
+ * Replacing a factory whose instance this container already handed out is
289
+ * rejected: the cached instance would silently win. Override in a child.
290
+ */ provide(token, factory) {
291
+ if (this.#instances?.has(token)) {
292
+ throw new ProviderConflictError(token);
293
+ }
294
+ (this.#factories ??= new Map()).set(token, factory);
295
+ }
296
+ cache(token, value) {
297
+ (this.#instances ??= new Map()).set(token, value);
298
+ }
299
+ /** Disposal makes the container unusable, so its storage is released. */ clear() {
300
+ this.#factories = undefined;
301
+ this.#instances = undefined;
302
+ }
303
+ }
304
+
305
+ ;// CONCATENATED MODULE: ./src/resources/cleanup.ts
306
+
307
+ /**
308
+ * Precedence is explicit disposer, then `Symbol.asyncDispose`/`Symbol.dispose`,
309
+ * then `ContainerObject.onClose`. `onClose` beside a symbol disposer is
310
+ * ambiguous and refused, unless an explicit disposer overrides both shapes.
311
+ */ function planCleanup(value, explicitDispose) {
312
+ const asyncDispose = value[Symbol.asyncDispose];
313
+ const dispose = value[Symbol.dispose];
314
+ const onClose = value.onClose;
315
+ const symbolDispose = typeof asyncDispose === "function" ? asyncDispose : dispose;
316
+ const cleanup = explicitDispose ? ()=>explicitDispose(value) : typeof symbolDispose === "function" ? ()=>symbolDispose.call(value) : typeof onClose === "function" ? ()=>onClose.call(value) : undefined;
317
+ const conflict = !explicitDispose && typeof onClose === "function" && typeof symbolDispose === "function" ? new DisposalConflictError("multiple-hooks") : undefined;
318
+ return {
319
+ cleanup,
320
+ conflict
321
+ };
322
+ }
323
+
324
+ ;// CONCATENATED MODULE: ./src/resources/queue.ts
325
+
326
+
327
+ /**
328
+ * LIFO cleanup storage for one owner, drained once. Knows nothing about
329
+ * containers: every callback runs through the invoker its owner supplied.
330
+ */ class DisposalQueue {
331
+ invoke;
332
+ #cleanups;
333
+ #drained = false;
334
+ constructor(invoke){
335
+ this.invoke = invoke;
336
+ }
337
+ /** Cleanup registered while draining is drained too; after that nothing would run it. */ defer(cleanup) {
338
+ if (this.#drained) {
339
+ throw new ContainerClosedError("disposed");
340
+ }
341
+ (this.#cleanups ??= []).push(cleanup);
342
+ }
343
+ /** Drains in reverse registration order, retaining independent failures. */ async close() {
344
+ // Yield past a synchronous factory that initiated disposal before returning its resource.
345
+ await Promise.resolve();
346
+ const errors = [];
347
+ while(this.#cleanups?.length){
348
+ try {
349
+ await this.invoke(this.#cleanups.pop());
350
+ } catch (error) {
351
+ errors.push(error);
352
+ }
353
+ }
354
+ this.#drained = true;
355
+ this.#cleanups = undefined;
356
+ // Independent failures keep their identity, in drain order.
357
+ if (errors.length) {
358
+ throw combinedError(errors, "Errors during disposal.");
359
+ }
360
+ }
361
+ }
362
+
363
+ ;// CONCATENATED MODULE: ./src/resources/owner.ts
364
+
365
+
366
+
367
+
368
+ /**
369
+ * The resources one container owns: it binds the ambient container around
370
+ * construction and teardown, claims each constructed value at most once, and
371
+ * releases what it claimed in LIFO order.
372
+ */ class ResourceOwner {
373
+ container;
374
+ ownership;
375
+ #queue;
376
+ constructor(container, ownership){
377
+ this.container = container;
378
+ this.ownership = ownership;
379
+ this.#queue = new DisposalQueue((cleanup)=>activeContainer.run(container, cleanup));
380
+ }
381
+ /** Only the queue knows whether it already drained, so admission is its call. */ defer(cleanup) {
382
+ this.#queue.defer(cleanup);
383
+ }
384
+ /** Construct with ambient resolution bound, then take disposal ownership. */ construct(factory, dispose) {
385
+ const value = activeContainer.run(this.container, factory, this.container);
386
+ this.#adopt(value, dispose);
387
+ return value;
388
+ }
389
+ /** Once drained, this owner is responsible for nothing it claimed. */ close() {
390
+ return this.#queue.close().finally(()=>this.ownership.release(this));
391
+ }
392
+ #adopt(value, explicitDispose) {
393
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
394
+ if (explicitDispose) {
395
+ this.defer(()=>explicitDispose(value));
396
+ }
397
+ return;
398
+ }
399
+ const target = value;
400
+ if (this.ownership.hasLiveOwner(target)) {
401
+ // An alias of a live resource borrows it; a second disposer would double-release.
402
+ if (explicitDispose) {
403
+ throw new DisposalConflictError("already-owned");
404
+ }
405
+ return;
406
+ }
407
+ const plan = planCleanup(value, explicitDispose);
408
+ if (plan.cleanup) {
409
+ this.defer(plan.cleanup);
410
+ this.ownership.claim(target, this);
411
+ }
412
+ // Registered cleanup above survives refusal, because the value is never returned.
413
+ if (plan.conflict) {
414
+ this.ownership.reject(target, plan.conflict);
415
+ throw plan.conflict;
416
+ }
417
+ }
418
+ }
419
+
420
+ ;// CONCATENATED MODULE: ./src/resources/ownership.ts
421
+ /** A permanently refused value, boxed so it is never mistaken for an owner. */ class Rejection {
422
+ error;
423
+ constructor(error){
424
+ this.error = error;
425
+ }
426
+ }
427
+ // Keyed by root so surviving children share ownership without initializing a
428
+ // closed ancestor's resources.
429
+ const registriesByRoot = new WeakMap();
430
+ /** At most one disposal owner per object, shared across one container tree. */ class OwnershipRegistry {
431
+ #entries = new WeakMap();
432
+ #drained = new WeakSet();
433
+ /** Every container descending from `root` claims into the same registry. */ static forRoot(root) {
434
+ let registry = registriesByRoot.get(root);
435
+ if (!registry) {
436
+ registriesByRoot.set(root, registry = new OwnershipRegistry());
437
+ }
438
+ return registry;
439
+ }
440
+ /**
441
+ * Whether someone is still responsible for releasing `value`; an owner that
442
+ * already drained leaves it adoptable again. Rethrows a refused shape's
443
+ * cached rejection so every later alias of that object fails identically.
444
+ */ hasLiveOwner(value) {
445
+ const entry = this.#entries.get(value);
446
+ if (entry instanceof Rejection) {
447
+ throw entry.error;
448
+ }
449
+ return entry !== undefined && !this.#drained.has(entry);
450
+ }
451
+ claim(value, owner) {
452
+ this.#entries.set(value, owner);
453
+ }
454
+ /** `owner` finished its cleanup: every value it claimed is unowned again. */ release(owner) {
455
+ this.#drained.add(owner);
456
+ }
457
+ /** Refusal is permanent: `value` can never gain a disposal owner afterwards. */ reject(value, error) {
458
+ this.#entries.set(value, new Rejection(error));
459
+ }
460
+ }
461
+
462
+ ;// CONCATENATED MODULE: ./src/container.ts
463
+
464
+
465
+
466
+
467
+
468
+
469
+ /**
470
+ * A hierarchical dependency container and resource owner. A child resolves its
471
+ * ancestors' providers but owns and disposes only what it constructed itself.
472
+ */ class Container {
473
+ #parent;
474
+ #root;
475
+ #providers = new ProviderRegistry();
476
+ #path;
477
+ /** Diagnostics live on the root; a child records into its root's tracker. */ #graph;
478
+ #resources;
479
+ #disposal;
480
+ #phase = "open";
481
+ constructor(parent){
482
+ this.#parent = parent;
483
+ this.#root = parent ? parent.#root : this;
484
+ this.#path = parent ? parent.#path : new ResolutionPath();
485
+ }
486
+ get #owner() {
487
+ if (!this.#resources) {
488
+ // A drained container must not build an owner whose queue nothing drains.
489
+ this.#admitRetainedAccess();
490
+ this.#resources = new ResourceOwner(this, OwnershipRegistry.forRoot(this.#root));
491
+ }
492
+ return this.#resources;
493
+ }
494
+ /** Undefined once the root is gone, so a surviving child cannot repopulate it. */ get #tracker() {
495
+ if (this.#root.#phase === "disposed") {
496
+ return undefined;
497
+ }
498
+ return this.#root.#graph ??= new ResolutionTracker();
499
+ }
500
+ /** A child container resolves application singletons through its parent. */ child() {
501
+ this.#admitNewAcquisition();
502
+ return new Container(this);
503
+ }
504
+ /** Includes failed attempts and active children, but never disposed containers. */ resolutionGraph() {
505
+ return this.#root.#graph?.snapshot() ?? {
506
+ nodes: [],
507
+ edges: []
508
+ };
509
+ }
510
+ /** Register a provider before the token is resolved here. */ provide(token, factory) {
511
+ this.#admitNewAcquisition();
512
+ this.#providers.provide(token, factory);
513
+ }
514
+ has(token) {
515
+ return this.#providers.has(token) || (this.#parent?.has(token) ?? false);
516
+ }
517
+ /** Resolve local cache/provider, then ancestors; default classes live at root. */ resolve(token) {
518
+ if (this.#providers.hasInstance(token)) {
519
+ this.#admitRetainedAccess();
520
+ this.#tracker?.record(this, token, this.#path.current);
521
+ return this.#providers.instance(token);
522
+ }
523
+ // Ancestors own their own admission; a closing child may still read singletons.
524
+ if (!this.#providers.hasFactory(token) && this.#parent) {
525
+ this.#admitRetainedAccess();
526
+ return this.#parent.resolve(token);
527
+ }
528
+ this.#admitNewAcquisition();
529
+ return this.#acquire(token, ()=>{
530
+ const factory = this.#providers.factory(token);
531
+ if (factory) {
532
+ return factory(this);
533
+ }
534
+ if (typeof token === "function") {
535
+ return new token();
536
+ }
537
+ throw new ResolutionError("missing-provider", token);
538
+ });
539
+ }
540
+ /**
541
+ * Acquire inline resources once per container, with explicit or automatic
542
+ * disposal. Like a provider factory, `factory` receives this container.
543
+ */ use(token, factory, dispose) {
544
+ if (this.#providers.hasInstance(token)) {
545
+ this.#admitRetainedAccess();
546
+ this.#tracker?.record(this, token, this.#path.current);
547
+ return this.#providers.instance(token);
548
+ }
549
+ this.#admitNewAcquisition();
550
+ return this.#acquire(token, factory, dispose);
551
+ }
552
+ /**
553
+ * Register LIFO cleanup. Blind to the phase on purpose: a resource released
554
+ * mid-drain may still register its own cleanup, and only the queue knows
555
+ * whether anything is left to run it.
556
+ */ defer(cleanup) {
557
+ this.#owner.defer(cleanup);
558
+ }
559
+ /**
560
+ * Close a container that never constructed or deferred anything, avoiding an
561
+ * `await`. Returns `false` and changes nothing otherwise.
562
+ */ disposeSync() {
563
+ if (this.#resources || this.#providers.hasInstances) {
564
+ return false;
565
+ }
566
+ if (this.#phase !== "disposed") {
567
+ this.#clearResolution();
568
+ }
569
+ return true;
570
+ }
571
+ /** Close acquisition synchronously, then clear resolution storage after teardown. */ [Symbol.asyncDispose]() {
572
+ if (this.#phase === "open") {
573
+ this.#phase = "closing";
574
+ this.#disposal = this.#owner.close().finally(()=>{
575
+ this.#clearResolution();
576
+ });
577
+ }
578
+ return this.#disposal ??= Promise.resolve();
579
+ }
580
+ /** Reading what this container already holds stays legal until it is disposed. */ #admitRetainedAccess() {
581
+ if (this.#phase === "disposed") {
582
+ throw new ContainerClosedError("disposed");
583
+ }
584
+ }
585
+ /** Providers, children, and construction stop the moment teardown begins. */ #admitNewAcquisition() {
586
+ const phase = this.#phase;
587
+ if (phase !== "open") {
588
+ throw new ContainerClosedError(phase);
589
+ }
590
+ }
591
+ #clearResolution() {
592
+ this.#phase = "disposed";
593
+ this.#providers.clear();
594
+ if (this === this.#root) {
595
+ this.#graph = undefined;
596
+ } else {
597
+ this.#root.#graph?.remove(this);
598
+ }
599
+ }
600
+ #acquire(token, factory, dispose) {
601
+ // A cycle is still an attempt worth reporting, so record before guarding.
602
+ this.#tracker?.record(this, token, this.#path.current);
603
+ this.#path.enter(this, token);
604
+ try {
605
+ const value = this.#owner.construct(factory, dispose);
606
+ this.#providers.cache(token, value);
607
+ return value;
608
+ } finally{
609
+ this.#path.exit();
610
+ }
611
+ }
612
+ }
613
+
614
+ ;// CONCATENATED MODULE: ./src/index.ts
615
+ /** Hierarchical dependency resolution and resource ownership. */
616
+
617
+
618
+
619
+
620
+ var __webpack_exports__Container = __webpack_exports__.mc;
621
+ var __webpack_exports__ContainerClosedError = __webpack_exports__.a$;
622
+ var __webpack_exports__ContainerKey = __webpack_exports__.jc;
623
+ var __webpack_exports__DisposalConflictError = __webpack_exports__.uP;
624
+ var __webpack_exports__ProviderConflictError = __webpack_exports__.kb;
625
+ var __webpack_exports__ResolutionError = __webpack_exports__.fe;
626
+ var __webpack_exports__currentContainer = __webpack_exports__.zt;
627
+ var __webpack_exports__inject = __webpack_exports__.WQ;
628
+ var __webpack_exports__onDispose = __webpack_exports__.zp;
629
+ var __webpack_exports__scoped = __webpack_exports__.P1;
630
+ var __webpack_exports__token = __webpack_exports__.Sh;
631
+ var __webpack_exports__withContainer = __webpack_exports__.Ub;
632
+ export { __webpack_exports__Container as Container, __webpack_exports__ContainerClosedError as ContainerClosedError, __webpack_exports__ContainerKey as ContainerKey, __webpack_exports__DisposalConflictError as DisposalConflictError, __webpack_exports__ProviderConflictError as ProviderConflictError, __webpack_exports__ResolutionError as ResolutionError, __webpack_exports__currentContainer as currentContainer, __webpack_exports__inject as inject, __webpack_exports__onDispose as onDispose, __webpack_exports__scoped as scoped, __webpack_exports__token as token, __webpack_exports__withContainer as withContainer };
633
+
634
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["webpack://@tiberjs/di/webpack/runtime/define_property_getters","webpack://@tiberjs/di/webpack/runtime/has_own_property","webpack://@tiberjs/di/./src/resources/active-container.ts","webpack://@tiberjs/di/./src/ambient.ts","webpack://@tiberjs/di/./src/tokens.ts","webpack://@tiberjs/di/./src/errors.ts","webpack://@tiberjs/di/./src/resolution/graph.ts","webpack://@tiberjs/di/./src/resolution/path.ts","webpack://@tiberjs/di/./src/resolution/providers.ts","webpack://@tiberjs/di/./src/resources/cleanup.ts","webpack://@tiberjs/di/./src/resources/queue.ts","webpack://@tiberjs/di/./src/resources/owner.ts","webpack://@tiberjs/di/./src/resources/ownership.ts","webpack://@tiberjs/di/./src/container.ts","webpack://@tiberjs/di/./src/index.ts"],"sourcesContent":["__webpack_require__.d = (exports, getters, values) => {\n\tvar define = (defs, kind) => {\n\t\tfor(var key in defs) {\n\t\t\tif(__webpack_require__.o(defs, key) && !__webpack_require__.o(exports, key)) {\n\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, [kind]: defs[key] });\n\t\t\t}\n\t\t}\n\t};\n\tdefine(getters, \"get\");\n\tdefine(values, \"value\");\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { Container } from \"../container.js\";\n\n/**\n * The container bound as ambient while one of its resources is constructed or\n * torn down. Nested construction overrides it for the inner call only.\n */\nexport const activeContainer = new AsyncLocalStorage<Container>();\n","import { contextKey, peekState, provide, withContext, type ContextKey } from \"@tiberjs/runner\";\nimport type { Container } from \"./container.js\";\nimport { activeContainer } from \"./resources/active-container.js\";\nimport type { Factory, InjectionToken } from \"./tokens.js\";\n\n/** The execution-context binding that carries a container across executions. */\nexport const ContainerKey: ContextKey<Container> = contextKey<Container>(\"di.container\");\n\n/** Construction container first, otherwise the current execution's binding. */\nexport function currentContainer(): Container {\n const constructing = activeContainer.getStore();\n if (constructing) {\n return constructing;\n }\n\n // One state read and one frame walk: inject() runs on request paths, and a\n // bound container is never undefined, so absence needs no separate probe.\n const bound = peekState()?.context.values.get(ContainerKey.id) as Container | undefined;\n if (!bound) {\n throw new Error(\n \"No active container. This API requires construction, disposal, withContainer(), or an execution bound to ContainerKey.\",\n );\n }\n\n return bound;\n}\n\n/** Resolve a dependency during construction or inside a bound execution. */\nexport function inject<T>(token: InjectionToken<T>): T {\n return currentContainer().resolve(token);\n}\n\n/** Acquire a resource once per container and release it when that container closes. */\nexport function scoped<T>(\n token: InjectionToken<T>,\n factory: Factory<T>,\n dispose?: (value: T) => unknown | Promise<unknown>,\n): T {\n return currentContainer().use(token, factory, dispose);\n}\n\n/** Register LIFO cleanup in the ambient container. */\nexport function onDispose(cleanup: () => unknown | Promise<unknown>): void {\n currentContainer().defer(cleanup);\n}\n\n/**\n * Bind `container` as the ambient container for `handler`.\n *\n * The binding is always installed locally, so nested calls override an outer\n * construction container. With an active execution it is additionally published\n * on the context so derived executions observe the same container.\n */\nexport function withContainer<T>(container: Container, handler: () => T): T {\n if (peekState()) {\n return withContext([provide(ContainerKey, container)], () =>\n activeContainer.run(container, handler),\n );\n }\n\n return activeContainer.run(container, handler);\n}\n","import type { Container } from \"./container.js\";\n\n/** A zero-argument constructor usable as its own injection token. */\nexport type Constructor<T = object> = new (...args: never[]) => T;\n\n/** An opaque token for values/interfaces that have no runtime class. */\nexport interface Token<T> {\n readonly key: symbol;\n /** Phantom carrier; never present at runtime. */\n readonly _type?: T;\n}\n\nexport type InjectionToken<T> = Constructor<T> | Token<T>;\nexport type Factory<T> = (container: Container) => T;\n\nexport function token<T>(description: string): Token<T> {\n return { key: Symbol(description) };\n}\n\nexport function describeToken(token: InjectionToken<unknown>): string {\n if (typeof token === \"function\") {\n return token.name || \"anonymous class\";\n }\n\n return token.key.description ?? \"token\";\n}\n","import { describeToken, type InjectionToken } from \"./tokens.js\";\n\n/** A container resolution failure; provider exceptions propagate unchanged. */\nexport class ResolutionError extends Error {\n constructor(\n readonly reason: \"missing-provider\" | \"circular-dependency\",\n readonly token: InjectionToken<unknown>,\n options?: ErrorOptions,\n ) {\n super(\n reason === \"missing-provider\"\n ? `No provider registered for token \"${describeToken(token)}\". Use provide(token, factory) for values/interfaces.`\n : `Circular dependency while resolving \"${describeToken(token)}\".`,\n options,\n );\n this.name = \"ResolutionError\";\n }\n}\n\n/** Resource admission failed because this container's teardown has begun. */\nexport class ContainerClosedError extends Error {\n constructor(\n readonly state: \"closing\" | \"disposed\",\n options?: ErrorOptions,\n ) {\n super(state === \"closing\" ? \"Container is closing.\" : \"Container has been disposed.\", options);\n this.name = \"ContainerClosedError\";\n }\n}\n\n/** A provider cannot replace an instance this container has already handed out. */\nexport class ProviderConflictError extends Error {\n constructor(readonly token: InjectionToken<unknown>) {\n super(\n `\"${describeToken(token)}\" is already resolved in this container. Register providers before resolving, or override the token in a child container.`,\n );\n this.name = \"ProviderConflictError\";\n }\n}\n\n/** An object must have exactly one disposal owner and one automatic close protocol. */\nexport class DisposalConflictError extends Error {\n constructor(readonly reason: \"multiple-hooks\" | \"already-owned\") {\n super(\n reason === \"multiple-hooks\"\n ? \"ContainerObject.onClose cannot coexist with Symbol.asyncDispose or Symbol.dispose.\"\n : \"An explicit disposer cannot take ownership of an already-owned resource.\",\n );\n this.name = \"DisposalConflictError\";\n }\n}\n","import { describeToken, type InjectionToken } from \"../tokens.js\";\nimport type { ResolutionFrame } from \"./path.js\";\n\n/** The root container's resolution attempts: `from` resolves `to`. */\nexport interface ResolutionGraph {\n readonly nodes: ReadonlyArray<{ readonly id: number; readonly name: string }>;\n readonly edges: ReadonlyArray<{ readonly from: number; readonly to: number }>;\n}\n\n/**\n * Root-local resolution diagnostics. Node identity is per container, and both\n * edge directions are indexed so removing a disposed container costs its own\n * nodes rather than a full scan.\n */\nexport class ResolutionTracker {\n #nextId = 0;\n readonly #idsByOwner = new WeakMap<object, Map<InjectionToken<unknown>, number>>();\n readonly #nodes = new Map<number, string>();\n readonly #outgoing = new Map<number, Set<number>>();\n readonly #incoming = new Map<number, Set<number>>();\n\n /** Records a resolution as a dependency of the construction that requested it. */\n record(owner: object, token: InjectionToken<unknown>, parent?: ResolutionFrame): void {\n let ids = this.#idsByOwner.get(owner);\n if (!ids) {\n this.#idsByOwner.set(owner, (ids = new Map()));\n }\n\n let id = ids.get(token);\n if (id === undefined) {\n id = this.#nextId++;\n ids.set(token, id);\n this.#nodes.set(id, describeToken(token));\n }\n\n // A node never depends on itself, and a removed owner's frame links nothing.\n if (parent && (parent.owner !== owner || parent.token !== token)) {\n const from = this.#idsByOwner.get(parent.owner)?.get(parent.token);\n if (from !== undefined) {\n this.#link(from, id);\n }\n }\n }\n\n snapshot(): ResolutionGraph {\n const edges: Array<{ from: number; to: number }> = [];\n for (const [from, targets] of this.#outgoing) {\n for (const to of targets) {\n edges.push({ from, to });\n }\n }\n\n return { nodes: Array.from(this.#nodes, ([id, name]) => ({ id, name })), edges };\n }\n\n /** Drops a disposed owner's nodes and every edge that touched them. */\n remove(owner: object): void {\n const ids = this.#idsByOwner.get(owner);\n if (!ids) {\n return;\n }\n\n for (const id of ids.values()) {\n this.#nodes.delete(id);\n this.#unlink(id);\n }\n\n this.#idsByOwner.delete(owner);\n }\n\n #link(from: number, to: number): void {\n let targets = this.#outgoing.get(from);\n if (!targets) {\n this.#outgoing.set(from, (targets = new Set()));\n }\n targets.add(to);\n\n let sources = this.#incoming.get(to);\n if (!sources) {\n this.#incoming.set(to, (sources = new Set()));\n }\n sources.add(from);\n }\n\n #unlink(id: number): void {\n const targets = this.#outgoing.get(id);\n if (targets) {\n for (const to of targets) {\n this.#detach(this.#incoming, to, id);\n }\n this.#outgoing.delete(id);\n }\n\n const sources = this.#incoming.get(id);\n if (sources) {\n for (const from of sources) {\n this.#detach(this.#outgoing, from, id);\n }\n this.#incoming.delete(id);\n }\n }\n\n #detach(index: Map<number, Set<number>>, node: number, peer: number): void {\n const peers = index.get(node);\n if (peers?.delete(peer) && peers.size === 0) {\n index.delete(node);\n }\n }\n}\n","import { ResolutionError } from \"../errors.js\";\nimport type { InjectionToken } from \"../tokens.js\";\n\n/** One construction in flight: `owner` is building `token` right now. */\nexport interface ResolutionFrame {\n readonly owner: object;\n readonly token: InjectionToken<unknown>;\n}\n\n/**\n * The chain of constructions in flight under one container tree, newest last.\n *\n * A frame is a container and a token together, so the same token may be in\n * flight in two containers while a child decorates an ancestor's\n * implementation; only a container re-entering its own token is a cycle.\n */\nexport class ResolutionPath {\n readonly #frames: ResolutionFrame[] = [];\n\n /** The construction that whatever resolves next belongs to. */\n get current(): ResolutionFrame | undefined {\n return this.#frames[this.#frames.length - 1];\n }\n\n /** Rejects a container that re-enters a token it is already constructing. */\n enter(owner: object, token: InjectionToken<unknown>): void {\n for (const frame of this.#frames) {\n if (frame.owner === owner && frame.token === token) {\n throw new ResolutionError(\"circular-dependency\", token);\n }\n }\n\n this.#frames.push({ owner, token });\n }\n\n /** A failed attempt leaves no trace, so the token stays resolvable. */\n exit(): void {\n this.#frames.pop();\n }\n}\n","import { ProviderConflictError } from \"../errors.js\";\nimport type { Factory, InjectionToken } from \"../tokens.js\";\n\n/** One container's providers and the instances it has constructed. */\nexport class ProviderRegistry {\n #factories: Map<InjectionToken<unknown>, Factory<unknown>> | undefined;\n #instances: Map<InjectionToken<unknown>, unknown> | undefined;\n\n /** Whether this container has ever cached an instance of its own. */\n get hasInstances(): boolean {\n return this.#instances !== undefined;\n }\n\n /** An explicit provider or a cached instance, never a constructibility probe. */\n has(token: InjectionToken<unknown>): boolean {\n return (this.#instances?.has(token) ?? false) || (this.#factories?.has(token) ?? false);\n }\n\n /** Distinguishes a cached `undefined` from a missing instance. */\n hasInstance(token: InjectionToken<unknown>): boolean {\n return this.#instances?.has(token) ?? false;\n }\n\n instance<T>(token: InjectionToken<T>): T {\n return this.#instances?.get(token) as T;\n }\n\n hasFactory(token: InjectionToken<unknown>): boolean {\n return this.#factories?.has(token) ?? false;\n }\n\n factory<T>(token: InjectionToken<T>): Factory<T> | undefined {\n return this.#factories?.get(token) as Factory<T> | undefined;\n }\n\n /**\n * Replacing a factory whose instance this container already handed out is\n * rejected: the cached instance would silently win. Override in a child.\n */\n provide<T>(token: InjectionToken<T>, factory: Factory<T>): void {\n if (this.#instances?.has(token)) {\n throw new ProviderConflictError(token);\n }\n (this.#factories ??= new Map()).set(token, factory as Factory<unknown>);\n }\n\n cache(token: InjectionToken<unknown>, value: unknown): void {\n (this.#instances ??= new Map()).set(token, value);\n }\n\n /** Disposal makes the container unusable, so its storage is released. */\n clear(): void {\n this.#factories = undefined;\n this.#instances = undefined;\n }\n}\n","import { DisposalConflictError } from \"../errors.js\";\n\n/** Releases one resource; a returned promise is awaited during disposal. */\nexport type Cleanup = () => unknown | Promise<unknown>;\n\n/** Structural resource hook. Do not combine onClose with a symbol disposer. */\nexport interface ContainerObject {\n onClose?(): unknown | Promise<unknown>;\n}\n\n/**\n * How a constructed value is released, plus the conflict that must reject it.\n *\n * A conflicting shape still yields `cleanup`: the caller registers it to roll\n * back an object it will never hand out, then throws `conflict`.\n */\nexport interface CleanupPlan {\n readonly cleanup: Cleanup | undefined;\n readonly conflict: DisposalConflictError | undefined;\n}\n\n/**\n * Precedence is explicit disposer, then `Symbol.asyncDispose`/`Symbol.dispose`,\n * then `ContainerObject.onClose`. `onClose` beside a symbol disposer is\n * ambiguous and refused, unless an explicit disposer overrides both shapes.\n */\nexport function planCleanup<T>(\n value: T,\n explicitDispose?: (value: T) => unknown | Promise<unknown>,\n): CleanupPlan {\n const asyncDispose = (value as Partial<AsyncDisposable>)[Symbol.asyncDispose];\n const dispose = (value as Partial<Disposable>)[Symbol.dispose];\n const onClose = (value as ContainerObject).onClose;\n const symbolDispose = typeof asyncDispose === \"function\" ? asyncDispose : dispose;\n\n const cleanup = explicitDispose\n ? () => explicitDispose(value)\n : typeof symbolDispose === \"function\"\n ? () => symbolDispose.call(value)\n : typeof onClose === \"function\"\n ? () => onClose.call(value)\n : undefined;\n const conflict =\n !explicitDispose && typeof onClose === \"function\" && typeof symbolDispose === \"function\"\n ? new DisposalConflictError(\"multiple-hooks\")\n : undefined;\n\n return { cleanup, conflict };\n}\n","import { combinedError } from \"@tiberjs/runner\";\nimport { ContainerClosedError } from \"../errors.js\";\nimport type { Cleanup } from \"./cleanup.js\";\n\n/** Runs one cleanup callback with the owner's ambient binding installed. */\nexport type CleanupInvoker = (cleanup: Cleanup) => unknown;\n\n/**\n * LIFO cleanup storage for one owner, drained once. Knows nothing about\n * containers: every callback runs through the invoker its owner supplied.\n */\nexport class DisposalQueue {\n #cleanups: Cleanup[] | undefined;\n #drained = false;\n\n constructor(private readonly invoke: CleanupInvoker) {}\n\n /** Cleanup registered while draining is drained too; after that nothing would run it. */\n defer(cleanup: Cleanup): void {\n if (this.#drained) {\n throw new ContainerClosedError(\"disposed\");\n }\n (this.#cleanups ??= []).push(cleanup);\n }\n\n /** Drains in reverse registration order, retaining independent failures. */\n async close(): Promise<void> {\n // Yield past a synchronous factory that initiated disposal before returning its resource.\n await Promise.resolve();\n\n const errors: unknown[] = [];\n while (this.#cleanups?.length) {\n try {\n await this.invoke(this.#cleanups.pop()!);\n } catch (error) {\n errors.push(error);\n }\n }\n\n this.#drained = true;\n this.#cleanups = undefined;\n\n // Independent failures keep their identity, in drain order.\n if (errors.length) {\n throw combinedError(errors, \"Errors during disposal.\");\n }\n }\n}\n","import type { Container } from \"../container.js\";\nimport { DisposalConflictError } from \"../errors.js\";\nimport { activeContainer } from \"./active-container.js\";\nimport { type Cleanup, planCleanup } from \"./cleanup.js\";\nimport type { OwnershipRegistry } from \"./ownership.js\";\nimport { DisposalQueue } from \"./queue.js\";\n\n/**\n * The resources one container owns: it binds the ambient container around\n * construction and teardown, claims each constructed value at most once, and\n * releases what it claimed in LIFO order.\n */\nexport class ResourceOwner {\n readonly #queue: DisposalQueue;\n\n constructor(\n private readonly container: Container,\n private readonly ownership: OwnershipRegistry,\n ) {\n this.#queue = new DisposalQueue((cleanup) => activeContainer.run(container, cleanup));\n }\n\n /** Only the queue knows whether it already drained, so admission is its call. */\n defer(cleanup: Cleanup): void {\n this.#queue.defer(cleanup);\n }\n\n /** Construct with ambient resolution bound, then take disposal ownership. */\n construct<T>(\n factory: (container: Container) => T,\n dispose?: (value: T) => unknown | Promise<unknown>,\n ): T {\n const value = activeContainer.run(this.container, factory, this.container);\n this.#adopt(value, dispose);\n\n return value;\n }\n\n /** Once drained, this owner is responsible for nothing it claimed. */\n close(): Promise<void> {\n return this.#queue.close().finally(() => this.ownership.release(this));\n }\n\n #adopt<T>(value: T, explicitDispose?: (value: T) => unknown | Promise<unknown>): void {\n if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) {\n if (explicitDispose) {\n this.defer(() => explicitDispose(value));\n }\n return;\n }\n\n const target = value as object;\n if (this.ownership.hasLiveOwner(target)) {\n // An alias of a live resource borrows it; a second disposer would double-release.\n if (explicitDispose) {\n throw new DisposalConflictError(\"already-owned\");\n }\n return;\n }\n\n const plan = planCleanup(value, explicitDispose);\n if (plan.cleanup) {\n this.defer(plan.cleanup);\n this.ownership.claim(target, this);\n }\n // Registered cleanup above survives refusal, because the value is never returned.\n if (plan.conflict) {\n this.ownership.reject(target, plan.conflict);\n throw plan.conflict;\n }\n }\n}\n","/** A permanently refused value, boxed so it is never mistaken for an owner. */\nclass Rejection {\n constructor(readonly error: unknown) {}\n}\n\n// Keyed by root so surviving children share ownership without initializing a\n// closed ancestor's resources.\nconst registriesByRoot = new WeakMap<object, OwnershipRegistry>();\n\n/** At most one disposal owner per object, shared across one container tree. */\nexport class OwnershipRegistry {\n readonly #entries = new WeakMap<object, object>();\n readonly #drained = new WeakSet<object>();\n\n /** Every container descending from `root` claims into the same registry. */\n static forRoot(root: object): OwnershipRegistry {\n let registry = registriesByRoot.get(root);\n if (!registry) {\n registriesByRoot.set(root, (registry = new OwnershipRegistry()));\n }\n\n return registry;\n }\n\n /**\n * Whether someone is still responsible for releasing `value`; an owner that\n * already drained leaves it adoptable again. Rethrows a refused shape's\n * cached rejection so every later alias of that object fails identically.\n */\n hasLiveOwner(value: object): boolean {\n const entry = this.#entries.get(value);\n if (entry instanceof Rejection) {\n throw entry.error;\n }\n\n return entry !== undefined && !this.#drained.has(entry);\n }\n\n claim(value: object, owner: object): void {\n this.#entries.set(value, owner);\n }\n\n /** `owner` finished its cleanup: every value it claimed is unowned again. */\n release(owner: object): void {\n this.#drained.add(owner);\n }\n\n /** Refusal is permanent: `value` can never gain a disposal owner afterwards. */\n reject(value: object, error: unknown): void {\n this.#entries.set(value, new Rejection(error));\n }\n}\n","import { ContainerClosedError, ResolutionError } from \"./errors.js\";\nimport { ResolutionTracker, type ResolutionGraph } from \"./resolution/graph.js\";\nimport { ResolutionPath } from \"./resolution/path.js\";\nimport { ProviderRegistry } from \"./resolution/providers.js\";\nimport { ResourceOwner } from \"./resources/owner.js\";\nimport { OwnershipRegistry } from \"./resources/ownership.js\";\nimport type { Factory, InjectionToken } from \"./tokens.js\";\n\ntype ContainerPhase = \"open\" | \"closing\" | \"disposed\";\n\n/**\n * A hierarchical dependency container and resource owner. A child resolves its\n * ancestors' providers but owns and disposes only what it constructed itself.\n */\nexport class Container {\n readonly #parent: Container | undefined;\n readonly #root: Container;\n readonly #providers = new ProviderRegistry();\n readonly #path: ResolutionPath;\n /** Diagnostics live on the root; a child records into its root's tracker. */\n #graph: ResolutionTracker | undefined;\n #resources: ResourceOwner | undefined;\n #disposal: Promise<void> | undefined;\n #phase: ContainerPhase = \"open\";\n\n constructor(parent?: Container) {\n this.#parent = parent;\n this.#root = parent ? parent.#root : this;\n this.#path = parent ? parent.#path : new ResolutionPath();\n }\n\n get #owner(): ResourceOwner {\n if (!this.#resources) {\n // A drained container must not build an owner whose queue nothing drains.\n this.#admitRetainedAccess();\n this.#resources = new ResourceOwner(this, OwnershipRegistry.forRoot(this.#root));\n }\n return this.#resources;\n }\n\n /** Undefined once the root is gone, so a surviving child cannot repopulate it. */\n get #tracker(): ResolutionTracker | undefined {\n if (this.#root.#phase === \"disposed\") {\n return undefined;\n }\n\n return (this.#root.#graph ??= new ResolutionTracker());\n }\n\n /** A child container resolves application singletons through its parent. */\n child(): Container {\n this.#admitNewAcquisition();\n return new Container(this);\n }\n\n /** Includes failed attempts and active children, but never disposed containers. */\n resolutionGraph(): ResolutionGraph {\n return this.#root.#graph?.snapshot() ?? { nodes: [], edges: [] };\n }\n\n /** Register a provider before the token is resolved here. */\n provide<T>(token: InjectionToken<T>, factory: Factory<T>): void {\n this.#admitNewAcquisition();\n this.#providers.provide(token, factory);\n }\n\n has(token: InjectionToken<unknown>): boolean {\n return this.#providers.has(token) || (this.#parent?.has(token) ?? false);\n }\n\n /** Resolve local cache/provider, then ancestors; default classes live at root. */\n resolve<T>(token: InjectionToken<T>): T {\n if (this.#providers.hasInstance(token)) {\n this.#admitRetainedAccess();\n this.#tracker?.record(this, token, this.#path.current);\n\n return this.#providers.instance(token);\n }\n\n // Ancestors own their own admission; a closing child may still read singletons.\n if (!this.#providers.hasFactory(token) && this.#parent) {\n this.#admitRetainedAccess();\n return this.#parent.resolve(token);\n }\n\n this.#admitNewAcquisition();\n\n return this.#acquire(token, () => {\n const factory = this.#providers.factory(token);\n if (factory) {\n return factory(this);\n }\n if (typeof token === \"function\") {\n return new token();\n }\n\n throw new ResolutionError(\"missing-provider\", token);\n });\n }\n\n /**\n * Acquire inline resources once per container, with explicit or automatic\n * disposal. Like a provider factory, `factory` receives this container.\n */\n use<T>(\n token: InjectionToken<T>,\n factory: Factory<T>,\n dispose?: (value: T) => unknown | Promise<unknown>,\n ): T {\n if (this.#providers.hasInstance(token)) {\n this.#admitRetainedAccess();\n this.#tracker?.record(this, token, this.#path.current);\n\n return this.#providers.instance(token);\n }\n\n this.#admitNewAcquisition();\n\n return this.#acquire(token, factory, dispose);\n }\n\n /**\n * Register LIFO cleanup. Blind to the phase on purpose: a resource released\n * mid-drain may still register its own cleanup, and only the queue knows\n * whether anything is left to run it.\n */\n defer(cleanup: () => unknown | Promise<unknown>): void {\n this.#owner.defer(cleanup);\n }\n\n /**\n * Close a container that never constructed or deferred anything, avoiding an\n * `await`. Returns `false` and changes nothing otherwise.\n */\n disposeSync(): boolean {\n if (this.#resources || this.#providers.hasInstances) {\n return false;\n }\n if (this.#phase !== \"disposed\") {\n this.#clearResolution();\n }\n return true;\n }\n\n /** Close acquisition synchronously, then clear resolution storage after teardown. */\n [Symbol.asyncDispose](): Promise<void> {\n if (this.#phase === \"open\") {\n this.#phase = \"closing\";\n this.#disposal = this.#owner.close().finally(() => {\n this.#clearResolution();\n });\n }\n\n return (this.#disposal ??= Promise.resolve());\n }\n\n /** Reading what this container already holds stays legal until it is disposed. */\n #admitRetainedAccess(): void {\n if (this.#phase === \"disposed\") {\n throw new ContainerClosedError(\"disposed\");\n }\n }\n\n /** Providers, children, and construction stop the moment teardown begins. */\n #admitNewAcquisition(): void {\n const phase = this.#phase;\n if (phase !== \"open\") {\n throw new ContainerClosedError(phase);\n }\n }\n\n #clearResolution(): void {\n this.#phase = \"disposed\";\n this.#providers.clear();\n\n if (this === this.#root) {\n this.#graph = undefined;\n } else {\n this.#root.#graph?.remove(this);\n }\n }\n\n #acquire<T>(\n token: InjectionToken<T>,\n factory: Factory<T>,\n dispose?: (value: T) => unknown | Promise<unknown>,\n ): T {\n // A cycle is still an attempt worth reporting, so record before guarding.\n this.#tracker?.record(this, token, this.#path.current);\n this.#path.enter(this, token);\n\n try {\n const value = this.#owner.construct(factory, dispose);\n this.#providers.cache(token, value);\n\n return value;\n } finally {\n this.#path.exit();\n }\n }\n}\n","/** Hierarchical dependency resolution and resource ownership. */\n\nexport {\n ContainerKey,\n currentContainer,\n inject,\n onDispose,\n scoped,\n withContainer,\n} from \"./ambient.js\";\nexport type { ContainerObject } from \"./resources/cleanup.js\";\nexport { Container } from \"./container.js\";\nexport {\n ContainerClosedError,\n DisposalConflictError,\n ProviderConflictError,\n ResolutionError,\n} from \"./errors.js\";\nexport type { ResolutionGraph } from \"./resolution/graph.js\";\nexport { token } from \"./tokens.js\";\nexport type { Constructor, Factory, InjectionToken, Token } from \"./tokens.js\";\n"],"names":["AsyncLocalStorage","activeContainer","contextKey","peekState","provide","withContext","ContainerKey","currentContainer","constructing","bound","Error","inject","token","scoped","factory","dispose","onDispose","cleanup","withContainer","container","handler","description","Symbol","describeToken","ResolutionError","reason","options","ContainerClosedError","state","ProviderConflictError","DisposalConflictError","ResolutionTracker","WeakMap","Map","owner","parent","ids","id","undefined","from","edges","targets","to","Array","name","Set","sources","index","node","peer","peers","ResolutionPath","frame","ProviderRegistry","value","planCleanup","explicitDispose","asyncDispose","onClose","symbolDispose","conflict","combinedError","DisposalQueue","invoke","Promise","errors","error","ResourceOwner","ownership","target","plan","Rejection","registriesByRoot","OwnershipRegistry","WeakSet","root","registry","entry","Container","phase"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type InjectionToken } from "../tokens.js";
2
+ import type { ResolutionFrame } from "./path.js";
3
+ /** The root container's resolution attempts: `from` resolves `to`. */
4
+ export interface ResolutionGraph {
5
+ readonly nodes: ReadonlyArray<{
6
+ readonly id: number;
7
+ readonly name: string;
8
+ }>;
9
+ readonly edges: ReadonlyArray<{
10
+ readonly from: number;
11
+ readonly to: number;
12
+ }>;
13
+ }
14
+ /**
15
+ * Root-local resolution diagnostics. Node identity is per container, and both
16
+ * edge directions are indexed so removing a disposed container costs its own
17
+ * nodes rather than a full scan.
18
+ */
19
+ export declare class ResolutionTracker {
20
+ #private;
21
+ /** Records a resolution as a dependency of the construction that requested it. */
22
+ record(owner: object, token: InjectionToken<unknown>, parent?: ResolutionFrame): void;
23
+ snapshot(): ResolutionGraph;
24
+ /** Drops a disposed owner's nodes and every edge that touched them. */
25
+ remove(owner: object): void;
26
+ }
27
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/resolution/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;;IAO5B,kFAAkF;IAClF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI;IAsBrF,QAAQ,IAAI,eAAe;IAW3B,uEAAuE;IACvE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAoD5B"}
@@ -0,0 +1,23 @@
1
+ import type { InjectionToken } from "../tokens.js";
2
+ /** One construction in flight: `owner` is building `token` right now. */
3
+ export interface ResolutionFrame {
4
+ readonly owner: object;
5
+ readonly token: InjectionToken<unknown>;
6
+ }
7
+ /**
8
+ * The chain of constructions in flight under one container tree, newest last.
9
+ *
10
+ * A frame is a container and a token together, so the same token may be in
11
+ * flight in two containers while a child decorates an ancestor's
12
+ * implementation; only a container re-entering its own token is a cycle.
13
+ */
14
+ export declare class ResolutionPath {
15
+ #private;
16
+ /** The construction that whatever resolves next belongs to. */
17
+ get current(): ResolutionFrame | undefined;
18
+ /** Rejects a container that re-enters a token it is already constructing. */
19
+ enter(owner: object, token: InjectionToken<unknown>): void;
20
+ /** A failed attempt leaves no trace, so the token stays resolvable. */
21
+ exit(): void;
22
+ }
23
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/resolution/path.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;;IAGzB,+DAA+D;IAC/D,IAAI,OAAO,IAAI,eAAe,GAAG,SAAS,CAEzC;IAED,6EAA6E;IAC7E,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,IAAI;IAU1D,uEAAuE;IACvE,IAAI,IAAI,IAAI;CAGb"}
@@ -0,0 +1,23 @@
1
+ import type { Factory, InjectionToken } from "../tokens.js";
2
+ /** One container's providers and the instances it has constructed. */
3
+ export declare class ProviderRegistry {
4
+ #private;
5
+ /** Whether this container has ever cached an instance of its own. */
6
+ get hasInstances(): boolean;
7
+ /** An explicit provider or a cached instance, never a constructibility probe. */
8
+ has(token: InjectionToken<unknown>): boolean;
9
+ /** Distinguishes a cached `undefined` from a missing instance. */
10
+ hasInstance(token: InjectionToken<unknown>): boolean;
11
+ instance<T>(token: InjectionToken<T>): T;
12
+ hasFactory(token: InjectionToken<unknown>): boolean;
13
+ factory<T>(token: InjectionToken<T>): Factory<T> | undefined;
14
+ /**
15
+ * Replacing a factory whose instance this container already handed out is
16
+ * rejected: the cached instance would silently win. Override in a child.
17
+ */
18
+ provide<T>(token: InjectionToken<T>, factory: Factory<T>): void;
19
+ cache(token: InjectionToken<unknown>, value: unknown): void;
20
+ /** Disposal makes the container unusable, so its storage is released. */
21
+ clear(): void;
22
+ }
23
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/resolution/providers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5D,sEAAsE;AACtE,qBAAa,gBAAgB;;IAI3B,qEAAqE;IACrE,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,iFAAiF;IACjF,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO;IAI5C,kEAAkE;IAClE,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO;IAIpD,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC;IAIxC,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO;IAInD,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS;IAI5D;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAO/D,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI3D,yEAAyE;IACzE,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,8 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import type { Container } from "../container.js";
3
+ /**
4
+ * The container bound as ambient while one of its resources is constructed or
5
+ * torn down. Nested construction overrides it for the inner call only.
6
+ */
7
+ export declare const activeContainer: AsyncLocalStorage<Container>;
8
+ //# sourceMappingURL=active-container.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-container.d.ts","sourceRoot":"","sources":["../../src/resources/active-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,eAAe,8BAAqC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { DisposalConflictError } from "../errors.js";
2
+ /** Releases one resource; a returned promise is awaited during disposal. */
3
+ export type Cleanup = () => unknown | Promise<unknown>;
4
+ /** Structural resource hook. Do not combine onClose with a symbol disposer. */
5
+ export interface ContainerObject {
6
+ onClose?(): unknown | Promise<unknown>;
7
+ }
8
+ /**
9
+ * How a constructed value is released, plus the conflict that must reject it.
10
+ *
11
+ * A conflicting shape still yields `cleanup`: the caller registers it to roll
12
+ * back an object it will never hand out, then throws `conflict`.
13
+ */
14
+ export interface CleanupPlan {
15
+ readonly cleanup: Cleanup | undefined;
16
+ readonly conflict: DisposalConflictError | undefined;
17
+ }
18
+ /**
19
+ * Precedence is explicit disposer, then `Symbol.asyncDispose`/`Symbol.dispose`,
20
+ * then `ContainerObject.onClose`. `onClose` beside a symbol disposer is
21
+ * ambiguous and refused, unless an explicit disposer overrides both shapes.
22
+ */
23
+ export declare function planCleanup<T>(value: T, explicitDispose?: (value: T) => unknown | Promise<unknown>): CleanupPlan;
24
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/resources/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvD,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,CAAC;CACtD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,EACR,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACzD,WAAW,CAmBb"}
@@ -0,0 +1,21 @@
1
+ import type { Container } from "../container.js";
2
+ import { type Cleanup } from "./cleanup.js";
3
+ import type { OwnershipRegistry } from "./ownership.js";
4
+ /**
5
+ * The resources one container owns: it binds the ambient container around
6
+ * construction and teardown, claims each constructed value at most once, and
7
+ * releases what it claimed in LIFO order.
8
+ */
9
+ export declare class ResourceOwner {
10
+ #private;
11
+ private readonly container;
12
+ private readonly ownership;
13
+ constructor(container: Container, ownership: OwnershipRegistry);
14
+ /** Only the queue knows whether it already drained, so admission is its call. */
15
+ defer(cleanup: Cleanup): void;
16
+ /** Construct with ambient resolution bound, then take disposal ownership. */
17
+ construct<T>(factory: (container: Container) => T, dispose?: (value: T) => unknown | Promise<unknown>): T;
18
+ /** Once drained, this owner is responsible for nothing it claimed. */
19
+ close(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=owner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner.d.ts","sourceRoot":"","sources":["../../src/resources/owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,KAAK,OAAO,EAAe,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD;;;;GAIG;AACH,qBAAa,aAAa;;IAItB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,iBAAiB;IAK/C,iFAAiF;IACjF,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI7B,6EAA6E;IAC7E,SAAS,CAAC,CAAC,EACT,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,CAAC,EACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACjD,CAAC;IAOJ,sEAAsE;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAgCvB"}
@@ -0,0 +1,18 @@
1
+ /** At most one disposal owner per object, shared across one container tree. */
2
+ export declare class OwnershipRegistry {
3
+ #private;
4
+ /** Every container descending from `root` claims into the same registry. */
5
+ static forRoot(root: object): OwnershipRegistry;
6
+ /**
7
+ * Whether someone is still responsible for releasing `value`; an owner that
8
+ * already drained leaves it adoptable again. Rethrows a refused shape's
9
+ * cached rejection so every later alias of that object fails identically.
10
+ */
11
+ hasLiveOwner(value: object): boolean;
12
+ claim(value: object, owner: object): void;
13
+ /** `owner` finished its cleanup: every value it claimed is unowned again. */
14
+ release(owner: object): void;
15
+ /** Refusal is permanent: `value` can never gain a disposal owner afterwards. */
16
+ reject(value: object, error: unknown): void;
17
+ }
18
+ //# sourceMappingURL=ownership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownership.d.ts","sourceRoot":"","sources":["../../src/resources/ownership.ts"],"names":[],"mappings":"AASA,+EAA+E;AAC/E,qBAAa,iBAAiB;;IAI5B,4EAA4E;IAC5E,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB;IAS/C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IASpC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzC,6EAA6E;IAC7E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,gFAAgF;IAChF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;CAG5C"}
@@ -0,0 +1,17 @@
1
+ import type { Cleanup } from "./cleanup.js";
2
+ /** Runs one cleanup callback with the owner's ambient binding installed. */
3
+ export type CleanupInvoker = (cleanup: Cleanup) => unknown;
4
+ /**
5
+ * LIFO cleanup storage for one owner, drained once. Knows nothing about
6
+ * containers: every callback runs through the invoker its owner supplied.
7
+ */
8
+ export declare class DisposalQueue {
9
+ #private;
10
+ private readonly invoke;
11
+ constructor(invoke: CleanupInvoker);
12
+ /** Cleanup registered while draining is drained too; after that nothing would run it. */
13
+ defer(cleanup: Cleanup): void;
14
+ /** Drains in reverse registration order, retaining independent failures. */
15
+ close(): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/resources/queue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAE3D;;;GAGG;AACH,qBAAa,aAAa;;IAIZ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAEnD,yFAAyF;IACzF,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO7B,4EAA4E;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB7B"}
@@ -0,0 +1,14 @@
1
+ import type { Container } from "./container.js";
2
+ /** A zero-argument constructor usable as its own injection token. */
3
+ export type Constructor<T = object> = new (...args: never[]) => T;
4
+ /** An opaque token for values/interfaces that have no runtime class. */
5
+ export interface Token<T> {
6
+ readonly key: symbol;
7
+ /** Phantom carrier; never present at runtime. */
8
+ readonly _type?: T;
9
+ }
10
+ export type InjectionToken<T> = Constructor<T> | Token<T>;
11
+ export type Factory<T> = (container: Container) => T;
12
+ export declare function token<T>(description: string): Token<T>;
13
+ export declare function describeToken(token: InjectionToken<unknown>): string;
14
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,qEAAqE;AACrE,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAElE,wEAAwE;AACxE,MAAM,WAAW,KAAK,CAAC,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1D,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAErD,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEtD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAMpE"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@tiberjs/di",
3
+ "version": "0.1.0",
4
+ "description": "Hierarchical dependency container and resource ownership for tiberjs.",
5
+ "author": "miinhho",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "dependencies": {
22
+ "@tiberjs/runner": "^0.3.0"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/tiberjs/manager.git",
27
+ "directory": "packages/di"
28
+ },
29
+ "engines": {
30
+ "node": ">=24"
31
+ },
32
+ "scripts": {
33
+ "build": "rspack build --config ../../rspack.config.mjs && tsc -p tsconfig.build.json",
34
+ "typecheck": "tsc --noEmit",
35
+ "test": "pnpm -w exec vitest run --project @tiberjs/di"
36
+ }
37
+ }