@fougere/container 0.1.0-alpha.0 → 0.2.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/README.md +8 -3
- package/dist/container.d.ts +46 -13
- package/dist/container.d.ts.map +1 -1
- package/dist/create.d.ts +3 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +95 -0
- package/dist/create.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# @fougere/container
|
|
2
|
-
>
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
> Fougere's DI container
|
|
3
|
+
Resolution **by type**, never by parameter name: the AST scan reads the constructor
|
|
4
|
+
signature and wires what it asks for. Nested scopes, one per frond. Zero dependencies.
|
|
5
|
+
|
|
6
|
+
It is small on purpose. The scan already knows the graph — every class and every
|
|
7
|
+
dependency is read from source before boot — so there is almost nothing left to bind
|
|
8
|
+
late: a Map with a parent chain, a scope, and a resolver of last resort for a frond
|
|
9
|
+
that lives in another process.
|
|
5
10
|
|
|
6
11
|
## Installation
|
|
7
12
|
```bash
|
package/dist/container.d.ts
CHANGED
|
@@ -1,30 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A class constructor with any arguments.
|
|
3
3
|
*/
|
|
4
|
-
export type
|
|
4
|
+
export type Constructor<T = unknown> = new (...args: any[]) => T;
|
|
5
5
|
/**
|
|
6
6
|
* Registration options.
|
|
7
7
|
*/
|
|
8
8
|
export interface RegisterOptions {
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Lifetime of the resolved value. Default: 'transient'.
|
|
11
|
+
*
|
|
12
|
+
* Two words, because two are used: `Config` is a singleton, every handler,
|
|
13
|
+
* presenter, collector and provider is transient — a fresh instance per
|
|
14
|
+
* resolution, which is what keeps one call's state out of the next. A third,
|
|
15
|
+
* `'scoped'`, was declared and never passed by any caller: {@link Container.createScope}
|
|
16
|
+
* already answers "one instance per frond, per surface", and two mechanisms for
|
|
17
|
+
* one need is one too many.
|
|
18
|
+
*/
|
|
19
|
+
lifetime?: 'singleton' | 'transient';
|
|
11
20
|
/** Dependency type names for type-based resolution (from AST scan). */
|
|
12
21
|
deps?: string[];
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* What can be registered: a class or a resolver function.
|
|
24
|
+
* Anything holding a resource can say so, and disposing the container says it back.
|
|
25
|
+
*
|
|
26
|
+
* Not an interface a class implements — a shape a class happens to have. A handler
|
|
27
|
+
* that opens nothing declares nothing.
|
|
20
28
|
*/
|
|
21
|
-
export
|
|
29
|
+
export interface Disposable {
|
|
30
|
+
dispose(): void | Promise<void>;
|
|
31
|
+
}
|
|
22
32
|
/**
|
|
23
33
|
* DI container interface — the only thing application code sees.
|
|
34
|
+
*
|
|
35
|
+
* It is deliberately small, and the reason is that the scan already knows the graph:
|
|
36
|
+
* every class and every dependency is read from source before boot, so there is
|
|
37
|
+
* almost nothing left to bind late. What remains is a Map with a parent chain, plus
|
|
38
|
+
* the two gestures the scan cannot cover — {@link createScope} for isolation, and
|
|
39
|
+
* {@link setFallback} for a frond that lives in another process.
|
|
40
|
+
*
|
|
41
|
+
* Resolution is by name, and the names are TYPE names produced by the AST scan.
|
|
42
|
+
* The container never sees a type; "DI by type" is realized upstream, in the
|
|
43
|
+
* scanner, which is why this file mentions neither.
|
|
24
44
|
*/
|
|
25
45
|
export interface Container {
|
|
26
|
-
/** Register a
|
|
27
|
-
register<T>(name: string,
|
|
46
|
+
/** Register a class by name. Its `deps` are resolved from this container. */
|
|
47
|
+
register<T>(name: string, ctor: Constructor<T>, options?: RegisterOptions): void;
|
|
28
48
|
/** Register a pre-built value by name. */
|
|
29
49
|
registerValue<T>(name: string, value: T): void;
|
|
30
50
|
/** Resolve a dependency by name. */
|
|
@@ -44,7 +64,20 @@ export interface Container {
|
|
|
44
64
|
setFallback?(resolve: (name: string) => unknown): void;
|
|
45
65
|
/** Create a child scope. Inherits parent registrations. */
|
|
46
66
|
createScope(): Container;
|
|
47
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Dispose the container: every instance it KEPT — the singletons — that has a
|
|
69
|
+
* `dispose` method is told, most recent first, and awaited. A failure does not
|
|
70
|
+
* silence the rest; they travel out together in an `AggregateError`.
|
|
71
|
+
*
|
|
72
|
+
* This used to clear the registry and nothing else, while the doc above it promised
|
|
73
|
+
* disposal — so `await using app` (which routes here through `app[Symbol.asyncDispose]`)
|
|
74
|
+
* announced a cleanup that never happened. A container that holds instances is the
|
|
75
|
+
* only thing that knows they exist; if it stays silent, nobody else can speak.
|
|
76
|
+
*
|
|
77
|
+
* What it does NOT dispose: a transient, handed over and forgotten in the same
|
|
78
|
+
* breath, whose caller is the one who knows when it is done; and a value passed to
|
|
79
|
+
* {@link registerValue}, which the container did not build.
|
|
80
|
+
*/
|
|
48
81
|
dispose(): Promise<void>;
|
|
49
82
|
}
|
|
50
83
|
//# sourceMappingURL=container.d.ts.map
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IACrC,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAEjF,0CAA0C;IAC1C,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAE/C,oCAAoC;IACpC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;IAE5B,+DAA+D;IAC/D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3B;;;;;;;;;OASG;IACH,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IAEvD,2DAA2D;IAC3D,WAAW,IAAI,SAAS,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,gBAAgB,CAAC;AAsH1F,wBAAgB,eAAe,IAAI,SAAS,CAE3C"}
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const isDisposable = (value) => typeof value === 'object' && value !== null &&
|
|
2
|
+
typeof value.dispose === 'function';
|
|
3
|
+
function createScope(parent) {
|
|
4
|
+
const registry = new Map();
|
|
5
|
+
// Construction order, so disposal can run in reverse: a thing built later may
|
|
6
|
+
// hold something built earlier.
|
|
7
|
+
const built = [];
|
|
8
|
+
let fallback;
|
|
9
|
+
const remember = (value) => {
|
|
10
|
+
if (isDisposable(value))
|
|
11
|
+
built.push(value);
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
const container = {
|
|
15
|
+
register(name, ctor, options) {
|
|
16
|
+
const lifetime = options?.lifetime ?? 'transient';
|
|
17
|
+
const deps = options?.deps ?? [];
|
|
18
|
+
registry.set(name, {
|
|
19
|
+
factory: (c) => new ctor(...deps.map((d) => c.resolve(d))),
|
|
20
|
+
lifetime,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
registerValue(name, value) {
|
|
24
|
+
// A value the container did not build is not the container's to dispose.
|
|
25
|
+
registry.set(name, { factory: () => value, lifetime: 'singleton', instance: value });
|
|
26
|
+
},
|
|
27
|
+
resolve(name) {
|
|
28
|
+
let entry = registry.get(name);
|
|
29
|
+
// Not found locally — the parent holds it, and holds its instance too.
|
|
30
|
+
if (!entry && parent && parent._getEntry(name)) {
|
|
31
|
+
return parent.resolve(name);
|
|
32
|
+
}
|
|
33
|
+
// Nobody holds it. Before failing, ask whoever set a last resort — a frond declared
|
|
34
|
+
// in `remotes` registers nothing here, so its façade is fabricated rather than found.
|
|
35
|
+
if (!entry) {
|
|
36
|
+
const made = container._getFallback()?.(name);
|
|
37
|
+
if (made !== undefined) {
|
|
38
|
+
registry.set(name, { factory: () => made, lifetime: 'singleton', instance: made });
|
|
39
|
+
return made;
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`[container] '${name}' is not registered`);
|
|
42
|
+
}
|
|
43
|
+
if (entry.instance !== undefined)
|
|
44
|
+
return entry.instance;
|
|
45
|
+
const value = entry.factory(container);
|
|
46
|
+
// The container disposes what it KEEPS. A transient is handed over and
|
|
47
|
+
// forgotten in the same breath — remembering it would be a leak that grows
|
|
48
|
+
// once per call, and its caller is the one who knows when it is done.
|
|
49
|
+
if (entry.lifetime === 'singleton') {
|
|
50
|
+
entry.instance = value;
|
|
51
|
+
remember(value);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
},
|
|
55
|
+
has(name) {
|
|
56
|
+
return registry.has(name) || (parent?.has(name) ?? false);
|
|
57
|
+
},
|
|
58
|
+
createScope() {
|
|
59
|
+
return createScope(container);
|
|
60
|
+
},
|
|
61
|
+
async dispose() {
|
|
62
|
+
// Reverse order, and one failure must not silence the rest: everything gets
|
|
63
|
+
// told, then the errors travel together.
|
|
64
|
+
const failures = [];
|
|
65
|
+
for (const value of built.reverse()) {
|
|
66
|
+
try {
|
|
67
|
+
await value.dispose();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
failures.push(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
built.length = 0;
|
|
74
|
+
registry.clear();
|
|
75
|
+
if (failures.length > 0) {
|
|
76
|
+
throw new AggregateError(failures, '[container] one or more disposals failed');
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
setFallback(resolve) {
|
|
80
|
+
fallback = resolve;
|
|
81
|
+
},
|
|
82
|
+
_getEntry(name) {
|
|
83
|
+
return registry.get(name) ?? parent?._getEntry(name);
|
|
84
|
+
},
|
|
85
|
+
/** Set on the root, honoured from any scope — a scope inherits it by asking upward. */
|
|
86
|
+
_getFallback() {
|
|
87
|
+
return fallback ?? parent?._getFallback();
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
return container;
|
|
91
|
+
}
|
|
92
|
+
export function createContainer() {
|
|
93
|
+
return createScope();
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { type Container, type
|
|
1
|
+
export { type Container, type RegisterOptions, type Constructor, type Disposable, } from './container.js';
|
|
2
|
+
export { createContainer } from './create.js';
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { createContainer } from './create.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/container",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Fougere's DI container
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
|
+
"description": "Fougere's DI container: resolution by type, wired from the AST scan. Zero dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fougere",
|
|
7
|
+
"typescript",
|
|
8
|
+
"dependency-injection",
|
|
9
|
+
"ioc",
|
|
10
|
+
"container"
|
|
11
|
+
],
|
|
5
12
|
"license": "MIT",
|
|
6
13
|
"repository": {
|
|
7
14
|
"type": "git",
|
|
@@ -21,11 +28,16 @@
|
|
|
21
28
|
"files": [
|
|
22
29
|
"dist"
|
|
23
30
|
],
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"vitest": "^4.1.0"
|
|
33
|
+
},
|
|
24
34
|
"publishConfig": {
|
|
25
35
|
"access": "public"
|
|
26
36
|
},
|
|
27
37
|
"scripts": {
|
|
28
38
|
"build": "rm -rf dist && tsc",
|
|
29
|
-
"
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"typecheck": "tsc --noEmit -p tsconfig.test.json"
|
|
30
42
|
}
|
|
31
43
|
}
|