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 */
|
10 | import { Locale } from 'locale';
|
11 | import { createPlugin, memoize, html } from 'fusion-core';
|
12 | import { UniversalEventsToken } from 'fusion-plugin-universal-events';
|
13 | import bodyparser from 'koa-bodyparser';
|
14 | import querystring from 'querystring';
|
15 | import { I18nLoaderToken } from './tokens.js';
|
16 | import createLoader from './loader.js';
|
17 | // exported for testing
|
18 | export 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 |
|
54 | function 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 |
|
73 | const 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 |
|
203 | export 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 |