ynkap-payment
Version:
Module de paiement Y-Nkap pour Angular - Intégration simple des paiements mobiles (Orange Money, MTN Mobile Money)
151 lines • 18.9 kB
JavaScript
import { Injectable } from '@angular/core';
import { timer, throwError, switchMap, tap } from 'rxjs';
import { YnkapError, ErrorCategory } from '../../error-handling/models/error.model';
import * as i0 from "@angular/core";
export class RetryService {
constructor() {
this.retryStates = new Map();
this.defaultRetryOptions = {
maxRetries: 3,
baseDelayMs: 1000,
maxDelayMs: 30000,
nonRetryableErrors: [
ErrorCategory.AUTHENTICATION,
ErrorCategory.AUTHORIZATION,
ErrorCategory.VALIDATION
]
};
}
/**
* Initialize retry state for a transaction
*/
initializeRetryState(transactionId, options) {
const retryOptions = { ...this.defaultRetryOptions, ...options };
const state = {
attemptNumber: 0,
maxRetries: retryOptions.maxRetries,
nextRetryDelayMs: retryOptions.baseDelayMs,
canRetry: true,
transactionId
};
this.retryStates.set(transactionId, state);
return state;
}
/**
* Get retry state for a transaction
*/
getRetryState(transactionId) {
return this.retryStates.get(transactionId) || null;
}
/**
* Check if an error is retryable
*/
isRetryable(error, options) {
const retryOptions = { ...this.defaultRetryOptions, ...options };
// Check if error explicitly says it's not retryable
if (error.retryable === false) {
return false;
}
// Check if error category is in non-retryable list
if (retryOptions.nonRetryableErrors?.includes(error.code)) {
return false;
}
// Network errors are usually retryable
if (error.code === ErrorCategory.NETWORK) {
return true;
}
// Server errors (5xx) are retryable
if (error.httpStatus && error.httpStatus >= 500) {
return true;
}
// Payment processing errors might be retryable
if (error.code === ErrorCategory.PAYMENT_PROCESSING) {
return true;
}
// Default to retryable if not explicitly set to false
return error.retryable === undefined || error.retryable === true;
}
/**
* Calculate next retry delay with exponential backoff
*/
calculateRetryDelay(attemptNumber, options) {
const retryOptions = { ...this.defaultRetryOptions, ...options };
// Exponential backoff: baseDelay * 2^(attempt-1)
const delay = retryOptions.baseDelayMs * Math.pow(2, attemptNumber - 1);
// Add some jitter to avoid thundering herd
const jitter = Math.random() * 0.1 * delay;
return Math.min(delay + jitter, retryOptions.maxDelayMs);
}
/**
* Update retry state after an attempt
*/
updateRetryState(transactionId, error) {
const state = this.retryStates.get(transactionId);
if (!state)
return null;
state.attemptNumber++;
state.lastError = error;
state.canRetry = state.attemptNumber < state.maxRetries && this.isRetryable(error);
if (state.canRetry) {
state.nextRetryDelayMs = this.calculateRetryDelay(state.attemptNumber);
}
return state;
}
/**
* Execute retry with proper delay and state management
*/
executeRetry(transactionId, retryFn, options) {
const state = this.getRetryState(transactionId);
if (!state || !state.canRetry) {
return throwError(() => state?.lastError || new YnkapError('RETRY_EXHAUSTED', 'Maximum retry attempts reached'));
}
const retryOptions = { ...this.defaultRetryOptions, ...options };
// Call onRetry callback if provided
if (retryOptions.onRetry && state.lastError) {
retryOptions.onRetry(state.lastError, state.attemptNumber + 1);
}
// Wait for the calculated delay, then execute retry
return timer(state.nextRetryDelayMs).pipe(switchMap(() => retryFn()), tap({
error: (error) => {
this.updateRetryState(transactionId, error);
}
}));
}
/**
* Clear retry state for a transaction
*/
clearRetryState(transactionId) {
this.retryStates.delete(transactionId);
}
/**
* Get human-readable retry information
*/
getRetryInfo(transactionId) {
const state = this.getRetryState(transactionId);
if (!state)
return '';
if (!state.canRetry) {
return 'Nombre maximum de tentatives atteint';
}
const remainingAttempts = state.maxRetries - state.attemptNumber;
const nextRetrySeconds = Math.ceil(state.nextRetryDelayMs / 1000);
return `${remainingAttempts} tentative(s) restante(s). Prochaine tentative dans ${nextRetrySeconds}s`;
}
/**
* Check if automatic retry should be attempted
*/
shouldAutoRetry(error) {
// Auto-retry only for network errors and server errors
return error.code === ErrorCategory.NETWORK ||
(error.httpStatus !== undefined && error.httpStatus >= 500);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RetryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RetryService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnkuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3lua2FwL3NyYy9saWIvcGF5bWVudC9yZXRyeS9yZXRyeS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFjLEtBQUssRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNyRSxPQUFPLEVBQUUsVUFBVSxFQUFnQixhQUFhLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQzs7QUFjbEcsTUFBTSxPQUFPLFlBQVk7SUFIekI7UUFJVSxnQkFBVyxHQUFHLElBQUksR0FBRyxFQUFzQixDQUFDO1FBRTVDLHdCQUFtQixHQUFpQjtZQUMxQyxVQUFVLEVBQUUsQ0FBQztZQUNiLFdBQVcsRUFBRSxJQUFJO1lBQ2pCLFVBQVUsRUFBRSxLQUFLO1lBQ2pCLGtCQUFrQixFQUFFO2dCQUNsQixhQUFhLENBQUMsY0FBYztnQkFDNUIsYUFBYSxDQUFDLGFBQWE7Z0JBQzNCLGFBQWEsQ0FBQyxVQUFVO2FBQ3pCO1NBQ0YsQ0FBQztLQWtLSDtJQWhLQzs7T0FFRztJQUNILG9CQUFvQixDQUFDLGFBQXFCLEVBQUUsT0FBK0I7UUFDekUsTUFBTSxZQUFZLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxHQUFHLE9BQU8sRUFBRSxDQUFDO1FBRWpFLE1BQU0sS0FBSyxHQUFlO1lBQ3hCLGFBQWEsRUFBRSxDQUFDO1lBQ2hCLFVBQVUsRUFBRSxZQUFZLENBQUMsVUFBVTtZQUNuQyxnQkFBZ0IsRUFBRSxZQUFZLENBQUMsV0FBVztZQUMxQyxRQUFRLEVBQUUsSUFBSTtZQUNkLGFBQWE7U0FDZCxDQUFDO1FBRUYsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsYUFBYSxFQUFFLEtBQUssQ0FB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QU8sc0NBQXNDLENBQUM7U0FDL0M7UUFFRCxNQUFNLGlCQUFpQixHQUFHLEtBQUssQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FBQztRQUNqRSxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQyxDQUFDO1FBRWxFLE9BQU8sR0FBRyxpQkFBaUIsdURBQXVELGdCQUFnQixHQUFHLENBQUM7SUFDeEcsQ0FBQztJQUVEOztPQUVHO0lBQ0gsZUFBZSxDQUFDLEtBQWlCO1FBQy9CLHVEQUF1RDtRQUN2RCxPQUFPLEtBQUssQ0FBQyxJQUFJLEtBQUssYUFBYSxDQUFDLE9BQU87WUFDcEMsQ0FBQyxLQUFLLENBQUMsVUFBVSxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsVUFBVSxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7K0dBN0tVLFlBQVk7bUhBQVosWUFBWSxjQUZYLE1BQU07OzRGQUVQLFlBQVk7a0JBSHhCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgdGltZXIsIHRocm93RXJyb3IsIHN3aXRjaE1hcCwgdGFwIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBZbmthcEVycm9yLCBSZXRyeU9wdGlvbnMsIEVycm9yQ2F0ZWdvcnkgfSBmcm9tICcuLi8uLi9lcnJvci1oYW5kbGluZy9tb2RlbHMvZXJyb3IubW9kZWwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFJldHJ5U3RhdGUge1xuICBhdHRlbXB0TnVtYmVyOiBudW1iZXI7XG4gIG1heFJldHJpZXM6IG51bWJlcjtcbiAgbmV4dFJldHJ5RGVsYXlNczogbnVtYmVyO1xuICBjYW5SZXRyeTogYm9vbGVhbjtcbiAgbGFzdEVycm9yPzogWW5rYXBFcnJvcjtcbiAgdHJhbnNhY3Rpb25JZD86IHN0cmluZztcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgUmV0cnlTZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZXRyeVN0YXRlcyA9IG5ldyBNYXA8c3RyaW5nLCBSZXRyeVN0YXRlPigpO1xuICBcbiAgcHJpdmF0ZSBkZWZhdWx0UmV0cnlPcHRpb25zOiBSZXRyeU9wdGlvbnMgPSB7XG4gICAgbWF4UmV0cmllczogMyxcbiAgICBiYXNlRGVsYXlNczogMTAwMCxcbiAgICBtYXhEZWxheU1zOiAzMDAwMCxcbiAgICBub25SZXRyeWFibGVFcnJvcnM6IFtcbiAgICAgIEVycm9yQ2F0ZWdvcnkuQVVUSEVOVElDQVRJT04sXG4gICAgICBFcnJvckNhdGVnb3J5LkFVVEhPUklaQVRJT04sXG4gICAgICBFcnJvckNhdGVnb3J5LlZBTElEQVRJT05cbiAgICBdXG4gIH07XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgcmV0cnkgc3RhdGUgZm9yIGEgdHJhbnNhY3Rpb25cbiAgICovXG4gIGluaXRpYWxpemVSZXRyeVN0YXRlKHRyYW5zYWN0aW9uSWQ6IHN0cmluZywgb3B0aW9ucz86IFBhcnRpYWw8UmV0cnlPcHRpb25zPik6IFJldHJ5U3RhdGUge1xuICAgIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHsgLi4udGhpcy5kZWZhdWx0UmV0cnlPcHRpb25zLCAuLi5vcHRpb25zIH07XG4gICAgXG4gICAgY29uc3Qgc3RhdGU6IFJldHJ5U3RhdGUgPSB7XG4gICAgICBhdHRlbXB0TnVtYmVyOiAwLFxuICAgICAgbWF4UmV0cmllczogcmV0cnlPcHRpb25zLm1heFJldHJpZXMsXG4gICAgICBuZXh0UmV0cnlEZWxheU1zOiByZXRyeU9wdGlvbnMuYmFzZURlbGF5TXMsXG4gICAgICBjYW5SZXRyeTogdHJ1ZSxcbiAgICAgIHRyYW5zYWN0aW9uSWRcbiAgICB9O1xuICAgIFxuICAgIHRoaXMucmV0cnlTdGF0ZXMuc2V0KHRyYW5zYWN0aW9uSWQsIHN0YXRlKTtcbiAgICByZXR1cm4gc3RhdGU7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHJldHJ5IHN0YXRlIGZvciBhIHRyYW5zYWN0aW9uXG4gICAqL1xuICBnZXRSZXRyeVN0YXRlKHRyYW5zYWN0aW9uSWQ6IHN0cmluZyk6IFJldHJ5U3RhdGUgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5yZXRyeVN0YXRlcy5nZXQodHJhbnNhY3Rpb25JZCkgfHwgbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhbiBlcnJvciBpcyByZXRyeWFibGVcbiAgICovXG4gIGlzUmV0cnlhYmxlKGVycm9yOiBZbmthcEVycm9yLCBvcHRpb25zPzogUGFydGlhbDxSZXRyeU9wdGlvbnM+KTogYm9vbGVhbiB7XG4gICAgY29uc3QgcmV0cnlPcHRpb25zID0geyAuLi50aGlzLmRlZmF1bHRSZXRyeU9wdGlvbnMsIC4uLm9wdGlvbnMgfTtcblxuICAgIC8vIENoZWNrIGlmIGVycm9yIGV4cGxpY2l0bHkgc2F5cyBpdCdzIG5vdCByZXRyeWFibGVcbiAgICBpZiAoZXJyb3IucmV0cnlhYmxlID09PSBmYWxzZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGlmIGVycm9yIGNhdGVnb3J5IGlzIGluIG5vbi1yZXRyeWFibGUgbGlzdFxuICAgIGlmIChyZXRyeU9wdGlvbnMubm9uUmV0cnlhYmxlRXJyb3JzPy5pbmNsdWRlcyhlcnJvci5jb2RlIGFzIEVycm9yQ2F0ZWdvcnkpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gTmV0d29yayBlcnJvcnMgYXJlIHVzdWFsbHkgcmV0cnlhYmxlXG4gICAgaWYgKGVycm9yLmNvZGUgPT09IEVycm9yQ2F0ZWdvcnkuTkVUV09SSykge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gU2VydmVyIGVycm9ycyAoNXh4KSBhcmUgcmV0cnlhYmxlXG4gICAgaWYgKGVycm9yLmh0dHBTdGF0dXMgJiYgZXJyb3IuaHR0cFN0YXR1cyA+PSA1MDApIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIFBheW1lbnQgcHJvY2Vzc2luZyBlcnJvcnMgbWlnaHQgYmUgcmV0cnlhYmxlXG4gICAgaWYgKGVycm9yLmNvZGUgPT09IEVycm9yQ2F0ZWdvcnkuUEFZTUVOVF9QUk9DRVNTSU5HKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvLyBEZWZhdWx0IHRvIHJldHJ5YWJsZSBpZiBub3QgZXhwbGljaXRseSBzZXQgdG8gZmFsc2VcbiAgICByZXR1cm4gZXJyb3IucmV0cnlhYmxlID09PSB1bmRlZmluZWQgfHwgZXJyb3IucmV0cnlhYmxlID09PSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBuZXh0IHJldHJ5IGRlbGF5IHdpdGggZXhwb25lbnRpYWwgYmFja29mZlxuICAgKi9cbiAgY2FsY3VsYXRlUmV0cnlEZWxheShhdHRlbXB0TnVtYmVyOiBudW1iZXIsIG9wdGlvbnM/OiBQYXJ0aWFsPFJldHJ5T3B0aW9ucz4pOiBudW1iZXIge1xuICAgIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHsgLi4udGhpcy5kZWZhdWx0UmV0cnlPcHRpb25zLCAuLi5vcHRpb25zIH07XG4gICAgXG4gICAgLy8gRXhwb25lbnRpYWwgYmFja29mZjogYmFzZURlbGF5ICogMl4oYXR0ZW1wdC0xKVxuICAgIGNvbnN0IGRlbGF5ID0gcmV0cnlPcHRpb25zLmJhc2VEZWxheU1zICogTWF0aC5wb3coMiwgYXR0ZW1wdE51bWJlciAtIDEpO1xuICAgIFxuICAgIC8vIEFkZCBzb21lIGppdHRlciB0byBhdm9pZCB0aHVuZGVyaW5nIGhlcmRcbiAgICBjb25zdCBqaXR0ZXIgPSBNYXRoLnJhbmRvbSgpICogMC4xICogZGVsYXk7XG4gICAgXG4gICAgcmV0dXJuIE1hdGgubWluKGRlbGF5ICsgaml0dGVyLCByZXRyeU9wdGlvbnMubWF4RGVsYXlNcyk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHJldHJ5IHN0YXRlIGFmdGVyIGFuIGF0dGVtcHRcbiAgICovXG4gIHVwZGF0ZVJldHJ5U3RhdGUodHJhbnNhY3Rpb25JZDogc3RyaW5nLCBlcnJvcjogWW5rYXBFcnJvcik6IFJldHJ5U3RhdGUgfCBudWxsIHtcbiAgICBjb25zdCBzdGF0ZSA9IHRoaXMucmV0cnlTdGF0ZXMuZ2V0KHRyYW5zYWN0aW9uSWQpO1xuICAgIGlmICghc3RhdGUpIHJldHVybiBudWxsO1xuICAgIFxuICAgIHN0YXRlLmF0dGVtcHROdW1iZXIrKztcbiAgICBzdGF0ZS5sYXN0RXJyb3IgPSBlcnJvcjtcbiAgICBzdGF0ZS5jYW5SZXRyeSA9IHN0YXRlLmF0dGVtcHROdW1iZXIgPCBzdGF0ZS5tYXhSZXRyaWVzICYmIHRoaXMuaXNSZXRyeWFibGUoZXJyb3IpO1xuICAgIFxuICAgIGlmIChzdGF0ZS5jYW5SZXRyeSkge1xuICAgICAgc3RhdGUubmV4dFJldHJ5RGVsYXlNcyA9IHRoaXMuY2FsY3VsYXRlUmV0cnlEZWxheShzdGF0ZS5hdHRlbXB0TnVtYmVyKTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHN0YXRlO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgcmV0cnkgd2l0aCBwcm9wZXIgZGVsYXkgYW5kIHN0YXRlIG1hbmFnZW1lbnRcbiAgICovXG4gIGV4ZWN1dGVSZXRyeTxUPihcbiAgICB0cmFuc2FjdGlvbklkOiBzdHJpbmcsIFxuICAgIHJldHJ5Rm46ICgpID0+IE9ic2VydmFibGU8VD4sXG4gICAgb3B0aW9ucz86IFBhcnRpYWw8UmV0cnlPcHRpb25zPlxuICApOiBPYnNlcnZhYmxlPFQ+IHtcbiAgICBjb25zdCBzdGF0ZSA9IHRoaXMuZ2V0UmV0cnlTdGF0ZSh0cmFuc2FjdGlvbklkKTtcbiAgICBcbiAgICBpZiAoIXN0YXRlIHx8ICFzdGF0ZS5jYW5SZXRyeSkge1xuICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gc3RhdGU/Lmxhc3RFcnJvciB8fCBuZXcgWW5rYXBFcnJvcihcbiAgICAgICAgJ1JFVFJZX0VYSEFVU1RFRCcsXG4gICAgICAgICdNYXhpbXVtIHJldHJ5IGF0dGVtcHRzIHJlYWNoZWQnXG4gICAgICApKTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgcmV0cnlPcHRpb25zID0geyAuLi50aGlzLmRlZmF1bHRSZXRyeU9wdGlvbnMsIC4uLm9wdGlvbnMgfTtcbiAgICBcbiAgICAvLyBDYWxsIG9uUmV0cnkgY2FsbGJhY2sgaWYgcHJvdmlkZWRcbiAgICBpZiAocmV0cnlPcHRpb25zLm9uUmV0cnkgJiYgc3RhdGUubGFzdEVycm9yKSB7XG4gICAgICByZXRyeU9wdGlvbnMub25SZXRyeShzdGF0ZS5sYXN0RXJyb3IsIHN0YXRlLmF0dGVtcHROdW1iZXIgKyAxKTtcbiAgICB9XG4gICAgXG4gICAgLy8gV2FpdCBmb3IgdGhlIGNhbGN1bGF0ZWQgZGVsYXksIHRoZW4gZXhlY3V0ZSByZXRyeVxuICAgIHJldHVybiB0aW1lcihzdGF0ZS5uZXh0UmV0cnlEZWxheU1zKS5waXBlKFxuICAgICAgc3dpdGNoTWFwKCgpID0+IHJldHJ5Rm4oKSksXG4gICAgICB0YXAoe1xuICAgICAgICBlcnJvcjogKGVycm9yKSA9PiB7XG4gICAgICAgICAgdGhpcy51cGRhdGVSZXRyeVN0YXRlKHRyYW5zYWN0aW9uSWQsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIHJldHJ5IHN0YXRlIGZvciBhIHRyYW5zYWN0aW9uXG4gICAqL1xuICBjbGVhclJldHJ5U3RhdGUodHJhbnNhY3Rpb25JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5yZXRyeVN0YXRlcy5kZWxldGUodHJhbnNhY3Rpb25JZCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGh1bWFuLXJlYWRhYmxlIHJldHJ5IGluZm9ybWF0aW9uXG4gICAqL1xuICBnZXRSZXRyeUluZm8odHJhbnNhY3Rpb25JZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBzdGF0ZSA9IHRoaXMuZ2V0UmV0cnlTdGF0ZSh0cmFuc2FjdGlvbklkKTtcbiAgICBpZiAoIXN0YXRlKSByZXR1cm4gJyc7XG4gICAgXG4gICAgaWYgKCFzdGF0ZS5jYW5SZXRyeSkge1xuICAgICAgcmV0dXJuICdOb21icmUgbWF4aW11bSBkZSB0ZW50YXRpdmVzIGF0dGVpbnQnO1xuICAgIH1cbiAgICBcbiAgICBjb25zdCByZW1haW5pbmdBdHRlbXB0cyA9IHN0YXRlLm1heFJldHJpZXMgLSBzdGF0ZS5hdHRlbXB0TnVtYmVyO1xuICAgIGNvbnN0IG5leHRSZXRyeVNlY29uZHMgPSBNYXRoLmNlaWwoc3RhdGUubmV4dFJldHJ5RGVsYXlNcyAvIDEwMDApO1xuICAgIFxuICAgIHJldHVybiBgJHtyZW1haW5pbmdBdHRlbXB0c30gdGVudGF0aXZlKHMpIHJlc3RhbnRlKHMpLiBQcm9jaGFpbmUgdGVudGF0aXZlIGRhbnMgJHtuZXh0UmV0cnlTZWNvbmRzfXNgO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGF1dG9tYXRpYyByZXRyeSBzaG91bGQgYmUgYXR0ZW1wdGVkXG4gICAqL1xuICBzaG91bGRBdXRvUmV0cnkoZXJyb3I6IFlua2FwRXJyb3IpOiBib29sZWFuIHtcbiAgICAvLyBBdXRvLXJldHJ5IG9ubHkgZm9yIG5ldHdvcmsgZXJyb3JzIGFuZCBzZXJ2ZXIgZXJyb3JzXG4gICAgcmV0dXJuIGVycm9yLmNvZGUgPT09IEVycm9yQ2F0ZWdvcnkuTkVUV09SSyB8fCBcbiAgICAgICAgICAgKGVycm9yLmh0dHBTdGF0dXMgIT09IHVuZGVmaW5lZCAmJiBlcnJvci5odHRwU3RhdHVzID49IDUwMCk7XG4gIH1cbn1cbiJdfQ==