@pipeline-builder/pipeline-data 0.0.1

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.
Files changed (48) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +61 -0
  3. package/drizzle.config.ts +17 -0
  4. package/lib/api/access-control-builder.d.ts +148 -0
  5. package/lib/api/access-control-builder.js +199 -0
  6. package/lib/api/crud-service.d.ts +343 -0
  7. package/lib/api/crud-service.js +849 -0
  8. package/lib/api/query-builders.d.ts +82 -0
  9. package/lib/api/query-builders.js +460 -0
  10. package/lib/api/reporting-service.d.ts +685 -0
  11. package/lib/api/reporting-service.js +1564 -0
  12. package/lib/api/soft-delete-sweep.d.ts +65 -0
  13. package/lib/api/soft-delete-sweep.js +124 -0
  14. package/lib/api/viewer-context.d.ts +29 -0
  15. package/lib/api/viewer-context.js +63 -0
  16. package/lib/core/query-filters.d.ts +263 -0
  17. package/lib/core/query-filters.js +4 -0
  18. package/lib/database/drizzle-schema.d.ts +7357 -0
  19. package/lib/database/drizzle-schema.js +66 -0
  20. package/lib/database/index.d.ts +5 -0
  21. package/lib/database/index.js +8 -0
  22. package/lib/database/migrator.d.ts +20 -0
  23. package/lib/database/migrator.js +80 -0
  24. package/lib/database/postgres-connection.d.ts +201 -0
  25. package/lib/database/postgres-connection.js +413 -0
  26. package/lib/database/retry-strategy.d.ts +68 -0
  27. package/lib/database/retry-strategy.js +122 -0
  28. package/lib/database/schema/alert.d.ts +588 -0
  29. package/lib/database/schema/alert.js +130 -0
  30. package/lib/database/schema/compliance.d.ts +3319 -0
  31. package/lib/database/schema/compliance.js +377 -0
  32. package/lib/database/schema/dashboard.d.ts +474 -0
  33. package/lib/database/schema/dashboard.js +129 -0
  34. package/lib/database/schema/message-attachment.d.ts +183 -0
  35. package/lib/database/schema/message-attachment.js +40 -0
  36. package/lib/database/schema/message.d.ts +411 -0
  37. package/lib/database/schema/message.js +114 -0
  38. package/lib/database/schema/pipeline-template.d.ts +466 -0
  39. package/lib/database/schema/pipeline-template.js +80 -0
  40. package/lib/database/schema/pipeline.d.ts +1613 -0
  41. package/lib/database/schema/pipeline.js +332 -0
  42. package/lib/database/schema/plugin.d.ts +749 -0
  43. package/lib/database/schema/plugin.js +166 -0
  44. package/lib/database/tenancy.d.ts +103 -0
  45. package/lib/database/tenancy.js +167 -0
  46. package/lib/index.d.ts +32 -0
  47. package/lib/index.js +38 -0
  48. package/package.json +161 -0
