@sneat/logging 0.11.0 → 0.12.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/esm2022/index.js +1 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/chunk-load-error.handler.js +84 -0
- package/esm2022/lib/chunk-load-error.handler.js.map +1 -0
- package/esm2022/lib/sentry-setup.js +5 -2
- package/esm2022/lib/sentry-setup.js.map +1 -1
- package/index.d.ts +1 -0
- package/lib/chunk-load-error.handler.d.ts +41 -0
- package/lib/sentry-setup.d.ts +2 -1
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/index.js
CHANGED
package/esm2022/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/logging/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC","sourcesContent":["export * from './lib/sentry-setup';\nexport * from './lib/sneat-logging.module';\nexport * from './lib/analytics';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/logging/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC","sourcesContent":["export * from './lib/sentry-setup';\nexport * from './lib/chunk-load-error.handler';\nexport * from './lib/sneat-logging.module';\nexport * from './lib/analytics';\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ErrorHandler } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Delegating {@link ErrorHandler} that reloads the app once when a lazy-loaded
|
|
4
|
+
* chunk fails to load, and forwards every other error to an inner handler.
|
|
5
|
+
*
|
|
6
|
+
* After a deploy, the hash-named lazy chunks are replaced on the server. A tab
|
|
7
|
+
* that was already open is still running an older `main.js` that references the
|
|
8
|
+
* previous chunk hashes, so navigating to a lazy route fetches a chunk URL that
|
|
9
|
+
* now 404s. Firebase Hosting's SPA rewrite then returns `index.html` for the
|
|
10
|
+
* missing `.js`, and the browser throws a `ChunkLoadError` /
|
|
11
|
+
* "'text/html' is not a valid JavaScript MIME type". A single full reload pulls
|
|
12
|
+
* the current `index.html` + matching chunks and transparently recovers.
|
|
13
|
+
*
|
|
14
|
+
* Non-chunk errors are delegated unchanged, so existing error reporting (e.g.
|
|
15
|
+
* the Sentry handler) keeps working. A short time-based guard (in
|
|
16
|
+
* `sessionStorage`) prevents a reload loop when a chunk is genuinely broken.
|
|
17
|
+
*
|
|
18
|
+
* Alternative considered: an Angular service worker (ngsw) + `SwUpdate` would
|
|
19
|
+
* largely *prevent* this by caching a consistent version set and prompting on a
|
|
20
|
+
* new deploy. We deliberately do NOT add a service worker just for this — the
|
|
21
|
+
* apps currently ship without one, and a SW brings its own caching/staleness
|
|
22
|
+
* complexity. If a PWA service worker is adopted later (for offline/installable
|
|
23
|
+
* support), this handler can stay as a cheap backstop.
|
|
24
|
+
*/
|
|
25
|
+
export class ChunkLoadErrorHandler {
|
|
26
|
+
static { this.reloadKey = 'sneat:chunk-reload-at'; }
|
|
27
|
+
static { this.reloadGuardMs = 10_000; }
|
|
28
|
+
constructor(delegate) {
|
|
29
|
+
this.delegate = delegate;
|
|
30
|
+
}
|
|
31
|
+
handleError(error) {
|
|
32
|
+
if (ChunkLoadErrorHandler.isChunkLoadError(error) &&
|
|
33
|
+
this.reloadOnceWithinGuard()) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.delegate.handleError(error);
|
|
37
|
+
}
|
|
38
|
+
/** Reloads at most once per guard window; returns true if a reload was triggered. */
|
|
39
|
+
reloadOnceWithinGuard() {
|
|
40
|
+
let lastReloadAt = 0;
|
|
41
|
+
try {
|
|
42
|
+
lastReloadAt = Number(sessionStorage.getItem(ChunkLoadErrorHandler.reloadKey) ?? 0);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// sessionStorage may be unavailable (e.g. private mode) — fall through.
|
|
46
|
+
}
|
|
47
|
+
if (Date.now() - lastReloadAt <= ChunkLoadErrorHandler.reloadGuardMs) {
|
|
48
|
+
// Reloaded very recently and still failing -> stop to avoid a loop.
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
sessionStorage.setItem(ChunkLoadErrorHandler.reloadKey, String(Date.now()));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// ignore storage failures; still attempt the reload below.
|
|
56
|
+
}
|
|
57
|
+
location.reload();
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
static isChunkLoadError(error) {
|
|
61
|
+
const err = error;
|
|
62
|
+
const name = err?.name ?? '';
|
|
63
|
+
const message = err?.message ?? String(error);
|
|
64
|
+
return (name === 'ChunkLoadError' ||
|
|
65
|
+
/Failed to fetch dynamically imported module/i.test(message) ||
|
|
66
|
+
/error loading dynamically imported module/i.test(message) ||
|
|
67
|
+
/Loading chunk [\w-]+ failed/i.test(message) ||
|
|
68
|
+
/Importing a module script failed/i.test(message) ||
|
|
69
|
+
/is not a valid JavaScript MIME type/i.test(message));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Provides a baseline {@link ChunkLoadErrorHandler} wrapping Angular's default
|
|
74
|
+
* `ErrorHandler`. Apps get this via `getStandardSneatProviders`. When Sentry is
|
|
75
|
+
* configured its own (also chunk-aware) handler is provided later and wins; this
|
|
76
|
+
* baseline covers apps without Sentry.
|
|
77
|
+
*/
|
|
78
|
+
export function provideChunkLoadErrorRecovery() {
|
|
79
|
+
return {
|
|
80
|
+
provide: ErrorHandler,
|
|
81
|
+
useFactory: () => new ChunkLoadErrorHandler(new ErrorHandler()),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=chunk-load-error.handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-load-error.handler.js","sourceRoot":"","sources":["../../../../../libs/logging/src/lib/chunk-load-error.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAY,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,qBAAqB;aACT,cAAS,GAAG,uBAAuB,CAAC;aACpC,kBAAa,GAAG,MAAM,CAAC;IAE/C,YAA6B,QAAsB;QAAtB,aAAQ,GAAR,QAAQ,CAAc;IAAG,CAAC;IAEvD,WAAW,CAAC,KAAc;QACzB,IACC,qBAAqB,CAAC,gBAAgB,CAAC,KAAK,CAAC;YAC7C,IAAI,CAAC,qBAAqB,EAAE,EAC3B,CAAC;YACF,OAAO;QACR,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,qFAAqF;IAC7E,qBAAqB;QAC5B,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC;YACJ,YAAY,GAAG,MAAM,CACpB,cAAc,CAAC,OAAO,CAAC,qBAAqB,CAAC,SAAS,CAAC,IAAI,CAAC,CAC5D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,wEAAwE;QACzE,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,IAAI,qBAAqB,CAAC,aAAa,EAAE,CAAC;YACtE,oEAAoE;YACpE,OAAO,KAAK,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACJ,cAAc,CAAC,OAAO,CACrB,qBAAqB,CAAC,SAAS,EAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAClB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,2DAA2D;QAC5D,CAAC;QACD,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACb,CAAC;IAEO,MAAM,CAAC,gBAAgB,CAAC,KAAc;QAC7C,MAAM,GAAG,GAAG,KAAmD,CAAC;QAChE,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CACN,IAAI,KAAK,gBAAgB;YACzB,8CAA8C,CAAC,IAAI,CAAC,OAAO,CAAC;YAC5D,4CAA4C,CAAC,IAAI,CAAC,OAAO,CAAC;YAC1D,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC;YAC5C,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC;YACjD,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC,CACpD,CAAC;IACH,CAAC;;AAGF;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B;IAC5C,OAAO;QACN,OAAO,EAAE,YAAY;QACrB,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,CAAC,IAAI,YAAY,EAAE,CAAC;KAC/D,CAAC;AACH,CAAC","sourcesContent":["import { ErrorHandler, Provider } from '@angular/core';\n\n/**\n * Delegating {@link ErrorHandler} that reloads the app once when a lazy-loaded\n * chunk fails to load, and forwards every other error to an inner handler.\n *\n * After a deploy, the hash-named lazy chunks are replaced on the server. A tab\n * that was already open is still running an older `main.js` that references the\n * previous chunk hashes, so navigating to a lazy route fetches a chunk URL that\n * now 404s. Firebase Hosting's SPA rewrite then returns `index.html` for the\n * missing `.js`, and the browser throws a `ChunkLoadError` /\n * \"'text/html' is not a valid JavaScript MIME type\". A single full reload pulls\n * the current `index.html` + matching chunks and transparently recovers.\n *\n * Non-chunk errors are delegated unchanged, so existing error reporting (e.g.\n * the Sentry handler) keeps working. A short time-based guard (in\n * `sessionStorage`) prevents a reload loop when a chunk is genuinely broken.\n *\n * Alternative considered: an Angular service worker (ngsw) + `SwUpdate` would\n * largely *prevent* this by caching a consistent version set and prompting on a\n * new deploy. We deliberately do NOT add a service worker just for this — the\n * apps currently ship without one, and a SW brings its own caching/staleness\n * complexity. If a PWA service worker is adopted later (for offline/installable\n * support), this handler can stay as a cheap backstop.\n */\nexport class ChunkLoadErrorHandler implements ErrorHandler {\n\tprivate static readonly reloadKey = 'sneat:chunk-reload-at';\n\tprivate static readonly reloadGuardMs = 10_000;\n\n\tconstructor(private readonly delegate: ErrorHandler) {}\n\n\thandleError(error: unknown): void {\n\t\tif (\n\t\t\tChunkLoadErrorHandler.isChunkLoadError(error) &&\n\t\t\tthis.reloadOnceWithinGuard()\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tthis.delegate.handleError(error);\n\t}\n\n\t/** Reloads at most once per guard window; returns true if a reload was triggered. */\n\tprivate reloadOnceWithinGuard(): boolean {\n\t\tlet lastReloadAt = 0;\n\t\ttry {\n\t\t\tlastReloadAt = Number(\n\t\t\t\tsessionStorage.getItem(ChunkLoadErrorHandler.reloadKey) ?? 0,\n\t\t\t);\n\t\t} catch {\n\t\t\t// sessionStorage may be unavailable (e.g. private mode) — fall through.\n\t\t}\n\t\tif (Date.now() - lastReloadAt <= ChunkLoadErrorHandler.reloadGuardMs) {\n\t\t\t// Reloaded very recently and still failing -> stop to avoid a loop.\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tsessionStorage.setItem(\n\t\t\t\tChunkLoadErrorHandler.reloadKey,\n\t\t\t\tString(Date.now()),\n\t\t\t);\n\t\t} catch {\n\t\t\t// ignore storage failures; still attempt the reload below.\n\t\t}\n\t\tlocation.reload();\n\t\treturn true;\n\t}\n\n\tprivate static isChunkLoadError(error: unknown): boolean {\n\t\tconst err = error as { name?: string; message?: string } | null;\n\t\tconst name = err?.name ?? '';\n\t\tconst message = err?.message ?? String(error);\n\t\treturn (\n\t\t\tname === 'ChunkLoadError' ||\n\t\t\t/Failed to fetch dynamically imported module/i.test(message) ||\n\t\t\t/error loading dynamically imported module/i.test(message) ||\n\t\t\t/Loading chunk [\\w-]+ failed/i.test(message) ||\n\t\t\t/Importing a module script failed/i.test(message) ||\n\t\t\t/is not a valid JavaScript MIME type/i.test(message)\n\t\t);\n\t}\n}\n\n/**\n * Provides a baseline {@link ChunkLoadErrorHandler} wrapping Angular's default\n * `ErrorHandler`. Apps get this via `getStandardSneatProviders`. When Sentry is\n * configured its own (also chunk-aware) handler is provided later and wins; this\n * baseline covers apps without Sentry.\n */\nexport function provideChunkLoadErrorRecovery(): Provider {\n\treturn {\n\t\tprovide: ErrorHandler,\n\t\tuseFactory: () => new ChunkLoadErrorHandler(new ErrorHandler()),\n\t};\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { inject, provideAppInitializer, ErrorHandler } from '@angular/core';
|
|
2
2
|
import { TraceService, init, createErrorHandler } from '@sentry/angular';
|
|
3
3
|
import { Router } from '@angular/router';
|
|
4
|
+
import { ChunkLoadErrorHandler } from './chunk-load-error.handler';
|
|
4
5
|
export const provideSentryAppInitializer = (options) => {
|
|
5
6
|
initSentry(options);
|
|
6
7
|
return [
|
|
@@ -20,10 +21,12 @@ const sentryAppInitializerProviders = [
|
|
|
20
21
|
deps: [Router],
|
|
21
22
|
},
|
|
22
23
|
{
|
|
24
|
+
// Recover from stale lazy-chunk loads after a deploy, delegating all other
|
|
25
|
+
// errors to Sentry's handler (see ChunkLoadErrorHandler doc).
|
|
23
26
|
provide: ErrorHandler,
|
|
24
|
-
useValue: createErrorHandler({
|
|
27
|
+
useValue: new ChunkLoadErrorHandler(createErrorHandler({
|
|
25
28
|
showDialog: true,
|
|
26
|
-
}),
|
|
29
|
+
})),
|
|
27
30
|
},
|
|
28
31
|
];
|
|
29
32
|
//# sourceMappingURL=sentry-setup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry-setup.js","sourceRoot":"","sources":["../../../../../libs/logging/src/lib/sentry-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"sentry-setup.js","sourceRoot":"","sources":["../../../../../libs/logging/src/lib/sentry-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,OAAuB,EAAE,EAAE;IACrE,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO;QACL,GAAG,6BAA6B;QAChC,qBAAqB,CAAC,GAAG,EAAE;YACzB,MAAM,CAAC,YAAY,CAAC,CAAC;QACvB,CAAC,CAAC;KACH,CAAC;AACJ,CAAC,CAAC;AAEF,SAAS,UAAU,CAAC,OAAuB;IACzC,+BAA+B;IAC/B,IAAI,CAAC,OAAO,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,6BAA6B,GAAG;IACpC;QACE,OAAO,EAAE,YAAY;QACrB,IAAI,EAAE,CAAC,MAAM,CAAC;KACf;IACD;QACE,2EAA2E;QAC3E,8DAA8D;QAC9D,OAAO,EAAE,YAAY;QACrB,QAAQ,EAAE,IAAI,qBAAqB,CACjC,kBAAkB,CAAC;YACjB,UAAU,EAAE,IAAI;SACjB,CAAC,CACH;KACF;CACF,CAAC","sourcesContent":["import { inject, provideAppInitializer, ErrorHandler } from '@angular/core';\nimport { TraceService, init, createErrorHandler } from '@sentry/angular';\nimport { Router } from '@angular/router';\nimport { BrowserOptions } from '@sentry/browser';\nimport { ChunkLoadErrorHandler } from './chunk-load-error.handler';\n\nexport const provideSentryAppInitializer = (options: BrowserOptions) => {\n initSentry(options);\n return [\n ...sentryAppInitializerProviders,\n provideAppInitializer(() => {\n inject(TraceService);\n }),\n ];\n};\n\nfunction initSentry(options: BrowserOptions): void {\n // console.log('initSentry()');\n init(options);\n}\n\nconst sentryAppInitializerProviders = [\n {\n provide: TraceService,\n deps: [Router],\n },\n {\n // Recover from stale lazy-chunk loads after a deploy, delegating all other\n // errors to Sentry's handler (see ChunkLoadErrorHandler doc).\n provide: ErrorHandler,\n useValue: new ChunkLoadErrorHandler(\n createErrorHandler({\n showDialog: true,\n }),\n ),\n },\n];\n"]}
|
package/index.d.ts
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ErrorHandler, Provider } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Delegating {@link ErrorHandler} that reloads the app once when a lazy-loaded
|
|
4
|
+
* chunk fails to load, and forwards every other error to an inner handler.
|
|
5
|
+
*
|
|
6
|
+
* After a deploy, the hash-named lazy chunks are replaced on the server. A tab
|
|
7
|
+
* that was already open is still running an older `main.js` that references the
|
|
8
|
+
* previous chunk hashes, so navigating to a lazy route fetches a chunk URL that
|
|
9
|
+
* now 404s. Firebase Hosting's SPA rewrite then returns `index.html` for the
|
|
10
|
+
* missing `.js`, and the browser throws a `ChunkLoadError` /
|
|
11
|
+
* "'text/html' is not a valid JavaScript MIME type". A single full reload pulls
|
|
12
|
+
* the current `index.html` + matching chunks and transparently recovers.
|
|
13
|
+
*
|
|
14
|
+
* Non-chunk errors are delegated unchanged, so existing error reporting (e.g.
|
|
15
|
+
* the Sentry handler) keeps working. A short time-based guard (in
|
|
16
|
+
* `sessionStorage`) prevents a reload loop when a chunk is genuinely broken.
|
|
17
|
+
*
|
|
18
|
+
* Alternative considered: an Angular service worker (ngsw) + `SwUpdate` would
|
|
19
|
+
* largely *prevent* this by caching a consistent version set and prompting on a
|
|
20
|
+
* new deploy. We deliberately do NOT add a service worker just for this — the
|
|
21
|
+
* apps currently ship without one, and a SW brings its own caching/staleness
|
|
22
|
+
* complexity. If a PWA service worker is adopted later (for offline/installable
|
|
23
|
+
* support), this handler can stay as a cheap backstop.
|
|
24
|
+
*/
|
|
25
|
+
export declare class ChunkLoadErrorHandler implements ErrorHandler {
|
|
26
|
+
private readonly delegate;
|
|
27
|
+
private static readonly reloadKey;
|
|
28
|
+
private static readonly reloadGuardMs;
|
|
29
|
+
constructor(delegate: ErrorHandler);
|
|
30
|
+
handleError(error: unknown): void;
|
|
31
|
+
/** Reloads at most once per guard window; returns true if a reload was triggered. */
|
|
32
|
+
private reloadOnceWithinGuard;
|
|
33
|
+
private static isChunkLoadError;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Provides a baseline {@link ChunkLoadErrorHandler} wrapping Angular's default
|
|
37
|
+
* `ErrorHandler`. Apps get this via `getStandardSneatProviders`. When Sentry is
|
|
38
|
+
* configured its own (also chunk-aware) handler is provided later and wins; this
|
|
39
|
+
* baseline covers apps without Sentry.
|
|
40
|
+
*/
|
|
41
|
+
export declare function provideChunkLoadErrorRecovery(): Provider;
|
package/lib/sentry-setup.d.ts
CHANGED
|
@@ -2,12 +2,13 @@ import { ErrorHandler } from '@angular/core';
|
|
|
2
2
|
import { TraceService } from '@sentry/angular';
|
|
3
3
|
import { Router } from '@angular/router';
|
|
4
4
|
import { BrowserOptions } from '@sentry/browser';
|
|
5
|
+
import { ChunkLoadErrorHandler } from './chunk-load-error.handler';
|
|
5
6
|
export declare const provideSentryAppInitializer: (options: BrowserOptions) => ({
|
|
6
7
|
provide: typeof TraceService;
|
|
7
8
|
deps: (typeof Router)[];
|
|
8
9
|
useValue?: undefined;
|
|
9
10
|
} | {
|
|
10
11
|
provide: typeof ErrorHandler;
|
|
11
|
-
useValue:
|
|
12
|
+
useValue: ChunkLoadErrorHandler;
|
|
12
13
|
deps?: undefined;
|
|
13
14
|
} | import("@angular/core").EnvironmentProviders)[];
|