UNPKG

@angular/platform-server

Version:

Angular - library for using Angular in Node.js

166 lines (165 loc) 23.2 kB
/** * @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 { ApplicationRef, InjectionToken, Renderer2, ɵannotateForHydration as annotateForHydration, ɵENABLED_SSR_FEATURES as ENABLED_SSR_FEATURES, ɵIS_HYDRATION_DOM_REUSE_ENABLED as IS_HYDRATION_DOM_REUSE_ENABLED, ɵSSR_CONTENT_INTEGRITY_MARKER as SSR_CONTENT_INTEGRITY_MARKER } from '@angular/core'; import { first } from 'rxjs/operators'; import { PlatformState } from './platform_state'; import { platformServer } from './server'; import { BEFORE_APP_SERIALIZED, INITIAL_CONFIG } from './tokens'; /** * 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 ]); } /** * 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 marken 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)); const features = injector.get(ENABLED_SSR_FEATURES); if (features.size > 0) { // Append features information into the server context value. serverContext += `|${Array.from(features).join(',')}`; } applicationRef.components.forEach(componentRef => { const renderer = componentRef.injector.get(Renderer2); const element = componentRef.location.nativeElement; if (element) { renderer.setAttribute(element, 'ng-server-context', serverContext); } }); } async function _render(platformRef, applicationRef) { const environmentInjector = applicationRef.injector; // Block until application is stable. await applicationRef.isStable.pipe((first((isStable) => isStable))).toPromise(); const platformState = platformRef.injector.get(PlatformState); if (applicationRef.injector.get(IS_HYDRATION_DOM_REUSE_ENABLED, false)) { const doc = platformState.getDocument(); appendSsrContentIntegrityMarker(doc); annotateForHydration(applicationRef, doc); } // Run any BEFORE_APP_SERIALIZED callbacks just before rendering to string. 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 * @developerPreview */ export async function renderApplication(bootstrap, options) { const platformRef = createServerPlatform(options); const applicationRef = await bootstrap(); return _render(platformRef, applicationRef); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9wbGF0Zm9ybS1zZXJ2ZXIvc3JjL3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBQyxjQUFjLEVBQUUsY0FBYyxFQUF5QixTQUFTLEVBQXdCLHFCQUFxQixJQUFJLG9CQUFvQixFQUFFLHFCQUFxQixJQUFJLG9CQUFvQixFQUFFLCtCQUErQixJQUFJLDhCQUE4QixFQUFFLDZCQUE2QixJQUFJLDRCQUE0QixFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3JWLE9BQU8sRUFBQyxLQUFLLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUVyQyxPQUFPLEVBQUMsYUFBYSxFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFDL0MsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUN4QyxPQUFPLEVBQUMscUJBQXFCLEVBQUUsY0FBYyxFQUFDLE1BQU0sVUFBVSxDQUFDO0FBUS9EOzs7R0FHRztBQUNILFNBQVMsb0JBQW9CLENBQUMsT0FBd0I7SUFDcEQsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLGlCQUFpQixJQUFJLEVBQUUsQ0FBQztJQUN2RCxPQUFPLGNBQWMsQ0FBQztRQUNwQixFQUFDLE9BQU8sRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLEVBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFLE9BQU8sQ0FBQyxHQUFHLEVBQUMsRUFBQztRQUNuRixjQUFjO0tBQ2YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUywrQkFBK0IsQ0FBQyxHQUFhO0lBQ3BELHVDQUF1QztJQUN2QyxNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsYUFBYSxDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDaEUsR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLENB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QUFHLG9CQUFvQixDQUFDLEVBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRSxpQkFBaUIsRUFBQyxDQUFDLENBQUM7SUFDN0UsTUFBTSxTQUFTLEdBQUcsTUFBTSxXQUFXLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ2hFLE1BQU0sY0FBYyxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzlELE9BQU8sT0FBTyxDQUFDLFdBQVcsRUFBRSxjQUFjLENBQUMsQ0FBQztBQUM5QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxpQkFBaUIsQ0FBSSxTQUF3QyxFQUFFLE9BSXBGO0lBQ0MsTUFBTSxXQUFXLEdBQUcsb0JBQW9CLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbEQsTUFBTSxjQUFjLEdBQUcsTUFBTSxTQUFTLEVBQUUsQ0FBQztJQUN6QyxPQUFPLE9BQU8sQ0FBQyxXQUFXLEVBQUUsY0FBYyxDQUFDLENBQUM7QUFDOUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge0FwcGxpY2F0aW9uUmVmLCBJbmplY3Rpb25Ub2tlbiwgUGxhdGZvcm1SZWYsIFByb3ZpZGVyLCBSZW5kZXJlcjIsIFN0YXRpY1Byb3ZpZGVyLCBUeXBlLCDJtWFubm90YXRlRm9ySHlkcmF0aW9uIGFzIGFubm90YXRlRm9ySHlkcmF0aW9uLCDJtUVOQUJMRURfU1NSX0ZFQVRVUkVTIGFzIEVOQUJMRURfU1NSX0ZFQVRVUkVTLCDJtUlTX0hZRFJBVElPTl9ET01fUkVVU0VfRU5BQkxFRCBhcyBJU19IWURSQVRJT05fRE9NX1JFVVNFX0VOQUJMRUQsIMm1U1NSX0NPTlRFTlRfSU5URUdSSVRZX01BUktFUiBhcyBTU1JfQ09OVEVOVF9JTlRFR1JJVFlfTUFSS0VSfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7Zmlyc3R9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHtQbGF0Zm9ybVN0YXRlfSBmcm9tICcuL3BsYXRmb3JtX3N0YXRlJztcbmltcG9ydCB7cGxhdGZvcm1TZXJ2ZXJ9IGZyb20gJy4vc2VydmVyJztcbmltcG9ydCB7QkVGT1JFX0FQUF9TRVJJQUxJWkVELCBJTklUSUFMX0NPTkZJR30gZnJvbSAnLi90b2tlbnMnO1xuXG5pbnRlcmZhY2UgUGxhdGZvcm1PcHRpb25zIHtcbiAgZG9jdW1lbnQ/OiBzdHJpbmd8RG9jdW1lbnQ7XG4gIHVybD86IHN0cmluZztcbiAgcGxhdGZvcm1Qcm92aWRlcnM/OiBQcm92aWRlcltdO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYSBzZXJ2ZXIgcGxhdGZvcm0gKHdpdGggb3Igd2l0aG91dCBKSVQgY29tcGlsZXIgc3VwcG9ydFxuICogZGVwZW5kaW5nIG9uIHRoZSBgbmdKaXRNb2RlYCBnbG9iYWwgY29uc3QgdmFsdWUpLCB1c2luZyBwcm92aWRlZCBvcHRpb25zLlxuICovXG5mdW5jdGlvbiBjcmVhdGVTZXJ2ZXJQbGF0Zm9ybShvcHRpb25zOiBQbGF0Zm9ybU9wdGlvbnMpOiBQbGF0Zm9ybVJlZiB7XG4gIGNvbnN0IGV4dHJhUHJvdmlkZXJzID0gb3B0aW9ucy5wbGF0Zm9ybVByb3ZpZGVycyA/PyBbXTtcbiAgcmV0dXJuIHBsYXRmb3JtU2VydmVyKFtcbiAgICB7cHJvdmlkZTogSU5JVElBTF9DT05GSUcsIHVzZVZhbHVlOiB7ZG9jdW1lbnQ6IG9wdGlvbnMuZG9jdW1lbnQsIHVybDogb3B0aW9ucy51cmx9fSxcbiAgICBleHRyYVByb3ZpZGVyc1xuICBdKTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgbWFya2VyIGNvbW1lbnQgbm9kZSBhbmQgYXBwZW5kIGl0IGludG8gdGhlIGA8Ym9keT5gLlxuICogU29tZSBDRE5zIGhhdmUgbWVjaGFuaXNtcyB0byByZW1vdmUgYWxsIGNvbW1lbnQgbm9kZSBmcm9tIEhUTUwuXG4gKiBUaGlzIGJlaGF2aW91ciBicmVha3MgaHlkcmF0aW9uLCBzbyB3ZSdsbCBkZXRlY3Qgb24gdGhlIGNsaWVudCBzaWRlIGlmIHRoaXNcbiAqIG1hcmtlciBjb21tZW50IGlzIHN0aWxsIGF2YWlsYWJsZSBvciBlbHNlIHRocm93IGFuIGVycm9yXG4gKi9cbmZ1bmN0aW9uIGFwcGVuZFNzckNvbnRlbnRJbnRlZ3JpdHlNYXJrZXIoZG9jOiBEb2N1bWVudCkge1xuICAvLyBBZGRpbmcgYSBuZyBoeWRyYXRpb24gbWFya2VuIGNvbW1lbnRcbiAgY29uc3QgY29tbWVudCA9IGRvYy5jcmVhdGVDb21tZW50KFNTUl9DT05URU5UX0lOVEVHUklUWV9NQVJLRVIpO1xuICBkb2MuYm9keS5maXJzdENoaWxkID8gZG9jLmJvZHkuaW5zZXJ0QmVmb3JlKGNvbW1lbnQsIGRvYy5ib2R5LmZpcnN0Q2hpbGQpIDpcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvYy5ib2R5LmFwcGVuZChjb21tZW50KTtcbn1cblxuLyoqXG4gKiBBZGRzIHRoZSBgbmctc2VydmVyLWNvbnRleHRgIGF0dHJpYnV0ZSB0byBob3N0IGVsZW1lbnRzIG9mIGFsbCBib290c3RyYXBwZWQgY29tcG9uZW50c1xuICogd2l0aGluIGEgZ2l2ZW4gYXBwbGljYXRpb24uXG4gKi9cbmZ1bmN0aW9uIGFwcGVuZFNlcnZlckNvbnRleHRJbmZvKGFwcGxpY2F0aW9uUmVmOiBBcHBsaWNhdGlvblJlZikge1xuICBjb25zdCBpbmplY3RvciA9IGFwcGxpY2F0aW9uUmVmLmluamVjdG9yO1xuICBsZXQgc2VydmVyQ29udGV4dCA9IHNhbml0aXplU2VydmVyQ29udGV4dChpbmplY3Rvci5nZXQoU0VSVkVSX0NPTlRFWFQsIERFRkFVTFRfU0VSVkVSX0NPTlRFWFQpKTtcbiAgY29uc3QgZmVhdHVyZXMgPSBpbmplY3Rvci5nZXQoRU5BQkxFRF9TU1JfRkVBVFVSRVMpO1xuICBpZiAoZmVhdHVyZXMuc2l6ZSA+IDApIHtcbiAgICAvLyBBcHBlbmQgZmVhdHVyZXMgaW5mb3JtYXRpb24gaW50byB0aGUgc2VydmVyIGNvbnRleHQgdmFsdWUuXG4gICAgc2VydmVyQ29udGV4dCArPSBgfCR7QXJyYXkuZnJvbShmZWF0dXJlcykuam9pbignLCcpfWA7XG4gIH1cbiAgYXBwbGljYXRpb25SZWYuY29tcG9uZW50cy5mb3JFYWNoKGNvbXBvbmVudFJlZiA9PiB7XG4gICAgY29uc3QgcmVuZGVyZXIgPSBjb21wb25lbnRSZWYuaW5qZWN0b3IuZ2V0KFJlbmRlcmVyMik7XG4gICAgY29uc3QgZWxlbWVudCA9IGNvbXBvbmVudFJlZi5sb2NhdGlvbi5uYXRpdmVFbGVtZW50O1xuICAgIGlmIChlbGVtZW50KSB7XG4gICAgICByZW5kZXJlci5zZXRBdHRyaWJ1dGUoZWxlbWVudCwgJ25nLXNlcnZlci1jb250ZXh0Jywgc2VydmVyQ29udGV4dCk7XG4gICAgfVxuICB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gX3JlbmRlcihwbGF0Zm9ybVJlZjogUGxhdGZvcm1SZWYsIGFwcGxpY2F0aW9uUmVmOiBBcHBsaWNhdGlvblJlZik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IGVudmlyb25tZW50SW5qZWN0b3IgPSBhcHBsaWNhdGlvblJlZi5pbmplY3RvcjtcblxuICAvLyBCbG9jayB1bnRpbCBhcHBsaWNhdGlvbiBpcyBzdGFibGUuXG4gIGF3YWl0IGFwcGxpY2F0aW9uUmVmLmlzU3RhYmxlLnBpcGUoKGZpcnN0KChpc1N0YWJsZTogYm9vbGVhbikgPT4gaXNTdGFibGUpKSkudG9Qcm9taXNlKCk7XG5cbiAgY29uc3QgcGxhdGZvcm1TdGF0ZSA9IHBsYXRmb3JtUmVmLmluamVjdG9yLmdldChQbGF0Zm9ybVN0YXRlKTtcbiAgaWYgKGFwcGxpY2F0aW9uUmVmLmluamVjdG9yLmdldChJU19IWURSQVRJT05fRE9NX1JFVVNFX0VOQUJMRUQsIGZhbHNlKSkge1xuICAgIGNvbnN0IGRvYyA9IHBsYXRmb3JtU3RhdGUuZ2V0RG9jdW1lbnQoKTtcbiAgICBhcHBlbmRTc3JDb250ZW50SW50ZWdyaXR5TWFya2VyKGRvYyk7XG4gICAgYW5ub3RhdGVGb3JIeWRyYXRpb24oYXBwbGljYXRpb25SZWYsIGRvYyk7XG4gIH1cblxuICAvLyBSdW4gYW55IEJFRk9SRV9BUFBfU0VSSUFMSVpFRCBjYWxsYmFja3MganVzdCBiZWZvcmUgcmVuZGVyaW5nIHRvIHN0cmluZy5cbiAgY29uc3QgY2FsbGJhY2tzID0gZW52aXJvbm1lbnRJbmplY3Rvci5nZXQoQkVGT1JFX0FQUF9TRVJJQUxJWkVELCBudWxsKTtcbiAgaWYgKGNhbGxiYWNrcykge1xuICAgIGNvbnN0IGFzeW5jQ2FsbGJhY2tzOiBQcm9taXNlPHZvaWQ+W10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGNhbGxiYWNrIG9mIGNhbGxiYWNrcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY2FsbGJhY2tSZXN1bHQgPSBjYWxsYmFjaygpO1xuICAgICAgICBpZiAoY2FsbGJhY2tSZXN1bHQpIHtcbiAgICAgICAgICBhc3luY0NhbGxiYWNrcy5wdXNoKGNhbGxiYWNrUmVzdWx0KTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvLyBJZ25vcmUgZXhjZXB0aW9ucy5cbiAgICAgICAgY29uc29sZS53YXJuKCdJZ25vcmluZyBCRUZPUkVfQVBQX1NFUklBTElaRUQgRXhjZXB0aW9uOiAnLCBlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoYXN5bmNDYWxsYmFja3MubGVuZ3RoKSB7XG4gICAgICBmb3IgKGNvbnN0IHJlc3VsdCBvZiBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYXN5bmNDYWxsYmFja3MpKSB7XG4gICAgICAgIGlmIChyZXN1bHQuc3RhdHVzID09PSAncmVqZWN0ZWQnKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKCdJZ25vcmluZyBCRUZPUkVfQVBQX1NFUklBTElaRUQgRXhjZXB0aW9uOiAnLCByZXN1bHQucmVhc29uKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGFwcGVuZFNlcnZlckNvbnRleHRJbmZvKGFwcGxpY2F0aW9uUmVmKTtcbiAgY29uc3Qgb3V0cHV0ID0gcGxhdGZvcm1TdGF0ZS5yZW5kZXJUb1N0cmluZygpO1xuXG4gIC8vIERlc3Ryb3kgdGhlIGFwcGxpY2F0aW9uIGluIGEgbWFjcm90YXNrLCB0aGlzIGFsbG93cyBwZW5kaW5nIHByb21pc2VzIHRvIGJlIHNldHRsZWQgYW5kIGVycm9yc1xuICAvLyB0byBiZSBzdXJmYWNlZCB0byB0aGUgdXNlcnMuXG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBwbGF0Zm9ybVJlZi5kZXN0cm95KCk7XG4gICAgICByZXNvbHZlKCk7XG4gICAgfSwgMCk7XG4gIH0pO1xuXG4gIHJldHVybiBvdXRwdXQ7XG59XG5cbi8qKlxuICogU3BlY2lmaWVzIHRoZSB2YWx1ZSB0aGF0IHNob3VsZCBiZSB1c2VkIGlmIG5vIHNlcnZlciBjb250ZXh0IHZhbHVlIGhhcyBiZWVuIHByb3ZpZGVkLlxuICovXG5jb25zdCBERUZBVUxUX1NFUlZFUl9DT05URVhUID0gJ290aGVyJztcblxuLyoqXG4gKiBBbiBpbnRlcm5hbCB0b2tlbiB0aGF0IGFsbG93cyBwcm92aWRpbmcgZXh0cmEgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNlcnZlciBjb250ZXh0XG4gKiAoZS5nLiB3aGV0aGVyIFNTUiBvciBTU0cgd2FzIHVzZWQpLiBUaGUgdmFsdWUgaXMgYSBzdHJpbmcgYW5kIGNoYXJhY3RlcnMgb3RoZXJcbiAqIHRoYW4gW2EtekEtWjAtOVxcLV0gYXJlIHJlbW92ZWQuIFNlZSB0aGUgZGVmYXVsdCB2YWx1ZSBpbiBgREVGQVVMVF9TRVJWRVJfQ09OVEVYVGAgY29uc3QuXG4gKi9cbmV4cG9ydCBjb25zdCBTRVJWRVJfQ09OVEVYVCA9IG5ldyBJbmplY3Rpb25Ub2tlbjxzdHJpbmc+KCdTRVJWRVJfQ09OVEVYVCcpO1xuXG4vKipcbiAqIFNhbml0aXplcyBwcm92aWRlZCBzZXJ2ZXIgY29udGV4dDpcbiAqIC0gcmVtb3ZlcyBhbGwgY2hhcmFjdGVycyBvdGhlciB0aGFuIGEteiwgQS1aLCAwLTkgYW5kIGAtYFxuICogLSByZXR1cm5zIGBvdGhlcmAgaWYgbm90aGluZyBpcyBwcm92aWRlZCBvciB0aGUgc3RyaW5nIGlzIGVtcHR5IGFmdGVyIHNhbml0aXphdGlvblxuICovXG5mdW5jdGlvbiBzYW5pdGl6ZVNlcnZlckNvbnRleHQoc2VydmVyQ29udGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgY29udGV4dCA9IHNlcnZlckNvbnRleHQucmVwbGFjZSgvW15hLXpBLVowLTlcXC1dL2csICcnKTtcbiAgcmV0dXJuIGNvbnRleHQubGVuZ3RoID4gMCA/IGNvbnRleHQgOiBERUZBVUxUX1NFUlZFUl9DT05URVhUO1xufVxuXG4vKipcbiAqIEJvb3RzdHJhcHMgYW4gYXBwbGljYXRpb24gdXNpbmcgcHJvdmlkZWQgTmdNb2R1bGUgYW5kIHNlcmlhbGl6ZXMgdGhlIHBhZ2UgY29udGVudCB0byBzdHJpbmcuXG4gKlxuICogQHBhcmFtIG1vZHVsZVR5cGUgQSByZWZlcmVuY2UgdG8gYW4gTmdNb2R1bGUgdGhhdCBzaG91bGQgYmUgdXNlZCBmb3IgYm9vdHN0cmFwLlxuICogQHBhcmFtIG9wdGlvbnMgQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGZvciB0aGUgcmVuZGVyIG9wZXJhdGlvbjpcbiAqICAtIGBkb2N1bWVudGAgLSB0aGUgZG9jdW1lbnQgb2YgdGhlIHBhZ2UgdG8gcmVuZGVyLCBlaXRoZXIgYXMgYW4gSFRNTCBzdHJpbmcgb3JcbiAqICAgICAgICAgICAgICAgICBhcyBhIHJlZmVyZW5jZSB0byB0aGUgYGRvY3VtZW50YCBpbnN0YW5jZS5cbiAqICAtIGB1cmxgIC0gdGhlIFVSTCBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKiAgLSBgZXh0cmFQcm92aWRlcnNgIC0gc2V0IG9mIHBsYXRmb3JtIGxldmVsIHByb3ZpZGVycyBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKlxuICogQHB1YmxpY0FwaVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVuZGVyTW9kdWxlPFQ+KG1vZHVsZVR5cGU6IFR5cGU8VD4sIG9wdGlvbnM6IHtcbiAgZG9jdW1lbnQ/OiBzdHJpbmd8RG9jdW1lbnQsXG4gIHVybD86IHN0cmluZyxcbiAgZXh0cmFQcm92aWRlcnM/OiBTdGF0aWNQcm92aWRlcltdLFxufSk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHtkb2N1bWVudCwgdXJsLCBleHRyYVByb3ZpZGVyczogcGxhdGZvcm1Qcm92aWRlcnN9ID0gb3B0aW9ucztcbiAgY29uc3QgcGxhdGZvcm1SZWYgPSBjcmVhdGVTZXJ2ZXJQbGF0Zm9ybSh7ZG9jdW1lbnQsIHVybCwgcGxhdGZvcm1Qcm92aWRlcnN9KTtcbiAgY29uc3QgbW9kdWxlUmVmID0gYXdhaXQgcGxhdGZvcm1SZWYuYm9vdHN0cmFwTW9kdWxlKG1vZHVsZVR5cGUpO1xuICBjb25zdCBhcHBsaWNhdGlvblJlZiA9IG1vZHVsZVJlZi5pbmplY3Rvci5nZXQoQXBwbGljYXRpb25SZWYpO1xuICByZXR1cm4gX3JlbmRlcihwbGF0Zm9ybVJlZiwgYXBwbGljYXRpb25SZWYpO1xufVxuXG4vKipcbiAqIEJvb3RzdHJhcHMgYW4gaW5zdGFuY2Ugb2YgYW4gQW5ndWxhciBhcHBsaWNhdGlvbiBhbmQgcmVuZGVycyBpdCB0byBhIHN0cmluZy5cblxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgYm9vdHN0cmFwID0gKCkgPT4gYm9vdHN0cmFwQXBwbGljYXRpb24oUm9vdENvbXBvbmVudCwgYXBwQ29uZmlnKTtcbiAqIGNvbnN0IG91dHB1dDogc3RyaW5nID0gYXdhaXQgcmVuZGVyQXBwbGljYXRpb24oYm9vdHN0cmFwKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBib290c3RyYXAgQSBtZXRob2QgdGhhdCB3aGVuIGludm9rZWQgcmV0dXJucyBhIHByb21pc2UgdGhhdCByZXR1cm5zIGFuIGBBcHBsaWNhdGlvblJlZmBcbiAqICAgICBpbnN0YW5jZSBvbmNlIHJlc29sdmVkLlxuICogQHBhcmFtIG9wdGlvbnMgQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGZvciB0aGUgcmVuZGVyIG9wZXJhdGlvbjpcbiAqICAtIGBkb2N1bWVudGAgLSB0aGUgZG9jdW1lbnQgb2YgdGhlIHBhZ2UgdG8gcmVuZGVyLCBlaXRoZXIgYXMgYW4gSFRNTCBzdHJpbmcgb3JcbiAqICAgICAgICAgICAgICAgICBhcyBhIHJlZmVyZW5jZSB0byB0aGUgYGRvY3VtZW50YCBpbnN0YW5jZS5cbiAqICAtIGB1cmxgIC0gdGhlIFVSTCBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKiAgLSBgcGxhdGZvcm1Qcm92aWRlcnNgIC0gdGhlIHBsYXRmb3JtIGxldmVsIHByb3ZpZGVycyBmb3IgdGhlIGN1cnJlbnQgcmVuZGVyIHJlcXVlc3QuXG4gKlxuICogQHJldHVybnMgQSBQcm9taXNlLCB0aGF0IHJldHVybnMgc2VyaWFsaXplZCAodG8gYSBzdHJpbmcpIHJlbmRlcmVkIHBhZ2UsIG9uY2UgcmVzb2x2ZWQuXG4gKlxuICogQHB1YmxpY0FwaVxuICogQGRldmVsb3BlclByZXZpZXdcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlbmRlckFwcGxpY2F0aW9uPFQ+KGJvb3RzdHJhcDogKCkgPT4gUHJvbWlzZTxBcHBsaWNhdGlvblJlZj4sIG9wdGlvbnM6IHtcbiAgZG9jdW1lbnQ/OiBzdHJpbmd8RG9jdW1lbnQsXG4gIHVybD86IHN0cmluZyxcbiAgcGxhdGZvcm1Qcm92aWRlcnM/OiBQcm92aWRlcltdLFxufSk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHBsYXRmb3JtUmVmID0gY3JlYXRlU2VydmVyUGxhdGZvcm0ob3B0aW9ucyk7XG4gIGNvbnN0IGFwcGxpY2F0aW9uUmVmID0gYXdhaXQgYm9vdHN0cmFwKCk7XG4gIHJldHVybiBfcmVuZGVyKHBsYXRmb3JtUmVmLCBhcHBsaWNhdGlvblJlZik7XG59XG4iXX0=