diff --git a/.codacy.yml b/.codacy.yml index a383c5dd..27a14089 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -5,3 +5,5 @@ exclude_paths: - "graphql/query/*/appsync.js" # The auto-formatter fights with Codacy PMD wanting to be compatiable with all browsers, but this is a local config file - "graphql/jest.config.js" + - "ui/tests/*.tsx" + diff --git a/design/MAIN-PAGE-DESIGN.md b/design/MAIN-PAGE-DESIGN.md new file mode 100644 index 00000000..9f5bcd95 --- /dev/null +++ b/design/MAIN-PAGE-DESIGN.md @@ -0,0 +1,1204 @@ +I am writing a webapp. I'd like you to give me some CSS and HTML (produced by Javascript) that will attach to the "app" div, and give a UI with a title section, with a user icon on the right with a drop-down menu with the "Log Out" option (settings will come later). Under that, there should be a tab-bar, based on the `sheets` property in a data structure. On each tab, will be a number of sections, based on `sections` inside the `sheet` object. Each section has a title piece, and expandable main section, and a a pencil indicator to go into editing mode for that section. There will be different types of section, but for now we will introduce only one - the "text" type, which will contain paragraphs of text. + +Here is the CSS for the menu page before this page, to keep consistency: + +.hidden { + display: none; +} + +.gameslist { + font-family: Arial, sans-serif; + max-width: 600px; + margin: 0 auto; + padding: 20px; + background-color: #f2f2f2; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0 0 0 0.1); +} + +.allgames { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.joingame, +.newgame { + width: 48%; + background-color: #fff; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0 0 0 0.1); +} + +h1 { + font-size: 24px; + margin-bottom: 10px; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + padding: 10px; + border-bottom: 1px solid #ddd; +} + +li:last-child { + border-bottom: none; +} + +form { + display: flex; + flex-direction: column; +} + +label { + font-weight: bold; + margin-bottom: 5px; +} + +input, +textarea { + padding: 5px; + margin-bottom: 10px; + border: 1px solid #ddd; + border-radius: 3px; +} + +button { + padding: 10px; + background-color: #4CAF50; + color: #fff; + border: none; + border-radius: 3px; + cursor: pointer; +} + +button:hover { + background-color: #45a049; +} + +.error-container { + background-color: #ffebee; + border: 1px solid #f44336; + border-radius: 3px; + padding: 10px; + margin-top: 10px; +} + +.error-container.hidden { + display: none; +} + +.error-message { + color: #f44336; + margin-bottom: 10px; +} + +.error-container button { + background-color: #f44336; + color: white; + border: none; + padding: 5px 10px; + border-radius: 3px; + cursor: pointer; +} + +.error-container button:hover { + background-color: #d32f2f; +} + +.game-link { + text-decoration: none; + color: inherit; + display: block; + width: 100%; + height: 100%; + padding: 10px; + transition: background-color 0.2s ease; +} + +.game-link:hover { + background-color: #f0f0f0; +} + + + + + + +Here's a CSS and HTML structure (produced by JavaScript) that meets your requirements. This will create a UI with a title section, user icon with a dropdown menu, tab bar, and sections with expandable content and edit functionality. + +First, let's add some CSS: + +```css +.app { + font-family: Arial, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f2f2f2; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.title { + font-size: 24px; + font-weight: bold; +} + +.user-menu { + position: relative; +} + +.user-icon { + cursor: pointer; + font-size: 24px; +} + +.dropdown-menu { + position: absolute; + right: 0; + top: 100%; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + display: none; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-menu ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.dropdown-menu li { + padding: 10px; + cursor: pointer; +} + +.dropdown-menu li:hover { + background-color: #f0f0f0; +} + +.tab-bar { + display: flex; + border-bottom: 1px solid #ddd; + margin-bottom: 20px; +} + +.tab { + padding: 10px 20px; + cursor: pointer; + border: 1px solid transparent; + border-bottom: none; + border-radius: 3px 3px 0 0; +} + +.tab.active { + background-color: #fff; + border-color: #ddd; +} + +.section { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; + margin-bottom: 20px; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background-color: #f0f0f0; + border-bottom: 1px solid #ddd; +} + +.section-title { + font-weight: bold; +} + +.section-controls { + display: flex; + gap: 10px; +} + +.expand-btn, .edit-btn { + background: none; + border: none; + cursor: pointer; + font-size: 16px; +} + +.section-content { + padding: 10px; + display: none; +} + +.section-content.expanded { + display: block; +} +``` + +Now, let's create a JavaScript function to generate the HTML structure: + +```javascript +function generateAppHTML(data) { + const app = document.getElementById('app'); + app.innerHTML = ` +
+

${data.title}

+
+
👤
+ +
+
+
+ ${data.sheets.map((sheet, index) => ` +
+ ${sheet.name} +
+ `).join('')} +
+ ${data.sheets.map((sheet, sheetIndex) => ` +
+ ${sheet.sections.map((section, sectionIndex) => ` +
+
+ ${section.title} +
+ + +
+
+
+ ${section.type === 'text' ? ` + ${section.content.map(paragraph => ` +

${paragraph}

+ `).join('')} + ` : ''} +
+
+ `).join('')} +
+ `).join('')} + `; + + // Add event listeners + const userIcon = app.querySelector('.user-icon'); + const dropdownMenu = app.querySelector('.dropdown-menu'); + userIcon.addEventListener('click', () => { + dropdownMenu.classList.toggle('show'); + }); + + const tabs = app.querySelectorAll('.tab'); + const sheets = app.querySelectorAll('.sheet'); + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + const sheetIndex = tab.dataset.sheet; + sheets.forEach(sheet => { + sheet.style.display = sheet.dataset.sheet === sheetIndex ? 'block' : 'none'; + }); + }); + }); + + const expandBtns = app.querySelectorAll('.expand-btn'); + expandBtns.forEach(btn => { + btn.addEventListener('click', () => { + const content = btn.closest('.section').querySelector('.section-content'); + content.classList.toggle('expanded'); + btn.textContent = content.classList.contains('expanded') ? '🔼' : '🔽'; + }); + }); +} +``` + +To use this, you would call the `generateAppHTML` function with your data structure: + +```javascript +const appData = { + title: "My App", + sheets: [ + { + name: "Sheet 1", + sections: [ + { + title: "Section 1", + type: "text", + content: ["This is paragraph 1", "This is paragraph 2"] + }, + { + title: "Section 2", + type: "text", + content: ["This is another paragraph"] + } + ] + }, + { + name: "Sheet 2", + sections: [ + { + title: "Section 3", + type: "text", + content: ["Content for Sheet 2"] + } + ] + } + ] +}; + +generateAppHTML(appData); +``` + +This will create a UI with a title, user icon with dropdown, tab bar for sheets, and sections with expandable content. The edit functionality (pencil icon) is included in the UI, but doesn't have any behavior attached to it yet. You can add that functionality as needed. + + + + + + + + +I'd like the user icon to be the standard gravatar in a circle thing + + + + +Certainly! Assuming you'll import an MD5 library, let's continue with the rest of the implementation. Here's what we need to do: + +1. Update the data structure to include the user's email. +2. Modify the `generateAppHTML` function to use the Gravatar image. +3. Add some additional styling for better appearance. + +Here's the updated JavaScript: + +```javascript +function generateAppHTML(data) { + const app = document.getElementById('app'); + const gravatarUrl = `https://www.gravatar.com/avatar/${md5(data.userEmail)}?d=identicon&s=200`; + + app.innerHTML = ` +
+

${data.title}

