@angular/platform-server
Version:
Angular - library for using Angular in Node.js
223 lines (222 loc) • 31 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { APP_ID, ApplicationRef, CSP_NONCE, InjectionToken, Renderer2, ɵannotateForHydration as annotateForHydration, ɵIS_HYDRATION_DOM_REUSE_ENABLED as IS_HYDRATION_DOM_REUSE_ENABLED, ɵSSR_CONTENT_INTEGRITY_MARKER as SSR_CONTENT_INTEGRITY_MARKER, ɵwhenStable as whenStable, } from '@angular/core';
import { PlatformState } from './platform_state';
import { platformServer } from './server';
import { BEFORE_APP_SERIALIZED, INITIAL_CONFIG } from './tokens';
import { createScript } from './transfer_state';
import { runAndMeasurePerf } from './profiler';
/**
* Event dispatch (JSAction) script is inlined into the HTML by the build
* process to avoid extra blocking request on a page. The script looks like this:
* ```
* <script type="text/javascript" id="ng-event-dispatch-contract">...</script>
* ```
* This const represents the "id" attribute value.
*/
export const EVENT_DISPATCH_SCRIPT_ID = 'ng-event-dispatch-contract';
/**
* Creates an instance of a server platform (with or without JIT compiler support
* depending on the `ngJitMode` global const value), using provided options.
*/
function createServerPlatform(options) {
const extraProviders = options.platformProviders ?? [];
return platformServer([
{ provide: INITIAL_CONFIG, useValue: { document: options.document, url: options.url } },
extraProviders,
]);
}
/**
* Finds and returns inlined event dispatch script if it exists.
* See the `EVENT_DISPATCH_SCRIPT_ID` const docs for additional info.
*/
function findEventDispatchScript(doc) {
return doc.getElementById(EVENT_DISPATCH_SCRIPT_ID);
}
/**
* Removes inlined event dispatch script if it exists.
* See the `EVENT_DISPATCH_SCRIPT_ID` const docs for additional info.
*/
function removeEventDispatchScript(doc) {
findEventDispatchScript(doc)?.remove();
}
/**
* Annotate nodes for hydration and remove event dispatch script when not needed.
*/
function prepareForHydration(platformState, applicationRef) {
const environmentInjector = applicationRef.injector;
const doc = platformState.getDocument();
if (!environmentInjector.get(IS_HYDRATION_DOM_REUSE_ENABLED, false)) {
// Hydration is diabled, remove inlined event dispatch script.
// (which was injected by the build process) from the HTML.
removeEventDispatchScript(doc);
return;
}
appendSsrContentIntegrityMarker(doc);
const eventTypesToReplay = annotateForHydration(applicationRef, doc);
if (eventTypesToReplay.regular.size || eventTypesToReplay.capture.size) {
insertEventRecordScript(environmentInjector.get(APP_ID), doc, eventTypesToReplay, environmentInjector.get(CSP_NONCE, null));
}
else {
// No events to replay, we should remove inlined event dispatch script
// (which was injected by the build process) from the HTML.
removeEventDispatchScript(doc);
}
}
/**
* Creates a marker comment node and append it into the `<body>`.
* Some CDNs have mechanisms to remove all comment node from HTML.
* This behaviour breaks hydration, so we'll detect on the client side if this
* marker comment is still available or else throw an error
*/
function appendSsrContentIntegrityMarker(doc) {
// Adding a ng hydration marker comment
const comment = doc.createComment(SSR_CONTENT_INTEGRITY_MARKER);
doc.body.firstChild
? doc.body.insertBefore(comment, doc.body.firstChild)
: doc.body.append(comment);
}
/**
* Adds the `ng-server-context` attribute to host elements of all bootstrapped components
* within a given application.
*/
function appendServerContextInfo(applicationRef) {
const injector = applicationRef.injector;
let serverContext = sanitizeServerContext(injector.get(SERVER_CONTEXT, DEFAULT_SERVER_CONTEXT));
applicationRef.components.forEach((componentRef) => {
const renderer = componentRef.injector.get(Renderer2);
const element = componentRef.location.nativeElement;
if (element) {
renderer.setAttribute(element, 'ng-server-context', serverContext);
}
});
}
function insertEventRecordScript(appId, doc, eventTypesToReplay, nonce) {
const { regular, capture } = eventTypesToReplay;
const eventDispatchScript = findEventDispatchScript(doc);
if (eventDispatchScript) {
// This is defined in packages/core/primitives/event-dispatch/contract_binary.ts
const replayScriptContents = `window.__jsaction_bootstrap(` +
`document.body,` +
`"${appId}",` +
`${JSON.stringify(Array.from(regular))},` +
`${JSON.stringify(Array.from(capture))}` +
`);`;
const replayScript = createScript(doc, replayScriptContents, nonce);
// Insert replay script right after inlined event dispatch script, since it
// relies on `__jsaction_bootstrap` to be defined in the global scope.
eventDispatchScript.after(replayScript);
}
}
async function _render(platformRef, applicationRef) {
// Block until application is stable.
await whenStable(applicationRef);
const platformState = platformRef.injector.get(PlatformState);
prepareForHydration(platformState, applicationRef);
// Run any BEFORE_APP_SERIALIZED callbacks just before rendering to string.
const environmentInjector = applicationRef.injector;
const callbacks = environmentInjector.get(BEFORE_APP_SERIALIZED, null);
if (callbacks) {
const asyncCallbacks = [];
for (const callback of callbacks) {
try {
const callbackResult = callback();
if (callbackResult) {
asyncCallbacks.push(callbackResult);
}
}
catch (e) {
// Ignore exceptions.
console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', e);
}
}
if (asyncCallbacks.length) {
for (const result of await Promise.allSettled(asyncCallbacks)) {
if (result.status === 'rejected') {
console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', result.reason);
}
}
}
}
appendServerContextInfo(applicationRef);
const output = platformState.renderToString();
// Destroy the application in a macrotask, this allows pending promises to be settled and errors
// to be surfaced to the users.
await new Promise((resolve) => {
setTimeout(() => {
platformRef.destroy();
resolve();
}, 0);
});
return output;
}
/**
* Specifies the value that should be used if no server context value has been provided.
*/
const DEFAULT_SERVER_CONTEXT = 'other';
/**
* An internal token that allows providing extra information about the server context
* (e.g. whether SSR or SSG was used). The value is a string and characters other
* than [a-zA-Z0-9\-] are removed. See the default value in `DEFAULT_SERVER_CONTEXT` const.
*/
export const SERVER_CONTEXT = new InjectionToken('SERVER_CONTEXT');
/**
* Sanitizes provided server context:
* - removes all characters other than a-z, A-Z, 0-9 and `-`
* - returns `other` if nothing is provided or the string is empty after sanitization
*/
function sanitizeServerContext(serverContext) {
const context = serverContext.replace(/[^a-zA-Z0-9\-]/g, '');
return context.length > 0 ? context : DEFAULT_SERVER_CONTEXT;
}
/**
* Bootstraps an application using provided NgModule and serializes the page content to string.
*
* @param moduleType A reference to an NgModule that should be used for bootstrap.
* @param options Additional configuration for the render operation:
* - `document` - the document of the page to render, either as an HTML string or
* as a reference to the `document` instance.
* - `url` - the URL for the current render request.
* - `extraProviders` - set of platform level providers for the current render request.
*
* @publicApi
*/
export async function renderModule(moduleType, options) {
const { document, url, extraProviders: platformProviders } = options;
const platformRef = createServerPlatform({ document, url, platformProviders });
const moduleRef = await platformRef.bootstrapModule(moduleType);
const applicationRef = moduleRef.injector.get(ApplicationRef);
return _render(platformRef, applicationRef);
}
/**
* Bootstraps an instance of an Angular application and renders it to a string.
* ```typescript
* const bootstrap = () => bootstrapApplication(RootComponent, appConfig);
* const output: string = await renderApplication(bootstrap);
* ```
*
* @param bootstrap A method that when invoked returns a promise that returns an `ApplicationRef`
* instance once resolved.
* @param options Additional configuration for the render operation:
* - `document` - the document of the page to render, either as an HTML string or
* as a reference to the `document` instance.
* - `url` - the URL for the current render request.
* - `platformProviders` - the platform level providers for the current render request.
*
* @returns A Promise, that returns serialized (to a string) rendered page, once resolved.
*
* @publicApi
*/
export async function renderApplication(bootstrap, options) {
return runAndMeasurePerf('renderApplication', async () => {
const platformRef = createServerPlatform(options);
const applicationRef = await bootstrap();
return _render(platformRef, applicationRef);
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9wbGF0Zm9ybS1zZXJ2ZXIvc3JjL3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFDTCxNQUFNLEVBQ04sY0FBYyxFQUNkLFNBQVMsRUFDVCxjQUFjLEVBR2QsU0FBUyxFQUdULHFCQUFxQixJQUFJLG9CQUFvQixFQUM3QywrQkFBK0IsSUFBSSw4QkFBOEIsRUFDakUsNkJBQTZCLElBQUksNEJBQTRCLEVBQzdELFdBQVcsSUFBSSxVQUFVLEdBQzFCLE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sVUFBVSxDQUFDO0FBQ3hDLE9BQU8sRUFBQyxxQkFBcUIsRUFBRSxjQUFjLEVBQUMsTUFBTSxVQUFVLENBQUM7QUFDL0QsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUU3Qzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsNEJBQTRCLENBQUM7QUFRckU7OztHQUdHO0FBQ0gsU0FBUyxvQkFBb0IsQ0FBQyxPQUF3QjtJQUNwRCxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsaUJBQWlCLElBQUksRUFBRSxDQUFDO0lBQ3ZELE9BQU8sY0FBYyxDQUFDO1FBQ3BCLEVBQUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsRUFBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsRUFBQyxFQUFDO1FBQ25GLGNBQWM7S0FDZixDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyx1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QyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge1xuICBBUFBfSUQsXG4gIEFwcGxpY2F0aW9uUmVmLFxuICBDU1BfTk9OQ0UsXG4gIEluamVjdGlvblRva2VuLFxuICBQbGF0Zm9ybVJlZixcbiAgUHJvdmlkZXIsXG4gIFJlbmRlcmVyMixcbiAgU3RhdGljUHJvdmlkZXIsXG4gIFR5cGUsXG4gIMm1YW5ub3RhdGVGb3JIeWRyYXRpb24gYXMgYW5ub3RhdGVGb3JIeWRyYXRpb24sXG4gIMm1SVNfSFlEUkFUSU9OX0RPTV9SRVVTRV9FTkFCTEVEIGFzIElTX0hZRFJBVElPTl9ET01fUkVVU0VfRU5BQkxFRCxcbiAgybVTU1JfQ09OVEVOVF9JTlRFR1JJVFlfTUFSS0VSIGFzIFNTUl9DT05URU5UX0lOVEVHUklUWV9NQVJLRVIsXG4gIMm1d2hlblN0YWJsZSBhcyB3aGVuU3RhYmxlLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHtQbGF0Zm9ybVN0YXRlfSBmcm9tICcuL3BsYXRmb3JtX3N0YXRlJztcbmltcG9ydCB7cGxhdGZvcm1TZXJ2ZXJ9IGZyb20gJy4vc2VydmVyJztcbmltcG9ydCB7QkVGT1JFX0FQUF9TRVJJQUxJWkVELCBJTklUSUFMX0NPTkZJR30gZnJvbSAnLi90b2tlbnMnO1xuaW1wb3J0IHtjcmVhdGVTY3JpcHR9IGZyb20gJy4vdHJhbnNmZXJfc3RhdGUnO1xuaW1wb3J0IHtydW5BbmRNZWFzdXJlUGVyZn0gZnJvbSAnLi9wcm9maWxlcic7XG5cbi8qKlxuICogRXZlbnQgZGlzcGF0Y2ggKEpTQWN0aW9uKSBzY3JpcHQgaXMgaW5saW5lZCBpbnRvIHRoZSBIVE1MIGJ5IHRoZSBidWlsZFxuICogcHJvY2VzcyB0byBhdm9pZCBleHRyYSBibG9ja2luZyByZXF1ZXN0IG9uIGEgcGFnZS4gVGhlIHNjcmlwdCBsb29rcyBsaWtlIHRoaXM6XG4gKiBgYGBcbiAqIDxzY3JpcHQgdHlwZT1cInRleHQvamF2YXNjcmlwdFwiIGlkPVwibmctZXZlbnQtZGlzcGF0Y2gtY29udHJhY3RcIj4uLi48L3NjcmlwdD5cbiAqIGBgYFxuICogVGhpcyBjb25zdCByZXByZXNlbnRzIHRoZSBcImlkXCIgYXR0cmlidXRlIHZhbHVlLlxuICovXG5leHBvcnQgY29uc3QgRVZFTlRfRElTUEFUQ0hfU0NSSVBUX0lEID0gJ25nLWV2ZW50LWRpc3BhdGNoLWNvbnRyYWN0JztcblxuaW50ZXJmYWNlIFBsYXRmb3JtT3B0aW9ucyB7XG4gIGRvY3VtZW50Pzogc3RyaW5nIHwgRG9jdW1lbnQ7XG4gIHVybD86IHN0cmluZztcbiAgcGxhdGZvcm1Qcm92aWRlcnM/OiBQcm92aWRlcltdO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBzZXJ2ZXIgcGxhdGZvcm0gKHdpdGggb3Igd2l0aG91dCBKSVQgY29tcGlsZXIgc3VwcG9ydFxuICogZGVwZW5kaW5nIG9uIHRoZSBgbmdKaXRNb2RlYCBnbG9iYWwgY29uc3QgdmFsdWUpLCB1c2luZyBwcm92aWRlZCBvcHRpb25zLlxuICovXG5mdW5jdGlvbiBjcmVhdGVTZXJ2ZXJQbGF0Zm9ybShvcHRpb25zOiBQbGF0Zm9ybU9wdGlvbnMpOiBQbGF0Zm9ybVJlZiB7XG4gIGNvbnN0IGV4dHJhUHJvdmlkZXJzID0gb3B0aW9ucy5wbGF0Zm9ybVByb3ZpZGVycyA/PyBbXTtcbiAgcmV0dXJuIHBsYXRmb3JtU2VydmVyKFtcbiAgICB7cHJvdmlkZTogSU5JVElBTF9DT05GSUcsIHVzZVZhbHVlOiB7ZG9jdW1lbnQ6IG9wdGlvbnMuZG9jdW1lbnQsIHVybDogb3B0aW9ucy51cmx9fSxcbiAgICBleHRyYVByb3ZpZGVycyxcbiAgXSk7XG59XG5cbi8qKlxuICogRmluZHMgYW5kIHJldHVybnMgaW5saW5lZCBldmVudCBkaXNwYXRjaCBzY3JpcHQgaWYgaXQgZXhpc3RzLlxuICogU2VlIHRoZSBgRVZFTlRfRElTUEFUQ0hfU0NSSVBUX0lEYCBjb25zdCBkb2NzIGZvciBhZGRpdGlvbmFsIGluZm8uXG4gKi9cbmZ1bmN0aW9uIGZpbmRFdmVudERpc3BhdGNoU2NyaXB0KGRvYzogRG9jdW1lbnQpIHtcbiAgcmV0dXJuIGRvYy5nZXRFbGVtZW50QnlJZChFVkVOVF9ESVNQQVRDSF9TQ1JJUFRfSUQpO1xufVxuXG4vKipcbiAqIFJlbW92ZXMgaW5saW5lZCBldmVudCBkaXNwYXRjaCBzY3JpcHQgaWYgaXQgZXhpc3RzLlxuICogU2VlIHRoZSBgRVZFTlRfRElTUEFUQ0hfU0NSSVBUX0lEYCBjb25zdCBkb2NzIGZvciBhZGRpdGlvbmFsIGluZm8uXG4gKi9cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50RGlzcGF0Y2hTY3JpcHQoZG9jOiBEb2N1bWVudCkge1xuICBmaW5kRXZlbnREaXNwYXRjaFNjcmlwdChkb2MpPy5yZW1vdmUoKTtcbn1cblxuLyoqXG4gKiBBbm5vdGF0ZSBub2RlcyBmb3IgaHlkcmF0aW9uIGFuZCByZW1vdmUgZXZlbnQgZGlzcGF0Y2ggc2NyaXB0IHdoZW4gbm90IG5lZWRlZC5cbiAqL1xuZnVuY3Rpb24gcHJlcGFyZUZvckh5ZHJhdGlvbihwbGF0Zm9ybVN0YXRlOiBQbGF0Zm9ybVN0YXRlLCBhcHBsaWNhdGlvblJlZjogQXBwbGljYXRpb25SZWYpOiB2b2lkIHtcbiAgY29uc3QgZW52aXJvbm1lbnRJbmplY3RvciA9IGFwcGxpY2F0aW9uUmVmLmluamVjdG9yO1xuICBjb25zdCBkb2MgPSBwbGF0Zm9ybVN0YXRlLmdldERvY3VtZW50KCk7XG5cbiAgaWYgKCFlbnZpcm9ubWVudEluamVjdG9yLmdldChJU19IWURSQVRJT05fRE9NX1JFVVNFX0VOQUJMRUQsIGZhbHNlKSkge1xuICAgIC8vIEh5ZHJhdGlvbiBpcyBkaWFibGVkLCByZW1vdmUgaW5saW5lZCBldmVudCBkaXNwYXRjaCBzY3JpcHQuXG4gICAgLy8gKHdoaWNoIHdhcyBpbmplY3RlZCBieSB0aGUgYnVpbGQgcHJvY2VzcykgZnJvbSB0aGUgSFRNTC5cbiAgICByZW1vdmVFdmVudERpc3BhdGNoU2NyaXB0KGRvYyk7XG5cbiAgICByZXR1cm47XG4gIH1cblxuICBhcHBlbmRTc3JDb250ZW50SW50ZWdyaXR5TWFya2VyKGRvYyk7XG5cbiAgY29uc3QgZXZlbnRUeXBlc1RvUmVwbGF5ID0gYW5ub3RhdGVGb3JIeWRyYXRpb24oYXBwbGljYXRpb25SZWYsIGRvYyk7XG4gIGlmIChldmVudFR5cGVzVG9SZXBsYXkucmVndWxhci5zaXplIHx8IGV2ZW50VHlwZXNUb1JlcGxheS5jYXB0dXJlLnNpemUpIHtcbiAgICBpbnNlcnRFdmVudFJlY29yZFNjcmlwdChcbiAgICAgIGVudmlyb25tZW50SW5qZWN0b3IuZ2V0KEFQUF9JRCksXG4gICAgICBkb2MsXG4gICAgICBldmVudFR5cGVzVG9SZXBsYXksXG4gICAgICBlbnZpcm9ubWVudEluamVjdG9yLmdldChDU1BfTk9OQ0UsIG51bGwpLFxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgLy8gTm8gZXZlbnRzIHRvIHJlcGxheSwgd2Ugc2hvdWxkIHJlbW92ZSBpbmxpbmVkIGV2ZW50IGRpc3BhdGNoIHNjcmlwdFxuICAgIC8vICh3aGljaCB3YXMgaW5qZWN0ZWQgYnkgdGhlIGJ1aWxkIHByb2Nlc3MpIGZyb20gdGhlIEhUTUwuXG4gICAgcmVtb3ZlRXZlbnREaXNwYXRjaFNjcmlwdChkb2MpO1xuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIG1hcmtlciBjb21tZW50IG5vZGUgYW5kIGFwcGVuZCBpdCBpbnRvIHRoZSBgPGJvZHk+YC5cbiAqIFNvbWUgQ0ROcyBoYXZlIG1lY2hhbmlzbXMgdG8gcmVtb3ZlIGFsbCBjb21tZW50IG5vZGUgZnJvbSBIVE1MLlxuICogVGhpcyBiZWhhdmlvdXIgYnJlYWtzIGh5ZHJhdGlvbiwgc28gd2UnbGwgZGV0ZWN0IG9uIHRoZSBjbGllbnQgc2lkZSBpZiB0aGlzXG4gKiBtYXJrZXIgY29tbWVudCBpcyBzdGlsbCBhdmFpbGFibGUgb3IgZWxzZSB0aHJvdyBhbiBlcnJvclxuICovXG5mdW5jdGlvbiBhcHBlbmRTc3JDb250ZW50SW50ZWdyaXR5TWFya2VyKGRvYzogRG9jdW1lbnQpIHtcbiAgLy8gQWRkaW5nIGEgbmcgaHlkcmF0aW9uIG1hcmtlciBjb21tZW50XG4gIGNvbnN0IGNvbW1lbnQgPSBkb2MuY3JlYXRlQ29tbWVudChTU1JfQ09OVEVOVF9JTlRFR1JJVFlfTUFSS0VSKTtcbiAgZG9jLmJvZHkuZmlyc3RDaGlsZFxuICAgID8gZG9jLmJvZHkuaW5zZXJ0QmVmb3JlKGNvbW1lbnQsIGRvYy5ib2R5LmZpcnN0Q2hpbGQpXG4gICAgOiBkb2MuYm9keS5hcHBlbmQoY29tbWVudCk7XG59XG5cbi8qKlxuICogQWRkcyB0aGUgYG5nLXNlcnZlci1jb250ZXh0YCBhdHRyaWJ1dGUgdG8gaG9zdCBlbGVtZW50cyBvZiBhbGwgYm9vdHN0cmFwcGVkIGNvbXBvbmVudHNcbiAqIHdpdGhpbiBhIGdpdmVuIGFwcGxpY2F0aW9uLlxuICovXG5mdW5jdGlvbiBhcHBlbmRTZXJ2ZXJDb250ZXh0SW5mbyhhcHBsaWNhdGlvblJlZjogQXBwbGljYXRpb25SZWYpIHtcbiAgY29uc3QgaW5qZWN0b3IgPSBhcHBsaWNhdGlvblJlZi5pbmplY3RvcjtcbiAgbGV0IHNlcnZlckNvbnRleHQgPSBzYW5pdGl6ZVNlcnZlckNvbnRleHQoaW5qZWN0b3IuZ2V0KFNFUlZFUl9DT05URVhULCBERUZBVUxUX1NFUlZFUl9DT05URVhUKSk7XG4gIGFwcGxpY2F0aW9uUmVmLmNvbXBvbmVudHMuZm9yRWFjaCgoY29tcG9uZW50UmVmKSA9PiB7XG4gICAgY29uc3QgcmVuZGVyZXIgPSBjb21wb25lbnRSZWYuaW5qZWN0b3IuZ2V0KFJlbmRlcmVyMik7XG4gICAgY29uc3QgZWxlbWVudCA9IGNvbXBvbmVudFJlZi5sb2NhdGlvbi5uYXRpdmVFbGVtZW50O1xuICAgIGlmIChlbGVtZW50KSB7XG4gICAgICByZW5kZXJlci5zZXRBdHRyaWJ1dGUoZWxlbWVudCwgJ25nLXNlcnZlci1jb250ZXh0Jywgc2VydmVyQ29udGV4dCk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gaW5zZXJ0RXZlbnRSZWNvcmRTY3JpcHQoXG4gIGFwcElkOiBzdHJpbmcsXG4gIGRvYzogRG9jdW1lbnQsXG4gIGV2ZW50VHlwZXNUb1JlcGxheToge3JlZ3VsYXI6IFNldDxzdHJpbmc+OyBjYXB0dXJlOiBTZXQ8c3RyaW5nPn0sXG4gIG5vbmNlOiBzdHJpbmcgfCBudWxsLFxuKTogdm9pZCB7XG4gIGNvbnN0IHtyZWd1bGFyLCBjYXB0dXJlfSA9IGV2ZW50VHlwZXNUb1JlcGxheTtcbiAgY29uc3QgZXZlbnREaXNwYXRjaFNjcmlwdCA9IGZpbmRFdmVudERpc3BhdGNoU2NyaXB0KGRvYyk7XG4gIGlmIChldmVudERpc3BhdGNoU2NyaXB0KSB7XG4gICAgLy8gVGhpcyBpcyBkZWZpbmVkIGluIHBhY2thZ2VzL2NvcmUvcHJpbWl0aXZlcy9ldmVudC1kaXNwYXRjaC9jb250cmFjdF9iaW5hcnkudHNcbiAgICBjb25zdCByZXBsYXlTY3JpcHRDb250ZW50cyA9XG4gICAgICBgd2luZG93Ll9fanNhY3Rpb25fYm9vdHN0cmFwKGAgK1xuICAgICAgYGRvY3VtZW50LmJvZHksYCArXG4gICAgICBgXCIke2FwcElkfVwiLGAgK1xuICAgICAgYCR7SlNPTi5zdHJpbmdpZnkoQXJyYXkuZnJvbShyZWd1bGFyKSl9LGAgK1xuICAgICAgYCR7SlNPTi5zdHJpbmdpZnkoQXJyYXkuZnJvbShjYXB0dXJlKSl9YCArXG4gICAgICBgKTtgO1xuXG4gICAgY29uc3QgcmVwbGF5U2NyaXB0ID0gY3JlYXRlU2NyaXB0KGRvYywgcmVwbGF5U2NyaXB0Q29udGVudHMsIG5vbmNlKTtcblxuICAgIC8vIEluc2VydCByZXBsYXkgc2NyaXB0IHJpZ2h0IGFmdGVyIGlubGluZWQgZXZlbnQgZGlzcGF0Y2ggc2NyaXB0LCBzaW5jZSBpdFxuICAgIC8vIHJlbGllcyBvbiBgX19qc2FjdGlvbl9ib290c3RyYXBgIHRvIGJlIGRlZmluZWQgaW4gdGhlIGdsb2JhbCBzY29wZS5cbiAgICBldmVudERpc3BhdGNoU2NyaXB0LmFmdGVyKHJlcGxheVNjcmlwdCk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gX3JlbmRlcihwbGF0Zm9ybVJlZjogUGxhdGZvcm1SZWYsIGFwcGxpY2F0aW9uUmVmOiBBcHBsaWNhdGlvblJlZik6IFByb21pc2U8c3RyaW5nPiB7XG4gIC8vIEJsb2NrIHVudGlsIGFwcGxpY2F0aW9uIGlzIHN0YWJsZS5cbiAgYXdhaXQgd2hlblN0YWJsZShhcHBsaWNhdGlvblJlZik7XG5cbiAgY29uc3QgcGxhdGZvcm1TdGF0ZSA9IHBsYXRmb3JtUmVmLmluamVjdG9yLmdldChQbGF0Zm9ybVN0YXRlKTtcbiAgcHJlcGFyZUZvckh5ZHJhdGlvbihwbGF0Zm9ybVN0YXRlLCBhcHBsaWNhdGlvblJlZik7XG5cbiAgLy8gUnVuIGFueSBCRUZPUkVfQVBQX1NFUklBTElaRUQgY2FsbGJhY2tzIGp1c3QgYmVmb3JlIHJlbmRlcmluZyB0byBzdHJpbmcuXG4gIGNvbnN0IGVudmlyb25tZW50SW5qZWN0b3IgPSBhcHBsaWNhdGlvblJlZi5pbmplY3RvcjtcbiAgY29uc3QgY2FsbGJhY2tzID0gZW52aXJvbm1lbnRJbmplY3Rvci5nZXQoQkVGT1JFX0FQUF9TRVJJQUxJWkVELCBudWxsKTtcbiAgaWYgKGNhbGxiYWNrcykge1xuICAgIGNvbnN0IGFzeW5jQ2FsbGJhY2tzOiBQcm9taXNlPHZvaWQ+W10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGNhbGxiYWNrIG9mIGNhbGxiYWNrcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY2FsbGJhY2tSZXN1bHQgPSBjYWxsYmFjaygpO1xuICAgICAgICBpZiAoY2FsbGJhY2tSZXN1bHQpIHtcbiAgICAgICAgICBhc3luY0NhbGxiYWNrcy5wdXNoKGNhbGxiYWNrUmVzdWx0KTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvLyBJZ25vcmUgZXhjZXB0aW9ucy5cbiAgICAgICAgY29uc29sZS53YXJuKCdJZ25vcmluZyBCRUZPUkVfQVBQX1NFUklBTElaRUQgRXhjZXB0aW9uOiAnLCBlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoYXN5bmNDYWxsYmFja3MubGVuZ3RoKSB7XG4gICAgICBmb3IgKGNvbnN0IHJlc3VsdCBvZiBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYXN5bmNDYWxsYmFja3MpKSB7XG4gICAgICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAncmVqZWN0ZWQnKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKCdJZ25vcmluZyBCRUZPUkVfQVBQX1NFUklBTElaRUQgRXhjZXB0aW9uOiAnLCByZXN1bHQucmVhc29uKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGFwcGVuZFNlcnZlckNvbnRleHRJbmZvKGFwcGxpY2F0aW9uUmVmKTtcbiAgY29uc3Qgb3V0cHV0ID0gcGxhdGZvcm1TdGF0ZS5yZW5kZXJUb1N0cmluZygpO1xuXG4gIC8vIERlc3Ryb3kgdGhlIGFwcGxpY2F0aW9uIGluIGEgbWFjcm90YXNrLCB0aGlzIGFsbG93cyBwZW5kaW5nIHByb21pc2VzIHRvIGJlIHNldHRsZWQgYW5kIGVycm9yc1xuICAvLyB0byBiZSBzdXJmYWNlZCB0byB0aGUgdXNlcnMuXG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBwbGF0Zm9ybVJlZi5kZXN0cm95KCk7XG4gICAgICByZXNvbHZlKCk7XG4gICAgfSwgMCk7XG4gIH0pO1xuXG4gIHJldHVybiBvdXRwdXQ7XG59XG5cbi8qKlxuICogU3BlY2lmaWVzIHRoZSB2YWx1ZSB0aGF0IHNob3VsZCBiZSB1c2VkIGlmIG5vIHNlcnZlciBjb250ZXh0IHZhbHVlIGhhcyBiZWVuIHByb3ZpZGVkLlxuICovXG5jb25zdCBERUZBVUxUX1NFUlZFUl9DT05URVhUID0gJ290aGVyJztcblxuLyoqXG4gKiBBbiBpbnRlcm5hbCB0b2tlbiB0aGF0IGFsbG93cyBwcm92aWRpbmcgZXh0cmEgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNlcnZlciBjb250ZXh0XG4gKiAoZS5nLiB3aGV0aGVyIFNTUiBvciBTU0cgd2FzIHVzZWQpLiBUaGUgdmFsdWUgaXMgYSBzdHJpbmcgYW5kIGNoYXJhY3RlcnMgb3RoZXJcbiAqIHRoYW4gW2EtekEtWjAtOVxcLV0gYXJlIHJlbW92ZWQuIFNlZSB0aGUgZGVmYXVsdCB2YWx1ZSBpbiBgREVGQVVMVF9TRVJWRVJfQ09OVEVYVGAgY29uc3QuXG4gKi9cbmV4cG9ydCBjb25zdCBTRVJWRVJfQ09OVEVYVCA9IG5ldyBJbmplY3Rpb25Ub2tlbjxzdHJpbmc+KCdTRVJWRVJfQ09OVEVYVCcpO1xuXG4vKipcbiAqIFNhbml0aXplcyBwcm92aWRlZCBzZXJ2ZXIgY29udGV4dDpcbiAqIC0gcmVtb3ZlcyBhbGwgY2hhcmFjdGVycyBvdGhlciB0aGFuIGEteiwgQS1aLCAwLTkgYW5kIGAtYFxuICogLSByZXR1cm5zIGBvdGhlcmAgaWYgbm90aGluZyBpcyBwcm92aWRlZCBvciB0aGUgc3RyaW5nIGlzIGVtcHR5IGFmdGVyIHNhbml0aXphdGlvblxuICovXG5mdW5jdGlvbiBzYW5pdGl6ZVNlcnZlckNvbnRleHQoc2VydmVyQ29udGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgY29udGV4dCA9IHNlcnZlckNvbnRleHQucmVwbGFjZSgvW15hLXpBLVowLTlcXC1dL2csICcnKTtcbiAgcmV0dXJuIGNvbnRleHQubGVuZ3RoID4gMCA/IGNvbnRleHQgOiBERUZBVUxUX1NFUlZFUl9DT05URVhUO1xufVxuXG4vKipcbiAqIEJvb3RzdHJhcHMgYW4gYXBwbGljYXRpb24gdXNpbmcgcHJvdmlkZWQgTmdNb2R1bGUgYW5kIHNlcmlhbGl6ZXMgdGhlIHBhZ2UgY29udGVudCB0byBzdHJpbmcuXG4gKlxuICogQHBhcmFtIG1vZHVsZVR5cGUgQSByZWZlcmVuY2UgdG8gYW4gTmdNb2R1bGUgdGhhdCBzaG91bGQgYmUgdXNlZCBmb3IgYm9vdHN0cmFwLlxuICogQHBhcmFtIG9wdGlvbnMgQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGZvciB0aGUgcmVuZGVyIG9wZXJhdGlvbjpcbiAqICAtIGBkb2N1bWVudGAgLSB0aGUgZG9jdW1lbnQgb2YgdGhlIHBhZ2UgdG8gcmVuZGVyLCBlaXRoZXIgYXMgYW4gSFRNTCBzdHJpbmcgb3JcbiAqICAgICAgICAgICAgICAgICBhcyBhIHJlZmVyZW5jZSB0byB0aGUgYGRvY3VtZW50YCBpbnN0YW5jZS5cbiAqICAtIGB1cmxgIC0gdGhlIFVSTCBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKiAgLSBgZXh0cmFQcm92aWRlcnNgIC0gc2V0IG9mIHBsYXRmb3JtIGxldmVsIHByb3ZpZGVycyBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKlxuICogQHB1YmxpY0FwaVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVuZGVyTW9kdWxlPFQ+KFxuICBtb2R1bGVUeXBlOiBUeXBlPFQ+LFxuICBvcHRpb25zOiB7ZG9jdW1lbnQ/OiBzdHJpbmcgfCBEb2N1bWVudDsgdXJsPzogc3RyaW5nOyBleHRyYVByb3ZpZGVycz86IFN0YXRpY1Byb3ZpZGVyW119LFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3Qge2RvY3VtZW50LCB1cmwsIGV4dHJhUHJvdmlkZXJzOiBwbGF0Zm9ybVByb3ZpZGVyc30gPSBvcHRpb25zO1xuICBjb25zdCBwbGF0Zm9ybVJlZiA9IGNyZWF0ZVNlcnZlclBsYXRmb3JtKHtkb2N1bWVudCwgdXJsLCBwbGF0Zm9ybVByb3ZpZGVyc30pO1xuICBjb25zdCBtb2R1bGVSZWYgPSBhd2FpdCBwbGF0Zm9ybVJlZi5ib290c3RyYXBNb2R1bGUobW9kdWxlVHlwZSk7XG4gIGNvbnN0IGFwcGxpY2F0aW9uUmVmID0gbW9kdWxlUmVmLmluamVjdG9yLmdldChBcHBsaWNhdGlvblJlZik7XG4gIHJldHVybiBfcmVuZGVyKHBsYXRmb3JtUmVmLCBhcHBsaWNhdGlvblJlZik7XG59XG5cbi8qKlxuICogQm9vdHN0cmFwcyBhbiBpbnN0YW5jZSBvZiBhbiBBbmd1bGFyIGFwcGxpY2F0aW9uIGFuZCByZW5kZXJzIGl0IHRvIGEgc3RyaW5nLlxuXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBib290c3RyYXAgPSAoKSA9PiBib290c3RyYXBBcHBsaWNhdGlvbihSb290Q29tcG9uZW50LCBhcHBDb25maWcpO1xuICogY29uc3Qgb3V0cHV0OiBzdHJpbmcgPSBhd2FpdCByZW5kZXJBcHBsaWNhdGlvbihib290c3RyYXApO1xuICogYGBgXG4gKlxuICogQHBhcmFtIGJvb3RzdHJhcCBBIG1ldGhvZCB0aGF0IHdoZW4gaW52b2tlZCByZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHJldHVybnMgYW4gYEFwcGxpY2F0aW9uUmVmYFxuICogICAgIGluc3RhbmNlIG9uY2UgcmVzb2x2ZWQuXG4gKiBAcGFyYW0gb3B0aW9ucyBBZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSByZW5kZXIgb3BlcmF0aW9uOlxuICogIC0gYGRvY3VtZW50YCAtIHRoZSBkb2N1bWVudCBvZiB0aGUgcGFnZSB0byByZW5kZXIsIGVpdGhlciBhcyBhbiBIVE1MIHN0cmluZyBvclxuICogICAgICAgICAgICAgICAgIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBgZG9jdW1lbnRgIGluc3RhbmNlLlxuICogIC0gYHVybGAgLSB0aGUgVVJMIGZvciB0aGUgY3VycmVudCByZW5kZXIgcmVxdWVzdC5cbiAqICAtIGBwbGF0Zm9ybVByb3ZpZGVyc2AgLSB0aGUgcGxhdGZvcm0gbGV2ZWwgcHJvdmlkZXJzIGZvciB0aGUgY3VycmVudCByZW5kZXIgcmVxdWVzdC5cbiAqXG4gKiBAcmV0dXJucyBBIFByb21pc2UsIHRoYXQgcmV0dXJucyBzZXJpYWxpemVkICh0byBhIHN0cmluZykgcmVuZGVyZWQgcGFnZSwgb25jZSByZXNvbHZlZC5cbiAqXG4gKiBAcHVibGljQXBpXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5kZXJBcHBsaWNhdGlvbjxUPihcbiAgYm9vdHN0cmFwOiAoKSA9PiBQcm9taXNlPEFwcGxpY2F0aW9uUmVmPixcbiAgb3B0aW9uczoge2RvY3VtZW50Pzogc3RyaW5nIHwgRG9jdW1lbnQ7IHVybD86IHN0cmluZzsgcGxhdGZvcm1Qcm92aWRlcnM/OiBQcm92aWRlcltdfSxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIHJldHVybiBydW5BbmRNZWFzdXJlUGVyZigncmVuZGVyQXBwbGljYXRpb24nLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcGxhdGZvcm1SZWYgPSBjcmVhdGVTZXJ2ZXJQbGF0Zm9ybShvcHRpb25zKTtcblxuICAgIGNvbnN0IGFwcGxpY2F0aW9uUmVmID0gYXdhaXQgYm9vdHN0cmFwKCk7XG4gICAgcmV0dXJuIF9yZW5kZXIocGxhdGZvcm1SZWYsIGFwcGxpY2F0aW9uUmVmKTtcbiAgfSk7XG59XG4iXX0=