@fougere/container 0.8.3-alpha.0 → 0.9.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.
@@ -1,14 +1,15 @@
1
- /**
2
- * A class constructor with any arguments.
3
- */
4
1
  export type Constructor<T = unknown> = new (...args: any[]) => T;
5
- /**
6
- * Registration options.
7
- */
8
2
  export interface RegisterOptions {
9
- /** Lifetime of the resolved value. */
3
+ /**
4
+ * `'singleton'` builds once per scope and the container disposes it; `'transient'`
5
+ * builds per `resolve` and the caller closes it. Absent means `'transient'`.
6
+ */
10
7
  lifetime?: 'singleton' | 'transient';
11
- /** Dependency type names for type-based resolution (from AST scan). */
8
+ /**
9
+ * The TYPE names of the constructor's parameters, resolved in this scope in order —
10
+ * `constructor(private users: UserRepository, private log: Logger)` is
11
+ * `deps: ['UserRepository', 'Logger']`.
12
+ */
12
13
  deps?: string[];
13
14
  }
14
15
  /** Anything holding a resource can say so, and disposing the container says it back. */
@@ -17,19 +18,19 @@ export interface Disposable {
17
18
  }
18
19
  /** DI container interface — the only thing application code sees. */
19
20
  export interface Container {
20
- /** Register a class by name. Its `deps` are resolved from this container. */
21
+ /** The container builds it: `register('UserService', UserService, { deps: ['UserRepository'] })`. */
21
22
  register<T>(name: string, ctor: Constructor<T>, options?: RegisterOptions): void;
22
- /** Register a pre-built value by name. */
23
+ /** Already built, so never disposed here — `registerValue('Logger', new Logger('app'))`, a scope, a config. */
23
24
  registerValue<T>(name: string, value: T): void;
24
- /** Resolve a dependency by name. */
25
+ /** Throws when nothing answers the name; `has` asks without throwing. */
25
26
  resolve<T>(name: string): T;
26
- /** Check if a name is registered (including parent scopes). */
27
+ /** Answers for this scope and its parents, and builds nothing. */
27
28
  has(name: string): boolean;
28
29
  /** A resolver of last resort, consulted when no scope holds the name. */
29
30
  setFallback?(resolve: (name: string) => unknown): void;
30
- /** Create a child scope. Inherits parent registrations. */
31
+ /** The child reads every registration above it; the parent closes it. */
31
32
  createScope(): Container;
32
- /** Dispose the container. */
33
+ /** Children first, then what this scope built, in reverse — refusals travel as one `AggregateError`. */
33
34
  dispose(): Promise<void>;
34
35
  }
35
36
  //# sourceMappingURL=container.d.ts.map
@@ -1 +1 @@
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,sCAAsC;IACtC,QAAQ,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IACrC,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,qEAAqE;AACrE,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,yEAAyE;IACzE,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IAEvD,2DAA2D;IAC3D,WAAW,IAAI,SAAS,CAAC;IAEzB,6BAA6B;IAC7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IAErC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,qGAAqG;IACrG,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAEjF,+GAA+G;IAC/G,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAE/C,yEAAyE;IACzE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;IAE5B,kEAAkE;IAClE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3B,yEAAyE;IACzE,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IAEvD,yEAAyE;IACzE,WAAW,IAAI,SAAS,CAAC;IAEzB,wGAAwG;IACxG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,gBAAgB,CAAC;AAiJ1F,wBAAgB,eAAe,IAAI,SAAS,CAE3C"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,gBAAgB,CAAC;AA4I1F,wBAAgB,eAAe,IAAI,SAAS,CAE3C"}
package/dist/create.js CHANGED
@@ -2,14 +2,11 @@ const isDisposable = (value) => typeof value === 'object' && value !== null &&
2
2
  typeof value.dispose === 'function';
