mohen 1.0.0 → 1.1.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;
@@ -112,6 +137,11 @@ class UnifiedLogger {
112
137
  // ===========================================================================
113
138
  expressMiddleware() {
114
139
  return (req, res, next) => {
140
+ const requestPath = req.originalUrl || req.url;
141
+ // Check if we should log this path
142
+ if (!this.shouldLog(requestPath)) {
143
+ return next();
144
+ }
115
145
  const start = Date.now();
116
146
  const requestId = this.generateRequestId();
117
147
  // Initialize metadata object on request
@@ -119,6 +149,7 @@ class UnifiedLogger {
119
149
  // Detect SSE - check both request Accept header and response Content-Type
120
150
  let isSSE = req.headers.accept === 'text/event-stream';
121
151
  const chunks = [];
152
+ const textDeltas = []; // Collect text-delta content
122
153
  // Intercept setHeader to detect SSE by Content-Type
123
154
  const originalSetHeader = res.setHeader.bind(res);
124
155
  res.setHeader = ((name, value) => {
@@ -148,7 +179,7 @@ class UnifiedLogger {
148
179
  // If write is called, treat as streaming
149
180
  if (chunk && isSSE) {
150
181
  const chunkStr = chunk.toString();
151
- const parsed = this.parseSSEChunk(chunkStr);
182
+ const parsed = this.parseSSEChunk(chunkStr, textDeltas);
152
183
  if (parsed) {
153
184
  chunks.push(parsed);
154
185
  }
@@ -163,7 +194,7 @@ class UnifiedLogger {
163
194
  // SSE streaming path
164
195
  if (chunk) {
165
196
  const chunkStr = chunk.toString();
166
- const parsed = this.parseSSEChunk(chunkStr);
197
+ const parsed = this.parseSSEChunk(chunkStr, textDeltas);
167
198
  if (parsed) {
168
199
  chunks.push(parsed);
169
200
  }
@@ -173,7 +204,7 @@ class UnifiedLogger {
173
204
  requestId,
174
205
  type: 'http',
175
206
  method: req.method,
176
- path: req.originalUrl || req.url,
207
+ path: requestPath,
177
208
  statusCode: res.statusCode,
178
209
  duration: Date.now() - start,
179
210
  request: requestInfo,
@@ -182,6 +213,10 @@ class UnifiedLogger {
182
213
  chunks,
183
214
  },
184
215
  };
216
+ // Add aggregated text if we collected text-deltas
217
+ if (textDeltas.length > 0) {
218
+ entry.response.text = textDeltas.join('');
219
+ }
185
220
  if (req.logMetadata && Object.keys(req.logMetadata).length > 0) {
186
221
  entry.metadata = req.logMetadata;
187
222
  }
@@ -194,7 +229,7 @@ class UnifiedLogger {
194
229
  requestId,
195
230
  type: 'http',
196
231
  method: req.method,
197
- path: req.originalUrl || req.url,
232
+ path: requestPath,
198
233
  statusCode: res.statusCode,
199
234
  duration: Date.now() - start,
200
235
  request: requestInfo,
@@ -219,7 +254,7 @@ class UnifiedLogger {
219
254
  requestId,
220
255
  type: 'http',
221
256
  method: req.method,
222
- path: req.originalUrl || req.url,
257
+ path: requestPath,
223
258
  statusCode: res.statusCode,
224
259
  duration: Date.now() - start,
225
260
  request: requestInfo,
@@ -253,22 +288,35 @@ class UnifiedLogger {
253
288
  next();
254
289
  };
255
290
  }
256
- parseSSEChunk(raw) {
291
+ parseSSEChunk(raw, textDeltas) {
257
292
  const lines = raw.split('\n');
293
+ const results = [];
258
294
  for (const line of lines) {
259
295
  if (line.startsWith('data: ')) {
260
296
  const data = line.slice(6).trim();
261
- if (data === '[DONE]')
262
- return { done: true };
297
+ if (data === '[DONE]') {
298
+ results.push({ type: 'done' });
299
+ continue;
300
+ }
263
301
  try {
264
- return JSON.parse(data);
302
+ const parsed = JSON.parse(data);
303
+ // Handle text-delta type - collect the delta text
304
+ if (parsed.type === 'text-delta' && typeof parsed.delta === 'string') {
305
+ textDeltas.push(parsed.delta);
306
+ }
307
+ results.push(parsed);
265
308
  }
266
309
  catch {
267
- return { raw: data };
310
+ results.push({ raw: data });
268
311
  }
269
312
  }
270
313
  }
271
- return null;
314
+ // Return single result or array
315
+ if (results.length === 0)
316
+ return null;
317
+ if (results.length === 1)
318
+ return results[0];
319
+ return results;
272
320
  }
273
321
  // ===========================================================================
274
322
  // tRPC Middleware
@@ -276,6 +324,10 @@ class UnifiedLogger {
276
324
  trpcMiddleware() {
277
325
  const logger = this;
278
326
  return async function loggerMiddleware(opts) {
327
+ // Check if we should log this path
328
+ if (!logger.shouldLog(opts.path)) {
329
+ return opts.next();
330
+ }
279
331
  const start = Date.now();
280
332
  const requestId = logger.generateRequestId();
281
333
  // Initialize metadata on context if not present
@@ -386,4 +438,4 @@ function attachTrpcMetadata(ctx, metadata) {
386
438
  }
387
439
  // Default export for simpler imports
388
440
  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
441
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXNkQSxvQ0FzQkM7QUFTRCx3Q0FLQztBQUtELGdEQVFDO0FBdmdCRCx1Q0FBeUI7QUFDekIsMkNBQTZCO0FBa0Q3QiwrRUFBK0U7QUFDL0Usb0JBQW9CO0FBQ3BCLCtFQUErRTtBQUUvRSxNQUFNLGFBQWE7SUFRakIsWUFBWSxRQUFnQixFQUFFLFVBQXlCLEVBQUU7UUFDdkQsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxZQUFZLEdBQUcsT0FBTyxDQUFDLFlBQVksSUFBSSxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxDQUFDLGVBQWU7UUFDN0UsSUFBSSxDQUFDLGNBQWMsR0FBRyxPQUFPLENBQUMsY0FBYyxJQUFJLEtBQUssQ0FBQztRQUN0RCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxNQUFNLElBQUksQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQ2hHLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsSUFBSSxFQUFFLENBQUM7UUFDN0MsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLENBQUMsWUFBWSxJQUFJLEVBQUUsQ0FBQztRQUUvQywwQkFBMEI7UUFDMUIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDeEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN4QixFQUFFLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxFQUFFLFNBQVMs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LENBQUM7UUFDckIsR0FBRyxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUNELE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQixrQkFBa0IsQ0FDaEMsR0FBYSxFQUNiLFFBQWlDO0lBRWpDLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDckIsR0FBRyxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUNELE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBRUQscUNBQXFDO0FBQ3JDLGtCQUFlLFlBQVksQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBOZXh0RnVuY3Rpb24gfSBmcm9tICdleHByZXNzJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gVHlwZXNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuaW50ZXJmYWNlIExvZ0VudHJ5IHtcbiAgdGltZXN0YW1wOiBzdHJpbmc7XG4gIHJlcXVlc3RJZDogc3RyaW5nO1xuICB0eXBlOiAnaHR0cCcgfCAndHJwYyc7XG4gIG1ldGhvZDogc3RyaW5nO1xuICBwYXRoOiBzdHJpbmc7XG4gIHN0YXR1c0NvZGU/OiBudW1iZXI7XG4gIGR1cmF0aW9uOiBudW1iZXI7XG4gIHJlcXVlc3Q/OiB7XG4gICAgYm9keT86IHVua25vd247XG4gICAgcXVlcnk/OiB1bmtub3duO1xuICAgIGhlYWRlcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICB9O1xuICByZXNwb25zZT86IHtcbiAgICBib2R5PzogdW5rbm93bjtcbiAgICBzdHJlYW1pbmc/OiBib29sZWFuO1xuICAgIGNodW5rcz86IHVua25vd25bXTtcbiAgICB0ZXh0Pzogc3RyaW5nOyAvLyBBZ2dyZWdhdGVkIHRleHQgZnJvbSB0ZXh0LWRlbHRhIGNodW5rc1xuICB9O1xuICBlcnJvcj86IHtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgc3RhY2s/OiBzdHJpbmc7XG4gIH07XG4gIG1ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbmludGVyZmFjZSBMb2dnZXJPcHRpb25zIHtcbiAgbWF4U2l6ZUJ5dGVzPzogbnVtYmVyOyAgICAgIC8vIERlZmF1bHQ6IDEwTUJcbiAgaW5jbHVkZUhlYWRlcnM/OiBib29sZWFuOyAgIC8vIERlZmF1bHQ6IGZhbHNlXG4gIHJlZGFjdD86IHN0cmluZ1tdOyAgICAgICAgICAvLyBGaWVsZHMgdG8gcmVkYWN0IGZyb20gbG9nc1xuICBpZ25vcmVQYXRocz86IHN0cmluZ1tdOyAgICAgLy8gUGF0aHMgdG8gaWdub3JlIChzdXBwb3J0cyB3aWxkY2FyZHMgbGlrZSAvaGVhbHRoLyopXG4gIGluY2x1ZGVQYXRocz86IHN0cmluZ1tdOyAgICAvLyBPbmx5IGxvZyB0aGVzZSBwYXRocyAoc3VwcG9ydHMgd2lsZGNhcmRzKVxufVxuXG4vLyBFeHRlbmQgRXhwcmVzcyBSZXF1ZXN0IHRvIGluY2x1ZGUgbWV0YWRhdGFcbmRlY2xhcmUgZ2xvYmFsIHtcbiAgbmFtZXNwYWNlIEV4cHJlc3Mge1xuICAgIGludGVyZmFjZSBSZXF1ZXN0IHtcbiAgICAgIGxvZ01ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgfVxuICB9XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENvcmUgTG9nZ2VyIENsYXNzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmNsYXNzIFVuaWZpZWRMb2dnZXIge1xuICBwcml2YXRlIGZpbGVQYXRoOiBzdHJpbmc7XG4gIHByaXZhdGUgbWF4U2l6ZUJ5dGVzOiBudW1iZXI7XG4gIHByaXZhdGUgaW5jbHVkZUhlYWRlcnM6IGJvb2xlYW47XG4gIHByaXZhdGUgcmVkYWN0RmllbGRzOiBTZXQ8c3RyaW5nPjtcbiAgcHJpdmF0ZSBpZ25vcmVQYXRoczogc3RyaW5nW107XG4gIHByaXZhdGUgaW5jbHVkZVBhdGhzOiBzdHJpbmdbXTtcblxuICBjb25zdHJ1Y3RvcihmaWxlUGF0aDogc3RyaW5nLCBvcHRpb25zOiBMb2dnZXJPcHRpb25zID0ge30pIHtcbiAgICB0aGlzLmZpbGVQYXRoID0gcGF0aC5yZXNvbHZlKGZpbGVQYXRoKTtcbiAgICB0aGlzLm1heFNpemVCeXRlcyA9IG9wdGlvbnMubWF4U2l6ZUJ5dGVzID8/IDEwICogMTAyNCAqIDEwMjQ7IC8vIDEwTUIgZGVmYXVsdFxuICAgIHRoaXMuaW5jbHVkZUhlYWRlcnMgPSBvcHRpb25zLmluY2x1ZGVIZWFkZXJzID8/IGZhbHNlO1xuICAgIHRoaXMucmVkYWN0RmllbGRzID0gbmV3IFNldChvcHRpb25zLnJlZGFjdCA/PyBbJ3Bhc3N3b3JkJywgJ3Rva2VuJywgJ2F1dGhvcml6YXRpb24nLCAnY29va2llJ10pO1xuICAgIHRoaXMuaWdub3JlUGF0aHMgPSBvcHRpb25zLmlnbm9yZVBhdGhzID8/IFtdO1xuICAgIHRoaXMuaW5jbHVkZVBhdGhzID0gb3B0aW9ucy5pbmNsdWRlUGF0aHMgPz8gW107XG5cbiAgICAvLyBFbnN1cmUgZGlyZWN0b3J5IGV4aXN0c1xuICAgIGNvbnN0IGRpciA9IHBhdGguZGlybmFtZSh0aGlzLmZpbGVQYXRoKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoZGlyKSkge1xuICAgICAgZnMubWtkaXJTeW5jKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZW5lcmF0ZVJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBgJHtEYXRlLm5vdygpLnRvU3RyaW5nKDM2KX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA5KX1gO1xuICB9XG5cbiAgcHJpdmF0ZSBtYXRjaFBhdGgocGF0dGVybjogc3RyaW5nLCByZXF1ZXN0UGF0aDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgLy8gQ29udmVydCB3aWxkY2FyZCBwYXR0ZXJuIHRvIHJlZ2V4XG4gICAgLy8gL2FwaS8qIG1hdGNoZXMgL2FwaS9hbnl0aGluZ1xuICAgIC8vIC9oZWFsdGggbWF0Y2hlcyBleGFjdGx5IC9oZWFsdGhcbiAgICBjb25zdCByZWdleFBhdHRlcm4gPSBwYXR0ZXJuXG4gICAgICAucmVwbGFjZSgvWy4rP14ke30oKXxbXFxdXFxcXF0vZywgJ1xcXFwkJicpIC8vIEVzY2FwZSBzcGVjaWFsIHJlZ2V4IGNoYXJzIGV4Y2VwdCAqXG4gICAgICAucmVwbGFjZSgvXFwqL2csICcuKicpOyAvLyBDb252ZXJ0ICogdG8gLipcbiAgICBjb25zdCByZWdleCA9IG5ldyBSZWdFeHAoYF4ke3JlZ2V4UGF0dGVybn0kYCk7XG4gICAgcmV0dXJuIHJlZ2V4LnRlc3QocmVxdWVzdFBhdGgpO1xuICB9XG5cbiAgcHJpdmF0ZSBzaG91bGRMb2cocmVxdWVzdFBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIC8vIEV4dHJhY3QgcGF0aCB3aXRob3V0IHF1ZXJ5IHN0cmluZ1xuICAgIGNvbnN0IHBhdGhPbmx5ID0gcmVxdWVzdFBhdGguc3BsaXQoJz8nKVswXTtcblxuICAgIC8vIElmIGluY2x1ZGVQYXRocyBpcyBzZXQsIG9ubHkgbG9nIG1hdGNoaW5nIHBhdGhzXG4gICAgaWYgKHRoaXMuaW5jbHVkZVBhdGhzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiB0aGlzLmluY2x1ZGVQYXRocy5zb21lKChwYXR0ZXJuKSA9PiB0aGlzLm1hdGNoUGF0aChwYXR0ZXJuLCBwYXRoT25seSkpO1xuICAgIH1cblxuICAgIC8vIElmIGlnbm9yZVBhdGhzIGlzIHNldCwgc2tpcCBtYXRjaGluZyBwYXRoc1xuICAgIGlmICh0aGlzLmlnbm9yZVBhdGhzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiAhdGhpcy5pZ25vcmVQYXRocy5zb21lKChwYXR0ZXJuKSA9PiB0aGlzLm1hdGNoUGF0aChwYXR0ZXJuLCBwYXRoT25seSkpO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSByZWRhY3Qob2JqOiB1bmtub3duKTogdW5rbm93biB7XG4gICAgaWYgKG9iaiA9PT0gbnVsbCB8fCBvYmogPT09IHVuZGVmaW5lZCkgcmV0dXJuIG9iajtcbiAgICBpZiAodHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHJldHVybiBvYmo7XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgICByZXR1cm4gb2JqLm1hcCgoaXRlbSkgPT4gdGhpcy5yZWRhY3QoaXRlbSkpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvYmogYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pKSB7XG4gICAgICBpZiAodGhpcy5yZWRhY3RGaWVsZHMuaGFzKGtleS50b0xvd2VyQ2FzZSgpKSkge1xuICAgICAgICByZXN1bHRba2V5XSA9ICdbUkVEQUNURURdJztcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICByZXN1bHRba2V5XSA9IHRoaXMucmVkYWN0KHZhbHVlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdFtrZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrQW5kUm90YXRlKCk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWZzLmV4aXN0c1N5bmModGhpcy5maWxlUGF0aCkpIHJldHVybjtcblxuICAgICAgY29uc3Qgc3RhdHMgPSBmcy5zdGF0U3luYyh0aGlzLmZpbGVQYXRoKTtcbiAgICAgIGlmIChzdGF0cy5zaXplID4gdGhpcy5tYXhTaXplQnl0ZXMpIHtcbiAgICAgICAgLy8gUmVhZCBmaWxlLCBrZWVwIGxhc3QgMjUlIG9mIGxpbmVzXG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmModGhpcy5maWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgIGNvbnN0IGxpbmVzID0gY29udGVudC50cmltKCkuc3BsaXQoJ1xcbicpO1xuICAgICAgICBjb25zdCBrZWVwQ291bnQgPSBNYXRoLmZsb29yKGxpbmVzLmxlbmd0aCAqIDAuMjUpO1xuICAgICAgICBjb25zdCBuZXdDb250ZW50ID0gbGluZXMuc2xpY2UoLWtlZXBDb3VudCkuam9pbignXFxuJykgKyAnXFxuJztcbiAgICAgICAgZnMud3JpdGVGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBuZXdDb250ZW50KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciByb3RhdGlvbiBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIHdyaXRlKGVudHJ5OiBMb2dFbnRyeSk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmNoZWNrQW5kUm90YXRlKCk7XG4gICAgICBjb25zdCByZWRhY3RlZEVudHJ5ID0gdGhpcy5yZWRhY3QoZW50cnkpIGFzIExvZ0VudHJ5O1xuICAgICAgY29uc3QgbGluZSA9IEpTT04uc3RyaW5naWZ5KHJlZGFjdGVkRW50cnkpICsgJ1xcbic7XG4gICAgICBmcy5hcHBlbmRGaWxlU3luYyh0aGlzLmZpbGVQYXRoLCBsaW5lKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0xvZ2dlciB3cml0ZSBlcnJvcjonLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBFeHByZXNzIE1pZGRsZXdhcmVcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgZXhwcmVzc01pZGRsZXdhcmUoKSB7XG4gICAgcmV0dXJuIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgICAgY29uc3QgcmVxdWVzdFBhdGggPSByZXEub3JpZ2luYWxVcmwgfHwgcmVxLnVybDtcblxuICAgICAgLy8gQ2hlY2sgaWYgd2Ugc2hvdWxkIGxvZyB0aGlzIHBhdGhcbiAgICAgIGlmICghdGhpcy5zaG91bGRMb2cocmVxdWVzdFBhdGgpKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgICAgIGNvbnN0IHJlcXVlc3RJZCA9IHRoaXMuZ2VuZXJhdGVSZXF1ZXN0SWQoKTtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSBtZXRhZGF0YSBvYmplY3Qgb24gcmVxdWVzdFxuICAgICAgcmVxLmxvZ01ldGFkYXRhID0ge307XG5cbiAgICAgIC8vIERldGVjdCBTU0UgLSBjaGVjayBib3RoIHJlcXVlc3QgQWNjZXB0IGhlYWRlciBhbmQgcmVzcG9uc2UgQ29udGVudC1UeXBlXG4gICAgICBsZXQgaXNTU0UgPSByZXEuaGVhZGVycy5hY2NlcHQgPT09ICd0ZXh0L2V2ZW50LXN0cmVhbSc7XG4gICAgICBjb25zdCBjaHVua3M6IHVua25vd25bXSA9IFtdO1xuICAgICAgY29uc3QgdGV4dERlbHRhczogc3RyaW5nW10gPSBbXTsgLy8gQ29sbGVjdCB0ZXh0LWRlbHRhIGNvbnRlbnRcbiAgICAgIFxuICAgICAgLy8gSW50ZXJjZXB0IHNldEhlYWRlciB0byBkZXRlY3QgU1NFIGJ5IENvbnRlbnQtVHlwZVxuICAgICAgY29uc3Qgb3JpZ2luYWxTZXRIZWFkZXIgPSByZXMuc2V0SGVhZGVyLmJpbmQocmVzKTtcbiAgICAgIHJlcy5zZXRIZWFkZXIgPSAoKG5hbWU6IHN0cmluZywgdmFsdWU6IHN0cmluZyB8IG51bWJlciB8IHJlYWRvbmx5IHN0cmluZ1tdKTogUmVzcG9uc2UgPT4ge1xuICAgICAgICBpZiAobmFtZS50b0xvd2VyQ2FzZSgpID09PSAnY29udGVudC10eXBlJyAmJiBcbiAgICAgICAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiYgXG4gICAgICAgICAgICB2YWx1ZS5pbmNsdWRlcygndGV4dC9ldmVudC1zdHJlYW0nKSkge1xuICAgICAgICAgIGlzU1NFID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb3JpZ2luYWxTZXRIZWFkZXIobmFtZSwgdmFsdWUpO1xuICAgICAgfSkgYXMgdHlwZW9mIHJlcy5zZXRIZWFkZXI7XG5cbiAgICAgIC8vIENhcHR1cmUgcmVxdWVzdCBpbmZvXG4gICAgICBjb25zdCByZXF1ZXN0SW5mbzogTG9nRW50cnlbJ3JlcXVlc3QnXSA9IHtcbiAgICAgICAgYm9keTogcmVxLmJvZHksXG4gICAgICAgIHF1ZXJ5OiByZXEucXVlcnksXG4gICAgICB9O1xuXG4gICAgICBpZiAodGhpcy5pbmNsdWRlSGVhZGVycykge1xuICAgICAgICByZXF1ZXN0SW5mby5oZWFkZXJzID0gcmVxLmhlYWRlcnMgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgICAgIH1cblxuICAgICAgLy8gSW50ZXJjZXB0IHdyaXRlL2VuZCBmb3Igc3RyZWFtaW5nIGRldGVjdGlvblxuICAgICAgY29uc3Qgb3JpZ2luYWxXcml0ZSA9IHJlcy53cml0ZS5iaW5kKHJlcyk7XG4gICAgICBjb25zdCBvcmlnaW5hbEVuZCA9IHJlcy5lbmQuYmluZChyZXMpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxKc29uID0gcmVzLmpzb24uYmluZChyZXMpO1xuICAgICAgY29uc3Qgb3JpZ2luYWxTZW5kID0gcmVzLnNlbmQuYmluZChyZXMpO1xuICAgICAgbGV0IHJlc3BvbnNlQm9keTogdW5rbm93bjtcbiAgICAgIGxldCBsb2dnZWQgPSBmYWxzZTtcblxuICAgICAgcmVzLndyaXRlID0gKChjaHVuazogYW55LCBlbmNvZGluZ09yQ2FsbGJhY2s/OiBhbnksIGNhbGxiYWNrPzogYW55KTogYm9vbGVhbiA9PiB7XG4gICAgICAgIC8vIElmIHdyaXRlIGlzIGNhbGxlZCwgdHJlYXQgYXMgc3RyZWFtaW5nXG4gICAgICAgIGlmIChjaHVuayAmJiBpc1NTRSkge1xuICAgICAgICAgIGNvbnN0IGNodW5rU3RyID0gY2h1bmsudG9TdHJpbmcoKTtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSB0aGlzLnBhcnNlU1NFQ2h1bmsoY2h1bmtTdHIsIHRleHREZWx0YXMpO1xuICAgICAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgICAgIGNodW5rcy5wdXNoKHBhcnNlZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvcmlnaW5hbFdyaXRlKGNodW5rLCBlbmNvZGluZ09yQ2FsbGJhY2ssIGNhbGxiYWNrKTtcbiAgICAgIH0pIGFzIHR5cGVvZiByZXMud3JpdGU7XG5cbiAgICAgIHJlcy5lbmQgPSAoKGNodW5rPzogYW55LCBlbmNvZGluZ09yQ2FsbGJhY2s/OiBhbnksIGNhbGxiYWNrPzogYW55KTogUmVzcG9uc2UgPT4ge1xuICAgICAgICBpZiAobG9nZ2VkKSByZXR1cm4gb3JpZ2luYWxFbmQoY2h1bmssIGVuY29kaW5nT3JDYWxsYmFjaywgY2FsbGJhY2spO1xuICAgICAgICBsb2dnZWQgPSB0cnVlO1xuXG4gICAgICAgIGlmIChpc1NTRSkge1xuICAgICAgICAgIC8vIFNTRSBzdHJlYW1pbmcgcGF0aFxuICAgICAgICAgIGlmIChjaHVuaykge1xuICAgICAgICAgICAgY29uc3QgY2h1bmtTdHIgPSBjaHVuay50b1N0cmluZygpO1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gdGhpcy5wYXJzZVNTRUNodW5rKGNodW5rU3RyLCB0ZXh0RGVsdGFzKTtcbiAgICAgICAgICAgIGlmIChwYXJzZWQpIHtcbiAgICAgICAgICAgICAgY2h1bmtzLnB1c2gocGFyc2VkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICAgIHR5cGU6ICdodHRwJyxcbiAgICAgICAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCxcbiAgICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgICAgc3RhdHVzQ29kZTogcmVzLnN0YXR1c0NvZGUsXG4gICAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgICAgICBzdHJlYW1pbmc6IHRydWUsXG4gICAgICAgICAgICAgIGNodW5rcyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfTtcblxuICAgICAgICAgIC8vIEFkZCBhZ2dyZWdhdGVkIHRleHQgaWYgd2UgY29sbGVjdGVkIHRleHQtZGVsdGFzXG4gICAgICAgICAgaWYgKHRleHREZWx0YXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZW50cnkucmVzcG9uc2UhLnRleHQgPSB0ZXh0RGVsdGFzLmpvaW4oJycpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChyZXEubG9nTWV0YWRhdGEgJiYgT2JqZWN0LmtleXMocmVxLmxvZ01ldGFkYXRhKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBlbnRyeS5tZXRhZGF0YSA9IHJlcS5sb2dNZXRhZGF0YTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLndyaXRlKGVudHJ5KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZWd1bGFyIHJlc3BvbnNlIHBhdGhcbiAgICAgICAgICBjb25zdCBlbnRyeTogTG9nRW50cnkgPSB7XG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICAgIHR5cGU6ICdodHRwJyxcbiAgICAgICAgICAgIG1ldGhvZDogcmVxLm1ldGhvZCxcbiAgICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgICAgc3RhdHVzQ29kZTogcmVzLnN0YXR1c0NvZGUsXG4gICAgICAgICAgICBkdXJhdGlvbjogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgICAgICBib2R5OiByZXNwb25zZUJvZHksXG4gICAgICAgICAgICAgIHN0cmVhbWluZzogZmFsc2UsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBpZiAocmVxLmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKHJlcS5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgZW50cnkubWV0YWRhdGEgPSByZXEubG9nTWV0YWRhdGE7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy53cml0ZShlbnRyeSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gb3JpZ2luYWxFbmQoY2h1bmssIGVuY29kaW5nT3JDYWxsYmFjaywgY2FsbGJhY2spO1xuICAgICAgfSkgYXMgdHlwZW9mIHJlcy5lbmQ7XG5cbiAgICAgIGNvbnN0IGxvZ1Jlc3BvbnNlID0gKCkgPT4ge1xuICAgICAgICBpZiAobG9nZ2VkKSByZXR1cm47XG4gICAgICAgIGxvZ2dlZCA9IHRydWU7XG5cbiAgICAgICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICB0eXBlOiAnaHR0cCcsXG4gICAgICAgICAgbWV0aG9kOiByZXEubWV0aG9kLFxuICAgICAgICAgIHBhdGg6IHJlcXVlc3RQYXRoLFxuICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLFxuICAgICAgICAgIGR1cmF0aW9uOiBEYXRlLm5vdygpIC0gc3RhcnQsXG4gICAgICAgICAgcmVxdWVzdDogcmVxdWVzdEluZm8sXG4gICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgIGJvZHk6IHJlc3BvbnNlQm9keSxcbiAgICAgICAgICAgIHN0cmVhbWluZzogZmFsc2UsXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcblxuICAgICAgICBpZiAocmVxLmxvZ01ldGFkYXRhICYmIE9iamVjdC5rZXlzKHJlcS5sb2dNZXRhZGF0YSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGVudHJ5Lm1ldGFkYXRhID0gcmVxLmxvZ01ldGFkYXRhO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy53cml0ZShlbnRyeSk7XG4gICAgICB9O1xuXG4gICAgICByZXMuanNvbiA9IChib2R5OiBhbnkpID0+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.1.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;
@@ -129,6 +164,13 @@ class UnifiedLogger {
129
164
 
130
165
  expressMiddleware() {
131
166
  return (req: Request, res: Response, next: NextFunction) => {
167
+ const requestPath = req.originalUrl || req.url;
168
+
169
+ // Check if we should log this path
170
+ if (!this.shouldLog(requestPath)) {
171
+ return next();
172
+ }
173
+
132
174
  const start = Date.now();
133
175
  const requestId = this.generateRequestId();
134
176
 
@@ -138,6 +180,7 @@ class UnifiedLogger {
138
180
  // Detect SSE - check both request Accept header and response Content-Type
139
181
  let isSSE = req.headers.accept === 'text/event-stream';
140
182
  const chunks: unknown[] = [];
183
+ const textDeltas: string[] = []; // Collect text-delta content
141
184
 
142
185
  // Intercept setHeader to detect SSE by Content-Type
143
186
  const originalSetHeader = res.setHeader.bind(res);
@@ -172,7 +215,7 @@ class UnifiedLogger {
172
215
  // If write is called, treat as streaming
173
216
  if (chunk && isSSE) {
174
217
  const chunkStr = chunk.toString();
175
- const parsed = this.parseSSEChunk(chunkStr);
218
+ const parsed = this.parseSSEChunk(chunkStr, textDeltas);
176
219
  if (parsed) {
177
220
  chunks.push(parsed);
178
221
  }
@@ -188,7 +231,7 @@ class UnifiedLogger {
188
231
  // SSE streaming path
189
232
  if (chunk) {
190
233
  const chunkStr = chunk.toString();
191
- const parsed = this.parseSSEChunk(chunkStr);
234
+ const parsed = this.parseSSEChunk(chunkStr, textDeltas);
192
235
  if (parsed) {
193
236
  chunks.push(parsed);
194
237
  }
@@ -199,7 +242,7 @@ class UnifiedLogger {
199
242
  requestId,
200
243
  type: 'http',
201
244
  method: req.method,
202
- path: req.originalUrl || req.url,
245
+ path: requestPath,
203
246
  statusCode: res.statusCode,
204
247
  duration: Date.now() - start,
205
248
  request: requestInfo,
@@ -209,6 +252,11 @@ class UnifiedLogger {
209
252
  },
210
253
  };
211
254
 
255
+ // Add aggregated text if we collected text-deltas
256
+ if (textDeltas.length > 0) {
257
+ entry.response!.text = textDeltas.join('');
258
+ }
259
+
212
260
  if (req.logMetadata && Object.keys(req.logMetadata).length > 0) {
213
261
  entry.metadata = req.logMetadata;
214
262
  }
@@ -221,7 +269,7 @@ class UnifiedLogger {
221
269
  requestId,
222
270
  type: 'http',
223
271
  method: req.method,
224
- path: req.originalUrl || req.url,
272
+ path: requestPath,
225
273
  statusCode: res.statusCode,
226
274
  duration: Date.now() - start,
227
275
  request: requestInfo,
@@ -250,7 +298,7 @@ class UnifiedLogger {
250
298
  requestId,
251
299
  type: 'http',
252
300
  method: req.method,
253
- path: req.originalUrl || req.url,
301
+ path: requestPath,
254
302
  statusCode: res.statusCode,
255
303
  duration: Date.now() - start,
256
304
  request: requestInfo,
@@ -289,20 +337,36 @@ class UnifiedLogger {
289
337
  };
290
338
  }
291
339
 
292
- private parseSSEChunk(raw: string): unknown {
340
+ private parseSSEChunk(raw: string, textDeltas: string[]): unknown {
293
341
  const lines = raw.split('\n');
342
+ const results: unknown[] = [];
343
+
294
344
  for (const line of lines) {
295
345
  if (line.startsWith('data: ')) {
296
346
  const data = line.slice(6).trim();
297
- if (data === '[DONE]') return { done: true };
347
+ if (data === '[DONE]') {
348
+ results.push({ type: 'done' });
349
+ continue;
350
+ }
298
351
  try {
299
- return JSON.parse(data);
352
+ const parsed = JSON.parse(data);
353
+
354
+ // Handle text-delta type - collect the delta text
355
+ if (parsed.type === 'text-delta' && typeof parsed.delta === 'string') {
356
+ textDeltas.push(parsed.delta);
357
+ }
358
+
359
+ results.push(parsed);
300
360
  } catch {
301
- return { raw: data };
361
+ results.push({ raw: data });
302
362
  }
303
363
  }
304
364
  }
305
- return null;
365
+
366
+ // Return single result or array
367
+ if (results.length === 0) return null;
368
+ if (results.length === 1) return results[0];
369
+ return results;
306
370
  }
307
371
 
308
372
  // ===========================================================================
@@ -319,6 +383,11 @@ class UnifiedLogger {
319
383
  ctx: TContext & { logMetadata?: Record<string, unknown> };
320
384
  next: () => Promise<{ ok: boolean; data?: unknown; error?: Error }>;
321
385
  }) {
386
+ // Check if we should log this path
387
+ if (!logger.shouldLog(opts.path)) {
388
+ return opts.next();
389
+ }
390
+
322
391
  const start = Date.now();
323
392
  const requestId = logger.generateRequestId();
324
393
 
@@ -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,99 @@ 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('SSE Text Delta Parsing', () => {
525
+ it('should aggregate text-delta chunks into text field', async () => {
526
+ app.get('/api/stream', (req, res) => {
527
+ res.setHeader('Content-Type', 'text/event-stream');
528
+ res.setHeader('Cache-Control', 'no-cache');
529
+ res.setHeader('Connection', 'keep-alive');
530
+
531
+ res.write('data: {"type":"start"}\n\n');
532
+ res.write('data: {"type":"text-delta","id":"0","delta":"Hello"}\n\n');
533
+ res.write('data: {"type":"text-delta","id":"0","delta":" world"}\n\n');
534
+ res.write('data: {"type":"text-delta","id":"0","delta":"!"}\n\n');
535
+ res.write('data: {"type":"finish","finishReason":"stop"}\n\n');
536
+ res.write('data: [DONE]\n\n');
537
+ res.end();
538
+ });
539
+
540
+ await request(app)
541
+ .get('/api/stream')
542
+ .set('Accept', 'text/event-stream')
543
+ .expect(200);
544
+
545
+ const entries = readLogEntries();
546
+ expect(entries).toHaveLength(1);
547
+ expect(entries[0]).toMatchObject({
548
+ timestamp: '2024-01-15T10:30:00.000Z',
549
+ type: 'http',
550
+ method: 'GET',
551
+ path: '/api/stream',
552
+ statusCode: 200,
553
+ response: {
554
+ streaming: true,
555
+ text: 'Hello world!',
556
+ },
557
+ });
558
+ expect(entries[0].response.chunks).toContainEqual({ type: 'start' });
559
+ expect(entries[0].response.chunks).toContainEqual({ type: 'text-delta', id: '0', delta: 'Hello' });
560
+ expect(entries[0].response.chunks).toContainEqual({ type: 'done' });
561
+ });
562
+ });
563
+
471
564
  describe('File Size Management', () => {
472
565
  it('should truncate log file when exceeding max size', async () => {
473
566
  vi.useRealTimers(); // Use real timers for this test