= toSignal(
+ this.router.events.pipe(
+ filter(
+ (event) =>
+ event instanceof NavigationStart ||
+ event instanceof NavigationEnd ||
+ event instanceof NavigationCancel ||
+ event instanceof NavigationError
+ ),
+ map((event) => event instanceof NavigationStart)
+ ),
+ { initialValue: false }
+ );
+
constructor() {
this.mediaQueryService.currentQueryMatch$.subscribe(console.log);
}
@@ -69,7 +98,7 @@ export class App {
content: 'This is where we have the display content directive!',
tourItem: 'display-content',
beforeVisible: () => {
- return from(this.router.navigate(['../../en','layout']));
+ return from(this.router.navigate(['../../en', 'layout']));
},
},
]);
diff --git a/apps/opensource/src/lazy-loaded/lib-1/i18n/assets/en.json b/apps/opensource/src/lazy-loaded/lib-1/i18n/assets/en.json
new file mode 100644
index 0000000..b2d15df
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-1/i18n/assets/en.json
@@ -0,0 +1,7 @@
+{
+ "lib-1": {
+ "start": {
+ "body": "AngularJS (also known as Angular 1) is a discontinued free and open-source JavaScript-based web framework for developing single-page applications. It was maintained mainly by Google and a community of individuals and corporations. It aimed to simplify both the development and the testing of such applications by providing a framework for client-side model–view–controller (MVC) and model–view–viewmodel (MVVM) architectures, along with components commonly used in web applications and progressive web applications.
AngularJS was used as the frontend of the MEAN stack, that consisted of MongoDB database, Express.js web application server framework, AngularJS itself (or Angular), and Node.js server runtime environment.
As of January 1, 2022, Google no longer updates AngularJS to fix security, browser compatibility, or jQuery issues. The Angular team recommends upgrading to Angular (v2+) as the best path forward, but they also provided some other options.
diff --git a/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.scss b/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.scss
new file mode 100644
index 0000000..d0a69bc
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.scss
@@ -0,0 +1,3 @@
+:host {
+ display: block;
+}
diff --git a/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.ts b/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.ts
new file mode 100644
index 0000000..aa3861a
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-1/pages/lib-1-start/lib-1-start.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+import { TranslatePipe } from '@ngx-translate/core';
+
+@Component({
+ selector: 'lib-1-start-page',
+ templateUrl: './lib-1-start.component.html',
+ styleUrl: './lib-1-start.component.scss',
+ imports: [TranslatePipe],
+})
+export class Lib1StartComponent {}
diff --git a/apps/opensource/src/lazy-loaded/lib-2/i18n/assets/en.json b/apps/opensource/src/lazy-loaded/lib-2/i18n/assets/en.json
new file mode 100644
index 0000000..c79f344
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/i18n/assets/en.json
@@ -0,0 +1,7 @@
+{
+ "lib-2": {
+ "start": {
+ "body": "Angular (also referred to as Angular 2+) is a TypeScript-based free and open-source single-page web application framework. It is developed by Google and by a community of individuals and corporations. Angular is a complete rewrite from the same team that built AngularJS. The Angular ecosystem consists of a diverse group of over 1.7 million developers, library authors, and content creators. According to the Stack Overflow Developer Survey, Angular is one of the most commonly used web frameworks.
Google designed Angular as a ground-up rewrite of AngularJS. Unlike AngularJS, Angular does not have a concept of \"scope\" or controllers; instead, it uses a hierarchy of components as its primary architectural characteristic.[8] Angular has a different expression syntax, focusing on \"[ ]\" for property binding, and \"( )\" for event binding. Angular recommends the use of Microsoft's TypeScript language, which introduces features such as static typing, generics, and type annotations.
"
+ }
+ }
+}
diff --git a/apps/opensource/src/lazy-loaded/lib-2/i18n/const/index.ts b/apps/opensource/src/lazy-loaded/lib-2/i18n/const/index.ts
new file mode 100644
index 0000000..5ffd27c
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/i18n/const/index.ts
@@ -0,0 +1 @@
+export const TRANSLATION_PATHS = ['./assets/i18n/lazy-loaded/lib-2/'];
diff --git a/apps/opensource/src/lazy-loaded/lib-2/i18n/index.ts b/apps/opensource/src/lazy-loaded/lib-2/i18n/index.ts
new file mode 100644
index 0000000..e47ea3a
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/i18n/index.ts
@@ -0,0 +1 @@
+export * from './const';
diff --git a/apps/opensource/src/lazy-loaded/lib-2/index.ts b/apps/opensource/src/lazy-loaded/lib-2/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/apps/opensource/src/lazy-loaded/lib-2/lib-2.routes.ts b/apps/opensource/src/lazy-loaded/lib-2/lib-2.routes.ts
new file mode 100644
index 0000000..b001d7a
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/lib-2.routes.ts
@@ -0,0 +1,20 @@
+import { Routes } from '@angular/router';
+
+import { provideWithTranslations } from '@lib/ngx-i18n';
+
+import { TRANSLATION_PATHS } from './i18n';
+import { Lib2StartComponent } from './pages';
+import { startResolver } from './resolvers';
+
+export const LazyLoadedLib2Routes: Routes = [
+ provideWithTranslations(
+ {
+ path: '',
+ component: Lib2StartComponent,
+ resolve: {
+ data: startResolver,
+ },
+ },
+ TRANSLATION_PATHS
+ ),
+];
diff --git a/apps/opensource/src/lazy-loaded/lib-2/pages/index.ts b/apps/opensource/src/lazy-loaded/lib-2/pages/index.ts
new file mode 100644
index 0000000..fdbdc14
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/pages/index.ts
@@ -0,0 +1 @@
+export { Lib2StartComponent } from './lib-2-start/lib-2-start.component';
diff --git a/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.html b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.html
new file mode 100644
index 0000000..2c1cb2d
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.html
@@ -0,0 +1 @@
+
diff --git a/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.scss b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.scss
new file mode 100644
index 0000000..d0a69bc
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.scss
@@ -0,0 +1,3 @@
+:host {
+ display: block;
+}
diff --git a/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.ts b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.ts
new file mode 100644
index 0000000..1e2b399
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/pages/lib-2-start/lib-2-start.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+import { TranslatePipe } from '@ngx-translate/core';
+
+@Component({
+ selector: 'lib-2-start-page',
+ templateUrl: './lib-2-start.component.html',
+ styleUrl: './lib-2-start.component.scss',
+ imports: [TranslatePipe],
+})
+export class Lib2StartComponent {}
diff --git a/apps/opensource/src/lazy-loaded/lib-2/resolvers/index.ts b/apps/opensource/src/lazy-loaded/lib-2/resolvers/index.ts
new file mode 100644
index 0000000..4eaa7da
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/resolvers/index.ts
@@ -0,0 +1 @@
+export { startResolver } from './start/start.resolver';
diff --git a/apps/opensource/src/lazy-loaded/lib-2/resolvers/start/start.resolver.ts b/apps/opensource/src/lazy-loaded/lib-2/resolvers/start/start.resolver.ts
new file mode 100644
index 0000000..8148ef7
--- /dev/null
+++ b/apps/opensource/src/lazy-loaded/lib-2/resolvers/start/start.resolver.ts
@@ -0,0 +1,6 @@
+import { ResolveFn } from '@angular/router';
+import { delay, Observable, of } from 'rxjs';
+
+export const startResolver: ResolveFn> = (): Observable => {
+ return of(true).pipe(delay(1000));
+};
diff --git a/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.spec.ts b/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.spec.ts
new file mode 100644
index 0000000..2de65ce
--- /dev/null
+++ b/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.spec.ts
@@ -0,0 +1,105 @@
+import { HttpBackend } from '@angular/common/http';
+import { Injector, runInInjectionContext } from '@angular/core';
+import { subscribeSpyTo } from '@hirez_io/observer-spy';
+import { of, throwError } from 'rxjs';
+
+import { NgxI18nLoadingService } from '../../services';
+import {
+ NgxI18nClientToken,
+ NgxI18nConfigurationToken,
+ NgxI18nTranslationPathsToken,
+} from '../../tokens';
+
+import { NgxI18nMultiTranslationHttpLoader } from './multi-translation.loader';
+
+describe('NgxI18nMultiTranslationHttpLoader', () => {
+ const createLoader = (
+ loadingService: NgxI18nLoadingService,
+ client: { getTranslations: jest.Mock },
+ paths: string[]
+ ): NgxI18nMultiTranslationHttpLoader => {
+ const injector = Injector.create({
+ providers: [
+ { provide: NgxI18nLoadingService, useValue: loadingService },
+ { provide: NgxI18nClientToken, useValue: client },
+ { provide: NgxI18nConfigurationToken, useValue: {} },
+ { provide: HttpBackend, useValue: {} },
+ { provide: NgxI18nTranslationPathsToken, useValue: paths },
+ ],
+ });
+
+ return runInInjectionContext(injector, () => new NgxI18nMultiTranslationHttpLoader());
+ };
+
+ describe('getTranslation', () => {
+ it('should not persist a failed fetch to the translations store, so it can be retried when the next loader is called that has overlap for that path.', () => {
+ const failingPath = './assets/i18n/failing-path/';
+ const translations = { foo: 'bar' };
+
+ /**
+ * Denis: NgxI18nLoadingService is providedIn: 'root'. So in an actual app, a single instance
+ * is shared between the root-scoped loader and every lazy route's own loader (created via
+ * provideWithTranslations), which is what makes this cross-loader issues possible.
+ */
+ const loadingService = new NgxI18nLoadingService();
+
+ const failingClient = {
+ getTranslations: jest
+ .fn()
+ .mockReturnValue(throwError(() => new Error('network error'))),
+ };
+ const firstLoader = createLoader(loadingService, failingClient, [failingPath]);
+
+ subscribeSpyTo(firstLoader.getTranslation('nl'));
+
+ // Denis: the failed path should not be cached as "loaded".
+ expect(loadingService.getTranslations()[failingPath]).toBeUndefined();
+
+ const recoveredClient = {
+ getTranslations: jest.fn().mockReturnValue(of(translations)),
+ };
+ const secondLoader = createLoader(loadingService, recoveredClient, [
+ failingPath,
+ './assets/i18n/other-path/',
+ ]);
+
+ const secondSpy = subscribeSpyTo(secondLoader.getTranslation('nl'));
+
+ expect(recoveredClient.getTranslations).toHaveBeenCalledWith(
+ failingPath,
+ 'nl',
+ undefined
+ );
+ expect(secondSpy.getFirstValue()).toEqual(translations);
+ });
+
+ it('should persist a successful fetch to the translations store so it is reused by a later loader for an overlapping path.', () => {
+ const path = './assets/i18n/working-path/';
+ const translations = { foo: 'bar' };
+
+ const loadingService = new NgxI18nLoadingService();
+
+ const client = {
+ getTranslations: jest.fn().mockReturnValue(of(translations)),
+ };
+ const firstLoader = createLoader(loadingService, client, [path]);
+
+ subscribeSpyTo(firstLoader.getTranslation('nl'));
+
+ expect(loadingService.getTranslations()[path]).toEqual(translations);
+
+ const secondClient = {
+ getTranslations: jest.fn().mockReturnValue(of({})),
+ };
+ const secondLoader = createLoader(loadingService, secondClient, [
+ path,
+ './assets/i18n/other-path/',
+ ]);
+
+ subscribeSpyTo(secondLoader.getTranslation('nl'));
+
+ // Denis: the already-loaded path should come from the store, not a fresh fetch.
+ expect(secondClient.getTranslations).not.toHaveBeenCalledWith(path, 'nl', undefined);
+ });
+ });
+});
diff --git a/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.ts b/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.ts
index d9995e8..114101f 100644
--- a/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.ts
+++ b/libs/angular/i18n/src/lib/loader/multi-translation/multi-translation.loader.ts
@@ -64,6 +64,7 @@ export class NgxI18nMultiTranslationHttpLoader implements TranslateLoader {
translations: availableTranslations[path],
fromStore,
path,
+ failed: false,
});
} else {
// Iben: If the translations aren't available in the store, we fetch them from the server
@@ -84,6 +85,7 @@ export class NgxI18nMultiTranslationHttpLoader implements TranslateLoader {
translations,
path,
fromStore,
+ failed: false,
};
}),
// Iben: In case the translation is not loaded, we log an error
@@ -99,11 +101,16 @@ export class NgxI18nMultiTranslationHttpLoader implements TranslateLoader {
this.translationLoadingService.markTranslationsLoadedAsFailed();
}
- // Iben: Return a translation loaded object so the translations service isn't broken
+ /**
+ * Denis (9/7/2026): Mark the result as 'failed' so it isn't persisted to the translations store.
+ * This will prevent it from being "permanently" treated as something that's already loaded.
+ * In doing so, a retry can still occur.
+ */
return of({
translations: {},
path,
fromStore,
+ failed: true,
});
})
);
@@ -115,10 +122,14 @@ export class NgxI18nMultiTranslationHttpLoader implements TranslateLoader {
this.translationsPaths.toString(),
forkJoin(requestedTranslations).pipe(
tap((translations) => {
- // Iben: Filter out the newly requested translations, and add them to the loaded translations store
+ /**
+ * Iben: Filter out the newly requested translations, and add them to the loaded translations store
+ *
+ * Denis (9/7/2026): Failed fetches are excluded so a retry can still happen for those translation paths.
+ */
this.translationLoadingService.addLoadedTranslations(
translations
- .filter((translation) => !translation.fromStore)
+ .filter((translation) => !translation.fromStore && !translation.failed)
.reduce((previous, next) => {
return {
...previous,
diff --git a/libs/angular/i18n/src/lib/services/i18n/i18n.service.spec.ts b/libs/angular/i18n/src/lib/services/i18n/i18n.service.spec.ts
index 6abc1e5..21041cc 100644
--- a/libs/angular/i18n/src/lib/services/i18n/i18n.service.spec.ts
+++ b/libs/angular/i18n/src/lib/services/i18n/i18n.service.spec.ts
@@ -11,7 +11,10 @@ const translateService: any = {
getLangs: jest.fn().mockReturnValue(['nl', 'en']),
getFallbackLang: jest.fn().mockReturnValue('nl'),
use: jest.fn(),
- reloadLang: jest.fn().mockReturnValue(of('nl')),
+ setTranslation: jest.fn(),
+ currentLoader: {
+ getTranslation: jest.fn().mockReturnValue(of({ SOME_KEY: 'some value' })),
+ },
get: jest.fn().mockReturnValue(of('something')),
instant: jest.fn().mockReturnValue('something'),
};
@@ -67,11 +70,25 @@ describe('NgxI18nService', () => {
});
describe('initI18n', () => {
- it('should set the language to use in the translateService & reload', (done) => {
+ it('should set the language to use in the translateService & fetch the new translations', (done) => {
subscriptions.push(
service.initI18n('nl').subscribe(() => {
expect(translateService.use).toHaveBeenCalledWith('nl');
- expect(translateService.reloadLang).toHaveBeenCalledWith('nl');
+ expect(translateService.currentLoader.getTranslation).toHaveBeenCalledWith(
+ 'nl'
+ );
+
+ done();
+ })
+ );
+ });
+
+ it('should only swap the translations, once they have been fetched', (done) => {
+ subscriptions.push(
+ service.initI18n('nl').subscribe(() => {
+ expect(translateService.setTranslation).toHaveBeenCalledWith('nl', {
+ SOME_KEY: 'some value',
+ });
done();
})
diff --git a/libs/angular/i18n/src/lib/services/i18n/i18n.service.ts b/libs/angular/i18n/src/lib/services/i18n/i18n.service.ts
index a132833..0a19860 100644
--- a/libs/angular/i18n/src/lib/services/i18n/i18n.service.ts
+++ b/libs/angular/i18n/src/lib/services/i18n/i18n.service.ts
@@ -1,6 +1,7 @@
import { inject, Injectable } from '@angular/core';
-import { TranslateService } from '@ngx-translate/core';
+import { TranslateService, TranslationObject } from '@ngx-translate/core';
import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
import { NgxI18nAbstractService } from '../../abstracts';
import { NgxI18nRootService } from '../root-i18n/root-i18n.service';
@@ -54,7 +55,22 @@ export class NgxI18nService implements NgxI18nAbstractService {
this.translateService.use(this.rootI18nService.currentLanguage);
- return this.translateService.reloadLang(language);
+ /**
+ * Denis (9/7/2026)
+ *
+ * `TranslateService.reloadLang` will delete translations immediately
+ * when attempting to fetch new ones. Because the currently rendered components all share the same store,
+ * this creates a short instant where it is left without translations.
+ *
+ * By only switching out the translations when they are loaded, we close that gap.
+ */
+ return this.translateService.currentLoader
+ .getTranslation(language)
+ .pipe(
+ tap((translations: TranslationObject) =>
+ this.translateService.setTranslation(language, translations)
+ )
+ );
}
/**
diff --git a/package-lock.json b/package-lock.json
index 2bd7e21..4f1e176 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3356,6 +3356,33 @@
}
}
},
+ "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
"node_modules/@compodoc/compodoc/node_modules/@babel/core": {
"version": "7.25.8",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz",
@@ -3716,6 +3743,36 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@compodoc/compodoc/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/@compodoc/compodoc/node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/@compodoc/compodoc/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
@@ -37341,6 +37398,24 @@
"rxjs": "7.8.1",
"source-map": "0.7.4"
}
+ },
+ "chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
}
}
},
@@ -37597,6 +37672,27 @@
"minipass": "^7.1.2"
}
},
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ },
+ "dependencies": {
+ "picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ }
+ }
+ },
"rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",