mohen 1.0.0 → 1.2.0
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 +56 -2
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +162 -19
- package/package.json +1 -1
- package/src/logger.ts +193 -17
- package/test/logger.test.ts +206 -1
package/README.md
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.png" alt="mohen logo" width="200" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">mohen 墨痕</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>A simple, unified request/response logger for Express and tRPC</strong><br>
|
|
9
|
+
Writes to a single file with JSON lines format
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/mohen"><img src="https://img.shields.io/npm/v/mohen.svg" alt="npm version"></a>
|
|
14
|
+
<a href="https://github.com/ivanleomk/mohen/actions"><img src="https://github.com/ivanleomk/mohen/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
15
|
+
<a href="https://github.com/ivanleomk/mohen/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/mohen.svg" alt="license"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
4
19
|
|
|
5
20
|
## Features
|
|
6
21
|
|
|
@@ -131,9 +146,34 @@ createLogger(filePath, {
|
|
|
131
146
|
maxSizeBytes: 10 * 1024 * 1024, // Max file size before truncation (default: 10MB)
|
|
132
147
|
includeHeaders: false, // Log request headers (default: false)
|
|
133
148
|
redact: ['password', 'token'], // Fields to redact (default: password, token, authorization, cookie)
|
|
149
|
+
ignorePaths: ['/health', '/health/*', '/metrics'], // Paths to skip logging (supports wildcards)
|
|
150
|
+
includePaths: ['/api/*'], // Only log these paths (supports wildcards)
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Path Filtering
|
|
155
|
+
|
|
156
|
+
Use `ignorePaths` to skip noisy endpoints like health checks:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const logger = createLogger('./logs/app.log', {
|
|
160
|
+
ignorePaths: ['/health', '/health/*', '/metrics', '/favicon.ico'],
|
|
134
161
|
});
|
|
135
162
|
```
|
|
136
163
|
|
|
164
|
+
Or use `includePaths` to only log specific routes:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const logger = createLogger('./logs/app.log', {
|
|
168
|
+
includePaths: ['/api/*', '/trpc/*'],
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Wildcard patterns:
|
|
173
|
+
- `/health` - matches exactly `/health`
|
|
174
|
+
- `/health/*` - matches `/health/live`, `/health/ready`, etc.
|
|
175
|
+
- `/api/*` - matches any path starting with `/api/`
|
|
176
|
+
|
|
137
177
|
## Log Format
|
|
138
178
|
|
|
139
179
|
Each line is a JSON object with the following structure:
|
|
@@ -162,6 +202,20 @@ Each line is a JSON object with the following structure:
|
|
|
162
202
|
}
|
|
163
203
|
```
|
|
164
204
|
|
|
205
|
+
For SSE streaming responses with text-delta events (like LLM responses), the text is automatically aggregated:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"type": "http",
|
|
210
|
+
"path": "/api/chat",
|
|
211
|
+
"response": {
|
|
212
|
+
"streaming": true,
|
|
213
|
+
"chunks": [{"type": "start"}, {"type": "text-delta", "delta": "Hello"}, ...],
|
|
214
|
+
"text": "Hello world! This is the complete aggregated response."
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
165
219
|
## File Size Management
|
|
166
220
|
|
|
167
221
|
When the log file exceeds `maxSizeBytes`, the oldest 75% of log entries are removed, keeping the most recent 25%. This happens automatically before each write.
|
package/dist/logger.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface LogEntry {
|
|
|
16
16
|
body?: unknown;
|
|
17
17
|
streaming?: boolean;
|
|
18
18
|
chunks?: unknown[];
|
|
19
|
+
text?: string;
|
|
19
20
|
};
|
|
20
21
|
error?: {
|
|
21
22
|
message: string;
|
|
@@ -27,6 +28,8 @@ interface LoggerOptions {
|
|
|
27
28
|
maxSizeBytes?: number;
|
|
28
29
|
includeHeaders?: boolean;
|
|
29
30
|
redact?: string[];
|
|
31
|
+
ignorePaths?: string[];
|
|
32
|
+
includePaths?: string[];
|
|
30
33
|
}
|
|
31
34
|
declare global {
|
|
32
35
|
namespace Express {
|
package/dist/logger.js
CHANGED
|
@@ -47,6 +47,8 @@ class UnifiedLogger {
|
|
|
47
47
|
this.maxSizeBytes = options.maxSizeBytes ?? 10 * 1024 * 1024; // 10MB default
|
|
48
48
|
this.includeHeaders = options.includeHeaders ?? false;
|
|
49
49
|
this.redactFields = new Set(options.redact ?? ['password', 'token', 'authorization', 'cookie']);
|
|
50
|
+
this.ignorePaths = options.ignorePaths ?? [];
|
|
51
|
+
this.includePaths = options.includePaths ?? [];
|
|
50
52
|
// Ensure directory exists
|
|
51
53
|
const dir = path.dirname(this.filePath);
|
|
52
54
|
if (!fs.existsSync(dir)) {
|
|
@@ -56,6 +58,29 @@ class UnifiedLogger {
|
|
|
56
58
|
generateRequestId() {
|
|
57
59
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
58
60
|
}
|
|
61
|
+
matchPath(pattern, requestPath) {
|
|
62
|
+
// Convert wildcard pattern to regex
|
|
63
|
+
// /api/* matches /api/anything
|
|
64
|
+
// /health matches exactly /health
|
|
65
|
+
const regexPattern = pattern
|
|
66
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
|
|
67
|
+
.replace(/\*/g, '.*'); // Convert * to .*
|
|
68
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
69
|
+
return regex.test(requestPath);
|
|
70
|
+
}
|
|
71
|
+
shouldLog(requestPath) {
|
|
72
|
+
// Extract path without query string
|
|
73
|
+
const pathOnly = requestPath.split('?')[0];
|
|
74
|
+
// If includePaths is set, only log matching paths
|
|
75
|
+
if (this.includePaths.length > 0) {
|
|
76
|
+
return this.includePaths.some((pattern) => this.matchPath(pattern, pathOnly));
|
|
77
|
+
}
|
|
78
|
+
// If ignorePaths is set, skip matching paths
|
|
79
|
+
if (this.ignorePaths.length > 0) {
|
|
80
|
+
return !this.ignorePaths.some((pattern) => this.matchPath(pattern, pathOnly));
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
59
84
|
redact(obj) {
|
|
60
85
|
if (obj === null || obj === undefined)
|
|
61
86
|
return obj;
|
|
@@ -107,18 +132,95 @@ class UnifiedLogger {
|
|
|
107
132
|
console.error('Logger write error:', err);
|
|
108
133
|
}
|
|
109
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Decode chunk to string, handling Uint8Array/Buffer from TextEncoderStream
|
|
137
|
+
*/
|
|
138
|
+
decodeChunk(chunk) {
|
|
139
|
+
if (chunk === null || chunk === undefined) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
// Handle Uint8Array (from TextEncoderStream in AI SDK)
|
|
143
|
+
if (chunk instanceof Uint8Array) {
|
|
144
|
+
return Buffer.from(chunk).toString('utf-8');
|
|
145
|
+
}
|
|
146
|
+
// Handle Buffer
|
|
147
|
+
if (Buffer.isBuffer(chunk)) {
|
|
148
|
+
return chunk.toString('utf-8');
|
|
149
|
+
}
|
|
150
|
+
// Handle string
|
|
151
|
+
if (typeof chunk === 'string') {
|
|
152
|
+
return chunk;
|
|
153
|
+
}
|
|
154
|
+
// Fallback: try toString but check if it looks like byte array
|
|
155
|
+
const str = chunk.toString();
|
|
156
|
+
// Detect if toString produced a comma-separated byte list like "100,97,116,97"
|
|
157
|
+
// This happens when Uint8Array.toString() is called without proper decoding
|
|
158
|
+
if (/^\d+(,\d+)*$/.test(str) && str.includes(',')) {
|
|
159
|
+
try {
|
|
160
|
+
const bytes = new Uint8Array(str.split(',').map(Number));
|
|
161
|
+
return Buffer.from(bytes).toString('utf-8');
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return str;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return str;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check if content looks like SSE data
|
|
171
|
+
*/
|
|
172
|
+
looksLikeSSE(content) {
|
|
173
|
+
return content.trimStart().startsWith('data:') ||
|
|
174
|
+
content.includes('\ndata:') ||
|
|
175
|
+
content.trimStart().startsWith('event:');
|
|
176
|
+
}
|
|
110
177
|
// ===========================================================================
|
|
111
178
|
// Express Middleware
|
|
112
179
|
// ===========================================================================
|
|
113
180
|
expressMiddleware() {
|
|
114
181
|
return (req, res, next) => {
|
|
182
|
+
const requestPath = req.originalUrl || req.url;
|
|
183
|
+
// Check if we should log this path
|
|
184
|
+
if (!this.shouldLog(requestPath)) {
|
|
185
|
+
return next();
|
|
186
|
+
}
|
|
115
187
|
const start = Date.now();
|
|
116
188
|
const requestId = this.generateRequestId();
|
|
117
189
|
// Initialize metadata object on request
|
|
118
190
|
req.logMetadata = {};
|
|
119
|
-
// Detect SSE - check
|
|
191
|
+
// Detect SSE - check request Accept header initially
|
|
120
192
|
let isSSE = req.headers.accept === 'text/event-stream';
|
|
121
193
|
const chunks = [];
|
|
194
|
+
const textDeltas = []; // Collect text-delta content
|
|
195
|
+
// Helper to check Content-Type header for SSE
|
|
196
|
+
const checkContentTypeForSSE = (headers) => {
|
|
197
|
+
if (!headers)
|
|
198
|
+
return;
|
|
199
|
+
// headers can be an object or array of [key, value] pairs
|
|
200
|
+
if (Array.isArray(headers)) {
|
|
201
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
202
|
+
const key = headers[i];
|
|
203
|
+
const value = headers[i + 1];
|
|
204
|
+
if (typeof key === 'string' &&
|
|
205
|
+
key.toLowerCase() === 'content-type' &&
|
|
206
|
+
typeof value === 'string' &&
|
|
207
|
+
value.includes('text/event-stream')) {
|
|
208
|
+
isSSE = true;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (typeof headers === 'object') {
|
|
214
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
215
|
+
if (key.toLowerCase() === 'content-type' &&
|
|
216
|
+
typeof value === 'string' &&
|
|
217
|
+
value.includes('text/event-stream')) {
|
|
218
|
+
isSSE = true;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
122
224
|
// Intercept setHeader to detect SSE by Content-Type
|
|
123
225
|
const originalSetHeader = res.setHeader.bind(res);
|
|
124
226
|
res.setHeader = ((name, value) => {
|
|
@@ -129,6 +231,20 @@ class UnifiedLogger {
|
|
|
129
231
|
}
|
|
130
232
|
return originalSetHeader(name, value);
|
|
131
233
|
});
|
|
234
|
+
// Intercept writeHead to detect SSE (used by AI SDK's pipeUIMessageStreamToResponse)
|
|
235
|
+
const originalWriteHead = res.writeHead.bind(res);
|
|
236
|
+
res.writeHead = ((statusCode, statusMessageOrHeaders, maybeHeaders) => {
|
|
237
|
+
// writeHead can be called as:
|
|
238
|
+
// writeHead(statusCode)
|
|
239
|
+
// writeHead(statusCode, headers)
|
|
240
|
+
// writeHead(statusCode, statusMessage, headers)
|
|
241
|
+
let headers = maybeHeaders;
|
|
242
|
+
if (!headers && typeof statusMessageOrHeaders === 'object') {
|
|
243
|
+
headers = statusMessageOrHeaders;
|
|
244
|
+
}
|
|
245
|
+
checkContentTypeForSSE(headers);
|
|
246
|
+
return originalWriteHead(statusCode, statusMessageOrHeaders, maybeHeaders);
|
|
247
|
+
});
|
|
132
248
|
// Capture request info
|
|
133
249
|
const requestInfo = {
|
|
134
250
|
body: req.body,
|
|
@@ -145,12 +261,18 @@ class UnifiedLogger {
|
|
|
145
261
|
let responseBody;
|
|
146
262
|
let logged = false;
|
|
147
263
|
res.write = ((chunk, encodingOrCallback, callback) => {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const chunkStr =
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
|
|
264
|
+
if (chunk) {
|
|
265
|
+
// Properly decode the chunk (handles Uint8Array from TextEncoderStream)
|
|
266
|
+
const chunkStr = this.decodeChunk(chunk);
|
|
267
|
+
// Auto-detect SSE from content if not already detected
|
|
268
|
+
if (!isSSE && this.looksLikeSSE(chunkStr)) {
|
|
269
|
+
isSSE = true;
|
|
270
|
+
}
|
|
271
|
+
if (isSSE) {
|
|
272
|
+
const parsed = this.parseSSEChunk(chunkStr, textDeltas);
|
|
273
|
+
if (parsed) {
|
|
274
|
+
chunks.push(parsed);
|
|
275
|
+
}
|
|
154
276
|
}
|
|
155
277
|
}
|
|
156
278
|
return originalWrite(chunk, encodingOrCallback, callback);
|
|
@@ -162,8 +284,8 @@ class UnifiedLogger {
|
|
|
162
284
|
if (isSSE) {
|
|
163
285
|
// SSE streaming path
|
|
164
286
|
if (chunk) {
|
|
165
|
-
const chunkStr =
|
|
166
|
-
const parsed = this.parseSSEChunk(chunkStr);
|
|
287
|
+
const chunkStr = this.decodeChunk(chunk);
|
|
288
|
+
const parsed = this.parseSSEChunk(chunkStr, textDeltas);
|
|
167
289
|
if (parsed) {
|
|
168
290
|
chunks.push(parsed);
|
|
169
291
|
}
|
|
@@ -173,7 +295,7 @@ class UnifiedLogger {
|
|
|
173
295
|
requestId,
|
|
174
296
|
type: 'http',
|
|
175
297
|
method: req.method,
|
|
176
|
-
path:
|
|
298
|
+
path: requestPath,
|
|
177
299
|
statusCode: res.statusCode,
|
|
178
300
|
duration: Date.now() - start,
|
|
179
301
|
request: requestInfo,
|
|
@@ -182,6 +304,10 @@ class UnifiedLogger {
|
|
|
182
304
|
chunks,
|
|
183
305
|
},
|
|
184
306
|
};
|
|
307
|
+
// Add aggregated text if we collected text-deltas
|
|
308
|
+
if (textDeltas.length > 0) {
|
|
309
|
+
entry.response.text = textDeltas.join('');
|
|
310
|
+
}
|
|
185
311
|
if (req.logMetadata && Object.keys(req.logMetadata).length > 0) {
|
|
186
312
|
entry.metadata = req.logMetadata;
|
|
187
313
|
}
|
|
@@ -194,7 +320,7 @@ class UnifiedLogger {
|
|
|
194
320
|
requestId,
|
|
195
321
|
type: 'http',
|
|
196
322
|
method: req.method,
|
|
197
|
-
path:
|
|
323
|
+
path: requestPath,
|
|
198
324
|
statusCode: res.statusCode,
|
|
199
325
|
duration: Date.now() - start,
|
|
200
326
|
request: requestInfo,
|
|
@@ -219,7 +345,7 @@ class UnifiedLogger {
|
|
|
219
345
|
requestId,
|
|
220
346
|
type: 'http',
|
|
221
347
|
method: req.method,
|
|
222
|
-
path:
|
|
348
|
+
path: requestPath,
|
|
223
349
|
statusCode: res.statusCode,
|
|
224
350
|
duration: Date.now() - start,
|
|
225
351
|
request: requestInfo,
|
|
@@ -253,22 +379,35 @@ class UnifiedLogger {
|
|
|
253
379
|
next();
|
|
254
380
|
};
|
|
255
381
|
}
|
|
256
|
-
parseSSEChunk(raw) {
|
|
382
|
+
parseSSEChunk(raw, textDeltas) {
|
|
257
383
|
const lines = raw.split('\n');
|
|
384
|
+
const results = [];
|
|
258
385
|
for (const line of lines) {
|
|
259
386
|
if (line.startsWith('data: ')) {
|
|
260
387
|
const data = line.slice(6).trim();
|
|
261
|
-
if (data === '[DONE]')
|
|
262
|
-
|
|
388
|
+
if (data === '[DONE]') {
|
|
389
|
+
results.push({ type: 'done' });
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
263
392
|
try {
|
|
264
|
-
|
|
393
|
+
const parsed = JSON.parse(data);
|
|
394
|
+
// Handle text-delta type - collect the delta text
|
|
395
|
+
if (parsed.type === 'text-delta' && typeof parsed.delta === 'string') {
|
|
396
|
+
textDeltas.push(parsed.delta);
|
|
397
|
+
}
|
|
398
|
+
results.push(parsed);
|
|
265
399
|
}
|
|
266
400
|
catch {
|
|
267
|
-
|
|
401
|
+
results.push({ raw: data });
|
|
268
402
|
}
|
|
269
403
|
}
|
|
270
404
|
}
|
|
271
|
-
|
|
405
|
+
// Return single result or array
|
|
406
|
+
if (results.length === 0)
|
|
407
|
+
return null;
|
|
408
|
+
if (results.length === 1)
|
|
409
|
+
return results[0];
|
|
410
|
+
return results;
|
|
272
411
|
}
|
|
273
412
|
// ===========================================================================
|
|
274
413
|
// tRPC Middleware
|
|
@@ -276,6 +415,10 @@ class UnifiedLogger {
|
|
|
276
415
|
trpcMiddleware() {
|
|
277
416
|
const logger = this;
|
|
278
417
|
return async function loggerMiddleware(opts) {
|
|
418
|
+
// Check if we should log this path
|
|
419
|
+
if (!logger.shouldLog(opts.path)) {
|
|
420
|
+
return opts.next();
|
|
421
|
+
}
|
|
279
422
|
const start = Date.now();
|
|
280
423
|
const requestId = logger.generateRequestId();
|
|
281
424
|
// Initialize metadata on context if not present
|
|
@@ -386,4 +529,4 @@ function attachTrpcMetadata(ctx, metadata) {
|
|
|
386
529
|
}
|
|
387
530
|
// Default export for simpler imports
|
|
388
531
|
exports.default = createLogger;
|
|
389
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlaQSxvQ0FzQkM7QUFTRCx3Q0FLQztBQUtELGdEQVFDO0FBbGNELHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUErQzdCLCtFQUErRTtBQUMvRSxvQkFBb0I7QUFDcEIsK0VBQStFO0FBRS9FLE1BQU0sYUFBYTtJQU1qQixZQUFZLFFBQWdCLEVBQUUsVUFBeUIsRUFBRTtRQUN2RCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLENBQUMsWUFBWSxJQUFJLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsZUFBZTtRQUM3RSxJQUFJLENBQUMsY0FBYyxHQUFHLE9BQU8sQ0FBQyxjQUFjLElBQUksS0FBSyxDQUFDO1FBQ3RELElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFFaEcsMEJBQTBCO1FBQzFCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEIsRUFBRSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVPLGlCQUFpQjtRQUN2QixPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNoRixDQUFDO0lBRU8sTUFBTSxDQUFDLEdBQVk7UUFDekIsSUFBSSxHQUFHLEtBQUssSUFBSSxJQUFJLEdBQUcsS0FBSyxTQUFTO1lBQUUsT0FBTyxHQUFHLENBQUM7UUFDbEQsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRO1lBQUUsT0FBTyxHQUFHLENBQUM7UUFFeEMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkIsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDOUMsQ0FBQztRQUVELE1BQU0sTUFBTSxHQUE0QixFQUFFLENBQUM7UUFDM0MsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBOEIsQ0FBQyxFQUFFLENBQUM7WUFDMUUsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDO2dCQUM3QyxNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsWUFBWSxDQUFDO1lBQzdCLENBQUM7aUJBQU0sSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDdEIsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sY0FBYztRQUNwQixJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO2dCQUFFLE9BQU87WUFFMUMsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDekMsSUFBSSxLQUFLLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztnQkFDbkMsb0NBQW9DO2dCQUNwQyxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3hELE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3pDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzdELEVBQUUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUM5QyxDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixPQUFPLENBQUMsS0FBSyxDQUFDLHdCQUF3QixFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQy9DLENBQUM7SUFDSCxDQUFDO0lBRUQsS0FBSyxDQUFDLEtBQWU7UUFDbkIsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFhLENBQUM7WUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBQUM7WUFDbEQsRUFBRSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ3pDLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFDLEtBQUssQ0FBQyxxQkFBcUIsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUM1QyxDQUFDO0lBQ0gsQ0FBQztJQUVELDhFQUE4RTtJQUM5RSxxQkFBcUI7SUFDckIsOEVBQThFO0lBRTlFLGlCQUFpQjtRQUNmLE9BQU8sQ0FBQyxHQUFZLEVBQUUsR0FBYSxFQUFFLElBQWtCLEVBQUUsRUFBRTtZQUN6RCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDekIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFFM0Msd0NBQXdDO1lBQ3hDLEdBQUcsQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO1lBRXJCLDBFQUEwRTtZQUMxRSxJQUFJLEtBQUssR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sS0FBSyxtQkFBbUIsQ0FBQztZQUN2RCxNQUFNLE1BQU0sR0FBYyxFQUFFLENBQUM7WUFFN0Isb0RBQW9EO1lBQ3BELE1BQU0saUJBQWlCLEdBQUcsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbEQsR0FBRyxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsSUFBWSxFQUFFLEtBQTBDLEVBQVksRUFBRTtnQkFDdEYsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFLEtBQUssY0FBYztvQkFDckMsT0FBTyxLQUFLLEtBQUssUUFBUTtvQkFDekIsS0FBSyxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUM7b0JBQ3hDLEtBQUssR0FBRyxJQUFJLENBQUM7Z0JBQ2YsQ0FBQztnQkFDRCxPQUFPLGlCQUFpQixDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztZQUN4QyxDQUFDLENBQXlCLENBQUM7WUFFM0IsdUJBQXVCO1lBQ3ZCLE1BQU0sV0FBVyxHQUF3QjtnQkFDdkMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2dCQUNkLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSzthQUNqQixDQUFDO1lBRUYsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQ3hCLFdBQVcsQ0FBQyxPQUFPLEdBQUcsR0FBRyxDQUFDLE9BQWlDLENBQUM7WUFDOUQsQ0FBQztZQUVELDhDQUE4QztZQUM5QyxNQUFNLGFBQWEsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMxQyxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN0QyxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QyxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QyxJQUFJLFlBQXFCLENBQUM7WUFDMUIsSUFBSSxNQUFNLEdBQUcsS0FBSyxDQUFDO1lBRW5CLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEtBQVUsRUFBRSxrQkFBd0IsRUFBRSxRQUFjLEVBQVcsRUFBRTtnQkFDN0UseUNBQXlDO2dCQUN6QyxJQUFJLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztvQkFDbkIsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDO29CQUNsQyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUM1QyxJQUFJLE1BQU0sRUFBRSxDQUFDO3dCQUNYLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBQ3RCLENBQUM7Z0JBQ0gsQ0FBQztnQkFDRCxPQUFPLGFBQWEsQ0FBQyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDNUQsQ0FBQyxDQUFxQixDQUFDO1lBRXZCLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQVcsRUFBRSxrQkFBd0IsRUFBRSxRQUFjLEVBQVksRUFBRTtnQkFDN0UsSUFBSSxNQUFNO29CQUFFLE9BQU8sV0FBVyxDQUFDLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDcEUsTUFBTSxHQUFHLElBQUksQ0FBQztnQkFFZCxJQUFJLEtBQUssRUFBRSxDQUFDO29CQUNWLHFCQUFxQjtvQkFDckIsSUFBSSxLQUFLLEVBQUUsQ0FBQzt3QkFDVixNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7d0JBQ2xDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7d0JBQzVDLElBQUksTUFBTSxFQUFFLENBQUM7NEJBQ1gsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQzt3QkFDdEIsQ0FBQztvQkFDSCxDQUFDO29CQUVELE1BQU0sS0FBSyxHQUFhO3dCQUN0QixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUU7d0JBQ25DLFNBQVM7d0JBQ1QsSUFBSSxFQUFFLE1BQU07d0JBQ1osTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNO3dCQUNsQixJQUFJLEVBQUUsR0FBRyxDQUFDLFdBQVcsSUFBSSxHQUFHLENBQUMsR0FBRzt3QkFDaEMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxVQUFVO3dCQUMxQixRQUFRLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUs7d0JBQzVCLE9BQU8sRUFBRSxXQUFXO3dCQUNwQixRQUFRLEVBQUU7NEJBQ1IsU0FBUyxFQUFFLElBQUk7NEJBQ2YsTUFBTTt5QkFDUDtxQkFDRixDQUFDO29CQUVGLElBQUksR0FBRyxDQUFDLFdBQVcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7d0JBQy9ELEtBQUssQ0FBQyxRQUFRLEdBQUcsR0FBRyxDQUFDLFdBQVcsQ0FBQztvQkFDbkMsQ0FBQztvQkFFRCxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNwQixDQUFDO3FCQUFNLENBQUM7b0JBQ04sd0JBQXdCO29CQUN4QixNQUFNLEtBQUssR0FBYTt3QkFDdEIsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFO3dCQUNuQyxTQUFTO3dCQUNULElBQUksRUFBRSxNQUFNO3dCQUNaLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTt3QkFDbEIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxXQUFXLElBQUksR0FBRyxDQUFDLEdBQUc7d0JBQ2hDLFVBQVUsRUFBRSxHQUFHLENBQUMsVUFBVTt3QkFDMUIsUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLO3dCQUM1QixPQUFPLEVBQUUsV0FBVzt3QkFDcEIsUUFBUSxFQUFFOzRCQUNSLElBQUksRUFBRSxZQUFZOzRCQUNsQixTQUFTLEVBQUUsS0FBSzt5QkFDakI7cUJBQ0YsQ0FBQztvQkFFRixJQUFJLEdBQUcsQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO3dCQUMvRCxLQUFLLENBQUMsUUFBUSxHQUFHLEdBQUcsQ0FBQyxXQUFXLENBQUM7b0JBQ25DLENBQUM7b0JBRUQsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDcEIsQ0FBQztnQkFFRCxPQUFPLFdBQVcsQ0FBQyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDMUQsQ0FBQyxDQUFtQixDQUFDO1lBRXJCLE1BQU0sV0FBVyxHQUFHLEdBQUcsRUFBRTtnQkFDdkIsSUFBSSxNQUFNO29CQUFFLE9BQU87Z0JBQ25CLE1BQU0sR0FBRyxJQUFJLENBQUM7Z0JBRWQsTUFBTSxLQUFLLEdBQWE7b0JBQ3RCLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtvQkFDbkMsU0FBUztvQkFDVCxJQUFJLEVBQUUsTUFBTTtvQkFDWixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07b0JBQ2xCLElBQUksRUFBRSxHQUFHLENBQUMsV0FBVyxJQUFJLEdBQUcsQ0FBQyxHQUFHO29CQUNoQyxVQUFVLEVBQUUsR0FBRyxDQUFDLFVBQVU7b0JBQzFCLFFBQVEsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSztvQkFDNUIsT0FBTyxFQUFFLFdBQVc7b0JBQ3BCLFFBQVEsRUFBRTt3QkFDUixJQUFJLEVBQUUsWUFBWTt3QkFDbEIsU0FBUyxFQUFFLEtBQUs7cUJBQ2pCO2lCQUNGLENBQUM7Z0JBRUYsSUFBSSxHQUFHLENBQUMsV0FBVyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDL0QsS0FBSyxDQUFDLFFBQVEsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUFDO2dCQUNuQyxDQUFDO2dCQUVELElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEIsQ0FBQyxDQUFDO1lBRUYsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLElBQVMsRUFBRSxFQUFFO2dCQUN2QixZQUFZLEdBQUcsSUFBSSxDQUFDO2dCQUNwQixXQUFXLEVBQUUsQ0FBQztnQkFDZCxPQUFPLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM1QixDQUFDLENBQUM7WUFFRixHQUFHLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBUyxFQUFFLEVBQUU7Z0JBQ3ZCLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztvQkFDWixJQUFJLENBQUM7d0JBQ0gsWUFBWSxHQUFHLE9BQU8sSUFBSSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO29CQUNwRSxDQUFDO29CQUFDLE1BQU0sQ0FBQzt3QkFDUCxZQUFZLEdBQUcsSUFBSSxDQUFDO29CQUN0QixDQUFDO29CQUNELFdBQVcsRUFBRSxDQUFDO2dCQUNoQixDQUFDO2dCQUNELE9BQU8sWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzVCLENBQUMsQ0FBQztZQUVGLElBQUksRUFBRSxDQUFDO1FBQ1QsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVPLGFBQWEsQ0FBQyxHQUFXO1FBQy9CLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDOUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDbEMsSUFBSSxJQUFJLEtBQUssUUFBUTtvQkFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO2dCQUM3QyxJQUFJLENBQUM7b0JBQ0gsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMxQixDQUFDO2dCQUFDLE1BQU0sQ0FBQztvQkFDUCxPQUFPLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDO2dCQUN2QixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCw4RUFBOEU7SUFDOUUsa0JBQWtCO0lBQ2xCLDhFQUE4RTtJQUU5RSxjQUFjO1FBQ1osTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBRXBCLE9BQU8sS0FBSyxVQUFVLGdCQUFnQixDQUFDLElBTXRDO1lBQ0MsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ3pCLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1lBRTdDLGdEQUFnRDtZQUNoRCxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDMUIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO1lBQzVCLENBQUM7WUFFRCxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBRWpDLE1BQU0sS0FBSyxHQUFhO29CQUN0QixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUU7b0JBQ25DLFNBQVM7b0JBQ1QsSUFBSSxFQUFFLE1BQU07b0JBQ1osTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFO29CQUMvQixJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7b0JBQ2YsVUFBVSxFQUFFLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRztvQkFDakMsUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLO29CQUM1QixPQUFPLEVBQUU7d0JBQ1AsSUFBSSxFQUFFLElBQUksQ0FBQyxLQUFLO3FCQUNqQjtvQkFDRCxRQUFRLEVBQUU7d0JBQ1IsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO3dCQUNqQixTQUFTLEVBQUUsS0FBSztxQkFDakI7aUJBQ0YsQ0FBQztnQkFFRiw2QkFBNkI7Z0JBQzdCLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDekUsS0FBSyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQztnQkFDeEMsQ0FBQztnQkFFRCxJQUFJLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztvQkFDakIsS0FBSyxDQUFDLEtBQUssR0FBRzt3QkFDWixPQUFPLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPO3dCQUM3QixLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLO3FCQUMxQixDQUFDO2dCQUNKLENBQUM7Z0JBRUQsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFFcEIsT0FBTyxNQUFNLENBQUM7WUFDaEIsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsTUFBTSxHQUFHLEdBQUcsS0FBYyxDQUFDO2dCQUUzQixNQUFNLEtBQUssR0FBYTtvQkFDdEIsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFO29CQUNuQyxTQUFTO29CQUNULElBQUksRUFBRSxNQUFNO29CQUNaLE1BQU0sRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRTtvQkFDL0IsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO29CQUNmLFVBQVUsRUFBRSxHQUFHO29CQUNmLFFBQVEsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSztvQkFDNUIsT0FBTyxFQUFFO3dCQUNQLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSztxQkFDakI7b0JBQ0QsS0FBSyxFQUFFO3dCQUNMLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTzt3QkFDcEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO3FCQUNqQjtpQkFDRixDQUFDO2dCQUVGLDZCQUE2QjtnQkFDN0IsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO29CQUN6RSxLQUFLLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDO2dCQUN4QyxDQUFDO2dCQUVELE1BQU0sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBRXBCLE1BQU0sS0FBSyxDQUFDO1lBQ2QsQ0FBQztRQUNILENBQUMsQ0FBQztJQUNKLENBQUM7Q0FDRjtBQUVELCtFQUErRTtBQUMvRSxpQ0FBaUM7QUFDakMsK0VBQStFO0FBRS9FLFNBQWdCLFlBQVksQ0FBQyxRQUFnQixFQUFFLE9BQXVCO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLElBQUksYUFBYSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUVwRCxPQUFPO1FBQ0wsOENBQThDO1FBQzlDLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsaUJBQWlCLEVBQUU7UUFFekMsbURBQW1EO1FBQ25ELElBQUksRUFBRSxHQUF1RSxFQUFFLENBQzdFLE1BQU0sQ0FBQyxjQUFjLEVBQVk7UUFFbkMsNkNBQTZDO1FBQzdDLEtBQUssRUFBRSxDQUFDLEtBQXdCLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7WUFDaEQsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFO1lBQ25DLFNBQVMsRUFBRSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFO1lBQ2pGLElBQUksRUFBRSxNQUFNO1lBQ1osTUFBTSxFQUFFLFFBQVE7WUFDaEIsSUFBSSxFQUFFLEdBQUc7WUFDVCxRQUFRLEVBQUUsQ0FBQztZQUNYLEdBQUcsS0FBSztTQUNHLENBQUM7S0FDZixDQUFDO0FBQ0osQ0FBQztBQUVELCtFQUErRTtBQUMvRSw4Q0FBOEM7QUFDOUMsK0VBQStFO0FBRS9FOztHQUVHO0FBQ0gsU0FBZ0IsY0FBYyxDQUFDLEdBQVksRUFBRSxRQUFpQztJQUM1RSxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLEdBQUcsQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0Isa0JBQWtCLENBQ2hDLEdBQWEsRUFDYixRQUFpQztJQUVqQyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLEdBQUcsQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVELHFDQUFxQztBQUNyQyxrQkFBZSxZQUFZLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFR5cGVzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmludGVyZmFjZSBMb2dFbnRyeSB7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICByZXF1ZXN0SWQ6IHN0cmluZztcbiAgdHlwZTogJ2h0dHAnIHwgJ3RycGMnO1xuICBtZXRob2Q6IHN0cmluZztcbiAgcGF0aDogc3RyaW5nO1xuICBzdGF0dXNDb2RlPzogbnVtYmVyO1xuICBkdXJhdGlvbjogbnVtYmVyO1xuICByZXF1ZXN0Pzoge1xuICAgIGJvZHk/OiB1bmtub3duO1xuICAgIHF1ZXJ5PzogdW5rbm93bjtcbiAgICBoZWFkZXJzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgfTtcbiAgcmVzcG9uc2U/OiB7XG4gICAgYm9keT86IHVua25vd247XG4gICAgc3RyZWFtaW5nPzogYm9vbGVhbjtcbiAgICBjaHVua3M/OiB1bmtub3duW107XG4gIH07XG4gIGVycm9yPzoge1xuICAgIG1lc3NhZ2U6IHN0cmluZztcbiAgICBzdGFjaz86IHN0cmluZztcbiAgfTtcbiAgbWV0YWRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbn1cblxuaW50ZXJmYWNlIExvZ2dlck9wdGlvbnMge1xuICBtYXhTaXplQnl0ZXM/OiBudW1iZXI7ICAgICAgLy8gRGVmYXVsdDogMTBNQlxuICBpbmNsdWRlSGVhZGVycz86IGJvb2xlYW47ICAgLy8gRGVmYXVsdDogZmFsc2VcbiAgcmVkYWN0Pzogc3RyaW5nW107ICAgICAgICAgIC8vIEZpZWxkcyB0byByZWRhY3QgZnJvbSBsb2dzXG59XG5cbi8vIEV4dGVuZCBFeHByZXNzIFJlcXVlc3QgdG8gaW5jbHVkZSBtZXRhZGF0YVxuZGVjbGFyZSBnbG9iYWwge1xuICBuYW1lc3BhY2UgRXhwcmVzcyB7XG4gICAgaW50ZXJmYWNlIFJlcXVlc3Qge1xuICAgICAgbG9nTWV0YWRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICB9XG4gIH1cbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQ29yZSBMb2dnZXIgQ2xhc3Ncbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuY2xhc3MgVW5pZmllZExvZ2dlciB7XG4gIHByaXZhdGUgZmlsZVBhdGg6IHN0cmluZztcbiAgcHJpdmF0ZSBtYXhTaXplQnl0ZXM6IG51bWJlcjtcbiAgcHJpdmF0ZSBpbmNsdWRlSGVhZGVyczogYm9vbGVhbjtcbiAgcHJpdmF0ZSByZWRhY3RGaWVsZHM6IFNldDxzdHJpbmc+O1xuXG4gIGNvbnN0cnVjdG9yKGZpbGVQYXRoOiBzdHJpbmcsIG9wdGlvbnM6IExvZ2dlck9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuZmlsZVBhdGggPSBwYXRoLnJlc29sdmUoZmlsZVBhdGgpO1xuICAgIHRoaXMubWF4U2l6ZUJ5dGVzID0gb3B0aW9ucy5tYXhTaXplQnl0ZXMgPz8gMTAgKiAxMDI0ICogMTAyNDsgLy8gMTBNQiBkZWZhdWx0XG4gICAgdGhpcy5pbmNsdWRlSGVhZGVycyA9IG9wdGlvbnMuaW5jbHVkZUhlYWRlcnMgPz8gZmFsc2U7XG4gICAgdGhpcy5yZWRhY3RGaWVsZHMgPSBuZXcgU2V0KG9wdGlvbnMucmVkYWN0ID8/IFsncGFzc3dvcmQnLCAndG9rZW4nLCAnYXV0aG9yaXphdGlvbicsICdjb29raWUnXSk7XG5cbiAgICAvLyBFbnN1cmUgZGlyZWN0b3J5IGV4aXN0c1xuICAgIGNvbnN0IGRpciA9IHBhdGguZGlybmFtZSh0aGlzLmZpbGVQYXRoKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoZGlyKSkge1xuICAgICAgZnMubWtkaXJTeW5jKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZW5lcmF0ZVJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBgJHtEYXRlLm5vdygpLnRvU3RyaW5nKDM2KX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA5KX1gO1xuICB9XG5cbiAgcHJpdmF0ZSByZWRhY3Qob2JqOiB1bmtub3duKTogdW5rbm93biB7XG4gICAgaWYgKG9iaiA9PT0gbnVsbCB8fCBvYmogPT09IHVuZGVmaW5lZCkgcmV0dXJuIG9iajtcbiAgICBpZiAodHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHJldHVybiBvYmo7XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgICByZXR1cm4gb2JqLm1hcCgoaXRlbSkgPT4gdGhpcy5yZWRhY3QoaXRlbSkpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvYmogYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pKSB7XG4gICAgICBpZiAodGhpcy5yZWRhY3RGaWVsZHMuaGFzKGtleS50b0xvd2VyQ2FzZSgpKSkge1xuICAgICAgICByZXN1bHRba2V5XSA9ICdbUkVEQUNURURdJztcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICByZXN1bHRba2V5XSA9IHRoaXMucmVkYWN0KHZhbHVlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdFtrZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrQW5kUm90YXRlKCk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWZzLmV4aXN0c1N5bmModGhpcy5maWxlUGF0aCkpIHJldHVybjtcblxuICAgICAgY29uc3Qgc3RhdHMgPSBmcy5zdGF0U3luYyh0aGlzLmZpbGVQYXRoKTtcbiAgICAgIGlmIChzdGF0cy5zaXplID4gdGhpcy5tYXhTaXplQnl0ZXMpIHtcbiAgICAgICAgLy8gUmVhZCBmaWxlLCBrZWVwIGxhc3QgMjUlIG9mIGxpbmVzXG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmModGhpcy5maWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgIGNvbnN0IGxpbmVzID0gY29udGVudC50cmltKCkuc3BsaXQoJ1xcbicpO1xuICAgICAgICBjb25zdCBrZWVwQ291bnQgPSBNYXRoLmZsb29yKGxpbmVzLmxlbmd0aCAqIDAuMjUpO1xuICAgICAgICBjb25zdCBuZXdDb250ZW50ID0gbGluZXMuc2xpY2UoLWtlZXBDb3VudCkuam9pbignXFxuJykgKyAnXFxuJztcbiAgICAgICAgZnMud3JpdGVGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBuZXdDb250ZW50KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciByb3RhdGlvbiBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIHdyaXRlKGVudHJ5OiBMb2dFbnRyeSk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmNoZWNrQW5kUm90YXRlKCk7XG4gICAgICBjb25zdCByZWRhY3RlZEVudHJ5ID0gdGhpcy5yZWRhY3QoZW50cnkpIGFzIExvZ0VudHJ5O1xuICAgICAgY29uc3QgbGluZSA9IEpTT04uc3RyaW5naWZ5KHJlZGFjdGVkRW50cnkpICsgJ1xcbic7XG4gICAgICBmcy5hcHBlbmRGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBsaW5lKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciB3cml0ZSBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBFeHByZXNzIE1pZGRsZXdhcmVcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgZXhwcmVzc01pZGRsZXdhcmUoKSB7XG4gICAgcmV0dXJuIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgICAgY29uc3Qgc3RhcnQgPSBEYXRlLm5vdygpO1xuICAgICAgY29uc3QgcmVxdWVzdElkID0gdGhpcy5nZW5lcmF0ZVJlcXVlc3RJZCgpO1xuXG4gICAgICAvLyBJbml0aWFsaXplIG1ldGFkYXRhIG9iamVjdCBvbiByZXF1ZXN0XG4gICAgICByZXEubG9nTWV0YWRhdGEgPSB7fTtcblxuICAgICAgLy8gRGV0ZWN0IFNTRSAtIGNoZWNrIGJvdGggcmVxdWVzdCBBY2NlcHQgaGVhZGVyIGFuZCByZXNwb25zZSBDb250ZW50LVR5cGVcbiAgICAgIGxldCBpc1NTRSA9IHJlcS5oZWFkZXJzLmFjY2VwdCA9PT0gJ3RleHQvZXZlbnQtc3RyZWFtJztcbiAgICAgIGNvbnN0IGNodW5rczogdW5rbm93bltdID0gW107XG4gICAgICBcbiAgICAgIC8vIEludGVyY2VwdCBzZXRIZWFkZXIgdG8gZGV0ZWN0IFNTRSBieSBDb250ZW50LVR5cGVcbiAgICAgIGNvbnN0IG9yaWdpbmFsU2V0SGVhZGVyID0gcmVzLnNldEhlYWRlci5iaW5kKHJlcyk7XG4gICAgICByZXMuc2V0SGVhZGVyID0gKChuYW1lOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcgfCBudW1iZXIgfCByZWFkb25seSBzdHJpbmdbXSk6IFJlc3BvbnNlID0+IHtcbiAgICAgICAgaWYgKG5hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2NvbnRlbnQtdHlwZScgJiYgXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIFxuICAgICAgICAgICAgdmFsdWUuaW5jbHVkZXMoJ3RleHQvZXZlbnQtc3RyZWFtJykpIHtcbiAgICAgICAgICBpc1NTRSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsU2V0SGVhZGVyKG5hbWUsIHZhbHVlKTtcbiAgICAgIH0pIGFzIHR5cGVvZiByZXMuc2V0SGVhZGVyO1xuXG4gICAgICAvLyBDYXB0dXJlIHJlcXVlc3QgaW5mb1xuICAgICAgY29uc3QgcmVxdWVzdEluZm86IExvZ0VudHJ5WydyZXF1ZXN0J10gPSB7XG4gICAgICAgIGJvZHk6IHJlcS5ib2R5LFxuICAgICAgICBxdWVyeTogcmVxLnF1ZXJ5LFxuICAgICAgfTtcblxuICAgICAgaWYgKHRoaXMuaW5jbHVkZUhlYWRlcnMpIHtcbiAgICAgICAgcmVxdWVzdEluZm8uaGVhZGVycyA9IHJlcS5oZWFkZXJzIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gICAgICB9XG5cbiAgICAgIC8vIEludGVyY2VwdCB3cml0ZS9lbmQgZm9yIHN0cmVhbWluZyBkZXRlY3Rpb25cbiAgICAgIGNvbnN0IG9yaWdpbmFsV3JpdGUgPSByZXMud3JpdGUuYmluZChyZXMpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxFbmQgPSByZXMuZW5kLmJpbmQocmVzKTtcbiAgICAgIGNvbnN0IG9yaWdpbmFsSnNvbiA9IHJlcy5qc29uLmJpbmQocmVzKTtcbiAgICAgIGNvbnN0IG9yaWdpbmFsU2VuZCA9IHJlcy5zZW5kLmJpbmQocmVzKTtcbiAgICAgIGxldCByZXNwb25zZUJvZHk6IHVua25vd247XG4gICAgICBsZXQgbG9nZ2VkID0gZmFsc2U7XG5cbiAgICAgIHJlcy53cml0ZSA9ICgoY2h1bms6IGFueSwgZW5jb2RpbmdPckNhbGxiYWNrPzogYW55LCBjYWxsYmFjaz86IGFueSk6IGJvb2xlYW4gPT4ge1xuICAgICAgICAvLyBJZiB3cml0ZSBpcyBjYWxsZWQsIHRyZWF0IGFzIHN0cmVhbWluZ1xuICAgICAgICBpZiAoY2h1bmsgJiYgaXNTU0UpIHtcbiAgICAgICAgICBjb25zdCBjaHVua1N0ciA9IGNodW5rLnRvU3RyaW5nKCk7XG4gICAgICAgICAgY29uc3QgcGFyc2VkID0gdGhpcy5wYXJzZVNTRUNodW5rKGNodW5rU3RyKTtcbiAgICAgICAgICBpZiAocGFyc2VkKSB7XG4gICAgICAgICAgICBjaHVua3MucHVzaChwYXJzZWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb3JpZ2luYWxXcml0ZShjaHVuaywgZW5jb2RpbmdPckNhbGxiYWNrLCBjYWxsYmFjayk7XG4gICAgICB9KSBhcyB0eXBlb2YgcmVzLndyaXRlO1xuXG4gICAgICByZXMuZW5kID0gKChjaHVuaz86IGFueSwgZW5jb2RpbmdPckNhbGxiYWNrPzogYW55LCBjYWxsYmFjaz86IGFueSk6IFJlc3BvbnNlID0+IHtcbiAgICAgICAgaWYgKGxvZ2dlZCkgcmV0dXJuIG9yaWdpbmFsRW5kKGNodW5rLCBlbmNvZGluZ09yQ2FsbGJhY2ssIGNhbGxiYWNrKTtcbiAgICAgICAgbG9nZ2VkID0gdHJ1ZTtcblxuICAgICAgICBpZiAoaXNTU0UpIHtcbiAgICAgICAgICAvLyBTU0Ugc3RyZWFtaW5nIHBhdGhcbiAgICAgICAgICBpZiAoY2h1bmspIHtcbiAgICAgICAgICAgIGNvbnN0IGNodW5rU3RyID0gY2h1bmsudG9TdHJpbmcoKTtcbiAgICAgICAgICAgIGNvbnN0IHBhcnNlZCA9IHRoaXMucGFyc2VTU0VDaHVuayhjaHVua1N0cik7XG4gICAgICAgICAgICBpZiAocGFyc2VkKSB7XG4gICAgICAgICAgICAgIGNodW5rcy5wdXNoKHBhcnNlZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgICB0eXBlOiAnaHR0cCcsXG4gICAgICAgICAgICBtZXRob2Q6IHJlcS5tZXRob2QsXG4gICAgICAgICAgICBwYXRoOiByZXEub3JpZ2luYWxVcmwgfHwgcmVxLnVybCxcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLFxuICAgICAgICAgICAgZHVyYXRpb246IERhdGUubm93KCkgLSBzdGFydCxcbiAgICAgICAgICAgIHJlcXVlc3Q6IHJlcXVlc3RJbmZvLFxuICAgICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgICAgc3RyZWFtaW5nOiB0cnVlLFxuICAgICAgICAgICAgICBjaHVua3MsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBpZiAocmVxLmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKHJlcS5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZW50cnkubWV0YWRhdGEgPSByZXEubG9nTWV0YWRhdGE7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy53cml0ZShlbnRyeSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gUmVndWxhciByZXNwb25zZSBwYXRoXG4gICAgICAgICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgICB0eXBlOiAnaHR0cCcsXG4gICAgICAgICAgICBtZXRob2Q6IHJlcS5tZXRob2QsXG4gICAgICAgICAgICBwYXRoOiByZXEub3JpZ2luYWxVcmwgfHwgcmVxLnVybCxcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLFxuICAgICAgICAgICAgZHVyYXRpb246IERhdGUubm93KCkgLSBzdGFydCxcbiAgICAgICAgICAgIHJlcXVlc3Q6IHJlcXVlc3RJbmZvLFxuICAgICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgICAgYm9keTogcmVzcG9uc2VCb2R5LFxuICAgICAgICAgICAgICBzdHJlYW1pbmc6IGZhbHNlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgaWYgKHJlcS5sb2dNZXRhZGF0YSAmJiBPYmplY3Qua2V5cyhyZXEubG9nTWV0YWRhdGEpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gcmVxLmxvZ01ldGFkYXRhO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMud3JpdGUoZW50cnkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsRW5kKGNodW5rLCBlbmNvZGluZ09yQ2FsbGJhY2ssIGNhbGxiYWNrKTtcbiAgICAgIH0pIGFzIHR5cGVvZiByZXMuZW5kO1xuXG4gICAgICBjb25zdCBsb2dSZXNwb25zZSA9ICgpID0+IHtcbiAgICAgICAgaWYgKGxvZ2dlZCkgcmV0dXJuO1xuICAgICAgICBsb2dnZWQgPSB0cnVlO1xuXG4gICAgICAgIGNvbnN0IGVudHJ5OiBMb2dFbnRyeSA9IHtcbiAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgdHlwZTogJ2h0dHAnLFxuICAgICAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCxcbiAgICAgICAgICBwYXRoOiByZXEub3JpZ2luYWxVcmwgfHwgcmVxLnVybCxcbiAgICAgICAgICBzdGF0dXNDb2RlOiByZXMuc3RhdHVzQ29kZSxcbiAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgIHJlcXVlc3Q6IHJlcXVlc3RJbmZvLFxuICAgICAgICAgIHJlc3BvbnNlOiB7XG4gICAgICAgICAgICBib2R5OiByZXNwb25zZUJvZHksXG4gICAgICAgICAgICBzdHJlYW1pbmc6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKHJlcS5sb2dNZXRhZGF0YSAmJiBPYmplY3Qua2V5cyhyZXEubG9nTWV0YWRhdGEpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBlbnRyeS5tZXRhZGF0YSA9IHJlcS5sb2dNZXRhZGF0YTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMud3JpdGUoZW50cnkpO1xuICAgICAgfTtcblxuICAgICAgcmVzLmpzb24gPSAoYm9keTogYW55KSA9PiB7XG4gICAgICAgIHJlc3BvbnNlQm9keSA9IGJvZHk7XG4gICAgICAgIGxvZ1Jlc3BvbnNlKCk7XG4gICAgICAgIHJldHVybiBvcmlnaW5hbEpzb24oYm9keSk7XG4gICAgICB9O1xuXG4gICAgICByZXMuc2VuZCA9IChib2R5OiBhbnkpID0+IHtcbiAgICAgICAgaWYgKCFsb2dnZWQpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmVzcG9uc2VCb2R5ID0gdHlwZW9mIGJvZHkgPT09ICdzdHJpbmcnID8gSlNPTi5wYXJzZShib2R5KSA6IGJvZHk7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICByZXNwb25zZUJvZHkgPSBib2R5O1xuICAgICAgICAgIH1cbiAgICAgICAgICBsb2dSZXNwb25zZSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvcmlnaW5hbFNlbmQoYm9keSk7XG4gICAgICB9O1xuXG4gICAgICBuZXh0KCk7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgcGFyc2VTU0VDaHVuayhyYXc6IHN0cmluZyk6IHVua25vd24ge1xuICAgIGNvbnN0IGxpbmVzID0gcmF3LnNwbGl0KCdcXG4nKTtcbiAgICBmb3IgKGNvbnN0IGxpbmUgb2YgbGluZXMpIHtcbiAgICAgIGlmIChsaW5lLnN0YXJ0c1dpdGgoJ2RhdGE6ICcpKSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBsaW5lLnNsaWNlKDYpLnRyaW0oKTtcbiAgICAgICAgaWYgKGRhdGEgPT09ICdbRE9ORV0nKSByZXR1cm4geyBkb25lOiB0cnVlIH07XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIHJldHVybiB7IHJhdzogZGF0YSB9O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIHRSUEMgTWlkZGxld2FyZVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICB0cnBjTWlkZGxld2FyZTxUQ29udGV4dCBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+KCkge1xuICAgIGNvbnN0IGxvZ2dlciA9IHRoaXM7XG5cbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gbG9nZ2VyTWlkZGxld2FyZShvcHRzOiB7XG4gICAgICBwYXRoOiBzdHJpbmc7XG4gICAgICB0eXBlOiAncXVlcnknIHwgJ211dGF0aW9uJyB8ICdzdWJzY3JpcHRpb24nO1xuICAgICAgaW5wdXQ6IHVua25vd247XG4gICAgICBjdHg6IFRDb250ZXh0ICYgeyBsb2dNZXRhZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+IH07XG4gICAgICBuZXh0OiAoKSA9PiBQcm9taXNlPHsgb2s6IGJvb2xlYW47IGRhdGE/OiB1bmtub3duOyBlcnJvcj86IEVycm9yIH0+O1xuICAgIH0pIHtcbiAgICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgICAgIGNvbnN0IHJlcXVlc3RJZCA9IGxvZ2dlci5nZW5lcmF0ZVJlcXVlc3RJZCgpO1xuXG4gICAgICAvLyBJbml0aWFsaXplIG1ldGFkYXRhIG9uIGNvbnRleHQgaWYgbm90IHByZXNlbnRcbiAgICAgIGlmICghb3B0cy5jdHgubG9nTWV0YWRhdGEpIHtcbiAgICAgICAgb3B0cy5jdHgubG9nTWV0YWRhdGEgPSB7fTtcbiAgICAgIH1cblxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgb3B0cy5uZXh0KCk7XG5cbiAgICAgICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICB0eXBlOiAndHJwYycsXG4gICAgICAgICAgbWV0aG9kOiBvcHRzLnR5cGUudG9VcHBlckNhc2UoKSxcbiAgICAgICAgICBwYXRoOiBvcHRzLnBhdGgsXG4gICAgICAgICAgc3RhdHVzQ29kZTogcmVzdWx0Lm9rID8gMjAwIDogNTAwLFxuICAgICAgICAgIGR1cmF0aW9uOiBEYXRlLm5vdygpIC0gc3RhcnQsXG4gICAgICAgICAgcmVxdWVzdDoge1xuICAgICAgICAgICAgYm9keTogb3B0cy5pbnB1dCxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHJlc3BvbnNlOiB7XG4gICAgICAgICAgICBib2R5OiByZXN1bHQuZGF0YSxcbiAgICAgICAgICAgIHN0cmVhbWluZzogZmFsc2UsXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBBdHRhY2ggbWV0YWRhdGEgaWYgcHJlc2VudFxuICAgICAgICBpZiAob3B0cy5jdHgubG9nTWV0YWRhdGEgJiYgT2JqZWN0LmtleXMob3B0cy5jdHgubG9nTWV0YWRhdGEpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBlbnRyeS5tZXRhZGF0YSA9IG9wdHMuY3R4LmxvZ01ldGFkYXRhO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgICAgICAgIGVudHJ5LmVycm9yID0ge1xuICAgICAgICAgICAgbWVzc2FnZTogcmVzdWx0LmVycm9yLm1lc3NhZ2UsXG4gICAgICAgICAgICBzdGFjazogcmVzdWx0LmVycm9yLnN0YWNrLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICBsb2dnZXIud3JpdGUoZW50cnkpO1xuXG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zdCBlcnIgPSBlcnJvciBhcyBFcnJvcjtcblxuICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgcmVxdWVzdElkLFxuICAgICAgICAgIHR5cGU6ICd0cnBjJyxcbiAgICAgICAgICBtZXRob2Q6IG9wdHMudHlwZS50b1VwcGVyQ2FzZSgpLFxuICAgICAgICAgIHBhdGg6IG9wdHMucGF0aCxcbiAgICAgICAgICBzdGF0dXNDb2RlOiA1MDAsXG4gICAgICAgICAgZHVyYXRpb246IERhdGUubm93KCkgLSBzdGFydCxcbiAgICAgICAgICByZXF1ZXN0OiB7XG4gICAgICAgICAgICBib2R5OiBvcHRzLmlucHV0LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICAgICAgc3RhY2s6IGVyci5zdGFjayxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIEF0dGFjaCBtZXRhZGF0YSBpZiBwcmVzZW50XG4gICAgICAgIGlmIChvcHRzLmN0eC5sb2dNZXRhZGF0YSAmJiBPYmplY3Qua2V5cyhvcHRzLmN0eC5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gb3B0cy5jdHgubG9nTWV0YWRhdGE7XG4gICAgICAgIH1cblxuICAgICAgICBsb2dnZXIud3JpdGUoZW50cnkpO1xuXG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gRmFjdG9yeSBGdW5jdGlvbiAoTWFpbiBFeHBvcnQpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVMb2dnZXIoZmlsZVBhdGg6IHN0cmluZywgb3B0aW9ucz86IExvZ2dlck9wdGlvbnMpIHtcbiAgY29uc3QgbG9nZ2VyID0gbmV3IFVuaWZpZWRMb2dnZXIoZmlsZVBhdGgsIG9wdGlvbnMpO1xuXG4gIHJldHVybiB7XG4gICAgLyoqIEV4cHJlc3MgbWlkZGxld2FyZSAtIHVzZSB3aXRoIGFwcC51c2UoKSAqL1xuICAgIGV4cHJlc3M6ICgpID0+IGxvZ2dlci5leHByZXNzTWlkZGxld2FyZSgpLFxuXG4gICAgLyoqIHRSUEMgbWlkZGxld2FyZSAtIHVzZSB3aXRoIHQucHJvY2VkdXJlLnVzZSgpICovXG4gICAgdHJwYzogPFRDb250ZXh0IGV4dGVuZHMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4oKSA9PiBcbiAgICAgIGxvZ2dlci50cnBjTWlkZGxld2FyZTxUQ29udGV4dD4oKSxcblxuICAgIC8qKiBEaXJlY3Qgd3JpdGUgYWNjZXNzIGZvciBjdXN0b20gbG9nZ2luZyAqL1xuICAgIHdyaXRlOiAoZW50cnk6IFBhcnRpYWw8TG9nRW50cnk+KSA9PiBsb2dnZXIud3JpdGUoe1xuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICByZXF1ZXN0SWQ6IGAke0RhdGUubm93KCkudG9TdHJpbmcoMzYpfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnNsaWNlKDIsIDkpfWAsXG4gICAgICB0eXBlOiAnaHR0cCcsXG4gICAgICBtZXRob2Q6ICdDVVNUT00nLFxuICAgICAgcGF0aDogJy8nLFxuICAgICAgZHVyYXRpb246IDAsXG4gICAgICAuLi5lbnRyeSxcbiAgICB9IGFzIExvZ0VudHJ5KSxcbiAgfTtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gSGVscGVyIHRvIGF0dGFjaCBtZXRhZGF0YSAoZm9yIGNsZWFuZXIgQVBJKVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIEF0dGFjaCBtZXRhZGF0YSB0byB0aGUgY3VycmVudCByZXF1ZXN0IGxvZyBlbnRyeSAoRXhwcmVzcylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGF0dGFjaE1ldGFkYXRhKHJlcTogUmVxdWVzdCwgbWV0YWRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogdm9pZCB7XG4gIGlmICghcmVxLmxvZ01ldGFkYXRhKSB7XG4gICAgcmVxLmxvZ01ldGFkYXRhID0ge307XG4gIH1cbiAgT2JqZWN0LmFzc2lnbihyZXEubG9nTWV0YWRhdGEsIG1ldGFkYXRhKTtcbn1cblxuLyoqXG4gKiBBdHRhY2ggbWV0YWRhdGEgdG8gdGhlIGN1cnJlbnQgcmVxdWVzdCBsb2cgZW50cnkgKHRSUEMpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdHRhY2hUcnBjTWV0YWRhdGE8VENvbnRleHQgZXh0ZW5kcyB7IGxvZ01ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfT4oXG4gIGN0eDogVENvbnRleHQsXG4gIG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuKTogdm9pZCB7XG4gIGlmICghY3R4LmxvZ01ldGFkYXRhKSB7XG4gICAgY3R4LmxvZ01ldGFkYXRhID0ge307XG4gIH1cbiAgT2JqZWN0LmFzc2lnbihjdHgubG9nTWV0YWRhdGEsIG1ldGFkYXRhKTtcbn1cblxuLy8gRGVmYXVsdCBleHBvcnQgZm9yIHNpbXBsZXIgaW1wb3J0c1xuZXhwb3J0IGRlZmF1bHQgY3JlYXRlTG9nZ2VyO1xuIl19
|
|
532
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlrQkEsb0NBc0JDO0FBU0Qsd0NBS0M7QUFLRCxnREFRQztBQWxuQkQsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQWtEN0IsK0VBQStFO0FBQy9FLG9CQUFvQjtBQUNwQiwrRUFBK0U7QUFFL0UsTUFBTSxhQUFhO0lBUWpCLFlBQVksUUFBZ0IsRUFBRSxVQUF5QixFQUFFO1FBQ3ZELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsWUFBWSxHQUFHLE9BQU8sQ0FBQyxZQUFZLElBQUksRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxlQUFlO1FBQzdFLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLGNBQWMsSUFBSSxLQUFLLENBQUM7UUFDdEQsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxJQUFJLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNoRyxJQUFJLENBQUMsV0FBVyxHQUFHLE9BQU8sQ0FBQyxXQUFXLElBQUksRUFBRSxDQUFDO1FBQzdDLElBQUksQ0FBQyxZQUFZLEdBQUcsT0FBTyxDQUFDLFlBQVksSUFBSSxFQUFFLENBQUM7UUFFL0MsMEJBQTBCO1FBQzFCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEIsRUFBRSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFT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R0FBRyxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQixrQkFBa0IsQ0FDaEMsR0FBYSxFQUNiLFFBQWlDO0lBRWpDLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDckIsR0FBRyxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUNELE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBRUQscUNBQXFDO0FBQ3JDLGtCQUFlLFlBQVksQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBOZXh0RnVuY3Rpb24gfSBmcm9tICdleHByZXNzJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gVHlwZXNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuaW50ZXJmYWNlIExvZ0VudHJ5IHtcbiAgdGltZXN0YW1wOiBzdHJpbmc7XG4gIHJlcXVlc3RJZDogc3RyaW5nO1xuICB0eXBlOiAnaHR0cCcgfCAndHJwYyc7XG4gIG1ldGhvZDogc3RyaW5nO1xuICBwYXRoOiBzdHJpbmc7XG4gIHN0YXR1c0NvZGU/OiBudW1iZXI7XG4gIGR1cmF0aW9uOiBudW1iZXI7XG4gIHJlcXVlc3Q/OiB7XG4gICAgYm9keT86IHVua25vd247XG4gICAgcXVlcnk/OiB1bmtub3duO1xuICAgIGhlYWRlcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICB9O1xuICByZXNwb25zZT86IHtcbiAgICBib2R5PzogdW5rbm93bjtcbiAgICBzdHJlYW1pbmc/OiBib29sZWFuO1xuICAgIGNodW5rcz86IHVua25vd25bXTtcbiAgICB0ZXh0Pzogc3RyaW5nOyAvLyBBZ2dyZWdhdGVkIHRleHQgZnJvbSB0ZXh0LWRlbHRhIGNodW5rc1xuICB9O1xuICBlcnJvcj86IHtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgc3RhY2s/OiBzdHJpbmc7XG4gIH07XG4gIG1ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbmludGVyZmFjZSBMb2dnZXJPcHRpb25zIHtcbiAgbWF4U2l6ZUJ5dGVzPzogbnVtYmVyOyAgICAgIC8vIERlZmF1bHQ6IDEwTUJcbiAgaW5jbHVkZUhlYWRlcnM/OiBib29sZWFuOyAgIC8vIERlZmF1bHQ6IGZhbHNlXG4gIHJlZGFjdD86IHN0cmluZ1tdOyAgICAgICAgICAvLyBGaWVsZHMgdG8gcmVkYWN0IGZyb20gbG9nc1xuICBpZ25vcmVQYXRocz86IHN0cmluZ1tdOyAgICAgLy8gUGF0aHMgdG8gaWdub3JlIChzdXBwb3J0cyB3aWxkY2FyZHMgbGlrZSAvaGVhbHRoLyopXG4gIGluY2x1ZGVQYXRocz86IHN0cmluZ1tdOyAgICAvLyBPbmx5IGxvZyB0aGVzZSBwYXRocyAoc3VwcG9ydHMgd2lsZGNhcmRzKVxufVxuXG4vLyBFeHRlbmQgRXhwcmVzcyBSZXF1ZXN0IHRvIGluY2x1ZGUgbWV0YWRhdGFcbmRlY2xhcmUgZ2xvYmFsIHtcbiAgbmFtZXNwYWNlIEV4cHJlc3Mge1xuICAgIGludGVyZmFjZSBSZXF1ZXN0IHtcbiAgICAgIGxvZ01ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgfVxuICB9XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvcmUgTG9nZ2VyIENsYXNzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmNsYXNzIFVuaWZpZWRMb2dnZXIge1xuICBwcml2YXRlIGZpbGVQYXRoOiBzdHJpbmc7XG4gIHByaXZhdGUgbWF4U2l6ZUJ5dGVzOiBudW1iZXI7XG4gIHByaXZhdGUgaW5jbHVkZUhlYWRlcnM6IGJvb2xlYW47XG4gIHByaXZhdGUgcmVkYWN0RmllbGRzOiBTZXQ8c3RyaW5nPjtcbiAgcHJpdmF0ZSBpZ25vcmVQYXRoczogc3RyaW5nW107XG4gIHByaXZhdGUgaW5jbHVkZVBhdGhzOiBzdHJpbmdbXTtcblxuICBjb25zdHJ1Y3RvcihmaWxlUGF0aDogc3RyaW5nLCBvcHRpb25zOiBMb2dnZXJPcHRpb25zID0ge30pIHtcbiAgICB0aGlzLmZpbGVQYXRoID0gcGF0aC5yZXNvbHZlKGZpbGVQYXRoKTtcbiAgICB0aGlzLm1heFNpemVCeXRlcyA9IG9wdGlvbnMubWF4U2l6ZUJ5dGVzID8/IDEwICogMTAyNCAqIDEwMjQ7IC8vIDEwTUIgZGVmYXVsdFxuICAgIHRoaXMuaW5jbHVkZUhlYWRlcnMgPSBvcHRpb25zLmluY2x1ZGVIZWFkZXJzID8/IGZhbHNlO1xuICAgIHRoaXMucmVkYWN0RmllbGRzID0gbmV3IFNldChvcHRpb25zLnJlZGFjdCA/PyBbJ3Bhc3N3b3JkJywgJ3Rva2VuJywgJ2F1dGhvcml6YXRpb24nLCAnY29va2llJ10pO1xuICAgIHRoaXMuaWdub3JlUGF0aHMgPSBvcHRpb25zLmlnbm9yZVBhdGhzID8/IFtdO1xuICAgIHRoaXMuaW5jbHVkZVBhdGhzID0gb3B0aW9ucy5pbmNsdWRlUGF0aHMgPz8gW107XG5cbiAgICAvLyBFbnN1cmUgZGlyZWN0b3J5IGV4aXN0c1xuICAgIGNvbnN0IGRpciA9IHBhdGguZGlybmFtZSh0aGlzLmZpbGVQYXRoKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoZGlyKSkge1xuICAgICAgZnMubWtkaXJTeW5jKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZW5lcmF0ZVJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBgJHtEYXRlLm5vdygpLnRvU3RyaW5nKDM2KX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA5KX1gO1xuICB9XG5cbiAgcHJpdmF0ZSBtYXRjaFBhdGgocGF0dGVybjogc3RyaW5nLCByZXF1ZXN0UGF0aDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgLy8gQ29udmVydCB3aWxkY2FyZCBwYXR0ZXJuIHRvIHJlZ2V4XG4gICAgLy8gL2FwaS8qIG1hdGNoZXMgL2FwaS9hbnl0aGluZ1xuICAgIC8vIC9oZWFsdGggbWF0Y2hlcyBleGFjdGx5IC9oZWFsdGhcbiAgICBjb25zdCByZWdleFBhdHRlcm4gPSBwYXR0ZXJuXG4gICAgICAucmVwbGFjZSgvWy4rP14ke30oKXxbXFxdXFxcXF0vZywgJ1xcXFwkJicpIC8vIEVzY2FwZSBzcGVjaWFsIHJlZ2V4IGNoYXJzIGV4Y2VwdCAqXG4gICAgICAucmVwbGFjZSgvXFwqL2csICcuKicpOyAvLyBDb252ZXJ0ICogdG8gLipcbiAgICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoYF4ke3JlZ2V4UGF0dGVybn0kYCk7XG4gICAgcmV0dXJuIHJlZ2V4LnRlc3QocmVxdWVzdFBhdGgpO1xuICB9XG5cbiAgcHJpdmF0ZSBzaG91bGRMb2cocmVxdWVzdFBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIC8vIEV4dHJhY3QgcGF0aCB3aXRob3V0IHF1ZXJ5IHN0cmluZ1xuICAgIGNvbnN0IHBhdGhPbmx5ID0gcmVxdWVzdFBhdGguc3BsaXQoJz8nKVswXTtcblxuICAgIC8vIElmIGluY2x1ZGVQYXRocyBpcyBzZXQsIG9ubHkgbG9nIG1hdGNoaW5nIHBhdGhzXG4gICAgaWYgKHRoaXMuaW5jbHVkZVBhdGhzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiB0aGlzLmluY2x1ZGVQYXRocy5zb21lKChwYXR0ZXJuKSA9PiB0aGlzLm1hdGNoUGF0aChwYXR0ZXJuLCBwYXRoT25seSkpO1xuICAgIH1cblxuICAgIC8vIElmIGlnbm9yZVBhdGhzIGlzIHNldCwgc2tpcCBtYXRjaGluZyBwYXRoc1xuICAgIGlmICh0aGlzLmlnbm9yZVBhdGhzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiAhdGhpcy5pZ25vcmVQYXRocy5zb21lKChwYXR0ZXJuKSA9PiB0aGlzLm1hdGNoUGF0aChwYXR0ZXJuLCBwYXRoT25seSkpO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSByZWRhY3Qob2JqOiB1bmtub3duKTogdW5rbm93biB7XG4gICAgaWYgKG9iaiA9PT0gbnVsbCB8fCBvYmogPT09IHVuZGVmaW5lZCkgcmV0dXJuIG9iajtcbiAgICBpZiAodHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHJldHVybiBvYmo7XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgICByZXR1cm4gb2JqLm1hcCgoaXRlbSkgPT4gdGhpcy5yZWRhY3QoaXRlbSkpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvYmogYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pKSB7XG4gICAgICBpZiAodGhpcy5yZWRhY3RGaWVsZHMuaGFzKGtleS50b0xvd2VyQ2FzZSgpKSkge1xuICAgICAgICByZXN1bHRba2V5XSA9ICdbUkVEQUNURURdJztcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICByZXN1bHRba2V5XSA9IHRoaXMucmVkYWN0KHZhbHVlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdFtrZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrQW5kUm90YXRlKCk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWZzLmV4aXN0c1N5bmModGhpcy5maWxlUGF0aCkpIHJldHVybjtcblxuICAgICAgY29uc3Qgc3RhdHMgPSBmcy5zdGF0U3luYyh0aGlzLmZpbGVQYXRoKTtcbiAgICAgIGlmIChzdGF0cy5zaXplID4gdGhpcy5tYXhTaXplQnl0ZXMpIHtcbiAgICAgICAgLy8gUmVhZCBmaWxlLCBrZWVwIGxhc3QgMjUlIG9mIGxpbmVzXG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmModGhpcy5maWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgIGNvbnN0IGxpbmVzID0gY29udGVudC50cmltKCkuc3BsaXQoJ1xcbicpO1xuICAgICAgICBjb25zdCBrZWVwQ291bnQgPSBNYXRoLmZsb29yKGxpbmVzLmxlbmd0aCAqIDAuMjUpO1xuICAgICAgICBjb25zdCBuZXdDb250ZW50ID0gbGluZXMuc2xpY2UoLWtlZXBDb3VudCkuam9pbignXFxuJykgKyAnXFxuJztcbiAgICAgICAgZnMud3JpdGVGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBuZXdDb250ZW50KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciByb3RhdGlvbiBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIHdyaXRlKGVudHJ5OiBMb2dFbnRyeSk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmNoZWNrQW5kUm90YXRlKCk7XG4gICAgICBjb25zdCByZWRhY3RlZEVudHJ5ID0gdGhpcy5yZWRhY3QoZW50cnkpIGFzIExvZ0VudHJ5O1xuICAgICAgY29uc3QgbGluZSA9IEpTT04uc3RyaW5naWZ5KHJlZGFjdGVkRW50cnkpICsgJ1xcbic7XG4gICAgICBmcy5hcHBlbmRGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBsaW5lKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciB3cml0ZSBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNvZGUgY2h1bmsgdG8gc3RyaW5nLCBoYW5kbGluZyBVaW50OEFycmF5L0J1ZmZlciBmcm9tIFRleHRFbmNvZGVyU3RyZWFtXG4gICAqL1xuICBwcml2YXRlIGRlY29kZUNodW5rKGNodW5rOiBhbnkpOiBzdHJpbmcge1xuICAgIGlmIChjaHVuayA9PT0gbnVsbCB8fCBjaHVuayA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICAgIFxuICAgIC8vIEhhbmRsZSBVaW50OEFycmF5IChmcm9tIFRleHRFbmNvZGVyU3RyZWFtIGluIEFJIFNESylcbiAgICBpZiAoY2h1bmsgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICByZXR1cm4gQnVmZmVyLmZyb20oY2h1bmspLnRvU3RyaW5nKCd1dGYtOCcpO1xuICAgIH1cbiAgICBcbiAgICAvLyBIYW5kbGUgQnVmZmVyXG4gICAgaWYgKEJ1ZmZlci5pc0J1ZmZlcihjaHVuaykpIHtcbiAgICAgIHJldHVybiBjaHVuay50b1N0cmluZygndXRmLTgnKTtcbiAgICB9XG4gICAgXG4gICAgLy8gSGFuZGxlIHN0cmluZ1xuICAgIGlmICh0eXBlb2YgY2h1bmsgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gY2h1bms7XG4gICAgfVxuICAgIFxuICAgIC8vIEZhbGxiYWNrOiB0cnkgdG9TdHJpbmcgYnV0IGNoZWNrIGlmIGl0IGxvb2tzIGxpa2UgYnl0ZSBhcnJheVxuICAgIGNvbnN0IHN0ciA9IGNodW5rLnRvU3RyaW5nKCk7XG4gICAgXG4gICAgLy8gRGV0ZWN0IGlmIHRvU3RyaW5nIHByb2R1Y2VkIGEgY29tbWEtc2VwYXJhdGVkIGJ5dGUgbGlzdCBsaWtlIFwiMTAwLDk3LDExNiw5N1wiXG4gICAgLy8gVGhpcyBoYXBwZW5zIHdoZW4gVWludDhBcnJheS50b1N0cmluZygpIGlzIGNhbGxlZCB3aXRob3V0IHByb3BlciBkZWNvZGluZ1xuICAgIGlmICgvXlxcZCsoLFxcZCspKiQvLnRlc3Qoc3RyKSAmJiBzdHIuaW5jbHVkZXMoJywnKSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheShzdHIuc3BsaXQoJywnKS5tYXAoTnVtYmVyKSk7XG4gICAgICAgIHJldHVybiBCdWZmZXIuZnJvbShieXRlcykudG9TdHJpbmcoJ3V0Zi04Jyk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIHN0cjtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHN0cjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBjb250ZW50IGxvb2tzIGxpa2UgU1NFIGRhdGFcbiAgICovXG4gIHByaXZhdGUgbG9va3NMaWtlU1NFKGNvbnRlbnQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBjb250ZW50LnRyaW1TdGFydCgpLnN0YXJ0c1dpdGgoJ2RhdGE6JykgfHwgXG4gICAgICAgICAgIGNvbnRlbnQuaW5jbHVkZXMoJ1xcbmRhdGE6JykgfHxcbiAgICAgICAgICAgY29udGVudC50cmltU3RhcnQoKS5zdGFydHNXaXRoKCdldmVudDonKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBFeHByZXNzIE1pZGRsZXdhcmVcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgZXhwcmVzc01pZGRsZXdhcmUoKSB7XG4gICAgcmV0dXJuIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgICAgY29uc3QgcmVxdWVzdFBhdGggPSByZXEub3JpZ2luYWxVcmwgfHwgcmVxLnVybDtcblxuICAgICAgLy8gQ2hlY2sgaWYgd2Ugc2hvdWxkIGxvZyB0aGlzIHBhdGhcbiAgICAgIGlmICghdGhpcy5zaG91bGRMb2cocmVxdWVzdFBhdGgpKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgICAgIGNvbnN0IHJlcXVlc3RJZCA9IHRoaXMuZ2VuZXJhdGVSZXF1ZXN0SWQoKTtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSBtZXRhZGF0YSBvYmplY3Qgb24gcmVxdWVzdFxuICAgICAgcmVxLmxvZ01ldGFkYXRhID0ge307XG5cbiAgICAgIC8vIERldGVjdCBTU0UgLSBjaGVjayByZXF1ZXN0IEFjY2VwdCBoZWFkZXIgaW5pdGlhbGx5XG4gICAgICBsZXQgaXNTU0UgPSByZXEuaGVhZGVycy5hY2NlcHQgPT09ICd0ZXh0L2V2ZW50LXN0cmVhbSc7XG4gICAgICBjb25zdCBjaHVua3M6IHVua25vd25bXSA9IFtdO1xuICAgICAgY29uc3QgdGV4dERlbHRhczogc3RyaW5nW10gPSBbXTsgLy8gQ29sbGVjdCB0ZXh0LWRlbHRhIGNvbnRlbnRcblxuICAgICAgLy8gSGVscGVyIHRvIGNoZWNrIENvbnRlbnQtVHlwZSBoZWFkZXIgZm9yIFNTRVxuICAgICAgY29uc3QgY2hlY2tDb250ZW50VHlwZUZvclNTRSA9IChoZWFkZXJzOiBhbnkpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKCFoZWFkZXJzKSByZXR1cm47XG4gICAgICAgIFxuICAgICAgICAvLyBoZWFkZXJzIGNhbiBiZSBhbiBvYmplY3Qgb3IgYXJyYXkgb2YgW2tleSwgdmFsdWVdIHBhaXJzXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KGhlYWRlcnMpKSB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBoZWFkZXJzLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICBjb25zdCBrZXkgPSBoZWFkZXJzW2ldO1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSBoZWFkZXJzW2kgKyAxXTtcbiAgICAgICAgICAgIGlmICh0eXBlb2Yga2V5ID09PSAnc3RyaW5nJyAmJiBcbiAgICAgICAgICAgICAgICBrZXkudG9Mb3dlckNhc2UoKSA9PT0gJ2NvbnRlbnQtdHlwZScgJiYgXG4gICAgICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiBcbiAgICAgICAgICAgICAgICB2YWx1ZS5pbmNsdWRlcygndGV4dC9ldmVudC1zdHJlYW0nKSkge1xuICAgICAgICAgICAgICBpc1NTRSA9IHRydWU7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGhlYWRlcnMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoaGVhZGVycykpIHtcbiAgICAgICAgICAgIGlmIChrZXkudG9Mb3dlckNhc2UoKSA9PT0gJ2NvbnRlbnQtdHlwZScgJiYgXG4gICAgICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiBcbiAgICAgICAgICAgICAgICB2YWx1ZS5pbmNsdWRlcygndGV4dC9ldmVudC1zdHJlYW0nKSkge1xuICAgICAgICAgICAgICBpc1NTRSA9IHRydWU7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBcbiAgICAgIC8vIEludGVyY2VwdCBzZXRIZWFkZXIgdG8gZGV0ZWN0IFNTRSBieSBDb250ZW50LVR5cGVcbiAgICAgIGNvbnN0IG9yaWdpbmFsU2V0SGVhZGVyID0gcmVzLnNldEhlYWRlci5iaW5kKHJlcyk7XG4gICAgICByZXMuc2V0SGVhZGVyID0gKChuYW1lOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcgfCBudW1iZXIgfCByZWFkb25seSBzdHJpbmdbXSk6IFJlc3BvbnNlID0+IHtcbiAgICAgICAgaWYgKG5hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2NvbnRlbnQtdHlwZScgJiYgXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIFxuICAgICAgICAgICAgdmFsdWUuaW5jbHVkZXMoJ3RleHQvZXZlbnQtc3RyZWFtJykpIHtcbiAgICAgICAgICBpc1NTRSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsU2V0SGVhZGVyKG5hbWUsIHZhbHVlKTtcbiAgICAgIH0pIGFzIHR5cGVvZiByZXMuc2V0SGVhZGVyO1xuXG4gICAgICAvLyBJbnRlcmNlcHQgd3JpdGVIZWFkIHRvIGRldGVjdCBTU0UgKHVzZWQgYnkgQUkgU0RLJ3MgcGlwZVVJTWVzc2FnZVN0cmVhbVRvUmVzcG9uc2UpXG4gICAgICBjb25zdCBvcmlnaW5hbFdyaXRlSGVhZCA9IHJlcy53cml0ZUhlYWQuYmluZChyZXMpO1xuICAgICAgcmVzLndyaXRlSGVhZCA9ICgoXG4gICAgICAgIHN0YXR1c0NvZGU6IG51bWJlcixcbiAgICAgICAgc3RhdHVzTWVzc2FnZU9ySGVhZGVycz86IHN0cmluZyB8IGFueSxcbiAgICAgICAgbWF5YmVIZWFkZXJzPzogYW55XG4gICAgICApOiBSZXNwb25zZSA9PiB7XG4gICAgICAgIC8vIHdyaXRlSGVhZCBjYW4gYmUgY2FsbGVkIGFzOlxuICAgICAgICAvLyB3cml0ZUhlYWQoc3RhdHVzQ29kZSlcbiAgICAgICAgLy8gd3JpdGVIZWFkKHN0YXR1c0NvZGUsIGhlYWRlcnMpXG4gICAgICAgIC8vIHdyaXRlSGVhZChzdGF0dXNDb2RlLCBzdGF0dXNNZXNzYWdlLCBoZWFkZXJzKVxuICAgICAgICBsZXQgaGVhZGVycyA9IG1heWJlSGVhZGVycztcbiAgICAgICAgaWYgKCFoZWFkZXJzICYmIHR5cGVvZiBzdGF0dXNNZXNzYWdlT3JIZWFkZXJzID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgIGhlYWRlcnMgPSBzdGF0dXNNZXNzYWdlT3JIZWFkZXJzO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBjaGVja0NvbnRlbnRUeXBlRm9yU1NFKGhlYWRlcnMpO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsV3JpdGVIZWFkKHN0YXR1c0NvZGUsIHN0YXR1c01lc3NhZ2VPckhlYWRlcnMgYXMgYW55LCBtYXliZUhlYWRlcnMpO1xuICAgICAgfSkgYXMgdHlwZW9mIHJlcy53cml0ZUhlYWQ7XG5cbiAgICAgIC8vIENhcHR1cmUgcmVxdWVzdCBpbmZvXG4gICAgICBjb25zdCByZXF1ZXN0SW5mbzogTG9nRW50cnlbJ3JlcXVlc3QnXSA9IHtcbiAgICAgICAgYm9keTogcmVxLmJvZHksXG4gICAgICAgIHF1ZXJ5OiByZXEucXVlcnksXG4gICAgICB9O1xuXG4gICAgICBpZiAodGhpcy5pbmNsdWRlSGVhZGVycykge1xuICAgICAgICByZXF1ZXN0SW5mby5oZWFkZXJzID0gcmVxLmhlYWRlcnMgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgICAgIH1cblxuICAgICAgLy8gSW50ZXJjZXB0IHdyaXRlL2VuZCBmb3Igc3RyZWFtaW5nIGRldGVjdGlvblxuICAgICAgY29uc3Qgb3JpZ2luYWxXcml0ZSA9IHJlcy53cml0ZS5iaW5kKHJlcyk7XG4gICAgICBjb25zdCBvcmlnaW5hbEVuZCA9IHJlcy5lbmQuYmluZChyZXMpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxKc29uID0gcmVzLmpzb24uYmluZChyZXMpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxTZW5kID0gcmVzLnNlbmQuYmluZChyZXMpO1xuICAgICAgbGV0IHJlc3BvbnNlQm9keTogdW5rbm93bjtcbiAgICAgIGxldCBsb2dnZWQgPSBmYWxzZTtcblxuICAgICAgcmVzLndyaXRlID0gKChjaHVuazogYW55LCBlbmNvZGluZ09yQ2FsbGJhY2s/OiBhbnksIGNhbGxiYWNrPzogYW55KTogYm9vbGVhbiA9PiB7XG4gICAgICAgIGlmIChjaHVuaykge1xuICAgICAgICAgIC8vIFByb3Blcmx5IGRlY29kZSB0aGUgY2h1bmsgKGhhbmRsZXMgVWludDhBcnJheSBmcm9tIFRleHRFbmNvZGVyU3RyZWFtKVxuICAgICAgICAgIGNvbnN0IGNodW5rU3RyID0gdGhpcy5kZWNvZGVDaHVuayhjaHVuayk7XG4gICAgICAgICAgXG4gICAgICAgICAgLy8gQXV0by1kZXRlY3QgU1NFIGZyb20gY29udGVudCBpZiBub3QgYWxyZWFkeSBkZXRlY3RlZFxuICAgICAgICAgIGlmICghaXNTU0UgJiYgdGhpcy5sb29rc0xpa2VTU0UoY2h1bmtTdHIpKSB7XG4gICAgICAgICAgICBpc1NTRSA9IHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIFxuICAgICAgICAgIGlmIChpc1NTRSkge1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gdGhpcy5wYXJzZVNTRUNodW5rKGNodW5rU3RyLCB0ZXh0RGVsdGFzKTtcbiAgICAgICAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgICAgICAgY2h1bmtzLnB1c2gocGFyc2VkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsV3JpdGUoY2h1bmssIGVuY29kaW5nT3JDYWxsYmFjaywgY2FsbGJhY2spO1xuICAgICAgfSkgYXMgdHlwZW9mIHJlcy53cml0ZTtcblxuICAgICAgcmVzLmVuZCA9ICgoY2h1bms/OiBhbnksIGVuY29kaW5nT3JDYWxsYmFjaz86IGFueSwgY2FsbGJhY2s/OiBhbnkpOiBSZXNwb25zZSA9PiB7XG4gICAgICAgIGlmIChsb2dnZWQpIHJldHVybiBvcmlnaW5hbEVuZChjaHVuaywgZW5jb2RpbmdPckNhbGxiYWNrLCBjYWxsYmFjayk7XG4gICAgICAgIGxvZ2dlZCA9IHRydWU7XG5cbiAgICAgICAgaWYgKGlzU1NFKSB7XG4gICAgICAgICAgLy8gU1NFIHN0cmVhbWluZyBwYXRoXG4gICAgICAgICAgaWYgKGNodW5rKSB7XG4gICAgICAgICAgICBjb25zdCBjaHVua1N0ciA9IHRoaXMuZGVjb2RlQ2h1bmsoY2h1bmspO1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gdGhpcy5wYXJzZVNTRUNodW5rKGNodW5rU3RyLCB0ZXh0RGVsdGFzKTtcbiAgICAgICAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgICAgICAgY2h1bmtzLnB1c2gocGFyc2VkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICAgIHR5cGU6ICdodHRwJyxcbiAgICAgICAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCxcbiAgICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgICAgc3RhdHVzQ29kZTogcmVzLnN0YXR1c0NvZGUsXG4gICAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgICAgICBzdHJlYW1pbmc6IHRydWUsXG4gICAgICAgICAgICAgIGNodW5rcyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfTtcblxuICAgICAgICAgIC8vIEFkZCBhZ2dyZWdhdGVkIHRleHQgaWYgd2UgY29sbGVjdGVkIHRleHQtZGVsdGFzXG4gICAgICAgICAgaWYgKHRleHREZWx0YXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZW50cnkucmVzcG9uc2UhLnRleHQgPSB0ZXh0RGVsdGFzLmpvaW4oJycpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChyZXEubG9nTWV0YWRhdGEgJiYgT2JqZWN0LmtleXMocmVxLmxvZ01ldGFkYXRhKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBlbnRyeS5tZXRhZGF0YSA9IHJlcS5sb2dNZXRhZGF0YTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLndyaXRlKGVudHJ5KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZWd1bGFyIHJlc3BvbnNlIHBhdGhcbiAgICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICAgIHR5cGU6ICdodHRwJyxcbiAgICAgICAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCxcbiAgICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgICAgc3RhdHVzQ29kZTogcmVzLnN0YXR1c0NvZGUsXG4gICAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgICAgICBib2R5OiByZXNwb25zZUJvZHksXG4gICAgICAgICAgICAgIHN0cmVhbWluZzogZmFsc2UsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBpZiAocmVxLmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKHJlcS5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZW50cnkubWV0YWRhdGEgPSByZXEubG9nTWV0YWRhdGE7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy53cml0ZShlbnRyeSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gb3JpZ2luYWxFbmQoY2h1bmssIGVuY29kaW5nT3JDYWxsYmFjaywgY2FsbGJhY2spO1xuICAgICAgfSkgYXMgdHlwZW9mIHJlcy5lbmQ7XG5cbiAgICAgIGNvbnN0IGxvZ1Jlc3BvbnNlID0gKCkgPT4ge1xuICAgICAgICBpZiAobG9nZ2VkKSByZXR1cm47XG4gICAgICAgIGxvZ2dlZCA9IHRydWU7XG5cbiAgICAgICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICB0eXBlOiAnaHR0cCcsXG4gICAgICAgICAgbWV0aG9kOiByZXEubWV0aG9kLFxuICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLFxuICAgICAgICAgIGR1cmF0aW9uOiBEYXRlLm5vdygpIC0gc3RhcnQsXG4gICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgIGJvZHk6IHJlc3BvbnNlQm9keSxcbiAgICAgICAgICAgIHN0cmVhbWluZzogZmFsc2UsXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcblxuICAgICAgICBpZiAocmVxLmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKHJlcS5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gcmVxLmxvZ01ldGFkYXRhO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy53cml0ZShlbnRyeSk7XG4gICAgICB9O1xuXG4gICAgICByZXMuanNvbiA9IChib2R5OiBhbnkpID0+IHtcbiAgICAgICAgcmVzcG9uc2VCb2R5ID0gYm9keTtcbiAgICAgICAgbG9nUmVzcG9uc2UoKTtcbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsSnNvbihib2R5KTtcbiAgICAgIH07XG5cbiAgICAgIHJlcy5zZW5kID0gKGJvZHk6IGFueSkgPT4ge1xuICAgICAgICBpZiAoIWxvZ2dlZCkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXNwb25zZUJvZHkgPSB0eXBlb2YgYm9keSA9PT0gJ3N0cmluZycgPyBKU09OLnBhcnNlKGJvZHkpIDogYm9keTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIHJlc3BvbnNlQm9keSA9IGJvZHk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ1Jlc3BvbnNlKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsU2VuZChib2R5KTtcbiAgICAgIH07XG5cbiAgICAgIG5leHQoKTtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBwYXJzZVNTRUNodW5rKHJhdzogc3RyaW5nLCB0ZXh0RGVsdGFzOiBzdHJpbmdbXSk6IHVua25vd24ge1xuICAgIGNvbnN0IGxpbmVzID0gcmF3LnNwbGl0KCdcXG4nKTtcbiAgICBjb25zdCByZXN1bHRzOiB1bmtub3duW10gPSBbXTtcblxuICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgaWYgKGxpbmUuc3RhcnRzV2l0aCgnZGF0YTogJykpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGxpbmUuc2xpY2UoNikudHJpbSgpO1xuICAgICAgICBpZiAoZGF0YSA9PT0gJ1tET05FXScpIHtcbiAgICAgICAgICByZXN1bHRzLnB1c2goeyB0eXBlOiAnZG9uZScgfSk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIEhhbmRsZSB0ZXh0LWRlbHRhIHR5cGUgLSBjb2xsZWN0IHRoZSBkZWx0YSB0ZXh0XG4gICAgICAgICAgaWYgKHBhcnNlZC50eXBlID09PSAndGV4dC1kZWx0YScgJiYgdHlwZW9mIHBhcnNlZC5kZWx0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgIHRleHREZWx0YXMucHVzaChwYXJzZWQuZGVsdGEpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICByZXN1bHRzLnB1c2gocGFyc2VkKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgcmVzdWx0cy5wdXNoKHsgcmF3OiBkYXRhIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gUmV0dXJuIHNpbmdsZSByZXN1bHQgb3IgYXJyYXlcbiAgICBpZiAocmVzdWx0cy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICAgIGlmIChyZXN1bHRzLmxlbmd0aCA9PT0gMSkgcmV0dXJuIHJlc3VsdHNbMF07XG4gICAgcmV0dXJuIHJlc3VsdHM7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gdFJQQyBNaWRkbGV3YXJlXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIHRycGNNaWRkbGV3YXJlPFRDb250ZXh0IGV4dGVuZHMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4oKSB7XG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcztcblxuICAgIHJldHVybiBhc3luYyBmdW5jdGlvbiBsb2dnZXJNaWRkbGV3YXJlKG9wdHM6IHtcbiAgICAgIHBhdGg6IHN0cmluZztcbiAgICAgIHR5cGU6ICdxdWVyeScgfCAnbXV0YXRpb24nIHwgJ3N1YnNjcmlwdGlvbic7XG4gICAgICBpbnB1dDogdW5rbm93bjtcbiAgICAgIGN0eDogVENvbnRleHQgJiB7IGxvZ01ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfTtcbiAgICAgIG5leHQ6ICgpID0+IFByb21pc2U8eyBvazogYm9vbGVhbjsgZGF0YT86IHVua25vd247IGVycm9yPzogRXJyb3IgfT47XG4gICAgfSkge1xuICAgICAgLy8gQ2hlY2sgaWYgd2Ugc2hvdWxkIGxvZyB0aGlzIHBhdGhcbiAgICAgIGlmICghbG9nZ2VyLnNob3VsZExvZyhvcHRzLnBhdGgpKSB7XG4gICAgICAgIHJldHVybiBvcHRzLm5leHQoKTtcbiAgICAgIH1cblxuICAgICAgY29uc3Qgc3RhcnQgPSBEYXRlLm5vdygpO1xuICAgICAgY29uc3QgcmVxdWVzdElkID0gbG9nZ2VyLmdlbmVyYXRlUmVxdWVzdElkKCk7XG5cbiAgICAgIC8vIEluaXRpYWxpemUgbWV0YWRhdGEgb24gY29udGV4dCBpZiBub3QgcHJlc2VudFxuICAgICAgaWYgKCFvcHRzLmN0eC5sb2dNZXRhZGF0YSkge1xuICAgICAgICBvcHRzLmN0eC5sb2dNZXRhZGF0YSA9IHt9O1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBvcHRzLm5leHQoKTtcblxuICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgcmVxdWVzdElkLFxuICAgICAgICAgIHR5cGU6ICd0cnBjJyxcbiAgICAgICAgICBtZXRob2Q6IG9wdHMudHlwZS50b1VwcGVyQ2FzZSgpLFxuICAgICAgICAgIHBhdGg6IG9wdHMucGF0aCxcbiAgICAgICAgICBzdGF0dXNDb2RlOiByZXN1bHQub2sgPyAyMDAgOiA1MDAsXG4gICAgICAgICAgZHVyYXRpb246IERhdGUubm93KCkgLSBzdGFydCxcbiAgICAgICAgICByZXF1ZXN0OiB7XG4gICAgICAgICAgICBib2R5OiBvcHRzLmlucHV0LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgIGJvZHk6IHJlc3VsdC5kYXRhLFxuICAgICAgICAgICAgc3RyZWFtaW5nOiBmYWxzZSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIEF0dGFjaCBtZXRhZGF0YSBpZiBwcmVzZW50XG4gICAgICAgIGlmIChvcHRzLmN0eC5sb2dNZXRhZGF0YSAmJiBPYmplY3Qua2V5cyhvcHRzLmN0eC5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gb3B0cy5jdHgubG9nTWV0YWRhdGE7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgZW50cnkuZXJyb3IgPSB7XG4gICAgICAgICAgICBtZXNzYWdlOiByZXN1bHQuZXJyb3IubWVzc2FnZSxcbiAgICAgICAgICAgIHN0YWNrOiByZXN1bHQuZXJyb3Iuc3RhY2ssXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxvZ2dlci53cml0ZShlbnRyeSk7XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnN0IGVyciA9IGVycm9yIGFzIEVycm9yO1xuXG4gICAgICAgIGNvbnN0IGVudHJ5OiBMb2dFbnRyeSA9IHtcbiAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgdHlwZTogJ3RycGMnLFxuICAgICAgICAgIG1ldGhvZDogb3B0cy50eXBlLnRvVXBwZXJDYXNlKCksXG4gICAgICAgICAgcGF0aDogb3B0cy5wYXRoLFxuICAgICAgICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgIHJlcXVlc3Q6IHtcbiAgICAgICAgICAgIGJvZHk6IG9wdHMuaW5wdXQsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBlcnJvcjoge1xuICAgICAgICAgICAgbWVzc2FnZTogZXJyLm1lc3NhZ2UsXG4gICAgICAgICAgICBzdGFjazogZXJyLnN0YWNrLFxuICAgICAgICAgIH0sXG4gICAgICAgIH07XG5cbiAgICAgICAgLy8gQXR0YWNoIG1ldGFkYXRhIGlmIHByZXNlbnRcbiAgICAgICAgaWYgKG9wdHMuY3R4LmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKG9wdHMuY3R4LmxvZ01ldGFkYXRhKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgZW50cnkubWV0YWRhdGEgPSBvcHRzLmN0eC5sb2dNZXRhZGF0YTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxvZ2dlci53cml0ZShlbnRyeSk7XG5cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfTtcbiAgfVxufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBGYWN0b3J5IEZ1bmN0aW9uIChNYWluIEV4cG9ydClcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUxvZ2dlcihmaWxlUGF0aDogc3RyaW5nLCBvcHRpb25zPzogTG9nZ2VyT3B0aW9ucykge1xuICBjb25zdCBsb2dnZXIgPSBuZXcgVW5pZmllZExvZ2dlcihmaWxlUGF0aCwgb3B0aW9ucyk7XG5cbiAgcmV0dXJuIHtcbiAgICAvKiogRXhwcmVzcyBtaWRkbGV3YXJlIC0gdXNlIHdpdGggYXBwLnVzZSgpICovXG4gICAgZXhwcmVzczogKCkgPT4gbG9nZ2VyLmV4cHJlc3NNaWRkbGV3YXJlKCksXG5cbiAgICAvKiogdFJQQyBtaWRkbGV3YXJlIC0gdXNlIHdpdGggdC5wcm9jZWR1cmUudXNlKCkgKi9cbiAgICB0cnBjOiA8VENvbnRleHQgZXh0ZW5kcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IFJlY29yZDxzdHJpbmcsIHVua25vd24+PigpID0+IFxuICAgICAgbG9nZ2VyLnRycGNNaWRkbGV3YXJlPFRDb250ZXh0PigpLFxuXG4gICAgLyoqIERpcmVjdCB3cml0ZSBhY2Nlc3MgZm9yIGN1c3RvbSBsb2dnaW5nICovXG4gICAgd3JpdGU6IChlbnRyeTogUGFydGlhbDxMb2dFbnRyeT4pID0+IGxvZ2dlci53cml0ZSh7XG4gICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgIHJlcXVlc3RJZDogYCR7RGF0ZS5ub3coKS50b1N0cmluZygzNil9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMiwgOSl9YCxcbiAgICAgIHR5cGU6ICdodHRwJyxcbiAgICAgIG1ldGhvZDogJ0NVU1RPTScsXG4gICAgICBwYXRoOiAnLycsXG4gICAgICBkdXJhdGlvbjogMCxcbiAgICAgIC4uLmVudHJ5LFxuICAgIH0gYXMgTG9nRW50cnkpLFxuICB9O1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIZWxwZXIgdG8gYXR0YWNoIG1ldGFkYXRhIChmb3IgY2xlYW5lciBBUEkpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogQXR0YWNoIG1ldGFkYXRhIHRvIHRoZSBjdXJyZW50IHJlcXVlc3QgbG9nIGVudHJ5IChFeHByZXNzKVxuICovXG5leHBvcnQgZnVuY3Rpb24gYXR0YWNoTWV0YWRhdGEocmVxOiBSZXF1ZXN0LCBtZXRhZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiB2b2lkIHtcbiAgaWYgKCFyZXEubG9nTWV0YWRhdGEpIHtcbiAgICByZXEubG9nTWV0YWRhdGEgPSB7fTtcbiAgfVxuICBPYmplY3QuYXNzaWduKHJlcS5sb2dNZXRhZGF0YSwgbWV0YWRhdGEpO1xufVxuXG4vKipcbiAqIEF0dGFjaCBtZXRhZGF0YSB0byB0aGUgY3VycmVudCByZXF1ZXN0IGxvZyBlbnRyeSAodFJQQylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGF0dGFjaFRycGNNZXRhZGF0YTxUQ29udGV4dCBleHRlbmRzIHsgbG9nTWV0YWRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PihcbiAgY3R4OiBUQ29udGV4dCxcbiAgbWV0YWRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+XG4pOiB2b2lkIHtcbiAgaWYgKCFjdHgubG9nTWV0YWRhdGEpIHtcbiAgICBjdHgubG9nTWV0YWRhdGEgPSB7fTtcbiAgfVxuICBPYmplY3QuYXNzaWduKGN0eC5sb2dNZXRhZGF0YSwgbWV0YWRhdGEpO1xufVxuXG4vLyBEZWZhdWx0IGV4cG9ydCBmb3Igc2ltcGxlciBpbXBvcnRzXG5leHBvcnQgZGVmYXVsdCBjcmVhdGVMb2dnZXI7XG4iXX0=
|
package/package.json
CHANGED
package/src/logger.ts
CHANGED
|
@@ -23,6 +23,7 @@ interface LogEntry {
|
|
|
23
23
|
body?: unknown;
|
|
24
24
|
streaming?: boolean;
|
|
25
25
|
chunks?: unknown[];
|
|
26
|
+
text?: string; // Aggregated text from text-delta chunks
|
|
26
27
|
};
|
|
27
28
|
error?: {
|
|
28
29
|
message: string;
|
|
@@ -35,6 +36,8 @@ interface LoggerOptions {
|
|
|
35
36
|
maxSizeBytes?: number; // Default: 10MB
|
|
36
37
|
includeHeaders?: boolean; // Default: false
|
|
37
38
|
redact?: string[]; // Fields to redact from logs
|
|
39
|
+
ignorePaths?: string[]; // Paths to ignore (supports wildcards like /health/*)
|
|
40
|
+
includePaths?: string[]; // Only log these paths (supports wildcards)
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
// Extend Express Request to include metadata
|
|
@@ -55,12 +58,16 @@ class UnifiedLogger {
|
|
|
55
58
|
private maxSizeBytes: number;
|
|
56
59
|
private includeHeaders: boolean;
|
|
57
60
|
private redactFields: Set<string>;
|
|
61
|
+
private ignorePaths: string[];
|
|
62
|
+
private includePaths: string[];
|
|
58
63
|
|
|
59
64
|
constructor(filePath: string, options: LoggerOptions = {}) {
|
|
60
65
|
this.filePath = path.resolve(filePath);
|
|
61
66
|
this.maxSizeBytes = options.maxSizeBytes ?? 10 * 1024 * 1024; // 10MB default
|
|
62
67
|
this.includeHeaders = options.includeHeaders ?? false;
|
|
63
68
|
this.redactFields = new Set(options.redact ?? ['password', 'token', 'authorization', 'cookie']);
|
|
69
|
+
this.ignorePaths = options.ignorePaths ?? [];
|
|
70
|
+
this.includePaths = options.includePaths ?? [];
|
|
64
71
|
|
|
65
72
|
// Ensure directory exists
|
|
66
73
|
const dir = path.dirname(this.filePath);
|
|
@@ -73,6 +80,34 @@ class UnifiedLogger {
|
|
|
73
80
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
74
81
|
}
|
|
75
82
|
|
|
83
|
+
private matchPath(pattern: string, requestPath: string): boolean {
|
|
84
|
+
// Convert wildcard pattern to regex
|
|
85
|
+
// /api/* matches /api/anything
|
|
86
|
+
// /health matches exactly /health
|
|
87
|
+
const regexPattern = pattern
|
|
88
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
|
|
89
|
+
.replace(/\*/g, '.*'); // Convert * to .*
|
|
90
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
91
|
+
return regex.test(requestPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private shouldLog(requestPath: string): boolean {
|
|
95
|
+
// Extract path without query string
|
|
96
|
+
const pathOnly = requestPath.split('?')[0];
|
|
97
|
+
|
|
98
|
+
// If includePaths is set, only log matching paths
|
|
99
|
+
if (this.includePaths.length > 0) {
|
|
100
|
+
return this.includePaths.some((pattern) => this.matchPath(pattern, pathOnly));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If ignorePaths is set, skip matching paths
|
|
104
|
+
if (this.ignorePaths.length > 0) {
|
|
105
|
+
return !this.ignorePaths.some((pattern) => this.matchPath(pattern, pathOnly));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
76
111
|
private redact(obj: unknown): unknown {
|
|
77
112
|
if (obj === null || obj === undefined) return obj;
|
|
78
113
|
if (typeof obj !== 'object') return obj;
|
|
@@ -123,21 +158,107 @@ class UnifiedLogger {
|
|
|
123
158
|
}
|
|
124
159
|
}
|
|
125
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Decode chunk to string, handling Uint8Array/Buffer from TextEncoderStream
|
|
163
|
+
*/
|
|
164
|
+
private decodeChunk(chunk: any): string {
|
|
165
|
+
if (chunk === null || chunk === undefined) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Handle Uint8Array (from TextEncoderStream in AI SDK)
|
|
170
|
+
if (chunk instanceof Uint8Array) {
|
|
171
|
+
return Buffer.from(chunk).toString('utf-8');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Handle Buffer
|
|
175
|
+
if (Buffer.isBuffer(chunk)) {
|
|
176
|
+
return chunk.toString('utf-8');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Handle string
|
|
180
|
+
if (typeof chunk === 'string') {
|
|
181
|
+
return chunk;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Fallback: try toString but check if it looks like byte array
|
|
185
|
+
const str = chunk.toString();
|
|
186
|
+
|
|
187
|
+
// Detect if toString produced a comma-separated byte list like "100,97,116,97"
|
|
188
|
+
// This happens when Uint8Array.toString() is called without proper decoding
|
|
189
|
+
if (/^\d+(,\d+)*$/.test(str) && str.includes(',')) {
|
|
190
|
+
try {
|
|
191
|
+
const bytes = new Uint8Array(str.split(',').map(Number));
|
|
192
|
+
return Buffer.from(bytes).toString('utf-8');
|
|
193
|
+
} catch {
|
|
194
|
+
return str;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return str;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Check if content looks like SSE data
|
|
203
|
+
*/
|
|
204
|
+
private looksLikeSSE(content: string): boolean {
|
|
205
|
+
return content.trimStart().startsWith('data:') ||
|
|
206
|
+
content.includes('\ndata:') ||
|
|
207
|
+
content.trimStart().startsWith('event:');
|
|
208
|
+
}
|
|
209
|
+
|
|
126
210
|
// ===========================================================================
|
|
127
211
|
// Express Middleware
|
|
128
212
|
// ===========================================================================
|
|
129
213
|
|
|
130
214
|
expressMiddleware() {
|
|
131
215
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
216
|
+
const requestPath = req.originalUrl || req.url;
|
|
217
|
+
|
|
218
|
+
// Check if we should log this path
|
|
219
|
+
if (!this.shouldLog(requestPath)) {
|
|
220
|
+
return next();
|
|
221
|
+
}
|
|
222
|
+
|
|
132
223
|
const start = Date.now();
|
|
133
224
|
const requestId = this.generateRequestId();
|
|
134
225
|
|
|
135
226
|
// Initialize metadata object on request
|
|
136
227
|
req.logMetadata = {};
|
|
137
228
|
|
|
138
|
-
// Detect SSE - check
|
|
229
|
+
// Detect SSE - check request Accept header initially
|
|
139
230
|
let isSSE = req.headers.accept === 'text/event-stream';
|
|
140
231
|
const chunks: unknown[] = [];
|
|
232
|
+
const textDeltas: string[] = []; // Collect text-delta content
|
|
233
|
+
|
|
234
|
+
// Helper to check Content-Type header for SSE
|
|
235
|
+
const checkContentTypeForSSE = (headers: any): void => {
|
|
236
|
+
if (!headers) return;
|
|
237
|
+
|
|
238
|
+
// headers can be an object or array of [key, value] pairs
|
|
239
|
+
if (Array.isArray(headers)) {
|
|
240
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
241
|
+
const key = headers[i];
|
|
242
|
+
const value = headers[i + 1];
|
|
243
|
+
if (typeof key === 'string' &&
|
|
244
|
+
key.toLowerCase() === 'content-type' &&
|
|
245
|
+
typeof value === 'string' &&
|
|
246
|
+
value.includes('text/event-stream')) {
|
|
247
|
+
isSSE = true;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} else if (typeof headers === 'object') {
|
|
252
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
253
|
+
if (key.toLowerCase() === 'content-type' &&
|
|
254
|
+
typeof value === 'string' &&
|
|
255
|
+
value.includes('text/event-stream')) {
|
|
256
|
+
isSSE = true;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
141
262
|
|
|
142
263
|
// Intercept setHeader to detect SSE by Content-Type
|
|
143
264
|
const originalSetHeader = res.setHeader.bind(res);
|
|
@@ -150,6 +271,27 @@ class UnifiedLogger {
|
|
|
150
271
|
return originalSetHeader(name, value);
|
|
151
272
|
}) as typeof res.setHeader;
|
|
152
273
|
|
|
274
|
+
// Intercept writeHead to detect SSE (used by AI SDK's pipeUIMessageStreamToResponse)
|
|
275
|
+
const originalWriteHead = res.writeHead.bind(res);
|
|
276
|
+
res.writeHead = ((
|
|
277
|
+
statusCode: number,
|
|
278
|
+
statusMessageOrHeaders?: string | any,
|
|
279
|
+
maybeHeaders?: any
|
|
280
|
+
): Response => {
|
|
281
|
+
// writeHead can be called as:
|
|
282
|
+
// writeHead(statusCode)
|
|
283
|
+
// writeHead(statusCode, headers)
|
|
284
|
+
// writeHead(statusCode, statusMessage, headers)
|
|
285
|
+
let headers = maybeHeaders;
|
|
286
|
+
if (!headers && typeof statusMessageOrHeaders === 'object') {
|
|
287
|
+
headers = statusMessageOrHeaders;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
checkContentTypeForSSE(headers);
|
|
291
|
+
|
|
292
|
+
return originalWriteHead(statusCode, statusMessageOrHeaders as any, maybeHeaders);
|
|
293
|
+
}) as typeof res.writeHead;
|
|
294
|
+
|
|
153
295
|
// Capture request info
|
|
154
296
|
const requestInfo: LogEntry['request'] = {
|
|
155
297
|
body: req.body,
|
|
@@ -169,12 +311,20 @@ class UnifiedLogger {
|
|
|
169
311
|
let logged = false;
|
|
170
312
|
|
|
171
313
|
res.write = ((chunk: any, encodingOrCallback?: any, callback?: any): boolean => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const chunkStr =
|
|
175
|
-
|
|
176
|
-
if
|
|
177
|
-
|
|
314
|
+
if (chunk) {
|
|
315
|
+
// Properly decode the chunk (handles Uint8Array from TextEncoderStream)
|
|
316
|
+
const chunkStr = this.decodeChunk(chunk);
|
|
317
|
+
|
|
318
|
+
// Auto-detect SSE from content if not already detected
|
|
319
|
+
if (!isSSE && this.looksLikeSSE(chunkStr)) {
|
|
320
|
+
isSSE = true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (isSSE) {
|
|
324
|
+
const parsed = this.parseSSEChunk(chunkStr, textDeltas);
|
|
325
|
+
if (parsed) {
|
|
326
|
+
chunks.push(parsed);
|
|
327
|
+
}
|
|
178
328
|
}
|
|
179
329
|
}
|
|
180
330
|
return originalWrite(chunk, encodingOrCallback, callback);
|
|
@@ -187,8 +337,8 @@ class UnifiedLogger {
|
|
|
187
337
|
if (isSSE) {
|
|
188
338
|
// SSE streaming path
|
|
189
339
|
if (chunk) {
|
|
190
|
-
const chunkStr =
|
|
191
|
-
const parsed = this.parseSSEChunk(chunkStr);
|
|
340
|
+
const chunkStr = this.decodeChunk(chunk);
|
|
341
|
+
const parsed = this.parseSSEChunk(chunkStr, textDeltas);
|
|
192
342
|
if (parsed) {
|
|
193
343
|
chunks.push(parsed);
|
|
194
344
|
}
|
|
@@ -199,7 +349,7 @@ class UnifiedLogger {
|
|
|
199
349
|
requestId,
|
|
200
350
|
type: 'http',
|
|
201
351
|
method: req.method,
|
|
202
|
-
path:
|
|
352
|
+
path: requestPath,
|
|
203
353
|
statusCode: res.statusCode,
|
|
204
354
|
duration: Date.now() - start,
|
|
205
355
|
request: requestInfo,
|
|
@@ -209,6 +359,11 @@ class UnifiedLogger {
|
|
|
209
359
|
},
|
|
210
360
|
};
|
|
211
361
|
|
|
362
|
+
// Add aggregated text if we collected text-deltas
|
|
363
|
+
if (textDeltas.length > 0) {
|
|
364
|
+
entry.response!.text = textDeltas.join('');
|
|
365
|
+
}
|
|
366
|
+
|
|
212
367
|
if (req.logMetadata && Object.keys(req.logMetadata).length > 0) {
|
|
213
368
|
entry.metadata = req.logMetadata;
|
|
214
369
|
}
|
|
@@ -221,7 +376,7 @@ class UnifiedLogger {
|
|
|
221
376
|
requestId,
|
|
222
377
|
type: 'http',
|
|
223
378
|
method: req.method,
|
|
224
|
-
path:
|
|
379
|
+
path: requestPath,
|
|
225
380
|
statusCode: res.statusCode,
|
|
226
381
|
duration: Date.now() - start,
|
|
227
382
|
request: requestInfo,
|
|
@@ -250,7 +405,7 @@ class UnifiedLogger {
|
|
|
250
405
|
requestId,
|
|
251
406
|
type: 'http',
|
|
252
407
|
method: req.method,
|
|
253
|
-
path:
|
|
408
|
+
path: requestPath,
|
|
254
409
|
statusCode: res.statusCode,
|
|
255
410
|
duration: Date.now() - start,
|
|
256
411
|
request: requestInfo,
|
|
@@ -289,20 +444,36 @@ class UnifiedLogger {
|
|
|
289
444
|
};
|
|
290
445
|
}
|
|
291
446
|
|
|
292
|
-
private parseSSEChunk(raw: string): unknown {
|
|
447
|
+
private parseSSEChunk(raw: string, textDeltas: string[]): unknown {
|
|
293
448
|
const lines = raw.split('\n');
|
|
449
|
+
const results: unknown[] = [];
|
|
450
|
+
|
|
294
451
|
for (const line of lines) {
|
|
295
452
|
if (line.startsWith('data: ')) {
|
|
296
453
|
const data = line.slice(6).trim();
|
|
297
|
-
if (data === '[DONE]')
|
|
454
|
+
if (data === '[DONE]') {
|
|
455
|
+
results.push({ type: 'done' });
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
298
458
|
try {
|
|
299
|
-
|
|
459
|
+
const parsed = JSON.parse(data);
|
|
460
|
+
|
|
461
|
+
// Handle text-delta type - collect the delta text
|
|
462
|
+
if (parsed.type === 'text-delta' && typeof parsed.delta === 'string') {
|
|
463
|
+
textDeltas.push(parsed.delta);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
results.push(parsed);
|
|
300
467
|
} catch {
|
|
301
|
-
|
|
468
|
+
results.push({ raw: data });
|
|
302
469
|
}
|
|
303
470
|
}
|
|
304
471
|
}
|
|
305
|
-
|
|
472
|
+
|
|
473
|
+
// Return single result or array
|
|
474
|
+
if (results.length === 0) return null;
|
|
475
|
+
if (results.length === 1) return results[0];
|
|
476
|
+
return results;
|
|
306
477
|
}
|
|
307
478
|
|
|
308
479
|
// ===========================================================================
|
|
@@ -319,6 +490,11 @@ class UnifiedLogger {
|
|
|
319
490
|
ctx: TContext & { logMetadata?: Record<string, unknown> };
|
|
320
491
|
next: () => Promise<{ ok: boolean; data?: unknown; error?: Error }>;
|
|
321
492
|
}) {
|
|
493
|
+
// Check if we should log this path
|
|
494
|
+
if (!logger.shouldLog(opts.path)) {
|
|
495
|
+
return opts.next();
|
|
496
|
+
}
|
|
497
|
+
|
|
322
498
|
const start = Date.now();
|
|
323
499
|
const requestId = logger.generateRequestId();
|
|
324
500
|
|
package/test/logger.test.ts
CHANGED
|
@@ -196,7 +196,7 @@ describe('mohen logger', () => {
|
|
|
196
196
|
{ count: 1 },
|
|
197
197
|
{ count: 2 },
|
|
198
198
|
{ count: 3 },
|
|
199
|
-
{
|
|
199
|
+
{ type: 'done' },
|
|
200
200
|
],
|
|
201
201
|
},
|
|
202
202
|
});
|
|
@@ -468,6 +468,211 @@ describe('mohen logger', () => {
|
|
|
468
468
|
});
|
|
469
469
|
});
|
|
470
470
|
|
|
471
|
+
describe('Path Filtering', () => {
|
|
472
|
+
it('should ignore paths matching ignorePaths patterns', async () => {
|
|
473
|
+
vi.useRealTimers();
|
|
474
|
+
clearLogFile();
|
|
475
|
+
|
|
476
|
+
const filteredLogger = createLogger(TEST_LOG_FILE, {
|
|
477
|
+
ignorePaths: ['/health', '/health/*', '/metrics'],
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const filteredApp = express();
|
|
481
|
+
filteredApp.use(express.json());
|
|
482
|
+
filteredApp.use(filteredLogger.express());
|
|
483
|
+
filteredApp.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
484
|
+
filteredApp.get('/health/live', (req, res) => res.json({ live: true }));
|
|
485
|
+
filteredApp.get('/metrics', (req, res) => res.json({ cpu: 50 }));
|
|
486
|
+
filteredApp.get('/api/users', (req, res) => res.json({ users: [] }));
|
|
487
|
+
|
|
488
|
+
await request(filteredApp).get('/health').expect(200);
|
|
489
|
+
await request(filteredApp).get('/health/live').expect(200);
|
|
490
|
+
await request(filteredApp).get('/metrics').expect(200);
|
|
491
|
+
await request(filteredApp).get('/api/users').expect(200);
|
|
492
|
+
|
|
493
|
+
const entries = readLogEntries();
|
|
494
|
+
expect(entries).toHaveLength(1);
|
|
495
|
+
expect(entries[0].path).toBe('/api/users');
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should only log paths matching includePaths patterns', async () => {
|
|
499
|
+
vi.useRealTimers();
|
|
500
|
+
clearLogFile();
|
|
501
|
+
|
|
502
|
+
const filteredLogger = createLogger(TEST_LOG_FILE, {
|
|
503
|
+
includePaths: ['/api/*'],
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const filteredApp = express();
|
|
507
|
+
filteredApp.use(express.json());
|
|
508
|
+
filteredApp.use(filteredLogger.express());
|
|
509
|
+
filteredApp.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
510
|
+
filteredApp.get('/api/users', (req, res) => res.json({ users: [] }));
|
|
511
|
+
filteredApp.get('/api/orders', (req, res) => res.json({ orders: [] }));
|
|
512
|
+
|
|
513
|
+
await request(filteredApp).get('/health').expect(200);
|
|
514
|
+
await request(filteredApp).get('/api/users').expect(200);
|
|
515
|
+
await request(filteredApp).get('/api/orders').expect(200);
|
|
516
|
+
|
|
517
|
+
const entries = readLogEntries();
|
|
518
|
+
expect(entries).toHaveLength(2);
|
|
519
|
+
expect(entries[0].path).toBe('/api/users');
|
|
520
|
+
expect(entries[1].path).toBe('/api/orders');
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
describe('AI SDK Style Streaming', () => {
|
|
525
|
+
it('should detect SSE via writeHead (AI SDK pipeUIMessageStreamToResponse)', async () => {
|
|
526
|
+
app.get('/api/ai-stream', (req, res) => {
|
|
527
|
+
// AI SDK uses writeHead instead of setHeader
|
|
528
|
+
res.writeHead(200, {
|
|
529
|
+
'Content-Type': 'text/event-stream',
|
|
530
|
+
'Cache-Control': 'no-cache',
|
|
531
|
+
'Connection': 'keep-alive',
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
res.write('data: {"type":"start"}\n\n');
|
|
535
|
+
res.write('data: {"type":"text-delta","delta":"Hello"}\n\n');
|
|
536
|
+
res.write('data: {"type":"finish"}\n\n');
|
|
537
|
+
res.end();
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
await request(app)
|
|
541
|
+
.get('/api/ai-stream')
|
|
542
|
+
.expect(200);
|
|
543
|
+
|
|
544
|
+
const entries = readLogEntries();
|
|
545
|
+
expect(entries).toHaveLength(1);
|
|
546
|
+
expect(entries[0]).toMatchObject({
|
|
547
|
+
type: 'http',
|
|
548
|
+
method: 'GET',
|
|
549
|
+
path: '/api/ai-stream',
|
|
550
|
+
response: {
|
|
551
|
+
streaming: true,
|
|
552
|
+
text: 'Hello',
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
expect(entries[0].response.chunks).toContainEqual({ type: 'start' });
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('should handle Uint8Array chunks from TextEncoderStream', async () => {
|
|
559
|
+
app.get('/api/binary-stream', (req, res) => {
|
|
560
|
+
res.writeHead(200, {
|
|
561
|
+
'Content-Type': 'text/event-stream',
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
// Simulate TextEncoderStream output (Uint8Array)
|
|
565
|
+
const encoder = new TextEncoder();
|
|
566
|
+
res.write(encoder.encode('data: {"type":"start"}\n\n'));
|
|
567
|
+
res.write(encoder.encode('data: {"type":"text-delta","delta":"Binary"}\n\n'));
|
|
568
|
+
res.write(encoder.encode('data: {"type":"text-delta","delta":" works"}\n\n'));
|
|
569
|
+
res.write(encoder.encode('data: [DONE]\n\n'));
|
|
570
|
+
res.end();
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
await request(app)
|
|
574
|
+
.get('/api/binary-stream')
|
|
575
|
+
.expect(200);
|
|
576
|
+
|
|
577
|
+
const entries = readLogEntries();
|
|
578
|
+
expect(entries).toHaveLength(1);
|
|
579
|
+
expect(entries[0]).toMatchObject({
|
|
580
|
+
type: 'http',
|
|
581
|
+
path: '/api/binary-stream',
|
|
582
|
+
response: {
|
|
583
|
+
streaming: true,
|
|
584
|
+
text: 'Binary works',
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('should auto-detect SSE from content when headers not set', async () => {
|
|
590
|
+
app.get('/api/auto-detect', (req, res) => {
|
|
591
|
+
// No SSE headers set, but content is SSE format
|
|
592
|
+
res.write('data: {"type":"start"}\n\n');
|
|
593
|
+
res.write('data: {"type":"text-delta","delta":"Auto"}\n\n');
|
|
594
|
+
res.write('data: {"type":"text-delta","delta":" detected"}\n\n');
|
|
595
|
+
res.end();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
await request(app)
|
|
599
|
+
.get('/api/auto-detect')
|
|
600
|
+
.expect(200);
|
|
601
|
+
|
|
602
|
+
const entries = readLogEntries();
|
|
603
|
+
expect(entries).toHaveLength(1);
|
|
604
|
+
expect(entries[0]).toMatchObject({
|
|
605
|
+
type: 'http',
|
|
606
|
+
path: '/api/auto-detect',
|
|
607
|
+
response: {
|
|
608
|
+
streaming: true,
|
|
609
|
+
text: 'Auto detected',
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('should handle writeHead with statusMessage parameter', async () => {
|
|
615
|
+
app.get('/api/writehead-msg', (req, res) => {
|
|
616
|
+
// writeHead(statusCode, statusMessage, headers)
|
|
617
|
+
res.writeHead(200, 'OK', {
|
|
618
|
+
'Content-Type': 'text/event-stream',
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
res.write('data: {"type":"text-delta","delta":"Test"}\n\n');
|
|
622
|
+
res.end();
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
await request(app)
|
|
626
|
+
.get('/api/writehead-msg')
|
|
627
|
+
.expect(200);
|
|
628
|
+
|
|
629
|
+
const entries = readLogEntries();
|
|
630
|
+
expect(entries).toHaveLength(1);
|
|
631
|
+
expect(entries[0].response.streaming).toBe(true);
|
|
632
|
+
expect(entries[0].response.text).toBe('Test');
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
describe('SSE Text Delta Parsing', () => {
|
|
637
|
+
it('should aggregate text-delta chunks into text field', async () => {
|
|
638
|
+
app.get('/api/stream', (req, res) => {
|
|
639
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
640
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
641
|
+
res.setHeader('Connection', 'keep-alive');
|
|
642
|
+
|
|
643
|
+
res.write('data: {"type":"start"}\n\n');
|
|
644
|
+
res.write('data: {"type":"text-delta","id":"0","delta":"Hello"}\n\n');
|
|
645
|
+
res.write('data: {"type":"text-delta","id":"0","delta":" world"}\n\n');
|
|
646
|
+
res.write('data: {"type":"text-delta","id":"0","delta":"!"}\n\n');
|
|
647
|
+
res.write('data: {"type":"finish","finishReason":"stop"}\n\n');
|
|
648
|
+
res.write('data: [DONE]\n\n');
|
|
649
|
+
res.end();
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
await request(app)
|
|
653
|
+
.get('/api/stream')
|
|
654
|
+
.set('Accept', 'text/event-stream')
|
|
655
|
+
.expect(200);
|
|
656
|
+
|
|
657
|
+
const entries = readLogEntries();
|
|
658
|
+
expect(entries).toHaveLength(1);
|
|
659
|
+
expect(entries[0]).toMatchObject({
|
|
660
|
+
timestamp: '2024-01-15T10:30:00.000Z',
|
|
661
|
+
type: 'http',
|
|
662
|
+
method: 'GET',
|
|
663
|
+
path: '/api/stream',
|
|
664
|
+
statusCode: 200,
|
|
665
|
+
response: {
|
|
666
|
+
streaming: true,
|
|
667
|
+
text: 'Hello world!',
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
expect(entries[0].response.chunks).toContainEqual({ type: 'start' });
|
|
671
|
+
expect(entries[0].response.chunks).toContainEqual({ type: 'text-delta', id: '0', delta: 'Hello' });
|
|
672
|
+
expect(entries[0].response.chunks).toContainEqual({ type: 'done' });
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
|
|
471
676
|
describe('File Size Management', () => {
|
|
472
677
|
it('should truncate log file when exceeding max size', async () => {
|
|
473
678
|
vi.useRealTimers(); // Use real timers for this test
|