+
+
+ User Avatar +
+ +
+
+
+ ${data.sheets.map((sheet, index) => ` +
+ ${sheet.name} +
+ `).join('')} +
+ ${data.sheets.map((sheet, sheetIndex) => ` +
+ ${sheet.sections.map((section, sectionIndex) => ` +
+
+ ${section.title} +
+ + +
+
+
+ ${section.type === 'text' ? ` + ${section.content.map(paragraph => ` +

${paragraph}

+ `).join('')} + ` : ''} +
+
+ `).join('')} +
+ `).join('')} + `; + + // Add event listeners + const userIcon = app.querySelector('.user-icon'); + const dropdownMenu = app.querySelector('.dropdown-menu'); + userIcon.addEventListener('click', () => { + dropdownMenu.classList.toggle('show'); + }); + + const tabs = app.querySelectorAll('.tab'); + const sheets = app.querySelectorAll('.sheet'); + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + const sheetIndex = tab.dataset.sheet; + sheets.forEach(sheet => { + sheet.style.display = sheet.dataset.sheet === sheetIndex ? 'block' : 'none'; + }); + }); + }); + + const expandBtns = app.querySelectorAll('.expand-btn'); + expandBtns.forEach(btn => { + btn.addEventListener('click', () => { + const content = btn.closest('.section').querySelector('.section-content'); + content.classList.toggle('expanded'); + btn.textContent = content.classList.contains('expanded') ? '🔼' : '🔽'; + }); + }); +} +``` + +Now, let's add some additional CSS to improve the appearance: + +```css +.app { + font-family: Arial, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f2f2f2; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.title { + font-size: 24px; + font-weight: bold; +} + +.user-menu { + position: relative; +} + +.user-icon { + cursor: pointer; + width: 40px; + height: 40px; + border-radius: 50%; + overflow: hidden; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.user-icon img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.dropdown-menu { + position: absolute; + right: 0; + top: 100%; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + display: none; + z-index: 1000; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-menu ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.dropdown-menu li { + padding: 10px 20px; + cursor: pointer; +} + +.dropdown-menu li:hover { + background-color: #f0f0f0; +} + +.tab-bar { + display: flex; + border-bottom: 1px solid #ddd; + margin-bottom: 20px; +} + +.tab { + padding: 10px 20px; + cursor: pointer; + border: 1px solid transparent; + border-bottom: none; + border-radius: 3px 3px 0 0; + transition: background-color 0.3s ease; +} + +.tab:hover { + background-color: #e0e0e0; +} + +.tab.active { + background-color: #fff; + border-color: #ddd; +} + +.section { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; + margin-bottom: 20px; + overflow: hidden; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 15px; + background-color: #f0f0f0; + border-bottom: 1px solid #ddd; +} + +.section-title { + font-weight: bold; +} + +.section-controls { + display: flex; + gap: 10px; +} + +.expand-btn, .edit-btn { + background: none; + border: none; + cursor: pointer; + font-size: 16px; + padding: 5px; + border-radius: 3px; + transition: background-color 0.3s ease; +} + +.expand-btn:hover, .edit-btn:hover { + background-color: #ddd; +} + +.section-content { + padding: 0 15px; + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease, padding 0.3s ease; +} + +.section-content.expanded { + padding: 15px; + max-height: 1000px; /* Adjust this value as needed */ +} +``` + +To use this updated version, you would call the `generateAppHTML` function with your data structure, including the user's email: + +```javascript +const appData = { + title: "My App", + userEmail: "user@example.com", + sheets: [ + { + name: "Sheet 1", + sections: [ + { + title: "Section 1", + type: "text", + content: ["This is paragraph 1", "This is paragraph 2"] + }, + { + title: "Section 2", + type: "text", + content: ["This is another paragraph"] + } + ] + }, + { + name: "Sheet 2", + sections: [ + { + title: "Section 3", + type: "text", + content: ["Content for Sheet 2"] + } + ] + } + ] +}; + +generateAppHTML(appData); +``` + +This will create a UI with a title, circular Gravatar user icon with dropdown, tab bar for sheets, and sections with expandable content. The edit functionality (pencil icon) is included in the UI, but doesn't have any behavior attached to it yet. You can add that functionality as needed. + + + + + + + + + + +Can you include the logic for the "text" type sections to become editable? After editing, they should call a function editSection, with the new content, sheet ID, and section ID. You'll need to amend what you have to include the IDs + +Certainly! I'll modify the code to include the logic for making "text" type sections editable and add the necessary IDs. Here's the updated JavaScript function: + +```javascript +function generateAppHTML(data) { + const app = document.getElementById('app'); + const gravatarUrl = `https://www.gravatar.com/avatar/${md5(data.userEmail)}?d=identicon&s=200`; + + app.innerHTML = ` +
+

${data.title}

+
+
+ User Avatar +
+ +
+
+
+ ${data.sheets.map((sheet, index) => ` +
+ ${sheet.name} +
+ `).join('')} +
+ ${data.sheets.map((sheet, sheetIndex) => ` +
+ ${sheet.sections.map((section, sectionIndex) => ` +
+
+ ${section.title} +
+ + +
+
+
+ ${section.type === 'text' ? ` +
+ ${section.content.map(paragraph => ` +

${paragraph}

+ `).join('')} +
+ + + ` : ''} +
+
+ `).join('')} +
+ `).join('')} + `; + + // Add event listeners + const userIcon = app.querySelector('.user-icon'); + const dropdownMenu = app.querySelector('.dropdown-menu'); + userIcon.addEventListener('click', () => { + dropdownMenu.classList.toggle('show'); + }); + + const tabs = app.querySelectorAll('.tab'); + const sheets = app.querySelectorAll('.sheet'); + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + const sheetIndex = tab.dataset.sheet; + sheets.forEach(sheet => { + sheet.style.display = sheet.dataset.sheet === sheetIndex ? 'block' : 'none'; + }); + }); + }); + + const expandBtns = app.querySelectorAll('.expand-btn'); + expandBtns.forEach(btn => { + btn.addEventListener('click', () => { + const content = btn.closest('.section').querySelector('.section-content'); + content.classList.toggle('expanded'); + btn.textContent = content.classList.contains('expanded') ? '🔼' : '🔽'; + }); + }); + + const editBtns = app.querySelectorAll('.edit-btn'); + editBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const textContent = section.querySelector('.text-content'); + const textarea = section.querySelector('.edit-textarea'); + const editControls = section.querySelector('.edit-controls'); + + textContent.style.display = 'none'; + textarea.style.display = 'block'; + editControls.style.display = 'block'; + textarea.focus(); + }); + }); + + const saveBtns = app.querySelectorAll('.save-btn'); + saveBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const sheetId = section.dataset.sheetId; + const sectionId = section.dataset.sectionId; + const textarea = section.querySelector('.edit-textarea'); + const newContent = textarea.value.split('\n\n').filter(para => para.trim() !== ''); + + editSection(newContent, sheetId, sectionId); + + const textContent = section.querySelector('.text-content'); + textContent.innerHTML = newContent.map(para => `

${para}

