@sochdb/sochdb 0.4.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/LICENSE +201 -0
- package/README.md +3349 -0
- package/_bin/aarch64-apple-darwin/libsochdb_storage.dylib +0 -0
- package/_bin/aarch64-apple-darwin/sochdb-bulk +0 -0
- package/_bin/aarch64-apple-darwin/sochdb-grpc-server +0 -0
- package/_bin/aarch64-apple-darwin/sochdb-server +0 -0
- package/_bin/x86_64-pc-windows-msvc/sochdb-bulk.exe +0 -0
- package/_bin/x86_64-pc-windows-msvc/sochdb-grpc-server.exe +0 -0
- package/_bin/x86_64-pc-windows-msvc/sochdb_storage.dll +0 -0
- package/_bin/x86_64-unknown-linux-gnu/libsochdb_storage.so +0 -0
- package/_bin/x86_64-unknown-linux-gnu/sochdb-bulk +0 -0
- package/_bin/x86_64-unknown-linux-gnu/sochdb-grpc-server +0 -0
- package/_bin/x86_64-unknown-linux-gnu/sochdb-server +0 -0
- package/bin/sochdb-bulk.js +80 -0
- package/bin/sochdb-grpc-server.js +80 -0
- package/bin/sochdb-server.js +84 -0
- package/dist/cjs/analytics.js +196 -0
- package/dist/cjs/database.js +929 -0
- package/dist/cjs/embedded/database.js +236 -0
- package/dist/cjs/embedded/ffi/bindings.js +113 -0
- package/dist/cjs/embedded/ffi/library-finder.js +135 -0
- package/dist/cjs/embedded/index.js +14 -0
- package/dist/cjs/embedded/transaction.js +172 -0
- package/dist/cjs/errors.js +71 -0
- package/dist/cjs/format.js +176 -0
- package/dist/cjs/grpc-client.js +328 -0
- package/dist/cjs/index.js +75 -0
- package/dist/cjs/ipc-client.js +504 -0
- package/dist/cjs/query.js +154 -0
- package/dist/cjs/server-manager.js +295 -0
- package/dist/cjs/sql-engine.js +874 -0
- package/dist/esm/analytics.js +196 -0
- package/dist/esm/database.js +931 -0
- package/dist/esm/embedded/database.js +239 -0
- package/dist/esm/embedded/ffi/bindings.js +142 -0
- package/dist/esm/embedded/ffi/library-finder.js +135 -0
- package/dist/esm/embedded/index.js +14 -0
- package/dist/esm/embedded/transaction.js +176 -0
- package/dist/esm/errors.js +71 -0
- package/dist/esm/format.js +179 -0
- package/dist/esm/grpc-client.js +333 -0
- package/dist/esm/index.js +75 -0
- package/dist/esm/ipc-client.js +505 -0
- package/dist/esm/query.js +159 -0
- package/dist/esm/server-manager.js +295 -0
- package/dist/esm/sql-engine.js +875 -0
- package/dist/types/analytics.d.ts +66 -0
- package/dist/types/analytics.d.ts.map +1 -0
- package/dist/types/database.d.ts +523 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/embedded/database.d.ts +105 -0
- package/dist/types/embedded/database.d.ts.map +1 -0
- package/dist/types/embedded/ffi/bindings.d.ts +24 -0
- package/dist/types/embedded/ffi/bindings.d.ts.map +1 -0
- package/dist/types/embedded/ffi/library-finder.d.ts +17 -0
- package/dist/types/embedded/ffi/library-finder.d.ts.map +1 -0
- package/dist/types/embedded/index.d.ts +9 -0
- package/dist/types/embedded/index.d.ts.map +1 -0
- package/dist/types/embedded/transaction.d.ts +21 -0
- package/dist/types/embedded/transaction.d.ts.map +1 -0
- package/dist/types/errors.d.ts +36 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/format.d.ts +117 -0
- package/dist/types/format.d.ts.map +1 -0
- package/dist/types/grpc-client.d.ts +120 -0
- package/dist/types/grpc-client.d.ts.map +1 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/ipc-client.d.ts +177 -0
- package/dist/types/ipc-client.d.ts.map +1 -0
- package/dist/types/query.d.ts +85 -0
- package/dist/types/query.d.ts.map +1 -0
- package/dist/types/server-manager.d.ts +29 -0
- package/dist/types/server-manager.d.ts.map +1 -0
- package/dist/types/sql-engine.d.ts +100 -0
- package/dist/types/sql-engine.d.ts.map +1 -0
- package/package.json +90 -0
- package/scripts/postinstall.js +50 -0
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SochDB Embedded Database
|
|
4
|
+
*
|
|
5
|
+
* Direct database access via IPC to the SochDB server.
|
|
6
|
+
* This provides the same API as the Python SDK's Database class.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.Database = exports.Transaction = void 0;
|
|
45
|
+
// Copyright 2025 Sushanth (https://github.com/sushanthpy)
|
|
46
|
+
//
|
|
47
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
48
|
+
// you may not use this file except in compliance with the License.
|
|
49
|
+
// You may obtain a copy of the License at
|
|
50
|
+
//
|
|
51
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const errors_1 = require("./errors");
|
|
55
|
+
const ipc_client_1 = require("./ipc-client");
|
|
56
|
+
const query_1 = require("./query");
|
|
57
|
+
const server_manager_1 = require("./server-manager");
|
|
58
|
+
/**
|
|
59
|
+
* Transaction handle for atomic operations.
|
|
60
|
+
*/
|
|
61
|
+
class Transaction {
|
|
62
|
+
constructor(db) {
|
|
63
|
+
this._txnId = null;
|
|
64
|
+
this._committed = false;
|
|
65
|
+
this._aborted = false;
|
|
66
|
+
this._db = db;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Begin the transaction.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
async begin() {
|
|
73
|
+
this._txnId = await this._db['_beginTransaction']();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get a value by key within this transaction.
|
|
77
|
+
*/
|
|
78
|
+
async get(key) {
|
|
79
|
+
this._ensureActive();
|
|
80
|
+
return this._db.get(key);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Put a key-value pair within this transaction.
|
|
84
|
+
*/
|
|
85
|
+
async put(key, value) {
|
|
86
|
+
this._ensureActive();
|
|
87
|
+
return this._db.put(key, value);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Delete a key within this transaction.
|
|
91
|
+
*/
|
|
92
|
+
async delete(key) {
|
|
93
|
+
this._ensureActive();
|
|
94
|
+
return this._db.delete(key);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Scan keys with a prefix within this transaction.
|
|
98
|
+
* @param prefix - The prefix to scan for
|
|
99
|
+
* @param end - Optional end boundary (exclusive)
|
|
100
|
+
*/
|
|
101
|
+
async *scan(prefix, end) {
|
|
102
|
+
this._ensureActive();
|
|
103
|
+
// Delegate to database's scan method
|
|
104
|
+
// Transactional isolation is maintained by the underlying storage
|
|
105
|
+
for await (const entry of this._db.scanGenerator(prefix, end)) {
|
|
106
|
+
yield entry;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get a value by path within this transaction.
|
|
111
|
+
*/
|
|
112
|
+
async getPath(pathStr) {
|
|
113
|
+
this._ensureActive();
|
|
114
|
+
return this._db.getPath(pathStr);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Put a value at a path within this transaction.
|
|
118
|
+
*/
|
|
119
|
+
async putPath(pathStr, value) {
|
|
120
|
+
this._ensureActive();
|
|
121
|
+
return this._db.putPath(pathStr, value);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Commit the transaction.
|
|
125
|
+
*
|
|
126
|
+
* After committing, an optional checkpoint is triggered to ensure writes
|
|
127
|
+
* are durable. This prevents race conditions where subsequent reads might
|
|
128
|
+
* not see committed data due to async flush timing.
|
|
129
|
+
*/
|
|
130
|
+
async commit() {
|
|
131
|
+
this._ensureActive();
|
|
132
|
+
if (this._txnId !== null) {
|
|
133
|
+
await this._db['_commitTransaction'](this._txnId);
|
|
134
|
+
// Trigger checkpoint to ensure durability (addresses race condition)
|
|
135
|
+
// Note: This is a trade-off between consistency and performance.
|
|
136
|
+
// For high-throughput scenarios, consider batching checkpoints.
|
|
137
|
+
await this._db.checkpoint();
|
|
138
|
+
}
|
|
139
|
+
this._committed = true;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Abort/rollback the transaction.
|
|
143
|
+
*/
|
|
144
|
+
async abort() {
|
|
145
|
+
if (this._committed || this._aborted)
|
|
146
|
+
return;
|
|
147
|
+
if (this._txnId !== null) {
|
|
148
|
+
await this._db['_abortTransaction'](this._txnId);
|
|
149
|
+
}
|
|
150
|
+
this._aborted = true;
|
|
151
|
+
}
|
|
152
|
+
_ensureActive() {
|
|
153
|
+
if (this._committed) {
|
|
154
|
+
throw new errors_1.TransactionError('Transaction already committed');
|
|
155
|
+
}
|
|
156
|
+
if (this._aborted) {
|
|
157
|
+
throw new errors_1.TransactionError('Transaction already aborted');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.Transaction = Transaction;
|
|
162
|
+
/**
|
|
163
|
+
* SochDB Database client.
|
|
164
|
+
*
|
|
165
|
+
* Provides access to SochDB with full transaction support.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* import { Database } from '@sushanth/sochdb';
|
|
170
|
+
*
|
|
171
|
+
* // Open a database
|
|
172
|
+
* const db = await Database.open('./my_database');
|
|
173
|
+
*
|
|
174
|
+
* // Simple key-value operations
|
|
175
|
+
* await db.put(Buffer.from('user:123'), Buffer.from('{"name": "Alice"}'));
|
|
176
|
+
* const value = await db.get(Buffer.from('user:123'));
|
|
177
|
+
*
|
|
178
|
+
* // Path-native API
|
|
179
|
+
* await db.putPath('users/alice/email', Buffer.from('alice@example.com'));
|
|
180
|
+
* const email = await db.getPath('users/alice/email');
|
|
181
|
+
*
|
|
182
|
+
* // Transactions
|
|
183
|
+
* await db.withTransaction(async (txn) => {
|
|
184
|
+
* await txn.put(Buffer.from('key1'), Buffer.from('value1'));
|
|
185
|
+
* await txn.put(Buffer.from('key2'), Buffer.from('value2'));
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* // Clean up
|
|
189
|
+
* await db.close();
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
class Database {
|
|
193
|
+
constructor(config) {
|
|
194
|
+
this._client = null;
|
|
195
|
+
this._closed = false;
|
|
196
|
+
this._embeddedServerStarted = false;
|
|
197
|
+
this._config = {
|
|
198
|
+
createIfMissing: true,
|
|
199
|
+
walEnabled: true,
|
|
200
|
+
syncMode: 'normal',
|
|
201
|
+
memtableSizeBytes: 64 * 1024 * 1024,
|
|
202
|
+
embedded: true, // Default to embedded mode
|
|
203
|
+
...config,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Open a database at the specified path.
|
|
208
|
+
*
|
|
209
|
+
* @param pathOrConfig - Path to the database directory or configuration object
|
|
210
|
+
* @returns A new Database instance
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* // Simple usage (embedded mode - starts server automatically)
|
|
215
|
+
* const db = await Database.open('./my_database');
|
|
216
|
+
*
|
|
217
|
+
* // With configuration
|
|
218
|
+
* const db = await Database.open({
|
|
219
|
+
* path: './my_database',
|
|
220
|
+
* walEnabled: true,
|
|
221
|
+
* syncMode: 'full',
|
|
222
|
+
* });
|
|
223
|
+
*
|
|
224
|
+
* // Connect to existing external server
|
|
225
|
+
* const db = await Database.open({
|
|
226
|
+
* path: './my_database',
|
|
227
|
+
* embedded: false, // Don't start embedded server
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
static async open(pathOrConfig) {
|
|
232
|
+
const config = typeof pathOrConfig === 'string' ? { path: pathOrConfig } : pathOrConfig;
|
|
233
|
+
// Ensure database directory exists
|
|
234
|
+
if (config.createIfMissing !== false) {
|
|
235
|
+
if (!fs.existsSync(config.path)) {
|
|
236
|
+
fs.mkdirSync(config.path, { recursive: true });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const db = new Database(config);
|
|
240
|
+
// Start embedded server if configured (default: true)
|
|
241
|
+
let socketPath;
|
|
242
|
+
if (db._config.embedded !== false) {
|
|
243
|
+
// Start embedded server and get socket path
|
|
244
|
+
socketPath = await (0, server_manager_1.startEmbeddedServer)(config.path);
|
|
245
|
+
db._embeddedServerStarted = true;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Connect to existing server socket
|
|
249
|
+
socketPath = path.join(config.path, 'sochdb.sock');
|
|
250
|
+
}
|
|
251
|
+
db._client = await ipc_client_1.IpcClient.connect(socketPath);
|
|
252
|
+
// Track database open event (only analytics event we send)
|
|
253
|
+
try {
|
|
254
|
+
const { trackDatabaseOpen } = await Promise.resolve().then(() => __importStar(require('./analytics.js')));
|
|
255
|
+
await trackDatabaseOpen(config.path, 'embedded');
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Never let analytics break database operations
|
|
259
|
+
}
|
|
260
|
+
return db;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get a value by key.
|
|
264
|
+
*
|
|
265
|
+
* @param key - The key to look up (Buffer or string)
|
|
266
|
+
* @returns The value as a Buffer, or null if not found
|
|
267
|
+
*/
|
|
268
|
+
async get(key) {
|
|
269
|
+
this._ensureOpen();
|
|
270
|
+
const keyBuf = typeof key === 'string' ? Buffer.from(key) : key;
|
|
271
|
+
return this._client.get(keyBuf);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Put a key-value pair.
|
|
275
|
+
*
|
|
276
|
+
* @param key - The key (Buffer or string)
|
|
277
|
+
* @param value - The value (Buffer or string)
|
|
278
|
+
*/
|
|
279
|
+
async put(key, value) {
|
|
280
|
+
this._ensureOpen();
|
|
281
|
+
const keyBuf = typeof key === 'string' ? Buffer.from(key) : key;
|
|
282
|
+
const valueBuf = typeof value === 'string' ? Buffer.from(value) : value;
|
|
283
|
+
return this._client.put(keyBuf, valueBuf);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Delete a key.
|
|
287
|
+
*
|
|
288
|
+
* @param key - The key to delete (Buffer or string)
|
|
289
|
+
*/
|
|
290
|
+
async delete(key) {
|
|
291
|
+
this._ensureOpen();
|
|
292
|
+
const keyBuf = typeof key === 'string' ? Buffer.from(key) : key;
|
|
293
|
+
return this._client.delete(keyBuf);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get a value by path.
|
|
297
|
+
*
|
|
298
|
+
* @param pathStr - The path (e.g., "users/alice/email")
|
|
299
|
+
* @returns The value as a Buffer, or null if not found
|
|
300
|
+
*/
|
|
301
|
+
async getPath(pathStr) {
|
|
302
|
+
this._ensureOpen();
|
|
303
|
+
return this._client.getPath(pathStr);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Put a value at a path.
|
|
307
|
+
*
|
|
308
|
+
* @param pathStr - The path (e.g., "users/alice/email")
|
|
309
|
+
* @param value - The value (Buffer or string)
|
|
310
|
+
*/
|
|
311
|
+
async putPath(pathStr, value) {
|
|
312
|
+
this._ensureOpen();
|
|
313
|
+
const valueBuf = typeof value === 'string' ? Buffer.from(value) : value;
|
|
314
|
+
return this._client.putPath(pathStr, valueBuf);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Create a query builder for the given path prefix.
|
|
318
|
+
*
|
|
319
|
+
* @param pathPrefix - The path prefix to query (e.g., "users/")
|
|
320
|
+
* @returns A Query builder instance
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```typescript
|
|
324
|
+
* const results = await db.query('users/')
|
|
325
|
+
* .limit(10)
|
|
326
|
+
* .select(['name', 'email'])
|
|
327
|
+
* .execute();
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
query(pathPrefix) {
|
|
331
|
+
this._ensureOpen();
|
|
332
|
+
return new query_1.Query(this._client, pathPrefix);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Scan for keys with a prefix, returning key-value pairs.
|
|
336
|
+
* This is the preferred method for simple prefix-based iteration.
|
|
337
|
+
*
|
|
338
|
+
* @param prefix - The prefix to scan for (e.g., "users/", "tenants/tenant1/")
|
|
339
|
+
* @returns Array of key-value pairs
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* const results = await db.scan('tenants/tenant1/');
|
|
344
|
+
* for (const { key, value } of results) {
|
|
345
|
+
* console.log(`${key.toString()}: ${value.toString()}`);
|
|
346
|
+
* }
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
async scan(prefix) {
|
|
350
|
+
this._ensureOpen();
|
|
351
|
+
return this._client.scan(prefix);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Scan for keys with a prefix using an async generator.
|
|
355
|
+
* This allows for memory-efficient iteration over large result sets.
|
|
356
|
+
*
|
|
357
|
+
* @param prefix - The prefix to scan for
|
|
358
|
+
* @param end - Optional end boundary (exclusive)
|
|
359
|
+
* @returns Async generator yielding [key, value] tuples
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```typescript
|
|
363
|
+
* for await (const [key, value] of db.scanGenerator('users/')) {
|
|
364
|
+
* console.log(`${key.toString()}: ${value.toString()}`);
|
|
365
|
+
* }
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
async *scanGenerator(prefix, end) {
|
|
369
|
+
this._ensureOpen();
|
|
370
|
+
const prefixBuf = typeof prefix === 'string' ? Buffer.from(prefix) : prefix;
|
|
371
|
+
const endBuf = end ? (typeof end === 'string' ? Buffer.from(end) : end) : undefined;
|
|
372
|
+
const results = await this._client.scan(prefixBuf.toString());
|
|
373
|
+
for (const { key, value } of results) {
|
|
374
|
+
// Filter by end boundary if provided
|
|
375
|
+
if (endBuf && Buffer.compare(key, endBuf) >= 0) {
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
yield [key, value];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Execute operations within a transaction.
|
|
383
|
+
*
|
|
384
|
+
* The transaction automatically commits on success or aborts on error.
|
|
385
|
+
*
|
|
386
|
+
* @param fn - Async function that receives a Transaction object
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```typescript
|
|
390
|
+
* await db.withTransaction(async (txn) => {
|
|
391
|
+
* await txn.put(Buffer.from('key1'), Buffer.from('value1'));
|
|
392
|
+
* await txn.put(Buffer.from('key2'), Buffer.from('value2'));
|
|
393
|
+
* // Automatically commits
|
|
394
|
+
* });
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
async withTransaction(fn) {
|
|
398
|
+
this._ensureOpen();
|
|
399
|
+
const txn = new Transaction(this);
|
|
400
|
+
await txn.begin();
|
|
401
|
+
try {
|
|
402
|
+
const result = await fn(txn);
|
|
403
|
+
await txn.commit();
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
await txn.abort();
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Create a new transaction.
|
|
413
|
+
*
|
|
414
|
+
* @returns A new Transaction instance
|
|
415
|
+
* @deprecated Use withTransaction() for automatic commit/abort handling
|
|
416
|
+
*/
|
|
417
|
+
async transaction() {
|
|
418
|
+
this._ensureOpen();
|
|
419
|
+
const txn = new Transaction(this);
|
|
420
|
+
await txn.begin();
|
|
421
|
+
return txn;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Force a checkpoint to persist memtable to disk.
|
|
425
|
+
*/
|
|
426
|
+
async checkpoint() {
|
|
427
|
+
this._ensureOpen();
|
|
428
|
+
return this._client.checkpoint();
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get storage statistics.
|
|
432
|
+
*/
|
|
433
|
+
async stats() {
|
|
434
|
+
this._ensureOpen();
|
|
435
|
+
return this._client.stats();
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Execute a SQL query.
|
|
439
|
+
*
|
|
440
|
+
* SochDB supports a subset of SQL for relational data stored on top of
|
|
441
|
+
* the key-value engine. Tables and rows are stored as:
|
|
442
|
+
* - Schema: _sql/tables/{table_name}/schema
|
|
443
|
+
* - Rows: _sql/tables/{table_name}/rows/{row_id}
|
|
444
|
+
*
|
|
445
|
+
* Supported SQL:
|
|
446
|
+
* - CREATE TABLE table_name (col1 TYPE, col2 TYPE, ...)
|
|
447
|
+
* - DROP TABLE table_name
|
|
448
|
+
* - INSERT INTO table_name (cols) VALUES (vals)
|
|
449
|
+
* - SELECT cols FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]
|
|
450
|
+
* - UPDATE table_name SET col=val [WHERE ...]
|
|
451
|
+
* - DELETE FROM table_name [WHERE ...]
|
|
452
|
+
*
|
|
453
|
+
* Supported types: INT, TEXT, FLOAT, BOOL, BLOB
|
|
454
|
+
*
|
|
455
|
+
* @param sql - SQL query string
|
|
456
|
+
* @returns SQLQueryResult with rows and metadata
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* // Create a table
|
|
461
|
+
* await db.execute("CREATE TABLE users (id INT PRIMARY KEY, name TEXT, age INT)");
|
|
462
|
+
*
|
|
463
|
+
* // Insert data
|
|
464
|
+
* await db.execute("INSERT INTO users (id, name, age) VALUES (1, 'Alice', 30)");
|
|
465
|
+
*
|
|
466
|
+
* // Query data
|
|
467
|
+
* const result = await db.execute("SELECT * FROM users WHERE age > 26");
|
|
468
|
+
* result.rows.forEach(row => console.log(row));
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
async execute(sql) {
|
|
472
|
+
this._ensureOpen();
|
|
473
|
+
// Import the SQL executor
|
|
474
|
+
const { SQLExecutor } = await Promise.resolve().then(() => __importStar(require('./sql-engine.js')));
|
|
475
|
+
// Create a database adapter for the SQL executor
|
|
476
|
+
const dbAdapter = {
|
|
477
|
+
get: (key) => this.get(key),
|
|
478
|
+
put: (key, value) => this.put(key, value),
|
|
479
|
+
delete: (key) => this.delete(key),
|
|
480
|
+
scan: (prefix) => this.scan(prefix),
|
|
481
|
+
};
|
|
482
|
+
const executor = new SQLExecutor(dbAdapter);
|
|
483
|
+
return executor.execute(sql);
|
|
484
|
+
}
|
|
485
|
+
// =========================================================================
|
|
486
|
+
// Static Serialization Methods
|
|
487
|
+
// =========================================================================
|
|
488
|
+
/**
|
|
489
|
+
* Convert records to TOON format for token-efficient LLM context.
|
|
490
|
+
*
|
|
491
|
+
* TOON format achieves 40-66% token reduction compared to JSON by using
|
|
492
|
+
* a columnar text format with minimal syntax.
|
|
493
|
+
*
|
|
494
|
+
* @param tableName - Name of the table/collection
|
|
495
|
+
* @param records - Array of objects with the data
|
|
496
|
+
* @param fields - Optional array of field names to include
|
|
497
|
+
* @returns TOON-formatted string
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* const records = [
|
|
502
|
+
* { id: 1, name: 'Alice', email: 'alice@ex.com' },
|
|
503
|
+
* { id: 2, name: 'Bob', email: 'bob@ex.com' }
|
|
504
|
+
* ];
|
|
505
|
+
* console.log(Database.toToon('users', records, ['name', 'email']));
|
|
506
|
+
* // users[2]{name,email}:Alice,alice@ex.com;Bob,bob@ex.com
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
static toToon(tableName, records, fields) {
|
|
510
|
+
if (!records || records.length === 0) {
|
|
511
|
+
return `${tableName}[0]{}:`;
|
|
512
|
+
}
|
|
513
|
+
// Determine fields from first record if not specified
|
|
514
|
+
const useFields = fields ?? Object.keys(records[0]);
|
|
515
|
+
// Build header: table[count]{field1,field2,...}:
|
|
516
|
+
const header = `${tableName}[${records.length}]{${useFields.join(',')}}:`;
|
|
517
|
+
// Escape values containing delimiters
|
|
518
|
+
const escapeValue = (v) => {
|
|
519
|
+
const s = v != null ? String(v) : '';
|
|
520
|
+
if (s.includes(',') || s.includes(';') || s.includes('\n')) {
|
|
521
|
+
return `"${s}"`;
|
|
522
|
+
}
|
|
523
|
+
return s;
|
|
524
|
+
};
|
|
525
|
+
// Build rows: value1,value2;value1,value2;...
|
|
526
|
+
const rows = records
|
|
527
|
+
.map(r => useFields.map(f => escapeValue(r[f])).join(','))
|
|
528
|
+
.join(';');
|
|
529
|
+
return header + rows;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Convert records to JSON format for easy application decoding.
|
|
533
|
+
*
|
|
534
|
+
* While TOON format is optimized for LLM context (40-66% token reduction),
|
|
535
|
+
* JSON is often easier for applications to parse. Use this method when
|
|
536
|
+
* the output will be consumed by application code rather than LLMs.
|
|
537
|
+
*
|
|
538
|
+
* @param tableName - Name of the table/collection
|
|
539
|
+
* @param records - Array of objects with the data
|
|
540
|
+
* @param fields - Optional array of field names to include
|
|
541
|
+
* @param compact - If true (default), outputs minified JSON
|
|
542
|
+
* @returns JSON-formatted string
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```typescript
|
|
546
|
+
* const records = [
|
|
547
|
+
* { id: 1, name: 'Alice' },
|
|
548
|
+
* { id: 2, name: 'Bob' }
|
|
549
|
+
* ];
|
|
550
|
+
* console.log(Database.toJson('users', records));
|
|
551
|
+
* // {"table":"users","count":2,"records":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]}
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
static toJson(tableName, records, fields, compact = true) {
|
|
555
|
+
if (!records || records.length === 0) {
|
|
556
|
+
return JSON.stringify({ table: tableName, count: 0, records: [] });
|
|
557
|
+
}
|
|
558
|
+
// Filter fields if specified
|
|
559
|
+
const filteredRecords = fields
|
|
560
|
+
? records.map(r => {
|
|
561
|
+
const filtered = {};
|
|
562
|
+
for (const f of fields) {
|
|
563
|
+
filtered[f] = r[f];
|
|
564
|
+
}
|
|
565
|
+
return filtered;
|
|
566
|
+
})
|
|
567
|
+
: records;
|
|
568
|
+
const output = {
|
|
569
|
+
table: tableName,
|
|
570
|
+
count: filteredRecords.length,
|
|
571
|
+
records: filteredRecords,
|
|
572
|
+
};
|
|
573
|
+
return compact ? JSON.stringify(output) : JSON.stringify(output, null, 2);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Parse a JSON format string back to structured data.
|
|
577
|
+
*
|
|
578
|
+
* @param jsonStr - JSON-formatted string (from toJson)
|
|
579
|
+
* @returns Object with table, fields, and records
|
|
580
|
+
*/
|
|
581
|
+
static fromJson(jsonStr) {
|
|
582
|
+
const data = JSON.parse(jsonStr);
|
|
583
|
+
const table = data.table ?? 'unknown';
|
|
584
|
+
const records = data.records ?? [];
|
|
585
|
+
const fields = records.length > 0 ? Object.keys(records[0]) : [];
|
|
586
|
+
return { table, fields, records };
|
|
587
|
+
}
|
|
588
|
+
// =========================================================================
|
|
589
|
+
// Graph Overlay Operations
|
|
590
|
+
// =========================================================================
|
|
591
|
+
/**
|
|
592
|
+
* Add a node to the graph overlay.
|
|
593
|
+
*
|
|
594
|
+
* @param namespace - Namespace for the graph
|
|
595
|
+
* @param nodeId - Unique node identifier
|
|
596
|
+
* @param nodeType - Type of node (e.g., "person", "document", "concept")
|
|
597
|
+
* @param properties - Optional node properties
|
|
598
|
+
*
|
|
599
|
+
* @example
|
|
600
|
+
* ```typescript
|
|
601
|
+
* await db.addNode('default', 'alice', 'person', { role: 'engineer' });
|
|
602
|
+
* await db.addNode('default', 'project_x', 'project', { status: 'active' });
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
async addNode(namespace, nodeId, nodeType, properties) {
|
|
606
|
+
this._ensureOpen();
|
|
607
|
+
const key = `_graph/${namespace}/nodes/${nodeId}`;
|
|
608
|
+
const value = JSON.stringify({
|
|
609
|
+
id: nodeId,
|
|
610
|
+
node_type: nodeType,
|
|
611
|
+
properties: properties || {},
|
|
612
|
+
});
|
|
613
|
+
await this.put(Buffer.from(key), Buffer.from(value));
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Add an edge between nodes in the graph overlay.
|
|
617
|
+
*
|
|
618
|
+
* @param namespace - Namespace for the graph
|
|
619
|
+
* @param fromId - Source node ID
|
|
620
|
+
* @param edgeType - Type of relationship
|
|
621
|
+
* @param toId - Target node ID
|
|
622
|
+
* @param properties - Optional edge properties
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```typescript
|
|
626
|
+
* await db.addEdge('default', 'alice', 'works_on', 'project_x');
|
|
627
|
+
* await db.addEdge('default', 'alice', 'knows', 'bob', { since: '2020' });
|
|
628
|
+
* ```
|
|
629
|
+
*/
|
|
630
|
+
async addEdge(namespace, fromId, edgeType, toId, properties) {
|
|
631
|
+
this._ensureOpen();
|
|
632
|
+
const key = `_graph/${namespace}/edges/${fromId}/${edgeType}/${toId}`;
|
|
633
|
+
const value = JSON.stringify({
|
|
634
|
+
from_id: fromId,
|
|
635
|
+
edge_type: edgeType,
|
|
636
|
+
to_id: toId,
|
|
637
|
+
properties: properties || {},
|
|
638
|
+
});
|
|
639
|
+
await this.put(Buffer.from(key), Buffer.from(value));
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Traverse the graph from a starting node.
|
|
643
|
+
*
|
|
644
|
+
* @param namespace - Namespace for the graph
|
|
645
|
+
* @param startNode - Node ID to start traversal from
|
|
646
|
+
* @param maxDepth - Maximum traversal depth (default: 10)
|
|
647
|
+
* @param order - "bfs" for breadth-first, "dfs" for depth-first
|
|
648
|
+
* @returns Object with nodes and edges arrays
|
|
649
|
+
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```typescript
|
|
652
|
+
* const { nodes, edges } = await db.traverse('default', 'alice', 2);
|
|
653
|
+
* for (const node of nodes) {
|
|
654
|
+
* console.log(`Node: ${node.id} (${node.node_type})`);
|
|
655
|
+
* }
|
|
656
|
+
* ```
|
|
657
|
+
*/
|
|
658
|
+
async traverse(namespace, startNode, maxDepth = 10, order = 'bfs') {
|
|
659
|
+
this._ensureOpen();
|
|
660
|
+
const visited = new Set();
|
|
661
|
+
const nodes = [];
|
|
662
|
+
const edges = [];
|
|
663
|
+
const frontier = [[startNode, 0]];
|
|
664
|
+
while (frontier.length > 0) {
|
|
665
|
+
const [currentNode, depth] = order === 'bfs'
|
|
666
|
+
? frontier.shift()
|
|
667
|
+
: frontier.pop();
|
|
668
|
+
if (depth > maxDepth || visited.has(currentNode)) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
visited.add(currentNode);
|
|
672
|
+
// Get node data
|
|
673
|
+
const nodeKey = `_graph/${namespace}/nodes/${currentNode}`;
|
|
674
|
+
const nodeData = await this.get(Buffer.from(nodeKey));
|
|
675
|
+
if (nodeData) {
|
|
676
|
+
nodes.push(JSON.parse(nodeData.toString()));
|
|
677
|
+
}
|
|
678
|
+
// Get outgoing edges
|
|
679
|
+
const edgePrefix = `_graph/${namespace}/edges/${currentNode}/`;
|
|
680
|
+
const edgeResults = await this.scan(edgePrefix);
|
|
681
|
+
for (const { value } of edgeResults) {
|
|
682
|
+
const edge = JSON.parse(value.toString());
|
|
683
|
+
edges.push(edge);
|
|
684
|
+
if (!visited.has(edge.to_id)) {
|
|
685
|
+
frontier.push([edge.to_id, depth + 1]);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { nodes, edges };
|
|
690
|
+
}
|
|
691
|
+
// =========================================================================
|
|
692
|
+
// Semantic Cache Operations
|
|
693
|
+
// =========================================================================
|
|
694
|
+
/**
|
|
695
|
+
* Store a value in the semantic cache with its embedding.
|
|
696
|
+
*
|
|
697
|
+
* @param cacheName - Name of the cache
|
|
698
|
+
* @param key - Cache key (for display/debugging)
|
|
699
|
+
* @param value - Value to cache
|
|
700
|
+
* @param embedding - Embedding vector for similarity matching
|
|
701
|
+
* @param ttlSeconds - Time-to-live in seconds (0 = no expiry)
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```typescript
|
|
705
|
+
* await db.cachePut(
|
|
706
|
+
* 'llm_responses',
|
|
707
|
+
* 'What is Python?',
|
|
708
|
+
* 'Python is a programming language...',
|
|
709
|
+
* [0.1, 0.2, 0.3, ...], // 384-dim
|
|
710
|
+
* 3600
|
|
711
|
+
* );
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
async cachePut(cacheName, key, value, embedding, ttlSeconds = 0) {
|
|
715
|
+
this._ensureOpen();
|
|
716
|
+
// Hash the key for storage
|
|
717
|
+
const keyHash = Buffer.from(key).toString('hex').slice(0, 16);
|
|
718
|
+
const cacheKey = `_cache/${cacheName}/${keyHash}`;
|
|
719
|
+
const expiresAt = ttlSeconds > 0
|
|
720
|
+
? Math.floor(Date.now() / 1000) + ttlSeconds
|
|
721
|
+
: 0;
|
|
722
|
+
const cacheValue = JSON.stringify({
|
|
723
|
+
key,
|
|
724
|
+
value,
|
|
725
|
+
embedding,
|
|
726
|
+
expires_at: expiresAt,
|
|
727
|
+
});
|
|
728
|
+
await this.put(Buffer.from(cacheKey), Buffer.from(cacheValue));
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Look up a value in the semantic cache by embedding similarity.
|
|
732
|
+
*
|
|
733
|
+
* @param cacheName - Name of the cache
|
|
734
|
+
* @param queryEmbedding - Query embedding to match against
|
|
735
|
+
* @param threshold - Minimum cosine similarity threshold (0.0 to 1.0)
|
|
736
|
+
* @returns Cached value if similarity >= threshold, null otherwise
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const result = await db.cacheGet(
|
|
741
|
+
* 'llm_responses',
|
|
742
|
+
* [0.12, 0.18, ...], // Similar to "What is Python?"
|
|
743
|
+
* 0.85
|
|
744
|
+
* );
|
|
745
|
+
* if (result) {
|
|
746
|
+
* console.log(`Cache hit: ${result}`);
|
|
747
|
+
* }
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
async cacheGet(cacheName, queryEmbedding, threshold = 0.85) {
|
|
751
|
+
this._ensureOpen();
|
|
752
|
+
const prefix = `_cache/${cacheName}/`;
|
|
753
|
+
const entries = await this.scan(prefix);
|
|
754
|
+
const now = Math.floor(Date.now() / 1000);
|
|
755
|
+
let bestMatch = null;
|
|
756
|
+
for (const { value } of entries) {
|
|
757
|
+
const entry = JSON.parse(value.toString());
|
|
758
|
+
// Check expiry
|
|
759
|
+
if (entry.expires_at > 0 && now > entry.expires_at) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
// Compute cosine similarity
|
|
763
|
+
if (entry.embedding && entry.embedding.length === queryEmbedding.length) {
|
|
764
|
+
const similarity = this._cosineSimilarity(queryEmbedding, entry.embedding);
|
|
765
|
+
if (similarity >= threshold) {
|
|
766
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
767
|
+
bestMatch = { similarity, value: entry.value };
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return bestMatch?.value ?? null;
|
|
773
|
+
}
|
|
774
|
+
_cosineSimilarity(a, b) {
|
|
775
|
+
let dot = 0, normA = 0, normB = 0;
|
|
776
|
+
for (let i = 0; i < a.length; i++) {
|
|
777
|
+
dot += a[i] * b[i];
|
|
778
|
+
normA += a[i] * a[i];
|
|
779
|
+
normB += b[i] * b[i];
|
|
780
|
+
}
|
|
781
|
+
normA = Math.sqrt(normA);
|
|
782
|
+
normB = Math.sqrt(normB);
|
|
783
|
+
return normA === 0 || normB === 0 ? 0 : dot / (normA * normB);
|
|
784
|
+
}
|
|
785
|
+
// =========================================================================
|
|
786
|
+
// Trace Operations (Observability)
|
|
787
|
+
// =========================================================================
|
|
788
|
+
/**
|
|
789
|
+
* Start a new trace.
|
|
790
|
+
*
|
|
791
|
+
* @param name - Name of the trace (e.g., "user_request", "batch_job")
|
|
792
|
+
* @returns Object with traceId and rootSpanId
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* ```typescript
|
|
796
|
+
* const { traceId, rootSpanId } = await db.startTrace('user_query');
|
|
797
|
+
* // ... do work ...
|
|
798
|
+
* await db.endSpan(traceId, rootSpanId, 'ok');
|
|
799
|
+
* ```
|
|
800
|
+
*/
|
|
801
|
+
async startTrace(name) {
|
|
802
|
+
this._ensureOpen();
|
|
803
|
+
const traceId = `trace_${Date.now().toString(16)}${Math.random().toString(16).slice(2, 10)}`;
|
|
804
|
+
const spanId = `span_${Date.now().toString(16)}${Math.random().toString(16).slice(2, 10)}`;
|
|
805
|
+
const now = Date.now() * 1000; // microseconds
|
|
806
|
+
// Store trace
|
|
807
|
+
const traceKey = `_traces/${traceId}`;
|
|
808
|
+
const traceValue = JSON.stringify({
|
|
809
|
+
trace_id: traceId,
|
|
810
|
+
name,
|
|
811
|
+
start_us: now,
|
|
812
|
+
root_span_id: spanId,
|
|
813
|
+
});
|
|
814
|
+
await this.put(Buffer.from(traceKey), Buffer.from(traceValue));
|
|
815
|
+
// Store root span
|
|
816
|
+
const spanKey = `_traces/${traceId}/spans/${spanId}`;
|
|
817
|
+
const spanValue = JSON.stringify({
|
|
818
|
+
span_id: spanId,
|
|
819
|
+
name,
|
|
820
|
+
start_us: now,
|
|
821
|
+
parent_span_id: null,
|
|
822
|
+
status: 'active',
|
|
823
|
+
});
|
|
824
|
+
await this.put(Buffer.from(spanKey), Buffer.from(spanValue));
|
|
825
|
+
return { traceId, rootSpanId: spanId };
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Start a child span within a trace.
|
|
829
|
+
*
|
|
830
|
+
* @param traceId - ID of the parent trace
|
|
831
|
+
* @param parentSpanId - ID of the parent span
|
|
832
|
+
* @param name - Name of this span
|
|
833
|
+
* @returns The new span ID
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```typescript
|
|
837
|
+
* const { traceId, rootSpanId } = await db.startTrace('user_query');
|
|
838
|
+
* const dbSpan = await db.startSpan(traceId, rootSpanId, 'database_lookup');
|
|
839
|
+
* // ... do database work ...
|
|
840
|
+
* const duration = await db.endSpan(traceId, dbSpan, 'ok');
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
async startSpan(traceId, parentSpanId, name) {
|
|
844
|
+
this._ensureOpen();
|
|
845
|
+
const spanId = `span_${Date.now().toString(16)}${Math.random().toString(16).slice(2, 10)}`;
|
|
846
|
+
const now = Date.now() * 1000;
|
|
847
|
+
const spanKey = `_traces/${traceId}/spans/${spanId}`;
|
|
848
|
+
const spanValue = JSON.stringify({
|
|
849
|
+
span_id: spanId,
|
|
850
|
+
name,
|
|
851
|
+
start_us: now,
|
|
852
|
+
parent_span_id: parentSpanId,
|
|
853
|
+
status: 'active',
|
|
854
|
+
});
|
|
855
|
+
await this.put(Buffer.from(spanKey), Buffer.from(spanValue));
|
|
856
|
+
return spanId;
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* End a span and record its duration.
|
|
860
|
+
*
|
|
861
|
+
* @param traceId - ID of the trace
|
|
862
|
+
* @param spanId - ID of the span to end
|
|
863
|
+
* @param status - "ok", "error", or "unset"
|
|
864
|
+
* @returns Duration in microseconds
|
|
865
|
+
*
|
|
866
|
+
* @example
|
|
867
|
+
* ```typescript
|
|
868
|
+
* const duration = await db.endSpan(traceId, spanId, 'ok');
|
|
869
|
+
* console.log(`Operation took ${duration}µs`);
|
|
870
|
+
* ```
|
|
871
|
+
*/
|
|
872
|
+
async endSpan(traceId, spanId, status = 'ok') {
|
|
873
|
+
this._ensureOpen();
|
|
874
|
+
const spanKey = `_traces/${traceId}/spans/${spanId}`;
|
|
875
|
+
const spanData = await this.get(Buffer.from(spanKey));
|
|
876
|
+
if (!spanData) {
|
|
877
|
+
throw new errors_1.DatabaseError(`Span not found: ${spanId}`);
|
|
878
|
+
}
|
|
879
|
+
const span = JSON.parse(spanData.toString());
|
|
880
|
+
const now = Date.now() * 1000;
|
|
881
|
+
const duration = now - span.start_us;
|
|
882
|
+
const updatedSpan = {
|
|
883
|
+
...span,
|
|
884
|
+
status,
|
|
885
|
+
end_us: now,
|
|
886
|
+
duration_us: duration,
|
|
887
|
+
};
|
|
888
|
+
await this.put(Buffer.from(spanKey), Buffer.from(JSON.stringify(updatedSpan)));
|
|
889
|
+
return duration;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Close the database connection.
|
|
893
|
+
* If running in embedded mode, also stops the embedded server.
|
|
894
|
+
*/
|
|
895
|
+
async close() {
|
|
896
|
+
if (this._closed)
|
|
897
|
+
return;
|
|
898
|
+
if (this._client) {
|
|
899
|
+
await this._client.close();
|
|
900
|
+
this._client = null;
|
|
901
|
+
}
|
|
902
|
+
// Stop embedded server if we started it
|
|
903
|
+
if (this._embeddedServerStarted) {
|
|
904
|
+
await (0, server_manager_1.stopEmbeddedServer)(this._config.path);
|
|
905
|
+
this._embeddedServerStarted = false;
|
|
906
|
+
}
|
|
907
|
+
this._closed = true;
|
|
908
|
+
}
|
|
909
|
+
// Internal methods for transaction management
|
|
910
|
+
async _beginTransaction() {
|
|
911
|
+
return this._client.beginTransaction();
|
|
912
|
+
}
|
|
913
|
+
async _commitTransaction(txnId) {
|
|
914
|
+
return this._client.commitTransaction(txnId);
|
|
915
|
+
}
|
|
916
|
+
async _abortTransaction(txnId) {
|
|
917
|
+
return this._client.abortTransaction(txnId);
|
|
918
|
+
}
|
|
919
|
+
_ensureOpen() {
|
|
920
|
+
if (this._closed) {
|
|
921
|
+
throw new errors_1.DatabaseError('Database is closed');
|
|
922
|
+
}
|
|
923
|
+
if (!this._client) {
|
|
924
|
+
throw new errors_1.DatabaseError('Database not connected');
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
exports.Database = Database;
|
|
929
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGF0YWJhc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7O0dBT0c7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVILDBEQUEwRDtBQUMxRCxFQUFFO0FBQ0Ysa0VBQWtFO0FBQ2xFLG1FQUFtRTtBQUNuRSwwQ0FBMEM7QUFDMUMsRUFBRTtBQUNGLGlEQUFpRDtBQUVqRCx1Q0FBeUI7QUFDekIsMkNBQTZCO0FBQzdCLHFDQUEyRDtBQUMzRCw2Q0FBeUM7QUFDekMsbUNBQWdDO0FBQ2hDLHFEQUEyRTtBQW1DM0U7O0dBRUc7QUFDSCxNQUFhLFdBQVc7SUFNdEIsWUFBWSxFQUFZO1FBSmhCLFdBQU0sR0FBa0IsSUFBSSxDQUFDO1FBQzdCLGVBQVUsR0FBRyxLQUFLLENBQUM7UUFDbkIsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUd2QixJQUFJLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQztJQUNoQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLEtBQUs7UUFDVCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUM7SUFDdEQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFvQjtRQUM1QixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDckIsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMzQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQW9CLEVBQUUsS0FBc0I7UUFDcEQsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3JCLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lB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SSxzQkFBYSxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFDcEQsQ0FBQztJQUNILENBQUM7Q0FDRjtBQXgyQkQsNEJBdzJCQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU29jaERCIEVtYmVkZGVkIERhdGFiYXNlXG4gKlxuICogRGlyZWN0IGRhdGFiYXNlIGFjY2VzcyB2aWEgSVBDIHRvIHRoZSBTb2NoREIgc2VydmVyLlxuICogVGhpcyBwcm92aWRlcyB0aGUgc2FtZSBBUEkgYXMgdGhlIFB5dGhvbiBTREsncyBEYXRhYmFzZSBjbGFzcy5cbiAqXG4gKiBAcGFja2FnZURvY3VtZW50YXRpb25cbiAqL1xuXG4vLyBDb3B5cmlnaHQgMjAyNSBTdXNoYW50aCAoaHR0cHM6Ly9naXRodWIuY29tL3N1c2hhbnRocHkpXG4vL1xuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbi8vIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbi8vIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuLy9cbi8vICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IERhdGFiYXNlRXJyb3IsIFRyYW5zYWN0aW9uRXJyb3IgfSBmcm9tICcuL2Vycm9ycyc7XG5pbXBvcnQgeyBJcGNDbGllbnQgfSBmcm9tICcuL2lwYy1jbGllbnQnO1xuaW1wb3J0IHsgUXVlcnkgfSBmcm9tICcuL3F1ZXJ5JztcbmltcG9ydCB7IHN0YXJ0RW1iZWRkZWRTZXJ2ZXIsIHN0b3BFbWJlZGRlZFNlcnZlciB9IGZyb20gJy4vc2VydmVyLW1hbmFnZXInO1xuXG4vKipcbiAqIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIERhdGFiYXNlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIERhdGFiYXNlQ29uZmlnIHtcbiAgLyoqIFBhdGggdG8gdGhlIGRhdGFiYXNlIGRpcmVjdG9yeSAqL1xuICBwYXRoOiBzdHJpbmc7XG4gIC8qKiBXaGV0aGVyIHRvIGNyZWF0ZSB0aGUgZGF0YWJhc2UgaWYgaXQgZG9lc24ndCBleGlzdCAoZGVmYXVsdDogdHJ1ZSkgKi9cbiAgY3JlYXRlSWZNaXNzaW5nPzogYm9vbGVhbjtcbiAgLyoqIEVuYWJsZSBXQUwgKFdyaXRlLUFoZWFkIExvZ2dpbmcpIGZvciBkdXJhYmlsaXR5IChkZWZhdWx0OiB0cnVlKSAqL1xuICB3YWxFbmFibGVkPzogYm9vbGVhbjtcbiAgLyoqIFN5bmMgbW9kZTogJ2Z1bGwnIHwgJ25vcm1hbCcgfCAnb2ZmJyAoZGVmYXVsdDogJ25vcm1hbCcpICovXG4gIHN5bmNNb2RlPzogJ2Z1bGwnIHwgJ25vcm1hbCcgfCAnb2ZmJztcbiAgLyoqIE1heGltdW0gc2l6ZSBvZiB0aGUgbWVtdGFibGUgYmVmb3JlIGZsdXNoaW5nIChkZWZhdWx0OiA2NE1CKSAqL1xuICBtZW10YWJsZVNpemVCeXRlcz86IG51bWJlcjtcbiAgLyoqIFxuICAgKiBXaGV0aGVyIHRvIGF1dG9tYXRpY2FsbHkgc3RhcnQgYW4gZW1iZWRkZWQgc2VydmVyIChkZWZhdWx0OiB0cnVlKVxuICAgKiBTZXQgdG8gZmFsc2UgaWYgY29ubmVjdGluZyB0byBhbiBleGlzdGluZyBleHRlcm5hbCBzZXJ2ZXJcbiAgICovXG4gIGVtYmVkZGVkPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBSZXN1bHQgb2YgYSBTUUwgcXVlcnkgZXhlY3V0aW9uLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNRTFF1ZXJ5UmVzdWx0IHtcbiAgLyoqIFJlc3VsdCByb3dzICovXG4gIHJvd3M6IEFycmF5PFJlY29yZDxzdHJpbmcsIGFueT4+O1xuICAvKiogQ29sdW1uIG5hbWVzICovXG4gIGNvbHVtbnM6IHN0cmluZ1tdO1xuICAvKiogTnVtYmVyIG9mIHJvd3MgYWZmZWN0ZWQgKGZvciBJTlNFUlQvVVBEQVRFL0RFTEVURSkgKi9cbiAgcm93c0FmZmVjdGVkOiBudW1iZXI7XG59XG5cbi8qKlxuICogVHJhbnNhY3Rpb24gaGFuZGxlIGZvciBhdG9taWMgb3BlcmF0aW9ucy5cbiAqL1xuZXhwb3J0IGNsYXNzIFRyYW5zYWN0aW9uIHtcbiAgcHJpdmF0ZSBfZGI6IERhdGFiYXNlO1xuICBwcml2YXRlIF90eG5JZDogYmlnaW50IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgX2NvbW1pdHRlZCA9IGZhbHNlO1xuICBwcml2YXRlIF9hYm9ydGVkID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoZGI6IERhdGFiYXNlKSB7XG4gICAgdGhpcy5fZGIgPSBkYjtcbiAgfVxuXG4gIC8qKlxuICAgKiBCZWdpbiB0aGUgdHJhbnNhY3Rpb24uXG4gICAqIEBpbnRlcm5hbFxuICAgKi9cbiAgYXN5bmMgYmVnaW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fdHhuSWQgPSBhd2FpdCB0aGlzLl9kYlsnX2JlZ2luVHJhbnNhY3Rpb24nXSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHZhbHVlIGJ5IGtleSB3aXRoaW4gdGhpcyB0cmFuc2FjdGlvbi5cbiAgICovXG4gIGFzeW5jIGdldChrZXk6IEJ1ZmZlciB8IHN0cmluZyk6IFByb21pc2U8QnVmZmVyIHwgbnVsbD4ge1xuICAgIHRoaXMuX2Vuc3VyZUFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl9kYi5nZXQoa2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQdXQgYSBrZXktdmFsdWUgcGFpciB3aXRoaW4gdGhpcyB0cmFuc2FjdGlvbi5cbiAgICovXG4gIGFzeW5jIHB1dChrZXk6IEJ1ZmZlciB8IHN0cmluZywgdmFsdWU6IEJ1ZmZlciB8IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuX2Vuc3VyZUFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl9kYi5wdXQoa2V5LCB2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIGEga2V5IHdpdGhpbiB0aGlzIHRyYW5zYWN0aW9uLlxuICAgKi9cbiAgYXN5bmMgZGVsZXRlKGtleTogQnVmZmVyIHwgc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fZW5zdXJlQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX2RiLmRlbGV0ZShrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNjYW4ga2V5cyB3aXRoIGEgcHJlZml4IHdpdGhpbiB0aGlzIHRyYW5zYWN0aW9uLlxuICAgKiBAcGFyYW0gcHJlZml4IC0gVGhlIHByZWZpeCB0byBzY2FuIGZvclxuICAgKiBAcGFyYW0gZW5kIC0gT3B0aW9uYWwgZW5kIGJvdW5kYXJ5IChleGNsdXNpdmUpXG4gICAqL1xuICBhc3luYyAqc2NhbihwcmVmaXg6IHN0cmluZyB8IEJ1ZmZlciwgZW5kPzogc3RyaW5nIHwgQnVmZmVyKTogQXN5bmNHZW5lcmF0b3I8W0J1ZmZlciwgQnVmZmVyXT4ge1xuICAgIHRoaXMuX2Vuc3VyZUFjdGl2ZSgpO1xuICAgIC8vIERlbGVnYXRlIHRvIGRhdGFiYXNlJ3Mgc2NhbiBtZXRob2RcbiAgICAvLyBUcmFuc2FjdGlvbmFsIGlzb2xhdGlvbiBpcyBtYWludGFpbmVkIGJ5IHRoZSB1bmRlcmx5aW5nIHN0b3JhZ2VcbiAgICBmb3IgYXdhaXQgKGNvbnN0IGVudHJ5IG9mIHRoaXMuX2RiLnNjYW5HZW5lcmF0b3IocHJlZml4LCBlbmQpKSB7XG4gICAgICB5aWVsZCBlbnRyeTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgdmFsdWUgYnkgcGF0aCB3aXRoaW4gdGhpcyB0cmFuc2FjdGlvbi5cbiAgICovXG4gIGFzeW5jIGdldFBhdGgocGF0aFN0cjogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXIgfCBudWxsPiB7XG4gICAgdGhpcy5fZW5zdXJlQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX2RiLmdldFBhdGgocGF0aFN0cik7XG4gIH1cblxuICAvKipcbiAgICogUHV0IGEgdmFsdWUgYXQgYSBwYXRoIHdpdGhpbiB0aGlzIHRyYW5zYWN0aW9uLlxuICAgKi9cbiAgYXN5bmMgcHV0UGF0aChwYXRoU3RyOiBzdHJpbmcsIHZhbHVlOiBCdWZmZXIgfCBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLl9lbnN1cmVBY3RpdmUoKTtcbiAgICByZXR1cm4gdGhpcy5fZGIucHV0UGF0aChwYXRoU3RyLCB2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tbWl0IHRoZSB0cmFuc2FjdGlvbi5cbiAgICogXG4gICAqIEFmdGVyIGNvbW1pdHRpbmcsIGFuIG9wdGlvbmFsIGNoZWNrcG9pbnQgaXMgdHJpZ2dlcmVkIHRvIGVuc3VyZSB3cml0ZXNcbiAgICogYXJlIGR1cmFibGUuIFRoaXMgcHJldmVudHMgcmFjZSBjb25kaXRpb25zIHdoZXJlIHN1YnNlcXVlbnQgcmVhZHMgbWlnaHRcbiAgICogbm90IHNlZSBjb21taXR0ZWQgZGF0YSBkdWUgdG8gYXN5bmMgZmx1c2ggdGltaW5nLlxuICAgKi9cbiAgYXN5bmMgY29tbWl0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuX2Vuc3VyZUFjdGl2ZSgpO1xuICAgIGlmICh0aGlzLl90eG5JZCAhPT0gbnVsbCkge1xuICAgICAgYXdhaXQgdGhpcy5fZGJbJ19jb21taXRUcmFuc2FjdGlvbiddKHRoaXMuX3R4bklkKTtcbiAgICAgIC8vIFRyaWdnZXIgY2hlY2twb2ludCB0byBlbnN1cmUgZHVyYWJpbGl0eSAoYWRkcmVzc2VzIHJhY2UgY29uZGl0aW9uKVxuICAgICAgLy8gTm90ZTogVGhpcyBpcyBhIHRyYWRlLW9mZiBiZXR3ZWVuIGNvbnNpc3RlbmN5IGFuZCBwZXJmb3JtYW5jZS5cbiAgICAgIC8vIEZvciBoaWdoLXRocm91Z2hwdXQgc2NlbmFyaW9zLCBjb25zaWRlciBiYXRjaGluZyBjaGVja3BvaW50cy5cbiAgICAgIGF3YWl0IHRoaXMuX2RiLmNoZWNrcG9pbnQoKTtcbiAgICB9XG4gICAgdGhpcy5fY29tbWl0dGVkID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBYm9ydC9yb2xsYmFjayB0aGUgdHJhbnNhY3Rpb24uXG4gICAqL1xuICBhc3luYyBhYm9ydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5fY29tbWl0dGVkIHx8IHRoaXMuX2Fib3J0ZWQpIHJldHVybjtcbiAgICBpZiAodGhpcy5fdHhuSWQgIT09IG51bGwpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2RiWydfYWJvcnRUcmFuc2FjdGlvbiddKHRoaXMuX3R4bklkKTtcbiAgICB9XG4gICAgdGhpcy5fYWJvcnRlZCA9IHRydWU7XG4gIH1cblxuICBwcml2YXRlIF9lbnN1cmVBY3RpdmUoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuX2NvbW1pdHRlZCkge1xuICAgICAgdGhyb3cgbmV3IFRyYW5zYWN0aW9uRXJyb3IoJ1RyYW5zYWN0aW9uIGFscmVhZHkgY29tbWl0dGVkJyk7XG4gICAgfVxuICAgIGlmICh0aGlzLl9hYm9ydGVkKSB7XG4gICAgICB0aHJvdyBuZXcgVHJhbnNhY3Rpb25FcnJvcignVHJhbnNhY3Rpb24gYWxyZWFkeSBhYm9ydGVkJyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogU29jaERCIERhdGFiYXNlIGNsaWVudC5cbiAqXG4gKiBQcm92aWRlcyBhY2Nlc3MgdG8gU29jaERCIHdpdGggZnVsbCB0cmFuc2FjdGlvbiBzdXBwb3J0LlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBEYXRhYmFzZSB9IGZyb20gJ0BzdXNoYW50aC9zb2NoZGInO1xuICpcbiAqIC8vIE9wZW4gYSBkYXRhYmFzZVxuICogY29uc3QgZGIgPSBhd2FpdCBEYXRhYmFzZS5vcGVuKCcuL215X2RhdGFiYXNlJyk7XG4gKlxuICogLy8gU2ltcGxlIGtleS12YWx1ZSBvcGVyYXRpb25zXG4gKiBhd2FpdCBkYi5wdXQoQnVmZmVyLmZyb20oJ3VzZXI6MTIzJyksIEJ1ZmZlci5mcm9tKCd7XCJuYW1lXCI6IFwiQWxpY2VcIn0nKSk7XG4gKiBjb25zdCB2YWx1ZSA9IGF3YWl0IGRiLmdldChCdWZmZXIuZnJvbSgndXNlcjoxMjMnKSk7XG4gKlxuICogLy8gUGF0aC1uYXRpdmUgQVBJXG4gKiBhd2FpdCBkYi5wdXRQYXRoKCd1c2Vycy9hbGljZS9lbWFpbCcsIEJ1ZmZlci5mcm9tKCdhbGljZUBleGFtcGxlLmNvbScpKTtcbiAqIGNvbnN0IGVtYWlsID0gYXdhaXQgZGIuZ2V0UGF0aCgndXNlcnMvYWxpY2UvZW1haWwnKTtcbiAqXG4gKiAvLyBUcmFuc2FjdGlvbnNcbiAqIGF3YWl0IGRiLndpdGhUcmFuc2FjdGlvbihhc3luYyAodHhuKSA9PiB7XG4gKiAgIGF3YWl0IHR4bi5wdXQoQnVmZmVyLmZyb20oJ2tleTEnKSwgQnVmZmVyLmZyb20oJ3ZhbHVlMScpKTtcbiAqICAgYXdhaXQgdHhuLnB1dChCdWZmZXIuZnJvbSgna2V5MicpLCBCdWZmZXIuZnJvbSgndmFsdWUyJykpO1xuICogfSk7XG4gKlxuICogLy8gQ2xlYW4gdXBcbiAqIGF3YWl0IGRiLmNsb3NlKCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIERhdGFiYXNlIHtcbiAgcHJpdmF0ZSBfY2xpZW50OiBJcGNDbGllbnQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBfY29uZmlnOiBEYXRhYmFzZUNvbmZpZztcbiAgcHJpdmF0ZSBfY2xvc2VkID0gZmFsc2U7XG4gIHByaXZhdGUgX2VtYmVkZGVkU2VydmVyU3RhcnRlZCA9IGZhbHNlO1xuXG4gIHByaXZhdGUgY29uc3RydWN0b3IoY29uZmlnOiBEYXRhYmFzZUNvbmZpZykge1xuICAgIHRoaXMuX2NvbmZpZyA9IHtcbiAgICAgIGNyZWF0ZUlmTWlzc2luZzogdHJ1ZSxcbiAgICAgIHdhbEVuYWJsZWQ6IHRydWUsXG4gICAgICBzeW5jTW9kZTogJ25vcm1hbCcsXG4gICAgICBtZW10YWJsZVNpemVCeXRlczogNjQgKiAxMDI0ICogMTAyNCxcbiAgICAgIGVtYmVkZGVkOiB0cnVlLCAgLy8gRGVmYXVsdCB0byBlbWJlZGRlZCBtb2RlXG4gICAgICAuLi5jb25maWcsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcGVuIGEgZGF0YWJhc2UgYXQgdGhlIHNwZWNpZmllZCBwYXRoLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aE9yQ29uZmlnIC0gUGF0aCB0byB0aGUgZGF0YWJhc2UgZGlyZWN0b3J5IG9yIGNvbmZpZ3VyYXRpb24gb2JqZWN0XG4gICAqIEByZXR1cm5zIEEgbmV3IERhdGFiYXNlIGluc3RhbmNlXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogLy8gU2ltcGxlIHVzYWdlIChlbWJlZGRlZCBtb2RlIC0gc3RhcnRzIHNlcnZlciBhdXRvbWF0aWNhbGx5KVxuICAgKiBjb25zdCBkYiA9IGF3YWl0IERhdGFiYXNlLm9wZW4oJy4vbXlfZGF0YWJhc2UnKTtcbiAgICpcbiAgICogLy8gV2l0aCBjb25maWd1cmF0aW9uXG4gICAqIGNvbnN0IGRiID0gYXdhaXQgRGF0YWJhc2Uub3Blbih7XG4gICAqICAgcGF0aDogJy4vbXlfZGF0YWJhc2UnLFxuICAgKiAgIHdhbEVuYWJsZWQ6IHRydWUsXG4gICAqICAgc3luY01vZGU6ICdmdWxsJyxcbiAgICogfSk7XG4gICAqIFxuICAgKiAvLyBDb25uZWN0IHRvIGV4aXN0aW5nIGV4dGVybmFsIHNlcnZlclxuICAgKiBjb25zdCBkYiA9IGF3YWl0IERhdGFiYXNlLm9wZW4oe1xuICAgKiAgIHBhdGg6ICcuL215X2RhdGFiYXNlJyxcbiAgICogICBlbWJlZGRlZDogZmFsc2UsICAvLyBEb24ndCBzdGFydCBlbWJlZGRlZCBzZXJ2ZXJcbiAgICogfSk7XG4gICAqIGBgYFxuICAgKi9cbiAgc3RhdGljIGFzeW5jIG9wZW4ocGF0aE9yQ29uZmlnOiBzdHJpbmcgfCBEYXRhYmFzZUNvbmZpZyk6IFByb21pc2U8RGF0YWJhc2U+IHtcbiAgICBjb25zdCBjb25maWc6IERhdGFiYXNlQ29uZmlnID1cbiAgICAgIHR5cGVvZiBwYXRoT3JDb25maWcgPT09ICdzdHJpbmcnID8geyBwYXRoOiBwYXRoT3JDb25maWcgfSA6IHBhdGhPckNvbmZpZztcblxuICAgIC8vIEVuc3VyZSBkYXRhYmFzZSBkaXJlY3RvcnkgZXhpc3RzXG4gICAgaWYgKGNvbmZpZy5jcmVhdGVJZk1pc3NpbmcgIT09IGZhbHNlKSB7XG4gICAgICBpZiAoIWZzLmV4aXN0c1N5bmMoY29uZmlnLnBhdGgpKSB7XG4gICAgICAgIGZzLm1rZGlyU3luYyhjb25maWcucGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgZGIgPSBuZXcgRGF0YWJhc2UoY29uZmlnKTtcblxuICAgIC8vIFN0YXJ0IGVtYmVkZGVkIHNlcnZlciBpZiBjb25maWd1cmVkIChkZWZhdWx0OiB0cnVlKVxuICAgIGxldCBzb2NrZXRQYXRoOiBzdHJpbmc7XG4gICAgaWYgKGRiLl9jb25maWcuZW1iZWRkZWQgIT09IGZhbHNlKSB7XG4gICAgICAvLyBTdGFydCBlbWJlZGRlZCBzZXJ2ZXIgYW5kIGdldCBzb2NrZXQgcGF0aFxuICAgICAgc29ja2V0UGF0aCA9IGF3YWl0IHN0YXJ0RW1iZWRkZWRTZXJ2ZXIoY29uZmlnLnBhdGgpO1xuICAgICAgZGIuX2VtYmVkZGVkU2VydmVyU3RhcnRlZCA9IHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIENvbm5lY3QgdG8gZXhpc3Rpbmcgc2VydmVyIHNvY2tldFxuICAgICAgc29ja2V0UGF0aCA9IHBhdGguam9pbihjb25maWcucGF0aCwgJ3NvY2hkYi5zb2NrJyk7XG4gICAgfVxuXG4gICAgZGIuX2NsaWVudCA9IGF3YWl0IElwY0NsaWVudC5jb25uZWN0KHNvY2tldFBhdGgpO1xuXG4gICAgLy8gVHJhY2sgZGF0YWJhc2Ugb3BlbiBldmVudCAob25seSBhbmFseXRpY3MgZXZlbnQgd2Ugc2VuZClcbiAgICB0cnkge1xuICAgICAgY29uc3QgeyB0cmFja0RhdGFiYXNlT3BlbiB9ID0gYXdhaXQgaW1wb3J0KCcuL2FuYWx5dGljcy5qcycpO1xuICAgICAgYXdhaXQgdHJhY2tEYXRhYmFzZU9wZW4oY29uZmlnLnBhdGgsICdlbWJlZGRlZCcpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gTmV2ZXIgbGV0IGFuYWx5dGljcyBicmVhayBkYXRhYmFzZSBvcGVyYXRpb25zXG4gICAgfVxuXG4gICAgcmV0dXJuIGRiO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHZhbHVlIGJ5IGtleS5cbiAgICpcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gbG9vayB1cCAoQnVmZmVyIG9yIHN0cmluZylcbiAgICogQHJldHVybnMgVGhlIHZhbHVlIGFzIGEgQnVmZmVyLCBvciBudWxsIGlmIG5vdCBmb3VuZFxuICAgKi9cbiAgYXN5bmMgZ2V0KGtleTogQnVmZmVyIHwgc3RyaW5nKTogUHJvbWlzZTxCdWZmZXIgfCBudWxsPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuICAgIGNvbnN0IGtleUJ1ZiA9IHR5cGVvZiBrZXkgPT09ICdzdHJpbmcnID8gQnVmZmVyLmZyb20oa2V5KSA6IGtleTtcbiAgICByZXR1cm4gdGhpcy5fY2xpZW50IS5nZXQoa2V5QnVmKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQdXQgYSBrZXktdmFsdWUgcGFpci5cbiAgICpcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgKEJ1ZmZlciBvciBzdHJpbmcpXG4gICAqIEBwYXJhbSB2YWx1ZSAtIFRoZSB2YWx1ZSAoQnVmZmVyIG9yIHN0cmluZylcbiAgICovXG4gIGFzeW5jIHB1dChrZXk6IEJ1ZmZlciB8IHN0cmluZywgdmFsdWU6IEJ1ZmZlciB8IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcbiAgICBjb25zdCBrZXlCdWYgPSB0eXBlb2Yga2V5ID09PSAnc3RyaW5nJyA/IEJ1ZmZlci5mcm9tKGtleSkgOiBrZXk7XG4gICAgY29uc3QgdmFsdWVCdWYgPSB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gQnVmZmVyLmZyb20odmFsdWUpIDogdmFsdWU7XG4gICAgcmV0dXJuIHRoaXMuX2NsaWVudCEucHV0KGtleUJ1ZiwgdmFsdWVCdWYpO1xuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZSBhIGtleS5cbiAgICpcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gZGVsZXRlIChCdWZmZXIgb3Igc3RyaW5nKVxuICAgKi9cbiAgYXN5bmMgZGVsZXRlKGtleTogQnVmZmVyIHwgc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuICAgIGNvbnN0IGtleUJ1ZiA9IHR5cGVvZiBrZXkgPT09ICdzdHJpbmcnID8gQnVmZmVyLmZyb20oa2V5KSA6IGtleTtcbiAgICByZXR1cm4gdGhpcy5fY2xpZW50IS5kZWxldGUoa2V5QnVmKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYSB2YWx1ZSBieSBwYXRoLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aFN0ciAtIFRoZSBwYXRoIChlLmcuLCBcInVzZXJzL2FsaWNlL2VtYWlsXCIpXG4gICAqIEByZXR1cm5zIFRoZSB2YWx1ZSBhcyBhIEJ1ZmZlciwgb3IgbnVsbCBpZiBub3QgZm91bmRcbiAgICovXG4gIGFzeW5jIGdldFBhdGgocGF0aFN0cjogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXIgfCBudWxsPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuICAgIHJldHVybiB0aGlzLl9jbGllbnQhLmdldFBhdGgocGF0aFN0cik7XG4gIH1cblxuICAvKipcbiAgICogUHV0IGEgdmFsdWUgYXQgYSBwYXRoLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aFN0ciAtIFRoZSBwYXRoIChlLmcuLCBcInVzZXJzL2FsaWNlL2VtYWlsXCIpXG4gICAqIEBwYXJhbSB2YWx1ZSAtIFRoZSB2YWx1ZSAoQnVmZmVyIG9yIHN0cmluZylcbiAgICovXG4gIGFzeW5jIHB1dFBhdGgocGF0aFN0cjogc3RyaW5nLCB2YWx1ZTogQnVmZmVyIHwgc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuICAgIGNvbnN0IHZhbHVlQnVmID0gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IEJ1ZmZlci5mcm9tKHZhbHVlKSA6IHZhbHVlO1xuICAgIHJldHVybiB0aGlzLl9jbGllbnQhLnB1dFBhdGgocGF0aFN0ciwgdmFsdWVCdWYpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIHF1ZXJ5IGJ1aWxkZXIgZm9yIHRoZSBnaXZlbiBwYXRoIHByZWZpeC5cbiAgICpcbiAgICogQHBhcmFtIHBhdGhQcmVmaXggLSBUaGUgcGF0aCBwcmVmaXggdG8gcXVlcnkgKGUuZy4sIFwidXNlcnMvXCIpXG4gICAqIEByZXR1cm5zIEEgUXVlcnkgYnVpbGRlciBpbnN0YW5jZVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBkYi5xdWVyeSgndXNlcnMvJylcbiAgICogICAubGltaXQoMTApXG4gICAqICAgLnNlbGVjdChbJ25hbWUnLCAnZW1haWwnXSlcbiAgICogICAuZXhlY3V0ZSgpO1xuICAgKiBgYGBcbiAgICovXG4gIHF1ZXJ5KHBhdGhQcmVmaXg6IHN0cmluZyk6IFF1ZXJ5IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG4gICAgcmV0dXJuIG5ldyBRdWVyeSh0aGlzLl9jbGllbnQhLCBwYXRoUHJlZml4KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTY2FuIGZvciBrZXlzIHdpdGggYSBwcmVmaXgsIHJldHVybmluZyBrZXktdmFsdWUgcGFpcnMuXG4gICAqIFRoaXMgaXMgdGhlIHByZWZlcnJlZCBtZXRob2QgZm9yIHNpbXBsZSBwcmVmaXgtYmFzZWQgaXRlcmF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gcHJlZml4IC0gVGhlIHByZWZpeCB0byBzY2FuIGZvciAoZS5nLiwgXCJ1c2Vycy9cIiwgXCJ0ZW5hbnRzL3RlbmFudDEvXCIpXG4gICAqIEByZXR1cm5zIEFycmF5IG9mIGtleS12YWx1ZSBwYWlyc1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBkYi5zY2FuKCd0ZW5hbnRzL3RlbmFudDEvJyk7XG4gICAqIGZvciAoY29uc3QgeyBrZXksIHZhbHVlIH0gb2YgcmVzdWx0cykge1xuICAgKiAgIGNvbnNvbGUubG9nKGAke2tleS50b1N0cmluZygpfTogJHt2YWx1ZS50b1N0cmluZygpfWApO1xuICAgKiB9XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgc2NhbihwcmVmaXg6IHN0cmluZyk6IFByb21pc2U8QXJyYXk8eyBrZXk6IEJ1ZmZlcjsgdmFsdWU6IEJ1ZmZlciB9Pj4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcbiAgICByZXR1cm4gdGhpcy5fY2xpZW50IS5zY2FuKHByZWZpeCk7XG4gIH1cblxuICAvKipcbiAgICogU2NhbiBmb3Iga2V5cyB3aXRoIGEgcHJlZml4IHVzaW5nIGFuIGFzeW5jIGdlbmVyYXRvci5cbiAgICogVGhpcyBhbGxvd3MgZm9yIG1lbW9yeS1lZmZpY2llbnQgaXRlcmF0aW9uIG92ZXIgbGFyZ2UgcmVzdWx0IHNldHMuXG4gICAqXG4gICAqIEBwYXJhbSBwcmVmaXggLSBUaGUgcHJlZml4IHRvIHNjYW4gZm9yXG4gICAqIEBwYXJhbSBlbmQgLSBPcHRpb25hbCBlbmQgYm91bmRhcnkgKGV4Y2x1c2l2ZSlcbiAgICogQHJldHVybnMgQXN5bmMgZ2VuZXJhdG9yIHlpZWxkaW5nIFtrZXksIHZhbHVlXSB0dXBsZXNcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBmb3IgYXdhaXQgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBkYi5zY2FuR2VuZXJhdG9yKCd1c2Vycy8nKSkge1xuICAgKiAgIGNvbnNvbGUubG9nKGAke2tleS50b1N0cmluZygpfTogJHt2YWx1ZS50b1N0cmluZygpfWApO1xuICAgKiB9XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgKnNjYW5HZW5lcmF0b3IocHJlZml4OiBzdHJpbmcgfCBCdWZmZXIsIGVuZD86IHN0cmluZyB8IEJ1ZmZlcik6IEFzeW5jR2VuZXJhdG9yPFtCdWZmZXIsIEJ1ZmZlcl0+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG4gICAgY29uc3QgcHJlZml4QnVmID0gdHlwZW9mIHByZWZpeCA9PT0gJ3N0cmluZycgPyBCdWZmZXIuZnJvbShwcmVmaXgpIDogcHJlZml4O1xuICAgIGNvbnN0IGVuZEJ1ZiA9IGVuZCA/ICh0eXBlb2YgZW5kID09PSAnc3RyaW5nJyA/IEJ1ZmZlci5mcm9tKGVuZCkgOiBlbmQpIDogdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IHRoaXMuX2NsaWVudCEuc2NhbihwcmVmaXhCdWYudG9TdHJpbmcoKSk7XG4gICAgZm9yIChjb25zdCB7IGtleSwgdmFsdWUgfSBvZiByZXN1bHRzKSB7XG4gICAgICAvLyBGaWx0ZXIgYnkgZW5kIGJvdW5kYXJ5IGlmIHByb3ZpZGVkXG4gICAgICBpZiAoZW5kQnVmICYmIEJ1ZmZlci5jb21wYXJlKGtleSwgZW5kQnVmKSA+PSAwKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgeWllbGQgW2tleSwgdmFsdWVdO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlIG9wZXJhdGlvbnMgd2l0aGluIGEgdHJhbnNhY3Rpb24uXG4gICAqXG4gICAqIFRoZSB0cmFuc2FjdGlvbiBhdXRvbWF0aWNhbGx5IGNvbW1pdHMgb24gc3VjY2VzcyBvciBhYm9ydHMgb24gZXJyb3IuXG4gICAqXG4gICAqIEBwYXJhbSBmbiAtIEFzeW5jIGZ1bmN0aW9uIHRoYXQgcmVjZWl2ZXMgYSBUcmFuc2FjdGlvbiBvYmplY3RcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBhd2FpdCBkYi53aXRoVHJhbnNhY3Rpb24oYXN5bmMgKHR4bikgPT4ge1xuICAgKiAgIGF3YWl0IHR4bi5wdXQoQnVmZmVyLmZyb20oJ2tleTEnKSwgQnVmZmVyLmZyb20oJ3ZhbHVlMScpKTtcbiAgICogICBhd2FpdCB0eG4ucHV0KEJ1ZmZlci5mcm9tKCdrZXkyJyksIEJ1ZmZlci5mcm9tKCd2YWx1ZTInKSk7XG4gICAqICAgLy8gQXV0b21hdGljYWxseSBjb21taXRzXG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIHdpdGhUcmFuc2FjdGlvbjxUPihmbjogKHR4bjogVHJhbnNhY3Rpb24pID0+IFByb21pc2U8VD4pOiBQcm9taXNlPFQ+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG4gICAgY29uc3QgdHhuID0gbmV3IFRyYW5zYWN0aW9uKHRoaXMpO1xuICAgIGF3YWl0IHR4bi5iZWdpbigpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBmbih0eG4pO1xuICAgICAgYXdhaXQgdHhuLmNvbW1pdCgpO1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgYXdhaXQgdHhuLmFib3J0KCk7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IHRyYW5zYWN0aW9uLlxuICAgKlxuICAgKiBAcmV0dXJucyBBIG5ldyBUcmFuc2FjdGlvbiBpbnN0YW5jZVxuICAgKiBAZGVwcmVjYXRlZCBVc2Ugd2l0aFRyYW5zYWN0aW9uKCkgZm9yIGF1dG9tYXRpYyBjb21taXQvYWJvcnQgaGFuZGxpbmdcbiAgICovXG4gIGFzeW5jIHRyYW5zYWN0aW9uKCk6IFByb21pc2U8VHJhbnNhY3Rpb24+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG4gICAgY29uc3QgdHhuID0gbmV3IFRyYW5zYWN0aW9uKHRoaXMpO1xuICAgIGF3YWl0IHR4bi5iZWdpbigpO1xuICAgIHJldHVybiB0eG47XG4gIH1cblxuICAvKipcbiAgICogRm9yY2UgYSBjaGVja3BvaW50IHRvIHBlcnNpc3QgbWVtdGFibGUgdG8gZGlzay5cbiAgICovXG4gIGFzeW5jIGNoZWNrcG9pbnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuICAgIHJldHVybiB0aGlzLl9jbGllbnQhLmNoZWNrcG9pbnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgc3RvcmFnZSBzdGF0aXN0aWNzLlxuICAgKi9cbiAgYXN5bmMgc3RhdHMoKTogUHJvbWlzZTx7XG4gICAgbWVtdGFibGVTaXplQnl0ZXM6IG51bWJlcjtcbiAgICB3YWxTaXplQnl0ZXM6IG51bWJlcjtcbiAgICBhY3RpdmVUcmFuc2FjdGlvbnM6IG51bWJlcjtcbiAgfT4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcbiAgICByZXR1cm4gdGhpcy5fY2xpZW50IS5zdGF0cygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgYSBTUUwgcXVlcnkuXG4gICAqIFxuICAgKiBTb2NoREIgc3VwcG9ydHMgYSBzdWJzZXQgb2YgU1FMIGZvciByZWxhdGlvbmFsIGRhdGEgc3RvcmVkIG9uIHRvcCBvZiBcbiAgICogdGhlIGtleS12YWx1ZSBlbmdpbmUuIFRhYmxlcyBhbmQgcm93cyBhcmUgc3RvcmVkIGFzOlxuICAgKiAtIFNjaGVtYTogX3NxbC90YWJsZXMve3RhYmxlX25hbWV9L3NjaGVtYVxuICAgKiAtIFJvd3M6IF9zcWwvdGFibGVzL3t0YWJsZV9uYW1lfS9yb3dzL3tyb3dfaWR9XG4gICAqIFxuICAgKiBTdXBwb3J0ZWQgU1FMOlxuICAgKiAtIENSRUFURSBUQUJMRSB0YWJsZV9uYW1lIChjb2wxIFRZUEUsIGNvbDIgVFlQRSwgLi4uKVxuICAgKiAtIERST1AgVEFCTEUgdGFibGVfbmFtZVxuICAgKiAtIElOU0VSVCBJTlRPIHRhYmxlX25hbWUgKGNvbHMpIFZBTFVFUyAodmFscylcbiAgICogLSBTRUxFQ1QgY29scyBGUk9NIHRhYmxlX25hbWUgW1dIRVJFIC4uLl0gW09SREVSIEJZIC4uLl0gW0xJTUlUIC4uLl1cbiAgICogLSBVUERBVEUgdGFibGVfbmFtZSBTRVQgY29sPXZhbCBbV0hFUkUgLi4uXVxuICAgKiAtIERFTEVURSBGUk9NIHRhYmxlX25hbWUgW1dIRVJFIC4uLl1cbiAgICogXG4gICAqIFN1cHBvcnRlZCB0eXBlczogSU5ULCBURVhULCBGTE9BVCwgQk9PTCwgQkxPQlxuICAgKiBcbiAgICogQHBhcmFtIHNxbCAtIFNRTCBxdWVyeSBzdHJpbmdcbiAgICogQHJldHVybnMgU1FMUXVlcnlSZXN1bHQgd2l0aCByb3dzIGFuZCBtZXRhZGF0YVxuICAgKiBcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiAvLyBDcmVhdGUgYSB0YWJsZVxuICAgKiBhd2FpdCBkYi5leGVjdXRlKFwiQ1JFQVRFIFRBQkxFIHVzZXJzIChpZCBJTlQgUFJJTUFSWSBLRVksIG5hbWUgVEVYVCwgYWdlIElOVClcIik7XG4gICAqIFxuICAgKiAvLyBJbnNlcnQgZGF0YVxuICAgKiBhd2FpdCBkYi5leGVjdXRlKFwiSU5TRVJUIElOVE8gdXNlcnMgKGlkLCBuYW1lLCBhZ2UpIFZBTFVFUyAoMSwgJ0FsaWNlJywgMzApXCIpO1xuICAgKiBcbiAgICogLy8gUXVlcnkgZGF0YVxuICAgKiBjb25zdCByZXN1bHQgPSBhd2FpdCBkYi5leGVjdXRlKFwiU0VMRUNUICogRlJPTSB1c2VycyBXSEVSRSBhZ2UgPiAyNlwiKTtcbiAgICogcmVzdWx0LnJvd3MuZm9yRWFjaChyb3cgPT4gY29uc29sZS5sb2cocm93KSk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZShzcWw6IHN0cmluZyk6IFByb21pc2U8U1FMUXVlcnlSZXN1bHQ+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG5cbiAgICAvLyBJbXBvcnQgdGhlIFNRTCBleGVjdXRvclxuICAgIGNvbnN0IHsgU1FMRXhlY3V0b3IgfSA9IGF3YWl0IGltcG9ydCgnLi9zcWwtZW5naW5lLmpzJyk7XG5cbiAgICAvLyBDcmVhdGUgYSBkYXRhYmFzZSBhZGFwdGVyIGZvciB0aGUgU1FMIGV4ZWN1dG9yXG4gICAgY29uc3QgZGJBZGFwdGVyID0ge1xuICAgICAgZ2V0OiAoa2V5OiBCdWZmZXIgfCBzdHJpbmcpID0+IHRoaXMuZ2V0KGtleSksXG4gICAgICBwdXQ6IChrZXk6IEJ1ZmZlciB8IHN0cmluZywgdmFsdWU6IEJ1ZmZlciB8IHN0cmluZykgPT4gdGhpcy5wdXQoa2V5LCB2YWx1ZSksXG4gICAgICBkZWxldGU6IChrZXk6IEJ1ZmZlciB8IHN0cmluZykgPT4gdGhpcy5kZWxldGUoa2V5KSxcbiAgICAgIHNjYW46IChwcmVmaXg6IHN0cmluZykgPT4gdGhpcy5zY2FuKHByZWZpeCksXG4gICAgfTtcblxuICAgIGNvbnN0IGV4ZWN1dG9yID0gbmV3IFNRTEV4ZWN1dG9yKGRiQWRhcHRlcik7XG4gICAgcmV0dXJuIGV4ZWN1dG9yLmV4ZWN1dGUoc3FsKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gU3RhdGljIFNlcmlhbGl6YXRpb24gTWV0aG9kc1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgcmVjb3JkcyB0byBUT09OIGZvcm1hdCBmb3IgdG9rZW4tZWZmaWNpZW50IExMTSBjb250ZXh0LlxuICAgKiBcbiAgICogVE9PTiBmb3JtYXQgYWNoaWV2ZXMgNDAtNjYlIHRva2VuIHJlZHVjdGlvbiBjb21wYXJlZCB0byBKU09OIGJ5IHVzaW5nXG4gICAqIGEgY29sdW1uYXIgdGV4dCBmb3JtYXQgd2l0aCBtaW5pbWFsIHN5bnRheC5cbiAgICogXG4gICAqIEBwYXJhbSB0YWJsZU5hbWUgLSBOYW1lIG9mIHRoZSB0YWJsZS9jb2xsZWN0aW9uXG4gICAqIEBwYXJhbSByZWNvcmRzIC0gQXJyYXkgb2Ygb2JqZWN0cyB3aXRoIHRoZSBkYXRhXG4gICAqIEBwYXJhbSBmaWVsZHMgLSBPcHRpb25hbCBhcnJheSBvZiBmaWVsZCBuYW1lcyB0byBpbmNsdWRlXG4gICAqIEByZXR1cm5zIFRPT04tZm9ybWF0dGVkIHN0cmluZ1xuICAgKiBcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZWNvcmRzID0gW1xuICAgKiAgIHsgaWQ6IDEsIG5hbWU6ICdBbGljZScsIGVtYWlsOiAnYWxpY2VAZXguY29tJyB9LFxuICAgKiAgIHsgaWQ6IDIsIG5hbWU6ICdCb2InLCBlbWFpbDogJ2JvYkBleC5jb20nIH1cbiAgICogXTtcbiAgICogY29uc29sZS5sb2coRGF0YWJhc2UudG9Ub29uKCd1c2VycycsIHJlY29yZHMsIFsnbmFtZScsICdlbWFpbCddKSk7XG4gICAqIC8vIHVzZXJzWzJde25hbWUsZW1haWx9OkFsaWNlLGFsaWNlQGV4LmNvbTtCb2IsYm9iQGV4LmNvbVxuICAgKiBgYGBcbiAgICovXG4gIHN0YXRpYyB0b1Rvb24oXG4gICAgdGFibGVOYW1lOiBzdHJpbmcsXG4gICAgcmVjb3JkczogQXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4sXG4gICAgZmllbGRzPzogc3RyaW5nW11cbiAgKTogc3RyaW5nIHtcbiAgICBpZiAoIXJlY29yZHMgfHwgcmVjb3Jkcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBgJHt0YWJsZU5hbWV9WzBde306YDtcbiAgICB9XG5cbiAgICAvLyBEZXRlcm1pbmUgZmllbGRzIGZyb20gZmlyc3QgcmVjb3JkIGlmIG5vdCBzcGVjaWZpZWRcbiAgICBjb25zdCB1c2VGaWVsZHMgPSBmaWVsZHMgPz8gT2JqZWN0LmtleXMocmVjb3Jkc1swXSk7XG5cbiAgICAvLyBCdWlsZCBoZWFkZXI6IHRhYmxlW2NvdW50XXtmaWVsZDEsZmllbGQyLC4uLn06XG4gICAgY29uc3QgaGVhZGVyID0gYCR7dGFibGVOYW1lfVske3JlY29yZHMubGVuZ3RofV17JHt1c2VGaWVsZHMuam9pbignLCcpfX06YDtcblxuICAgIC8vIEVzY2FwZSB2YWx1ZXMgY29udGFpbmluZyBkZWxpbWl0ZXJzXG4gICAgY29uc3QgZXNjYXBlVmFsdWUgPSAodjogYW55KTogc3RyaW5nID0+IHtcbiAgICAgIGNvbnN0IHMgPSB2ICE9IG51bGwgPyBTdHJpbmcodikgOiAnJztcbiAgICAgIGlmIChzLmluY2x1ZGVzKCcsJykgfHwgcy5pbmNsdWRlcygnOycpIHx8IHMuaW5jbHVkZXMoJ1xcbicpKSB7XG4gICAgICAgIHJldHVybiBgXCIke3N9XCJgO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHM7XG4gICAgfTtcblxuICAgIC8vIEJ1aWxkIHJvd3M6IHZhbHVlMSx2YWx1ZTI7dmFsdWUxLHZhbHVlMjsuLi5cbiAgICBjb25zdCByb3dzID0gcmVjb3Jkc1xuICAgICAgLm1hcChyID0+IHVzZUZpZWxkcy5tYXAoZiA9PiBlc2NhcGVWYWx1ZShyW2ZdKSkuam9pbignLCcpKVxuICAgICAgLmpvaW4oJzsnKTtcblxuICAgIHJldHVybiBoZWFkZXIgKyByb3dzO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgcmVjb3JkcyB0byBKU09OIGZvcm1hdCBmb3IgZWFzeSBhcHBsaWNhdGlvbiBkZWNvZGluZy5cbiAgICogXG4gICAqIFdoaWxlIFRPT04gZm9ybWF0IGlzIG9wdGltaXplZCBmb3IgTExNIGNvbnRleHQgKDQwLTY2JSB0b2tlbiByZWR1Y3Rpb24pLFxuICAgKiBKU09OIGlzIG9mdGVuIGVhc2llciBmb3IgYXBwbGljYXRpb25zIHRvIHBhcnNlLiBVc2UgdGhpcyBtZXRob2Qgd2hlblxuICAgKiB0aGUgb3V0cHV0IHdpbGwgYmUgY29uc3VtZWQgYnkgYXBwbGljYXRpb24gY29kZSByYXRoZXIgdGhhbiBMTE1zLlxuICAgKiBcbiAgICogQHBhcmFtIHRhYmxlTmFtZSAtIE5hbWUgb2YgdGhlIHRhYmxlL2NvbGxlY3Rpb25cbiAgICogQHBhcmFtIHJlY29yZHMgLSBBcnJheSBvZiBvYmplY3RzIHdpdGggdGhlIGRhdGFcbiAgICogQHBhcmFtIGZpZWxkcyAtIE9wdGlvbmFsIGFycmF5IG9mIGZpZWxkIG5hbWVzIHRvIGluY2x1ZGVcbiAgICogQHBhcmFtIGNvbXBhY3QgLSBJZiB0cnVlIChkZWZhdWx0KSwgb3V0cHV0cyBtaW5pZmllZCBKU09OXG4gICAqIEByZXR1cm5zIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xuICAgKiBcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZWNvcmRzID0gW1xuICAgKiAgIHsgaWQ6IDEsIG5hbWU6ICdBbGljZScgfSxcbiAgICogICB7IGlkOiAyLCBuYW1lOiAnQm9iJyB9XG4gICAqIF07XG4gICAqIGNvbnNvbGUubG9nKERhdGFiYXNlLnRvSnNvbigndXNlcnMnLCByZWNvcmRzKSk7XG4gICAqIC8vIHtcInRhYmxlXCI6XCJ1c2Vyc1wiLFwiY291bnRcIjoyLFwicmVjb3Jkc1wiOlt7XCJpZFwiOjEsXCJuYW1lXCI6XCJBbGljZVwifSx7XCJpZFwiOjIsXCJuYW1lXCI6XCJCb2JcIn1dfVxuICAgKiBgYGBcbiAgICovXG4gIHN0YXRpYyB0b0pzb24oXG4gICAgdGFibGVOYW1lOiBzdHJpbmcsXG4gICAgcmVjb3JkczogQXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4sXG4gICAgZmllbGRzPzogc3RyaW5nW10sXG4gICAgY29tcGFjdDogYm9vbGVhbiA9IHRydWVcbiAgKTogc3RyaW5nIHtcbiAgICBpZiAoIXJlY29yZHMgfHwgcmVjb3Jkcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh7IHRhYmxlOiB0YWJsZU5hbWUsIGNvdW50OiAwLCByZWNvcmRzOiBbXSB9KTtcbiAgICB9XG5cbiAgICAvLyBGaWx0ZXIgZmllbGRzIGlmIHNwZWNpZmllZFxuICAgIGNvbnN0IGZpbHRlcmVkUmVjb3JkcyA9IGZpZWxkc1xuICAgICAgPyByZWNvcmRzLm1hcChyID0+IHtcbiAgICAgICAgY29uc3QgZmlsdGVyZWQ6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCBmIG9mIGZpZWxkcykge1xuICAgICAgICAgIGZpbHRlcmVkW2ZdID0gcltmXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmlsdGVyZWQ7XG4gICAgICB9KVxuICAgICAgOiByZWNvcmRzO1xuXG4gICAgY29uc3Qgb3V0cHV0ID0ge1xuICAgICAgdGFibGU6IHRhYmxlTmFtZSxcbiAgICAgIGNvdW50OiBmaWx0ZXJlZFJlY29yZHMubGVuZ3RoLFxuICAgICAgcmVjb3JkczogZmlsdGVyZWRSZWNvcmRzLFxuICAgIH07XG5cbiAgICByZXR1cm4gY29tcGFjdCA/IEpTT04uc3RyaW5naWZ5KG91dHB1dCkgOiBKU09OLnN0cmluZ2lmeShvdXRwdXQsIG51bGwsIDIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlIGEgSlNPTiBmb3JtYXQgc3RyaW5nIGJhY2sgdG8gc3RydWN0dXJlZCBkYXRhLlxuICAgKiBcbiAgICogQHBhcmFtIGpzb25TdHIgLSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgKGZyb20gdG9Kc29uKVxuICAgKiBAcmV0dXJucyBPYmplY3Qgd2l0aCB0YWJsZSwgZmllbGRzLCBhbmQgcmVjb3Jkc1xuICAgKi9cbiAgc3RhdGljIGZyb21Kc29uKGpzb25TdHI6IHN0cmluZyk6IHtcbiAgICB0YWJsZTogc3RyaW5nO1xuICAgIGZpZWxkczogc3RyaW5nW107XG4gICAgcmVjb3JkczogQXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj47XG4gIH0ge1xuICAgIGNvbnN0IGRhdGEgPSBKU09OLnBhcnNlKGpzb25TdHIpO1xuICAgIGNvbnN0IHRhYmxlID0gZGF0YS50YWJsZSA/PyAndW5rbm93bic7XG4gICAgY29uc3QgcmVjb3JkcyA9IGRhdGEucmVjb3JkcyA/PyBbXTtcbiAgICBjb25zdCBmaWVsZHMgPSByZWNvcmRzLmxlbmd0aCA+IDAgPyBPYmplY3Qua2V5cyhyZWNvcmRzWzBdKSA6IFtdO1xuXG4gICAgcmV0dXJuIHsgdGFibGUsIGZpZWxkcywgcmVjb3JkcyB9O1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBHcmFwaCBPdmVybGF5IE9wZXJhdGlvbnNcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBBZGQgYSBub2RlIHRvIHRoZSBncmFwaCBvdmVybGF5LlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZXNwYWNlIC0gTmFtZXNwYWNlIGZvciB0aGUgZ3JhcGhcbiAgICogQHBhcmFtIG5vZGVJZCAtIFVuaXF1ZSBub2RlIGlkZW50aWZpZXJcbiAgICogQHBhcmFtIG5vZGVUeXBlIC0gVHlwZSBvZiBub2RlIChlLmcuLCBcInBlcnNvblwiLCBcImRvY3VtZW50XCIsIFwiY29uY2VwdFwiKVxuICAgKiBAcGFyYW0gcHJvcGVydGllcyAtIE9wdGlvbmFsIG5vZGUgcHJvcGVydGllc1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGF3YWl0IGRiLmFkZE5vZGUoJ2RlZmF1bHQnLCAnYWxpY2UnLCAncGVyc29uJywgeyByb2xlOiAnZW5naW5lZXInIH0pO1xuICAgKiBhd2FpdCBkYi5hZGROb2RlKCdkZWZhdWx0JywgJ3Byb2plY3RfeCcsICdwcm9qZWN0JywgeyBzdGF0dXM6ICdhY3RpdmUnIH0pO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGFkZE5vZGUoXG4gICAgbmFtZXNwYWNlOiBzdHJpbmcsXG4gICAgbm9kZUlkOiBzdHJpbmcsXG4gICAgbm9kZVR5cGU6IHN0cmluZyxcbiAgICBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPlxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG5cbiAgICBjb25zdCBrZXkgPSBgX2dyYXBoLyR7bmFtZXNwYWNlfS9ub2Rlcy8ke25vZGVJZH1gO1xuICAgIGNvbnN0IHZhbHVlID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgaWQ6IG5vZGVJZCxcbiAgICAgIG5vZGVfdHlwZTogbm9kZVR5cGUsXG4gICAgICBwcm9wZXJ0aWVzOiBwcm9wZXJ0aWVzIHx8IHt9LFxuICAgIH0pO1xuXG4gICAgYXdhaXQgdGhpcy5wdXQoQnVmZmVyLmZyb20oa2V5KSwgQnVmZmVyLmZyb20odmFsdWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgYW4gZWRnZSBiZXR3ZWVuIG5vZGVzIGluIHRoZSBncmFwaCBvdmVybGF5LlxuICAgKlxuICAgKiBAcGFyYW0gbmFtZXNwYWNlIC0gTmFtZXNwYWNlIGZvciB0aGUgZ3JhcGhcbiAgICogQHBhcmFtIGZyb21JZCAtIFNvdXJjZSBub2RlIElEXG4gICAqIEBwYXJhbSBlZGdlVHlwZSAtIFR5cGUgb2YgcmVsYXRpb25zaGlwXG4gICAqIEBwYXJhbSB0b0lkIC0gVGFyZ2V0IG5vZGUgSURcbiAgICogQHBhcmFtIHByb3BlcnRpZXMgLSBPcHRpb25hbCBlZGdlIHByb3BlcnRpZXNcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBhd2FpdCBkYi5hZGRFZGdlKCdkZWZhdWx0JywgJ2FsaWNlJywgJ3dvcmtzX29uJywgJ3Byb2plY3RfeCcpO1xuICAgKiBhd2FpdCBkYi5hZGRFZGdlKCdkZWZhdWx0JywgJ2FsaWNlJywgJ2tub3dzJywgJ2JvYicsIHsgc2luY2U6ICcyMDIwJyB9KTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBhZGRFZGdlKFxuICAgIG5hbWVzcGFjZTogc3RyaW5nLFxuICAgIGZyb21JZDogc3RyaW5nLFxuICAgIGVkZ2VUeXBlOiBzdHJpbmcsXG4gICAgdG9JZDogc3RyaW5nLFxuICAgIHByb3BlcnRpZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+XG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcblxuICAgIGNvbnN0IGtleSA9IGBfZ3JhcGgvJHtuYW1lc3BhY2V9L2VkZ2VzLyR7ZnJvbUlkfS8ke2VkZ2VUeXBlfS8ke3RvSWR9YDtcbiAgICBjb25zdCB2YWx1ZSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIGZyb21faWQ6IGZyb21JZCxcbiAgICAgIGVkZ2VfdHlwZTogZWRnZVR5cGUsXG4gICAgICB0b19pZDogdG9JZCxcbiAgICAgIHByb3BlcnRpZXM6IHByb3BlcnRpZXMgfHwge30sXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLnB1dChCdWZmZXIuZnJvbShrZXkpLCBCdWZmZXIuZnJvbSh2YWx1ZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYXZlcnNlIHRoZSBncmFwaCBmcm9tIGEgc3RhcnRpbmcgbm9kZS5cbiAgICpcbiAgICogQHBhcmFtIG5hbWVzcGFjZSAtIE5hbWVzcGFjZSBmb3IgdGhlIGdyYXBoXG4gICAqIEBwYXJhbSBzdGFydE5vZGUgLSBOb2RlIElEIHRvIHN0YXJ0IHRyYXZlcnNhbCBmcm9tXG4gICAqIEBwYXJhbSBtYXhEZXB0aCAtIE1heGltdW0gdHJhdmVyc2FsIGRlcHRoIChkZWZhdWx0OiAxMClcbiAgICogQHBhcmFtIG9yZGVyIC0gXCJiZnNcIiBmb3IgYnJlYWR0aC1maXJzdCwgXCJkZnNcIiBmb3IgZGVwdGgtZmlyc3RcbiAgICogQHJldHVybnMgT2JqZWN0IHdpdGggbm9kZXMgYW5kIGVkZ2VzIGFycmF5c1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHsgbm9kZXMsIGVkZ2VzIH0gPSBhd2FpdCBkYi50cmF2ZXJzZSgnZGVmYXVsdCcsICdhbGljZScsIDIpO1xuICAgKiBmb3IgKGNvbnN0IG5vZGUgb2Ygbm9kZXMpIHtcbiAgICogICBjb25zb2xlLmxvZyhgTm9kZTogJHtub2RlLmlkfSAoJHtub2RlLm5vZGVfdHlwZX0pYCk7XG4gICAqIH1cbiAgICogYGBgXG4gICAqL1xuICBhc3luYyB0cmF2ZXJzZShcbiAgICBuYW1lc3BhY2U6IHN0cmluZyxcbiAgICBzdGFydE5vZGU6IHN0cmluZyxcbiAgICBtYXhEZXB0aDogbnVtYmVyID0gMTAsXG4gICAgb3JkZXI6ICdiZnMnIHwgJ2RmcycgPSAnYmZzJ1xuICApOiBQcm9taXNlPHsgbm9kZXM6IGFueVtdOyBlZGdlczogYW55W10gfT4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcblxuICAgIGNvbnN0IHZpc2l0ZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBjb25zdCBub2RlczogYW55W10gPSBbXTtcbiAgICBjb25zdCBlZGdlczogYW55W10gPSBbXTtcbiAgICBjb25zdCBmcm9udGllcjogQXJyYXk8W3N0cmluZywgbnVtYmVyXT4gPSBbW3N0YXJ0Tm9kZSwgMF1dO1xuXG4gICAgd2hpbGUgKGZyb250aWVyLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IFtjdXJyZW50Tm9kZSwgZGVwdGhdID0gb3JkZXIgPT09ICdiZnMnXG4gICAgICAgID8gZnJvbnRpZXIuc2hpZnQoKSFcbiAgICAgICAgOiBmcm9udGllci5wb3AoKSE7XG5cbiAgICAgIGlmIChkZXB0aCA+IG1heERlcHRoIHx8IHZpc2l0ZWQuaGFzKGN1cnJlbnROb2RlKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHZpc2l0ZWQuYWRkKGN1cnJlbnROb2RlKTtcblxuICAgICAgLy8gR2V0IG5vZGUgZGF0YVxuICAgICAgY29uc3Qgbm9kZUtleSA9IGBfZ3JhcGgvJHtuYW1lc3BhY2V9L25vZGVzLyR7Y3VycmVudE5vZGV9YDtcbiAgICAgIGNvbnN0IG5vZGVEYXRhID0gYXdhaXQgdGhpcy5nZXQoQnVmZmVyLmZyb20obm9kZUtleSkpO1xuICAgICAgaWYgKG5vZGVEYXRhKSB7XG4gICAgICAgIG5vZGVzLnB1c2goSlNPTi5wYXJzZShub2RlRGF0YS50b1N0cmluZygpKSk7XG4gICAgICB9XG5cbiAgICAgIC8vIEdldCBvdXRnb2luZyBlZGdlc1xuICAgICAgY29uc3QgZWRnZVByZWZpeCA9IGBfZ3JhcGgvJHtuYW1lc3BhY2V9L2VkZ2VzLyR7Y3VycmVudE5vZGV9L2A7XG4gICAgICBjb25zdCBlZGdlUmVzdWx0cyA9IGF3YWl0IHRoaXMuc2NhbihlZGdlUHJlZml4KTtcbiAgICAgIGZvciAoY29uc3QgeyB2YWx1ZSB9IG9mIGVkZ2VSZXN1bHRzKSB7XG4gICAgICAgIGNvbnN0IGVkZ2UgPSBKU09OLnBhcnNlKHZhbHVlLnRvU3RyaW5nKCkpO1xuICAgICAgICBlZGdlcy5wdXNoKGVkZ2UpO1xuXG4gICAgICAgIGlmICghdmlzaXRlZC5oYXMoZWRnZS50b19pZCkpIHtcbiAgICAgICAgICBmcm9udGllci5wdXNoKFtlZGdlLnRvX2lkLCBkZXB0aCArIDFdKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IG5vZGVzLCBlZGdlcyB9O1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBTZW1hbnRpYyBDYWNoZSBPcGVyYXRpb25zXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogU3RvcmUgYSB2YWx1ZSBpbiB0aGUgc2VtYW50aWMgY2FjaGUgd2l0aCBpdHMgZW1iZWRkaW5nLlxuICAgKlxuICAgKiBAcGFyYW0gY2FjaGVOYW1lIC0gTmFtZSBvZiB0aGUgY2FjaGVcbiAgICogQHBhcmFtIGtleSAtIENhY2hlIGtleSAoZm9yIGRpc3BsYXkvZGVidWdnaW5nKVxuICAgKiBAcGFyYW0gdmFsdWUgLSBWYWx1ZSB0byBjYWNoZVxuICAgKiBAcGFyYW0gZW1iZWRkaW5nIC0gRW1iZWRkaW5nIHZlY3RvciBmb3Igc2ltaWxhcml0eSBtYXRjaGluZ1xuICAgKiBAcGFyYW0gdHRsU2Vjb25kcyAtIFRpbWUtdG8tbGl2ZSBpbiBzZWNvbmRzICgwID0gbm8gZXhwaXJ5KVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGF3YWl0IGRiLmNhY2hlUHV0KFxuICAgKiAgICdsbG1fcmVzcG9uc2VzJyxcbiAgICogICAnV2hhdCBpcyBQeXRob24/JyxcbiAgICogICAnUHl0aG9uIGlzIGEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UuLi4nLFxuICAgKiAgIFswLjEsIDAuMiwgMC4zLCAuLi5dLCAgLy8gMzg0LWRpbVxuICAgKiAgIDM2MDBcbiAgICogKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBjYWNoZVB1dChcbiAgICBjYWNoZU5hbWU6IHN0cmluZyxcbiAgICBrZXk6IHN0cmluZyxcbiAgICB2YWx1ZTogc3RyaW5nLFxuICAgIGVtYmVkZGluZzogbnVtYmVyW10sXG4gICAgdHRsU2Vjb25kczogbnVtYmVyID0gMFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG5cbiAgICAvLyBIYXNoIHRoZSBrZXkgZm9yIHN0b3JhZ2VcbiAgICBjb25zdCBrZXlIYXNoID0gQnVmZmVyLmZyb20oa2V5KS50b1N0cmluZygnaGV4Jykuc2xpY2UoMCwgMTYpO1xuICAgIGNvbnN0IGNhY2hlS2V5ID0gYF9jYWNoZS8ke2NhY2hlTmFtZX0vJHtrZXlIYXNofWA7XG5cbiAgICBjb25zdCBleHBpcmVzQXQgPSB0dGxTZWNvbmRzID4gMFxuICAgICAgPyBNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKSArIHR0bFNlY29uZHNcbiAgICAgIDogMDtcblxuICAgIGNvbnN0IGNhY2hlVmFsdWUgPSBKU09OLnN0cmluZ2lmeSh7XG4gICAgICBrZXksXG4gICAgICB2YWx1ZSxcbiAgICAgIGVtYmVkZGluZyxcbiAgICAgIGV4cGlyZXNfYXQ6IGV4cGlyZXNBdCxcbiAgICB9KTtcblxuICAgIGF3YWl0IHRoaXMucHV0KEJ1ZmZlci5mcm9tKGNhY2hlS2V5KSwgQnVmZmVyLmZyb20oY2FjaGVWYWx1ZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIExvb2sgdXAgYSB2YWx1ZSBpbiB0aGUgc2VtYW50aWMgY2FjaGUgYnkgZW1iZWRkaW5nIHNpbWlsYXJpdHkuXG4gICAqXG4gICAqIEBwYXJhbSBjYWNoZU5hbWUgLSBOYW1lIG9mIHRoZSBjYWNoZVxuICAgKiBAcGFyYW0gcXVlcnlFbWJlZGRpbmcgLSBRdWVyeSBlbWJlZGRpbmcgdG8gbWF0Y2ggYWdhaW5zdFxuICAgKiBAcGFyYW0gdGhyZXNob2xkIC0gTWluaW11bSBjb3NpbmUgc2ltaWxhcml0eSB0aHJlc2hvbGQgKDAuMCB0byAxLjApXG4gICAqIEByZXR1cm5zIENhY2hlZCB2YWx1ZSBpZiBzaW1pbGFyaXR5ID49IHRocmVzaG9sZCwgbnVsbCBvdGhlcndpc2VcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZXN1bHQgPSBhd2FpdCBkYi5jYWNoZUdldChcbiAgICogICAnbGxtX3Jlc3BvbnNlcycsXG4gICAqICAgWzAuMTIsIDAuMTgsIC4uLl0sICAvLyBTaW1pbGFyIHRvIFwiV2hhdCBpcyBQeXRob24/XCJcbiAgICogICAwLjg1XG4gICAqICk7XG4gICAqIGlmIChyZXN1bHQpIHtcbiAgICogICBjb25zb2xlLmxvZyhgQ2FjaGUgaGl0OiAke3Jlc3VsdH1gKTtcbiAgICogfVxuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGNhY2hlR2V0KFxuICAgIGNhY2hlTmFtZTogc3RyaW5nLFxuICAgIHF1ZXJ5RW1iZWRkaW5nOiBudW1iZXJbXSxcbiAgICB0aHJlc2hvbGQ6IG51bWJlciA9IDAuODVcbiAgKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgdGhpcy5fZW5zdXJlT3BlbigpO1xuXG4gICAgY29uc3QgcHJlZml4ID0gYF9jYWNoZS8ke2NhY2hlTmFtZX0vYDtcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgdGhpcy5zY2FuKHByZWZpeCk7XG5cbiAgICBjb25zdCBub3cgPSBNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKTtcbiAgICBsZXQgYmVzdE1hdGNoOiB7IHNpbWlsYXJpdHk6IG51bWJlcjsgdmFsdWU6IHN0cmluZyB9IHwgbnVsbCA9IG51bGw7XG5cbiAgICBmb3IgKGNvbnN0IHsgdmFsdWUgfSBvZiBlbnRyaWVzKSB7XG4gICAgICBjb25zdCBlbnRyeSA9IEpTT04ucGFyc2UodmFsdWUudG9TdHJpbmcoKSk7XG5cbiAgICAgIC8vIENoZWNrIGV4cGlyeVxuICAgICAgaWYgKGVudHJ5LmV4cGlyZXNfYXQgPiAwICYmIG5vdyA+IGVudHJ5LmV4cGlyZXNfYXQpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIENvbXB1dGUgY29zaW5lIHNpbWlsYXJpdHlcbiAgICAgIGlmIChlbnRyeS5lbWJlZGRpbmcgJiYgZW50cnkuZW1iZWRkaW5nLmxlbmd0aCA9PT0gcXVlcnlFbWJlZGRpbmcubGVuZ3RoKSB7XG4gICAgICAgIGNvbnN0IHNpbWlsYXJpdHkgPSB0aGlzLl9jb3NpbmVTaW1pbGFyaXR5KHF1ZXJ5RW1iZWRkaW5nLCBlbnRyeS5lbWJlZGRpbmcpO1xuICAgICAgICBpZiAoc2ltaWxhcml0eSA+PSB0aHJlc2hvbGQpIHtcbiAgICAgICAgICBpZiAoIWJlc3RNYXRjaCB8fCBzaW1pbGFyaXR5ID4gYmVzdE1hdGNoLnNpbWlsYXJpdHkpIHtcbiAgICAgICAgICAgIGJlc3RNYXRjaCA9IHsgc2ltaWxhcml0eSwgdmFsdWU6IGVudHJ5LnZhbHVlIH07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGJlc3RNYXRjaD8udmFsdWUgPz8gbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgX2Nvc2luZVNpbWlsYXJpdHkoYTogbnVtYmVyW10sIGI6IG51bWJlcltdKTogbnVtYmVyIHtcbiAgICBsZXQgZG90ID0gMCwgbm9ybUEgPSAwLCBub3JtQiA9IDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhLmxlbmd0aDsgaSsrKSB7XG4gICAgICBkb3QgKz0gYVtpXSAqIGJbaV07XG4gICAgICBub3JtQSArPSBhW2ldICogYVtpXTtcbiAgICAgIG5vcm1CICs9IGJbaV0gKiBiW2ldO1xuICAgIH1cbiAgICBub3JtQSA9IE1hdGguc3FydChub3JtQSk7XG4gICAgbm9ybUIgPSBNYXRoLnNxcnQobm9ybUIpO1xuICAgIHJldHVybiBub3JtQSA9PT0gMCB8fCBub3JtQiA9PT0gMCA/IDAgOiBkb3QgLyAobm9ybUEgKiBub3JtQik7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFRyYWNlIE9wZXJhdGlvbnMgKE9ic2VydmFiaWxpdHkpXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogU3RhcnQgYSBuZXcgdHJhY2UuXG4gICAqXG4gICAqIEBwYXJhbSBuYW1lIC0gTmFtZSBvZiB0aGUgdHJhY2UgKGUuZy4sIFwidXNlcl9yZXF1ZXN0XCIsIFwiYmF0Y2hfam9iXCIpXG4gICAqIEByZXR1cm5zIE9iamVjdCB3aXRoIHRyYWNlSWQgYW5kIHJvb3RTcGFuSWRcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCB7IHRyYWNlSWQsIHJvb3RTcGFuSWQgfSA9IGF3YWl0IGRiLnN0YXJ0VHJhY2UoJ3VzZXJfcXVlcnknKTtcbiAgICogLy8gLi4uIGRvIHdvcmsgLi4uXG4gICAqIGF3YWl0IGRiLmVuZFNwYW4odHJhY2VJZCwgcm9vdFNwYW5JZCwgJ29rJyk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgc3RhcnRUcmFjZShuYW1lOiBzdHJpbmcpOiBQcm9taXNlPHsgdHJhY2VJZDogc3RyaW5nOyByb290U3BhbklkOiBzdHJpbmcgfT4ge1xuICAgIHRoaXMuX2Vuc3VyZU9wZW4oKTtcblxuICAgIGNvbnN0IHRyYWNlSWQgPSBgdHJhY2VfJHtEYXRlLm5vdygpLnRvU3RyaW5nKDE2KX0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMTYpLnNsaWNlKDIsIDEwKX1gO1xuICAgIGNvbnN0IHNwYW5JZCA9IGBzcGFuXyR7RGF0ZS5ub3coKS50b1N0cmluZygxNil9JHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDE2KS5zbGljZSgyLCAxMCl9YDtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpICogMTAwMDsgLy8gbWljcm9zZWNvbmRzXG5cbiAgICAvLyBTdG9yZSB0cmFjZVxuICAgIGNvbnN0IHRyYWNlS2V5ID0gYF90cmFjZXMvJHt0cmFjZUlkfWA7XG4gICAgY29uc3QgdHJhY2VWYWx1ZSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIHRyYWNlX2lkOiB0cmFjZUlkLFxuICAgICAgbmFtZSxcbiAgICAgIHN0YXJ0X3VzOiBub3csXG4gICAgICByb290X3NwYW5faWQ6IHNwYW5JZCxcbiAgICB9KTtcbiAgICBhd2FpdCB0aGlzLnB1dChCdWZmZXIuZnJvbSh0cmFjZUtleSksIEJ1ZmZlci5mcm9tKHRyYWNlVmFsdWUpKTtcblxuICAgIC8vIFN0b3JlIHJvb3Qgc3BhblxuICAgIGNvbnN0IHNwYW5LZXkgPSBgX3RyYWNlcy8ke3RyYWNlSWR9L3NwYW5zLyR7c3BhbklkfWA7XG4gICAgY29uc3Qgc3BhblZhbHVlID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgc3Bhbl9pZDogc3BhbklkLFxuICAgICAgbmFtZSxcbiAgICAgIHN0YXJ0X3VzOiBub3csXG4gICAgICBwYXJlbnRfc3Bhbl9pZDogbnVsbCxcbiAgICAgIHN0YXR1czogJ2FjdGl2ZScsXG4gICAgfSk7XG4gICAgYXdhaXQgdGhpcy5wdXQoQnVmZmVyLmZyb20oc3BhbktleSksIEJ1ZmZlci5mcm9tKHNwYW5WYWx1ZSkpO1xuXG4gICAgcmV0dXJuIHsgdHJhY2VJZCwgcm9vdFNwYW5JZDogc3BhbklkIH07XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgYSBjaGlsZCBzcGFuIHdpdGhpbiBhIHRyYWNlLlxuICAgKlxuICAgKiBAcGFyYW0gdHJhY2VJZCAtIElEIG9mIHRoZSBwYXJlbnQgdHJhY2VcbiAgICogQHBhcmFtIHBhcmVudFNwYW5JZCAtIElEIG9mIHRoZSBwYXJlbnQgc3BhblxuICAgKiBAcGFyYW0gbmFtZSAtIE5hbWUgb2YgdGhpcyBzcGFuXG4gICAqIEByZXR1cm5zIFRoZSBuZXcgc3BhbiBJRFxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHsgdHJhY2VJZCwgcm9vdFNwYW5JZCB9ID0gYXdhaXQgZGIuc3RhcnRUcmFjZSgndXNlcl9xdWVyeScpO1xuICAgKiBjb25zdCBkYlNwYW4gPSBhd2FpdCBkYi5zdGFydFNwYW4odHJhY2VJZCwgcm9vdFNwYW5JZCwgJ2RhdGFiYXNlX2xvb2t1cCcpO1xuICAgKiAvLyAuLi4gZG8gZGF0YWJhc2Ugd29yayAuLi5cbiAgICogY29uc3QgZHVyYXRpb24gPSBhd2FpdCBkYi5lbmRTcGFuKHRyYWNlSWQsIGRiU3BhbiwgJ29rJyk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgc3RhcnRTcGFuKFxuICAgIHRyYWNlSWQ6IHN0cmluZyxcbiAgICBwYXJlbnRTcGFuSWQ6IHN0cmluZyxcbiAgICBuYW1lOiBzdHJpbmdcbiAgKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG5cbiAgICBjb25zdCBzcGFuSWQgPSBgc3Bhbl8ke0RhdGUubm93KCkudG9TdHJpbmcoMTYpfSR7TWF0aC5yYW5kb20oKS50b1N0cmluZygxNikuc2xpY2UoMiwgMTApfWA7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKSAqIDEwMDA7XG5cbiAgICBjb25zdCBzcGFuS2V5ID0gYF90cmFjZXMvJHt0cmFjZUlkfS9zcGFucy8ke3NwYW5JZH1gO1xuICAgIGNvbnN0IHNwYW5WYWx1ZSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIHNwYW5faWQ6IHNwYW5JZCxcbiAgICAgIG5hbWUsXG4gICAgICBzdGFydF91czogbm93LFxuICAgICAgcGFyZW50X3NwYW5faWQ6IHBhcmVudFNwYW5JZCxcbiAgICAgIHN0YXR1czogJ2FjdGl2ZScsXG4gICAgfSk7XG4gICAgYXdhaXQgdGhpcy5wdXQoQnVmZmVyLmZyb20oc3BhbktleSksIEJ1ZmZlci5mcm9tKHNwYW5WYWx1ZSkpO1xuXG4gICAgcmV0dXJuIHNwYW5JZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmQgYSBzcGFuIGFuZCByZWNvcmQgaXRzIGR1cmF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gdHJhY2VJZCAtIElEIG9mIHRoZSB0cmFjZVxuICAgKiBAcGFyYW0gc3BhbklkIC0gSUQgb2YgdGhlIHNwYW4gdG8gZW5kXG4gICAqIEBwYXJhbSBzdGF0dXMgLSBcIm9rXCIsIFwiZXJyb3JcIiwgb3IgXCJ1bnNldFwiXG4gICAqIEByZXR1cm5zIER1cmF0aW9uIGluIG1pY3Jvc2Vjb25kc1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGR1cmF0aW9uID0gYXdhaXQgZGIuZW5kU3Bhbih0cmFjZUlkLCBzcGFuSWQsICdvaycpO1xuICAgKiBjb25zb2xlLmxvZyhgT3BlcmF0aW9uIHRvb2sgJHtkdXJhdGlvbn3CtXNgKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBlbmRTcGFuKFxuICAgIHRyYWNlSWQ6IHN0cmluZyxcbiAgICBzcGFuSWQ6IHN0cmluZyxcbiAgICBzdGF0dXM6ICdvaycgfCAnZXJyb3InIHwgJ3Vuc2V0JyA9ICdvaydcbiAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICB0aGlzLl9lbnN1cmVPcGVuKCk7XG5cbiAgICBjb25zdCBzcGFuS2V5ID0gYF90cmFjZXMvJHt0cmFjZUlkfS9zcGFucy8ke3NwYW5JZH1gO1xuICAgIGNvbnN0IHNwYW5EYXRhID0gYXdhaXQgdGhpcy5nZXQoQnVmZmVyLmZyb20oc3BhbktleSkpO1xuXG4gICAgaWYgKCFzcGFuRGF0YSkge1xuICAgICAgdGhyb3cgbmV3IERhdGFiYXNlRXJyb3IoYFNwYW4gbm90IGZvdW5kOiAke3NwYW5JZH1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBzcGFuID0gSlNPTi5wYXJzZShzcGFuRGF0YS50b1N0cmluZygpKTtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpICogMTAwMDtcbiAgICBjb25zdCBkdXJhdGlvbiA9IG5vdyAtIHNwYW4uc3RhcnRfdXM7XG5cbiAgICBjb25zdCB1cGRhdGVkU3BhbiA9IHtcbiAgICAgIC4uLnNwYW4sXG4gICAgICBzdGF0dXMsXG4gICAgICBlbmRfdXM6IG5vdyxcbiAgICAgIGR1cmF0aW9uX3VzOiBkdXJhdGlvbixcbiAgICB9O1xuICAgIGF3YWl0IHRoaXMucHV0KEJ1ZmZlci5mcm9tKHNwYW5LZXkpLCBCdWZmZXIuZnJvbShKU09OLnN0cmluZ2lmeSh1cGRhdGVkU3BhbikpKTtcblxuICAgIHJldHVybiBkdXJhdGlvbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbG9zZSB0aGUgZGF0YWJhc2UgY29ubmVjdGlvbi5cbiAgICogSWYgcnVubmluZyBpbiBlbWJlZGRlZCBtb2RlLCBhbHNvIHN0b3BzIHRoZSBlbWJlZGRlZCBzZXJ2ZXIuXG4gICAqL1xuICBhc3luYyBjbG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5fY2xvc2VkKSByZXR1cm47XG4gICAgaWYgKHRoaXMuX2NsaWVudCkge1xuICAgICAgYXdhaXQgdGhpcy5fY2xpZW50LmNsb3NlKCk7XG4gICAgICB0aGlzLl9jbGllbnQgPSBudWxsO1xuICAgIH1cbiAgICAvLyBTdG9wIGVtYmVkZGVkIHNlcnZlciBpZiB3ZSBzdGFydGVkIGl0XG4gICAgaWYgKHRoaXMuX2VtYmVkZGVkU2VydmVyU3RhcnRlZCkge1xuICAgICAgYXdhaXQgc3RvcEVtYmVkZGVkU2VydmVyKHRoaXMuX2NvbmZpZy5wYXRoKTtcbiAgICAgIHRoaXMuX2VtYmVkZGVkU2VydmVyU3RhcnRlZCA9IGZhbHNlO1xuICAgIH1cbiAgICB0aGlzLl9jbG9zZWQgPSB0cnVlO1xuICB9XG5cbiAgLy8gSW50ZXJuYWwgbWV0aG9kcyBmb3IgdHJhbnNhY3Rpb24gbWFuYWdlbWVudFxuICBwcml2YXRlIGFzeW5jIF9iZWdpblRyYW5zYWN0aW9uKCk6IFByb21pc2U8YmlnaW50PiB7XG4gICAgcmV0dXJuIHRoaXMuX2NsaWVudCEuYmVnaW5UcmFuc2FjdGlvbigpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBfY29tbWl0VHJhbnNhY3Rpb24odHhuSWQ6IGJpZ2ludCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLl9jbGllbnQhLmNvbW1pdFRyYW5zYWN0aW9uKHR4bklkKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgX2Fib3J0VHJhbnNhY3Rpb24odHhuSWQ6IGJpZ2ludCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLl9jbGllbnQhLmFib3J0VHJhbnNhY3Rpb24odHhuSWQpO1xuICB9XG5cbiAgcHJpdmF0ZSBfZW5zdXJlT3BlbigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5fY2xvc2VkKSB7XG4gICAgICB0aHJvdyBuZXcgRGF0YWJhc2VFcnJvcignRGF0YWJhc2UgaXMgY2xvc2VkJyk7XG4gICAgfVxuICAgIGlmICghdGhpcy5fY2xpZW50KSB7XG4gICAgICB0aHJvdyBuZXcgRGF0YWJhc2VFcnJvcignRGF0YWJhc2Ugbm90IGNvbm5lY3RlZCcpO1xuICAgIH1cbiAgfVxufVxuIl19
|