@orieljs/container 0.1.0-alpha.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/LICENSE +21 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +211 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 meeeet-dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Container as Container$1, Factory, Abstract, Concrete, OrielError } from '@orieljs/contracts';
|
|
2
|
+
|
|
3
|
+
type Key = unknown;
|
|
4
|
+
type Lifetime = 'transient' | 'singleton' | 'scoped';
|
|
5
|
+
interface Binding {
|
|
6
|
+
lifetime: Lifetime;
|
|
7
|
+
factory: Factory<unknown>;
|
|
8
|
+
}
|
|
9
|
+
type Extender = (instance: unknown, c: Container$1) => unknown;
|
|
10
|
+
type Callback = (instance: unknown, c: Container$1) => void;
|
|
11
|
+
interface SharedState {
|
|
12
|
+
bindings: Map<Key, Binding>;
|
|
13
|
+
extenders: Map<Key, Extender[]>;
|
|
14
|
+
resolving: Map<Key, Callback[]>;
|
|
15
|
+
resolved: Set<Key>;
|
|
16
|
+
}
|
|
17
|
+
declare class Container implements Container$1 {
|
|
18
|
+
#private;
|
|
19
|
+
static getInstance(): Container | undefined;
|
|
20
|
+
static setInstance(container: Container | undefined): void;
|
|
21
|
+
protected readonly shared: SharedState;
|
|
22
|
+
protected readonly root: Container;
|
|
23
|
+
protected readonly parent: Container | undefined;
|
|
24
|
+
protected readonly local: Map<unknown, unknown>;
|
|
25
|
+
constructor(parent?: Container);
|
|
26
|
+
bind<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void;
|
|
27
|
+
singleton<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void;
|
|
28
|
+
scoped<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void;
|
|
29
|
+
instance<T>(abstract: Abstract<T>, instance: T): T;
|
|
30
|
+
make<T>(abstract: Abstract<T>): T;
|
|
31
|
+
bound(abstract: Abstract<unknown>): boolean;
|
|
32
|
+
resolved(abstract: Abstract<unknown>): boolean;
|
|
33
|
+
extend<T>(abstract: Abstract<T>, extender: (instance: T, container: Container$1) => T): void;
|
|
34
|
+
resolving<T>(abstract: Abstract<T>, callback: (instance: T, container: Container$1) => void): void;
|
|
35
|
+
forgetInstance(abstract: Abstract<unknown>): void;
|
|
36
|
+
/** Scopes are always plain Containers (never subclasses such as Application). */
|
|
37
|
+
createScope(): Container;
|
|
38
|
+
run<R>(fn: () => R): R;
|
|
39
|
+
protected build(abstract: Abstract<unknown>, key: Key, binding: Binding): unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
declare class BindingResolutionError extends Error implements OrielError {
|
|
43
|
+
readonly kind: "BindingResolution";
|
|
44
|
+
readonly name = "BindingResolutionError";
|
|
45
|
+
}
|
|
46
|
+
declare class CircularDependencyError extends Error implements OrielError {
|
|
47
|
+
readonly chain: string[];
|
|
48
|
+
readonly kind: "CircularDependency";
|
|
49
|
+
readonly name = "CircularDependencyError";
|
|
50
|
+
constructor(chain: string[]);
|
|
51
|
+
}
|
|
52
|
+
declare class ContainerNotAvailableError extends Error implements OrielError {
|
|
53
|
+
readonly kind: "ContainerNotAvailable";
|
|
54
|
+
readonly name = "ContainerNotAvailableError";
|
|
55
|
+
constructor(what: string);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare function describe(abstract: Abstract<unknown>): string;
|
|
59
|
+
|
|
60
|
+
declare function currentContainer(): Container$1 | undefined;
|
|
61
|
+
declare function inject<T>(abstract: Abstract<T>): T;
|
|
62
|
+
|
|
63
|
+
export { BindingResolutionError, CircularDependencyError, Container, ContainerNotAvailableError, currentContainer, describe, inject };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// Container.ts
|
|
2
|
+
import { Container as ContainerToken } from "@orieljs/contracts";
|
|
3
|
+
|
|
4
|
+
// context.ts
|
|
5
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
6
|
+
|
|
7
|
+
// describe.ts
|
|
8
|
+
function describe(abstract) {
|
|
9
|
+
return typeof abstract === "function" ? abstract.name || "<anonymous class>" : abstract.description;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// errors.ts
|
|
13
|
+
var BindingResolutionError = class extends Error {
|
|
14
|
+
kind = "BindingResolution";
|
|
15
|
+
name = "BindingResolutionError";
|
|
16
|
+
};
|
|
17
|
+
var CircularDependencyError = class extends Error {
|
|
18
|
+
constructor(chain) {
|
|
19
|
+
super(`Circular dependency detected: ${chain.join(" -> ")}`);
|
|
20
|
+
this.chain = chain;
|
|
21
|
+
}
|
|
22
|
+
chain;
|
|
23
|
+
kind = "CircularDependency";
|
|
24
|
+
name = "CircularDependencyError";
|
|
25
|
+
};
|
|
26
|
+
var ContainerNotAvailableError = class extends Error {
|
|
27
|
+
kind = "ContainerNotAvailable";
|
|
28
|
+
name = "ContainerNotAvailableError";
|
|
29
|
+
constructor(what) {
|
|
30
|
+
super(
|
|
31
|
+
`Cannot resolve [${what}]: no container is available. inject(), helpers and facades only work while the application is running (inside a request, job, command, provider or app.run()). Don't call them at module top level.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// context.ts
|
|
37
|
+
var ALS_KEY = /* @__PURE__ */ Symbol.for("oriel:container.als");
|
|
38
|
+
var GLOBAL_KEY = /* @__PURE__ */ Symbol.for("oriel:container.instance");
|
|
39
|
+
var BUILDING_KEY = /* @__PURE__ */ Symbol.for("oriel:container.building");
|
|
40
|
+
var g = globalThis;
|
|
41
|
+
var storage = g[ALS_KEY] ??= new AsyncLocalStorage();
|
|
42
|
+
var building = g[BUILDING_KEY] ??= {
|
|
43
|
+
keys: [],
|
|
44
|
+
names: [],
|
|
45
|
+
containers: []
|
|
46
|
+
};
|
|
47
|
+
function currentContainer() {
|
|
48
|
+
return building.containers.at(-1) ?? storage.getStore() ?? g[GLOBAL_KEY];
|
|
49
|
+
}
|
|
50
|
+
function inject(abstract) {
|
|
51
|
+
const container = currentContainer();
|
|
52
|
+
if (!container) throw new ContainerNotAvailableError(describe(abstract));
|
|
53
|
+
return container.make(abstract);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Container.ts
|
|
57
|
+
var globals = globalThis;
|
|
58
|
+
var keyOf = (abstract) => typeof abstract === "function" ? abstract : abstract.id;
|
|
59
|
+
var isClass = (fn) => typeof fn === "function" && /^class[\s{]/.test(Function.prototype.toString.call(fn));
|
|
60
|
+
var Container = class _Container {
|
|
61
|
+
static getInstance() {
|
|
62
|
+
return globals[GLOBAL_KEY];
|
|
63
|
+
}
|
|
64
|
+
static setInstance(container) {
|
|
65
|
+
globals[GLOBAL_KEY] = container;
|
|
66
|
+
}
|
|
67
|
+
shared;
|
|
68
|
+
root;
|
|
69
|
+
parent;
|
|
70
|
+
local = /* @__PURE__ */ new Map();
|
|
71
|
+
constructor(parent) {
|
|
72
|
+
this.parent = parent;
|
|
73
|
+
this.root = parent?.root ?? this;
|
|
74
|
+
this.shared = parent?.shared ?? { bindings: /* @__PURE__ */ new Map(), extenders: /* @__PURE__ */ new Map(), resolving: /* @__PURE__ */ new Map(), resolved: /* @__PURE__ */ new Set() };
|
|
75
|
+
this.local.set(keyOf(ContainerToken), this);
|
|
76
|
+
this.local.set(_Container, this);
|
|
77
|
+
}
|
|
78
|
+
bind(abstract, concrete) {
|
|
79
|
+
this.#assertRoot("bind");
|
|
80
|
+
this.#addBinding(abstract, concrete, "transient");
|
|
81
|
+
}
|
|
82
|
+
singleton(abstract, concrete) {
|
|
83
|
+
this.#assertRoot("singleton");
|
|
84
|
+
this.#addBinding(abstract, concrete, "singleton");
|
|
85
|
+
}
|
|
86
|
+
scoped(abstract, concrete) {
|
|
87
|
+
this.#assertRoot("scoped");
|
|
88
|
+
this.#addBinding(abstract, concrete, "scoped");
|
|
89
|
+
}
|
|
90
|
+
instance(abstract, instance) {
|
|
91
|
+
const key = keyOf(abstract);
|
|
92
|
+
this.local.set(key, instance);
|
|
93
|
+
if (this === this.root) this.shared.resolved.add(key);
|
|
94
|
+
return instance;
|
|
95
|
+
}
|
|
96
|
+
make(abstract) {
|
|
97
|
+
const key = keyOf(abstract);
|
|
98
|
+
const owner = this.#owning(key);
|
|
99
|
+
if (owner) return owner.local.get(key);
|
|
100
|
+
const binding = this.shared.bindings.get(key) ?? this.#implicitBinding(abstract);
|
|
101
|
+
if (binding.lifetime === "scoped" && this === this.root) {
|
|
102
|
+
throw new BindingResolutionError(
|
|
103
|
+
`Scoped binding [${describe(abstract)}] was resolved outside a scope. Resolve it inside app.createScope().run(...), and don't inject scoped services into singletons.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const builder = binding.lifetime === "singleton" ? this.root : this;
|
|
107
|
+
const instance = builder.build(abstract, key, binding);
|
|
108
|
+
if (binding.lifetime === "singleton") this.root.local.set(key, instance);
|
|
109
|
+
if (binding.lifetime === "scoped") this.local.set(key, instance);
|
|
110
|
+
return instance;
|
|
111
|
+
}
|
|
112
|
+
bound(abstract) {
|
|
113
|
+
const key = keyOf(abstract);
|
|
114
|
+
return this.shared.bindings.has(key) || this.#owning(key) !== void 0;
|
|
115
|
+
}
|
|
116
|
+
resolved(abstract) {
|
|
117
|
+
const key = keyOf(abstract);
|
|
118
|
+
return this.shared.resolved.has(key) || this.#owning(key) !== void 0;
|
|
119
|
+
}
|
|
120
|
+
extend(abstract, extender) {
|
|
121
|
+
this.#assertRoot("extend");
|
|
122
|
+
const key = keyOf(abstract);
|
|
123
|
+
const list = this.shared.extenders.get(key) ?? [];
|
|
124
|
+
list.push(extender);
|
|
125
|
+
this.shared.extenders.set(key, list);
|
|
126
|
+
if (this.local.has(key)) this.local.set(key, extender(this.local.get(key), this));
|
|
127
|
+
}
|
|
128
|
+
resolving(abstract, callback) {
|
|
129
|
+
this.#assertRoot("resolving");
|
|
130
|
+
const key = keyOf(abstract);
|
|
131
|
+
const list = this.shared.resolving.get(key) ?? [];
|
|
132
|
+
list.push(callback);
|
|
133
|
+
this.shared.resolving.set(key, list);
|
|
134
|
+
}
|
|
135
|
+
forgetInstance(abstract) {
|
|
136
|
+
this.local.delete(keyOf(abstract));
|
|
137
|
+
}
|
|
138
|
+
/** Scopes are always plain Containers (never subclasses such as Application). */
|
|
139
|
+
createScope() {
|
|
140
|
+
return new _Container(this);
|
|
141
|
+
}
|
|
142
|
+
run(fn) {
|
|
143
|
+
return storage.run(this, fn);
|
|
144
|
+
}
|
|
145
|
+
/** The nearest container in this -> parent scopes -> root that holds an instance for the key. */
|
|
146
|
+
#owning(key) {
|
|
147
|
+
if (this.local.has(key)) return this;
|
|
148
|
+
return this.parent === void 0 ? void 0 : this.parent.#owning(key);
|
|
149
|
+
}
|
|
150
|
+
/** Bindings, extenders and callbacks are app-wide state: a request scope must never write them. */
|
|
151
|
+
#assertRoot(method) {
|
|
152
|
+
if (this !== this.root) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Cannot register bindings on a request scope (${method}). Register them on the application in a service provider; use instance() for per-request values.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
#addBinding(abstract, concrete, lifetime) {
|
|
159
|
+
const key = keyOf(abstract);
|
|
160
|
+
let factory;
|
|
161
|
+
if (concrete === void 0) {
|
|
162
|
+
if (typeof abstract !== "function") throw new TypeError(`bind(${describe(abstract)}) needs a concrete: tokens can't be constructed`);
|
|
163
|
+
factory = () => new abstract();
|
|
164
|
+
} else if (isClass(concrete)) {
|
|
165
|
+
factory = () => new concrete();
|
|
166
|
+
} else {
|
|
167
|
+
factory = concrete;
|
|
168
|
+
}
|
|
169
|
+
this.shared.bindings.set(key, { lifetime, factory });
|
|
170
|
+
this.local.delete(key);
|
|
171
|
+
}
|
|
172
|
+
#implicitBinding(abstract) {
|
|
173
|
+
if (typeof abstract === "function") {
|
|
174
|
+
return { lifetime: "transient", factory: () => new abstract() };
|
|
175
|
+
}
|
|
176
|
+
throw new BindingResolutionError(
|
|
177
|
+
`Target [${abstract.description}] is not bound. Is its service provider registered in bootstrap/providers.ts?`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
build(abstract, key, binding) {
|
|
181
|
+
const stackIndex = building.keys.indexOf(key);
|
|
182
|
+
if (stackIndex !== -1) {
|
|
183
|
+
const chain = [...building.names.slice(stackIndex), describe(abstract)];
|
|
184
|
+
throw new CircularDependencyError(chain);
|
|
185
|
+
}
|
|
186
|
+
building.keys.push(key);
|
|
187
|
+
building.names.push(describe(abstract));
|
|
188
|
+
building.containers.push(this);
|
|
189
|
+
try {
|
|
190
|
+
let instance = binding.factory(this);
|
|
191
|
+
for (const extender of this.shared.extenders.get(key) ?? []) instance = extender(instance, this);
|
|
192
|
+
for (const callback of this.shared.resolving.get(key) ?? []) callback(instance, this);
|
|
193
|
+
this.shared.resolved.add(key);
|
|
194
|
+
return instance;
|
|
195
|
+
} finally {
|
|
196
|
+
building.keys.pop();
|
|
197
|
+
building.names.pop();
|
|
198
|
+
building.containers.pop();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
export {
|
|
203
|
+
BindingResolutionError,
|
|
204
|
+
CircularDependencyError,
|
|
205
|
+
Container,
|
|
206
|
+
ContainerNotAvailableError,
|
|
207
|
+
currentContainer,
|
|
208
|
+
describe,
|
|
209
|
+
inject
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../Container.ts","../context.ts","../describe.ts","../errors.ts"],"sourcesContent":["import { Container as ContainerToken, type Abstract, type Concrete, type Container as ContainerContract, type Factory } from '@orieljs/contracts'\nimport { building, GLOBAL_KEY, storage } from './context'\nimport { describe } from './describe'\nimport { BindingResolutionError, CircularDependencyError } from './errors'\n\ntype Key = unknown\ntype Lifetime = 'transient' | 'singleton' | 'scoped'\ninterface Binding { lifetime: Lifetime; factory: Factory<unknown> }\ntype Extender = (instance: unknown, c: ContainerContract) => unknown\ntype Callback = (instance: unknown, c: ContainerContract) => void\n\ninterface SharedState {\n bindings: Map<Key, Binding>\n extenders: Map<Key, Extender[]>\n resolving: Map<Key, Callback[]>\n resolved: Set<Key>\n}\n\nconst globals = globalThis as unknown as Record<symbol, Container | undefined>\n\nconst keyOf = (abstract: Abstract<unknown>): Key => (typeof abstract === 'function' ? abstract : abstract.id)\nconst isClass = (fn: unknown): fn is new () => unknown =>\n typeof fn === 'function' && /^class[\\s{]/.test(Function.prototype.toString.call(fn))\n\nexport class Container implements ContainerContract {\n static getInstance(): Container | undefined {\n return globals[GLOBAL_KEY]\n }\n static setInstance(container: Container | undefined): void {\n globals[GLOBAL_KEY] = container\n }\n\n protected readonly shared: SharedState\n protected readonly root: Container\n protected readonly parent: Container | undefined\n protected readonly local = new Map<Key, unknown>()\n\n constructor(parent?: Container) {\n this.parent = parent\n this.root = parent?.root ?? this\n this.shared = parent?.shared ?? { bindings: new Map(), extenders: new Map(), resolving: new Map(), resolved: new Set() }\n // Every container (root or scope) resolves \"the container\" as itself, by token and by class.\n this.local.set(keyOf(ContainerToken), this)\n this.local.set(Container, this)\n }\n\n bind<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void {\n this.#assertRoot('bind')\n this.#addBinding(abstract, concrete, 'transient')\n }\n singleton<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void {\n this.#assertRoot('singleton')\n this.#addBinding(abstract, concrete, 'singleton')\n }\n scoped<T>(abstract: Abstract<T>, concrete?: Concrete<T>): void {\n this.#assertRoot('scoped')\n this.#addBinding(abstract, concrete, 'scoped')\n }\n\n instance<T>(abstract: Abstract<T>, instance: T): T {\n const key = keyOf(abstract)\n this.local.set(key, instance)\n // A scope's instances are its own business: only the root records them in the shared resolved set.\n if (this === this.root) this.shared.resolved.add(key)\n return instance\n }\n\n make<T>(abstract: Abstract<T>): T {\n const key = keyOf(abstract)\n const owner = this.#owning(key)\n if (owner) return owner.local.get(key) as T\n\n const binding = this.shared.bindings.get(key) ?? this.#implicitBinding(abstract)\n if (binding.lifetime === 'scoped' && this === this.root) {\n throw new BindingResolutionError(\n `Scoped binding [${describe(abstract)}] was resolved outside a scope. Resolve it inside app.createScope().run(...), and don't inject scoped services into singletons.`,\n )\n }\n const builder = binding.lifetime === 'singleton' ? this.root : this\n const instance = builder.build(abstract, key, binding)\n\n if (binding.lifetime === 'singleton') this.root.local.set(key, instance)\n if (binding.lifetime === 'scoped') this.local.set(key, instance)\n return instance as T\n }\n\n bound(abstract: Abstract<unknown>): boolean {\n const key = keyOf(abstract)\n return this.shared.bindings.has(key) || this.#owning(key) !== undefined\n }\n\n resolved(abstract: Abstract<unknown>): boolean {\n const key = keyOf(abstract)\n return this.shared.resolved.has(key) || this.#owning(key) !== undefined\n }\n\n extend<T>(abstract: Abstract<T>, extender: (instance: T, container: ContainerContract) => T): void {\n this.#assertRoot('extend')\n const key = keyOf(abstract)\n const list = this.shared.extenders.get(key) ?? []\n list.push(extender as Extender)\n this.shared.extenders.set(key, list)\n if (this.local.has(key)) this.local.set(key, extender(this.local.get(key) as T, this))\n }\n\n resolving<T>(abstract: Abstract<T>, callback: (instance: T, container: ContainerContract) => void): void {\n this.#assertRoot('resolving')\n const key = keyOf(abstract)\n const list = this.shared.resolving.get(key) ?? []\n list.push(callback as Callback)\n this.shared.resolving.set(key, list)\n }\n\n forgetInstance(abstract: Abstract<unknown>): void {\n this.local.delete(keyOf(abstract))\n }\n\n /** Scopes are always plain Containers (never subclasses such as Application). */\n createScope(): Container {\n return new Container(this)\n }\n\n run<R>(fn: () => R): R {\n return storage.run(this, fn)\n }\n\n /** The nearest container in this -> parent scopes -> root that holds an instance for the key. */\n #owning(key: Key): Container | undefined {\n if (this.local.has(key)) return this\n return this.parent === undefined ? undefined : this.parent.#owning(key)\n }\n\n /** Bindings, extenders and callbacks are app-wide state: a request scope must never write them. */\n #assertRoot(method: string): void {\n if (this !== this.root) {\n throw new Error(\n `Cannot register bindings on a request scope (${method}). Register them on the application in a service provider; use instance() for per-request values.`,\n )\n }\n }\n\n #addBinding(abstract: Abstract<unknown>, concrete: Concrete<unknown> | undefined, lifetime: Lifetime): void {\n const key = keyOf(abstract)\n let factory: Factory<unknown>\n if (concrete === undefined) {\n if (typeof abstract !== 'function') throw new TypeError(`bind(${describe(abstract)}) needs a concrete: tokens can't be constructed`)\n factory = () => new (abstract as new () => unknown)()\n } else if (isClass(concrete)) {\n factory = () => new concrete()\n } else {\n factory = concrete as Factory<unknown>\n }\n this.shared.bindings.set(key, { lifetime, factory })\n this.local.delete(key)\n }\n\n #implicitBinding(abstract: Abstract<unknown>): Binding {\n if (typeof abstract === 'function') {\n return { lifetime: 'transient', factory: () => new (abstract as new () => unknown)() }\n }\n throw new BindingResolutionError(\n `Target [${abstract.description}] is not bound. Is its service provider registered in bootstrap/providers.ts?`,\n )\n }\n\n protected build(abstract: Abstract<unknown>, key: Key, binding: Binding): unknown {\n const stackIndex = building.keys.indexOf(key)\n if (stackIndex !== -1) {\n const chain = [...building.names.slice(stackIndex), describe(abstract)]\n throw new CircularDependencyError(chain)\n }\n building.keys.push(key)\n building.names.push(describe(abstract))\n building.containers.push(this)\n try {\n let instance = binding.factory(this)\n for (const extender of this.shared.extenders.get(key) ?? []) instance = extender(instance, this)\n for (const callback of this.shared.resolving.get(key) ?? []) callback(instance, this)\n this.shared.resolved.add(key)\n return instance\n } finally {\n building.keys.pop()\n building.names.pop()\n building.containers.pop()\n }\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { Abstract, Container } from '@orieljs/contracts'\nimport { describe } from './describe'\nimport { ContainerNotAvailableError } from './errors'\n\nconst ALS_KEY = Symbol.for('oriel:container.als')\nexport const GLOBAL_KEY = Symbol.for('oriel:container.instance')\nconst BUILDING_KEY = Symbol.for('oriel:container.building')\nconst g = globalThis as unknown as Record<symbol, unknown>\n\n/** One AsyncLocalStorage per process, even if @orieljs/container is installed twice. */\nexport const storage: AsyncLocalStorage<Container> = (g[ALS_KEY] ??= new AsyncLocalStorage<Container>()) as AsyncLocalStorage<Container>\n\n/** Synchronous build stack. Shared through globalThis so duplicate package copies share the same stack. */\nexport const building = (g[BUILDING_KEY] ??= {\n keys: [] as unknown[],\n names: [] as string[],\n containers: [] as Container[],\n}) as { keys: unknown[]; names: string[]; containers: Container[] }\n\nexport function currentContainer(): Container | undefined {\n return building.containers.at(-1) ?? storage.getStore() ?? (g[GLOBAL_KEY] as Container | undefined)\n}\n\nexport function inject<T>(abstract: Abstract<T>): T {\n const container = currentContainer()\n if (!container) throw new ContainerNotAvailableError(describe(abstract))\n return container.make(abstract)\n}\n","import type { Abstract } from '@orieljs/contracts'\n\nexport function describe(abstract: Abstract<unknown>): string {\n return typeof abstract === 'function' ? abstract.name || '<anonymous class>' : abstract.description\n}\n","import type { OrielError } from '@orieljs/contracts'\n\nexport class BindingResolutionError extends Error implements OrielError {\n readonly kind = 'BindingResolution' as const\n override readonly name = 'BindingResolutionError'\n}\n\nexport class CircularDependencyError extends Error implements OrielError {\n readonly kind = 'CircularDependency' as const\n override readonly name = 'CircularDependencyError'\n constructor(readonly chain: string[]) {\n super(`Circular dependency detected: ${chain.join(' -> ')}`)\n }\n}\n\nexport class ContainerNotAvailableError extends Error implements OrielError {\n readonly kind = 'ContainerNotAvailable' as const\n override readonly name = 'ContainerNotAvailableError'\n constructor(what: string) {\n super(\n `Cannot resolve [${what}]: no container is available. inject(), helpers and facades only work while the ` +\n `application is running (inside a request, job, command, provider or app.run()). ` +\n `Don't call them at module top level.`,\n )\n }\n}\n"],"mappings":";AAAA,SAAS,aAAa,sBAAuG;;;ACA7H,SAAS,yBAAyB;;;ACE3B,SAAS,SAAS,UAAqC;AAC5D,SAAO,OAAO,aAAa,aAAa,SAAS,QAAQ,sBAAsB,SAAS;AAC1F;;;ACFO,IAAM,yBAAN,cAAqC,MAA4B;AAAA,EAC7D,OAAO;AAAA,EACE,OAAO;AAC3B;AAEO,IAAM,0BAAN,cAAsC,MAA4B;AAAA,EAGvE,YAAqB,OAAiB;AACpC,UAAM,iCAAiC,MAAM,KAAK,MAAM,CAAC,EAAE;AADxC;AAAA,EAErB;AAAA,EAFqB;AAAA,EAFZ,OAAO;AAAA,EACE,OAAO;AAI3B;AAEO,IAAM,6BAAN,cAAyC,MAA4B;AAAA,EACjE,OAAO;AAAA,EACE,OAAO;AAAA,EACzB,YAAY,MAAc;AACxB;AAAA,MACE,mBAAmB,IAAI;AAAA,IAGzB;AAAA,EACF;AACF;;;AFpBA,IAAM,UAAU,uBAAO,IAAI,qBAAqB;AACzC,IAAM,aAAa,uBAAO,IAAI,0BAA0B;AAC/D,IAAM,eAAe,uBAAO,IAAI,0BAA0B;AAC1D,IAAM,IAAI;AAGH,IAAM,UAAyC,EAAE,OAAO,MAAM,IAAI,kBAA6B;AAG/F,IAAM,WAAY,EAAE,YAAY,MAAM;AAAA,EAC3C,MAAM,CAAC;AAAA,EACP,OAAO,CAAC;AAAA,EACR,YAAY,CAAC;AACf;AAEO,SAAS,mBAA0C;AACxD,SAAO,SAAS,WAAW,GAAG,EAAE,KAAK,QAAQ,SAAS,KAAM,EAAE,UAAU;AAC1E;AAEO,SAAS,OAAU,UAA0B;AAClD,QAAM,YAAY,iBAAiB;AACnC,MAAI,CAAC,UAAW,OAAM,IAAI,2BAA2B,SAAS,QAAQ,CAAC;AACvE,SAAO,UAAU,KAAK,QAAQ;AAChC;;;ADVA,IAAM,UAAU;AAEhB,IAAM,QAAQ,CAAC,aAAsC,OAAO,aAAa,aAAa,WAAW,SAAS;AAC1G,IAAM,UAAU,CAAC,OACf,OAAO,OAAO,cAAc,cAAc,KAAK,SAAS,UAAU,SAAS,KAAK,EAAE,CAAC;AAE9E,IAAM,YAAN,MAAM,WAAuC;AAAA,EAClD,OAAO,cAAqC;AAC1C,WAAO,QAAQ,UAAU;AAAA,EAC3B;AAAA,EACA,OAAO,YAAY,WAAwC;AACzD,YAAQ,UAAU,IAAI;AAAA,EACxB;AAAA,EAEmB;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ,oBAAI,IAAkB;AAAA,EAEjD,YAAY,QAAoB;AAC9B,SAAK,SAAS;AACd,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,SAAS,QAAQ,UAAU,EAAE,UAAU,oBAAI,IAAI,GAAG,WAAW,oBAAI,IAAI,GAAG,WAAW,oBAAI,IAAI,GAAG,UAAU,oBAAI,IAAI,EAAE;AAEvH,SAAK,MAAM,IAAI,MAAM,cAAc,GAAG,IAAI;AAC1C,SAAK,MAAM,IAAI,YAAW,IAAI;AAAA,EAChC;AAAA,EAEA,KAAQ,UAAuB,UAA8B;AAC3D,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY,UAAU,UAAU,WAAW;AAAA,EAClD;AAAA,EACA,UAAa,UAAuB,UAA8B;AAChE,SAAK,YAAY,WAAW;AAC5B,SAAK,YAAY,UAAU,UAAU,WAAW;AAAA,EAClD;AAAA,EACA,OAAU,UAAuB,UAA8B;AAC7D,SAAK,YAAY,QAAQ;AACzB,SAAK,YAAY,UAAU,UAAU,QAAQ;AAAA,EAC/C;AAAA,EAEA,SAAY,UAAuB,UAAgB;AACjD,UAAM,MAAM,MAAM,QAAQ;AAC1B,SAAK,MAAM,IAAI,KAAK,QAAQ;AAE5B,QAAI,SAAS,KAAK,KAAM,MAAK,OAAO,SAAS,IAAI,GAAG;AACpD,WAAO;AAAA,EACT;AAAA,EAEA,KAAQ,UAA0B;AAChC,UAAM,MAAM,MAAM,QAAQ;AAC1B,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,MAAO,QAAO,MAAM,MAAM,IAAI,GAAG;AAErC,UAAM,UAAU,KAAK,OAAO,SAAS,IAAI,GAAG,KAAK,KAAK,iBAAiB,QAAQ;AAC/E,QAAI,QAAQ,aAAa,YAAY,SAAS,KAAK,MAAM;AACvD,YAAM,IAAI;AAAA,QACR,mBAAmB,SAAS,QAAQ,CAAC;AAAA,MACvC;AAAA,IACF;AACA,UAAM,UAAU,QAAQ,aAAa,cAAc,KAAK,OAAO;AAC/D,UAAM,WAAW,QAAQ,MAAM,UAAU,KAAK,OAAO;AAErD,QAAI,QAAQ,aAAa,YAAa,MAAK,KAAK,MAAM,IAAI,KAAK,QAAQ;AACvE,QAAI,QAAQ,aAAa,SAAU,MAAK,MAAM,IAAI,KAAK,QAAQ;AAC/D,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAsC;AAC1C,UAAM,MAAM,MAAM,QAAQ;AAC1B,WAAO,KAAK,OAAO,SAAS,IAAI,GAAG,KAAK,KAAK,QAAQ,GAAG,MAAM;AAAA,EAChE;AAAA,EAEA,SAAS,UAAsC;AAC7C,UAAM,MAAM,MAAM,QAAQ;AAC1B,WAAO,KAAK,OAAO,SAAS,IAAI,GAAG,KAAK,KAAK,QAAQ,GAAG,MAAM;AAAA,EAChE;AAAA,EAEA,OAAU,UAAuB,UAAkE;AACjG,SAAK,YAAY,QAAQ;AACzB,UAAM,MAAM,MAAM,QAAQ;AAC1B,UAAM,OAAO,KAAK,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC;AAChD,SAAK,KAAK,QAAoB;AAC9B,SAAK,OAAO,UAAU,IAAI,KAAK,IAAI;AACnC,QAAI,KAAK,MAAM,IAAI,GAAG,EAAG,MAAK,MAAM,IAAI,KAAK,SAAS,KAAK,MAAM,IAAI,GAAG,GAAQ,IAAI,CAAC;AAAA,EACvF;AAAA,EAEA,UAAa,UAAuB,UAAqE;AACvG,SAAK,YAAY,WAAW;AAC5B,UAAM,MAAM,MAAM,QAAQ;AAC1B,UAAM,OAAO,KAAK,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC;AAChD,SAAK,KAAK,QAAoB;AAC9B,SAAK,OAAO,UAAU,IAAI,KAAK,IAAI;AAAA,EACrC;AAAA,EAEA,eAAe,UAAmC;AAChD,SAAK,MAAM,OAAO,MAAM,QAAQ,CAAC;AAAA,EACnC;AAAA;AAAA,EAGA,cAAyB;AACvB,WAAO,IAAI,WAAU,IAAI;AAAA,EAC3B;AAAA,EAEA,IAAO,IAAgB;AACrB,WAAO,QAAQ,IAAI,MAAM,EAAE;AAAA,EAC7B;AAAA;AAAA,EAGA,QAAQ,KAAiC;AACvC,QAAI,KAAK,MAAM,IAAI,GAAG,EAAG,QAAO;AAChC,WAAO,KAAK,WAAW,SAAY,SAAY,KAAK,OAAO,QAAQ,GAAG;AAAA,EACxE;AAAA;AAAA,EAGA,YAAY,QAAsB;AAChC,QAAI,SAAS,KAAK,MAAM;AACtB,YAAM,IAAI;AAAA,QACR,gDAAgD,MAAM;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY,UAA6B,UAAyC,UAA0B;AAC1G,UAAM,MAAM,MAAM,QAAQ;AAC1B,QAAI;AACJ,QAAI,aAAa,QAAW;AAC1B,UAAI,OAAO,aAAa,WAAY,OAAM,IAAI,UAAU,QAAQ,SAAS,QAAQ,CAAC,iDAAiD;AACnI,gBAAU,MAAM,IAAK,SAA+B;AAAA,IACtD,WAAW,QAAQ,QAAQ,GAAG;AAC5B,gBAAU,MAAM,IAAI,SAAS;AAAA,IAC/B,OAAO;AACL,gBAAU;AAAA,IACZ;AACA,SAAK,OAAO,SAAS,IAAI,KAAK,EAAE,UAAU,QAAQ,CAAC;AACnD,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AAAA,EAEA,iBAAiB,UAAsC;AACrD,QAAI,OAAO,aAAa,YAAY;AAClC,aAAO,EAAE,UAAU,aAAa,SAAS,MAAM,IAAK,SAA+B,EAAE;AAAA,IACvF;AACA,UAAM,IAAI;AAAA,MACR,WAAW,SAAS,WAAW;AAAA,IACjC;AAAA,EACF;AAAA,EAEU,MAAM,UAA6B,KAAU,SAA2B;AAChF,UAAM,aAAa,SAAS,KAAK,QAAQ,GAAG;AAC5C,QAAI,eAAe,IAAI;AACrB,YAAM,QAAQ,CAAC,GAAG,SAAS,MAAM,MAAM,UAAU,GAAG,SAAS,QAAQ,CAAC;AACtE,YAAM,IAAI,wBAAwB,KAAK;AAAA,IACzC;AACA,aAAS,KAAK,KAAK,GAAG;AACtB,aAAS,MAAM,KAAK,SAAS,QAAQ,CAAC;AACtC,aAAS,WAAW,KAAK,IAAI;AAC7B,QAAI;AACF,UAAI,WAAW,QAAQ,QAAQ,IAAI;AACnC,iBAAW,YAAY,KAAK,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC,EAAG,YAAW,SAAS,UAAU,IAAI;AAC/F,iBAAW,YAAY,KAAK,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC,EAAG,UAAS,UAAU,IAAI;AACpF,WAAK,OAAO,SAAS,IAAI,GAAG;AAC5B,aAAO;AAAA,IACT,UAAE;AACA,eAAS,KAAK,IAAI;AAClB,eAAS,MAAM,IAAI;AACnB,eAAS,WAAW,IAAI;AAAA,IAC1B;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orieljs/container",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "The Oriel IoC container: bindings, request scopes and inject().",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/orieljs/framework.git",
|
|
11
|
+
"directory": "src/Oriel/Container"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22"
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@orieljs/contracts": "0.1.0-alpha.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup"
|
|
34
|
+
}
|
|
35
|
+
}
|