{"version":3,"file":"bundle.min.js","sources":["js/db.js","js/ui.js","js/app.js"],"sourcesContent":["const dbName = 'WebhookTesterDb';\r\nconst dbVersion = 1;\r\nconst storeName = 'webhooksDb';\r\n\r\nlet db;\r\n\r\nconst openDB = () => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, dbVersion);\r\n\r\n request.onerror = (event) => reject(\"IndexedDB error: \" + event.target.error);\r\n\r\n request.onsuccess = (event) => {\r\n db = event.target.result;\r\n resolve(db);\r\n };\r\n\r\n request.onupgradeneeded = (event) => {\r\n db = event.target.result;\r\n db.createObjectStore(storeName, { keyPath: 'slug' });\r\n };\r\n });\r\n};\r\n\r\nconst saveWebhooksInDb = (webhooks) => {\r\n return new Promise((resolve, reject) => {\r\n const transaction = db.transaction([storeName], 'readwrite');\r\n const store = transaction.objectStore(storeName);\r\n\r\n webhooks.forEach(webhook => {\r\n store.put(webhook);\r\n });\r\n\r\n transaction.oncomplete = () => resolve();\r\n transaction.onerror = (event) => reject(event.target.error);\r\n });\r\n};\r\n\r\nconst getWebhooksFromDb = () => {\r\n return new Promise((resolve, reject) => {\r\n const transaction = db.transaction([storeName], 'readonly');\r\n const store = transaction.objectStore(storeName);\r\n const request = store.getAll();\r\n\r\n request.onsuccess = () => resolve(request.result);\r\n request.onerror = (event) => reject(event.target.error);\r\n });\r\n};\r\n\r\nconst removeWebhookFromDb = (slug) => {\r\n return new Promise((resolve, reject) => {\r\n const transaction = db.transaction([storeName], 'readwrite');\r\n const store = transaction.objectStore(storeName);\r\n const request = store.delete(slug);\r\n\r\n request.onsuccess = () => resolve();\r\n request.onerror = (event) => reject(event.target.error);\r\n });\r\n};\r\n\r\nexport { openDB, saveWebhooksInDb, getWebhooksFromDb, removeWebhookFromDb };","import { saveWebhooksInDb, getWebhooksFromDb, removeWebhookFromDb } from './db.js';\r\n\r\nlet webhooks = [];\r\nlet signalRConnection = null;\r\nlet baseUrl = null;\r\n\r\nconst webhookList = document.getElementById('webhook-list');\r\nconst requestList = document.getElementById('request-list');\r\nconst requestHeadersTable = document.getElementById('request-headers');\r\nconst requestBodyPre = document.getElementById('request-body-raw');\r\nconst waitingForEvents = document.getElementById('waiting-for-events');\r\nconst requestsContainer = document.getElementById('requests-container');\r\nconst copyPopup = document.getElementById('copy-popup');\r\nconst curlExample = document.getElementById('curl-example');\r\nconst powershellExample = document.getElementById('powershell-example');\r\nconst copyExampleButton = document.getElementById('copy-example');\r\nconst copyActiveWebhookUrlButton = document.getElementById('copy-active-webhook-url');\r\nconst activeWebhookUrl = document.getElementById('active-webhook-url');\r\nconst copyBodyButton = document.getElementById('copy-body');\r\n\r\nconst setSignalRConnection = (connection) => {\r\n signalRConnection = connection;\r\n};\r\n\r\nconst setBaseUrl = (url) => {\r\n baseUrl = url;\r\n};\r\n\r\nconst initWebhooks = async () => {\r\n webhooks = await getWebhooksFromDb();\r\n if (webhooks.length === 0) {\r\n await createWebhook();\r\n } else {\r\n // send existing webhooks to the server\r\n if (signalRConnection) {\r\n await signalRConnection.invoke('CreateWebhooksAsync', webhooks.map(w => w.slug));\r\n }\r\n }\r\n renderWebhooks(webhooks);\r\n showWebhookDetails(0, webhooks);\r\n};\r\n\r\nconst createWebhook = async () => {\r\n const slug = generateWebhookSlug();\r\n const url = `${baseUrl}/webhooks/${slug}`;\r\n const newWebhook = { url, slug, requests: [] };\r\n\r\n if (signalRConnection) {\r\n try {\r\n await signalRConnection.invoke('CreateWebhooksAsync', [slug]);\r\n webhooks.push(newWebhook);\r\n await saveWebhooksInDb(webhooks);\r\n renderWebhooks(webhooks);\r\n showWebhookDetails(webhooks.length - 1, webhooks);\r\n } catch (error) {\r\n console.error('Failed to create webhook on server:', error);\r\n alert('Failed to create webhook. Please try again.');\r\n }\r\n } else {\r\n console.error('SignalR connection not available');\r\n alert('Cannot create webhook. Connection to server not available.');\r\n }\r\n};\r\n\r\nconst generateWebhookSlug = () => {\r\n const colors = [\r\n 'red', 'blue', 'green', 'yellow', 'purple', 'orange', 'pink', 'grey', 'white',\r\n 'black', 'brown', 'cyan', 'magenta', 'lime', 'teal', 'indigo', 'violet', 'gold',\r\n 'silver', 'bronze', 'maroon', 'navy', 'olive', 'aqua', 'turquoise', 'lavender'\r\n ];\r\n\r\n const animals = [\r\n 'dog', 'hawk', 'tiger', 'wolf', 'panther', 'dragon', 'falcon', 'panda', 'cat',\r\n 'bear', 'fox', 'mouse', 'rat', 'bird', 'pig', 'chicken', 'duck', 'elephant',\r\n 'lion', 'giraffe', 'zebra', 'koala', 'rhino', 'hippo', 'monkey', 'gorilla',\r\n 'penguin', 'dolphin', 'whale', 'shark', 'octopus', 'eagle', 'owl', 'bat',\r\n 'squirrel', 'rabbit', 'deer', 'moose', 'bison', 'coyote', 'leopard', 'jaguar'\r\n ];\r\n\r\n const number = Math.floor(Math.random() * 1000000); // 6-digit number (0 to 999999)\r\n const randomColor = colors[Math.floor(Math.random() * colors.length)];\r\n const randomAnimal = animals[Math.floor(Math.random() * animals.length)];\r\n\r\n return `${randomColor}-${randomAnimal}-${number.toString().padStart(6, '0')}`;\r\n};\r\n\r\nconst addRequestToWebhook = (slug, request) => {\r\n const webhook = webhooks.find(w => w.slug === slug);\r\n if (webhook) {\r\n webhook.requests.push(request);\r\n saveWebhooksInDb(webhooks);\r\n showWebhookDetails(webhooks.indexOf(webhook), webhooks);\r\n }\r\n};\r\n\r\nconst removeWebhook = async (slug) => {\r\n webhooks = webhooks.filter(webhook => webhook.slug !== slug);\r\n await removeWebhookFromDb(slug);\r\n renderWebhooks(webhooks);\r\n if (webhooks.length > 0) {\r\n showWebhookDetails(0, webhooks);\r\n } else {\r\n showWaitingForEvents();\r\n }\r\n};\r\n\r\nconst renderWebhooks = (webhooks) => {\r\n webhookList.innerHTML = '';\r\n webhooks.forEach((webhook, index) => {\r\n const li = document.createElement('li');\r\n li.innerHTML = `\r\n ${webhook.slug}\r\n \r\n `;\r\n li.onclick = () => showWebhookDetails(index, webhooks);\r\n li.querySelector('.remove-btn').onclick = (event) => {\r\n event.stopPropagation();\r\n removeWebhook(webhook.slug);\r\n };\r\n webhookList.appendChild(li);\r\n });\r\n};\r\n\r\nconst showWebhookDetails = (index, webhooks) => {\r\n const selectedWebhook = webhooks[index];\r\n updateExampleCommands(selectedWebhook.url);\r\n activeWebhookUrl.textContent = selectedWebhook.url;\r\n\r\n if (selectedWebhook.requests.length === 0) {\r\n showWaitingForEvents();\r\n } else {\r\n showRequests(selectedWebhook);\r\n showRequestDetails(selectedWebhook.requests[selectedWebhook.requests.length - 1]);\r\n }\r\n};\r\n\r\nconst showWaitingForEvents = () => {\r\n waitingForEvents.style.display = 'flex';\r\n requestsContainer.style.display = 'none';\r\n};\r\n\r\nconst showRequests = (webhook) => {\r\n waitingForEvents.style.display = 'none';\r\n requestsContainer.style.display = 'flex';\r\n\r\n requestList.innerHTML = '';\r\n webhook.requests.forEach((request, index) => {\r\n const li = document.createElement('li');\r\n const date = new Date(request.timestamp);\r\n const formattedDate = date.toLocaleDateString('en-US', {\r\n year: 'numeric',\r\n month: 'short',\r\n day: 'numeric'\r\n });\r\n const formattedTime = date.toLocaleTimeString('en-US', {\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit'\r\n });\r\n li.innerHTML = `\r\n ${request.method}\r\n \r\n `;\r\n li.onclick = () => showRequestDetails(request);\r\n requestList.appendChild(li);\r\n });\r\n};\r\n\r\nconst showRequestDetails = (request) => {\r\n renderHeadersTable(request.headers);\r\n const rawBody = request.body;\r\n const getHeader = (headers, name) => {\r\n const lowercaseName = name.toLowerCase();\r\n const header = Object.keys(headers).find(h => h.toLowerCase() === lowercaseName);\r\n return header ? headers[header] : null;\r\n };\r\n const contentType = (getHeader(request.headers, 'content-type') || '').toLowerCase();\r\n const rawTab = document.getElementById('request-body-raw');\r\n const formattedTab = document.getElementById('request-body-formatted');\r\n const rawTabButton = document.querySelector('.body-tabs .tab-btn[data-tab=\"raw\"]');\r\n const formattedTabButton = document.querySelector('.body-tabs .tab-btn[data-tab=\"formatted\"]');\r\n rawTab.textContent = rawBody;\r\n let formattedContent = '';\r\n let displayFormatted = true;\r\n let tabName = 'Formatted';\r\n if (/^application\\/json\\b/.test(contentType)) {\r\n try {\r\n const jsonBody = JSON.parse(rawBody);\r\n formattedContent = formatJSON(jsonBody);\r\n tabName = 'JSON';\r\n } catch (e) {\r\n formattedContent = '
Invalid JSON
';\r\n tabName = 'Invalid JSON';\r\n }\r\n } else if (/^(application|text)\\/xml\\b/.test(contentType)) {\r\n formattedContent = formatXML(rawBody);\r\n tabName = 'XML';\r\n } else if (/^application\\/x-www-form-urlencoded\\b/.test(contentType)) {\r\n formattedContent = formatFormData(rawBody);\r\n tabName = 'Form Data';\r\n } else if (/^text\\/plain\\b/.test(contentType)) {\r\n formattedContent = `${escapeHTML(rawBody)}`;\r\n tabName = 'Plain Text';\r\n } else {\r\n displayFormatted = false;\r\n }\r\n formattedTab.innerHTML = formattedContent;\r\n formattedTabButton.textContent = tabName;\r\n formattedTabButton.style.display = displayFormatted ? 'inline-block' : 'none';\r\n\r\n const isFormattedTabActive = formattedTabButton.classList.contains('active');\r\n if (!displayFormatted && isFormattedTabActive) {\r\n rawTabButton.click();\r\n }\r\n\r\n const bodyTabs = document.querySelectorAll('.body-tabs .tab-btn');\r\n const bodyContents = document.querySelectorAll('.body-tab-content .code-example');\r\n bodyTabs.forEach(viewerTab => {\r\n viewerTab.addEventListener('click', () => {\r\n bodyTabs.forEach(t => t.classList.remove('active'));\r\n bodyContents.forEach(c => c.classList.remove('active'));\r\n viewerTab.classList.add('active');\r\n document.querySelector(`.body-tab-content .code-example#request-body-${viewerTab.dataset.tab}`).classList.add('active');\r\n });\r\n });\r\n};\r\n\r\nconst formatXML = (xml) => {\r\n const parser = new DOMParser();\r\n const xmlDoc = parser.parseFromString(xml, \"text/xml\");\r\n const serializer = new XMLSerializer();\r\n let formatted = '';\r\n let indent = 0;\r\n\r\n const format = (node) => {\r\n if (node.nodeType === Node.ELEMENT_NODE) {\r\n const nodeName = node.nodeName;\r\n const attributes = Array.from(node.attributes)\r\n .map(attr => `${attr.name}=\"${attr.value}\"`)\r\n .join(' ');\r\n\r\n formatted += ' '.repeat(indent);\r\n formatted += `<${nodeName}${attributes ? ' ' + attributes : ''}>`;\r\n\r\n if (node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE) {\r\n formatted += `${node.textContent}`;\r\n formatted += `</${nodeName}>\\n`;\r\n } else {\r\n formatted += '\\n';\r\n indent++;\r\n Array.from(node.childNodes).forEach(format);\r\n indent--;\r\n formatted += ' '.repeat(indent);\r\n formatted += `</${nodeName}>\\n`;\r\n }\r\n } else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {\r\n formatted += ' '.repeat(indent);\r\n formatted += `${node.textContent.trim()}\\n`;\r\n }\r\n };\r\n\r\n format(xmlDoc.documentElement);\r\n return `