3
3
  function createScope(parent) {
4
4
  const registry = new Map();
5
- // Construction order, so disposal can run in reverse: a thing built later may
6
- // hold something built earlier.
5
+ // In construction order: a thing built later may hold one built earlier, so disposal
6
+ // walks this backwards.
7
7
  const built = [];
8
- // The scopes opened from this one. A child is built BY this container, so it is this
9
- // container's to close and it is closed first, because it may hold what the parent
10
- // built while the parent holds nothing of its. Without this a frond's scope, which is
11
- // where every provider lives, was never disposed at all: it is registered as a VALUE
12
- // under `frond:<name>`, and a value is not the container's to dispose.
8
+ // Closed by this container, and before `built` a child may hold what this scope built,
9
+ // never the other way round.
13
10
  const children = [];
14
11
  let fallback;
15
12
  const remember = (value) => {
@@ -36,8 +33,8 @@ function createScope(parent) {
36
33
  if (!entry && parent && parent._getEntry(name)) {
37
34
  return parent.resolve(name);
38
35
  }
39
- // Nobody holds it. Before failing, ask whoever set a last resort a frond declared
40
- // in `remotes` registers nothing here, so its façade is fabricated rather than found.
36
+ // Nobody holds it. A frond declared in `remotes` registers nothing here, so its
37
+ // façade is fabricated by the fallback rather than found.
41
38
  if (!entry) {
42
39
  const made = container._getFallback()?.(name);
43
40
  if (made !== undefined) {
@@ -49,9 +46,8 @@ function createScope(parent) {
49
46
  if (entry.instance !== undefined)
50
47
  return entry.instance;
51
48
  const value = entry.factory(container);
52
- // The container disposes what it KEEPS. A transient is handed over and
53
- // forgotten in the same breath remembering it would be a leak that grows
54
- // once per call, and its caller is the one who knows when it is done.
49
+ // The container disposes what it KEEPS: a transient is handed over and forgotten,
50
+ // and its caller is the one who knows when it is done.
55
51
  if (entry.lifetime === 'singleton') {
56
52
  entry.instance = value;
57
53
  remember(value);
@@ -67,15 +63,14 @@ function createScope(parent) {
67
63
  return child;
68
64
  },
69
65
  async dispose() {
70
- // Reverse order, and one failure must not silence the rest: everything gets
71
- // told, then the errors travel together.
72
- // Its parent kept a reference so it could close this scope; the scope closing itself
73
- // makes that reference garbage. Nothing created a scope at RUN time until frames did,
74
- // so the list only ever grew at boot and stayed bounded — one per request, or one per
75
- // transaction, and it grows for the life of the process.
66
+ // Everything is told before anything throws, then the failures travel together.
67
+ // Dropped from the parent first: a scope that closes itself leaves a reference the
68
+ // parent would hold for the life of the process.
76
69
  parent?._forget(container);
77
70
  const failures = [];
78
- for (const child of children.reverse()) {
71
+ // A copy: closing a child splices it out of `children`, so walking the array
72
+ // itself stepped over every second sibling.
73
+ for (const child of [...children].reverse()) {
79
74
  try {
80
75
  await child.dispose();
81
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { type Container, type RegisterOptions, type Constructor, type Disposable, } from './container.js';
1
+ export { type Container, type Constructor, } from './container.js';
2
2
  export { createContainer } from './create.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/container",
3
- "version": "0.8.3-alpha.0",
3
+ "version": "0.9.0-alpha.0",
4
4
  "description": "Fougere's DI container: resolution by type, wired from the AST scan. Zero dependencies.",
5
5
  "keywords": [
6
6
  "fougere",
package/src/container.ts CHANGED
@@ -1,16 +1,18 @@
1
- /**
2
- * A class constructor with any arguments.
3
- */
4
1
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
2
  export type Constructor<T = unknown> = new (...args: any[]) => T;
6
3
 
7
- /**
8
- * Registration options.
9
- */
10
4
  export interface RegisterOptions {
11
- /** Lifetime of the resolved value. */
5
+ /**
6
+ * `'singleton'` builds once per scope and the container disposes it; `'transient'`
7
+ * builds per `resolve` and the caller closes it. Absent means `'transient'`.
8
+ */
12
9
  lifetime?: 'singleton' | 'transient';
13
- /** Dependency type names for type-based resolution (from AST scan). */
10
+
11
+ /**
12
+ * The TYPE names of the constructor's parameters, resolved in this scope in order —
13
+ * `constructor(private users: UserRepository, private log: Logger)` is
14
+ * `deps: ['UserRepository', 'Logger']`.
15
+ */
14
16
  deps?: string[];
15
17
  }
16
18
 
@@ -21,24 +23,24 @@ export interface Disposable {
21
23
 
22
24
  /** DI container interface — the only thing application code sees. */
23
25
  export interface Container {
24
- /** Register a class by name. Its `deps` are resolved from this container. */
26
+ /** The container builds it: `register('UserService', UserService, { deps: ['UserRepository'] })`. */
25
27
  register<T>(name: string, ctor: Constructor<T>, options?: RegisterOptions): void;
26
28
 
27
- /** Register a pre-built value by name. */
29
+ /** Already built, so never disposed here — `registerValue('Logger', new Logger('app'))`, a scope, a config. */
28
30
  registerValue<T>(name: string, value: T): void;
29
31
 
30
- /** Resolve a dependency by name. */
32
+ /** Throws when nothing answers the name; `has` asks without throwing. */
31
33
  resolve<T>(name: string): T;
32
34
 
33
- /** Check if a name is registered (including parent scopes). */
35
+ /** Answers for this scope and its parents, and builds nothing. */
34
36
  has(name: string): boolean;
35
37
 
36
38
  /** A resolver of last resort, consulted when no scope holds the name. */
37
39
  setFallback?(resolve: (name: string) => unknown): void;
38
40
 
39
- /** Create a child scope. Inherits parent registrations. */
41
+ /** The child reads every registration above it; the parent closes it. */
40
42
  createScope(): Container;
41
43
 
42
- /** Dispose the container. */
44
+ /** Children first, then what this scope built, in reverse — refusals travel as one `AggregateError`. */
43
45
  dispose(): Promise<void>;
44
46
  }
package/src/create.ts CHANGED
@@ -18,14 +18,11 @@ const isDisposable = (value: unknown): value is Disposable =>
18
18
 
19
19
  function createScope(parent?: ScopeContainer): ScopeContainer {
20
20
  const registry = new Map<string, Entry>();
21
- // Construction order, so disposal can run in reverse: a thing built later may
22
- // hold something built earlier.
21
+ // In construction order: a thing built later may hold one built earlier, so disposal
22
+ // walks this backwards.
23
23
  const built: unknown[] = [];
24
- // The scopes opened from this one. A child is built BY this container, so it is this
25
- // container's to close and it is closed first, because it may hold what the parent
26
- // built while the parent holds nothing of its. Without this a frond's scope, which is
27
- // where every provider lives, was never disposed at all: it is registered as a VALUE
28
- // under `frond:<name>`, and a value is not the container's to dispose.
24
+ // Closed by this container, and before `built` a child may hold what this scope built,
25
+ // never the other way round.
29
26
  const children: ScopeContainer[] = [];
30
27
  let fallback: ((name: string) => unknown) | undefined;
31
28
 
@@ -57,8 +54,8 @@ function createScope(parent?: ScopeContainer): ScopeContainer {
57
54
  return parent.resolve<T>(name);
58
55
  }
59
56
 
60
- // Nobody holds it. Before failing, ask whoever set a last resort a frond declared
61
- // in `remotes` registers nothing here, so its façade is fabricated rather than found.
57
+ // Nobody holds it. A frond declared in `remotes` registers nothing here, so its
58
+ // façade is fabricated by the fallback rather than found.
62
59
  if (!entry) {
63
60
  const made = container._getFallback()?.(name);
64
61
  if (made !== undefined) {
@@ -70,9 +67,8 @@ function createScope(parent?: ScopeContainer): ScopeContainer {
70
67
 
71
68
  if (entry.instance !== undefined) return entry.instance as T;
72
69
  const value = entry.factory(container) as T;
73
- // The container disposes what it KEEPS. A transient is handed over and
74
- // forgotten in the same breath remembering it would be a leak that grows
75
- // once per call, and its caller is the one who knows when it is done.
70
+ // The container disposes what it KEEPS: a transient is handed over and forgotten,
71
+ // and its caller is the one who knows when it is done.
76
72
  if (entry.lifetime === 'singleton') {
77
73
  entry.instance = value;
78
74
  remember(value);
@@ -91,15 +87,14 @@ function createScope(parent?: ScopeContainer): ScopeContainer {
91
87
  },
92
88
 
93
89
  async dispose(): Promise<void> {
94
- // Reverse order, and one failure must not silence the rest: everything gets
95
- // told, then the errors travel together.
96
- // Its parent kept a reference so it could close this scope; the scope closing itself
97
- // makes that reference garbage. Nothing created a scope at RUN time until frames did,
98
- // so the list only ever grew at boot and stayed bounded — one per request, or one per
99
- // transaction, and it grows for the life of the process.
90
+ // Everything is told before anything throws, then the failures travel together.
91
+ // Dropped from the parent first: a scope that closes itself leaves a reference the
92
+ // parent would hold for the life of the process.
100
93
  parent?._forget(container);
101
94
  const failures: unknown[] = [];
102
- for (const child of children.reverse()) {
95
+ // A copy: closing a child splices it out of `children`, so walking the array
96
+ // itself stepped over every second sibling.
97
+ for (const child of [...children].reverse()) {
103
98
  try {
104
99
  await child.dispose();
105
100
  } catch (error) {
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export {
2
2
  type Container,
3
- type RegisterOptions,
4
3
  type Constructor,
5
- type Disposable,
6
4
  } from './container.js';
5
+
7
6
  export { createContainer } from './create.js';