@fluojs/di 1.0.0-beta.2 → 1.0.0-beta.4

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.
@@ -9,15 +9,17 @@ export declare class Container {
9
9
  private readonly registrations;
10
10
  private readonly multiRegistrations;
11
11
  private readonly multiOverriddenTokens;
12
- private readonly requestCache;
13
- private readonly multiRequestCache;
12
+ private requestCache;
13
+ private multiRequestCache;
14
14
  private readonly multiSingletonCache;
15
15
  private readonly staleDisposalTasks;
16
16
  private readonly staleDisposalErrors;
17
17
  private readonly singletonCache;
18
- private readonly childScopes;
18
+ private readonly forwardRefTokenCache;
19
+ private childScopes;
19
20
  private disposePromise;
20
21
  private disposed;
22
+ private trackedByRoot;
21
23
  constructor(parent?: Container | undefined, requestScopeEnabled?: boolean, singletonCache?: Map<Token, Promise<unknown>>);
22
24
  /**
23
25
  * Registers providers in the current container scope.
@@ -78,6 +80,7 @@ export declare class Container {
78
80
  */
79
81
  dispose(): Promise<void>;
80
82
  private disposeAll;
83
+ private isDisposedInHierarchy;
81
84
  private hasMulti;
82
85
  private assertNoRegistrationConflict;
83
86
  private hasAncestorSingleRegistration;
@@ -94,11 +97,15 @@ export declare class Container {
94
97
  private resolveMultiProviderInstance;
95
98
  private resolveExistingProviderTarget;
96
99
  private resolveScopedOrSingletonInstance;
100
+ private getCachedScopedOrSingletonInstance;
97
101
  private shouldResolveFromRoot;
98
102
  private shouldResolveMultiProviderFromRoot;
99
103
  private resolveDepToken;
100
104
  private withTokenInChain;
101
105
  private root;
106
+ private ensureTrackedRequestScope;
107
+ private requestCacheForWrite;
108
+ private multiRequestCacheForWrite;
102
109
  private lookupProvider;
103
110
  /**
104
111
  * Resolve the cache map that should hold the instance for `provider`.
@@ -127,6 +134,7 @@ export declare class Container {
127
134
  private assertSingletonDependencyScopes;
128
135
  private resolveEffectiveProvider;
129
136
  private resolveProviderDependencyToken;
137
+ private resolveForwardRefToken;
130
138
  private resolveProviderDeps;
131
139
  private invalidateCachedEntry;
132
140
  }
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAW3E,OAAO,KAAK,EASV,QAAQ,EAET,MAAM,YAAY,CAAC;AAuIpB;;GAEG;AACH,qBAAa,SAAS;IAelB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAftC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0C;IAC7E,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAoB;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmD;IACrF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmD;IACvF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAiB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAwB;IACpD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAS;gBAGN,MAAM,CAAC,EAAE,SAAS,YAAA,EAClB,mBAAmB,UAAQ,EAC5C,cAAc,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAK/C;;;;;;;;;OASG;IACH,QAAQ,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IAyCxC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA6BxC;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAI1B;;;;;OAKG;IACH,kBAAkB,IAAI,SAAS;IAa/B;;;;;;;;;OASG;IACG,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW7C;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBhB,UAAU;IAgBxB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,4BAA4B;IAsBpC,OAAO,CAAC,6BAA6B;IAIrC,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,4BAA4B;IAIpC,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,qBAAqB;YAgBf,gBAAgB;YAehB,8BAA8B;IA+B5C,OAAO,CAAC,eAAe;YAgBT,kBAAkB;IAMhC,OAAO,CAAC,mCAAmC;YAoB7B,6BAA6B;YAc7B,4BAA4B;IA4B1C,OAAO,CAAC,6BAA6B;YAQvB,gCAAgC;IAuB9C,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,kCAAkC;YAI5B,eAAe;YAwBf,gBAAgB;IAiB9B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,oBAAoB;YAkBd,YAAY;YAaZ,0BAA0B;YA0B1B,8BAA8B;IAc5C,OAAO,CAAC,mBAAmB;YAWb,yBAAyB;IAMvC,OAAO,CAAC,qBAAqB;IAoB7B,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,YAAY;YAIN,WAAW;IA+BzB,OAAO,CAAC,+BAA+B;IAsBvC,OAAO,CAAC,wBAAwB;IA6BhC,OAAO,CAAC,8BAA8B;YAYxB,mBAAmB;IAUjC,OAAO,CAAC,qBAAqB;CA6C9B"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
package/dist/container.js CHANGED
@@ -119,15 +119,17 @@ export class Container {
119
119
  registrations = new Map();
120
120
  multiRegistrations = new Map();
121
121
  multiOverriddenTokens = new Set();
122
- requestCache = new Map();
123
- multiRequestCache = new Map();
122
+ requestCache;
123
+ multiRequestCache;
124
124
  multiSingletonCache = new Map();
125
125
  staleDisposalTasks = new Set();
126
126
  staleDisposalErrors = [];
127
127
  singletonCache;
128
- childScopes = new Set();
128
+ forwardRefTokenCache = new WeakMap();
129
+ childScopes;
129
130
  disposePromise;
130
131
  disposed = false;
132
+ trackedByRoot = false;
131
133
  constructor(parent, requestScopeEnabled = false, singletonCache) {
132
134
  this.parent = parent;
133
135
  this.requestScopeEnabled = requestScopeEnabled;
@@ -145,7 +147,7 @@ export class Container {
145
147
  * @throws {InvalidProviderError} When a provider definition is structurally invalid.
146
148
  */
147
149
  register(...providers) {
148
- if (this.disposed) {
150
+ if (this.isDisposedInHierarchy()) {
149
151
  throw new ContainerResolutionError('Container has been disposed and can no longer register providers.', {
150
152
  hint: 'Ensure providers are registered before calling container.dispose().'
151
153
  });
@@ -189,7 +191,7 @@ export class Container {
189
191
  * @throws {InvalidProviderError} When a provider definition is structurally invalid.
190
192
  */
191
193
  override(...providers) {
192
- if (this.disposed) {
194
+ if (this.isDisposedInHierarchy()) {
193
195
  throw new ContainerResolutionError('Container has been disposed and can no longer override providers.', {
194
196
  hint: 'Ensure overrides are applied before calling container.dispose().'
195
197
  });
@@ -228,14 +230,12 @@ export class Container {
228
230
  * @throws {ContainerResolutionError} When called after the container was disposed.
229
231
  */
230
232
  createRequestScope() {
231
- if (this.disposed) {
233
+ if (this.isDisposedInHierarchy()) {
232
234
  throw new ContainerResolutionError('Container has been disposed and can no longer create request scopes.', {
233
235
  hint: 'Create request scopes before calling container.dispose().'
234
236
  });
235
237
  }
236
- const child = new Container(this, true, this.root().singletonCache);
237
- this.root().childScopes.add(child);
238
- return child;
238
+ return new Container(this, true, this.root().singletonCache);
239
239
  }
240
240
 
241
241
  /**
@@ -249,7 +249,7 @@ export class Container {
249
249
  * @throws {CircularDependencyError} When provider dependency resolution detects a cycle.
250
250
  */
251
251
  async resolve(token) {
252
- if (this.disposed) {
252
+ if (this.isDisposedInHierarchy()) {
253
253
  throw new ContainerResolutionError('Container has been disposed and can no longer resolve providers.', {
254
254
  token,
255
255
  hint: 'Ensure all resolves complete before calling container.dispose().'
@@ -281,17 +281,24 @@ export class Container {
281
281
  async disposeAll() {
282
282
  try {
283
283
  // Dispose all live request-scope children first (root only)
284
- if (!this.parent && this.childScopes.size > 0) {
284
+ if (!this.parent && this.childScopes && this.childScopes.size > 0) {
285
285
  await Promise.all(Array.from(this.childScopes).map(child => child.dispose()));
286
286
  this.childScopes.clear();
287
287
  }
288
288
  await this.disposeCache(this.disposalCacheEntries());
289
289
  } finally {
290
- if (this.parent) {
291
- this.root().childScopes.delete(this);
290
+ if (this.parent && this.trackedByRoot) {
291
+ this.root().childScopes?.delete(this);
292
+ this.trackedByRoot = false;
292
293
  }
293
294
  }
294
295
  }
296
+ isDisposedInHierarchy() {
297
+ if (this.disposed) {
298
+ return true;
299
+ }
300
+ return this.parent?.isDisposedInHierarchy() ?? false;
301
+ }
295
302
  hasMulti(token) {
296
303
  if (this.multiRegistrations.has(token)) return true;
297
304
  return this.parent?.hasMulti(token) ?? false;
@@ -362,6 +369,10 @@ export class Container {
362
369
  if (provider.scope === 'transient') {
363
370
  return await this.withTokenInChain(token, chain, activeTokens, async (c, at) => this.instantiate(provider, c, at));
364
371
  }
372
+ const cachedInstance = this.getCachedScopedOrSingletonInstance(provider);
373
+ if (cachedInstance) {
374
+ return await cachedInstance;
375
+ }
365
376
  return await this.withTokenInChain(token, chain, activeTokens, async (c, at) => this.resolveScopedOrSingletonInstance(provider, c, at));
366
377
  }
367
378
  requireProvider(token) {
@@ -431,6 +442,15 @@ export class Container {
431
442
  }
432
443
  return cache.get(provider.provide);
433
444
  }
445
+ getCachedScopedOrSingletonInstance(provider) {
446
+ if (provider.scope !== Scope.DEFAULT) {
447
+ return undefined;
448
+ }
449
+ if (this.shouldResolveFromRoot(provider)) {
450
+ return this.root().getCachedScopedOrSingletonInstance(provider);
451
+ }
452
+ return this.cacheFor(provider).get(provider.provide);
453
+ }
434
454
  shouldResolveFromRoot(provider) {
435
455
  return provider.scope === Scope.DEFAULT && this.requestScopeEnabled && !this.registrations.has(provider.provide);
436
456
  }
@@ -446,7 +466,7 @@ export class Container {
446
466
  return this.resolveWithChain(innerToken, chain, activeTokens);
447
467
  }
448
468
  if (isForwardRef(depEntry)) {
449
- const resolvedToken = depEntry.forwardRef();
469
+ const resolvedToken = this.resolveForwardRefToken(depEntry);
450
470
  return this.resolveWithChain(resolvedToken, chain, activeTokens, /* allowForwardRef */true);
451
471
  }
452
472
  return this.resolveWithChain(depEntry, chain, activeTokens);
@@ -464,6 +484,25 @@ export class Container {
464
484
  root() {
465
485
  return this.parent ? this.parent.root() : this;
466
486
  }
487
+ ensureTrackedRequestScope() {
488
+ if (!this.requestScopeEnabled || !this.parent || this.trackedByRoot) {
489
+ return;
490
+ }
491
+ const root = this.root();
492
+ root.childScopes ??= new Set();
493
+ root.childScopes.add(this);
494
+ this.trackedByRoot = true;
495
+ }
496
+ requestCacheForWrite() {
497
+ this.ensureTrackedRequestScope();
498
+ this.requestCache ??= new Map();
499
+ return this.requestCache;
500
+ }
501
+ multiRequestCacheForWrite() {
502
+ this.ensureTrackedRequestScope();
503
+ this.multiRequestCache ??= new Map();
504
+ return this.multiRequestCache;
505
+ }
467
506
  lookupProvider(token) {
468
507
  const local = this.registrations.get(token);
469
508
  if (local) {
@@ -486,7 +525,7 @@ export class Container {
486
525
  cacheFor(provider) {
487
526
  if (provider.scope === Scope.DEFAULT) {
488
527
  if (this.requestScopeEnabled && this.registrations.has(provider.provide)) {
489
- return this.requestCache;
528
+ return this.requestCacheForWrite();
490
529
  }
491
530
  return this.root().singletonCache;
492
531
  }
@@ -497,12 +536,12 @@ export class Container {
497
536
  hint: 'Wrap the resolve call inside a request-scoped child container created via container.createRequestScope().'
498
537
  });
499
538
  }
500
- return this.requestCache;
539
+ return this.requestCacheForWrite();
501
540
  }
502
541
  multiCacheFor(provider) {
503
542
  if (provider.scope === Scope.DEFAULT) {
504
543
  if (this.requestScopeEnabled && this.hasLocalMultiProvider(provider)) {
505
- return this.multiRequestCache;
544
+ return this.multiRequestCacheForWrite();
506
545
  }
507
546
  return this.root().multiSingletonCache;
508
547
  }
@@ -513,15 +552,15 @@ export class Container {
513
552
  hint: 'Wrap the resolve call inside a request-scoped child container created via container.createRequestScope().'
514
553
  });
515
554
  }
516
- return this.multiRequestCache;
555
+ return this.multiRequestCacheForWrite();
517
556
  }
518
557
  hasLocalMultiProvider(provider) {
519
558
  return this.multiRegistrations.get(provider.provide)?.includes(provider) ?? false;
520
559
  }
521
560
  disposalCacheEntries() {
522
561
  if (this.parent) {
523
- const entries = Array.from(this.requestCache.entries());
524
- for (const [provider, promise] of this.multiRequestCache.entries()) {
562
+ const entries = Array.from(this.requestCache?.entries() ?? []);
563
+ for (const [provider, promise] of this.multiRequestCache?.entries() ?? []) {
525
564
  entries.push([provider, promise]);
526
565
  }
527
566
  return entries;
@@ -577,8 +616,8 @@ export class Container {
577
616
  }
578
617
  clearDisposalCaches() {
579
618
  if (this.parent) {
580
- this.requestCache.clear();
581
- this.multiRequestCache.clear();
619
+ this.requestCache?.clear();
620
+ this.multiRequestCache?.clear();
582
621
  return;
583
622
  }
584
623
  this.singletonCache.clear();
@@ -679,13 +718,21 @@ export class Container {
679
718
  }
680
719
  resolveProviderDependencyToken(depEntry) {
681
720
  if (isForwardRef(depEntry)) {
682
- return depEntry.forwardRef();
721
+ return this.resolveForwardRefToken(depEntry);
683
722
  }
684
723
  if (isOptionalToken(depEntry)) {
685
724
  return depEntry.token;
686
725
  }
687
726
  return depEntry;
688
727
  }
728
+ resolveForwardRefToken(forwardRefEntry) {
729
+ if (this.forwardRefTokenCache.has(forwardRefEntry)) {
730
+ return this.forwardRefTokenCache.get(forwardRefEntry);
731
+ }
732
+ const resolvedToken = forwardRefEntry.forwardRef();
733
+ this.forwardRefTokenCache.set(forwardRefEntry, resolvedToken);
734
+ return resolvedToken;
735
+ }
689
736
  async resolveProviderDeps(provider, chain, activeTokens) {
690
737
  const deps = new Array(provider.inject.length);
691
738
  for (const [index, entry] of provider.inject.entries()) {
@@ -694,7 +741,7 @@ export class Container {
694
741
  return deps;
695
742
  }
696
743
  invalidateCachedEntry(token, scope) {
697
- if (this.requestCache.has(token)) {
744
+ if (this.requestCache?.has(token)) {
698
745
  const cached = this.requestCache.get(token);
699
746
  if (cached) {
700
747
  this.scheduleStaleDisposal(cached);
@@ -720,12 +767,16 @@ export class Container {
720
767
  this.multiSingletonCache.delete(provider);
721
768
  }
722
769
  }
723
- for (const [provider, cached] of this.multiRequestCache.entries()) {
770
+ const multiRequestCache = this.multiRequestCache;
771
+ if (!multiRequestCache) {
772
+ return;
773
+ }
774
+ for (const [provider, cached] of multiRequestCache.entries()) {
724
775
  if (provider.provide !== token) {
725
776
  continue;
726
777
  }
727
778
  this.scheduleStaleDisposal(cached);
728
- this.multiRequestCache.delete(provider);
779
+ multiRequestCache.delete(provider);
729
780
  }
730
781
  }
731
782
  }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "container",
10
10
  "provider"
11
11
  ],
12
- "version": "1.0.0-beta.2",
12
+ "version": "1.0.0-beta.4",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@fluojs/core": "^1.0.0-beta.1"
39
+ "@fluojs/core": "^1.0.0-beta.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "vitest": "^3.2.4"