@edynamix/mf-isolation 0.0.21 → 0.0.23
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 +17 -1
- package/dist/angular-location.d.ts +12 -0
- package/dist/angular-location.d.ts.map +1 -0
- package/dist/angular-location.js +52 -0
- package/dist/angular-location.js.map +1 -0
- package/dist/angular.d.ts +1 -0
- package/dist/angular.d.ts.map +1 -1
- package/dist/angular.js +1 -0
- package/dist/angular.js.map +1 -1
- package/dist/vite-development.d.ts.map +1 -1
- package/dist/vite-development.js +13 -3
- package/dist/vite-development.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,7 +130,23 @@ const localHostNavigationUrl = new URL(
|
|
|
130
130
|
).href;
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
`hostNavigationUrl` selects the host environment. Load the browser entry from `localHostNavigationUrl`; the BFF serves the selected bundle and its host-owned logo assets through the secure local origin, avoiding mixed-content failures when the selected host is an HTTP localhost instance. Host API calls go to the selected host origin, while module API calls go to the explicit `moduleApiOrigin`. The BFF performs OIDC authorization code with PKCE, retains tokens and module API cookies in the development process, and forwards authenticated API requests without exposing either to module JavaScript. The refresh token is also kept in an HttpOnly `__Host-edyn_dev_refresh` cookie for the local origin, so one sign-in survives dev server restarts and switching between modules until the token expires. Host API requests keep the browser's navigation selection headers, which the host validates. Module API requests instead carry the selection the host last confirmed, read from `/api/navigation` responses and navigation stream events, through `X-Edyn-Navigation-Group-Id`, `X-Edyn-Navigation-Dealer-Ids` and, for a single dealer, `X-Edyn-Navigation-Dealer-Id`; module backends must still authorize those values before constructing module-specific session state. Request bodies are forwarded with a fixed length (up to 100 MB), because deployed module front ends reject chunked bodies. Before a module's first API call the BFF asks the host for the current selection, because the local shell starts the module beside the navigation rather than after it. A module's redirect to its legacy `/login` page leads to the development sign-in while signed out; signed in, it shows that the module API refused the session instead of reloading the module in a loop.
|
|
133
|
+
`hostNavigationUrl` selects the host environment. Load the browser entry from `localHostNavigationUrl`; the BFF serves the selected bundle and its host-owned logo assets through the secure local origin, avoiding mixed-content failures when the selected host is an HTTP localhost instance. Host API calls go to the selected host origin, while module API calls go to the explicit `moduleApiOrigin`. The BFF performs OIDC authorization code with PKCE, retains tokens and module API cookies in the development process, and forwards authenticated API requests without exposing either to module JavaScript. The refresh token is also kept in an HttpOnly `__Host-edyn_dev_refresh` cookie for the local origin, so one sign-in survives dev server restarts and switching between modules until the token expires. The sign-in service accepts each refresh token once, so requests the browser sent before a restart's rotated cookie reached it share that restore instead of spending the token again and clearing the new cookie. Host API requests keep the browser's navigation selection headers, which the host validates. Module API requests instead carry the selection the host last confirmed, read from `/api/navigation` responses and navigation stream events, through `X-Edyn-Navigation-Group-Id`, `X-Edyn-Navigation-Dealer-Ids` and, for a single dealer, `X-Edyn-Navigation-Dealer-Id`; module backends must still authorize those values before constructing module-specific session state. Request bodies are forwarded with a fixed length (up to 100 MB), because deployed module front ends reject chunked bodies. Before a module's first API call the BFF asks the host for the current selection, because the local shell starts the module beside the navigation rather than after it. A module's redirect to its legacy `/login` page leads to the development sign-in while signed out; signed in, it shows that the module API refused the session instead of reloading the module in a loop.
|
|
134
|
+
|
|
135
|
+
## Angular base path in any letter case
|
|
136
|
+
|
|
137
|
+
IIS serves a module at any letter case of its path, and old bookmarks keep the old case (`/AutoPoint/...`), but Angular's `Location` strips the base path only on an exact match. Install the module's `LocationStrategy` from this package so `/AutoPoint/...` and `/autopoint/...` both open the module, and the address keeps the case it was opened with (no redirect). Earlier names of the module's path can be passed as aliases:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { NgModule } from '@angular/core';
|
|
141
|
+
import { provideCaseInsensitiveBasePath } from '@edynamix/mf-isolation/angular';
|
|
142
|
+
|
|
143
|
+
@NgModule({
|
|
144
|
+
providers: [...provideCaseInsensitiveBasePath(['motcleanse'])],
|
|
145
|
+
})
|
|
146
|
+
export class AppModule {}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The base is the module's `APP_BASE_HREF` (or the document's `<base>`); a root base (`/`) is left untouched. A host that redirects case variants of a module's path to lowercase can stop doing so once the module installs this strategy.
|
|
134
150
|
|
|
135
151
|
## Angular CDK overlays
|
|
136
152
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PathLocationStrategy, PlatformLocation } from '@angular/common';
|
|
2
|
+
import { type StaticProvider } from '@angular/core';
|
|
3
|
+
export declare class CaseInsensitiveBasePathLocationStrategy extends PathLocationStrategy {
|
|
4
|
+
private readonly addressLocation;
|
|
5
|
+
private readonly base;
|
|
6
|
+
private readonly bases;
|
|
7
|
+
constructor(platformLocation: PlatformLocation, href?: string, aliases?: readonly string[]);
|
|
8
|
+
path(includeHash?: boolean): string;
|
|
9
|
+
prepareExternalUrl(internal: string): string;
|
|
10
|
+
}
|
|
11
|
+
export declare const provideCaseInsensitiveBasePath: (aliases?: readonly string[]) => StaticProvider[];
|
|
12
|
+
//# sourceMappingURL=angular-location.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angular-location.d.ts","sourceRoot":"","sources":["../src/angular-location.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC1G,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAOtE,qBAAa,uCAAwC,SAAQ,oBAAoB;IAC/E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmB;IACnD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAE9B,gBAAgB,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;IAOrF,IAAI,CAAC,WAAW,UAAQ,GAAG,MAAM;IAIjC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAItD;AAGD,eAAO,MAAM,8BAA8B,GAAI,UAAS,SAAS,MAAM,EAAO,KAAG,cAAc,EAO9F,CAAC","sourcesContent":["import { APP_BASE_HREF, LocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common';\nimport { Inject, Optional, type StaticProvider } from '@angular/core';\n\n// IIS serves a module at any letter case of its path, and old bookmarks keep the old case (/AutoPoint/...), but Angular's\n// Location strips the base only on an exact, case-sensitive match, so such an address showed the module's 404 page.\n// This strategy reads the address with the base in its configured case and writes addresses with the base in the case\n// the address bar shows, so /AutoPoint/... and /autopoint/... both work and stay as typed, without a redirect. Aliases\n// are earlier names of the module's path (motcleanse for autocleanse, for example) that open it the same way.\nexport class CaseInsensitiveBasePathLocationStrategy extends PathLocationStrategy {\n private readonly addressLocation: PlatformLocation;\n private readonly base: string;\n private readonly bases: readonly string[];\n\n constructor(platformLocation: PlatformLocation, href?: string, aliases: readonly string[] = []) {\n super(platformLocation, href);\n this.addressLocation = platformLocation;\n this.base = withoutTrailingSlash(this.getBaseHref());\n this.bases = this.base ? [this.base, ...aliases.map(aliasBase)] : [];\n }\n\n override path(includeHash = false): string {\n return replaceBase(super.path(includeHash), this.bases, this.base);\n }\n\n override prepareExternalUrl(internal: string): string {\n const addressBase = matchedBasePrefix(this.addressLocation.pathname, this.bases) ?? this.base;\n return replaceBase(super.prepareExternalUrl(internal), this.bases, addressBase);\n }\n}\n\n// The module's LocationStrategy: its base path (APP_BASE_HREF, or the document's <base>) in any letter case, plus aliases.\nexport const provideCaseInsensitiveBasePath = (aliases: readonly string[] = []): StaticProvider[] => [\n {\n provide: LocationStrategy,\n useFactory: (platformLocation: PlatformLocation, href: string | null) =>\n new CaseInsensitiveBasePathLocationStrategy(platformLocation, href ?? undefined, aliases),\n deps: [PlatformLocation, [new Optional(), new Inject(APP_BASE_HREF)]],\n },\n];\n\n// The start of path that is one of bases in any letter case, followed by the end of the path or a path, query, fragment\n// or matrix separator; undefined when path starts with none of them.\nconst matchedBasePrefix = (path: string, bases: readonly string[]): string | undefined => {\n for (const base of bases) {\n const prefix = path.slice(0, base.length);\n const next = path.charAt(base.length);\n if (base && prefix.toLowerCase() === base.toLowerCase() && (next === '' || '/?#;'.includes(next))) {\n return prefix;\n }\n }\n\n return undefined;\n};\n\nconst replaceBase = (path: string, bases: readonly string[], base: string): string => {\n const prefix = matchedBasePrefix(path, bases);\n return prefix === undefined ? path : base + path.slice(prefix.length);\n};\n\nconst aliasBase = (alias: string): string => `/${alias.replace(/^\\/+|\\/+$/g, '')}`;\n\nconst withoutTrailingSlash = (href: string): string => href.replace(/\\/+$/, '');\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { APP_BASE_HREF, LocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common';
|
|
2
|
+
import { Inject, Optional } from '@angular/core';
|
|
3
|
+
// IIS serves a module at any letter case of its path, and old bookmarks keep the old case (/AutoPoint/...), but Angular's
|
|
4
|
+
// Location strips the base only on an exact, case-sensitive match, so such an address showed the module's 404 page.
|
|
5
|
+
// This strategy reads the address with the base in its configured case and writes addresses with the base in the case
|
|
6
|
+
// the address bar shows, so /AutoPoint/... and /autopoint/... both work and stay as typed, without a redirect. Aliases
|
|
7
|
+
// are earlier names of the module's path (motcleanse for autocleanse, for example) that open it the same way.
|
|
8
|
+
export class CaseInsensitiveBasePathLocationStrategy extends PathLocationStrategy {
|
|
9
|
+
addressLocation;
|
|
10
|
+
base;
|
|
11
|
+
bases;
|
|
12
|
+
constructor(platformLocation, href, aliases = []) {
|
|
13
|
+
super(platformLocation, href);
|
|
14
|
+
this.addressLocation = platformLocation;
|
|
15
|
+
this.base = withoutTrailingSlash(this.getBaseHref());
|
|
16
|
+
this.bases = this.base ? [this.base, ...aliases.map(aliasBase)] : [];
|
|
17
|
+
}
|
|
18
|
+
path(includeHash = false) {
|
|
19
|
+
return replaceBase(super.path(includeHash), this.bases, this.base);
|
|
20
|
+
}
|
|
21
|
+
prepareExternalUrl(internal) {
|
|
22
|
+
const addressBase = matchedBasePrefix(this.addressLocation.pathname, this.bases) ?? this.base;
|
|
23
|
+
return replaceBase(super.prepareExternalUrl(internal), this.bases, addressBase);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// The module's LocationStrategy: its base path (APP_BASE_HREF, or the document's <base>) in any letter case, plus aliases.
|
|
27
|
+
export const provideCaseInsensitiveBasePath = (aliases = []) => [
|
|
28
|
+
{
|
|
29
|
+
provide: LocationStrategy,
|
|
30
|
+
useFactory: (platformLocation, href) => new CaseInsensitiveBasePathLocationStrategy(platformLocation, href ?? undefined, aliases),
|
|
31
|
+
deps: [PlatformLocation, [new Optional(), new Inject(APP_BASE_HREF)]],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
// The start of path that is one of bases in any letter case, followed by the end of the path or a path, query, fragment
|
|
35
|
+
// or matrix separator; undefined when path starts with none of them.
|
|
36
|
+
const matchedBasePrefix = (path, bases) => {
|
|
37
|
+
for (const base of bases) {
|
|
38
|
+
const prefix = path.slice(0, base.length);
|
|
39
|
+
const next = path.charAt(base.length);
|
|
40
|
+
if (base && prefix.toLowerCase() === base.toLowerCase() && (next === '' || '/?#;'.includes(next))) {
|
|
41
|
+
return prefix;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
};
|
|
46
|
+
const replaceBase = (path, bases, base) => {
|
|
47
|
+
const prefix = matchedBasePrefix(path, bases);
|
|
48
|
+
return prefix === undefined ? path : base + path.slice(prefix.length);
|
|
49
|
+
};
|
|
50
|
+
const aliasBase = (alias) => `/${alias.replace(/^\/+|\/+$/g, '')}`;
|
|
51
|
+
const withoutTrailingSlash = (href) => href.replace(/\/+$/, '');
|
|
52
|
+
//# sourceMappingURL=angular-location.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angular-location.js","sourceRoot":"","sources":["../src/angular-location.ts"],"names":[],"mappings":"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","sourcesContent":["import { APP_BASE_HREF, LocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common';\nimport { Inject, Optional, type StaticProvider } from '@angular/core';\n\n// IIS serves a module at any letter case of its path, and old bookmarks keep the old case (/AutoPoint/...), but Angular's\n// Location strips the base only on an exact, case-sensitive match, so such an address showed the module's 404 page.\n// This strategy reads the address with the base in its configured case and writes addresses with the base in the case\n// the address bar shows, so /AutoPoint/... and /autopoint/... both work and stay as typed, without a redirect. Aliases\n// are earlier names of the module's path (motcleanse for autocleanse, for example) that open it the same way.\nexport class CaseInsensitiveBasePathLocationStrategy extends PathLocationStrategy {\n private readonly addressLocation: PlatformLocation;\n private readonly base: string;\n private readonly bases: readonly string[];\n\n constructor(platformLocation: PlatformLocation, href?: string, aliases: readonly string[] = []) {\n super(platformLocation, href);\n this.addressLocation = platformLocation;\n this.base = withoutTrailingSlash(this.getBaseHref());\n this.bases = this.base ? [this.base, ...aliases.map(aliasBase)] : [];\n }\n\n override path(includeHash = false): string {\n return replaceBase(super.path(includeHash), this.bases, this.base);\n }\n\n override prepareExternalUrl(internal: string): string {\n const addressBase = matchedBasePrefix(this.addressLocation.pathname, this.bases) ?? this.base;\n return replaceBase(super.prepareExternalUrl(internal), this.bases, addressBase);\n }\n}\n\n// The module's LocationStrategy: its base path (APP_BASE_HREF, or the document's <base>) in any letter case, plus aliases.\nexport const provideCaseInsensitiveBasePath = (aliases: readonly string[] = []): StaticProvider[] => [\n {\n provide: LocationStrategy,\n useFactory: (platformLocation: PlatformLocation, href: string | null) =>\n new CaseInsensitiveBasePathLocationStrategy(platformLocation, href ?? undefined, aliases),\n deps: [PlatformLocation, [new Optional(), new Inject(APP_BASE_HREF)]],\n },\n];\n\n// The start of path that is one of bases in any letter case, followed by the end of the path or a path, query, fragment\n// or matrix separator; undefined when path starts with none of them.\nconst matchedBasePrefix = (path: string, bases: readonly string[]): string | undefined => {\n for (const base of bases) {\n const prefix = path.slice(0, base.length);\n const next = path.charAt(base.length);\n if (base && prefix.toLowerCase() === base.toLowerCase() && (next === '' || '/?#;'.includes(next))) {\n return prefix;\n }\n }\n\n return undefined;\n};\n\nconst replaceBase = (path: string, bases: readonly string[], base: string): string => {\n const prefix = matchedBasePrefix(path, bases);\n return prefix === undefined ? path : base + path.slice(prefix.length);\n};\n\nconst aliasBase = (alias: string): string => `/${alias.replace(/^\\/+|\\/+$/g, '')}`;\n\nconst withoutTrailingSlash = (href: string): string => href.replace(/\\/+$/, '');\n"]}
|
package/dist/angular.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProviderToken, StaticProvider } from '@angular/core';
|
|
2
2
|
export declare const provideMfOverlayRoot: (overlayRoot?: HTMLElement) => StaticProvider[];
|
|
3
3
|
export declare const provideMfOverlayRootFromToken: (overlayRootToken: ProviderToken<HTMLElement>) => StaticProvider[];
|
|
4
|
+
export { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';
|
|
4
5
|
//# sourceMappingURL=angular.d.ts.map
|
package/dist/angular.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular.d.ts","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA2SnE,eAAO,MAAM,oBAAoB,GAAI,cAAc,WAAW,KAAG,cAAc,EAkB9E,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,kBAAkB,aAAa,CAAC,WAAW,CAAC,KAAG,cAAc,EAE1G,CAAC","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport type { ProviderToken, StaticProvider } from '@angular/core';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"angular.d.ts","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA2SnE,eAAO,MAAM,oBAAoB,GAAI,cAAc,WAAW,KAAG,cAAc,EAkB9E,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,kBAAkB,aAAa,CAAC,WAAW,CAAC,KAAG,cAAc,EAE1G,CAAC;AAEF,OAAO,EAAE,uCAAuC,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport type { ProviderToken, StaticProvider } from '@angular/core';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
package/dist/angular.js
CHANGED
|
@@ -262,4 +262,5 @@ export const provideMfOverlayRoot = (overlayRoot) => {
|
|
|
262
262
|
export const provideMfOverlayRootFromToken = (overlayRootToken) => {
|
|
263
263
|
return createTokenProviders(overlayRootToken);
|
|
264
264
|
};
|
|
265
|
+
export { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';
|
|
265
266
|
//# sourceMappingURL=angular.js.map
|
package/dist/angular.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular.js","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport type { ProviderToken, StaticProvider } from '@angular/core';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"angular.js","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport type { ProviderToken, StaticProvider } from '@angular/core';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-development.d.ts","sourceRoot":"","sources":["../src/vite-development.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAUnC,eAAO,MAAM,mCAAmC,sCAAsC,CAAC;AAqFvF,MAAM,WAAW,8BAA8B;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAw3BD,eAAO,MAAM,sBAAsB,GAAI,SAAS,8BAA8B,KAAG,MAmBhF,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;AAExI,eAAO,MAAM,gCAAgC,GAAI,SAAS,8BAA8B,KAAG,2BAK1F,CAAC","sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { Plugin } from 'vite';\n\nconst SESSION_COOKIE = '__Host-edyn_dev_session';\nconst REFRESH_COOKIE = '__Host-edyn_dev_refresh';\nconst FLOW_COOKIE = '__Host-edyn_oidc_flow';\nconst LOGOUT_COOKIE = '__Host-edyn_oidc_logout';\nconst CALLBACK_PATH = '/callback';\nconst LOGGED_OUT_PATH = '/logged-out';\nconst LOGIN_PATH = '/api/dev-auth/login';\nconst LEGACY_LOGIN_PATH = '/login';\nexport const MF_DEVELOPMENT_HOST_NAVIGATION_PATH = '/api/dev-host-navigation/index.js';\nconst HOST_ICON_PATH = '/exsto-icon.svg';\nconst HOST_NAVIGATION_ASSET_PATHS = new Set([HOST_ICON_PATH, '/exsto-text.svg']);\nconst ICON_LINK_PATTERN = /<link\\b[^>]*\\brel=[\"'](?:shortcut\\s+)?icon[\"'][^>]*>\\s*/gi;\nconst MAX_HOST_NAVIGATION_BYTES = 5 * 1024 * 1024;\nconst MAX_HOST_NAVIGATION_ASSET_BYTES = 512 * 1024;\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst REFRESH_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;\nconst FLOW_MAX_AGE_SECONDS = 10 * 60;\nconst TOKEN_REFRESH_SKEW_MS = 30_000;\nconst NAVIGATION_GROUP_HEADER = 'X-Edyn-Navigation-Group-Id';\nconst NAVIGATION_DEALER_HEADER = 'X-Edyn-Navigation-Dealer-Id';\nconst NAVIGATION_DEALERS_HEADER = 'X-Edyn-Navigation-Dealer-Ids';\nconst NAVIGATION_HEADERS = [NAVIGATION_GROUP_HEADER, NAVIGATION_DEALER_HEADER, NAVIGATION_DEALERS_HEADER].map((name) => name.toLowerCase());\nconst MAX_NAVIGATION_EVENT_BYTES = 5 * 1024 * 1024;\nconst MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024;\nconst EVENT_SEPARATOR = /\\r?\\n\\r?\\n/;\nconst COOKIE_NAME_PATTERN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst COOKIE_VALUE_PATTERN = /^[\\x20-\\x7e]*$/;\nconst ALLOWED_HOST_API_PATHS = [\n '/api/auth/me',\n '/api/navigation',\n '/api/navigation/access-check',\n '/api/navigation/access-request',\n '/api/navigation/counter-stream',\n] as const;\n\ninterface IOidcClientConfiguration {\n clientId: string;\n issuer: string;\n scope: string;\n}\n\ninterface IOidcDiscovery {\n authorizationEndpoint: string;\n endSessionEndpoint: string;\n issuer: string;\n jwksUri: string;\n revocationEndpoint: string | null;\n tokenEndpoint: string;\n}\n\ninterface IOidcFlow {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n returnTo: string;\n state: string;\n}\n\ninterface IModuleNavigationContext {\n dealerIds: readonly number[];\n groupId: number;\n}\n\ninterface IModuleProxyContext {\n cookies: Map<string, string>;\n navigationContext: IModuleNavigationContext | null;\n}\n\ninterface ITokenSession {\n accessToken: string;\n expiresAt: number;\n idToken: string | null;\n moduleBootstrapTail: Promise<void>;\n moduleCookies: Map<string, string>;\n moduleNavigationContext: IModuleNavigationContext | null;\n navigationContextPromise?: Promise<void>;\n refreshPromise?: Promise<void>;\n refreshToken: string | null;\n}\n\ninterface ILogoutFlow {\n createdAt: number;\n idToken: string | null;\n}\n\ninterface ITokenResponse {\n accessToken: string;\n expiresIn: number;\n idToken: string | null;\n refreshToken: string | null;\n tokenType: string;\n}\n\nexport interface IMfDevelopmentBffPluginOptions {\n developmentOrigin?: string;\n hostNavigationUrl: string;\n moduleApiOrigin: string;\n moduleApiPath: string;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst requiredString = (value: Record<string, unknown>, key: string): string => {\n const candidate = value[key];\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response is missing ${key}.`);\n }\n return candidate;\n};\n\nconst optionalString = (value: Record<string, unknown>, key: string): string | null => {\n const candidate = value[key];\n if (candidate === undefined || candidate === null) {\n return null;\n }\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response contains an invalid ${key}.`);\n }\n return candidate;\n};\n\nconst absoluteHttpsUrl = (candidate: string, label: string, allowLocalHttp = false): URL => {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new Error(`${label} must be an absolute URL.`);\n }\n const isLocalHttp = allowLocalHttp && url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1');\n if (url.protocol !== 'https:' && !isLocalHttp) {\n throw new Error(`${label} must use HTTPS.`);\n }\n if (url.username || url.password || url.hash) {\n throw new Error(`${label} must not contain credentials or a fragment.`);\n }\n return url;\n};\n\nconst moduleApiPrefix = (candidate: string): string => {\n if (!/^\\/[a-z][a-z0-9-]*\\/api\\/$/.test(candidate)) {\n throw new Error('moduleApiPath must use the canonical /module/api/ boundary.');\n }\n return candidate;\n};\n\nconst base64Url = (size = 32): string => randomBytes(size).toString('base64url');\nconst codeChallengeOf = (verifier: string): string => createHash('sha256').update(verifier).digest('base64url');\n\nconst cookiesOf = (request: IncomingMessage): ReadonlyMap<string, string> => {\n const cookies = new Map<string, string>();\n for (const part of (request.headers.cookie ?? '').split(';')) {\n const separator = part.indexOf('=');\n if (separator <= 0) {\n continue;\n }\n const name = part.slice(0, separator).trim();\n const value = part.slice(separator + 1).trim();\n if (name && value) {\n cookies.set(name, value);\n }\n }\n return cookies;\n};\n\nconst cookie = (name: string, value: string, maxAge: number): string => `${name}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${maxAge}`;\n\nconst clearCookie = (name: string): string => cookie(name, '', 0);\n\n// The refresh token stays in an HttpOnly cookie for this origin, which every module's development server shares, so\n// one sign-in survives restarts and module switches until the token expires; page scripts can never read it.\nconst refreshCookieOf = (session: ITokenSession): string =>\n session.refreshToken\n ? cookie(REFRESH_COOKIE, encodeURIComponent(session.refreshToken), REFRESH_COOKIE_MAX_AGE_SECONDS)\n : clearCookie(REFRESH_COOKIE);\n\nconst decodedCookieValue = (value: string | undefined): string | null => {\n if (!value) {\n return null;\n }\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n};\n\nconst writeJson = (response: ServerResponse, status: number, body: unknown): void => {\n response.statusCode = status;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(JSON.stringify(body));\n};\n\nconst redirect = (response: ServerResponse, location: string, cookies: string[] = []): void => {\n response.statusCode = 302;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Location', location);\n if (cookies.length > 0) {\n response.setHeader('Set-Cookie', cookies);\n }\n response.end();\n};\n\nconst safeReturnTo = (candidate: string | null): string => {\n if (!candidate?.startsWith('/') || candidate.startsWith('//')) {\n return '/';\n }\n try {\n const parsed = new URL(candidate, 'https://localhost');\n return `${parsed.pathname}${parsed.search}${parsed.hash}`;\n } catch {\n return '/';\n }\n};\n\nconst parseTokenResponse = (value: unknown, requireIdToken: boolean): ITokenResponse => {\n if (!isRecord(value)) {\n throw new Error('OIDC token endpoint returned an invalid response.');\n }\n const expiresIn = value.expires_in;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw new Error('OIDC token endpoint returned an invalid expires_in value.');\n }\n const idToken = optionalString(value, 'id_token');\n if (requireIdToken && !idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const tokenType = requiredString(value, 'token_type');\n if (tokenType.toLowerCase() !== 'bearer') {\n throw new Error('OIDC token endpoint returned an unsupported token type.');\n }\n return {\n accessToken: requiredString(value, 'access_token'),\n expiresIn,\n idToken,\n refreshToken: optionalString(value, 'refresh_token'),\n tokenType,\n };\n};\n\nconst pathIsHostApi = (path: string): boolean => {\n return ALLOWED_HOST_API_PATHS.some((candidate) => path === candidate || path.startsWith(`${candidate}/`));\n};\n\n// The host validates a selection the browser sends it, so host requests keep the navigation headers. A module trusts\n// them, so its requests carry only the selection the host last confirmed.\nconst proxyHeaders = (request: IncomingMessage, accessToken: string, module?: IModuleProxyContext): Headers => {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n const lowerName = name.toLowerCase();\n if (\n !value ||\n name.startsWith(':') ||\n ['authorization', 'connection', 'content-length', 'cookie', 'host', 'origin', 'transfer-encoding'].includes(lowerName) ||\n (module && NAVIGATION_HEADERS.includes(lowerName))\n ) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (module?.cookies.size) {\n headers.set('Cookie', [...module.cookies.values()].join('; '));\n }\n const navigationContext = module?.navigationContext;\n if (navigationContext) {\n headers.set(NAVIGATION_GROUP_HEADER, String(navigationContext.groupId));\n headers.set(NAVIGATION_DEALERS_HEADER, navigationContext.dealerIds.join(','));\n if (navigationContext.dealerIds.length === 1) {\n headers.set(NAVIGATION_DEALER_HEADER, String(navigationContext.dealerIds[0]));\n }\n }\n return headers;\n};\n\nconst navigationContextIn = (value: unknown): IModuleNavigationContext | null => {\n if (!isRecord(value) || !isRecord(value.context)) {\n return null;\n }\n const { selectedDealerId, selectedDealerIds, selectedGroupId } = value.context;\n const dealerIds: unknown[] = Array.isArray(selectedDealerIds) ? selectedDealerIds : [selectedDealerId];\n if (\n !Number.isSafeInteger(selectedGroupId) ||\n (selectedGroupId as number) <= 0 ||\n dealerIds.length === 0 ||\n !dealerIds.every((dealerId) => Number.isSafeInteger(dealerId) && (dealerId as number) >= 0)\n ) {\n return null;\n }\n return { dealerIds: dealerIds as number[], groupId: selectedGroupId as number };\n};\n\n// Reads the selection each `navigation` event of the host's navigation stream confirms, chunk by chunk as it passes.\nconst navigationEventReader = (onContext: (context: IModuleNavigationContext) => void): ((chunk: Buffer) => void) => {\n const decoder = new TextDecoder();\n let pending = '';\n return (chunk) => {\n pending += decoder.decode(chunk, { stream: true });\n for (let separator = EVENT_SEPARATOR.exec(pending); separator; separator = EVENT_SEPARATOR.exec(pending)) {\n const block = pending.slice(0, separator.index);\n pending = pending.slice(separator.index + separator[0].length);\n const lines = block.split(/\\r?\\n/);\n if (!lines.includes('event: navigation') && !lines.includes('event:navigation')) {\n continue;\n }\n const data = lines\n .filter((line) => line.startsWith('data:'))\n .map((line) => line.slice(5).replace(/^ /, ''))\n .join('\\n');\n let payload: unknown = null;\n try {\n payload = JSON.parse(data);\n } catch {\n continue;\n }\n const context = navigationContextIn(payload);\n if (context) {\n onContext(context);\n }\n }\n if (pending.length > MAX_NAVIGATION_EVENT_BYTES) {\n pending = '';\n }\n };\n};\n\nconst updateModuleCookies = (cookies: Map<string, string>, headers: Headers): void => {\n const values = headers.getSetCookie();\n for (const value of values) {\n if (value.length > 4096) {\n continue;\n }\n const [pair, ...attributes] = value.split(';');\n const separator = pair?.indexOf('=') ?? -1;\n if (!pair || separator <= 0) {\n continue;\n }\n const name = pair.slice(0, separator).trim();\n const cookieValue = pair.slice(separator + 1).trim();\n if (!COOKIE_NAME_PATTERN.test(name) || !COOKIE_VALUE_PATTERN.test(cookieValue) || cookieValue.includes(';')) {\n continue;\n }\n const deleted = cookieValue === '' || attributes.some((attribute) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(attribute));\n if (deleted) {\n cookies.delete(name);\n continue;\n }\n if (!cookies.has(name) && cookies.size >= 32) {\n continue;\n }\n cookies.set(name, `${name}=${cookieValue}`);\n }\n};\n\nconst requestBodyOf = async (request: IncomingMessage): Promise<Buffer | null> => {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string);\n size += buffer.length;\n if (size > MAX_REQUEST_BODY_BYTES) {\n return null;\n }\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n};\n\nconst copyProxyResponse = async (upstream: Response, response: ServerResponse, onChunk?: (chunk: Buffer) => void): Promise<void> => {\n response.statusCode = upstream.status;\n for (const [name, value] of upstream.headers) {\n if (!['connection', 'content-encoding', 'content-length', 'set-cookie', 'transfer-encoding'].includes(name.toLowerCase())) {\n response.setHeader(name, value);\n }\n }\n response.setHeader('Cache-Control', upstream.headers.get('cache-control') ?? 'no-store');\n if (!upstream.body) {\n response.end();\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const readable = Readable.fromWeb(upstream.body as never);\n if (onChunk) {\n readable.on('data', onChunk);\n }\n readable.once('error', reject);\n response.once('error', reject);\n response.once('finish', resolve);\n readable.pipe(response);\n });\n};\n\nconst readResponseBody = async (response: Response, maximumBytes: number, label: string): Promise<Buffer> => {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {\n await response.body?.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n if (!response.body) {\n return Buffer.alloc(0);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maximumBytes) {\n await reader.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks, totalBytes);\n};\n\nclass MfDevelopmentBff {\n readonly #configurationUrl: URL;\n readonly #developmentOrigin: URL;\n readonly #flows = new Map<string, IOidcFlow>();\n readonly #hostOrigin: URL;\n readonly #hostNavigationUrl: URL;\n readonly #jwks = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n readonly #logoutFlows = new Map<string, ILogoutFlow>();\n readonly #moduleApiOrigin: URL;\n readonly #moduleApiPath: string;\n readonly #sessions = new Map<string, ITokenSession>();\n readonly #restores = new Map<string, Promise<{ session: ITokenSession; sessionId: string } | null>>();\n #configurationPromise: Promise<IOidcClientConfiguration> | null = null;\n #discoveryPromise: Promise<IOidcDiscovery> | null = null;\n\n constructor(options: IMfDevelopmentBffPluginOptions) {\n const hostNavigation = absoluteHttpsUrl(options.hostNavigationUrl, 'hostNavigationUrl', true);\n this.#hostNavigationUrl = hostNavigation;\n this.#hostOrigin = new URL(hostNavigation.origin);\n this.#configurationUrl = new URL('/api/auth/oidc-configuration', this.#hostOrigin);\n this.#developmentOrigin = absoluteHttpsUrl(options.developmentOrigin ?? 'https://localhost:4200', 'developmentOrigin');\n if (this.#developmentOrigin.pathname !== '/' || this.#developmentOrigin.search || this.#developmentOrigin.hash) {\n throw new Error('developmentOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiOrigin = absoluteHttpsUrl(options.moduleApiOrigin, 'moduleApiOrigin', true);\n if (this.#moduleApiOrigin.pathname !== '/' || this.#moduleApiOrigin.search || this.#moduleApiOrigin.hash) {\n throw new Error('moduleApiOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiPath = moduleApiPrefix(options.moduleApiPath);\n }\n\n async handle(request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void): Promise<void> {\n if (!request.url || !request.method) {\n next();\n return;\n }\n const url = new URL(request.url, this.#developmentOrigin);\n this.#prune();\n\n try {\n if (request.method === 'GET' && url.pathname === MF_DEVELOPMENT_HOST_NAVIGATION_PATH) {\n await this.#hostNavigationResource(response, this.#hostNavigationUrl, 'script');\n return;\n }\n if (request.method === 'GET' && HOST_NAVIGATION_ASSET_PATHS.has(url.pathname)) {\n await this.#hostNavigationResource(response, new URL(url.pathname, this.#hostOrigin), 'asset');\n return;\n }\n if (request.method === 'GET' && url.pathname.toLowerCase() === LEGACY_LOGIN_PATH) {\n await this.#legacyLogin(request, response);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGIN_PATH) {\n await this.#login(request, response, safeReturnTo(url.searchParams.get('returnTo')));\n return;\n }\n if (request.method === 'GET' && url.pathname === CALLBACK_PATH) {\n await this.#callback(request, response, url);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGGED_OUT_PATH) {\n const returnTo = safeReturnTo(url.searchParams.get('state'));\n redirect(response, `${LOGIN_PATH}?returnTo=${encodeURIComponent(returnTo)}`, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n if (request.method === 'GET' && url.pathname === '/frontchannel-logout') {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.statusCode = 204;\n response.setHeader('Set-Cookie', [clearCookie(SESSION_COOKIE), clearCookie(REFRESH_COOKIE)]);\n response.end();\n return;\n }\n if (request.method === 'POST' && url.pathname === '/api/auth/logout') {\n await this.#logout(request, response);\n return;\n }\n if (pathIsHostApi(url.pathname) || url.pathname.startsWith(this.#moduleApiPath)) {\n await this.#proxy(request, response, url);\n return;\n }\n next();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Development authentication failed.';\n writeJson(response, 502, { code: 'development_authentication_failed', error: message });\n }\n }\n\n async #hostNavigationResource(response: ServerResponse, upstreamUrl: URL, kind: 'asset' | 'script'): Promise<void> {\n const upstream = await fetch(upstreamUrl, {\n headers: { Accept: kind === 'script' ? 'text/javascript, application/javascript' : 'image/svg+xml' },\n redirect: 'error',\n });\n if (!upstream.ok) {\n throw new Error(`Host navigation ${kind} failed (${upstream.status}).`);\n }\n const contentType = upstream.headers.get('content-type') ?? '';\n const validContentType =\n kind === 'script' ? /^(?:text|application)\\/javascript(?:;|$)/i.test(contentType) : /^image\\/svg\\+xml(?:;|$)/i.test(contentType);\n if (!validContentType) {\n throw new Error(`Host navigation ${kind} returned an invalid content type.`);\n }\n const maximumBytes = kind === 'script' ? MAX_HOST_NAVIGATION_BYTES : MAX_HOST_NAVIGATION_ASSET_BYTES;\n const body = await readResponseBody(upstream, maximumBytes, `Host navigation ${kind}`);\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', contentType);\n response.setHeader('X-Content-Type-Options', 'nosniff');\n response.end(body);\n }\n\n async #configuration(): Promise<IOidcClientConfiguration> {\n this.#configurationPromise ??= fetch(this.#configurationUrl, { headers: { Accept: 'application/json' }, redirect: 'error' })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Host OIDC configuration failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('Host OIDC configuration returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC issuer', true).href.replace(/\\/$/, '');\n return { clientId: requiredString(value, 'clientId'), issuer, scope: requiredString(value, 'scope') };\n })\n .catch((error) => {\n this.#configurationPromise = null;\n throw error;\n });\n return this.#configurationPromise;\n }\n\n async #discovery(): Promise<IOidcDiscovery> {\n this.#discoveryPromise ??= this.#configuration()\n .then(async (configuration) => {\n const response = await fetch(`${configuration.issuer}/.well-known/openid-configuration`, {\n headers: { Accept: 'application/json' },\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC discovery failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('OIDC discovery returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC discovery issuer', true).href.replace(/\\/$/, '');\n if (issuer !== configuration.issuer) {\n throw new Error('OIDC discovery issuer does not match the host configuration.');\n }\n const endpoint = (key: string): string => {\n const url = absoluteHttpsUrl(requiredString(value, key), `OIDC ${key}`, true);\n if (url.origin !== new URL(issuer).origin) {\n throw new Error(`OIDC ${key} must use the configured issuer origin.`);\n }\n return url.href;\n };\n const revocation = optionalString(value, 'revocation_endpoint');\n return {\n authorizationEndpoint: endpoint('authorization_endpoint'),\n endSessionEndpoint: endpoint('end_session_endpoint'),\n issuer,\n jwksUri: endpoint('jwks_uri'),\n revocationEndpoint: revocation ? endpoint('revocation_endpoint') : null,\n tokenEndpoint: endpoint('token_endpoint'),\n };\n })\n .catch((error) => {\n this.#discoveryPromise = null;\n throw error;\n });\n return this.#discoveryPromise;\n }\n\n async #login(request: IncomingMessage, response: ServerResponse, returnTo: string): Promise<void> {\n const logoutId = cookiesOf(request).get(LOGOUT_COOKIE);\n if (logoutId) {\n const logout = this.#logoutFlows.get(logoutId);\n this.#logoutFlows.delete(logoutId);\n if (logout) {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const endpoint = new URL(discovery.endSessionEndpoint);\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('post_logout_redirect_uri', new URL(LOGGED_OUT_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('state', returnTo);\n if (logout.idToken) {\n endpoint.searchParams.set('id_token_hint', logout.idToken);\n }\n redirect(response, endpoint.href, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const flowId = base64Url();\n const codeVerifier = base64Url(48);\n const flow: IOidcFlow = { codeVerifier, createdAt: Date.now(), nonce: base64Url(), returnTo, state: base64Url() };\n this.#flows.set(flowId, flow);\n\n const endpoint = new URL(discovery.authorizationEndpoint);\n endpoint.searchParams.set('response_type', 'code');\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('redirect_uri', new URL(CALLBACK_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('scope', configuration.scope);\n endpoint.searchParams.set('state', flow.state);\n endpoint.searchParams.set('nonce', flow.nonce);\n endpoint.searchParams.set('code_challenge', codeChallengeOf(codeVerifier));\n endpoint.searchParams.set('code_challenge_method', 'S256');\n redirect(response, endpoint.href, [cookie(FLOW_COOKIE, flowId, FLOW_MAX_AGE_SECONDS)]);\n }\n\n async #callback(request: IncomingMessage, response: ServerResponse, url: URL): Promise<void> {\n const flowId = cookiesOf(request).get(FLOW_COOKIE);\n const flow = flowId ? this.#flows.get(flowId) : undefined;\n if (flowId) {\n this.#flows.delete(flowId);\n }\n if (!flow || url.searchParams.get('state') !== flow.state) {\n writeJson(response, 400, { code: 'invalid_oidc_state', error: 'The OIDC callback state is invalid or expired.' });\n return;\n }\n const providerError = url.searchParams.get('error');\n if (providerError) {\n writeJson(response, 401, { code: providerError, error: url.searchParams.get('error_description') ?? 'Sign-in was rejected.' });\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n writeJson(response, 400, { code: 'missing_authorization_code', error: 'The OIDC callback did not include an authorization code.' });\n return;\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const fields = new URLSearchParams({\n client_id: configuration.clientId,\n code,\n code_verifier: flow.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: new URL(CALLBACK_PATH, this.#developmentOrigin).href,\n });\n const tokenResponse = await fetch(discovery.tokenEndpoint, {\n body: fields,\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!tokenResponse.ok) {\n throw new Error(`OIDC token exchange failed (${tokenResponse.status}).`);\n }\n const tokens = parseTokenResponse(await tokenResponse.json(), true);\n const idToken = tokens.idToken;\n if (!idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const jwks = this.#jwks.get(discovery.jwksUri) ?? createRemoteJWKSet(new URL(discovery.jwksUri));\n this.#jwks.set(discovery.jwksUri, jwks);\n const verified = await jwtVerify(idToken, jwks, {\n audience: configuration.clientId,\n issuer: discovery.issuer,\n requiredClaims: ['sub', 'nonce'],\n });\n if (verified.payload.nonce !== flow.nonce) {\n throw new Error('OIDC ID token nonce does not match the authorization request.');\n }\n\n const sessionId = base64Url();\n const session: ITokenSession = {\n accessToken: tokens.accessToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n idToken,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken: tokens.refreshToken,\n };\n this.#sessions.set(sessionId, session);\n redirect(response, flow.returnTo, [\n cookie(SESSION_COOKIE, sessionId, SESSION_MAX_AGE_SECONDS),\n refreshCookieOf(session),\n clearCookie(FLOW_COOKIE),\n ]);\n }\n\n async #logout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n if (session) {\n void this.#revoke(session).catch(() => undefined);\n }\n const logoutId = base64Url();\n this.#logoutFlows.set(logoutId, { createdAt: Date.now(), idToken: session?.idToken ?? null });\n response.setHeader('Set-Cookie', [\n clearCookie(SESSION_COOKIE),\n clearCookie(REFRESH_COOKIE),\n cookie(LOGOUT_COOKIE, logoutId, FLOW_MAX_AGE_SECONDS),\n ]);\n writeJson(response, 200, { loggedOut: true });\n }\n\n async #revoke(session: ITokenSession): Promise<void> {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const revocationEndpoint = discovery.revocationEndpoint;\n if (!revocationEndpoint) {\n return;\n }\n const revoke = async (token: string, hint: string): Promise<void> => {\n await fetch(revocationEndpoint, {\n body: new URLSearchParams({ client_id: configuration.clientId, token, token_type_hint: hint }),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n };\n await Promise.all([\n revoke(session.accessToken, 'access_token'),\n session.refreshToken ? revoke(session.refreshToken, 'refresh_token') : Promise.resolve(),\n ]);\n }\n\n // Standalone modules send the browser to their legacy /login page when an API call answers 401, and locally no such\n // page exists: serving the module again would repeat the 401 in a reload loop. Signed out, this is the development\n // sign-in; signed in, the module API itself refused the session, so the page says so and redirects nowhere.\n async #legacyLogin(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!(await this.#authenticatedSession(request, response))) {\n redirect(response, `${LOGIN_PATH}?returnTo=%2F`);\n return;\n }\n response.statusCode = 401;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n response.end(\n '<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Module sign-in refused</title></head>' +\n '<body style=\"font-family:system-ui,sans-serif;margin:3rem;line-height:1.5\">' +\n '<h1 style=\"font-size:1.25rem\">The module API refused your session</h1>' +\n '<p>You are signed in, but a module API request answered 401 and the module asked for its legacy sign-in page. ' +\n 'Check the module backend (MODULE_API_ORIGIN) and its dev server output.</p>' +\n `<p><a href=\"${LOGIN_PATH}?returnTo=%2F\">Sign in again</a> · <a href=\"/\">Back to the module</a></p>` +\n '</body></html>',\n );\n }\n\n async #authenticatedSession(request: IncomingMessage, response: ServerResponse): Promise<ITokenSession | null> {\n const cookies = cookiesOf(request);\n const sessionId = cookies.get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (!session) {\n return this.#restoredSession(decodedCookieValue(cookies.get(REFRESH_COOKIE)), response);\n }\n if (session.expiresAt > Date.now() + TOKEN_REFRESH_SKEW_MS) {\n return session;\n }\n if (!session.refreshToken) {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n return null;\n }\n session.refreshPromise ??= this.#refresh(session).finally(() => {\n session.refreshPromise = undefined;\n });\n try {\n await session.refreshPromise;\n response.setHeader('Set-Cookie', refreshCookieOf(session));\n return session;\n } catch {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n }\n\n // Starts a session from the browser's refresh cookie after this server restarted or another module's server signed\n // the developer in. Parallel requests carrying the same token share one refresh.\n async #restoredSession(refreshToken: string | null, response: ServerResponse): Promise<ITokenSession | null> {\n if (!refreshToken) {\n return null;\n }\n let restore = this.#restores.get(refreshToken);\n if (!restore) {\n restore = this.#restore(refreshToken).finally(() => this.#restores.delete(refreshToken));\n this.#restores.set(refreshToken, restore);\n }\n const restored = await restore;\n if (!restored) {\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n response.setHeader('Set-Cookie', [cookie(SESSION_COOKIE, restored.sessionId, SESSION_MAX_AGE_SECONDS), refreshCookieOf(restored.session)]);\n return restored.session;\n }\n\n async #restore(refreshToken: string): Promise<{ session: ITokenSession; sessionId: string } | null> {\n const session: ITokenSession = {\n accessToken: '',\n expiresAt: 0,\n idToken: null,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken,\n };\n try {\n await this.#refresh(session);\n } catch {\n return null;\n }\n const sessionId = base64Url();\n this.#sessions.set(sessionId, session);\n return { session, sessionId };\n }\n\n async #refresh(session: ITokenSession): Promise<void> {\n const refreshToken = session.refreshToken;\n if (!refreshToken) {\n throw new Error('OIDC refresh token is unavailable.');\n }\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const response = await fetch(discovery.tokenEndpoint, {\n body: new URLSearchParams({\n client_id: configuration.clientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n }),\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC refresh failed (${response.status}).`);\n }\n const tokens = parseTokenResponse(await response.json(), false);\n session.accessToken = tokens.accessToken;\n session.expiresAt = Date.now() + tokens.expiresIn * 1000;\n session.refreshToken = tokens.refreshToken ?? session.refreshToken;\n session.idToken = tokens.idToken ?? session.idToken;\n }\n\n async #proxy(request: IncomingMessage, response: ServerResponse, localUrl: URL): Promise<void> {\n const session = await this.#authenticatedSession(request, response);\n if (!session) {\n // This server refreshes the session's tokens itself, so the browser can only sign in again. A refreshable code\n // would send the host client to a refresh endpoint that only the host has.\n writeJson(response, 401, { code: 'sign_in_required', error: 'Sign in is required.' });\n return;\n }\n const isModuleApi = localUrl.pathname.startsWith(this.#moduleApiPath);\n if (isModuleApi && !session.moduleNavigationContext) {\n // The host mounts a module only once the selection is known, so its first API calls already carry it. Locally\n // the module starts beside the navigation, so its first calls can arrive first: ask the host before forwarding.\n session.navigationContextPromise ??= this.#loadNavigationContext(session).finally(() => {\n session.navigationContextPromise = undefined;\n });\n await session.navigationContextPromise;\n }\n if (isModuleApi && session.moduleCookies.size === 0) {\n const previous = session.moduleBootstrapTail;\n let release = (): void => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n session.moduleBootstrapTail = previous.catch(() => undefined).then(() => current);\n await previous.catch(() => undefined);\n try {\n await this.#proxyAuthenticated(request, response, localUrl, session, true);\n } finally {\n release();\n }\n return;\n }\n await this.#proxyAuthenticated(request, response, localUrl, session, isModuleApi);\n }\n\n async #loadNavigationContext(session: ITokenSession): Promise<void> {\n const upstream = await fetch(new URL('/api/navigation', this.#hostOrigin), {\n headers: { Accept: 'application/json', Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n }).catch(() => null);\n if (!upstream?.ok || session.moduleNavigationContext) {\n return;\n }\n session.moduleNavigationContext = navigationContextIn(await upstream.json().catch(() => null));\n }\n\n async #proxyAuthenticated(\n request: IncomingMessage,\n response: ServerResponse,\n localUrl: URL,\n session: ITokenSession,\n isModuleApi: boolean,\n ): Promise<void> {\n const upstreamOrigin = isModuleApi ? this.#moduleApiOrigin : this.#hostOrigin;\n const upstreamUrl = new URL(`${localUrl.pathname}${localUrl.search}`, upstreamOrigin);\n const method = request.method ?? 'GET';\n const init: RequestInit = {\n headers: proxyHeaders(\n request,\n session.accessToken,\n isModuleApi ? { cookies: session.moduleCookies, navigationContext: session.moduleNavigationContext } : undefined,\n ),\n method,\n redirect: 'manual',\n };\n if (method !== 'GET' && method !== 'HEAD') {\n // A fixed-length body: the web front ends of the deployed module servers answer chunked request bodies with 404.\n const body = await requestBodyOf(request);\n if (!body) {\n writeJson(response, 413, { code: 'request_body_too_large', error: 'The request body exceeds the development proxy limit.' });\n return;\n }\n init.body = body;\n }\n const upstream = await fetch(upstreamUrl, init);\n let onChunk: ((chunk: Buffer) => void) | undefined;\n if (isModuleApi) {\n updateModuleCookies(session.moduleCookies, upstream.headers);\n } else if (localUrl.pathname === '/api/navigation' && upstream.ok) {\n session.moduleNavigationContext = navigationContextIn(\n await upstream\n .clone()\n .json()\n .catch(() => null),\n );\n } else if (localUrl.pathname === '/api/navigation/stream' && upstream.ok) {\n onChunk = navigationEventReader((context) => {\n session.moduleNavigationContext = context;\n });\n }\n await copyProxyResponse(upstream, response, onChunk);\n }\n\n #prune(): void {\n const now = Date.now();\n for (const [id, flow] of this.#flows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#flows.delete(id);\n }\n }\n for (const [id, flow] of this.#logoutFlows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#logoutFlows.delete(id);\n }\n }\n for (const [id, session] of this.#sessions) {\n if (!session.refreshToken && session.expiresAt + TOKEN_REFRESH_SKEW_MS < now) {\n this.#sessions.delete(id);\n }\n }\n }\n}\n\nexport const mfDevelopmentBffPlugin = (options: IMfDevelopmentBffPluginOptions): Plugin => {\n const middleware = createMfDevelopmentBffMiddleware(options);\n return {\n apply: 'serve',\n name: 'edynamix-mf-development-bff',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n middleware(request, response, next);\n });\n },\n // Locally the module runs inside the host shell, so its page shows the host's icon. The module's own favicon is\n // only copied into the build output, so this server has none to answer the browser's favicon request with.\n transformIndexHtml(html) {\n return {\n html: html.replace(ICON_LINK_PATTERN, ''),\n tags: [{ tag: 'link', attrs: { rel: 'icon', type: 'image/svg+xml', href: HOST_ICON_PATH }, injectTo: 'head' }],\n };\n },\n };\n};\n\nexport type TMfDevelopmentBffMiddleware = (request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void) => void;\n\nexport const createMfDevelopmentBffMiddleware = (options: IMfDevelopmentBffPluginOptions): TMfDevelopmentBffMiddleware => {\n const bff = new MfDevelopmentBff(options);\n return (request, response, next) => {\n void bff.handle(request, response, next);\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"vite-development.d.ts","sourceRoot":"","sources":["../src/vite-development.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAUnC,eAAO,MAAM,mCAAmC,sCAAsC,CAAC;AAsFvF,MAAM,WAAW,8BAA8B;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAi4BD,eAAO,MAAM,sBAAsB,GAAI,SAAS,8BAA8B,KAAG,MAmBhF,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;AAExI,eAAO,MAAM,gCAAgC,GAAI,SAAS,8BAA8B,KAAG,2BAK1F,CAAC","sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { Plugin } from 'vite';\n\nconst SESSION_COOKIE = '__Host-edyn_dev_session';\nconst REFRESH_COOKIE = '__Host-edyn_dev_refresh';\nconst FLOW_COOKIE = '__Host-edyn_oidc_flow';\nconst LOGOUT_COOKIE = '__Host-edyn_oidc_logout';\nconst CALLBACK_PATH = '/callback';\nconst LOGGED_OUT_PATH = '/logged-out';\nconst LOGIN_PATH = '/api/dev-auth/login';\nconst LEGACY_LOGIN_PATH = '/login';\nexport const MF_DEVELOPMENT_HOST_NAVIGATION_PATH = '/api/dev-host-navigation/index.js';\nconst HOST_ICON_PATH = '/exsto-icon.svg';\nconst HOST_NAVIGATION_ASSET_PATHS = new Set([HOST_ICON_PATH, '/exsto-text.svg']);\nconst ICON_LINK_PATTERN = /<link\\b[^>]*\\brel=[\"'](?:shortcut\\s+)?icon[\"'][^>]*>\\s*/gi;\nconst MAX_HOST_NAVIGATION_BYTES = 5 * 1024 * 1024;\nconst MAX_HOST_NAVIGATION_ASSET_BYTES = 512 * 1024;\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst REFRESH_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;\nconst FLOW_MAX_AGE_SECONDS = 10 * 60;\nconst TOKEN_REFRESH_SKEW_MS = 30_000;\nconst SPENT_REFRESH_TOKEN_REUSE_MS = 30_000;\nconst NAVIGATION_GROUP_HEADER = 'X-Edyn-Navigation-Group-Id';\nconst NAVIGATION_DEALER_HEADER = 'X-Edyn-Navigation-Dealer-Id';\nconst NAVIGATION_DEALERS_HEADER = 'X-Edyn-Navigation-Dealer-Ids';\nconst NAVIGATION_HEADERS = [NAVIGATION_GROUP_HEADER, NAVIGATION_DEALER_HEADER, NAVIGATION_DEALERS_HEADER].map((name) => name.toLowerCase());\nconst MAX_NAVIGATION_EVENT_BYTES = 5 * 1024 * 1024;\nconst MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024;\nconst EVENT_SEPARATOR = /\\r?\\n\\r?\\n/;\nconst COOKIE_NAME_PATTERN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst COOKIE_VALUE_PATTERN = /^[\\x20-\\x7e]*$/;\nconst ALLOWED_HOST_API_PATHS = [\n '/api/auth/me',\n '/api/navigation',\n '/api/navigation/access-check',\n '/api/navigation/access-request',\n '/api/navigation/counter-stream',\n] as const;\n\ninterface IOidcClientConfiguration {\n clientId: string;\n issuer: string;\n scope: string;\n}\n\ninterface IOidcDiscovery {\n authorizationEndpoint: string;\n endSessionEndpoint: string;\n issuer: string;\n jwksUri: string;\n revocationEndpoint: string | null;\n tokenEndpoint: string;\n}\n\ninterface IOidcFlow {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n returnTo: string;\n state: string;\n}\n\ninterface IModuleNavigationContext {\n dealerIds: readonly number[];\n groupId: number;\n}\n\ninterface IModuleProxyContext {\n cookies: Map<string, string>;\n navigationContext: IModuleNavigationContext | null;\n}\n\ninterface ITokenSession {\n accessToken: string;\n expiresAt: number;\n idToken: string | null;\n moduleBootstrapTail: Promise<void>;\n moduleCookies: Map<string, string>;\n moduleNavigationContext: IModuleNavigationContext | null;\n navigationContextPromise?: Promise<void>;\n refreshPromise?: Promise<void>;\n refreshToken: string | null;\n}\n\ninterface ILogoutFlow {\n createdAt: number;\n idToken: string | null;\n}\n\ninterface ITokenResponse {\n accessToken: string;\n expiresIn: number;\n idToken: string | null;\n refreshToken: string | null;\n tokenType: string;\n}\n\nexport interface IMfDevelopmentBffPluginOptions {\n developmentOrigin?: string;\n hostNavigationUrl: string;\n moduleApiOrigin: string;\n moduleApiPath: string;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst requiredString = (value: Record<string, unknown>, key: string): string => {\n const candidate = value[key];\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response is missing ${key}.`);\n }\n return candidate;\n};\n\nconst optionalString = (value: Record<string, unknown>, key: string): string | null => {\n const candidate = value[key];\n if (candidate === undefined || candidate === null) {\n return null;\n }\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response contains an invalid ${key}.`);\n }\n return candidate;\n};\n\nconst absoluteHttpsUrl = (candidate: string, label: string, allowLocalHttp = false): URL => {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new Error(`${label} must be an absolute URL.`);\n }\n const isLocalHttp = allowLocalHttp && url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1');\n if (url.protocol !== 'https:' && !isLocalHttp) {\n throw new Error(`${label} must use HTTPS.`);\n }\n if (url.username || url.password || url.hash) {\n throw new Error(`${label} must not contain credentials or a fragment.`);\n }\n return url;\n};\n\nconst moduleApiPrefix = (candidate: string): string => {\n if (!/^\\/[a-z][a-z0-9-]*\\/api\\/$/.test(candidate)) {\n throw new Error('moduleApiPath must use the canonical /module/api/ boundary.');\n }\n return candidate;\n};\n\nconst base64Url = (size = 32): string => randomBytes(size).toString('base64url');\nconst codeChallengeOf = (verifier: string): string => createHash('sha256').update(verifier).digest('base64url');\n\nconst cookiesOf = (request: IncomingMessage): ReadonlyMap<string, string> => {\n const cookies = new Map<string, string>();\n for (const part of (request.headers.cookie ?? '').split(';')) {\n const separator = part.indexOf('=');\n if (separator <= 0) {\n continue;\n }\n const name = part.slice(0, separator).trim();\n const value = part.slice(separator + 1).trim();\n if (name && value) {\n cookies.set(name, value);\n }\n }\n return cookies;\n};\n\nconst cookie = (name: string, value: string, maxAge: number): string => `${name}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${maxAge}`;\n\nconst clearCookie = (name: string): string => cookie(name, '', 0);\n\n// The refresh token stays in an HttpOnly cookie for this origin, which every module's development server shares, so\n// one sign-in survives restarts and module switches until the token expires; page scripts can never read it.\nconst refreshCookieOf = (session: ITokenSession): string =>\n session.refreshToken\n ? cookie(REFRESH_COOKIE, encodeURIComponent(session.refreshToken), REFRESH_COOKIE_MAX_AGE_SECONDS)\n : clearCookie(REFRESH_COOKIE);\n\nconst decodedCookieValue = (value: string | undefined): string | null => {\n if (!value) {\n return null;\n }\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n};\n\nconst writeJson = (response: ServerResponse, status: number, body: unknown): void => {\n response.statusCode = status;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(JSON.stringify(body));\n};\n\nconst redirect = (response: ServerResponse, location: string, cookies: string[] = []): void => {\n response.statusCode = 302;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Location', location);\n if (cookies.length > 0) {\n response.setHeader('Set-Cookie', cookies);\n }\n response.end();\n};\n\nconst safeReturnTo = (candidate: string | null): string => {\n if (!candidate?.startsWith('/') || candidate.startsWith('//')) {\n return '/';\n }\n try {\n const parsed = new URL(candidate, 'https://localhost');\n return `${parsed.pathname}${parsed.search}${parsed.hash}`;\n } catch {\n return '/';\n }\n};\n\nconst parseTokenResponse = (value: unknown, requireIdToken: boolean): ITokenResponse => {\n if (!isRecord(value)) {\n throw new Error('OIDC token endpoint returned an invalid response.');\n }\n const expiresIn = value.expires_in;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw new Error('OIDC token endpoint returned an invalid expires_in value.');\n }\n const idToken = optionalString(value, 'id_token');\n if (requireIdToken && !idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const tokenType = requiredString(value, 'token_type');\n if (tokenType.toLowerCase() !== 'bearer') {\n throw new Error('OIDC token endpoint returned an unsupported token type.');\n }\n return {\n accessToken: requiredString(value, 'access_token'),\n expiresIn,\n idToken,\n refreshToken: optionalString(value, 'refresh_token'),\n tokenType,\n };\n};\n\nconst pathIsHostApi = (path: string): boolean => {\n return ALLOWED_HOST_API_PATHS.some((candidate) => path === candidate || path.startsWith(`${candidate}/`));\n};\n\n// The host validates a selection the browser sends it, so host requests keep the navigation headers. A module trusts\n// them, so its requests carry only the selection the host last confirmed.\nconst proxyHeaders = (request: IncomingMessage, accessToken: string, module?: IModuleProxyContext): Headers => {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n const lowerName = name.toLowerCase();\n if (\n !value ||\n name.startsWith(':') ||\n ['authorization', 'connection', 'content-length', 'cookie', 'host', 'origin', 'transfer-encoding'].includes(lowerName) ||\n (module && NAVIGATION_HEADERS.includes(lowerName))\n ) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (module?.cookies.size) {\n headers.set('Cookie', [...module.cookies.values()].join('; '));\n }\n const navigationContext = module?.navigationContext;\n if (navigationContext) {\n headers.set(NAVIGATION_GROUP_HEADER, String(navigationContext.groupId));\n headers.set(NAVIGATION_DEALERS_HEADER, navigationContext.dealerIds.join(','));\n if (navigationContext.dealerIds.length === 1) {\n headers.set(NAVIGATION_DEALER_HEADER, String(navigationContext.dealerIds[0]));\n }\n }\n return headers;\n};\n\nconst navigationContextIn = (value: unknown): IModuleNavigationContext | null => {\n if (!isRecord(value) || !isRecord(value.context)) {\n return null;\n }\n const { selectedDealerId, selectedDealerIds, selectedGroupId } = value.context;\n const dealerIds: unknown[] = Array.isArray(selectedDealerIds) ? selectedDealerIds : [selectedDealerId];\n if (\n !Number.isSafeInteger(selectedGroupId) ||\n (selectedGroupId as number) <= 0 ||\n dealerIds.length === 0 ||\n !dealerIds.every((dealerId) => Number.isSafeInteger(dealerId) && (dealerId as number) >= 0)\n ) {\n return null;\n }\n return { dealerIds: dealerIds as number[], groupId: selectedGroupId as number };\n};\n\n// Reads the selection each `navigation` event of the host's navigation stream confirms, chunk by chunk as it passes.\nconst navigationEventReader = (onContext: (context: IModuleNavigationContext) => void): ((chunk: Buffer) => void) => {\n const decoder = new TextDecoder();\n let pending = '';\n return (chunk) => {\n pending += decoder.decode(chunk, { stream: true });\n for (let separator = EVENT_SEPARATOR.exec(pending); separator; separator = EVENT_SEPARATOR.exec(pending)) {\n const block = pending.slice(0, separator.index);\n pending = pending.slice(separator.index + separator[0].length);\n const lines = block.split(/\\r?\\n/);\n if (!lines.includes('event: navigation') && !lines.includes('event:navigation')) {\n continue;\n }\n const data = lines\n .filter((line) => line.startsWith('data:'))\n .map((line) => line.slice(5).replace(/^ /, ''))\n .join('\\n');\n let payload: unknown = null;\n try {\n payload = JSON.parse(data);\n } catch {\n continue;\n }\n const context = navigationContextIn(payload);\n if (context) {\n onContext(context);\n }\n }\n if (pending.length > MAX_NAVIGATION_EVENT_BYTES) {\n pending = '';\n }\n };\n};\n\nconst updateModuleCookies = (cookies: Map<string, string>, headers: Headers): void => {\n const values = headers.getSetCookie();\n for (const value of values) {\n if (value.length > 4096) {\n continue;\n }\n const [pair, ...attributes] = value.split(';');\n const separator = pair?.indexOf('=') ?? -1;\n if (!pair || separator <= 0) {\n continue;\n }\n const name = pair.slice(0, separator).trim();\n const cookieValue = pair.slice(separator + 1).trim();\n if (!COOKIE_NAME_PATTERN.test(name) || !COOKIE_VALUE_PATTERN.test(cookieValue) || cookieValue.includes(';')) {\n continue;\n }\n const deleted = cookieValue === '' || attributes.some((attribute) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(attribute));\n if (deleted) {\n cookies.delete(name);\n continue;\n }\n if (!cookies.has(name) && cookies.size >= 32) {\n continue;\n }\n cookies.set(name, `${name}=${cookieValue}`);\n }\n};\n\nconst requestBodyOf = async (request: IncomingMessage): Promise<Buffer | null> => {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string);\n size += buffer.length;\n if (size > MAX_REQUEST_BODY_BYTES) {\n return null;\n }\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n};\n\nconst copyProxyResponse = async (upstream: Response, response: ServerResponse, onChunk?: (chunk: Buffer) => void): Promise<void> => {\n response.statusCode = upstream.status;\n for (const [name, value] of upstream.headers) {\n if (!['connection', 'content-encoding', 'content-length', 'set-cookie', 'transfer-encoding'].includes(name.toLowerCase())) {\n response.setHeader(name, value);\n }\n }\n response.setHeader('Cache-Control', upstream.headers.get('cache-control') ?? 'no-store');\n if (!upstream.body) {\n response.end();\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const readable = Readable.fromWeb(upstream.body as never);\n if (onChunk) {\n readable.on('data', onChunk);\n }\n readable.once('error', reject);\n response.once('error', reject);\n response.once('finish', resolve);\n readable.pipe(response);\n });\n};\n\nconst readResponseBody = async (response: Response, maximumBytes: number, label: string): Promise<Buffer> => {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {\n await response.body?.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n if (!response.body) {\n return Buffer.alloc(0);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maximumBytes) {\n await reader.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks, totalBytes);\n};\n\nclass MfDevelopmentBff {\n readonly #configurationUrl: URL;\n readonly #developmentOrigin: URL;\n readonly #flows = new Map<string, IOidcFlow>();\n readonly #hostOrigin: URL;\n readonly #hostNavigationUrl: URL;\n readonly #jwks = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n readonly #logoutFlows = new Map<string, ILogoutFlow>();\n readonly #moduleApiOrigin: URL;\n readonly #moduleApiPath: string;\n readonly #sessions = new Map<string, ITokenSession>();\n readonly #restores = new Map<string, Promise<{ session: ITokenSession; sessionId: string } | null>>();\n #configurationPromise: Promise<IOidcClientConfiguration> | null = null;\n #discoveryPromise: Promise<IOidcDiscovery> | null = null;\n\n constructor(options: IMfDevelopmentBffPluginOptions) {\n const hostNavigation = absoluteHttpsUrl(options.hostNavigationUrl, 'hostNavigationUrl', true);\n this.#hostNavigationUrl = hostNavigation;\n this.#hostOrigin = new URL(hostNavigation.origin);\n this.#configurationUrl = new URL('/api/auth/oidc-configuration', this.#hostOrigin);\n this.#developmentOrigin = absoluteHttpsUrl(options.developmentOrigin ?? 'https://localhost:4200', 'developmentOrigin');\n if (this.#developmentOrigin.pathname !== '/' || this.#developmentOrigin.search || this.#developmentOrigin.hash) {\n throw new Error('developmentOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiOrigin = absoluteHttpsUrl(options.moduleApiOrigin, 'moduleApiOrigin', true);\n if (this.#moduleApiOrigin.pathname !== '/' || this.#moduleApiOrigin.search || this.#moduleApiOrigin.hash) {\n throw new Error('moduleApiOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiPath = moduleApiPrefix(options.moduleApiPath);\n }\n\n async handle(request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void): Promise<void> {\n if (!request.url || !request.method) {\n next();\n return;\n }\n const url = new URL(request.url, this.#developmentOrigin);\n this.#prune();\n\n try {\n if (request.method === 'GET' && url.pathname === MF_DEVELOPMENT_HOST_NAVIGATION_PATH) {\n await this.#hostNavigationResource(response, this.#hostNavigationUrl, 'script');\n return;\n }\n if (request.method === 'GET' && HOST_NAVIGATION_ASSET_PATHS.has(url.pathname)) {\n await this.#hostNavigationResource(response, new URL(url.pathname, this.#hostOrigin), 'asset');\n return;\n }\n if (request.method === 'GET' && url.pathname.toLowerCase() === LEGACY_LOGIN_PATH) {\n await this.#legacyLogin(request, response);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGIN_PATH) {\n await this.#login(request, response, safeReturnTo(url.searchParams.get('returnTo')));\n return;\n }\n if (request.method === 'GET' && url.pathname === CALLBACK_PATH) {\n await this.#callback(request, response, url);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGGED_OUT_PATH) {\n const returnTo = safeReturnTo(url.searchParams.get('state'));\n redirect(response, `${LOGIN_PATH}?returnTo=${encodeURIComponent(returnTo)}`, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n if (request.method === 'GET' && url.pathname === '/frontchannel-logout') {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.statusCode = 204;\n response.setHeader('Set-Cookie', [clearCookie(SESSION_COOKIE), clearCookie(REFRESH_COOKIE)]);\n response.end();\n return;\n }\n if (request.method === 'POST' && url.pathname === '/api/auth/logout') {\n await this.#logout(request, response);\n return;\n }\n if (pathIsHostApi(url.pathname) || url.pathname.startsWith(this.#moduleApiPath)) {\n await this.#proxy(request, response, url);\n return;\n }\n next();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Development authentication failed.';\n writeJson(response, 502, { code: 'development_authentication_failed', error: message });\n }\n }\n\n async #hostNavigationResource(response: ServerResponse, upstreamUrl: URL, kind: 'asset' | 'script'): Promise<void> {\n const upstream = await fetch(upstreamUrl, {\n headers: { Accept: kind === 'script' ? 'text/javascript, application/javascript' : 'image/svg+xml' },\n redirect: 'error',\n });\n if (!upstream.ok) {\n throw new Error(`Host navigation ${kind} failed (${upstream.status}).`);\n }\n const contentType = upstream.headers.get('content-type') ?? '';\n const validContentType =\n kind === 'script' ? /^(?:text|application)\\/javascript(?:;|$)/i.test(contentType) : /^image\\/svg\\+xml(?:;|$)/i.test(contentType);\n if (!validContentType) {\n throw new Error(`Host navigation ${kind} returned an invalid content type.`);\n }\n const maximumBytes = kind === 'script' ? MAX_HOST_NAVIGATION_BYTES : MAX_HOST_NAVIGATION_ASSET_BYTES;\n const body = await readResponseBody(upstream, maximumBytes, `Host navigation ${kind}`);\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', contentType);\n response.setHeader('X-Content-Type-Options', 'nosniff');\n response.end(body);\n }\n\n async #configuration(): Promise<IOidcClientConfiguration> {\n this.#configurationPromise ??= fetch(this.#configurationUrl, { headers: { Accept: 'application/json' }, redirect: 'error' })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Host OIDC configuration failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('Host OIDC configuration returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC issuer', true).href.replace(/\\/$/, '');\n return { clientId: requiredString(value, 'clientId'), issuer, scope: requiredString(value, 'scope') };\n })\n .catch((error) => {\n this.#configurationPromise = null;\n throw error;\n });\n return this.#configurationPromise;\n }\n\n async #discovery(): Promise<IOidcDiscovery> {\n this.#discoveryPromise ??= this.#configuration()\n .then(async (configuration) => {\n const response = await fetch(`${configuration.issuer}/.well-known/openid-configuration`, {\n headers: { Accept: 'application/json' },\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC discovery failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('OIDC discovery returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC discovery issuer', true).href.replace(/\\/$/, '');\n if (issuer !== configuration.issuer) {\n throw new Error('OIDC discovery issuer does not match the host configuration.');\n }\n const endpoint = (key: string): string => {\n const url = absoluteHttpsUrl(requiredString(value, key), `OIDC ${key}`, true);\n if (url.origin !== new URL(issuer).origin) {\n throw new Error(`OIDC ${key} must use the configured issuer origin.`);\n }\n return url.href;\n };\n const revocation = optionalString(value, 'revocation_endpoint');\n return {\n authorizationEndpoint: endpoint('authorization_endpoint'),\n endSessionEndpoint: endpoint('end_session_endpoint'),\n issuer,\n jwksUri: endpoint('jwks_uri'),\n revocationEndpoint: revocation ? endpoint('revocation_endpoint') : null,\n tokenEndpoint: endpoint('token_endpoint'),\n };\n })\n .catch((error) => {\n this.#discoveryPromise = null;\n throw error;\n });\n return this.#discoveryPromise;\n }\n\n async #login(request: IncomingMessage, response: ServerResponse, returnTo: string): Promise<void> {\n const logoutId = cookiesOf(request).get(LOGOUT_COOKIE);\n if (logoutId) {\n const logout = this.#logoutFlows.get(logoutId);\n this.#logoutFlows.delete(logoutId);\n if (logout) {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const endpoint = new URL(discovery.endSessionEndpoint);\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('post_logout_redirect_uri', new URL(LOGGED_OUT_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('state', returnTo);\n if (logout.idToken) {\n endpoint.searchParams.set('id_token_hint', logout.idToken);\n }\n redirect(response, endpoint.href, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const flowId = base64Url();\n const codeVerifier = base64Url(48);\n const flow: IOidcFlow = { codeVerifier, createdAt: Date.now(), nonce: base64Url(), returnTo, state: base64Url() };\n this.#flows.set(flowId, flow);\n\n const endpoint = new URL(discovery.authorizationEndpoint);\n endpoint.searchParams.set('response_type', 'code');\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('redirect_uri', new URL(CALLBACK_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('scope', configuration.scope);\n endpoint.searchParams.set('state', flow.state);\n endpoint.searchParams.set('nonce', flow.nonce);\n endpoint.searchParams.set('code_challenge', codeChallengeOf(codeVerifier));\n endpoint.searchParams.set('code_challenge_method', 'S256');\n redirect(response, endpoint.href, [cookie(FLOW_COOKIE, flowId, FLOW_MAX_AGE_SECONDS)]);\n }\n\n async #callback(request: IncomingMessage, response: ServerResponse, url: URL): Promise<void> {\n const flowId = cookiesOf(request).get(FLOW_COOKIE);\n const flow = flowId ? this.#flows.get(flowId) : undefined;\n if (flowId) {\n this.#flows.delete(flowId);\n }\n if (!flow || url.searchParams.get('state') !== flow.state) {\n writeJson(response, 400, { code: 'invalid_oidc_state', error: 'The OIDC callback state is invalid or expired.' });\n return;\n }\n const providerError = url.searchParams.get('error');\n if (providerError) {\n writeJson(response, 401, { code: providerError, error: url.searchParams.get('error_description') ?? 'Sign-in was rejected.' });\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n writeJson(response, 400, { code: 'missing_authorization_code', error: 'The OIDC callback did not include an authorization code.' });\n return;\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const fields = new URLSearchParams({\n client_id: configuration.clientId,\n code,\n code_verifier: flow.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: new URL(CALLBACK_PATH, this.#developmentOrigin).href,\n });\n const tokenResponse = await fetch(discovery.tokenEndpoint, {\n body: fields,\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!tokenResponse.ok) {\n throw new Error(`OIDC token exchange failed (${tokenResponse.status}).`);\n }\n const tokens = parseTokenResponse(await tokenResponse.json(), true);\n const idToken = tokens.idToken;\n if (!idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const jwks = this.#jwks.get(discovery.jwksUri) ?? createRemoteJWKSet(new URL(discovery.jwksUri));\n this.#jwks.set(discovery.jwksUri, jwks);\n const verified = await jwtVerify(idToken, jwks, {\n audience: configuration.clientId,\n issuer: discovery.issuer,\n requiredClaims: ['sub', 'nonce'],\n });\n if (verified.payload.nonce !== flow.nonce) {\n throw new Error('OIDC ID token nonce does not match the authorization request.');\n }\n\n const sessionId = base64Url();\n const session: ITokenSession = {\n accessToken: tokens.accessToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n idToken,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken: tokens.refreshToken,\n };\n this.#sessions.set(sessionId, session);\n redirect(response, flow.returnTo, [\n cookie(SESSION_COOKIE, sessionId, SESSION_MAX_AGE_SECONDS),\n refreshCookieOf(session),\n clearCookie(FLOW_COOKIE),\n ]);\n }\n\n async #logout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n if (session) {\n void this.#revoke(session).catch(() => undefined);\n }\n const logoutId = base64Url();\n this.#logoutFlows.set(logoutId, { createdAt: Date.now(), idToken: session?.idToken ?? null });\n response.setHeader('Set-Cookie', [\n clearCookie(SESSION_COOKIE),\n clearCookie(REFRESH_COOKIE),\n cookie(LOGOUT_COOKIE, logoutId, FLOW_MAX_AGE_SECONDS),\n ]);\n writeJson(response, 200, { loggedOut: true });\n }\n\n async #revoke(session: ITokenSession): Promise<void> {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const revocationEndpoint = discovery.revocationEndpoint;\n if (!revocationEndpoint) {\n return;\n }\n const revoke = async (token: string, hint: string): Promise<void> => {\n await fetch(revocationEndpoint, {\n body: new URLSearchParams({ client_id: configuration.clientId, token, token_type_hint: hint }),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n };\n await Promise.all([\n revoke(session.accessToken, 'access_token'),\n session.refreshToken ? revoke(session.refreshToken, 'refresh_token') : Promise.resolve(),\n ]);\n }\n\n // Standalone modules send the browser to their legacy /login page when an API call answers 401, and locally no such\n // page exists: serving the module again would repeat the 401 in a reload loop. Signed out, this is the development\n // sign-in; signed in, the module API itself refused the session, so the page says so and redirects nowhere.\n async #legacyLogin(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!(await this.#authenticatedSession(request, response))) {\n redirect(response, `${LOGIN_PATH}?returnTo=%2F`);\n return;\n }\n response.statusCode = 401;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n response.end(\n '<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Module sign-in refused</title></head>' +\n '<body style=\"font-family:system-ui,sans-serif;margin:3rem;line-height:1.5\">' +\n '<h1 style=\"font-size:1.25rem\">The module API refused your session</h1>' +\n '<p>You are signed in, but a module API request answered 401 and the module asked for its legacy sign-in page. ' +\n 'Check the module backend (MODULE_API_ORIGIN) and its dev server output.</p>' +\n `<p><a href=\"${LOGIN_PATH}?returnTo=%2F\">Sign in again</a> · <a href=\"/\">Back to the module</a></p>` +\n '</body></html>',\n );\n }\n\n async #authenticatedSession(request: IncomingMessage, response: ServerResponse): Promise<ITokenSession | null> {\n const cookies = cookiesOf(request);\n const sessionId = cookies.get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (!session) {\n return this.#restoredSession(decodedCookieValue(cookies.get(REFRESH_COOKIE)), response);\n }\n if (session.expiresAt > Date.now() + TOKEN_REFRESH_SKEW_MS) {\n return session;\n }\n if (!session.refreshToken) {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n return null;\n }\n session.refreshPromise ??= this.#refresh(session).finally(() => {\n session.refreshPromise = undefined;\n });\n try {\n await session.refreshPromise;\n response.setHeader('Set-Cookie', refreshCookieOf(session));\n return session;\n } catch {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n }\n\n // Starts a session from the browser's refresh cookie after this server restarted or another module's server signed\n // the developer in. Parallel requests carrying the same token share one refresh, and so, for a short while, do\n // requests the browser sent before the rotated cookie reached it: the sign-in service accepts a refresh token once,\n // so spending it again would fail and clear the cookie that replaced it.\n async #restoredSession(refreshToken: string | null, response: ServerResponse): Promise<ITokenSession | null> {\n if (!refreshToken) {\n return null;\n }\n let restore = this.#restores.get(refreshToken);\n if (!restore) {\n restore = this.#restore(refreshToken);\n this.#restores.set(refreshToken, restore);\n void restore.then((restoredSession) => {\n if (!restoredSession) {\n this.#restores.delete(refreshToken);\n return;\n }\n setTimeout(() => this.#restores.delete(refreshToken), SPENT_REFRESH_TOKEN_REUSE_MS).unref();\n });\n }\n const restored = await restore;\n if (!restored || this.#sessions.get(restored.sessionId) !== restored.session) {\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n response.setHeader('Set-Cookie', [cookie(SESSION_COOKIE, restored.sessionId, SESSION_MAX_AGE_SECONDS), refreshCookieOf(restored.session)]);\n return restored.session;\n }\n\n async #restore(refreshToken: string): Promise<{ session: ITokenSession; sessionId: string } | null> {\n const session: ITokenSession = {\n accessToken: '',\n expiresAt: 0,\n idToken: null,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken,\n };\n try {\n await this.#refresh(session);\n } catch {\n return null;\n }\n const sessionId = base64Url();\n this.#sessions.set(sessionId, session);\n return { session, sessionId };\n }\n\n async #refresh(session: ITokenSession): Promise<void> {\n const refreshToken = session.refreshToken;\n if (!refreshToken) {\n throw new Error('OIDC refresh token is unavailable.');\n }\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const response = await fetch(discovery.tokenEndpoint, {\n body: new URLSearchParams({\n client_id: configuration.clientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n }),\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC refresh failed (${response.status}).`);\n }\n const tokens = parseTokenResponse(await response.json(), false);\n session.accessToken = tokens.accessToken;\n session.expiresAt = Date.now() + tokens.expiresIn * 1000;\n session.refreshToken = tokens.refreshToken ?? session.refreshToken;\n session.idToken = tokens.idToken ?? session.idToken;\n }\n\n async #proxy(request: IncomingMessage, response: ServerResponse, localUrl: URL): Promise<void> {\n const session = await this.#authenticatedSession(request, response);\n if (!session) {\n // This server refreshes the session's tokens itself, so the browser can only sign in again. A refreshable code\n // would send the host client to a refresh endpoint that only the host has.\n writeJson(response, 401, { code: 'sign_in_required', error: 'Sign in is required.' });\n return;\n }\n const isModuleApi = localUrl.pathname.startsWith(this.#moduleApiPath);\n if (isModuleApi && !session.moduleNavigationContext) {\n // The host mounts a module only once the selection is known, so its first API calls already carry it. Locally\n // the module starts beside the navigation, so its first calls can arrive first: ask the host before forwarding.\n session.navigationContextPromise ??= this.#loadNavigationContext(session).finally(() => {\n session.navigationContextPromise = undefined;\n });\n await session.navigationContextPromise;\n }\n if (isModuleApi && session.moduleCookies.size === 0) {\n const previous = session.moduleBootstrapTail;\n let release = (): void => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n session.moduleBootstrapTail = previous.catch(() => undefined).then(() => current);\n await previous.catch(() => undefined);\n try {\n await this.#proxyAuthenticated(request, response, localUrl, session, true);\n } finally {\n release();\n }\n return;\n }\n await this.#proxyAuthenticated(request, response, localUrl, session, isModuleApi);\n }\n\n async #loadNavigationContext(session: ITokenSession): Promise<void> {\n const upstream = await fetch(new URL('/api/navigation', this.#hostOrigin), {\n headers: { Accept: 'application/json', Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n }).catch(() => null);\n if (!upstream?.ok || session.moduleNavigationContext) {\n return;\n }\n session.moduleNavigationContext = navigationContextIn(await upstream.json().catch(() => null));\n }\n\n async #proxyAuthenticated(\n request: IncomingMessage,\n response: ServerResponse,\n localUrl: URL,\n session: ITokenSession,\n isModuleApi: boolean,\n ): Promise<void> {\n const upstreamOrigin = isModuleApi ? this.#moduleApiOrigin : this.#hostOrigin;\n const upstreamUrl = new URL(`${localUrl.pathname}${localUrl.search}`, upstreamOrigin);\n const method = request.method ?? 'GET';\n const init: RequestInit = {\n headers: proxyHeaders(\n request,\n session.accessToken,\n isModuleApi ? { cookies: session.moduleCookies, navigationContext: session.moduleNavigationContext } : undefined,\n ),\n method,\n redirect: 'manual',\n };\n if (method !== 'GET' && method !== 'HEAD') {\n // A fixed-length body: the web front ends of the deployed module servers answer chunked request bodies with 404.\n const body = await requestBodyOf(request);\n if (!body) {\n writeJson(response, 413, { code: 'request_body_too_large', error: 'The request body exceeds the development proxy limit.' });\n return;\n }\n init.body = body;\n }\n const upstream = await fetch(upstreamUrl, init);\n let onChunk: ((chunk: Buffer) => void) | undefined;\n if (isModuleApi) {\n updateModuleCookies(session.moduleCookies, upstream.headers);\n } else if (localUrl.pathname === '/api/navigation' && upstream.ok) {\n session.moduleNavigationContext = navigationContextIn(\n await upstream\n .clone()\n .json()\n .catch(() => null),\n );\n } else if (localUrl.pathname === '/api/navigation/stream' && upstream.ok) {\n onChunk = navigationEventReader((context) => {\n session.moduleNavigationContext = context;\n });\n }\n await copyProxyResponse(upstream, response, onChunk);\n }\n\n #prune(): void {\n const now = Date.now();\n for (const [id, flow] of this.#flows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#flows.delete(id);\n }\n }\n for (const [id, flow] of this.#logoutFlows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#logoutFlows.delete(id);\n }\n }\n for (const [id, session] of this.#sessions) {\n if (!session.refreshToken && session.expiresAt + TOKEN_REFRESH_SKEW_MS < now) {\n this.#sessions.delete(id);\n }\n }\n }\n}\n\nexport const mfDevelopmentBffPlugin = (options: IMfDevelopmentBffPluginOptions): Plugin => {\n const middleware = createMfDevelopmentBffMiddleware(options);\n return {\n apply: 'serve',\n name: 'edynamix-mf-development-bff',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n middleware(request, response, next);\n });\n },\n // Locally the module runs inside the host shell, so its page shows the host's icon. The module's own favicon is\n // only copied into the build output, so this server has none to answer the browser's favicon request with.\n transformIndexHtml(html) {\n return {\n html: html.replace(ICON_LINK_PATTERN, ''),\n tags: [{ tag: 'link', attrs: { rel: 'icon', type: 'image/svg+xml', href: HOST_ICON_PATH }, injectTo: 'head' }],\n };\n },\n };\n};\n\nexport type TMfDevelopmentBffMiddleware = (request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void) => void;\n\nexport const createMfDevelopmentBffMiddleware = (options: IMfDevelopmentBffPluginOptions): TMfDevelopmentBffMiddleware => {\n const bff = new MfDevelopmentBff(options);\n return (request, response, next) => {\n void bff.handle(request, response, next);\n };\n};\n"]}
|
package/dist/vite-development.js
CHANGED
|
@@ -19,6 +19,7 @@ const SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;
|
|
|
19
19
|
const REFRESH_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
|
|
20
20
|
const FLOW_MAX_AGE_SECONDS = 10 * 60;
|
|
21
21
|
const TOKEN_REFRESH_SKEW_MS = 30_000;
|
|
22
|
+
const SPENT_REFRESH_TOKEN_REUSE_MS = 30_000;
|
|
22
23
|
const NAVIGATION_GROUP_HEADER = 'X-Edyn-Navigation-Group-Id';
|
|
23
24
|
const NAVIGATION_DEALER_HEADER = 'X-Edyn-Navigation-Dealer-Id';
|
|
24
25
|
const NAVIGATION_DEALERS_HEADER = 'X-Edyn-Navigation-Dealer-Ids';
|
|
@@ -702,18 +703,27 @@ class MfDevelopmentBff {
|
|
|
702
703
|
}
|
|
703
704
|
}
|
|
704
705
|
// Starts a session from the browser's refresh cookie after this server restarted or another module's server signed
|
|
705
|
-
// the developer in. Parallel requests carrying the same token share one refresh
|
|
706
|
+
// the developer in. Parallel requests carrying the same token share one refresh, and so, for a short while, do
|
|
707
|
+
// requests the browser sent before the rotated cookie reached it: the sign-in service accepts a refresh token once,
|
|
708
|
+
// so spending it again would fail and clear the cookie that replaced it.
|
|
706
709
|
async #restoredSession(refreshToken, response) {
|
|
707
710
|
if (!refreshToken) {
|
|
708
711
|
return null;
|
|
709
712
|
}
|
|
710
713
|
let restore = this.#restores.get(refreshToken);
|
|
711
714
|
if (!restore) {
|
|
712
|
-
restore = this.#restore(refreshToken)
|
|
715
|
+
restore = this.#restore(refreshToken);
|
|
713
716
|
this.#restores.set(refreshToken, restore);
|
|
717
|
+
void restore.then((restoredSession) => {
|
|
718
|
+
if (!restoredSession) {
|
|
719
|
+
this.#restores.delete(refreshToken);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
setTimeout(() => this.#restores.delete(refreshToken), SPENT_REFRESH_TOKEN_REUSE_MS).unref();
|
|
723
|
+
});
|
|
714
724
|
}
|
|
715
725
|
const restored = await restore;
|
|
716
|
-
if (!restored) {
|
|
726
|
+
if (!restored || this.#sessions.get(restored.sessionId) !== restored.session) {
|
|
717
727
|
response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));
|
|
718
728
|
return null;
|
|
719
729
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-development.js","sourceRoot":"","sources":["../src/vite-development.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { Plugin } from 'vite';\n\nconst SESSION_COOKIE = '__Host-edyn_dev_session';\nconst REFRESH_COOKIE = '__Host-edyn_dev_refresh';\nconst FLOW_COOKIE = '__Host-edyn_oidc_flow';\nconst LOGOUT_COOKIE = '__Host-edyn_oidc_logout';\nconst CALLBACK_PATH = '/callback';\nconst LOGGED_OUT_PATH = '/logged-out';\nconst LOGIN_PATH = '/api/dev-auth/login';\nconst LEGACY_LOGIN_PATH = '/login';\nexport const MF_DEVELOPMENT_HOST_NAVIGATION_PATH = '/api/dev-host-navigation/index.js';\nconst HOST_ICON_PATH = '/exsto-icon.svg';\nconst HOST_NAVIGATION_ASSET_PATHS = new Set([HOST_ICON_PATH, '/exsto-text.svg']);\nconst ICON_LINK_PATTERN = /<link\\b[^>]*\\brel=[\"'](?:shortcut\\s+)?icon[\"'][^>]*>\\s*/gi;\nconst MAX_HOST_NAVIGATION_BYTES = 5 * 1024 * 1024;\nconst MAX_HOST_NAVIGATION_ASSET_BYTES = 512 * 1024;\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst REFRESH_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;\nconst FLOW_MAX_AGE_SECONDS = 10 * 60;\nconst TOKEN_REFRESH_SKEW_MS = 30_000;\nconst NAVIGATION_GROUP_HEADER = 'X-Edyn-Navigation-Group-Id';\nconst NAVIGATION_DEALER_HEADER = 'X-Edyn-Navigation-Dealer-Id';\nconst NAVIGATION_DEALERS_HEADER = 'X-Edyn-Navigation-Dealer-Ids';\nconst NAVIGATION_HEADERS = [NAVIGATION_GROUP_HEADER, NAVIGATION_DEALER_HEADER, NAVIGATION_DEALERS_HEADER].map((name) => name.toLowerCase());\nconst MAX_NAVIGATION_EVENT_BYTES = 5 * 1024 * 1024;\nconst MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024;\nconst EVENT_SEPARATOR = /\\r?\\n\\r?\\n/;\nconst COOKIE_NAME_PATTERN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst COOKIE_VALUE_PATTERN = /^[\\x20-\\x7e]*$/;\nconst ALLOWED_HOST_API_PATHS = [\n '/api/auth/me',\n '/api/navigation',\n '/api/navigation/access-check',\n '/api/navigation/access-request',\n '/api/navigation/counter-stream',\n] as const;\n\ninterface IOidcClientConfiguration {\n clientId: string;\n issuer: string;\n scope: string;\n}\n\ninterface IOidcDiscovery {\n authorizationEndpoint: string;\n endSessionEndpoint: string;\n issuer: string;\n jwksUri: string;\n revocationEndpoint: string | null;\n tokenEndpoint: string;\n}\n\ninterface IOidcFlow {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n returnTo: string;\n state: string;\n}\n\ninterface IModuleNavigationContext {\n dealerIds: readonly number[];\n groupId: number;\n}\n\ninterface IModuleProxyContext {\n cookies: Map<string, string>;\n navigationContext: IModuleNavigationContext | null;\n}\n\ninterface ITokenSession {\n accessToken: string;\n expiresAt: number;\n idToken: string | null;\n moduleBootstrapTail: Promise<void>;\n moduleCookies: Map<string, string>;\n moduleNavigationContext: IModuleNavigationContext | null;\n navigationContextPromise?: Promise<void>;\n refreshPromise?: Promise<void>;\n refreshToken: string | null;\n}\n\ninterface ILogoutFlow {\n createdAt: number;\n idToken: string | null;\n}\n\ninterface ITokenResponse {\n accessToken: string;\n expiresIn: number;\n idToken: string | null;\n refreshToken: string | null;\n tokenType: string;\n}\n\nexport interface IMfDevelopmentBffPluginOptions {\n developmentOrigin?: string;\n hostNavigationUrl: string;\n moduleApiOrigin: string;\n moduleApiPath: string;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst requiredString = (value: Record<string, unknown>, key: string): string => {\n const candidate = value[key];\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response is missing ${key}.`);\n }\n return candidate;\n};\n\nconst optionalString = (value: Record<string, unknown>, key: string): string | null => {\n const candidate = value[key];\n if (candidate === undefined || candidate === null) {\n return null;\n }\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response contains an invalid ${key}.`);\n }\n return candidate;\n};\n\nconst absoluteHttpsUrl = (candidate: string, label: string, allowLocalHttp = false): URL => {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new Error(`${label} must be an absolute URL.`);\n }\n const isLocalHttp = allowLocalHttp && url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1');\n if (url.protocol !== 'https:' && !isLocalHttp) {\n throw new Error(`${label} must use HTTPS.`);\n }\n if (url.username || url.password || url.hash) {\n throw new Error(`${label} must not contain credentials or a fragment.`);\n }\n return url;\n};\n\nconst moduleApiPrefix = (candidate: string): string => {\n if (!/^\\/[a-z][a-z0-9-]*\\/api\\/$/.test(candidate)) {\n throw new Error('moduleApiPath must use the canonical /module/api/ boundary.');\n }\n return candidate;\n};\n\nconst base64Url = (size = 32): string => randomBytes(size).toString('base64url');\nconst codeChallengeOf = (verifier: string): string => createHash('sha256').update(verifier).digest('base64url');\n\nconst cookiesOf = (request: IncomingMessage): ReadonlyMap<string, string> => {\n const cookies = new Map<string, string>();\n for (const part of (request.headers.cookie ?? '').split(';')) {\n const separator = part.indexOf('=');\n if (separator <= 0) {\n continue;\n }\n const name = part.slice(0, separator).trim();\n const value = part.slice(separator + 1).trim();\n if (name && value) {\n cookies.set(name, value);\n }\n }\n return cookies;\n};\n\nconst cookie = (name: string, value: string, maxAge: number): string => `${name}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${maxAge}`;\n\nconst clearCookie = (name: string): string => cookie(name, '', 0);\n\n// The refresh token stays in an HttpOnly cookie for this origin, which every module's development server shares, so\n// one sign-in survives restarts and module switches until the token expires; page scripts can never read it.\nconst refreshCookieOf = (session: ITokenSession): string =>\n session.refreshToken\n ? cookie(REFRESH_COOKIE, encodeURIComponent(session.refreshToken), REFRESH_COOKIE_MAX_AGE_SECONDS)\n : clearCookie(REFRESH_COOKIE);\n\nconst decodedCookieValue = (value: string | undefined): string | null => {\n if (!value) {\n return null;\n }\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n};\n\nconst writeJson = (response: ServerResponse, status: number, body: unknown): void => {\n response.statusCode = status;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(JSON.stringify(body));\n};\n\nconst redirect = (response: ServerResponse, location: string, cookies: string[] = []): void => {\n response.statusCode = 302;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Location', location);\n if (cookies.length > 0) {\n response.setHeader('Set-Cookie', cookies);\n }\n response.end();\n};\n\nconst safeReturnTo = (candidate: string | null): string => {\n if (!candidate?.startsWith('/') || candidate.startsWith('//')) {\n return '/';\n }\n try {\n const parsed = new URL(candidate, 'https://localhost');\n return `${parsed.pathname}${parsed.search}${parsed.hash}`;\n } catch {\n return '/';\n }\n};\n\nconst parseTokenResponse = (value: unknown, requireIdToken: boolean): ITokenResponse => {\n if (!isRecord(value)) {\n throw new Error('OIDC token endpoint returned an invalid response.');\n }\n const expiresIn = value.expires_in;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw new Error('OIDC token endpoint returned an invalid expires_in value.');\n }\n const idToken = optionalString(value, 'id_token');\n if (requireIdToken && !idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const tokenType = requiredString(value, 'token_type');\n if (tokenType.toLowerCase() !== 'bearer') {\n throw new Error('OIDC token endpoint returned an unsupported token type.');\n }\n return {\n accessToken: requiredString(value, 'access_token'),\n expiresIn,\n idToken,\n refreshToken: optionalString(value, 'refresh_token'),\n tokenType,\n };\n};\n\nconst pathIsHostApi = (path: string): boolean => {\n return ALLOWED_HOST_API_PATHS.some((candidate) => path === candidate || path.startsWith(`${candidate}/`));\n};\n\n// The host validates a selection the browser sends it, so host requests keep the navigation headers. A module trusts\n// them, so its requests carry only the selection the host last confirmed.\nconst proxyHeaders = (request: IncomingMessage, accessToken: string, module?: IModuleProxyContext): Headers => {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n const lowerName = name.toLowerCase();\n if (\n !value ||\n name.startsWith(':') ||\n ['authorization', 'connection', 'content-length', 'cookie', 'host', 'origin', 'transfer-encoding'].includes(lowerName) ||\n (module && NAVIGATION_HEADERS.includes(lowerName))\n ) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (module?.cookies.size) {\n headers.set('Cookie', [...module.cookies.values()].join('; '));\n }\n const navigationContext = module?.navigationContext;\n if (navigationContext) {\n headers.set(NAVIGATION_GROUP_HEADER, String(navigationContext.groupId));\n headers.set(NAVIGATION_DEALERS_HEADER, navigationContext.dealerIds.join(','));\n if (navigationContext.dealerIds.length === 1) {\n headers.set(NAVIGATION_DEALER_HEADER, String(navigationContext.dealerIds[0]));\n }\n }\n return headers;\n};\n\nconst navigationContextIn = (value: unknown): IModuleNavigationContext | null => {\n if (!isRecord(value) || !isRecord(value.context)) {\n return null;\n }\n const { selectedDealerId, selectedDealerIds, selectedGroupId } = value.context;\n const dealerIds: unknown[] = Array.isArray(selectedDealerIds) ? selectedDealerIds : [selectedDealerId];\n if (\n !Number.isSafeInteger(selectedGroupId) ||\n (selectedGroupId as number) <= 0 ||\n dealerIds.length === 0 ||\n !dealerIds.every((dealerId) => Number.isSafeInteger(dealerId) && (dealerId as number) >= 0)\n ) {\n return null;\n }\n return { dealerIds: dealerIds as number[], groupId: selectedGroupId as number };\n};\n\n// Reads the selection each `navigation` event of the host's navigation stream confirms, chunk by chunk as it passes.\nconst navigationEventReader = (onContext: (context: IModuleNavigationContext) => void): ((chunk: Buffer) => void) => {\n const decoder = new TextDecoder();\n let pending = '';\n return (chunk) => {\n pending += decoder.decode(chunk, { stream: true });\n for (let separator = EVENT_SEPARATOR.exec(pending); separator; separator = EVENT_SEPARATOR.exec(pending)) {\n const block = pending.slice(0, separator.index);\n pending = pending.slice(separator.index + separator[0].length);\n const lines = block.split(/\\r?\\n/);\n if (!lines.includes('event: navigation') && !lines.includes('event:navigation')) {\n continue;\n }\n const data = lines\n .filter((line) => line.startsWith('data:'))\n .map((line) => line.slice(5).replace(/^ /, ''))\n .join('\\n');\n let payload: unknown = null;\n try {\n payload = JSON.parse(data);\n } catch {\n continue;\n }\n const context = navigationContextIn(payload);\n if (context) {\n onContext(context);\n }\n }\n if (pending.length > MAX_NAVIGATION_EVENT_BYTES) {\n pending = '';\n }\n };\n};\n\nconst updateModuleCookies = (cookies: Map<string, string>, headers: Headers): void => {\n const values = headers.getSetCookie();\n for (const value of values) {\n if (value.length > 4096) {\n continue;\n }\n const [pair, ...attributes] = value.split(';');\n const separator = pair?.indexOf('=') ?? -1;\n if (!pair || separator <= 0) {\n continue;\n }\n const name = pair.slice(0, separator).trim();\n const cookieValue = pair.slice(separator + 1).trim();\n if (!COOKIE_NAME_PATTERN.test(name) || !COOKIE_VALUE_PATTERN.test(cookieValue) || cookieValue.includes(';')) {\n continue;\n }\n const deleted = cookieValue === '' || attributes.some((attribute) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(attribute));\n if (deleted) {\n cookies.delete(name);\n continue;\n }\n if (!cookies.has(name) && cookies.size >= 32) {\n continue;\n }\n cookies.set(name, `${name}=${cookieValue}`);\n }\n};\n\nconst requestBodyOf = async (request: IncomingMessage): Promise<Buffer | null> => {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string);\n size += buffer.length;\n if (size > MAX_REQUEST_BODY_BYTES) {\n return null;\n }\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n};\n\nconst copyProxyResponse = async (upstream: Response, response: ServerResponse, onChunk?: (chunk: Buffer) => void): Promise<void> => {\n response.statusCode = upstream.status;\n for (const [name, value] of upstream.headers) {\n if (!['connection', 'content-encoding', 'content-length', 'set-cookie', 'transfer-encoding'].includes(name.toLowerCase())) {\n response.setHeader(name, value);\n }\n }\n response.setHeader('Cache-Control', upstream.headers.get('cache-control') ?? 'no-store');\n if (!upstream.body) {\n response.end();\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const readable = Readable.fromWeb(upstream.body as never);\n if (onChunk) {\n readable.on('data', onChunk);\n }\n readable.once('error', reject);\n response.once('error', reject);\n response.once('finish', resolve);\n readable.pipe(response);\n });\n};\n\nconst readResponseBody = async (response: Response, maximumBytes: number, label: string): Promise<Buffer> => {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {\n await response.body?.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n if (!response.body) {\n return Buffer.alloc(0);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maximumBytes) {\n await reader.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks, totalBytes);\n};\n\nclass MfDevelopmentBff {\n readonly #configurationUrl: URL;\n readonly #developmentOrigin: URL;\n readonly #flows = new Map<string, IOidcFlow>();\n readonly #hostOrigin: URL;\n readonly #hostNavigationUrl: URL;\n readonly #jwks = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n readonly #logoutFlows = new Map<string, ILogoutFlow>();\n readonly #moduleApiOrigin: URL;\n readonly #moduleApiPath: string;\n readonly #sessions = new Map<string, ITokenSession>();\n readonly #restores = new Map<string, Promise<{ session: ITokenSession; sessionId: string } | null>>();\n #configurationPromise: Promise<IOidcClientConfiguration> | null = null;\n #discoveryPromise: Promise<IOidcDiscovery> | null = null;\n\n constructor(options: IMfDevelopmentBffPluginOptions) {\n const hostNavigation = absoluteHttpsUrl(options.hostNavigationUrl, 'hostNavigationUrl', true);\n this.#hostNavigationUrl = hostNavigation;\n this.#hostOrigin = new URL(hostNavigation.origin);\n this.#configurationUrl = new URL('/api/auth/oidc-configuration', this.#hostOrigin);\n this.#developmentOrigin = absoluteHttpsUrl(options.developmentOrigin ?? 'https://localhost:4200', 'developmentOrigin');\n if (this.#developmentOrigin.pathname !== '/' || this.#developmentOrigin.search || this.#developmentOrigin.hash) {\n throw new Error('developmentOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiOrigin = absoluteHttpsUrl(options.moduleApiOrigin, 'moduleApiOrigin', true);\n if (this.#moduleApiOrigin.pathname !== '/' || this.#moduleApiOrigin.search || this.#moduleApiOrigin.hash) {\n throw new Error('moduleApiOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiPath = moduleApiPrefix(options.moduleApiPath);\n }\n\n async handle(request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void): Promise<void> {\n if (!request.url || !request.method) {\n next();\n return;\n }\n const url = new URL(request.url, this.#developmentOrigin);\n this.#prune();\n\n try {\n if (request.method === 'GET' && url.pathname === MF_DEVELOPMENT_HOST_NAVIGATION_PATH) {\n await this.#hostNavigationResource(response, this.#hostNavigationUrl, 'script');\n return;\n }\n if (request.method === 'GET' && HOST_NAVIGATION_ASSET_PATHS.has(url.pathname)) {\n await this.#hostNavigationResource(response, new URL(url.pathname, this.#hostOrigin), 'asset');\n return;\n }\n if (request.method === 'GET' && url.pathname.toLowerCase() === LEGACY_LOGIN_PATH) {\n await this.#legacyLogin(request, response);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGIN_PATH) {\n await this.#login(request, response, safeReturnTo(url.searchParams.get('returnTo')));\n return;\n }\n if (request.method === 'GET' && url.pathname === CALLBACK_PATH) {\n await this.#callback(request, response, url);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGGED_OUT_PATH) {\n const returnTo = safeReturnTo(url.searchParams.get('state'));\n redirect(response, `${LOGIN_PATH}?returnTo=${encodeURIComponent(returnTo)}`, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n if (request.method === 'GET' && url.pathname === '/frontchannel-logout') {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.statusCode = 204;\n response.setHeader('Set-Cookie', [clearCookie(SESSION_COOKIE), clearCookie(REFRESH_COOKIE)]);\n response.end();\n return;\n }\n if (request.method === 'POST' && url.pathname === '/api/auth/logout') {\n await this.#logout(request, response);\n return;\n }\n if (pathIsHostApi(url.pathname) || url.pathname.startsWith(this.#moduleApiPath)) {\n await this.#proxy(request, response, url);\n return;\n }\n next();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Development authentication failed.';\n writeJson(response, 502, { code: 'development_authentication_failed', error: message });\n }\n }\n\n async #hostNavigationResource(response: ServerResponse, upstreamUrl: URL, kind: 'asset' | 'script'): Promise<void> {\n const upstream = await fetch(upstreamUrl, {\n headers: { Accept: kind === 'script' ? 'text/javascript, application/javascript' : 'image/svg+xml' },\n redirect: 'error',\n });\n if (!upstream.ok) {\n throw new Error(`Host navigation ${kind} failed (${upstream.status}).`);\n }\n const contentType = upstream.headers.get('content-type') ?? '';\n const validContentType =\n kind === 'script' ? /^(?:text|application)\\/javascript(?:;|$)/i.test(contentType) : /^image\\/svg\\+xml(?:;|$)/i.test(contentType);\n if (!validContentType) {\n throw new Error(`Host navigation ${kind} returned an invalid content type.`);\n }\n const maximumBytes = kind === 'script' ? MAX_HOST_NAVIGATION_BYTES : MAX_HOST_NAVIGATION_ASSET_BYTES;\n const body = await readResponseBody(upstream, maximumBytes, `Host navigation ${kind}`);\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', contentType);\n response.setHeader('X-Content-Type-Options', 'nosniff');\n response.end(body);\n }\n\n async #configuration(): Promise<IOidcClientConfiguration> {\n this.#configurationPromise ??= fetch(this.#configurationUrl, { headers: { Accept: 'application/json' }, redirect: 'error' })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Host OIDC configuration failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('Host OIDC configuration returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC issuer', true).href.replace(/\\/$/, '');\n return { clientId: requiredString(value, 'clientId'), issuer, scope: requiredString(value, 'scope') };\n })\n .catch((error) => {\n this.#configurationPromise = null;\n throw error;\n });\n return this.#configurationPromise;\n }\n\n async #discovery(): Promise<IOidcDiscovery> {\n this.#discoveryPromise ??= this.#configuration()\n .then(async (configuration) => {\n const response = await fetch(`${configuration.issuer}/.well-known/openid-configuration`, {\n headers: { Accept: 'application/json' },\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC discovery failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('OIDC discovery returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC discovery issuer', true).href.replace(/\\/$/, '');\n if (issuer !== configuration.issuer) {\n throw new Error('OIDC discovery issuer does not match the host configuration.');\n }\n const endpoint = (key: string): string => {\n const url = absoluteHttpsUrl(requiredString(value, key), `OIDC ${key}`, true);\n if (url.origin !== new URL(issuer).origin) {\n throw new Error(`OIDC ${key} must use the configured issuer origin.`);\n }\n return url.href;\n };\n const revocation = optionalString(value, 'revocation_endpoint');\n return {\n authorizationEndpoint: endpoint('authorization_endpoint'),\n endSessionEndpoint: endpoint('end_session_endpoint'),\n issuer,\n jwksUri: endpoint('jwks_uri'),\n revocationEndpoint: revocation ? endpoint('revocation_endpoint') : null,\n tokenEndpoint: endpoint('token_endpoint'),\n };\n })\n .catch((error) => {\n this.#discoveryPromise = null;\n throw error;\n });\n return this.#discoveryPromise;\n }\n\n async #login(request: IncomingMessage, response: ServerResponse, returnTo: string): Promise<void> {\n const logoutId = cookiesOf(request).get(LOGOUT_COOKIE);\n if (logoutId) {\n const logout = this.#logoutFlows.get(logoutId);\n this.#logoutFlows.delete(logoutId);\n if (logout) {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const endpoint = new URL(discovery.endSessionEndpoint);\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('post_logout_redirect_uri', new URL(LOGGED_OUT_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('state', returnTo);\n if (logout.idToken) {\n endpoint.searchParams.set('id_token_hint', logout.idToken);\n }\n redirect(response, endpoint.href, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const flowId = base64Url();\n const codeVerifier = base64Url(48);\n const flow: IOidcFlow = { codeVerifier, createdAt: Date.now(), nonce: base64Url(), returnTo, state: base64Url() };\n this.#flows.set(flowId, flow);\n\n const endpoint = new URL(discovery.authorizationEndpoint);\n endpoint.searchParams.set('response_type', 'code');\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('redirect_uri', new URL(CALLBACK_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('scope', configuration.scope);\n endpoint.searchParams.set('state', flow.state);\n endpoint.searchParams.set('nonce', flow.nonce);\n endpoint.searchParams.set('code_challenge', codeChallengeOf(codeVerifier));\n endpoint.searchParams.set('code_challenge_method', 'S256');\n redirect(response, endpoint.href, [cookie(FLOW_COOKIE, flowId, FLOW_MAX_AGE_SECONDS)]);\n }\n\n async #callback(request: IncomingMessage, response: ServerResponse, url: URL): Promise<void> {\n const flowId = cookiesOf(request).get(FLOW_COOKIE);\n const flow = flowId ? this.#flows.get(flowId) : undefined;\n if (flowId) {\n this.#flows.delete(flowId);\n }\n if (!flow || url.searchParams.get('state') !== flow.state) {\n writeJson(response, 400, { code: 'invalid_oidc_state', error: 'The OIDC callback state is invalid or expired.' });\n return;\n }\n const providerError = url.searchParams.get('error');\n if (providerError) {\n writeJson(response, 401, { code: providerError, error: url.searchParams.get('error_description') ?? 'Sign-in was rejected.' });\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n writeJson(response, 400, { code: 'missing_authorization_code', error: 'The OIDC callback did not include an authorization code.' });\n return;\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const fields = new URLSearchParams({\n client_id: configuration.clientId,\n code,\n code_verifier: flow.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: new URL(CALLBACK_PATH, this.#developmentOrigin).href,\n });\n const tokenResponse = await fetch(discovery.tokenEndpoint, {\n body: fields,\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!tokenResponse.ok) {\n throw new Error(`OIDC token exchange failed (${tokenResponse.status}).`);\n }\n const tokens = parseTokenResponse(await tokenResponse.json(), true);\n const idToken = tokens.idToken;\n if (!idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const jwks = this.#jwks.get(discovery.jwksUri) ?? createRemoteJWKSet(new URL(discovery.jwksUri));\n this.#jwks.set(discovery.jwksUri, jwks);\n const verified = await jwtVerify(idToken, jwks, {\n audience: configuration.clientId,\n issuer: discovery.issuer,\n requiredClaims: ['sub', 'nonce'],\n });\n if (verified.payload.nonce !== flow.nonce) {\n throw new Error('OIDC ID token nonce does not match the authorization request.');\n }\n\n const sessionId = base64Url();\n const session: ITokenSession = {\n accessToken: tokens.accessToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n idToken,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken: tokens.refreshToken,\n };\n this.#sessions.set(sessionId, session);\n redirect(response, flow.returnTo, [\n cookie(SESSION_COOKIE, sessionId, SESSION_MAX_AGE_SECONDS),\n refreshCookieOf(session),\n clearCookie(FLOW_COOKIE),\n ]);\n }\n\n async #logout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n if (session) {\n void this.#revoke(session).catch(() => undefined);\n }\n const logoutId = base64Url();\n this.#logoutFlows.set(logoutId, { createdAt: Date.now(), idToken: session?.idToken ?? null });\n response.setHeader('Set-Cookie', [\n clearCookie(SESSION_COOKIE),\n clearCookie(REFRESH_COOKIE),\n cookie(LOGOUT_COOKIE, logoutId, FLOW_MAX_AGE_SECONDS),\n ]);\n writeJson(response, 200, { loggedOut: true });\n }\n\n async #revoke(session: ITokenSession): Promise<void> {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const revocationEndpoint = discovery.revocationEndpoint;\n if (!revocationEndpoint) {\n return;\n }\n const revoke = async (token: string, hint: string): Promise<void> => {\n await fetch(revocationEndpoint, {\n body: new URLSearchParams({ client_id: configuration.clientId, token, token_type_hint: hint }),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n };\n await Promise.all([\n revoke(session.accessToken, 'access_token'),\n session.refreshToken ? revoke(session.refreshToken, 'refresh_token') : Promise.resolve(),\n ]);\n }\n\n // Standalone modules send the browser to their legacy /login page when an API call answers 401, and locally no such\n // page exists: serving the module again would repeat the 401 in a reload loop. Signed out, this is the development\n // sign-in; signed in, the module API itself refused the session, so the page says so and redirects nowhere.\n async #legacyLogin(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!(await this.#authenticatedSession(request, response))) {\n redirect(response, `${LOGIN_PATH}?returnTo=%2F`);\n return;\n }\n response.statusCode = 401;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n response.end(\n '<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Module sign-in refused</title></head>' +\n '<body style=\"font-family:system-ui,sans-serif;margin:3rem;line-height:1.5\">' +\n '<h1 style=\"font-size:1.25rem\">The module API refused your session</h1>' +\n '<p>You are signed in, but a module API request answered 401 and the module asked for its legacy sign-in page. ' +\n 'Check the module backend (MODULE_API_ORIGIN) and its dev server output.</p>' +\n `<p><a href=\"${LOGIN_PATH}?returnTo=%2F\">Sign in again</a> · <a href=\"/\">Back to the module</a></p>` +\n '</body></html>',\n );\n }\n\n async #authenticatedSession(request: IncomingMessage, response: ServerResponse): Promise<ITokenSession | null> {\n const cookies = cookiesOf(request);\n const sessionId = cookies.get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (!session) {\n return this.#restoredSession(decodedCookieValue(cookies.get(REFRESH_COOKIE)), response);\n }\n if (session.expiresAt > Date.now() + TOKEN_REFRESH_SKEW_MS) {\n return session;\n }\n if (!session.refreshToken) {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n return null;\n }\n session.refreshPromise ??= this.#refresh(session).finally(() => {\n session.refreshPromise = undefined;\n });\n try {\n await session.refreshPromise;\n response.setHeader('Set-Cookie', refreshCookieOf(session));\n return session;\n } catch {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n }\n\n // Starts a session from the browser's refresh cookie after this server restarted or another module's server signed\n // the developer in. Parallel requests carrying the same token share one refresh.\n async #restoredSession(refreshToken: string | null, response: ServerResponse): Promise<ITokenSession | null> {\n if (!refreshToken) {\n return null;\n }\n let restore = this.#restores.get(refreshToken);\n if (!restore) {\n restore = this.#restore(refreshToken).finally(() => this.#restores.delete(refreshToken));\n this.#restores.set(refreshToken, restore);\n }\n const restored = await restore;\n if (!restored) {\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n response.setHeader('Set-Cookie', [cookie(SESSION_COOKIE, restored.sessionId, SESSION_MAX_AGE_SECONDS), refreshCookieOf(restored.session)]);\n return restored.session;\n }\n\n async #restore(refreshToken: string): Promise<{ session: ITokenSession; sessionId: string } | null> {\n const session: ITokenSession = {\n accessToken: '',\n expiresAt: 0,\n idToken: null,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken,\n };\n try {\n await this.#refresh(session);\n } catch {\n return null;\n }\n const sessionId = base64Url();\n this.#sessions.set(sessionId, session);\n return { session, sessionId };\n }\n\n async #refresh(session: ITokenSession): Promise<void> {\n const refreshToken = session.refreshToken;\n if (!refreshToken) {\n throw new Error('OIDC refresh token is unavailable.');\n }\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const response = await fetch(discovery.tokenEndpoint, {\n body: new URLSearchParams({\n client_id: configuration.clientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n }),\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC refresh failed (${response.status}).`);\n }\n const tokens = parseTokenResponse(await response.json(), false);\n session.accessToken = tokens.accessToken;\n session.expiresAt = Date.now() + tokens.expiresIn * 1000;\n session.refreshToken = tokens.refreshToken ?? session.refreshToken;\n session.idToken = tokens.idToken ?? session.idToken;\n }\n\n async #proxy(request: IncomingMessage, response: ServerResponse, localUrl: URL): Promise<void> {\n const session = await this.#authenticatedSession(request, response);\n if (!session) {\n // This server refreshes the session's tokens itself, so the browser can only sign in again. A refreshable code\n // would send the host client to a refresh endpoint that only the host has.\n writeJson(response, 401, { code: 'sign_in_required', error: 'Sign in is required.' });\n return;\n }\n const isModuleApi = localUrl.pathname.startsWith(this.#moduleApiPath);\n if (isModuleApi && !session.moduleNavigationContext) {\n // The host mounts a module only once the selection is known, so its first API calls already carry it. Locally\n // the module starts beside the navigation, so its first calls can arrive first: ask the host before forwarding.\n session.navigationContextPromise ??= this.#loadNavigationContext(session).finally(() => {\n session.navigationContextPromise = undefined;\n });\n await session.navigationContextPromise;\n }\n if (isModuleApi && session.moduleCookies.size === 0) {\n const previous = session.moduleBootstrapTail;\n let release = (): void => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n session.moduleBootstrapTail = previous.catch(() => undefined).then(() => current);\n await previous.catch(() => undefined);\n try {\n await this.#proxyAuthenticated(request, response, localUrl, session, true);\n } finally {\n release();\n }\n return;\n }\n await this.#proxyAuthenticated(request, response, localUrl, session, isModuleApi);\n }\n\n async #loadNavigationContext(session: ITokenSession): Promise<void> {\n const upstream = await fetch(new URL('/api/navigation', this.#hostOrigin), {\n headers: { Accept: 'application/json', Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n }).catch(() => null);\n if (!upstream?.ok || session.moduleNavigationContext) {\n return;\n }\n session.moduleNavigationContext = navigationContextIn(await upstream.json().catch(() => null));\n }\n\n async #proxyAuthenticated(\n request: IncomingMessage,\n response: ServerResponse,\n localUrl: URL,\n session: ITokenSession,\n isModuleApi: boolean,\n ): Promise<void> {\n const upstreamOrigin = isModuleApi ? this.#moduleApiOrigin : this.#hostOrigin;\n const upstreamUrl = new URL(`${localUrl.pathname}${localUrl.search}`, upstreamOrigin);\n const method = request.method ?? 'GET';\n const init: RequestInit = {\n headers: proxyHeaders(\n request,\n session.accessToken,\n isModuleApi ? { cookies: session.moduleCookies, navigationContext: session.moduleNavigationContext } : undefined,\n ),\n method,\n redirect: 'manual',\n };\n if (method !== 'GET' && method !== 'HEAD') {\n // A fixed-length body: the web front ends of the deployed module servers answer chunked request bodies with 404.\n const body = await requestBodyOf(request);\n if (!body) {\n writeJson(response, 413, { code: 'request_body_too_large', error: 'The request body exceeds the development proxy limit.' });\n return;\n }\n init.body = body;\n }\n const upstream = await fetch(upstreamUrl, init);\n let onChunk: ((chunk: Buffer) => void) | undefined;\n if (isModuleApi) {\n updateModuleCookies(session.moduleCookies, upstream.headers);\n } else if (localUrl.pathname === '/api/navigation' && upstream.ok) {\n session.moduleNavigationContext = navigationContextIn(\n await upstream\n .clone()\n .json()\n .catch(() => null),\n );\n } else if (localUrl.pathname === '/api/navigation/stream' && upstream.ok) {\n onChunk = navigationEventReader((context) => {\n session.moduleNavigationContext = context;\n });\n }\n await copyProxyResponse(upstream, response, onChunk);\n }\n\n #prune(): void {\n const now = Date.now();\n for (const [id, flow] of this.#flows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#flows.delete(id);\n }\n }\n for (const [id, flow] of this.#logoutFlows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#logoutFlows.delete(id);\n }\n }\n for (const [id, session] of this.#sessions) {\n if (!session.refreshToken && session.expiresAt + TOKEN_REFRESH_SKEW_MS < now) {\n this.#sessions.delete(id);\n }\n }\n }\n}\n\nexport const mfDevelopmentBffPlugin = (options: IMfDevelopmentBffPluginOptions): Plugin => {\n const middleware = createMfDevelopmentBffMiddleware(options);\n return {\n apply: 'serve',\n name: 'edynamix-mf-development-bff',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n middleware(request, response, next);\n });\n },\n // Locally the module runs inside the host shell, so its page shows the host's icon. The module's own favicon is\n // only copied into the build output, so this server has none to answer the browser's favicon request with.\n transformIndexHtml(html) {\n return {\n html: html.replace(ICON_LINK_PATTERN, ''),\n tags: [{ tag: 'link', attrs: { rel: 'icon', type: 'image/svg+xml', href: HOST_ICON_PATH }, injectTo: 'head' }],\n };\n },\n };\n};\n\nexport type TMfDevelopmentBffMiddleware = (request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void) => void;\n\nexport const createMfDevelopmentBffMiddleware = (options: IMfDevelopmentBffPluginOptions): TMfDevelopmentBffMiddleware => {\n const bff = new MfDevelopmentBff(options);\n return (request, response, next) => {\n void bff.handle(request, response, next);\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"vite-development.js","sourceRoot":"","sources":["../src/vite-development.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport type { Plugin } from 'vite';\n\nconst SESSION_COOKIE = '__Host-edyn_dev_session';\nconst REFRESH_COOKIE = '__Host-edyn_dev_refresh';\nconst FLOW_COOKIE = '__Host-edyn_oidc_flow';\nconst LOGOUT_COOKIE = '__Host-edyn_oidc_logout';\nconst CALLBACK_PATH = '/callback';\nconst LOGGED_OUT_PATH = '/logged-out';\nconst LOGIN_PATH = '/api/dev-auth/login';\nconst LEGACY_LOGIN_PATH = '/login';\nexport const MF_DEVELOPMENT_HOST_NAVIGATION_PATH = '/api/dev-host-navigation/index.js';\nconst HOST_ICON_PATH = '/exsto-icon.svg';\nconst HOST_NAVIGATION_ASSET_PATHS = new Set([HOST_ICON_PATH, '/exsto-text.svg']);\nconst ICON_LINK_PATTERN = /<link\\b[^>]*\\brel=[\"'](?:shortcut\\s+)?icon[\"'][^>]*>\\s*/gi;\nconst MAX_HOST_NAVIGATION_BYTES = 5 * 1024 * 1024;\nconst MAX_HOST_NAVIGATION_ASSET_BYTES = 512 * 1024;\nconst SESSION_MAX_AGE_SECONDS = 8 * 60 * 60;\nconst REFRESH_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;\nconst FLOW_MAX_AGE_SECONDS = 10 * 60;\nconst TOKEN_REFRESH_SKEW_MS = 30_000;\nconst SPENT_REFRESH_TOKEN_REUSE_MS = 30_000;\nconst NAVIGATION_GROUP_HEADER = 'X-Edyn-Navigation-Group-Id';\nconst NAVIGATION_DEALER_HEADER = 'X-Edyn-Navigation-Dealer-Id';\nconst NAVIGATION_DEALERS_HEADER = 'X-Edyn-Navigation-Dealer-Ids';\nconst NAVIGATION_HEADERS = [NAVIGATION_GROUP_HEADER, NAVIGATION_DEALER_HEADER, NAVIGATION_DEALERS_HEADER].map((name) => name.toLowerCase());\nconst MAX_NAVIGATION_EVENT_BYTES = 5 * 1024 * 1024;\nconst MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024;\nconst EVENT_SEPARATOR = /\\r?\\n\\r?\\n/;\nconst COOKIE_NAME_PATTERN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst COOKIE_VALUE_PATTERN = /^[\\x20-\\x7e]*$/;\nconst ALLOWED_HOST_API_PATHS = [\n '/api/auth/me',\n '/api/navigation',\n '/api/navigation/access-check',\n '/api/navigation/access-request',\n '/api/navigation/counter-stream',\n] as const;\n\ninterface IOidcClientConfiguration {\n clientId: string;\n issuer: string;\n scope: string;\n}\n\ninterface IOidcDiscovery {\n authorizationEndpoint: string;\n endSessionEndpoint: string;\n issuer: string;\n jwksUri: string;\n revocationEndpoint: string | null;\n tokenEndpoint: string;\n}\n\ninterface IOidcFlow {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n returnTo: string;\n state: string;\n}\n\ninterface IModuleNavigationContext {\n dealerIds: readonly number[];\n groupId: number;\n}\n\ninterface IModuleProxyContext {\n cookies: Map<string, string>;\n navigationContext: IModuleNavigationContext | null;\n}\n\ninterface ITokenSession {\n accessToken: string;\n expiresAt: number;\n idToken: string | null;\n moduleBootstrapTail: Promise<void>;\n moduleCookies: Map<string, string>;\n moduleNavigationContext: IModuleNavigationContext | null;\n navigationContextPromise?: Promise<void>;\n refreshPromise?: Promise<void>;\n refreshToken: string | null;\n}\n\ninterface ILogoutFlow {\n createdAt: number;\n idToken: string | null;\n}\n\ninterface ITokenResponse {\n accessToken: string;\n expiresIn: number;\n idToken: string | null;\n refreshToken: string | null;\n tokenType: string;\n}\n\nexport interface IMfDevelopmentBffPluginOptions {\n developmentOrigin?: string;\n hostNavigationUrl: string;\n moduleApiOrigin: string;\n moduleApiPath: string;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst requiredString = (value: Record<string, unknown>, key: string): string => {\n const candidate = value[key];\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response is missing ${key}.`);\n }\n return candidate;\n};\n\nconst optionalString = (value: Record<string, unknown>, key: string): string | null => {\n const candidate = value[key];\n if (candidate === undefined || candidate === null) {\n return null;\n }\n if (typeof candidate !== 'string' || candidate.trim() === '') {\n throw new Error(`OIDC response contains an invalid ${key}.`);\n }\n return candidate;\n};\n\nconst absoluteHttpsUrl = (candidate: string, label: string, allowLocalHttp = false): URL => {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new Error(`${label} must be an absolute URL.`);\n }\n const isLocalHttp = allowLocalHttp && url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1');\n if (url.protocol !== 'https:' && !isLocalHttp) {\n throw new Error(`${label} must use HTTPS.`);\n }\n if (url.username || url.password || url.hash) {\n throw new Error(`${label} must not contain credentials or a fragment.`);\n }\n return url;\n};\n\nconst moduleApiPrefix = (candidate: string): string => {\n if (!/^\\/[a-z][a-z0-9-]*\\/api\\/$/.test(candidate)) {\n throw new Error('moduleApiPath must use the canonical /module/api/ boundary.');\n }\n return candidate;\n};\n\nconst base64Url = (size = 32): string => randomBytes(size).toString('base64url');\nconst codeChallengeOf = (verifier: string): string => createHash('sha256').update(verifier).digest('base64url');\n\nconst cookiesOf = (request: IncomingMessage): ReadonlyMap<string, string> => {\n const cookies = new Map<string, string>();\n for (const part of (request.headers.cookie ?? '').split(';')) {\n const separator = part.indexOf('=');\n if (separator <= 0) {\n continue;\n }\n const name = part.slice(0, separator).trim();\n const value = part.slice(separator + 1).trim();\n if (name && value) {\n cookies.set(name, value);\n }\n }\n return cookies;\n};\n\nconst cookie = (name: string, value: string, maxAge: number): string => `${name}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${maxAge}`;\n\nconst clearCookie = (name: string): string => cookie(name, '', 0);\n\n// The refresh token stays in an HttpOnly cookie for this origin, which every module's development server shares, so\n// one sign-in survives restarts and module switches until the token expires; page scripts can never read it.\nconst refreshCookieOf = (session: ITokenSession): string =>\n session.refreshToken\n ? cookie(REFRESH_COOKIE, encodeURIComponent(session.refreshToken), REFRESH_COOKIE_MAX_AGE_SECONDS)\n : clearCookie(REFRESH_COOKIE);\n\nconst decodedCookieValue = (value: string | undefined): string | null => {\n if (!value) {\n return null;\n }\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n};\n\nconst writeJson = (response: ServerResponse, status: number, body: unknown): void => {\n response.statusCode = status;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'application/json; charset=utf-8');\n response.end(JSON.stringify(body));\n};\n\nconst redirect = (response: ServerResponse, location: string, cookies: string[] = []): void => {\n response.statusCode = 302;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Location', location);\n if (cookies.length > 0) {\n response.setHeader('Set-Cookie', cookies);\n }\n response.end();\n};\n\nconst safeReturnTo = (candidate: string | null): string => {\n if (!candidate?.startsWith('/') || candidate.startsWith('//')) {\n return '/';\n }\n try {\n const parsed = new URL(candidate, 'https://localhost');\n return `${parsed.pathname}${parsed.search}${parsed.hash}`;\n } catch {\n return '/';\n }\n};\n\nconst parseTokenResponse = (value: unknown, requireIdToken: boolean): ITokenResponse => {\n if (!isRecord(value)) {\n throw new Error('OIDC token endpoint returned an invalid response.');\n }\n const expiresIn = value.expires_in;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw new Error('OIDC token endpoint returned an invalid expires_in value.');\n }\n const idToken = optionalString(value, 'id_token');\n if (requireIdToken && !idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const tokenType = requiredString(value, 'token_type');\n if (tokenType.toLowerCase() !== 'bearer') {\n throw new Error('OIDC token endpoint returned an unsupported token type.');\n }\n return {\n accessToken: requiredString(value, 'access_token'),\n expiresIn,\n idToken,\n refreshToken: optionalString(value, 'refresh_token'),\n tokenType,\n };\n};\n\nconst pathIsHostApi = (path: string): boolean => {\n return ALLOWED_HOST_API_PATHS.some((candidate) => path === candidate || path.startsWith(`${candidate}/`));\n};\n\n// The host validates a selection the browser sends it, so host requests keep the navigation headers. A module trusts\n// them, so its requests carry only the selection the host last confirmed.\nconst proxyHeaders = (request: IncomingMessage, accessToken: string, module?: IModuleProxyContext): Headers => {\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n const lowerName = name.toLowerCase();\n if (\n !value ||\n name.startsWith(':') ||\n ['authorization', 'connection', 'content-length', 'cookie', 'host', 'origin', 'transfer-encoding'].includes(lowerName) ||\n (module && NAVIGATION_HEADERS.includes(lowerName))\n ) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, value);\n }\n }\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (module?.cookies.size) {\n headers.set('Cookie', [...module.cookies.values()].join('; '));\n }\n const navigationContext = module?.navigationContext;\n if (navigationContext) {\n headers.set(NAVIGATION_GROUP_HEADER, String(navigationContext.groupId));\n headers.set(NAVIGATION_DEALERS_HEADER, navigationContext.dealerIds.join(','));\n if (navigationContext.dealerIds.length === 1) {\n headers.set(NAVIGATION_DEALER_HEADER, String(navigationContext.dealerIds[0]));\n }\n }\n return headers;\n};\n\nconst navigationContextIn = (value: unknown): IModuleNavigationContext | null => {\n if (!isRecord(value) || !isRecord(value.context)) {\n return null;\n }\n const { selectedDealerId, selectedDealerIds, selectedGroupId } = value.context;\n const dealerIds: unknown[] = Array.isArray(selectedDealerIds) ? selectedDealerIds : [selectedDealerId];\n if (\n !Number.isSafeInteger(selectedGroupId) ||\n (selectedGroupId as number) <= 0 ||\n dealerIds.length === 0 ||\n !dealerIds.every((dealerId) => Number.isSafeInteger(dealerId) && (dealerId as number) >= 0)\n ) {\n return null;\n }\n return { dealerIds: dealerIds as number[], groupId: selectedGroupId as number };\n};\n\n// Reads the selection each `navigation` event of the host's navigation stream confirms, chunk by chunk as it passes.\nconst navigationEventReader = (onContext: (context: IModuleNavigationContext) => void): ((chunk: Buffer) => void) => {\n const decoder = new TextDecoder();\n let pending = '';\n return (chunk) => {\n pending += decoder.decode(chunk, { stream: true });\n for (let separator = EVENT_SEPARATOR.exec(pending); separator; separator = EVENT_SEPARATOR.exec(pending)) {\n const block = pending.slice(0, separator.index);\n pending = pending.slice(separator.index + separator[0].length);\n const lines = block.split(/\\r?\\n/);\n if (!lines.includes('event: navigation') && !lines.includes('event:navigation')) {\n continue;\n }\n const data = lines\n .filter((line) => line.startsWith('data:'))\n .map((line) => line.slice(5).replace(/^ /, ''))\n .join('\\n');\n let payload: unknown = null;\n try {\n payload = JSON.parse(data);\n } catch {\n continue;\n }\n const context = navigationContextIn(payload);\n if (context) {\n onContext(context);\n }\n }\n if (pending.length > MAX_NAVIGATION_EVENT_BYTES) {\n pending = '';\n }\n };\n};\n\nconst updateModuleCookies = (cookies: Map<string, string>, headers: Headers): void => {\n const values = headers.getSetCookie();\n for (const value of values) {\n if (value.length > 4096) {\n continue;\n }\n const [pair, ...attributes] = value.split(';');\n const separator = pair?.indexOf('=') ?? -1;\n if (!pair || separator <= 0) {\n continue;\n }\n const name = pair.slice(0, separator).trim();\n const cookieValue = pair.slice(separator + 1).trim();\n if (!COOKIE_NAME_PATTERN.test(name) || !COOKIE_VALUE_PATTERN.test(cookieValue) || cookieValue.includes(';')) {\n continue;\n }\n const deleted = cookieValue === '' || attributes.some((attribute) => /^\\s*max-age\\s*=\\s*0\\s*$/i.test(attribute));\n if (deleted) {\n cookies.delete(name);\n continue;\n }\n if (!cookies.has(name) && cookies.size >= 32) {\n continue;\n }\n cookies.set(name, `${name}=${cookieValue}`);\n }\n};\n\nconst requestBodyOf = async (request: IncomingMessage): Promise<Buffer | null> => {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string);\n size += buffer.length;\n if (size > MAX_REQUEST_BODY_BYTES) {\n return null;\n }\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n};\n\nconst copyProxyResponse = async (upstream: Response, response: ServerResponse, onChunk?: (chunk: Buffer) => void): Promise<void> => {\n response.statusCode = upstream.status;\n for (const [name, value] of upstream.headers) {\n if (!['connection', 'content-encoding', 'content-length', 'set-cookie', 'transfer-encoding'].includes(name.toLowerCase())) {\n response.setHeader(name, value);\n }\n }\n response.setHeader('Cache-Control', upstream.headers.get('cache-control') ?? 'no-store');\n if (!upstream.body) {\n response.end();\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const readable = Readable.fromWeb(upstream.body as never);\n if (onChunk) {\n readable.on('data', onChunk);\n }\n readable.once('error', reject);\n response.once('error', reject);\n response.once('finish', resolve);\n readable.pipe(response);\n });\n};\n\nconst readResponseBody = async (response: Response, maximumBytes: number, label: string): Promise<Buffer> => {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {\n await response.body?.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n if (!response.body) {\n return Buffer.alloc(0);\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n totalBytes += value.byteLength;\n if (totalBytes > maximumBytes) {\n await reader.cancel();\n throw new Error(`${label} exceeded the configured size limit.`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks, totalBytes);\n};\n\nclass MfDevelopmentBff {\n readonly #configurationUrl: URL;\n readonly #developmentOrigin: URL;\n readonly #flows = new Map<string, IOidcFlow>();\n readonly #hostOrigin: URL;\n readonly #hostNavigationUrl: URL;\n readonly #jwks = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n readonly #logoutFlows = new Map<string, ILogoutFlow>();\n readonly #moduleApiOrigin: URL;\n readonly #moduleApiPath: string;\n readonly #sessions = new Map<string, ITokenSession>();\n readonly #restores = new Map<string, Promise<{ session: ITokenSession; sessionId: string } | null>>();\n #configurationPromise: Promise<IOidcClientConfiguration> | null = null;\n #discoveryPromise: Promise<IOidcDiscovery> | null = null;\n\n constructor(options: IMfDevelopmentBffPluginOptions) {\n const hostNavigation = absoluteHttpsUrl(options.hostNavigationUrl, 'hostNavigationUrl', true);\n this.#hostNavigationUrl = hostNavigation;\n this.#hostOrigin = new URL(hostNavigation.origin);\n this.#configurationUrl = new URL('/api/auth/oidc-configuration', this.#hostOrigin);\n this.#developmentOrigin = absoluteHttpsUrl(options.developmentOrigin ?? 'https://localhost:4200', 'developmentOrigin');\n if (this.#developmentOrigin.pathname !== '/' || this.#developmentOrigin.search || this.#developmentOrigin.hash) {\n throw new Error('developmentOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiOrigin = absoluteHttpsUrl(options.moduleApiOrigin, 'moduleApiOrigin', true);\n if (this.#moduleApiOrigin.pathname !== '/' || this.#moduleApiOrigin.search || this.#moduleApiOrigin.hash) {\n throw new Error('moduleApiOrigin must be an origin without a path, query, or fragment.');\n }\n this.#moduleApiPath = moduleApiPrefix(options.moduleApiPath);\n }\n\n async handle(request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void): Promise<void> {\n if (!request.url || !request.method) {\n next();\n return;\n }\n const url = new URL(request.url, this.#developmentOrigin);\n this.#prune();\n\n try {\n if (request.method === 'GET' && url.pathname === MF_DEVELOPMENT_HOST_NAVIGATION_PATH) {\n await this.#hostNavigationResource(response, this.#hostNavigationUrl, 'script');\n return;\n }\n if (request.method === 'GET' && HOST_NAVIGATION_ASSET_PATHS.has(url.pathname)) {\n await this.#hostNavigationResource(response, new URL(url.pathname, this.#hostOrigin), 'asset');\n return;\n }\n if (request.method === 'GET' && url.pathname.toLowerCase() === LEGACY_LOGIN_PATH) {\n await this.#legacyLogin(request, response);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGIN_PATH) {\n await this.#login(request, response, safeReturnTo(url.searchParams.get('returnTo')));\n return;\n }\n if (request.method === 'GET' && url.pathname === CALLBACK_PATH) {\n await this.#callback(request, response, url);\n return;\n }\n if (request.method === 'GET' && url.pathname === LOGGED_OUT_PATH) {\n const returnTo = safeReturnTo(url.searchParams.get('state'));\n redirect(response, `${LOGIN_PATH}?returnTo=${encodeURIComponent(returnTo)}`, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n if (request.method === 'GET' && url.pathname === '/frontchannel-logout') {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.statusCode = 204;\n response.setHeader('Set-Cookie', [clearCookie(SESSION_COOKIE), clearCookie(REFRESH_COOKIE)]);\n response.end();\n return;\n }\n if (request.method === 'POST' && url.pathname === '/api/auth/logout') {\n await this.#logout(request, response);\n return;\n }\n if (pathIsHostApi(url.pathname) || url.pathname.startsWith(this.#moduleApiPath)) {\n await this.#proxy(request, response, url);\n return;\n }\n next();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Development authentication failed.';\n writeJson(response, 502, { code: 'development_authentication_failed', error: message });\n }\n }\n\n async #hostNavigationResource(response: ServerResponse, upstreamUrl: URL, kind: 'asset' | 'script'): Promise<void> {\n const upstream = await fetch(upstreamUrl, {\n headers: { Accept: kind === 'script' ? 'text/javascript, application/javascript' : 'image/svg+xml' },\n redirect: 'error',\n });\n if (!upstream.ok) {\n throw new Error(`Host navigation ${kind} failed (${upstream.status}).`);\n }\n const contentType = upstream.headers.get('content-type') ?? '';\n const validContentType =\n kind === 'script' ? /^(?:text|application)\\/javascript(?:;|$)/i.test(contentType) : /^image\\/svg\\+xml(?:;|$)/i.test(contentType);\n if (!validContentType) {\n throw new Error(`Host navigation ${kind} returned an invalid content type.`);\n }\n const maximumBytes = kind === 'script' ? MAX_HOST_NAVIGATION_BYTES : MAX_HOST_NAVIGATION_ASSET_BYTES;\n const body = await readResponseBody(upstream, maximumBytes, `Host navigation ${kind}`);\n response.statusCode = 200;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', contentType);\n response.setHeader('X-Content-Type-Options', 'nosniff');\n response.end(body);\n }\n\n async #configuration(): Promise<IOidcClientConfiguration> {\n this.#configurationPromise ??= fetch(this.#configurationUrl, { headers: { Accept: 'application/json' }, redirect: 'error' })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Host OIDC configuration failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('Host OIDC configuration returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC issuer', true).href.replace(/\\/$/, '');\n return { clientId: requiredString(value, 'clientId'), issuer, scope: requiredString(value, 'scope') };\n })\n .catch((error) => {\n this.#configurationPromise = null;\n throw error;\n });\n return this.#configurationPromise;\n }\n\n async #discovery(): Promise<IOidcDiscovery> {\n this.#discoveryPromise ??= this.#configuration()\n .then(async (configuration) => {\n const response = await fetch(`${configuration.issuer}/.well-known/openid-configuration`, {\n headers: { Accept: 'application/json' },\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC discovery failed (${response.status}).`);\n }\n const value: unknown = await response.json();\n if (!isRecord(value)) {\n throw new Error('OIDC discovery returned an invalid response.');\n }\n const issuer = absoluteHttpsUrl(requiredString(value, 'issuer'), 'OIDC discovery issuer', true).href.replace(/\\/$/, '');\n if (issuer !== configuration.issuer) {\n throw new Error('OIDC discovery issuer does not match the host configuration.');\n }\n const endpoint = (key: string): string => {\n const url = absoluteHttpsUrl(requiredString(value, key), `OIDC ${key}`, true);\n if (url.origin !== new URL(issuer).origin) {\n throw new Error(`OIDC ${key} must use the configured issuer origin.`);\n }\n return url.href;\n };\n const revocation = optionalString(value, 'revocation_endpoint');\n return {\n authorizationEndpoint: endpoint('authorization_endpoint'),\n endSessionEndpoint: endpoint('end_session_endpoint'),\n issuer,\n jwksUri: endpoint('jwks_uri'),\n revocationEndpoint: revocation ? endpoint('revocation_endpoint') : null,\n tokenEndpoint: endpoint('token_endpoint'),\n };\n })\n .catch((error) => {\n this.#discoveryPromise = null;\n throw error;\n });\n return this.#discoveryPromise;\n }\n\n async #login(request: IncomingMessage, response: ServerResponse, returnTo: string): Promise<void> {\n const logoutId = cookiesOf(request).get(LOGOUT_COOKIE);\n if (logoutId) {\n const logout = this.#logoutFlows.get(logoutId);\n this.#logoutFlows.delete(logoutId);\n if (logout) {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const endpoint = new URL(discovery.endSessionEndpoint);\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('post_logout_redirect_uri', new URL(LOGGED_OUT_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('state', returnTo);\n if (logout.idToken) {\n endpoint.searchParams.set('id_token_hint', logout.idToken);\n }\n redirect(response, endpoint.href, [clearCookie(LOGOUT_COOKIE)]);\n return;\n }\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const flowId = base64Url();\n const codeVerifier = base64Url(48);\n const flow: IOidcFlow = { codeVerifier, createdAt: Date.now(), nonce: base64Url(), returnTo, state: base64Url() };\n this.#flows.set(flowId, flow);\n\n const endpoint = new URL(discovery.authorizationEndpoint);\n endpoint.searchParams.set('response_type', 'code');\n endpoint.searchParams.set('client_id', configuration.clientId);\n endpoint.searchParams.set('redirect_uri', new URL(CALLBACK_PATH, this.#developmentOrigin).href);\n endpoint.searchParams.set('scope', configuration.scope);\n endpoint.searchParams.set('state', flow.state);\n endpoint.searchParams.set('nonce', flow.nonce);\n endpoint.searchParams.set('code_challenge', codeChallengeOf(codeVerifier));\n endpoint.searchParams.set('code_challenge_method', 'S256');\n redirect(response, endpoint.href, [cookie(FLOW_COOKIE, flowId, FLOW_MAX_AGE_SECONDS)]);\n }\n\n async #callback(request: IncomingMessage, response: ServerResponse, url: URL): Promise<void> {\n const flowId = cookiesOf(request).get(FLOW_COOKIE);\n const flow = flowId ? this.#flows.get(flowId) : undefined;\n if (flowId) {\n this.#flows.delete(flowId);\n }\n if (!flow || url.searchParams.get('state') !== flow.state) {\n writeJson(response, 400, { code: 'invalid_oidc_state', error: 'The OIDC callback state is invalid or expired.' });\n return;\n }\n const providerError = url.searchParams.get('error');\n if (providerError) {\n writeJson(response, 401, { code: providerError, error: url.searchParams.get('error_description') ?? 'Sign-in was rejected.' });\n return;\n }\n const code = url.searchParams.get('code');\n if (!code) {\n writeJson(response, 400, { code: 'missing_authorization_code', error: 'The OIDC callback did not include an authorization code.' });\n return;\n }\n\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const fields = new URLSearchParams({\n client_id: configuration.clientId,\n code,\n code_verifier: flow.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: new URL(CALLBACK_PATH, this.#developmentOrigin).href,\n });\n const tokenResponse = await fetch(discovery.tokenEndpoint, {\n body: fields,\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!tokenResponse.ok) {\n throw new Error(`OIDC token exchange failed (${tokenResponse.status}).`);\n }\n const tokens = parseTokenResponse(await tokenResponse.json(), true);\n const idToken = tokens.idToken;\n if (!idToken) {\n throw new Error('OIDC token endpoint did not return an ID token.');\n }\n const jwks = this.#jwks.get(discovery.jwksUri) ?? createRemoteJWKSet(new URL(discovery.jwksUri));\n this.#jwks.set(discovery.jwksUri, jwks);\n const verified = await jwtVerify(idToken, jwks, {\n audience: configuration.clientId,\n issuer: discovery.issuer,\n requiredClaims: ['sub', 'nonce'],\n });\n if (verified.payload.nonce !== flow.nonce) {\n throw new Error('OIDC ID token nonce does not match the authorization request.');\n }\n\n const sessionId = base64Url();\n const session: ITokenSession = {\n accessToken: tokens.accessToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n idToken,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken: tokens.refreshToken,\n };\n this.#sessions.set(sessionId, session);\n redirect(response, flow.returnTo, [\n cookie(SESSION_COOKIE, sessionId, SESSION_MAX_AGE_SECONDS),\n refreshCookieOf(session),\n clearCookie(FLOW_COOKIE),\n ]);\n }\n\n async #logout(request: IncomingMessage, response: ServerResponse): Promise<void> {\n const sessionId = cookiesOf(request).get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n if (session) {\n void this.#revoke(session).catch(() => undefined);\n }\n const logoutId = base64Url();\n this.#logoutFlows.set(logoutId, { createdAt: Date.now(), idToken: session?.idToken ?? null });\n response.setHeader('Set-Cookie', [\n clearCookie(SESSION_COOKIE),\n clearCookie(REFRESH_COOKIE),\n cookie(LOGOUT_COOKIE, logoutId, FLOW_MAX_AGE_SECONDS),\n ]);\n writeJson(response, 200, { loggedOut: true });\n }\n\n async #revoke(session: ITokenSession): Promise<void> {\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const revocationEndpoint = discovery.revocationEndpoint;\n if (!revocationEndpoint) {\n return;\n }\n const revoke = async (token: string, hint: string): Promise<void> => {\n await fetch(revocationEndpoint, {\n body: new URLSearchParams({ client_id: configuration.clientId, token, token_type_hint: hint }),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n };\n await Promise.all([\n revoke(session.accessToken, 'access_token'),\n session.refreshToken ? revoke(session.refreshToken, 'refresh_token') : Promise.resolve(),\n ]);\n }\n\n // Standalone modules send the browser to their legacy /login page when an API call answers 401, and locally no such\n // page exists: serving the module again would repeat the 401 in a reload loop. Signed out, this is the development\n // sign-in; signed in, the module API itself refused the session, so the page says so and redirects nowhere.\n async #legacyLogin(request: IncomingMessage, response: ServerResponse): Promise<void> {\n if (!(await this.#authenticatedSession(request, response))) {\n redirect(response, `${LOGIN_PATH}?returnTo=%2F`);\n return;\n }\n response.statusCode = 401;\n response.setHeader('Cache-Control', 'no-store');\n response.setHeader('Content-Type', 'text/html; charset=utf-8');\n response.end(\n '<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Module sign-in refused</title></head>' +\n '<body style=\"font-family:system-ui,sans-serif;margin:3rem;line-height:1.5\">' +\n '<h1 style=\"font-size:1.25rem\">The module API refused your session</h1>' +\n '<p>You are signed in, but a module API request answered 401 and the module asked for its legacy sign-in page. ' +\n 'Check the module backend (MODULE_API_ORIGIN) and its dev server output.</p>' +\n `<p><a href=\"${LOGIN_PATH}?returnTo=%2F\">Sign in again</a> · <a href=\"/\">Back to the module</a></p>` +\n '</body></html>',\n );\n }\n\n async #authenticatedSession(request: IncomingMessage, response: ServerResponse): Promise<ITokenSession | null> {\n const cookies = cookiesOf(request);\n const sessionId = cookies.get(SESSION_COOKIE);\n const session = sessionId ? this.#sessions.get(sessionId) : undefined;\n if (!session) {\n return this.#restoredSession(decodedCookieValue(cookies.get(REFRESH_COOKIE)), response);\n }\n if (session.expiresAt > Date.now() + TOKEN_REFRESH_SKEW_MS) {\n return session;\n }\n if (!session.refreshToken) {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n return null;\n }\n session.refreshPromise ??= this.#refresh(session).finally(() => {\n session.refreshPromise = undefined;\n });\n try {\n await session.refreshPromise;\n response.setHeader('Set-Cookie', refreshCookieOf(session));\n return session;\n } catch {\n if (sessionId) {\n this.#sessions.delete(sessionId);\n }\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n }\n\n // Starts a session from the browser's refresh cookie after this server restarted or another module's server signed\n // the developer in. Parallel requests carrying the same token share one refresh, and so, for a short while, do\n // requests the browser sent before the rotated cookie reached it: the sign-in service accepts a refresh token once,\n // so spending it again would fail and clear the cookie that replaced it.\n async #restoredSession(refreshToken: string | null, response: ServerResponse): Promise<ITokenSession | null> {\n if (!refreshToken) {\n return null;\n }\n let restore = this.#restores.get(refreshToken);\n if (!restore) {\n restore = this.#restore(refreshToken);\n this.#restores.set(refreshToken, restore);\n void restore.then((restoredSession) => {\n if (!restoredSession) {\n this.#restores.delete(refreshToken);\n return;\n }\n setTimeout(() => this.#restores.delete(refreshToken), SPENT_REFRESH_TOKEN_REUSE_MS).unref();\n });\n }\n const restored = await restore;\n if (!restored || this.#sessions.get(restored.sessionId) !== restored.session) {\n response.setHeader('Set-Cookie', clearCookie(REFRESH_COOKIE));\n return null;\n }\n response.setHeader('Set-Cookie', [cookie(SESSION_COOKIE, restored.sessionId, SESSION_MAX_AGE_SECONDS), refreshCookieOf(restored.session)]);\n return restored.session;\n }\n\n async #restore(refreshToken: string): Promise<{ session: ITokenSession; sessionId: string } | null> {\n const session: ITokenSession = {\n accessToken: '',\n expiresAt: 0,\n idToken: null,\n moduleBootstrapTail: Promise.resolve(),\n moduleCookies: new Map(),\n moduleNavigationContext: null,\n refreshToken,\n };\n try {\n await this.#refresh(session);\n } catch {\n return null;\n }\n const sessionId = base64Url();\n this.#sessions.set(sessionId, session);\n return { session, sessionId };\n }\n\n async #refresh(session: ITokenSession): Promise<void> {\n const refreshToken = session.refreshToken;\n if (!refreshToken) {\n throw new Error('OIDC refresh token is unavailable.');\n }\n const [configuration, discovery] = await Promise.all([this.#configuration(), this.#discovery()]);\n const response = await fetch(discovery.tokenEndpoint, {\n body: new URLSearchParams({\n client_id: configuration.clientId,\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n }),\n headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },\n method: 'POST',\n redirect: 'error',\n });\n if (!response.ok) {\n throw new Error(`OIDC refresh failed (${response.status}).`);\n }\n const tokens = parseTokenResponse(await response.json(), false);\n session.accessToken = tokens.accessToken;\n session.expiresAt = Date.now() + tokens.expiresIn * 1000;\n session.refreshToken = tokens.refreshToken ?? session.refreshToken;\n session.idToken = tokens.idToken ?? session.idToken;\n }\n\n async #proxy(request: IncomingMessage, response: ServerResponse, localUrl: URL): Promise<void> {\n const session = await this.#authenticatedSession(request, response);\n if (!session) {\n // This server refreshes the session's tokens itself, so the browser can only sign in again. A refreshable code\n // would send the host client to a refresh endpoint that only the host has.\n writeJson(response, 401, { code: 'sign_in_required', error: 'Sign in is required.' });\n return;\n }\n const isModuleApi = localUrl.pathname.startsWith(this.#moduleApiPath);\n if (isModuleApi && !session.moduleNavigationContext) {\n // The host mounts a module only once the selection is known, so its first API calls already carry it. Locally\n // the module starts beside the navigation, so its first calls can arrive first: ask the host before forwarding.\n session.navigationContextPromise ??= this.#loadNavigationContext(session).finally(() => {\n session.navigationContextPromise = undefined;\n });\n await session.navigationContextPromise;\n }\n if (isModuleApi && session.moduleCookies.size === 0) {\n const previous = session.moduleBootstrapTail;\n let release = (): void => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n session.moduleBootstrapTail = previous.catch(() => undefined).then(() => current);\n await previous.catch(() => undefined);\n try {\n await this.#proxyAuthenticated(request, response, localUrl, session, true);\n } finally {\n release();\n }\n return;\n }\n await this.#proxyAuthenticated(request, response, localUrl, session, isModuleApi);\n }\n\n async #loadNavigationContext(session: ITokenSession): Promise<void> {\n const upstream = await fetch(new URL('/api/navigation', this.#hostOrigin), {\n headers: { Accept: 'application/json', Authorization: `Bearer ${session.accessToken}` },\n redirect: 'manual',\n }).catch(() => null);\n if (!upstream?.ok || session.moduleNavigationContext) {\n return;\n }\n session.moduleNavigationContext = navigationContextIn(await upstream.json().catch(() => null));\n }\n\n async #proxyAuthenticated(\n request: IncomingMessage,\n response: ServerResponse,\n localUrl: URL,\n session: ITokenSession,\n isModuleApi: boolean,\n ): Promise<void> {\n const upstreamOrigin = isModuleApi ? this.#moduleApiOrigin : this.#hostOrigin;\n const upstreamUrl = new URL(`${localUrl.pathname}${localUrl.search}`, upstreamOrigin);\n const method = request.method ?? 'GET';\n const init: RequestInit = {\n headers: proxyHeaders(\n request,\n session.accessToken,\n isModuleApi ? { cookies: session.moduleCookies, navigationContext: session.moduleNavigationContext } : undefined,\n ),\n method,\n redirect: 'manual',\n };\n if (method !== 'GET' && method !== 'HEAD') {\n // A fixed-length body: the web front ends of the deployed module servers answer chunked request bodies with 404.\n const body = await requestBodyOf(request);\n if (!body) {\n writeJson(response, 413, { code: 'request_body_too_large', error: 'The request body exceeds the development proxy limit.' });\n return;\n }\n init.body = body;\n }\n const upstream = await fetch(upstreamUrl, init);\n let onChunk: ((chunk: Buffer) => void) | undefined;\n if (isModuleApi) {\n updateModuleCookies(session.moduleCookies, upstream.headers);\n } else if (localUrl.pathname === '/api/navigation' && upstream.ok) {\n session.moduleNavigationContext = navigationContextIn(\n await upstream\n .clone()\n .json()\n .catch(() => null),\n );\n } else if (localUrl.pathname === '/api/navigation/stream' && upstream.ok) {\n onChunk = navigationEventReader((context) => {\n session.moduleNavigationContext = context;\n });\n }\n await copyProxyResponse(upstream, response, onChunk);\n }\n\n #prune(): void {\n const now = Date.now();\n for (const [id, flow] of this.#flows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#flows.delete(id);\n }\n }\n for (const [id, flow] of this.#logoutFlows) {\n if (flow.createdAt + FLOW_MAX_AGE_SECONDS * 1000 < now) {\n this.#logoutFlows.delete(id);\n }\n }\n for (const [id, session] of this.#sessions) {\n if (!session.refreshToken && session.expiresAt + TOKEN_REFRESH_SKEW_MS < now) {\n this.#sessions.delete(id);\n }\n }\n }\n}\n\nexport const mfDevelopmentBffPlugin = (options: IMfDevelopmentBffPluginOptions): Plugin => {\n const middleware = createMfDevelopmentBffMiddleware(options);\n return {\n apply: 'serve',\n name: 'edynamix-mf-development-bff',\n configureServer(server) {\n server.middlewares.use((request, response, next) => {\n middleware(request, response, next);\n });\n },\n // Locally the module runs inside the host shell, so its page shows the host's icon. The module's own favicon is\n // only copied into the build output, so this server has none to answer the browser's favicon request with.\n transformIndexHtml(html) {\n return {\n html: html.replace(ICON_LINK_PATTERN, ''),\n tags: [{ tag: 'link', attrs: { rel: 'icon', type: 'image/svg+xml', href: HOST_ICON_PATH }, injectTo: 'head' }],\n };\n },\n };\n};\n\nexport type TMfDevelopmentBffMiddleware = (request: IncomingMessage, response: ServerResponse, next: (error?: unknown) => void) => void;\n\nexport const createMfDevelopmentBffMiddleware = (options: IMfDevelopmentBffPluginOptions): TMfDevelopmentBffMiddleware => {\n const bff = new MfDevelopmentBff(options);\n return (request, response, next) => {\n void bff.handle(request, response, next);\n };\n};\n"]}
|