@okeyamy/lua 5.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +552 -0
- package/build/es5/__tests__/ai-personalize.test.js +811 -0
- package/build/es5/__tests__/lua.js +134 -0
- package/build/es5/__tests__/original-roughly.js +197 -0
- package/build/es5/__tests__/original.js +174 -0
- package/build/es5/__tests__/unit.js +72 -0
- package/build/es5/__tests__/weighted-history.test.js +376 -0
- package/build/es5/ai-personalize.js +641 -0
- package/build/es5/index.js +30 -0
- package/build/es5/lua.js +366 -0
- package/build/es5/personalization.js +811 -0
- package/build/es5/prompts/personalization-prompts.js +260 -0
- package/build/es5/storage/weighted-history.js +384 -0
- package/build/es5/stores/browser-cookie.js +25 -0
- package/build/es5/stores/local.js +29 -0
- package/build/es5/stores/memory.js +22 -0
- package/build/es5/utils.js +54 -0
- package/build/es5/utm-personalize.js +817 -0
- package/build/es5/utm.js +304 -0
- package/build/lua.dev.js +1574 -0
- package/build/lua.es.js +1566 -0
- package/build/lua.js +1574 -0
- package/build/lua.min.js +8 -0
- package/package.json +68 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
5
|
+
/**
|
|
6
|
+
* AI-Powered Personalization Engine
|
|
7
|
+
* ==================================
|
|
8
|
+
* Integrates OpenAI GPT models with the Lua personalization system.
|
|
9
|
+
* Works alongside the existing decision engine as an optional enhancement.
|
|
10
|
+
*
|
|
11
|
+
* Supports two connection modes:
|
|
12
|
+
* 1. Direct OpenAI API (user provides apiKey) - uses https://api.openai.com/v1/chat/completions
|
|
13
|
+
* 2. Proxy URL (user provides apiUrl pointing to their backend)
|
|
14
|
+
*
|
|
15
|
+
* Supports two personalization modes:
|
|
16
|
+
* - 'select': AI chooses the best variant from user-provided templates
|
|
17
|
+
* - 'generate': AI creates new personalized content from scratch
|
|
18
|
+
*
|
|
19
|
+
* Depends on:
|
|
20
|
+
* - window.LuaWeightedHistory (from storage/weighted-history.js)
|
|
21
|
+
* - window.LuaPrompts (from prompts/personalization-prompts.js)
|
|
22
|
+
* - window.LuaUTM or window.LuaUTMPersonalize (for context, optional)
|
|
23
|
+
*
|
|
24
|
+
* Registers on window.LuaAIPersonalize
|
|
25
|
+
* No ES6 imports. Self-contained IIFE.
|
|
26
|
+
*/
|
|
27
|
+
;
|
|
28
|
+
(function (root) {
|
|
29
|
+
'use strict';
|
|
30
|
+
|
|
31
|
+
// ===================================================================
|
|
32
|
+
// Constants & Defaults
|
|
33
|
+
// ===================================================================
|
|
34
|
+
var OPENAI_BASE_URL = 'https://api.openai.com/v1/chat/completions';
|
|
35
|
+
var DEFAULT_MODEL = 'gpt-4o-mini';
|
|
36
|
+
var DEFAULT_TIMEOUT = 5000;
|
|
37
|
+
var DEFAULT_MAX_TOKENS = 500;
|
|
38
|
+
var DEFAULT_TEMPERATURE = 0.7;
|
|
39
|
+
var DEFAULT_MAX_RETRIES = 1;
|
|
40
|
+
var CACHE_KEY_PREFIX = 'lua_ai_cache_';
|
|
41
|
+
var DEFAULT_CACHE_DURATION = 3600000; // 1 hour
|
|
42
|
+
|
|
43
|
+
// ===================================================================
|
|
44
|
+
// Configuration Validator
|
|
45
|
+
// ===================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate and normalize AI configuration
|
|
49
|
+
* @param {Object} config - Raw AI configuration
|
|
50
|
+
* @returns {Object} - Normalized configuration with defaults applied
|
|
51
|
+
*/
|
|
52
|
+
function normalizeConfig(config) {
|
|
53
|
+
if (!config || (0, _typeof2.default)(config) !== 'object') {
|
|
54
|
+
return {
|
|
55
|
+
valid: false,
|
|
56
|
+
error: 'AI config must be an object'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Must have either apiKey or apiUrl
|
|
61
|
+
var hasApiKey = typeof config.apiKey === 'string' && config.apiKey.trim().length > 0;
|
|
62
|
+
var hasApiUrl = typeof config.apiUrl === 'string' && config.apiUrl.trim().length > 0;
|
|
63
|
+
if (!hasApiKey && !hasApiUrl) {
|
|
64
|
+
return {
|
|
65
|
+
valid: false,
|
|
66
|
+
error: 'AI config requires either "apiKey" (for direct OpenAI) or "apiUrl" (for proxy endpoint)'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
valid: true,
|
|
71
|
+
// Connection
|
|
72
|
+
apiKey: hasApiKey ? config.apiKey.trim() : null,
|
|
73
|
+
apiUrl: hasApiUrl ? config.apiUrl.trim() : OPENAI_BASE_URL,
|
|
74
|
+
useDirectApi: hasApiKey && !hasApiUrl,
|
|
75
|
+
// Model settings
|
|
76
|
+
model: config.model || DEFAULT_MODEL,
|
|
77
|
+
temperature: typeof config.temperature === 'number' ? config.temperature : DEFAULT_TEMPERATURE,
|
|
78
|
+
maxTokens: typeof config.maxTokens === 'number' ? config.maxTokens : DEFAULT_MAX_TOKENS,
|
|
79
|
+
// Behavior
|
|
80
|
+
mode: config.mode === 'generate' ? 'generate' : 'select',
|
|
81
|
+
timeout: typeof config.timeout === 'number' ? config.timeout : DEFAULT_TIMEOUT,
|
|
82
|
+
maxRetries: typeof config.maxRetries === 'number' ? config.maxRetries : DEFAULT_MAX_RETRIES,
|
|
83
|
+
fallbackToStandard: config.fallbackToStandard !== false,
|
|
84
|
+
// Caching
|
|
85
|
+
cacheDecisions: config.cacheDecisions !== false,
|
|
86
|
+
cacheDuration: typeof config.cacheDuration === 'number' ? config.cacheDuration : DEFAULT_CACHE_DURATION,
|
|
87
|
+
// History
|
|
88
|
+
historyEnabled: config.historyEnabled !== false,
|
|
89
|
+
historyDecayRate: typeof config.historyDecayRate === 'number' ? config.historyDecayRate : 0.9,
|
|
90
|
+
maxHistorySize: typeof config.maxHistorySize === 'number' ? config.maxHistorySize : 10,
|
|
91
|
+
// Brand context (for generate mode)
|
|
92
|
+
brandContext: config.brandContext || null,
|
|
93
|
+
// Custom prompts
|
|
94
|
+
customPrompts: config.customPrompts || {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ===================================================================
|
|
99
|
+
// Cache Management
|
|
100
|
+
// ===================================================================
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate a cache key from the context (hash-like identifier)
|
|
104
|
+
* @param {Object} context - Current UTM context
|
|
105
|
+
* @param {string} mode - 'select' or 'generate'
|
|
106
|
+
* @returns {string} - Cache key
|
|
107
|
+
*/
|
|
108
|
+
function buildCacheKey(context, mode) {
|
|
109
|
+
var parts = [mode];
|
|
110
|
+
if (context.utm) {
|
|
111
|
+
if (context.utm.utm_source) parts.push('s:' + context.utm.utm_source);
|
|
112
|
+
if (context.utm.utm_medium) parts.push('m:' + context.utm.utm_medium);
|
|
113
|
+
if (context.utm.utm_campaign) parts.push('c:' + context.utm.utm_campaign);
|
|
114
|
+
}
|
|
115
|
+
if (context.referrer) {
|
|
116
|
+
parts.push('r:' + (context.referrer.source || 'direct'));
|
|
117
|
+
}
|
|
118
|
+
if (context.userAgent) {
|
|
119
|
+
parts.push('d:' + (context.userAgent.isMobile ? 'mob' : context.userAgent.isTablet ? 'tab' : 'desk'));
|
|
120
|
+
}
|
|
121
|
+
return CACHE_KEY_PREFIX + parts.join('_');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Read a cached AI decision
|
|
126
|
+
* @param {string} cacheKey - Cache key
|
|
127
|
+
* @param {number} cacheDuration - Max age in ms
|
|
128
|
+
* @returns {Object|null} - Cached decision or null if expired/missing
|
|
129
|
+
*/
|
|
130
|
+
function readCache(cacheKey, cacheDuration) {
|
|
131
|
+
try {
|
|
132
|
+
if (typeof localStorage === 'undefined') return null;
|
|
133
|
+
var raw = localStorage.getItem(cacheKey);
|
|
134
|
+
if (!raw) return null;
|
|
135
|
+
var cached = JSON.parse(raw);
|
|
136
|
+
if (!cached || !cached.timestamp || !cached.decision) return null;
|
|
137
|
+
|
|
138
|
+
// Check expiry
|
|
139
|
+
var age = Date.now() - cached.timestamp;
|
|
140
|
+
if (age > cacheDuration) {
|
|
141
|
+
localStorage.removeItem(cacheKey);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return cached.decision;
|
|
145
|
+
} catch (e) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Write an AI decision to cache
|
|
152
|
+
* @param {string} cacheKey - Cache key
|
|
153
|
+
* @param {Object} decision - Decision to cache
|
|
154
|
+
*/
|
|
155
|
+
function writeCache(cacheKey, decision) {
|
|
156
|
+
try {
|
|
157
|
+
if (typeof localStorage === 'undefined') return;
|
|
158
|
+
localStorage.setItem(cacheKey, JSON.stringify({
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
decision: decision
|
|
161
|
+
}));
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.warn('[Lua AI] Failed to write cache:', e);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Clear all AI decision caches
|
|
169
|
+
*/
|
|
170
|
+
function clearCache() {
|
|
171
|
+
try {
|
|
172
|
+
if (typeof localStorage === 'undefined') return;
|
|
173
|
+
var keysToRemove = [];
|
|
174
|
+
for (var i = 0; i < localStorage.length; i++) {
|
|
175
|
+
var key = localStorage.key(i);
|
|
176
|
+
if (key && key.indexOf(CACHE_KEY_PREFIX) === 0) {
|
|
177
|
+
keysToRemove.push(key);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (var j = 0; j < keysToRemove.length; j++) {
|
|
181
|
+
localStorage.removeItem(keysToRemove[j]);
|
|
182
|
+
}
|
|
183
|
+
} catch (e) {
|
|
184
|
+
console.warn('[Lua AI] Failed to clear cache:', e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ===================================================================
|
|
189
|
+
// API Communication
|
|
190
|
+
// ===================================================================
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Call the OpenAI API (direct or via proxy)
|
|
194
|
+
* @param {Array} messages - Chat messages array
|
|
195
|
+
* @param {Object} config - Normalized AI config
|
|
196
|
+
* @returns {Promise<Object>} - Parsed AI response
|
|
197
|
+
*/
|
|
198
|
+
function callOpenAI(messages, config) {
|
|
199
|
+
var url = config.useDirectApi ? OPENAI_BASE_URL : config.apiUrl;
|
|
200
|
+
var headers = {
|
|
201
|
+
'Content-Type': 'application/json'
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Add Authorization header for direct API access
|
|
205
|
+
if (config.useDirectApi && config.apiKey) {
|
|
206
|
+
headers['Authorization'] = 'Bearer ' + config.apiKey;
|
|
207
|
+
}
|
|
208
|
+
var body = {
|
|
209
|
+
model: config.model,
|
|
210
|
+
messages: messages,
|
|
211
|
+
temperature: config.temperature,
|
|
212
|
+
max_tokens: config.maxTokens,
|
|
213
|
+
response_format: {
|
|
214
|
+
type: 'json_object'
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Create abort controller for timeout
|
|
219
|
+
var controller = null;
|
|
220
|
+
var timeoutId = null;
|
|
221
|
+
if (typeof AbortController !== 'undefined') {
|
|
222
|
+
controller = new AbortController();
|
|
223
|
+
timeoutId = setTimeout(function () {
|
|
224
|
+
controller.abort();
|
|
225
|
+
}, config.timeout);
|
|
226
|
+
}
|
|
227
|
+
var fetchOptions = {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: headers,
|
|
230
|
+
body: JSON.stringify(body)
|
|
231
|
+
};
|
|
232
|
+
if (controller) {
|
|
233
|
+
fetchOptions.signal = controller.signal;
|
|
234
|
+
}
|
|
235
|
+
return fetch(url, fetchOptions).then(function (response) {
|
|
236
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
return response.text().then(function (text) {
|
|
239
|
+
throw new Error('API request failed (' + response.status + '): ' + text.substring(0, 200));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return response.json();
|
|
243
|
+
}).then(function (data) {
|
|
244
|
+
// Parse OpenAI response structure
|
|
245
|
+
if (data && data.choices && data.choices[0] && data.choices[0].message) {
|
|
246
|
+
var content = data.choices[0].message.content;
|
|
247
|
+
try {
|
|
248
|
+
return JSON.parse(content);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
// Try to extract JSON from content if it's wrapped in markdown
|
|
251
|
+
var jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
252
|
+
if (jsonMatch) {
|
|
253
|
+
return JSON.parse(jsonMatch[0]);
|
|
254
|
+
}
|
|
255
|
+
throw new Error('Failed to parse AI response as JSON: ' + content.substring(0, 200));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// If the response IS the parsed content (proxy might forward differently)
|
|
260
|
+
if (data && (data.selectedVariant || data.headline)) {
|
|
261
|
+
return data;
|
|
262
|
+
}
|
|
263
|
+
throw new Error('Unexpected API response structure');
|
|
264
|
+
}).catch(function (error) {
|
|
265
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
266
|
+
if (error.name === 'AbortError') {
|
|
267
|
+
throw new Error('AI request timed out after ' + config.timeout + 'ms');
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Call OpenAI with retry logic
|
|
275
|
+
* @param {Array} messages - Chat messages
|
|
276
|
+
* @param {Object} config - Normalized config
|
|
277
|
+
* @param {number} [attempt] - Current attempt number
|
|
278
|
+
* @returns {Promise<Object>} - Parsed AI response
|
|
279
|
+
*/
|
|
280
|
+
function callWithRetry(messages, config, attempt) {
|
|
281
|
+
attempt = attempt || 0;
|
|
282
|
+
return callOpenAI(messages, config).catch(function (error) {
|
|
283
|
+
if (attempt < config.maxRetries) {
|
|
284
|
+
console.warn('[Lua AI] Retry attempt ' + (attempt + 1) + ':', error.message);
|
|
285
|
+
// Exponential backoff: 500ms, 1000ms, 2000ms...
|
|
286
|
+
return new Promise(function (resolve) {
|
|
287
|
+
setTimeout(function () {
|
|
288
|
+
resolve(callWithRetry(messages, config, attempt + 1));
|
|
289
|
+
}, 500 * Math.pow(2, attempt));
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
throw error;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ===================================================================
|
|
297
|
+
// Response Validation
|
|
298
|
+
// ===================================================================
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Validate a SELECTION mode response
|
|
302
|
+
* @param {Object} response - Parsed AI response
|
|
303
|
+
* @param {Object} variants - Available variants (to validate key exists)
|
|
304
|
+
* @returns {Object} - { valid: boolean, error?: string }
|
|
305
|
+
*/
|
|
306
|
+
function validateSelectResponse(response, variants) {
|
|
307
|
+
if (!response || (0, _typeof2.default)(response) !== 'object') {
|
|
308
|
+
return {
|
|
309
|
+
valid: false,
|
|
310
|
+
error: 'Response is not an object'
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (!response.selectedVariant || typeof response.selectedVariant !== 'string') {
|
|
314
|
+
return {
|
|
315
|
+
valid: false,
|
|
316
|
+
error: 'Missing or invalid "selectedVariant" field'
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check that the selected variant actually exists
|
|
321
|
+
if (!variants[response.selectedVariant]) {
|
|
322
|
+
return {
|
|
323
|
+
valid: false,
|
|
324
|
+
error: 'Selected variant "' + response.selectedVariant + '" does not exist in templates'
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
valid: true
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Validate a GENERATION mode response
|
|
334
|
+
* @param {Object} response - Parsed AI response
|
|
335
|
+
* @returns {Object} - { valid: boolean, error?: string }
|
|
336
|
+
*/
|
|
337
|
+
function validateGenerateResponse(response) {
|
|
338
|
+
if (!response || (0, _typeof2.default)(response) !== 'object') {
|
|
339
|
+
return {
|
|
340
|
+
valid: false,
|
|
341
|
+
error: 'Response is not an object'
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (!response.headline || typeof response.headline !== 'string') {
|
|
345
|
+
return {
|
|
346
|
+
valid: false,
|
|
347
|
+
error: 'Missing or invalid "headline" field'
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (!response.subheadline || typeof response.subheadline !== 'string') {
|
|
351
|
+
return {
|
|
352
|
+
valid: false,
|
|
353
|
+
error: 'Missing or invalid "subheadline" field'
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (!response.ctaLabel || typeof response.ctaLabel !== 'string') {
|
|
357
|
+
return {
|
|
358
|
+
valid: false,
|
|
359
|
+
error: 'Missing or invalid "ctaLabel" field'
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
valid: true
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ===================================================================
|
|
368
|
+
// AI Decision Engine
|
|
369
|
+
// ===================================================================
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Main AI decision function
|
|
373
|
+
* Gathers context, builds prompts, calls API, validates response
|
|
374
|
+
*
|
|
375
|
+
* @param {Object} context - Current UTM/referrer/device context
|
|
376
|
+
* @param {Object} options - Full options object
|
|
377
|
+
* @param {Object} options.templates - User-provided templates (REQUIRED)
|
|
378
|
+
* @param {Object} options.aiConfig - AI configuration (REQUIRED)
|
|
379
|
+
* @param {boolean} [options.log] - Enable logging (default: true)
|
|
380
|
+
* @returns {Promise<Object>} - Decision result { template, intent, source, context, aiResponse }
|
|
381
|
+
*/
|
|
382
|
+
function aiDecide(context, options) {
|
|
383
|
+
var config = normalizeConfig(options.aiConfig);
|
|
384
|
+
if (!config.valid) {
|
|
385
|
+
return Promise.reject(new Error('[Lua AI] ' + config.error));
|
|
386
|
+
}
|
|
387
|
+
var templates = options.templates || {};
|
|
388
|
+
var log = options.log !== false;
|
|
389
|
+
|
|
390
|
+
// Check cache first
|
|
391
|
+
if (config.cacheDecisions) {
|
|
392
|
+
var cacheKey = buildCacheKey(context, config.mode);
|
|
393
|
+
var cached = readCache(cacheKey, config.cacheDuration);
|
|
394
|
+
if (cached) {
|
|
395
|
+
if (log) {
|
|
396
|
+
console.log('[Lua AI] Using cached decision:', cached.intent);
|
|
397
|
+
}
|
|
398
|
+
cached.source = 'ai-cached';
|
|
399
|
+
return Promise.resolve(cached);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Get weighted history
|
|
404
|
+
var HistoryModule = root.LuaWeightedHistory;
|
|
405
|
+
var weightedHistory = '';
|
|
406
|
+
var preferences = {};
|
|
407
|
+
var history = null;
|
|
408
|
+
if (config.historyEnabled && HistoryModule) {
|
|
409
|
+
history = HistoryModule.getHistory();
|
|
410
|
+
var weighted = HistoryModule.buildWeightedContext(history, {
|
|
411
|
+
decayRate: config.historyDecayRate
|
|
412
|
+
});
|
|
413
|
+
preferences = HistoryModule.aggregatePreferences(weighted);
|
|
414
|
+
weightedHistory = HistoryModule.formatForPrompt(weighted);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Get prompt module
|
|
418
|
+
var PromptsModule = root.LuaPrompts;
|
|
419
|
+
if (!PromptsModule) {
|
|
420
|
+
return Promise.reject(new Error('[Lua AI] LuaPrompts module not loaded. Include prompts/personalization-prompts.js'));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Build prompt parameters
|
|
424
|
+
var promptParams = {
|
|
425
|
+
context: context,
|
|
426
|
+
weightedHistory: weightedHistory,
|
|
427
|
+
preferences: preferences
|
|
428
|
+
};
|
|
429
|
+
if (config.mode === 'select') {
|
|
430
|
+
promptParams.variants = templates;
|
|
431
|
+
} else {
|
|
432
|
+
// Generate mode
|
|
433
|
+
promptParams.brandContext = config.brandContext;
|
|
434
|
+
promptParams.fallbackTemplate = templates['default'] || templates[Object.keys(templates)[0]] || null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Build messages
|
|
438
|
+
var messages = PromptsModule.buildMessages(config.mode, promptParams, config.customPrompts);
|
|
439
|
+
var startTime = Date.now();
|
|
440
|
+
|
|
441
|
+
// Call AI
|
|
442
|
+
return callWithRetry(messages, config).then(function (aiResponse) {
|
|
443
|
+
var latency = Date.now() - startTime;
|
|
444
|
+
var decision;
|
|
445
|
+
if (config.mode === 'select') {
|
|
446
|
+
// Validate selection
|
|
447
|
+
var selectValidation = validateSelectResponse(aiResponse, templates);
|
|
448
|
+
if (!selectValidation.valid) {
|
|
449
|
+
throw new Error('Invalid AI selection: ' + selectValidation.error);
|
|
450
|
+
}
|
|
451
|
+
decision = {
|
|
452
|
+
template: templates[aiResponse.selectedVariant],
|
|
453
|
+
intent: aiResponse.selectedVariant,
|
|
454
|
+
source: 'ai',
|
|
455
|
+
context: context,
|
|
456
|
+
aiResponse: {
|
|
457
|
+
confidence: aiResponse.confidence || null,
|
|
458
|
+
reasoning: aiResponse.reasoning || null,
|
|
459
|
+
latency: latency,
|
|
460
|
+
model: config.model,
|
|
461
|
+
mode: 'select',
|
|
462
|
+
cached: false
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
} else {
|
|
466
|
+
// Validate generation
|
|
467
|
+
var genValidation = validateGenerateResponse(aiResponse);
|
|
468
|
+
if (!genValidation.valid) {
|
|
469
|
+
throw new Error('Invalid AI generation: ' + genValidation.error);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Build a template from generated content
|
|
473
|
+
var generatedTemplate = {
|
|
474
|
+
headline: aiResponse.headline,
|
|
475
|
+
subheadline: aiResponse.subheadline,
|
|
476
|
+
ctaLabel: aiResponse.ctaLabel,
|
|
477
|
+
ctaLink: templates['default'] && templates['default'].ctaLink || '/shop',
|
|
478
|
+
image: templates['default'] && templates['default'].image || null
|
|
479
|
+
};
|
|
480
|
+
decision = {
|
|
481
|
+
template: generatedTemplate,
|
|
482
|
+
intent: 'ai-generated',
|
|
483
|
+
source: 'ai',
|
|
484
|
+
context: context,
|
|
485
|
+
aiResponse: {
|
|
486
|
+
confidence: aiResponse.confidence || null,
|
|
487
|
+
reasoning: aiResponse.reasoning || null,
|
|
488
|
+
latency: latency,
|
|
489
|
+
model: config.model,
|
|
490
|
+
mode: 'generate',
|
|
491
|
+
cached: false
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Cache the decision
|
|
497
|
+
if (config.cacheDecisions) {
|
|
498
|
+
writeCache(buildCacheKey(context, config.mode), decision);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Record visit to history
|
|
502
|
+
if (config.historyEnabled && HistoryModule) {
|
|
503
|
+
HistoryModule.recordVisit({
|
|
504
|
+
context: context,
|
|
505
|
+
intent: decision.intent,
|
|
506
|
+
selectedVariant: decision.intent,
|
|
507
|
+
source: 'ai',
|
|
508
|
+
aiDecision: true
|
|
509
|
+
}, {
|
|
510
|
+
maxHistorySize: config.maxHistorySize
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (log) {
|
|
514
|
+
console.log('[Lua AI] Decision made:', {
|
|
515
|
+
mode: config.mode,
|
|
516
|
+
intent: decision.intent,
|
|
517
|
+
source: 'ai',
|
|
518
|
+
confidence: decision.aiResponse.confidence,
|
|
519
|
+
latency: latency + 'ms',
|
|
520
|
+
model: config.model
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
return decision;
|
|
524
|
+
}).catch(function (error) {
|
|
525
|
+
var latency = Date.now() - startTime;
|
|
526
|
+
if (log) {
|
|
527
|
+
console.warn('[Lua AI] Error after ' + latency + 'ms:', error.message);
|
|
528
|
+
}
|
|
529
|
+
throw error;
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ===================================================================
|
|
534
|
+
// Integration Helpers
|
|
535
|
+
// ===================================================================
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* High-level AI personalize function
|
|
539
|
+
* Wraps aiDecide with full context gathering and DOM application
|
|
540
|
+
* Designed to be called from the main personalize() function
|
|
541
|
+
*
|
|
542
|
+
* @param {Object} options - Full personalization options
|
|
543
|
+
* @param {Object} options.templates - User templates (REQUIRED)
|
|
544
|
+
* @param {Object} options.aiConfig - AI configuration (REQUIRED)
|
|
545
|
+
* @param {Object} [options.context] - Pre-computed context
|
|
546
|
+
* @param {boolean} [options.log] - Enable logging
|
|
547
|
+
* @returns {Promise<Object>} - Decision result
|
|
548
|
+
*/
|
|
549
|
+
function personalizeWithAI(options) {
|
|
550
|
+
options = options || {};
|
|
551
|
+
|
|
552
|
+
// Get context
|
|
553
|
+
var context = options.context;
|
|
554
|
+
if (!context) {
|
|
555
|
+
// Try to get context from UTM modules
|
|
556
|
+
var utmModule = root.LuaUTMPersonalize || root.LuaUTM;
|
|
557
|
+
if (utmModule && typeof utmModule.getContext === 'function') {
|
|
558
|
+
context = utmModule.getContext();
|
|
559
|
+
} else {
|
|
560
|
+
context = {
|
|
561
|
+
utm: {},
|
|
562
|
+
referrer: {
|
|
563
|
+
source: 'direct',
|
|
564
|
+
category: 'direct',
|
|
565
|
+
url: ''
|
|
566
|
+
},
|
|
567
|
+
userAgent: {
|
|
568
|
+
raw: '',
|
|
569
|
+
isMobile: false,
|
|
570
|
+
isTablet: false,
|
|
571
|
+
isDesktop: true
|
|
572
|
+
},
|
|
573
|
+
timestamp: Date.now(),
|
|
574
|
+
hasUTM: false,
|
|
575
|
+
primaryIntent: 'default'
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return aiDecide(context, options);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Quick check: is AI properly configured and ready?
|
|
584
|
+
* @param {Object} aiConfig - AI config to check
|
|
585
|
+
* @returns {Object} - { ready: boolean, error?: string }
|
|
586
|
+
*/
|
|
587
|
+
function isReady(aiConfig) {
|
|
588
|
+
var config = normalizeConfig(aiConfig);
|
|
589
|
+
if (!config.valid) {
|
|
590
|
+
return {
|
|
591
|
+
ready: false,
|
|
592
|
+
error: config.error
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Check dependencies
|
|
597
|
+
if (!root.LuaPrompts) {
|
|
598
|
+
return {
|
|
599
|
+
ready: false,
|
|
600
|
+
error: 'LuaPrompts module not loaded'
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// History is optional but recommended
|
|
605
|
+
if (!root.LuaWeightedHistory) {
|
|
606
|
+
console.warn('[Lua AI] LuaWeightedHistory not loaded. History features disabled.');
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
ready: true
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// ===================================================================
|
|
614
|
+
// Public API
|
|
615
|
+
// ===================================================================
|
|
616
|
+
|
|
617
|
+
var LuaAIPersonalize = {
|
|
618
|
+
// Core
|
|
619
|
+
decide: aiDecide,
|
|
620
|
+
personalizeWithAI: personalizeWithAI,
|
|
621
|
+
isReady: isReady,
|
|
622
|
+
// Configuration
|
|
623
|
+
normalizeConfig: normalizeConfig,
|
|
624
|
+
// API communication
|
|
625
|
+
callOpenAI: callOpenAI,
|
|
626
|
+
// Validation
|
|
627
|
+
validateSelectResponse: validateSelectResponse,
|
|
628
|
+
validateGenerateResponse: validateGenerateResponse,
|
|
629
|
+
// Cache
|
|
630
|
+
clearCache: clearCache,
|
|
631
|
+
// Constants
|
|
632
|
+
OPENAI_BASE_URL: OPENAI_BASE_URL,
|
|
633
|
+
DEFAULT_MODEL: DEFAULT_MODEL,
|
|
634
|
+
DEFAULT_TIMEOUT: DEFAULT_TIMEOUT,
|
|
635
|
+
DEFAULT_CACHE_DURATION: DEFAULT_CACHE_DURATION
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// Register globally
|
|
639
|
+
root.LuaAIPersonalize = LuaAIPersonalize;
|
|
640
|
+
})(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : void 0);
|
|
641
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyb290IiwiT1BFTkFJX0JBU0VfVVJMIiwiREVGQVVMVF9NT0RFTCIsIkRFRkFVTFRfVElNRU9VVCIsIkRFRkFVTFRfTUFYX1RPS0VOUyIsIkRFRkFVTFRfVEVNUEVSQVRVUkUiLCJERUZBVUxUX01BWF9SRVRSSUVTIiwiQ0FDSEVfS0VZX1BSRUZJWCIsIkRFRkFVTFRfQ0FDSEVfRFVSQVRJT04iLCJub3JtYWxpemVDb25maWciLCJjb25maWciLCJfdHlwZW9mMiIsImRlZmF1bHQiLCJ2YWxpZCIsImVycm9yIiwiaGFzQXBpS2V5IiwiYXBpS2V5IiwidHJpbSIsImxlbmd0aCIsImhhc0FwaVVybCIsImFwaVVybCIsInVzZURpcmVjdEFwaSIsIm1vZGVsIiwidGVtcGVyYXR1cmUiLCJtYXhUb2tlbnMiLCJtb2RlIiwidGltZW91dCIsIm1heFJldHJpZXMiLCJmYWxsYmFja1RvU3RhbmRhcmQiLCJjYWNoZURlY2lzaW9ucyIsImNhY2hlRHVyYXRpb24iLCJoaXN0b3J5RW5hYmxlZCIsImhpc3RvcnlEZWNheVJhdGUiLCJtYXhIaXN0b3J5U2l6ZSIsImJyYW5kQ29udGV4dCIsImN1c3RvbVByb21wdHMiLCJidWlsZENhY2hlS2V5IiwiY29udGV4dCIsInBhcnRzIiwidXRtIiwidXRtX3NvdXJjZSIsInB1c2giLCJ1dG1fbWVkaXVtIiwidXRtX2NhbXBhaWduIiwicmVmZXJyZXIiLCJzb3VyY2UiLCJ1c2VyQWdlbnQiLCJpc01vYmlsZSIsImlzVGFibGV0Iiwiam9pbiIsInJlYWRDYWNoZSIsImNhY2hlS2V5IiwibG9jYWxTdG9yYWdlIiwicmF3IiwiZ2V0SXRlbSIsImNhY2hlZCIsIkpTT04iLCJwYXJzZSIsInRpbWVzdGFtcCIsImRlY2lzaW9uIiwiYWdlIiwiRGF0ZSIsIm5vdyIsInJlbW92ZUl0ZW0iLCJlIiwid3JpdGVDYWNoZSIsInNldEl0ZW0iLCJzdHJpbmdpZnkiLCJjb25zb2xlIiwid2FybiIsImNsZWFyQ2FjaGUiLCJrZXlzVG9SZW1vdmUiLCJpIiwia2V5IiwiaW5kZXhPZiIsImoiLCJjYWxsT3BlbkFJIiwibWVzc2FnZXMiLCJ1cmwiLCJoZWFkZXJzIiwiYm9keSIsIm1heF90b2tlbnMiLCJyZXNwb25zZV9mb3JtYXQiLCJ0eXBlIiwiY29udHJvbGxlciIsInRpbWVvdXRJZCIsIkFib3J0Q29udHJvbGxlciIsInNldFRpbWVvdXQiLCJhYm9ydCIsImZldGNoT3B0aW9ucyIsIm1ldGhvZCIsInNpZ25hbCIsImZldGNoIiwidGhlbiIsInJlc3BvbnNlIiwiY2xlYXJUaW1lb3V0Iiwib2siLCJ0ZXh0IiwiRXJyb3IiLCJzdGF0dXMiLCJzdWJzdHJpbmciLCJqc29uIiwiZGF0YSIsImNob2ljZXMiLCJtZXNzYWdlIiwiY29udGVudCIsImpzb25NYXRjaCIsIm1hdGNoIiwic2VsZWN0ZWRWYXJpYW50IiwiaGVhZGxpbmUiLCJjYXRjaCIsIm5hbWUiLCJjYWxsV2l0aFJldHJ5IiwiYXR0ZW1wdCIsIlByb21pc2UiLCJyZXNvbHZlIiwiTWF0aCIsInBvdyIsInZhbGlkYXRlU2VsZWN0UmVzcG9uc2UiLCJ2YXJpYW50cyIsInZhbGlkYXRlR2VuZXJhdGVSZXNwb25zZSIsInN1YmhlYWRsaW5lIiwiY3RhTGFiZWwiLCJhaURlY2lkZSIsIm9wdGlvbnMiLCJhaUNvbmZpZyIsInJlamVjdCIsInRlbXBsYXRlcyIsImxvZyIsImludGVudCIsIkhpc3RvcnlNb2R1bGUiLCJMdWFXZWlnaHRlZEhpc3RvcnkiLCJ3ZWlnaHRlZEhpc3RvcnkiLCJwcmVmZXJlbmNlcyIsImhpc3RvcnkiLCJnZXRIaXN0b3J5Iiwid2VpZ2h0ZWQiLCJidWlsZFdlaWdodGVkQ29udGV4dCIsImRlY2F5UmF0ZSIsImFnZ3JlZ2F0ZVByZWZlcmVuY2VzIiwiZm9ybWF0Rm9yUHJvbXB0IiwiUHJvbXB0c01vZHVsZSIsIkx1YVByb21wdHMiLCJwcm9tcHRQYXJhbXMiLCJmYWxsYmFja1RlbXBsYXRlIiwiT2JqZWN0Iiwia2V5cyIsImJ1aWxkTWVzc2FnZXMiLCJzdGFydFRpbWUiLCJhaVJlc3BvbnNlIiwibGF0ZW5jeSIsInNlbGVjdFZhbGlkYXRpb24iLCJ0ZW1wbGF0ZSIsImNvbmZpZGVuY2UiLCJyZWFzb25pbmciLCJnZW5WYWxpZGF0aW9uIiwiZ2VuZXJhdGVkVGVtcGxhdGUiLCJjdGFMaW5rIiwiaW1hZ2UiLCJyZWNvcmRWaXNpdCIsImFpRGVjaXNpb24iLCJwZXJzb25hbGl6ZVdpdGhBSSIsInV0bU1vZHVsZSIsIkx1YVVUTVBlcnNvbmFsaXplIiwiTHVhVVRNIiwiZ2V0Q29udGV4dCIsImNhdGVnb3J5IiwiaXNEZXNrdG9wIiwiaGFzVVRNIiwicHJpbWFyeUludGVudCIsImlzUmVhZHkiLCJyZWFkeSIsIkx1YUFJUGVyc29uYWxpemUiLCJkZWNpZGUiLCJ3aW5kb3ciLCJnbG9iYWwiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvYWktcGVyc29uYWxpemUuanMiXSwic291cmNlc0NvbnRlbnQiOlsiXG4vKipcbiAqIEFJLVBvd2VyZWQgUGVyc29uYWxpemF0aW9uIEVuZ2luZVxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICogSW50ZWdyYXRlcyBPcGVuQUkgR1BUIG1vZGVscyB3aXRoIHRoZSBMdWEgcGVyc29uYWxpemF0aW9uIHN5c3RlbS5cbiAqIFdvcmtzIGFsb25nc2lkZSB0aGUgZXhpc3RpbmcgZGVjaXNpb24gZW5naW5lIGFzIGFuIG9wdGlvbmFsIGVuaGFuY2VtZW50LlxuICpcbiAqIFN1cHBvcnRzIHR3byBjb25uZWN0aW9uIG1vZGVzOlxuICogICAxLiBEaXJlY3QgT3BlbkFJIEFQSSAodXNlciBwcm92aWRlcyBhcGlLZXkpIC0gdXNlcyBodHRwczovL2FwaS5vcGVuYWkuY29tL3YxL2NoYXQvY29tcGxldGlvbnNcbiAqICAgMi4gUHJveHkgVVJMICh1c2VyIHByb3ZpZGVzIGFwaVVybCBwb2ludGluZyB0byB0aGVpciBiYWNrZW5kKVxuICpcbiAqIFN1cHBvcnRzIHR3byBwZXJzb25hbGl6YXRpb24gbW9kZXM6XG4gKiAgIC0gJ3NlbGVjdCc6IEFJIGNob29zZXMgdGhlIGJlc3QgdmFyaWFudCBmcm9tIHVzZXItcHJvdmlkZWQgdGVtcGxhdGVzXG4gKiAgIC0gJ2dlbmVyYXRlJzogQUkgY3JlYXRlcyBuZXcgcGVyc29uYWxpemVkIGNvbnRlbnQgZnJvbSBzY3JhdGNoXG4gKlxuICogRGVwZW5kcyBvbjpcbiAqICAgLSB3aW5kb3cuTHVhV2VpZ2h0ZWRIaXN0b3J5IChmcm9tIHN0b3JhZ2Uvd2VpZ2h0ZWQtaGlzdG9yeS5qcylcbiAqICAgLSB3aW5kb3cuTHVhUHJvbXB0cyAoZnJvbSBwcm9tcHRzL3BlcnNvbmFsaXphdGlvbi1wcm9tcHRzLmpzKVxuICogICAtIHdpbmRvdy5MdWFVVE0gb3Igd2luZG93Lkx1YVVUTVBlcnNvbmFsaXplIChmb3IgY29udGV4dCwgb3B0aW9uYWwpXG4gKlxuICogUmVnaXN0ZXJzIG9uIHdpbmRvdy5MdWFBSVBlcnNvbmFsaXplXG4gKiBObyBFUzYgaW1wb3J0cy4gU2VsZi1jb250YWluZWQgSUlGRS5cbiAqL1xuOyhmdW5jdGlvbiAocm9vdCkge1xuICAgICd1c2Ugc3RyaWN0J1xuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIENvbnN0YW50cyAmIERlZmF1bHRzXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgdmFyIE9QRU5BSV9CQVNFX1VSTCA9ICdodHRwczovL2FwaS5vcGVuYWkuY29tL3YxL2NoYXQvY29tcGxldGlvbnMnXG4gICAgdmFyIERFRkFVTFRfTU9ERUwgPSAnZ3B0LTRvLW1pbmknXG4gICAgdmFyIERFRkFVTFRfVElNRU9VVCA9IDUwMDBcbiAgICB2YXIgREVGQVVMVF9NQVhfVE9LRU5TID0gNTAwXG4gICAgdmFyIERFRkFVTFRfVEVNUEVSQVRVUkUgPSAwLjdcbiAgICB2YXIgREVGQVVMVF9NQVhfUkVUUklFUyA9IDFcbiAgICB2YXIgQ0FDSEVfS0VZX1BSRUZJWCA9ICdsdWFfYWlfY2FjaGVfJ1xuICAgIHZhciBERUZBVUxUX0NBQ0hFX0RVUkFUSU9OID0gMzYwMDAwMCAvLyAxIGhvdXJcblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBDb25maWd1cmF0aW9uIFZhbGlkYXRvclxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIFZhbGlkYXRlIGFuZCBub3JtYWxpemUgQUkgY29uZmlndXJhdGlvblxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBSYXcgQUkgY29uZmlndXJhdGlvblxuICAgICAqIEByZXR1cm5zIHtPYmplY3R9IC0gTm9ybWFsaXplZCBjb25maWd1cmF0aW9uIHdpdGggZGVmYXVsdHMgYXBwbGllZFxuICAgICAqL1xuICAgIGZ1bmN0aW9uIG5vcm1hbGl6ZUNvbmZpZyhjb25maWcpIHtcbiAgICAgICAgaWYgKCFjb25maWcgfHwgdHlwZW9mIGNvbmZpZyAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdBSSBjb25maWcgbXVzdCBiZSBhbiBvYmplY3QnIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIE11c3QgaGF2ZSBlaXRoZXIgYXBpS2V5IG9yIGFwaVVybFxuICAgICAgICB2YXIgaGFzQXBpS2V5ID0gdHlwZW9mIGNvbmZpZy5hcGlLZXkgPT09ICdzdHJpbmcnICYmIGNvbmZpZy5hcGlLZXkudHJpbSgpLmxlbmd0aCA+IDBcbiAgICAgICAgdmFyIGhhc0FwaVVybCA9IHR5cGVvZiBjb25maWcuYXBpVXJsID09PSAnc3RyaW5nJyAmJiBjb25maWcuYXBpVXJsLnRyaW0oKS5sZW5ndGggPiAwXG5cbiAgICAgICAgaWYgKCFoYXNBcGlLZXkgJiYgIWhhc0FwaVVybCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICB2YWxpZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgZXJyb3I6ICdBSSBjb25maWcgcmVxdWlyZXMgZWl0aGVyIFwiYXBpS2V5XCIgKGZvciBkaXJlY3QgT3BlbkFJKSBvciBcImFwaVVybFwiIChmb3IgcHJveHkgZW5kcG9pbnQpJ1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHZhbGlkOiB0cnVlLFxuXG4gICAgICAgICAgICAvLyBDb25uZWN0aW9uXG4gICAgICAgICAgICBhcGlLZXk6IGhhc0FwaUtleSA/IGNvbmZpZy5hcGlLZXkudHJpbSgpIDogbnVsbCxcbiAgICAgICAgICAgIGFwaVVybDogaGFzQXBpVXJsID8gY29uZmlnLmFwaVVybC50cmltKCkgOiBPUEVOQUlfQkFTRV9VUkwsXG4gICAgICAgICAgICB1c2VEaXJlY3RBcGk6IGhhc0FwaUtleSAmJiAhaGFzQXBpVXJsLFxuXG4gICAgICAgICAgICAvLyBNb2RlbCBzZXR0aW5nc1xuICAgICAgICAgICAgbW9kZWw6IGNvbmZpZy5tb2RlbCB8fCBERUZBVUxUX01PREVMLFxuICAgICAgICAgICAgdGVtcGVyYXR1cmU6IHR5cGVvZiBjb25maWcudGVtcGVyYXR1cmUgPT09ICdudW1iZXInID8gY29uZmlnLnRlbXBlcmF0dXJlIDogREVGQVVMVF9URU1QRVJBVFVSRSxcbiAgICAgICAgICAgIG1heFRva2VuczogdHlwZW9mIGNvbmZpZy5tYXhUb2tlbnMgPT09ICdudW1iZXInID8gY29uZmlnLm1heFRva2VucyA6IERFRkFVTFRfTUFYX1RPS0VOUyxcblxuICAgICAgICAgICAgLy8gQmVoYXZpb3JcbiAgICAgICAgICAgIG1vZGU6IGNvbmZpZy5tb2RlID09PSAnZ2VuZXJhdGUnID8gJ2dlbmVyYXRlJyA6ICdzZWxlY3QnLFxuICAgICAgICAgICAgdGltZW91dDogdHlwZW9mIGNvbmZpZy50aW1lb3V0ID09PSAnbnVtYmVyJyA/IGNvbmZpZy50aW1lb3V0IDogREVGQVVMVF9USU1FT1VULFxuICAgICAgICAgICAgbWF4UmV0cmllczogdHlwZW9mIGNvbmZpZy5tYXhSZXRyaWVzID09PSAnbnVtYmVyJyA/IGNvbmZpZy5tYXhSZXRyaWVzIDogREVGQVVMVF9NQVhfUkVUUklFUyxcbiAgICAgICAgICAgIGZhbGxiYWNrVG9TdGFuZGFyZDogY29uZmlnLmZhbGxiYWNrVG9TdGFuZGFyZCAhPT0gZmFsc2UsXG5cbiAgICAgICAgICAgIC8vIENhY2hpbmdcbiAgICAgICAgICAgIGNhY2hlRGVjaXNpb25zOiBjb25maWcuY2FjaGVEZWNpc2lvbnMgIT09IGZhbHNlLFxuICAgICAgICAgICAgY2FjaGVEdXJhdGlvbjogdHlwZW9mIGNvbmZpZy5jYWNoZUR1cmF0aW9uID09PSAnbnVtYmVyJyA/IGNvbmZpZy5jYWNoZUR1cmF0aW9uIDogREVGQVVMVF9DQUNIRV9EVVJBVElPTixcblxuICAgICAgICAgICAgLy8gSGlzdG9yeVxuICAgICAgICAgICAgaGlzdG9yeUVuYWJsZWQ6IGNvbmZpZy5oaXN0b3J5RW5hYmxlZCAhPT0gZmFsc2UsXG4gICAgICAgICAgICBoaXN0b3J5RGVjYXlSYXRlOiB0eXBlb2YgY29uZmlnLmhpc3RvcnlEZWNheVJhdGUgPT09ICdudW1iZXInID8gY29uZmlnLmhpc3RvcnlEZWNheVJhdGUgOiAwLjksXG4gICAgICAgICAgICBtYXhIaXN0b3J5U2l6ZTogdHlwZW9mIGNvbmZpZy5tYXhIaXN0b3J5U2l6ZSA9PT0gJ251bWJlcicgPyBjb25maWcubWF4SGlzdG9yeVNpemUgOiAxMCxcblxuICAgICAgICAgICAgLy8gQnJhbmQgY29udGV4dCAoZm9yIGdlbmVyYXRlIG1vZGUpXG4gICAgICAgICAgICBicmFuZENvbnRleHQ6IGNvbmZpZy5icmFuZENvbnRleHQgfHwgbnVsbCxcblxuICAgICAgICAgICAgLy8gQ3VzdG9tIHByb21wdHNcbiAgICAgICAgICAgIGN1c3RvbVByb21wdHM6IGNvbmZpZy5jdXN0b21Qcm9tcHRzIHx8IHt9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gQ2FjaGUgTWFuYWdlbWVudFxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIEdlbmVyYXRlIGEgY2FjaGUga2V5IGZyb20gdGhlIGNvbnRleHQgKGhhc2gtbGlrZSBpZGVudGlmaWVyKVxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBjb250ZXh0IC0gQ3VycmVudCBVVE0gY29udGV4dFxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBtb2RlIC0gJ3NlbGVjdCcgb3IgJ2dlbmVyYXRlJ1xuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9IC0gQ2FjaGUga2V5XG4gICAgICovXG4gICAgZnVuY3Rpb24gYnVpbGRDYWNoZUtleShjb250ZXh0LCBtb2RlKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IFttb2RlXVxuXG4gICAgICAgIGlmIChjb250ZXh0LnV0bSkge1xuICAgICAgICAgICAgaWYgKGNvbnRleHQudXRtLnV0bV9zb3VyY2UpIHBhcnRzLnB1c2goJ3M6JyArIGNvbnRleHQudXRtLnV0bV9zb3VyY2UpXG4gICAgICAgICAgICBpZiAoY29udGV4dC51dG0udXRtX21lZGl1bSkgcGFydHMucHVzaCgnbTonICsgY29udGV4dC51dG0udXRtX21lZGl1bSlcbiAgICAgICAgICAgIGlmIChjb250ZXh0LnV0bS51dG1fY2FtcGFpZ24pIHBhcnRzLnB1c2goJ2M6JyArIGNvbnRleHQudXRtLnV0bV9jYW1wYWlnbilcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb250ZXh0LnJlZmVycmVyKSB7XG4gICAgICAgICAgICBwYXJ0cy5wdXNoKCdyOicgKyAoY29udGV4dC5yZWZlcnJlci5zb3VyY2UgfHwgJ2RpcmVjdCcpKVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNvbnRleHQudXNlckFnZW50KSB7XG4gICAgICAgICAgICBwYXJ0cy5wdXNoKCdkOicgKyAoY29udGV4dC51c2VyQWdlbnQuaXNNb2JpbGUgPyAnbW9iJyA6IGNvbnRleHQudXNlckFnZW50LmlzVGFibGV0ID8gJ3RhYicgOiAnZGVzaycpKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIENBQ0hFX0tFWV9QUkVGSVggKyBwYXJ0cy5qb2luKCdfJylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWFkIGEgY2FjaGVkIEFJIGRlY2lzaW9uXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGNhY2hlS2V5IC0gQ2FjaGUga2V5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGNhY2hlRHVyYXRpb24gLSBNYXggYWdlIGluIG1zXG4gICAgICogQHJldHVybnMge09iamVjdHxudWxsfSAtIENhY2hlZCBkZWNpc2lvbiBvciBudWxsIGlmIGV4cGlyZWQvbWlzc2luZ1xuICAgICAqL1xuICAgIGZ1bmN0aW9uIHJlYWRDYWNoZShjYWNoZUtleSwgY2FjaGVEdXJhdGlvbikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBsb2NhbFN0b3JhZ2UgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gbnVsbFxuICAgICAgICAgICAgdmFyIHJhdyA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKGNhY2hlS2V5KVxuICAgICAgICAgICAgaWYgKCFyYXcpIHJldHVybiBudWxsXG5cbiAgICAgICAgICAgIHZhciBjYWNoZWQgPSBKU09OLnBhcnNlKHJhdylcbiAgICAgICAgICAgIGlmICghY2FjaGVkIHx8ICFjYWNoZWQudGltZXN0YW1wIHx8ICFjYWNoZWQuZGVjaXNpb24pIHJldHVybiBudWxsXG5cbiAgICAgICAgICAgIC8vIENoZWNrIGV4cGlyeVxuICAgICAgICAgICAgdmFyIGFnZSA9IERhdGUubm93KCkgLSBjYWNoZWQudGltZXN0YW1wXG4gICAgICAgICAgICBpZiAoYWdlID4gY2FjaGVEdXJhdGlvbikge1xuICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKGNhY2hlS2V5KVxuICAgICAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBjYWNoZWQuZGVjaXNpb25cbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdyaXRlIGFuIEFJIGRlY2lzaW9uIHRvIGNhY2hlXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGNhY2hlS2V5IC0gQ2FjaGUga2V5XG4gICAgICogQHBhcmFtIHtPYmplY3R9IGRlY2lzaW9uIC0gRGVjaXNpb24gdG8gY2FjaGVcbiAgICAgKi9cbiAgICBmdW5jdGlvbiB3cml0ZUNhY2hlKGNhY2hlS2V5LCBkZWNpc2lvbikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBsb2NhbFN0b3JhZ2UgPT09ICd1bmRlZmluZWQnKSByZXR1cm5cbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKGNhY2hlS2V5LCBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgICAgIGRlY2lzaW9uOiBkZWNpc2lvblxuICAgICAgICAgICAgfSkpXG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybignW0x1YSBBSV0gRmFpbGVkIHRvIHdyaXRlIGNhY2hlOicsIGUpXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciBhbGwgQUkgZGVjaXNpb24gY2FjaGVzXG4gICAgICovXG4gICAgZnVuY3Rpb24gY2xlYXJDYWNoZSgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgbG9jYWxTdG9yYWdlID09PSAndW5kZWZpbmVkJykgcmV0dXJuXG4gICAgICAgICAgICB2YXIga2V5c1RvUmVtb3ZlID0gW11cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbG9jYWxTdG9yYWdlLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgdmFyIGtleSA9IGxvY2FsU3RvcmFnZS5rZXkoaSlcbiAgICAgICAgICAgICAgICBpZiAoa2V5ICYmIGtleS5pbmRleE9mKENBQ0hFX0tFWV9QUkVGSVgpID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGtleXNUb1JlbW92ZS5wdXNoKGtleSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGtleXNUb1JlbW92ZS5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKGtleXNUb1JlbW92ZVtqXSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdbTHVhIEFJXSBGYWlsZWQgdG8gY2xlYXIgY2FjaGU6JywgZSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBBUEkgQ29tbXVuaWNhdGlvblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIENhbGwgdGhlIE9wZW5BSSBBUEkgKGRpcmVjdCBvciB2aWEgcHJveHkpXG4gICAgICogQHBhcmFtIHtBcnJheX0gbWVzc2FnZXMgLSBDaGF0IG1lc3NhZ2VzIGFycmF5XG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIE5vcm1hbGl6ZWQgQUkgY29uZmlnXG4gICAgICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gLSBQYXJzZWQgQUkgcmVzcG9uc2VcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBjYWxsT3BlbkFJKG1lc3NhZ2VzLCBjb25maWcpIHtcbiAgICAgICAgdmFyIHVybCA9IGNvbmZpZy51c2VEaXJlY3RBcGkgPyBPUEVOQUlfQkFTRV9VUkwgOiBjb25maWcuYXBpVXJsXG5cbiAgICAgICAgdmFyIGhlYWRlcnMgPSB7XG4gICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgQXV0aG9yaXphdGlvbiBoZWFkZXIgZm9yIGRpcmVjdCBBUEkgYWNjZXNzXG4gICAgICAgIGlmIChjb25maWcudXNlRGlyZWN0QXBpICYmIGNvbmZpZy5hcGlLZXkpIHtcbiAgICAgICAgICAgIGhlYWRlcnNbJ0F1dGhvcml6YXRpb24nXSA9ICdCZWFyZXIgJyArIGNvbmZpZy5hcGlLZXlcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBib2R5ID0ge1xuICAgICAgICAgICAgbW9kZWw6IGNvbmZpZy5tb2RlbCxcbiAgICAgICAgICAgIG1lc3NhZ2VzOiBtZXNzYWdlcyxcbiAgICAgICAgICAgIHRlbXBlcmF0dXJlOiBjb25maWcudGVtcGVyYXR1cmUsXG4gICAgICAgICAgICBtYXhfdG9rZW5zOiBjb25maWcubWF4VG9rZW5zLFxuICAgICAgICAgICAgcmVzcG9uc2VfZm9ybWF0OiB7IHR5cGU6ICdqc29uX29iamVjdCcgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ3JlYXRlIGFib3J0IGNvbnRyb2xsZXIgZm9yIHRpbWVvdXRcbiAgICAgICAgdmFyIGNvbnRyb2xsZXIgPSBudWxsXG4gICAgICAgIHZhciB0aW1lb3V0SWQgPSBudWxsXG5cbiAgICAgICAgaWYgKHR5cGVvZiBBYm9ydENvbnRyb2xsZXIgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG4gICAgICAgICAgICB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBjb250cm9sbGVyLmFib3J0KClcbiAgICAgICAgICAgIH0sIGNvbmZpZy50aW1lb3V0KVxuICAgICAgICB9XG5cbiAgICAgICAgdmFyIGZldGNoT3B0aW9ucyA9IHtcbiAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgICAgaGVhZGVyczogaGVhZGVycyxcbiAgICAgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KGJvZHkpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY29udHJvbGxlcikge1xuICAgICAgICAgICAgZmV0Y2hPcHRpb25zLnNpZ25hbCA9IGNvbnRyb2xsZXIuc2lnbmFsXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZmV0Y2godXJsLCBmZXRjaE9wdGlvbnMpXG4gICAgICAgICAgICAudGhlbihmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICBpZiAodGltZW91dElkKSBjbGVhclRpbWVvdXQodGltZW91dElkKVxuXG4gICAgICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2UudGV4dCgpLnRoZW4oZnVuY3Rpb24gKHRleHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQVBJIHJlcXVlc3QgZmFpbGVkICgnICsgcmVzcG9uc2Uuc3RhdHVzICsgJyk6ICcgKyB0ZXh0LnN1YnN0cmluZygwLCAyMDApKVxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2UuanNvbigpXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHtcbiAgICAgICAgICAgICAgICAvLyBQYXJzZSBPcGVuQUkgcmVzcG9uc2Ugc3RydWN0dXJlXG4gICAgICAgICAgICAgICAgaWYgKGRhdGEgJiYgZGF0YS5jaG9pY2VzICYmIGRhdGEuY2hvaWNlc1swXSAmJiBkYXRhLmNob2ljZXNbMF0ubWVzc2FnZSkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgY29udGVudCA9IGRhdGEuY2hvaWNlc1swXS5tZXNzYWdlLmNvbnRlbnRcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGNvbnRlbnQpXG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRyeSB0byBleHRyYWN0IEpTT04gZnJvbSBjb250ZW50IGlmIGl0J3Mgd3JhcHBlZCBpbiBtYXJrZG93blxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGpzb25NYXRjaCA9IGNvbnRlbnQubWF0Y2goL1xce1tcXHNcXFNdKlxcfS8pXG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoanNvbk1hdGNoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoanNvbk1hdGNoWzBdKVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gcGFyc2UgQUkgcmVzcG9uc2UgYXMgSlNPTjogJyArIGNvbnRlbnQuc3Vic3RyaW5nKDAsIDIwMCkpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBJZiB0aGUgcmVzcG9uc2UgSVMgdGhlIHBhcnNlZCBjb250ZW50IChwcm94eSBtaWdodCBmb3J3YXJkIGRpZmZlcmVudGx5KVxuICAgICAgICAgICAgICAgIGlmIChkYXRhICYmIChkYXRhLnNlbGVjdGVkVmFyaWFudCB8fCBkYXRhLmhlYWRsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZGF0YVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCBBUEkgcmVzcG9uc2Ugc3RydWN0dXJlJylcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRpbWVvdXRJZCkgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZClcblxuICAgICAgICAgICAgICAgIGlmIChlcnJvci5uYW1lID09PSAnQWJvcnRFcnJvcicpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBSSByZXF1ZXN0IHRpbWVkIG91dCBhZnRlciAnICsgY29uZmlnLnRpbWVvdXQgKyAnbXMnKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvclxuICAgICAgICAgICAgfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxsIE9wZW5BSSB3aXRoIHJldHJ5IGxvZ2ljXG4gICAgICogQHBhcmFtIHtBcnJheX0gbWVzc2FnZXMgLSBDaGF0IG1lc3NhZ2VzXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIE5vcm1hbGl6ZWQgY29uZmlnXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0XSAtIEN1cnJlbnQgYXR0ZW1wdCBudW1iZXJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSAtIFBhcnNlZCBBSSByZXNwb25zZVxuICAgICAqL1xuICAgIGZ1bmN0aW9uIGNhbGxXaXRoUmV0cnkobWVzc2FnZXMsIGNvbmZpZywgYXR0ZW1wdCkge1xuICAgICAgICBhdHRlbXB0ID0gYXR0ZW1wdCB8fCAwXG5cbiAgICAgICAgcmV0dXJuIGNhbGxPcGVuQUkobWVzc2FnZXMsIGNvbmZpZykuY2F0Y2goZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICAgICAgICBpZiAoYXR0ZW1wdCA8IGNvbmZpZy5tYXhSZXRyaWVzKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdbTHVhIEFJXSBSZXRyeSBhdHRlbXB0ICcgKyAoYXR0ZW1wdCArIDEpICsgJzonLCBlcnJvci5tZXNzYWdlKVxuICAgICAgICAgICAgICAgIC8vIEV4cG9uZW50aWFsIGJhY2tvZmY6IDUwMG1zLCAxMDAwbXMsIDIwMDBtcy4uLlxuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSkge1xuICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoY2FsbFdpdGhSZXRyeShtZXNzYWdlcywgY29uZmlnLCBhdHRlbXB0ICsgMSkpXG4gICAgICAgICAgICAgICAgICAgIH0sIDUwMCAqIE1hdGgucG93KDIsIGF0dGVtcHQpKVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aHJvdyBlcnJvclxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBSZXNwb25zZSBWYWxpZGF0aW9uXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgLyoqXG4gICAgICogVmFsaWRhdGUgYSBTRUxFQ1RJT04gbW9kZSByZXNwb25zZVxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSByZXNwb25zZSAtIFBhcnNlZCBBSSByZXNwb25zZVxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YXJpYW50cyAtIEF2YWlsYWJsZSB2YXJpYW50cyAodG8gdmFsaWRhdGUga2V5IGV4aXN0cylcbiAgICAgKiBAcmV0dXJucyB7T2JqZWN0fSAtIHsgdmFsaWQ6IGJvb2xlYW4sIGVycm9yPzogc3RyaW5nIH1cbiAgICAgKi9cbiAgICBmdW5jdGlvbiB2YWxpZGF0ZVNlbGVjdFJlc3BvbnNlKHJlc3BvbnNlLCB2YXJpYW50cykge1xuICAgICAgICBpZiAoIXJlc3BvbnNlIHx8IHR5cGVvZiByZXNwb25zZSAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdSZXNwb25zZSBpcyBub3QgYW4gb2JqZWN0JyB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXJlc3BvbnNlLnNlbGVjdGVkVmFyaWFudCB8fCB0eXBlb2YgcmVzcG9uc2Uuc2VsZWN0ZWRWYXJpYW50ICE9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIHsgdmFsaWQ6IGZhbHNlLCBlcnJvcjogJ01pc3Npbmcgb3IgaW52YWxpZCBcInNlbGVjdGVkVmFyaWFudFwiIGZpZWxkJyB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayB0aGF0IHRoZSBzZWxlY3RlZCB2YXJpYW50IGFjdHVhbGx5IGV4aXN0c1xuICAgICAgICBpZiAoIXZhcmlhbnRzW3Jlc3BvbnNlLnNlbGVjdGVkVmFyaWFudF0pIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdTZWxlY3RlZCB2YXJpYW50IFwiJyArIHJlc3BvbnNlLnNlbGVjdGVkVmFyaWFudCArICdcIiBkb2VzIG5vdCBleGlzdCBpbiB0ZW1wbGF0ZXMnIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7IHZhbGlkOiB0cnVlIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBWYWxpZGF0ZSBhIEdFTkVSQVRJT04gbW9kZSByZXNwb25zZVxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSByZXNwb25zZSAtIFBhcnNlZCBBSSByZXNwb25zZVxuICAgICAqIEByZXR1cm5zIHtPYmplY3R9IC0geyB2YWxpZDogYm9vbGVhbiwgZXJyb3I/OiBzdHJpbmcgfVxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHZhbGlkYXRlR2VuZXJhdGVSZXNwb25zZShyZXNwb25zZSkge1xuICAgICAgICBpZiAoIXJlc3BvbnNlIHx8IHR5cGVvZiByZXNwb25zZSAhPT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdSZXNwb25zZSBpcyBub3QgYW4gb2JqZWN0JyB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXJlc3BvbnNlLmhlYWRsaW5lIHx8IHR5cGVvZiByZXNwb25zZS5oZWFkbGluZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdNaXNzaW5nIG9yIGludmFsaWQgXCJoZWFkbGluZVwiIGZpZWxkJyB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXJlc3BvbnNlLnN1YmhlYWRsaW5lIHx8IHR5cGVvZiByZXNwb25zZS5zdWJoZWFkbGluZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdNaXNzaW5nIG9yIGludmFsaWQgXCJzdWJoZWFkbGluZVwiIGZpZWxkJyB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXJlc3BvbnNlLmN0YUxhYmVsIHx8IHR5cGVvZiByZXNwb25zZS5jdGFMYWJlbCAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3I6ICdNaXNzaW5nIG9yIGludmFsaWQgXCJjdGFMYWJlbFwiIGZpZWxkJyB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4geyB2YWxpZDogdHJ1ZSB9XG4gICAgfVxuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIEFJIERlY2lzaW9uIEVuZ2luZVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIE1haW4gQUkgZGVjaXNpb24gZnVuY3Rpb25cbiAgICAgKiBHYXRoZXJzIGNvbnRleHQsIGJ1aWxkcyBwcm9tcHRzLCBjYWxscyBBUEksIHZhbGlkYXRlcyByZXNwb25zZVxuICAgICAqXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGNvbnRleHQgLSBDdXJyZW50IFVUTS9yZWZlcnJlci9kZXZpY2UgY29udGV4dFxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRnVsbCBvcHRpb25zIG9iamVjdFxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zLnRlbXBsYXRlcyAtIFVzZXItcHJvdmlkZWQgdGVtcGxhdGVzIChSRVFVSVJFRClcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucy5haUNvbmZpZyAtIEFJIGNvbmZpZ3VyYXRpb24gKFJFUVVJUkVEKVxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubG9nXSAtIEVuYWJsZSBsb2dnaW5nIChkZWZhdWx0OiB0cnVlKVxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IC0gRGVjaXNpb24gcmVzdWx0IHsgdGVtcGxhdGUsIGludGVudCwgc291cmNlLCBjb250ZXh0LCBhaVJlc3BvbnNlIH1cbiAgICAgKi9cbiAgICBmdW5jdGlvbiBhaURlY2lkZShjb250ZXh0LCBvcHRpb25zKSB7XG4gICAgICAgIHZhciBjb25maWcgPSBub3JtYWxpemVDb25maWcob3B0aW9ucy5haUNvbmZpZylcblxuICAgICAgICBpZiAoIWNvbmZpZy52YWxpZCkge1xuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcignW0x1YSBBSV0gJyArIGNvbmZpZy5lcnJvcikpXG4gICAgICAgIH1cblxuICAgICAgICB2YXIgdGVtcGxhdGVzID0gb3B0aW9ucy50ZW1wbGF0ZXMgfHwge31cbiAgICAgICAgdmFyIGxvZyA9IG9wdGlvbnMubG9nICE9PSBmYWxzZVxuXG4gICAgICAgIC8vIENoZWNrIGNhY2hlIGZpcnN0XG4gICAgICAgIGlmIChjb25maWcuY2FjaGVEZWNpc2lvbnMpIHtcbiAgICAgICAgICAgIHZhciBjYWNoZUtleSA9IGJ1aWxkQ2FjaGVLZXkoY29udGV4dCwgY29uZmlnLm1vZGUpXG4gICAgICAgICAgICB2YXIgY2FjaGVkID0gcmVhZENhY2hlKGNhY2hlS2V5LCBjb25maWcuY2FjaGVEdXJhdGlvbilcbiAgICAgICAgICAgIGlmIChjYWNoZWQpIHtcbiAgICAgICAgICAgICAgICBpZiAobG9nKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdbTHVhIEFJXSBVc2luZyBjYWNoZWQgZGVjaXNpb246JywgY2FjaGVkLmludGVudClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgY2FjaGVkLnNvdXJjZSA9ICdhaS1jYWNoZWQnXG4gICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShjYWNoZWQpXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBHZXQgd2VpZ2h0ZWQgaGlzdG9yeVxuICAgICAgICB2YXIgSGlzdG9yeU1vZHVsZSA9IHJvb3QuTHVhV2VpZ2h0ZWRIaXN0b3J5XG4gICAgICAgIHZhciB3ZWlnaHRlZEhpc3RvcnkgPSAnJ1xuICAgICAgICB2YXIgcHJlZmVyZW5jZXMgPSB7fVxuICAgICAgICB2YXIgaGlzdG9yeSA9IG51bGxcblxuICAgICAgICBpZiAoY29uZmlnLmhpc3RvcnlFbmFibGVkICYmIEhpc3RvcnlNb2R1bGUpIHtcbiAgICAgICAgICAgIGhpc3RvcnkgPSBIaXN0b3J5TW9kdWxlLmdldEhpc3RvcnkoKVxuICAgICAgICAgICAgdmFyIHdlaWdodGVkID0gSGlzdG9yeU1vZHVsZS5idWlsZFdlaWdodGVkQ29udGV4dChoaXN0b3J5LCB7XG4gICAgICAgICAgICAgICAgZGVjYXlSYXRlOiBjb25maWcuaGlzdG9yeURlY2F5UmF0ZVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHByZWZlcmVuY2VzID0gSGlzdG9yeU1vZHVsZS5hZ2dyZWdhdGVQcmVmZXJlbmNlcyh3ZWlnaHRlZClcbiAgICAgICAgICAgIHdlaWdodGVkSGlzdG9yeSA9IEhpc3RvcnlNb2R1bGUuZm9ybWF0Rm9yUHJvbXB0KHdlaWdodGVkKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gR2V0IHByb21wdCBtb2R1bGVcbiAgICAgICAgdmFyIFByb21wdHNNb2R1bGUgPSByb290Lkx1YVByb21wdHNcblxuICAgICAgICBpZiAoIVByb21wdHNNb2R1bGUpIHtcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoJ1tMdWEgQUldIEx1YVByb21wdHMgbW9kdWxlIG5vdCBsb2FkZWQuIEluY2x1ZGUgcHJvbXB0cy9wZXJzb25hbGl6YXRpb24tcHJvbXB0cy5qcycpKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQnVpbGQgcHJvbXB0IHBhcmFtZXRlcnNcbiAgICAgICAgdmFyIHByb21wdFBhcmFtcyA9IHtcbiAgICAgICAgICAgIGNvbnRleHQ6IGNvbnRleHQsXG4gICAgICAgICAgICB3ZWlnaHRlZEhpc3Rvcnk6IHdlaWdodGVkSGlzdG9yeSxcbiAgICAgICAgICAgIHByZWZlcmVuY2VzOiBwcmVmZXJlbmNlc1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNvbmZpZy5tb2RlID09PSAnc2VsZWN0Jykge1xuICAgICAgICAgICAgcHJvbXB0UGFyYW1zLnZhcmlhbnRzID0gdGVtcGxhdGVzXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBHZW5lcmF0ZSBtb2RlXG4gICAgICAgICAgICBwcm9tcHRQYXJhbXMuYnJhbmRDb250ZXh0ID0gY29uZmlnLmJyYW5kQ29udGV4dFxuICAgICAgICAgICAgcHJvbXB0UGFyYW1zLmZhbGxiYWNrVGVtcGxhdGUgPSB0ZW1wbGF0ZXNbJ2RlZmF1bHQnXSB8fCB0ZW1wbGF0ZXNbT2JqZWN0LmtleXModGVtcGxhdGVzKVswXV0gfHwgbnVsbFxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQnVpbGQgbWVzc2FnZXNcbiAgICAgICAgdmFyIG1lc3NhZ2VzID0gUHJvbXB0c01vZHVsZS5idWlsZE1lc3NhZ2VzKGNvbmZpZy5tb2RlLCBwcm9tcHRQYXJhbXMsIGNvbmZpZy5jdXN0b21Qcm9tcHRzKVxuXG4gICAgICAgIHZhciBzdGFydFRpbWUgPSBEYXRlLm5vdygpXG5cbiAgICAgICAgLy8gQ2FsbCBBSVxuICAgICAgICByZXR1cm4gY2FsbFdpdGhSZXRyeShtZXNzYWdlcywgY29uZmlnKS50aGVuKGZ1bmN0aW9uIChhaVJlc3BvbnNlKSB7XG4gICAgICAgICAgICB2YXIgbGF0ZW5jeSA9IERhdGUubm93KCkgLSBzdGFydFRpbWVcbiAgICAgICAgICAgIHZhciBkZWNpc2lvblxuXG4gICAgICAgICAgICBpZiAoY29uZmlnLm1vZGUgPT09ICdzZWxlY3QnKSB7XG4gICAgICAgICAgICAgICAgLy8gVmFsaWRhdGUgc2VsZWN0aW9uXG4gICAgICAgICAgICAgICAgdmFyIHNlbGVjdFZhbGlkYXRpb24gPSB2YWxpZGF0ZVNlbGVjdFJlc3BvbnNlKGFpUmVzcG9uc2UsIHRlbXBsYXRlcylcbiAgICAgICAgICAgICAgICBpZiAoIXNlbGVjdFZhbGlkYXRpb24udmFsaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIEFJIHNlbGVjdGlvbjogJyArIHNlbGVjdFZhbGlkYXRpb24uZXJyb3IpXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZGVjaXNpb24gPSB7XG4gICAgICAgICAgICAgICAgICAgIHRlbXBsYXRlOiB0ZW1wbGF0ZXNbYWlSZXNwb25zZS5zZWxlY3RlZFZhcmlhbnRdLFxuICAgICAgICAgICAgICAgICAgICBpbnRlbnQ6IGFpUmVzcG9uc2Uuc2VsZWN0ZWRWYXJpYW50LFxuICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICdhaScsXG4gICAgICAgICAgICAgICAgICAgIGNvbnRleHQ6IGNvbnRleHQsXG4gICAgICAgICAgICAgICAgICAgIGFpUmVzcG9uc2U6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZGVuY2U6IGFpUmVzcG9uc2UuY29uZmlkZW5jZSB8fCBudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uaW5nOiBhaVJlc3BvbnNlLnJlYXNvbmluZyB8fCBudWxsLFxuICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZW5jeTogbGF0ZW5jeSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsOiBjb25maWcubW9kZWwsXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RlOiAnc2VsZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhY2hlZDogZmFsc2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gVmFsaWRhdGUgZ2VuZXJhdGlvblxuICAgICAgICAgICAgICAgIHZhciBnZW5WYWxpZGF0aW9uID0gdmFsaWRhdGVHZW5lcmF0ZVJlc3BvbnNlKGFpUmVzcG9uc2UpXG4gICAgICAgICAgICAgICAgaWYgKCFnZW5WYWxpZGF0aW9uLnZhbGlkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBBSSBnZW5lcmF0aW9uOiAnICsgZ2VuVmFsaWRhdGlvbi5lcnJvcilcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBCdWlsZCBhIHRlbXBsYXRlIGZyb20gZ2VuZXJhdGVkIGNvbnRlbnRcbiAgICAgICAgICAgICAgICB2YXIgZ2VuZXJhdGVkVGVtcGxhdGUgPSB7XG4gICAgICAgICAgICAgICAgICAgIGhlYWRsaW5lOiBhaVJlc3BvbnNlLmhlYWRsaW5lLFxuICAgICAgICAgICAgICAgICAgICBzdWJoZWFkbGluZTogYWlSZXNwb25zZS5zdWJoZWFkbGluZSxcbiAgICAgICAgICAgICAgICAgICAgY3RhTGFiZWw6IGFpUmVzcG9uc2UuY3RhTGFiZWwsXG4gICAgICAgICAgICAgICAgICAgIGN0YUxpbms6ICh0ZW1wbGF0ZXNbJ2RlZmF1bHQnXSAmJiB0ZW1wbGF0ZXNbJ2RlZmF1bHQnXS5jdGFMaW5rKSB8fCAnL3Nob3AnLFxuICAgICAgICAgICAgICAgICAgICBpbWFnZTogKHRlbXBsYXRlc1snZGVmYXVsdCddICYmIHRlbXBsYXRlc1snZGVmYXVsdCddLmltYWdlKSB8fCBudWxsXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZGVjaXNpb24gPSB7XG4gICAgICAgICAgICAgICAgICAgIHRlbXBsYXRlOiBnZW5lcmF0ZWRUZW1wbGF0ZSxcbiAgICAgICAgICAgICAgICAgICAgaW50ZW50OiAnYWktZ2VuZXJhdGVkJyxcbiAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnYWknLFxuICAgICAgICAgICAgICAgICAgICBjb250ZXh0OiBjb250ZXh0LFxuICAgICAgICAgICAgICAgICAgICBhaVJlc3BvbnNlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25maWRlbmNlOiBhaVJlc3BvbnNlLmNvbmZpZGVuY2UgfHwgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlYXNvbmluZzogYWlSZXNwb25zZS5yZWFzb25pbmcgfHwgbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhdGVuY3k6IGxhdGVuY3ksXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RlbDogY29uZmlnLm1vZGVsLFxuICAgICAgICAgICAgICAgICAgICAgICAgbW9kZTogJ2dlbmVyYXRlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhY2hlZDogZmFsc2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2FjaGUgdGhlIGRlY2lzaW9uXG4gICAgICAgICAgICBpZiAoY29uZmlnLmNhY2hlRGVjaXNpb25zKSB7XG4gICAgICAgICAgICAgICAgd3JpdGVDYWNoZShidWlsZENhY2hlS2V5KGNvbnRleHQsIGNvbmZpZy5tb2RlKSwgZGVjaXNpb24pXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFJlY29yZCB2aXNpdCB0byBoaXN0b3J5XG4gICAgICAgICAgICBpZiAoY29uZmlnLmhpc3RvcnlFbmFibGVkICYmIEhpc3RvcnlNb2R1bGUpIHtcbiAgICAgICAgICAgICAgICBIaXN0b3J5TW9kdWxlLnJlY29yZFZpc2l0KHtcbiAgICAgICAgICAgICAgICAgICAgY29udGV4dDogY29udGV4dCxcbiAgICAgICAgICAgICAgICAgICAgaW50ZW50OiBkZWNpc2lvbi5pbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkVmFyaWFudDogZGVjaXNpb24uaW50ZW50LFxuICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICdhaScsXG4gICAgICAgICAgICAgICAgICAgIGFpRGVjaXNpb246IHRydWVcbiAgICAgICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgICAgICAgIG1heEhpc3RvcnlTaXplOiBjb25maWcubWF4SGlzdG9yeVNpemVcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAobG9nKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coJ1tMdWEgQUldIERlY2lzaW9uIG1hZGU6Jywge1xuICAgICAgICAgICAgICAgICAgICBtb2RlOiBjb25maWcubW9kZSxcbiAgICAgICAgICAgICAgICAgICAgaW50ZW50OiBkZWNpc2lvbi5pbnRlbnQsXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJ2FpJyxcbiAgICAgICAgICAgICAgICAgICAgY29uZmlkZW5jZTogZGVjaXNpb24uYWlSZXNwb25zZS5jb25maWRlbmNlLFxuICAgICAgICAgICAgICAgICAgICBsYXRlbmN5OiBsYXRlbmN5ICsgJ21zJyxcbiAgICAgICAgICAgICAgICAgICAgbW9kZWw6IGNvbmZpZy5tb2RlbFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBkZWNpc2lvblxuXG4gICAgICAgIH0pLmNhdGNoKGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgdmFyIGxhdGVuY3kgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lXG5cbiAgICAgICAgICAgIGlmIChsb2cpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ1tMdWEgQUldIEVycm9yIGFmdGVyICcgKyBsYXRlbmN5ICsgJ21zOicsIGVycm9yLm1lc3NhZ2UpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRocm93IGVycm9yXG4gICAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIEludGVncmF0aW9uIEhlbHBlcnNcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICAvKipcbiAgICAgKiBIaWdoLWxldmVsIEFJIHBlcnNvbmFsaXplIGZ1bmN0aW9uXG4gICAgICogV3JhcHMgYWlEZWNpZGUgd2l0aCBmdWxsIGNvbnRleHQgZ2F0aGVyaW5nIGFuZCBET00gYXBwbGljYXRpb25cbiAgICAgKiBEZXNpZ25lZCB0byBiZSBjYWxsZWQgZnJvbSB0aGUgbWFpbiBwZXJzb25hbGl6ZSgpIGZ1bmN0aW9uXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIEZ1bGwgcGVyc29uYWxpemF0aW9uIG9wdGlvbnNcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucy50ZW1wbGF0ZXMgLSBVc2VyIHRlbXBsYXRlcyAoUkVRVUlSRUQpXG4gICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMuYWlDb25maWcgLSBBSSBjb25maWd1cmF0aW9uIChSRVFVSVJFRClcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnMuY29udGV4dF0gLSBQcmUtY29tcHV0ZWQgY29udGV4dFxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubG9nXSAtIEVuYWJsZSBsb2dnaW5nXG4gICAgICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gLSBEZWNpc2lvbiByZXN1bHRcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBwZXJzb25hbGl6ZVdpdGhBSShvcHRpb25zKSB7XG4gICAgICAgIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9XG5cbiAgICAgICAgLy8gR2V0IGNvbnRleHRcbiAgICAgICAgdmFyIGNvbnRleHQgPSBvcHRpb25zLmNvbnRleHRcblxuICAgICAgICBpZiAoIWNvbnRleHQpIHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBnZXQgY29udGV4dCBmcm9tIFVUTSBtb2R1bGVzXG4gICAgICAgICAgICB2YXIgdXRtTW9kdWxlID0gcm9vdC5MdWFVVE1QZXJzb25hbGl6ZSB8fCByb290Lkx1YVVUTVxuICAgICAgICAgICAgaWYgKHV0bU1vZHVsZSAmJiB0eXBlb2YgdXRtTW9kdWxlLmdldENvbnRleHQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICBjb250ZXh0ID0gdXRtTW9kdWxlLmdldENvbnRleHQoKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb250ZXh0ID0ge1xuICAgICAgICAgICAgICAgICAgICB1dG06IHt9LFxuICAgICAgICAgICAgICAgICAgICByZWZlcnJlcjogeyBzb3VyY2U6ICdkaXJlY3QnLCBjYXRlZ29yeTogJ2RpcmVjdCcsIHVybDogJycgfSxcbiAgICAgICAgICAgICAgICAgICAgdXNlckFnZW50OiB7IHJhdzogJycsIGlzTW9iaWxlOiBmYWxzZSwgaXNUYWJsZXQ6IGZhbHNlLCBpc0Rlc2t0b3A6IHRydWUgfSxcbiAgICAgICAgICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgICAgICAgICBoYXNVVE06IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBwcmltYXJ5SW50ZW50OiAnZGVmYXVsdCdcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gYWlEZWNpZGUoY29udGV4dCwgb3B0aW9ucylcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBRdWljayBjaGVjazogaXMgQUkgcHJvcGVybHkgY29uZmlndXJlZCBhbmQgcmVhZHk/XG4gICAgICogQHBhcmFtIHtPYmplY3R9IGFpQ29uZmlnIC0gQUkgY29uZmlnIHRvIGNoZWNrXG4gICAgICogQHJldHVybnMge09iamVjdH0gLSB7IHJlYWR5OiBib29sZWFuLCBlcnJvcj86IHN0cmluZyB9XG4gICAgICovXG4gICAgZnVuY3Rpb24gaXNSZWFkeShhaUNvbmZpZykge1xuICAgICAgICB2YXIgY29uZmlnID0gbm9ybWFsaXplQ29uZmlnKGFpQ29uZmlnKVxuICAgICAgICBpZiAoIWNvbmZpZy52YWxpZCkge1xuICAgICAgICAgICAgcmV0dXJuIHsgcmVhZHk6IGZhbHNlLCBlcnJvcjogY29uZmlnLmVycm9yIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGRlcGVuZGVuY2llc1xuICAgICAgICBpZiAoIXJvb3QuTHVhUHJvbXB0cykge1xuICAgICAgICAgICAgcmV0dXJuIHsgcmVhZHk6IGZhbHNlLCBlcnJvcjogJ0x1YVByb21wdHMgbW9kdWxlIG5vdCBsb2FkZWQnIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEhpc3RvcnkgaXMgb3B0aW9uYWwgYnV0IHJlY29tbWVuZGVkXG4gICAgICAgIGlmICghcm9vdC5MdWFXZWlnaHRlZEhpc3RvcnkpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybignW0x1YSBBSV0gTHVhV2VpZ2h0ZWRIaXN0b3J5IG5vdCBsb2FkZWQuIEhpc3RvcnkgZmVhdHVyZXMgZGlzYWJsZWQuJylcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7IHJlYWR5OiB0cnVlIH1cbiAgICB9XG5cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gUHVibGljIEFQSVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIHZhciBMdWFBSVBlcnNvbmFsaXplID0ge1xuICAgICAgICAvLyBDb3JlXG4gICAgICAgIGRlY2lkZTogYWlEZWNpZGUsXG4gICAgICAgIHBlcnNvbmFsaXplV2l0aEFJOiBwZXJzb25hbGl6ZVdpdGhBSSxcbiAgICAgICAgaXNSZWFkeTogaXNSZWFkeSxcblxuICAgICAgICAvLyBDb25maWd1cmF0aW9uXG4gICAgICAgIG5vcm1hbGl6ZUNvbmZpZzogbm9ybWFsaXplQ29uZmlnLFxuXG4gICAgICAgIC8vIEFQSSBjb21tdW5pY2F0aW9uXG4gICAgICAgIGNhbGxPcGVuQUk6IGNhbGxPcGVuQUksXG5cbiAgICAgICAgLy8gVmFsaWRhdGlvblxuICAgICAgICB2YWxpZGF0ZVNlbGVjdFJlc3BvbnNlOiB2YWxpZGF0ZVNlbGVjdFJlc3BvbnNlLFxuICAgICAgICB2YWxpZGF0ZUdlbmVyYXRlUmVzcG9uc2U6IHZhbGlkYXRlR2VuZXJhdGVSZXNwb25zZSxcblxuICAgICAgICAvLyBDYWNoZVxuICAgICAgICBjbGVhckNhY2hlOiBjbGVhckNhY2hlLFxuXG4gICAgICAgIC8vIENvbnN0YW50c1xuICAgICAgICBPUEVOQUlfQkFTRV9VUkw6IE9QRU5BSV9CQVNFX1VSTCxcbiAgICAgICAgREVGQVVMVF9NT0RFTDogREVGQVVMVF9NT0RFTCxcbiAgICAgICAgREVGQVVMVF9USU1FT1VUOiBERUZBVUxUX1RJTUVPVVQsXG4gICAgICAgIERFRkFVTFRfQ0FDSEVfRFVSQVRJT046IERFRkFVTFRfQ0FDSEVfRFVSQVRJT05cbiAgICB9XG5cbiAgICAvLyBSZWdpc3RlciBnbG9iYWxseVxuICAgIHJvb3QuTHVhQUlQZXJzb25hbGl6ZSA9IEx1YUFJUGVyc29uYWxpemVcblxufSkodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgPyB3aW5kb3cgOiB0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyA/IGdsb2JhbCA6IHRoaXMpXG4iXSwibWFwcGluZ3MiOiI7Ozs7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUMsQ0FBQyxVQUFVQSxJQUFJLEVBQUU7RUFDZCxZQUFZOztFQUVaO0VBQ0E7RUFDQTtFQUVBLElBQUlDLGVBQWUsR0FBRyw0Q0FBNEM7RUFDbEUsSUFBSUMsYUFBYSxHQUFHLGFBQWE7RUFDakMsSUFBSUMsZUFBZSxHQUFHLElBQUk7RUFDMUIsSUFBSUMsa0JBQWtCLEdBQUcsR0FBRztFQUM1QixJQUFJQyxtQkFBbUIsR0FBRyxHQUFHO0VBQzdCLElBQUlDLG1CQUFtQixHQUFHLENBQUM7RUFDM0IsSUFBSUMsZ0JBQWdCLEdBQUcsZUFBZTtFQUN0QyxJQUFJQyxzQkFBc0IsR0FBRyxPQUFPLEVBQUM7O0VBRXJDO0VBQ0E7RUFDQTs7RUFFQTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0VBQ0ksU0FBU0MsZUFB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
|