`).join(''); + + textContent.style.display = 'block'; + textarea.style.display = 'none'; + btn.parentElement.style.display = 'none'; + }); + }); + + const cancelBtns = app.querySelectorAll('.cancel-btn'); + cancelBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const textContent = section.querySelector('.text-content'); + const textarea = section.querySelector('.edit-textarea'); + + textContent.style.display = 'block'; + textarea.style.display = 'none'; + btn.parentElement.style.display = 'none'; + }); + }); +} + +function editSection(newContent, sheetId, sectionId) { + // This function should be implemented to handle the editing of a section + console.log('Editing section:', { newContent, sheetId, sectionId }); + // Here you would typically send the new content to your backend or update your data structure +} +``` + +I've made the following changes: + +1. Added `data-sheet-id` and `data-section-id` attributes to each section div. +2. Included a textarea and edit controls (Save and Cancel buttons) for each text section. +3. Added event listeners for the edit, save, and cancel buttons. +4. Implemented the logic to switch between viewing and editing modes. +5. Created an `editSection` function stub that you can implement to handle the actual data updates. + +You'll need to add some CSS to style the new elements: + +```css +.edit-textarea { + width: 100%; + min-height: 100px; + margin-bottom: 10px; + padding: 5px; + border: 1px solid #ddd; + border-radius: 3px; +} + +.edit-controls { + display: flex; + gap: 10px; + margin-bottom: 10px; +} + +.save-btn, .cancel-btn { + padding: 5px 10px; + border: none; + border-radius: 3px; + cursor: pointer; +} + +.save-btn { + background-color: #4CAF50; + color: white; +} + +.cancel-btn { + background-color: #f44336; + color: white; +} + +.save-btn:hover, .cancel-btn:hover { + opacity: 0.8; +} +``` + +Now, when you click the edit button, the text content will be replaced with a textarea for editing. Clicking "Save" will update the content and call the `editSection` function with the new content, sheet ID, and section ID. Clicking "Cancel" will revert the changes and return to the view mode. + +Remember to implement the `editSection` function according to your specific needs, such as updating your data structure or sending the changes to a backend server. + + + + + + + +.innerHTML is bad practice + + +You're absolutely right, and I apologize for using .innerHTML in the previous example. It's indeed considered bad practice due to potential security risks (like XSS vulnerabilities) and performance issues. Let's refactor the code to use DOM manipulation methods instead. Here's an updated version of the `generateAppHTML` function that avoids using .innerHTML: + +```javascript +function generateAppHTML(data) { + const app = document.getElementById('app'); + app.textContent = ''; // Clear existing content + + const header = createHeader(data.title, data.userEmail); + const tabBar = createTabBar(data.sheets); + const sheets = createSheets(data.sheets); + + app.appendChild(header); + app.appendChild(tabBar); + sheets.forEach(sheet => app.appendChild(sheet)); + + addEventListeners(app); +} + +function createHeader(title, userEmail) { + const header = document.createElement('div'); + header.className = 'header'; + + const titleElement = document.createElement('h1'); + titleElement.className = 'title'; + titleElement.textContent = title; + + const userMenu = createUserMenu(userEmail); + + header.appendChild(titleElement); + header.appendChild(userMenu); + + return header; +} + +function createUserMenu(userEmail) { + const userMenu = document.createElement('div'); + userMenu.className = 'user-menu'; + + const userIcon = document.createElement('div'); + userIcon.className = 'user-icon'; + + const img = document.createElement('img'); + img.src = `https://www.gravatar.com/avatar/${md5(userEmail)}?d=identicon&s=200`; + img.alt = 'User Avatar'; + + userIcon.appendChild(img); + + const dropdownMenu = document.createElement('div'); + dropdownMenu.className = 'dropdown-menu'; + + const ul = document.createElement('ul'); + const li = document.createElement('li'); + li.textContent = 'Log Out'; + ul.appendChild(li); + + dropdownMenu.appendChild(ul); + + userMenu.appendChild(userIcon); + userMenu.appendChild(dropdownMenu); + + return userMenu; +} + +function createTabBar(sheets) { + const tabBar = document.createElement('div'); + tabBar.className = 'tab-bar'; + + sheets.forEach((sheet, index) => { + const tab = document.createElement('div'); + tab.className = `tab ${index === 0 ? 'active' : ''}`; + tab.dataset.sheet = index; + tab.textContent = sheet.name; + tabBar.appendChild(tab); + }); + + return tabBar; +} + +function createSheets(sheets) { + return sheets.map((sheet, sheetIndex) => { + const sheetElement = document.createElement('div'); + sheetElement.className = 'sheet'; + sheetElement.dataset.sheet = sheetIndex; + if (sheetIndex !== 0) { + sheetElement.style.display = 'none'; + } + + sheet.sections.forEach((section, sectionIndex) => { + const sectionElement = createSection(section, sheetIndex, sectionIndex); + sheetElement.appendChild(sectionElement); + }); + + return sheetElement; + }); +} + +function createSection(section, sheetIndex, sectionIndex) { + const sectionElement = document.createElement('div'); + sectionElement.className = 'section'; + sectionElement.dataset.sheetId = sheetIndex; + sectionElement.dataset.sectionId = sectionIndex; + + const header = createSectionHeader(section.title); + const content = createSectionContent(section); + + sectionElement.appendChild(header); + sectionElement.appendChild(content); + + return sectionElement; +} + +function createSectionHeader(title) { + const header = document.createElement('div'); + header.className = 'section-header'; + + const titleSpan = document.createElement('span'); + titleSpan.className = 'section-title'; + titleSpan.textContent = title; + + const controls = document.createElement('div'); + controls.className = 'section-controls'; + + const expandBtn = document.createElement('button'); + expandBtn.className = 'expand-btn'; + expandBtn.textContent = '🔽'; + + const editBtn = document.createElement('button'); + editBtn.className = 'edit-btn'; + editBtn.textContent = '✏️'; + + controls.appendChild(expandBtn); + controls.appendChild(editBtn); + + header.appendChild(titleSpan); + header.appendChild(controls); + + return header; +} + +function createSectionContent(section) { + const content = document.createElement('div'); + content.className = 'section-content'; + + if (section.type === 'text') { + const textContent = document.createElement('div'); + textContent.className = 'text-content'; + + section.content.forEach(paragraph => { + const p = document.createElement('p'); + p.textContent = paragraph; + textContent.appendChild(p); + }); + + const textarea = document.createElement('textarea'); + textarea.className = 'edit-textarea'; + textarea.style.display = 'none'; + textarea.value = section.content.join('\n\n'); + + const editControls = document.createElement('div'); + editControls.className = 'edit-controls'; + editControls.style.display = 'none'; + + const saveBtn = document.createElement('button'); + saveBtn.className = 'save-btn'; + saveBtn.textContent = 'Save'; + + const cancelBtn = document.createElement('button'); + cancelBtn.className = 'cancel-btn'; + cancelBtn.textContent = 'Cancel'; + + editControls.appendChild(saveBtn); + editControls.appendChild(cancelBtn); + + content.appendChild(textContent); + content.appendChild(textarea); + content.appendChild(editControls); + } + + return content; +} + +function addEventListeners(app) { + // User menu dropdown + const userIcon = app.querySelector('.user-icon'); + const dropdownMenu = app.querySelector('.dropdown-menu'); + userIcon.addEventListener('click', () => { + dropdownMenu.classList.toggle('show'); + }); + + // Tab switching + const tabs = app.querySelectorAll('.tab'); + const sheets = app.querySelectorAll('.sheet'); + tabs.forEach(tab => { + tab.addEventListener('click', () => { + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + const sheetIndex = tab.dataset.sheet; + sheets.forEach(sheet => { + sheet.style.display = sheet.dataset.sheet === sheetIndex ? 'block' : 'none'; + }); + }); + }); + + // Expand/collapse sections + const expandBtns = app.querySelectorAll('.expand-btn'); + expandBtns.forEach(btn => { + btn.addEventListener('click', () => { + const content = btn.closest('.section').querySelector('.section-content'); + content.classList.toggle('expanded'); + btn.textContent = content.classList.contains('expanded') ? '🔼' : '🔽'; + }); + }); + + // Edit sections + const editBtns = app.querySelectorAll('.edit-btn'); + editBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const textContent = section.querySelector('.text-content'); + const textarea = section.querySelector('.edit-textarea'); + const editControls = section.querySelector('.edit-controls'); + + textContent.style.display = 'none'; + textarea.style.display = 'block'; + editControls.style.display = 'block'; + textarea.focus(); + }); + }); + + // Save edits + const saveBtns = app.querySelectorAll('.save-btn'); + saveBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const sheetId = section.dataset.sheetId; + const sectionId = section.dataset.sectionId; + const textarea = section.querySelector('.edit-textarea'); + const newContent = textarea.value.split('\n\n').filter(para => para.trim() !== ''); + + editSection(newContent, sheetId, sectionId); + + const textContent = section.querySelector('.text-content'); + textContent.innerHTML = ''; + newContent.forEach(para => { + const p = document.createElement('p'); + p.textContent = para; + textContent.appendChild(p); + }); + + textContent.style.display = 'block'; + textarea.style.display = 'none'; + btn.parentElement.style.display = 'none'; + }); + }); + + // Cancel edits + const cancelBtns = app.querySelectorAll('.cancel-btn'); + cancelBtns.forEach(btn => { + btn.addEventListener('click', () => { + const section = btn.closest('.section'); + const textContent = section.querySelector('.text-content'); + const textarea = section.querySelector('.edit-textarea'); + + textContent.style.display = 'block'; + textarea.style.display = 'none'; + btn.parentElement.style.display = 'none'; + }); + }); +} + +function editSection(newContent, sheetId, sectionId) { + // This function should be implemented to handle the editing of a section + console.log('Editing section:', { newContent, sheetId, sectionId }); + // Here you would typically send the new content to your backend or update your data structure +} +``` + +This refactored version creates all elements using DOM manipulation methods like `createElement`, `appendChild`, and `textContent`. It's more secure and follows better practices. The functionality remains the same as in the previous version. + +You can use this function the same way as before: + +```javascript +const appData = { + title: "My App", + userEmail: "user@example.com", + sheets: [ + { + name: "Sheet 1", + sections: [ + { + title: "Section 1", + type: "text", + content: ["This is paragraph 1", "This is paragraph 2"] + }, + { + title: "Section 2", + type: "text", + content: ["This is another paragraph"] + } + ] + }, + { + name: "Sheet 2", + sections: [ + { + title: "Section 3", + type: "text", + content: ["Content for Sheet 2"] + } + ] + } + ] +}; + +generateAppHTML(appData); +``` + +This approach is safer and more maintainable, avoiding the security risks associated with `innerHTML`. diff --git a/ui/index.html b/ui/index.html index 0ec0c71c..88d2d494 100644 --- a/ui/index.html +++ b/ui/index.html @@ -7,12 +7,7 @@ Wildsea -
- - -
- - - +
+ diff --git a/ui/jest.config.js b/ui/jest.config.js index 60500da1..6920560f 100644 --- a/ui/jest.config.js +++ b/ui/jest.config.js @@ -1,6 +1,17 @@ module.exports = { - transform: { "^.+\\.ts?$": "ts-jest" }, - testEnvironment: "node", - testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$", - moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"] + preset: 'ts-jest', + testEnvironment: 'jsdom', + setupFilesAfterEnv: ['/setupTests.ts'], + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + transform: { + '^.+\\.tsx?$': ['ts-jest', { + tsconfig: 'tsconfig.json', + isolatedModules: true + }] + }, + globals: { + "process.env.NODE_ENV": "test" + } }; diff --git a/ui/package-lock.json b/ui/package-lock.json index b8043d41..cc8b42f7 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -5,22 +5,40 @@ "packages": { "": { "dependencies": { - "aws-amplify": "6.6.0" + "@aws-amplify/api-graphql": "4.3.0", + "@vitejs/plugin-react": "4.3.1", + "aws-amplify": "6.6.0", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-intl": "6.6.8" }, "devDependencies": { + "@testing-library/jest-dom": "6.5.0", + "@testing-library/react": "16.0.1", "@types/jest": "29.5.12", "@types/node": "22.5.4", + "@types/node-fetch": "2.6.11", + "@types/react": "18.3.5", + "@types/react-dom": "18.3.0", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", + "node-fetch": "3.3.2", "ts-jest": "29.2.5", + "typescript": "5.5.4", "vite": "5.4.3" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -58,6 +76,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.3.0.tgz", "integrity": "sha512-wvem3CY3sWHqeKm140XJsQ+vcKKW69oF/1YwVDW5oQYR7zAWSyaU2Bvi5PAVUdZQeQrf2E6usuBYbY7DVNXJfg==", + "license": "Apache-2.0", "dependencies": { "@aws-amplify/api-rest": "4.0.47", "@aws-amplify/core": "6.4.0", @@ -1237,7 +1256,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, "dependencies": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" @@ -1250,7 +1268,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -1259,7 +1276,6 @@ "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -1289,7 +1305,6 @@ "version": "7.25.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", - "dev": true, "dependencies": { "@babel/types": "^7.25.4", "@jridgewell/gen-mapping": "^0.3.5", @@ -1304,7 +1319,6 @@ "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", @@ -1320,7 +1334,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -1333,7 +1346,6 @@ "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", - "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", @@ -1351,7 +1363,6 @@ "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -1360,7 +1371,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -1373,7 +1383,6 @@ "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -1382,7 +1391,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -1391,7 +1399,6 @@ "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -1400,7 +1407,6 @@ "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", - "dev": true, "dependencies": { "@babel/template": "^7.25.0", "@babel/types": "^7.25.0" @@ -1413,7 +1419,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -1428,7 +1433,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -1440,7 +1444,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1454,7 +1457,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -1462,14 +1464,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -1478,7 +1478,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -1487,7 +1486,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1499,7 +1497,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", - "dev": true, "dependencies": { "@babel/types": "^7.25.4" }, @@ -1732,11 +1729,53 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/parser": "^7.25.0", @@ -1750,7 +1789,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/generator": "^7.25.4", @@ -1768,7 +1806,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -1791,7 +1828,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -1807,7 +1843,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -1823,7 +1858,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -1839,7 +1873,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -1855,7 +1888,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -1871,7 +1903,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -1887,7 +1918,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -1903,7 +1933,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -1919,7 +1948,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1935,7 +1963,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1951,7 +1978,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1967,7 +1993,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1983,7 +2008,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1999,7 +2023,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2015,7 +2038,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2031,7 +2053,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2047,7 +2068,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2063,7 +2083,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -2079,7 +2098,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -2095,7 +2113,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -2111,7 +2128,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2127,7 +2143,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2143,7 +2158,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2152,6 +2166,100 @@ "node": ">=12" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", + "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", + "license": "MIT", + "dependencies": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz", + "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-skeleton-parser": "1.8.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz", + "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.4.tgz", + "integrity": "sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz", + "integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz", + "integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2459,7 +2567,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2473,7 +2580,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2482,7 +2588,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -2490,14 +2595,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2510,7 +2613,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -2523,7 +2625,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -2536,7 +2637,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -2549,7 +2649,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -2562,7 +2661,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2575,7 +2673,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2588,7 +2685,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2601,7 +2697,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2614,7 +2709,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2627,7 +2721,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2640,7 +2733,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2653,7 +2745,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2666,7 +2757,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2679,7 +2769,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2692,7 +2781,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2705,7 +2793,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -3461,6 +3548,135 @@ "node": ">=16.0.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", + "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3471,6 +3687,14 @@ "node": ">= 10" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/aws-lambda": { "version": "8.10.145", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", @@ -3480,7 +3704,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -3493,7 +3716,6 @@ "version": "7.6.8", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -3502,7 +3724,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -3512,7 +3733,6 @@ "version": "7.20.6", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, "dependencies": { "@babel/types": "^7.20.7" } @@ -3520,8 +3740,7 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -3532,6 +3751,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -3582,11 +3811,48 @@ "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", - "dev": true, + "devOptional": true, "dependencies": { "undici-types": "~6.19.2" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3620,6 +3886,25 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -3739,6 +4024,16 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3933,7 +4228,6 @@ "version": "4.23.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4020,7 +4314,6 @@ "version": "1.0.30001651", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4152,8 +4445,7 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/create-jest": { "version": "29.7.0", @@ -4190,6 +4482,13 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -4217,6 +4516,22 @@ "dev": true, "license": "MIT" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -4236,7 +4551,6 @@ "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -4289,6 +4603,16 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4307,6 +4631,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -4339,8 +4671,7 @@ "node_modules/electron-to-chromium": { "version": "1.5.13", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", - "dev": true + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "node_modules/emittery": { "version": "0.13.1", @@ -4386,7 +4717,6 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -4424,7 +4754,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, "engines": { "node": ">=6" } @@ -4577,6 +4906,30 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -4647,6 +5000,19 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4657,7 +5023,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -4680,7 +5045,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -4740,7 +5104,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -4780,6 +5143,21 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -4911,6 +5289,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4928,6 +5316,18 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/intl-messageformat": { + "version": "10.5.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", + "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "tslib": "^2.4.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5708,8 +6108,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -5774,7 +6173,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -5792,7 +6190,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -5847,15 +6244,37 @@ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "dependencies": { "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -5949,6 +6368,16 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5964,14 +6393,12 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -5991,6 +6418,45 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6000,8 +6466,7 @@ "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6173,8 +6638,7 @@ "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6213,7 +6677,6 @@ "version": "8.4.44", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.44.tgz", "integrity": "sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6316,12 +6779,94 @@ "dev": true, "license": "MIT" }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-intl": { + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz", + "integrity": "sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl": "2.10.4", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "react": "^16.6.0 || 17 || 18", + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6389,7 +6934,6 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", - "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -6448,11 +6992,19 @@ "node": ">=v12.22.7" } }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -6512,7 +7064,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6602,6 +7153,19 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6674,7 +7238,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -6810,8 +7373,8 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "peer": true, + "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6832,7 +7395,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "devOptional": true }, "node_modules/universalify": { "version": "0.2.0", @@ -6848,7 +7411,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6915,7 +7477,6 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", - "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -6992,6 +7553,16 @@ "makeerror": "1.0.12" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -7141,8 +7712,7 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yargs": { "version": "17.7.2", @@ -7185,11 +7755,16 @@ } }, "dependencies": { + "@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true + }, "@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -8238,7 +8813,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, "requires": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" @@ -8247,14 +8821,12 @@ "@babel/compat-data": { "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", - "dev": true + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==" }, "@babel/core": { "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -8277,7 +8849,6 @@ "version": "7.25.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", - "dev": true, "requires": { "@babel/types": "^7.25.4", "@jridgewell/gen-mapping": "^0.3.5", @@ -8289,7 +8860,6 @@ "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", - "dev": true, "requires": { "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", @@ -8302,7 +8872,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, "requires": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -8312,7 +8881,6 @@ "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", @@ -8323,14 +8891,12 @@ "@babel/helper-plugin-utils": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==" }, "@babel/helper-simple-access": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, "requires": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -8339,26 +8905,22 @@ "@babel/helper-string-parser": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==" }, "@babel/helper-validator-identifier": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" }, "@babel/helper-validator-option": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==" }, "@babel/helpers": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", - "dev": true, "requires": { "@babel/template": "^7.25.0", "@babel/types": "^7.25.0" @@ -8368,7 +8930,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -8380,7 +8941,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -8389,7 +8949,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -8400,7 +8959,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -8408,26 +8966,22 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -8438,7 +8992,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", - "dev": true, "requires": { "@babel/types": "^7.25.4" } @@ -8596,11 +9149,35 @@ "@babel/helper-plugin-utils": "^7.24.8" } }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", + "requires": { + "@babel/helper-plugin-utils": "^7.24.7" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.24.7" + } + }, + "@babel/runtime": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, "@babel/template": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, "requires": { "@babel/code-frame": "^7.24.7", "@babel/parser": "^7.25.0", @@ -8611,7 +9188,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", - "dev": true, "requires": { "@babel/code-frame": "^7.24.7", "@babel/generator": "^7.25.4", @@ -8626,7 +9202,6 @@ "version": "7.25.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", - "dev": true, "requires": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -8643,163 +9218,218 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "dev": true, "optional": true }, "@esbuild/android-arm": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "dev": true, "optional": true }, "@esbuild/android-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "dev": true, "optional": true }, "@esbuild/android-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "dev": true, "optional": true }, "@esbuild/darwin-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "dev": true, "optional": true }, "@esbuild/darwin-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "dev": true, "optional": true }, "@esbuild/freebsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "dev": true, "optional": true }, "@esbuild/linux-arm": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "dev": true, "optional": true }, "@esbuild/linux-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "dev": true, "optional": true }, "@esbuild/linux-ia32": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "dev": true, "optional": true }, "@esbuild/linux-loong64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "dev": true, "optional": true }, "@esbuild/linux-mips64el": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "dev": true, "optional": true }, "@esbuild/linux-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "dev": true, "optional": true }, "@esbuild/linux-riscv64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "dev": true, "optional": true }, "@esbuild/linux-s390x": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "dev": true, "optional": true }, "@esbuild/linux-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "dev": true, "optional": true }, "@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "dev": true, "optional": true }, "@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "dev": true, "optional": true }, "@esbuild/sunos-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "dev": true, "optional": true }, "@esbuild/win32-arm64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "dev": true, "optional": true }, "@esbuild/win32-ia32": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "dev": true, "optional": true }, "@esbuild/win32-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "dev": true, "optional": true }, + "@formatjs/ecma402-abstract": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", + "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", + "requires": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz", + "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-skeleton-parser": "1.8.2", + "tslib": "^2.4.0" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz", + "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "tslib": "^2.4.0" + } + }, + "@formatjs/intl": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.4.tgz", + "integrity": "sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" + } + }, + "@formatjs/intl-displaynames": { + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz", + "integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "@formatjs/intl-listformat": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz", + "integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "requires": { + "tslib": "^2.4.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -9043,7 +9673,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "requires": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -9053,26 +9682,22 @@ "@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/set-array": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" }, "@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -9082,112 +9707,96 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", - "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", - "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", - "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", - "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", - "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", - "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", - "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", - "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", - "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", - "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", - "dev": true, "optional": true }, "@sinclair/typebox": { @@ -9803,12 +10412,106 @@ "tslib": "^2.6.2" } }, + "@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "peer": true + } + } + }, + "@testing-library/jest-dom": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", + "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + } + } + }, + "@testing-library/react": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, + "@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "peer": true + }, "@types/aws-lambda": { "version": "8.10.145", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", @@ -9818,7 +10521,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, "requires": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -9831,7 +10533,6 @@ "version": "7.6.8", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, "requires": { "@babel/types": "^7.0.0" } @@ -9840,7 +10541,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, "requires": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -9850,7 +10550,6 @@ "version": "7.20.6", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, "requires": { "@babel/types": "^7.20.7" } @@ -9858,8 +10557,7 @@ "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "@types/graceful-fs": { "version": "4.1.9", @@ -9870,6 +10568,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -9919,11 +10626,44 @@ "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", - "dev": true, + "devOptional": true, "requires": { "undici-types": "~6.19.2" } }, + "@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "@types/react": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -9956,6 +10696,18 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "requires": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + } + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -10039,6 +10791,15 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "requires": { + "dequal": "^2.0.3" + } + }, "async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -10193,7 +10954,6 @@ "version": "4.23.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001646", "electron-to-chromium": "^1.5.4", @@ -10250,8 +11010,7 @@ "caniuse-lite": { "version": "1.0.30001651", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", - "dev": true + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==" }, "chalk": { "version": "4.1.2", @@ -10337,8 +11096,7 @@ "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "create-jest": { "version": "29.7.0", @@ -10366,6 +11124,12 @@ "which": "^2.0.1" } }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, "cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -10389,6 +11153,17 @@ } } }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, "data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -10404,7 +11179,6 @@ "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -10434,6 +11208,12 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -10446,6 +11226,13 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, + "dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "peer": true + }, "domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -10467,8 +11254,7 @@ "electron-to-chromium": { "version": "1.5.13", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", - "dev": true + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "emittery": { "version": "0.13.1", @@ -10501,7 +11287,6 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, "requires": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", @@ -10531,8 +11316,7 @@ "escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" }, "escape-string-regexp": { "version": "2.0.0", @@ -10629,6 +11413,16 @@ "bser": "2.1.1" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -10688,6 +11482,15 @@ "mime-types": "^2.1.12" } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10698,7 +11501,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "optional": true }, "function-bind": { @@ -10710,8 +11512,7 @@ "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-caller-file": { "version": "2.0.5", @@ -10748,8 +11549,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "graceful-fs": { "version": "4.2.11", @@ -10777,6 +11577,21 @@ "function-bind": "^1.1.2" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -10859,6 +11674,12 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -10875,6 +11696,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "intl-messageformat": { + "version": "10.5.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", + "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "tslib": "^2.4.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -11460,8 +12292,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", @@ -11510,8 +12341,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -11522,8 +12352,7 @@ "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "kleur": { "version": "3.0.3", @@ -11563,15 +12392,29 @@ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "requires": { "yallist": "^3.0.2" } }, + "lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "peer": true + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -11641,6 +12484,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -11653,14 +12502,12 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -11668,6 +12515,23 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11677,8 +12541,7 @@ "node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "normalize-path": { "version": "3.0.0", @@ -11802,8 +12665,7 @@ "picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "picomatch": { "version": "2.3.1", @@ -11830,7 +12692,6 @@ "version": "8.4.44", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.44.tgz", "integrity": "sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==", - "dev": true, "requires": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", @@ -11890,12 +12751,67 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + } + }, + "react-intl": { + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz", + "integrity": "sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA==", + "requires": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl": "2.10.4", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" + } + }, "react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==" + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11944,7 +12860,6 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", - "dev": true, "requires": { "@rollup/rollup-android-arm-eabi": "4.21.0", "@rollup/rollup-android-arm64": "4.21.0", @@ -11989,11 +12904,18 @@ "xmlchars": "^2.2.0" } }, + "scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "shebang-command": { "version": "2.0.0", @@ -12037,8 +12959,7 @@ "source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" }, "source-map-support": { "version": "0.5.13", @@ -12107,6 +13028,15 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -12159,8 +13089,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-regex-range": { "version": "5.0.1", @@ -12238,8 +13167,7 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "peer": true + "devOptional": true }, "ulid": { "version": "2.3.0", @@ -12250,7 +13178,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "devOptional": true }, "universalify": { "version": "0.2.0", @@ -12262,7 +13190,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, "requires": { "escalade": "^3.1.2", "picocolors": "^1.0.1" @@ -12298,7 +13225,6 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", - "dev": true, "requires": { "esbuild": "^0.21.3", "fsevents": "~2.3.3", @@ -12324,6 +13250,12 @@ "makeerror": "1.0.12" } }, + "web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -12419,8 +13351,7 @@ "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { "version": "17.7.2", diff --git a/ui/package.json b/ui/package.json index bd1b2950..14ea2fcc 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,11 @@ { "dependencies": { - "aws-amplify": "6.6.0" + "@aws-amplify/api-graphql": "4.3.0", + "@vitejs/plugin-react": "4.3.1", + "aws-amplify": "6.6.0", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-intl": "6.6.8" }, "scripts": { "dev": "vite", @@ -8,11 +13,18 @@ "preview": "vite preview" }, "devDependencies": { + "@testing-library/jest-dom": "6.5.0", + "@testing-library/react": "16.0.1", "@types/jest": "29.5.12", "@types/node": "22.5.4", + "@types/node-fetch": "2.6.11", + "@types/react": "18.3.5", + "@types/react-dom": "18.3.0", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", + "node-fetch": "3.3.2", "ts-jest": "29.2.5", + "typescript": "5.5.4", "vite": "5.4.3" } } diff --git a/ui/setupTests.ts b/ui/setupTests.ts new file mode 100644 index 00000000..7b0828bf --- /dev/null +++ b/ui/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/ui/src/game.ts b/ui/src/game.ts deleted file mode 100644 index 3b8b5506..00000000 --- a/ui/src/game.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { generateClient } from "aws-amplify/api"; -import { Game } from "../../appsync/graphql"; -import { getGameQuery } from "../../appsync/schema"; - -export async function play(id: string) { - const gameData = await fetchGame(id); - - // Debug - const formattedJSON = JSON.stringify(gameData, null, 2); - - // Create a pre element to preserve formatting - const preElement = document.createElement('pre'); - - // Set the text content of the pre element - preElement.textContent = formattedJSON; - - // Add some basic styling - preElement.style.backgroundColor = '#f4f4f4'; - preElement.style.padding = '10px'; - preElement.style.border = '1px solid #ddd'; - preElement.style.borderRadius = '4px'; - preElement.style.whiteSpace = 'pre-wrap'; - - // Append the pre element to the body (or any other container element) - document.body.appendChild(preElement); -} - -export async function fetchGame(id: string): Promise { - const client = generateClient(); - const response = await client.graphql({ - query: getGameQuery, - variables: { - id: id - } - }) as { data: { getGame: Game } }; - - return response.data.getGame; -} diff --git a/ui/src/game.tsx b/ui/src/game.tsx new file mode 100644 index 00000000..222ba47d --- /dev/null +++ b/ui/src/game.tsx @@ -0,0 +1,67 @@ +import React, { useState, useEffect } from 'react'; +import { generateClient } from "aws-amplify/api"; +import { Game as GameType } from "../../appsync/graphql"; +import { getGameQuery } from "../../appsync/schema"; +import { IntlProvider, FormattedMessage, useIntl } from 'react-intl'; +import { messages } from './translations'; + +interface GameProps { + id: string; +} + +const GameContent: React.FC = ({ id }) => { + const [game, setGame] = useState(null); + const [error, setError] = useState(null); + const intl = useIntl(); + + useEffect(() => { + async function fetchGame() { + try { + const client = generateClient(); + const response = await client.graphql({ + query: getGameQuery, + variables: { + id: id + } + }) as { data: { getGame: GameType } }; + + setGame(response.data.getGame); + } catch (err) { + setError(intl.formatMessage({ id: 'errorFetchingGameData'})); + } + } + + fetchGame(); + }, [id]); + + if (error) { + return
: {error}
; + } + + if (!game) { + return
; + } + + return ( +
+

: {game.gameName}

+
+                {JSON.stringify(game, null, 2)}
+            
+
+ ); +}; + +const Game: React.FC = (props) => ( + + + +); + +export default Game; diff --git a/ui/src/gamesMenu.ts b/ui/src/gamesMenu.ts deleted file mode 100644 index a257464a..00000000 --- a/ui/src/gamesMenu.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { generateClient } from "aws-amplify/api"; -import { createGameMutation, getGamesQuery } from "../../appsync/schema"; -import { PlayerSheetSummary, CreateGameInput, Game } from "../../appsync/graphql"; -import { GraphQLResult } from "@aws-amplify/api-graphql"; - -export async function fetchGames(): Promise { - const client = generateClient(); - const response = await client.graphql({ - query: getGamesQuery, - }) as GraphQLResult<{ getGames: PlayerSheetSummary[] }>; - - return response.data.getGames; -} - -export function createGameElement(game: PlayerSheetSummary): HTMLLIElement { - const li = document.createElement('li'); - const link = document.createElement('a'); - link.href = `/?gameId=${game.gameId}`; - link.textContent = `${game.gameName} - ${game.gameDescription}`; - link.setAttribute('aria-label', `Play game: ${game.gameName}`); - li.appendChild(link); - return li; -} - -export function createGamesList(games: PlayerSheetSummary[]): HTMLUListElement { - const ul = document.createElement('ul'); - for (const game of games) { - const li = createGameElement(game); - ul.appendChild(li); - } - return ul; -} - -export function createNewGameForm(): HTMLFormElement { - const form = document.createElement('form'); - form.setAttribute('aria-label', 'Create New Game'); - - const gameNameLabel = document.createElement('label'); - gameNameLabel.setAttribute('for', 'gameName'); - gameNameLabel.textContent = 'Game Name:'; - - const gameNameInput = document.createElement('input'); - gameNameInput.setAttribute('type', 'text'); - gameNameInput.setAttribute('id', 'gameName'); - gameNameInput.setAttribute('name', 'gameName'); - gameNameInput.required = true; - - const gameDescriptionLabel = document.createElement('label'); - gameDescriptionLabel.setAttribute('for', 'gameDescription'); - gameDescriptionLabel.textContent = 'Game Description:'; - - const gameDescriptionTextarea = document.createElement('textarea'); - gameDescriptionTextarea.setAttribute('id', 'gameDescription'); - gameDescriptionTextarea.setAttribute('name', 'gameDescription'); - gameDescriptionTextarea.required = true; - - const submitButton = document.createElement('button'); - submitButton.setAttribute('type', 'submit'); - submitButton.textContent = 'Create Game'; - - const errorContainer = document.createElement('div'); - errorContainer.setAttribute('role', 'alert'); - errorContainer.classList.add('error-container', 'hidden'); - errorContainer.id = 'error'; - - const errorMessage = document.createElement('p'); - errorMessage.classList.add('error-message'); - errorMessage.id = 'error-message'; - - const dismissButton = document.createElement('button'); - dismissButton.textContent = 'Dismiss'; - dismissButton.setAttribute('aria-label', 'Dismiss error message'); - dismissButton.addEventListener('click', (event) => { - event.preventDefault(); // Prevent any default action - errorContainer.classList.add('hidden'); - }); - - errorContainer.appendChild(errorMessage); - errorContainer.appendChild(dismissButton); - - form.addEventListener('submit', handleCreateGame); - form.appendChild(gameNameLabel); - form.appendChild(gameNameInput); - form.appendChild(gameDescriptionLabel); - form.appendChild(gameDescriptionTextarea); - form.appendChild(submitButton); - form.appendChild(errorContainer); - - return form; -} - -export async function doCreateGame(data: CreateGameInput): Promise { - try { - const client = generateClient(); - const result = await client.graphql({ - query: createGameMutation, - variables: {input: data}, - }) as GraphQLResult<{ createGame: Game }>; - - const newGame = result.data.createGame; - const gameId = newGame.gameId; - - return gameId; - } catch (error) { - const msg = 'Error creating game: ' + JSON.stringify(error); - logError(msg); - return ""; - } -} - -function logError(msg: string) { - console.log(msg); - const errorElement = document.getElementById('error'); - const errorMessage = document.getElementById('error-message'); - if (errorElement != null && errorMessage != null) { - errorMessage.textContent = msg; - errorElement.classList.remove('hidden'); - } -} - -export async function handleCreateGame(event: SubmitEvent) { - event.preventDefault(); - - const formData = new FormData(event.target as HTMLFormElement); - const gameName = formData.get('gameName') as string; - const gameDescription = formData.get('gameDescription') as string; - - const createGameInput: CreateGameInput = { - name: gameName, - description: gameDescription, - }; - - const gameId = await doCreateGame(createGameInput); - if (gameId) { - // Redirect to the new game page - console.log(gameId); - window.location.href = `${window.location.origin}/?gameId=${gameId}`; - } -} - -export async function gamesMenuScreen(): Promise { - const games = await fetchGames(); - - const gamesList = document.getElementById('gameslist'); - if (gamesList) { - const all = document.createElement('div'); - all.className = 'allgames'; - - const join = document.createElement('div'); - join.className = 'joingame'; - all.appendChild(join); - - const h1 = document.createElement('h1'); - h1.innerText = 'Available Games'; - join.appendChild(h1); - - const ul = createGamesList(games); - join.appendChild(ul); - - const newGame = document.createElement('div'); - newGame.className = 'newgame'; - - const h1b = document.createElement('h1'); - h1b.innerText = 'Create New Game'; - newGame.appendChild(h1b); - - const form = createNewGameForm(); - newGame.appendChild(form); - - all.appendChild(newGame); - - gamesList.innerHTML = ''; - gamesList.appendChild(all); - gamesList.className = 'gameslist'; - } -} diff --git a/ui/src/gamesMenu.tsx b/ui/src/gamesMenu.tsx new file mode 100644 index 00000000..33778011 --- /dev/null +++ b/ui/src/gamesMenu.tsx @@ -0,0 +1,115 @@ +import React, { useState, useEffect } from 'react'; +import { generateClient } from "aws-amplify/api"; +import { createGameMutation, getGamesQuery } from "../../appsync/schema"; +import { PlayerSheetSummary, CreateGameInput, Game } from "../../appsync/graphql"; +import { GraphQLResult } from "@aws-amplify/api-graphql"; +import { IntlProvider, FormattedMessage, useIntl } from 'react-intl'; +import { messages } from './translations'; + + +export const GamesMenuContent: React.FC = () => { + const client = generateClient(); + const [games, setGames] = useState([]); + const [error, setError] = useState(null); + const [gameName, setGameName] = useState(''); + const [gameDescription, setGameDescription] = useState(''); + const intl = useIntl(); + + useEffect(() => { + fetchGames(); + }, []); + + const fetchGames = async () => { + try { + const response = await client.graphql({ + query: getGamesQuery, + }) as GraphQLResult<{ getGames: PlayerSheetSummary[] }>; + setGames(response.data.getGames); + } catch (error) { + setError(intl.formatMessage({ id: 'errorFetchingGames' }) + JSON.stringify(error)); + } + }; + + const handleCreateGame = async (event: React.FormEvent) => { + event.preventDefault(); + + const createGameInput: CreateGameInput = { + name: gameName, + description: gameDescription, + }; + + try { + const result = await client.graphql({ + query: createGameMutation, + variables: {input: createGameInput}, + }) as GraphQLResult<{ createGame: Game }>; + + const newGame = result.data.createGame; + const gameId = newGame.gameId; + + if (gameId) { + window.location.href = `${window.location.origin}/?gameId=${gameId}`; + } + } catch (error) { + setError('Error creating game: ' + JSON.stringify(error)); + } + }; + + return ( +
+
+
+

+ +
+
+

Create New Game

+
+ + setGameName(e.target.value)} + /> + + + +
+ {error && ( +
+

{error}

+ +
+ )} +
+
+
+ ); +}; + +export const GamesMenu: React.FC = () => ( + + + +); + +export default GamesMenu; diff --git a/ui/src/jest-dom.ts b/ui/src/jest-dom.ts new file mode 100644 index 00000000..7b0828bf --- /dev/null +++ b/ui/src/jest-dom.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/ui/src/main.ts b/ui/src/main.tsx similarity index 62% rename from ui/src/main.ts rename to ui/src/main.tsx index ab7c6ee9..60472bd7 100644 --- a/ui/src/main.ts +++ b/ui/src/main.tsx @@ -1,8 +1,13 @@ +import React, { useEffect, useState, Suspense } from "react"; +import { createRoot } from "react-dom/client"; import { Amplify } from "aws-amplify"; import { signInWithRedirect, signOut } from "@aws-amplify/auth"; import amplifyconfig from "./amplifyconfiguration.json"; -import { gamesMenuScreen } from "./gamesMenu"; -import { play } from "./game"; +import { IntlProvider, FormattedMessage } from 'react-intl'; +import { messages } from './translations'; + +const GamesMenu = React.lazy(() => import("./gamesMenu")) +const Game = React.lazy(() => import("./game")) function handleSignOutClick() { signOut(); @@ -12,7 +17,7 @@ function handleSignInClick() { signInWithRedirect({}); } -function getPageURL() { +export function getPageURL() { const url = new URL(window.location.href); return url.origin + url.pathname; } @@ -75,37 +80,59 @@ export async function mergeConfig(configUpdates: AmplifyConfigJSON, pageUrl: str return amplifyconfig; } -async function amplifySetup() { +export async function amplifySetup() { const response = await fetch("/config.json"); const configUpdates = await response.json(); const pageUrl = getPageURL(); const config = await mergeConfig(configUpdates, pageUrl); Amplify.configure(config); - - const loginButton = document.getElementById("login-button"); - loginButton?.addEventListener("click", handleSignInClick); - const logoutButton = document.getElementById("logout-button"); - logoutButton?.addEventListener("click", handleSignOutClick); } -async function main() { - await amplifySetup(); +export function AppContent() { + const [gameId, setGameId] = useState(null); + const [isAmplifyConfigured, setIsAmplifyConfigured] = useState(false); - const gameId = getGameId(); + useEffect(() => { + async function setup() { + await amplifySetup(); + setIsAmplifyConfigured(true); + const id = getGameId(); + setGameId(id); + } + setup(); + }, []); - if (gameId) { - await play(gameId); - } else { - await gamesMenuScreen(); + if (!isAmplifyConfigured) { + return
; } + + return ( +
+ + +
}> + {gameId ? : } + + + ); } -function getGameId(): string | null { +export function getGameId(): string | null { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('gameId'); } -if (typeof window !== "undefined") { - main(); + +export function App() { + return ( + + + + ); +} + +if (process.env.NODE_ENV !== "test") { + const root = createRoot(document.getElementById("root") as HTMLElement); + root.render(); } diff --git a/ui/src/translations.ts b/ui/src/translations.ts new file mode 100644 index 00000000..d70279d9 --- /dev/null +++ b/ui/src/translations.ts @@ -0,0 +1,21 @@ +export const messages = { + en: { + 'availableGames': 'Available Games', + 'createNewGame': 'Create New Game', + 'gameName': 'Game Name:', + 'gameDescription': 'Game Description:', + 'createGame': 'Create Game', + 'errorFetchingGames': 'Error fetching games:', + 'errorCreatingGame': 'Error creating game:', + 'dismiss': 'Dismiss', + 'playGame': 'Play game:', + 'loading': 'Loading...', + 'login': 'Login', + 'logout': 'Logout', + 'loadingGamesMenu': 'Loading games menu...', + 'errorFetchingGameData': 'Error fetching game data', + 'error': 'Error', + 'loadingGameData': 'Loading game data...', + 'gameTitle': 'Game', + }, +}; diff --git a/ui/tests/gamesMenu.test.ts b/ui/tests/gamesMenu.test.ts deleted file mode 100644 index edf01f97..00000000 --- a/ui/tests/gamesMenu.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { createGameElement, createGamesList, createNewGameForm } from "../src/gamesMenu"; -import { PlayerSheetSummary } from '../../appsync/graphql'; - -describe('createGameElement', () => { - it('should create a game element correctly', () => { - const game: PlayerSheetSummary = { - __typename: 'PlayerSheetSummary', - gameName: 'Test Game', - gameDescription: 'Test Description', - gameId: '1', - type: 'Test Type', - createdAt: '2023-05-01T00:00:00.000Z', - updatedAt: '2023-05-01T00:00:00.000Z', - }; - - const li = createGameElement(game); - expect(li.textContent).toBe(`${game.gameName} - ${game.gameDescription}`); - }); -}); - -describe('createGamesList', () => { - it('should create a games list correctly', () => { - const games: PlayerSheetSummary[] = [ - { - __typename: 'PlayerSheetSummary', - gameName: 'Game 1', - gameDescription: 'Description 1', - gameId: '1', - type: 'Type 1', - createdAt: '2023-05-01T00:00:00.000Z', - updatedAt: '2023-05-01T00:00:00.000Z', - }, - { - __typename: 'PlayerSheetSummary', - gameName: 'Game 2', - gameDescription: 'Description 2', - gameId: '2', - type: 'Type 2', - createdAt: '2023-05-01T00:00:00.000Z', - updatedAt: '2023-05-01T00:00:00.000Z', - }, - ]; - - const ul = createGamesList(games); - const liElements = ul.querySelectorAll('li'); - expect(liElements.length).toBe(games.length); - liElements.forEach((li, index) => { - expect(li.textContent).toBe( - `${games[index].gameName} - ${games[index].gameDescription}` - ); - }); - }); -}); - -describe('createNewGameForm', () => { - it('should create a new game form correctly', () => { - const form = createNewGameForm(); - expect(form.tagName).toBe('FORM'); - expect(form.querySelectorAll('input, textarea, button').length).toBe(4); - }); -}); diff --git a/ui/tests/gamesMenu.test.tsx b/ui/tests/gamesMenu.test.tsx new file mode 100644 index 00000000..c76f4885 --- /dev/null +++ b/ui/tests/gamesMenu.test.tsx @@ -0,0 +1,152 @@ +import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; +import { GamesMenu } from '../src/gamesMenu'; +import { generateClient } from "aws-amplify/api"; +import { GraphQLResult } from "@aws-amplify/api-graphql"; +import { PlayerSheetSummary } from "../../appsync/graphql"; + +jest.mock("aws-amplify/api", () => ({ + generateClient: jest.fn(), +})); + +jest.mock("../src/gamesMenu", () => { + const originalModule = jest.requireActual("../src/gamesMenu"); + return { + ...originalModule, + client: { + graphql: jest.fn(), + }, + }; +}) + +describe('GamesMenu', () => { + let mockGraphql: jest.Mock; + + beforeEach(() => { + mockGraphql = jest.fn(); + (generateClient as jest.Mock).mockReturnValue({ graphql: mockGraphql }); + }); + + it('renders without crashing', async () => { + await act(async () => { + render(); + }); + expect(screen.getByText('Available Games')).toBeInTheDocument(); + expect(screen.getByText('Create New Game')).toBeInTheDocument(); + }); + + it('fetches and displays games', async () => { + const mockGames: PlayerSheetSummary[] = [ + { + gameId: '1', + gameName: 'Test Game 1', + gameDescription: 'Description 1', + createdAt: '2023-01-01T00:00:00Z', + updatedAt: '2023-01-01T00:00:00Z', + type: 'GAME' + }, + { + gameId: '2', + gameName: 'Test Game 2', + gameDescription: 'Description 2', + createdAt: '2023-01-02T00:00:00Z', + updatedAt: '2023-01-02T00:00:00Z', + type: 'GAME' + }, + ]; + + mockGraphql.mockResolvedValueOnce({ data: { getGames: mockGames } } as GraphQLResult<{ getGames: PlayerSheetSummary[] }>); + + await act(async () => { + render(); + }); + + await act(async () => { + await waitFor(() => { + expect(screen.getByText(/Test Game 1/)).toBeInTheDocument(); + expect(screen.getByText(/Test Game 2/)).toBeInTheDocument(); + }); + }); + }); + + it('handles game creation', async () => { + await act(async () => { + render(); + }); + + // Fill in the form + fireEvent.change(screen.getByLabelText('Game Name:'), { target: { value: 'New Game' } }); + fireEvent.change(screen.getByLabelText('Game Description:'), { target: { value: 'New Description' } }); + + // Submit the form + await act(async () => { + fireEvent.click(screen.getByText('Create Game')); + }); + + // Check if the graphql function was called with the correct arguments + expect(mockGraphql).toHaveBeenCalledWith( + expect.objectContaining({ + variables: { + input: { + name: 'New Game', + description: 'New Description' + } + }, + }) + ); + }); + + it('displays error message when fetching games fails', async () => { + mockGraphql.mockRejectedValueOnce(new Error('Fetch error')); + + await act(async () => { + render(); + }); + + await act(async () => { + await waitFor(() => { + expect(screen.getByText(/Error fetching games:/)).toBeInTheDocument(); + }); + }); + }); + + it('displays error message when creating game fails', async () => { + mockGraphql.mockRejectedValueOnce(new Error('Create error')); + + await act(async () => { + render(); + }); + + fireEvent.change(screen.getByLabelText('Game Name:'), { target: { value: 'New Game' } }); + fireEvent.change(screen.getByLabelText('Game Description:'), { target: { value: 'New Description' } }); + + await act(async () => { + fireEvent.click(screen.getByText('Create Game')); + }); + + await act(async () => { + await waitFor(() => { + expect(screen.getByText(/Error creating game:/)).toBeInTheDocument(); + }); + }); + }); + + it('allows dismissing error messages', async () => { + mockGraphql.mockRejectedValueOnce(new Error('Fetch error')); + + await act(async () => { + render(); + }); + + await act(async () => { + await waitFor(() => { + expect(screen.getByText(/Error fetching games:/)).toBeInTheDocument(); + }); + }); + + await act(async () => { + fireEvent.click(screen.getByText('Dismiss')); + }); + + expect(screen.queryByText(/Error fetching games:/)).not.toBeInTheDocument(); + }); +}); diff --git a/ui/tests/main.test.ts b/ui/tests/main.test.ts deleted file mode 100644 index 311a0f64..00000000 --- a/ui/tests/main.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Import the necessary modules -import { mergeConfig } from '../src/main'; - -// Mock the amplifyconfig object -jest.mock('aws-amplify', () => ({})); -jest.mock('@aws-amplify/auth', () => ({})); - -describe('mergeConfig', () => { - it('should update amplifyconfig based on configUpdates and pageUrl', async () => { - const configUpdates = { - identity_pool: 'NewIdentityPool', - user_pool: 'NewUserPool', - web_client: 'NewWebClient', - graphql: 'NewGraphQLEndpoint', - region: 'NewRegion', - loginDomain: 'NewLoginDomain', - }; - const pageUrl = 'https://newredirect.com'; - - const expectedConfig = { - aws_project_region: 'NewRegion', - aws_appsync_graphqlEndpoint: 'NewGraphQLEndpoint', - aws_appsync_region: 'NewRegion', - aws_appsync_authenticationType: "AMAZON_COGNITO_USER_POOLS", - aws_cognito_identity_pool_id: 'NewIdentityPool', - aws_cognito_region: 'NewRegion', - aws_user_pools_id: 'NewUserPool', - aws_user_pools_web_client_id: 'NewWebClient', - oauth: { - domain: 'NewLoginDomain', - scope: ["openid"], - redirectSignIn: 'https://newredirect.com', - redirectSignOut: 'https://newredirect.com', - responseType: "code" - }, - federationTarget: "COGNITO_USER_POOLS", - aws_cognito_username_attributes: [], - aws_cognito_social_providers: [], - aws_cognito_signup_attributes: ["EMAIL"], - aws_cognito_mfa_configuration: "OFF", - aws_cognito_mfa_types: ["SMS"], - aws_cognito_password_protection_settings: { - passwordPolicyMinLength: 8, - passwordPolicyCharacters: [] - }, - aws_cognito_verification_mechanisms: ["EMAIL"] - }; - - const result = await mergeConfig(configUpdates, pageUrl); - - expect(result).toEqual(expectedConfig); - }); -}); diff --git a/ui/tests/main.test.tsx b/ui/tests/main.test.tsx new file mode 100644 index 00000000..c2239a71 --- /dev/null +++ b/ui/tests/main.test.tsx @@ -0,0 +1,136 @@ +// Mock fetch globally +global.fetch = jest.fn(() => Promise.resolve({ + json: () => Promise.resolve({}) +})) as jest.Mock; + +import { render, screen, waitFor, act } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { App, mergeConfig, getPageURL, getGameId } from '../src/main'; + + +// Mock the modules +jest.mock('aws-amplify', () => ({ + Amplify: { + configure: jest.fn(), + }, +})); + +jest.mock('@aws-amplify/auth', () => ({ + signInWithRedirect: jest.fn(), + signOut: jest.fn(), +})); + +// Mock the Game and GamesMenu components +jest.mock('../src/game', () => ({ + __esModule: true, + default: ({ id }: { id: string }) =>
Game Component {id}
, +})); + +jest.mock('../src/gamesMenu', () => ({ + __esModule: true, + default: () =>
Games Menu Component
, +})); + +// Mock the amplifySetup function +jest.mock('../src/main', () => { + const originalModule = jest.requireActual('../src/main'); + return { + ...originalModule, + amplifySetup: jest.fn().mockResolvedValue(undefined), + }; +}); + +describe('main.tsx', () => { + beforeEach(() => { + // Create a root element for each test + const root = document.createElement('div'); + root.id = 'root'; + document.body.appendChild(root); + + jest.clearAllMocks(); + Object.defineProperty(window, 'location', { + value: { + href: 'http://localhost:3000', + origin: 'http://localhost:3000', + pathname: '/', + search: '', + }, + writable: true, + }); + }); + + test('App renders login and logout buttons', async () => { + await act(async () => { + render(); + }); + await waitFor(() => { + expect(screen.getByText('Login')).toBeInTheDocument(); + expect(screen.getByText('Logout')).toBeInTheDocument(); + }); + }); + + test('App renders GamesMenu when no gameId is present', async () => { + await act(async () => { + render(); + }); + await waitFor(() => { + expect(screen.getByText('Games Menu Component')).toBeInTheDocument(); + }); + }); + + test('App renders Game component when gameId is present', async () => { + Object.defineProperty(window, 'location', { + value: { + href: 'http://localhost:3000?gameId=123', + search: '?gameId=123', + }, + writable: true, + }); + + await act(async () => { + render(); + }); + await waitFor(() => { + expect(screen.getByText('Game Component 123')).toBeInTheDocument(); + }); + }); + + test('mergeConfig updates amplifyconfig correctly', async () => { + const configUpdates = { + identity_pool: 'test_identity_pool', + loginDomain: 'test_login_domain', + region: 'test_region', + graphql: 'test_graphql', + web_client: 'test_web_client', + user_pool: 'test_user_pool', + }; + const pageUrl = 'http://test.com'; + + const result = await mergeConfig(configUpdates, pageUrl); + + expect(result.oauth.redirectSignIn).toBe(pageUrl); + expect(result.oauth.redirectSignOut).toBe(pageUrl); + expect(result.aws_cognito_identity_pool_id).toBe('test_identity_pool'); + expect(result.aws_user_pools_id).toBe('test_user_pool'); + expect(result.aws_user_pools_web_client_id).toBe('test_web_client'); + expect(result.aws_appsync_graphqlEndpoint).toBe('test_graphql'); + expect(result.aws_project_region).toBe('test_region'); + expect(result.aws_appsync_region).toBe('test_region'); + expect(result.aws_cognito_region).toBe('test_region'); + expect(result.oauth.domain).toBe('test_login_domain'); + }); + + test('getPageURL returns correct URL', () => { + expect(getPageURL()).toBe('http://localhost:3000/'); + }); + + test('getGameId returns correct gameId from URL', () => { + Object.defineProperty(window, 'location', { + value: { + search: '?gameId=123', + }, + writable: true, + }); + expect(getGameId()).toBe('123'); + }); +}); diff --git a/ui/tsconfig.json b/ui/tsconfig.json index e8e9c8bf..93bc3aeb 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -12,9 +12,11 @@ /* Language and Environment */ //"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["ES2020", "DOM", "DOM.Iterable"], // "jsx": "preserve", /* Specify what JSX code is generated. */ + "jsx": "react-jsx", // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ @@ -22,21 +24,28 @@ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + //"module": "commonjs", /* Specify what module code is generated. */ + "module": "ESNext", // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "Bundler", // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + "typeRoots": [ + "./node_modules/@types", + "./types" + ], // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + "types": ["jest", "@testing-library/jest-dom"], // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ @@ -58,7 +67,7 @@ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ + "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ @@ -91,11 +100,11 @@ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ @@ -105,5 +114,10 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + "include": [ + "src/**/*", + "tests/**/*" + ], + "references": [{"path": "./tsconfig.node.json"}] } diff --git a/ui/tsconfig.node.json b/ui/tsconfig.node.json new file mode 100644 index 00000000..42872c59 --- /dev/null +++ b/ui/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/ui/types/jest-extended.d.ts b/ui/types/jest-extended.d.ts new file mode 100644 index 00000000..00003d2f --- /dev/null +++ b/ui/types/jest-extended.d.ts @@ -0,0 +1,8 @@ +/// + +declare namespace jest { + interface Matchers { + toBeInTheDocument(): R; + // Add other custom matchers here if needed + } +} diff --git a/ui/vite.config.ts b/ui/vite.config.ts index b7ab0a96..fbb83f34 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,11 +1,15 @@ -export default { - build: { - rollupOptions: { - output: { - entryFileNames: 'assets/[name].js', - chunkFileNames: 'assets/[name].js', - assetFileNames: 'assets/[name].[ext]' - } - } +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + build: { + rollupOptions: { + output: { + entryFileNames: 'assets/[name].js', + chunkFileNames: 'assets/[name].js', + assetFileNames: 'assets/[name].[ext]' + } } -}; + } +});