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 CHANGED
@@ -1,6 +1,21 @@
1
- # mohen 墨痕
1
+ <p align="center">
2
+ <img src="logo.png" alt="mohen logo" width="200" />
3
+ </p>
2
4
 
3
- A simple, unified request/response logger for Express and tRPC that writes to a single file with JSON lines format.
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 both request Accept header and response Content-Type
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
- // If write is called, treat as streaming
149
- if (chunk && isSSE) {
150
- const chunkStr = chunk.toString();
151
- const parsed = this.parseSSEChunk(chunkStr);
152
- if (parsed) {
153
- chunks.push(parsed);
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 = chunk.toString();
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: req.originalUrl || req.url,
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: req.originalUrl || req.url,
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: req.originalUrl || req.url,
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
- return { done: true };
388
+ if (data === '[DONE]') {
389
+ results.push({ type: 'done' });
390
+ continue;
391
+ }
263
392
  try {
264
- return JSON.parse(data);
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
- return { raw: data };
401
+ results.push({ raw: data });
268
402
  }
269
403
  }
270
404
  }
271
- return null;
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlaQSxvQ0FzQkM7QUFTRCx3Q0FLQztBQUtELGdEQVFDO0FBbGNELHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUErQzdCLCtFQUErRTtBQUMvRSxvQkFBb0I7QUFDcEIsK0VBQStFO0FBRS9FLE1BQU0sYUFBYTtJQU1qQixZQUFZLFFBQWdCLEVBQUUsVUFBeUIsRUFBRTtRQUN2RCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLENBQUMsWUFBWSxJQUFJLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsZUFBZTtRQUM3RSxJQUFJLENBQUMsY0FBYyxHQUFHLE9BQU8sQ0FBQyxjQUFjLElBQUksS0FBSyxDQUFDO1FBQ3RELElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFFaEcsMEJBQTBCO1FBQzFCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEIsRUFBRSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVPLGlCQUFpQjtRQUN2QixPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFF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QUFDO0lBQ3ZCLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0Isa0JBQWtCLENBQ2hDLEdBQWEsRUFDYixRQUFpQztJQUVqQyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLEdBQUcsQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVELHFDQUFxQztBQUNyQyxrQkFBZSxZQUFZLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFR5cGVzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmludGVyZmFjZSBMb2dFbnRyeSB7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICByZXF1ZXN0SWQ6IHN0cmluZztcbiAgdHlwZTogJ2h0dHAnIHwgJ3RycGMnO1xuICBtZXRob2Q6IHN0cmluZztcbiAgcGF0aDogc3RyaW5nO1xuICBzdGF0dXNDb2RlPzogbnVtYmVyO1xuICBkdXJhdGlvbjogbnVtYmVyO1xuICByZXF1ZXN0Pzoge1xuICAgIGJvZHk/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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mohen",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Unified request/response logger for Express and tRPC with SSE support (墨痕 - ink trace)",
5
5
  "main": "dist/logger.js",
6
6
  "types": "dist/logger.d.ts",
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 both request Accept header and response Content-Type
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
- // If write is called, treat as streaming
173
- if (chunk && isSSE) {
174
- const chunkStr = chunk.toString();
175
- const parsed = this.parseSSEChunk(chunkStr);
176
- if (parsed) {
177
- chunks.push(parsed);
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 = chunk.toString();
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: req.originalUrl || req.url,
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: req.originalUrl || req.url,
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: req.originalUrl || req.url,
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]') return { done: true };
454
+ if (data === '[DONE]') {
455
+ results.push({ type: 'done' });
456
+ continue;
457
+ }
298
458
  try {
299
- return JSON.parse(data);
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
- return { raw: data };
468
+ results.push({ raw: data });
302
469
  }
303
470
  }
304
471
  }
305
- return null;
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
 
@@ -196,7 +196,7 @@ describe('mohen logger', () => {
196
196
  { count: 1 },
197
197
  { count: 2 },
198
198
  { count: 3 },
199
- { done: true },
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