UNPKG

45.5 kBJavaScriptView Raw
1"use strict";
2
3function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
4
5function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
7function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
9const fs = require('fs');
10
11const path = require('path');
12
13const util = require('util');
14
15const I18N = require('@ladjs/i18n');
16
17const _ = require('lodash');
18
19const consolidate = require('consolidate');
20
21const debug = require('debug')('email-templates');
22
23const getPaths = require('get-paths');
24
25const htmlToText = require('html-to-text');
26
27const juice = require('juice');
28
29const nodemailer = require('nodemailer');
30
31const previewEmail = require('preview-email'); // promise version of `juice.juiceResources`
32
33
34const juiceResources = (html, options) => {
35 return new Promise((resolve, reject) => {
36 juice.juiceResources(html, options, (err, html) => {
37 if (err) return reject(err);
38 resolve(html);
39 });
40 });
41};
42
43const env = (process.env.NODE_ENV || 'development').toLowerCase();
44const stat = util.promisify(fs.stat);
45const readFile = util.promisify(fs.readFile);
46
47class Email {
48 constructor(config = {}) {
49 debug('config passed %O', config); // 2.x backwards compatible support
50
51 if (config.juiceOptions) {
52 config.juiceResources = config.juiceOptions;
53 delete config.juiceOptions;
54 }
55
56 if (config.disableJuice) {
57 config.juice = false;
58 delete config.disableJuice;
59 }
60
61 if (config.render) {
62 config.customRender = true;
63 }
64
65 this.config = _.merge({
66 views: {
67 // directory where email templates reside
68 root: path.resolve('emails'),
69 options: {
70 // default file extension for template
71 extension: 'pug',
72 map: {
73 hbs: 'handlebars',
74 njk: 'nunjucks'
75 },
76 engineSource: consolidate
77 },
78 // locals to pass to templates for rendering
79 locals: {
80 // turn on caching for non-development environments
81 cache: !['development', 'test'].includes(env),
82 // pretty is automatically set to `false` for subject/text
83 pretty: true
84 }
85 },
86 // <https://nodemailer.com/message/>
87 message: {},
88 send: !['development', 'test'].includes(env),
89 preview: env === 'development',
90 // <https://github.com/ladjs/i18n>
91 // set to an object to configure and enable it
92 i18n: false,
93 // pass a custom render function if necessary
94 render: this.render.bind(this),
95 customRender: false,
96 // force text-only rendering of template (disregards template folder)
97 textOnly: false,
98 // <https://github.com/werk85/node-html-to-text>
99 htmlToText: {
100 ignoreImage: true
101 },
102 subjectPrefix: false,
103 // <https://github.com/Automattic/juice>
104 juice: true,
105 // Override juice global settings <https://github.com/Automattic/juice#juicecodeblockss>
106 juiceSettings: {
107 tableElements: ['TABLE']
108 },
109 juiceResources: {
110 preserveImportant: true,
111 webResources: {
112 relativeTo: path.resolve('build'),
113 images: false
114 }
115 },
116 // pass a transport configuration object or a transport instance
117 // (e.g. an instance is created via `nodemailer.createTransport`)
118 // <https://nodemailer.com/transports/>
119 transport: {},
120 // last locale field name (also used by @ladjs/i18n)
121 lastLocaleField: 'last_locale',
122
123 getPath(type, template) {
124 return path.join(template, type);
125 }
126
127 }, config); // override existing method
128
129 this.render = this.config.render;
130 if (!_.isFunction(this.config.transport.sendMail)) this.config.transport = nodemailer.createTransport(this.config.transport); // Override juice global settings https://github.com/Automattic/juice#juicecodeblocks
131
132 if (_.isObject(this.config.juiceSettings)) {
133 for (const [key, value] of Object.entries(this.config.juiceSettings)) {
134 juice[key] = value;
135 }
136 }
137
138 debug('transformed config %O', this.config);
139 this.juiceResources = this.juiceResources.bind(this);
140 this.getTemplatePath = this.getTemplatePath.bind(this);
141 this.templateExists = this.templateExists.bind(this);
142 this.checkAndRender = this.checkAndRender.bind(this);
143 this.render = this.render.bind(this);
144 this.renderAll = this.renderAll.bind(this);
145 this.send = this.send.bind(this);
146 } // shorthand use of `juiceResources` with the config
147 // (mainly for custom renders like from a database)
148
149
150 juiceResources(html, juiceRenderResources = {}) {
151 const juiceR = _.merge(this.config.juiceResources, juiceRenderResources);
152
153 return juiceResources(html, juiceR);
154 } // a simple helper function that gets the actual file path for the template
155
156
157 async getTemplatePath(template) {
158 let juiceRenderResources = {};
159
160 if (_.isObject(template)) {
161 juiceRenderResources = template.juiceResources;
162 template = template.path;
163 }
164
165 const [root, view] = path.isAbsolute(template) ? [path.dirname(template), path.basename(template)] : [this.config.views.root, template];
166 const paths = await getPaths(root, view, this.config.views.options.extension);
167 const filePath = path.resolve(root, paths.rel);
168 return {
169 filePath,
170 paths,
171 juiceRenderResources
172 };
173 } // returns true or false if a template exists
174 // (uses same look-up approach as `render` function)
175
176
177 async templateExists(view) {
178 try {
179 const {
180 filePath
181 } = await this.getTemplatePath(view);
182 const stats = await stat(filePath);
183 if (!stats.isFile()) throw new Error(`${filePath} was not a file`);
184 return true;
185 } catch (err) {
186 debug('templateExists', err);
187 return false;
188 }
189 }
190
191 async checkAndRender(type, template, locals) {
192 let juiceRenderResources = {};
193
194 if (_.isObject(template)) {
195 juiceRenderResources = template.juiceResources;
196 template = template.path;
197 }
198
199 const string = this.config.getPath(type, template, locals);
200
201 if (!this.config.customRender) {
202 const exists = await this.templateExists(string);
203 if (!exists) return;
204 }
205
206 return this.render(string, _objectSpread(_objectSpread({}, locals), type === 'html' ? {} : {
207 pretty: false
208 }), juiceRenderResources);
209 } // promise version of consolidate's render
210 // inspired by koa-views and re-uses the same config
211 // <https://github.com/queckezz/koa-views>
212
213
214 async render(view, locals = {}) {
215 const {
216 map,
217 engineSource
218 } = this.config.views.options;
219 const {
220 filePath,
221 paths,
222 juiceRenderResources
223 } = await this.getTemplatePath(view);
224
225 if (paths.ext === 'html' && !map) {
226 const res = await readFile(filePath, 'utf8');
227 return res;
228 }
229
230 const engineName = map && map[paths.ext] ? map[paths.ext] : paths.ext;
231 const renderFn = engineSource[engineName];
232 if (!engineName || !renderFn) throw new Error(`Engine not found for the ".${paths.ext}" file extension`);
233
234 if (_.isObject(this.config.i18n)) {
235 if (this.config.i18n.lastLocaleField && this.config.lastLocaleField && this.config.i18n.lastLocaleField !== this.config.lastLocaleField) throw new Error(`The 'lastLocaleField' (String) option for @ladjs/i18n and email-templates do not match, i18n value was ${this.config.i18n.lastLocaleField} and email-templates value was ${this.config.lastLocaleField}`);
236 const i18n = new I18N(_objectSpread(_objectSpread({}, this.config.i18n), {}, {
237 register: locals
238 })); // support `locals.user.last_locale` (variable based name lastLocaleField)
239 // (e.g. for <https://lad.js.org>)
240
241 if (_.isObject(locals.user) && _.isString(locals.user[this.config.lastLocaleField])) locals.locale = locals.user[this.config.lastLocaleField];
242 if (_.isString(locals.locale)) i18n.setLocale(locals.locale);
243 }
244
245 const res = await util.promisify(renderFn)(filePath, locals); // transform the html with juice using remote paths
246 // google now supports media queries
247 // https://developers.google.com/gmail/design/reference/supported_css
248
249 if (!this.config.juice) return res;
250 const html = await this.juiceResources(res, juiceRenderResources);
251 return html;
252 } // eslint-disable-next-line complexity
253
254
255 async renderAll(template, locals = {}, nodemailerMessage = {}) {
256 const message = _objectSpread({}, nodemailerMessage);
257
258 if (template && (!message.subject || !message.html || !message.text)) {
259 const [subject, html, text] = await Promise.all(['subject', 'html', 'text'].map(type => this.checkAndRender(type, template, locals)));
260 if (subject && !message.subject) message.subject = subject;
261 if (html && !message.html) message.html = html;
262 if (text && !message.text) message.text = text;
263 }
264
265 if (message.subject && this.config.subjectPrefix) message.subject = this.config.subjectPrefix + message.subject; // trim subject
266
267 if (message.subject) message.subject = message.subject.trim();
268 if (this.config.htmlToText && message.html && !message.text) // we'd use nodemailer-html-to-text plugin
269 // but we really don't need to support cid
270 // <https://github.com/andris9/nodemailer-html-to-text>
271 message.text = htmlToText.fromString(message.html, this.config.htmlToText); // if we only want a text-based version of the email
272
273 if (this.config.textOnly) delete message.html; // if no subject, html, or text content exists then we should
274 // throw an error that says at least one must be found
275 // otherwise the email would be blank (defeats purpose of email-templates)
276
277 if ((!_.isString(message.subject) || _.isEmpty(_.trim(message.subject))) && (!_.isString(message.text) || _.isEmpty(_.trim(message.text))) && (!_.isString(message.html) || _.isEmpty(_.trim(message.html))) && _.isEmpty(message.attachments)) throw new Error(`No content was passed for subject, html, text, nor attachments message props. Check that the files for the template "${template}" exist.`);
278 return message;
279 }
280
281 async send(options = {}) {
282 options = _objectSpread({
283 template: '',
284 message: {},
285 locals: {}
286 }, options);
287 let {
288 template,
289 message,
290 locals
291 } = options;
292 const attachments = message.attachments || this.config.message.attachments || [];
293 message = _.defaultsDeep({}, _.omit(message, 'attachments'), _.omit(this.config.message, 'attachments'));
294 locals = _.defaultsDeep({}, this.config.views.locals, locals);
295 if (attachments) message.attachments = attachments;
296 debug('template %s', template);
297 debug('message %O', message);
298 debug('locals (keys only): %O', Object.keys(locals)); // get all available templates
299
300 const object = await this.renderAll(template, locals, message); // assign the object variables over to the message
301
302 Object.assign(message, object);
303
304 if (this.config.preview) {
305 debug('using `preview-email` to preview email');
306 await (_.isObject(this.config.preview) ? previewEmail(message, this.config.preview) : previewEmail(message));
307 }
308
309 if (!this.config.send) {
310 debug('send disabled so we are ensuring JSONTransport'); // <https://github.com/nodemailer/nodemailer/issues/798>
311 // if (this.config.transport.name !== 'JSONTransport')
312
313 this.config.transport = nodemailer.createTransport({
314 jsonTransport: true
315 });
316 }
317
318 const res = await this.config.transport.sendMail(message);
319 debug('message sent');
320 res.originalMessage = message;
321 return res;
322 }
323
324}
325
326module.exports = Email;
327//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6WyJmcyIsInJlcXVpcmUiLCJwYXRoIiwidXRpbCIsIkkxOE4iLCJfIiwiY29uc29saWRhdGUiLCJkZWJ1ZyIsImdldFBhdGhzIiwiaHRtbFRvVGV4dCIsImp1aWNlIiwibm9kZW1haWxlciIsInByZXZpZXdFbWFpbCIsImp1aWNlUmVzb3VyY2VzIiwiaHRtbCIsIm9wdGlvbnMiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImVyciIsImVudiIsInByb2Nlc3MiLCJOT0RFX0VOViIsInRvTG93ZXJDYXNlIiwic3RhdCIsInByb21pc2lmeSIsInJlYWRGaWxlIiwiRW1haWwiLCJjb25zdHJ1Y3RvciIsImNvbmZpZyIsImp1aWNlT3B0aW9ucyIsImRpc2FibGVKdWljZSIsInJlbmRlciIsImN1c3RvbVJlbmRlciIsIm1lcmdlIiwidmlld3MiLCJyb290IiwiZXh0ZW5zaW9uIiwibWFwIiwiaGJzIiwibmprIiwiZW5naW5lU291cmNlIiwibG9jYWxzIiwiY2FjaGUiLCJpbmNsdWRlcyIsInByZXR0eSIsIm1lc3NhZ2UiLCJzZW5kIiwicHJldmlldyIsImkxOG4iLCJiaW5kIiwidGV4dE9ubHkiLCJpZ25vcmVJbWFnZSIsInN1YmplY3RQcmVmaXgiLCJqdWljZVNldHRpbmdzIiwidGFibGVFbGVtZW50cyIsInByZXNlcnZlSW1wb3J0YW50Iiwid2ViUmVzb3VyY2VzIiwicmVsYXRpdmVUbyIsImltYWdlcyIsInRyYW5zcG9ydCIsImxhc3RMb2NhbGVGaWVsZCIsImdldFBhdGgiLCJ0eXBlIiwidGVtcGxhdGUiLCJqb2luIiwiaXNGdW5jdGlvbiIsInNlbmRNYWlsIiwiY3JlYXRlVHJhbnNwb3J0IiwiaXNPYmplY3QiLCJrZXkiLCJ2YWx1ZSIsIk9iamVjdCIsImVudHJpZXMiLCJnZXRUZW1wbGF0ZVBhdGgiLCJ0ZW1wbGF0ZUV4aXN0cyIsImNoZWNrQW5kUmVuZGVyIiwicmVuZGVyQWxsIiwianVpY2VSZW5kZXJSZXNvdXJjZXMiLCJqdWljZVIiLCJ2aWV3IiwiaXNBYnNvbHV0ZSIsImRpcm5hbWUiLCJiYXNlbmFtZSIsInBhdGhzIiwiZmlsZVBhdGgiLCJyZWwiLCJzdGF0cyIsImlzRmlsZSIsIkVycm9yIiwic3RyaW5nIiwiZXhpc3RzIiwiZXh0IiwicmVzIiwiZW5naW5lTmFtZSIsInJlbmRlckZuIiwicmVnaXN0ZXIiLCJ1c2VyIiwiaXNTdHJpbmciLCJsb2NhbGUiLCJzZXRMb2NhbGUiLCJub2RlbWFpbGVyTWVzc2FnZSIsInN1YmplY3QiLCJ0ZXh0IiwiYWxsIiwidHJpbSIsImZyb21TdHJpbmciLCJpc0VtcHR5IiwiYXR0YWNobWVudHMiLCJkZWZhdWx0c0RlZXAiLCJvbWl0Iiwia2V5cyIsIm9iamVjdCIsImFzc2lnbiIsImpzb25UcmFuc3BvcnQiLCJvcmlnaW5hbE1lc3NhZ2UiLCJtb2R1bGUiLCJleHBvcnRzIl0sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQUFBLE1BQU1BLEVBQUUsR0FBR0MsT0FBTyxDQUFDLElBQUQsQ0FBbEI7O0FBQ0EsTUFBTUMsSUFBSSxHQUFHRCxPQUFPLENBQUMsTUFBRCxDQUFwQjs7QUFDQSxNQUFNRSxJQUFJLEdBQUdGLE9BQU8sQ0FBQyxNQUFELENBQXBCOztBQUVBLE1BQU1HLElBQUksR0FBR0gsT0FBTyxDQUFDLGFBQUQsQ0FBcEI7O0FBQ0EsTUFBTUksQ0FBQyxHQUFHSixPQUFPLENBQUMsUUFBRCxDQUFqQjs7QUFDQSxNQUFNSyxXQUFXLEdBQUdMLE9BQU8sQ0FBQyxhQUFELENBQTNCOztBQUNBLE1BQU1NLEtBQUssR0FBR04sT0FBTyxDQUFDLE9BQUQsQ0FBUCxDQUFpQixpQkFBakIsQ0FBZDs7QUFDQSxNQUFNTyxRQUFRLEdBQUdQLE9BQU8sQ0FBQyxXQUFELENBQXhCOztBQUNBLE1BQU1RLFVBQVUsR0FBR1IsT0FBTyxDQUFDLGNBQUQsQ0FBMUI7O0FBQ0EsTUFBTVMsS0FBSyxHQUFHVCxPQUFPLENBQUMsT0FBRCxDQUFyQjs7QUFDQSxNQUFNVSxVQUFVLEdB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QUtuQyxTQUFMLENBQWViLFFBQWYsRUFBeUJ0QixNQUF6QixFQUFpQ0ksT0FBakMsQ0FBckIsQ0EzQnVCLENBNkJ2Qjs7QUFDQTBCLElBQUFBLE1BQU0sQ0FBQ3lDLE1BQVAsQ0FBY25FLE9BQWQsRUFBdUJrRSxNQUF2Qjs7QUFFQSxRQUFJLEtBQUtuRixNQUFMLENBQVltQixPQUFoQixFQUF5QjtBQUN2QnpDLE1BQUFBLEtBQUssQ0FBQyx3Q0FBRCxDQUFMO0FBQ0EsYUFBT0YsQ0FBQyxDQUFDZ0UsUUFBRixDQUFXLEtBQUt4QyxNQUFMLENBQVltQixPQUF2QixJQUNIcEMsWUFBWSxDQUFDa0MsT0FBRCxFQUFVLEtBQUtqQixNQUFMLENBQVltQixPQUF0QixDQURULEdBRUhwQyxZQUFZLENBQUNrQyxPQUFELENBRmhCO0FBR0Q7O0FBRUQsUUFBSSxDQUFDLEtBQUtqQixNQUFMLENBQVlrQixJQUFqQixFQUF1QjtBQUNyQnhDLE1BQUFBLEtBQUssQ0FBQyxnREFBRCxDQUFMLENBRHFCLENBRXJCO0FBQ0E7O0FBQ0EsV0FBS3NCLE1BQUwsQ0FBWStCLFNBQVosR0FBd0JqRCxVQUFVLENBQUN5RCxlQUFYLENBQTJCO0FBQ2pEOEMsUUFBQUEsYUFBYSxFQUFFO0FBRGtDLE9BQTNCLENBQXhCO0FBR0Q7O0FBRUQsVUFBTXJCLEdBQUcsR0FBRyxNQUFNLEtBQUtoRSxNQUFMLENBQVkrQixTQUFaLENBQXNCTyxRQUF0QixDQUErQnJCLE9BQS9CLENBQWxCO0FBQ0F2QyxJQUFBQSxLQUFLLENBQUMsY0FBRCxDQUFMO0FBQ0FzRixJQUFBQSxHQUFHLENBQUNzQixlQUFKLEdBQXNCckUsT0FBdEI7QUFDQSxXQUFPK0MsR0FBUDtBQUNEOztBQXZVUzs7QUEwVVp1QixNQUFNLENBQUNDLE9BQVAsR0FBaUIxRixLQUFqQiIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTtcbmNvbnN0IHBhdGggPSByZXF1aXJlKCdwYXRoJyk7XG5jb25zdCB1dGlsID0gcmVxdWlyZSgndXRpbCcpO1xuXG5jb25zdCBJMThOID0gcmVxdWlyZSgnQGxhZGpzL2kxOG4nKTtcbmNvbnN0IF8gPSByZXF1aXJlKCdsb2Rhc2gnKTtcbmNvbnN0IGNvbnNvbGlkYXRlID0gcmVxdWlyZSgnY29uc29saWRhdGUnKTtcbmNvbnN0IGRlYnVnID0gcmVxdWlyZSgnZGVidWcnKSgnZW1haWwtdGVtcGxhdGVzJyk7XG5jb25zdCBnZXRQYXRocyA9IHJlcXVpcmUoJ2dldC1wYXRocycpO1xuY29uc3QgaHRtbFRvVGV4dCA9IHJlcXVpcmUoJ2h0bWwtdG8tdGV4dCcpO1xuY29uc3QganVpY2UgPSByZXF1aXJlKCdqdWljZScpO1xuY29uc3Qgbm9kZW1haWxlciA9IHJlcXVpcmUoJ25vZGVtYWlsZXInKTtcbmNvbnN0IHByZXZpZXdFbWFpbCA9IHJlcXVpcmUoJ3ByZXZpZXctZW1haWwnKTtcblxuLy8gcHJvbWlzZSB2ZXJzaW9uIG9mIGBqdWljZS5qdWljZVJlc291cmNlc2BcbmNvbnN0IGp1aWNlUmVzb3VyY2VzID0gKGh0bWwsIG9wdGlvbnMpID0+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBqdWljZS5qdWljZVJlc291cmNlcyhodG1sLCBvcHRpb25zLCAoZXJyLCBodG1sKSA9PiB7XG4gICAgICBpZiAoZXJyKSByZXR1cm4gcmVqZWN0KGVycik7XG4gICAgICByZXNvbHZlKGh0bWwpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbmNvbnN0IGVudiA9IChwcm9jZXNzLmVudi5OT0RFX0VOViB8fCAnZGV2ZWxvcG1lbnQnKS50b0xvd2VyQ2FzZSgpO1xuY29uc3Qgc3RhdCA9IHV0aWwucHJvbWlzaWZ5KGZzLnN0YXQpO1xuY29uc3QgcmVhZEZpbGUgPSB1dGlsLnByb21pc2lmeShmcy5yZWFkRmlsZSk7XG5cbmNsYXNzIEVtYWlsIHtcbiAgY29uc3RydWN0b3IoY29uZmlnID0ge30pIHtcbiAgICBkZWJ1ZygnY29uZmlnIHBhc3NlZCAlTycsIGNvbmZpZyk7XG5cbiAgICAvLyAyLnggYmFja3dhcmRzIGNvbXBhdGlibGUgc3VwcG9ydFxuICAgIGlmIChjb25maWcuanVpY2VPcHRpb25zKSB7XG4gICAgICBjb25maWcuanVpY2VSZXNvdXJjZXMgPSBjb25maWcuanVpY2VPcHRpb25zO1xuICAgICAgZGVsZXRlIGNvbmZpZy5qdWljZU9wdGlvbnM7XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpZy5kaXNhYmxlSnVpY2UpIHtcbiAgICAgIGNvbmZpZy5qdWljZSA9IGZhbHNlO1xuICAgICAgZGVsZXRlIGNvbmZpZy5kaXNhYmxlSnVpY2U7XG4gICAgfVxuXG4gICAgaWYgKGNvbmZpZy5yZW5kZXIpIHtcbiAgICAgIGNvbmZpZy5jdXN0b21SZW5kZXIgPSB0cnVlO1xuICAgIH1cblxuICAgIHRoaXMuY29uZmlnID0gXy5tZXJnZShcbiAgICAgIHtcbiAgICAgICAgdmlld3M6IHtcbiAgICAgICAgICAvLyBkaXJlY3Rvcnkgd2hlcmUgZW1haWwgdGVtcGxhdGVzIHJlc2lkZVxuICAgICAgICAgIHJvb3Q6IHBhdGgucmVzb2x2ZSgnZW1haWxzJyksXG4gICAgICAgICAgb3B0aW9uczoge1xuICAgICAgICAgICAgLy8gZGVmYXVsdCBmaWxlIGV4dGVuc2lvbiBmb3IgdGVtcGxhdGVcbiAgICAgICAgICAgIGV4dGVuc2lvbjogJ3B1ZycsXG4gICAgICAgICAgICBtYXA6IHtcbiAgICAgICAgICAgICAgaGJzOiAnaGFuZGxlYmFycycsXG4gICAgICAgICAgICAgIG5qazogJ251bmp1Y2tzJ1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGVuZ2luZVNvdXJjZTogY29uc29saWRhdGVcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vIGxvY2FscyB0byBwYXNzIHRvIHRlbXBsYXRlcyBmb3IgcmVuZGVyaW5nXG4gICAgICAgICAgbG9jYWxzOiB7XG4gICAgICAgICAgICAvLyB0dXJuIG9uIGNhY2hpbmcgZm9yIG5vbi1kZXZlbG9wbWVudCBlbnZpcm9ubWVudHNcbiAgICAgICAgICAgIGNhY2hlOiAhWydkZXZlbG9wbWVudCcsICd0ZXN0J10uaW5jbHVkZXMoZW52KSxcbiAgICAgICAgICAgIC8vIHByZXR0eSBpcyBhdXRvbWF0aWNhbGx5IHNldCB0byBgZmFsc2VgIGZvciBzdWJqZWN0L3RleHRcbiAgICAgICAgICAgIHByZXR0eTogdHJ1ZVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gPGh0dHBzOi8vbm9kZW1haWxlci5jb20vbWVzc2FnZS8+XG4gICAgICAgIG1lc3NhZ2U6IHt9LFxuICAgICAgICBzZW5kOiAhWydkZXZlbG9wbWVudCcsICd0ZXN0J10uaW5jbHVkZXMoZW52KSxcbiAgICAgICAgcHJldmlldzogZW52ID09PSAnZGV2ZWxvcG1lbnQnLFxuICAgICAgICAvLyA8aHR0cHM6Ly9naXRodWIuY29tL2xhZGpzL2kxOG4+XG4gICAgICAgIC8vIHNldCB0byBhbiBvYmplY3QgdG8gY29uZmlndXJlIGFuZCBlbmFibGUgaXRcbiAgICAgICAgaTE4bjogZmFsc2UsXG4gICAgICAgIC8vIHBhc3MgYSBjdXN0b20gcmVuZGVyIGZ1bmN0aW9uIGlmIG5lY2Vzc2FyeVxuICAgICAgICByZW5kZXI6IHRoaXMucmVuZGVyLmJpbmQodGhpcyksXG4gICAgICAgIGN1c3RvbVJlbmRlcjogZmFsc2UsXG4gICAgICAgIC8vIGZvcmNlIHRleHQtb25seSByZW5kZXJpbmcgb2YgdGVtcGxhdGUgKGRpc3JlZ2FyZHMgdGVtcGxhdGUgZm9sZGVyKVxuICAgICAgICB0ZXh0T25seTogZmFsc2UsXG4gICAgICAgIC8vIDxodHRwczovL2dpdGh1Yi5jb20vd2Vyazg1L25vZGUtaHRtbC10by10ZXh0PlxuICAgICAgICBodG1sVG9UZXh0OiB7XG4gICAgICAgICAgaWdub3JlSW1hZ2U6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgc3ViamVjdFByZWZpeDogZmFsc2UsXG4gICAgICAgIC8vIDxodHRwczovL2dpdGh1Yi5jb20vQXV0b21hdHRpYy9qdWljZT5cbiAgICAgICAganVpY2U6IHRydWUsXG4gICAgICAgIC8vIE92ZXJyaWRlIGp1aWNlIGdsb2JhbCBzZXR0aW5ncyA8aHR0cHM6Ly9naXRodWIuY29tL0F1dG9tYXR0aWMvanVpY2UjanVpY2Vjb2RlYmxvY2tzcz5cbiAgICAgICAganVpY2VTZXR0aW5nczoge1xuICAgICAgICAgIHRhYmxlRWxlbWVudHM6IFsnVEFCTEUnXVxuICAgICAgICB9LFxuICAgICAgICBqdWljZVJlc291cmNlczoge1xuICAgICAgICAgIHByZXNlcnZlSW1wb3J0YW50OiB0cnVlLFxuICAgICAgICAgIHdlYlJlc291cmNlczoge1xuICAgICAgICAgICAgcmVsYXRpdmVUbzogcGF0aC5yZXNvbHZlKCdidWlsZCcpLFxuICAgICAgICAgICAgaW1hZ2VzOiBmYWxzZVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gcGFzcyBhIHRyYW5zcG9ydCBjb25maWd1cmF0aW9uIG9iamVjdCBvciBhIHRyYW5zcG9ydCBpbnN0YW5jZVxuICAgICAgICAvLyAoZS5nLiBhbiBpbnN0YW5jZSBpcyBjcmVhdGVkIHZpYSBgbm9kZW1haWxlci5jcmVhdGVUcmFuc3BvcnRgKVxuICAgICAgICAvLyA8aHR0cHM6Ly9ub2RlbWFpbGVyLmNvbS90cmFuc3BvcnRzLz5cbiAgICAgICAgdHJhbnNwb3J0OiB7fSxcbiAgICAgICAgLy8gbGFzdCBsb2NhbGUgZmllbGQgbmFtZSAoYWxzbyB1c2VkIGJ5IEBsYWRqcy9pMThuKVxuICAgICAgICBsYXN0TG9jYWxlRmllbGQ6ICdsYXN0X2xvY2FsZScsXG4gICAgICAgIGdldFBhdGgodHlwZSwgdGVtcGxhdGUpIHtcbiAgICAgICAgICByZXR1cm4gcGF0aC5qb2luKHRlbXBsYXRlLCB0eXBlKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGNvbmZpZ1xuICAgICk7XG5cbiAgICAvLyBvdmVycmlkZSBleGlzdGluZyBtZXRob2RcbiAgICB0aGlzLnJlbmRlciA9IHRoaXMuY29uZmlnLnJlbmRlcjtcblxuICAgIGlmICghXy5pc0Z1bmN0aW9uKHRoaXMuY29uZmlnLnRyYW5zcG9ydC5zZW5kTWFpbCkpXG4gICAgICB0aGlzLmNvbmZpZy50cmFuc3BvcnQgPSBub2RlbWFpbGVyLmNyZWF0ZVRyYW5zcG9ydCh0aGlzLmNvbmZpZy50cmFuc3BvcnQpO1xuXG4gICAgLy8gT3ZlcnJpZGUganVpY2UgZ2xvYmFsIHNldHRpbmdzIGh0dHBzOi8vZ2l0aHViLmNvbS9BdXRvbWF0dGljL2p1aWNlI2p1aWNlY29kZWJsb2Nrc1xuICAgIGlmIChfLmlzT2JqZWN0KHRoaXMuY29uZmlnLmp1aWNlU2V0dGluZ3MpKSB7XG4gICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh0aGlzLmNvbmZpZy5qdWljZVNldHRpbmdzKSkge1xuICAgICAgICBqdWljZVtrZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZGVidWcoJ3RyYW5zZm9ybWVkIGNvbmZpZyAlTycsIHRoaXMuY29uZmlnKTtcblxuICAgIHRoaXMuanVpY2VSZXNvdXJjZXMgPSB0aGlzLmp1aWNlUmVzb3VyY2VzLmJpbmQodGhpcyk7XG4gICAgdGhpcy5nZXRUZW1wbGF0ZVBhdGggPSB0aGlzLmdldFRlbXBsYXRlUGF0aC5iaW5kKHRoaXMpO1xuICAgIHRoaXMudGVtcGxhdGVFeGlzdHMgPSB0aGlzLnRlbXBsYXRlRXhpc3RzLmJpbmQodGhpcyk7XG4gICAgdGhpcy5jaGVja0FuZFJlbmRlciA9IHRoaXMuY2hlY2tBbmRSZW5kZXIuYmluZCh0aGlzKTtcbiAgICB0aGlzLnJlbmRlciA9IHRoaXMucmVuZGVyLmJpbmQodGhpcyk7XG4gICAgdGhpcy5yZW5kZXJBbGwgPSB0aGlzLnJlbmRlckFsbC5iaW5kKHRoaXMpO1xuICAgIHRoaXMuc2VuZCA9IHRoaXMuc2VuZC5iaW5kKHRoaXMpO1xuICB9XG5cbiAgLy8gc2hvcnRoYW5kIHVzZSBvZiBganVpY2VSZXNvdXJjZXNgIHdpdGggdGhlIGNvbmZpZ1xuICAvLyAobWFpbmx5IGZvciBjdXN0b20gcmVuZGVycyBsaWtlIGZyb20gYSBkYXRhYmFzZSlcbiAganVpY2VSZXNvdXJjZXMoaHRtbCwganVpY2VSZW5kZXJSZXNvdXJjZXMgPSB7fSkge1xuICAgIGNvbnN0IGp1aWNlUiA9IF8ubWVyZ2UodGhpcy5jb25maWcuanVpY2VSZXNvdXJjZXMsIGp1aWNlUmVuZGVyUmVzb3VyY2VzKTtcbiAgICByZXR1cm4ganVpY2VSZXNvdXJjZXMoaHRtbCwganVpY2VSKTtcbiAgfVxuXG4gIC8vIGEgc2ltcGxlIGhlbHBlciBmdW5jdGlvbiB0aGF0IGdldHMgdGhlIGFjdHVhbCBmaWxlIHBhdGggZm9yIHRoZSB0ZW1wbGF0ZVxuICBhc3luYyBnZXRUZW1wbGF0ZVBhdGgodGVtcGxhdGUpIHtcbiAgICBsZXQganVpY2VSZW5kZXJSZXNvdXJjZXMgPSB7fTtcblxuICAgIGlmIChfLmlzT2JqZWN0KHRlbXBsYXRlKSkge1xuICAgICAganVpY2VSZW5kZXJSZXNvdXJjZXMgPSB0ZW1wbGF0ZS5qdWljZVJlc291cmNlcztcbiAgICAgIHRlbXBsYXRlID0gdGVtcGxhdGUucGF0aDtcbiAgICB9XG5cbiAgICBjb25zdCBbcm9vdCwgdmlld10gPSBwYXRoLmlzQWJzb2x1dGUodGVtcGxhdGUpXG4gICAgICA/IFtwYXRoLmRpcm5hbWUodGVtcGxhdGUpLCBwYXRoLmJhc2VuYW1lKHRlbXBsYXRlKV1cbiAgICAgIDogW3RoaXMuY29uZmlnLnZpZXdzLnJvb3QsIHRlbXBsYXRlXTtcbiAgICBjb25zdCBwYXRocyA9IGF3YWl0IGdldFBhdGhzKFxuICAgICAgcm9vdCxcbiAgICAgIHZpZXcsXG4gICAgICB0aGlzLmNvbmZpZy52aWV3cy5vcHRpb25zLmV4dGVuc2lvblxuICAgICk7XG4gICAgY29uc3QgZmlsZVBhdGggPSBwYXRoLnJlc29sdmUocm9vdCwgcGF0aHMucmVsKTtcbiAgICByZXR1cm4geyBmaWxlUGF0aCwgcGF0aHMsIGp1aWNlUmVuZGVyUmVzb3VyY2VzIH07XG4gIH1cblxuICAvLyByZXR1cm5zIHRydWUgb3IgZmFsc2UgaWYgYSB0ZW1wbGF0ZSBleGlzdHNcbiAgLy8gKHVzZXMgc2FtZSBsb29rLXVwIGFwcHJvYWNoIGFzIGByZW5kZXJgIGZ1bmN0aW9uKVxuICBhc3luYyB0ZW1wbGF0ZUV4aXN0cyh2aWV3KSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgZmlsZVBhdGggfSA9IGF3YWl0IHRoaXMuZ2V0VGVtcGxhdGVQYXRoKHZpZXcpO1xuICAgICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBzdGF0KGZpbGVQYXRoKTtcbiAgICAgIGlmICghc3RhdHMuaXNGaWxlKCkpIHRocm93IG5ldyBFcnJvcihgJHtmaWxlUGF0aH0gd2FzIG5vdCBhIGZpbGVgKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgZGVidWcoJ3RlbXBsYXRlRXhpc3RzJywgZXJyKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBjaGVja0FuZFJlbmRlcih0eXBlLCB0ZW1wbGF0ZSwgbG9jYWxzKSB7XG4gICAgbGV0IGp1aWNlUmVuZGVyUmVzb3VyY2VzID0ge307XG5cbiAgICBpZiAoXy5pc09iamVjdCh0ZW1wbGF0ZSkpIHtcbiAgICAgIGp1aWNlUmVuZGVyUmVzb3VyY2VzID0gdGVtcGxhdGUuanVpY2VSZXNvdXJjZXM7XG4gICAgICB0ZW1wbGF0ZSA9IHRlbXBsYXRlLnBhdGg7XG4gICAgfVxuXG4gICAgY29uc3Qgc3RyaW5nID0gdGhpcy5jb25maWcuZ2V0UGF0aCh0eXBlLCB0ZW1wbGF0ZSwgbG9jYWxzKTtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmN1c3RvbVJlbmRlcikge1xuICAgICAgY29uc3QgZXhpc3RzID0gYXdhaXQgdGhpcy50ZW1wbGF0ZUV4aXN0cyhzdHJpbmcpO1xuICAgICAgaWYgKCFleGlzdHMpIHJldHVybjtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5yZW5kZXIoXG4gICAgICBzdHJpbmcsXG4gICAgICB7XG4gICAgICAgIC4uLmxvY2FscyxcbiAgICAgICAgLi4uKHR5cGUgPT09ICdodG1sJyA/IHt9IDogeyBwcmV0dHk6IGZhbHNlIH0pXG4gICAgICB9LFxuICAgICAganVpY2VSZW5kZXJSZXNvdXJjZXNcbiAgICApO1xuICB9XG5cbiAgLy8gcHJvbWlzZSB2ZXJzaW9uIG9mIGNvbnNvbGlkYXRlJ3MgcmVuZGVyXG4gIC8vIGluc3BpcmVkIGJ5IGtvYS12aWV3cyBhbmQgcmUtdXNlcyB0aGUgc2FtZSBjb25maWdcbiAgLy8gPGh0dHBzOi8vZ2l0aHViLmNvbS9xdWVja2V6ei9rb2Etdmlld3M+XG4gIGFzeW5jIHJlbmRlcih2aWV3LCBsb2NhbHMgPSB7fSkge1xuICAgIGNvbnN0IHsgbWFwLCBlbmdpbmVTb3VyY2UgfSA9IHRoaXMuY29uZmlnLnZpZXdzLm9wdGlvbnM7XG4gICAgY29uc3QgeyBmaWxlUGF0aCwgcGF0aHMsIGp1aWNlUmVuZGVyUmVzb3VyY2VzIH0gPVxuICAgICAgYXdhaXQgdGhpcy5nZXRUZW1wbGF0ZVBhdGgodmlldyk7XG4gICAgaWYgKHBhdGhzLmV4dCA9PT0gJ2h0bWwnICYmICFtYXApIHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHJlYWRGaWxlKGZpbGVQYXRoLCAndXRmOCcpO1xuICAgICAgcmV0dXJuIHJlcztcbiAgICB9XG5cbiAgICBjb25zdCBlbmdpbmVOYW1lID0gbWFwICYmIG1hcFtwYXRocy5leHRdID8gbWFwW3BhdGhzLmV4dF0gOiBwYXRocy5leHQ7XG4gICAgY29uc3QgcmVuZGVyRm4gPSBlbmdpbmVTb3VyY2VbZW5naW5lTmFtZV07XG4gICAgaWYgKCFlbmdpbmVOYW1lIHx8ICFyZW5kZXJGbilcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYEVuZ2luZSBub3QgZm91bmQgZm9yIHRoZSBcIi4ke3BhdGhzLmV4dH1cIiBmaWxlIGV4dGVuc2lvbmBcbiAgICAgICk7XG5cbiAgICBpZiAoXy5pc09iamVjdCh0aGlzLmNvbmZpZy5pMThuKSkge1xuICAgICAgaWYgKFxuICAgICAgICB0aGlzLmNvbmZpZy5pMThuLmxhc3RMb2NhbGVGaWVsZCAmJlxuICAgICAgICB0aGlzLmNvbmZpZy5sYXN0TG9jYWxlRmllbGQgJiZcbiAgICAgICAgdGhpcy5jb25maWcuaTE4bi5sYXN0TG9jYWxlRmllbGQgIT09IHRoaXMuY29uZmlnLmxhc3RMb2NhbGVGaWVsZFxuICAgICAgKVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYFRoZSAnbGFzdExvY2FsZUZpZWxkJyAoU3RyaW5nKSBvcHRpb24gZm9yIEBsYWRqcy9pMThuIGFuZCBlbWFpbC10ZW1wbGF0ZXMgZG8gbm90IG1hdGNoLCBpMThuIHZhbHVlIHdhcyAke3RoaXMuY29uZmlnLmkxOG4ubGFzdExvY2FsZUZpZWxkfSBhbmQgZW1haWwtdGVtcGxhdGVzIHZhbHVlIHdhcyAke3RoaXMuY29uZmlnLmxhc3RMb2NhbGVGaWVsZH1gXG4gICAgICAgICk7XG5cbiAgICAgIGNvbnN0IGkxOG4gPSBuZXcgSTE4Tih7IC4uLnRoaXMuY29uZmlnLmkxOG4sIHJlZ2lzdGVyOiBsb2NhbHMgfSk7XG5cbiAgICAgIC8vIHN1cHBvcnQgYGxvY2Fscy51c2VyLmxhc3RfbG9jYWxlYCAodmFyaWFibGUgYmFzZWQgbmFtZSBsYXN0TG9jYWxlRmllbGQpXG4gICAgICAvLyAoZS5nLiBmb3IgPGh0dHBzOi8vbGFkLmpzLm9yZz4pXG4gICAgICBpZiAoXG4gICAgICAgIF8uaXNPYmplY3QobG9jYWxzLnVzZXIpICYmXG4gICAgICAgIF8uaXNTdHJpbmcobG9jYWxzLnVzZXJbdGhpcy5jb25maWcubGFzdExvY2FsZUZpZWxkXSlcbiAgICAgIClcbiAgICAgICAgbG9jYWxzLmxvY2FsZSA9IGxvY2Fscy51c2VyW3RoaXMuY29uZmlnLmxhc3RMb2NhbGVGaWVsZF07XG5cbiAgICAgIGlmIChfLmlzU3RyaW5nKGxvY2Fscy5sb2NhbGUpKSBpMThuLnNldExvY2FsZShsb2NhbHMubG9jYWxlKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXMgPSBhd2FpdCB1dGlsLnByb21pc2lmeShyZW5kZXJGbikoZmlsZVBhdGgsIGxvY2Fscyk7XG4gICAgLy8gdHJhbnNmb3JtIHRoZSBodG1sIHdpdGgganVpY2UgdXNpbmcgcmVtb3RlIHBhdGhzXG4gICAgLy8gZ29vZ2xlIG5vdyBzdXBwb3J0cyBtZWRpYSBxdWVyaWVzXG4gICAgLy8gaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vZ21haWwvZGVzaWduL3JlZmVyZW5jZS9zdXBwb3J0ZWRfY3NzXG4gICAgaWYgKCF0aGlzLmNvbmZpZy5qdWljZSkgcmV0dXJuIHJlcztcbiAgICBjb25zdCBodG1sID0gYXdhaXQgdGhpcy5qdWljZVJlc291cmNlcyhyZXMsIGp1aWNlUmVuZGVyUmVzb3VyY2VzKTtcbiAgICByZXR1cm4gaHRtbDtcbiAgfVxuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gIGFzeW5jIHJlbmRlckFsbCh0ZW1wbGF0ZSwgbG9jYWxzID0ge30sIG5vZGVtYWlsZXJNZXNzYWdlID0ge30pIHtcbiAgICBjb25zdCBtZXNzYWdlID0geyAuLi5ub2RlbWFpbGVyTWVzc2FnZSB9O1xuXG4gICAgaWYgKHRlbXBsYXRlICYmICghbWVzc2FnZS5zdWJqZWN0IHx8ICFtZXNzYWdlLmh0bWwgfHwgIW1lc3NhZ2UudGV4dCkpIHtcbiAgICAgIGNvbnN0IFtzdWJqZWN0LCBodG1sLCB0ZXh0XSA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBbJ3N1YmplY3QnLCAnaHRtbCcsICd0ZXh0J10ubWFwKCh0eXBlKSA9PlxuICAgICAgICAgIHRoaXMuY2hlY2tBbmRSZW5kZXIodHlwZSwgdGVtcGxhdGUsIGxvY2FscylcbiAgICAgICAgKVxuICAgICAgKTtcblxuICAgICAgaWYgKHN1YmplY3QgJiYgIW1lc3NhZ2Uuc3ViamVjdCkgbWVzc2FnZS5zdWJqZWN0ID0gc3ViamVjdDtcbiAgICAgIGlmIChodG1sICYmICFtZXNzYWdlLmh0bWwpIG1lc3NhZ2UuaHRtbCA9IGh0bWw7XG4gICAgICBpZiAodGV4dCAmJiAhbWVzc2FnZS50ZXh0KSBtZXNzYWdlLnRleHQgPSB0ZXh0O1xuICAgIH1cblxuICAgIGlmIChtZXNzYWdlLnN1YmplY3QgJiYgdGhpcy5jb25maWcuc3ViamVjdFByZWZpeClcbiAgICAgIG1lc3NhZ2Uuc3ViamVjdCA9IHRoaXMuY29uZmlnLnN1YmplY3RQcmVmaXggKyBtZXNzYWdlLnN1YmplY3Q7XG5cbiAgICAvLyB0cmltIHN1YmplY3RcbiAgICBpZiAobWVzc2FnZS5zdWJqZWN0KSBtZXNzYWdlLnN1YmplY3QgPSBtZXNzYWdlLnN1YmplY3QudHJpbSgpO1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLmh0bWxUb1RleHQgJiYgbWVzc2FnZS5odG1sICYmICFtZXNzYWdlLnRleHQpXG4gICAgICAvLyB3ZSdkIHVzZSBub2RlbWFpbGVyLWh0bWwtdG8tdGV4dCBwbHVnaW5cbiAgICAgIC8vIGJ1dCB3ZSByZWFsbHkgZG9uJ3QgbmVlZCB0byBzdXBwb3J0IGNpZFxuICAgICAgLy8gPGh0dHBzOi8vZ2l0aHViLmNvbS9hbmRyaXM5L25vZGVtYWlsZXItaHRtbC10by10ZXh0PlxuICAgICAgbWVzc2FnZS50ZXh0ID0gaHRtbFRvVGV4dC5mcm9tU3RyaW5nKFxuICAgICAgICBtZXNzYWdlLmh0bWwsXG4gICAgICAgIHRoaXMuY29uZmlnLmh0bWxUb1RleHRcbiAgICAgICk7XG5cbiAgICAvLyBpZiB3ZSBvbmx5IHdhbnQgYSB0ZXh0LWJhc2VkIHZlcnNpb24gb2YgdGhlIGVtYWlsXG4gICAgaWYgKHRoaXMuY29uZmlnLnRleHRPbmx5KSBkZWxldGUgbWVzc2FnZS5odG1sO1xuXG4gICAgLy8gaWYgbm8gc3ViamVjdCwgaHRtbCwgb3IgdGV4dCBjb250ZW50IGV4aXN0cyB0aGVuIHdlIHNob3VsZFxuICAgIC8vIHRocm93IGFuIGVycm9yIHRoYXQgc2F5cyBhdCBsZWFzdCBvbmUgbXVzdCBiZSBmb3VuZFxuICAgIC8vIG90aGVyd2lzZSB0aGUgZW1haWwgd291bGQgYmUgYmxhbmsgKGRlZmVhdHMgcHVycG9zZSBvZiBlbWFpbC10ZW1wbGF0ZXMpXG4gICAgaWYgKFxuICAgICAgKCFfLmlzU3RyaW5nKG1lc3NhZ2Uuc3ViamVjdCkgfHwgXy5pc0VtcHR5KF8udHJpbShtZXNzYWdlLnN1YmplY3QpKSkgJiZcbiAgICAgICghXy5pc1N0cmluZyhtZXNzYWdlLnRleHQpIHx8IF8uaXNFbXB0eShfLnRyaW0obWVzc2FnZS50ZXh0KSkpICYmXG4gICAgICAoIV8uaXNTdHJpbmcobWVzc2FnZS5odG1sKSB8fCBfLmlzRW1wdHkoXy50cmltKG1lc3NhZ2UuaHRtbCkpKSAmJlxuICAgICAgXy5pc0VtcHR5KG1lc3NhZ2UuYXR0YWNobWVudHMpXG4gICAgKVxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgTm8gY29udGVudCB3YXMgcGFzc2VkIGZvciBzdWJqZWN0LCBodG1sLCB0ZXh0LCBub3IgYXR0YWNobWVudHMgbWVzc2FnZSBwcm9wcy4gQ2hlY2sgdGhhdCB0aGUgZmlsZXMgZm9yIHRoZSB0ZW1wbGF0ZSBcIiR7dGVtcGxhdGV9XCIgZXhpc3QuYFxuICAgICAgKTtcblxuICAgIHJldHVybiBtZXNzYWdlO1xuICB9XG5cbiAgYXN5bmMgc2VuZChvcHRpb25zID0ge30pIHtcbiAgICBvcHRpb25zID0ge1xuICAgICAgdGVtcGxhdGU6ICcnLFxuICAgICAgbWVzc2FnZToge30sXG4gICAgICBsb2NhbHM6IHt9LFxuICAgICAgLi4ub3B0aW9uc1xuICAgIH07XG5cbiAgICBsZXQgeyB0ZW1wbGF0ZSwgbWVzc2FnZSwgbG9jYWxzIH0gPSBvcHRpb25zO1xuXG4gICAgY29uc3QgYXR0YWNobWVudHMgPVxuICAgICAgbWVzc2FnZS5hdHRhY2htZW50cyB8fCB0aGlzLmNvbmZpZy5tZXNzYWdlLmF0dGFjaG1lbnRzIHx8IFtdO1xuXG4gICAgbWVzc2FnZSA9IF8uZGVmYXVsdHNEZWVwKFxuICAgICAge30sXG4gICAgICBfLm9taXQobWVzc2FnZSwgJ2F0dGFjaG1lbnRzJyksXG4gICAgICBfLm9taXQodGhpcy5jb25maWcubWVzc2FnZSwgJ2F0dGFjaG1lbnRzJylcbiAgICApO1xuICAgIGxvY2FscyA9IF8uZGVmYXVsdHNEZWVwKHt9LCB0aGlzLmNvbmZpZy52aWV3cy5sb2NhbHMsIGxvY2Fscyk7XG5cbiAgICBpZiAoYXR0YWNobWVudHMpIG1lc3NhZ2UuYXR0YWNobWVudHMgPSBhdHRhY2htZW50cztcblxuICAgIGRlYnVnKCd0ZW1wbGF0ZSAlcycsIHRlbXBsYXRlKTtcbiAgICBkZWJ1ZygnbWVzc2FnZSAlTycsIG1lc3NhZ2UpO1xuICAgIGRlYnVnKCdsb2NhbHMgKGtleXMgb25seSk6ICVPJywgT2JqZWN0LmtleXMobG9jYWxzKSk7XG5cbiAgICAvLyBnZXQgYWxsIGF2YWlsYWJsZSB0ZW1wbGF0ZXNcbiAgICBjb25zdCBvYmplY3QgPSBhd2FpdCB0aGlzLnJlbmRlckFsbCh0ZW1wbGF0ZSwgbG9jYWxzLCBtZXNzYWdlKTtcblxuICAgIC8vIGFzc2lnbiB0aGUgb2JqZWN0IHZhcmlhYmxlcyBvdmVyIHRvIHRoZSBtZXNzYWdlXG4gICAgT2JqZWN0LmFzc2lnbihtZXNzYWdlLCBvYmplY3QpO1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLnByZXZpZXcpIHtcbiAgICAgIGRlYnVnKCd1c2luZyBgcHJldmlldy1lbWFpbGAgdG8gcHJldmlldyBlbWFpbCcpO1xuICAgICAgYXdhaXQgKF8uaXNPYmplY3QodGhpcy5jb25maWcucHJldmlldylcbiAgICAgICAgPyBwcmV2aWV3RW1haWwobWVzc2FnZSwgdGhpcy5jb25maWcucHJldmlldylcbiAgICAgICAgOiBwcmV2aWV3RW1haWwobWVzc2FnZSkpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5jb25maWcuc2VuZCkge1xuICAgICAgZGVidWcoJ3NlbmQgZGlzYWJsZWQgc28gd2UgYXJlIGVuc3VyaW5nIEpTT05UcmFuc3BvcnQnKTtcbiAgICAgIC8vIDxodHRwczovL2dpdGh1Yi5jb20vbm9kZW1haWxlci9ub2RlbWFpbGVyL2lzc3Vlcy83OTg+XG4gICAgICAvLyBpZiAodGhpcy5jb25maWcudHJhbnNwb3J0Lm5hbWUgIT09ICdKU09OVHJhbnNwb3J0JylcbiAgICAgIHRoaXMuY29uZmlnLnRyYW5zcG9ydCA9IG5vZGVtYWlsZXIuY3JlYXRlVHJhbnNwb3J0KHtcbiAgICAgICAganNvblRyYW5zcG9ydDogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5jb25maWcudHJhbnNwb3J0LnNlbmRNYWlsKG1lc3NhZ2UpO1xuICAgIGRlYnVnKCdtZXNzYWdlIHNlbnQnKTtcbiAgICByZXMub3JpZ2luYWxNZXNzYWdlID0gbWVzc2FnZTtcbiAgICByZXR1cm4gcmVzO1xuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gRW1haWw7XG4iXX0=
\No newline at end of file