├── .gitignore ├── .npmignore ├── assets └── react-webcomponent.png ├── dist ├── index.d.ts ├── react-dom-child.d.ts ├── prop-bridge.d.ts └── build.js ├── src ├── types │ └── react-create-ref.d.ts ├── react-dom-child.tsx ├── prop-bridge.tsx └── index.ts ├── tsconfig.json ├── webpack.config.js ├── LICENSE ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | webpack.config.js 3 | assets -------------------------------------------------------------------------------- /assets/react-webcomponent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a7ul/react-webcomponentify/HEAD/assets/react-webcomponent.png -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | declare type Mode = 'open' | 'element'; 3 | export declare const registerAsWebComponent: (component: React.ElementType, customElementName: string, mode?: Mode) => void; 4 | export {}; 5 | -------------------------------------------------------------------------------- /dist/react-dom-child.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'child-replace-with-polyfill'; 3 | export declare class ReactDomChild extends React.Component { 4 | ref: React.RefObject; 5 | componentDidMount(): void; 6 | render(): JSX.Element; 7 | } 8 | -------------------------------------------------------------------------------- /src/types/react-create-ref.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-create-ref' { 2 | import React from 'react'; 3 | 4 | type ReactCreateRef = typeof React.createRef extends Function 5 | ? React.RefObject 6 | : () => { current: null }; 7 | 8 | export default function createRef(): ReactCreateRef; 9 | } 10 | -------------------------------------------------------------------------------- /dist/prop-bridge.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export declare const renderReact2Node: (RComponent: React.ElementType, initialProps: {}, targetDomNode: Element | DocumentFragment, onRender: (ref: React.RefObject) => void) => void; 3 | export declare const sendPropsToReact: (propBridgeRef: React.RefObject, props: any) => void; 4 | export declare const getPropsFromNode: (node: HTMLElement) => { 5 | [key: string]: string | JSX.Element; 6 | }; 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "declaration": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "sourceMap": true, 8 | "target": "es6", 9 | "allowSyntheticDefaultImports": true, 10 | "jsx": "react", 11 | "typeRoots": [ 12 | "node_modules/@types", 13 | "src/types" 14 | ], 15 | "lib": [ 16 | "ES2017", 17 | "DOM" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /src/react-dom-child.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import 'child-replace-with-polyfill'; 3 | import createRef from 'react-create-ref'; 4 | 5 | /* 6 | Wrapper class to wrap the raw html nodes in a 7 | react component before passing it down to react as children 8 | */ 9 | export class ReactDomChild extends React.Component { 10 | ref = createRef(); 11 | componentDidMount() { 12 | const childNodes: Node[] = this.props.children as Node[]; 13 | this.ref.current.replaceWith(...childNodes); 14 | } 15 | render() { 16 | return
; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.ts', 5 | devtool: 'inline-source-map', 6 | mode: 'development', 7 | resolve: { 8 | extensions: ['.ts', '.js', '.tsx'], 9 | }, 10 | output: { 11 | path: path.resolve(__dirname, 'dist'), 12 | filename: 'build.js', 13 | libraryTarget: 'commonjs2', 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.tsx?/, 19 | use: 'ts-loader', 20 | exclude: /node_modules/, 21 | }, 22 | ], 23 | }, 24 | externals: { 25 | react: { 26 | root: 'React', 27 | commonjs2: 'react', 28 | commonjs: 'react', 29 | amd: 'react', 30 | umd: 'react', 31 | }, 32 | 'react-dom': { 33 | root: 'ReactDOM', 34 | commonjs2: 'react-dom', 35 | commonjs: 'react-dom', 36 | amd: 'react-dom', 37 | umd: 'react-dom', 38 | }, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Atul Ramachandran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-webcomponentify", 3 | "version": "1.3.2", 4 | "description": "Build and export React Components as Web Components without any extra effort.", 5 | "main": "dist/build", 6 | "typings": "dist/index", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "webpack -p" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/master-atul/react-webcomponentify.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "web", 18 | "component", 19 | "export", 20 | "web-component", 21 | "javascript" 22 | ], 23 | "author": "atulanand94@gmail.com", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/master-atul/react-webcomponentify/issues" 27 | }, 28 | "homepage": "https://github.com/master-atul/react-webcomponentify#readme", 29 | "dependencies": { 30 | "child-replace-with-polyfill": "1.0.1", 31 | "react-create-ref": "1.0.1" 32 | }, 33 | "devDependencies": { 34 | "@types/react": "17.0.4", 35 | "@types/react-dom": "17.0.3", 36 | "react": "17.0.2", 37 | "react-dom": "17.0.2", 38 | "ts-loader": "8.2.0", 39 | "typescript": "4.2.4", 40 | "webpack": "4.29.0", 41 | "webpack-cli": "3.2.1" 42 | }, 43 | "peerDependencies": { 44 | "react": "*", 45 | "react-dom": "*" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/prop-bridge.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import React from 'react'; 3 | import { ReactDomChild } from './react-dom-child'; 4 | import createRef from 'react-create-ref'; 5 | 6 | /* 7 | PropBridge stores props passed to it via setProps in the state. 8 | And then passes those to the component as regular props 9 | Hence when you call setProps you are calling setState 10 | and then passing those as props to the react component. 11 | */ 12 | 13 | export const renderReact2Node = ( 14 | RComponent: React.ElementType, 15 | initialProps: {}, 16 | targetDomNode: Element | DocumentFragment, 17 | onRender: (ref: React.RefObject) => void 18 | ) => { 19 | class PropBridge extends React.PureComponent { 20 | state = { ...initialProps }; 21 | setProps = (props: React.RefObject) => 22 | this.setState(() => props); 23 | render() { 24 | return ; 25 | } 26 | } 27 | const propBridgeRef = createRef(); 28 | ReactDOM.render(, targetDomNode, () => 29 | onRender(propBridgeRef) 30 | ); 31 | }; 32 | 33 | export const sendPropsToReact = ( 34 | propBridgeRef: React.RefObject, 35 | props: any 36 | ) => { 37 | if (propBridgeRef && propBridgeRef.current) { 38 | propBridgeRef.current.setProps(props); 39 | } 40 | }; 41 | 42 | export const getPropsFromNode = (node: HTMLElement) => { 43 | const attributeNames = node.getAttributeNames(); 44 | const mappedProps = attributeNames.reduce( 45 | (props: { [key: string]: string | JSX.Element }, name: string) => { 46 | props[name] = node.getAttribute(name); 47 | return props; 48 | }, 49 | {} 50 | ); 51 | 52 | const children = Array.from(node.childNodes).map((e) => e.cloneNode(true)); 53 | mappedProps.children = {children}; 54 | return mappedProps; 55 | }; 56 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from "react-dom"; 3 | import { 4 | renderReact2Node, 5 | getPropsFromNode, 6 | sendPropsToReact, 7 | } from './prop-bridge'; 8 | 9 | type Mode = 'open' | 'element'; 10 | 11 | const getCustomElementFromReactComponent = ( 12 | RComponent: React.ElementType, 13 | mode?: Mode 14 | ) => { 15 | return class ReactAsCustomElement extends HTMLElement { 16 | targetNode: this | ShadowRoot = null; 17 | propBridgeRef: any = null; 18 | props = {}; 19 | observer: MutationObserver = null; 20 | 21 | constructor() { 22 | super(); 23 | this.props = getPropsFromNode(this); 24 | this.observer = new MutationObserver(this._onMutation); 25 | 26 | switch (mode) { 27 | case 'open': 28 | this.targetNode = this.attachShadow({ mode: 'open' }); 29 | break; 30 | case 'element': 31 | this.targetNode = this; 32 | break; 33 | default: 34 | this.targetNode = this.attachShadow({ mode: 'closed' }); 35 | break; 36 | } 37 | 38 | renderReact2Node( 39 | RComponent, 40 | this.props, 41 | this.targetNode, 42 | this._onReactMount 43 | ); 44 | } 45 | 46 | setProps = (newProps: {}) => { 47 | this.props = { ...this.props, ...newProps }; 48 | sendPropsToReact(this.propBridgeRef, this.props); 49 | }; 50 | 51 | _onReactMount = (propBridgeRef: React.RefObject) => { 52 | this.propBridgeRef = propBridgeRef; 53 | this.setProps(this.props); 54 | }; 55 | 56 | _onMutation = (mutationsList: any[]) => { 57 | const newProps = mutationsList.reduce( 58 | ( 59 | props: { [x: string]: string }, 60 | mutation: { type: string; attributeName: string } 61 | ) => { 62 | if (mutation.type === 'attributes') { 63 | const propKey = mutation.attributeName; 64 | props[propKey] = this.getAttribute(propKey); 65 | } 66 | return props; 67 | }, 68 | {} 69 | ); 70 | this.setProps(newProps); 71 | }; 72 | /* 73 | No need to observe child elements here. 74 | Child elements are HTMLCollection which are live by default. 75 | That means when child elements change the HTMLCollection will change automatically. 76 | The reference to HTMLCollection doesnt change, only the items inside it will change. 77 | Hence we should just pass through the children (HTMLCollection) as is on first render to react and forget about it. 78 | */ 79 | 80 | connectedCallback() { 81 | this.observer.observe(this, { attributes: true }); 82 | } 83 | disconnectedCallback() { 84 | // clean up React event handlers and state 85 | ReactDOM.unmountComponentAtNode(this.targetNode); 86 | this.observer.disconnect(); 87 | } 88 | }; 89 | }; 90 | 91 | /* mode options: 92 | - no option (default) - closed shadow DOM 93 | - "open" - open shadow DOM 94 | - "element" - no shadow DOM 95 | */ 96 | export const registerAsWebComponent = ( 97 | component: React.ElementType, 98 | customElementName: string, 99 | mode?: Mode 100 | ) => { 101 | const ReactCustomElement = getCustomElementFromReactComponent( 102 | component, 103 | mode 104 | ); 105 | customElements.define(customElementName, ReactCustomElement); 106 | }; 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-webcomponentify 2 | 3 | [![npm version](https://badge.fury.io/js/react-webcomponentify.svg)](https://badge.fury.io/js/react-webcomponentify) 4 | 5 | logo 6 | 7 | **Build and export React Components as Web Components without any extra effort.** 8 | 9 | Size = [~1.5kB after gzip](https://bundlephobia.com/package/react-webcomponentify) 10 | 11 | _\* works nicely with preact aswell: See demo_ 12 | 13 | ## Show me live demo? 14 | 15 | - Demo Link: 16 | - Demo source code (recommended): 17 | 18 | ### Table of Contents 19 | 20 | - [Use cases](#use-cases) 21 | - [Install](#install) 22 | - [Usage](#usage) 23 | - [Basic](#basic) 24 | - [Advanced](#advanced) 25 | - [Sending non string props to react](#sending-non-string-props-to-react) 26 | - [What about child elements?](#what-about-child-elements) 27 | - [TypeScript support](#typescript-support) 28 | - [Maintainers](#maintainers) 29 | 30 | ## Use cases 31 | 32 | - **Export existing react components as web components** so you can use them with Vue or Angular. 33 | - **Use react's rich api to build web components** with state management, etc. Instruction on how to do exactly that and Live Demo here: 34 | - Lets say you are writing a component library with web components but you already have a huge collection of component in react.You can use this library to generate a component library with the existing components. And then safely continue to rewrite each one of them behind the scene. This makes sure other teams are not waiting for you to finish. 35 | - For more crazy people - You can even export your entire react app as a web component and embed it into another app made with Angular or Vue. So you can keep writing newer parts of code in react while keeping your legacy code working on the side. 36 | - Maybe (not tried) you can embed another old react app (wrapped with this module) inside ur current react app. 37 | 38 | ## Install 39 | 40 | ```bash 41 | npm install react-webcomponentify 42 | ``` 43 | 44 | or 45 | 46 | ```bash 47 | yarn add react-webcomponentify 48 | ``` 49 | 50 | ## Usage 51 | 52 | ### Basic 53 | 54 | **Simple use case** 55 | 56 | ```js 57 | import React from "react"; 58 | import { registerAsWebComponent } from "react-webcomponentify"; 59 | 60 | export const ExampleReactComponent = () => { 61 | return
Hello
; 62 | }; 63 | 64 | registerAsWebComponent(ExampleReactComponent, "example-component"); 65 | ``` 66 | 67 | In HTML: 68 | 69 | ```html 70 | 71 | 72 | .... 73 | 74 | 75 | 76 | .... 77 | 78 | ``` 79 | 80 | ### Advanced 81 | 82 | #### Sending non string props to react 83 | 84 | You can send serializable string props via the html attributes itself. But for props like callback functions or complex objects you can use the `setProps` method on the element as shown below. 85 | 86 | ```js 87 | import React from "react"; 88 | import { registerAsWebComponent } from "react-webcomponentify"; 89 | 90 | export const ButtonComponent = props => { 91 | return ( 92 |
93 | Hello 94 |
95 | ); 96 | }; 97 | 98 | registerAsWebComponent(ButtonComponent, "button-web"); 99 | ``` 100 | 101 | In HTML: 102 | 103 | ```html 104 | 105 | 106 | .... 107 | 108 | 109 | 110 | .... 111 | 117 | 118 | ``` 119 | 120 | Every custom component built using react-webcomponentify will have an instance method `setProps` 121 | 122 | ```js 123 | element.setProps({ 124 | .... 125 | /* set the props here that you want to send to react */ 126 | .... 127 | }) 128 | ``` 129 | 130 | #### What about child elements? 131 | 132 | Thats possible too 😎 133 | 134 | ```js 135 | import React from "react"; 136 | import { registerAsWebComponent } from "react-webcomponentify"; 137 | 138 | // You access the children just like you would in react (using this.props.children) 139 | export const ComponentWithChild = props => { 140 | return ( 141 |
142 | Hello World 143 | {this.props.text} 144 |
{this.props.children}
145 |
146 | ); 147 | }; 148 | 149 | registerAsWebComponent(ComponentWithChild, "component-with-child"); 150 | ``` 151 | 152 | In HTML: 153 | 154 | ```html 155 | 156 | 157 | .... 158 | 159 | 160 |

