@navios/di 0.7.0 → 0.8.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 +31 -0
- package/lib/browser/index.d.mts +62 -2
- package/lib/browser/index.d.mts.map +1 -1
- package/lib/browser/index.mjs +132 -15
- package/lib/browser/index.mjs.map +1 -1
- package/lib/{container-CXDYDJSM.d.mts → container-Bp1W-pWJ.d.mts} +63 -3
- package/lib/container-Bp1W-pWJ.d.mts.map +1 -0
- package/lib/{container-Bv6PZZLJ.mjs → container-DAKOvAgr.mjs} +137 -18
- package/lib/container-DAKOvAgr.mjs.map +1 -0
- package/lib/{container-BCv3XS6m.cjs → container-DENMeJ87.cjs} +137 -18
- package/lib/container-DENMeJ87.cjs.map +1 -0
- package/lib/{container-b6mDUdGq.d.cts → container-YPwvmlK2.d.cts} +63 -3
- package/lib/container-YPwvmlK2.d.cts.map +1 -0
- package/lib/index.cjs +1 -1
- package/lib/index.d.cts +1 -1
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +1 -1
- package/lib/testing/index.cjs +1 -1
- package/lib/testing/index.d.cts +1 -1
- package/lib/testing/index.d.mts +1 -1
- package/lib/testing/index.mjs +1 -1
- package/package.json +1 -1
- package/src/internal/context/request-context.mts +11 -0
- package/src/internal/core/instance-resolver.mts +11 -0
- package/src/internal/core/token-processor.mts +60 -1
- package/src/internal/holder/base-holder-manager.mts +106 -10
- package/src/internal/holder/request-storage.mts +7 -14
- package/src/internal/holder/singleton-storage.mts +3 -16
- package/src/token/registry.mts +21 -0
- package/lib/container-BCv3XS6m.cjs.map +0 -1
- package/lib/container-Bv6PZZLJ.mjs.map +0 -1
- package/lib/container-CXDYDJSM.d.mts.map +0 -1
- package/lib/container-b6mDUdGq.d.cts.map +0 -1
|
@@ -19,9 +19,15 @@ export type HolderReadyResult<T> = [undefined, InstanceHolder<T>] | [DIError]
|
|
|
19
19
|
*/
|
|
20
20
|
export abstract class BaseHolderManager {
|
|
21
21
|
protected readonly _holders: Map<string, InstanceHolder>
|
|
22
|
+
/**
|
|
23
|
+
* Reverse dependency index: maps a dependency name to the set of holder names that depend on it.
|
|
24
|
+
* This allows O(1) lookup of dependents instead of O(n) iteration.
|
|
25
|
+
*/
|
|
26
|
+
protected readonly _dependents: Map<string, Set<string>>
|
|
22
27
|
|
|
23
28
|
constructor(protected readonly logger: Console | null = null) {
|
|
24
29
|
this._holders = new Map()
|
|
30
|
+
this._dependents = new Map()
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -48,32 +54,118 @@ export abstract class BaseHolderManager {
|
|
|
48
54
|
abstract has(name: string): any
|
|
49
55
|
|
|
50
56
|
/**
|
|
51
|
-
* Deletes a holder by name.
|
|
57
|
+
* Deletes a holder by name and cleans up the reverse dependency index.
|
|
52
58
|
* @param name The name of the holder to delete
|
|
53
59
|
* @returns true if the holder was deleted, false if it didn't exist
|
|
54
60
|
*/
|
|
55
61
|
delete(name: string): boolean {
|
|
62
|
+
const holder = this._holders.get(name)
|
|
63
|
+
if (holder) {
|
|
64
|
+
// Remove this holder from the reverse index for all its dependencies
|
|
65
|
+
this.removeFromDependentsIndex(name, holder.deps)
|
|
66
|
+
}
|
|
56
67
|
return this._holders.delete(name)
|
|
57
68
|
}
|
|
58
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Registers a holder's dependencies in the reverse index.
|
|
72
|
+
* Call this after creating a holder with dependencies.
|
|
73
|
+
* @param holderName The name of the holder that has dependencies
|
|
74
|
+
* @param deps The set of dependency names
|
|
75
|
+
*/
|
|
76
|
+
registerDependencies(holderName: string, deps: Set<string>): void {
|
|
77
|
+
for (const dep of deps) {
|
|
78
|
+
let dependents = this._dependents.get(dep)
|
|
79
|
+
if (!dependents) {
|
|
80
|
+
dependents = new Set()
|
|
81
|
+
this._dependents.set(dep, dependents)
|
|
82
|
+
}
|
|
83
|
+
dependents.add(holderName)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Removes a holder from the reverse dependency index.
|
|
89
|
+
* @param holderName The name of the holder to remove
|
|
90
|
+
* @param deps The set of dependency names to clean up
|
|
91
|
+
*/
|
|
92
|
+
protected removeFromDependentsIndex(holderName: string, deps: Set<string>): void {
|
|
93
|
+
for (const dep of deps) {
|
|
94
|
+
const dependents = this._dependents.get(dep)
|
|
95
|
+
if (dependents) {
|
|
96
|
+
dependents.delete(holderName)
|
|
97
|
+
if (dependents.size === 0) {
|
|
98
|
+
this._dependents.delete(dep)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Gets all holder names that depend on the given instance name.
|
|
106
|
+
* O(1) lookup using the reverse dependency index.
|
|
107
|
+
* @param instanceName The instance name to find dependents for
|
|
108
|
+
* @returns Array of holder names that depend on this instance
|
|
109
|
+
*/
|
|
110
|
+
getDependents(instanceName: string): string[] {
|
|
111
|
+
const dependents = this._dependents.get(instanceName)
|
|
112
|
+
return dependents ? Array.from(dependents) : []
|
|
113
|
+
}
|
|
114
|
+
|
|
59
115
|
/**
|
|
60
116
|
* Filters holders based on a predicate function.
|
|
61
117
|
* @param predicate Function to test each holder
|
|
62
118
|
* @returns A new Map containing only the holders that match the predicate
|
|
119
|
+
* @deprecated Use forEachHolder() for iteration to avoid allocations
|
|
63
120
|
*/
|
|
64
121
|
filter(
|
|
65
122
|
predicate: (value: InstanceHolder<any>, key: string) => boolean,
|
|
66
123
|
): Map<string, InstanceHolder> {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
124
|
+
const result = new Map<string, InstanceHolder>()
|
|
125
|
+
for (const [key, value] of this._holders) {
|
|
126
|
+
if (predicate(value, key)) {
|
|
127
|
+
result.set(key, value)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return result
|
|
70
131
|
}
|
|
71
132
|
|
|
72
133
|
/**
|
|
73
|
-
*
|
|
134
|
+
* Iterates over holders with a callback. More efficient than filter() as it
|
|
135
|
+
* avoids creating intermediate arrays and Maps.
|
|
136
|
+
* @param callback Function called for each holder with (holder, name)
|
|
137
|
+
*/
|
|
138
|
+
forEachHolder(
|
|
139
|
+
callback: (holder: InstanceHolder<any>, name: string) => void,
|
|
140
|
+
): void {
|
|
141
|
+
for (const [name, holder] of this._holders) {
|
|
142
|
+
callback(holder, name)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Finds the first holder matching a predicate. More efficient than filter()
|
|
148
|
+
* when only one result is needed.
|
|
149
|
+
* @param predicate Function to test each holder
|
|
150
|
+
* @returns The first matching holder or undefined
|
|
151
|
+
*/
|
|
152
|
+
findHolder(
|
|
153
|
+
predicate: (holder: InstanceHolder<any>, name: string) => boolean,
|
|
154
|
+
): InstanceHolder | undefined {
|
|
155
|
+
for (const [name, holder] of this._holders) {
|
|
156
|
+
if (predicate(holder, name)) {
|
|
157
|
+
return holder
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Clears all holders from this manager and the reverse dependency index.
|
|
74
165
|
*/
|
|
75
166
|
clear(): void {
|
|
76
167
|
this._holders.clear()
|
|
168
|
+
this._dependents.clear()
|
|
77
169
|
}
|
|
78
170
|
|
|
79
171
|
/**
|
|
@@ -202,16 +294,20 @@ export abstract class BaseHolderManager {
|
|
|
202
294
|
return [DIError.circularDependency(cycle)]
|
|
203
295
|
}
|
|
204
296
|
|
|
205
|
-
|
|
206
|
-
|
|
297
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
298
|
+
// Track the waiting relationship
|
|
299
|
+
waiterHolder.waitingFor.add(holder.name)
|
|
300
|
+
}
|
|
207
301
|
}
|
|
208
302
|
|
|
209
303
|
try {
|
|
210
304
|
await holder.creationPromise
|
|
211
305
|
} finally {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
waiterHolder
|
|
306
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
307
|
+
// Clean up the waiting relationship
|
|
308
|
+
if (waiterHolder) {
|
|
309
|
+
waiterHolder.waitingFor.delete(holder.name)
|
|
310
|
+
}
|
|
215
311
|
}
|
|
216
312
|
}
|
|
217
313
|
|
|
@@ -113,22 +113,15 @@ export class RequestStorage implements IHolderStorage {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
findDependents(instanceName: string): string[] {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Check request-scoped holders
|
|
119
|
-
for (const [name, holder] of this.contextHolder.holders) {
|
|
120
|
-
if (holder.deps.has(instanceName)) {
|
|
121
|
-
dependents.push(name)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
116
|
+
// Get dependents from request context (O(1) using reverse index)
|
|
117
|
+
const requestDependents = this.contextHolder.getDependents(instanceName)
|
|
124
118
|
|
|
125
119
|
// Also check singleton holders - a singleton may depend on this request-scoped service
|
|
126
|
-
|
|
127
|
-
if (holder.deps.has(instanceName)) {
|
|
128
|
-
dependents.push(name)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
120
|
+
const singletonDependents = this.holderManager.getDependents(instanceName)
|
|
131
121
|
|
|
132
|
-
|
|
122
|
+
// Combine both arrays (avoid allocations if one is empty)
|
|
123
|
+
if (requestDependents.length === 0) return singletonDependents
|
|
124
|
+
if (singletonDependents.length === 0) return requestDependents
|
|
125
|
+
return [...requestDependents, ...singletonDependents]
|
|
133
126
|
}
|
|
134
127
|
}
|
|
@@ -79,27 +79,14 @@ export class SingletonStorage implements IHolderStorage {
|
|
|
79
79
|
forEach(
|
|
80
80
|
callback: (name: string, holder: InstanceHolder) => void,
|
|
81
81
|
): void {
|
|
82
|
-
|
|
83
|
-
callback(name, holder)
|
|
84
|
-
}
|
|
82
|
+
this.manager.forEachHolder((holder, name) => callback(name, holder))
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
findByInstance(instance: unknown): InstanceHolder | null {
|
|
88
|
-
|
|
89
|
-
(h) => h.instance === instance,
|
|
90
|
-
)) {
|
|
91
|
-
return holder
|
|
92
|
-
}
|
|
93
|
-
return null
|
|
86
|
+
return this.manager.findHolder((h) => h.instance === instance) ?? null
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
findDependents(instanceName: string): string[] {
|
|
97
|
-
|
|
98
|
-
for (const [name, holder] of this.manager.filter(() => true)) {
|
|
99
|
-
if (holder.deps.has(instanceName)) {
|
|
100
|
-
dependents.push(name)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return dependents
|
|
90
|
+
return this.manager.getDependents(instanceName)
|
|
104
91
|
}
|
|
105
92
|
}
|
package/src/token/registry.mts
CHANGED
|
@@ -49,6 +49,27 @@ export class Registry {
|
|
|
49
49
|
delete(token: InjectionToken<any, any>) {
|
|
50
50
|
this.factories.delete(token.id)
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Updates the scope of an already registered factory.
|
|
55
|
+
* This is useful when you need to dynamically change a service's scope
|
|
56
|
+
* (e.g., when a singleton controller has request-scoped dependencies).
|
|
57
|
+
*
|
|
58
|
+
* @param token The injection token to update
|
|
59
|
+
* @param scope The new scope to set
|
|
60
|
+
* @returns true if the scope was updated, false if the token was not found
|
|
61
|
+
*/
|
|
62
|
+
updateScope(token: InjectionToken<any, any>, scope: InjectableScope): boolean {
|
|
63
|
+
const factory = this.factories.get(token.id)
|
|
64
|
+
if (factory) {
|
|
65
|
+
factory.scope = scope
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
if (this.parent) {
|
|
69
|
+
return this.parent.updateScope(token, scope)
|
|
70
|
+
}
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
52
73
|
}
|
|
53
74
|
|
|
54
75
|
export const globalRegistry = new Registry()
|