@@ -0,0 +1,413 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createLogger } from '@pipeline-builder/api-core';
4
+ import { drizzle } from 'drizzle-orm/node-postgres';
5
+ import { Pool } from 'pg';
6
+ import { schema } from './drizzle-schema.js';
7
+ import { ConnectionRetryStrategy } from './retry-strategy.js';
8
+ const logger = createLogger('database');
9
+ /**
10
+ * Get database configuration from environment variables
11
+ * Note: Uses environment variables directly to avoid circular dependency with pipeline-core
12
+ */
13
+ function parseIntEnv(value, fallback) {
14
+ if (!value)
15
+ return fallback;
16
+ const parsed = parseInt(value, 10);
17
+ return Number.isNaN(parsed) ? fallback : parsed;
18
+ }
19
+ /** Detect Lambda environment for pool-size tuning */
20
+ const isLambda = !!process.env.AWS_LAMBDA_FUNCTION_NAME;
21
+ /**
22
+ * Resolve the pg TLS/SSL config from an explicit option or the environment,
23
+ * following the same env-tunable pattern as the pool sizing above.
24
+ *
25
+ * Precedence:
26
+ * 1. An explicit `options.ssl` (constructor arg) always wins — including an
27
+ * explicit `false` to force plaintext.
28
+ * 2. `DB_SSL` (`true`/`1` on, `false`/`0` off) or Postgres' standard
29
+ * `PGSSLMODE` (`disable` = off, any other mode = on).
30
+ * 3. Default: ON in production (RDS connections must be encrypted in transit),
31
+ * OFF everywhere else so a local Postgres without TLS keeps working.
32
+ *
33
+ * When enabled, `rejectUnauthorized` follows `DB_SSL_REJECT_UNAUTHORIZED`
34
+ * (default `false`): AWS RDS presents a cert signed by the RDS CA, which is not
35
+ * in Node's default trust store unless the CA bundle is mounted, so certificate
36
+ * VERIFICATION is opt-in to avoid breaking a stock RDS deploy — the channel is
37
+ * still ENCRYPTED. Set `DB_SSL_REJECT_UNAUTHORIZED=true` once the RDS CA bundle
38
+ * is wired to get full verification.
39
+ */
40
+ export function getSslConfig(explicit) {
41
+ if (explicit !== undefined)
42
+ return explicit;
43
+ const dbSsl = process.env.DB_SSL?.toLowerCase();
44
+ const sslMode = process.env.PGSSLMODE?.toLowerCase();
45
+ let enabled;
46
+ if (dbSsl === 'true' || dbSsl === '1')
47
+ enabled = true;
48
+ else if (dbSsl === 'false' || dbSsl === '0')
49
+ enabled = false;
50
+ else if (sslMode)
51
+ enabled = sslMode !== 'disable';
52
+ else
53
+ enabled = process.env.NODE_ENV === 'production';
54
+ if (!enabled)
55
+ return false;
56
+ return { rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED === 'true' };
57
+ }
58
+ function getDatabaseConfig() {
59
+ // Lambda: small pool (each invocation is short-lived, many concurrent instances)
60
+ // ECS/long-running: larger pool for sustained concurrency
61
+ const defaultPoolSize = isLambda ? 2 : 20;
62
+ const defaultIdleTimeout = isLambda ? 10000 : 30000;
63
+ return {
64
+ host: process.env.DB_HOST || 'postgres',
65
+ port: parseIntEnv(process.env.DB_PORT, 5432),
66
+ database: process.env.DATABASE || 'pipeline_builder',
67
+ user: process.env.DB_USER || 'postgres',
68
+ password: process.env.DB_PASSWORD || (process.env.NODE_ENV === 'production' ? (() => { throw new Error('DB_PASSWORD is required in production'); })() : ''),
69
+ maxPoolSize: parseIntEnv(process.env.DRIZZLE_MAX_POOL_SIZE, defaultPoolSize),
70
+ idleTimeoutMillis: parseIntEnv(process.env.DRIZZLE_IDLE_TIMEOUT_MILLIS, defaultIdleTimeout),
71
+ connectionTimeoutMillis: parseIntEnv(process.env.DRIZZLE_CONNECTION_TIMEOUT_MILLIS, 5000),
72
+ };
73
+ }
74
+ /**
75
+ * Singleton database connection class.
76
+ * Manages PostgreSQL connection pooling and Drizzle ORM instance.
77
+ *
78
+ * Features:
79
+ * - Singleton pattern for single connection pool
80
+ * - Automatic connection retry with backoff
81
+ * - Connection health monitoring
82
+ * - Graceful shutdown handling
83
+ * - Comprehensive error handling
84
+ * - Connection statistics tracking
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * import { Connection } from './connection';
89
+ *
90
+ * const connection = Connection.getInstance();
91
+ * const plugins = await connection.db.select().from(schema.plugin);
92
+ *
93
+ * // During shutdown
94
+ * await connection.close();
95
+ * ```
96
+ */
97
+ export class Connection {
98
+ static instance = null;
99
+ /**
100
+ * Drizzle ORM database instance with schema
101
+ */
102
+ db;
103
+ pool;
104
+ options;
105
+ retryStrategy;
106
+ isShuttingDown = false;
107
+ /**
108
+ * Private constructor to enforce singleton pattern.
109
+ * Initializes PostgreSQL connection pool and Drizzle ORM instance.
110
+ *
111
+ * @param options - Optional configuration for the connection
112
+ * @throws {Error} If database initialization fails after all retries
113
+ */
114
+ constructor(options = {}) {
115
+ this.options = {
116
+ enableLogging: options.enableLogging ?? true,
117
+ enableAutoRetry: options.enableAutoRetry ?? true,
118
+ maxRetries: options.maxRetries ?? parseInt(process.env.DB_MAX_RETRIES || '3', 10),
119
+ retryDelay: options.retryDelay ?? parseInt(process.env.DB_RETRY_DELAY_MS || '1000', 10),
120
+ // Env-driven TLS: on-by-default in production, env-enableable elsewhere.
121
+ // getInstance() is normally called with no options, so this is the only
122
+ // place production RDS connections pick up SSL (formerly hard-coded off).
123
+ ssl: getSslConfig(options.ssl),
124
+ };
125
+ // Initialize retry strategy
126
+ this.retryStrategy = new ConnectionRetryStrategy({
127
+ maxRetries: this.options.maxRetries,
128
+ baseDelay: this.options.retryDelay,
129
+ });
130
+ try {
131
+ const config = getDatabaseConfig();
132
+ const poolConfig = {
133
+ host: config.host,
134
+ port: config.port,
135
+ database: config.database,
136
+ user: config.user,
137
+ password: config.password,
138
+ max: config.maxPoolSize,
139
+ idleTimeoutMillis: config.idleTimeoutMillis,
140
+ connectionTimeoutMillis: config.connectionTimeoutMillis,
141
+ ssl: this.options.ssl,
142
+ allowExitOnIdle: true,
143
+ };
144
+ this.pool = new Pool(poolConfig);
145
+ this.setupEventHandlers();
146
+ this.db = drizzle(this.pool, { schema });
147
+ if (this.options.enableLogging) {
148
+ logger.info('Database connection initialized successfully');
149
+ this.logConnectionConfig(poolConfig);
150
+ }
151
+ }
152
+ catch (error) {
153
+ logger.error('Failed to initialize database connection:', error);
154
+ throw new Error('Database initialization failed');
155
+ }
156
+ }
157
+ /**
158
+ * Gets the singleton instance of the Connection class.
159
+ * Creates a new instance if one doesn't exist.
160
+ *
161
+ * @param options - Optional configuration (only used on first call)
162
+ * @returns The singleton Connection instance
163
+ */
164
+ static getInstance(options) {
165
+ if (!Connection.instance) {
166
+ Connection.instance = new Connection(options);
167
+ }
168
+ return Connection.instance;
169
+ }
170
+ /**
171
+ * Resets the singleton instance.
172
+ * Useful for testing or reconfiguring the connection.
173
+ *
174
+ * @param closeExisting - Whether to close existing connection before reset
175
+ */
176
+ static async reset(closeExisting = true) {
177
+ if (Connection.instance && closeExisting) {
178
+ await Connection.instance.close();
179
+ }
180
+ Connection.instance = null;
181
+ }
182
+ /**
183
+ * Tests the database connection
184
+ *
185
+ * @returns Promise that resolves to true if connection is healthy
186
+ */
187
+ async testConnection() {
188
+ try {
189
+ const client = await this.pool.connect();
190
+ // Release even if the probe query throws — otherwise a flaky DB (the exact
191
+ // case this check exists for) leaks a pooled client each failure and can
192
+ // exhaust the pool on the retry path.
193
+ try {
194
+ const result = await client.query('SELECT 1');
195
+ if (this.options.enableLogging) {
196
+ logger.info('Database connection test successful');
197
+ }
198
+ return result.rows.length > 0;
199
+ }
200
+ finally {
201
+ client.release();
202
+ }
203
+ }
204
+ catch (error) {
205
+ logger.error('Database connection test failed:', error);
206
+ return false;
207
+ }
208
+ }
209
+ /**
210
+ * Gets connection pool statistics
211
+ *
212
+ * @returns Current connection pool statistics
213
+ */
214
+ getStats() {
215
+ return {
216
+ totalCount: this.pool.totalCount,
217
+ idleCount: this.pool.idleCount,
218
+ waitingCount: this.pool.waitingCount,
219
+ };
220
+ }
221
+ /**
222
+ * Closes the database connection pool gracefully.
223
+ * Should be called during application shutdown.
224
+ *
225
+ * @param timeout - Maximum time to wait for connections to close (ms)
226
+ * @returns Promise that resolves when pool is closed
227
+ */
228
+ async close(timeout = parseIntEnv(process.env.DB_CLOSE_TIMEOUT_MS, 5000)) {
229
+ if (this.isShuttingDown) {
230
+ logger.warn('Connection is already shutting down');
231
+ return;
232
+ }
233
+ this.isShuttingDown = true;
234
+ try {
235
+ if (this.options.enableLogging) {
236
+ logger.info('Closing database connection pool...');
237
+ const stats = this.getStats();
238
+ logger.info(`Pool stats - Total: ${stats.totalCount}, Idle: ${stats.idleCount}, Waiting: ${stats.waitingCount}`);
239
+ }
240
+ // Set a timeout for graceful shutdown
241
+ const closePromise = this.pool.end();
242
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Connection close timeout')), timeout));
243
+ await Promise.race([closePromise, timeoutPromise]);
244
+ if (this.options.enableLogging) {
245
+ logger.info('Database connection closed successfully');
246
+ }
247
+ }
248
+ catch (error) {
249
+ logger.error('Error closing database connection:', error);
250
+ throw error;
251
+ }
252
+ finally {
253
+ this.isShuttingDown = false;
254
+ }
255
+ }
256
+ /**
257
+ * Checks if the connection is shutting down
258
+ *
259
+ * @returns true if connection is in shutdown state
260
+ */
261
+ isClosing() {
262
+ return this.isShuttingDown;
263
+ }
264
+ /**
265
+ * Sets up event handlers for the connection pool
266
+ */
267
+ setupEventHandlers() {
268
+ this.pool.on('error', (err) => {
269
+ logger.error('Unexpected error on idle client:', err);
270
+ if (this.options.enableAutoRetry && this.retryStrategy.getAttempts() < this.options.maxRetries) {
271
+ void this.retryStrategy.handleConnectionError(err, () => this.testConnection()).catch((retryErr) => {
272
+ logger.error('Connection retry error:', retryErr);
273
+ });
274
+ }
275
+ });
276
+ this.pool.on('connect', () => {
277
+ this.retryStrategy.reset(); // Reset on successful connection
278
+ if (this.options.enableLogging) {
279
+ logger.debug('New database connection established');
280
+ }
281
+ });
282
+ this.pool.on('remove', () => {
283
+ if (this.options.enableLogging) {
284
+ logger.debug('Client removed from pool');
285
+ }
286
+ });
287
+ }
288
+ /**
289
+ * Logs connection configuration (sanitized)
290
+ */
291
+ logConnectionConfig(config) {
292
+ logger.info('Database Configuration:', {
293
+ host: `${config.host}:${config.port}`,
294
+ database: config.database,
295
+ user: config.user,
296
+ maxPoolSize: config.max,
297
+ idleTimeoutMs: config.idleTimeoutMillis,
298
+ connectionTimeoutMs: config.connectionTimeoutMillis,
299
+ ssl: config.ssl ? 'enabled' : 'disabled',
300
+ });
301
+ }
302
+ }
303
+ /**
304
+ * Singleton database instance for use throughout the application.
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * import { db } from './connection';
309
+ *
310
+ * // Select queries
311
+ * const plugins = await db.select().from(schema.plugin);
312
+ *
313
+ * // Insert queries
314
+ * await db.insert(schema.plugin).values({ name: 'my-plugin' });
315
+ *
316
+ * // Transactions
317
+ * await db.transaction(async (tx) => {
318
+ * await tx.insert(schema.plugin).values({ ... });
319
+ * await tx.update(schema.plugin).set({ ... });
320
+ * });
321
+ * ```
322
+ */
323
+ // Lazy initialization to avoid race condition on module load
324
+ let _dbInstance = null;
325
+ /**
326
+ * Get the database instance with lazy initialization
327
+ * This avoids the race condition where the module is loaded before environment is configured
328
+ */
329
+ function getDbInstance() {
330
+ if (!_dbInstance) {
331
+ _dbInstance = Connection.getInstance().db;
332
+ }
333
+ return _dbInstance;
334
+ }
335
+ /**
336
+ * Proxy-based lazy database instance
337
+ * The actual connection is only created when first accessed
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * import { db } from './connection';
342
+ *
343
+ * // Connection is created here on first use, not on import
344
+ * const plugins = await db.select().from(schema.plugin);
345
+ * ```
346
+ */
347
+ export const db = new Proxy({}, {
348
+ get(_, prop) {
349
+ const instance = getDbInstance();
350
+ const value = instance[prop];
351
+ // Bind methods to the instance to preserve 'this' context
352
+ if (typeof value === 'function') {
353
+ return value.bind(instance);
354
+ }
355
+ return value;
356
+ },
357
+ });
358
+ /**
359
+ * Gets the Connection instance for advanced operations
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * import { getConnection } from './connection';
364
+ *
365
+ * const connection = getConnection();
366
+ * const stats = connection.getStats();
367
+ * console.log(`Active connections: ${stats.totalCount}`);
368
+ * ```
369
+ */
370
+ export function getConnection() {
371
+ return Connection.getInstance();
372
+ }
373
+ /**
374
+ * Closes the database connection
375
+ * Should be called during application shutdown
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * import { closeConnection } from './connection';
380
+ *
381
+ * process.on('SIGTERM', async () => {
382
+ * await closeConnection();
383
+ * process.exit(0);
384
+ * });
385
+ * ```
386
+ */
387
+ export async function closeConnection() {
388
+ const connection = Connection.getInstance();
389
+ await connection.close();
390
+ _dbInstance = null; // Reset lazy instance
391
+ }
392
+ /**
393
+ * Tests the database connection
394
+ *
395
+ * @returns Promise that resolves to true if connection is healthy
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * import { testConnection } from './connection';
400
+ *
401
+ * if (await testConnection()) {
402
+ * console.log('Database is ready');
403
+ * } else {
404
+ * console.error('Database connection failed');
405
+ * process.exit(1);
406
+ * }
407
+ * ```
408
+ */
409
+ export async function testConnection() {
410
+ const connection = Connection.getInstance();
411
+ return connection.testConnection();
412
+ }
413
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdGdyZXMtY29ubmVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhYmFzZS9wb3N0Z3Jlcy1jb25uZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzFELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsSUFBSSxFQUFtQixNQUFNLElBQUksQ0FBQztBQUMzQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDN0MsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFOUQsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0FBRXhDOzs7R0FHRztBQUNILFNBQVMsV0FBVyxDQUFDLEtBQXlCLEVBQUUsUUFBZ0I7SUFDOUQsSUFBSSxDQUFDLEtBQUs7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUM1QixNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25DLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7QUFDbEQsQ0FBQztBQUVELHFEQUFxRDtBQUNyRCxNQUFNLFFBQVEsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQztBQUV4RDs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxRQUFtQztJQUM5RCxJQUFJLFFBQVEsS0FBSyxTQUFTO1FBQUUsT0FBTyxRQUFRLENBQUM7SUFFNUMsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVB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TUFBTSxVQUFVLGFBQWE7SUFDM0IsT0FBTyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDbEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWU7SUFDbkMsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQzVDLE1BQU0sVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3pCLFdBQVcsR0FBRyxJQUFJLENBQUMsQ0FBQyxzQkFBc0I7QUFDNUMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjO0lBQ2xDLE1BQU0sVUFBVSxHQUFHLFVBQVUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUM1QyxPQUFPLFVBQVUsQ0FBQyxjQUFjLEVBQUUsQ0FBQztBQUNyQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IGRyaXp6bGUgfSBmcm9tICdkcml6emxlLW9ybS9ub2RlLXBvc3RncmVzJztcbmltcG9ydCB7IFBvb2wsIHR5cGUgUG9vbENvbmZpZyB9IGZyb20gJ3BnJztcbmltcG9ydCB7IHNjaGVtYSB9IGZyb20gJy4vZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHsgQ29ubmVjdGlvblJldHJ5U3RyYXRlZ3kgfSBmcm9tICcuL3JldHJ5LXN0cmF0ZWd5LmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdkYXRhYmFzZScpO1xuXG4vKipcbiAqIEdldCBkYXRhYmFzZSBjb25maWd1cmF0aW9uIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gKiBOb3RlOiBVc2VzIGVudmlyb25tZW50IHZhcmlhYmxlcyBkaXJlY3RseSB0byBhdm9pZCBjaXJjdWxhciBkZXBlbmRlbmN5IHdpdGggcGlwZWxpbmUtY29yZVxuICovXG5mdW5jdGlvbiBwYXJzZUludEVudih2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkLCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIGZhbGxiYWNrO1xuICBjb25zdCBwYXJzZWQgPSBwYXJzZUludCh2YWx1ZSwgMTApO1xuICByZXR1cm4gTnVtYmVyLmlzTmFOKHBhcnNlZCkgPyBmYWxsYmFjayA6IHBhcnNlZDtcbn1cblxuLyoqIERldGVjdCBMYW1iZGEgZW52aXJvbm1lbnQgZm9yIHBvb2wtc2l6ZSB0dW5pbmcgKi9cbmNvbnN0IGlzTGFtYmRhID0gISFwcm9jZXNzLmVudi5BV1NfTEFNQkRBX0ZVTkNUSU9OX05BTUU7XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgcGcgVExTL1NTTCBjb25maWcgZnJvbSBhbiBleHBsaWNpdCBvcHRpb24gb3IgdGhlIGVudmlyb25tZW50LFxuICogZm9sbG93aW5nIHRoZSBzYW1lIGVudi10dW5hYmxlIHBhdHRlcm4gYXMgdGhlIHBvb2wgc2l6aW5nIGFib3ZlLlxuICpcbiAqIFByZWNlZGVuY2U6XG4gKiAgIDEuIEFuIGV4cGxpY2l0IGBvcHRpb25zLnNzbGAgKGNvbnN0cnVjdG9yIGFyZykgYWx3YXlzIHdpbnMg4oCUIGluY2x1ZGluZyBhblxuICogICAgICBleHBsaWNpdCBgZmFsc2VgIHRvIGZvcmNlIHBsYWludGV4dC5cbiAqICAgMi4gYERCX1NTTGAgKGB0cnVlYC9gMWAgb24sIGBmYWxzZWAvYDBgIG9mZikgb3IgUG9zdGdyZXMnIHN0YW5kYXJkXG4gKiAgICAgIGBQR1NTTE1PREVgIChgZGlzYWJsZWAgPSBvZmYsIGFueSBvdGhlciBtb2RlID0gb24pLlxuICogICAzLiBEZWZhdWx0OiBPTiBpbiBwcm9kdWN0aW9uIChSRFMgY29ubmVjdGlvbnMgbXVzdCBiZSBlbmNyeXB0ZWQgaW4gdHJhbnNpdCksXG4gKiAgICAgIE9GRiBldmVyeXdoZXJlIGVsc2Ugc28gYSBsb2NhbCBQb3N0Z3JlcyB3aXRob3V0IFRMUyBrZWVwcyB3b3JraW5nLlxuICpcbiAqIFdoZW4gZW5hYmxlZCwgYHJlamVjdFVuYXV0aG9yaXplZGAgZm9sbG93cyBgREJfU1NMX1JFSkVDVF9VTkFVVEhPUklaRURgXG4gKiAoZGVmYXVsdCBgZmFsc2VgKTogQVdTIFJEUyBwcmVzZW50cyBhIGNlcnQgc2lnbmVkIGJ5IHRoZSBSRFMgQ0EsIHdoaWNoIGlzIG5vdFxuICogaW4gTm9kZSdzIGRlZmF1bHQgdHJ1c3Qgc3RvcmUgdW5sZXNzIHRoZSBDQSBidW5kbGUgaXMgbW91bnRlZCwgc28gY2VydGlmaWNhdGVcbiAqIFZFUklGSUNBVElPTiBpcyBvcHQtaW4gdG8gYXZvaWQgYnJlYWtpbmcgYSBzdG9jayBSRFMgZGVwbG95IOKAlCB0aGUgY2hhbm5lbCBpc1xuICogc3RpbGwgRU5DUllQVEVELiBTZXQgYERCX1NTTF9SRUpFQ1RfVU5BVVRIT1JJWkVEPXRydWVgIG9uY2UgdGhlIFJEUyBDQSBidW5kbGVcbiAqIGlzIHdpcmVkIHRvIGdldCBmdWxsIHZlcmlmaWNhdGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFNzbENvbmZpZyhleHBsaWNpdD86IENvbm5lY3Rpb25PcHRpb25zWydzc2wnXSk6IGJvb2xlYW4gfCB7IHJlamVjdFVuYXV0aG9yaXplZDogYm9vbGVhbiB9IHtcbiAgaWYgKGV4cGxpY2l0ICE9PSB1bmRlZmluZWQpIHJldHVybiBleHBsaWNpdDtcblxuICBjb25zdCBkYlNzbCA9IHByb2Nlc3MuZW52LkRCX1NTTD8udG9Mb3dlckNhc2UoKTtcbiAgY29uc3Qgc3NsTW9kZSA9IHByb2Nlc3MuZW52LlBHU1NMTU9ERT8udG9Mb3dlckNhc2UoKTtcblxuICBsZXQgZW5hYmxlZDogYm9vbGVhbjtcbiAgaWYgKGRiU3NsID09PSAndHJ1ZScgfHwgZGJTc2wgPT09ICcxJykgZW5hYmxlZCA9IHRydWU7XG4gIGVsc2UgaWYgKGRiU3NsID09PSAnZmFsc2UnIHx8IGRiU3NsID09PSAnMCcpIGVuYWJsZWQgPSBmYWxzZTtcbiAgZWxzZSBpZiAoc3NsTW9kZSkgZW5hYmxlZCA9IHNzbE1vZGUgIT09ICdkaXNhYmxlJztcbiAgZWxzZSBlbmFibGVkID0gcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICdwcm9kdWN0aW9uJztcblxuICBpZiAoIWVuYWJsZWQpIHJldHVybiBmYWxzZTtcbiAgcmV0dXJuIHsgcmVqZWN0VW5hdXRob3JpemVkOiBwcm9jZXNzLmVudi5EQl9TU0xfUkVKRUNUX1VOQVVUSE9SSVpFRCA9PT0gJ3RydWUnIH07XG59XG5cbmZ1bmN0aW9uIGdldERhdGFiYXNlQ29uZmlnKCkge1xuICAvLyBMYW1iZGE6IHNtYWxsIHBvb2wgKGVhY2ggaW52b2NhdGlvbiBpcyBzaG9ydC1saXZlZCwgbWFueSBjb25jdXJyZW50IGluc3RhbmNlcylcbiAgLy8gRUNTL2xvbmctcnVubmluZzogbGFyZ2VyIHBvb2wgZm9yIHN1c3RhaW5lZCBjb25jdXJyZW5jeVxuICBjb25zdCBkZWZhdWx0UG9vbFNpemUgPSBpc0xhbWJkYSA/IDIgOiAyMDtcbiAgY29uc3QgZGVmYXVsdElkbGVUaW1lb3V0ID0gaXNMYW1iZGEgPyAxMDAwMCA6IDMwMDAwO1xuXG4gIHJldHVybiB7XG4gICAgaG9zdDogcHJvY2Vzcy5lbnYuREJfSE9TVCB8fCAncG9zdGdyZXMnLFxuICAgIHBvcnQ6IHBhcnNlSW50RW52KHByb2Nlc3MuZW52LkRCX1BPUlQsIDU0MzIpLFxuICAgIGRhdGFiYXNlOiBwcm9jZXNzLmVudi5EQVRBQkFTRSB8fCAncGlwZWxpbmVfYnVpbGRlcicsXG4gICAgdXNlcjogcHJvY2Vzcy5lbnYuREJfVVNFUiB8fCAncG9zdGdyZXMnLFxuICAgIHBhc3N3b3JkOiBwcm9jZXNzLmVudi5EQl9QQVNTV09SRCB8fCAocHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICdwcm9kdWN0aW9uJyA/ICgoKSA9PiB7IHRocm93IG5ldyBFcnJvcignREJfUEFTU1dPUkQgaXMgcmVxdWlyZWQgaW4gcHJvZHVjdGlvbicpOyB9KSgpIGFzIHN0cmluZyA6ICcnKSxcbiAgICBtYXhQb29sU2l6ZTogcGFyc2VJbnRFbnYocHJvY2Vzcy5lbnYuRFJJWlpMRV9NQVhfUE9PTF9TSVpFLCBkZWZhdWx0UG9vbFNpemUpLFxuICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwYXJzZUludEVudihwcm9jZXNzLmVudi5EUklaWkxFX0lETEVfVElNRU9VVF9NSUxMSVMsIGRlZmF1bHRJZGxlVGltZW91dCksXG4gICAgY29ubmVjdGlvblRpbWVvdXRNaWxsaXM6IHBhcnNlSW50RW52KHByb2Nlc3MuZW52LkRSSVpaTEVfQ09OTkVDVElPTl9USU1FT1VUX01JTExJUywgNTAwMCksXG4gIH07XG59XG5cbi8qKlxuICogRGF0YWJhc2UgY29ubmVjdGlvbiBzdGF0aXN0aWNzIGZvciBtb25pdG9yaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ29ubmVjdGlvblN0YXRzIHtcbiAgdG90YWxDb3VudDogbnVtYmVyO1xuICBpZGxlQ291bnQ6IG51bWJlcjtcbiAgd2FpdGluZ0NvdW50OiBudW1iZXI7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgY29uZmlndXJpbmcgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDb25uZWN0aW9uT3B0aW9ucyB7XG4gIC8qKiBXaGV0aGVyIHRvIGVuYWJsZSBjb25uZWN0aW9uIGxvZ2dpbmcgKi9cbiAgZW5hYmxlTG9nZ2luZz86IGJvb2xlYW47XG5cbiAgLyoqIFdoZXRoZXIgdG8gYXV0b21hdGljYWxseSByZXRyeSBmYWlsZWQgY29ubmVjdGlvbnMgKi9cbiAgZW5hYmxlQXV0b1JldHJ5PzogYm9vbGVhbjtcblxuICAvKiogTWF4aW11bSBudW1iZXIgb2YgY29ubmVjdGlvbiByZXRyeSBhdHRlbXB0cyAqL1xuICBtYXhSZXRyaWVzPzogbnVtYmVyO1xuXG4gIC8qKiBEZWxheSBiZXR3ZWVuIHJldHJ5IGF0dGVtcHRzIGluIG1pbGxpc2Vjb25kcyAqL1xuICByZXRyeURlbGF5PzogbnVtYmVyO1xuXG4gIC8qKiBTU0wgY29uZmlndXJhdGlvbiAqL1xuICBzc2w/OiBib29sZWFuIHwgeyByZWplY3RVbmF1dGhvcml6ZWQ6IGJvb2xlYW4gfTtcbn1cblxuLyoqXG4gKiBTaW5nbGV0b24gZGF0YWJhc2UgY29ubmVjdGlvbiBjbGFzcy5cbiAqIE1hbmFnZXMgUG9zdGdyZVNRTCBjb25uZWN0aW9uIHBvb2xpbmcgYW5kIERyaXp6bGUgT1JNIGluc3RhbmNlLlxuICpcbiAqIEZlYXR1cmVzOlxuICogLSBTaW5nbGV0b24gcGF0dGVybiBmb3Igc2luZ2xlIGNvbm5lY3Rpb24gcG9vbFxuICogLSBBdXRvbWF0aWMgY29ubmVjdGlvbiByZXRyeSB3aXRoIGJhY2tvZmZcbiAqIC0gQ29ubmVjdGlvbiBoZWFsdGggbW9uaXRvcmluZ1xuICogLSBHcmFjZWZ1bCBzaHV0ZG93biBoYW5kbGluZ1xuICogLSBDb21wcmVoZW5zaXZlIGVycm9yIGhhbmRsaW5nXG4gKiAtIENvbm5lY3Rpb24gc3RhdGlzdGljcyB0cmFja2luZ1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBDb25uZWN0aW9uIH0gZnJvbSAnLi9jb25uZWN0aW9uJztcbiAqXG4gKiBjb25zdCBjb25uZWN0aW9uID0gQ29ubmVjdGlvbi5nZXRJbnN0YW5jZSgpO1xuICogY29uc3QgcGx1Z2lucyA9IGF3YWl0IGNvbm5lY3Rpb24uZGIuc2VsZWN0KCkuZnJvbShzY2hlbWEucGx1Z2luKTtcbiAqXG4gKiAvLyBEdXJpbmcgc2h1dGRvd25cbiAqIGF3YWl0IGNvbm5lY3Rpb24uY2xvc2UoKTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgQ29ubmVjdGlvbiB7XG4gIHByaXZhdGUgc3RhdGljIGluc3RhbmNlOiBDb25uZWN0aW9uIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqXG4gICAqIERyaXp6bGUgT1JNIGRhdGFiYXNlIGluc3RhbmNlIHdpdGggc2NoZW1hXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZGI6IFJldHVyblR5cGU8dHlwZW9mIGRyaXp6bGU+O1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgcG9vbDogUG9vbDtcbiAgcHJpdmF0ZSByZWFkb25seSBvcHRpb25zOiBSZXF1aXJlZDxDb25uZWN0aW9uT3B0aW9ucz47XG4gIHByaXZhdGUgcmVhZG9ubHkgcmV0cnlTdHJhdGVneTogQ29ubmVjdGlvblJldHJ5U3RyYXRlZ3k7XG4gIHByaXZhdGUgaXNTaHV0dGluZ0Rvd24gPSBmYWxzZTtcblxuICAvKipcbiAgICogUHJpdmF0ZSBjb25zdHJ1Y3RvciB0byBlbmZvcmNlIHNpbmdsZXRvbiBwYXR0ZXJuLlxuICAgKiBJbml0aWFsaXplcyBQb3N0Z3JlU1FMIGNvbm5lY3Rpb24gcG9vbCBhbmQgRHJpenpsZSBPUk0gaW5zdGFuY2UuXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9uYWwgY29uZmlndXJhdGlvbiBmb3IgdGhlIGNvbm5lY3Rpb25cbiAgICogQHRocm93cyB7RXJyb3J9IElmIGRhdGFiYXNlIGluaXRpYWxpemF0aW9uIGZhaWxzIGFmdGVyIGFsbCByZXRyaWVzXG4gICAqL1xuICBwcml2YXRlIGNvbnN0cnVjdG9yKG9wdGlvbnM6IENvbm5lY3Rpb25PcHRpb25zID0ge30pIHtcbiAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICBlbmFibGVMb2dnaW5nOiBvcHRpb25zLmVuYWJsZUxvZ2dpbmcgPz8gdHJ1ZSxcbiAgICAgIGVuYWJsZUF1dG9SZXRyeTogb3B0aW9ucy5lbmFibGVBdXRvUmV0cnkgPz8gdHJ1ZSxcbiAgICAgIG1heFJldHJpZXM6IG9wdGlvbnMubWF4UmV0cmllcyA/PyBwYXJzZUludChwcm9jZXNzLmVudi5EQl9NQVhfUkVUUklFUyB8fCAnMycsIDEwKSxcbiAgICAgIHJldHJ5RGVsYXk6IG9wdGlvbnMucmV0cnlEZWxheSA/PyBwYXJzZUludChwcm9jZXNzLmVudi5EQl9SRVRSWV9ERUxBWV9NUyB8fCAnMTAwMCcsIDEwKSxcbiAgICAgIC8vIEVudi1kcml2ZW4gVExTOiBvbi1ieS1kZWZhdWx0IGluIHByb2R1Y3Rpb24sIGVudi1lbmFibGVhYmxlIGVsc2V3aGVyZS5cbiAgICAgIC8vIGdldEluc3RhbmNlKCkgaXMgbm9ybWFsbHkgY2FsbGVkIHdpdGggbm8gb3B0aW9ucywgc28gdGhpcyBpcyB0aGUgb25seVxuICAgICAgLy8gcGxhY2UgcHJvZHVjdGlvbiBSRFMgY29ubmVjdGlvbnMgcGljayB1cCBTU0wgKGZvcm1lcmx5IGhhcmQtY29kZWQgb2ZmKS5cbiAgICAgIHNzbDogZ2V0U3NsQ29uZmlnKG9wdGlvbnMuc3NsKSxcbiAgICB9O1xuXG4gICAgLy8gSW5pdGlhbGl6ZSByZXRyeSBzdHJhdGVneVxuICAgIHRoaXMucmV0cnlTdHJhdGVneSA9IG5ldyBDb25uZWN0aW9uUmV0cnlTdHJhdGVneSh7XG4gICAgICBtYXhSZXRyaWVzOiB0aGlzLm9wdGlvbnMubWF4UmV0cmllcyxcbiAgICAgIGJhc2VEZWxheTogdGhpcy5vcHRpb25zLnJldHJ5RGVsYXksXG4gICAgfSk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgY29uZmlnID0gZ2V0RGF0YWJhc2VDb25maWcoKTtcblxuICAgICAgY29uc3QgcG9vbENvbmZpZzogUG9vbENvbmZpZyA9IHtcbiAgICAgICAgaG9zdDogY29uZmlnLmhvc3QsXG4gICAgICAgIHBvcnQ6IGNvbmZpZy5wb3J0LFxuICAgICAgICBkYXRhYmFzZTogY29uZmlnLmRhdGFiYXNlLFxuICAgICAgICB1c2VyOiBjb25maWcudXNlcixcbiAgICAgICAgcGFzc3dvcmQ6IGNvbmZpZy5wYXNzd29yZCxcbiAgICAgICAgbWF4OiBjb25maWcubWF4UG9vbFNpemUsXG4gICAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBjb25maWcuaWRsZVRpbWVvdXRNaWxsaXMsXG4gICAgICAgIGNvbm5lY3Rpb25UaW1lb3V0TWlsbGlzOiBjb25maWcuY29ubmVjdGlvblRpbWVvdXRNaWxsaXMsXG4gICAgICAgIHNzbDogdGhpcy5vcHRpb25zLnNzbCxcbiAgICAgICAgYWxsb3dFeGl0T25JZGxlOiB0cnVlLFxuICAgICAgfTtcblxuICAgICAgdGhpcy5wb29sID0gbmV3IFBvb2wocG9vbENvbmZpZyk7XG4gICAgICB0aGlzLnNldHVwRXZlbnRIYW5kbGVycygpO1xuXG4gICAgICB0aGlzLmRiID0gZHJpenpsZSh0aGlzLnBvb2wsIHsgc2NoZW1hIH0pO1xuXG4gICAgICBpZiAodGhpcy5vcHRpb25zLmVuYWJsZUxvZ2dpbmcpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ0RhdGFiYXNlIGNvbm5lY3Rpb24gaW5pdGlhbGl6ZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgICAgIHRoaXMubG9nQ29ubmVjdGlvbkNvbmZpZyhwb29sQ29uZmlnKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gaW5pdGlhbGl6ZSBkYXRhYmFzZSBjb25uZWN0aW9uOicsIGVycm9yKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRGF0YWJhc2UgaW5pdGlhbGl6YXRpb24gZmFpbGVkJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIHNpbmdsZXRvbiBpbnN0YW5jZSBvZiB0aGUgQ29ubmVjdGlvbiBjbGFzcy5cbiAgICogQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSBpZiBvbmUgZG9lc24ndCBleGlzdC5cbiAgICpcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25hbCBjb25maWd1cmF0aW9uIChvbmx5IHVzZWQgb24gZmlyc3QgY2FsbClcbiAgICogQHJldHVybnMgVGhlIHNpbmdsZXRvbiBDb25uZWN0aW9uIGluc3RhbmNlXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGdldEluc3RhbmNlKG9wdGlvbnM/OiBDb25uZWN0aW9uT3B0aW9ucyk6IENvbm5lY3Rpb24ge1xuICAgIGlmICghQ29ubmVjdGlvbi5pbnN0YW5jZSkge1xuICAgICAgQ29ubmVjdGlvbi5pbnN0YW5jZSA9IG5ldyBDb25uZWN0aW9uKG9wdGlvbnMpO1xuICAgIH1cbiAgICByZXR1cm4gQ29ubmVjdGlvbi5pbnN0YW5jZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldHMgdGhlIHNpbmdsZXRvbiBpbnN0YW5jZS5cbiAgICogVXNlZnVsIGZvciB0ZXN0aW5nIG9yIHJlY29uZmlndXJpbmcgdGhlIGNvbm5lY3Rpb24uXG4gICAqXG4gICAqIEBwYXJhbSBjbG9zZUV4aXN0aW5nIC0gV2hldGhlciB0byBjbG9zZSBleGlzdGluZyBjb25uZWN0aW9uIGJlZm9yZSByZXNldFxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBhc3luYyByZXNldChjbG9zZUV4aXN0aW5nOiBib29sZWFuID0gdHJ1ZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChDb25uZWN0aW9uLmluc3RhbmNlICYmIGNsb3NlRXhpc3RpbmcpIHtcbiAgICAgIGF3YWl0IENvbm5lY3Rpb24uaW5zdGFuY2UuY2xvc2UoKTtcbiAgICB9XG4gICAgQ29ubmVjdGlvbi5pbnN0YW5jZSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogVGVzdHMgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAgICpcbiAgICogQHJldHVybnMgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgY29ubmVjdGlvbiBpcyBoZWFsdGh5XG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdGVzdENvbm5lY3Rpb24oKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNsaWVudCA9IGF3YWl0IHRoaXMucG9vbC5jb25uZWN0KCk7XG4gICAgICAvLyBSZWxlYXNlIGV2ZW4gaWYgdGhlIHByb2JlIHF1ZXJ5IHRocm93cyDigJQgb3RoZXJ3aXNlIGEgZmxha3kgREIgKHRoZSBleGFjdFxuICAgICAgLy8gY2FzZSB0aGlzIGNoZWNrIGV4aXN0cyBmb3IpIGxlYWtzIGEgcG9vbGVkIGNsaWVudCBlYWNoIGZhaWx1cmUgYW5kIGNhblxuICAgICAgLy8gZXhoYXVzdCB0aGUgcG9vbCBvbiB0aGUgcmV0cnkgcGF0aC5cbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNsaWVudC5xdWVyeSgnU0VMRUNUIDEnKTtcbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMb2dnaW5nKSB7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oJ0RhdGFiYXNlIGNvbm5lY3Rpb24gdGVzdCBzdWNjZXNzZnVsJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdC5yb3dzLmxlbmd0aCA+IDA7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBjbGllbnQucmVsZWFzZSgpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0RhdGFiYXNlIGNvbm5lY3Rpb24gdGVzdCBmYWlsZWQ6JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIGNvbm5lY3Rpb24gcG9vbCBzdGF0aXN0aWNzXG4gICAqXG4gICAqIEByZXR1cm5zIEN1cnJlbnQgY29ubmVjdGlvbiBwb29sIHN0YXRpc3RpY3NcbiAgICovXG4gIHB1YmxpYyBnZXRTdGF0cygpOiBDb25uZWN0aW9uU3RhdHMge1xuICAgIHJldHVybiB7XG4gICAgICB0b3RhbENvdW50OiB0aGlzLnBvb2wudG90YWxDb3VudCxcbiAgICAgIGlkbGVDb3VudDogdGhpcy5wb29sLmlkbGVDb3VudCxcbiAgICAgIHdhaXRpbmdDb3VudDogdGhpcy5wb29sLndhaXRpbmdDb3VudCxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyB0aGUgZGF0YWJhc2UgY29ubmVjdGlvbiBwb29sIGdyYWNlZnVsbHkuXG4gICAqIFNob3VsZCBiZSBjYWxsZWQgZHVyaW5nIGFwcGxpY2F0aW9uIHNodXRkb3duLlxuICAgKlxuICAgKiBAcGFyYW0gdGltZW91dCAtIE1heGltdW0gdGltZSB0byB3YWl0IGZvciBjb25uZWN0aW9ucyB0byBjbG9zZSAobXMpXG4gICAqIEByZXR1cm5zIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHBvb2wgaXMgY2xvc2VkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgY2xvc2UodGltZW91dDogbnVtYmVyID0gcGFyc2VJbnRFbnYocHJvY2Vzcy5lbnYuREJfQ0xPU0VfVElNRU9VVF9NUywgNTAwMCkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5pc1NodXR0aW5nRG93bikge1xuICAgICAgbG9nZ2VyLndhcm4oJ0Nvbm5lY3Rpb24gaXMgYWxyZWFkeSBzaHV0dGluZyBkb3duJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5pc1NodXR0aW5nRG93biA9IHRydWU7XG5cbiAgICB0cnkge1xuICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMb2dnaW5nKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdDbG9zaW5nIGRhdGFiYXNlIGNvbm5lY3Rpb24gcG9vbC4uLicpO1xuICAgICAgICBjb25zdCBzdGF0cyA9IHRoaXMuZ2V0U3RhdHMoKTtcbiAgICAgICAgbG9nZ2VyLmluZm8oYFBvb2wgc3RhdHMgLSBUb3RhbDogJHtzdGF0cy50b3RhbENvdW50fSwgSWRsZTogJHtzdGF0cy5pZGxlQ291bnR9LCBXYWl0aW5nOiAke3N0YXRzLndhaXRpbmdDb3VudH1gKTtcbiAgICAgIH1cblxuICAgICAgLy8gU2V0IGEgdGltZW91dCBmb3IgZ3JhY2VmdWwgc2h1dGRvd25cbiAgICAgIGNvbnN0IGNsb3NlUHJvbWlzZSA9IHRoaXMucG9vbC5lbmQoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXRQcm9taXNlID0gbmV3IFByb21pc2UoKF8sIHJlamVjdCkgPT5cbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiByZWplY3QobmV3IEVycm9yKCdDb25uZWN0aW9uIGNsb3NlIHRpbWVvdXQnKSksIHRpbWVvdXQpLFxuICAgICAgKTtcblxuICAgICAgYXdhaXQgUHJvbWlzZS5yYWNlKFtjbG9zZVByb21pc2UsIHRpbWVvdXRQcm9taXNlXSk7XG5cbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlTG9nZ2luZykge1xuICAgICAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiBjbG9zZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgY2xvc2luZyBkYXRhYmFzZSBjb25uZWN0aW9uOicsIGVycm9yKTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLmlzU2h1dHRpbmdEb3duID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyBpZiB0aGUgY29ubmVjdGlvbiBpcyBzaHV0dGluZyBkb3duXG4gICAqXG4gICAqIEByZXR1cm5zIHRydWUgaWYgY29ubmVjdGlvbiBpcyBpbiBzaHV0ZG93biBzdGF0ZVxuICAgKi9cbiAgcHVibGljIGlzQ2xvc2luZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc1NodXR0aW5nRG93bjtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHVwIGV2ZW50IGhhbmRsZXJzIGZvciB0aGUgY29ubmVjdGlvbiBwb29sXG4gICAqL1xuICBwcml2YXRlIHNldHVwRXZlbnRIYW5kbGVycygpOiB2b2lkIHtcbiAgICB0aGlzLnBvb2wub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgbG9nZ2VyLmVycm9yKCdVbmV4cGVjdGVkIGVycm9yIG9uIGlkbGUgY2xpZW50OicsIGVycik7XG5cbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuZW5hYmxlQXV0b1JldHJ5ICYmIHRoaXMucmV0cnlTdHJhdGVneS5nZXRBdHRlbXB0cygpIDwgdGhpcy5vcHRpb25zLm1heFJldHJpZXMpIHtcbiAgICAgICAgdm9pZCB0aGlzLnJldHJ5U3RyYXRlZ3kuaGFuZGxlQ29ubmVjdGlvbkVycm9yKGVyciwgKCkgPT4gdGhpcy50ZXN0Q29ubmVjdGlvbigpKS5jYXRjaCgocmV0cnlFcnIpID0+IHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoJ0Nvbm5lY3Rpb24gcmV0cnkgZXJyb3I6JywgcmV0cnlFcnIpO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHRoaXMucG9vbC5vbignY29ubmVjdCcsICgpID0+IHtcbiAgICAgIHRoaXMucmV0cnlTdHJhdGVneS5yZXNldCgpOyAvLyBSZXNldCBvbiBzdWNjZXNzZnVsIGNvbm5lY3Rpb25cblxuICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMb2dnaW5nKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnTmV3IGRhdGFiYXNlIGNvbm5lY3Rpb24gZXN0YWJsaXNoZWQnKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHRoaXMucG9vbC5vbigncmVtb3ZlJywgKCkgPT4ge1xuICAgICAgaWYgKHRoaXMub3B0aW9ucy5lbmFibGVMb2dnaW5nKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnQ2xpZW50IHJlbW92ZWQgZnJvbSBwb29sJyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBjb25uZWN0aW9uIGNvbmZpZ3VyYXRpb24gKHNhbml0aXplZClcbiAgICovXG4gIHByaXZhdGUgbG9nQ29ubmVjdGlvbkNvbmZpZyhjb25maWc6IFBvb2xDb25maWcpOiB2b2lkIHtcbiAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgQ29uZmlndXJhdGlvbjonLCB7XG4gICAgICBob3N0OiBgJHtjb25maWcuaG9zdH06JHtjb25maWcucG9ydH1gLFxuICAgICAgZGF0YWJhc2U6IGNvbmZpZy5kYXRhYmFzZSxcbiAgICAgIHVzZXI6IGNvbmZpZy51c2VyLFxuICAgICAgbWF4UG9vbFNpemU6IGNvbmZpZy5tYXgsXG4gICAgICBpZGxlVGltZW91dE1zOiBjb25maWcuaWRsZVRpbWVvdXRNaWxsaXMsXG4gICAgICBjb25uZWN0aW9uVGltZW91dE1zOiBjb25maWcuY29ubmVjdGlvblRpbWVvdXRNaWxsaXMsXG4gICAgICBzc2w6IGNvbmZpZy5zc2wgPyAnZW5hYmxlZCcgOiAnZGlzYWJsZWQnLFxuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogU2luZ2xldG9uIGRhdGFiYXNlIGluc3RhbmNlIGZvciB1c2UgdGhyb3VnaG91dCB0aGUgYXBwbGljYXRpb24uXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IGRiIH0gZnJvbSAnLi9jb25uZWN0aW9uJztcbiAqXG4gKiAvLyBTZWxlY3QgcXVlcmllc1xuICogY29uc3QgcGx1Z2lucyA9IGF3YWl0IGRiLnNlbGVjdCgpLmZyb20oc2NoZW1hLnBsdWdpbik7XG4gKlxuICogLy8gSW5zZXJ0IHF1ZXJpZXNcbiAqIGF3YWl0IGRiLmluc2VydChzY2hlbWEucGx1Z2luKS52YWx1ZXMoeyBuYW1lOiAnbXktcGx1Z2luJyB9KTtcbiAqXG4gKiAvLyBUcmFuc2FjdGlvbnNcbiAqIGF3YWl0IGRiLnRyYW5zYWN0aW9uKGFzeW5jICh0eCkgPT4ge1xuICogICBhd2FpdCB0eC5pbnNlcnQoc2NoZW1hLnBsdWdpbikudmFsdWVzKHsgLi4uIH0pO1xuICogICBhd2FpdCB0eC51cGRhdGUoc2NoZW1hLnBsdWdpbikuc2V0KHsgLi4uIH0pO1xuICogfSk7XG4gKiBgYGBcbiAqL1xuXG4vLyBMYXp5IGluaXRpYWxpemF0aW9uIHRvIGF2b2lkIHJhY2UgY29uZGl0aW9uIG9uIG1vZHVsZSBsb2FkXG5sZXQgX2RiSW5zdGFuY2U6IFJldHVyblR5cGU8dHlwZW9mIGRyaXp6bGU+IHwgbnVsbCA9IG51bGw7XG5cbi8qKlxuICogR2V0IHRoZSBkYXRhYmFzZSBpbnN0YW5jZSB3aXRoIGxhenkgaW5pdGlhbGl6YXRpb25cbiAqIFRoaXMgYXZvaWRzIHRoZSByYWNlIGNvbmRpdGlvbiB3aGVyZSB0aGUgbW9kdWxlIGlzIGxvYWRlZCBiZWZvcmUgZW52aXJvbm1lbnQgaXMgY29uZmlndXJlZFxuICovXG5mdW5jdGlvbiBnZXREYkluc3RhbmNlKCk6IFJldHVyblR5cGU8dHlwZW9mIGRyaXp6bGU+IHtcbiAgaWYgKCFfZGJJbnN0YW5jZSkge1xuICAgIF9kYkluc3RhbmNlID0gQ29ubmVjdGlvbi5nZXRJbnN0YW5jZSgpLmRiO1xuICB9XG4gIHJldHVybiBfZGJJbnN0YW5jZTtcbn1cblxuLyoqXG4gKiBQcm94eS1iYXNlZCBsYXp5IGRhdGFiYXNlIGluc3RhbmNlXG4gKiBUaGUgYWN0dWFsIGNvbm5lY3Rpb24gaXMgb25seSBjcmVhdGVkIHdoZW4gZmlyc3QgYWNjZXNzZWRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgZGIgfSBmcm9tICcuL2Nvbm5lY3Rpb24nO1xuICpcbiAqIC8vIENvbm5lY3Rpb24gaXMgY3JlYXRlZCBoZXJlIG9uIGZpcnN0IHVzZSwgbm90IG9uIGltcG9ydFxuICogY29uc3QgcGx1Z2lucyA9IGF3YWl0IGRiLnNlbGVjdCgpLmZyb20oc2NoZW1hLnBsdWdpbik7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNvbnN0IGRiID0gbmV3IFByb3h5KHt9IGFzIFJldHVyblR5cGU8dHlwZW9mIGRyaXp6bGU+LCB7XG4gIGdldChfLCBwcm9wOiBzdHJpbmcgfCBzeW1ib2wpIHtcbiAgICBjb25zdCBpbnN0YW5jZSA9IGdldERiSW5zdGFuY2UoKTtcbiAgICBjb25zdCB2YWx1ZSA9IGluc3RhbmNlW3Byb3AgYXMga2V5b2YgdHlwZW9mIGluc3RhbmNlXTtcbiAgICAvLyBCaW5kIG1ldGhvZHMgdG8gdGhlIGluc3RhbmNlIHRvIHByZXNlcnZlICd0aGlzJyBjb250ZXh0XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgcmV0dXJuIHZhbHVlLmJpbmQoaW5zdGFuY2UpO1xuICAgIH1cbiAgICByZXR1cm4gdmFsdWU7XG4gIH0sXG59KTtcblxuLyoqXG4gKiBHZXRzIHRoZSBDb25uZWN0aW9uIGluc3RhbmNlIGZvciBhZHZhbmNlZCBvcGVyYXRpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IGdldENvbm5lY3Rpb24gfSBmcm9tICcuL2Nvbm5lY3Rpb24nO1xuICpcbiAqIGNvbnN0IGNvbm5lY3Rpb24gPSBnZXRDb25uZWN0aW9uKCk7XG4gKiBjb25zdCBzdGF0cyA9IGNvbm5lY3Rpb24uZ2V0U3RhdHMoKTtcbiAqIGNvbnNvbGUubG9nKGBBY3RpdmUgY29ubmVjdGlvbnM6ICR7c3RhdHMudG90YWxDb3VudH1gKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29ubmVjdGlvbigpOiBDb25uZWN0aW9uIHtcbiAgcmV0dXJuIENvbm5lY3Rpb24uZ2V0SW5zdGFuY2UoKTtcbn1cblxuLyoqXG4gKiBDbG9zZXMgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAqIFNob3VsZCBiZSBjYWxsZWQgZHVyaW5nIGFwcGxpY2F0aW9uIHNodXRkb3duXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IGNsb3NlQ29ubmVjdGlvbiB9IGZyb20gJy4vY29ubmVjdGlvbic7XG4gKlxuICogcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jICgpID0+IHtcbiAqICAgYXdhaXQgY2xvc2VDb25uZWN0aW9uKCk7XG4gKiAgIHByb2Nlc3MuZXhpdCgwKTtcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbG9zZUNvbm5lY3Rpb24oKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGNvbm5lY3Rpb24gPSBDb25uZWN0aW9uLmdldEluc3RhbmNlKCk7XG4gIGF3YWl0IGNvbm5lY3Rpb24uY2xvc2UoKTtcbiAgX2RiSW5zdGFuY2UgPSBudWxsOyAvLyBSZXNldCBsYXp5IGluc3RhbmNlXG59XG5cbi8qKlxuICogVGVzdHMgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAqXG4gKiBAcmV0dXJucyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiBjb25uZWN0aW9uIGlzIGhlYWx0aHlcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgdGVzdENvbm5lY3Rpb24gfSBmcm9tICcuL2Nvbm5lY3Rpb24nO1xuICpcbiAqIGlmIChhd2FpdCB0ZXN0Q29ubmVjdGlvbigpKSB7XG4gKiAgIGNvbnNvbGUubG9nKCdEYXRhYmFzZSBpcyByZWFkeScpO1xuICogfSBlbHNlIHtcbiAqICAgY29uc29sZS5lcnJvcignRGF0YWJhc2UgY29ubmVjdGlvbiBmYWlsZWQnKTtcbiAqICAgcHJvY2Vzcy5leGl0KDEpO1xuICogfVxuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB0ZXN0Q29ubmVjdGlvbigpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgY29ubmVjdGlvbiA9IENvbm5lY3Rpb24uZ2V0SW5zdGFuY2UoKTtcbiAgcmV0dXJuIGNvbm5lY3Rpb24udGVzdENvbm5lY3Rpb24oKTtcbn1cbiJdfQ==
@@ -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
+ }