@codefast/di 0.11.0 → 0.13.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/CHANGELOG.md +151 -0
- package/README.md +128 -31
- package/dist/container/{binding-builders.d.ts → binding-chain.d.ts} +21 -28
- package/dist/container/{binding-builders.js → binding-chain.js} +53 -163
- package/dist/container/container.d.ts +11 -3
- package/dist/container/container.js +176 -102
- package/dist/core/binding-builders.d.ts +117 -0
- package/dist/core/binding-declaration.d.ts +9 -2
- package/dist/core/binding-declaration.js +5 -4
- package/dist/core/binding.d.ts +21 -149
- package/dist/core/binding.js +15 -25
- package/dist/core/module.d.ts +1 -1
- package/dist/core/registry.d.ts +9 -24
- package/dist/core/registry.js +30 -155
- package/dist/core/tag.js +1 -1
- package/dist/decorators/inject.d.ts +3 -1
- package/dist/decorators/inject.js +5 -5
- package/dist/decorators/injectable.js +2 -2
- package/dist/decorators/lifecycle.d.ts +20 -0
- package/dist/decorators/{lifecycle-decorators.js → lifecycle.js} +7 -3
- package/dist/decorators/{decorator-metadata.js → metadata-record.js} +1 -1
- package/dist/{errors/errors.d.ts → errors.d.ts} +29 -19
- package/dist/{errors/errors.js → errors.js} +43 -25
- package/dist/index.d.ts +12 -10
- package/dist/index.js +6 -6
- package/dist/injection/descriptor.d.ts +1 -1
- package/dist/introspection/dependency-graph.d.ts +1 -1
- package/dist/introspection/dependency-graph.js +3 -4
- package/dist/introspection/explanation.d.ts +101 -0
- package/dist/introspection/explanation.js +169 -0
- package/dist/introspection/inspector.d.ts +9 -2
- package/dist/introspection/inspector.js +25 -20
- package/dist/lifecycle/{lifecycle-manager.d.ts → hooks.d.ts} +11 -2
- package/dist/lifecycle/{lifecycle-manager.js → hooks.js} +51 -28
- package/dist/lifecycle/{scope-manager.d.ts → scopes.d.ts} +23 -6
- package/dist/lifecycle/{scope-manager.js → scopes.js} +93 -20
- package/dist/metadata/{metadata-keys.d.ts → keys.d.ts} +1 -1
- package/dist/metadata/{metadata-keys.js → keys.js} +1 -1
- package/dist/metadata/{metadata-reader-token.d.ts → reader-token.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.d.ts → symbol-reader.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.js → symbol-reader.js} +1 -1
- package/dist/metadata/{metadata-types.d.ts → types.d.ts} +1 -1
- package/dist/metadata/types.js +1 -0
- package/dist/metadata/verify.d.ts +43 -0
- package/dist/metadata/verify.js +133 -0
- package/dist/metadata/{verifying-metadata-reader.d.ts → verifying-reader.d.ts} +1 -1
- package/dist/metadata/{verifying-metadata-reader.js → verifying-reader.js} +2 -2
- package/dist/resolution/cache/activation-need.d.ts +1 -1
- package/dist/resolution/cache/activation-need.js +5 -1
- package/dist/resolution/cache/class-introspector.d.ts +27 -36
- package/dist/resolution/cache/class-introspector.js +61 -159
- package/dist/resolution/cache/{binding-lookup-cache.d.ts → lookup.d.ts} +7 -2
- package/dist/resolution/cache/{binding-lookup-cache.js → lookup.js} +40 -8
- package/dist/resolution/context.d.ts +1 -1
- package/dist/resolution/context.js +1 -1
- package/dist/resolution/{path/resolution-path.d.ts → path.d.ts} +12 -2
- package/dist/resolution/{path/resolution-path.js → path.js} +23 -3
- package/dist/resolution/plan/{plan-codegen.d.ts → codegen.d.ts} +2 -4
- package/dist/resolution/plan/{plan-codegen.js → codegen.js} +2 -4
- package/dist/resolution/plan/{instantiation-plan.d.ts → compiler.d.ts} +1 -1
- package/dist/resolution/plan/{instantiation-plan.js → compiler.js} +4 -4
- package/dist/resolution/resolver.d.ts +19 -7
- package/dist/resolution/resolver.js +343 -125
- package/dist/resolution/select/{binding-select.d.ts → candidates.d.ts} +30 -0
- package/dist/resolution/select/{binding-select.js → candidates.js} +51 -10
- package/dist/resolution/select/constraints.js +1 -1
- package/package.json +80 -72
- package/dist/core/binding-scope.d.ts +0 -12
- package/dist/core/binding-scope.js +0 -12
- package/dist/decorators/lifecycle-decorators.d.ts +0 -14
- /package/dist/{ambient/active-container.d.ts → ambient-container.d.ts} +0 -0
- /package/dist/{ambient/active-container.js → ambient-container.js} +0 -0
- /package/dist/{metadata/metadata-types.js → core/binding-builders.js} +0 -0
- /package/dist/decorators/{decorator-metadata.d.ts → metadata-record.d.ts} +0 -0
- /package/dist/injection/{resolve-options.d.ts → dependency-slot.d.ts} +0 -0
- /package/dist/injection/{resolve-options.js → dependency-slot.js} +0 -0
- /package/dist/{errors → introspection}/diagnostics.d.ts +0 -0
- /package/dist/{errors → introspection}/diagnostics.js +0 -0
- /package/dist/metadata/{metadata-reader-token.js → reader-token.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,156 @@
|
|
|
1
1
|
# @codefast/di
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1022](https://github.com/codefastlabs/codefast/pull/1022) **Breaking:** `Container` no longer declares `[Symbol.dispose]`, and `SyncDisposalNotSupportedError` is removed. A
|
|
8
|
+
deactivation hook may be async, so a container disposes through `await using` or `dispose()` only, and a synchronous
|
|
9
|
+
`using` on one is now a compile error instead of a `SyncDisposalNotSupportedError` at runtime. Replace `using` with
|
|
10
|
+
`await using`, and drop any `instanceof SyncDisposalNotSupportedError` check.
|
|
11
|
+
|
|
12
|
+
- [#1024](https://github.com/codefastlabs/codefast/pull/1024) **Breaking:** `@postConstruct()` and `@preDestroy()` now reject a private (`#name`) method, as they already rejected a
|
|
13
|
+
static or symbol-keyed one. The lifecycle manager calls a hook by name on the instance, so a private hook passed
|
|
14
|
+
`validate()` and then failed at resolve with an `InvalidMetadataError` that blamed the metadata reader, or at
|
|
15
|
+
`dispose()` for `@preDestroy()`. Decorating one now throws the new `PrivateLifecycleMethodError` when the class is
|
|
16
|
+
defined. The decorators' types also accept only a public, instance, string-named method, and `@inject` only an instance
|
|
17
|
+
accessor, so TypeScript reports all of these at the decoration site (TS1240, TS1241). To run private code from a hook,
|
|
18
|
+
call it from a public one.
|
|
19
|
+
|
|
20
|
+
- [#1022](https://github.com/codefastlabs/codefast/pull/1022) `TokenNotBoundError` and `NoMatchingBindingError` now carry `path`: the token names from the outermost request down to
|
|
21
|
+
the one that missed, alias hops included. A miss below the token asked for ends its message with that path —
|
|
22
|
+
`Path: UserService → UserRepository → Database → app:Logger` — so a deep graph says which consumer needed the binding; a
|
|
23
|
+
miss on the token asked for keeps its message as it was. Every lane reports the same path: sync and async, generated
|
|
24
|
+
plans, factory bodies, and `explain()` for an ancestor that selects nothing.
|
|
25
|
+
|
|
26
|
+
- [#1022](https://github.com/codefastlabs/codefast/pull/1022) **Breaking:** a `scoped` instance is now deactivated with the child container that cached it. Disposing that child runs
|
|
27
|
+
the binding's `onDeactivation`, the container-level `onDeactivation` hooks of the container that owns the binding, and
|
|
28
|
+
`@preDestroy()` for every scoped instance it cached, latest first and before its own singletons; unbinding a scoped
|
|
29
|
+
binding on the child that cached it deactivates that instance too. `.scoped()` now offers `onDeactivation`, and
|
|
30
|
+
`Module.fromBindings` accepts `onDeactivation` with `scope: "scoped"`, so per-request resources — a transaction, a
|
|
31
|
+
connection — can be released where they are declared. `validate()` no longer reports a deactivation hook on a
|
|
32
|
+
scoped-only token as unreachable. Code that relied on a scoped instance never being torn down, or that closed one by
|
|
33
|
+
hand after `dispose()`, should drop the manual close.
|
|
34
|
+
|
|
35
|
+
- [#1022](https://github.com/codefastlabs/codefast/pull/1022) **Breaking:** `validate()` now throws the error `resolve()` would for a singleton graph that cannot resolve — a required
|
|
36
|
+
dependency nothing binds (`TokenNotBoundError`), a slot nothing matches (`NoMatchingBindingError`), or a cycle
|
|
37
|
+
(`CircularDependencyError`) — with the path from that singleton down to the problem. A parent-owned singleton reached
|
|
38
|
+
from a child is now read from the parent's chain, as `resolve` reads it. It still leaves alone what it cannot decide: a
|
|
39
|
+
transient or scoped consumer, whose dependencies a child container may bind, and a request only a `when()` candidate
|
|
40
|
+
matches. A container that passed `validate()` while holding such a graph now fails it; bind the missing dependency, or
|
|
41
|
+
mark it `optional()` when it may be absent.
|
|
42
|
+
|
|
43
|
+
## 0.12.0
|
|
44
|
+
|
|
45
|
+
### Minor Changes
|
|
46
|
+
|
|
47
|
+
- [#1010](https://github.com/codefastlabs/codefast/pull/1010) **Breaking:** `resolveAsync`, `resolveOptionalAsync` and `resolveAllAsync` on a disposed container — or on a child of
|
|
48
|
+
one — now return a promise rejected with `DisposedContainerError` instead of throwing it, and `resolveAllAsync(token)`
|
|
49
|
+
likewise rejects when a `when()` predicate throws or an alias cycle is followed, so every async method reports its
|
|
50
|
+
failure through the promise it returns. Code that awaits these calls, or chains `.catch()`, behaves as before or better;
|
|
51
|
+
only a caller that wrapped the call itself in `try`/`catch` without awaiting it no longer sees the error there, and
|
|
52
|
+
should await it instead.
|
|
53
|
+
|
|
54
|
+
- [#995](https://github.com/codefastlabs/codefast/pull/995) Add `container.explain(token, options)`, which answers why a request selects the binding it does without instantiating
|
|
55
|
+
anything. It reads the container chain the way `resolve` does and decides by the same rules, so its answer is the
|
|
56
|
+
engine's own. It reports each registry it read with every candidate and its verdict (`eligible`, `slot-mismatch`,
|
|
57
|
+
`predicate-refused`, `collection-member`), the rule that decided (`sole-candidate`, `sole-predicate`, `most-criteria`,
|
|
58
|
+
`default-alias`, `ambiguous`), the binding the request ends on after every alias, and an `outcome` naming the error
|
|
59
|
+
`resolve` would throw when it ends on none. `when()` predicates run as they would in `resolve`, and `options.ancestors`
|
|
60
|
+
explains a request made from inside other resolutions, so a predicate that reads the parent sees the one it would.
|
|
61
|
+
|
|
62
|
+
- [#999](https://github.com/codefastlabs/codefast/pull/999) **Breaking:** a binding's slot is declared before its strategy —
|
|
63
|
+
`bind(Logger).whenNamed("file").to(FileLogger).singleton()`. `when()`, `whenNamed()`, `whenTagged()`, `whenDefault()`
|
|
64
|
+
and `many()` now come before `to*()` and are refused after it with `ChainAlreadyRegisteredError`, so a binding registers
|
|
65
|
+
once, in its final slot, and no later step moves it, displaces something else or brings back what it displaced. Scope
|
|
66
|
+
and lifecycle hooks stay after `to*()`. To migrate, move each chain's slot steps in front of its `to*()` call.
|
|
67
|
+
|
|
68
|
+
The registry no longer parks the bindings a re-slot displaced or restores them, so a bind with a name or a tag costs a
|
|
69
|
+
quarter to two fifths less. `BindingBuilder`, `ConstantBindingBuilder` and `AliasBindingBuilder` lose their `Names` type
|
|
70
|
+
parameter, since the slot is settled before they exist.
|
|
71
|
+
|
|
72
|
+
- [#1005](https://github.com/codefastlabs/codefast/pull/1005) **Breaking for deep subpath imports only** — the root entry `@codefast/di` is unchanged. `src/` now follows three rules,
|
|
73
|
+
and the published subpaths follow `src/` with no exception: a directory names a family of two or more modules and a lone
|
|
74
|
+
module sits flat at its layer; a directory carries the topic and a file its role, so no file repeats its directory's
|
|
75
|
+
name; and the exports map is the source tree, so the `strip` that kept the introspection modules at flat specifiers is
|
|
76
|
+
gone.
|
|
77
|
+
|
|
78
|
+
Moves: the `MetadataReader` verification leaves `resolution/cache/class-introspector` for `metadata/verify` — it was the
|
|
79
|
+
one value import pointing up from `metadata/` into `resolution/`; the `RESOLUTION_DIAGNOSTICS` channel leaves `errors/`
|
|
80
|
+
for `introspection/diagnostics`; the fluent chain's contract leaves `core/binding` for `core/binding-builders`, and the
|
|
81
|
+
class implementing it is `container/binding-chain`; `effectiveBindingScope` folds into `core/binding`.
|
|
82
|
+
|
|
83
|
+
Renamed specifiers: `./errors/errors` → `./errors`; `./errors/diagnostics` → `./introspection/diagnostics`;
|
|
84
|
+
`./ambient/active-container` → `./ambient-container`; `./container/binding-builders` → `./container/binding-chain`;
|
|
85
|
+
`./core/binding-scope` → gone (`effectiveBindingScope` is in `./core/binding`); `./injection/resolve-options` →
|
|
86
|
+
`./injection/dependency-slot`;
|
|
87
|
+
`./metadata/{metadata-keys,metadata-types,metadata-reader-token,symbol-metadata-reader,verifying-metadata-reader}` →
|
|
88
|
+
`./metadata/{keys,types,reader-token,symbol-reader,verifying-reader}`; `./lifecycle/{lifecycle-manager,scope-manager}` →
|
|
89
|
+
`./lifecycle/{hooks,scopes}`; `./decorators/{lifecycle-decorators,decorator-metadata}` →
|
|
90
|
+
`./decorators/{lifecycle,metadata-record}`; `./resolution/path/resolution-path` → `./resolution/path`;
|
|
91
|
+
`./resolution/plan/{instantiation-plan,plan-codegen}` → `./resolution/plan/{compiler,codegen}`;
|
|
92
|
+
`./resolution/select/binding-select` → `./resolution/select/candidates`; `./resolution/cache/binding-lookup-cache` →
|
|
93
|
+
`./resolution/cache/lookup`; `./{inspector,explanation,dependency-graph,graph-adapters/*}` →
|
|
94
|
+
`./introspection/{inspector,explanation,dependency-graph,graph-adapters/*}`. New: `./core/binding-builders`,
|
|
95
|
+
`./metadata/verify`.
|
|
96
|
+
|
|
97
|
+
`@codefast/di-testing` follows the one specifier it imports, `metadata/verifying-reader`.
|
|
98
|
+
|
|
99
|
+
### Patch Changes
|
|
100
|
+
|
|
101
|
+
- [#996](https://github.com/codefastlabs/codefast/pull/996) A request with criteria that matches no slot is fast again when the container holds no alias: `resolveOptional()` of a
|
|
102
|
+
missing tagged slot no longer probes for a default-slot alias to forward to in a registry that never held one.
|
|
103
|
+
|
|
104
|
+
- [#1009](https://github.com/codefastlabs/codefast/pull/1009) `resolveAsync` no longer rejects with `AsyncResolutionError` when a later sibling reads a singleton or scoped binding
|
|
105
|
+
synchronously — a `toDynamic` factory's `ctx.resolve`, an `@inject` accessor, a top-level `resolve` — while the async
|
|
106
|
+
lane is still materialising it from an earlier sibling. A level of the async lane now yields only where a dependency or
|
|
107
|
+
hook really awaits, so an instance built without yielding is cached before the next sibling starts, and only a
|
|
108
|
+
materialisation that is genuinely pending is published in flight.
|
|
109
|
+
|
|
110
|
+
- [#994](https://github.com/codefastlabs/codefast/pull/994) A scope refinement costs less at bind time. `singleton()`, `transient()` and `scoped()` now do nothing when the scope
|
|
111
|
+
does not change, and release only what exists when it does, so `bind(T).to(X).singleton()` straight after registration
|
|
112
|
+
no longer calls into the scope manager.
|
|
113
|
+
|
|
114
|
+
- [#997](https://github.com/codefastlabs/codefast/pull/997) Every read on a child container is cheaper. The child's check that no ancestor was disposed is now one epoch compare
|
|
115
|
+
that V8 inlines into each entry point, the walk up the chain runs only after some container was disposed, and a new
|
|
116
|
+
child starts confirmed live, so a per-request child skips the walk on its first read.
|
|
117
|
+
|
|
118
|
+
- [#994](https://github.com/codefastlabs/codefast/pull/994) Resolving a class binding for the first time is cheaper. The metadata cache now keeps one record per class, which a cold
|
|
119
|
+
resolve looks up once instead of reading a separate map for each fact, and a class with no constructor parameters is
|
|
120
|
+
built without allocating an empty argument list. This mostly speeds up containers that bind and resolve many class
|
|
121
|
+
singletons at startup.
|
|
122
|
+
|
|
123
|
+
- [#1001](https://github.com/codefastlabs/codefast/pull/1001) The first resolve of a class binding is cheaper. The class's parameter list, and the check that a subclass is not
|
|
124
|
+
dropping its base's declared dependencies, are settled once per class instead of once per instantiation, and the class
|
|
125
|
+
asked about last is answered without a map lookup, so a hundred singletons of one class bind and materialize about a
|
|
126
|
+
fifth faster.
|
|
127
|
+
|
|
128
|
+
- [#994](https://github.com/codefastlabs/codefast/pull/994) A root-level `resolveAll()` read in a loop over the same token, as an event bus does for every event, is cheaper: the
|
|
129
|
+
lookup cache keeps the last collection in front of its map, so a repeat read skips the map lookup.
|
|
130
|
+
|
|
131
|
+
- [#997](https://github.com/codefastlabs/codefast/pull/997) A container that reads a collection once no longer builds a value list it never reads again. A root-level `resolveAll`
|
|
132
|
+
keeps its stable value list from the second read on, the way an instantiation plan compiles on repeat, so a fresh
|
|
133
|
+
container per request pays only for the members it resolves.
|
|
134
|
+
|
|
135
|
+
- [#996](https://github.com/codefastlabs/codefast/pull/996) A resolve that carries options on a root container gets back much of the speed 0.11.0 took from it. The check that
|
|
136
|
+
refuses a disposed chain grew in that release enough that V8 stopped inlining the lookup behind that entry point; a live
|
|
137
|
+
root now pays two field reads for it, and only a child reads the dispose epoch.
|
|
138
|
+
|
|
139
|
+
- [#998](https://github.com/codefastlabs/codefast/pull/998) A root container's `resolve(token, options)` no longer pays for the check a child makes that no ancestor was disposed.
|
|
140
|
+
Each container is created with the `resolve` of its role, so the root's never carries the child's path and the rule that
|
|
141
|
+
a disposed ancestor refuses its descendants stays as it was.
|
|
142
|
+
|
|
143
|
+
- [#994](https://github.com/codefastlabs/codefast/pull/994) A scoped binding whose scope changes stops handing children the instance they cached before the change. A binding
|
|
144
|
+
registered on a parent, resolved from a child, then refined `scoped()` → `transient()` → `scoped()` gave that child its
|
|
145
|
+
old scoped instance back, because only the registering container's cache was cleared. Every container now files a scoped
|
|
146
|
+
instance under a key the binding replaces when it leaves `scoped`, so the cached instance is never found again.
|
|
147
|
+
|
|
148
|
+
- [#1003](https://github.com/codefastlabs/codefast/pull/1003) A teardown reads a class's `@preDestroy` methods once for a run of bindings of that class, instead of once per binding,
|
|
149
|
+
and names the token only when a hook fails.
|
|
150
|
+
|
|
151
|
+
- [#1002](https://github.com/codefastlabs/codefast/pull/1002) A rebind that owes no teardown allocates nothing for the binding it displaces, and a collection read skips its scan for
|
|
152
|
+
dangling aliases in a chain that never held one.
|
|
153
|
+
|
|
3
154
|
## 0.11.0
|
|
4
155
|
|
|
5
156
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @codefast/di
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Type-safe dependency injection for TypeScript applications, with zero dependencies, built on standard decorators with no
|
|
4
4
|
`reflect-metadata` and no `experimentalDecorators`.
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/@codefast/di)
|
|
@@ -20,12 +20,12 @@ its keep as the dependency graph grows.
|
|
|
20
20
|
|
|
21
21
|
- **Typed tokens.** `Token<Value>` flows through every `bind → resolve` path, so `resolve()` returns exactly the type
|
|
22
22
|
you registered.
|
|
23
|
-
- **
|
|
24
|
-
|
|
23
|
+
- **Standard decorators.** `@injectable`, `inject`, `optional`, `injectAll`, `@postConstruct`, and `@preDestroy` declare
|
|
24
|
+
dependencies explicitly — no runtime reflection.
|
|
25
25
|
- **Fluent bindings.** Constants, classes, sync and async factories, aliases, named/tagged/predicate constraints, and
|
|
26
26
|
lifecycle hooks compose in one invariant chain order.
|
|
27
27
|
- **Scopes with validation.** Choose `singleton`, `scoped`, or `transient`, and call `validate()` to catch captive
|
|
28
|
-
dependencies before the first request.
|
|
28
|
+
dependencies, missing bindings and cycles before the first request.
|
|
29
29
|
- **Modules and introspection.** Bundle bindings into reusable modules, and inspect a container or render its dependency
|
|
30
30
|
graph as DOT, Mermaid, Cytoscape, or React Flow.
|
|
31
31
|
|
|
@@ -36,7 +36,7 @@ pnpm add @codefast/di
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
`@codefast/di` runs on Node.js 24 or later and in Chrome and Edge 136, Firefox 136, or Safari 18.4 or later
|
|
39
|
-
([support policy](../../SUPPORT.md#browsers)). It requires TypeScript 7 or later, with
|
|
39
|
+
([support policy](../../SUPPORT.md#browsers)). It requires TypeScript 7 or later, with standard decorators. Leave
|
|
40
40
|
`experimentalDecorators` off — it's off by default. Its declarations use explicit resource management, which no numbered
|
|
41
41
|
`lib` declares yet, so your program needs those types: `@types/node` 24 or later loads them, and any other program adds
|
|
42
42
|
`ESNext.Disposable` to `lib` once its runtime ships explicit resource management. A browser program that targets Safari,
|
|
@@ -44,6 +44,67 @@ which has not shipped it, keeps `skipLibCheck` on and calls `dispose()` instead
|
|
|
44
44
|
published on 0.x and versioned on its own track: breaking changes ship as minor versions, so pin the minor version when
|
|
45
45
|
you need stability.
|
|
46
46
|
|
|
47
|
+
### Toolchain
|
|
48
|
+
|
|
49
|
+
No JavaScript engine runs decorators natively yet — the TC39 proposal went back to Stage 2.7 in May 2026 — so a
|
|
50
|
+
decorated class always passes through a transform that lowers it. What that takes depends on the toolchain:
|
|
51
|
+
|
|
52
|
+
| Toolchain | What it needs |
|
|
53
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| tsx | Nothing: it lowers standard decorators for the Node it runs on. |
|
|
55
|
+
| esbuild | A `target` below `esnext`, such as `es2024` or `node24`: at its default, `esnext`, decorators pass through. |
|
|
56
|
+
| Vite 8, and Vitest on it | The Babel decorators plugin: their Oxc transform lowers only `experimentalDecorators`. |
|
|
57
|
+
| SWC 1.15.21 or later | `jsc.parser.decorators: true` and `jsc.transform.decoratorVersion: "2023-11"`. |
|
|
58
|
+
| `tsc`, then Node directly | A `target` below `ESNext`, and `Symbol.metadata` installed before any decorated class is defined. |
|
|
59
|
+
|
|
60
|
+
For Vite and Vitest, add `@babel/core`, `@babel/plugin-proposal-decorators` and `@rolldown/plugin-babel` as dev
|
|
61
|
+
dependencies, then the decorators preset to the config — `vitest.config.ts` takes the same one:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// vite.config.ts
|
|
65
|
+
import babel, { defineRolldownBabelPreset } from "@rolldown/plugin-babel";
|
|
66
|
+
import { defineConfig } from "vite";
|
|
67
|
+
|
|
68
|
+
export default defineConfig({
|
|
69
|
+
plugins: [
|
|
70
|
+
babel({
|
|
71
|
+
presets: [
|
|
72
|
+
defineRolldownBabelPreset({
|
|
73
|
+
preset: () => ({ plugins: [["@babel/plugin-proposal-decorators", { version: "2023-11" }]] }),
|
|
74
|
+
// Every decorator holds an `@`, so Babel skips only files that cannot hold one.
|
|
75
|
+
rolldown: { filter: { code: "@" } },
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The filter is what keeps Babel off the files without decorators; the plugin applies it only when every preset carries
|
|
84
|
+
one and `plugins` is left empty. Keep it the plain `"@"`: a narrower pattern can miss a legal position such as
|
|
85
|
+
`export @injectable() class`, and a missed file ships its decorators raw. Beside other presets, list this one last,
|
|
86
|
+
since Babel runs presets last to first.
|
|
87
|
+
|
|
88
|
+
> **Warning.** Without the plugin the decorators pass through untouched, and nothing flags it at build time:
|
|
89
|
+
> `vite build` succeeds, then the bundle throws `SyntaxError: Invalid or unexpected token` as it loads — the same error
|
|
90
|
+
> Vitest reports for a test file that imports a decorated class.
|
|
91
|
+
|
|
92
|
+
`tsc` leaves decorators in its output at `target: "ESNext"`. At any lower target it lowers them, but on a runtime
|
|
93
|
+
without `Symbol.metadata` it hands them no metadata object, which every decorator here reports as
|
|
94
|
+
`MissingDecoratorMetadataError`. Install the symbol in a module the entry point imports first:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
(Symbol as { metadata?: symbol }).metadata ??= Symbol.for("Symbol.metadata");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Not every toolchain lowers them yet. Node's own type stripping rejects decorators; Next.js cannot select the `2023-11`
|
|
101
|
+
version (vercel/next.js#96328 adds it); and Bun 1.4.2 and earlier inject the wrong values, silently, into a module that
|
|
102
|
+
holds two decorated classes (oven-sh/bun#28316).
|
|
103
|
+
|
|
104
|
+
[The specification → tsconfig setup](./spec/decorators.md#tsconfig-setup) covers the compiler options. And
|
|
105
|
+
[decorators](#decorators) are optional: an app wired through `toResolved`, `toDynamic` and `Module.fromBindings` needs
|
|
106
|
+
none of this.
|
|
107
|
+
|
|
47
108
|
## Quick start
|
|
48
109
|
|
|
49
110
|
```ts
|
|
@@ -98,9 +159,9 @@ A class constructor works as a key too: `container.bind(UserService).toSelf()`,
|
|
|
98
159
|
|
|
99
160
|
## Bindings
|
|
100
161
|
|
|
101
|
-
A binding describes how the container produces a value for a token. You declare it as a chain, in one fixed order:
|
|
102
|
-
strategy (the `to*` step, which produces the value
|
|
103
|
-
`container.bind(key).to*(…)` is required
|
|
162
|
+
A binding describes how the container produces a value for a token. You declare it as a chain, in one fixed order:
|
|
163
|
+
optional [constraints](#constraints), then a strategy (the `to*` step, which produces the value and registers the
|
|
164
|
+
binding), then an optional scope and lifecycle hooks. Only `container.bind(key).to*(…)` is required.
|
|
104
165
|
|
|
105
166
|
| Strategy | Produces |
|
|
106
167
|
| --------------------------------- | ----------------------------------------------------------------- |
|
|
@@ -134,7 +195,7 @@ no scope produces a new instance every time.
|
|
|
134
195
|
| Scope | Lifetime |
|
|
135
196
|
| -------------- | ---------------------------------------------------------------------------------------- |
|
|
136
197
|
| `.singleton()` | One instance for the container that owns the binding; children resolve the same instance |
|
|
137
|
-
| `.scoped()` | One instance per child container — resolving
|
|
198
|
+
| `.scoped()` | One instance per child container, deactivated with it — resolving outside a child throws |
|
|
138
199
|
| `.transient()` | A new instance on every resolution — the default |
|
|
139
200
|
|
|
140
201
|
```ts
|
|
@@ -148,9 +209,10 @@ container.bind(RequestContextToken).toSelf().scoped();
|
|
|
148
209
|
### Lifecycle hooks
|
|
149
210
|
|
|
150
211
|
A lifecycle hook runs your code as an instance is created or torn down. `.onActivation(fn)` runs right after an instance
|
|
151
|
-
is created, and may replace it; `.onDeactivation(fn)` runs
|
|
152
|
-
|
|
153
|
-
|
|
212
|
+
is created, and may replace it; `.onDeactivation(fn)` runs as the instance's lifetime ends — a singleton's when its
|
|
213
|
+
container is disposed or the binding is unbound, a scoped instance's when the child container that cached it is
|
|
214
|
+
disposed. A transient instance has no deactivation. Both hooks are also available container-wide, through
|
|
215
|
+
`container.onActivation(token, fn)` and `container.onDeactivation(token, fn)`:
|
|
154
216
|
|
|
155
217
|
```ts
|
|
156
218
|
container
|
|
@@ -169,8 +231,8 @@ constraints is its slot — think of a slot as a labelled variant of the token.
|
|
|
169
231
|
`{ name }`:
|
|
170
232
|
|
|
171
233
|
```ts
|
|
172
|
-
container.bind(LoggerToken).
|
|
173
|
-
container.bind(LoggerToken).
|
|
234
|
+
container.bind(LoggerToken).whenNamed("console").toConstantValue(consoleLogger);
|
|
235
|
+
container.bind(LoggerToken).whenNamed("file").toConstantValue(fileLogger);
|
|
174
236
|
|
|
175
237
|
container.resolve(LoggerToken, { name: "file" }); // → fileLogger
|
|
176
238
|
```
|
|
@@ -180,7 +242,7 @@ Declare the names on the token and they become checked, completable literals at
|
|
|
180
242
|
```ts
|
|
181
243
|
const LoggerToken = token<Logger, "console" | "file">("app:Logger");
|
|
182
244
|
|
|
183
|
-
container.bind(LoggerToken).
|
|
245
|
+
container.bind(LoggerToken).whenNamed("file").toConstantValue(fileLogger);
|
|
184
246
|
container.resolve(LoggerToken, { name: "file" }); // { name: "fiel" } is a compile error
|
|
185
247
|
```
|
|
186
248
|
|
|
@@ -193,7 +255,7 @@ import { tag } from "@codefast/di";
|
|
|
193
255
|
|
|
194
256
|
const Provider = tag<"s3" | "gcs">("app:provider");
|
|
195
257
|
|
|
196
|
-
container.bind(StorageToken).
|
|
258
|
+
container.bind(StorageToken).whenTagged(Provider.of("s3")).to(S3Storage);
|
|
197
259
|
container.resolve(StorageToken, { tag: Provider.of("s3") }); // → S3Storage
|
|
198
260
|
```
|
|
199
261
|
|
|
@@ -212,6 +274,19 @@ container.resolve(StorageToken, { tag: Provider.of("s3") }); // → S3Storage
|
|
|
212
274
|
`injectAll` alike. Chain `.whenTagged(...)` once per criterion a slot carries, and request several at once with
|
|
213
275
|
`{ tags: [...] }`.
|
|
214
276
|
|
|
277
|
+
**One binding per slot; a collection with `many()`.** Binding a token again in a slot it already fills replaces the
|
|
278
|
+
earlier binding — `bind` is last-wins per slot, so `resolveAll` then finds only the newer one. To register several
|
|
279
|
+
implementations for `resolveAll` and `injectAll` to gather, mark each one a collection member with `.many()`:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
container.bind(PluginToken).many().to(AuditPlugin);
|
|
283
|
+
container.bind(PluginToken).many().to(MetricsPlugin);
|
|
284
|
+
|
|
285
|
+
container.resolveAll(PluginToken); // → an AuditPlugin and a MetricsPlugin, in registration order
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
A member keeps the default slot and is never what a single `resolve` selects.
|
|
289
|
+
|
|
215
290
|
**Predicate constraints — graph-aware selection.** When a slot isn't enough — when you need to choose based on _who_ is
|
|
216
291
|
resolving — pass a predicate to `.when(ctx => boolean)`. It runs at resolve time, after slot matching. These ready-made
|
|
217
292
|
predicates ship from the root entry:
|
|
@@ -230,7 +305,7 @@ predicates ship from the root entry:
|
|
|
230
305
|
| `whenAnyAncestorTaggedAll(criteria)` | some ancestor's slot carries all criteria in the array |
|
|
231
306
|
|
|
232
307
|
For the exact matching and most-specific-wins rules, see
|
|
233
|
-
[
|
|
308
|
+
[the specification → Slots and last-wins](./spec/binding.md#slots-and-last-wins--the-exact-definition).
|
|
234
309
|
|
|
235
310
|
## Decorators
|
|
236
311
|
|
|
@@ -252,7 +327,7 @@ class UserRepository {
|
|
|
252
327
|
constructor(
|
|
253
328
|
private readonly db: Database,
|
|
254
329
|
private readonly cache: Cache | undefined,
|
|
255
|
-
private readonly plugins:
|
|
330
|
+
private readonly plugins: ReadonlyArray<Plugin>,
|
|
256
331
|
private readonly audit: Logger,
|
|
257
332
|
) {}
|
|
258
333
|
|
|
@@ -349,17 +424,21 @@ parent's singletons, while each `scoped` binding gets a fresh instance per child
|
|
|
349
424
|
|
|
350
425
|
A captive dependency is a long-lived binding that holds a shorter-lived one — a `singleton` that depends on a `scoped`
|
|
351
426
|
or `transient` binding — which silently freezes that dependency for the singleton's whole life. `validate()` fails fast
|
|
352
|
-
on captive dependencies, and on constraints no request can satisfy, before the first resolve.
|
|
427
|
+
on captive dependencies, and on constraints no request can satisfy, before the first resolve. It also throws the error
|
|
428
|
+
`resolve()` would for a singleton graph that cannot resolve: a required dependency nothing binds, a slot nothing
|
|
429
|
+
matches, or a cycle.
|
|
353
430
|
|
|
354
431
|
```ts
|
|
355
|
-
container.validate(); // throws
|
|
432
|
+
container.validate(); // throws on the first violation — ScopeViolationError, TokenNotBoundError, …
|
|
356
433
|
```
|
|
357
434
|
|
|
435
|
+
A transient or scoped consumer's dependencies are left to `resolve()`, because a child container may still bind them.
|
|
436
|
+
|
|
358
437
|
### Disposal
|
|
359
438
|
|
|
360
439
|
`Container` implements `AsyncDisposable`, so `await using` runs every deactivation hook automatically as the block
|
|
361
|
-
exits
|
|
362
|
-
`
|
|
440
|
+
exits, and disposing a child deactivates the scoped instances it cached. Synchronous `using` is a compile error: a
|
|
441
|
+
deactivation hook may be async, so the container implements `Symbol.asyncDispose` only.
|
|
363
442
|
|
|
364
443
|
### Introspection
|
|
365
444
|
|
|
@@ -367,6 +446,18 @@ exits. Synchronous `using` isn't supported — `onDeactivation` may be async —
|
|
|
367
446
|
token's bindings; and `container.generateDependencyGraph()` returns a JSON graph. Adapters render that graph for common
|
|
368
447
|
viewers: `toDotGraph`, `toMermaidGraph`, `toCytoscapeGraph`, and `toReactFlowGraph`.
|
|
369
448
|
|
|
449
|
+
`container.explain(token, options)` answers why a request selects the binding it does, without instantiating anything:
|
|
450
|
+
every candidate each registry offered and what selection made of it, the rule that decided, and the binding it ends on.
|
|
451
|
+
It evaluates `when()` predicates the way `resolve` does, and `options.ancestors` explains a request made from inside
|
|
452
|
+
other resolutions, so a predicate that reads the parent sees the one it would:
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
const explanation = container.explain(Logger, { ancestors: [Settlement] });
|
|
456
|
+
|
|
457
|
+
explanation.selected?.id; // the binding `resolve` would answer with
|
|
458
|
+
explanation.steps[0]?.rule; // "sole-predicate": the only candidate with a `when()` beat the rest
|
|
459
|
+
```
|
|
460
|
+
|
|
370
461
|
## Modules
|
|
371
462
|
|
|
372
463
|
A module is a reusable, stateless bundle of related bindings. Group them once, then load them into any container.
|
|
@@ -463,31 +554,37 @@ Every error extends `DiError` and carries a stable `code`, so you can branch on
|
|
|
463
554
|
| `MissingContainerContextError` | `MISSING_CONTAINER_CONTEXT` | An `@inject` accessor initializes with no container open |
|
|
464
555
|
| `DisposedContainerError` | `DISPOSED_CONTAINER` | A disposed container is used |
|
|
465
556
|
|
|
557
|
+
A miss below the token you asked for names its path: `TokenNotBoundError` and `NoMatchingBindingError` carry `path`, and
|
|
558
|
+
the message ends with it — `Path: UserService → UserRepository → app:Logger`.
|
|
559
|
+
|
|
466
560
|
The full taxonomy — including `MissingMetadataError`, `InvalidMetadataError`, `RebindUnboundTokenError`,
|
|
467
561
|
`AsyncModuleLoadError`, `InvalidBindingDeclarationError`, and the rest — is exported from the root entry and from
|
|
468
|
-
`@codefast/di/errors
|
|
562
|
+
`@codefast/di/errors`.
|
|
469
563
|
|
|
470
564
|
## Subpath exports
|
|
471
565
|
|
|
472
566
|
Prefer the root entry: it re-exports the whole public API. Reach for a subpath only to trim a bundle, or to pull in
|
|
473
567
|
something the root doesn't surface.
|
|
474
568
|
|
|
475
|
-
Every module is also published as a subpath that
|
|
476
|
-
(`core/token`, `core/tag`, `core/module`, …),
|
|
477
|
-
`lifecycle/*`, and `resolution/*` (for example
|
|
478
|
-
metadata under `decorators/*` and `metadata
|
|
479
|
-
`@codefast/di/
|
|
569
|
+
Every module is also published as a subpath that is its source path, with no exception: the model under
|
|
570
|
+
`@codefast/di/core/*` (`core/token`, `core/tag`, `core/module`, …), the error taxonomy at `@codefast/di/errors`, the
|
|
571
|
+
runtime under `container/*`, `injection/*`, `lifecycle/*`, and `resolution/*` (for example
|
|
572
|
+
`@codefast/di/resolution/select/constraints`), decorators and metadata under `decorators/*` and `metadata/*`, and
|
|
573
|
+
introspection under `introspection/*`: `@codefast/di/introspection/inspector`, `@codefast/di/introspection/explanation`,
|
|
574
|
+
`@codefast/di/introspection/dependency-graph`, and
|
|
575
|
+
`@codefast/di/introspection/graph-adapters/{dot,mermaid,cytoscape,reactflow}`.
|
|
480
576
|
|
|
481
577
|
## Benchmarks
|
|
482
578
|
|
|
483
579
|
A first-party benchmark suite lives in the monorepo, at [`benchmarks/di`](../../benchmarks/di). It runs the same
|
|
484
|
-
workloads through `@codefast/di`, InversifyJS, Awilix, and
|
|
485
|
-
alongside the method that produced them. Run it yourself rather than taking any figure on
|
|
580
|
+
workloads through `@codefast/di`, InversifyJS, Awilix, tsyringe, Brandi, ditox, and injection-js, and its `RESULTS.md`
|
|
581
|
+
ledger records the numbers alongside the method that produced them. Run it yourself rather than taking any figure on
|
|
582
|
+
faith.
|
|
486
583
|
|
|
487
584
|
## Documentation
|
|
488
585
|
|
|
489
586
|
- [Rendered docs on codefastlabs.com](https://codefastlabs.com/docs/di)
|
|
490
|
-
- [`
|
|
587
|
+
- [`spec/`](./spec/README.md) — the behavioural contract: public API, semantics, and errors, one document per API area.
|
|
491
588
|
- [`ARCHITECTURE.md`](./ARCHITECTURE.md) — the internal shape and the invariants the hot paths depend on.
|
|
492
589
|
- [`DECISIONS.md`](./DECISIONS.md) — why the API looks like this, and what it did not take from InversifyJS v8.
|
|
493
590
|
- [`examples/`](./examples/README.md) — runnable examples from basic tokens to a multi-file Ports & Adapters app.
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @remarks One object plays every role: each builder step, the record the registry stores, and the
|
|
5
5
|
* binding the resolver reads. The binding fields are declared first and in a fixed order, so every
|
|
6
|
-
* binding in the process shares one hidden class; the return type of each step
|
|
7
|
-
*
|
|
6
|
+
* binding in the process shares one hidden class; the return type of each step pins the chain's
|
|
7
|
+
* order, slot before strategy, so the binding registers once and is never re-slotted.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { Binding, BindingSlot } from "#core/binding";
|
|
10
|
+
import type { AliasBindingBuilder, BindingBuilder, BindToBuilder, ConstantBindingBuilder, ScopedBindingBuilder, SingletonBindingBuilder, SingletonLifecycleBuilder, TransientBindingBuilder } from "#core/binding-builders";
|
|
10
11
|
import type { DeclaredBinding } from "#core/binding-declaration";
|
|
11
12
|
import type { BindingRegistry } from "#core/registry";
|
|
12
13
|
import type { BindingTag } from "#core/tag";
|
|
13
14
|
import type { Token } from "#core/token";
|
|
14
15
|
import type { ActivationHandler, BindingConstraint, BindingIdentifier, BindingKind, BindingScope, Constructor, DeactivationHandler, ResolutionContext, ResolutionFrame } from "#core/types";
|
|
15
16
|
import type { InjectableDependency, InjectionDescriptor, ResolvedDependencyValue } from "#injection/descriptor";
|
|
16
|
-
import type { ScopeManager } from "#lifecycle/
|
|
17
|
+
import type { ScopeManager } from "#lifecycle/scopes";
|
|
17
18
|
/**
|
|
18
19
|
* Where a chain registers, and on whose behalf.
|
|
19
20
|
*
|
|
@@ -26,24 +27,22 @@ export interface BindingRegistration {
|
|
|
26
27
|
readonly registry: BindingRegistry;
|
|
27
28
|
readonly scope: ScopeManager;
|
|
28
29
|
readonly moduleBindingIds: Array<BindingIdentifier> | undefined;
|
|
29
|
-
/** Runs for a binding this registration displaces,
|
|
30
|
+
/** Runs for a binding this registration displaces, deactivating it on the spot. */
|
|
30
31
|
readonly deactivateDisplaced?: ((binding: Binding) => void) | undefined;
|
|
31
|
-
/** Notes a
|
|
32
|
+
/** Notes a displaced binding so the container can still tear it down, when nothing deactivates it on the spot. */
|
|
32
33
|
readonly onDisplaced?: ((binding: Binding) => void) | undefined;
|
|
33
|
-
/** Undoes {@link BindingRegistration.onDisplaced} when a refinement restores the binding to the registry. */
|
|
34
|
-
readonly onRestored?: ((binding: Binding) => void) | undefined;
|
|
35
34
|
}
|
|
36
35
|
/**
|
|
37
36
|
* The one builder behind `bind()` and every `to*()` return type — and the binding it registers.
|
|
38
37
|
*
|
|
39
38
|
* @remarks The binding fields come first, in the order every binding shares, and the chain's own
|
|
40
|
-
* bookkeeping follows as private
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* bookkeeping follows as a private field. The slot steps write the fields before anything registers;
|
|
40
|
+
* `to*()` fills the strategy in place and hands this object to the registry once, so a bind is one
|
|
41
|
+
* allocation and one add. A scope or hook afterwards writes the registered object in place.
|
|
43
42
|
*
|
|
44
43
|
* @since 0.5.0-canary.8
|
|
45
44
|
*/
|
|
46
|
-
export declare class BindingChain<Value, Names extends string = string> implements AliasBindingBuilder
|
|
45
|
+
export declare class BindingChain<Value, Names extends string = string> implements AliasBindingBuilder, BindingBuilder<Value>, BindToBuilder<Value, Names>, ConstantBindingBuilder<Value>, ScopedBindingBuilder<Value>, SingletonBindingBuilder<Value>, SingletonLifecycleBuilder<Value>, TransientBindingBuilder<Value> {
|
|
47
46
|
#private;
|
|
48
47
|
kind: BindingKind;
|
|
49
48
|
readonly identifier: BindingIdentifier;
|
|
@@ -51,8 +50,8 @@ export declare class BindingChain<Value, Names extends string = string> implemen
|
|
|
51
50
|
frame: ResolutionFrame | undefined;
|
|
52
51
|
rootContext: ResolutionContext | undefined;
|
|
53
52
|
activationStamp: number;
|
|
54
|
-
registrationOrder: number;
|
|
55
53
|
instance: unknown;
|
|
54
|
+
scopedCacheKey: BindingIdentifier;
|
|
56
55
|
readonly token: Token<Value, Names> | Constructor<Value>;
|
|
57
56
|
slot: BindingSlot;
|
|
58
57
|
predicate: BindingConstraint | undefined;
|
|
@@ -65,26 +64,20 @@ export declare class BindingChain<Value, Names extends string = string> implemen
|
|
|
65
64
|
activationHook: ActivationHandler<Value> | undefined;
|
|
66
65
|
deactivationHook: DeactivationHandler<Value> | undefined;
|
|
67
66
|
constructor(token: Token<Value, Names> | Constructor<Value>, registration: BindingRegistration);
|
|
68
|
-
/**
|
|
69
|
-
* Registers a declared module's bindings in list order, each a chain of its own that no step
|
|
70
|
-
* will ever refine.
|
|
71
|
-
*
|
|
72
|
-
* @remarks A displaced binding is handled as `to*()` handles it, never parked: nothing refines the
|
|
73
|
-
* chain, so nothing could restore it.
|
|
74
|
-
*/
|
|
67
|
+
/** Registers a declared module's bindings in list order, each a chain of its own, handled as `to*()` handles one. */
|
|
75
68
|
static registerDeclared(declarations: ReadonlyArray<DeclaredBinding>, registration: BindingRegistration): void;
|
|
76
|
-
to(type: Constructor<Value>): BindingBuilder<Value
|
|
77
|
-
toSelf(): BindingBuilder<Value
|
|
78
|
-
toConstantValue(value: Value): ConstantBindingBuilder<Value
|
|
79
|
-
toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value
|
|
80
|
-
toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value
|
|
69
|
+
to(type: Constructor<Value>): BindingBuilder<Value>;
|
|
70
|
+
toSelf(): BindingBuilder<Value>;
|
|
71
|
+
toConstantValue(value: Value): ConstantBindingBuilder<Value>;
|
|
72
|
+
toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value>;
|
|
73
|
+
toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value>;
|
|
81
74
|
toResolved<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
82
75
|
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
83
|
-
}) => Value, deps: Deps): BindingBuilder<Value
|
|
76
|
+
}) => Value, deps: Deps): BindingBuilder<Value>;
|
|
84
77
|
toResolvedAsync<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
85
78
|
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
86
|
-
}) => Promise<Value>, deps: Deps): BindingBuilder<Value
|
|
87
|
-
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder
|
|
79
|
+
}) => Promise<Value>, deps: Deps): BindingBuilder<Value>;
|
|
80
|
+
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder;
|
|
88
81
|
when(predicate: BindingConstraint): this;
|
|
89
82
|
whenNamed(name: Names): this;
|
|
90
83
|
whenTagged(criterion: BindingTag): this;
|