@adimm/x-injection 2.0.3 → 2.1.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 CHANGED
@@ -41,6 +41,7 @@ xInjection&nbsp;<a href="https://www.npmjs.com/package/@adimm/x-injection" targe
41
41
  - [Blueprints](#blueprints-1)
42
42
  - [Import Behavior](#import-behavior)
43
43
  - [isGlobal](#isglobal)
44
+ - [Definitions](#definitions)
44
45
  - [Advanced Usage](#advanced-usage)
45
46
  - [Events](#events)
46
47
  - [Middlewares](#middlewares)
@@ -309,7 +310,7 @@ The below list shows them in order of priority _(highest to lowest)_, meaning th
309
310
  3. By providing the [defaultScope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#defaultscope) property when initializing a `ProviderModule`:
310
311
  ```ts
311
312
  const RainModuleDef = new ProviderModuleDef({
312
- identifier: 'RainModule',
313
+ id: 'RainModule',
313
314
  defaultScope: InjectionScope.Transient,
314
315
  });
315
316
  ```
@@ -408,6 +409,7 @@ A [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToke
408
409
  const THEY_MAY_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: 'Maybe they know?' };
409
410
 
410
411
  // As you can see we now have 2 different ProviderTokens which use the same `provide` key.
412
+ // This means that resolving the `CONSTANT_SECRET_PROVIDER` will return an array of strings.
411
413
  ```
412
414
 
413
415
  - [ProviderFactoryToken](https://adimarianmutu.github.io/x-injection/types/ProviderFactoryToken.html)
@@ -475,6 +477,45 @@ MyModule.update.addProvider(PizzaService, true);
475
477
  MyModule.update.addProvider({ provide: HumanService, useClass: FemaleService });
476
478
  ```
477
479
 
480
+ Now you may probably ask yourself `If we import with the 'addImport' method a new module into an already imported module, will we have access to the providers of that newly imported module?`
481
+
482
+ The ansuwer is `yes`, we do have access thanks to the _dynamic_ nature of the `ProviderModule` class. Meaning that doing the following will work as expected:
483
+
484
+ ```ts
485
+ const InnerModule = ProviderModule.create({
486
+ id: 'InnerModule',
487
+ providers: [FirstService],
488
+ exports: [FirstService],
489
+ });
490
+
491
+ const OuterModule = ProviderModule.create({
492
+ id: 'OuterModule',
493
+ imports: [InnerModule],
494
+ });
495
+
496
+ const UnknownModule = ProviderModule.create({
497
+ id: 'UnknownModule',
498
+ providers: [SecondService],
499
+ exports: [SecondService],
500
+ });
501
+
502
+ InnerModule.update.addImport(UnknownModule, true); // Don't forget to provide `true` to the `addToExports` optional parameter!
503
+
504
+ const secondService = OuterModule.get(SecondService);
505
+ ```
506
+
507
+ The `OuterModule` has now access to the `UnknownModule` exports because it has been _dynamically_ imported _(later at run-time)_ into the `InnerModule` _(which has been imported into `OuterModule` during the `bootstrap` phase)_
508
+
509
+ Basically what happens is that when a `module` is imported, it takes care of _notify_ the `host` module if its _definiton_ changed.
510
+
511
+ > [!WARNING]
512
+ >
513
+ > This is a very powerful feature which comes in with some costs, _most of the time negligible_, but if you have an app which has thousand and thousand of `modules` doing this type of _dynamic_ behavior, you may incur in some performance issues which will require proper design to keep under control.
514
+ >
515
+ > _Most of the times the best solution is to leverage the nature of `blueprints`._
516
+
517
+ ---
518
+
478
519
  Sometimes you may actually want to _lazy_ import a `module` from a _file_, this can be done very easily with `xInjection`:
479
520
 
480
521
  ```ts
@@ -490,25 +531,21 @@ Sometimes you may actually want to _lazy_ import a `module` from a _file_, this
490
531
  >
491
532
  > This design pattern is _extremely_ powerful and useful when you may have a lot of `modules` initializing during the app bootstrap process as you can defer their initialization, or even never load them if the user never needs those specific `modules` _(this is mostly applicable on the client-side rather than the server-side)_
492
533
 
493
- Keep reading to understand how you can defer initialization of the modules _both_ `client-side` and `server-side`.
534
+ Keep reading to understand how you can defer initialization of the `modules` by using `blueprints`.
494
535
 
495
536
  ## Blueprints
496
537
 
497
538
  The [ProviderModuleBlueprint](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html) `class` main purpose is to encapsulate the `definitions` of a `Module`, when you do `ProviderModule.blueprint({...})` you are _not_ actually creating an instance of the `ProviderModule` class, but an instance of the `ProviderModuleBlueprint` class.
498
539
 
499
- Before diving into some examples, let's first clarify some important aspects about the behavior of the `blueprint` class.
540
+ > To better understand the above concept; imagine the `blueprint` as being a _dormant_ _(static)_ `module` which is not fully awake _(dynamic)_ till it is actually _imported_ into a `module`.
500
541
 
501
542
  ### Import Behavior
502
543
 
503
544
  Whenever you _import_ a `blueprint` into a `module`, it'll automatically be "transformed" to a `ProviderModule` instance by the engine, this step is crucial as a `blueprint` per se does not contain a _container_, just its _definitions_.
504
545
 
505
- This has a "gotcha", because the conversion happens internally, you'll "not" be able to get a reference to that imported module, not directly at least _(I may change this in a future patch)_, this means that if you don't have the reference of the `ProviderModule`, you can't remove it from the _imported_ module _(if you'll ever need to remove it)_.
506
-
507
546
  > [!NOTE]
508
547
  >
509
- > There's currently an workaround for this, you can `subscribe` to the `Import` event and get from there the `ProviderModule` instance, just make sure to check the `id` of the module via `module.toString()` when doing so.
510
-
511
- It is also important, to understand the _injection_ `scope` of an imported `blueprint`; we previously learned that when we import a `blueprint` into a `module` it automatically creates an instance of the `ProviderModule` from it, this means that all the `singleton` providers of the `blueprint` definition are now _scoped singleton_, where _scoped_ means _singleton in relation to their imported module_.
548
+ > Therefore it is important to understand the _injection_ `scope` of an imported `blueprint`; we previously learned that when we import a `blueprint` into a `module` it automatically creates an instance of the `ProviderModule` from it, this means that all the `singleton` providers of the `blueprint` definition are now _scoped singleton_, where _scoped_ means _singleton in relation to their imported module_.
512
549
 
513
550
  ### isGlobal
514
551
 
@@ -522,7 +559,7 @@ Now you can decide when to import it into the `AppModule` by doing `AppModule.ad
522
559
 
523
560
  ---
524
561
 
525
- I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules` and import them wherever you have to import only when needed;
562
+ I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules`;
526
563
 
527
564
  Why?
528
565
 
@@ -530,6 +567,31 @@ Why?
530
567
  - To reuse module _definitions across_ different parts of your application while maintaining isolated instances. _(when possible/applicable)_
531
568
  - To _compose modules flexibly_, allowing you to adjust module dependencies dynamically before instantiation.
532
569
 
570
+ ### Definitions
571
+
572
+ After you have provided the _initial_ `definitons` of a `blueprint`, you can always modify them with the [updateDefinition](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html#updatedefinition) `method`.
573
+
574
+ > [!NOTE]
575
+ >
576
+ > Updating the `definitions` of a `blueprint` after has been _imported_ into a `module` will **_not_** propagate those changes to the `module` where it has been imported.
577
+
578
+ ---
579
+
580
+ This means that we can actually _leverage_ the `blueprints` nature to _defer_ the actual initialization of a `module` by doing so:
581
+
582
+ ```ts
583
+ const UserModuleBp = ProviderModule.blueprint({
584
+ id: 'UserModule',
585
+ ...
586
+ });
587
+
588
+ // Later in your code
589
+
590
+ const UserModule = ProviderModule.create(UserModuleBp);
591
+ ```
592
+
593
+ The `UserModule` will be created only when _necessary_ and it'll use the same exact definitons which are available into the `UserModuleBp` at the time of the `create` invokation.
594
+
533
595
  ## Advanced Usage
534
596
 
535
597
  > [!WARNING]
@@ -746,7 +808,7 @@ const ApiModuleBp = new ProviderModule.blueprint({
746
808
 
747
809
  // Clone returns a `deep` clone and wraps all the `methods` to break their reference!
748
810
  const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
749
- identifier: 'ApiModuleMocked',
811
+ id: 'ApiModuleMocked',
750
812
  providers: [
751
813
  {
752
814
  provide: UserService,
package/dist/index.cjs CHANGED
@@ -17,11 +17,11 @@ var e, t = Object.defineProperty, i = Object.getOwnPropertyDescriptor, o = Objec
17
17
  InjectFromBase: () => N,
18
18
  Injectable: () => q,
19
19
  InjectionError: () => I,
20
- InjectionProviderModuleDisposedError: () => P,
20
+ InjectionProviderModuleDisposedError: () => w,
21
21
  InjectionProviderModuleError: () => b,
22
22
  InjectionProviderModuleMissingIdentifierError: () => x,
23
23
  InjectionProviderModuleMissingProviderError: () => E,
24
- InjectionProviderModuleUnknownProviderError: () => w,
24
+ InjectionProviderModuleUnknownProviderError: () => P,
25
25
  InjectionScope: () => u,
26
26
  MiddlewareType: () => a,
27
27
  MultiInject: () => U,
@@ -194,7 +194,7 @@ var g, I = class e extends Error {
194
194
  } catch {}
195
195
  super(`{ProviderModule.${i}} => ${t}`);
196
196
  }
197
- }, w = class e extends b {
197
+ }, P = class e extends b {
198
198
  static {
199
199
  n(this, "InjectionProviderModuleUnknownProviderError");
200
200
  }
@@ -202,7 +202,7 @@ var g, I = class e extends Error {
202
202
  constructor(e, t) {
203
203
  super(e, `The [${h.providerTokenToString(t)}] provider is of an unknown type!`);
204
204
  }
205
- }, P = class e extends b {
205
+ }, w = class e extends b {
206
206
  static {
207
207
  n(this, "InjectionProviderModuleDisposedError");
208
208
  }
@@ -270,7 +270,7 @@ var g, I = class e extends Error {
270
270
  return t.useFactory(...i);
271
271
  }))), "bind")
272
272
  } ], {bind: o} = i.find((({providerTypeMatches: t}) => t(e))) ?? {};
273
- if (!o) throw new w(this.providerModule, e);
273
+ if (!o) throw new P(this.providerModule, e);
274
274
  const r = h.isProviderIdentifier(e);
275
275
  let s = o();
276
276
  if (h.isValueToken(e) || r || (s = this.setBindingScope(e, s)), r) return;
@@ -388,7 +388,7 @@ var g, I = class e extends Error {
388
388
  return this.providerModule.moduleContainer;
389
389
  }
390
390
  get subscribe() {
391
- if (null === this.event$) throw new P(this.providerModule);
391
+ if (null === this.event$) throw new w(this.providerModule);
392
392
  return this.event$.subscribe.bind(this.event$);
393
393
  }
394
394
  moduleDef;
@@ -444,22 +444,24 @@ var g, I = class e extends Error {
444
444
  this.addProvider(i, t);
445
445
  }
446
446
  removeImport(e) {
447
- if (!this.moduleDef.imports.has(e)) return !1;
448
- if (!1 === this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveImport, e)) return !1;
449
- this.unsubscribeFromImportedModuleEvents(e);
450
- return this.providerModule.importedModuleContainers.get(e).dispose(), this.providerModule.importedModuleContainers.delete(e),
451
- this.moduleDef.imports.delete(e), this.emitEventSafely({
447
+ const t = g.isModule(e) ? e : this.getImportedModuleById(e);
448
+ if (!t) return !1;
449
+ if (!1 === this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveImport, t)) return !1;
450
+ this.unsubscribeFromImportedModuleEvents(t);
451
+ return this.providerModule.importedModuleContainers.get(t).dispose(), this.providerModule.importedModuleContainers.delete(t),
452
+ this.moduleDef.imports.delete(t), this.emitEventSafely({
452
453
  type: p.ImportRemoved,
453
- change: e
454
- }), this.removeFromExports(e), !0;
454
+ change: t
455
+ }), this.removeFromExports(t), !0;
455
456
  }
456
457
  removeProvider(e) {
457
- if (!this.moduleDef.providers.has(e)) return !1;
458
- return !1 !== this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveProvider, e) && (this.moduleDef.providers.delete(e),
459
- this.moduleContainer.container.unbindSync(h.toProviderIdentifier(e)), this.emitEventSafely({
458
+ const t = h.isProviderIdentifier(e) ? this.getProviderByIdentifier(e) : e;
459
+ if (!t) return !1;
460
+ return !1 !== this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveProvider, t) && (this.moduleDef.providers.delete(t),
461
+ this.moduleContainer.container.unbindSync(h.toProviderIdentifier(t)), this.emitEventSafely({
460
462
  type: p.ProviderRemoved,
461
- change: e
462
- }), this.removeFromExports(e), !0);
463
+ change: t
464
+ }), this.removeFromExports(t), !0);
463
465
  }
464
466
  removeFromExports(e) {
465
467
  if (!this.moduleDef.exports.has(e)) return !1;
@@ -475,6 +477,12 @@ var g, I = class e extends Error {
475
477
  change: e
476
478
  }), !0);
477
479
  }
480
+ getImportedModuleById(e) {
481
+ return this.moduleDef.imports.values().find((t => t.id === e));
482
+ }
483
+ getProviderByIdentifier(e) {
484
+ return this.moduleDef.providers.values().find((t => h.toProviderIdentifier(t) === e));
485
+ }
478
486
  emitEventSafely(e) {
479
487
  if (!this.emittingModules.has(this.providerModule)) try {
480
488
  this.emittingModules.add(this.providerModule), this.event$.emit(e);
@@ -535,7 +543,7 @@ var g, I = class e extends Error {
535
543
  i ? i.push(t) : this.middlewaresMap.set(e, [ t ]);
536
544
  }
537
545
  applyMiddlewares(e, ...t) {
538
- if (null === this.middlewaresMap) throw new P(this.providerModule);
546
+ if (null === this.middlewaresMap) throw new w(this.providerModule);
539
547
  const i = this.middlewaresMap.get(e);
540
548
  switch (e) {
541
549
  case a.BeforeAddImport:
@@ -709,7 +717,7 @@ var g, I = class e extends Error {
709
717
  if (!this.options.id || 0 === this.options.id.toString().trim().length) throw new x(this);
710
718
  }
711
719
  throwIfDisposed() {
712
- if (this.isDisposed) throw new P(this);
720
+ if (this.isDisposed) throw new w(this);
713
721
  }
714
722
  };
715
723