@pipeline-builder/pipeline-data 3.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.
@@ -0,0 +1,456 @@
1
+ "use strict";
2
+ // Copyright 2026 Pipeline Builder Contributors
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.db = exports.Connection = void 0;
6
+ exports.getConnection = getConnection;
7
+ exports.closeConnection = closeConnection;
8
+ exports.testConnection = testConnection;
9
+ exports.initializeDatabase = initializeDatabase;
10
+ const api_core_1 = require("@pipeline-builder/api-core");
11
+ const drizzle_orm_1 = require("drizzle-orm");
12
+ const node_postgres_1 = require("drizzle-orm/node-postgres");
13
+ const pg_1 = require("pg");
14
+ const drizzle_schema_1 = require("./drizzle-schema");
15
+ const retry_strategy_1 = require("./retry-strategy");
16
+ const logger = (0, api_core_1.createLogger)('Database');
17
+ /**
18
+ * Get database configuration from environment variables
19
+ * Note: Uses environment variables directly to avoid circular dependency with pipeline-core
20
+ */
21
+ function parseIntEnv(value, fallback) {
22
+ if (!value)
23
+ return fallback;
24
+ const parsed = parseInt(value, 10);
25
+ return Number.isNaN(parsed) ? fallback : parsed;
26
+ }
27
+ /** Detect Lambda environment for pool-size tuning */
28
+ const isLambda = !!process.env.AWS_LAMBDA_FUNCTION_NAME;
29
+ function getDatabaseConfig() {
30
+ // Lambda: small pool (each invocation is short-lived, many concurrent instances)
31
+ // ECS/long-running: larger pool for sustained concurrency
32
+ const defaultPoolSize = isLambda ? 2 : 20;
33
+ const defaultIdleTimeout = isLambda ? 10000 : 30000;
34
+ return {
35
+ host: process.env.DB_HOST || 'postgres',
36
+ port: parseIntEnv(process.env.DB_PORT, 5432),
37
+ database: process.env.DATABASE || 'pipeline_builder',
38
+ user: process.env.DB_USER || 'postgres',
39
+ password: process.env.DB_PASSWORD || (process.env.NODE_ENV === 'production' ? (() => { throw new Error('DB_PASSWORD is required in production'); })() : ''),
40
+ maxPoolSize: parseIntEnv(process.env.DRIZZLE_MAX_POOL_SIZE, defaultPoolSize),
41
+ idleTimeoutMillis: parseIntEnv(process.env.DRIZZLE_IDLE_TIMEOUT_MILLIS, defaultIdleTimeout),
42
+ connectionTimeoutMillis: parseIntEnv(process.env.DRIZZLE_CONNECTION_TIMEOUT_MILLIS, 5000),
43
+ };
44
+ }
45
+ /**
46
+ * Singleton database connection class.
47
+ * Manages PostgreSQL connection pooling and Drizzle ORM instance.
48
+ *
49
+ * Features:
50
+ * - Singleton pattern for single connection pool
51
+ * - Automatic connection retry with backoff
52
+ * - Connection health monitoring
53
+ * - Graceful shutdown handling
54
+ * - Comprehensive error handling
55
+ * - Connection statistics tracking
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { Connection } from './connection';
60
+ *
61
+ * const connection = Connection.getInstance();
62
+ * const plugins = await connection.db.select().from(schema.plugin);
63
+ *
64
+ * // During shutdown
65
+ * await connection.close();
66
+ * ```
67
+ */
68
+ class Connection {
69
+ static instance = null;
70
+ /**
71
+ * Drizzle ORM database instance with schema
72
+ */
73
+ db;
74
+ pool;
75
+ options;
76
+ retryStrategy;
77
+ isShuttingDown = false;
78
+ /**
79
+ * Private constructor to enforce singleton pattern.
80
+ * Initializes PostgreSQL connection pool and Drizzle ORM instance.
81
+ *
82
+ * @param options - Optional configuration for the connection
83
+ * @throws {Error} If database initialization fails after all retries
84
+ */
85
+ constructor(options = {}) {
86
+ this.options = {
87
+ enableLogging: options.enableLogging ?? true,
88
+ enableAutoRetry: options.enableAutoRetry ?? true,
89
+ maxRetries: options.maxRetries ?? parseInt(process.env.DB_MAX_RETRIES || '3', 10),
90
+ retryDelay: options.retryDelay ?? parseInt(process.env.DB_RETRY_DELAY_MS || '1000', 10),
91
+ ssl: options.ssl ?? false,
92
+ };
93
+ // Initialize retry strategy
94
+ this.retryStrategy = new retry_strategy_1.ConnectionRetryStrategy({
95
+ maxRetries: this.options.maxRetries,
96
+ baseDelay: this.options.retryDelay,
97
+ });
98
+ try {
99
+ const config = getDatabaseConfig();
100
+ const poolConfig = {
101
+ host: config.host,
102
+ port: config.port,
103
+ database: config.database,
104
+ user: config.user,
105
+ password: config.password,
106
+ max: config.maxPoolSize,
107
+ idleTimeoutMillis: config.idleTimeoutMillis,
108
+ connectionTimeoutMillis: config.connectionTimeoutMillis,
109
+ ssl: this.options.ssl,
110
+ allowExitOnIdle: true,
111
+ };
112
+ this.pool = new pg_1.Pool(poolConfig);
113
+ this.setupEventHandlers();
114
+ this.db = (0, node_postgres_1.drizzle)(this.pool, { schema: drizzle_schema_1.schema });
115
+ if (this.options.enableLogging) {
116
+ logger.info('Database connection initialized successfully');
117
+ this.logConnectionConfig(poolConfig);
118
+ }
119
+ }
120
+ catch (error) {
121
+ logger.error('Failed to initialize database connection:', error);
122
+ throw new Error('Database initialization failed');
123
+ }
124
+ }
125
+ /**
126
+ * Gets the singleton instance of the Connection class.
127
+ * Creates a new instance if one doesn't exist.
128
+ *
129
+ * @param options - Optional configuration (only used on first call)
130
+ * @returns The singleton Connection instance
131
+ */
132
+ static getInstance(options) {
133
+ if (!Connection.instance) {
134
+ Connection.instance = new Connection(options);
135
+ }
136
+ return Connection.instance;
137
+ }
138
+ /**
139
+ * Resets the singleton instance.
140
+ * Useful for testing or reconfiguring the connection.
141
+ *
142
+ * @param closeExisting - Whether to close existing connection before reset
143
+ */
144
+ static async reset(closeExisting = true) {
145
+ if (Connection.instance && closeExisting) {
146
+ await Connection.instance.close();
147
+ }
148
+ Connection.instance = null;
149
+ }
150
+ /**
151
+ * Tests the database connection
152
+ *
153
+ * @returns Promise that resolves to true if connection is healthy
154
+ */
155
+ async testConnection() {
156
+ try {
157
+ const client = await this.pool.connect();
158
+ const result = await client.query('SELECT 1');
159
+ client.release();
160
+ if (this.options.enableLogging) {
161
+ logger.info('Database connection test successful');
162
+ }
163
+ return result.rows.length > 0;
164
+ }
165
+ catch (error) {
166
+ logger.error('Database connection test failed:', error);
167
+ return false;
168
+ }
169
+ }
170
+ /**
171
+ * Gets connection pool statistics
172
+ *
173
+ * @returns Current connection pool statistics
174
+ */
175
+ getStats() {
176
+ return {
177
+ totalCount: this.pool.totalCount,
178
+ idleCount: this.pool.idleCount,
179
+ waitingCount: this.pool.waitingCount,
180
+ };
181
+ }
182
+ /**
183
+ * Executes a database transaction
184
+ *
185
+ * @param callback - Function to execute within the transaction
186
+ * @returns Result of the transaction
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const result = await connection.transaction(async (tx) => {
191
+ * await tx.insert(schema.plugin).values({ ... });
192
+ * await tx.insert(schema.metadata).values({ ... });
193
+ * return { success: true };
194
+ * });
195
+ * ```
196
+ */
197
+ async transaction(callback, timeoutMs = parseIntEnv(process.env.DB_TRANSACTION_TIMEOUT_MS, 30000)) {
198
+ let timer;
199
+ // Wrap callback to set PostgreSQL statement_timeout as a server-side guard.
200
+ // The Promise.race timeout below handles the JS side, but statement_timeout
201
+ // ensures the DB itself cancels long-running queries if the JS timeout fires
202
+ // but the connection isn't cleaned up.
203
+ const wrappedCallback = async (tx) => {
204
+ await tx.execute((0, drizzle_orm_1.sql) `SET LOCAL statement_timeout = ${String(timeoutMs)}`);
205
+ return callback(tx);
206
+ };
207
+ const txPromise = this.db.transaction(wrappedCallback);
208
+ const timeoutPromise = new Promise((_, reject) => {
209
+ timer = setTimeout(() => reject(new Error(`Transaction timeout after ${timeoutMs}ms`)), timeoutMs);
210
+ });
211
+ try {
212
+ return await Promise.race([txPromise, timeoutPromise]);
213
+ }
214
+ finally {
215
+ clearTimeout(timer);
216
+ }
217
+ }
218
+ /**
219
+ * Acquires a client from the pool for manual query execution
220
+ * Remember to release the client when done
221
+ *
222
+ * @returns PostgreSQL client from the pool
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * const client = await connection.getClient();
227
+ * try {
228
+ * await client.query('BEGIN');
229
+ * await client.query('INSERT INTO ...');
230
+ * await client.query('COMMIT');
231
+ * } finally {
232
+ * client.release();
233
+ * }
234
+ * ```
235
+ */
236
+ async getClient() {
237
+ return this.pool.connect();
238
+ }
239
+ /**
240
+ * Closes the database connection pool gracefully.
241
+ * Should be called during application shutdown.
242
+ *
243
+ * @param timeout - Maximum time to wait for connections to close (ms)
244
+ * @returns Promise that resolves when pool is closed
245
+ */
246
+ async close(timeout = parseIntEnv(process.env.DB_CLOSE_TIMEOUT_MS, 5000)) {
247
+ if (this.isShuttingDown) {
248
+ logger.warn('Connection is already shutting down');
249
+ return;
250
+ }
251
+ this.isShuttingDown = true;
252
+ try {
253
+ if (this.options.enableLogging) {
254
+ logger.info('Closing database connection pool...');
255
+ const stats = this.getStats();
256
+ logger.info(`Pool stats - Total: ${stats.totalCount}, Idle: ${stats.idleCount}, Waiting: ${stats.waitingCount}`);
257
+ }
258
+ // Set a timeout for graceful shutdown
259
+ const closePromise = this.pool.end();
260
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Connection close timeout')), timeout));
261
+ await Promise.race([closePromise, timeoutPromise]);
262
+ if (this.options.enableLogging) {
263
+ logger.info('Database connection closed successfully');
264
+ }
265
+ }
266
+ catch (error) {
267
+ logger.error('Error closing database connection:', error);
268
+ throw error;
269
+ }
270
+ finally {
271
+ this.isShuttingDown = false;
272
+ }
273
+ }
274
+ /**
275
+ * Checks if the connection is shutting down
276
+ *
277
+ * @returns true if connection is in shutdown state
278
+ */
279
+ isClosing() {
280
+ return this.isShuttingDown;
281
+ }
282
+ /**
283
+ * Sets up event handlers for the connection pool
284
+ */
285
+ setupEventHandlers() {
286
+ this.pool.on('error', (err) => {
287
+ logger.error('Unexpected error on idle client:', err);
288
+ if (this.options.enableAutoRetry && this.retryStrategy.getAttempts() < this.options.maxRetries) {
289
+ void this.retryStrategy.handleConnectionError(err, () => this.testConnection()).catch((retryErr) => {
290
+ logger.error('Connection retry error:', retryErr);
291
+ });
292
+ }
293
+ });
294
+ this.pool.on('connect', () => {
295
+ this.retryStrategy.reset(); // Reset on successful connection
296
+ if (this.options.enableLogging) {
297
+ logger.debug('New database connection established');
298
+ }
299
+ });
300
+ this.pool.on('remove', () => {
301
+ if (this.options.enableLogging) {
302
+ logger.debug('Client removed from pool');
303
+ }
304
+ });
305
+ }
306
+ /**
307
+ * Logs connection configuration (sanitized)
308
+ */
309
+ logConnectionConfig(config) {
310
+ logger.info('Database Configuration:', {
311
+ host: `${config.host}:${config.port}`,
312
+ database: config.database,
313
+ user: config.user,
314
+ maxPoolSize: config.max,
315
+ idleTimeoutMs: config.idleTimeoutMillis,
316
+ connectionTimeoutMs: config.connectionTimeoutMillis,
317
+ ssl: config.ssl ? 'enabled' : 'disabled',
318
+ });
319
+ }
320
+ }
321
+ exports.Connection = Connection;
322
+ /**
323
+ * Singleton database instance for use throughout the application.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * import { db } from './connection';
328
+ *
329
+ * // Select queries
330
+ * const plugins = await db.select().from(schema.plugin);
331
+ *
332
+ * // Insert queries
333
+ * await db.insert(schema.plugin).values({ name: 'my-plugin' });
334
+ *
335
+ * // Transactions
336
+ * await db.transaction(async (tx) => {
337
+ * await tx.insert(schema.plugin).values({ ... });
338
+ * await tx.update(schema.plugin).set({ ... });
339
+ * });
340
+ * ```
341
+ */
342
+ // Lazy initialization to avoid race condition on module load
343
+ let _dbInstance = null;
344
+ /**
345
+ * Get the database instance with lazy initialization
346
+ * This avoids the race condition where the module is loaded before environment is configured
347
+ */
348
+ function getDbInstance() {
349
+ if (!_dbInstance) {
350
+ _dbInstance = Connection.getInstance().db;
351
+ }
352
+ return _dbInstance;
353
+ }
354
+ /**
355
+ * Proxy-based lazy database instance
356
+ * The actual connection is only created when first accessed
357
+ *
358
+ * @example
359
+ * ```typescript
360
+ * import { db } from './connection';
361
+ *
362
+ * // Connection is created here on first use, not on import
363
+ * const plugins = await db.select().from(schema.plugin);
364
+ * ```
365
+ */
366
+ exports.db = new Proxy({}, {
367
+ get(_, prop) {
368
+ const instance = getDbInstance();
369
+ const value = instance[prop];
370
+ // Bind methods to the instance to preserve 'this' context
371
+ if (typeof value === 'function') {
372
+ return value.bind(instance);
373
+ }
374
+ return value;
375
+ },
376
+ });
377
+ /**
378
+ * Gets the Connection instance for advanced operations
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * import { getConnection } from './connection';
383
+ *
384
+ * const connection = getConnection();
385
+ * const stats = connection.getStats();
386
+ * console.log(`Active connections: ${stats.totalCount}`);
387
+ * ```
388
+ */
389
+ function getConnection() {
390
+ return Connection.getInstance();
391
+ }
392
+ /**
393
+ * Closes the database connection
394
+ * Should be called during application shutdown
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * import { closeConnection } from './connection';
399
+ *
400
+ * process.on('SIGTERM', async () => {
401
+ * await closeConnection();
402
+ * process.exit(0);
403
+ * });
404
+ * ```
405
+ */
406
+ async function closeConnection() {
407
+ const connection = Connection.getInstance();
408
+ await connection.close();
409
+ _dbInstance = null; // Reset lazy instance
410
+ }
411
+ /**
412
+ * Tests the database connection
413
+ *
414
+ * @returns Promise that resolves to true if connection is healthy
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * import { testConnection } from './connection';
419
+ *
420
+ * if (await testConnection()) {
421
+ * console.log('Database is ready');
422
+ * } else {
423
+ * console.error('Database connection failed');
424
+ * process.exit(1);
425
+ * }
426
+ * ```
427
+ */
428
+ async function testConnection() {
429
+ const connection = Connection.getInstance();
430
+ return connection.testConnection();
431
+ }
432
+ /**
433
+ * Initialize the database connection explicitly
434
+ * Call this during application startup after environment is configured
435
+ *
436
+ * @example
437
+ * ```typescript
438
+ * import { initializeDatabase } from './connection';
439
+ *
440
+ * async function bootstrap() {
441
+ * // Load environment variables first
442
+ * dotenv.config();
443
+ *
444
+ * // Then initialize database
445
+ * await initializeDatabase();
446
+ * }
447
+ * ```
448
+ */
449
+ async function initializeDatabase() {
450
+ const connection = Connection.getInstance();
451
+ const healthy = await connection.testConnection();
452
+ if (!healthy) {
453
+ throw new Error('Database connection failed during initialization');
454
+ }
455
+ }
456
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdGdyZXMtY29ubmVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhYmFzZS9wb3N0Z3Jlcy1jb25uZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDOzs7QUFpY3RDLHNDQUVDO0FBZ0JELDBDQUlDO0FBbUJELHdDQUdDO0FBbUJELGdEQU1DO0FBcGdCRCx5REFBMEQ7QUFDMUQsNkNBQWtDO0FBQ2xDLDZEQUFvRDtBQUNwRCwyQkFBa0Q7QUFDbEQscURBQTBDO0FBQzFDLHFEQUEyRDtBQUUzRCxNQUFNLE1BQU0sR0FBRyxJQUFBLHVCQUFZLEVBQUMsVUFBVSxDQUFDLENBQUM7QUFFeEM7OztHQUdHO0FBQ0gsU0FBUyxXQUFXLENBQUMsS0FBeUIsRUFBRSxRQUFnQjtJQUM5RCxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBQzVCLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDbkMsT0FBTyxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztBQUNsRCxDQUFDO0FBRUQscURBQXFEO0FBQ3JELE1BQU0sUUFBUSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDO0FBRXhELFNBQVMsaUJBQWlCO0lBQ3hCLGlGQUFpRjtJQUNqRiwwREFBMEQ7SUFDMUQsTUFBTSxlQUFlLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUMxQyxNQUFNLGtCQUFrQixHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFFcEQsT0FBTztRQUNMLElBQUksRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFD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QUssQ0FBQztJQUNmLENBQUM7Q0FDRixDQUFDLENBQUM7QUFFSDs7Ozs7Ozs7Ozs7R0FXRztBQUNILFNBQWdCLGFBQWE7SUFDM0IsT0FBTyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDbEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSSxLQUFLLFVBQVUsZUFBZTtJQUNuQyxNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDNUMsTUFBTSxVQUFVLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDekIsV0FBVyxHQUFHLElBQUksQ0FBQyxDQUFDLHNCQUFzQjtBQUM1QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSSxLQUFLLFVBQVUsY0FBYztJQUNsQyxNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDNUMsT0FBTyxVQUFVLENBQUMsY0FBYyxFQUFFLENBQUM7QUFDckMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0ksS0FBSyxVQUFVLGtCQUFrQjtJQUN0QyxNQUFNLFVBQVUsR0FBRyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDNUMsTUFBTSxPQUFPLEdBQUcsTUFBTSxVQUFVLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDbEQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxrREFBa0QsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IHNxbCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IGRyaXp6bGUgfSBmcm9tICdkcml6emxlLW9ybS9ub2RlLXBvc3RncmVzJztcbmltcG9ydCB7IFBvb2wsIFBvb2xDb25maWcsIFBvb2xDbGllbnQgfSBmcm9tICdwZyc7XG5pbXBvcnQgeyBzY2hlbWEgfSBmcm9tICcuL2RyaXp6bGUtc2NoZW1hJztcbmltcG9ydCB7IENvbm5lY3Rpb25SZXRyeVN0cmF0ZWd5IH0gZnJvbSAnLi9yZXRyeS1zdHJhdGVneSc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignRGF0YWJhc2UnKTtcblxuLyoqXG4gKiBHZXQgZGF0YWJhc2UgY29uZmlndXJhdGlvbiBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICogTm90ZTogVXNlcyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZGlyZWN0bHkgdG8gYXZvaWQgY2lyY3VsYXIgZGVwZW5kZW5jeSB3aXRoIHBpcGVsaW5lLWNvcmVcbiAqL1xuZnVuY3Rpb24gcGFyc2VJbnRFbnYodmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCwgZmFsbGJhY2s6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICghdmFsdWUpIHJldHVybiBmYWxsYmFjaztcbiAgY29uc3QgcGFyc2VkID0gcGFyc2VJbnQodmFsdWUsIDEwKTtcbiAgcmV0dXJuIE51bWJlci5pc05hTihwYXJzZWQpID8gZmFsbGJhY2sgOiBwYXJzZWQ7XG59XG5cbi8qKiBEZXRlY3QgTGFtYmRhIGVudmlyb25tZW50IGZvciBwb29sLXNpemUgdHVuaW5nICovXG5jb25zdCBpc0xhbWJkYSA9ICEhcHJvY2Vzcy5lbnYuQVdTX0xBTUJEQV9GVU5DVElPTl9OQU1FO1xuXG5mdW5jdGlvbiBnZXREYXRhYmFzZUNvbmZpZygpIHtcbiAgLy8gTGFtYmRhOiBzbWFsbCBwb29sIChlYWNoIGludm9jYXRpb24gaXMgc2hvcnQtbGl2ZWQsIG1hbnkgY29uY3VycmVudCBpbnN0YW5jZXMpXG4gIC8vIEVDUy9sb25nLXJ1bm5pbmc6IGxhcmdlciBwb29sIGZvciBzdXN0YWluZWQgY29uY3VycmVuY3lcbiAgY29uc3QgZGVmYXVsdFBvb2xTaXplID0gaXNMYW1iZGEgPyAyIDogMjA7XG4gIGNvbnN0IGRlZmF1bHRJZGxlVGltZW91dCA9IGlzTGFtYmRhID8gMTAwMDAgOiAzMDAwMDtcblxuICByZXR1cm4ge1xuICAgIGhvc3Q6IHByb2Nlc3MuZW52LkRCX0hPU1QgfHwgJ3Bvc3RncmVzJyxcbiAgICBwb3J0OiBwYXJzZUludEVudihwcm9jZXNzLmVudi5EQl9QT1JULCA1NDMyKSxcbiAgICBkYXRhYmFzZTogcHJvY2Vzcy5lbnYuREFUQUJBU0UgfHwgJ3BpcGVsaW5lX2J1aWxkZXInLFxuICAgIHVzZXI6IHByb2Nlc3MuZW52LkRCX1VTRVIgfHwgJ3Bvc3RncmVzJyxcbiAgICBwYXNzd29yZDogcHJvY2Vzcy5lbnYuREJfUEFTU1dPUkQgfHwgKHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAncHJvZHVjdGlvbicgPyAoKCkgPT4geyB0aHJvdyBuZXcgRXJyb3IoJ0RCX1BBU1NXT1JEIGlzIHJlcXVpcmVkIGluIHByb2R1Y3Rpb24nKTsgfSkoKSBhcyBzdHJpbmcgOiAnJyksXG4gICAgbWF4UG9vbFNpemU6IHBhcnNlSW50RW52KHByb2Nlc3MuZW52LkRSSVpaTEVfTUFYX1BPT0xfU0laRSwgZGVmYXVsdFBvb2xTaXplKSxcbiAgICBpZGxlVGltZW91dE1pbGxpczogcGFyc2VJbnRFbnYocHJvY2Vzcy5lbnYuRFJJWlpMRV9JRExFX1RJTUVPVVRfTUlMTElTLCBkZWZhdWx0SWRsZVRpbWVvdXQpLFxuICAgIGNvbm5lY3Rpb25UaW1lb3V0TWlsbGlzOiBwYXJzZUludEVudihwcm9jZXNzLmVudi5EUklaWkxFX0NPTk5FQ1RJT05fVElNRU9VVF9NSUxMSVMsIDUwMDApLFxuICB9O1xufVxuXG4vKipcbiAqIERhdGFiYXNlIGNvbm5lY3Rpb24gc3RhdGlzdGljcyBmb3IgbW9uaXRvcmluZ1xuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbm5lY3Rpb25TdGF0cyB7XG4gIHRvdGFsQ291bnQ6IG51bWJlcjtcbiAgaWRsZUNvdW50OiBudW1iZXI7XG4gIHdhaXRpbmdDb3VudDogbnVtYmVyO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGNvbmZpZ3VyaW5nIHRoZSBkYXRhYmFzZSBjb25uZWN0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ29ubmVjdGlvbk9wdGlvbnMge1xuICAvKiogV2hldGhlciB0byBlbmFibGUgY29ubmVjdGlvbiBsb2dnaW5nICovXG4gIGVuYWJsZUxvZ2dpbmc/OiBib29sZWFuO1xuXG4gIC8qKiBXaGV0aGVyIHRvIGF1dG9tYXRpY2FsbHkgcmV0cnkgZmFpbGVkIGNvbm5lY3Rpb25zICovXG4gIGVuYWJsZUF1dG9SZXRyeT86IGJvb2xlYW47XG5cbiAgLyoqIE1heGltdW0gbnVtYmVyIG9mIGNvbm5lY3Rpb24gcmV0cnkgYXR0ZW1wdHMgKi9cbiAgbWF4UmV0cmllcz86IG51bWJlcjtcblxuICAvKiogRGVsYXkgYmV0d2VlbiByZXRyeSBhdHRlbXB0cyBpbiBtaWxsaXNlY29uZHMgKi9cbiAgcmV0cnlEZWxheT86IG51bWJlcjtcblxuICAvKiogU1NMIGNvbmZpZ3VyYXRpb24gKi9cbiAgc3NsPzogYm9vbGVhbiB8IHsgcmVqZWN0VW5hdXRob3JpemVkOiBib29sZWFuIH07XG59XG5cbi8qKlxuICogU2luZ2xldG9uIGRhdGFiYXNlIGNvbm5lY3Rpb24gY2xhc3MuXG4gKiBNYW5hZ2VzIFBvc3RncmVTUUwgY29ubmVjdGlvbiBwb29saW5nIGFuZCBEcml6emxlIE9STSBpbnN0YW5jZS5cbiAqXG4gKiBGZWF0dXJlczpcbiAqIC0gU2luZ2xldG9uIHBhdHRlcm4gZm9yIHNpbmdsZSBjb25uZWN0aW9uIHBvb2xcbiAqIC0gQXV0b21hdGljIGNvbm5lY3Rpb24gcmV0cnkgd2l0aCBiYWNrb2ZmXG4gKiAtIENvbm5lY3Rpb24gaGVhbHRoIG1vbml0b3JpbmdcbiAqIC0gR3JhY2VmdWwgc2h1dGRvd24gaGFuZGxpbmdcbiAqIC0gQ29tcHJlaGVuc2l2ZSBlcnJvciBoYW5kbGluZ1xuICogLSBDb25uZWN0aW9uIHN0YXRpc3RpY3MgdHJhY2tpbmdcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgQ29ubmVjdGlvbiB9IGZyb20gJy4vY29ubmVjdGlvbic7XG4gKlxuICogY29uc3QgY29ubmVjdGlvbiA9IENvbm5lY3Rpb24uZ2V0SW5zdGFuY2UoKTtcbiAqIGNvbnN0IHBsdWdpbnMgPSBhd2FpdCBjb25uZWN0aW9uLmRiLnNlbGVjdCgpLmZyb20oc2NoZW1hLnBsdWdpbik7XG4gKlxuICogLy8gRHVyaW5nIHNodXRkb3duXG4gKiBhd2FpdCBjb25uZWN0aW9uLmNsb3NlKCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIENvbm5lY3Rpb24ge1xuICBwcml2YXRlIHN0YXRpYyBpbnN0YW5jZTogQ29ubmVjdGlvbiB8IG51bGwgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBEcml6emxlIE9STSBkYXRhYmFzZSBpbnN0YW5jZSB3aXRoIHNjaGVtYVxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRiOiBSZXR1cm5UeXBlPHR5cGVvZiBkcml6emxlPjtcblxuICBwcml2YXRlIHJlYWRvbmx5IHBvb2w6IFBvb2w7XG4gIHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczogUmVxdWlyZWQ8Q29ubmVjdGlvbk9wdGlvbnM+O1xuICBwcml2YXRlIHJlYWRvbmx5IHJldHJ5U3RyYXRlZ3k6IENvbm5lY3Rpb25SZXRyeVN0cmF0ZWd5O1xuICBwcml2YXRlIGlzU2h1dHRpbmdEb3duID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIFByaXZhdGUgY29uc3RydWN0b3IgdG8gZW5mb3JjZSBzaW5nbGV0b24gcGF0dGVybi5cbiAgICogSW5pdGlhbGl6ZXMgUG9zdGdyZVNRTCBjb25uZWN0aW9uIHBvb2wgYW5kIERyaXp6bGUgT1JNIGluc3RhbmNlLlxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBjb25uZWN0aW9uXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBJZiBkYXRhYmFzZSBpbml0aWFsaXphdGlvbiBmYWlscyBhZnRlciBhbGwgcmV0cmllc1xuICAgKi9cbiAgcHJpdmF0ZSBjb25zdHJ1Y3RvcihvcHRpb25zOiBDb25uZWN0aW9uT3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgZW5hYmxlTG9nZ2luZzogb3B0aW9ucy5lbmFibGVMb2dnaW5nID8/IHRydWUsXG4gICAgICBlbmFibGVBdXRvUmV0cnk6IG9wdGlvbnMuZW5hYmxlQXV0b1JldHJ5ID8/IHRydWUsXG4gICAgICBtYXhSZXRyaWVzOiBvcHRpb25zLm1heFJldHJpZXMgPz8gcGFyc2VJbnQocHJvY2Vzcy5lbnYuREJfTUFYX1JFVFJJRVMgfHwgJzMnLCAxMCksXG4gICAgICByZXRyeURlbGF5OiBvcHRpb25zLnJldHJ5RGVsYXkgPz8gcGFyc2VJbnQocHJvY2Vzcy5lbnYuREJfUkVUUllfREVMQVlfTVMgfHwgJzEwMDAnLCAxMCksXG4gICAgICBzc2w6IG9wdGlvbnMuc3NsID8/IGZhbHNlLFxuICAgIH07XG5cbiAgICAvLyBJbml0aWFsaXplIHJldHJ5IHN0cmF0ZWd5XG4gICAgdGhpcy5yZXRyeVN0cmF0ZWd5ID0gbmV3IENvbm5lY3Rpb25SZXRyeVN0cmF0ZWd5KHtcbiAgICAgIG1heFJldHJpZXM6IHRoaXMub3B0aW9ucy5tYXhSZXRyaWVzLFxuICAgICAgYmFzZURlbGF5OiB0aGlzLm9wdGlvbnMucmV0cnlEZWxheSxcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBjb25maWcgPSBnZXREYXRhYmFzZUNvbmZpZygpO1xuXG4gICAgICBjb25zdCBwb29sQ29uZmlnOiBQb29sQ29uZmlnID0ge1xuICAgICAgICBob3N0OiBjb25maWcuaG9zdCxcbiAgICAgICAgcG9ydDogY29uZmlnLnBvcnQsXG4gICAgICAgIGRhdGFiYXNlOiBjb25maWcuZGF0YWJhc2UsXG4gICAgICAgIHVzZXI6IGNvbmZpZy51c2VyLFxuICAgICAgICBwYXNzd29yZDogY29uZmlnLnBhc3N3b3JkLFxuICAgICAgICBtYXg6IGNvbmZpZy5tYXhQb29sU2l6ZSxcbiAgICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IGNvbmZpZy5pZGxlVGltZW91dE1pbGxpcyxcbiAgICAgICAgY29ubmVjdGlvblRpbWVvdXRNaWxsaXM6IGNvbmZpZy5jb25uZWN0aW9uVGltZW91dE1pbGxpcyxcbiAgICAgICAgc3NsOiB0aGlzLm9wdGlvbnMuc3NsLFxuICAgICAgICBhbGxvd0V4aXRPbklkbGU6IHRydWUsXG4gICAgICB9O1xuXG4gICAgICB0aGlzLnBvb2wgPSBuZXcgUG9vbChwb29sQ29uZmlnKTtcbiAgICAgIHRoaXMuc2V0dXBFdmVudEhhbmRsZXJzKCk7XG5cbiAgICAgIHRoaXMuZGIgPSBkcml6emxlKHRoaXMucG9vbCwgeyBzY2hlbWEgfSk7XG5cbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlTG9nZ2luZykge1xuICAgICAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiBpbml0aWFsaXplZCBzdWNjZXNzZnVsbHknKTtcbiAgICAgICAgdGhpcy5sb2dDb25uZWN0aW9uQ29uZmlnKHBvb2xDb25maWcpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBpbml0aWFsaXplIGRhdGFiYXNlIGNvbm5lY3Rpb246JywgZXJyb3IpO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhYmFzZSBpbml0aWFsaXphdGlvbiBmYWlsZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgc2luZ2xldG9uIGluc3RhbmNlIG9mIHRoZSBDb25uZWN0aW9uIGNsYXNzLlxuICAgKiBDcmVhdGVzIGEgbmV3IGluc3RhbmNlIGlmIG9uZSBkb2Vzbid0IGV4aXN0LlxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIGNvbmZpZ3VyYXRpb24gKG9ubHkgdXNlZCBvbiBmaXJzdCBjYWxsKVxuICAgKiBAcmV0dXJucyBUaGUgc2luZ2xldG9uIENvbm5lY3Rpb24gaW5zdGFuY2VcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgZ2V0SW5zdGFuY2Uob3B0aW9ucz86IENvbm5lY3Rpb25PcHRpb25zKTogQ29ubmVjdGlvbiB7XG4gICAgaWYgKCFDb25uZWN0aW9uLmluc3RhbmNlKSB7XG4gICAgICBDb25uZWN0aW9uLmluc3RhbmNlID0gbmV3IENvbm5lY3Rpb24ob3B0aW9ucyk7XG4gICAgfVxuICAgIHJldHVybiBDb25uZWN0aW9uLmluc3RhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0cyB0aGUgc2luZ2xldG9uIGluc3RhbmNlLlxuICAgKiBVc2VmdWwgZm9yIHRlc3Rpbmcgb3IgcmVjb25maWd1cmluZyB0aGUgY29ubmVjdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIGNsb3NlRXhpc3RpbmcgLSBXaGV0aGVyIHRvIGNsb3NlIGV4aXN0aW5nIGNvbm5lY3Rpb24gYmVmb3JlIHJlc2V0XG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIHJlc2V0KGNsb3NlRXhpc3Rpbmc6IGJvb2xlYW4gPSB0cnVlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKENvbm5lY3Rpb24uaW5zdGFuY2UgJiYgY2xvc2VFeGlzdGluZykge1xuICAgICAgYXdhaXQgQ29ubmVjdGlvbi5pbnN0YW5jZS5jbG9zZSgpO1xuICAgIH1cbiAgICBDb25uZWN0aW9uLmluc3RhbmNlID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUZXN0cyB0aGUgZGF0YWJhc2UgY29ubmVjdGlvblxuICAgKlxuICAgKiBAcmV0dXJucyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiBjb25uZWN0aW9uIGlzIGhlYWx0aHlcbiAgICovXG4gIHB1YmxpYyBhc3luYyB0ZXN0Q29ubmVjdGlvbigpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY2xpZW50ID0gYXdhaXQgdGhpcy5wb29sLmNvbm5lY3QoKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNsaWVudC5xdWVyeSgnU0VMRUNUIDEnKTtcbiAgICAgIGNsaWVudC5yZWxlYXNlKCk7XG5cbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlTG9nZ2luZykge1xuICAgICAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiB0ZXN0IHN1Y2Nlc3NmdWwnKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdC5yb3dzLmxlbmd0aCA+IDA7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRGF0YWJhc2UgY29ubmVjdGlvbiB0ZXN0IGZhaWxlZDonLCBlcnJvcik7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgY29ubmVjdGlvbiBwb29sIHN0YXRpc3RpY3NcbiAgICpcbiAgICogQHJldHVybnMgQ3VycmVudCBjb25uZWN0aW9uIHBvb2wgc3RhdGlzdGljc1xuICAgKi9cbiAgcHVibGljIGdldFN0YXRzKCk6IENvbm5lY3Rpb25TdGF0cyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHRvdGFsQ291bnQ6IHRoaXMucG9vbC50b3RhbENvdW50LFxuICAgICAgaWRsZUNvdW50OiB0aGlzLnBvb2wuaWRsZUNvdW50LFxuICAgICAgd2FpdGluZ0NvdW50OiB0aGlzLnBvb2wud2FpdGluZ0NvdW50LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZXMgYSBkYXRhYmFzZSB0cmFuc2FjdGlvblxuICAgKlxuICAgKiBAcGFyYW0gY2FsbGJhY2sgLSBGdW5jdGlvbiB0byBleGVjdXRlIHdpdGhpbiB0aGUgdHJhbnNhY3Rpb25cbiAgICogQHJldHVybnMgUmVzdWx0IG9mIHRoZSB0cmFuc2FjdGlvblxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNvbm5lY3Rpb24udHJhbnNhY3Rpb24oYXN5bmMgKHR4KSA9PiB7XG4gICAqICAgYXdhaXQgdHguaW5zZXJ0KHNjaGVtYS5wbHVnaW4pLnZhbHVlcyh7IC4uLiB9KTtcbiAgICogICBhd2FpdCB0eC5pbnNlcnQoc2NoZW1hLm1ldGFkYXRhKS52YWx1ZXMoeyAuLi4gfSk7XG4gICAqICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSB9O1xuICAgKiB9KTtcbiAgICogYGBgXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdHJhbnNhY3Rpb248VD4oXG4gICAgY2FsbGJhY2s6IFBhcmFtZXRlcnM8dHlwZW9mIHRoaXMuZGIudHJhbnNhY3Rpb24+WzBdLFxuICAgIHRpbWVvdXRNczogbnVtYmVyID0gcGFyc2VJbnRFbnYocHJvY2Vzcy5lbnYuREJfVFJBTlNBQ1RJT05fVElNRU9VVF9NUywgMzAwMDApLFxuICApOiBQcm9taXNlPFQ+IHtcbiAgICBsZXQgdGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+O1xuICAgIC8vIFdyYXAgY2FsbGJhY2sgdG8gc2V0IFBvc3RncmVTUUwgc3RhdGVtZW50X3RpbWVvdXQgYXMgYSBzZXJ2ZXItc2lkZSBndWFyZC5cbiAgICAvLyBUaGUgUHJvbWlzZS5yYWNlIHRpbWVvdXQgYmVsb3cgaGFuZGxlcyB0aGUgSlMgc2lkZSwgYnV0IHN0YXRlbWVudF90aW1lb3V0XG4gICAgLy8gZW5zdXJlcyB0aGUgREIgaXRzZWxmIGNhbmNlbHMgbG9uZy1ydW5uaW5nIHF1ZXJpZXMgaWYgdGhlIEpTIHRpbWVvdXQgZmlyZXNcbiAgICAvLyBidXQgdGhlIGNvbm5lY3Rpb24gaXNuJ3QgY2xlYW5lZCB1cC5cbiAgICBjb25zdCB3cmFwcGVkQ2FsbGJhY2s6IHR5cGVvZiBjYWxsYmFjayA9IGFzeW5jICh0eCkgPT4ge1xuICAgICAgYXdhaXQgdHguZXhlY3V0ZShzcWxgU0VUIExPQ0FMIHN0YXRlbWVudF90aW1lb3V0ID0gJHtTdHJpbmcodGltZW91dE1zKX1gKTtcbiAgICAgIHJldHVybiBjYWxsYmFjayh0eCk7XG4gICAgfTtcbiAgICBjb25zdCB0eFByb21pc2UgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKHdyYXBwZWRDYWxsYmFjaykgYXMgUHJvbWlzZTxUPjtcbiAgICBjb25zdCB0aW1lb3V0UHJvbWlzZSA9IG5ldyBQcm9taXNlPG5ldmVyPigoXywgcmVqZWN0KSA9PiB7XG4gICAgICB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihgVHJhbnNhY3Rpb24gdGltZW91dCBhZnRlciAke3RpbWVvdXRNc31tc2ApKSwgdGltZW91dE1zKTtcbiAgICB9KTtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IFByb21pc2UucmFjZShbdHhQcm9taXNlLCB0aW1lb3V0UHJvbWlzZV0pO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIhKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWNxdWlyZXMgYSBjbGllbnQgZnJvbSB0aGUgcG9vbCBmb3IgbWFudWFsIHF1ZXJ5IGV4ZWN1dGlvblxuICAgKiBSZW1lbWJlciB0byByZWxlYXNlIHRoZSBjbGllbnQgd2hlbiBkb25lXG4gICAqXG4gICAqIEByZXR1cm5zIFBvc3RncmVTUUwgY2xpZW50IGZyb20gdGhlIHBvb2xcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjbGllbnQgPSBhd2FpdCBjb25uZWN0aW9uLmdldENsaWVudCgpO1xuICAgKiB0cnkge1xuICAgKiAgIGF3YWl0IGNsaWVudC5xdWVyeSgnQkVHSU4nKTtcbiAgICogICBhd2FpdCBjbGllbnQucXVlcnkoJ0lOU0VSVCBJTlRPIC4uLicpO1xuICAgKiAgIGF3YWl0IGNsaWVudC5xdWVyeSgnQ09NTUlUJyk7XG4gICAqIH0gZmluYWxseSB7XG4gICAqICAgY2xpZW50LnJlbGVhc2UoKTtcbiAgICogfVxuICAgKiBgYGBcbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZXRDbGllbnQoKTogUHJvbWlzZTxQb29sQ2xpZW50PiB7XG4gICAgcmV0dXJuIHRoaXMucG9vbC5jb25uZWN0KCk7XG4gIH1cblxuICAvKipcbiAgICogQ2xvc2VzIHRoZSBkYXRhYmFzZSBjb25uZWN0aW9uIHBvb2wgZ3JhY2VmdWxseS5cbiAgICogU2hvdWxkIGJlIGNhbGxlZCBkdXJpbmcgYXBwbGljYXRpb24gc2h1dGRvd24uXG4gICAqXG4gICAqIEBwYXJhbSB0aW1lb3V0IC0gTWF4aW11bSB0aW1lIHRvIHdhaXQgZm9yIGNvbm5lY3Rpb25zIHRvIGNsb3NlIChtcylcbiAgICogQHJldHVybnMgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gcG9vbCBpcyBjbG9zZWRcbiAgICovXG4gIHB1YmxpYyBhc3luYyBjbG9zZSh0aW1lb3V0OiBudW1iZXIgPSBwYXJzZUludEVudihwcm9jZXNzLmVudi5EQl9DTE9TRV9USU1FT1VUX01TLCA1MDAwKSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmlzU2h1dHRpbmdEb3duKSB7XG4gICAgICBsb2dnZXIud2FybignQ29ubmVjdGlvbiBpcyBhbHJlYWR5IHNodXR0aW5nIGRvd24nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmlzU2h1dHRpbmdEb3duID0gdHJ1ZTtcblxuICAgIHRyeSB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxvZ2dpbmcpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ0Nsb3NpbmcgZGF0YWJhc2UgY29ubmVjdGlvbiBwb29sLi4uJyk7XG4gICAgICAgIGNvbnN0IHN0YXRzID0gdGhpcy5nZXRTdGF0cygpO1xuICAgICAgICBsb2dnZXIuaW5mbyhgUG9vbCBzdGF0cyAtIFRvdGFsOiAke3N0YXRzLnRvdGFsQ291bnR9LCBJZGxlOiAke3N0YXRzLmlkbGVDb3VudH0sIFdhaXRpbmc6ICR7c3RhdHMud2FpdGluZ0NvdW50fWApO1xuICAgICAgfVxuXG4gICAgICAvLyBTZXQgYSB0aW1lb3V0IGZvciBncmFjZWZ1bCBzaHV0ZG93blxuICAgICAgY29uc3QgY2xvc2VQcm9taXNlID0gdGhpcy5wb29sLmVuZCgpO1xuICAgICAgY29uc3QgdGltZW91dFByb21pc2UgPSBuZXcgUHJvbWlzZSgoXywgcmVqZWN0KSA9PlxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChuZXcgRXJyb3IoJ0Nvbm5lY3Rpb24gY2xvc2UgdGltZW91dCcpKSwgdGltZW91dCksXG4gICAgICApO1xuXG4gICAgICBhd2FpdCBQcm9taXNlLnJhY2UoW2Nsb3NlUHJvbWlzZSwgdGltZW91dFByb21pc2VdKTtcblxuICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMb2dnaW5nKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdEYXRhYmFzZSBjb25uZWN0aW9uIGNsb3NlZCBzdWNjZXNzZnVsbHknKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdFcnJvciBjbG9zaW5nIGRhdGFiYXNlIGNvbm5lY3Rpb246JywgZXJyb3IpO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuaXNTaHV0dGluZ0Rvd24gPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSBjb25uZWN0aW9uIGlzIHNodXR0aW5nIGRvd25cbiAgICpcbiAgICogQHJldHVybnMgdHJ1ZSBpZiBjb25uZWN0aW9uIGlzIGluIHNodXRkb3duIHN0YXRlXG4gICAqL1xuICBwdWJsaWMgaXNDbG9zaW5nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzU2h1dHRpbmdEb3duO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdXAgZXZlbnQgaGFuZGxlcnMgZm9yIHRoZSBjb25uZWN0aW9uIHBvb2xcbiAgICovXG4gIHByaXZhdGUgc2V0dXBFdmVudEhhbmRsZXJzKCk6IHZvaWQge1xuICAgIHRoaXMucG9vbC5vbignZXJyb3InLCAoZXJyKSA9PiB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1VuZXhwZWN0ZWQgZXJyb3Igb24gaWRsZSBjbGllbnQ6JywgZXJyKTtcblxuICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVBdXRvUmV0cnkgJiYgdGhpcy5yZXRyeVN0cmF0ZWd5LmdldEF0dGVtcHRzKCkgPCB0aGlzLm9wdGlvbnMubWF4UmV0cmllcykge1xuICAgICAgICB2b2lkIHRoaXMucmV0cnlTdHJhdGVneS5oYW5kbGVDb25uZWN0aW9uRXJyb3IoZXJyLCAoKSA9PiB0aGlzLnRlc3RDb25uZWN0aW9uKCkpLmNhdGNoKChyZXRyeUVycikgPT4ge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcignQ29ubmVjdGlvbiByZXRyeSBlcnJvcjonLCByZXRyeUVycik7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgdGhpcy5wb29sLm9uKCdjb25uZWN0JywgKCkgPT4ge1xuICAgICAgdGhpcy5yZXRyeVN0cmF0ZWd5LnJlc2V0KCk7IC8vIFJlc2V0IG9uIHN1Y2Nlc3NmdWwgY29ubmVjdGlvblxuXG4gICAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxvZ2dpbmcpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdOZXcgZGF0YWJhc2UgY29ubmVjdGlvbiBlc3RhYmxpc2hlZCcpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgdGhpcy5wb29sLm9uKCdyZW1vdmUnLCAoKSA9PiB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxvZ2dpbmcpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdDbGllbnQgcmVtb3ZlZCBmcm9tIHBvb2wnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2dzIGNvbm5lY3Rpb24gY29uZmlndXJhdGlvbiAoc2FuaXRpemVkKVxuICAgKi9cbiAgcHJpdmF0ZSBsb2dDb25uZWN0aW9uQ29uZmlnKGNvbmZpZzogUG9vbENvbmZpZyk6IHZvaWQge1xuICAgIGxvZ2dlci5pbmZvKCdEYXRhYmFzZSBDb25maWd1cmF0aW9uOicsIHtcbiAgICAgIGhvc3Q6IGAke2NvbmZpZy5ob3N0fToke2NvbmZpZy5wb3J0fWAsXG4gICAgICBkYXRhYmFzZTogY29uZmlnLmRhdGFiYXNlLFxuICAgICAgdXNlcjogY29uZmlnLnVzZXIsXG4gICAgICBtYXhQb29sU2l6ZTogY29uZmlnLm1heCxcbiAgICAgIGlkbGVUaW1lb3V0TXM6IGNvbmZpZy5pZGxlVGltZW91dE1pbGxpcyxcbiAgICAgIGNvbm5lY3Rpb25UaW1lb3V0TXM6IGNvbmZpZy5jb25uZWN0aW9uVGltZW91dE1pbGxpcyxcbiAgICAgIHNzbDogY29uZmlnLnNzbCA/ICdlbmFibGVkJyA6ICdkaXNhYmxlZCcsXG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBTaW5nbGV0b24gZGF0YWJhc2UgaW5zdGFuY2UgZm9yIHVzZSB0aHJvdWdob3V0IHRoZSBhcHBsaWNhdGlvbi5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgZGIgfSBmcm9tICcuL2Nvbm5lY3Rpb24nO1xuICpcbiAqIC8vIFNlbGVjdCBxdWVyaWVzXG4gKiBjb25zdCBwbHVnaW5zID0gYXdhaXQgZGIuc2VsZWN0KCkuZnJvbShzY2hlbWEucGx1Z2luKTtcbiAqXG4gKiAvLyBJbnNlcnQgcXVlcmllc1xuICogYXdhaXQgZGIuaW5zZXJ0KHNjaGVtYS5wbHVnaW4pLnZhbHVlcyh7IG5hbWU6ICdteS1wbHVnaW4nIH0pO1xuICpcbiAqIC8vIFRyYW5zYWN0aW9uc1xuICogYXdhaXQgZGIudHJhbnNhY3Rpb24oYXN5bmMgKHR4KSA9PiB7XG4gKiAgIGF3YWl0IHR4Lmluc2VydChzY2hlbWEucGx1Z2luKS52YWx1ZXMoeyAuLi4gfSk7XG4gKiAgIGF3YWl0IHR4LnVwZGF0ZShzY2hlbWEucGx1Z2luKS5zZXQoeyAuLi4gfSk7XG4gKiB9KTtcbiAqIGBgYFxuICovXG5cbi8vIExhenkgaW5pdGlhbGl6YXRpb24gdG8gYXZvaWQgcmFjZSBjb25kaXRpb24gb24gbW9kdWxlIGxvYWRcbmxldCBfZGJJbnN0YW5jZTogUmV0dXJuVHlwZTx0eXBlb2YgZHJpenpsZT4gfCBudWxsID0gbnVsbDtcblxuLyoqXG4gKiBHZXQgdGhlIGRhdGFiYXNlIGluc3RhbmNlIHdpdGggbGF6eSBpbml0aWFsaXphdGlvblxuICogVGhpcyBhdm9pZHMgdGhlIHJhY2UgY29uZGl0aW9uIHdoZXJlIHRoZSBtb2R1bGUgaXMgbG9hZGVkIGJlZm9yZSBlbnZpcm9ubWVudCBpcyBjb25maWd1cmVkXG4gKi9cbmZ1bmN0aW9uIGdldERiSW5zdGFuY2UoKTogUmV0dXJuVHlwZTx0eXBlb2YgZHJpenpsZT4ge1xuICBpZiAoIV9kYkluc3RhbmNlKSB7XG4gICAgX2RiSW5zdGFuY2UgPSBDb25uZWN0aW9uLmdldEluc3RhbmNlKCkuZGI7XG4gIH1cbiAgcmV0dXJuIF9kYkluc3RhbmNlO1xufVxuXG4vKipcbiAqIFByb3h5LWJhc2VkIGxhenkgZGF0YWJhc2UgaW5zdGFuY2VcbiAqIFRoZSBhY3R1YWwgY29ubmVjdGlvbiBpcyBvbmx5IGNyZWF0ZWQgd2hlbiBmaXJzdCBhY2Nlc3NlZFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBkYiB9IGZyb20gJy4vY29ubmVjdGlvbic7XG4gKlxuICogLy8gQ29ubmVjdGlvbiBpcyBjcmVhdGVkIGhlcmUgb24gZmlyc3QgdXNlLCBub3Qgb24gaW1wb3J0XG4gKiBjb25zdCBwbHVnaW5zID0gYXdhaXQgZGIuc2VsZWN0KCkuZnJvbShzY2hlbWEucGx1Z2luKTtcbiAqIGBgYFxuICovXG5leHBvcnQgY29uc3QgZGIgPSBuZXcgUHJveHkoe30gYXMgUmV0dXJuVHlwZTx0eXBlb2YgZHJpenpsZT4sIHtcbiAgZ2V0KF8sIHByb3A6IHN0cmluZyB8IHN5bWJvbCkge1xuICAgIGNvbnN0IGluc3RhbmNlID0gZ2V0RGJJbnN0YW5jZSgpO1xuICAgIGNvbnN0IHZhbHVlID0gaW5zdGFuY2VbcHJvcCBhcyBrZXlvZiB0eXBlb2YgaW5zdGFuY2VdO1xuICAgIC8vIEJpbmQgbWV0aG9kcyB0byB0aGUgaW5zdGFuY2UgdG8gcHJlc2VydmUgJ3RoaXMnIGNvbnRleHRcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gdmFsdWUuYmluZChpbnN0YW5jZSk7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfSxcbn0pO1xuXG4vKipcbiAqIEdldHMgdGhlIENvbm5lY3Rpb24gaW5zdGFuY2UgZm9yIGFkdmFuY2VkIG9wZXJhdGlvbnNcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgZ2V0Q29ubmVjdGlvbiB9IGZyb20gJy4vY29ubmVjdGlvbic7XG4gKlxuICogY29uc3QgY29ubmVjdGlvbiA9IGdldENvbm5lY3Rpb24oKTtcbiAqIGNvbnN0IHN0YXRzID0gY29ubmVjdGlvbi5nZXRTdGF0cygpO1xuICogY29uc29sZS5sb2coYEFjdGl2ZSBjb25uZWN0aW9uczogJHtzdGF0cy50b3RhbENvdW50fWApO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb25uZWN0aW9uKCk6IENvbm5lY3Rpb24ge1xuICByZXR1cm4gQ29ubmVjdGlvbi5nZXRJbnN0YW5jZSgpO1xufVxuXG4vKipcbiAqIENsb3NlcyB0aGUgZGF0YWJhc2UgY29ubmVjdGlvblxuICogU2hvdWxkIGJlIGNhbGxlZCBkdXJpbmcgYXBwbGljYXRpb24gc2h1dGRvd25cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgY2xvc2VDb25uZWN0aW9uIH0gZnJvbSAnLi9jb25uZWN0aW9uJztcbiAqXG4gKiBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKCkgPT4ge1xuICogICBhd2FpdCBjbG9zZUNvbm5lY3Rpb24oKTtcbiAqICAgcHJvY2Vzcy5leGl0KDApO1xuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQ29ubmVjdGlvbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgY29ubmVjdGlvbiA9IENvbm5lY3Rpb24uZ2V0SW5zdGFuY2UoKTtcbiAgYXdhaXQgY29ubmVjdGlvbi5jbG9zZSgpO1xuICBfZGJJbnN0YW5jZSA9IG51bGw7IC8vIFJlc2V0IGxhenkgaW5zdGFuY2Vcbn1cblxuLyoqXG4gKiBUZXN0cyB0aGUgZGF0YWJhc2UgY29ubmVjdGlvblxuICpcbiAqIEByZXR1cm5zIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIGlmIGNvbm5lY3Rpb24gaXMgaGVhbHRoeVxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyB0ZXN0Q29ubmVjdGlvbiB9IGZyb20gJy4vY29ubmVjdGlvbic7XG4gKlxuICogaWYgKGF3YWl0IHRlc3RDb25uZWN0aW9uKCkpIHtcbiAqICAgY29uc29sZS5sb2coJ0RhdGFiYXNlIGlzIHJlYWR5Jyk7XG4gKiB9IGVsc2Uge1xuICogICBjb25zb2xlLmVycm9yKCdEYXRhYmFzZSBjb25uZWN0aW9uIGZhaWxlZCcpO1xuICogICBwcm9jZXNzLmV4aXQoMSk7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRlc3RDb25uZWN0aW9uKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCBjb25uZWN0aW9uID0gQ29ubmVjdGlvbi5nZXRJbnN0YW5jZSgpO1xuICByZXR1cm4gY29ubmVjdGlvbi50ZXN0Q29ubmVjdGlvbigpO1xufVxuXG4vKipcbiAqIEluaXRpYWxpemUgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb24gZXhwbGljaXRseVxuICogQ2FsbCB0aGlzIGR1cmluZyBhcHBsaWNhdGlvbiBzdGFydHVwIGFmdGVyIGVudmlyb25tZW50IGlzIGNvbmZpZ3VyZWRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgaW5pdGlhbGl6ZURhdGFiYXNlIH0gZnJvbSAnLi9jb25uZWN0aW9uJztcbiAqXG4gKiBhc3luYyBmdW5jdGlvbiBib290c3RyYXAoKSB7XG4gKiAgIC8vIExvYWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZpcnN0XG4gKiAgIGRvdGVudi5jb25maWcoKTtcbiAqXG4gKiAgIC8vIFRoZW4gaW5pdGlhbGl6ZSBkYXRhYmFzZVxuICogICBhd2FpdCBpbml0aWFsaXplRGF0YWJhc2UoKTtcbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZURhdGFiYXNlKCk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBjb25uZWN0aW9uID0gQ29ubmVjdGlvbi5nZXRJbnN0YW5jZSgpO1xuICBjb25zdCBoZWFsdGh5ID0gYXdhaXQgY29ubmVjdGlvbi50ZXN0Q29ubmVjdGlvbigpO1xuICBpZiAoIWhlYWx0aHkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGFiYXNlIGNvbm5lY3Rpb24gZmFpbGVkIGR1cmluZyBpbml0aWFsaXphdGlvbicpO1xuICB9XG59Il19
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Configuration for connection retry strategy
3
+ */
4
+ export interface RetryConfig {
5
+ /** Maximum number of retry attempts */
6
+ maxRetries: number;
7
+ /** Base delay between retries in milliseconds */
8
+ baseDelay: number;
9
+ }
10
+ /**
11
+ * Implements exponential backoff retry strategy for database connections.
12
+ *
13
+ * Features:
14
+ * - Exponential backoff with configurable base delay
15
+ * - Attempt tracking and logging
16
+ * - Graceful failure after max retries
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const strategy = new ConnectionRetryStrategy({ maxRetries: 3, baseDelay: 1000 });
21
+ *
22
+ * const result = await strategy.execute(async () => {
23
+ * return await db.query('SELECT 1');
24
+ * });
25
+ * ```
26
+ */
27
+ export declare class ConnectionRetryStrategy {
28
+ private readonly config;
29
+ private attempts;
30
+ constructor(config: RetryConfig);
31
+ /**
32
+ * Executes an operation with retry logic
33
+ *
34
+ * @param operation - Async function to execute with retries
35
+ * @returns Promise resolving to operation result
36
+ * @throws Error if all retry attempts fail
37
+ */
38
+ execute<T>(operation: () => Promise<T>): Promise<T>;
39
+ /**
40
+ * Handles connection errors with retry tracking
41
+ *
42
+ * @param error - Error that occurred
43
+ * @param testConnection - Function to test if connection is restored
44
+ */
45
+ handleConnectionError(error: Error, testConnection: () => Promise<boolean>): Promise<void>;
46
+ /**
47
+ * Resets the attempt counter
48
+ * Call this after a successful operation
49
+ */
50
+ reset(): void;
51
+ /**
52
+ * Gets the current attempt count
53
+ */
54
+ getAttempts(): number;
55
+ /**
56
+ * Calculates exponential backoff delay
57
+ *
58
+ * @param attempt - Current attempt number (1-indexed)
59
+ * @returns Delay in milliseconds
60
+ */
61
+ private calculateBackoff;
62
+ /**
63
+ * Sleeps for the specified duration
64
+ *
65
+ * @param ms - Milliseconds to sleep
66
+ */
67
+ private sleep;
68
+ }