Some child

161 |
162 | 163 | .... 164 | 165 | ``` 166 | 167 | This will send `

Some Child

` via this.props.children to the React component `ComponentWithChild`. 168 | Note that `

Some Child

` is a dom node and not a react component. So it will be wrapped with a simple react component found here: https://github.com/a7ul/react-webcomponentify/blob/master/src/react-dom-child.js 169 | But for implementation purposed use it like a regular child component. 170 | 171 | ### TypeScript support 172 | 173 | This library is written in TypeScript. All declarations are included. 174 | 175 | ## Maintainers 176 | 177 | 178 | 179 | 180 | 182 | 183 | 184 |

Atul R

181 | Ihor
185 | -------------------------------------------------------------------------------- /dist/build.js: -------------------------------------------------------------------------------- 1 | module.exports=function(e){var t={};function r(o){if(t[o])return t[o].exports;var n=t[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,o){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(o,n,function(t){return e[t]}.bind(null,n));return o},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=3)}([function(e,t){e.exports=require("react")},function(e,t){e.exports=require("react-dom")},function(e,t){function r(){var e,t=this.parentNode,r=arguments.length;if(t)for(r||t.removeChild(this);r--;)"object"!=typeof(e=arguments[r])?e=this.ownerDocument.createTextNode(e):e.parentNode&&e.parentNode.removeChild(e),r?t.insertBefore(this.previousSibling,e):t.replaceChild(e,this)}Element.prototype.replaceWith||(Element.prototype.replaceWith=r),CharacterData.prototype.replaceWith||(CharacterData.prototype.replaceWith=r),DocumentType.prototype.replaceWith||(DocumentType.prototype.replaceWith=r)},function(e,t,r){"use strict";r.r(t);var o=r(1),n=r.n(o),s=r(0),i=r.n(s),a=(r(2),i.a.createRef||function(){var e=function(t){e.current=t};return e(null),e});class c extends i.a.Component{constructor(){super(...arguments),this.ref=a()}componentDidMount(){const e=this.props.children;this.ref.current.replaceWith(...e)}render(){return i.a.createElement("div",{ref:this.ref})}}const u=(e,t,r,o)=>{class s extends i.a.PureComponent{constructor(){super(...arguments),this.state=Object.assign({},t),this.setProps=e=>this.setState(()=>e)}render(){return i.a.createElement(e,Object.assign({},this.props,this.state))}}const c=a();n.a.render(i.a.createElement(s,{ref:c}),r,()=>o(c))};r.d(t,"registerAsWebComponent",(function(){return l}));const p=(e,t)=>class extends HTMLElement{constructor(){switch(super(),this.targetNode=null,this.propBridgeRef=null,this.props={},this.observer=null,this.setProps=e=>{var t,r;this.props=Object.assign(Object.assign({},this.props),e),t=this.propBridgeRef,r=this.props,t&&t.current&&t.current.setProps(r)},this._onReactMount=e=>{this.propBridgeRef=e,this.setProps(this.props)},this._onMutation=e=>{const t=e.reduce((e,t)=>{if("attributes"===t.type){const r=t.attributeName;e[r]=this.getAttribute(r)}return e},{});this.setProps(t)},this.props=(e=>{const t=e.getAttributeNames().reduce((t,r)=>(t[r]=e.getAttribute(r),t),{}),r=Array.from(e.childNodes).map(e=>e.cloneNode(!0));return t.children=i.a.createElement(c,null,r),t})(this),this.observer=new MutationObserver(this._onMutation),t){case"open":this.targetNode=this.attachShadow({mode:"open"});break;case"element":this.targetNode=this;break;default:this.targetNode=this.attachShadow({mode:"closed"})}u(e,this.props,this.targetNode,this._onReactMount)}connectedCallback(){this.observer.observe(this,{attributes:!0})}disconnectedCallback(){n.a.unmountComponentAtNode(this.targetNode),this.observer.disconnect()}},l=(e,t,r)=>{const o=p(e,r);customElements.define(t,o)}}]); 2 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL2V4dGVybmFsIHtcInJvb3RcIjpcIlJlYWN0XCIsXCJjb21tb25qczJcIjpcInJlYWN0XCIsXCJjb21tb25qc1wiOlwicmVhY3RcIixcImFtZFwiOlwicmVhY3RcIixcInVtZFwiOlwicmVhY3RcIn0iLCJ3ZWJwYWNrOi8vL2V4dGVybmFsIHtcInJvb3RcIjpcIlJlYWN0RE9NXCIsXCJjb21tb25qczJcIjpcInJlYWN0LWRvbVwiLFwiY29tbW9uanNcIjpcInJlYWN0LWRvbVwiLFwiYW1kXCI6XCJyZWFjdC1kb21cIixcInVtZFwiOlwicmVhY3QtZG9tXCJ9Iiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jaGlsZC1yZXBsYWNlLXdpdGgtcG9seWZpbGwvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3JlYWN0LWNyZWF0ZS1yZWYvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3JlYWN0LWRvbS1jaGlsZC50c3giLCJ3ZWJwYWNrOi8vLy4vc3JjL3Byb3AtYnJpZGdlLnRzeCIsIndlYnBhY2s6Ly8vLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsInQiLCJtb2RlIiwiX19lc01vZHVsZSIsIm5zIiwiY3JlYXRlIiwia2V5IiwiYmluZCIsIm4iLCJvYmplY3QiLCJwcm9wZXJ0eSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwicCIsInMiLCJyZXF1aXJlIiwiUmVwbGFjZVdpdGhQb2x5ZmlsbCIsImN1cnJlbnROb2RlIiwicGFyZW50IiwidGhpcyIsInBhcmVudE5vZGUiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJyZW1vdmVDaGlsZCIsIm93bmVyRG9jdW1lbnQiLCJjcmVhdGVUZXh0Tm9kZSIsImluc2VydEJlZm9yZSIsInByZXZpb3VzU2libGluZyIsInJlcGxhY2VDaGlsZCIsIkVsZW1lbnQiLCJyZXBsYWNlV2l0aCIsIkNoYXJhY3RlckRhdGEiLCJEb2N1bWVudFR5cGUiLCJjcmVhdGVSZWYiLCJyZWYiLCJfIiwiY3VycmVudCIsIkNvbXBvbmVudCIsImNoaWxkTm9kZXMiLCJwcm9wcyIsImNoaWxkcmVuIiwicmVuZGVyUmVhY3QyTm9kZSIsIlJDb21wb25lbnQiLCJpbml0aWFsUHJvcHMiLCJ0YXJnZXREb21Ob2RlIiwib25SZW5kZXIiLCJQcm9wQnJpZGdlIiwiUHVyZUNvbXBvbmVudCIsInN0YXRlIiwic2V0UHJvcHMiLCJzZXRTdGF0ZSIsInByb3BCcmlkZ2VSZWYiLCJyZW5kZXIiLCJnZXRDdXN0b21FbGVtZW50RnJvbVJlYWN0Q29tcG9uZW50IiwiSFRNTEVsZW1lbnQiLCJzdXBlciIsInRhcmdldE5vZGUiLCJvYnNlcnZlciIsIm5ld1Byb3BzIiwiX29uUmVhY3RNb3VudCIsIl9vbk11dGF0aW9uIiwibXV0YXRpb25zTGlzdCIsInJlZHVjZSIsIm11dGF0aW9uIiwidHlwZSIsInByb3BLZXkiLCJhdHRyaWJ1dGVOYW1lIiwiZ2V0QXR0cmlidXRlIiwibm9kZSIsIm1hcHBlZFByb3BzIiwiZ2V0QXR0cmlidXRlTmFtZXMiLCJBcnJheSIsImZyb20iLCJtYXAiLCJlIiwiY2xvbmVOb2RlIiwiZ2V0UHJvcHNGcm9tTm9kZSIsIk11dGF0aW9uT2JzZXJ2ZXIiLCJhdHRhY2hTaGFkb3ciLCJvYnNlcnZlIiwiYXR0cmlidXRlcyIsInVubW91bnRDb21wb25lbnRBdE5vZGUiLCJkaXNjb25uZWN0IiwicmVnaXN0ZXJBc1dlYkNvbXBvbmVudCIsImNvbXBvbmVudCIsImN1c3Rv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dCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMyk7XG4iLCJtb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoXCJyZWFjdFwiKTsiLCJtb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoXCJyZWFjdC1kb21cIik7IiwiLy8gRnJvbSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvQ2hpbGROb2RlL3JlcGxhY2VXaXRoXG5mdW5jdGlvbiBSZXBsYWNlV2l0aFBvbHlmaWxsKCkge1xuICBcInVzZS1zdHJpY3RcIjsgLy8gRm9yIHNhZmFyaSwgYW5kIElFID4gMTBcbiAgdmFyIHBhcmVudCA9IHRoaXMucGFyZW50Tm9kZSxcbiAgICBpID0gYXJndW1lbnRzLmxlbmd0aCxcbiAgICBjdXJyZW50Tm9kZTtcbiAgaWYgKCFwYXJlbnQpIHJldHVybjtcbiAgaWYgKCFpKVxuICAgIC8vIGlmIHRoZXJlIGFyZSBubyBhcmd1bWVudHNcbiAgICBwYXJlbnQucmVtb3ZlQ2hpbGQodGhpcyk7XG4gIHdoaWxlIChpLS0pIHtcbiAgICAvLyBpLS0gZGVjcmVtZW50cyBpIGFuZCByZXR1cm5zIHRoZSB2YWx1ZSBvZiBpIGJlZm9yZSB0aGUgZGVjcmVtZW50XG4gICAgY3VycmVudE5vZGUgPSBhcmd1bWVudHNbaV07XG4gICAgaWYgKHR5cGVvZiBjdXJyZW50Tm9kZSAhPT0gXCJvYmplY3RcIikge1xuICAgICAgY3VycmVudE5vZGUgPSB0aGlzLm93bmVyRG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoY3VycmVudE5vZGUpO1xuICAgIH0gZWxzZSBpZiAoY3VycmVudE5vZGUucGFyZW50Tm9kZSkge1xuICAgICAgY3VycmVudE5vZGUucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChjdXJyZW50Tm9kZSk7XG4gICAgfVxuICAgIC8vIHRoZSB2YWx1ZSBvZiBcImlcIiBiZWxvdyBpcyBhZnRlciB0aGUgZGVjcmVtZW50XG4gICAgaWYgKCFpKVxuICAgICAgLy8gaWYgY3VycmVudE5vZGUgaXMgdGhlIGZpcnN0IGFyZ3VtZW50IChjdXJyZW50Tm9kZSA9PT0gYXJndW1lbnRzWzBdKVxuICAgICAgcGFyZW50LnJlcGxhY2VDaGlsZChjdXJyZW50Tm9kZSwgdGhpcyk7XG4gICAgLy8gaWYgY3VycmVudE5vZGUgaXNuJ3QgdGhlIGZpcnN0XG4gICAgZWxzZSBwYXJlbnQuaW5zZXJ0QmVmb3JlKHRoaXMucHJldmlvdXNTaWJsaW5nLCBjdXJyZW50Tm9kZSk7XG4gIH1cbn1cblxuaWYgKCFFbGVtZW50LnByb3RvdHlwZS5yZXBsYWNlV2l0aClcbiAgRWxlbWVudC5wcm90b3R5cGUucmVwbGFjZVdpdGggPSBSZXBsYWNlV2l0aFBvbHlmaWxsO1xuaWYgKCFDaGFyYWN0ZXJEYXRhLnByb3RvdHlwZS5yZXBsYWNlV2l0aClcbiAgQ2hhcmFjdGVyRGF0YS5wcm90b3R5cGUucmVwbGFjZVdpdGggPSBSZXBsYWNlV2l0aFBvbHlmaWxsO1xuaWYgKCFEb2N1bWVudFR5cGUucHJvdG90eXBlLnJlcGxhY2VXaXRoKVxuICBEb2N1bWVudFR5cGUucHJvdG90eXBlLnJlcGxhY2VXaXRoID0gUmVwbGFjZVdpdGhQb2x5ZmlsbDtcbiIsImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcclxuXHJcbmV4cG9ydCBkZWZhdWx0IFJlYWN0LmNyZWF0ZVJlZiB8fCBmdW5jdGlvbiBjcmVhdGVSZWYoKSB7XHJcbiAgdmFyIHJlZiA9IGZ1bmN0aW9uKF8pIHsgcmVmLmN1cnJlbnQgPSBfIH1cclxuICByZWYobnVsbClcclxuICByZXR1cm4gcmVmXHJcbn1cclxuIiwiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0JztcbmltcG9ydCAnY2hpbGQtcmVwbGFjZS13aXRoLXBvbHlmaWxsJztcbmltcG9ydCBjcmVhdGVSZWYgZnJvbSAncmVhY3QtY3JlYXRlLXJlZic7XG5cbi8qXG4gV3JhcHBlciBjbGFzcyB0byB3cmFwIHRoZSByYXcgaHRtbCBub2RlcyBpbiBhIFxuIHJlYWN0IGNvbXBvbmVudCBiZWZvcmUgcGFzc2luZyBpdCBkb3duIHRvIHJlYWN0IGFzIGNoaWxkcmVuXG4qL1xuZXhwb3J0IGNsYXNzIFJlYWN0RG9tQ2hpbGQgZXh0ZW5kcyBSZWFjdC5Db21wb25lbnQge1xuICByZWYgPSBjcmVhdGVSZWY8SFRNTERpdkVsZW1lbnQ+KCk7XG4gIGNvbXBvbmVudERpZE1vdW50KCkge1xuICAgIGNvbnN0IGNoaWxkTm9kZXM6IE5vZGVbXSA9IHRoaXMucHJvcHMuY2hpbGRyZW4gYXMgTm9kZVtdO1xuICAgIHRoaXMucmVmLmN1cnJlbnQucmVwbGFjZVdpdGgoLi4uY2hpbGROb2Rlcyk7XG4gIH1cbiAgcmVuZGVyKCkge1xuICAgIHJldHVybiA8ZGl2IHJlZj17dGhpcy5yZWZ9IC8+O1xuICB9XG59XG4iLCJpbXBvcnQgUmVhY3RET00gZnJvbSAncmVhY3QtZG9tJztcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCc7XG5pbXBvcnQgeyBSZWFjdERvbUNoaWxkIH0gZnJvbSAnLi9yZWFjdC1kb20tY2hpbGQnO1xuaW1wb3J0IGNyZWF0ZVJlZiBmcm9tICdyZWFjdC1jcmVhdGUtcmVmJztcblxuLypcblByb3BCcmlkZ2Ugc3RvcmVzIHByb3BzIHBhc3NlZCB0byBpdCB2aWEgc2V0UHJvcHMgaW4gdGhlIHN0YXRlLlxuQW5kIHRoZW4gcGFzc2VzIHRob3NlIHRvIHRoZSBjb21wb25lbnQgYXMgcmVndWxhciBwcm9wc1xuSGVuY2Ugd2hlbiB5b3UgY2FsbCBzZXRQcm9wcyB5b3UgYXJlIGNhbGxpbmcgc2V0U3RhdGVcbmFuZCB0aGVuIHBhc3NpbmcgdGhvc2UgYXMgcHJvcHMgdG8gdGhlIHJlYWN0IGNvbXBvbmVudC5cbiovXG5cbmV4cG9ydCBjb25zdCByZW5kZXJSZWFjdDJOb2RlID0gKFxuICBSQ29tcG9uZW50OiBSZWFjdC5FbGVtZW50VHlwZSxcbiAgaW5pdGlhbFByb3BzOiB7fSxcbiAgdGFyZ2V0RG9tTm9kZTogRWxlbWVudCB8IERvY3VtZW50RnJhZ21lbnQsXG4gIG9uUmVuZGVyOiAocmVmOiBSZWFjdC5SZWZPYmplY3Q8YW55PikgPT4gdm9pZFxuKSA9PiB7XG4gIGNsYXNzIFByb3BCcmlkZ2UgZXh0ZW5kcyBSZWFjdC5QdXJlQ29tcG9uZW50IHtcbiAgICBzdGF0ZSA9IHsgLi4uaW5pdGlhbFByb3BzIH07XG4gICAgc2V0UHJvcHMgPSAocHJvcHM6IFJlYWN0LlJlZk9iamVjdDxQcm9wQnJpZGdlPikgPT5cbiAgICAgIHRoaXMuc2V0U3RhdGUoKCkgPT4gcHJvcHMpO1xuICAgIHJlbmRlcigpIHtcbiAgICAgIHJldHVybiA8UkNvbXBvbmVudCB7Li4udGhpcy5wcm9wc30gey4uLnRoaXMuc3RhdGV9IC8+O1xuICAgIH1cbiAgfVxuICBjb25zdCBwcm9wQnJpZGdlUmVmID0gY3JlYXRlUmVmPFByb3BCcmlkZ2U+KCk7XG4gIFJlYWN0RE9NLnJlbmRlcig8UHJvcEJyaWRnZSByZWY9e3Byb3BCcmlkZ2VSZWZ9IC8+LCB0YXJnZXREb21Ob2RlLCAoKSA9PlxuICAgIG9uUmVuZGVyKHByb3BCcmlkZ2VSZWYpXG4gICk7XG59O1xuXG5leHBvcnQgY29uc3Qgc2VuZFByb3BzVG9SZWFjdCA9IChcbiAgcHJvcEJyaWRnZVJlZjogUmVhY3QuUmVmT2JqZWN0PGFueT4sXG4gIHByb3BzOiBhbnlcbikgPT4ge1xuICBpZiAocHJvcEJyaWRnZVJlZiAmJiBwcm9wQnJpZGdlUmVmLmN1cnJlbnQpIHtcbiAgICBwcm9wQnJpZGdlUmVmLmN1cnJlbnQuc2V0UHJvcHMocHJvcHMpO1xuICB9XG59O1xuXG5leHBvcnQgY29uc3QgZ2V0UHJvcHNGcm9tTm9kZSA9IChub2RlOiBIVE1MRWxlbWVudCkgPT4ge1xuICBjb25zdCBhdHRyaWJ1dGVOYW1lcyA9IG5vZGUuZ2V0QXR0cmlidXRlTmFtZXMoKTtcbiAgY29uc3QgbWFwcGVkUHJvcHMgPSBhdHRyaWJ1dGVOYW1lcy5yZWR1Y2UoXG4gICAgKHByb3BzOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB8IEpTWC5FbGVtZW50IH0sIG5hbWU6IHN0cmluZykgPT4ge1xuICAgICAgcHJvcHNbbmFtZV0gPSBub2RlLmdldEF0dHJpYnV0ZShuYW1lKTtcbiAgICAgIHJldHVybiBwcm9wcztcbiAgICB9LFxuICAgIHt9XG4gICk7XG5cbiAgY29uc3QgY2hpbGRyZW4gPSBBcnJheS5mcm9tKG5vZGUuY2hpbGROb2RlcykubWFwKChlKSA9PiBlLmNsb25lTm9kZSh0cnVlKSk7XG4gIG1hcHBlZFByb3BzLmNoaWxkcmVuID0gPFJlYWN0RG9tQ2hpbGQ+e2NoaWxkcmVufTwvUmVhY3REb21DaGlsZD47XG4gIHJldHVybiBtYXBwZWRQcm9wcztcbn07XG4iLCJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnO1xuaW1wb3J0IFJlYWN0RE9NIGZyb20gXCJyZWFjdC1kb21cIjtcbmltcG9ydCB7XG4gIHJlbmRlclJlYWN0Mk5vZGUsXG4gIGdldFByb3BzRnJvbU5vZGUsXG4gIHNlbmRQcm9wc1RvUmVhY3QsXG59IGZyb20gJy4vcHJvcC1icmlkZ2UnO1xuXG50eXBlIE1vZGUgPSAnb3BlbicgfCAnZWxlbWVudCc7XG5cbmNvbnN0IGdldEN1c3RvbUVsZW1lbnRGcm9tUmVhY3RDb21wb25lbnQgPSAoXG4gIFJDb21wb25lbnQ6IFJlYWN0LkVsZW1lbnRUeXBlLFxuICBtb2RlPzogTW9kZVxuKSA9PiB7XG4gIHJldHVybiBjbGFzcyBSZWFjdEFzQ3VzdG9tRWxlbWVudCBleHRlbmRzIEhUTUxFbGVtZW50IHtcbiAgICB0YXJnZXROb2RlOiB0aGlzIHwgU2hhZG93Um9vdCA9IG51bGw7XG4gICAgcHJvcEJyaWRnZVJlZjogYW55ID0gbnVsbDtcbiAgICBwcm9wcyA9IHt9O1xuICAgIG9ic2VydmVyOiBNdXRhdGlvbk9ic2VydmVyID0gbnVsbDtcblxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgc3VwZXIoKTtcbiAgICAgIHRoaXMucHJvcHMgPSBnZXRQcm9wc0Zyb21Ob2RlKHRoaXMpO1xuICAgICAgdGhpcy5vYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKHRoaXMuX29uTXV0YXRpb24pO1xuXG4gICAgICBzd2l0Y2ggKG1vZGUpIHtcbiAgICAgICAgY2FzZSAnb3Blbic6XG4gICAgICAgICAgdGhpcy50YXJnZXROb2RlID0gdGhpcy5hdHRhY2hTaGFkb3coeyBtb2RlOiAnb3BlbicgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2VsZW1lbnQnOlxuICAgICAgICAgIHRoaXMudGFyZ2V0Tm9kZSA9IHRoaXM7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhpcy50YXJnZXROb2RlID0gdGhpcy5hdHRhY2hTaGFkb3coeyBtb2RlOiAnY2xvc2VkJyB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgcmVuZGVyUmVhY3QyTm9kZShcbiAgICAgICAgUkNvbXBvbmVudCxcbiAgICAgICAgdGhpcy5wcm9wcyxcbiAgICAgICAgdGhpcy50YXJnZXROb2RlLFxuICAgICAgICB0aGlzLl9vblJlYWN0TW91bnRcbiAgICAgICk7XG4gICAgfVxuXG4gICAgc2V0UHJvcHMgPSAobmV3UHJvcHM6IHt9KSA9PiB7XG4gICAgICB0aGlzLnByb3BzID0geyAuLi50aGlzLnByb3BzLCAuLi5uZXdQcm9wcyB9O1xuICAgICAgc2VuZFByb3BzVG9SZWFjdCh0aGlzLnByb3BCcmlkZ2VSZWYsIHRoaXMucHJvcHMpO1xuICAgIH07XG5cbiAgICBfb25SZWFjdE1vdW50ID0gKHByb3BCcmlkZ2VSZWY6IFJlYWN0LlJlZk9iamVjdDxhbnk+KSA9PiB7XG4gICAgICB0aGlzLnByb3BCcmlkZ2VSZWYgPSBwcm9wQnJpZGdlUmVmO1xuICAgICAgdGhpcy5zZXRQcm9wcyh0aGlzLnByb3BzKTtcbiAgICB9O1xuXG4gICAgX29uTXV0YXRpb24gPSAobXV0YXRpb25zTGlzdDogYW55W10pID0+IHtcbiAgICAgIGNvbnN0IG5ld1Byb3BzID0gbXV0YXRpb25zTGlzdC5yZWR1Y2UoXG4gICAgICAgIChcbiAgICAgICAgICBwcm9wczogeyBbeDogc3RyaW5nXTogc3RyaW5nIH0sXG4gICAgICAgICAgbXV0YXRpb246IHsgdHlwZTogc3RyaW5nOyBhdHRyaWJ1dGVOYW1lOiBzdHJpbmcgfVxuICAgICAgICApID0+IHtcbiAgICAgICAgICBpZiAobXV0YXRpb24udHlwZSA9PT0gJ2F0dHJpYnV0ZXMnKSB7XG4gICAgICAgICAgICBjb25zdCBwcm9wS2V5ID0gbXV0YXRpb24uYXR0cmlidXRlTmFtZTtcbiAgICAgICAgICAgIHByb3BzW3Byb3BLZXldID0gdGhpcy5nZXRBdHRyaWJ1dGUocHJvcEtleSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBwcm9wcztcbiAgICAgICAgfSxcbiAgICAgICAge31cbiAgICAgICk7XG4gICAgICB0aGlzLnNldFByb3BzKG5ld1Byb3BzKTtcbiAgICB9O1xuICAgIC8qXG4gICAgICAgIE5vIG5lZWQgdG8gb2JzZXJ2ZSBjaGlsZCBlbGVtZW50cyBoZXJlLlxuICAgICAgICBDaGlsZCBlbGVtZW50cyBhcmUgSFRNTENvbGxlY3Rpb24gd2hpY2ggYXJlIGxpdmUgYnkgZGVmYXVsdC4gXG4gICAgICAgIFRoYXQgbWVhbnMgd2hlbiBjaGlsZCBlbGVtZW50cyBjaGFuZ2UgdGhlIEhUTUxDb2xsZWN0aW9uIHdpbGwgY2hhbmdlIGF1dG9tYXRpY2FsbHkuXG4gICAgICAgIFRoZSByZWZlcmVuY2UgdG8gSFRNTENvbGxlY3Rpb24gZG9lc250IGNoYW5nZSwgb25seSB0aGUgaXRlbXMgaW5zaWRlIGl0IHdpbGwgY2hhbmdlLlxuICAgICAgICBIZW5jZSB3ZSBzaG91bGQganVzdCBwYXNzIHRocm91Z2ggdGhlIGNoaWxkcmVuIChIVE1MQ29sbGVjdGlvbikgYXMgaXMgb24gZmlyc3QgcmVuZGVyIHRvIHJlYWN0IGFuZCBmb3JnZXQgYWJvdXQgaXQuXG4gICAgKi9cblxuICAgIGNvbm5lY3RlZENhbGxiYWNrKCkge1xuICAgICAgdGhpcy5vYnNlcnZlci5vYnNlcnZlKHRoaXMsIHsgYXR0cmlidXRlczogdHJ1ZSB9KTtcbiAgICB9XG4gICAgZGlzY29ubmVjdGVkQ2FsbGJhY2soKSB7XG4gICAgICAvLyBjbGVhbiB1cCBSZWFjdCBldmVudCBoYW5kbGVycyBhbmQgc3RhdGVcbiAgICAgIFJlYWN0RE9NLnVubW91bnRDb21wb25lbnRBdE5vZGUodGhpcy50YXJnZXROb2RlKTtcbiAgICAgIHRoaXMub2JzZXJ2ZXIuZGlzY29ubmVjdCgpO1xuICAgIH1cbiAgfTtcbn07XG5cbi8qIG1vZGUgb3B0aW9uczpcbiAgLSBubyBvcHRpb24gKGRlZmF1bHQpIC0gY2xvc2VkIHNoYWRvdyBET01cbiAgLSBcIm9wZW5cIiAtIG9wZW4gc2hhZG93IERPTVxuICAtIFwiZWxlbWVudFwiIC0gbm8gc2hhZG93IERPTVxuKi9cbmV4cG9ydCBjb25zdCByZWdpc3RlckFzV2ViQ29tcG9uZW50ID0gKFxuICBjb21wb25lbnQ6IFJlYWN0LkVsZW1lbnRUeXBlLFxuICBjdXN0b21FbGVtZW50TmFtZTogc3RyaW5nLFxuICBtb2RlPzogTW9kZVxuKSA9PiB7XG4gIGNvbnN0IFJlYWN0Q3VzdG9tRWxlbWVudCA9IGdldEN1c3RvbUVsZW1lbnRGcm9tUmVhY3RDb21wb25lbnQoXG4gICAgY29tcG9uZW50LFxuICAgIG1vZGVcbiAgKTtcbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKGN1c3RvbUVsZW1lbnROYW1lLCBSZWFjdEN1c3RvbUVsZW1lbnQpO1xufTtcbiJdLCJzb3VyY2VSb290IjoiIn0= --------------------------------------------------------------------------------