UNPKG

25 kBJavaScriptView Raw
1/** Copyright (c) 2018 Uber Technologies, Inc.
2 *
3 * This source code is licensed under the MIT license found in the
4 * LICENSE file in the root directory of this source tree.
5 *
6 *
7 */
8
9/* eslint-env node */
10import { Locale } from 'locale';
11import { createPlugin, memoize, html } from 'fusion-core';
12import { UniversalEventsToken } from 'fusion-plugin-universal-events';
13import bodyparser from 'koa-bodyparser';
14import querystring from 'querystring';
15import { I18nLoaderToken } from './tokens.js';
16import createLoader from './loader.js';
17// exported for testing
18export function matchesLiteralSections(literalSections) {
19 return translation => {
20 let lastMatchIndex = 0;
21
22 if (literalSections.length === 1) {
23 const literal = literalSections[0];
24 return literal !== '' && translation === literal;
25 }
26
27 return literalSections.every((literal, literalIndex) => {
28 if (literal === '') {
29 // literal section either:
30 // - starts/ends the literal
31 // - is the result of two adjacent interpolations
32 return true;
33 } else if (literalIndex === 0 && translation.startsWith(literal)) {
34 lastMatchIndex += literal.length;
35 return true;
36 } else if (literalIndex === literalSections.length - 1 && translation.endsWith(literal)) {
37 return true;
38 } else {
39 // start search from `lastMatchIndex`
40 const matchIndex = translation.indexOf(literal, lastMatchIndex);
41
42 if (matchIndex !== -1) {
43 lastMatchIndex = matchIndex + literal.length;
44 return true;
45 }
46 } // matching failed
47
48
49 return false;
50 });
51 };
52}
53
54function getKeysFromContext(ctx) {
55 if (ctx.request.body && Array.isArray(ctx.request.body)) {
56 return ctx.request.body;
57 }
58
59 const querystringParams = querystring.parse(ctx.querystring);
60
61 if (querystringParams.keys) {
62 try {
63 const keys = JSON.parse(querystringParams.keys);
64 return Array.isArray(keys) ? keys : [];
65 } catch (e) {
66 return [];
67 }
68 }
69
70 return [];
71}
72
73const pluginFactory = () => createPlugin({
74 deps: {
75 loader: I18nLoaderToken.optional,
76 events: UniversalEventsToken.optional
77 },
78 provides: ({
79 loader,
80 events
81 }) => {
82 class I18n {
83 constructor(ctx) {
84 if (!loader) {
85 loader = createLoader();
86 }
87
88 const {
89 translations,
90 locale
91 } = loader.from(ctx);
92 this.emitter = events && events.from(ctx);
93 this.translations = translations;
94 this.locale = locale;
95 }
96
97 async load() {} //mirror client API
98
99
100 translate(key, interpolations = {}) {
101 const template = this.translations[key];
102
103 if (typeof template !== 'string') {
104 this.emitter && this.emitter.emit('i18n-translate-miss', {
105 key
106 });
107 return key;
108 }
109
110 return template.replace(/\${(.*?)}/g, (_, k) => interpolations[k] === void 0 ? '${' + k + '}' : String(interpolations[k]));
111 }
112
113 }
114
115 const service = {
116 from: memoize(ctx => new I18n(ctx))
117 };
118 return service;
119 },
120 middleware: (_, plugin) => {
121 // TODO(#4) refactor: this currently depends on babel plugins in framework's webpack config.
122 // Ideally these babel plugins should be part of this package, not hard-coded in framework core
123 const chunkTranslationMap = require('../chunk-translation-map');
124
125 const parseBody = bodyparser();
126 return async (ctx, next) => {
127 if (ctx.element) {
128 await next();
129 const i18n = plugin.from(ctx); // get the webpack chunks that are used and serialize their translations
130
131 const chunks = [...ctx.syncChunks, ...ctx.preloadChunks];
132 const translations = {};
133 const possibleTranslations = i18n.translations ? Object.keys(i18n.translations) : [];
134 chunks.forEach(id => {
135 const keys = Array.from(chunkTranslationMap.translationsForChunk(id));
136 keys.forEach(key => {
137 if (Array.isArray(key)) {
138 const matches = possibleTranslations.filter(matchesLiteralSections(key));
139
140 for (const match of matches) {
141 translations[match] = i18n.translations && i18n.translations[match];
142 }
143 } else {
144 translations[key] = i18n.translations && i18n.translations[key];
145 }
146 });
147 }); // i18n.locale is actually a locale.Locale instance
148
149 if (!i18n.locale) {
150 throw new Error('i18n.locale was empty');
151 }
152
153 const localeCode = typeof i18n.locale === 'string' ? i18n.locale : i18n.locale.code;
154 const serialized = JSON.stringify({
155 localeCode,
156 translations
157 });
158 const script = html`
159 <script type="application/json" id="__TRANSLATIONS__">
160 ${serialized}
161 </script>
162 `; // consumed by ./browser
163
164 ctx.template.body.push(script); // set HTML lang tag as a hint for signal screen readers to switch to the
165 // recommended language.
166
167 ctx.template.htmlAttrs.lang = localeCode;
168 } else if (ctx.path === '/_translations') {
169 const i18n = plugin.from(ctx);
170
171 try {
172 await parseBody(ctx, () => Promise.resolve());
173 } catch (e) {
174 ctx.request.body = [];
175 }
176
177 const keys = getKeysFromContext(ctx);
178 const possibleTranslations = i18n.translations ? Object.keys(i18n.translations) : [];
179 const translations = keys.reduce((acc, key) => {
180 if (Array.isArray(key)) {
181 const matches = possibleTranslations.filter(matchesLiteralSections(key));
182
183 for (const match of matches) {
184 acc[match] = i18n.translations && i18n.translations[match];
185 }
186 } else {
187 acc[key] = i18n.translations && i18n.translations[key];
188 }
189
190 return acc;
191 }, {});
192 ctx.body = translations;
193 ctx.set('cache-control', 'public, max-age=3600'); // cache translations for up to 1 hour
194
195 return next();
196 } else {
197 return next();
198 }
199 };
200 }
201});
202
203export default true && pluginFactory();
204//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGUuanMiXSwibmFtZXMiOlsiTG9jYWxlIiwiY3JlYXRlUGx1Z2luIiwibWVtb2l6ZSIsImh0bWwiLCJVbml2ZXJzYWxFdmVudHNUb2tlbiIsImJvZHlwYXJzZXIiLCJxdWVyeXN0cmluZyIsIkkxOG5Mb2FkZXJUb2tlbiIsImNyZWF0ZUxvYWRlciIsIm1hdGNoZXNMaXRlcmFsU2VjdGlvbnMiLCJsaXRlcmFsU2VjdGlvbnMiLCJ0cmFuc2xhdGlvbiIsImxhc3RNYXRjaEluZGV4IiwibGVuZ3RoIiwibGl0ZXJhbCIsImV2ZXJ5IiwibGl0ZXJhbEluZGV4Iiwic3RhcnRzV2l0aCIsImVuZHNXaXRoIiwibWF0Y2hJbmRleCIsImluZGV4T2YiLCJnZXRLZXlzRnJvbUNvbnRleHQiLCJjdHgiLCJyZXF1ZXN0IiwiYm9keSIsIkFycmF5IiwiaXNBcnJheSIsInF1ZXJ5c3RyaW5nUGFyYW1zIiwicGFyc2UiLCJrZXlzIiwiSlNPTiIsImUiLCJwbHVnaW5GYWN0b3J5IiwiZGVwcyIsImxvYWRlciIsIm9wdGlvbmFsIiwiZXZlbnRzIiwicHJvdmlkZXMiLCJJMThuIiwiY29uc3RydWN0b3IiLCJ0cmFuc2xhdGlvbnMiLCJsb2NhbGUiLCJmcm9tIiwiZW1pdHRlciIsImxvYWQiLCJ0cmFuc2xhdGUiLCJrZXkiLCJpbnRlcnBvbGF0aW9ucyIsInRlbXBsYXRlIiwiZW1pdCIsInJlcGxhY2UiLCJfIiwiayIsIlN0cmluZyIsInNlcnZpY2UiLCJtaWRkbGV3YXJlIiwicGx1Z2luIiwiY2h1bmtUcmFuc2xhdGlvbk1hcCIsInJlcXVpcmUiLCJwYXJzZUJvZHkiLCJuZXh0IiwiZWxlbWVudCIsImkxOG4iLCJjaHVua3MiLCJzeW5jQ2h1bmtzIiwicHJlbG9hZENodW5rcyIsInBvc3NpYmxlVHJhbnNsYXRpb25zIiwiT2JqZWN0IiwiZm9yRWFjaCIsImlkIiwidHJhbnNsYXRpb25zRm9yQ2h1bmsiLCJtYXRjaGVzIiwiZmlsdGVyIiwibWF0Y2giLCJFcnJvciIsImxvY2FsZUNvZGUiLCJjb2RlIiwic2VyaWFsaXplZCIsInN0cmluZ2lmeSIsInNjcmlwdCIsInB1c2giLCJodG1sQXR0cnMiLCJsYW5nIiwicGF0aCIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVkdWNlIiwiYWNjIiwic2V0Il0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7QUFRQTtBQUVBLFNBQVFBLE1BQVIsUUFBcUIsUUFBckI7QUFFQSxTQUFRQyxZQUFSLEVBQXNCQyxPQUF0QixFQUErQkMsSUFBL0IsUUFBMEMsYUFBMUM7QUFFQSxTQUFRQyxvQkFBUixRQUFtQyxnQ0FBbkM7QUFDQSxPQUFPQyxVQUFQLE1BQXVCLGdCQUF2QjtBQUNBLE9BQU9DLFdBQVAsTUFBd0IsYUFBeEI7QUFFQSxTQUFRQyxlQUFSLFFBQThCLGFBQTlCO0FBQ0EsT0FBT0MsWUFBUCxNQUF5QixhQUF6QjtBQVFBO0FBQ0EsT0FBTyxTQUFTQyxzQkFBVCxDQUFnQ0MsZUFBaEMsRUFBZ0U7QUFDckUsU0FBUUMsV0FBRCxJQUF5QjtBQUM5QixRQUFJQyxjQUFjLEdBQUcsQ0FBckI7O0FBRUEsUUFBSUYsZUFBZSxDQUFDRyxNQUFoQixLQUEyQixDQUEvQixFQUFrQztBQUNoQyxZQUFNQyxPQUFPLEdBQUdKLGVBQWUsQ0FBQyxDQUFELENBQS9CO0FBQ0EsYUFBT0ksT0FBTyxLQUFLLEVBQVosSUFBa0JILFdBQVcsS0FBS0csT0FBekM7QUFDRDs7QUFFRCxXQUFPSixlQUFlLENBQUNLLEtBQWhCLENBQXNCLENBQUNELE9BQUQsRUFBVUUsWUFBVixLQUEyQjtBQUN0RCxVQUFJRixPQUFPLEtBQUssRUFBaEIsRUFBb0I7QUFDbEI7QUFDQTtBQUNBO0FBQ0EsZUFBTyxJQUFQO0FBQ0QsT0FMRCxNQUtPLElBQUlFLFlBQVksS0FBSyxDQUFqQixJQUFzQkwsV0FBVyxDQUFDTSxVQUFaLENBQXVCSCxPQUF2QixDQUExQixFQUEyRDtBQUNoRUYsUUFBQUEsY0FBYyxJQUFJRSxPQUFPLENBQUNELE1BQTFCO0FBQ0EsZUFBTyxJQUFQO0FBQ0QsT0FITSxN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QUFILEdBQWFYLElBQUksQ0FBQ3RCLFlBQUwsSUFBcUJzQixJQUFJLENBQUN0QixZQUFMLENBQWtCaUMsS0FBbEIsQ0FBbEM7QUFDRDtBQUNGLFdBUEQsTUFPTztBQUNMYyxZQUFBQSxHQUFHLENBQUN6QyxHQUFELENBQUgsR0FBV2dCLElBQUksQ0FBQ3RCLFlBQUwsSUFBcUJzQixJQUFJLENBQUN0QixZQUFMLENBQWtCTSxHQUFsQixDQUFoQztBQUNEOztBQUNELGlCQUFPeUMsR0FBUDtBQUNELFNBWm9CLEVBWWxCLEVBWmtCLENBQXJCO0FBYUFqRSxRQUFBQSxHQUFHLENBQUNFLElBQUosR0FBV2dCLFlBQVg7QUFDQWxCLFFBQUFBLEdBQUcsQ0FBQ2tFLEdBQUosQ0FBUSxlQUFSLEVBQXlCLHNCQUF6QixFQXpCd0MsQ0F5QlU7O0FBQ2xELGVBQU81QixJQUFJLEVBQVg7QUFDRCxPQTNCTSxNQTJCQTtBQUNMLGVBQU9BLElBQUksRUFBWDtBQUNEO0FBQ0YsS0EvRUQ7QUFnRkQ7QUE5SFUsQ0FBRCxDQURkOztBQWtJQSxlQUFpQixRQUFZNUIsYUFBYSxFQUExQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKiBDb3B5cmlnaHQgKGMpIDIwMTggVWJlciBUZWNobm9sb2dpZXMsIEluYy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbi8qIGVzbGludC1lbnYgbm9kZSAqL1xuXG5pbXBvcnQge0xvY2FsZX0gZnJvbSAnbG9jYWxlJztcblxuaW1wb3J0IHtjcmVhdGVQbHVnaW4sIG1lbW9pemUsIGh0bWx9IGZyb20gJ2Z1c2lvbi1jb3JlJztcbmltcG9ydCB0eXBlIHtGdXNpb25QbHVnaW4sIENvbnRleHR9IGZyb20gJ2Z1c2lvbi1jb3JlJztcbmltcG9ydCB7VW5pdmVyc2FsRXZlbnRzVG9rZW59IGZyb20gJ2Z1c2lvbi1wbHVnaW4tdW5pdmVyc2FsLWV2ZW50cyc7XG5pbXBvcnQgYm9keXBhcnNlciBmcm9tICdrb2EtYm9keXBhcnNlcic7XG5pbXBvcnQgcXVlcnlzdHJpbmcgZnJvbSAncXVlcnlzdHJpbmcnO1xuXG5pbXBvcnQge0kxOG5Mb2FkZXJUb2tlbn0gZnJvbSAnLi90b2tlbnMuanMnO1xuaW1wb3J0IGNyZWF0ZUxvYWRlciBmcm9tICcuL2xvYWRlci5qcyc7XG5pbXBvcnQgdHlwZSB7XG4gIEkxOG5EZXBzVHlwZSxcbiAgSTE4blNlcnZpY2VUeXBlLFxuICBUcmFuc2xhdGlvbnNPYmplY3RUeXBlLFxuICBJRW1pdHRlcixcbn0gZnJvbSAnLi90eXBlcy5qcyc7XG5cbi8vIGV4cG9ydGVkIGZvciB0ZXN0aW5nXG5leHBvcnQgZnVuY3Rpb24gbWF0Y2hlc0xpdGVyYWxTZWN0aW9ucyhsaXRlcmFsU2VjdGlvbnM6IEFycmF5PHN0cmluZz4pIHtcbiAgcmV0dXJuICh0cmFuc2xhdGlvbjogc3RyaW5nKSA9PiB7XG4gICAgbGV0IGxhc3RNYXRjaEluZGV4ID0gMDtcblxuICAgIGlmIChsaXRlcmFsU2VjdGlvbnMubGVuZ3RoID09PSAxKSB7XG4gICAgICBjb25zdCBsaXRlcmFsID0gbGl0ZXJhbFNlY3Rpb25zWzBdO1xuICAgICAgcmV0dXJuIGxpdGVyYWwgIT09ICcnICYmIHRyYW5zbGF0aW9uID09PSBsaXRlcmFsO1xuICAgIH1cblxuICAgIHJldHVybiBsaXRlcmFsU2VjdGlvbnMuZXZlcnkoKGxpdGVyYWwsIGxpdGVyYWxJbmRleCkgPT4ge1xuICAgICAgaWYgKGxpdGVyYWwgPT09ICcnKSB7XG4gICAgICAgIC8vIGxpdGVyYWwgc2VjdGlvbiBlaXRoZXI6XG4gICAgICAgIC8vIC0gc3RhcnRzL2VuZHMgdGhlIGxpdGVyYWxcbiAgICAgICAgLy8gLSBpcyB0aGUgcmVzdWx0IG9mIHR3byBhZGphY2VudCBpbnRlcnBvbGF0aW9uc1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0gZWxzZSBpZiAobGl0ZXJhbEluZGV4ID09PSAwICYmIHRyYW5zbGF0aW9uLnN0YXJ0c1dpdGgobGl0ZXJhbCkpIHtcbiAgICAgICAgbGFzdE1hdGNoSW5kZXggKz0gbGl0ZXJhbC5sZW5ndGg7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgbGl0ZXJhbEluZGV4ID09PSBsaXRlcmFsU2VjdGlvbnMubGVuZ3RoIC0gMSAmJlxuICAgICAgICB0cmFuc2xhdGlvbi5lbmRzV2l0aChsaXRlcmFsKVxuICAgICAgKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gc3RhcnQgc2VhcmNoIGZyb20gYGxhc3RNYXRjaEluZGV4YFxuICAgICAgICBjb25zdCBtYXRjaEluZGV4ID0gdHJhbnNsYXRpb24uaW5kZXhPZihsaXRlcmFsLCBsYXN0TWF0Y2hJbmRleCk7XG4gICAgICAgIGlmIChtYXRjaEluZGV4ICE9PSAtMSkge1xuICAgICAgICAgIGxhc3RNYXRjaEluZGV4ID0gbWF0Y2hJbmRleCArIGxpdGVyYWwubGVuZ3RoO1xuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBtYXRjaGluZyBmYWlsZWRcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9KTtcbiAgfTtcbn1cblxuZnVuY3Rpb24gZ2V0S2V5c0Zyb21Db250ZXh0KGN0eDogQ29udGV4dCk6IHN0cmluZ1tdIHtcbiAgaWYgKGN0eC5yZXF1ZXN0LmJvZHkgJiYgQXJyYXkuaXNBcnJheShjdHgucmVxdWVzdC5ib2R5KSkge1xuICAgIHJldHVybiAoY3R4LnJlcXVlc3QuYm9keTogYW55KTtcbiAgfVxuXG4gIGNvbnN0IHF1ZXJ5c3RyaW5nUGFyYW1zID0gcXVlcnlzdHJpbmcucGFyc2UoY3R4LnF1ZXJ5c3RyaW5nKTtcbiAgaWYgKHF1ZXJ5c3RyaW5nUGFyYW1zLmtleXMpIHtcbiAgICB0cnkge1xuICAgICAgY29uc3Qga2V5cyA9IEpTT04ucGFyc2UocXVlcnlzdHJpbmdQYXJhbXMua2V5cyk7XG4gICAgICByZXR1cm4gQXJyYXkuaXNBcnJheShrZXlzKSA/IGtleXMgOiBbXTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIFtdO1xufVxuXG50eXBlIFBsdWdpblR5cGUgPSBGdXNpb25QbHVnaW48STE4bkRlcHNUeXBlLCBJMThuU2VydmljZVR5cGU+O1xuY29uc3QgcGx1Z2luRmFjdG9yeTogKCkgPT4gUGx1Z2luVHlwZSA9ICgpID0+XG4gIGNyZWF0ZVBsdWdpbih7XG4gICAgZGVwczoge1xuICAgICAgbG9hZGVyOiBJMThuTG9hZGVyVG9rZW4ub3B0aW9uYWwsXG4gICAgICBldmVudHM6IFVuaXZlcnNhbEV2ZW50c1Rva2VuLm9wdGlvbmFsLFxuICAgIH0sXG4gICAgcHJvdmlkZXM6ICh7bG9hZGVyLCBldmVudHN9KSA9PiB7XG4gICAgICBjbGFzcyBJMThuIHtcbiAgICAgICAgdHJhbnNsYXRpb25zOiBUcmFuc2xhdGlvbnNPYmplY3RUeXBlO1xuICAgICAgICBsb2NhbGU6IHN0cmluZyB8IExvY2FsZTtcbiAgICAgICAgZW1pdHRlcjogP0lFbWl0dGVyO1xuXG4gICAgICAgIGNvbnN0cnVjdG9yKGN0eCkge1xuICAgICAgICAgIGlmICghbG9hZGVyKSB7XG4gICAgICAgICAgICBsb2FkZXIgPSBjcmVhdGVMb2FkZXIoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3Qge3RyYW5zbGF0aW9ucywgbG9jYWxlfSA9IGxvYWRlci5mcm9tKGN0eCk7XG4gICAgICAgICAgdGhpcy5lbWl0dGVyID0gZXZlbnRzICYmIGV2ZW50cy5mcm9tKGN0eCk7XG4gICAgICAgICAgdGhpcy50cmFuc2xhdGlvbnMgPSB0cmFuc2xhdGlvbnM7XG4gICAgICAgICAgdGhpcy5sb2NhbGUgPSBsb2NhbGU7XG4gICAgICAgIH1cbiAgICAgICAgYXN5bmMgbG9hZCgpIHt9IC8vbWlycm9yIGNsaWVudCBBUElcbiAgICAgICAgdHJhbnNsYXRlKGtleSwgaW50ZXJwb2xhdGlvbnMgPSB7fSkge1xuICAgICAgICAgIGNvbnN0IHRlbXBsYXRlID0gdGhpcy50cmFuc2xhdGlvbnNba2V5XTtcblxuICAgICAgICAgIGlmICh0eXBlb2YgdGVtcGxhdGUgIT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICB0aGlzLmVtaXR0ZXIgJiYgdGhpcy5lbWl0dGVyLmVtaXQoJ2kxOG4tdHJhbnNsYXRlLW1pc3MnLCB7a2V5fSk7XG4gICAgICAgICAgICByZXR1cm4ga2V5O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB0ZW1wbGF0ZS5yZXBsYWNlKC9cXCR7KC4qPyl9L2csIChfLCBrKSA9PlxuICAgICAgICAgICAgaW50ZXJwb2xhdGlvbnNba10gPT09IHZvaWQgMFxuICAgICAgICAgICAgICA/ICckeycgKyBrICsgJ30nXG4gICAgICAgICAgICAgIDogU3RyaW5nKGludGVycG9sYXRpb25zW2tdKVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3Qgc2VydmljZSA9IHtmcm9tOiBtZW1vaXplKGN0eCA9PiBuZXcgSTE4bihjdHgpKX07XG4gICAgICByZXR1cm4gc2VydmljZTtcbiAgICB9LFxuICAgIG1pZGRsZXdhcmU6IChfLCBwbHVnaW4pID0+IHtcbiAgICAgIC8vIFRPRE8oIzQpIHJlZmFjdG9yOiB0aGlzIGN1cnJlbnRseSBkZXBlbmRzIG9uIGJhYmVsIHBsdWdpbnMgaW4gZnJhbWV3b3JrJ3Mgd2VicGFjayBjb25maWcuXG4gICAgICAvLyBJZGVhbGx5IHRoZXNlIGJhYmVsIHBsdWdpbnMgc2hvdWxkIGJlIHBhcnQgb2YgdGhpcyBwYWNrYWdlLCBub3QgaGFyZC1jb2RlZCBpbiBmcmFtZXdvcmsgY29yZVxuICAgICAgY29uc3QgY2h1bmtUcmFuc2xhdGlvbk1hcCA9IHJlcXVpcmUoJy4uL2NodW5rLXRyYW5zbGF0aW9uLW1hcCcpO1xuICAgICAgY29uc3QgcGFyc2VCb2R5ID0gYm9keXBhcnNlcigpO1xuXG4gICAgICByZXR1cm4gYXN5bmMgKGN0eCwgbmV4dCkgPT4ge1xuICAgICAgICBpZiAoY3R4LmVsZW1lbnQpIHtcbiAgICAgICAgICBhd2FpdCBuZXh0KCk7XG4gICAgICAgICAgY29uc3QgaTE4biA9IHBsdWdpbi5mcm9tKGN0eCk7XG5cbiAgICAgICAgICAvLyBnZXQgdGhlIHdlYnBhY2sgY2h1bmtzIHRoYXQgYXJlIHVzZWQgYW5kIHNlcmlhbGl6ZSB0aGVpciB0cmFuc2xhdGlvbnNcbiAgICAgICAgICBjb25zdCBjaHVua3M6IEFycmF5PHN0cmluZyB8IG51bWJlcj4gPSBbXG4gICAgICAgICAgICAuLi5jdHguc3luY0NodW5rcyxcbiAgICAgICAgICAgIC4uLmN0eC5wcmVsb2FkQ2h1bmtzLFxuICAgICAgICAgIF07XG4gICAgICAgICAgY29uc3QgdHJhbnNsYXRpb25zID0ge307XG4gICAgICAgICAgY29uc3QgcG9zc2libGVUcmFuc2xhdGlvbnMgPSBpMThuLnRyYW5zbGF0aW9uc1xuICAgICAgICAgICAgPyBPYmplY3Qua2V5cyhpMThuLnRyYW5zbGF0aW9ucylcbiAgICAgICAgICAgIDogW107XG4gICAgICAgICAgY2h1bmtzLmZvckVhY2goaWQgPT4ge1xuICAgICAgICAgICAgY29uc3Qga2V5cyA9IEFycmF5LmZyb20oXG4gICAgICAgICAgICAgIGNodW5rVHJhbnNsYXRpb25NYXAudHJhbnNsYXRpb25zRm9yQ2h1bmsoaWQpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAga2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KGtleSkpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtYXRjaGVzID0gcG9zc2libGVUcmFuc2xhdGlvbnMuZmlsdGVyKFxuICAgICAgICAgICAgICAgICAgbWF0Y2hlc0xpdGVyYWxTZWN0aW9ucyhrZXkpXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IG1hdGNoIG9mIG1hdGNoZXMpIHtcbiAgICAgICAgICAgICAgICAgIHRyYW5zbGF0aW9uc1ttYXRjaF0gPVxuICAgICAgICAgICAgICAgICAgICBpMThuLnRyYW5zbGF0aW9ucyAmJiBpMThuLnRyYW5zbGF0aW9uc1ttYXRjaF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRyYW5zbGF0aW9uc1trZXldID0gaTE4bi50cmFuc2xhdGlvbnMgJiYgaTE4bi50cmFuc2xhdGlvbnNba2V5XTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgLy8gaTE4bi5sb2NhbGUgaXMgYWN0dWFsbHkgYSBsb2NhbGUuTG9jYWxlIGluc3RhbmNlXG4gICAgICAgICAgaWYgKCFpMThuLmxvY2FsZSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdpMThuLmxvY2FsZSB3YXMgZW1wdHknKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgbG9jYWxlQ29kZSA9XG4gICAgICAgICAgICB0eXBlb2YgaTE4bi5sb2NhbGUgPT09ICdzdHJpbmcnID8gaTE4bi5sb2NhbGUgOiBpMThuLmxvY2FsZS5jb2RlO1xuICAgICAgICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBKU09OLnN0cmluZ2lmeSh7bG9jYWxlQ29kZSwgdHJhbnNsYXRpb25zfSk7XG4gICAgICAgICAgY29uc3Qgc2NyaXB0ID0gaHRtbGBcbiAgICAgICAgICAgIDxzY3JpcHQgdHlwZT1cImFwcGxpY2F0aW9uL2pzb25cIiBpZD1cIl9fVFJBTlNMQVRJT05TX19cIj5cbiAgICAgICAgICAgICAgJHtzZXJpYWxpemVkfVxuICAgICAgICAgICAgPC9zY3JpcHQ+XG4gICAgICAgICAgYDsgLy8gY29uc3VtZWQgYnkgLi9icm93c2VyXG4gICAgICAgICAgY3R4LnRlbXBsYXRlLmJvZHkucHVzaChzY3JpcHQpO1xuXG4gICAgICAgICAgLy8gc2V0IEhUTUwgbGFuZyB0YWcgYXMgYSBoaW50IGZvciBzaWduYWwgc2NyZWVuIHJlYWRlcnMgdG8gc3dpdGNoIHRvIHRoZVxuICAgICAgICAgIC8vIHJlY29tbWVuZGVkIGxhbmd1YWdlLlxuICAgICAgICAgIGN0eC50ZW1wbGF0ZS5odG1sQXR0cnMubGFuZyA9IGxvY2FsZUNvZGU7XG4gICAgICAgIH0gZWxzZSBpZiAoY3R4LnBhdGggPT09ICcvX3RyYW5zbGF0aW9ucycpIHtcbiAgICAgICAgICBjb25zdCBpMThuID0gcGx1Z2luLmZyb20oY3R4KTtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgcGFyc2VCb2R5KGN0eCwgKCkgPT4gUHJvbWlzZS5yZXNvbHZlKCkpO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGN0eC5yZXF1ZXN0LmJvZHkgPSBbXTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3Qga2V5cyA9IGdldEtleXNGcm9tQ29udGV4dChjdHgpO1xuICAgICAgICAgIGNvbnN0IHBvc3NpYmxlVHJhbnNsYXRpb25zID0gaTE4bi50cmFuc2xhdGlvbnNcbiAgICAgICAgICAgID8gT2JqZWN0LmtleXMoaTE4bi50cmFuc2xhdGlvbnMpXG4gICAgICAgICAgICA6IFtdO1xuICAgICAgICAgIGNvbnN0IHRyYW5zbGF0aW9ucyA9IGtleXMucmVkdWNlKChhY2MsIGtleSkgPT4ge1xuICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoa2V5KSkge1xuICAgICAgICAgICAgICBjb25zdCBtYXRjaGVzID0gcG9zc2libGVUcmFuc2xhdGlvbnMuZmlsdGVyKFxuICAgICAgICAgICAgICAgIG1hdGNoZXNMaXRlcmFsU2VjdGlvbnMoa2V5KVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBmb3IgKGNvbnN0IG1hdGNoIG9mIG1hdGNoZXMpIHtcbiAgICAgICAgICAgICAgICBhY2NbbWF0Y2hdID0gaTE4bi50cmFuc2xhdGlvbnMgJiYgaTE4bi50cmFuc2xhdGlvbnNbbWF0Y2hdO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBhY2Nba2V5XSA9IGkxOG4udHJhbnNsYXRpb25zICYmIGkxOG4udHJhbnNsYXRpb25zW2tleV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICAgIH0sIHt9KTtcbiAgICAgICAgICBjdHguYm9keSA9IHRyYW5zbGF0aW9ucztcbiAgICAgICAgICBjdHguc2V0KCdjYWNoZS1jb250cm9sJywgJ3B1YmxpYywgbWF4LWFnZT0zNjAwJyk7IC8vIGNhY2hlIHRyYW5zbGF0aW9ucyBmb3IgdXAgdG8gMSBob3VyXG4gICAgICAgICAgcmV0dXJuIG5leHQoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgIH0sXG4gIH0pO1xuXG5leHBvcnQgZGVmYXVsdCAoKF9fTk9ERV9fICYmIHBsdWdpbkZhY3RvcnkoKTogYW55KTogUGx1Z2luVHlwZSk7XG4iXX0=
\No newline at end of file